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/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
;
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 !!)
57 // Hrc/Graph hierarchy
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
;
78 _TransparencyPriority
= 0;
81 // No logicInfo by default
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
;
103 QuadGridClipEnabled= 0.
105 IsUserLightable= 1 // default, the model may be lighted.
107 IsNeedUpdateLighting= 0
108 ISNeedUpdateFrozenStaticLightSetup= 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)
133 _DontUnfreezeChildren
= false;
134 _AncestorSkeletonModel
= NULL
;
135 _ClipLinkedInSonsOfAncestorSkeletonModelGroup
= false;
137 // **** Clip Init Traversal Computed Data.
140 _IndexInVisibleList
= -1;
142 // **** AnimDetail Init Traversal Computed Data.
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.
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.
176 // Must also remove me from the lightingManager.
177 // must test getOwnerScene() because of CCluster usage out of CScene (thanks to mat!! :) )
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
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
241 nlassert(_FatherSkeletonModel
);
242 _FatherSkeletonModel
->dirtSkinRenderLists();
247 // ***************************************************************************
248 void CTransform::setTransparency(bool v
)
250 bool bTmp
= getStateFlag(IsTransparent
) == 0 ? false : true;
253 setStateFlag(IsTransparent
, v
);
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;
274 setStateFlag(IsOpaque
, v
);
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
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
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!!)
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!
341 // Deriver note: else call BaseClass::getDefaultTrack(valueId);
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
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()).
387 // Then flag the frozen state.
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.
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 );
438 getOwnerScene()->getRoot()->hrcLinkSon( this );
440 // Link this object to the validateList.
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
);
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.
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).
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
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.
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
)
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
614 nlassert(_FatherSkeletonModel
);
615 _FatherSkeletonModel
->dirtSkinRenderLists();
618 // ***************************************************************************
619 void CTransform::setIsLoadbalancable(bool val
)
621 setStateFlag(IsLoadBalancable
, val
);
625 // ***************************************************************************
626 void CTransform::linkToUpdateList()
631 // If the model is not already inserted.
632 if( ! (_PrecModelToUpdate
!=NULL
|| _OwnerScene
->_UpdateModelList
==this) )
635 _NextModelToUpdate
= _OwnerScene
->_UpdateModelList
;
636 _PrecModelToUpdate
= NULL
;
637 if(_NextModelToUpdate
)
638 _NextModelToUpdate
->_PrecModelToUpdate
= this;
639 _OwnerScene
->_UpdateModelList
= this;
643 // ***************************************************************************
644 void CTransform::unlinkFromUpdateList()
649 // If the model is inserted.
650 if( _PrecModelToUpdate
!=NULL
|| _OwnerScene
->_UpdateModelList
==this )
653 if(_PrecModelToUpdate
)
654 _PrecModelToUpdate
->_NextModelToUpdate
= _NextModelToUpdate
;
656 _OwnerScene
->_UpdateModelList
= _NextModelToUpdate
;
659 if(_NextModelToUpdate
)
660 _NextModelToUpdate
->_PrecModelToUpdate
= _PrecModelToUpdate
;
663 _PrecModelToUpdate
= NULL
;
664 _NextModelToUpdate
= NULL
;
669 // ***************************************************************************
670 // ***************************************************************************
672 // ***************************************************************************
673 // ***************************************************************************
676 // ***************************************************************************
677 void CTransform::updateWorld()
679 const CMatrix
*pFatherWM
;
682 // If not root case, link to Fahter.
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
)
693 // herit _AncestorSkeletonModel
694 if (_HrcParent
->_AncestorSkeletonModel
)
695 // If my father has an _AncestorSkeletonModel, get it.
696 _AncestorSkeletonModel
= _HrcParent
->_AncestorSkeletonModel
;
698 // else I have an ancestor skel model if I am sticked/binded directly to a skeleton model.
699 _AncestorSkeletonModel
= _FatherSkeletonModel
;
704 pFatherWM
= &(CMatrix::Identity
);
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.
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
745 // Combine visibility.
748 case CHrcTrav::Herit
: _WorldVis
= visFather
; break;
749 case CHrcTrav::Hide
: _WorldVis
= false; break;
750 case CHrcTrav::Show
: _WorldVis
= true; 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.
770 // And link to SonsOfAncestorSkeletonModelGroup.
771 getOwnerScene()->SonsOfAncestorSkeletonModelGroup
->clipAddChild(this);
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.
787 // and now, link to std root.
788 getOwnerScene()->getRoot()->clipAddChild(this);
792 _ClipLinkedInSonsOfAncestorSkeletonModelGroup
= false;
797 // ***************************************************************************
798 void CTransform::traverseHrc()
800 // Recompute the matrix, according to _HrcParent matrix mode, and local matrix.
803 // Traverse the Hrc sons.
804 uint num
= hrcGetNumChildren();
805 for(uint i
=0;i
<num
;i
++)
806 hrcGetChild(i
)->traverseHrc();
810 // ***************************************************************************
811 // ***************************************************************************
813 // ***************************************************************************
814 // ***************************************************************************
817 // ***************************************************************************
818 void CTransform::setClusterSystem(CInstanceGroup
*pCS
)
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);
832 _ClusterSystem
= pCS
;
833 setStateFlag(ClusterSystemAuto
, false);
837 // ***************************************************************************
838 CInstanceGroup
* CTransform::getClusterSystem ()
840 if(getStateFlag(ClusterSystemAuto
))
841 return (CInstanceGroup
*)-1;
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
)
857 _ClipDate
= clipTrav
.CurrentDate
;
859 // clip: update Visible flag.
861 // if at least visible.
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
887 // If the instance is not filtered
888 if(scene
->getFilterRenderFlags() & _RenderFilterType
)
890 // User cliping enabled ?
891 if (_StateFlags
& UserClipping
)
899 // if visible, add to list.
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.
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.
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 // ***************************************************************************
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();
960 const CMatrix
&parentWM
= fatherTransform
->_WorldMatrix
;
961 // combine worldMatrix
962 _WorldMatrix
= parentWM
* _LocalMatrix
;
965 _WorldMatrix
= _LocalMatrix
;
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
979 // The parent matrix must be computed from a special matrix given to the skeleton model
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
;
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 // ***************************************************************************
1024 // ***************************************************************************
1025 // ***************************************************************************
1028 // ***************************************************************************
1029 void CTransform::traverseLoadBalancing()
1035 // ***************************************************************************
1036 // ***************************************************************************
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())
1052 // For all light not in FrozenStaticLightSetup, remove me from their list
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.
1068 // remove me from this light.
1069 pl
->removeLightedModel(_LightContribution
.TransformIterator
[i
]);
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.
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
1100 for(i
=0;i
<numPointLights
;i
++)
1103 _LightContribution
.PointLight
[i
]= pointLight
[i
];
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.
1112 if(i
<NL3D_MAX_LIGHT_CONTRIBUTION
)
1113 _LightContribution
.PointLight
[i
]= NULL
;
1116 // ***************************************************************************
1117 void CTransform::unfreezeStaticLightSetup()
1119 // resetLighting() first.
1122 // Disable StaticLightSetup.
1123 _LightContribution
.FrozenStaticLightSetup
= false;
1124 _LightContribution
.NumFrozenStaticLight
= 0;
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())
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.
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
;
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
);
1176 setStateFlag(CTransform::IsNeedUpdateLighting
, false);
1180 // ***************************************************************************
1181 // ***************************************************************************
1183 // ***************************************************************************
1184 // ***************************************************************************
1187 // ***************************************************************************
1188 void CTransform::traverseRender()
1193 // ***************************************************************************
1194 void CTransform::profileRender()
1200 // ***************************************************************************
1201 // ***************************************************************************
1203 // ***************************************************************************
1204 // ***************************************************************************
1207 // ***************************************************************************
1208 void CTransform::hrcLinkSon(CTransform
*son
)
1213 // If not unfrozen, can't link
1214 if (son
->_FreezeHRCState
!= CTransform::FreezeHRCStateDisabled
)
1217 // no-op if already me.
1218 if(son
->_HrcParent
==this)
1221 // unlink from anyone
1225 _HrcSons
.insert(son
, &son
->_HrcNode
);
1228 son
->_HrcParent
= this;
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
)
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.
1250 // unlink me from parent
1252 _HrcParentUnfreeze
= NULL
;
1254 // I should recompute my worldMatrix (well not useful since not linked, but still do it...)
1258 // ***************************************************************************
1259 CTransform
*CTransform::hrcGetChild(uint index
) const
1261 nlassert(index
< _HrcSons
.size());
1262 return (const_cast<CTransform
*>(this))->_HrcSons
.begin()[index
];
1266 // ***************************************************************************
1267 // ***************************************************************************
1269 // ***************************************************************************
1270 // ***************************************************************************
1273 // ***************************************************************************
1274 void CTransform::clipAddChild(CTransform
*son
)
1279 // if already linked, no-op.
1280 if(son
->clipHasParent(this))
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
)
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
)
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]);
1358 delete _ClipParents
[numParents
-1];
1359 _ClipParents
.resize(numParents
-1);
1366 // ***************************************************************************
1367 void CTransform::setUserClipping(bool enable
)
1369 setStateFlag (UserClipping
, enable
);
1372 // ***************************************************************************
1373 bool CTransform::getUserClipping() const
1375 return getStateFlag(UserClipping
) != 0;
1378 // ***************************************************************************
1379 // ***************************************************************************
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
);
1399 setStateFlag(IsFinalShadowMapCaster
, false);
1401 // if just enabled, create the shadowMap
1402 if(canCastShadowMap() && !precState
)
1405 // The user must have created it.
1406 nlassert(getShadowMap());
1408 // if just disabled, free ressource
1409 else if(!canCastShadowMap() && precState
)
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
);
1425 // force to compute the father
1428 _HrcParent
->forceCompute();
1433 updateWorldMatrixFromFather();
1436 // ***************************************************************************
1437 void CTransform::setForceClipRoot(bool forceClipRoot
)
1439 if (forceClipRoot
== (getStateFlag(ForceClipRoot
) != 0)) return;
1442 // unlink from previous father and link to the root
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
;