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 Implementation of the 3ds importer class */ 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: #include "TargetAnimation.h" nuclear@0: nuclear@0: using namespace Assimp; nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Setup final material indices, generae a default material if necessary nuclear@0: void Discreet3DSImporter::ReplaceDefaultMaterial() nuclear@0: { nuclear@0: nuclear@0: // Try to find an existing material that matches the nuclear@0: // typical default material setting: nuclear@0: // - no textures nuclear@0: // - diffuse color (in grey!) nuclear@0: // NOTE: This is here to workaround the fact that some nuclear@0: // exporters are writing a default material, too. nuclear@0: unsigned int idx = 0xcdcdcdcd; nuclear@0: for (unsigned int i = 0; i < mScene->mMaterials.size();++i) nuclear@0: { nuclear@0: std::string s = mScene->mMaterials[i].mName; nuclear@0: for (std::string::iterator it = s.begin(); it != s.end(); ++it) nuclear@0: *it = ::tolower(*it); nuclear@0: nuclear@0: if (std::string::npos == s.find("default"))continue; nuclear@0: nuclear@0: if (mScene->mMaterials[i].mDiffuse.r != nuclear@0: mScene->mMaterials[i].mDiffuse.g || nuclear@0: mScene->mMaterials[i].mDiffuse.r != nuclear@0: mScene->mMaterials[i].mDiffuse.b)continue; nuclear@0: nuclear@0: if (mScene->mMaterials[i].sTexDiffuse.mMapName.length() != 0 || nuclear@0: mScene->mMaterials[i].sTexBump.mMapName.length() != 0 || nuclear@0: mScene->mMaterials[i].sTexOpacity.mMapName.length() != 0 || nuclear@0: mScene->mMaterials[i].sTexEmissive.mMapName.length() != 0 || nuclear@0: mScene->mMaterials[i].sTexSpecular.mMapName.length() != 0 || nuclear@0: mScene->mMaterials[i].sTexShininess.mMapName.length() != 0 ) nuclear@0: { nuclear@0: continue; nuclear@0: } nuclear@0: idx = i; nuclear@0: } nuclear@0: if (0xcdcdcdcd == idx)idx = (unsigned int)mScene->mMaterials.size(); nuclear@0: nuclear@0: // now iterate through all meshes and through all faces and nuclear@0: // find all faces that are using the default material nuclear@0: unsigned int cnt = 0; nuclear@0: for (std::vector::iterator nuclear@0: i = mScene->mMeshes.begin(); nuclear@0: i != mScene->mMeshes.end();++i) nuclear@0: { nuclear@0: for (std::vector::iterator nuclear@0: a = (*i).mFaceMaterials.begin(); nuclear@0: a != (*i).mFaceMaterials.end();++a) nuclear@0: { nuclear@0: // NOTE: The additional check seems to be necessary, nuclear@0: // some exporters seem to generate invalid data here nuclear@0: if (0xcdcdcdcd == (*a)) nuclear@0: { nuclear@0: (*a) = idx; nuclear@0: ++cnt; nuclear@0: } nuclear@0: else if ( (*a) >= mScene->mMaterials.size()) nuclear@0: { nuclear@0: (*a) = idx; nuclear@0: DefaultLogger::get()->warn("Material index overflow in 3DS file. Using default material"); nuclear@0: ++cnt; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: if (cnt && idx == mScene->mMaterials.size()) nuclear@0: { nuclear@0: // We need to create our own default material nuclear@0: D3DS::Material sMat; nuclear@0: sMat.mDiffuse = aiColor3D(0.3f,0.3f,0.3f); nuclear@0: sMat.mName = "%%%DEFAULT"; nuclear@0: mScene->mMaterials.push_back(sMat); nuclear@0: nuclear@0: DefaultLogger::get()->info("3DS: Generating default material"); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Check whether all indices are valid. Otherwise we'd crash before the validation step is reached nuclear@0: void Discreet3DSImporter::CheckIndices(D3DS::Mesh& sMesh) nuclear@0: { nuclear@0: for (std::vector< D3DS::Face >::iterator i = sMesh.mFaces.begin(); i != sMesh.mFaces.end();++i) nuclear@0: { nuclear@0: // check whether all indices are in range nuclear@0: for (unsigned int a = 0; a < 3;++a) nuclear@0: { nuclear@0: if ((*i).mIndices[a] >= sMesh.mPositions.size()) nuclear@0: { nuclear@0: DefaultLogger::get()->warn("3DS: Vertex index overflow)"); nuclear@0: (*i).mIndices[a] = (uint32_t)sMesh.mPositions.size()-1; nuclear@0: } nuclear@0: if ( !sMesh.mTexCoords.empty() && (*i).mIndices[a] >= sMesh.mTexCoords.size()) nuclear@0: { nuclear@0: DefaultLogger::get()->warn("3DS: Texture coordinate index overflow)"); nuclear@0: (*i).mIndices[a] = (uint32_t)sMesh.mTexCoords.size()-1; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Generate out unique verbose format representation nuclear@0: void Discreet3DSImporter::MakeUnique(D3DS::Mesh& sMesh) nuclear@0: { nuclear@0: // TODO: really necessary? I don't think. Just a waste of memory and time nuclear@0: // to do it now in a separate buffer. nuclear@0: nuclear@0: // Allocate output storage nuclear@0: std::vector vNew (sMesh.mFaces.size() * 3); nuclear@0: std::vector vNew2; nuclear@0: if (sMesh.mTexCoords.size()) nuclear@0: vNew2.resize(sMesh.mFaces.size() * 3); nuclear@0: nuclear@0: for (unsigned int i = 0, base = 0; i < sMesh.mFaces.size();++i) nuclear@0: { nuclear@0: D3DS::Face& face = sMesh.mFaces[i]; nuclear@0: nuclear@0: // Positions nuclear@0: for (unsigned int a = 0; a < 3;++a,++base) nuclear@0: { nuclear@0: vNew[base] = sMesh.mPositions[face.mIndices[a]]; nuclear@0: if (sMesh.mTexCoords.size()) nuclear@0: vNew2[base] = sMesh.mTexCoords[face.mIndices[a]]; nuclear@0: nuclear@0: face.mIndices[a] = base; nuclear@0: } nuclear@0: } nuclear@0: sMesh.mPositions = vNew; nuclear@0: sMesh.mTexCoords = vNew2; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Convert a 3DS texture to texture keys in an aiMaterial nuclear@0: void CopyTexture(aiMaterial& mat, D3DS::Texture& texture, aiTextureType type) nuclear@0: { nuclear@0: // Setup the texture name nuclear@0: aiString tex; nuclear@0: tex.Set( texture.mMapName); nuclear@0: mat.AddProperty( &tex, AI_MATKEY_TEXTURE(type,0)); nuclear@0: nuclear@0: // Setup the texture blend factor nuclear@0: if (is_not_qnan(texture.mTextureBlend)) nuclear@0: mat.AddProperty( &texture.mTextureBlend, 1, AI_MATKEY_TEXBLEND(type,0)); nuclear@0: nuclear@0: // Setup the texture mapping mode nuclear@0: mat.AddProperty((int*)&texture.mMapMode,1,AI_MATKEY_MAPPINGMODE_U(type,0)); nuclear@0: mat.AddProperty((int*)&texture.mMapMode,1,AI_MATKEY_MAPPINGMODE_V(type,0)); nuclear@0: nuclear@0: // Mirroring - double the scaling values nuclear@0: // FIXME: this is not really correct ... nuclear@0: if (texture.mMapMode == aiTextureMapMode_Mirror) nuclear@0: { nuclear@0: texture.mScaleU *= 2.f; nuclear@0: texture.mScaleV *= 2.f; nuclear@0: texture.mOffsetU /= 2.f; nuclear@0: texture.mOffsetV /= 2.f; nuclear@0: } nuclear@0: nuclear@0: // Setup texture UV transformations nuclear@0: mat.AddProperty(&texture.mOffsetU,5,AI_MATKEY_UVTRANSFORM(type,0)); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Convert a 3DS material to an aiMaterial nuclear@0: void Discreet3DSImporter::ConvertMaterial(D3DS::Material& oldMat, nuclear@0: aiMaterial& mat) nuclear@0: { nuclear@0: // NOTE: Pass the background image to the viewer by bypassing the nuclear@0: // material system. This is an evil hack, never do it again! nuclear@0: if (0 != mBackgroundImage.length() && bHasBG) nuclear@0: { nuclear@0: aiString tex; nuclear@0: tex.Set( mBackgroundImage); nuclear@0: mat.AddProperty( &tex, AI_MATKEY_GLOBAL_BACKGROUND_IMAGE); nuclear@0: nuclear@0: // Be sure this is only done for the first material nuclear@0: mBackgroundImage = std::string(""); nuclear@0: } nuclear@0: nuclear@0: // At first add the base ambient color of the scene to the material nuclear@0: oldMat.mAmbient.r += mClrAmbient.r; nuclear@0: oldMat.mAmbient.g += mClrAmbient.g; nuclear@0: oldMat.mAmbient.b += mClrAmbient.b; nuclear@0: nuclear@0: aiString name; nuclear@0: name.Set( oldMat.mName); nuclear@0: mat.AddProperty( &name, AI_MATKEY_NAME); nuclear@0: nuclear@0: // Material colors nuclear@0: mat.AddProperty( &oldMat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT); nuclear@0: mat.AddProperty( &oldMat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); nuclear@0: mat.AddProperty( &oldMat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR); nuclear@0: mat.AddProperty( &oldMat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE); nuclear@0: nuclear@0: // Phong shininess and shininess strength nuclear@0: if (D3DS::Discreet3DS::Phong == oldMat.mShading || nuclear@0: D3DS::Discreet3DS::Metal == oldMat.mShading) nuclear@0: { nuclear@0: if (!oldMat.mSpecularExponent || !oldMat.mShininessStrength) nuclear@0: { nuclear@0: oldMat.mShading = D3DS::Discreet3DS::Gouraud; nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: mat.AddProperty( &oldMat.mSpecularExponent, 1, AI_MATKEY_SHININESS); nuclear@0: mat.AddProperty( &oldMat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // Opacity nuclear@0: mat.AddProperty( &oldMat.mTransparency,1,AI_MATKEY_OPACITY); nuclear@0: nuclear@0: // Bump height scaling nuclear@0: mat.AddProperty( &oldMat.mBumpHeight,1,AI_MATKEY_BUMPSCALING); nuclear@0: nuclear@0: // Two sided rendering? nuclear@0: if (oldMat.mTwoSided) nuclear@0: { nuclear@0: int i = 1; nuclear@0: mat.AddProperty(&i,1,AI_MATKEY_TWOSIDED); nuclear@0: } nuclear@0: nuclear@0: // Shading mode nuclear@0: aiShadingMode eShading = aiShadingMode_NoShading; nuclear@0: switch (oldMat.mShading) nuclear@0: { nuclear@0: case D3DS::Discreet3DS::Flat: nuclear@0: eShading = aiShadingMode_Flat; break; nuclear@0: nuclear@0: // I don't know what "Wire" shading should be, nuclear@0: // assume it is simple lambertian diffuse shading nuclear@0: case D3DS::Discreet3DS::Wire: nuclear@0: { nuclear@0: // Set the wireframe flag nuclear@0: unsigned int iWire = 1; nuclear@0: mat.AddProperty( (int*)&iWire,1,AI_MATKEY_ENABLE_WIREFRAME); nuclear@0: } nuclear@0: nuclear@0: case D3DS::Discreet3DS::Gouraud: nuclear@0: eShading = aiShadingMode_Gouraud; break; nuclear@0: nuclear@0: // assume cook-torrance shading for metals. nuclear@0: case D3DS::Discreet3DS::Phong : nuclear@0: eShading = aiShadingMode_Phong; break; nuclear@0: nuclear@0: case D3DS::Discreet3DS::Metal : nuclear@0: eShading = aiShadingMode_CookTorrance; break; nuclear@0: nuclear@0: // FIX to workaround a warning with GCC 4 who complained nuclear@0: // about a missing case Blinn: here - Blinn isn't a valid nuclear@0: // value in the 3DS Loader, it is just needed for ASE nuclear@0: case D3DS::Discreet3DS::Blinn : nuclear@0: eShading = aiShadingMode_Blinn; break; nuclear@0: } nuclear@0: mat.AddProperty( (int*)&eShading,1,AI_MATKEY_SHADING_MODEL); nuclear@0: nuclear@0: // DIFFUSE texture nuclear@0: if( oldMat.sTexDiffuse.mMapName.length() > 0) nuclear@0: CopyTexture(mat,oldMat.sTexDiffuse, aiTextureType_DIFFUSE); nuclear@0: nuclear@0: // SPECULAR texture nuclear@0: if( oldMat.sTexSpecular.mMapName.length() > 0) nuclear@0: CopyTexture(mat,oldMat.sTexSpecular, aiTextureType_SPECULAR); nuclear@0: nuclear@0: // OPACITY texture nuclear@0: if( oldMat.sTexOpacity.mMapName.length() > 0) nuclear@0: CopyTexture(mat,oldMat.sTexOpacity, aiTextureType_OPACITY); nuclear@0: nuclear@0: // EMISSIVE texture nuclear@0: if( oldMat.sTexEmissive.mMapName.length() > 0) nuclear@0: CopyTexture(mat,oldMat.sTexEmissive, aiTextureType_EMISSIVE); nuclear@0: nuclear@0: // BUMP texture nuclear@0: if( oldMat.sTexBump.mMapName.length() > 0) nuclear@0: CopyTexture(mat,oldMat.sTexBump, aiTextureType_HEIGHT); nuclear@0: nuclear@0: // SHININESS texture nuclear@0: if( oldMat.sTexShininess.mMapName.length() > 0) nuclear@0: CopyTexture(mat,oldMat.sTexShininess, aiTextureType_SHININESS); nuclear@0: nuclear@0: // REFLECTION texture nuclear@0: if( oldMat.sTexReflective.mMapName.length() > 0) nuclear@0: CopyTexture(mat,oldMat.sTexReflective, aiTextureType_REFLECTION); nuclear@0: nuclear@0: // Store the name of the material itself, too nuclear@0: if( oldMat.mName.length()) { nuclear@0: aiString tex; nuclear@0: tex.Set( oldMat.mName); nuclear@0: mat.AddProperty( &tex, AI_MATKEY_NAME); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Split meshes by their materials and generate output aiMesh'es nuclear@0: void Discreet3DSImporter::ConvertMeshes(aiScene* pcOut) nuclear@0: { nuclear@0: std::vector avOutMeshes; nuclear@0: avOutMeshes.reserve(mScene->mMeshes.size() * 2); nuclear@0: nuclear@0: unsigned int iFaceCnt = 0,num = 0; nuclear@0: aiString name; nuclear@0: nuclear@0: // we need to split all meshes by their materials nuclear@0: for (std::vector::iterator i = mScene->mMeshes.begin(); i != mScene->mMeshes.end();++i) { nuclear@0: boost::scoped_array< std::vector > aiSplit(new std::vector[mScene->mMaterials.size()]); nuclear@0: nuclear@0: name.length = ASSIMP_itoa10(name.data,num++); nuclear@0: nuclear@0: unsigned int iNum = 0; nuclear@0: for (std::vector::const_iterator a = (*i).mFaceMaterials.begin(); nuclear@0: a != (*i).mFaceMaterials.end();++a,++iNum) nuclear@0: { nuclear@0: aiSplit[*a].push_back(iNum); nuclear@0: } nuclear@0: // now generate submeshes nuclear@0: for (unsigned int p = 0; p < mScene->mMaterials.size();++p) nuclear@0: { nuclear@0: if (aiSplit[p].empty()) { nuclear@0: continue; nuclear@0: } nuclear@0: aiMesh* meshOut = new aiMesh(); nuclear@0: meshOut->mName = name; nuclear@0: meshOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; nuclear@0: nuclear@0: // be sure to setup the correct material index nuclear@0: meshOut->mMaterialIndex = p; nuclear@0: nuclear@0: // use the color data as temporary storage nuclear@0: meshOut->mColors[0] = (aiColor4D*)(&*i); nuclear@0: avOutMeshes.push_back(meshOut); nuclear@0: nuclear@0: // convert vertices nuclear@0: meshOut->mNumFaces = (unsigned int)aiSplit[p].size(); nuclear@0: meshOut->mNumVertices = meshOut->mNumFaces*3; nuclear@0: nuclear@0: // allocate enough storage for faces nuclear@0: meshOut->mFaces = new aiFace[meshOut->mNumFaces]; nuclear@0: iFaceCnt += meshOut->mNumFaces; nuclear@0: nuclear@0: meshOut->mVertices = new aiVector3D[meshOut->mNumVertices]; nuclear@0: meshOut->mNormals = new aiVector3D[meshOut->mNumVertices]; nuclear@0: if ((*i).mTexCoords.size()) nuclear@0: { nuclear@0: meshOut->mTextureCoords[0] = new aiVector3D[meshOut->mNumVertices]; nuclear@0: } nuclear@0: for (unsigned int q = 0, base = 0; q < aiSplit[p].size();++q) nuclear@0: { nuclear@0: register unsigned int index = aiSplit[p][q]; nuclear@0: aiFace& face = meshOut->mFaces[q]; nuclear@0: nuclear@0: face.mIndices = new unsigned int[3]; nuclear@0: face.mNumIndices = 3; nuclear@0: nuclear@0: for (unsigned int a = 0; a < 3;++a,++base) nuclear@0: { nuclear@0: unsigned int idx = (*i).mFaces[index].mIndices[a]; nuclear@0: meshOut->mVertices[base] = (*i).mPositions[idx]; nuclear@0: meshOut->mNormals [base] = (*i).mNormals[idx]; nuclear@0: nuclear@0: if ((*i).mTexCoords.size()) nuclear@0: meshOut->mTextureCoords[0][base] = (*i).mTexCoords[idx]; nuclear@0: nuclear@0: face.mIndices[a] = base; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // Copy them to the output array nuclear@0: pcOut->mNumMeshes = (unsigned int)avOutMeshes.size(); nuclear@0: pcOut->mMeshes = new aiMesh*[pcOut->mNumMeshes](); nuclear@0: for (unsigned int a = 0; a < pcOut->mNumMeshes;++a) { nuclear@0: pcOut->mMeshes[a] = avOutMeshes[a]; nuclear@0: } nuclear@0: nuclear@0: // We should have at least one face here nuclear@0: if (!iFaceCnt) { nuclear@0: throw DeadlyImportError("No faces loaded. The mesh is empty"); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Add a node to the scenegraph and setup its final transformation nuclear@0: void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut, nuclear@0: D3DS::Node* pcIn, aiMatrix4x4& /*absTrafo*/) nuclear@0: { nuclear@0: std::vector iArray; nuclear@0: iArray.reserve(3); nuclear@0: nuclear@0: aiMatrix4x4 abs; nuclear@0: nuclear@0: // Find all meshes with the same name as the node nuclear@0: for (unsigned int a = 0; a < pcSOut->mNumMeshes;++a) nuclear@0: { nuclear@0: const D3DS::Mesh* pcMesh = (const D3DS::Mesh*)pcSOut->mMeshes[a]->mColors[0]; nuclear@0: ai_assert(NULL != pcMesh); nuclear@0: nuclear@0: if (pcIn->mName == pcMesh->mName) nuclear@0: iArray.push_back(a); nuclear@0: } nuclear@0: if (!iArray.empty()) nuclear@0: { nuclear@0: // The matrix should be identical for all meshes with the nuclear@0: // same name. It HAS to be identical for all meshes ..... nuclear@0: D3DS::Mesh* imesh = ((D3DS::Mesh*)pcSOut->mMeshes[iArray[0]]->mColors[0]); nuclear@0: nuclear@0: // Compute the inverse of the transformation matrix to move the nuclear@0: // vertices back to their relative and local space nuclear@0: aiMatrix4x4 mInv = imesh->mMat, mInvTransposed = imesh->mMat; nuclear@0: mInv.Inverse();mInvTransposed.Transpose(); nuclear@0: aiVector3D pivot = pcIn->vPivot; nuclear@0: nuclear@0: pcOut->mNumMeshes = (unsigned int)iArray.size(); nuclear@0: pcOut->mMeshes = new unsigned int[iArray.size()]; nuclear@0: for (unsigned int i = 0;i < iArray.size();++i) { nuclear@0: const unsigned int iIndex = iArray[i]; nuclear@0: aiMesh* const mesh = pcSOut->mMeshes[iIndex]; nuclear@0: nuclear@0: // Transform the vertices back into their local space nuclear@0: // fixme: consider computing normals after this, so we don't need to transform them nuclear@0: const aiVector3D* const pvEnd = mesh->mVertices+mesh->mNumVertices; nuclear@0: aiVector3D* pvCurrent = mesh->mVertices, *t2 = mesh->mNormals; nuclear@0: nuclear@0: for (;pvCurrent != pvEnd;++pvCurrent,++t2) { nuclear@0: *pvCurrent = mInv * (*pvCurrent); nuclear@0: *t2 = mInvTransposed * (*t2); nuclear@0: } nuclear@0: nuclear@0: // Handle negative transformation matrix determinant -> invert vertex x nuclear@0: if (imesh->mMat.Determinant() < 0.0f) nuclear@0: { nuclear@0: /* we *must* have normals */ nuclear@0: for (pvCurrent = mesh->mVertices,t2 = mesh->mNormals;pvCurrent != pvEnd;++pvCurrent,++t2) { nuclear@0: pvCurrent->x *= -1.f; nuclear@0: t2->x *= -1.f; nuclear@0: } nuclear@0: DefaultLogger::get()->info("3DS: Flipping mesh X-Axis"); nuclear@0: } nuclear@0: nuclear@0: // Handle pivot point nuclear@0: if(pivot.x || pivot.y || pivot.z) nuclear@0: { nuclear@0: for (pvCurrent = mesh->mVertices;pvCurrent != pvEnd;++pvCurrent) { nuclear@0: *pvCurrent -= pivot; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // Setup the mesh index nuclear@0: pcOut->mMeshes[i] = iIndex; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // Setup the name of the node nuclear@0: pcOut->mName.Set(pcIn->mName); nuclear@0: nuclear@0: // Now build the transformation matrix of the node nuclear@0: // ROTATION nuclear@0: if (pcIn->aRotationKeys.size()){ nuclear@0: nuclear@0: // FIX to get to Assimp's quaternion conventions nuclear@0: for (std::vector::iterator it = pcIn->aRotationKeys.begin(); it != pcIn->aRotationKeys.end(); ++it) { nuclear@0: (*it).mValue.w *= -1.f; nuclear@0: } nuclear@0: nuclear@0: pcOut->mTransformation = aiMatrix4x4( pcIn->aRotationKeys[0].mValue.GetMatrix() ); nuclear@0: } nuclear@0: else if (pcIn->aCameraRollKeys.size()) nuclear@0: { nuclear@0: aiMatrix4x4::RotationZ(AI_DEG_TO_RAD(- pcIn->aCameraRollKeys[0].mValue), nuclear@0: pcOut->mTransformation); nuclear@0: } nuclear@0: nuclear@0: // SCALING nuclear@0: aiMatrix4x4& m = pcOut->mTransformation; nuclear@0: if (pcIn->aScalingKeys.size()) nuclear@0: { nuclear@0: const aiVector3D& v = pcIn->aScalingKeys[0].mValue; nuclear@0: m.a1 *= v.x; m.b1 *= v.x; m.c1 *= v.x; nuclear@0: m.a2 *= v.y; m.b2 *= v.y; m.c2 *= v.y; nuclear@0: m.a3 *= v.z; m.b3 *= v.z; m.c3 *= v.z; nuclear@0: } nuclear@0: nuclear@0: // TRANSLATION nuclear@0: if (pcIn->aPositionKeys.size()) nuclear@0: { nuclear@0: const aiVector3D& v = pcIn->aPositionKeys[0].mValue; nuclear@0: m.a4 += v.x; nuclear@0: m.b4 += v.y; nuclear@0: m.c4 += v.z; nuclear@0: } nuclear@0: nuclear@0: // Generate animation channels for the node nuclear@0: if (pcIn->aPositionKeys.size() > 1 || pcIn->aRotationKeys.size() > 1 || nuclear@0: pcIn->aScalingKeys.size() > 1 || pcIn->aCameraRollKeys.size() > 1 || nuclear@0: pcIn->aTargetPositionKeys.size() > 1) nuclear@0: { nuclear@0: aiAnimation* anim = pcSOut->mAnimations[0]; nuclear@0: ai_assert(NULL != anim); nuclear@0: nuclear@0: if (pcIn->aCameraRollKeys.size() > 1) nuclear@0: { nuclear@0: DefaultLogger::get()->debug("3DS: Converting camera roll track ..."); nuclear@0: nuclear@0: // Camera roll keys - in fact they're just rotations nuclear@0: // around the camera's z axis. The angles are given nuclear@0: // in degrees (and they're clockwise). nuclear@0: pcIn->aRotationKeys.resize(pcIn->aCameraRollKeys.size()); nuclear@0: for (unsigned int i = 0; i < pcIn->aCameraRollKeys.size();++i) nuclear@0: { nuclear@0: aiQuatKey& q = pcIn->aRotationKeys[i]; nuclear@0: aiFloatKey& f = pcIn->aCameraRollKeys[i]; nuclear@0: nuclear@0: q.mTime = f.mTime; nuclear@0: nuclear@0: // FIX to get to Assimp quaternion conventions nuclear@0: q.mValue = aiQuaternion(0.f,0.f,AI_DEG_TO_RAD( /*-*/ f.mValue)); nuclear@0: } nuclear@0: } nuclear@0: #if 0 nuclear@0: if (pcIn->aTargetPositionKeys.size() > 1) nuclear@0: { nuclear@0: DefaultLogger::get()->debug("3DS: Converting target track ..."); nuclear@0: nuclear@0: // Camera or spot light - need to convert the separate nuclear@0: // target position channel to our representation nuclear@0: TargetAnimationHelper helper; nuclear@0: nuclear@0: if (pcIn->aPositionKeys.empty()) nuclear@0: { nuclear@0: // We can just pass zero here ... nuclear@0: helper.SetFixedMainAnimationChannel(aiVector3D()); nuclear@0: } nuclear@0: else helper.SetMainAnimationChannel(&pcIn->aPositionKeys); nuclear@0: helper.SetTargetAnimationChannel(&pcIn->aTargetPositionKeys); nuclear@0: nuclear@0: // Do the conversion nuclear@0: std::vector distanceTrack; nuclear@0: helper.Process(&distanceTrack); nuclear@0: nuclear@0: // Now add a new node as child, name it .Target nuclear@0: // and assign the distance track to it. This is that the nuclear@0: // information where the target is and how it moves is nuclear@0: // not lost nuclear@0: D3DS::Node* nd = new D3DS::Node(); nuclear@0: pcIn->push_back(nd); nuclear@0: nuclear@0: nd->mName = pcIn->mName + ".Target"; nuclear@0: nuclear@0: aiNodeAnim* nda = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim(); nuclear@0: nda->mNodeName.Set(nd->mName); nuclear@0: nuclear@0: nda->mNumPositionKeys = (unsigned int)distanceTrack.size(); nuclear@0: nda->mPositionKeys = new aiVectorKey[nda->mNumPositionKeys]; nuclear@0: ::memcpy(nda->mPositionKeys,&distanceTrack[0], nuclear@0: sizeof(aiVectorKey)*nda->mNumPositionKeys); nuclear@0: } nuclear@0: #endif nuclear@0: 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: for (unsigned int n = 0; n < pcSOut->mNumCameras;++n) { nuclear@0: if (pcSOut->mCameras[n]->mName == pcOut->mName) { nuclear@0: pcSOut->mCameras[n]->mLookAt = aiVector3D(0.f,0.f,1.f); nuclear@0: } nuclear@0: } nuclear@0: for (unsigned int n = 0; n < pcSOut->mNumLights;++n) { nuclear@0: if (pcSOut->mLights[n]->mName == pcOut->mName) { nuclear@0: pcSOut->mLights[n]->mDirection = aiVector3D(0.f,0.f,1.f); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // Allocate a new node anim and setup its name nuclear@0: aiNodeAnim* nda = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim(); nuclear@0: nda->mNodeName.Set(pcIn->mName); nuclear@0: nuclear@0: // POSITION keys nuclear@0: if (pcIn->aPositionKeys.size() > 0) nuclear@0: { nuclear@0: nda->mNumPositionKeys = (unsigned int)pcIn->aPositionKeys.size(); nuclear@0: nda->mPositionKeys = new aiVectorKey[nda->mNumPositionKeys]; nuclear@0: ::memcpy(nda->mPositionKeys,&pcIn->aPositionKeys[0], nuclear@0: sizeof(aiVectorKey)*nda->mNumPositionKeys); nuclear@0: } nuclear@0: nuclear@0: // ROTATION keys nuclear@0: if (pcIn->aRotationKeys.size() > 0) nuclear@0: { nuclear@0: nda->mNumRotationKeys = (unsigned int)pcIn->aRotationKeys.size(); nuclear@0: nda->mRotationKeys = new aiQuatKey[nda->mNumRotationKeys]; nuclear@0: nuclear@0: // Rotations are quaternion offsets nuclear@0: aiQuaternion abs; nuclear@0: for (unsigned int n = 0; n < nda->mNumRotationKeys;++n) nuclear@0: { nuclear@0: const aiQuatKey& q = pcIn->aRotationKeys[n]; nuclear@0: nuclear@0: abs = (n ? abs * q.mValue : q.mValue); nuclear@0: nda->mRotationKeys[n].mTime = q.mTime; nuclear@0: nda->mRotationKeys[n].mValue = abs.Normalize(); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // SCALING keys nuclear@0: if (pcIn->aScalingKeys.size() > 0) nuclear@0: { nuclear@0: nda->mNumScalingKeys = (unsigned int)pcIn->aScalingKeys.size(); nuclear@0: nda->mScalingKeys = new aiVectorKey[nda->mNumScalingKeys]; nuclear@0: ::memcpy(nda->mScalingKeys,&pcIn->aScalingKeys[0], nuclear@0: sizeof(aiVectorKey)*nda->mNumScalingKeys); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // Allocate storage for children nuclear@0: pcOut->mNumChildren = (unsigned int)pcIn->mChildren.size(); nuclear@0: pcOut->mChildren = new aiNode*[pcIn->mChildren.size()]; nuclear@0: nuclear@0: // Recursively process all children nuclear@0: const unsigned int size = pcIn->mChildren.size(); nuclear@0: for (unsigned int i = 0; i < size;++i) nuclear@0: { nuclear@0: pcOut->mChildren[i] = new aiNode(); nuclear@0: pcOut->mChildren[i]->mParent = pcOut; nuclear@0: AddNodeToGraph(pcSOut,pcOut->mChildren[i],pcIn->mChildren[i],abs); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Find out how many node animation channels we'll have finally nuclear@0: void CountTracks(D3DS::Node* node, unsigned int& cnt) nuclear@0: { nuclear@0: ////////////////////////////////////////////////////////////////////////////// nuclear@0: // We will never generate more than one channel for a node, so nuclear@0: // this is rather easy here. nuclear@0: nuclear@0: if (node->aPositionKeys.size() > 1 || node->aRotationKeys.size() > 1 || nuclear@0: node->aScalingKeys.size() > 1 || node->aCameraRollKeys.size() > 1 || nuclear@0: node->aTargetPositionKeys.size() > 1) nuclear@0: { nuclear@0: ++cnt; nuclear@0: nuclear@0: // account for the additional channel for the camera/spotlight target position nuclear@0: if (node->aTargetPositionKeys.size() > 1)++cnt; nuclear@0: } nuclear@0: nuclear@0: // Recursively process all children nuclear@0: for (unsigned int i = 0; i < node->mChildren.size();++i) nuclear@0: CountTracks(node->mChildren[i],cnt); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Generate the output node graph nuclear@0: void Discreet3DSImporter::GenerateNodeGraph(aiScene* pcOut) nuclear@0: { nuclear@0: pcOut->mRootNode = new aiNode(); nuclear@0: if (0 == mRootNode->mChildren.size()) nuclear@0: { nuclear@0: ////////////////////////////////////////////////////////////////////////////// nuclear@0: // It seems the file is so messed up that it has not even a hierarchy. nuclear@0: // generate a flat hiearachy which looks like this: nuclear@0: // nuclear@0: // ROOT_NODE nuclear@0: // | nuclear@0: // ---------------------------------------- nuclear@0: // | | | | | nuclear@0: // MESH_0 MESH_1 MESH_2 ... MESH_N CAMERA_0 .... nuclear@0: // nuclear@0: DefaultLogger::get()->warn("No hierarchy information has been found in the file. "); nuclear@0: nuclear@0: pcOut->mRootNode->mNumChildren = pcOut->mNumMeshes + nuclear@0: mScene->mCameras.size() + mScene->mLights.size(); nuclear@0: nuclear@0: pcOut->mRootNode->mChildren = new aiNode* [ pcOut->mRootNode->mNumChildren ]; nuclear@0: pcOut->mRootNode->mName.Set("<3DSDummyRoot>"); nuclear@0: nuclear@0: // Build dummy nodes for all meshes nuclear@0: unsigned int a = 0; nuclear@0: for (unsigned int i = 0; i < pcOut->mNumMeshes;++i,++a) nuclear@0: { nuclear@0: aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode(); nuclear@0: pcNode->mParent = pcOut->mRootNode; nuclear@0: pcNode->mMeshes = new unsigned int[1]; nuclear@0: pcNode->mMeshes[0] = i; nuclear@0: pcNode->mNumMeshes = 1; nuclear@0: nuclear@0: // Build a name for the node nuclear@0: pcNode->mName.length = sprintf(pcNode->mName.data,"3DSMesh_%i",i); nuclear@0: } nuclear@0: nuclear@0: // Build dummy nodes for all cameras nuclear@0: for (unsigned int i = 0; i < (unsigned int )mScene->mCameras.size();++i,++a) nuclear@0: { nuclear@0: aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode(); nuclear@0: pcNode->mParent = pcOut->mRootNode; nuclear@0: nuclear@0: // Build a name for the node nuclear@0: pcNode->mName = mScene->mCameras[i]->mName; nuclear@0: } nuclear@0: nuclear@0: // Build dummy nodes for all lights nuclear@0: for (unsigned int i = 0; i < (unsigned int )mScene->mLights.size();++i,++a) nuclear@0: { nuclear@0: aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode(); nuclear@0: pcNode->mParent = pcOut->mRootNode; nuclear@0: nuclear@0: // Build a name for the node nuclear@0: pcNode->mName = mScene->mLights[i]->mName; nuclear@0: } nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: // First of all: find out how many scaling, rotation and translation nuclear@0: // animation tracks we'll have afterwards nuclear@0: unsigned int numChannel = 0; nuclear@0: CountTracks(mRootNode,numChannel); nuclear@0: nuclear@0: if (numChannel) nuclear@0: { nuclear@0: // Allocate a primary animation channel nuclear@0: pcOut->mNumAnimations = 1; nuclear@0: pcOut->mAnimations = new aiAnimation*[1]; nuclear@0: aiAnimation* anim = pcOut->mAnimations[0] = new aiAnimation(); nuclear@0: nuclear@0: anim->mName.Set("3DSMasterAnim"); nuclear@0: nuclear@0: // Allocate enough storage for all node animation channels, nuclear@0: // but don't set the mNumChannels member - we'll use it to nuclear@0: // index into the array nuclear@0: anim->mChannels = new aiNodeAnim*[numChannel]; nuclear@0: } nuclear@0: nuclear@0: aiMatrix4x4 m; nuclear@0: AddNodeToGraph(pcOut, pcOut->mRootNode, mRootNode,m); nuclear@0: } nuclear@0: nuclear@0: // We used the first vertex color set to store some emporary values so we need to cleanup here nuclear@0: for (unsigned int a = 0; a < pcOut->mNumMeshes;++a) nuclear@0: pcOut->mMeshes[a]->mColors[0] = NULL; nuclear@0: nuclear@0: pcOut->mRootNode->mTransformation = aiMatrix4x4( nuclear@0: 1.f,0.f,0.f,0.f, nuclear@0: 0.f,0.f,1.f,0.f, nuclear@0: 0.f,-1.f,0.f,0.f, nuclear@0: 0.f,0.f,0.f,1.f) * pcOut->mRootNode->mTransformation; nuclear@0: nuclear@0: // If the root node is unnamed name it "<3DSRoot>" nuclear@0: if (::strstr( pcOut->mRootNode->mName.data, "UNNAMED" ) || nuclear@0: (pcOut->mRootNode->mName.data[0] == '$' && pcOut->mRootNode->mName.data[1] == '$') ) nuclear@0: { nuclear@0: pcOut->mRootNode->mName.Set("<3DSRoot>"); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Convert all meshes in the scene and generate the final output scene. nuclear@0: void Discreet3DSImporter::ConvertScene(aiScene* pcOut) nuclear@0: { nuclear@0: // Allocate enough storage for all output materials nuclear@0: pcOut->mNumMaterials = (unsigned int)mScene->mMaterials.size(); nuclear@0: pcOut->mMaterials = new aiMaterial*[pcOut->mNumMaterials]; nuclear@0: nuclear@0: // ... and convert the 3DS materials to aiMaterial's nuclear@0: for (unsigned int i = 0; i < pcOut->mNumMaterials;++i) nuclear@0: { nuclear@0: aiMaterial* pcNew = new aiMaterial(); nuclear@0: ConvertMaterial(mScene->mMaterials[i],*pcNew); nuclear@0: pcOut->mMaterials[i] = pcNew; nuclear@0: } nuclear@0: nuclear@0: // Generate the output mesh list nuclear@0: ConvertMeshes(pcOut); nuclear@0: nuclear@0: // Now copy all light sources to the output scene nuclear@0: pcOut->mNumLights = (unsigned int)mScene->mLights.size(); nuclear@0: if (pcOut->mNumLights) nuclear@0: { nuclear@0: pcOut->mLights = new aiLight*[pcOut->mNumLights]; nuclear@0: ::memcpy(pcOut->mLights,&mScene->mLights[0],sizeof(void*)*pcOut->mNumLights); nuclear@0: } nuclear@0: nuclear@0: // Now copy all cameras to the output scene nuclear@0: pcOut->mNumCameras = (unsigned int)mScene->mCameras.size(); nuclear@0: if (pcOut->mNumCameras) nuclear@0: { nuclear@0: pcOut->mCameras = new aiCamera*[pcOut->mNumCameras]; nuclear@0: ::memcpy(pcOut->mCameras,&mScene->mCameras[0],sizeof(void*)*pcOut->mNumCameras); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: #endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER