1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "nel/3d/material.h"
20 #include "nel/3d/texture.h"
21 #include "nel/3d/driver.h"
22 #include "nel/misc/stream.h"
25 using namespace NLMISC
;
34 // ***************************************************************************
35 CMaterial::CMaterial()
37 /* ***********************************************
38 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
39 * It can be loaded/called through CAsyncFileManager for instance
40 * ***********************************************/
42 _Flags
= IDRV_MAT_ZWRITE
;
43 // Must init All the flags by default.
46 _DstBlend
= invsrcalpha
;
47 _ZFunction
= lessequal
;
49 _Color
.set(255,255,255,255);
50 _StainedGlassWindow
= false;
51 _AlphaTestThreshold
= 0.5f
;
53 _LightMapsMulx2
= false;
56 // ***************************************************************************
57 void CMaterial::initUnlit()
61 setColor(CRGBA(255,255,255,255));
62 for(uint32 i
=0;i
<IDRV_MAT_MAXTEXTURES
;i
++)
63 setTexture((uint8
)i
,NULL
);
68 setAlphaTestThreshold(0.5f
);
71 // ***************************************************************************
73 void CMaterial::initLighted()
80 // ***************************************************************************
81 CMaterial
&CMaterial::operator=(const CMaterial
&mat
)
83 _ShaderType
= mat
._ShaderType
;
85 _SrcBlend
= mat
._SrcBlend
;
86 _DstBlend
= mat
._DstBlend
;
87 _ZFunction
= mat
._ZFunction
;
90 _Emissive
= mat
._Emissive
;
91 _Ambient
= mat
._Ambient
;
92 _Diffuse
= mat
._Diffuse
;
93 _Specular
= mat
._Specular
;
94 _Shininess
= mat
._Shininess
;
95 _AlphaTestThreshold
= mat
._AlphaTestThreshold
;
96 _TexCoordGenMode
= mat
._TexCoordGenMode
;
98 for(uint32 i
=0;i
<IDRV_MAT_MAXTEXTURES
;i
++)
100 _Textures
[i
]= mat
._Textures
[i
];
101 _TexEnvs
[i
]= mat
._TexEnvs
[i
];
102 _TexAddrMode
[i
] = mat
._TexAddrMode
[i
];
106 _LightMaps
= mat
._LightMaps
;
107 _LightMapsMulx2
= mat
._LightMapsMulx2
;
109 // copy texture matrix if there.
110 if (mat
._TexUserMat
.get())
112 CUniquePtr
<CUserTexMat
> texMatClone(new CUserTexMat(*(mat
._TexUserMat
))); // make cpy
113 //std::swap(texMatClone, _TexUserMat); // swap with old
114 _TexUserMat
= CUniquePtrMove(texMatClone
);
121 // Must do not copy drv info.
123 // All states of material is modified.
124 _Touched
= IDRV_TOUCHED_ALL
;
130 // ***************************************************************************
131 CMaterial::~CMaterial()
133 /* ***********************************************
134 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
135 * It can be loaded/called through CAsyncFileManager for instance
136 * ***********************************************/
138 // Must kill the drv mirror of this material.
143 // ***************************************************************************
144 void CMaterial::serial(NLMISC::IStream
&f
)
146 /* ***********************************************
147 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
148 * It can be loaded/called through CAsyncFileManager for instance
149 * ***********************************************/
153 - Added support for third operand (for Mad operator)
155 - Serial _TexCoordGenMode
157 - Lightmap color and Mulx2
159 - Texture matrix animation
161 - AlphaTest threshold
163 - Texture Addressing modes
169 - texture environement.
174 sint ver
= f
.serialVersion(9);
175 // For the version <=1:
176 nlassert(IDRV_MAT_MAXTEXTURES
==4);
178 f
.serialEnum(_ShaderType
);
180 f
.serialEnum(_SrcBlend
);
181 f
.serialEnum(_DstBlend
);
182 f
.serialEnum(_ZFunction
);
185 f
.serial(_Emissive
, _Ambient
, _Diffuse
, _Specular
);
188 f
.serial(_Shininess
);
192 f
.serial(_AlphaTestThreshold
);
196 f
.serial(_TexCoordGenMode
);
199 _TexCoordGenMode
= 0;
202 for(uint32 i
=0;i
<IDRV_MAT_MAXTEXTURES
;i
++)
204 // Serial texture descriptor.
205 _Textures
[i
].serialPolyPtr(f
);
207 // Read texture environnement, or setup them.
210 _TexEnvs
[i
].serial(f
, ver
>= 9 ? 1 : 0);
214 // Else setup as default behavior, like before...
216 _TexEnvs
[i
].setDefault();
228 _LightMaps
.resize(n
);
232 n
= (uint32
)_LightMaps
.size();
235 for (uint32 i
= 0; i
< n
; ++i
)
236 _LightMaps
[i
].serial2(f
);
237 f
.serial(_LightMapsMulx2
);
241 f
.serialCont(_LightMaps
);
247 if (_Flags
& IDRV_MAT_TEX_ADDR
)
249 for(uint32 i
=0;i
<IDRV_MAT_MAXTEXTURES
;i
++)
251 f
.serial(_TexAddrMode
[i
]);
258 // Converte Deprecated DEFMAT to std Mat.
259 if(_Flags
& IDRV_MAT_DEFMAT
)
261 setEmissive(CRGBA::Black
);
262 setAmbient(CRGBA::White
);
263 setDiffuse(CRGBA::White
);
264 setSpecular(CRGBA::Black
);
267 // All states of material are modified.
268 _Touched
= IDRV_TOUCHED_ALL
;
270 if ((_Flags
& IDRV_MAT_USER_TEX_MAT_ALL
)) // are there user textrue coordinates matrix ?
272 CUniquePtr
<CUserTexMat
> newPtr(new CUserTexMat
); // create new
273 //std::swap(_TexUserMat, newPtr); // replace old
274 _TexUserMat
= CUniquePtrMove(newPtr
);
280 for(uint i
=0; i
< IDRV_MAT_MAXTEXTURES
; ++i
)
282 if (isUserTexMatEnabled(i
))
284 f
.serial(_TexUserMat
->TexMat
[i
]);
292 // ***************************************************************************
293 void CMaterial::setShader(TShader val
)
295 // First, reset all textures.
296 uint nTexts
= IDRV_MAT_MAXTEXTURES
;
297 // If user color or lightmap, set only the 1st.
298 if(_ShaderType
==LightMap
|| _ShaderType
==UserColor
)
301 for(uint i
=0;i
<nTexts
;i
++)
304 // If userColor, use TexEnv caps (we got it, so use it :) ).
305 if(val
== CMaterial::UserColor
)
307 // force normal, to setup TexEnvMode correclty.
308 _ShaderType
=CMaterial::Normal
;
310 // First stage, interpolate Constant and texture with Alpha of texture.
311 texEnvOpRGB(0, InterpolateTexture
);
312 texEnvArg0RGB(0, Texture
, SrcColor
);
313 texEnvArg1RGB(0, Constant
, SrcColor
);
314 // And just use Alpha Diffuse.
315 texEnvOpAlpha(0, Replace
);
316 texEnvArg0Alpha(0, Previous
, SrcAlpha
);
318 // Second stage, modulate result with diffuse color.
319 texEnvOpRGB(1, Modulate
);
320 texEnvArg0RGB(1, Previous
, SrcColor
);
321 texEnvArg1RGB(1, Diffuse
, SrcColor
);
322 // And just use Alpha Diffuse.
323 texEnvOpAlpha(1, Replace
);
324 texEnvArg0Alpha(1, Previous
, SrcAlpha
);
328 _Touched
|=IDRV_TOUCHED_SHADER
;
332 // ***************************************************************************
333 void CMaterial::setTexture(uint8 n
, ITexture
* ptex
)
335 nlassert(n
<IDRV_MAT_MAXTEXTURES
);
337 // User Color material?
338 if( _ShaderType
== CMaterial::UserColor
)
340 // user color. Only texture 0 can be set.
343 // Affect the 2 first textures.
346 _Touched
|=IDRV_TOUCHED_TEX
[0];
347 _Touched
|=IDRV_TOUCHED_TEX
[1];
349 else if( _ShaderType
== CMaterial::LightMap
)
351 // Only texture 0 can be set.
354 _Touched
|=IDRV_TOUCHED_TEX
[n
];
360 _Touched
|=IDRV_TOUCHED_TEX
[n
];
365 // ***************************************************************************
366 void CMaterial::flushTextures (IDriver
&driver
, uint selectedTexture
)
369 for (uint tex
=0; tex
<IDRV_MAT_MAXTEXTURES
; tex
++)
374 // Select the good texture
375 _Textures
[tex
]->selectTexture (selectedTexture
);
377 // Force setup texture
378 driver
.setupTexture (*_Textures
[tex
]);
382 // If Lightmap material
383 if(_ShaderType
==LightMap
)
386 for (uint lmap
=0; lmap
<_LightMaps
.size(); lmap
++)
389 if(_LightMaps
[lmap
].Texture
)
391 // Force setup texture
392 driver
.setupTexture (*_LightMaps
[lmap
].Texture
);
400 // ***************************************************************************
401 void CMaterial::setLightMap(uint lmapId
, ITexture
*lmap
)
403 nlassert(_ShaderType
==CMaterial::LightMap
);
404 if(lmapId
>=_LightMaps
.size())
405 _LightMaps
.resize(lmapId
+1);
406 _LightMaps
[lmapId
].Texture
= lmap
;
408 _Touched
|=IDRV_TOUCHED_LIGHTMAP
;
411 // ***************************************************************************
412 ITexture
*CMaterial::getLightMap(uint lmapId
) const
414 nlassert(_ShaderType
==CMaterial::LightMap
);
415 if(lmapId
<_LightMaps
.size())
416 return _LightMaps
[lmapId
].Texture
;
421 // ***************************************************************************
422 void CMaterial::setLightMapFactor(uint lmapId
, CRGBA factor
)
424 if (_ShaderType
==CMaterial::LightMap
)
426 if(lmapId
>=_LightMaps
.size())
427 _LightMaps
.resize(lmapId
+1);
428 _LightMaps
[lmapId
].Factor
= factor
;
430 _Touched
|=IDRV_TOUCHED_LIGHTMAP
;
434 // ***************************************************************************
435 void CMaterial::setLMCColors(uint lmapId
, CRGBA ambColor
, CRGBA diffColor
)
437 if (_ShaderType
==CMaterial::LightMap
)
439 if(lmapId
>=_LightMaps
.size())
440 _LightMaps
.resize(lmapId
+1);
441 _LightMaps
[lmapId
].LMCAmbient
= ambColor
;
442 _LightMaps
[lmapId
].LMCDiffuse
= diffColor
;
444 _Touched
|=IDRV_TOUCHED_LIGHTMAP
;
448 // ***************************************************************************
449 // DEPRECATED VERSION
450 void CMaterial::CLightMap::serial(NLMISC::IStream
&f
)
453 // Serial texture descriptor.
454 Texture
.serialPolyPtr(f
);
457 // ***************************************************************************
458 void CMaterial::CLightMap::serial2(NLMISC::IStream
&f
)
460 sint ver
= f
.serialVersion(1);
463 f
.serial(LMCDiffuse
);
465 f
.serial(LMCAmbient
);
466 // Serial texture descriptor.
467 Texture
.serialPolyPtr(f
);
472 // ***************************************************************************
473 void CMaterial::enableTexAddrMode(bool enable
/*= true*/)
477 if (!(_Flags
& IDRV_MAT_TEX_ADDR
))
479 _Flags
|= IDRV_MAT_TEX_ADDR
;
480 for (uint32 k
= 0; k
< IDRV_MAT_MAXTEXTURES
; ++k
)
482 _TexAddrMode
[k
] = (uint8
) TextureOff
;
488 _Flags
&= ~IDRV_MAT_TEX_ADDR
;
492 // ***************************************************************************
493 bool CMaterial::texAddrEnabled() const
495 return( _Flags
& IDRV_MAT_TEX_ADDR
) != 0;
498 // ***************************************************************************
499 void CMaterial::setTexAddressingMode(uint8 stage
, TTexAddressingMode mode
)
501 nlassert(_Flags
& IDRV_MAT_TEX_ADDR
);
502 nlassert(stage
< IDRV_MAT_MAXTEXTURES
);
503 nlassert(mode
< TexAddrCount
);
504 _TexAddrMode
[stage
] = (uint8
) mode
;
508 // ***************************************************************************
509 CMaterial::TTexAddressingMode
CMaterial::getTexAddressingMode(uint8 stage
)
511 nlassert(_Flags
& IDRV_MAT_TEX_ADDR
);
512 nlassert(stage
< IDRV_MAT_MAXTEXTURES
);
513 return (TTexAddressingMode
) _TexAddrMode
[stage
];
516 // ***************************************************************************
517 void CMaterial::decompUserTexMat(uint stage
, float &uTrans
, float &vTrans
, float &wRot
, float &uScale
, float &vScale
)
519 nlassert(stage
< IDRV_MAT_MAXTEXTURES
);
520 nlassert(isUserTexMatEnabled(stage
)); // must activate animated texture matrix for this stage
521 CMatrix convMat
; // exported v are already inverted (todo: optim this...)
522 convMat
.setRot(CVector::I
, -CVector::J
, CVector::K
);
523 convMat
.setPos(CVector::J
);
525 const NLMISC::CMatrix texMat
= convMat
* _TexUserMat
->TexMat
[stage
] * convMat
;
526 /// find the rotation around w
527 NLMISC::CVector i
= texMat
.getI();
528 NLMISC::CVector j
= texMat
.getJ();
529 uScale
= sqrtf(i
.x
* i
.x
+ j
.x
* j
.x
);
530 vScale
= sqrtf(i
.y
* i
.y
+ j
.y
* j
.y
);
534 float angle
= acosf(i
.x
/ i
.norm());
537 angle
= 2.f
* (float) NLMISC::Pi
- angle
;
543 InvSR
.setRot(texMat
.getI(), texMat
.getJ(), texMat
.getK());
545 CVector
half(0.5f
, 0.5f
, 0.f
);
546 CVector offset
= half
+ InvSR
* (texMat
.getPos() -half
);
551 // ***************************************************************************
552 void CMaterial::selectTextureSet(uint index
)
554 for (uint k
= 0; k
< IDRV_MAT_MAXTEXTURES
; ++k
)
556 if (_Textures
[k
] != NULL
) _Textures
[k
]->selectTexture(index
);
560 // ***************************************************************************
562 IMaterialDrvInfos::~IMaterialDrvInfos()
564 _Driver
->removeMatDrvInfoPtr(_DriverIterator
);
568 // ***************************************************************************
569 uint
CMaterial::getNumUsedTextureStages() const
571 for(uint k
= 0; k
< IDRV_MAT_MAXTEXTURES
; ++k
)
573 if (!_Textures
[k
]) return k
;
575 return IDRV_MAT_MAXTEXTURES
;
578 // ***************************************************************************
579 bool CMaterial::isSupportedByDriver(IDriver
&drv
, bool forceBaseCaps
) const
581 uint numTexStages
= drv
.getNbTextureStages();
582 // special case for radeon : though 3 stages are supported, do as if there were only 2, because of the texEnvColor feature
583 // not managed in Direct3D : emulation is provided, but for no more than 2 constants (and if diffuse is not used)
584 if (numTexStages
== 3) numTexStages
= 2;
585 if (forceBaseCaps
) numTexStages
= std::min(numTexStages
, (uint
) 2);
590 if (getNumUsedTextureStages() > numTexStages
) return false;
591 // see if each tex env is supported
592 for(uint k
= 0; k
< IDRV_MAT_MAXTEXTURES
; ++k
)
596 switch(getTexEnvOpRGB(k
))
598 case InterpolateConstant
: if (!drv
.supportBlendConstantColor()) return false;
599 case EMBM
: if (forceBaseCaps
|| !drv
.supportEMBM() || !drv
.isEMBMSupportedAtStage(k
)) return false;
600 case Mad
: if (!drv
.supportMADOperator()) return false;
603 switch(getTexEnvOpAlpha(k
))
605 case InterpolateConstant
: if (!drv
.supportBlendConstantColor()) return false;
606 case EMBM
: if (forceBaseCaps
|| !drv
.supportEMBM() || !drv
.isEMBMSupportedAtStage(k
)) return false;
607 case Mad
: if (!drv
.supportMADOperator()) return false;
615 case Bump
: return false; // not impl.
616 case UserColor
: return true;
617 case LightMap
: return true;
618 case Specular
: return true;
619 case Caustics
: return false;
620 case PerPixelLighting
: return drv
.supportPerPixelLighting(true);
621 case PerPixelLightingNoSpec
: return drv
.supportPerPixelLighting(false);
622 case Cloud
: return true;
623 case Water
: return true;
625 nlassert(0); // unknown shader, must complete