Merge branch 'ryzom/ark-features' into main/gingo-test
[ryzomcore.git] / nel / src / 3d / transform.cpp
blob559a3c3550837e19e5db4411f60207041ed49e52
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/transform.h"
20 #include "nel/3d/skeleton_model.h"
21 #include "nel/3d/scene.h"
22 #include "nel/3d/scene_group.h"
23 #include "nel/3d/root_model.h"
24 #include "nel/3d/u_transform.h"
25 #include "nel/misc/fast_floor.h"
26 #include "nel/misc/hierarchical_timer.h"
29 using namespace NLMISC;
30 using namespace std;
32 #ifdef DEBUG_NEW
33 #define new DEBUG_NEW
34 #endif
36 namespace NL3D
40 // ***************************************************************************
41 #define NL3D_TRANSFORM_DEFAULT_SHADOW_MAP_DEPTH 8.f
44 // ***************************************************************************
45 void CTransform::registerBasic()
47 CScene::registerModel( TransformId, 0, CTransform::creator);
51 // ***************************************************************************
52 CTransform::CTransform()
54 // important to reset for destructor to know if linked or not (CCluster !!)
55 _OwnerScene= NULL;
57 // Hrc/Graph hierarchy
58 _HrcParent= NULL;
59 _HrcParentUnfreeze= NULL;
61 _PrecModelToUpdate= NULL;
62 _NextModelToUpdate= NULL;
64 _TransformDirty= true;
66 Visibility= CHrcTrav::Herit;
68 _LastTransformableMatrixDate= 0;
70 _FatherSkeletonModel= NULL;
72 _ClusterSystem = NULL;
74 _FreezeHRCState= FreezeHRCStateDisabled;
76 _OrderingLayer = 2;
78 _TransparencyPriority = 0;
81 // No logicInfo by default
82 _LogicInfo= NULL;
84 _ForceCLodSticked= false;
86 // default MeanColor value
87 _MeanColor.set(255,255,255,255);
89 // Default ShadowMap direction
90 _ShadowMapDirectionZThreshold= -0.5f;
91 _ShadowMapMaxDepth= NL3D_TRANSFORM_DEFAULT_SHADOW_MAP_DEPTH;
93 // Setup some state.
96 Default are:
97 IsAnimDetailable= 0
98 IsLoadBalancable= 0
99 IsLightable= 0
100 IsRenderable= 0
101 IsTransparent= 0
102 IsOpaque= 1
103 QuadGridClipEnabled= 0.
105 IsUserLightable= 1 // default, the model may be lighted.
106 IsFinalLightable= 0
107 IsNeedUpdateLighting= 0
108 ISNeedUpdateFrozenStaticLightSetup= 0
110 IsSkeleton= 0
111 IsTransformShape=0
112 IsCluster= 0
113 IsMeshBaseInstance= 0
115 IsDeleteChannelMixer = 0;
117 _StateFlags= IsOpaque | IsUserLightable;
119 // By default, always allow rendering of Transform Models.
120 _RenderFilterType = std::numeric_limits<uint32>::max();
122 // By default, don't suport fast intersection detection
123 _SupportFastIntersect= false;
126 // **** HRC Init Traversal Computed Data.
127 _LocalVis= CHrcTrav::Herit; _LocalMatrix.identity(); _LocalDate=0;
128 _WorldVis= true; _WorldMatrix.identity();
129 // Init the _WorldDate to -1 so at first pass, _LocalDate>_WorldDate, and so
130 // the model will be processed and so it'll may be inserted in LightingManager (for example)
131 _WorldDate=-1;
132 _Frozen = false;
133 _DontUnfreezeChildren = false;
134 _AncestorSkeletonModel= NULL;
135 _ClipLinkedInSonsOfAncestorSkeletonModelGroup= false;
137 // **** Clip Init Traversal Computed Data.
138 _ClipDate= 0;
139 _Visible=false;
140 _IndexInVisibleList= -1;
142 // **** AnimDetail Init Traversal Computed Data.
143 // none
145 // **** LoadBalancing Init Traversal Computed Data.
146 _LoadBalancingGroup= NULL;
150 // ***************************************************************************
151 CTransform::~CTransform()
153 // If still binded to a father skeleton
154 if( _FatherSkeletonModel )
156 /* If skinned, cannot detach me from skeleton here because detachSkeletonSon()
157 use some virtual calls of transform: setApplySkin().
158 Hence, It is the deriver job to detach himself from the skeleton.
160 NB: test isSkinned(), not isSkinnable(), since isSkinned() is not virtual ....
161 This means that if a Mesh isSkinnable(), but never skinned, it is not asserted here.
163 if( isSkinned() )
165 nlstop;
167 else
168 // Can detach Me. Important for UTransform sticked
169 _FatherSkeletonModel->detachSkeletonSon(this);
172 // resetLighting, removing me from PointLight Transform list.
173 // NB: not done for FrozenStaticLightSetup, because those lights don't owns me.
174 resetLighting();
176 // Must also remove me from the lightingManager.
177 // must test getOwnerScene() because of CCluster usage out of CScene (thanks to mat!! :) )
178 if(getOwnerScene())
180 CLightTrav &lightTrav= getOwnerScene()->getLightTrav();
181 _LightedModelIt= lightTrav.LightingManager.eraseStaticLightedModel(_LightedModelIt);
184 if (getChannelMixerOwnerShip()) delete (CChannelMixer *) _ChannelMixer;
186 // ensure the model is no more linked to the UpdateList.
187 unlinkFromUpdateList();
189 // I must remove me from _VisibleList.
190 if(_IndexInVisibleList>=0)
192 CClipTrav &clipTrav= getOwnerScene()->getClipTrav();
193 nlassert(_IndexInVisibleList < (sint)clipTrav._CurrentNumVisibleModels );
194 // Mark NULL. NB: faster than a CRefPtr.
195 clipTrav._VisibleList[_IndexInVisibleList]= NULL;
196 _IndexInVisibleList= -1;
199 // remove me from parents in Hrc and Clip
200 setStateFlag(ForceClipRoot, false); // ensure that not 'glued' to the root so that the following call will succeed
201 hrcUnlink();
202 clipUnlinkFromAll();
204 // remove mys sons.
205 while(hrcGetNumChildren())
207 hrcGetChild(0)->hrcUnlink();
209 while(clipGetNumChildren())
211 clipDelChild(clipGetChild(0));
214 nlassert(_HrcSons.empty());
215 nlassert(_HrcParent==NULL);
216 nlassert(_ClipSons.empty());
217 nlassert(_ClipParents.empty());
221 // ***************************************************************************
222 void CTransform::initModel()
224 // assign me to the default group
225 _LoadBalancingGroup= getOwnerScene()->getLoadBalancingTrav().getDefaultGroup();
230 // ***************************************************************************
231 void CTransform::hide()
233 // Optim: do nothing if already set
234 if(Visibility!= CHrcTrav::Hide)
236 _TransformDirty= true;
237 Visibility= CHrcTrav::Hide;
238 // If skinned, then must inform skeleton parent that it must recompute skin render/animDetail lists
239 if(isSkinned())
241 nlassert(_FatherSkeletonModel);
242 _FatherSkeletonModel->dirtSkinRenderLists();
247 // ***************************************************************************
248 void CTransform::setTransparency(bool v)
250 bool bTmp = getStateFlag(IsTransparent) == 0 ? false : true;
251 if (bTmp != v)
253 setStateFlag(IsTransparent, v);
254 if(isSkinned())
256 nlassert(_FatherSkeletonModel);
257 _FatherSkeletonModel->dirtSkinRenderLists();
262 // ***************************************************************************
263 void CTransform::setBypassLODOpacityFlag(bool bypass)
265 setStateFlag(BypassLODOpacity, bypass);
268 // ***************************************************************************
269 void CTransform::setOpacity(bool v)
271 bool bTmp = getStateFlag(IsOpaque) == 0 ? false : true;
272 if (bTmp != v)
274 setStateFlag(IsOpaque, v);
275 if(isSkinned())
277 nlassert(_FatherSkeletonModel);
278 _FatherSkeletonModel->dirtSkinRenderLists();
284 // ***************************************************************************
285 void CTransform::show()
287 // Optim: do nothing if already set
288 if(Visibility!= CHrcTrav::Show)
290 _TransformDirty= true;
291 Visibility= CHrcTrav::Show;
292 // If skinned, then must inform skeleton parent that it must recompute skin render/animDetail lists
293 if(isSkinned())
295 nlassert(_FatherSkeletonModel);
296 _FatherSkeletonModel->dirtSkinRenderLists();
300 // ***************************************************************************
301 void CTransform::heritVisibility()
303 // Optim: do nothing if already set
304 if(Visibility!= CHrcTrav::Herit)
306 _TransformDirty= true;
307 Visibility= CHrcTrav::Herit;
308 // If skinned, then must inform skeleton parent that it must recompute skin render/animDetail lists
309 if(isSkinned())
311 nlassert(_FatherSkeletonModel);
312 _FatherSkeletonModel->dirtSkinRenderLists();
318 // ***************************************************************************
319 CTrackDefaultVector CTransform::DefaultPos( CVector::Null );
320 CTrackDefaultVector CTransform::DefaultRotEuler( CVector::Null );
321 CTrackDefaultQuat CTransform::DefaultRotQuat( NLMISC::CQuat::Identity );
322 CTrackDefaultVector CTransform::DefaultScale( CVector(1,1,1) );
323 CTrackDefaultVector CTransform::DefaultPivot( CVector::Null );
325 ITrack* CTransform::getDefaultTrack (uint valueId)
327 // Cyril: prefer do it here in CTransform, because of CCamera, CLight etc... (which may not need a default value too!!)
329 // what value ?
330 switch (valueId)
332 case PosValue: return &DefaultPos;
333 case RotEulerValue: return &DefaultRotEuler;
334 case RotQuatValue: return &DefaultRotQuat;
335 case ScaleValue: return &DefaultScale;
336 case PivotValue: return &DefaultPivot;
339 // No, only ITrnasformable values!
340 nlstop;
341 // Deriver note: else call BaseClass::getDefaultTrack(valueId);
343 return NULL;
347 // ***************************************************************************
348 void CTransform::registerToChannelMixer(CChannelMixer *chanMixer, const std::string &prefix)
350 if (getChannelMixerOwnerShip() && chanMixer != _ChannelMixer)
352 delete _ChannelMixer;
353 setChannelMixerOwnerShip(false);
356 // Hey!! we are animated!!
357 _ChannelMixer= chanMixer;
359 // Update flag, if we must be inserted in AnimDetail
360 setStateFlag(IsAnimDetailable, _ChannelMixer || getStateFlag(IsForceAnimDetail) );
362 // If skinned, then must inform skeleton parent that it must recompute skin render/animDetail lists
363 if(isSkinned())
365 nlassert(_FatherSkeletonModel);
366 _FatherSkeletonModel->dirtSkinRenderLists();
369 // For CTransfom, channels are not detailled.
370 addValue(chanMixer, PosValue, OwnerBit, prefix, false);
371 addValue(chanMixer, RotEulerValue, OwnerBit, prefix, false);
372 addValue(chanMixer, RotQuatValue, OwnerBit, prefix, false);
373 addValue(chanMixer, ScaleValue, OwnerBit, prefix, false);
374 addValue(chanMixer, PivotValue, OwnerBit, prefix, false);
376 // Deriver note: if necessary, call BaseClass::registerToChannelMixer(chanMixer, prefix);
380 // ***************************************************************************
381 void CTransform::freeze()
383 // First, update the model
384 // _Frozen state is disabled here (in CTransform::update()).
385 update();
387 // Then flag the frozen state.
388 _Frozen= true;
391 // ***************************************************************************
392 void CTransform::setDontUnfreezeChildren(bool val)
394 _DontUnfreezeChildren = val;
398 // ***************************************************************************
399 void CTransform::freezeHRC()
401 // if disabled, say we are ready to validate our worldMatrix for long.
402 if(_FreezeHRCState==FreezeHRCStateDisabled)
404 _FreezeHRCState= FreezeHRCStateRequest;
405 setStateFlag(QuadGridClipEnabled, true);
407 /* If the transform is not frozen (ie staticaly inserted in a cluster),
408 We must be sure it will be tested against QuadGridClipManager at next ClipTrav pass.
409 => must make this object a "moving object" at next render=> dirt _LocalMatrixDate.
411 if(!_Frozen)
413 _TransformDirty= true;
419 // ***************************************************************************
420 void CTransform::unfreezeHRC()
422 // if this model is no HRC frozen disabled
423 if(_FreezeHRCState!=FreezeHRCStateDisabled)
425 // if model correctly frozen.
426 if(_FreezeHRCState == CTransform::FreezeHRCStateEnabled )
428 // Should not be linked : can't link after a freezeHRC
429 nlassert (_HrcParent == NULL);
431 // Set as unfreeze else, hrcLinkSon doesn't work
432 _FreezeHRCState= FreezeHRCStateDisabled;
434 // Link this model to the previous HRC parent.
435 if (_HrcParentUnfreeze)
436 _HrcParentUnfreeze->hrcLinkSon( this );
437 else
438 getOwnerScene()->getRoot()->hrcLinkSon( this );
440 // Link this object to the validateList.
441 linkToUpdateList();
443 // if lightable()
444 if( isLightable() )
446 CLightTrav &lightTrav= getOwnerScene()->getLightTrav();
447 // Lighting: must remove the object from the quadGrid.
448 // NB: works if _LightedModelIt==NULL. result is that _LightedModelIt= NULL.
449 _LightedModelIt= lightTrav.LightingManager.eraseStaticLightedModel(_LightedModelIt);
453 else
454 _FreezeHRCState= FreezeHRCStateDisabled;
456 // unlink me from any QuadCluster, and disable QuadCluster
457 unlinkFromQuadCluster();
458 setStateFlag(QuadGridClipEnabled, false);
463 // ***************************************************************************
464 void CTransform::update()
466 // test if the matrix has been changed in ITransformable.
467 if(ITransformable::compareMatrixDate(_LastTransformableMatrixDate))
469 _LastTransformableMatrixDate= ITransformable::getMatrixDate();
470 _TransformDirty= true;
473 // update the freezeHRC state.
474 if(_FreezeHRCState != CTransform::FreezeHRCStateDisabled)
476 // if the model request to be frozen in HRC
477 if(_FreezeHRCState == CTransform::FreezeHRCStateRequest )
479 // Wait for next Hrc traversal to compute good _WorldMatrix for this model and his sons.
480 // Also, next Hrc traversal will insert the model in the LightingManager quadGrid (if lightable)
481 _FreezeHRCState = CTransform::FreezeHRCStateReady;
483 // if the model is ready to be frozen in HRC, then do it!!
484 else if( _FreezeHRCState == CTransform::FreezeHRCStateReady )
486 // Unlink this model.
487 hrcUnlink();
489 // unLink this object from the validateList. NB: the list will still be correclty parsed.
490 unlinkFromUpdateList();
492 // if lightable, the model is inserted in a quadgrid to update his lighting only when
493 // dynamicLights touch him (since himself is static).
494 if( isLightable() )
496 CLightTrav &lightTrav= getOwnerScene()->getLightTrav();
497 // Lighting: must reinsert the object from the quadGrid.
498 // NB: works if _LightedModelIt==NULL. result is that _LightedModelIt= NULL.
499 _LightedModelIt= lightTrav.LightingManager.eraseStaticLightedModel(_LightedModelIt);
500 // insert in the quadgrid.
501 _LightedModelIt= lightTrav.LightingManager.insertStaticLightedModel(this);
504 // Now this model won't be tested for validation nor for worldMatrix update. End!!
505 _FreezeHRCState = CTransform::FreezeHRCStateEnabled;
509 // update _LocalMatrix
510 if(_TransformDirty)
512 // update the local matrix.
513 _LocalMatrix= getMatrix();
514 _LocalVis= Visibility;
515 // update the date of the local matrix.
516 _LocalDate= getOwnerScene()->getHrcTrav().CurrentDate;
518 // The transform has been modified. Hence, it is no more frozen.
519 _Frozen= false;
521 // ok!
522 _TransformDirty= false;
527 // ***************************************************************************
528 void CTransform::getAABBox(NLMISC::CAABBox &bbox) const
530 bbox.setCenter(CVector::Null);
531 bbox.setHalfSize(CVector::Null);
535 // ***************************************************************************
536 void CTransform::setLoadBalancingGroup(const std::string &group)
538 // Get the traversal.
539 CLoadBalancingTrav &trav= getOwnerScene()->getLoadBalancingTrav();
540 // get the group from trav (create if needed), and set it.
541 _LoadBalancingGroup= trav.getOrCreateGroup(group);
545 // ***************************************************************************
546 const std::string &CTransform::getLoadBalancingGroup() const
548 // get the group name
549 return _LoadBalancingGroup->Name;
553 // ***************************************************************************
554 void CTransform::setMeanColor(CRGBA color)
556 // if the color is different from prec
557 if(color!=_MeanColor)
559 // change it.
560 _MeanColor= color;
565 // ***************************************************************************
566 void CTransform::setIsLightable(bool val)
568 setStateFlag(IsLightable, val);
569 // update IsFinalLightable
570 setStateFlag(IsFinalLightable, (getStateFlag(IsLightable) && getStateFlag(IsUserLightable)) );
572 // ***************************************************************************
573 void CTransform::setUserLightable(bool enable)
575 setStateFlag(IsUserLightable, enable);
576 // update IsFinalLightable
577 setStateFlag(IsFinalLightable, (getStateFlag(IsLightable) && getStateFlag(IsUserLightable)) );
581 // ***************************************************************************
582 void CTransform::setIsRenderable(bool val)
584 setStateFlag(IsRenderable, val);
587 // ***************************************************************************
588 void CTransform::setIsBigLightable(bool val)
590 setStateFlag(IsBigLightable, val);
592 // ***************************************************************************
593 void CTransform::setIsSkeleton(bool val)
595 setStateFlag(IsSkeleton, val);
597 // ***************************************************************************
598 void CTransform::setApplySkin(bool state)
600 setStateFlag(IsSkinned, state);
603 // ***************************************************************************
604 void CTransform::setIsForceAnimDetail(bool val)
606 setStateFlag(IsForceAnimDetail, val );
608 // Update flag, if we must be inserted in AnimDetail
609 setStateFlag(IsAnimDetailable, _ChannelMixer || getStateFlag(IsForceAnimDetail) );
611 // If skinned, then must inform skeleton parent that it must recompute skin render/animDetail lists
612 if(isSkinned())
614 nlassert(_FatherSkeletonModel);
615 _FatherSkeletonModel->dirtSkinRenderLists();
618 // ***************************************************************************
619 void CTransform::setIsLoadbalancable(bool val)
621 setStateFlag(IsLoadBalancable, val );
625 // ***************************************************************************
626 void CTransform::linkToUpdateList()
628 if(!_OwnerScene)
629 return;
631 // If the model is not already inserted.
632 if( ! (_PrecModelToUpdate!=NULL || _OwnerScene->_UpdateModelList==this) )
634 // insert it.
635 _NextModelToUpdate= _OwnerScene->_UpdateModelList;
636 _PrecModelToUpdate= NULL;
637 if(_NextModelToUpdate)
638 _NextModelToUpdate->_PrecModelToUpdate= this;
639 _OwnerScene->_UpdateModelList= this;
643 // ***************************************************************************
644 void CTransform::unlinkFromUpdateList()
646 if(!_OwnerScene)
647 return;
649 // If the model is inserted.
650 if( _PrecModelToUpdate!=NULL || _OwnerScene->_UpdateModelList==this )
652 // update prec.
653 if(_PrecModelToUpdate)
654 _PrecModelToUpdate->_NextModelToUpdate= _NextModelToUpdate;
655 else
656 _OwnerScene->_UpdateModelList= _NextModelToUpdate;
658 // update next.
659 if(_NextModelToUpdate)
660 _NextModelToUpdate->_PrecModelToUpdate= _PrecModelToUpdate;
662 // End.
663 _PrecModelToUpdate= NULL;
664 _NextModelToUpdate= NULL;
669 // ***************************************************************************
670 // ***************************************************************************
671 // Hrc Trav
672 // ***************************************************************************
673 // ***************************************************************************
676 // ***************************************************************************
677 void CTransform::updateWorld()
679 const CMatrix *pFatherWM;
680 bool visFather;
682 // If not root case, link to Fahter.
683 if(_HrcParent)
685 pFatherWM= &(_HrcParent->_WorldMatrix);
686 visFather= _HrcParent->_WorldVis;
688 // if _HrcParent is not frozen (for any reason), disable us!
690 if (!_HrcParent->_Frozen && !_HrcParent->_DontUnfreezeChildren)
691 _Frozen= false;
693 // herit _AncestorSkeletonModel
694 if (_HrcParent->_AncestorSkeletonModel)
695 // If my father has an _AncestorSkeletonModel, get it.
696 _AncestorSkeletonModel= _HrcParent->_AncestorSkeletonModel;
697 else
698 // else I have an ancestor skel model if I am sticked/binded directly to a skeleton model.
699 _AncestorSkeletonModel= _FatherSkeletonModel;
701 // else, default!!
702 else
704 pFatherWM= &(CMatrix::Identity);
705 visFather= true;
707 // at the root of the hierarchy, we have no parent, hence no FatherSkeletonModel nor _AncestorSkeletonModel.
708 _AncestorSkeletonModel= NULL;
710 // NB: Root is Frozen by essence :), so don't modify the frozen state here.
713 // Combine matrix
714 if(_LocalDate>_WorldDate || (_HrcParent && _HrcParent->_WorldDate>_WorldDate) )
716 // Must recompute the world matrix. ONLY IF I AM NOT SKINNED/STICKED TO A SKELETON in the hierarchy!
717 if( _AncestorSkeletonModel==NULL )
719 _WorldMatrix= *pFatherWM * _LocalMatrix;
720 _WorldDate= getOwnerScene()->getHrcTrav().CurrentDate;
722 // Add the model to the moving object list, only if I am a transform shape
723 if (!_Frozen && isTransformShape() && !getStateFlag(ForceClipRoot))
724 getOwnerScene()->getHrcTrav()._MovingObjects.push_back (static_cast<CTransformShape*>(this));
728 // Update dynamic lighting.
730 If the model is not frozen in StaticLight, then must update lighting each frame.
731 Even if the object doesn't move, a new dynamic light may enter in its aera. Hence we must test
732 it in the light quadrid. StaticLight-ed Objects don't need it because they are inserted in a special quadgrid,
733 where dynamics lights touch all StaticLight-ed object to force their computing
735 NB: not done if _AncestorSkeletonModel!=NULL. no need because in this case,
736 result is driven by the _LightContribution of the _AncestorSkeletonModel.
738 if( !_LightContribution.FrozenStaticLightSetup && _AncestorSkeletonModel==NULL )
740 // if the model is lightable reset lighting
741 if( isLightable() )
742 resetLighting();
745 // Combine visibility.
746 switch(_LocalVis)
748 case CHrcTrav::Herit: _WorldVis= visFather; break;
749 case CHrcTrav::Hide: _WorldVis= false; break;
750 case CHrcTrav::Show: _WorldVis= true; break;
751 default: break;
755 // If I have an ancestor Skeleton Model, I must be binded in ClipTrav to the SonsOfAncestorSkeletonModelGroup
756 updateClipTravForAncestorSkeleton();
761 // ***************************************************************************
762 void CTransform::updateClipTravForAncestorSkeleton()
764 // If I have an ancestor Skeleton Model, I must be binded in ClipTrav to the SonsOfAncestorSkeletonModelGroup
765 if(_AncestorSkeletonModel && !_ClipLinkedInSonsOfAncestorSkeletonModelGroup)
767 // must unlink from ALL olds models.
768 clipUnlinkFromAll();
770 // And link to SonsOfAncestorSkeletonModelGroup.
771 getOwnerScene()->SonsOfAncestorSkeletonModelGroup->clipAddChild(this);
773 // update the flag.
774 _ClipLinkedInSonsOfAncestorSkeletonModelGroup= true;
778 // else I must be binded to the standard Root.
779 if(!_AncestorSkeletonModel && _ClipLinkedInSonsOfAncestorSkeletonModelGroup)
781 // verify first I am really still linked to the SonsOfAncestorSkeletonModelGroup.
782 // This test is important, because link may have changed for any reason (portals, clipManager....).
783 if( clipGetNumParents() == 1 && clipGetParent(0)==getOwnerScene()->SonsOfAncestorSkeletonModelGroup )
785 // must unlink from ALL olds models.
786 clipUnlinkFromAll();
787 // and now, link to std root.
788 getOwnerScene()->getRoot()->clipAddChild(this);
791 // update the flag
792 _ClipLinkedInSonsOfAncestorSkeletonModelGroup= false;
797 // ***************************************************************************
798 void CTransform::traverseHrc()
800 // Recompute the matrix, according to _HrcParent matrix mode, and local matrix.
801 updateWorld();
803 // Traverse the Hrc sons.
804 uint num= hrcGetNumChildren();
805 for(uint i=0;i<num;i++)
806 hrcGetChild(i)->traverseHrc();
810 // ***************************************************************************
811 // ***************************************************************************
812 // Clip Trav
813 // ***************************************************************************
814 // ***************************************************************************
817 // ***************************************************************************
818 void CTransform::setClusterSystem(CInstanceGroup *pCS)
820 if (pCS != NULL)
822 nlassert(!getStateFlag(ForceClipRoot)); // the transform must be linked to the root, and have not cluster system when this flag is set
824 // Special case for the "AutoClusterSystem" when pCS==-1
825 if(pCS==(CInstanceGroup*)-1)
827 _ClusterSystem = NULL;
828 setStateFlag(ClusterSystemAuto, true);
830 else
832 _ClusterSystem = pCS;
833 setStateFlag(ClusterSystemAuto, false);
837 // ***************************************************************************
838 CInstanceGroup* CTransform::getClusterSystem ()
840 if(getStateFlag(ClusterSystemAuto))
841 return (CInstanceGroup*)-1;
842 else
843 return _ClusterSystem;
846 // ***************************************************************************
847 void CTransform::traverseClip()
849 // disable H_AUTO, because slowdown when lot of models (eg 1000-2000 tested in forest)
850 //H_AUTO( NL3D_TransformClip );
852 CScene *scene= getOwnerScene();
853 CClipTrav &clipTrav= scene->getClipTrav();
855 if ((_ClipDate == clipTrav.CurrentDate) && _Visible)
856 return;
857 _ClipDate = clipTrav.CurrentDate;
859 // clip: update Visible flag.
860 _Visible= false;
861 // if at least visible.
862 if(_WorldVis)
864 // If linked to a SkeletonModel anywhere in the hierarchy, don't clip, and use skeleton model clip result.
865 // This works because we are sons of a special node which is not in the clip traversal, and
866 // which is traversed at end of the traversal.
867 if( _AncestorSkeletonModel!=NULL )
869 _Visible= _AncestorSkeletonModel->isClipVisible();
870 // Special test: if we are sticked to a skeletonModel, and if we are still visible, maybe we don't have to
871 if(_Visible && _FatherSkeletonModel)
873 // if our skeletonModel father is displayed with a Lod, maybe we are not to be displayed
874 if(_FatherSkeletonModel->isDisplayedAsLodCharacter())
876 // We are visible only if we where sticked to the skeleton with forceCLod==true.
877 // This is also true if we are actually a skeletonModel
878 if(!_ForceCLodSticked)
879 // otherWise we are not visible. eg: this is the case of skins and some sticked object
880 _Visible= false;
884 // else, clip.
885 else
887 // If the instance is not filtered
888 if(scene->getFilterRenderFlags() & _RenderFilterType)
890 // User cliping enabled ?
891 if (_StateFlags & UserClipping)
892 _Visible= true;
893 else
894 _Visible= clip();
899 // if visible, add to list.
900 if(_Visible)
902 // add this model to the visibility list.
903 clipTrav.addVisibleModel(this);
905 // Has not an ancestor skeleton model?
906 if( _AncestorSkeletonModel==NULL )
908 // If needed, insert the model in the lighted list.
909 // don't insert if has an ancestorSkeletonModel, because in this case, result is driven by
910 // the _LightContribution of the _AncestorSkeletonModel.
911 if( isLightable() )
912 scene->getLightTrav().addLightedModel(this);
914 // If needed, insert the model in the animDetail list.
915 // don't insert if has an ancestoreSkeletonModel, because in this case, this ancestore will
916 // animDetail through the hierarchy...
917 if( isAnimDetailable() )
918 scene->getAnimDetailTrav().addVisibleModel(this);
921 // If needed, Add it to the loadBalancing trav
922 if( isLoadBalancable() )
923 scene->getLoadBalancingTrav().addVisibleModel(this);
925 // If needed, insert the model in the render list.
926 if( isRenderable() )
927 scene->getRenderTrav().addRenderModel(this);
930 // Traverse the Clip sons.
931 uint num= clipGetNumChildren();
932 for(uint i=0;i<num;i++)
933 clipGetChild(i)->traverseClip();
938 // ***************************************************************************
939 // ***************************************************************************
940 // AnimDetail Trav
941 // ***************************************************************************
942 // ***************************************************************************
945 // ***************************************************************************
946 void CTransform::updateWorldMatrixFromFather()
948 // If I am not skinned, and If I have a skeleton ancestor
949 if(!isSkinned() && _AncestorSkeletonModel )
951 // Compute the HRC _WorldMatrix.
952 // if I am not sticked.
953 if(!_FatherSkeletonModel)
955 // get the normal father worldMatrix in Hrc.
956 CTransform *fatherTransform= hrcGetParent();
957 // if exist
958 if(fatherTransform)
960 const CMatrix &parentWM= fatherTransform->_WorldMatrix;
961 // combine worldMatrix
962 _WorldMatrix= parentWM * _LocalMatrix;
964 else
965 _WorldMatrix= _LocalMatrix;
967 else
969 // get the worldMatrix of the bone if I am sticked (standard stick)
970 if(!getStateFlag(SSSWO))
972 const CMatrix &parentWM= _FatherSkeletonModel->Bones[_FatherBoneId].getWorldMatrix();
973 // combine worldMatrix
974 _WorldMatrix= parentWM * _LocalMatrix;
976 // Special SkeletonSpawnScript stick
977 else
979 // The parent matrix must be computed from a special matrix given to the skeleton model
980 CMatrix parentWM;
981 parentWM.setRot(CVector::I, _FatherSkeletonModel->getSSSWODir(), CVector::K);
982 parentWM.normalize(CMatrix::YZX);
983 parentWM.setPos(_FatherSkeletonModel->getSSSWOPos());
984 // combine worldMatrix
985 _WorldMatrix= parentWM * _LocalMatrix;
992 // ***************************************************************************
993 void CTransform::traverseAnimDetailWithoutUpdateWorldMatrix()
995 // AnimDetail behavior: animate only if not clipped.
996 // NB: no need to test because of VisibilityList use.
998 // test if the refptr is NULL or not (RefPtr).
999 CChannelMixer *chanmix= _ChannelMixer;
1000 if(chanmix)
1002 // eval detail!!
1003 chanmix->eval(true, getOwnerScene()->getAnimDetailTrav().CurrentDate);
1007 // ***************************************************************************
1008 void CTransform::traverseAnimDetail()
1010 // First, test if I must update my worldMatrix because of the ancestorSkeleton scheme
1011 updateWorldMatrixFromFather();
1013 // eval channelMixer.
1014 traverseAnimDetailWithoutUpdateWorldMatrix();
1016 // NB: if want to add something, do it in traverseAnimDetailWithoutUpdateWorldMatrix(), because
1017 // CSkeletonModel doesn't call CTransform::traverseAnimDetail()
1021 // ***************************************************************************
1022 // ***************************************************************************
1023 // LoadBalancing
1024 // ***************************************************************************
1025 // ***************************************************************************
1028 // ***************************************************************************
1029 void CTransform::traverseLoadBalancing()
1031 // noop
1035 // ***************************************************************************
1036 // ***************************************************************************
1037 // Lighting.
1038 // ***************************************************************************
1039 // ***************************************************************************
1042 // ***************************************************************************
1043 void CTransform::resetLighting()
1045 // if the model is already isNeedUpdateLighting, his light setup is reseted.
1046 // so no need to reset again
1048 if(isNeedUpdateLighting())
1049 return;
1052 // For all light not in FrozenStaticLightSetup, remove me from their list
1053 uint startLight= 0;
1054 if(_LightContribution.FrozenStaticLightSetup)
1056 startLight= _LightContribution.NumFrozenStaticLight;
1059 // for all light in the list, remove me from their list.
1060 for(uint i=startLight; i<NL3D_MAX_LIGHT_CONTRIBUTION; i++)
1062 CPointLight *pl= _LightContribution.PointLight[i];
1063 // if end of list, break.
1064 if(!pl)
1065 break;
1066 else
1068 // remove me from this light.
1069 pl->removeLightedModel(_LightContribution.TransformIterator[i]);
1072 // empty the list.
1073 if(startLight<NL3D_MAX_LIGHT_CONTRIBUTION)
1074 _LightContribution.PointLight[startLight]= NULL;
1077 // the model needs to update his lighting.
1078 setStateFlag(IsNeedUpdateLighting, true);
1083 // ***************************************************************************
1084 void CTransform::freezeStaticLightSetup(CPointLight *pointLight[NL3D_MAX_LIGHT_CONTRIBUTION],
1085 uint numPointLights, uint8 sunContribution, CPointLight *frozenAmbientlight)
1087 nlassert(numPointLights <= NL3D_MAX_LIGHT_CONTRIBUTION);
1089 // resetLighting() first.
1090 resetLighting();
1092 // Enable StaticLightSetup.
1093 _LightContribution.FrozenStaticLightSetup= true;
1094 _LightContribution.NumFrozenStaticLight= uint8(numPointLights);
1095 _LightContribution.SunContribution= sunContribution;
1096 // setup the FrozenAmbientLight
1097 _LightContribution.FrozenAmbientLight= frozenAmbientlight;
1098 // Setup other pointLights
1099 uint i;
1100 for(i=0;i<numPointLights;i++)
1102 // set the light
1103 _LightContribution.PointLight[i]= pointLight[i];
1104 // Enable at max.
1105 _LightContribution.Factor[i]= 255;
1106 // Compute static AttFactor Later because don't have WorlPosition of the model here!!
1107 setStateFlag(IsNeedUpdateFrozenStaticLightSetup, true);
1109 // Do NOT set the iterator, because it is a staticLight.
1111 // End the list
1112 if(i<NL3D_MAX_LIGHT_CONTRIBUTION)
1113 _LightContribution.PointLight[i]= NULL;
1116 // ***************************************************************************
1117 void CTransform::unfreezeStaticLightSetup()
1119 // resetLighting() first.
1120 resetLighting();
1122 // Disable StaticLightSetup.
1123 _LightContribution.FrozenStaticLightSetup= false;
1124 _LightContribution.NumFrozenStaticLight= 0;
1125 // End the list
1126 _LightContribution.PointLight[0]= NULL;
1127 // No more FrozenAmbientLight
1128 _LightContribution.FrozenAmbientLight= NULL;
1130 // Don't need to update StaticLightSetup since no more exist.
1131 setStateFlag(IsNeedUpdateFrozenStaticLightSetup, false);
1135 // ***************************************************************************
1136 void CTransform::traverseLight()
1138 // if the model do not need to update his lighting, just skip.
1139 if(!isNeedUpdateLighting())
1140 return;
1143 // If a freezeStaticLightSetup() has been called on this model recently.
1144 if(isNeedUpdateFrozenStaticLightSetup())
1146 // Now, the correct matrix is computed.
1147 // get the untransformed bbox from the model.
1148 CAABBox bbox;
1149 getAABBox(bbox);
1150 // get transformed center pos of bbox
1151 CVector worldModelPos= getWorldMatrix() * bbox.getCenter();
1153 // So we can compute AttFactor for each static light influencing this static object
1154 uint numPointLights= _LightContribution.NumFrozenStaticLight;
1155 for(uint i=0;i<numPointLights;i++)
1157 const CPointLight *pl= _LightContribution.PointLight[i];
1158 // don't worry about the precision of floor, because of *255.
1159 float distToModel= (pl->getPosition() - worldModelPos).norm();
1160 sint attFactor= NLMISC::OptFastFloor( 255 * pl->computeLinearAttenuation(worldModelPos, distToModel) );
1161 _LightContribution.AttFactor[i]= (uint8)attFactor;
1164 // clean.
1165 setStateFlag(CTransform::IsNeedUpdateFrozenStaticLightSetup, false);
1169 // see CTransform::clip(), here I am Lightable(), and I have no _AncestorSkeletonModel
1170 // So I am sure that I really need to recompute my ModelLightContributions.
1171 CScene *scene= getOwnerScene();
1172 scene->getLightTrav().LightingManager.computeModelLightContributions(scene->getSunAmbient(), this,
1173 _LightContribution, _LogicInfo);
1175 // done!
1176 setStateFlag(CTransform::IsNeedUpdateLighting, false);
1180 // ***************************************************************************
1181 // ***************************************************************************
1182 // Rendering
1183 // ***************************************************************************
1184 // ***************************************************************************
1187 // ***************************************************************************
1188 void CTransform::traverseRender()
1190 // no-op
1193 // ***************************************************************************
1194 void CTransform::profileRender()
1196 // no-op
1200 // ***************************************************************************
1201 // ***************************************************************************
1202 // Hrc Linking
1203 // ***************************************************************************
1204 // ***************************************************************************
1207 // ***************************************************************************
1208 void CTransform::hrcLinkSon(CTransform *son)
1210 if(!son)
1211 return;
1213 // If not unfrozen, can't link
1214 if (son->_FreezeHRCState != CTransform::FreezeHRCStateDisabled)
1215 return;
1217 // no-op if already me.
1218 if(son->_HrcParent==this)
1219 return;
1221 // unlink from anyone
1222 son->hrcUnlink();
1224 // link son to me
1225 _HrcSons.insert(son, &son->_HrcNode);
1227 // link me to son
1228 son->_HrcParent= this;
1230 // Backup parent
1231 son->_HrcParentUnfreeze= this;
1233 // my son should recompute his worldMatrix!
1234 son->_WorldDate= -1;
1237 // ***************************************************************************
1238 void CTransform::hrcUnlink()
1240 // no-op if already NULL
1241 if(_HrcParent==NULL)
1242 return;
1244 // if ForceClipRoot flag is set, then the fx can't be linked elsewhere in the hierarchy
1245 nlassert(!getStateFlag(ForceClipRoot));
1247 // unlink my parent from me.
1248 _HrcNode.unlink();
1250 // unlink me from parent
1251 _HrcParent= NULL;
1252 _HrcParentUnfreeze= NULL;
1254 // I should recompute my worldMatrix (well not useful since not linked, but still do it...)
1255 _WorldDate= -1;
1258 // ***************************************************************************
1259 CTransform *CTransform::hrcGetChild(uint index) const
1261 nlassert(index < _HrcSons.size());
1262 return (const_cast<CTransform*>(this))->_HrcSons.begin()[index];
1266 // ***************************************************************************
1267 // ***************************************************************************
1268 // Clip Linking
1269 // ***************************************************************************
1270 // ***************************************************************************
1273 // ***************************************************************************
1274 void CTransform::clipAddChild(CTransform *son)
1276 if(!son)
1277 return;
1279 // if already linked, no-op.
1280 if(son->clipHasParent(this))
1281 return;
1283 // add a new parent entry for our son.
1284 CClipNode *clipNode= new CClipNode;
1285 son->_ClipParents.push_back(clipNode);
1287 // link the son to us
1288 clipNode->Parent= this;
1290 // link us to the son
1291 _ClipSons.insert(son, &clipNode->ClipNode);
1294 // ***************************************************************************
1295 void CTransform::clipDelChild(CTransform *son)
1297 if(!son)
1298 return;
1300 // try to remove from me from my parent
1301 son->clipDelFromParent(this);
1304 // ***************************************************************************
1305 void CTransform::clipUnlinkFromAll()
1307 // unlink from all parent clip
1308 while( clipGetNumParents() )
1310 clipDelFromParent( clipGetParent(0) );
1314 // ***************************************************************************
1315 CTransform *CTransform::clipGetParent(uint index) const
1317 nlassert(index < _ClipParents.size());
1318 return _ClipParents[index]->Parent;
1321 // ***************************************************************************
1322 CTransform *CTransform::clipGetChild(uint index) const
1324 nlassert(index < _ClipSons.size());
1325 return (const_cast<CTransform*>(this))->_ClipSons.begin()[index];
1329 // ***************************************************************************
1330 bool CTransform::clipHasParent(CTransform *parent)
1332 // search O(n) for all parents
1333 for(uint i=0;i<_ClipParents.size();i++)
1335 if(_ClipParents[i]->Parent==parent)
1336 return true;
1339 return false;
1342 // ***************************************************************************
1343 void CTransform::clipDelFromParent(CTransform *parent)
1345 // search O(n) for all Parents
1346 uint numParents= (uint)_ClipParents.size();
1347 for(uint i=0;i<numParents;i++)
1349 if(_ClipParents[i]->Parent==parent)
1351 // found! remove me from my parent list
1352 _ClipParents[i]->ClipNode.unlink();
1354 // remove this parent entry. swap with last
1355 swap(_ClipParents[i], _ClipParents[numParents-1]);
1357 // and delete last.
1358 delete _ClipParents[numParents-1];
1359 _ClipParents.resize(numParents-1);
1361 break;
1366 // ***************************************************************************
1367 void CTransform::setUserClipping(bool enable)
1369 setStateFlag (UserClipping, enable);
1372 // ***************************************************************************
1373 bool CTransform::getUserClipping() const
1375 return getStateFlag(UserClipping) != 0;
1378 // ***************************************************************************
1379 // ***************************************************************************
1380 // ShadowMap
1381 // ***************************************************************************
1382 // ***************************************************************************
1384 // ***************************************************************************
1385 void CTransform::getReceiverBBox(CAABBox &bbox)
1387 bbox.setCenter(CVector::Null);
1388 bbox.setHalfSize(CVector::Null);
1391 // ***************************************************************************
1392 void CTransform::enableCastShadowMap(bool state)
1394 bool precState= canCastShadowMap();
1396 if(modelCanCastShadowMap())
1397 setStateFlag(IsFinalShadowMapCaster, state);
1398 else
1399 setStateFlag(IsFinalShadowMapCaster, false);
1401 // if just enabled, create the shadowMap
1402 if(canCastShadowMap() && !precState)
1404 createShadowMap();
1405 // The user must have created it.
1406 nlassert(getShadowMap());
1408 // if just disabled, free ressource
1409 else if(!canCastShadowMap() && precState)
1411 deleteShadowMap();
1415 // ***************************************************************************
1416 void CTransform::forceCompute()
1418 // if father is a skeleton, force to compute the bone we are sticked to
1419 if (_FatherSkeletonModel)
1421 _FatherSkeletonModel->forceComputeBone(_FatherBoneId);
1423 else
1425 // force to compute the father
1426 if (_HrcParent)
1428 _HrcParent->forceCompute();
1431 // compute
1432 update();
1433 updateWorldMatrixFromFather();
1436 // ***************************************************************************
1437 void CTransform::setForceClipRoot(bool forceClipRoot)
1439 if (forceClipRoot == (getStateFlag(ForceClipRoot) != 0)) return;
1440 if (forceClipRoot)
1442 // unlink from previous father and link to the root
1443 hrcUnlink();
1444 if (_OwnerScene)
1446 _OwnerScene->getRoot()->hrcLinkSon(this);
1448 setClusterSystem(NULL);
1450 setStateFlag(ForceClipRoot, forceClipRoot);
1453 // ***************************************************************************
1454 UTransform *CTransform::buildMatchingUserInterfaceObject()
1456 return new UTransform(this);
1459 // ***************************************************************************
1460 void CTransform::setShadowMapDirectionZThreshold(float zthre)
1462 clamp(zthre, -1.f, 1.f);
1463 _ShadowMapDirectionZThreshold= zthre;
1466 // ***************************************************************************
1467 void CTransform::setShadowMapMaxDepth(float depth)
1469 depth= max(0.f, depth);
1470 _ShadowMapMaxDepth= depth;