1441 lines
42 KiB
C++
1441 lines
42 KiB
C++
/*
|
|
---------------------------------------------------------------------------
|
|
Open Asset Import Library (ASSIMP)
|
|
---------------------------------------------------------------------------
|
|
|
|
Copyright (c) 2006-2008, ASSIMP Development 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 Development Team.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
---------------------------------------------------------------------------
|
|
*/
|
|
|
|
/** @file Implementation of the 3ds importer class */
|
|
|
|
#include "AssimpPCH.h"
|
|
#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
|
|
|
|
// internal headers
|
|
#include "3DSLoader.h"
|
|
|
|
using namespace Assimp;
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Begins a new parsing block
|
|
// - Reads the current chunk and validates it
|
|
// - computes its length
|
|
#define ASSIMP_3DS_BEGIN_CHUNK() \
|
|
Discreet3DS::Chunk chunk; \
|
|
ReadChunk(&chunk); \
|
|
int chunkSize = chunk.Size-sizeof(Discreet3DS::Chunk); \
|
|
const int oldReadLimit = stream->GetReadLimit(); \
|
|
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()
|
|
{}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// 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) const
|
|
{
|
|
// simple check of file extension is enough for the moment
|
|
std::string::size_type pos = pFile.find_last_of('.');
|
|
// no file extension - can't read
|
|
if( pos == std::string::npos)
|
|
return false;
|
|
std::string extension = pFile.substr( pos);
|
|
for (std::string::iterator i = extension.begin(); i != extension.end();++i)
|
|
*i = ::tolower(*i);
|
|
|
|
return (extension == ".3ds");
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// 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 new ImportErrorException("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;
|
|
|
|
// Parse the file
|
|
ParseMainChunk();
|
|
|
|
// Process all meshes in the file. First check whether all
|
|
// face indices haev valid values. The generate our
|
|
// internal verbose representation. Finally compute normal
|
|
// vectors from the smoothing groups we read from the
|
|
// file.
|
|
for (std::vector<D3DS::Mesh>::iterator i = mScene->mMeshes.begin(),
|
|
end = mScene->mMeshes.end(); i != end;++i)
|
|
{
|
|
CheckIndices(*i);
|
|
MakeUnique (*i);
|
|
ComputeNormalsWithSmoothingsGroups<D3DS::Face>(*i);
|
|
}
|
|
|
|
// Replace all occurences 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 new ImportErrorException("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_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();
|
|
// recursively continue processing this hierarchy level
|
|
return ParseEditorChunk();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
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();
|
|
// recursively continue processing this hierarchy level
|
|
return ParseObjectChunk();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Discreet3DSImporter::ParseChunk(const char* name, unsigned int num)
|
|
{
|
|
ASSIMP_3DS_BEGIN_CHUNK();
|
|
|
|
// 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 (excluding color)
|
|
int8_t* p = stream->GetPtr();
|
|
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));
|
|
|
|
// Camera position and look-at vector are difficult to handle.
|
|
// If an animation track is given, we must make sure that
|
|
// the track is relative to these values - or , easier
|
|
// we must copy the information here to the node matrix of
|
|
// the camera's parent in the graph.
|
|
|
|
// 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;
|
|
camera->mLookAt.Normalize();
|
|
|
|
// And finally - the camera rotation angle, in
|
|
// counter clockwise direction
|
|
float angle = AI_DEG_TO_RAD( stream->GetF4() );
|
|
aiQuaternion quat(camera->mLookAt,angle);
|
|
camera->mUp = quat.GetMatrix() * aiVector3D(0.f,1.f,0.f);
|
|
|
|
// 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);
|
|
}
|
|
break;
|
|
};
|
|
ASSIMP_3DS_END_CHUNK();
|
|
// recursively continue processing this hierarchy level
|
|
return ParseChunk(name,num);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Discreet3DSImporter::ParseLightChunk()
|
|
{
|
|
ASSIMP_3DS_BEGIN_CHUNK();
|
|
aiLight* light = mScene->mLights.back();
|
|
|
|
// get chunk type
|
|
switch (chunk.Flag)
|
|
{
|
|
case Discreet3DS::CHUNK_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();
|
|
// recursively continue processing this hierarchy level
|
|
return ParseLightChunk();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
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();
|
|
// recursively continue processing this hierarchy level
|
|
return ParseCameraChunk();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Discreet3DSImporter::ParseKeyframeChunk()
|
|
{
|
|
ASSIMP_3DS_BEGIN_CHUNK();
|
|
|
|
// get chunk type
|
|
switch (chunk.Flag)
|
|
{
|
|
case Discreet3DS::CHUNK_TRACKCAMTGT:
|
|
case Discreet3DS::CHUNK_SPOTLIGHT:
|
|
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();
|
|
|
|
// recursively continue processing this hierarchy level
|
|
return ParseKeyframeChunk();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// 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);
|
|
if (pcNode)
|
|
{
|
|
// Make this node the current node
|
|
mCurrentNode = pcNode;
|
|
break;
|
|
}
|
|
pcNode = new D3DS::Node();
|
|
pcNode->mName = name;
|
|
|
|
// 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
|
|
{
|
|
if (mCurrentNode->mName != "$$$DUMMY")
|
|
{
|
|
DefaultLogger::get()->warn("3DS: Skipping dummy object name for non-dummy object");
|
|
break;
|
|
}
|
|
|
|
const char* sz = (const char*)stream->GetPtr();
|
|
while (stream->GetI1());
|
|
mCurrentNode->mDummyName = std::string(sz);
|
|
}
|
|
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
|
|
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();
|
|
// recursively continue processing this hierarchy level
|
|
return ParseHierarchyChunk(parent);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// 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 frame. Up to 32 smoothing groups assigned to a
|
|
// face.
|
|
unsigned int num = chunkSize/4, m = 0;
|
|
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)
|
|
{
|
|
// compare case-independent to be sure it works
|
|
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);
|
|
|
|
// ******************************************************************
|
|
// This material is not known. Ignore this. We will later
|
|
// assign the default material to all faces using *this*
|
|
// material. We use 0xcdcdcdcd as special value to indicate this.
|
|
// ******************************************************************
|
|
}
|
|
|
|
// 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();
|
|
// recursively continue processing this hierarchy level
|
|
return ParseFaceChunk();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// 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. However, all vertices are pretransformed
|
|
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();
|
|
|
|
// Now check whether the matrix has got a negative determinant
|
|
// If yes, we need to flip all vertices' Z axis ....
|
|
// This code has been taken from lib3ds
|
|
if (mMesh.mMat.Determinant() < 0.0f)
|
|
{
|
|
// Compute the inverse of the matrix
|
|
aiMatrix4x4 mInv = mMesh.mMat;
|
|
mInv.Inverse();
|
|
|
|
aiMatrix4x4 mMe = mMesh.mMat;
|
|
mMe.c1 *= -1.0f;
|
|
mMe.c2 *= -1.0f;
|
|
mMe.c3 *= -1.0f;
|
|
mMe.c4 *= -1.0f;
|
|
mInv = mInv * mMe;
|
|
|
|
// Now transform all vertices
|
|
for (unsigned int i = 0; i < (unsigned int)mMesh.mPositions.size();++i)
|
|
{
|
|
aiVector3D a,c;
|
|
a = mMesh.mPositions[i];
|
|
c[0]= mInv[0][0]*a[0] + mInv[1][0]*a[1] + mInv[2][0]*a[2] + mInv[3][0];
|
|
c[1]= mInv[0][1]*a[0] + mInv[1][1]*a[1] + mInv[2][1]*a[2] + mInv[3][1];
|
|
c[2]= mInv[0][2]*a[0] + mInv[1][2]*a[1] + mInv[2][2]*a[2] + mInv[3][2];
|
|
mMesh.mPositions[i] = c;
|
|
}
|
|
|
|
DefaultLogger::get()->info("3DS: Flipping mesh Z-Axis");
|
|
}}
|
|
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 > sizeof(Discreet3DS::Chunk))
|
|
ParseFaceChunk();
|
|
}
|
|
break;
|
|
};
|
|
ASSIMP_3DS_END_CHUNK();
|
|
// recursively continue processing this hierarchy level
|
|
return ParseMeshChunk();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Read a 3DS material chunk
|
|
void Discreet3DSImporter::ParseMaterialChunk()
|
|
{
|
|
ASSIMP_3DS_BEGIN_CHUNK();
|
|
|
|
// get chunk type
|
|
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
|
|
float* pcf = &mScene->mMaterials.back().mTransparency;
|
|
*pcf = ParsePercentageChunk();
|
|
|
|
// NOTE: transparency, not opacity
|
|
if (is_qnan(*pcf))*pcf = 1.0f;
|
|
else *pcf = 1.0f - *pcf * (float)0xFFFF / 100.0f;
|
|
}
|
|
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
|
|
float* pcf = &mScene->mMaterials.back().mSpecularExponent;
|
|
*pcf = ParsePercentageChunk();
|
|
if (is_qnan(*pcf))*pcf = 0.0f;
|
|
else *pcf *= (float)0xFFFF;
|
|
}
|
|
break;
|
|
|
|
case Discreet3DS::CHUNK_MAT_SHININESS_PERCENT:
|
|
{ // This is the shininess strength of the material
|
|
float* pcf = &mScene->mMaterials.back().mShininessStrength;
|
|
*pcf = ParsePercentageChunk();
|
|
if (is_qnan(*pcf))*pcf = 0.0f;
|
|
else *pcf *= (float)0xffff / 100.0f;
|
|
}
|
|
break;
|
|
|
|
case Discreet3DS::CHUNK_MAT_SELF_ILPCT:
|
|
{ // This is the self illumination strength of the material
|
|
float f = ParsePercentageChunk();
|
|
if (is_qnan(f))f = 0.0f;
|
|
else f *= (float)0xFFFF / 100.0f;
|
|
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 - no support in Assimp
|
|
DefaultLogger::get()->warn("3DS: Found reflection map in file. This is not supported");
|
|
|
|
break;
|
|
};
|
|
ASSIMP_3DS_END_CHUNK();
|
|
// recursively continue processing this hierarchy level
|
|
return ParseMaterialChunk();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
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_PERCENTF:
|
|
// Manually parse the blend factor
|
|
pcOut->mTextureBlend = stream->GetF4();
|
|
break;
|
|
|
|
case Discreet3DS::CHUNK_PERCENTW:
|
|
// Manually parse the blend factor
|
|
pcOut->mTextureBlend = (float)((uint16_t)stream->GetI2()) / 100.0f;
|
|
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();
|
|
// recursively continue processing this hierarchy level
|
|
return ParseTextureChunk(pcOut);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// Read a percentage chunk
|
|
float Discreet3DSImporter::ParsePercentageChunk()
|
|
{
|
|
Discreet3DS::Chunk chunk;
|
|
ReadChunk(&chunk);
|
|
|
|
if (Discreet3DS::CHUNK_PERCENTF == chunk.Flag)
|
|
return stream->GetF4();
|
|
else if (Discreet3DS::CHUNK_PERCENTW == chunk.Flag)
|
|
return (float)((uint16_t)stream->GetI2()) / (float)0xFFFF;
|
|
return std::numeric_limits<float>::quiet_NaN();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// 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 float qnan = std::numeric_limits<float>::quiet_NaN();
|
|
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;
|
|
}
|
|
out->r = (float)(uint8_t)stream->GetI1() / 255.0f;
|
|
out->g = (float)(uint8_t)stream->GetI1() / 255.0f;
|
|
out->b = (float)(uint8_t)stream->GetI1() / 255.0f;
|
|
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 = (float)(uint8_t)stream->GetI1() / 255.0f;
|
|
break;
|
|
}
|
|
*out = clrError;
|
|
return;
|
|
|
|
default:
|
|
stream->IncPtr(diff);
|
|
|
|
// Skip unknown chunks, hope this won't cause any problems.
|
|
return ParseColorChunk(out,acceptPercent);
|
|
};
|
|
}
|
|
|
|
#endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER
|