Show bonus/malus timer text if available
[ryzomcore.git] / nel / src / 3d / skeleton_model.cpp
blob94b6fb896c2a27f913683086f9f225c0e1f8b301
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "std3d.h"
19 #include "nel/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"
36 using namespace std;
37 using namespace NLMISC;
39 #ifdef DEBUG_NEW
40 #define new DEBUG_NEW
41 #endif
43 namespace NL3D
47 // ***************************************************************************
48 void CSkeletonModel::registerBasic()
50 CScene::registerModel(SkeletonModelId, TransformShapeId, CSkeletonModel::creator);
54 // ***************************************************************************
55 CTrackDefaultString CSkeletonModel::_DefaultSpawnScript;
58 // ***************************************************************************
59 IAnimatedValue *CSkeletonModel::getValue (uint valueId)
61 // what value ?
62 switch (valueId)
64 case SpawnScriptValue: return &_SpawnScript;
67 return CTransformShape::getValue(valueId);
70 // ***************************************************************************
71 const char *CSkeletonModel::getValueName (uint valueId) const
73 // what value ?
74 switch (valueId)
76 case SpawnScriptValue: return getSpawnScriptValueName();
79 return CTransformShape::getValueName(valueId);
82 // ***************************************************************************
83 ITrack *CSkeletonModel::getDefaultTrack (uint valueId)
85 // what value ?
86 switch (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);
107 // add standard
108 CTransformShape::registerToChannelMixer(chanMixer, prefix);
110 // Add any bones.
111 for(uint i=0;i<Bones.size();i++)
113 // append bonename.
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;
126 _IsEnableLOD=true;
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
141 setOpacity(true);
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;
162 _AnimCtrlUsage= 0;
164 // ShadowMap
165 CTransform::setIsShadowMapCaster(true);
166 _ShadowMap= NULL;
168 // SpawnScript
169 _SSSWOPos= CVector::Null;
170 _SSSWODir= CVector::J;
171 _SpawnScriptChannelId= -1;
175 // ***************************************************************************
176 CSkeletonModel::~CSkeletonModel()
178 // if initModel() called
179 if(getOwnerScene())
181 // release the _SpawnScriptEvaluator (delete any instance sticked)
182 _SpawnScriptEvaluator.release(getOwnerScene());
184 // remove from scene
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());
201 // Free Lod instance
202 setLodCharacterShape(-1);
204 // delete the shadowMap
205 deleteShadowMap();
209 // ***************************************************************************
210 void CSkeletonModel::initModel()
212 // Link this skeleton to the CScene.
213 _ItSkeletonInScene= getOwnerScene()->appendSkeletonModelToList(this);
215 // Call base class
216 CTransformShape::initModel();
220 // ***************************************************************************
221 void CSkeletonModel::initBoneUsages()
223 // reset all to 0.
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;
237 _CurLod= 0;
238 _CurLodInterp= 1.f;
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
250 uint16 *usagePtr;
251 if(boneUsageType == UsageNormal)
252 usagePtr= &_BoneUsage[i].Usage;
253 else if(boneUsageType == UsageForced)
254 usagePtr= &_BoneUsage[i].ForcedUsage;
255 else
256 usagePtr= &_BoneUsage[i].CLodForcedUsage;
258 // If the bone was not used before, must update MustCompute.
259 if(*usagePtr==0)
260 _BoneToComputeDirty= true;
262 // Inc the refCount of the bone.
263 nlassert(*usagePtr<65535);
264 (*usagePtr)++;
268 // ***************************************************************************
269 void CSkeletonModel::decBoneUsage(uint i, TBoneUsageType boneUsageType)
271 nlassert(i<_BoneUsage.size());
273 // Get ptr on according refCount
274 uint16 *usagePtr;
275 if(boneUsageType == UsageNormal)
276 usagePtr= &_BoneUsage[i].Usage;
277 else if(boneUsageType == UsageForced)
278 usagePtr= &_BoneUsage[i].ForcedUsage;
279 else
280 usagePtr= &_BoneUsage[i].CLodForcedUsage;
282 // If the bone was used before (and now won't be more), must update MustCompute.
283 if(*usagePtr==1)
284 _BoneToComputeDirty= true;
286 // Inc the refCount of the bone.
287 nlassert(*usagePtr>0);
288 (*usagePtr)--;
292 // ***************************************************************************
293 void CSkeletonModel::flagBoneAndParents(uint32 boneId, std::vector<bool> &boneUsage) const
295 nlassert( boneUsage.size()==Bones.size() );
296 nlassert( boneId<Bones.size() );
298 // Flag this bone.
299 boneUsage[boneId]= true;
301 // if has father, flag it (recurs).
302 sint fatherId= Bones[boneId].getFatherId();
303 if(fatherId>=0)
304 flagBoneAndParents(fatherId, boneUsage);
308 // ***************************************************************************
309 void CSkeletonModel::incForcedBoneUsageAndParents(uint i, bool forceCLod)
311 // inc forced.
312 incBoneUsage(i, forceCLod?UsageCLodForced:UsageForced );
314 // recurs to father
315 sint fatherId= Bones[i].getFatherId();
316 // if not a root bone...
317 if(fatherId>=0)
318 incForcedBoneUsageAndParents(fatherId, forceCLod);
321 // ***************************************************************************
322 void CSkeletonModel::decForcedBoneUsageAndParents(uint i, bool forceCLod)
324 // dec forced
325 decBoneUsage(i, forceCLod?UsageCLodForced:UsageForced);
327 // recurs to father
328 sint fatherId= Bones[i].getFatherId();
329 // if not a root bone...
330 if(fatherId>=0)
331 decForcedBoneUsageAndParents(fatherId, forceCLod);
335 // ***************************************************************************
336 void CSkeletonModel::updateBoneToCompute()
338 // If already computed, skip
339 if(!_BoneToComputeDirty)
340 return;
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();
352 // For all bones
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;
359 else
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
371 if(chanMixer)
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.
378 //-------
379 CBoneCompute bc;
380 bc.Bone= &Bones[i];
381 sint fatherId= Bones[i].getFatherId();
382 // if a root bone...
383 if(fatherId==-1)
384 bc.Father= NULL;
385 else
386 bc.Father= &Bones[fatherId];
387 // MustInterpolate??
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 )
393 // get next lod.
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
399 && bc.Father)
400 bc.MustInterpolate= true;
402 // append
403 _BoneToCompute.push_back(bc);
405 else
407 // lodDisable the channels of this bone
408 if(chanMixer)
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();
413 if(fatherId<0)
414 // just take me, even if not computed.
415 _BoneUsage[i].ValidBoneSkinMatrix= i;
416 else
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());
426 // computed
427 _BoneToComputeDirty= false;
431 // ***************************************************************************
432 bool CSkeletonModel::isBoneComputed(uint boneId) const
434 if(boneId>=_BoneUsage.size())
435 return false;
436 else
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;
457 for(;;)
459 currTransform = currTransform->_HrcParent ? currTransform->_HrcParent : currTransform->_FatherSkeletonModel; // find father transform (maybe a skeleton or a std transform)
460 if (!currTransform) break; // root reached ?
461 ++ numAncestors;
463 ancestors.reserve(numAncestors);
464 // build list of ancestor
465 currTransform = this;
466 uint currStickBone = boneId;
467 for(;;)
469 // if curr transform is a skeleton, animate all bone from stick bone to the root bone
470 if (currTransform->isSkeleton())
472 if (_ChannelMixer)
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();
487 else
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);
510 // reorder bones
511 uint numBones = 0;
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();
521 ++ numBones;
523 while (currBoneIndex != -1);
524 const CMatrix &modelWorldMatrix = it->Transform->getWorldMatrix();
525 // recompute bones
526 CBone *fatherBone = NULL;
527 while (numBones--)
529 OrderedBone[numBones]->compute(fatherBone, modelWorldMatrix, NULL);
530 fatherBone = OrderedBone[numBones];
532 parentWorldMatrix = &(OrderedBone[0]->getWorldMatrix());
534 else
536 parentWorldMatrix = &it->Transform->getWorldMatrix();
539 return true;
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)
557 nlassert(mi);
558 if( !mi->isSkinnable() )
559 return false;
561 // try to detach this object from any skeleton first (possibly me).
562 if(mi->_FatherSkeletonModel)
563 mi->_FatherSkeletonModel->detachSkeletonSon(mi);
565 // Then Add me.
566 _Skins.insert(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...)
576 mi->unfreezeHRC();
577 // then never re-parse in validateList/Hrc/Clip
578 mi->unlinkFromUpdateList();
579 mi->hrcUnlink();
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();
591 // Ok, skinned
592 return true;
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)
603 nlassert(mi);
605 // if "mi" is a skeleton, forceCLod must be true, for correct animation purpose
606 if(dynamic_cast<CSkeletonModel*>(mi))
607 forceCLod= true;
609 // try to detach this object from any skeleton first (possibly me).
610 if(mi->_FatherSkeletonModel)
611 mi->_FatherSkeletonModel->detachSkeletonSon(mi);
613 // Then Add me.
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.
625 hrcLinkSon( mi );
627 // must recompute lod vertex alpha when LodCharacter used
628 dirtLodVertexAlpha();
630 // ***************************************************************************
631 void CSkeletonModel::detachSkeletonSon(CTransform *tr)
633 nlassert(tr);
635 // If the instance is not binded/sticked to the skeleton, exit.
636 if(tr->_FatherSkeletonModel!=this)
637 return;
639 // try to erase from StickObject.
640 _StickedObjects.erase(tr);
641 // try to erase from Skins.
642 _Skins.erase(tr);
644 // If the instance is not skinned, then it is sticked
645 bool wasSkinned= tr->isSkinned()!=0;
646 if( !wasSkinned )
648 // Then decrement Bone Usage RefCount. Decrement from CLodForcedUsage if was sticked with forceCLod==true
649 decForcedBoneUsageAndParents(tr->_FatherBoneId, tr->_ForceCLodSticked);
651 else
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 );
664 if( !wasSkinned )
666 // No-op. ClipTrav graph/UpdateList updated in Hrc traversal.
668 else
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
681 if( wasSkinned )
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?
699 if(dist==0)
700 _LodInterpMultiplier= 0.f;
701 else
702 _LodInterpMultiplier= 1.f / dist;
705 // ***************************************************************************
706 float CSkeletonModel::getInterpolationDistance() const
708 if(_LodInterpMultiplier==0)
709 return 0.f;
710 else
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;
730 bool genShadow;
731 if(_AncestorSkeletonModel)
732 genShadow= _AncestorSkeletonModel->isGeneratingShadowMap();
733 else
734 genShadow= isGeneratingShadowMap();
735 // do the test.
736 if(genShadow && isDisplayedAsLodCharacter() && isHrcVisible() )
738 tempAvoidCLod= true;
739 // Disable it just the time of this AnimDetail
740 setDisplayLodCharacterFlag(false);
744 // Update Lod, and animate.
745 //===============
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.
762 _CurLod= newLod;
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();
770 // Animate skeleton.
771 CTransformShape::traverseAnimDetailWithoutUpdateWorldMatrix();
773 // If in normal mode, must update the SpawnScript
774 if(!isDisplayedAsLodCharacter())
776 _SpawnScriptEvaluator.evaluate(this);
779 // Prepare Lod Bone interpolation.
780 //===============
782 float lodBoneInterp;
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)
787 // get next lod.
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)
794 lodNext=NULL;
796 // else, no interpolation
797 else
799 lodBoneInterp=1.f;
801 _CurLodInterp= lodBoneInterp;
804 // Compute bones
805 //===============
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);
833 // Sticked Objects:
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)
839 if(tempAvoidCLod)
841 setDisplayLodCharacterFlag(true);
845 // Update Animated Skins.
846 //===============
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();
863 // if a root bone...
864 if(fatherId==-1)
865 // Compute root bone worldMatrix. Do not allow special AnimCtrl
866 Bones[i].compute( NULL, modelWorldMatrix, NULL);
867 else
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;
881 else
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).
893 if(!mngr)
894 return;
896 // If a shape was setup, free the instance
897 if(_CLodInstance.ShapeId>=0)
899 mngr->releaseInstance(_CLodInstance);
900 _CLodInstance.ShapeId= -1;
903 // assign
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()
917 // is lod setuped
918 if(_CLodInstance.ShapeId<0)
919 return;
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
925 nlassert(mngr);
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
932 if(!asyncMngr)
933 return;
936 // **** start process. If cannot (TextureId==no more texture space), just quit.
937 if(!mngr->startTextureCompute(_CLodInstance))
938 return;
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();
952 // if setuped
953 if(lodText)
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;
972 if(asyncTextId!=-1)
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
979 if(!coarseBitmap)
981 dstBmp.build(mbi->Materials[i].getDiffuse());
983 else
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
1023 // if enabled
1024 if(_LodCharacterDistance>0 && _CLodInstance.ShapeId>=0)
1026 CVector globalPos;
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() )
1034 return 0;
1035 // take ancestor world position
1036 globalPos= _AncestorSkeletonModel->getWorldMatrix().getPos();
1038 else
1040 // if the skeleton is clipped, quit
1041 if( !isClipVisible() )
1042 return 0;
1043 // take our world position
1044 globalPos= getWorldMatrix().getPos();
1047 // compute distance from camera.
1048 float dist= (getOwnerScene()->getClipTrav().CamPos - globalPos).norm();
1050 // compute priority
1051 return dist*_OOLodCharacterDistance;
1053 else
1054 return 0;
1058 // ***************************************************************************
1059 void CSkeletonModel::setDisplayLodCharacterFlag(bool displayCLod)
1061 // if enabled
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;
1068 // set new state
1069 _DisplayedAsLodCharacter= displayCLod;
1074 // ***************************************************************************
1075 void CSkeletonModel::traverseRender()
1077 H_AUTO( NL3D_Skeleton_Render );
1079 // render as CLod, or render Skins.
1080 if(isDisplayedAsLodCharacter())
1081 renderCLod();
1082 else
1083 renderSkins();
1087 // ***************************************************************************
1088 void CSkeletonModel::computeCLodVertexAlpha(CLodCharacterManager *mngr)
1090 // if shape id set.
1091 if(_CLodInstance.ShapeId<0)
1092 return;
1093 // get the lod shape,a nd check exist in the manager
1094 const CLodCharacterShape *lodShape= mngr->getShape(_CLodInstance.ShapeId);
1095 if(lodShape)
1097 // start process.
1098 //-----------------
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)
1104 boneMap.clear();
1105 boneMap.resize(Bones.size(), -1);
1106 uint i;
1107 // for all skeletons bones.
1108 for(i=0; i<boneMap.size(); i++)
1110 boneMap[i]= lodShape->getBoneIdByName(Bones[i].getBoneName());;
1113 // Parse all skins
1114 //-----------------
1115 ItTransformSet it;
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
1123 if(skinUsage)
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
1132 if(idInLod>=0)
1133 // add color to this bone.
1134 lodShape->addBoneAlpha(idInLod, _CLodInstance.VertexAlphas);
1140 // Parse all sticked objects
1141 //-----------------
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
1151 if(idInLod>=0)
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)
1167 uint i;
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
1194 uint opaqueSize= 0;
1195 uint transparentSize= 0;
1196 uint animDetailSize= 0;
1197 ItTransformSet it;
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)
1206 continue;
1208 // if transparent, then must fill in transparent list.
1209 if(skin->isTransparent())
1210 transparentSize++;
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())
1214 opaqueSize++;
1216 // if animDetailable, then must fill list
1217 if(skin->isAnimDetailable())
1218 animDetailSize++;
1220 // if the skin support MRM, then must update levelDetal number of faces
1221 CTransformShape *trShape= dynamic_cast<CTransformShape*>(skin);
1222 if(trShape)
1224 const CMRMLevelDetail *skinLevelDetail= trShape->getMRMLevelDetail();
1225 if(skinLevelDetail)
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;
1260 else
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();
1280 else
1281 _LevelDetail.compileDistanceSetup();
1284 // alloc array.
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.
1293 uint opaqueId= 0;
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)
1302 continue;
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())
1354 return;
1356 // the lod manager. no op if not here
1357 CLodCharacterManager *mngr= scene->getLodCharacterManager();
1358 if(!mngr)
1359 return;
1361 // Get global lighting on the instance. Suppose SunAmbient only.
1362 //=================
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.
1369 else
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];
1386 if(mainPL)
1388 CRGBA plDiffuse;
1389 // get the diffuse of the pointLight, attenuated from distance and importance.
1390 plDiffuse.modulateFromuiRGBOnly(mainPL->getDiffuse(), lightContrib->AttFactor[0]);
1391 plDiffuse.A = 255;
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.
1396 if(d1>d0)
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.
1407 //=================
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.
1420 //=================
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
1432 mngr->endRender();
1433 // and restart.
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();
1450 nlassert(drv);
1453 // Compute the levelOfDetail
1454 float alphaMRM= _LevelDetail.getLevelDetailFromPolyCount(getNumTrianglesAfterLoadBalancing());
1456 // force normalisation of normals..
1457 bool bkupNorm= drv->isForceNormalize();
1458 drv->forceNormalize(true);
1461 // rdr good pass
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.
1471 else
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);
1483 else
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);
1515 else
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);
1539 else
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
1551 uint skinId= 0;
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.
1560 //------------
1561 // lock buffer
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)
1573 break;
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
1581 skinId++;
1584 // release buffer. ATI: release only vertices used.
1585 meshSkinManager.unlock(currentBaseVertex);
1587 // Second pass, render the primitives.
1588 //------------
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() )
1636 return 0;
1637 else
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)
1645 // check input.
1646 if(distanceFinest<0) return;
1647 if(distanceMiddle<=distanceFinest) return;
1648 if(distanceCoarsest<=distanceMiddle) return;
1650 // Change.
1651 _LevelDetail.DistanceFinest= distanceFinest;
1652 _LevelDetail.DistanceMiddle= distanceMiddle;
1653 _LevelDetail.DistanceCoarsest= distanceCoarsest;
1655 // compile
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)
1676 // reset bbox
1677 CAABBox tmpBBox;
1678 tmpBBox.setCenter(CVector::Null);
1679 tmpBBox.setHalfSize(CVector::Null);
1680 bool empty= true;
1682 // Not visible => empty bbox
1683 if(!isClipVisible())
1684 return false;
1686 // For all bones
1687 uint i;
1688 for(i=0;i<Bones.size();i++)
1690 if(isBoneComputed(i))
1692 CVector pos;
1693 if(computeInWorld)
1694 pos= Bones[i].getWorldMatrix().getPos();
1695 else
1696 pos= Bones[i].getLocalSkeletonMatrix().getPos();
1697 if(empty)
1699 empty= false;
1700 tmpBBox.setCenter(pos);
1702 else
1703 tmpBBox.extend(pos);
1707 // End!
1708 if(!empty)
1710 bbox= tmpBBox;
1711 return true;
1713 else
1714 return false;
1717 // ***************************************************************************
1718 void CSkeletonModel::getWorldMaxBoneSpheres(std::vector<NLMISC::CBSphere> &dest) const
1720 dest.clear();
1721 // Not visible => empty bbox
1722 if(!isClipVisible())
1723 return;
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())
1737 return false;
1739 if(_BoneToCompute.empty())
1740 return false;
1742 if (_Skins.empty())
1743 return false;
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();
1754 CBSphere sphere;
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;
1762 minBone.x-= r;
1763 minBone.y-= r;
1764 minBone.z-= r;
1765 maxBone.x+= r;
1766 maxBone.y+= r;
1767 maxBone.z+= r;
1770 // set or extend
1771 if(i==0)
1773 minBB= minBone;
1774 maxBB= maxBone;
1776 else
1778 minBB.minof(minBB, minBone);
1779 maxBB.maxof(maxBB, maxBone);
1783 // build the bbox
1784 bbox.setMinMax(minBB, maxBB);
1785 return true;
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();
1794 if (chanmix)
1796 // Force detail evaluation.
1797 chanmix->resetEvalDetailDate();
1798 chanmix->eval(true, 0);
1799 chanmix->resetEvalDetailDate();
1801 // compute all skeleton bones
1802 computeAllBones(CMatrix::Identity);
1804 // reset bbox
1805 CAABBox tmpBBox;
1806 tmpBBox.setCenter(CVector::Null);
1807 tmpBBox.setHalfSize(CVector::Null);
1808 bool empty= true;
1810 // For all bones
1811 uint i;
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.
1818 if(mustCompute)
1820 CVector pos;
1821 if(computeInWorld)
1822 pos= Bones[i].getWorldMatrix().getPos();
1823 else
1824 pos= Bones[i].getLocalSkeletonMatrix().getPos();
1825 if(empty)
1827 empty= false;
1828 tmpBBox.setCenter(pos);
1830 else
1831 tmpBBox.extend(pos);
1835 // End!
1836 if(!empty)
1838 bbox= tmpBBox;
1839 return true;
1841 else
1842 return false;
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();
1854 else
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
1865 modelRadius= 0;
1869 // ***************************************************************************
1870 void CSkeletonModel::setBoneAnimCtrl(uint boneId, IAnimCtrl *ctrl)
1872 if(boneId>=Bones.size())
1873 return;
1875 CBone &bone= Bones[boneId];
1877 // Update refCount
1878 if(ctrl && !bone._AnimCtrl)
1879 _AnimCtrlUsage++;
1880 else if(!ctrl && bone._AnimCtrl)
1881 _AnimCtrlUsage--;
1883 // set
1884 bone._AnimCtrl= ctrl;
1888 // ***************************************************************************
1889 IAnimCtrl *CSkeletonModel::getBoneAnimCtrl(uint boneId) const
1891 if(boneId>=Bones.size())
1892 return NULL;
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)
1902 return false;
1904 // no intersection by default
1905 dist2D= FLT_MAX;
1906 distZ= FLT_MAX;
1908 // The skinning must be done in final RaySpace.
1909 CMatrix toRaySpace;
1910 // compute the ray matrix
1911 CVector dirn= dir;
1912 if(dirn.isNull())
1913 dirn= CVector::K;
1914 dirn.normalize();
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();
1926 if(!mngr)
1927 return false;
1929 // test the instance
1930 if(!mngr->fastIntersect(_CLodInstance, toRaySpace, dist2D, distZ, computeDist2D))
1931 return false;
1933 else
1935 // For all skins
1936 ItTransformSet it;
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)
1943 continue;
1945 if(!skin->supportIntersectSkin())
1946 continue;
1948 // compute intersection with this skin
1949 float skinDist2D, skinDistZ;
1950 if(skin->intersectSkin(toRaySpace, skinDist2D, skinDistZ, computeDist2D))
1952 // true intersection found?
1953 if(skinDist2D==0)
1955 dist2D= 0;
1956 distZ= min(distZ, skinDistZ);
1958 // else lower the distance to the skins?
1959 else if(dist2D>0)
1961 dist2D= min(dist2D, skinDist2D);
1967 // no intersection found? set Z to 0 (to be clean)
1968 if(dist2D>0)
1969 distZ= 0;
1971 return true;
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;
1990 // Bones found ?
1991 if (boneId != -1)
1993 // Set the bone id
1994 remap[bone] = (uint32)boneId;
1996 else
1998 // Put id 0
1999 remap[bone] = 0;
2001 // Error
2002 nlwarning ("Bone %s not found in the skeleton.", bonesName[bone].c_str());
2007 // ***************************************************************************
2008 // ***************************************************************************
2009 // ShadowMap
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();
2024 if(!Shape)
2025 return;
2027 // update ShadowMap data if needed.
2028 // ****
2029 updateShadowMap(driver);
2031 // compute the ProjectionMatrix.
2032 // ****
2034 // Compute the BBox in World, with bounding Box of Bones, and with BoundingBox of sticked Objects
2035 CAABBox bbWorld;
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);
2048 // Render.
2049 // ****
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);
2058 // Infos.
2059 // ****
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;
2067 ldir.z= 0;
2068 ldir.normalize();
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;
2072 // localPos.
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()
2082 return _ShadowMap;
2085 // ***************************************************************************
2086 void CSkeletonModel::createShadowMap()
2088 // create the shadowMap
2089 if(!_ShadowMap)
2091 _ShadowMap= new CShadowMap(&getOwnerScene()->getRenderTrav().getShadowMapManager());
2092 getOwnerScene()->registerShadowCasterToList(this);
2096 // ***************************************************************************
2097 void CSkeletonModel::deleteShadowMap()
2099 if(_ShadowMap)
2101 delete _ShadowMap;
2102 _ShadowMap= NULL;
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() )
2132 // can occurs?????
2133 // ABORT!! ... avoid Mesh Shadowing (free shadowMap)? Replace with a dummy Shadow?
2134 // For now, no-op...
2136 else
2138 uint i;
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
2169 uint skinId= 0;
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.
2177 //------------
2178 // lock buffer
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)
2190 break;
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
2198 skinId++;
2201 // release buffer. ATI: release only vertices used.
2202 meshSkinManager.unlock(currentBaseVertex);
2204 // Second pass, render the primitives.
2205 //------------
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)
2225 uint i;
2227 // If even not visible, no-op
2228 if(!isHrcVisible() || !Shape)
2229 return false;
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;
2244 minBone.x-= r;
2245 minBone.y-= r;
2246 minBone.z-= r;
2247 maxBone.x+= r;
2248 maxBone.y+= r;
2249 maxBone.z+= r;
2250 // set or extend
2251 if(i==0)
2253 minBB= minBone;
2254 maxBB= maxBone;
2256 else
2258 minBB.minof(minBB, minBone);
2259 maxBB.maxof(maxBB, maxBone);
2262 // build the bbox
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();
2270 if(i==0)
2271 worldBB.setCenter(worldMat.getPos());
2272 else
2273 worldBB.extend(worldMat.getPos());
2275 worldBB.setHalfSize(worldBB.getHalfSize() *1.5f);
2279 // **** Add to this bbox the ones of the Sticked objects.
2280 ItTransformSet it;
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!!)
2285 CAABBox stickBB;
2286 if(stickModel->computeWorldBBoxForShadow(stickBB))
2288 // Make union of the 2
2289 worldBB= CAABBox::computeAABBoxUnion(worldBB, stickBB);
2293 // Done!
2294 return true;
2297 // ***************************************************************************
2298 void CSkeletonModel::renderIntoSkeletonShadowMap(CSkeletonModel *rootSkeleton, CMaterial &castMat)
2300 // If even not visible, no-op
2301 if(!isHrcVisible() || !Shape)
2302 return;
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.
2319 ItTransformSet it;
2320 for(it= _StickedObjects.begin();it!=_StickedObjects.end();it++)
2322 CTransform *stickModel= *it;
2323 stickModel->renderIntoSkeletonShadowMap(rootSkeleton, castMat);
2329 } // NL3D