mirror of
https://github.com/minetest/irrlicht.git
synced 2024-09-07 05:10:19 +02:00
This breaks compiling. To have old values replace false with EZW_OFF and true with EWZ_AUTO. There's a bit history to this change. ZWriteFineControl got introduced after 1.8 so it was never in a released version. Basically it was needed after some changes had been made to allow shaders to have zwrite enabled independent of the material-type (which worked badly for shaders). This had caused other problems as it was then enabled too often instead. So to quickly fix those bugs and avoid breaking compatibility I had introduced a new enum ZWriteFineControl in SMaterial. This worked and didn't break compiling - but I noticed by now that introducing a second flag for this made maintainance for an already very hard to understand problem (figuring out the implementation of transparency and zwriting) even more complicated. So to keep maintance somewhat sane I decided to break compiling now and merge those two flags. The behavior should not be affected by this commit - except for users which set this flag already in their code and have to switch to the enum now. Serialization is switched on loading old files (so SMaterial has enum already and writes that out). git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@6026 dfc29bdd-3216-0410-991c-e03cc46cb475
1608 lines
44 KiB
C++
1608 lines
44 KiB
C++
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
|
// This file is part of the "Irrlicht Engine".
|
|
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
|
// Originally written by Christian Stehno, modified by Nikolaus Gebhardt
|
|
|
|
#include "IrrCompileConfig.h"
|
|
#ifdef _IRR_COMPILE_WITH_OGRE_LOADER_
|
|
|
|
#include "COgreMeshFileLoader.h"
|
|
#include "CMeshTextureLoader.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();
|
|
|
|
TextureLoader = new CMeshTextureLoader( FileSystem, Driver );
|
|
}
|
|
|
|
|
|
//! 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)
|
|
{
|
|
if ( !file )
|
|
return 0;
|
|
|
|
if ( getMeshTextureLoader() )
|
|
getMeshTextureLoader()->setMeshFile(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]"))
|
|
{
|
|
os::Printer::log("Unsupported ogre mesh version", Version.c_str(), ELL_INFORMATION);
|
|
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;
|
|
for (u32 i=0; i<Materials[k].Techniques[0].Passes[0].Texture.Filename.size(); ++i)
|
|
{
|
|
video::ITexture * texture = NULL;
|
|
if ( getMeshTextureLoader() )
|
|
{
|
|
texture = getMeshTextureLoader()->getTexture(Materials[k].Techniques[0].Passes[0].Texture.Filename[i]);
|
|
if ( texture )
|
|
material.setTexture(i, texture);
|
|
}
|
|
}
|
|
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;
|
|
// make sure we have data for a second texture coord
|
|
const bool secondCoord = (eSize>ePos+3);
|
|
for (s32 k=0; k<geom.NumVertex; ++k)
|
|
{
|
|
mb->Vertices[k].TCoords.set(geom.Buffers[j].Data[ePos], geom.Buffers[j].Data[ePos+1]);
|
|
if (secondCoord)
|
|
mb->Vertices[k].TCoords2.set(geom.Buffers[j].Data[ePos+2], geom.Buffers[j].Data[ePos+3]);
|
|
else
|
|
mb->Vertices[k].TCoords2.set(geom.Buffers[j].Data[ePos], geom.Buffers[j].Data[ePos+1]);
|
|
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;
|
|
// make sure we have data for a second texture coord
|
|
const bool secondCoord = (eSize>ePos+3);
|
|
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)
|
|
{
|
|
if (secondCoord)
|
|
mb->Vertices_2TCoords[k].TCoords2.set(geom.Buffers[j].Data[ePos+2], geom.Buffers[j].Data[ePos+3]);
|
|
else
|
|
mb->Vertices_2TCoords[k].TCoords2.set(geom.Buffers[j].Data[ePos], geom.Buffers[j].Data[ePos+1]);
|
|
}
|
|
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;
|
|
|
|
// IRR_TEST_BROKEN_QUATERNION_USE: TODO - switched to getMatrix_transposed instead of getMatrix for downward compatibility.
|
|
// Not tested so far if this was correct or wrong before quaternion fix!
|
|
Skeleton.Bones[i].Orientation.getMatrix_transposed(joint->LocalMatrix);
|
|
|
|
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;
|
|
|
|
// IRR_TEST_BROKEN_QUATERNION_USE: TODO - switched from keyjoint->LocalMatrix to keyjoint->LocalMatrix.getTransposed() for downward compatibility.
|
|
// Not tested so far if this was correct or wrong before quaternion fix!
|
|
rotkey->rotation=core::quaternion(keyjoint->LocalMatrix.getTransposed())*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_DISABLED;
|
|
}
|
|
else if (token=="depth_write")
|
|
{
|
|
getMaterialToken(file, token);
|
|
pass.Material.ZWriteEnable=(token=="on") ? video::EZW_ON : video::EZW_OFF;
|
|
}
|
|
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=core::strtoul10(token.c_str());
|
|
}
|
|
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=core::strtoul10(token.c_str());
|
|
}
|
|
else if (token=="point_size_max")
|
|
{
|
|
getMaterialToken(file, token);
|
|
pass.PointSizeMax=core::strtoul10(token.c_str());
|
|
}
|
|
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, token);
|
|
pass.Texture.Filename.push_back(token);
|
|
#ifdef IRR_OGRE_LOADER_DEBUG
|
|
os::Printer::log("Read Texture", token, ELL_DEBUG);
|
|
#endif
|
|
getMaterialToken(file, pass.Texture.CoordsType, true);
|
|
getMaterialToken(file, pass.Texture.MipMaps, true);
|
|
getMaterialToken(file, pass.Texture.Alpha, true);
|
|
// Hmm, we might need more hints for other material types using two textures...
|
|
if (textureUnit>0)
|
|
pass.Material.MaterialType=video::EMT_LIGHTMAP;
|
|
}
|
|
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::strtoul10(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_
|
|
|