commit
d3ee157342
|
@ -43,6 +43,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
* @brief Implementation of the Irr importer class
|
* @brief Implementation of the Irr importer class
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "assimp/Exceptional.h"
|
||||||
|
#include "assimp/StringComparison.h"
|
||||||
#ifndef ASSIMP_BUILD_NO_IRR_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_IRR_IMPORTER
|
||||||
|
|
||||||
#include "AssetLib/Irr/IRRLoader.h"
|
#include "AssetLib/Irr/IRRLoader.h"
|
||||||
|
@ -62,8 +64,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include <assimp/DefaultLogger.hpp>
|
#include <assimp/DefaultLogger.hpp>
|
||||||
#include <assimp/IOSystem.hpp>
|
#include <assimp/IOSystem.hpp>
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
using namespace Assimp;
|
using namespace Assimp;
|
||||||
|
|
||||||
static const aiImporterDesc desc = {
|
static const aiImporterDesc desc = {
|
||||||
|
@ -380,7 +380,6 @@ void IRRImporter::ComputeAnimations(Node *root, aiNode *real, std::vector<aiNode
|
||||||
if (360 == lcm)
|
if (360 == lcm)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
// find out how many time units we'll need for the finest
|
// find out how many time units we'll need for the finest
|
||||||
// track (in seconds) - this defines the number of output
|
// track (in seconds) - this defines the number of output
|
||||||
// keys (fps * seconds)
|
// keys (fps * seconds)
|
||||||
|
@ -836,175 +835,164 @@ void IRRImporter::GenerateGraph(Node *root, aiNode *rootOut, aiScene *scene,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
void IRRImporter::ParseNodeAttributes(pugi::xml_node &attributesNode, IRRImporter::Node *nd, BatchLoader &batch) {
|
||||||
// Imports the given file into the given scene structure.
|
ai_assert(!ASSIMP_stricmp(attributesNode.name(), "attributes")); // Node must be <attributes>
|
||||||
void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
|
ai_assert(nd != nullptr); // dude
|
||||||
std::unique_ptr<IOStream> file(pIOHandler->Open(pFile));
|
|
||||||
|
|
||||||
// Check whether we can read from the file
|
// Big switch statement that tests for various tags inside <attributes>
|
||||||
if (file == nullptr) {
|
// and applies them to nd
|
||||||
throw DeadlyImportError("Failed to open IRR file ", pFile);
|
// I don't believe nodes have boolean attributes
|
||||||
|
for (pugi::xml_node &attribute : attributesNode.children()) {
|
||||||
|
if (attribute.type() != pugi::node_element) continue;
|
||||||
|
if (!ASSIMP_stricmp(attribute.name(), "vector3d")) { // <vector3d />
|
||||||
|
VectorProperty prop;
|
||||||
|
ReadVectorProperty(prop, attribute);
|
||||||
|
if (prop.name == "Position") {
|
||||||
|
nd->position = prop.value;
|
||||||
|
} else if (prop.name == "Rotation") {
|
||||||
|
nd->rotation = prop.value;
|
||||||
|
} else if (prop.name == "Scale") {
|
||||||
|
nd->scaling = prop.value;
|
||||||
|
} else if (Node::CAMERA == nd->type) {
|
||||||
|
aiCamera *cam = cameras.back();
|
||||||
|
if (prop.name == "Target") {
|
||||||
|
cam->mLookAt = prop.value;
|
||||||
|
} else if (prop.name == "UpVector") {
|
||||||
|
cam->mUp = prop.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct the irrXML parser
|
|
||||||
XmlParser st;
|
|
||||||
if (!st.parse( file.get() )) {
|
|
||||||
throw DeadlyImportError("XML parse error while loading IRR file ", pFile);
|
|
||||||
}
|
}
|
||||||
pugi::xml_node rootElement = st.getRootNode();
|
} else if (!ASSIMP_stricmp(attribute.name(), "float")) { // <float />
|
||||||
|
FloatProperty prop;
|
||||||
// The root node of the scene
|
ReadFloatProperty(prop, attribute);
|
||||||
Node *root = new Node(Node::DUMMY);
|
if (prop.name == "FramesPerSecond" && Node::ANIMMESH == nd->type) {
|
||||||
root->parent = nullptr;
|
nd->framesPerSecond = prop.value;
|
||||||
root->name = "<IRRSceneRoot>";
|
} else if (Node::CAMERA == nd->type) {
|
||||||
|
/* This is the vertical, not the horizontal FOV.
|
||||||
// Current node parent
|
* We need to compute the right FOV from the
|
||||||
Node *curParent = root;
|
* screen aspect which we don't know yet.
|
||||||
|
|
||||||
// Scene-graph node we're currently working on
|
|
||||||
Node *curNode = nullptr;
|
|
||||||
|
|
||||||
// List of output cameras
|
|
||||||
std::vector<aiCamera *> cameras;
|
|
||||||
|
|
||||||
// List of output lights
|
|
||||||
std::vector<aiLight *> lights;
|
|
||||||
|
|
||||||
// Batch loader used to load external models
|
|
||||||
BatchLoader batch(pIOHandler);
|
|
||||||
//batch.SetBasePath(pFile);
|
|
||||||
|
|
||||||
cameras.reserve(5);
|
|
||||||
lights.reserve(5);
|
|
||||||
|
|
||||||
bool inMaterials = false, inAnimator = false;
|
|
||||||
unsigned int guessedAnimCnt = 0, guessedMeshCnt = 0, guessedMatCnt = 0;
|
|
||||||
|
|
||||||
// Parse the XML file
|
|
||||||
|
|
||||||
//while (reader->read()) {
|
|
||||||
for (pugi::xml_node child : rootElement.children())
|
|
||||||
switch (child.type()) {
|
|
||||||
case pugi::node_element:
|
|
||||||
if (!ASSIMP_stricmp(child.name(), "node")) {
|
|
||||||
// ***********************************************************************
|
|
||||||
/* What we're going to do with the node depends
|
|
||||||
* on its type:
|
|
||||||
*
|
|
||||||
* "mesh" - Load a mesh from an external file
|
|
||||||
* "cube" - Generate a cube
|
|
||||||
* "skybox" - Generate a skybox
|
|
||||||
* "light" - A light source
|
|
||||||
* "sphere" - Generate a sphere mesh
|
|
||||||
* "animatedMesh" - Load an animated mesh from an external file
|
|
||||||
* and join its animation channels with ours.
|
|
||||||
* "empty" - A dummy node
|
|
||||||
* "camera" - A camera
|
|
||||||
* "terrain" - a terrain node (data comes from a heightmap)
|
|
||||||
* "billboard", ""
|
|
||||||
*
|
|
||||||
* Each of these nodes can be animated and all can have multiple
|
|
||||||
* materials assigned (except lights, cameras and dummies, of course).
|
|
||||||
*/
|
*/
|
||||||
// ***********************************************************************
|
if (prop.name == "Fovy") {
|
||||||
//const char *sz = reader->getAttributeValueSafe("type");
|
cameras.back()->mHorizontalFOV = prop.value;
|
||||||
pugi::xml_attribute attrib = child.attribute("type");
|
} else if (prop.name == "Aspect") {
|
||||||
Node *nd;
|
cameras.back()->mAspect = prop.value;
|
||||||
if (!ASSIMP_stricmp(attrib.name(), "mesh") || !ASSIMP_stricmp(attrib.name(), "octTree")) {
|
} else if (prop.name == "ZNear") {
|
||||||
// OctTree's and meshes are treated equally
|
cameras.back()->mClipPlaneNear = prop.value;
|
||||||
nd = new Node(Node::MESH);
|
} else if (prop.name == "ZFar") {
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "cube")) {
|
cameras.back()->mClipPlaneFar = prop.value;
|
||||||
nd = new Node(Node::CUBE);
|
}
|
||||||
++guessedMeshCnt;
|
} else if (Node::LIGHT == nd->type) {
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "skybox")) {
|
/* Additional light information
|
||||||
nd = new Node(Node::SKYBOX);
|
*/
|
||||||
guessedMeshCnt += 6;
|
if (prop.name == "Attenuation") {
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "camera")) {
|
lights.back()->mAttenuationLinear = prop.value;
|
||||||
nd = new Node(Node::CAMERA);
|
} else if (prop.name == "OuterCone") {
|
||||||
|
lights.back()->mAngleOuterCone = AI_DEG_TO_RAD(prop.value);
|
||||||
|
} else if (prop.name == "InnerCone") {
|
||||||
|
lights.back()->mAngleInnerCone = AI_DEG_TO_RAD(prop.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// radius of the sphere to be generated -
|
||||||
|
// or alternatively, size of the cube
|
||||||
|
else if ((Node::SPHERE == nd->type && prop.name == "Radius") ||
|
||||||
|
(Node::CUBE == nd->type && prop.name == "Size")) {
|
||||||
|
nd->sphereRadius = prop.value;
|
||||||
|
}
|
||||||
|
} else if (!ASSIMP_stricmp(attribute.name(), "int")) { // <int />
|
||||||
|
// Only sphere nodes make use of integer attributes
|
||||||
|
if (Node::SPHERE == nd->type) {
|
||||||
|
IntProperty prop;
|
||||||
|
ReadIntProperty(prop, attribute);
|
||||||
|
if (prop.name == "PolyCountX") {
|
||||||
|
nd->spherePolyCountX = prop.value;
|
||||||
|
} else if (prop.name == "PolyCountY") {
|
||||||
|
nd->spherePolyCountY = prop.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (!ASSIMP_stricmp(attribute.name(), "string") || !ASSIMP_stricmp(attribute.name(), "enum")) { // <string /> or < enum />
|
||||||
|
StringProperty prop;
|
||||||
|
ReadStringProperty(prop, attribute);
|
||||||
|
if (prop.value.length() == 0) continue; // skip empty strings
|
||||||
|
if (prop.name == "Name") {
|
||||||
|
nd->name = prop.value;
|
||||||
|
|
||||||
// Setup a temporary name for the camera
|
/* If we're either a camera or a light source
|
||||||
aiCamera *cam = new aiCamera();
|
* we need to update the name in the aiLight/
|
||||||
cam->mName.Set(nd->name);
|
* aiCamera structure, too.
|
||||||
cameras.push_back(cam);
|
*/
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "light")) {
|
if (Node::CAMERA == nd->type) {
|
||||||
nd = new Node(Node::LIGHT);
|
cameras.back()->mName.Set(prop.value);
|
||||||
|
} else if (Node::LIGHT == nd->type) {
|
||||||
|
lights.back()->mName.Set(prop.value);
|
||||||
|
}
|
||||||
|
} else if (Node::LIGHT == nd->type && "LightType" == prop.name) {
|
||||||
|
if (prop.value == "Spot")
|
||||||
|
lights.back()->mType = aiLightSource_SPOT;
|
||||||
|
else if (prop.value == "Point")
|
||||||
|
lights.back()->mType = aiLightSource_POINT;
|
||||||
|
else if (prop.value == "Directional")
|
||||||
|
lights.back()->mType = aiLightSource_DIRECTIONAL;
|
||||||
|
else {
|
||||||
|
// We won't pass the validation with aiLightSourceType_UNDEFINED,
|
||||||
|
// so we remove the light and replace it with a silly dummy node
|
||||||
|
delete lights.back();
|
||||||
|
lights.pop_back();
|
||||||
|
nd->type = Node::DUMMY;
|
||||||
|
|
||||||
// Setup a temporary name for the light
|
ASSIMP_LOG_ERROR("Ignoring light of unknown type: ", prop.value);
|
||||||
aiLight *cam = new aiLight();
|
}
|
||||||
cam->mName.Set(nd->name);
|
} else if ((prop.name == "Mesh" && Node::MESH == nd->type) ||
|
||||||
lights.push_back(cam);
|
Node::ANIMMESH == nd->type) {
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "sphere")) {
|
/* This is the file name of the mesh - either
|
||||||
nd = new Node(Node::SPHERE);
|
* animated or not. We need to make sure we setup
|
||||||
++guessedMeshCnt;
|
* the correct post-processing settings here.
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "animatedMesh")) {
|
*/
|
||||||
nd = new Node(Node::ANIMMESH);
|
unsigned int pp = 0;
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "empty")) {
|
BatchLoader::PropertyMap map;
|
||||||
nd = new Node(Node::DUMMY);
|
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "terrain")) {
|
/* If the mesh is a static one remove all animations from the impor data
|
||||||
nd = new Node(Node::TERRAIN);
|
*/
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "billBoard")) {
|
if (Node::ANIMMESH != nd->type) {
|
||||||
// We don't support billboards, so ignore them
|
pp |= aiProcess_RemoveComponent;
|
||||||
ASSIMP_LOG_ERROR("IRR: Billboards are not supported by Assimp");
|
SetGenericProperty<int>(map.ints, AI_CONFIG_PP_RVC_FLAGS,
|
||||||
nd = new Node(Node::DUMMY);
|
aiComponent_ANIMATIONS | aiComponent_BONEWEIGHTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: maybe implement the protection against recursive
|
||||||
|
* loading calls directly in BatchLoader? The current
|
||||||
|
* implementation is not absolutely safe. A LWS and an IRR
|
||||||
|
* file referencing each other *could* cause the system to
|
||||||
|
* recurse forever.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const std::string extension = GetExtension(prop.value);
|
||||||
|
if ("irr" == extension) {
|
||||||
|
ASSIMP_LOG_ERROR("IRR: Can't load another IRR file recursively");
|
||||||
} else {
|
} else {
|
||||||
ASSIMP_LOG_WARN("IRR: Found unknown node: ", attrib.name());
|
nd->id = batch.AddLoadRequest(prop.value, pp, &map);
|
||||||
|
nd->meshPath = prop.value;
|
||||||
/* We skip the contents of nodes we don't know.
|
}
|
||||||
* We parse the transformation and all animators
|
}
|
||||||
* and skip the rest.
|
}
|
||||||
*/
|
}
|
||||||
nd = new Node(Node::DUMMY);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Attach the newly created node to the scene-graph
|
|
||||||
*/
|
|
||||||
curNode = nd;
|
|
||||||
nd->parent = curParent;
|
|
||||||
curParent->children.push_back(nd);
|
|
||||||
} else if (!ASSIMP_stricmp(child.name(), "materials")) {
|
|
||||||
inMaterials = true;
|
|
||||||
} else if (!ASSIMP_stricmp(child.name(), "animators")) {
|
|
||||||
inAnimator = true;
|
|
||||||
} else if (!ASSIMP_stricmp(child.name(), "attributes")) {
|
|
||||||
// We should have a valid node here
|
|
||||||
// FIX: no ... the scene root node is also contained in an attributes block
|
|
||||||
if (!curNode) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IRRImporter::ParseAnimators(pugi::xml_node &animatorNode, IRRImporter::Node *nd) {
|
||||||
Animator *curAnim = nullptr;
|
Animator *curAnim = nullptr;
|
||||||
|
// Make empty animator
|
||||||
// Materials can occur for nearly any type of node
|
nd->animators.emplace_back();
|
||||||
if (inMaterials && curNode->type != Node::DUMMY) {
|
curAnim = &nd->animators.back(); // Push it back
|
||||||
// This is a material description - parse it!
|
pugi::xml_node attributes = animatorNode.child("attributes");
|
||||||
curNode->materials.emplace_back();
|
if (!attributes) {
|
||||||
std::pair<aiMaterial *, unsigned int> &p = curNode->materials.back();
|
ASSIMP_LOG_WARN("Animator node does not contain attributes. ");
|
||||||
|
return;
|
||||||
p.first = ParseMaterial(p.second);
|
|
||||||
++guessedMatCnt;
|
|
||||||
continue;
|
|
||||||
} else if (inAnimator) {
|
|
||||||
// This is an animation path - add a new animator
|
|
||||||
// to the list.
|
|
||||||
curNode->animators.emplace_back();
|
|
||||||
curAnim = &curNode->animators.back();
|
|
||||||
|
|
||||||
++guessedAnimCnt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parse all elements in the attributes block
|
for (pugi::xml_node attrib : attributes.children()) {
|
||||||
* and process them.
|
// XML may contain useless noes like CDATA
|
||||||
*/
|
|
||||||
// while (reader->read()) {
|
|
||||||
for (pugi::xml_node attrib : child.children()) {
|
|
||||||
if (attrib.type() == pugi::node_element) {
|
|
||||||
//if (reader->getNodeType() == EXN_ELEMENT) {
|
|
||||||
//if (!ASSIMP_stricmp(reader->getNodeName(), "vector3d")) {
|
|
||||||
if (!ASSIMP_stricmp(attrib.name(), "vector3d")) {
|
if (!ASSIMP_stricmp(attrib.name(), "vector3d")) {
|
||||||
VectorProperty prop;
|
VectorProperty prop;
|
||||||
ReadVectorProperty(prop);
|
ReadVectorProperty(prop, attrib);
|
||||||
|
|
||||||
if (inAnimator) {
|
|
||||||
if (curAnim->type == Animator::ROTATION && prop.name == "Rotation") {
|
if (curAnim->type == Animator::ROTATION && prop.name == "Rotation") {
|
||||||
// We store the rotation euler angles in 'direction'
|
// We store the rotation euler angles in 'direction'
|
||||||
curAnim->direction = prop.value;
|
curAnim->direction = prop.value;
|
||||||
|
@ -1041,36 +1029,20 @@ void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
|
||||||
curAnim->direction = prop.value;
|
curAnim->direction = prop.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (prop.name == "Position") {
|
|
||||||
curNode->position = prop.value;
|
|
||||||
} else if (prop.name == "Rotation") {
|
|
||||||
curNode->rotation = prop.value;
|
|
||||||
} else if (prop.name == "Scale") {
|
|
||||||
curNode->scaling = prop.value;
|
|
||||||
} else if (Node::CAMERA == curNode->type) {
|
|
||||||
aiCamera *cam = cameras.back();
|
|
||||||
if (prop.name == "Target") {
|
|
||||||
cam->mLookAt = prop.value;
|
|
||||||
} else if (prop.name == "UpVector") {
|
|
||||||
cam->mUp = prop.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//} else if (!ASSIMP_stricmp(reader->getNodeName(), "bool")) {
|
//} else if (!ASSIMP_stricmp(reader->getNodeName(), "bool")) {
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "bool")) {
|
} else if (!ASSIMP_stricmp(attrib.name(), "bool")) {
|
||||||
BoolProperty prop;
|
BoolProperty prop;
|
||||||
ReadBoolProperty(prop);
|
ReadBoolProperty(prop, attrib);
|
||||||
|
|
||||||
if (inAnimator && curAnim->type == Animator::FLY_CIRCLE && prop.name == "Loop") {
|
if (curAnim->type == Animator::FLY_CIRCLE && prop.name == "Loop") {
|
||||||
curAnim->loop = prop.value;
|
curAnim->loop = prop.value;
|
||||||
}
|
}
|
||||||
//} else if (!ASSIMP_stricmp(reader->getNodeName(), "float")) {
|
//} else if (!ASSIMP_stricmp(reader->getNodeName(), "float")) {
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "float")) {
|
} else if (!ASSIMP_stricmp(attrib.name(), "float")) {
|
||||||
FloatProperty prop;
|
FloatProperty prop;
|
||||||
ReadFloatProperty(prop);
|
ReadFloatProperty(prop, attrib);
|
||||||
|
|
||||||
if (inAnimator) {
|
|
||||||
// The speed property exists for several animators
|
// The speed property exists for several animators
|
||||||
if (prop.name == "Speed") {
|
if (prop.name == "Speed") {
|
||||||
curAnim->speed = prop.value;
|
curAnim->speed = prop.value;
|
||||||
|
@ -1079,126 +1051,20 @@ void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
|
||||||
} else if (curAnim->type == Animator::FOLLOW_SPLINE && prop.name == "Tightness") {
|
} else if (curAnim->type == Animator::FOLLOW_SPLINE && prop.name == "Tightness") {
|
||||||
curAnim->tightness = prop.value;
|
curAnim->tightness = prop.value;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (prop.name == "FramesPerSecond" && Node::ANIMMESH == curNode->type) {
|
|
||||||
curNode->framesPerSecond = prop.value;
|
|
||||||
} else if (Node::CAMERA == curNode->type) {
|
|
||||||
/* This is the vertical, not the horizontal FOV.
|
|
||||||
* We need to compute the right FOV from the
|
|
||||||
* screen aspect which we don't know yet.
|
|
||||||
*/
|
|
||||||
if (prop.name == "Fovy") {
|
|
||||||
cameras.back()->mHorizontalFOV = prop.value;
|
|
||||||
} else if (prop.name == "Aspect") {
|
|
||||||
cameras.back()->mAspect = prop.value;
|
|
||||||
} else if (prop.name == "ZNear") {
|
|
||||||
cameras.back()->mClipPlaneNear = prop.value;
|
|
||||||
} else if (prop.name == "ZFar") {
|
|
||||||
cameras.back()->mClipPlaneFar = prop.value;
|
|
||||||
}
|
|
||||||
} else if (Node::LIGHT == curNode->type) {
|
|
||||||
/* Additional light information
|
|
||||||
*/
|
|
||||||
if (prop.name == "Attenuation") {
|
|
||||||
lights.back()->mAttenuationLinear = prop.value;
|
|
||||||
} else if (prop.name == "OuterCone") {
|
|
||||||
lights.back()->mAngleOuterCone = AI_DEG_TO_RAD(prop.value);
|
|
||||||
} else if (prop.name == "InnerCone") {
|
|
||||||
lights.back()->mAngleInnerCone = AI_DEG_TO_RAD(prop.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// radius of the sphere to be generated -
|
|
||||||
// or alternatively, size of the cube
|
|
||||||
else if ((Node::SPHERE == curNode->type && prop.name == "Radius") || (Node::CUBE == curNode->type && prop.name == "Size")) {
|
|
||||||
|
|
||||||
curNode->sphereRadius = prop.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//} else if (!ASSIMP_stricmp(reader->getNodeName(), "int")) {
|
//} else if (!ASSIMP_stricmp(reader->getNodeName(), "int")) {
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "int")) {
|
} else if (!ASSIMP_stricmp(attrib.name(), "int")) {
|
||||||
IntProperty prop;
|
IntProperty prop;
|
||||||
ReadIntProperty(prop);
|
ReadIntProperty(prop, attrib);
|
||||||
|
|
||||||
if (inAnimator) {
|
|
||||||
if (curAnim->type == Animator::FLY_STRAIGHT && prop.name == "TimeForWay") {
|
if (curAnim->type == Animator::FLY_STRAIGHT && prop.name == "TimeForWay") {
|
||||||
curAnim->timeForWay = prop.value;
|
curAnim->timeForWay = prop.value;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// sphere polygon numbers in each direction
|
|
||||||
if (Node::SPHERE == curNode->type) {
|
|
||||||
|
|
||||||
if (prop.name == "PolyCountX") {
|
|
||||||
curNode->spherePolyCountX = prop.value;
|
|
||||||
} else if (prop.name == "PolyCountY") {
|
|
||||||
curNode->spherePolyCountY = prop.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//} else if (!ASSIMP_stricmp(reader->getNodeName(), "string") || !ASSIMP_stricmp(reader->getNodeName(), "enum")) {
|
//} else if (!ASSIMP_stricmp(reader->getNodeName(), "string") || !ASSIMP_stricmp(reader->getNodeName(), "enum")) {
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "string") || !ASSIMP_stricmp(attrib.name(), "enum")) {
|
} else if (!ASSIMP_stricmp(attrib.name(), "string") || !ASSIMP_stricmp(attrib.name(), "enum")) {
|
||||||
StringProperty prop;
|
StringProperty prop;
|
||||||
ReadStringProperty(prop);
|
ReadStringProperty(prop, attrib);
|
||||||
if (prop.value.length()) {
|
|
||||||
if (prop.name == "Name") {
|
|
||||||
curNode->name = prop.value;
|
|
||||||
|
|
||||||
/* If we're either a camera or a light source
|
if (prop.name == "Type") {
|
||||||
* we need to update the name in the aiLight/
|
|
||||||
* aiCamera structure, too.
|
|
||||||
*/
|
|
||||||
if (Node::CAMERA == curNode->type) {
|
|
||||||
cameras.back()->mName.Set(prop.value);
|
|
||||||
} else if (Node::LIGHT == curNode->type) {
|
|
||||||
lights.back()->mName.Set(prop.value);
|
|
||||||
}
|
|
||||||
} else if (Node::LIGHT == curNode->type && "LightType" == prop.name) {
|
|
||||||
if (prop.value == "Spot")
|
|
||||||
lights.back()->mType = aiLightSource_SPOT;
|
|
||||||
else if (prop.value == "Point")
|
|
||||||
lights.back()->mType = aiLightSource_POINT;
|
|
||||||
else if (prop.value == "Directional")
|
|
||||||
lights.back()->mType = aiLightSource_DIRECTIONAL;
|
|
||||||
else {
|
|
||||||
// We won't pass the validation with aiLightSourceType_UNDEFINED,
|
|
||||||
// so we remove the light and replace it with a silly dummy node
|
|
||||||
delete lights.back();
|
|
||||||
lights.pop_back();
|
|
||||||
curNode->type = Node::DUMMY;
|
|
||||||
|
|
||||||
ASSIMP_LOG_ERROR("Ignoring light of unknown type: ", prop.value);
|
|
||||||
}
|
|
||||||
} else if ((prop.name == "Mesh" && Node::MESH == curNode->type) ||
|
|
||||||
Node::ANIMMESH == curNode->type) {
|
|
||||||
/* This is the file name of the mesh - either
|
|
||||||
* animated or not. We need to make sure we setup
|
|
||||||
* the correct post-processing settings here.
|
|
||||||
*/
|
|
||||||
unsigned int pp = 0;
|
|
||||||
BatchLoader::PropertyMap map;
|
|
||||||
|
|
||||||
/* If the mesh is a static one remove all animations from the impor data
|
|
||||||
*/
|
|
||||||
if (Node::ANIMMESH != curNode->type) {
|
|
||||||
pp |= aiProcess_RemoveComponent;
|
|
||||||
SetGenericProperty<int>(map.ints, AI_CONFIG_PP_RVC_FLAGS,
|
|
||||||
aiComponent_ANIMATIONS | aiComponent_BONEWEIGHTS);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: maybe implement the protection against recursive
|
|
||||||
* loading calls directly in BatchLoader? The current
|
|
||||||
* implementation is not absolutely safe. A LWS and an IRR
|
|
||||||
* file referencing each other *could* cause the system to
|
|
||||||
* recurse forever.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const std::string extension = GetExtension(prop.value);
|
|
||||||
if ("irr" == extension) {
|
|
||||||
ASSIMP_LOG_ERROR("IRR: Can't load another IRR file recursively");
|
|
||||||
} else {
|
|
||||||
curNode->id = batch.AddLoadRequest(prop.value, pp, &map);
|
|
||||||
curNode->meshPath = prop.value;
|
|
||||||
}
|
|
||||||
} else if (inAnimator && prop.name == "Type") {
|
|
||||||
// type of the animator
|
// type of the animator
|
||||||
if (prop.value == "rotation") {
|
if (prop.value == "rotation") {
|
||||||
curAnim->type = Animator::ROTATION;
|
curAnim->type = Animator::ROTATION;
|
||||||
|
@ -1216,42 +1082,168 @@ void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//} else if (reader->getNodeType() == EXN_ELEMENT_END && !ASSIMP_stricmp(reader->getNodeName(), "attributes")) {
|
|
||||||
} else if (attrib.type() == pugi::node_null && !ASSIMP_stricmp(attrib.name(), "attributes")) {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
/*case EXN_ELEMENT_END:
|
IRRImporter::Node *IRRImporter::ParseNode(pugi::xml_node &node, BatchLoader &batch) {
|
||||||
|
// Parse <node> tags.
|
||||||
|
// <node> tags have various types
|
||||||
|
// <node> tags can contain <attribute>, <material>
|
||||||
|
// they can also contain other <node> tags, (and can reference other files as well?)
|
||||||
|
// ***********************************************************************
|
||||||
|
/* What we're going to do with the node depends
|
||||||
|
* on its type:
|
||||||
|
*
|
||||||
|
* "mesh" - Load a mesh from an external file
|
||||||
|
* "cube" - Generate a cube
|
||||||
|
* "skybox" - Generate a skybox
|
||||||
|
* "light" - A light source
|
||||||
|
* "sphere" - Generate a sphere mesh
|
||||||
|
* "animatedMesh" - Load an animated mesh from an external file
|
||||||
|
* and join its animation channels with ours.
|
||||||
|
* "empty" - A dummy node
|
||||||
|
* "camera" - A camera
|
||||||
|
* "terrain" - a terrain node (data comes from a heightmap)
|
||||||
|
* "billboard", ""
|
||||||
|
*
|
||||||
|
* Each of these nodes can be animated and all can have multiple
|
||||||
|
* materials assigned (except lights, cameras and dummies, of course).
|
||||||
|
* Said materials and animators are all collected at the bottom
|
||||||
|
*/
|
||||||
|
// ***********************************************************************
|
||||||
|
Node *nd;
|
||||||
|
pugi::xml_attribute nodeTypeAttrib = node.attribute("type");
|
||||||
|
if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "mesh") || !ASSIMP_stricmp(nodeTypeAttrib.value(), "octTree")) {
|
||||||
|
// OctTree's and meshes are treated equally
|
||||||
|
nd = new Node(Node::MESH);
|
||||||
|
} else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "cube")) {
|
||||||
|
nd = new Node(Node::CUBE);
|
||||||
|
guessedMeshCnt += 1; // Cube is only one mesh
|
||||||
|
} else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "skybox")) {
|
||||||
|
nd = new Node(Node::SKYBOX);
|
||||||
|
guessedMeshCnt += 6; // Skybox is a box, with 6 meshes?
|
||||||
|
} else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "camera")) {
|
||||||
|
nd = new Node(Node::CAMERA);
|
||||||
|
// Setup a temporary name for the camera
|
||||||
|
aiCamera *cam = new aiCamera();
|
||||||
|
cam->mName.Set(nd->name);
|
||||||
|
cameras.push_back(cam);
|
||||||
|
} else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "light")) {
|
||||||
|
nd = new Node(Node::LIGHT);
|
||||||
|
// Setup a temporary name for the light
|
||||||
|
aiLight *cam = new aiLight();
|
||||||
|
cam->mName.Set(nd->name);
|
||||||
|
lights.push_back(cam);
|
||||||
|
} else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "sphere")) {
|
||||||
|
nd = new Node(Node::SPHERE);
|
||||||
|
guessedMeshCnt += 1;
|
||||||
|
} else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "animatedMesh")) {
|
||||||
|
nd = new Node(Node::ANIMMESH);
|
||||||
|
} else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "empty")) {
|
||||||
|
nd = new Node(Node::DUMMY);
|
||||||
|
} else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "terrain")) {
|
||||||
|
nd = new Node(Node::TERRAIN);
|
||||||
|
} else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "billBoard")) {
|
||||||
|
// We don't support billboards, so ignore them
|
||||||
|
ASSIMP_LOG_ERROR("IRR: Billboards are not supported by Assimp");
|
||||||
|
nd = new Node(Node::DUMMY);
|
||||||
|
} else {
|
||||||
|
ASSIMP_LOG_WARN("IRR: Found unknown node: ", nodeTypeAttrib.value());
|
||||||
|
|
||||||
// If we reached the end of a node, we need to continue processing its parent
|
/* We skip the contents of nodes we don't know.
|
||||||
if (!ASSIMP_stricmp(reader->getNodeName(), "node")) {
|
* We parse the transformation and all animators
|
||||||
if (!curNode) {
|
* and skip the rest.
|
||||||
// currently is no node set. We need to go
|
*/
|
||||||
// back in the node hierarchy
|
nd = new Node(Node::DUMMY);
|
||||||
if (!curParent) {
|
|
||||||
curParent = root;
|
|
||||||
ASSIMP_LOG_ERROR("IRR: Too many closing <node> elements");
|
|
||||||
} else
|
|
||||||
curParent = curParent->parent;
|
|
||||||
} else
|
|
||||||
curNode = nullptr;
|
|
||||||
}
|
}
|
||||||
// clear all flags
|
|
||||||
else if (!ASSIMP_stricmp(reader->getNodeName(), "materials")) {
|
|
||||||
inMaterials = false;
|
|
||||||
} else if (!ASSIMP_stricmp(reader->getNodeName(), "animators")) {
|
|
||||||
inAnimator = false;
|
|
||||||
}
|
|
||||||
break;*/
|
|
||||||
|
|
||||||
default:
|
// TODO: consolidate all into one loop
|
||||||
// GCC complains that not all enumeration values are handled
|
for (pugi::xml_node subNode : node.children()) {
|
||||||
break;
|
// Collect node attributes first
|
||||||
|
if (!ASSIMP_stricmp(subNode.name(), "attributes")) {
|
||||||
|
ParseNodeAttributes(subNode, nd, batch); // Parse attributes into this node
|
||||||
|
} else if (!ASSIMP_stricmp(subNode.name(), "animators")) {
|
||||||
|
// Then parse any animators
|
||||||
|
// All animators should contain an <attributes> tag
|
||||||
|
|
||||||
|
// This is an animation path - add a new animator
|
||||||
|
// to the list.
|
||||||
|
ParseAnimators(subNode, nd); // Function modifies nd's animator vector
|
||||||
|
guessedAnimCnt += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then parse any materials
|
||||||
|
// Materials are available to almost all node types
|
||||||
|
if (nd->type != Node::DUMMY) {
|
||||||
|
if (!ASSIMP_stricmp(subNode.name(), "materials")) {
|
||||||
|
// Parse material description directly
|
||||||
|
// Each material should contain an <attributes> node
|
||||||
|
// with everything specified
|
||||||
|
nd->materials.emplace_back();
|
||||||
|
std::pair<aiMaterial *, unsigned int> &p = nd->materials.back();
|
||||||
|
p.first = ParseMaterial(subNode, p.second);
|
||||||
|
guessedMatCnt += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then parse any child nodes
|
||||||
|
// Attach the newly created node to the scene-graph
|
||||||
|
for (pugi::xml_node child : node.children()) {
|
||||||
|
if (!ASSIMP_stricmp(child.name(), "node")) { // Is a child node
|
||||||
|
Node *childNd = ParseNode(child, batch); // Repeat this function for all children
|
||||||
|
nd->children.push_back(childNd);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return fully specified node
|
||||||
|
return nd;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Imports the given file into the given scene structure.
|
||||||
|
void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
|
||||||
|
std::unique_ptr<IOStream> file(pIOHandler->Open(pFile));
|
||||||
|
// Check whether we can read from the file
|
||||||
|
if (file == nullptr) {
|
||||||
|
throw DeadlyImportError("Failed to open IRR file ", pFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct the irrXML parser
|
||||||
|
XmlParser st;
|
||||||
|
if (!st.parse(file.get())) {
|
||||||
|
throw DeadlyImportError("XML parse error while loading IRR file ", pFile);
|
||||||
|
}
|
||||||
|
pugi::xml_node documentRoot = st.getRootNode();
|
||||||
|
|
||||||
|
// The root node of the scene
|
||||||
|
Node *root = new Node(Node::DUMMY);
|
||||||
|
root->parent = nullptr;
|
||||||
|
root->name = "<IRRSceneRoot>";
|
||||||
|
|
||||||
|
// Batch loader used to load external models
|
||||||
|
BatchLoader batch(pIOHandler);
|
||||||
|
// batch.SetBasePath(pFile);
|
||||||
|
|
||||||
|
cameras.reserve(1); // Probably only one camera in entire scene
|
||||||
|
lights.reserve(5);
|
||||||
|
|
||||||
|
this->guessedAnimCnt = 0;
|
||||||
|
this->guessedMeshCnt = 0;
|
||||||
|
this->guessedMatCnt = 0;
|
||||||
|
|
||||||
|
// Parse the XML
|
||||||
|
// Find the scene root from document root.
|
||||||
|
const pugi::xml_node &sceneRoot = documentRoot.child("irr_scene");
|
||||||
|
if (!sceneRoot) throw new DeadlyImportError("IRR: <irr_scene> not found in file");
|
||||||
|
for (pugi::xml_node &child : sceneRoot.children()) {
|
||||||
|
// XML elements are either nodes, animators, attributes, or materials
|
||||||
|
if (!ASSIMP_stricmp(child.name(), "node")) {
|
||||||
|
// Recursive collect subtree children
|
||||||
|
Node *nd = ParseNode(child, batch);
|
||||||
|
// Attach to root
|
||||||
|
root->children.push_back(nd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//}
|
|
||||||
|
|
||||||
// Now iterate through all cameras and compute their final (horizontal) FOV
|
// Now iterate through all cameras and compute their final (horizontal) FOV
|
||||||
for (aiCamera *cam : cameras) {
|
for (aiCamera *cam : cameras) {
|
||||||
|
@ -1337,8 +1329,7 @@ void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
|
||||||
// Now merge all sub scenes and attach them to the correct
|
// Now merge all sub scenes and attach them to the correct
|
||||||
// attachment points in the scenegraph.
|
// attachment points in the scenegraph.
|
||||||
SceneCombiner::MergeScenes(&pScene, tempScene, attach,
|
SceneCombiner::MergeScenes(&pScene, tempScene, attach,
|
||||||
AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES | (!configSpeedFlag ? (
|
AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES | (!configSpeedFlag ? (AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY | AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES) :
|
||||||
AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY | AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES) :
|
|
||||||
0));
|
0));
|
||||||
|
|
||||||
// If we have no meshes | no materials now set the INCOMPLETE
|
// If we have no meshes | no materials now set the INCOMPLETE
|
||||||
|
|
|
@ -94,18 +94,10 @@ private:
|
||||||
|
|
||||||
} type;
|
} type;
|
||||||
|
|
||||||
explicit Animator(AT t = UNKNOWN)
|
explicit Animator(AT t = UNKNOWN) :
|
||||||
: type (t)
|
type(t), speed(ai_real(0.001)), direction(ai_real(0.0), ai_real(1.0), ai_real(0.0)), circleRadius(ai_real(1.0)), tightness(ai_real(0.5)), loop(true), timeForWay(100) {
|
||||||
, speed ( ai_real( 0.001 ) )
|
|
||||||
, direction ( ai_real( 0.0 ), ai_real( 1.0 ), ai_real( 0.0 ) )
|
|
||||||
, circleRadius ( ai_real( 1.0) )
|
|
||||||
, tightness ( ai_real( 0.5 ) )
|
|
||||||
, loop (true)
|
|
||||||
, timeForWay (100)
|
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// common parameters
|
// common parameters
|
||||||
ai_real speed;
|
ai_real speed;
|
||||||
aiVector3D direction;
|
aiVector3D direction;
|
||||||
|
@ -128,11 +120,9 @@ private:
|
||||||
|
|
||||||
/** Data structure for a scene-graph node in an IRR file
|
/** Data structure for a scene-graph node in an IRR file
|
||||||
*/
|
*/
|
||||||
struct Node
|
struct Node {
|
||||||
{
|
|
||||||
// Type of the node
|
// Type of the node
|
||||||
enum ET
|
enum ET {
|
||||||
{
|
|
||||||
LIGHT,
|
LIGHT,
|
||||||
CUBE,
|
CUBE,
|
||||||
MESH,
|
MESH,
|
||||||
|
@ -144,16 +134,15 @@ private:
|
||||||
ANIMMESH
|
ANIMMESH
|
||||||
} type;
|
} type;
|
||||||
|
|
||||||
explicit Node(ET t)
|
explicit Node(ET t) :
|
||||||
: type (t)
|
type(t), scaling(1.0, 1.0, 1.0) // assume uniform scaling by default
|
||||||
, scaling (1.0,1.0,1.0) // assume uniform scaling by default
|
,
|
||||||
, parent()
|
parent(),
|
||||||
, framesPerSecond (0.0)
|
framesPerSecond(0.0),
|
||||||
, id()
|
id(),
|
||||||
, sphereRadius (1.0)
|
sphereRadius(1.0),
|
||||||
, spherePolyCountX (100)
|
spherePolyCountX(100),
|
||||||
, spherePolyCountY (100)
|
spherePolyCountY(100) {
|
||||||
{
|
|
||||||
|
|
||||||
// Generate a default name for the node
|
// Generate a default name for the node
|
||||||
char buffer[128];
|
char buffer[128];
|
||||||
|
@ -204,8 +193,7 @@ private:
|
||||||
|
|
||||||
/** Data structure for a vertex in an IRR skybox
|
/** Data structure for a vertex in an IRR skybox
|
||||||
*/
|
*/
|
||||||
struct SkyboxVertex
|
struct SkyboxVertex {
|
||||||
{
|
|
||||||
SkyboxVertex() = default;
|
SkyboxVertex() = default;
|
||||||
|
|
||||||
//! Construction from single vertex components
|
//! Construction from single vertex components
|
||||||
|
@ -213,14 +201,29 @@ private:
|
||||||
ai_real nx, ai_real ny, ai_real nz,
|
ai_real nx, ai_real ny, ai_real nz,
|
||||||
ai_real uvx, ai_real uvy)
|
ai_real uvx, ai_real uvy)
|
||||||
|
|
||||||
: position (px,py,pz)
|
:
|
||||||
, normal (nx,ny,nz)
|
position(px, py, pz), normal(nx, ny, nz), uv(uvx, uvy, 0.0) {}
|
||||||
, uv (uvx,uvy,0.0)
|
|
||||||
{}
|
|
||||||
|
|
||||||
aiVector3D position, normal, uv;
|
aiVector3D position, normal, uv;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Parse <node> tag from XML file and extract child node
|
||||||
|
// @param node XML node
|
||||||
|
// @param guessedMeshesContained number of extra guessed meshes
|
||||||
|
IRRImporter::Node *ParseNode(pugi::xml_node &node, BatchLoader& batch);
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Parse <attributes> tags within <node> tags and apply to scene node
|
||||||
|
// @param attributeNode XML child node
|
||||||
|
// @param nd Attributed scene node
|
||||||
|
void ParseNodeAttributes(pugi::xml_node &attributeNode, IRRImporter::Node *nd, BatchLoader& batch);
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Parse an <animator> node and attach an animator to a node
|
||||||
|
// @param animatorNode XML animator node
|
||||||
|
// @param nd Animated scene node
|
||||||
|
void ParseAnimators(pugi::xml_node &animatorNode, IRRImporter::Node *nd);
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/// Fill the scene-graph recursively
|
/// Fill the scene-graph recursively
|
||||||
|
@ -276,6 +279,12 @@ private:
|
||||||
|
|
||||||
/// Configuration option: speed flag was set?
|
/// Configuration option: speed flag was set?
|
||||||
bool configSpeedFlag;
|
bool configSpeedFlag;
|
||||||
|
|
||||||
|
std::vector<aiCamera*> cameras;
|
||||||
|
std::vector<aiLight*> lights;
|
||||||
|
unsigned int guessedMeshCnt;
|
||||||
|
unsigned int guessedMatCnt;
|
||||||
|
unsigned int guessedAnimCnt;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end of namespace Assimp
|
} // end of namespace Assimp
|
||||||
|
|
|
@ -133,6 +133,7 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
|
||||||
meshes.reserve(5);
|
meshes.reserve(5);
|
||||||
|
|
||||||
// temporary data - current mesh buffer
|
// temporary data - current mesh buffer
|
||||||
|
// TODO move all these to inside loop
|
||||||
aiMaterial *curMat = nullptr;
|
aiMaterial *curMat = nullptr;
|
||||||
aiMesh *curMesh = nullptr;
|
aiMesh *curMesh = nullptr;
|
||||||
unsigned int curMatFlags = 0;
|
unsigned int curMatFlags = 0;
|
||||||
|
@ -142,23 +143,28 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
|
||||||
std::vector<aiVector3D> curUVs, curUV2s;
|
std::vector<aiVector3D> curUVs, curUV2s;
|
||||||
|
|
||||||
// some temporary variables
|
// some temporary variables
|
||||||
int textMeaning = 0;
|
// textMeaning is a 15 year old variable, that could've been an enum
|
||||||
int vertexFormat = 0; // 0 = normal; 1 = 2 tcoords, 2 = tangents
|
// int textMeaning = 0; // 0=none? 1=vertices 2=indices
|
||||||
|
// int vertexFormat = 0; // 0 = normal; 1 = 2 tcoords, 2 = tangents
|
||||||
bool useColors = false;
|
bool useColors = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
** irrmesh files have a top level <mesh> owning multiple <buffer> nodes.
|
||||||
|
** Each <buffer> contains <material>, <vertices>, and <indices>
|
||||||
|
** <material> tags here directly owns the material data specs
|
||||||
|
** <vertices> are a vertex per line, contains position, UV1 coords, maybe UV2, normal, tangent, bitangent
|
||||||
|
** <boundingbox> is ignored, I think assimp recalculates those?
|
||||||
|
*/
|
||||||
|
|
||||||
// Parse the XML file
|
// Parse the XML file
|
||||||
for (pugi::xml_node child : root.children()) {
|
pugi::xml_node const &meshNode = root.child("mesh");
|
||||||
if (child.type() == pugi::node_element) {
|
for (pugi::xml_node bufferNode : meshNode.children()) {
|
||||||
if (!ASSIMP_stricmp(child.name(), "buffer") && (curMat || curMesh)) {
|
if (ASSIMP_stricmp(bufferNode.name(), "buffer")) {
|
||||||
// end of previous buffer. A material and a mesh should be there
|
// Might be a useless warning
|
||||||
if (!curMat || !curMesh) {
|
ASSIMP_LOG_WARN("IRRMESH: Ignoring non buffer node <", bufferNode.name(), "> in mesh declaration");
|
||||||
ASSIMP_LOG_ERROR("IRRMESH: A buffer must contain a mesh and a material");
|
continue;
|
||||||
releaseMaterial(&curMat);
|
|
||||||
releaseMesh(&curMesh);
|
|
||||||
} else {
|
|
||||||
materials.push_back(curMat);
|
|
||||||
meshes.push_back(curMesh);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
curMat = nullptr;
|
curMat = nullptr;
|
||||||
curMesh = nullptr;
|
curMesh = nullptr;
|
||||||
|
|
||||||
|
@ -169,42 +175,47 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
|
||||||
curUVs.clear();
|
curUVs.clear();
|
||||||
curTangents.clear();
|
curTangents.clear();
|
||||||
curBitangents.clear();
|
curBitangents.clear();
|
||||||
}
|
|
||||||
|
|
||||||
if (!ASSIMP_stricmp(child.name(), "material")) {
|
// TODO ensure all three nodes are present and populated
|
||||||
if (curMat) {
|
// before allocating everything
|
||||||
|
|
||||||
|
// Get first material node
|
||||||
|
pugi::xml_node materialNode = bufferNode.child("material");
|
||||||
|
if (materialNode) {
|
||||||
|
curMat = ParseMaterial(materialNode, curMatFlags);
|
||||||
|
// Warn if there's more materials
|
||||||
|
if (materialNode.next_sibling("material")) {
|
||||||
ASSIMP_LOG_WARN("IRRMESH: Only one material description per buffer, please");
|
ASSIMP_LOG_WARN("IRRMESH: Only one material description per buffer, please");
|
||||||
releaseMaterial(&curMat);
|
|
||||||
}
|
}
|
||||||
curMat = ParseMaterial(curMatFlags);
|
} else {
|
||||||
}
|
ASSIMP_LOG_ERROR("IRRMESH: Buffer must contain one material");
|
||||||
/* no else here! */ if (!ASSIMP_stricmp(child.name(), "vertices")) {
|
|
||||||
pugi::xml_attribute attr = child.attribute("vertexCount");
|
|
||||||
int num = attr.as_int();
|
|
||||||
//int num = reader->getAttributeValueAsInt("vertexCount");
|
|
||||||
|
|
||||||
if (!num) {
|
|
||||||
// This is possible ... remove the mesh from the list and skip further reading
|
|
||||||
ASSIMP_LOG_WARN("IRRMESH: Found mesh with zero vertices");
|
|
||||||
|
|
||||||
releaseMaterial(&curMat);
|
|
||||||
releaseMesh(&curMesh);
|
|
||||||
textMeaning = 0;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
curVertices.reserve(num);
|
// Get first vertices node
|
||||||
curNormals.reserve(num);
|
pugi::xml_node verticesNode = bufferNode.child("vertices");
|
||||||
curColors.reserve(num);
|
if (verticesNode) {
|
||||||
curUVs.reserve(num);
|
pugi::xml_attribute vertexCountAttrib = verticesNode.attribute("vertexCount");
|
||||||
|
int vertexCount = vertexCountAttrib.as_int();
|
||||||
|
if (vertexCount == 0) {
|
||||||
|
// This is possible ... remove the mesh from the list and skip further reading
|
||||||
|
ASSIMP_LOG_WARN("IRRMESH: Found mesh with zero vertices");
|
||||||
|
releaseMaterial(&curMat);
|
||||||
|
// releaseMesh(&curMesh);
|
||||||
|
continue; // Bail out early
|
||||||
|
};
|
||||||
|
|
||||||
|
curVertices.reserve(vertexCount);
|
||||||
|
curNormals.reserve(vertexCount);
|
||||||
|
curColors.reserve(vertexCount);
|
||||||
|
curUVs.reserve(vertexCount);
|
||||||
|
|
||||||
|
VertexFormat vertexFormat;
|
||||||
// Determine the file format
|
// Determine the file format
|
||||||
//const char *t = reader->getAttributeValueSafe("type");
|
pugi::xml_attribute typeAttrib = verticesNode.attribute("type");
|
||||||
pugi::xml_attribute t = child.attribute("type");
|
if (!ASSIMP_stricmp("2tcoords", typeAttrib.value())) {
|
||||||
if (!ASSIMP_stricmp("2tcoords", t.name())) {
|
curUV2s.reserve(vertexCount);
|
||||||
curUV2s.reserve(num);
|
vertexFormat = VertexFormat::t2coord;
|
||||||
vertexFormat = 1;
|
|
||||||
|
|
||||||
if (curMatFlags & AI_IRRMESH_EXTRA_2ND_TEXTURE) {
|
if (curMatFlags & AI_IRRMESH_EXTRA_2ND_TEXTURE) {
|
||||||
// *********************************************************
|
// *********************************************************
|
||||||
// We have a second texture! So use this UV channel
|
// We have a second texture! So use this UV channel
|
||||||
|
@ -223,29 +234,38 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
|
||||||
mat->AddProperty(&idx, 1, AI_MATKEY_UVWSRC_DIFFUSE(1));
|
mat->AddProperty(&idx, 1, AI_MATKEY_UVWSRC_DIFFUSE(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (!ASSIMP_stricmp("tangents", t.name())) {
|
} else if (!ASSIMP_stricmp("tangents", typeAttrib.value())) {
|
||||||
curTangents.reserve(num);
|
curTangents.reserve(vertexCount);
|
||||||
curBitangents.reserve(num);
|
curBitangents.reserve(vertexCount);
|
||||||
vertexFormat = 2;
|
vertexFormat = VertexFormat::tangent;
|
||||||
} else if (ASSIMP_stricmp("standard", t.name())) {
|
} else if (!ASSIMP_stricmp("standard", typeAttrib.value())) {
|
||||||
|
vertexFormat = VertexFormat::standard;
|
||||||
|
} else {
|
||||||
|
// Unsupported format, discard whole buffer/mesh
|
||||||
|
// Assuming we have a correct material, then release it
|
||||||
|
// We don't have a correct mesh for sure here
|
||||||
releaseMaterial(&curMat);
|
releaseMaterial(&curMat);
|
||||||
ASSIMP_LOG_WARN("IRRMESH: Unknown vertex format");
|
ASSIMP_LOG_ERROR("IRRMESH: Unknown vertex format");
|
||||||
} else
|
continue; // Skip rest of buffer
|
||||||
vertexFormat = 0;
|
};
|
||||||
textMeaning = 1;
|
|
||||||
} else if (!ASSIMP_stricmp(child.name(), "indices")) {
|
// We know what format buffer is, collect numbers
|
||||||
if (curVertices.empty() && curMat) {
|
ParseBufferVertices(verticesNode.text().get(), vertexFormat,
|
||||||
releaseMaterial(&curMat);
|
curVertices, curNormals,
|
||||||
throw DeadlyImportError("IRRMESH: indices must come after vertices");
|
curTangents, curBitangents,
|
||||||
|
curUVs, curUV2s, curColors, useColors);
|
||||||
}
|
}
|
||||||
|
|
||||||
textMeaning = 2;
|
// Get indices
|
||||||
|
// At this point we have some vertices and a valid material
|
||||||
|
// Collect indices and create aiMesh at the same time
|
||||||
|
pugi::xml_node indicesNode = bufferNode.child("indices");
|
||||||
|
if (indicesNode) {
|
||||||
// start a new mesh
|
// start a new mesh
|
||||||
curMesh = new aiMesh();
|
curMesh = new aiMesh();
|
||||||
|
|
||||||
// allocate storage for all faces
|
// allocate storage for all faces
|
||||||
pugi::xml_attribute attr = child.attribute("indexCount");
|
pugi::xml_attribute attr = indicesNode.attribute("indexCount");
|
||||||
curMesh->mNumVertices = attr.as_int();
|
curMesh->mNumVertices = attr.as_int();
|
||||||
if (!curMesh->mNumVertices) {
|
if (!curMesh->mNumVertices) {
|
||||||
// This is possible ... remove the mesh from the list and skip further reading
|
// This is possible ... remove the mesh from the list and skip further reading
|
||||||
|
@ -256,9 +276,7 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
|
||||||
|
|
||||||
// material - away
|
// material - away
|
||||||
releaseMaterial(&curMat);
|
releaseMaterial(&curMat);
|
||||||
|
continue; // Go to next buffer
|
||||||
textMeaning = 0;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (curMesh->mNumVertices % 3) {
|
if (curMesh->mNumVertices % 3) {
|
||||||
|
@ -293,107 +311,6 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
|
||||||
if (curUV2s.size() == curVertices.size()) {
|
if (curUV2s.size() == curVertices.size()) {
|
||||||
curMesh->mTextureCoords[1] = new aiVector3D[curMesh->mNumVertices];
|
curMesh->mTextureCoords[1] = new aiVector3D[curMesh->mNumVertices];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
//break;
|
|
||||||
|
|
||||||
//case EXN_TEXT: {
|
|
||||||
const char *sz = child.child_value();
|
|
||||||
if (textMeaning == 1) {
|
|
||||||
textMeaning = 0;
|
|
||||||
|
|
||||||
// read vertices
|
|
||||||
do {
|
|
||||||
SkipSpacesAndLineEnd(&sz);
|
|
||||||
aiVector3D temp;
|
|
||||||
aiColor4D c;
|
|
||||||
|
|
||||||
// Read the vertex position
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.z);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
curVertices.push_back(temp);
|
|
||||||
|
|
||||||
// Read the vertex normals
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.z);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
curNormals.push_back(temp);
|
|
||||||
|
|
||||||
// read the vertex colors
|
|
||||||
uint32_t clr = strtoul16(sz, &sz);
|
|
||||||
ColorFromARGBPacked(clr, c);
|
|
||||||
|
|
||||||
if (!curColors.empty() && c != *(curColors.end() - 1))
|
|
||||||
useColors = true;
|
|
||||||
|
|
||||||
curColors.push_back(c);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
|
|
||||||
// read the first UV coordinate set
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
temp.z = 0.f;
|
|
||||||
temp.y = 1.f - temp.y; // DX to OGL
|
|
||||||
curUVs.push_back(temp);
|
|
||||||
|
|
||||||
// read the (optional) second UV coordinate set
|
|
||||||
if (vertexFormat == 1) {
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
|
|
||||||
temp.y = 1.f - temp.y; // DX to OGL
|
|
||||||
curUV2s.push_back(temp);
|
|
||||||
}
|
|
||||||
// read optional tangent and bitangent vectors
|
|
||||||
else if (vertexFormat == 2) {
|
|
||||||
// tangents
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.z);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
temp.y *= -1.0f;
|
|
||||||
curTangents.push_back(temp);
|
|
||||||
|
|
||||||
// bitangents
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.z);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
|
|
||||||
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
|
|
||||||
SkipSpaces(&sz);
|
|
||||||
temp.y *= -1.0f;
|
|
||||||
curBitangents.push_back(temp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* IMPORTANT: We assume that each vertex is specified in one
|
|
||||||
line. So we can skip the rest of the line - unknown vertex
|
|
||||||
elements are ignored.
|
|
||||||
*/
|
|
||||||
|
|
||||||
while (SkipLine(&sz));
|
|
||||||
} else if (textMeaning == 2) {
|
|
||||||
textMeaning = 0;
|
|
||||||
|
|
||||||
// read indices
|
// read indices
|
||||||
aiFace *curFace = curMesh->mFaces;
|
aiFace *curFace = curMesh->mFaces;
|
||||||
|
@ -409,24 +326,33 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
|
||||||
|
|
||||||
unsigned int curIdx = 0;
|
unsigned int curIdx = 0;
|
||||||
unsigned int total = 0;
|
unsigned int total = 0;
|
||||||
|
|
||||||
|
// NOTE this might explode for UTF-16 and wchars
|
||||||
|
const char *sz = indicesNode.text().get();
|
||||||
|
// For each index loop over aiMesh faces
|
||||||
while (SkipSpacesAndLineEnd(&sz)) {
|
while (SkipSpacesAndLineEnd(&sz)) {
|
||||||
if (curFace >= faceEnd) {
|
if (curFace >= faceEnd) {
|
||||||
ASSIMP_LOG_ERROR("IRRMESH: Too many indices");
|
ASSIMP_LOG_ERROR("IRRMESH: Too many indices");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// if new face
|
||||||
if (!curIdx) {
|
if (!curIdx) {
|
||||||
curFace->mNumIndices = 3;
|
curFace->mNumIndices = 3;
|
||||||
curFace->mIndices = new unsigned int[3];
|
curFace->mIndices = new unsigned int[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read index base 10
|
||||||
|
// function advances the pointer
|
||||||
unsigned int idx = strtoul10(sz, &sz);
|
unsigned int idx = strtoul10(sz, &sz);
|
||||||
if (idx >= curVertices.size()) {
|
if (idx >= curVertices.size()) {
|
||||||
ASSIMP_LOG_ERROR("IRRMESH: Index out of range");
|
ASSIMP_LOG_ERROR("IRRMESH: Index out of range");
|
||||||
idx = 0;
|
idx = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// make up our own indices?
|
||||||
curFace->mIndices[curIdx] = total++;
|
curFace->mIndices[curIdx] = total++;
|
||||||
|
|
||||||
|
// Copy over data to aiMesh
|
||||||
*pcV++ = curVertices[idx];
|
*pcV++ = curVertices[idx];
|
||||||
if (pcN) *pcN++ = curNormals[idx];
|
if (pcN) *pcN++ = curNormals[idx];
|
||||||
if (pcT) *pcT++ = curTangents[idx];
|
if (pcT) *pcT++ = curTangents[idx];
|
||||||
|
@ -435,14 +361,16 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
|
||||||
if (pcT0) *pcT0++ = curUVs[idx];
|
if (pcT0) *pcT0++ = curUVs[idx];
|
||||||
if (pcT1) *pcT1++ = curUV2s[idx];
|
if (pcT1) *pcT1++ = curUV2s[idx];
|
||||||
|
|
||||||
|
// start new face
|
||||||
if (++curIdx == 3) {
|
if (++curIdx == 3) {
|
||||||
++curFace;
|
++curFace;
|
||||||
curIdx = 0;
|
curIdx = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// We should be at the end of mFaces
|
||||||
if (curFace != faceEnd)
|
if (curFace != faceEnd)
|
||||||
ASSIMP_LOG_ERROR("IRRMESH: Not enough indices");
|
ASSIMP_LOG_ERROR("IRRMESH: Not enough indices");
|
||||||
|
}
|
||||||
|
|
||||||
// Finish processing the mesh - do some small material workarounds
|
// Finish processing the mesh - do some small material workarounds
|
||||||
if (curMatFlags & AI_IRRMESH_MAT_trans_vertex_alpha && !useColors) {
|
if (curMatFlags & AI_IRRMESH_MAT_trans_vertex_alpha && !useColors) {
|
||||||
|
@ -451,12 +379,9 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
|
||||||
aiMaterial *mat = (aiMaterial *)curMat;
|
aiMaterial *mat = (aiMaterial *)curMat;
|
||||||
mat->AddProperty(&curColors[0].a, 1, AI_MATKEY_OPACITY);
|
mat->AddProperty(&curColors[0].a, 1, AI_MATKEY_OPACITY);
|
||||||
}
|
}
|
||||||
}
|
// textMeaning = 2;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// End of the last buffer. A material and a mesh should be there
|
// end of previous buffer. A material and a mesh should be there
|
||||||
if (curMat || curMesh) {
|
|
||||||
if (!curMat || !curMesh) {
|
if (!curMat || !curMesh) {
|
||||||
ASSIMP_LOG_ERROR("IRRMESH: A buffer must contain a mesh and a material");
|
ASSIMP_LOG_ERROR("IRRMESH: A buffer must contain a mesh and a material");
|
||||||
releaseMaterial(&curMat);
|
releaseMaterial(&curMat);
|
||||||
|
@ -467,7 +392,8 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (materials.empty()) {
|
// If one is empty then so is the other
|
||||||
|
if (materials.empty() || meshes.empty()) {
|
||||||
throw DeadlyImportError("IRRMESH: Unable to read a mesh from this file");
|
throw DeadlyImportError("IRRMESH: Unable to read a mesh from this file");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -492,7 +418,105 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
|
||||||
|
|
||||||
for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
|
for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
|
||||||
pScene->mRootNode->mMeshes[i] = i;
|
pScene->mRootNode->mMeshes[i] = i;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IRRMeshImporter::ParseBufferVertices(const char *sz, VertexFormat vertexFormat,
|
||||||
|
std::vector<aiVector3D> &vertices, std::vector<aiVector3D> &normals,
|
||||||
|
std::vector<aiVector3D> &tangents, std::vector<aiVector3D> &bitangents,
|
||||||
|
std::vector<aiVector3D> &UVs, std::vector<aiVector3D> &UV2s,
|
||||||
|
std::vector<aiColor4D> &colors, bool &useColors) {
|
||||||
|
// read vertices
|
||||||
|
do {
|
||||||
|
SkipSpacesAndLineEnd(&sz);
|
||||||
|
aiVector3D temp;
|
||||||
|
aiColor4D c;
|
||||||
|
|
||||||
|
// Read the vertex position
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.z);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
vertices.push_back(temp);
|
||||||
|
|
||||||
|
// Read the vertex normals
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.z);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
normals.push_back(temp);
|
||||||
|
|
||||||
|
// read the vertex colors
|
||||||
|
uint32_t clr = strtoul16(sz, &sz);
|
||||||
|
ColorFromARGBPacked(clr, c);
|
||||||
|
|
||||||
|
// If we're pushing more than one distinct color
|
||||||
|
if (!colors.empty() && c != *(colors.end() - 1))
|
||||||
|
useColors = true;
|
||||||
|
|
||||||
|
colors.push_back(c);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
|
||||||
|
// read the first UV coordinate set
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
temp.z = 0.f;
|
||||||
|
temp.y = 1.f - temp.y; // DX to OGL
|
||||||
|
UVs.push_back(temp);
|
||||||
|
|
||||||
|
// NOTE these correspond to specific S3DVertex* structs in irr sourcecode
|
||||||
|
// So by definition, all buffers have either UV2 or tangents or neither
|
||||||
|
// read the (optional) second UV coordinate set
|
||||||
|
if (vertexFormat == VertexFormat::t2coord) {
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
|
||||||
|
temp.y = 1.f - temp.y; // DX to OGL
|
||||||
|
UV2s.push_back(temp);
|
||||||
|
}
|
||||||
|
// read optional tangent and bitangent vectors
|
||||||
|
else if (vertexFormat == VertexFormat::tangent) {
|
||||||
|
// tangents
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.z);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
temp.y *= -1.0f;
|
||||||
|
tangents.push_back(temp);
|
||||||
|
|
||||||
|
// bitangents
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.z);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
|
||||||
|
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
|
||||||
|
SkipSpaces(&sz);
|
||||||
|
temp.y *= -1.0f;
|
||||||
|
bitangents.push_back(temp);
|
||||||
|
}
|
||||||
|
} while (SkipLine(&sz));
|
||||||
|
/* IMPORTANT: We assume that each vertex is specified in one
|
||||||
|
line. So we can skip the rest of the line - unknown vertex
|
||||||
|
elements are ignored.
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // !! ASSIMP_BUILD_NO_IRRMESH_IMPORTER
|
#endif // !! ASSIMP_BUILD_NO_IRRMESH_IMPORTER
|
||||||
|
|
|
@ -85,6 +85,19 @@ protected:
|
||||||
*/
|
*/
|
||||||
void InternReadFile(const std::string &pFile, aiScene *pScene,
|
void InternReadFile(const std::string &pFile, aiScene *pScene,
|
||||||
IOSystem *pIOHandler) override;
|
IOSystem *pIOHandler) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum class VertexFormat {
|
||||||
|
standard = 0, // "standard" - also noted as 'normal' format elsewhere
|
||||||
|
t2coord = 1, // "2tcoord" - standard + 2 UV maps
|
||||||
|
tangent = 2, // "tangents" - standard + tangents and bitangents
|
||||||
|
};
|
||||||
|
|
||||||
|
void ParseBufferVertices(const char *sz, VertexFormat vertexFormat,
|
||||||
|
std::vector<aiVector3D> &vertices, std::vector<aiVector3D> &normals,
|
||||||
|
std::vector<aiVector3D> &tangents, std::vector<aiVector3D> &bitangents,
|
||||||
|
std::vector<aiVector3D> &UVs, std::vector<aiVector3D> &UV2s,
|
||||||
|
std::vector<aiColor4D> &colors, bool &useColors);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end of namespace Assimp
|
} // end of namespace Assimp
|
||||||
|
|
|
@ -49,8 +49,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include "IRRShared.h"
|
#include "IRRShared.h"
|
||||||
#include <assimp/ParsingUtils.h>
|
#include <assimp/ParsingUtils.h>
|
||||||
#include <assimp/fast_atof.h>
|
#include <assimp/fast_atof.h>
|
||||||
#include <assimp/DefaultLogger.hpp>
|
|
||||||
#include <assimp/material.h>
|
#include <assimp/material.h>
|
||||||
|
#include <assimp/DefaultLogger.hpp>
|
||||||
|
|
||||||
using namespace Assimp;
|
using namespace Assimp;
|
||||||
|
|
||||||
|
@ -63,34 +63,34 @@ const aiMatrix4x4 Assimp::AI_TO_IRR_MATRIX = aiMatrix4x4 (
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// read a property in hexadecimal format (i.e. ffffffff)
|
// read a property in hexadecimal format (i.e. ffffffff)
|
||||||
void IrrlichtBase::ReadHexProperty(HexProperty &out ) {
|
void IrrlichtBase::ReadHexProperty(HexProperty &out, pugi::xml_node& hexnode) {
|
||||||
for (pugi::xml_attribute attrib : mNode->attributes()) {
|
for (pugi::xml_attribute attrib : hexnode.attributes()) {
|
||||||
if (!ASSIMP_stricmp(attrib.name(), "name")) {
|
if (!ASSIMP_stricmp(attrib.name(), "name")) {
|
||||||
out.name = std::string(attrib.value());
|
out.name = std::string(attrib.value());
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "value")) {
|
} else if (!ASSIMP_stricmp(attrib.name(), "value")) {
|
||||||
// parse the hexadecimal value
|
// parse the hexadecimal value
|
||||||
out.value = strtoul16(attrib.name());
|
out.value = strtoul16(attrib.value());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// read a decimal property
|
// read a decimal property
|
||||||
void IrrlichtBase::ReadIntProperty(IntProperty & out) {
|
void IrrlichtBase::ReadIntProperty(IntProperty &out, pugi::xml_node& intnode) {
|
||||||
for (pugi::xml_attribute attrib : mNode->attributes()) {
|
for (pugi::xml_attribute attrib : intnode.attributes()) {
|
||||||
if (!ASSIMP_stricmp(attrib.name(), "name")) {
|
if (!ASSIMP_stricmp(attrib.name(), "name")) {
|
||||||
out.name = std::string(attrib.value());
|
out.name = std::string(attrib.value());
|
||||||
} else if (!ASSIMP_stricmp(attrib.value(),"value")) {
|
} else if (!ASSIMP_stricmp(attrib.name(), "value")) {
|
||||||
// parse the int value
|
// parse the int value
|
||||||
out.value = strtol10(attrib.name());
|
out.value = strtol10(attrib.value());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// read a string property
|
// read a string property
|
||||||
void IrrlichtBase::ReadStringProperty( StringProperty& out) {
|
void IrrlichtBase::ReadStringProperty(StringProperty &out, pugi::xml_node& stringnode) {
|
||||||
for (pugi::xml_attribute attrib : mNode->attributes()) {
|
for (pugi::xml_attribute attrib : stringnode.attributes()) {
|
||||||
if (!ASSIMP_stricmp(attrib.name(), "name")) {
|
if (!ASSIMP_stricmp(attrib.name(), "name")) {
|
||||||
out.name = std::string(attrib.value());
|
out.name = std::string(attrib.value());
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "value")) {
|
} else if (!ASSIMP_stricmp(attrib.name(), "value")) {
|
||||||
|
@ -102,8 +102,8 @@ void IrrlichtBase::ReadStringProperty( StringProperty& out) {
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// read a boolean property
|
// read a boolean property
|
||||||
void IrrlichtBase::ReadBoolProperty(BoolProperty &out) {
|
void IrrlichtBase::ReadBoolProperty(BoolProperty &out, pugi::xml_node& boolnode) {
|
||||||
for (pugi::xml_attribute attrib : mNode->attributes()) {
|
for (pugi::xml_attribute attrib : boolnode.attributes()) {
|
||||||
if (!ASSIMP_stricmp(attrib.name(), "name")) {
|
if (!ASSIMP_stricmp(attrib.name(), "name")) {
|
||||||
out.name = std::string(attrib.value());
|
out.name = std::string(attrib.value());
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "value")) {
|
} else if (!ASSIMP_stricmp(attrib.name(), "value")) {
|
||||||
|
@ -115,8 +115,8 @@ void IrrlichtBase::ReadBoolProperty(BoolProperty &out) {
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// read a float property
|
// read a float property
|
||||||
void IrrlichtBase::ReadFloatProperty(FloatProperty &out) {
|
void IrrlichtBase::ReadFloatProperty(FloatProperty &out, pugi::xml_node &floatnode) {
|
||||||
for (pugi::xml_attribute attrib : mNode->attributes()) {
|
for (pugi::xml_attribute attrib : floatnode.attributes()) {
|
||||||
if (!ASSIMP_stricmp(attrib.name(), "name")) {
|
if (!ASSIMP_stricmp(attrib.name(), "name")) {
|
||||||
out.name = std::string(attrib.value());
|
out.name = std::string(attrib.value());
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "value")) {
|
} else if (!ASSIMP_stricmp(attrib.name(), "value")) {
|
||||||
|
@ -128,8 +128,8 @@ void IrrlichtBase::ReadFloatProperty(FloatProperty &out) {
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// read a vector property
|
// read a vector property
|
||||||
void IrrlichtBase::ReadVectorProperty( VectorProperty &out ) {
|
void IrrlichtBase::ReadVectorProperty(VectorProperty &out, pugi::xml_node& vectornode) {
|
||||||
for (pugi::xml_attribute attrib : mNode->attributes()) {
|
for (pugi::xml_attribute attrib : vectornode.attributes()) {
|
||||||
if (!ASSIMP_stricmp(attrib.name(), "name")) {
|
if (!ASSIMP_stricmp(attrib.name(), "name")) {
|
||||||
out.name = std::string(attrib.value());
|
out.name = std::string(attrib.value());
|
||||||
} else if (!ASSIMP_stricmp(attrib.name(), "value")) {
|
} else if (!ASSIMP_stricmp(attrib.name(), "value")) {
|
||||||
|
@ -170,7 +170,7 @@ int ConvertMappingMode(const std::string& mode) {
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Parse a material from the XML file
|
// Parse a material from the XML file
|
||||||
aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags) {
|
aiMaterial *IrrlichtBase::ParseMaterial(pugi::xml_node& materialNode, unsigned int &matFlags) {
|
||||||
aiMaterial *mat = new aiMaterial();
|
aiMaterial *mat = new aiMaterial();
|
||||||
aiColor4D clr;
|
aiColor4D clr;
|
||||||
aiString s;
|
aiString s;
|
||||||
|
@ -179,10 +179,10 @@ aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags) {
|
||||||
int cnt = 0; // number of used texture channels
|
int cnt = 0; // number of used texture channels
|
||||||
unsigned int nd = 0;
|
unsigned int nd = 0;
|
||||||
|
|
||||||
for (pugi::xml_node child : mNode->children()) {
|
for (pugi::xml_node child : materialNode.children()) {
|
||||||
if (!ASSIMP_stricmp(child.name(), "color")) { // Hex properties
|
if (!ASSIMP_stricmp(child.name(), "color")) { // Hex properties
|
||||||
HexProperty prop;
|
HexProperty prop;
|
||||||
ReadHexProperty(prop);
|
ReadHexProperty(prop, child);
|
||||||
if (prop.name == "Diffuse") {
|
if (prop.name == "Diffuse") {
|
||||||
ColorFromARGBPacked(prop.value, clr);
|
ColorFromARGBPacked(prop.value, clr);
|
||||||
mat->AddProperty(&clr, 1, AI_MATKEY_COLOR_DIFFUSE);
|
mat->AddProperty(&clr, 1, AI_MATKEY_COLOR_DIFFUSE);
|
||||||
|
@ -206,13 +206,13 @@ aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags) {
|
||||||
#endif
|
#endif
|
||||||
} else if (!ASSIMP_stricmp(child.name(), "float")) { // Float properties
|
} else if (!ASSIMP_stricmp(child.name(), "float")) { // Float properties
|
||||||
FloatProperty prop;
|
FloatProperty prop;
|
||||||
ReadFloatProperty(prop);
|
ReadFloatProperty(prop, child);
|
||||||
if (prop.name == "Shininess") {
|
if (prop.name == "Shininess") {
|
||||||
mat->AddProperty(&prop.value, 1, AI_MATKEY_SHININESS);
|
mat->AddProperty(&prop.value, 1, AI_MATKEY_SHININESS);
|
||||||
}
|
}
|
||||||
} else if (!ASSIMP_stricmp(child.name(), "bool")) { // Bool properties
|
} else if (!ASSIMP_stricmp(child.name(), "bool")) { // Bool properties
|
||||||
BoolProperty prop;
|
BoolProperty prop;
|
||||||
ReadBoolProperty(prop);
|
ReadBoolProperty(prop, child);
|
||||||
if (prop.name == "Wireframe") {
|
if (prop.name == "Wireframe") {
|
||||||
int val = (prop.value ? true : false);
|
int val = (prop.value ? true : false);
|
||||||
mat->AddProperty(&val, 1, AI_MATKEY_ENABLE_WIREFRAME);
|
mat->AddProperty(&val, 1, AI_MATKEY_ENABLE_WIREFRAME);
|
||||||
|
@ -226,7 +226,7 @@ aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags) {
|
||||||
} else if (!ASSIMP_stricmp(child.name(), "texture") ||
|
} else if (!ASSIMP_stricmp(child.name(), "texture") ||
|
||||||
!ASSIMP_stricmp(child.name(), "enum")) { // String properties - textures and texture related properties
|
!ASSIMP_stricmp(child.name(), "enum")) { // String properties - textures and texture related properties
|
||||||
StringProperty prop;
|
StringProperty prop;
|
||||||
ReadStringProperty(prop);
|
ReadStringProperty(prop, child);
|
||||||
if (prop.value.length()) {
|
if (prop.value.length()) {
|
||||||
// material type (shader)
|
// material type (shader)
|
||||||
if (prop.name == "Type") {
|
if (prop.name == "Type") {
|
||||||
|
@ -379,7 +379,7 @@ aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags) {
|
||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
ASSIMP_LOG_ERROR("IRRMESH: Unexpected end of file. Material is not complete");
|
//ASSIMP_LOG_ERROR("IRRMESH: Unexpected end of file. Material is not complete");
|
||||||
|
|
||||||
return mat;
|
return mat;
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,8 +58,7 @@ extern const aiMatrix4x4 AI_TO_IRR_MATRIX;
|
||||||
*/
|
*/
|
||||||
class IrrlichtBase {
|
class IrrlichtBase {
|
||||||
protected:
|
protected:
|
||||||
IrrlichtBase() :
|
IrrlichtBase() {
|
||||||
mNode(nullptr) {
|
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,25 +81,25 @@ protected:
|
||||||
|
|
||||||
/// XML reader instance
|
/// XML reader instance
|
||||||
XmlParser mParser;
|
XmlParser mParser;
|
||||||
pugi::xml_node *mNode;
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Parse a material description from the XML
|
/** Parse a material description from the XML
|
||||||
* @return The created material
|
* @return The created material
|
||||||
* @param matFlags Receives AI_IRRMESH_MAT_XX flags
|
* @param matFlags Receives AI_IRRMESH_MAT_XX flags
|
||||||
*/
|
*/
|
||||||
aiMaterial *ParseMaterial(unsigned int &matFlags);
|
aiMaterial *ParseMaterial(pugi::xml_node &materialNode, unsigned int &matFlags);
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Read a property of the specified type from the current XML element.
|
/** Read a property of the specified type from the current XML element.
|
||||||
* @param out Receives output data
|
* @param out Receives output data
|
||||||
|
* @param node XML attribute element containing data
|
||||||
*/
|
*/
|
||||||
void ReadHexProperty(HexProperty &out);
|
void ReadHexProperty(HexProperty &out, pugi::xml_node& hexnode);
|
||||||
void ReadStringProperty(StringProperty &out);
|
void ReadStringProperty(StringProperty &out, pugi::xml_node& stringnode);
|
||||||
void ReadBoolProperty(BoolProperty &out);
|
void ReadBoolProperty(BoolProperty &out, pugi::xml_node& boolnode);
|
||||||
void ReadFloatProperty(FloatProperty &out);
|
void ReadFloatProperty(FloatProperty &out, pugi::xml_node& floatnode);
|
||||||
void ReadVectorProperty(VectorProperty &out);
|
void ReadVectorProperty(VectorProperty &out, pugi::xml_node& vectornode);
|
||||||
void ReadIntProperty(IntProperty &out);
|
void ReadIntProperty(IntProperty &out, pugi::xml_node& intnode);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
|
@ -51,7 +51,8 @@ public:
|
||||||
virtual bool importerTest() {
|
virtual bool importerTest() {
|
||||||
Assimp::Importer importer;
|
Assimp::Importer importer;
|
||||||
const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/IRR/box.irr", aiProcess_ValidateDataStructure);
|
const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/IRR/box.irr", aiProcess_ValidateDataStructure);
|
||||||
return nullptr != scene;
|
// Only one box thus only one mesh
|
||||||
|
return nullptr != scene && scene->mNumMeshes == 1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -63,4 +64,7 @@ TEST_F(utIrrImportExport, importSGIrrTest) {
|
||||||
Assimp::Importer importer;
|
Assimp::Importer importer;
|
||||||
const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/IRR/dawfInCellar_SameHierarchy.irr", aiProcess_ValidateDataStructure);
|
const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/IRR/dawfInCellar_SameHierarchy.irr", aiProcess_ValidateDataStructure);
|
||||||
EXPECT_NE(nullptr, scene);
|
EXPECT_NE(nullptr, scene);
|
||||||
|
EXPECT_EQ(scene->mNumMeshes, 2);
|
||||||
|
EXPECT_EQ(scene->mNumMaterials, 2);
|
||||||
|
EXPECT_GT(scene->mMeshes[0]->mNumVertices, 0);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue