2441 lines
71 KiB
C++
2441 lines
71 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/ps_mesh.h"
|
||
|
#include "nel/3d/ps_macro.h"
|
||
|
#include "nel/3d/shape.h"
|
||
|
#include "nel/3d/mesh.h"
|
||
|
#include "nel/3d/transform_shape.h"
|
||
|
#include "nel/3d/shape_bank.h"
|
||
|
#include "nel/3d/texture_mem.h"
|
||
|
#include "nel/3d/scene.h"
|
||
|
#include "nel/3d/ps_located.h"
|
||
|
#include "nel/3d/particle_system.h"
|
||
|
#include "nel/3d/particle_system_shape.h"
|
||
|
#include "nel/3d/particle_system_model.h"
|
||
|
#include "nel/3d/ps_iterator.h"
|
||
|
#include "nel/misc/stream.h"
|
||
|
#include "nel/misc/path.h"
|
||
|
|
||
|
#include <memory>
|
||
|
#include <functional>
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
namespace NL3D
|
||
|
{
|
||
|
|
||
|
////////////////////
|
||
|
// static members //
|
||
|
////////////////////
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
CPSConstraintMesh::CMeshDisplayShare CPSConstraintMesh::_MeshDisplayShare(16);
|
||
|
CVertexBuffer CPSConstraintMesh::_PreRotatedMeshVB; // mesh has no normals
|
||
|
CVertexBuffer CPSConstraintMesh::_PreRotatedMeshVBWithNormal; // mesh has normals
|
||
|
CPSConstraintMesh::TMeshName2RamVB CPSConstraintMesh::_MeshRamVBs;
|
||
|
|
||
|
|
||
|
|
||
|
// this produce a random unit vector
|
||
|
static CVector MakeRandomUnitVect(void)
|
||
|
{
|
||
|
NL_PS_FUNC(MakeRandomUnitVect)
|
||
|
CVector v((float) ((rand() % 20000) - 10000)
|
||
|
,(float) ((rand() % 20000) - 10000)
|
||
|
,(float) ((rand() % 20000) - 10000)
|
||
|
);
|
||
|
v.normalize();
|
||
|
return v;
|
||
|
}
|
||
|
|
||
|
|
||
|
////////////////////////////
|
||
|
// CPSMesh implementation //
|
||
|
////////////////////////////
|
||
|
|
||
|
|
||
|
//====================================================================================
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
const std::string DummyShapeName("dummy mesh shape");
|
||
|
|
||
|
/** a private function that create a dummy mesh :a cube with dummy textures
|
||
|
*/
|
||
|
|
||
|
static CMesh *CreateDummyMesh(void)
|
||
|
{
|
||
|
NL_PS_FUNC(CreateDummyMesh)
|
||
|
CMesh::CMeshBuild mb;
|
||
|
CMeshBase::CMeshBaseBuild mbb;
|
||
|
|
||
|
mb.VertexFlags = CVertexBuffer::PositionFlag | CVertexBuffer::TexCoord0Flag;
|
||
|
mb.Vertices.push_back(CVector(-.5f, -.5f, -.5f));
|
||
|
mb.Vertices.push_back(CVector(.5f, -.5f, -.5f));
|
||
|
mb.Vertices.push_back(CVector(.5f, -.5f, .5f));
|
||
|
mb.Vertices.push_back(CVector(-.5f, -.5f, .5f));
|
||
|
|
||
|
mb.Vertices.push_back(CVector(-.5f, .5f, -.5f));
|
||
|
mb.Vertices.push_back(CVector(.5f, .5f, -.5f));
|
||
|
mb.Vertices.push_back(CVector(.5f, .5f, .5f));
|
||
|
mb.Vertices.push_back(CVector(-.5f, .5f, .5f));
|
||
|
|
||
|
// index for each face
|
||
|
uint32 tab[] = { 4, 1, 0,
|
||
|
4, 5, 1,
|
||
|
5, 2, 1,
|
||
|
5, 6, 2,
|
||
|
6, 3, 2,
|
||
|
6, 7, 3,
|
||
|
7, 0, 3,
|
||
|
7, 4, 0,
|
||
|
7, 5, 4,
|
||
|
7, 6, 5,
|
||
|
2, 0, 1,
|
||
|
2, 3, 0
|
||
|
};
|
||
|
|
||
|
for (uint k = 0; k < 6; ++k)
|
||
|
{
|
||
|
CMesh::CFace f;
|
||
|
f.Corner[0].Vertex = tab[6 * k];
|
||
|
f.Corner[0].Uvws[0] = NLMISC::CUVW(0, 0, 0);
|
||
|
|
||
|
f.Corner[1].Vertex = tab[6 * k + 1];
|
||
|
f.Corner[1].Uvws[0] = NLMISC::CUVW(1, 1, 0);
|
||
|
|
||
|
f.Corner[2].Vertex = tab[6 * k + 2];
|
||
|
f.Corner[2].Uvws[0] = NLMISC::CUVW(0, 1, 0);
|
||
|
|
||
|
f.MaterialId = 0;
|
||
|
|
||
|
mb.Faces.push_back(f);
|
||
|
|
||
|
f.Corner[0].Vertex = tab[6 * k + 3];
|
||
|
f.Corner[0].Uvws[0] = NLMISC::CUVW(0, 0, 0);
|
||
|
|
||
|
f.Corner[1].Vertex = tab[6 * k + 4];
|
||
|
f.Corner[1].Uvws[0] = NLMISC::CUVW(1, 0, 0);
|
||
|
|
||
|
f.Corner[2].Vertex = tab[6 * k + 5];
|
||
|
f.Corner[2].Uvws[0] = NLMISC::CUVW(1, 1, 0);
|
||
|
|
||
|
f.MaterialId = 0;
|
||
|
mb.Faces.push_back(f);
|
||
|
}
|
||
|
|
||
|
CMaterial mat;
|
||
|
CTextureMem *tex = new CTextureMem;
|
||
|
tex->makeDummy();
|
||
|
mat.setTexture(0, tex);
|
||
|
mat.setLighting(false);
|
||
|
mat.setColor(CRGBA::White);
|
||
|
mbb.Materials.push_back(mat);
|
||
|
CMesh *m = new CMesh;
|
||
|
m->build(mbb, mb);
|
||
|
return m;
|
||
|
}
|
||
|
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSMesh::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSMesh_IStream )
|
||
|
(void)f.serialVersion(3);
|
||
|
CPSParticle::serial(f);
|
||
|
CPSSizedParticle::serialSizeScheme(f);
|
||
|
CPSRotated3DPlaneParticle::serialPlaneBasisScheme(f);
|
||
|
CPSRotated2DParticle::serialAngle2DScheme(f);
|
||
|
f.serial(_Shape);
|
||
|
if (f.isReading())
|
||
|
{
|
||
|
|
||
|
uint maxSize = 0;
|
||
|
if (_Owner)
|
||
|
{
|
||
|
maxSize = _Owner->getMaxSize();
|
||
|
_Instances.resize(maxSize);
|
||
|
}
|
||
|
for(uint k = 0; k < maxSize; ++k)
|
||
|
{
|
||
|
_Instances.insert(NULL);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSMesh::setShape(const std::string &shape)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSMesh_setShape)
|
||
|
if (shape == _Shape) return;
|
||
|
_Shape = shape;
|
||
|
removeAllInstancesFromScene();
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
uint32 CPSMesh::getNumWantedTris() const
|
||
|
{
|
||
|
NL_PS_FUNC(CPSMesh_getNumWantedTris)
|
||
|
/// we don't draw any face ! (the meshs are drawn by the scene)
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
bool CPSMesh::hasTransparentFaces(void)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSMesh_hasTransparentFaces)
|
||
|
/// we don't draw any tri ! (the meshs are drawn by the scene)
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
bool CPSMesh::hasOpaqueFaces(void)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSMesh_hasOpaqueFaces)
|
||
|
/// We don't draw any tri !
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
bool CPSMesh::hasLightableFaces()
|
||
|
{
|
||
|
NL_PS_FUNC(CPSMesh_hasLightableFaces)
|
||
|
/// we don't draw any tri ! (the meshs are drawn by the scene)
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSMesh::releaseAllRef()
|
||
|
{
|
||
|
NL_PS_FUNC(CPSMesh_releaseAllRef)
|
||
|
CPSParticle::releaseAllRef();
|
||
|
nlassert(_Owner && _Owner->getScene());
|
||
|
removeAllInstancesFromScene();
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSMesh::removeAllInstancesFromScene()
|
||
|
{
|
||
|
NL_PS_FUNC(CPSMesh_removeAllInstancesFromScene)
|
||
|
for(uint k = 0; k < _Instances.getSize(); ++k)
|
||
|
{
|
||
|
if (_Instances[k])
|
||
|
{
|
||
|
if (_Owner) _Owner->getScene()->deleteInstance(_Instances[k]);
|
||
|
_Instances[k] = NULL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
CTransformShape *CPSMesh::createInstance()
|
||
|
{
|
||
|
NL_PS_FUNC(CPSMesh_createInstance)
|
||
|
CScene *scene = _Owner->getScene();
|
||
|
nlassert(scene); // the setScene method of the particle system should have been called
|
||
|
CTransformShape *instance = scene->createInstance(_Shape);
|
||
|
if (!instance)
|
||
|
{
|
||
|
// mesh not found ...
|
||
|
IShape *is = CreateDummyMesh();
|
||
|
scene->getShapeBank()->add(DummyShapeName, is);
|
||
|
instance = scene->createInstance(DummyShapeName);
|
||
|
nlassert(instance);
|
||
|
}
|
||
|
instance->setTransformMode(CTransform::DirectMatrix);
|
||
|
instance->hide(); // the object hasn't the right matrix yet so we hide it. It'll be shown once it is computed
|
||
|
return instance;
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSMesh::newElement(const CPSEmitterInfo &info)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSMesh_newElement)
|
||
|
newPlaneBasisElement(info);
|
||
|
newAngle2DElement(info);
|
||
|
newSizeElement(info);
|
||
|
nlassert(_Owner);
|
||
|
nlassert(_Owner->getOwner());
|
||
|
CTransformShape *instance = createInstance();
|
||
|
nlassert(instance);
|
||
|
_Instances.insert(instance);
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSMesh::deleteElement(uint32 index)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSMesh_deleteElement)
|
||
|
deleteSizeElement(index);
|
||
|
deleteAngle2DElement(index);
|
||
|
deletePlaneBasisElement(index);
|
||
|
|
||
|
// check whether CTransformShape have been instanciated
|
||
|
nlassert(_Owner);
|
||
|
nlassert(_Owner->getOwner());
|
||
|
CScene *scene = _Owner->getScene();
|
||
|
nlassert(scene); // the setScene method of the particle system should have been called
|
||
|
if (_Instances[index])
|
||
|
{
|
||
|
scene->deleteInstance(_Instances[index]);
|
||
|
}
|
||
|
_Instances.remove(index);
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSMesh::step(TPSProcessPass pass)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSMesh_step)
|
||
|
if (pass == PSMotion)
|
||
|
{
|
||
|
updatePos();
|
||
|
}
|
||
|
else
|
||
|
if (pass == PSToolRender) // edition mode only
|
||
|
{
|
||
|
showTool();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSMesh::updatePos()
|
||
|
{
|
||
|
NL_PS_FUNC(CPSMesh_updatePos)
|
||
|
const uint MeshBufSize = 512;
|
||
|
PARTICLES_CHECK_MEM;
|
||
|
nlassert(_Owner);
|
||
|
const uint32 size = _Owner->getSize();
|
||
|
if (!size) return;
|
||
|
|
||
|
|
||
|
_Owner->incrementNbDrawnParticles(size); // for benchmark purpose
|
||
|
|
||
|
|
||
|
if (!_Instances[0])
|
||
|
{
|
||
|
for (uint k = 0; k < size; ++k)
|
||
|
{
|
||
|
nlassert(!_Instances[k]);
|
||
|
_Instances[k] = createInstance();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
float sizes[MeshBufSize];
|
||
|
float angles[MeshBufSize];
|
||
|
static CPlaneBasis planeBasis[MeshBufSize];
|
||
|
|
||
|
uint32 leftToDo = size, toProcess;
|
||
|
|
||
|
|
||
|
float *ptCurrSize;
|
||
|
const uint ptCurrSizeIncrement = _SizeScheme ? 1 : 0;
|
||
|
|
||
|
float *ptCurrAngle;
|
||
|
const uint ptCurrAngleIncrement = _Angle2DScheme ? 1 : 0;
|
||
|
|
||
|
CPlaneBasis *ptBasis;
|
||
|
const uint ptCurrPlaneBasisIncrement = _PlaneBasisScheme ? 1 : 0;
|
||
|
|
||
|
TPSAttribVector::const_iterator posIt = _Owner->getPos().begin(), endPosIt;
|
||
|
|
||
|
|
||
|
TInstanceCont::iterator instanceIt = _Instances.begin();
|
||
|
|
||
|
do
|
||
|
{
|
||
|
toProcess = leftToDo < MeshBufSize ? leftToDo : MeshBufSize;
|
||
|
|
||
|
if (_SizeScheme)
|
||
|
{
|
||
|
ptCurrSize = (float *) (_SizeScheme->make(_Owner, size - leftToDo, &sizes[0], sizeof(float), toProcess, true));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ptCurrSize =& _ParticleSize;
|
||
|
}
|
||
|
|
||
|
if (_Angle2DScheme)
|
||
|
{
|
||
|
ptCurrAngle = (float *) (_Angle2DScheme->make(_Owner, size - leftToDo, &angles[0], sizeof(float), toProcess, true));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ptCurrAngle =& _Angle2D;
|
||
|
}
|
||
|
|
||
|
|
||
|
if (_PlaneBasisScheme)
|
||
|
{
|
||
|
ptBasis = (CPlaneBasis *) (_PlaneBasisScheme->make(_Owner, size - leftToDo, &planeBasis[0], sizeof(CPlaneBasis), toProcess, true));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ptBasis = &_PlaneBasis;
|
||
|
}
|
||
|
|
||
|
endPosIt = posIt + toProcess;
|
||
|
CMatrix mat, tmat;
|
||
|
|
||
|
// the matrix used to get in the right basis
|
||
|
const CMatrix &transfo = getLocalToWorldMatrix();
|
||
|
do
|
||
|
{
|
||
|
|
||
|
tmat.identity();
|
||
|
mat.identity();
|
||
|
|
||
|
tmat.translate(*posIt);
|
||
|
|
||
|
|
||
|
|
||
|
mat.setRot( ptBasis->X * CPSUtil::getCos((sint32) *ptCurrAngle) + ptBasis->Y * CPSUtil::getSin((sint32) *ptCurrAngle)
|
||
|
, ptBasis->X * CPSUtil::getCos((sint32) *ptCurrAngle + 64) + ptBasis->Y * CPSUtil::getSin((sint32) *ptCurrAngle + 64)
|
||
|
, ptBasis->X ^ ptBasis->Y
|
||
|
);
|
||
|
|
||
|
mat.scale(*ptCurrSize);
|
||
|
|
||
|
(*instanceIt)->setMatrix(transfo * tmat * mat);
|
||
|
if (CParticleSystem::OwnerModel)
|
||
|
{
|
||
|
// make sure the visibility is the same
|
||
|
if (CParticleSystem::OwnerModel->isHrcVisible())
|
||
|
{
|
||
|
(*instanceIt)->show();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
(*instanceIt)->hide();
|
||
|
}
|
||
|
(*instanceIt)->setClusterSystem(CParticleSystem::OwnerModel->getClusterSystem());
|
||
|
}
|
||
|
|
||
|
++instanceIt;
|
||
|
++posIt;
|
||
|
ptCurrSize += ptCurrSizeIncrement;
|
||
|
ptCurrAngle += ptCurrAngleIncrement;
|
||
|
ptBasis += ptCurrPlaneBasisIncrement;
|
||
|
}
|
||
|
while (posIt != endPosIt);
|
||
|
leftToDo -= toProcess;
|
||
|
}
|
||
|
while (leftToDo);
|
||
|
|
||
|
PARTICLES_CHECK_MEM;
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSMesh::resize(uint32 size)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSMesh_resize)
|
||
|
nlassert(size < (1 << 16));
|
||
|
resizeSize(size);
|
||
|
resizeAngle2D(size);
|
||
|
resizePlaneBasis(size);
|
||
|
if (size < _Instances.getSize())
|
||
|
{
|
||
|
for(uint k = size; k < _Instances.getSize(); ++k)
|
||
|
{
|
||
|
if (_Owner) _Owner->getScene()->deleteInstance(_Instances[k]);
|
||
|
}
|
||
|
}
|
||
|
_Instances.resize(size);
|
||
|
}
|
||
|
|
||
|
|
||
|
//====================================================================================
|
||
|
CPSMesh::~CPSMesh()
|
||
|
{
|
||
|
NL_PS_FUNC(CPSMesh_CPSMeshDtor)
|
||
|
if (_Owner && _Owner->getOwner())
|
||
|
{
|
||
|
removeAllInstancesFromScene();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
#ifdef NL_DEBUG
|
||
|
for (TInstanceCont::iterator it = _Instances.begin(); it != _Instances.end(); ++it)
|
||
|
{
|
||
|
nlassert(*it == NULL); // there's a leak..:(
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////
|
||
|
// CPSConstraintMesh implementation //
|
||
|
//////////////////////////////////////
|
||
|
|
||
|
/// private : eval the number of triangles in a mesh
|
||
|
static uint getMeshNumTri(const CMesh &m)
|
||
|
{
|
||
|
NL_PS_FUNC(getMeshNumTri)
|
||
|
uint numFaces = 0;
|
||
|
for (uint k = 0; k < m.getNbMatrixBlock(); ++k)
|
||
|
{
|
||
|
for (uint l = 0; l < m.getNbRdrPass(k); ++l)
|
||
|
{
|
||
|
const CIndexBuffer pb = m.getRdrPassPrimitiveBlock(k, l);
|
||
|
numFaces += pb.getNumIndexes()/3;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
return numFaces;
|
||
|
}
|
||
|
|
||
|
|
||
|
//====================================================================================
|
||
|
/// private use : check if there are transparent and / or opaque faces in a mesh
|
||
|
static void CheckForOpaqueAndTransparentFacesInMesh(const CMesh &m, bool &hasTransparentFaces, bool &hasOpaqueFaces)
|
||
|
{
|
||
|
NL_PS_FUNC(CheckForOpaqueAndTransparentFacesInMesh)
|
||
|
hasTransparentFaces = false;
|
||
|
hasOpaqueFaces = false;
|
||
|
|
||
|
for (uint k = 0; k < m.getNbRdrPass(0); ++k)
|
||
|
{
|
||
|
const CMaterial &currMat = m.getMaterial(m.getRdrPassMaterial(0, k));
|
||
|
if (!currMat.getZWrite())
|
||
|
{
|
||
|
hasTransparentFaces = true;
|
||
|
}
|
||
|
else // z-buffer write or no blending -> the face is opaque
|
||
|
{
|
||
|
hasOpaqueFaces = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
/// private use : check if there are lightable faces in a mesh
|
||
|
static bool CheckForLightableFacesInMesh(const CMesh &m)
|
||
|
{
|
||
|
NL_PS_FUNC(CheckForLightableFacesInMesh)
|
||
|
for (uint k = 0; k < m.getNbRdrPass(0); ++k)
|
||
|
{
|
||
|
const CMaterial &currMat = m.getMaterial(m.getRdrPassMaterial(0, k));
|
||
|
if (currMat.isLighted()) return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
/** Well, we could have put a method template in CPSConstraintMesh, but some compilers
|
||
|
* want the definition of the methods in the header, and some compilers
|
||
|
* don't want friend with function template, so we use a static method template of a friend class instead,
|
||
|
* which gives us the same result :)
|
||
|
*/
|
||
|
class CPSConstraintMeshHelper
|
||
|
{
|
||
|
public:
|
||
|
template <class T>
|
||
|
static void drawMeshs(T posIt, CPSConstraintMesh &m, uint size, uint32 srcStep, bool opaque)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMeshHelper_drawMeshs)
|
||
|
const CVertexBuffer &modelVb = m.getMeshVB(0);
|
||
|
|
||
|
|
||
|
// size for model vertices
|
||
|
const uint inVSize = modelVb.getVertexSize(); // vertex size
|
||
|
|
||
|
// driver setup
|
||
|
IDriver *driver = m.getDriver();
|
||
|
m.setupDriverModelMatrix();
|
||
|
|
||
|
// buffer to compute sizes
|
||
|
float sizes[ConstraintMeshBufSize];
|
||
|
|
||
|
float *ptCurrSize;
|
||
|
uint ptCurrSizeIncrement = m._SizeScheme ? 1 : 0;
|
||
|
|
||
|
T endPosIt;
|
||
|
uint leftToDo = size, toProcess;
|
||
|
|
||
|
/// get a vb in which to write. It has the same format than the input mesh, but can also have a color flag added
|
||
|
CPSConstraintMesh::CMeshDisplay &md= m._MeshDisplayShare.getMeshDisplay(m._Meshes[0], modelVb, modelVb.getVertexFormat()
|
||
|
| (m._ColorScheme ? CVertexBuffer::PrimaryColorFlag : 0));
|
||
|
|
||
|
m.setupRenderPasses((float) m._Owner->getOwner()->getSystemDate() - m._GlobalAnimDate, md.RdrPasses, opaque);
|
||
|
|
||
|
CVertexBuffer &outVb = md.VB;
|
||
|
const uint outVSize = outVb.getVertexSize();
|
||
|
|
||
|
|
||
|
|
||
|
// we don't have precomputed mesh there ... so each mesh must be transformed, which is the worst case
|
||
|
CPlaneBasis planeBasis[ConstraintMeshBufSize];
|
||
|
CPlaneBasis *ptBasis;
|
||
|
uint ptBasisIncrement = m._PlaneBasisScheme ? 1 : 0;
|
||
|
|
||
|
const uint nbVerticesInSource = modelVb.getNumVertices();
|
||
|
|
||
|
sint inNormalOff=0;
|
||
|
sint outNormalOff=0;
|
||
|
if (modelVb.getVertexFormat() & CVertexBuffer::NormalFlag)
|
||
|
{
|
||
|
inNormalOff = modelVb.getNormalOff();
|
||
|
outNormalOff = outVb.getNormalOff();
|
||
|
}
|
||
|
if (m._ColorScheme)
|
||
|
{
|
||
|
CVertexBuffer::TVertexColorType vtc = driver->getVertexColorFormat();
|
||
|
m._ColorScheme->setColorType(vtc);
|
||
|
if (modelVb.getVertexFormat() & CVertexBuffer::PrimaryColorFlag)
|
||
|
{
|
||
|
const_cast<CVertexBuffer &>(modelVb).setVertexColorFormat(vtc);
|
||
|
}
|
||
|
}
|
||
|
CVertexBufferRead vbaRead;
|
||
|
modelVb.lock (vbaRead);
|
||
|
do
|
||
|
{
|
||
|
toProcess = std::min(leftToDo, ConstraintMeshBufSize);
|
||
|
outVb.setNumVertices(toProcess * nbVerticesInSource);
|
||
|
{
|
||
|
CVertexBufferReadWrite vba;
|
||
|
outVb.lock(vba);
|
||
|
uint8 *outVertex = (uint8 *) vba.getVertexCoordPointer();
|
||
|
if (m._SizeScheme)
|
||
|
{
|
||
|
ptCurrSize = (float *) (m._SizeScheme->make(m._Owner, size -leftToDo, &sizes[0], sizeof(float), toProcess, true, srcStep));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ptCurrSize = &m._ParticleSize;
|
||
|
}
|
||
|
|
||
|
if (m._PlaneBasisScheme)
|
||
|
{
|
||
|
ptBasis = (CPlaneBasis *) (m._PlaneBasisScheme->make(m._Owner, size -leftToDo, &planeBasis[0], sizeof(CPlaneBasis), toProcess, true, srcStep));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ptBasis = &m._PlaneBasis;
|
||
|
}
|
||
|
|
||
|
|
||
|
endPosIt = posIt + toProcess;
|
||
|
// transfo matrix & scaled transfo matrix;
|
||
|
CMatrix M, sM;
|
||
|
|
||
|
|
||
|
if (m._Meshes.size() == 1)
|
||
|
{
|
||
|
/// unmorphed case
|
||
|
do
|
||
|
{
|
||
|
const uint8 *inVertex = (const uint8 *) vbaRead.getVertexCoordPointer();
|
||
|
uint k = nbVerticesInSource;
|
||
|
|
||
|
// do we need a normal ?
|
||
|
if (modelVb.getVertexFormat() & CVertexBuffer::NormalFlag)
|
||
|
{
|
||
|
M.identity();
|
||
|
M.setRot(ptBasis->X, ptBasis->Y, ptBasis->X ^ ptBasis->Y);
|
||
|
sM = M;
|
||
|
sM.scale(*ptCurrSize);
|
||
|
|
||
|
// offset of normals in the prerotated mesh
|
||
|
do
|
||
|
{
|
||
|
CHECK_VERTEX_BUFFER(modelVb, inVertex);
|
||
|
CHECK_VERTEX_BUFFER(outVb, outVertex);
|
||
|
CHECK_VERTEX_BUFFER(modelVb, inVertex + inNormalOff);
|
||
|
CHECK_VERTEX_BUFFER(outVb, outVertex + outNormalOff);
|
||
|
|
||
|
// translate and resize the vertex (relatively to the mesh origin)
|
||
|
*(CVector *) outVertex = *posIt + sM * *(CVector *) inVertex;
|
||
|
// copy the normal
|
||
|
*(CVector *) (outVertex + outNormalOff) = M * *(CVector *) (inVertex + inNormalOff);
|
||
|
|
||
|
|
||
|
inVertex += inVSize;
|
||
|
outVertex += outVSize;
|
||
|
}
|
||
|
while (--k);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// no normal to transform
|
||
|
sM.identity();
|
||
|
sM.setRot(ptBasis->X, ptBasis->Y, ptBasis->X ^ ptBasis->Y);
|
||
|
sM.scale(*ptCurrSize);
|
||
|
|
||
|
do
|
||
|
{
|
||
|
CHECK_VERTEX_BUFFER(modelVb, inVertex);
|
||
|
CHECK_VERTEX_BUFFER(outVb, outVertex);
|
||
|
|
||
|
// translate and resize the vertex (relatively to the mesh origin)
|
||
|
*(CVector *) outVertex = *posIt + sM * *(CVector *) inVertex;
|
||
|
|
||
|
inVertex += inVSize;
|
||
|
outVertex += outVSize;
|
||
|
}
|
||
|
while (--k);
|
||
|
}
|
||
|
|
||
|
|
||
|
++posIt;
|
||
|
ptCurrSize += ptCurrSizeIncrement;
|
||
|
ptBasis += ptBasisIncrement;
|
||
|
}
|
||
|
while (posIt != endPosIt);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// morphed case
|
||
|
|
||
|
// first, compute the morph value for each mesh
|
||
|
float morphValues[ConstraintMeshBufSize];
|
||
|
float *currMorphValue;
|
||
|
uint morphValueIncr;
|
||
|
|
||
|
if (m._MorphScheme) // variable case
|
||
|
{
|
||
|
currMorphValue = (float *) m._MorphScheme->make(m._Owner, size - leftToDo, &morphValues[0], sizeof(float), toProcess, true, srcStep);
|
||
|
morphValueIncr = 1;
|
||
|
}
|
||
|
else /// constant case
|
||
|
{
|
||
|
currMorphValue = &m._MorphValue;
|
||
|
morphValueIncr = 0;
|
||
|
}
|
||
|
|
||
|
do
|
||
|
{
|
||
|
const uint numShapes = m._Meshes.size();
|
||
|
const uint8 *m0, *m1;
|
||
|
float lambda;
|
||
|
float opLambda;
|
||
|
const CVertexBuffer *inVB0, *inVB1;
|
||
|
if (*currMorphValue >= numShapes - 1)
|
||
|
{
|
||
|
lambda = 0.f;
|
||
|
opLambda = 1.f;
|
||
|
inVB0 = inVB1 = &(m.getMeshVB(numShapes - 1));
|
||
|
}
|
||
|
else if (*currMorphValue <= 0)
|
||
|
{
|
||
|
lambda = 0.f;
|
||
|
opLambda = 1.f;
|
||
|
inVB0 = inVB1 = &(m.getMeshVB(0));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
uint iMeshIndex = (uint) *currMorphValue;
|
||
|
lambda = *currMorphValue - iMeshIndex;
|
||
|
opLambda = 1.f - lambda;
|
||
|
inVB0 = &(m.getMeshVB(iMeshIndex));
|
||
|
inVB1 = &(m.getMeshVB(iMeshIndex + 1));
|
||
|
}
|
||
|
CVertexBufferRead vba0;
|
||
|
inVB0->lock (vba0);
|
||
|
CVertexBufferRead vba1;
|
||
|
inVB1->lock (vba1);
|
||
|
|
||
|
m0 = (uint8 *) vba0.getVertexCoordPointer();
|
||
|
m1 = (uint8 *) vba1.getVertexCoordPointer();
|
||
|
|
||
|
|
||
|
uint k = nbVerticesInSource;
|
||
|
// do we need a normal ?
|
||
|
if (modelVb.getVertexFormat() & CVertexBuffer::NormalFlag)
|
||
|
{
|
||
|
M.identity();
|
||
|
M.setRot(ptBasis->X, ptBasis->Y, ptBasis->X ^ ptBasis->Y);
|
||
|
sM = M;
|
||
|
sM.scale(*ptCurrSize);
|
||
|
|
||
|
// offset of normals in the prerotated mesh
|
||
|
do
|
||
|
{
|
||
|
CHECK_VERTEX_BUFFER((*inVB0), m0);
|
||
|
CHECK_VERTEX_BUFFER((*inVB1), m1);
|
||
|
CHECK_VERTEX_BUFFER((*inVB0), m0 + inNormalOff);
|
||
|
CHECK_VERTEX_BUFFER((*inVB1), m1 + inNormalOff);
|
||
|
CHECK_VERTEX_BUFFER(outVb, outVertex);
|
||
|
CHECK_VERTEX_BUFFER(outVb, outVertex + outNormalOff);
|
||
|
|
||
|
// morph, and transform the vertex
|
||
|
*(CVector *) outVertex = *posIt + sM * (opLambda * *(CVector *) m0 + lambda * *(CVector *) m1);
|
||
|
// morph, and transform the normal
|
||
|
*(CVector *) (outVertex + outNormalOff) = M * (opLambda * *(CVector *) (m0 + inNormalOff)
|
||
|
+ lambda * *(CVector *) (m1 + inNormalOff)).normed();
|
||
|
|
||
|
|
||
|
m0 += inVSize;
|
||
|
m1 += inVSize;
|
||
|
outVertex += outVSize;
|
||
|
}
|
||
|
while (--k);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// no normal to transform
|
||
|
sM.identity();
|
||
|
sM.setRot(ptBasis->X, ptBasis->Y, ptBasis->X ^ ptBasis->Y);
|
||
|
sM.scale(*ptCurrSize);
|
||
|
|
||
|
do
|
||
|
{
|
||
|
CHECK_VERTEX_BUFFER((*inVB0), m0);
|
||
|
CHECK_VERTEX_BUFFER((*inVB1), m1);
|
||
|
CHECK_VERTEX_BUFFER(outVb, outVertex);
|
||
|
// morph, and transform the vertex
|
||
|
*(CVector *) outVertex = *posIt + sM * (opLambda * *(CVector *) m0 + opLambda * *(CVector *) m1);
|
||
|
|
||
|
m0 += inVSize;
|
||
|
m1 += inVSize;
|
||
|
outVertex += outVSize;
|
||
|
}
|
||
|
while (--k);
|
||
|
}
|
||
|
|
||
|
|
||
|
++posIt;
|
||
|
ptCurrSize += ptCurrSizeIncrement;
|
||
|
ptBasis += ptBasisIncrement;
|
||
|
currMorphValue += morphValueIncr;
|
||
|
}
|
||
|
while (posIt != endPosIt);
|
||
|
}
|
||
|
|
||
|
// compute colors if needed
|
||
|
if (m._ColorScheme)
|
||
|
{
|
||
|
m.computeColors(outVb, modelVb, size - leftToDo, toProcess, srcStep, *driver, vba, vbaRead);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// render meshs
|
||
|
driver->activeVertexBuffer(outVb);
|
||
|
m.doRenderPasses(driver, toProcess, md.RdrPasses, opaque);
|
||
|
leftToDo -= toProcess;
|
||
|
|
||
|
}
|
||
|
while (leftToDo);
|
||
|
}
|
||
|
|
||
|
|
||
|
template <class T, class U>
|
||
|
static void drawPrerotatedMeshs(T posIt,
|
||
|
U indexIt,
|
||
|
CPSConstraintMesh &m,
|
||
|
uint size,
|
||
|
uint32 srcStep,
|
||
|
bool opaque)
|
||
|
{
|
||
|
// get the vb from the original mesh
|
||
|
const CVertexBuffer &modelVb = m.getMeshVB(0);
|
||
|
|
||
|
/// precompute rotation in a VB from the src mesh
|
||
|
CVertexBuffer &prerotVb = m.makePrerotatedVb(modelVb);
|
||
|
|
||
|
// driver setup
|
||
|
IDriver *driver = m.getDriver();
|
||
|
m.setupDriverModelMatrix();
|
||
|
|
||
|
// renderPasses setup
|
||
|
nlassert(m._Owner);
|
||
|
|
||
|
// storage for sizes of meshs
|
||
|
float sizes[ConstraintMeshBufSize];
|
||
|
|
||
|
// point the size for the current mesh
|
||
|
float *ptCurrSize;
|
||
|
uint ptCurrSizeIncrement = m._SizeScheme ? 1 : 0;
|
||
|
|
||
|
T endPosIt;
|
||
|
uint leftToDo = size, toProcess;
|
||
|
const uint nbVerticesInSource = modelVb.getNumVertices();
|
||
|
|
||
|
|
||
|
|
||
|
// size of a complete prerotated model
|
||
|
const uint prerotatedModelSize = prerotVb.getVertexSize() * modelVb.getNumVertices();
|
||
|
|
||
|
/// get a mesh display struct on this shape, with eventually a primary color added.
|
||
|
CPSConstraintMesh::CMeshDisplay &md = m._MeshDisplayShare.getMeshDisplay(m._Meshes[0], modelVb, modelVb.getVertexFormat()
|
||
|
| (m._ColorScheme ? CVertexBuffer::PrimaryColorFlag : 0));
|
||
|
|
||
|
|
||
|
m.setupRenderPasses((float) m._Owner->getOwner()->getSystemDate() - m._GlobalAnimDate, md.RdrPasses, opaque);
|
||
|
|
||
|
CVertexBuffer &outVb = md.VB;
|
||
|
|
||
|
|
||
|
|
||
|
// size of vertices in prerotated model
|
||
|
const uint inVSize = prerotVb.getVertexSize();
|
||
|
|
||
|
// size ofr vertices in dest vb
|
||
|
const uint outVSize = outVb.getVertexSize();
|
||
|
|
||
|
// offset of normals in vertices of the prerotated model, and source model
|
||
|
uint normalOff=0;
|
||
|
uint pNormalOff=0;
|
||
|
if (prerotVb.getVertexFormat() & CVertexBuffer::NormalFlag)
|
||
|
{
|
||
|
normalOff = outVb.getNormalOff();
|
||
|
pNormalOff = prerotVb.getNormalOff();
|
||
|
}
|
||
|
|
||
|
if (m._ColorScheme)
|
||
|
{
|
||
|
CVertexBuffer::TVertexColorType vtc = driver->getVertexColorFormat();
|
||
|
m._ColorScheme->setColorType(vtc);
|
||
|
if (modelVb.getVertexFormat() & CVertexBuffer::PrimaryColorFlag)
|
||
|
{
|
||
|
const_cast<CVertexBuffer &>(modelVb).setVertexColorFormat(vtc);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CVertexBufferRead PrerotVba;
|
||
|
prerotVb.lock(PrerotVba);
|
||
|
do
|
||
|
{
|
||
|
toProcess = std::min(leftToDo, ConstraintMeshBufSize);
|
||
|
outVb.setNumVertices(toProcess * nbVerticesInSource);
|
||
|
{
|
||
|
CVertexBufferReadWrite vba;
|
||
|
outVb.lock(vba);
|
||
|
|
||
|
|
||
|
if (m._SizeScheme)
|
||
|
{
|
||
|
// compute size
|
||
|
ptCurrSize = (float *) (m._SizeScheme->make(m._Owner, size - leftToDo, &sizes[0], sizeof(float), toProcess, true, srcStep));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// pointer on constant size
|
||
|
ptCurrSize = &m._ParticleSize;
|
||
|
}
|
||
|
|
||
|
endPosIt = posIt + toProcess;
|
||
|
uint8 *outVertex = (uint8 *) vba.getVertexCoordPointer();
|
||
|
/// copy datas for several mesh
|
||
|
do
|
||
|
{
|
||
|
uint8 *inVertex = (uint8 *) PrerotVba.getVertexCoordPointer() + prerotatedModelSize * *indexIt; // prerotated vertex
|
||
|
uint k = nbVerticesInSource;
|
||
|
|
||
|
if (prerotVb.getVertexFormat() & CVertexBuffer::NormalFlag) // has it a normal ?
|
||
|
{
|
||
|
do
|
||
|
{
|
||
|
CHECK_VERTEX_BUFFER(outVb, outVertex);
|
||
|
CHECK_VERTEX_BUFFER(prerotVb, inVertex);
|
||
|
CHECK_VERTEX_BUFFER(outVb, outVertex + normalOff);
|
||
|
CHECK_VERTEX_BUFFER(prerotVb, inVertex + pNormalOff);
|
||
|
|
||
|
|
||
|
// translate and resize the vertex (relatively to the mesh origin)
|
||
|
*(CVector *) outVertex = *posIt + *ptCurrSize * *(CVector *) inVertex;
|
||
|
// copy the normal
|
||
|
*(CVector *) (outVertex + normalOff ) = *(CVector *) (inVertex + pNormalOff);
|
||
|
inVertex += inVSize;
|
||
|
outVertex += outVSize;
|
||
|
}
|
||
|
while (--k);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
do
|
||
|
{
|
||
|
// translate and resize the vertex (relatively to the mesh origin)
|
||
|
CHECK_VERTEX_BUFFER(outVb, outVertex);
|
||
|
CHECK_VERTEX_BUFFER(prerotVb, inVertex);
|
||
|
*(CVector *) outVertex = *posIt + *ptCurrSize * *(CVector *) inVertex;
|
||
|
inVertex += inVSize;
|
||
|
outVertex += outVSize;
|
||
|
}
|
||
|
while (--k);
|
||
|
}
|
||
|
|
||
|
++indexIt;
|
||
|
++posIt;
|
||
|
ptCurrSize += ptCurrSizeIncrement;
|
||
|
}
|
||
|
while (posIt != endPosIt);
|
||
|
|
||
|
// compute colors if needed
|
||
|
if (m._ColorScheme)
|
||
|
{
|
||
|
m.computeColors(outVb, modelVb, size - leftToDo, toProcess, srcStep, *driver, vba, PrerotVba);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// render the result
|
||
|
driver->activeVertexBuffer(outVb);
|
||
|
m.doRenderPasses(driver, toProcess, md.RdrPasses, opaque);
|
||
|
leftToDo -= toProcess;
|
||
|
|
||
|
}
|
||
|
while (leftToDo);
|
||
|
PARTICLES_CHECK_MEM
|
||
|
}
|
||
|
};
|
||
|
|
||
|
CPSConstraintMesh::CPSConstraintMesh() : _NumFaces(0),
|
||
|
_ModelBank(NULL),
|
||
|
_ModulatedStages(0),
|
||
|
_Touched(1),
|
||
|
_HasOpaqueFaces(0),
|
||
|
_VertexColorLightingForced(false),
|
||
|
_GlobalAnimationEnabled(0),
|
||
|
_ReinitGlobalAnimTimeOnNewElement(0),
|
||
|
_HasLightableFaces(0),
|
||
|
_ValidBuild(0),
|
||
|
_MorphValue(0),
|
||
|
_MorphScheme(NULL)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_CPSConstraintMesh)
|
||
|
if (CParticleSystem::getSerializeIdentifierFlag()) _Name = std::string("ConstraintMesh");
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
uint32 CPSConstraintMesh::getNumWantedTris() const
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_getNumWantedTris)
|
||
|
// nlassert(_ModelVb);
|
||
|
//return _NumFaces * _Owner->getMaxSize();
|
||
|
return _NumFaces * _Owner->getSize();
|
||
|
}
|
||
|
|
||
|
|
||
|
//====================================================================================
|
||
|
bool CPSConstraintMesh::hasTransparentFaces(void)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_hasTransparentFaces)
|
||
|
if (!_Touched) return _HasTransparentFaces != 0;
|
||
|
/// we must update the mesh to know whether it has transparent faces
|
||
|
update();
|
||
|
return _HasTransparentFaces != 0;
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
bool CPSConstraintMesh::hasOpaqueFaces(void)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_hasOpaqueFaces)
|
||
|
if (!_Touched) return _HasOpaqueFaces != 0;
|
||
|
update();
|
||
|
return _HasOpaqueFaces != 0;
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
bool CPSConstraintMesh::hasLightableFaces()
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_hasLightableFaces)
|
||
|
if (!_Touched) return _HasLightableFaces != 0;
|
||
|
update();
|
||
|
return _HasLightableFaces != 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSConstraintMesh::setShape(const std::string &meshFileName)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_setShape)
|
||
|
_MeshShapeFileName.resize(1);
|
||
|
_MeshShapeFileName[0] = meshFileName;
|
||
|
_Touched = 1;
|
||
|
_ValidBuild = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
//===========================================================================
|
||
|
std::string CPSConstraintMesh::getShape(void) const
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_getShape)
|
||
|
if (_Touched)
|
||
|
{
|
||
|
const_cast<CPSConstraintMesh *>(this)->update();
|
||
|
}
|
||
|
nlassert(_MeshShapeFileName.size() == 1);
|
||
|
return _MeshShapeFileName[0];
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
bool CPSConstraintMesh::isValidBuild() const
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_isValidBuild)
|
||
|
if (_Touched)
|
||
|
{
|
||
|
const_cast<CPSConstraintMesh *>(this)->update();
|
||
|
}
|
||
|
return _ValidBuild != 0;
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSConstraintMesh::setShapes(const std::string *shapesNames, uint numShapes)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_setShapes)
|
||
|
_MeshShapeFileName.resize(numShapes);
|
||
|
std::copy(shapesNames, shapesNames + numShapes, _MeshShapeFileName.begin());
|
||
|
_Touched = 1;
|
||
|
_ValidBuild = 0;
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
uint CPSConstraintMesh::getNumShapes() const
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_getNumShapes)
|
||
|
if (_Touched)
|
||
|
{
|
||
|
const_cast<CPSConstraintMesh *>(this)->update();
|
||
|
}
|
||
|
return _MeshShapeFileName.size();
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSConstraintMesh::getShapesNames(std::string *shapesNames) const
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_getShapesNames)
|
||
|
if (_Touched)
|
||
|
{
|
||
|
const_cast<CPSConstraintMesh *>(this)->update();
|
||
|
}
|
||
|
std::copy(_MeshShapeFileName.begin(), _MeshShapeFileName.end(), shapesNames);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSConstraintMesh::setShape(uint index, const std::string &shapeName)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_setShape)
|
||
|
nlassert(index < _MeshShapeFileName.size());
|
||
|
_MeshShapeFileName[index] = shapeName;
|
||
|
_Touched = 1;
|
||
|
_ValidBuild = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
//====================================================================================
|
||
|
const std::string &CPSConstraintMesh::getShape(uint index) const
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_getShape)
|
||
|
if (_Touched)
|
||
|
{
|
||
|
const_cast<CPSConstraintMesh *>(this)->update();
|
||
|
}
|
||
|
nlassert(index < _MeshShapeFileName.size());
|
||
|
return _MeshShapeFileName[index];
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSConstraintMesh::setMorphValue(float value)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_setMorphValue)
|
||
|
delete _MorphScheme;
|
||
|
_MorphScheme = NULL;
|
||
|
_MorphValue = value;
|
||
|
}
|
||
|
|
||
|
|
||
|
//====================================================================================
|
||
|
float CPSConstraintMesh::getMorphValue() const
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_getMorphValue)
|
||
|
return _MorphValue;
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSConstraintMesh::setMorphScheme(CPSAttribMaker<float> *scheme)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_setMorphScheme)
|
||
|
delete _MorphScheme;
|
||
|
_MorphScheme = scheme;
|
||
|
if (_MorphScheme->hasMemory()) _MorphScheme->resize(_Owner->getMaxSize(), _Owner->getSize());
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
CPSAttribMaker<float> *CPSConstraintMesh::getMorphScheme()
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_getMorphScheme)
|
||
|
return _MorphScheme;
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
const CPSAttribMaker<float> *CPSConstraintMesh::getMorphScheme() const
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_getMorphScheme)
|
||
|
return _MorphScheme;
|
||
|
}
|
||
|
|
||
|
|
||
|
//====================================================================================
|
||
|
static CMesh *GetDummyMeshFromBank(CShapeBank &sb)
|
||
|
{
|
||
|
NL_PS_FUNC(GetDummyMeshFromBank)
|
||
|
static const std::string dummyMeshName("dummy constraint mesh shape");
|
||
|
if (sb.getPresentState(dummyMeshName) == CShapeBank::Present)
|
||
|
{
|
||
|
return NLMISC::safe_cast<CMesh *>(sb.addRef(dummyMeshName));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// no dummy shape created -> add one to the bank
|
||
|
CMesh *m = CreateDummyMesh();
|
||
|
sb.add(std::string("dummy constraint mesh shape"), m);
|
||
|
return m;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSConstraintMesh::getShapeNumVerts(std::vector<sint> &numVerts)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_getShapeNumVerts)
|
||
|
_Touched = 1; // force reload
|
||
|
update(&numVerts);
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
bool CPSConstraintMesh::update(std::vector<sint> *numVertsVect /*= NULL*/)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_update)
|
||
|
bool ok = true;
|
||
|
if (!_Touched) return ok;
|
||
|
|
||
|
clean();
|
||
|
|
||
|
nlassert(_Owner->getScene());
|
||
|
|
||
|
CScene *scene = _Owner->getScene();
|
||
|
_ModelBank = scene->getShapeBank();
|
||
|
IShape *is = 0;
|
||
|
|
||
|
|
||
|
uint32 vFormat = 0;
|
||
|
uint numVerts = 0;
|
||
|
uint8 uvRouting[CVertexBuffer::MaxStage];
|
||
|
|
||
|
if (_MeshShapeFileName.size() == 0)
|
||
|
{
|
||
|
_MeshShapeFileName.resize(1);
|
||
|
_MeshShapeFileName[0] = DummyShapeName;
|
||
|
}
|
||
|
|
||
|
|
||
|
_Meshes.resize(_MeshShapeFileName.size());
|
||
|
_MeshVertexBuffers.resize(_MeshShapeFileName.size());
|
||
|
std::fill(_MeshVertexBuffers.begin(), _MeshVertexBuffers.end(), (CVertexBuffer *) NULL);
|
||
|
if (numVertsVect) numVertsVect->resize(_MeshShapeFileName.size());
|
||
|
for (uint k = 0; k < _MeshShapeFileName.size(); ++k)
|
||
|
{
|
||
|
if (_ModelBank->getPresentState(_MeshShapeFileName[k]) == CShapeBank::Present)
|
||
|
{
|
||
|
CMesh *mesh = dynamic_cast<CMesh *>( _ModelBank->addRef(_MeshShapeFileName[k]));
|
||
|
if (!mesh)
|
||
|
{
|
||
|
nlwarning("Tried to bind a shape that is not a mesh to a mesh particle : %s", _MeshShapeFileName[k].c_str());
|
||
|
_ModelBank->release(is);
|
||
|
ok = false;
|
||
|
if (numVertsVect) (*numVertsVect)[k] = ShapeFileIsNotAMesh;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_Meshes[k] = mesh;
|
||
|
/// get the mesh format, or check that is was the same that previous shapes ' one
|
||
|
if (k == 0)
|
||
|
{
|
||
|
vFormat = mesh->getVertexBuffer().getVertexFormat();
|
||
|
numVerts = mesh->getVertexBuffer().getNumVertices();
|
||
|
std::copy(mesh->getVertexBuffer().getUVRouting(), mesh->getVertexBuffer().getUVRouting() + CVertexBuffer::MaxStage, uvRouting);
|
||
|
if (numVertsVect) (*numVertsVect)[k] = (sint) numVerts;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (vFormat != mesh->getVertexBuffer().getVertexFormat())
|
||
|
{
|
||
|
nlwarning("Vertex format differs between meshs");
|
||
|
ok = false;
|
||
|
}
|
||
|
if (numVerts != mesh->getVertexBuffer().getNumVertices())
|
||
|
{
|
||
|
nlwarning("Num vertices differs between meshs");
|
||
|
ok = false;
|
||
|
}
|
||
|
if (!std::equal(mesh->getVertexBuffer().getUVRouting(), mesh->getVertexBuffer().getUVRouting() + CVertexBuffer::MaxStage, uvRouting))
|
||
|
{
|
||
|
nlwarning("UV routing differs between meshs");
|
||
|
ok = false;
|
||
|
}
|
||
|
if (numVertsVect) (*numVertsVect)[k] = (sint) mesh->getVertexBuffer().getNumVertices();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
_ModelBank->load(_MeshShapeFileName[k]);
|
||
|
}
|
||
|
catch (NLMISC::EPathNotFound &)
|
||
|
{
|
||
|
nlwarning("mesh not found : %s; used as a constraint mesh particle", _MeshShapeFileName[k].c_str());
|
||
|
// shape not found, so not present in the shape bank -> we create a dummy shape
|
||
|
}
|
||
|
|
||
|
if (_ModelBank->getPresentState(_MeshShapeFileName[k]) != CShapeBank::Present)
|
||
|
{
|
||
|
ok = false;
|
||
|
if (numVertsVect) (*numVertsVect)[k] = ShapeFileNotLoaded;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
is = _ModelBank->addRef(_MeshShapeFileName[k]);
|
||
|
if (!dynamic_cast<CMesh *>(is)) // is it a mesh
|
||
|
{
|
||
|
nlwarning("Tried to bind a shape that is not a mesh to a mesh particle : %s", _MeshShapeFileName[k].c_str());
|
||
|
_ModelBank->release(is);
|
||
|
ok = false;
|
||
|
if (numVertsVect) (*numVertsVect)[k] = ShapeFileIsNotAMesh;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CMesh &m = * NLMISC::safe_cast<CMesh *>(is);
|
||
|
/// make sure there are not too many vertices
|
||
|
if (m.getVertexBuffer().getNumVertices() > ConstraintMeshMaxNumVerts)
|
||
|
{
|
||
|
nlwarning("Tried to bind a mesh that has more than %d vertices to a particle mesh: %s", (int) ConstraintMeshMaxNumVerts, _MeshShapeFileName[k].c_str());
|
||
|
_ModelBank->release(is);
|
||
|
ok = false;
|
||
|
if (numVertsVect) (*numVertsVect)[k] = ShapeHasTooMuchVertices;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_Meshes[k] = &m;
|
||
|
if (k == 0)
|
||
|
{
|
||
|
vFormat = m.getVertexBuffer().getVertexFormat();
|
||
|
numVerts = m.getVertexBuffer().getNumVertices();
|
||
|
std::copy(m.getVertexBuffer().getUVRouting(), m.getVertexBuffer().getUVRouting() + CVertexBuffer::MaxStage, uvRouting);
|
||
|
if (numVertsVect) (*numVertsVect)[k] = numVerts;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
uint32 otherVFormat = m.getVertexBuffer().getVertexFormat();
|
||
|
uint otherNumVerts = m.getVertexBuffer().getNumVertices();
|
||
|
if (otherVFormat != vFormat ||
|
||
|
otherNumVerts != numVerts ||
|
||
|
!(std::equal(m.getVertexBuffer().getUVRouting(), m.getVertexBuffer().getUVRouting() + CVertexBuffer::MaxStage, uvRouting)))
|
||
|
{
|
||
|
ok = false;
|
||
|
}
|
||
|
if (numVertsVect) (*numVertsVect)[k] = otherNumVerts;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!ok && !numVertsVect) break;
|
||
|
}
|
||
|
|
||
|
if (!ok)
|
||
|
{
|
||
|
releaseShapes();
|
||
|
_Meshes.resize(1);
|
||
|
_MeshVertexBuffers.resize(1);
|
||
|
_Meshes[0] = GetDummyMeshFromBank(*_ModelBank);
|
||
|
_MeshVertexBuffers[0] = &_Meshes[0]->getVertexBuffer();
|
||
|
}
|
||
|
|
||
|
const CMesh &m = *_Meshes[0];
|
||
|
|
||
|
/// update the number of faces
|
||
|
_NumFaces = getMeshNumTri(m);
|
||
|
/*
|
||
|
notifyOwnerMaxNumFacesChanged();
|
||
|
if (_Owner && _Owner->getOwner())
|
||
|
{
|
||
|
_Owner->getOwner()->notifyMaxNumFacesChanged();
|
||
|
}*/
|
||
|
|
||
|
/// update opacity / transparency state
|
||
|
bool hasTransparentFaces, hasOpaqueFaces;
|
||
|
CheckForOpaqueAndTransparentFacesInMesh(m, hasTransparentFaces, hasOpaqueFaces);
|
||
|
_HasTransparentFaces = hasTransparentFaces;
|
||
|
_HasOpaqueFaces = hasOpaqueFaces;
|
||
|
_HasLightableFaces = CheckForLightableFacesInMesh(m);
|
||
|
_GlobalAnimDate = _Owner->getOwner()->getSystemDate();
|
||
|
_Touched = 0;
|
||
|
_ValidBuild = ok ? 1 : 0;
|
||
|
nlassert(_Meshes.size() > 0);
|
||
|
|
||
|
return ok;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSConstraintMesh::hintRotateTheSame(uint32 nbConfiguration,
|
||
|
float minAngularVelocity,
|
||
|
float maxAngularVelocity
|
||
|
)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_hintRotateTheSame)
|
||
|
nlassert(nbConfiguration <= ConstraintMeshMaxNumPrerotatedModels);
|
||
|
|
||
|
// TODO : avoid code duplication with CPSFace ...
|
||
|
_MinAngularVelocity = minAngularVelocity;
|
||
|
_MaxAngularVelocity = maxAngularVelocity;
|
||
|
|
||
|
|
||
|
|
||
|
_PrecompBasis.resize(nbConfiguration);
|
||
|
|
||
|
if (nbConfiguration)
|
||
|
{
|
||
|
// each precomp basis is created randomly;
|
||
|
for (uint k = 0; k < nbConfiguration; ++k)
|
||
|
{
|
||
|
CVector v = MakeRandomUnitVect();
|
||
|
_PrecompBasis[k].Basis = CPlaneBasis(v);
|
||
|
_PrecompBasis[k].Axis = MakeRandomUnitVect();
|
||
|
_PrecompBasis[k].AngularVelocity = minAngularVelocity
|
||
|
+ (rand() % 20000) / 20000.f * (maxAngularVelocity - minAngularVelocity);
|
||
|
|
||
|
}
|
||
|
|
||
|
// we need to do this because nbConfs may have changed
|
||
|
fillIndexesInPrecompBasis();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSConstraintMesh::fillIndexesInPrecompBasis(void)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_fillIndexesInPrecompBasis)
|
||
|
// TODO : avoid code duplication with CPSFace ...
|
||
|
const uint32 nbConf = _PrecompBasis.size();
|
||
|
if (_Owner)
|
||
|
{
|
||
|
_IndexInPrecompBasis.resize( _Owner->getMaxSize() );
|
||
|
}
|
||
|
for (CPSVector<uint32>::V::iterator it = _IndexInPrecompBasis.begin(); it != _IndexInPrecompBasis.end(); ++it)
|
||
|
{
|
||
|
*it = rand() % nbConf;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
/// serialisation. Derivers must override this, and call their parent version
|
||
|
void CPSConstraintMesh::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_IStream )
|
||
|
|
||
|
sint ver = f.serialVersion(4);
|
||
|
if (f.isReading())
|
||
|
{
|
||
|
clean();
|
||
|
}
|
||
|
|
||
|
CPSParticle::serial(f);
|
||
|
CPSSizedParticle::serialSizeScheme(f);
|
||
|
CPSRotated3DPlaneParticle::serialPlaneBasisScheme(f);
|
||
|
|
||
|
// prerotations ...
|
||
|
|
||
|
if (f.isReading())
|
||
|
{
|
||
|
uint32 nbConfigurations;
|
||
|
f.serial(nbConfigurations);
|
||
|
if (nbConfigurations)
|
||
|
{
|
||
|
f.serial(_MinAngularVelocity, _MaxAngularVelocity);
|
||
|
}
|
||
|
hintRotateTheSame(nbConfigurations, _MinAngularVelocity, _MaxAngularVelocity);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
uint32 nbConfigurations = _PrecompBasis.size();
|
||
|
f.serial(nbConfigurations);
|
||
|
if (nbConfigurations)
|
||
|
{
|
||
|
f.serial(_MinAngularVelocity, _MaxAngularVelocity);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// saves the model file name, or an empty string if nothing has been set
|
||
|
static std::string emptyStr;
|
||
|
|
||
|
if (ver < 4) // early version : no morphing support
|
||
|
{
|
||
|
if (!f.isReading())
|
||
|
{
|
||
|
if (_MeshShapeFileName.size() > 0)
|
||
|
{
|
||
|
f.serial(_MeshShapeFileName[0]);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
f.serial(emptyStr);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_MeshShapeFileName.resize(1);
|
||
|
f.serial(_MeshShapeFileName[0]);
|
||
|
_Touched = true;
|
||
|
_ValidBuild = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (ver > 1)
|
||
|
{
|
||
|
CPSColoredParticle::serialColorScheme(f);
|
||
|
f.serial(_ModulatedStages);
|
||
|
if (f.isReading())
|
||
|
{
|
||
|
bool vcEnabled;
|
||
|
f.serial(vcEnabled);
|
||
|
_VertexColorLightingForced = vcEnabled;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bool vcEnabled = (_VertexColorLightingForced != 0);
|
||
|
f.serial(vcEnabled);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (ver > 2) // texture animation
|
||
|
{
|
||
|
if (f.isReading())
|
||
|
{
|
||
|
bool gaEnabled;
|
||
|
f.serial(gaEnabled);
|
||
|
_GlobalAnimationEnabled = gaEnabled;
|
||
|
if (gaEnabled)
|
||
|
{
|
||
|
PGlobalTexAnims newPtr(new CGlobalTexAnims); // create new
|
||
|
//std::swap(_GlobalTexAnims, newPtr); // replace old
|
||
|
_GlobalTexAnims = newPtr;
|
||
|
f.serial(*_GlobalTexAnims);
|
||
|
}
|
||
|
|
||
|
bool rgt;
|
||
|
f.serial(rgt);
|
||
|
_ReinitGlobalAnimTimeOnNewElement = rgt;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bool gaEnabled = (_GlobalAnimationEnabled != 0);
|
||
|
f.serial(gaEnabled);
|
||
|
if (gaEnabled)
|
||
|
{
|
||
|
f.serial(*_GlobalTexAnims);
|
||
|
}
|
||
|
|
||
|
bool rgt = _ReinitGlobalAnimTimeOnNewElement != 0;
|
||
|
f.serial(rgt);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (ver > 3) // mesh morphing
|
||
|
{
|
||
|
if (!f.isReading())
|
||
|
{
|
||
|
// remove path
|
||
|
TMeshNameVect meshNamesWithoutPath = _MeshShapeFileName;
|
||
|
std::transform(meshNamesWithoutPath.begin(), meshNamesWithoutPath.end(), meshNamesWithoutPath.begin(), std::ptr_fun(NLMISC::CFile::getFilename));
|
||
|
f.serialCont(meshNamesWithoutPath);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
f.serialCont(_MeshShapeFileName);
|
||
|
}
|
||
|
bool useScheme;
|
||
|
if (f.isReading())
|
||
|
{
|
||
|
delete _MorphScheme;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
useScheme = _MorphScheme != NULL;
|
||
|
}
|
||
|
f.serial(useScheme);
|
||
|
if (useScheme)
|
||
|
{
|
||
|
f.serialPolyPtr(_MorphScheme);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
f.serial(_MorphValue);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
CPSConstraintMesh::~CPSConstraintMesh()
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_CPSConstraintMeshDtor)
|
||
|
clean();
|
||
|
delete _MorphScheme;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSConstraintMesh::releaseShapes()
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_releaseShapes)
|
||
|
for (TMeshVect::iterator it = _Meshes.begin(); it != _Meshes.end(); ++it)
|
||
|
{
|
||
|
if (*it)
|
||
|
{
|
||
|
if (_ModelBank) _ModelBank->release(*it);
|
||
|
}
|
||
|
}
|
||
|
_Meshes.clear();
|
||
|
_MeshVertexBuffers.clear();
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSConstraintMesh::clean(void)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_clean)
|
||
|
if (_ModelBank)
|
||
|
{
|
||
|
releaseShapes();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//====================================================================================
|
||
|
CVertexBuffer &CPSConstraintMesh::makePrerotatedVb(const CVertexBuffer &inVb)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_makePrerotatedVb)
|
||
|
// get a VB that has positions and eventually normals
|
||
|
CVertexBuffer &prerotatedVb = inVb.getVertexFormat() & CVertexBuffer::NormalFlag ? _PreRotatedMeshVBWithNormal : _PreRotatedMeshVB;
|
||
|
CVertexBufferReadWrite vba;
|
||
|
prerotatedVb.lock (vba);
|
||
|
CVertexBufferRead vbaIn;
|
||
|
inVb.lock (vbaIn);
|
||
|
|
||
|
// size of vertices for source VB
|
||
|
const uint vSize = inVb.getVertexSize();
|
||
|
|
||
|
// size for vertices in prerotated model
|
||
|
const uint vpSize = prerotatedVb.getVertexSize();
|
||
|
|
||
|
|
||
|
// offset of normals in vertices of the prerotated model, and source model
|
||
|
uint normalOff=0;
|
||
|
uint pNormalOff=0;
|
||
|
if (prerotatedVb.getVertexFormat() & CVertexBuffer::NormalFlag)
|
||
|
{
|
||
|
normalOff = inVb.getNormalOff();
|
||
|
pNormalOff = prerotatedVb.getNormalOff();
|
||
|
}
|
||
|
|
||
|
const uint nbVerticesInSource = inVb.getNumVertices();
|
||
|
|
||
|
|
||
|
// rotate basis
|
||
|
// and compute the set of prerotated meshs that will then duplicated (with scale and translation) to create the Vb of what must be drawn
|
||
|
uint8 *outVertex = (uint8 *) vba.getVertexCoordPointer();
|
||
|
for (CPSVector<CPlaneBasisPair>::V::iterator it = _PrecompBasis.begin(); it != _PrecompBasis.end(); ++it)
|
||
|
{
|
||
|
// not optimized at all, but this will apply to very few elements anyway...
|
||
|
CMatrix mat;
|
||
|
mat.rotate(CQuat(it->Axis, CParticleSystem::EllapsedTime * it->AngularVelocity));
|
||
|
CVector n = mat * it->Basis.getNormal();
|
||
|
it->Basis = CPlaneBasis(n);
|
||
|
|
||
|
mat.identity();
|
||
|
mat.setRot(it->Basis.X, it->Basis.Y, it->Basis.X ^ it->Basis.Y);
|
||
|
|
||
|
uint8 *inVertex = (uint8 *) vbaIn.getVertexCoordPointer();
|
||
|
|
||
|
uint k = nbVerticesInSource;
|
||
|
|
||
|
// check whether we need to rotate normals as well...
|
||
|
if (inVb.getVertexFormat() & CVertexBuffer::NormalFlag)
|
||
|
{
|
||
|
|
||
|
do
|
||
|
{
|
||
|
CHECK_VERTEX_BUFFER(inVb, inVertex);
|
||
|
CHECK_VERTEX_BUFFER(inVb, inVertex + normalOff);
|
||
|
CHECK_VERTEX_BUFFER(prerotatedVb, outVertex);
|
||
|
CHECK_VERTEX_BUFFER(prerotatedVb, outVertex + pNormalOff);
|
||
|
|
||
|
* (CVector *) outVertex = mat.mulVector(* (CVector *) inVertex);
|
||
|
* (CVector *) (outVertex + normalOff) = mat.mulVector(* (CVector *) (inVertex + pNormalOff) );
|
||
|
outVertex += vpSize;
|
||
|
inVertex += vSize;
|
||
|
|
||
|
}
|
||
|
while (--k);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// no normal included
|
||
|
do
|
||
|
{
|
||
|
|
||
|
CHECK_VERTEX_BUFFER(prerotatedVb, outVertex);
|
||
|
CHECK_VERTEX_BUFFER(inVb, inVertex);
|
||
|
|
||
|
* (CVector *) outVertex = mat.mulVector(* (CVector *) inVertex);
|
||
|
outVertex += vpSize;
|
||
|
inVertex += vSize;
|
||
|
}
|
||
|
while (--k);
|
||
|
|
||
|
}
|
||
|
}
|
||
|
return prerotatedVb;
|
||
|
}
|
||
|
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSConstraintMesh::step(TPSProcessPass pass)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_step)
|
||
|
if (
|
||
|
(pass == PSBlendRender && hasTransparentFaces())
|
||
|
|| (pass == PSSolidRender && hasOpaqueFaces())
|
||
|
)
|
||
|
{
|
||
|
draw(pass == PSSolidRender);
|
||
|
}
|
||
|
else
|
||
|
if (pass == PSToolRender) // edition mode only
|
||
|
{
|
||
|
showTool();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSConstraintMesh::draw(bool opaque)
|
||
|
{
|
||
|
// if (!FilterPS[4]) return;
|
||
|
NL_PS_FUNC(CPSConstraintMesh_draw)
|
||
|
PARTICLES_CHECK_MEM;
|
||
|
nlassert(_Owner);
|
||
|
|
||
|
update(); // update mesh datas if needed
|
||
|
uint32 step;
|
||
|
uint numToProcess;
|
||
|
computeSrcStep(step, numToProcess);
|
||
|
if (!numToProcess) return;
|
||
|
_Owner->incrementNbDrawnParticles(numToProcess); // for benchmark purpose
|
||
|
|
||
|
|
||
|
if (_PrecompBasis.size() == 0) /// do we deal with prerotated meshs ?
|
||
|
{
|
||
|
if (step == (1 << 16))
|
||
|
{
|
||
|
CPSConstraintMeshHelper::drawMeshs(_Owner->getPos().begin(),
|
||
|
*this,
|
||
|
numToProcess,
|
||
|
step,
|
||
|
opaque
|
||
|
);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CPSConstraintMeshHelper::drawMeshs(TIteratorVectStep1616(_Owner->getPos().begin(), 0, step),
|
||
|
*this,
|
||
|
numToProcess,
|
||
|
step,
|
||
|
opaque
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (step == (1 << 16))
|
||
|
{
|
||
|
CPSConstraintMeshHelper::drawPrerotatedMeshs(_Owner->getPos().begin(),
|
||
|
_IndexInPrecompBasis.begin(),
|
||
|
*this,
|
||
|
numToProcess,
|
||
|
step,
|
||
|
opaque
|
||
|
);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
typedef CAdvance1616Iterator<CPSVector<uint32>::V::const_iterator, uint32> TIndexIterator;
|
||
|
CPSConstraintMeshHelper::drawPrerotatedMeshs(TIteratorVectStep1616(_Owner->getPos().begin(), 0, step),
|
||
|
TIndexIterator(_IndexInPrecompBasis.begin(), 0, step),
|
||
|
*this,
|
||
|
numToProcess,
|
||
|
step,
|
||
|
opaque
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSConstraintMesh::setupMaterialColor(CMaterial &destMat, CMaterial &srcMat)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_setupMaterialColor)
|
||
|
if (destMat.getShader() != CMaterial::Normal) return;
|
||
|
for (uint k = 0; k < IDRV_MAT_MAXTEXTURES; ++k)
|
||
|
{
|
||
|
if (_ModulatedStages & (1 << k))
|
||
|
{
|
||
|
destMat.texEnvArg0RGB(k, CMaterial::Texture, CMaterial::SrcColor);
|
||
|
destMat.texEnvArg0Alpha(k, CMaterial::Texture, CMaterial::SrcAlpha);
|
||
|
destMat.texEnvArg1RGB(k, CMaterial::Diffuse, CMaterial::SrcColor);
|
||
|
destMat.texEnvArg1Alpha(k, CMaterial::Diffuse, CMaterial::SrcAlpha);
|
||
|
destMat.texEnvOpRGB(k, CMaterial::Modulate);
|
||
|
destMat.texEnvOpAlpha(k, CMaterial::Modulate);
|
||
|
}
|
||
|
else // restore from source material
|
||
|
{
|
||
|
destMat.setTexEnvMode(k, srcMat.getTexEnvMode(k));
|
||
|
}
|
||
|
}
|
||
|
if (_ColorScheme == NULL) // per mesh color ?
|
||
|
{
|
||
|
destMat.setColor(_Color);
|
||
|
if (destMat.isLighted())
|
||
|
{
|
||
|
destMat.setDiffuse(_Color);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSConstraintMesh::setupRenderPasses(float date, TRdrPassSet &rdrPasses, bool opaque)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_setupRenderPasses)
|
||
|
// render meshs : we process each rendering pass
|
||
|
for (TRdrPassSet::iterator rdrPassIt = rdrPasses.begin();
|
||
|
rdrPassIt != rdrPasses.end(); ++rdrPassIt)
|
||
|
{
|
||
|
|
||
|
CMaterial &Mat = rdrPassIt->Mat;
|
||
|
CMaterial &SourceMat = rdrPassIt->SourceMat;
|
||
|
|
||
|
|
||
|
/// check whether this material has to be rendered
|
||
|
if ((opaque && Mat.getZWrite()) || (!opaque && ! Mat.getZWrite()))
|
||
|
{
|
||
|
|
||
|
|
||
|
// has to setup material constant color ?
|
||
|
// global color not supported for mesh
|
||
|
/* CParticleSystem &ps = *(_Owner->getOwner());
|
||
|
if (!_ColorScheme)
|
||
|
{
|
||
|
NLMISC::CRGBA col;
|
||
|
col.modulateFromColor(SourceMat.getColor(), _Color);
|
||
|
if (ps.getColorAttenuationScheme() == NULL || ps.isUserColorUsed())
|
||
|
{
|
||
|
col.modulateFromColor(col, ps.getGlobalColor());
|
||
|
}
|
||
|
Mat.setColor(col);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Mat.setColor(ps.getGlobalColor());
|
||
|
}*/
|
||
|
|
||
|
/** Force modulation for some stages & setup global color
|
||
|
*/
|
||
|
setupMaterialColor(Mat, SourceMat);
|
||
|
|
||
|
/// force vertex lighting
|
||
|
bool forceVertexcolorLighting;
|
||
|
if (_ColorScheme != NULL)
|
||
|
{
|
||
|
forceVertexcolorLighting = _VertexColorLightingForced != 0 ? true : SourceMat.getLightedVertexColor();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
forceVertexcolorLighting = false;
|
||
|
}
|
||
|
if (forceVertexcolorLighting != Mat.getLightedVertexColor()) // avoid to touch mat if not needed
|
||
|
{
|
||
|
Mat.setLightedVertexColor(forceVertexcolorLighting);
|
||
|
}
|
||
|
|
||
|
///global texture animation
|
||
|
if (_GlobalAnimationEnabled != 0)
|
||
|
{
|
||
|
for (uint k = 0; k < IDRV_MAT_MAXTEXTURES; ++k)
|
||
|
{
|
||
|
if (Mat.getTexture(k) != NULL)
|
||
|
{
|
||
|
Mat.enableUserTexMat(k, true);
|
||
|
CMatrix mat;
|
||
|
_GlobalTexAnims->Anims[k].buildMatrix(date, mat);
|
||
|
Mat.setUserTexMat(k ,mat);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSConstraintMesh::doRenderPasses(IDriver *driver, uint numObj, TRdrPassSet &rdrPasses, bool opaque)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_doRenderPasses)
|
||
|
// render meshs : we process each rendering pass
|
||
|
for (TRdrPassSet::iterator rdrPassIt = rdrPasses.begin(); rdrPassIt != rdrPasses.end(); ++rdrPassIt)
|
||
|
{
|
||
|
CMaterial &Mat = rdrPassIt->Mat;
|
||
|
if ((opaque && Mat.getZWrite()) || (!opaque && ! Mat.getZWrite()))
|
||
|
{
|
||
|
/// setup number of primitives to be rendered
|
||
|
rdrPassIt->PbTri.setNumIndexes(((rdrPassIt->PbTri.capacity()/3) * numObj / ConstraintMeshBufSize) * 3);
|
||
|
rdrPassIt->PbLine.setNumIndexes(((rdrPassIt->PbLine.capacity()/2) * numObj / ConstraintMeshBufSize) * 2);
|
||
|
|
||
|
/// render the primitives
|
||
|
driver->activeIndexBuffer (rdrPassIt->PbTri);
|
||
|
driver->renderTriangles(rdrPassIt->Mat, 0, rdrPassIt->PbTri.getNumIndexes()/3);
|
||
|
if (rdrPassIt->PbLine.getNumIndexes() != 0)
|
||
|
{
|
||
|
driver->activeIndexBuffer (rdrPassIt->PbLine);
|
||
|
driver->renderLines(rdrPassIt->Mat, 0, rdrPassIt->PbLine.getNumIndexes()/2);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSConstraintMesh::computeColors(CVertexBuffer &outVB, const CVertexBuffer &inVB, uint startIndex, uint toProcess, uint32 srcStep, IDriver &drv,
|
||
|
CVertexBufferReadWrite &vba,
|
||
|
CVertexBufferRead &vbaIn
|
||
|
)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_computeColors)
|
||
|
nlassert(_ColorScheme);
|
||
|
// there are 2 case : 1 - the source mesh has colors, which are modulated with the current color
|
||
|
// 2 - the source mesh has no colors : colors are directly copied into the dest vb
|
||
|
|
||
|
if (inVB.getVertexFormat() & CVertexBuffer::PrimaryColorFlag) // case 1
|
||
|
{
|
||
|
// TODO: optimisation : avoid to duplicate colors...
|
||
|
_ColorScheme->makeN(_Owner, startIndex, vba.getColorPointer(), outVB.getVertexSize(), toProcess, inVB.getNumVertices(), srcStep);
|
||
|
// modulate from the source mesh
|
||
|
// todo hulud d3d vertex color RGBA / BGRA
|
||
|
uint8 *vDest = (uint8 *) vba.getColorPointer();
|
||
|
uint8 *vSrc = (uint8 *) vbaIn.getColorPointer();
|
||
|
const uint vSize = outVB.getVertexSize();
|
||
|
const uint numVerts = inVB.getNumVertices();
|
||
|
uint meshSize = vSize * numVerts;
|
||
|
for (uint k = 0; k < toProcess; ++k)
|
||
|
{
|
||
|
NLMISC::CRGBA::modulateColors((CRGBA *) vDest, (CRGBA *) vSrc, (CRGBA *) vDest, numVerts, vSize, vSize);
|
||
|
vDest += meshSize;
|
||
|
}
|
||
|
}
|
||
|
else // case 2
|
||
|
{
|
||
|
_ColorScheme->makeN(_Owner, startIndex, vba.getColorPointer(), outVB.getVertexSize(), toProcess, inVB.getNumVertices(), srcStep);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSConstraintMesh::newElement(const CPSEmitterInfo &info)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_newElement)
|
||
|
newSizeElement(info);
|
||
|
newPlaneBasisElement(info);
|
||
|
// TODO : avoid code cuplication with CPSFace ...
|
||
|
const uint32 nbConf = _PrecompBasis.size();
|
||
|
if (nbConf) // do we use precomputed basis ?
|
||
|
{
|
||
|
_IndexInPrecompBasis[_Owner->getNewElementIndex()] = rand() % nbConf;
|
||
|
}
|
||
|
newColorElement(info);
|
||
|
if (_GlobalAnimationEnabled && _ReinitGlobalAnimTimeOnNewElement)
|
||
|
{
|
||
|
_GlobalAnimDate = _Owner->getOwner()->getSystemDate();
|
||
|
}
|
||
|
if (_MorphScheme && _MorphScheme->hasMemory()) _MorphScheme->newElement(info);
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSConstraintMesh::deleteElement(uint32 index)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_deleteElement)
|
||
|
deleteSizeElement(index);
|
||
|
deletePlaneBasisElement(index);
|
||
|
// TODO : avoid code cuplication with CPSFace ...
|
||
|
if (_PrecompBasis.size()) // do we use precomputed basis ?
|
||
|
{
|
||
|
// replace ourself by the last element...
|
||
|
_IndexInPrecompBasis[index] = _IndexInPrecompBasis[_Owner->getSize() - 1];
|
||
|
}
|
||
|
deleteColorElement(index);
|
||
|
if (_MorphScheme && _MorphScheme->hasMemory()) _MorphScheme->deleteElement(index);
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSConstraintMesh::resize(uint32 size)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_resize)
|
||
|
nlassert(size < (1 << 16));
|
||
|
resizeSize(size);
|
||
|
resizePlaneBasis(size);
|
||
|
// TODO : avoid code cuplication with CPSFace ...
|
||
|
if (_PrecompBasis.size()) // do we use precomputed basis ?
|
||
|
{
|
||
|
_IndexInPrecompBasis.resize(size);
|
||
|
}
|
||
|
resizeColor(size);
|
||
|
if (_MorphScheme && _MorphScheme->hasMemory()) _MorphScheme->resize(size, _Owner->getSize());
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSConstraintMesh::updateMatAndVbForColor(void)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_updateMatAndVbForColor)
|
||
|
// nothing to do for us...
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSConstraintMesh::forceStageModulationByColor(uint stage, bool force)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_forceStageModulationByColor)
|
||
|
nlassert(stage < IDRV_MAT_MAXTEXTURES);
|
||
|
if (force)
|
||
|
{
|
||
|
_ModulatedStages |= 1 << stage;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_ModulatedStages &= ~(1 << stage);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
bool CPSConstraintMesh::isStageModulationForced(uint stage) const
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_isStageModulationForced)
|
||
|
nlassert(stage < IDRV_MAT_MAXTEXTURES);
|
||
|
return (_ModulatedStages & (1 << stage)) != 0;
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
|
||
|
/** This duplicate a primitive block n time in the destination primitive block
|
||
|
* This is used to draw several mesh at once
|
||
|
* For each duplication, vertices indices are shifted from the given offset (number of vertices in the mesh)
|
||
|
*/
|
||
|
|
||
|
static void DuplicatePrimitiveBlock(const CIndexBuffer &srcBlock, CIndexBuffer &destBlock, uint nbReplicate, uint vertOffset)
|
||
|
{
|
||
|
NL_PS_FUNC(DuplicatePrimitiveBlock)
|
||
|
PARTICLES_CHECK_MEM;
|
||
|
|
||
|
// this must be update each time a new primitive is added
|
||
|
|
||
|
// loop counters, and index of the current primitive in the dest pb
|
||
|
uint k, l, index;
|
||
|
|
||
|
// the current vertex offset.
|
||
|
uint currVertOffset;
|
||
|
|
||
|
|
||
|
// duplicate triangles
|
||
|
uint numTri = srcBlock.getNumIndexes()/3;
|
||
|
destBlock.setFormat(NL_DEFAULT_INDEX_BUFFER_FORMAT);
|
||
|
destBlock.setNumIndexes(3 * numTri * nbReplicate);
|
||
|
|
||
|
index = 0;
|
||
|
currVertOffset = 0;
|
||
|
|
||
|
CIndexBufferRead ibaRead;
|
||
|
srcBlock.lock (ibaRead);
|
||
|
CIndexBufferReadWrite ibaWrite;
|
||
|
destBlock.lock (ibaWrite);
|
||
|
|
||
|
|
||
|
nlassert(destBlock.getFormat() == CIndexBuffer::Indices16);
|
||
|
|
||
|
// TMP TMP TMP
|
||
|
if (ibaRead.getFormat() == CIndexBuffer::Indices16)
|
||
|
{
|
||
|
const TIndexType *triPtr = (TIndexType *) ibaRead.getPtr();
|
||
|
const TIndexType *currTriPtr; // current Tri
|
||
|
for (k = 0; k < nbReplicate; ++k)
|
||
|
{
|
||
|
currTriPtr = triPtr;
|
||
|
for (l = 0; l < numTri; ++l)
|
||
|
{
|
||
|
ibaWrite.setTri(3*index, currTriPtr[0] + currVertOffset, currTriPtr[1] + currVertOffset, currTriPtr[2] + currVertOffset);
|
||
|
currTriPtr += 3;
|
||
|
++ index;
|
||
|
}
|
||
|
currVertOffset += vertOffset;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
const uint32 *triPtr = (uint32 *) ibaRead.getPtr();
|
||
|
const uint32 *currTriPtr; // current Tri
|
||
|
for (k = 0; k < nbReplicate; ++k)
|
||
|
{
|
||
|
currTriPtr = triPtr;
|
||
|
for (l = 0; l < numTri; ++l)
|
||
|
{
|
||
|
nlassert(currTriPtr[0] + currVertOffset <= 0xffff);
|
||
|
nlassert(currTriPtr[1] + currVertOffset <= 0xffff);
|
||
|
nlassert(currTriPtr[2] + currVertOffset <= 0xffff);
|
||
|
//
|
||
|
ibaWrite.setTri(3*index, (uint16) (currTriPtr[0] + currVertOffset), (uint16) (currTriPtr[1] + currVertOffset), (uint16) (currTriPtr[2] + currVertOffset));
|
||
|
currTriPtr += 3;
|
||
|
++ index;
|
||
|
}
|
||
|
currVertOffset += vertOffset;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// TODO quad / strips duplication : (unimplemented in primitive blocks for now)
|
||
|
|
||
|
PARTICLES_CHECK_MEM;
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSConstraintMesh::initPrerotVB()
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_initPrerotVB)
|
||
|
// position, no normals
|
||
|
_PreRotatedMeshVB.setVertexFormat(CVertexBuffer::PositionFlag);
|
||
|
_PreRotatedMeshVB.setNumVertices(ConstraintMeshMaxNumPrerotatedModels * ConstraintMeshMaxNumVerts);
|
||
|
_PreRotatedMeshVB.setName("CPSConstraintMesh::_PreRotatedMeshVB");
|
||
|
|
||
|
// position & normals
|
||
|
_PreRotatedMeshVBWithNormal.setVertexFormat(CVertexBuffer::PositionFlag | CVertexBuffer::NormalFlag);
|
||
|
_PreRotatedMeshVBWithNormal.setNumVertices(ConstraintMeshMaxNumPrerotatedModels * ConstraintMeshMaxNumVerts);
|
||
|
_PreRotatedMeshVB.setName("CPSConstraintMesh::_PreRotatedMeshVBWithNormal");
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
CPSConstraintMesh::CMeshDisplay &CPSConstraintMesh::CMeshDisplayShare::getMeshDisplay(CMesh *mesh, const CVertexBuffer &meshVB, uint32 format)
|
||
|
{
|
||
|
NL_PS_FUNC(CMeshDisplayShare_getMeshDisplay)
|
||
|
nlassert(mesh);
|
||
|
// linear search is ok because of small size
|
||
|
for(std::list<CMDEntry>::iterator it = _Cache.begin(); it != _Cache.end(); ++it)
|
||
|
{
|
||
|
if (it->Format == format && it->Mesh == mesh)
|
||
|
{
|
||
|
// relink at start (most recent use)
|
||
|
_Cache.splice(_Cache.begin(), _Cache, it);
|
||
|
return it->MD;
|
||
|
}
|
||
|
}
|
||
|
if (_NumMD == _MaxNumMD)
|
||
|
{
|
||
|
_Cache.pop_back(); // remove least recently used mesh
|
||
|
-- _NumMD;
|
||
|
}
|
||
|
//NLMISC::TTicks start = NLMISC::CTime::getPerformanceTime();
|
||
|
_Cache.push_front(CMDEntry());
|
||
|
_Cache.front().Mesh = mesh;
|
||
|
_Cache.front().Format = format;
|
||
|
buildRdrPassSet(_Cache.front().MD.RdrPasses, *mesh);
|
||
|
_Cache.front().MD.VB.setName("CPSConstraintMesh::CMeshDisplay");
|
||
|
buildVB(_Cache.front().MD.VB, meshVB, format);
|
||
|
++ _NumMD;
|
||
|
/*NLMISC::TTicks end = NLMISC::CTime::getPerformanceTime();
|
||
|
nlinfo("mesh setup time = %.2f", (float) (1000 * NLMISC::CTime::ticksToSecond(end - start))); */
|
||
|
return _Cache.front().MD;
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSConstraintMesh::CMeshDisplayShare::buildRdrPassSet(TRdrPassSet &dest, const CMesh &m)
|
||
|
{
|
||
|
NL_PS_FUNC(CMeshDisplayShare_buildRdrPassSet)
|
||
|
// we don't support skinning for mesh particles, so there must be only one matrix block
|
||
|
nlassert(m.getNbMatrixBlock() == 1); // SKINNING UNSUPPORTED
|
||
|
|
||
|
dest.resize(m.getNbRdrPass(0));
|
||
|
const CVertexBuffer &srcVb = m.getVertexBuffer();
|
||
|
|
||
|
for (uint k = 0; k < m.getNbRdrPass(0); ++k)
|
||
|
{
|
||
|
dest[k].Mat = m.getMaterial(m.getRdrPassMaterial(0, k));
|
||
|
dest[k].SourceMat = dest[k].Mat;
|
||
|
DuplicatePrimitiveBlock(m.getRdrPassPrimitiveBlock(0, k), dest[k].PbTri, ConstraintMeshBufSize, srcVb.getNumVertices() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//====================================================================================
|
||
|
void CPSConstraintMesh::CMeshDisplayShare::buildVB(CVertexBuffer &dest, const CVertexBuffer &meshVb, uint32 destFormat)
|
||
|
{
|
||
|
NL_PS_FUNC(CMeshDisplayShare_buildVB)
|
||
|
/// we duplicate the original mesh data's 'ConstraintMeshBufSize' times, eventually adding a color
|
||
|
nlassert(destFormat == meshVb.getVertexFormat() || destFormat == (meshVb.getVertexFormat() | (uint32) CVertexBuffer::PrimaryColorFlag) );
|
||
|
dest.setVertexFormat(destFormat);
|
||
|
dest.setPreferredMemory(CVertexBuffer::AGPVolatile, true);
|
||
|
dest.setNumVertices(ConstraintMeshBufSize * meshVb.getNumVertices());
|
||
|
for(uint k = 0; k < CVertexBuffer::MaxStage; ++k)
|
||
|
{
|
||
|
dest.setUVRouting((uint8) k, meshVb.getUVRouting()[k]);
|
||
|
}
|
||
|
|
||
|
CVertexBufferReadWrite vba;
|
||
|
dest.lock (vba);
|
||
|
CVertexBufferRead vbaIn;
|
||
|
meshVb.lock (vbaIn);
|
||
|
|
||
|
uint8 *outPtr = (uint8 *) vba.getVertexCoordPointer();
|
||
|
uint8 *inPtr = (uint8 *) vbaIn.getVertexCoordPointer();
|
||
|
uint meshSize = dest.getVertexSize() * meshVb.getNumVertices();
|
||
|
|
||
|
if (destFormat == meshVb.getVertexFormat()) // no color added
|
||
|
{
|
||
|
for (uint k = 0; k < ConstraintMeshBufSize; ++k)
|
||
|
{
|
||
|
::memcpy((void *) (outPtr + k * meshSize), (void *) inPtr, meshSize);
|
||
|
}
|
||
|
}
|
||
|
else // color added, but not available in src
|
||
|
{
|
||
|
sint colorOff = dest.getColorOff();
|
||
|
uint inVSize = meshVb.getVertexSize();
|
||
|
uint outVSize = dest.getVertexSize();
|
||
|
for (uint k = 0; k < ConstraintMeshBufSize; ++k)
|
||
|
{
|
||
|
for (uint v = 0; v < meshVb.getNumVertices(); ++v)
|
||
|
{
|
||
|
// copy until color
|
||
|
::memcpy((void *) (outPtr + k * meshSize + v * outVSize), (void *) (inPtr + v * inVSize), colorOff);
|
||
|
// copy datas after color
|
||
|
::memcpy((void *) (outPtr + k * meshSize + v * outVSize + colorOff + sizeof(uint8[4])), (void *) (inPtr + v * inVSize + colorOff), inVSize - colorOff);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//=====================================================================================
|
||
|
CPSConstraintMesh::CGlobalTexAnim::CGlobalTexAnim() : TransOffset(NLMISC::CVector2f::Null),
|
||
|
TransSpeed(NLMISC::CVector2f::Null),
|
||
|
TransAccel(NLMISC::CVector2f::Null),
|
||
|
ScaleStart(1 ,1),
|
||
|
ScaleSpeed(NLMISC::CVector2f::Null),
|
||
|
ScaleAccel(NLMISC::CVector2f::Null),
|
||
|
WRotSpeed(0),
|
||
|
WRotAccel(0)
|
||
|
{
|
||
|
NL_PS_FUNC(CGlobalTexAnim_CGlobalTexAnim)
|
||
|
}
|
||
|
|
||
|
//=====================================================================================
|
||
|
void CPSConstraintMesh::CGlobalTexAnim::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
|
||
|
{
|
||
|
NL_PS_FUNC(CGlobalTexAnim_IStream )
|
||
|
// version 1 : added offset
|
||
|
sint ver = f.serialVersion(1);
|
||
|
if (ver >= 1)
|
||
|
{
|
||
|
f.serial(TransOffset);
|
||
|
}
|
||
|
f.serial(TransSpeed, TransAccel, ScaleStart, ScaleSpeed, ScaleAccel);
|
||
|
f.serial(WRotSpeed, WRotAccel);
|
||
|
}
|
||
|
|
||
|
//=====================================================================================
|
||
|
void CPSConstraintMesh::CGlobalTexAnim::buildMatrix(float date, NLMISC::CMatrix &dest)
|
||
|
{
|
||
|
NL_PS_FUNC(CGlobalTexAnim_buildMatrix)
|
||
|
float fDate = (float) date;
|
||
|
float halfDateSquared = 0.5f * fDate * fDate;
|
||
|
NLMISC::CVector2f pos = fDate * TransSpeed + halfDateSquared * fDate * TransAccel + TransOffset;
|
||
|
NLMISC::CVector2f scale = ScaleStart + fDate * ScaleSpeed + halfDateSquared * fDate * ScaleAccel;
|
||
|
float rot = fDate * WRotSpeed + halfDateSquared * WRotAccel;
|
||
|
|
||
|
|
||
|
float fCos, fSin;
|
||
|
if (rot != 0.f)
|
||
|
{
|
||
|
fCos = ::cosf(- rot);
|
||
|
fSin = ::sinf(- rot);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fCos = 1.f;
|
||
|
fSin = 0.f;
|
||
|
}
|
||
|
|
||
|
NLMISC::CVector I(fCos, fSin, 0);
|
||
|
NLMISC::CVector J(-fSin, fCos, 0);
|
||
|
dest.setRot(scale.x * I, scale.y * J, NLMISC::CVector::K);
|
||
|
NLMISC::CVector center(-0.5f, -0.5f, 0.f);
|
||
|
NLMISC::CVector t(pos.x, pos.y, 0);
|
||
|
dest.setPos(t + dest.mulVector(center) - center);
|
||
|
}
|
||
|
|
||
|
//=====================================================================================
|
||
|
void CPSConstraintMesh::setGlobalTexAnim(uint stage, const CGlobalTexAnim &properties)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_setGlobalTexAnim)
|
||
|
nlassert(_GlobalAnimationEnabled != 0);
|
||
|
nlassert(stage < IDRV_MAT_MAXTEXTURES);
|
||
|
nlassert(_GlobalTexAnims.get());
|
||
|
_GlobalTexAnims->Anims[stage] = properties;
|
||
|
}
|
||
|
|
||
|
//=====================================================================================
|
||
|
const CPSConstraintMesh::CGlobalTexAnim &CPSConstraintMesh::getGlobalTexAnim(uint stage) const
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_getGlobalTexAnim)
|
||
|
nlassert(_GlobalAnimationEnabled != 0);
|
||
|
nlassert(stage < IDRV_MAT_MAXTEXTURES);
|
||
|
nlassert(_GlobalTexAnims.get());
|
||
|
return _GlobalTexAnims->Anims[stage];
|
||
|
}
|
||
|
|
||
|
|
||
|
//=====================================================================================
|
||
|
CPSConstraintMesh::TTexAnimType CPSConstraintMesh::getTexAnimType() const
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_getTexAnimType)
|
||
|
return (TTexAnimType) (_GlobalAnimationEnabled != 0 ? GlobalAnim : NoAnim);
|
||
|
}
|
||
|
|
||
|
//=====================================================================================
|
||
|
void CPSConstraintMesh::setTexAnimType(TTexAnimType type)
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_setTexAnimType)
|
||
|
nlassert(type < Last);
|
||
|
if (type == getTexAnimType()) return; // does the type of animation change ?
|
||
|
switch (type)
|
||
|
{
|
||
|
case NoAnim:
|
||
|
_GlobalTexAnims.reset();
|
||
|
restoreMaterials();
|
||
|
_GlobalAnimationEnabled = 0;
|
||
|
break;
|
||
|
case GlobalAnim:
|
||
|
{
|
||
|
PGlobalTexAnims newPtr(new CGlobalTexAnims);
|
||
|
//std::swap(_GlobalTexAnims, newPtr);
|
||
|
_GlobalTexAnims = newPtr;
|
||
|
_GlobalAnimationEnabled = 1;
|
||
|
}
|
||
|
break;
|
||
|
default: break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//=====================================================================================
|
||
|
void CPSConstraintMesh::CGlobalTexAnims::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
|
||
|
{
|
||
|
NL_PS_FUNC(CGlobalTexAnims_IStream )
|
||
|
f.serialVersion(0);
|
||
|
for (uint k = 0; k < IDRV_MAT_MAXTEXTURES; ++k)
|
||
|
{
|
||
|
f.serial(Anims[k]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//=====================================================================================
|
||
|
void CPSConstraintMesh::restoreMaterials()
|
||
|
{
|
||
|
NL_PS_FUNC(CPSConstraintMesh_restoreMaterials)
|
||
|
update();
|
||
|
CMeshDisplay &md= _MeshDisplayShare.getMeshDisplay(_Meshes[0], getMeshVB(0), _Meshes[0]->getVertexBuffer().getVertexFormat() | (_ColorScheme ? CVertexBuffer::PrimaryColorFlag : 0));
|
||
|
TRdrPassSet rdrPasses = md.RdrPasses;
|
||
|
// render meshs : we process each rendering pass
|
||
|
for (TRdrPassSet::iterator rdrPassIt = rdrPasses.begin(); rdrPassIt != rdrPasses.end(); ++rdrPassIt)
|
||
|
{
|
||
|
rdrPassIt->Mat = rdrPassIt->SourceMat;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//=====================================================================================
|
||
|
const CVertexBuffer &CPSConstraintMesh::getMeshVB(uint index)
|
||
|
{
|
||
|
nlassert(!_Touched);
|
||
|
nlassert(index < _Meshes.size());
|
||
|
nlassert(_MeshVertexBuffers.size() == _Meshes.size());
|
||
|
const CVertexBuffer *vb = _MeshVertexBuffers[index];
|
||
|
if (!vb )
|
||
|
{
|
||
|
CMesh &mesh = * NLMISC::safe_cast<CMesh *>((IShape *) _Meshes[index]);
|
||
|
vb = _MeshVertexBuffers[index] = &mesh.getVertexBuffer();
|
||
|
}
|
||
|
if (vb->getLocation() != CVertexBuffer::NotResident)
|
||
|
{
|
||
|
TMeshName2RamVB::iterator it = _MeshRamVBs.find(_MeshShapeFileName[index]);
|
||
|
if (it == _MeshRamVBs.end())
|
||
|
{
|
||
|
CVertexBuffer &destVb = _MeshRamVBs[_MeshShapeFileName[index]];
|
||
|
CMesh &mesh = * NLMISC::safe_cast<CMesh *>((IShape *) _Meshes[index]);
|
||
|
mesh.getVertexBuffer().copyVertices(destVb);
|
||
|
_MeshVertexBuffers[index] = vb = &destVb;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_MeshVertexBuffers[index] = vb = &it->second;
|
||
|
}
|
||
|
}
|
||
|
nlassert(vb->getLocation() == CVertexBuffer::NotResident);
|
||
|
return *vb;
|
||
|
}
|
||
|
|
||
|
//=====================================================================================
|
||
|
void CPSMesh::onShow(bool shown)
|
||
|
{
|
||
|
for(uint k = 0; k < _Instances.getSize(); ++k)
|
||
|
{
|
||
|
if (_Instances[k])
|
||
|
{
|
||
|
if (shown)
|
||
|
{
|
||
|
_Instances[k]->show();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_Instances[k]->hide();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} // NL3D
|