| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396 |
- /*
- ---------------------------------------------------------------------------
- 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;
- }
- // ------------------------------------------------------------------------------------------------
- void IRRImporter::GetExtensionList(std::string& append)
- {
- /* NOTE: The file extenxsion .xml is too generic. We'll
- * need to open the file in CanRead() and check whether it is
- * a real irrlicht file
- */
- append.append("*.xml;*.irr");
- }
- // ------------------------------------------------------------------------------------------------
- void IRRImporter::SetupProperties(const Importer* pImp)
- {
- // read the output frame rate of all node animation channels
- fps = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_IRR_ANIM_FPS,100);
- if (!fps)
- {
- DefaultLogger::get()->error("IRR: Invalid FPS configuration");
- fps = 100;
- }
- }
- // ------------------------------------------------------------------------------------------------
- // Build a mesh tha consists of a single squad (a side of a skybox)
- aiMesh* IRRImporter::BuildSingleQuadMesh(const SkyboxVertex& v1,
- const SkyboxVertex& v2,
- const SkyboxVertex& v3,
- const SkyboxVertex& v4)
- {
- // allocate and prepare the mesh
- aiMesh* out = new aiMesh();
- out->mPrimitiveTypes = aiPrimitiveType_POLYGON;
- out->mNumFaces = 1;
- // build the face
- out->mFaces = new aiFace[1];
- aiFace& face = out->mFaces[0];
-
- face.mNumIndices = 4;
- face.mIndices = new unsigned int[4];
- for (unsigned int i = 0; i < 4;++i)
- face.mIndices[i] = i;
- out->mNumVertices = 4;
- // copy vertex positions
- aiVector3D* vec = out->mVertices = new aiVector3D[4];
- *vec++ = v1.position;
- *vec++ = v2.position;
- *vec++ = v3.position;
- *vec = v4.position;
- // copy vertex normals
- vec = out->mNormals = new aiVector3D[4];
- *vec++ = v1.normal;
- *vec++ = v2.normal;
- *vec++ = v3.normal;
- *vec = v4.normal;
- // copy texture coordinates
- out->mTextureCoords[0] = new aiVector3D[4];
- *vec++ = v1.uv;
- *vec++ = v2.uv;
- *vec++ = v3.uv;
- *vec = v4.uv;
- return out;
- }
- // ------------------------------------------------------------------------------------------------
- void IRRImporter::BuildSkybox(std::vector<aiMesh*>& meshes, std::vector<aiMaterial*> materials)
- {
- // Update the material of the skybox - replace the name
- // and disable shading for skyboxes.
- for (unsigned int i = 0; i < 6;++i)
- {
- MaterialHelper* out = ( MaterialHelper* ) (*(materials.end()-(6-i)));
- aiString s;
- s.length = ::sprintf( s.data, "SkyboxSide_%i",i );
- out->AddProperty(&s,AI_MATKEY_NAME);
- int shading = aiShadingMode_NoShading;
- out->AddProperty(&shading,1,AI_MATKEY_SHADING_MODEL);
- }
- // Skyboxes are much more difficult. They are represented
- // by six single planes with different textures, so we'll
- // need to build six meshes.
- const float l = 10.f; // the size used by Irrlicht
- // FRONT SIDE
- meshes.push_back( BuildSingleQuadMesh(
- SkyboxVertex(-l,-l,-l, 0, 0, 1, 1.f,1.f),
- SkyboxVertex( l,-l,-l, 0, 0, 1, 0.f,1.f),
- SkyboxVertex( l, l,-l, 0, 0, 1, 0.f,0.f),
- SkyboxVertex(-l, l,-l, 0, 0, 1, 1.f,0.f)) );
- meshes.back()->mMaterialIndex = materials.size()-6u;
- // LEFT SIDE
- meshes.push_back( BuildSingleQuadMesh(
- SkyboxVertex( l,-l,-l, -1, 0, 0, 1.f,1.f),
- SkyboxVertex( l,-l, l, -1, 0, 0, 0.f,1.f),
- SkyboxVertex( l, l, l, -1, 0, 0, 0.f,0.f),
- SkyboxVertex( l, l,-l, -1, 0, 0, 1.f,0.f)) );
- meshes.back()->mMaterialIndex = materials.size()-5u;
- // BACK SIDE
- meshes.push_back( BuildSingleQuadMesh(
- SkyboxVertex( l,-l, l, 0, 0, -1, 1.f,1.f),
- SkyboxVertex(-l,-l, l, 0, 0, -1, 0.f,1.f),
- SkyboxVertex(-l, l, l, 0, 0, -1, 0.f,0.f),
- SkyboxVertex( l, l, l, 0, 0, -1, 1.f,0.f)) );
- meshes.back()->mMaterialIndex = materials.size()-4u;
- // RIGHT SIDE
- meshes.push_back( BuildSingleQuadMesh(
- SkyboxVertex(-l,-l, l, 1, 0, 0, 1.f,1.f),
- SkyboxVertex(-l,-l,-l, 1, 0, 0, 0.f,1.f),
- SkyboxVertex(-l, l,-l, 1, 0, 0, 0.f,0.f),
- SkyboxVertex(-l, l, l, 1, 0, 0, 1.f,0.f)) );
- meshes.back()->mMaterialIndex = materials.size()-3u;
- // TOP SIDE
- meshes.push_back( BuildSingleQuadMesh(
- SkyboxVertex( l, l,-l, 0, -1, 0, 1.f,1.f),
- SkyboxVertex( l, l, l, 0, -1, 0, 0.f,1.f),
- SkyboxVertex(-l, l, l, 0, -1, 0, 0.f,0.f),
- SkyboxVertex(-l, l,-l, 0, -1, 0, 1.f,0.f)) );
- meshes.back()->mMaterialIndex = materials.size()-2u;
- // BOTTOM SIDE
- meshes.push_back( BuildSingleQuadMesh(
- SkyboxVertex( l,-l, l, 0, 1, 0, 0.f,0.f),
- SkyboxVertex( l,-l,-l, 0, 1, 0, 1.f,0.f),
- SkyboxVertex(-l,-l,-l, 0, 1, 0, 1.f,1.f),
- SkyboxVertex(-l,-l,-l, 0, 1, 0, 0.f,1.f)) );
- meshes.back()->mMaterialIndex = materials.size()-1u;
- }
- // ------------------------------------------------------------------------------------------------
- void IRRImporter::CopyMaterial(std::vector<aiMaterial*> materials,
- std::vector< std::pair<aiMaterial*, unsigned int> >& inmaterials,
- unsigned int& defMatIdx,
- aiMesh* mesh)
- {
- if (inmaterials.empty())
- {
- // Do we have a default material? If not we need to create one
- if (0xffffffff == defMatIdx)
- {
- defMatIdx = (unsigned int)materials.size();
- MaterialHelper* mat = new MaterialHelper();
- aiString s;
- s.Set(AI_DEFAULT_MATERIAL_NAME);
- mat->AddProperty(&s,AI_MATKEY_NAME);
- aiColor3D c(0.6f,0.6f,0.6f);
- mat->AddProperty(&c,1,AI_MATKEY_COLOR_DIFFUSE);
- }
- mesh->mMaterialIndex = defMatIdx;
- return;
- }
- else if (inmaterials.size() > 1)
- {
- DefaultLogger::get()->info("IRR: Skipping additional materials");
- }
- mesh->mMaterialIndex = (unsigned int)materials.size();
- materials.push_back(inmaterials[0].first);
- }
- // ------------------------------------------------------------------------------------------------
- inline int ClampSpline(int idx, int size)
- {
- return ( idx<0 ? size+idx : ( idx>=size ? idx-size : idx ) );
- }
- // ------------------------------------------------------------------------------------------------
- inline void FindSuitableMultiple(int& angle)
- {
- if (angle < 3)angle = 3;
- else if (angle < 10) angle = 10;
- else if (angle < 20) angle = 20;
- else if (angle < 30) angle = 30;
- else
- {
- }
- }
- // ------------------------------------------------------------------------------------------------
- void IRRImporter::ComputeAnimations(Node* root, std::vector<aiNodeAnim*>& anims,
- const aiMatrix4x4& transform)
- {
- ai_assert(NULL != root);
- if (root->animators.empty())return;
- typedef std::pair< TemporaryAnim, Animator* > AnimPair;
- const unsigned int resolution = 1;
- std::vector<AnimPair> temp;
- temp.reserve(root->animators.size());
- for (std::list<Animator>::iterator it = root->animators.begin();
- it != root->animators.end(); ++it)
- {
- if ((*it).type == Animator::UNKNOWN ||
- (*it).type == Animator::OTHER)
- {
- DefaultLogger::get()->warn("IRR: Skipping unknown or unsupported animator");
- continue;
- }
- temp.push_back(AnimPair(TemporaryAnim(),&(*it)));
- }
- if (temp.empty())return;
- // All animators are applied one after another. We generate a set of
- // transformation matrices for each of it. Then we combine all
- // transformation matrices, decompose them and build an output animation.
- for (std::vector<AnimPair>::iterator it = temp.begin();
- it != temp.end(); ++it)
- {
- TemporaryAnim& out = (*it).first;
- Animator* in = (*it).second;
- switch (in->type)
- {
- case Animator::ROTATION:
- {
- // -----------------------------------------------------
- // find out how long a full rotation will take
- // This is the least common multiple of 360.f and all
- // three euler angles. Although we'll surely find a
- // possible multiple (haha) it could be somewhat large
- // for our purposes. So we need to modify the angles
- // here in order to get good results.
- // -----------------------------------------------------
- int angles[3];
- angles[0] = (int)(in->direction.x*100);
- angles[1] = (int)(in->direction.y*100);
- angles[2] = (int)(in->direction.z*100);
- angles[0] %= 360;
- angles[1] %= 360;
- angles[2] %= 360;
- FindSuitableMultiple(angles[0]);
- FindSuitableMultiple(angles[1]);
- FindSuitableMultiple(angles[2]);
- int lcm = 360;
- if (angles[0])
- lcm = boost::math::lcm(lcm,angles[0]);
- if (angles[1])
- lcm = boost::math::lcm(lcm,angles[1]);
- if (angles[2])
- lcm = boost::math::lcm(lcm,angles[2]);
- if (360 == lcm)
- break;
- // This can be a division through zero, but we don't care
- float f1 = (float)lcm / angles[0];
- float f2 = (float)lcm / angles[1];
- float f3 = (float)lcm / angles[2];
-
- // find out how many time units we'll need for the finest
- // track (in seconds) - this defines the number of output
- // keys (fps * seconds)
- float max ;
- if (angles[0])
- max = (float)lcm / angles[0];
- if (angles[1])
- max = std::max(max, (float)lcm / angles[1]);
- if (angles[2])
- max = std::max(max, (float)lcm / angles[2]);
-
- // Allocate transformation matrices
- out.SetupMatrices((unsigned int)(max*fps));
- // begin with a zero angle
- aiVector3D angle;
- for (unsigned int i = 0; i < out.last;++i)
- {
- // build the rotation matrix for the given euler angles
- aiMatrix4x4& m = out.matrices[i];
- // we start with the node transformation
- m = transform;
- aiMatrix4x4 m2;
- if (angle.x)
- m *= aiMatrix4x4::RotationX(angle.x,m2);
- if (angle.y)
- m *= aiMatrix4x4::RotationX(angle.y,m2);
- if (angle.z)
- m *= aiMatrix4x4::RotationZ(angle.z,m2);
- // increase the angle
- angle += in->direction;
- }
- // This animation is repeated and repeated ...
- out.post = aiAnimBehaviour_REPEAT;
- }
- break;
- case Animator::FLY_CIRCLE:
- {
-
- }
- break;
- case Animator::FLY_STRAIGHT:
- {
-
- }
- break;
- case Animator::FOLLOW_SPLINE:
- {
- out.post = aiAnimBehaviour_REPEAT;
- const int size = (int)in->splineKeys.size();
- if (!size)
- {
- // We have no point in the spline. That's bad. Really bad.
- DefaultLogger::get()->warn("IRR: Spline animators with no points defined");
- break;
- }
- else if (size == 1)
- {
- // We have just one point in the spline
- out.SetupMatrices(1);
- out.matrices[0].a4 = in->splineKeys[0].mValue.x;
- out.matrices[0].b4 = in->splineKeys[0].mValue.y;
- out.matrices[0].c4 = in->splineKeys[0].mValue.z;
- break;
- }
- unsigned int ticksPerFull = 15;
- out.SetupMatrices(ticksPerFull*fps);
- for (unsigned int i = 0; i < out.last;++i)
- {
- aiMatrix4x4& m = out.matrices[i];
- const float dt = (i * in->speed * 0.001f );
- const float u = dt - floor(dt);
- const int idx = (int)floor(dt) % size;
- // get the 4 current points to evaluate the spline
- const aiVector3D& p0 = in->splineKeys[ ClampSpline( idx - 1, size ) ].mValue;
- const aiVector3D& p1 = in->splineKeys[ ClampSpline( idx + 0, size ) ].mValue;
- const aiVector3D& p2 = in->splineKeys[ ClampSpline( idx + 1, size ) ].mValue;
- const aiVector3D& p3 = in->splineKeys[ ClampSpline( idx + 2, size ) ].mValue;
- // compute polynomials
- const float u2 = u*u;
- const float u3 = u2*2;
- const float h1 = 2.0f * u3 - 3.0f * u2 + 1.0f;
- const float h2 = -2.0f * u3 + 3.0f * u3;
- const float h3 = u3 - 2.0f * u3;
- const float h4 = u3 - u2;
- // compute the spline tangents
- const aiVector3D t1 = ( p2 - p0 ) * in->tightness;
- aiVector3D t2 = ( p3 - p1 ) * in->tightness;
- // and use them to get the interpolated point
- t2 = (h1 * p1 + p2 * h2 + t1 * h3 + h4 * t2);
- // build a simple translation matrix from it
- m.a4 = t2.x;
- m.b4 = t2.y;
- m.c4 = t2.z;
- }
- }
- break;
- };
- }
- aiNodeAnim* out = new aiNodeAnim();
- out->mNodeName.Set(root->name);
- if (temp.size() == 1)
- {
- // If there's just one animator to be processed our
- // task is quite easy
- TemporaryAnim& one = temp[0].first;
-
- out->mPostState = one.post;
- out->mNumPositionKeys = one.last;
- out->mNumScalingKeys = one.last;
- out->mNumRotationKeys = one.last;
- out->mPositionKeys = new aiVectorKey[one.last];
- out->mScalingKeys = new aiVectorKey[one.last];
- out->mRotationKeys = new aiQuatKey[one.last];
- for (unsigned int i = 0; i < one.last;++i)
- {
- aiVectorKey& scaling = out->mScalingKeys[i];
- aiVectorKey& position = out->mPositionKeys[i];
- aiQuatKey& rotation = out->mRotationKeys[i];
- scaling.mTime = position.mTime = rotation.mTime = (double)i;
- one.matrices[i].Decompose(scaling.mValue, rotation.mValue, position.mValue);
- }
- }
- // NOTE: It is possible that some of the tracks we're returning
- // are dummy tracks, but the ScenePreprocessor will fix that, hopefully
- }
- // ------------------------------------------------------------------------------------------------
- void IRRImporter::GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
- BatchLoader& batch,
- std::vector<aiMesh*>& meshes,
- std::vector<aiNodeAnim*>& anims,
- std::vector<AttachmentInfo>& attach,
- std::vector<aiMaterial*> materials,
- unsigned int& defMatIdx)
- {
- unsigned int oldMeshSize = (unsigned int)meshes.size();
- // Now determine the type of the node
- switch (root->type)
- {
- case Node::ANIMMESH:
- case Node::MESH:
- {
- // get the loaded mesh from the scene and add it to
- // the list of all scenes to be attached to the
- // graph we're currently building
- aiScene* scene = batch.GetImport(root->meshPath);
- if (!scene)
- {
- DefaultLogger::get()->error("IRR: Unable to load external file: "
- + root->meshPath);
- break;
- }
- attach.push_back(AttachmentInfo(scene,rootOut));
- // now combine the material we've loaded for this mesh
- // with the real meshes we got from the file. As we
- // don't execute any pp-steps on the file, the numbers
- // should be equal. If they are not, we can impossibly
- // do this ...
- if (root->materials.size() != (unsigned int)scene->mNumMaterials)
- {
- DefaultLogger::get()->warn("IRR: Failed to match imported materials "
- "with the materials found in the IRR scene file");
- break;
- }
- for (unsigned int i = 0; i < scene->mNumMaterials;++i)
- {
- // delete the old material
- delete scene->mMaterials[i];
- std::pair<aiMaterial*, unsigned int>& src = root->materials[i];
- scene->mMaterials[i] = src.first;
- }
- // NOTE: Each mesh should have exactly one material assigned,
- // but we do it in a separate loop if this behaviour changes
- // in the future.
- for (unsigned int i = 0; i < scene->mNumMeshes;++i)
- {
- // Process material flags
- aiMesh* mesh = scene->mMeshes[i];
- // If "trans_vertex_alpha" mode is enabled, search all vertex colors
- // and check whether they have a common alpha value. This is quite
- // often the case so we can simply extract it to a shared oacity
- // value.
- std::pair<aiMaterial*, unsigned int>& src = root->materials[
- mesh->mMaterialIndex];
- MaterialHelper* mat = (MaterialHelper*)src.first;
- if (mesh->HasVertexColors(0) &&
- src.second & AI_IRRMESH_MAT_trans_vertex_alpha)
- {
- bool bdo = true;
- for (unsigned int a = 1; a < mesh->mNumVertices;++a)
- {
- if (mesh->mColors[0][a].a != mesh->mColors[0][a-1].a)
- {
- bdo = false;
- break;
- }
- }
- if (bdo)
- {
- DefaultLogger::get()->info("IRR: Replacing mesh vertex "
- "alpha with common opacity");
- for (unsigned int a = 0; a < mesh->mNumVertices;++a)
- mesh->mColors[0][a].a = 1.f;
- mat->AddProperty(& mesh->mColors[0][0].a, 1, AI_MATKEY_OPACITY);
- }
- }
- // If we have a second texture coordinate set and a second texture
- // (either lightmap, normalmap, 2layered material we need to
- // setup the correct UV index for it). The texture can either
- // be diffuse (lightmap & 2layer) or a normal map (normal & parallax)
- if (mesh->HasTextureCoords(1))
- {
- int idx = 1;
- if (src.second & (AI_IRRMESH_MAT_solid_2layer |
- AI_IRRMESH_MAT_lightmap))
- {
- mat->AddProperty(&idx,1,AI_MATKEY_UVWSRC_DIFFUSE(0));
- }
- else if (src.second & AI_IRRMESH_MAT_normalmap_solid)
- {
- mat->AddProperty(&idx,1,AI_MATKEY_UVWSRC_NORMALS(0));
- }
- }
- }
- }
- break;
- case Node::LIGHT:
- case Node::CAMERA:
- // We're already finished with lights and cameras
- break;
- case Node::SPHERE:
- {
- // generate the sphere model. Our input parameter to
- // the sphere generation algorithm is the number of
- // subdivisions of each triangle - but here we have
- // the number of poylgons on a specific axis. Just
- // use some limits ...
- unsigned int mul = root->spherePolyCountX*root->spherePolyCountY;
- if (mul < 100)mul = 2;
- else if (mul < 300)mul = 3;
- else mul = 4;
- meshes.push_back(StandardShapes::MakeMesh(mul,
- &StandardShapes::MakeSphere));
- // Adjust scaling
- root->scaling *= root->sphereRadius;
- // Copy one output material
- CopyMaterial(materials, root->materials, defMatIdx, meshes.back());
- }
- break;
- case Node::CUBE:
- {
- // Generate an unit cube first
- meshes.push_back(StandardShapes::MakeMesh(
- &StandardShapes::MakeHexahedron));
- // Adjust scaling
- root->scaling *= root->sphereRadius;
- // Copy one output material
- CopyMaterial(materials, root->materials, defMatIdx, meshes.back());
- }
- break;
- case Node::SKYBOX:
- {
- // A skybox is defined by six materials
- if (root->materials.size() < 6)
- {
- DefaultLogger::get()->error("IRR: There should be six materials "
- "for a skybox");
- break;
- }
- // copy those materials and generate 6 meshes for our new skybox
- materials.reserve(materials.size() + 6);
- for (unsigned int i = 0; i < 6;++i)
- materials.insert(materials.end(),root->materials[i].first);
- BuildSkybox(meshes,materials);
- // *************************************************************
- // Skyboxes will require a different code path for rendering,
- // so there must be a way for the user to add special support
- // for IRR skyboxes. We add a 'IRR.SkyBox_' prefix to the node.
- // *************************************************************
- root->name = "IRR.SkyBox_" + root->name;
- DefaultLogger::get()->info("IRR: Loading skybox, this will "
- "require special handling to be displayed correctly");
- }
- break;
- case Node::TERRAIN:
- {
- // to support terrains, we'd need to have a texture decoder
- DefaultLogger::get()->error("IRR: Unsupported node - TERRAIN");
- }
- break;
- };
- // Check whether we added a mesh (or more than one ...). In this case
- // we'll also need to attach it to the node
- if (oldMeshSize != (unsigned int) meshes.size())
- {
- rootOut->mNumMeshes = (unsigned int)meshes.size() - oldMeshSize;
- rootOut->mMeshes = new unsigned int[rootOut->mNumMeshes];
- for (unsigned int a = 0; a < rootOut->mNumMeshes;++a)
- {
- rootOut->mMeshes[a] = oldMeshSize+a;
- }
- }
- // Setup the name of this node
- rootOut->mName.Set(root->name);
- // Now compute the final local transformation matrix of the
- // node from the given translation, rotation and scaling values.
- // (the rotation is given in Euler angles, XYZ order)
- aiMatrix4x4 m;
- rootOut->mTransformation = aiMatrix4x4::RotationX(AI_DEG_TO_RAD(root->rotation.x),m)
- * aiMatrix4x4::RotationY(AI_DEG_TO_RAD(root->rotation.y),m)
- * aiMatrix4x4::RotationZ(AI_DEG_TO_RAD(root->rotation.z),m);
- // apply scaling
- aiMatrix4x4& mat = rootOut->mTransformation;
- mat.a1 *= root->scaling.x;
- mat.b1 *= root->scaling.x;
- mat.c1 *= root->scaling.x;
- mat.a2 *= root->scaling.y;
- mat.b2 *= root->scaling.y;
- mat.c2 *= root->scaling.y;
- mat.a3 *= root->scaling.z;
- mat.b3 *= root->scaling.z;
- mat.c3 *= root->scaling.z;
- // apply translation
- mat.a4 = root->position.x;
- mat.b4 = root->position.y;
- mat.c4 = root->position.z;
- // now compute animations for the node
- ComputeAnimations(root,anims,mat);
- // Add all children recursively. First allocate enough storage
- // for them, then call us again
- rootOut->mNumChildren = (unsigned int)root->children.size();
- if (rootOut->mNumChildren)
- {
- rootOut->mChildren = new aiNode*[rootOut->mNumChildren];
- for (unsigned int i = 0; i < rootOut->mNumChildren;++i)
- {
- aiNode* node = rootOut->mChildren[i] = new aiNode();
- node->mParent = rootOut;
- GenerateGraph(root->children[i],node,scene,batch,meshes,
- anims,attach,materials,defMatIdx);
- }
- }
- }
- // ------------------------------------------------------------------------------------------------
- // 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;
- // Batch loader used to load external models
- BatchLoader batch(pIOHandler);
-
- 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())
- {
- 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 and all can have multiple
- * materials assigned (except lights, cameras and dummies, of course).
- */
- // ***********************************************************************
- 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;
- // FIX: Materials can occur for nearly any type of node
- 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);
- ++guessedMatCnt;
- 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;
- // From Irrlicht source - a workaround for backward
- // compatibility with Irrlicht 1.1
- if (curAnim->direction == aiVector3D())
- {
- curAnim->direction = aiVector3D(0.f,1.f,0.f);
- }
- else curAnim->direction.Normalize();
- }
- }
- 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 (curAnim->type == Animator::FOLLOW_SPLINE && prop.name == "Tightness")
- {
- 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"))
- {
- 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);
- curNode->meshPath = prop.value;
- }
- else if (inAnimator && prop.name == "Type")
- {
- // type of the animator
- if (prop.value == "rotation")
- {
- curAnim->type = Animator::ROTATION;
- }
- else if (prop.value == "flyCircle")
- {
- curAnim->type = Animator::FLY_CIRCLE;
- }
- else if (prop.value == "flyStraight")
- {
- curAnim->type = Animator::FLY_CIRCLE;
- }
- else if (prop.value == "followSpline")
- {
- curAnim->type = Animator::FOLLOW_SPLINE;
- }
- else
- {
- DefaultLogger::get()->warn("IRR: Ignoring unknown animator: "
- + prop.value);
- curAnim->type = Animator::UNKNOWN;
- }
- }
- }
- }
- }
- 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< aiMaterial*> materials;
- std::vector< AttachmentInfo > attach;
- std::vector<aiMesh*> meshes;
- // try to guess how much storage we'll need
- anims.reserve (guessedAnimCnt + (guessedAnimCnt >> 2));
- meshes.reserve (guessedMeshCnt + (guessedMeshCnt >> 2));
- materials.reserve (guessedMatCnt + (guessedMatCnt >> 2));
- /* Now process our scenegraph recursively: generate final
- * meshes and generate animation channels for all nodes.
- */
- unsigned int defMatIdx = 0xffffffff;
- GenerateGraph(root,tempScene->mRootNode, tempScene,
- batch, meshes, anims, attach, materials, defMatIdx);
- 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");
- }
- else
- {
- // copy all meshes to the temporary scene
- tempScene->mNumMeshes = (unsigned int)meshes.size();
- tempScene->mMeshes = new aiMesh*[tempScene->mNumMeshes];
- ::memcpy(tempScene->mMeshes,&meshes[0],tempScene->mNumMeshes);
- }
- /* Now merge all sub scenes and attach them to the correct
- * attachment points in the scenegraph.
- */
- SceneCombiner::MergeScenes(pScene,tempScene,attach);
- /* Finished ... everything destructs automatically and all
- * temporary scenes have already been deleted by MergeScenes()
- */
- }
|