Linux multi-monitor fullscreen support
[ryzomcore.git] / nel / src / 3d / material.cpp
blob3cae5dd3084b9ec7cf6e753c85e50efe6483b441
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
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.
8 //
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/>.
17 #include "std3d.h"
19 #include "nel/3d/material.h"
20 #include "nel/3d/texture.h"
21 #include "nel/3d/driver.h"
22 #include "nel/misc/stream.h"
24 using namespace std;
25 using namespace NLMISC;
27 #ifdef DEBUG_NEW
28 #define new DEBUG_NEW
29 #endif
31 namespace NL3D
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 * ***********************************************/
41 _Touched= 0;
42 _Flags= IDRV_MAT_ZWRITE;
43 // Must init All the flags by default.
44 _ShaderType= Normal;
45 _SrcBlend= srcalpha;
46 _DstBlend= invsrcalpha;
47 _ZFunction= lessequal;
48 _ZBias= 0;
49 _Color.set(255,255,255,255);
50 _StainedGlassWindow = false;
51 _AlphaTestThreshold= 0.5f;
52 _TexCoordGenMode= 0;
53 _LightMapsMulx2= false;
56 // ***************************************************************************
57 void CMaterial::initUnlit()
59 setShader(Normal);
60 setLighting(false);
61 setColor(CRGBA(255,255,255,255));
62 for(uint32 i=0;i<IDRV_MAT_MAXTEXTURES;i++)
63 setTexture((uint8)i ,NULL);
64 setZBias(0);
65 setZFunc(lessequal);
66 setZWrite(true);
67 setBlend(false);
68 setAlphaTestThreshold(0.5f);
71 // ***************************************************************************
73 void CMaterial::initLighted()
75 initUnlit();
76 setLighting(true);
80 // ***************************************************************************
81 CMaterial &CMaterial::operator=(const CMaterial &mat)
83 _ShaderType= mat._ShaderType;
84 _Flags= mat._Flags;
85 _SrcBlend= mat._SrcBlend;
86 _DstBlend= mat._DstBlend;
87 _ZFunction= mat._ZFunction;
88 _ZBias= mat._ZBias;
89 _Color= mat._Color;
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];
105 // copy lightmaps.
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);
116 else
118 _TexUserMat.reset();
121 // Must do not copy drv info.
123 // All states of material is modified.
124 _Touched= IDRV_TOUCHED_ALL;
126 return *this;
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.
139 _MatDrvInfo.kill();
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 * ***********************************************/
152 Version 9:
153 - Added support for third operand (for Mad operator)
154 Version 8:
155 - Serial _TexCoordGenMode
156 Version 7:
157 - Lightmap color and Mulx2
158 Version 6:
159 - Texture matrix animation
160 Version 5:
161 - AlphaTest threshold
162 Version 4:
163 - Texture Addressing modes
164 Version 3:
165 - LightMaps.
166 Version 2:
167 - Shininess.
168 Version 1:
169 - texture environement.
170 Version 0:
171 - base version.
174 sint ver= f.serialVersion(9);
175 // For the version <=1:
176 nlassert(IDRV_MAT_MAXTEXTURES==4);
178 f.serialEnum(_ShaderType);
179 f.serial(_Flags);
180 f.serialEnum(_SrcBlend);
181 f.serialEnum(_DstBlend);
182 f.serialEnum(_ZFunction);
183 f.serial(_ZBias);
184 f.serial(_Color);
185 f.serial(_Emissive, _Ambient, _Diffuse, _Specular);
186 if(ver>=2)
188 f.serial(_Shininess);
190 if(ver>=5)
192 f.serial(_AlphaTestThreshold);
194 if(ver>=8)
196 f.serial(_TexCoordGenMode);
198 else
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.
208 if(ver>=1)
210 _TexEnvs[i].serial(f, ver >= 9 ? 1 : 0);
212 else
214 // Else setup as default behavior, like before...
215 if(f.isReading())
216 _TexEnvs[i].setDefault();
220 if(ver>=3)
222 if(ver>=7)
224 uint32 n;
225 if (f.isReading())
227 f.serial(n);
228 _LightMaps.resize(n);
230 else
232 n = (uint32)_LightMaps.size();
233 f.serial(n);
235 for (uint32 i = 0; i < n; ++i)
236 _LightMaps[i].serial2(f);
237 f.serial(_LightMapsMulx2);
239 else
241 f.serialCont(_LightMaps);
245 if (ver >= 4)
247 if (_Flags & IDRV_MAT_TEX_ADDR)
249 for(uint32 i=0;i<IDRV_MAT_MAXTEXTURES;i++)
251 f.serial(_TexAddrMode[i]);
256 if(f.isReading())
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);
278 if (ver >= 6)
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)
299 nTexts=1;
300 // reset all needed
301 for(uint i=0;i<nTexts;i++)
302 setTexture(i ,NULL);
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);
327 _ShaderType= val;
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.
341 nlassert( n==0 );
343 // Affect the 2 first textures.
344 _Textures[0]=ptex;
345 _Textures[1]=ptex;
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.
352 nlassert( n==0 );
353 _Textures[n]=ptex;
354 _Touched|=IDRV_TOUCHED_TEX[n];
356 // Normal material?
357 else
359 _Textures[n]=ptex;
360 _Touched|=IDRV_TOUCHED_TEX[n];
365 // ***************************************************************************
366 void CMaterial::flushTextures (IDriver &driver, uint selectedTexture)
368 // For each textures
369 for (uint tex=0; tex<IDRV_MAT_MAXTEXTURES; tex++)
371 // Texture exist ?
372 if (_Textures[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)
385 // For each lightmap
386 for (uint lmap=0; lmap<_LightMaps.size(); lmap++)
388 // Texture exist?
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;
417 else
418 return NULL;
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)
452 f.serial(Factor);
453 // Serial texture descriptor.
454 Texture.serialPolyPtr(f);
457 // ***************************************************************************
458 void CMaterial::CLightMap::serial2(NLMISC::IStream &f)
460 sint ver= f.serialVersion(1);
462 f.serial(Factor);
463 f.serial(LMCDiffuse);
464 if(ver>=1)
465 f.serial(LMCAmbient);
466 // Serial texture descriptor.
467 Texture.serialPolyPtr(f);
472 // ***************************************************************************
473 void CMaterial::enableTexAddrMode(bool enable /*= true*/)
475 if (enable)
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;
486 else
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);
532 i.normalize();
534 float angle = acosf(i.x / i.norm());
535 if (i.y < 0)
537 angle = 2.f * (float) NLMISC::Pi - angle;
539 wRot = angle;
541 // compute position
542 CMatrix InvSR;
543 InvSR.setRot(texMat.getI(), texMat.getJ(), texMat.getK());
544 InvSR.invert();
545 CVector half(0.5f, 0.5f, 0.f);
546 CVector offset = half + InvSR * (texMat.getPos() -half);
547 uTrans = - offset.x;
548 vTrans = - offset.y;
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);
586 switch(getShader())
588 case Normal:
590 if (getNumUsedTextureStages() > numTexStages) return false;
591 // see if each tex env is supported
592 for(uint k = 0; k < IDRV_MAT_MAXTEXTURES; ++k)
594 if (getTexture(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;
601 default: break;
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;
608 default: break;
612 return true;
614 break;
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;
624 default:
625 nlassert(0); // unknown shader, must complete
627 return false;