nuclear@0: /* nuclear@0: --------------------------------------------------------------------------- nuclear@0: Open Asset Import Library (assimp) nuclear@0: --------------------------------------------------------------------------- nuclear@0: nuclear@0: Copyright (c) 2006-2012, assimp team nuclear@0: nuclear@0: All rights reserved. nuclear@0: nuclear@0: Redistribution and use of this software in source and binary forms, nuclear@0: with or without modification, are permitted provided that the following nuclear@0: conditions are met: nuclear@0: nuclear@0: * Redistributions of source code must retain the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer. nuclear@0: nuclear@0: * Redistributions in binary form must reproduce the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer in the documentation and/or other nuclear@0: materials provided with the distribution. nuclear@0: nuclear@0: * Neither the name of the assimp team, nor the names of its nuclear@0: contributors may be used to endorse or promote products nuclear@0: derived from this software without specific prior nuclear@0: written permission of the assimp team. nuclear@0: nuclear@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS nuclear@0: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT nuclear@0: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR nuclear@0: A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT nuclear@0: OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, nuclear@0: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT nuclear@0: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, nuclear@0: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY nuclear@0: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT nuclear@0: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE nuclear@0: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. nuclear@0: --------------------------------------------------------------------------- nuclear@0: */ nuclear@0: nuclear@0: /** @file 3DSLoader.cpp nuclear@0: * @brief Implementation of the 3ds importer class nuclear@0: * nuclear@0: * http://www.the-labs.com/Blender/3DS-details.html nuclear@0: */ nuclear@0: nuclear@0: #include "AssimpPCH.h" nuclear@0: #ifndef ASSIMP_BUILD_NO_3DS_IMPORTER nuclear@0: nuclear@0: // internal headers nuclear@0: #include "3DSLoader.h" nuclear@0: nuclear@0: using namespace Assimp; nuclear@0: nuclear@0: static const aiImporterDesc desc = { nuclear@0: "Discreet 3DS Importer", nuclear@0: "", nuclear@0: "", nuclear@0: "Limited animation support", nuclear@0: aiImporterFlags_SupportBinaryFlavour, nuclear@0: 0, nuclear@0: 0, nuclear@0: 0, nuclear@0: 0, nuclear@0: "3ds prj" nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Begins a new parsing block nuclear@0: // - Reads the current chunk and validates it nuclear@0: // - computes its length nuclear@0: #define ASSIMP_3DS_BEGIN_CHUNK() \ nuclear@0: while (true) { \ nuclear@0: if (stream->GetRemainingSizeToLimit() < sizeof(Discreet3DS::Chunk)){ \ nuclear@0: return; \ nuclear@0: } \ nuclear@0: Discreet3DS::Chunk chunk; \ nuclear@0: ReadChunk(&chunk); \ nuclear@0: int chunkSize = chunk.Size-sizeof(Discreet3DS::Chunk); \ nuclear@0: const int oldReadLimit = stream->GetReadLimit(); \ nuclear@0: stream->SetReadLimit(stream->GetCurrentPos() + chunkSize); \ nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // End a parsing block nuclear@0: // Must follow at the end of each parsing block, reset chunk end marker to previous value nuclear@0: #define ASSIMP_3DS_END_CHUNK() \ nuclear@0: stream->SkipToReadLimit(); \ nuclear@0: stream->SetReadLimit(oldReadLimit); \ nuclear@0: if (stream->GetRemainingSizeToLimit() == 0) \ nuclear@0: return; \ nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Constructor to be privately used by Importer nuclear@0: Discreet3DSImporter::Discreet3DSImporter() nuclear@0: {} nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Destructor, private as well nuclear@0: Discreet3DSImporter::~Discreet3DSImporter() nuclear@0: {} nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Returns whether the class can handle the format of the given file. nuclear@0: bool Discreet3DSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const nuclear@0: { nuclear@0: std::string extension = GetExtension(pFile); nuclear@0: if(extension == "3ds" || extension == "prj" ) { nuclear@0: return true; nuclear@0: } nuclear@0: if (!extension.length() || checkSig) { nuclear@0: uint16_t token[3]; nuclear@0: token[0] = 0x4d4d; nuclear@0: token[1] = 0x3dc2; nuclear@0: //token[2] = 0x3daa; nuclear@0: return CheckMagicToken(pIOHandler,pFile,token,2,0,2); nuclear@0: } nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Loader registry entry nuclear@0: const aiImporterDesc* Discreet3DSImporter::GetInfo () const nuclear@0: { nuclear@0: return &desc; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Setup configuration properties nuclear@0: void Discreet3DSImporter::SetupProperties(const Importer* /*pImp*/) nuclear@0: { nuclear@0: // nothing to be done for the moment nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Imports the given file into the given scene structure. nuclear@0: void Discreet3DSImporter::InternReadFile( const std::string& pFile, nuclear@0: aiScene* pScene, IOSystem* pIOHandler) nuclear@0: { nuclear@0: StreamReaderLE stream(pIOHandler->Open(pFile,"rb")); nuclear@0: this->stream = &stream; nuclear@0: nuclear@0: // We should have at least one chunk nuclear@0: if (stream.GetRemainingSize() < 16) { nuclear@0: throw DeadlyImportError("3DS file is either empty or corrupt: " + pFile); nuclear@0: } nuclear@0: nuclear@0: // Allocate our temporary 3DS representation nuclear@0: mScene = new D3DS::Scene(); nuclear@0: nuclear@0: // Initialize members nuclear@0: mLastNodeIndex = -1; nuclear@0: mCurrentNode = new D3DS::Node(); nuclear@0: mRootNode = mCurrentNode; nuclear@0: mRootNode->mHierarchyPos = -1; nuclear@0: mRootNode->mHierarchyIndex = -1; nuclear@0: mRootNode->mParent = NULL; nuclear@0: mMasterScale = 1.0f; nuclear@0: mBackgroundImage = ""; nuclear@0: bHasBG = false; nuclear@0: bIsPrj = false; nuclear@0: nuclear@0: // Parse the file nuclear@0: ParseMainChunk(); nuclear@0: nuclear@0: // Process all meshes in the file. First check whether all nuclear@0: // face indices haev valid values. The generate our nuclear@0: // internal verbose representation. Finally compute normal nuclear@0: // vectors from the smoothing groups we read from the nuclear@0: // file. nuclear@0: for (std::vector::iterator i = mScene->mMeshes.begin(), nuclear@0: end = mScene->mMeshes.end(); i != end;++i) { nuclear@0: CheckIndices(*i); nuclear@0: MakeUnique (*i); nuclear@0: ComputeNormalsWithSmoothingsGroups(*i); nuclear@0: } nuclear@0: nuclear@0: // Replace all occurences of the default material with a nuclear@0: // valid material. Generate it if no material containing nuclear@0: // DEFAULT in its name has been found in the file nuclear@0: ReplaceDefaultMaterial(); nuclear@0: nuclear@0: // Convert the scene from our internal representation to an nuclear@0: // aiScene object. This involves copying all meshes, lights nuclear@0: // and cameras to the scene nuclear@0: ConvertScene(pScene); nuclear@0: nuclear@0: // Generate the node graph for the scene. This is a little bit nuclear@0: // tricky since we'll need to split some meshes into submeshes nuclear@0: GenerateNodeGraph(pScene); nuclear@0: nuclear@0: // Now apply the master scaling factor to the scene nuclear@0: ApplyMasterScale(pScene); nuclear@0: nuclear@0: // Delete our internal scene representation and the root nuclear@0: // node, so the whole hierarchy will follow nuclear@0: delete mRootNode; nuclear@0: delete mScene; nuclear@0: nuclear@0: AI_DEBUG_INVALIDATE_PTR(mRootNode); nuclear@0: AI_DEBUG_INVALIDATE_PTR(mScene); nuclear@0: AI_DEBUG_INVALIDATE_PTR(this->stream); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Applies a master-scaling factor to the imported scene nuclear@0: void Discreet3DSImporter::ApplyMasterScale(aiScene* pScene) nuclear@0: { nuclear@0: // There are some 3DS files with a zero scaling factor nuclear@0: if (!mMasterScale)mMasterScale = 1.0f; nuclear@0: else mMasterScale = 1.0f / mMasterScale; nuclear@0: nuclear@0: // Construct an uniform scaling matrix and multiply with it nuclear@0: pScene->mRootNode->mTransformation *= aiMatrix4x4( nuclear@0: mMasterScale,0.0f, 0.0f, 0.0f, nuclear@0: 0.0f, mMasterScale,0.0f, 0.0f, nuclear@0: 0.0f, 0.0f, mMasterScale,0.0f, nuclear@0: 0.0f, 0.0f, 0.0f, 1.0f); nuclear@0: nuclear@0: // Check whether a scaling track is assigned to the root node. nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reads a new chunk from the file nuclear@0: void Discreet3DSImporter::ReadChunk(Discreet3DS::Chunk* pcOut) nuclear@0: { nuclear@0: ai_assert(pcOut != NULL); nuclear@0: nuclear@0: pcOut->Flag = stream->GetI2(); nuclear@0: pcOut->Size = stream->GetI4(); nuclear@0: nuclear@0: if (pcOut->Size - sizeof(Discreet3DS::Chunk) > stream->GetRemainingSize()) nuclear@0: throw DeadlyImportError("Chunk is too large"); nuclear@0: nuclear@0: if (pcOut->Size - sizeof(Discreet3DS::Chunk) > stream->GetRemainingSizeToLimit()) nuclear@0: DefaultLogger::get()->error("3DS: Chunk overflow"); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Skip a chunk nuclear@0: void Discreet3DSImporter::SkipChunk() nuclear@0: { nuclear@0: Discreet3DS::Chunk psChunk; nuclear@0: ReadChunk(&psChunk); nuclear@0: nuclear@0: stream->IncPtr(psChunk.Size-sizeof(Discreet3DS::Chunk)); nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Process the primary chunk of the file nuclear@0: void Discreet3DSImporter::ParseMainChunk() nuclear@0: { nuclear@0: ASSIMP_3DS_BEGIN_CHUNK(); nuclear@0: nuclear@0: // get chunk type nuclear@0: switch (chunk.Flag) nuclear@0: { nuclear@0: nuclear@0: case Discreet3DS::CHUNK_PRJ: nuclear@0: bIsPrj = true; nuclear@0: case Discreet3DS::CHUNK_MAIN: nuclear@0: ParseEditorChunk(); nuclear@0: break; nuclear@0: }; nuclear@0: nuclear@0: ASSIMP_3DS_END_CHUNK(); nuclear@0: // recursively continue processing this hierarchy level nuclear@0: return ParseMainChunk(); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void Discreet3DSImporter::ParseEditorChunk() nuclear@0: { nuclear@0: ASSIMP_3DS_BEGIN_CHUNK(); nuclear@0: nuclear@0: // get chunk type nuclear@0: switch (chunk.Flag) nuclear@0: { nuclear@0: case Discreet3DS::CHUNK_OBJMESH: nuclear@0: nuclear@0: ParseObjectChunk(); nuclear@0: break; nuclear@0: nuclear@0: // NOTE: In several documentations in the internet this nuclear@0: // chunk appears at different locations nuclear@0: case Discreet3DS::CHUNK_KEYFRAMER: nuclear@0: nuclear@0: ParseKeyframeChunk(); nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_VERSION: nuclear@0: { nuclear@0: // print the version number nuclear@0: char buff[10]; nuclear@0: ASSIMP_itoa10(buff,stream->GetI2()); nuclear@0: DefaultLogger::get()->info(std::string("3DS file format version: ") + buff); nuclear@0: } nuclear@0: break; nuclear@0: }; nuclear@0: ASSIMP_3DS_END_CHUNK(); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void Discreet3DSImporter::ParseObjectChunk() nuclear@0: { nuclear@0: ASSIMP_3DS_BEGIN_CHUNK(); nuclear@0: nuclear@0: // get chunk type nuclear@0: switch (chunk.Flag) nuclear@0: { nuclear@0: case Discreet3DS::CHUNK_OBJBLOCK: nuclear@0: { nuclear@0: unsigned int cnt = 0; nuclear@0: const char* sz = (const char*)stream->GetPtr(); nuclear@0: nuclear@0: // Get the name of the geometry object nuclear@0: while (stream->GetI1())++cnt; nuclear@0: ParseChunk(sz,cnt); nuclear@0: } nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_MAT_MATERIAL: nuclear@0: nuclear@0: // Add a new material to the list nuclear@0: mScene->mMaterials.push_back(D3DS::Material()); nuclear@0: ParseMaterialChunk(); nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_AMBCOLOR: nuclear@0: nuclear@0: // This is the ambient base color of the scene. nuclear@0: // We add it to the ambient color of all materials nuclear@0: ParseColorChunk(&mClrAmbient,true); nuclear@0: if (is_qnan(mClrAmbient.r)) nuclear@0: { nuclear@0: // We failed to read the ambient base color. nuclear@0: DefaultLogger::get()->error("3DS: Failed to read ambient base color"); nuclear@0: mClrAmbient.r = mClrAmbient.g = mClrAmbient.b = 0.0f; nuclear@0: } nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_BIT_MAP: nuclear@0: { nuclear@0: // Specifies the background image. The string should already be nuclear@0: // properly 0 terminated but we need to be sure nuclear@0: unsigned int cnt = 0; nuclear@0: const char* sz = (const char*)stream->GetPtr(); nuclear@0: while (stream->GetI1())++cnt; nuclear@0: mBackgroundImage = std::string(sz,cnt); nuclear@0: } nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_BIT_MAP_EXISTS: nuclear@0: bHasBG = true; nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_MASTER_SCALE: nuclear@0: // Scene master scaling factor nuclear@0: mMasterScale = stream->GetF4(); nuclear@0: break; nuclear@0: }; nuclear@0: ASSIMP_3DS_END_CHUNK(); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void Discreet3DSImporter::ParseChunk(const char* name, unsigned int num) nuclear@0: { nuclear@0: ASSIMP_3DS_BEGIN_CHUNK(); nuclear@0: nuclear@0: // IMPLEMENTATION NOTE; nuclear@0: // Cameras or lights define their transformation in their parent node and in the nuclear@0: // corresponding light or camera chunks. However, we read and process the latter nuclear@0: // to to be able to return valid cameras/lights even if no scenegraph is given. nuclear@0: nuclear@0: // get chunk type nuclear@0: switch (chunk.Flag) nuclear@0: { nuclear@0: case Discreet3DS::CHUNK_TRIMESH: nuclear@0: { nuclear@0: // this starts a new triangle mesh nuclear@0: mScene->mMeshes.push_back(D3DS::Mesh()); nuclear@0: D3DS::Mesh& m = mScene->mMeshes.back(); nuclear@0: nuclear@0: // Setup the name of the mesh nuclear@0: m.mName = std::string(name, num); nuclear@0: nuclear@0: // Read mesh chunks nuclear@0: ParseMeshChunk(); nuclear@0: } nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_LIGHT: nuclear@0: { nuclear@0: // This starts a new light nuclear@0: aiLight* light = new aiLight(); nuclear@0: mScene->mLights.push_back(light); nuclear@0: nuclear@0: light->mName.Set(std::string(name, num)); nuclear@0: nuclear@0: // First read the position of the light nuclear@0: light->mPosition.x = stream->GetF4(); nuclear@0: light->mPosition.y = stream->GetF4(); nuclear@0: light->mPosition.z = stream->GetF4(); nuclear@0: nuclear@0: light->mColorDiffuse = aiColor3D(1.f,1.f,1.f); nuclear@0: nuclear@0: // Now check for further subchunks nuclear@0: if (!bIsPrj) /* fixme */ nuclear@0: ParseLightChunk(); nuclear@0: nuclear@0: // The specular light color is identical the the diffuse light color. The ambient light color nuclear@0: // is equal to the ambient base color of the whole scene. nuclear@0: light->mColorSpecular = light->mColorDiffuse; nuclear@0: light->mColorAmbient = mClrAmbient; nuclear@0: nuclear@0: if (light->mType == aiLightSource_UNDEFINED) nuclear@0: { nuclear@0: // It must be a point light nuclear@0: light->mType = aiLightSource_POINT; nuclear@0: }} nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_CAMERA: nuclear@0: { nuclear@0: // This starts a new camera nuclear@0: aiCamera* camera = new aiCamera(); nuclear@0: mScene->mCameras.push_back(camera); nuclear@0: camera->mName.Set(std::string(name, num)); nuclear@0: nuclear@0: // First read the position of the camera nuclear@0: camera->mPosition.x = stream->GetF4(); nuclear@0: camera->mPosition.y = stream->GetF4(); nuclear@0: camera->mPosition.z = stream->GetF4(); nuclear@0: nuclear@0: // Then the camera target nuclear@0: camera->mLookAt.x = stream->GetF4() - camera->mPosition.x; nuclear@0: camera->mLookAt.y = stream->GetF4() - camera->mPosition.y; nuclear@0: camera->mLookAt.z = stream->GetF4() - camera->mPosition.z; nuclear@0: float len = camera->mLookAt.Length(); nuclear@0: if (len < 1e-5f) { nuclear@0: nuclear@0: // There are some files with lookat == position. Don't know why or whether it's ok or not. nuclear@0: DefaultLogger::get()->error("3DS: Unable to read proper camera look-at vector"); nuclear@0: camera->mLookAt = aiVector3D(0.f,1.f,0.f); nuclear@0: nuclear@0: } nuclear@0: else camera->mLookAt /= len; nuclear@0: nuclear@0: // And finally - the camera rotation angle, in counter clockwise direction nuclear@0: const float angle = AI_DEG_TO_RAD( stream->GetF4() ); nuclear@0: aiQuaternion quat(camera->mLookAt,angle); nuclear@0: camera->mUp = quat.GetMatrix() * aiVector3D(0.f,1.f,0.f); nuclear@0: nuclear@0: // Read the lense angle nuclear@0: camera->mHorizontalFOV = AI_DEG_TO_RAD ( stream->GetF4() ); nuclear@0: if (camera->mHorizontalFOV < 0.001f) { nuclear@0: camera->mHorizontalFOV = AI_DEG_TO_RAD(45.f); nuclear@0: } nuclear@0: nuclear@0: // Now check for further subchunks nuclear@0: if (!bIsPrj) /* fixme */ { nuclear@0: ParseCameraChunk(); nuclear@0: }} nuclear@0: break; nuclear@0: }; nuclear@0: ASSIMP_3DS_END_CHUNK(); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void Discreet3DSImporter::ParseLightChunk() nuclear@0: { nuclear@0: ASSIMP_3DS_BEGIN_CHUNK(); nuclear@0: aiLight* light = mScene->mLights.back(); nuclear@0: nuclear@0: // get chunk type nuclear@0: switch (chunk.Flag) nuclear@0: { nuclear@0: case Discreet3DS::CHUNK_DL_SPOTLIGHT: nuclear@0: // Now we can be sure that the light is a spot light nuclear@0: light->mType = aiLightSource_SPOT; nuclear@0: nuclear@0: // We wouldn't need to normalize here, but we do it nuclear@0: light->mDirection.x = stream->GetF4() - light->mPosition.x; nuclear@0: light->mDirection.y = stream->GetF4() - light->mPosition.y; nuclear@0: light->mDirection.z = stream->GetF4() - light->mPosition.z; nuclear@0: light->mDirection.Normalize(); nuclear@0: nuclear@0: // Now the hotspot and falloff angles - in degrees nuclear@0: light->mAngleInnerCone = AI_DEG_TO_RAD( stream->GetF4() ); nuclear@0: nuclear@0: // FIX: the falloff angle is just an offset nuclear@0: light->mAngleOuterCone = light->mAngleInnerCone+AI_DEG_TO_RAD( stream->GetF4() ); nuclear@0: break; nuclear@0: nuclear@0: // intensity multiplier nuclear@0: case Discreet3DS::CHUNK_DL_MULTIPLIER: nuclear@0: light->mColorDiffuse = light->mColorDiffuse * stream->GetF4(); nuclear@0: break; nuclear@0: nuclear@0: // light color nuclear@0: case Discreet3DS::CHUNK_RGBF: nuclear@0: case Discreet3DS::CHUNK_LINRGBF: nuclear@0: light->mColorDiffuse.r *= stream->GetF4(); nuclear@0: light->mColorDiffuse.g *= stream->GetF4(); nuclear@0: light->mColorDiffuse.b *= stream->GetF4(); nuclear@0: break; nuclear@0: nuclear@0: // light attenuation nuclear@0: case Discreet3DS::CHUNK_DL_ATTENUATE: nuclear@0: light->mAttenuationLinear = stream->GetF4(); nuclear@0: break; nuclear@0: }; nuclear@0: nuclear@0: ASSIMP_3DS_END_CHUNK(); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void Discreet3DSImporter::ParseCameraChunk() nuclear@0: { nuclear@0: ASSIMP_3DS_BEGIN_CHUNK(); nuclear@0: aiCamera* camera = mScene->mCameras.back(); nuclear@0: nuclear@0: // get chunk type nuclear@0: switch (chunk.Flag) nuclear@0: { nuclear@0: // near and far clip plane nuclear@0: case Discreet3DS::CHUNK_CAM_RANGES: nuclear@0: camera->mClipPlaneNear = stream->GetF4(); nuclear@0: camera->mClipPlaneFar = stream->GetF4(); nuclear@0: break; nuclear@0: } nuclear@0: nuclear@0: ASSIMP_3DS_END_CHUNK(); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void Discreet3DSImporter::ParseKeyframeChunk() nuclear@0: { nuclear@0: ASSIMP_3DS_BEGIN_CHUNK(); nuclear@0: nuclear@0: // get chunk type nuclear@0: switch (chunk.Flag) nuclear@0: { nuclear@0: case Discreet3DS::CHUNK_TRACKCAMTGT: nuclear@0: case Discreet3DS::CHUNK_TRACKSPOTL: nuclear@0: case Discreet3DS::CHUNK_TRACKCAMERA: nuclear@0: case Discreet3DS::CHUNK_TRACKINFO: nuclear@0: case Discreet3DS::CHUNK_TRACKLIGHT: nuclear@0: case Discreet3DS::CHUNK_TRACKLIGTGT: nuclear@0: nuclear@0: // this starts a new mesh hierarchy chunk nuclear@0: ParseHierarchyChunk(chunk.Flag); nuclear@0: break; nuclear@0: }; nuclear@0: nuclear@0: ASSIMP_3DS_END_CHUNK(); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Little helper function for ParseHierarchyChunk nuclear@0: void Discreet3DSImporter::InverseNodeSearch(D3DS::Node* pcNode,D3DS::Node* pcCurrent) nuclear@0: { nuclear@0: if (!pcCurrent) { nuclear@0: mRootNode->push_back(pcNode); nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: if (pcCurrent->mHierarchyPos == pcNode->mHierarchyPos) { nuclear@0: if(pcCurrent->mParent) { nuclear@0: pcCurrent->mParent->push_back(pcNode); nuclear@0: } nuclear@0: else pcCurrent->push_back(pcNode); nuclear@0: return; nuclear@0: } nuclear@0: return InverseNodeSearch(pcNode,pcCurrent->mParent); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Find a node with a specific name in the import hierarchy nuclear@0: D3DS::Node* FindNode(D3DS::Node* root, const std::string& name) nuclear@0: { nuclear@0: if (root->mName == name) nuclear@0: return root; nuclear@0: for (std::vector::iterator it = root->mChildren.begin();it != root->mChildren.end(); ++it) { nuclear@0: D3DS::Node* nd; nuclear@0: if (( nd = FindNode(*it,name))) nuclear@0: return nd; nuclear@0: } nuclear@0: return NULL; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Binary predicate for std::unique() nuclear@0: template nuclear@0: bool KeyUniqueCompare(const T& first, const T& second) nuclear@0: { nuclear@0: return first.mTime == second.mTime; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Skip some additional import data. nuclear@0: void Discreet3DSImporter::SkipTCBInfo() nuclear@0: { nuclear@0: unsigned int flags = stream->GetI2(); nuclear@0: nuclear@0: if (!flags) { nuclear@0: // Currently we can't do anything with these values. They occur nuclear@0: // quite rare, so it wouldn't be worth the effort implementing nuclear@0: // them. 3DS ist not really suitable for complex animations, nuclear@0: // so full support is not required. nuclear@0: DefaultLogger::get()->warn("3DS: Skipping TCB animation info"); nuclear@0: } nuclear@0: nuclear@0: if (flags & Discreet3DS::KEY_USE_TENS) { nuclear@0: stream->IncPtr(4); nuclear@0: } nuclear@0: if (flags & Discreet3DS::KEY_USE_BIAS) { nuclear@0: stream->IncPtr(4); nuclear@0: } nuclear@0: if (flags & Discreet3DS::KEY_USE_CONT) { nuclear@0: stream->IncPtr(4); nuclear@0: } nuclear@0: if (flags & Discreet3DS::KEY_USE_EASE_FROM) { nuclear@0: stream->IncPtr(4); nuclear@0: } nuclear@0: if (flags & Discreet3DS::KEY_USE_EASE_TO) { nuclear@0: stream->IncPtr(4); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Read hierarchy and keyframe info nuclear@0: void Discreet3DSImporter::ParseHierarchyChunk(uint16_t parent) nuclear@0: { nuclear@0: ASSIMP_3DS_BEGIN_CHUNK(); nuclear@0: nuclear@0: // get chunk type nuclear@0: switch (chunk.Flag) nuclear@0: { nuclear@0: case Discreet3DS::CHUNK_TRACKOBJNAME: nuclear@0: nuclear@0: // This is the name of the object to which the track applies. The chunk also nuclear@0: // defines the position of this object in the hierarchy. nuclear@0: { nuclear@0: nuclear@0: // First of all: get the name of the object nuclear@0: unsigned int cnt = 0; nuclear@0: const char* sz = (const char*)stream->GetPtr(); nuclear@0: nuclear@0: while (stream->GetI1())++cnt; nuclear@0: std::string name = std::string(sz,cnt); nuclear@0: nuclear@0: // Now find out whether we have this node already (target animation channels nuclear@0: // are stored with a separate object ID) nuclear@0: D3DS::Node* pcNode = FindNode(mRootNode,name); nuclear@0: if (pcNode) nuclear@0: { nuclear@0: // Make this node the current node nuclear@0: mCurrentNode = pcNode; nuclear@0: break; nuclear@0: } nuclear@0: pcNode = new D3DS::Node(); nuclear@0: pcNode->mName = name; nuclear@0: nuclear@0: // There are two unknown values which we can safely ignore nuclear@0: stream->IncPtr(4); nuclear@0: nuclear@0: // Now read the hierarchy position of the object nuclear@0: uint16_t hierarchy = stream->GetI2() + 1; nuclear@0: pcNode->mHierarchyPos = hierarchy; nuclear@0: pcNode->mHierarchyIndex = mLastNodeIndex; nuclear@0: nuclear@0: // And find a proper position in the graph for it nuclear@0: if (mCurrentNode && mCurrentNode->mHierarchyPos == hierarchy) { nuclear@0: nuclear@0: // add to the parent of the last touched node nuclear@0: mCurrentNode->mParent->push_back(pcNode); nuclear@0: mLastNodeIndex++; nuclear@0: } nuclear@0: else if(hierarchy >= mLastNodeIndex) { nuclear@0: nuclear@0: // place it at the current position in the hierarchy nuclear@0: mCurrentNode->push_back(pcNode); nuclear@0: mLastNodeIndex = hierarchy; nuclear@0: } nuclear@0: else { nuclear@0: // need to go back to the specified position in the hierarchy. nuclear@0: InverseNodeSearch(pcNode,mCurrentNode); nuclear@0: mLastNodeIndex++; nuclear@0: } nuclear@0: // Make this node the current node nuclear@0: mCurrentNode = pcNode; nuclear@0: } nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_TRACKDUMMYOBJNAME: nuclear@0: nuclear@0: // This is the "real" name of a $$$DUMMY object nuclear@0: { nuclear@0: const char* sz = (const char*) stream->GetPtr(); nuclear@0: while (stream->GetI1()); nuclear@0: nuclear@0: // If object name is DUMMY, take this one instead nuclear@0: if (mCurrentNode->mName == "$$$DUMMY") { nuclear@0: //DefaultLogger::get()->warn("3DS: Skipping dummy object name for non-dummy object"); nuclear@0: mCurrentNode->mName = std::string(sz); nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_TRACKPIVOT: nuclear@0: nuclear@0: if ( Discreet3DS::CHUNK_TRACKINFO != parent) nuclear@0: { nuclear@0: DefaultLogger::get()->warn("3DS: Skipping pivot subchunk for non usual object"); nuclear@0: break; nuclear@0: } nuclear@0: nuclear@0: // Pivot = origin of rotation and scaling nuclear@0: mCurrentNode->vPivot.x = stream->GetF4(); nuclear@0: mCurrentNode->vPivot.y = stream->GetF4(); nuclear@0: mCurrentNode->vPivot.z = stream->GetF4(); nuclear@0: break; nuclear@0: nuclear@0: nuclear@0: // //////////////////////////////////////////////////////////////////// nuclear@0: // POSITION KEYFRAME nuclear@0: case Discreet3DS::CHUNK_TRACKPOS: nuclear@0: { nuclear@0: stream->IncPtr(10); nuclear@0: const unsigned int numFrames = stream->GetI4(); nuclear@0: bool sortKeys = false; nuclear@0: nuclear@0: // This could also be meant as the target position for nuclear@0: // (targeted) lights and cameras nuclear@0: std::vector* l; nuclear@0: if ( Discreet3DS::CHUNK_TRACKCAMTGT == parent || Discreet3DS::CHUNK_TRACKLIGTGT == parent) { nuclear@0: l = & mCurrentNode->aTargetPositionKeys; nuclear@0: } nuclear@0: else l = & mCurrentNode->aPositionKeys; nuclear@0: nuclear@0: l->reserve(numFrames); nuclear@0: for (unsigned int i = 0; i < numFrames;++i) { nuclear@0: const unsigned int fidx = stream->GetI4(); nuclear@0: nuclear@0: // Setup a new position key nuclear@0: aiVectorKey v; nuclear@0: v.mTime = (double)fidx; nuclear@0: nuclear@0: SkipTCBInfo(); nuclear@0: v.mValue.x = stream->GetF4(); nuclear@0: v.mValue.y = stream->GetF4(); nuclear@0: v.mValue.z = stream->GetF4(); nuclear@0: nuclear@0: // check whether we'll need to sort the keys nuclear@0: if (!l->empty() && v.mTime <= l->back().mTime) nuclear@0: sortKeys = true; nuclear@0: nuclear@0: // Add the new keyframe to the list nuclear@0: l->push_back(v); nuclear@0: } nuclear@0: nuclear@0: // Sort all keys with ascending time values and remove duplicates? nuclear@0: if (sortKeys) { nuclear@0: std::stable_sort(l->begin(),l->end()); nuclear@0: l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare), l->end() ); nuclear@0: }} nuclear@0: nuclear@0: break; nuclear@0: nuclear@0: // //////////////////////////////////////////////////////////////////// nuclear@0: // CAMERA ROLL KEYFRAME nuclear@0: case Discreet3DS::CHUNK_TRACKROLL: nuclear@0: { nuclear@0: // roll keys are accepted for cameras only nuclear@0: if (parent != Discreet3DS::CHUNK_TRACKCAMERA) { nuclear@0: DefaultLogger::get()->warn("3DS: Ignoring roll track for non-camera object"); nuclear@0: break; nuclear@0: } nuclear@0: bool sortKeys = false; nuclear@0: std::vector* l = &mCurrentNode->aCameraRollKeys; nuclear@0: nuclear@0: stream->IncPtr(10); nuclear@0: const unsigned int numFrames = stream->GetI4(); nuclear@0: l->reserve(numFrames); nuclear@0: for (unsigned int i = 0; i < numFrames;++i) { nuclear@0: const unsigned int fidx = stream->GetI4(); nuclear@0: nuclear@0: // Setup a new position key nuclear@0: aiFloatKey v; nuclear@0: v.mTime = (double)fidx; nuclear@0: nuclear@0: // This is just a single float nuclear@0: SkipTCBInfo(); nuclear@0: v.mValue = stream->GetF4(); nuclear@0: nuclear@0: // Check whether we'll need to sort the keys nuclear@0: if (!l->empty() && v.mTime <= l->back().mTime) nuclear@0: sortKeys = true; nuclear@0: nuclear@0: // Add the new keyframe to the list nuclear@0: l->push_back(v); nuclear@0: } nuclear@0: nuclear@0: // Sort all keys with ascending time values and remove duplicates? nuclear@0: if (sortKeys) { nuclear@0: std::stable_sort(l->begin(),l->end()); nuclear@0: l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare), l->end() ); nuclear@0: }} nuclear@0: break; nuclear@0: nuclear@0: nuclear@0: // //////////////////////////////////////////////////////////////////// nuclear@0: // CAMERA FOV KEYFRAME nuclear@0: case Discreet3DS::CHUNK_TRACKFOV: nuclear@0: { nuclear@0: DefaultLogger::get()->error("3DS: Skipping FOV animation track. " nuclear@0: "This is not supported"); nuclear@0: } nuclear@0: break; nuclear@0: nuclear@0: nuclear@0: // //////////////////////////////////////////////////////////////////// nuclear@0: // ROTATION KEYFRAME nuclear@0: case Discreet3DS::CHUNK_TRACKROTATE: nuclear@0: { nuclear@0: stream->IncPtr(10); nuclear@0: const unsigned int numFrames = stream->GetI4(); nuclear@0: nuclear@0: bool sortKeys = false; nuclear@0: std::vector* l = &mCurrentNode->aRotationKeys; nuclear@0: l->reserve(numFrames); nuclear@0: nuclear@0: for (unsigned int i = 0; i < numFrames;++i) { nuclear@0: const unsigned int fidx = stream->GetI4(); nuclear@0: SkipTCBInfo(); nuclear@0: nuclear@0: aiQuatKey v; nuclear@0: v.mTime = (double)fidx; nuclear@0: nuclear@0: // The rotation keyframe is given as an axis-angle pair nuclear@0: const float rad = stream->GetF4(); nuclear@0: aiVector3D axis; nuclear@0: axis.x = stream->GetF4(); nuclear@0: axis.y = stream->GetF4(); nuclear@0: axis.z = stream->GetF4(); nuclear@0: nuclear@0: if (!axis.x && !axis.y && !axis.z) nuclear@0: axis.y = 1.f; nuclear@0: nuclear@0: // Construct a rotation quaternion from the axis-angle pair nuclear@0: v.mValue = aiQuaternion(axis,rad); nuclear@0: nuclear@0: // Check whether we'll need to sort the keys nuclear@0: if (!l->empty() && v.mTime <= l->back().mTime) nuclear@0: sortKeys = true; nuclear@0: nuclear@0: // add the new keyframe to the list nuclear@0: l->push_back(v); nuclear@0: } nuclear@0: // Sort all keys with ascending time values and remove duplicates? nuclear@0: if (sortKeys) { nuclear@0: std::stable_sort(l->begin(),l->end()); nuclear@0: l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare), l->end() ); nuclear@0: }} nuclear@0: break; nuclear@0: nuclear@0: // //////////////////////////////////////////////////////////////////// nuclear@0: // SCALING KEYFRAME nuclear@0: case Discreet3DS::CHUNK_TRACKSCALE: nuclear@0: { nuclear@0: stream->IncPtr(10); nuclear@0: const unsigned int numFrames = stream->GetI2(); nuclear@0: stream->IncPtr(2); nuclear@0: nuclear@0: bool sortKeys = false; nuclear@0: std::vector* l = &mCurrentNode->aScalingKeys; nuclear@0: l->reserve(numFrames); nuclear@0: nuclear@0: for (unsigned int i = 0; i < numFrames;++i) { nuclear@0: const unsigned int fidx = stream->GetI4(); nuclear@0: SkipTCBInfo(); nuclear@0: nuclear@0: // Setup a new key nuclear@0: aiVectorKey v; nuclear@0: v.mTime = (double)fidx; nuclear@0: nuclear@0: // ... and read its value nuclear@0: v.mValue.x = stream->GetF4(); nuclear@0: v.mValue.y = stream->GetF4(); nuclear@0: v.mValue.z = stream->GetF4(); nuclear@0: nuclear@0: // check whether we'll need to sort the keys nuclear@0: if (!l->empty() && v.mTime <= l->back().mTime) nuclear@0: sortKeys = true; nuclear@0: nuclear@0: // Remove zero-scalings on singular axes - they've been reported to be there erroneously in some strange files nuclear@0: if (!v.mValue.x) v.mValue.x = 1.f; nuclear@0: if (!v.mValue.y) v.mValue.y = 1.f; nuclear@0: if (!v.mValue.z) v.mValue.z = 1.f; nuclear@0: nuclear@0: l->push_back(v); nuclear@0: } nuclear@0: // Sort all keys with ascending time values and remove duplicates? nuclear@0: if (sortKeys) { nuclear@0: std::stable_sort(l->begin(),l->end()); nuclear@0: l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare), l->end() ); nuclear@0: }} nuclear@0: break; nuclear@0: }; nuclear@0: nuclear@0: ASSIMP_3DS_END_CHUNK(); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Read a face chunk - it contains smoothing groups and material assignments nuclear@0: void Discreet3DSImporter::ParseFaceChunk() nuclear@0: { nuclear@0: ASSIMP_3DS_BEGIN_CHUNK(); nuclear@0: nuclear@0: // Get the mesh we're currently working on nuclear@0: D3DS::Mesh& mMesh = mScene->mMeshes.back(); nuclear@0: nuclear@0: // Get chunk type nuclear@0: switch (chunk.Flag) nuclear@0: { nuclear@0: case Discreet3DS::CHUNK_SMOOLIST: nuclear@0: { nuclear@0: // This is the list of smoothing groups - a bitfield for every face. nuclear@0: // Up to 32 smoothing groups assigned to a single face. nuclear@0: unsigned int num = chunkSize/4, m = 0; nuclear@0: for (std::vector::iterator i = mMesh.mFaces.begin(); m != num;++i, ++m) { nuclear@0: // nth bit is set for nth smoothing group nuclear@0: (*i).iSmoothGroup = stream->GetI4(); nuclear@0: }} nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_FACEMAT: nuclear@0: { nuclear@0: // at fist an asciiz with the material name nuclear@0: const char* sz = (const char*)stream->GetPtr(); nuclear@0: while (stream->GetI1()); nuclear@0: nuclear@0: // find the index of the material nuclear@0: unsigned int idx = 0xcdcdcdcd, cnt = 0; nuclear@0: for (std::vector::const_iterator i = mScene->mMaterials.begin();i != mScene->mMaterials.end();++i,++cnt) { nuclear@0: // use case independent comparisons. hopefully it will work. nuclear@0: if ((*i).mName.length() && !ASSIMP_stricmp(sz, (*i).mName.c_str())) { nuclear@0: idx = cnt; nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: if (0xcdcdcdcd == idx) { nuclear@0: DefaultLogger::get()->error(std::string("3DS: Unknown material: ") + sz); nuclear@0: } nuclear@0: nuclear@0: // Now continue and read all material indices nuclear@0: cnt = (uint16_t)stream->GetI2(); nuclear@0: for (unsigned int i = 0; i < cnt;++i) { nuclear@0: unsigned int fidx = (uint16_t)stream->GetI2(); nuclear@0: nuclear@0: // check range nuclear@0: if (fidx >= mMesh.mFaceMaterials.size()) { nuclear@0: DefaultLogger::get()->error("3DS: Invalid face index in face material list"); nuclear@0: } nuclear@0: else mMesh.mFaceMaterials[fidx] = idx; nuclear@0: }} nuclear@0: break; nuclear@0: }; nuclear@0: ASSIMP_3DS_END_CHUNK(); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Read a mesh chunk. Here's the actual mesh data nuclear@0: void Discreet3DSImporter::ParseMeshChunk() nuclear@0: { nuclear@0: ASSIMP_3DS_BEGIN_CHUNK(); nuclear@0: nuclear@0: // Get the mesh we're currently working on nuclear@0: D3DS::Mesh& mMesh = mScene->mMeshes.back(); nuclear@0: nuclear@0: // get chunk type nuclear@0: switch (chunk.Flag) nuclear@0: { nuclear@0: case Discreet3DS::CHUNK_VERTLIST: nuclear@0: { nuclear@0: // This is the list of all vertices in the current mesh nuclear@0: int num = (int)(uint16_t)stream->GetI2(); nuclear@0: mMesh.mPositions.reserve(num); nuclear@0: while (num-- > 0) { nuclear@0: aiVector3D v; nuclear@0: v.x = stream->GetF4(); nuclear@0: v.y = stream->GetF4(); nuclear@0: v.z = stream->GetF4(); nuclear@0: mMesh.mPositions.push_back(v); nuclear@0: }} nuclear@0: break; nuclear@0: case Discreet3DS::CHUNK_TRMATRIX: nuclear@0: { nuclear@0: // This is the RLEATIVE transformation matrix of the current mesh. Vertices are nuclear@0: // pretransformed by this matrix wonder. nuclear@0: mMesh.mMat.a1 = stream->GetF4(); nuclear@0: mMesh.mMat.b1 = stream->GetF4(); nuclear@0: mMesh.mMat.c1 = stream->GetF4(); nuclear@0: mMesh.mMat.a2 = stream->GetF4(); nuclear@0: mMesh.mMat.b2 = stream->GetF4(); nuclear@0: mMesh.mMat.c2 = stream->GetF4(); nuclear@0: mMesh.mMat.a3 = stream->GetF4(); nuclear@0: mMesh.mMat.b3 = stream->GetF4(); nuclear@0: mMesh.mMat.c3 = stream->GetF4(); nuclear@0: mMesh.mMat.a4 = stream->GetF4(); nuclear@0: mMesh.mMat.b4 = stream->GetF4(); nuclear@0: mMesh.mMat.c4 = stream->GetF4(); nuclear@0: } nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_MAPLIST: nuclear@0: { nuclear@0: // This is the list of all UV coords in the current mesh nuclear@0: int num = (int)(uint16_t)stream->GetI2(); nuclear@0: mMesh.mTexCoords.reserve(num); nuclear@0: while (num-- > 0) { nuclear@0: aiVector3D v; nuclear@0: v.x = stream->GetF4(); nuclear@0: v.y = stream->GetF4(); nuclear@0: mMesh.mTexCoords.push_back(v); nuclear@0: }} nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_FACELIST: nuclear@0: { nuclear@0: // This is the list of all faces in the current mesh nuclear@0: int num = (int)(uint16_t)stream->GetI2(); nuclear@0: mMesh.mFaces.reserve(num); nuclear@0: while (num-- > 0) { nuclear@0: // 3DS faces are ALWAYS triangles nuclear@0: mMesh.mFaces.push_back(D3DS::Face()); nuclear@0: D3DS::Face& sFace = mMesh.mFaces.back(); nuclear@0: nuclear@0: sFace.mIndices[0] = (uint16_t)stream->GetI2(); nuclear@0: sFace.mIndices[1] = (uint16_t)stream->GetI2(); nuclear@0: sFace.mIndices[2] = (uint16_t)stream->GetI2(); nuclear@0: nuclear@0: stream->IncPtr(2); // skip edge visibility flag nuclear@0: } nuclear@0: nuclear@0: // Resize the material array (0xcdcdcdcd marks the default material; so if a face is nuclear@0: // not referenced by a material, $$DEFAULT will be assigned to it) nuclear@0: mMesh.mFaceMaterials.resize(mMesh.mFaces.size(),0xcdcdcdcd); nuclear@0: nuclear@0: // Larger 3DS files could have multiple FACE chunks here nuclear@0: chunkSize = stream->GetRemainingSizeToLimit(); nuclear@0: if ( chunkSize > (int) sizeof(Discreet3DS::Chunk ) ) nuclear@0: ParseFaceChunk(); nuclear@0: } nuclear@0: break; nuclear@0: }; nuclear@0: ASSIMP_3DS_END_CHUNK(); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Read a 3DS material chunk nuclear@0: void Discreet3DSImporter::ParseMaterialChunk() nuclear@0: { nuclear@0: ASSIMP_3DS_BEGIN_CHUNK(); nuclear@0: switch (chunk.Flag) nuclear@0: { nuclear@0: case Discreet3DS::CHUNK_MAT_MATNAME: nuclear@0: nuclear@0: { nuclear@0: // The material name string is already zero-terminated, but we need to be sure ... nuclear@0: const char* sz = (const char*)stream->GetPtr(); nuclear@0: unsigned int cnt = 0; nuclear@0: while (stream->GetI1()) nuclear@0: ++cnt; nuclear@0: nuclear@0: if (!cnt) { nuclear@0: // This may not be, we use the default name instead nuclear@0: DefaultLogger::get()->error("3DS: Empty material name"); nuclear@0: } nuclear@0: else mScene->mMaterials.back().mName = std::string(sz,cnt); nuclear@0: } nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_MAT_DIFFUSE: nuclear@0: { nuclear@0: // This is the diffuse material color nuclear@0: aiColor3D* pc = &mScene->mMaterials.back().mDiffuse; nuclear@0: ParseColorChunk(pc); nuclear@0: if (is_qnan(pc->r)) { nuclear@0: // color chunk is invalid. Simply ignore it nuclear@0: DefaultLogger::get()->error("3DS: Unable to read DIFFUSE chunk"); nuclear@0: pc->r = pc->g = pc->b = 1.0f; nuclear@0: }} nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_MAT_SPECULAR: nuclear@0: { nuclear@0: // This is the specular material color nuclear@0: aiColor3D* pc = &mScene->mMaterials.back().mSpecular; nuclear@0: ParseColorChunk(pc); nuclear@0: if (is_qnan(pc->r)) { nuclear@0: // color chunk is invalid. Simply ignore it nuclear@0: DefaultLogger::get()->error("3DS: Unable to read SPECULAR chunk"); nuclear@0: pc->r = pc->g = pc->b = 1.0f; nuclear@0: }} nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_MAT_AMBIENT: nuclear@0: { nuclear@0: // This is the ambient material color nuclear@0: aiColor3D* pc = &mScene->mMaterials.back().mAmbient; nuclear@0: ParseColorChunk(pc); nuclear@0: if (is_qnan(pc->r)) { nuclear@0: // color chunk is invalid. Simply ignore it nuclear@0: DefaultLogger::get()->error("3DS: Unable to read AMBIENT chunk"); nuclear@0: pc->r = pc->g = pc->b = 0.0f; nuclear@0: }} nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_MAT_SELF_ILLUM: nuclear@0: { nuclear@0: // This is the emissive material color nuclear@0: aiColor3D* pc = &mScene->mMaterials.back().mEmissive; nuclear@0: ParseColorChunk(pc); nuclear@0: if (is_qnan(pc->r)) { nuclear@0: // color chunk is invalid. Simply ignore it nuclear@0: DefaultLogger::get()->error("3DS: Unable to read EMISSIVE chunk"); nuclear@0: pc->r = pc->g = pc->b = 0.0f; nuclear@0: }} nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_MAT_TRANSPARENCY: nuclear@0: { nuclear@0: // This is the material's transparency nuclear@0: float* pcf = &mScene->mMaterials.back().mTransparency; nuclear@0: *pcf = ParsePercentageChunk(); nuclear@0: nuclear@0: // NOTE: transparency, not opacity nuclear@0: if (is_qnan(*pcf)) nuclear@0: *pcf = 1.0f; nuclear@0: else *pcf = 1.0f - *pcf * (float)0xFFFF / 100.0f; nuclear@0: } nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_MAT_SHADING: nuclear@0: // This is the material shading mode nuclear@0: mScene->mMaterials.back().mShading = (D3DS::Discreet3DS::shadetype3ds)stream->GetI2(); nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_MAT_TWO_SIDE: nuclear@0: // This is the two-sided flag nuclear@0: mScene->mMaterials.back().mTwoSided = true; nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_MAT_SHININESS: nuclear@0: { // This is the shininess of the material nuclear@0: float* pcf = &mScene->mMaterials.back().mSpecularExponent; nuclear@0: *pcf = ParsePercentageChunk(); nuclear@0: if (is_qnan(*pcf)) nuclear@0: *pcf = 0.0f; nuclear@0: else *pcf *= (float)0xFFFF; nuclear@0: } nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_MAT_SHININESS_PERCENT: nuclear@0: { // This is the shininess strength of the material nuclear@0: float* pcf = &mScene->mMaterials.back().mShininessStrength; nuclear@0: *pcf = ParsePercentageChunk(); nuclear@0: if (is_qnan(*pcf)) nuclear@0: *pcf = 0.0f; nuclear@0: else *pcf *= (float)0xffff / 100.0f; nuclear@0: } nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_MAT_SELF_ILPCT: nuclear@0: { // This is the self illumination strength of the material nuclear@0: float f = ParsePercentageChunk(); nuclear@0: if (is_qnan(f)) nuclear@0: f = 0.0f; nuclear@0: else f *= (float)0xFFFF / 100.0f; nuclear@0: mScene->mMaterials.back().mEmissive = aiColor3D(f,f,f); nuclear@0: } nuclear@0: break; nuclear@0: nuclear@0: // Parse texture chunks nuclear@0: case Discreet3DS::CHUNK_MAT_TEXTURE: nuclear@0: // Diffuse texture nuclear@0: ParseTextureChunk(&mScene->mMaterials.back().sTexDiffuse); nuclear@0: break; nuclear@0: case Discreet3DS::CHUNK_MAT_BUMPMAP: nuclear@0: // Height map nuclear@0: ParseTextureChunk(&mScene->mMaterials.back().sTexBump); nuclear@0: break; nuclear@0: case Discreet3DS::CHUNK_MAT_OPACMAP: nuclear@0: // Opacity texture nuclear@0: ParseTextureChunk(&mScene->mMaterials.back().sTexOpacity); nuclear@0: break; nuclear@0: case Discreet3DS::CHUNK_MAT_MAT_SHINMAP: nuclear@0: // Shininess map nuclear@0: ParseTextureChunk(&mScene->mMaterials.back().sTexShininess); nuclear@0: break; nuclear@0: case Discreet3DS::CHUNK_MAT_SPECMAP: nuclear@0: // Specular map nuclear@0: ParseTextureChunk(&mScene->mMaterials.back().sTexSpecular); nuclear@0: break; nuclear@0: case Discreet3DS::CHUNK_MAT_SELFIMAP: nuclear@0: // Self-illumination (emissive) map nuclear@0: ParseTextureChunk(&mScene->mMaterials.back().sTexEmissive); nuclear@0: break; nuclear@0: case Discreet3DS::CHUNK_MAT_REFLMAP: nuclear@0: // Reflection map nuclear@0: ParseTextureChunk(&mScene->mMaterials.back().sTexReflective); nuclear@0: break; nuclear@0: }; nuclear@0: ASSIMP_3DS_END_CHUNK(); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void Discreet3DSImporter::ParseTextureChunk(D3DS::Texture* pcOut) nuclear@0: { nuclear@0: ASSIMP_3DS_BEGIN_CHUNK(); nuclear@0: nuclear@0: // get chunk type nuclear@0: switch (chunk.Flag) nuclear@0: { nuclear@0: case Discreet3DS::CHUNK_MAPFILE: nuclear@0: { nuclear@0: // The material name string is already zero-terminated, but we need to be sure ... nuclear@0: const char* sz = (const char*)stream->GetPtr(); nuclear@0: unsigned int cnt = 0; nuclear@0: while (stream->GetI1()) nuclear@0: ++cnt; nuclear@0: pcOut->mMapName = std::string(sz,cnt); nuclear@0: } nuclear@0: break; nuclear@0: nuclear@0: nuclear@0: case Discreet3DS::CHUNK_PERCENTF: nuclear@0: // Manually parse the blend factor nuclear@0: pcOut->mTextureBlend = stream->GetF4(); nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_PERCENTW: nuclear@0: // Manually parse the blend factor nuclear@0: pcOut->mTextureBlend = (float)((uint16_t)stream->GetI2()) / 100.0f; nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_MAT_MAP_USCALE: nuclear@0: // Texture coordinate scaling in the U direction nuclear@0: pcOut->mScaleU = stream->GetF4(); nuclear@0: if (0.0f == pcOut->mScaleU) nuclear@0: { nuclear@0: DefaultLogger::get()->warn("Texture coordinate scaling in the x direction is zero. Assuming 1."); nuclear@0: pcOut->mScaleU = 1.0f; nuclear@0: } nuclear@0: break; nuclear@0: case Discreet3DS::CHUNK_MAT_MAP_VSCALE: nuclear@0: // Texture coordinate scaling in the V direction nuclear@0: pcOut->mScaleV = stream->GetF4(); nuclear@0: if (0.0f == pcOut->mScaleV) nuclear@0: { nuclear@0: DefaultLogger::get()->warn("Texture coordinate scaling in the y direction is zero. Assuming 1."); nuclear@0: pcOut->mScaleV = 1.0f; nuclear@0: } nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_MAT_MAP_UOFFSET: nuclear@0: // Texture coordinate offset in the U direction nuclear@0: pcOut->mOffsetU = -stream->GetF4(); nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_MAT_MAP_VOFFSET: nuclear@0: // Texture coordinate offset in the V direction nuclear@0: pcOut->mOffsetV = stream->GetF4(); nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_MAT_MAP_ANG: nuclear@0: // Texture coordinate rotation, CCW in DEGREES nuclear@0: pcOut->mRotation = -AI_DEG_TO_RAD( stream->GetF4() ); nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_MAT_MAP_TILING: nuclear@0: { nuclear@0: const uint16_t iFlags = stream->GetI2(); nuclear@0: nuclear@0: // Get the mapping mode (for both axes) nuclear@0: if (iFlags & 0x2u) nuclear@0: pcOut->mMapMode = aiTextureMapMode_Mirror; nuclear@0: nuclear@0: else if (iFlags & 0x10u) nuclear@0: pcOut->mMapMode = aiTextureMapMode_Decal; nuclear@0: nuclear@0: // wrapping in all remaining cases nuclear@0: else pcOut->mMapMode = aiTextureMapMode_Wrap; nuclear@0: } nuclear@0: break; nuclear@0: }; nuclear@0: nuclear@0: ASSIMP_3DS_END_CHUNK(); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Read a percentage chunk nuclear@0: float Discreet3DSImporter::ParsePercentageChunk() nuclear@0: { nuclear@0: Discreet3DS::Chunk chunk; nuclear@0: ReadChunk(&chunk); nuclear@0: nuclear@0: if (Discreet3DS::CHUNK_PERCENTF == chunk.Flag) nuclear@0: return stream->GetF4(); nuclear@0: else if (Discreet3DS::CHUNK_PERCENTW == chunk.Flag) nuclear@0: return (float)((uint16_t)stream->GetI2()) / (float)0xFFFF; nuclear@0: return get_qnan(); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Read a color chunk. If a percentage chunk is found instead it is read as a grayscale color nuclear@0: void Discreet3DSImporter::ParseColorChunk(aiColor3D* out, nuclear@0: bool acceptPercent) nuclear@0: { nuclear@0: ai_assert(out != NULL); nuclear@0: nuclear@0: // error return value nuclear@0: const float qnan = get_qnan(); nuclear@0: static const aiColor3D clrError = aiColor3D(qnan,qnan,qnan); nuclear@0: nuclear@0: Discreet3DS::Chunk chunk; nuclear@0: ReadChunk(&chunk); nuclear@0: const unsigned int diff = chunk.Size - sizeof(Discreet3DS::Chunk); nuclear@0: nuclear@0: bool bGamma = false; nuclear@0: nuclear@0: // Get the type of the chunk nuclear@0: switch(chunk.Flag) nuclear@0: { nuclear@0: case Discreet3DS::CHUNK_LINRGBF: nuclear@0: bGamma = true; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_RGBF: nuclear@0: if (sizeof(float) * 3 > diff) { nuclear@0: *out = clrError; nuclear@0: return; nuclear@0: } nuclear@0: out->r = stream->GetF4(); nuclear@0: out->g = stream->GetF4(); nuclear@0: out->b = stream->GetF4(); nuclear@0: break; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_LINRGBB: nuclear@0: bGamma = true; nuclear@0: case Discreet3DS::CHUNK_RGBB: nuclear@0: if (sizeof(char) * 3 > diff) { nuclear@0: *out = clrError; nuclear@0: return; nuclear@0: } nuclear@0: out->r = (float)(uint8_t)stream->GetI1() / 255.0f; nuclear@0: out->g = (float)(uint8_t)stream->GetI1() / 255.0f; nuclear@0: out->b = (float)(uint8_t)stream->GetI1() / 255.0f; nuclear@0: break; nuclear@0: nuclear@0: // Percentage chunks are accepted, too. nuclear@0: case Discreet3DS::CHUNK_PERCENTF: nuclear@0: if (acceptPercent && 4 <= diff) { nuclear@0: out->g = out->b = out->r = stream->GetF4(); nuclear@0: break; nuclear@0: } nuclear@0: *out = clrError; nuclear@0: return; nuclear@0: nuclear@0: case Discreet3DS::CHUNK_PERCENTW: nuclear@0: if (acceptPercent && 1 <= diff) { nuclear@0: out->g = out->b = out->r = (float)(uint8_t)stream->GetI1() / 255.0f; nuclear@0: break; nuclear@0: } nuclear@0: *out = clrError; nuclear@0: return; nuclear@0: nuclear@0: default: nuclear@0: stream->IncPtr(diff); nuclear@0: // Skip unknown chunks, hope this won't cause any problems. nuclear@0: return ParseColorChunk(out,acceptPercent); nuclear@0: }; nuclear@0: (void)bGamma; nuclear@0: } nuclear@0: nuclear@0: #endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER