1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "nel/3d/mesh.h"
20 #include "nel/3d/mesh_instance.h"
21 #include "nel/3d/scene.h"
22 #include "nel/3d/skeleton_model.h"
23 #include "nel/3d/mesh_morpher.h"
24 #include "nel/misc/bsphere.h"
25 #include "nel/3d/stripifier.h"
26 #include "nel/misc/fast_floor.h"
27 #include "nel/misc/hierarchical_timer.h"
28 #include "nel/3d/mesh_blender.h"
29 #include "nel/3d/matrix_3x4.h"
30 #include "nel/3d/render_trav.h"
31 #include "nel/3d/visual_collision_mesh.h"
32 #include "nel/3d/meshvp_wind_tree.h"
33 #include "nel/3d/vertex_stream_manager.h"
36 using namespace NLMISC
;
48 // ***************************************************************************
49 // ***************************************************************************
51 // ***************************************************************************
52 // ***************************************************************************
55 // ***************************************************************************
56 static NLMISC::CAABBoxExt
makeBBox(const std::vector
<CVector
> &Vertices
)
59 nlassert(!Vertices
.empty());
60 ret
.setCenter(Vertices
[0]);
61 for(sint i
=0;i
<(sint
)Vertices
.size();i
++)
63 ret
.extend(Vertices
[i
]);
70 // ***************************************************************************
71 sint
CMeshGeom::CCornerTmp::Flags
=0;
74 // ***************************************************************************
75 bool CMeshGeom::CCornerTmp::operator<(const CCornerTmp
&c
) const
81 return Vertex
<c
.Vertex
;
83 // Order: normal, uvs, color0, color1, skinning.
84 if((CCornerTmp::Flags
& CVertexBuffer::NormalFlag
) && Normal
!=c
.Normal
)
85 return Normal
<c
.Normal
;
86 for(i
=0; i
<CVertexBuffer::MaxStage
; i
++)
88 if((CCornerTmp::Flags
& (CVertexBuffer::TexCoord0Flag
<<i
)) && Uvws
[i
]!=c
.Uvws
[i
])
89 return Uvws
[i
]<c
.Uvws
[i
];
91 if((CCornerTmp::Flags
& CVertexBuffer::PrimaryColorFlag
) && Color
!=c
.Color
)
93 if((CCornerTmp::Flags
& CVertexBuffer::SecondaryColorFlag
) && Specular
!=c
.Specular
)
94 return Specular
<c
.Specular
;
96 if ((CCornerTmp::Flags
& CVertexBuffer::PaletteSkinFlag
)==CVertexBuffer::PaletteSkinFlag
)
98 for(i
=0;i
<NL3D_MESH_SKINNING_MAX_MATRIX
;i
++)
100 if(Palette
.MatrixId
[i
] != c
.Palette
.MatrixId
[i
])
101 return Palette
.MatrixId
[i
] < c
.Palette
.MatrixId
[i
];
102 if(Weights
[i
] != c
.Weights
[i
])
103 return Weights
[i
] < c
.Weights
[i
];
113 // ***************************************************************************
114 // ***************************************************************************
116 // ***************************************************************************
117 // ***************************************************************************
120 // ***************************************************************************
121 CMeshGeom::CMeshGeom()
123 /* ***********************************************
124 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
125 * It can be loaded/called through CAsyncFileManager for instance
126 * ***********************************************/
129 _OriginalSkinRestored
= true;
130 _MeshMorpher
= new CMeshMorpher
;
131 _BoneIdComputed
= false;
132 _BoneIdExtended
= false;
133 _PreciseClipping
= false;
137 // ***************************************************************************
138 CMeshGeom::~CMeshGeom()
140 /* ***********************************************
141 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
142 * It can be loaded/called through CAsyncFileManager for instance
143 * ***********************************************/
149 // ***************************************************************************
150 void CMeshGeom::optimizeTriangleOrder()
152 CStripifier stripifier
;
154 // for all rdrpass of all matrix blocks.
155 for(uint mb
= 0;mb
<_MatrixBlocks
.size();mb
++)
157 for(uint rp
=0; rp
<_MatrixBlocks
[mb
].RdrPass
.size(); rp
++ )
159 // stripify list of triangles of this pass.
160 CRdrPass
&pass
= _MatrixBlocks
[mb
].RdrPass
[rp
];
161 stripifier
.optimizeTriangles(pass
.PBlock
, pass
.PBlock
);
168 // ***************************************************************************
169 void CMeshGeom::build (CMesh::CMeshBuild
&m
, uint numMaxMaterial
)
174 if(m
.Vertices
.empty() || m
.Faces
.empty())
176 _VBuffer
.setNumVertices(0);
177 _VBuffer
.setName("CMeshGeom");
179 _MatrixBlocks
.clear();
180 _BBox
.setCenter(CVector::Null
);
181 _BBox
.setSize(CVector::Null
);
184 nlassert(numMaxMaterial
>0);
186 // Copy the UV routing table
187 for (i
=0; i
<CVertexBuffer::MaxStage
; i
++)
188 _VBuffer
.setUVRouting (i
, m
.UVRouting
[i
]);
190 /// 0. First, make bbox.
191 //======================
192 _BBox
= makeBBox(m
.Vertices
);
195 /// 1. If skinning, group by matrix Block the vertices.
196 //================================================
198 // First, copy Face array.
199 vector
<CFaceTmp
> tmpFaces
;
200 tmpFaces
.resize(m
.Faces
.size());
201 for(i
=0;i
<(sint
)tmpFaces
.size();i
++)
202 tmpFaces
[i
]= m
.Faces
[i
];
204 _Skinned
= ((m
.VertexFlags
& CVertexBuffer::PaletteSkinFlag
)==CVertexBuffer::PaletteSkinFlag
);
205 // Skinning is OK only if SkinWeights are of same size as vertices.
206 _Skinned
= _Skinned
&& (m
.Vertices
.size()==m
.SkinWeights
.size());
208 // If skinning is KO, remove the Skin option.
209 uint vbFlags
= m
.VertexFlags
;
211 vbFlags
&= ~CVertexBuffer::PaletteSkinFlag
;
212 // Force presence of vertex.
213 vbFlags
|= CVertexBuffer::PositionFlag
;
216 // If the mesh is not skinned, we have just 1 _MatrixBlocks.
219 _MatrixBlocks
.resize(1);
220 // For each faces, assign it to the matrix block 0.
221 for(i
=0;i
<(sint
)tmpFaces
.size();i
++)
222 tmpFaces
[i
].MatrixBlockId
= 0;
224 // Else We must group/compute the matrixs blocks.
227 // reset matrix blocks.
228 _MatrixBlocks
.clear();
229 // build matrix blocks, and link faces to good matrix blocks.
230 buildSkin(m
, tmpFaces
);
233 /// 2. Then, for all faces, resolve continuities, building VBuffer.
234 //================================================
236 _VBuffer
.setNumVertices(0);
239 bool useFormatExt
= false;
240 /** If all texture coordinates are of dimension 2, we can setup the flags as before.
241 * If this isn't the case, we must setup a custom format
243 for (uint k
= 0; k
< CVertexBuffer::MaxStage
; ++k
)
246 (vbFlags
& (CVertexBuffer::TexCoord0Flag
<< k
))
247 && m
.NumCoords
[k
] != 2)
256 // setup standard format
257 _VBuffer
.setVertexFormat(vbFlags
);
259 else // setup extended format
261 _VBuffer
.clearValueEx();
262 if (vbFlags
& CVertexBuffer::PositionFlag
) _VBuffer
.addValueEx(CVertexBuffer::Position
, CVertexBuffer::Float3
);
263 if (vbFlags
& CVertexBuffer::NormalFlag
) _VBuffer
.addValueEx(CVertexBuffer::Normal
, CVertexBuffer::Float3
);
264 if (vbFlags
& CVertexBuffer::PrimaryColorFlag
) _VBuffer
.addValueEx(CVertexBuffer::PrimaryColor
, CVertexBuffer::UChar4
);
265 if (vbFlags
& CVertexBuffer::SecondaryColorFlag
) _VBuffer
.addValueEx(CVertexBuffer::SecondaryColor
, CVertexBuffer::UChar4
);
266 if (vbFlags
& CVertexBuffer::WeightFlag
) _VBuffer
.addValueEx(CVertexBuffer::Weight
, CVertexBuffer::Float4
);
267 if (vbFlags
& CVertexBuffer::PaletteSkinFlag
) _VBuffer
.addValueEx(CVertexBuffer::PaletteSkin
, CVertexBuffer::UChar4
);
268 if (vbFlags
& CVertexBuffer::FogFlag
) _VBuffer
.addValueEx(CVertexBuffer::Fog
, CVertexBuffer::Float1
);
270 for (uint k
= 0; k
< CVertexBuffer::MaxStage
; ++k
)
272 if (vbFlags
& (CVertexBuffer::TexCoord0Flag
<< k
))
274 switch(m
.NumCoords
[k
])
277 _VBuffer
.addValueEx((CVertexBuffer::TValue
) (CVertexBuffer::TexCoord0
+ k
), CVertexBuffer::Float2
);
280 _VBuffer
.addValueEx((CVertexBuffer::TValue
) (CVertexBuffer::TexCoord0
+ k
), CVertexBuffer::Float3
);
291 // Set local flags for corner comparison.
292 CCornerTmp::Flags
= vbFlags
;
295 const CFaceTmp
*pFace
= &(*tmpFaces
.begin());
297 sint N
= (sint
)tmpFaces
.size();
298 sint currentVBIndex
=0;
302 // process each face, building up the VB.
303 for(;N
>0;N
--, pFace
++)
305 sint v0
= pFace
->Corner
[0].Vertex
;
306 sint v1
= pFace
->Corner
[1].Vertex
;
307 sint v2
= pFace
->Corner
[2].Vertex
;
308 findVBId(corners
, &pFace
->Corner
[0], currentVBIndex
, m
.Vertices
[v0
], m
);
309 findVBId(corners
, &pFace
->Corner
[1], currentVBIndex
, m
.Vertices
[v1
], m
);
310 findVBId(corners
, &pFace
->Corner
[2], currentVBIndex
, m
.Vertices
[v2
], m
);
311 CMesh::CVertLink
vl1(nFaceMB
, 0, pFace
->Corner
[0].VBId
);
312 CMesh::CVertLink
vl2(nFaceMB
, 1, pFace
->Corner
[1].VBId
);
313 CMesh::CVertLink
vl3(nFaceMB
, 2, pFace
->Corner
[2].VBId
);
314 m
.VertLink
.push_back(vl1
);
315 m
.VertLink
.push_back(vl2
);
316 m
.VertLink
.push_back(vl3
);
321 /// 3. build the RdrPass material.
322 //================================
325 // For each _MatrixBlocks, point on those materials.
326 for(mb
= 0;mb
<_MatrixBlocks
.size();mb
++)
328 // Build RdrPass ids.
329 _MatrixBlocks
[mb
].RdrPass
.resize (numMaxMaterial
);
331 for(i
=0;i
<(sint
)_MatrixBlocks
[mb
].RdrPass
.size(); i
++)
333 _MatrixBlocks
[mb
].RdrPass
[i
].MaterialId
= i
;
334 // for build, force 32 bit indices
335 _MatrixBlocks
[mb
].RdrPass
[i
].PBlock
.setFormat(CIndexBuffer::Indices32
);
340 /// 4. Then, for all faces, build the RdrPass PBlock.
341 //===================================================
342 pFace
= &(*tmpFaces
.begin());
343 N
= (sint
)tmpFaces
.size();
344 for(;N
>0;N
--, pFace
++)
346 sint mbId
= pFace
->MatrixBlockId
;
347 nlassert(mbId
>=0 && mbId
<(sint
)_MatrixBlocks
.size());
348 // Insert the face in good MatrixBlock/RdrPass.
349 CIndexBuffer
&ib
= _MatrixBlocks
[mbId
].RdrPass
[pFace
->MaterialId
].PBlock
;
350 uint index
= ib
.getNumIndexes();
351 ib
.setNumIndexes (index
+3);
352 CIndexBufferReadWrite iba
;
354 iba
.setTri(index
, pFace
->Corner
[0].VBId
, pFace
->Corner
[1].VBId
, pFace
->Corner
[2].VBId
);
358 /// 5. Remove empty RdrPasses.
359 //============================
360 for(mb
= 0;mb
<_MatrixBlocks
.size();mb
++)
362 // NB: slow process (erase from a vector). Doens't matter since made at build.
363 vector
<CRdrPass
>::iterator itPass
;
364 for( itPass
=_MatrixBlocks
[mb
].RdrPass
.begin(); itPass
!=_MatrixBlocks
[mb
].RdrPass
.end(); )
366 // If this pass is empty, remove it.
367 if( itPass
->PBlock
.getNumIndexes()==0 )
368 itPass
= _MatrixBlocks
[mb
].RdrPass
.erase(itPass
);
375 //============================
377 this->_MeshMorpher
->BlendShapes
= m
.BlendShapes
;
379 // sort triangles for better cache use.
380 optimizeTriangleOrder();
382 // SmartPtr Copy VertexProgram effect.
383 this->_MeshVertexProgram
= m
.MeshVertexProgram
;
385 /// 7. Compact bones id and build bones name array.
386 //=================================================
391 // Reserve some space
392 _BonesName
.reserve (m
.BonesNames
.size ());
394 // Current local bone
395 uint currentBone
= 0;
397 // For each matrix block
399 for (matrixBlock
=0; matrixBlock
<_MatrixBlocks
.size(); matrixBlock
++)
401 // Ref on the matrix block
402 CMatrixBlock
&mb
= _MatrixBlocks
[matrixBlock
];
404 // Remap the skeleton index in model index
405 std::map
<uint
, uint
> remap
;
409 for (matrix
=0; matrix
<mb
.NumMatrix
; matrix
++)
411 // Get bone id in the skeleton
412 std::map
<uint
, uint
>::iterator ite
= remap
.find (mb
.MatrixId
[matrix
]);
415 if (ite
== remap
.end())
418 remap
.insert (std::map
<uint
, uint
>::value_type (mb
.MatrixId
[matrix
], currentBone
));
420 // Check the matrix id
421 nlassert (mb
.MatrixId
[matrix
] < m
.BonesNames
.size());
424 _BonesName
.push_back (m
.BonesNames
[mb
.MatrixId
[matrix
]]);
426 // Set the id in local
427 mb
.MatrixId
[matrix
] = currentBone
++;
431 // Set the id in local
432 mb
.MatrixId
[matrix
] = ite
->second
;
438 _BoneIdComputed
= false;
439 _BoneIdExtended
= false;
442 // Set the vertex buffer preferred memory
443 bool avoidVBHard
= _Skinned
|| ( _MeshMorpher
&& !_MeshMorpher
->BlendShapes
.empty() );
444 _VBuffer
.setPreferredMemory (avoidVBHard
?CVertexBuffer::RAMPreferred
:CVertexBuffer::StaticPreferred
, false);
447 // Some runtime not serialized compilation
451 // ***************************************************************************
452 void CMeshGeom::setBlendShapes(std::vector
<CBlendShape
>&bs
)
454 _MeshMorpher
->BlendShapes
= bs
;
455 // must update some RunTime parameters
460 // ***************************************************************************
461 void CMeshGeom::applyMaterialRemap(const std::vector
<sint
> &remap
)
463 for(uint mb
=0;mb
<getNbMatrixBlock();mb
++)
465 for(uint rp
=0;rp
<getNbRdrPass(mb
);rp
++)
468 uint32
&matId
= _MatrixBlocks
[mb
].RdrPass
[rp
].MaterialId
;
469 nlassert(remap
[matId
]>=0);
476 // ***************************************************************************
477 void CMeshGeom::initInstance(CMeshBaseInstance
*mbi
)
479 // init the instance with _MeshVertexProgram infos
480 if(_MeshVertexProgram
)
481 _MeshVertexProgram
->initInstance(mbi
);
484 // ***************************************************************************
485 bool CMeshGeom::clip(const std::vector
<CPlane
> &pyramid
, const CMatrix
&worldMatrix
)
487 // Speed Clip: clip just the sphere.
488 CBSphere
localSphere(_BBox
.getCenter(), _BBox
.getRadius());
489 CBSphere worldSphere
;
491 // transform the sphere in WorldMatrix (with nearly good scale info).
492 localSphere
.applyTransform(worldMatrix
, worldSphere
);
494 // if out of only plane, entirely out.
495 for(sint i
=0;i
<(sint
)pyramid
.size();i
++)
497 // We are sure that pyramid has normalized plane normals.
498 // if SpherMax OUT return false.
499 float d
= pyramid
[i
]*worldSphere
.Center
;
500 if(d
>worldSphere
.Radius
)
504 // test if must do a precise clip, according to mesh size.
505 if( _PreciseClipping
)
509 // if out of only plane, entirely out.
510 for(sint i
=0;i
<(sint
)pyramid
.size();i
++)
512 // Transform the pyramid in Object space.
513 localPlane
= pyramid
[i
]*worldMatrix
;
514 // localPlane must be normalized, because worldMatrix mya have a scale.
515 localPlane
.normalize();
516 // if the box is not partially inside the plane, quit
517 if( !_BBox
.clipBack(localPlane
) )
525 // ***************************************************************************
526 void CMeshGeom::render(IDriver
*drv
, CTransformShape
*trans
, float polygonCount
, uint32 rdrFlags
, float globalAlpha
)
529 // get the mesh instance.
530 CMeshBaseInstance
*mi
= safe_cast
<CMeshBaseInstance
*>(trans
);
531 // get a ptr on scene
532 CScene
*ownerScene
= mi
->getOwnerScene();
533 // get a ptr on renderTrav
534 CRenderTrav
*renderTrav
= &ownerScene
->getRenderTrav();
536 // Soft vb if not supported by the driver
537 if (drv
->slowUnlockVertexBufferHard())
538 _VBuffer
.setPreferredMemory (CVertexBuffer::RAMPreferred
, false);
540 // get the skeleton model to which I am binded (else NULL).
541 CSkeletonModel
*skeleton
;
542 skeleton
= mi
->getSkeletonModel();
543 // The mesh must not be skinned for render()
544 nlassert(!(_Skinned
&& mi
->isSkinned() && skeleton
));
545 bool bMorphApplied
= !_MeshMorpher
->BlendShapes
.empty();
546 bool useTangentSpace
= _MeshVertexProgram
&& _MeshVertexProgram
->needTangentSpace();
551 H_AUTO( NL3D_MeshGeom_RenderNormal
);
558 // If _Skinned (NB: the skin is not applied) and if lod.OriginalSkinRestored, then restoreOriginalSkinPart is
559 // not called but mush morpher write changed vertices into VBHard so its ok. The unchanged vertices
560 // are written in the preceding call to restoreOriginalSkinPart.
563 _MeshMorpher
->initSkinned(&_VBufferOri
,
566 &_OriginalSkinVertices
,
567 &_OriginalSkinNormals
,
568 useTangentSpace
? &_OriginalTGSpace
: NULL
,
570 _MeshMorpher
->updateSkinned (mi
->getBlendShapeFactors());
572 else // Not even skinned so we have to do all the stuff
574 _MeshMorpher
->init(&_VBufferOri
,
577 _MeshMorpher
->update (mi
->getBlendShapeFactors());
585 // else setup instance matrix
586 drv
->setupModelMatrix(trans
->getWorldMatrix());
589 // since instance skin is invalid but mesh is skinned , we must copy vertices/normals from original vertices.
592 // do it for this Lod only, and if cache say it is necessary.
593 if (!_OriginalSkinRestored
)
594 restoreOriginalSkinVertices();
598 // Setup meshVertexProgram
601 // use MeshVertexProgram effect?
602 bool useMeshVP
= _MeshVertexProgram
!= NULL
;
605 CMatrix invertedObjectMatrix
;
606 invertedObjectMatrix
= trans
->getWorldMatrix().inverted();
607 // really ok if success to begin VP
608 useMeshVP
= _MeshVertexProgram
->begin(drv
, ownerScene
, mi
, invertedObjectMatrix
, renderTrav
->CamPos
);
609 if (!useMeshVP
&& !mi
->_VPWindTreeFixed
)
611 if (dynamic_cast<CMeshVPWindTree
*>(&(*_MeshVertexProgram
)))
613 // fix for mesh tree v.p : all material should be lighted
614 for(uint mb
=0;mb
<_MatrixBlocks
.size();mb
++)
616 CMatrixBlock
&mBlock
= _MatrixBlocks
[mb
];
617 for(uint i
=0;i
<mBlock
.RdrPass
.size();i
++)
619 CMaterial
&mat
=mi
->Materials
[mBlock
.RdrPass
[i
].MaterialId
];
620 mat
.setLighting(true, mat
.getEmissive(), mat
.getAmbient(), mat
.getDiffuse(), mat
.getSpecular());
624 mi
->_VPWindTreeFixed
= true;
633 drv
->activeVertexBuffer(_VBuffer
);
636 // Global alpha used ?
637 uint32 globalAlphaUsed
= rdrFlags
& IMeshGeom::RenderGlobalAlpha
;
638 uint8 globalAlphaInt
=(uint8
)NLMISC::OptFastFloor(globalAlpha
*255);
641 // For all _MatrixBlocks
642 for(uint mb
=0;mb
<_MatrixBlocks
.size();mb
++)
644 CMatrixBlock
&mBlock
= _MatrixBlocks
[mb
];
645 if(mBlock
.RdrPass
.empty())
651 bool gaDisableZWrite
= (rdrFlags
& IMeshGeom::RenderGADisableZWrite
)?true:false;
654 for (uint i
=0;i
<mBlock
.RdrPass
.size();i
++)
656 CRdrPass
&rdrPass
= mBlock
.RdrPass
[i
];
657 // Render with the Materials of the MeshInstance.
658 if ( ( (mi
->Materials
[rdrPass
.MaterialId
].getBlend() == false) && (rdrFlags
& IMeshGeom::RenderOpaqueMaterial
) ) ||
659 ( (mi
->Materials
[rdrPass
.MaterialId
].getBlend() == true) && (rdrFlags
& IMeshGeom::RenderTransparentMaterial
) ) )
662 CMaterial
&material
=mi
->Materials
[rdrPass
.MaterialId
];
664 // Use a MeshBlender to modify material and driver.
665 CMeshBlender blender
;
666 blender
.prepareRenderForGlobalAlpha(material
, drv
, globalAlpha
, globalAlphaInt
, gaDisableZWrite
);
671 _MeshVertexProgram
->setupForMaterial(material
, drv
, ownerScene
, &_VBuffer
);
675 drv
->activeIndexBuffer(rdrPass
.PBlock
);
676 drv
->renderTriangles(material
, 0, rdrPass
.PBlock
.getNumIndexes()/3);
678 // Resetup material/driver
679 blender
.restoreRender(material
, drv
, gaDisableZWrite
);
686 for(uint i
=0;i
<mBlock
.RdrPass
.size();i
++)
688 CRdrPass
&rdrPass
= mBlock
.RdrPass
[i
];
689 // Render with the Materials of the MeshInstance.
690 if( ( (mi
->Materials
[rdrPass
.MaterialId
].getBlend() == false) && (rdrFlags
& IMeshGeom::RenderOpaqueMaterial
) ) ||
691 ( (mi
->Materials
[rdrPass
.MaterialId
].getBlend() == true) && (rdrFlags
& IMeshGeom::RenderTransparentMaterial
) ) )
694 CMaterial
&material
=mi
->Materials
[rdrPass
.MaterialId
];
699 _MeshVertexProgram
->setupForMaterial(material
, drv
, ownerScene
, &_VBuffer
);
703 drv
->activeIndexBuffer(rdrPass
.PBlock
);
704 drv
->renderTriangles(material
, 0, rdrPass
.PBlock
.getNumIndexes()/3);
710 // End VertexProgram effect
714 _MeshVertexProgram
->end(drv
);
719 // ***************************************************************************
720 void CMeshGeom::renderSkin(CTransformShape
*trans
, float alphaMRM
)
722 // get the mesh instance.
723 CMeshBaseInstance
*mi
= safe_cast
<CMeshBaseInstance
*>(trans
);
724 // get a ptr on scene
725 CScene
*ownerScene
= mi
->getOwnerScene();
726 // get a ptr on renderTrav
727 CRenderTrav
*renderTrav
= &ownerScene
->getRenderTrav();
728 // get a ptr on the driver
729 IDriver
*drv
= renderTrav
->getDriver();
732 // get the skeleton model to which I am binded (else NULL).
733 CSkeletonModel
*skeleton
;
734 skeleton
= mi
->getSkeletonModel();
735 // must be skinned for renderSkin()
736 nlassert(_Skinned
&& mi
->isSkinned() && skeleton
);
737 bool bMorphApplied
= !_MeshMorpher
->BlendShapes
.empty();
738 bool useTangentSpace
= _MeshVertexProgram
&& _MeshVertexProgram
->needTangentSpace();
743 H_AUTO( NL3D_MeshGeom_RenderSkinned
);
750 // Since Skinned we must update original skin vertices and normals because skinning use it
751 _MeshMorpher
->initSkinned(&_VBufferOri
,
754 &_OriginalSkinVertices
,
755 &_OriginalSkinNormals
,
756 useTangentSpace
? &_OriginalTGSpace
: NULL
,
758 _MeshMorpher
->updateSkinned (mi
->getBlendShapeFactors());
765 // NB: the skeleton matrix has already been setuped by CSkeletonModel
766 // NB: the normalize flag has already been setuped by CSkeletonModel
768 bool supportVertexStream
= ((_VBuffer
.getVertexFormat() & ~(CVertexBuffer::PaletteSkinFlag
| CVertexBuffer::WeightFlag
))
769 == (CVertexBuffer::PositionFlag
| CVertexBuffer::NormalFlag
| CVertexBuffer::TexCoord0Flag
))
770 && (_VBuffer
.getValueType(CVertexBuffer::TexCoord0
) == CVertexBuffer::Float2
)
771 && (_OriginalSkinNormals
.size()) && (!_OriginalTGSpace
.size());
772 CVertexStreamManager
*meshSkinManager
= renderTrav
->getMeshSkinManager();
774 if (supportVertexStream
)
776 uint maxVertices
= meshSkinManager
->getMaxVertices();
777 uint vertexSize
= meshSkinManager
->getVertexSize();
779 if (maxVertices
>= _OriginalSkinVertices
.size() && vertexSize
== 32)
781 // apply the skinning
782 uint8
*dstVb
= meshSkinManager
->lock();
783 applySkin(dstVb
, skeleton
);
784 meshSkinManager
->unlock(_OriginalSkinVertices
.size());
788 supportVertexStream
= false;
789 nlwarning("Unable to animate skinned model, too many vertices, or stream format unsupported");
794 nlwarning("Unable to animate skinned model, unsupported vertex format");
798 // Setup meshVertexProgram
801 // use MeshVertexProgram effect?
802 bool useMeshVP
= _MeshVertexProgram
!= NULL
;
805 CMatrix invertedObjectMatrix
;
806 invertedObjectMatrix
= skeleton
->getWorldMatrix().inverted();
807 // really ok if success to begin VP
808 useMeshVP
= _MeshVertexProgram
->begin(drv
, ownerScene
, mi
, invertedObjectMatrix
, renderTrav
->CamPos
);
815 if (supportVertexStream
)
817 meshSkinManager
->activate();
821 drv
->activeVertexBuffer(_VBuffer
);
825 // For all _MatrixBlocks
826 for(uint mb
=0;mb
<_MatrixBlocks
.size();mb
++)
828 CMatrixBlock
&mBlock
= _MatrixBlocks
[mb
];
829 if(mBlock
.RdrPass
.empty())
833 for(uint i
=0;i
<mBlock
.RdrPass
.size();i
++)
835 CRdrPass
&rdrPass
= mBlock
.RdrPass
[i
];
838 CMaterial
&material
=mi
->Materials
[rdrPass
.MaterialId
];
843 _MeshVertexProgram
->setupForMaterial(material
, drv
, ownerScene
, &_VBuffer
);
847 drv
->activeIndexBuffer(rdrPass
.PBlock
);
848 drv
->renderTriangles(material
, 0, rdrPass
.PBlock
.getNumIndexes()/3);
852 if (supportVertexStream
)
854 meshSkinManager
->swapVBHard();
857 // End VertexProgram effect
861 _MeshVertexProgram
->end(drv
);
867 // ***************************************************************************
868 void CMeshGeom::renderSimpleWithMaterial(IDriver
*drv
, const CMatrix
&worldMatrix
, CMaterial
&mat
)
870 H_AUTO( NL3D_MeshGeom_RenderSimpleWithMaterial
);
875 drv
->setupModelMatrix(worldMatrix
);
878 drv
->activeVertexBuffer(_VBuffer
);
880 // For all _MatrixBlocks
881 for(uint mb
=0;mb
<_MatrixBlocks
.size();mb
++)
883 CMatrixBlock
&mBlock
= _MatrixBlocks
[mb
];
884 if(mBlock
.RdrPass
.empty())
888 for(uint i
=0;i
<mBlock
.RdrPass
.size();i
++)
890 CRdrPass
&rdrPass
= mBlock
.RdrPass
[i
];
893 drv
->activeIndexBuffer(rdrPass
.PBlock
);
894 drv
->renderTriangles(mat
, 0, rdrPass
.PBlock
.getNumIndexes()/3);
901 // ***************************************************************************
902 void CMeshGeom::serial(NLMISC::IStream
&f
)
904 /* ***********************************************
905 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
906 * It can be loaded/called through CAsyncFileManager for instance
907 * ***********************************************/
917 - precompute of triangle order. (nothing more to load).
921 - separate serialisation CMesh / CMeshGeom.
923 sint ver
= f
.serialVersion (4);
926 // must have good original Skinned Vertex before writing.
927 if( !f
.isReading() && _Skinned
&& !_OriginalSkinRestored
)
929 restoreOriginalSkinVertices();
933 // Version 4+: Array of bone name
936 f
.serialCont (_BonesName
);
941 // Version3-: Bones index are in skeleton model id list
942 _BoneIdComputed
= (ver
< 4);
943 // In all case, must recompute usage of parents.
944 _BoneIdExtended
= false;
948 // Warning, if you have skinned this shape, you can't write it anymore because skinning id have been changed!
949 nlassert (_BoneIdComputed
==false);
952 // Version3+: MeshVertexProgram.
955 IMeshVertexProgram
*mvp
= NULL
;
958 f
.serialPolyPtr(mvp
);
959 _MeshVertexProgram
= mvp
;
963 mvp
= _MeshVertexProgram
;
964 f
.serialPolyPtr(mvp
);
967 else if(f
.isReading())
970 _MeshVertexProgram
= NULL
;
974 //_MeshVertexProgram= NULL;
976 // Version1+: _MeshMorpher.
978 f
.serial (*_MeshMorpher
);
983 f
.serialCont (_MatrixBlocks
);
988 // If _VertexBuffer changed, flag the VertexBufferHard.
991 // if >= version 2, reorder of triangles is precomputed, else compute it now.
994 optimizeTriangleOrder();
998 // Skinning: If Version < 4, _BonesName are not present, must compute _BonesId from localId
999 // Else it is computed at first computeBonesId().
1001 buildBoneUsageVer3();
1004 //_MeshVertexProgram= NULL;
1007 for(uint i=0;i<_MatrixBlocks.size();i++)
1009 for(uint j=0;j<_MatrixBlocks[i].RdrPass.size();j++)
1010 numTris+= _MatrixBlocks[i].RdrPass[j].PBlock.getNumTri();
1012 nlinfo("YOYO: %d Vertices. %d Triangles.", _VBuffer.getNumVertices(), numTris);
1015 // Some runtime not serialized compilation
1021 // ***************************************************************************
1022 void CMeshGeom::compileRunTime()
1024 /* ***********************************************
1025 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
1026 * It can be loaded/called through CAsyncFileManager for instance
1027 * ***********************************************/
1029 // if skinned, prepare skinning
1033 bkupOriginalSkinVertices();
1034 // build the shadow skin
1038 // Do precise clipping for big object??
1039 _PreciseClipping
= _BBox
.getRadius() >= NL3D_MESH_PRECISE_CLIP_THRESHOLD
;
1041 // Support MeshBlockRendering only if not skinned/meshMorphed.
1042 bool supportMeshBlockRendering
= !_Skinned
&& _MeshMorpher
->BlendShapes
.empty();
1044 // true only if one matrix block, and at least one rdrPass.
1045 supportMeshBlockRendering
= supportMeshBlockRendering
&& _MatrixBlocks
.size()==1 && !_MatrixBlocks
[0].RdrPass
.empty();
1046 if (supportMeshBlockRendering
&& _MeshVertexProgram
)
1048 supportMeshBlockRendering
= supportMeshBlockRendering
&& _MeshVertexProgram
->supportMeshBlockRendering();
1052 //supportMeshBlockRendering= false;
1054 // support MeshVertexProgram, but no material sorting...
1055 bool supportMBRPerMaterial
= supportMeshBlockRendering
&& _MeshVertexProgram
==NULL
;
1059 _SupportMBRFlags
= 0;
1060 if(supportMeshBlockRendering
)
1061 _SupportMBRFlags
|= MBROk
;
1062 if(supportMBRPerMaterial
)
1063 _SupportMBRFlags
|= MBRSortPerMaterial
;
1065 bool avoidVBHard
= _Skinned
|| ( _MeshMorpher
&& !_MeshMorpher
->BlendShapes
.empty() );
1066 _VBuffer
.setPreferredMemory (avoidVBHard
?CVertexBuffer::RAMPreferred
:CVertexBuffer::StaticPreferred
, false);
1069 // ***************************************************************************
1070 bool CMeshGeom::retrieveVertices(std::vector
<NLMISC::CVector
> &vertices
) const
1072 /* ***********************************************
1073 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
1074 * It can be loaded/called through CAsyncFileManager for instance
1075 * ***********************************************/
1079 // if resident, fails!!! cannot read!
1080 const CVertexBuffer
&vb
= getVertexBuffer();
1085 vertices
.resize(vb
.getNumVertices());
1087 CVertexBufferRead vba
;
1089 const uint8
*pVert
= (const uint8
*)vba
.getVertexCoordPointer(0);
1090 uint vSize
= vb
.getVertexSize();
1091 for(i
=0;i
<vertices
.size();i
++)
1093 vertices
[i
]= *(const CVector
*)pVert
;
1101 // ***************************************************************************
1102 bool CMeshGeom::retrieveTriangles(std::vector
<uint32
> &indices
) const
1104 /* ***********************************************
1105 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
1106 * It can be loaded/called through CAsyncFileManager for instance
1107 * ***********************************************/
1115 for(i
=0;i
<getNbMatrixBlock();i
++)
1117 for(uint rp
=0;rp
<getNbRdrPass(i
);rp
++)
1119 // if resident, fails!!! cannot read!
1120 const CIndexBuffer
&pb
= getRdrPassPrimitiveBlock(i
, rp
);
1123 numTris
+= getRdrPassPrimitiveBlock(i
, rp
).getNumIndexes()/3;
1126 indices
.resize(numTris
*3);
1130 for(i
=0;i
<getNbMatrixBlock();i
++)
1132 for(uint rp
=0;rp
<getNbRdrPass(i
);rp
++)
1134 const CIndexBuffer
&pb
= getRdrPassPrimitiveBlock(i
, rp
);
1135 CIndexBufferRead iba
;
1138 if (pb
.getFormat() == CIndexBuffer::Indices32
)
1140 memcpy(&indices
[triIdx
*3], iba
.getPtr(), pb
.getNumIndexes()*sizeof(uint32
));
1144 // std::copy will convert from 16 bits index to 32 bit index
1146 std::copy((uint16
*)iba
.getPtr(), ((uint16
*)iba
.getPtr()) + pb
.getNumIndexes(), stdext::make_checked_array_iterator(&indices
[triIdx
* 3], indices
.size() - triIdx
* 3));
1148 std::copy((uint16
*)iba
.getPtr(), ((uint16
*)iba
.getPtr()) + pb
.getNumIndexes(), &indices
[triIdx
* 3]);
1152 triIdx
+= pb
.getNumIndexes()/3;
1160 // ***************************************************************************
1161 // ***************************************************************************
1163 // ***************************************************************************
1164 // ***************************************************************************
1167 // ***************************************************************************
1168 void CMeshGeom::buildSkin(CMesh::CMeshBuild
&m
, std::vector
<CFaceTmp
> &tmpFaces
)
1171 TBoneMap remainingBones
;
1172 list
<uint
> remainingFaces
;
1175 // 0. normalize SkinWeights: for all weights at 0, copy the matrixId from 0th matrix => no random/bad use of matrix.
1176 //================================
1177 for(i
=0;i
<(sint
)m
.SkinWeights
.size();i
++)
1179 CMesh::CSkinWeight
&sw
= m
.SkinWeights
[i
];
1181 // 0th weight must not be 0.
1182 nlassert(sw
.Weights
[0]!=0);
1184 // Begin at 1, tests all other weights.
1185 for(j
=1;j
<NL3D_MESH_SKINNING_MAX_MATRIX
;j
++)
1187 // We don't use this entry??
1188 if(sw
.Weights
[j
]==0)
1190 // Setup MatrixId so that this vertex do no use more matrix than it really wants.
1191 sw
.MatrixId
[j
]= sw
.MatrixId
[0];
1197 // 1. build the list of used/remaining bones, in ascending order. (so we use the depth-first topolgy of hierarchy).
1198 //================================
1199 for(i
=0;i
<(sint
)tmpFaces
.size();i
++)
1201 CFaceTmp
&face
= tmpFaces
[i
];
1205 CMesh::CSkinWeight
&sw
= m
.SkinWeights
[face
.Corner
[j
].Vertex
];
1206 for(k
=0;k
<NL3D_MESH_SKINNING_MAX_MATRIX
;k
++)
1208 // insert (if not already here) the used bone in the set.
1209 // and insert his refcount. (NB: ctor() init it to 0 :) ).
1210 remainingBones
[sw
.MatrixId
[k
]].RefCount
++;
1216 // 2. Create the list of un-inserted faces.
1217 //================================
1218 for(i
=0;i
<(sint
)tmpFaces
.size();i
++)
1220 remainingFaces
.push_back(i
);
1225 // 3. Create as many Blocks as necessary.
1226 //================================
1227 // Which bones a face use (up to 12).
1228 vector
<uint
> boneUse
;
1229 boneUse
.reserve(NL3D_MESH_SKINNING_MAX_MATRIX
*3);
1231 // While still exist faces.
1232 while(!remainingFaces
.empty())
1234 // create a new matrix block.
1235 _MatrixBlocks
.push_back(CMatrixBlock());
1236 CMatrixBlock
&matrixBlock
= _MatrixBlocks
[_MatrixBlocks
.size()-1];
1237 matrixBlock
.NumMatrix
=0;
1239 // a. reset remainingBones as not inserted in the current matrixBlock.
1240 //============================
1242 for(itBone
= remainingBones
.begin();itBone
!=remainingBones
.end();itBone
++)
1244 itBone
->second
.Inserted
= false;
1248 // b. while still exist bones, try to insert faces which use them in matrixBlock.
1249 //============================
1250 while(!remainingBones
.empty())
1252 // get the first bone from the map. (remind: depth-first order).
1253 uint currentBoneId
= remainingBones
.begin()->first
;
1255 // If no more faces in the remainingFace list use this bone, remove it, and continue.
1256 if(remainingBones
.begin()->second
.RefCount
==0)
1258 remainingBones
.erase(remainingBones
.begin());
1262 // this is a marker, to know if a face insertion will occurs.
1263 bool faceAdded
= false;
1265 // traverse all faces, trying to insert them in current MatrixBlock processed.
1266 list
<uint
>::iterator itFace
;
1267 for(itFace
= remainingFaces
.begin(); itFace
!=remainingFaces
.end();)
1269 bool useCurrentBoneId
;
1272 // i/ Get info on current face.
1273 //-----------------------------
1275 // build which bones this face use.
1276 tmpFaces
[*itFace
].buildBoneUse(boneUse
, m
.SkinWeights
);
1278 // test if this face use the currentBoneId.
1279 useCurrentBoneId
= false;
1280 for(i
=0;i
<(sint
)boneUse
.size();i
++)
1282 // if this face use the currentBoneId
1283 if(boneUse
[i
]==currentBoneId
)
1285 useCurrentBoneId
= true;
1289 // compute how many bones that are not in the current matrixblock this face use.
1291 for(i
=0;i
<(sint
)boneUse
.size();i
++)
1293 // if this bone is not inserted in the current matrix block, inform it.
1294 if(!remainingBones
[boneUse
[i
]].Inserted
)
1299 // ii/ insert/reject face.
1300 //------------------------
1302 // If this face do not add any more bone, we can insert it into the current matrixblock.
1303 // If it use the currentBoneId, and do not explode max count, we allow insert it too in the current matrixblock.
1304 if( newBoneAdded
==0 ||
1305 (useCurrentBoneId
&& newBoneAdded
+matrixBlock
.NumMatrix
< IDriver::MaxModelMatrix
) )
1307 // Insert this face in the current matrix block
1309 CFaceTmp
&face
= tmpFaces
[*itFace
];
1311 // for all vertices of this face.
1314 CMesh::CSkinWeight
&sw
= m
.SkinWeights
[face
.Corner
[j
].Vertex
];
1316 // for each corner weight (4)
1317 for(k
=0;k
<NL3D_MESH_SKINNING_MAX_MATRIX
;k
++)
1319 // get the global boneId this corner weight use.
1320 uint boneId
= sw
.MatrixId
[k
];
1321 // get the CBoneTmp this corner weight use.
1322 CBoneTmp
&bone
= remainingBones
[boneId
];
1324 // decRef the bone .
1327 // Is this bone already inserted in the MatrixBlock ?
1328 if( !bone
.Inserted
)
1331 bone
.Inserted
= true;
1332 // link it to the MatrixId in the current matrixBlock.
1333 bone
.MatrixIdInMB
= matrixBlock
.NumMatrix
;
1335 // modify the matrixBlock
1336 matrixBlock
.MatrixId
[matrixBlock
.NumMatrix
]= boneId
;
1337 // increment the number of matrix in the matrixBlock.
1338 matrixBlock
.NumMatrix
++;
1341 // Copy Weight info for this Corner.
1342 // Set what matrix in the current matrix block this corner use.
1343 face
.Corner
[j
].Palette
.MatrixId
[k
]= bone
.MatrixIdInMB
;
1345 face
.Corner
[j
].Weights
[k
]= sw
.Weights
[k
];
1349 // to Which matrixblock this face is inserted.
1350 face
.MatrixBlockId
= (sint
)_MatrixBlocks
.size()-1;
1352 // remove the face from remain face list.
1353 itFace
= remainingFaces
.erase(itFace
);
1355 // inform the algorithm that a face has been added.
1360 // do not append this face to the current matrix block, skip to the next
1365 // If no faces have been added during this pass, we are blocked, and either the MatrixBlock may be full,
1366 // or there is no more face. So quit this block and process a new one.
1372 // NB: at the end of this loop, remainingBones may not be empty(), but all remainingBones should have RefCount==0.
1376 // 4. Re-order matrix use in MatrixBlocks, for minimum matrix change between MatrixBlocks.
1377 //================================
1378 vector
<CMatrixBlockRemap
> blockRemaps
;
1379 blockRemaps
.resize(_MatrixBlocks
.size());
1382 // For all MatrixBlocks > first, try to "mirror" bones from previous.
1383 for(i
=1;i
<(sint
)_MatrixBlocks
.size();i
++)
1385 CMatrixBlock
&mBlock
= _MatrixBlocks
[i
];
1386 CMatrixBlock
&mPrevBlock
= _MatrixBlocks
[i
-1];
1387 CMatrixBlockRemap
&remap
= blockRemaps
[i
];
1389 // First bkup the bone ids in remap table.
1390 for(j
=0;j
<(sint
)mBlock
.NumMatrix
;j
++)
1392 remap
.Remap
[j
]= mBlock
.MatrixId
[j
];
1395 // For all ids of this blocks, try to mirror them.
1396 for(j
=0;j
<(sint
)mBlock
.NumMatrix
;j
++)
1398 // get the location of this bone in the prev bone.
1399 sint idLoc
= mPrevBlock
.getMatrixIdLocation(mBlock
.MatrixId
[j
]);
1400 // If not found, or if bigger than current array, fails (cant be mirrored).
1401 // Or if already mirrored.
1402 if(idLoc
==-1 || idLoc
>=(sint
)mBlock
.NumMatrix
|| idLoc
==j
)
1409 // puts me on my mirrored location. and swap with the current one at this mirrored location.
1410 swap(mBlock
.MatrixId
[j
], mBlock
.MatrixId
[idLoc
]);
1411 // mBlock.MatrixId[j] is now a candidate for mirror.
1415 // Then build the Remap table, to re-order faces matrixId which use this matrix block.
1416 for(j
=0;j
<(sint
)mBlock
.NumMatrix
;j
++)
1418 // get the boneid which was at this position j before.
1419 uint boneId
= remap
.Remap
[j
];
1420 // search his new position, and store the result in the remap table.
1421 remap
.Remap
[j
]= mBlock
.getMatrixIdLocation(boneId
);
1424 // NB: this matrixBlock is re-ordered. next matrixBlock use this state.
1428 // For all faces/corners/weights, remap MatrixIds.
1429 for(i
=0;i
<(sint
)tmpFaces
.size();i
++)
1431 CFaceTmp
&face
= tmpFaces
[i
];
1432 // do it but for matrixblock0.
1433 if(face
.MatrixBlockId
!=0)
1435 CMatrixBlockRemap
&remap
= blockRemaps
[face
.MatrixBlockId
];
1439 for(k
=0;k
<NL3D_MESH_SKINNING_MAX_MATRIX
;k
++)
1441 uint oldId
= face
.Corner
[j
].Palette
.MatrixId
[k
];
1442 face
.Corner
[j
].Palette
.MatrixId
[k
]= (uint8
)remap
.Remap
[oldId
];
1451 // ***************************************************************************
1452 void CMeshGeom::CFaceTmp::buildBoneUse(vector
<uint
> &boneUse
, vector
<CMesh::CSkinWeight
> &skinWeights
)
1456 // For the 3 corners of the face.
1457 for(sint i
=0;i
<3;i
++)
1459 // get the CSkinWeight of this vertex.
1460 CMesh::CSkinWeight
&sw
= skinWeights
[Corner
[i
].Vertex
];
1462 // For all skin weights of this vertex,
1463 for(sint j
=0;j
<NL3D_MESH_SKINNING_MAX_MATRIX
;j
++)
1465 uint boneId
= sw
.MatrixId
[j
];
1466 // insert (if not in the array) this bone.
1467 if( find(boneUse
.begin(), boneUse
.end(), boneId
)==boneUse
.end() )
1468 boneUse
.push_back(boneId
);
1477 // ***************************************************************************
1478 sint
CMeshGeom::CMatrixBlock::getMatrixIdLocation(uint32 boneId
) const
1480 for(uint i
=0;i
<NumMatrix
;i
++)
1482 if(MatrixId
[i
]==boneId
)
1491 // ***************************************************************************
1492 float CMeshGeom::getNumTriangles (float distance
)
1497 // For each matrix block
1498 uint mbCount
=(uint
)_MatrixBlocks
.size();
1499 for (uint mb
=0; mb
<mbCount
; mb
++)
1501 CMatrixBlock
&block
=_MatrixBlocks
[mb
];
1503 // Count of primitive block
1504 uint pCount
=(uint
)block
.RdrPass
.size();
1505 for (uint pb
=0; pb
<pCount
; pb
++)
1507 // Ref on the primitive block
1508 CRdrPass
&pass
=block
.RdrPass
[pb
];
1511 triCount
+=pass
.PBlock
.getNumIndexes ()/3;
1514 return (float)triCount
;
1518 // ***************************************************************************
1519 void CMeshGeom::computeBonesId (CSkeletonModel
*skeleton
)
1521 // Already computed ?
1522 if (!_BoneIdComputed
)
1524 // Get a pointer on the skeleton
1525 nlassert (skeleton
);
1528 // **** For each bones, compute remap
1529 std::vector
<uint
> remap
;
1530 skeleton
->remapSkinBones(_BonesName
, _BonesId
, remap
);
1533 // **** Remap matrix blocks
1534 for (uint matrixBlock
=0; matrixBlock
<_MatrixBlocks
.size(); matrixBlock
++)
1536 // Ref on the matrix block
1537 CMatrixBlock
&mb
= _MatrixBlocks
[matrixBlock
];
1541 for (matrix
=0; matrix
<mb
.NumMatrix
; matrix
++)
1543 // Get bone id in the skeleton
1544 nlassert (mb
.MatrixId
[matrix
]<remap
.size());
1545 mb
.MatrixId
[matrix
] = remap
[mb
.MatrixId
[matrix
]];
1549 // **** Remap ShadowSkin, and compute Bone Sphere
1550 // Prepare Sphere compute
1551 static std::vector
<CAABBox
> boneBBoxes
;
1552 static std::vector
<bool> boneBBEmpty
;
1554 boneBBEmpty
.clear();
1555 boneBBoxes
.resize(_BonesId
.size());
1556 boneBBEmpty
.resize(_BonesId
.size(), true);
1558 // For simplicity, use the shadow skin info only, to compute bone sphere
1559 for(uint vert
=0;vert
<_ShadowSkin
.Vertices
.size();vert
++)
1561 CShadowVertex
&v
= _ShadowSkin
.Vertices
[vert
];
1563 uint srcId
= v
.MatrixId
;
1564 nlassert ( srcId
< remap
.size());
1566 v
.MatrixId
= remap
[srcId
];
1568 // if the boneId is valid (ie found)
1569 if(_BonesId
[srcId
]>=0)
1571 // transform the vertex pos in BoneSpace
1572 CVector p
= skeleton
->Bones
[_BonesId
[srcId
]].getBoneBase().InvBindPos
* v
.Vertex
;
1573 // extend the bone bbox.
1574 if(boneBBEmpty
[srcId
])
1576 boneBBoxes
[srcId
].setCenter(p
);
1577 boneBBEmpty
[srcId
]= false;
1581 boneBBoxes
[srcId
].extend(p
);
1587 _BonesSphere
.resize(_BonesId
.size());
1588 for(uint bone
=0;bone
<_BonesSphere
.size();bone
++)
1590 // If the bone is empty, mark with -1 in the radius.
1591 if(boneBBEmpty
[bone
])
1593 _BonesSphere
[bone
].Radius
= -1;
1597 _BonesSphere
[bone
].Center
= boneBBoxes
[bone
].getCenter();
1598 _BonesSphere
[bone
].Radius
= boneBBoxes
[bone
].getRadius();
1604 _BoneIdComputed
= true;
1608 // Already extended ?
1609 if (!_BoneIdExtended
)
1611 nlassert (skeleton
);
1614 // the total bone Usage of the mesh.
1615 vector
<bool> boneUsage
;
1616 boneUsage
.resize(skeleton
->Bones
.size(), false);
1618 // for all Bones marked as valid.
1620 for(i
=0; i
<_BonesId
.size(); i
++)
1622 // if not a valid boneId, skip it.
1626 // mark him and his father in boneUsage.
1627 skeleton
->flagBoneAndParents(_BonesId
[i
], boneUsage
);
1630 // fill _BonesIdExt with bones of _BonesId and their parents.
1631 _BonesIdExt
.clear();
1632 for(i
=0; i
<boneUsage
.size();i
++)
1634 // if the bone is used by the mesh, add it to BoneIdExt.
1636 _BonesIdExt
.push_back(i
);
1642 _BoneIdExtended
= true;
1648 // ***************************************************************************
1649 void CMeshGeom::buildBoneUsageVer3 ()
1653 // parse all matrixBlocks, couting MaxBoneId used.
1654 uint32 maxBoneId
= 0;
1655 // For each matrix block
1657 for (matrixBlock
=0; matrixBlock
<_MatrixBlocks
.size(); matrixBlock
++)
1659 CMatrixBlock
&mb
= _MatrixBlocks
[matrixBlock
];
1661 for (uint matrix
=0; matrix
<mb
.NumMatrix
; matrix
++)
1663 maxBoneId
= max(mb
.MatrixId
[matrix
], maxBoneId
);
1667 // alloc an array of maxBoneId+1, reset to 0.
1668 std::vector
<uint8
> boneUsage
;
1669 boneUsage
.resize(maxBoneId
+1, 0);
1671 // reparse all matrixBlocks, counting usage for each bone.
1672 for (matrixBlock
=0; matrixBlock
<_MatrixBlocks
.size(); matrixBlock
++)
1674 CMatrixBlock
&mb
= _MatrixBlocks
[matrixBlock
];
1676 for (uint matrix
=0; matrix
<mb
.NumMatrix
; matrix
++)
1678 // mark this bone as used.
1679 boneUsage
[mb
.MatrixId
[matrix
]]= 1;
1683 // For each bone used
1685 for(uint i
=0; i
<boneUsage
.size();i
++)
1687 // if the bone is used by the mesh, add it to BoneId.
1689 _BonesId
.push_back(i
);
1692 // Must also compute _BonesSphere
1693 // Cannot do it easily, deprecated data format => suppose radius 0 sphere
1694 CBSphere
sphere(CVector::Null
, 0.f
);
1695 _BonesSphere
.clear();
1696 _BonesSphere
.resize(_BonesId
.size(), sphere
);
1702 // ***************************************************************************
1703 void CMeshGeom::updateSkeletonUsage(CSkeletonModel
*sm
, bool increment
)
1705 // For all Bones used by this mesh.
1706 for(uint i
=0; i
<_BonesIdExt
.size();i
++)
1708 uint boneId
= _BonesIdExt
[i
];
1709 // Some explicit Error.
1710 if(boneId
>=sm
->Bones
.size())
1711 nlerror(" Skin is incompatible with Skeleton: tries to use bone %d", boneId
);
1712 // increment or decrement not Forced, because CMeshGeom use getActiveBoneSkinMatrix().
1714 sm
->incBoneUsage(boneId
, CSkeletonModel::UsageNormal
);
1716 sm
->decBoneUsage(boneId
, CSkeletonModel::UsageNormal
);
1721 // ***************************************************************************
1722 void CMeshGeom::bkupOriginalSkinVertices()
1727 contReset(_OriginalSkinVertices
);
1728 contReset(_OriginalSkinNormals
);
1729 contReset(_OriginalTGSpace
);
1731 // get num of vertices
1732 uint numVertices
= _VBuffer
.getNumVertices();
1734 CVertexBufferRead vba
;
1735 _VBuffer
.lock (vba
);
1737 // Copy VBuffer content into Original vertices normals.
1738 if(_VBuffer
.getVertexFormat() & CVertexBuffer::PositionFlag
)
1740 // copy vertices from VBuffer. (NB: useless geomorphed vertices are still copied, but doesn't matter).
1741 _OriginalSkinVertices
.resize(numVertices
);
1742 for(uint i
=0; i
<numVertices
;i
++)
1744 _OriginalSkinVertices
[i
]= *vba
.getVertexCoordPointer(i
);
1747 if(_VBuffer
.getVertexFormat() & CVertexBuffer::NormalFlag
)
1749 // copy normals from VBuffer. (NB: useless geomorphed normals are still copied, but doesn't matter).
1750 _OriginalSkinNormals
.resize(numVertices
);
1751 for(uint i
=0; i
<numVertices
;i
++)
1753 _OriginalSkinNormals
[i
]= *vba
.getNormalCoordPointer(i
);
1757 // is there tangent space added ?
1758 if (_MeshVertexProgram
&& _MeshVertexProgram
->needTangentSpace())
1761 nlassert(_VBuffer
.getNumTexCoordUsed() > 0);
1762 uint tgSpaceStage
= _VBuffer
.getNumTexCoordUsed() - 1;
1763 _OriginalTGSpace
.resize(numVertices
);
1764 for(uint i
=0; i
<numVertices
;i
++)
1766 _OriginalTGSpace
[i
]= *(CVector
*)vba
.getTexCoordPointer(i
, tgSpaceStage
);
1772 // ***************************************************************************
1773 void CMeshGeom::restoreOriginalSkinVertices()
1777 // get num of vertices
1778 uint numVertices
= _VBuffer
.getNumVertices();
1780 CVertexBufferReadWrite vba
;
1781 _VBuffer
.lock (vba
);
1783 // Copy VBuffer content into Original vertices normals.
1784 if(_VBuffer
.getVertexFormat() & CVertexBuffer::PositionFlag
)
1786 // copy vertices from VBuffer. (NB: useless geomorphed vertices are still copied, but doesn't matter).
1787 for(uint i
=0; i
<numVertices
;i
++)
1789 *vba
.getVertexCoordPointer(i
)= _OriginalSkinVertices
[i
];
1792 if(_VBuffer
.getVertexFormat() & CVertexBuffer::NormalFlag
)
1794 // copy normals from VBuffer. (NB: useless geomorphed normals are still copied, but doesn't matter).
1795 for(uint i
=0; i
<numVertices
;i
++)
1797 *vba
.getNormalCoordPointer(i
)= _OriginalSkinNormals
[i
];
1800 if (_MeshVertexProgram
&& _MeshVertexProgram
->needTangentSpace())
1802 uint numTexCoords
= _VBuffer
.getNumTexCoordUsed();
1803 nlassert(numTexCoords
>= 2);
1804 nlassert(_OriginalTGSpace
.size() == numVertices
);
1805 // copy tangent space vectors
1806 for(uint i
= 0; i
< numVertices
; ++i
)
1808 *(CVector
*)vba
.getTexCoordPointer(i
, numTexCoords
- 1)= _OriginalTGSpace
[i
];
1813 _OriginalSkinRestored
= true;
1817 // ***************************************************************************
1818 // Flags for software vertex skinning.
1819 #define NL3D_SOFTSKIN_VNEEDCOMPUTE 3
1820 #define NL3D_SOFTSKIN_VMUSTCOMPUTE 1
1821 #define NL3D_SOFTSKIN_VCOMPUTED 0
1822 // 3 means "vertex may need compute".
1823 // 1 means "Primitive say vertex must be computed".
1824 // 0 means "vertex is computed".
1827 // ***************************************************************************
1828 void CMeshGeom::applySkin(void *dstVb
, CSkeletonModel
*skeleton
)
1831 //===================
1832 if(_OriginalSkinVertices
.empty())
1835 // Use correct skinning
1836 nlassert(!_OriginalSkinNormals
.empty());
1837 nlassert(_OriginalTGSpace
.empty());
1839 // Get VB src/dst info/ptrs.
1840 uint numVertices
= (uint
)_OriginalSkinVertices
.size();
1841 static const uint dstStride
= 32; // _VBuffer.getVertexSize();
1842 uint srcStride
= _VBuffer
.getVertexSize();
1844 // Mark all vertices flag to not computed.
1845 static vector
<uint8
> skinFlags
;
1846 skinFlags
.resize(numVertices
);
1848 memset(&skinFlags
[0], NL3D_SOFTSKIN_VNEEDCOMPUTE
, numVertices
);
1850 CVertexBufferRead vba
;
1851 _VBuffer
.lock (vba
);
1853 uint8
*dstVbPos
= (uint8
*)dstVb
;
1854 uint8
*dstVbNormal
= dstVbPos
+ 12;
1855 uint8
*dstVbUV
= dstVbPos
+ 24;
1857 // For all MatrixBlocks
1858 //===================
1859 for(uint mb
= 0; mb
<_MatrixBlocks
.size();mb
++)
1861 // compute matrixes for this block.
1862 static CMatrix3x4 matrixes
[IDriver::MaxModelMatrix
];
1863 computeSkinMatrixes(skeleton
, matrixes
, mb
==0?NULL
:&_MatrixBlocks
[mb
-1], _MatrixBlocks
[mb
]);
1865 // check what vertex to skin for this PB.
1866 flagSkinVerticesForMatrixBlock(&skinFlags
[0], _MatrixBlocks
[mb
]);
1868 // Get VB src/dst ptrs.
1869 uint8
*pFlag
= &skinFlags
[0];
1870 CVector
*srcVector
= &_OriginalSkinVertices
[0];
1871 uint8
*srcPal
= (uint8
*)vba
.getPaletteSkinPointer(0);
1872 uint8
*srcWgt
= (uint8
*)vba
.getWeightPointer(0);
1873 uint8
*dstVector
= dstVbPos
;
1875 CVector
*srcNormal
= &_OriginalSkinNormals
[0];
1876 uint8
*dstNormal
= dstVbNormal
;
1878 // For all vertices that need to be computed.
1879 uint size
= numVertices
;
1882 // If we must compute this vertex.
1883 if(*pFlag
==NL3D_SOFTSKIN_VMUSTCOMPUTE
)
1885 // Flag this vertex as computed.
1886 *pFlag
=NL3D_SOFTSKIN_VCOMPUTED
;
1888 CPaletteSkin
*psPal
= (CPaletteSkin
*)srcPal
;
1891 nlassert(psPal
->MatrixId
[0]<IDriver::MaxModelMatrix
);
1892 nlassert(psPal
->MatrixId
[1]<IDriver::MaxModelMatrix
);
1893 nlassert(psPal
->MatrixId
[2]<IDriver::MaxModelMatrix
);
1894 nlassert(psPal
->MatrixId
[3]<IDriver::MaxModelMatrix
);
1896 // compute vertex part.
1897 computeSoftwarePointSkinning(matrixes
, srcVector
, psPal
, (float*)srcWgt
, (CVector
*)dstVector
);
1899 // compute normal part.
1900 computeSoftwareVectorSkinning(matrixes
, srcNormal
, psPal
, (float*)srcWgt
, (CVector
*)dstNormal
);
1905 // inc src (all whatever skin type used...)
1908 // inc paletteSkin and dst (all whatever skin type used...)
1911 dstVector
+= dstStride
;
1912 dstNormal
+= dstStride
;
1916 // Remaining vertices
1918 uint8
*pFlag
= &skinFlags
[0];
1919 CVector
*srcVector
= &_OriginalSkinVertices
[0];
1920 uint8
*dstVector
= dstVbPos
;
1921 CVector
*srcNormal
= &_OriginalSkinNormals
[0];
1922 uint8
*dstNormal
= dstVbNormal
;
1923 uint8
*srcUV
= (uint8
*)vba
.getTexCoordPointer(0, 0);
1924 uint8
*dstUV
= dstVbUV
;
1925 uint srcStride
= _VBuffer
.getVertexSize();
1927 for (uint i
= 0; i
< numVertices
; ++i
)
1929 if (*pFlag
!= NL3D_SOFTSKIN_VCOMPUTED
)
1931 *(CVector
*)dstVector
= *srcVector
;
1932 *(CVector
*)dstNormal
= *srcNormal
;
1934 *(CVector2f
*)dstUV
= *(CVector2f
*)srcUV
;
1938 // inc src (all whatever skin type used...)
1942 // inc paletteSkin and dst (all whatever skin type used...)
1943 dstVector
+= dstStride
;
1944 dstNormal
+= dstStride
;
1951 // ***************************************************************************
1952 void CMeshGeom::flagSkinVerticesForMatrixBlock(uint8
*skinFlags
, CMatrixBlock
&mb
)
1954 for(uint i
=0; i
<mb
.RdrPass
.size(); i
++)
1956 CIndexBuffer
&PB
= mb
.RdrPass
[i
].PBlock
;
1960 // This may be better to flags in 2 pass (first traverse primitives, then test vertices).
1961 // Better sol for BTB..., because number of tests are divided by 6 (for triangles).
1963 // for all prims, indicate which vertex we must compute.
1964 // nothing if not already computed (ie 0), because 0&1==0.
1967 CIndexBufferRead iba
;
1969 nIndex
= PB
.getNumIndexes();
1970 if (iba
.getFormat() == CIndexBuffer::Indices32
)
1972 uint32
*pIndex
= (uint32
*)iba
.getPtr();
1973 for(;nIndex
>0;nIndex
--, pIndex
++)
1974 skinFlags
[*pIndex
]&= NL3D_SOFTSKIN_VMUSTCOMPUTE
;
1978 uint16
*pIndex
= (uint16
*)iba
.getPtr();
1979 for(;nIndex
>0;nIndex
--, pIndex
++)
1980 skinFlags
[*pIndex
]&= NL3D_SOFTSKIN_VMUSTCOMPUTE
;
1986 // ***************************************************************************
1987 void CMeshGeom::computeSoftwarePointSkinning(CMatrix3x4
*matrixes
, CVector
*srcVec
, CPaletteSkin
*srcPal
, float *srcWgt
, CVector
*pDst
)
1991 // 0th matrix influence.
1992 pMat
= matrixes
+ srcPal
->MatrixId
[0];
1993 pMat
->mulSetPoint(*srcVec
, srcWgt
[0], *pDst
);
1994 // 1th matrix influence.
1995 pMat
= matrixes
+ srcPal
->MatrixId
[1];
1996 pMat
->mulAddPoint(*srcVec
, srcWgt
[1], *pDst
);
1997 // 2th matrix influence.
1998 pMat
= matrixes
+ srcPal
->MatrixId
[2];
1999 pMat
->mulAddPoint(*srcVec
, srcWgt
[2], *pDst
);
2000 // 3th matrix influence.
2001 pMat
= matrixes
+ srcPal
->MatrixId
[3];
2002 pMat
->mulAddPoint(*srcVec
, srcWgt
[3], *pDst
);
2006 // ***************************************************************************
2007 void CMeshGeom::computeSoftwareVectorSkinning(CMatrix3x4
*matrixes
, CVector
*srcVec
, CPaletteSkin
*srcPal
, float *srcWgt
, CVector
*pDst
)
2011 // 0th matrix influence.
2012 pMat
= matrixes
+ srcPal
->MatrixId
[0];
2013 pMat
->mulSetVector(*srcVec
, srcWgt
[0], *pDst
);
2014 // 1th matrix influence.
2015 pMat
= matrixes
+ srcPal
->MatrixId
[1];
2016 pMat
->mulAddVector(*srcVec
, srcWgt
[1], *pDst
);
2017 // 2th matrix influence.
2018 pMat
= matrixes
+ srcPal
->MatrixId
[2];
2019 pMat
->mulAddVector(*srcVec
, srcWgt
[2], *pDst
);
2020 // 3th matrix influence.
2021 pMat
= matrixes
+ srcPal
->MatrixId
[3];
2022 pMat
->mulAddVector(*srcVec
, srcWgt
[3], *pDst
);
2026 // ***************************************************************************
2027 void CMeshGeom::computeSkinMatrixes(CSkeletonModel
*skeleton
, CMatrix3x4
*matrixes
, CMatrixBlock
*prevBlock
, CMatrixBlock
&mBlock
)
2029 // For all matrix of this mBlock.
2030 for(uint idMat
=0;idMat
<mBlock
.NumMatrix
;idMat
++)
2032 uint curBoneId
= mBlock
.MatrixId
[idMat
];
2034 // If same matrix binded as previous block, no need to bind!!
2035 if(prevBlock
&& idMat
<prevBlock
->NumMatrix
&& prevBlock
->MatrixId
[idMat
]== curBoneId
)
2038 // Else, we must setup the matrix
2039 matrixes
[idMat
].set(skeleton
->getActiveBoneSkinMatrix(curBoneId
));
2044 // ***************************************************************************
2045 void CMeshGeom::profileSceneRender(CRenderTrav
*rdrTrav
, CTransformShape
*trans
, float polygonCount
, uint32 rdrFlags
)
2047 // get the mesh instance.
2048 CMeshBaseInstance
*mi
= safe_cast
<CMeshBaseInstance
*>(trans
);
2050 // For all _MatrixBlocks
2052 for(uint mb
=0;mb
<_MatrixBlocks
.size();mb
++)
2054 CMatrixBlock
&mBlock
= _MatrixBlocks
[mb
];
2056 // Profile all pass.
2057 for (uint i
=0;i
<mBlock
.RdrPass
.size();i
++)
2059 CRdrPass
&rdrPass
= mBlock
.RdrPass
[i
];
2060 // Profile with the Materials of the MeshInstance.
2061 if ( ( (mi
->Materials
[rdrPass
.MaterialId
].getBlend() == false) && (rdrFlags
& IMeshGeom::RenderOpaqueMaterial
) ) ||
2062 ( (mi
->Materials
[rdrPass
.MaterialId
].getBlend() == true) && (rdrFlags
& IMeshGeom::RenderTransparentMaterial
) ) )
2064 triCount
+= rdrPass
.PBlock
.getNumIndexes()/3;
2073 rdrTrav
->Scene
->incrementProfileTriVBFormat(rdrTrav
->Scene
->BenchRes
.MeshProfileTriVBFormat
,
2074 _VBuffer
.getVertexFormat(), triCount
);
2077 if(_VBuffer
.getPreferredMemory()!=CVertexBuffer::RAMPreferred
)
2078 rdrTrav
->Scene
->BenchRes
.NumMeshVBufferHard
++;
2080 rdrTrav
->Scene
->BenchRes
.NumMeshVBufferStd
++;
2082 // rendered in BlockRendering, only if not transparent pass (known it if RenderTransparentMaterial is set)
2083 if(supportMeshBlockRendering() && (rdrFlags
& IMeshGeom::RenderTransparentMaterial
)==0 )
2085 if(isMeshInVBHeap())
2087 rdrTrav
->Scene
->BenchRes
.NumMeshRdrBlockWithVBHeap
++;
2088 rdrTrav
->Scene
->BenchRes
.NumMeshTriRdrBlockWithVBHeap
+= triCount
;
2092 rdrTrav
->Scene
->BenchRes
.NumMeshRdrBlock
++;
2093 rdrTrav
->Scene
->BenchRes
.NumMeshTriRdrBlock
+= triCount
;
2098 rdrTrav
->Scene
->BenchRes
.NumMeshRdrNormal
++;
2099 rdrTrav
->Scene
->BenchRes
.NumMeshTriRdrNormal
+= triCount
;
2104 // ***************************************************************************
2105 bool CMeshGeom::intersectSkin(CTransformShape
*mi
, const CMatrix
&toRaySpace
, float &dist2D
, float &distZ
, bool computeDist2D
)
2107 // for Mesh, Use the Shadow Skinning (simple version).
2110 if(!mi
|| _OriginalSkinVertices
.empty())
2112 CSkeletonModel
*skeleton
= mi
->getSkeletonModel();
2116 // Compute skinning with all matrix this Mesh use. (the shadow geometry cannot use other Matrix than the mesh use).
2117 static std::vector
<uint32
> matInfs
;
2118 matInfs
.resize(_BonesId
.size());
2119 for(uint i
=0;i
<matInfs
.size();i
++)
2121 // treat any "missing bone" as the root one.
2122 matInfs
[i
]= max(_BonesId
[i
], (sint32
)0);
2124 return _ShadowSkin
.getRayIntersection(toRaySpace
, *skeleton
, matInfs
, dist2D
, distZ
, computeDist2D
);
2127 // ***************************************************************************
2128 void CMeshGeom::buildShadowSkin()
2130 /* ***********************************************
2131 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
2132 * It can be loaded/called through CAsyncFileManager for instance
2133 * ***********************************************/
2136 contReset(_ShadowSkin
.Vertices
);
2137 contReset(_ShadowSkin
.Triangles
);
2139 nlassert(_Skinned
&& (_VBuffer
.getVertexFormat() & CVertexBuffer::PaletteSkinFlag
)
2140 && (_VBuffer
.getVertexFormat() & CVertexBuffer::PositionFlag
) );
2143 // *** Copy VBuffer content
2145 // get num of vertices
2146 uint numVertices
= _VBuffer
.getNumVertices();
2149 CVertexBufferRead vba
;
2150 _VBuffer
.lock (vba
);
2151 uint8
*srcPal
= (uint8
*)vba
.getPaletteSkinPointer(0);
2152 uint8
*srcVert
= (uint8
*)vba
.getVertexCoordPointer(0);
2153 uint32 srcVertSize
= _VBuffer
.getVertexSize();
2155 // copy vertices from VBuffer
2156 _ShadowSkin
.Vertices
.resize(numVertices
);
2157 for(uint i
=0; i
<numVertices
;i
++)
2160 _ShadowSkin
.Vertices
[i
].Vertex
= *((CVector
*)srcVert
);
2161 // Suppose the 0 matrix inf is the highest (we are at least sure it is not 0)
2162 // And SkinWeight Export show the 0th is the highest one...
2163 _ShadowSkin
.Vertices
[i
].MatrixId
= ((CPaletteSkin
*)srcPal
)->MatrixId
[0];
2166 srcVert
+= srcVertSize
;
2167 srcPal
+= srcVertSize
;
2171 // But _ShadowSkin.Vertices[i].MatrixId is incorrect, since < IDriver::MaxModelMatrix
2174 // *** Count number of triangles, and get start index of each matrix block in the final Tri list
2177 // can't be static cause of ThreadSafe
2178 vector
<pair
<uint32
,uint32
> > mbIndexRange
;
2179 mbIndexRange
.resize(_MatrixBlocks
.size());
2180 for(mb
=0;mb
<_MatrixBlocks
.size();mb
++)
2182 // this matrix block start here
2183 mbIndexRange
[mb
].first
= numIndices
;
2185 // count tris rendered for this matrix block
2186 const CMatrixBlock
&mBlock
= _MatrixBlocks
[mb
];
2187 for(uint rp
=0;rp
<mBlock
.RdrPass
.size();rp
++)
2189 const CRdrPass
&rPass
= mBlock
.RdrPass
[rp
];
2190 numIndices
+= rPass
.PBlock
.getNumIndexes();
2193 // this matrix block end here
2194 mbIndexRange
[mb
].second
= numIndices
;
2198 // *** Fill Triangles
2199 nlverify(retrieveTriangles(_ShadowSkin
.Triangles
));
2200 nlassert(numIndices
==_ShadowSkin
.Triangles
.size());
2203 // *** Reindex correctly MatrixId, (ie unpack matrix blocks)
2204 // can't be static cause of ThreadSafe
2205 vector
<bool> vertReIndexed
;
2206 vertReIndexed
.resize(_ShadowSkin
.Vertices
.size(), false);
2207 // for all matrix blocks
2208 for(mb
=0;mb
<mbIndexRange
.size();mb
++)
2210 const CMatrixBlock
&mBlock
= _MatrixBlocks
[mb
];
2212 uint iStart
= mbIndexRange
[mb
].first
;
2213 uint iEnd
= mbIndexRange
[mb
].second
;
2214 nlassert(iStart
<= iEnd
&& iEnd
<=_ShadowSkin
.Triangles
.size());
2216 // For all indices in this range
2217 uint32
*pIndex
= &_ShadowSkin
.Triangles
[iStart
];
2218 uint nbIndex
= iEnd
- iStart
;
2219 for(;nbIndex
>0;--nbIndex
, pIndex
++)
2221 uint index
= *pIndex
;
2222 // if not already reindexed
2223 if(!vertReIndexed
[index
])
2225 vertReIndexed
[index
]= true;
2227 uint matId
= _ShadowSkin
.Vertices
[index
].MatrixId
;
2228 nlassert(matId
<mBlock
.NumMatrix
);
2229 _ShadowSkin
.Vertices
[index
].MatrixId
= mBlock
.MatrixId
[matId
];
2236 // ***************************************************************************
2237 // ***************************************************************************
2238 // Mesh Block Render Interface
2239 // ***************************************************************************
2240 // ***************************************************************************
2243 // ***************************************************************************
2244 bool CMeshGeom::supportMeshBlockRendering () const
2246 return _SupportMBRFlags
!=0;
2249 // ***************************************************************************
2250 bool CMeshGeom::sortPerMaterial() const
2252 return (_SupportMBRFlags
& MBRSortPerMaterial
)!=0;
2254 // ***************************************************************************
2255 uint
CMeshGeom::getNumRdrPassesForMesh() const
2257 return (uint
)_MatrixBlocks
[0].RdrPass
.size();
2259 // ***************************************************************************
2260 uint
CMeshGeom::getNumRdrPassesForInstance(CMeshBaseInstance
*inst
) const
2262 return (uint
)_MatrixBlocks
[0].RdrPass
.size();
2264 // ***************************************************************************
2265 void CMeshGeom::beginMesh(CMeshGeomRenderContext
&rdrCtx
)
2267 if(rdrCtx
.RenderThroughVBHeap
)
2269 // Don't setup VB in this case, since use the VBHeap setuped one.
2270 // NB: no VertexProgram test since VBHeap not possible with it...
2271 nlassert( (_SupportMBRFlags
& MBRCurrentUseVP
)==0 );
2275 // use MeshVertexProgram effect?
2276 if( _MeshVertexProgram
!= NULL
&& _MeshVertexProgram
->isMBRVpOk(rdrCtx
.Driver
) )
2279 _SupportMBRFlags
|= MBRCurrentUseVP
;
2280 // Before VB activation
2281 _MeshVertexProgram
->beginMBRMesh(rdrCtx
.Driver
, rdrCtx
.Scene
);
2284 // active VB. SoftwareSkinning: reset flags for skinning.
2285 rdrCtx
.Driver
->activeVertexBuffer(_VBuffer
);
2288 // ***************************************************************************
2289 void CMeshGeom::activeInstance(CMeshGeomRenderContext
&rdrCtx
, CMeshBaseInstance
*inst
, float polygonCount
, void *vbDst
)
2291 // setup instance matrix
2292 rdrCtx
.Driver
->setupModelMatrix(inst
->getWorldMatrix());
2295 inst
->changeLightSetup(rdrCtx
.RenderTrav
);
2297 // MeshVertexProgram ?
2298 if( _SupportMBRFlags
& MBRCurrentUseVP
)
2300 CMatrix invertedObjectMatrix
;
2301 invertedObjectMatrix
= inst
->getWorldMatrix().inverted();
2302 _MeshVertexProgram
->beginMBRInstance(rdrCtx
.Driver
, rdrCtx
.Scene
, inst
, invertedObjectMatrix
);
2305 // ***************************************************************************
2306 void CMeshGeom::renderPass(CMeshGeomRenderContext
&rdrCtx
, CMeshBaseInstance
*mi
, float polygonCount
, uint rdrPassId
)
2308 CMatrixBlock
&mBlock
= _MatrixBlocks
[0];
2310 CRdrPass
&rdrPass
= mBlock
.RdrPass
[rdrPassId
];
2311 // Render with the Materials of the MeshInstance, only if not blended.
2312 if( ( (mi
->Materials
[rdrPass
.MaterialId
].getBlend() == false) ) )
2314 CMaterial
&material
= mi
->Materials
[rdrPass
.MaterialId
];
2316 // MeshVertexProgram ?
2317 if( _SupportMBRFlags
& MBRCurrentUseVP
)
2319 rdrCtx
.RenderTrav
->changeVPLightSetupMaterial(material
, false);
2322 if(rdrCtx
.RenderThroughVBHeap
)
2324 // render shifted primitives
2325 rdrCtx
.Driver
->activeIndexBuffer(rdrPass
.VBHeapPBlock
);
2326 rdrCtx
.Driver
->renderTriangles(material
, 0, rdrPass
.VBHeapPBlock
.getNumIndexes()/3);
2330 // render primitives
2331 rdrCtx
.Driver
->activeIndexBuffer(rdrPass
.PBlock
);
2332 rdrCtx
.Driver
->renderTriangles(material
, 0, rdrPass
.PBlock
.getNumIndexes()/3);
2336 // ***************************************************************************
2337 void CMeshGeom::endMesh(CMeshGeomRenderContext
&rdrCtx
)
2339 // MeshVertexProgram ?
2340 if( _SupportMBRFlags
& MBRCurrentUseVP
)
2343 _MeshVertexProgram
->endMBRMesh( rdrCtx
.Driver
);
2345 // and remove Current Flag.
2346 _SupportMBRFlags
&= ~MBRCurrentUseVP
;
2350 // ***************************************************************************
2351 bool CMeshGeom::getVBHeapInfo(uint
&vertexFormat
, uint
&numVertices
)
2353 // CMeshGeom support VBHeap rendering, assuming supportMeshBlockRendering is true.
2354 if( _SupportMBRFlags
)
2355 /* Yoyo: If VertexProgram, DON'T SUPPORT!! because VB need to be activated AFTER meshVP activation
2356 NB: still possible with complex code to do it (sort per VP type (with or not)...), but tests in Ryzom
2357 shows that VBHeap is not really important (not so much different shapes...)
2359 if( _MeshVertexProgram
==NULL
)
2361 vertexFormat
= _VBuffer
.getVertexFormat();
2362 numVertices
= _VBuffer
.getNumVertices();
2369 // ***************************************************************************
2370 void CMeshGeom::computeMeshVBHeap(void *dst
, uint indexStart
)
2372 // Fill dst with Buffer content.
2373 CVertexBufferRead vba
;
2374 _VBuffer
.lock (vba
);
2375 memcpy(dst
, vba
.getVertexCoordPointer(), _VBuffer
.getNumVertices()*_VBuffer
.getVertexSize() );
2377 // NB: only 1 MB is possible ...
2378 nlassert(_MatrixBlocks
.size()==1);
2379 CMatrixBlock
&mBlock
= _MatrixBlocks
[0];
2381 for(uint i
=0;i
<mBlock
.RdrPass
.size();i
++)
2384 CIndexBuffer
&srcPb
= mBlock
.RdrPass
[i
].PBlock
;
2385 CIndexBuffer
&dstPb
= mBlock
.RdrPass
[i
].VBHeapPBlock
;
2389 dstPb
.setNumIndexes(srcPb
.getNumIndexes());
2390 CIndexBufferRead ibaRead
;
2391 srcPb
.lock (ibaRead
);
2392 CIndexBufferReadWrite ibaWrite
;
2393 dstPb
.lock (ibaWrite
);
2394 // nico : apparently not used, so don't manage 16 bits index here
2395 nlassert(ibaRead
.getFormat() == CIndexBuffer::Indices32
);
2396 nlassert(ibaWrite
.getFormat() == CIndexBuffer::Indices32
);
2397 const uint32
*srcTriPtr
= (const uint32
*) ibaRead
.getPtr();
2398 uint32
*dstTriPtr
= (uint32
*) ibaWrite
.getPtr();
2399 for(j
=0; j
<dstPb
.getNumIndexes();j
++)
2401 dstTriPtr
[j
]= srcTriPtr
[j
]+indexStart
;
2407 // ***************************************************************************
2408 // ***************************************************************************
2409 // CMeshBuild components.
2410 // ***************************************************************************
2411 // ***************************************************************************
2415 // ***************************************************************************
2416 CMesh::CCorner::CCorner()
2420 Normal
= CVector::Null
;
2421 for(i
=0;i
<CVertexBuffer::MaxStage
;i
++)
2423 Uvws
[i
]= CUVW(0, 0, 0);
2425 Color
.set(255,255,255,255);
2426 Specular
.set(0,0,0,0);
2430 // ***************************************************************************
2431 void CMesh::CCorner::serial(NLMISC::IStream
&f
)
2433 nlassert(0); // not used
2436 for(int i
=0;i
<CVertexBuffer::MaxStage
;++i
) f
.serial(Uvws
[i
]);
2441 // ***************************************************************************
2442 void CMesh::CFace::serial(NLMISC::IStream
&f
)
2444 for(int i
=0;i
<3;++i
)
2445 f
.serial(Corner
[i
]);
2446 f
.serial(MaterialId
);
2447 f
.serial(SmoothGroup
);
2450 // ***************************************************************************
2451 void CMesh::CSkinWeight::serial(NLMISC::IStream
&f
)
2453 for(int i
=0;i
<NL3D_MESH_SKINNING_MAX_MATRIX
;++i
)
2455 f
.serial(MatrixId
[i
]);
2456 f
.serial(Weights
[i
]);
2460 // ***************************************************************************
2461 /* Serialization is not used.
2462 void CMesh::CMeshBuild::serial(NLMISC::IStream &f)
2464 sint ver= f.serialVersion(0);
2466 // Serial mesh base (material info).
2467 CMeshBaseBuild::serial(f);
2470 f.serial( VertexFlags );
2471 f.serialCont( Vertices );
2472 f.serialCont( SkinWeights );
2473 f.serialCont( Faces );
2478 // ************************************
2479 CMesh::CMeshBuild::CMeshBuild()
2481 for (uint k
= 0; k
< CVertexBuffer::MaxStage
; ++k
)
2489 // ***************************************************************************
2490 // ***************************************************************************
2492 // ***************************************************************************
2493 // ***************************************************************************
2497 // ***************************************************************************
2500 /* ***********************************************
2501 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
2502 * It can be loaded/called through CAsyncFileManager for instance
2503 * ***********************************************/
2505 // create the MeshGeom
2506 _MeshGeom
= new CMeshGeom
;
2508 // ***************************************************************************
2511 /* ***********************************************
2512 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
2513 * It can be loaded/called through CAsyncFileManager for instance
2514 * ***********************************************/
2516 // delete the MeshGeom
2521 // ***************************************************************************
2522 CMesh::CMesh(const CMesh
&mesh
) : CMeshBase()
2524 // create the MeshGeom
2525 _MeshGeom
= new CMeshGeom(*mesh
._MeshGeom
);
2529 // ***************************************************************************
2530 CMesh
&CMesh::operator=(const CMesh
&mesh
)
2532 // Copy CMeshBase part
2533 (CMeshBase
&)*this= (CMeshBase
&)mesh
;
2535 // copy content of meshGeom.
2536 *_MeshGeom
= *mesh
._MeshGeom
;
2544 // ***************************************************************************
2545 void CMesh::build (CMeshBase::CMeshBaseBuild
&mbase
, CMeshBuild
&m
)
2547 /// copy MeshBase info: materials ....
2548 CMeshBase::buildMeshBase (mbase
);
2550 // build the geometry.
2551 _MeshGeom
->build (m
, (uint
)mbase
.Materials
.size());
2553 // compile some stuff
2558 // ***************************************************************************
2559 void CMesh::optimizeMaterialUsage(std::vector
<sint
> &remap
)
2561 // For each material, count usage.
2562 vector
<bool> materialUsed
;
2563 materialUsed
.resize(CMeshBase::_Materials
.size(), false);
2564 for(uint mb
=0;mb
<getNbMatrixBlock();mb
++)
2566 for(uint rp
=0;rp
<getNbRdrPass(mb
);rp
++)
2568 uint matId
= getRdrPassMaterial(mb
, rp
);
2570 materialUsed
[matId
]= true;
2574 // Apply it to meshBase
2575 CMeshBase::applyMaterialUsageOptim(materialUsed
, remap
);
2577 // Apply lut to meshGeom.
2578 _MeshGeom
->applyMaterialRemap(remap
);
2582 // ***************************************************************************
2583 void CMesh::setBlendShapes(std::vector
<CBlendShape
>&bs
)
2585 _MeshGeom
->setBlendShapes (bs
);
2588 // ***************************************************************************
2589 void CMesh::build(CMeshBase::CMeshBaseBuild
&mbuild
, CMeshGeom
&meshGeom
)
2591 /// copy MeshBase info: materials ....
2592 CMeshBase::buildMeshBase(mbuild
);
2594 // build the geometry.
2595 *_MeshGeom
= meshGeom
;
2597 // compile some stuff
2602 // ***************************************************************************
2603 CTransformShape
*CMesh::createInstance(CScene
&scene
)
2605 // Create a CMeshInstance, an instance of a mesh.
2606 //===============================================
2607 CMeshInstance
*mi
= (CMeshInstance
*)scene
.createModel(NL3D::MeshInstanceId
);
2610 // instanciate the material part of the Mesh, ie the CMeshBase.
2611 CMeshBase::instanciateMeshBase(mi
, &scene
);
2614 // do some instance init for MeshGeom
2615 _MeshGeom
->initInstance(mi
);
2617 // init render Filter
2618 mi
->initRenderFilterType();
2624 // ***************************************************************************
2625 bool CMesh::clip(const std::vector
<CPlane
> &pyramid
, const CMatrix
&worldMatrix
)
2627 return _MeshGeom
->clip(pyramid
, worldMatrix
);
2631 // ***************************************************************************
2632 void CMesh::render(IDriver
*drv
, CTransformShape
*trans
, bool passOpaque
)
2635 uint32 mask
= (0-(uint32
)passOpaque
);
2637 // select rdrFlags, without ifs.
2638 rdrFlags
= mask
& (IMeshGeom::RenderOpaqueMaterial
| IMeshGeom::RenderPassOpaque
);
2639 rdrFlags
|= ~mask
& (IMeshGeom::RenderTransparentMaterial
);
2641 _MeshGeom
->render(drv
, trans
, 0, rdrFlags
, 1);
2645 // ***************************************************************************
2646 void CMesh::serial(NLMISC::IStream
&f
)
2648 /* ***********************************************
2649 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
2650 * It can be loaded/called through CAsyncFileManager for instance
2651 * ***********************************************/
2655 - cut in serialisation, because of:
2656 - bad ITexture serialisation (with no version....) => must cut. (see CMeshBase serial).
2657 - because of this and to simplify, make a cut too in CMesh serialisation.
2658 NB : all old version code is dropped.
2660 sint ver
= f
.serialVersion(6);
2664 throw NLMISC::EStream(f
, "Mesh in Stream is too old (Mesh version < 6)");
2667 // serial Materials infos contained in CMeshBase.
2668 CMeshBase::serialMeshBase(f
);
2672 _MeshGeom
->serial(f
);
2674 // if reading, compile some stuff
2680 // ***************************************************************************
2681 const NLMISC::CAABBoxExt
& CMesh::getBoundingBox() const
2683 return _MeshGeom
->getBoundingBox();
2685 // ***************************************************************************
2686 const CVertexBuffer
&CMesh::getVertexBuffer() const
2688 return _MeshGeom
->getVertexBuffer() ;
2690 // ***************************************************************************
2691 uint
CMesh::getNbMatrixBlock() const
2693 return _MeshGeom
->getNbMatrixBlock();
2695 // ***************************************************************************
2696 uint
CMesh::getNbRdrPass(uint matrixBlockIndex
) const
2698 return _MeshGeom
->getNbRdrPass(matrixBlockIndex
) ;
2700 // ***************************************************************************
2701 const CIndexBuffer
&CMesh::getRdrPassPrimitiveBlock(uint matrixBlockIndex
, uint renderingPassIndex
) const
2703 return _MeshGeom
->getRdrPassPrimitiveBlock(matrixBlockIndex
, renderingPassIndex
) ;
2705 // ***************************************************************************
2706 uint32
CMesh::getRdrPassMaterial(uint matrixBlockIndex
, uint renderingPassIndex
) const
2708 return _MeshGeom
->getRdrPassMaterial(matrixBlockIndex
, renderingPassIndex
) ;
2710 // ***************************************************************************
2711 float CMesh::getNumTriangles (float distance
)
2713 // A CMesh do not degrad himself, so return 0, to not be taken into account.
2716 // ***************************************************************************
2717 const CMeshGeom
& CMesh::getMeshGeom () const
2721 // ***************************************************************************
2722 void CMesh::computeBonesId (CSkeletonModel
*skeleton
)
2724 nlassert (_MeshGeom
);
2725 _MeshGeom
->computeBonesId (skeleton
);
2729 // ***************************************************************************
2730 void CMesh::updateSkeletonUsage(CSkeletonModel
*sm
, bool increment
)
2732 nlassert (_MeshGeom
);
2733 _MeshGeom
->updateSkeletonUsage(sm
, increment
);
2736 // ***************************************************************************
2737 IMeshGeom
*CMesh::supportMeshBlockRendering (CTransformShape
*trans
, float &polygonCount
) const
2739 // Ok if meshGeom is ok.
2740 if(_MeshGeom
->supportMeshBlockRendering())
2750 // ***************************************************************************
2751 void CMesh::profileSceneRender(CRenderTrav
*rdrTrav
, CTransformShape
*trans
, bool passOpaque
)
2754 uint32 mask
= (0-(uint32
)passOpaque
);
2756 // select rdrFlags, without ifs.
2757 rdrFlags
= mask
& (IMeshGeom::RenderOpaqueMaterial
| IMeshGeom::RenderPassOpaque
);
2758 rdrFlags
|= ~mask
& (IMeshGeom::RenderTransparentMaterial
);
2760 _MeshGeom
->profileSceneRender(rdrTrav
, trans
, 0, rdrFlags
);
2763 // ***************************************************************************
2764 void CMesh::compileRunTime()
2766 /* ***********************************************
2767 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
2768 * It can be loaded/called through CAsyncFileManager for instance
2769 * ***********************************************/
2771 // **** try to build a Visual Collision Mesh
2773 if(_VisualCollisionMesh
)
2775 delete _VisualCollisionMesh
;
2776 _VisualCollisionMesh
= NULL
;
2778 // build only if wanted
2779 if( (_CollisionMeshGeneration
==AutoCameraCol
&& !_LightInfos
.empty()) ||
2780 _CollisionMeshGeneration
==ForceCameraCol
)
2783 vector
<CVector
> vertices
;
2784 vector
<uint32
> indices
;
2785 if(_MeshGeom
->retrieveVertices(vertices
) && _MeshGeom
->retrieveTriangles(indices
))
2788 _VisualCollisionMesh
= new CVisualCollisionMesh
;
2789 // if fails to build cause of too many vertices/indices for instance
2790 if( !_VisualCollisionMesh
->build(vertices
, indices
,const_cast<CVertexBuffer
&>(_MeshGeom
->getVertexBuffer())) )
2793 delete _VisualCollisionMesh
;
2794 _VisualCollisionMesh
= NULL
;
2800 // ***************************************************************************
2801 void CMesh::buildSystemGeometry()
2804 _SystemGeometry
.clear();
2806 // don't build a system copy if skinned. In this case, ray intersection is done through CSkeletonModel
2807 // and intersectSkin() scheme
2808 if(_MeshGeom
->isSkinned())
2811 // retrieve geometry (if VB/IB not resident)
2812 if( !_MeshGeom
->retrieveVertices(_SystemGeometry
.Vertices
) ||
2813 !_MeshGeom
->retrieveTriangles(_SystemGeometry
.Triangles
))
2815 _SystemGeometry
.clear();
2819 /*static uint32 totalMem= 0;
2820 totalMem+= _SystemGeometry.Vertices.size()*sizeof(CVector);
2821 totalMem+= _SystemGeometry.Triangles.size()*sizeof(uint32);
2822 nlinfo("CMesh: TotalMem: %d", totalMem);*/