570 lines
21 KiB
C++
570 lines
21 KiB
C++
/*
|
|
---------------------------------------------------------------------------
|
|
Open Asset Import Library (assimp)
|
|
---------------------------------------------------------------------------
|
|
|
|
Copyright (c) 2006-2021, assimp team
|
|
|
|
All rights reserved.
|
|
|
|
Redistribution and use of this software in source and binary forms,
|
|
with or without modification, are permitted provided that the following
|
|
conditions are met:
|
|
|
|
* Redistributions of source code must retain the above
|
|
copyright notice, this list of conditions and the
|
|
following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above
|
|
copyright notice, this list of conditions and the
|
|
following disclaimer in the documentation and/or other
|
|
materials provided with the distribution.
|
|
|
|
* Neither the name of the assimp team, nor the names of its
|
|
contributors may be used to endorse or promote products
|
|
derived from this software without specific prior
|
|
written permission of the assimp team.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
---------------------------------------------------------------------------
|
|
*/
|
|
|
|
/** @file Q3DLoader.cpp
|
|
* @brief Implementation of the Q3D importer class
|
|
*/
|
|
|
|
#ifndef ASSIMP_BUILD_NO_Q3D_IMPORTER
|
|
|
|
// internal headers
|
|
#include "Q3DLoader.h"
|
|
#include <assimp/StreamReader.h>
|
|
#include <assimp/fast_atof.h>
|
|
#include <assimp/importerdesc.h>
|
|
#include <assimp/scene.h>
|
|
#include <assimp/DefaultLogger.hpp>
|
|
#include <assimp/IOSystem.hpp>
|
|
|
|
using namespace Assimp;
|
|
|
|
static const aiImporterDesc desc = {
|
|
"Quick3D Importer",
|
|
"",
|
|
"",
|
|
"http://www.quick3d.com/",
|
|
aiImporterFlags_SupportBinaryFlavour,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
"q3o q3s"
|
|
};
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Constructor to be privately used by Importer
|
|
Q3DImporter::Q3DImporter() {
|
|
// empty
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Destructor, private as well
|
|
Q3DImporter::~Q3DImporter() {
|
|
// empty
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Returns whether the class can handle the format of the given file.
|
|
bool Q3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
|
|
static const char *tokens[] = { "quick3Do", "quick3Ds" };
|
|
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
const aiImporterDesc *Q3DImporter::GetInfo() const {
|
|
return &desc;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Imports the given file into the given scene structure.
|
|
void Q3DImporter::InternReadFile(const std::string &pFile,
|
|
aiScene *pScene, IOSystem *pIOHandler) {
|
|
StreamReaderLE stream(pIOHandler->Open(pFile, "rb"));
|
|
|
|
// The header is 22 bytes large
|
|
if (stream.GetRemainingSize() < 22)
|
|
throw DeadlyImportError("File is either empty or corrupt: ", pFile);
|
|
|
|
// Check the file's signature
|
|
if (ASSIMP_strincmp((const char *)stream.GetPtr(), "quick3Do", 8) &&
|
|
ASSIMP_strincmp((const char *)stream.GetPtr(), "quick3Ds", 8)) {
|
|
throw DeadlyImportError("Not a Quick3D file. Signature string is: ", std::string((const char *)stream.GetPtr(), 8));
|
|
}
|
|
|
|
// Print the file format version
|
|
ASSIMP_LOG_INFO_F("Quick3D File format version: ",
|
|
std::string(&((const char *)stream.GetPtr())[8], 2));
|
|
|
|
// ... an store it
|
|
char major = ((const char *)stream.GetPtr())[8];
|
|
char minor = ((const char *)stream.GetPtr())[9];
|
|
|
|
stream.IncPtr(10);
|
|
unsigned int numMeshes = (unsigned int)stream.GetI4();
|
|
unsigned int numMats = (unsigned int)stream.GetI4();
|
|
unsigned int numTextures = (unsigned int)stream.GetI4();
|
|
|
|
std::vector<Material> materials;
|
|
materials.reserve(numMats);
|
|
|
|
std::vector<Mesh> meshes;
|
|
meshes.reserve(numMeshes);
|
|
|
|
// Allocate the scene root node
|
|
pScene->mRootNode = new aiNode();
|
|
|
|
aiColor3D fgColor(0.6f, 0.6f, 0.6f);
|
|
|
|
// Now read all file chunks
|
|
while (true) {
|
|
if (stream.GetRemainingSize() < 1) break;
|
|
char c = stream.GetI1();
|
|
switch (c) {
|
|
// Meshes chunk
|
|
case 'm': {
|
|
for (unsigned int quak = 0; quak < numMeshes; ++quak) {
|
|
meshes.push_back(Mesh());
|
|
Mesh &mesh = meshes.back();
|
|
|
|
// read all vertices
|
|
unsigned int numVerts = (unsigned int)stream.GetI4();
|
|
if (!numVerts)
|
|
throw DeadlyImportError("Quick3D: Found mesh with zero vertices");
|
|
|
|
std::vector<aiVector3D> &verts = mesh.verts;
|
|
verts.resize(numVerts);
|
|
|
|
for (unsigned int i = 0; i < numVerts; ++i) {
|
|
verts[i].x = stream.GetF4();
|
|
verts[i].y = stream.GetF4();
|
|
verts[i].z = stream.GetF4();
|
|
}
|
|
|
|
// read all faces
|
|
numVerts = (unsigned int)stream.GetI4();
|
|
if (!numVerts)
|
|
throw DeadlyImportError("Quick3D: Found mesh with zero faces");
|
|
|
|
std::vector<Face> &faces = mesh.faces;
|
|
faces.reserve(numVerts);
|
|
|
|
// number of indices
|
|
for (unsigned int i = 0; i < numVerts; ++i) {
|
|
faces.push_back(Face(stream.GetI2()));
|
|
if (faces.back().indices.empty())
|
|
throw DeadlyImportError("Quick3D: Found face with zero indices");
|
|
}
|
|
|
|
// indices
|
|
for (unsigned int i = 0; i < numVerts; ++i) {
|
|
Face &vec = faces[i];
|
|
for (unsigned int a = 0; a < (unsigned int)vec.indices.size(); ++a)
|
|
vec.indices[a] = stream.GetI4();
|
|
}
|
|
|
|
// material indices
|
|
for (unsigned int i = 0; i < numVerts; ++i) {
|
|
faces[i].mat = (unsigned int)stream.GetI4();
|
|
}
|
|
|
|
// read all normals
|
|
numVerts = (unsigned int)stream.GetI4();
|
|
std::vector<aiVector3D> &normals = mesh.normals;
|
|
normals.resize(numVerts);
|
|
|
|
for (unsigned int i = 0; i < numVerts; ++i) {
|
|
normals[i].x = stream.GetF4();
|
|
normals[i].y = stream.GetF4();
|
|
normals[i].z = stream.GetF4();
|
|
}
|
|
|
|
numVerts = (unsigned int)stream.GetI4();
|
|
if (numTextures && numVerts) {
|
|
// read all texture coordinates
|
|
std::vector<aiVector3D> &uv = mesh.uv;
|
|
uv.resize(numVerts);
|
|
|
|
for (unsigned int i = 0; i < numVerts; ++i) {
|
|
uv[i].x = stream.GetF4();
|
|
uv[i].y = stream.GetF4();
|
|
}
|
|
|
|
// UV indices
|
|
for (unsigned int i = 0; i < (unsigned int)faces.size(); ++i) {
|
|
Face &vec = faces[i];
|
|
for (unsigned int a = 0; a < (unsigned int)vec.indices.size(); ++a) {
|
|
vec.uvindices[a] = stream.GetI4();
|
|
if (!i && !a)
|
|
mesh.prevUVIdx = vec.uvindices[a];
|
|
else if (vec.uvindices[a] != mesh.prevUVIdx)
|
|
mesh.prevUVIdx = UINT_MAX;
|
|
}
|
|
}
|
|
}
|
|
|
|
// we don't need the rest, but we need to get to the next chunk
|
|
stream.IncPtr(36);
|
|
if (minor > '0' && major == '3')
|
|
stream.IncPtr(mesh.faces.size());
|
|
}
|
|
// stream.IncPtr(4); // unknown value here
|
|
} break;
|
|
|
|
// materials chunk
|
|
case 'c':
|
|
|
|
for (unsigned int i = 0; i < numMats; ++i) {
|
|
materials.push_back(Material());
|
|
Material &mat = materials.back();
|
|
|
|
// read the material name
|
|
c = stream.GetI1();
|
|
while (c) {
|
|
mat.name.data[mat.name.length++] = c;
|
|
c = stream.GetI1();
|
|
}
|
|
|
|
// add the terminal character
|
|
mat.name.data[mat.name.length] = '\0';
|
|
|
|
// read the ambient color
|
|
mat.ambient.r = stream.GetF4();
|
|
mat.ambient.g = stream.GetF4();
|
|
mat.ambient.b = stream.GetF4();
|
|
|
|
// read the diffuse color
|
|
mat.diffuse.r = stream.GetF4();
|
|
mat.diffuse.g = stream.GetF4();
|
|
mat.diffuse.b = stream.GetF4();
|
|
|
|
// read the ambient color
|
|
mat.specular.r = stream.GetF4();
|
|
mat.specular.g = stream.GetF4();
|
|
mat.specular.b = stream.GetF4();
|
|
|
|
// read the transparency
|
|
mat.transparency = stream.GetF4();
|
|
|
|
// unknown value here
|
|
// stream.IncPtr(4);
|
|
// FIX: it could be the texture index ...
|
|
mat.texIdx = (unsigned int)stream.GetI4();
|
|
}
|
|
|
|
break;
|
|
|
|
// texture chunk
|
|
case 't':
|
|
|
|
pScene->mNumTextures = numTextures;
|
|
if (!numTextures) {
|
|
break;
|
|
}
|
|
pScene->mTextures = new aiTexture *[pScene->mNumTextures];
|
|
// to make sure we won't crash if we leave through an exception
|
|
::memset(pScene->mTextures, 0, sizeof(void *) * pScene->mNumTextures);
|
|
for (unsigned int i = 0; i < pScene->mNumTextures; ++i) {
|
|
aiTexture *tex = pScene->mTextures[i] = new aiTexture;
|
|
|
|
// skip the texture name
|
|
while (stream.GetI1())
|
|
;
|
|
|
|
// read texture width and height
|
|
tex->mWidth = (unsigned int)stream.GetI4();
|
|
tex->mHeight = (unsigned int)stream.GetI4();
|
|
|
|
if (!tex->mWidth || !tex->mHeight) {
|
|
throw DeadlyImportError("Quick3D: Invalid texture. Width or height is zero");
|
|
}
|
|
|
|
unsigned int mul = tex->mWidth * tex->mHeight;
|
|
aiTexel *begin = tex->pcData = new aiTexel[mul];
|
|
aiTexel *const end = &begin[mul - 1] + 1;
|
|
|
|
for (; begin != end; ++begin) {
|
|
begin->r = stream.GetI1();
|
|
begin->g = stream.GetI1();
|
|
begin->b = stream.GetI1();
|
|
begin->a = 0xff;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
// scene chunk
|
|
case 's': {
|
|
// skip position and rotation
|
|
stream.IncPtr(12);
|
|
|
|
for (unsigned int i = 0; i < 4; ++i)
|
|
for (unsigned int a = 0; a < 4; ++a)
|
|
pScene->mRootNode->mTransformation[i][a] = stream.GetF4();
|
|
|
|
stream.IncPtr(16);
|
|
|
|
// now setup a single camera
|
|
pScene->mNumCameras = 1;
|
|
pScene->mCameras = new aiCamera *[1];
|
|
aiCamera *cam = pScene->mCameras[0] = new aiCamera();
|
|
cam->mPosition.x = stream.GetF4();
|
|
cam->mPosition.y = stream.GetF4();
|
|
cam->mPosition.z = stream.GetF4();
|
|
cam->mName.Set("Q3DCamera");
|
|
|
|
// skip eye rotation for the moment
|
|
stream.IncPtr(12);
|
|
|
|
// read the default material color
|
|
fgColor.r = stream.GetF4();
|
|
fgColor.g = stream.GetF4();
|
|
fgColor.b = stream.GetF4();
|
|
|
|
// skip some unimportant properties
|
|
stream.IncPtr(29);
|
|
|
|
// setup a single point light with no attenuation
|
|
pScene->mNumLights = 1;
|
|
pScene->mLights = new aiLight *[1];
|
|
aiLight *light = pScene->mLights[0] = new aiLight();
|
|
light->mName.Set("Q3DLight");
|
|
light->mType = aiLightSource_POINT;
|
|
|
|
light->mAttenuationConstant = 1;
|
|
light->mAttenuationLinear = 0;
|
|
light->mAttenuationQuadratic = 0;
|
|
|
|
light->mColorDiffuse.r = stream.GetF4();
|
|
light->mColorDiffuse.g = stream.GetF4();
|
|
light->mColorDiffuse.b = stream.GetF4();
|
|
|
|
light->mColorSpecular = light->mColorDiffuse;
|
|
|
|
// We don't need the rest, but we need to know where this chunk ends.
|
|
unsigned int temp = (unsigned int)(stream.GetI4() * stream.GetI4());
|
|
|
|
// skip the background file name
|
|
while (stream.GetI1())
|
|
;
|
|
|
|
// skip background texture data + the remaining fields
|
|
stream.IncPtr(temp * 3 + 20); // 4 bytes of unknown data here
|
|
|
|
// TODO
|
|
goto outer;
|
|
} break;
|
|
|
|
default:
|
|
throw DeadlyImportError("Quick3D: Unknown chunk");
|
|
break;
|
|
};
|
|
}
|
|
outer:
|
|
|
|
// If we have no mesh loaded - break here
|
|
if (meshes.empty())
|
|
throw DeadlyImportError("Quick3D: No meshes loaded");
|
|
|
|
// If we have no materials loaded - generate a default mat
|
|
if (materials.empty()) {
|
|
ASSIMP_LOG_INFO("Quick3D: No material found, generating one");
|
|
materials.push_back(Material());
|
|
materials.back().diffuse = fgColor;
|
|
}
|
|
|
|
// find out which materials we'll need
|
|
typedef std::pair<unsigned int, unsigned int> FaceIdx;
|
|
typedef std::vector<FaceIdx> FaceIdxArray;
|
|
FaceIdxArray *fidx = new FaceIdxArray[materials.size()];
|
|
|
|
unsigned int p = 0;
|
|
for (std::vector<Mesh>::iterator it = meshes.begin(), end = meshes.end();
|
|
it != end; ++it, ++p) {
|
|
unsigned int q = 0;
|
|
for (std::vector<Face>::iterator fit = (*it).faces.begin(), fend = (*it).faces.end();
|
|
fit != fend; ++fit, ++q) {
|
|
if ((*fit).mat >= materials.size()) {
|
|
ASSIMP_LOG_WARN("Quick3D: Material index overflow");
|
|
(*fit).mat = 0;
|
|
}
|
|
if (fidx[(*fit).mat].empty()) ++pScene->mNumMeshes;
|
|
fidx[(*fit).mat].push_back(FaceIdx(p, q));
|
|
}
|
|
}
|
|
pScene->mNumMaterials = pScene->mNumMeshes;
|
|
pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials];
|
|
pScene->mMeshes = new aiMesh *[pScene->mNumMaterials];
|
|
|
|
for (unsigned int i = 0, real = 0; i < (unsigned int)materials.size(); ++i) {
|
|
if (fidx[i].empty()) continue;
|
|
|
|
// Allocate a mesh and a material
|
|
aiMesh *mesh = pScene->mMeshes[real] = new aiMesh();
|
|
aiMaterial *mat = new aiMaterial();
|
|
pScene->mMaterials[real] = mat;
|
|
|
|
mesh->mMaterialIndex = real;
|
|
|
|
// Build the output material
|
|
Material &srcMat = materials[i];
|
|
mat->AddProperty(&srcMat.diffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
|
|
mat->AddProperty(&srcMat.specular, 1, AI_MATKEY_COLOR_SPECULAR);
|
|
mat->AddProperty(&srcMat.ambient, 1, AI_MATKEY_COLOR_AMBIENT);
|
|
|
|
// NOTE: Ignore transparency for the moment - it seems
|
|
// unclear how to interpret the data
|
|
#if 0
|
|
if (!(minor > '0' && major == '3'))
|
|
srcMat.transparency = 1.0f - srcMat.transparency;
|
|
mat->AddProperty(&srcMat.transparency, 1, AI_MATKEY_OPACITY);
|
|
#endif
|
|
|
|
// add shininess - Quick3D seems to use it ins its viewer
|
|
srcMat.transparency = 16.f;
|
|
mat->AddProperty(&srcMat.transparency, 1, AI_MATKEY_SHININESS);
|
|
|
|
int m = (int)aiShadingMode_Phong;
|
|
mat->AddProperty(&m, 1, AI_MATKEY_SHADING_MODEL);
|
|
|
|
if (srcMat.name.length)
|
|
mat->AddProperty(&srcMat.name, AI_MATKEY_NAME);
|
|
|
|
// Add a texture
|
|
if (srcMat.texIdx < pScene->mNumTextures || real < pScene->mNumTextures) {
|
|
srcMat.name.data[0] = '*';
|
|
srcMat.name.length = ASSIMP_itoa10(&srcMat.name.data[1], 1000,
|
|
(srcMat.texIdx < pScene->mNumTextures ? srcMat.texIdx : real));
|
|
mat->AddProperty(&srcMat.name, AI_MATKEY_TEXTURE_DIFFUSE(0));
|
|
}
|
|
|
|
mesh->mNumFaces = (unsigned int)fidx[i].size();
|
|
aiFace *faces = mesh->mFaces = new aiFace[mesh->mNumFaces];
|
|
|
|
// Now build the output mesh. First find out how many
|
|
// vertices we'll need
|
|
for (FaceIdxArray::const_iterator it = fidx[i].begin(), end = fidx[i].end();
|
|
it != end; ++it) {
|
|
mesh->mNumVertices += (unsigned int)meshes[(*it).first].faces[(*it).second].indices.size();
|
|
}
|
|
|
|
aiVector3D *verts = mesh->mVertices = new aiVector3D[mesh->mNumVertices];
|
|
aiVector3D *norms = mesh->mNormals = new aiVector3D[mesh->mNumVertices];
|
|
aiVector3D *uv = nullptr;
|
|
if (real < pScene->mNumTextures) {
|
|
uv = mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
|
|
mesh->mNumUVComponents[0] = 2;
|
|
}
|
|
|
|
// Build the final array
|
|
unsigned int cnt = 0;
|
|
for (FaceIdxArray::const_iterator it = fidx[i].begin(), end = fidx[i].end();
|
|
it != end; ++it, ++faces) {
|
|
Mesh &curMesh = meshes[(*it).first];
|
|
Face &face = curMesh.faces[(*it).second];
|
|
faces->mNumIndices = (unsigned int)face.indices.size();
|
|
faces->mIndices = new unsigned int[faces->mNumIndices];
|
|
|
|
aiVector3D faceNormal;
|
|
bool fnOK = false;
|
|
|
|
for (unsigned int n = 0; n < faces->mNumIndices; ++n, ++cnt, ++norms, ++verts) {
|
|
if (face.indices[n] >= curMesh.verts.size()) {
|
|
ASSIMP_LOG_WARN("Quick3D: Vertex index overflow");
|
|
face.indices[n] = 0;
|
|
}
|
|
|
|
// copy vertices
|
|
*verts = curMesh.verts[face.indices[n]];
|
|
|
|
if (face.indices[n] >= curMesh.normals.size() && faces->mNumIndices >= 3) {
|
|
// we have no normal here - assign the face normal
|
|
if (!fnOK) {
|
|
const aiVector3D &pV1 = curMesh.verts[face.indices[0]];
|
|
const aiVector3D &pV2 = curMesh.verts[face.indices[1]];
|
|
const aiVector3D &pV3 = curMesh.verts[face.indices.size() - 1];
|
|
faceNormal = (pV2 - pV1) ^ (pV3 - pV1).Normalize();
|
|
fnOK = true;
|
|
}
|
|
*norms = faceNormal;
|
|
} else {
|
|
*norms = curMesh.normals[face.indices[n]];
|
|
}
|
|
|
|
// copy texture coordinates
|
|
if (uv && curMesh.uv.size()) {
|
|
if (curMesh.prevUVIdx != 0xffffffff && curMesh.uv.size() >= curMesh.verts.size()) // workaround
|
|
{
|
|
*uv = curMesh.uv[face.indices[n]];
|
|
} else {
|
|
if (face.uvindices[n] >= curMesh.uv.size()) {
|
|
ASSIMP_LOG_WARN("Quick3D: Texture coordinate index overflow");
|
|
face.uvindices[n] = 0;
|
|
}
|
|
*uv = curMesh.uv[face.uvindices[n]];
|
|
}
|
|
uv->y = 1.f - uv->y;
|
|
++uv;
|
|
}
|
|
|
|
// setup the new vertex index
|
|
faces->mIndices[n] = cnt;
|
|
}
|
|
}
|
|
++real;
|
|
}
|
|
|
|
// Delete our nice helper array
|
|
delete[] fidx;
|
|
|
|
// Now we need to attach the meshes to the root node of the scene
|
|
pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
|
|
pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes];
|
|
for (unsigned int i = 0; i < pScene->mNumMeshes; ++i)
|
|
pScene->mRootNode->mMeshes[i] = i;
|
|
|
|
/*pScene->mRootNode->mTransformation *= aiMatrix4x4(
|
|
1.f, 0.f, 0.f, 0.f,
|
|
0.f, -1.f,0.f, 0.f,
|
|
0.f, 0.f, 1.f, 0.f,
|
|
0.f, 0.f, 0.f, 1.f);*/
|
|
|
|
// Add cameras and light sources to the scene root node
|
|
pScene->mRootNode->mNumChildren = pScene->mNumLights + pScene->mNumCameras;
|
|
if (pScene->mRootNode->mNumChildren) {
|
|
pScene->mRootNode->mChildren = new aiNode *[pScene->mRootNode->mNumChildren];
|
|
|
|
// the light source
|
|
aiNode *nd = pScene->mRootNode->mChildren[0] = new aiNode();
|
|
nd->mParent = pScene->mRootNode;
|
|
nd->mName.Set("Q3DLight");
|
|
nd->mTransformation = pScene->mRootNode->mTransformation;
|
|
nd->mTransformation.Inverse();
|
|
|
|
// camera
|
|
nd = pScene->mRootNode->mChildren[1] = new aiNode();
|
|
nd->mParent = pScene->mRootNode;
|
|
nd->mName.Set("Q3DCamera");
|
|
nd->mTransformation = pScene->mRootNode->mChildren[0]->mTransformation;
|
|
}
|
|
}
|
|
|
|
#endif // !! ASSIMP_BUILD_NO_Q3D_IMPORTER
|