// NeL - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "std3d.h" #include "nel/3d/transform.h" #include "nel/3d/skeleton_model.h" #include "nel/3d/scene.h" #include "nel/3d/scene_group.h" #include "nel/3d/root_model.h" #include "nel/3d/u_transform.h" #include "nel/misc/fast_floor.h" #include "nel/misc/hierarchical_timer.h" using namespace NLMISC; using namespace std; namespace NL3D { // *************************************************************************** #define NL3D_TRANSFORM_DEFAULT_SHADOW_MAP_DEPTH 8.f // *************************************************************************** void CTransform::registerBasic() { CScene::registerModel( TransformId, 0, CTransform::creator); } // *************************************************************************** CTransform::CTransform() { // important to reset for destructor to know if linked or not (CCluster !!) _OwnerScene= NULL; // Hrc/Graph hierarchy _HrcParent= NULL; _HrcParentUnfreeze= NULL; _PrecModelToUpdate= NULL; _NextModelToUpdate= NULL; _TransformDirty= true; Visibility= CHrcTrav::Herit; _LastTransformableMatrixDate= 0; _FatherSkeletonModel= NULL; _ClusterSystem = NULL; _FreezeHRCState= FreezeHRCStateDisabled; _OrderingLayer = 2; _TransparencyPriority = 0; // No logicInfo by default _LogicInfo= NULL; _ForceCLodSticked= false; // default MeanColor value _MeanColor.set(255,255,255,255); // Default ShadowMap direction _ShadowMapDirectionZThreshold= -0.5f; _ShadowMapMaxDepth= NL3D_TRANSFORM_DEFAULT_SHADOW_MAP_DEPTH; // Setup some state. /* Default are: IsAnimDetailable= 0 IsLoadBalancable= 0 IsLightable= 0 IsRenderable= 0 IsTransparent= 0 IsOpaque= 1 QuadGridClipEnabled= 0. IsUserLightable= 1 // default, the model may be lighted. IsFinalLightable= 0 IsNeedUpdateLighting= 0 ISNeedUpdateFrozenStaticLightSetup= 0 IsSkeleton= 0 IsTransformShape=0 IsCluster= 0 IsMeshBaseInstance= 0 IsDeleteChannelMixer = 0; */ _StateFlags= IsOpaque | IsUserLightable; // By default, always allow rendering of Transform Models. _RenderFilterType= std::numeric_limits::max(); // By default, don't suport fast intersection detection _SupportFastIntersect= false; // **** HRC Init Traversal Computed Data. _LocalVis= CHrcTrav::Herit; _LocalMatrix.identity(); _LocalDate=0; _WorldVis= true; _WorldMatrix.identity(); // Init the _WorldDate to -1 so at first pass, _LocalDate>_WorldDate, and so // the model will be processed and so it'll may be inserted in LightingManager (for example) _WorldDate=-1; _Frozen = false; _DontUnfreezeChildren = false; _AncestorSkeletonModel= NULL; _ClipLinkedInSonsOfAncestorSkeletonModelGroup= false; // **** Clip Init Traversal Computed Data. _ClipDate= 0; _Visible=false; _IndexInVisibleList= -1; // **** AnimDetail Init Traversal Computed Data. // none // **** LoadBalancing Init Traversal Computed Data. _LoadBalancingGroup= NULL; } // *************************************************************************** CTransform::~CTransform() { // If still binded to a father skeleton if( _FatherSkeletonModel ) { /* If skinned, cannot detach me from skeleton here because detachSkeletonSon() use some virtual calls of transform: setApplySkin(). Hence, It is the deriver job to detach himself from the skeleton. NB: test isSkinned(), not isSkinnable(), since isSkinned() is not virtual .... This means that if a Mesh isSkinnable(), but never skinned, it is not asserted here. */ if( isSkinned() ) { nlstop; } else // Can detach Me. Important for UTransform sticked _FatherSkeletonModel->detachSkeletonSon(this); } // resetLighting, removing me from PointLight Transform list. // NB: not done for FrozenStaticLightSetup, because those lights don't owns me. resetLighting(); // Must also remove me from the lightingManager. // must test getOwnerScene() because of CCluster usage out of CScene (thanks to mat!! :) ) if(getOwnerScene()) { CLightTrav &lightTrav= getOwnerScene()->getLightTrav(); _LightedModelIt= lightTrav.LightingManager.eraseStaticLightedModel(_LightedModelIt); } if (getChannelMixerOwnerShip()) delete (CChannelMixer *) _ChannelMixer; // ensure the model is no more linked to the UpdateList. unlinkFromUpdateList(); // I must remove me from _VisibleList. if(_IndexInVisibleList>=0) { CClipTrav &clipTrav= getOwnerScene()->getClipTrav(); nlassert(_IndexInVisibleList < (sint)clipTrav._CurrentNumVisibleModels ); // Mark NULL. NB: faster than a CRefPtr. clipTrav._VisibleList[_IndexInVisibleList]= NULL; _IndexInVisibleList= -1; } // remove me from parents in Hrc and Clip setStateFlag(ForceClipRoot, false); // ensure that not 'glued' to the root so that the following call will succeed hrcUnlink(); clipUnlinkFromAll(); // remove mys sons. while(hrcGetNumChildren()) { hrcGetChild(0)->hrcUnlink(); } while(clipGetNumChildren()) { clipDelChild(clipGetChild(0)); } nlassert(_HrcSons.empty()); nlassert(_HrcParent==NULL); nlassert(_ClipSons.empty()); nlassert(_ClipParents.empty()); } // *************************************************************************** void CTransform::initModel() { // assign me to the default group _LoadBalancingGroup= getOwnerScene()->getLoadBalancingTrav().getDefaultGroup(); } // *************************************************************************** void CTransform::hide() { // Optim: do nothing if already set if(Visibility!= CHrcTrav::Hide) { _TransformDirty= true; Visibility= CHrcTrav::Hide; // If skinned, then must inform skeleton parent that it must recompute skin render/animDetail lists if(isSkinned()) { nlassert(_FatherSkeletonModel); _FatherSkeletonModel->dirtSkinRenderLists(); } } } // *************************************************************************** void CTransform::setTransparency(bool v) { bool bTmp = getStateFlag(IsTransparent) == 0 ? false : true; if (bTmp != v) { setStateFlag(IsTransparent, v); if(isSkinned()) { nlassert(_FatherSkeletonModel); _FatherSkeletonModel->dirtSkinRenderLists(); } } } // *************************************************************************** void CTransform::setBypassLODOpacityFlag(bool bypass) { setStateFlag(BypassLODOpacity, bypass); } // *************************************************************************** void CTransform::setOpacity(bool v) { bool bTmp = getStateFlag(IsOpaque) == 0 ? false : true; if (bTmp != v) { setStateFlag(IsOpaque, v); if(isSkinned()) { nlassert(_FatherSkeletonModel); _FatherSkeletonModel->dirtSkinRenderLists(); } } } // *************************************************************************** void CTransform::show() { // Optim: do nothing if already set if(Visibility!= CHrcTrav::Show) { _TransformDirty= true; Visibility= CHrcTrav::Show; // If skinned, then must inform skeleton parent that it must recompute skin render/animDetail lists if(isSkinned()) { nlassert(_FatherSkeletonModel); _FatherSkeletonModel->dirtSkinRenderLists(); } } } // *************************************************************************** void CTransform::heritVisibility() { // Optim: do nothing if already set if(Visibility!= CHrcTrav::Herit) { _TransformDirty= true; Visibility= CHrcTrav::Herit; // If skinned, then must inform skeleton parent that it must recompute skin render/animDetail lists if(isSkinned()) { nlassert(_FatherSkeletonModel); _FatherSkeletonModel->dirtSkinRenderLists(); } } } // *************************************************************************** CTrackDefaultVector CTransform::DefaultPos( CVector::Null ); CTrackDefaultVector CTransform::DefaultRotEuler( CVector::Null ); CTrackDefaultQuat CTransform::DefaultRotQuat( NLMISC::CQuat::Identity ); CTrackDefaultVector CTransform::DefaultScale( CVector(1,1,1) ); CTrackDefaultVector CTransform::DefaultPivot( CVector::Null ); ITrack* CTransform::getDefaultTrack (uint valueId) { // Cyril: prefer do it here in CTransform, because of CCamera, CLight etc... (which may not need a default value too!!) // what value ? switch (valueId) { case PosValue: return &DefaultPos; case RotEulerValue: return &DefaultRotEuler; case RotQuatValue: return &DefaultRotQuat; case ScaleValue: return &DefaultScale; case PivotValue: return &DefaultPivot; } // No, only ITrnasformable values! nlstop; // Deriver note: else call BaseClass::getDefaultTrack(valueId); return NULL; } // *************************************************************************** void CTransform::registerToChannelMixer(CChannelMixer *chanMixer, const std::string &prefix) { if (getChannelMixerOwnerShip() && chanMixer != _ChannelMixer) { delete _ChannelMixer; setChannelMixerOwnerShip(false); } // Hey!! we are animated!! _ChannelMixer= chanMixer; // Update flag, if we must be inserted in AnimDetail setStateFlag(IsAnimDetailable, _ChannelMixer || getStateFlag(IsForceAnimDetail) ); // If skinned, then must inform skeleton parent that it must recompute skin render/animDetail lists if(isSkinned()) { nlassert(_FatherSkeletonModel); _FatherSkeletonModel->dirtSkinRenderLists(); } // For CTransfom, channels are not detailled. addValue(chanMixer, PosValue, OwnerBit, prefix, false); addValue(chanMixer, RotEulerValue, OwnerBit, prefix, false); addValue(chanMixer, RotQuatValue, OwnerBit, prefix, false); addValue(chanMixer, ScaleValue, OwnerBit, prefix, false); addValue(chanMixer, PivotValue, OwnerBit, prefix, false); // Deriver note: if necessary, call BaseClass::registerToChannelMixer(chanMixer, prefix); } // *************************************************************************** void CTransform::freeze() { // First, update the model // _Frozen state is disabled here (in CTransform::update()). update(); // Then flag the frozen state. _Frozen= true; } // *************************************************************************** void CTransform::setDontUnfreezeChildren(bool val) { _DontUnfreezeChildren = val; } // *************************************************************************** void CTransform::freezeHRC() { // if disabled, say we are ready to validate our worldMatrix for long. if(_FreezeHRCState==FreezeHRCStateDisabled) { _FreezeHRCState= FreezeHRCStateRequest; setStateFlag(QuadGridClipEnabled, true); /* If the transform is not frozen (ie staticaly inserted in a cluster), We must be sure it will be tested against QuadGridClipManager at next ClipTrav pass. => must make this object a "moving object" at next render=> dirt _LocalMatrixDate. */ if(!_Frozen) { _TransformDirty= true; } } } // *************************************************************************** void CTransform::unfreezeHRC() { // if this model is no HRC frozen disabled if(_FreezeHRCState!=FreezeHRCStateDisabled) { // if model correctly frozen. if(_FreezeHRCState == CTransform::FreezeHRCStateEnabled ) { // Should not be linked : can't link after a freezeHRC nlassert (_HrcParent == NULL); // Set as unfreeze else, hrcLinkSon doesn't work _FreezeHRCState= FreezeHRCStateDisabled; // Link this model to the previous HRC parent. if (_HrcParentUnfreeze) _HrcParentUnfreeze->hrcLinkSon( this ); else getOwnerScene()->getRoot()->hrcLinkSon( this ); // Link this object to the validateList. linkToUpdateList(); // if lightable() if( isLightable() ) { CLightTrav &lightTrav= getOwnerScene()->getLightTrav(); // Lighting: must remove the object from the quadGrid. // NB: works if _LightedModelIt==NULL. result is that _LightedModelIt= NULL. _LightedModelIt= lightTrav.LightingManager.eraseStaticLightedModel(_LightedModelIt); } } else _FreezeHRCState= FreezeHRCStateDisabled; // unlink me from any QuadCluster, and disable QuadCluster unlinkFromQuadCluster(); setStateFlag(QuadGridClipEnabled, false); } } // *************************************************************************** void CTransform::update() { // test if the matrix has been changed in ITransformable. if(ITransformable::compareMatrixDate(_LastTransformableMatrixDate)) { _LastTransformableMatrixDate= ITransformable::getMatrixDate(); _TransformDirty= true; } // update the freezeHRC state. if(_FreezeHRCState != CTransform::FreezeHRCStateDisabled) { // if the model request to be frozen in HRC if(_FreezeHRCState == CTransform::FreezeHRCStateRequest ) { // Wait for next Hrc traversal to compute good _WorldMatrix for this model and his sons. // Also, next Hrc traversal will insert the model in the LightingManager quadGrid (if lightable) _FreezeHRCState = CTransform::FreezeHRCStateReady; } // if the model is ready to be frozen in HRC, then do it!! else if( _FreezeHRCState == CTransform::FreezeHRCStateReady ) { // Unlink this model. hrcUnlink(); // unLink this object from the validateList. NB: the list will still be correclty parsed. unlinkFromUpdateList(); // if lightable, the model is inserted in a quadgrid to update his lighting only when // dynamicLights touch him (since himself is static). if( isLightable() ) { CLightTrav &lightTrav= getOwnerScene()->getLightTrav(); // Lighting: must reinsert the object from the quadGrid. // NB: works if _LightedModelIt==NULL. result is that _LightedModelIt= NULL. _LightedModelIt= lightTrav.LightingManager.eraseStaticLightedModel(_LightedModelIt); // insert in the quadgrid. _LightedModelIt= lightTrav.LightingManager.insertStaticLightedModel(this); } // Now this model won't be tested for validation nor for worldMatrix update. End!! _FreezeHRCState = CTransform::FreezeHRCStateEnabled; } } // update _LocalMatrix if(_TransformDirty) { // update the local matrix. _LocalMatrix= getMatrix(); _LocalVis= Visibility; // update the date of the local matrix. _LocalDate= getOwnerScene()->getHrcTrav().CurrentDate; // The transform has been modified. Hence, it is no more frozen. _Frozen= false; // ok! _TransformDirty= false; } } // *************************************************************************** void CTransform::getAABBox(NLMISC::CAABBox &bbox) const { bbox.setCenter(CVector::Null); bbox.setHalfSize(CVector::Null); } // *************************************************************************** void CTransform::setLoadBalancingGroup(const std::string &group) { // Get the traversal. CLoadBalancingTrav &trav= getOwnerScene()->getLoadBalancingTrav(); // get the group from trav (create if needed), and set it. _LoadBalancingGroup= trav.getOrCreateGroup(group); } // *************************************************************************** const std::string &CTransform::getLoadBalancingGroup() const { // get the group name return _LoadBalancingGroup->Name; } // *************************************************************************** void CTransform::setMeanColor(CRGBA color) { // if the color is different from prec if(color!=_MeanColor) { // change it. _MeanColor= color; } } // *************************************************************************** void CTransform::setIsLightable(bool val) { setStateFlag(IsLightable, val); // update IsFinalLightable setStateFlag(IsFinalLightable, (getStateFlag(IsLightable) && getStateFlag(IsUserLightable)) ); } // *************************************************************************** void CTransform::setUserLightable(bool enable) { setStateFlag(IsUserLightable, enable); // update IsFinalLightable setStateFlag(IsFinalLightable, (getStateFlag(IsLightable) && getStateFlag(IsUserLightable)) ); } // *************************************************************************** void CTransform::setIsRenderable(bool val) { setStateFlag(IsRenderable, val); } // *************************************************************************** void CTransform::setIsBigLightable(bool val) { setStateFlag(IsBigLightable, val); } // *************************************************************************** void CTransform::setIsSkeleton(bool val) { setStateFlag(IsSkeleton, val); } // *************************************************************************** void CTransform::setApplySkin(bool state) { setStateFlag(IsSkinned, state); } // *************************************************************************** void CTransform::setIsForceAnimDetail(bool val) { setStateFlag(IsForceAnimDetail, val ); // Update flag, if we must be inserted in AnimDetail setStateFlag(IsAnimDetailable, _ChannelMixer || getStateFlag(IsForceAnimDetail) ); // If skinned, then must inform skeleton parent that it must recompute skin render/animDetail lists if(isSkinned()) { nlassert(_FatherSkeletonModel); _FatherSkeletonModel->dirtSkinRenderLists(); } } // *************************************************************************** void CTransform::setIsLoadbalancable(bool val) { setStateFlag(IsLoadBalancable, val ); } // *************************************************************************** void CTransform::linkToUpdateList() { if(!_OwnerScene) return; // If the model is not already inserted. if( ! (_PrecModelToUpdate!=NULL || _OwnerScene->_UpdateModelList==this) ) { // insert it. _NextModelToUpdate= _OwnerScene->_UpdateModelList; _PrecModelToUpdate= NULL; if(_NextModelToUpdate) _NextModelToUpdate->_PrecModelToUpdate= this; _OwnerScene->_UpdateModelList= this; } } // *************************************************************************** void CTransform::unlinkFromUpdateList() { if(!_OwnerScene) return; // If the model is inserted. if( _PrecModelToUpdate!=NULL || _OwnerScene->_UpdateModelList==this ) { // update prec. if(_PrecModelToUpdate) _PrecModelToUpdate->_NextModelToUpdate= _NextModelToUpdate; else _OwnerScene->_UpdateModelList= _NextModelToUpdate; // update next. if(_NextModelToUpdate) _NextModelToUpdate->_PrecModelToUpdate= _PrecModelToUpdate; // End. _PrecModelToUpdate= NULL; _NextModelToUpdate= NULL; } } // *************************************************************************** // *************************************************************************** // Hrc Trav // *************************************************************************** // *************************************************************************** // *************************************************************************** void CTransform::updateWorld() { const CMatrix *pFatherWM; bool visFather; // If not root case, link to Fahter. if(_HrcParent) { pFatherWM= &(_HrcParent->_WorldMatrix); visFather= _HrcParent->_WorldVis; // if _HrcParent is not frozen (for any reason), disable us! if (!_HrcParent->_Frozen && !_HrcParent->_DontUnfreezeChildren) _Frozen= false; // herit _AncestorSkeletonModel if (_HrcParent->_AncestorSkeletonModel) // If my father has an _AncestorSkeletonModel, get it. _AncestorSkeletonModel= _HrcParent->_AncestorSkeletonModel; else // else I have an ancestor skel model if I am sticked/binded directly to a skeleton model. _AncestorSkeletonModel= _FatherSkeletonModel; } // else, default!! else { pFatherWM= &(CMatrix::Identity); visFather= true; // at the root of the hierarchy, we have no parent, hence no FatherSkeletonModel nor _AncestorSkeletonModel. _AncestorSkeletonModel= NULL; // NB: Root is Frozen by essence :), so don't modify the frozen state here. } // Combine matrix if(_LocalDate>_WorldDate || (_HrcParent && _HrcParent->_WorldDate>_WorldDate) ) { // Must recompute the world matrix. ONLY IF I AM NOT SKINNED/STICKED TO A SKELETON in the hierarchy! if( _AncestorSkeletonModel==NULL ) { _WorldMatrix= *pFatherWM * _LocalMatrix; _WorldDate= getOwnerScene()->getHrcTrav().CurrentDate; // Add the model to the moving object list, only if I am a transform shape if (!_Frozen && isTransformShape() && !getStateFlag(ForceClipRoot)) getOwnerScene()->getHrcTrav()._MovingObjects.push_back (static_cast(this)); } } // Update dynamic lighting. /* If the model is not frozen in StaticLight, then must update lighting each frame. Even if the object doesn't move, a new dynamic light may enter in its aera. Hence we must test it in the light quadrid. StaticLight-ed Objects don't need it because they are inserted in a special quadgrid, where dynamics lights touch all StaticLight-ed object to force their computing NB: not done if _AncestorSkeletonModel!=NULL. no need because in this case, result is driven by the _LightContribution of the _AncestorSkeletonModel. */ if( !_LightContribution.FrozenStaticLightSetup && _AncestorSkeletonModel==NULL ) { // if the model is lightable reset lighting if( isLightable() ) resetLighting(); } // Combine visibility. switch(_LocalVis) { case CHrcTrav::Herit: _WorldVis= visFather; break; case CHrcTrav::Hide: _WorldVis= false; break; case CHrcTrav::Show: _WorldVis= true; break; default: break; } // If I have an ancestor Skeleton Model, I must be binded in ClipTrav to the SonsOfAncestorSkeletonModelGroup updateClipTravForAncestorSkeleton(); } // *************************************************************************** void CTransform::updateClipTravForAncestorSkeleton() { // If I have an ancestor Skeleton Model, I must be binded in ClipTrav to the SonsOfAncestorSkeletonModelGroup if(_AncestorSkeletonModel && !_ClipLinkedInSonsOfAncestorSkeletonModelGroup) { // must unlink from ALL olds models. clipUnlinkFromAll(); // And link to SonsOfAncestorSkeletonModelGroup. getOwnerScene()->SonsOfAncestorSkeletonModelGroup->clipAddChild(this); // update the flag. _ClipLinkedInSonsOfAncestorSkeletonModelGroup= true; } // else I must be binded to the standard Root. if(!_AncestorSkeletonModel && _ClipLinkedInSonsOfAncestorSkeletonModelGroup) { // verify first I am really still linked to the SonsOfAncestorSkeletonModelGroup. // This test is important, because link may have changed for any reason (portals, clipManager....). if( clipGetNumParents() == 1 && clipGetParent(0)==getOwnerScene()->SonsOfAncestorSkeletonModelGroup ) { // must unlink from ALL olds models. clipUnlinkFromAll(); // and now, link to std root. getOwnerScene()->getRoot()->clipAddChild(this); } // update the flag _ClipLinkedInSonsOfAncestorSkeletonModelGroup= false; } } // *************************************************************************** void CTransform::traverseHrc() { // Recompute the matrix, according to _HrcParent matrix mode, and local matrix. updateWorld(); // Traverse the Hrc sons. uint num= hrcGetNumChildren(); for(uint i=0;itraverseHrc(); } // *************************************************************************** // *************************************************************************** // Clip Trav // *************************************************************************** // *************************************************************************** // *************************************************************************** void CTransform::setClusterSystem(CInstanceGroup *pCS) { if (pCS != NULL) { nlassert(!getStateFlag(ForceClipRoot)); // the transform must be linked to the root, and have not cluster system when this flag is set } // Special case for the "AutoClusterSystem" when pCS==-1 if(pCS==(CInstanceGroup*)-1) { _ClusterSystem = NULL; setStateFlag(ClusterSystemAuto, true); } else { _ClusterSystem = pCS; setStateFlag(ClusterSystemAuto, false); } } // *************************************************************************** CInstanceGroup* CTransform::getClusterSystem () { if(getStateFlag(ClusterSystemAuto)) return (CInstanceGroup*)-1; else return _ClusterSystem; } // *************************************************************************** void CTransform::traverseClip() { // disable H_AUTO, because slowdown when lot of models (eg 1000-2000 tested in forest) //H_AUTO( NL3D_TransformClip ); CScene *scene= getOwnerScene(); CClipTrav &clipTrav= scene->getClipTrav(); if ((_ClipDate == clipTrav.CurrentDate) && _Visible) return; _ClipDate = clipTrav.CurrentDate; // clip: update Visible flag. _Visible= false; // if at least visible. if(_WorldVis) { // If linked to a SkeletonModel anywhere in the hierarchy, don't clip, and use skeleton model clip result. // This works because we are sons of a special node which is not in the clip traversal, and // which is traversed at end of the traversal. if( _AncestorSkeletonModel!=NULL ) { _Visible= _AncestorSkeletonModel->isClipVisible(); // Special test: if we are sticked to a skeletonModel, and if we are still visible, maybe we don't have to if(_Visible && _FatherSkeletonModel) { // if our skeletonModel father is displayed with a Lod, maybe we are not to be displayed if(_FatherSkeletonModel->isDisplayedAsLodCharacter()) { // We are visible only if we where sticked to the skeleton with forceCLod==true. // This is also true if we are actually a skeletonModel if(!_ForceCLodSticked) // otherWise we are not visible. eg: this is the case of skins and some sticked object _Visible= false; } } } // else, clip. else { // If the instance is not filtered if(scene->getFilterRenderFlags() & _RenderFilterType) { // User cliping enabled ? if (_StateFlags & UserClipping) _Visible= true; else _Visible= clip(); } } } // if visible, add to list. if(_Visible) { // add this model to the visibility list. clipTrav.addVisibleModel(this); // Has not an ancestor skeleton model? if( _AncestorSkeletonModel==NULL ) { // If needed, insert the model in the lighted list. // don't insert if has an ancestorSkeletonModel, because in this case, result is driven by // the _LightContribution of the _AncestorSkeletonModel. if( isLightable() ) scene->getLightTrav().addLightedModel(this); // If needed, insert the model in the animDetail list. // don't insert if has an ancestoreSkeletonModel, because in this case, this ancestore will // animDetail through the hierarchy... if( isAnimDetailable() ) scene->getAnimDetailTrav().addVisibleModel(this); } // If needed, Add it to the loadBalancing trav if( isLoadBalancable() ) scene->getLoadBalancingTrav().addVisibleModel(this); // If needed, insert the model in the render list. if( isRenderable() ) scene->getRenderTrav().addRenderModel(this); } // Traverse the Clip sons. uint num= clipGetNumChildren(); for(uint i=0;itraverseClip(); } // *************************************************************************** // *************************************************************************** // AnimDetail Trav // *************************************************************************** // *************************************************************************** // *************************************************************************** void CTransform::updateWorldMatrixFromFather() { // If I am not skinned, and If I have a skeleton ancestor if(!isSkinned() && _AncestorSkeletonModel ) { // Compute the HRC _WorldMatrix. // if I am not sticked. if(!_FatherSkeletonModel) { // get the normal father worldMatrix in Hrc. CTransform *fatherTransform= hrcGetParent(); // if exist if(fatherTransform) { const CMatrix &parentWM= fatherTransform->_WorldMatrix; // combine worldMatrix _WorldMatrix= parentWM * _LocalMatrix; } else _WorldMatrix= _LocalMatrix; } else { // get the worldMatrix of the bone if I am sticked (standard stick) if(!getStateFlag(SSSWO)) { const CMatrix &parentWM= _FatherSkeletonModel->Bones[_FatherBoneId].getWorldMatrix(); // combine worldMatrix _WorldMatrix= parentWM * _LocalMatrix; } // Special SkeletonSpawnScript stick else { // The parent matrix must be computed from a special matrix given to the skeleton model CMatrix parentWM; parentWM.setRot(CVector::I, _FatherSkeletonModel->getSSSWODir(), CVector::K); parentWM.normalize(CMatrix::YZX); parentWM.setPos(_FatherSkeletonModel->getSSSWOPos()); // combine worldMatrix _WorldMatrix= parentWM * _LocalMatrix; } } } } // *************************************************************************** void CTransform::traverseAnimDetailWithoutUpdateWorldMatrix() { // AnimDetail behavior: animate only if not clipped. // NB: no need to test because of VisibilityList use. // test if the refptr is NULL or not (RefPtr). CChannelMixer *chanmix= _ChannelMixer; if(chanmix) { // eval detail!! chanmix->eval(true, getOwnerScene()->getAnimDetailTrav().CurrentDate); } } // *************************************************************************** void CTransform::traverseAnimDetail() { // First, test if I must update my worldMatrix because of the ancestorSkeleton scheme updateWorldMatrixFromFather(); // eval channelMixer. traverseAnimDetailWithoutUpdateWorldMatrix(); // NB: if want to add something, do it in traverseAnimDetailWithoutUpdateWorldMatrix(), because // CSkeletonModel doesn't call CTransform::traverseAnimDetail() } // *************************************************************************** // *************************************************************************** // LoadBalancing // *************************************************************************** // *************************************************************************** // *************************************************************************** void CTransform::traverseLoadBalancing() { // noop } // *************************************************************************** // *************************************************************************** // Lighting. // *************************************************************************** // *************************************************************************** // *************************************************************************** void CTransform::resetLighting() { // if the model is already isNeedUpdateLighting, his light setup is reseted. // so no need to reset again if(isNeedUpdateLighting()) return; // For all light not in FrozenStaticLightSetup, remove me from their list uint startLight= 0; if(_LightContribution.FrozenStaticLightSetup) { startLight= _LightContribution.NumFrozenStaticLight; } // for all light in the list, remove me from their list. for(uint i=startLight; iremoveLightedModel(_LightContribution.TransformIterator[i]); } } // empty the list. if(startLightgetPosition() - worldModelPos).norm(); sint attFactor= NLMISC::OptFastFloor( 255 * pl->computeLinearAttenuation(worldModelPos, distToModel) ); _LightContribution.AttFactor[i]= (uint8)attFactor; } // clean. setStateFlag(CTransform::IsNeedUpdateFrozenStaticLightSetup, false); } // see CTransform::clip(), here I am Lightable(), and I have no _AncestorSkeletonModel // So I am sure that I really need to recompute my ModelLightContributions. CScene *scene= getOwnerScene(); scene->getLightTrav().LightingManager.computeModelLightContributions(scene->getSunAmbient(), this, _LightContribution, _LogicInfo); // done! setStateFlag(CTransform::IsNeedUpdateLighting, false); } // *************************************************************************** // *************************************************************************** // Rendering // *************************************************************************** // *************************************************************************** // *************************************************************************** void CTransform::traverseRender() { // no-op } // *************************************************************************** void CTransform::profileRender() { // no-op } // *************************************************************************** // *************************************************************************** // Hrc Linking // *************************************************************************** // *************************************************************************** // *************************************************************************** void CTransform::hrcLinkSon(CTransform *son) { if(!son) return; // If not unfrozen, can't link if (son->_FreezeHRCState != CTransform::FreezeHRCStateDisabled) return; // no-op if already me. if(son->_HrcParent==this) return; // unlink from anyone son->hrcUnlink(); // link son to me _HrcSons.insert(son, &son->_HrcNode); // link me to son son->_HrcParent= this; // Backup parent son->_HrcParentUnfreeze= this; // my son should recompute his worldMatrix! son->_WorldDate= -1; } // *************************************************************************** void CTransform::hrcUnlink() { // no-op if already NULL if(_HrcParent==NULL) return; // if ForceClipRoot flag is set, then the fx can't be linked elsewhere in the hierarchy nlassert(!getStateFlag(ForceClipRoot)); // unlink my parent from me. _HrcNode.unlink(); // unlink me from parent _HrcParent= NULL; _HrcParentUnfreeze= NULL; // I should recompute my worldMatrix (well not useful since not linked, but still do it...) _WorldDate= -1; } // *************************************************************************** CTransform *CTransform::hrcGetChild(uint index) const { nlassert(index < _HrcSons.size()); return (const_cast(this))->_HrcSons.begin()[index]; } // *************************************************************************** // *************************************************************************** // Clip Linking // *************************************************************************** // *************************************************************************** // *************************************************************************** void CTransform::clipAddChild(CTransform *son) { if(!son) return; // if already linked, no-op. if(son->clipHasParent(this)) return; // add a new parent entry for our son. CClipNode *clipNode= new CClipNode; son->_ClipParents.push_back(clipNode); // link the son to us clipNode->Parent= this; // link us to the son _ClipSons.insert(son, &clipNode->ClipNode); } // *************************************************************************** void CTransform::clipDelChild(CTransform *son) { if(!son) return; // try to remove from me from my parent son->clipDelFromParent(this); } // *************************************************************************** void CTransform::clipUnlinkFromAll() { // unlink from all parent clip while( clipGetNumParents() ) { clipDelFromParent( clipGetParent(0) ); } } // *************************************************************************** CTransform *CTransform::clipGetParent(uint index) const { nlassert(index < _ClipParents.size()); return _ClipParents[index]->Parent; } // *************************************************************************** CTransform *CTransform::clipGetChild(uint index) const { nlassert(index < _ClipSons.size()); return (const_cast(this))->_ClipSons.begin()[index]; } // *************************************************************************** bool CTransform::clipHasParent(CTransform *parent) { // search O(n) for all parents for(uint i=0;i<_ClipParents.size();i++) { if(_ClipParents[i]->Parent==parent) return true; } return false; } // *************************************************************************** void CTransform::clipDelFromParent(CTransform *parent) { // search O(n) for all Parents uint numParents= (uint)_ClipParents.size(); for(uint i=0;iParent==parent) { // found! remove me from my parent list _ClipParents[i]->ClipNode.unlink(); // remove this parent entry. swap with last swap(_ClipParents[i], _ClipParents[numParents-1]); // and delete last. delete _ClipParents[numParents-1]; _ClipParents.resize(numParents-1); break; } } } // *************************************************************************** void CTransform::setUserClipping(bool enable) { setStateFlag (UserClipping, enable); } // *************************************************************************** bool CTransform::getUserClipping() const { return getStateFlag(UserClipping) != 0; } // *************************************************************************** // *************************************************************************** // ShadowMap // *************************************************************************** // *************************************************************************** // *************************************************************************** void CTransform::getReceiverBBox(CAABBox &bbox) { bbox.setCenter(CVector::Null); bbox.setHalfSize(CVector::Null); } // *************************************************************************** void CTransform::enableCastShadowMap(bool state) { bool precState= canCastShadowMap(); if(modelCanCastShadowMap()) setStateFlag(IsFinalShadowMapCaster, state); else setStateFlag(IsFinalShadowMapCaster, false); // if just enabled, create the shadowMap if(canCastShadowMap() && !precState) { createShadowMap(); // The user must have created it. nlassert(getShadowMap()); } // if just disabled, free ressource else if(!canCastShadowMap() && precState) { deleteShadowMap(); } } // *************************************************************************** void CTransform::forceCompute() { // if father is a skeleton, force to compute the bone we are sticked to if (_FatherSkeletonModel) { _FatherSkeletonModel->forceComputeBone(_FatherBoneId); } else { // force to compute the father if (_HrcParent) { _HrcParent->forceCompute(); } } // compute update(); updateWorldMatrixFromFather(); } // *************************************************************************** void CTransform::setForceClipRoot(bool forceClipRoot) { if (forceClipRoot == (getStateFlag(ForceClipRoot) != 0)) return; if (forceClipRoot) { // unlink from previous father and link to the root hrcUnlink(); if (_OwnerScene) { _OwnerScene->getRoot()->hrcLinkSon(this); } setClusterSystem(NULL); } setStateFlag(ForceClipRoot, forceClipRoot); } // *************************************************************************** UTransform *CTransform::buildMatchingUserInterfaceObject() { return new UTransform(this); } // *************************************************************************** void CTransform::setShadowMapDirectionZThreshold(float zthre) { clamp(zthre, -1.f, 1.f); _ShadowMapDirectionZThreshold= zthre; } // *************************************************************************** void CTransform::setShadowMapMaxDepth(float depth) { depth= max(0.f, depth); _ShadowMapMaxDepth= depth; } }