Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / 3d / visual_collision_mesh.cpp
blobd3bd95b93b9a9f8eefd185822e7be88ad7d3bb67
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) 2012 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
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/3d/visual_collision_mesh.h"
23 #include "nel/3d/quad_grid.h"
24 #include "nel/3d/camera_col.h"
25 #include "nel/3d/driver.h"
26 #include "nel/3d/shadow_map.h"
29 using namespace std;
30 using namespace NLMISC;
32 #ifdef DEBUG_NEW
33 #define new DEBUG_NEW
34 #endif
36 // TestYoyo. external debug flag
37 bool TESTYOYO_VCM_RedShadow= false;
40 namespace NL3D
44 // ***************************************************************************
45 #define NL3D_VCM_SHADOW_NUM_CLIP_PLANE 7
46 #define NL3D_VCM_SHADOW_NUM_CLIP_PLANE_SHIFT (1<<NL3D_VCM_SHADOW_NUM_CLIP_PLANE)
47 #define NL3D_VCM_SHADOW_NUM_CLIP_PLANE_MASK (NL3D_VCM_SHADOW_NUM_CLIP_PLANE_SHIFT-1)
50 // ***************************************************************************
51 // ***************************************************************************
52 // CStaticGrid
53 // ***************************************************************************
54 // ***************************************************************************
57 // ***************************************************************************
58 void CVisualCollisionMesh::CStaticGrid::create(uint nbQuads, uint nbElts, const NLMISC::CAABBox &gridBBox)
60 /* ***********************************************
61 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
62 * It can be loaded/called through CAsyncFileManager for instance
63 * ***********************************************/
65 nlassert(nbQuads>0 && isPowerOf2(nbQuads));
67 // init the grid
68 _GridSize= nbQuads;
69 _GridSizePower= getPowerOf2(nbQuads);
70 _Grid.resize(_GridSize*_GridSize);
71 // start with 0 elt in each case
72 memset(_Grid.getPtr(), 0, _Grid.size() * sizeof(CCase));
74 // init the Elt Build
75 _EltBuild.resize(nbElts);
77 // total size is 0
78 _GridDataSize= 0;
80 // bbox init
81 _GridPos= gridBBox.getMin();
82 _GridScale= gridBBox.getSize();
83 _GridScale.x= _GridSize / _GridScale.x;
84 _GridScale.y= _GridSize / _GridScale.y;
86 // reset intersection data
87 _ItSession= 0;
90 // ***************************************************************************
91 void CVisualCollisionMesh::CStaticGrid::add(uint16 id, const NLMISC::CAABBox &bbox)
93 /* ***********************************************
94 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
95 * It can be loaded/called through CAsyncFileManager for instance
96 * ***********************************************/
98 CVector minp= bbox.getMin() - _GridPos;
99 CVector maxp= bbox.getMax() - _GridPos;
101 // compute the 2D bbox
102 sint xmin= (sint)floorf(minp.x*_GridScale.x);
103 sint ymin= (sint)floorf(minp.y*_GridScale.y);
104 sint xmax= (sint)ceilf(maxp.x*_GridScale.x);
105 sint ymax= (sint)ceilf(maxp.y*_GridScale.y);
106 clamp(xmin, 0, (sint)_GridSize-1);
107 clamp(ymin, 0, (sint)_GridSize-1);
108 clamp(xmax, xmin+1, (sint)_GridSize);
109 clamp(ymax, ymin+1, (sint)_GridSize);
111 // set in the elt build
112 _EltBuild[id].X0= xmin;
113 _EltBuild[id].Y0= ymin;
114 _EltBuild[id].X1= xmax;
115 _EltBuild[id].Y1= ymax;
117 // for each case touched, increment NumElts
118 for(uint y=ymin;y<(uint)ymax;y++)
120 for(uint x=xmin;x<(uint)xmax;x++)
122 _Grid[(y<<_GridSizePower)+x].NumElts++;
123 _GridDataSize++;
128 // ***************************************************************************
129 void CVisualCollisionMesh::CStaticGrid::compile()
131 /* ***********************************************
132 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
133 * It can be loaded/called through CAsyncFileManager for instance
134 * ***********************************************/
136 uint i;
138 // create the data
139 _GridData.resize(_GridDataSize);
141 // Init Start ptr for each case
142 uint idx= 0;
143 for(i=0;i<_Grid.size();i++)
145 _Grid[i].Start= idx;
146 idx+= _Grid[i].NumElts;
147 // reset NumElts, because use it like an index below
148 _Grid[i].NumElts= 0;
150 nlassert(_GridDataSize==idx);
152 // For each element, fill the grid and grid data
153 for(i=0;i<_EltBuild.size();i++)
155 CEltBuild &eb= _EltBuild[i];
157 for(uint y=eb.Y0;y<eb.Y1;y++)
159 for(uint x=eb.X0;x<eb.X1;x++)
161 CCase &gcase= _Grid[(y<<_GridSizePower)+x];
162 uint idx= gcase.Start + gcase.NumElts;
163 // store the idx
164 _GridData[idx]= i;
165 // increment the number of elements for this case
166 gcase.NumElts++;
171 // create the temp array used for intersection test
172 _Sessions.resize(_EltBuild.size());
173 _Sessions.fill(0);
175 // clear no more needed data
176 _EltBuild.clear();
179 // ***************************************************************************
180 uint CVisualCollisionMesh::CStaticGrid::select(const NLMISC::CAABBox &bbox, std::vector<uint16> &res)
182 /* ***********************************************
183 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
184 * It can be loaded/called through CAsyncFileManager for instance
185 * ***********************************************/
187 // increment the intersection session
188 _ItSession++;
189 // enlarge the result array as needed
190 if(res.size()<_Sessions.size())
191 res.resize(_Sessions.size());
192 // the number of selected element
193 uint numSel= 0;
195 // compute the 2D bbox
196 CVector minp= bbox.getMin() - _GridPos;
197 CVector maxp= bbox.getMax() - _GridPos;
198 sint xmin= (sint)floorf(minp.x*_GridScale.x);
199 sint ymin= (sint)floorf(minp.y*_GridScale.y);
200 sint xmax= (sint)ceilf(maxp.x*_GridScale.x);
201 sint ymax= (sint)ceilf(maxp.y*_GridScale.y);
202 clamp(xmin, 0, (sint)_GridSize-1);
203 clamp(ymin, 0, (sint)_GridSize-1);
204 clamp(xmax, xmin+1, (sint)_GridSize);
205 clamp(ymax, ymin+1, (sint)_GridSize);
207 // for each case touched, increment NumElts
208 for(uint y=ymin;y<(uint)ymax;y++)
210 for(uint x=xmin;x<(uint)xmax;x++)
212 CCase &gcase= _Grid[(y<<_GridSizePower)+x];
213 // for each element in this case
214 for(uint i= gcase.Start;i<gcase.Start + gcase.NumElts;i++)
216 uint elt= _GridData[i];
218 // if not alread inserted in the dest
219 if(_Sessions[elt]!=_ItSession)
221 // mark as intersected
222 _Sessions[elt]= _ItSession;
223 // append
224 res[numSel++]= elt;
230 // return the number of selected elements
231 return numSel;
235 // ***************************************************************************
236 // ***************************************************************************
237 // CVisualCollisionMesh
238 // ***************************************************************************
239 // ***************************************************************************
242 // ***************************************************************************
243 CVisualCollisionMesh::CVisualCollisionMesh()
245 /* ***********************************************
246 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
247 * It can be loaded/called through CAsyncFileManager for instance
248 * ***********************************************/
252 // ***************************************************************************
253 bool CVisualCollisionMesh::build(const std::vector<CVector> &vertices, const std::vector<uint32> &triangles, CVertexBuffer &vbForShadowRender)
255 /* ***********************************************
256 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
257 * It can be loaded/called through CAsyncFileManager for instance
258 * ***********************************************/
260 uint i;
261 // if no vertices, or no triangles, abort
262 if(vertices.empty())
263 return false;
264 if(triangles.empty())
265 return false;
266 // vertices and triangles id are stored in uint16 form. so their should not be more than 65535*3 indices
267 if(vertices.size()>65535 || triangles.size()>65535*3)
268 return false;
270 // copy
271 _Vertices= vertices;
273 // compress indexes to 16 bits
274 _Triangles.resize(triangles.size());
275 for(i=0;i<_Triangles.size();i++)
276 _Triangles[i]= (uint16)triangles[i];
278 // Build the Local bbox for this col mesh
279 CAABBox localBBox;
280 localBBox.setCenter(vertices[0]);
281 for(i=1;i<vertices.size();i++)
282 localBBox.extend(vertices[i]);
284 // Build the Static Grid
285 uint numTris= (uint)triangles.size()/3;
286 _QuadGrid.create(16, numTris, localBBox);
287 // Add all triangles
288 for(i=0;i<numTris;i++)
290 CAABBox bb;
291 bb.setCenter(_Vertices[_Triangles[i*3+0]]);
292 bb.extend(_Vertices[_Triangles[i*3+1]]);
293 bb.extend(_Vertices[_Triangles[i*3+2]]);
294 _QuadGrid.add(i, bb);
296 // compile
297 _QuadGrid.compile();
300 // Keep a RefPtr on the AGP vertex Buffer for shadow receiving
301 _VertexBuffer= &vbForShadowRender;
304 return true;
307 // ***************************************************************************
308 float CVisualCollisionMesh::getCameraCollision(const CMatrix &instanceMatrix, CCameraCol &camCol)
310 // Make the Camera Collision local to the mesh!
311 CCameraCol camColLocal;
312 camColLocal.setApplyMatrix(camCol, instanceMatrix.inverted());
315 // Select triangles
316 static std::vector<uint16> selection;
317 uint numSel= _QuadGrid.select(camColLocal.getBBox(), selection);
319 // **** For all triangles, test if intersect the camera collision
320 float sqrMinDist= FLT_MAX;
321 for(uint i=0;i<numSel;i++)
323 uint triId= selection[i];
324 // build the triangle
325 camColLocal.minimizeDistanceAgainstTri(
326 _Vertices[_Triangles[triId*3+0]],
327 _Vertices[_Triangles[triId*3+1]],
328 _Vertices[_Triangles[triId*3+2]],
329 sqrMinDist);
332 // **** return the collision found, between [0,1]
333 if(sqrMinDist == FLT_MAX)
334 return 1;
335 else
337 float f= 1;
338 float d= camColLocal.getRayLen();
339 if(d>0)
341 f= sqrtf(sqrMinDist) / d;
342 f= min(f, 1.f);
344 return f;
348 // ***************************************************************************
349 NLMISC::CAABBox CVisualCollisionMesh::computeWorldBBox(const CMatrix &instanceMatrix)
351 CAABBox ret;
352 if(!_Vertices.empty())
354 ret.setCenter(instanceMatrix*_Vertices[0]);
355 for(uint i=1;i<_Vertices.size();i++)
357 ret.extend(instanceMatrix*_Vertices[i]);
361 return ret;
365 // ***************************************************************************
366 void CVisualCollisionMesh::receiveShadowMap(const NLMISC::CMatrix &instanceMatrix, const CShadowContext &shadowContext)
368 // empty mesh => no op
369 if(_Vertices.empty())
370 return;
372 // The VertexBuffer RefPtr has been released? quit
373 if(_VertexBuffer == NULL)
374 return;
377 // **** Select triangles to be rendered with quadGrid
378 // select with quadGrid local in mesh
379 CAABBox localBB;
380 localBB= CAABBox::transformAABBox(instanceMatrix.inverted(), shadowContext.ShadowWorldBB);
381 static std::vector<uint16> triInQuadGrid;
382 uint numTrisInQuadGrid= _QuadGrid.select(localBB, triInQuadGrid);
384 // no intersection at all? quit
385 if(numTrisInQuadGrid==0)
386 return;
389 // **** prepare more precise Clip with shadow pyramid
390 // enlarge temp flag array
391 static std::vector<uint8> vertexFlags;
392 if(vertexFlags.size()<_Vertices.size())
393 vertexFlags.resize(_Vertices.size());
394 // reset all to 0
395 memset(&vertexFlags[0], 0, _Vertices.size()*sizeof(uint8));
397 // Compute the "LocalToInstance" shadow Clip Volume
398 static std::vector<CPlane> localClipPlanes;
399 /* We want to apply to plane this matrix: IM-1 * MCasterPos,
400 where IM=instanceMatrix and MCasterPos= matrix translation of "shadowContext.CasterPos"
401 BUT, since to transform a plane, we must do plane * M-1, then compute this matrix:
402 localMat= MCasterPos-1 * IM
404 CMatrix localMat;
405 localMat.setPos(-shadowContext.CasterPos);
406 localMat*= instanceMatrix;
407 // Allow max bits of planes clip.
408 localClipPlanes.resize(min((uint)shadowContext.ShadowMap->LocalClipPlanes.size(), (uint)NL3D_VCM_SHADOW_NUM_CLIP_PLANE));
409 // Transform into Mesh local space
410 for(uint i=0;i<localClipPlanes.size();i++)
412 localClipPlanes[i]= shadowContext.ShadowMap->LocalClipPlanes[i] * localMat;
416 // **** Clip and fill the triangles
417 uint currentTriIdx= 0;
418 // enlarge the index buffer as max of triangles possibly intersected
419 shadowContext.IndexBuffer.setFormat(NL_DEFAULT_INDEX_BUFFER_FORMAT);
420 if(shadowContext.IndexBuffer.getNumIndexes()<numTrisInQuadGrid*3)
421 shadowContext.IndexBuffer.setNumIndexes(numTrisInQuadGrid*3);
423 // Start to clip and fill
425 CIndexBufferReadWrite iba;
426 shadowContext.IndexBuffer.lock(iba);
427 if (iba.getFormat() == CIndexBuffer::Indices32)
429 uint32 *ibPtr= (uint32 *) iba.getPtr();
430 // for all triangles selected with the quadgrid
431 for(uint triq=0; triq<numTrisInQuadGrid;triq++)
433 uint triId[3];
434 triId[0]= _Triangles[uint(triInQuadGrid[triq])*3+0];
435 triId[1]= _Triangles[uint(triInQuadGrid[triq])*3+1];
436 triId[2]= _Triangles[uint(triInQuadGrid[triq])*3+2];
437 uint triFlag= NL3D_VCM_SHADOW_NUM_CLIP_PLANE_MASK;
439 // for all vertices, clip them
440 for(uint i=0;i<3;i++)
442 uint vid= triId[i];
443 uint vf= vertexFlags[vid];
445 // if this vertex is still not computed
446 if(!vf)
448 // For all planes of the Clip Volume, clip this vertex.
449 for(uint j=0;j<localClipPlanes.size();j++)
451 // out if in front
452 bool out= localClipPlanes[j]*_Vertices[vid] > 0;
454 vf|= ((uint)out)<<j;
457 // add the bit flag to say "computed".
458 vf|= NL3D_VCM_SHADOW_NUM_CLIP_PLANE_SHIFT;
460 // store
461 vertexFlags[vid]= vf;
464 // And all vertex bits.
465 triFlag&= vf;
468 // if triangle not clipped, add the triangle
469 if( (triFlag & NL3D_VCM_SHADOW_NUM_CLIP_PLANE_MASK)==0 )
471 // Add the 3 index to the index buffer.
472 ibPtr[currentTriIdx++]= triId[0];
473 ibPtr[currentTriIdx++]= triId[1];
474 ibPtr[currentTriIdx++]= triId[2];
478 else
480 nlassert(iba.getFormat() == CIndexBuffer::Indices16);
481 uint16 *ibPtr= (uint16 *) iba.getPtr();
482 // for all triangles selected with the quadgrid
483 for(uint triq=0; triq<numTrisInQuadGrid;triq++)
485 uint triId[3];
486 triId[0]= _Triangles[uint(triInQuadGrid[triq])*3+0];
487 triId[1]= _Triangles[uint(triInQuadGrid[triq])*3+1];
488 triId[2]= _Triangles[uint(triInQuadGrid[triq])*3+2];
489 uint triFlag= NL3D_VCM_SHADOW_NUM_CLIP_PLANE_MASK;
491 // for all vertices, clip them
492 for(uint i=0;i<3;i++)
494 uint vid= triId[i];
495 uint vf= vertexFlags[vid];
497 // if this vertex is still not computed
498 if(!vf)
500 // For all planes of the Clip Volume, clip this vertex.
501 for(uint j=0;j<localClipPlanes.size();j++)
503 // out if in front
504 bool out= localClipPlanes[j]*_Vertices[vid] > 0;
506 vf|= ((uint)out)<<j;
509 // add the bit flag to say "computed".
510 vf|= NL3D_VCM_SHADOW_NUM_CLIP_PLANE_SHIFT;
512 // store
513 vertexFlags[vid]= vf;
516 // And all vertex bits.
517 triFlag&= vf;
520 // if triangle not clipped, add the triangle
521 // if( (triFlag & NL3D_VCM_SHADOW_NUM_CLIP_PLANE_MASK)==0 )
522 if (triFlag == 0) // previous line not useful due to init
524 // Add the 3 index to the index buffer.
525 ibPtr[currentTriIdx++]= (uint16) triId[0];
526 ibPtr[currentTriIdx++]= (uint16) triId[1];
527 ibPtr[currentTriIdx++]= (uint16) triId[2];
534 // **** Render
535 // if some triangle to render
536 if(currentTriIdx)
538 IDriver *drv= shadowContext.Driver;
539 // setup the collision instance matrix
540 drv->setupModelMatrix(instanceMatrix);
541 // update the material projection matrix, cause matrix changed
542 shadowContext.ShadowMapProjector.applyToMaterial(instanceMatrix, shadowContext.ShadowMaterial);
543 // render
544 drv->activeVertexBuffer(*_VertexBuffer);
545 drv->activeIndexBuffer(shadowContext.IndexBuffer);
546 drv->renderTriangles(shadowContext.ShadowMaterial, 0, currentTriIdx/3);
547 // TestYoyo. Show in Red triangles selected
548 /*if(TESTYOYO_VCM_RedShadow)
550 static CMaterial tam;
551 tam.initUnlit();
552 tam.setColor(CRGBA(255,0,0,128));
553 tam.setZFunc(CMaterial::always);
554 tam.setZWrite(false);
555 tam.setBlend(true);
556 tam.setBlendFunc(CMaterial::srcalpha, CMaterial::invsrcalpha);
557 tam.setDoubleSided(true);
558 drv->renderTriangles(tam, 0, currentTriIdx/3);
564 } // NL3D