Commit c77b66a5 authored by hybrid's avatar hybrid

Support for Ogre skeletons. Animations are not correctly working, yet. Also...

Support for Ogre skeletons. Animations are not correctly working, yet. Also flipping mesh from right-handed to left-handed now, so expect some changes with loaded meshes.

git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@2827 dfc29bdd-3216-0410-991c-e03cc46cb475
parent 1aa52165
......@@ -31,6 +31,7 @@ namespace scene
# error compiler not supported
#endif
namespace {
// File header
struct MS3DHeader
{
......@@ -99,6 +100,8 @@ struct MS3DVertexWeights
u8 weights[3];
} PACK_STRUCT;
} // end namespace
// Default alignment
#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
# pragma pack( pop, packing )
......
......@@ -27,6 +27,7 @@ namespace scene
{
// Main Chunks
COGRE_HEADER= 0x1000,
COGRE_SKELETON= 0x2000,
COGRE_MESH= 0x3000,
// sub chunks of COGRE_MESH
......@@ -39,6 +40,13 @@ namespace scene
COGRE_MESH_SUBMESH_NAME_TABLE= 0xA000,
COGRE_MESH_EDGE_LISTS= 0xB000,
// sub chunks of COGRE_SKELETON
COGRE_BONE_PARENT= 0x3000,
COGRE_ANIMATION= 0x4000,
COGRE_ANIMATION_TRACK= 0x4100,
COGRE_ANIMATION_KEYFRAME= 0x4110,
COGRE_ANIMATION_LINK= 0x5000,
// sub chunks of COGRE_SUBMESH
COGRE_SUBMESH_OPERATION= 0x4010,
COGRE_SUBMESH_BONE_ASSIGNMENT= 0x4100,
......@@ -120,24 +128,35 @@ IAnimatedMesh* COgreMeshFileLoader::createMesh(io::IReadFile* file)
CurrentlyLoadingFromPath = FileSystem->getFileDir(file->getFileName());
loadMaterials(file);
Mesh = new SMesh();
if (readChunk(file))
{
// success
SAnimatedMesh* am = new SAnimatedMesh();
am->Type = EAMT_3DS;
// delete data loaded from file
clearMeshes();
if (Skeleton.Bones.size())
{
ISkinnedMesh* tmp = static_cast<CSkinnedMesh*>(Mesh);
static_cast<CSkinnedMesh*>(Mesh)->updateBoundingBox();
Skeleton.Animations.clear();
Skeleton.Bones.clear();
Mesh=0;
return tmp;
}
else
{
for (u32 i=0; i<Mesh->getMeshBufferCount(); ++i)
((SMeshBuffer*)Mesh->getMeshBuffer(i))->recalculateBoundingBox();
Mesh->recalculateBoundingBox();
((SMesh*)Mesh)->recalculateBoundingBox();
SAnimatedMesh* am = new SAnimatedMesh();
am->Type = EAMT_3DS;
am->addMesh(Mesh);
am->recalculateBoundingBox();
Mesh->drop();
Mesh = 0;
return am;
}
}
Mesh->drop();
Mesh = 0;
......@@ -159,6 +178,10 @@ bool COgreMeshFileLoader::readChunk(io::IReadFile* file)
{
Meshes.push_back(OgreMesh());
readObjectChunk(file, data, Meshes.getLast());
if (Skeleton.Bones.size())
Mesh = new CSkinnedMesh();
else
Mesh = new SMesh();
composeObject();
}
break;
......@@ -195,24 +218,40 @@ bool COgreMeshFileLoader::readObjectChunk(io::IReadFile* file, ChunkData& parent
break;
case COGRE_MESH_BOUNDS:
{
#ifdef IRR_OGRE_LOADER_DEBUG
os::Printer::log("Read Mesh Bounds");
#endif
readVector(file, data, mesh.BBoxMinEdge);
readVector(file, data, mesh.BBoxMaxEdge);
readFloat(file, data, &mesh.BBoxRadius);
}
break;
case COGRE_SKELETON_LINK:
{
#ifdef IRR_OGRE_LOADER_DEBUG
os::Printer::log("Read Skeleton link");
#endif
core::stringc name;
readString(file, data, name);
loadSkeleton(file, name);
}
break;
case COGRE_BONE_ASSIGNMENT:
{
mesh.BoneAssignments.push_back(OgreBoneAssignment());
readInt(file, data, &mesh.BoneAssignments.getLast().VertexID);
readShort(file, data, &mesh.BoneAssignments.getLast().BoneID);
readFloat(file, data, &mesh.BoneAssignments.getLast().Weight);
}
break;
case COGRE_MESH_LOD:
case COGRE_MESH_SUBMESH_NAME_TABLE:
case COGRE_MESH_EDGE_LISTS:
default:
// ignore chunk
file->seek(data.header.length-data.read, true);
data.read += data.header.length-data.read;
break;
default:
parent.read=parent.header.length;
file->seek(-(long)sizeof(ChunkHeader), true);
return true;
}
parent.read += data.read;
}
......@@ -359,9 +398,17 @@ bool COgreMeshFileLoader::readSubMesh(io::IReadFile* file, ChunkData& parent, Og
{
case COGRE_SUBMESH_OPERATION:
readShort(file, data, &subMesh.Operation);
#ifdef IRR_OGRE_LOADER_DEBUG
os::Printer::log("Read Submesh Operation",core::stringc(subMesh.Operation));
#endif
if (subMesh.Operation != 4)
os::Printer::log("Primitive type != trilist not yet implemented", ELL_WARNING);
break;
case COGRE_SUBMESH_TEXTURE_ALIAS:
{
#ifdef IRR_OGRE_LOADER_DEBUG
os::Printer::log("Read Submesh Texture Alias");
#endif
core::stringc texture, alias;
readString(file, data, texture);
readString(file, data, alias);
......@@ -369,9 +416,12 @@ bool COgreMeshFileLoader::readSubMesh(io::IReadFile* file, ChunkData& parent, Og
}
break;
case COGRE_SUBMESH_BONE_ASSIGNMENT:
// currently ignore chunk
file->seek(data.header.length-data.read, true);
data.read += data.header.length-data.read;
{
subMesh.BoneAssignments.push_back(OgreBoneAssignment());
readInt(file, data, &subMesh.BoneAssignments.getLast().VertexID);
readShort(file, data, &subMesh.BoneAssignments.getLast().BoneID);
readFloat(file, data, &subMesh.BoneAssignments.getLast().Weight);
}
break;
default:
parent.read=parent.header.length;
......@@ -554,6 +604,91 @@ scene::SMeshBufferLightMap* COgreMeshFileLoader::composeMeshBufferLightMap(const
}
scene::IMeshBuffer* COgreMeshFileLoader::composeMeshBufferSkinned(scene::CSkinnedMesh& mesh, const core::array<s32>& indices, const OgreGeometry& geom)
{
scene::SSkinMeshBuffer *mb=mesh.addMeshBuffer();
if (NumUV>1)
{
mb->convertTo2TCoords();
mb->Vertices_2TCoords.set_used(geom.NumVertex);
}
else
mb->Vertices_Standard.set_used(geom.NumVertex);
u32 i;
mb->Indices.set_used(indices.size());
for (i=0; i<indices.size(); i+=3)
{
mb->Indices[i+0]=indices[i+2];
mb->Indices[i+1]=indices[i+1];
mb->Indices[i+2]=indices[i+0];
}
for (i=0; i<geom.Elements.size(); ++i)
{
if (geom.Elements[i].Semantic==1) //Pos
{
for (u32 j=0; j<geom.Buffers.size(); ++j)
{
if (geom.Elements[i].Source==geom.Buffers[j].BindIndex)
{
u32 eSize=geom.Buffers[j].VertexSize;
u32 ePos=geom.Elements[i].Offset;
for (s32 k=0; k<geom.NumVertex; ++k)
{
if (NumUV>1)
mb->Vertices_2TCoords[k].Color=mb->Material.DiffuseColor;
else
mb->Vertices_Standard[k].Color=mb->Material.DiffuseColor;
mb->getPosition(k).set(-geom.Buffers[j].Data[ePos],geom.Buffers[j].Data[ePos+1],geom.Buffers[j].Data[ePos+2]);
ePos += eSize;
}
}
}
}
if (geom.Elements[i].Semantic==4) //Normal
{
for (u32 j=0; j<geom.Buffers.size(); ++j)
{
if (geom.Elements[i].Source==geom.Buffers[j].BindIndex)
{
u32 eSize=geom.Buffers[j].VertexSize;
u32 ePos=geom.Elements[i].Offset;
for (s32 k=0; k<geom.NumVertex; ++k)
{
mb->getNormal(k).set(-geom.Buffers[j].Data[ePos],geom.Buffers[j].Data[ePos+1],geom.Buffers[j].Data[ePos+2]);
ePos += eSize;
}
}
}
}
if (geom.Elements[i].Semantic==7) //TexCoord
{
for (u32 j=0; j<geom.Buffers.size(); ++j)
{
if (geom.Elements[i].Source==geom.Buffers[j].BindIndex)
{
u32 eSize=geom.Buffers[j].VertexSize;
u32 ePos=geom.Elements[i].Offset;
for (s32 k=0; k<geom.NumVertex; ++k)
{
mb->getTCoords(k).set(geom.Buffers[j].Data[ePos], geom.Buffers[j].Data[ePos+1]);
if (NumUV>1)
mb->Vertices_2TCoords[k].TCoords2.set(geom.Buffers[j].Data[ePos+2], geom.Buffers[j].Data[ePos+3]);
ePos += eSize;
}
}
}
}
}
return mb;
}
void COgreMeshFileLoader::composeObject(void)
{
for (u32 i=0; i<Meshes.size(); ++i)
......@@ -563,7 +698,11 @@ void COgreMeshFileLoader::composeObject(void)
IMeshBuffer* mb;
if (Meshes[i].SubMeshes[j].SharedVertices)
{
if (NumUV < 2)
if (Skeleton.Bones.size())
{
mb = composeMeshBufferSkinned(*(CSkinnedMesh*)Mesh, Meshes[i].SubMeshes[j].Indices, Meshes[i].Geometry);
}
else if (NumUV < 2)
{
mb = composeMeshBuffer(Meshes[i].SubMeshes[j].Indices, Meshes[i].Geometry);
}
......@@ -574,7 +713,11 @@ void COgreMeshFileLoader::composeObject(void)
}
else
{
if (NumUV < 2)
if (Skeleton.Bones.size())
{
mb = composeMeshBufferSkinned(*(CSkinnedMesh*)Mesh, Meshes[i].SubMeshes[j].Indices, Meshes[i].SubMeshes[j].Geometry);
}
else if (NumUV < 2)
{
mb = composeMeshBuffer(Meshes[i].SubMeshes[j].Indices, Meshes[i].SubMeshes[j].Geometry);
}
......@@ -587,11 +730,88 @@ void COgreMeshFileLoader::composeObject(void)
if (mb != 0)
{
composeMeshBufferMaterial(mb, Meshes[i].SubMeshes[j].Material);
Mesh->addMeshBuffer(mb);
if (!Skeleton.Bones.size())
{
((SMesh*)Mesh)->addMeshBuffer(mb);
mb->drop();
}
}
}
}
if (Skeleton.Bones.size())
{
CSkinnedMesh* m = (CSkinnedMesh*)Mesh;
// Create Joints
for (u32 i=0; i<Skeleton.Bones.size(); ++i)
{
ISkinnedMesh::SJoint* joint = m->addJoint();
joint->Name=Skeleton.Bones[i].Name;
joint->LocalMatrix = Skeleton.Bones[i].Orientation.getMatrix();
if (Skeleton.Bones[i].Scale != core::vector3df(1,1,1))
{
core::matrix4 scaleMatrix;
scaleMatrix.setScale( Skeleton.Bones[i].Scale );
joint->LocalMatrix *= scaleMatrix;
}
/*
joint->LocalMatrix[2]=-joint->LocalMatrix[2];
joint->LocalMatrix[6]=-joint->LocalMatrix[6];
joint->LocalMatrix[8]=-joint->LocalMatrix[8];
joint->LocalMatrix[9]=-joint->LocalMatrix[9];
*/
joint->LocalMatrix.setTranslation( Skeleton.Bones[i].Position );
}
// Joints hierarchy
core::array<bool> isRoot;
isRoot.set_used(Skeleton.Bones.size());
memset(isRoot.pointer(), true, Skeleton.Bones.size());
for (u32 i=0; i<Skeleton.Bones.size(); ++i)
{
if (Skeleton.Bones[i].Parent<m->getJointCount())
{
m->getAllJoints()[Skeleton.Bones[i].Parent]->Children.push_back(m->getAllJoints()[Skeleton.Bones[i].Handle]);
isRoot[Skeleton.Bones[i].Handle]=false;
}
}
// Weights
u32 bufCount=0;
for (u32 i=0; i<Meshes.size(); ++i)
{
for (u32 j=0; j<Meshes[i].SubMeshes.size(); ++j)
{
for (u32 k=0; k<Meshes[i].SubMeshes[j].BoneAssignments.size(); ++k)
{
ISkinnedMesh::SWeight* w = m->addWeight(m->getAllJoints()[Meshes[i].SubMeshes[j].BoneAssignments[k].BoneID]);
w->strength=Meshes[i].SubMeshes[j].BoneAssignments[k].Weight;
w->vertex_id=Meshes[i].SubMeshes[j].BoneAssignments[k].VertexID;
w->buffer_id=bufCount;
}
++bufCount;
}
}
#if 0
// currently not working correctly
for (u32 i=0; i<Skeleton.Animations.size(); ++i)
{
for (u32 j=0; j<Skeleton.Animations[i].Keyframes.size(); ++j)
{
ISkinnedMesh::SPositionKey* poskey = m->addPositionKey(m->getAllJoints()[Skeleton.Animations[i].Keyframes[j].BoneID]);
poskey->frame=Skeleton.Animations[i].Keyframes[j].Time;
poskey->position=Skeleton.Animations[i].Keyframes[j].Position;
ISkinnedMesh::SRotationKey* rotkey = m->addRotationKey(m->getAllJoints()[Skeleton.Animations[i].Keyframes[j].BoneID]);
rotkey->frame=Skeleton.Animations[i].Keyframes[j].Time;
rotkey->rotation=Skeleton.Animations[i].Keyframes[j].Orientation;
ISkinnedMesh::SScaleKey* scalekey = m->addScaleKey(m->getAllJoints()[Skeleton.Animations[i].Keyframes[j].BoneID]);
scalekey->frame=Skeleton.Animations[i].Keyframes[j].Time;
scalekey->scale=Skeleton.Animations[i].Keyframes[j].Scale;
}
}
#endif
m->finalize();
}
}
......@@ -1007,7 +1227,6 @@ void COgreMeshFileLoader::readTechnique(io::IReadFile* file, OgreMaterial& mat)
}
void COgreMeshFileLoader::loadMaterials(io::IReadFile* meshFile)
{
#ifdef IRR_OGRE_LOADER_DEBUG
......@@ -1015,11 +1234,11 @@ void COgreMeshFileLoader::loadMaterials(io::IReadFile* meshFile)
#endif
core::stringc token = meshFile->getFileName();
io::path filename = token.subString(0, token.size()-4) + L"material";
io::IReadFile* file = FileSystem->createAndOpenFile(filename.c_str());
io::IReadFile* file = FileSystem->createAndOpenFile(filename);
if (!file)
{
os::Printer::log("Could not load OGRE material", filename.c_str());
os::Printer::log("Could not load OGRE material", filename);
return;
}
......@@ -1095,6 +1314,137 @@ void COgreMeshFileLoader::loadMaterials(io::IReadFile* meshFile)
}
bool COgreMeshFileLoader::loadSkeleton(io::IReadFile* meshFile, const core::stringc& name)
{
#ifdef IRR_OGRE_LOADER_DEBUG
os::Printer::log("Load Skeleton", name);
#endif
io::IReadFile* file = FileSystem->createAndOpenFile(name);
if (!file)
{
io::path filename = meshFile->getFileName().subString(0, meshFile->getFileName().size()-4) + L"skeleton";
file = FileSystem->createAndOpenFile(name);
if (!file)
{
os::Printer::log("Could not load matching skeleton", name);
return false;
}
}
s16 id;
file->read(&id, 2);
if (SwapEndian)
id = os::Byteswap::byteswap(id);
if (id != COGRE_HEADER)
{
file->drop();
return false;
}
core::stringc skeletonVersion;
ChunkData head;
readString(file, head, skeletonVersion);
if (skeletonVersion != "[Serializer_v1.10]")
{
file->drop();
return false;
}
u16 bone=0;
f32 animationTotal=0.f;
while(file->getPos() < file->getSize())
{
ChunkData data;
readChunkData(file, data);
switch(data.header.id)
{
case COGRE_SKELETON:
{
Skeleton.Bones.push_back(OgreBone());
OgreBone& bone = Skeleton.Bones.getLast();
readString(file, data, bone.Name);
readShort(file, data, &bone.Handle);
readVector(file, data, bone.Position);
readQuaternion(file, data, bone.Orientation);
#ifdef IRR_OGRE_LOADER_DEBUG
os::Printer::log("Bone", bone.Name+" ("+core::stringc(bone.Handle)+")");
// os::Printer::log("Position", core::stringc(bone.Position.X)+" "+core::stringc(bone.Position.Y)+" "+core::stringc(bone.Position.Z));
// os::Printer::log("Rotation quat", core::stringc(bone.Orientation.W)+" "+core::stringc(bone.Orientation.X)+" "+core::stringc(bone.Orientation.Y)+" "+core::stringc(bone.Orientation.Z));
// core::vector3df rot;
// bone.Orientation.toEuler(rot);
// rot *= core::RADTODEG;
// os::Printer::log("Rotation", core::stringc(rot.X)+" "+core::stringc(rot.Y)+" "+core::stringc(rot.Z));
#endif
if (data.read<(data.header.length-bone.Name.size()))
{
readVector(file, data, bone.Scale);
bone.Scale.X *= -1.f;
}
else
bone.Scale=core::vector3df(1,1,1);
bone.Parent=0xffff;
}
break;
case COGRE_BONE_PARENT:
{
u16 parent;
readShort(file, data, &bone);
readShort(file, data, &parent);
if (bone<Skeleton.Bones.size() && parent<Skeleton.Bones.size())
Skeleton.Bones[bone].Parent=parent;
}
break;
case COGRE_ANIMATION:
{
if (Skeleton.Animations.size())
animationTotal+=Skeleton.Animations.getLast().Length;
Skeleton.Animations.push_back(OgreAnimation());
OgreAnimation& anim = Skeleton.Animations.getLast();
readString(file, data, anim.Name);
readFloat(file, data, &anim.Length);
#ifdef IRR_OGRE_LOADER_DEBUG
os::Printer::log("Animation", anim.Name);
os::Printer::log("Length", core::stringc(anim.Length));
#endif
}
break;
case COGRE_ANIMATION_TRACK:
#ifdef IRR_OGRE_LOADER_DEBUG
// os::Printer::log("for Bone ", core::stringc(bone));
#endif
readShort(file, data, &bone); // store current bone
break;
case COGRE_ANIMATION_KEYFRAME:
{
Skeleton.Animations.getLast().Keyframes.push_back(OgreKeyframe());
OgreKeyframe& keyframe = Skeleton.Animations.getLast().Keyframes.getLast();
readFloat(file, data, &keyframe.Time);
keyframe.Time+=animationTotal;
readVector(file, data, keyframe.Position);
readQuaternion(file, data, keyframe.Orientation);
readVector(file, data, keyframe.Scale);
keyframe.Scale *= 1.f;
keyframe.BoneID=bone;
#ifdef IRR_OGRE_LOADER_DEBUG
// os::Printer::log("Keyframe time", core::stringc(keyframe.Time));
#endif
}
break;
case COGRE_ANIMATION_LINK:
#ifdef IRR_OGRE_LOADER_DEBUG
os::Printer::log("Animation link");
#endif
break;
default:
break;
}
}
file->drop();
return true;
}
void COgreMeshFileLoader::readChunkData(io::IReadFile* file, ChunkData& data)
{
file->read(&data.header, sizeof(ChunkHeader));
......@@ -1177,6 +1527,14 @@ void COgreMeshFileLoader::readVector(io::IReadFile* file, ChunkData& data, core:
readFloat(file, data, &out.X);
readFloat(file, data, &out.Y);
readFloat(file, data, &out.Z);
out.X *= -1.f;
}
void COgreMeshFileLoader::readQuaternion(io::IReadFile* file, ChunkData& data, core::quaternion& out)
{
readVector(file, data, *((core::vector3df*)&out.X));
readFloat(file, data, &out.W);
}
......@@ -1202,3 +1560,4 @@ void COgreMeshFileLoader::clearMeshes()
} // end namespace irr
#endif // _IRR_COMPILE_WITH_OGRE_LOADER_
......@@ -15,6 +15,8 @@
#include "SMeshBufferLightMap.h"
#include "IMeshManipulator.h"
#include "matrix4.h"
#include "quaternion.h"
#include "CSkinnedMesh.h"
namespace irr
{
......@@ -168,6 +170,13 @@ private:
core::stringc Alias;
};
struct OgreBoneAssignment
{
s32 VertexID;
u16 BoneID;
f32 Weight;
};
struct OgreSubMesh
{
core::stringc Material;
......@@ -176,6 +185,7 @@ private:
OgreGeometry Geometry;
u16 Operation;
core::array<OgreTextureAlias> TextureAliases;
core::array<OgreBoneAssignment> BoneAssignments;
bool Indices32Bit;
};
......@@ -184,11 +194,44 @@ private:
bool SkeletalAnimation;
OgreGeometry Geometry;
core::array<OgreSubMesh> SubMeshes;
core::array<OgreBoneAssignment> BoneAssignments;
core::vector3df BBoxMinEdge;
core::vector3df BBoxMaxEdge;
f32 BBoxRadius;
};
struct OgreBone
{
core::stringc Name;
core::vector3df Position;
core::quaternion Orientation;
core::vector3df Scale;
u16 Handle;
u16 Parent;
};
struct OgreKeyframe
{
u16 BoneID;
f32 Time;
core::vector3df Position;
core::quaternion Orientation;
core::vector3df Scale;
};
struct OgreAnimation
{
core::stringc Name;
f32 Length;
core::array<OgreKeyframe> Keyframes;
};
struct OgreSkeleton
{
core::array<OgreBone> Bones;
core::array<OgreAnimation> Animations;
};
bool readChunk(io::IReadFile* file);
bool readObjectChunk(io::IReadFile* file, ChunkData& parent, OgreMesh& mesh);
bool readGeometry(io::IReadFile* file, ChunkData& parent, OgreGeometry& geometry);
......@@ -203,16 +246,19 @@ private:
void readShort(io::IReadFile* file, ChunkData& data, u16* out, u32 num=1);
void readFloat(io::IReadFile* file, ChunkData& data, f32* out, u32 num=1);
void readVector(io::IReadFile* file, ChunkData& data, core::vector3df& out);
void readQuaternion(io::IReadFile* file, ChunkData& data, core::quaternion& out);
void composeMeshBufferMaterial(scene::IMeshBuffer* mb, const core::stringc& materialName);
scene::SMeshBuffer* composeMeshBuffer(const core::array<s32>& indices, const OgreGeometry& geom);
scene::SMeshBufferLightMap* composeMeshBufferLightMap(const core::array<s32>& indices, const OgreGeometry& geom);
scene::IMeshBuffer* composeMeshBufferSkinned(scene::CSkinnedMesh& mesh, const core::array<s32>& indices, const OgreGeometry& geom);
void composeObject(void);
bool readColor(io::IReadFile* meshFile, video::SColor& col);
void getMaterialToken(io::IReadFile* file, core::stringc& token, bool noNewLine=false);
void readTechnique(io::IReadFile* meshFile, OgreMaterial& mat);
void readPass(io::IReadFile* file, OgreTechnique& technique);
void loadMaterials(io::IReadFile* file);
bool loadSkeleton(io::IReadFile* meshFile, const core::stringc& name);
core::stringc getTextureFileName(const core::stringc& texture, core::stringc& model);
void clearMeshes();
......@@ -225,8 +271,9 @@ private:
io::path CurrentlyLoadingFromPath;
core::array<OgreMaterial> Materials;
OgreSkeleton Skeleton;
SMesh* Mesh;
IMesh* Mesh;
u32 NumUV;
};
......
......@@ -1448,12 +1448,6 @@ void CSkinnedMesh::calculateTangents(
}
} // end namespace scene
} // end namespace irr
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment