1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2012 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
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/>.
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"
30 using namespace NLMISC
;
36 // TestYoyo. external debug flag
37 bool TESTYOYO_VCM_RedShadow
= false;
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 // ***************************************************************************
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
));
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
));
75 _EltBuild
.resize(nbElts
);
81 _GridPos
= gridBBox
.getMin();
82 _GridScale
= gridBBox
.getSize();
83 _GridScale
.x
= _GridSize
/ _GridScale
.x
;
84 _GridScale
.y
= _GridSize
/ _GridScale
.y
;
86 // reset intersection data
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
++;
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 * ***********************************************/
139 _GridData
.resize(_GridDataSize
);
141 // Init Start ptr for each case
143 for(i
=0;i
<_Grid
.size();i
++)
146 idx
+= _Grid
[i
].NumElts
;
147 // reset NumElts, because use it like an index below
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
;
165 // increment the number of elements for this case
171 // create the temp array used for intersection test
172 _Sessions
.resize(_EltBuild
.size());
175 // clear no more needed data
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
189 // enlarge the result array as needed
190 if(res
.size()<_Sessions
.size())
191 res
.resize(_Sessions
.size());
192 // the number of selected element
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
;
230 // return the number of selected elements
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 * ***********************************************/
261 // if no vertices, or no triangles, abort
264 if(triangles
.empty())
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)
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
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
);
288 for(i
=0;i
<numTris
;i
++)
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
);
300 // Keep a RefPtr on the AGP vertex Buffer for shadow receiving
301 _VertexBuffer
= &vbForShadowRender
;
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());
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]],
332 // **** return the collision found, between [0,1]
333 if(sqrMinDist
== FLT_MAX
)
338 float d
= camColLocal
.getRayLen();
341 f
= sqrtf(sqrMinDist
) / d
;
348 // ***************************************************************************
349 NLMISC::CAABBox
CVisualCollisionMesh::computeWorldBBox(const CMatrix
&instanceMatrix
)
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
]);
365 // ***************************************************************************
366 void CVisualCollisionMesh::receiveShadowMap(const NLMISC::CMatrix
&instanceMatrix
, const CShadowContext
&shadowContext
)
368 // empty mesh => no op
369 if(_Vertices
.empty())
372 // The VertexBuffer RefPtr has been released? quit
373 if(_VertexBuffer
== NULL
)
377 // **** Select triangles to be rendered with quadGrid
378 // select with quadGrid local in mesh
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)
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());
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
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
++)
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
++)
443 uint vf
= vertexFlags
[vid
];
445 // if this vertex is still not computed
448 // For all planes of the Clip Volume, clip this vertex.
449 for(uint j
=0;j
<localClipPlanes
.size();j
++)
452 bool out
= localClipPlanes
[j
]*_Vertices
[vid
] > 0;
457 // add the bit flag to say "computed".
458 vf
|= NL3D_VCM_SHADOW_NUM_CLIP_PLANE_SHIFT
;
461 vertexFlags
[vid
]= vf
;
464 // And all vertex bits.
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];
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
++)
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
++)
495 uint vf
= vertexFlags
[vid
];
497 // if this vertex is still not computed
500 // For all planes of the Clip Volume, clip this vertex.
501 for(uint j
=0;j
<localClipPlanes
.size();j
++)
504 bool out
= localClipPlanes
[j
]*_Vertices
[vid
] > 0;
509 // add the bit flag to say "computed".
510 vf
|= NL3D_VCM_SHADOW_NUM_CLIP_PLANE_SHIFT
;
513 vertexFlags
[vid
]= vf
;
516 // And all vertex bits.
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];
535 // if some triangle to render
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
);
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;
552 tam.setColor(CRGBA(255,0,0,128));
553 tam.setZFunc(CMaterial::always);
554 tam.setZWrite(false);
556 tam.setBlendFunc(CMaterial::srcalpha, CMaterial::invsrcalpha);
557 tam.setDoubleSided(true);
558 drv->renderTriangles(tam, 0, currentTriIdx/3);