// 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;
}
}