|
@@ -43,6 +43,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
* @brief Implementation of the Irr importer class
|
|
|
*/
|
|
|
|
|
|
+#include "assimp/Exceptional.h"
|
|
|
+#include "assimp/StringComparison.h"
|
|
|
#ifndef ASSIMP_BUILD_NO_IRR_IMPORTER
|
|
|
|
|
|
#include "AssetLib/Irr/IRRLoader.h"
|
|
@@ -62,28 +64,26 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
#include <assimp/DefaultLogger.hpp>
|
|
|
#include <assimp/IOSystem.hpp>
|
|
|
|
|
|
-#include <memory>
|
|
|
-
|
|
|
using namespace Assimp;
|
|
|
|
|
|
static const aiImporterDesc desc = {
|
|
|
- "Irrlicht Scene Reader",
|
|
|
- "",
|
|
|
- "",
|
|
|
- "http://irrlicht.sourceforge.net/",
|
|
|
- aiImporterFlags_SupportTextFlavour,
|
|
|
- 0,
|
|
|
- 0,
|
|
|
- 0,
|
|
|
- 0,
|
|
|
- "irr xml"
|
|
|
+ "Irrlicht Scene Reader",
|
|
|
+ "",
|
|
|
+ "",
|
|
|
+ "http://irrlicht.sourceforge.net/",
|
|
|
+ aiImporterFlags_SupportTextFlavour,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ "irr xml"
|
|
|
};
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
// Constructor to be privately used by Importer
|
|
|
IRRImporter::IRRImporter() :
|
|
|
- fps(), configSpeedFlag() {
|
|
|
- // empty
|
|
|
+ fps(), configSpeedFlag() {
|
|
|
+ // empty
|
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
@@ -93,154 +93,154 @@ IRRImporter::~IRRImporter() = default;
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
// Returns whether the class can handle the format of the given file.
|
|
|
bool IRRImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
|
|
|
- static const char *tokens[] = { "irr_scene" };
|
|
|
- return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
|
|
|
+ static const char *tokens[] = { "irr_scene" };
|
|
|
+ return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
|
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
const aiImporterDesc *IRRImporter::GetInfo() const {
|
|
|
- return &desc;
|
|
|
+ return &desc;
|
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
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 < 10.) {
|
|
|
- ASSIMP_LOG_ERROR("IRR: Invalid FPS configuration");
|
|
|
- fps = 100;
|
|
|
- }
|
|
|
-
|
|
|
- // AI_CONFIG_FAVOUR_SPEED
|
|
|
- configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED, 0));
|
|
|
+ // read the output frame rate of all node animation channels
|
|
|
+ fps = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_IRR_ANIM_FPS, 100);
|
|
|
+ if (fps < 10.) {
|
|
|
+ ASSIMP_LOG_ERROR("IRR: Invalid FPS configuration");
|
|
|
+ fps = 100;
|
|
|
+ }
|
|
|
+
|
|
|
+ // AI_CONFIG_FAVOUR_SPEED
|
|
|
+ configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED, 0));
|
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
// Build a mesh that 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
|
|
|
- vec = out->mTextureCoords[0] = new aiVector3D[4];
|
|
|
- *vec++ = v1.uv;
|
|
|
- *vec++ = v2.uv;
|
|
|
- *vec++ = v3.uv;
|
|
|
- *vec = v4.uv;
|
|
|
- return out;
|
|
|
+ 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
|
|
|
+ vec = 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) {
|
|
|
- aiMaterial *out = (aiMaterial *)(*(materials.end() - (6 - i)));
|
|
|
-
|
|
|
- aiString s;
|
|
|
- s.length = ::ai_snprintf(s.data, MAXLEN, "SkyboxSide_%u", 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 ai_real l = 10.0; // the size used by Irrlicht
|
|
|
-
|
|
|
- // FRONT SIDE
|
|
|
- meshes.push_back(BuildSingleQuadMesh(
|
|
|
- SkyboxVertex(-l, -l, -l, 0, 0, 1, 1.0, 1.0),
|
|
|
- SkyboxVertex(l, -l, -l, 0, 0, 1, 0.0, 1.0),
|
|
|
- SkyboxVertex(l, l, -l, 0, 0, 1, 0.0, 0.0),
|
|
|
- SkyboxVertex(-l, l, -l, 0, 0, 1, 1.0, 0.0)));
|
|
|
- meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size() - 6u);
|
|
|
-
|
|
|
- // LEFT SIDE
|
|
|
- meshes.push_back(BuildSingleQuadMesh(
|
|
|
- SkyboxVertex(l, -l, -l, -1, 0, 0, 1.0, 1.0),
|
|
|
- SkyboxVertex(l, -l, l, -1, 0, 0, 0.0, 1.0),
|
|
|
- SkyboxVertex(l, l, l, -1, 0, 0, 0.0, 0.0),
|
|
|
- SkyboxVertex(l, l, -l, -1, 0, 0, 1.0, 0.0)));
|
|
|
- meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size() - 5u);
|
|
|
-
|
|
|
- // BACK SIDE
|
|
|
- meshes.push_back(BuildSingleQuadMesh(
|
|
|
- SkyboxVertex(l, -l, l, 0, 0, -1, 1.0, 1.0),
|
|
|
- SkyboxVertex(-l, -l, l, 0, 0, -1, 0.0, 1.0),
|
|
|
- SkyboxVertex(-l, l, l, 0, 0, -1, 0.0, 0.0),
|
|
|
- SkyboxVertex(l, l, l, 0, 0, -1, 1.0, 0.0)));
|
|
|
- meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size() - 4u);
|
|
|
-
|
|
|
- // RIGHT SIDE
|
|
|
- meshes.push_back(BuildSingleQuadMesh(
|
|
|
- SkyboxVertex(-l, -l, l, 1, 0, 0, 1.0, 1.0),
|
|
|
- SkyboxVertex(-l, -l, -l, 1, 0, 0, 0.0, 1.0),
|
|
|
- SkyboxVertex(-l, l, -l, 1, 0, 0, 0.0, 0.0),
|
|
|
- SkyboxVertex(-l, l, l, 1, 0, 0, 1.0, 0.0)));
|
|
|
- meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size() - 3u);
|
|
|
-
|
|
|
- // TOP SIDE
|
|
|
- meshes.push_back(BuildSingleQuadMesh(
|
|
|
- SkyboxVertex(l, l, -l, 0, -1, 0, 1.0, 1.0),
|
|
|
- SkyboxVertex(l, l, l, 0, -1, 0, 0.0, 1.0),
|
|
|
- SkyboxVertex(-l, l, l, 0, -1, 0, 0.0, 0.0),
|
|
|
- SkyboxVertex(-l, l, -l, 0, -1, 0, 1.0, 0.0)));
|
|
|
- meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size() - 2u);
|
|
|
-
|
|
|
- // BOTTOM SIDE
|
|
|
- meshes.push_back(BuildSingleQuadMesh(
|
|
|
- SkyboxVertex(l, -l, l, 0, 1, 0, 0.0, 0.0),
|
|
|
- SkyboxVertex(l, -l, -l, 0, 1, 0, 1.0, 0.0),
|
|
|
- SkyboxVertex(-l, -l, -l, 0, 1, 0, 1.0, 1.0),
|
|
|
- SkyboxVertex(-l, -l, l, 0, 1, 0, 0.0, 1.0)));
|
|
|
- meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size() - 1u);
|
|
|
+ // Update the material of the skybox - replace the name and disable shading for skyboxes.
|
|
|
+ for (unsigned int i = 0; i < 6; ++i) {
|
|
|
+ aiMaterial *out = (aiMaterial *)(*(materials.end() - (6 - i)));
|
|
|
+
|
|
|
+ aiString s;
|
|
|
+ s.length = ::ai_snprintf(s.data, MAXLEN, "SkyboxSide_%u", 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 ai_real l = 10.0; // the size used by Irrlicht
|
|
|
+
|
|
|
+ // FRONT SIDE
|
|
|
+ meshes.push_back(BuildSingleQuadMesh(
|
|
|
+ SkyboxVertex(-l, -l, -l, 0, 0, 1, 1.0, 1.0),
|
|
|
+ SkyboxVertex(l, -l, -l, 0, 0, 1, 0.0, 1.0),
|
|
|
+ SkyboxVertex(l, l, -l, 0, 0, 1, 0.0, 0.0),
|
|
|
+ SkyboxVertex(-l, l, -l, 0, 0, 1, 1.0, 0.0)));
|
|
|
+ meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size() - 6u);
|
|
|
+
|
|
|
+ // LEFT SIDE
|
|
|
+ meshes.push_back(BuildSingleQuadMesh(
|
|
|
+ SkyboxVertex(l, -l, -l, -1, 0, 0, 1.0, 1.0),
|
|
|
+ SkyboxVertex(l, -l, l, -1, 0, 0, 0.0, 1.0),
|
|
|
+ SkyboxVertex(l, l, l, -1, 0, 0, 0.0, 0.0),
|
|
|
+ SkyboxVertex(l, l, -l, -1, 0, 0, 1.0, 0.0)));
|
|
|
+ meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size() - 5u);
|
|
|
+
|
|
|
+ // BACK SIDE
|
|
|
+ meshes.push_back(BuildSingleQuadMesh(
|
|
|
+ SkyboxVertex(l, -l, l, 0, 0, -1, 1.0, 1.0),
|
|
|
+ SkyboxVertex(-l, -l, l, 0, 0, -1, 0.0, 1.0),
|
|
|
+ SkyboxVertex(-l, l, l, 0, 0, -1, 0.0, 0.0),
|
|
|
+ SkyboxVertex(l, l, l, 0, 0, -1, 1.0, 0.0)));
|
|
|
+ meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size() - 4u);
|
|
|
+
|
|
|
+ // RIGHT SIDE
|
|
|
+ meshes.push_back(BuildSingleQuadMesh(
|
|
|
+ SkyboxVertex(-l, -l, l, 1, 0, 0, 1.0, 1.0),
|
|
|
+ SkyboxVertex(-l, -l, -l, 1, 0, 0, 0.0, 1.0),
|
|
|
+ SkyboxVertex(-l, l, -l, 1, 0, 0, 0.0, 0.0),
|
|
|
+ SkyboxVertex(-l, l, l, 1, 0, 0, 1.0, 0.0)));
|
|
|
+ meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size() - 3u);
|
|
|
+
|
|
|
+ // TOP SIDE
|
|
|
+ meshes.push_back(BuildSingleQuadMesh(
|
|
|
+ SkyboxVertex(l, l, -l, 0, -1, 0, 1.0, 1.0),
|
|
|
+ SkyboxVertex(l, l, l, 0, -1, 0, 0.0, 1.0),
|
|
|
+ SkyboxVertex(-l, l, l, 0, -1, 0, 0.0, 0.0),
|
|
|
+ SkyboxVertex(-l, l, -l, 0, -1, 0, 1.0, 0.0)));
|
|
|
+ meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size() - 2u);
|
|
|
+
|
|
|
+ // BOTTOM SIDE
|
|
|
+ meshes.push_back(BuildSingleQuadMesh(
|
|
|
+ SkyboxVertex(l, -l, l, 0, 1, 0, 0.0, 0.0),
|
|
|
+ SkyboxVertex(l, -l, -l, 0, 1, 0, 1.0, 0.0),
|
|
|
+ SkyboxVertex(-l, -l, -l, 0, 1, 0, 1.0, 1.0),
|
|
|
+ SkyboxVertex(-l, -l, l, 0, 1, 0, 0.0, 1.0)));
|
|
|
+ meshes.back()->mMaterialIndex = static_cast<unsigned int>(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 (UINT_MAX == defMatIdx) {
|
|
|
- defMatIdx = (unsigned int)materials.size();
|
|
|
- //TODO: add this materials to someone?
|
|
|
- /*aiMaterial* mat = new aiMaterial();
|
|
|
+ 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 (UINT_MAX == defMatIdx) {
|
|
|
+ defMatIdx = (unsigned int)materials.size();
|
|
|
+ // TODO: add this materials to someone?
|
|
|
+ /*aiMaterial* mat = new aiMaterial();
|
|
|
|
|
|
aiString s;
|
|
|
s.Set(AI_DEFAULT_MATERIAL_NAME);
|
|
@@ -248,1110 +248,1101 @@ void IRRImporter::CopyMaterial(std::vector<aiMaterial *> &materials,
|
|
|
|
|
|
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) {
|
|
|
- ASSIMP_LOG_INFO("IRR: Skipping additional materials");
|
|
|
- }
|
|
|
-
|
|
|
- mesh->mMaterialIndex = (unsigned int)materials.size();
|
|
|
- materials.push_back(inmaterials[0].first);
|
|
|
+ }
|
|
|
+ mesh->mMaterialIndex = defMatIdx;
|
|
|
+ return;
|
|
|
+ } else if (inmaterials.size() > 1) {
|
|
|
+ ASSIMP_LOG_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));
|
|
|
+ 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;
|
|
|
+ if (angle < 3)
|
|
|
+ angle = 3;
|
|
|
+ else if (angle < 10)
|
|
|
+ angle = 10;
|
|
|
+ else if (angle < 20)
|
|
|
+ angle = 20;
|
|
|
+ else if (angle < 30)
|
|
|
+ angle = 30;
|
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
void IRRImporter::ComputeAnimations(Node *root, aiNode *real, std::vector<aiNodeAnim *> &anims) {
|
|
|
- ai_assert(nullptr != root && nullptr != real);
|
|
|
-
|
|
|
- // XXX totally WIP - doesn't produce proper results, need to evaluate
|
|
|
- // whether there's any use for Irrlicht's proprietary scene format
|
|
|
- // outside Irrlicht ...
|
|
|
- // This also applies to the above function of FindSuitableMultiple and ClampSpline which are
|
|
|
- // solely used in this function
|
|
|
-
|
|
|
- if (root->animators.empty()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- unsigned int total(0);
|
|
|
- for (std::list<Animator>::iterator it = root->animators.begin(); it != root->animators.end(); ++it) {
|
|
|
- if ((*it).type == Animator::UNKNOWN || (*it).type == Animator::OTHER) {
|
|
|
- ASSIMP_LOG_WARN("IRR: Skipping unknown or unsupported animator");
|
|
|
- continue;
|
|
|
- }
|
|
|
- ++total;
|
|
|
- }
|
|
|
- if (!total) {
|
|
|
- return;
|
|
|
- } else if (1 == total) {
|
|
|
- ASSIMP_LOG_WARN("IRR: Adding dummy nodes to simulate multiple animators");
|
|
|
- }
|
|
|
-
|
|
|
- // NOTE: 1 tick == i millisecond
|
|
|
-
|
|
|
- unsigned int cur = 0;
|
|
|
- for (std::list<Animator>::iterator it = root->animators.begin();
|
|
|
- it != root->animators.end(); ++it) {
|
|
|
- if ((*it).type == Animator::UNKNOWN || (*it).type == Animator::OTHER) continue;
|
|
|
-
|
|
|
- Animator &in = *it;
|
|
|
- aiNodeAnim *anim = new aiNodeAnim();
|
|
|
-
|
|
|
- if (cur != total - 1) {
|
|
|
- // Build a new name - a prefix instead of a suffix because it is
|
|
|
- // easier to check against
|
|
|
- anim->mNodeName.length = ::ai_snprintf(anim->mNodeName.data, MAXLEN,
|
|
|
- "$INST_DUMMY_%i_%s", total - 1,
|
|
|
- (root->name.length() ? root->name.c_str() : ""));
|
|
|
-
|
|
|
- // we'll also need to insert a dummy in the node hierarchy.
|
|
|
- aiNode *dummy = new aiNode();
|
|
|
-
|
|
|
- for (unsigned int i = 0; i < real->mParent->mNumChildren; ++i)
|
|
|
- if (real->mParent->mChildren[i] == real)
|
|
|
- real->mParent->mChildren[i] = dummy;
|
|
|
-
|
|
|
- dummy->mParent = real->mParent;
|
|
|
- dummy->mName = anim->mNodeName;
|
|
|
-
|
|
|
- dummy->mNumChildren = 1;
|
|
|
- dummy->mChildren = new aiNode *[dummy->mNumChildren];
|
|
|
- dummy->mChildren[0] = real;
|
|
|
-
|
|
|
- // the transformation matrix of the dummy node is the identity
|
|
|
-
|
|
|
- real->mParent = dummy;
|
|
|
- } else
|
|
|
- anim->mNodeName.Set(root->name);
|
|
|
- ++cur;
|
|
|
-
|
|
|
- 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;
|
|
|
-
|
|
|
- if ((angles[0] * angles[1]) != 0 && (angles[1] * angles[2]) != 0) {
|
|
|
- FindSuitableMultiple(angles[0]);
|
|
|
- FindSuitableMultiple(angles[1]);
|
|
|
- FindSuitableMultiple(angles[2]);
|
|
|
- }
|
|
|
-
|
|
|
- int lcm = 360;
|
|
|
-
|
|
|
- if (angles[0])
|
|
|
- lcm = Math::lcm(lcm, angles[0]);
|
|
|
-
|
|
|
- if (angles[1])
|
|
|
- lcm = Math::lcm(lcm, angles[1]);
|
|
|
-
|
|
|
- if (angles[2])
|
|
|
- lcm = Math::lcm(lcm, angles[2]);
|
|
|
-
|
|
|
- if (360 == lcm)
|
|
|
- break;
|
|
|
-
|
|
|
-
|
|
|
- // 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 = 0.f;
|
|
|
- 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]);
|
|
|
-
|
|
|
- anim->mNumRotationKeys = (unsigned int)(max * fps);
|
|
|
- anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys];
|
|
|
-
|
|
|
- // begin with a zero angle
|
|
|
- aiVector3D angle;
|
|
|
- for (unsigned int i = 0; i < anim->mNumRotationKeys; ++i) {
|
|
|
- // build the quaternion for the given euler angles
|
|
|
- aiQuatKey &q = anim->mRotationKeys[i];
|
|
|
-
|
|
|
- q.mValue = aiQuaternion(angle.x, angle.y, angle.z);
|
|
|
- q.mTime = (double)i;
|
|
|
-
|
|
|
- // increase the angle
|
|
|
- angle += in.direction;
|
|
|
- }
|
|
|
-
|
|
|
- // This animation is repeated and repeated ...
|
|
|
- anim->mPostState = anim->mPreState = aiAnimBehaviour_REPEAT;
|
|
|
- } break;
|
|
|
-
|
|
|
- case Animator::FLY_CIRCLE: {
|
|
|
- // -----------------------------------------------------
|
|
|
- // Find out how much time we'll need to perform a
|
|
|
- // full circle.
|
|
|
- // -----------------------------------------------------
|
|
|
- const double seconds = (1. / in.speed) / 1000.;
|
|
|
- const double tdelta = 1000. / fps;
|
|
|
-
|
|
|
- anim->mNumPositionKeys = (unsigned int)(fps * seconds);
|
|
|
- anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
|
|
|
-
|
|
|
- // from Irrlicht, what else should we do than copying it?
|
|
|
- aiVector3D vecU, vecV;
|
|
|
- if (in.direction.y) {
|
|
|
- vecV = aiVector3D(50, 0, 0) ^ in.direction;
|
|
|
- } else
|
|
|
- vecV = aiVector3D(0, 50, 00) ^ in.direction;
|
|
|
- vecV.Normalize();
|
|
|
- vecU = (vecV ^ in.direction).Normalize();
|
|
|
-
|
|
|
- // build the output keys
|
|
|
- for (unsigned int i = 0; i < anim->mNumPositionKeys; ++i) {
|
|
|
- aiVectorKey &key = anim->mPositionKeys[i];
|
|
|
- key.mTime = i * tdelta;
|
|
|
-
|
|
|
- const ai_real t = (ai_real)(in.speed * key.mTime);
|
|
|
- key.mValue = in.circleCenter + in.circleRadius * ((vecU * std::cos(t)) + (vecV * std::sin(t)));
|
|
|
- }
|
|
|
-
|
|
|
- // This animation is repeated and repeated ...
|
|
|
- anim->mPostState = anim->mPreState = aiAnimBehaviour_REPEAT;
|
|
|
- } break;
|
|
|
-
|
|
|
- case Animator::FLY_STRAIGHT: {
|
|
|
- anim->mPostState = anim->mPreState = (in.loop ? aiAnimBehaviour_REPEAT : aiAnimBehaviour_CONSTANT);
|
|
|
- const double seconds = in.timeForWay / 1000.;
|
|
|
- const double tdelta = 1000. / fps;
|
|
|
-
|
|
|
- anim->mNumPositionKeys = (unsigned int)(fps * seconds);
|
|
|
- anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
|
|
|
-
|
|
|
- aiVector3D diff = in.direction - in.circleCenter;
|
|
|
- const ai_real lengthOfWay = diff.Length();
|
|
|
- diff.Normalize();
|
|
|
-
|
|
|
- const double timeFactor = lengthOfWay / in.timeForWay;
|
|
|
-
|
|
|
- // build the output keys
|
|
|
- for (unsigned int i = 0; i < anim->mNumPositionKeys; ++i) {
|
|
|
- aiVectorKey &key = anim->mPositionKeys[i];
|
|
|
- key.mTime = i * tdelta;
|
|
|
- key.mValue = in.circleCenter + diff * ai_real(timeFactor * key.mTime);
|
|
|
- }
|
|
|
- } break;
|
|
|
-
|
|
|
- case Animator::FOLLOW_SPLINE: {
|
|
|
- // repeat outside the defined time range
|
|
|
- anim->mPostState = anim->mPreState = aiAnimBehaviour_REPEAT;
|
|
|
- const int size = (int)in.splineKeys.size();
|
|
|
- if (!size) {
|
|
|
- // We have no point in the spline. That's bad. Really bad.
|
|
|
- ASSIMP_LOG_WARN("IRR: Spline animators with no points defined");
|
|
|
-
|
|
|
- delete anim;
|
|
|
- anim = nullptr;
|
|
|
- break;
|
|
|
- } else if (size == 1) {
|
|
|
- // We have just one point in the spline so we don't need the full calculation
|
|
|
- anim->mNumPositionKeys = 1;
|
|
|
- anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
|
|
|
-
|
|
|
- anim->mPositionKeys[0].mValue = in.splineKeys[0].mValue;
|
|
|
- anim->mPositionKeys[0].mTime = 0.f;
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- unsigned int ticksPerFull = 15;
|
|
|
- anim->mNumPositionKeys = (unsigned int)(ticksPerFull * fps);
|
|
|
- anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
|
|
|
-
|
|
|
- for (unsigned int i = 0; i < anim->mNumPositionKeys; ++i) {
|
|
|
- aiVectorKey &key = anim->mPositionKeys[i];
|
|
|
-
|
|
|
- const ai_real dt = (i * in.speed * ai_real(0.001));
|
|
|
- const ai_real u = dt - std::floor(dt);
|
|
|
- const int idx = (int)std::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 ai_real u2 = u * u;
|
|
|
- const ai_real u3 = u2 * 2;
|
|
|
-
|
|
|
- const ai_real h1 = ai_real(2.0) * u3 - ai_real(3.0) * u2 + ai_real(1.0);
|
|
|
- const ai_real h2 = ai_real(-2.0) * u3 + ai_real(3.0) * u3;
|
|
|
- const ai_real h3 = u3 - ai_real(2.0) * u3;
|
|
|
- const ai_real 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
|
|
|
- key.mValue = t2;
|
|
|
- key.mTime = (double)i;
|
|
|
- }
|
|
|
- } break;
|
|
|
- default:
|
|
|
- // UNKNOWN , OTHER
|
|
|
- break;
|
|
|
- };
|
|
|
- if (anim) {
|
|
|
- anims.push_back(anim);
|
|
|
- ++total;
|
|
|
- }
|
|
|
- }
|
|
|
+ ai_assert(nullptr != root && nullptr != real);
|
|
|
+
|
|
|
+ // XXX totally WIP - doesn't produce proper results, need to evaluate
|
|
|
+ // whether there's any use for Irrlicht's proprietary scene format
|
|
|
+ // outside Irrlicht ...
|
|
|
+ // This also applies to the above function of FindSuitableMultiple and ClampSpline which are
|
|
|
+ // solely used in this function
|
|
|
+
|
|
|
+ if (root->animators.empty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ unsigned int total(0);
|
|
|
+ for (std::list<Animator>::iterator it = root->animators.begin(); it != root->animators.end(); ++it) {
|
|
|
+ if ((*it).type == Animator::UNKNOWN || (*it).type == Animator::OTHER) {
|
|
|
+ ASSIMP_LOG_WARN("IRR: Skipping unknown or unsupported animator");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ ++total;
|
|
|
+ }
|
|
|
+ if (!total) {
|
|
|
+ return;
|
|
|
+ } else if (1 == total) {
|
|
|
+ ASSIMP_LOG_WARN("IRR: Adding dummy nodes to simulate multiple animators");
|
|
|
+ }
|
|
|
+
|
|
|
+ // NOTE: 1 tick == i millisecond
|
|
|
+
|
|
|
+ unsigned int cur = 0;
|
|
|
+ for (std::list<Animator>::iterator it = root->animators.begin();
|
|
|
+ it != root->animators.end(); ++it) {
|
|
|
+ if ((*it).type == Animator::UNKNOWN || (*it).type == Animator::OTHER) continue;
|
|
|
+
|
|
|
+ Animator &in = *it;
|
|
|
+ aiNodeAnim *anim = new aiNodeAnim();
|
|
|
+
|
|
|
+ if (cur != total - 1) {
|
|
|
+ // Build a new name - a prefix instead of a suffix because it is
|
|
|
+ // easier to check against
|
|
|
+ anim->mNodeName.length = ::ai_snprintf(anim->mNodeName.data, MAXLEN,
|
|
|
+ "$INST_DUMMY_%i_%s", total - 1,
|
|
|
+ (root->name.length() ? root->name.c_str() : ""));
|
|
|
+
|
|
|
+ // we'll also need to insert a dummy in the node hierarchy.
|
|
|
+ aiNode *dummy = new aiNode();
|
|
|
+
|
|
|
+ for (unsigned int i = 0; i < real->mParent->mNumChildren; ++i)
|
|
|
+ if (real->mParent->mChildren[i] == real)
|
|
|
+ real->mParent->mChildren[i] = dummy;
|
|
|
+
|
|
|
+ dummy->mParent = real->mParent;
|
|
|
+ dummy->mName = anim->mNodeName;
|
|
|
+
|
|
|
+ dummy->mNumChildren = 1;
|
|
|
+ dummy->mChildren = new aiNode *[dummy->mNumChildren];
|
|
|
+ dummy->mChildren[0] = real;
|
|
|
+
|
|
|
+ // the transformation matrix of the dummy node is the identity
|
|
|
+
|
|
|
+ real->mParent = dummy;
|
|
|
+ } else
|
|
|
+ anim->mNodeName.Set(root->name);
|
|
|
+ ++cur;
|
|
|
+
|
|
|
+ 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;
|
|
|
+
|
|
|
+ if ((angles[0] * angles[1]) != 0 && (angles[1] * angles[2]) != 0) {
|
|
|
+ FindSuitableMultiple(angles[0]);
|
|
|
+ FindSuitableMultiple(angles[1]);
|
|
|
+ FindSuitableMultiple(angles[2]);
|
|
|
+ }
|
|
|
+
|
|
|
+ int lcm = 360;
|
|
|
+
|
|
|
+ if (angles[0])
|
|
|
+ lcm = Math::lcm(lcm, angles[0]);
|
|
|
+
|
|
|
+ if (angles[1])
|
|
|
+ lcm = Math::lcm(lcm, angles[1]);
|
|
|
+
|
|
|
+ if (angles[2])
|
|
|
+ lcm = Math::lcm(lcm, angles[2]);
|
|
|
+
|
|
|
+ if (360 == lcm)
|
|
|
+ break;
|
|
|
+
|
|
|
+ // 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 = 0.f;
|
|
|
+ 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]);
|
|
|
+
|
|
|
+ anim->mNumRotationKeys = (unsigned int)(max * fps);
|
|
|
+ anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys];
|
|
|
+
|
|
|
+ // begin with a zero angle
|
|
|
+ aiVector3D angle;
|
|
|
+ for (unsigned int i = 0; i < anim->mNumRotationKeys; ++i) {
|
|
|
+ // build the quaternion for the given euler angles
|
|
|
+ aiQuatKey &q = anim->mRotationKeys[i];
|
|
|
+
|
|
|
+ q.mValue = aiQuaternion(angle.x, angle.y, angle.z);
|
|
|
+ q.mTime = (double)i;
|
|
|
+
|
|
|
+ // increase the angle
|
|
|
+ angle += in.direction;
|
|
|
+ }
|
|
|
+
|
|
|
+ // This animation is repeated and repeated ...
|
|
|
+ anim->mPostState = anim->mPreState = aiAnimBehaviour_REPEAT;
|
|
|
+ } break;
|
|
|
+
|
|
|
+ case Animator::FLY_CIRCLE: {
|
|
|
+ // -----------------------------------------------------
|
|
|
+ // Find out how much time we'll need to perform a
|
|
|
+ // full circle.
|
|
|
+ // -----------------------------------------------------
|
|
|
+ const double seconds = (1. / in.speed) / 1000.;
|
|
|
+ const double tdelta = 1000. / fps;
|
|
|
+
|
|
|
+ anim->mNumPositionKeys = (unsigned int)(fps * seconds);
|
|
|
+ anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
|
|
|
+
|
|
|
+ // from Irrlicht, what else should we do than copying it?
|
|
|
+ aiVector3D vecU, vecV;
|
|
|
+ if (in.direction.y) {
|
|
|
+ vecV = aiVector3D(50, 0, 0) ^ in.direction;
|
|
|
+ } else
|
|
|
+ vecV = aiVector3D(0, 50, 00) ^ in.direction;
|
|
|
+ vecV.Normalize();
|
|
|
+ vecU = (vecV ^ in.direction).Normalize();
|
|
|
+
|
|
|
+ // build the output keys
|
|
|
+ for (unsigned int i = 0; i < anim->mNumPositionKeys; ++i) {
|
|
|
+ aiVectorKey &key = anim->mPositionKeys[i];
|
|
|
+ key.mTime = i * tdelta;
|
|
|
+
|
|
|
+ const ai_real t = (ai_real)(in.speed * key.mTime);
|
|
|
+ key.mValue = in.circleCenter + in.circleRadius * ((vecU * std::cos(t)) + (vecV * std::sin(t)));
|
|
|
+ }
|
|
|
+
|
|
|
+ // This animation is repeated and repeated ...
|
|
|
+ anim->mPostState = anim->mPreState = aiAnimBehaviour_REPEAT;
|
|
|
+ } break;
|
|
|
+
|
|
|
+ case Animator::FLY_STRAIGHT: {
|
|
|
+ anim->mPostState = anim->mPreState = (in.loop ? aiAnimBehaviour_REPEAT : aiAnimBehaviour_CONSTANT);
|
|
|
+ const double seconds = in.timeForWay / 1000.;
|
|
|
+ const double tdelta = 1000. / fps;
|
|
|
+
|
|
|
+ anim->mNumPositionKeys = (unsigned int)(fps * seconds);
|
|
|
+ anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
|
|
|
+
|
|
|
+ aiVector3D diff = in.direction - in.circleCenter;
|
|
|
+ const ai_real lengthOfWay = diff.Length();
|
|
|
+ diff.Normalize();
|
|
|
+
|
|
|
+ const double timeFactor = lengthOfWay / in.timeForWay;
|
|
|
+
|
|
|
+ // build the output keys
|
|
|
+ for (unsigned int i = 0; i < anim->mNumPositionKeys; ++i) {
|
|
|
+ aiVectorKey &key = anim->mPositionKeys[i];
|
|
|
+ key.mTime = i * tdelta;
|
|
|
+ key.mValue = in.circleCenter + diff * ai_real(timeFactor * key.mTime);
|
|
|
+ }
|
|
|
+ } break;
|
|
|
+
|
|
|
+ case Animator::FOLLOW_SPLINE: {
|
|
|
+ // repeat outside the defined time range
|
|
|
+ anim->mPostState = anim->mPreState = aiAnimBehaviour_REPEAT;
|
|
|
+ const int size = (int)in.splineKeys.size();
|
|
|
+ if (!size) {
|
|
|
+ // We have no point in the spline. That's bad. Really bad.
|
|
|
+ ASSIMP_LOG_WARN("IRR: Spline animators with no points defined");
|
|
|
+
|
|
|
+ delete anim;
|
|
|
+ anim = nullptr;
|
|
|
+ break;
|
|
|
+ } else if (size == 1) {
|
|
|
+ // We have just one point in the spline so we don't need the full calculation
|
|
|
+ anim->mNumPositionKeys = 1;
|
|
|
+ anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
|
|
|
+
|
|
|
+ anim->mPositionKeys[0].mValue = in.splineKeys[0].mValue;
|
|
|
+ anim->mPositionKeys[0].mTime = 0.f;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ unsigned int ticksPerFull = 15;
|
|
|
+ anim->mNumPositionKeys = (unsigned int)(ticksPerFull * fps);
|
|
|
+ anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
|
|
|
+
|
|
|
+ for (unsigned int i = 0; i < anim->mNumPositionKeys; ++i) {
|
|
|
+ aiVectorKey &key = anim->mPositionKeys[i];
|
|
|
+
|
|
|
+ const ai_real dt = (i * in.speed * ai_real(0.001));
|
|
|
+ const ai_real u = dt - std::floor(dt);
|
|
|
+ const int idx = (int)std::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 ai_real u2 = u * u;
|
|
|
+ const ai_real u3 = u2 * 2;
|
|
|
+
|
|
|
+ const ai_real h1 = ai_real(2.0) * u3 - ai_real(3.0) * u2 + ai_real(1.0);
|
|
|
+ const ai_real h2 = ai_real(-2.0) * u3 + ai_real(3.0) * u3;
|
|
|
+ const ai_real h3 = u3 - ai_real(2.0) * u3;
|
|
|
+ const ai_real 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
|
|
|
+ key.mValue = t2;
|
|
|
+ key.mTime = (double)i;
|
|
|
+ }
|
|
|
+ } break;
|
|
|
+ default:
|
|
|
+ // UNKNOWN , OTHER
|
|
|
+ break;
|
|
|
+ };
|
|
|
+ if (anim) {
|
|
|
+ anims.push_back(anim);
|
|
|
+ ++total;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
// This function is maybe more generic than we'd need it here
|
|
|
void SetupMapping(aiMaterial *mat, aiTextureMapping mode, const aiVector3D &axis = aiVector3D(0.f, 0.f, -1.f)) {
|
|
|
- if (nullptr == mat) {
|
|
|
- return;
|
|
|
- }
|
|
|
+ if (nullptr == mat) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
// Check whether there are texture properties defined - setup
|
|
|
- // the desired texture mapping mode for all of them and ignore
|
|
|
- // all UV settings we might encounter. WE HAVE NO UVS!
|
|
|
-
|
|
|
- std::vector<aiMaterialProperty *> p;
|
|
|
- p.reserve(mat->mNumProperties + 1);
|
|
|
-
|
|
|
- for (unsigned int i = 0; i < mat->mNumProperties; ++i) {
|
|
|
- aiMaterialProperty *prop = mat->mProperties[i];
|
|
|
- if (!::strcmp(prop->mKey.data, "$tex.file")) {
|
|
|
- // Setup the mapping key
|
|
|
- aiMaterialProperty *m = new aiMaterialProperty();
|
|
|
- m->mKey.Set("$tex.mapping");
|
|
|
- m->mIndex = prop->mIndex;
|
|
|
- m->mSemantic = prop->mSemantic;
|
|
|
- m->mType = aiPTI_Integer;
|
|
|
-
|
|
|
- m->mDataLength = 4;
|
|
|
- m->mData = new char[4];
|
|
|
- *((int *)m->mData) = mode;
|
|
|
-
|
|
|
- p.push_back(prop);
|
|
|
- p.push_back(m);
|
|
|
-
|
|
|
- // Setup the mapping axis
|
|
|
- if (mode == aiTextureMapping_CYLINDER || mode == aiTextureMapping_PLANE || mode == aiTextureMapping_SPHERE) {
|
|
|
- m = new aiMaterialProperty();
|
|
|
- m->mKey.Set("$tex.mapaxis");
|
|
|
- m->mIndex = prop->mIndex;
|
|
|
- m->mSemantic = prop->mSemantic;
|
|
|
- m->mType = aiPTI_Float;
|
|
|
-
|
|
|
- m->mDataLength = 12;
|
|
|
- m->mData = new char[12];
|
|
|
- *((aiVector3D *)m->mData) = axis;
|
|
|
- p.push_back(m);
|
|
|
- }
|
|
|
- } else if (!::strcmp(prop->mKey.data, "$tex.uvwsrc")) {
|
|
|
- delete mat->mProperties[i];
|
|
|
- } else
|
|
|
- p.push_back(prop);
|
|
|
- }
|
|
|
-
|
|
|
- if (p.empty()) return;
|
|
|
-
|
|
|
- // rebuild the output array
|
|
|
- if (p.size() > mat->mNumAllocated) {
|
|
|
- delete[] mat->mProperties;
|
|
|
- mat->mProperties = new aiMaterialProperty *[p.size() * 2];
|
|
|
-
|
|
|
- mat->mNumAllocated = static_cast<unsigned int>(p.size() * 2);
|
|
|
- }
|
|
|
- mat->mNumProperties = (unsigned int)p.size();
|
|
|
- ::memcpy(mat->mProperties, &p[0], sizeof(void *) * mat->mNumProperties);
|
|
|
+ // the desired texture mapping mode for all of them and ignore
|
|
|
+ // all UV settings we might encounter. WE HAVE NO UVS!
|
|
|
+
|
|
|
+ std::vector<aiMaterialProperty *> p;
|
|
|
+ p.reserve(mat->mNumProperties + 1);
|
|
|
+
|
|
|
+ for (unsigned int i = 0; i < mat->mNumProperties; ++i) {
|
|
|
+ aiMaterialProperty *prop = mat->mProperties[i];
|
|
|
+ if (!::strcmp(prop->mKey.data, "$tex.file")) {
|
|
|
+ // Setup the mapping key
|
|
|
+ aiMaterialProperty *m = new aiMaterialProperty();
|
|
|
+ m->mKey.Set("$tex.mapping");
|
|
|
+ m->mIndex = prop->mIndex;
|
|
|
+ m->mSemantic = prop->mSemantic;
|
|
|
+ m->mType = aiPTI_Integer;
|
|
|
+
|
|
|
+ m->mDataLength = 4;
|
|
|
+ m->mData = new char[4];
|
|
|
+ *((int *)m->mData) = mode;
|
|
|
+
|
|
|
+ p.push_back(prop);
|
|
|
+ p.push_back(m);
|
|
|
+
|
|
|
+ // Setup the mapping axis
|
|
|
+ if (mode == aiTextureMapping_CYLINDER || mode == aiTextureMapping_PLANE || mode == aiTextureMapping_SPHERE) {
|
|
|
+ m = new aiMaterialProperty();
|
|
|
+ m->mKey.Set("$tex.mapaxis");
|
|
|
+ m->mIndex = prop->mIndex;
|
|
|
+ m->mSemantic = prop->mSemantic;
|
|
|
+ m->mType = aiPTI_Float;
|
|
|
+
|
|
|
+ m->mDataLength = 12;
|
|
|
+ m->mData = new char[12];
|
|
|
+ *((aiVector3D *)m->mData) = axis;
|
|
|
+ p.push_back(m);
|
|
|
+ }
|
|
|
+ } else if (!::strcmp(prop->mKey.data, "$tex.uvwsrc")) {
|
|
|
+ delete mat->mProperties[i];
|
|
|
+ } else
|
|
|
+ p.push_back(prop);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (p.empty()) return;
|
|
|
+
|
|
|
+ // rebuild the output array
|
|
|
+ if (p.size() > mat->mNumAllocated) {
|
|
|
+ delete[] mat->mProperties;
|
|
|
+ mat->mProperties = new aiMaterialProperty *[p.size() * 2];
|
|
|
+
|
|
|
+ mat->mNumAllocated = static_cast<unsigned int>(p.size() * 2);
|
|
|
+ }
|
|
|
+ mat->mNumProperties = (unsigned int)p.size();
|
|
|
+ ::memcpy(mat->mProperties, &p[0], sizeof(void *) * mat->mNumProperties);
|
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
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();
|
|
|
- //unsigned int meshTrafoAssign = 0;
|
|
|
-
|
|
|
- // Now determine the type of the node
|
|
|
- switch (root->type) {
|
|
|
- case Node::ANIMMESH:
|
|
|
- case Node::MESH: {
|
|
|
- if (!root->meshPath.length())
|
|
|
- break;
|
|
|
-
|
|
|
- // 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 *localScene = batch.GetImport(root->id);
|
|
|
- if (!localScene) {
|
|
|
- ASSIMP_LOG_ERROR("IRR: Unable to load external file: ", root->meshPath);
|
|
|
- break;
|
|
|
- }
|
|
|
- attach.emplace_back(localScene, rootOut);
|
|
|
-
|
|
|
- // Now combine the material we've loaded for this mesh
|
|
|
- // with the real materials 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)localScene->mNumMaterials) {
|
|
|
- ASSIMP_LOG_WARN("IRR: Failed to match imported materials "
|
|
|
- "with the materials found in the IRR scene file");
|
|
|
-
|
|
|
- break;
|
|
|
- }
|
|
|
- for (unsigned int i = 0; i < localScene->mNumMaterials; ++i) {
|
|
|
- // Delete the old material, we don't need it anymore
|
|
|
- delete localScene->mMaterials[i];
|
|
|
-
|
|
|
- std::pair<aiMaterial *, unsigned int> &src = root->materials[i];
|
|
|
- localScene->mMaterials[i] = src.first;
|
|
|
- }
|
|
|
-
|
|
|
- // NOTE: Each mesh should have exactly one material assigned,
|
|
|
- // but we do it in a separate loop if this behavior changes
|
|
|
- // in future.
|
|
|
- for (unsigned int i = 0; i < localScene->mNumMeshes; ++i) {
|
|
|
- // Process material flags
|
|
|
- aiMesh *mesh = localScene->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];
|
|
|
- aiMaterial *mat = (aiMaterial *)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) {
|
|
|
- ASSIMP_LOG_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 light-map, normal-map, 2layered material) we need to
|
|
|
- // setup the correct UV index for it. The texture can either
|
|
|
- // be diffuse (light-map & 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 polygons on a specific axis. Just
|
|
|
- // use some hard-coded limits to approximate this ...
|
|
|
- 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 / 2;
|
|
|
-
|
|
|
- // Copy one output material
|
|
|
- CopyMaterial(materials, root->materials, defMatIdx, meshes.back());
|
|
|
-
|
|
|
- // Now adjust this output material - if there is a first texture
|
|
|
- // set, setup spherical UV mapping around the Y axis.
|
|
|
- SetupMapping((aiMaterial *)materials.back(), aiTextureMapping_SPHERE);
|
|
|
- } 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());
|
|
|
-
|
|
|
- // Now adjust this output material - if there is a first texture
|
|
|
- // set, setup cubic UV mapping
|
|
|
- SetupMapping((aiMaterial *)materials.back(), aiTextureMapping_BOX);
|
|
|
- } break;
|
|
|
-
|
|
|
- case Node::SKYBOX: {
|
|
|
- // A sky-box is defined by six materials
|
|
|
- if (root->materials.size() < 6) {
|
|
|
- ASSIMP_LOG_ERROR("IRR: There should be six materials for a skybox");
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- // copy those materials and generate 6 meshes for our new sky-box
|
|
|
- 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;
|
|
|
- ASSIMP_LOG_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
|
|
|
- ASSIMP_LOG_ERROR("IRR: Unsupported node - TERRAIN");
|
|
|
- } break;
|
|
|
- default:
|
|
|
- // DUMMY
|
|
|
- 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)
|
|
|
- //std::swap((float&)root->rotation.z,(float&)root->rotation.y);
|
|
|
- rootOut->mTransformation.FromEulerAnglesXYZ(AI_DEG_TO_RAD(root->rotation));
|
|
|
-
|
|
|
- // 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, rootOut, anims);
|
|
|
-
|
|
|
- // 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);
|
|
|
- }
|
|
|
- }
|
|
|
+ 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();
|
|
|
+ // unsigned int meshTrafoAssign = 0;
|
|
|
+
|
|
|
+ // Now determine the type of the node
|
|
|
+ switch (root->type) {
|
|
|
+ case Node::ANIMMESH:
|
|
|
+ case Node::MESH: {
|
|
|
+ if (!root->meshPath.length())
|
|
|
+ break;
|
|
|
+
|
|
|
+ // 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 *localScene = batch.GetImport(root->id);
|
|
|
+ if (!localScene) {
|
|
|
+ ASSIMP_LOG_ERROR("IRR: Unable to load external file: ", root->meshPath);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ attach.emplace_back(localScene, rootOut);
|
|
|
+
|
|
|
+ // Now combine the material we've loaded for this mesh
|
|
|
+ // with the real materials 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)localScene->mNumMaterials) {
|
|
|
+ ASSIMP_LOG_WARN("IRR: Failed to match imported materials "
|
|
|
+ "with the materials found in the IRR scene file");
|
|
|
+
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ for (unsigned int i = 0; i < localScene->mNumMaterials; ++i) {
|
|
|
+ // Delete the old material, we don't need it anymore
|
|
|
+ delete localScene->mMaterials[i];
|
|
|
+
|
|
|
+ std::pair<aiMaterial *, unsigned int> &src = root->materials[i];
|
|
|
+ localScene->mMaterials[i] = src.first;
|
|
|
+ }
|
|
|
+
|
|
|
+ // NOTE: Each mesh should have exactly one material assigned,
|
|
|
+ // but we do it in a separate loop if this behavior changes
|
|
|
+ // in future.
|
|
|
+ for (unsigned int i = 0; i < localScene->mNumMeshes; ++i) {
|
|
|
+ // Process material flags
|
|
|
+ aiMesh *mesh = localScene->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];
|
|
|
+ aiMaterial *mat = (aiMaterial *)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) {
|
|
|
+ ASSIMP_LOG_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 light-map, normal-map, 2layered material) we need to
|
|
|
+ // setup the correct UV index for it. The texture can either
|
|
|
+ // be diffuse (light-map & 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 polygons on a specific axis. Just
|
|
|
+ // use some hard-coded limits to approximate this ...
|
|
|
+ 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 / 2;
|
|
|
+
|
|
|
+ // Copy one output material
|
|
|
+ CopyMaterial(materials, root->materials, defMatIdx, meshes.back());
|
|
|
+
|
|
|
+ // Now adjust this output material - if there is a first texture
|
|
|
+ // set, setup spherical UV mapping around the Y axis.
|
|
|
+ SetupMapping((aiMaterial *)materials.back(), aiTextureMapping_SPHERE);
|
|
|
+ } 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());
|
|
|
+
|
|
|
+ // Now adjust this output material - if there is a first texture
|
|
|
+ // set, setup cubic UV mapping
|
|
|
+ SetupMapping((aiMaterial *)materials.back(), aiTextureMapping_BOX);
|
|
|
+ } break;
|
|
|
+
|
|
|
+ case Node::SKYBOX: {
|
|
|
+ // A sky-box is defined by six materials
|
|
|
+ if (root->materials.size() < 6) {
|
|
|
+ ASSIMP_LOG_ERROR("IRR: There should be six materials for a skybox");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ // copy those materials and generate 6 meshes for our new sky-box
|
|
|
+ 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;
|
|
|
+ ASSIMP_LOG_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
|
|
|
+ ASSIMP_LOG_ERROR("IRR: Unsupported node - TERRAIN");
|
|
|
+ } break;
|
|
|
+ default:
|
|
|
+ // DUMMY
|
|
|
+ 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)
|
|
|
+ // std::swap((float&)root->rotation.z,(float&)root->rotation.y);
|
|
|
+ rootOut->mTransformation.FromEulerAnglesXYZ(AI_DEG_TO_RAD(root->rotation));
|
|
|
+
|
|
|
+ // 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, rootOut, anims);
|
|
|
+
|
|
|
+ // 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);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void IRRImporter::ParseNodeAttributes(pugi::xml_node &attributesNode, IRRImporter::Node *nd, BatchLoader &batch) {
|
|
|
+ ai_assert(!ASSIMP_stricmp(attributesNode.name(), "attributes")); // Node must be <attributes>
|
|
|
+ ai_assert(nd != nullptr); // dude
|
|
|
+
|
|
|
+ // Big switch statement that tests for various tags inside <attributes>
|
|
|
+ // and applies them to nd
|
|
|
+ // 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;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if (!ASSIMP_stricmp(attribute.name(), "float")) { // <float />
|
|
|
+ FloatProperty prop;
|
|
|
+ ReadFloatProperty(prop, attribute);
|
|
|
+ if (prop.name == "FramesPerSecond" && Node::ANIMMESH == nd->type) {
|
|
|
+ nd->framesPerSecond = prop.value;
|
|
|
+ } else if (Node::CAMERA == nd->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 == nd->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 == 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;
|
|
|
+
|
|
|
+ /* 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 == nd->type) {
|
|
|
+ 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;
|
|
|
+
|
|
|
+ ASSIMP_LOG_ERROR("Ignoring light of unknown type: ", prop.value);
|
|
|
+ }
|
|
|
+ } else if ((prop.name == "Mesh" && Node::MESH == nd->type) ||
|
|
|
+ Node::ANIMMESH == nd->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 != nd->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 {
|
|
|
+ nd->id = batch.AddLoadRequest(prop.value, pp, &map);
|
|
|
+ nd->meshPath = prop.value;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void IRRImporter::ParseAnimators(pugi::xml_node &animatorNode, IRRImporter::Node *nd) {
|
|
|
+ Animator *curAnim = nullptr;
|
|
|
+ // Make empty animator
|
|
|
+ nd->animators.emplace_back();
|
|
|
+ curAnim = &nd->animators.back(); // Push it back
|
|
|
+ pugi::xml_node attributes = animatorNode.child("attributes");
|
|
|
+ if (!attributes) {
|
|
|
+ ASSIMP_LOG_WARN("Animator node does not contain attributes. ");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (pugi::xml_node attrib : attributes.children()) {
|
|
|
+ // XML may contain useless noes like CDATA
|
|
|
+ if (!ASSIMP_stricmp(attrib.name(), "vector3d")) {
|
|
|
+ VectorProperty prop;
|
|
|
+ ReadVectorProperty(prop, attrib);
|
|
|
+
|
|
|
+ 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.emplace_back();
|
|
|
+ aiVectorKey &key = curAnim->splineKeys.back();
|
|
|
+
|
|
|
+ // and parse its properties
|
|
|
+ key.mValue = prop.value;
|
|
|
+ key.mTime = strtoul10(&prop.name[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's 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 (!ASSIMP_stricmp(reader->getNodeName(), "bool")) {
|
|
|
+ } else if (!ASSIMP_stricmp(attrib.name(), "bool")) {
|
|
|
+ BoolProperty prop;
|
|
|
+ ReadBoolProperty(prop, attrib);
|
|
|
+
|
|
|
+ if (curAnim->type == Animator::FLY_CIRCLE && prop.name == "Loop") {
|
|
|
+ curAnim->loop = prop.value;
|
|
|
+ }
|
|
|
+ //} else if (!ASSIMP_stricmp(reader->getNodeName(), "float")) {
|
|
|
+ } else if (!ASSIMP_stricmp(attrib.name(), "float")) {
|
|
|
+ FloatProperty prop;
|
|
|
+ ReadFloatProperty(prop, attrib);
|
|
|
+
|
|
|
+ // 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 (!ASSIMP_stricmp(reader->getNodeName(), "int")) {
|
|
|
+ } else if (!ASSIMP_stricmp(attrib.name(), "int")) {
|
|
|
+ IntProperty prop;
|
|
|
+ ReadIntProperty(prop, attrib);
|
|
|
+
|
|
|
+ if (curAnim->type == Animator::FLY_STRAIGHT && prop.name == "TimeForWay") {
|
|
|
+ curAnim->timeForWay = prop.value;
|
|
|
+ }
|
|
|
+ //} else if (!ASSIMP_stricmp(reader->getNodeName(), "string") || !ASSIMP_stricmp(reader->getNodeName(), "enum")) {
|
|
|
+ } else if (!ASSIMP_stricmp(attrib.name(), "string") || !ASSIMP_stricmp(attrib.name(), "enum")) {
|
|
|
+ StringProperty prop;
|
|
|
+ ReadStringProperty(prop, attrib);
|
|
|
+
|
|
|
+ if (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 {
|
|
|
+ ASSIMP_LOG_WARN("IRR: Ignoring unknown animator: ", prop.value);
|
|
|
+
|
|
|
+ curAnim->type = Animator::UNKNOWN;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+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());
|
|
|
+
|
|
|
+ /* 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);
|
|
|
+ }
|
|
|
+
|
|
|
+ // TODO: consolidate all into one loop
|
|
|
+ for (pugi::xml_node subNode : node.children()) {
|
|
|
+ // 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
|
|
|
+ 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() )) {
|
|
|
+ XmlParser st;
|
|
|
+ if (!st.parse(file.get())) {
|
|
|
throw DeadlyImportError("XML parse error while loading IRR file ", pFile);
|
|
|
}
|
|
|
- pugi::xml_node rootElement = st.getRootNode();
|
|
|
-
|
|
|
- // The root node of the scene
|
|
|
- Node *root = new Node(Node::DUMMY);
|
|
|
- root->parent = nullptr;
|
|
|
- root->name = "<IRRSceneRoot>";
|
|
|
-
|
|
|
- // Current node parent
|
|
|
- Node *curParent = root;
|
|
|
-
|
|
|
- // 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).
|
|
|
- */
|
|
|
- // ***********************************************************************
|
|
|
- //const char *sz = reader->getAttributeValueSafe("type");
|
|
|
- pugi::xml_attribute attrib = child.attribute("type");
|
|
|
- Node *nd;
|
|
|
- if (!ASSIMP_stricmp(attrib.name(), "mesh") || !ASSIMP_stricmp(attrib.name(), "octTree")) {
|
|
|
- // OctTree's and meshes are treated equally
|
|
|
- nd = new Node(Node::MESH);
|
|
|
- } else if (!ASSIMP_stricmp(attrib.name(), "cube")) {
|
|
|
- nd = new Node(Node::CUBE);
|
|
|
- ++guessedMeshCnt;
|
|
|
- } else if (!ASSIMP_stricmp(attrib.name(), "skybox")) {
|
|
|
- nd = new Node(Node::SKYBOX);
|
|
|
- guessedMeshCnt += 6;
|
|
|
- } else if (!ASSIMP_stricmp(attrib.name(), "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(attrib.name(), "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(attrib.name(), "sphere")) {
|
|
|
- nd = new Node(Node::SPHERE);
|
|
|
- ++guessedMeshCnt;
|
|
|
- } else if (!ASSIMP_stricmp(attrib.name(), "animatedMesh")) {
|
|
|
- nd = new Node(Node::ANIMMESH);
|
|
|
- } else if (!ASSIMP_stricmp(attrib.name(), "empty")) {
|
|
|
- nd = new Node(Node::DUMMY);
|
|
|
- } else if (!ASSIMP_stricmp(attrib.name(), "terrain")) {
|
|
|
- nd = new Node(Node::TERRAIN);
|
|
|
- } else if (!ASSIMP_stricmp(attrib.name(), "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: ", attrib.name());
|
|
|
-
|
|
|
- /* 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;
|
|
|
- }
|
|
|
-
|
|
|
- Animator *curAnim = nullptr;
|
|
|
-
|
|
|
- // Materials can occur for nearly any type of node
|
|
|
- if (inMaterials && curNode->type != Node::DUMMY) {
|
|
|
- // This is a material description - parse it!
|
|
|
- curNode->materials.emplace_back();
|
|
|
- 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.emplace_back();
|
|
|
- curAnim = &curNode->animators.back();
|
|
|
-
|
|
|
- ++guessedAnimCnt;
|
|
|
- }
|
|
|
-
|
|
|
- /* Parse all elements in the attributes block
|
|
|
- * and process them.
|
|
|
- */
|
|
|
- // 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")) {
|
|
|
- VectorProperty prop;
|
|
|
- ReadVectorProperty(prop);
|
|
|
-
|
|
|
- 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.emplace_back();
|
|
|
- aiVectorKey &key = curAnim->splineKeys.back();
|
|
|
-
|
|
|
- // and parse its properties
|
|
|
- key.mValue = prop.value;
|
|
|
- key.mTime = strtoul10(&prop.name[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's 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")) {
|
|
|
- } else if (!ASSIMP_stricmp(attrib.name(), "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")) {
|
|
|
- } else if (!ASSIMP_stricmp(attrib.name(), "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")) {
|
|
|
- } else if (!ASSIMP_stricmp(attrib.name(), "int")) {
|
|
|
- IntProperty prop;
|
|
|
- ReadIntProperty(prop);
|
|
|
-
|
|
|
- if (inAnimator) {
|
|
|
- if (curAnim->type == Animator::FLY_STRAIGHT && prop.name == "TimeForWay") {
|
|
|
- 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(attrib.name(), "string") || !ASSIMP_stricmp(attrib.name(), "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) {
|
|
|
- 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
|
|
|
- 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 {
|
|
|
- ASSIMP_LOG_WARN("IRR: Ignoring unknown animator: ", prop.value);
|
|
|
-
|
|
|
- curAnim->type = Animator::UNKNOWN;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- //} 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:
|
|
|
-
|
|
|
- // 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
|
|
|
- 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:
|
|
|
- // GCC complains that not all enumeration values are handled
|
|
|
- break;
|
|
|
- }
|
|
|
- //}
|
|
|
-
|
|
|
- // Now iterate through all cameras and compute their final (horizontal) FOV
|
|
|
- for (aiCamera *cam : cameras) {
|
|
|
- // screen aspect could be missing
|
|
|
- if (cam->mAspect) {
|
|
|
- cam->mHorizontalFOV *= cam->mAspect;
|
|
|
- } else {
|
|
|
- ASSIMP_LOG_WARN("IRR: Camera aspect is not given, can't compute horizontal FOV");
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- batch.LoadAll();
|
|
|
-
|
|
|
- // Allocate a temporary scene data structure
|
|
|
- aiScene *tempScene = new aiScene();
|
|
|
- tempScene->mRootNode = new aiNode();
|
|
|
- tempScene->mRootNode->mName.Set("<IRRRoot>");
|
|
|
-
|
|
|
- // Copy the cameras to the output array
|
|
|
- if (!cameras.empty()) {
|
|
|
- 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
|
|
|
- if (!lights.empty()) {
|
|
|
- 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 scene-graph recursively: generate final
|
|
|
- // meshes and generate animation channels for all nodes.
|
|
|
- unsigned int defMatIdx = UINT_MAX;
|
|
|
- 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()) {
|
|
|
- // 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 * sizeof(void *));
|
|
|
- }
|
|
|
-
|
|
|
- // Copy all materials to the output array
|
|
|
- if (!materials.empty()) {
|
|
|
- tempScene->mNumMaterials = (unsigned int)materials.size();
|
|
|
- tempScene->mMaterials = new aiMaterial *[tempScene->mNumMaterials];
|
|
|
- ::memcpy(tempScene->mMaterials, &materials[0], sizeof(void *) * tempScene->mNumMaterials);
|
|
|
- }
|
|
|
-
|
|
|
- // Now merge all sub scenes and attach them to the correct
|
|
|
- // attachment points in the scenegraph.
|
|
|
- SceneCombiner::MergeScenes(&pScene, tempScene, attach,
|
|
|
- AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES | (!configSpeedFlag ? (
|
|
|
- AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY | AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES) :
|
|
|
- 0));
|
|
|
-
|
|
|
- // If we have no meshes | no materials now set the INCOMPLETE
|
|
|
- // scene flag. This is necessary if we failed to load all
|
|
|
- // models from external files
|
|
|
- if (!pScene->mNumMeshes || !pScene->mNumMaterials) {
|
|
|
- ASSIMP_LOG_WARN("IRR: No meshes loaded, setting AI_SCENE_FLAGS_INCOMPLETE");
|
|
|
- pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
|
|
|
- }
|
|
|
-
|
|
|
- // Finished ... everything destructs automatically and all
|
|
|
- // temporary scenes have already been deleted by MergeScenes()
|
|
|
- delete root;
|
|
|
+ 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
|
|
|
+ for (aiCamera *cam : cameras) {
|
|
|
+ // screen aspect could be missing
|
|
|
+ if (cam->mAspect) {
|
|
|
+ cam->mHorizontalFOV *= cam->mAspect;
|
|
|
+ } else {
|
|
|
+ ASSIMP_LOG_WARN("IRR: Camera aspect is not given, can't compute horizontal FOV");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ batch.LoadAll();
|
|
|
+
|
|
|
+ // Allocate a temporary scene data structure
|
|
|
+ aiScene *tempScene = new aiScene();
|
|
|
+ tempScene->mRootNode = new aiNode();
|
|
|
+ tempScene->mRootNode->mName.Set("<IRRRoot>");
|
|
|
+
|
|
|
+ // Copy the cameras to the output array
|
|
|
+ if (!cameras.empty()) {
|
|
|
+ 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
|
|
|
+ if (!lights.empty()) {
|
|
|
+ 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 scene-graph recursively: generate final
|
|
|
+ // meshes and generate animation channels for all nodes.
|
|
|
+ unsigned int defMatIdx = UINT_MAX;
|
|
|
+ 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()) {
|
|
|
+ // 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 * sizeof(void *));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Copy all materials to the output array
|
|
|
+ if (!materials.empty()) {
|
|
|
+ tempScene->mNumMaterials = (unsigned int)materials.size();
|
|
|
+ tempScene->mMaterials = new aiMaterial *[tempScene->mNumMaterials];
|
|
|
+ ::memcpy(tempScene->mMaterials, &materials[0], sizeof(void *) * tempScene->mNumMaterials);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Now merge all sub scenes and attach them to the correct
|
|
|
+ // attachment points in the scenegraph.
|
|
|
+ SceneCombiner::MergeScenes(&pScene, tempScene, attach,
|
|
|
+ AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES | (!configSpeedFlag ? (AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY | AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES) :
|
|
|
+ 0));
|
|
|
+
|
|
|
+ // If we have no meshes | no materials now set the INCOMPLETE
|
|
|
+ // scene flag. This is necessary if we failed to load all
|
|
|
+ // models from external files
|
|
|
+ if (!pScene->mNumMeshes || !pScene->mNumMaterials) {
|
|
|
+ ASSIMP_LOG_WARN("IRR: No meshes loaded, setting AI_SCENE_FLAGS_INCOMPLETE");
|
|
|
+ pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Finished ... everything destructs automatically and all
|
|
|
+ // temporary scenes have already been deleted by MergeScenes()
|
|
|
+ delete root;
|
|
|
}
|
|
|
|
|
|
#endif // !! ASSIMP_BUILD_NO_IRR_IMPORTER
|