Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / 3d / cluster.cpp
blob312042364031a8c4f31957f4069e6f2d856bd64f
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/cluster.h"
20 #include "nel/3d/portal.h"
21 #include "nel/misc/stream.h"
22 #include "nel/misc/string_mapper.h"
23 #include "nel/3d/scene.h"
24 #include "nel/3d/transform_shape.h"
25 //#include "mesh_instance.h"
26 #include "nel/3d/scene_group.h"
28 using namespace NLMISC;
29 using namespace std;
31 #ifdef DEBUG_NEW
32 #define new DEBUG_NEW
33 #endif
35 namespace NL3D
38 // 0.5 cm of precision
39 #define CLUSTERPRECISION 0.005
42 // ***************************************************************************
43 CCluster::CCluster ()
45 /* ***********************************************
46 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
47 * It can be loaded/called through CAsyncFileManager for instance
48 * ***********************************************/
50 FatherVisible = VisibleFromFather = false;
51 FatherAudible = AudibleFromFather = false;
52 Father = NULL;
53 Group = NULL;
55 // map a no fx string
56 _EnvironmentFxId = CStringMapper::map("no fx");
57 // map a no soundgroup string
58 _SoundGroupId = CStringMapper::map("");
60 // I am a transform cluster
61 CTransform::setIsCluster(true);
63 // Default: not traversed
64 _Visited= false;
65 _CameraIn= false;
69 // ***************************************************************************
70 CCluster::~CCluster()
72 /* ***********************************************
73 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
74 * It can be loaded/called through CAsyncFileManager for instance
75 * ***********************************************/
77 unlinkFromClusterTree();
81 void CCluster::setSoundGroup(const std::string &soundGroup)
83 _SoundGroupId = CStringMapper::map(soundGroup);
85 void CCluster::setSoundGroup(const NLMISC::TStringId &soundGroupId)
87 _SoundGroupId = soundGroupId;
89 const std::string &CCluster::getSoundGroup()
91 return CStringMapper::unmap(_SoundGroupId);
94 NLMISC::TStringId CCluster::getSoundGroupId()
96 return _SoundGroupId;
98 void CCluster::setEnvironmentFx(const std::string &environmentFx)
100 _EnvironmentFxId = CStringMapper::map(environmentFx);
102 void CCluster::setEnvironmentFx(const NLMISC::TStringId &environmentFxId)
104 _EnvironmentFxId = environmentFxId;
106 const std::string &CCluster::getEnvironmentFx()
108 return CStringMapper::unmap(_EnvironmentFxId);
111 NLMISC::TStringId CCluster::getEnvironmentFxId()
113 return _EnvironmentFxId;
118 // ***************************************************************************
119 void CCluster::unlinkFromParent()
121 /* ***********************************************
122 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
123 * It can be loaded/called through CAsyncFileManager for instance
124 * ***********************************************/
126 // unlink from father sons list
127 if (Father)
129 Father->Children.erase(remove(Father->Children.begin(), Father->Children.end(), this), Father->Children.end());
130 Father = NULL;
134 // ***************************************************************************
135 void CCluster::unlinkSons()
137 /* ***********************************************
138 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
139 * It can be loaded/called through CAsyncFileManager for instance
140 * ***********************************************/
142 // tells all sons that they have no more father
143 for(uint k = 0; k < Children.size(); ++k)
145 if (Children[k]->Father == this)
147 Children[k]->Father = NULL;
150 NLMISC::contReset(Children);
155 // ***************************************************************************
156 void CCluster::unlinkFromClusterTree()
158 /* ***********************************************
159 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
160 * It can be loaded/called through CAsyncFileManager for instance
161 * ***********************************************/
163 unlinkFromParent();
164 unlinkSons();
169 // ***************************************************************************
170 void CCluster::registerBasic ()
172 CScene::registerModel (ClusterId, 0, CCluster::creator);
175 // ***************************************************************************
176 bool CCluster::makeVolume (const CVector& p1, const CVector& p2, const CVector& p3)
178 uint i;
179 // Check if the plane is not close to a plane that already define the cluster
180 for (i = 0; i < _LocalVolume.size(); ++i)
182 float f1 = fabsf (_LocalVolume[i]*p1);
183 float f2 = fabsf (_LocalVolume[i]*p2);
184 float f3 = fabsf (_LocalVolume[i]*p3);
185 if ((f1 < CLUSTERPRECISION) && (f2 < CLUSTERPRECISION) && (f3 < CLUSTERPRECISION))
186 return true;
188 // Check if we want to add a triangle not completely in the predefined volume
189 for (i = 0; i < _LocalVolume.size(); ++i)
191 float f1 = _LocalVolume[i]*p1;
192 float f2 = _LocalVolume[i]*p2;
193 float f3 = _LocalVolume[i]*p3;
194 if ((f1 > CLUSTERPRECISION) && (f2 > CLUSTERPRECISION) && (f3 > CLUSTERPRECISION))
195 return false;
197 // Build volume
198 CPlane p;
199 p.make (p1, p2, p3);
200 p.normalize();
201 _LocalVolume.push_back (p);
202 // Build BBox
203 if (_LocalVolume.size() == 1)
204 _LocalBBox.setCenter(p1);
205 else
206 _LocalBBox.extend(p1);
207 _LocalBBox.extend(p2);
208 _LocalBBox.extend(p3);
209 _Volume = _LocalVolume;
210 _BBox = _LocalBBox;
211 return true;
214 // ***************************************************************************
215 bool CCluster::isIn (const CVector& p)
217 for (uint i = 0; i < _Volume.size(); ++i)
218 if (_Volume[i]*p > CLUSTERPRECISION)
219 return false;
220 return true;
224 // ***************************************************************************
225 bool CCluster::isIn (const CAABBox& b)
227 for (uint i = 0; i < _Volume.size(); ++i)
229 if (!b.clipBack (_Volume[i]))
230 return false;
232 return true;
235 // ***************************************************************************
236 bool CCluster::isIn (const NLMISC::CVector& center, float size)
238 for (uint i = 0; i < _Volume.size(); ++i)
239 if (_Volume[i]*center > size)
240 return false;
241 return true;
244 // ***************************************************************************
245 bool CCluster::clipSegment (NLMISC::CVector &p0, NLMISC::CVector &p1)
247 for (uint i = 0; i < _Volume.size(); ++i)
249 if (!_Volume[i].clipSegmentBack(p0, p1))
250 return false;
252 return true;
255 // ***************************************************************************
256 void CCluster::resetPortalLinks ()
258 _Portals.clear();
261 // ***************************************************************************
262 void CCluster::link (CPortal* portal)
264 _Portals.push_back (portal);
267 // ***************************************************************************
268 void CCluster::unlink (CPortal* portal)
270 uint32 pos;
271 for (pos = 0; pos < _Portals.size(); ++pos)
273 if (_Portals[pos] == portal)
274 break;
276 if (pos < _Portals.size())
277 _Portals.erase (_Portals.begin()+pos);
280 // ***************************************************************************
281 void CCluster::serial (NLMISC::IStream&f)
283 /* ***********************************************
284 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
285 * It can be loaded/called through CAsyncFileManager for instance
286 * ***********************************************/
288 sint version = f.serialVersion (3);
290 if (version >= 1)
291 f.serial (Name);
293 f.serialCont (_LocalVolume);
294 f.serial (_LocalBBox);
295 f.serial (FatherVisible);
296 f.serial (VisibleFromFather);
297 if (f.isReading())
299 _Volume = _LocalVolume;
300 _BBox = _LocalBBox;
303 if (version >= 2)
305 if (f.isReading())
307 std::string soundGroup;
308 std::string envFxName;
310 f.serial(soundGroup);
311 _SoundGroupId = CStringMapper::map(soundGroup);
313 f.serial(envFxName);
314 if (envFxName.empty())
315 envFxName = "no fx";
316 _EnvironmentFxId = CStringMapper::map(envFxName);
318 else
320 // write the sound group
321 std::string soundGroup = CStringMapper::unmap(_SoundGroupId);
322 f.serial(soundGroup);
323 // write the env fx name
324 std::string envFxName = CStringMapper::unmap(_EnvironmentFxId);
325 if (envFxName == "no fx")
326 envFxName.clear();
327 f.serial(envFxName);
330 // nldebug("Cluster %s, sound group [%s]", Name.c_str(), CStringMapper::unmap(_SoundGroupId).c_str());
333 if (version >= 3)
335 f.serial(AudibleFromFather);
336 f.serial(FatherAudible);
338 else
340 // copy the visual property
341 AudibleFromFather = VisibleFromFather;
342 FatherAudible = FatherVisible;
346 // ***************************************************************************
347 void CCluster::setWorldMatrix (const CMatrix &WM)
349 uint32 i;
350 CMatrix invWM = WM;
351 invWM.invert();
353 // Transform the volume
354 for (i = 0; i < _LocalVolume.size(); ++i)
355 _Volume[i] = _LocalVolume[i] * invWM;
357 _BBox = NLMISC::CAABBox::transformAABBox(WM, _LocalBBox);
359 // Transform the bounding box
360 /*CVector p[8];
361 p[0].x = _LocalBBox.getMin().x;
362 p[0].y = _LocalBBox.getMin().y;
363 p[0].z = _LocalBBox.getMin().z;
365 p[1].x = _LocalBBox.getMax().x;
366 p[1].y = _LocalBBox.getMin().y;
367 p[1].z = _LocalBBox.getMin().z;
369 p[2].x = _LocalBBox.getMin().x;
370 p[2].y = _LocalBBox.getMax().y;
371 p[2].z = _LocalBBox.getMin().z;
373 p[3].x = _LocalBBox.getMax().x;
374 p[3].y = _LocalBBox.getMax().y;
375 p[3].z = _LocalBBox.getMin().z;
377 p[4].x = _LocalBBox.getMin().x;
378 p[4].y = _LocalBBox.getMin().y;
379 p[4].z = _LocalBBox.getMax().z;
381 p[5].x = _LocalBBox.getMax().x;
382 p[5].y = _LocalBBox.getMin().y;
383 p[5].z = _LocalBBox.getMax().z;
385 p[6].x = _LocalBBox.getMin().x;
386 p[6].y = _LocalBBox.getMax().y;
387 p[6].z = _LocalBBox.getMax().z;
389 p[7].x = _LocalBBox.getMax().x;
390 p[7].y = _LocalBBox.getMax().y;
391 p[7].z = _LocalBBox.getMax().z;
393 for (i = 0; i < 8; ++i)
394 p[i] = WM.mulPoint(p[i]);
396 CAABBox boxTemp;
398 boxTemp.setCenter(p[0]);
399 for (i = 1; i < 8; ++i)
400 boxTemp.extend(p[i]);
401 _BBox = boxTemp;*/
404 // ***************************************************************************
405 void CCluster::traverseHrc ()
407 CTransform::traverseHrc ();
409 setWorldMatrix (_WorldMatrix);
411 for (uint32 i = 0; i < getNbPortals(); ++i)
413 CPortal *pPortal = getPortal(i);
414 pPortal->setWorldMatrix (_WorldMatrix);
417 // Re affect the cluster to the accelerator if not the root
418 if (!isRoot())
420 Group->_ClipTrav->unregisterCluster(this);
421 Group->_ClipTrav->registerCluster (this);
425 // ***************************************************************************
426 bool CCluster::clip ()
428 return true;
432 // ***************************************************************************
433 void CCluster::traverseClip ()
435 // This is the root call called by the SceneRoot
436 recursTraverseClip(NULL);
440 // ***************************************************************************
441 void CCluster::recursTraverseClip(CTransform *caller)
443 if (_Visited)
444 return;
445 _Visited = true;
447 // The cluster is visible because we are in it
448 // So clip the models attached (with MOT links) to the cluster
449 uint num= clipGetNumChildren();
450 uint32 i;
451 for(i=0;i<num;i++)
452 clipGetChild(i)->traverseClip();
454 // Debug visible clusters
455 CClipTrav &clipTrav = getOwnerScene()->getClipTrav();
456 if (clipTrav.getClusterVisibilityTracking())
458 clipTrav.addVisibleCluster(this);
461 // And look through portals
462 for (i = 0; i < getNbPortals(); ++i)
464 CPortal*pPortal = getPortal (i);
465 vector<CPlane> WorldPyrTemp = clipTrav.WorldPyramid;
466 bool backfaceclipped = false;
467 CCluster *pOtherSideCluster;
468 if (pPortal->getCluster(0) == this)
469 pOtherSideCluster = pPortal->getCluster (1);
470 else
471 pOtherSideCluster = pPortal->getCluster (0);
473 if (Father != NULL)
474 if (caller == Father) // If the caller is the father
475 if (VisibleFromFather)
476 // Backface clipping
477 if( !pPortal->isInFront( clipTrav.CamPos ))
478 backfaceclipped = true;
480 if (!backfaceclipped && pOtherSideCluster)
482 /* If the otherSide cluster is fully visible because the camera is IN, then don't need to clip.
483 This is important to landscape test, to ensure that pyramid are strictly equal from 2 paths which
484 come from the 2 clusters where the camera start
486 if (pOtherSideCluster->isCameraIn() || pPortal->clipPyramid (clipTrav.CamPos, clipTrav.WorldPyramid))
488 pOtherSideCluster->recursTraverseClip(this);
492 clipTrav.WorldPyramid = WorldPyrTemp;
495 // Link up in hierarchy
496 if ((FatherVisible)&&(Father != NULL))
498 Father->recursTraverseClip(this);
501 // Link down in hierarchy
502 for (i = 0; i < Children.size(); ++i)
503 if (Children[i]->VisibleFromFather)
505 Children[i]->recursTraverseClip(this);
508 _Visited = false;
512 // ***************************************************************************
513 void CCluster::applyMatrix(const NLMISC::CMatrix &m)
515 uint32 i;
516 CMatrix invM = m;
517 invM.invert();
518 nlassert(_Volume.size() == _LocalVolume.size());
520 // Transform the volume
521 for (i = 0; i < _LocalVolume.size(); ++i)
523 _Volume[i] = _Volume[i] * invM;
524 _LocalVolume[i] = _LocalVolume[i] * invM;
527 // Transform the bounding boxes
528 _BBox = NLMISC::CAABBox::transformAABBox(m, _BBox);
529 _LocalBBox = NLMISC::CAABBox::transformAABBox(m, _LocalBBox);
533 // ***************************************************************************
534 void CCluster::cameraRayClip(const CVector &start, const CVector &end, std::vector<CCluster*> &clusterVisited)
536 uint i;
537 if (_Visited)
538 return;
539 _Visited = true;
541 // The cluster is visible because we are in it. add it to the list of cluster (if not already inserted)
542 for(i=0;i<clusterVisited.size();i++)
544 if(clusterVisited[i]==this)
545 break;
547 if(i==clusterVisited.size())
548 clusterVisited.push_back(this);
550 // look through portals
551 for (i = 0; i < getNbPortals(); ++i)
553 CPortal*pPortal = getPortal (i);
554 CCluster *pOtherSideCluster;
555 if (pPortal->getCluster(0) == this)
556 pOtherSideCluster = pPortal->getCluster (1);
557 else
558 pOtherSideCluster = pPortal->getCluster (0);
561 bool backfaceclipped = false;
562 if (Father != NULL)
563 if (caller == Father) // If the caller is the father
564 if (VisibleFromFather)
565 // Backface clipping
566 if( !pPortal->isInFront( clipTrav.CamPos ))
567 backfaceclipped = true;
568 if (!backfaceclipped && pOtherSideCluster)*/
570 if (pOtherSideCluster)
572 if (pPortal->clipRay (start, end))
574 pOtherSideCluster->cameraRayClip(start, end, clusterVisited);
579 /* Link up in hierarchy. Test the Inverse Flag, cause the path is inverted here!!!
580 ie: if I allow the camera to go out, it MUST can re-enter (ie if I am VisibleFromFather)
582 if ((VisibleFromFather)&&(Father != NULL))
584 Father->cameraRayClip(start, end, clusterVisited);
587 // Link down in hierarchy
588 for (i = 0; i < Children.size(); ++i)
590 // same remark. test FatherVisible, not VisibleFromFather
591 if (Children[i]->FatherVisible)
593 Children[i]->cameraRayClip(start, end, clusterVisited);
597 _Visited = false;
601 } // NL3D