#include "IrrCompileConfig.h"
#ifdef _IRR_COMPILE_WITH_COLLADA_LOADER_
#include "CColladaFileLoader.h"
#include "os.h"
#include "IXMLReader.h"
#include "IDummyTransformationSceneNode.h"
#include "SAnimatedMesh.h"
#include "fast_atof.h"
#include "quaternion.h"
#include "ILightSceneNode.h"
#include "ICameraSceneNode.h"
#include "IMeshManipulator.h"
#include "IReadFile.h"
#include "IAttributes.h"
#include "IMeshCache.h"
#include "IMeshSceneNode.h"
#include "SMeshBufferLightMap.h"
#include "irrMap.h"
#ifdef _DEBUG
#define COLLADA_READER_DEBUG
#endif
namespace irr
{
namespace scene
{
namespace
{
const core::stringc colladaSectionName = "COLLADA";
const core::stringc librarySectionName = "library";
const core::stringc libraryNodesSectionName = "library_nodes";
const core::stringc libraryGeometriesSectionName = "library_geometries";
const core::stringc libraryMaterialsSectionName = "library_materials";
const core::stringc libraryImagesSectionName = "library_images";
const core::stringc libraryVisualScenesSectionName = "library_visual_scenes";
const core::stringc libraryCamerasSectionName = "library_cameras";
const core::stringc libraryLightsSectionName = "library_lights";
const core::stringc libraryEffectsSectionName = "library_effects";
const core::stringc assetSectionName = "asset";
const core::stringc sceneSectionName = "scene";
const core::stringc visualSceneSectionName = "visual_scene";
const core::stringc lightPrefabName = "light";
const core::stringc cameraPrefabName = "camera";
const core::stringc materialSectionName = "material";
const core::stringc geometrySectionName = "geometry";
const core::stringc imageSectionName = "image";
const core::stringc textureSectionName = "texture";
const core::stringc effectSectionName = "effect";
const core::stringc pointSectionName = "point";
const core::stringc directionalSectionName ="directional";
const core::stringc spotSectionName = "spot";
const core::stringc ambientSectionName = "ambient";
const core::stringc meshSectionName = "mesh";
const core::stringc sourceSectionName = "source";
const core::stringc arraySectionName = "array";
const core::stringc floatArraySectionName ="float_array";
const core::stringc intArraySectionName = "int_array";
const core::stringc techniqueCommonSectionName = "technique_common";
const core::stringc accessorSectionName = "accessor";
const core::stringc verticesSectionName = "vertices";
const core::stringc inputTagName = "input";
const core::stringc polylistSectionName = "polylist";
const core::stringc trianglesSectionName = "triangles";
const core::stringc polygonsSectionName = "polygons";
const core::stringc primitivesName = "p";
const core::stringc vcountName = "vcount";
const core::stringc upAxisNodeName = "up_axis";
const core::stringc nodeSectionName = "node";
const core::stringc lookatNodeName = "lookat";
const core::stringc matrixNodeName = "matrix";
const core::stringc perspectiveNodeName = "perspective";
const core::stringc rotateNodeName = "rotate";
const core::stringc scaleNodeName = "scale";
const core::stringc translateNodeName = "translate";
const core::stringc skewNodeName = "skew";
const core::stringc bboxNodeName = "boundingbox";
const core::stringc minNodeName = "min";
const core::stringc maxNodeName = "max";
const core::stringc instanceName = "instance";
const core::stringc instanceGeometryName = "instance_geometry";
const core::stringc instanceSceneName = "instance_visual_scene";
const core::stringc instanceEffectName = "instance_effect";
const core::stringc instanceMaterialName = "instance_material";
const core::stringc instanceLightName = "instance_light";
const core::stringc instanceNodeName = "instance_node";
const core::stringc bindMaterialName = "bind_material";
const core::stringc extraNodeName = "extra";
const core::stringc techniqueNodeName = "technique";
const core::stringc colorNodeName = "color";
const core::stringc floatNodeName = "float";
const core::stringc float2NodeName = "float2";
const core::stringc float3NodeName = "float3";
const core::stringc newParamName = "newparam";
const core::stringc paramTagName = "param";
const core::stringc initFromName = "init_from";
const core::stringc dataName = "data";
const core::stringc wrapsName = "wrap_s";
const core::stringc wraptName = "wrap_t";
const core::stringc minfilterName = "minfilter";
const core::stringc magfilterName = "magfilter";
const core::stringc mipfilterName = "mipfilter";
const core::stringc textureNodeName = "texture";
const core::stringc doubleSidedNodeName = "double_sided";
const core::stringc constantAttenuationNodeName = "constant_attenuation";
const core::stringc linearAttenuationNodeName = "linear_attenuation";
const core::stringc quadraticAttenuationNodeName = "quadratic_attenuation";
const core::stringc falloffAngleNodeName = "falloff_angle";
const core::stringc falloffExponentNodeName = "falloff_exponent";
const core::stringc profileCOMMONSectionName = "profile_COMMON";
const core::stringc profileCOMMONAttributeName = "COMMON";
const char* const inputSemanticNames[] = {"POSITION", "VERTEX", "NORMAL", "TEXCOORD",
"UV", "TANGENT", "IMAGE", "TEXTURE", 0};
}
class CPrefab : public IColladaPrefab
{
public:
CPrefab(const core::stringc& id) : Id(id)
{
}
virtual scene::ISceneNode* addInstance(scene::ISceneNode* parent,
scene::ISceneManager* mgr)
{
return 0;
}
virtual const core::stringc& getId()
{
return Id;
}
protected:
core::stringc Id;
};
class CLightPrefab : public CPrefab
{
public:
CLightPrefab(const core::stringc& id) : CPrefab(id)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA: loaded light prefab", Id.c_str());
#endif
}
video::SLight LightData;
virtual scene::ISceneNode* addInstance(scene::ISceneNode* parent,
scene::ISceneManager* mgr)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA: Constructing light instance", Id.c_str());
#endif
scene::ILightSceneNode* l = mgr->addLightSceneNode(parent);
if (l)
{
l->setLightData ( LightData );
l->setName(getId());
}
return l;
}
};
class CGeometryPrefab : public CPrefab
{
public:
CGeometryPrefab(const core::stringc& id) : CPrefab(id)
{
}
scene::IMesh* Mesh;
virtual scene::ISceneNode* addInstance(scene::ISceneNode* parent,
scene::ISceneManager* mgr)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA: Constructing mesh instance", Id.c_str());
#endif
scene::ISceneNode* m = mgr->addMeshSceneNode(Mesh, parent);
if (m)
{
m->setName(getId());
}
return m;
}
};
class CCameraPrefab : public CPrefab
{
public:
CCameraPrefab(const core::stringc& id)
: CPrefab(id), YFov(core::PI / 2.5f), ZNear(1.0f), ZFar(3000.0f)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA: loaded camera prefab", Id.c_str());
#endif
}
f32 YFov;
f32 ZNear;
f32 ZFar;
virtual scene::ISceneNode* addInstance(scene::ISceneNode* parent,
scene::ISceneManager* mgr)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA: Constructing camera instance", Id.c_str());
#endif
scene::ICameraSceneNode* c = mgr->addCameraSceneNode(parent);
if (c)
{
c->setFOV(YFov);
c->setNearValue(ZNear);
c->setFarValue(ZFar);
c->setName(getId());
}
return c;
}
};
class CScenePrefab : public CPrefab
{
public:
CScenePrefab(const core::stringc& id) : CPrefab(id)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA: loaded scene prefab", Id.c_str());
#endif
}
virtual scene::ISceneNode* addInstance(scene::ISceneNode* parent,
scene::ISceneManager* mgr)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA: Constructing scene instance", Id.c_str());
#endif
if (Children.size()==0)
return 0;
scene::IDummyTransformationSceneNode* s = mgr->addDummyTransformationSceneNode(parent);
if (s)
{
s->setName(getId());
s->getRelativeTransformationMatrix() = Transformation;
core::stringc t;
for (u32 i=0; i<16; ++i)
{
t+=core::stringc(Transformation[i]);
t+=" ";
}
os::Printer::log("COLLADA: Transformation", t.c_str());
for (u32 i=0; i<Children.size(); ++i)
Children[i]->addInstance(s, mgr);
}
return s;
}
core::array<IColladaPrefab*> Children;
core::matrix4 Transformation;
};
CColladaFileLoader::CColladaFileLoader(scene::ISceneManager* smgr,
io::IFileSystem* fs)
: SceneManager(smgr), FileSystem(fs), DummyMesh(0),
FirstLoadedMesh(0), LoadedMeshCount(0), CreateInstances(false)
{
#ifdef _DEBUG
setDebugName("CColladaFileLoader");
#endif
}
CColladaFileLoader::~CColladaFileLoader()
{
if (DummyMesh)
DummyMesh->drop();
if (FirstLoadedMesh)
FirstLoadedMesh->drop();
}
bool CColladaFileLoader::isALoadableFileExtension(const io::path& filename) const
{
return core::hasFileExtension ( filename, "xml", "dae" );
}
IAnimatedMesh* CColladaFileLoader::createMesh(io::IReadFile* file)
{
io::IXMLReaderUTF8* reader = FileSystem->createXMLReaderUTF8(file);
if (!reader)
return 0;
CurrentlyLoadingMesh = file->getFileName();
CreateInstances = SceneManager->getParameters()->getAttributeAsBool(
scene::COLLADA_CREATE_SCENE_INSTANCES);
Version = 0;
FlipAxis = false;
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
if (colladaSectionName == reader->getNodeName())
readColladaSection(reader);
else
skipSection(reader, true);
}
}
reader->drop();
if (!Version)
return 0;
if (!DummyMesh)
DummyMesh = new SAnimatedMesh();
scene::IAnimatedMesh* returnMesh = DummyMesh;
if (Version < 10400)
instantiateNode(SceneManager->getRootSceneNode());
if (LoadedMeshCount>1 && FirstLoadedMesh)
{
os::Printer::log("Added COLLADA mesh", FirstLoadedMeshName.c_str());
SceneManager->getMeshCache()->addMesh(FirstLoadedMeshName.c_str(), FirstLoadedMesh);
}
clearData();
returnMesh->grab();
DummyMesh->drop();
DummyMesh = 0;
if (FirstLoadedMesh)
FirstLoadedMesh->drop();
FirstLoadedMesh = 0;
LoadedMeshCount = 0;
return returnMesh;
}
void CColladaFileLoader::skipSection(io::IXMLReaderUTF8* reader, bool reportSkipping)
{
#ifndef COLLADA_READER_DEBUG
if (reportSkipping)
#endif
os::Printer::log("COLLADA skipping section", core::stringc(reader->getNodeName()).c_str());
if (reader->isEmptyElement())
return;
u32 tagCounter = 1;
while(tagCounter && reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT &&
!reader->isEmptyElement())
{
#ifdef COLLADA_READER_DEBUG
if (reportSkipping)
os::Printer::log("Skipping COLLADA unknown element", core::stringc(reader->getNodeName()).c_str());
#endif
++tagCounter;
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
--tagCounter;
}
}
void CColladaFileLoader::readColladaSection(io::IXMLReaderUTF8* reader)
{
if (reader->isEmptyElement())
return;
const f32 version = core::fast_atof(core::stringc(reader->getAttributeValue("version")).c_str());
Version = core::floor32(version)*10000+core::round32(core::fract(version)*1000.0f);
while(reader->read())
if (reader->getNodeType() == io::EXN_ELEMENT)
{
if (assetSectionName == reader->getNodeName())
readAssetSection(reader);
else
if (librarySectionName == reader->getNodeName())
readLibrarySection(reader);
else
if (libraryNodesSectionName == reader->getNodeName())
readLibrarySection(reader);
else
if (libraryGeometriesSectionName == reader->getNodeName())
readLibrarySection(reader);
else
if (libraryMaterialsSectionName == reader->getNodeName())
readLibrarySection(reader);
else
if (libraryEffectsSectionName == reader->getNodeName())
readLibrarySection(reader);
else
if (libraryImagesSectionName == reader->getNodeName())
readLibrarySection(reader);
else
if (libraryCamerasSectionName == reader->getNodeName())
readLibrarySection(reader);
else
if (libraryLightsSectionName == reader->getNodeName())
readLibrarySection(reader);
else
if (libraryVisualScenesSectionName == reader->getNodeName())
readVisualSceneLibrary(reader);
else
if (assetSectionName == reader->getNodeName())
readAssetSection(reader);
else
if (sceneSectionName == reader->getNodeName())
readSceneSection(reader);
else
{
os::Printer::log("COLLADA loader warning: Wrong tag usage found", reader->getNodeName(), ELL_WARNING);
skipSection(reader, true);
}
}
}
void CColladaFileLoader::readLibrarySection(io::IXMLReaderUTF8* reader)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading library");
#endif
if (reader->isEmptyElement())
return;
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
if (cameraPrefabName == reader->getNodeName())
readCameraPrefab(reader);
else
if (geometrySectionName == reader->getNodeName())
readGeometry(reader);
else
if (imageSectionName == reader->getNodeName())
readImage(reader);
else
if (lightPrefabName == reader->getNodeName())
readLightPrefab(reader);
else
if (materialSectionName == reader->getNodeName())
readMaterial(reader);
else
if (nodeSectionName == reader->getNodeName())
{
CScenePrefab p("");
readNodeSection(reader, SceneManager->getRootSceneNode(), &p);
}
else
if (effectSectionName == reader->getNodeName())
readEffect(reader);
else
if (textureSectionName == reader->getNodeName())
readTexture(reader);
else
skipSection(reader, true);
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
{
if (librarySectionName == reader->getNodeName())
break;
if (libraryNodesSectionName == reader->getNodeName())
break;
if (libraryGeometriesSectionName == reader->getNodeName())
break;
if (libraryMaterialsSectionName == reader->getNodeName())
break;
if (libraryEffectsSectionName == reader->getNodeName())
break;
if (libraryImagesSectionName == reader->getNodeName())
break;
if (libraryLightsSectionName == reader->getNodeName())
break;
if (libraryCamerasSectionName == reader->getNodeName())
break;
}
}
}
void CColladaFileLoader::readVisualSceneLibrary(io::IXMLReaderUTF8* reader)
{
CScenePrefab* p = 0;
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
if (visualSceneSectionName == reader->getNodeName())
p = new CScenePrefab(readId(reader));
else
if (p && nodeSectionName == reader->getNodeName())
readNodeSection(reader, SceneManager->getRootSceneNode(), p);
else
if (assetSectionName == reader->getNodeName())
readAssetSection(reader);
else
if (extraNodeName == reader->getNodeName())
skipSection(reader, false);
else
{
os::Printer::log("COLLADA loader warning: Wrong tag usage found", reader->getNodeName(), ELL_WARNING);
skipSection(reader, true);
}
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
{
if (libraryVisualScenesSectionName == reader->getNodeName())
return;
else
if ((visualSceneSectionName == reader->getNodeName()) && p)
{
Prefabs.push_back(p);
p = 0;
}
}
}
}
void CColladaFileLoader::readSceneSection(io::IXMLReaderUTF8* reader)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading scene");
#endif
if (reader->isEmptyElement())
return;
core::matrix4 transform;
core::aabbox3df bbox;
scene::IDummyTransformationSceneNode* node = 0;
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
if (lookatNodeName == reader->getNodeName())
transform *= readLookAtNode(reader);
else
if (matrixNodeName == reader->getNodeName())
transform *= readMatrixNode(reader);
else
if (perspectiveNodeName == reader->getNodeName())
transform *= readPerspectiveNode(reader);
else
if (rotateNodeName == reader->getNodeName())
transform *= readRotateNode(reader);
else
if (scaleNodeName == reader->getNodeName())
transform *= readScaleNode(reader);
else
if (skewNodeName == reader->getNodeName())
transform *= readSkewNode(reader);
else
if (translateNodeName == reader->getNodeName())
transform *= readTranslateNode(reader);
else
if (bboxNodeName == reader->getNodeName())
readBboxNode(reader, bbox);
else
if (nodeSectionName == reader->getNodeName())
{
if (!node)
node = SceneManager->addDummyTransformationSceneNode(SceneManager->getRootSceneNode());
readNodeSection(reader, node);
}
else
if ((instanceSceneName == reader->getNodeName()))
readInstanceNode(reader, SceneManager->getRootSceneNode(), 0, 0,instanceSceneName);
else
if (extraNodeName == reader->getNodeName())
skipSection(reader, false);
else
{
os::Printer::log("COLLADA loader warning: Wrong tag usage found", reader->getNodeName(), ELL_WARNING);
skipSection(reader, true);
}
}
else
if ((reader->getNodeType() == io::EXN_ELEMENT_END) &&
(sceneSectionName == reader->getNodeName()))
return;
}
if (node)
node->getRelativeTransformationMatrix() = transform;
}
void CColladaFileLoader::readAssetSection(io::IXMLReaderUTF8* reader)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading asset");
#endif
if (reader->isEmptyElement())
return;
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
if (upAxisNodeName == reader->getNodeName())
{
reader->read();
FlipAxis = (core::stringc("Z_UP") == reader->getNodeData());
}
}
else
if ((reader->getNodeType() == io::EXN_ELEMENT_END) &&
(assetSectionName == reader->getNodeName()))
return;
}
}
void CColladaFileLoader::readNodeSection(io::IXMLReaderUTF8* reader, scene::ISceneNode* parent, CScenePrefab* p)
{
if (reader->isEmptyElement())
{
return;
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading empty node");
#endif
}
core::stringc name = readId(reader);
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading node", name);
#endif
core::matrix4 transform;
core::aabbox3df bbox;
scene::ISceneNode* node = 0;
CScenePrefab* nodeprefab = 0;
if (p)
{
nodeprefab = new CScenePrefab(readId(reader));
p->Children.push_back(nodeprefab);
Prefabs.push_back(nodeprefab);
}
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
if (assetSectionName == reader->getNodeName())
readAssetSection(reader);
else
if (lookatNodeName == reader->getNodeName())
transform *= readLookAtNode(reader);
else
if (matrixNodeName == reader->getNodeName())
transform *= readMatrixNode(reader);
else
if (perspectiveNodeName == reader->getNodeName())
transform *= readPerspectiveNode(reader);
else
if (rotateNodeName == reader->getNodeName())
transform *= readRotateNode(reader);
else
if (scaleNodeName == reader->getNodeName())
transform *= readScaleNode(reader);
else
if (skewNodeName == reader->getNodeName())
transform *= readSkewNode(reader);
else
if (translateNodeName == reader->getNodeName())
transform *= readTranslateNode(reader);
else
if (bboxNodeName == reader->getNodeName())
readBboxNode(reader, bbox);
else
if ((instanceName == reader->getNodeName()) ||
(instanceNodeName == reader->getNodeName()) ||
(instanceGeometryName == reader->getNodeName()) ||
(instanceLightName == reader->getNodeName()))
{
scene::ISceneNode* newnode = 0;
readInstanceNode(reader, parent, &newnode, nodeprefab, reader->getNodeName());
if (node && newnode)
{
ISceneNodeList::ConstIterator it = node->getChildren().begin();
for (; it != node->getChildren().end(); it = node->getChildren().begin())
(*it)->setParent(newnode);
node->remove();
node = newnode;
}
}
else
if (nodeSectionName == reader->getNodeName())
{
if (CreateInstances && !node)
{
scene::IDummyTransformationSceneNode* dummy =
SceneManager->addDummyTransformationSceneNode(parent);
dummy->getRelativeTransformationMatrix() = transform;
node = dummy;
}
else
node = parent;
readNodeSection(reader, node, nodeprefab);
}
else
if (extraNodeName == reader->getNodeName())
skipSection(reader, false);
else
skipSection(reader, true);
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
{
if (nodeSectionName == reader->getNodeName())
break;
}
}
if (nodeprefab)
nodeprefab->Transformation = transform;
else
if (node)
{
node->setPosition(transform.getTranslation());
node->setRotation(transform.getRotationDegrees());
node->setScale(transform.getScale());
node->updateAbsolutePosition();
node->setName(name);
}
}
core::matrix4 CColladaFileLoader::readLookAtNode(io::IXMLReaderUTF8* reader)
{
core::matrix4 mat;
if (reader->isEmptyElement())
return mat;
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading look at node");
#endif
f32 floats[9];
readFloatsInsideElement(reader, floats, 9);
mat.buildCameraLookAtMatrixLH(
core::vector3df(floats[0], floats[1], floats[2]),
core::vector3df(floats[3], floats[4], floats[5]),
core::vector3df(floats[6], floats[7], floats[8]));
return mat;
}
core::matrix4 CColladaFileLoader::readSkewNode(io::IXMLReaderUTF8* reader)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading skew node");
#endif
core::matrix4 mat;
if (reader->isEmptyElement())
return mat;
f32 floats[7];
readFloatsInsideElement(reader, floats, 7);
core::quaternion q;
q.fromAngleAxis(floats[0]*core::DEGTORAD, core::vector3df(floats[1], floats[2], floats[3]));
mat = q.getMatrix();
if (floats[4]==1.f)
{
mat[4]=0.f;
mat[6]=0.f;
mat[8]=0.f;
mat[9]=0.f;
}
else
if (floats[5]==1.f)
{
mat[1]=0.f;
mat[2]=0.f;
mat[8]=0.f;
mat[9]=0.f;
}
else
if (floats[6]==1.f)
{
mat[1]=0.f;
mat[2]=0.f;
mat[4]=0.f;
mat[6]=0.f;
}
return mat;
}
void CColladaFileLoader::readBboxNode(io::IXMLReaderUTF8* reader,
core::aabbox3df& bbox)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading boundingbox node");
#endif
bbox.reset(core::aabbox3df());
if (reader->isEmptyElement())
return;
f32 floats[3];
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
if (minNodeName == reader->getNodeName())
{
readFloatsInsideElement(reader, floats, 3);
bbox.MinEdge.set(floats[0], floats[1], floats[2]);
}
else
if (maxNodeName == reader->getNodeName())
{
readFloatsInsideElement(reader, floats, 3);
bbox.MaxEdge.set(floats[0], floats[1], floats[2]);
}
else
skipSection(reader, true);
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
{
if (bboxNodeName == reader->getNodeName())
break;
}
}
}
core::matrix4 CColladaFileLoader::readMatrixNode(io::IXMLReaderUTF8* reader)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading matrix node");
#endif
core::matrix4 mat;
if (reader->isEmptyElement())
return mat;
readFloatsInsideElement(reader, mat.pointer(), 16);
if (FlipAxis)
{
core::matrix4 mat2(mat, core::matrix4::EM4CONST_TRANSPOSED);
mat2[1]=mat[8];
mat2[2]=mat[4];
mat2[4]=mat[2];
mat2[5]=mat[10];
mat2[6]=mat[6];
mat2[8]=mat[1];
mat2[9]=mat[9];
mat2[10]=mat[5];
mat2[12]=mat[3];
mat2[13]=mat[11];
mat2[14]=mat[7];
return mat2;
}
else
return core::matrix4(mat, core::matrix4::EM4CONST_TRANSPOSED);
}
core::matrix4 CColladaFileLoader::readPerspectiveNode(io::IXMLReaderUTF8* reader)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading perspective node");
#endif
core::matrix4 mat;
if (reader->isEmptyElement())
return mat;
f32 floats[1];
readFloatsInsideElement(reader, floats, 1);
os::Printer::log("COLLADA loader warning: <perspective> not implemented yet.", ELL_WARNING);
return mat;
}
core::matrix4 CColladaFileLoader::readRotateNode(io::IXMLReaderUTF8* reader)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading rotate node");
#endif
core::matrix4 mat;
if (reader->isEmptyElement())
return mat;
f32 floats[4];
readFloatsInsideElement(reader, floats, 4);
if (!core::iszero(floats[3]))
{
core::quaternion q;
if (FlipAxis)
q.fromAngleAxis(floats[3]*core::DEGTORAD, core::vector3df(floats[0], floats[2], floats[1]));
else
q.fromAngleAxis(floats[3]*core::DEGTORAD, core::vector3df(floats[0], floats[1], floats[2]));
return q.getMatrix();
}
else
return core::IdentityMatrix;
}
core::matrix4 CColladaFileLoader::readScaleNode(io::IXMLReaderUTF8* reader)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading scale node");
#endif
core::matrix4 mat;
if (reader->isEmptyElement())
return mat;
f32 floats[3];
readFloatsInsideElement(reader, floats, 3);
if (FlipAxis)
mat.setScale(core::vector3df(floats[0], floats[2], floats[1]));
else
mat.setScale(core::vector3df(floats[0], floats[1], floats[2]));
return mat;
}
core::matrix4 CColladaFileLoader::readTranslateNode(io::IXMLReaderUTF8* reader)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading translate node");
#endif
core::matrix4 mat;
if (reader->isEmptyElement())
return mat;
f32 floats[3];
readFloatsInsideElement(reader, floats, 3);
if (FlipAxis)
mat.setTranslation(core::vector3df(floats[0], floats[2], floats[1]));
else
mat.setTranslation(core::vector3df(floats[0], floats[1], floats[2]));
return mat;
}
void CColladaFileLoader::readInstanceNode(io::IXMLReaderUTF8* reader,
scene::ISceneNode* parent, scene::ISceneNode** outNode,
CScenePrefab* p, const core::stringc& type)
{
core::stringc url = reader->getAttributeValue("url");
uriToId(url);
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading instance", url);
#endif
if (!reader->isEmptyElement())
{
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
if (bindMaterialName == reader->getNodeName())
readBindMaterialSection(reader,url);
else
if (extraNodeName == reader->getNodeName())
skipSection(reader, false);
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
break;
}
}
instantiateNode(parent, outNode, p, url, type);
}
void CColladaFileLoader::instantiateNode(scene::ISceneNode* parent,
scene::ISceneNode** outNode, CScenePrefab* p, const core::stringc& url,
const core::stringc& type)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA instantiate node");
#endif
for (u32 i=0; i<Prefabs.size(); ++i)
{
if (url == "" || url == Prefabs[i]->getId())
{
if (p)
p->Children.push_back(Prefabs[i]);
else
if (CreateInstances)
{
scene::ISceneNode * newNode
= Prefabs[i]->addInstance(parent, SceneManager);
if (outNode)
{
*outNode = newNode;
if (*outNode)
(*outNode)->setName(url);
}
}
return;
}
}
if (p)
{
if (instanceGeometryName==type)
{
Prefabs.push_back(new CGeometryPrefab(url));
p->Children.push_back(Prefabs.getLast());
}
}
}
void CColladaFileLoader::readCameraPrefab(io::IXMLReaderUTF8* reader)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading camera prefab");
#endif
CCameraPrefab* prefab = new CCameraPrefab(readId(reader));
if (!reader->isEmptyElement())
{
readColladaParameters(reader, cameraPrefabName);
SColladaParam* p;
p = getColladaParameter(ECPN_YFOV);
if (p && p->Type == ECPT_FLOAT)
prefab->YFov = p->Floats[0];
p = getColladaParameter(ECPN_ZNEAR);
if (p && p->Type == ECPT_FLOAT)
prefab->ZNear = p->Floats[0];
p = getColladaParameter(ECPN_ZFAR);
if (p && p->Type == ECPT_FLOAT)
prefab->ZFar = p->Floats[0];
}
Prefabs.push_back(prefab);
}
void CColladaFileLoader::readImage(io::IXMLReaderUTF8* reader)
{
Images.push_back(SColladaImage());
SColladaImage& image=Images.getLast();
image.Id = readId(reader);
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading image", core::stringc(image.Id));
#endif
image.Dimension.Height = (u32)reader->getAttributeValueAsInt("height");
image.Dimension.Width = (u32)reader->getAttributeValueAsInt("width");
if (Version >= 10400)
{
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
if (assetSectionName == reader->getNodeName())
skipSection(reader, false);
else
if (initFromName == reader->getNodeName())
{
reader->read();
image.Source = reader->getNodeData();
image.Source.trim();
image.SourceIsFilename=true;
}
else
if (dataName == reader->getNodeName())
{
reader->read();
image.Source = reader->getNodeData();
image.Source.trim();
image.SourceIsFilename=false;
}
else
if (extraNodeName == reader->getNodeName())
skipSection(reader, false);
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
{
if (initFromName == reader->getNodeName())
return;
}
}
}
else
{
image.Source = reader->getAttributeValue("source");
image.Source.trim();
image.SourceIsFilename=false;
}
}
void CColladaFileLoader::readTexture(io::IXMLReaderUTF8* reader)
{
Textures.push_back(SColladaTexture());
SColladaTexture& texture=Textures.getLast();
texture.Id = readId(reader);
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading texture", core::stringc(texture.Id));
#endif
if (!reader->isEmptyElement())
{
readColladaInputs(reader, textureSectionName);
SColladaInput* input = getColladaInput(ECIS_IMAGE);
if (input)
{
const core::stringc imageName = input->Source;
texture.Texture = getTextureFromImage(imageName);
}
}
}
void CColladaFileLoader::readMaterial(io::IXMLReaderUTF8* reader)
{
Materials.push_back(SColladaMaterial());
SColladaMaterial& material = Materials.getLast();
material.Id = readId(reader);
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading material", core::stringc(material.Id));
#endif
if (Version >= 10400)
{
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT &&
instanceEffectName == reader->getNodeName())
{
material.InstanceEffectId = reader->getAttributeValue("url");
uriToId(material.InstanceEffectId);
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END &&
materialSectionName == reader->getNodeName())
{
break;
}
}
}
else
{
if (!reader->isEmptyElement())
{
readColladaInputs(reader, materialSectionName);
SColladaInput* input = getColladaInput(ECIS_TEXTURE);
if (input)
{
core::stringc textureName = input->Source;
uriToId(textureName);
for (u32 i=0; i<Textures.size(); ++i)
if (textureName == Textures[i].Id)
{
material.Mat.setTexture(0, Textures[i].Texture);
break;
}
}
#if 0
readColladaParameters(reader, materialSectionName);
SColladaParam* p;
p = getColladaParameter(ECPN_AMBIENT);
if (p && p->Type == ECPT_FLOAT3)
material.Mat.AmbientColor = video::SColorf(p->Floats[0],p->Floats[1],p->Floats[2]).toSColor();
p = getColladaParameter(ECPN_DIFFUSE);
if (p && p->Type == ECPT_FLOAT3)
material.Mat.DiffuseColor = video::SColorf(p->Floats[0],p->Floats[1],p->Floats[2]).toSColor();
p = getColladaParameter(ECPN_SPECULAR);
if (p && p->Type == ECPT_FLOAT3)
material.Mat.DiffuseColor = video::SColorf(p->Floats[0],p->Floats[1],p->Floats[2]).toSColor();
p = getColladaParameter(ECPN_SHININESS);
if (p && p->Type == ECPT_FLOAT)
material.Mat.Shininess = p->Floats[0];
#endif
}
}
}
void CColladaFileLoader::readEffect(io::IXMLReaderUTF8* reader, SColladaEffect * effect)
{
static const core::stringc constantNode("constant");
static const core::stringc lambertNode("lambert");
static const core::stringc phongNode("phong");
static const core::stringc blinnNode("blinn");
static const core::stringc emissionNode("emission");
static const core::stringc ambientNode("ambient");
static const core::stringc diffuseNode("diffuse");
static const core::stringc specularNode("specular");
static const core::stringc shininessNode("shininess");
static const core::stringc reflectiveNode("reflective");
static const core::stringc reflectivityNode("reflectivity");
static const core::stringc transparentNode("transparent");
static const core::stringc transparencyNode("transparency");
static const core::stringc indexOfRefractionNode("index_of_refraction");
if (!effect)
{
Effects.push_back(SColladaEffect());
effect = &Effects.getLast();
effect->Id = readId(reader);
effect->Transparency = 1.f;
effect->Mat.Lighting=true;
effect->Mat.NormalizeNormals=true;
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading effect", core::stringc(effect->Id));
#endif
}
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
if (!reader->isEmptyElement() && ((profileCOMMONSectionName == reader->getNodeName()) ||
(techniqueNodeName == reader->getNodeName())))
readEffect(reader,effect);
else
if (newParamName == reader->getNodeName())
readParameter(reader);
else
if (constantNode == reader->getNodeName() ||
lambertNode == reader->getNodeName() ||
phongNode == reader->getNodeName() ||
blinnNode == reader->getNodeName())
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading effect part", reader->getNodeName());
#endif
effect->Mat.setFlag(irr::video::EMF_GOURAUD_SHADING,
phongNode == reader->getNodeName() ||
blinnNode == reader->getNodeName());
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
const core::stringc node = reader->getNodeName();
if (emissionNode == node || ambientNode == node ||
diffuseNode == node || specularNode == node ||
reflectiveNode == node || transparentNode == node )
{
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT &&
colorNodeName == reader->getNodeName())
{
const video::SColorf colorf = readColorNode(reader);
const video::SColor color = colorf.toSColor();
if (emissionNode == node)
effect->Mat.EmissiveColor = color;
else
if (ambientNode == node)
effect->Mat.AmbientColor = color;
else
if (diffuseNode == node)
effect->Mat.DiffuseColor = color;
else
if (specularNode == node)
effect->Mat.SpecularColor = color;
else
if (transparentNode == node)
effect->Transparency = colorf.getAlpha();
}
else
if (reader->getNodeType() == io::EXN_ELEMENT &&
textureNodeName == reader->getNodeName())
{
effect->Textures.push_back(reader->getAttributeValue("texture"));
break;
}
else
if (reader->getNodeType() == io::EXN_ELEMENT)
skipSection(reader, false);
else
if (reader->getNodeType() == io::EXN_ELEMENT_END &&
node == reader->getNodeName())
break;
}
}
else
if (shininessNode == node || reflectivityNode == node ||
transparencyNode == node || indexOfRefractionNode == node )
{
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT &&
floatNodeName == reader->getNodeName())
{
f32 f = readFloatNode(reader);
if (shininessNode == node)
effect->Mat.Shininess = f;
else
if (transparencyNode == node)
effect->Transparency *= f;
}
else
if (reader->getNodeType() == io::EXN_ELEMENT)
skipSection(reader, false);
else
if (reader->getNodeType() == io::EXN_ELEMENT_END &&
node == reader->getNodeName())
break;
}
}
else
skipSection(reader, true);
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END && (
constantNode == reader->getNodeName() ||
lambertNode == reader->getNodeName() ||
phongNode == reader->getNodeName() ||
blinnNode == reader->getNodeName()
))
break;
}
}
else
if (!reader->isEmptyElement() && (extraNodeName == reader->getNodeName()))
readEffect(reader,effect);
else
if (doubleSidedNodeName == reader->getNodeName())
{
s32 doubleSided = 0;
readIntsInsideElement(reader,&doubleSided,1);
if (doubleSided)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("Setting double sided flag for effect.");
#endif
effect->Mat.setFlag(irr::video::EMF_BACK_FACE_CULLING,false);
}
}
else
skipSection(reader, true);
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
{
if (effectSectionName == reader->getNodeName())
break;
else
if (profileCOMMONSectionName == reader->getNodeName())
break;
else
if (techniqueNodeName == reader->getNodeName())
break;
else
if (extraNodeName == reader->getNodeName())
break;
}
}
if (effect->Mat.AmbientColor == video::SColor(0) &&
effect->Mat.DiffuseColor != video::SColor(0))
effect->Mat.AmbientColor = effect->Mat.DiffuseColor;
if (effect->Mat.DiffuseColor == video::SColor(0) &&
effect->Mat.AmbientColor != video::SColor(0))
effect->Mat.DiffuseColor = effect->Mat.AmbientColor;
if ((effect->Transparency != 0.0f) && (effect->Transparency != 1.0f))
{
effect->Mat.MaterialType = irr::video::EMT_TRANSPARENT_VERTEX_ALPHA;
effect->Mat.ZWriteEnable = false;
}
effect->Mat.setFlag(video::EMF_TEXTURE_WRAP, !Parameters.getAttributeAsBool("wrap_s"));
effect->Mat.setFlag(video::EMF_BILINEAR_FILTER, Parameters.getAttributeAsBool("bilinear"));
effect->Mat.setFlag(video::EMF_TRILINEAR_FILTER, Parameters.getAttributeAsBool("trilinear"));
effect->Mat.setFlag(video::EMF_ANISOTROPIC_FILTER, Parameters.getAttributeAsBool("anisotropic"));
}
const SColladaMaterial* CColladaFileLoader::findMaterial(const core::stringc& materialName)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA find material", materialName);
#endif
SColladaMaterial matToFind;
matToFind.Id = materialName;
s32 mat = Materials.binary_search(matToFind);
if (mat == -1)
return 0;
if (Materials[mat].InstanceEffectId.size() != 0)
{
SColladaEffect effectToFind;
effectToFind.Id = Materials[mat].InstanceEffectId;
s32 effect = Effects.binary_search(effectToFind);
if (effect != -1)
{
Materials[mat].Mat = Effects[effect].Mat;
if (Effects[effect].Textures.size())
Materials[mat].Mat.setTexture(0, getTextureFromImage(Effects[effect].Textures[0]));
Materials[mat].Transparency = Effects[effect].Transparency;
Materials[mat].InstanceEffectId = "";
}
else
return 0;
}
return &Materials[mat];
}
void CColladaFileLoader::readBindMaterialSection(io::IXMLReaderUTF8* reader, const core::stringc & id)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading bind material");
#endif
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
if (instanceMaterialName == reader->getNodeName())
{
core::stringc meshbufferReference = reader->getAttributeValue("symbol");
if (meshbufferReference.size()==0)
continue;
core::stringc target = reader->getAttributeValue("target");
uriToId(target);
if (target.size()==0)
continue;
const SColladaMaterial * material = findMaterial(target);
if (!material)
continue;
meshbufferReference = id+"/"+meshbufferReference;
#ifdef COLLADA_READER_DEBUG
os::Printer::log((core::stringc("Material binding: ")+meshbufferReference+" "+target).c_str());
#endif
if (MaterialsToBind.find(meshbufferReference))
{
core::array<irr::scene::IMeshBuffer*> & toBind
= MeshesToBind[MaterialsToBind[meshbufferReference]];
#ifdef COLLADA_READER_DEBUG
os::Printer::log("Material binding now ",material->Id.c_str());
os::Printer::log("#meshbuffers",core::stringc(toBind.size()).c_str());
#endif
SMesh tmpmesh;
for (u32 i = 0; i < toBind.size(); ++i)
{
toBind[i]->getMaterial() = material->Mat;
tmpmesh.addMeshBuffer(toBind[i]);
if ((material->Transparency!=0.0f) && (material->Transparency!=1.0f))
{
toBind[i]->getMaterial().MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
toBind[i]->getMaterial().ZWriteEnable = false;
}
}
SceneManager->getMeshManipulator()->setVertexColors(&tmpmesh,material->Mat.DiffuseColor);
if ((material->Transparency!=0.0f) && (material->Transparency!=1.0f))
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA found transparency material", core::stringc(material->Transparency).c_str());
#endif
SceneManager->getMeshManipulator()->setVertexColorAlpha(&tmpmesh, core::floor32(material->Transparency*255.0f));
}
}
}
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END &&
bindMaterialName == reader->getNodeName())
break;
}
}
void CColladaFileLoader::readGeometry(io::IXMLReaderUTF8* reader)
{
core::stringc id = readId(reader);
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading geometry", id);
#endif
SAnimatedMesh* amesh = new SAnimatedMesh();
scene::SMesh* mesh = new SMesh();
amesh->addMesh(mesh);
core::array<SSource> sources;
bool okToReadArray = false;
if (!reader->isEmptyElement())
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
const char* nodeName = reader->getNodeName();
if (meshSectionName == nodeName)
{
}
else
if (sourceSectionName == nodeName)
{
sources.push_back(SSource());
sources.getLast().Id = readId(reader);
#ifdef COLLADA_READER_DEBUG
os::Printer::log("Reading source", sources.getLast().Id.c_str());
#endif
}
else
if (arraySectionName == nodeName || floatArraySectionName == nodeName || intArraySectionName == nodeName)
{
if (!sources.empty())
{
sources.getLast().Array.Name = readId(reader);
int count = reader->getAttributeValueAsInt("count");
sources.getLast().Array.Data.set_used(count);
const char* type = reader->getAttributeValue("type");
okToReadArray = (type && (!strcmp("float", type) || !strcmp("int", type))) || floatArraySectionName == nodeName || intArraySectionName == nodeName;
#ifdef COLLADA_READER_DEBUG
os::Printer::log("Read array", sources.getLast().Array.Name.c_str());
#endif
}
#ifdef COLLADA_READER_DEBUG
else
os::Printer::log("Warning, array outside source found",
readId(reader).c_str());
#endif
}
else
if (accessorSectionName == nodeName)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("Reading accessor");
#endif
SAccessor accessor;
accessor.Count = reader->getAttributeValueAsInt("count");
accessor.Offset = reader->getAttributeValueAsInt("offset");
accessor.Stride = reader->getAttributeValueAsInt("stride");
if (accessor.Stride == 0)
accessor.Stride = 1;
readColladaParameters(reader, accessorSectionName);
if (!sources.empty())
{
sources.getLast().Accessors.push_back(accessor);
sources.getLast().Accessors.getLast().Parameters = ColladaParameters;
}
}
else
if (verticesSectionName == nodeName)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("Reading vertices");
#endif
readColladaInputs(reader, verticesSectionName);
}
else
if (polygonsSectionName == nodeName ||
polylistSectionName == nodeName ||
trianglesSectionName == nodeName)
{
readPolygonSection(reader, sources, mesh, id);
}
else
if (doubleSidedNodeName == reader->getNodeName())
{
s32 doubleSided = 0;
readIntsInsideElement(reader,&doubleSided,1);
if (doubleSided)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("Setting double sided flag for mesh.");
#endif
amesh->setMaterialFlag(irr::video::EMF_BACK_FACE_CULLING,false);
}
}
else
if ((techniqueCommonSectionName != nodeName)
&& (techniqueNodeName != nodeName)
&& (extraNodeName != nodeName))
{
os::Printer::log("COLLADA loader warning: Wrong tag usage found in geometry", reader->getNodeName(), ELL_WARNING);
skipSection(reader, true);
}
}
else
if (reader->getNodeType() == io::EXN_TEXT)
{
if (okToReadArray && !sources.empty())
{
core::array<f32>& a = sources.getLast().Array.Data;
core::stringc data = reader->getNodeData();
data.trim();
const c8* p = &data[0];
for (u32 i=0; i<a.size(); ++i)
{
findNextNoneWhiteSpace(&p);
if (*p)
a[i] = readFloat(&p);
else
a[i] = 0.0f;
}
}
okToReadArray = false;
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
{
if (geometrySectionName == reader->getNodeName())
{
break;
}
}
}
mesh->recalculateBoundingBox();
amesh->recalculateBoundingBox();
io::path filename = CurrentlyLoadingMesh;
filename += '#';
filename += id;
if (LoadedMeshCount)
{
SceneManager->getMeshCache()->addMesh(filename.c_str(), amesh);
os::Printer::log("Added COLLADA mesh", filename.c_str());
}
else
{
FirstLoadedMeshName = filename;
FirstLoadedMesh = amesh;
FirstLoadedMesh->grab();
}
++LoadedMeshCount;
mesh->drop();
amesh->drop();
u32 i;
for (i=0; i<Prefabs.size(); ++i)
{
if (Prefabs[i]->getId()==id)
{
((CGeometryPrefab*)Prefabs[i])->Mesh=mesh;
break;
}
}
if (i==Prefabs.size())
{
CGeometryPrefab* prefab = new CGeometryPrefab(id);
prefab->Mesh = mesh;
Prefabs.push_back(prefab);
}
if (!CreateInstances && !DummyMesh)
{
DummyMesh = amesh;
DummyMesh->grab();
}
}
struct SPolygon
{
core::array<s32> Indices;
};
void CColladaFileLoader::readPolygonSection(io::IXMLReaderUTF8* reader,
core::array<SSource>& sources, scene::SMesh* mesh,
const core::stringc& geometryId)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading polygon section");
#endif
core::stringc materialName = reader->getAttributeValue("material");
core::stringc polygonType = reader->getNodeName();
const int polygonCount = reader->getAttributeValueAsInt("count");
core::array<SPolygon> polygons;
if (polygonType == polygonsSectionName)
polygons.reallocate(polygonCount);
core::array<int> vCounts;
bool parsePolygonOK = false;
bool parseVcountOK = false;
u32 inputSemanticCount = 0;
bool unresolvedInput=false;
u32 maxOffset = 0;
core::array<SColladaInput> localInputs;
if (!reader->isEmptyElement())
while(reader->read())
{
const char* nodeName = reader->getNodeName();
if (reader->getNodeType() == io::EXN_ELEMENT)
{
if (inputTagName == nodeName)
{
readColladaInput(reader, localInputs);
SColladaInput& inp = localInputs.getLast();
if (inp.Semantic == ECIS_VERTEX)
{
inp.Source = Inputs[0].Source;
for (u32 i=1; i<Inputs.size(); ++i)
{
localInputs.push_back(Inputs[i]);
uriToId(localInputs.getLast().Source);
maxOffset = core::max_(maxOffset,localInputs.getLast().Offset);
++inputSemanticCount;
}
}
uriToId(inp.Source);
maxOffset = core::max_(maxOffset,inp.Offset);
++inputSemanticCount;
}
else
if (primitivesName == nodeName)
{
parsePolygonOK = true;
polygons.push_back(SPolygon());
}
else
if (vcountName == nodeName)
{
parseVcountOK = true;
}
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
{
if (primitivesName == nodeName)
parsePolygonOK = false;
else
if (vcountName == nodeName)
parseVcountOK = false;
else
if (polygonType == nodeName)
break;
}
else
if (reader->getNodeType() == io::EXN_TEXT)
{
if (parseVcountOK)
{
core::stringc data = reader->getNodeData();
data.trim();
const c8* p = &data[0];
while(*p)
{
findNextNoneWhiteSpace(&p);
if (*p)
vCounts.push_back(readInt(&p));
}
parseVcountOK = false;
}
else
if (parsePolygonOK && polygons.size())
{
core::stringc data = reader->getNodeData();
data.trim();
const c8* p = &data[0];
SPolygon& poly = polygons.getLast();
if (polygonType == polygonsSectionName)
poly.Indices.reallocate((maxOffset+1)*3);
else
poly.Indices.reallocate(polygonCount*(maxOffset+1)*3);
if (vCounts.empty())
{
while(*p)
{
findNextNoneWhiteSpace(&p);
poly.Indices.push_back(readInt(&p));
}
}
else
{
for (u32 i = 0; i < vCounts.size(); i++)
{
const int polyVCount = vCounts[i];
core::array<int> polyCorners;
for (u32 j = 0; j < polyVCount * inputSemanticCount; j++)
{
if (!*p)
break;
findNextNoneWhiteSpace(&p);
polyCorners.push_back(readInt(&p));
}
while (polyCorners.size() >= 3 * inputSemanticCount)
{
for (u32 k = 0; k < inputSemanticCount * 3; ++k)
{
poly.Indices.push_back(polyCorners[k]);
}
polyCorners.erase(inputSemanticCount,inputSemanticCount);
}
polyCorners.clear();
}
vCounts.clear();
}
parsePolygonOK = false;
}
}
}
for (u32 i=0; i<localInputs.size(); ++i)
{
SColladaInput& inp = localInputs[i];
u32 s;
for (s=0; s<sources.size(); ++s)
{
if (sources[s].Id == inp.Source)
{
inp.Data = sources[s].Array.Data.pointer();
inp.Stride = sources[s].Accessors[0].Stride;
break;
}
}
if (s == sources.size())
{
os::Printer::log("COLLADA Warning, polygon input source not found",
inp.Source.c_str());
inp.Semantic=ECIS_COUNT;
unresolvedInput=true;
}
else
{
#ifdef COLLADA_READER_DEBUG
core::stringc tmp = "Added slot ";
tmp += inputSemanticNames[inp.Semantic];
tmp += " sourceArray:";
tmp += inp.Source;
os::Printer::log(tmp.c_str());
#endif
}
}
if ((inputSemanticCount == 0) || !polygons.size())
return;
u32 u;
u32 textureCoordSetCount = 0;
bool normalSlotCount = false;
u32 secondTexCoordSetIndex = 0xFFFFFFFF;
for (u=0; u<Inputs.size(); ++u)
{
if (Inputs[u].Semantic == ECIS_TEXCOORD || Inputs[u].Semantic == ECIS_UV )
{
++textureCoordSetCount;
if (textureCoordSetCount==2)
secondTexCoordSetIndex = u;
}
else
if (Inputs[u].Semantic == ECIS_NORMAL)
normalSlotCount=true;
}
scene::IMeshBuffer* buffer = 0;
++maxOffset;
if ( textureCoordSetCount < 2 )
{
scene::SMeshBuffer* mbuffer = new SMeshBuffer();
buffer = mbuffer;
core::map<video::S3DVertex, int> vertMap;
for (u32 i=0; i<polygons.size(); ++i)
{
core::array<u16> indices;
const u32 vertexCount = polygons[i].Indices.size() / maxOffset;
mbuffer->Vertices.reallocate(mbuffer->Vertices.size()+vertexCount);
for (u32 v=0; v<polygons[i].Indices.size(); v+=maxOffset)
{
video::S3DVertex vtx;
vtx.Color.set(255,255,255,255);
for (u32 k=0; k<localInputs.size(); ++k)
{
if (!localInputs[k].Data)
continue;
const u32 idx = localInputs[k].Stride*polygons[i].Indices[v+localInputs[k].Offset];
switch(localInputs[k].Semantic)
{
case ECIS_POSITION:
case ECIS_VERTEX:
vtx.Pos.X = localInputs[k].Data[idx+0];
if (FlipAxis)
{
vtx.Pos.Z = localInputs[k].Data[idx+1];
vtx.Pos.Y = localInputs[k].Data[idx+2];
}
else
{
vtx.Pos.Y = localInputs[k].Data[idx+1];
vtx.Pos.Z = localInputs[k].Data[idx+2];
}
break;
case ECIS_NORMAL:
vtx.Normal.X = localInputs[k].Data[idx+0];
if (FlipAxis)
{
vtx.Normal.Z = localInputs[k].Data[idx+1];
vtx.Normal.Y = localInputs[k].Data[idx+2];
}
else
{
vtx.Normal.Y = localInputs[k].Data[idx+1];
vtx.Normal.Z = localInputs[k].Data[idx+2];
}
break;
case ECIS_TEXCOORD:
case ECIS_UV:
vtx.TCoords.X = localInputs[k].Data[idx+0];
vtx.TCoords.Y = 1-localInputs[k].Data[idx+1];
break;
case ECIS_TANGENT:
break;
default:
break;
}
}
core::map<video::S3DVertex, int>::Node* n = vertMap.find(vtx);
if (n)
{
indices.push_back(n->getValue());
}
else
{
indices.push_back(mbuffer->getVertexCount());
mbuffer->Vertices.push_back(vtx);
vertMap.insert(vtx, mbuffer->getVertexCount()-1);
}
}
if (polygonsSectionName == polygonType &&
indices.size() > 3)
{
if (FlipAxis)
{
for (u32 ind = indices.size()-3; ind>0 ; --ind)
{
mbuffer->Indices.push_back(indices[0]);
mbuffer->Indices.push_back(indices[ind+2]);
mbuffer->Indices.push_back(indices[ind+1]);
}
}
else
{
for (u32 ind = 0; i+2 < indices.size(); ++ind)
{
mbuffer->Indices.push_back(indices[0]);
mbuffer->Indices.push_back(indices[ind+1]);
mbuffer->Indices.push_back(indices[ind+2]);
}
}
}
else
{
for (u32 ind = 0; ind < indices.size(); ind+=3)
{
if (FlipAxis)
{
mbuffer->Indices.push_back(indices[ind+2]);
mbuffer->Indices.push_back(indices[ind+1]);
mbuffer->Indices.push_back(indices[ind+0]);
}
else
{
mbuffer->Indices.push_back(indices[ind+0]);
mbuffer->Indices.push_back(indices[ind+1]);
mbuffer->Indices.push_back(indices[ind+2]);
}
}
}
}
}
else
{
scene::SMeshBufferLightMap* mbuffer = new SMeshBufferLightMap();
buffer = mbuffer;
for (u32 i=0; i<polygons.size(); ++i)
{
const u32 vertexCount = polygons[i].Indices.size() / maxOffset;
mbuffer->Vertices.reallocate(mbuffer->Vertices.size()+vertexCount);
for (u32 v=0; v<polygons[i].Indices.size(); v+=maxOffset)
{
video::S3DVertex2TCoords vtx;
vtx.Color.set(100,255,255,255);
for (u32 k=0; k<Inputs.size(); ++k)
{
const u32 idx = localInputs[k].Stride*polygons[i].Indices[v+Inputs[k].Offset];
switch(localInputs[k].Semantic)
{
case ECIS_POSITION:
case ECIS_VERTEX:
vtx.Pos.X = localInputs[k].Data[idx+0];
if (FlipAxis)
{
vtx.Pos.Z = localInputs[k].Data[idx+1];
vtx.Pos.Y = localInputs[k].Data[idx+2];
}
else
{
vtx.Pos.Y = localInputs[k].Data[idx+1];
vtx.Pos.Z = localInputs[k].Data[idx+2];
}
break;
case ECIS_NORMAL:
vtx.Normal.X = localInputs[k].Data[idx+0];
if (FlipAxis)
{
vtx.Normal.Z = localInputs[k].Data[idx+1];
vtx.Normal.Y = localInputs[k].Data[idx+2];
}
else
{
vtx.Normal.Y = localInputs[k].Data[idx+1];
vtx.Normal.Z = localInputs[k].Data[idx+2];
}
break;
case ECIS_TEXCOORD:
case ECIS_UV:
if (k==secondTexCoordSetIndex)
{
vtx.TCoords2.X = localInputs[k].Data[idx+0];
vtx.TCoords2.Y = 1-localInputs[k].Data[idx+1];
}
else
{
vtx.TCoords.X = localInputs[k].Data[idx+0];
vtx.TCoords.Y = 1-localInputs[k].Data[idx+1];
}
break;
case ECIS_TANGENT:
break;
default:
break;
}
}
mbuffer->Vertices.push_back(vtx);
}
const u32 oldVertexCount = mbuffer->Vertices.size() - vertexCount;
for (u32 face=0; face<vertexCount-2; ++face)
{
mbuffer->Indices.push_back(oldVertexCount + 0);
mbuffer->Indices.push_back(oldVertexCount + 1 + face);
mbuffer->Indices.push_back(oldVertexCount + 2 + face);
}
}
}
const SColladaMaterial* m = findMaterial(materialName);
if (m)
{
buffer->getMaterial() = m->Mat;
SMesh tmpmesh;
tmpmesh.addMeshBuffer(buffer);
SceneManager->getMeshManipulator()->setVertexColors(&tmpmesh,m->Mat.DiffuseColor);
if (m->Transparency != 1.0f)
SceneManager->getMeshManipulator()->setVertexColorAlpha(&tmpmesh,core::floor32(m->Transparency*255.0f));
}
core::stringc meshbufferReference = geometryId+"/"+materialName;
if (!MaterialsToBind.find(meshbufferReference))
{
MaterialsToBind[meshbufferReference] = MeshesToBind.size();
MeshesToBind.push_back(core::array<irr::scene::IMeshBuffer*>());
}
MeshesToBind[MaterialsToBind[meshbufferReference]].push_back(buffer);
if (!normalSlotCount)
SceneManager->getMeshManipulator()->recalculateNormals(buffer, true);
buffer->recalculateBoundingBox();
mesh->addMeshBuffer(buffer);
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA added meshbuffer", core::stringc(buffer->getVertexCount())+" vertices, "+core::stringc(buffer->getIndexCount())+" indices.");
#endif
buffer->drop();
}
void CColladaFileLoader::readLightPrefab(io::IXMLReaderUTF8* reader)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading light prefab");
#endif
CLightPrefab* prefab = new CLightPrefab(readId(reader));
if (!reader->isEmptyElement())
{
if (Version >= 10400)
{
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
if (pointSectionName == reader->getNodeName())
prefab->LightData.Type=video::ELT_POINT;
else
if (directionalSectionName == reader->getNodeName())
prefab->LightData.Type=video::ELT_DIRECTIONAL;
else
if (spotSectionName == reader->getNodeName())
prefab->LightData.Type=video::ELT_SPOT;
else
if (ambientSectionName == reader->getNodeName())
prefab->LightData.Type=video::ELT_POINT;
else
if (colorNodeName == reader->getNodeName())
prefab->LightData.DiffuseColor=readColorNode(reader);
else
if (constantAttenuationNodeName == reader->getNodeName())
readFloatsInsideElement(reader,&prefab->LightData.Attenuation.X,1);
else
if (linearAttenuationNodeName == reader->getNodeName())
readFloatsInsideElement(reader,&prefab->LightData.Attenuation.Y,1);
else
if (quadraticAttenuationNodeName == reader->getNodeName())
readFloatsInsideElement(reader,&prefab->LightData.Attenuation.Z,1);
else
if (falloffAngleNodeName == reader->getNodeName())
{
readFloatsInsideElement(reader,&prefab->LightData.OuterCone,1);
prefab->LightData.OuterCone *= core::DEGTORAD;
}
else
if (falloffExponentNodeName == reader->getNodeName())
readFloatsInsideElement(reader,&prefab->LightData.Falloff,1);
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
{
if ((pointSectionName == reader->getNodeName()) ||
(directionalSectionName == reader->getNodeName()) ||
(spotSectionName == reader->getNodeName()) ||
(ambientSectionName == reader->getNodeName()))
break;
}
}
}
else
{
readColladaParameters(reader, lightPrefabName);
SColladaParam* p = getColladaParameter(ECPN_COLOR);
if (p && p->Type == ECPT_FLOAT3)
prefab->LightData.DiffuseColor.set(p->Floats[0], p->Floats[1], p->Floats[2]);
}
}
Prefabs.push_back(prefab);
}
SColladaParam* CColladaFileLoader::getColladaParameter(ECOLLADA_PARAM_NAME name)
{
for (u32 i=0; i<ColladaParameters.size(); ++i)
if (ColladaParameters[i].Name == name)
return &ColladaParameters[i];
return 0;
}
SColladaInput* CColladaFileLoader::getColladaInput(ECOLLADA_INPUT_SEMANTIC input)
{
for (u32 i=0; i<Inputs.size(); ++i)
if (Inputs[i].Semantic == input)
return &Inputs[i];
return 0;
}
void CColladaFileLoader::readColladaInput(io::IXMLReaderUTF8* reader, core::array<SColladaInput>& inputs)
{
SColladaInput p;
core::stringc semanticName = reader->getAttributeValue("semantic");
for (u32 i=0; inputSemanticNames[i]; ++i)
{
if (semanticName == inputSemanticNames[i])
{
p.Semantic = (ECOLLADA_INPUT_SEMANTIC)i;
break;
}
}
p.Source = reader->getAttributeValue("source");
if (reader->getAttributeValue("offset"))
p.Offset = (u32)reader->getAttributeValueAsInt("offset");
else
p.Offset = (u32)reader->getAttributeValueAsInt("idx");
p.Set = (u32)reader->getAttributeValueAsInt("set");
inputs.push_back(p);
}
void CColladaFileLoader::readColladaInputs(io::IXMLReaderUTF8* reader, const core::stringc& parentName)
{
Inputs.clear();
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT &&
inputTagName == reader->getNodeName())
{
readColladaInput(reader, Inputs);
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
{
if (parentName == reader->getNodeName())
return;
}
}
}
void CColladaFileLoader::readColladaParameters(io::IXMLReaderUTF8* reader,
const core::stringc& parentName)
{
ColladaParameters.clear();
const char* const paramNames[] = {"COLOR", "AMBIENT", "DIFFUSE",
"SPECULAR", "SHININESS", "YFOV", "ZNEAR", "ZFAR", 0};
const char* const typeNames[] = {"float", "float2", "float3", 0};
while(reader->read())
{
const char* nodeName = reader->getNodeName();
if (reader->getNodeType() == io::EXN_ELEMENT &&
paramTagName == nodeName)
{
SColladaParam p;
u32 i;
core::stringc typeName = reader->getAttributeValue("type");
for (i=0; typeNames[i]; ++i)
if (typeName == typeNames[i])
{
p.Type = (ECOLLADA_PARAM_TYPE)i;
break;
}
core::stringc nameName = reader->getAttributeValue("name");
for (i=0; typeNames[i]; ++i)
if (nameName == paramNames[i])
{
p.Name = (ECOLLADA_PARAM_NAME)i;
break;
}
switch(p.Type)
{
case ECPT_FLOAT:
case ECPT_FLOAT2:
case ECPT_FLOAT3:
case ECPT_FLOAT4:
readFloatsInsideElement(reader, p.Floats, p.Type - ECPT_FLOAT + 1);
break;
default:
break;
}
ColladaParameters.push_back(p);
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
{
if (parentName == reader->getNodeName())
return;
}
}
}
inline f32 CColladaFileLoader::readFloat(const c8** p)
{
f32 ftmp;
*p = core::fast_atof_move(*p, ftmp);
return ftmp;
}
inline s32 CColladaFileLoader::readInt(const c8** p)
{
return (s32)readFloat(p);
}
void CColladaFileLoader::findNextNoneWhiteSpace(const c8** start)
{
const c8* p = *start;
while(*p && (*p==' ' || *p=='\n' || *p=='\r' || *p=='\t'))
++p;
*start = p;
}
void CColladaFileLoader::readFloatsInsideElement(io::IXMLReaderUTF8* reader, f32* floats, u32 count)
{
if (reader->isEmptyElement())
return;
while(reader->read())
{
if (reader->getNodeType() == io::EXN_TEXT)
{
core::stringc data = reader->getNodeData();
data.trim();
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;
}
}
void CColladaFileLoader::readIntsInsideElement(io::IXMLReaderUTF8* reader, s32* ints, u32 count)
{
if (reader->isEmptyElement())
return;
while(reader->read())
{
if (reader->getNodeType() == io::EXN_TEXT)
{
core::stringc data = reader->getNodeData();
data.trim();
const c8* p = &data[0];
for (u32 i=0; i<count; ++i)
{
findNextNoneWhiteSpace(&p);
if (*p)
ints[i] = readInt(&p);
else
ints[i] = 0;
}
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
break;
}
}
video::SColorf CColladaFileLoader::readColorNode(io::IXMLReaderUTF8* reader)
{
if (reader->getNodeType() == io::EXN_ELEMENT &&
colorNodeName == reader->getNodeName())
{
f32 color[4];
readFloatsInsideElement(reader,color,4);
return video::SColorf(color[0], color[1], color[2], color[3]);
}
return video::SColorf();
}
f32 CColladaFileLoader::readFloatNode(io::IXMLReaderUTF8* reader)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading <float>");
#endif
f32 result = 0.0f;
if (reader->getNodeType() == io::EXN_ELEMENT &&
floatNodeName == reader->getNodeName())
{
readFloatsInsideElement(reader,&result,1);
}
return result;
}
void CColladaFileLoader::clearData()
{
for (u32 i=0; i<Prefabs.size(); ++i)
Prefabs[i]->drop();
Prefabs.clear();
ColladaParameters.clear();
Images.clear();
Textures.clear();
Materials.clear();
Inputs.clear();
Effects.clear();
MaterialsToBind.clear();
MeshesToBind.clear();
}
void CColladaFileLoader::uriToId(core::stringc& str)
{
if (!str.size())
return;
if (str[0] == '#')
str.erase(0);
}
core::stringc CColladaFileLoader::readId(io::IXMLReaderUTF8* reader)
{
core::stringc id = reader->getAttributeValue("id");
if (id.size()==0)
id = reader->getAttributeValue("name");
return id;
}
video::ITexture* CColladaFileLoader::getTextureFromImage(core::stringc uri)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA searching texture", uri);
#endif
video::IVideoDriver* driver = SceneManager->getVideoDriver();
for (;;)
{
uriToId(uri);
for (u32 i=0; i<Images.size(); ++i)
{
if (uri == Images[i].Id)
{
if (Images[i].Source.size() && Images[i].SourceIsFilename)
{
if (FileSystem->existFile(Images[i].Source))
return driver->getTexture(Images[i].Source);
return driver->getTexture((FileSystem->getFileDir(CurrentlyLoadingMesh)+"/"+Images[i].Source));
}
else
if (Images[i].Source.size())
{
const u32 size = Images[i].Dimension.Width * Images[i].Dimension.Height;;
u32* data = new u32[size];
u32* ptrdest = data;
const c8* ptrsrc = Images[i].Source.c_str();
for (u32 j=0; j<size; ++j)
{
sscanf(ptrsrc, "%x", ptrdest);
++ptrdest;
ptrsrc += 4;
}
video::IImage* img = driver->createImageFromData(video::ECF_A8R8G8B8, Images[i].Dimension, data, true, true);
video::ITexture* tex = driver->addTexture((CurrentlyLoadingMesh+"#"+Images[i].Id).c_str(), img);
img->drop();
return tex;
}
break;
}
}
if (Parameters.getAttributeType(uri.c_str())==io::EAT_STRING)
{
uri = Parameters.getAttributeAsString(uri.c_str());
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA now searching texture", uri.c_str());
#endif
}
else
break;
}
return 0;
}
void CColladaFileLoader::readParameter(io::IXMLReaderUTF8* reader)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading parameter");
#endif
const core::stringc name = reader->getAttributeValue("sid");
if (!reader->isEmptyElement())
{
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
if (floatNodeName == reader->getNodeName())
{
const f32 f = readFloatNode(reader);
Parameters.addFloat(name.c_str(), f);
}
else
if (float2NodeName == reader->getNodeName())
{
f32 f[2];
readFloatsInsideElement(reader, f, 2);
}
else
if (float3NodeName == reader->getNodeName())
{
f32 f[3];
readFloatsInsideElement(reader, f, 3);
Parameters.addVector3d(name.c_str(), core::vector3df(f[0],f[1],f[2]));
}
else
if ((initFromName == reader->getNodeName()) ||
(sourceSectionName == reader->getNodeName()))
{
reader->read();
Parameters.addString(name.c_str(), reader->getNodeData());
}
else
if (wrapsName == reader->getNodeName())
{
reader->read();
const core::stringc val = reader->getNodeData();
if (val == "WRAP")
Parameters.addBool("wrap_s", true);
}
else
if (wraptName == reader->getNodeName())
{
reader->read();
const core::stringc val = reader->getNodeData();
if (val == "WRAP")
Parameters.addBool("wrap_t", true);
}
else
if (minfilterName == reader->getNodeName())
{
reader->read();
const core::stringc val = reader->getNodeData();
if (val == "LINEAR_MIPMAP_LINEAR")
Parameters.addBool("trilinear", true);
else
if (val == "LINEAR_MIPMAP_NEAREST")
Parameters.addBool("bilinear", true);
}
else
if (magfilterName == reader->getNodeName())
{
reader->read();
const core::stringc val = reader->getNodeData();
if (val != "LINEAR")
{
Parameters.addBool("bilinear", false);
Parameters.addBool("trilinear", false);
}
}
else
if (mipfilterName == reader->getNodeName())
{
Parameters.addBool("anisotropic", true);
}
}
else
if(reader->getNodeType() == io::EXN_ELEMENT_END)
{
if (newParamName == reader->getNodeName())
break;
}
}
}
}
}
}
#endif