# Home    # nevrax.com   
Nevrax
Nevrax.org
#News
#Mailing-list
#Documentation
#CVS
#Bugs
#License
Docs
 
Documentation  
Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages   Search  

skeleton_model.cpp

Go to the documentation of this file.
00001 
00007 /* Copyright, 2001 Nevrax Ltd.
00008  *
00009  * This file is part of NEVRAX NEL.
00010  * NEVRAX NEL is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2, or (at your option)
00013  * any later version.
00014 
00015  * NEVRAX NEL is distributed in the hope that it will be useful, but
00016  * WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00018  * General Public License for more details.
00019 
00020  * You should have received a copy of the GNU General Public License
00021  * along with NEVRAX NEL; see the file COPYING. If not, write to the
00022  * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
00023  * MA 02111-1307, USA.
00024  */
00025 
00026 #include "std3d.h"
00027 
00028 #include "nel/misc/hierarchical_timer.h"
00029 #include "3d/skeleton_model.h"
00030 #include "3d/hrc_trav.h"
00031 #include "3d/clip_trav.h"
00032 #include "3d/anim_detail_trav.h"
00033 #include "3d/render_trav.h"
00034 #include "3d/skeleton_shape.h"
00035 #include "3d/scene.h"
00036 #include "3d/lod_character_manager.h"
00037 #include "3d/lod_character_shape.h"
00038 #include "3d/skip_model.h"
00039 #include "nel/misc/rgba.h"
00040 #include "nel/misc/aabbox.h"
00041 #include "3d/mesh_skin_manager.h"
00042 #include "3d/mesh_base_instance.h"
00043 #include "3d/async_texture_manager.h"
00044 
00045 
00046 
00047 using namespace std;
00048 using namespace NLMISC;
00049 
00050 namespace NL3D
00051 {
00052 
00053 
00054 // ***************************************************************************
00055 void            CSkeletonModel::registerBasic()
00056 {
00057         CMOT::registerModel(SkeletonModelId, TransformShapeId, CSkeletonModel::creator);
00058         CMOT::registerObs(AnimDetailTravId, SkeletonModelId, CSkeletonModelAnimDetailObs::creator);
00059         CMOT::registerObs(RenderTravId, SkeletonModelId, CSkeletonModelRenderObs::creator);
00060 }
00061 
00062 
00063 // ***************************************************************************
00064 void            CSkeletonModel::registerToChannelMixer(CChannelMixer *chanMixer, const std::string &prefix)
00065 {
00066         CTransformShape::registerToChannelMixer(chanMixer, prefix);
00067 
00068         // Add any bones.
00069         for(uint i=0;i<Bones.size();i++)
00070         {
00071                 // append  bonename.
00072                 Bones[i].registerToChannelMixer(chanMixer, prefix + Bones[i].getBoneName() + ".");
00073         }
00074 
00075 }
00076 
00077 // ***************************************************************************
00078 CSkeletonModel::CSkeletonModel()
00079 {
00080         IAnimatable::resize(AnimValueLast);
00081         HrcTrav= NULL;
00082         ClipTrav= NULL;
00083         _DisplayedAsLodCharacter= false;
00084         _LodCharacterDistance= 0;
00085         _OOLodCharacterDistance= 0;
00086 
00087         _DefaultMRMSetup= true;
00088 
00089         _SkinToRenderDirty= false;
00090 
00091         _CLodVertexColorDirty= true;
00092 
00093         // Inform the transform that I am a skeleton
00094         CTransform::setIsSkeleton(true);
00095 
00096         // By default, no skins, hence, impossible to have transparent pass. But opaque pass is always possible
00097         // because of CLod rendering
00098         setOpacity(true);
00099         setTransparency(false);
00100 
00101         // AnimDetail behavior: Must be traversed in AnimDetail, even if no channel mixer registered
00102         CTransform::setIsForceAnimDetail(true);
00103 
00104         // LoadBalancing behavior. true because directly act on skins to draw all their MRM level
00105         CTransform::setIsLoadbalancable(true);
00106 
00107         // Lighting behavior. Lightable because skins/stickedObjects may surely need its LightContribution
00108         CTransform::setIsLightable(true);
00109 
00110         // Render behavior. Always renderable, because either render the skeleton as a CLod, or render skins
00111         CTransform::setIsRenderable(true);
00112 
00113         // build a bug-free level detail
00114         buildDefaultLevelDetail();
00115 }
00116 
00117         
00118 // ***************************************************************************
00119 CSkeletonModel::~CSkeletonModel()
00120 {
00121         // if initModel() called
00122         if(ClipTrav)
00123         {
00124                 // remove from scene
00125                 ClipTrav->Scene->eraseSkeletonModelToList(_ItSkeletonInScene);
00126         }
00127 
00128 
00129         // detach skeleton sons from skins.
00130         while(_Skins.begin()!=_Skins.end())
00131         {
00132                 detachSkeletonSon(*_Skins.begin());
00133         }
00134 
00135         // detach skeleton sons from sticked objects.
00136         while(_StickedObjects.begin()!=_StickedObjects.end())
00137         {
00138                 detachSkeletonSon(*_StickedObjects.begin());
00139         }
00140 
00141         // Free Lod instance
00142         setLodCharacterShape(-1);
00143 
00144 }
00145 
00146 
00147 // ***************************************************************************
00148 void    CSkeletonModel::initModel()
00149 {
00150         IObs                    *HrcObs= getObs(NL3D::HrcTravId);
00151         HrcTrav= (CHrcTrav*)HrcObs->Trav;
00152         IObs                    *ClipObs= getObs(NL3D::ClipTravId);
00153         ClipTrav= (CClipTrav*)ClipObs->Trav;
00154 
00155         // Link this skeleton to the CScene.
00156         _ItSkeletonInScene= ClipTrav->Scene->appendSkeletonModelToList(this);
00157 
00158         // Call base class
00159         CTransformShape::initModel();
00160 }
00161 
00162 
00163 // ***************************************************************************
00164 void            CSkeletonModel::initBoneUsages()
00165 {
00166         // reset all to 0.
00167         _BoneUsage.resize(Bones.size());
00168         for(uint i=0; i<_BoneUsage.size(); i++)
00169         {
00170                 _BoneUsage[i].Usage= 0;
00171                 _BoneUsage[i].ForcedUsage= 0;
00172                 _BoneUsage[i].CLodForcedUsage= 0;
00173                 _BoneUsage[i].MustCompute= 0;
00174                 _BoneUsage[i].ValidBoneSkinMatrix= 0;
00175         }
00176         // reserve space for bone to compute
00177         _BoneToCompute.reserve(Bones.size());
00178 
00179         _BoneToComputeDirty= false;
00180         _CurLod= 0;
00181         _CurLodInterp= 1.f;
00182         // Default is 0.5 meters.
00183         _LodInterpMultiplier= 1.f / 0.5f;
00184 }
00185 
00186 
00187 // ***************************************************************************
00188 void            CSkeletonModel::incBoneUsage(uint i, TBoneUsageType boneUsageType)
00189 {
00190         nlassert(i<_BoneUsage.size());
00191 
00192         // Get ptr on according refCount
00193         uint8   *usagePtr;
00194         if(boneUsageType == UsageNormal)
00195                 usagePtr= &_BoneUsage[i].Usage;
00196         else if(boneUsageType == UsageForced)
00197                 usagePtr= &_BoneUsage[i].ForcedUsage;
00198         else
00199                 usagePtr= &_BoneUsage[i].CLodForcedUsage;
00200 
00201         // If the bone was not used before, must update MustCompute.
00202         if(*usagePtr==0)
00203                 _BoneToComputeDirty= true;
00204 
00205         // Inc the refCount of the bone.
00206         nlassert(*usagePtr<255);
00207         (*usagePtr)++;
00208 }
00209 
00210 
00211 // ***************************************************************************
00212 void            CSkeletonModel::decBoneUsage(uint i, TBoneUsageType boneUsageType)
00213 {
00214         nlassert(i<_BoneUsage.size());
00215 
00216         // Get ptr on according refCount
00217         uint8   *usagePtr;
00218         if(boneUsageType == UsageNormal)
00219                 usagePtr= &_BoneUsage[i].Usage;
00220         else if(boneUsageType == UsageForced)
00221                 usagePtr= &_BoneUsage[i].ForcedUsage;
00222         else
00223                 usagePtr= &_BoneUsage[i].CLodForcedUsage;
00224 
00225         // If the bone was used before (and now won't be more), must update MustCompute.
00226         if(*usagePtr==1)
00227                 _BoneToComputeDirty= true;
00228 
00229         // Inc the refCount of the bone.
00230         nlassert(*usagePtr>0);
00231         (*usagePtr)--;
00232 }
00233 
00234 
00235 // ***************************************************************************
00236 void            CSkeletonModel::flagBoneAndParents(uint32 boneId, std::vector<bool>     &boneUsage) const
00237 {
00238         nlassert( boneUsage.size()==Bones.size() );
00239         nlassert( boneId<Bones.size() );
00240 
00241         // Flag this bone.
00242         boneUsage[boneId]= true;
00243 
00244         // if has father, flag it (recurs).
00245         sint    fatherId= Bones[boneId].getFatherId();
00246         if(fatherId>=0)
00247                 flagBoneAndParents(fatherId, boneUsage);
00248 }
00249 
00250 
00251 // ***************************************************************************
00252 void            CSkeletonModel::incForcedBoneUsageAndParents(uint i, bool forceCLod)
00253 {
00254         // inc forced.
00255         incBoneUsage(i, forceCLod?UsageCLodForced:UsageForced );
00256 
00257         // recurs to father
00258         sint    fatherId= Bones[i].getFatherId();
00259         // if not a root bone...
00260         if(fatherId>=0)
00261                 incForcedBoneUsageAndParents(fatherId, forceCLod);
00262 }
00263 
00264 // ***************************************************************************
00265 void            CSkeletonModel::decForcedBoneUsageAndParents(uint i, bool forceCLod)
00266 {
00267         // dec forced
00268         decBoneUsage(i, forceCLod?UsageCLodForced:UsageForced);
00269 
00270         // recurs to father
00271         sint    fatherId= Bones[i].getFatherId();
00272         // if not a root bone...
00273         if(fatherId>=0)
00274                 decForcedBoneUsageAndParents(fatherId, forceCLod);
00275 }
00276 
00277 
00278 // ***************************************************************************
00279 void            CSkeletonModel::updateBoneToCompute()
00280 {
00281         // If already computed, skip
00282         if(!_BoneToComputeDirty)
00283                 return;
00284 
00285         // get the channelMixer owned by CTransform.
00286         CChannelMixer   *chanMixer= getChannelMixer();
00287 
00288         // Get Lod infos from skeletonShape
00289         CSkeletonShape          *skeShape= (CSkeletonShape*)(IShape*)Shape;
00290         const CSkeletonShape::CLod      &lod= skeShape->getLod(_CurLod);
00291 
00292         // reset _BoneToCompute
00293         _BoneToCompute.clear();
00294 
00295         // For all bones
00296         for(uint i=0; i<_BoneUsage.size(); i++)
00297         {
00298                 // If we are in CLod mode
00299                 if(isDisplayedAsLodCharacter())
00300                         // don't compute the bone
00301                         _BoneUsage[i].MustCompute= 0;
00302                 else
00303                 {
00304                         // set MustCompute to non 0 if (Usage && Lod) || ForcedUsage;
00305                         _BoneUsage[i].MustCompute= (_BoneUsage[i].Usage & lod.ActiveBones[i]) | _BoneUsage[i].ForcedUsage;
00306                 }
00307                 // if CLodForcedUsage for the bone, it must be computed, whatever _DisplayedAsLodCharacter state
00308                 _BoneUsage[i].MustCompute|= _BoneUsage[i].CLodForcedUsage;
00309 
00310                 // If the bone must be computed (if !0)
00311                 if(_BoneUsage[i].MustCompute)
00312                 {
00313                         // lodEnable the channels of this bone
00314                         if(chanMixer)
00315                                 Bones[i].lodEnableChannels(chanMixer, true);
00316 
00317                         // This bone is computed => take his valid boneSkinMatrix.
00318                         _BoneUsage[i].ValidBoneSkinMatrix= i;
00319 
00320                         // Append to the list to compute.
00321                         //-------
00322                         CBoneCompute    bc;
00323                         bc.Bone= &Bones[i];
00324                         sint    fatherId= Bones[i].getFatherId();
00325                         // if a root bone...
00326                         if(fatherId==-1)
00327                                 bc.Father= NULL;
00328                         else
00329                                 bc.Father= &Bones[fatherId];
00330                         // MustInterpolate??
00331                         bc.MustInterpolate= false;
00332                         const CSkeletonShape::CLod      *lodNext= NULL;
00333                         // if a lod exist after current lod, and if lod interpolation enabled
00334                         if( _CurLod < skeShape->getNumLods()-1 && _LodInterpMultiplier>0 )
00335                         {
00336                                 // get next lod.
00337                                 lodNext= &skeShape->getLod(_CurLod+1);
00338                                 // Lod interpolation on this bone ?? only if at next lod, the bone is disabled.
00339                                 // And only if it is not enabed because of a "Forced reason"
00340                                 // Must also have a father, esle can't interpolate.
00341                                 if(lodNext->ActiveBones[i]==0 && _BoneUsage[i].ForcedUsage==0 && _BoneUsage[i].CLodForcedUsage==0 
00342                                         && bc.Father)
00343                                         bc.MustInterpolate= true;
00344                         }
00345                         // append
00346                         _BoneToCompute.push_back(bc);
00347                 }
00348                 else
00349                 {
00350                         // lodDisable the channels of this bone
00351                         if(chanMixer)
00352                                 Bones[i].lodEnableChannels(chanMixer, false);
00353 
00354                         // This bone is not computed => take the valid boneSkinMatrix of his father
00355                         sint    fatherId= Bones[i].getFatherId();
00356                         if(fatherId<0)
00357                                 // just take me, even if not computed.
00358                                 _BoneUsage[i].ValidBoneSkinMatrix= i;
00359                         else
00360                                 // NB: father ValidBoneSkinMatrix already computed because of the hierarchy order of Bones array.
00361                                 _BoneUsage[i].ValidBoneSkinMatrix= _BoneUsage[fatherId].ValidBoneSkinMatrix;
00362                 }
00363         }
00364 
00365         // For 
00366 
00367         // computed
00368         _BoneToComputeDirty= false;
00369 }
00370 
00371 
00372 // ***************************************************************************
00373 bool            CSkeletonModel::isBoneComputed(uint boneId) const
00374 {
00375         if(boneId>=_BoneUsage.size())
00376                 return false;
00377         else
00378                 return _BoneUsage[boneId].MustCompute!=0;
00379 }
00380 
00381 
00382 // ***************************************************************************
00383 const NLMISC::CMatrix   &CSkeletonModel::getActiveBoneSkinMatrix(uint boneId) const
00384 {
00385         // Get me or first father with MustCompute==true.
00386         uint validBoneId= _BoneUsage[boneId].ValidBoneSkinMatrix;
00387         // return his WorldMatrix.
00388         return Bones[validBoneId].getBoneSkinMatrix();
00389 }
00390 
00391 
00392 // ***************************************************************************
00393 bool            CSkeletonModel::bindSkin(CTransform *mi)
00394 {
00395         nlassert(mi);
00396         if( !mi->isSkinnable() )
00397                 return false;
00398 
00399         // try to detach this object from any skeleton first (possibly me).
00400         if(mi->_FatherSkeletonModel)
00401                 mi->_FatherSkeletonModel->detachSkeletonSon(mi);
00402 
00403         // Then Add me.
00404         _Skins.insert(mi);
00405 
00406         // advert skin transform it is skinned.
00407         mi->_FatherSkeletonModel= this;
00408         // setApplySkin() use _FatherSkeletonModel to computeBonesId() and to update current skeleton bone usage.
00409         mi->setApplySkin(true);
00410 
00411 
00412         // Unlink the Skin from Hrc and clip, because SkeletonModel now does the job for him.
00413         nlassert(HrcTrav && ClipTrav);
00414         // First ensure that the transform is not frozen (unlink from some quadGrids etc...)
00415         mi->unfreezeHRC();
00416         // then never re-parse in validateList/Hrc/Clip
00417         mi->unlinkFromValidateList();
00418         HrcTrav->link(HrcTrav->Scene->getSkipModelRoot(), mi);
00419         // ClipTrav is a graph, so must unlink from ALL olds models.
00420         IModel  *father= ClipTrav->getFirstParent(mi);
00421         while(father)
00422         {
00423                 ClipTrav->unlink(father, mi);
00424                 father= ClipTrav->getFirstParent(mi);
00425         }
00426         // Ensure flag is correct
00427         mi->_HrcObs->ClipLinkedInSonsOfAncestorSkeletonModelGroup= false;
00428         // link to the SkipModelRoot One.
00429         ClipTrav->link(ClipTrav->Scene->getSkipModelRoot(), mi);
00430 
00431 
00432         // must recompute lod vertex color when LodCharacter used
00433         dirtLodVertexColor();
00434         // must recompute list of skins.
00435         dirtSkinRenderLists();
00436 
00437         // Ok, skinned
00438         return true;
00439 }
00440 // ***************************************************************************
00441 void            CSkeletonModel::stickObject(CTransform *mi, uint boneId)
00442 {
00443         // by default don't force display of "mi" if the skeleton become in CLod state
00444         stickObjectEx(mi, boneId, false);
00445 }
00446 // ***************************************************************************
00447 void            CSkeletonModel::stickObjectEx(CTransform *mi, uint boneId, bool forceCLod)
00448 {
00449         nlassert(mi);
00450 
00451         // if "mi" is a skeleton, forceCLod must be true, for correct animation purpose
00452         if(dynamic_cast<CSkeletonModel*>(mi))
00453                 forceCLod= true;
00454 
00455         // try to detach this object from any skeleton first (possibly me).
00456         if(mi->_FatherSkeletonModel)
00457                 mi->_FatherSkeletonModel->detachSkeletonSon(mi);
00458 
00459         // Then Add me.
00460         _StickedObjects.insert(mi);
00461         // increment the refCount usage of the bone
00462         incForcedBoneUsageAndParents(boneId, forceCLod);
00463 
00464         // advert transform of its sticked state.
00465         mi->_FatherSkeletonModel= this;
00466         mi->_FatherBoneId= boneId;
00467         // advert him if it is "ForceCLod" sticked
00468         mi->_ForceCLodSticked= forceCLod;
00469 
00470         // link correctly Hrc only. ClipTrav grah updated in Hrc traversal.
00471         nlassert(HrcTrav && ClipTrav);
00472         HrcTrav->link(this, mi);
00473 
00474         // must recompute lod vertex color when LodCharacter used
00475         dirtLodVertexColor();
00476 }
00477 // ***************************************************************************
00478 void            CSkeletonModel::detachSkeletonSon(CTransform *tr)
00479 {
00480         nlassert(tr);
00481 
00482         // If the instance is not binded/sticked to the skeleton, exit.
00483         if(tr->_FatherSkeletonModel!=this)
00484                 return;
00485 
00486         // try to erase from StickObject.
00487         _StickedObjects.erase(tr);
00488         // try to erase from Skins.
00489         _Skins.erase(tr);
00490 
00491         // If the instance is not skinned, then it is sticked
00492         bool    wasSkinned= tr->isSkinned()!=0;
00493         if( !wasSkinned )
00494         {
00495                 // Then decrement Bone Usage RefCount. Decrement from CLodForcedUsage if was sticked with forceCLod==true
00496                 decForcedBoneUsageAndParents(tr->_FatherBoneId, tr->_ForceCLodSticked);
00497         }
00498         else
00499         {
00500                 // it is skinned, advert the skinning is no more OK.
00501                 // setApplySkin() use _FatherSkeletonModel to update current skeleton bone usage.
00502                 tr->setApplySkin(false);
00503         }
00504 
00505         // advert transform it is no more sticked/skinned.
00506         tr->_FatherSkeletonModel= NULL;
00507         tr->_ForceCLodSticked= false;
00508 
00509         // link correctly Hrc / Clip / ValidateList...
00510         nlassert(HrcTrav && ClipTrav);
00511         HrcTrav->link(NULL, tr);
00512         if( !wasSkinned )
00513         {
00514                 //  No-op. ClipTrav graph/ValidateList updated in Hrc traversal.
00515         }
00516         else
00517         {
00518                 // Skin case: must do the Job here.
00519                 // Update ClipTrav here.
00520                 ClipTrav->unlink(ClipTrav->Scene->getSkipModelRoot(), tr);
00521                 ClipTrav->link(ClipTrav->getRoot(), tr);
00522                 // Must re-add to the validate list.
00523                 tr->linkToValidateList();
00524         }
00525 
00526 
00527         // must recompute lod vertex color when LodCharacter used
00528         dirtLodVertexColor();
00529         // must recompute list of skins if was skinned
00530         if( wasSkinned )
00531                 dirtSkinRenderLists();
00532 }
00533 
00534 
00535 // ***************************************************************************
00536 sint32          CSkeletonModel::getBoneIdByName(const std::string &name) const
00537 {
00538         CSkeletonShape          *shp= safe_cast<CSkeletonShape*>((IShape*)Shape);
00539         return shp->getBoneIdByName(name);
00540 }
00541 
00542 
00543 // ***************************************************************************
00544 void            CSkeletonModel::setInterpolationDistance(float dist)
00545 {
00546         dist= std::max(0.f, dist);
00547         // disable interpolation?
00548         if(dist==0)
00549                 _LodInterpMultiplier= 0.f;
00550         else
00551                 _LodInterpMultiplier= 1.f / dist;
00552 }
00553 
00554 // ***************************************************************************
00555 float           CSkeletonModel::getInterpolationDistance() const
00556 {
00557         if(_LodInterpMultiplier==0)
00558                 return 0.f;
00559         else
00560                 return 1.f / _LodInterpMultiplier;
00561 }
00562 
00563 
00564 // ***************************************************************************
00565 void    CSkeletonModelAnimDetailObs::traverse(IObs *caller)
00566 {
00567         CSkeletonModel  *sm= (CSkeletonModel*)Model;
00568         CSkeletonShape  *skeShape= ((CSkeletonShape*)(IShape*)sm->Shape);
00569 
00570         // Update Lod, and animate.
00571         //===============
00572 
00573         /*
00574                 CTransformAnimDetailObs::traverse() is torn in 2 here because 
00575                 channels may be enabled/disabled by updateBoneToCompute()
00576         */
00577 
00578         // First update Skeleton WorldMatrix (case where the skeleton is sticked).
00579         CTransformAnimDetailObs::updateWorldMatrixFromFather();
00580         // get dist from camera.
00581         float   dist= (HrcObs->WorldMatrix.getPos() - ((CClipTrav*)ClipObs->Trav)->CamPos).norm();
00582         // Use dist to get current lod to use for this skeleton
00583         uint    newLod= skeShape->getLodForDistance( dist );
00584         if(newLod != sm->_CurLod)
00585         {
00586                 // set new lod to use.
00587                 sm->_CurLod= newLod;
00588                 // dirt the skeleton.
00589                 sm->_BoneToComputeDirty= true;
00590         }
00591 
00592         // If needed, let's know which bone has to be computed, and enable / disable (lod) channels in channelMixer.
00593         bool forceUpdate= sm->_BoneToComputeDirty;
00594         sm->updateBoneToCompute();
00595 
00596         // Animate skeleton.
00597         CTransformAnimDetailObs::traverseWithoutUpdateWorldMatrix(caller);
00598 
00599 
00600         // Prepare Lod Bone interpolation.
00601         //===============
00602 
00603         float   lodBoneInterp;
00604         const CSkeletonShape::CLod      *lodNext= NULL;
00605         // if a lod exist after current lod, and if lod interpolation enabled
00606         if( sm->_CurLod < skeShape->getNumLods()-1 && sm->_LodInterpMultiplier>0 )
00607         {
00608                 // get next lod.
00609                 lodNext= &skeShape->getLod(sm->_CurLod+1);
00610                 // get interp value to next.
00611                 lodBoneInterp= (lodNext->Distance - dist) * sm->_LodInterpMultiplier;
00612                 NLMISC::clamp(lodBoneInterp, 0.f, 1.f);
00613                 // if still 1, keep cur matrix => disable interpolation
00614                 if(lodBoneInterp==1.f)
00615                         lodNext=NULL;
00616         }
00617         // else, no interpolation
00618         else
00619         {
00620                 lodBoneInterp=1.f;
00621         }
00622         // If the interpolation value is different from last one, must update.
00623         if(lodBoneInterp != sm->_CurLodInterp)
00624         {
00625                 // set new one.
00626                 sm->_CurLodInterp= lodBoneInterp;
00627                 // must update bone compute.
00628                 forceUpdate= true;
00629         }
00630 
00631 
00632 
00633         // Compute bones
00634         //===============
00635 
00636         // test if bones must be updated. either if animation change or if BoneUsage change.
00637         if(sm->IAnimatable::isTouched(CSkeletonModel::OwnerBit) || forceUpdate)
00638         {
00639                 // Retrieve the WorldMatrix of the current CTransformShape.
00640                 CMatrix         &modelWorldMatrix= HrcObs->WorldMatrix;
00641 
00642                 // must test / update the hierarchy of Bones.
00643                 // Since they are orderd in depth-first order, we are sure that parent are computed before sons.
00644                 uint                                                    numBoneToCompute= sm->_BoneToCompute.size();
00645                 CSkeletonModel::CBoneCompute    *pBoneCompute= numBoneToCompute? &sm->_BoneToCompute[0] : NULL;
00646                 // traverse only bones which need to be computed
00647                 for(;numBoneToCompute>0;numBoneToCompute--, pBoneCompute++)
00648                 {
00649                         // compute the bone with his father, if any
00650                         pBoneCompute->Bone->compute( pBoneCompute->Father, modelWorldMatrix);
00651 
00652                         // Lod interpolation on this bone .. only if interp is enabled now, and if bone wants it
00653                         if(lodNext && pBoneCompute->MustInterpolate)
00654                         {
00655                                 // interpolate with my father matrix.
00656                                 const CMatrix           &fatherMatrix= pBoneCompute->Father->getBoneSkinMatrix();
00657                                 pBoneCompute->Bone->interpolateBoneSkinMatrix(fatherMatrix, lodBoneInterp);
00658                         }
00659                 }
00660 
00661                 sm->IAnimatable::clearFlag(CSkeletonModel::OwnerBit);
00662         }
00663 
00664         // Sticked Objects: 
00665         // they will update their WorldMatrix after, because of the AnimDetail traverse scheme:
00666         // traverse visible ClipObs, and if skeleton, traverse Hrc sons.
00667 
00668 
00669         // Update Animated Skins.
00670         //===============
00671         for(uint i=0;i<sm->_AnimDetailSkins.size();i++)
00672         {
00673                 // get the detail Obs, via the clipObs
00674                 CTransformAnimDetailObs         *adObs;
00675                 adObs= safe_cast<CTransformAnimDetailObs*>(sm->_AnimDetailSkins[i]->_ClipObs->AnimDetailObs);
00676 
00677                 // traverse it. NB: updateWorldMatrixFromFather() is called but no-op because isSkinned()
00678                 adObs->traverse(NULL);
00679         }
00680 
00681 }
00682 
00683 
00684 // ***************************************************************************
00685 void            CSkeletonModel::computeAllBones(const CMatrix &modelWorldMatrix)
00686 {
00687         // must test / update the hierarchy of Bones.
00688         // Since they are orderd in depth-first order, we are sure that parent are computed before sons.
00689         for(uint i=0;i<Bones.size();i++)
00690         {
00691                 sint    fatherId= Bones[i].getFatherId();
00692                 // if a root bone...
00693                 if(fatherId==-1)
00694                         // Compute root bone worldMatrix.
00695                         Bones[i].compute( NULL, modelWorldMatrix);
00696                 else
00697                         // Compute bone worldMatrix.
00698                         Bones[i].compute( &Bones[fatherId], modelWorldMatrix);
00699         }
00700 
00701 }
00702 
00703 
00704 // ***************************************************************************
00705 void            CSkeletonModel::setLodCharacterDistance(float dist)
00706 {
00707         _LodCharacterDistance= max(dist, 0.f);
00708         if(_LodCharacterDistance>0)
00709                 _OOLodCharacterDistance= 1.0f/_LodCharacterDistance;
00710         else
00711                 _OOLodCharacterDistance= 0;
00712 }
00713 
00714 // ***************************************************************************
00715 void            CSkeletonModel::setLodCharacterShape(sint shapeId)
00716 {
00717         // get a ptr on the scene which owns us, and so on the lodManager.
00718         CScene                                  *scene= static_cast<CScene*>(_OwnerMot);
00719         CLodCharacterManager    *mngr= scene->getLodCharacterManager();
00720 
00721         // if mngr not setuped, noop (lod not possible).
00722         if(!mngr)
00723                 return;
00724 
00725         // If a shape was setup, free the instance
00726         if(_CLodInstance.ShapeId>=0)
00727         {
00728                 mngr->releaseInstance(_CLodInstance);
00729                 _CLodInstance.ShapeId= -1;
00730         }
00731 
00732         // assign
00733         _CLodInstance.ShapeId= shapeId;
00734 
00735         // if a real shape is setuped, alloc an instance
00736         if(_CLodInstance.ShapeId>=0)
00737         {
00738                 mngr->initInstance(_CLodInstance);
00739         }
00740 }
00741 
00742 
00743 // ***************************************************************************
00744 void            CSkeletonModel::computeLodTexture()
00745 {
00746         // is lod setuped
00747         if(_CLodInstance.ShapeId<0)
00748                 return;
00749 
00750         // get a ptr on the scene which owns us, and so on the lodManager.
00751         CScene                                  *scene= static_cast<CScene*>(_OwnerMot);
00752         CLodCharacterManager    *mngr= scene->getLodCharacterManager();
00753         // mngr must be setuped since shape Id is >-1
00754         nlassert(mngr);
00755         /* Get the asyncTextureManager. This is a Hack. We use the AsyncTextureManager to store very low version of Textures
00756                 (kept in DXTC1 format for minimum memory overhead).
00757                 HENCE Lod Texture can work only with Async Textured instances!!
00758         */
00759         CAsyncTextureManager    *asyncMngr= scene->getAsyncTextureManager();
00760         // if not setuped, cancel
00761         if(!asyncMngr)
00762                 return;
00763 
00764 
00765         // **** start process. If cannot (TextureId==no more texture space), just quit.
00766         if(!mngr->startTextureCompute(_CLodInstance))
00767                 return;
00768         uint maxNumBmpToReset= 0;
00769 
00770         // **** For all skins which have a LodTexture setuped
00771         ItTransformSet  it= _Skins.begin();
00772         for(;it!=_Skins.end();it++)
00773         {
00774                 // the skin should be a meshBaseInstance setuped to asyncTexturing
00775                 CMeshBaseInstance       *mbi= dynamic_cast<CMeshBaseInstance*>(*it);
00776                 if(mbi && mbi->getAsyncTextureMode() && mbi->Shape)
00777                 {
00778                         CMeshBase       *mb= (CMeshBase*)(IShape*)(mbi->Shape);
00779                         // get the LodTexture info of this shape.
00780                         const CLodCharacterTexture      *lodText= mb->getLodCharacterTexture();
00781                         // if setuped
00782                         if(lodText)
00783                         {
00784                                 // Ok, compute influence of this instance on the Lod.
00785 
00786                                 // ---- Build all bmps of the instance with help of the asyncTextureManager
00787                                 uint    numMats= mbi->Materials.size();
00788                                 // 256 materials possibles for the lod Manager
00789                                 numMats= min(numMats, 256U);
00790                                 // for endTexturecompute
00791                                 maxNumBmpToReset= max(maxNumBmpToReset, numMats);
00792                                 // process each materials
00793                                 for(uint i=0;i<numMats;i++)
00794                                 {
00795                                         // get the manager bitmap to write to
00796                                         CLodCharacterTmpBitmap  &dstBmp= mngr->getTmpBitmap(i);
00797 
00798                                         // if the material stage 0 is not textured, or has not a valid async id, build the bitmap with a color.
00799                                         sint                    asyncTextId= mbi->getAsyncTextureId(i,0);
00800                                         const CBitmap   *coarseBitmap= NULL;
00801                                         if(asyncTextId!=-1)
00802                                         {
00803                                                 // get it from async manager
00804                                                 coarseBitmap= asyncMngr->getCoarseBitmap(asyncTextId);
00805                                         }
00806 
00807                                         // So if we have no bmp here, build with material color, else build a texture
00808                                         if(!coarseBitmap)
00809                                         {
00810                                                 dstBmp.build(mbi->Materials[i].getDiffuse());
00811                                         }
00812                                         else
00813                                         {
00814                                                 dstBmp.build(*coarseBitmap);
00815                                         }
00816                                 }
00817 
00818                                 // ---- add the lodTextureInfo to the current texture computed
00819                                 mngr->addTextureCompute(_CLodInstance, *lodText);
00820                         }
00821                 }
00822         }
00823 
00824         // **** compile the process
00825         mngr->endTextureCompute(_CLodInstance, maxNumBmpToReset);
00826 
00827 }
00828 
00829 
00830 // ***************************************************************************
00831 void            CSkeletonModel::setLodCharacterAnimId(uint animId)
00832 {
00833         _CLodInstance.AnimId= animId;
00834 }
00835 
00836 // ***************************************************************************
00837 void            CSkeletonModel::setLodCharacterAnimTime(TGlobalAnimationTime time)
00838 {
00839         _CLodInstance.AnimTime= time;
00840 }
00841 
00842 // ***************************************************************************
00843 void            CSkeletonModel::setLodCharacterWrapMode(bool wrapMode)
00844 {
00845         _CLodInstance.WrapMode= wrapMode;
00846 }
00847 
00848 
00849 // ***************************************************************************
00850 float           CSkeletonModel::computeDisplayLodCharacterPriority() const
00851 {
00852         // if enabled
00853         if(_LodCharacterDistance>0 && _CLodInstance.ShapeId>=0)
00854         {
00855                 CVector         globalPos;
00856                 /* \todo yoyo: bad test of visibility. If the skeleton is hidden but has a _AncestorSkeletonModel 
00857                         wich is visible, then it is supposed to be visible (in this test), but only for The CLod LoadBalancing 
00858                         (priority not 0). Not so important...
00859                 */
00860 
00861                 // Get object position, test visibility;
00862                 // If has a skeleton ancestor, take his world position instead, because ours is invalid.
00863                 if( _HrcObs->_AncestorSkeletonModel != NULL)
00864                 {
00865                         // if the ancestore is clipped, quit
00866                         if( !_HrcObs->_AncestorSkeletonModel->isClipVisible() )
00867                                 return 0;
00868                         // take ancestor world position
00869                         globalPos= _HrcObs->_AncestorSkeletonModel->getWorldMatrix().getPos();
00870                 }
00871                 else
00872                 {
00873                         // if the skeleton is clipped, quit
00874                         if( !isClipVisible() )
00875                                 return 0;
00876                         // take our world position
00877                         globalPos= _HrcObs->WorldMatrix.getPos();
00878                 }
00879 
00880                 // compute distance from camera.
00881                 float   dist= (ClipTrav->CamPos - globalPos).norm();
00882 
00883                 // compute priority
00884                 return dist*_OOLodCharacterDistance;
00885         }
00886         else
00887                 return 0;
00888 }
00889 
00890 
00891 // ***************************************************************************
00892 void            CSkeletonModel::setDisplayLodCharacterFlag(bool displayCLod)
00893 {
00894         // if enabled
00895         if(_LodCharacterDistance>0 && _CLodInstance.ShapeId>=0)
00896         {
00897                 // If the flag has changed since last frame, must recompute bone Usage.
00898                 if(_DisplayedAsLodCharacter != displayCLod)
00899                         _BoneToComputeDirty= true;
00900 
00901                 // set new state
00902                 _DisplayedAsLodCharacter= displayCLod;
00903         }
00904 }
00905 
00906 
00907 // ***************************************************************************
00908 void            CSkeletonModelRenderObs::traverse(IObs *caller)
00909 {
00910         H_AUTO( NL3D_Skeleton_Render );
00911 
00912         CSkeletonModel          *sm= (CSkeletonModel*)Model;
00913 
00914         // render as CLod, or render Skins.
00915         if(sm->isDisplayedAsLodCharacter())
00916                 renderCLod();
00917         else
00918                 renderSkins();
00919 }
00920 
00921 
00922 // ***************************************************************************
00923 void                    CSkeletonModel::computeCLodVertexColors(CLodCharacterManager *mngr)
00924 {
00925         // if shape id set.
00926         if(_CLodInstance.ShapeId<0)
00927                 return;
00928         // get the lod shape,a nd check exist in the manager
00929         const CLodCharacterShape        *lodShape= mngr->getShape(_CLodInstance.ShapeId);
00930         if(lodShape)
00931         {
00932                 static vector<CRGBAF>   tmpColors;
00933                 tmpColors.clear();
00934 
00935                 // start process.
00936                 //-----------------
00937                 lodShape->startBoneColor(tmpColors);
00938 
00939                 // build an Id map, from Skeleton Ids to the lodShapes ids. (because may be differents)
00940                 static vector<sint>     boneMap;
00941                 // reset to -1 (ie not found)
00942                 boneMap.clear();
00943                 boneMap.resize(Bones.size(), -1);
00944                 uint i;
00945                 // for all skeletons bones.
00946                 for(i=0; i<boneMap.size(); i++)
00947                 {
00948                         boneMap[i]= lodShape->getBoneIdByName(Bones[i].getBoneName());;
00949                 }
00950 
00951                 // Parse all skins
00952                 //-----------------
00953                 ItTransformSet  it;
00954                 for(it= _Skins.begin(); it!=_Skins.end(); it++)
00955                 {
00956                         CTransform      *skin= *it;
00957 
00958                         // get color of this skin.
00959                         CRGBA   color= skin->getMeanColor();
00960 
00961                         // get array of bone used for this skin.
00962                         const vector<sint32>    *skinUsage= skin->getSkinBoneUsage();
00963                         // check correct skin
00964                         if(skinUsage)
00965                         {
00966                                 // For all bones used
00967                                 for(uint i=0; i<skinUsage->size(); i++)
00968                                 {
00969                                         // the id in the vector point to a bone in the skeleton. Hence use the boneMap to translate it
00970                                         // in the lodShape ids.
00971                                         sint    idInLod= boneMap[(*skinUsage)[i]];
00972                                         // only if id found in the lod shape
00973                                         if(idInLod>=0)
00974                                                 // add color to this bone.
00975                                                 lodShape->addBoneColor(idInLod, color, tmpColors);
00976                                 }
00977 
00978                         }
00979                 }
00980 
00981                 // Parse all sticked objects
00982                 //-----------------
00983                 for(it= _StickedObjects.begin(); it!=_StickedObjects.end(); it++)
00984                 {
00985                         CTransform      *object= *it;
00986 
00987                         // get color of this object.
00988                         CRGBA   color= object->getMeanColor();
00989 
00990                         // get on which bone this object is linked.
00991                         // use the boneMap to translate id to lodShape id.
00992                         sint    idInLod= boneMap[object->_FatherBoneId];
00993 
00994                         // only if id found in the lod shape
00995                         if(idInLod>=0)
00996                                 // add color to this bone.
00997                                 lodShape->addBoneColor(idInLod, color, tmpColors);
00998                 }
00999 
01000 
01001                 // compile colors
01002                 //-----------------
01003                 lodShape->endBoneColor(tmpColors, _CLodInstance.VertexColors);
01004         }
01005 
01006 }
01007 
01008 
01009 // ***************************************************************************
01010 void                    CSkeletonModel::updateSkinRenderLists()
01011 {
01012         // If need to update array of skins to compute
01013         if(_SkinToRenderDirty)
01014         {
01015                 _SkinToRenderDirty= false;
01016 
01017                 // Reset the LevelDetail.
01018                 _LevelDetail.MinFaceUsed= 0;
01019                 _LevelDetail.MaxFaceUsed= 0;
01020                 // If must follow default MRM setup from skins.
01021                 if(_DefaultMRMSetup)
01022                 {
01023                         _LevelDetail.DistanceCoarsest= 0;
01024                         _LevelDetail.DistanceMiddle= 0;
01025                         _LevelDetail.DistanceFinest= 0;
01026                 }
01027 
01028                 // Parse to count new size of the arrays, and to build MRM info
01029                 uint    opaqueSize= 0;
01030                 uint    transparentSize= 0;
01031                 uint    animDetailSize= 0;
01032                 ItTransformSet          it;
01033                 for(it= _Skins.begin();it!=_Skins.end();it++)
01034                 {
01035                         CTransform      *skin= *it;
01036                         // if transparent, then must fill in transparent list.
01037                         if(skin->isTransparent())
01038                                 transparentSize++;
01039                         // else may fill in opaquelist. NB: for optimisation, don't add in opaqueList 
01040                         // if added to the transperent list (all materials are rendered)
01041                         else if(skin->isOpaque())
01042                                 opaqueSize++;
01043 
01044                         // if animDetailable, then must fill list
01045                         if(skin->isAnimDetailable())
01046                                 animDetailSize++;
01047 
01048                         // if the skin support MRM, then must update levelDetal number of faces
01049                         CTransformShape         *trShape= dynamic_cast<CTransformShape*>(skin);
01050                         if(trShape)
01051                         {
01052                                 const   CMRMLevelDetail         *skinLevelDetail= trShape->getMRMLevelDetail();
01053                                 if(skinLevelDetail)
01054                                 {
01055                                         // Add Faces to the Skeleton level detail
01056                                         _LevelDetail.MinFaceUsed+= skinLevelDetail->MinFaceUsed;
01057                                         _LevelDetail.MaxFaceUsed+= skinLevelDetail->MaxFaceUsed;
01058                                         // MRM Max skin setup.
01059                                         if(_DefaultMRMSetup)
01060                                         {
01061                                                 // Get the maximum distance setup (ie the one which degrades the less)
01062                                                 _LevelDetail.DistanceCoarsest= max(_LevelDetail.DistanceCoarsest, skinLevelDetail->DistanceCoarsest);
01063                                                 _LevelDetail.DistanceMiddle= max(_LevelDetail.DistanceMiddle, skinLevelDetail->DistanceMiddle);
01064                                                 _LevelDetail.DistanceFinest= max(_LevelDetail.DistanceFinest, skinLevelDetail->DistanceFinest);
01065                                         }
01066                                 }
01067                         }
01068                 }
01069 
01070                 // MRM Max skin setup.
01071                 if(_DefaultMRMSetup)
01072                 {
01073                         // compile LevelDetail.
01074                         if(_LevelDetail.MaxFaceUsed==0)
01075                                 // build a bug-free level detail
01076                                 buildDefaultLevelDetail();
01077                         else
01078                                 _LevelDetail.compileDistanceSetup();
01079                 }
01080 
01081                 // alloc array.
01082                 _OpaqueSkins.clear();
01083                 _TransparentSkins.clear();
01084                 _AnimDetailSkins.clear();
01085                 _OpaqueSkins.resize(opaqueSize);
01086                 _TransparentSkins.resize(transparentSize);
01087                 _AnimDetailSkins.resize(animDetailSize);
01088 
01089                 // ReParse, to fill array.
01090                 uint    opaqueId= 0;
01091                 uint    transparentId= 0;
01092                 uint    animDetailId= 0;
01093                 for(it= _Skins.begin();it!=_Skins.end();it++)
01094                 {
01095                         CTransform      *skin= *it;
01096                         // if transparent, then must fill in transparent list.
01097                         if(skin->isTransparent())
01098                         {
01099                                 nlassert(transparentId<transparentSize);
01100                                 _TransparentSkins[transparentId++]= skin;
01101                         }
01102                         // else may fill in opaquelist. NB: for optimisation, don't add in opaqueList 
01103                         // if added to the transperent list (all materials are rendered)
01104                         else if(skin->isOpaque())
01105                         {
01106                                 nlassert(opaqueId<opaqueSize);
01107                                 _OpaqueSkins[opaqueId++]= skin;
01108                         }
01109 
01110                         // if animDetailable, then must fill list
01111                         if(skin->isAnimDetailable())
01112                         {
01113                                 nlassert(animDetailId<animDetailSize);
01114                                 _AnimDetailSkins[animDetailId++]= skin;
01115                         }
01116                 }
01117 
01118                 // set the Transparency to the skeleton only if has at least one transparent skin
01119                 setTransparency( transparentSize>0 );
01120         }
01121 }
01122 
01123 
01124 // ***************************************************************************
01125 void                    CSkeletonModel::buildDefaultLevelDetail()
01126 {
01127         // Avoid divide by zero.
01128         _LevelDetail.MinFaceUsed= 0;
01129         _LevelDetail.MaxFaceUsed= 0;
01130         _LevelDetail.DistanceFinest= 1;
01131         _LevelDetail.DistanceMiddle= 2;
01132         _LevelDetail.DistanceCoarsest= 3;
01133         _LevelDetail.compileDistanceSetup();
01134 }
01135 
01136 
01137 // ***************************************************************************
01138 void                    CSkeletonModelRenderObs::renderCLod()
01139 {
01140         CRenderTrav                     *trav= (CRenderTrav*)Trav;
01141         CSkeletonModel          *sm= (CSkeletonModel*)Model;
01142         IDriver                         *drv= trav->getDriver();
01143         CScene                          *scene= trav->Scene;
01144         // the lod manager. no op if not here
01145         CLodCharacterManager    *mngr= trav->Scene->getLodCharacterManager();
01146         if(!mngr)
01147                 return;
01148 
01149         // Get global lighting on the instance. Suppose SunAmbient only.
01150         //=================
01151         const CLightContribution        *lightContrib;
01152         // Get HrcObs.
01153         CTransformHrcObs        *hrcObs= (CTransformHrcObs*)HrcObs;
01154 
01155         // the std case is to take my model lightContribution
01156         if(hrcObs->_AncestorSkeletonModel==NULL)
01157                 lightContrib= &sm->getSkeletonLightContribution();
01158         // but if skinned/sticked (directly or not) to a skeleton, take its.
01159         else
01160                 lightContrib= &hrcObs->_AncestorSkeletonModel->getSkeletonLightContribution();
01161 
01162         // compute his main light contribution result. Try first with sun
01163         CRGBA   mainAmbient= scene->getSunAmbient();
01164         CRGBA   mainDiffuse= scene->getSunDiffuse();
01165         // modulate sun contribution
01166         mainDiffuse.modulateFromuiRGBOnly(mainDiffuse, lightContrib->SunContribution );
01167         CVector mainLightDir= scene->getSunDirection();
01168 
01169 
01170         /* During night, and in the buildings, it may be better to use one of the other Points lights
01171                 Test only with the first pointLight, for faster compute, even if It may fail in some cases.
01172         */
01173         CPointLight     *mainPL= lightContrib->PointLight[0];
01174         if(mainPL)
01175         {
01176                 CRGBA   plDiffuse;
01177                 // get the diffuse of the pointLight, attenuated from distance and importance.
01178                 plDiffuse.modulateFromuiRGBOnly(mainPL->getDiffuse(), lightContrib->AttFactor[0]);
01179                 // compare the 2 diffuse
01180                 uint    d0= mainDiffuse.R + mainDiffuse.G + mainDiffuse.B;
01181                 uint    d1= plDiffuse.R + plDiffuse.G + plDiffuse.B;
01182                 // if the pointLight is lighter, take it.
01183                 if(d1>d0)
01184                 {
01185                         // leave ambient, but take diffuse and pointLight fake Direction
01186                         mainDiffuse= plDiffuse;
01187                         mainLightDir= hrcObs->WorldMatrix.getPos() - mainPL->getPosition();
01188                         mainLightDir.normalize();
01189                 }
01190         }
01191 
01192 
01193         // compute colors of the lods.
01194         //=================
01195         // NB: even if texturing is sufficient, still important for AlphaTest.
01196 
01197         // If must recompute color because of change of skin color or if skin added/deleted
01198         if(sm->_CLodVertexColorDirty)
01199         {
01200                 // recompute vertex colors
01201                 sm->computeCLodVertexColors(mngr);
01202                 // set sm->_CLodVertexColorDirty to false.
01203                 sm->_CLodVertexColorDirty= false;
01204         }
01205 
01206         // render the Lod in the LodManager.
01207         //=================
01208         // render must have been intialized
01209         nlassert(mngr->isRendering());
01210 
01211 
01212         // add the instance to the manager. 
01213         if(!mngr->addRenderCharacterKey(sm->_CLodInstance, hrcObs->WorldMatrix, 
01214                 mainAmbient, mainDiffuse, mainLightDir) )
01215         {
01216                 // If failed to add it because no more vertex space in the manager, retry.
01217 
01218                 // close vertexBlock, compile render
01219                 mngr->endRender();
01220                 // and restart.
01221                 mngr->beginRender(drv, trav->CamPos);
01222 
01223                 // retry. but no-op if refail.
01224                 mngr->addRenderCharacterKey(sm->_CLodInstance, hrcObs->WorldMatrix, 
01225                         mainAmbient, mainDiffuse, mainLightDir);
01226         }
01227 }
01228 
01229 
01230 // ***************************************************************************
01231 void                    CSkeletonModelRenderObs::renderSkins()
01232 {
01233         // Render skins according to the pass.
01234         CRenderTrav                     *rdrTrav= (CRenderTrav*)Trav;
01235         CSkeletonModel          *sm= (CSkeletonModel*)Model;
01236         CTransformHrcObs        *hrcObs= (CTransformHrcObs*)HrcObs;
01237         // get a ptr on the driver
01238         IDriver                         *drv= rdrTrav->getDriver();
01239         nlassert(drv);
01240 
01241 
01242         // Compute the levelOfDetail
01243         float   alphaMRM= sm->_LevelDetail.getLevelDetailFromPolyCount(sm->getNumTrianglesAfterLoadBalancing());
01244 
01245         // force normalisation of normals..
01246         bool    bkupNorm= drv->isForceNormalize();
01247         drv->forceNormalize(true);                      
01248 
01249 
01250         // rdr good pass
01251         if(rdrTrav->isCurrentPassOpaque())
01252         {
01253                 // Compute in Pass Opaque only the light contribution. 
01254                 // Easier for skeleton: suppose lightable, no local attenuation
01255 
01256                 // the std case is to take my model lightContribution
01257                 if(hrcObs->_AncestorSkeletonModel==NULL)
01258                         sm->setupCurrentLightContribution(&sm->_LightContribution, false);
01259                 // but if sticked (directly or not) to a skeleton, take its.
01260                 else
01261                         sm->setupCurrentLightContribution(&hrcObs->_AncestorSkeletonModel->_LightContribution, false);
01262 
01263 
01264                 // Activate Driver setup: light and modelMatrix
01265                 sm->changeLightSetup( rdrTrav );
01266                 rdrTrav->getDriver()->setupModelMatrix(hrcObs->WorldMatrix);
01267 
01268 
01269                 // Render all totaly opaque skins.
01270                 renderSkinList(sm->_OpaqueSkins, alphaMRM);
01271         }
01272         else
01273         {
01274                 // NB: must have some transparent skins, since thee skeletonModel is traversed in the transparent pass.
01275 
01276                 // Activate Driver setup: light and modelMatrix
01277                 sm->changeLightSetup( rdrTrav );
01278                 rdrTrav->getDriver()->setupModelMatrix(hrcObs->WorldMatrix);
01279 
01280 
01281                 // render all opaque/transparent skins
01282                 renderSkinList(sm->_TransparentSkins, alphaMRM);
01283         }
01284 
01285 
01286         // bkup force normalisation.
01287         drv->forceNormalize(bkupNorm);
01288 }
01289 
01290 
01291 // ***************************************************************************
01292 void                    CSkeletonModelRenderObs::renderSkinList(NLMISC::CObjectVector<CTransform*, false> &skinList, float alphaMRM)
01293 {
01294         CRenderTrav                     *rdrTrav= (CRenderTrav*)Trav;
01295 
01296         // if the SkinManager is not possible at all, just rendered the std way
01297         if( !rdrTrav->getMeshSkinManager() || !rdrTrav->getMeshSkinManager()->enabled() )
01298         {
01299                 for(uint i=0;i<skinList.size();i++)
01300                 {
01301                         skinList[i]->renderSkin(alphaMRM);
01302                 }
01303         }
01304         else
01305         {
01306                 // get the meshSkinManager
01307                 CMeshSkinManager        &meshSkinManager= *rdrTrav->getMeshSkinManager();
01308 
01309                 // array (rarely allocated) of skins with grouping support
01310                 static  std::vector<CTransform*>        skinsToGroup;
01311                 static  std::vector<uint>                       baseVertices;
01312                 skinsToGroup.clear();
01313                 baseVertices.clear();
01314 
01315                 // get the maxVertices the manager support
01316                 uint    maxVertices= meshSkinManager.getMaxVertices();
01317                 uint    vertexSize= meshSkinManager.getVertexSize();
01318 
01319                 // render any skins which do not support SkinGrouping, and fill array of skins to group
01320                 for(uint i=0;i<skinList.size();i++)
01321                 {
01322                         // If don't support, or if too big to fit in the manager, just renderSkin()
01323                         if(!skinList[i]->supportSkinGrouping())
01324                         {
01325                                 H_AUTO( NL3D_Skin_NotGrouped );
01326                                 skinList[i]->renderSkin(alphaMRM);
01327                         }
01328                         else
01329                         {
01330                                 skinsToGroup.push_back(skinList[i]);
01331                         }
01332                 }
01333 
01334                 H_AUTO( NL3D_Skin_Grouped );
01335 
01336                 // For each skin, have an index which gives the decal of the vertices in the buffer
01337                 baseVertices.resize(skinsToGroup.size());
01338 
01339                 // while there is skin to render in group
01340                 uint    skinId= 0;
01341                 while(skinId<skinsToGroup.size())
01342                 {
01343                         // space left in the manager
01344                         uint    remainingVertices= maxVertices;
01345                         uint    currentBaseVertex= 0;
01346 
01347                         // First pass, fill The VB.
01348                         //------------
01349                         // lock buffer
01350                         uint8   *vbDest= meshSkinManager.lock();
01351 
01352                         // For all skins until the buffer is full
01353                         uint    startSkinId= skinId;
01354                         while(skinId<skinsToGroup.size())
01355                         {
01356                                 // if success to fill the AGP
01357                                 sint    numVerticesAdded= skinsToGroup[skinId]->renderSkinGroupGeom(alphaMRM, remainingVertices, 
01358                                         vbDest + vertexSize*currentBaseVertex );
01359                                 // -1 means that this skin can't render because no space left for her. Then stop for this block
01360                                 if(numVerticesAdded==-1)
01361                                         break;
01362                                 // Else ok, get the currentBaseVertex for this skin
01363                                 baseVertices[skinId]= currentBaseVertex;
01364                                 // and jump to the next place
01365                                 currentBaseVertex+= numVerticesAdded;
01366                                 remainingVertices-= numVerticesAdded;
01367 
01368                                 // go to the next skin
01369                                 skinId++;
01370                         }
01371 
01372                         // release buffer. ATI: release only vertices used.
01373                         meshSkinManager.unlock(currentBaseVertex);
01374 
01375                         // Second pass, render the primitives.
01376                         //------------
01377                         meshSkinManager.activate();
01378                         for(uint i=startSkinId;i<skinId;i++)
01379                         {
01380                                 // render the skin in the current buffer
01381                                 skinsToGroup[i]->renderSkinGroupPrimitives(baseVertices[i]);
01382                         }
01383 
01384 
01385                         // End of this block, swap to the next buffer
01386                         meshSkinManager.swapVBHard();
01387                 }
01388         }
01389 }
01390 
01391 
01392 // ***************************************************************************
01393 float                   CSkeletonModel::getNumTriangles (float distance)
01394 {
01395         // If the skeleton is displayed as a CLod suppose 0 triangles.
01396         if( isDisplayedAsLodCharacter() )
01397                 return 0;
01398         else
01399                 // NB: this is an approximation, but this is continious.
01400                 return _LevelDetail.getNumTriangles(distance);
01401 }
01402 
01403 // ***************************************************************************
01404 void                    CSkeletonModel::changeMRMDistanceSetup(float distanceFinest, float distanceMiddle, float distanceCoarsest)
01405 {
01406         // check input.
01407         if(distanceFinest<0)    return;
01408         if(distanceMiddle<=distanceFinest)      return;
01409         if(distanceCoarsest<=distanceMiddle)    return;
01410 
01411         // Change.
01412         _LevelDetail.DistanceFinest= distanceFinest;
01413         _LevelDetail.DistanceMiddle= distanceMiddle;
01414         _LevelDetail.DistanceCoarsest= distanceCoarsest;
01415 
01416         // compile 
01417         _LevelDetail.compileDistanceSetup();
01418 
01419         // Never more use MAX skin setup.
01420         _DefaultMRMSetup= false;
01421 }
01422 
01423 
01424 // ***************************************************************************
01425 void                    CSkeletonModel::resetDefaultMRMDistanceSetup()
01426 {
01427         _DefaultMRMSetup= true;
01428 
01429         // Must use Skins linked to know the MRM setup.
01430         dirtSkinRenderLists();
01431 }
01432 
01433 
01434 // ***************************************************************************
01435 bool                    CSkeletonModel::computeRenderedBBox(NLMISC::CAABBox &bbox)
01436 {
01437         // reset bbox
01438         CAABBox         tmpBBox;
01439         tmpBBox.setCenter(CVector::Null);
01440         tmpBBox.setHalfSize(CVector::Null);
01441         bool    empty= true;
01442 
01443         // Not visible => empty bbox
01444         if(!getLastClippedState())
01445                 return false;
01446 
01447         // For all bones
01448         uint    i;
01449         for(i=0;i<Bones.size();i++)
01450         {
01451                 if(isBoneComputed(i))
01452                 {
01453                         const CVector   &pos= Bones[i].getLocalSkeletonMatrix().getPos();
01454                         if(empty)
01455                         {
01456                                 empty= false;
01457                                 tmpBBox.setCenter(pos);
01458                         }
01459                         else
01460                                 tmpBBox.extend(pos);
01461                 }
01462         }
01463 
01464         // End!
01465         if(!empty)
01466         {
01467                 bbox= tmpBBox;
01468                 return true;
01469         }
01470         else
01471                 return false;
01472 }
01473 
01474 
01475 // ***************************************************************************
01476 bool                    CSkeletonModel::computeCurrentBBox(NLMISC::CAABBox &bbox, bool forceCompute /* = false*/)
01477 {
01478         // animate all bones channels (detail only channels). don't bother cur lod state.
01479         CChannelMixer   *chanmix= getChannelMixer();
01480         if (chanmix)
01481         {       
01482                 // Force detail evaluation.
01483                 chanmix->resetEvalDetailDate();
01484                 chanmix->eval(true, 0);
01485                 chanmix->resetEvalDetailDate();
01486         }
01487         // compute all skeleton bones
01488         computeAllBones(CMatrix::Identity);
01489 
01490         // reset bbox
01491         CAABBox         tmpBBox;
01492         tmpBBox.setCenter(CVector::Null);
01493         tmpBBox.setHalfSize(CVector::Null);
01494         bool    empty= true;
01495 
01496         // For all bones
01497         uint    i;
01498         for(i=0;i<Bones.size();i++)
01499         {
01500                 // Is the bone used ?? (whatever bone lod, or CLod state)
01501                 uint8   mustCompute = forceCompute ? 1 : _BoneUsage[i].Usage | _BoneUsage[i].ForcedUsage | _BoneUsage[i].CLodForcedUsage;
01502 
01503                 // If the bone is used.
01504                 if(mustCompute)
01505                 {
01506                         const CVector   &pos= Bones[i].getLocalSkeletonMatrix().getPos();
01507                         if(empty)
01508                         {
01509                                 empty= false;
01510                                 tmpBBox.setCenter(pos);
01511                         }
01512                         else
01513                                 tmpBBox.extend(pos);
01514                 }
01515         }
01516 
01517         // End!
01518         if(!empty)
01519         {
01520                 bbox= tmpBBox;
01521                 return true;
01522         }
01523         else
01524                 return false;
01525 }
01526 
01527 
01528 } // NL3D