123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652 |
- /*
- ---------------------------------------------------------------------------
- Open Asset Import Library (ASSIMP)
- ---------------------------------------------------------------------------
- Copyright (c) 2006-2008, ASSIMP Development Team
- All rights reserved.
- Redistribution and use of this software in source and binary forms,
- with or without modification, are permitted provided that the following
- conditions are met:
- * Redistributions of source code must retain the above
- copyright notice, this list of conditions and the
- following disclaimer.
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the
- following disclaimer in the documentation and/or other
- materials provided with the distribution.
- * Neither the name of the ASSIMP team, nor the names of its
- contributors may be used to endorse or promote products
- derived from this software without specific prior
- written permission of the ASSIMP Development Team.
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- ---------------------------------------------------------------------------
- */
- /** @file Implementation of the Irr importer class */
- #include "AssimpPCH.h"
- #include "IRRLoader.h"
- #include "ParsingUtils.h"
- #include "fast_atof.h"
- #include "GenericProperty.h"
- #include "SceneCombiner.h"
- #include "StandardShapes.h"
- using namespace Assimp;
- // ------------------------------------------------------------------------------------------------
- // Constructor to be privately used by Importer
- IRRImporter::IRRImporter()
- {
- // nothing to do here
- }
- // ------------------------------------------------------------------------------------------------
- // Destructor, private as well
- IRRImporter::~IRRImporter()
- {
- // nothing to do here
- }
- // ------------------------------------------------------------------------------------------------
- // Returns whether the class can handle the format of the given file.
- bool IRRImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
- {
- /* NOTE: A simple check for the file extension is not enough
- * here. Irrmesh and irr are easy, but xml is too generic
- * and could be collada, too. So we need to open the file and
- * search for typical tokens.
- */
- std::string::size_type pos = pFile.find_last_of('.');
- // no file extension - can't read
- if( pos == std::string::npos)
- return false;
- std::string extension = pFile.substr( pos);
- for (std::string::iterator i = extension.begin(); i != extension.end();++i)
- *i = ::tolower(*i);
- if (extension == ".irr")return true;
- else if (extension == ".xml")
- {
- /* If CanRead() is called to check whether the loader
- * supports a specific file extension in general we
- * must return true here.
- */
- if (!pIOHandler)return true;
- const char* tokens[] = {"irr_scene"};
- return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
- }
- return false;
- }
- // ------------------------------------------------------------------------------------------------
- // Imports the given file into the given scene structure.
- void IRRImporter::InternReadFile( const std::string& pFile,
- aiScene* pScene, IOSystem* pIOHandler)
- {
- boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
- // Check whether we can read from the file
- if( file.get() == NULL)
- throw new ImportErrorException( "Failed to open IRR file " + pFile + "");
- // Construct the irrXML parser
- CIrrXML_IOStreamReader st(file.get());
- reader = createIrrXMLReader((IFileReadCallBack*) &st);
- // The root node of the scene
- Node* root = new Node(Node::DUMMY);
- root->parent = NULL;
- // Current node parent
- Node* curParent = root;
- // Scenegraph node we're currently working on
- Node* curNode = NULL;
- // List of output cameras
- std::vector<aiCamera*> cameras;
- // List of output lights
- std::vector<aiLight*> lights;
- BatchLoader batch(pIOHandler);
-
- cameras.reserve(5);
- lights.reserve(5);
- bool inMaterials = false, inAnimator = false;
- unsigned int guessedAnimCnt = 0, guessedMeshCnt = 0;
- // Parse the XML file
- while (reader->read())
- {
- switch (reader->getNodeType())
- {
- case EXN_ELEMENT:
-
- if (!ASSIMP_stricmp(reader->getNodeName(),"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
- *
- * Each of these nodes can be animated.
- */
- const char* sz = reader->getAttributeValueSafe("type");
- Node* nd;
- if (!ASSIMP_stricmp(sz,"mesh"))
- {
- nd = new Node(Node::MESH);
- }
- else if (!ASSIMP_stricmp(sz,"cube"))
- {
- nd = new Node(Node::CUBE);
- ++guessedMeshCnt;
- // meshes.push_back(StandardShapes::MakeMesh(&StandardShapes::MakeHexahedron));
- }
- else if (!ASSIMP_stricmp(sz,"skybox"))
- {
- nd = new Node(Node::SKYBOX);
- ++guessedMeshCnt;
- }
- else if (!ASSIMP_stricmp(sz,"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(sz,"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(sz,"sphere"))
- {
- nd = new Node(Node::SPHERE);
- ++guessedMeshCnt;
- }
- else if (!ASSIMP_stricmp(sz,"animatedMesh"))
- {
- nd = new Node(Node::ANIMMESH);
- }
- else if (!ASSIMP_stricmp(sz,"empty"))
- {
- nd = new Node(Node::DUMMY);
- }
- else
- {
- DefaultLogger::get()->warn("IRR: Found unknown node: " + std::string(sz));
- /* 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 scenegraph
- */
- curNode = nd;
- nd->parent = curParent;
- curParent->children.push_back(nd);
- }
- else if (!ASSIMP_stricmp(reader->getNodeName(),"materials"))
- {
- inMaterials = true;
- }
- else if (!ASSIMP_stricmp(reader->getNodeName(),"animators"))
- {
- inAnimator = true;
- }
- else if (!ASSIMP_stricmp(reader->getNodeName(),"attributes"))
- {
- /* We should have a valid node here
- */
- if (!curNode)
- {
- DefaultLogger::get()->error("IRR: Encountered <attributes> element, but "
- "there is no node active");
- continue;
- }
- Animator* curAnim = NULL;
- if (inMaterials && curNode->type == Node::ANIMMESH ||
- curNode->type == Node::MESH )
- {
- /* This is a material description - parse it!
- */
- curNode->materials.push_back(std::pair< aiMaterial*, unsigned int > () );
- std::pair< aiMaterial*, unsigned int >& p = curNode->materials.back();
- p.first = ParseMaterial(p.second);
- continue;
- }
- else if (inAnimator)
- {
- /* This is an animation path - add a new animator
- * to the list.
- */
- curNode->animators.push_back(Animator());
- curAnim = & curNode->animators.back();
- ++guessedAnimCnt;
- }
- /* Parse all elements in the attributes block
- * and process them.
- */
- while (reader->read())
- {
- if (reader->getNodeType() == EXN_ELEMENT)
- {
- if (!ASSIMP_stricmp(reader->getNodeName(),"vector3d"))
- {
- VectorProperty prop;
- ReadVectorProperty(prop);
- // Convert to our coordinate system
- std::swap( (float&)prop.value.z, (float&)prop.value.y );
- prop.value.y *= -1.f;
- if (inAnimator)
- {
- if (curAnim->type == Animator::ROTATION && prop.name == "Rotation")
- {
- // We store the rotation euler angles in 'direction'
- curAnim->direction = prop.value;
- }
- else if (curAnim->type == Animator::FOLLOW_SPLINE)
- {
- // Check whether the vector follows the PointN naming scheme,
- // here N is the ONE-based index of the point
- if (prop.name.length() >= 6 && prop.name.substr(0,5) == "Point")
- {
- // Add a new key to the list
- curAnim->splineKeys.push_back(aiVectorKey());
- aiVectorKey& key = curAnim->splineKeys.back();
- // and parse its properties
- key.mValue = prop.value;
- key.mTime = strtol10(&prop.name.c_str()[5]);
- }
- }
- else if (curAnim->type == Animator::FLY_CIRCLE)
- {
- if (prop.name == "Center")
- {
- curAnim->circleCenter = prop.value;
- }
- else if (prop.name == "Direction")
- {
- curAnim->direction = prop.value;
- }
- }
- else if (curAnim->type == Animator::FLY_STRAIGHT)
- {
- if (prop.name == "Start")
- {
- // We reuse the field here
- curAnim->circleCenter = prop.value;
- }
- else if (prop.name == "End")
- {
- // We reuse the field here
- 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"))
- {
- BoolProperty prop;
- ReadBoolProperty(prop);
- if (inAnimator && curAnim->type == Animator::FLY_CIRCLE && prop.name == "Loop")
- {
- curAnim->loop = prop.value;
- }
- }
- else if (!ASSIMP_stricmp(reader->getNodeName(),"float"))
- {
- FloatProperty prop;
- ReadFloatProperty(prop);
- if (inAnimator)
- {
- // The speed property exists for several animators
- if (prop.name == "Speed")
- {
- curAnim->speed = prop.value;
- }
- else if (curAnim->type == Animator::FLY_CIRCLE && prop.name == "Radius")
- {
- curAnim->circleRadius = 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
- else if (Node::SPHERE == curNode->type)
- {
- if (prop.name == "Radius")
- {
- curNode->sphereRadius = prop.value;
- }
- }
- }
- }
- else if (!ASSIMP_stricmp(reader->getNodeName(),"int"))
- {
- IntProperty prop;
- ReadIntProperty(prop);
- if (inAnimator)
- {
- if (curAnim->type == Animator::FLY_STRAIGHT && prop.name == "TimeForWay")
- {
- curAnim->timeForWay = prop.value;
- }
- }
- else
- {
- // sphere polgon 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"))
- {
- StringProperty prop;
- ReadStringProperty(prop);
- if (prop.value.length())
- {
- if (prop.name == "Name")
- {
- curNode->name = prop.value;
- /* If we're either a camera or a light source
- * 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)
- {
- }
- 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 postprocessing settings here.
- */
- unsigned int pp = 0;
- BatchLoader::PropertyMap map;
- /* If the mesh is a static one remove all animations
- */
- if (Node::ANIMMESH != curNode->type)
- {
- pp |= aiProcess_RemoveComponent;
- SetGenericProperty<int>(map.ints,AI_CONFIG_PP_RVC_FLAGS,
- aiComponent_ANIMATIONS | aiComponent_BONEWEIGHTS);
- }
- batch.AddLoadRequest(prop.value,pp,&map);
- }
- }
- }
- }
- else if (reader->getNodeType() == EXN_ELEMENT_END &&
- !ASSIMP_stricmp(reader->getNodeName(),"attributes"))
- {
- break;
- }
- }
- }
- break;
- case EXN_ELEMENT_END:
-
- // If we reached the end of a node, we need to continue processing its parent
- if (!ASSIMP_stricmp(reader->getNodeName(),"node"))
- {
- if (!curNode)
- {
- // currently is no node set. We need to go
- // back in the node hierarchy
- curParent = curParent->parent;
- if (!curParent)
- {
- curParent = root;
- DefaultLogger::get()->error("IRR: Too many closing <node> elements");
- }
- }
- else curNode = NULL;
- }
- // clear all flags
- else if (!ASSIMP_stricmp(reader->getNodeName(),"materials"))
- {
- inMaterials = false;
- }
- else if (!ASSIMP_stricmp(reader->getNodeName(),"animators"))
- {
- inAnimator = false;
- }
- break;
- default:
- // GCC complains that not all enumeration values are handled
- break;
- }
- }
- /* Now iterate through all cameras and compute their final (horizontal) FOV
- */
- for (std::vector<aiCamera*>::iterator it = cameras.begin(), end = cameras.end();
- it != end; ++it)
- {
- aiCamera* cam = *it;
- if (cam->mAspect) // screen aspect could be missing
- {
- cam->mHorizontalFOV *= cam->mAspect;
- }
- else DefaultLogger::get()->warn("IRR: Camera aspect is not given, can't compute horizontal FOV");
- }
- /* Allocate a tempoary scene data structure
- */
- aiScene* tempScene = new aiScene();
- tempScene->mRootNode = new aiNode();
- tempScene->mRootNode->mName.Set("<IRRRoot>");
- /* Copy the cameras to the output array
- */
- tempScene->mNumCameras = (unsigned int)cameras.size();
- tempScene->mCameras = new aiCamera*[tempScene->mNumCameras];
- ::memcpy(tempScene->mCameras,&cameras[0],sizeof(void*)*tempScene->mNumCameras);
- /* Copy the light sources to the output array
- */
- tempScene->mNumLights = (unsigned int)lights.size();
- tempScene->mLights = new aiLight*[tempScene->mNumLights];
- ::memcpy(tempScene->mLights,&lights[0],sizeof(void*)*tempScene->mNumLights);
- // temporary data
- std::vector< aiNodeAnim*> anims;
- std::vector< AttachmentInfo > attach;
- std::vector<aiMesh*> meshes;
- anims.reserve(guessedAnimCnt + (guessedAnimCnt >> 2));
- meshes.reserve(guessedMeshCnt + (guessedMeshCnt >> 2));
- /* Now process our scenegraph recursively: generate final
- * meshes and generate animation channels for all nodes.
- */
- // GenerateGraph(root,tempScene->mRootNode, tempScene,
- // batch, meshes, anims, attach);
- if (!anims.empty())
- {
- tempScene->mNumAnimations = 1;
- tempScene->mAnimations = new aiAnimation*[tempScene->mNumAnimations];
- aiAnimation* an = tempScene->mAnimations[0] = new aiAnimation();
- // ***********************************************************
- // This is only the global animation channel of the scene.
- // If there are animated models, they will have separate
- // animation channels in the scene. To display IRR scenes
- // correctly, users will need to combine the global anim
- // channel with all the local animations they want to play
- // ***********************************************************
- an->mName.Set("Irr_GlobalAnimChannel");
- // copy all node animation channels to the global channel
- an->mNumChannels = (unsigned int)anims.size();
- an->mChannels = new aiNodeAnim*[an->mNumChannels];
- ::memcpy(an->mChannels, & anims [0], sizeof(void*)*an->mNumChannels);
- }
- if (meshes.empty())
- {
- // There are no meshes in the scene - the scene is incomplete
- pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
- DefaultLogger::get()->info("IRR: No Meshes loaded, setting AI_SCENE_FLAGS_INCOMPLETE flag");
- }
- /* Now merge all sub scenes and attach them to the correct
- * attachment points in the scenegraph.
- */
- SceneCombiner::MergeScenes(pScene,tempScene,attach);
- }
|