// 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/ps_particle_basic.h"
#include "nel/3d/ps_macro.h"
#include "nel/3d/driver.h"
#include "nel/3d/texture_grouped.h"
#include "nel/3d/texture_bump.h"
#include "nel/3d/texture_mem.h"
#include "nel/3d/particle_system.h"
namespace NL3D
{
/////////////////////////////////
// CPSParticle implementation //
/////////////////////////////////
//=======================================
CPSParticle::CPSParticle() : _DisableAutoLOD(false),
_UsesGlobalColorLighting(false)
{
NL_PS_FUNC(CPSParticle_CPSParticle)
}
//=======================================
void CPSParticle::showTool()
{
NL_PS_FUNC(CPSParticle_showTool)
PARTICLES_CHECK_MEM;
CVector I = CVector::I;
CVector J = CVector::J;
const CVector tab[] = { 2 * J, I + J
, I + J, 2 * I + J
, 2 * I + J, I
, I, 2 * I - J
, 2 * I - J, - .5f * J
, - .5f * J, -2 * I - J
, -2 * I - J, - I
, - I, -2 * I + J
, -2 * I + J, - I + J
, - I + J, 2 * J
};
const uint tabSize = sizeof(tab) / (2 * sizeof(CVector));
const float sSize = 0.1f;
displayIcon2d(tab, tabSize, sSize);
PARTICLES_CHECK_MEM;
}
//=======================================
void CPSParticle::computeSrcStep(uint32 &step, uint &numToProcess)
{
NL_PS_FUNC(CPSParticle_computeSrcStep)
nlassert(_Owner && _Owner->getOwner());
const CParticleSystem &ps = *(_Owner->getOwner());
if (_DisableAutoLOD || !ps.isAutoLODEnabled() || !ps.isSharingEnabled() || _Owner->getSize() == 0) // Should Auto-LOD be used ?
{
step = (1 << 16);
numToProcess = _Owner->getSize();
}
else
{
float oneMinusLODRatio = ps.getOneMinusCurrentLODRatio();
float LODRatio = 1.f - oneMinusLODRatio;
if (LODRatio > ps.getAutoLODStartDistPercent())
{
float factor = (LODRatio - 1.f) / (ps.getAutoLODStartDistPercent() - 1.f);
NLMISC::clamp(factor, 0.f, 1.f);
float r = factor;
for (uint k = 1; k < ps.getAutoLODDegradationExponent(); ++k)
{
r *= factor;
}
numToProcess = (uint) (_Owner->getSize() * r);
if (numToProcess < 1) { numToProcess = 1; }
step = ps.getAutoLODMode() ? // skip or limit number, depending on the mode
(_Owner->getSize() << 16) / numToProcess : // skip particles
(1 << 16); // just display less particles
}
else
{
step = (1 << 16);
numToProcess = _Owner->getSize();
}
}
}
//////////////////////////////////////
// coloured particle implementation //
//////////////////////////////////////
//=======================================
void CPSColoredParticle::setColorScheme(CPSAttribMaker *col)
{
NL_PS_FUNC(CPSColoredParticle_setColorScheme)
nlassert(col);
delete _ColorScheme;
_ColorScheme = col;
if (getColorOwner() && col->hasMemory()) col->resize(getColorOwner()->getMaxSize(), getColorOwner()->getSize());
updateMatAndVbForColor();
}
//=======================================
void CPSColoredParticle::setColor(NLMISC::CRGBA col)
{
NL_PS_FUNC(CPSColoredParticle_setColor)
delete _ColorScheme;
_ColorScheme = NULL;
_Color = col;
updateMatAndVbForColor();
}
//=======================================
CPSColoredParticle::CPSColoredParticle() : _Color(CRGBA(255, 255, 255)), _ColorScheme(NULL)
{
NL_PS_FUNC(CPSColoredParticle_CPSColoredParticle)
}
//=======================================
CPSColoredParticle::~CPSColoredParticle()
{
NL_PS_FUNC(CPSColoredParticle_CPSColoredParticleDtor)
delete _ColorScheme;
}
//=======================================
void CPSColoredParticle::serialColorScheme(NLMISC::IStream &f) throw(NLMISC::EStream)
{
NL_PS_FUNC(CPSColoredParticle_IStream )
f.serialVersion(1);
if (f.isReading())
{
if (_ColorScheme)
{
delete _ColorScheme;
_ColorScheme = NULL;
}
}
bool useColorScheme = _ColorScheme != NULL;
f.serial(useColorScheme);
if (useColorScheme)
{
f.serialPolyPtr(_ColorScheme);
}
else
{
f.serial(_Color);
}
}
///////////////////////////////////
// sized particle implementation //
///////////////////////////////////
//=======================================
void CPSSizedParticle::setSizeScheme(CPSAttribMaker *size)
{
NL_PS_FUNC(CPSSizedParticle_setSizeScheme)
nlassert(size != NULL);
delete _SizeScheme;
_SizeScheme = size;
if (getSizeOwner() && size->hasMemory()) size->resize(getSizeOwner()->getMaxSize(), getSizeOwner()->getSize());
}
//=======================================
void CPSSizedParticle::setSize(float size)
{
NL_PS_FUNC(CPSSizedParticle_setSize)
delete _SizeScheme;
_SizeScheme = NULL;
_ParticleSize = size;
}
//=======================================
CPSSizedParticle::CPSSizedParticle() : _ParticleSize(0.3f), _SizeScheme(NULL)
{
NL_PS_FUNC(CPSSizedParticle_CPSSizedParticle)
}
//=======================================
CPSSizedParticle::~CPSSizedParticle()
{
NL_PS_FUNC(CPSSizedParticle_CPSSizedParticleDtor)
delete _SizeScheme;
}
//=======================================
void CPSSizedParticle::serialSizeScheme(NLMISC::IStream &f) throw(NLMISC::EStream)
{
NL_PS_FUNC(CPSSizedParticle_serialSizeScheme)
f.serialVersion(1);
if (f.isReading())
{
if (_SizeScheme)
{
delete _SizeScheme;
_SizeScheme = NULL;
}
}
bool useSizeScheme = _SizeScheme != NULL;
f.serial(useSizeScheme);
if (useSizeScheme)
{
f.serialPolyPtr(_SizeScheme);
}
else
{
f.serial(_ParticleSize);
}
};
/////////////////////////////////////
// rotated particle implementation //
/////////////////////////////////////
float CPSRotated2DParticle::_RotTable[256 * 4];
bool CPSRotated2DParticle::_InitializedRotTab = false;
///===================================================================================
void CPSRotated2DParticle::setAngle2DScheme(CPSAttribMaker *angle2DScheme)
{
NL_PS_FUNC(CPSRotated2DParticle_setAngle2DScheme)
nlassert(angle2DScheme);
delete _Angle2DScheme;
_Angle2DScheme = angle2DScheme;
if (getAngle2DOwner() && angle2DScheme->hasMemory()) angle2DScheme->resize(getAngle2DOwner()->getMaxSize(), getAngle2DOwner()->getSize());
}
///===================================================================================
void CPSRotated2DParticle::setAngle2D(float angle2DScheme)
{
NL_PS_FUNC(CPSRotated2DParticle_setAngle2D)
delete _Angle2DScheme;
_Angle2DScheme = NULL;
_Angle2D = angle2DScheme;
}
///===================================================================================
CPSRotated2DParticle::CPSRotated2DParticle() : _Angle2D(0), _Angle2DScheme(NULL)
{
NL_PS_FUNC(CPSRotated2DParticle_CPSRotated2DParticle)
}
///===================================================================================
CPSRotated2DParticle::~CPSRotated2DParticle()
{
NL_PS_FUNC(CPSRotated2DParticle_CPSRotated2DParticleDtor)
delete _Angle2DScheme;
}
///===================================================================================
void CPSRotated2DParticle::serialAngle2DScheme(NLMISC::IStream &f) throw(NLMISC::EStream)
{
NL_PS_FUNC(CPSRotated2DParticle_serialAngle2DScheme)
f.serialVersion(1);
if (f.isReading())
{
if (_Angle2DScheme)
{
delete _Angle2DScheme;
_Angle2DScheme = NULL;
}
}
bool useAngle2DScheme = _Angle2DScheme != NULL;
f.serial(useAngle2DScheme);
if (useAngle2DScheme)
{
f.serialPolyPtr(_Angle2DScheme);
}
else
{
f.serial(_Angle2D);
}
}
///===================================================================================
void CPSRotated2DParticle::initRotTable(void)
{
NL_PS_FUNC(CPSRotated2DParticle_initRotTable)
float *ptFloat = _RotTable;
for (uint32 k = 0; k < 256; ++k)
{
const float ca = (float) cos(k * (1.0f / 256.0f) * 2.0f * NLMISC::Pi);
const float sa = (float) sin(k * (1.0f / 256.0f) * 2.0f * NLMISC::Pi);
*ptFloat++ = -ca - sa;
*ptFloat++ = -sa + ca;
*ptFloat++ = ca - sa;
*ptFloat++ = sa + ca;
}
_InitializedRotTab = true;
}
//////////////////////////////////////
// textured particle implementation //
//////////////////////////////////////
///===================================================================================
void CPSTexturedParticle::setTextureIndexScheme(CPSAttribMaker *animOrder)
{
NL_PS_FUNC(CPSTexturedParticle_setTextureIndexScheme)
nlassert(animOrder);
nlassert(_TexGroup); // setTextureGroup must have been called before this
delete _TextureIndexScheme;
_TextureIndexScheme = animOrder;
if (getTextureIndexOwner() && animOrder->hasMemory()) animOrder->resize(getTextureIndexOwner()->getMaxSize(), getTextureIndexOwner()->getSize());
updateMatAndVbForTexture();
}
///===================================================================================
void CPSTexturedParticle::setTextureIndex(sint32 index)
{
NL_PS_FUNC(CPSTexturedParticle_setTextureIndex)
delete _TextureIndexScheme;
_TextureIndexScheme = NULL;
_TextureIndex = index;
}
///===================================================================================
void CPSTexturedParticle::setTextureGroup(NLMISC::CSmartPtr texGroup)
{
NL_PS_FUNC(CPSTexturedParticle_setTextureGroup)
nlassert(texGroup);
if (_Tex)
{
_Tex = NULL;
}
_TexGroup = texGroup;
updateMatAndVbForTexture();
}
///===================================================================================
void CPSTexturedParticle::setTexture(CSmartPtr tex)
{
NL_PS_FUNC(CPSTexturedParticle_setTexture)
delete _TextureIndexScheme;
_TextureIndexScheme = NULL;
_Tex = tex;
_TexGroup = NULL; // release any grouped texture if one was set before
updateMatAndVbForTexture();
}
///===================================================================================
CPSTexturedParticle::CPSTexturedParticle() : _TexGroup(NULL),
_TextureIndexScheme(NULL),
_TextureIndex(0)
{
NL_PS_FUNC(CPSTexturedParticle_CPSTexturedParticle)
}
///===================================================================================
CPSTexturedParticle::~CPSTexturedParticle()
{
NL_PS_FUNC(CPSTexturedParticle_CPSTexturedParticleDtor)
delete _TextureIndexScheme;
}
///===================================================================================
void CPSTexturedParticle::serialTextureScheme(NLMISC::IStream &f) throw(NLMISC::EStream)
{
NL_PS_FUNC(CPSTexturedParticle_serialTextureScheme)
f.serialVersion(1);
if (f.isReading())
{
if (_TextureIndexScheme)
{
delete _TextureIndexScheme;
_TextureIndexScheme = NULL;
_Tex = NULL;
_TexGroup = NULL;
}
}
bool useAnimatedTexture;
if (!f.isReading())
{
useAnimatedTexture = (_TexGroup != NULL);
}
f.serial(useAnimatedTexture);
if (useAnimatedTexture)
{
if (f.isReading())
{
CTextureGrouped *ptTex = NULL;
f.serialPolyPtr(ptTex);
_TexGroup = ptTex;
}
else
{
CTextureGrouped *ptTex = _TexGroup;
f.serialPolyPtr(ptTex);
}
bool useTextureIndexScheme = _TextureIndexScheme != NULL;
f.serial(useTextureIndexScheme);
if (useTextureIndexScheme)
{
f.serialPolyPtr(_TextureIndexScheme);
_TextureIndex = 0;
}
else
{
f.serial(_TextureIndex);
}
}
else
{
if (f.isReading())
{
ITexture *ptTex = NULL;
f.serialPolyPtr(ptTex);
_Tex = ptTex;
}
else
{
ITexture *ptTex = _Tex;
f.serialPolyPtr(ptTex);
}
}
}
////////////////////////////////////////////////////////
// CPSRotated3DPlaneParticle implementation //
////////////////////////////////////////////////////////
///===================================================================================
void CPSRotated3DPlaneParticle::setPlaneBasisScheme(CPSAttribMaker *basisMaker)
{
NL_PS_FUNC(CPSRotated3DPlaneParticle_setPlaneBasisScheme)
nlassert(basisMaker);
delete _PlaneBasisScheme;
_PlaneBasisScheme = basisMaker;
if (getPlaneBasisOwner() && basisMaker->hasMemory()) basisMaker->resize(getPlaneBasisOwner()->getMaxSize(), getPlaneBasisOwner()->getSize());
}
///===================================================================================
void CPSRotated3DPlaneParticle::setPlaneBasis(const CPlaneBasis &basis)
{
NL_PS_FUNC(CPSRotated3DPlaneParticle_setPlaneBasis)
delete _PlaneBasisScheme;
_PlaneBasisScheme = NULL;
_PlaneBasis = basis;
}
///===================================================================================
CPSRotated3DPlaneParticle::CPSRotated3DPlaneParticle() : _PlaneBasisScheme(NULL)
{
NL_PS_FUNC(CPSRotated3DPlaneParticle_CPSRotated3DPlaneParticle)
_PlaneBasis.X = CVector::I;
_PlaneBasis.Y = CVector::J;
}
///===================================================================================
CPSRotated3DPlaneParticle::~CPSRotated3DPlaneParticle()
{
NL_PS_FUNC(CPSRotated3DPlaneParticle_CPSRotated3DPlaneParticleDtor)
delete _PlaneBasisScheme;
}
///===================================================================================
void CPSRotated3DPlaneParticle::serialPlaneBasisScheme(NLMISC::IStream &f) throw(NLMISC::EStream)
{
NL_PS_FUNC(CPSRotated3DPlaneParticle_serialPlaneBasisScheme)
f.serialVersion(1);
f.serialPolyPtr(_PlaneBasisScheme);
bool usePlaneBasisScheme = _PlaneBasisScheme != NULL;
if (!usePlaneBasisScheme)
{
f.serial(_PlaneBasis);
}
}
////////////////////////////////
// CPSMaterial implementation //
////////////////////////////////
///===================================================================================
CPSMaterial::CPSMaterial()
{
NL_PS_FUNC(CPSMaterial_CPSMaterial)
_Mat.setBlend(true);
_Mat.setBlendFunc(CMaterial::one, CMaterial::one);
_Mat.setZWrite(false);
}
///===================================================================================
void CPSMaterial::serialMaterial(NLMISC::IStream &f) throw(NLMISC::EStream)
{
NL_PS_FUNC(CPSMaterial_IStream )
// version 3 : added zbias
sint ver = f.serialVersion(3);
TBlendingMode m = getBlendingMode();
f.serialEnum(m);
setBlendingMode(m);
if (ver == 2)
{
bool zTest = isZTestEnabled();
f.serial(zTest);
enableZTest(zTest);
}
else
if (ver >= 3)
{
// bit 0 : ztest enabled
// bit 1 : 1 if zbias is not 0, in this case zbias follows
uint8 flags = (isZTestEnabled() ? 1 : 0) | (getZBias() != 0.f ? 2 : 0);
f.serial(flags);
enableZTest((flags & 1) != 0);
if (flags & 2)
{
float zBias = getZBias();
f.serial(zBias);
setZBias(zBias);
}
}
}
///===================================================================================
void CPSMaterial::enableZTest(bool enabled)
{
NL_PS_FUNC(CPSMaterial_enableZTest)
_Mat.setZFunc(enabled ? CMaterial::less : CMaterial::always);
}
///===================================================================================
bool CPSMaterial::isZTestEnabled() const
{
NL_PS_FUNC(CPSMaterial_isZTestEnabled)
return _Mat.getZFunc() != CMaterial::always;
}
///===================================================================================
void CPSMaterial::setBlendingMode(CPSMaterial::TBlendingMode mode)
{
NL_PS_FUNC(CPSMaterial_setBlendingMode)
switch (mode)
{
case add:
_Mat.setBlend(true);
_Mat.setBlendFunc(CMaterial::one, CMaterial::one);
_Mat.setZWrite(false);
_Mat.setAlphaTest(false);
break;
case modulate:
_Mat.setBlend(true);
_Mat.setBlendFunc(CMaterial::zero, CMaterial::srccolor);
_Mat.setZWrite(false);
_Mat.setAlphaTest(false);
break;
case alphaBlend:
_Mat.setBlend(true);
_Mat.setBlendFunc(CMaterial::srcalpha, CMaterial::invsrcalpha);
_Mat.setZWrite(false);
_Mat.setAlphaTest(false);
break;
case alphaTest:
_Mat.setBlend(false);
_Mat.setZWrite(true);
_Mat.setAlphaTest(true);
break;
}
}
///===================================================================================
CPSMaterial::TBlendingMode CPSMaterial::getBlendingMode(void) const
{
NL_PS_FUNC(CPSMaterial_getBlendingMode)
if (_Mat.getBlend())
{
CMaterial::TBlend srcBlend = _Mat.getSrcBlend();
CMaterial::TBlend destBlend = _Mat.getDstBlend();
if (srcBlend == CMaterial::one && destBlend == CMaterial::one) return add;
if (srcBlend == CMaterial::zero && destBlend == CMaterial::srccolor) return modulate;
if (srcBlend == CMaterial::srcalpha && destBlend == CMaterial::invsrcalpha) return alphaBlend;
// unrecognized mode
nlassert(0);
return alphaTest; // to avoid a warning only ...
}
else
{
return alphaTest;
}
}
///===================================================================================
void CPSMaterial::forceModulateConstantColor(bool force, const NLMISC::CRGBA &col)
{
NL_PS_FUNC(CPSMaterial_forceModulateConstantColor)
if (force)
{
/// TODO : caching..
_Mat.texConstantColor(1, col);
_Mat.texEnvOpRGB(1, CMaterial::Modulate);
_Mat.texEnvOpAlpha(1, CMaterial::Modulate);
_Mat.texEnvArg0RGB(1, CMaterial::Previous, CMaterial::SrcColor);
_Mat.texEnvArg1RGB(1, CMaterial::Constant, CMaterial::SrcColor);
_Mat.texEnvArg0Alpha(1, CMaterial::Previous, CMaterial::SrcAlpha);
_Mat.texEnvArg1Alpha(1, CMaterial::Constant, CMaterial::SrcAlpha);
forceTexturedMaterialStages(2);
}
else
{
if (_Mat.getTexture(1) != NULL)
{
_Mat.setTexture(1, NULL);
}
}
}
///===================================================================================
void CPSMaterial::forceTexturedMaterialStages(uint numStages)
{
NL_PS_FUNC(CPSMaterial_forceTexturedMaterialStages)
ITexture *blankTex = NULL;
uint k;
for (k = 0; k < numStages; ++k)
{
if (_Mat.getTexture(k) == NULL)
{
if (!blankTex)
{
blankTex = CTextureMem::Create1x1WhiteTex();
}
_Mat.setTexture(k, blankTex);
}
}
for (; k < IDRV_MAT_MAXTEXTURES; ++k)
{
if (_Mat.getTexture(k) != NULL)
{
_Mat.setTexture(k, NULL);
}
}
}
/////////////////////////////////////////////
// CPSMultiTexturedParticle implementation //
/////////////////////////////////////////////
//=======================================
bool CPSMultiTexturedParticle::_ForceBasicCaps = false;
//=======================================
CPSMultiTexturedParticle::CPSMultiTexturedParticle() : _MainOp(Add), _AlternateOp(Add), _MultiTexState(TouchFlag), _BumpFactor(1.f)
{
NL_PS_FUNC(CPSMultiTexturedParticle_CPSMultiTexturedParticle)
}
//=======================================
void CPSMultiTexturedParticle::enableMultiTexture(bool enabled /*= true*/)
{
NL_PS_FUNC(CPSMultiTexturedParticle_enableMultiTexture)
if (isMultiTextureEnabled() == enabled) return;
if (!enabled)
{
_Texture2 = NULL;
_AlternateTexture2 = NULL;
_MultiTexState = 0;
}
else
{
_MainOp = Modulate;
_TexScroll[0].set(0, 0);
_TexScroll[1].set(0, 0);
_MultiTexState = (uint8) MultiTextureEnabled;
}
touch();
}
//=======================================
void CPSMultiTexturedParticle::enableAlternateTex(bool enabled /*= true*/)
{
NL_PS_FUNC(CPSMultiTexturedParticle_enableAlternateTex)
nlassert(isMultiTextureEnabled()); // multitexturing must have been enabled first
if (enabled == isAlternateTexEnabled()) return;
if (enabled)
{
_TexScrollAlternate[0].set(0, 0);
_TexScrollAlternate[1].set(0, 0);
_AlternateOp = Modulate;
_MultiTexState |= (uint8) AlternateTextureEnabled;
}
else
{
_Texture2 = NULL;
_MultiTexState &= ~(uint8) AlternateTextureEnabled;
}
touch();
}
//=======================================
void CPSMultiTexturedParticle::serialMultiTex(NLMISC::IStream &f) throw(NLMISC::EStream)
{
NL_PS_FUNC(CPSMultiTexturedParticle_serialMultiTex)
/// version 1 : bump factor
sint ver = f.serialVersion(1);
f.serial(_MultiTexState);
if (isMultiTextureEnabled())
{
f.serialEnum(_MainOp);
if (_MainOp == EnvBumpMap && ver >= 1)
{
f.serial(_BumpFactor);
}
ITexture *tex = NULL;
if (f.isReading())
{
f.serialPolyPtr(tex);
_Texture2 = tex;
}
else
{
tex = _Texture2;
f.serialPolyPtr(tex);
}
f.serial(_TexScroll[0], _TexScroll[1]);
if (isAlternateTexEnabled())
{
f.serialEnum(_AlternateOp);
if (f.isReading())
{
f.serialPolyPtr(tex);
_AlternateTexture2 = tex;
}
else
{
tex = _AlternateTexture2;
f.serialPolyPtr(tex);
}
f.serial(_TexScrollAlternate[0], _TexScrollAlternate[1]);
}
else
{
_AlternateTexture2 = NULL;
}
}
else
{
if (f.isReading())
{
_Texture2 = NULL;
_AlternateTexture2 = NULL;
}
}
}
//=======================================
void CPSMultiTexturedParticle::setupMaterial(ITexture *primary, IDriver *driver, CMaterial &mat, CVertexBuffer &vb)
{
NL_PS_FUNC(CPSMultiTexturedParticle_setupMaterial)
/// if bump is used, the matrix must be setupped each time (not a material field)
if (!_ForceBasicCaps && isMultiTextureEnabled() && _MainOp == EnvBumpMap)
{
if (driver->supportTextureAddrMode(CMaterial::OffsetTexture))
{
CTextureBump *tb = dynamic_cast((ITexture *) _Texture2);
if (tb != NULL)
{
float normFactor = tb->getNormalizationFactor();
if (normFactor == 0.f) // have we computed the normalization factor ?
{
/// todo : caching of this value
tb->generate();
normFactor = tb->getNormalizationFactor();
tb->release();
}
const float bMat[] = { _BumpFactor * normFactor, 0.f, 0.f, _BumpFactor * normFactor};
driver->setMatrix2DForTextureOffsetAddrMode(1, bMat);
}
vb.setUVRouting(0, 1);
vb.setUVRouting(1, 0);
}
else if (driver->supportEMBM())
{
uint embmTex = 0;
const uint numTexStages = driver->getNbTextureStages();
for(uint k = 0; k < numTexStages; ++k)
{
vb.setUVRouting(k, 0);
if (driver->isEMBMSupportedAtStage(k))
{
vb.setUVRouting(k, 1);
if (k != (numTexStages - 1))
{
vb.setUVRouting(k + 1, 0);
}
embmTex = k;
break;
}
}
CTextureBump *tb = dynamic_cast((ITexture *) _Texture2);
if (tb != NULL)
{
float normFactor = tb->getNormalizationFactor();
if (normFactor == 0.f) // have we computed the normalization factor ?
{
/// todo : caching of this value
tb->generate();
normFactor = tb->getNormalizationFactor();
tb->release();
}
const float bMat[] = { _BumpFactor * normFactor, 0.f, 0.f, _BumpFactor * normFactor};
driver->setEMBMMatrix(embmTex, bMat);
}
}
}
if (!isTouched() && areBasicCapsForcedLocal() == areBasicCapsForced()) return;
forceBasicCapsLocal(areBasicCapsForced());
if (!isMultiTextureEnabled())
{
mat.setTexture(0, primary);
mat.texEnvOpRGB(0, CMaterial::Modulate);
mat.setTexture(1, NULL);
}
else
{
if (_MainOp != EnvBumpMap)
{
setupMultiTexEnv(_MainOp, primary, _Texture2, mat, *driver);
_MultiTexState &= ~(uint8) EnvBumpMapUsed;
_MultiTexState &= ~(uint8) AlternateTextureUsed;
}
else
{
if (!_ForceBasicCaps && (driver->supportTextureAddrMode(CMaterial::OffsetTexture) || driver->supportEMBM())) // envbumpmap supported ?
{
CTextureBump *tb = dynamic_cast((ITexture *) _Texture2);
if (tb != NULL)
{
float normFactor = tb->getNormalizationFactor();
if (normFactor == 0.f) // have we computed the normalization factor ?
{
/// todo : caching of this value
tb->generate();
normFactor = tb->getNormalizationFactor();
tb->release();
}
setupMultiTexEnv(_MainOp, primary, _Texture2, mat, *driver);
}
_MultiTexState &= ~(uint8) AlternateTextureUsed;
_MultiTexState |= (uint8) EnvBumpMapUsed;
}
else // switch to alternate
{
_MultiTexState |= (uint8) AlternateTextureUsed;
if (isAlternateTexEnabled() && _AlternateTexture2)
{
setupMultiTexEnv(_AlternateOp, primary, _AlternateTexture2, mat, *driver);
}
else
{
mat.setTexture(0, primary);
mat.texEnvOpRGB(0, CMaterial::Modulate);
}
_MultiTexState &= ~(uint8) EnvBumpMapUsed;
}
}
}
updateTexWrapMode(*driver);
unTouch();
}
//=======================================
void CPSMultiTexturedParticle::setupMultiTexEnv(TOperator op, ITexture *tex1, ITexture *tex2, CMaterial &mat, IDriver &drv)
{
NL_PS_FUNC(CPSMultiTexturedParticle_setupMultiTexEnv)
switch (op)
{
case Add:
mat.setTexture(0, tex1);
mat.setTexture(1, tex2);
mat.texEnvOpRGB(0, CMaterial::Modulate);
mat.texEnvOpRGB(1, CMaterial::Add);
mat.enableTexAddrMode(false);
break;
case Modulate:
mat.setTexture(0, tex1);
mat.setTexture(1, tex2);
mat.texEnvOpRGB(0, CMaterial::Modulate);
mat.texEnvOpRGB(1, CMaterial::Modulate);
mat.enableTexAddrMode(false);
break;
case EnvBumpMap:
if (drv.supportTextureAddrMode(CMaterial::OffsetTexture))
{
mat.setTexture(0, tex2);
mat.setTexture(1, tex1);
mat.texEnvOpRGB(0, CMaterial::Replace);
mat.texEnvOpRGB(1, CMaterial::Modulate);
mat.enableTexAddrMode(true);
mat.setTexAddressingMode(0, CMaterial::FetchTexture);
mat.setTexAddressingMode(1, CMaterial::OffsetTexture);
}
else if (drv.supportEMBM())
{
const uint numTexStages = drv.getNbTextureStages();
// if embm unit is at last stage, it operates on texture at previous stage
// otherwise it operates on texture at next stage
for(uint k = 0; k < numTexStages; ++k)
{
if (drv.isEMBMSupportedAtStage(k))
{
if (k == (numTexStages - 1))
{
mat.setTexture(k, tex2);
mat.texEnvOpRGB(k, CMaterial::EMBM);
mat.texEnvOpAlpha(k, CMaterial::EMBM);
mat.setTexture(0, tex1);
mat.texEnvOpRGB(0, CMaterial::Modulate);
mat.texEnvArg0RGB(0, CMaterial::Texture, CMaterial::SrcColor);
mat.texEnvArg1RGB(0, CMaterial::Diffuse, CMaterial::SrcColor);
mat.texEnvArg0Alpha(0, CMaterial::Texture, CMaterial::SrcAlpha);
mat.texEnvArg1Alpha(0, CMaterial::Diffuse, CMaterial::SrcAlpha);
}
else
{
// stage before the bump map must not be empty so fill them
for(uint l = 0; l < k; ++l)
{
mat.setTexture(l, tex1);
mat.texEnvOpRGB(l, CMaterial::Modulate);
}
mat.setTexture(k, tex2);
mat.texEnvOpRGB(k, CMaterial::EMBM);
mat.setTexture(k + 1, tex1);
mat.texEnvOpRGB(k + 1, CMaterial::Modulate);
mat.texEnvArg0RGB(k + 1, CMaterial::Texture, CMaterial::SrcColor);
mat.texEnvArg1RGB(k + 1, CMaterial::Diffuse, CMaterial::SrcColor);
mat.texEnvArg0Alpha(k + 1, CMaterial::Texture, CMaterial::SrcAlpha);
mat.texEnvArg1Alpha(k + 1, CMaterial::Diffuse, CMaterial::SrcAlpha);
}
break;
}
}
}
break;
case Decal:
mat.setTexture(0, tex1);
mat.texEnvOpRGB(0, CMaterial::Replace);
mat.setTexture(1, NULL);
mat.enableTexAddrMode(false);
break;
default: break;
}
}
//=====static func to convert a texture to a bumpmap
static void ConvertToBumpMap(NLMISC::CSmartPtr &ptr)
{
NL_PS_FUNC(ConvertToBumpMap)
if (!dynamic_cast( (ITexture *) ptr))
{
// convert to a bumpmap
CTextureBump *tb = new CTextureBump;
tb->setHeightMap((ITexture *) ptr);
tb->enableSharing(true);
ptr = tb;
}
}
//=====static func to convert a bumpmap to a texture (its heightmap)
static void ConvertFromBumpMap(NLMISC::CSmartPtr &ptr)
{
NL_PS_FUNC(ConvertFromBumpMap)
CTextureBump *bm = dynamic_cast( (ITexture *) ptr);
if (bm)
{
// retrieve the heightmap from the bumpmap
NLMISC::CSmartPtr hm = bm->getHeightMap();
ptr = hm;
}
}
//=========================================
void CPSMultiTexturedParticle::setTexture2Alternate(ITexture *tex)
{
NL_PS_FUNC(CPSMultiTexturedParticle_setTexture2Alternate)
_AlternateTexture2 = tex;
if (_AlternateOp != EnvBumpMap)
{
ConvertFromBumpMap(_AlternateTexture2);
}
else
{
ConvertToBumpMap(_AlternateTexture2);
}
touch();
}
//==========================================
void CPSMultiTexturedParticle::setTexture2(ITexture *tex)
{
NL_PS_FUNC(CPSMultiTexturedParticle_setTexture2)
_Texture2 = tex;
if (_MainOp != EnvBumpMap)
{
ConvertFromBumpMap(_Texture2);
}
else
{
if (!dynamic_cast((ITexture *) _Texture2))
{
ConvertToBumpMap(_Texture2);
}
}
touch();
}
//==========================================
void CPSMultiTexturedParticle::setMainTexOp(TOperator op)
{
NL_PS_FUNC(CPSMultiTexturedParticle_setMainTexOp)
_MainOp = op;
if (_MainOp == EnvBumpMap)
{
ConvertToBumpMap(_Texture2);
}
else
{
ConvertFromBumpMap(_Texture2);
}
touch();
}
//==========================================
void CPSMultiTexturedParticle::setAlternateTexOp(TOperator op)
{
NL_PS_FUNC(CPSMultiTexturedParticle_setAlternateTexOp)
_AlternateOp = op;
if (_AlternateOp == EnvBumpMap)
{
ConvertToBumpMap(_AlternateTexture2);
}
else
{
ConvertFromBumpMap(_AlternateTexture2);
}
touch();
}
//==========================================
void CPSMultiTexturedParticle::setUseLocalDate(bool use)
{
NL_PS_FUNC(CPSMultiTexturedParticle_setUseLocalDate)
if (use) _MultiTexState |= ScrollUseLocalDate;
else _MultiTexState &= ~ ScrollUseLocalDate;
}
//==========================================
void CPSMultiTexturedParticle::setUseLocalDateAlt(bool use)
{
NL_PS_FUNC(CPSMultiTexturedParticle_setUseLocalDateAlt)
if (use) _MultiTexState |= ScrollUseLocalDateAlternate;
else _MultiTexState &= ~ ScrollUseLocalDateAlternate;
}
//==========================================
void CPSTexturedParticle::enumTexs(std::vector > &dest)
{
NL_PS_FUNC(CPSTexturedParticle_enumTexs)
if (_Tex)
{
dest.push_back(_Tex);
}
if (getTextureGroup())
{
dest.push_back(getTextureGroup());
}
}
//==========================================
void CPSMultiTexturedParticle::enumTexs(std::vector > &dest, IDriver &drv)
{
NL_PS_FUNC(CPSMultiTexturedParticle_enumTexs)
if (_MainOp == EnvBumpMap && !_ForceBasicCaps)
{
if (drv.supportTextureAddrMode(CMaterial::OffsetTexture) || drv.supportEMBM())
{
if (_Texture2) dest.push_back(_Texture2);
}
else
{
if (_AlternateTexture2) dest.push_back(_AlternateTexture2);
}
return;
}
if (_Texture2) dest.push_back(_Texture2);
}
// *****************************************************************************************************
bool CPSMultiTexturedParticle::isAlternateTextureUsed(IDriver &driver) const
{
NL_PS_FUNC(CPSMultiTexturedParticle_isAlternateTextureUsed)
if (!isTouched() && areBasicCapsForcedLocal() == areBasicCapsForced()) return (_MultiTexState & AlternateTextureUsed) != 0;
if (_MainOp != EnvBumpMap) return false;
return _ForceBasicCaps || (!driver.supportTextureAddrMode(CMaterial::OffsetTexture) && !driver.supportEMBM());
}
} // NL3D