Rewrote 3DS loader to support BE - added temporary light & camera & animation support. few models not working anymore, transformation bug still unsolved.

Fixed normal vector handling in the ASE loader. Improved animation & camera & light support - as for the 3DS Loader, still in the works.
Fixed AC - Loader. It is now able to load models with complex hierarchies correctly. Added a small workaround for Quick3D, which writes invalid AC files.
DXF-Loader skips {...} blocks produced by 3DS Max & AutoCad correctly now.
DefaultLogger doesn't repeat identical log messages now.
FindInvalidData-Process removes empty or dummy animation tracks now.
LWO-Loader recognizes complex shaders (such as Fresnel) correctly now. Other LLWO bugs still unsolved.
Fixed bugs in the Ply parser, causing non-triangular faces to crash.
Added some additional helperutilities to StreamReader.
Fixed a bug in ProcessHelper.h causing SPlitLargeMeses and SortBxPType to crash for meshes containing many bones.
Changed OB default material from black to gray.
Added additional test fils: ply (bugfix), ase (animation), QUick3D & Irr (in the works), STL (export from 3DS Max)

git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@210 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
This commit is contained in:
aramis_acg
2008-10-31 19:32:00 +00:00
parent 2888a1099e
commit a7088d6263
47 changed files with 3628 additions and 1342 deletions

View File

@@ -49,6 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "MaterialSystem.h"
#include "StringComparison.h"
#include "TextureTransform.h"
#include "SkeletonMeshBuilder.h"
// utilities
#include "fast_atof.h"
@@ -78,12 +79,13 @@ bool ASEImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
return false;
std::string extension = pFile.substr( pos);
// Either ASE or ASK
// Either ASE, ASC or ASK
return !(extension.length() < 4 || extension[0] != '.' ||
extension[1] != 'a' && extension[1] != 'A' ||
extension[2] != 's' && extension[2] != 'S' ||
extension[3] != 'e' && extension[3] != 'E' &&
extension[3] != 'k' && extension[3] != 'K');
extension[3] != 'k' && extension[3] != 'K' &&
extension[3] != 'c' && extension[3] != 'C');
}
// ------------------------------------------------------------------------------------------------
@@ -96,8 +98,8 @@ void ASEImporter::SetupProperties(const Importer* pImp)
// ------------------------------------------------------------------------------------------------
// Imports the given file into the given scene structure.
void ASEImporter::InternReadFile(
const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
void ASEImporter::InternReadFile( const std::string& pFile,
aiScene* pScene, IOSystem* pIOHandler)
{
boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
@@ -122,53 +124,77 @@ void ASEImporter::InternReadFile(
mParser = &parser;
mParser->Parse();
// if absolutely no material has been loaded from the file
// we need to generate a default material
GenerateDefaultMaterial();
// process all meshes
bool tookNormals = false;
std::vector<aiMesh*> avOutMeshes;
avOutMeshes.reserve(mParser->m_vMeshes.size()*2);
for (std::vector<ASE::Mesh>::iterator
i = mParser->m_vMeshes.begin();
i != mParser->m_vMeshes.end();++i)
// Check whether we loaded at least one mesh. If we did - generate
// materials and copy meshes.
if ( !mParser->m_vMeshes.empty())
{
if ((*i).bSkip)continue;
// if absolutely no material has been loaded from the file
// we need to generate a default material
GenerateDefaultMaterial();
TransformVertices(*i);
// process all meshes
bool tookNormals = false;
std::vector<aiMesh*> avOutMeshes;
avOutMeshes.reserve(mParser->m_vMeshes.size()*2);
for (std::vector<ASE::Mesh>::iterator
i = mParser->m_vMeshes.begin();
i != mParser->m_vMeshes.end();++i)
{
if ((*i).bSkip)continue;
// now we need to create proper meshes from the import we need to
// split them by materials, build valid vertex/face lists ...
BuildUniqueRepresentation(*i);
// now we need to create proper meshes from the import we
// need to split them by materials, build valid vertex/
// face lists ...
BuildUniqueRepresentation(*i);
// need to generate proper vertex normals if necessary
if(GenerateNormals(*i))tookNormals = true;
// need to generate proper vertex normals if necessary
if(GenerateNormals(*i))
tookNormals = true;
// convert all meshes to aiMesh objects
ConvertMeshes(*i,avOutMeshes);
}
if (tookNormals)
{
DefaultLogger::get()->debug("ASE: Taking normals from the file. Use "
"the AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS option if you "
"experience problems");
// convert all meshes to aiMesh objects
ConvertMeshes(*i,avOutMeshes);
}
if (tookNormals)
{
DefaultLogger::get()->debug("ASE: Taking normals from the file. Use "
"the AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS option if you "
"experience problems");
}
// now build the output mesh list. Remove dummies
pScene->mNumMeshes = (unsigned int)avOutMeshes.size();
aiMesh** pp = pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
for (std::vector<aiMesh*>::const_iterator
i = avOutMeshes.begin();
i != avOutMeshes.end();++i)
{
if (!(*i)->mNumFaces)continue;
*pp++ = *i;
}
pScene->mNumMeshes = (unsigned int)(pp - pScene->mMeshes);
// build final material indices (remove submaterials and setup
// the final list)
BuildMaterialIndices();
}
// now build the output mesh list. remove dummies
pScene->mNumMeshes = (unsigned int)avOutMeshes.size();
aiMesh** pp = pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
for (std::vector<aiMesh*>::const_iterator
i = avOutMeshes.begin();
i != avOutMeshes.end();++i)
{
if (!(*i)->mNumFaces)continue;
*pp++ = *i;
}
pScene->mNumMeshes = (unsigned int)(pp - pScene->mMeshes);
// Copy all scene graph nodes - lights, cameras, dummies and meshes
// into one large array
nodes.reserve(mParser->m_vMeshes.size() +mParser->m_vLights.size()
+ mParser->m_vCameras.size() + mParser->m_vDummies.size());
// buil final material indices (remove submaterials and make the final list)
BuildMaterialIndices();
for (std::vector<ASE::Light>::iterator it = mParser->m_vLights.begin(),
end = mParser->m_vLights.end();it != end; ++it)nodes.push_back(&(*it));
for (std::vector<ASE::Camera>::iterator it = mParser->m_vCameras.begin(),
end = mParser->m_vCameras.end();it != end; ++it)nodes.push_back(&(*it));
for (std::vector<ASE::Mesh>::iterator it = mParser->m_vMeshes.begin(),
end = mParser->m_vMeshes.end();it != end; ++it)nodes.push_back(&(*it));
for (std::vector<ASE::Dummy>::iterator it = mParser->m_vDummies.begin(),
end = mParser->m_vDummies.end();it != end; ++it)nodes.push_back(&(*it));
// process target cameras and target lights (
// generate animation channels for them and adjust the node graph)
// ProcessTargets();
// build the final node graph
BuildNodes();
@@ -181,6 +207,15 @@ void ASEImporter::InternReadFile(
// build output lights
BuildLights();
// TODO: STRANGE RESULTS ATM
// If we have no meshes use the SkeletonMeshBuilder helper class
// to build a mesh for the animation skeleton
if (!pScene->mNumMeshes)
{
pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
SkeletonMeshBuilder skeleton(pScene);
}
}
// ------------------------------------------------------------------------------------------------
void ASEImporter::GenerateDefaultMaterial()
@@ -208,7 +243,7 @@ void ASEImporter::GenerateDefaultMaterial()
mat.mDiffuse = aiColor3D(0.6f,0.6f,0.6f);
mat.mSpecular = aiColor3D(1.0f,1.0f,1.0f);
mat.mAmbient = aiColor3D(0.05f,0.05f,0.05f);
mat.mShading = Dot3DSFile::Gouraud;
mat.mShading = Discreet3DS::Gouraud;
mat.mName = AI_DEFAULT_MATERIAL_NAME;
}
}
@@ -217,48 +252,69 @@ void ASEImporter::GenerateDefaultMaterial()
void ASEImporter::BuildAnimations()
{
// check whether we have at least one mesh which has animations
std::vector<ASE::Mesh>::iterator i = mParser->m_vMeshes.begin();
std::vector<ASE::BaseNode*>::iterator i = nodes.begin();
unsigned int iNum = 0;
for (;i != mParser->m_vMeshes.end();++i)
for (;i != nodes.end();++i)
{
if ((*i).bSkip)continue;
if ((*i).mAnim.akeyPositions.size() > 1 || (*i).mAnim.akeyRotations.size() > 1)
// TODO: Implement Bezier & TCB support
if ((*i)->mAnim.mPositionType != ASE::Animation::TRACK)
{
DefaultLogger::get()->warn("ASE: Position controller uses Bezier/TCB keys. "
"This is not supported.");
}
if ((*i)->mAnim.mRotationType != ASE::Animation::TRACK)
{
DefaultLogger::get()->warn("ASE: Rotation controller uses Bezier/TCB keys. "
"This is not supported.");
}
if ((*i)->mAnim.mScalingType != ASE::Animation::TRACK)
{
DefaultLogger::get()->warn("ASE: Position controller uses Bezier/TCB keys. "
"This is not supported.");
}
// We compare against 1 here - firstly one key is not
// really an animation and secondly MAX writes dummies
// that represent the node transformation.
if ((*i)->mAnim.akeyPositions.size() > 1 ||
(*i)->mAnim.akeyRotations.size() > 1 ||
(*i)->mAnim.akeyScaling.size() > 1)
{
++iNum;
}
}
if (iNum)
{
// Generate a new animation channel and setup everything for it
pcScene->mNumAnimations = 1;
pcScene->mAnimations = new aiAnimation*[1];
aiAnimation* pcAnim = pcScene->mAnimations[0] = new aiAnimation();
pcAnim->mNumChannels = iNum;
pcAnim->mChannels = new aiNodeAnim*[iNum];
pcScene->mAnimations = new aiAnimation*[1];
aiAnimation* pcAnim = pcScene->mAnimations[0] = new aiAnimation();
pcAnim->mNumChannels = iNum;
pcAnim->mChannels = new aiNodeAnim*[iNum];
pcAnim->mTicksPerSecond = mParser->iFrameSpeed * mParser->iTicksPerFrame;
iNum = 0;
i = mParser->m_vMeshes.begin();
// Now iterate through all meshes and collect all data we can find
for (;i != mParser->m_vMeshes.end();++i)
for (i = nodes.begin();i != nodes.end();++i)
{
if ((*i).bSkip)continue; // mesh unreferenced?
if ((*i).mAnim.akeyPositions.size() > 1 || (*i).mAnim.akeyRotations.size() > 1)
if ((*i)->mAnim.akeyPositions.size() > 1 || (*i)->mAnim.akeyRotations.size() > 1)
{
// Begin a new node animation channel for this node
aiNodeAnim* pcNodeAnim = pcAnim->mChannels[iNum++] = new aiNodeAnim();
pcNodeAnim->mNodeName.Set((*i).mName);
pcNodeAnim->mNodeName.Set((*i)->mName);
// copy position keys
if ((*i).mAnim.akeyPositions.size() > 1 )
if ((*i)->mAnim.akeyPositions.size() > 1 )
{
// Allocate the key array and fill it
pcNodeAnim->mNumPositionKeys = (unsigned int) (*i).mAnim.akeyPositions.size();
pcNodeAnim->mPositionKeys = new aiVectorKey[pcNodeAnim->mNumPositionKeys];
pcNodeAnim->mNumPositionKeys = (unsigned int) (*i)->mAnim.akeyPositions.size();
pcNodeAnim->mPositionKeys = new aiVectorKey[pcNodeAnim->mNumPositionKeys];
::memcpy(pcNodeAnim->mPositionKeys,&(*i).mAnim.akeyPositions[0],
::memcpy(pcNodeAnim->mPositionKeys,&(*i)->mAnim.akeyPositions[0],
pcNodeAnim->mNumPositionKeys * sizeof(aiVectorKey));
// get the longest node anim here
// get the longest node anim channel
for (unsigned int qq = 0; qq < pcNodeAnim->mNumPositionKeys;++qq)
{
pcAnim->mDuration = std::max(pcAnim->mDuration,
@@ -266,23 +322,39 @@ void ASEImporter::BuildAnimations()
}
}
// copy rotation keys
if ((*i).mAnim.akeyRotations.size() > 1 )
if ((*i)->mAnim.akeyRotations.size() > 1 )
{
// Allocate the key array and fill it
pcNodeAnim->mNumRotationKeys = (unsigned int) (*i).mAnim.akeyPositions.size();
pcNodeAnim->mRotationKeys = new aiQuatKey[pcNodeAnim->mNumPositionKeys];
pcNodeAnim->mNumRotationKeys = (unsigned int) (*i)->mAnim.akeyRotations.size();
pcNodeAnim->mRotationKeys = new aiQuatKey[pcNodeAnim->mNumRotationKeys];
::memcpy(pcNodeAnim->mRotationKeys,&(*i).mAnim.akeyRotations[0],
::memcpy(pcNodeAnim->mRotationKeys,&(*i)->mAnim.akeyRotations[0],
pcNodeAnim->mNumRotationKeys * sizeof(aiQuatKey));
// get the longest node anim here
// get the longest node anim channel
for (unsigned int qq = 0; qq < pcNodeAnim->mNumRotationKeys;++qq)
{
pcAnim->mDuration = std::max(pcAnim->mDuration,
pcNodeAnim->mRotationKeys[qq].mTime);
}
}
// there are no scaling keys
// copy scaling keys
if ((*i)->mAnim.akeyScaling.size() > 1 )
{
// Allocate the key array and fill it
pcNodeAnim->mNumScalingKeys = (unsigned int) (*i)->mAnim.akeyScaling.size();
pcNodeAnim->mScalingKeys = new aiVectorKey[pcNodeAnim->mNumScalingKeys];
::memcpy(pcNodeAnim->mScalingKeys,&(*i)->mAnim.akeyScaling[0],
pcNodeAnim->mNumScalingKeys * sizeof(aiVectorKey));
// get the longest node anim channel
for (unsigned int qq = 0; qq < pcNodeAnim->mNumScalingKeys;++qq)
{
pcAnim->mDuration = std::max(pcAnim->mDuration,
pcNodeAnim->mScalingKeys[qq].mTime);
}
}
}
}
}
@@ -306,6 +378,8 @@ void ASEImporter::BuildCameras()
out->mClipPlaneNear = (in.mNear ? in.mNear : 0.1f);
out->mHorizontalFOV = in.mFOV;
// TODO: Implement proper camera target
out->mName.Set(in.mName);
}
}
@@ -324,8 +398,32 @@ void ASEImporter::BuildLights()
aiLight* out = pcScene->mLights[i] = new aiLight();
ASE::Light& in = mParser->m_vLights[i];
// The direction is encoded in the transformation
// matrix of the node. In 3DS MAX the light source
// points in negative Z direction if the node
// transformation is the identity.
out->mDirection = aiVector3D(0.f,0.f,-1.f);
out->mName.Set(in.mName);
out->mType = aiLightSource_POINT;
switch (in.mLightType)
{
case ASE::Light::TARGET:
out->mType = aiLightSource_SPOT;
out->mAngleInnerCone = AI_DEG_TO_RAD(in.mAngle);
out->mAngleOuterCone = (in.mFalloff ? AI_DEG_TO_RAD(in.mFalloff)
: out->mAngleInnerCone);
break;
case ASE::Light::DIRECTIONAL:
out->mType = aiLightSource_DIRECTIONAL;
break;
default:
//case ASE::Light::OMNI:
out->mType = aiLightSource_POINT;
break;
};
out->mColorDiffuse = out->mColorSpecular = in.mColor * in.mIntensity;
}
}
@@ -447,12 +545,12 @@ void ASEImporter::AddNodes (std::vector<BaseNode*>& nodes,
}
// add sub nodes
aiMatrix4x4 mNewAbs = mat * node->mTransformation;
// aiMatrix4x4 mNewAbs = mat * node->mTransformation;
// prevent stack overflow
if (node->mName != node->mParent->mName)
{
AddNodes(nodes,node,node->mName.data,mNewAbs);
AddNodes(nodes,node,node->mName.data,snode->mTransform);
}
}
@@ -479,22 +577,22 @@ void ASEImporter::BuildNodes()
pcScene->mRootNode->mMeshes = 0;
pcScene->mRootNode->mName.Set("<root>");
// generate a full list of all scenegraph elements we have
std::vector<BaseNode*> nodes;
nodes.reserve(mParser->m_vMeshes.size() +mParser->m_vLights.size()
+ mParser->m_vCameras.size());
// Setup the coordinate system transformation
pcScene->mRootNode->mTransformation.c3 *= -1.f;
pcScene->mRootNode->mNumChildren = 1;
pcScene->mRootNode->mChildren = new aiNode*[1];
pcScene->mRootNode->mChildren[0] = new aiNode();
for (std::vector<ASE::Light>::iterator it = mParser->m_vLights.begin(),
end = mParser->m_vLights.end();it != end; ++it)nodes.push_back(&(*it));
for (std::vector<ASE::Camera>::iterator it = mParser->m_vCameras.begin(),
end = mParser->m_vCameras.end();it != end; ++it)nodes.push_back(&(*it));
for (std::vector<ASE::Mesh>::iterator it = mParser->m_vMeshes.begin(),
end = mParser->m_vMeshes.end();it != end; ++it)nodes.push_back(&(*it));
// Change the transformation matrix of all nodes
for (std::vector<BaseNode*>::iterator it = nodes.begin(), end = nodes.end();
it != end; ++it)
{
aiMatrix4x4& m = (*it)->mTransform;
m.Transpose(); // row-order vs column-order
}
// add all nodes
AddNodes(nodes,pcScene->mRootNode,NULL);
AddNodes(nodes,pcScene->mRootNode->mChildren[0],NULL);
// now iterate through al nodes and find those that have not yet
// been added to the nodegraph (= their parent could not be recognized)
@@ -566,11 +664,15 @@ void ASEImporter::BuildNodes()
pcScene->mMeshes[i]->mColors[2] = NULL;
// if there is only one subnode, set it as root node
if (1 == pcScene->mRootNode->mNumChildren)
// FIX: The sub node may not have animations assigned
if (1 == pcScene->mRootNode->mNumChildren && !pcScene->mNumAnimations)
{
aiNode* cc = pcScene->mRootNode->mChildren[0];
aiNode* pc = pcScene->mRootNode;
pcScene->mRootNode = pcScene->mRootNode->mChildren[0];
pcScene->mRootNode = cc;
pcScene->mRootNode->mParent = NULL;
cc->mTransformation = pc->mTransformation * cc->mTransformation;
// make sure the destructor won't delete us ...
delete[] pc->mChildren;
@@ -586,14 +688,6 @@ void ASEImporter::BuildNodes()
return;
}
// ------------------------------------------------------------------------------------------------
void ASEImporter::TransformVertices(ASE::Mesh& mesh)
{
// the matrix data is stored in column-major format,
// but we need row major
mesh.mTransform.Transpose();
}
// ------------------------------------------------------------------------------------------------
void ASEImporter::BuildUniqueRepresentation(ASE::Mesh& mesh)
{
@@ -633,16 +727,16 @@ void ASEImporter::BuildUniqueRepresentation(ASE::Mesh& mesh)
}
// iterate through all faces in the mesh
unsigned int iCurrent = 0;
unsigned int iCurrent = 0, fi = 0;
for (std::vector<ASE::Face>::iterator
i = mesh.mFaces.begin();
i != mesh.mFaces.end();++i)
i != mesh.mFaces.end();++i,++fi)
{
for (unsigned int n = 0; n < 3;++n,++iCurrent)
{
mPositions[iCurrent] = mesh.mPositions[(*i).mIndices[n]];
//std::swap((float&)mPositions[iCurrent].z,(float&)mPositions[iCurrent].y); // DX-to-OGL
mPositions[iCurrent].y *= -1.f;
//mPositions[iCurrent].y *= -1.f;
// add texture coordinates
for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c)
@@ -661,11 +755,11 @@ void ASEImporter::BuildUniqueRepresentation(ASE::Mesh& mesh)
// add normal vectors
if (!mesh.mNormals.empty())
{
mNormals[iCurrent] = mesh.mNormals[(*i).mIndices[n]];
mNormals[iCurrent] = mesh.mNormals[fi*3+n];
mNormals[iCurrent].Normalize();
//std::swap((float&)mNormals[iCurrent].z,(float&)mNormals[iCurrent].y); // DX-to-OGL
mNormals[iCurrent].y *= -1.0f;
//mNormals[iCurrent].y *= -1.0f;
}
// handle bone vertices
@@ -722,11 +816,11 @@ void ASEImporter::ConvertMaterial(ASE::Material& mat)
mat.pcInstance->AddProperty( &mat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH);
}
// if there is no shininess, we can disable phong lighting
else if (Dot3DS::Dot3DSFile::Metal == mat.mShading ||
Dot3DS::Dot3DSFile::Phong == mat.mShading ||
Dot3DS::Dot3DSFile::Blinn == mat.mShading)
else if (D3DS::Discreet3DS::Metal == mat.mShading ||
D3DS::Discreet3DS::Phong == mat.mShading ||
D3DS::Discreet3DS::Blinn == mat.mShading)
{
mat.mShading = Dot3DS::Dot3DSFile::Gouraud;
mat.mShading = D3DS::Discreet3DS::Gouraud;
}
// opacity
@@ -737,24 +831,24 @@ void ASEImporter::ConvertMaterial(ASE::Material& mat)
aiShadingMode eShading = aiShadingMode_NoShading;
switch (mat.mShading)
{
case Dot3DS::Dot3DSFile::Flat:
case D3DS::Discreet3DS::Flat:
eShading = aiShadingMode_Flat; break;
case Dot3DS::Dot3DSFile::Phong :
case D3DS::Discreet3DS::Phong :
eShading = aiShadingMode_Phong; break;
case Dot3DS::Dot3DSFile::Blinn :
case D3DS::Discreet3DS::Blinn :
eShading = aiShadingMode_Blinn; break;
// I don't know what "Wire" shading should be,
// assume it is simple lambertian diffuse (L dot N) shading
case Dot3DS::Dot3DSFile::Wire:
case Dot3DS::Dot3DSFile::Gouraud:
case D3DS::Discreet3DS::Wire:
case D3DS::Discreet3DS::Gouraud:
eShading = aiShadingMode_Gouraud; break;
case Dot3DS::Dot3DSFile::Metal :
case D3DS::Discreet3DS::Metal :
eShading = aiShadingMode_CookTorrance; break;
}
mat.pcInstance->AddProperty<int>( (int*)&eShading,1,AI_MATKEY_SHADING_MODEL);
if (Dot3DS::Dot3DSFile::Wire == mat.mShading)
if (D3DS::Discreet3DS::Wire == mat.mShading)
{
// set the wireframe flag
unsigned int iWire = 1;
@@ -1190,7 +1284,7 @@ void ASEImporter::BuildMaterialIndices()
// allocate the output material array
pcScene->mMaterials = new aiMaterial*[pcScene->mNumMaterials];
Dot3DS::Material** pcIntMaterials = new Dot3DS::Material*[pcScene->mNumMaterials];
D3DS::Material** pcIntMaterials = new D3DS::Material*[pcScene->mNumMaterials];
unsigned int iNum = 0;
for (unsigned int iMat = 0; iMat < mParser->m_vMaterials.size();++iMat)