// NeL - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "std3d.h" #include "nel/3d/material.h" #include "nel/3d/texture.h" #include "nel/3d/shader.h" #include "nel/3d/driver.h" #include "nel/misc/stream.h" using namespace std; using namespace NLMISC; namespace NL3D { // *************************************************************************** CMaterial::CMaterial() { /* *********************************************** * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance * It can be loaded/called through CAsyncFileManager for instance * ***********************************************/ _Touched= 0; _Flags= IDRV_MAT_ZWRITE; // Must init All the flags by default. _ShaderType= Normal; _SrcBlend= srcalpha; _DstBlend= invsrcalpha; _ZFunction= lessequal; _ZBias= 0; _Color.set(255,255,255,255); _StainedGlassWindow = false; _AlphaTestThreshold= 0.5f; _TexCoordGenMode= 0; _LightMapsMulx2= false; } // *************************************************************************** void CMaterial::initUnlit() { setShader(Normal); setLighting(false); setColor(CRGBA(255,255,255,255)); for(uint32 i=0;i texMatClone( new CUserTexMat(*(mat._TexUserMat))); // make cpy //std::swap(texMatClone, _TexUserMat); // swap with old _TexUserMat = texMatClone; } else { _TexUserMat.reset(); } // Must do not copy drv info. // All states of material is modified. _Touched= IDRV_TOUCHED_ALL; return *this; } // *************************************************************************** CMaterial::~CMaterial() { /* *********************************************** * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance * It can be loaded/called through CAsyncFileManager for instance * ***********************************************/ // Must kill the drv mirror of this material. _MatDrvInfo.kill(); } // *************************************************************************** void CMaterial::serial(NLMISC::IStream &f) { /* *********************************************** * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance * It can be loaded/called through CAsyncFileManager for instance * ***********************************************/ /* Version 9: - Added support for third operand (for Mad operator) Version 8: - Serial _TexCoordGenMode Version 7: - Lightmap color and Mulx2 Version 6: - Texture matrix animation Version 5: - AlphaTest threshold Version 4: - Texture Addressing modes Version 3: - LightMaps. Version 2: - Shininess. Version 1: - texture environement. Version 0: - base version. */ sint ver= f.serialVersion(9); // For the version <=1: nlassert(IDRV_MAT_MAXTEXTURES==4); f.serialEnum(_ShaderType); f.serial(_Flags); f.serialEnum(_SrcBlend); f.serialEnum(_DstBlend); f.serialEnum(_ZFunction); f.serial(_ZBias); f.serial(_Color); f.serial(_Emissive, _Ambient, _Diffuse, _Specular); if(ver>=2) { f.serial(_Shininess); } if(ver>=5) { f.serial(_AlphaTestThreshold); } if(ver>=8) { f.serial(_TexCoordGenMode); } else _TexCoordGenMode = 0; for(uint32 i=0;i=1) { _TexEnvs[i].serial(f, ver >= 9 ? 1 : 0); } else { // Else setup as default behavior, like before... if(f.isReading()) _TexEnvs[i].setDefault(); } } if(ver>=3) { if(ver>=7) { uint32 n; if (f.isReading()) { f.serial(n); _LightMaps.resize(n); } else { n = (uint32)_LightMaps.size(); f.serial(n); } for (uint32 i = 0; i < n; ++i) _LightMaps[i].serial2(f); f.serial(_LightMapsMulx2); } else { f.serialCont(_LightMaps); } } if (ver >= 4) { if (_Flags & IDRV_MAT_TEX_ADDR) { for(uint32 i=0;i newPtr(new CUserTexMat); // create new //std::swap(_TexUserMat, newPtr); // replace old _TexUserMat = newPtr; } } if (ver >= 6) { for(uint i=0; i < IDRV_MAT_MAXTEXTURES; ++i) { if (isUserTexMatEnabled(i)) { f.serial(_TexUserMat->TexMat[i]); } } } } // *************************************************************************** void CMaterial::setShader(TShader val) { // First, reset all textures. uint nTexts= IDRV_MAT_MAXTEXTURES; // If user color or lightmap, set only the 1st. if(_ShaderType==LightMap || _ShaderType==UserColor) nTexts=1; // reset all needed for(uint i=0;iselectTexture (selectedTexture); // Force setup texture driver.setupTexture (*_Textures[tex]); } } // If Lightmap material if(_ShaderType==LightMap) { // For each lightmap for (uint lmap=0; lmap<_LightMaps.size(); lmap++) { // Texture exist? if(_LightMaps[lmap].Texture) { // Force setup texture driver.setupTexture (*_LightMaps[lmap].Texture); } } } } // *************************************************************************** void CMaterial::setLightMap(uint lmapId, ITexture *lmap) { nlassert(_ShaderType==CMaterial::LightMap); if(lmapId>=_LightMaps.size()) _LightMaps.resize(lmapId+1); _LightMaps[lmapId].Texture= lmap; _Touched|=IDRV_TOUCHED_LIGHTMAP; } // *************************************************************************** ITexture *CMaterial::getLightMap(uint lmapId) const { nlassert(_ShaderType==CMaterial::LightMap); if(lmapId<_LightMaps.size()) return _LightMaps[lmapId].Texture; else return NULL; } // *************************************************************************** void CMaterial::setLightMapFactor(uint lmapId, CRGBA factor) { if (_ShaderType==CMaterial::LightMap) { if(lmapId>=_LightMaps.size()) _LightMaps.resize(lmapId+1); _LightMaps[lmapId].Factor= factor; _Touched|=IDRV_TOUCHED_LIGHTMAP; } } // *************************************************************************** void CMaterial::setLMCColors(uint lmapId, CRGBA ambColor, CRGBA diffColor) { if (_ShaderType==CMaterial::LightMap) { if(lmapId>=_LightMaps.size()) _LightMaps.resize(lmapId+1); _LightMaps[lmapId].LMCAmbient= ambColor; _LightMaps[lmapId].LMCDiffuse= diffColor; _Touched|=IDRV_TOUCHED_LIGHTMAP; } } // *************************************************************************** // DEPRECATED VERSION void CMaterial::CLightMap::serial(NLMISC::IStream &f) { f.serial(Factor); // Serial texture descriptor. Texture.serialPolyPtr(f); } // *************************************************************************** void CMaterial::CLightMap::serial2(NLMISC::IStream &f) { sint ver= f.serialVersion(1); f.serial(Factor); f.serial(LMCDiffuse); if(ver>=1) f.serial(LMCAmbient); // Serial texture descriptor. Texture.serialPolyPtr(f); } // *************************************************************************** void CMaterial::enableTexAddrMode(bool enable /*= true*/) { if (enable) { if (!(_Flags & IDRV_MAT_TEX_ADDR)) { _Flags |= IDRV_MAT_TEX_ADDR; for (uint32 k = 0; k < IDRV_MAT_MAXTEXTURES; ++k) { _TexAddrMode[k] = (uint8) TextureOff; } } } else { _Flags &= ~IDRV_MAT_TEX_ADDR; } } // *************************************************************************** bool CMaterial::texAddrEnabled() const { return( _Flags & IDRV_MAT_TEX_ADDR) != 0; } // *************************************************************************** void CMaterial::setTexAddressingMode(uint8 stage, TTexAddressingMode mode) { nlassert(_Flags & IDRV_MAT_TEX_ADDR); nlassert(stage < IDRV_MAT_MAXTEXTURES); nlassert(mode < TexAddrCount); _TexAddrMode[stage] = (uint8) mode; } // *************************************************************************** CMaterial::TTexAddressingMode CMaterial::getTexAddressingMode(uint8 stage) { nlassert(_Flags & IDRV_MAT_TEX_ADDR); nlassert(stage < IDRV_MAT_MAXTEXTURES); return (TTexAddressingMode) _TexAddrMode[stage]; } // *************************************************************************** void CMaterial::decompUserTexMat(uint stage, float &uTrans, float &vTrans, float &wRot, float &uScale, float &vScale) { nlassert(stage < IDRV_MAT_MAXTEXTURES); nlassert(isUserTexMatEnabled(stage)); // must activate animated texture matrix for this stage CMatrix convMat; // exported v are already inverted (todo: optim this...) convMat.setRot(CVector::I, -CVector::J, CVector::K); convMat.setPos(CVector::J); const NLMISC::CMatrix texMat = convMat * _TexUserMat->TexMat[stage] * convMat; /// find the rotation around w NLMISC::CVector i = texMat.getI(); NLMISC::CVector j = texMat.getJ(); uScale = sqrtf(i.x * i.x + j.x * j.x); vScale = sqrtf(i.y * i.y + j.y * j.y); // i.normalize(); // float angle = acosf(i.x / i.norm()); if (i.y < 0) { angle = 2.f * (float) NLMISC::Pi - angle; } wRot = angle; // compute position CMatrix InvSR; InvSR.setRot(texMat.getI(), texMat.getJ(), texMat.getK()); InvSR.invert(); CVector half(0.5f, 0.5f, 0.f); CVector offset = half + InvSR * (texMat.getPos() -half); uTrans = - offset.x; vTrans = - offset.y; } // *************************************************************************** void CMaterial::selectTextureSet(uint index) { for (uint k = 0; k < IDRV_MAT_MAXTEXTURES; ++k) { if (_Textures[k] != NULL) _Textures[k]->selectTexture(index); } } // *************************************************************************** IMaterialDrvInfos::~IMaterialDrvInfos() { _Driver->removeMatDrvInfoPtr(_DriverIterator); } // *************************************************************************** uint CMaterial::getNumUsedTextureStages() const { for(uint k = 0; k < IDRV_MAT_MAXTEXTURES; ++k) { if (!_Textures[k]) return k; } return IDRV_MAT_MAXTEXTURES; } // *************************************************************************** bool CMaterial::isSupportedByDriver(IDriver &drv, bool forceBaseCaps) const { uint numTexStages = drv.getNbTextureStages(); // special case for radeon : though 3 stages are supported, do as if there were only 2, because of the texEnvColor feature // not managed in Direct3D : emulation is provided, but for no more than 2 constants (and if diffuse is not used) if (numTexStages == 3) numTexStages = 2; if (forceBaseCaps) numTexStages = std::min(numTexStages, (uint) 2); switch(getShader()) { case Normal: { if (getNumUsedTextureStages() > numTexStages) return false; // see if each tex env is supported for(uint k = 0; k < IDRV_MAT_MAXTEXTURES; ++k) { if (getTexture(k)) { switch(getTexEnvOpRGB(k)) { case InterpolateConstant: if (!drv.supportBlendConstantColor()) return false; case EMBM: if (forceBaseCaps || !drv.supportEMBM() || !drv.isEMBMSupportedAtStage(k)) return false; case Mad: if (!drv.supportMADOperator()) return false; default: break; } switch(getTexEnvOpAlpha(k)) { case InterpolateConstant: if (!drv.supportBlendConstantColor()) return false; case EMBM: if (forceBaseCaps || !drv.supportEMBM() || !drv.isEMBMSupportedAtStage(k)) return false; case Mad: if (!drv.supportMADOperator()) return false; default: break; } } } return true; } break; case Bump: return false; // not impl. case UserColor: return true; case LightMap: return true; case Specular: return true; case Caustics: return false; case PerPixelLighting: return drv.supportPerPixelLighting(true); case PerPixelLightingNoSpec: return drv.supportPerPixelLighting(false); case Cloud: return true; case Water: return true; default: nlassert(0); // unknown shader, must complete } return false; } }