// Copyright (C) 2002-2011 Nikolaus Gebhardt // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h // orginally written by Christian Stehno, modified by Nikolaus Gebhardt #include "IrrCompileConfig.h" #ifdef _IRR_COMPILE_WITH_OGRE_LOADER_ #include "COgreMeshFileLoader.h" #include "os.h" #include "SMeshBuffer.h" #include "SAnimatedMesh.h" #include "IReadFile.h" #include "fast_atof.h" #include "coreutil.h" #ifdef _DEBUG #define IRR_OGRE_LOADER_DEBUG #endif namespace irr { namespace scene { namespace { enum OGRE_CHUNKS { // Main Chunks COGRE_HEADER= 0x1000, COGRE_SKELETON= 0x2000, COGRE_MESH= 0x3000, // sub chunks of COGRE_MESH COGRE_SUBMESH= 0x4000, COGRE_GEOMETRY= 0x5000, COGRE_SKELETON_LINK= 0x6000, COGRE_BONE_ASSIGNMENT= 0x7000, COGRE_MESH_LOD= 0x8000, COGRE_MESH_BOUNDS= 0x9000, 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, COGRE_SUBMESH_TEXTURE_ALIAS= 0x4200, // sub chunks of COGRE_GEOMETRY COGRE_GEOMETRY_VERTEX_DECLARATION= 0x5100, COGRE_GEOMETRY_VERTEX_ELEMENT= 0x5110, COGRE_GEOMETRY_VERTEX_BUFFER= 0x5200, COGRE_GEOMETRY_VERTEX_BUFFER_DATA= 0x5210 }; } //! Constructor COgreMeshFileLoader::COgreMeshFileLoader(io::IFileSystem* fs, video::IVideoDriver* driver) : FileSystem(fs), Driver(driver), SwapEndian(false), Mesh(0), NumUV(0) { #ifdef _DEBUG setDebugName("COgreMeshFileLoader"); #endif if (FileSystem) FileSystem->grab(); if (Driver) Driver->grab(); } //! destructor COgreMeshFileLoader::~COgreMeshFileLoader() { clearMeshes(); if (FileSystem) FileSystem->drop(); if (Driver) Driver->drop(); if (Mesh) Mesh->drop(); } //! returns true if the file maybe is able to be loaded by this class //! based on the file extension (e.g. ".bsp") bool COgreMeshFileLoader::isALoadableFileExtension(const io::path& filename) const { return core::hasFileExtension ( filename, "mesh" ); } //! 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 IReferenceCounted::drop() for more information. IAnimatedMesh* COgreMeshFileLoader::createMesh(io::IReadFile* file) { s16 id; file->read(&id, 2); if (id == COGRE_HEADER) SwapEndian=false; else if (id == 0x0010) SwapEndian=true; else return 0; ChunkData data; readString(file, data, Version); if ((Version != "[MeshSerializer_v1.30]") && (Version != "[MeshSerializer_v1.40]") && (Version != "[MeshSerializer_v1.41]")) return 0; clearMeshes(); if (Mesh) Mesh->drop(); CurrentlyLoadingFromPath = FileSystem->getFileDir(file->getFileName()); loadMaterials(file); if (readChunk(file)) { // 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(); ((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; return 0; } bool COgreMeshFileLoader::readChunk(io::IReadFile* file) { while(file->getPos() < file->getSize()) { ChunkData data; readChunkData(file, data); switch(data.header.id) { case COGRE_MESH: { Meshes.push_back(OgreMesh()); readObjectChunk(file, data, Meshes.getLast()); if (Skeleton.Bones.size()) Mesh = new CSkinnedMesh(); else Mesh = new SMesh(); composeObject(); } break; default: return true; } } return true; } bool COgreMeshFileLoader::readObjectChunk(io::IReadFile* file, ChunkData& parent, OgreMesh& mesh) { #ifdef IRR_OGRE_LOADER_DEBUG os::Printer::log("Read Object Chunk", ELL_DEBUG); #endif readBool(file, parent, mesh.SkeletalAnimation); bool skeleton_loaded=false; while ((parent.read < parent.header.length)&&(file->getPos() < file->getSize())) { ChunkData data; readChunkData(file, data); switch(data.header.id) { case COGRE_GEOMETRY: readGeometry(file, data, mesh.Geometry); break; case COGRE_SUBMESH: mesh.SubMeshes.push_back(OgreSubMesh()); readSubMesh(file, data, mesh.SubMeshes.getLast()); break; case COGRE_MESH_BOUNDS: { #ifdef IRR_OGRE_LOADER_DEBUG os::Printer::log("Read Mesh Bounds", ELL_DEBUG); #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", ELL_DEBUG); #endif core::stringc name; readString(file, data, name); loadSkeleton(file, name); skeleton_loaded=true; } 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: // ignore chunk file->seek(data.header.length-data.read, true); data.read += data.header.length-data.read; break; default: #ifdef IRR_OGRE_LOADER_DEBUG os::Printer::log("Skipping", core::stringc(data.header.id), ELL_DEBUG); #endif // ignore chunk file->seek(data.header.length-data.read, true); data.read += data.header.length-data.read; break; } parent.read += data.read; } if (!skeleton_loaded) loadSkeleton(file, FileSystem->getFileBasename(file->getFileName(), false)); return true; } bool COgreMeshFileLoader::readGeometry(io::IReadFile* file, ChunkData& parent, OgreGeometry& geometry) { #ifdef IRR_OGRE_LOADER_DEBUG os::Printer::log("Read Geometry", ELL_DEBUG); #endif readInt(file, parent, &geometry.NumVertex); while(parent.read < parent.header.length) { ChunkData data; readChunkData(file, data); switch(data.header.id) { case COGRE_GEOMETRY_VERTEX_DECLARATION: readVertexDeclaration(file, data, geometry); break; case COGRE_GEOMETRY_VERTEX_BUFFER: readVertexBuffer(file, data, geometry); break; default: // ignore chunk #ifdef IRR_OGRE_LOADER_DEBUG os::Printer::log("Skipping", core::stringc(data.header.id), ELL_DEBUG); #endif file->seek(data.header.length-data.read, true); data.read += data.header.length-data.read; } parent.read += data.read; } if (parent.read != parent.header.length) os::Printer::log("Incorrect geometry length. File might be corrupted."); return true; } bool COgreMeshFileLoader::readVertexDeclaration(io::IReadFile* file, ChunkData& parent, OgreGeometry& geometry) { #ifdef IRR_OGRE_LOADER_DEBUG os::Printer::log("Read Vertex Declaration", ELL_DEBUG); #endif NumUV = 0; while(parent.read < parent.header.length) { ChunkData data; readChunkData(file, data); switch(data.header.id) { case COGRE_GEOMETRY_VERTEX_ELEMENT: { geometry.Elements.push_back(OgreVertexElement()); OgreVertexElement& elem = geometry.Elements.getLast(); readShort(file, data, &elem.Source); readShort(file, data, &elem.Type); readShort(file, data, &elem.Semantic); if (elem.Semantic == 7) //Tex coords { ++NumUV; } readShort(file, data, &elem.Offset); elem.Offset /= sizeof(f32); readShort(file, data, &elem.Index); } break; default: // ignore chunk file->seek(data.header.length-data.read, true); data.read += data.header.length-data.read; } parent.read += data.read; } if (parent.read != parent.header.length) os::Printer::log("Incorrect vertex declaration length. File might be corrupted."); return true; } bool COgreMeshFileLoader::readVertexBuffer(io::IReadFile* file, ChunkData& parent, OgreGeometry& geometry) { #ifdef IRR_OGRE_LOADER_DEBUG os::Printer::log("Read Vertex Buffer", ELL_DEBUG); #endif OgreVertexBuffer buf; readShort(file, parent, &buf.BindIndex); readShort(file, parent, &buf.VertexSize); buf.VertexSize /= sizeof(f32); ChunkData data; readChunkData(file, data); if (data.header.id == COGRE_GEOMETRY_VERTEX_BUFFER_DATA) { buf.Data.set_used(geometry.NumVertex*buf.VertexSize); readFloat(file, data, buf.Data.pointer(), geometry.NumVertex*buf.VertexSize); } geometry.Buffers.push_back(buf); parent.read += data.read; if (parent.read != parent.header.length) os::Printer::log("Incorrect vertex buffer length. File might be corrupted."); return true; } bool COgreMeshFileLoader::readSubMesh(io::IReadFile* file, ChunkData& parent, OgreSubMesh& subMesh) { #ifdef IRR_OGRE_LOADER_DEBUG os::Printer::log("Read Submesh", ELL_DEBUG); #endif readString(file, parent, subMesh.Material); #ifdef IRR_OGRE_LOADER_DEBUG os::Printer::log("using material", subMesh.Material, ELL_DEBUG); #endif readBool(file, parent, subMesh.SharedVertices); s32 numIndices; readInt(file, parent, &numIndices); subMesh.Indices.set_used(numIndices); readBool(file, parent, subMesh.Indices32Bit); if (subMesh.Indices32Bit) readInt(file, parent, subMesh.Indices.pointer(), numIndices); else { for (s32 i=0; i<numIndices; ++i) { u16 num; readShort(file, parent, &num); subMesh.Indices[i]=num; } } while(parent.read < parent.header.length) { ChunkData data; readChunkData(file, data); switch(data.header.id) { case COGRE_GEOMETRY: readGeometry(file, data, subMesh.Geometry); break; case COGRE_SUBMESH_OPERATION: readShort(file, data, &subMesh.Operation); #ifdef IRR_OGRE_LOADER_DEBUG os::Printer::log("Read Submesh Operation",core::stringc(subMesh.Operation), ELL_DEBUG); #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", ELL_DEBUG); #endif core::stringc texture, alias; readString(file, data, texture); readString(file, data, alias); subMesh.TextureAliases.push_back(OgreTextureAlias(texture,alias)); } break; case COGRE_SUBMESH_BONE_ASSIGNMENT: { 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: #ifdef IRR_OGRE_LOADER_DEBUG os::Printer::log("Skipping", core::stringc(data.header.id), ELL_DEBUG); #endif parent.read=parent.header.length; file->seek(-(long)sizeof(ChunkHeader), true); return true; } parent.read += data.read; } if (parent.read != parent.header.length) os::Printer::log("Incorrect submesh length. File might be corrupted."); #ifdef IRR_OGRE_LOADER_DEBUG os::Printer::log("Done with submesh", ELL_DEBUG); #endif return true; } void COgreMeshFileLoader::composeMeshBufferMaterial(scene::IMeshBuffer* mb, const core::stringc& materialName) { video::SMaterial& material=mb->getMaterial(); for (u32 k=0; k<Materials.size(); ++k) { if ((materialName==Materials[k].Name)&&(Materials[k].Techniques.size())&&(Materials[k].Techniques[0].Passes.size())) { material=Materials[k].Techniques[0].Passes[0].Material; if (Materials[k].Techniques[0].Passes[0].Texture.Filename.size()) { if (FileSystem->existFile(Materials[k].Techniques[0].Passes[0].Texture.Filename)) material.setTexture(0, Driver->getTexture(Materials[k].Techniques[0].Passes[0].Texture.Filename)); else material.setTexture(0, Driver->getTexture((CurrentlyLoadingFromPath+"/"+FileSystem->getFileBasename(Materials[k].Techniques[0].Passes[0].Texture.Filename)))); } break; } } } scene::SMeshBuffer* COgreMeshFileLoader::composeMeshBuffer(const core::array<s32>& indices, const OgreGeometry& geom) { scene::SMeshBuffer *mb=new scene::SMeshBuffer(); u32 i; mb->Indices.set_used(indices.size()); for (i=0; i<indices.size(); ++i) mb->Indices[i]=indices[i]; mb->Vertices.set_used(geom.NumVertex); 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) { mb->Vertices[k].Color=mb->Material.DiffuseColor; mb->Vertices[k].Pos.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->Vertices[k].Normal.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->Vertices[k].TCoords.set(geom.Buffers[j].Data[ePos],geom.Buffers[j].Data[ePos+1]); ePos += eSize; } } } } } return mb; } scene::SMeshBufferLightMap* COgreMeshFileLoader::composeMeshBufferLightMap(const core::array<s32>& indices, const OgreGeometry& geom) { scene::SMeshBufferLightMap *mb=new scene::SMeshBufferLightMap(); u32 i; mb->Indices.set_used(indices.size()); for (i=0; i<indices.size(); ++i) mb->Indices[i]=indices[i]; mb->Vertices.set_used(geom.NumVertex); 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) { mb->Vertices[k].Color=mb->Material.DiffuseColor; mb->Vertices[k].Pos.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->Vertices[k].Normal.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->Vertices[k].TCoords.set(geom.Buffers[j].Data[ePos], geom.Buffers[j].Data[ePos+1]); mb->Vertices[k].TCoords2.set(geom.Buffers[j].Data[ePos+2], geom.Buffers[j].Data[ePos+3]); ePos += eSize; } } } } } return mb; } 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) { for (u32 j=0; j<Meshes[i].SubMeshes.size(); ++j) { IMeshBuffer* mb; if (Meshes[i].SubMeshes[j].SharedVertices) { 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); } else { mb = composeMeshBufferLightMap(Meshes[i].SubMeshes[j].Indices, Meshes[i].Geometry); } } else { 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); } else { mb = composeMeshBufferLightMap(Meshes[i].SubMeshes[j].Indices, Meshes[i].SubMeshes[j].Geometry); } } if (mb != 0) { composeMeshBufferMaterial(mb, Meshes[i].SubMeshes[j].Material); 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.setTranslation( Skeleton.Bones[i].Position ); } // Joints hierarchy 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]); } } // 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) { const OgreBoneAssignment& ba = Meshes[i].SubMeshes[j].BoneAssignments[k]; if (ba.BoneID<m->getJointCount()) { ISkinnedMesh::SWeight* w = m->addWeight(m->getAllJoints()[ba.BoneID]); w->strength=ba.Weight; w->vertex_id=ba.VertexID; w->buffer_id=bufCount; } } ++bufCount; } } for (u32 i=0; i<Skeleton.Animations.size(); ++i) { for (u32 j=0; j<Skeleton.Animations[i].Keyframes.size(); ++j) { OgreKeyframe& frame = Skeleton.Animations[i].Keyframes[j]; ISkinnedMesh::SJoint* keyjoint = m->getAllJoints()[frame.BoneID]; ISkinnedMesh::SPositionKey* poskey = m->addPositionKey(keyjoint); poskey->frame=frame.Time*25; poskey->position=keyjoint->LocalMatrix.getTranslation()+frame.Position; ISkinnedMesh::SRotationKey* rotkey = m->addRotationKey(keyjoint); rotkey->frame=frame.Time*25; rotkey->rotation=core::quaternion(keyjoint->LocalMatrix)*frame.Orientation; ISkinnedMesh::SScaleKey* scalekey = m->addScaleKey(keyjoint); scalekey->frame=frame.Time*25; scalekey->scale=frame.Scale; } } m->finalize(); } } void COgreMeshFileLoader::getMaterialToken(io::IReadFile* file, core::stringc& token, bool noNewLine) { bool parseString=false; c8 c=0; token = ""; if (file->getPos() >= file->getSize()) return; file->read(&c, sizeof(c8)); // search for word beginning while ( core::isspace(c) && (file->getPos() < file->getSize())) { if (noNewLine && c=='\n') { file->seek(-1, true); return; } file->read(&c, sizeof(c8)); } // check if we read a string if (c=='"') { parseString = true; file->read(&c, sizeof(c8)); } do { if (c=='/') { file->read(&c, sizeof(c8)); // check for comments, cannot be part of strings if (!parseString && (c=='/')) { // skip comments while(c!='\n') file->read(&c, sizeof(c8)); if (!token.size()) { // if we start with a comment we need to skip // following whitespaces, so restart getMaterialToken(file, token, noNewLine); return; } else { // else continue with next character file->read(&c, sizeof(c8)); continue; } } else { // else append first slash and check if second char // ends this token token.append('/'); if ((!parseString && core::isspace(c)) || (parseString && (c=='"'))) return; } } token.append(c); file->read(&c, sizeof(c8)); // read until a token delimiter is found } while (((!parseString && !core::isspace(c)) || (parseString && (c!='"'))) && (file->getPos() < file->getSize())); // we want to skip the last quotes of a string , but other chars might be the next // token already. if (!parseString) file->seek(-1, true); } bool COgreMeshFileLoader::readColor(io::IReadFile* file, video::SColor& col) { core::stringc token; getMaterialToken(file, token); if (token!="vertexcolour") { video::SColorf col_f; col_f.r=core::fast_atof(token.c_str()); getMaterialToken(file, token); col_f.g=core::fast_atof(token.c_str()); getMaterialToken(file, token); col_f.b=core::fast_atof(token.c_str()); getMaterialToken(file, token, true); if (token.size()) col_f.a=core::fast_atof(token.c_str()); else col_f.a=1.0f; if ((col_f.r==0.0f)&&(col_f.g==0.0f)&&(col_f.b==0.0f)) col.set(255,255,255,255); else col=col_f.toSColor(); return false; } return true; } void COgreMeshFileLoader::readPass(io::IReadFile* file, OgreTechnique& technique) { #ifdef IRR_OGRE_LOADER_DEBUG os::Printer::log("Read Pass"); #endif core::stringc token; technique.Passes.push_back(OgrePass()); OgrePass& pass=technique.Passes.getLast(); getMaterialToken(file, token); //open brace or name if (token != "{") getMaterialToken(file, token); //open brace getMaterialToken(file, token); if (token == "}") return; u32 inBlocks=1; u32 textureUnit=0; while(inBlocks) { if (token=="ambient") pass.AmbientTokenColor=readColor(file, pass.Material.AmbientColor); else if (token=="diffuse") pass.DiffuseTokenColor=readColor(file, pass.Material.DiffuseColor); else if (token=="specular") { pass.SpecularTokenColor=readColor(file, pass.Material.SpecularColor); getMaterialToken(file, token); pass.Material.Shininess=core::fast_atof(token.c_str()); } else if (token=="emissive") pass.EmissiveTokenColor=readColor(file, pass.Material.EmissiveColor); else if (token=="scene_blend") { // TODO: Choose correct values getMaterialToken(file, token); if (token=="add") pass.Material.MaterialType=video::EMT_TRANSPARENT_ADD_COLOR; else if (token=="modulate") pass.Material.MaterialType=video::EMT_SOLID; else if (token=="alpha_blend") pass.Material.MaterialType=video::EMT_TRANSPARENT_ALPHA_CHANNEL; else if (token=="colour_blend") pass.Material.MaterialType=video::EMT_TRANSPARENT_VERTEX_ALPHA; else getMaterialToken(file, token); } else if (token=="depth_check") { getMaterialToken(file, token); if (token!="on") pass.Material.ZBuffer=video::ECFN_NEVER; } else if (token=="depth_write") { getMaterialToken(file, token); pass.Material.ZWriteEnable=(token=="on"); } else if (token=="depth_func") { getMaterialToken(file, token); // Function name if (token=="always_fail") pass.Material.ZBuffer=video::ECFN_NEVER; else if (token=="always_pass") pass.Material.ZBuffer=video::ECFN_ALWAYS; else if (token=="equal") pass.Material.ZBuffer=video::ECFN_EQUAL; else if (token=="greater") pass.Material.ZBuffer=video::ECFN_GREATER; else if (token=="greater_equal") pass.Material.ZBuffer=video::ECFN_GREATEREQUAL; else if (token=="less") pass.Material.ZBuffer=video::ECFN_LESS; else if (token=="less_equal") pass.Material.ZBuffer=video::ECFN_LESSEQUAL; else if (token=="not_equal") pass.Material.ZBuffer=video::ECFN_NOTEQUAL; } else if (token=="normalise_normals") { getMaterialToken(file, token); pass.Material.NormalizeNormals=(token=="on"); } else if (token=="depth_bias") { getMaterialToken(file, token); // bias value } else if (token=="alpha_rejection") { getMaterialToken(file, token); // function name getMaterialToken(file, token); // value pass.Material.MaterialTypeParam=core::fast_atof(token.c_str()); } else if (token=="alpha_to_coverage") { getMaterialToken(file, token); if (token=="on") pass.Material.AntiAliasing |= video::EAAM_ALPHA_TO_COVERAGE; } else if (token=="colour_write") { getMaterialToken(file, token); pass.Material.ColorMask = (token=="on")?video::ECP_ALL:video::ECP_NONE; } else if (token=="cull_hardware") { getMaterialToken(file, token); // rotation name } else if (token=="cull_software") { getMaterialToken(file, token); // culling side } else if (token=="lighting") { getMaterialToken(file, token); pass.Material.Lighting=(token=="on"); } else if (token=="shading") { getMaterialToken(file, token); // We take phong as gouraud pass.Material.GouraudShading=(token!="flat"); } else if (token=="polygon_mode") { getMaterialToken(file, token); pass.Material.Wireframe=(token=="wireframe"); pass.Material.PointCloud=(token=="points"); } else if (token=="max_lights") { getMaterialToken(file, token); pass.MaxLights=strtol(token.c_str(),NULL,10); } else if (token=="point_size") { getMaterialToken(file, token); pass.PointSize=core::fast_atof(token.c_str()); } else if (token=="point_sprites") { getMaterialToken(file, token); pass.PointSprites=(token=="on"); } else if (token=="point_size_min") { getMaterialToken(file, token); pass.PointSizeMin=strtol(token.c_str(),NULL,10); } else if (token=="point_size_max") { getMaterialToken(file, token); pass.PointSizeMax=strtol(token.c_str(),NULL,10); } else if (token=="texture_unit") { #ifdef IRR_OGRE_LOADER_DEBUG os::Printer::log("Read Texture unit", ELL_DEBUG); #endif getMaterialToken(file, token); //open brace getMaterialToken(file, token); while(token != "}") { if (token=="texture") { getMaterialToken(file, pass.Texture.Filename); #ifdef IRR_OGRE_LOADER_DEBUG os::Printer::log("Read Texture", pass.Texture.Filename.c_str(), ELL_DEBUG); #endif getMaterialToken(file, pass.Texture.CoordsType, true); getMaterialToken(file, pass.Texture.MipMaps, true); getMaterialToken(file, pass.Texture.Alpha, true); } else if (token=="filtering") { getMaterialToken(file, token); pass.Material.TextureLayer[textureUnit].AnisotropicFilter=0; if (token=="point") { pass.Material.TextureLayer[textureUnit].BilinearFilter=false; pass.Material.TextureLayer[textureUnit].TrilinearFilter=false; getMaterialToken(file, token); getMaterialToken(file, token); } else if (token=="linear") { getMaterialToken(file, token); if (token=="point") { pass.Material.TextureLayer[textureUnit].BilinearFilter=false; pass.Material.TextureLayer[textureUnit].TrilinearFilter=false; getMaterialToken(file, token); } else { pass.Material.TextureLayer[textureUnit].BilinearFilter=true; getMaterialToken(file, token); pass.Material.TextureLayer[textureUnit].TrilinearFilter=(token=="linear"); } } else { pass.Material.TextureLayer[textureUnit].BilinearFilter=(token=="bilinear"); pass.Material.TextureLayer[textureUnit].TrilinearFilter=(token=="trilinear"); pass.Material.TextureLayer[textureUnit].AnisotropicFilter=(token=="anisotropic")?2:1; } } else if (token=="max_anisotropy") { getMaterialToken(file, token); pass.Material.TextureLayer[textureUnit].AnisotropicFilter=(u8)core::strtol10(token.c_str()); } else if (token=="texture_alias") { getMaterialToken(file, pass.Texture.Alias); } else if (token=="mipmap_bias") { getMaterialToken(file, token); pass.Material.TextureLayer[textureUnit].LODBias=(s8)core::fast_atof(token.c_str()); } else if (token=="colour_op") { // TODO: Choose correct values getMaterialToken(file, token); if (token=="add") pass.Material.MaterialType=video::EMT_TRANSPARENT_ADD_COLOR; else if (token=="modulate") pass.Material.MaterialType=video::EMT_SOLID; else if (token=="alpha_blend") pass.Material.MaterialType=video::EMT_TRANSPARENT_ALPHA_CHANNEL; else if (token=="colour_blend") pass.Material.MaterialType=video::EMT_TRANSPARENT_VERTEX_ALPHA; else getMaterialToken(file, token); } getMaterialToken(file, token); } ++textureUnit; } else if (token=="shadow_caster_program_ref") { do { getMaterialToken(file, token); } while (token != "}"); } else if (token=="shadow_caster_vertex_program_ref") { do { getMaterialToken(file, token); } while (token != "}"); } else if (token=="vertex_program_ref") { do { getMaterialToken(file, token); } while (token != "}"); } //fog_override, iteration, point_size_attenuation //not considered yet! getMaterialToken(file, token); if (token=="{") ++inBlocks; else if (token=="}") --inBlocks; } } void COgreMeshFileLoader::readTechnique(io::IReadFile* file, OgreMaterial& mat) { #ifdef IRR_OGRE_LOADER_DEBUG os::Printer::log("Read Technique"); #endif core::stringc token; mat.Techniques.push_back(OgreTechnique()); OgreTechnique& technique=mat.Techniques.getLast(); getMaterialToken(file, technique.Name); //open brace or name if (technique.Name != "{") getMaterialToken(file, token); //open brace else technique.Name=core::stringc((int)mat.Techniques.size()); getMaterialToken(file, token); while (token != "}") { if (token == "pass") readPass(file, technique); else if (token == "scheme") getMaterialToken(file, token); else if (token == "lod_index") getMaterialToken(file, token); getMaterialToken(file, token); } } void COgreMeshFileLoader::loadMaterials(io::IReadFile* meshFile) { #ifdef IRR_OGRE_LOADER_DEBUG os::Printer::log("Load Materials", ELL_DEBUG); #endif core::stringc token; io::IReadFile* file = 0; io::path filename = FileSystem->getFileBasename(meshFile->getFileName(), false) + ".material"; if (FileSystem->existFile(filename)) file = FileSystem->createAndOpenFile(filename); else file = FileSystem->createAndOpenFile(FileSystem->getFileDir(meshFile->getFileName())+"/"+filename); if (!file) { os::Printer::log("Could not load OGRE material", filename); return; } getMaterialToken(file, token); while (file->getPos() < file->getSize()) { if ((token == "fragment_program") || (token == "vertex_program")) { // skip whole block u32 blocks=1; do { getMaterialToken(file, token); } while (token != "{"); do { getMaterialToken(file, token); if (token == "{") ++blocks; else if (token == "}") --blocks; } while (blocks); getMaterialToken(file, token); continue; } if (token != "material") { if (token.trim().size()) os::Printer::log("Unknown material group", token.c_str()); break; } Materials.push_back(OgreMaterial()); OgreMaterial& mat = Materials.getLast(); getMaterialToken(file, mat.Name); #ifdef IRR_OGRE_LOADER_DEBUG os::Printer::log("Load Material", mat.Name.c_str(), ELL_DEBUG); #endif getMaterialToken(file, token); //open brace getMaterialToken(file, token); while(token != "}") { if (token=="lod_distances") // can have several items getMaterialToken(file, token); else if (token=="receive_shadows") { getMaterialToken(file, token); mat.ReceiveShadows=(token=="on"); } else if (token=="transparency_casts_shadows") { getMaterialToken(file, token); mat.TransparencyCastsShadows=(token=="on"); } else if (token=="set_texture_alias") { getMaterialToken(file, token); getMaterialToken(file, token); } else if (token=="technique") readTechnique(file, mat); getMaterialToken(file, token); } getMaterialToken(file, token); } file->drop(); #ifdef IRR_OGRE_LOADER_DEBUG os::Printer::log("Finished loading Materials", ELL_DEBUG); #endif } bool COgreMeshFileLoader::loadSkeleton(io::IReadFile* meshFile, const core::stringc& name) { #ifdef IRR_OGRE_LOADER_DEBUG os::Printer::log("Load Skeleton", name, ELL_DEBUG); #endif io::IReadFile* file = 0; io::path filename; if (FileSystem->existFile(name)) file = FileSystem->createAndOpenFile(name); else if (FileSystem->existFile(filename = FileSystem->getFileDir(meshFile->getFileName())+"/"+name)) file = FileSystem->createAndOpenFile(filename); else if (FileSystem->existFile(filename = FileSystem->getFileBasename(meshFile->getFileName(), false) + ".skeleton")) file = FileSystem->createAndOpenFile(filename); else file = FileSystem->createAndOpenFile(FileSystem->getFileDir(meshFile->getFileName())+"/"+filename); 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)+")", ELL_DEBUG); os::Printer::log("Position", core::stringc(bone.Position.X)+" "+core::stringc(bone.Position.Y)+" "+core::stringc(bone.Position.Z), ELL_DEBUG); 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), ELL_DEBUG); // 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, ELL_DEBUG); os::Printer::log("Length", core::stringc(anim.Length), ELL_DEBUG); #endif } break; case COGRE_ANIMATION_TRACK: #ifdef IRR_OGRE_LOADER_DEBUG os::Printer::log("for Bone ", core::stringc(bone), ELL_DEBUG); #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; readQuaternion(file, data, keyframe.Orientation); readVector(file, data, keyframe.Position); if (data.read<data.header.length) { readVector(file, data, keyframe.Scale); keyframe.Scale.X *= -1.f; } else keyframe.Scale=core::vector3df(1,1,1); keyframe.BoneID=bone; } break; case COGRE_ANIMATION_LINK: #ifdef IRR_OGRE_LOADER_DEBUG os::Printer::log("Animation link", ELL_DEBUG); #endif break; default: break; } } file->drop(); return true; } void COgreMeshFileLoader::readChunkData(io::IReadFile* file, ChunkData& data) { file->read(&data.header, sizeof(ChunkHeader)); if (SwapEndian) { data.header.id = os::Byteswap::byteswap(data.header.id); data.header.length = os::Byteswap::byteswap(data.header.length); } data.read += sizeof(ChunkHeader); } void COgreMeshFileLoader::readString(io::IReadFile* file, ChunkData& data, core::stringc& out) { c8 c = 0; out = ""; while (c!='\n') { file->read(&c, sizeof(c8)); if (c!='\n') out.append(c); } data.read+=out.size()+1; } void COgreMeshFileLoader::readBool(io::IReadFile* file, ChunkData& data, bool& out) { // normal C type because we read a bit string char c = 0; file->read(&c, sizeof(char)); out=(c!=0); ++data.read; } void COgreMeshFileLoader::readInt(io::IReadFile* file, ChunkData& data, s32* out, u32 num) { // normal C type because we read a bit string file->read(out, sizeof(int)*num); if (SwapEndian) { for (u32 i=0; i<num; ++i) out[i] = os::Byteswap::byteswap(out[i]); } data.read+=sizeof(int)*num; } void COgreMeshFileLoader::readShort(io::IReadFile* file, ChunkData& data, u16* out, u32 num) { // normal C type because we read a bit string file->read(out, sizeof(short)*num); if (SwapEndian) { for (u32 i=0; i<num; ++i) out[i] = os::Byteswap::byteswap(out[i]); } data.read+=sizeof(short)*num; } void COgreMeshFileLoader::readFloat(io::IReadFile* file, ChunkData& data, f32* out, u32 num) { // normal C type because we read a bit string file->read(out, sizeof(float)*num); if (SwapEndian) { for (u32 i=0; i<num; ++i) out[i] = os::Byteswap::byteswap(out[i]); } data.read+=sizeof(float)*num; } void COgreMeshFileLoader::readVector(io::IReadFile* file, ChunkData& data, core::vector3df& out) { 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); } void COgreMeshFileLoader::clearMeshes() { for (u32 i=0; i<Meshes.size(); ++i) { for (int k=0; k<(int)Meshes[i].Geometry.Buffers.size(); ++k) Meshes[i].Geometry.Buffers[k].Data.clear(); for (u32 j=0; j<Meshes[i].SubMeshes.size(); ++j) { for (int h=0; h<(int)Meshes[i].SubMeshes[j].Geometry.Buffers.size(); ++h) Meshes[i].SubMeshes[j].Geometry.Buffers[h].Data.clear(); } } Meshes.clear(); } } // end namespace scene } // end namespace irr #endif // _IRR_COMPILE_WITH_OGRE_LOADER_
Options | Liens officiels | Caractéristiques | Statistiques | Communauté |
---|---|---|---|---|
Corrections |
|
xhtml 1.0 css 2.1 Propulsé par FluxBB Traduit par FluxBB.fr |
882 membres 1429 sujets 11119 messages |
Dernier membre inscrit: LiseBuisson96 60 invités en ligne Aucun membre connecté RSS Feed |