summaryrefslogtreecommitdiff
path: root/libs/assimp/code/AssetLib/3DS
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2022-04-16 11:55:09 -0500
committersanine <sanine.not@pm.me>2022-04-16 11:55:09 -0500
commitdb81b925d776103326128bf629cbdda576a223e7 (patch)
tree58bea8155c686733310009f6bed7363f91fbeb9d /libs/assimp/code/AssetLib/3DS
parent55860037b14fb3893ba21cf2654c83d349cc1082 (diff)
move 3rd-party librarys into libs/ and add built-in honeysuckle
Diffstat (limited to 'libs/assimp/code/AssetLib/3DS')
-rw-r--r--libs/assimp/code/AssetLib/3DS/3DSConverter.cpp807
-rw-r--r--libs/assimp/code/AssetLib/3DS/3DSExporter.cpp584
-rw-r--r--libs/assimp/code/AssetLib/3DS/3DSExporter.h98
-rw-r--r--libs/assimp/code/AssetLib/3DS/3DSHelper.h702
-rw-r--r--libs/assimp/code/AssetLib/3DS/3DSLoader.cpp1336
-rw-r--r--libs/assimp/code/AssetLib/3DS/3DSLoader.h289
6 files changed, 3816 insertions, 0 deletions
diff --git a/libs/assimp/code/AssetLib/3DS/3DSConverter.cpp b/libs/assimp/code/AssetLib/3DS/3DSConverter.cpp
new file mode 100644
index 0000000..5a01429
--- /dev/null
+++ b/libs/assimp/code/AssetLib/3DS/3DSConverter.cpp
@@ -0,0 +1,807 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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>
+
+using namespace Assimp;
+
+static const 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);
+ }
+
+ 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 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
+ pcOut->mNumChildren = (unsigned int)pcIn->mChildren.size();
+ pcOut->mChildren = new aiNode *[pcIn->mChildren.size()];
+
+ // Recursively process all children
+ const unsigned int size = static_cast<unsigned int>(pcIn->mChildren.size());
+ 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, 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);
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER
diff --git a/libs/assimp/code/AssetLib/3DS/3DSExporter.cpp b/libs/assimp/code/AssetLib/3DS/3DSExporter.cpp
new file mode 100644
index 0000000..71588f9
--- /dev/null
+++ b/libs/assimp/code/AssetLib/3DS/3DSExporter.cpp
@@ -0,0 +1,584 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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.
+
+----------------------------------------------------------------------
+*/
+
+#ifndef ASSIMP_BUILD_NO_EXPORT
+#ifndef ASSIMP_BUILD_NO_3DS_EXPORTER
+
+#include "AssetLib/3DS/3DSExporter.h"
+#include "AssetLib/3DS/3DSHelper.h"
+#include "AssetLib/3DS/3DSLoader.h"
+#include "PostProcessing/SplitLargeMeshes.h"
+
+#include <assimp/SceneCombiner.h>
+#include <assimp/StringComparison.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/Exporter.hpp>
+#include <assimp/IOSystem.hpp>
+
+#include <memory>
+
+namespace Assimp {
+
+using namespace D3DS;
+
+namespace {
+
+//////////////////////////////////////////////////////////////////////////////////////
+// Scope utility to write a 3DS file chunk.
+//
+// Upon construction, the chunk header is written with the chunk type (flags)
+// filled out, but the chunk size left empty. Upon destruction, the correct chunk
+// size based on the then-position of the output stream cursor is filled in.
+class ChunkWriter {
+ enum {
+ CHUNK_SIZE_NOT_SET = 0xdeadbeef,
+ SIZE_OFFSET = 2
+ };
+
+public:
+ ChunkWriter(StreamWriterLE &writer, uint16_t chunk_type) :
+ writer(writer) {
+ chunk_start_pos = writer.GetCurrentPos();
+ writer.PutU2(chunk_type);
+ writer.PutU4((uint32_t)CHUNK_SIZE_NOT_SET);
+ }
+
+ ~ChunkWriter() {
+ std::size_t head_pos = writer.GetCurrentPos();
+
+ ai_assert(head_pos > chunk_start_pos);
+ const std::size_t chunk_size = head_pos - chunk_start_pos;
+
+ writer.SetCurrentPos(chunk_start_pos + SIZE_OFFSET);
+ writer.PutU4(static_cast<uint32_t>(chunk_size));
+ writer.SetCurrentPos(head_pos);
+ }
+
+private:
+ StreamWriterLE &writer;
+ std::size_t chunk_start_pos;
+};
+
+// Return an unique name for a given |mesh| attached to |node| that
+// preserves the mesh's given name if it has one. |index| is the index
+// of the mesh in |aiScene::mMeshes|.
+std::string GetMeshName(const aiMesh &mesh, unsigned int index, const aiNode &node) {
+ static const char underscore = '_';
+ char postfix[10] = { 0 };
+ ASSIMP_itoa10(postfix, index);
+
+ std::string result = node.mName.C_Str();
+ if (mesh.mName.length > 0) {
+ result += underscore;
+ result += mesh.mName.C_Str();
+ }
+ return result + underscore + postfix;
+}
+
+// Return an unique name for a given |mat| with original position |index|
+// in |aiScene::mMaterials|. The name preserves the original material
+// name if possible.
+std::string GetMaterialName(const aiMaterial &mat, unsigned int index) {
+ static const std::string underscore = "_";
+ char postfix[10] = { 0 };
+ ASSIMP_itoa10(postfix, index);
+
+ aiString mat_name;
+ if (AI_SUCCESS == mat.Get(AI_MATKEY_NAME, mat_name)) {
+ return mat_name.C_Str() + underscore + postfix;
+ }
+
+ return "Material" + underscore + postfix;
+}
+
+// Collect world transformations for each node
+void CollectTrafos(const aiNode *node, std::map<const aiNode *, aiMatrix4x4> &trafos) {
+ const aiMatrix4x4 &parent = node->mParent ? trafos[node->mParent] : aiMatrix4x4();
+ trafos[node] = parent * node->mTransformation;
+ for (unsigned int i = 0; i < node->mNumChildren; ++i) {
+ CollectTrafos(node->mChildren[i], trafos);
+ }
+}
+
+// Generate a flat list of the meshes (by index) assigned to each node
+void CollectMeshes(const aiNode *node, std::multimap<const aiNode *, unsigned int> &meshes) {
+ for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
+ meshes.insert(std::make_pair(node, node->mMeshes[i]));
+ }
+ for (unsigned int i = 0; i < node->mNumChildren; ++i) {
+ CollectMeshes(node->mChildren[i], meshes);
+ }
+}
+} // namespace
+
+// ------------------------------------------------------------------------------------------------
+// Worker function for exporting a scene to 3DS. Prototyped and registered in Exporter.cpp
+void ExportScene3DS(const char *pFile, IOSystem *pIOSystem, const aiScene *pScene, const ExportProperties * /*pProperties*/) {
+ std::shared_ptr<IOStream> outfile(pIOSystem->Open(pFile, "wb"));
+ if (!outfile) {
+ throw DeadlyExportError("Could not open output .3ds file: " + std::string(pFile));
+ }
+
+ // TODO: This extra copy should be avoided and all of this made a preprocess
+ // requirement of the 3DS exporter.
+ //
+ // 3DS meshes can be max 0xffff (16 Bit) vertices and faces, respectively.
+ // SplitLargeMeshes can do this, but it requires the correct limit to be set
+ // which is not possible with the current way of specifying preprocess steps
+ // in |Exporter::ExportFormatEntry|.
+ aiScene *scenecopy_tmp;
+ SceneCombiner::CopyScene(&scenecopy_tmp, pScene);
+ std::unique_ptr<aiScene> scenecopy(scenecopy_tmp);
+
+ SplitLargeMeshesProcess_Triangle tri_splitter;
+ tri_splitter.SetLimit(0xffff);
+ tri_splitter.Execute(scenecopy.get());
+
+ SplitLargeMeshesProcess_Vertex vert_splitter;
+ vert_splitter.SetLimit(0xffff);
+ vert_splitter.Execute(scenecopy.get());
+
+ // Invoke the actual exporter
+ Discreet3DSExporter exporter(outfile, scenecopy.get());
+}
+
+} // end of namespace Assimp
+
+// ------------------------------------------------------------------------------------------------
+Discreet3DSExporter::Discreet3DSExporter(std::shared_ptr<IOStream> &outfile, const aiScene *scene) :
+ scene(scene), writer(outfile) {
+ CollectTrafos(scene->mRootNode, trafos);
+ CollectMeshes(scene->mRootNode, meshes);
+
+ ChunkWriter curRootChunk(writer, Discreet3DS::CHUNK_MAIN);
+
+ {
+ ChunkWriter curChunk(writer, Discreet3DS::CHUNK_OBJMESH);
+ WriteMaterials();
+ WriteMeshes();
+
+ {
+ ChunkWriter curChunk1(writer, Discreet3DS::CHUNK_MASTER_SCALE);
+ writer.PutF4(1.0f);
+ }
+ }
+
+ {
+ ChunkWriter curChunk(writer, Discreet3DS::CHUNK_KEYFRAMER);
+ WriteHierarchy(*scene->mRootNode, -1, -1);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+Discreet3DSExporter::~Discreet3DSExporter() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+int Discreet3DSExporter::WriteHierarchy(const aiNode &node, int seq, int sibling_level) {
+ // 3DS scene hierarchy is serialized as in http://www.martinreddy.net/gfx/3d/3DS.spec
+ {
+ ChunkWriter curRootChunk(writer, Discreet3DS::CHUNK_TRACKINFO);
+ {
+ ChunkWriter curChunk(writer, Discreet3DS::CHUNK_TRACKOBJNAME);
+
+ // Assimp node names are unique and distinct from all mesh-node
+ // names we generate; thus we can use them as-is
+ WriteString(node.mName);
+
+ // Two unknown int16 values - it is even unclear if 0 is a safe value
+ // but luckily importers do not know better either.
+ writer.PutI4(0);
+
+ int16_t hierarchy_pos = static_cast<int16_t>(seq);
+ if (sibling_level != -1) {
+ hierarchy_pos = (uint16_t)sibling_level;
+ }
+
+ // Write the hierarchy position
+ writer.PutI2(hierarchy_pos);
+ }
+ }
+
+ // TODO: write transformation chunks
+
+ ++seq;
+ sibling_level = seq;
+
+ // Write all children
+ for (unsigned int i = 0; i < node.mNumChildren; ++i) {
+ seq = WriteHierarchy(*node.mChildren[i], seq, i == 0 ? -1 : sibling_level);
+ }
+
+ // Write all meshes as separate nodes to be able to reference the meshes by name
+ for (unsigned int i = 0; i < node.mNumMeshes; ++i) {
+ const bool first_child = node.mNumChildren == 0 && i == 0;
+
+ const unsigned int mesh_idx = node.mMeshes[i];
+ const aiMesh &mesh = *scene->mMeshes[mesh_idx];
+
+ ChunkWriter curChunk(writer, Discreet3DS::CHUNK_TRACKINFO);
+ {
+ ChunkWriter chunk(writer, Discreet3DS::CHUNK_TRACKOBJNAME);
+ WriteString(GetMeshName(mesh, mesh_idx, node));
+
+ writer.PutI4(0);
+ writer.PutI2(static_cast<int16_t>(first_child ? seq : sibling_level));
+ ++seq;
+ }
+ }
+ return seq;
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSExporter::WriteMaterials() {
+ for (unsigned int i = 0; i < scene->mNumMaterials; ++i) {
+ ChunkWriter curRootChunk(writer, Discreet3DS::CHUNK_MAT_MATERIAL);
+ const aiMaterial &mat = *scene->mMaterials[i];
+
+ {
+ ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_MATNAME);
+ const std::string &name = GetMaterialName(mat, i);
+ WriteString(name);
+ }
+
+ aiColor3D color;
+ if (mat.Get(AI_MATKEY_COLOR_DIFFUSE, color) == AI_SUCCESS) {
+ ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAT_DIFFUSE);
+ WriteColor(color);
+ }
+
+ if (mat.Get(AI_MATKEY_COLOR_SPECULAR, color) == AI_SUCCESS) {
+ ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAT_SPECULAR);
+ WriteColor(color);
+ }
+
+ if (mat.Get(AI_MATKEY_COLOR_AMBIENT, color) == AI_SUCCESS) {
+ ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAT_AMBIENT);
+ WriteColor(color);
+ }
+
+ float f;
+ if (mat.Get(AI_MATKEY_OPACITY, f) == AI_SUCCESS) {
+ ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_TRANSPARENCY);
+ WritePercentChunk(1.0f - f);
+ }
+
+ if (mat.Get(AI_MATKEY_COLOR_EMISSIVE, color) == AI_SUCCESS) {
+ ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAT_SELF_ILLUM);
+ WriteColor(color);
+ }
+
+ aiShadingMode shading_mode = aiShadingMode_Flat;
+ if (mat.Get(AI_MATKEY_SHADING_MODEL, shading_mode) == AI_SUCCESS) {
+ ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_SHADING);
+
+ Discreet3DS::shadetype3ds shading_mode_out;
+ switch (shading_mode) {
+ case aiShadingMode_Flat:
+ case aiShadingMode_NoShading:
+ shading_mode_out = Discreet3DS::Flat;
+ break;
+
+ case aiShadingMode_Gouraud:
+ case aiShadingMode_Toon:
+ case aiShadingMode_OrenNayar:
+ case aiShadingMode_Minnaert:
+ shading_mode_out = Discreet3DS::Gouraud;
+ break;
+
+ case aiShadingMode_Phong:
+ case aiShadingMode_Blinn:
+ case aiShadingMode_CookTorrance:
+ case aiShadingMode_Fresnel:
+ case aiShadingMode_PBR_BRDF: // Possibly should be Discreet3DS::Metal in some cases but this is undocumented
+ shading_mode_out = Discreet3DS::Phong;
+ break;
+
+ default:
+ shading_mode_out = Discreet3DS::Flat;
+ ai_assert(false);
+ };
+ writer.PutU2(static_cast<uint16_t>(shading_mode_out));
+ }
+
+ if (mat.Get(AI_MATKEY_SHININESS, f) == AI_SUCCESS) {
+ ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_SHININESS);
+ WritePercentChunk(f);
+ }
+
+ if (mat.Get(AI_MATKEY_SHININESS_STRENGTH, f) == AI_SUCCESS) {
+ ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_SHININESS_PERCENT);
+ WritePercentChunk(f);
+ }
+
+ int twosided;
+ if (mat.Get(AI_MATKEY_TWOSIDED, twosided) == AI_SUCCESS && twosided != 0) {
+ ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_TWO_SIDE);
+ writer.PutI2(1);
+ }
+
+ // Fallback to BASE_COLOR if no DIFFUSE
+ if (!WriteTexture(mat, aiTextureType_DIFFUSE, Discreet3DS::CHUNK_MAT_TEXTURE))
+ WriteTexture(mat, aiTextureType_BASE_COLOR, Discreet3DS::CHUNK_MAT_TEXTURE);
+
+ WriteTexture(mat, aiTextureType_HEIGHT, Discreet3DS::CHUNK_MAT_BUMPMAP);
+ WriteTexture(mat, aiTextureType_OPACITY, Discreet3DS::CHUNK_MAT_OPACMAP);
+ WriteTexture(mat, aiTextureType_SHININESS, Discreet3DS::CHUNK_MAT_MAT_SHINMAP);
+ WriteTexture(mat, aiTextureType_SPECULAR, Discreet3DS::CHUNK_MAT_SPECMAP);
+ WriteTexture(mat, aiTextureType_EMISSIVE, Discreet3DS::CHUNK_MAT_SELFIMAP);
+ WriteTexture(mat, aiTextureType_REFLECTION, Discreet3DS::CHUNK_MAT_REFLMAP);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// returns true if the texture existed
+bool Discreet3DSExporter::WriteTexture(const aiMaterial &mat, aiTextureType type, uint16_t chunk_flags) {
+ aiString path;
+ aiTextureMapMode map_mode[2] = {
+ aiTextureMapMode_Wrap, aiTextureMapMode_Wrap
+ };
+ ai_real blend = 1.0;
+ if (mat.GetTexture(type, 0, &path, nullptr, nullptr, &blend, nullptr, map_mode) != AI_SUCCESS || !path.length) {
+ return false;
+ }
+
+ // TODO: handle embedded textures properly
+ if (path.data[0] == '*') {
+ ASSIMP_LOG_ERROR("Ignoring embedded texture for export: ", path.C_Str());
+ return false;
+ }
+
+ ChunkWriter chunk(writer, chunk_flags);
+ {
+ ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAPFILE);
+ WriteString(path);
+ }
+
+ WritePercentChunk(blend);
+
+ {
+ ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAT_MAP_TILING);
+ uint16_t val = 0; // WRAP
+ if (map_mode[0] == aiTextureMapMode_Mirror) {
+ val = 0x2;
+ } else if (map_mode[0] == aiTextureMapMode_Decal) {
+ val = 0x10;
+ }
+ writer.PutU2(val);
+ }
+ // TODO: export texture transformation (i.e. UV offset, scale, rotation)
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSExporter::WriteMeshes() {
+ // NOTE: 3DS allows for instances. However:
+ // i) not all importers support reading them
+ // ii) instances are not as flexible as they are in assimp, in particular,
+ // nodes can carry (and instance) only one mesh.
+ //
+ // This exporter currently deep clones all instanced meshes, i.e. for each mesh
+ // attached to a node a full TRIMESH chunk is written to the file.
+ //
+ // Furthermore, the TRIMESH is transformed into world space so that it will
+ // appear correctly if importers don't read the scene hierarchy at all.
+ for (MeshesByNodeMap::const_iterator it = meshes.begin(); it != meshes.end(); ++it) {
+ const aiNode &node = *(*it).first;
+ const unsigned int mesh_idx = (*it).second;
+
+ const aiMesh &mesh = *scene->mMeshes[mesh_idx];
+
+ // This should not happen if the SLM step is correctly executed
+ // before the scene is handed to the exporter
+ ai_assert(mesh.mNumVertices <= 0xffff);
+ ai_assert(mesh.mNumFaces <= 0xffff);
+
+ const aiMatrix4x4 &trafo = trafos[&node];
+
+ ChunkWriter chunk(writer, Discreet3DS::CHUNK_OBJBLOCK);
+
+ // Mesh name is tied to the node it is attached to so it can later be referenced
+ const std::string &name = GetMeshName(mesh, mesh_idx, node);
+ WriteString(name);
+
+ // TRIMESH chunk
+ ChunkWriter chunk2(writer, Discreet3DS::CHUNK_TRIMESH);
+
+ // Vertices in world space
+ {
+ ChunkWriter curChunk(writer, Discreet3DS::CHUNK_VERTLIST);
+
+ const uint16_t count = static_cast<uint16_t>(mesh.mNumVertices);
+ writer.PutU2(count);
+ for (unsigned int i = 0; i < mesh.mNumVertices; ++i) {
+ const aiVector3D &v = mesh.mVertices[i];
+ writer.PutF4(v.x);
+ writer.PutF4(v.y);
+ writer.PutF4(v.z);
+ }
+ }
+
+ // UV coordinates
+ if (mesh.HasTextureCoords(0)) {
+ ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAPLIST);
+ const uint16_t count = static_cast<uint16_t>(mesh.mNumVertices);
+ writer.PutU2(count);
+
+ for (unsigned int i = 0; i < mesh.mNumVertices; ++i) {
+ const aiVector3D &v = mesh.mTextureCoords[0][i];
+ writer.PutF4(v.x);
+ writer.PutF4(v.y);
+ }
+ }
+
+ // Faces (indices)
+ {
+ ChunkWriter curChunk(writer, Discreet3DS::CHUNK_FACELIST);
+
+ ai_assert(mesh.mNumFaces <= 0xffff);
+
+ // Count triangles, discard lines and points
+ uint16_t count = 0;
+ for (unsigned int i = 0; i < mesh.mNumFaces; ++i) {
+ const aiFace &f = mesh.mFaces[i];
+ if (f.mNumIndices < 3) {
+ continue;
+ }
+ // TRIANGULATE step is a pre-requisite so we should not see polys here
+ ai_assert(f.mNumIndices == 3);
+ ++count;
+ }
+
+ writer.PutU2(count);
+ for (unsigned int i = 0; i < mesh.mNumFaces; ++i) {
+ const aiFace &f = mesh.mFaces[i];
+ if (f.mNumIndices < 3) {
+ continue;
+ }
+
+ for (unsigned int j = 0; j < 3; ++j) {
+ ai_assert(f.mIndices[j] <= 0xffff);
+ writer.PutI2(static_cast<uint16_t>(f.mIndices[j]));
+ }
+
+ // Edge visibility flag
+ writer.PutI2(0x0);
+ }
+
+ // TODO: write smoothing groups (CHUNK_SMOOLIST)
+
+ WriteFaceMaterialChunk(mesh);
+ }
+
+ // Transformation matrix by which the mesh vertices have been pre-transformed with.
+ {
+ ChunkWriter curChunk(writer, Discreet3DS::CHUNK_TRMATRIX);
+ // Store rotation 3x3 matrix row wise
+ for (unsigned int r = 0; r < 3; ++r) {
+ for (unsigned int c = 0; c < 3; ++c) {
+ writer.PutF4(trafo[r][c]);
+ }
+ }
+ // Store translation sub vector column wise
+ for (unsigned int r = 0; r < 3; ++r) {
+ writer.PutF4(trafo[r][3]);
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSExporter::WriteFaceMaterialChunk(const aiMesh &mesh) {
+ ChunkWriter curChunk(writer, Discreet3DS::CHUNK_FACEMAT);
+ const std::string &name = GetMaterialName(*scene->mMaterials[mesh.mMaterialIndex], mesh.mMaterialIndex);
+ WriteString(name);
+
+ // Because assimp splits meshes by material, only a single
+ // FACEMAT chunk needs to be written
+ ai_assert(mesh.mNumFaces <= 0xffff);
+ const uint16_t count = static_cast<uint16_t>(mesh.mNumFaces);
+ writer.PutU2(count);
+
+ for (unsigned int i = 0; i < mesh.mNumFaces; ++i) {
+ writer.PutU2(static_cast<uint16_t>(i));
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSExporter::WriteString(const std::string &s) {
+ for (std::string::const_iterator it = s.begin(); it != s.end(); ++it) {
+ writer.PutI1(*it);
+ }
+ writer.PutI1('\0');
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSExporter::WriteString(const aiString &s) {
+ for (std::size_t i = 0; i < s.length; ++i) {
+ writer.PutI1(s.data[i]);
+ }
+ writer.PutI1('\0');
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSExporter::WriteColor(const aiColor3D &color) {
+ ChunkWriter curChunk(writer, Discreet3DS::CHUNK_RGBF);
+ writer.PutF4(color.r);
+ writer.PutF4(color.g);
+ writer.PutF4(color.b);
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSExporter::WritePercentChunk(float f) {
+ ChunkWriter curChunk(writer, Discreet3DS::CHUNK_PERCENTF);
+ writer.PutF4(f);
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSExporter::WritePercentChunk(double f) {
+ ChunkWriter ccurChunkhunk(writer, Discreet3DS::CHUNK_PERCENTD);
+ writer.PutF8(f);
+}
+
+#endif // ASSIMP_BUILD_NO_3DS_EXPORTER
+#endif // ASSIMP_BUILD_NO_EXPORT
diff --git a/libs/assimp/code/AssetLib/3DS/3DSExporter.h b/libs/assimp/code/AssetLib/3DS/3DSExporter.h
new file mode 100644
index 0000000..82ec351
--- /dev/null
+++ b/libs/assimp/code/AssetLib/3DS/3DSExporter.h
@@ -0,0 +1,98 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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 3DSExporter.h
+ * 3DS Exporter Main Header
+ */
+#ifndef AI_3DSEXPORTER_H_INC
+#define AI_3DSEXPORTER_H_INC
+
+#include <map>
+#include <memory>
+
+#include <assimp/StreamWriter.h>
+#include <assimp/material.h>
+
+struct aiScene;
+struct aiNode;
+struct aiMaterial;
+struct aiMesh;
+
+namespace Assimp
+{
+
+// ------------------------------------------------------------------------------------------------
+/**
+ * @brief Helper class to export a given scene to a 3DS file.
+ */
+// ------------------------------------------------------------------------------------------------
+class Discreet3DSExporter {
+public:
+ Discreet3DSExporter(std::shared_ptr<IOStream> &outfile, const aiScene* pScene);
+ ~Discreet3DSExporter();
+
+private:
+ void WriteMeshes();
+ void WriteMaterials();
+ bool WriteTexture(const aiMaterial& mat, aiTextureType type, uint16_t chunk_flags);
+ void WriteFaceMaterialChunk(const aiMesh& mesh);
+ int WriteHierarchy(const aiNode& node, int level, int sibling_level);
+ void WriteString(const std::string& s);
+ void WriteString(const aiString& s);
+ void WriteColor(const aiColor3D& color);
+ void WritePercentChunk(float f);
+ void WritePercentChunk(double f);
+
+private:
+ const aiScene* const scene;
+ StreamWriterLE writer;
+
+ std::map<const aiNode*, aiMatrix4x4> trafos;
+
+ typedef std::multimap<const aiNode*, unsigned int> MeshesByNodeMap;
+ MeshesByNodeMap meshes;
+
+};
+
+} // Namespace Assimp
+
+#endif // AI_3DSEXPORTER_H_INC
diff --git a/libs/assimp/code/AssetLib/3DS/3DSHelper.h b/libs/assimp/code/AssetLib/3DS/3DSHelper.h
new file mode 100644
index 0000000..dc10980
--- /dev/null
+++ b/libs/assimp/code/AssetLib/3DS/3DSHelper.h
@@ -0,0 +1,702 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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 Defines helper data structures for the import of 3DS files */
+
+#ifndef AI_3DSFILEHELPER_H_INC
+#define AI_3DSFILEHELPER_H_INC
+
+#include <assimp/SmoothingGroups.h>
+#include <assimp/SpatialSort.h>
+#include <assimp/StringUtils.h>
+#include <assimp/anim.h>
+#include <assimp/camera.h>
+#include <assimp/light.h>
+#include <assimp/material.h>
+#include <assimp/qnan.h>
+#include <cstdio> //sprintf
+
+namespace Assimp {
+namespace D3DS {
+
+#include <assimp/Compiler/pushpack1.h>
+
+// ---------------------------------------------------------------------------
+/** Defines chunks and data structures.
+*/
+namespace Discreet3DS {
+
+ //! data structure for a single chunk in a .3ds file
+ struct Chunk {
+ uint16_t Flag;
+ uint32_t Size;
+ } PACK_STRUCT;
+
+ //! Used for shading field in material3ds structure
+ //! From AutoDesk 3ds SDK
+ typedef enum {
+ // translated to gouraud shading with wireframe active
+ Wire = 0x0,
+
+ // if this material is set, no vertex normals will
+ // be calculated for the model. Face normals + gouraud
+ Flat = 0x1,
+
+ // standard gouraud shading
+ Gouraud = 0x2,
+
+ // phong shading
+ Phong = 0x3,
+
+ // cooktorrance or anistropic phong shading ...
+ // the exact meaning is unknown, if you know it
+ // feel free to tell me ;-)
+ Metal = 0x4,
+
+ // required by the ASE loader
+ Blinn = 0x5
+ } shadetype3ds;
+
+ // Flags for animated keys
+ enum {
+ KEY_USE_TENS = 0x1,
+ KEY_USE_CONT = 0x2,
+ KEY_USE_BIAS = 0x4,
+ KEY_USE_EASE_TO = 0x8,
+ KEY_USE_EASE_FROM = 0x10
+ };
+
+ enum {
+
+ // ********************************************************************
+ // Basic chunks which can be found everywhere in the file
+ CHUNK_VERSION = 0x0002,
+ CHUNK_RGBF = 0x0010, // float4 R; float4 G; float4 B
+ CHUNK_RGBB = 0x0011, // int1 R; int1 G; int B
+
+ // Linear color values (gamma = 2.2?)
+ CHUNK_LINRGBF = 0x0013, // float4 R; float4 G; float4 B
+ CHUNK_LINRGBB = 0x0012, // int1 R; int1 G; int B
+
+ CHUNK_PERCENTW = 0x0030, // int2 percentage
+ CHUNK_PERCENTF = 0x0031, // float4 percentage
+ CHUNK_PERCENTD = 0x0032, // float8 percentage
+ // ********************************************************************
+
+ // Prj master chunk
+ CHUNK_PRJ = 0xC23D,
+
+ // MDLI master chunk
+ CHUNK_MLI = 0x3DAA,
+
+ // Primary main chunk of the .3ds file
+ CHUNK_MAIN = 0x4D4D,
+
+ // Mesh main chunk
+ CHUNK_OBJMESH = 0x3D3D,
+
+ // Specifies the background color of the .3ds file
+ // This is passed through the material system for
+ // viewing purposes.
+ CHUNK_BKGCOLOR = 0x1200,
+
+ // Specifies the ambient base color of the scene.
+ // This is added to all materials in the file
+ CHUNK_AMBCOLOR = 0x2100,
+
+ // Specifies the background image for the whole scene
+ // This value is passed through the material system
+ // to the viewer
+ CHUNK_BIT_MAP = 0x1100,
+ CHUNK_BIT_MAP_EXISTS = 0x1101,
+
+ // ********************************************************************
+ // Viewport related stuff. Ignored
+ CHUNK_DEFAULT_VIEW = 0x3000,
+ CHUNK_VIEW_TOP = 0x3010,
+ CHUNK_VIEW_BOTTOM = 0x3020,
+ CHUNK_VIEW_LEFT = 0x3030,
+ CHUNK_VIEW_RIGHT = 0x3040,
+ CHUNK_VIEW_FRONT = 0x3050,
+ CHUNK_VIEW_BACK = 0x3060,
+ CHUNK_VIEW_USER = 0x3070,
+ CHUNK_VIEW_CAMERA = 0x3080,
+ // ********************************************************************
+
+ // Mesh chunks
+ CHUNK_OBJBLOCK = 0x4000,
+ CHUNK_TRIMESH = 0x4100,
+ CHUNK_VERTLIST = 0x4110,
+ CHUNK_VERTFLAGS = 0x4111,
+ CHUNK_FACELIST = 0x4120,
+ CHUNK_FACEMAT = 0x4130,
+ CHUNK_MAPLIST = 0x4140,
+ CHUNK_SMOOLIST = 0x4150,
+ CHUNK_TRMATRIX = 0x4160,
+ CHUNK_MESHCOLOR = 0x4165,
+ CHUNK_TXTINFO = 0x4170,
+ CHUNK_LIGHT = 0x4600,
+ CHUNK_CAMERA = 0x4700,
+ CHUNK_HIERARCHY = 0x4F00,
+
+ // Specifies the global scaling factor. This is applied
+ // to the root node's transformation matrix
+ CHUNK_MASTER_SCALE = 0x0100,
+
+ // ********************************************************************
+ // Material chunks
+ CHUNK_MAT_MATERIAL = 0xAFFF,
+
+ // asciiz containing the name of the material
+ CHUNK_MAT_MATNAME = 0xA000,
+ CHUNK_MAT_AMBIENT = 0xA010, // followed by color chunk
+ CHUNK_MAT_DIFFUSE = 0xA020, // followed by color chunk
+ CHUNK_MAT_SPECULAR = 0xA030, // followed by color chunk
+
+ // Specifies the shininess of the material
+ // followed by percentage chunk
+ CHUNK_MAT_SHININESS = 0xA040,
+ CHUNK_MAT_SHININESS_PERCENT = 0xA041,
+
+ // Specifies the shading mode to be used
+ // followed by a short
+ CHUNK_MAT_SHADING = 0xA100,
+
+ // NOTE: Emissive color (self illumination) seems not
+ // to be a color but a single value, type is unknown.
+ // Make the parser accept both of them.
+ // followed by percentage chunk (?)
+ CHUNK_MAT_SELF_ILLUM = 0xA080,
+
+ // Always followed by percentage chunk (?)
+ CHUNK_MAT_SELF_ILPCT = 0xA084,
+
+ // Always followed by percentage chunk
+ CHUNK_MAT_TRANSPARENCY = 0xA050,
+
+ // Diffuse texture channel 0
+ CHUNK_MAT_TEXTURE = 0xA200,
+
+ // Contains opacity information for each texel
+ CHUNK_MAT_OPACMAP = 0xA210,
+
+ // Contains a reflection map to be used to reflect
+ // the environment. This is partially supported.
+ CHUNK_MAT_REFLMAP = 0xA220,
+
+ // Self Illumination map (emissive colors)
+ CHUNK_MAT_SELFIMAP = 0xA33d,
+
+ // Bumpmap. Not specified whether it is a heightmap
+ // or a normal map. Assme it is a heightmap since
+ // artist normally prefer this format.
+ CHUNK_MAT_BUMPMAP = 0xA230,
+
+ // Specular map. Seems to influence the specular color
+ CHUNK_MAT_SPECMAP = 0xA204,
+
+ // Holds shininess data.
+ CHUNK_MAT_MAT_SHINMAP = 0xA33C,
+
+ // Scaling in U/V direction.
+ // (need to gen separate UV coordinate set
+ // and do this by hand)
+ CHUNK_MAT_MAP_USCALE = 0xA354,
+ CHUNK_MAT_MAP_VSCALE = 0xA356,
+
+ // Translation in U/V direction.
+ // (need to gen separate UV coordinate set
+ // and do this by hand)
+ CHUNK_MAT_MAP_UOFFSET = 0xA358,
+ CHUNK_MAT_MAP_VOFFSET = 0xA35a,
+
+ // UV-coordinates rotation around the z-axis
+ // Assumed to be in radians.
+ CHUNK_MAT_MAP_ANG = 0xA35C,
+
+ // Tiling flags for 3DS files
+ CHUNK_MAT_MAP_TILING = 0xa351,
+
+ // Specifies the file name of a texture
+ CHUNK_MAPFILE = 0xA300,
+
+ // Specifies whether a material requires two-sided rendering
+ CHUNK_MAT_TWO_SIDE = 0xA081,
+ // ********************************************************************
+
+ // Main keyframer chunk. Contains translation/rotation/scaling data
+ CHUNK_KEYFRAMER = 0xB000,
+
+ // Supported sub chunks
+ CHUNK_TRACKINFO = 0xB002,
+ CHUNK_TRACKOBJNAME = 0xB010,
+ CHUNK_TRACKDUMMYOBJNAME = 0xB011,
+ CHUNK_TRACKPIVOT = 0xB013,
+ CHUNK_TRACKPOS = 0xB020,
+ CHUNK_TRACKROTATE = 0xB021,
+ CHUNK_TRACKSCALE = 0xB022,
+
+ // ********************************************************************
+ // Keyframes for various other stuff in the file
+ // Partially ignored
+ CHUNK_AMBIENTKEY = 0xB001,
+ CHUNK_TRACKMORPH = 0xB026,
+ CHUNK_TRACKHIDE = 0xB029,
+ CHUNK_OBJNUMBER = 0xB030,
+ CHUNK_TRACKCAMERA = 0xB003,
+ CHUNK_TRACKFOV = 0xB023,
+ CHUNK_TRACKROLL = 0xB024,
+ CHUNK_TRACKCAMTGT = 0xB004,
+ CHUNK_TRACKLIGHT = 0xB005,
+ CHUNK_TRACKLIGTGT = 0xB006,
+ CHUNK_TRACKSPOTL = 0xB007,
+ CHUNK_FRAMES = 0xB008,
+ // ********************************************************************
+
+ // light sub-chunks
+ CHUNK_DL_OFF = 0x4620,
+ CHUNK_DL_OUTER_RANGE = 0x465A,
+ CHUNK_DL_INNER_RANGE = 0x4659,
+ CHUNK_DL_MULTIPLIER = 0x465B,
+ CHUNK_DL_EXCLUDE = 0x4654,
+ CHUNK_DL_ATTENUATE = 0x4625,
+ CHUNK_DL_SPOTLIGHT = 0x4610,
+
+ // camera sub-chunks
+ CHUNK_CAM_RANGES = 0x4720
+ };
+}
+
+// ---------------------------------------------------------------------------
+/** Helper structure representing a 3ds mesh face */
+struct Face : public FaceWithSmoothingGroup {
+};
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4315)
+#endif // _MSC_VER
+
+// ---------------------------------------------------------------------------
+/** Helper structure representing a texture */
+struct Texture {
+ //! Default constructor
+ Texture() AI_NO_EXCEPT
+ : mTextureBlend(0.0f),
+ mMapName(),
+ mOffsetU(0.0),
+ mOffsetV(0.0),
+ mScaleU(1.0),
+ mScaleV(1.0),
+ mRotation(0.0),
+ mMapMode(aiTextureMapMode_Wrap),
+ bPrivate(),
+ iUVSrc(0) {
+ mTextureBlend = get_qnan();
+ }
+
+ Texture(const Texture &other) :
+ mTextureBlend(other.mTextureBlend),
+ mMapName(other.mMapName),
+ mOffsetU(other.mOffsetU),
+ mOffsetV(other.mOffsetV),
+ mScaleU(other.mScaleU),
+ mScaleV(other.mScaleV),
+ mRotation(other.mRotation),
+ mMapMode(other.mMapMode),
+ bPrivate(other.bPrivate),
+ iUVSrc(other.iUVSrc) {
+ // empty
+ }
+
+ Texture(Texture &&other) AI_NO_EXCEPT : mTextureBlend(other.mTextureBlend),
+ mMapName(std::move(other.mMapName)),
+ mOffsetU(other.mOffsetU),
+ mOffsetV(other.mOffsetV),
+ mScaleU(other.mScaleU),
+ mScaleV(other.mScaleV),
+ mRotation(other.mRotation),
+ mMapMode(other.mMapMode),
+ bPrivate(other.bPrivate),
+ iUVSrc(other.iUVSrc) {
+ // empty
+ }
+
+ Texture &operator=(Texture &&other) AI_NO_EXCEPT {
+ if (this == &other) {
+ return *this;
+ }
+
+ mTextureBlend = other.mTextureBlend;
+ mMapName = std::move(other.mMapName);
+ mOffsetU = other.mOffsetU;
+ mOffsetV = other.mOffsetV;
+ mScaleU = other.mScaleU;
+ mScaleV = other.mScaleV;
+ mRotation = other.mRotation;
+ mMapMode = other.mMapMode;
+ bPrivate = other.bPrivate;
+ iUVSrc = other.iUVSrc;
+
+ return *this;
+ }
+
+ //! Specifies the blend factor for the texture
+ ai_real mTextureBlend;
+
+ //! Specifies the filename of the texture
+ std::string mMapName;
+
+ //! Specifies texture coordinate offsets/scaling/rotations
+ ai_real mOffsetU;
+ ai_real mOffsetV;
+ ai_real mScaleU;
+ ai_real mScaleV;
+ ai_real mRotation;
+
+ //! Specifies the mapping mode to be used for the texture
+ aiTextureMapMode mMapMode;
+
+ //! Used internally
+ bool bPrivate;
+ int iUVSrc;
+};
+
+#include <assimp/Compiler/poppack1.h>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif // _MSC_VER
+
+// ---------------------------------------------------------------------------
+/** Helper structure representing a 3ds material */
+struct Material {
+ //! Default constructor has been deleted
+ Material() :
+ mName(),
+ mDiffuse(ai_real(0.6), ai_real(0.6), ai_real(0.6)),
+ mSpecularExponent(ai_real(0.0)),
+ mShininessStrength(ai_real(1.0)),
+ mShading(Discreet3DS::Gouraud),
+ mTransparency(ai_real(1.0)),
+ mBumpHeight(ai_real(1.0)),
+ mTwoSided(false) {
+ // empty
+ }
+
+ //! Constructor with explicit name
+ explicit Material(const std::string &name) :
+ mName(name),
+ mDiffuse(ai_real(0.6), ai_real(0.6), ai_real(0.6)),
+ mSpecularExponent(ai_real(0.0)),
+ mShininessStrength(ai_real(1.0)),
+ mShading(Discreet3DS::Gouraud),
+ mTransparency(ai_real(1.0)),
+ mBumpHeight(ai_real(1.0)),
+ mTwoSided(false) {
+ // empty
+ }
+
+ Material(const Material &other) :
+ mName(other.mName),
+ mDiffuse(other.mDiffuse),
+ mSpecularExponent(other.mSpecularExponent),
+ mShininessStrength(other.mShininessStrength),
+ mSpecular(other.mSpecular),
+ mAmbient(other.mAmbient),
+ mShading(other.mShading),
+ mTransparency(other.mTransparency),
+ sTexDiffuse(other.sTexDiffuse),
+ sTexOpacity(other.sTexOpacity),
+ sTexSpecular(other.sTexSpecular),
+ sTexReflective(other.sTexReflective),
+ sTexBump(other.sTexBump),
+ sTexEmissive(other.sTexEmissive),
+ sTexShininess(other.sTexShininess),
+ mBumpHeight(other.mBumpHeight),
+ mEmissive(other.mEmissive),
+ sTexAmbient(other.sTexAmbient),
+ mTwoSided(other.mTwoSided) {
+ // empty
+ }
+
+ //! Move constructor. This is explicitly written because MSVC doesn't support defaulting it
+ Material(Material &&other) AI_NO_EXCEPT : mName(std::move(other.mName)),
+ mDiffuse(other.mDiffuse),
+ mSpecularExponent(other.mSpecularExponent),
+ mShininessStrength(other.mShininessStrength),
+ mSpecular(other.mSpecular),
+ mAmbient(other.mAmbient),
+ mShading(other.mShading),
+ mTransparency(other.mTransparency),
+ sTexDiffuse(std::move(other.sTexDiffuse)),
+ sTexOpacity(std::move(other.sTexOpacity)),
+ sTexSpecular(std::move(other.sTexSpecular)),
+ sTexReflective(std::move(other.sTexReflective)),
+ sTexBump(std::move(other.sTexBump)),
+ sTexEmissive(std::move(other.sTexEmissive)),
+ sTexShininess(std::move(other.sTexShininess)),
+ mBumpHeight(other.mBumpHeight),
+ mEmissive(other.mEmissive),
+ sTexAmbient(std::move(other.sTexAmbient)),
+ mTwoSided(other.mTwoSided) {
+ // empty
+ }
+
+ Material &operator=(Material &&other) AI_NO_EXCEPT {
+ if (this == &other) {
+ return *this;
+ }
+
+ mName = std::move(other.mName);
+ mDiffuse = other.mDiffuse;
+ mSpecularExponent = other.mSpecularExponent;
+ mShininessStrength = other.mShininessStrength,
+ mSpecular = other.mSpecular;
+ mAmbient = other.mAmbient;
+ mShading = other.mShading;
+ mTransparency = other.mTransparency;
+ sTexDiffuse = std::move(other.sTexDiffuse);
+ sTexOpacity = std::move(other.sTexOpacity);
+ sTexSpecular = std::move(other.sTexSpecular);
+ sTexReflective = std::move(other.sTexReflective);
+ sTexBump = std::move(other.sTexBump);
+ sTexEmissive = std::move(other.sTexEmissive);
+ sTexShininess = std::move(other.sTexShininess);
+ mBumpHeight = other.mBumpHeight;
+ mEmissive = other.mEmissive;
+ sTexAmbient = std::move(other.sTexAmbient);
+ mTwoSided = other.mTwoSided;
+
+ return *this;
+ }
+
+ virtual ~Material() {
+ // empty
+ }
+
+ //! Name of the material
+ std::string mName;
+ //! Diffuse color of the material
+ aiColor3D mDiffuse;
+ //! Specular exponent
+ ai_real mSpecularExponent;
+ //! Shininess strength, in percent
+ ai_real mShininessStrength;
+ //! Specular color of the material
+ aiColor3D mSpecular;
+ //! Ambient color of the material
+ aiColor3D mAmbient;
+ //! Shading type to be used
+ Discreet3DS::shadetype3ds mShading;
+ //! Opacity of the material
+ ai_real mTransparency;
+ //! Diffuse texture channel
+ Texture sTexDiffuse;
+ //! Opacity texture channel
+ Texture sTexOpacity;
+ //! Specular texture channel
+ Texture sTexSpecular;
+ //! Reflective texture channel
+ Texture sTexReflective;
+ //! Bump texture channel
+ Texture sTexBump;
+ //! Emissive texture channel
+ Texture sTexEmissive;
+ //! Shininess texture channel
+ Texture sTexShininess;
+ //! Scaling factor for the bump values
+ ai_real mBumpHeight;
+ //! Emissive color
+ aiColor3D mEmissive;
+ //! Ambient texture channel
+ //! (used by the ASE format)
+ Texture sTexAmbient;
+ //! True if the material must be rendered from two sides
+ bool mTwoSided;
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure to represent a 3ds file mesh */
+struct Mesh : public MeshWithSmoothingGroups<D3DS::Face> {
+ //! Default constructor has been deleted
+ Mesh() = delete;
+
+ //! Constructor with explicit name
+ explicit Mesh(const std::string &name) :
+ mName(name) {
+ }
+
+ //! Name of the mesh
+ std::string mName;
+
+ //! Texture coordinates
+ std::vector<aiVector3D> mTexCoords;
+
+ //! Face materials
+ std::vector<unsigned int> mFaceMaterials;
+
+ //! Local transformation matrix
+ aiMatrix4x4 mMat;
+};
+
+// ---------------------------------------------------------------------------
+/** Float key - quite similar to aiVectorKey and aiQuatKey. Both are in the
+ C-API, so it would be difficult to make them a template. */
+struct aiFloatKey {
+ double mTime; ///< The time of this key
+ ai_real mValue; ///< The value of this key
+
+#ifdef __cplusplus
+
+ // time is not compared
+ bool operator==(const aiFloatKey &o) const { return o.mValue == this->mValue; }
+
+ bool operator!=(const aiFloatKey &o) const { return o.mValue != this->mValue; }
+
+ // Only time is compared. This operator is defined
+ // for use with std::sort
+ bool operator<(const aiFloatKey &o) const { return mTime < o.mTime; }
+
+ bool operator>(const aiFloatKey &o) const { return mTime > o.mTime; }
+
+#endif
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure to represent a 3ds file node */
+struct Node {
+ Node() = delete;
+
+ explicit Node(const std::string &name) :
+ mParent(nullptr),
+ mName(name),
+ mInstanceNumber(0),
+ mHierarchyPos(0),
+ mHierarchyIndex(0),
+ mInstanceCount(1) {
+ aRotationKeys.reserve(20);
+ aPositionKeys.reserve(20);
+ aScalingKeys.reserve(20);
+ }
+
+ ~Node() {
+ for (unsigned int i = 0; i < mChildren.size(); ++i)
+ delete mChildren[i];
+ }
+
+ //! Pointer to the parent node
+ Node *mParent;
+
+ //! Holds all child nodes
+ std::vector<Node *> mChildren;
+
+ //! Name of the node
+ std::string mName;
+
+ //! InstanceNumber of the node
+ int32_t mInstanceNumber;
+
+ //! Dummy nodes: real name to be combined with the $$$DUMMY
+ std::string mDummyName;
+
+ //! Position of the node in the hierarchy (tree depth)
+ int16_t mHierarchyPos;
+
+ //! Index of the node
+ int16_t mHierarchyIndex;
+
+ //! Rotation keys loaded from the file
+ std::vector<aiQuatKey> aRotationKeys;
+
+ //! Position keys loaded from the file
+ std::vector<aiVectorKey> aPositionKeys;
+
+ //! Scaling keys loaded from the file
+ std::vector<aiVectorKey> aScalingKeys;
+
+ // For target lights (spot lights and directional lights):
+ // The position of the target
+ std::vector<aiVectorKey> aTargetPositionKeys;
+
+ // For cameras: the camera roll angle
+ std::vector<aiFloatKey> aCameraRollKeys;
+
+ //! Pivot position loaded from the file
+ aiVector3D vPivot;
+
+ //instance count, will be kept only for the first node
+ int32_t mInstanceCount;
+
+ //! Add a child node, setup the right parent node for it
+ //! \param pc Node to be 'adopted'
+ inline Node &push_back(Node *pc) {
+ mChildren.push_back(pc);
+ pc->mParent = this;
+ return *this;
+ }
+};
+// ---------------------------------------------------------------------------
+/** Helper structure analogue to aiScene */
+struct Scene {
+ //! List of all materials loaded
+ //! NOTE: 3ds references materials globally
+ std::vector<Material> mMaterials;
+
+ //! List of all meshes loaded
+ std::vector<Mesh> mMeshes;
+
+ //! List of all cameras loaded
+ std::vector<aiCamera *> mCameras;
+
+ //! List of all lights loaded
+ std::vector<aiLight *> mLights;
+
+ //! Pointer to the root node of the scene
+ // --- moved to main class
+ // Node* pcRootNode;
+};
+
+} // end of namespace D3DS
+} // end of namespace Assimp
+
+#endif // AI_XFILEHELPER_H_INC
diff --git a/libs/assimp/code/AssetLib/3DS/3DSLoader.cpp b/libs/assimp/code/AssetLib/3DS/3DSLoader.cpp
new file mode 100644
index 0000000..0ec8b87
--- /dev/null
+++ b/libs/assimp/code/AssetLib/3DS/3DSLoader.cpp
@@ -0,0 +1,1336 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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 3DSLoader.cpp
+ * @brief Implementation of the 3ds importer class
+ *
+ * http://www.the-labs.com/Blender/3DS-details.html
+ */
+
+#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
+
+#include "3DSLoader.h"
+#include <assimp/StringComparison.h>
+#include <assimp/importerdesc.h>
+#include <assimp/scene.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/IOSystem.hpp>
+
+using namespace Assimp;
+
+static const aiImporterDesc desc = {
+ "Discreet 3DS Importer",
+ "",
+ "",
+ "Limited animation support",
+ aiImporterFlags_SupportBinaryFlavour,
+ 0,
+ 0,
+ 0,
+ 0,
+ "3ds prj"
+};
+
+// ------------------------------------------------------------------------------------------------
+// Begins a new parsing block
+// - Reads the current chunk and validates it
+// - computes its length
+#define ASSIMP_3DS_BEGIN_CHUNK() \
+ while (true) { \
+ if (stream->GetRemainingSizeToLimit() < sizeof(Discreet3DS::Chunk)) { \
+ return; \
+ } \
+ Discreet3DS::Chunk chunk; \
+ ReadChunk(&chunk); \
+ int chunkSize = chunk.Size - sizeof(Discreet3DS::Chunk); \
+ if (chunkSize <= 0) \
+ continue; \
+ const unsigned int oldReadLimit = stream->SetReadLimit( \
+ stream->GetCurrentPos() + chunkSize);
+
+// ------------------------------------------------------------------------------------------------
+// End a parsing block
+// Must follow at the end of each parsing block, reset chunk end marker to previous value
+#define ASSIMP_3DS_END_CHUNK() \
+ stream->SkipToReadLimit(); \
+ stream->SetReadLimit(oldReadLimit); \
+ if (stream->GetRemainingSizeToLimit() == 0) \
+ return; \
+ }
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+Discreet3DSImporter::Discreet3DSImporter() :
+ stream(), mLastNodeIndex(), mCurrentNode(), mRootNode(), mScene(), mMasterScale(), bHasBG(), bIsPrj() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+Discreet3DSImporter::~Discreet3DSImporter() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool Discreet3DSImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
+ static const uint16_t token[] = { 0x4d4d, 0x3dc2 /*, 0x3daa */ };
+ return CheckMagicToken(pIOHandler, pFile, token, AI_COUNT_OF(token), 0, sizeof token[0]);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Loader registry entry
+const aiImporterDesc *Discreet3DSImporter::GetInfo() const {
+ return &desc;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties
+void Discreet3DSImporter::SetupProperties(const Importer * /*pImp*/) {
+ // nothing to be done for the moment
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void Discreet3DSImporter::InternReadFile(const std::string &pFile,
+ aiScene *pScene, IOSystem *pIOHandler) {
+
+ auto theFile = pIOHandler->Open(pFile, "rb");
+ if (!theFile) {
+ throw DeadlyImportError("3DS: Could not open ", pFile);
+ }
+
+ StreamReaderLE theStream(theFile);
+
+ // We should have at least one chunk
+ if (theStream.GetRemainingSize() < 16) {
+ throw DeadlyImportError("3DS file is either empty or corrupt: ", pFile);
+ }
+ this->stream = &theStream;
+
+ // Allocate our temporary 3DS representation
+ D3DS::Scene _scene;
+ mScene = &_scene;
+
+ // Initialize members
+ D3DS::Node _rootNode("UNNAMED");
+ mLastNodeIndex = -1;
+ mCurrentNode = &_rootNode;
+ mRootNode = mCurrentNode;
+ mRootNode->mHierarchyPos = -1;
+ mRootNode->mHierarchyIndex = -1;
+ mRootNode->mParent = nullptr;
+ mMasterScale = 1.0f;
+ mBackgroundImage = std::string();
+ bHasBG = false;
+ bIsPrj = false;
+
+ // Parse the file
+ ParseMainChunk();
+
+ // Process all meshes in the file. First check whether all
+ // face indices have valid values. The generate our
+ // internal verbose representation. Finally compute normal
+ // vectors from the smoothing groups we read from the
+ // file.
+ for (auto &mesh : mScene->mMeshes) {
+ if (mesh.mFaces.size() > 0 && mesh.mPositions.size() == 0) {
+ throw DeadlyImportError("3DS file contains faces but no vertices: ", pFile);
+ }
+ CheckIndices(mesh);
+ MakeUnique(mesh);
+ ComputeNormalsWithSmoothingsGroups<D3DS::Face>(mesh);
+ }
+
+ // Replace all occurrences of the default material with a
+ // valid material. Generate it if no material containing
+ // DEFAULT in its name has been found in the file
+ ReplaceDefaultMaterial();
+
+ // Convert the scene from our internal representation to an
+ // aiScene object. This involves copying all meshes, lights
+ // and cameras to the scene
+ ConvertScene(pScene);
+
+ // Generate the node graph for the scene. This is a little bit
+ // tricky since we'll need to split some meshes into sub-meshes
+ GenerateNodeGraph(pScene);
+
+ // Now apply the master scaling factor to the scene
+ ApplyMasterScale(pScene);
+
+ // Our internal scene representation and the root
+ // node will be automatically deleted, so the whole hierarchy will follow
+
+ AI_DEBUG_INVALIDATE_PTR(mRootNode);
+ AI_DEBUG_INVALIDATE_PTR(mScene);
+ AI_DEBUG_INVALIDATE_PTR(this->stream);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Applies a master-scaling factor to the imported scene
+void Discreet3DSImporter::ApplyMasterScale(aiScene *pScene) {
+ // There are some 3DS files with a zero scaling factor
+ if (!mMasterScale)
+ mMasterScale = 1.0f;
+ else
+ mMasterScale = 1.0f / mMasterScale;
+
+ // Construct an uniform scaling matrix and multiply with it
+ pScene->mRootNode->mTransformation *= aiMatrix4x4(
+ mMasterScale, 0.0f, 0.0f, 0.0f,
+ 0.0f, mMasterScale, 0.0f, 0.0f,
+ 0.0f, 0.0f, mMasterScale, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f);
+
+ // Check whether a scaling track is assigned to the root node.
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a new chunk from the file
+void Discreet3DSImporter::ReadChunk(Discreet3DS::Chunk *pcOut) {
+ ai_assert(pcOut != nullptr);
+
+ pcOut->Flag = stream->GetI2();
+ pcOut->Size = stream->GetI4();
+
+ if (pcOut->Size - sizeof(Discreet3DS::Chunk) > stream->GetRemainingSize()) {
+ throw DeadlyImportError("Chunk is too large");
+ }
+
+ if (pcOut->Size - sizeof(Discreet3DS::Chunk) > stream->GetRemainingSizeToLimit()) {
+ ASSIMP_LOG_ERROR("3DS: Chunk overflow");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Skip a chunk
+void Discreet3DSImporter::SkipChunk() {
+ Discreet3DS::Chunk psChunk;
+ ReadChunk(&psChunk);
+
+ stream->IncPtr(psChunk.Size - sizeof(Discreet3DS::Chunk));
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Process the primary chunk of the file
+void Discreet3DSImporter::ParseMainChunk() {
+ ASSIMP_3DS_BEGIN_CHUNK();
+
+ // get chunk type
+ switch (chunk.Flag) {
+
+ case Discreet3DS::CHUNK_PRJ:
+ bIsPrj = true;
+ break;
+ case Discreet3DS::CHUNK_MAIN:
+ ParseEditorChunk();
+ break;
+ };
+
+ ASSIMP_3DS_END_CHUNK();
+ // recursively continue processing this hierarchy level
+ return ParseMainChunk();
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSImporter::ParseEditorChunk() {
+ ASSIMP_3DS_BEGIN_CHUNK();
+
+ // get chunk type
+ switch (chunk.Flag) {
+ case Discreet3DS::CHUNK_OBJMESH:
+
+ ParseObjectChunk();
+ break;
+
+ // NOTE: In several documentations in the internet this
+ // chunk appears at different locations
+ case Discreet3DS::CHUNK_KEYFRAMER:
+
+ ParseKeyframeChunk();
+ break;
+
+ case Discreet3DS::CHUNK_VERSION: {
+ // print the version number
+ char buff[10];
+ ASSIMP_itoa10(buff, stream->GetI2());
+ ASSIMP_LOG_INFO("3DS file format version: ", buff);
+ } break;
+ };
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSImporter::ParseObjectChunk() {
+ ASSIMP_3DS_BEGIN_CHUNK();
+
+ // get chunk type
+ switch (chunk.Flag) {
+ case Discreet3DS::CHUNK_OBJBLOCK: {
+ unsigned int cnt = 0;
+ const char *sz = (const char *)stream->GetPtr();
+
+ // Get the name of the geometry object
+ while (stream->GetI1())
+ ++cnt;
+ ParseChunk(sz, cnt);
+ } break;
+
+ case Discreet3DS::CHUNK_MAT_MATERIAL:
+
+ // Add a new material to the list
+ mScene->mMaterials.push_back(D3DS::Material(std::string("UNNAMED_" + ai_to_string(mScene->mMaterials.size()))));
+ ParseMaterialChunk();
+ break;
+
+ case Discreet3DS::CHUNK_AMBCOLOR:
+
+ // This is the ambient base color of the scene.
+ // We add it to the ambient color of all materials
+ ParseColorChunk(&mClrAmbient, true);
+ if (is_qnan(mClrAmbient.r)) {
+ // We failed to read the ambient base color.
+ ASSIMP_LOG_ERROR("3DS: Failed to read ambient base color");
+ mClrAmbient.r = mClrAmbient.g = mClrAmbient.b = 0.0f;
+ }
+ break;
+
+ case Discreet3DS::CHUNK_BIT_MAP: {
+ // Specifies the background image. The string should already be
+ // properly 0 terminated but we need to be sure
+ unsigned int cnt = 0;
+ const char *sz = (const char *)stream->GetPtr();
+ while (stream->GetI1())
+ ++cnt;
+ mBackgroundImage = std::string(sz, cnt);
+ } break;
+
+ case Discreet3DS::CHUNK_BIT_MAP_EXISTS:
+ bHasBG = true;
+ break;
+
+ case Discreet3DS::CHUNK_MASTER_SCALE:
+ // Scene master scaling factor
+ mMasterScale = stream->GetF4();
+ break;
+ };
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSImporter::ParseChunk(const char *name, unsigned int num) {
+ ASSIMP_3DS_BEGIN_CHUNK();
+
+ // IMPLEMENTATION NOTE;
+ // 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 to be able to return valid cameras/lights even if no scenegraph is given.
+
+ // get chunk type
+ switch (chunk.Flag) {
+ case Discreet3DS::CHUNK_TRIMESH: {
+ // this starts a new triangle mesh
+ mScene->mMeshes.push_back(D3DS::Mesh(std::string(name, num)));
+
+ // Read mesh chunks
+ ParseMeshChunk();
+ } break;
+
+ case Discreet3DS::CHUNK_LIGHT: {
+ // This starts a new light
+ aiLight *light = new aiLight();
+ mScene->mLights.push_back(light);
+
+ light->mName.Set(std::string(name, num));
+
+ // First read the position of the light
+ light->mPosition.x = stream->GetF4();
+ light->mPosition.y = stream->GetF4();
+ light->mPosition.z = stream->GetF4();
+
+ light->mColorDiffuse = aiColor3D(1.f, 1.f, 1.f);
+
+ // Now check for further subchunks
+ if (!bIsPrj) /* fixme */
+ ParseLightChunk();
+
+ // The specular light color is identical the the diffuse light color. The ambient light color
+ // is equal to the ambient base color of the whole scene.
+ light->mColorSpecular = light->mColorDiffuse;
+ light->mColorAmbient = mClrAmbient;
+
+ if (light->mType == aiLightSource_UNDEFINED) {
+ // It must be a point light
+ light->mType = aiLightSource_POINT;
+ }
+ } break;
+
+ case Discreet3DS::CHUNK_CAMERA: {
+ // This starts a new camera
+ aiCamera *camera = new aiCamera();
+ mScene->mCameras.push_back(camera);
+ camera->mName.Set(std::string(name, num));
+
+ // First read the position of the camera
+ camera->mPosition.x = stream->GetF4();
+ camera->mPosition.y = stream->GetF4();
+ camera->mPosition.z = stream->GetF4();
+
+ // Then the camera target
+ camera->mLookAt.x = stream->GetF4() - camera->mPosition.x;
+ camera->mLookAt.y = stream->GetF4() - camera->mPosition.y;
+ camera->mLookAt.z = stream->GetF4() - camera->mPosition.z;
+ ai_real len = camera->mLookAt.Length();
+ if (len < 1e-5) {
+
+ // There are some files with lookat == position. Don't know why or whether it's ok or not.
+ ASSIMP_LOG_ERROR("3DS: Unable to read proper camera look-at vector");
+ camera->mLookAt = aiVector3D(0.0, 1.0, 0.0);
+
+ } else
+ camera->mLookAt /= len;
+
+ // And finally - the camera rotation angle, in counter clockwise direction
+ const ai_real angle = AI_DEG_TO_RAD(stream->GetF4());
+ aiQuaternion quat(camera->mLookAt, angle);
+ camera->mUp = quat.GetMatrix() * aiVector3D(0.0, 1.0, 0.0);
+
+ // Read the lense angle
+ camera->mHorizontalFOV = AI_DEG_TO_RAD(stream->GetF4());
+ if (camera->mHorizontalFOV < 0.001f) {
+ camera->mHorizontalFOV = float(AI_DEG_TO_RAD(45.f));
+ }
+
+ // Now check for further subchunks
+ if (!bIsPrj) /* fixme */ {
+ ParseCameraChunk();
+ }
+ } break;
+ };
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSImporter::ParseLightChunk() {
+ ASSIMP_3DS_BEGIN_CHUNK();
+ aiLight *light = mScene->mLights.back();
+
+ // get chunk type
+ switch (chunk.Flag) {
+ case Discreet3DS::CHUNK_DL_SPOTLIGHT:
+ // Now we can be sure that the light is a spot light
+ light->mType = aiLightSource_SPOT;
+
+ // We wouldn't need to normalize here, but we do it
+ light->mDirection.x = stream->GetF4() - light->mPosition.x;
+ light->mDirection.y = stream->GetF4() - light->mPosition.y;
+ light->mDirection.z = stream->GetF4() - light->mPosition.z;
+ light->mDirection.Normalize();
+
+ // Now the hotspot and falloff angles - in degrees
+ light->mAngleInnerCone = AI_DEG_TO_RAD(stream->GetF4());
+
+ // FIX: the falloff angle is just an offset
+ light->mAngleOuterCone = light->mAngleInnerCone + AI_DEG_TO_RAD(stream->GetF4());
+ break;
+
+ // intensity multiplier
+ case Discreet3DS::CHUNK_DL_MULTIPLIER:
+ light->mColorDiffuse = light->mColorDiffuse * stream->GetF4();
+ break;
+
+ // light color
+ case Discreet3DS::CHUNK_RGBF:
+ case Discreet3DS::CHUNK_LINRGBF:
+ light->mColorDiffuse.r *= stream->GetF4();
+ light->mColorDiffuse.g *= stream->GetF4();
+ light->mColorDiffuse.b *= stream->GetF4();
+ break;
+
+ // light attenuation
+ case Discreet3DS::CHUNK_DL_ATTENUATE:
+ light->mAttenuationLinear = stream->GetF4();
+ break;
+ };
+
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSImporter::ParseCameraChunk() {
+ ASSIMP_3DS_BEGIN_CHUNK();
+ aiCamera *camera = mScene->mCameras.back();
+
+ // get chunk type
+ switch (chunk.Flag) {
+ // near and far clip plane
+ case Discreet3DS::CHUNK_CAM_RANGES:
+ camera->mClipPlaneNear = stream->GetF4();
+ camera->mClipPlaneFar = stream->GetF4();
+ break;
+ }
+
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSImporter::ParseKeyframeChunk() {
+ ASSIMP_3DS_BEGIN_CHUNK();
+
+ // get chunk type
+ switch (chunk.Flag) {
+ case Discreet3DS::CHUNK_TRACKCAMTGT:
+ case Discreet3DS::CHUNK_TRACKSPOTL:
+ case Discreet3DS::CHUNK_TRACKCAMERA:
+ case Discreet3DS::CHUNK_TRACKINFO:
+ case Discreet3DS::CHUNK_TRACKLIGHT:
+ case Discreet3DS::CHUNK_TRACKLIGTGT:
+
+ // this starts a new mesh hierarchy chunk
+ ParseHierarchyChunk(chunk.Flag);
+ break;
+ };
+
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Little helper function for ParseHierarchyChunk
+void Discreet3DSImporter::InverseNodeSearch(D3DS::Node *pcNode, D3DS::Node *pcCurrent) {
+ if (!pcCurrent) {
+ mRootNode->push_back(pcNode);
+ return;
+ }
+
+ if (pcCurrent->mHierarchyPos == pcNode->mHierarchyPos) {
+ if (pcCurrent->mParent) {
+ pcCurrent->mParent->push_back(pcNode);
+ } else
+ pcCurrent->push_back(pcNode);
+ return;
+ }
+ return InverseNodeSearch(pcNode, pcCurrent->mParent);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Find a node with a specific name in the import hierarchy
+D3DS::Node *FindNode(D3DS::Node *root, const std::string &name) {
+ if (root->mName == name) {
+ return root;
+ }
+
+ for (std::vector<D3DS::Node *>::iterator it = root->mChildren.begin(); it != root->mChildren.end(); ++it) {
+ D3DS::Node *nd = FindNode(*it, name);
+ if (nullptr != nd) {
+ return nd;
+ }
+ }
+
+ return nullptr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Binary predicate for std::unique()
+template <class T>
+bool KeyUniqueCompare(const T &first, const T &second) {
+ return first.mTime == second.mTime;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Skip some additional import data.
+void Discreet3DSImporter::SkipTCBInfo() {
+ unsigned int flags = stream->GetI2();
+
+ if (!flags) {
+ // Currently we can't do anything with these values. They occur
+ // quite rare, so it wouldn't be worth the effort implementing
+ // them. 3DS is not really suitable for complex animations,
+ // so full support is not required.
+ ASSIMP_LOG_WARN("3DS: Skipping TCB animation info");
+ }
+
+ if (flags & Discreet3DS::KEY_USE_TENS) {
+ stream->IncPtr(4);
+ }
+ if (flags & Discreet3DS::KEY_USE_BIAS) {
+ stream->IncPtr(4);
+ }
+ if (flags & Discreet3DS::KEY_USE_CONT) {
+ stream->IncPtr(4);
+ }
+ if (flags & Discreet3DS::KEY_USE_EASE_FROM) {
+ stream->IncPtr(4);
+ }
+ if (flags & Discreet3DS::KEY_USE_EASE_TO) {
+ stream->IncPtr(4);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read hierarchy and keyframe info
+void Discreet3DSImporter::ParseHierarchyChunk(uint16_t parent) {
+ ASSIMP_3DS_BEGIN_CHUNK();
+
+ // get chunk type
+ switch (chunk.Flag) {
+ case Discreet3DS::CHUNK_TRACKOBJNAME:
+
+ // This is the name of the object to which the track applies. The chunk also
+ // defines the position of this object in the hierarchy.
+ {
+
+ // First of all: get the name of the object
+ unsigned int cnt = 0;
+ const char *sz = (const char *)stream->GetPtr();
+
+ while (stream->GetI1())
+ ++cnt;
+ std::string name = std::string(sz, cnt);
+
+ // Now find out whether we have this node already (target animation channels
+ // are stored with a separate object ID)
+ D3DS::Node *pcNode = FindNode(mRootNode, name);
+ int instanceNumber = 1;
+
+ if (pcNode) {
+ // if the source is not a CHUNK_TRACKINFO block it won't be an object instance
+ if (parent != Discreet3DS::CHUNK_TRACKINFO) {
+ mCurrentNode = pcNode;
+ break;
+ }
+ pcNode->mInstanceCount++;
+ instanceNumber = pcNode->mInstanceCount;
+ }
+ pcNode = new D3DS::Node(name);
+ pcNode->mInstanceNumber = instanceNumber;
+
+ // There are two unknown values which we can safely ignore
+ stream->IncPtr(4);
+
+ // Now read the hierarchy position of the object
+ uint16_t hierarchy = stream->GetI2() + 1;
+ pcNode->mHierarchyPos = hierarchy;
+ pcNode->mHierarchyIndex = mLastNodeIndex;
+
+ // And find a proper position in the graph for it
+ if (mCurrentNode && mCurrentNode->mHierarchyPos == hierarchy) {
+
+ // add to the parent of the last touched node
+ mCurrentNode->mParent->push_back(pcNode);
+ mLastNodeIndex++;
+ } else if (hierarchy >= mLastNodeIndex) {
+
+ // place it at the current position in the hierarchy
+ mCurrentNode->push_back(pcNode);
+ mLastNodeIndex = hierarchy;
+ } else {
+ // need to go back to the specified position in the hierarchy.
+ InverseNodeSearch(pcNode, mCurrentNode);
+ mLastNodeIndex++;
+ }
+ // Make this node the current node
+ mCurrentNode = pcNode;
+ }
+ break;
+
+ case Discreet3DS::CHUNK_TRACKDUMMYOBJNAME:
+
+ // This is the "real" name of a $$$DUMMY object
+ {
+ const char *sz = (const char *)stream->GetPtr();
+ while (stream->GetI1())
+ ;
+
+ // If object name is DUMMY, take this one instead
+ if (mCurrentNode->mName == "$$$DUMMY") {
+ mCurrentNode->mName = std::string(sz);
+ break;
+ }
+ }
+ break;
+
+ case Discreet3DS::CHUNK_TRACKPIVOT:
+
+ if (Discreet3DS::CHUNK_TRACKINFO != parent) {
+ ASSIMP_LOG_WARN("3DS: Skipping pivot subchunk for non usual object");
+ break;
+ }
+
+ // Pivot = origin of rotation and scaling
+ mCurrentNode->vPivot.x = stream->GetF4();
+ mCurrentNode->vPivot.y = stream->GetF4();
+ mCurrentNode->vPivot.z = stream->GetF4();
+ break;
+
+ // ////////////////////////////////////////////////////////////////////
+ // POSITION KEYFRAME
+ case Discreet3DS::CHUNK_TRACKPOS: {
+ stream->IncPtr(10);
+ const unsigned int numFrames = stream->GetI4();
+ bool sortKeys = false;
+
+ // This could also be meant as the target position for
+ // (targeted) lights and cameras
+ std::vector<aiVectorKey> *l;
+ if (Discreet3DS::CHUNK_TRACKCAMTGT == parent || Discreet3DS::CHUNK_TRACKLIGTGT == parent) {
+ l = &mCurrentNode->aTargetPositionKeys;
+ } else
+ l = &mCurrentNode->aPositionKeys;
+
+ l->reserve(numFrames);
+ for (unsigned int i = 0; i < numFrames; ++i) {
+ const unsigned int fidx = stream->GetI4();
+
+ // Setup a new position key
+ aiVectorKey v;
+ v.mTime = (double)fidx;
+
+ SkipTCBInfo();
+ v.mValue.x = stream->GetF4();
+ v.mValue.y = stream->GetF4();
+ v.mValue.z = stream->GetF4();
+
+ // check whether we'll need to sort the keys
+ if (!l->empty() && v.mTime <= l->back().mTime)
+ sortKeys = true;
+
+ // Add the new keyframe to the list
+ l->push_back(v);
+ }
+
+ // Sort all keys with ascending time values and remove duplicates?
+ if (sortKeys) {
+ std::stable_sort(l->begin(), l->end());
+ l->erase(std::unique(l->begin(), l->end(), &KeyUniqueCompare<aiVectorKey>), l->end());
+ }
+ }
+
+ break;
+
+ // ////////////////////////////////////////////////////////////////////
+ // CAMERA ROLL KEYFRAME
+ case Discreet3DS::CHUNK_TRACKROLL: {
+ // roll keys are accepted for cameras only
+ if (parent != Discreet3DS::CHUNK_TRACKCAMERA) {
+ ASSIMP_LOG_WARN("3DS: Ignoring roll track for non-camera object");
+ break;
+ }
+ bool sortKeys = false;
+ std::vector<aiFloatKey> *l = &mCurrentNode->aCameraRollKeys;
+
+ stream->IncPtr(10);
+ const unsigned int numFrames = stream->GetI4();
+ l->reserve(numFrames);
+ for (unsigned int i = 0; i < numFrames; ++i) {
+ const unsigned int fidx = stream->GetI4();
+
+ // Setup a new position key
+ aiFloatKey v;
+ v.mTime = (double)fidx;
+
+ // This is just a single float
+ SkipTCBInfo();
+ v.mValue = stream->GetF4();
+
+ // Check whether we'll need to sort the keys
+ if (!l->empty() && v.mTime <= l->back().mTime)
+ sortKeys = true;
+
+ // Add the new keyframe to the list
+ l->push_back(v);
+ }
+
+ // Sort all keys with ascending time values and remove duplicates?
+ if (sortKeys) {
+ std::stable_sort(l->begin(), l->end());
+ l->erase(std::unique(l->begin(), l->end(), &KeyUniqueCompare<aiFloatKey>), l->end());
+ }
+ } break;
+
+ // ////////////////////////////////////////////////////////////////////
+ // CAMERA FOV KEYFRAME
+ case Discreet3DS::CHUNK_TRACKFOV: {
+ ASSIMP_LOG_ERROR("3DS: Skipping FOV animation track. "
+ "This is not supported");
+ } break;
+
+ // ////////////////////////////////////////////////////////////////////
+ // ROTATION KEYFRAME
+ case Discreet3DS::CHUNK_TRACKROTATE: {
+ stream->IncPtr(10);
+ const unsigned int numFrames = stream->GetI4();
+
+ bool sortKeys = false;
+ std::vector<aiQuatKey> *l = &mCurrentNode->aRotationKeys;
+ l->reserve(numFrames);
+
+ for (unsigned int i = 0; i < numFrames; ++i) {
+ const unsigned int fidx = stream->GetI4();
+ SkipTCBInfo();
+
+ aiQuatKey v;
+ v.mTime = (double)fidx;
+
+ // The rotation keyframe is given as an axis-angle pair
+ const float rad = stream->GetF4();
+ aiVector3D axis;
+ axis.x = stream->GetF4();
+ axis.y = stream->GetF4();
+ axis.z = stream->GetF4();
+
+ if (!axis.x && !axis.y && !axis.z)
+ axis.y = 1.f;
+
+ // Construct a rotation quaternion from the axis-angle pair
+ v.mValue = aiQuaternion(axis, rad);
+
+ // Check whether we'll need to sort the keys
+ if (!l->empty() && v.mTime <= l->back().mTime)
+ sortKeys = true;
+
+ // add the new keyframe to the list
+ l->push_back(v);
+ }
+ // Sort all keys with ascending time values and remove duplicates?
+ if (sortKeys) {
+ std::stable_sort(l->begin(), l->end());
+ l->erase(std::unique(l->begin(), l->end(), &KeyUniqueCompare<aiQuatKey>), l->end());
+ }
+ } break;
+
+ // ////////////////////////////////////////////////////////////////////
+ // SCALING KEYFRAME
+ case Discreet3DS::CHUNK_TRACKSCALE: {
+ stream->IncPtr(10);
+ const unsigned int numFrames = stream->GetI2();
+ stream->IncPtr(2);
+
+ bool sortKeys = false;
+ std::vector<aiVectorKey> *l = &mCurrentNode->aScalingKeys;
+ l->reserve(numFrames);
+
+ for (unsigned int i = 0; i < numFrames; ++i) {
+ const unsigned int fidx = stream->GetI4();
+ SkipTCBInfo();
+
+ // Setup a new key
+ aiVectorKey v;
+ v.mTime = (double)fidx;
+
+ // ... and read its value
+ v.mValue.x = stream->GetF4();
+ v.mValue.y = stream->GetF4();
+ v.mValue.z = stream->GetF4();
+
+ // check whether we'll need to sort the keys
+ if (!l->empty() && v.mTime <= l->back().mTime)
+ sortKeys = true;
+
+ // Remove zero-scalings on singular axes - they've been reported to be there erroneously in some strange files
+ if (!v.mValue.x) v.mValue.x = 1.f;
+ if (!v.mValue.y) v.mValue.y = 1.f;
+ if (!v.mValue.z) v.mValue.z = 1.f;
+
+ l->push_back(v);
+ }
+ // Sort all keys with ascending time values and remove duplicates?
+ if (sortKeys) {
+ std::stable_sort(l->begin(), l->end());
+ l->erase(std::unique(l->begin(), l->end(), &KeyUniqueCompare<aiVectorKey>), l->end());
+ }
+ } break;
+ };
+
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a face chunk - it contains smoothing groups and material assignments
+void Discreet3DSImporter::ParseFaceChunk() {
+ ASSIMP_3DS_BEGIN_CHUNK();
+
+ // Get the mesh we're currently working on
+ D3DS::Mesh &mMesh = mScene->mMeshes.back();
+
+ // Get chunk type
+ switch (chunk.Flag) {
+ case Discreet3DS::CHUNK_SMOOLIST: {
+ // This is the list of smoothing groups - a bitfield for every face.
+ // Up to 32 smoothing groups assigned to a single face.
+ unsigned int num = chunkSize / 4, m = 0;
+ if (num > mMesh.mFaces.size()) {
+ throw DeadlyImportError("3DS: More smoothing groups than faces");
+ }
+ for (std::vector<D3DS::Face>::iterator i = mMesh.mFaces.begin(); m != num; ++i, ++m) {
+ // nth bit is set for nth smoothing group
+ (*i).iSmoothGroup = stream->GetI4();
+ }
+ } break;
+
+ case Discreet3DS::CHUNK_FACEMAT: {
+ // at fist an asciiz with the material name
+ const char *sz = (const char *)stream->GetPtr();
+ while (stream->GetI1())
+ ;
+
+ // find the index of the material
+ unsigned int idx = 0xcdcdcdcd, cnt = 0;
+ for (std::vector<D3DS::Material>::const_iterator i = mScene->mMaterials.begin(); i != mScene->mMaterials.end(); ++i, ++cnt) {
+ // use case independent comparisons. hopefully it will work.
+ if ((*i).mName.length() && !ASSIMP_stricmp(sz, (*i).mName.c_str())) {
+ idx = cnt;
+ break;
+ }
+ }
+ if (0xcdcdcdcd == idx) {
+ ASSIMP_LOG_ERROR("3DS: Unknown material: ", sz);
+ }
+
+ // Now continue and read all material indices
+ cnt = (uint16_t)stream->GetI2();
+ for (unsigned int i = 0; i < cnt; ++i) {
+ unsigned int fidx = (uint16_t)stream->GetI2();
+
+ // check range
+ if (fidx >= mMesh.mFaceMaterials.size()) {
+ ASSIMP_LOG_ERROR("3DS: Invalid face index in face material list");
+ } else
+ mMesh.mFaceMaterials[fidx] = idx;
+ }
+ } break;
+ };
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a mesh chunk. Here's the actual mesh data
+void Discreet3DSImporter::ParseMeshChunk() {
+ ASSIMP_3DS_BEGIN_CHUNK();
+
+ // Get the mesh we're currently working on
+ D3DS::Mesh &mMesh = mScene->mMeshes.back();
+
+ // get chunk type
+ switch (chunk.Flag) {
+ case Discreet3DS::CHUNK_VERTLIST: {
+ // This is the list of all vertices in the current mesh
+ int num = (int)(uint16_t)stream->GetI2();
+ mMesh.mPositions.reserve(num);
+ while (num-- > 0) {
+ aiVector3D v;
+ v.x = stream->GetF4();
+ v.y = stream->GetF4();
+ v.z = stream->GetF4();
+ mMesh.mPositions.push_back(v);
+ }
+ } break;
+ case Discreet3DS::CHUNK_TRMATRIX: {
+ // This is the RLEATIVE transformation matrix of the current mesh. Vertices are
+ // pretransformed by this matrix wonder.
+ mMesh.mMat.a1 = stream->GetF4();
+ mMesh.mMat.b1 = stream->GetF4();
+ mMesh.mMat.c1 = stream->GetF4();
+ mMesh.mMat.a2 = stream->GetF4();
+ mMesh.mMat.b2 = stream->GetF4();
+ mMesh.mMat.c2 = stream->GetF4();
+ mMesh.mMat.a3 = stream->GetF4();
+ mMesh.mMat.b3 = stream->GetF4();
+ mMesh.mMat.c3 = stream->GetF4();
+ mMesh.mMat.a4 = stream->GetF4();
+ mMesh.mMat.b4 = stream->GetF4();
+ mMesh.mMat.c4 = stream->GetF4();
+ } break;
+
+ case Discreet3DS::CHUNK_MAPLIST: {
+ // This is the list of all UV coords in the current mesh
+ int num = (int)(uint16_t)stream->GetI2();
+ mMesh.mTexCoords.reserve(num);
+ while (num-- > 0) {
+ aiVector3D v;
+ v.x = stream->GetF4();
+ v.y = stream->GetF4();
+ mMesh.mTexCoords.push_back(v);
+ }
+ } break;
+
+ case Discreet3DS::CHUNK_FACELIST: {
+ // This is the list of all faces in the current mesh
+ int num = (int)(uint16_t)stream->GetI2();
+ mMesh.mFaces.reserve(num);
+ while (num-- > 0) {
+ // 3DS faces are ALWAYS triangles
+ mMesh.mFaces.push_back(D3DS::Face());
+ D3DS::Face &sFace = mMesh.mFaces.back();
+
+ sFace.mIndices[0] = (uint16_t)stream->GetI2();
+ sFace.mIndices[1] = (uint16_t)stream->GetI2();
+ sFace.mIndices[2] = (uint16_t)stream->GetI2();
+
+ stream->IncPtr(2); // skip edge visibility flag
+ }
+
+ // Resize the material array (0xcdcdcdcd marks the default material; so if a face is
+ // not referenced by a material, $$DEFAULT will be assigned to it)
+ mMesh.mFaceMaterials.resize(mMesh.mFaces.size(), 0xcdcdcdcd);
+
+ // Larger 3DS files could have multiple FACE chunks here
+ chunkSize = (int)stream->GetRemainingSizeToLimit();
+ if (chunkSize > (int)sizeof(Discreet3DS::Chunk))
+ ParseFaceChunk();
+ } break;
+ };
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a 3DS material chunk
+void Discreet3DSImporter::ParseMaterialChunk() {
+ ASSIMP_3DS_BEGIN_CHUNK();
+ switch (chunk.Flag) {
+ case Discreet3DS::CHUNK_MAT_MATNAME:
+
+ {
+ // The material name string is already zero-terminated, but we need to be sure ...
+ const char *sz = (const char *)stream->GetPtr();
+ unsigned int cnt = 0;
+ while (stream->GetI1())
+ ++cnt;
+
+ if (!cnt) {
+ // This may not be, we use the default name instead
+ ASSIMP_LOG_ERROR("3DS: Empty material name");
+ } else
+ mScene->mMaterials.back().mName = std::string(sz, cnt);
+ } break;
+
+ case Discreet3DS::CHUNK_MAT_DIFFUSE: {
+ // This is the diffuse material color
+ aiColor3D *pc = &mScene->mMaterials.back().mDiffuse;
+ ParseColorChunk(pc);
+ if (is_qnan(pc->r)) {
+ // color chunk is invalid. Simply ignore it
+ ASSIMP_LOG_ERROR("3DS: Unable to read DIFFUSE chunk");
+ pc->r = pc->g = pc->b = 1.0f;
+ }
+ } break;
+
+ case Discreet3DS::CHUNK_MAT_SPECULAR: {
+ // This is the specular material color
+ aiColor3D *pc = &mScene->mMaterials.back().mSpecular;
+ ParseColorChunk(pc);
+ if (is_qnan(pc->r)) {
+ // color chunk is invalid. Simply ignore it
+ ASSIMP_LOG_ERROR("3DS: Unable to read SPECULAR chunk");
+ pc->r = pc->g = pc->b = 1.0f;
+ }
+ } break;
+
+ case Discreet3DS::CHUNK_MAT_AMBIENT: {
+ // This is the ambient material color
+ aiColor3D *pc = &mScene->mMaterials.back().mAmbient;
+ ParseColorChunk(pc);
+ if (is_qnan(pc->r)) {
+ // color chunk is invalid. Simply ignore it
+ ASSIMP_LOG_ERROR("3DS: Unable to read AMBIENT chunk");
+ pc->r = pc->g = pc->b = 0.0f;
+ }
+ } break;
+
+ case Discreet3DS::CHUNK_MAT_SELF_ILLUM: {
+ // This is the emissive material color
+ aiColor3D *pc = &mScene->mMaterials.back().mEmissive;
+ ParseColorChunk(pc);
+ if (is_qnan(pc->r)) {
+ // color chunk is invalid. Simply ignore it
+ ASSIMP_LOG_ERROR("3DS: Unable to read EMISSIVE chunk");
+ pc->r = pc->g = pc->b = 0.0f;
+ }
+ } break;
+
+ case Discreet3DS::CHUNK_MAT_TRANSPARENCY: {
+ // This is the material's transparency
+ ai_real *pcf = &mScene->mMaterials.back().mTransparency;
+ *pcf = ParsePercentageChunk();
+
+ // NOTE: transparency, not opacity
+ if (is_qnan(*pcf))
+ *pcf = ai_real(1.0);
+ else
+ *pcf = ai_real(1.0) - *pcf * (ai_real)0xFFFF / ai_real(100.0);
+ } break;
+
+ case Discreet3DS::CHUNK_MAT_SHADING:
+ // This is the material shading mode
+ mScene->mMaterials.back().mShading = (D3DS::Discreet3DS::shadetype3ds)stream->GetI2();
+ break;
+
+ case Discreet3DS::CHUNK_MAT_TWO_SIDE:
+ // This is the two-sided flag
+ mScene->mMaterials.back().mTwoSided = true;
+ break;
+
+ case Discreet3DS::CHUNK_MAT_SHININESS: { // This is the shininess of the material
+ ai_real *pcf = &mScene->mMaterials.back().mSpecularExponent;
+ *pcf = ParsePercentageChunk();
+ if (is_qnan(*pcf))
+ *pcf = 0.0;
+ else
+ *pcf *= (ai_real)0xFFFF;
+ } break;
+
+ case Discreet3DS::CHUNK_MAT_SHININESS_PERCENT: { // This is the shininess strength of the material
+ ai_real *pcf = &mScene->mMaterials.back().mShininessStrength;
+ *pcf = ParsePercentageChunk();
+ if (is_qnan(*pcf))
+ *pcf = ai_real(0.0);
+ else
+ *pcf *= (ai_real)0xffff / ai_real(100.0);
+ } break;
+
+ case Discreet3DS::CHUNK_MAT_SELF_ILPCT: { // This is the self illumination strength of the material
+ ai_real f = ParsePercentageChunk();
+ if (is_qnan(f))
+ f = ai_real(0.0);
+ else
+ f *= (ai_real)0xFFFF / ai_real(100.0);
+ mScene->mMaterials.back().mEmissive = aiColor3D(f, f, f);
+ } break;
+
+ // Parse texture chunks
+ case Discreet3DS::CHUNK_MAT_TEXTURE:
+ // Diffuse texture
+ ParseTextureChunk(&mScene->mMaterials.back().sTexDiffuse);
+ break;
+ case Discreet3DS::CHUNK_MAT_BUMPMAP:
+ // Height map
+ ParseTextureChunk(&mScene->mMaterials.back().sTexBump);
+ break;
+ case Discreet3DS::CHUNK_MAT_OPACMAP:
+ // Opacity texture
+ ParseTextureChunk(&mScene->mMaterials.back().sTexOpacity);
+ break;
+ case Discreet3DS::CHUNK_MAT_MAT_SHINMAP:
+ // Shininess map
+ ParseTextureChunk(&mScene->mMaterials.back().sTexShininess);
+ break;
+ case Discreet3DS::CHUNK_MAT_SPECMAP:
+ // Specular map
+ ParseTextureChunk(&mScene->mMaterials.back().sTexSpecular);
+ break;
+ case Discreet3DS::CHUNK_MAT_SELFIMAP:
+ // Self-illumination (emissive) map
+ ParseTextureChunk(&mScene->mMaterials.back().sTexEmissive);
+ break;
+ case Discreet3DS::CHUNK_MAT_REFLMAP:
+ // Reflection map
+ ParseTextureChunk(&mScene->mMaterials.back().sTexReflective);
+ break;
+ };
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSImporter::ParseTextureChunk(D3DS::Texture *pcOut) {
+ ASSIMP_3DS_BEGIN_CHUNK();
+
+ // get chunk type
+ switch (chunk.Flag) {
+ case Discreet3DS::CHUNK_MAPFILE: {
+ // The material name string is already zero-terminated, but we need to be sure ...
+ const char *sz = (const char *)stream->GetPtr();
+ unsigned int cnt = 0;
+ while (stream->GetI1())
+ ++cnt;
+ pcOut->mMapName = std::string(sz, cnt);
+ } break;
+
+ case Discreet3DS::CHUNK_PERCENTD:
+ // Manually parse the blend factor
+ pcOut->mTextureBlend = ai_real(stream->GetF8());
+ break;
+
+ case Discreet3DS::CHUNK_PERCENTF:
+ // Manually parse the blend factor
+ pcOut->mTextureBlend = stream->GetF4();
+ break;
+
+ case Discreet3DS::CHUNK_PERCENTW:
+ // Manually parse the blend factor
+ pcOut->mTextureBlend = (ai_real)((uint16_t)stream->GetI2()) / ai_real(100.0);
+ break;
+
+ case Discreet3DS::CHUNK_MAT_MAP_USCALE:
+ // Texture coordinate scaling in the U direction
+ pcOut->mScaleU = stream->GetF4();
+ if (0.0f == pcOut->mScaleU) {
+ ASSIMP_LOG_WARN("Texture coordinate scaling in the x direction is zero. Assuming 1.");
+ pcOut->mScaleU = 1.0f;
+ }
+ break;
+ case Discreet3DS::CHUNK_MAT_MAP_VSCALE:
+ // Texture coordinate scaling in the V direction
+ pcOut->mScaleV = stream->GetF4();
+ if (0.0f == pcOut->mScaleV) {
+ ASSIMP_LOG_WARN("Texture coordinate scaling in the y direction is zero. Assuming 1.");
+ pcOut->mScaleV = 1.0f;
+ }
+ break;
+
+ case Discreet3DS::CHUNK_MAT_MAP_UOFFSET:
+ // Texture coordinate offset in the U direction
+ pcOut->mOffsetU = -stream->GetF4();
+ break;
+
+ case Discreet3DS::CHUNK_MAT_MAP_VOFFSET:
+ // Texture coordinate offset in the V direction
+ pcOut->mOffsetV = stream->GetF4();
+ break;
+
+ case Discreet3DS::CHUNK_MAT_MAP_ANG:
+ // Texture coordinate rotation, CCW in DEGREES
+ pcOut->mRotation = -AI_DEG_TO_RAD(stream->GetF4());
+ break;
+
+ case Discreet3DS::CHUNK_MAT_MAP_TILING: {
+ const uint16_t iFlags = stream->GetI2();
+
+ // Get the mapping mode (for both axes)
+ if (iFlags & 0x2u)
+ pcOut->mMapMode = aiTextureMapMode_Mirror;
+
+ else if (iFlags & 0x10u)
+ pcOut->mMapMode = aiTextureMapMode_Decal;
+
+ // wrapping in all remaining cases
+ else
+ pcOut->mMapMode = aiTextureMapMode_Wrap;
+ } break;
+ };
+
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a percentage chunk
+ai_real Discreet3DSImporter::ParsePercentageChunk() {
+ Discreet3DS::Chunk chunk;
+ ReadChunk(&chunk);
+
+ if (Discreet3DS::CHUNK_PERCENTF == chunk.Flag) {
+ return stream->GetF4() * ai_real(100) / ai_real(0xFFFF);
+ } else if (Discreet3DS::CHUNK_PERCENTW == chunk.Flag) {
+ return (ai_real)((uint16_t)stream->GetI2()) / (ai_real)0xFFFF;
+ }
+
+ return get_qnan();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a color chunk. If a percentage chunk is found instead it is read as a grayscale color
+void Discreet3DSImporter::ParseColorChunk(aiColor3D *out, bool acceptPercent) {
+ ai_assert(out != nullptr);
+
+ // error return value
+ const ai_real qnan = get_qnan();
+ static const aiColor3D clrError = aiColor3D(qnan, qnan, qnan);
+
+ Discreet3DS::Chunk chunk;
+ ReadChunk(&chunk);
+ const unsigned int diff = chunk.Size - sizeof(Discreet3DS::Chunk);
+
+ bool bGamma = false;
+
+ // Get the type of the chunk
+ switch (chunk.Flag) {
+ case Discreet3DS::CHUNK_LINRGBF:
+ bGamma = true;
+
+ case Discreet3DS::CHUNK_RGBF:
+ if (sizeof(float) * 3 > diff) {
+ *out = clrError;
+ return;
+ }
+ out->r = stream->GetF4();
+ out->g = stream->GetF4();
+ out->b = stream->GetF4();
+ break;
+
+ case Discreet3DS::CHUNK_LINRGBB:
+ bGamma = true;
+ case Discreet3DS::CHUNK_RGBB: {
+ if (sizeof(char) * 3 > diff) {
+ *out = clrError;
+ return;
+ }
+ const ai_real invVal = ai_real(1.0) / ai_real(255.0);
+ out->r = (ai_real)(uint8_t)stream->GetI1() * invVal;
+ out->g = (ai_real)(uint8_t)stream->GetI1() * invVal;
+ out->b = (ai_real)(uint8_t)stream->GetI1() * invVal;
+ } break;
+
+ // Percentage chunks are accepted, too.
+ case Discreet3DS::CHUNK_PERCENTF:
+ if (acceptPercent && 4 <= diff) {
+ out->g = out->b = out->r = stream->GetF4();
+ break;
+ }
+ *out = clrError;
+ return;
+
+ case Discreet3DS::CHUNK_PERCENTW:
+ if (acceptPercent && 1 <= diff) {
+ out->g = out->b = out->r = (ai_real)(uint8_t)stream->GetI1() / ai_real(255.0);
+ break;
+ }
+ *out = clrError;
+ return;
+
+ default:
+ stream->IncPtr(diff);
+ // Skip unknown chunks, hope this won't cause any problems.
+ return ParseColorChunk(out, acceptPercent);
+ };
+ (void)bGamma;
+}
+
+#endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER
diff --git a/libs/assimp/code/AssetLib/3DS/3DSLoader.h b/libs/assimp/code/AssetLib/3DS/3DSLoader.h
new file mode 100644
index 0000000..f47fcfe
--- /dev/null
+++ b/libs/assimp/code/AssetLib/3DS/3DSLoader.h
@@ -0,0 +1,289 @@
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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 3DSLoader.h
+ * @brief 3DS File format loader
+ */
+#ifndef AI_3DSIMPORTER_H_INC
+#define AI_3DSIMPORTER_H_INC
+#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
+
+#include <assimp/BaseImporter.h>
+#include <assimp/types.h>
+
+
+#include "3DSHelper.h"
+#include <assimp/StreamReader.h>
+
+struct aiNode;
+
+namespace Assimp {
+
+
+using namespace D3DS;
+
+// ---------------------------------------------------------------------------------
+/** Importer class for 3D Studio r3 and r4 3DS files
+ */
+class Discreet3DSImporter : public BaseImporter {
+public:
+ Discreet3DSImporter();
+ ~Discreet3DSImporter();
+
+ // -------------------------------------------------------------------
+ /** Returns whether the class can handle the format of the given file.
+ * See BaseImporter::CanRead() for details.
+ */
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const override;
+
+ // -------------------------------------------------------------------
+ /** Called prior to ReadFile().
+ * The function is a request to the importer to update its configuration
+ * basing on the Importer's configuration property list.
+ */
+ void SetupProperties(const Importer* pImp) override;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Return importer meta information.
+ * See #BaseImporter::GetInfo for the details
+ */
+ const aiImporterDesc* GetInfo () const override;
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler) override;
+
+ // -------------------------------------------------------------------
+ /** Converts a temporary material to the outer representation
+ */
+ void ConvertMaterial(D3DS::Material& p_cMat,
+ aiMaterial& p_pcOut);
+
+ // -------------------------------------------------------------------
+ /** Read a chunk
+ *
+ * @param pcOut Receives the current chunk
+ */
+ void ReadChunk(Discreet3DS::Chunk* pcOut);
+
+ // -------------------------------------------------------------------
+ /** Parse a percentage chunk. mCurrent will point to the next
+ * chunk behind afterwards. If no percentage chunk is found
+ * QNAN is returned.
+ */
+ ai_real ParsePercentageChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a color chunk. mCurrent will point to the next
+ * chunk behind afterwards. If no color chunk is found
+ * QNAN is returned in all members.
+ */
+ void ParseColorChunk(aiColor3D* p_pcOut,
+ bool p_bAcceptPercent = true);
+
+
+ // -------------------------------------------------------------------
+ /** Skip a chunk in the file
+ */
+ void SkipChunk();
+
+ // -------------------------------------------------------------------
+ /** Generate the nodegraph
+ */
+ void GenerateNodeGraph(aiScene* pcOut);
+
+ // -------------------------------------------------------------------
+ /** Parse a main top-level chunk in the file
+ */
+ void ParseMainChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a top-level chunk in the file
+ */
+ void ParseChunk(const char* name, unsigned int num);
+
+ // -------------------------------------------------------------------
+ /** Parse a top-level editor chunk in the file
+ */
+ void ParseEditorChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a top-level object chunk in the file
+ */
+ void ParseObjectChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a material chunk in the file
+ */
+ void ParseMaterialChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a mesh chunk in the file
+ */
+ void ParseMeshChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a light chunk in the file
+ */
+ void ParseLightChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a camera chunk in the file
+ */
+ void ParseCameraChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a face list chunk in the file
+ */
+ void ParseFaceChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a keyframe chunk in the file
+ */
+ void ParseKeyframeChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a hierarchy chunk in the file
+ */
+ void ParseHierarchyChunk(uint16_t parent);
+
+ // -------------------------------------------------------------------
+ /** Parse a texture chunk in the file
+ */
+ void ParseTextureChunk(D3DS::Texture* pcOut);
+
+ // -------------------------------------------------------------------
+ /** Convert the meshes in the file
+ */
+ void ConvertMeshes(aiScene* pcOut);
+
+ // -------------------------------------------------------------------
+ /** Replace the default material in the scene
+ */
+ void ReplaceDefaultMaterial();
+
+ bool ContainsTextures(unsigned int i) const {
+ return !mScene->mMaterials[i].sTexDiffuse.mMapName.empty() ||
+ !mScene->mMaterials[i].sTexBump.mMapName.empty() ||
+ !mScene->mMaterials[i].sTexOpacity.mMapName.empty() ||
+ !mScene->mMaterials[i].sTexEmissive.mMapName.empty() ||
+ !mScene->mMaterials[i].sTexSpecular.mMapName.empty() ||
+ !mScene->mMaterials[i].sTexShininess.mMapName.empty() ;
+ }
+
+ // -------------------------------------------------------------------
+ /** Convert the whole scene
+ */
+ void ConvertScene(aiScene* pcOut);
+
+ // -------------------------------------------------------------------
+ /** generate unique vertices for a mesh
+ */
+ void MakeUnique(D3DS::Mesh& sMesh);
+
+ // -------------------------------------------------------------------
+ /** Add a node to the node graph
+ */
+ void AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,D3DS::Node* pcIn,
+ aiMatrix4x4& absTrafo);
+
+ // -------------------------------------------------------------------
+ /** Search for a node in the graph.
+ * Called recursively
+ */
+ void InverseNodeSearch(D3DS::Node* pcNode,D3DS::Node* pcCurrent);
+
+ // -------------------------------------------------------------------
+ /** Apply the master scaling factor to the mesh
+ */
+ void ApplyMasterScale(aiScene* pScene);
+
+ // -------------------------------------------------------------------
+ /** Clamp all indices in the file to a valid range
+ */
+ void CheckIndices(D3DS::Mesh& sMesh);
+
+ // -------------------------------------------------------------------
+ /** Skip the TCB info in a track key
+ */
+ void SkipTCBInfo();
+
+protected:
+
+ /** Stream to read from */
+ StreamReaderLE* stream;
+
+ /** Last touched node index */
+ short mLastNodeIndex;
+
+ /** Current node, root node */
+ D3DS::Node* mCurrentNode, *mRootNode;
+
+ /** Scene under construction */
+ D3DS::Scene* mScene;
+
+ /** Ambient base color of the scene */
+ aiColor3D mClrAmbient;
+
+ /** Master scaling factor of the scene */
+ ai_real mMasterScale;
+
+ /** Path to the background image of the scene */
+ std::string mBackgroundImage;
+ bool bHasBG;
+
+ /** true if PRJ file */
+ bool bIsPrj;
+};
+
+} // end of namespace Assimp
+
+#endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER
+
+#endif // AI_3DSIMPORTER_H_INC