Use configured resolution for login/outgame/ingame
[ryzomcore.git] / nel / src / 3d / mesh_mrm_skinned.cpp
blobb7f0045ecfaa17d098f8003190b3206fdc85cd47
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2011 Robert TIMM (rti) <mail@rtti.de>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "std3d.h"
22 #include "nel/misc/bsphere.h"
23 #include "nel/misc/system_info.h"
24 #include "nel/misc/hierarchical_timer.h"
25 #include "nel/misc/fast_mem.h"
26 #include "nel/3d/mesh_mrm_skinned.h"
27 #include "nel/3d/mrm_builder.h"
28 #include "nel/3d/mrm_parameters.h"
29 #include "nel/3d/mesh_mrm_skinned_instance.h"
30 #include "nel/3d/scene.h"
31 #include "nel/3d/skeleton_model.h"
32 #include "nel/3d/stripifier.h"
33 #include "nel/3d/mesh_blender.h"
34 #include "nel/3d/render_trav.h"
35 #include "nel/misc/fast_floor.h"
36 #include "nel/3d/raw_skinned.h"
37 #include "nel/3d/shifted_triangle_cache.h"
38 #include "nel/3d/texture_file.h"
39 #include "nel/3d/matrix_3x4.h"
42 using namespace NLMISC;
43 using namespace std;
45 #ifdef DEBUG_NEW
46 #define new DEBUG_NEW
47 #endif
49 namespace NL3D
53 H_AUTO_DECL( NL3D_MeshMRMGeom_RenderShadow )
55 // ***************************************************************************
56 // ***************************************************************************
57 // CMeshMRMSkinnedGeom::CLod
58 // ***************************************************************************
59 // ***************************************************************************
62 // ***************************************************************************
63 void CMeshMRMSkinnedGeom::CLod::serial(NLMISC::IStream &f)
66 Version 0:
67 - base vdrsion.
70 f.serialVersion(0);
71 uint i;
73 f.serial(NWedges);
74 f.serialCont(RdrPass);
75 f.serialCont(Geomorphs);
76 f.serialCont(MatrixInfluences);
78 // Serial array of InfluencedVertices. NB: code written so far for NL3D_MESH_SKINNING_MAX_MATRIX==4 only.
79 nlassert(NL3D_MESH_SKINNING_MAX_MATRIX==4);
80 for(i= 0; i<NL3D_MESH_SKINNING_MAX_MATRIX; i++)
82 f.serialCont(InfluencedVertices[i]);
87 // ***************************************************************************
88 void CMeshMRMSkinnedGeom::CLod::optimizeTriangleOrder()
90 CStripifier stripifier;
92 // for all rdrpass
93 for(uint rp=0; rp<RdrPass.size(); rp++ )
95 // stripify list of triangles of this pass.
96 CIndexBuffer block;
97 getRdrPassPrimitiveBlock(rp, block);
99 stripifier.optimizeTriangles(block, block);
101 buildPrimitiveBlock(rp, block);
106 // ***************************************************************************
107 void CMeshMRMSkinnedGeom::CLod::getRdrPassPrimitiveBlock (uint renderPass, CIndexBuffer &block) const
109 const CRdrPass &rd = RdrPass[renderPass];
110 const uint count = rd.getNumTriangle();
111 block.setFormat(NL_SKINNED_MESH_MRM_INDEX_FORMAT);
112 block.setNumIndexes (count*3);
114 CIndexBufferReadWrite ibaWrite;
115 block.lock (ibaWrite);
117 uint i;
118 for (i=0; i<count; i++)
120 ibaWrite.setTri (i*3, rd.PBlock[i*3+0], rd.PBlock[i*3+1], rd.PBlock[i*3+2]);
124 // ***************************************************************************
126 void CMeshMRMSkinnedGeom::CLod::buildPrimitiveBlock(uint renderPass, const CIndexBuffer &block)
128 CRdrPass &rd = RdrPass[renderPass];
129 const uint count = block.getNumIndexes()/3;
130 rd.PBlock.resize (3*count);
131 uint i;
132 CIndexBufferRead ibaRead;
133 block.lock (ibaRead);
134 if (ibaRead.getFormat() == CIndexBuffer::Indices32)
136 const uint32 *triPtr = (const uint32 *) ibaRead.getPtr ();
137 for (i=0; i<count; i++)
139 rd.PBlock[i*3+0] = (uint16) (triPtr[3*i+0]);
140 rd.PBlock[i*3+1] = (uint16) (triPtr[3*i+1]);
141 rd.PBlock[i*3+2] = (uint16) (triPtr[3*i+2]);
144 else
146 const uint16 *triPtr = (const uint16 *) ibaRead.getPtr ();
147 for (i=0; i<count; i++)
149 rd.PBlock[i*3+0] = (triPtr[3*i+0]);
150 rd.PBlock[i*3+1] = (triPtr[3*i+1]);
151 rd.PBlock[i*3+2] = (triPtr[3*i+2]);
156 // ***************************************************************************
157 // ***************************************************************************
158 // CMeshMRMSkinnedGeom.
159 // ***************************************************************************
160 // ***************************************************************************
165 // ***************************************************************************
166 static NLMISC::CAABBoxExt makeBBox(const std::vector<CVector> &Vertices)
168 NLMISC::CAABBox ret;
169 nlassert(!Vertices.empty());
170 ret.setCenter(Vertices[0]);
171 for(sint i=0;i<(sint)Vertices.size();i++)
173 ret.extend(Vertices[i]);
176 return ret;
180 // ***************************************************************************
181 CMeshMRMSkinnedGeom::CMeshMRMSkinnedGeom()
183 _BoneIdComputed = false;
184 _BoneIdExtended = false;
185 _PreciseClipping= false;
186 _MeshDataId= 0;
187 _SupportShadowSkinGrouping= false;
191 // ***************************************************************************
192 CMeshMRMSkinnedGeom::~CMeshMRMSkinnedGeom()
197 // ***************************************************************************
198 void CMeshMRMSkinnedGeom::changeMRMDistanceSetup(float distanceFinest, float distanceMiddle, float distanceCoarsest)
200 // check input.
201 if(distanceFinest<0) return;
202 if(distanceMiddle<=distanceFinest) return;
203 if(distanceCoarsest<=distanceMiddle) return;
205 // Change.
206 _LevelDetail.DistanceFinest= distanceFinest;
207 _LevelDetail.DistanceMiddle= distanceMiddle;
208 _LevelDetail.DistanceCoarsest= distanceCoarsest;
210 // compile
211 _LevelDetail.compileDistanceSetup();
214 // ***************************************************************************
216 void CMeshMRMSkinnedGeom::build(CMesh::CMeshBuild &m,
217 uint numMaxMaterial, const CMRMParameters &params)
219 // Empty geometry?
220 if(m.Vertices.empty() || m.Faces.empty())
222 _VBufferFinal.clear();
223 _Lods.clear();
224 _BBox.setCenter(CVector::Null);
225 _BBox.setSize(CVector::Null);
226 return;
228 nlassert(numMaxMaterial>0);
231 /// 0. First, make bbox.
232 //======================
233 // NB: this is equivalent as building BBox from MRM VBuffer, because CMRMBuilder create new vertices
234 // which are just interpolation of original vertices.
235 _BBox= makeBBox(m.Vertices);
238 /// 1. Launch the MRM build process.
239 //================================================
240 CMRMBuilder mrmBuilder;
241 CMeshBuildMRM meshBuildMRM;
242 std::vector<CMesh::CMeshBuild*> bsList;
244 mrmBuilder.compileMRM(m, bsList, params, meshBuildMRM, numMaxMaterial);
245 nlassert (meshBuildMRM.Skinned);
247 // Then build the packed vertex buffer
248 //================================================
249 _VBufferFinal.build (meshBuildMRM.VBuffer, meshBuildMRM.SkinWeights);
250 _Lods= meshBuildMRM.Lods;
252 // Compute degradation control.
253 //================================================
254 _LevelDetail.DistanceFinest= meshBuildMRM.DistanceFinest;
255 _LevelDetail.DistanceMiddle= meshBuildMRM.DistanceMiddle;
256 _LevelDetail.DistanceCoarsest= meshBuildMRM.DistanceCoarsest;
257 nlassert(_LevelDetail.DistanceFinest>=0);
258 nlassert(_LevelDetail.DistanceMiddle > _LevelDetail.DistanceFinest);
259 nlassert(_LevelDetail.DistanceCoarsest > _LevelDetail.DistanceMiddle);
260 // Compute OODistDelta and DistancePow
261 _LevelDetail.compileDistanceSetup();
264 // For load balancing.
265 //================================================
266 // compute Max Face Used
267 _LevelDetail.MaxFaceUsed= 0;
268 _LevelDetail.MinFaceUsed= 0;
269 // Count of primitive block
270 if(!_Lods.empty())
272 uint pb;
273 // Compute MinFaces.
274 CLod &firstLod= _Lods[0];
275 for (pb=0; pb<firstLod.RdrPass.size(); pb++)
277 CRdrPass &pass= firstLod.RdrPass[pb];
278 // Sum tri
279 _LevelDetail.MinFaceUsed+= pass.getNumTriangle ();
281 // Compute MaxFaces.
282 CLod &lastLod= _Lods[_Lods.size()-1];
283 for (pb=0; pb<lastLod.RdrPass.size(); pb++)
285 CRdrPass &pass= lastLod.RdrPass[pb];
286 // Sum tri
287 _LevelDetail.MaxFaceUsed+= pass.getNumTriangle ();
292 // For skinning.
293 //================================================
295 // Inform that the mesh data has changed
296 dirtMeshDataId();
299 // For AGP SKinning optim, and for Render optim
300 //================================================
301 uint i;
302 for(i=0;i<_Lods.size();i++)
304 // sort triangles for better cache use.
305 _Lods[i].optimizeTriangleOrder();
308 // No Blend Shapes
309 //================================================
310 nlassert (meshBuildMRM.BlendShapes.empty());
312 // Compact bone id and build a bone id names
313 //================================================
315 // Remap
316 std::map<uint, uint> remap;
318 // Current bone
319 uint currentBone = 0;
321 // Reserve memory
322 _BonesName.reserve (m.BonesNames.size());
324 // For each vertices
325 uint vert;
326 CPackedVertexBuffer::CPackedVertex *vertices = _VBufferFinal.getPackedVertices();
327 for (vert=0; vert<_VBufferFinal.getNumVertices(); vert++)
329 // Found one ?
330 bool found=false;
332 // For each weight
333 uint weight;
334 for (weight=0; weight<NL3D_MESH_MRM_SKINNED_MAX_MATRIX; weight++)
336 // Active ?
337 if ((vertices[vert].Weights[weight]>0)||(weight==0))
339 // Look for it
340 std::map<uint, uint>::iterator ite = remap.find (vertices[vert].Matrices[weight]);
342 // Find ?
343 if (ite == remap.end())
345 // Insert it
346 remap.insert (std::map<uint, uint>::value_type (vertices[vert].Matrices[weight], currentBone));
348 // Check the id
349 nlassert (vertices[vert].Matrices[weight]<m.BonesNames.size());
351 // Set the bone name
352 _BonesName.push_back (m.BonesNames[vertices[vert].Matrices[weight]]);
354 // Set the local bone id
355 vertices[vert].Matrices[weight] = currentBone++;
357 else
359 // Set the local bone id
360 vertices[vert].Matrices[weight] = ite->second;
363 // Found one
364 found = true;
368 // Found one ?
369 nlassert (found);
372 // Remap the vertex influence by lods
373 uint lod;
374 for (lod=0; lod<_Lods.size(); lod++)
376 // For each matrix used
377 uint matrix;
378 for (matrix=0; matrix<_Lods[lod].MatrixInfluences.size(); matrix++)
380 // Remap
381 std::map<uint, uint>::iterator ite = remap.find (_Lods[lod].MatrixInfluences[matrix]);
383 // Not find ?
384 if (ite == remap.end())
386 // Remove it
387 _Lods[lod].MatrixInfluences.erase (_Lods[lod].MatrixInfluences.begin()+matrix);
388 matrix--;
389 continue;
392 // Remap
393 _Lods[lod].MatrixInfluences[matrix] = ite->second;
397 // Misc.
398 //===================
399 // Some runtime not serialized compilation
400 compileRunTime();
404 // ***************************************************************************
405 void CMeshMRMSkinnedGeom::applyMaterialRemap(const std::vector<sint> &remap)
407 for(uint lod=0;lod<getNbLod();lod++)
409 for(uint rp=0;rp<getNbRdrPass(lod);rp++)
411 // remap
412 uint32 &matId= _Lods[lod].RdrPass[rp].MaterialId;
413 nlassert(remap[matId]>=0);
414 matId= remap[matId];
419 // ***************************************************************************
420 void CMeshMRMSkinnedGeom::applyGeomorph(std::vector<CMRMWedgeGeom> &geoms, float alphaLod)
422 applyGeomorphWithVBHardPtr(geoms, alphaLod);
426 // ***************************************************************************
427 void CMeshMRMSkinnedGeom::applyGeomorphWithVBHardPtr(std::vector<CMRMWedgeGeom> &geoms, float alphaLod)
429 // no geomorphs? quit.
430 if(geoms.empty())
431 return;
433 clamp(alphaLod, 0.f, 1.f);
434 sint a= (uint)floor((256.f*alphaLod)+0.5f);
435 sint a1= 256 - a;
438 // info from VBuffer.
439 uint8 *vertexPtr= (uint8*)_VBufferFinal.getPackedVertices();
440 sint32 vertexSize= sizeof(CPackedVertexBuffer::CPackedVertex);
442 // use a faster method
443 applyGeomorphPosNormalUV0Int(geoms, vertexPtr, vertexPtr, vertexSize, a, a1);
447 // ***************************************************************************
448 void CMeshMRMSkinnedGeom::applyGeomorphPosNormalUV0(std::vector<CMRMWedgeGeom> &geoms, uint8 *vertexPtr, uint8 *vertexDestPtr, sint32 vertexSize, float a, float a1)
450 nlassert(vertexSize==32);
453 // For all geomorphs.
454 uint nGeoms= (uint)geoms.size();
455 CMRMWedgeGeom *ptrGeom= &(geoms[0]);
456 uint8 *destPtr= vertexDestPtr;
457 for(; nGeoms>0; nGeoms--, ptrGeom++, destPtr+= vertexSize )
459 // Consider the Pos/Normal/UV as an array of 8 float to interpolate.
460 float *start= (float*)(vertexPtr + (ptrGeom->Start<<5));
461 float *end= (float*)(vertexPtr + (ptrGeom->End<<5));
462 float *dst= (float*)(destPtr);
464 // unrolled
465 dst[0]= start[0] * a + end[0]* a1;
466 dst[1]= start[1] * a + end[1]* a1;
467 dst[2]= start[2] * a + end[2]* a1;
468 dst[3]= start[3] * a + end[3]* a1;
469 dst[4]= start[4] * a + end[4]* a1;
470 dst[5]= start[5] * a + end[5]* a1;
471 dst[6]= start[6] * a + end[6]* a1;
472 dst[7]= start[7] * a + end[7]* a1;
477 // ***************************************************************************
478 void CMeshMRMSkinnedGeom::applyGeomorphPosNormalUV0Int(std::vector<CMRMWedgeGeom> &geoms, uint8 *vertexPtr, uint8 *vertexDestPtr, sint32 vertexSize, sint a, sint a1)
480 // For all geomorphs.
481 uint nGeoms= (uint)geoms.size();
482 CMRMWedgeGeom *ptrGeom= &(geoms[0]);
483 uint8 *destPtr= vertexDestPtr;
484 for(; nGeoms>0; nGeoms--, ptrGeom++, destPtr+= vertexSize )
486 // Consider the Pos/Normal/UV as an array of 8 float to interpolate.
487 sint16 *start= (sint16*)(vertexPtr + (ptrGeom->Start*vertexSize));
488 sint16 *end= (sint16*)(vertexPtr + (ptrGeom->End*vertexSize));
489 sint16 *dst= (sint16*)(destPtr);
491 /* Hulud
492 * This is slow but, we don't care because this method is called for debug purpose only (watch skin without skinning)
495 // unrolled
496 dst[0]= (sint16)(((sint)(start[0]) * a + (sint)(end[0]) * a1)>>8);
497 dst[1]= (sint16)(((sint)(start[1]) * a + (sint)(end[1]) * a1)>>8);
498 dst[2]= (sint16)(((sint)(start[2]) * a + (sint)(end[2]) * a1)>>8);
499 dst[3]= (sint16)(((sint)(start[3]) * a + (sint)(end[3]) * a1)>>8);
500 dst[4]= (sint16)(((sint)(start[4]) * a + (sint)(end[4]) * a1)>>8);
501 dst[5]= (sint16)(((sint)(start[5]) * a + (sint)(end[5]) * a1)>>8);
502 dst[6]= (sint16)(((sint)(start[6]) * a + (sint)(end[6]) * a1)>>8);
503 dst[7]= (sint16)(((sint)(start[7]) * a + (sint)(end[7]) * a1)>>8);
508 // ***************************************************************************
509 void CMeshMRMSkinnedGeom::initInstance(CMeshBaseInstance *mbi)
514 // ***************************************************************************
515 bool CMeshMRMSkinnedGeom::clip(const std::vector<CPlane> &pyramid, const CMatrix &worldMatrix)
517 // Speed Clip: clip just the sphere.
518 CBSphere localSphere(_BBox.getCenter(), _BBox.getRadius());
519 CBSphere worldSphere;
521 // transform the sphere in WorldMatrix (with nearly good scale info).
522 localSphere.applyTransform(worldMatrix, worldSphere);
524 // if out of only plane, entirely out.
525 for(sint i=0;i<(sint)pyramid.size();i++)
527 // We are sure that pyramid has normalized plane normals.
528 // if SpherMax OUT return false.
529 float d= pyramid[i]*worldSphere.Center;
530 if(d>worldSphere.Radius)
531 return false;
534 // test if must do a precise clip, according to mesh size.
535 if( _PreciseClipping )
537 CPlane localPlane;
539 // if out of only plane, entirely out.
540 for(sint i=0;i<(sint)pyramid.size();i++)
542 // Transform the pyramid in Object space.
543 localPlane= pyramid[i]*worldMatrix;
544 // localPlane must be normalized, because worldMatrix mya have a scale.
545 localPlane.normalize();
546 // if the box is not partially inside the plane, quit
547 if( !_BBox.clipBack(localPlane) )
548 return false;
552 return true;
556 // ***************************************************************************
557 inline sint CMeshMRMSkinnedGeom::chooseLod(float alphaMRM, float &alphaLod)
559 // Choose what Lod to draw.
560 alphaMRM*= _Lods.size()-1;
561 sint numLod= (sint)ceil(alphaMRM);
562 if(numLod==0)
564 numLod= 0;
565 alphaLod= 0;
567 else
569 // Lerp beetween lod i-1 and lod i.
570 alphaLod= alphaMRM-(numLod-1);
573 /// Ensure numLod is correct
574 if(numLod>=(sint)_Lods.size())
576 numLod= (sint)_Lods.size()-1;
577 alphaLod= 1;
580 return numLod;
584 // ***************************************************************************
585 void CMeshMRMSkinnedGeom::render(IDriver *drv, CTransformShape *trans, float polygonCount, uint32 rdrFlags, float globalAlpha)
587 nlassert(drv);
588 if(_Lods.empty())
589 return;
592 // get the meshMRM instance.
593 CMeshBaseInstance *mi= safe_cast<CMeshBaseInstance*>(trans);
596 // get the result of the Load Balancing.
597 float alphaMRM= _LevelDetail.getLevelDetailFromPolyCount(polygonCount);
599 // choose the lod.
600 float alphaLod;
601 sint numLod= chooseLod(alphaMRM, alphaLod);
604 // Render the choosen Lod.
605 CLod &lod= _Lods[numLod];
606 if(lod.RdrPass.empty())
607 return;
610 // get the skeleton model to which I am binded (else NULL).
611 CSkeletonModel *skeleton;
612 skeleton = mi->getSkeletonModel();
613 // The mesh must not be skinned for render()
614 nlassert(!(mi->isSkinned() && skeleton));
617 // Profiling
618 //===========
619 H_AUTO( NL3D_MeshMRMGeom_RenderNormal );
621 // Skinning.
622 //===========
624 // set the instance worldmatrix.
625 drv->setupModelMatrix(trans->getWorldMatrix());
628 // Geomorph.
629 //===========
630 // Geomorph the choosen Lod (if not the coarser mesh).
631 if(numLod>0)
633 applyGeomorph(lod.Geomorphs, alphaLod);
637 // force normalisation of normals..
638 bool bkupNorm= drv->isForceNormalize();
639 drv->forceNormalize(true);
642 // Setup meshVertexProgram
643 //===========
645 // Render the lod.
646 //===========
647 // active VB.
649 /* Hulud
650 * This is slow but, we don't care because this method is called for debug purpose only (watch skin without skinning)
652 CVertexBuffer tmp;
653 getVertexBuffer (tmp);
655 drv->activeVertexBuffer(tmp);
658 // Global alpha used ?
659 uint32 globalAlphaUsed= rdrFlags & IMeshGeom::RenderGlobalAlpha;
660 uint8 globalAlphaInt=(uint8)NLMISC::OptFastFloor(globalAlpha*255);
662 // Render all pass.
663 if (globalAlphaUsed)
665 bool gaDisableZWrite= (rdrFlags & IMeshGeom::RenderGADisableZWrite)?true:false;
667 // for all passes
668 for(uint i=0;i<lod.RdrPass.size();i++)
670 CRdrPass &rdrPass= lod.RdrPass[i];
672 if ( ( (mi->Materials[rdrPass.MaterialId].getBlend() == false) && (rdrFlags & IMeshGeom::RenderOpaqueMaterial) ) ||
673 ( (mi->Materials[rdrPass.MaterialId].getBlend() == true) && (rdrFlags & IMeshGeom::RenderTransparentMaterial) ) )
675 // CMaterial Ref
676 CMaterial &material=mi->Materials[rdrPass.MaterialId];
678 // Use a MeshBlender to modify material and driver.
679 CMeshBlender blender;
680 blender.prepareRenderForGlobalAlpha(material, drv, globalAlpha, globalAlphaInt, gaDisableZWrite);
682 /* Hulud
683 * This is slow but, we don't care because this method is called for debug purpose only (watch skin without skinning)
685 CIndexBuffer block;
686 lod.getRdrPassPrimitiveBlock (i, block);
688 // Render
689 drv->activeIndexBuffer(block);
690 drv->renderTriangles(material, 0, block.getNumIndexes()/3);
692 // Resetup material/driver
693 blender.restoreRender(material, drv, gaDisableZWrite);
697 else
699 for(uint i=0;i<lod.RdrPass.size();i++)
701 CRdrPass &rdrPass= lod.RdrPass[i];
703 if ( ( (mi->Materials[rdrPass.MaterialId].getBlend() == false) && (rdrFlags & IMeshGeom::RenderOpaqueMaterial) ) ||
704 ( (mi->Materials[rdrPass.MaterialId].getBlend() == true) && (rdrFlags & IMeshGeom::RenderTransparentMaterial) ) )
706 // CMaterial Ref
707 CMaterial &material=mi->Materials[rdrPass.MaterialId];
709 /* Hulud
710 * This is slow but, we don't care because this method is called for debug purpose only (watch skin without skinning)
712 CIndexBuffer block;
713 lod.getRdrPassPrimitiveBlock (i, block);
715 // Render with the Materials of the MeshInstance.
716 drv->activeIndexBuffer(block);
717 drv->renderTriangles(material, 0, block.getNumIndexes()/3);
722 // bkup force normalisation.
723 drv->forceNormalize(bkupNorm);
728 // ***************************************************************************
729 void CMeshMRMSkinnedGeom::renderSkin(CTransformShape *trans, float alphaMRM)
734 // ***************************************************************************
735 bool CMeshMRMSkinnedGeom::supportSkinGrouping() const
737 return true;
740 // ***************************************************************************
741 sint CMeshMRMSkinnedGeom::renderSkinGroupGeom(CMeshMRMSkinnedInstance *mi, float alphaMRM, uint remainingVertices, uint8 *vbDest)
743 H_AUTO( NL3D_MeshMRMGeom_rdrSkinGrpGeom )
745 // since not tested in supportSkinGrouping(), must test _Lods.empty(): no lod, no draw
746 if(_Lods.empty())
747 return 0;
749 // get a ptr on scene
750 CScene *ownerScene= mi->getOwnerScene();
751 // get a ptr on renderTrav
752 CRenderTrav *renderTrav= &ownerScene->getRenderTrav();
753 // get a ptr on the driver
754 IDriver *drv= renderTrav->getDriver();
755 nlassert(drv);
758 // choose the lod.
759 float alphaLod;
760 sint numLod= chooseLod(alphaMRM, alphaLod);
761 _LastLodComputed= numLod;
764 // Render the choosen Lod.
765 CLod &lod= _Lods[numLod];
766 if(lod.RdrPass.empty())
767 // return no vertices added
768 return 0;
770 // If the Lod is too big to render in the VBufferHard
771 if(lod.NWedges>remainingVertices)
772 // return Failure
773 return -1;
775 // get the skeleton model to which I am skinned
776 CSkeletonModel *skeleton;
777 skeleton = mi->getSkeletonModel();
778 // must be skinned for renderSkin()
779 nlassert(mi->isSkinned() && skeleton);
782 // Profiling
783 //===========
784 H_AUTO( NL3D_MeshMRMGeom_rdrSkinGrpGeom_go );
787 // Skinning.
788 //===========
790 // Use RawSkin if possible: only if no morph, and only Vertex/Normal
791 updateRawSkinNormal(true, mi, numLod);
793 // NB: the skeleton matrix has already been setuped by CSkeletonModel
794 // NB: the normalize flag has already been setuped by CSkeletonModel
796 // applySkin with RawSkin.
797 //--------
798 nlassert(mi->_RawSkinCache);
800 H_AUTO( NL3D_RawSkinning );
802 // RawSkin do all the job in optimized way: Skinning, copy to VBHard and Geomorph.
804 // skinning with normal, but no tangent space
805 applyRawSkinWithNormal (lod, *(mi->_RawSkinCache), skeleton, vbDest, alphaLod);
807 // Vertices are packed in RawSkin mode (ie no holes due to MRM!)
808 return (sint)mi->_RawSkinCache->Geomorphs.size() +
809 mi->_RawSkinCache->TotalSoftVertices +
810 mi->_RawSkinCache->TotalHardVertices;
813 // ***************************************************************************
814 void CMeshMRMSkinnedGeom::renderSkinGroupPrimitives(CMeshMRMSkinnedInstance *mi, uint baseVertex, std::vector<CSkinSpecularRdrPass> &specularRdrPasses, uint skinIndex)
816 H_AUTO( NL3D_MeshMRMGeom_rdrSkinGrpPrimitives );
818 // get a ptr on scene
819 CScene *ownerScene= mi->getOwnerScene();
820 // get a ptr on renderTrav
821 CRenderTrav *renderTrav= &ownerScene->getRenderTrav();
822 // get a ptr on the driver
823 IDriver *drv= renderTrav->getDriver();
824 nlassert(drv);
826 // Get the lod choosen in renderSkinGroupGeom()
827 CLod &lod= _Lods[_LastLodComputed];
830 // must update primitive cache
831 updateShiftedTriangleCache(mi, _LastLodComputed, baseVertex);
832 nlassert(mi->_ShiftedTriangleCache);
835 // Render Triangles with cache
836 //===========
837 for(uint i=0;i<lod.RdrPass.size();i++)
839 CRdrPass &rdrPass= lod.RdrPass[i];
841 // CMaterial Ref
842 CMaterial &material=mi->Materials[rdrPass.MaterialId];
844 // TestYoyo. Material Speed Test
845 /*if( material.getDiffuse()!=CRGBA(250, 251, 252) )
847 material.setDiffuse(CRGBA(250, 251, 252));
848 // Set all texture the same.
849 static CSmartPtr<ITexture> pTexFile= new CTextureFile("fy_hom_visage_c1_fy_e1.tga");
850 material.setTexture(0, pTexFile );
851 // Remove Specular.
852 if(material.getShader()==CMaterial::Specular)
854 CSmartPtr<ITexture> tex= material.getTexture(0);
855 material.setShader(CMaterial::Normal);
856 material.setTexture(0, tex );
858 // Remove MakeUp
859 material.setTexture(1, NULL);
862 // If the material is a specular material, don't render it now!
863 if(material.getShader()==CMaterial::Specular)
865 // Add it to the rdrPass to sort!
866 CSkinSpecularRdrPass specRdrPass;
867 specRdrPass.SkinIndex= skinIndex;
868 specRdrPass.RdrPassIndex= i;
869 // Get the handle of the specular Map as the sort Key
870 ITexture *specTex= material.getTexture(1);
871 if(!specTex)
872 specRdrPass.SpecId= 0;
873 else
874 specRdrPass.SpecId= drv->getTextureHandle( *specTex );
875 // Append it to the list
876 specularRdrPasses.push_back(specRdrPass);
878 else
880 // Get the shifted triangles.
881 CShiftedTriangleCache::CRdrPass &shiftedRdrPass= mi->_ShiftedTriangleCache->RdrPass[i];
883 // Render with the Materials of the MeshInstance.
884 drv->activeIndexBuffer(mi->_ShiftedTriangleCache->RawIndices);
885 drv->renderTriangles(material, shiftedRdrPass.Triangles, shiftedRdrPass.NumTriangles);
891 // ***************************************************************************
892 void CMeshMRMSkinnedGeom::renderSkinGroupSpecularRdrPass(CMeshMRMSkinnedInstance *mi, uint rdrPassId)
894 H_AUTO( NL3D_MeshMRMGeom_rdrSkinGrpSpecularRdrPass );
896 // get a ptr on scene
897 CScene *ownerScene= mi->getOwnerScene();
898 // get a ptr on renderTrav
899 CRenderTrav *renderTrav= &ownerScene->getRenderTrav();
900 // get a ptr on the driver
901 IDriver *drv= renderTrav->getDriver();
902 nlassert(drv);
904 // Get the lod choosen in renderSkinGroupGeom()
905 CLod &lod= _Lods[_LastLodComputed];
908 // _ShiftedTriangleCache must have been computed in renderSkinGroupPrimitives
909 nlassert(mi->_ShiftedTriangleCache);
912 // Render Triangles with cache
913 //===========
914 CRdrPass &rdrPass= lod.RdrPass[rdrPassId];
916 // CMaterial Ref
917 CMaterial &material=mi->Materials[rdrPass.MaterialId];
919 // Get the shifted triangles.
920 CShiftedTriangleCache::CRdrPass &shiftedRdrPass= mi->_ShiftedTriangleCache->RdrPass[rdrPassId];
922 // Render with the Materials of the MeshInstance.
923 drv->activeIndexBuffer(mi->_ShiftedTriangleCache->RawIndices);
924 drv->renderTriangles(material, shiftedRdrPass.Triangles, shiftedRdrPass.NumTriangles);
928 // ***************************************************************************
929 void CMeshMRMSkinnedGeom::updateShiftedTriangleCache(CMeshMRMSkinnedInstance *mi, sint curLodId, uint baseVertex)
931 // if the instance has a cache, but not sync to us, delete it.
932 if( mi->_ShiftedTriangleCache && (
933 mi->_ShiftedTriangleCache->MeshDataId != _MeshDataId ||
934 mi->_ShiftedTriangleCache->LodId != curLodId ||
935 mi->_ShiftedTriangleCache->BaseVertex != baseVertex) )
937 mi->clearShiftedTriangleCache();
940 // If the instance has not a valid cache, must create it.
941 if( !mi->_ShiftedTriangleCache )
943 mi->_ShiftedTriangleCache= new CShiftedTriangleCache;
944 // Fill the cache Key.
945 mi->_ShiftedTriangleCache->MeshDataId= _MeshDataId;
946 mi->_ShiftedTriangleCache->LodId= curLodId;
947 mi->_ShiftedTriangleCache->BaseVertex= baseVertex;
949 // Build list of PBlock. From Lod, or from RawSkin cache.
950 static vector<CIndexBuffer*> pbList;
951 pbList.clear();
952 nlassert(mi->_RawSkinCache);
953 pbList.resize(mi->_RawSkinCache->RdrPass.size());
954 for(uint i=0;i<pbList.size();i++)
956 pbList[i]= &mi->_RawSkinCache->RdrPass[i];
959 // Build RdrPass
960 mi->_ShiftedTriangleCache->RdrPass.resize((uint32)pbList.size());
962 // First pass, count number of triangles, and fill header info
963 uint totalTri= 0;
964 uint i;
965 for(i=0;i<pbList.size();i++)
967 mi->_ShiftedTriangleCache->RdrPass[i].NumTriangles= pbList[i]->getNumIndexes()/3;
968 totalTri+= pbList[i]->getNumIndexes()/3;
971 // Allocate triangles indices.
972 mi->_ShiftedTriangleCache->RawIndices.setFormat(NL_SKINNED_MESH_MRM_INDEX_FORMAT);
973 mi->_ShiftedTriangleCache->RawIndices.setNumIndexes(totalTri*3);
975 CIndexBufferReadWrite iba;
976 mi->_ShiftedTriangleCache->RawIndices.lock(iba);
978 // Second pass, fill ptrs, and fill Arrays
979 uint indexTri= 0;
980 for(i=0;i<pbList.size();i++)
982 CShiftedTriangleCache::CRdrPass &dstRdrPass= mi->_ShiftedTriangleCache->RdrPass[i];
983 dstRdrPass.Triangles= indexTri*3;
985 // Fill the array
986 uint numTris= pbList[i]->getNumIndexes()/3;
987 if(numTris)
989 uint nIds= numTris*3;
990 // index, and fill
991 CIndexBufferRead ibaRead;
992 pbList[i]->lock (ibaRead);
993 #ifndef NL_SKINNED_MESH_MRM_INDEX16
994 nlassert(iba.getFormat() == CIndexBuffer::Indices32);
995 const uint32 *pSrcTri= (const uint32 *) ibaRead.getPtr();
996 uint32 *pDstTri= (uint32 *) iba.getPtr() + dstRdrPass.Triangles;
997 for(;nIds>0;nIds--,pSrcTri++,pDstTri++)
998 *pDstTri= *pSrcTri + baseVertex;
999 #else
1000 nlassert(iba.getFormat() == CIndexBuffer::Indices16);
1001 const uint16 *pSrcTri= (const uint16 *) ibaRead.getPtr();
1002 uint16 *pDstTri= (uint16 *) iba.getPtr() + dstRdrPass.Triangles;
1003 for(;nIds>0;nIds--,pSrcTri++,pDstTri++)
1004 *pDstTri= *pSrcTri + baseVertex;
1005 #endif
1008 // Next
1009 indexTri+= dstRdrPass.NumTriangles;
1015 // ***************************************************************************
1016 void CMeshMRMSkinnedGeom::serial(NLMISC::IStream &f)
1018 // because of complexity, serial is separated in save / load.
1021 Version 0:
1022 - base version.
1024 f.serialVersion(0);
1027 // serial bones names
1028 f.serialCont (_BonesName);
1030 if (f.isReading())
1032 // Bones index are in skeleton model id list
1033 _BoneIdComputed = false;
1035 // Must always recompute usage of parents of bones used.
1036 _BoneIdExtended = false;
1039 // serial Basic info.
1040 // ==================
1041 f.serial(_BBox);
1042 f.serial(_LevelDetail.MaxFaceUsed);
1043 f.serial(_LevelDetail.MinFaceUsed);
1044 f.serial(_LevelDetail.DistanceFinest);
1045 f.serial(_LevelDetail.DistanceMiddle);
1046 f.serial(_LevelDetail.DistanceCoarsest);
1047 f.serial(_LevelDetail.OODistanceDelta);
1048 f.serial(_LevelDetail.DistancePow);
1050 // Prepare the VBuffer.
1051 if (f.isReading())
1052 _VBufferFinal.contReset();
1053 _VBufferFinal.serial(f);
1055 // serial Shadow Skin Information
1056 f.serialCont (_ShadowSkin.Vertices);
1057 f.serialCont (_ShadowSkin.Triangles);
1059 // resest the Lod arrays. NB: each Lod is empty, and ready to receive Lod data.
1060 // ==================
1061 if (f.isReading())
1063 contReset(_Lods);
1066 f.serialCont (_Lods);
1068 if (f.isReading())
1070 // Inform that the mesh data has changed
1071 dirtMeshDataId();
1073 // Some runtime not serialized compilation
1074 compileRunTime();
1078 // ***************************************************************************
1080 float CMeshMRMSkinnedGeom::getNumTriangles (float distance)
1082 // NB: this is an approximation, but this is continious.
1083 return _LevelDetail.getNumTriangles(distance);
1086 // ***************************************************************************
1087 void CMeshMRMSkinnedGeom::computeBonesId (CSkeletonModel *skeleton)
1089 // Already computed ?
1090 if (!_BoneIdComputed)
1092 // Get a pointer on the skeleton
1093 nlassert (skeleton);
1094 if (skeleton)
1096 // **** For each bones, compute remap
1097 std::vector<uint> remap;
1098 skeleton->remapSkinBones(_BonesName, _BonesId, remap);
1101 // **** Remap the vertices, and compute Bone Spheres.
1103 // Find the Geomorph space: to process only real vertices, not geomorphed ones.
1104 uint nGeomSpace= 0;
1105 uint lod;
1106 for (lod=0; lod<_Lods.size(); lod++)
1108 nGeomSpace= max(nGeomSpace, (uint)_Lods[lod].Geomorphs.size());
1111 // Prepare Sphere compute
1112 static std::vector<CAABBox> boneBBoxes;
1113 static std::vector<bool> boneBBEmpty;
1114 boneBBoxes.clear();
1115 boneBBEmpty.clear();
1116 boneBBoxes.resize(_BonesId.size());
1117 boneBBEmpty.resize(_BonesId.size(), true);
1120 // Remap the vertex, and compute the bone spheres. see CTransform::getSkinBoneSphere() doc.
1121 // for true vertices
1122 uint vert;
1123 const uint vertexCount = _VBufferFinal.getNumVertices();
1124 CPackedVertexBuffer::CPackedVertex *vertices = _VBufferFinal.getPackedVertices();
1125 for (vert=nGeomSpace; vert<vertexCount; vert++)
1127 // get the vertex position.
1128 CVector vertex;
1129 _VBufferFinal.getPos (vertex, vertices[vert]);
1131 // For each weight
1132 uint weight;
1133 for (weight=0; weight<NL3D_MESH_SKINNING_MAX_MATRIX; weight++)
1135 // Active ?
1136 if ((vertices[vert].Weights[weight]>0)||(weight==0))
1138 // Check id
1139 uint srcId= vertices[vert].Matrices[weight];
1140 nlassert (srcId < remap.size());
1141 // remap
1142 vertices[vert].Matrices[weight] = remap[srcId];
1144 // if the boneId is valid (ie found)
1145 if(_BonesId[srcId]>=0)
1147 // transform the vertex pos in BoneSpace
1148 CVector p= skeleton->Bones[_BonesId[srcId]].getBoneBase().InvBindPos * vertex;
1149 // extend the bone bbox.
1150 if(boneBBEmpty[srcId])
1152 boneBBoxes[srcId].setCenter(p);
1153 boneBBEmpty[srcId]= false;
1155 else
1157 boneBBoxes[srcId].extend(p);
1161 else
1162 break;
1166 // Compile spheres
1167 _BonesSphere.resize(_BonesId.size());
1168 for(uint bone=0;bone<_BonesSphere.size();bone++)
1170 // If the bone is empty, mark with -1 in the radius.
1171 if(boneBBEmpty[bone])
1173 _BonesSphere[bone].Radius= -1;
1175 else
1177 _BonesSphere[bone].Center= boneBBoxes[bone].getCenter();
1178 _BonesSphere[bone].Radius= boneBBoxes[bone].getRadius();
1182 // **** Remap the vertex influence by lods
1183 for (lod=0; lod<_Lods.size(); lod++)
1185 // For each matrix used
1186 uint matrix;
1187 for (matrix=0; matrix<_Lods[lod].MatrixInfluences.size(); matrix++)
1189 // Check
1190 nlassert (_Lods[lod].MatrixInfluences[matrix]<remap.size());
1192 // Remap
1193 _Lods[lod].MatrixInfluences[matrix] = remap[_Lods[lod].MatrixInfluences[matrix]];
1197 // **** Remap Shadow Vertices.
1198 for(vert=0;vert<_ShadowSkin.Vertices.size();vert++)
1200 CShadowVertex &v= _ShadowSkin.Vertices[vert];
1201 // Check id
1202 nlassert (v.MatrixId < remap.size());
1203 v.MatrixId= remap[v.MatrixId];
1206 // Computed
1207 _BoneIdComputed = true;
1211 // Already extended ?
1212 if (!_BoneIdExtended)
1214 nlassert (skeleton);
1215 if (skeleton)
1217 // the total bone Usage of the mesh.
1218 vector<bool> boneUsage;
1219 boneUsage.resize(skeleton->Bones.size(), false);
1221 // for all Bones marked as valid.
1222 uint i;
1223 for(i=0; i<_BonesId.size(); i++)
1225 // if not a valid boneId, skip it.
1226 if(_BonesId[i]<0)
1227 continue;
1229 // mark him and his father in boneUsage.
1230 skeleton->flagBoneAndParents(_BonesId[i], boneUsage);
1233 // fill _BonesIdExt with bones of _BonesId and their parents.
1234 _BonesIdExt.clear();
1235 for(i=0; i<boneUsage.size();i++)
1237 // if the bone is used by the mesh, add it to BoneIdExt.
1238 if(boneUsage[i])
1239 _BonesIdExt.push_back(i);
1244 // Extended
1245 _BoneIdExtended= true;
1251 // ***************************************************************************
1252 void CMeshMRMSkinnedGeom::updateSkeletonUsage(CSkeletonModel *sm, bool increment)
1254 // For all Bones used.
1255 for(uint i=0; i<_BonesIdExt.size();i++)
1257 uint boneId= _BonesIdExt[i];
1258 // Some explicit Error.
1259 if(boneId>=sm->Bones.size())
1260 nlerror(" Skin is incompatible with Skeleton: tries to use bone %d", boneId);
1261 // increment or decrement not Forced, because CMeshGeom use getActiveBoneSkinMatrix().
1262 if(increment)
1263 sm->incBoneUsage(boneId, CSkeletonModel::UsageNormal);
1264 else
1265 sm->decBoneUsage(boneId, CSkeletonModel::UsageNormal);
1270 // ***************************************************************************
1271 void CMeshMRMSkinnedGeom::compileRunTime()
1273 _PreciseClipping= _BBox.getRadius() >= NL3D_MESH_PRECISE_CLIP_THRESHOLD;
1275 // The Mesh must follow those restrictions, to support group skinning
1276 nlassert (_VBufferFinal.getNumVertices() < NL3D_MESH_SKIN_MANAGER_MAXVERTICES);
1278 // Support Shadow SkinGrouping if Shadow setuped, and if not too many vertices.
1279 _SupportShadowSkinGrouping= !_ShadowSkin.Vertices.empty() &&
1280 NL3D_SHADOW_MESH_SKIN_MANAGER_VERTEXFORMAT==CVertexBuffer::PositionFlag &&
1281 _ShadowSkin.Vertices.size() <= NL3D_SHADOW_MESH_SKIN_MANAGER_MAXVERTICES;
1285 // ***************************************************************************
1286 void CMeshMRMSkinnedGeom::profileSceneRender(CRenderTrav *rdrTrav, CTransformShape *trans, float polygonCount, uint32 rdrFlags)
1288 // if no _Lods, no draw
1289 if(_Lods.empty())
1290 return;
1292 // get the result of the Load Balancing.
1293 float alphaMRM= _LevelDetail.getLevelDetailFromPolyCount(polygonCount);
1295 // choose the lod.
1296 float alphaLod;
1297 sint numLod= chooseLod(alphaMRM, alphaLod);
1299 // Render the choosen Lod.
1300 CLod &lod= _Lods[numLod];
1302 // get the mesh instance.
1303 CMeshBaseInstance *mi= safe_cast<CMeshBaseInstance*>(trans);
1305 // Profile all pass.
1306 uint triCount= 0;
1307 for (uint i=0;i<lod.RdrPass.size();i++)
1309 CRdrPass &rdrPass= lod.RdrPass[i];
1310 // Profile with the Materials of the MeshInstance.
1311 if ( ( (mi->Materials[rdrPass.MaterialId].getBlend() == false) && (rdrFlags & IMeshGeom::RenderOpaqueMaterial) ) ||
1312 ( (mi->Materials[rdrPass.MaterialId].getBlend() == true) && (rdrFlags & IMeshGeom::RenderTransparentMaterial) ) )
1314 triCount+= rdrPass.getNumTriangle();
1318 // Profile
1319 if(triCount)
1321 // tri per VBFormat
1322 rdrTrav->Scene->incrementProfileTriVBFormat(rdrTrav->Scene->BenchRes.MeshMRMProfileTriVBFormat,
1323 NL3D_MESH_SKIN_MANAGER_VERTEXFORMAT, triCount);
1325 rdrTrav->Scene->BenchRes.NumMeshMRMVBufferStd++;
1326 rdrTrav->Scene->BenchRes.NumMeshMRMRdrNormal++;
1327 rdrTrav->Scene->BenchRes.NumMeshMRMTriRdrNormal+= triCount;
1332 // ***************************************************************************
1333 bool CMeshMRMSkinnedGeom::getSkinBoneBBox(CSkeletonModel *skeleton, NLMISC::CAABBox &bbox, uint boneId) const
1335 bbox.setCenter(CVector::Null);
1336 bbox.setHalfSize(CVector::Null);
1338 if(!skeleton)
1339 return false;
1341 // get the bindpos of the wanted bone
1342 nlassert(boneId<skeleton->Bones.size());
1343 const CMatrix &invBindPos= skeleton->Bones[boneId].getBoneBase().InvBindPos;
1346 // Find the Geomorph space: to process only real vertices, not geomorphed ones.
1347 uint nGeomSpace= 0;
1348 uint lod;
1349 for (lod=0; lod<_Lods.size(); lod++)
1351 nGeomSpace= max(nGeomSpace, (uint)_Lods[lod].Geomorphs.size());
1354 // Prepare BBox compute
1355 bool bbEmpty= true;
1357 // Remap the vertex, and compute the wanted bone bbox
1358 // for true vertices
1359 const uint vertexCount = _VBufferFinal.getNumVertices();
1360 const CPackedVertexBuffer::CPackedVertex *vertices = _VBufferFinal.getPackedVertices();
1361 for (uint vert=nGeomSpace; vert<vertexCount; vert++)
1363 // get the vertex position.
1364 CVector vertex;
1365 _VBufferFinal.getPos (vertex, vertices[vert]);
1367 // For each weight
1368 uint weight;
1369 for (weight=0; weight<NL3D_MESH_SKINNING_MAX_MATRIX; weight++)
1371 // Active ?
1372 if ((vertices[vert].Weights[weight]>0)||(weight==0))
1374 // Check id is the wanted one
1375 if(vertices[vert].Matrices[weight]==boneId)
1377 // transform the vertex pos in BoneSpace
1378 CVector p= invBindPos * vertex;
1379 // extend the bone bbox.
1380 if(bbEmpty)
1382 bbox.setCenter(p);
1383 bbEmpty= false;
1385 else
1387 bbox.extend(p);
1391 else
1392 break;
1396 // return true if some influence found
1397 return !bbEmpty;
1402 // ***************************************************************************
1403 // ***************************************************************************
1404 // Mesh Block Render Interface
1405 // ***************************************************************************
1406 // ***************************************************************************
1409 // ***************************************************************************
1410 bool CMeshMRMSkinnedGeom::supportMeshBlockRendering () const
1413 Yoyo: Don't Support It for MRM because too Slow!!
1414 The problem is that lock() unlock() on each instance, on the same VBHeap IS AS SLOWER AS
1415 VB switching.
1417 TODO_OPTIMIZE: find a way to optimize MRM.
1419 return false;
1422 // ***************************************************************************
1423 // ***************************************************************************
1424 // CMeshMRMSkinned.
1425 // ***************************************************************************
1426 // ***************************************************************************
1430 // ***************************************************************************
1431 CMeshMRMSkinned::CMeshMRMSkinned()
1435 // ***************************************************************************
1437 bool CMeshMRMSkinned::isCompatible(const CMesh::CMeshBuild &m)
1439 /* Optimised shape for skinned object with MRM, 1 UV coordinates, 1 to 4 skinning weight and 256 matrices
1440 Tangeant space, vertex program, mesh block rendering and vertex buffer hard are not available. */
1442 // Vertex format
1443 if (m.VertexFlags != NL3D_MESH_MRM_SKINNED_VERTEX_FORMAT)
1444 return false;
1446 // No W
1447 if (m.NumCoords[0] != 2)
1448 return false;
1450 // No blend shape
1451 if (!m.BlendShapes.empty())
1452 return false;
1454 // Check number of vertices
1455 if (m.Vertices.size() > NL3D_MESH_SKIN_MANAGER_MAXVERTICES)
1456 return false;
1458 // Ok
1459 return true;
1462 // ***************************************************************************
1463 void CMeshMRMSkinned::build (CMeshBase::CMeshBaseBuild &mBase, CMesh::CMeshBuild &m,
1464 const CMRMParameters &params)
1466 nlassert (isCompatible(m));
1468 /// copy MeshBase info: materials ....
1469 CMeshBase::buildMeshBase (mBase);
1471 // Then build the geom.
1472 _MeshMRMGeom.build (m, (uint)mBase.Materials.size(), params);
1474 // ***************************************************************************
1475 void CMeshMRMSkinned::build (CMeshBase::CMeshBaseBuild &m, const CMeshMRMSkinnedGeom &mgeom)
1477 /// copy MeshBase info: materials ....
1478 CMeshBase::buildMeshBase(m);
1480 // Then copy the geom.
1481 _MeshMRMGeom= mgeom;
1485 // ***************************************************************************
1486 void CMeshMRMSkinned::optimizeMaterialUsage(std::vector<sint> &remap)
1488 // For each material, count usage.
1489 vector<bool> materialUsed;
1490 materialUsed.resize(CMeshBase::_Materials.size(), false);
1491 for(uint lod=0;lod<getNbLod();lod++)
1493 for(uint rp=0;rp<getNbRdrPass(lod);rp++)
1495 uint matId= getRdrPassMaterial(lod, rp);
1496 // flag as used.
1497 materialUsed[matId]= true;
1501 // Apply it to meshBase
1502 CMeshBase::applyMaterialUsageOptim(materialUsed, remap);
1504 // Apply lut to meshGeom.
1505 _MeshMRMGeom.applyMaterialRemap(remap);
1509 // ***************************************************************************
1510 CTransformShape *CMeshMRMSkinned::createInstance(CScene &scene)
1512 // Create a CMeshMRMSkinnedInstance, an instance of a mesh.
1513 //===============================================
1514 CMeshMRMSkinnedInstance *mi= (CMeshMRMSkinnedInstance*)scene.createModel(NL3D::MeshMRMSkinnedInstanceId);
1515 mi->Shape= this;
1517 // instanciate the material part of the MeshMRM, ie the CMeshBase.
1518 CMeshBase::instanciateMeshBase(mi, &scene);
1521 // do some instance init for MeshGeom
1522 _MeshMRMGeom.initInstance(mi);
1524 // init the FilterType
1525 mi->initRenderFilterType();
1527 return mi;
1531 // ***************************************************************************
1532 bool CMeshMRMSkinned::clip(const std::vector<CPlane> &pyramid, const CMatrix &worldMatrix)
1534 return _MeshMRMGeom.clip(pyramid, worldMatrix);
1538 // ***************************************************************************
1539 void CMeshMRMSkinned::render(IDriver *drv, CTransformShape *trans, bool passOpaque)
1541 // 0 or 0xFFFFFFFF
1542 uint32 mask= (0-(uint32)passOpaque);
1543 uint32 rdrFlags;
1544 // select rdrFlags, without ifs.
1545 rdrFlags= mask & (IMeshGeom::RenderOpaqueMaterial | IMeshGeom::RenderPassOpaque);
1546 rdrFlags|= ~mask & (IMeshGeom::RenderTransparentMaterial);
1547 // render the mesh
1548 _MeshMRMGeom.render(drv, trans, trans->getNumTrianglesAfterLoadBalancing(), rdrFlags, 1);
1552 // ***************************************************************************
1553 void CMeshMRMSkinned::serial(NLMISC::IStream &f)
1556 Version 0:
1557 - base version.
1559 (void)f.serialVersion(0);
1561 // serial Materials infos contained in CMeshBase.
1562 CMeshBase::serialMeshBase(f);
1565 // serial the geometry.
1566 _MeshMRMGeom.serial(f);
1570 // ***************************************************************************
1571 float CMeshMRMSkinned::getNumTriangles (float distance)
1573 return _MeshMRMGeom.getNumTriangles (distance);
1577 // ***************************************************************************
1578 const CMeshMRMSkinnedGeom& CMeshMRMSkinned::getMeshGeom () const
1580 return _MeshMRMGeom;
1584 // ***************************************************************************
1585 void CMeshMRMSkinned::computeBonesId (CSkeletonModel *skeleton)
1587 _MeshMRMGeom.computeBonesId (skeleton);
1590 // ***************************************************************************
1591 void CMeshMRMSkinned::updateSkeletonUsage (CSkeletonModel *skeleton, bool increment)
1593 _MeshMRMGeom.updateSkeletonUsage(skeleton, increment);
1596 // ***************************************************************************
1597 void CMeshMRMSkinned::changeMRMDistanceSetup(float distanceFinest, float distanceMiddle, float distanceCoarsest)
1599 _MeshMRMGeom.changeMRMDistanceSetup(distanceFinest, distanceMiddle, distanceCoarsest);
1602 // ***************************************************************************
1603 IMeshGeom *CMeshMRMSkinned::supportMeshBlockRendering (CTransformShape *trans, float &polygonCount ) const
1605 return NULL;
1608 // ***************************************************************************
1609 void CMeshMRMSkinned::profileSceneRender(CRenderTrav *rdrTrav, CTransformShape *trans, bool passOpaque)
1611 // 0 or 0xFFFFFFFF
1612 uint32 mask= (0-(uint32)passOpaque);
1613 uint32 rdrFlags;
1614 // select rdrFlags, without ifs.
1615 rdrFlags= mask & (IMeshGeom::RenderOpaqueMaterial | IMeshGeom::RenderPassOpaque);
1616 rdrFlags|= ~mask & (IMeshGeom::RenderTransparentMaterial);
1617 // profile the mesh
1618 _MeshMRMGeom.profileSceneRender(rdrTrav, trans, trans->getNumTrianglesAfterLoadBalancing(), rdrFlags);
1623 // ***************************************************************************
1624 // ***************************************************************************
1625 // CMeshMRMSkinnedGeom RawSkin optimisation
1626 // ***************************************************************************
1627 // ***************************************************************************
1630 // ***************************************************************************
1631 void CMeshMRMSkinnedGeom::dirtMeshDataId()
1633 // see updateRawSkinNormal()
1634 _MeshDataId++;
1638 // ***************************************************************************
1639 void CMeshMRMSkinnedGeom::updateRawSkinNormal(bool enabled, CMeshMRMSkinnedInstance *mi, sint curLodId)
1641 if(!enabled)
1643 // if the instance cache is not cleared, must clear.
1644 mi->clearRawSkinCache();
1646 else
1648 // If the instance has no RawSkin, or has a too old RawSkin cache, must delete it, and recreate
1649 if ((mi->_RawSkinCache == NULL) || (mi->_RawSkinCache->MeshDataId!=_MeshDataId))
1651 // first delete if too old.
1652 mi->clearRawSkinCache();
1654 // Then recreate, and use _MeshDataId to verify that the instance works with same data.
1655 mi->_RawSkinCache= new CRawSkinnedNormalCache;
1656 mi->_RawSkinCache->MeshDataId= _MeshDataId;
1657 mi->_RawSkinCache->LodId= -1;
1661 /* If the instance rawSkin has a different Lod (or if -1), then must recreate it.
1662 NB: The lod may change each frame per instance, but suppose not so many change, so we can cache those data.
1664 if( mi->_RawSkinCache->LodId != curLodId )
1666 H_AUTO( NL3D_CMeshMRMGeom_updateRawSkinNormal );
1668 CRawSkinnedNormalCache &skinLod= *mi->_RawSkinCache;
1669 CLod &lod= _Lods[curLodId];
1670 uint i;
1671 sint rawIdx;
1673 // Clear the raw skin mesh.
1674 skinLod.clearArrays();
1676 // Cache this lod
1677 mi->_RawSkinCache->LodId= curLodId;
1679 // For each matrix influence.
1680 nlassert(NL3D_MESH_SKINNING_MAX_MATRIX==4);
1682 // For each vertex, acknowledge if it is a src for geomorph.
1683 static vector<uint8> softVertices;
1684 softVertices.clear();
1685 softVertices.resize( _VBufferFinal.getNumVertices(), 0 );
1686 for(i=0;i<lod.Geomorphs.size();i++)
1688 softVertices[lod.Geomorphs[i].Start]= 1;
1689 softVertices[lod.Geomorphs[i].End]= 1;
1692 // The remap from old index in _VBufferFinal to RawSkin vertices (without Geomorphs).
1693 static vector<uint32> vertexRemap;
1694 vertexRemap.resize( _VBufferFinal.getNumVertices() );
1695 sint softSize[4];
1696 sint hardSize[4];
1697 sint softStart[4];
1698 sint hardStart[4];
1699 // count vertices
1700 skinLod.TotalSoftVertices= 0;
1701 skinLod.TotalHardVertices= 0;
1702 for(i=0;i<4;i++)
1704 softSize[i]= 0;
1705 hardSize[i]= 0;
1706 // Count.
1707 for(uint j=0;j<lod.InfluencedVertices[i].size();j++)
1709 uint vid= lod.InfluencedVertices[i][j];
1710 if(softVertices[vid])
1711 softSize[i]++;
1712 else
1713 hardSize[i]++;
1715 skinLod.TotalSoftVertices+= softSize[i];
1716 skinLod.TotalHardVertices+= hardSize[i];
1717 skinLod.SoftVertices[i]= softSize[i];
1718 skinLod.HardVertices[i]= hardSize[i];
1720 // compute offsets
1721 softStart[0]= 0;
1722 hardStart[0]= skinLod.TotalSoftVertices;
1723 for(i=1;i<4;i++)
1725 softStart[i]= softStart[i-1]+softSize[i-1];
1726 hardStart[i]= hardStart[i-1]+hardSize[i-1];
1728 // compute remap
1729 for(i=0;i<4;i++)
1731 uint softIdx= softStart[i];
1732 uint hardIdx= hardStart[i];
1733 for(uint j=0;j<lod.InfluencedVertices[i].size();j++)
1735 uint vid= lod.InfluencedVertices[i][j];
1736 if(softVertices[vid])
1737 vertexRemap[vid]= softIdx++;
1738 else
1739 vertexRemap[vid]= hardIdx++;
1744 // Resize the dest array.
1745 skinLod.Vertices1.resize((uint32)lod.InfluencedVertices[0].size());
1746 skinLod.Vertices2.resize((uint32)lod.InfluencedVertices[1].size());
1747 skinLod.Vertices3.resize((uint32)lod.InfluencedVertices[2].size());
1748 skinLod.Vertices4.resize((uint32)lod.InfluencedVertices[3].size());
1750 // Vertex buffer pointers
1751 const CPackedVertexBuffer::CPackedVertex *vertices = _VBufferFinal.getPackedVertices();
1753 // 1 Matrix skinning.
1754 //========
1755 for(i=0;i<skinLod.Vertices1.size();i++)
1757 // get the dest vertex.
1758 uint vid= lod.InfluencedVertices[0][i];
1759 // where to store?
1760 rawIdx= vertexRemap[vid];
1761 if(softVertices[vid])
1762 rawIdx-= softStart[0];
1763 else
1764 rawIdx+= softSize[0]-hardStart[0];
1766 // fill raw struct
1767 const CPackedVertexBuffer::CPackedVertex &vertex = vertices[vid];
1768 skinLod.Vertices1[rawIdx].MatrixId[0]= vertex.Matrices[0];
1769 _VBufferFinal.getPos (skinLod.Vertices1[rawIdx].Vertex, vertex);
1770 vertex.getNormal (skinLod.Vertices1[rawIdx].Normal);
1771 vertex.getU (skinLod.Vertices1[rawIdx].UV.U);
1772 vertex.getV (skinLod.Vertices1[rawIdx].UV.V);
1775 // 2 Matrix skinning.
1776 //========
1777 for(i=0;i<skinLod.Vertices2.size();i++)
1779 // get the dest vertex.
1780 uint vid= lod.InfluencedVertices[1][i];
1781 // where to store?
1782 rawIdx= vertexRemap[vid];
1783 if(softVertices[vid])
1784 rawIdx-= softStart[1];
1785 else
1786 rawIdx+= softSize[1]-hardStart[1];
1787 // fill raw struct
1788 const CPackedVertexBuffer::CPackedVertex &vertex = vertices[vid];
1789 skinLod.Vertices2[rawIdx].MatrixId[0]= vertex.Matrices[0];
1790 skinLod.Vertices2[rawIdx].MatrixId[1]= vertex.Matrices[1];
1791 _VBufferFinal.getPos (skinLod.Vertices2[rawIdx].Vertex, vertex);
1792 vertex.getWeight (skinLod.Vertices2[rawIdx].Weights[0], 0);
1793 vertex.getWeight (skinLod.Vertices2[rawIdx].Weights[1], 1);
1794 vertex.getNormal (skinLod.Vertices2[rawIdx].Normal);
1795 vertex.getU (skinLod.Vertices2[rawIdx].UV.U);
1796 vertex.getV (skinLod.Vertices2[rawIdx].UV.V);
1799 // 3 Matrix skinning.
1800 //========
1801 for(i=0;i<skinLod.Vertices3.size();i++)
1803 // get the dest vertex.
1804 uint vid= lod.InfluencedVertices[2][i];
1805 // where to store?
1806 rawIdx= vertexRemap[vid];
1807 if(softVertices[vid])
1808 rawIdx-= softStart[2];
1809 else
1810 rawIdx+= softSize[2]-hardStart[2];
1811 // fill raw struct
1812 const CPackedVertexBuffer::CPackedVertex &vertex = vertices[vid];
1813 skinLod.Vertices3[rawIdx].MatrixId[0]= vertex.Matrices[0];
1814 skinLod.Vertices3[rawIdx].MatrixId[1]= vertex.Matrices[1];
1815 skinLod.Vertices3[rawIdx].MatrixId[2]= vertex.Matrices[2];
1816 _VBufferFinal.getPos (skinLod.Vertices3[rawIdx].Vertex, vertex);
1817 vertex.getWeight (skinLod.Vertices3[rawIdx].Weights[0], 0);
1818 vertex.getWeight (skinLod.Vertices3[rawIdx].Weights[1], 1);
1819 vertex.getWeight (skinLod.Vertices3[rawIdx].Weights[2], 2);
1820 vertex.getNormal (skinLod.Vertices3[rawIdx].Normal);
1821 vertex.getU (skinLod.Vertices3[rawIdx].UV.U);
1822 vertex.getV (skinLod.Vertices3[rawIdx].UV.V);
1825 // 4 Matrix skinning.
1826 //========
1827 for(i=0;i<skinLod.Vertices4.size();i++)
1829 // get the dest vertex.
1830 uint vid= lod.InfluencedVertices[3][i];
1831 // where to store?
1832 rawIdx= vertexRemap[vid];
1833 if(softVertices[vid])
1834 rawIdx-= softStart[3];
1835 else
1836 rawIdx+= softSize[3]-hardStart[3];
1837 // fill raw struct
1838 const CPackedVertexBuffer::CPackedVertex &vertex = vertices[vid];
1839 skinLod.Vertices4[rawIdx].MatrixId[0]= vertex.Matrices[0];
1840 skinLod.Vertices4[rawIdx].MatrixId[1]= vertex.Matrices[1];
1841 skinLod.Vertices4[rawIdx].MatrixId[2]= vertex.Matrices[2];
1842 skinLod.Vertices4[rawIdx].MatrixId[3]= vertex.Matrices[3];
1843 _VBufferFinal.getPos (skinLod.Vertices4[rawIdx].Vertex, vertex);
1844 vertex.getWeight (skinLod.Vertices4[rawIdx].Weights[0], 0);
1845 vertex.getWeight (skinLod.Vertices4[rawIdx].Weights[1], 1);
1846 vertex.getWeight (skinLod.Vertices4[rawIdx].Weights[2], 2);
1847 vertex.getWeight (skinLod.Vertices4[rawIdx].Weights[3], 3);
1848 vertex.getNormal (skinLod.Vertices4[rawIdx].Normal);
1849 vertex.getU (skinLod.Vertices4[rawIdx].UV.U);
1850 vertex.getV (skinLod.Vertices4[rawIdx].UV.V);
1853 // Remap Geomorphs.
1854 //========
1855 uint numGeoms= (uint)lod.Geomorphs.size();
1856 skinLod.Geomorphs.resize( numGeoms );
1857 for(i=0;i<numGeoms;i++)
1859 // NB: don't add "numGeoms" to the index because RawSkin look in a TempArray in RAM, which start at 0...
1860 skinLod.Geomorphs[i].Start= vertexRemap[lod.Geomorphs[i].Start];
1861 skinLod.Geomorphs[i].End= vertexRemap[lod.Geomorphs[i].End];
1864 // Remap RdrPass.
1865 //========
1866 skinLod.RdrPass.resize(lod.RdrPass.size());
1867 for(i=0;i<skinLod.RdrPass.size();i++)
1869 // remap tris.
1870 skinLod.RdrPass[i].setFormat(NL_SKINNED_MESH_MRM_INDEX_FORMAT);
1871 skinLod.RdrPass[i].setNumIndexes(lod.RdrPass[i].getNumTriangle()*3);
1872 const uint16 *srcTriPtr= &(lod.RdrPass[i].PBlock[0]);
1873 CIndexBufferReadWrite ibaWrite;
1874 skinLod.RdrPass[i].lock (ibaWrite);
1875 #ifndef NL_SKINNED_MESH_MRM_INDEX16
1876 nlassert(ibaWrite.getFormat() == CIndexBuffer::Indices32);
1877 uint32 *dstTriPtr= (uint32 *) ibaWrite.getPtr();
1878 uint32 numIndices= lod.RdrPass[i].PBlock.size();
1879 for(uint j=0;j<numIndices;j++, srcTriPtr++, dstTriPtr++)
1881 uint vid= (uint)*srcTriPtr;
1882 // If this index refers to a Geomorphed vertex, don't modify!
1883 if(vid<numGeoms)
1884 *dstTriPtr= vid;
1885 else
1886 *dstTriPtr= vertexRemap[vid] + numGeoms;
1888 #else
1889 nlassert(ibaWrite.getFormat() == CIndexBuffer::Indices16);
1890 uint16 *dstTriPtr= (uint16 *) ibaWrite.getPtr();
1891 uint32 numIndices= (uint32)lod.RdrPass[i].PBlock.size();
1892 for(uint j=0;j<numIndices;j++, srcTriPtr++, dstTriPtr++)
1894 uint vid= (uint)*srcTriPtr;
1895 // If this index refers to a Geomorphed vertex, don't modify!
1896 if(vid<numGeoms)
1897 *dstTriPtr= vid;
1898 else
1899 *dstTriPtr= (uint16) (vertexRemap[vid] + numGeoms);
1901 #endif
1908 // ***************************************************************************
1909 // ***************************************************************************
1910 // CMeshMRMSkinnedGeom Shadow Skin Rendering
1911 // ***************************************************************************
1912 // ***************************************************************************
1915 // ***************************************************************************
1916 void CMeshMRMSkinnedGeom::setShadowMesh(const std::vector<CShadowVertex> &shadowVertices, const std::vector<uint32> &triangles)
1918 _ShadowSkin.Vertices= shadowVertices;
1919 _ShadowSkin.Triangles= triangles;
1920 // update flag. Support Shadow SkinGrouping if Shadow setuped, and if not too many vertices.
1921 _SupportShadowSkinGrouping= !_ShadowSkin.Vertices.empty() &&
1922 NL3D_SHADOW_MESH_SKIN_MANAGER_VERTEXFORMAT==CVertexBuffer::PositionFlag &&
1923 _ShadowSkin.Vertices.size() <= NL3D_SHADOW_MESH_SKIN_MANAGER_MAXVERTICES;
1926 // ***************************************************************************
1927 uint CMeshMRMSkinnedGeom::getNumShadowSkinVertices() const
1929 return (uint)_ShadowSkin.Vertices.size();
1932 // ***************************************************************************
1933 sint CMeshMRMSkinnedGeom::renderShadowSkinGeom(CMeshMRMSkinnedInstance *mi, uint remainingVertices, uint8 *vbDest)
1935 uint numVerts= (uint)_ShadowSkin.Vertices.size();
1937 // if no verts, no draw
1938 if(numVerts==0)
1939 return 0;
1941 // if no lods, there should be no verts, but still ensure no bug in skinning
1942 if(_Lods.empty())
1943 return 0;
1945 // If the Lod is too big to render in the VBufferHard
1946 if(numVerts>remainingVertices)
1947 // return Failure
1948 return -1;
1950 // get the skeleton model to which I am skinned
1951 CSkeletonModel *skeleton;
1952 skeleton = mi->getSkeletonModel();
1953 // must be skinned for renderSkin()
1954 nlassert(skeleton);
1957 // Profiling
1958 //===========
1959 H_AUTO_USE( NL3D_MeshMRMGeom_RenderShadow );
1962 // Skinning.
1963 //===========
1965 // For all matrix this Mesh use. (the shadow geometry cannot use other Matrix than the mesh use).
1966 // NB: take the best lod since the lower lods cannot use other Matrix than the higher one.
1967 static vector<CMatrix3x4> boneMat3x4;
1968 CLod &lod= _Lods[_Lods.size()-1];
1969 computeBoneMatrixes3x4(boneMat3x4, lod.MatrixInfluences, skeleton);
1971 _ShadowSkin.applySkin((CVector*)vbDest, boneMat3x4);
1974 // How many vertices are added to the VBuffer ???
1975 return numVerts;
1978 // ***************************************************************************
1979 void CMeshMRMSkinnedGeom::renderShadowSkinPrimitives(CMeshMRMSkinnedInstance *mi, CMaterial &castMat, IDriver *drv, uint baseVertex)
1981 nlassert(drv);
1983 if(_ShadowSkin.Triangles.empty())
1984 return;
1986 // Profiling
1987 //===========
1988 H_AUTO_USE( NL3D_MeshMRMGeom_RenderShadow );
1990 // NB: the skeleton matrix has already been setuped by CSkeletonModel
1991 // NB: the normalize flag has already been setuped by CSkeletonModel
1993 // TODO_SHADOW: optim: Special triangle cache for shadow!
1994 static CIndexBuffer shiftedTris;
1995 shiftedTris.setPreferredMemory(CIndexBuffer::RAMVolatile, false);
1996 if (shiftedTris.getName().empty()) NL_SET_IB_NAME(shiftedTris, "CMeshMRMSkinnedGeom::renderShadowSkinPrimitives::shiftedTris");
1997 //if(shiftedTris.getNumIndexes()<_ShadowSkin.Triangles.size())
1999 shiftedTris.setFormat(NL_SKINNED_MESH_MRM_INDEX_FORMAT);
2000 shiftedTris.setNumIndexes((uint32)_ShadowSkin.Triangles.size());
2003 CIndexBufferReadWrite iba;
2004 shiftedTris.lock(iba);
2005 const uint32 *src= &_ShadowSkin.Triangles[0];
2006 TSkinnedMeshMRMIndexType *dst= (TSkinnedMeshMRMIndexType*) iba.getPtr();
2007 for(uint n= (uint)_ShadowSkin.Triangles.size();n>0;n--, src++, dst++)
2009 *dst= (TSkinnedMeshMRMIndexType)(*src + baseVertex);
2013 // Render Triangles with cache
2014 //===========
2016 uint numTris= (uint)_ShadowSkin.Triangles.size()/3;
2018 // Render with the Materials of the MeshInstance.
2019 drv->activeIndexBuffer(shiftedTris);
2020 drv->renderTriangles(castMat, 0, numTris);
2023 // ***************************************************************************
2024 bool CMeshMRMSkinnedGeom::intersectSkin(CMeshMRMSkinnedInstance *mi, const CMatrix &toRaySpace, float &dist2D, float &distZ, bool computeDist2D)
2026 // no inst/verts/lod => no intersection
2027 if(!mi || _ShadowSkin.Vertices.empty() || _Lods.empty())
2028 return false;
2029 CSkeletonModel *skeleton= mi->getSkeletonModel();
2030 if(!skeleton)
2031 return false;
2033 // Compute skinning with all matrix this Mesh use. (the shadow geometry cannot use other Matrix than the mesh use).
2034 // NB: take the best lod (_Lods.back()) since the lower lods cannot use other Matrix than the higher one.
2035 return _ShadowSkin.getRayIntersection(toRaySpace, *skeleton, _Lods.back().MatrixInfluences, dist2D, distZ, computeDist2D);
2039 // ***************************************************************************
2040 // ***************************************************************************
2041 // CMeshMRMSkinnedGeom::CPackedVertexBuffer
2042 // ***************************************************************************
2043 // ***************************************************************************
2045 void CMeshMRMSkinnedGeom::CPackedVertexBuffer::serial(NLMISC::IStream &f)
2047 // Version
2048 f.serialVersion(0);
2050 f.serialCont (_PackedBuffer);
2051 f.serial (_DecompactScale);
2054 // ***************************************************************************
2056 void CMeshMRMSkinnedGeom::CPackedVertexBuffer::CPackedVertex::serial(NLMISC::IStream &f)
2058 // Version
2059 f.serialVersion(0);
2061 f.serial (X);
2062 f.serial (Y);
2063 f.serial (Z);
2064 f.serial (Nx);
2065 f.serial (Ny);
2066 f.serial (Nz);
2067 f.serial (U);
2068 f.serial (V);
2069 uint i;
2070 for (i=0; i<NL3D_MESH_MRM_SKINNED_MAX_MATRIX; i++)
2072 f.serial (Matrices[i]);
2073 f.serial (Weights[i]);
2077 // ***************************************************************************
2079 void CMeshMRMSkinnedGeom::CPackedVertexBuffer::CPackedVertex::setNormal (const CVector &src)
2081 CVector pos = src;
2082 pos *= NL3D_MESH_MRM_SKINNED_NORMAL_FACTOR;
2083 pos.x = (float)floor(pos.x+0.5f);
2084 pos.y = (float)floor(pos.y+0.5f);
2085 pos.z = (float)floor(pos.z+0.5f);
2086 clamp (pos.x, -32768.f, 32767.f);
2087 clamp (pos.y, -32768.f, 32767.f);
2088 clamp (pos.z, -32768.f, 32767.f);
2089 Nx = (sint16)pos.x;
2090 Ny = (sint16)pos.y;
2091 Nz = (sint16)pos.z;
2094 // ***************************************************************************
2096 void CMeshMRMSkinnedGeom::CPackedVertexBuffer::CPackedVertex::setPos (const NLMISC::CVector &pos, float scaleFactor)
2098 CVector _pos = pos;
2099 _pos /= scaleFactor;
2100 _pos.x = (float)floor(_pos.x+0.5f);
2101 _pos.y = (float)floor(_pos.y+0.5f);
2102 _pos.z = (float)floor(_pos.z+0.5f);
2103 clamp (_pos.x, -32768.f, 32767.f);
2104 clamp (_pos.y, -32768.f, 32767.f);
2105 clamp (_pos.z, -32768.f, 32767.f);
2106 X = (sint16)_pos.x;
2107 Y = (sint16)_pos.y;
2108 Z = (sint16)_pos.z;
2111 // ***************************************************************************
2113 void CMeshMRMSkinnedGeom::CPackedVertexBuffer::CPackedVertex::setUV (float _u, float _v)
2115 float u = _u * NL3D_MESH_MRM_SKINNED_UV_FACTOR;
2116 float v = _v * NL3D_MESH_MRM_SKINNED_UV_FACTOR;
2117 u = (float)floor(u+0.5f);
2118 v = (float)floor(v+0.5f);
2119 clamp (u, -32768.f, 32767.f);
2120 clamp (v, -32768.f, 32767.f);
2121 U = (sint16)u;
2122 V = (sint16)v;
2125 // ***************************************************************************
2127 void CMeshMRMSkinnedGeom::CPackedVertexBuffer::CPackedVertex::setWeight (uint weightId, float weight)
2129 weight = weight * NL3D_MESH_MRM_SKINNED_WEIGHT_FACTOR;
2130 weight = (float)floor(weight+0.5f);
2131 clamp (weight, 0.f, 255.f);
2132 Weights[weightId] = (uint8)weight;
2135 // ***************************************************************************
2137 void CMeshMRMSkinnedGeom::CPackedVertexBuffer::build (const CVertexBuffer &buffer, const std::vector<CMesh::CSkinWeight> &skinWeight)
2139 const uint numVertices = buffer.getNumVertices();
2140 nlassert (numVertices == skinWeight.size());
2141 nlassert ((buffer.getVertexFormat() & (CVertexBuffer::PositionFlag|CVertexBuffer::NormalFlag|CVertexBuffer::TexCoord0Flag)) == (CVertexBuffer::PositionFlag|CVertexBuffer::NormalFlag|CVertexBuffer::TexCoord0Flag));
2143 _PackedBuffer.resize (numVertices);
2145 // default scale
2146 _DecompactScale = NL3D_MESH_MRM_SKINNED_DEFAULT_POS_SCALE;
2148 CVertexBufferRead vba;
2149 buffer.lock (vba);
2151 if (numVertices)
2153 // Get the min max of the bbox
2154 CVector _min = *vba.getVertexCoordPointer(0);
2155 CVector _max = _min;
2157 // For each vertex
2158 uint i;
2159 for (i=1; i<numVertices; i++)
2161 // Update min max
2162 const CVector &vect = *vba.getVertexCoordPointer(i);
2163 _min.minof(_min, vect);
2164 _max.maxof(_max, vect);
2167 // Scale enough ?
2168 float max_width = std::max((float)fabs(_min.x), std::max((float)fabs(_min.y), (float)fabs(_min.z)));
2169 max_width = std::max(max_width, std::max((float)fabs(_max.x), std::max((float)fabs(_max.y), (float)fabs(_max.z))));
2170 _DecompactScale = std::max(_DecompactScale, max_width / 32767.f);
2172 // Pack
2173 for (i=0; i<numVertices; i++)
2175 CPackedVertex &vertex = _PackedBuffer[i];
2177 // Position
2178 vertex.setPos (*vba.getVertexCoordPointer(i), _DecompactScale);
2180 // Normal
2181 vertex.setNormal (*vba.getNormalCoordPointer(i));
2183 // UV
2184 const float *uv = (const float*)vba.getTexCoordPointer(i, 0);
2185 vertex.setUV (uv[0], uv[1]);
2187 // Matrices
2188 uint j;
2189 sint weightSum = 0;
2190 for (j=0; j<NL3D_MESH_MRM_SKINNED_MAX_MATRIX; j++)
2192 vertex.Matrices[j] = (uint8)skinWeight[i].MatrixId[j];
2193 vertex.setWeight (j, skinWeight[i].Weights[j]);
2194 weightSum += (sint)vertex.Weights[j];
2197 // Weight error
2198 weightSum = 255 - weightSum;
2200 // Add error to the first weight
2201 nlassert ((sint)vertex.Weights[0] + weightSum <= 255);
2202 nlassert ((sint)vertex.Weights[0] + weightSum > 0);
2203 vertex.Weights[0] += weightSum;
2208 // ***************************************************************************
2210 void CMeshMRMSkinnedGeom::getVertexBuffer(CVertexBuffer &output) const
2212 output.setVertexFormat (CVertexBuffer::PositionFlag|CVertexBuffer::NormalFlag|CVertexBuffer::TexCoord0Flag);
2213 const uint numVertices = _VBufferFinal.getNumVertices();
2214 output.setNumVertices (numVertices);
2216 // Set all UV routing on 0
2217 uint i;
2218 for (i=0; i<CVertexBuffer::MaxStage; i++)
2219 output.setUVRouting (i, 0);
2221 CVertexBufferReadWrite vba;
2222 output.lock (vba);
2223 const CPackedVertexBuffer::CPackedVertex *vertex = _VBufferFinal.getPackedVertices();
2224 for (i=0; i<numVertices; i++)
2226 _VBufferFinal.getPos(*vba.getVertexCoordPointer(i), vertex[i]);
2227 vertex[i].getNormal(*vba.getNormalCoordPointer(i));
2228 float *texCoord = (float*)vba.getTexCoordPointer(i,0);
2229 vertex[i].getU(texCoord[0]);
2230 vertex[i].getV(texCoord[1]);
2234 // ***************************************************************************
2236 void CMeshMRMSkinnedGeom::getSkinWeights (std::vector<CMesh::CSkinWeight> &skinWeights) const
2238 const uint vertexCount = _VBufferFinal.getNumVertices();
2239 skinWeights.resize (vertexCount);
2240 const CPackedVertexBuffer::CPackedVertex *vertices = _VBufferFinal.getPackedVertices();
2241 uint i;
2242 for (i=0; i<vertexCount; i++)
2244 const CPackedVertexBuffer::CPackedVertex &vertex = vertices[i];
2246 uint j;
2247 // Matrices
2248 for (j=0; j<NL3D_MESH_MRM_SKINNED_MAX_MATRIX; j++)
2250 skinWeights[i].MatrixId[j] = vertex.Matrices[j];
2251 vertex.getWeight (skinWeights[i].Weights[j], j);
2257 // ***************************************************************************
2258 // ***************************************************************************
2259 // Old school Template skinning: SSE or not.
2260 // ***************************************************************************
2261 // ***************************************************************************
2264 // RawSkin Cache constants
2265 //===============
2266 // The number of byte to process per block
2267 const uint NL_BlockByteL1= 4096;
2269 // Number of vertices per block to process with 1 matrix.
2270 uint CMeshMRMSkinnedGeom::NumCacheVertexNormal1= NL_BlockByteL1 / sizeof(CRawVertexNormalSkinned1);
2271 // Number of vertices per block to process with 2 matrix.
2272 uint CMeshMRMSkinnedGeom::NumCacheVertexNormal2= NL_BlockByteL1 / sizeof(CRawVertexNormalSkinned2);
2273 // Number of vertices per block to process with 3 matrix.
2274 uint CMeshMRMSkinnedGeom::NumCacheVertexNormal3= NL_BlockByteL1 / sizeof(CRawVertexNormalSkinned3);
2275 // Number of vertices per block to process with 4 matrix.
2276 uint CMeshMRMSkinnedGeom::NumCacheVertexNormal4= NL_BlockByteL1 / sizeof(CRawVertexNormalSkinned4);
2279 /* Old School template: include the same file with define switching,
2280 Was used before to reuse same code for and without SSE.
2281 Useless now because SSE removed, but keep it for possible future work on it.
2283 #define ADD_MESH_MRM_SKINNED_TEMPLATE
2284 #include "mesh_mrm_skinned_template.cpp"
2287 } // NL3D