/* Copyright (c) 2002-2012 Croteam Ltd. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // does parser remember smc source files? BOOL bRememberSourceFN = FALSE; // pointer to model instance for parser CModelInstance *_yy_mi = NULL; // calculate fade factor of animation list in animqueue FLOAT CalculateFadeFactor(AnimList &alList) { if(alList.al_fFadeTime==0) { return 1.0f; } FLOAT fFadeFactor = (_pTimer->GetLerpedCurrentTick() - alList.al_fStartTime) / alList.al_fFadeTime; return Clamp(fFadeFactor,0.0f,1.0f); } // create model instance CModelInstance *CreateModelInstance(CTString strName) { CModelInstance *pmi = new CModelInstance; pmi->SetName(strName); return pmi; } void DeleteModelInstance(CModelInstance *pmi) { ASSERT(pmi!=NULL); // if model instance is valid if(pmi!=NULL) { // Clear model instance pmi->Clear(); // Delete model instance delete pmi; pmi = NULL; } } // Parse smc file in existing model instance void ParseSmcFile_t(CModelInstance &mi, const CTString &fnSmcFile) { // Clear given model instance before parsing mi.Clear(); CTFileName fnFileName = fnSmcFile; try { fnFileName.RemoveApplicationPath_t(); } catch (char *) { } CTString strIncludeFile; strIncludeFile.Load_t(fnFileName); _yy_mi = &mi; SMCPushBuffer(fnFileName, strIncludeFile, TRUE); engine_ska_yyparse(); } // Create model instance and parse smc file in it CModelInstance *ParseSmcFile_t(const CTString &fnSmcFile) { _yy_mi = NULL; // Create new model instance for parser _yy_mi = CreateModelInstance("Temp"); // Parse given smc file ParseSmcFile_t(*_yy_mi,fnSmcFile); return _yy_mi; } CModelInstance::CModelInstance() { mi_psklSkeleton = NULL; mi_iParentBoneID = -1; mi_colModelColor = 0; mi_vStretch = FLOAT3D(1,1,1); mi_colModelColor = 0xFFFFFFFF; mi_aqAnims.aq_Lists.SetAllocationStep(1); mi_cmiChildren.SetAllocationStep(1); memset(&mi_qvOffset,0,sizeof(QVect)); mi_qvOffset.qRot.q_w = 1; mi_iCurentBBox = -1; // set default all frames bbox // mi_cbAllFramesBBox.SetName("All Frames Bounding box"); mi_cbAllFramesBBox.SetMin(FLOAT3D(-0.5,0,-0.5)); mi_cbAllFramesBBox.SetMax(FLOAT3D(0.5,2,0.5)); // Set default model instance name // SetName("Noname"); } CModelInstance::~CModelInstance() { } // copy constructor CModelInstance::CModelInstance(CModelInstance &miOther) { // Forbiden ASSERT(FALSE); } void CModelInstance::operator=(CModelInstance &miOther) { // Forbiden ASSERT(FALSE); } void CModelInstance::GetAllFramesBBox(FLOATaabbox3D &aabbox) { aabbox = FLOATaabbox3D(mi_cbAllFramesBBox.Min(),mi_cbAllFramesBBox.Max()); } // fills curent colision box info void CModelInstance::GetCurrentColisionBox(FLOATaabbox3D &aabbox) { ColisionBox &cb = GetCurrentColisionBox(); aabbox = FLOATaabbox3D(cb.Min(),cb.Max()); } ColisionBox &CModelInstance::GetCurrentColisionBox() { ASSERT(mi_iCurentBBox>=0); ASSERT(mi_iCurentBBox0); return mi_cbAABox[mi_iCurentBBox]; } INDEX CModelInstance::GetColisionBoxIndex(INDEX iBoxID) { INDEX ctcb = mi_cbAABox.Count(); // for each existing box for(SLONG icb=0;icb=0); ASSERT(icb=mi_cbAABox.Count()) { // give last colision box iCollisionBox = mi_cbAABox.Count()-1; } // check if error is fixed ASSERT(mi_cbAABox.Count()>iCollisionBox); ColisionBox &cb = this->mi_cbAABox[iCollisionBox]; FLOAT fWeigth = cb.Max()(1) - cb.Min()(1); FLOAT fHeight = cb.Max()(2) - cb.Min()(2); FLOAT fLength = cb.Max()(3) - cb.Min()(3); if(fLength == fHeight) { return SKA_LENGTH_EQ_HEIGHT; } else if(fHeight == fWeigth) { return SKA_HEIGHT_EQ_WIDTH; // default fLength == fWeight } else { return SKA_LENGTH_EQ_WIDTH; } }; // add colision box to model instance void CModelInstance::AddColisionBox(CTString strName,FLOAT3D vMin,FLOAT3D vMax) { INDEX ctcb = mi_cbAABox.Count(); mi_cbAABox.Expand(ctcb+1); ColisionBox &cb = mi_cbAABox[ctcb]; cb.SetName(strName); cb.SetMin(vMin); cb.SetMax(vMax); mi_iCurentBBox = 0; } // remove colision box from model instance void CModelInstance::RemoveColisionBox(INDEX iIndex) { INDEX ctcb = mi_cbAABox.Count(); INDEX icbNew = 0; CStaticArray aColisionBoxesTemp; aColisionBoxesTemp.New(ctcb-1); for(INDEX icb=0;icb0) { pmi->SetParentBone(iParentBoneID); } } // remove model instance child void CModelInstance::RemoveChild(CModelInstance *pmi) { ASSERT(pmi!=this); SKAASSERT(pmi!=NULL); // aditional check if(pmi==NULL) return; if(pmi==this) return; mi_cmiChildren.Remove(pmi); } // set new parent bone index void CModelInstance::SetParentBone(INDEX iParentBoneID) { mi_iParentBoneID = iParentBoneID; } // Model instance offsets from parent model void CModelInstance::SetOffset(FLOAT fOffset[6]) { FLOAT3D fRot(fOffset[3],fOffset[4],fOffset[5]); mi_qvOffset.qRot.FromEuler(fRot); mi_qvOffset.vPos = FLOAT3D(fOffset[0],fOffset[1],fOffset[2]); } void CModelInstance::SetOffsetPos(FLOAT3D vPos) { mi_qvOffset.vPos = vPos; } void CModelInstance::SetOffsetRot(ANGLE3D aRot) { mi_qvOffset.qRot.FromEuler(aRot); } FLOAT3D CModelInstance::GetOffsetPos() { return mi_qvOffset.vPos; } ANGLE3D CModelInstance::GetOffsetRot() { ANGLE3D aRot; FLOATmatrix3D mat; mi_qvOffset.qRot.ToMatrix(mat); DecomposeRotationMatrix(aRot,mat); return aRot; } // Stretch model instance void CModelInstance::StretchModel(const FLOAT3D &vStretch) { mi_vStretch = vStretch; } // Stretch model instance without attachments void CModelInstance::StretchSingleModel(const FLOAT3D &vStretch) { mi_vStretch = vStretch; // for each child of model instance INDEX ctch = mi_cmiChildren.Count(); for(INDEX ich=0;ichObtain_t(fnMesh); } // Add skeleton to ModelInstance void CModelInstance::AddSkeleton_t(CTFileName fnSkeleton) { mi_psklSkeleton = _pSkeletonStock->Obtain_t(fnSkeleton); } // Add AnimSet to ModelInstance void CModelInstance::AddAnimSet_t(CTFileName fnAnimSet) { CAnimSet *Anim = _pAnimSetStock->Obtain_t(fnAnimSet); mi_aAnimSet.Add(Anim); } // Add texture to ModelInstance (if no mesh instance given, add texture to last mesh instance) void CModelInstance::AddTexture_t(CTFileName fnTexture, CTString strTexID,MeshInstance *pmshi) { if(pmshi == NULL) { INDEX ctMeshInst = mi_aMeshInst.Count(); if(ctMeshInst<=0) throw("Error adding texture\nMesh instance does not exists"); pmshi = &mi_aMeshInst[ctMeshInst-1]; } INDEX ctTextInst = pmshi->mi_tiTextures.Count(); pmshi->mi_tiTextures.Expand(ctTextInst+1); pmshi->mi_tiTextures[ctTextInst].ti_toTexture.SetData_t(fnTexture); pmshi->mi_tiTextures[ctTextInst].SetName(strTexID); } // Remove one texture from model instance void CModelInstance::RemoveTexture(TextureInstance *ptiRemove,MeshInstance *pmshi) { ASSERT(pmshi!=NULL); CStaticArray atiTextures; INDEX ctti=pmshi->mi_tiTextures.Count(); atiTextures.New(ctti-1); // for each texture instance in mesh instance INDEX iIndexSrc=0; for(INDEX iti=0;itimi_tiTextures[iti]; // if texture instance is different from selected one if(pti != ptiRemove) { // copy it to new array of texture isntances atiTextures[iIndexSrc] = pmshi->mi_tiTextures[iti]; iIndexSrc++; } } // copy new texture instances array in mesh instance pmshi->mi_tiTextures.CopyArray(atiTextures); // clear temp texture isntances array atiTextures.Clear(); } // Find texture instance in all mesh instances in model instance TextureInstance *CModelInstance::FindTexureInstance(INDEX iTexID) { // for each mesh instance INDEX ctmshi = mi_aMeshInst.Count(); for(INDEX imshi=0;imshimi_cmiChildren.Remove(this); pmiNewParent->mi_cmiChildren.Add(this); } // return parent of this model instance // must suply first model instance in hierarchy cos model instance does not have its parent remembered CModelInstance *CModelInstance::GetParent(CModelInstance *pmiStartFrom) { ASSERT(pmiStartFrom!=NULL); // aditional check if(pmiStartFrom==NULL) return NULL; // if 'this' is member of pmiStartFrom return it if(pmiStartFrom->mi_cmiChildren.IsMember(this)) { return pmiStartFrom; } // count childrent of pmiStartFrom INDEX ctcmi = pmiStartFrom->mi_cmiChildren.Count(); // for each child of pmiStartFrom for(INDEX icmi=0;icmimi_cmiChildren[icmi]); if(pmiReturned != NULL) { return pmiReturned; } } return NULL; } // returns child with specified id /*CModelInstance *CModelInstance::GetChild(INDEX iChildID, BOOL bRecursive) { INDEX ctcmi = mi_cmiChildren.Count(); for(INDEX icmi=0;icmimi_iModelID == iChildID) { return pmi; } } return NULL; }*/ CModelInstance *CModelInstance::GetChild(INDEX iChildID, BOOL bRecursive/*=FALSE*/) { INDEX ctcmi = mi_cmiChildren.Count(); for(INDEX icmi=0;icmimi_iModelID == iChildID) { return pmi; } // if child has own children, go recursive if(bRecursive && pmi->mi_cmiChildren.Count()>0) { pmi = pmi->GetChild(iChildID, TRUE); if (pmi!=NULL) return pmi; } } return NULL; } // returns parent that is not included in his parents smc file CModelInstance *CModelInstance::GetFirstNonReferencedParent(CModelInstance *pmiRoot) { ASSERT(this!=NULL); ASSERT(pmiRoot!=NULL); CModelInstance *pmiParent = this->GetParent(pmiRoot); CModelInstance *pmiLast = this; while(pmiParent != NULL) { if(pmiParent->mi_fnSourceFile != mi_fnSourceFile) { return pmiLast; } pmiLast = pmiParent; pmiParent = pmiParent->GetParent(pmiRoot); } return NULL;//return pmiRoot } // add animation to ModelInstance void CModelInstance::AddAnimation(INDEX iAnimID, ULONG ulFlags, FLOAT fStrength, INDEX iGroupID, FLOAT fSpeedMul/*=1.0f*/) { #ifdef SKADEBUG // see whether this animation even exists in the current skeleton INDEX iDummy1, iDummy2; if (!FindAnimationByID(iAnimID, &iDummy1, &iDummy2)) { /*this ModelInstance does not contain the required animation!!!*/ SKAASSERT(FALSE); } #endif fSpeedMul = 1/fSpeedMul; // if no restart flag was set if(ulFlags&AN_NORESTART) { // if given animtion is allready playing if(IsAnimationPlaying(iAnimID)) { if(ulFlags&AN_LOOPING) { AddFlagsToPlayingAnim(iAnimID,AN_LOOPING); } // return without adding animtion return; } } // if flag for new cleared state is set if(ulFlags&AN_CLEAR) { // do new clear state with default length NewClearState(CLEAR_STATE_LENGTH); // if flag for new cloned state is set } else if(ulFlags&AN_CLONE) { // do new clear state with default length NewClonedState(CLONED_STATE_LENGTH); } // if anim queue is empty INDEX ctal = mi_aqAnims.aq_Lists.Count(); if(ctal == 0) { // add new clear state NewClearState(0); } ctal = mi_aqAnims.aq_Lists.Count(); AnimList &alList = mi_aqAnims.aq_Lists[ctal-1]; // if flag is set not to sort anims if(ulFlags&AN_NOGROUP_SORT) { // just add new animations to end of list PlayedAnim &plAnim = alList.al_PlayedAnims.Push(); plAnim.pa_iAnimID = iAnimID; plAnim.pa_fSpeedMul = fSpeedMul; plAnim.pa_fStartTime = _pTimer->CurrentTick(); plAnim.pa_Strength = fStrength; plAnim.pa_ulFlags = ulFlags; plAnim.pa_GroupID = iGroupID; // no flag set, sort animation by groupID } else { // add one animation to anim list alList.al_PlayedAnims.Push(); INDEX ctpa = alList.al_PlayedAnims.Count(); INDEX ipa=ctpa-1; if(ipa>0) { // for each old animation from last to first for(;ipa>0;ipa--) { PlayedAnim &pa = alList.al_PlayedAnims[ipa-1]; PlayedAnim &paNext = alList.al_PlayedAnims[ipa]; // if anim group id is larger than new group id if(pa.pa_GroupID>iGroupID) { // move animation in array to right paNext = pa; } else break; } } // set new animation as current index in anim list PlayedAnim &plAnim = alList.al_PlayedAnims[ipa]; plAnim.pa_iAnimID = iAnimID; plAnim.pa_fSpeedMul = fSpeedMul; plAnim.pa_fStartTime = _pTimer->CurrentTick(); plAnim.pa_Strength = fStrength; plAnim.pa_ulFlags = ulFlags; plAnim.pa_GroupID = iGroupID; } } // remove played anim from stack void CModelInstance::RemAnimation(INDEX iAnimID) { INDEX ctal = mi_aqAnims.aq_Lists.Count(); // if anim queue is empty if(ctal < 1) { SKAASSERT(FALSE); // no anim to remove return; } // get last anim list in queue AnimList &alList = mi_aqAnims.aq_Lists[ctal-1]; // count played anims in anim list INDEX ctpa = alList.al_PlayedAnims.Count(); // loop each played anim in anim list for(int ipa=0;ipa=0;ial--) { AnimList &alList = mi_aqAnims.aq_Lists[ial]; // calculate fade factor for this animlist FLOAT fFadeFactor = CalculateFadeFactor(alList); // if factor is 1 remove all animlists before this one if(fFadeFactor >= 1.0f) { iFirstAnimList = ial; break; } } if(iFirstAnimList <= 0) return; // move later anim lists to first pos for(ial=iFirstAnimList;ialCurrentTick(); } // create new cleared state and give it a fade time void CModelInstance::NewClearState(FLOAT fFadeTime) { RemovePassedAnimsFromQueue(); // add new empty list AnimList &alNewList = mi_aqAnims.aq_Lists.Push(); alNewList.al_PlayedAnims.SetAllocationStep(1); alNewList.al_fFadeTime = fFadeTime; alNewList.al_fStartTime = _pTimer->CurrentTick(); alNewList.al_PlayedAnims.PopAll(); } // stop all animations in this model instance and its children void CModelInstance::StopAllAnimations(FLOAT fFadeTime) { INDEX ctmi = mi_cmiChildren.Count(); for(INDEX imi=0;imi=0;ias--) { CAnimSet &asAnimSet = mi_aAnimSet[ias]; INDEX ctan = asAnimSet.as_Anims.Count(); // for each animation for(int ian=0;ian0) { // check last one AnimList &al = mi_aqAnims.aq_Lists[ctal-1]; INDEX ctpa = al.al_PlayedAnims.Count(); for(INDEX ipa=0;ipa0) { // check last one AnimList &al = mi_aqAnims.aq_Lists[ctal-1]; INDEX ctpa = al.al_PlayedAnims.Count(); for(INDEX ipa=0;ipa &avVertices, FLOATmatrix3D &mRotation, FLOAT3D &vPosition, FLOAT fNormalOffset, FLOAT fDistance) { RM_SetObjectPlacement(mRotation,vPosition); RM_GetModelVertices(*this,avVertices, mRotation, vPosition, fNormalOffset, fDistance); } // Model color COLOR &CModelInstance::GetModelColor(void) { return mi_colModelColor; } void CModelInstance::SetModelColor(COLOR colNewColor) { mi_colModelColor = colNewColor; INDEX ctch = mi_cmiChildren.Count(); // for each child for(INDEX ich=0;ich> IsModelVisible") return TRUE; } BOOL CModelInstance::HasShadow(FLOAT fMipFactor) { #pragma message(">> HasShadow") return TRUE; } // simple shadow rendering void CModelInstance::AddSimpleShadow( const FLOAT fIntensity, const FLOATplane3D &plShadowPlane) { // if shadows are not rendered for current mip, model is half/full face-forward, // intensitiy is too low or projection is not perspective - do nothing! //if( !HasShadow(1) || fIntensity<0.01f || !_aprProjection.IsPerspective() // || (rm.rm_pmdModelData->md_Flags&(MF_FACE_FORWARD|MF_HALF_FACE_FORWARD))) return; // ASSERT( _iRenderingType==1); ASSERT( fIntensity>0 && fIntensity<=1); // do some rendering // _pfModelProfile.StartTimer( CModelProfile::PTI_RENDERSIMPLESHADOW); // _pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_RENDERSIMPLESHADOW); // _sfStats.IncrementCounter( CStatForm::SCI_MODELSHADOWS); // calculate projection model bounding box in object space (if needed) // CalculateBoundingBox( this, rm); // add one simple shadow to batch list RM_SetObjectMatrices(*this); RM_AddSimpleShadow_View(*this, fIntensity, plShadowPlane); // all done // _pfModelProfile.StopTimer( CModelProfile::PTI_RENDERSIMPLESHADOW); } // Copy mesh instance for other model instance void CModelInstance::CopyMeshInstance(CModelInstance &miOther) { INDEX ctmshi = miOther.mi_aMeshInst.Count(); // for each mesh insntace for(INDEX imshi=0;imshiGetName()); MeshInstance *pmshi = &mi_aMeshInst[imshi]; INDEX ctti = mshiOther.mi_tiTextures.Count(); // for each texture in mesh instance for(INDEX iti=0;itiGetName()); } // copy animsets INDEX ctas = miOther.mi_aAnimSet.Count(); // for each animset for(INDEX ias=0;iasCopy(chmiOther); AddChild(pchmi); } } // Synchronize with another model (copy animations/attachments positions etc from there) void CModelInstance::Synchronize(CModelInstance &miOther) { // Sync animations mi_aqAnims.aq_Lists = miOther.mi_aqAnims.aq_Lists; // Sync misc params mi_qvOffset = miOther.mi_qvOffset; mi_iParentBoneID = miOther.mi_iParentBoneID; mi_colModelColor = miOther.mi_colModelColor; mi_vStretch = miOther.mi_vStretch; // for each child in model instance INDEX ctchmi=mi_cmiChildren.Count(); for(INDEX ichmi=0;ichmiRelease(pmesh); } // release all textures in stock used by mesh INDEX ctti = mshi.mi_tiTextures.Count(); for(INDEX iti=0;itiRelease(mi_psklSkeleton); mi_psklSkeleton = NULL; } // release all animsets in stock used by mi INDEX ctas = mi_aAnimSet.Count(); for(INDEX ias=0;iasRelease(&mi_aAnimSet[ias]); } mi_aAnimSet.Clear(); // clear all colision boxes mi_cbAABox.Clear(); // clear anim list mi_aqAnims.aq_Lists.Clear(); } // Count used memory SLONG CModelInstance::GetUsedMemory(void) { SLONG slMemoryUsed = sizeof(*this); // Count mesh instances INDEX ctmshi = mi_aMeshInst.Count(); for(INDEX imshi=0;imshi