123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816 |
- /*
- ---------------------------------------------------------------------------
- Open Asset Import Library (assimp)
- ---------------------------------------------------------------------------
- Copyright (c) 2006-2025, 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 Implementation of the 3ds importer class */
- #ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
- // internal headers
- #include "3DSLoader.h"
- #include "Common/TargetAnimation.h"
- #include <assimp/StringComparison.h>
- #include <assimp/scene.h>
- #include <assimp/DefaultLogger.hpp>
- #include <cctype>
- #include <memory>
- namespace Assimp {
- static constexpr unsigned int NotSet = 0xcdcdcdcd;
- // ------------------------------------------------------------------------------------------------
- // Setup final material indices, generae a default material if necessary
- void Discreet3DSImporter::ReplaceDefaultMaterial() {
- // Try to find an existing material that matches the
- // typical default material setting:
- // - no textures
- // - diffuse color (in grey!)
- // NOTE: This is here to workaround the fact that some
- // exporters are writing a default material, too.
- unsigned int idx(NotSet);
- for (unsigned int i = 0; i < mScene->mMaterials.size(); ++i) {
- std::string s = mScene->mMaterials[i].mName;
- for (char &it : s) {
- it = static_cast<char>(::tolower(static_cast<unsigned char>(it)));
- }
- if (std::string::npos == s.find("default")) continue;
- if (mScene->mMaterials[i].mDiffuse.r !=
- mScene->mMaterials[i].mDiffuse.g ||
- mScene->mMaterials[i].mDiffuse.r !=
- mScene->mMaterials[i].mDiffuse.b) continue;
- if (ContainsTextures(i)) {
- continue;
- }
- idx = i;
- }
- if (NotSet == idx) {
- idx = (unsigned int)mScene->mMaterials.size();
- }
- // now iterate through all meshes and through all faces and
- // find all faces that are using the default material
- unsigned int cnt = 0;
- for (std::vector<D3DS::Mesh>::iterator
- i = mScene->mMeshes.begin();
- i != mScene->mMeshes.end(); ++i) {
- for (std::vector<unsigned int>::iterator
- a = (*i).mFaceMaterials.begin();
- a != (*i).mFaceMaterials.end(); ++a) {
- // NOTE: The additional check seems to be necessary,
- // some exporters seem to generate invalid data here
- if (0xcdcdcdcd == (*a)) {
- (*a) = idx;
- ++cnt;
- } else if ((*a) >= mScene->mMaterials.size()) {
- (*a) = idx;
- ASSIMP_LOG_WARN("Material index overflow in 3DS file. Using default material");
- ++cnt;
- }
- }
- }
- if (cnt && idx == mScene->mMaterials.size()) {
- // We need to create our own default material
- D3DS::Material sMat("%%%DEFAULT");
- sMat.mDiffuse = aiColor3D(0.3f, 0.3f, 0.3f);
- mScene->mMaterials.push_back(sMat);
- ASSIMP_LOG_INFO("3DS: Generating default material");
- }
- }
- // ------------------------------------------------------------------------------------------------
- // Check whether all indices are valid. Otherwise we'd crash before the validation step is reached
- void Discreet3DSImporter::CheckIndices(D3DS::Mesh &sMesh) {
- for (std::vector<D3DS::Face>::iterator i = sMesh.mFaces.begin(); i != sMesh.mFaces.end(); ++i) {
- // check whether all indices are in range
- for (unsigned int a = 0; a < 3; ++a) {
- if ((*i).mIndices[a] >= sMesh.mPositions.size()) {
- ASSIMP_LOG_WARN("3DS: Vertex index overflow)");
- (*i).mIndices[a] = (uint32_t)sMesh.mPositions.size() - 1;
- }
- if (!sMesh.mTexCoords.empty() && (*i).mIndices[a] >= sMesh.mTexCoords.size()) {
- ASSIMP_LOG_WARN("3DS: Texture coordinate index overflow)");
- (*i).mIndices[a] = (uint32_t)sMesh.mTexCoords.size() - 1;
- }
- }
- }
- }
- // ------------------------------------------------------------------------------------------------
- // Generate out unique verbose format representation
- void Discreet3DSImporter::MakeUnique(D3DS::Mesh &sMesh) {
- // TODO: really necessary? I don't think. Just a waste of memory and time
- // to do it now in a separate buffer.
- // Allocate output storage
- std::vector<aiVector3D> vNew(sMesh.mFaces.size() * 3);
- std::vector<aiVector3D> vNew2;
- if (sMesh.mTexCoords.size())
- vNew2.resize(sMesh.mFaces.size() * 3);
- for (unsigned int i = 0, base = 0; i < sMesh.mFaces.size(); ++i) {
- D3DS::Face &face = sMesh.mFaces[i];
- // Positions
- for (unsigned int a = 0; a < 3; ++a, ++base) {
- vNew[base] = sMesh.mPositions[face.mIndices[a]];
- if (sMesh.mTexCoords.size())
- vNew2[base] = sMesh.mTexCoords[face.mIndices[a]];
- face.mIndices[a] = base;
- }
- }
- sMesh.mPositions = vNew;
- sMesh.mTexCoords = vNew2;
- }
- // ------------------------------------------------------------------------------------------------
- // Convert a 3DS texture to texture keys in an aiMaterial
- void CopyTexture(aiMaterial &mat, D3DS::Texture &texture, aiTextureType type) {
- // Setup the texture name
- aiString tex;
- tex.Set(texture.mMapName);
- mat.AddProperty(&tex, AI_MATKEY_TEXTURE(type, 0));
- // Setup the texture blend factor
- if (is_not_qnan(texture.mTextureBlend))
- mat.AddProperty<ai_real>(&texture.mTextureBlend, 1, AI_MATKEY_TEXBLEND(type, 0));
- // Setup the texture mapping mode
- int mapMode = static_cast<int>(texture.mMapMode);
- mat.AddProperty<int>(&mapMode, 1, AI_MATKEY_MAPPINGMODE_U(type, 0));
- mat.AddProperty<int>(&mapMode, 1, AI_MATKEY_MAPPINGMODE_V(type, 0));
- // Mirroring - double the scaling values
- // FIXME: this is not really correct ...
- if (texture.mMapMode == aiTextureMapMode_Mirror) {
- texture.mScaleU *= 2.0;
- texture.mScaleV *= 2.0;
- texture.mOffsetU /= 2.0;
- texture.mOffsetV /= 2.0;
- }
- // Setup texture UV transformations
- mat.AddProperty<ai_real>(&texture.mOffsetU, 5, AI_MATKEY_UVTRANSFORM(type, 0));
- }
- // ------------------------------------------------------------------------------------------------
- // Convert a 3DS material to an aiMaterial
- void Discreet3DSImporter::ConvertMaterial(D3DS::Material &oldMat,
- aiMaterial &mat) {
- // NOTE: Pass the background image to the viewer by bypassing the
- // material system. This is an evil hack, never do it again!
- if (0 != mBackgroundImage.length() && bHasBG) {
- aiString tex;
- tex.Set(mBackgroundImage);
- mat.AddProperty(&tex, AI_MATKEY_GLOBAL_BACKGROUND_IMAGE);
- // Be sure this is only done for the first material
- mBackgroundImage = std::string();
- }
- // At first add the base ambient color of the scene to the material
- oldMat.mAmbient.r += mClrAmbient.r;
- oldMat.mAmbient.g += mClrAmbient.g;
- oldMat.mAmbient.b += mClrAmbient.b;
- aiString name;
- name.Set(oldMat.mName);
- mat.AddProperty(&name, AI_MATKEY_NAME);
- // Material colors
- mat.AddProperty(&oldMat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT);
- mat.AddProperty(&oldMat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
- mat.AddProperty(&oldMat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR);
- mat.AddProperty(&oldMat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE);
- // Phong shininess and shininess strength
- if (D3DS::Discreet3DS::Phong == oldMat.mShading ||
- D3DS::Discreet3DS::Metal == oldMat.mShading) {
- if (!oldMat.mSpecularExponent || !oldMat.mShininessStrength) {
- oldMat.mShading = D3DS::Discreet3DS::Gouraud;
- } else {
- mat.AddProperty(&oldMat.mSpecularExponent, 1, AI_MATKEY_SHININESS);
- mat.AddProperty(&oldMat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH);
- }
- }
- // Opacity
- mat.AddProperty<ai_real>(&oldMat.mTransparency, 1, AI_MATKEY_OPACITY);
- // Bump height scaling
- mat.AddProperty<ai_real>(&oldMat.mBumpHeight, 1, AI_MATKEY_BUMPSCALING);
- // Two sided rendering?
- if (oldMat.mTwoSided) {
- int i = 1;
- mat.AddProperty<int>(&i, 1, AI_MATKEY_TWOSIDED);
- }
- // Shading mode
- aiShadingMode eShading = aiShadingMode_NoShading;
- switch (oldMat.mShading) {
- case D3DS::Discreet3DS::Flat:
- eShading = aiShadingMode_Flat;
- break;
- // I don't know what "Wire" shading should be,
- // assume it is simple lambertian diffuse shading
- case D3DS::Discreet3DS::Wire: {
- // Set the wireframe flag
- unsigned int iWire = 1;
- mat.AddProperty<int>((int *)&iWire, 1, AI_MATKEY_ENABLE_WIREFRAME);
- }
- [[fallthrough]];
- case D3DS::Discreet3DS::Gouraud:
- eShading = aiShadingMode_Gouraud;
- break;
- // assume cook-torrance shading for metals.
- case D3DS::Discreet3DS::Phong:
- eShading = aiShadingMode_Phong;
- break;
- case D3DS::Discreet3DS::Metal:
- eShading = aiShadingMode_CookTorrance;
- break;
- // FIX to workaround a warning with GCC 4 who complained
- // about a missing case Blinn: here - Blinn isn't a valid
- // value in the 3DS Loader, it is just needed for ASE
- case D3DS::Discreet3DS::Blinn:
- eShading = aiShadingMode_Blinn;
- break;
- }
- int eShading_ = static_cast<int>(eShading);
- mat.AddProperty<int>(&eShading_, 1, AI_MATKEY_SHADING_MODEL);
- // DIFFUSE texture
- if (oldMat.sTexDiffuse.mMapName.length() > 0)
- CopyTexture(mat, oldMat.sTexDiffuse, aiTextureType_DIFFUSE);
- // SPECULAR texture
- if (oldMat.sTexSpecular.mMapName.length() > 0)
- CopyTexture(mat, oldMat.sTexSpecular, aiTextureType_SPECULAR);
- // OPACITY texture
- if (oldMat.sTexOpacity.mMapName.length() > 0)
- CopyTexture(mat, oldMat.sTexOpacity, aiTextureType_OPACITY);
- // EMISSIVE texture
- if (oldMat.sTexEmissive.mMapName.length() > 0)
- CopyTexture(mat, oldMat.sTexEmissive, aiTextureType_EMISSIVE);
- // BUMP texture
- if (oldMat.sTexBump.mMapName.length() > 0)
- CopyTexture(mat, oldMat.sTexBump, aiTextureType_HEIGHT);
- // SHININESS texture
- if (oldMat.sTexShininess.mMapName.length() > 0)
- CopyTexture(mat, oldMat.sTexShininess, aiTextureType_SHININESS);
- // REFLECTION texture
- if (oldMat.sTexReflective.mMapName.length() > 0)
- CopyTexture(mat, oldMat.sTexReflective, aiTextureType_REFLECTION);
- // Store the name of the material itself, too
- if (oldMat.mName.length()) {
- aiString tex;
- tex.Set(oldMat.mName);
- mat.AddProperty(&tex, AI_MATKEY_NAME);
- }
- }
- // ------------------------------------------------------------------------------------------------
- // Split meshes by their materials and generate output aiMesh'es
- void Discreet3DSImporter::ConvertMeshes(aiScene *pcOut) {
- std::vector<aiMesh *> avOutMeshes;
- avOutMeshes.reserve(mScene->mMeshes.size() * 2);
- unsigned int iFaceCnt = 0, num = 0;
- aiString name;
- // we need to split all meshes by their materials
- for (std::vector<D3DS::Mesh>::iterator i = mScene->mMeshes.begin(); i != mScene->mMeshes.end(); ++i) {
- std::unique_ptr<std::vector<unsigned int>[]> aiSplit(new std::vector<unsigned int>[mScene->mMaterials.size()]);
- name.length = ASSIMP_itoa10(name.data, num++);
- unsigned int iNum = 0;
- for (std::vector<unsigned int>::const_iterator a = (*i).mFaceMaterials.begin();
- a != (*i).mFaceMaterials.end(); ++a, ++iNum) {
- aiSplit[*a].push_back(iNum);
- }
- // now generate submeshes
- for (unsigned int p = 0; p < mScene->mMaterials.size(); ++p) {
- if (aiSplit[p].empty()) {
- continue;
- }
- aiMesh *meshOut = new aiMesh();
- meshOut->mName = name;
- meshOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
- // be sure to setup the correct material index
- meshOut->mMaterialIndex = p;
- // use the color data as temporary storage
- meshOut->mColors[0] = (aiColor4D *)(&*i);
- avOutMeshes.push_back(meshOut);
- // convert vertices
- meshOut->mNumFaces = (unsigned int)aiSplit[p].size();
- meshOut->mNumVertices = meshOut->mNumFaces * 3;
- // allocate enough storage for faces
- meshOut->mFaces = new aiFace[meshOut->mNumFaces];
- iFaceCnt += meshOut->mNumFaces;
- meshOut->mVertices = new aiVector3D[meshOut->mNumVertices];
- meshOut->mNormals = new aiVector3D[meshOut->mNumVertices];
- if ((*i).mTexCoords.size()) {
- meshOut->mTextureCoords[0] = new aiVector3D[meshOut->mNumVertices];
- }
- for (unsigned int q = 0, base = 0; q < aiSplit[p].size(); ++q) {
- unsigned int index = aiSplit[p][q];
- aiFace &face = meshOut->mFaces[q];
- face.mIndices = new unsigned int[3];
- face.mNumIndices = 3;
- for (unsigned int a = 0; a < 3; ++a, ++base) {
- unsigned int idx = (*i).mFaces[index].mIndices[a];
- meshOut->mVertices[base] = (*i).mPositions[idx];
- meshOut->mNormals[base] = (*i).mNormals[idx];
- if ((*i).mTexCoords.size())
- meshOut->mTextureCoords[0][base] = (*i).mTexCoords[idx];
- face.mIndices[a] = base;
- }
- }
- }
- }
- // Copy them to the output array
- pcOut->mNumMeshes = (unsigned int)avOutMeshes.size();
- pcOut->mMeshes = new aiMesh *[pcOut->mNumMeshes]();
- for (unsigned int a = 0; a < pcOut->mNumMeshes; ++a) {
- pcOut->mMeshes[a] = avOutMeshes[a];
- }
- // We should have at least one face here
- if (!iFaceCnt) {
- throw DeadlyImportError("No faces loaded. The mesh is empty");
- }
- }
- // ------------------------------------------------------------------------------------------------
- // Add a node to the scenegraph and setup its final transformation
- void Discreet3DSImporter::AddNodeToGraph(aiScene *pcSOut, aiNode *pcOut,
- D3DS::Node *pcIn, aiMatrix4x4 & /*absTrafo*/) {
- std::vector<unsigned int> iArray;
- iArray.reserve(3);
- aiMatrix4x4 abs;
- // Find all meshes with the same name as the node
- for (unsigned int a = 0; a < pcSOut->mNumMeshes; ++a) {
- const D3DS::Mesh *pcMesh = (const D3DS::Mesh *)pcSOut->mMeshes[a]->mColors[0];
- ai_assert(nullptr != pcMesh);
- if (pcIn->mName == pcMesh->mName)
- iArray.push_back(a);
- }
- if (!iArray.empty()) {
- // The matrix should be identical for all meshes with the
- // same name. It HAS to be identical for all meshes .....
- D3DS::Mesh *imesh = ((D3DS::Mesh *)pcSOut->mMeshes[iArray[0]]->mColors[0]);
- // Compute the inverse of the transformation matrix to move the
- // vertices back to their relative and local space
- aiMatrix4x4 mInv = imesh->mMat, mInvTransposed = imesh->mMat;
- mInv.Inverse();
- mInvTransposed.Transpose();
- aiVector3D pivot = pcIn->vPivot;
- pcOut->mNumMeshes = (unsigned int)iArray.size();
- pcOut->mMeshes = new unsigned int[iArray.size()];
- for (unsigned int i = 0; i < iArray.size(); ++i) {
- const unsigned int iIndex = iArray[i];
- aiMesh *const mesh = pcSOut->mMeshes[iIndex];
- if (mesh->mColors[1] == nullptr) {
- // Transform the vertices back into their local space
- // fixme: consider computing normals after this, so we don't need to transform them
- const aiVector3D *const pvEnd = mesh->mVertices + mesh->mNumVertices;
- aiVector3D *pvCurrent = mesh->mVertices, *t2 = mesh->mNormals;
- for (; pvCurrent != pvEnd; ++pvCurrent, ++t2) {
- *pvCurrent = mInv * (*pvCurrent);
- *t2 = mInvTransposed * (*t2);
- }
- // Handle negative transformation matrix determinant -> invert vertex x
- if (imesh->mMat.Determinant() < 0.0f) {
- /* we *must* have normals */
- for (pvCurrent = mesh->mVertices, t2 = mesh->mNormals; pvCurrent != pvEnd; ++pvCurrent, ++t2) {
- pvCurrent->x *= -1.f;
- t2->x *= -1.f;
- }
- ASSIMP_LOG_INFO("3DS: Flipping mesh X-Axis");
- }
- // Handle pivot point
- if (pivot.x || pivot.y || pivot.z) {
- for (pvCurrent = mesh->mVertices; pvCurrent != pvEnd; ++pvCurrent) {
- *pvCurrent -= pivot;
- }
- }
- mesh->mColors[1] = (aiColor4D *)1;
- } else
- mesh->mColors[1] = (aiColor4D *)1;
- // Setup the mesh index
- pcOut->mMeshes[i] = iIndex;
- }
- }
- // Setup the name of the node
- // First instance keeps its name otherwise something might break, all others will be postfixed with their instance number
- if (pcIn->mInstanceNumber > 1) {
- char tmp[12];
- ASSIMP_itoa10(tmp, pcIn->mInstanceNumber);
- std::string tempStr = pcIn->mName + "_inst_";
- tempStr += tmp;
- pcOut->mName.Set(tempStr);
- } else
- pcOut->mName.Set(pcIn->mName);
- // Now build the transformation matrix of the node
- // ROTATION
- if (pcIn->aRotationKeys.size()) {
- // FIX to get to Assimp's quaternion conventions
- for (std::vector<aiQuatKey>::iterator it = pcIn->aRotationKeys.begin(); it != pcIn->aRotationKeys.end(); ++it) {
- (*it).mValue.w *= -1.f;
- }
- pcOut->mTransformation = aiMatrix4x4(pcIn->aRotationKeys[0].mValue.GetMatrix());
- } else if (pcIn->aCameraRollKeys.size()) {
- aiMatrix4x4::RotationZ(AI_DEG_TO_RAD(-pcIn->aCameraRollKeys[0].mValue),
- pcOut->mTransformation);
- }
- // SCALING
- aiMatrix4x4 &m = pcOut->mTransformation;
- if (pcIn->aScalingKeys.size()) {
- const aiVector3D &v = pcIn->aScalingKeys[0].mValue;
- m.a1 *= v.x;
- m.b1 *= v.x;
- m.c1 *= v.x;
- m.a2 *= v.y;
- m.b2 *= v.y;
- m.c2 *= v.y;
- m.a3 *= v.z;
- m.b3 *= v.z;
- m.c3 *= v.z;
- }
- // TRANSLATION
- if (pcIn->aPositionKeys.size()) {
- const aiVector3D &v = pcIn->aPositionKeys[0].mValue;
- m.a4 += v.x;
- m.b4 += v.y;
- m.c4 += v.z;
- }
- // Generate animation channels for the node
- if (pcIn->aPositionKeys.size() > 1 || pcIn->aRotationKeys.size() > 1 ||
- pcIn->aScalingKeys.size() > 1 || pcIn->aCameraRollKeys.size() > 1 ||
- pcIn->aTargetPositionKeys.size() > 1) {
- aiAnimation *anim = pcSOut->mAnimations[0];
- ai_assert(nullptr != anim);
- if (pcIn->aCameraRollKeys.size() > 1) {
- ASSIMP_LOG_VERBOSE_DEBUG("3DS: Converting camera roll track ...");
- // Camera roll keys - in fact they're just rotations
- // around the camera's z axis. The angles are given
- // in degrees (and they're clockwise).
- pcIn->aRotationKeys.resize(pcIn->aCameraRollKeys.size());
- for (unsigned int i = 0; i < pcIn->aCameraRollKeys.size(); ++i) {
- aiQuatKey &q = pcIn->aRotationKeys[i];
- aiFloatKey &f = pcIn->aCameraRollKeys[i];
- q.mTime = f.mTime;
- // FIX to get to Assimp quaternion conventions
- q.mValue = aiQuaternion(0.f, 0.f, AI_DEG_TO_RAD(/*-*/ f.mValue));
- }
- }
- #if 0
- if (pcIn->aTargetPositionKeys.size() > 1)
- {
- ASSIMP_LOG_VERBOSE_DEBUG("3DS: Converting target track ...");
- // Camera or spot light - need to convert the separate
- // target position channel to our representation
- TargetAnimationHelper helper;
- if (pcIn->aPositionKeys.empty())
- {
- // We can just pass zero here ...
- helper.SetFixedMainAnimationChannel(aiVector3D());
- }
- else helper.SetMainAnimationChannel(&pcIn->aPositionKeys);
- helper.SetTargetAnimationChannel(&pcIn->aTargetPositionKeys);
- // Do the conversion
- std::vector<aiVectorKey> distanceTrack;
- helper.Process(&distanceTrack);
- // Now add a new node as child, name it <ourName>.Target
- // and assign the distance track to it. This is that the
- // information where the target is and how it moves is
- // not lost
- D3DS::Node* nd = new D3DS::Node();
- pcIn->push_back(nd);
- nd->mName = pcIn->mName + ".Target";
- aiNodeAnim* nda = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim();
- nda->mNodeName.Set(nd->mName);
- nda->mNumPositionKeys = (unsigned int)distanceTrack.size();
- nda->mPositionKeys = new aiVectorKey[nda->mNumPositionKeys];
- ::memcpy(nda->mPositionKeys,&distanceTrack[0],
- sizeof(aiVectorKey)*nda->mNumPositionKeys);
- }
- #endif
- // Cameras or lights define their transformation in their parent node and in the
- // corresponding light or camera chunks. However, we read and process the latter
- // to be able to return valid cameras/lights even if no scenegraph is given.
- for (unsigned int n = 0; n < pcSOut->mNumCameras; ++n) {
- if (pcSOut->mCameras[n]->mName == pcOut->mName) {
- pcSOut->mCameras[n]->mLookAt = aiVector3D(0.f, 0.f, 1.f);
- }
- }
- for (unsigned int n = 0; n < pcSOut->mNumLights; ++n) {
- if (pcSOut->mLights[n]->mName == pcOut->mName) {
- pcSOut->mLights[n]->mDirection = aiVector3D(0.f, 0.f, 1.f);
- }
- }
- // Allocate a new node anim and setup its name
- aiNodeAnim *nda = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim();
- nda->mNodeName.Set(pcIn->mName);
- // POSITION keys
- if (pcIn->aPositionKeys.size() > 0) {
- nda->mNumPositionKeys = (unsigned int)pcIn->aPositionKeys.size();
- nda->mPositionKeys = new aiVectorKey[nda->mNumPositionKeys];
- ::memcpy(nda->mPositionKeys, &pcIn->aPositionKeys[0],
- sizeof(aiVectorKey) * nda->mNumPositionKeys);
- }
- // ROTATION keys
- if (pcIn->aRotationKeys.size() > 0) {
- nda->mNumRotationKeys = (unsigned int)pcIn->aRotationKeys.size();
- nda->mRotationKeys = new aiQuatKey[nda->mNumRotationKeys];
- // Rotations are quaternion offsets
- aiQuaternion abs1;
- for (unsigned int n = 0; n < nda->mNumRotationKeys; ++n) {
- const aiQuatKey &q = pcIn->aRotationKeys[n];
- abs1 = (n ? abs1 * q.mValue : q.mValue);
- nda->mRotationKeys[n].mTime = q.mTime;
- nda->mRotationKeys[n].mValue = abs1.Normalize();
- }
- }
- // SCALING keys
- if (pcIn->aScalingKeys.size() > 0) {
- nda->mNumScalingKeys = (unsigned int)pcIn->aScalingKeys.size();
- nda->mScalingKeys = new aiVectorKey[nda->mNumScalingKeys];
- ::memcpy(nda->mScalingKeys, &pcIn->aScalingKeys[0],
- sizeof(aiVectorKey) * nda->mNumScalingKeys);
- }
- }
- // Allocate storage for children
- const unsigned int size = static_cast<unsigned int>(pcIn->mChildren.size());
- pcOut->mNumChildren = size;
- if (size == 0) {
- return;
- }
- pcOut->mChildren = new aiNode *[pcIn->mChildren.size()];
- // Recursively process all children
- for (unsigned int i = 0; i < size; ++i) {
- pcOut->mChildren[i] = new aiNode();
- pcOut->mChildren[i]->mParent = pcOut;
- AddNodeToGraph(pcSOut, pcOut->mChildren[i], pcIn->mChildren[i], abs);
- }
- }
- // ------------------------------------------------------------------------------------------------
- // Find out how many node animation channels we'll have finally
- void CountTracks(D3DS::Node *node, unsigned int &cnt) {
- //////////////////////////////////////////////////////////////////////////////
- // We will never generate more than one channel for a node, so
- // this is rather easy here.
- if (node->aPositionKeys.size() > 1 || node->aRotationKeys.size() > 1 ||
- node->aScalingKeys.size() > 1 || node->aCameraRollKeys.size() > 1 ||
- node->aTargetPositionKeys.size() > 1) {
- ++cnt;
- // account for the additional channel for the camera/spotlight target position
- if (node->aTargetPositionKeys.size() > 1) ++cnt;
- }
- // Recursively process all children
- for (unsigned int i = 0; i < node->mChildren.size(); ++i)
- CountTracks(node->mChildren[i], cnt);
- }
- // ------------------------------------------------------------------------------------------------
- // Generate the output node graph
- void Discreet3DSImporter::GenerateNodeGraph(aiScene *pcOut) {
- pcOut->mRootNode = new aiNode();
- if (0 == mRootNode->mChildren.size()) {
- //////////////////////////////////////////////////////////////////////////////
- // It seems the file is so messed up that it has not even a hierarchy.
- // generate a flat hiearachy which looks like this:
- //
- // ROOT_NODE
- // |
- // ----------------------------------------
- // | | | | |
- // MESH_0 MESH_1 MESH_2 ... MESH_N CAMERA_0 ....
- //
- ASSIMP_LOG_WARN("No hierarchy information has been found in the file. ");
- pcOut->mRootNode->mNumChildren = pcOut->mNumMeshes +
- static_cast<unsigned int>(mScene->mCameras.size() + mScene->mLights.size());
- pcOut->mRootNode->mChildren = new aiNode *[pcOut->mRootNode->mNumChildren];
- pcOut->mRootNode->mName.Set("<3DSDummyRoot>");
- // Build dummy nodes for all meshes
- unsigned int a = 0;
- for (unsigned int i = 0; i < pcOut->mNumMeshes; ++i, ++a) {
- aiNode *pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
- pcNode->mParent = pcOut->mRootNode;
- pcNode->mMeshes = new unsigned int[1];
- pcNode->mMeshes[0] = i;
- pcNode->mNumMeshes = 1;
- // Build a name for the node
- pcNode->mName.length = ai_snprintf(pcNode->mName.data, AI_MAXLEN, "3DSMesh_%u", i);
- }
- // Build dummy nodes for all cameras
- for (unsigned int i = 0; i < (unsigned int)mScene->mCameras.size(); ++i, ++a) {
- aiNode *pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
- pcNode->mParent = pcOut->mRootNode;
- // Build a name for the node
- pcNode->mName = mScene->mCameras[i]->mName;
- }
- // Build dummy nodes for all lights
- for (unsigned int i = 0; i < (unsigned int)mScene->mLights.size(); ++i, ++a) {
- aiNode *pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
- pcNode->mParent = pcOut->mRootNode;
- // Build a name for the node
- pcNode->mName = mScene->mLights[i]->mName;
- }
- } else {
- // First of all: find out how many scaling, rotation and translation
- // animation tracks we'll have afterwards
- unsigned int numChannel = 0;
- CountTracks(mRootNode, numChannel);
- if (numChannel) {
- // Allocate a primary animation channel
- pcOut->mNumAnimations = 1;
- pcOut->mAnimations = new aiAnimation *[1];
- aiAnimation *anim = pcOut->mAnimations[0] = new aiAnimation();
- anim->mName.Set("3DSMasterAnim");
- // Allocate enough storage for all node animation channels,
- // but don't set the mNumChannels member - we'll use it to
- // index into the array
- anim->mChannels = new aiNodeAnim *[numChannel];
- }
- aiMatrix4x4 m;
- AddNodeToGraph(pcOut, pcOut->mRootNode, mRootNode, m);
- }
- // We used the first and second vertex color set to store some temporary values so we need to cleanup here
- for (unsigned int a = 0; a < pcOut->mNumMeshes; ++a) {
- pcOut->mMeshes[a]->mColors[0] = nullptr;
- pcOut->mMeshes[a]->mColors[1] = nullptr;
- }
- pcOut->mRootNode->mTransformation = aiMatrix4x4(
- 1.f, 0.f, 0.f, 0.f,
- 0.f, 0.f, 1.f, 0.f,
- 0.f, -1.f, 0.f, 0.f,
- 0.f, 0.f, 0.f, 1.f) *
- pcOut->mRootNode->mTransformation;
- // If the root node is unnamed name it "<3DSRoot>"
- if (::strstr(pcOut->mRootNode->mName.data, "UNNAMED") ||
- (pcOut->mRootNode->mName.data[0] == '$' && pcOut->mRootNode->mName.data[1] == '$')) {
- pcOut->mRootNode->mName.Set("<3DSRoot>");
- }
- }
- // ------------------------------------------------------------------------------------------------
- // Convert all meshes in the scene and generate the final output scene.
- void Discreet3DSImporter::ConvertScene(aiScene *pcOut) {
- // Allocate enough storage for all output materials
- pcOut->mNumMaterials = (unsigned int)mScene->mMaterials.size();
- pcOut->mMaterials = new aiMaterial *[pcOut->mNumMaterials];
- // ... and convert the 3DS materials to aiMaterial's
- for (unsigned int i = 0; i < pcOut->mNumMaterials; ++i) {
- aiMaterial *pcNew = new aiMaterial();
- ConvertMaterial(mScene->mMaterials[i], *pcNew);
- pcOut->mMaterials[i] = pcNew;
- }
- // Generate the output mesh list
- ConvertMeshes(pcOut);
- // Now copy all light sources to the output scene
- pcOut->mNumLights = (unsigned int)mScene->mLights.size();
- if (pcOut->mNumLights) {
- pcOut->mLights = new aiLight *[pcOut->mNumLights];
- ::memcpy(pcOut->mLights, &mScene->mLights[0], sizeof(void *) * pcOut->mNumLights);
- }
- // Now copy all cameras to the output scene
- pcOut->mNumCameras = (unsigned int)mScene->mCameras.size();
- if (pcOut->mNumCameras) {
- pcOut->mCameras = new aiCamera *[pcOut->mNumCameras];
- ::memcpy(pcOut->mCameras, &mScene->mCameras[0], sizeof(void *) * pcOut->mNumCameras);
- }
- }
- } // namespace Assimp
- #endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER
|