2597 lines
70 KiB
C++
2597 lines
70 KiB
C++
// Copyright (C) 2002-2007 Nikolaus Gebhardt
|
|
// This file is part of the "Irrlicht Engine".
|
|
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
|
|
|
#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"
|
|
|
|
#define COLLADA_READER_DEBUG
|
|
namespace irr
|
|
{
|
|
namespace scene
|
|
{
|
|
// currently supported COLLADA tag names
|
|
|
|
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 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 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 textureNodeName = "texture";
|
|
const core::stringc doubleSidedName = "double_sided";
|
|
|
|
const core::stringc profileCOMMONSectionName = "profile_COMMON";
|
|
|
|
const char* const inputSemanticNames[] = {"POSITION", "VERTEX", "NORMAL", "TEXCOORD",
|
|
"UV", "TANGENT", "IMAGE", "TEXTURE", 0};
|
|
|
|
//! following class is for holding and creating instances of library
|
|
//! objects, named prefabs in this loader.
|
|
class CPrefab : public IColladaPrefab
|
|
{
|
|
public:
|
|
|
|
CPrefab(const core::stringc& id) : Id(id)
|
|
{
|
|
}
|
|
|
|
//! creates an instance of this prefab
|
|
virtual scene::ISceneNode* addInstance(scene::ISceneNode* parent,
|
|
scene::ISceneManager* mgr)
|
|
{
|
|
// empty implementation
|
|
return 0;
|
|
}
|
|
|
|
//! returns id of this prefab
|
|
virtual const core::stringc& getId()
|
|
{
|
|
return Id;
|
|
}
|
|
|
|
protected:
|
|
|
|
core::stringc Id;
|
|
};
|
|
|
|
|
|
//! prefab for a light scene node
|
|
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; // publically accessible
|
|
|
|
//! creates an instance of this prefab
|
|
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);
|
|
l->setLightData ( LightData );
|
|
return l;
|
|
}
|
|
};
|
|
|
|
|
|
//! prefab for a mesh scene node
|
|
class CGeometryPrefab : public CPrefab
|
|
{
|
|
public:
|
|
|
|
CGeometryPrefab(const core::stringc& id) : CPrefab(id)
|
|
{
|
|
}
|
|
|
|
scene::IMesh* Mesh;
|
|
|
|
//! creates an instance of this prefab
|
|
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);
|
|
return m;
|
|
}
|
|
};
|
|
|
|
|
|
//! prefab for a camera scene node
|
|
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
|
|
}
|
|
|
|
// publicly accessible data
|
|
f32 YFov;
|
|
f32 ZNear;
|
|
f32 ZFar;
|
|
|
|
//! creates an instance of this prefab
|
|
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);
|
|
c->setFOV(YFov);
|
|
c->setNearValue(ZNear);
|
|
c->setFarValue(ZFar);
|
|
return c;
|
|
}
|
|
};
|
|
|
|
|
|
//! prefab for a container scene node
|
|
//! Collects other prefabs and instantiates them upon instantiation
|
|
//! Uses a dummy scene node to return the childs as one scene node
|
|
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
|
|
}
|
|
|
|
//! creates an instance of this prefab
|
|
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 (Childs.size()==0)
|
|
return 0;
|
|
|
|
scene::IDummyTransformationSceneNode* c = mgr->addDummyTransformationSceneNode(parent);
|
|
if (c)
|
|
{
|
|
c->getRelativeTransformationMatrix() = Transformation;
|
|
|
|
for (u32 i=0; i<Childs.size(); ++i)
|
|
Childs[i]->addInstance(c, mgr);
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
core::array<IColladaPrefab*> Childs;
|
|
core::matrix4 Transformation;
|
|
};
|
|
|
|
|
|
//! Constructor
|
|
CColladaFileLoader::CColladaFileLoader(scene::ISceneManager* smgr,
|
|
io::IFileSystem* fs)
|
|
: SceneManager(smgr), FileSystem(fs), DummyMesh(0),
|
|
FirstLoadedMesh(0), LoadedMeshCount(0), CreateInstances(false)
|
|
{
|
|
|
|
}
|
|
|
|
|
|
//! destructor
|
|
CColladaFileLoader::~CColladaFileLoader()
|
|
{
|
|
if (DummyMesh)
|
|
DummyMesh->drop();
|
|
|
|
if (FirstLoadedMesh)
|
|
FirstLoadedMesh->drop();
|
|
}
|
|
|
|
|
|
//! Returns true if the file maybe is able to be loaded by this class.
|
|
/** This decision should be based only on the file extension (e.g. ".cob") */
|
|
bool CColladaFileLoader::isALoadableFileExtension(const c8* fileName) const
|
|
{
|
|
return strstr(fileName, ".xml") || strstr(fileName, ".dae");
|
|
}
|
|
|
|
|
|
//! 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* 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);
|
|
|
|
// read until COLLADA section, skip other parts
|
|
|
|
while(reader->read())
|
|
{
|
|
if (reader->getNodeType() == io::EXN_ELEMENT)
|
|
{
|
|
if (colladaSectionName == reader->getNodeName())
|
|
readColladaSection(reader);
|
|
else
|
|
skipSection(reader, true); // unknown section
|
|
}
|
|
}
|
|
|
|
reader->drop();
|
|
|
|
scene::IAnimatedMesh* returnMesh = DummyMesh;
|
|
|
|
// because this loader loads and creates a complete scene instead of
|
|
// a single mesh, return an empty dummy mesh to make the scene manager
|
|
// know that everything went well.
|
|
if (!DummyMesh)
|
|
{
|
|
DummyMesh = new SAnimatedMesh();
|
|
returnMesh = DummyMesh;
|
|
}
|
|
|
|
// add the first loaded mesh into the mesh cache too, if more than one
|
|
// meshes have been loaded from the file
|
|
if (LoadedMeshCount>1 && FirstLoadedMesh)
|
|
{
|
|
os::Printer::log("Added COLLADA mesh", FirstLoadedMeshName.c_str());
|
|
SceneManager->getMeshCache()->addMesh(FirstLoadedMeshName.c_str(), FirstLoadedMesh);
|
|
}
|
|
|
|
// clean up temporary loaded data
|
|
clearData();
|
|
|
|
returnMesh->grab(); // store until this loader is destroyed
|
|
|
|
DummyMesh->drop();
|
|
DummyMesh = 0;
|
|
|
|
if (FirstLoadedMesh)
|
|
FirstLoadedMesh->drop();
|
|
FirstLoadedMesh = 0;
|
|
LoadedMeshCount = 0;
|
|
|
|
return returnMesh;
|
|
}
|
|
|
|
|
|
//! skips an (unknown) section in the collada document
|
|
void CColladaFileLoader::skipSection(io::IXMLReaderUTF8* reader, bool reportSkipping)
|
|
{
|
|
#ifndef COLLADA_READER_DEBUG
|
|
if (reportSkipping) // always report in COLLADA_READER_DEBUG mode
|
|
#endif
|
|
os::Printer::log("COLLADA skipping section", core::stringc(reader->getNodeName()).c_str());
|
|
|
|
// skip if this element is empty anyway.
|
|
if (reader->isEmptyElement())
|
|
return;
|
|
|
|
// read until we've reached the last element in this section
|
|
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 // COLLADA_READER_DEBUG
|
|
|
|
++tagCounter;
|
|
}
|
|
else
|
|
if (reader->getNodeType() == io::EXN_ELEMENT_END)
|
|
--tagCounter;
|
|
}
|
|
}
|
|
|
|
|
|
//! reads the <COLLADA> section and its content
|
|
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::ceil32(core::fract(version)*1000.0f);
|
|
// Version 1.4 can be checked for by if (Version >= 10400)
|
|
|
|
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); // unknown section
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//! reads a <library> section and its content
|
|
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)
|
|
{
|
|
// animation section tbd
|
|
if (cameraPrefabName == reader->getNodeName())
|
|
readCameraPrefab(reader);
|
|
else
|
|
// code section tbd
|
|
// controller section tbd
|
|
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
|
|
// program section tbd
|
|
if (textureSectionName == reader->getNodeName())
|
|
readTexture(reader);
|
|
else
|
|
skipSection(reader, true); // unknown section, not all allowed supported yet
|
|
}
|
|
else
|
|
if (reader->getNodeType() == io::EXN_ELEMENT_END)
|
|
{
|
|
if (librarySectionName == reader->getNodeName())
|
|
break; // end reading.
|
|
if (libraryNodesSectionName == reader->getNodeName())
|
|
break; // end reading.
|
|
if (libraryGeometriesSectionName == reader->getNodeName())
|
|
break; // end reading.
|
|
if (libraryMaterialsSectionName == reader->getNodeName())
|
|
break; // end reading.
|
|
if (libraryEffectsSectionName == reader->getNodeName())
|
|
break; // end reading.
|
|
if (libraryImagesSectionName == reader->getNodeName())
|
|
break; // end reading.
|
|
if (libraryLightsSectionName == reader->getNodeName())
|
|
break; // end reading.
|
|
if (libraryCamerasSectionName == reader->getNodeName())
|
|
break; // end reading.
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//! reads a <visual_scene> element and stores it as a prefab
|
|
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()) // as a child of visual_scene
|
|
readNodeSection(reader, SceneManager->getRootSceneNode(), p);
|
|
else
|
|
if (assetSectionName == reader->getNodeName())
|
|
readAssetSection(reader);
|
|
else
|
|
if (extraNodeName == reader->getNodeName())
|
|
skipSection(reader, false); // ignore all other sections
|
|
else
|
|
{
|
|
os::Printer::log("COLLADA loader warning: Wrong tag usage found", reader->getNodeName(), ELL_WARNING);
|
|
skipSection(reader, true); // ignore all other sections
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//! reads a <scene> section and its content
|
|
void CColladaFileLoader::readSceneSection(io::IXMLReaderUTF8* reader)
|
|
{
|
|
#ifdef COLLADA_READER_DEBUG
|
|
os::Printer::log("COLLADA reading scene");
|
|
#endif
|
|
|
|
if (reader->isEmptyElement())
|
|
return;
|
|
|
|
// read the scene
|
|
|
|
core::matrix4 transform; // transformation of this node
|
|
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())
|
|
{
|
|
// create dummy node if there is none yet.
|
|
if (!node)
|
|
node = SceneManager->addDummyTransformationSceneNode(SceneManager->getRootSceneNode());
|
|
|
|
readNodeSection(reader, node);
|
|
}
|
|
else
|
|
if ((instanceSceneName == reader->getNodeName()))
|
|
readInstanceNode(reader, SceneManager->getRootSceneNode(), 0);
|
|
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); // ignore all other sections
|
|
}
|
|
}
|
|
else
|
|
if ((reader->getNodeType() == io::EXN_ELEMENT_END) &&
|
|
(sceneSectionName == reader->getNodeName()))
|
|
return;
|
|
}
|
|
if (node)
|
|
node->getRelativeTransformationMatrix() = transform;
|
|
}
|
|
|
|
|
|
//! reads a <asset> section and its content
|
|
void CColladaFileLoader::readAssetSection(io::IXMLReaderUTF8* reader)
|
|
{
|
|
#ifdef COLLADA_READER_DEBUG
|
|
os::Printer::log("COLLADA reading asset");
|
|
#endif
|
|
|
|
// don't need asset data so far, so skip it
|
|
skipSection(reader, false);
|
|
}
|
|
|
|
|
|
//! reads a <node> section and its content
|
|
void CColladaFileLoader::readNodeSection(io::IXMLReaderUTF8* reader, scene::ISceneNode* parent, CScenePrefab* p)
|
|
{
|
|
#ifdef COLLADA_READER_DEBUG
|
|
os::Printer::log("COLLADA reading node");
|
|
#endif
|
|
|
|
if (reader->isEmptyElement())
|
|
return;
|
|
|
|
core::stringc name = readId(reader);
|
|
|
|
core::matrix4 transform; // transformation of this node
|
|
core::aabbox3df bbox;
|
|
scene::ISceneNode* node = 0; // instance
|
|
CScenePrefab* nodeprefab = 0; // prefab for library_nodes usage
|
|
|
|
if (p)
|
|
{
|
|
nodeprefab = new CScenePrefab(readId(reader));
|
|
p->Childs.push_back(nodeprefab);
|
|
Prefabs.push_back(nodeprefab); // in order to delete them later on
|
|
}
|
|
|
|
// read the node
|
|
|
|
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);
|
|
|
|
if (node && newnode)
|
|
{
|
|
// move children from dummy to new node
|
|
core::list<ISceneNode*>::ConstIterator it = node->getChildren().begin();
|
|
for (; it != node->getChildren().end(); it = node->getChildren().begin())
|
|
(*it)->setParent(newnode);
|
|
|
|
// remove previous dummy node
|
|
node->remove();
|
|
}
|
|
|
|
node = newnode;
|
|
}
|
|
else
|
|
if (nodeSectionName == reader->getNodeName())
|
|
{
|
|
// create dummy node if there is none yet.
|
|
if (!node)
|
|
{
|
|
scene::IDummyTransformationSceneNode* dummy =
|
|
SceneManager->addDummyTransformationSceneNode(parent);
|
|
dummy->getRelativeTransformationMatrix() = transform;
|
|
node = dummy;
|
|
}
|
|
|
|
// read and add child
|
|
readNodeSection(reader, node, nodeprefab);
|
|
}
|
|
else
|
|
if (extraNodeName == reader->getNodeName())
|
|
skipSection(reader, false);
|
|
else
|
|
skipSection(reader, true); // ignore all other sections
|
|
|
|
} // end if node
|
|
else
|
|
if (reader->getNodeType() == io::EXN_ELEMENT_END)
|
|
{
|
|
if (nodeSectionName == reader->getNodeName())
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (p)
|
|
p->Transformation = transform;
|
|
else
|
|
if (node)
|
|
{
|
|
// set transformation correctly into node.
|
|
node->setPosition(transform.getTranslation());
|
|
node->setRotation(transform.getRotationDegrees());
|
|
node->setScale(transform.getScale());
|
|
node->updateAbsolutePosition();
|
|
|
|
node->setName(name.c_str());
|
|
}
|
|
}
|
|
|
|
|
|
//! reads a <lookat> element and its content and creates a matrix from it
|
|
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;
|
|
}
|
|
|
|
|
|
//! reads a <skew> element and its content and creates a matrix from it
|
|
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]; // angle rotation-axis translation-axis
|
|
readFloatsInsideElement(reader, floats, 7);
|
|
|
|
// build skew matrix from these 7 floats
|
|
core::quaternion q;
|
|
q.fromAngleAxis(floats[0]*core::DEGTORAD, core::vector3df(floats[1], floats[2], floats[3]));
|
|
q.getMatrix(mat);
|
|
|
|
if (floats[4]==1.f) // along x-axis
|
|
{
|
|
mat[4]=0.f;
|
|
mat[6]=0.f;
|
|
mat[8]=0.f;
|
|
mat[9]=0.f;
|
|
}
|
|
else
|
|
if (floats[5]==1.f) // along y-axis
|
|
{
|
|
mat[1]=0.f;
|
|
mat[2]=0.f;
|
|
mat[8]=0.f;
|
|
mat[9]=0.f;
|
|
}
|
|
else
|
|
if (floats[6]==1.f) // along z-axis
|
|
{
|
|
mat[1]=0.f;
|
|
mat[2]=0.f;
|
|
mat[4]=0.f;
|
|
mat[6]=0.f;
|
|
}
|
|
|
|
return mat;
|
|
}
|
|
|
|
|
|
//! reads a <boundingbox> element and its content and stores it in bbox
|
|
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); // ignore all other sections
|
|
}
|
|
else
|
|
if (reader->getNodeType() == io::EXN_ELEMENT_END)
|
|
{
|
|
if (bboxNodeName == reader->getNodeName())
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//! reads a <matrix> element and its content and creates a matrix from it
|
|
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);
|
|
// put translation into the correct place
|
|
mat.setTranslation(core::vector3df(mat[3],mat[7],mat[11]));
|
|
mat[3]=mat[7]=mat[11]=0.f;
|
|
|
|
return mat;
|
|
}
|
|
|
|
|
|
//! reads a <perspective> element and its content and creates a matrix from it
|
|
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);
|
|
|
|
// TODO: build perspecitve matrix from this float
|
|
|
|
os::Printer::log("COLLADA loader warning: <perspective> not implemented yet.", ELL_WARNING);
|
|
|
|
return mat;
|
|
}
|
|
|
|
|
|
//! reads a <rotate> element and its content and creates a matrix from it
|
|
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;
|
|
q.fromAngleAxis(floats[3]*core::DEGTORAD, core::vector3df(floats[0], floats[1], floats[2]));
|
|
return q.getMatrix();
|
|
}
|
|
else
|
|
return core::IdentityMatrix;
|
|
}
|
|
|
|
|
|
//! reads a <scale> element and its content and creates a matrix from it
|
|
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);
|
|
|
|
mat.setScale(core::vector3df(floats[0], floats[1], floats[2]));
|
|
|
|
return mat;
|
|
}
|
|
|
|
|
|
//! reads a <translate> element and its content and creates a matrix from it
|
|
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);
|
|
|
|
mat.setTranslation(core::vector3df(floats[0], floats[1], floats[2]));
|
|
|
|
return mat;
|
|
}
|
|
|
|
|
|
//! reads any kind of <instance*> node and creates a scene node from it
|
|
void CColladaFileLoader::readInstanceNode(io::IXMLReaderUTF8* reader, scene::ISceneNode* parent,
|
|
scene::ISceneNode** outNode, CScenePrefab* p)
|
|
{
|
|
#ifdef COLLADA_READER_DEBUG
|
|
os::Printer::log("COLLADA reading instance");
|
|
#endif
|
|
|
|
// find prefab of the specified id
|
|
core::stringc url = reader->getAttributeValue("url");
|
|
uriToId(url);
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
for (u32 i=0; i<Prefabs.size(); ++i)
|
|
{
|
|
if (url == Prefabs[i]->getId())
|
|
{
|
|
if (p)
|
|
p->Childs.push_back(Prefabs[i]);
|
|
else
|
|
if (CreateInstances)
|
|
{
|
|
scene::ISceneNode * newNode
|
|
= Prefabs[i]->addInstance(parent, SceneManager);
|
|
if (outNode)
|
|
{
|
|
*outNode = newNode;
|
|
if (*outNode)
|
|
(*outNode)->setName(readId(reader).c_str());
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//! reads a <camera> element and stores it as prefab
|
|
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())
|
|
{
|
|
// read techniques optics and imager (the latter is completely ignored, though)
|
|
readColladaParameters(reader, cameraPrefabName);
|
|
|
|
SColladaParam* p;
|
|
|
|
// XFOV not yet supported
|
|
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];
|
|
// orthographic camera uses LEFT, RIGHT, TOP, and BOTTOM
|
|
}
|
|
|
|
Prefabs.push_back(prefab);
|
|
}
|
|
|
|
|
|
//! reads a <image> element and stores it in the image section
|
|
void CColladaFileLoader::readImage(io::IXMLReaderUTF8* reader)
|
|
{
|
|
#ifdef COLLADA_READER_DEBUG
|
|
os::Printer::log("COLLADA reading image");
|
|
#endif
|
|
|
|
// add image to list of loaded images.
|
|
Images.push_back(SColladaImage());
|
|
SColladaImage& image=Images.getLast();
|
|
|
|
image.Id = readId(reader);
|
|
image.Dimension.Height = (u32)reader->getAttributeValueAsInt("height");
|
|
image.Dimension.Width = (u32)reader->getAttributeValueAsInt("width");
|
|
|
|
if (Version >= 10400) // start with 1.4
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
//! reads a <texture> element and stores it in the texture section
|
|
void CColladaFileLoader::readTexture(io::IXMLReaderUTF8* reader)
|
|
{
|
|
#ifdef COLLADA_READER_DEBUG
|
|
os::Printer::log("COLLADA reading texture");
|
|
#endif
|
|
|
|
// add texture to list of loaded textures.
|
|
Textures.push_back(SColladaTexture());
|
|
SColladaTexture& texture=Textures.getLast();
|
|
|
|
texture.Id = readId(reader);
|
|
|
|
if (!reader->isEmptyElement())
|
|
{
|
|
readColladaInputs(reader, textureSectionName);
|
|
SColladaInput* input = getColladaInput(ECIS_IMAGE);
|
|
if (input)
|
|
{
|
|
const core::stringc imageName = input->Source;
|
|
texture.Texture = getTextureFromImage(imageName);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//! reads a <material> element and stores it in the material section
|
|
void CColladaFileLoader::readMaterial(io::IXMLReaderUTF8* reader)
|
|
{
|
|
#ifdef COLLADA_READER_DEBUG
|
|
os::Printer::log("COLLADA reading material");
|
|
#endif
|
|
|
|
SColladaMaterial material;
|
|
material.Id = readId(reader);
|
|
|
|
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;
|
|
}
|
|
} // end while reader->read();
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
|
|
//does not work because the wrong start node is chosen due to reading of inputs before
|
|
#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
|
|
}
|
|
}
|
|
|
|
// add material to list of loaded materials.
|
|
Materials.push_back(material);
|
|
}
|
|
|
|
void CColladaFileLoader::readEffect(io::IXMLReaderUTF8* reader, SColladaEffect * effect)
|
|
{
|
|
#ifdef COLLADA_READER_DEBUG
|
|
if (!effect) os::Printer::log("COLLADA reading effect");
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
while(reader->read())
|
|
{
|
|
if (reader->getNodeType() == io::EXN_ELEMENT)
|
|
{
|
|
// first come the tags we descend, but ignore the top-levels
|
|
if (!reader->isEmptyElement() && ((profileCOMMONSectionName == reader->getNodeName()) ||
|
|
(techniqueNodeName == reader->getNodeName())))
|
|
readEffect(reader,effect);
|
|
else
|
|
if (newParamName == reader->getNodeName())
|
|
readParameter(reader);
|
|
else
|
|
// these are the actual materials inside technique
|
|
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 )
|
|
{
|
|
// color or texture types
|
|
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.a;
|
|
}
|
|
else
|
|
if (reader->getNodeType() == io::EXN_ELEMENT &&
|
|
textureNodeName == reader->getNodeName())
|
|
{
|
|
const core::stringc tname = reader->getAttributeValue("texture");
|
|
effect->Mat.setTexture(0, getTextureFromImage(tname));
|
|
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 )
|
|
{
|
|
// float or param types
|
|
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); // ignore all other nodes
|
|
}
|
|
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 (doubleSidedName == reader->getNodeName())
|
|
{
|
|
// read the GoogleEarth extra flag for double sided polys
|
|
s32 doubleSided = 0;
|
|
readIntsInsideElement(reader,&doubleSided,1);
|
|
if (doubleSided)
|
|
{
|
|
effect->Mat.setFlag(irr::video::EMF_BACK_FACE_CULLING,false);
|
|
}
|
|
}
|
|
else
|
|
skipSection(reader, true); // ignore all other sections
|
|
}
|
|
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 != 1.0f)
|
|
effect->Mat.MaterialType = irr::video::EMT_TRANSPARENT_VERTEX_ALPHA;
|
|
}
|
|
|
|
|
|
const SColladaMaterial * CColladaFileLoader::findMaterial(const core::stringc & materialName)
|
|
{
|
|
// do a quick lookup in the materials
|
|
SColladaMaterial matToFind;
|
|
matToFind.Id = materialName;
|
|
s32 mat = Materials.binary_search(matToFind);
|
|
if (mat == -1)
|
|
return 0;
|
|
// instantiate the material effect if needed
|
|
if (Materials[mat].InstanceEffectId.size() > 0)
|
|
{
|
|
// do a quick lookup in the effects
|
|
SColladaEffect effectToFind;
|
|
effectToFind.Id = Materials[mat].InstanceEffectId;
|
|
s32 effect = Effects.binary_search(effectToFind);
|
|
if (effect != -1)
|
|
{
|
|
// found the effect, instantiate by copying into the material
|
|
Materials[mat].Mat = Effects[effect].Mat;
|
|
Materials[mat].Transparency = Effects[effect].Transparency;
|
|
// and indicate the material is instantiated by removing the effect ref
|
|
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())
|
|
{
|
|
// the symbol to retarget, and the target material
|
|
core::stringc materialReference = reader->getAttributeValue("symbol");
|
|
if (materialReference.size()==0)
|
|
continue;
|
|
core::stringc target = reader->getAttributeValue("target");
|
|
uriToId(target);
|
|
if (target.size()==0)
|
|
continue;
|
|
const SColladaMaterial * material = findMaterial(target);
|
|
if (!material)
|
|
continue;
|
|
// bind any pending materials for this node
|
|
materialReference = id+"/"+materialReference;
|
|
#ifdef COLLADA_READER_DEBUG
|
|
os::Printer::log((core::stringc("Material binding: ")+materialReference+" "+target).c_str());
|
|
#endif
|
|
if (MaterialsToBind.find(materialReference))
|
|
{
|
|
core::array<irr::scene::IMeshBuffer*> & toBind
|
|
= MeshesToBind[MaterialsToBind[materialReference]];
|
|
SMesh tmpmesh;
|
|
for (u32 i = 0; i < toBind.size(); ++i)
|
|
{
|
|
toBind[i]->getMaterial() = material->Mat;
|
|
if (material->Transparency!=1.0f)
|
|
tmpmesh.addMeshBuffer(toBind[i]);
|
|
}
|
|
if (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;
|
|
}
|
|
}
|
|
|
|
|
|
//! reads a <geometry> element and stores it as mesh if possible
|
|
void CColladaFileLoader::readGeometry(io::IXMLReaderUTF8* reader)
|
|
{
|
|
#ifdef COLLADA_READER_DEBUG
|
|
os::Printer::log("COLLADA reading geometry");
|
|
#endif
|
|
|
|
core::stringc id = readId(reader);
|
|
|
|
core::stringc VertexPositionSource; // each mesh has exactly one <vertex> member, containing
|
|
// a POSITION input. This string stores the source of this input.
|
|
core::array<SSource> sources;
|
|
bool okToReadArray = false;
|
|
|
|
SAnimatedMesh* amesh = new SAnimatedMesh();
|
|
scene::SMesh* mesh = new SMesh();
|
|
amesh->addMesh(mesh);
|
|
|
|
// handles geometry node and the mesh childs in this loop
|
|
// read sources with arrays and accessor for each mesh
|
|
if (!reader->isEmptyElement())
|
|
while(reader->read())
|
|
{
|
|
if (reader->getNodeType() == io::EXN_ELEMENT)
|
|
{
|
|
const char* nodeName = reader->getNodeName();
|
|
if (meshSectionName == nodeName)
|
|
{
|
|
// inside a mesh section. Don't have to do anything here.
|
|
}
|
|
else
|
|
if (sourceSectionName == nodeName)
|
|
{
|
|
// create a new source
|
|
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)
|
|
{
|
|
// create a new array and read it.
|
|
if (!sources.empty())
|
|
{
|
|
sources.getLast().Array.Name = readId(reader);
|
|
|
|
int count = reader->getAttributeValueAsInt("count");
|
|
sources.getLast().Array.Data.set_used(count); // pre allocate
|
|
|
|
// check if type of array is ok
|
|
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) // child of source (below a technique tag)
|
|
{
|
|
#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;
|
|
|
|
// the accessor contains some information on how to access (boi!) the array,
|
|
// the info is stored in collada style parameters, so just read them.
|
|
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
|
|
// read vertex input position source
|
|
readColladaInputs(reader, verticesSectionName);
|
|
SColladaInput* input = getColladaInput(ECIS_POSITION);
|
|
if (input)
|
|
VertexPositionSource = input->Source;
|
|
}
|
|
else
|
|
// lines and linestrips missing
|
|
if (polygonsSectionName == nodeName ||
|
|
polylistSectionName == nodeName ||
|
|
trianglesSectionName == nodeName)
|
|
{
|
|
// read polygons section
|
|
readPolygonSection(reader, VertexPositionSource, sources, mesh, id);
|
|
}
|
|
else
|
|
// trifans, and tristrips missing
|
|
if (extraNodeName == reader->getNodeName())
|
|
skipSection(reader, false);
|
|
else
|
|
if (techniqueCommonSectionName != nodeName) // techniqueCommon must not be skipped
|
|
{
|
|
// os::Printer::log("COLLADA loader warning: Wrong tag usage found", reader->getNodeName(), ELL_WARNING);
|
|
skipSection(reader, true); // ignore all other sections
|
|
}
|
|
} // end if node type is element
|
|
else
|
|
if (reader->getNodeType() == io::EXN_TEXT)
|
|
{
|
|
// read array data
|
|
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;
|
|
}
|
|
} // end reading array
|
|
|
|
okToReadArray = false;
|
|
|
|
} // end if node type is text
|
|
else
|
|
if (reader->getNodeType() == io::EXN_ELEMENT_END)
|
|
{
|
|
if (geometrySectionName == reader->getNodeName())
|
|
{
|
|
// end of geometry section reached, cancel out
|
|
break;
|
|
}
|
|
}
|
|
} // end while reader->read();
|
|
|
|
// add mesh as geometry
|
|
|
|
mesh->recalculateBoundingBox();
|
|
amesh->recalculateBoundingBox();
|
|
|
|
// create virtual file name
|
|
core::stringc filename = CurrentlyLoadingMesh;
|
|
filename += '#';
|
|
filename += id;
|
|
|
|
// add to scene manager
|
|
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();
|
|
|
|
// create geometry prefab
|
|
CGeometryPrefab* prefab = new CGeometryPrefab(id.c_str());
|
|
prefab->Mesh = mesh;
|
|
Prefabs.push_back(prefab);
|
|
|
|
// store as dummy mesh if no instances will be created
|
|
if (!CreateInstances && !DummyMesh)
|
|
{
|
|
DummyMesh = amesh;
|
|
DummyMesh->grab();
|
|
}
|
|
}
|
|
|
|
|
|
struct SPolygon
|
|
{
|
|
core::array<s32> Indices;
|
|
};
|
|
|
|
//! reads a polygons section and creates a mesh from it
|
|
void CColladaFileLoader::readPolygonSection(io::IXMLReaderUTF8* reader,
|
|
const core::stringc& vertexPositionSource, 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();
|
|
// int polygonCount = reader->getAttributeValueAsInt("count"); // Not useful because it only determines the number of primitives, which have arbitrary vertices n case of polygon
|
|
core::array<SPolygon> polygons;
|
|
core::array<int> vCounts;
|
|
bool parsePolygonOK = false;
|
|
bool parseVcountOK = false;
|
|
u32 inputSemanticCount = 0;
|
|
bool unresolvedInput=false;
|
|
u32 maxOffset = 0;
|
|
Inputs.clear();
|
|
|
|
// read all <input> and primitives
|
|
if (!reader->isEmptyElement())
|
|
while(reader->read())
|
|
{
|
|
const char* nodeName = reader->getNodeName();
|
|
|
|
if (reader->getNodeType() == io::EXN_ELEMENT)
|
|
{
|
|
// polygon node may contain params
|
|
if (inputTagName == nodeName)
|
|
{
|
|
// read input tag
|
|
readColladaInput(reader);
|
|
|
|
// resolve input source
|
|
SColladaInput& inp = Inputs.getLast();
|
|
core::stringc sourceArrayURI;
|
|
|
|
// get input source array id, if it is a vertex input, take
|
|
// the <vertex><input>-source attribute.
|
|
if (inp.Semantic == ECIS_VERTEX)
|
|
sourceArrayURI = vertexPositionSource;
|
|
else
|
|
sourceArrayURI = inp.Source;
|
|
|
|
uriToId(sourceArrayURI);
|
|
|
|
// find source array (we'll ignore accessors for this implementation)
|
|
u32 s;
|
|
for (s=0; s<sources.size(); ++s)
|
|
{
|
|
if (sources[s].Id == sourceArrayURI)
|
|
{
|
|
// slot found
|
|
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",
|
|
sourceArrayURI.c_str());
|
|
unresolvedInput=true;
|
|
}
|
|
else
|
|
{
|
|
#ifdef COLLADA_READER_DEBUG
|
|
// print slot
|
|
core::stringc tmp = "Added slot ";
|
|
tmp += inputSemanticNames[inp.Semantic];
|
|
tmp += " sourceArray:";
|
|
tmp += sourceArrayURI;
|
|
os::Printer::log(tmp.c_str());
|
|
#endif
|
|
}
|
|
|
|
maxOffset = core::max_(maxOffset,inp.Offset);
|
|
++inputSemanticCount;
|
|
}
|
|
else
|
|
if (primitivesName == nodeName)
|
|
{
|
|
parsePolygonOK = true;
|
|
polygons.push_back(SPolygon());
|
|
} // end is polygon node
|
|
else
|
|
if (vcountName == nodeName)
|
|
{
|
|
parseVcountOK = true;
|
|
} // end is polygon node
|
|
} // end is element node
|
|
else
|
|
if (reader->getNodeType() == io::EXN_ELEMENT_END)
|
|
{
|
|
if (primitivesName == nodeName)
|
|
parsePolygonOK = false; // end parsing a polygon
|
|
else
|
|
if (vcountName == nodeName)
|
|
parseVcountOK = false; // end parsing vcounts
|
|
else
|
|
if (polygonType == nodeName)
|
|
break; // cancel out and create mesh
|
|
|
|
} // end is element end
|
|
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 (vCounts.empty())
|
|
{
|
|
while(*p)
|
|
{
|
|
findNextNoneWhiteSpace(&p);
|
|
poly.Indices.push_back(readInt(&p));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
for (u32 i = 0; i < vCounts.size(); i++)
|
|
{
|
|
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)
|
|
{
|
|
// add one triangle's worth of indices
|
|
for (u32 k = 0; k < inputSemanticCount * 3; ++k)
|
|
{
|
|
poly.Indices.push_back(polyCorners[k]);
|
|
}
|
|
|
|
// remove one corner from our poly
|
|
polyCorners.erase(inputSemanticCount,inputSemanticCount);
|
|
}
|
|
polyCorners.clear();
|
|
}
|
|
vCounts.clear();
|
|
}
|
|
parsePolygonOK = false;
|
|
}
|
|
}
|
|
} // end while reader->read()
|
|
|
|
if (inputSemanticCount == 0 || unresolvedInput)
|
|
return; // we cannot create the mesh if one of the input semantics wasn't found.
|
|
|
|
if (!polygons.size())
|
|
return; // cancel if there are no polygons anyway.
|
|
|
|
// analyze content of Inputs to create a fitting mesh buffer
|
|
|
|
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;
|
|
}
|
|
|
|
// if there is more than one texture coordinate set, create a lightmap mesh buffer,
|
|
// otherwise use a standard mesh buffer
|
|
|
|
scene::IMeshBuffer* buffer = 0;
|
|
++maxOffset; // +1 to jump to the next value
|
|
|
|
if ( textureCoordSetCount <= 1 )
|
|
{
|
|
// standard mesh buffer
|
|
|
|
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 all index/semantic groups
|
|
for (u32 v=0; v<polygons[i].Indices.size(); v+=maxOffset)
|
|
{
|
|
video::S3DVertex vtx;
|
|
vtx.Color.set(255,255,255,255);
|
|
|
|
// for all input semantics
|
|
for (u32 k=0; k<Inputs.size(); ++k)
|
|
{
|
|
if (!Inputs[k].Data)
|
|
continue;
|
|
// build vertex from input semantics.
|
|
|
|
const u32 idx = Inputs[k].Stride*polygons[i].Indices[v+Inputs[k].Offset];
|
|
|
|
switch(Inputs[k].Semantic)
|
|
{
|
|
case ECIS_POSITION:
|
|
case ECIS_VERTEX:
|
|
vtx.Pos.X = Inputs[k].Data[idx+0];
|
|
vtx.Pos.Y = Inputs[k].Data[idx+1];
|
|
vtx.Pos.Z = Inputs[k].Data[idx+2];
|
|
break;
|
|
case ECIS_NORMAL:
|
|
vtx.Normal.X = Inputs[k].Data[idx+0];
|
|
vtx.Normal.Y = Inputs[k].Data[idx+1];
|
|
vtx.Normal.Z = Inputs[k].Data[idx+2];
|
|
break;
|
|
case ECIS_TEXCOORD:
|
|
case ECIS_UV:
|
|
vtx.TCoords.X = Inputs[k].Data[idx+0];
|
|
vtx.TCoords.Y = Inputs[k].Data[idx+1];
|
|
break;
|
|
case ECIS_TANGENT:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
//first, try to find this vertex in the mesh
|
|
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);
|
|
}
|
|
|
|
} // end for all vertices
|
|
|
|
if (polygonsSectionName == polygonType &&
|
|
indices.size() >= 4)
|
|
{
|
|
// need to tesselate for polygons of 4 or more vertices
|
|
// for now we naively turn interpret it as a triangle fan
|
|
// as full tesselation is problematic
|
|
for (u32 i = 0; i+2 < indices.size(); ++i)
|
|
{
|
|
mbuffer->Indices.push_back(indices[0]);
|
|
mbuffer->Indices.push_back(indices[i+1]);
|
|
mbuffer->Indices.push_back(indices[i+2]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// it's just triangles
|
|
for (u32 i = 0; i < indices.size(); ++i)
|
|
{
|
|
mbuffer->Indices.push_back(indices[i]);
|
|
}
|
|
}
|
|
|
|
} // end for all polygons
|
|
}
|
|
else
|
|
{
|
|
// lightmap mesh buffer
|
|
|
|
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 all vertices in array
|
|
for (u32 v=0; v<polygons[i].Indices.size(); v+=maxOffset)
|
|
{
|
|
video::S3DVertex2TCoords vtx;
|
|
vtx.Color.set(100,255,255,255);
|
|
|
|
// for all input semantics
|
|
for (u32 k=0; k<Inputs.size(); ++k)
|
|
{
|
|
// build vertex from input semantics.
|
|
|
|
const u32 idx = Inputs[k].Stride*polygons[i].Indices[v+Inputs[k].Offset];
|
|
|
|
switch(Inputs[k].Semantic)
|
|
{
|
|
case ECIS_POSITION:
|
|
case ECIS_VERTEX:
|
|
vtx.Pos.X = Inputs[k].Data[idx+0];
|
|
vtx.Pos.Y = Inputs[k].Data[idx+1];
|
|
vtx.Pos.Z = Inputs[k].Data[idx+2];
|
|
break;
|
|
case ECIS_NORMAL:
|
|
vtx.Normal.X = Inputs[k].Data[idx+0];
|
|
vtx.Normal.Y = Inputs[k].Data[idx+1];
|
|
vtx.Normal.Z = Inputs[k].Data[idx+2];
|
|
break;
|
|
case ECIS_TEXCOORD:
|
|
case ECIS_UV:
|
|
if (k==secondTexCoordSetIndex)
|
|
{
|
|
vtx.TCoords2.X = Inputs[k].Data[idx+0];
|
|
vtx.TCoords2.Y = Inputs[k].Data[idx+1];
|
|
}
|
|
else
|
|
{
|
|
vtx.TCoords.X = Inputs[k].Data[idx+0];
|
|
vtx.TCoords.Y = Inputs[k].Data[idx+1];
|
|
}
|
|
break;
|
|
case ECIS_TANGENT:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
mbuffer->Vertices.push_back(vtx);
|
|
|
|
} // end for all vertices
|
|
|
|
// add vertex indices
|
|
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);
|
|
}
|
|
|
|
} // end for all polygons
|
|
}
|
|
|
|
const SColladaMaterial* m = findMaterial(materialName);
|
|
if (m)
|
|
{
|
|
buffer->getMaterial() = m->Mat;
|
|
if (m->Transparency != 1.0f)
|
|
{
|
|
SMesh tmpmesh;
|
|
tmpmesh.addMeshBuffer(buffer);
|
|
SceneManager->getMeshManipulator()->setVertexColorAlpha(&tmpmesh,core::floor32(m->Transparency*255.0f));
|
|
}
|
|
}
|
|
// add future bind reference for the material
|
|
core::stringc materialReference = geometryId+"/"+materialName;
|
|
if (!MaterialsToBind.find(materialReference))
|
|
{
|
|
MaterialsToBind[materialReference] = MeshesToBind.size();
|
|
MeshesToBind.push_back(core::array<irr::scene::IMeshBuffer*>());
|
|
}
|
|
MeshesToBind[MaterialsToBind[materialReference]].push_back(buffer);
|
|
|
|
// calculate normals if there is no slot for it
|
|
|
|
if (!normalSlotCount)
|
|
SceneManager->getMeshManipulator()->recalculateNormals(buffer);
|
|
|
|
// recalculate bounding box
|
|
buffer->recalculateBoundingBox();
|
|
|
|
// add mesh buffer
|
|
mesh->addMeshBuffer(buffer);
|
|
|
|
buffer->drop();
|
|
}
|
|
|
|
|
|
//! reads a <light> element and stores it as prefab
|
|
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())
|
|
{
|
|
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);
|
|
}
|
|
|
|
|
|
//! returns a collada parameter or none if not found
|
|
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;
|
|
}
|
|
|
|
//! returns a collada input or none if not found
|
|
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;
|
|
}
|
|
|
|
|
|
//! reads a collada input tag and adds it to the input parameter
|
|
void CColladaFileLoader::readColladaInput(io::IXMLReaderUTF8* reader)
|
|
{
|
|
// parse param
|
|
SColladaInput p;
|
|
|
|
// get type
|
|
core::stringc semanticName = reader->getAttributeValue("semantic");
|
|
for (u32 i=0; inputSemanticNames[i]; ++i)
|
|
{
|
|
if (semanticName == inputSemanticNames[i])
|
|
{
|
|
p.Semantic = (ECOLLADA_INPUT_SEMANTIC)i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// get source
|
|
p.Source = reader->getAttributeValue("source");
|
|
p.Offset = (u32)reader->getAttributeValueAsInt("offset");
|
|
p.Set = (u32)reader->getAttributeValueAsInt("set");
|
|
|
|
// add input
|
|
Inputs.push_back(p);
|
|
}
|
|
|
|
//! parses all collada inputs inside an element and stores them in Inputs
|
|
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);
|
|
}
|
|
else
|
|
if (reader->getNodeType() == io::EXN_ELEMENT_END)
|
|
{
|
|
if (parentName == reader->getNodeName())
|
|
return; // end of parent reached
|
|
}
|
|
|
|
} // end while reader->read();
|
|
}
|
|
|
|
//! parses all collada parameters inside an element and stores them in Parameters
|
|
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)
|
|
{
|
|
// parse param
|
|
SColladaParam p;
|
|
|
|
// get type
|
|
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;
|
|
}
|
|
|
|
// get name
|
|
core::stringc nameName = reader->getAttributeValue("name");
|
|
for (i=0; typeNames[i]; ++i)
|
|
if (nameName == paramNames[i])
|
|
{
|
|
p.Name = (ECOLLADA_PARAM_NAME)i;
|
|
break;
|
|
}
|
|
|
|
// read parameter data inside parameter tags
|
|
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;
|
|
|
|
// TODO: other types of data (ints, bools or whatever)
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// add param
|
|
ColladaParameters.push_back(p);
|
|
}
|
|
else
|
|
if (reader->getNodeType() == io::EXN_ELEMENT_END)
|
|
{
|
|
if (parentName == reader->getNodeName())
|
|
return; // end of parent reached
|
|
}
|
|
|
|
} // end while reader->read();
|
|
}
|
|
|
|
|
|
//! parses a float from a char pointer and moves the pointer
|
|
//! to the end of the parsed float
|
|
inline f32 CColladaFileLoader::readFloat(const c8** p)
|
|
{
|
|
f32 ftmp;
|
|
*p = core::fast_atof_move(*p, ftmp);
|
|
return ftmp;
|
|
}
|
|
|
|
|
|
//! parses an int from a char pointer and moves the pointer to
|
|
//! the end of the parsed float
|
|
inline s32 CColladaFileLoader::readInt(const c8** p)
|
|
{
|
|
return (s32)readFloat(p);
|
|
}
|
|
|
|
|
|
//! places pointer to next begin of a token
|
|
void CColladaFileLoader::findNextNoneWhiteSpace(const c8** start)
|
|
{
|
|
const c8* p = *start;
|
|
|
|
while(*p && (*p==' ' || *p=='\n' || *p=='\r' || *p=='\t'))
|
|
++p;
|
|
|
|
// TODO: skip comments <!-- -->
|
|
|
|
*start = p;
|
|
}
|
|
|
|
|
|
//! reads floats from inside of xml element until end of xml element
|
|
void CColladaFileLoader::readFloatsInsideElement(io::IXMLReaderUTF8* reader, f32* floats, u32 count)
|
|
{
|
|
if (reader->isEmptyElement())
|
|
return;
|
|
|
|
while(reader->read())
|
|
{
|
|
// TODO: check for comments inside the element
|
|
// and ignore them.
|
|
|
|
if (reader->getNodeType() == io::EXN_TEXT)
|
|
{
|
|
// parse float data
|
|
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; // end parsing text
|
|
}
|
|
}
|
|
|
|
|
|
//! reads ints from inside of xml element until end of xml element
|
|
void CColladaFileLoader::readIntsInsideElement(io::IXMLReaderUTF8* reader, s32* ints, u32 count)
|
|
{
|
|
if (reader->isEmptyElement())
|
|
return;
|
|
|
|
while(reader->read())
|
|
{
|
|
// TODO: check for comments inside the element
|
|
// and ignore them.
|
|
|
|
if (reader->getNodeType() == io::EXN_TEXT)
|
|
{
|
|
// parse float data
|
|
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; // end parsing text
|
|
}
|
|
}
|
|
|
|
|
|
video::SColorf CColladaFileLoader::readColorNode(io::IXMLReaderUTF8* reader)
|
|
{
|
|
video::SColorf result;
|
|
if (reader->getNodeType() == io::EXN_ELEMENT &&
|
|
colorNodeName == reader->getNodeName())
|
|
{
|
|
f32 color[4];
|
|
readFloatsInsideElement(reader,color,4);
|
|
result.r = color[0];
|
|
result.g = color[1];
|
|
result.b = color[2];
|
|
result.a = color[3];
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
//! clears all loaded data
|
|
void CColladaFileLoader::clearData()
|
|
{
|
|
// delete all prefabs
|
|
|
|
for (u32 i=0; i<Prefabs.size(); ++i)
|
|
Prefabs[i]->drop();
|
|
|
|
Prefabs.clear();
|
|
|
|
// clear all parameters
|
|
ColladaParameters.clear();
|
|
|
|
// clear all materials
|
|
Images.clear();
|
|
|
|
// clear all materials
|
|
Textures.clear();
|
|
|
|
// clear all materials
|
|
Materials.clear();
|
|
|
|
// clear all inputs
|
|
Inputs.clear();
|
|
|
|
// clear all effects
|
|
Effects.clear();
|
|
|
|
// clear all the materials to bind
|
|
MaterialsToBind.clear();
|
|
MeshesToBind.clear();
|
|
}
|
|
|
|
|
|
//! changes the XML URI into an internal id
|
|
void CColladaFileLoader::uriToId(core::stringc& str)
|
|
{
|
|
// currently, we only remove the # from the begin if there
|
|
// because we simply don't support referencing other files.
|
|
if (!str.size())
|
|
return;
|
|
|
|
if (str[0] == '#')
|
|
str.erase(0);
|
|
}
|
|
|
|
|
|
//! read Collada Id, uses id or name if id is missing
|
|
core::stringc CColladaFileLoader::readId(io::IXMLReaderUTF8* reader)
|
|
{
|
|
core::stringc id = reader->getAttributeValue("id");
|
|
if (id.size()==0)
|
|
id = reader->getAttributeValue("name");
|
|
return id;
|
|
}
|
|
|
|
|
|
//! create an Irrlicht texture from the reference
|
|
video::ITexture* CColladaFileLoader::getTextureFromImage(core::stringc uri)
|
|
{
|
|
#ifdef COLLADA_READER_DEBUG
|
|
os::Printer::log("COLLADA searching texture", uri.c_str());
|
|
#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.c_str()))
|
|
return driver->getTexture(Images[i].Source.c_str());
|
|
return driver->getTexture((FileSystem->getFileDir(CurrentlyLoadingMesh)+"/"+Images[i].Source).c_str());
|
|
}
|
|
else
|
|
if (Images[i].Source.size())
|
|
{
|
|
//const u32 size = Images[i].Dimension.getArea();
|
|
const u32 size = Images[i].Dimension.Width * Images[i].Dimension.Height;;
|
|
u32* data = new u32[size]; // we assume RGBA
|
|
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;
|
|
}
|
|
|
|
|
|
//! read a parameter and value
|
|
void CColladaFileLoader::readParameter(io::IXMLReaderUTF8* reader)
|
|
{
|
|
#ifdef COLLADA_READER_DEBUG
|
|
os::Printer::log("COLLADA reading parameter");
|
|
#endif
|
|
|
|
// if it's a new parameter
|
|
if (newParamName == reader->getNodeName())
|
|
{
|
|
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);
|
|
// Parameters.addVector2d(name.c_str(), core::vector2df(f[0],f[1]));
|
|
}
|
|
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(reader->getNodeType() == io::EXN_ELEMENT_END)
|
|
{
|
|
if (newParamName == reader->getNodeName())
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
} // end namespace scene
|
|
} // end namespace irr
|
|
|
|
#endif // _IRR_COMPILE_WITH_COLLADA_LOADER_
|
|
|