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/misc/hierarchical_timer.h"
20 #include "nel/misc/polygon.h"
21 #include "nel/3d/shadow_poly_receiver.h"
22 #include "nel/3d/shadow_map.h"
23 #include "nel/3d/driver.h"
24 #include "nel/3d/camera_col.h"
28 using namespace NLMISC
;
37 // ***************************************************************************
38 CShadowPolyReceiver::CShadowPolyReceiver(uint quadGridSize
, float quadGridCellSize
)
40 _Vertices
.reserve(64);
41 _FreeVertices
.reserve(64);
42 _FreeTriangles
.reserve(64);
43 _Triangles
.reserve(64);
45 _TriangleGrid
.create(quadGridSize
, quadGridCellSize
);
47 _VB
.setVertexFormat(CVertexBuffer::PositionFlag
);
48 _VB
.setName("CShadowPolyReceiver");
49 // lock volatile, to avoid cpu stall when rendering multiple shadows in the same polyReceiver
50 _VB
.setPreferredMemory(CVertexBuffer::RAMVolatile
, false);
51 _RenderTriangles
.setPreferredMemory(CIndexBuffer::RAMVolatile
, false);
52 _RenderTriangles
.setFormat(NL_DEFAULT_INDEX_BUFFER_FORMAT
);
53 NL_SET_IB_NAME(_RenderTriangles
, "CShadowPolyReceiver");
57 // ***************************************************************************
58 uint
CShadowPolyReceiver::addTriangle(const NLMISC::CTriangle
&tri
)
62 // Look for a free triangle entry.
63 if(_FreeTriangles
.empty())
65 _Triangles
.push_back(TTriangleGrid::CIterator());
66 id
= (uint
)_Triangles
.size()-1;
67 // enlarge render size.
68 _RenderTriangles
.setNumIndexes((uint32
)_Triangles
.size() * 3);
72 id
= _FreeTriangles
.back();
73 _FreeTriangles
.pop_back();
76 // Allocate vertices, reusing same ones.
84 // Find the vertex in the map.
85 TVertexMap::iterator it
;
86 it
= _VertexMap
.find(v
[i
]);
87 // if not found, allocate it
88 if(it
==_VertexMap
.end())
90 triId
.Vertex
[i
]= allocateVertex(v
[i
]);
95 triId
.Vertex
[i
]= it
->second
;
98 // increment the reference of this vertex
99 incVertexRefCount(triId
.Vertex
[i
]);
102 // Insert the triangle in the quadGrid.
104 bb
.setCenter(tri
.V0
);
107 // insert in QuadGrid and store iterator for future remove
108 _Triangles
[id
]= _TriangleGrid
.insert(bb
.getMin(), bb
.getMax(), triId
);
113 // ***************************************************************************
114 void CShadowPolyReceiver::removeTriangle(uint id
)
116 nlassert(id
<_Triangles
.size());
117 // Must not be NULL iterator.
118 nlassert(_Triangles
[id
]!=_TriangleGrid
.end());
121 const CTriangleId
&triId
= *_Triangles
[id
];
122 releaseVertex(triId
.Vertex
[0]);
123 releaseVertex(triId
.Vertex
[1]);
124 releaseVertex(triId
.Vertex
[2]);
127 _TriangleGrid
.erase(_Triangles
[id
]);
128 _Triangles
[id
]= _TriangleGrid
.end();
129 // Append to free list.
130 _FreeTriangles
.push_back(id
);
134 // ***************************************************************************
135 uint
CShadowPolyReceiver::allocateVertex(const CVector
&v
)
139 // if not valid double, will crash cause map<float,...> crash when float are not valid
140 nlassert(isValidDouble(v
.x
) && isValidDouble(v
.y
) && isValidDouble(v
.z
));
142 // Look for a free vertex entry.
143 if(_FreeVertices
.empty())
145 // Add the vertex, and init refCount to 0.
146 _Vertices
.push_back(v
);
147 id
= (uint
)_Vertices
.size()-1;
149 // Resize the VBuffer at max possible
150 _VB
.setNumVertices((uint32
)_Vertices
.size());
154 id
= _FreeVertices
.back();
155 _FreeVertices
.pop_back();
158 _Vertices
[id
].RefCount
= 0;
161 // insert in the map (should not be here)
162 _VertexMap
.insert( make_pair(v
, id
) );
167 // ***************************************************************************
168 void CShadowPolyReceiver::releaseVertex(uint id
)
170 nlassert(id
<_Vertices
.size());
172 nlassert(_Vertices
[id
].RefCount
>0);
173 _Vertices
[id
].RefCount
--;
175 if(_Vertices
[id
].RefCount
==0)
178 _FreeVertices
.push_back(id
);
179 // Remove it from map.
180 TVertexMap::iterator it
= _VertexMap
.find(_Vertices
[id
]);
181 if (it
!=_VertexMap
.end())
182 _VertexMap
.erase(it
);
184 nlwarning("vertex %u doesn't exist in _VertexMap, this should not happen", id
);
188 // ***************************************************************************
189 void CShadowPolyReceiver::incVertexRefCount(uint id
)
191 nlassert(id
<_Vertices
.size());
192 nlassert(_Vertices
[id
].RefCount
< NL3D_SPR_MAX_REF_COUNT
);
193 _Vertices
[id
].RefCount
++;
198 // ***************************************************************************
199 inline void CShadowPolyReceiver::renderSelection(IDriver
*drv
, CMaterial
&shadowMat
, const CShadowMap
*shadowMap
, const CVector
&casterPos
, const CVector
&vertDelta
)
202 // For all triangles, reset vertices flags.
203 TTriangleGrid::CIterator it
;
204 for(it
=_TriangleGrid
.begin();it
!=_TriangleGrid
.end();it
++)
206 CTriangleId
&triId
= *it
;
209 _Vertices
[triId
.Vertex
[i
]].Flags
= 0;
210 _Vertices
[triId
.Vertex
[i
]].VBIdx
= -1;
214 // Compute the world Clip Volume
215 static std::vector
<CPlane
> worldClipPlanes
;
217 // set -casterPos, because to transform a plane, we must do plane * M-1
218 worldMat
.setPos(-casterPos
);
219 // Allow max bits of planes clip.
220 worldClipPlanes
.resize(min((uint
)shadowMap
->LocalClipPlanes
.size(), (uint
)NL3D_SPR_NUM_CLIP_PLANE
));
221 // Transform into world
222 for(i
=0;i
<worldClipPlanes
.size();i
++)
224 worldClipPlanes
[i
]= shadowMap
->LocalClipPlanes
[i
] * worldMat
;
227 uint currentTriIdx
= 0;
229 // Volatile: must resize before lock
230 _VB
.setNumVertices((uint32
)_Vertices
.size());
231 _RenderTriangles
.setNumIndexes((uint32
)_Triangles
.size() * 3);
233 // lock volatile, to avoid cpu stall
234 CVertexBufferReadWrite vba
;
236 CIndexBufferReadWrite iba
;
237 _RenderTriangles
.lock (iba
);
238 TIndexType
*triPtr
= (TIndexType
*) iba
.getPtr();
240 // For All triangles, clip them.
241 uint currentVbIdx
= 0;
242 for(it
=_TriangleGrid
.begin();it
!=_TriangleGrid
.end();it
++)
244 CTriangleId
&triId
= *it
;
245 uint triFlag
= NL3D_SPR_NUM_CLIP_PLANE_MASK
;
247 // for all vertices, clip them
250 uint vid
= triId
.Vertex
[i
];
251 uint vertexFlags
= _Vertices
[vid
].Flags
;
253 // if this vertex is still not computed
256 // For all planes of the Clip Volume, clip this vertex.
257 for(j
=0;j
<worldClipPlanes
.size();j
++)
260 bool out
= worldClipPlanes
[j
]*_Vertices
[vid
] > 0;
262 vertexFlags
|= ((uint
)out
)<<j
;
265 // add the bit flag to say "computed".
266 vertexFlags
|= NL3D_SPR_NUM_CLIP_PLANE_SHIFT
;
269 _Vertices
[vid
].Flags
= vertexFlags
;
272 // And all vertex bits.
273 triFlag
&= vertexFlags
;
276 // if triangle not clipped, add the triangle
277 if( (triFlag
& NL3D_SPR_NUM_CLIP_PLANE_MASK
)==0 )
279 // Add the 3 vertices to the VB, and to the index buffer.
282 uint vid
= triId
.Vertex
[i
];
283 sint vbId
= _Vertices
[vid
].VBIdx
;
285 // if not yet inserted in the VB, do it.
288 // allocate a new place in the VBuffer
289 vbId
= currentVbIdx
++;
290 _Vertices
[vid
].VBIdx
= vbId
;
292 vba
.setVertexCoord(vbId
, _Vertices
[vid
]+vertDelta
);
295 // add the index to the tri list.
296 triPtr
[currentTriIdx
++]= (TIndexType
) vbId
;
304 drv
->activeVertexBuffer(_VB
);
305 drv
->activeIndexBuffer(_RenderTriangles
);
306 drv
->renderTriangles(shadowMat
, 0, currentTriIdx
/3);
307 // TestYoyo. Show in Red triangles selected
308 /*static CMaterial tam;
310 tam.setColor(CRGBA(255,0,0,128));
311 tam.setZFunc(CMaterial::always);
312 tam.setZWrite(false);
314 tam.setBlendFunc(CMaterial::srcalpha, CMaterial::invsrcalpha);
315 tam.setDoubleSided(true);
316 drv->renderTriangles(tam, &_RenderTriangles[0], currentTriIdx/3);*/
319 // ***************************************************************************
320 void CShadowPolyReceiver::computeClippedTrisWithPolyClip(const CShadowMap
*shadowMap
, const CVector
&casterPos
, const CVector
&vertDelta
, const NLMISC::CPolygon2D
&poly
, std::vector
<CRGBAVertex
> &destTris
, bool colorUpfacingVertices
)
322 nlctassert(sizeof(CRGBAVertex
) == 12 + 4); // ensure padding works as expected
325 if (_TriangleGrid
.begin() == _TriangleGrid
.end()) return;
329 static std::vector
<CVector
> vertexNormals
; // normal for each vertex
330 static std::vector
<uint8
> vertexNormalsUndefined
; // normal for each vertex
331 static std::vector
<CTriangleId
*> visibleTris
; // triangles that passed the clip
332 vertexNormals
.resize(_Vertices
.size());
333 vertexNormalsUndefined
.resize(_Vertices
.size(), 1);
336 // For all triangles, reset vertices flags.
337 TTriangleGrid::CIterator it
;
338 for(it
=_TriangleGrid
.begin();it
!=_TriangleGrid
.end();it
++)
340 CTriangleId
&triId
= *it
;
343 _Vertices
[triId
.Vertex
[i
]].Flags
= 0;
347 // Compute the world Clip Volume
348 static std::vector
<CPlane
> worldClipPlanes
;
350 // set -casterPos, because to transform a plane, we must do plane * M-1
351 worldMat
.setPos(-casterPos
);
352 // Allow max bits of planes clip.
353 worldClipPlanes
.resize(min((uint
)shadowMap
->LocalClipPlanes
.size(), (uint
)NL3D_SPR_NUM_CLIP_PLANE
));
354 // Transform into world
355 for(i
=0;i
<worldClipPlanes
.size();i
++)
357 worldClipPlanes
[i
]= shadowMap
->LocalClipPlanes
[i
] * worldMat
;
360 static NLMISC::CPolygon clippedTri
;
365 // For All triangles, clip them.
366 for(it
=_TriangleGrid
.begin();it
!=_TriangleGrid
.end();it
++)
368 CTriangleId
&triId
= *it
;
369 uint triFlag
= NL3D_SPR_NUM_CLIP_PLANE_MASK
;
372 CVectorId
*vid
[3] = { &_Vertices
[triId
.Vertex
[0]],
373 &_Vertices
[triId
.Vertex
[1]],
374 &_Vertices
[triId
.Vertex
[2]]
377 // for all vertices, clip them
381 // if this vertex is still not computed
384 // For all planes of the Clip Volume, clip this vertex.
385 for(j
=0;j
<worldClipPlanes
.size();j
++)
388 bool out
= worldClipPlanes
[j
]* *vid
[i
] > 0;
390 vid
[i
]->Flags
|= ((uint
)out
)<<j
;
392 // add the bit flag to say "computed".
393 vid
[i
]->Flags
|= NL3D_SPR_NUM_CLIP_PLANE_SHIFT
;
396 vertexNormalsUndefined
[triId
.Vertex
[i
]] = 1; // invalidate normal for next pass
398 // And all vertex bits.
399 triFlag
&= vid
[i
]->Flags
;
401 // if triangle not clipped, do finer clip then add resulting triangles
402 if( (triFlag
& NL3D_SPR_NUM_CLIP_PLANE_MASK
)==0 )
404 visibleTris
.push_back(&triId
);
408 uint numVisibleTris
= (uint
)visibleTris
.size();
409 // compute normals if needed
410 if (colorUpfacingVertices
)
412 for (uint triIndex
= 0; triIndex
< numVisibleTris
; ++triIndex
)
414 CTriangleId
&triId
= *visibleTris
[triIndex
];
415 CVectorId
*vid
[3] = { &_Vertices
[triId
.Vertex
[0]],
416 &_Vertices
[triId
.Vertex
[1]],
417 &_Vertices
[triId
.Vertex
[2]]
419 // compute normal for this tri
420 triNormal
= ((*vid
[1] - *vid
[0]) ^ (*vid
[2] - *vid
[0])).normed();
422 // for all vertices, clip them
425 sint vertIndex
= triId
.Vertex
[i
];
426 if (vertexNormalsUndefined
[vertIndex
])
428 vertexNormals
[vertIndex
] = triNormal
;
429 vertexNormalsUndefined
[vertIndex
] = 0;
433 vertexNormals
[vertIndex
] += triNormal
;
440 if (colorUpfacingVertices
)
442 for (uint triIndex
= 0; triIndex
< numVisibleTris
; ++triIndex
)
444 CTriangleId
&triId
= *visibleTris
[triIndex
];
445 // use CPlane 'uv cliping', store 'color' in 'U'
446 static std::vector
<CVector
> corner0
;
447 static std::vector
<CVector
> corner1
;
448 static std::vector
<CUV
> uv0
;
449 static std::vector
<CUV
> uv1
;
450 uv0
.resize(3 + worldClipPlanes
.size());
451 uv1
.resize(3 + worldClipPlanes
.size());
452 corner0
.resize(3 + worldClipPlanes
.size());
453 corner1
.resize(3 + worldClipPlanes
.size());
455 corner0
[0] = _Vertices
[triId
.Vertex
[0]];
456 corner0
[1] = _Vertices
[triId
.Vertex
[1]];
457 corner0
[2] = _Vertices
[triId
.Vertex
[2]];
459 uv0
[0].set(vertexNormals
[triId
.Vertex
[0]].z
>= 0.f
? 1.f
: 0.f
, 0.f
);
460 uv0
[1].set(vertexNormals
[triId
.Vertex
[1]].z
>= 0.f
? 1.f
: 0.f
, 0.f
);
461 uv0
[2].set(vertexNormals
[triId
.Vertex
[2]].z
>= 0.f
? 1.f
: 0.f
, 0.f
);
465 for (uint k
= 0; k
< worldClipPlanes
.size(); ++k
)
467 numVerts
= worldClipPlanes
[k
].clipPolygonBack(&corner0
[0], &uv0
[0], &corner1
[0], &uv1
[0], numVerts
);
468 nlassert(numVerts
<= (sint
) corner1
.size());
469 if (numVerts
== 0) break;
471 corner0
.swap(corner1
);
473 for (sint k
= 0; k
< numVerts
- 2; ++k
)
477 (uint8
) (255.f
* uv0
[0].U
),
478 (uint8
) (255.f
* uv0
[k
+ 1].U
),
479 (uint8
) (255.f
* uv0
[k
+ 2].U
)
481 if (alpha
[0] != 0 || alpha
[1] != 0 || alpha
[2] != 0)
483 destTris
.push_back(CRGBAVertex(corner0
[0] + vertDelta
, CRGBA(255, 255, 255, alpha
[0])));
484 destTris
.push_back(CRGBAVertex(corner0
[k
+ 1] + vertDelta
, CRGBA(255, 255, 255, alpha
[1])));
485 destTris
.push_back(CRGBAVertex(corner0
[k
+ 2] + vertDelta
, CRGBA(255, 255, 255, alpha
[2])));
492 for (uint triIndex
= 0; triIndex
< numVisibleTris
; ++triIndex
)
494 CTriangleId
&triId
= *visibleTris
[triIndex
];
495 clippedTri
.Vertices
.resize(3);
496 clippedTri
.Vertices
[0] = _Vertices
[triId
.Vertex
[0]];
497 clippedTri
.Vertices
[1] = _Vertices
[triId
.Vertex
[1]];
498 clippedTri
.Vertices
[2] = _Vertices
[triId
.Vertex
[2]];
499 clippedTri
.clip(worldClipPlanes
);
500 if (clippedTri
.Vertices
.size() >= 3)
502 for(uint k
= 0; k
< clippedTri
.Vertices
.size() - 2; ++k
)
504 destTris
.push_back(CRGBAVertex(clippedTri
.Vertices
[0] + vertDelta
, CRGBA::White
));
505 destTris
.push_back(CRGBAVertex(clippedTri
.Vertices
[k
+ 1] + vertDelta
, CRGBA::White
));
506 destTris
.push_back(CRGBAVertex(clippedTri
.Vertices
[k
+ 2] + vertDelta
, CRGBA::White
));
514 // ***************************************************************************
515 void CShadowPolyReceiver::render(IDriver
*drv
, CMaterial
&shadowMat
, const CShadowMap
*shadowMap
, const CVector
&casterPos
, const CVector
&vertDelta
)
518 // **** Fill Triangles that are hit by the Caster
519 // First select with quadGrid
521 worldBB
= shadowMap
->LocalBoundingBox
;
522 worldBB
.setCenter(worldBB
.getCenter() + casterPos
);
523 _TriangleGrid
.select(worldBB
.getMin(), worldBB
.getMax());
524 if (_TriangleGrid
.begin() == _TriangleGrid
.end()) return;
525 renderSelection(drv
, shadowMat
, shadowMap
, casterPos
, vertDelta
);
529 // ***************************************************************************
530 void CShadowPolyReceiver::selectPolygon(const NLMISC::CPolygon2D
&poly
)
532 static TTriangleGrid::TSelectionShape selectionShape
;
533 _TriangleGrid
.buildSelectionShape(selectionShape
, poly
);
534 _TriangleGrid
.select(selectionShape
);
537 // ***************************************************************************
538 void CShadowPolyReceiver::renderWithPolyClip(IDriver
*drv
, CMaterial
&shadowMat
, const CShadowMap
*shadowMap
, const CVector
&casterPos
, const CVector
&vertDelta
, const NLMISC::CPolygon2D
&poly
)
542 renderSelection(drv
, shadowMat
, shadowMap
, casterPos
, vertDelta
);
545 // ***************************************************************************
546 float CShadowPolyReceiver::getCameraCollision(const CVector
&start
, const CVector
&end
, TCameraColTest testType
, float radius
)
548 // **** build the camera collision info
550 if(testType
==CameraColSimpleRay
)
551 camCol
.buildRay(start
, end
);
553 camCol
.build(start
, end
, radius
, testType
==CameraColCone
);
555 // select with quadGrid
556 if(testType
==CameraColSimpleRay
)
558 _TriangleGrid
.selectRay(start
, end
);
562 _TriangleGrid
.select(camCol
.getBBox().getMin(), camCol
.getBBox().getMax());
565 // **** For all triangles, test if intersect the camera collision
566 TTriangleGrid::CIterator it
;
567 float sqrMinDist
= FLT_MAX
;
568 for(it
=_TriangleGrid
.begin();it
!=_TriangleGrid
.end();it
++)
570 CTriangleId
&triId
= *it
;
571 camCol
.minimizeDistanceAgainstTri(
572 _Vertices
[triId
.Vertex
[0]],
573 _Vertices
[triId
.Vertex
[1]],
574 _Vertices
[triId
.Vertex
[2]],
578 // **** return the collision found, between [0,1]
579 if(sqrMinDist
== FLT_MAX
)
584 float d
= camCol
.getRayLen();
587 f
= sqrtf(sqrMinDist
) / d
;