#include "IrrCompileConfig.h"
#ifdef _IRR_COMPILE_WITH_IRR_MESH_LOADER_
#include "CIrrMeshFileLoader.h"
#include "os.h"
#include "IXMLReader.h"
#include "SAnimatedMesh.h"
#include "fast_atof.h"
#include "IReadFile.h"
#include "IAttributes.h"
#include "IMeshSceneNode.h"
#include "CDynamicMeshBuffer.h"
#include "SMeshBufferLightMap.h"
namespace irr
{
namespace scene
{
CIrrMeshFileLoader::CIrrMeshFileLoader(scene::ISceneManager* smgr,
io::IFileSystem* fs)
: SceneManager(smgr), FileSystem(fs)
{
#ifdef _DEBUG
setDebugName("CIrrMeshFileLoader");
#endif
}
bool CIrrMeshFileLoader::isALoadableFileExtension(const io::path& filename) const
{
return core::hasFileExtension ( filename, "xml", "irrmesh" );
}
IAnimatedMesh* CIrrMeshFileLoader::createMesh(io::IReadFile* file)
{
io::IXMLReader* reader = FileSystem->createXMLReader(file);
if (!reader)
return 0;
const core::stringc meshTagName = "mesh";
IAnimatedMesh* mesh = 0;
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
if (meshTagName == reader->getNodeName())
{
mesh = readMesh(reader);
break;
}
else
skipSection(reader, true);
}
}
reader->drop();
return mesh;
}
IAnimatedMesh* CIrrMeshFileLoader::readMesh(io::IXMLReader* reader)
{
SAnimatedMesh* animatedmesh = new SAnimatedMesh();
SMesh* mesh = new SMesh();
animatedmesh->addMesh(mesh);
mesh->drop();
core::stringc bbSectionName = "boundingBox";
core::stringc bufferSectionName = "buffer";
core::stringc meshSectionName = "mesh";
if (!reader->isEmptyElement())
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
const wchar_t* nodeName = reader->getNodeName();
if (bbSectionName == nodeName)
{
}
else
if (bufferSectionName == nodeName)
{
IMeshBuffer* buffer = readMeshBuffer(reader);
if (buffer)
{
mesh->addMeshBuffer(buffer);
buffer->drop();
}
}
else
skipSection(reader, true);
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
{
if (meshSectionName == reader->getNodeName())
{
break;
}
}
}
mesh->recalculateBoundingBox();
animatedmesh->recalculateBoundingBox();
return animatedmesh;
}
IMeshBuffer* CIrrMeshFileLoader::readMeshBuffer(io::IXMLReader* reader)
{
CDynamicMeshBuffer* buffer = 0;
core::stringc verticesSectionName = "vertices";
core::stringc bbSectionName = "boundingBox";
core::stringc materialSectionName = "material";
core::stringc indicesSectionName = "indices";
core::stringc bufferSectionName = "buffer";
bool insideVertexSection = false;
bool insideIndexSection = false;
int vertexCount = 0;
int indexCount = 0;
video::SMaterial material;
if (!reader->isEmptyElement())
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
const wchar_t* nodeName = reader->getNodeName();
if (bbSectionName == nodeName)
{
}
else
if (materialSectionName == nodeName)
{
io::IAttributes* attributes = FileSystem->createEmptyAttributes(SceneManager->getVideoDriver());
attributes->read(reader, true, L"material");
SceneManager->getVideoDriver()->fillMaterialStructureFromAttributes(material, attributes);
attributes->drop();
}
else
if (verticesSectionName == nodeName)
{
const core::stringc vertexTypeName1 = "standard";
const core::stringc vertexTypeName2 = "2tcoords";
const core::stringc vertexTypeName3 = "tangents";
const wchar_t* vertexType = reader->getAttributeValue(L"type");
vertexCount = reader->getAttributeValueAsInt(L"vertexCount");
insideVertexSection = true;
video::E_INDEX_TYPE itype = (vertexCount > 65536)?irr::video::EIT_32BIT:irr::video::EIT_16BIT;
if (vertexTypeName1 == vertexType)
{
buffer = new CDynamicMeshBuffer(irr::video::EVT_STANDARD, itype);
}
else
if (vertexTypeName2 == vertexType)
{
buffer = new CDynamicMeshBuffer(irr::video::EVT_2TCOORDS, itype);
}
else
if (vertexTypeName3 == vertexType)
{
buffer = new CDynamicMeshBuffer(irr::video::EVT_TANGENTS, itype);
}
buffer->getVertexBuffer().reallocate(vertexCount);
buffer->Material = material;
}
else
if (indicesSectionName == nodeName)
{
indexCount = reader->getAttributeValueAsInt(L"indexCount");
insideIndexSection = true;
}
}
else
if (reader->getNodeType() == io::EXN_TEXT)
{
if (insideVertexSection)
{
readMeshBuffer(reader, vertexCount, buffer);
insideVertexSection = false;
}
else
if (insideIndexSection)
{
readIndices(reader, indexCount, buffer->getIndexBuffer());
insideIndexSection = false;
}
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
{
if (bufferSectionName == reader->getNodeName())
{
break;
}
}
}
if (buffer)
buffer->recalculateBoundingBox();
return buffer;
}
void CIrrMeshFileLoader::readIndices(io::IXMLReader* reader, int indexCount, IIndexBuffer& indices)
{
indices.reallocate(indexCount);
core::stringc data = reader->getNodeData();
const c8* p = &data[0];
for (int i=0; i<indexCount && *p; ++i)
{
findNextNoneWhiteSpace(&p);
indices.push_back(readInt(&p));
}
}
void CIrrMeshFileLoader::readMeshBuffer(io::IXMLReader* reader, int vertexCount, CDynamicMeshBuffer* sbuffer)
{
core::stringc data = reader->getNodeData();
const c8* p = &data[0];
scene::IVertexBuffer& Vertices = sbuffer->getVertexBuffer();
video::E_VERTEX_TYPE vType = Vertices.getType();
if (sbuffer)
{
for (int i=0; i<vertexCount && *p; ++i)
{
switch(vType)
{
case video::EVT_STANDARD:
{
video::S3DVertex vtx;
findNextNoneWhiteSpace(&p);
vtx.Pos.X = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Pos.Y = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Pos.Z = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Normal.X = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Normal.Y = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Normal.Z = readFloat(&p);
u32 col;
findNextNoneWhiteSpace(&p);
sscanf(p, "%08x", &col);
vtx.Color.set(col);
skipCurrentNoneWhiteSpace(&p);
findNextNoneWhiteSpace(&p);
vtx.TCoords.X = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.TCoords.Y = readFloat(&p);
Vertices.push_back(vtx);
}
break;
case video::EVT_2TCOORDS:
{
video::S3DVertex2TCoords vtx;
findNextNoneWhiteSpace(&p);
vtx.Pos.X = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Pos.Y = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Pos.Z = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Normal.X = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Normal.Y = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Normal.Z = readFloat(&p);
u32 col;
findNextNoneWhiteSpace(&p);
sscanf(p, "%08x", &col);
vtx.Color.set(col);
skipCurrentNoneWhiteSpace(&p);
findNextNoneWhiteSpace(&p);
vtx.TCoords.X = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.TCoords.Y = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.TCoords2.X = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.TCoords2.Y = readFloat(&p);
Vertices.push_back(vtx);
}
break;
case video::EVT_TANGENTS:
{
video::S3DVertexTangents vtx;
findNextNoneWhiteSpace(&p);
vtx.Pos.X = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Pos.Y = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Pos.Z = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Normal.X = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Normal.Y = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Normal.Z = readFloat(&p);
u32 col;
findNextNoneWhiteSpace(&p);
sscanf(p, "%08x", &col);
vtx.Color.set(col);
skipCurrentNoneWhiteSpace(&p);
findNextNoneWhiteSpace(&p);
vtx.TCoords.X = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.TCoords.Y = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Tangent.X = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Tangent.Y = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Tangent.Z = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Binormal.X = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Binormal.Y = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Binormal.Z = readFloat(&p);
Vertices.push_back(vtx);
}
break;
};
}
}
}
void CIrrMeshFileLoader::skipSection(io::IXMLReader* reader, bool reportSkipping)
{
#ifdef _DEBUG
os::Printer::log("irrMesh skipping section", core::stringc(reader->getNodeName()).c_str());
#endif
if (reader->isEmptyElement())
return;
u32 tagCounter = 1;
while(tagCounter && reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT &&
!reader->isEmptyElement())
{
#ifdef _DEBUG
if (reportSkipping)
os::Printer::log("irrMesh unknown element:", core::stringc(reader->getNodeName()).c_str());
#endif
++tagCounter;
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
--tagCounter;
}
}
inline f32 CIrrMeshFileLoader::readFloat(const c8** p)
{
f32 ftmp;
*p = core::fast_atof_move(*p, ftmp);
return ftmp;
}
inline s32 CIrrMeshFileLoader::readInt(const c8** p)
{
return (s32)readFloat(p);
}
void CIrrMeshFileLoader::skipCurrentNoneWhiteSpace(const c8** start)
{
const c8* p = *start;
while(*p && !(*p==' ' || *p=='\n' || *p=='\r' || *p=='\t'))
++p;
*start = p;
}
void CIrrMeshFileLoader::findNextNoneWhiteSpace(const c8** start)
{
const c8* p = *start;
while(*p && (*p==' ' || *p=='\n' || *p=='\r' || *p=='\t'))
++p;
*start = p;
}
void CIrrMeshFileLoader::readFloatsInsideElement(io::IXMLReader* reader, f32* floats, u32 count)
{
if (reader->isEmptyElement())
return;
while(reader->read())
{
if (reader->getNodeType() == io::EXN_TEXT)
{
core::stringc data = reader->getNodeData();
const c8* p = &data[0];
for (u32 i=0; i<count; ++i)
{
findNextNoneWhiteSpace(&p);
if (*p)
floats[i] = readFloat(&p);
else
floats[i] = 0.0f;
}
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
break;
}
}
}
}
#endif