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/visual_collision_manager.h"
20 #include "nel/3d/visual_collision_entity.h"
21 #include "nel/3d/landscape.h"
22 #include "nel/3d/camera_col.h"
23 #include "nel/3d/shadow_map.h"
24 #include "nel/3d/light.h"
25 #include "nel/misc/common.h"
29 using namespace NLMISC
;
39 // ***************************************************************************
40 // Those blocks size are computed to be approximatively one block for 10 entities.
41 const uint TileDescNodeAllocatorBlockSize
= 40000;
42 const uint PatchQuadBlockAllocatorBlockSize
= 160;
44 const uint MeshColQuadGridSize
= 64;
45 const float MeshColQuadGridEltSize
= 20;
48 // ***************************************************************************
49 CVisualCollisionManager::CVisualCollisionManager() :
50 _TileDescNodeAllocator(TileDescNodeAllocatorBlockSize
),
51 _PatchQuadBlockAllocator(PatchQuadBlockAllocatorBlockSize
)
56 setSunContributionPower(0.5f
, 0.5f
);
58 // init the mesh quadGrid
59 _MeshQuadGrid
.create(MeshColQuadGridSize
, MeshColQuadGridEltSize
);
60 // valid id start at 1
64 _ShadowIndexBuffer
.setFormat(NL_DEFAULT_INDEX_BUFFER_FORMAT
);
65 NL_SET_IB_NAME(_ShadowIndexBuffer
, "CVisualCollisionManager");
67 // ***************************************************************************
68 CVisualCollisionManager::~CVisualCollisionManager()
74 // ***************************************************************************
75 void CVisualCollisionManager::setLandscape(CLandscape
*landscape
)
77 _Landscape
= landscape
;
81 // ***************************************************************************
82 CVisualCollisionEntity
*CVisualCollisionManager::createEntity()
84 return new CVisualCollisionEntity(this);
88 // ***************************************************************************
89 void CVisualCollisionManager::deleteEntity(CVisualCollisionEntity
*entity
)
95 // ***************************************************************************
96 CVisualTileDescNode
*CVisualCollisionManager::newVisualTileDescNode()
98 return _TileDescNodeAllocator
.allocate();
101 // ***************************************************************************
102 void CVisualCollisionManager::deleteVisualTileDescNode(CVisualTileDescNode
*ptr
)
104 _TileDescNodeAllocator
.freeBlock(ptr
);
107 // ***************************************************************************
108 CPatchQuadBlock
*CVisualCollisionManager::newPatchQuadBlock()
110 return _PatchQuadBlockAllocator
.allocate();
113 // ***************************************************************************
114 void CVisualCollisionManager::deletePatchQuadBlock(CPatchQuadBlock
*ptr
)
116 _PatchQuadBlockAllocator
.freeBlock(ptr
);
120 // ***************************************************************************
121 void CVisualCollisionManager::setSunContributionPower(float power
, float maxThreshold
)
123 NLMISC::clamp(power
, 0.f
, 1.f
);
125 for(uint i
=0; i
<256; i
++)
128 f
= powf(f
/maxThreshold
, power
);
129 sint uf
= (sint
)floor(255*f
);
130 NLMISC::clamp(uf
, 0, 255);
131 _SunContributionLUT
[i
]= uf
;
136 // ***************************************************************************
137 // ***************************************************************************
139 // ***************************************************************************
140 // ***************************************************************************
143 // ***************************************************************************
144 void CVisualCollisionManager::setPlayerInside(bool state
)
146 _PlayerInside
= state
;
150 // ***************************************************************************
151 float CVisualCollisionManager::getCameraCollision(const CVector
&start
, const CVector
&end
, float radius
, bool cone
)
155 // try col with landscape
158 minCol
= _Landscape
->getCameraCollision(start
, end
, radius
, cone
);
161 // try col with meshes
163 camCol
.build(start
, end
, radius
, cone
);
164 _MeshQuadGrid
.select(camCol
.getBBox().getMin(), camCol
.getBBox().getMax());
165 // try to intersect with any instance meshs
166 CQuadGrid
<CMeshInstanceCol
*>::CIterator it
;
167 for(it
= _MeshQuadGrid
.begin();it
!=_MeshQuadGrid
.end();it
++)
169 // Skip this mesh according to special flag (IBBR problem)
170 if((*it
)->AvoidCollisionWhenPlayerInside
&& _PlayerInside
)
172 if((*it
)->AvoidCollisionWhenPlayerOutside
&& !_PlayerInside
)
176 float meshCol
= (*it
)->getCameraCollision(camCol
);
177 // Keep only yhe smallest value
178 minCol
= min(minCol
, meshCol
);
185 // ***************************************************************************
186 bool CVisualCollisionManager::getRayCollision(const NLMISC::CVector
&start
, const NLMISC::CVector
&end
, bool landscapeOnly
)
188 // try col with landscape
191 if( _Landscape
->getRayCollision(start
, end
) < 1.f
)
195 // try col with meshes, if wanted
199 camCol
.buildRay(start
, end
);
200 _MeshQuadGrid
.select(camCol
.getBBox().getMin(), camCol
.getBBox().getMax());
201 // try to intersect with any instance meshs
202 CQuadGrid
<CMeshInstanceCol
*>::CIterator it
;
203 for(it
= _MeshQuadGrid
.begin();it
!=_MeshQuadGrid
.end();it
++)
205 // Skip this mesh according to special flag (IBBR problem)
206 if((*it
)->AvoidCollisionWhenPlayerInside
&& _PlayerInside
)
208 if((*it
)->AvoidCollisionWhenPlayerOutside
&& !_PlayerInside
)
212 float meshCol
= (*it
)->getCameraCollision(camCol
);
222 // ***************************************************************************
223 float CVisualCollisionManager::CMeshInstanceCol::getCameraCollision(CCameraCol
&camCol
)
225 // if mesh still present (else it s may be an error....)
228 // first test if intersect with the bboxes
229 if(!camCol
.getBBox().intersect(WorldBBox
))
232 // get the collision with the mesh
233 return Mesh
->getCameraCollision(WorldMatrix
, camCol
);
241 // ***************************************************************************
242 uint
CVisualCollisionManager::addMeshInstanceCollision(CVisualCollisionMesh
*mesh
, const CMatrix
&instanceMatrix
, bool avoidCollisionWhenInside
, bool avoidCollisionWhenOutside
)
248 uint32 id
= _MeshIdPool
++;
251 CMeshInstanceCol
&meshInst
= _Meshs
[id
];
253 // Build the col mesh instance
255 meshInst
.WorldMatrix
= instanceMatrix
;
256 meshInst
.WorldBBox
= mesh
->computeWorldBBox(instanceMatrix
);
257 meshInst
.AvoidCollisionWhenPlayerInside
= avoidCollisionWhenInside
;
258 meshInst
.AvoidCollisionWhenPlayerOutside
= avoidCollisionWhenOutside
;
261 // insert in quadGrid
262 meshInst
.QuadGridIt
= _MeshQuadGrid
.insert(meshInst
.WorldBBox
.getMin(), meshInst
.WorldBBox
.getMax(), &meshInst
);
267 // ***************************************************************************
268 void CVisualCollisionManager::removeMeshCollision(uint id
)
271 TMeshColMap::iterator it
= _Meshs
.find(id
);
274 // remove from the quadgrid
275 _MeshQuadGrid
.erase(it
->second
.QuadGridIt
);
276 // remove from the map
282 // ***************************************************************************
283 void CVisualCollisionManager::receiveShadowMap(IDriver
*drv
, CShadowMap
*shadowMap
, const CVector
&casterPos
, CMaterial
&shadowMat
, CShadowMapProjector
&smp
)
285 // Build a shadow context
286 CVisualCollisionMesh::CShadowContext
shadowContext(shadowMat
, _ShadowIndexBuffer
, smp
);
287 shadowContext
.Driver
= drv
;
288 shadowContext
.ShadowMap
= shadowMap
;
289 shadowContext
.CasterPos
= casterPos
;
290 shadowContext
.ShadowWorldBB
= shadowMap
->LocalBoundingBox
;
291 shadowContext
.ShadowWorldBB
.setCenter(shadowContext
.ShadowWorldBB
.getCenter() + casterPos
);
294 CRGBA shadowColor
= shadowContext
.ShadowMaterial
.getColor();
296 // try to intersect with any instance meshs in quadgrid
297 _MeshQuadGrid
.select(shadowContext
.ShadowWorldBB
.getMin(), shadowContext
.ShadowWorldBB
.getMax());
298 CQuadGrid
<CMeshInstanceCol
*>::CIterator it
;
299 bool lightSetupFaked
= false;
300 for(it
= _MeshQuadGrid
.begin();it
!=_MeshQuadGrid
.end();it
++)
302 /* NB: this is a collision flag, but the problem is exactly the same for shadows:
303 If the player is outside, then an "outside entity" can cast shadow only on "outside mesh"
305 // Skip this mesh according to special flag (IBBR problem)
306 if((*it
)->AvoidCollisionWhenPlayerInside
&& _PlayerInside
)
308 if((*it
)->AvoidCollisionWhenPlayerOutside
&& !_PlayerInside
)
312 /* Avoid BackFace Shadowing on CVisualCollisionMesh. To do this simply and smoothly, use a trick:
313 Use a FakeLight: a directional light in reverse direction of the shadow direction
314 The material is now a lighted material, with
315 Emissive= ShadowColor so we get correct dark color, for frontfaces agst shadow direction
316 (which are actually backfaces agst FakeLight)
317 Diffuse= White so shadow is off, for pure backFace agst shadow direction
318 (which are actually frontFaces agst FakeLight)
319 NB: CShadowMapManager::renderProject() has called CRenderTrav::resetLighting() and enableLight(0, true) so we get clean setup here
321 // need to do it only one time per shadowMap reception
324 lightSetupFaked
= true;
325 // Build the light direction as the opposite to shadow direction (in LocalProjectionMatrix.getJ())
327 fakeLight
.setupDirectional(CRGBA::Black
, CRGBA::White
, CRGBA::Black
, -shadowMap
->LocalProjectionMatrix
.getJ());
328 drv
->setLight(0, fakeLight
);
329 // setup the material
330 shadowContext
.ShadowMaterial
.setLighting(true, shadowColor
, CRGBA::Black
, CRGBA::White
, CRGBA::Black
);
334 (*it
)->receiveShadowMap(shadowContext
);
337 // if the material has been faked, reset
340 shadowContext
.ShadowMaterial
.setLighting(false);
341 shadowContext
.ShadowMaterial
.setColor(shadowColor
);
346 // ***************************************************************************
347 void CVisualCollisionManager::CMeshInstanceCol::receiveShadowMap(const CVisualCollisionMesh::CShadowContext
&shadowContext
)
349 // if mesh still present (else it s may be an error....)
352 // first test if intersect with the bboxes
353 if(!shadowContext
.ShadowWorldBB
.intersect(WorldBBox
))
356 // get the collision with the mesh
357 Mesh
->receiveShadowMap(WorldMatrix
, shadowContext
);
361 // ***************************************************************************
362 void CVisualCollisionManager::getMeshs(const NLMISC::CAABBox
&aabbox
, std::vector
<CMeshInstanceColInfo
> &dest
)
364 _MeshQuadGrid
.select(aabbox
.getMin(), aabbox
.getMax());
366 CQuadGrid
<CMeshInstanceCol
*>::CIterator it
= _MeshQuadGrid
.begin();
367 CQuadGrid
<CMeshInstanceCol
*>::CIterator endIt
= _MeshQuadGrid
.end();
370 if ((*it
)->WorldBBox
.intersect(aabbox
))
372 CMeshInstanceColInfo infos
;
373 infos
.Mesh
= (*it
)->Mesh
;
374 infos
.ID
= (*it
)->ID
;
375 infos
.WorldBBox
= &((*it
)->WorldBBox
);
376 infos
.WorldMatrix
= &((*it
)->WorldMatrix
);
377 dest
.push_back(infos
);