#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
{
COGRE_HEADER= 0x1000,
COGRE_SKELETON= 0x2000,
COGRE_MESH= 0x3000,
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,
COGRE_BONE_PARENT= 0x3000,
COGRE_ANIMATION= 0x4000,
COGRE_ANIMATION_TRACK= 0x4100,
COGRE_ANIMATION_KEYFRAME= 0x4110,
COGRE_ANIMATION_LINK= 0x5000,
COGRE_SUBMESH_OPERATION= 0x4010,
COGRE_SUBMESH_BONE_ASSIGNMENT= 0x4100,
COGRE_SUBMESH_TEXTURE_ALIAS= 0x4200,
COGRE_GEOMETRY_VERTEX_DECLARATION= 0x5100,
COGRE_GEOMETRY_VERTEX_ELEMENT= 0x5110,
COGRE_GEOMETRY_VERTEX_BUFFER= 0x5200,
COGRE_GEOMETRY_VERTEX_BUFFER_DATA= 0x5210
};
}
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();
}
COgreMeshFileLoader::~COgreMeshFileLoader()
{
clearMeshes();
if (FileSystem)
FileSystem->drop();
if (Driver)
Driver->drop();
if (Mesh)
Mesh->drop();
}
bool COgreMeshFileLoader::isALoadableFileExtension(const io::path& filename) const
{
return core::hasFileExtension ( filename, "mesh" );
}
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))
{
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:
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
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:
#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)
{
++NumUV;
}
readShort(file, data, &elem.Offset);
elem.Offset /= sizeof(f32);
readShort(file, data, &elem.Index);
}
break;
default:
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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;
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 );
}
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]);
}
}
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));
while ( core::isspace(c) && (file->getPos() < file->getSize()))
{
if (noNewLine && c=='\n')
{
file->seek(-1, true);
return;
}
file->read(&c, sizeof(c8));
}
if (c=='"')
{
parseString = true;
file->read(&c, sizeof(c8));
}
do
{
if (c=='/')
{
file->read(&c, sizeof(c8));
if (!parseString && (c=='/'))
{
while(c!='\n')
file->read(&c, sizeof(c8));
if (!token.size())
{
getMaterialToken(file, token, noNewLine);
return;
}
else
{
file->read(&c, sizeof(c8));
continue;
}
}
else
{
token.append('/');
if ((!parseString && core::isspace(c)) ||
(parseString && (c=='"')))
return;
}
}
token.append(c);
file->read(&c, sizeof(c8));
}
while (((!parseString && !core::isspace(c)) || (parseString && (c!='"'))) &&
(file->getPos() < file->getSize()));
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);
if (token != "{")
getMaterialToken(file, token);
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")
{
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);
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);
}
else if (token=="alpha_rejection")
{
getMaterialToken(file, token);
getMaterialToken(file, token);
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);
}
else if (token=="cull_software")
{
getMaterialToken(file, token);
}
else if (token=="lighting")
{
getMaterialToken(file, token);
pass.Material.Lighting=(token=="on");
}
else if (token=="shading")
{
getMaterialToken(file, token);
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);
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")
{
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 != "}");
}
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);
if (technique.Name != "{")
getMaterialToken(file, token);
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"))
{
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);
getMaterialToken(file, token);
while(token != "}")
{
if (token=="lod_distances")
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);
#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);
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)
{
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)
{
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)
{
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)
{
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();
}
}
}
#endif