Linux multi-monitor fullscreen support
[ryzomcore.git] / nel / src / 3d / visual_collision_manager.cpp
blob6d914464fc401712eaf90cb666362adfff7f5423
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
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.
8 //
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/>.
17 #include "std3d.h"
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"
28 using namespace std;
29 using namespace NLMISC;
31 #ifdef DEBUG_NEW
32 #define new DEBUG_NEW
33 #endif
35 namespace NL3D
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;
43 // For Mesh QuadGrid
44 const uint MeshColQuadGridSize= 64;
45 const float MeshColQuadGridEltSize= 20;
48 // ***************************************************************************
49 CVisualCollisionManager::CVisualCollisionManager() :
50 _TileDescNodeAllocator(TileDescNodeAllocatorBlockSize),
51 _PatchQuadBlockAllocator(PatchQuadBlockAllocatorBlockSize)
53 _Landscape= NULL;
55 // Default.
56 setSunContributionPower(0.5f, 0.5f);
58 // init the mesh quadGrid
59 _MeshQuadGrid.create(MeshColQuadGridSize, MeshColQuadGridEltSize);
60 // valid id start at 1
61 _MeshIdPool= 1;
63 _PlayerInside= false;
64 _ShadowIndexBuffer.setFormat(NL_DEFAULT_INDEX_BUFFER_FORMAT);
65 NL_SET_IB_NAME(_ShadowIndexBuffer, "CVisualCollisionManager");
67 // ***************************************************************************
68 CVisualCollisionManager::~CVisualCollisionManager()
70 _Landscape= NULL;
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)
91 delete 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++)
127 float f= i/255.f;
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 // ***************************************************************************
138 // Camera collision
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)
153 float minCol= 1;
155 // try col with landscape
156 if(_Landscape)
158 minCol= _Landscape->getCameraCollision(start, end, radius, cone);
161 // try col with meshes
162 CCameraCol camCol;
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)
171 continue;
172 if((*it)->AvoidCollisionWhenPlayerOutside && !_PlayerInside)
173 continue;
175 // collide
176 float meshCol= (*it)->getCameraCollision(camCol);
177 // Keep only yhe smallest value
178 minCol= min(minCol, meshCol);
181 return minCol;
185 // ***************************************************************************
186 bool CVisualCollisionManager::getRayCollision(const NLMISC::CVector &start, const NLMISC::CVector &end, bool landscapeOnly)
188 // try col with landscape
189 if(_Landscape)
191 if( _Landscape->getRayCollision(start, end) < 1.f)
192 return true;
195 // try col with meshes, if wanted
196 if(!landscapeOnly)
198 CCameraCol camCol;
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)
207 continue;
208 if((*it)->AvoidCollisionWhenPlayerOutside && !_PlayerInside)
209 continue;
211 // collide
212 float meshCol= (*it)->getCameraCollision(camCol);
213 if(meshCol<1.f)
214 return true;
218 return false;
222 // ***************************************************************************
223 float CVisualCollisionManager::CMeshInstanceCol::getCameraCollision(CCameraCol &camCol)
225 // if mesh still present (else it s may be an error....)
226 if(Mesh)
228 // first test if intersect with the bboxes
229 if(!camCol.getBBox().intersect(WorldBBox))
230 return 1;
232 // get the collision with the mesh
233 return Mesh->getCameraCollision(WorldMatrix, camCol);
235 else
236 // no collision
237 return 1;
241 // ***************************************************************************
242 uint CVisualCollisionManager::addMeshInstanceCollision(CVisualCollisionMesh *mesh, const CMatrix &instanceMatrix, bool avoidCollisionWhenInside, bool avoidCollisionWhenOutside)
244 if(!mesh)
245 return 0;
247 // allocate a new id
248 uint32 id= _MeshIdPool++;
250 // insert in map
251 CMeshInstanceCol &meshInst= _Meshs[id];
253 // Build the col mesh instance
254 meshInst.Mesh= mesh;
255 meshInst.WorldMatrix= instanceMatrix;
256 meshInst.WorldBBox= mesh->computeWorldBBox(instanceMatrix);
257 meshInst.AvoidCollisionWhenPlayerInside= avoidCollisionWhenInside;
258 meshInst.AvoidCollisionWhenPlayerOutside= avoidCollisionWhenOutside;
259 meshInst.ID = id;
261 // insert in quadGrid
262 meshInst.QuadGridIt= _MeshQuadGrid.insert(meshInst.WorldBBox.getMin(), meshInst.WorldBBox.getMax(), &meshInst);
264 return id;
267 // ***************************************************************************
268 void CVisualCollisionManager::removeMeshCollision(uint id)
270 // find in map
271 TMeshColMap::iterator it= _Meshs.find(id);
272 if(it!=_Meshs.end())
274 // remove from the quadgrid
275 _MeshQuadGrid.erase(it->second.QuadGridIt);
276 // remove from the map
277 _Meshs.erase(it);
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);
293 // bkup shadowColor
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)
307 continue;
308 if((*it)->AvoidCollisionWhenPlayerOutside && !_PlayerInside)
309 continue;
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
322 if(!lightSetupFaked)
324 lightSetupFaked= true;
325 // Build the light direction as the opposite to shadow direction (in LocalProjectionMatrix.getJ())
326 CLight fakeLight;
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);
333 // cast shadow
334 (*it)->receiveShadowMap(shadowContext);
337 // if the material has been faked, reset
338 if(lightSetupFaked)
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....)
350 if(Mesh)
352 // first test if intersect with the bboxes
353 if(!shadowContext.ShadowWorldBB.intersect(WorldBBox))
354 return;
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());
365 dest.clear();
366 CQuadGrid<CMeshInstanceCol*>::CIterator it = _MeshQuadGrid.begin();
367 CQuadGrid<CMeshInstanceCol*>::CIterator endIt = _MeshQuadGrid.end();
368 while (it != endIt)
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);
379 ++ it;
385 } // NL3D