| /* |
| --------------------------------------------------------------------------- |
| Open Asset Import Library (assimp) |
| --------------------------------------------------------------------------- |
| |
| Copyright (c) 2006-2017, 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 |
| |
| // internal headers |
| #include "3DSLoader.h" |
| #include "Macros.h" |
| #include <assimp/IOSystem.hpp> |
| #include <assimp/scene.h> |
| #include <assimp/DefaultLogger.hpp> |
| #include <assimp/importerdesc.h> |
| #include "StringComparison.h" |
| |
| 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() |
| {} |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Destructor, private as well |
| Discreet3DSImporter::~Discreet3DSImporter() |
| {} |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Returns whether the class can handle the format of the given file. |
| bool Discreet3DSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const |
| { |
| std::string extension = GetExtension(pFile); |
| if(extension == "3ds" || extension == "prj" ) { |
| return true; |
| } |
| if (!extension.length() || checkSig) { |
| uint16_t token[3]; |
| token[0] = 0x4d4d; |
| token[1] = 0x3dc2; |
| //token[2] = 0x3daa; |
| return CheckMagicToken(pIOHandler,pFile,token,2,0,2); |
| } |
| return false; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // 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) |
| { |
| StreamReaderLE stream(pIOHandler->Open(pFile,"rb")); |
| this->stream = &stream; |
| |
| // We should have at least one chunk |
| if (stream.GetRemainingSize() < 16) { |
| throw DeadlyImportError("3DS file is either empty or corrupt: " + pFile); |
| } |
| |
| // Allocate our temporary 3DS representation |
| mScene = new D3DS::Scene(); |
| |
| // Initialize members |
| mLastNodeIndex = -1; |
| mCurrentNode = new D3DS::Node(); |
| mRootNode = mCurrentNode; |
| mRootNode->mHierarchyPos = -1; |
| mRootNode->mHierarchyIndex = -1; |
| mRootNode->mParent = NULL; |
| mMasterScale = 1.0f; |
| mBackgroundImage = ""; |
| 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) { |
| delete mScene; |
| 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 submeshes |
| GenerateNodeGraph(pScene); |
| |
| // Now apply the master scaling factor to the scene |
| ApplyMasterScale(pScene); |
| |
| // Delete our internal scene representation and the root |
| // node, so the whole hierarchy will follow |
| delete mRootNode; |
| delete mScene; |
| |
| 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 != NULL); |
| |
| 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()) |
| DefaultLogger::get()->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; |
| 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()); |
| DefaultLogger::get()->info(std::string("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()); |
| 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. |
| DefaultLogger::get()->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()); |
| D3DS::Mesh& m = mScene->mMeshes.back(); |
| |
| // Setup the name of the mesh |
| m.mName = 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. |
| DefaultLogger::get()->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 = 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; |
| if (( nd = FindNode(*it,name))) |
| return nd; |
| } |
| return NULL; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // 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 ist not really suitable for complex animations, |
| // so full support is not required. |
| DefaultLogger::get()->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(); |
| pcNode->mName = 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") { |
| //DefaultLogger::get()->warn("3DS: Skipping dummy object name for non-dummy object"); |
| mCurrentNode->mName = std::string(sz); |
| break; |
| } |
| } |
| break; |
| |
| case Discreet3DS::CHUNK_TRACKPIVOT: |
| |
| if ( Discreet3DS::CHUNK_TRACKINFO != parent) |
| { |
| DefaultLogger::get()->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) { |
| DefaultLogger::get()->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: |
| { |
| DefaultLogger::get()->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) { |
| DefaultLogger::get()->error(std::string("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()) { |
| DefaultLogger::get()->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 = 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 |
| DefaultLogger::get()->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 |
| DefaultLogger::get()->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 |
| DefaultLogger::get()->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 |
| DefaultLogger::get()->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 |
| DefaultLogger::get()->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) |
| { |
| DefaultLogger::get()->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) |
| { |
| DefaultLogger::get()->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(); |
| 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 != NULL); |
| |
| // 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 |