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/misc/hierarchical_timer.h"
20 #include "nel/3d/skeleton_model.h"
21 #include "nel/3d/hrc_trav.h"
22 #include "nel/3d/clip_trav.h"
23 #include "nel/3d/anim_detail_trav.h"
24 #include "nel/3d/render_trav.h"
25 #include "nel/3d/skeleton_shape.h"
26 #include "nel/3d/scene.h"
27 #include "nel/3d/lod_character_manager.h"
28 #include "nel/3d/lod_character_shape.h"
29 #include "nel/misc/rgba.h"
30 #include "nel/misc/aabbox.h"
31 #include "nel/3d/vertex_stream_manager.h"
32 #include "nel/3d/mesh_base_instance.h"
33 #include "nel/3d/async_texture_manager.h"
37 using namespace NLMISC
;
47 // ***************************************************************************
48 void CSkeletonModel::registerBasic()
50 CScene::registerModel(SkeletonModelId
, TransformShapeId
, CSkeletonModel::creator
);
54 // ***************************************************************************
55 CTrackDefaultString
CSkeletonModel::_DefaultSpawnScript
;
58 // ***************************************************************************
59 IAnimatedValue
*CSkeletonModel::getValue (uint valueId
)
64 case SpawnScriptValue
: return &_SpawnScript
;
67 return CTransformShape::getValue(valueId
);
70 // ***************************************************************************
71 const char *CSkeletonModel::getValueName (uint valueId
) const
76 case SpawnScriptValue
: return getSpawnScriptValueName();
79 return CTransformShape::getValueName(valueId
);
82 // ***************************************************************************
83 ITrack
*CSkeletonModel::getDefaultTrack (uint valueId
)
88 case SpawnScriptValue
: return &_DefaultSpawnScript
;
91 return CTransformShape::getDefaultTrack(valueId
);
94 // ***************************************************************************
95 void CSkeletonModel::registerToChannelMixer(CChannelMixer
*chanMixer
, const std::string
&prefix
)
97 /* add the Spawn Script value. The animation is evaluated at detail time, as the script evaluation.
98 This seems dangerous (create and delete models at evalDetail time) but works because:
99 - deletedModels() in a current CScene::render() are delayed to end of render()
100 and are "temp removed" from the render trav
101 - createdModels() in CSkeletonSpawnScript are delayed to the end of CScene::render()
102 - if a skeleton is not visible, or in LOD form, then its sticked SpawnedModels are not visible too,
103 whether or not they are too old regarding the animation time
105 _SpawnScriptChannelId
= addValue(chanMixer
, SpawnScriptValue
, OwnerBit
, prefix
, true);
108 CTransformShape::registerToChannelMixer(chanMixer
, prefix
);
111 for(uint i
=0;i
<Bones
.size();i
++)
114 Bones
[i
].registerToChannelMixer(chanMixer
, prefix
+ Bones
[i
].getBoneName() + ".");
119 // ***************************************************************************
120 CSkeletonModel::CSkeletonModel()
122 IAnimatable::resize(AnimValueLast
);
123 _DisplayedAsLodCharacter
= false;
124 _LodCharacterDistance
= 0;
125 _OOLodCharacterDistance
= 0;
128 _DefaultMRMSetup
= true;
130 _SkinToRenderDirty
= false;
132 _CLodVertexAlphaDirty
= true;
134 _LodEmit
= CRGBA::Black
;
136 // Inform the transform that I am a skeleton
137 CTransform::setIsSkeleton(true);
139 // By default, no skins, hence, impossible to have transparent pass. But opaque pass is always possible
140 // because of CLod rendering
142 setTransparency(false);
144 // AnimDetail behavior: Must be traversed in AnimDetail, even if no channel mixer registered
145 CTransform::setIsForceAnimDetail(true);
147 // LoadBalancing behavior. true because directly act on skins to draw all their MRM level
148 CTransform::setIsLoadbalancable(true);
150 // Lighting behavior. Lightable because skins/stickedObjects may surely need its LightContribution
151 CTransform::setIsLightable(true);
153 // Render behavior. Always renderable, because either render the skeleton as a CLod, or render skins
154 CTransform::setIsRenderable(true);
156 // build a bug-free level detail
157 buildDefaultLevelDetail();
159 // RenderFilter: We are a skeleton
160 _RenderFilterType
= UScene::FilterSkeleton
;
165 CTransform::setIsShadowMapCaster(true);
169 _SSSWOPos
= CVector::Null
;
170 _SSSWODir
= CVector::J
;
171 _SpawnScriptChannelId
= -1;
175 // ***************************************************************************
176 CSkeletonModel::~CSkeletonModel()
178 // if initModel() called
181 // release the _SpawnScriptEvaluator (delete any instance sticked)
182 _SpawnScriptEvaluator
.release(getOwnerScene());
185 getOwnerScene()->eraseSkeletonModelToList(_ItSkeletonInScene
);
189 // detach skeleton sons from skins.
190 while(_Skins
.begin()!=_Skins
.end())
192 detachSkeletonSon(*_Skins
.begin());
195 // detach skeleton sons from sticked objects.
196 while(_StickedObjects
.begin()!=_StickedObjects
.end())
198 detachSkeletonSon(*_StickedObjects
.begin());
202 setLodCharacterShape(-1);
204 // delete the shadowMap
209 // ***************************************************************************
210 void CSkeletonModel::initModel()
212 // Link this skeleton to the CScene.
213 _ItSkeletonInScene
= getOwnerScene()->appendSkeletonModelToList(this);
216 CTransformShape::initModel();
220 // ***************************************************************************
221 void CSkeletonModel::initBoneUsages()
224 _BoneUsage
.resize(Bones
.size());
225 for(uint i
=0; i
<_BoneUsage
.size(); i
++)
227 _BoneUsage
[i
].Usage
= 0;
228 _BoneUsage
[i
].ForcedUsage
= 0;
229 _BoneUsage
[i
].CLodForcedUsage
= 0;
230 _BoneUsage
[i
].MustCompute
= 0;
231 _BoneUsage
[i
].ValidBoneSkinMatrix
= 0;
233 // reserve space for bone to compute
234 _BoneToCompute
.reserve(Bones
.size());
236 _BoneToComputeDirty
= false;
239 // Default is 0.5 meters.
240 _LodInterpMultiplier
= 1.f
/ 0.5f
;
244 // ***************************************************************************
245 void CSkeletonModel::incBoneUsage(uint i
, TBoneUsageType boneUsageType
)
247 nlassert(i
<_BoneUsage
.size());
249 // Get ptr on according refCount
251 if(boneUsageType
== UsageNormal
)
252 usagePtr
= &_BoneUsage
[i
].Usage
;
253 else if(boneUsageType
== UsageForced
)
254 usagePtr
= &_BoneUsage
[i
].ForcedUsage
;
256 usagePtr
= &_BoneUsage
[i
].CLodForcedUsage
;
258 // If the bone was not used before, must update MustCompute.
260 _BoneToComputeDirty
= true;
262 // Inc the refCount of the bone.
263 nlassert(*usagePtr
<65535);
268 // ***************************************************************************
269 void CSkeletonModel::decBoneUsage(uint i
, TBoneUsageType boneUsageType
)
271 nlassert(i
<_BoneUsage
.size());
273 // Get ptr on according refCount
275 if(boneUsageType
== UsageNormal
)
276 usagePtr
= &_BoneUsage
[i
].Usage
;
277 else if(boneUsageType
== UsageForced
)
278 usagePtr
= &_BoneUsage
[i
].ForcedUsage
;
280 usagePtr
= &_BoneUsage
[i
].CLodForcedUsage
;
282 // If the bone was used before (and now won't be more), must update MustCompute.
284 _BoneToComputeDirty
= true;
286 // Inc the refCount of the bone.
287 nlassert(*usagePtr
>0);
292 // ***************************************************************************
293 void CSkeletonModel::flagBoneAndParents(uint32 boneId
, std::vector
<bool> &boneUsage
) const
295 nlassert( boneUsage
.size()==Bones
.size() );
296 nlassert( boneId
<Bones
.size() );
299 boneUsage
[boneId
]= true;
301 // if has father, flag it (recurs).
302 sint fatherId
= Bones
[boneId
].getFatherId();
304 flagBoneAndParents(fatherId
, boneUsage
);
308 // ***************************************************************************
309 void CSkeletonModel::incForcedBoneUsageAndParents(uint i
, bool forceCLod
)
312 incBoneUsage(i
, forceCLod
?UsageCLodForced
:UsageForced
);
315 sint fatherId
= Bones
[i
].getFatherId();
316 // if not a root bone...
318 incForcedBoneUsageAndParents(fatherId
, forceCLod
);
321 // ***************************************************************************
322 void CSkeletonModel::decForcedBoneUsageAndParents(uint i
, bool forceCLod
)
325 decBoneUsage(i
, forceCLod
?UsageCLodForced
:UsageForced
);
328 sint fatherId
= Bones
[i
].getFatherId();
329 // if not a root bone...
331 decForcedBoneUsageAndParents(fatherId
, forceCLod
);
335 // ***************************************************************************
336 void CSkeletonModel::updateBoneToCompute()
338 // If already computed, skip
339 if(!_BoneToComputeDirty
)
342 // get the channelMixer owned by CTransform.
343 CChannelMixer
*chanMixer
= getChannelMixer();
345 // Get Lod infos from skeletonShape
346 CSkeletonShape
*skeShape
= (CSkeletonShape
*)(IShape
*)Shape
;
347 const CSkeletonShape::CLod
&lod
= skeShape
->getLod(_CurLod
);
349 // reset _BoneToCompute
350 _BoneToCompute
.clear();
353 for(uint i
=0; i
<_BoneUsage
.size(); i
++)
355 // If we are in CLod mode
356 if(isDisplayedAsLodCharacter())
357 // don't compute the bone
358 _BoneUsage
[i
].MustCompute
= 0;
361 // set MustCompute to non 0 if (Usage && Lod) || ForcedUsage;
362 _BoneUsage
[i
].MustCompute
= (_BoneUsage
[i
].Usage
& lod
.ActiveBones
[i
]) | _BoneUsage
[i
].ForcedUsage
;
364 // if CLodForcedUsage for the bone, it must be computed, whatever _DisplayedAsLodCharacter state
365 _BoneUsage
[i
].MustCompute
|= _BoneUsage
[i
].CLodForcedUsage
;
367 // If the bone must be computed (if !0)
368 if(_BoneUsage
[i
].MustCompute
)
370 // lodEnable the channels of this bone
372 Bones
[i
].lodEnableChannels(chanMixer
, true);
374 // This bone is computed => take his valid boneSkinMatrix.
375 _BoneUsage
[i
].ValidBoneSkinMatrix
= i
;
377 // Append to the list to compute.
381 sint fatherId
= Bones
[i
].getFatherId();
386 bc
.Father
= &Bones
[fatherId
];
388 bc
.MustInterpolate
= false;
389 const CSkeletonShape::CLod
*lodNext
= NULL
;
390 // if a lod exist after current lod, and if lod interpolation enabled
391 if( _CurLod
< skeShape
->getNumLods()-1 && _LodInterpMultiplier
>0 )
394 lodNext
= &skeShape
->getLod(_CurLod
+1);
395 // Lod interpolation on this bone ?? only if at next lod, the bone is disabled.
396 // And only if it is not enabed because of a "Forced reason"
397 // Must also have a father, esle can't interpolate.
398 if(lodNext
->ActiveBones
[i
]==0 && _BoneUsage
[i
].ForcedUsage
==0 && _BoneUsage
[i
].CLodForcedUsage
==0
400 bc
.MustInterpolate
= true;
403 _BoneToCompute
.push_back(bc
);
407 // lodDisable the channels of this bone
409 Bones
[i
].lodEnableChannels(chanMixer
, false);
411 // This bone is not computed => take the valid boneSkinMatrix of his father
412 sint fatherId
= Bones
[i
].getFatherId();
414 // just take me, even if not computed.
415 _BoneUsage
[i
].ValidBoneSkinMatrix
= i
;
417 // NB: father ValidBoneSkinMatrix already computed because of the hierarchy order of Bones array.
418 _BoneUsage
[i
].ValidBoneSkinMatrix
= _BoneUsage
[fatherId
].ValidBoneSkinMatrix
;
422 // Enable SpawnScript animation only if we are not in CLod
423 if(_SpawnScriptChannelId
>=0 && chanMixer
)
424 chanMixer
->lodEnableChannel(_SpawnScriptChannelId
, !isDisplayedAsLodCharacter());
427 _BoneToComputeDirty
= false;
431 // ***************************************************************************
432 bool CSkeletonModel::isBoneComputed(uint boneId
) const
434 if(boneId
>=_BoneUsage
.size())
437 return _BoneUsage
[boneId
].MustCompute
!=0 && isClipVisible();
441 // struct used by CSkeletonModel::forceComputeBone only
442 struct CForceComputeBoneInfo
444 CTransform
*Transform
;
445 uint StickBoneID
; // if the transform is a skeleton, gives the bone to which child of interest is sticked
448 // ***************************************************************************
449 bool CSkeletonModel::forceComputeBone(uint boneId
)
451 if(boneId
>= _BoneUsage
.size()) return false;
452 // build list of ancestor, then must build
453 std::vector
<CForceComputeBoneInfo
> ancestors
;
454 // count the number of ancestors (to avoid unwanted alloc with vector)
455 uint numAncestors
= 1;
456 CTransform
*currTransform
= this;
459 currTransform
= currTransform
->_HrcParent
? currTransform
->_HrcParent
: currTransform
->_FatherSkeletonModel
; // find father transform (maybe a skeleton or a std transform)
460 if (!currTransform
) break; // root reached ?
463 ancestors
.reserve(numAncestors
);
464 // build list of ancestor
465 currTransform
= this;
466 uint currStickBone
= boneId
;
469 // if curr transform is a skeleton, animate all bone from stick bone to the root bone
470 if (currTransform
->isSkeleton())
474 CSkeletonModel
*skel
= static_cast<CSkeletonModel
*>(currTransform
);
475 nlassert(boneId
< skel
->_BoneUsage
.size());
476 nlassert(currStickBone
< skel
->Bones
.size());
477 sint currBoneIndex
= currStickBone
;
478 // force channel mixer to eval for that bone
479 while (currBoneIndex
!= -1)
481 nlassert((uint
) currBoneIndex
< skel
->Bones
.size());
482 skel
->Bones
[currBoneIndex
].forceAnimate(*_ChannelMixer
);
483 currBoneIndex
= skel
->Bones
[currBoneIndex
].getFatherId();
489 // update stickBone ID (if father is a skeleton)
490 currStickBone
= _FatherBoneId
;
492 CForceComputeBoneInfo fcbi
;
493 fcbi
.StickBoneID
= currStickBone
;
494 fcbi
.Transform
= currTransform
;
495 ancestors
.push_back(fcbi
);
496 currTransform
= currTransform
->_HrcParent
? currTransform
->_HrcParent
: currTransform
->_FatherSkeletonModel
; // find father transform (maybe a skeleton or a std transform)
497 if (!currTransform
) break; // root reached ?
499 // bones must be recomputed from father bone to sons, so must traverse bones until root is reached to retrieve correct ordering
500 CBone
*OrderedBone
[MaxNumBones
];
502 const CMatrix
*parentWorldMatrix
= &CMatrix::Identity
;
503 for(std::vector
<CForceComputeBoneInfo
>::reverse_iterator it
= ancestors
.rbegin(); it
!= ancestors
.rend(); ++it
)
505 // update world matrix (NB : the call to getmatrix will update the local matrix)
506 it
->Transform
->setWorldMatrix(*parentWorldMatrix
* it
->Transform
->getMatrix());
507 if (it
->Transform
->isSkeleton())
509 CSkeletonModel
*skel
= static_cast<CSkeletonModel
*>(it
->Transform
);
512 nlassert(it
->StickBoneID
< skel
->Bones
.size());
513 sint currBoneIndex
= it
->StickBoneID
;
514 nlassert(currBoneIndex
!= -1);
517 nlassert(numBones
< MaxNumBones
);
518 nlassert((uint
) currBoneIndex
< skel
->Bones
.size());
519 OrderedBone
[numBones
] = &skel
->Bones
[currBoneIndex
];
520 currBoneIndex
= OrderedBone
[numBones
]->getFatherId();
523 while (currBoneIndex
!= -1);
524 const CMatrix
&modelWorldMatrix
= it
->Transform
->getWorldMatrix();
526 CBone
*fatherBone
= NULL
;
529 OrderedBone
[numBones
]->compute(fatherBone
, modelWorldMatrix
, NULL
);
530 fatherBone
= OrderedBone
[numBones
];
532 parentWorldMatrix
= &(OrderedBone
[0]->getWorldMatrix());
536 parentWorldMatrix
= &it
->Transform
->getWorldMatrix();
544 // ***************************************************************************
545 const NLMISC::CMatrix
&CSkeletonModel::getActiveBoneSkinMatrix(uint boneId
) const
547 // Get me or first father with MustCompute==true.
548 uint validBoneId
= _BoneUsage
[boneId
].ValidBoneSkinMatrix
;
549 // return his WorldMatrix.
550 return Bones
[validBoneId
].getBoneSkinMatrix();
554 // ***************************************************************************
555 bool CSkeletonModel::bindSkin(CTransform
*mi
)
558 if( !mi
->isSkinnable() )
561 // try to detach this object from any skeleton first (possibly me).
562 if(mi
->_FatherSkeletonModel
)
563 mi
->_FatherSkeletonModel
->detachSkeletonSon(mi
);
568 // advert skin transform it is skinned.
569 mi
->_FatherSkeletonModel
= this;
570 // setApplySkin() use _FatherSkeletonModel to computeBonesId() and to update current skeleton bone usage.
571 mi
->setApplySkin(true);
574 // Unlink the Skin from Hrc and clip, because SkeletonModel now does the job for him.
575 // First ensure that the transform is not frozen (unlink from some quadGrids etc...)
577 // then never re-parse in validateList/Hrc/Clip
578 mi
->unlinkFromUpdateList();
580 // ClipTrav is a graph, so must unlink from ALL olds models.
581 mi
->clipUnlinkFromAll();
582 // Ensure flag is correct
583 mi
->_ClipLinkedInSonsOfAncestorSkeletonModelGroup
= false;
586 // must recompute lod vertex alpha when LodCharacter used
587 dirtLodVertexAlpha();
588 // must recompute list of skins.
589 dirtSkinRenderLists();
594 // ***************************************************************************
595 void CSkeletonModel::stickObject(CTransform
*mi
, uint boneId
)
597 // by default don't force display of "mi" if the skeleton become in CLod state
598 stickObjectEx(mi
, boneId
, false);
600 // ***************************************************************************
601 void CSkeletonModel::stickObjectEx(CTransform
*mi
, uint boneId
, bool forceCLod
)
605 // if "mi" is a skeleton, forceCLod must be true, for correct animation purpose
606 if(dynamic_cast<CSkeletonModel
*>(mi
))
609 // try to detach this object from any skeleton first (possibly me).
610 if(mi
->_FatherSkeletonModel
)
611 mi
->_FatherSkeletonModel
->detachSkeletonSon(mi
);
614 _StickedObjects
.insert(mi
);
615 // increment the refCount usage of the bone
616 incForcedBoneUsageAndParents(boneId
, forceCLod
);
618 // advert transform of its sticked state.
619 mi
->_FatherSkeletonModel
= this;
620 mi
->_FatherBoneId
= boneId
;
621 // advert him if it is "ForceCLod" sticked
622 mi
->_ForceCLodSticked
= forceCLod
;
624 // link correctly Hrc only. ClipTrav grah updated in Hrc traversal.
627 // must recompute lod vertex alpha when LodCharacter used
628 dirtLodVertexAlpha();
630 // ***************************************************************************
631 void CSkeletonModel::detachSkeletonSon(CTransform
*tr
)
635 // If the instance is not binded/sticked to the skeleton, exit.
636 if(tr
->_FatherSkeletonModel
!=this)
639 // try to erase from StickObject.
640 _StickedObjects
.erase(tr
);
641 // try to erase from Skins.
644 // If the instance is not skinned, then it is sticked
645 bool wasSkinned
= tr
->isSkinned()!=0;
648 // Then decrement Bone Usage RefCount. Decrement from CLodForcedUsage if was sticked with forceCLod==true
649 decForcedBoneUsageAndParents(tr
->_FatherBoneId
, tr
->_ForceCLodSticked
);
653 // it is skinned, advert the skinning is no more OK.
654 // setApplySkin() use _FatherSkeletonModel to update current skeleton bone usage.
655 tr
->setApplySkin(false);
658 // advert transform it is no more sticked/skinned.
659 tr
->_FatherSkeletonModel
= NULL
;
660 tr
->_ForceCLodSticked
= false;
662 // link correctly Hrc / Clip / UpdateList...
663 getOwnerScene()->getRoot()->hrcLinkSon( tr
);
666 // No-op. ClipTrav graph/UpdateList updated in Hrc traversal.
670 // Skin case: must do the Job here.
671 // Update ClipTrav here.
672 getOwnerScene()->getRoot()->clipAddChild(tr
);
673 // Must re-add to the update list.
674 tr
->linkToUpdateList();
678 // must recompute lod vertex alpha when LodCharacter used
679 dirtLodVertexAlpha();
680 // must recompute list of skins if was skinned
682 dirtSkinRenderLists();
686 // ***************************************************************************
687 sint32
CSkeletonModel::getBoneIdByName(const std::string
&name
) const
689 CSkeletonShape
*shp
= safe_cast
<CSkeletonShape
*>((IShape
*)Shape
);
690 return shp
->getBoneIdByName(name
);
694 // ***************************************************************************
695 void CSkeletonModel::setInterpolationDistance(float dist
)
697 dist
= std::max(0.f
, dist
);
698 // disable interpolation?
700 _LodInterpMultiplier
= 0.f
;
702 _LodInterpMultiplier
= 1.f
/ dist
;
705 // ***************************************************************************
706 float CSkeletonModel::getInterpolationDistance() const
708 if(_LodInterpMultiplier
==0)
711 return 1.f
/ _LodInterpMultiplier
;
715 // ***************************************************************************
716 void CSkeletonModel::traverseAnimDetail()
718 CSkeletonShape
*skeShape
= ((CSkeletonShape
*)(IShape
*)Shape
);
720 /* NB: If "this" skeleton has an AncestorSkeletonModel displayed but "this" skeleton is clipped,
721 it will be still animDetailed.
722 So its possible sticked sons will be displayed with correct WorldMatrix (if not themselves clipped).
725 /* If the Root Skeleton Model (ie me or my AncestorSM) is asked to render a ShadowMap, BUT I am
726 in CLod Form (and visible in HRC else won't be rendered in shadowMap...), then temporarly
727 Avoid CLod!! To really compute the bones for this frame only.
729 bool tempAvoidCLod
= false;
731 if(_AncestorSkeletonModel
)
732 genShadow
= _AncestorSkeletonModel
->isGeneratingShadowMap();
734 genShadow
= isGeneratingShadowMap();
736 if(genShadow
&& isDisplayedAsLodCharacter() && isHrcVisible() )
739 // Disable it just the time of this AnimDetail
740 setDisplayLodCharacterFlag(false);
744 // Update Lod, and animate.
748 CTransformShape::traverseAnimDetail() is torn in 2 here because
749 channels may be enabled/disabled by updateBoneToCompute()
752 // First update Skeleton WorldMatrix (case where the skeleton is sticked).
753 CTransform::updateWorldMatrixFromFather();
754 // get dist from camera.
755 float dist
= (getWorldMatrix().getPos() - getOwnerScene()->getClipTrav().CamPos
).norm();
756 // Use dist to get current lod to use for this skeleton
757 uint newLod
= skeShape
->getLodForDistance( dist
);
758 if(!_IsEnableLOD
) newLod
= 0;
759 if(newLod
!= _CurLod
)
761 // set new lod to use.
763 // dirt the skeleton.
764 _BoneToComputeDirty
= true;
767 // If needed, let's know which bone has to be computed, and enable / disable (lod) channels in channelMixer.
768 updateBoneToCompute();
771 CTransformShape::traverseAnimDetailWithoutUpdateWorldMatrix();
773 // If in normal mode, must update the SpawnScript
774 if(!isDisplayedAsLodCharacter())
776 _SpawnScriptEvaluator
.evaluate(this);
779 // Prepare Lod Bone interpolation.
783 const CSkeletonShape::CLod
*lodNext
= NULL
;
784 // if a lod exist after current lod, and if lod interpolation enabled
785 if( _CurLod
< skeShape
->getNumLods()-1 && _LodInterpMultiplier
>0 && _IsEnableLOD
)
788 lodNext
= &skeShape
->getLod(_CurLod
+1);
789 // get interp value to next.
790 lodBoneInterp
= (lodNext
->Distance
- dist
) * _LodInterpMultiplier
;
791 NLMISC::clamp(lodBoneInterp
, 0.f
, 1.f
);
792 // if still 1, keep cur matrix => disable interpolation
793 if(lodBoneInterp
==1.f
)
796 // else, no interpolation
801 _CurLodInterp
= lodBoneInterp
;
808 // test if bones must be updated. either if animation change or if BoneUsage change.
809 // Retrieve the WorldMatrix of the current CTransformShape.
810 const CMatrix
&modelWorldMatrix
= getWorldMatrix();
812 // must test / update the hierarchy of Bones.
813 // Since they are orderd in depth-first order, we are sure that parent are computed before sons.
814 uint numBoneToCompute
= (uint
)_BoneToCompute
.size();
815 CSkeletonModel::CBoneCompute
*pBoneCompute
= numBoneToCompute
? &_BoneToCompute
[0] : NULL
;
816 // traverse only bones which need to be computed
817 for(;numBoneToCompute
>0;numBoneToCompute
--, pBoneCompute
++)
819 // compute the bone with his father, if any
820 pBoneCompute
->Bone
->compute( pBoneCompute
->Father
, modelWorldMatrix
, this);
822 // Lod interpolation on this bone .. only if interp is enabled now, and if bone wants it
823 if(lodNext
&& pBoneCompute
->MustInterpolate
)
825 // interpolate with my father matrix.
826 const CMatrix
&fatherMatrix
= pBoneCompute
->Father
->getBoneSkinMatrix();
827 pBoneCompute
->Bone
->interpolateBoneSkinMatrix(fatherMatrix
, lodBoneInterp
);
831 IAnimatable::clearFlag(CSkeletonModel::OwnerBit
);
834 // they will update their WorldMatrix after, because of the AnimDetail traverse scheme:
835 // traverse visible Clip models, and if skeleton, traverse Hrc sons.
838 // Restore the Initial CLod flag if needed (see above)
841 setDisplayLodCharacterFlag(true);
845 // Update Animated Skins.
847 for(uint i
=0;i
<_AnimDetailSkins
.size();i
++)
849 // traverse it. NB: updateWorldMatrixFromFather() is called but no-op because isSkinned()
850 _AnimDetailSkins
[i
]->traverseAnimDetail();
855 // ***************************************************************************
856 void CSkeletonModel::computeAllBones(const CMatrix
&modelWorldMatrix
)
858 // must test / update the hierarchy of Bones.
859 // Since they are orderd in depth-first order, we are sure that parent are computed before sons.
860 for(uint i
=0;i
<Bones
.size();i
++)
862 sint fatherId
= Bones
[i
].getFatherId();
865 // Compute root bone worldMatrix. Do not allow special AnimCtrl
866 Bones
[i
].compute( NULL
, modelWorldMatrix
, NULL
);
868 // Compute bone worldMatrix. Do not allow special AnimCtrl
869 Bones
[i
].compute( &Bones
[fatherId
], modelWorldMatrix
, NULL
);
875 // ***************************************************************************
876 void CSkeletonModel::setLodCharacterDistance(float dist
)
878 _LodCharacterDistance
= max(dist
, 0.f
);
879 if(_LodCharacterDistance
>0)
880 _OOLodCharacterDistance
= 1.0f
/_LodCharacterDistance
;
882 _OOLodCharacterDistance
= 0;
885 // ***************************************************************************
886 void CSkeletonModel::setLodCharacterShape(sint shapeId
)
888 // get a ptr on the scene which owns us, and so on the lodManager.
889 CScene
*scene
= getOwnerScene();
890 CLodCharacterManager
*mngr
= scene
->getLodCharacterManager();
892 // if mngr not setuped, noop (lod not possible).
896 // If a shape was setup, free the instance
897 if(_CLodInstance
.ShapeId
>=0)
899 mngr
->releaseInstance(_CLodInstance
);
900 _CLodInstance
.ShapeId
= -1;
904 _CLodInstance
.ShapeId
= shapeId
;
906 // if a real shape is setuped, alloc an instance
907 if(_CLodInstance
.ShapeId
>=0)
909 mngr
->initInstance(_CLodInstance
);
914 // ***************************************************************************
915 void CSkeletonModel::computeLodTexture()
918 if(_CLodInstance
.ShapeId
<0)
921 // get a ptr on the scene which owns us, and so on the lodManager.
922 CScene
*scene
= getOwnerScene();
923 CLodCharacterManager
*mngr
= scene
->getLodCharacterManager();
924 // mngr must be setuped since shape Id is >-1
926 /* Get the asyncTextureManager. This is a Hack. We use the AsyncTextureManager to store very low version of Textures
927 (kept in DXTC1 format for minimum memory overhead).
928 HENCE Lod Texture can work only with Async Textured instances!!
930 CAsyncTextureManager
*asyncMngr
= scene
->getAsyncTextureManager();
931 // if not setuped, cancel
936 // **** start process. If cannot (TextureId==no more texture space), just quit.
937 if(!mngr
->startTextureCompute(_CLodInstance
))
939 uint maxNumBmpToReset
= 0;
941 // **** For all skins which have a LodTexture setuped
942 ItTransformSet it
= _Skins
.begin();
943 for(;it
!=_Skins
.end();it
++)
945 // the skin should be a meshBaseInstance setuped to asyncTexturing
946 CMeshBaseInstance
*mbi
= dynamic_cast<CMeshBaseInstance
*>(*it
);
947 if(mbi
&& mbi
->getAsyncTextureMode() && mbi
->Shape
)
949 CMeshBase
*mb
= (CMeshBase
*)(IShape
*)(mbi
->Shape
);
950 // get the LodTexture info of this shape.
951 const CLodCharacterTexture
*lodText
= mb
->getLodCharacterTexture();
955 // Ok, compute influence of this instance on the Lod.
957 // ---- Build all bmps of the instance with help of the asyncTextureManager
958 uint numMats
= (uint
)mbi
->Materials
.size();
959 // 256 materials possibles for the lod Manager
960 numMats
= min(numMats
, 256U);
961 // for endTexturecompute
962 maxNumBmpToReset
= max(maxNumBmpToReset
, numMats
);
963 // process each materials
964 for(uint i
=0;i
<numMats
;i
++)
966 // get the manager bitmap to write to
967 CLodCharacterTmpBitmap
&dstBmp
= mngr
->getTmpBitmap(uint8(i
));
969 // if the material stage 0 is not textured, or has not a valid async id, build the bitmap with a color.
970 sint asyncTextId
= mbi
->getAsyncTextureId(i
,0);
971 const CBitmap
*coarseBitmap
= NULL
;
974 // get it from async manager
975 coarseBitmap
= asyncMngr
->getCoarseBitmap(asyncTextId
);
978 // So if we have no bmp here, build with material color, else build a texture
981 dstBmp
.build(mbi
->Materials
[i
].getDiffuse());
985 dstBmp
.build(*coarseBitmap
);
989 // ---- add the lodTextureInfo to the current texture computed
990 mngr
->addTextureCompute(_CLodInstance
, *lodText
);
995 // **** compile the process
996 mngr
->endTextureCompute(_CLodInstance
, maxNumBmpToReset
);
1001 // ***************************************************************************
1002 void CSkeletonModel::setLodCharacterAnimId(uint animId
)
1004 _CLodInstance
.AnimId
= animId
;
1007 // ***************************************************************************
1008 void CSkeletonModel::setLodCharacterAnimTime(TGlobalAnimationTime time
)
1010 _CLodInstance
.AnimTime
= time
;
1013 // ***************************************************************************
1014 void CSkeletonModel::setLodCharacterWrapMode(bool wrapMode
)
1016 _CLodInstance
.WrapMode
= wrapMode
;
1020 // ***************************************************************************
1021 float CSkeletonModel::computeDisplayLodCharacterPriority() const
1024 if(_LodCharacterDistance
>0 && _CLodInstance
.ShapeId
>=0)
1028 // Get object position, test visibility;
1029 // If has a skeleton ancestor, take his world position instead, because ours is invalid.
1030 if( _AncestorSkeletonModel
!= NULL
)
1032 // if the ancestore is clipped, quit
1033 if( !_AncestorSkeletonModel
->isClipVisible() )
1035 // take ancestor world position
1036 globalPos
= _AncestorSkeletonModel
->getWorldMatrix().getPos();
1040 // if the skeleton is clipped, quit
1041 if( !isClipVisible() )
1043 // take our world position
1044 globalPos
= getWorldMatrix().getPos();
1047 // compute distance from camera.
1048 float dist
= (getOwnerScene()->getClipTrav().CamPos
- globalPos
).norm();
1051 return dist
*_OOLodCharacterDistance
;
1058 // ***************************************************************************
1059 void CSkeletonModel::setDisplayLodCharacterFlag(bool displayCLod
)
1062 if(_LodCharacterDistance
>0 && _CLodInstance
.ShapeId
>=0)
1064 // If the flag has changed since last frame, must recompute bone Usage.
1065 if(_DisplayedAsLodCharacter
!= displayCLod
)
1066 _BoneToComputeDirty
= true;
1069 _DisplayedAsLodCharacter
= displayCLod
;
1074 // ***************************************************************************
1075 void CSkeletonModel::traverseRender()
1077 H_AUTO( NL3D_Skeleton_Render
);
1079 // render as CLod, or render Skins.
1080 if(isDisplayedAsLodCharacter())
1087 // ***************************************************************************
1088 void CSkeletonModel::computeCLodVertexAlpha(CLodCharacterManager
*mngr
)
1091 if(_CLodInstance
.ShapeId
<0)
1093 // get the lod shape,a nd check exist in the manager
1094 const CLodCharacterShape
*lodShape
= mngr
->getShape(_CLodInstance
.ShapeId
);
1099 lodShape
->startBoneAlpha(_CLodInstance
.VertexAlphas
);
1101 // build an Id map, from Skeleton Ids to the lodShapes ids. (because may be differents)
1102 static vector
<sint
> boneMap
;
1103 // reset to -1 (ie not found)
1105 boneMap
.resize(Bones
.size(), -1);
1107 // for all skeletons bones.
1108 for(i
=0; i
<boneMap
.size(); i
++)
1110 boneMap
[i
]= lodShape
->getBoneIdByName(Bones
[i
].getBoneName());;
1116 for(it
= _Skins
.begin(); it
!=_Skins
.end(); it
++)
1118 CTransform
*skin
= *it
;
1120 // get array of bone used for this skin.
1121 const vector
<sint32
> *skinUsage
= skin
->getSkinBoneUsage();
1122 // check correct skin
1125 // For all bones used
1126 for(uint i
=0; i
<skinUsage
->size(); i
++)
1128 // the id in the vector point to a bone in the skeleton. Hence use the boneMap to translate it
1129 // in the lodShape ids.
1130 sint idInLod
= boneMap
[(*skinUsage
)[i
]];
1131 // only if id found in the lod shape
1133 // add color to this bone.
1134 lodShape
->addBoneAlpha(idInLod
, _CLodInstance
.VertexAlphas
);
1140 // Parse all sticked objects
1142 for(it
= _StickedObjects
.begin(); it
!=_StickedObjects
.end(); it
++)
1144 CTransform
*object
= *it
;
1146 // get on which bone this object is linked.
1147 // use the boneMap to translate id to lodShape id.
1148 sint idInLod
= boneMap
[object
->_FatherBoneId
];
1150 // only if id found in the lod shape
1152 // add color to this bone.
1153 lodShape
->addBoneAlpha(idInLod
, _CLodInstance
.VertexAlphas
);
1161 // ***************************************************************************
1162 void CSkeletonModel::updateSkinRenderLists()
1164 // If need to update array of skins to compute
1165 if(_SkinToRenderDirty
)
1169 _SkinToRenderDirty
= false;
1171 // Reset the LevelDetail.
1172 _LevelDetail
.MinFaceUsed
= 0;
1173 _LevelDetail
.MaxFaceUsed
= 0;
1174 // If must follow default MRM setup from skins.
1175 if(_DefaultMRMSetup
)
1177 _LevelDetail
.DistanceCoarsest
= 0;
1178 _LevelDetail
.DistanceMiddle
= 0;
1179 _LevelDetail
.DistanceFinest
= 0;
1182 // reset Bone Sphere of skeleton.
1183 static std::vector
<bool> sphereEmpty
;
1184 sphereEmpty
.clear();
1185 sphereEmpty
.resize(Bones
.size(), true);
1186 for(i
=0;i
<Bones
.size();i
++)
1188 // Default sphere is centered on the bone pos, and has 0 radius.
1189 Bones
[i
]._MaxSphere
.Center
= CVector::Null
;
1190 Bones
[i
]._MaxSphere
.Radius
= 0;
1193 // Parse to count new size of the arrays, and to build MRM info, and bone Max sphere
1195 uint transparentSize
= 0;
1196 uint animDetailSize
= 0;
1198 // also test if can support fast intersection
1199 _SupportFastIntersect
= !_Skins
.empty();
1200 for(it
= _Skins
.begin();it
!=_Skins
.end();it
++)
1202 CTransform
*skin
= *it
;
1204 // If the skin is hidden, don't add it to any list!
1205 if(skin
->getVisibility()==CHrcTrav::Hide
)
1208 // if transparent, then must fill in transparent list.
1209 if(skin
->isTransparent())
1211 // else may fill in opaquelist. NB: for optimisation, don't add in opaqueList
1212 // if added to the transperent list (all materials are rendered)
1213 else if(skin
->isOpaque())
1216 // if animDetailable, then must fill list
1217 if(skin
->isAnimDetailable())
1220 // if the skin support MRM, then must update levelDetal number of faces
1221 CTransformShape
*trShape
= dynamic_cast<CTransformShape
*>(skin
);
1224 const CMRMLevelDetail
*skinLevelDetail
= trShape
->getMRMLevelDetail();
1227 // Add Faces to the Skeleton level detail
1228 _LevelDetail
.MinFaceUsed
+= skinLevelDetail
->MinFaceUsed
;
1229 _LevelDetail
.MaxFaceUsed
+= skinLevelDetail
->MaxFaceUsed
;
1230 // MRM Max skin setup.
1231 if(_DefaultMRMSetup
)
1233 // Get the maximum distance setup (ie the one which degrades the less)
1234 _LevelDetail
.DistanceCoarsest
= max(_LevelDetail
.DistanceCoarsest
, skinLevelDetail
->DistanceCoarsest
);
1235 _LevelDetail
.DistanceMiddle
= max(_LevelDetail
.DistanceMiddle
, skinLevelDetail
->DistanceMiddle
);
1236 _LevelDetail
.DistanceFinest
= max(_LevelDetail
.DistanceFinest
, skinLevelDetail
->DistanceFinest
);
1241 // Enlarge Bone BBox
1242 const std::vector
<sint32
> *boneUsage
= skin
->getSkinBoneUsage();
1243 const std::vector
<NLMISC::CBSphere
> *boneSphere
= skin
->getSkinBoneSphere();
1244 if(boneUsage
&& boneSphere
)
1246 nlassert(boneUsage
->size()==boneSphere
->size());
1247 for(i
=0;i
<boneUsage
->size();i
++)
1249 const CBSphere
&sphere
= (*boneSphere
)[i
];
1250 sint boneId
= (*boneUsage
)[i
];
1251 nlassert(boneId
<(sint
)Bones
.size());
1252 // if valid boneId, and sphere not empty (ie not -1 radius)
1253 if(boneId
>-1 && sphere
.Radius
>=0)
1255 if(sphereEmpty
[boneId
])
1257 sphereEmpty
[boneId
]= false;
1258 Bones
[boneId
]._MaxSphere
= sphere
;
1262 Bones
[boneId
]._MaxSphere
.setUnion(Bones
[boneId
]._MaxSphere
, sphere
);
1268 // Whole skeleton model Support Fast intersection only if all
1269 // displayed skins support skin intersection
1270 _SupportFastIntersect
= _SupportFastIntersect
&& skin
->supportIntersectSkin();
1273 // MRM Max skin setup.
1274 if(_DefaultMRMSetup
)
1276 // compile LevelDetail.
1277 if(_LevelDetail
.MaxFaceUsed
==0)
1278 // build a bug-free level detail
1279 buildDefaultLevelDetail();
1281 _LevelDetail
.compileDistanceSetup();
1285 _OpaqueSkins
.clear();
1286 _TransparentSkins
.clear();
1287 _AnimDetailSkins
.clear();
1288 _OpaqueSkins
.resize(opaqueSize
);
1289 _TransparentSkins
.resize(transparentSize
);
1290 _AnimDetailSkins
.resize(animDetailSize
);
1292 // ReParse, to fill array.
1294 uint transparentId
= 0;
1295 uint animDetailId
= 0;
1296 for(it
= _Skins
.begin();it
!=_Skins
.end();it
++)
1298 CTransform
*skin
= *it
;
1300 // If the skin is hidden, don't add it to any list!
1301 if(skin
->getVisibility()==CHrcTrav::Hide
)
1304 // if transparent, then must fill in transparent list.
1305 if(skin
->isTransparent())
1307 nlassert(transparentId
<transparentSize
);
1308 _TransparentSkins
[transparentId
++]= skin
;
1310 // else may fill in opaquelist. NB: for optimisation, don't add in opaqueList
1311 // if added to the transperent list (all materials are rendered)
1312 else if(skin
->isOpaque())
1314 nlassert(opaqueId
<opaqueSize
);
1315 _OpaqueSkins
[opaqueId
++]= skin
;
1318 // if animDetailable, then must fill list
1319 if(skin
->isAnimDetailable())
1321 nlassert(animDetailId
<animDetailSize
);
1322 _AnimDetailSkins
[animDetailId
++]= skin
;
1326 // set the Transparency to the skeleton only if has at least one transparent skin
1327 setTransparency( transparentSize
>0 );
1332 // ***************************************************************************
1333 void CSkeletonModel::buildDefaultLevelDetail()
1335 // Avoid divide by zero.
1336 _LevelDetail
.MinFaceUsed
= 0;
1337 _LevelDetail
.MaxFaceUsed
= 0;
1338 _LevelDetail
.DistanceFinest
= 1;
1339 _LevelDetail
.DistanceMiddle
= 2;
1340 _LevelDetail
.DistanceCoarsest
= 3;
1341 _LevelDetail
.compileDistanceSetup();
1345 // ***************************************************************************
1346 void CSkeletonModel::renderCLod()
1348 CRenderTrav
&renderTrav
= getOwnerScene()->getRenderTrav();
1349 IDriver
*drv
= renderTrav
.getDriver();
1350 CScene
*scene
= getOwnerScene();
1352 // Transparent pass? quit
1353 if(!renderTrav
.isCurrentPassOpaque())
1356 // the lod manager. no op if not here
1357 CLodCharacterManager
*mngr
= scene
->getLodCharacterManager();
1361 // Get global lighting on the instance. Suppose SunAmbient only.
1363 const CLightContribution
*lightContrib
;
1365 // the std case is to take my model lightContribution
1366 if(_AncestorSkeletonModel
==NULL
)
1367 lightContrib
= &getSkeletonLightContribution();
1368 // but if skinned/sticked (directly or not) to a skeleton, take its.
1370 lightContrib
= &_AncestorSkeletonModel
->getSkeletonLightContribution();
1372 // compute his main light contribution result. Try first with sun
1373 CRGBA mainAmbient
= scene
->getSunAmbient();
1374 CRGBA mainDiffuse
= scene
->getSunDiffuse();
1375 // modulate sun contribution
1376 mainDiffuse
.modulateFromuiRGBOnly(mainDiffuse
, lightContrib
->SunContribution
);
1377 CVector mainLightDir
= scene
->getSunDirection();
1378 // Add ambient with Lod Emit
1379 mainAmbient
.addRGBOnly(mainAmbient
, _LodEmit
);
1382 /* During night, and in the buildings, it may be better to use one of the other Points lights
1383 Test only with the first pointLight, for faster compute, even if It may fail in some cases.
1385 CPointLight
*mainPL
= lightContrib
->PointLight
[0];
1389 // get the diffuse of the pointLight, attenuated from distance and importance.
1390 plDiffuse
.modulateFromuiRGBOnly(mainPL
->getDiffuse(), lightContrib
->AttFactor
[0]);
1392 // compare the 2 diffuse
1393 uint d0
= mainDiffuse
.R
+ mainDiffuse
.G
+ mainDiffuse
.B
;
1394 uint d1
= plDiffuse
.R
+ plDiffuse
.G
+ plDiffuse
.B
;
1395 // if the pointLight is lighter, take it.
1398 // leave ambient, but take diffuse and pointLight fake Direction
1399 mainDiffuse
= plDiffuse
;
1400 mainLightDir
= getWorldMatrix().getPos() - mainPL
->getPosition();
1401 mainLightDir
.normalize();
1406 // compute colors of the lods.
1408 // NB: even if texturing is sufficient, still important for AlphaTest.
1410 // If must recompute alpha because of change of skin added/deleted
1411 if(_CLodVertexAlphaDirty
)
1413 // recompute vertex alpha
1414 computeCLodVertexAlpha(mngr
);
1415 // set _CLodVertexAlphaDirty to false.
1416 _CLodVertexAlphaDirty
= false;
1419 // render the Lod in the LodManager.
1421 // render must have been intialized
1422 nlassert(mngr
->isRendering());
1425 // add the instance to the manager.
1426 if(!mngr
->addRenderCharacterKey(_CLodInstance
, getWorldMatrix(),
1427 mainAmbient
, mainDiffuse
, mainLightDir
) )
1429 // If failed to add it because no more vertex space in the manager, retry.
1431 // close vertexBlock, compile render
1434 mngr
->beginRender(drv
, renderTrav
.CamPos
);
1436 // retry. but no-op if refail.
1437 mngr
->addRenderCharacterKey(_CLodInstance
, getWorldMatrix(),
1438 mainAmbient
, mainDiffuse
, mainLightDir
);
1443 // ***************************************************************************
1444 void CSkeletonModel::renderSkins()
1446 // Render skins according to the pass.
1447 CRenderTrav
&rdrTrav
= getOwnerScene()->getRenderTrav();
1448 // get a ptr on the driver
1449 IDriver
*drv
= rdrTrav
.getDriver();
1453 // Compute the levelOfDetail
1454 float alphaMRM
= _LevelDetail
.getLevelDetailFromPolyCount(getNumTrianglesAfterLoadBalancing());
1456 // force normalisation of normals..
1457 bool bkupNorm
= drv
->isForceNormalize();
1458 drv
->forceNormalize(true);
1462 if(rdrTrav
.isCurrentPassOpaque())
1464 // Compute in Pass Opaque only the light contribution.
1465 // Easier for skeleton: suppose lightable, no local attenuation
1467 // the std case is to take my model lightContribution
1468 if(_AncestorSkeletonModel
==NULL
)
1469 setupCurrentLightContribution(&_LightContribution
, false);
1470 // but if sticked (directly or not) to a skeleton, take its.
1472 setupCurrentLightContribution(&_AncestorSkeletonModel
->_LightContribution
, false);
1475 // Activate Driver setup: light and modelMatrix
1476 changeLightSetup( &rdrTrav
);
1477 rdrTrav
.getDriver()->setupModelMatrix(getWorldMatrix());
1480 // Render all totaly opaque skins.
1481 renderSkinList(_OpaqueSkins
, alphaMRM
);
1485 // NB: must have some transparent skins, since thee skeletonModel is traversed in the transparent pass.
1487 // Activate Driver setup: light and modelMatrix
1488 changeLightSetup( &rdrTrav
);
1489 rdrTrav
.getDriver()->setupModelMatrix(getWorldMatrix());
1492 // render all opaque/transparent skins
1493 renderSkinList(_TransparentSkins
, alphaMRM
);
1497 // bkup force normalisation.
1498 drv
->forceNormalize(bkupNorm
);
1502 // ***************************************************************************
1503 void CSkeletonModel::renderSkinList(NLMISC::CObjectVector
<CTransform
*, false> &skinList
, float alphaMRM
)
1505 CRenderTrav
&rdrTrav
= getOwnerScene()->getRenderTrav();
1507 // if the SkinManager is not possible at all, just rendered the std way.
1508 if( !rdrTrav
.getMeshSkinManager() )
1510 for(uint i
=0;i
<skinList
.size();i
++)
1512 skinList
[i
]->renderSkin(alphaMRM
);
1517 // get the meshSkinManager
1518 CVertexStreamManager
&meshSkinManager
= *rdrTrav
.getMeshSkinManager();
1520 // array (rarely allocated) of skins with grouping support
1521 static std::vector
<CTransform
*> skinsToGroup
;
1522 static std::vector
<uint
> baseVertices
;
1523 skinsToGroup
.clear();
1524 baseVertices
.clear();
1526 // get the maxVertices the manager support
1527 uint maxVertices
= meshSkinManager
.getMaxVertices();
1528 uint vertexSize
= meshSkinManager
.getVertexSize();
1530 // render any skins which do not support SkinGrouping, and fill array of skins to group
1531 for(uint i
=0;i
<skinList
.size();i
++)
1533 // If don't support, or if too big to fit in the manager, just renderSkin()
1534 if(!skinList
[i
]->supportSkinGrouping())
1536 H_AUTO( NL3D_Skin_NotGrouped
);
1537 skinList
[i
]->renderSkin(alphaMRM
);
1541 skinsToGroup
.push_back(skinList
[i
]);
1545 H_AUTO( NL3D_Skin_Grouped
);
1547 // For each skin, have an index which gives the decal of the vertices in the buffer
1548 baseVertices
.resize(skinsToGroup
.size());
1550 // while there is skin to render in group
1552 while(skinId
<skinsToGroup
.size())
1554 // space left in the manager
1555 uint remainingVertices
= maxVertices
;
1556 uint currentBaseVertex
= 0;
1559 // First pass, fill The VB.
1562 uint8
*vbDest
= meshSkinManager
.lock();
1564 // For all skins until the buffer is full
1565 uint startSkinId
= skinId
;
1566 while(skinId
<skinsToGroup
.size())
1568 // if success to fill the AGP
1569 sint numVerticesAdded
= skinsToGroup
[skinId
]->renderSkinGroupGeom(alphaMRM
, remainingVertices
,
1570 vbDest
+ vertexSize
*currentBaseVertex
);
1571 // -1 means that this skin can't render because no space left for her. Then stop for this block
1572 if(numVerticesAdded
==-1)
1574 // Else ok, get the currentBaseVertex for this skin
1575 baseVertices
[skinId
]= currentBaseVertex
;
1576 // and jump to the next place
1577 currentBaseVertex
+= numVerticesAdded
;
1578 remainingVertices
-= numVerticesAdded
;
1580 // go to the next skin
1584 // release buffer. ATI: release only vertices used.
1585 meshSkinManager
.unlock(currentBaseVertex
);
1587 // Second pass, render the primitives.
1589 meshSkinManager
.activate();
1591 /* Render any primitives that are not specular. Group specular ones into specularRdrPasses.
1592 NB: this speed a lot (specular setup is heavy)!
1594 static std::vector
<CSkinSpecularRdrPass
> specularRdrPasses
;
1595 specularRdrPasses
.clear();
1596 for(uint i
=startSkinId
;i
<skinId
;i
++)
1598 // render the skin in the current buffer
1599 skinsToGroup
[i
]->renderSkinGroupPrimitives(baseVertices
[i
], specularRdrPasses
, i
);
1602 // If any skin Specular rdrPass to render
1603 if(!specularRdrPasses
.empty())
1605 // Sort by Specular Map. HTimerInfo: take 0.0% time
1606 sort(specularRdrPasses
.begin(), specularRdrPasses
.end());
1608 // Batch Specular! HTimerInfo: take 0.2%
1609 rdrTrav
.getDriver()->startSpecularBatch();
1611 // Render all of them
1612 for(uint i
=0;i
<specularRdrPasses
.size();i
++)
1614 CSkinSpecularRdrPass
&specRdrPass
= specularRdrPasses
[i
];
1615 // render the associated skin in the current buffer
1616 skinsToGroup
[specRdrPass
.SkinIndex
]->renderSkinGroupSpecularRdrPass(specRdrPass
.RdrPassIndex
);
1619 // End Batch Specular! HTimerInfo: take 0.0%
1620 rdrTrav
.getDriver()->endSpecularBatch();
1624 // End of this block, swap to the next buffer. HTimerInfo: take 0.0%
1625 meshSkinManager
.swapVBHard();
1631 // ***************************************************************************
1632 float CSkeletonModel::getNumTriangles (float distance
)
1634 // If the skeleton is displayed as a CLod suppose 0 triangles.
1635 if( isDisplayedAsLodCharacter() )
1638 // NB: this is an approximation, but this is continious.
1639 return _LevelDetail
.getNumTriangles(distance
);
1642 // ***************************************************************************
1643 void CSkeletonModel::changeMRMDistanceSetup(float distanceFinest
, float distanceMiddle
, float distanceCoarsest
)
1646 if(distanceFinest
<0) return;
1647 if(distanceMiddle
<=distanceFinest
) return;
1648 if(distanceCoarsest
<=distanceMiddle
) return;
1651 _LevelDetail
.DistanceFinest
= distanceFinest
;
1652 _LevelDetail
.DistanceMiddle
= distanceMiddle
;
1653 _LevelDetail
.DistanceCoarsest
= distanceCoarsest
;
1656 _LevelDetail
.compileDistanceSetup();
1658 // Never more use MAX skin setup.
1659 _DefaultMRMSetup
= false;
1663 // ***************************************************************************
1664 void CSkeletonModel::resetDefaultMRMDistanceSetup()
1666 _DefaultMRMSetup
= true;
1668 // Must use Skins linked to know the MRM setup.
1669 dirtSkinRenderLists();
1673 // ***************************************************************************
1674 bool CSkeletonModel::computeRenderedBBox(NLMISC::CAABBox
&bbox
, bool computeInWorld
)
1678 tmpBBox
.setCenter(CVector::Null
);
1679 tmpBBox
.setHalfSize(CVector::Null
);
1682 // Not visible => empty bbox
1683 if(!isClipVisible())
1688 for(i
=0;i
<Bones
.size();i
++)
1690 if(isBoneComputed(i
))
1694 pos
= Bones
[i
].getWorldMatrix().getPos();
1696 pos
= Bones
[i
].getLocalSkeletonMatrix().getPos();
1700 tmpBBox
.setCenter(pos
);
1703 tmpBBox
.extend(pos
);
1717 // ***************************************************************************
1718 void CSkeletonModel::getWorldMaxBoneSpheres(std::vector
<NLMISC::CBSphere
> &dest
) const
1721 // Not visible => empty bbox
1722 if(!isClipVisible())
1724 dest
.resize(_BoneToCompute
.size());
1725 for(uint i
=0;i
<_BoneToCompute
.size();i
++)
1727 CBone
*bone
= _BoneToCompute
[i
].Bone
;
1728 bone
->_MaxSphere
.applyTransform(bone
->getWorldMatrix(), dest
[i
]);
1732 // ***************************************************************************
1733 bool CSkeletonModel::computeRenderedBBoxWithBoneSphere(NLMISC::CAABBox
&bbox
, bool computeInWorld
)
1735 // Not visible => empty bbox
1736 if(!isClipVisible())
1739 if(_BoneToCompute
.empty())
1745 updateSkinRenderLists();
1747 // **** Compute The BBox with Bones of the skeleton
1748 CVector
minBB(0.f
, 0.f
, 0.f
), maxBB(0.f
, 0.f
, 0.f
);
1749 for(uint i
=0;i
<_BoneToCompute
.size();i
++)
1751 CBone
*bone
= _BoneToCompute
[i
].Bone
;
1752 // compute the world / local sphere
1753 const CMatrix
&boneMat
= computeInWorld
? bone
->getWorldMatrix() : bone
->getLocalSkeletonMatrix();
1755 bone
->_MaxSphere
.applyTransform(boneMat
, sphere
);
1756 // compute bone min max bounding cube.
1757 CVector minBone
, maxBone
;
1758 minBone
= maxBone
= sphere
.Center
;
1759 float r
= sphere
.Radius
;
1778 minBB
.minof(minBB
, minBone
);
1779 maxBB
.maxof(maxBB
, maxBone
);
1784 bbox
.setMinMax(minBB
, maxBB
);
1789 // ***************************************************************************
1790 bool CSkeletonModel::computeCurrentBBox(NLMISC::CAABBox
&bbox
, bool forceCompute
/* = false*/, bool computeInWorld
)
1792 // animate all bones channels (detail only channels). don't bother cur lod state.
1793 CChannelMixer
*chanmix
= getChannelMixer();
1796 // Force detail evaluation.
1797 chanmix
->resetEvalDetailDate();
1798 chanmix
->eval(true, 0);
1799 chanmix
->resetEvalDetailDate();
1801 // compute all skeleton bones
1802 computeAllBones(CMatrix::Identity
);
1806 tmpBBox
.setCenter(CVector::Null
);
1807 tmpBBox
.setHalfSize(CVector::Null
);
1812 for(i
=0;i
<Bones
.size();i
++)
1814 // Is the bone used ?? (whatever bone lod, or CLod state)
1815 uint16 mustCompute
= forceCompute
? 1 : _BoneUsage
[i
].Usage
| _BoneUsage
[i
].ForcedUsage
| _BoneUsage
[i
].CLodForcedUsage
;
1817 // If the bone is used.
1822 pos
= Bones
[i
].getWorldMatrix().getPos();
1824 pos
= Bones
[i
].getLocalSkeletonMatrix().getPos();
1828 tmpBBox
.setCenter(pos
);
1831 tmpBBox
.extend(pos
);
1846 // ***************************************************************************
1847 void CSkeletonModel::getLightHotSpotInWorld(CVector
&modelPos
, float &modelRadius
) const
1849 // If the bone 0 is computed (pelvis), then return its worldMatrix
1850 if(isBoneComputed(0))
1852 modelPos
= Bones
[0].getWorldMatrix().getPos();
1856 /* Else return the skeleton pos. NB: this seems useless since bone 0 not computed means no Skins.
1857 But lighting computation is still done and may use a VisualCollisionEntity.
1858 This system cache some infos according to position. This is why we must return a position
1859 near the skeleton (else cache crash each frame => slowdown...)
1861 modelPos
= _WorldMatrix
.getPos();
1864 // Consider Skeletons as not big lightable
1869 // ***************************************************************************
1870 void CSkeletonModel::setBoneAnimCtrl(uint boneId
, IAnimCtrl
*ctrl
)
1872 if(boneId
>=Bones
.size())
1875 CBone
&bone
= Bones
[boneId
];
1878 if(ctrl
&& !bone
._AnimCtrl
)
1880 else if(!ctrl
&& bone
._AnimCtrl
)
1884 bone
._AnimCtrl
= ctrl
;
1888 // ***************************************************************************
1889 IAnimCtrl
*CSkeletonModel::getBoneAnimCtrl(uint boneId
) const
1891 if(boneId
>=Bones
.size())
1894 return Bones
[boneId
]._AnimCtrl
;
1898 // ***************************************************************************
1899 bool CSkeletonModel::fastIntersect(const NLMISC::CVector
&p0
, const NLMISC::CVector
&dir
, float &dist2D
, float &distZ
, bool computeDist2D
)
1901 if(!_SupportFastIntersect
)
1904 // no intersection by default
1908 // The skinning must be done in final RaySpace.
1910 // compute the ray matrix
1915 toRaySpace
.setArbitraryRotK(dirn
);
1916 toRaySpace
.setPos(p0
);
1917 // The skinning must be done in ray space: (RayMat-1)*skelWorldMatrix;
1918 toRaySpace
.invert();
1919 toRaySpace
*= getWorldMatrix();
1921 // displayed as a CLod?
1922 if(isDisplayedAsLodCharacter())
1924 // must do the test with the CLod, because Bones are not animated at all (hence skinning would be false)
1925 CLodCharacterManager
*mngr
= getOwnerScene()->getLodCharacterManager();
1929 // test the instance
1930 if(!mngr
->fastIntersect(_CLodInstance
, toRaySpace
, dist2D
, distZ
, computeDist2D
))
1937 for(it
= _Skins
.begin();it
!=_Skins
.end();it
++)
1939 CTransform
*skin
= *it
;
1941 // If the skin is hidden, don't test intersection!
1942 if(skin
->getVisibility()==CHrcTrav::Hide
)
1945 if(!skin
->supportIntersectSkin())
1948 // compute intersection with this skin
1949 float skinDist2D
, skinDistZ
;
1950 if(skin
->intersectSkin(toRaySpace
, skinDist2D
, skinDistZ
, computeDist2D
))
1952 // true intersection found?
1956 distZ
= min(distZ
, skinDistZ
);
1958 // else lower the distance to the skins?
1961 dist2D
= min(dist2D
, skinDist2D
);
1967 // no intersection found? set Z to 0 (to be clean)
1974 // ***************************************************************************
1975 void CSkeletonModel::remapSkinBones(const std::vector
<string
> &bonesName
, std::vector
<sint32
> &bonesId
, std::vector
<uint
> &remap
)
1977 // Resize boneId to the good size.
1978 bonesId
.resize(bonesName
.size());
1979 remap
.resize(bonesName
.size());
1981 // **** For each bones, compute remap
1982 for (uint bone
=0; bone
<remap
.size(); bone
++)
1984 // Look for the bone
1985 sint32 boneId
= getBoneIdByName (bonesName
[bone
]);
1987 // Setup the _BoneId.
1988 bonesId
[bone
]= boneId
;
1994 remap
[bone
] = (uint32
)boneId
;
2002 nlwarning ("Bone %s not found in the skeleton.", bonesName
[bone
].c_str());
2007 // ***************************************************************************
2008 // ***************************************************************************
2010 // ***************************************************************************
2011 // ***************************************************************************
2014 // ***************************************************************************
2015 void CSkeletonModel::generateShadowMap(const CVector
&lightDir
)
2017 H_AUTO( NL3D_Skeleton_GenerateShadow
);
2019 // get the driver for Texture Render
2020 CScene
*scene
= getOwnerScene();
2021 CRenderTrav
&renderTrav
= scene
->getRenderTrav();
2022 IDriver
*driver
= renderTrav
.getAuxDriver();
2027 // update ShadowMap data if needed.
2029 updateShadowMap(driver
);
2031 // compute the ProjectionMatrix.
2034 // Compute the BBox in World, with bounding Box of Bones, and with BoundingBox of sticked Objects
2036 computeWorldBBoxForShadow(bbWorld
);
2039 // Here the bbox is defined in world, hence must remove the World Pos.
2040 CMatrix localPosMatrix
;
2041 localPosMatrix
.setPos(-getWorldMatrix().getPos());
2043 // setup cameraMatrix with BBox and Enlarge For 1 pixel
2044 CMatrix cameraMatrix
;
2045 _ShadowMap
->buildCasterCameraMatrix(lightDir
, localPosMatrix
, bbWorld
, cameraMatrix
);
2050 // setup the orhtogonal frustum and viewMatrix to include all the object.
2051 driver
->setFrustum(0,1,0,1,0,1,false);
2052 driver
->setupViewMatrix(cameraMatrix
.inverted());
2054 // render the Skinned meshs, and also the Sticked Objects/Skeletons
2055 CMaterial
&castMat
= renderTrav
.getShadowMapManager().getCasterShadowMaterial();
2056 renderIntoSkeletonShadowMap(this, castMat
);
2061 // Compute the BackPoint: the first point to be shadowed.
2062 CVector backPoint
= bbWorld
.getCenter();
2063 // get the 3/4 bottom of the shape
2064 backPoint
.z
-= bbWorld
.getHalfSize().z
/2;
2065 // Use the 3/4 bottom of the BBox minus the light direction in XY.
2066 CVector ldir
= lightDir
;
2069 // NB: This way seems to works quite well, even if the worldBBox is changing every frame.
2070 float lenXY
= (CVector(bbWorld
.getHalfSize().x
, bbWorld
.getHalfSize().y
, 0)).norm();
2071 backPoint
-= ldir
*lenXY
;
2073 backPoint
-= getWorldMatrix().getPos();
2075 // Compute LocalProjectionMatrix and other infos from cameraMatrix and backPoint?
2076 _ShadowMap
->buildProjectionInfos(cameraMatrix
, backPoint
, getShadowMapMaxDepth());
2079 // ***************************************************************************
2080 CShadowMap
*CSkeletonModel::getShadowMap()
2085 // ***************************************************************************
2086 void CSkeletonModel::createShadowMap()
2088 // create the shadowMap
2091 _ShadowMap
= new CShadowMap(&getOwnerScene()->getRenderTrav().getShadowMapManager());
2092 getOwnerScene()->registerShadowCasterToList(this);
2096 // ***************************************************************************
2097 void CSkeletonModel::deleteShadowMap()
2103 getOwnerScene()->unregisterShadowCasterToList(this);
2107 // ***************************************************************************
2108 void CSkeletonModel::updateShadowMap(IDriver
* /* driver */)
2110 nlassert(_ShadowMap
);
2112 // create/update texture
2113 if(_ShadowMap
->getTextureSize()!=getOwnerScene()->getShadowMapTextureSize())
2115 _ShadowMap
->initTexture(getOwnerScene()->getShadowMapTextureSize());
2120 // ***************************************************************************
2121 void CSkeletonModel::renderShadowSkins(CMaterial
&castMat
)
2123 H_AUTO( NL3D_Skin_RenderShadow
);
2125 CRenderTrav
&rdrTrav
= getOwnerScene()->getRenderTrav();
2126 // Render Shadow in auxiliary driver.
2127 IDriver
*driver
= rdrTrav
.getAuxDriver();
2129 // if the SkinManager is not possible at all, just rendered the std way
2130 if( !rdrTrav
.getShadowMeshSkinManager() )
2133 // ABORT!! ... avoid Mesh Shadowing (free shadowMap)? Replace with a dummy Shadow?
2134 // For now, no-op...
2140 // get the meshSkinManager
2141 CVertexStreamManager
&meshSkinManager
= *rdrTrav
.getShadowMeshSkinManager();
2143 // array (rarely allocated) of skins with grouping support
2144 static std::vector
<CTransform
*> skinsToGroup
;
2145 static std::vector
<uint
> baseVertices
;
2146 skinsToGroup
.clear();
2147 baseVertices
.clear();
2149 // get the maxVertices the manager support
2150 uint maxVertices
= meshSkinManager
.getMaxVertices();
2151 uint vertexSize
= meshSkinManager
.getVertexSize();
2153 // fill array of skins to group (suppose all support else won't be rendered)
2154 for(i
=0;i
<_OpaqueSkins
.size();i
++)
2156 if(_OpaqueSkins
[i
]->supportShadowSkinGrouping())
2157 skinsToGroup
.push_back(_OpaqueSkins
[i
]);
2159 for(i
=0;i
<_TransparentSkins
.size();i
++)
2161 if(_TransparentSkins
[i
]->supportShadowSkinGrouping())
2162 skinsToGroup
.push_back(_TransparentSkins
[i
]);
2165 // For each skin, have an index which gives the decal of the vertices in the buffer
2166 baseVertices
.resize(skinsToGroup
.size());
2168 // while there is skin to render in group
2170 while(skinId
<skinsToGroup
.size())
2172 // space left in the manager
2173 uint remainingVertices
= maxVertices
;
2174 uint currentBaseVertex
= 0;
2176 // First pass, fill The VB.
2179 uint8
*vbDest
= meshSkinManager
.lock();
2181 // For all skins until the buffer is full
2182 uint startSkinId
= skinId
;
2183 while(skinId
<skinsToGroup
.size())
2185 // if success to fill the AGP
2186 sint numVerticesAdded
= skinsToGroup
[skinId
]->renderShadowSkinGeom(remainingVertices
,
2187 vbDest
+ vertexSize
*currentBaseVertex
);
2188 // -1 means that this skin can't render because no space left for her. Then stop for this block
2189 if(numVerticesAdded
==-1)
2191 // Else ok, get the currentBaseVertex for this skin
2192 baseVertices
[skinId
]= currentBaseVertex
;
2193 // and jump to the next place
2194 currentBaseVertex
+= numVerticesAdded
;
2195 remainingVertices
-= numVerticesAdded
;
2197 // go to the next skin
2201 // release buffer. ATI: release only vertices used.
2202 meshSkinManager
.unlock(currentBaseVertex
);
2204 // Second pass, render the primitives.
2206 meshSkinManager
.activate();
2208 // Render any primitives
2209 for(uint i
=startSkinId
;i
<skinId
;i
++)
2211 // render the skin in the current buffer
2212 skinsToGroup
[i
]->renderShadowSkinPrimitives(castMat
, driver
, baseVertices
[i
]);
2215 // End of this block, swap to the next buffer
2216 meshSkinManager
.swapVBHard();
2222 // ***************************************************************************
2223 bool CSkeletonModel::computeWorldBBoxForShadow(NLMISC::CAABBox
&worldBB
)
2227 // If even not visible, no-op
2228 if(!isHrcVisible() || !Shape
)
2231 // **** Compute The BBox with Bones of the skeleton
2232 CVector
minBB(0.f
, 0.f
, 0.f
), maxBB(0.f
, 0.f
, 0.f
);
2233 for(i
=0;i
<_BoneToCompute
.size();i
++)
2235 CBone
*bone
= _BoneToCompute
[i
].Bone
;
2236 // compute the world sphere
2237 const CMatrix
&worldMat
= bone
->getWorldMatrix();
2238 CBSphere worldSphere
;
2239 bone
->_MaxSphere
.applyTransform(worldMat
, worldSphere
);
2240 // compute bone min max bounding cube.
2241 CVector minBone
, maxBone
;
2242 minBone
= maxBone
= worldSphere
.Center
;
2243 float r
= worldSphere
.Radius
;
2258 minBB
.minof(minBB
, minBone
);
2259 maxBB
.maxof(maxBB
, maxBone
);
2263 worldBB
.setMinMax(minBB
, maxBB
);
2265 // Fake Version. Faster (-0.2 ms for 8 compute each frame) but false.
2266 for(i=0;i<_BoneToCompute.size();i++)
2268 CBone *bone= _BoneToCompute[i].Bone;
2269 const CMatrix &worldMat= bone->getWorldMatrix();
2271 worldBB.setCenter(worldMat.getPos());
2273 worldBB.extend(worldMat.getPos());
2275 worldBB.setHalfSize(worldBB.getHalfSize() *1.5f);
2279 // **** Add to this bbox the ones of the Sticked objects.
2281 for(it
= _StickedObjects
.begin();it
!=_StickedObjects
.end();it
++)
2283 CTransform
*stickModel
= *it
;
2284 // Do the same for this son (NB: recurs, may be a skeleton too!!)
2286 if(stickModel
->computeWorldBBoxForShadow(stickBB
))
2288 // Make union of the 2
2289 worldBB
= CAABBox::computeAABBoxUnion(worldBB
, stickBB
);
2297 // ***************************************************************************
2298 void CSkeletonModel::renderIntoSkeletonShadowMap(CSkeletonModel
*rootSkeleton
, CMaterial
&castMat
)
2300 // If even not visible, no-op
2301 if(!isHrcVisible() || !Shape
)
2304 // render into aux Driver
2305 IDriver
*driver
= getOwnerScene()->getRenderTrav().getAuxDriver();
2307 // **** Render the Skeleton Skins
2308 // The model Matrix is special here. It must be the Skeleton World Matrix, minus The Root Skeleton pos.
2309 CMatrix localPosMatrix
;
2310 localPosMatrix
.setRot( getWorldMatrix() );
2311 // NB: if this==rootSkeleton, then the final pos will be CVector::Null
2312 localPosMatrix
.setPos( getWorldMatrix().getPos() - rootSkeleton
->getWorldMatrix().getPos() );
2313 driver
->setupModelMatrix(localPosMatrix
);
2315 // render the skins.
2316 renderShadowSkins(castMat
);
2318 // **** Render The Sticked Objects.
2320 for(it
= _StickedObjects
.begin();it
!=_StickedObjects
.end();it
++)
2322 CTransform
*stickModel
= *it
;
2323 stickModel
->renderIntoSkeletonShadowMap(rootSkeleton
, castMat
);