diff --git a/src/Client/GUI/CBoneSceneNode.cpp b/src/Client/GUI/CBoneSceneNode.cpp new file mode 100755 index 0000000..735c629 --- /dev/null +++ b/src/Client/GUI/CBoneSceneNode.cpp @@ -0,0 +1,130 @@ +// Copyright (C) 2002-2007 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h +#include "irrlicht/irrlicht.h" + +#include "CBoneSceneNode.h" + +namespace irr +{ +namespace scene +{ + +//! constructor +CBoneSceneNode::CBoneSceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id, + u32 boneIndex, const c8* boneName) +: IBoneSceneNode(parent, mgr, id), BoneIndex(boneIndex), BoneName(boneName), + AnimationMode(EBAM_AUTOMATIC), SkinningSpace(EBSS_LOCAL) +{ + + #ifdef _DEBUG + setDebugName("CBoneSceneNode"); + #endif +} + + +//! Returns the name of the bone +const c8* CBoneSceneNode::getBoneName() const +{ + return BoneName.c_str(); +} + + +//! Returns the index of the bone +u32 CBoneSceneNode::getBoneIndex() const +{ + return BoneIndex; +} + + +//! Sets the animation mode of the bone. Returns true if successful. +bool CBoneSceneNode::setAnimationMode(E_BONE_ANIMATION_MODE mode) +{ + AnimationMode = mode; + return true; +} + + +//! Gets the current animation mode of the bone +E_BONE_ANIMATION_MODE CBoneSceneNode::getAnimationMode() const +{ + return AnimationMode; +} + + +//! returns the axis aligned bounding box of this node +const core::aabbox3d& CBoneSceneNode::getBoundingBox() const +{ + return Box; +} + + +/* +//! Returns the relative transformation of the scene node. +core::matrix4 CBoneSceneNode::getRelativeTransformation() const +{ + return core::matrix4(); // RelativeTransformation; +} +*/ + + +void CBoneSceneNode::OnAnimate(u32 timeMs) +{ + if (IsVisible) + { + // animate this node with all animators + + core::list::Iterator ait = Animators.begin(); + for (; ait != Animators.end(); ++ait) + (*ait)->animateNode(this, timeMs); + + // update absolute position + //updateAbsolutePosition(); + + // perform the post render process on all children + core::list::Iterator it = Children.begin(); + for (; it != Children.end(); ++it) + (*it)->OnAnimate(timeMs); + } +} + + +void CBoneSceneNode::helper_updateAbsolutePositionOfAllChildren(ISceneNode *Node) +{ + Node->updateAbsolutePosition(); + + core::list::ConstIterator it = Node->getChildren().begin(); + for (; it != Node->getChildren().end(); ++it) + { + helper_updateAbsolutePositionOfAllChildren( (*it) ); + } +} + + +void CBoneSceneNode::updateAbsolutePositionOfAllChildren() +{ + helper_updateAbsolutePositionOfAllChildren( this ); +} + + +void CBoneSceneNode::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const +{ + out->addInt("BoneIndex", BoneIndex); + out->addString("BoneName", BoneName.c_str()); + out->addEnum("AnimationMode", AnimationMode, BoneAnimationModeNames); +} + + +void CBoneSceneNode::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options) +{ + BoneIndex = in->getAttributeAsInt("BoneIndex"); + BoneName = in->getAttributeAsString("BoneName"); + AnimationMode = (E_BONE_ANIMATION_MODE)in->getAttributeAsEnumeration("AnimationMode", BoneAnimationModeNames); + // TODO: add/replace bone in parent with bone from mesh +} + + +} // namespace scene +} // namespace irr + + diff --git a/src/Client/GUI/CBoneSceneNode.h b/src/Client/GUI/CBoneSceneNode.h new file mode 100755 index 0000000..284732a --- /dev/null +++ b/src/Client/GUI/CBoneSceneNode.h @@ -0,0 +1,84 @@ +// Copyright (C) 2002-2007 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#ifndef __C_BONE_SCENE_NODE_H_INCLUDED__ +#define __C_BONE_SCENE_NODE_H_INCLUDED__ + +// Used with SkinnedMesh and IAnimatedMeshSceneNode, for boned meshes +#include "irrlicht/irrlicht.h" + + +namespace irr +{ +namespace scene +{ + + class CBoneSceneNode : public IBoneSceneNode + { + public: + + //! constructor + CBoneSceneNode(ISceneNode* parent, ISceneManager* mgr, + s32 id=-1, u32 boneIndex=0, const c8* boneName=0); + + //! Returns the name of the bone + virtual const c8* getBoneName() const; + + //! Returns the index of the bone + virtual u32 getBoneIndex() const; + + //! Sets the animation mode of the bone. Returns true if successful. + virtual bool setAnimationMode(E_BONE_ANIMATION_MODE mode); + + //! Gets the current animation mode of the bone + virtual E_BONE_ANIMATION_MODE getAnimationMode() const; + + //! returns the axis aligned bounding box of this node + virtual const core::aabbox3d& getBoundingBox() const; + + /* + //! Returns the relative transformation of the scene node. + //virtual core::matrix4 getRelativeTransformation() const; + */ + + virtual void OnAnimate(u32 timeMs); + + virtual void updateAbsolutePositionOfAllChildren(); + + //! Writes attributes of the scene node. + virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const; + + //! Reads attributes of the scene node. + virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0); + + //! How the relative transformation of the bone is used + virtual void setSkinningSpace( E_BONE_SKINNING_SPACE space ) + { + SkinningSpace=space; + } + + virtual E_BONE_SKINNING_SPACE getSkinningSpace() const + { + return SkinningSpace; + } + + private: + void helper_updateAbsolutePositionOfAllChildren(ISceneNode *Node); + + u32 BoneIndex; + core::stringc BoneName; + + core::aabbox3d Box; + + E_BONE_ANIMATION_MODE AnimationMode; + E_BONE_SKINNING_SPACE SkinningSpace; + }; + +} // end namespace scene +} // end namespace irr + + + +#endif + diff --git a/src/Client/GUI/CM2MeshFileLoader.cpp b/src/Client/GUI/CM2MeshFileLoader.cpp index baf2a13..c9063da 100644 --- a/src/Client/GUI/CM2MeshFileLoader.cpp +++ b/src/Client/GUI/CM2MeshFileLoader.cpp @@ -1,7 +1,32 @@ #include #include "CM2MeshFileLoader.h" +#include "SSkinnedMesh.h" +#define _DEBUG #include "common.h" +#ifdef _DEBUG +#define DEBUG(code) code; +#else +#define DEBUG(code) ; +#endif +/* + +void logdebug(const char *str, ...) +{ + if(!str) + return; + va_list ap; +// _log_setcolor(true,LBLUE); + va_start(ap, str); + vprintf( str, ap ); + va_end(ap); +// _log_resetcolor(true); + + + printf("\n"); + + fflush(stdout); +}*/ namespace irr { @@ -32,14 +57,31 @@ bool CM2MeshFileLoader::isALoadableFileExtension(const c8* filename)const //! See IUnknown::drop() for more information. IAnimatedMesh* CM2MeshFileLoader::createMesh(io::IReadFile* file) { -ILogger* logger =Device->getLogger(); + if(!file) + return 0; + MeshFile = file; + AnimatedMesh = new scene::CSkinnedMesh(); -DEBUG(logger->log("Trying to open file",file->getFileName(),ELL_INFORMATION)); + if ( load() ) + { + AnimatedMesh->finalize(); + } + else + { + AnimatedMesh->drop(); + AnimatedMesh = 0; + } + + return AnimatedMesh; +} +bool CM2MeshFileLoader::load() +{ +DEBUG(logdebug("Trying to open file %s",MeshFile->getFileName())); -file->read(&header,sizeof(ModelHeader)); - if (header.version[0] != 4 && header.version[1] != 1 && header.version[2] != 0 && header.version[3] != 0) { - logger->log("Something wrong!",ELL_ERROR); +MeshFile->read(&header,sizeof(ModelHeader)); +if (header.version[0] != 4 && header.version[1] != 1 && header.version[2] != 0 && header.version[3] != 0) { + printf("Wrong header! File version doesn't match or file is not a M2 file."); return 0; } else @@ -60,11 +102,19 @@ if(!M2MVertices.empty()) M2MVertices.clear(); ModelVertex tempM2MVert; -file->seek(header.ofsVertices); +f32 tempYZ; +MeshFile->seek(header.ofsVertices); for(u32 i =0;iread(&tempM2MVert,sizeof(ModelVertex)); + MeshFile->read(&tempM2MVert,sizeof(ModelVertex)); + tempYZ = tempM2MVert.pos.Y; + tempM2MVert.pos.Y=tempM2MVert.pos.Z; + tempM2MVert.pos.Z=tempYZ; + tempYZ = tempM2MVert.normal.Y; + tempM2MVert.normal.Y=tempM2MVert.normal.Z; + tempM2MVert.normal.Z=tempYZ; + M2MVertices.push_back(tempM2MVert); } DEBUG(logdebug("Read %u/%u Vertices",M2MVertices.size(),header.nVertices)); @@ -73,10 +123,10 @@ DEBUG(logdebug("Read %u/%u Vertices",M2MVertices.size(),header.nVertices)); if(M2MViews.size()>0) M2MViews.clear(); ModelView tempM2MView; -file->seek(header.ofsViews); +MeshFile->seek(header.ofsViews); for(u32 i =0;iread(&tempM2MView,sizeof(ModelView)); + MeshFile->read(&tempM2MView,sizeof(ModelView)); M2MViews.push_back(tempM2MView); } //std::cout << "Read "<0) M2MIndices.clear(); u16 tempM2Index; -file->seek(M2MViews[0].ofsIndex); +MeshFile->seek(M2MViews[0].ofsIndex); for(u32 i =0;iread(&tempM2Index,sizeof(u16)); + MeshFile->read(&tempM2Index,sizeof(u16)); M2MIndices.push_back(tempM2Index); } DEBUG(logdebug("Read %u/%u Indices",M2MIndices.size(),M2MViews[0].nIndex)); @@ -103,10 +153,10 @@ if(M2MTriangles.size()>0) M2MTriangles.clear(); u16 tempM2Triangle; -file->seek(M2MViews[0].ofsTris); +MeshFile->seek(M2MViews[0].ofsTris); for(u32 i =0;iread(&tempM2Triangle,sizeof(u16)); + MeshFile->read(&tempM2Triangle,sizeof(u16)); M2MTriangles.push_back(tempM2Triangle); } DEBUG(logdebug("Read %u/%u Triangles",M2MTriangles.size(),M2MViews[0].nTris)); @@ -115,10 +165,10 @@ if(M2MSubmeshes.size()>0) M2MSubmeshes.clear(); ModelViewSubmesh tempM2Submesh; -file->seek(M2MViews[0].ofsSub); +MeshFile->seek(M2MViews[0].ofsSub); for(u32 i =0;iread(&tempM2Submesh,sizeof(ModelViewSubmesh)); + MeshFile->read(&tempM2Submesh,sizeof(ModelViewSubmesh)); M2MSubmeshes.push_back(tempM2Submesh); // std::cout<< "Submesh " <seek(M2MViews[0].ofsTex); +MeshFile->seek(M2MViews[0].ofsTex); for(u32 i=0;iread(&tempM2TexUnit,sizeof(TextureUnit)); + MeshFile->read(&tempM2TexUnit,sizeof(TextureUnit)); M2MTextureUnit.push_back(tempM2TexUnit); +DEBUG(logdebug(" TexUnit %u: Submesh: %u %u Render Flag: %u TextureUnitNumber: %u %u TTU: %u",i,tempM2TexUnit.submeshIndex1,tempM2TexUnit.submeshIndex2, tempM2TexUnit.renderFlagsIndex, tempM2TexUnit.TextureUnitNumber, tempM2TexUnit.TextureUnitNumber2 ,tempM2TexUnit.textureIndex)); } DEBUG(logdebug("Read %u Texture Unit entries for View 0",M2MTextureUnit.size())); @@ -147,11 +198,12 @@ if(!M2MTextureLookup.empty()) { M2MTextureLookup.clear(); } -file->seek(header.ofsTexLookup); +MeshFile->seek(header.ofsTexLookup); for(u32 i=0;iread(&tempM2TexLookup,sizeof(u16)); + MeshFile->read(&tempM2TexLookup,sizeof(u16)); M2MTextureLookup.push_back(tempM2TexLookup); + printf("Texture %u Type %u\n",i,tempM2TexLookup); } DEBUG(logdebug("Read %u Texture lookup entries",M2MTextureLookup.size())); @@ -161,11 +213,12 @@ if(!M2MTextureDef.empty()) { M2MTextureDef.clear(); } -file->seek(header.ofsTextures); +MeshFile->seek(header.ofsTextures); for(u32 i=0;iread(&tempM2TexDef,sizeof(TextureDefinition)); + MeshFile->read(&tempM2TexDef,sizeof(TextureDefinition)); M2MTextureDef.push_back(tempM2TexDef); + printf("Texture %u Type %u\n",i,tempM2TexDef.texType); } DEBUG(logdebug("Read %u Texture Definition entries",M2MTextureDef.size())); @@ -175,11 +228,12 @@ if(!M2MRenderFlags.empty()) { M2MRenderFlags.clear(); } -file->seek(header.ofsTexFlags); +MeshFile->seek(header.ofsTexFlags); for(u32 i=0;iread(&tempM2RF,sizeof(RenderFlags)); + MeshFile->read(&tempM2RF,sizeof(RenderFlags)); M2MRenderFlags.push_back(tempM2RF); + DEBUG(logdebug("Flag %u: (%u, %u)",i,tempM2RF.blending,tempM2RF.flags)); } DEBUG(logdebug("Read %u Renderflags",M2MRenderFlags.size())); @@ -195,15 +249,218 @@ M2MTextureFiles.reallocate(M2MTextureDef.size()); for(u32 i=0; iseek(M2MTextureDef[i].texFileOfs); - file->read((void*)tempTexFileName.c_str(),M2MTextureDef[i].texFileLen); + MeshFile->seek(M2MTextureDef[i].texFileOfs); + MeshFile->read((void*)tempTexFileName.c_str(),M2MTextureDef[i].texFileLen); M2MTextureFiles.push_back(tempTexFileName.c_str()); DEBUG(logdebug("Texture: %u (%s)",M2MTextureFiles.size(),M2MTextureFiles[i].c_str())); } // std::cout << "Read "<seek(header.ofsAnimations); +for(u32 i=0;iread(&tempAnimation,sizeof(Animation)); + M2MAnimations.push_back(tempAnimation); + //std::cout<seek(header.ofsBones); +for(u32 i=0;iread(&tempBone,16); + MeshFile->read(&tempBone.translation.header,sizeof(AnimBlockHead)); + MeshFile->read(&tempBone.rotation.header,sizeof(AnimBlockHead)); + MeshFile->read(&tempBone.scaling.header,sizeof(AnimBlockHead)); + MeshFile->read(&tempBone.PivotPoint,sizeof(core::vector3df)); + tempYZ=tempBone.PivotPoint.Y; + tempBone.PivotPoint.Y=tempBone.PivotPoint.Z; + tempBone.PivotPoint.Z=tempYZ; + M2MBones.push_back(tempBone); + //std::cout<0) + { + MeshFile->seek(M2MBones[i].translation.header.ofsInterpolationRange); + for(u32 j=0; jread(&tempBoneIR, sizeof(InterpolationRange)); + M2MBones[i].translation.keyframes.push_back(tempBoneIR); + } + } + if(M2MBones[i].rotation.header.nInterpolationRange>0) + { + MeshFile->seek(M2MBones[i].rotation.header.ofsInterpolationRange); + for(u32 j=0; jread(&tempBoneIR, sizeof(InterpolationRange)); + M2MBones[i].rotation.keyframes.push_back(tempBoneIR); + } + } + if(M2MBones[i].scaling.header.nInterpolationRange>0) + { + MeshFile->seek(M2MBones[i].scaling.header.ofsInterpolationRange); + for(u32 j=0; jread(&tempBoneIR, sizeof(InterpolationRange)); + M2MBones[i].scaling.keyframes.push_back(tempBoneIR); + } + } + + if(M2MBones[i].translation.header.nTimeStamp>0) + { + MeshFile->seek(M2MBones[i].translation.header.ofsTimeStamp); + for(u32 j=0; jread(&tempBoneTS, sizeof(u32)); + M2MBones[i].translation.timestamps.push_back(tempBoneTS); + } + } + if(M2MBones[i].rotation.header.nTimeStamp>0) + { + MeshFile->seek(M2MBones[i].rotation.header.ofsTimeStamp); + for(u32 j=0; jread(&tempBoneTS, sizeof(u32)); + M2MBones[i].rotation.timestamps.push_back(tempBoneTS); + } + } + if(M2MBones[i].scaling.header.nTimeStamp>0) + { + MeshFile->seek(M2MBones[i].scaling.header.ofsTimeStamp); + for(u32 j=0; jread(&tempBoneTS, sizeof(u32)); + M2MBones[i].scaling.timestamps.push_back(tempBoneTS); + } + } + if(M2MBones[i].translation.header.nValues>0) + { + MeshFile->seek(M2MBones[i].translation.header.ofsValues); + for(u32 j=0; jread(&tempBoneValue, sizeof(float)); + M2MBones[i].translation.values.push_back(tempBoneValue); + } + } + if(M2MBones[i].rotation.header.nValues>0) + { + MeshFile->seek(M2MBones[i].rotation.header.ofsValues); + for(u32 j=0; jread(&tempBoneShort, sizeof(s16)); + tempBoneValue=(tempBoneShort>0?tempBoneShort-32767:tempBoneShort+32767)/32767.0f; + M2MBones[i].rotation.values.push_back(tempBoneValue); + } + } + if(M2MBones[i].scaling.header.nValues>0) + { + MeshFile->seek(M2MBones[i].scaling.header.ofsValues); + for(u32 j=0; jread(&tempBoneValue, sizeof(float)); + M2MBones[i].scaling.values.push_back(tempBoneValue); + } + } +} + +DEBUG(logdebug("Read %u Bones",M2MBones.size())); +scene::CSkinnedMesh::SJoint* Joint; +for(u32 i=0;igetAllJoints()[M2MBones[i].parentBone]; + } + Joint=AnimatedMesh->createJoint(ParentJoint); + + + //std::cout << i << " "<GlobalMatrix.getTranslation().X<< " "<GlobalMatrix.getTranslation().Y<< " "<GlobalMatrix.getTranslation().Z<<'\n'; + //std::cout << i << " "<createPositionKey(Joint); + pos->frame=M2MBones[i].translation.timestamps[j]*.01f; + pos->position=core::vector3df(M2MBones[i].translation.values[j*3],M2MBones[i].translation.values[j*3+1],M2MBones[i].translation.values[j*3+2]); + } +if(M2MBones[i].rotation.header.nValues>0) +{ + for(u32 j=0;jcreateRotationKey(Joint); + rot->frame=M2MBones[i].rotation.timestamps[j]*.01f; + core::quaternion tempQ=core::quaternion(M2MBones[i].rotation.values[j*4+0],M2MBones[i].rotation.values[j*4+1],M2MBones[i].rotation.values[j*4+2],M2MBones[i].rotation.values[j*4+3]); + rot->rotation=tempQ; +// std::cout <<" "<< M2MBones[i].rotation.values[j*4+0] <<" "<< M2MBones[i].rotation.values[j*4+1] <<" "<< M2MBones[i].rotation.values[j*4+2] <<" "<< M2MBones[i].rotation.values[j*4+3] <<'\n'; + } +} +if(M2MBones[i].scaling.header.nValues>0){ + for(u32 j=0;jcreateScaleKey(Joint); + scale->frame=M2MBones[i].scaling.timestamps[j]*.01f; + scale->scale=core::vector3df(M2MBones[i].scaling.values[j*3],M2MBones[i].scaling.values[j*3+1],M2MBones[i].scaling.values[j*3+2]); + } +*/ + // Joint->Animatedposition=M2MBones[i].PivotPoint; +// std::cout<Animatedposition.X<<' '<Animatedposition.Y<<' '<Animatedposition.Z<<' '<<'\n'; + +// Joint->Animatedscale=core::vector3df(1.0f,1.0f,1.0f); + //Joint->Animatedrotation=core::quaternion(0.0f,0.0f,0.0f,0.0f); + core::matrix4 positionMatrix; +// positionMatrix.setTranslation( Joint->Animatedposition ); + core::matrix4 scaleMatrix; + //scaleMatrix.setScale( Joint->Animatedscale ); + core::matrix4 rotationMatrix;// = Joint->Animatedrotation.getMatrix(); + + Joint->GlobalMatrix = positionMatrix * rotationMatrix * scaleMatrix;// + + if (ParentJoint) + { + core::matrix4 InverseParentGlobal; + ParentJoint->GlobalMatrix.getInverse(InverseParentGlobal); + Joint->LocalMatrix = InverseParentGlobal * Joint->GlobalMatrix; + } + else + Joint->LocalMatrix = Joint->GlobalMatrix; +} + +//std::cout<getAllJoints()[1]->Children.size()<<" Children\n"; //And M2MVertices are not usable like this. Thus we transform if(M2Vertices.size()>0) @@ -211,82 +468,89 @@ if(M2Vertices.size()>0) for(u32 i=0;idrop(); - -Mesh=new SMesh(); - - -while(Mesh->getMeshBufferCount()>0) -{ - Mesh->MeshBuffers.erase(0); -} - - +//Loop through the submeshes for(u32 i=0; i < M2MViews[0].nSub;i++)// { -//std::cout << "Proceeding with Submesh "<createBuffer(); -if(M2Indices.size()>0) - M2Indices.clear(); - -for(u32 j=M2MSubmeshes[i].ofsTris;jappend(M2Vertices.const_pointer(),M2Vertices.size(),M2Indices.const_pointer(),M2Indices.size()); -IMB->recalculateBoundingBox(); - -//IMB->getMaterial().DiffuseColor.set(255,255-(u32)(255/(M2MSubmeshes.size()))*i,(u32)(255/(M2MSubmeshes.size()))*i,0); -//IMB->getMaterial().DiffuseColor.set(255,(M2MSubmeshes[i].meshpartId==0?0:255),(M2MSubmeshes[i].meshpartId==0?255:0),0); - - -std::string TexName=Texdir.c_str(); -TexName+="/"; -if(iIndices.push_back(M2MIndices[M2MTriangles[j]]-M2MSubmeshes[i].ofsVertex); } -while(TexName.find(' ')Indices.size() << "\n"; + for(u32 j=M2MSubmeshes[i].ofsVertex;jVertices_Standard.push_back(M2Vertices[j]); + for(u32 k=0; k<4; k++) + { + //std::cout << (u32)M2MVertices[j].bones[k] << " "; + if((M2MVertices[j].weights[k]/255.0f)>0.0f) + { + scene::CSkinnedMesh::SWeight* weight = AnimatedMesh->createWeight(AnimatedMesh->getAllJoints()[(u32)M2MVertices[j].bones[k]]); + weight->strength=M2MVertices[j].weights[k]/255.0f; + weight->vertex_id=j-M2MSubmeshes[i].ofsVertex; + weight->buffer_id=i; + } + //std::cout<buffer_id << " " << weight->vertex_id << " " << weight->strength <<"|"; + } + // std::cout<<'\n'; } -std::transform(TexName.begin(), TexName.end(), TexName.begin(), tolower); + //std::cout << i << ": " << MeshBuffer->Vertices_Standard.size() <<" "<getMaterial().setTexture(0,Device->getVideoDriver()->getTexture(TexName.c_str())); -if(igetMaterial().BackfaceCulling=(M2MRenderFlags[i].flags & 0x04)?false:true; - if(M2MRenderFlags[i].blending==1) - IMB->getMaterial().MaterialType=video::EMT_TRANSPARENT_ALPHA_CHANNEL; -} -IMB->recalculateBoundingBox(); - Mesh->addMeshBuffer(IMB); -IMB->drop(); -//std::cout << "Mesh now has "<getMeshBufferCount()<<" Buffers\n"; -} -Device->getSceneManager()->getMeshManipulator()->flipSurfaces(Mesh); //Fix inverted surfaces after the rotation -Device->getSceneManager()->getMeshManipulator()->recalculateNormals(Mesh,true);//just to be sure -aniMesh= new SAnimatedMesh(); -aniMesh->addMesh(Mesh); -Mesh->drop(); -Mesh = 0; -aniMesh->recalculateBoundingBox(); + MeshBuffer->recalculateBoundingBox(); + //MeshBuffer->getMaterial().DiffuseColor.set(255,255-(u32)(255/(M2MSubmeshes.size()))*i,(u32)(255/(M2MSubmeshes.size()))*i,0); + //MeshBuffer->getMaterial().DiffuseColor.set(255,(M2MSubmeshes[i].meshpartId==0?0:255),(M2MSubmeshes[i].meshpartId==0?255:0),0); + for(u32 j=0;jgetMaterial().setTexture(M2MTextureUnit[j].TextureUnitNumber,Device->getVideoDriver()->getTexture(TexName.c_str())); + + DEBUG(logdebug("Render Flags: %u %u",M2MRenderFlags[M2MTextureUnit[j].renderFlagsIndex].flags,M2MRenderFlags[M2MTextureUnit[j].renderFlagsIndex].blending)); + MeshBuffer->getMaterial().BackfaceCulling=(M2MRenderFlags[M2MTextureUnit[j].renderFlagsIndex].flags & 0x04)?false:true; + if(M2MRenderFlags[M2MTextureUnit[j].renderFlagsIndex].blending==1) + MeshBuffer->getMaterial().MaterialType=video::EMT_TRANSPARENT_ALPHA_CHANNEL; + } + + } + + + //MeshBuffer->recalculateBoundingBox(); + // Mesh->addMeshBuffer(MeshBuffer); + // Mesh->recalculateBoundingBox(); + //MeshBuffer->drop(); + //std::cout << "Mesh now has "<getMeshBufferCount()<<" Buffers\n"; +} + + + + +Device->getSceneManager()->getMeshManipulator()->flipSurfaces(AnimatedMesh); //Fix inverted surfaces after the rotation +Device->getSceneManager()->getMeshManipulator()->recalculateNormals(AnimatedMesh,true);//just to be sure + +AnimatedMesh->setInterpolationMode(scene::EIM_LINEAR); M2MTriangles.clear(); M2Vertices.clear(); @@ -300,8 +564,7 @@ M2MSubmeshes.clear(); M2MTextureFiles.clear(); M2MTextureLookup.clear(); M2MViews.clear(); - -return aniMesh; +return true; } } diff --git a/src/Client/GUI/CM2MeshFileLoader.h b/src/Client/GUI/CM2MeshFileLoader.h index 1448993..f90779c 100644 --- a/src/Client/GUI/CM2MeshFileLoader.h +++ b/src/Client/GUI/CM2MeshFileLoader.h @@ -1,5 +1,6 @@ #include "irrlicht/irrlicht.h" #include "irrlicht/IMeshLoader.h" +#include "SSkinnedMesh.h" #include #include #include @@ -100,34 +101,12 @@ struct TextureDefinition { u32 texFileOfs; }; -class CM2MeshFileLoader : public IMeshLoader -{ -public: - - //! Constructor - CM2MeshFileLoader(IrrlichtDevice* device, c8* texdir); - - //! destructor - virtual ~CM2MeshFileLoader(); - - //! returns true if the file maybe is able to be loaded by this class - //! based on the file extension (e.g. ".cob") - virtual bool isALoadableFileExtension(const c8* fileName)const; - - //! creates/loads an animated mesh from the file. - //! \return Pointer to the created mesh. Returns 0 if loading failed. - //! If you no longer need the mesh, you should call IAnimatedMesh::drop(). - //! See IUnknown::drop() for more information. - virtual scene::IAnimatedMesh* createMesh(irr::io::IReadFile* file); -private: - ModelHeader header; - struct ModelVertex { - core::vector3df pos;//Use Irrlicht Vector here! + core::vector3df pos; u8 weights[4]; u8 bones[4]; - core::vector3df normal;//Use Irrlicht Vector here! - core::vector2df texcoords;//Use Irrlicht Vector here! + core::vector3df normal; + core::vector2df texcoords; u32 unk1, unk2; // always 0,0 so this is probably unused }; @@ -170,15 +149,87 @@ struct RenderFlags{ u16 blending; }; -// - io::IFileSystem* FileSystem; +struct Animation{ + u32 animationID; + u32 start, end; + float movespeed; + u32 loop, flags, unk1, unk2; + u32 playbackspeed; + float bbox[6]; + float radius; + s16 indexSameID; + u16 unk3; +}; + +struct AnimBlockHead{ + s16 interpolationType; + s16 globalSequenceID; + u32 nInterpolationRange; + u32 ofsInterpolationRange; + u32 nTimeStamp; + u32 ofsTimeStamp; + u32 nValues; + u32 ofsValues; +}; + +struct InterpolationRange{ + u32 start, end; +}; + +struct AnimBlock{ + AnimBlockHead header; + core::array keyframes; + core::array timestamps; + core::array values; +}; + +struct Bone{ + s32 indexF; + u32 flags; + s16 parentBone; + u16 unk1; + u32 unk2; + AnimBlock translation, rotation, scaling; + core::vector3df PivotPoint; +}; + + +class CM2MeshFileLoader : public IMeshLoader +{ +public: + + //! Constructor + CM2MeshFileLoader(IrrlichtDevice* device, c8* texdir); + + //! destructor + virtual ~CM2MeshFileLoader(); + + //! returns true if the file maybe is able to be loaded by this class + //! based on the file extension (e.g. ".cob") + virtual bool isALoadableFileExtension(const c8* fileName)const; + + //! creates/loads an animated mesh from the file. + //! \return Pointer to the created mesh. Returns 0 if loading failed. + //! If you no longer need the mesh, you should call IAnimatedMesh::drop(). + //! See IUnknown::drop() for more information. + virtual scene::IAnimatedMesh* createMesh(io::IReadFile* file); +private: + + bool load(); + IrrlichtDevice* Device; -// scene::IMeshManipulator* Manipulator; - core::stringc M2MeshName; core::stringc Texdir; - SAnimatedMesh* aniMesh; + io::IReadFile* MeshFile; + + CSkinnedMesh* AnimatedMesh; + scene::CSkinnedMesh::SJoint* ParentJoint; + + + + ModelHeader header; + core::stringc M2MeshName; SMesh* Mesh; - SMeshBuffer* IMB; + //SSkinMeshBuffer* MeshBuffer; //Taken from the Model file, thus m2M* core::array M2MVertices; core::array M2MViews; @@ -190,9 +241,13 @@ struct RenderFlags{ core::array M2MTextureFiles; core::array M2MTextureUnit; core::array M2MRenderFlags; + core::array M2MAnimations; + core::array M2MBones; //Used for the Mesh, thus m2_noM_* core::array M2Vertices; core::array M2Indices; + core::array M2Joints; + }; }//namespace scene diff --git a/src/Client/GUI/Makefile.am b/src/Client/GUI/Makefile.am index 9d19410..5a8c8f1 100644 --- a/src/Client/GUI/Makefile.am +++ b/src/Client/GUI/Makefile.am @@ -1,5 +1,5 @@ ## Process this file with automake to produce Makefile.in -AM_CPPFLAGS = -I$(top_builddir)/src/Client -I$(top_builddir)/src/shared -I$(top_builddir)/src/Client/DefScript -I$(top_builddir)/src/Client/World -I$(top_builddir)/src/Client/Realm -Wall +AM_CPPFLAGS = -I$(top_builddir)/src/Client -I$(top_builddir)/src/shared -I$(top_builddir)/src/Client/DefScript -I$(top_builddir)/src/Client/World -I$(top_builddir)/src/Client/Realm -Wall -D_DEBUG ## Build pseuwow noinst_LIBRARIES = libgui.a libgui_a_SOURCES =CCursorController.cpp DrawObject.cpp MInput.h Scene.h SImage.h\ @@ -10,7 +10,7 @@ CM2MeshFileLoader.cpp SceneData.h ShTlTerrainSceneNode.h\ CM2MeshFileLoader.h MCamera.h SceneGuiStart.cpp SImage.cpp SceneCharselection.cpp\ CIrrKlangAudioStreamLoaderMP3.cpp CIrrKlangAudioStreamLoaderMP3.h CIrrKlangAudioStreamMP3.cpp CIrrKlangAudioStreamMP3.h\ ikpMP3.cpp decoder/bits.c decoder/internal.h decoder/mpaudec.c decoder/mpaudec.h decoder/mpaudectab.h decoder/mpegaudio.h\ -irrKlangSceneNode.cpp irrKlangSceneNode.h +irrKlangSceneNode.cpp irrKlangSceneNode.h CBoneSceneNode.cpp CBoneSceneNode.h SSkinnedMesh.cpp SSkinnedMesh.h libgui_a_LIBADD = $(top_builddir)/src/shared/libshared.a $(top_builddir)/src/shared/Auth/libauth.a $(top_builddir)/src/shared/Network/libnetwork.a diff --git a/src/Client/GUI/SSkinnedMesh.cpp b/src/Client/GUI/SSkinnedMesh.cpp new file mode 100755 index 0000000..475626f --- /dev/null +++ b/src/Client/GUI/SSkinnedMesh.cpp @@ -0,0 +1,1414 @@ +// Copyright (C) 2002-2007 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h +#include +#include "irrlicht/irrlicht.h" +#include "SSkinnedMesh.h" +#include "CBoneSceneNode.h" +namespace irr +{ +namespace scene +{ + + +//! constructor +CSkinnedMesh::CSkinnedMesh() +: SkinningBuffers(0), HasAnimation(0), PreparedForSkinning(0), + AnimationFrames(0.f), LastAnimatedFrame(0.f), LastSkinnedFrame(0.f), + BoneControlUsed(false), AnimateNormals(true), HardwareSkinning(0), InterpolationMode(EIM_LINEAR) +{ + #ifdef _DEBUG + setDebugName("CSkinnedMesh"); + #endif + + SkinningBuffers=&LocalBuffers; +} + + +//! destructor +CSkinnedMesh::~CSkinnedMesh() +{ + for (u32 i=0; idrop(); + } +} + + +//! returns the amount of frames in milliseconds. +//! If the amount is 1, it is a static (=non animated) mesh. +u32 CSkinnedMesh::getFrameCount() const +{ + return core::floor32(AnimationFrames); +} + + +//! returns the animated mesh based on a detail level. 0 is the lowest, 255 the highest detail. Note, that some Meshes will ignore the detail level. +IMesh* CSkinnedMesh::getMesh(s32 frame, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop) +{ + //animate(frame,startFrameLoop, endFrameLoop); + if (frame==-1) + return this; + + animateMesh((f32)frame, 1.0f); + buildAll_LocalAnimatedMatrices(); + buildAll_GlobalAnimatedMatrices(); + skinMesh(); + return this; +} + + +//-------------------------------------------------------------------------- +// Keyframe Animation +//-------------------------------------------------------------------------- + + +//! Animates this mesh's joints based on frame input +//! blend: {0-old position, 1-New position} +void CSkinnedMesh::animateMesh(f32 frame, f32 blend) +{ +// std::cout<<"Frame "<Animatedposition; + const core::vector3df oldScale = joint->Animatedscale; + const core::quaternion oldRotation = joint->Animatedrotation; + + core::vector3df position = oldPosition; + core::vector3df scale = oldScale; + core::quaternion rotation = oldRotation; + getFrameData(frame, joint, + position, joint->positionHint, + scale, joint->scaleHint, + rotation, joint->rotationHint); + + if (blend==1.0f) + { + //No blending needed + joint->Animatedposition = position; + joint->Animatedscale = scale; + joint->Animatedrotation = rotation; + } + else + { + //Blend animation + joint->Animatedposition = core::lerp(oldPosition, position, blend); + joint->Animatedscale = core::lerp(oldScale, scale, blend); + joint->Animatedrotation.slerp(oldRotation, rotation, blend); + } + + //Note: + //_LocalAnimatedMatrix needs to be built at some point, but this function may be called lots of times for + //one render (to play two animations at the same time) _LocalAnimatedMatrix only needs to be built once. + //a call to buildAllLocalAnimatedMatrices is needed before skinning the mesh, and before the user gets the joints to move + + //---------------- + // Temp! + buildAll_LocalAnimatedMatrices(); + //----------------- + } +} + + +void CSkinnedMesh::buildAll_LocalAnimatedMatrices() +{ + for (u32 i=0; iUseAnimationFrom && + (joint->UseAnimationFrom->PositionKeys.size() || + joint->UseAnimationFrom->ScaleKeys.size() || + joint->UseAnimationFrom->RotationKeys.size() )) + { + joint->LocalAnimatedMatrix=joint->Animatedrotation.getMatrix(); + + // --- joint->LocalAnimatedMatrix *= joint->Animatedrotation.getMatrix() --- + f32 *m1 = joint->LocalAnimatedMatrix.pointer(); + core::vector3df &Pos = joint->Animatedposition; + m1[0] += Pos.X*m1[3]; + m1[1] += Pos.Y*m1[3]; + m1[2] += Pos.Z*m1[3]; + m1[4] += Pos.X*m1[7]; + m1[5] += Pos.Y*m1[7]; + m1[6] += Pos.Z*m1[7]; + m1[8] += Pos.X*m1[11]; + m1[9] += Pos.Y*m1[11]; + m1[10] += Pos.Z*m1[11]; + m1[12] += Pos.X*m1[15]; + m1[13] += Pos.Y*m1[15]; + m1[14] += Pos.Z*m1[15]; + // ----------------------------------- + + joint->GlobalSkinningSpace=false; + + if (joint->ScaleKeys.size()) + { + /* + core::matrix4 scaleMatrix; + scaleMatrix.setScale(joint->Animatedscale); + joint->LocalAnimatedMatrix *= scaleMatrix; + */ + + // -------- joint->LocalAnimatedMatrix *= scaleMatrix ----------------- + f32 *m1 = joint->LocalAnimatedMatrix.pointer(); + m1[0] *= joint->Animatedscale.X; + m1[1] *= joint->Animatedscale.X; + m1[2] *= joint->Animatedscale.X; + m1[3] *= joint->Animatedscale.X; + m1[4] *= joint->Animatedscale.Y; + m1[5] *= joint->Animatedscale.Y; + m1[6] *= joint->Animatedscale.Y; + m1[7] *= joint->Animatedscale.Y; + m1[8] *= joint->Animatedscale.Z; + m1[9] *= joint->Animatedscale.Z; + m1[10] *= joint->Animatedscale.Z; + m1[11] *= joint->Animatedscale.Z; + m1[12] *= joint->Animatedscale.X; + // ----------------------------------- + + } + } + else + { + joint->LocalAnimatedMatrix=joint->LocalMatrix; + } + } +} + + +void CSkinnedMesh::buildAll_GlobalAnimatedMatrices(SJoint *joint, SJoint *parentJoint) +{ + if (!joint) + { + for (u32 i=0; iGlobalSkinningSpace) + joint->GlobalAnimatedMatrix = joint->LocalAnimatedMatrix; + else + joint->GlobalAnimatedMatrix = parentJoint->GlobalAnimatedMatrix * joint->LocalAnimatedMatrix; + + } + + for (u32 j=0; jChildren.size(); ++j) + buildAll_GlobalAnimatedMatrices(joint->Children[j], joint); +} + + +void CSkinnedMesh::getFrameData(f32 frame, SJoint *joint, + core::vector3df &position, s32 &positionHint, + core::vector3df &scale, s32 &scaleHint, + core::quaternion &rotation, s32 &rotationHint) +{ + s32 foundPositionIndex = -1; + s32 foundScaleIndex = -1; + s32 foundRotationIndex = -1; + + if (joint->UseAnimationFrom) + { + const core::array &PositionKeys=joint->UseAnimationFrom->PositionKeys; + const core::array &ScaleKeys=joint->UseAnimationFrom->ScaleKeys; + const core::array &RotationKeys=joint->UseAnimationFrom->RotationKeys; + + if (PositionKeys.size()) + { + foundPositionIndex = -1; + + //Test the Hints... + if (positionHint>=0 && (u32)positionHint < PositionKeys.size()) + { + //check this hint + if (positionHint>0 && PositionKeys[positionHint].frame>=frame && PositionKeys[positionHint-1].frame=frame && + PositionKeys[positionHint+0].frame= frame) //Keys should to be sorted by frame + { + foundPositionIndex=i; + positionHint=i; + break; + } + } + } + + //Do interpolation... + if (foundPositionIndex!=-1) + { + if (InterpolationMode==EIM_CONSTANT || foundPositionIndex==0) + { + position = PositionKeys[foundPositionIndex].position; + } + else if (InterpolationMode==EIM_LINEAR) + { + const SPositionKey& KeyA = PositionKeys[foundPositionIndex]; + const SPositionKey& KeyB = PositionKeys[foundPositionIndex-1]; + + const f32 fd1 = frame - KeyA.frame; + const f32 fd2 = KeyB.frame - frame; + position = ((KeyB.position-KeyA.position)/(fd1+fd2))*fd1 + KeyA.position; + } + } + } + + //------------------------------------------------------------ + + if (ScaleKeys.size()) + { + foundScaleIndex = -1; + + //Test the Hints... + if (scaleHint>=0 && (u32)scaleHint < ScaleKeys.size()) + { + //check this hint + if (scaleHint>0 && ScaleKeys[scaleHint].frame>=frame && ScaleKeys[scaleHint-1].frame=frame && + ScaleKeys[scaleHint+0].frame= frame) //Keys should to be sorted by frame + { + foundScaleIndex=i; + scaleHint=i; + break; + } + } + } + + //Do interpolation... + if (foundScaleIndex!=-1) + { + if (InterpolationMode==EIM_CONSTANT || foundScaleIndex==0) + { + scale = ScaleKeys[foundScaleIndex].scale; + } + else if (InterpolationMode==EIM_LINEAR) + { + const SScaleKey& KeyA = ScaleKeys[foundScaleIndex]; + const SScaleKey& KeyB = ScaleKeys[foundScaleIndex-1]; + + const f32 fd1 = frame - KeyA.frame; + const f32 fd2 = KeyB.frame - frame; + scale = ((KeyB.scale-KeyA.scale)/(fd1+fd2))*fd1 + KeyA.scale; + } + } + } + + //------------------------------------------------------------- + + if (RotationKeys.size()) + { + foundRotationIndex = -1; + + //Test the Hints... + if (rotationHint>=0 && (u32)rotationHint < RotationKeys.size()) + { + //check this hint + if (rotationHint>0 && RotationKeys[rotationHint].frame>=frame && RotationKeys[rotationHint-1].frame=frame && + RotationKeys[rotationHint+0].frame= frame) //Keys should be sorted by frame + { + foundRotationIndex=i; + rotationHint=i; + break; + } + } + } + + //Do interpolation... + if (foundRotationIndex!=-1) + { + if (InterpolationMode==EIM_CONSTANT || foundRotationIndex==0) + { + rotation = RotationKeys[foundRotationIndex].rotation; + } + else if (InterpolationMode==EIM_LINEAR) + { + const SRotationKey& KeyA = RotationKeys[foundRotationIndex]; + const SRotationKey& KeyB = RotationKeys[foundRotationIndex-1]; + + const f32 fd1 = frame - KeyA.frame; + const f32 fd2 = KeyB.frame - frame; + const f32 t = fd1/(fd1+fd2); + + /* + f32 t = 0; + if (KeyA.frame!=KeyB.frame) + t = (frame-KeyA.frame) / (KeyB.frame - KeyA.frame); + */ + + rotation.slerp(KeyA.rotation, KeyB.rotation, t); + } + } + } + } +} + +//-------------------------------------------------------------------------- +// Software Skinning +//-------------------------------------------------------------------------- + +//! Preforms a software skin on this mesh based of joint positions +void CSkinnedMesh::skinMesh() +{ + if ( !HasAnimation) + return; + + //---------------- + // Temp! + buildAll_GlobalAnimatedMatrices(); + //----------------- + + if (!HardwareSkinning) + { + //Software skin.... + u32 i; + + //rigid animation + for (i=0; iAttachedMeshes.size(); ++j) + { + SSkinMeshBuffer* Buffer=(*SkinningBuffers)[ AllJoints[i]->AttachedMeshes[j] ]; + Buffer->Transformation=AllJoints[i]->GlobalAnimatedMatrix; + } + } + + //clear skinning helper array + for (i=0; isize(); ++i) + (*SkinningBuffers)[i]->setDirty(); + + } +} + + +void CSkinnedMesh::SkinJoint(SJoint *joint, SJoint *parentJoint) +{ + if (joint->Weights.size()) + { + //Find this joints pull on vertices... + core::matrix4 jointVertexPull(core::matrix4::EM4CONST_NOTHING); + jointVertexPull.setbyproduct(joint->GlobalAnimatedMatrix, joint->GlobalInversedMatrix); + + core::vector3df thisVertexMove, thisNormalMove; + + core::array &buffersUsed=*SkinningBuffers; + + //Skin Vertices Positions and Normals... + for (u32 i=0; iWeights.size(); ++i) + { + SWeight& weight = joint->Weights[i]; + + // Pull this vertex... + jointVertexPull.transformVect(thisVertexMove, weight.StaticPos); + + if (AnimateNormals) + jointVertexPull.rotateVect(thisNormalMove, weight.StaticNormal); + + if (! (*(weight.Moved)) ) + { + *(weight.Moved) = true; + + buffersUsed[weight.buffer_id]->getVertex(weight.vertex_id)->Pos = thisVertexMove * weight.strength; + + if (AnimateNormals) + buffersUsed[weight.buffer_id]->getVertex(weight.vertex_id)->Normal = thisNormalMove * weight.strength; + + //*(weight._Pos) = thisVertexMove * weight.strength; + } + else + { + buffersUsed[weight.buffer_id]->getVertex(weight.vertex_id)->Pos += thisVertexMove * weight.strength; + + if (AnimateNormals) + buffersUsed[weight.buffer_id]->getVertex(weight.vertex_id)->Normal += thisNormalMove * weight.strength; + + //*(weight._Pos) += thisVertexMove * weight.strength; + } + } + } + + //Skin all children + for (u32 j=0; jChildren.size(); ++j) + SkinJoint(joint->Children[j], joint); +} + + +E_ANIMATED_MESH_TYPE CSkinnedMesh::getMeshType() const +{ + return EAMT_SKINNED; +} + + +//! Gets joint count. +u32 CSkinnedMesh::getJointCount() const +{ + return AllJoints.size(); +} + + +//! Gets the name of a joint. +const c8* CSkinnedMesh::getJointName(u32 number) const +{ + if (number >= AllJoints.size()) + return 0; + return AllJoints[number]->Name.c_str(); +} + + +//! Gets a joint number from its name +s32 CSkinnedMesh::getJointNumber(const c8* name) const +{ + for (u32 i=0; iName == name) + return i; + } + + return -1; +} + + +//! returns amount of mesh buffers. +u32 CSkinnedMesh::getMeshBufferCount() const +{ + return LocalBuffers.size(); +} + + +//! returns pointer to a mesh buffer +IMeshBuffer* CSkinnedMesh::getMeshBuffer(u32 nr) const +{ + if (nr < LocalBuffers.size()) + return LocalBuffers[nr]; + else + return 0; +} + + +//! Returns pointer to a mesh buffer which fits a material +IMeshBuffer* CSkinnedMesh::getMeshBuffer(const video::SMaterial &material) const +{ + for (u32 i=0; igetMaterial() == material) + return LocalBuffers[i]; + } + return 0; +} + + +//! returns an axis aligned bounding box +const core::aabbox3d& CSkinnedMesh::getBoundingBox() const +{ + return BoundingBox; +} + + +//! set user axis aligned bounding box +void CSkinnedMesh::setBoundingBox( const core::aabbox3df& box) +{ + BoundingBox = box; +} + + +//! sets a flag of all contained materials to a new value +void CSkinnedMesh::setMaterialFlag(video::E_MATERIAL_FLAG flag, bool newvalue) +{ + for (u32 i=0; iMaterial.setFlag(flag,newvalue); +} + + +//! uses animation from another mesh +bool CSkinnedMesh::useAnimationFrom(const ISkinnedMesh *mesh) +{ + bool unmatched=false; + + for(u32 i=0;iUseAnimationFrom=0; + + if (joint->Name=="") + unmatched=true; + else + { + for(u32 j=0;jgetAllJoints().size();++j) + { + SJoint *otherJoint=mesh->getAllJoints()[j]; + if (joint->Name==otherJoint->Name) + { + joint->UseAnimationFrom=otherJoint; + } + } + if (!joint->UseAnimationFrom) + unmatched=true; + } + } + + checkForAnimation(); + + return !unmatched; +} + + +//!Update Normals when Animating +//!False= Don't animate them, faster +//!True= Update normals (default) +void CSkinnedMesh::updateNormalsWhenAnimating(bool on) +{ + AnimateNormals = on; +} + + +//!Sets Interpolation Mode +void CSkinnedMesh::setInterpolationMode(E_INTERPOLATION_MODE mode) +{ + InterpolationMode = mode; +} + + +core::array &CSkinnedMesh::getMeshBuffers() +{ + return LocalBuffers; +} + + +core::array &CSkinnedMesh::getAllJoints() +{ + return AllJoints; +} + + +const core::array &CSkinnedMesh::getAllJoints() const +{ + return AllJoints; +} + + +//! (This feature is not implementated in irrlicht yet) +bool CSkinnedMesh::setHardwareSkinning(bool on) +{ + if (HardwareSkinning!=on) + { + + if (on) + { + + //set mesh to static pose... + for (u32 i=0; iWeights.size(); ++j) + { + const u16 buffer_id=joint->Weights[j].buffer_id; + const u32 vertex_id=joint->Weights[j].vertex_id; + LocalBuffers[buffer_id]->getVertex(vertex_id)->Pos = joint->Weights[j].StaticPos; + LocalBuffers[buffer_id]->getVertex(vertex_id)->Normal = joint->Weights[j].StaticNormal; + } + } + + + } + + HardwareSkinning=on; + } + return HardwareSkinning; +} + + +void CSkinnedMesh::CalculateGlobalMatrices(SJoint *joint,SJoint *parentJoint) +{ + if (!joint && parentJoint) // bit of protection from endless loops + return; + + //Go through the root bones + if (!joint) + { + for (u32 i=0; iGlobalMatrix = joint->LocalMatrix; + else + joint->GlobalMatrix = parentJoint->GlobalMatrix * joint->LocalMatrix; + + joint->LocalAnimatedMatrix=joint->LocalMatrix; + joint->GlobalAnimatedMatrix=joint->GlobalMatrix; + + if (joint->GlobalInversedMatrix.isIdentity())//might be pre calculated + { + joint->GlobalInversedMatrix = joint->GlobalMatrix; + joint->GlobalInversedMatrix.makeInverse(); // slow + } + + for (u32 j=0; jChildren.size(); ++j) + CalculateGlobalMatrices(joint->Children[j],joint); +} + + +void CSkinnedMesh::checkForAnimation() +{ + u32 i,j; + //Check for animation... + HasAnimation = false; + for(i=0;iUseAnimationFrom) + { + if (AllJoints[i]->UseAnimationFrom->PositionKeys.size() || + AllJoints[i]->UseAnimationFrom->ScaleKeys.size() || + AllJoints[i]->UseAnimationFrom->RotationKeys.size() ) + { + HasAnimation = true; + } + } + } + + //meshes with weights, are still counted as animated for ragdolls, etc + if (!HasAnimation) + { + for(i=0;iWeights.size()) + HasAnimation = true; + } + } + + if (HasAnimation) + { + //--- Find the length of the animation --- + AnimationFrames=0; + for(i=0;iUseAnimationFrom) + { + if (AllJoints[i]->UseAnimationFrom->PositionKeys.size()) + if (AllJoints[i]->UseAnimationFrom->PositionKeys.getLast().frame > AnimationFrames) + AnimationFrames=AllJoints[i]->UseAnimationFrom->PositionKeys.getLast().frame; + + if (AllJoints[i]->UseAnimationFrom->ScaleKeys.size()) + if (AllJoints[i]->UseAnimationFrom->ScaleKeys.getLast().frame > AnimationFrames) + AnimationFrames=AllJoints[i]->UseAnimationFrom->ScaleKeys.getLast().frame; + + if (AllJoints[i]->UseAnimationFrom->RotationKeys.size()) + if (AllJoints[i]->UseAnimationFrom->RotationKeys.getLast().frame > AnimationFrames) + AnimationFrames=AllJoints[i]->UseAnimationFrom->RotationKeys.getLast().frame; + } + } + } + + if (HasAnimation && !PreparedForSkinning) + { + PreparedForSkinning=true; + + //check for bugs: + for(i=0; i < AllJoints.size(); ++i) + { + SJoint *joint = AllJoints[i]; + for (j=0; jWeights.size(); ++j) + { + const u16 buffer_id=joint->Weights[j].buffer_id; + const u32 vertex_id=joint->Weights[j].vertex_id; + //check for invalid ids + if (buffer_id>=LocalBuffers.size()) + { + printf("Skinned Mesh: Weight buffer id too large"); + joint->Weights[j].buffer_id = joint->Weights[j].vertex_id =0; + } + else if (vertex_id>=LocalBuffers[buffer_id]->getVertexCount()) + { + printf("Skinned Mesh: Weight vertex id too large"); + joint->Weights[j].buffer_id = joint->Weights[j].vertex_id =0; + } + } + } + + //An array used in skinning + + for (i=0; iWeights.size(); ++j) + { + const u16 buffer_id=joint->Weights[j].buffer_id; + const u32 vertex_id=joint->Weights[j].vertex_id; + + joint->Weights[j].Moved = &Vertices_Moved[buffer_id] [vertex_id]; + joint->Weights[j].StaticPos = LocalBuffers[buffer_id]->getVertex(vertex_id)->Pos; + joint->Weights[j].StaticNormal = LocalBuffers[buffer_id]->getVertex(vertex_id)->Normal; + + //joint->Weights[j]._Pos=&Buffers[buffer_id]->getVertex(vertex_id)->Pos; + } + } + + // normalize weights + normalizeWeights(); + } +} + + +//! called by loader after populating with mesh and bone data +void CSkinnedMesh::finalize() +{ + std::cout<<"Finalize has been called\n"; + u32 i; + + LastAnimatedFrame=-1; + LastSkinnedFrame=-1; + + //calculate bounding box + + for (i=0; irecalculateBoundingBox(); + } + + // Get BoundingBox... + if (LocalBuffers.empty()) + BoundingBox.reset(0,0,0); + else + { + BoundingBox.reset(LocalBuffers[0]->BoundingBox.MaxEdge); + for (u32 j=0; jBoundingBox); + } + } + + //add 5% padding to bounding box + core::vector3df Padding=BoundingBox.getExtent()*0.05f; + BoundingBox.MinEdge-=Padding; + BoundingBox.MaxEdge+=Padding; + + + if (AllJoints.size() || RootJoints.size()) + { + // populate AllJoints or RootJoints, depending on which is empty + if (!RootJoints.size()) + { + + for(u32 CheckingIdx=0; CheckingIdx < AllJoints.size(); ++CheckingIdx) + { + + bool foundParent=false; + for(i=0; i < AllJoints.size(); ++i) + { + for(u32 n=0; n < AllJoints[i]->Children.size(); ++n) + { + if (AllJoints[i]->Children[n] == AllJoints[CheckingIdx]) + foundParent=true; + } + } + + if (!foundParent) + RootJoints.push_back(AllJoints[CheckingIdx]); + } + } + else + { + AllJoints=RootJoints; + } + } + + for(i=0; i < AllJoints.size(); ++i) + { + AllJoints[i]->UseAnimationFrom=AllJoints[i]; + } + + //Set array sizes... + + for (i=0; i() ); + Vertices_Moved[i].set_used(LocalBuffers[i]->getVertexCount()); + } + + //Todo: optimise keys here... + + checkForAnimation(); + printf("Has Animation %u\n",HasAnimation); + if (HasAnimation) + { + //--- optimize and check keyframes --- + for(i=0;i &PositionKeys =AllJoints[i]->PositionKeys; + core::array &ScaleKeys = AllJoints[i]->ScaleKeys; + core::array &RotationKeys = AllJoints[i]->RotationKeys; + + if (PositionKeys.size()>2) + { + for(u32 j=0;j1) + { + for(u32 j=0;j= PositionKeys[j+1].frame) //bad frame, unneed and may cause problems + { + PositionKeys.erase(j+1); + --j; + } + } + } + + if (ScaleKeys.size()>2) + { + for(u32 j=0;j1) + { + for(u32 j=0;j= ScaleKeys[j+1].frame) //bad frame, unneed and may cause problems + { + ScaleKeys.erase(j+1); + --j; + } + } + } + + if (RotationKeys.size()>2) + { + for(u32 j=0;j1) + { + for(u32 j=0;j= RotationKeys[j+1].frame) //bad frame, unneed and may cause problems + { + RotationKeys.erase(j+1); + --j; + } + } + } + + + //Fill empty keyframe areas + if (PositionKeys.size()) + { + SPositionKey *Key; + Key=&PositionKeys[0];//getFirst + if (Key->frame!=0) + { + PositionKeys.push_front(*Key); + Key=&PositionKeys[0];//getFirst + Key->frame=0; + } + + Key=&PositionKeys.getLast(); + if (Key->frame!=AnimationFrames) + { + PositionKeys.push_back(*Key); + Key=&PositionKeys.getLast(); + Key->frame=AnimationFrames; + } + } + + if (ScaleKeys.size()) + { + SScaleKey *Key; + Key=&ScaleKeys[0];//getFirst + if (Key->frame!=0) + { + ScaleKeys.push_front(*Key); + Key=&ScaleKeys[0];//getFirst + Key->frame=0; + } + + Key=&ScaleKeys.getLast(); + if (Key->frame!=AnimationFrames) + { + ScaleKeys.push_back(*Key); + Key=&ScaleKeys.getLast(); + Key->frame=AnimationFrames; + } + } + + if (RotationKeys.size()) + { + SRotationKey *Key; + Key=&RotationKeys[0];//getFirst + if (Key->frame!=0) + { + RotationKeys.push_front(*Key); + Key=&RotationKeys[0];//getFirst + Key->frame=0; + } + + Key=&RotationKeys.getLast(); + if (Key->frame!=AnimationFrames) + { + RotationKeys.push_back(*Key); + Key=&RotationKeys.getLast(); + Key->frame=AnimationFrames; + } + } + } + } + + //Needed for animation and skinning... + + CalculateGlobalMatrices(0,0); + + //animateMesh(0, 1); + //buildAll_LocalAnimatedMatrices(); + //buildAll_GlobalAnimatedMatrices(); + + //rigid animation for non animated meshes + for (i=0; iAttachedMeshes.size(); ++j) + { + SSkinMeshBuffer* Buffer=(*SkinningBuffers)[ AllJoints[i]->AttachedMeshes[j] ]; + Buffer->Transformation=AllJoints[i]->GlobalAnimatedMatrix; + } + } +} + + +scene::SSkinMeshBuffer *CSkinnedMesh::createBuffer() +{ + scene::SSkinMeshBuffer *buffer=new scene::SSkinMeshBuffer(); + LocalBuffers.push_back(buffer); + return buffer; +} + + +CSkinnedMesh::SJoint *CSkinnedMesh::createJoint(SJoint *parent) +{ + SJoint *joint=new SJoint; + + AllJoints.push_back(joint); + if (!parent) + { + //Add root joints to array in finalize() + } + else + { + //Set parent (Be careful of the mesh loader also setting the parent) + parent->Children.push_back(joint); + } + + return joint; +} + + +CSkinnedMesh::SPositionKey *CSkinnedMesh::createPositionKey(SJoint *joint) +{ + if (!joint) + return 0; + + joint->PositionKeys.push_back(SPositionKey()); + return &joint->PositionKeys.getLast(); +} + + +CSkinnedMesh::SScaleKey *CSkinnedMesh::createScaleKey(SJoint *joint) +{ + if (!joint) + return 0; + + joint->ScaleKeys.push_back(SScaleKey()); + return &joint->ScaleKeys.getLast(); +} + + +CSkinnedMesh::SRotationKey *CSkinnedMesh::createRotationKey(SJoint *joint) +{ + if (!joint) + return 0; + + joint->RotationKeys.push_back(SRotationKey()); + return &joint->RotationKeys.getLast(); +} + + +CSkinnedMesh::SWeight *CSkinnedMesh::createWeight(SJoint *joint) +{ + if (!joint) + return 0; + + joint->Weights.push_back(SWeight()); + return &joint->Weights.getLast(); +} + + +bool CSkinnedMesh::isStatic() +{ + return !HasAnimation; +} + + +void CSkinnedMesh::normalizeWeights() +{ + // note: unsure if weights ids are going to be used. + + // Normalise the weights on bones.... + + u32 i,j; + core::array< core::array > Vertices_TotalWeight; + + for (i=0; i()); + Vertices_TotalWeight[i].set_used(LocalBuffers[i]->getVertexCount()); + } + + for (i=0; iWeights.size(); ++j) + { + if (joint->Weights[j].strength<=0)//Check for invalid weights + { + joint->Weights.erase(j); + --j; + } + else + { + Vertices_TotalWeight[ joint->Weights[j].buffer_id ] [ joint->Weights[j].vertex_id ] += joint->Weights[j].strength; + } + } + } + + for (i=0; iWeights.size(); ++j) + { + const f32 total = Vertices_TotalWeight[ joint->Weights[j].buffer_id ] [ joint->Weights[j].vertex_id ]; + if (total != 0 && total != 1) + joint->Weights[j].strength /= total; + } + } +} + + +void CSkinnedMesh::recoverJointsFromMesh(core::array &JointChildSceneNodes) +{ + for (u32 i=0;isetPosition( joint->LocalAnimatedMatrix.getTranslation() ); + node->setRotation( joint->LocalAnimatedMatrix.getRotationDegrees() ); + + //node->setScale( joint->LocalAnimatedMatrix.getScale() ); + + node->positionHint=joint->positionHint; + node->scaleHint=joint->scaleHint; + node->rotationHint=joint->rotationHint; + + //node->setAbsoluteTransformation(joint->GlobalMatrix); //not going to work + + //Note: This updateAbsolutePosition will not work well if joints are not nested like b3d + //node->updateAbsolutePosition(); + } +} + + +void CSkinnedMesh::transferJointsToMesh(const core::array &JointChildSceneNodes) +{ + for (u32 i=0; iLocalAnimatedMatrix.setTranslation(node->getPosition()); + joint->LocalAnimatedMatrix.setRotationDegrees(node->getRotation()); + + //joint->LocalAnimatedMatrix.setScale( node->getScale() ); + + joint->positionHint=node->positionHint; + joint->scaleHint=node->scaleHint; + joint->rotationHint=node->rotationHint; + + if (node->getSkinningSpace()==EBSS_GLOBAL) + joint->GlobalSkinningSpace=true; + else + joint->GlobalSkinningSpace=false; + } + //Remove cache, temp... + LastAnimatedFrame=-1; + LastSkinnedFrame=-1; +} + + +void CSkinnedMesh::transferOnlyJointsHintsToMesh(const core::array &JointChildSceneNodes) +{ + for (u32 i=0;ipositionHint=node->positionHint; + joint->scaleHint=node->scaleHint; + joint->rotationHint=node->rotationHint; + } +} + + +void CSkinnedMesh::createJoints(core::array &JointChildSceneNodes, + IAnimatedMeshSceneNode* AnimatedMeshSceneNode, + ISceneManager* SceneManager) +{ + u32 i; + + //Create new joints + for (i=0;iName.c_str())); + } + + //Match up parents + for (i=0;iChildren.size();++n) + { + if (parentTest->Children[n]==joint) + { + parentID=j; + break; + } + } + } + } + + if (parentID!=-1) + node->setParent( JointChildSceneNodes[parentID] ); + else + node->setParent( AnimatedMeshSceneNode ); + + node->drop(); + } +} + + +void CSkinnedMesh::convertMeshToTangents() +{ + // now calculate tangents + for (u32 b=0; b < LocalBuffers.size(); ++b) + { + if (LocalBuffers[b]) + { + LocalBuffers[b]->MoveTo_Tangents(); + + const s32 idxCnt = LocalBuffers[b]->getIndexCount(); + + u16* idx = LocalBuffers[b]->getIndices(); + video::S3DVertexTangents* v = + (video::S3DVertexTangents*)LocalBuffers[b]->getVertices(); + + for (s32 i=0; i& getBoundingBox() const; + + //! set user axis aligned bounding box + virtual void setBoundingBox( const core::aabbox3df& box); + + //! sets a flag of all contained materials to a new value + virtual void setMaterialFlag(video::E_MATERIAL_FLAG flag, bool newvalue); + + //! Returns the type of the animated mesh. + virtual E_ANIMATED_MESH_TYPE getMeshType() const; + + //! Gets joint count. + virtual u32 getJointCount() const; + + //! Gets the name of a joint. + virtual const c8* getJointName(u32 number) const; + + //! Gets a joint number from its name + virtual s32 getJointNumber(const c8* name) const; + + //! uses animation from another mesh + virtual bool useAnimationFrom(const ISkinnedMesh *mesh); + + //! Update Normals when Animating + //! False= Don't (default) + //! True = Update normals, slower + virtual void updateNormalsWhenAnimating(bool on); + + //! Sets Interpolation Mode + virtual void setInterpolationMode(E_INTERPOLATION_MODE mode); + + //! Recovers the joints from the mesh + virtual void recoverJointsFromMesh(core::array &JointChildSceneNodes); + + //! Tranfers the joint data to the mesh + virtual void transferJointsToMesh(const core::array &JointChildSceneNodes); + + //! Tranfers the joint hints to the mesh + virtual void transferOnlyJointsHintsToMesh(const core::array &JointChildSceneNodes); + + //! Creates an array of joints from this mesh + virtual void createJoints(core::array &JointChildSceneNodes, + IAnimatedMeshSceneNode* AnimatedMeshSceneNode, + ISceneManager* SceneManager); + + //! Convertes the mesh to contain tangent information + virtual void convertMeshToTangents(); + + //! Does the mesh have no animation + virtual bool isStatic(); + + //! (This feature is not implemented in irrlicht yet) + virtual bool setHardwareSkinning(bool on); + + //Interface for the mesh loaders (finalize should lock these functions, and they should have some prefix like loader_ + + //these functions will use the needed arrays, set vaules, etc to help the loaders + + //! exposed for loaders to add mesh buffers + virtual core::array &getMeshBuffers(); + + //! alternative method for adding joints + virtual core::array &getAllJoints(); + + //! alternative method for adding joints + virtual const core::array &getAllJoints() const; + + //! loaders should call this after populating the mesh + virtual void finalize(); + + virtual SSkinMeshBuffer *createBuffer(); + + virtual SJoint *createJoint(SJoint *parent=0); + + virtual SPositionKey *createPositionKey(SJoint *joint); + virtual SRotationKey *createRotationKey(SJoint *joint); + virtual SScaleKey *createScaleKey(SJoint *joint); + + virtual SWeight *createWeight(SJoint *joint); + +private: + + void checkForAnimation(); + + void normalizeWeights(); + + void buildAll_LocalAnimatedMatrices(); //public? + + void buildAll_GlobalAnimatedMatrices(SJoint *Joint=0, SJoint *ParentJoint=0); + + void getFrameData(f32 frame, SJoint *Node, + core::vector3df &position, s32 &positionHint, + core::vector3df &scale, s32 &scaleHint, + core::quaternion &rotation, s32 &rotationHint); + + void CalculateGlobalMatrices(SJoint *Joint,SJoint *ParentJoint); + + void SkinJoint(SJoint *Joint, SJoint *ParentJoint); + + void calculateTangents(core::vector3df& normal, + core::vector3df& tangent, core::vector3df& binormal, + core::vector3df& vt1, core::vector3df& vt2, core::vector3df& vt3, + core::vector2df& tc1, core::vector2df& tc2, core::vector2df& tc3); + + + core::array *SkinningBuffers; //Meshbuffer to skin, default is to skin localBuffers + + core::array LocalBuffers; + + core::array AllJoints; + core::array RootJoints; + + bool HasAnimation; + + bool PreparedForSkinning; + + f32 AnimationFrames; + + f32 LastAnimatedFrame; + f32 LastSkinnedFrame; + bool BoneControlUsed; + + bool AnimateNormals; + + bool HardwareSkinning; + + + E_INTERPOLATION_MODE InterpolationMode; + + core::aabbox3d BoundingBox; + + core::array< core::array > Vertices_Moved; + }; + +} // end namespace scene +} // end namespace irr + +#endif + + +