mirror of
https://port.numenaute.org/aleajactaest/khanat-opennel-code.git
synced 2024-11-28 01:56:24 +00:00
628 lines
16 KiB
C++
628 lines
16 KiB
C++
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
|
// 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 <http://www.gnu.org/licenses/>.
|
|
|
|
#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<IDRV_MAT_MAXTEXTURES;i++)
|
|
setTexture((uint8)i ,NULL);
|
|
setZBias(0);
|
|
setZFunc(lessequal);
|
|
setZWrite(true);
|
|
setBlend(false);
|
|
setAlphaTestThreshold(0.5f);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
|
|
void CMaterial::initLighted()
|
|
{
|
|
initUnlit();
|
|
setLighting(true);
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
CMaterial &CMaterial::operator=(const CMaterial &mat)
|
|
{
|
|
_ShaderType= mat._ShaderType;
|
|
_Flags= mat._Flags;
|
|
_SrcBlend= mat._SrcBlend;
|
|
_DstBlend= mat._DstBlend;
|
|
_ZFunction= mat._ZFunction;
|
|
_ZBias= mat._ZBias;
|
|
_Color= mat._Color;
|
|
_Emissive= mat._Emissive;
|
|
_Ambient= mat._Ambient;
|
|
_Diffuse= mat._Diffuse;
|
|
_Specular= mat._Specular;
|
|
_Shininess= mat._Shininess;
|
|
_AlphaTestThreshold= mat._AlphaTestThreshold;
|
|
_TexCoordGenMode= mat._TexCoordGenMode;
|
|
|
|
for(uint32 i=0;i<IDRV_MAT_MAXTEXTURES;i++)
|
|
{
|
|
_Textures[i]= mat._Textures[i];
|
|
_TexEnvs[i]= mat._TexEnvs[i];
|
|
_TexAddrMode[i] = mat._TexAddrMode[i];
|
|
}
|
|
|
|
// copy lightmaps.
|
|
_LightMaps= mat._LightMaps;
|
|
_LightMapsMulx2= mat._LightMapsMulx2;
|
|
|
|
// copy texture matrix if there.
|
|
if (mat._TexUserMat.get())
|
|
{
|
|
std::auto_ptr<CUserTexMat> 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<IDRV_MAT_MAXTEXTURES;i++)
|
|
{
|
|
// Serial texture descriptor.
|
|
_Textures[i].serialPolyPtr(f);
|
|
|
|
// Read texture environnement, or setup them.
|
|
if(ver>=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<IDRV_MAT_MAXTEXTURES;i++)
|
|
{
|
|
f.serial(_TexAddrMode[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(f.isReading())
|
|
{
|
|
// Converte Deprecated DEFMAT to std Mat.
|
|
if(_Flags & IDRV_MAT_DEFMAT)
|
|
{
|
|
setEmissive(CRGBA::Black);
|
|
setAmbient(CRGBA::White);
|
|
setDiffuse(CRGBA::White);
|
|
setSpecular(CRGBA::Black);
|
|
}
|
|
|
|
// All states of material are modified.
|
|
_Touched= IDRV_TOUCHED_ALL;
|
|
|
|
if ((_Flags & IDRV_MAT_USER_TEX_MAT_ALL)) // are there user textrue coordinates matrix ?
|
|
{
|
|
std::auto_ptr<CUserTexMat> 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;i<nTexts;i++)
|
|
setTexture(i ,NULL);
|
|
|
|
// If userColor, use TexEnv caps (we got it, so use it :) ).
|
|
if(val== CMaterial::UserColor)
|
|
{
|
|
// force normal, to setup TexEnvMode correclty.
|
|
_ShaderType=CMaterial::Normal;
|
|
|
|
// First stage, interpolate Constant and texture with Alpha of texture.
|
|
texEnvOpRGB(0, InterpolateTexture);
|
|
texEnvArg0RGB(0, Texture, SrcColor);
|
|
texEnvArg1RGB(0, Constant, SrcColor);
|
|
// And just use Alpha Diffuse.
|
|
texEnvOpAlpha(0, Replace);
|
|
texEnvArg0Alpha(0, Previous, SrcAlpha);
|
|
|
|
// Second stage, modulate result with diffuse color.
|
|
texEnvOpRGB(1, Modulate);
|
|
texEnvArg0RGB(1, Previous, SrcColor);
|
|
texEnvArg1RGB(1, Diffuse, SrcColor);
|
|
// And just use Alpha Diffuse.
|
|
texEnvOpAlpha(1, Replace);
|
|
texEnvArg0Alpha(1, Previous, SrcAlpha);
|
|
}
|
|
|
|
_ShaderType= val;
|
|
_Touched|=IDRV_TOUCHED_SHADER;
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CMaterial::setTexture(uint8 n, ITexture* ptex)
|
|
{
|
|
nlassert(n<IDRV_MAT_MAXTEXTURES);
|
|
|
|
// User Color material?
|
|
if( _ShaderType== CMaterial::UserColor)
|
|
{
|
|
// user color. Only texture 0 can be set.
|
|
nlassert( n==0 );
|
|
|
|
// Affect the 2 first textures.
|
|
_Textures[0]=ptex;
|
|
_Textures[1]=ptex;
|
|
_Touched|=IDRV_TOUCHED_TEX[0];
|
|
_Touched|=IDRV_TOUCHED_TEX[1];
|
|
}
|
|
else if( _ShaderType== CMaterial::LightMap)
|
|
{
|
|
// Only texture 0 can be set.
|
|
nlassert( n==0 );
|
|
_Textures[n]=ptex;
|
|
_Touched|=IDRV_TOUCHED_TEX[n];
|
|
}
|
|
// Normal material?
|
|
else
|
|
{
|
|
_Textures[n]=ptex;
|
|
_Touched|=IDRV_TOUCHED_TEX[n];
|
|
}
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CMaterial::flushTextures (IDriver &driver, uint selectedTexture)
|
|
{
|
|
// For each textures
|
|
for (uint tex=0; tex<IDRV_MAT_MAXTEXTURES; tex++)
|
|
{
|
|
// Texture exist ?
|
|
if (_Textures[tex])
|
|
{
|
|
// Select the good texture
|
|
_Textures[tex]->selectTexture (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;
|
|
}
|
|
|
|
}
|
|
|