commit b76f999cb76db1c48c3c2dff33a7e4e438ec0021 Author: kimmi Date: Mon May 5 12:36:31 2008 +0000 Initiual commit: AssetImporter source moved from ZFXCE repository to its own repository. PLease do not use the ZFXCE repo any more. git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@1 67173fc5-114c-0410-ac8e-9d2fd5bffc1f diff --git a/CREDITS b/CREDITS new file mode 100644 index 000000000..e69de29bb diff --git a/INSTALL b/INSTALL new file mode 100644 index 000000000..e69de29bb diff --git a/README b/README new file mode 100644 index 000000000..23ae4ebb6 --- /dev/null +++ b/README @@ -0,0 +1,5 @@ + AssetImporter + --------------- +To use the asset importer lbrary just compile it with visual-c++ 8.0 (at this +moment no other build enviroments are supported). + \ No newline at end of file diff --git a/code/3DSConverter.cpp b/code/3DSConverter.cpp new file mode 100644 index 000000000..fb4cdabb2 --- /dev/null +++ b/code/3DSConverter.cpp @@ -0,0 +1,827 @@ +/** @file Implementation of the 3ds importer class */ +#include "3DSLoader.h" +#include "MaterialSystem.h" + +#include "../include/IOStream.h" +#include "../include/IOSystem.h" +#include "../include/aiMesh.h" +#include "../include/aiScene.h" +#include "../include/aiAssert.h" + +#include + +using namespace Assimp; + + +// ------------------------------------------------------------------------------------------------ +void Dot3DSImporter::ReplaceDefaultMaterial() +{ + // try to find an existing material that matches the + // typical default material setting: + // - no textures + // - diffuse color (in grey!) + // NOTE: This is here to workaround the fact that some + // exporters are writing a default material, too. + unsigned int iIndex = 0xcdcdcdcd; + for (unsigned int i = 0; i < this->mScene->mMaterials.size();++i) + { + if (std::string::npos == this->mScene->mMaterials[i].mName.find("default") && + std::string::npos == this->mScene->mMaterials[i].mName.find("DEFAULT"))continue; + + if (this->mScene->mMaterials[i].mDiffuse.r != + this->mScene->mMaterials[i].mDiffuse.g || + this->mScene->mMaterials[i].mDiffuse.r != + this->mScene->mMaterials[i].mDiffuse.b)continue; + + if (this->mScene->mMaterials[i].sTexDiffuse.mMapName.length() != 0 || + this->mScene->mMaterials[i].sTexBump.mMapName.length()!= 0 || + this->mScene->mMaterials[i].sTexOpacity.mMapName.length() != 0 || + this->mScene->mMaterials[i].sTexEmissive.mMapName.length() != 0 || + this->mScene->mMaterials[i].sTexSpecular.mMapName.length() != 0 || + this->mScene->mMaterials[i].sTexShininess.mMapName.length() != 0 )continue; + + iIndex = i; + } + if (0xcdcdcdcd == iIndex)iIndex = this->mScene->mMaterials.size(); + + // now iterate through all meshes and through all faces and + // find all faces that are using the default material + unsigned int iCnt = 0; + for (std::vector::iterator + i = this->mScene->mMeshes.begin(); + i != this->mScene->mMeshes.end();++i) + { + for (std::vector::iterator + a = (*i).mFaceMaterials.begin(); + a != (*i).mFaceMaterials.end();++a) + { + // NOTE: The additional check seems to be necessary, + // some exporters seem to generate invalid data here + if (0xcdcdcdcd == (*a) || (*a) >= this->mScene->mMaterials.size()) + { + (*a) = iIndex; + ++iCnt; + } + } + } + if (0 != iCnt && iIndex == this->mScene->mMaterials.size()) + { + // we need to create our own default material + Dot3DS::Material sMat; + sMat.mDiffuse = aiColor3D(0.3f,0.3f,0.3f); + sMat.mName = "%%%DEFAULT"; + this->mScene->mMaterials.push_back(sMat); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Dot3DSImporter::CheckIndices(Dot3DS::Mesh* sMesh) +{ + for (std::vector< Dot3DS::Face >::iterator + i = sMesh->mFaces.begin(); + i != sMesh->mFaces.end();++i) + { + // check whether all indices are in range + if ((*i).i1 >= sMesh->mPositions.size()) + { + (*i).i1 = sMesh->mPositions.size()-1; + } + if ((*i).i2 >= sMesh->mPositions.size()) + { + (*i).i2 = sMesh->mPositions.size()-1; + } + if ((*i).i3 >= sMesh->mPositions.size()) + { + (*i).i3 = sMesh->mPositions.size()-1; + } + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Dot3DSImporter::MakeUnique(Dot3DS::Mesh* sMesh) +{ + std::vector vNew; + vNew.resize(sMesh->mFaces.size() * 3); + + std::vector vNew2; + + // TODO: Remove this step. By maintaining a small LUT it + // would be possible to do this directly in the parsing step + unsigned int iBase = 0; + + if (0 != sMesh->mTexCoords.size()) + { + vNew2.resize(sMesh->mFaces.size() * 3); + for (unsigned int i = 0; i < sMesh->mFaces.size();++i) + { + // position and texture coordinates + vNew[iBase] = sMesh->mPositions[sMesh->mFaces[i].i1]; + vNew2[iBase] = sMesh->mTexCoords[sMesh->mFaces[i].i1]; + sMesh->mFaces[i].i1 = iBase++; + + vNew[iBase] = sMesh->mPositions[sMesh->mFaces[i].i2]; + vNew2[iBase] = sMesh->mTexCoords[sMesh->mFaces[i].i2]; + sMesh->mFaces[i].i2 = iBase++; + + vNew[iBase] = sMesh->mPositions[sMesh->mFaces[i].i3]; + vNew2[iBase] = sMesh->mTexCoords[sMesh->mFaces[i].i3]; + sMesh->mFaces[i].i3 = iBase++; + } + } + else + { + for (unsigned int i = 0; i < sMesh->mFaces.size();++i) + { + // position only + vNew[iBase] = sMesh->mPositions[sMesh->mFaces[i].i1]; + sMesh->mFaces[i].i1 = iBase++; + vNew[iBase] = sMesh->mPositions[sMesh->mFaces[i].i2]; + sMesh->mFaces[i].i2 = iBase++; + vNew[iBase] = sMesh->mPositions[sMesh->mFaces[i].i3]; + sMesh->mFaces[i].i3 = iBase++; + } + } + sMesh->mPositions = vNew; + sMesh->mTexCoords = vNew2; + return; +} +// ------------------------------------------------------------------------------------------------ +void Dot3DSImporter::ConvertMaterial(Dot3DS::Material& oldMat, + MaterialHelper& mat) +{ + // NOTE: Pass the background image to the viewer by bypassing the + // material system. This is an evil hack, never do it again! + if (0 != this->mBackgroundImage.length() && this->bHasBG) + { + aiString tex; + tex.Set( this->mBackgroundImage); + mat.AddProperty( &tex, AI_MATKEY_GLOBAL_BACKGROUND_IMAGE); + + // be sure this is only done for the first material + this->mBackgroundImage = std::string(""); + } + + // At first add the base ambient color of the + // scene to the material + oldMat.mAmbient.r += this->mClrAmbient.r; + oldMat.mAmbient.g += this->mClrAmbient.g; + oldMat.mAmbient.b += this->mClrAmbient.b; + + aiString name; + name.Set( oldMat.mName); + mat.AddProperty( &name, AI_MATKEY_NAME); + + // material colors + mat.AddProperty( &oldMat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT); + mat.AddProperty( &oldMat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); + mat.AddProperty( &oldMat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR); + mat.AddProperty( &oldMat.mSpecularExponent, 1, AI_MATKEY_SHININESS); + mat.AddProperty( &oldMat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE); + + // opacity + mat.AddProperty( &oldMat.mTransparency,1,AI_MATKEY_OPACITY); + + // bump height scaling + mat.AddProperty( &oldMat.mBumpHeight,1,AI_MATKEY_BUMPSCALING); + + // shading mode + aiShadingMode eShading = aiShadingMode_NoShading; + switch (oldMat.mShading) + { + case Dot3DS::Dot3DSFile::Flat: + eShading = aiShadingMode_Flat; break; + case Dot3DS::Dot3DSFile::Phong : + eShading = aiShadingMode_Phong; 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: + eShading = aiShadingMode_Gouraud; break; + + // assume cook-torrance shading for metals. + // NOTE: I assume the real shader inside 3ds max is an anisotropic + // Phong-Blinn shader, but this is a good approximation too + case Dot3DS::Dot3DSFile::Metal : + eShading = aiShadingMode_CookTorrance; break; + } + mat.AddProperty( (int*)&eShading,1,AI_MATKEY_SHADING_MODEL); + + // texture, if there is one + if( oldMat.sTexDiffuse.mMapName.length() > 0) + { + aiString tex; + tex.Set( oldMat.sTexDiffuse.mMapName); + mat.AddProperty( &tex, AI_MATKEY_TEXTURE_DIFFUSE(0)); + mat.AddProperty( &oldMat.sTexDiffuse.mTextureBlend, 1, AI_MATKEY_TEXBLEND_DIFFUSE(0)); + } + if( oldMat.sTexSpecular.mMapName.length() > 0) + { + aiString tex; + tex.Set( oldMat.sTexSpecular.mMapName); + mat.AddProperty( &tex, AI_MATKEY_TEXTURE_SPECULAR(0)); + mat.AddProperty( &oldMat.sTexSpecular.mTextureBlend, 1, AI_MATKEY_TEXBLEND_SPECULAR(0)); + } + if( oldMat.sTexOpacity.mMapName.length() > 0) + { + aiString tex; + tex.Set( oldMat.sTexOpacity.mMapName); + mat.AddProperty( &tex, AI_MATKEY_TEXTURE_OPACITY(0)); + mat.AddProperty( &oldMat.sTexOpacity.mTextureBlend, 1,AI_MATKEY_TEXBLEND_OPACITY(0)); + } + if( oldMat.sTexEmissive.mMapName.length() > 0) + { + aiString tex; + tex.Set( oldMat.sTexEmissive.mMapName); + mat.AddProperty( &tex, AI_MATKEY_TEXTURE_EMISSIVE(0)); + mat.AddProperty( &oldMat.sTexEmissive.mTextureBlend, 1, AI_MATKEY_TEXBLEND_EMISSIVE(0)); + } + if( oldMat.sTexBump.mMapName.length() > 0) + { + aiString tex; + tex.Set( oldMat.sTexBump.mMapName); + mat.AddProperty( &tex, AI_MATKEY_TEXTURE_BUMP(0)); + mat.AddProperty( &oldMat.sTexBump.mTextureBlend, 1, AI_MATKEY_TEXBLEND_BUMP(0)); + } + if( oldMat.sTexShininess.mMapName.length() > 0) + { + aiString tex; + tex.Set( oldMat.sTexShininess.mMapName); + mat.AddProperty( &tex, AI_MATKEY_TEXTURE_SHININESS(0)); + mat.AddProperty( &oldMat.sTexBump.mTextureBlend, 1, AI_MATKEY_TEXBLEND_SHININESS(0)); + } + + // store the name of the material itself, too + if( oldMat.mName.length() > 0) + { + aiString tex; + tex.Set( oldMat.mName); + mat.AddProperty( &tex, AI_MATKEY_NAME); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void SetupMatUVSrc (aiMaterial* pcMat, const Dot3DS::Material* pcMatIn) + { + MaterialHelper* pcHelper = (MaterialHelper*)pcMat; + pcHelper->AddProperty(&pcMatIn->sTexDiffuse.iUVSrc,1,AI_MATKEY_UVWSRC_DIFFUSE(0)); + pcHelper->AddProperty(&pcMatIn->sTexSpecular.iUVSrc,1,AI_MATKEY_UVWSRC_SPECULAR(0)); + pcHelper->AddProperty(&pcMatIn->sTexEmissive.iUVSrc,1,AI_MATKEY_UVWSRC_EMISSIVE(0)); + pcHelper->AddProperty(&pcMatIn->sTexBump.iUVSrc,1,AI_MATKEY_UVWSRC_BUMP(0)); + pcHelper->AddProperty(&pcMatIn->sTexShininess.iUVSrc,1,AI_MATKEY_UVWSRC_SHININESS(0)); + pcHelper->AddProperty(&pcMatIn->sTexOpacity.iUVSrc,1,AI_MATKEY_UVWSRC_OPACITY(0)); + } +// ------------------------------------------------------------------------------------------------ +void Dot3DSImporter::ConvertMeshes(aiScene* pcOut) +{ + std::vector avOutMeshes; + avOutMeshes.reserve(this->mScene->mMeshes.size() * 2); + + unsigned int iFaceCnt = 0; + + // we need to split all meshes by their materials + for (std::vector::iterator + i = this->mScene->mMeshes.begin(); + i != this->mScene->mMeshes.end();++i) + { + std::vector* aiSplit = new std::vector[ + this->mScene->mMaterials.size()]; + + unsigned int iNum = 0; + for (std::vector::const_iterator + a = (*i).mFaceMaterials.begin(); + a != (*i).mFaceMaterials.end();++a,++iNum) + { + // check range + if ((*a) >= this->mScene->mMaterials.size()) + { + // use the last material instead + aiSplit[this->mScene->mMaterials.size()-1].push_back(iNum); + } + else aiSplit[*a].push_back(iNum); + } + // now generate submeshes +#if 0 + bool bFirst = true; +#endif + for (unsigned int p = 0; p < this->mScene->mMaterials.size();++p) + { + if (aiSplit[p].size() != 0) + { + aiMesh* p_pcOut = new aiMesh(); + + // be sure to setup the correct material index + p_pcOut->mMaterialIndex = p; + + // use the color data as temporary storage + p_pcOut->mColors[0] = (aiColor4D*)new std::string((*i).mName); + avOutMeshes.push_back(p_pcOut); + +#if 0 + if (bFirst) + { + p_pcOut->mColors[1] = (aiColor4D*)new aiMatrix4x4(); + + *((aiMatrix4x4*)p_pcOut->mColors[1]) = (*i).mMat; + bFirst = false; + } +#endif + + // convert vertices + p_pcOut->mNumVertices = aiSplit[p].size()*3; + p_pcOut->mNumFaces = aiSplit[p].size(); + + iFaceCnt += p_pcOut->mNumFaces; + if (p_pcOut->mNumVertices != 0) + { + p_pcOut->mVertices = new aiVector3D[p_pcOut->mNumVertices]; + p_pcOut->mNormals = new aiVector3D[p_pcOut->mNumVertices]; + unsigned int iBase = 0; + + p_pcOut->mFaces = new aiFace[p_pcOut->mNumFaces]; + for (unsigned int q = 0; q < aiSplit[p].size();++q) + { + unsigned int iIndex = aiSplit[p][q]; + + p_pcOut->mFaces[q].mIndices = new unsigned int[3]; + p_pcOut->mFaces[q].mNumIndices = 3; + + p_pcOut->mFaces[q].mIndices[0] = iBase; + p_pcOut->mVertices[iBase] = (*i).mPositions[(*i).mFaces[iIndex].i1]; + p_pcOut->mNormals[iBase++] = (*i).mNormals[(*i).mFaces[iIndex].i1]; + + p_pcOut->mFaces[q].mIndices[1] = iBase; + p_pcOut->mVertices[iBase] = (*i).mPositions[(*i).mFaces[iIndex].i2]; + p_pcOut->mNormals[iBase++] = (*i).mNormals[(*i).mFaces[iIndex].i2]; + + p_pcOut->mFaces[q].mIndices[2] = iBase; + p_pcOut->mVertices[iBase] = (*i).mPositions[(*i).mFaces[iIndex].i3]; + p_pcOut->mNormals[iBase++] = (*i).mNormals[(*i).mFaces[iIndex].i3]; + } + } + // convert texture coordinates + if ((*i).mTexCoords.size() != 0) + { + p_pcOut->mTextureCoords[0] = new aiVector3D[p_pcOut->mNumVertices]; + + unsigned int iBase = 0; + for (unsigned int q = 0; q < aiSplit[p].size();++q) + { + unsigned int iIndex2 = aiSplit[p][q]; + + unsigned int iIndex = (*i).mFaces[iIndex2].i1; + aiVector2D& pc = (*i).mTexCoords[iIndex]; + p_pcOut->mTextureCoords[0][iBase++] = aiVector3D(pc.x,pc.y,0.0f); + + iIndex = (*i).mFaces[iIndex2].i2; + pc = (*i).mTexCoords[iIndex]; + p_pcOut->mTextureCoords[0][iBase++] = aiVector3D(pc.x,pc.y,0.0f); + + iIndex = (*i).mFaces[iIndex2].i3; + pc = (*i).mTexCoords[iIndex]; + p_pcOut->mTextureCoords[0][iBase++] = aiVector3D(pc.x,pc.y,0.0f); + } + // apply texture coordinate scalings + this->BakeScaleNOffset ( p_pcOut, &this->mScene->mMaterials[ + p_pcOut->mMaterialIndex] ); + + // setup bitflags to indicate which texture coordinate + // channels are used + p_pcOut->mNumUVComponents[0] = 2; + if (p_pcOut->HasTextureCoords(1)) + p_pcOut->mNumUVComponents[1] = 2; + if (p_pcOut->HasTextureCoords(2)) + p_pcOut->mNumUVComponents[2] = 2; + if (p_pcOut->HasTextureCoords(3)) + p_pcOut->mNumUVComponents[3] = 2; + } + } + } + delete[] aiSplit; + } + pcOut->mNumMeshes = avOutMeshes.size(); + pcOut->mMeshes = new aiMesh*[pcOut->mNumMeshes](); + for (unsigned int a = 0; a < pcOut->mNumMeshes;++a) + { + pcOut->mMeshes[a] = avOutMeshes[a]; + } + + if (0 == iFaceCnt) + { + throw new ImportErrorException("No faces loaded. The mesh is empty"); + } + + // for each material in the scene we need to setup the UV source + // set for each texture + for (unsigned int a = 0; a < pcOut->mNumMaterials;++a) + { + SetupMatUVSrc( pcOut->mMaterials[a], &this->mScene->mMaterials[a] ); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Dot3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,Dot3DS::Node* pcIn) +{ + // find the corresponding mesh indices + std::vector iArray; + + if (pcIn->mName != "$$$DUMMY") + { + for (unsigned int a = 0; a < pcSOut->mNumMeshes;++a) + { + if (0 == ASSIMP_stricmp(pcIn->mName.c_str(), + ((std::string*)pcSOut->mMeshes[a]->mColors[0])->c_str())) + { + iArray.push_back(a); + } + } + } + pcOut->mName.Set(pcIn->mName); + pcOut->mNumMeshes = iArray.size(); + pcOut->mMeshes = new unsigned int[iArray.size()]; + + for (unsigned int i = 0;i < iArray.size();++i) + { + const unsigned int iIndex = iArray[i]; +#if 0 + if (NULL != pcSOut->mMeshes[iIndex]->mColors[1]) + { + pcOut->mTransformation = *((aiMatrix4x4*) + (pcSOut->mMeshes[iIndex]->mColors[1])); + + delete (aiMatrix4x4*)pcSOut->mMeshes[iIndex]->mColors[1]; + pcSOut->mMeshes[iIndex]->mColors[1] = NULL; + } +#endif + pcOut->mMeshes[i] = iIndex; + } + + // NOTE: Not necessary. We can use the given transformation matrix. + // However, we'd need it if we wanted to implement keyframe animation + +#if 0 + // build the scaling matrix. Toggle y and z axis + aiMatrix4x4 mS; + mS.a1 = pcIn->vScaling.x; + mS.b2 = pcIn->vScaling.z; + mS.c3 = pcIn->vScaling.y; + + // build the translation matrix. Toggle y and z axis + aiMatrix4x4 mT; + mT.a4 = pcIn->vPosition.x; + mT.b4 = pcIn->vPosition.z; + mT.c4 = pcIn->vPosition.y; + + // build the pivot matrix. Toggle y and z axis + aiMatrix4x4 mP; + mP.a4 = pcIn->vPivot.x; + mP.b4 = pcIn->vPivot.z; + mP.c4 = pcIn->vPivot.y; +#endif + pcOut->mTransformation = aiMatrix4x4(); // mT * pcIn->mRotation * mS * mP * pcOut->mTransformation.Inverse(); + + pcOut->mNumChildren = pcIn->mChildren.size(); + pcOut->mChildren = new aiNode*[pcIn->mChildren.size()]; + for (unsigned int i = 0; i < pcIn->mChildren.size();++i) + { + pcOut->mChildren[i] = new aiNode(); + pcOut->mChildren[i]->mParent = pcOut; + AddNodeToGraph(pcSOut,pcOut->mChildren[i], + pcIn->mChildren[i]); + } + return; +} +// ------------------------------------------------------------------------------------------------ +inline bool HasUVTransform(const Dot3DS::Texture& rcIn) + { + return (0.0f != rcIn.mOffsetU || + 0.0f != rcIn.mOffsetV || + 1.0f != rcIn.mScaleU || + 1.0f != rcIn.mScaleV || + 0.0f != rcIn.mRotation); + } +// ------------------------------------------------------------------------------------------------ +void Dot3DSImporter::ApplyScaleNOffset() + { + unsigned int iNum = 0; + for (std::vector::iterator + i = this->mScene->mMaterials.begin(); + i != this->mScene->mMaterials.end();++i,++iNum) + { + unsigned int iCnt = 0; + Dot3DS::Texture* pcTexture = NULL; + if (HasUVTransform((*i).sTexDiffuse)) + { + (*i).sTexDiffuse.bPrivate = true; + pcTexture = &(*i).sTexDiffuse; + ++iCnt; + } + if (HasUVTransform((*i).sTexSpecular)) + { + (*i).sTexSpecular.bPrivate = true; + pcTexture = &(*i).sTexSpecular; + ++iCnt; + } + if (HasUVTransform((*i).sTexOpacity)) + { + (*i).sTexOpacity.bPrivate = true; + pcTexture = &(*i).sTexOpacity; + ++iCnt; + } + if (HasUVTransform((*i).sTexEmissive)) + { + (*i).sTexEmissive.bPrivate = true; + pcTexture = &(*i).sTexEmissive; + ++iCnt; + } + if (HasUVTransform((*i).sTexBump)) + { + (*i).sTexBump.bPrivate = true; + pcTexture = &(*i).sTexBump; + ++iCnt; + } + if (HasUVTransform((*i).sTexShininess)) + { + (*i).sTexShininess.bPrivate = true; + pcTexture = &(*i).sTexShininess; + ++iCnt; + } + if (0 != iCnt) + { + // if only one texture needs scaling/offset operations + // we can apply them directly to the first texture + // coordinate sets of all meshes referencing *this* material + // However, we can't do it now. We need to wait until + // everything is sorted by materials. + if (1 == iCnt) + { + (*i).iBakeUVTransform = 1; + (*i).pcSingleTexture = pcTexture; + } + // we will need to generate a separate new texture channel + // for each texture. + // However, we can't do it now. We need to wait until + // everything is sorted by materials. + else (*i).iBakeUVTransform = 2; + } + } + } +// ------------------------------------------------------------------------------------------------ +struct STransformVecInfo + { + float fScaleU; + float fScaleV; + float fOffsetU; + float fOffsetV; + float fRotation; + + std::vector pcTextures; + }; +// ------------------------------------------------------------------------------------------------ +void AddToList(std::vector& rasVec,Dot3DS::Texture* pcTex) + { + if (0 == pcTex->mMapName.length())return; + + for (std::vector::iterator + i = rasVec.begin(); + i != rasVec.end();++i) + { + if ((*i).fOffsetU == pcTex->mOffsetU && + (*i).fOffsetV == pcTex->mOffsetV && + (*i).fScaleU == pcTex->mScaleU && + (*i).fScaleV == pcTex->mScaleV && + (*i).fRotation == pcTex->mRotation) + { + (*i).pcTextures.push_back(pcTex); + return; + } + } + STransformVecInfo sInfo; + sInfo.fScaleU = pcTex->mScaleU; + sInfo.fScaleV = pcTex->mScaleV; + sInfo.fOffsetU = pcTex->mOffsetU; + sInfo.fOffsetV = pcTex->mOffsetV; + sInfo.fRotation = pcTex->mRotation; + sInfo.pcTextures.push_back(pcTex); + + rasVec.push_back(sInfo); + } +// ------------------------------------------------------------------------------------------------ +void Dot3DSImporter::BakeScaleNOffset( + aiMesh* pcMesh, Dot3DS::Material* pcSrc) + { + if (!pcMesh->mTextureCoords[0])return; + if (1 == pcSrc->iBakeUVTransform) + { + if (0.0f == pcSrc->pcSingleTexture->mRotation) + { + for (unsigned int i = 0; i < pcMesh->mNumVertices;++i) + { + pcMesh->mTextureCoords[0][i].x *= pcSrc->pcSingleTexture->mScaleU; + pcMesh->mTextureCoords[0][i].y *= pcSrc->pcSingleTexture->mScaleV; + + pcMesh->mTextureCoords[0][i].x += pcSrc->pcSingleTexture->mOffsetU; + pcMesh->mTextureCoords[0][i].y += pcSrc->pcSingleTexture->mOffsetV; + } + } + else + { + const float fSin = sinf(pcSrc->pcSingleTexture->mRotation); + const float fCos = cosf(pcSrc->pcSingleTexture->mRotation); + for (unsigned int i = 0; i < pcMesh->mNumVertices;++i) + { + pcMesh->mTextureCoords[0][i].x *= pcSrc->pcSingleTexture->mScaleU; + pcMesh->mTextureCoords[0][i].y *= pcSrc->pcSingleTexture->mScaleV; + + pcMesh->mTextureCoords[0][i].x *= fCos; + pcMesh->mTextureCoords[0][i].y *= fSin; + + pcMesh->mTextureCoords[0][i].x += pcSrc->pcSingleTexture->mOffsetU; + pcMesh->mTextureCoords[0][i].y += pcSrc->pcSingleTexture->mOffsetV; + } + } + } + else if (2 == pcSrc->iBakeUVTransform) + { + // now we need to find all textures in the material + // which require scaling/offset operations + std::vector sOps; + AddToList(sOps,&pcSrc->sTexDiffuse); + AddToList(sOps,&pcSrc->sTexSpecular); + AddToList(sOps,&pcSrc->sTexEmissive); + AddToList(sOps,&pcSrc->sTexOpacity); + AddToList(sOps,&pcSrc->sTexBump); + AddToList(sOps,&pcSrc->sTexShininess); + + const aiVector3D* _pvBase; + if (0.0f == sOps[0].fOffsetU && 0.0f == sOps[0].fOffsetV && + 1.0f == sOps[0].fScaleU && 1.0f == sOps[0].fScaleV && + 0.0f == sOps[0].fRotation) + { + // we'll have an unmodified set, so we can use *this* one + _pvBase = pcMesh->mTextureCoords[0]; + } + else + { + _pvBase = new aiVector3D[pcMesh->mNumVertices]; + memcpy(const_cast(_pvBase),pcMesh->mTextureCoords[0], + pcMesh->mNumVertices * sizeof(aiVector3D)); + } + + unsigned int iCnt = 0; + for (std::vector::iterator + i = sOps.begin(); + i != sOps.end();++i,++iCnt) + { + if (!pcMesh->mTextureCoords[iCnt]) + { + pcMesh->mTextureCoords[iCnt] = new aiVector3D[pcMesh->mNumVertices]; + } + // more than 4 UV texture channels are not available + if (iCnt > 3) + { + for (std::vector::iterator + a = (*i).pcTextures.begin(); + a != (*i).pcTextures.end();++a) + { + (*a)->iUVSrc = 0; + } + continue; + } + const aiVector3D* pvBase = _pvBase; + + if (0.0f == (*i).fRotation) + { + for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) + { + pcMesh->mTextureCoords[iCnt][n].x = pvBase->x * (*i).fScaleU; + pcMesh->mTextureCoords[iCnt][n].y = pvBase->y * (*i).fScaleV; + + pcMesh->mTextureCoords[iCnt][n].x += (*i).fOffsetU; + pcMesh->mTextureCoords[iCnt][n].y += (*i).fOffsetV; + + pvBase++; + } + } + else + { + const float fSin = sinf((*i).fRotation); + const float fCos = cosf((*i).fRotation); + for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) + { + pcMesh->mTextureCoords[iCnt][n].x = pvBase->x * (*i).fScaleU; + pcMesh->mTextureCoords[iCnt][n].y = pvBase->y * (*i).fScaleV; + + pcMesh->mTextureCoords[iCnt][n].x *= fCos; + pcMesh->mTextureCoords[iCnt][n].y *= fSin; + + pcMesh->mTextureCoords[iCnt][n].x += (*i).fOffsetU; + pcMesh->mTextureCoords[iCnt][n].y += (*i).fOffsetV; + + pvBase++; + } + } + // setup UV source + for (std::vector::iterator + a = (*i).pcTextures.begin(); + a != (*i).pcTextures.end();++a) + { + (*a)->iUVSrc = iCnt; + } + } + + // release temporary storage + if (_pvBase != pcMesh->mTextureCoords[0]) + delete[] _pvBase; + } + } +// ------------------------------------------------------------------------------------------------ +void Dot3DSImporter::GenerateNodeGraph(aiScene* pcOut) +{ + pcOut->mRootNode = new aiNode(); + + if (0 == this->mRootNode->mChildren.size()) + { + // seems the file has not even a hierarchy. + // generate a flat hiearachy which looks like this: + // + // ROOT_NODE + // | + // ---------------------------------------- + // | | | | + // MESH_0 MESH_1 MESH_2 ... MESH_N + // + unsigned int iCnt = 0; + + pcOut->mRootNode->mNumChildren = pcOut->mNumMeshes; + pcOut->mRootNode->mChildren = new aiNode* [ pcOut->mNumMeshes ]; + + for (unsigned int i = 0; i < pcOut->mNumMeshes;++i) + { + aiNode* pcNode = new aiNode(); + pcNode->mParent = pcOut->mRootNode; + pcNode->mNumChildren = 0; + pcNode->mChildren = 0; + pcNode->mMeshes = new unsigned int[1]; + pcNode->mMeshes[0] = i; + pcNode->mNumMeshes = 1; + pcNode->mName.Set("UNNAMED"); + + // add the new child to the parent node + pcOut->mRootNode->mChildren[i] = pcNode; + } + } + else this->AddNodeToGraph(pcOut, pcOut->mRootNode, this->mRootNode); + + for (unsigned int a = 0; a < pcOut->mNumMeshes;++a) + { + delete (std::string*)pcOut->mMeshes[a]->mColors[0]; + pcOut->mMeshes[a]->mColors[0] = NULL; + + // may be NULL + delete (aiMatrix4x4*)pcOut->mMeshes[a]->mColors[1]; + pcOut->mMeshes[a]->mColors[1] = NULL; + } +} +// ------------------------------------------------------------------------------------------------ +void Dot3DSImporter::ConvertScene(aiScene* pcOut) +{ + pcOut->mNumMaterials = this->mScene->mMaterials.size(); + pcOut->mMaterials = new aiMaterial*[pcOut->mNumMaterials]; + + for (unsigned int i = 0; i < pcOut->mNumMaterials;++i) + { + MaterialHelper* pcNew = new MaterialHelper(); + this->ConvertMaterial(this->mScene->mMaterials[i],*pcNew); + pcOut->mMaterials[i] = pcNew; + } + this->ConvertMeshes(pcOut); + return; +} +// ------------------------------------------------------------------------------------------------ +void Dot3DSImporter::GenTexCoord (Dot3DS::Texture* pcTexture, + const std::vector& p_vIn, + std::vector& p_vOut) +{ + p_vOut.resize(p_vIn.size()); + + std::vector::const_iterator i = p_vIn.begin(); + std::vector::iterator a = p_vOut.begin(); + for(;i != p_vOut.end();++i,++a) + { + // TODO: Find out in which order 3ds max is performing + // scaling and translation. However it seems reasonable to + // scale first. + // + // TODO: http://www.jalix.org/ressources/graphics/3DS/_specifications/3ds-0.1.htm + // says it is not u and v scale but 1/u and 1/v scale. Other sources + // tell different things. Believe this one, the author seems to be funny + // or drunken or both ;-) + (*a) = (*i); + (*a).x /= pcTexture->mScaleU; + (*a).y /= pcTexture->mScaleV; + (*a).x += pcTexture->mOffsetU; + (*a).y += pcTexture->mOffsetV; + } + return; +} \ No newline at end of file diff --git a/code/3DSGenNormals.cpp b/code/3DSGenNormals.cpp new file mode 100644 index 000000000..661bf711a --- /dev/null +++ b/code/3DSGenNormals.cpp @@ -0,0 +1,134 @@ +/** @file Implementation of the 3ds importer class */ +#include "3DSLoader.h" +#include "MaterialSystem.h" +#include +#include "../include/IOStream.h" +#include "../include/IOSystem.h" +#include "../include/aiMesh.h" +#include "../include/aiScene.h" +#include "../include/aiAssert.h" +#include "3DSSpatialSort.h" + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +void Dot3DSImporter::GenNormals(Dot3DS::Mesh* sMesh) +{ + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // First generate face normals + sMesh->mNormals.resize(sMesh->mPositions.size(),aiVector3D()); + for( unsigned int a = 0; a < sMesh->mFaces.size(); a++) + { + const Dot3DS::Face& face = sMesh->mFaces[a]; + + // assume it is a triangle + aiVector3D* pV1 = &sMesh->mPositions[face.i1]; + aiVector3D* pV2 = &sMesh->mPositions[face.i2]; + aiVector3D* pV3 = &sMesh->mPositions[face.i3]; + + aiVector3D pDelta1 = *pV2 - *pV1; + aiVector3D pDelta2 = *pV3 - *pV1; + aiVector3D vNor = pDelta1 ^ pDelta2; + + //float fLength = vNor.Length(); + //if (0.0f != fLength)vNor /= fLength; + + + sMesh->mNormals[face.i1] = vNor; + sMesh->mNormals[face.i2] = vNor; + sMesh->mNormals[face.i3] = vNor; + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // calculate the position bounds so we have a reliable epsilon to + // check position differences against + // @Schrompf: This is the 6th time this snippet is repeated! + aiVector3D minVec( 1e10f, 1e10f, 1e10f), maxVec( -1e10f, -1e10f, -1e10f); + for( unsigned int a = 0; a < sMesh->mPositions.size(); a++) + { + minVec.x = std::min( minVec.x, sMesh->mPositions[a].x); + minVec.y = std::min( minVec.y, sMesh->mPositions[a].y); + minVec.z = std::min( minVec.z, sMesh->mPositions[a].z); + maxVec.x = std::max( maxVec.x, sMesh->mPositions[a].x); + maxVec.y = std::max( maxVec.y, sMesh->mPositions[a].y); + maxVec.z = std::max( maxVec.z, sMesh->mPositions[a].z); + } + const float posEpsilon = (maxVec - minVec).Length() * 1e-5f; + + + std::vector avNormals; + avNormals.resize(sMesh->mNormals.size()); + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // now generate the spatial sort tree + D3DSSpatialSorter sSort(sMesh); + + for( std::vector::iterator + i = sMesh->mFaces.begin(); + i != sMesh->mFaces.end();++i) + { + std::vector poResult; + // need to repeat the code for all three vertices of the triangle + + // vertex 1 + sSort.FindPositions(sMesh->mPositions[(*i).i1],(*i).iSmoothGroup, + posEpsilon,poResult); + + aiVector3D vNormals; + float fDiv = 0.0f; + for (std::vector::const_iterator + a = poResult.begin(); + a != poResult.end();++a) + { + vNormals += sMesh->mNormals[(*a)]; + fDiv += 1.0f; + } + vNormals.x /= fDiv; + vNormals.y /= fDiv; + vNormals.z /= fDiv; + vNormals.Normalize(); + avNormals[(*i).i1] = vNormals; + poResult.clear(); + + // vertex 2 + sSort.FindPositions(sMesh->mPositions[(*i).i2],(*i).iSmoothGroup, + posEpsilon,poResult); + + vNormals = aiVector3D(); + fDiv = 0.0f; + for (std::vector::const_iterator + a = poResult.begin(); + a != poResult.end();++a) + { + vNormals += sMesh->mNormals[(*a)]; + fDiv += 1.0f; + } + vNormals.x /= fDiv; + vNormals.y /= fDiv; + vNormals.z /= fDiv; + vNormals.Normalize(); + avNormals[(*i).i2] = vNormals; + poResult.clear(); + + // vertex 3 + sSort.FindPositions(sMesh->mPositions[(*i).i3],(*i).iSmoothGroup, + posEpsilon ,poResult); + + vNormals = aiVector3D(); + fDiv = 0.0f; + for (std::vector::const_iterator + a = poResult.begin(); + a != poResult.end();++a) + { + vNormals += sMesh->mNormals[(*a)]; + fDiv += 1.0f; + } + vNormals.x /= fDiv; + vNormals.y /= fDiv; + vNormals.z /= fDiv; + vNormals.Normalize(); + avNormals[(*i).i3] = vNormals; + } + sMesh->mNormals = avNormals; + return; +} \ No newline at end of file diff --git a/code/3DSHelper.h b/code/3DSHelper.h new file mode 100644 index 000000000..e450a21fe --- /dev/null +++ b/code/3DSHelper.h @@ -0,0 +1,472 @@ +/** @file Defines the helper data structures for importing XFiles */ +#ifndef AI_3DSFILEHELPER_H_INC +#define AI_3DSFILEHELPER_H_INC + +#include +#include +#include + +#include "../include/aiTypes.h" +#include "../include/aiQuaternion.h" +#include "../include/aiMesh.h" +#include "../include/aiAnim.h" +#include "SpatialSort.h" + +namespace Assimp +{ + namespace Dot3DS + { + +#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__) +# pragma pack(push,2) +# define PACK_STRUCT +#elif defined( __GNUC__ ) +# define PACK_STRUCT __attribute__((packed)) +#else +# error Compiler not supported +#endif + +// --------------------------------------------------------------------------- +/** Dot3DSFile class: Helper class for loading 3ds files. Defines chunks +* and data structures. +*/ +class Dot3DSFile +{ +public: + inline Dot3DSFile() {} + + // data structure for a single chunk in a .3ds file + struct Chunk + { + unsigned short Flag; + long Size; + } PACK_STRUCT; + + // source for this used own structures, + // replaced it with out standard math helpers + typedef aiMatrix3x3 MatTransform; + typedef aiVector3D MatTranslate; + + // Used for shading field in material3ds structure + // From AutoDesk 3ds SDK + typedef enum + { + Wire = 0, + Flat = 1, + Gouraud = 2, + Phong = 3, + Metal = 4 + } shadetype3ds; + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // enum for all chunks in 3ds files. Unused + // ones are commented, list is not complete since + // there are many undocumented chunks. + // + // Links: http://www.jalix.org/ressources/graphics/3DS/_unofficials/3ds-unofficial.txt + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + enum + { + + // ************************************************************** + // Base chunks which can be found everywhere in the file + CHUNK_VERSION = 0x0002, + CHUNK_RGBF = 0x0010, // float4 R; float4 G; float4 B + CHUNK_RGBB = 0x0011, // int1 R; int1 G; int B + + // Linear color values (gamma = 2.2?) + CHUNK_LINRGBF = 0x0013, // float4 R; float4 G; float4 B + CHUNK_LINRGBB = 0x0012, // int1 R; int1 G; int B + + CHUNK_PERCENTW = 0x0030, // int2 percentage + CHUNK_PERCENTF = 0x0031, // float4 percentage + // ************************************************************** + + // Unknown and ignored + CHUNK_PRJ = 0xC23D, + + // Unknown. Possibly a reference to an external .mli file? + CHUNK_MLI = 0x3DAA, + + // Primary main chunk of the .3ds file + CHUNK_MAIN = 0x4D4D, + + // Mesh main chunk + CHUNK_OBJMESH = 0x3D3D, + + // Specifies the background color of the .3ds file + // This is passed through the material system for + // viewing purposes. + CHUNK_BKGCOLOR = 0x1200, + + // Specifies the ambient base color of the scene. + // This is added to all materials in the file + CHUNK_AMBCOLOR = 0x2100, + + // Specifies the background image for the whole scene + // This value is passed through the material system + // to the viewer + CHUNK_BIT_MAP = 0x1100, + CHUNK_BIT_MAP_EXISTS = 0x1101, + + // ************************************************************** + // Viewport related stuff. Ignored + CHUNK_DEFAULT_VIEW = 0x3000, + CHUNK_VIEW_TOP = 0x3010, + CHUNK_VIEW_BOTTOM = 0x3020, + CHUNK_VIEW_LEFT = 0x3030, + CHUNK_VIEW_RIGHT = 0x3040, + CHUNK_VIEW_FRONT = 0x3050, + CHUNK_VIEW_BACK = 0x3060, + CHUNK_VIEW_USER = 0x3070, + CHUNK_VIEW_CAMERA = 0x3080, + // ************************************************************** + + // Mesh chunks + CHUNK_OBJBLOCK = 0x4000, + CHUNK_TRIMESH = 0x4100, + CHUNK_VERTLIST = 0x4110, + CHUNK_VERTFLAGS = 0x4111, + CHUNK_FACELIST = 0x4120, + CHUNK_FACEMAT = 0x4130, + CHUNK_MAPLIST = 0x4140, + CHUNK_SMOOLIST = 0x4150, + CHUNK_TRMATRIX = 0x4160, + CHUNK_MESHCOLOR = 0x4165, + CHUNK_TXTINFO = 0x4170, + CHUNK_LIGHT = 0x4600, + CHUNK_SPOTLIGHT = 0x4610, + CHUNK_CAMERA = 0x4700, + CHUNK_HIERARCHY = 0x4F00, + + // Specifies the global scaling factor. This is applied + // to the root node's transformation matrix + CHUNK_MASTER_SCALE = 0x0100, + + // ************************************************************** + // Material chunks + CHUNK_MAT_MATERIAL = 0xAFFF, + + // asciiz containing the name of the material + CHUNK_MAT_MATNAME = 0xA000, + CHUNK_MAT_AMBIENT = 0xA010, // followed by color chunk + CHUNK_MAT_DIFFUSE = 0xA020, // followed by color chunk + CHUNK_MAT_SPECULAR = 0xA030, // followed by color chunk + + // Specifies the shininess of the material + // followed by percentage chunk + CHUNK_MAT_SHININESS = 0xA040, + + // Specifies the shading mode to be used + // followed by a short + CHUNK_MAT_SHADING = 0xA100, + + // NOTE: Emissive color (self illumination) seems not + // to be a color but a single value, type is unknown. + // Make the parser accept both of them. + // followed by percentage chunk (?) + CHUNK_MAT_SELF_ILLUM = 0xA080, + + // Always followed by percentage chunk (?) + CHUNK_MAT_SELF_ILPCT = 0xA084, + + // Always followed by percentage chunk + CHUNK_MAT_TRANSPARENCY = 0xA050, + + // Diffuse texture channel 0 + CHUNK_MAT_TEXTURE = 0xA200, + + // Contains opacity information for each texel + CHUNK_MAT_OPACMAP = 0xA210, + + // Contains a reflection map to be used to reflect + // the environment. This is partially supported. + CHUNK_MAT_REFLMAP = 0xA220, + + // Self Illumination map (emissive colors) + CHUNK_MAT_SELFIMAP = 0xA33d, + + // Bumpmap. Not specified whether it is a heightmap + // or a normal map. Assme it is a heightmap since + // artist normally prefer this format. + CHUNK_MAT_BUMPMAP = 0xA230, + + // Specular map. Seems to influence the specular color + CHUNK_MAT_SPECMAP = 0xA204, + + // Holds shininess data. I assume the specular exponent is + // calculated like this: + // + // s[x,y] = stex[x,y] * base_shininess; + // I also assume the data in the texture must be renormalized + // (normally by dividing / 255) after loading. + CHUNK_MAT_MAT_SHINMAP = 0xA33C, + + // Scaling in U/V direction. + // (need to gen separate UV coordinate set + // and do this by hand) + CHUNK_MAT_MAP_USCALE = 0xA354, + CHUNK_MAT_MAP_VSCALE = 0xA356, + + // Translation in U/V direction. + // (need to gen separate UV coordinate set + // and do this by hand) + CHUNK_MAT_MAP_UOFFSET = 0xA358, + CHUNK_MAT_MAP_VOFFSET = 0xA35a, + + // UV-coordinates rotation around the z-axis + // Assumed to be in radians. + CHUNK_MAT_MAP_ANG = 0xA35C, + + // Specifies the file name of a texture + CHUNK_MAPFILE = 0xA300, + // ************************************************************** + + // Main keyframer chunk. Contains translation/rotation/scaling data + CHUNK_KEYFRAMER = 0xB000, + + // Supported sub chunks + CHUNK_TRACKINFO = 0xB002, + CHUNK_TRACKOBJNAME = 0xB010, + CHUNK_TRACKPIVOT = 0xB013, + CHUNK_TRACKPOS = 0xB020, + CHUNK_TRACKROTATE = 0xB021, + CHUNK_TRACKSCALE = 0xB022, + + // ************************************************************** + // Keyframes for various other stuff in the file + // Ignored + CHUNK_AMBIENTKEY = 0xB001, + CHUNK_TRACKMORPH = 0xB026, + CHUNK_TRACKHIDE = 0xB029, + CHUNK_OBJNUMBER = 0xB030, + CHUNK_TRACKCAMERA = 0xB003, + CHUNK_TRACKFOV = 0xB023, + CHUNK_TRACKROLL = 0xB024, + CHUNK_TRACKCAMTGT = 0xB004, + CHUNK_TRACKLIGHT = 0xB005, + CHUNK_TRACKLIGTGT = 0xB006, + CHUNK_TRACKSPOTL = 0xB007, + CHUNK_FRAMES = 0xB008 + // ************************************************************** + }; +}; + +#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__) +# pragma pack( pop ) +#endif +#undef PACK_STRUCT + +// --------------------------------------------------------------------------- +/** Helper structure representing a 3ds mesh face */ +struct Face +{ + Face() : iSmoothGroup(0), bDirection(true) + { + // let the rest uninitialized for performance + } + + + // Indices. .3ds is using uint16. However, after + // an unique vrtex set has been geneerated it might + // be an index becomes > 2^16 + uint32_t i1; + uint32_t i2; + uint32_t i3; + + // specifies to which smoothing group the face belongs to + uint32_t iSmoothGroup; + + // Direction the normal vector of the face + // will be pointing to + bool bDirection; +}; +// --------------------------------------------------------------------------- +/** Helper structure representing a texture */ +struct Texture +{ + Texture() + : + mScaleU(1.0f), + mScaleV(1.0f), + mOffsetU(0.0f), + mOffsetV(0.0f), + mRotation(0.0f), + iUVSrc(0) + { + mTextureBlend = std::numeric_limits::quiet_NaN(); + } + // Specifies the blending factor for the texture + float mTextureBlend; + + // Specifies the filename of the texture + std::string mMapName; + + // Specifies texture coordinate offsets/scaling/rotations + float mScaleU; + float mScaleV; + float mOffsetU; + float mOffsetV; + float mRotation; + + // Used internally + bool bPrivate; + int iUVSrc; +}; +// --------------------------------------------------------------------------- +/** Helper structure representing a 3ds material */ +struct Material +{ + Material() + : + mSpecularExponent (0.0f), + mShading(Dot3DSFile::Gouraud), + mTransparency (1.0f), + mBumpHeight (1.0f), + iBakeUVTransform (0), + pcSingleTexture (NULL) + { + static int iCnt = 0; + std::stringstream ss(mName); + ss << "%%_UNNAMED_" << iCnt++ << "_%%"; + } + + // Name of the material + std::string mName; + // Diffuse color of the material + aiColor3D mDiffuse; + // Specular exponent + float mSpecularExponent; + // Specular color of the material + aiColor3D mSpecular; + // Ambient color of the material + aiColor3D mAmbient; + // Shading type to be used + Dot3DSFile::shadetype3ds mShading; + // Opacity of the material + float mTransparency; + + // Different texture channels + Texture sTexDiffuse; + Texture sTexOpacity; + Texture sTexSpecular; + Texture sTexBump; + Texture sTexEmissive; + Texture sTexShininess; + + /* + float mReflectionTextureBlend; + std::string mReflectionTexture; + */ + float mBumpHeight; + + // Emissive color + aiColor3D mEmissive; + + // Used internally + unsigned int iBakeUVTransform; + Texture* pcSingleTexture; +}; +// --------------------------------------------------------------------------- +/** Helper structure to represent a 3ds file mesh */ +struct Mesh +{ + Mesh() + { + static int iCnt = 0; + std::stringstream ss(mName); + ss << "%%_UNNAMED_" << iCnt++ << "_%%"; +#if 0 + for (unsigned int i = 0; i < 32;++i) + bSmoothGroupRequired[i] = false; +#endif + } + std::string mName; + + std::vector mPositions; + std::vector mFaces; + std::vector mTexCoords; + std::vector mFaceMaterials; + std::vector mNormals; + +#if 0 + bool bSmoothGroupRequired[32]; +#endif + + aiMatrix4x4 mMat; +}; +// --------------------------------------------------------------------------- +/** Helper structure to represent a 3ds file node */ +struct Node +{ + Node() +#if 0 + : vScaling(1.0f,1.0f,1.0f) +#endif + { + static int iCnt = 0; + std::stringstream ss(mName); + ss << "%%_UNNAMED_" << iCnt++ << "_%%"; + + mHierarchyPos = 0; + mHierarchyIndex = 0; + } + + Node* mParent; + std::vector mChildren; + std::string mName; + int16_t mHierarchyPos; + int16_t mHierarchyIndex; + +#if 0 + aiVector3D vPivot; + aiVector3D vScaling; + aiMatrix4x4 mRotation; + aiVector3D vPosition; +#endif + + inline Node& push_back(Node* pc) + { + mChildren.push_back(pc); + pc->mParent = this; + pc->mHierarchyPos = this->mHierarchyPos+1; + return *this; + } +}; +// --------------------------------------------------------------------------- +/** Helper structure analogue to aiScene */ +struct Scene +{ + + // NOTE: 3ds references materials globally + std::vector mMaterials; + std::vector mMeshes; + + Node* pcRootNode; +}; + +// --------------------------------------------------------------------------- +inline bool is_qnan(float p_fIn) +{ + // NOTE: Comparison against qnan is generally problematic + // because qnan == qnan is false AFAIK + union FTOINT + { + float fFloat; + int32_t iInt; + } one, two; + one.fFloat = std::numeric_limits::quiet_NaN(); + two.fFloat = p_fIn; + + return (one.iInt == two.iInt); +} +// --------------------------------------------------------------------------- +inline bool is_not_qnan(float p_fIn) +{ + return !is_qnan(p_fIn); +} + +} // end of namespace Dot3DS +} // end of namespace Assimp + +#endif // AI_XFILEHELPER_H_INC \ No newline at end of file diff --git a/code/3DSLoader.cpp b/code/3DSLoader.cpp new file mode 100644 index 000000000..85bb42092 --- /dev/null +++ b/code/3DSLoader.cpp @@ -0,0 +1,1310 @@ +/** @file Implementation of the 3ds importer class */ +#include "3DSLoader.h" +#include "MaterialSystem.h" + +#include "../include/IOStream.h" +#include "../include/IOSystem.h" +#include "../include/aiMesh.h" +#include "../include/aiScene.h" +#include "../include/aiAssert.h" + +#include + +using namespace Assimp; + +#define ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG \ + "WARNING: Size of chunk data plus size of " \ + "subordinate chunks is larger than the size " \ + "specified in the higher-level chunk header." \ + + + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +Dot3DSImporter::Dot3DSImporter() +{ +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +Dot3DSImporter::~Dot3DSImporter() +{ +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the class can handle the format of the given file. +bool Dot3DSImporter::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); + + // not brilliant but working ;-) + if( extension == ".3ds" || extension == ".3DS" || + extension == ".3Ds" || extension == ".3dS") + return true; + + return false; +} +// ------------------------------------------------------------------------------------------------ +// recursively delete a given node +void DeleteNodeRecursively (aiNode* p_piNode) +{ + if (!p_piNode)return; + + if (p_piNode->mChildren) + { + for (unsigned int i = 0 ; i < p_piNode->mNumChildren;++i) + { + DeleteNodeRecursively(p_piNode->mChildren[i]); + } + } + delete p_piNode; + return; +} +// ------------------------------------------------------------------------------------------------ +// Imports the given file into the given scene structure. +void Dot3DSImporter::InternReadFile( + const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) +{ + boost::scoped_ptr file( pIOHandler->Open( pFile)); + + // Check whether we can read from the file + if( file.get() == NULL) + { + throw new ImportErrorException( "Failed to open file " + pFile + "."); + } + + // check whether the .3ds file is large enough to contain + // at least one chunk. + size_t fileSize = file->FileSize(); + if( fileSize < 16) + { + throw new ImportErrorException( ".3ds File is too small."); + } + + this->mScene = new Dot3DS::Scene(); + + // allocate storage and copy the contents of the file to a memory buffer + this->mBuffer = new unsigned char[fileSize]; + file->Read( mBuffer, 1, fileSize); + this->mCurrent = this->mBuffer; + this->mLast = this->mBuffer+fileSize; + + // initialize members + this->mLastNodeIndex = -1; + this->mCurrentNode = new Dot3DS::Node(); + this->mRootNode = this->mCurrentNode; + this->mRootNode->mHierarchyPos = -1; + this->mRootNode->mHierarchyIndex = -1; + this->mRootNode->mParent = NULL; + this->mMasterScale = 1.0f; + this->mBackgroundImage = ""; + this->bHasBG = false; + + int iRemaining = (unsigned int)fileSize; + this->ParseMainChunk(&iRemaining); + + // Generate an unique set of vertices/indices for + // all meshes contained in the file + for (std::vector::iterator + i = this->mScene->mMeshes.begin(); + i != this->mScene->mMeshes.end();++i) + { + // TODO: see function body + this->CheckIndices(&(*i)); + this->MakeUnique(&(*i)); + + // first generate normals for the mesh + this->GenNormals(&(*i)); + } + + // Apply scaling and offsets to all texture coordinates + this->ApplyScaleNOffset(); + + // 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 + this->ReplaceDefaultMaterial(); + + try + { + // Convert the scene from our internal representation to an aiScene object + this->ConvertScene(pScene); + } + catch (ImportErrorException ex) + { + // delete the scene itself + if (pScene->mMeshes) + { + for (unsigned int i = 0; i < pScene->mNumMeshes;++i) + delete pScene->mMeshes[i]; + delete[] pScene->mMeshes; + } + if (pScene->mMaterials) + { + for (unsigned int i = 0; i < pScene->mNumMaterials;++i) + delete pScene->mMaterials[i]; + delete[] pScene->mMaterials; + } + // there are no animations + if (pScene->mRootNode)DeleteNodeRecursively(pScene->mRootNode); + throw ex; + } + + // Generate the node graph for the scene. This is a little bit + // tricky since we'll need to split some meshes into submeshes + this->GenerateNodeGraph(pScene); + + // Now apply a master scaling factor to the scene + this->ApplyMasterScale(pScene); + + delete[] this->mBuffer; + delete this->mScene; + return; +} +// ------------------------------------------------------------------------------------------------ +void Dot3DSImporter::ApplyMasterScale(aiScene* pScene) +{ + // NOTE: Some invalid files have masterscale set to 0.0 + if (0.0f == this->mMasterScale) + { + this->mMasterScale = 1.0f; + } + else this->mMasterScale = 1.0f / this->mMasterScale; + + // construct an uniform scaling matrix and multiply with it + pScene->mRootNode->mTransformation *= aiMatrix4x4( + this->mMasterScale,0.0f, 0.0f, 0.0f, + 0.0f, this->mMasterScale,0.0f, 0.0f, + 0.0f, 0.0f, this->mMasterScale,0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); +} +// ------------------------------------------------------------------------------------------------ +void Dot3DSImporter::ReadChunk(const Dot3DSFile::Chunk** p_ppcOut) +{ + ai_assert(p_ppcOut != NULL); + + // read chunk + if ((unsigned int)this->mCurrent >= (unsigned int)this->mLast) + { + *p_ppcOut = NULL; + return; + } + const unsigned int iDiff = (unsigned int)this->mLast - (unsigned int)this->mCurrent; + if (iDiff < sizeof(Dot3DSFile::Chunk)) + { + *p_ppcOut = NULL; + return; + } + + *p_ppcOut = (const Dot3DSFile::Chunk*) this->mCurrent; + this->mCurrent += sizeof(Dot3DSFile::Chunk); + return; +} +// ------------------------------------------------------------------------------------------------ +void Dot3DSImporter::ParseMainChunk(int* piRemaining) +{ + const Dot3DSFile::Chunk* psChunk; + + this->ReadChunk(&psChunk); + if (NULL == psChunk)return; + + const unsigned char* pcCur = this->mCurrent; + const unsigned char* pcCurNext = pcCur + (psChunk->Size + - sizeof(Dot3DSFile::Chunk)); + + // get chunk type + int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk)); + switch (psChunk->Flag) + { + case Dot3DSFile::CHUNK_MAIN: + //case 0x444d: // bugfix + + this->ParseEditorChunk(&iRemaining); + break; + }; + if ((unsigned int)pcCurNext < (unsigned int)this->mCurrent) + { + // place an error message. If we crash the programmer + // will be able to find it + this->mErrorText = ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG; + pcCurNext = this->mCurrent; + } + // Go to the starting position of the next top-level chunk + this->mCurrent = pcCurNext; + *piRemaining -= psChunk->Size; + if (0 >= *piRemaining)return; + return this->ParseMainChunk(piRemaining); +} +// ------------------------------------------------------------------------------------------------ +void Dot3DSImporter::ParseEditorChunk(int* piRemaining) +{ + const Dot3DSFile::Chunk* psChunk; + + this->ReadChunk(&psChunk); + if (NULL == psChunk)return; + + const unsigned char* pcCur = this->mCurrent; + const unsigned char* pcCurNext = pcCur + (psChunk->Size + - sizeof(Dot3DSFile::Chunk)); + + // get chunk type + int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk)); + switch (psChunk->Flag) + { + case Dot3DSFile::CHUNK_OBJMESH: + + this->ParseObjectChunk(&iRemaining); + break; + + // NOTE: In several documentations in the internet this + // chunk appears at different locations + case Dot3DSFile::CHUNK_KEYFRAMER: + + this->ParseKeyframeChunk(&iRemaining); + break; + }; + if ((unsigned int)pcCurNext < (unsigned int)this->mCurrent) + { + // place an error message. If we crash the programmer + // will be able to find it + this->mErrorText = ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG; + pcCurNext = this->mCurrent; + } + // Go to the starting position of the next top-level chunk + this->mCurrent = pcCurNext; + *piRemaining -= psChunk->Size; + if (0 >= *piRemaining)return; + return this->ParseEditorChunk(piRemaining); +} +// ------------------------------------------------------------------------------------------------ +void Dot3DSImporter::ParseObjectChunk(int* piRemaining) +{ + const Dot3DSFile::Chunk* psChunk; + + this->ReadChunk(&psChunk); + if (NULL == psChunk)return; + + const unsigned char* pcCur = this->mCurrent; + const unsigned char* pcCurNext = pcCur + (psChunk->Size + - sizeof(Dot3DSFile::Chunk)); + + const unsigned char* sz = this->mCurrent; + unsigned int iCnt = 0; + + // get chunk type + int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk)); + switch (psChunk->Flag) + { + case Dot3DSFile::CHUNK_OBJBLOCK: + + this->mScene->mMeshes.push_back(Dot3DS::Mesh()); + + // at first we need to parse the name of the + // geometry object + + while (*sz++ != '\0') + { + if (sz > pcCurNext-1)break; + ++iCnt; + } + this->mScene->mMeshes.back().mName = std::string( + (const char*)this->mCurrent,iCnt); + ++iCnt; + + this->mCurrent += iCnt; + iRemaining -= iCnt; + this->ParseChunk(&iRemaining); + break; + + case Dot3DSFile::CHUNK_MAT_MATERIAL: + + this->mScene->mMaterials.push_back(Dot3DS::Material()); + this->ParseMaterialChunk(&iRemaining); + break; + + case Dot3DSFile::CHUNK_AMBCOLOR: + + // This is the ambient base color of the scene. + // We add it to the ambient color of all materials + this->ParseColorChunk(&this->mClrAmbient,true); + if (is_qnan(this->mClrAmbient.r)) + { + this->mClrAmbient.r = 0.0f; + this->mClrAmbient.g = 0.0f; + this->mClrAmbient.b = 0.0f; + } + break; + + + case Dot3DSFile::CHUNK_BIT_MAP: + + this->mBackgroundImage = std::string((const char*)this->mCurrent); + break; + + + case Dot3DSFile::CHUNK_BIT_MAP_EXISTS: + bHasBG = true; + break; + + + case Dot3DSFile::CHUNK_MASTER_SCALE: + + this->mMasterScale = *((float*)this->mCurrent); + this->mCurrent += sizeof(float); + break; + + // NOTE: In several documentations in the internet this + // chunk appears at different locations + case Dot3DSFile::CHUNK_KEYFRAMER: + + this->ParseKeyframeChunk(&iRemaining); + break; + + }; + if ((unsigned int)pcCurNext < (unsigned int)this->mCurrent) + { + // place an error message. If we crash the programmer + // will be able to find it + this->mErrorText = ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG; + pcCurNext = this->mCurrent; + } + // Go to the starting position of the next top-level chunk + this->mCurrent = pcCurNext; + *piRemaining -= psChunk->Size; + if (0 >= *piRemaining)return; + return this->ParseObjectChunk(piRemaining); +} +// ------------------------------------------------------------------------------------------------ +void Dot3DSImporter::SkipChunk() +{ + const Dot3DSFile::Chunk* psChunk; + this->ReadChunk(&psChunk); + if (NULL == psChunk)return; + this->mCurrent += psChunk->Size - sizeof(Dot3DSFile::Chunk); + return; +} +// ------------------------------------------------------------------------------------------------ +void Dot3DSImporter::ParseChunk(int* piRemaining) +{ + const Dot3DSFile::Chunk* psChunk; + + this->ReadChunk(&psChunk); + if (NULL == psChunk)return; + + const unsigned char* pcCur = this->mCurrent; + const unsigned char* pcCurNext = pcCur + (psChunk->Size + - sizeof(Dot3DSFile::Chunk)); + + // get chunk type + int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk)); + switch (psChunk->Flag) + { + case Dot3DSFile::CHUNK_TRIMESH: + // this starts a new mesh + this->ParseMeshChunk(&iRemaining); + break; + }; + if ((unsigned int)pcCurNext < (unsigned int)this->mCurrent) + { + // place an error message. If we crash the programmer + // will be able to find it + this->mErrorText = ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG; + pcCurNext = this->mCurrent; + } + // Go to the starting position of the next top-level chunk + this->mCurrent = pcCurNext; + + *piRemaining -= psChunk->Size; + if (0 >= *piRemaining)return; + return this->ParseChunk(piRemaining); +} +// ------------------------------------------------------------------------------------------------ +void Dot3DSImporter::ParseKeyframeChunk(int* piRemaining) +{ + const Dot3DSFile::Chunk* psChunk; + + this->ReadChunk(&psChunk); + if (NULL == psChunk)return; + + const unsigned char* pcCur = this->mCurrent; + const unsigned char* pcCurNext = pcCur + (psChunk->Size + - sizeof(Dot3DSFile::Chunk)); + + // get chunk type + int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk)); + switch (psChunk->Flag) + { + case Dot3DSFile::CHUNK_TRACKINFO: + // this starts a new mesh + this->ParseHierarchyChunk(&iRemaining); + break; + }; + if ((unsigned int)pcCurNext < (unsigned int)this->mCurrent) + { + // place an error message. If we crash the programmer + // will be able to find it + this->mErrorText = ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG; + pcCurNext = this->mCurrent; + } + // Go to the starting position of the next top-level chunk + this->mCurrent = pcCurNext; + + *piRemaining -= psChunk->Size; + if (0 >= *piRemaining)return; + return this->ParseKeyframeChunk(piRemaining); +} +// ------------------------------------------------------------------------------------------------ +void Dot3DSImporter::InverseNodeSearch(Dot3DS::Node* pcNode,Dot3DS::Node* pcCurrent) +{ + if (NULL == pcCurrent) + { + this->mRootNode->push_back(pcNode); + return; + } + if (pcCurrent->mHierarchyPos == pcNode->mHierarchyPos) + { + if(NULL != pcCurrent->mParent) + pcCurrent->mParent->push_back(pcNode); + else pcCurrent->push_back(pcNode); + return; + } + return this->InverseNodeSearch(pcNode,pcCurrent->mParent); +} +// ------------------------------------------------------------------------------------------------ +void Dot3DSImporter::ParseHierarchyChunk(int* piRemaining) +{ + const Dot3DSFile::Chunk* psChunk; + + this->ReadChunk(&psChunk); + if (NULL == psChunk)return; + + const unsigned char* pcCur = this->mCurrent; + const unsigned char* pcCurNext = pcCur + (psChunk->Size + - sizeof(Dot3DSFile::Chunk)); + + // get chunk type + const unsigned char* sz = (unsigned char*)this->mCurrent; + unsigned int iCnt = 0; + uint16_t iHierarchy; + //uint16_t iTemp; + Dot3DS::Node* pcNode; + switch (psChunk->Flag) + { + case Dot3DSFile::CHUNK_TRACKOBJNAME: + + // get object name + while (*sz++ != '\0') + { + if (sz > pcCurNext-1)break; + ++iCnt; + } + pcNode = new Dot3DS::Node(); + pcNode->mName = std::string((const char*)this->mCurrent,iCnt); + + iCnt++; + // there are two unknown values which we can safely ignore + this->mCurrent += iCnt + sizeof(uint16_t)*2; + iHierarchy = *((uint16_t*)this->mCurrent); + iHierarchy++; + pcNode->mHierarchyPos = iHierarchy; + pcNode->mHierarchyIndex = this->mLastNodeIndex; + if (iHierarchy > this->mLastNodeIndex) + { + // place it at the current position in the hierarchy + this->mCurrentNode->push_back(pcNode); + } + else + { + // need to go back to the specified position in the hierarchy. + this->InverseNodeSearch(pcNode,this->mCurrentNode); + } + this->mLastNodeIndex++; + this->mCurrentNode = pcNode; + break; + +#if 0 + + case Dot3DSFile::CHUNK_TRACKPIVOT: + + this->mCurrentNode->vPivot = *((aiVector3D*)this->mCurrent); + this->mCurrent += sizeof(aiVector3D); + break; + + + case Dot3DSFile::CHUNK_TRACKPOS: + + /* + +2 short flags; + +8 short unknown[4]; + +2 short keys; + +2 short unknown; + struct { + +2 short framenum; + +4 long unknown; + float pos_x, pos_y, pos_z; + } pos[keys]; + */ + this->mCurrent += 10; + iTemp = *((uint16_t*)mCurrent); + + this->mCurrent += sizeof(uint16_t) * 2; + + if (0 != iTemp) + { + for (unsigned int i = 0; i < (unsigned int)iTemp;++i) + { + uint16_t sNum = *((uint16_t*)mCurrent); + this->mCurrent += sizeof(uint16_t); + + if (0 == sNum) + { + this->mCurrent += sizeof(uint32_t); + this->mCurrentNode->vPosition = *((aiVector3D*)this->mCurrent); + this->mCurrent += sizeof(aiVector3D); + } + else this->mCurrent += sizeof(uint32_t) + sizeof(aiVector3D); + } + } + break; + + case Dot3DSFile::CHUNK_TRACKROTATE: + + /* + +2 short flags; + +8 short unknown[4]; + +2 short keys; + +2 short unknown; + struct { + +2 short framenum; + +4 long unknown; + float rad , pos_x, pos_y, pos_z; + } pos[keys]; + */ + this->mCurrent += 10; + iTemp = *((uint16_t*)mCurrent); + + this->mCurrent += sizeof(uint16_t) * 2; + if (0 != iTemp) + { + bool neg = false; + unsigned int iNum0 = 0; + for (unsigned int i = 0; i < (unsigned int)iTemp;++i) + { + uint16_t sNum = *((uint16_t*)mCurrent); + this->mCurrent += sizeof(uint16_t); + + if (0 == sNum) + { + this->mCurrent += sizeof(uint32_t); + float fRadians = *((float*)this->mCurrent); + this->mCurrent += sizeof(float); + aiVector3D vAxis = *((aiVector3D*)this->mCurrent); + this->mCurrent += sizeof(aiVector3D); + + // some idiotic files have rotations with fRadians = 0 ... + if (0.0f != fRadians) + { + + // if the radians go beyond PI then the rotations + // thereafter must be inversed +#if 0 + if (neg)fRadians *= -1.0f; + if ((fRadians >= 3.1415926f || fRadians <= -3.1415926f)) + { + neg = !neg; + } +#endif + + + // get the rotation matrix around the axis + const float fSin = sinf(-fRadians); + const float fCos = cosf(-fRadians); + const float fOneMinusCos = 1.0f - fCos; + + std::swap(vAxis.z,vAxis.y); + vAxis.Normalize(); + + aiMatrix4x4 mRot = aiMatrix4x4( + (vAxis.x * vAxis.x) * fOneMinusCos + fCos, + (vAxis.x * vAxis.y) * fOneMinusCos - (vAxis.z * fSin), + (vAxis.x * vAxis.z) * fOneMinusCos + (vAxis.y * fSin), + 0.0f, + (vAxis.y * vAxis.x) * fOneMinusCos + (vAxis.z * fSin), + (vAxis.y * vAxis.y) * fOneMinusCos + fCos, + (vAxis.y * vAxis.z) * fOneMinusCos - (vAxis.x * fSin), + 0.0f, + (vAxis.z * vAxis.x) * fOneMinusCos - (vAxis.y * fSin), + (vAxis.z * vAxis.y) * fOneMinusCos + (vAxis.x * fSin), + (vAxis.z * vAxis.z) * fOneMinusCos + fCos, + 0.0f,0.0f,0.0f,0.0f,1.0f); + //mRot.Transpose(); + + // build a chain of concatenated rotation matrix' + // if there are multiple track chunks for the same frame + if (0 != iNum0) + { + this->mCurrentNode->mRotation = this->mCurrentNode->mRotation * mRot; + } + else + { + // for the first time simply set the rotation matrix + this->mCurrentNode->mRotation = mRot; + } + iNum0++; + } + } + else this->mCurrent += sizeof(uint32_t) + sizeof(aiVector3D) + sizeof(float); + } + } + break; + + case Dot3DSFile::CHUNK_TRACKSCALE: + + /* + +2 short flags; + +8 short unknown[4]; + +2 short keys; + +2 short unknown; + struct { + +2 short framenum; + +4 long unknown; + float pos_x, pos_y, pos_z; + } pos[keys]; + */ + this->mCurrent += 10; + iTemp = *((uint16_t*)mCurrent); + + this->mCurrent += sizeof(uint16_t) * 2; + + if (0 != iTemp) + { + for (unsigned int i = 0; i < (unsigned int)iTemp;++i) + { + uint16_t sNum = *((uint16_t*)mCurrent); + this->mCurrent += sizeof(uint16_t); + if (0 == sNum) + { + this->mCurrent += sizeof(uint32_t); + aiVector3D vMe = *((aiVector3D*)this->mCurrent); + // ignore zero scalings + if (0.0f != vMe.x && 0.0f != vMe.y && 0.0f != vMe.z) + { + this->mCurrentNode->vScaling.x *= vMe.x; + this->mCurrentNode->vScaling.y *= vMe.y; + this->mCurrentNode->vScaling.z *= vMe.z; + } + this->mCurrent += sizeof(aiVector3D); + } + else this->mCurrent += sizeof(uint32_t) + sizeof(aiVector3D); + } + } + break; + +#endif // 0 + }; + if ((unsigned int)pcCurNext < (unsigned int)this->mCurrent) + { + // place an error message. If we crash the programmer + // will be able to find it + this->mErrorText = ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG; + pcCurNext = this->mCurrent; + } + // Go to the starting position of the next top-level chunk + this->mCurrent = pcCurNext; + + *piRemaining -= psChunk->Size; + if (0 >= *piRemaining)return; + return this->ParseHierarchyChunk(piRemaining); +} +// ------------------------------------------------------------------------------------------------ +void Dot3DSImporter::ParseFaceChunk(int* piRemaining) +{ + const Dot3DSFile::Chunk* psChunk; + + Dot3DS::Mesh& mMesh = this->mScene->mMeshes.back(); + + this->ReadChunk(&psChunk); + if (NULL == psChunk)return; + + const unsigned char* pcCur = this->mCurrent; + const unsigned char* pcCurNext = pcCur + (psChunk->Size + - sizeof(Dot3DSFile::Chunk)); + + // get chunk type + const unsigned char* sz = this->mCurrent; + uint32_t iCnt = 0,iTemp; + switch (psChunk->Flag) + { + case Dot3DSFile::CHUNK_SMOOLIST: + + // one int32 for each face + for (std::vector::iterator + i = mMesh.mFaces.begin(); + i != mMesh.mFaces.end();++i) + { + // nth bit is set for nth smoothing group + (*i).iSmoothGroup = *((uint32_t*)this->mCurrent); +#if 0 + for (unsigned int x = 0, a = 1; x < 32;++x,a <<= 1) + { + if ((*i).iSmoothGroup & a) + mMesh.bSmoothGroupRequired[x] = true; + } +#endif + this->mCurrent += sizeof(uint32_t); + } + break; + + case Dot3DSFile::CHUNK_FACEMAT: + + // at fist an asciiz with the material name + while (*sz++ != '\0') + { + if (sz > pcCurNext-1)break; + } + + // find the index of the material + unsigned int iIndex = 0xFFFFFFFF; + iCnt = 0; + for (std::vector::const_iterator + i = this->mScene->mMaterials.begin(); + i != this->mScene->mMaterials.end();++i,++iCnt) + { + // compare case-independent to be sure it works + if (0 == ASSIMP_stricmp((const char*)this->mCurrent, + (const char*)((*i).mName.c_str()))) + { + iIndex = iCnt; + break; + } + } + if (iIndex == 0xFFFFFFFF) + { + // this material is not known. Ignore this. We will later + // assign the default material to all faces using *this* + // material. Use 0xcdcdcdcd as special value to indicate + // this. + iIndex = 0xcdcdcdcd; + } + this->mCurrent = sz; + iCnt = (int)(*((uint16_t*)this->mCurrent)); + this->mCurrent += sizeof(uint16_t); + + for (unsigned int i = 0; i < iCnt;++i) + { + iTemp = (uint16_t)*((uint16_t*)this->mCurrent); + + // check range + if (iTemp >= mMesh.mFaceMaterials.size()) + { + mMesh.mFaceMaterials[mMesh.mFaceMaterials.size()-1] = iIndex; + } + else + { + mMesh.mFaceMaterials[iTemp] = iIndex; + } + this->mCurrent += sizeof(uint16_t); + } + + break; + }; + if ((unsigned int)pcCurNext < (unsigned int)this->mCurrent) + { + // place an error message. If we crash the programmer + // will be able to find it + this->mErrorText = ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG; + pcCurNext = this->mCurrent; + } + // Go to the starting position of the next chunk on this level + this->mCurrent = pcCurNext; + + *piRemaining -= psChunk->Size; + if (0 >= *piRemaining)return; + return ParseFaceChunk(piRemaining); +} +// ------------------------------------------------------------------------------------------------ +void Dot3DSImporter::ParseMeshChunk(int* piRemaining) +{ + const Dot3DSFile::Chunk* psChunk; + + Dot3DS::Mesh& mMesh = this->mScene->mMeshes.back(); + + this->ReadChunk(&psChunk); + if (NULL == psChunk)return; + + const unsigned char* pcCur = this->mCurrent; + const unsigned char* pcCurNext = pcCur + (psChunk->Size + - sizeof(Dot3DSFile::Chunk)); + + // get chunk type + const unsigned char* sz = this->mCurrent; + unsigned int iCnt = 0; + int iRemaining; + uint16_t iNum = 0; + float* pf; + switch (psChunk->Flag) + { + case Dot3DSFile::CHUNK_VERTLIST: + + iNum = *((short*)this->mCurrent); + this->mCurrent += sizeof(short); + while (iNum-- > 0) + { + mMesh.mPositions.push_back(*((aiVector3D*)this->mCurrent)); + mMesh.mPositions.back().z *= -1.0f; + this->mCurrent += sizeof(aiVector3D); + } + break; + + case Dot3DSFile::CHUNK_TRMATRIX: + { + // http://www.gamedev.net/community/forums/topic.asp?topic_id=263063 + // http://www.gamedev.net/community/forums/topic.asp?topic_id=392310 + pf = (float*)this->mCurrent; + this->mCurrent += 12 * sizeof(float); + + mMesh.mMat.a1 = pf[0]; + mMesh.mMat.a2 = pf[1]; + mMesh.mMat.a3 = pf[2]; + mMesh.mMat.b1 = pf[3]; + mMesh.mMat.b2 = pf[4]; + mMesh.mMat.b3 = pf[5]; + mMesh.mMat.c1 = pf[6]; + mMesh.mMat.c2 = pf[7]; + mMesh.mMat.c3 = pf[8]; + mMesh.mMat.d1 = pf[9]; + mMesh.mMat.d2 = pf[10]; + mMesh.mMat.d3 = pf[11]; + + std::swap((float&)mMesh.mMat.d2, (float&)mMesh.mMat.d3); + std::swap((float&)mMesh.mMat.a2, (float&)mMesh.mMat.a3); + std::swap((float&)mMesh.mMat.b1, (float&)mMesh.mMat.c1); + std::swap((float&)mMesh.mMat.c2, (float&)mMesh.mMat.b3); + std::swap((float&)mMesh.mMat.b2, (float&)mMesh.mMat.c3); + + mMesh.mMat.Transpose(); + + //aiMatrix4x4 mInv = mMesh.mMat; + //mInv.Inverse(); + + //// invert the matrix and transform all vertices with it + //// (the origin of all vertices is 0|0|0 now) + //for (register unsigned int i = 0; i < 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; + // } + + // now check whether the matrix has got a negative determinant + // If yes, we need to flip all vertices x axis .... + // From lib3ds, mesh.c + if (mMesh.mMat.Determinant() < 0.0f) + { + aiMatrix4x4 mInv = mMesh.mMat; + mInv.Inverse(); + + aiMatrix4x4 mMe = mMesh.mMat; + mMe.a1 *= -1.0f; + mMe.a2 *= -1.0f; + mMe.a3 *= -1.0f; + mMe.a4 *= -1.0f; + mInv = mMe * mInv; + for (register unsigned int i = 0; i < 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; + } + } + } + break; + + case Dot3DSFile::CHUNK_MAPLIST: + + iNum = *((uint16_t*)this->mCurrent); + this->mCurrent += sizeof(uint16_t); + while (iNum-- > 0) + { + mMesh.mTexCoords.push_back(*((aiVector2D*)this->mCurrent)); + this->mCurrent += sizeof(aiVector2D); + } + break; + +#if (defined _DEBUG) + + case Dot3DSFile::CHUNK_TXTINFO: + + // for debugging purposes. Read two bytes to determine the mapping type + iNum = *((uint16_t*)this->mCurrent); + this->mCurrent += sizeof(uint16_t); + break; +#endif + + case Dot3DSFile::CHUNK_FACELIST: + + iNum = *((uint16_t*)this->mCurrent); + this->mCurrent += sizeof(uint16_t); + while (iNum-- > 0) + { + Dot3DS::Face sFace; + sFace.i1 = *((uint16_t*)this->mCurrent); + this->mCurrent += sizeof(uint16_t); + sFace.i2 = *((uint16_t*)this->mCurrent); + this->mCurrent += sizeof(uint16_t); + sFace.i3 = *((uint16_t*)this->mCurrent); + this->mCurrent += 2*sizeof(uint16_t); + mMesh.mFaces.push_back(sFace); + + //if (sFace.i1 < sFace.i2)sFace.bDirection = false; + } + + // 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); + + iRemaining = (int)pcCurNext - (int)this->mCurrent; + if (iRemaining > 0)this->ParseFaceChunk(&iRemaining); + break; + + }; + if ((unsigned int)pcCurNext < (unsigned int)this->mCurrent) + { + // place an error message. If we crash the programmer + // will be able to find it + this->mErrorText = ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG; + pcCurNext = this->mCurrent; + } + // Go to the starting position of the next chunk on this level + this->mCurrent = pcCurNext; + + *piRemaining -= psChunk->Size; + if (0 >= *piRemaining)return; + return ParseMeshChunk(piRemaining); +} +// ------------------------------------------------------------------------------------------------ +void Dot3DSImporter::ParseMaterialChunk(int* piRemaining) +{ + const Dot3DSFile::Chunk* psChunk; + + this->ReadChunk(&psChunk); + if (NULL == psChunk)return; + + const unsigned char* pcCur = this->mCurrent; + const unsigned char* pcCurNext = pcCur + (psChunk->Size + - sizeof(Dot3DSFile::Chunk)); + + // get chunk type + const unsigned char* sz = this->mCurrent; + unsigned int iCnt = 0; + int iRemaining; + aiColor3D* pc; + float* pcf; + switch (psChunk->Flag) + { + case Dot3DSFile::CHUNK_MAT_MATNAME: + + // string in file is zero-terminated, + // this should be no problem. However, validate whether + // it overlaps the end of the chunk, if yes we should + // truncate it. + while (*sz++ != '\0') + { + if (sz > pcCurNext-1)break; + ++iCnt; + } + this->mScene->mMaterials.back().mName = std::string( + (const char*)this->mCurrent,iCnt); + break; + case Dot3DSFile::CHUNK_MAT_DIFFUSE: + pc = &this->mScene->mMaterials.back().mDiffuse; + this->ParseColorChunk(pc); + if (is_qnan(pc->r)) + { + // color chunk is invalid. Simply ignore it + pc->r = pc->g = pc->b = 1.0f; + } + break; + case Dot3DSFile::CHUNK_MAT_SPECULAR: + pc = &this->mScene->mMaterials.back().mSpecular; + this->ParseColorChunk(pc); + if (is_qnan(pc->r)) + { + // color chunk is invalid. Simply ignore it + pc->r = pc->g = pc->b = 1.0f; + } + break; + case Dot3DSFile::CHUNK_MAT_AMBIENT: + pc = &this->mScene->mMaterials.back().mAmbient; + this->ParseColorChunk(pc); + if (is_qnan(pc->r)) + { + // color chunk is invalid. Simply ignore it + pc->r = pc->g = pc->b = 1.0f; + } + break; + case Dot3DSFile::CHUNK_MAT_SELF_ILLUM: + pc = &this->mScene->mMaterials.back().mEmissive; + this->ParseColorChunk(pc); + if (is_qnan(pc->r)) + { + // color chunk is invalid. Simply ignore it + // EMISSSIVE TO 0|0|0 + pc->r = pc->g = pc->b = 0.0f; + } + break; + case Dot3DSFile::CHUNK_MAT_TRANSPARENCY: + pcf = &this->mScene->mMaterials.back().mTransparency; + *pcf = this->ParsePercentageChunk(); + // NOTE: transparency, not opacity + *pcf = 1.0f - *pcf; + if (is_qnan(*pcf)) + *pcf = 0.0f; + break; + + case Dot3DSFile::CHUNK_MAT_SHADING: + + this->mScene->mMaterials.back().mShading = + (Dot3DS::Dot3DSFile::shadetype3ds)*((uint16_t*)this->mCurrent); + + this->mCurrent += sizeof(uint16_t); + break; + + case Dot3DSFile::CHUNK_MAT_SHININESS: + pcf = &this->mScene->mMaterials.back().mSpecularExponent; + *pcf = this->ParsePercentageChunk(); + if (is_qnan(*pcf)) + *pcf = 0.0f; + else *pcf *= (float)0xFFFF; + break; + + case Dot3DSFile::CHUNK_MAT_SELF_ILPCT: + pcf = &this->mScene->mMaterials.back().sTexEmissive.mTextureBlend; + *pcf = this->ParsePercentageChunk(); + if (is_qnan(*pcf)) + *pcf = 1.0f; + break; + + // parse texture chunks + case Dot3DSFile::CHUNK_MAT_TEXTURE: + iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk)); + this->ParseTextureChunk(&iRemaining,&this->mScene->mMaterials.back().sTexDiffuse); + break; + case Dot3DSFile::CHUNK_MAT_BUMPMAP: + iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk)); + this->ParseTextureChunk(&iRemaining,&this->mScene->mMaterials.back().sTexBump); + break; + case Dot3DSFile::CHUNK_MAT_OPACMAP: + iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk)); + this->ParseTextureChunk(&iRemaining,&this->mScene->mMaterials.back().sTexOpacity); + break; + case Dot3DSFile::CHUNK_MAT_MAT_SHINMAP: + iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk)); + this->ParseTextureChunk(&iRemaining,&this->mScene->mMaterials.back().sTexShininess); + break; + case Dot3DSFile::CHUNK_MAT_SPECMAP: + iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk)); + this->ParseTextureChunk(&iRemaining,&this->mScene->mMaterials.back().sTexSpecular); + break; + case Dot3DSFile::CHUNK_MAT_SELFIMAP: + iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk)); + this->ParseTextureChunk(&iRemaining,&this->mScene->mMaterials.back().sTexEmissive); + break; + }; + if ((unsigned int)pcCurNext < (unsigned int)this->mCurrent) + { + // place an error message. If we crash the programmer + // will be able to find it + this->mErrorText = ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG; + pcCurNext = this->mCurrent; + } + // Go to the starting position of the next chunk on this level + this->mCurrent = pcCurNext; + + *piRemaining -= psChunk->Size; + if (0 >= *piRemaining)return; + return ParseMaterialChunk(piRemaining); +} +// ------------------------------------------------------------------------------------------------ +void Dot3DSImporter::ParseTextureChunk(int* piRemaining,Dot3DS::Texture* pcOut) +{ + const Dot3DSFile::Chunk* psChunk; + + this->ReadChunk(&psChunk); + if (NULL == psChunk)return; + + const unsigned char* pcCur = this->mCurrent; + const unsigned char* pcCurNext = pcCur + (psChunk->Size + - sizeof(Dot3DSFile::Chunk)); + + // get chunk type + const unsigned char* sz = this->mCurrent; + unsigned int iCnt = 0; + switch (psChunk->Flag) + { + case Dot3DSFile::CHUNK_MAPFILE: + // string in file is zero-terminated, + // this should be no problem. However, validate whether + // it overlaps the end of the chunk, if yes we should + // truncate it. + while (*sz++ != '\0') + { + if (sz > pcCurNext-1)break; + ++iCnt; + } + pcOut->mMapName = std::string((const char*)this->mCurrent,iCnt); + break; + // manually parse the blend factor + case Dot3DSFile::CHUNK_PERCENTF: + pcOut->mTextureBlend = *((float*)this->mCurrent); + break; + // manually parse the blend factor + case Dot3DSFile::CHUNK_PERCENTW: + pcOut->mTextureBlend = (float)(*((short*)this->mCurrent)) / (float)0xFFFF; + break; + + case Dot3DSFile::CHUNK_MAT_MAP_USCALE: + pcOut->mScaleU = *((float*)this->mCurrent); + break; + case Dot3DSFile::CHUNK_MAT_MAP_VSCALE: + pcOut->mScaleV = *((float*)this->mCurrent); + break; + case Dot3DSFile::CHUNK_MAT_MAP_UOFFSET: + pcOut->mOffsetU = *((float*)this->mCurrent); + break; + case Dot3DSFile::CHUNK_MAT_MAP_VOFFSET: + pcOut->mOffsetV = *((float*)this->mCurrent); + break; + case Dot3DSFile::CHUNK_MAT_MAP_ANG: + pcOut->mRotation = *((float*)this->mCurrent); + break; + }; + + // Go to the starting position of the next chunk on this level + this->mCurrent = pcCurNext; + + *piRemaining -= psChunk->Size; + if (0 >= *piRemaining)return; + return ParseTextureChunk(piRemaining,pcOut); +} +// ------------------------------------------------------------------------------------------------ +float Dot3DSImporter::ParsePercentageChunk() +{ + const Dot3DSFile::Chunk* psChunk; + this->ReadChunk(&psChunk); + if (NULL == psChunk)return std::numeric_limits::quiet_NaN(); + + if (Dot3DSFile::CHUNK_PERCENTF == psChunk->Flag) + { + if (sizeof(float) > psChunk->Size) + return std::numeric_limits::quiet_NaN(); + return *((float*)this->mCurrent); + } + else if (Dot3DSFile::CHUNK_PERCENTW == psChunk->Flag) + { + if (2 > psChunk->Size) + return std::numeric_limits::quiet_NaN(); + return (float)(*((short*)this->mCurrent)) / (float)0xFFFF; + } + this->mCurrent += psChunk->Size - sizeof(Dot3DSFile::Chunk); + return std::numeric_limits::quiet_NaN(); +} +// ------------------------------------------------------------------------------------------------ +void Dot3DSImporter::ParseColorChunk(aiColor3D* p_pcOut, + bool p_bAcceptPercent) +{ + ai_assert(p_pcOut != NULL); + + // error return value + static const aiColor3D clrError = aiColor3D(std::numeric_limits::quiet_NaN(), + std::numeric_limits::quiet_NaN(), + std::numeric_limits::quiet_NaN()); + + const Dot3DSFile::Chunk* psChunk; + this->ReadChunk(&psChunk); + if (NULL == psChunk) + { + *p_pcOut = clrError; + return; + } + const unsigned char* pcCur = this->mCurrent; + this->mCurrent += psChunk->Size - sizeof(Dot3DSFile::Chunk); + bool bGamma = false; + switch(psChunk->Flag) + { + case Dot3DSFile::CHUNK_LINRGBF: + bGamma = true; + case Dot3DSFile::CHUNK_RGBF: + if (sizeof(float) * 3 > psChunk->Size - sizeof(Dot3DSFile::Chunk)) + { + *p_pcOut = clrError; + return; + } + p_pcOut->r = ((float*)pcCur)[0]; + p_pcOut->g = ((float*)pcCur)[1]; + p_pcOut->b = ((float*)pcCur)[2]; + break; + + case Dot3DSFile::CHUNK_LINRGBB: + bGamma = true; + case Dot3DSFile::CHUNK_RGBB: + if (sizeof(char) * 3 > psChunk->Size - sizeof(Dot3DSFile::Chunk)) + { + *p_pcOut = clrError; + return; + } + p_pcOut->r = (float)pcCur[0] / 255.0f; + p_pcOut->g = (float)pcCur[1] / 255.0f; + p_pcOut->b = (float)pcCur[2] / 255.0f; + break; + + // percentage chunks: accepted to be compatible with various + // .3ds files with very curious content + case Dot3DSFile::CHUNK_PERCENTF: + if (p_bAcceptPercent && 4 <= psChunk->Size - sizeof(Dot3DSFile::Chunk)) + { + p_pcOut->r = *((float*)pcCur); + p_pcOut->g = *((float*)pcCur); + p_pcOut->b = *((float*)pcCur); + break; + } + *p_pcOut = clrError; + return; + case Dot3DSFile::CHUNK_PERCENTW: + if (p_bAcceptPercent && 1 <= psChunk->Size - sizeof(Dot3DSFile::Chunk)) + { + p_pcOut->r = (float)pcCur[0] / 255.0f; + p_pcOut->g = (float)pcCur[0] / 255.0f; + p_pcOut->b = (float)pcCur[0] / 255.0f; + break; + } + *p_pcOut = clrError; + return; + + default: + // skip unknown chunks, hope this won't cause any problems. + return this->ParseColorChunk(p_pcOut,p_bAcceptPercent); + }; + // assume input gamma = 1.0, output gamma = 2.2 + // Not sure whether this is correct, too tired to + // think about it ;-) + if (bGamma) + { + p_pcOut->r = powf(p_pcOut->r, 1.0f / 2.2f); + p_pcOut->g = powf(p_pcOut->g, 1.0f / 2.2f); + p_pcOut->b = powf(p_pcOut->b, 1.0f / 2.2f); + } + return; +} diff --git a/code/3DSLoader.h b/code/3DSLoader.h new file mode 100644 index 000000000..7c0ff6c4a --- /dev/null +++ b/code/3DSLoader.h @@ -0,0 +1,235 @@ +/** @file Definition of the .3ds importer class. */ +#ifndef AI_3DSIMPORTER_H_INC +#define AI_3DSIMPORTER_H_INC + +#include + +#include "BaseImporter.h" + +#include "../include/aiTypes.h" + +struct aiNode; + +#include "3DSHelper.h" +namespace Assimp +{ + class MaterialHelper; + + using namespace Dot3DS; + + // --------------------------------------------------------------------------- + /** The Dot3DSImporter is a worker class capable of importing a scene from a + * 3ds Max 4/5 File (.3ds) + */ + class Dot3DSImporter : public BaseImporter + { + friend class Importer; + + protected: + /** Constructor to be privately used by Importer */ + Dot3DSImporter(); + + /** Destructor, private as well */ + ~Dot3DSImporter(); + + public: + // ------------------------------------------------------------------- + /** Returns whether the class can handle the format of the given file. + * See BaseImporter::CanRead() for details. */ + bool CanRead( const std::string& pFile, IOSystem* pIOHandler) const; + + protected: + // ------------------------------------------------------------------- + /** Imports the given file into the given scene structure. + * See BaseImporter::InternReadFile() for details + */ + void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); + + + // ------------------------------------------------------------------- + /** Converts a temporary material to the outer representation + */ + void ConvertMaterial(Dot3DS::Material& p_cMat, + MaterialHelper& p_pcOut); + + + // ------------------------------------------------------------------- + /** Read a chunk, get a pointer to it + * The mCurrent pointer will be increased by sizeof(Dot3DSFile::Chunk), + * thus pointing directly to the data of the chunk + */ + void ReadChunk(const Dot3DSFile::Chunk** p_ppcOut); + + // ------------------------------------------------------------------- + /** Parse a percentage chunk. mCurrent will point to the next + * chunk behind afterwards. If no percentage chunk is found + * QNAN is returned. + */ + float ParsePercentageChunk(); + + // ------------------------------------------------------------------- + /** Parse a color chunk. mCurrent will point to the next + * chunk behind afterwards. If no color chunk is found + * QNAN is returned in all members. + */ + void ParseColorChunk(aiColor3D* p_pcOut, + bool p_bAcceptPercent = true); + + + // ------------------------------------------------------------------- + /** Skip a chunk in the file + */ + void SkipChunk(); + + // ------------------------------------------------------------------- + /** Generate the nodegraph + */ + void GenerateNodeGraph(aiScene* pcOut); + + // ------------------------------------------------------------------- + /** Parse a main top-level chunk in the file + */ + void ParseMainChunk(int* piRemaining); + + // ------------------------------------------------------------------- + /** Parse a top-level chunk in the file + */ + void ParseChunk(int* piRemaining); + + // ------------------------------------------------------------------- + /** Parse a top-level editor chunk in the file + */ + void ParseEditorChunk(int* piRemaining); + + // ------------------------------------------------------------------- + /** Parse a top-level object chunk in the file + */ + void ParseObjectChunk(int* piRemaining); + + // ------------------------------------------------------------------- + /** Parse a material chunk in the file + */ + void ParseMaterialChunk(int* piRemaining); + + // ------------------------------------------------------------------- + /** Apply texture coordinate offsets + */ + void ApplyScaleNOffset(); + void BakeScaleNOffset(aiMesh* pcMesh, Dot3DS::Material* pcSrc); + + // ------------------------------------------------------------------- + /** Parse a mesh chunk in the file + */ + void ParseMeshChunk(int* piRemaining); + + // ------------------------------------------------------------------- + /** Parse a face list chunk in the file + */ + void ParseFaceChunk(int* piRemaining); + + // ------------------------------------------------------------------- + /** Parse a keyframe chunk in the file + */ + void ParseKeyframeChunk(int* piRemaining); + + // ------------------------------------------------------------------- + /** Parse a hierarchy chunk in the file + */ + void ParseHierarchyChunk(int* piRemaining); + + // ------------------------------------------------------------------- + /** Parse a texture chunk in the file + */ + void ParseTextureChunk(int* piRemaining,Dot3DS::Texture* pcOut); + + // ------------------------------------------------------------------- + /** Convert the meshes in the file + */ + void ConvertMeshes(aiScene* pcOut); + + // ------------------------------------------------------------------- + /** Replace the default material in the scene + */ + void ReplaceDefaultMaterial(); + + // ------------------------------------------------------------------- + /** Convert the whole scene + */ + void ConvertScene(aiScene* pcOut); + + // ------------------------------------------------------------------- + /** U/V Scaling/Offset handling + */ + void GenTexCoord (Dot3DS::Texture* pcTexture, + const std::vector& p_vIn, + std::vector& p_vOut); + + // ------------------------------------------------------------------- + /** generate normal vectors for a given mesh + */ + void GenNormals(Dot3DS::Mesh* sMesh); + + + // ------------------------------------------------------------------- + /** generate unique vertices for a mesh + */ + void MakeUnique(Dot3DS::Mesh* sMesh); + + + // ------------------------------------------------------------------- + /** Add a node to the node graph + */ + void AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,Dot3DS::Node* pcIn); + + // ------------------------------------------------------------------- + /** Search for a node in the graph. + * Called recursively + */ + void InverseNodeSearch(Dot3DS::Node* pcNode,Dot3DS::Node* pcCurrent); + + + // ------------------------------------------------------------------- + /** Apply the master scaling factor to the mesh + */ + void ApplyMasterScale(aiScene* pScene); + + + // ------------------------------------------------------------------- + /** Clamp all indices in the file to a valid range + */ + void Dot3DSImporter::CheckIndices(Dot3DS::Mesh* sMesh); + + + protected: + /** Buffer to hold the loaded file */ + unsigned char* mBuffer; + + /** Pointer to the current read position */ + const unsigned char* mCurrent; + + /** Used to store old chunk addresses to jump back in the file*/ + const unsigned char* mLast; + + /** Last touched node index */ + short mLastNodeIndex; + + /** Current node, root node */ + Dot3DS::Node* mCurrentNode, *mRootNode; + + /** Scene under construction */ + Dot3DS::Scene* mScene; + + /** Ambient base color of the scene */ + aiColor3D mClrAmbient; + + /** Master scaling factor of the scene */ + float mMasterScale; + + /** Path to the background image of the scene */ + std::string mBackgroundImage; + bool bHasBG; + }; + +} // end of namespace Assimp + +#endif // AI_3DSIMPORTER_H_INC \ No newline at end of file diff --git a/code/3DSSpatialSort.cpp b/code/3DSSpatialSort.cpp new file mode 100644 index 000000000..40962b74c --- /dev/null +++ b/code/3DSSpatialSort.cpp @@ -0,0 +1,148 @@ +/** @file Implementation of the helper class to quickly find vertices close to a given position */ +#include +#include "3DSSpatialSort.h" + +using namespace Assimp; +using namespace Assimp::Dot3DS; + +// ------------------------------------------------------------------------------------------------ +// Constructs a spatially sorted representation from the given position array. +D3DSSpatialSorter::D3DSSpatialSorter( const aiVector3D* pPositions, + unsigned int pNumPositions, unsigned int pElementOffset) + { + // define the reference plane. We choose some arbitrary vector away from all basic axises + // in the hope that no model spreads all its vertices along this plane. + mPlaneNormal.Set( 0.8523f, 0.34321f, 0.5736f); + mPlaneNormal.Normalize(); + + // store references to all given positions along with their distance to the reference plane + mPositions.reserve( pNumPositions); + for( unsigned int a = 0; a < pNumPositions; a++) + { + const char* tempPointer = reinterpret_cast (pPositions); + const aiVector3D* vec = reinterpret_cast (tempPointer + a * pElementOffset); + + // store position by index and distance + float distance = *vec * mPlaneNormal; + mPositions.push_back( Entry( a, *vec, distance,0)); + } + + // now sort the array ascending by distance. + std::sort( mPositions.begin(), mPositions.end()); + } +// ------------------------------------------------------------------------------------------------ +D3DSSpatialSorter::D3DSSpatialSorter( const Dot3DS::Mesh* p_pcMesh) + { + // define the reference plane. We choose some arbitrary vector away from all basic axises + // in the hope that no model spreads all its vertices along this plane. + mPlaneNormal.Set( 0.8523f, 0.34321f, 0.5736f); + mPlaneNormal.Normalize(); + + // store references to all given positions along with their distance to the reference plane + mPositions.reserve( p_pcMesh->mPositions.size()); + for( std::vector::const_iterator + i = p_pcMesh->mFaces.begin(); + i != p_pcMesh->mFaces.end();++i) + { + // store position by index and distance + float distance = p_pcMesh->mPositions[(*i).i1] * mPlaneNormal; + mPositions.push_back( Entry( (*i).i1, p_pcMesh->mPositions[(*i).i1], + distance, (*i).iSmoothGroup)); + + // triangle vertex 2 + distance = p_pcMesh->mPositions[(*i).i2] * mPlaneNormal; + mPositions.push_back( Entry( (*i).i2, p_pcMesh->mPositions[(*i).i2], + distance, (*i).iSmoothGroup)); + + // triangle vertex 3 + distance = p_pcMesh->mPositions[(*i).i3] * mPlaneNormal; + mPositions.push_back( Entry( (*i).i3, p_pcMesh->mPositions[(*i).i3], + distance, (*i).iSmoothGroup)); + } + + // now sort the array ascending by distance. + std::sort( this->mPositions.begin(), this->mPositions.end()); + } +// ------------------------------------------------------------------------------------------------ +// Destructor +D3DSSpatialSorter::~D3DSSpatialSorter() + { + // nothing to do here, everything destructs automatically + } + +// ------------------------------------------------------------------------------------------------ +// Returns an iterator for all positions close to the given position. +void D3DSSpatialSorter::FindPositions( const aiVector3D& pPosition, + uint32_t pSG,float pRadius, std::vector& poResults) const + { + float dist = pPosition * mPlaneNormal; + float minDist = dist - pRadius, maxDist = dist + pRadius; + + // clear the array in this strange fashion because a simple clear() would also deallocate + // the array which we want to avoid + poResults.erase( poResults.begin(), poResults.end()); + + // quick check for positions outside the range + if( mPositions.size() == 0) + return; + if( maxDist < mPositions.front().mDistance) + return; + if( minDist > mPositions.back().mDistance) + return; + + // do a binary search for the minimal distance to start the iteration there + unsigned int index = mPositions.size() / 2; + unsigned int binaryStepSize = mPositions.size() / 4; + while( binaryStepSize > 1) + { + if( mPositions[index].mDistance < minDist) + index += binaryStepSize; + else + index -= binaryStepSize; + + binaryStepSize /= 2; + } + + // depending on the direction of the last step we need to single step a bit back or forth + // to find the actual beginning element of the range + while( index > 0 && mPositions[index].mDistance > minDist) + index--; + while( index < (mPositions.size() - 1) && mPositions[index].mDistance < minDist) + index++; + + // Mow start iterating from there until the first position lays outside of the distance range. + // Add all positions inside the distance range within the given radius to the result aray + + if (0 == pSG) + { + std::vector::const_iterator it = mPositions.begin() + index; + float squareEpsilon = pRadius * pRadius; + while( it->mDistance < maxDist) + { + if((it->mPosition - pPosition).SquareLength() < squareEpsilon) + { + poResults.push_back( it->mIndex); + } + ++it; + if( it == mPositions.end()) + break; + } + } + else + { + std::vector::const_iterator it = mPositions.begin() + index; + float squareEpsilon = pRadius * pRadius; + while( it->mDistance < maxDist) + { + if((it->mPosition - pPosition).SquareLength() < squareEpsilon && + (it->mSmoothGroups & pSG || 0 == it->mSmoothGroups)) + { + poResults.push_back( it->mIndex); + } + ++it; + if( it == mPositions.end()) + break; + } + } + } + diff --git a/code/3DSSpatialSort.h b/code/3DSSpatialSort.h new file mode 100644 index 000000000..e3590754d --- /dev/null +++ b/code/3DSSpatialSort.h @@ -0,0 +1,83 @@ +/** Small helper classes to optimise finding vertizes close to a given location */ +#ifndef AI_D3DSSPATIALSORT_H_INC +#define AI_D3DSSPATIALSORT_H_INC + +#include +#include "../include/aiVector3D.h" +#include "3DSHelper.h" + +namespace Assimp +{ + +using namespace Dot3DS; + +// ------------------------------------------------------------------------------------------------ +/** Specialized version of SpatialSort to support smoothing groups + * This is used in the .3ds loader + */ +class D3DSSpatialSorter +{ +public: + + D3DSSpatialSorter() {/* This is unintialized. This is evil. This is OK. */} + + /** Constructs a spatially sorted representation from the given position array. + * Supply the positions in its layout in memory, the class will only refer to them + * by index. + * @param pPositions Pointer to the first position vector of the array. + * @param pNumPositions Number of vectors to expect in that array. + * @param pElementOffset Offset in bytes from the beginning of one vector in memory to the beginning of the next vector. + * @note Smoothing groups are ignored + */ + D3DSSpatialSorter( const aiVector3D* pPositions, + unsigned int pNumPositions, unsigned int pElementOffset); + + /** Construction from a given face array, handling smoothing groups properly + * @param p_pcMesh Input mesh. + */ + D3DSSpatialSorter( const Dot3DS::Mesh* p_pcMesh); + + + /** Destructor */ + ~D3DSSpatialSorter(); + + /** Returns an iterator for all positions close to the given position. + * @param pPosition The position to look for vertices. + * @param pSG Only included vertices with at least one shared smooth group + * @param pRadius Maximal distance from the position a vertex may have to be counted in. + * @param poResults The container to store the indices of the found positions. Will be emptied + * by the call so it may contain anything. + * @return An iterator to iterate over all vertices in the given area. + */ + void FindPositions( const aiVector3D& pPosition, uint32_t pSG, + float pRadius, std::vector& poResults) const; + +protected: + /** Normal of the sorting plane, normalized. The center is always at (0, 0, 0) */ + aiVector3D mPlaneNormal; + + /** An entry in a spatially sorted position array. Consists of a vertex index, + * its position and its precalculated distance from the reference plane */ + struct Entry + { + unsigned int mIndex; ///< The vertex referred by this entry + aiVector3D mPosition; ///< Position + uint32_t mSmoothGroups; + float mDistance; ///< Distance of this vertex to the sorting plane + + Entry() { /** intentionally not initialized.*/ } + Entry( unsigned int pIndex, const aiVector3D& pPosition, float pDistance,uint32_t pSG) + : + mPosition( pPosition), mIndex( pIndex), mDistance( pDistance),mSmoothGroups (pSG) + { } + + bool operator < (const Entry& e) const { return mDistance < e.mDistance; } + }; + + // all positions, sorted by distance to the sorting plane + std::vector mPositions; +}; + +} // end of namespace Assimp + +#endif // AI_SPATIALSORT_H_INC \ No newline at end of file diff --git a/code/Assimp.cpp b/code/Assimp.cpp new file mode 100644 index 000000000..e0abd0d2f --- /dev/null +++ b/code/Assimp.cpp @@ -0,0 +1,61 @@ +/** @file Implementation of the Plain-C API */ +#include + +#include "../include/assimp.h" +#include "../include/assimp.hpp" + +/** Stores the importer objects for all active import processes */ +typedef std::map< const aiScene*, Assimp::Importer* > ImporterMap; +/** Local storage of all active import processes */ +static ImporterMap gActiveImports; + +/** Error message of the last failed import process */ +static std::string gLastErrorString; + + +// ------------------------------------------------------------------------------------------------ +// Reads the given file and returns its content. +const aiScene* aiImportFile( const char* pFile, unsigned int pFlags) +{ + // create an Importer for this file + Assimp::Importer* imp = new Assimp::Importer; + // and have it read the file + const aiScene* scene = imp->ReadFile( pFile, pFlags); + + // if succeeded, place it in the collection of active processes + if( scene) + { + gActiveImports[scene] = imp; + } + else + { + // if failed, extract error code and destroy the import + gLastErrorString = imp->GetErrorString(); + delete imp; + } + + // return imported data. If the import failed the pointer is NULL anyways + return scene; +} + +// ------------------------------------------------------------------------------------------------ +// Releases all resources associated with the given import process. +void aiReleaseImport( const aiScene* pScene) +{ + // find the importer associated with this data + ImporterMap::iterator it = gActiveImports.find( pScene); + // it should be there... else the user is playing fools with us + if( it == gActiveImports.end()) + return; + + // kill the importer, the data dies with it + delete it->second; + gActiveImports.erase( it); +} + +// ------------------------------------------------------------------------------------------------ +// Returns the error text of the last failed import process. +const char* aiGetErrorString() +{ + return gLastErrorString.c_str(); +} diff --git a/code/BaseImporter.cpp b/code/BaseImporter.cpp new file mode 100644 index 000000000..5de86c599 --- /dev/null +++ b/code/BaseImporter.cpp @@ -0,0 +1,45 @@ +/** @file Implementation of the few default functions of the base importer class */ +#include "BaseImporter.h" +#include "../include/aiScene.h" +#include "aiAssert.h" +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +BaseImporter::BaseImporter() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +BaseImporter::~BaseImporter() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Imports the given file and returns the imported data. +aiScene* BaseImporter::ReadFile( const std::string& pFile, IOSystem* pIOHandler) +{ + // create a scene object to hold the data + aiScene* scene = new aiScene; + + // dispatch importing + try + { + InternReadFile( pFile, scene, pIOHandler); + } catch( ImportErrorException* exception) + { + // extract error description + mErrorText = exception->GetErrorText(); + delete exception; + + // and kill the partially imported data + delete scene; + scene = NULL; + } + + // return what we gathered from the import. + return scene; +} diff --git a/code/BaseImporter.h b/code/BaseImporter.h new file mode 100644 index 000000000..ad6a2c8b0 --- /dev/null +++ b/code/BaseImporter.h @@ -0,0 +1,105 @@ +/** @file Definition of the base class for all importer worker classes. */ +#ifndef AI_BASEIMPORTER_H_INC +#define AI_BASEIMPORTER_H_INC + +#include + +struct aiScene; + +namespace Assimp +{ + +class IOSystem; + +// --------------------------------------------------------------------------- +/** Simple exception class to be thrown if an error occurs while importing. */ +class ImportErrorException +{ +public: + /** Constructor with arguments */ + ImportErrorException( const std::string& pErrorText) + { + mErrorText = pErrorText; + } + + /** Returns the error text provided when throwing the exception */ + const std::string& GetErrorText() const { return mErrorText; } + +private: + std::string mErrorText; +}; + +// --------------------------------------------------------------------------- +/** The BaseImporter defines a common interface for all importer worker + * classes. + * + * The interface defines two functions: CanRead() is used to check if the + * importer can handle the format of the given file. If an implementation of + * this function returns true, the importer then calls ReadFile() which + * imports the given file. ReadFile is not overridable, it just calls InternReadFile() + * and catches any ImportErrorException that might occur. + */ +class BaseImporter +{ + friend class Importer; + +protected: + /** Constructor to be privately used by Importer */ + BaseImporter(); + + /** Destructor, private as well */ + virtual ~BaseImporter(); + +public: + // ------------------------------------------------------------------- + /** Returns whether the class can handle the format of the given file. + * @param pFile Path and file name of the file to be examined. + * @param pIOHandler The IO handler to use for accessing any file. + * @return true if the class can read this file, false if not. + */ + virtual bool CanRead( const std::string& pFile, IOSystem* pIOHandler) const = 0; + + // ------------------------------------------------------------------- + /** Imports the given file and returns the imported data. + * If the import succeeds, ownership of the data is transferred to the caller. + * If the import failes, NULL is returned. The function takes care that any + * partially constructed data is destroyed beforehand. + * + * @param pFile Path of the file to be imported. + * @param pIOHandler IO-Handler used to open this and possible other files. + * @return The imported data or NULL if failed. If it failed a human-readable + * error description can be retrieved by calling GetErrorText() + * + * @note This function is not intended to be overridden. Implement InternReadFile() + * to do the import. If an exception is thrown somewhere in InternReadFile(), + * this function will catch it and transform it into a suitable response to the caller. + */ + aiScene* ReadFile( const std::string& pFile, IOSystem* pIOHandler); + + // ------------------------------------------------------------------- + /** Returns the error description of the last error that occured. + * @return A description of the last error that occured. An empty string if no error. + */ + const std::string& GetErrorText() const { return mErrorText; } + +protected: + // ------------------------------------------------------------------- + /** Imports the given file into the given scene structure. The function is + * expected to throw an ImportErrorException if there is an error. If it + * terminates normally, the data in aiScene is expected to be correct. + * Override this function to implement the actual importing. + * + * @param pFile Path of the file to be imported. + * @param pScene The scene object to hold the imported data. + * @param pIOHandler The IO handler to use for any file access. + */ + virtual void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) = 0; + +protected: + /** Error description in case there was one. */ + std::string mErrorText; +}; + +} // end of namespace Assimp + +#endif // AI_BASEIMPORTER_H_INC \ No newline at end of file diff --git a/code/BaseProcess.h b/code/BaseProcess.h new file mode 100644 index 000000000..f9a0bba93 --- /dev/null +++ b/code/BaseProcess.h @@ -0,0 +1,59 @@ +/** @file Base class of all import post processing steps */ +#ifndef AI_BASEPROCESS_H_INC +#define AI_BASEPROCESS_H_INC + +struct aiScene; + +namespace Assimp +{ + +// --------------------------------------------------------------------------- +/** The BaseProcess defines a common interface for all post processing steps. + * A post processing step is run after a successful import if the caller + * specified the corresponding flag when calling ReadFile(). Enum #aiPostProcessSteps + * defines which flags are available. + * After a successful import the Importer iterates over its internal array of processes + * and calls IsActive() on each process to evaluate if the step should be executed. + * If the function returns true, the class' Execute() function is called subsequently. + */ +class BaseProcess +{ + friend class Importer; + +protected: + /** Constructor to be privately used by Importer */ + BaseProcess(); + + /** Destructor, private as well */ + virtual ~BaseProcess(); + +public: + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag field. + * @param pFlags The processing flags the importer was called with. A bitwise + * combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, false if not. + */ + virtual bool IsActive( unsigned int pFlags) const = 0; + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + virtual void Execute( aiScene* pScene) = 0; +}; + +/** Constructor, dummy implementation to keep the compiler from complaining */ +inline BaseProcess::BaseProcess() +{ +} + +/** Destructor */ +inline BaseProcess::~BaseProcess() +{ +} + +} // end of namespace Assimp + +#endif // AI_BASEPROCESS_H_INC \ No newline at end of file diff --git a/code/CalcTangentsProcess.cpp b/code/CalcTangentsProcess.cpp new file mode 100644 index 000000000..08773f8f9 --- /dev/null +++ b/code/CalcTangentsProcess.cpp @@ -0,0 +1,186 @@ +/** @file Implementation of the post processing step to calculate tangents and bitangents + * for all imported meshes + */ + +#include +#include +#include "CalcTangentsProcess.h" +#include "SpatialSort.h" +#include "../include/aiPostProcess.h" +#include "../include/aiMesh.h" +#include "../include/aiScene.h" + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +CalcTangentsProcess::CalcTangentsProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +CalcTangentsProcess::~CalcTangentsProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool CalcTangentsProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_CalcTangentSpace) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void CalcTangentsProcess::Execute( aiScene* pScene) +{ + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) + ProcessMesh( pScene->mMeshes[a]); +} + +// ------------------------------------------------------------------------------------------------ +// Calculates tangents and bitangents for the given mesh +void CalcTangentsProcess::ProcessMesh( aiMesh* pMesh) +{ + // we assume that the mesh is still in the verbose vertex format where each face has its own set + // of vertices and no vertices are shared between faces. Sadly I don't know any quick test to + // assert() it here. + //assert( must be verbose, dammit); + + // what we can check, though, is if the mesh has normals and texture coord. That's a requirement + if( pMesh->mNormals == NULL || pMesh->mTextureCoords[0] == NULL) + return; + + // calculate the position bounds so we have a reliable epsilon to check position differences against + aiVector3D minVec( 1e10f, 1e10f, 1e10f), maxVec( -1e10f, -1e10f, -1e10f); + for( unsigned int a = 0; a < pMesh->mNumVertices; a++) + { + minVec.x = std::min( minVec.x, pMesh->mVertices[a].x); + minVec.y = std::min( minVec.y, pMesh->mVertices[a].y); + minVec.z = std::min( minVec.z, pMesh->mVertices[a].z); + maxVec.x = std::max( maxVec.x, pMesh->mVertices[a].x); + maxVec.y = std::max( maxVec.y, pMesh->mVertices[a].y); + maxVec.z = std::max( maxVec.z, pMesh->mVertices[a].z); + } + + // calculate epsilons border + const float epsilon = 1e-5f; + const float posEpsilon = (maxVec - minVec).Length() * epsilon; + const float angleEpsilon = 0.9999f; + + // create space for the tangents and bitangents + pMesh->mTangents = new aiVector3D[pMesh->mNumVertices]; + pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices]; + + const aiVector3D* meshPos = pMesh->mVertices; + const aiVector3D* meshNorm = pMesh->mNormals; + const aiVector3D* meshTex = pMesh->mTextureCoords[0]; + aiVector3D* meshTang = pMesh->mTangents; + aiVector3D* meshBitang = pMesh->mBitangents; + + // calculate the tangent and bitangent for every face + for( unsigned int a = 0; a < pMesh->mNumFaces; a++) + { + const aiFace& face = pMesh->mFaces[a]; + + // triangle or polygon... we always use only the first three indices. A polygon + // is supposed to be planar anyways.... + // FIXME: (thom) create correct calculation for multi-vertex polygons maybe? + const unsigned int p0 = face.mIndices[0], p1 = face.mIndices[1], p2 = face.mIndices[2]; + + // position differences p1->p2 and p1->p3 + aiVector3D v = meshPos[p1] - meshPos[p0], w = meshPos[p2] - meshPos[p0]; + + // texture offset p1->p2 and p1->p3 + float sx = meshTex[p1].x - meshTex[p0].x, sy = meshTex[p1].y - meshTex[p0].y; + float tx = meshTex[p2].x - meshTex[p0].x, ty = meshTex[p2].y - meshTex[p0].y; + float dirCorrection = (tx * sy - ty * sx) < 0.0f ? -1.0f : 1.0f; + + // tangent points in the direction where to positive X axis of the texture coords would point in model space + // bitangents points along the positive Y axis of the texture coords, respectively + aiVector3D tangent, bitangent; + tangent.x = (w.x * sy - v.x * ty) * dirCorrection; + tangent.y = (w.y * sy - v.y * ty) * dirCorrection; + tangent.z = (w.z * sy - v.z * ty) * dirCorrection; + bitangent.x = (w.x * sx - v.x * tx) * dirCorrection; + bitangent.y = (w.y * sx - v.y * tx) * dirCorrection; + bitangent.z = (w.z * sx - v.z * tx) * dirCorrection; + + // store for every vertex of that face + for( unsigned int b = 0; b < face.mNumIndices; b++) + { + unsigned int p = face.mIndices[b]; + + // project tangent and bitangent into the plane formed by the vertex' normal + aiVector3D localTangent = tangent - meshNorm[p] * (tangent * meshNorm[p]); + aiVector3D localBitangent = bitangent - meshNorm[p] * (bitangent * meshNorm[p]); + localTangent.Normalize(); localBitangent.Normalize(); + + // and write it into the mesh. + meshTang[p] = localTangent; + meshBitang[p] = localBitangent; + } + } + + // create a helper to quickly find locally close vertices among the vertex array + SpatialSort vertexFinder( meshPos, pMesh->mNumVertices, sizeof( aiVector3D)); + std::vector verticesFound; + + // in the second pass we now smooth out all tangents and bitangents at the same local position + // if they are not too far off. + std::vector vertexDone( pMesh->mNumVertices, false); + for( unsigned int a = 0; a < pMesh->mNumVertices; a++) + { + if( vertexDone[a]) + continue; + + const aiVector3D& origPos = pMesh->mVertices[a]; + const aiVector3D& origNorm = pMesh->mNormals[a]; + const aiVector3D& origTang = pMesh->mTangents[a]; + const aiVector3D& origBitang = pMesh->mBitangents[a]; + std::vector closeVertices; + closeVertices.push_back( a); + + // find all vertices close to that position + vertexFinder.FindPositions( origPos, posEpsilon, verticesFound); + + // look among them for other vertices sharing the same normal and a close-enough tangent/bitangent + static const float MAX_DIFF_ANGLE = 0.701f; + for( unsigned int b = 0; b < verticesFound.size(); b++) + { + unsigned int idx = verticesFound[b]; + if( vertexDone[idx]) + continue; + if( meshNorm[idx] * origNorm < angleEpsilon) + continue; + if( meshTang[idx] * origTang < MAX_DIFF_ANGLE) + continue; + if( meshBitang[idx] * origBitang < MAX_DIFF_ANGLE) + continue; + + // it's similar enough -> add it to the smoothing group + closeVertices.push_back( idx); + vertexDone[idx] = true; + } + + // smooth the tangents and bitangents of all vertices that were found to be close enough + aiVector3D smoothTangent( 0, 0, 0), smoothBitangent( 0, 0, 0); + for( unsigned int b = 0; b < closeVertices.size(); b++) + { + smoothTangent += meshTang[ closeVertices[b] ]; + smoothBitangent += meshBitang[ closeVertices[b] ]; + } + smoothTangent.Normalize(); + smoothBitangent.Normalize(); + + // and write it back into all affected tangents + for( unsigned int b = 0; b < closeVertices.size(); b++) + { + meshTang[ closeVertices[b] ] = smoothTangent; + meshBitang[ closeVertices[b] ] = smoothBitangent; + } + } +} diff --git a/code/CalcTangentsProcess.h b/code/CalcTangentsProcess.h new file mode 100644 index 000000000..295e07137 --- /dev/null +++ b/code/CalcTangentsProcess.h @@ -0,0 +1,55 @@ +/** @file Defines a post processing step to calculate tangents and bitangents on all imported meshes.*/ +#ifndef AI_CALCTANGENTSPROCESS_H_INC +#define AI_CALCTANGENTSPROCESS_H_INC + +#include "BaseProcess.h" + +struct aiMesh; + +namespace Assimp +{ + +// --------------------------------------------------------------------------- +/** The CalcTangentsProcess calculates the tangent and bitangent for any vertex + * of all meshes. It is expected to be run before the JoinVerticesProcess runs + * because the joining of vertices also considers tangents and bitangents for uniqueness. + + */ +class CalcTangentsProcess : public BaseProcess +{ + friend class Importer; + +protected: + /** Constructor to be privately used by Importer */ + CalcTangentsProcess(); + + /** Destructor, private as well */ + ~CalcTangentsProcess(); + +public: + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag field. + * @param pFlags The processing flags the importer was called with. A bitwise + * combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + +protected: + // ------------------------------------------------------------------- + /** Calculates tangents and bitangents for the given mesh + * @param pMesh The mesh to process. + */ + void ProcessMesh( aiMesh* pMesh); +}; + +} // end of namespace Assimp + +#endif // AI_CALCTANGENTSPROCESS_H_INC \ No newline at end of file diff --git a/code/ConvertToLHProcess.cpp b/code/ConvertToLHProcess.cpp new file mode 100644 index 000000000..22ff35596 --- /dev/null +++ b/code/ConvertToLHProcess.cpp @@ -0,0 +1,155 @@ +/** @file Implementation of the post processing step to convert all imported data + * to a left-handed coordinate system. + */ +#include "ConvertToLHProcess.h" +#include "../include/aiPostProcess.h" +#include "../include/aiMesh.h" +#include "../include/aiAnim.h" +#include "../include/aiScene.h" + + +using namespace Assimp; + +// The transformation matrix to convert from DirectX coordinates to OpenGL coordinates. +const aiMatrix3x3 Assimp::ConvertToLHProcess::sToOGLTransform( + 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f + ); +// The transformation matrix to convert from OpenGL coordinates to DirectX coordinates. +const aiMatrix3x3 Assimp::ConvertToLHProcess::sToDXTransform( + 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 0.0f + ); + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +ConvertToLHProcess::ConvertToLHProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +ConvertToLHProcess::~ConvertToLHProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool ConvertToLHProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_ConvertToLeftHanded) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void ConvertToLHProcess::Execute( aiScene* pScene) +{ + // Check for an existent root node to proceed + if (NULL == pScene->mRootNode) + return; + + // transform the root node of the scene, the other nodes will follow then + ConvertToDX( pScene->mRootNode->mTransformation); + + // transform all meshes accordingly + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) + ProcessMesh( pScene->mMeshes[a]); + + // transform all animation channels affecting the root node as well + for( unsigned int a = 0; a < pScene->mNumAnimations; a++) + { + aiAnimation* anim = pScene->mAnimations[a]; + for( unsigned int b = 0; b < anim->mNumBones; b++) + { + aiBoneAnim* boneAnim = anim->mBones[b]; + if( strcmp( boneAnim->mBoneName.data, pScene->mRootNode->mName.data) == 0) + ProcessAnimation( boneAnim); + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Converts a single mesh to left handed coordinates. +void ConvertToLHProcess::ProcessMesh( aiMesh* pMesh) +{ + // invert the order of all faces in this mesh + for( unsigned int a = 0; a < pMesh->mNumFaces; a++) + { + aiFace& face = pMesh->mFaces[a]; + for( unsigned int b = 0; b < face.mNumIndices / 2; b++) + std::swap( face.mIndices[b], face.mIndices[ face.mNumIndices - 1 - b]); + } + + // mirror texture y coordinate + for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++) + { + if( pMesh->HasTextureCoords( a)) + { + for( unsigned int b = 0; b < pMesh->mNumVertices; b++) + pMesh->mTextureCoords[a][b].y = 1.0f - pMesh->mTextureCoords[a][b].y; + } + } + + // mirror bitangents as well as they're derived from the texture coords + if( pMesh->HasTangentsAndBitangents()) + { + for( unsigned int a = 0; a < pMesh->mNumVertices; a++) + pMesh->mBitangents[a] = -pMesh->mBitangents[a]; + } +} + +// ------------------------------------------------------------------------------------------------ +// Converts the given animation to LH coordinates. +void ConvertToLHProcess::ProcessAnimation( aiBoneAnim* pAnim) +{ + // position keys + for( unsigned int a = 0; a < pAnim->mNumPositionKeys; a++) + ConvertToDX( pAnim->mPositionKeys[a].mValue); + + // rotation keys + for( unsigned int a = 0; a < pAnim->mNumRotationKeys; a++) + { + aiMatrix3x3 rotmat = pAnim->mRotationKeys[a].mValue.GetMatrix(); + ConvertToDX( rotmat); + pAnim->mRotationKeys[a].mValue = aiQuaternion( rotmat); + } +} + +// ------------------------------------------------------------------------------------------------ +// Static helper function to convert a vector/matrix from DX to OGL coords +void ConvertToLHProcess::ConvertToOGL( aiVector3D& poVector) +{ + poVector = sToOGLTransform * poVector; +} + +// ------------------------------------------------------------------------------------------------ +void ConvertToLHProcess::ConvertToOGL( aiMatrix3x3& poMatrix) +{ + poMatrix *= sToOGLTransform; +} + +// ------------------------------------------------------------------------------------------------ +void ConvertToLHProcess::ConvertToOGL( aiMatrix4x4& poMatrix) +{ + poMatrix *= aiMatrix4x4( sToOGLTransform); +} + +// ------------------------------------------------------------------------------------------------ +// Static helper function to convert a vector/matrix from OGL back to DX coords +void ConvertToLHProcess::ConvertToDX( aiVector3D& poVector) +{ + poVector = sToDXTransform * poVector; +} + +// ------------------------------------------------------------------------------------------------ +void ConvertToLHProcess::ConvertToDX( aiMatrix3x3& poMatrix) +{ + poMatrix *= sToDXTransform; +} + +// ------------------------------------------------------------------------------------------------ +void ConvertToLHProcess::ConvertToDX( aiMatrix4x4& poMatrix) +{ + aiMatrix4x4 temp(sToDXTransform); + poMatrix *= temp; +} diff --git a/code/ConvertToLHProcess.h b/code/ConvertToLHProcess.h new file mode 100644 index 000000000..428779f43 --- /dev/null +++ b/code/ConvertToLHProcess.h @@ -0,0 +1,88 @@ +/** @file Defines a post processing step to convert all data to a left-handed coordinate system.*/ +#ifndef AI_CONVERTTOLHPROCESS_H_INC +#define AI_CONVERTTOLHPROCESS_H_INC + +#include "../include/aiTypes.h" +#include "BaseProcess.h" + +struct aiMesh; +struct aiBoneAnim; + +namespace Assimp +{ + +// --------------------------------------------------------------------------- +/** The ConvertToLHProcess converts all imported data to a left-handed coordinate + * system. This implies inverting the Z axis for all transformation matrices + * invert the orientation of all faces, and adapting skinning and animation + * data in a similar way. + */ +class ConvertToLHProcess : public BaseProcess +{ + friend class Importer; + +protected: + /** Constructor to be privately used by Importer */ + ConvertToLHProcess(); + + /** Destructor, private as well */ + ~ConvertToLHProcess(); + +public: + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag field. + * @param pFlags The processing flags the importer was called with. A bitwise + * combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + + // ------------------------------------------------------------------- + /** Static helper function to convert a vector/matrix from DX coords to OGL coords. + * @param poMatrix The matrix to convert. + */ + static void ConvertToOGL( aiVector3D& poVector); + static void ConvertToOGL( aiMatrix3x3& poMatrix); + static void ConvertToOGL( aiMatrix4x4& poMatrix); + + // ------------------------------------------------------------------- + /** Static helper function to convert a vector/matrix from OGL coords back to DX coords. + * @param poMatrix The matrix to convert. + */ + static void ConvertToDX( aiVector3D& poVector); + static void ConvertToDX( aiMatrix3x3& poMatrix); + static void ConvertToDX( aiMatrix4x4& poMatrix); + +protected: + // ------------------------------------------------------------------- + /** Converts a single mesh to left handed coordinates. + * This simply means the order of all faces is inverted. + * @param pMesh The mesh to convert. + */ + void ProcessMesh( aiMesh* pMesh); + + // ------------------------------------------------------------------- + /** Converts the given animation to LH coordinates. + * The rotation and translation keys are transformed, the scale keys + * work in local space and can therefore be left untouched. + * @param pAnim The bone animation to transform + */ + void ProcessAnimation( aiBoneAnim* pAnim); + +public: + /** The transformation matrix to convert from DirectX coordinates to OpenGL coordinates. */ + static const aiMatrix3x3 sToOGLTransform; + /** The transformation matrix to convert from OpenGL coordinates to DirectX coordinates. */ + static const aiMatrix3x3 sToDXTransform; +}; + +} // end of namespace Assimp + +#endif // AI_CONVERTTOLHPROCESS_H_INC \ No newline at end of file diff --git a/code/DefaultIOStream.cpp b/code/DefaultIOStream.cpp new file mode 100644 index 000000000..5e7955c31 --- /dev/null +++ b/code/DefaultIOStream.cpp @@ -0,0 +1,92 @@ +/** @file Default File I/O implementation for #Importer */ + +#include "DefaultIOStream.h" +#include "aiAssert.h" +#include +#include + +using namespace Assimp; + + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +DefaultIOStream::~DefaultIOStream() +{ + if (this->mFile) + { + fclose(this->mFile); + } +} + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +size_t DefaultIOStream::Read(void* pvBuffer, + size_t pSize, + size_t pCount) +{ + if (!this->mFile) + return 0; + + return fread(pvBuffer, pSize, pCount, this->mFile); +} + + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +size_t DefaultIOStream::Write(const void* pvBuffer, + size_t pSize, + size_t pCount) +{ + if (!this->mFile)return 0; + + fseek(mFile, 0, SEEK_SET); + return fwrite(pvBuffer, pSize, pCount, this->mFile); +} + + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +aiReturn DefaultIOStream::Seek(size_t pOffset, + aiOrigin pOrigin) +{ + if (!this->mFile)return AI_FAILURE; + + return (0 == fseek(this->mFile, pOffset, + (aiOrigin_CUR == pOrigin ? SEEK_CUR : + (aiOrigin_END == pOrigin ? SEEK_END : SEEK_SET))) + ? AI_SUCCESS : AI_FAILURE); +} + + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +size_t DefaultIOStream::Tell() const +{ + if (!this->mFile)return 0; + + return ftell(this->mFile); +} + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +size_t DefaultIOStream::FileSize() const +{ + ai_assert (!mFilename.empty()); + + if (NULL == mFile) + return 0; +#if defined _WIN32 && !defined __GNUC__ + struct __stat64 fileStat; + int err = _stat64( mFilename.c_str(), &fileStat ); + if (0 != err) + return 0; + return (size_t) (fileStat.st_size); +#else + struct stat fileStat; + int err = stat(mFilename.c_str(), &fileStat ); + if (0 != err) + return 0; + return (size_t) (fileStat.st_size); +#endif +} +// --------------------------------------------------------------------------- diff --git a/code/DefaultIOStream.h b/code/DefaultIOStream.h new file mode 100644 index 000000000..6f486cea9 --- /dev/null +++ b/code/DefaultIOStream.h @@ -0,0 +1,82 @@ +/** @file Default file I/O using fXXX()-family of functions */ +#ifndef AI_DEFAULTIOSTREAM_H_INC +#define AI_DEFAULTIOSTREAM_H_INC + +#include +#include +#include "IOStream.h" + +namespace Assimp +{ + +// --------------------------------------------------------------------------- +//! \class DefaultIOStream +//! \brief Default IO implementation, use standard IO operations +// --------------------------------------------------------------------------- +class DefaultIOStream : public IOStream +{ + friend class DefaultIOSystem; + +protected: + DefaultIOStream (); + DefaultIOStream (FILE* pFile, const std::string &strFilename); + +public: + /** Destructor public to allow simple deletion to close the file. */ + ~DefaultIOStream (); + + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + size_t Read(void* pvBuffer, + size_t pSize, + size_t pCount); + + + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + size_t Write(const void* pvBuffer, + size_t pSize, + size_t pCount); + + + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + aiReturn Seek(size_t pOffset, + aiOrigin pOrigin); + + + // ------------------------------------------------------------------- + // ------------------------------------------------------------------- + size_t Tell() const; + + //! Returns filesize + size_t FileSize() const; + +private: + //! File datastructure, using clib + FILE* mFile; + //! Filename + std::string mFilename; +}; +// --------------------------------------------------------------------------- +inline DefaultIOStream::DefaultIOStream () : + mFile(NULL), + mFilename("") +{ + // empty +} + +// --------------------------------------------------------------------------- +inline DefaultIOStream::DefaultIOStream (FILE* pFile, + const std::string &strFilename) : + mFile(pFile), + mFilename(strFilename) +{ + // empty +} + +// --------------------------------------------------------------------------- + +} // ns assimp + +#endif //!!AI_DEFAULTIOSTREAM_H_INC diff --git a/code/DefaultIOSystem.cpp b/code/DefaultIOSystem.cpp new file mode 100644 index 000000000..a9d99e719 --- /dev/null +++ b/code/DefaultIOSystem.cpp @@ -0,0 +1,64 @@ +/** @file Default implementation of IOSystem using the standard C file functions */ +#include +#include + +#include "DefaultIOSystem.h" +#include "DefaultIOStream.h" + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor. +DefaultIOSystem::DefaultIOSystem() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor. +DefaultIOSystem::~DefaultIOSystem() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Tests for the existence of a file at the given path. +bool DefaultIOSystem::Exists( const std::string& pFile) const +{ + FILE* file = fopen( pFile.c_str(), "rb"); + if( !file) + return false; + + fclose( file); + return true; +} + +// ------------------------------------------------------------------------------------------------ +// Open a new file with a given path. +IOStream* DefaultIOSystem::Open( const std::string& strFile, const std::string& strMode) +{ + FILE* file = fopen( strFile.c_str(), strMode.c_str()); + if( NULL == file) + return NULL; + + return new DefaultIOStream( file, strFile); +} + +// ------------------------------------------------------------------------------------------------ +// Closes the given file and releases all resources associated with it. +void DefaultIOSystem::Close( IOStream* pFile) +{ + delete pFile; +} + +// ------------------------------------------------------------------------------------------------ +// Returns the operation specific directory separator +std::string DefaultIOSystem::getOsSeparator() const +{ +#ifndef _WIN32 + std::string sep = "/"; +#else + std::string sep = "\\"; +#endif + return sep; +} diff --git a/code/DefaultIOSystem.h b/code/DefaultIOSystem.h new file mode 100644 index 000000000..c442fe809 --- /dev/null +++ b/code/DefaultIOSystem.h @@ -0,0 +1,40 @@ +/** @file Default implementation of IOSystem using the standard C file functions */ +#ifndef AI_DEFAULTIOSYSTEM_H_INC +#define AI_DEFAULTIOSYSTEM_H_INC + +#include "IOSystem.h" + +namespace Assimp +{ + +// --------------------------------------------------------------------------- +/** Default implementation of IOSystem using the standard C file functions */ +class DefaultIOSystem : public IOSystem +{ +public: + /** Constructor. */ + DefaultIOSystem(); + + /** Destructor. */ + ~DefaultIOSystem(); + + // ------------------------------------------------------------------- + /** Tests for the existence of a file at the given path. */ + bool Exists( const std::string& pFile) const; + + // ------------------------------------------------------------------- + /** Returns the directory separator. */ + std::string getOsSeparator() const; + + // ------------------------------------------------------------------- + /** Open a new file with a given path. */ + IOStream* Open( const std::string& pFile, const std::string& pMode = std::string("rb")); + + // ------------------------------------------------------------------- + /** Closes the given file and releases all resources associated with it. */ + void Close( IOStream* pFile); +}; + +} //!ns Assimp + +#endif //AI_DEFAULTIOSYSTEM_H_INC diff --git a/code/GenFaceNormalsProcess.cpp b/code/GenFaceNormalsProcess.cpp new file mode 100644 index 000000000..b2ac27d2c --- /dev/null +++ b/code/GenFaceNormalsProcess.cpp @@ -0,0 +1,67 @@ +/** @file Implementation of the post processing step to generate face +* normals for all imported faces. +*/ +#include "GenFaceNormalsProcess.h" +#include "../include/aiPostProcess.h" +#include "../include/aiMesh.h" +#include "../include/aiScene.h" + +using namespace Assimp; + +// Constructor to be privately used by Importer +GenFaceNormalsProcess::GenFaceNormalsProcess() + { + } + +// Destructor, private as well +GenFaceNormalsProcess::~GenFaceNormalsProcess() + { + // nothing to do here + } + +// ------------------------------------------------------------------- +// Returns whether the processing step is present in the given flag field. +bool GenFaceNormalsProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_GenNormals) != 0; +} + +// ------------------------------------------------------------------- +// Executes the post processing step on the given imported data. +void GenFaceNormalsProcess::Execute( aiScene* pScene) +{ + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) + this->GenMeshFaceNormals( pScene->mMeshes[a]); +} + +// ------------------------------------------------------------------- +// Executes the post processing step on the given imported data. +void GenFaceNormalsProcess::GenMeshFaceNormals (aiMesh* pMesh) +{ + if (NULL != pMesh->mNormals)return; + + pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; + + for( unsigned int a = 0; a < pMesh->mNumFaces; a++) + { + const aiFace& face = pMesh->mFaces[a]; + + // assume it is a triangle + aiVector3D* pV1 = &pMesh->mVertices[face.mIndices[0]]; + aiVector3D* pV2 = &pMesh->mVertices[face.mIndices[1]]; + aiVector3D* pV3 = &pMesh->mVertices[face.mIndices[2]]; + + aiVector3D pDelta1 = *pV2 - *pV1; + aiVector3D pDelta2 = *pV3 - *pV1; + aiVector3D vNor = pDelta1 ^ pDelta2; + + // NOTE: Never normalize here. Causes problems ... + //float fLength = vNor.Length(); + //if (0.0f != fLength)vNor /= fLength; + + pMesh->mNormals[face.mIndices[0]] = vNor; + pMesh->mNormals[face.mIndices[1]] = vNor; + pMesh->mNormals[face.mIndices[2]] = vNor; + } + return; +} \ No newline at end of file diff --git a/code/GenFaceNormalsProcess.h b/code/GenFaceNormalsProcess.h new file mode 100644 index 000000000..d1d29da1d --- /dev/null +++ b/code/GenFaceNormalsProcess.h @@ -0,0 +1,48 @@ +/** @file Defines a post processing step to compute face normals for all loaded faces*/ +#ifndef AI_GENFACENORMALPROCESS_H_INC +#define AI_GENFACENORMALPROCESS_H_INC + +#include "BaseProcess.h" +#include "../include/aiMesh.h" + +namespace Assimp +{ + +// --------------------------------------------------------------------------- +/** The GenFaceNormalsProcess computes face normals for all faces of all meshes +*/ +class GenFaceNormalsProcess : public BaseProcess +{ + friend class Importer; + +protected: + /** Constructor to be privately used by Importer */ + GenFaceNormalsProcess(); + + /** Destructor, private as well */ + ~GenFaceNormalsProcess(); + +public: + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag field. + * @param pFlags The processing flags the importer was called with. A bitwise + * combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + + +private: + void GenMeshFaceNormals (aiMesh* pcMesh); +}; + +} // end of namespace Assimp + +#endif // !!AI_GENFACENORMALPROCESS_H_INC \ No newline at end of file diff --git a/code/GenVertexNormalsProcess.cpp b/code/GenVertexNormalsProcess.cpp new file mode 100644 index 000000000..cc0a717b2 --- /dev/null +++ b/code/GenVertexNormalsProcess.cpp @@ -0,0 +1,104 @@ +/** @file Implementation of the post processing step to generate face +* normals for all imported faces. +*/ +#include "GenVertexNormalsProcess.h" +#include "SpatialSort.h" +#include "../include/aiPostProcess.h" +#include "../include/aiMesh.h" +#include "../include/aiScene.h" + +using namespace Assimp; + +// Constructor to be privately used by Importer +GenVertexNormalsProcess::GenVertexNormalsProcess() +{ +} + +// Destructor, private as well +GenVertexNormalsProcess::~GenVertexNormalsProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------- +// Returns whether the processing step is present in the given flag field. +bool GenVertexNormalsProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_GenSmoothNormals) != 0; +} + +// ------------------------------------------------------------------- +// Executes the post processing step on the given imported data. +void GenVertexNormalsProcess::Execute( aiScene* pScene) +{ + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) + this->GenMeshVertexNormals( pScene->mMeshes[a]); +} + +// ------------------------------------------------------------------- +// Executes the post processing step on the given imported data. +void GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh) +{ + if (NULL != pMesh->mNormals)return; + + pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; + for( unsigned int a = 0; a < pMesh->mNumFaces; a++) + { + const aiFace& face = pMesh->mFaces[a]; + + // assume it is a triangle + aiVector3D* pV1 = &pMesh->mVertices[face.mIndices[0]]; + aiVector3D* pV2 = &pMesh->mVertices[face.mIndices[1]]; + aiVector3D* pV3 = &pMesh->mVertices[face.mIndices[2]]; + + aiVector3D pDelta1 = *pV2 - *pV1; + aiVector3D pDelta2 = *pV3 - *pV1; + aiVector3D vNor = pDelta1 ^ pDelta2; + float fLength = vNor.Length(); + if (0.0f != fLength)vNor /= fLength; + + pMesh->mNormals[face.mIndices[0]] = vNor; + pMesh->mNormals[face.mIndices[1]] = vNor; + pMesh->mNormals[face.mIndices[2]] = vNor; + } + + // calculate the position bounds so we have a reliable epsilon to + // check position differences against + aiVector3D minVec( 1e10f, 1e10f, 1e10f), maxVec( -1e10f, -1e10f, -1e10f); + for( unsigned int a = 0; a < pMesh->mNumVertices; a++) + { + minVec.x = std::min( minVec.x, pMesh->mVertices[a].x); + minVec.y = std::min( minVec.y, pMesh->mVertices[a].y); + minVec.z = std::min( minVec.z, pMesh->mVertices[a].z); + maxVec.x = std::max( maxVec.x, pMesh->mVertices[a].x); + maxVec.y = std::max( maxVec.y, pMesh->mVertices[a].y); + maxVec.z = std::max( maxVec.z, pMesh->mVertices[a].z); + } + + const float posEpsilon = (maxVec - minVec).Length() * 1e-5f; + // set up a SpatialSort to quickly find all vertices close to a given position + SpatialSort vertexFinder( pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D)); + std::vector verticesFound; + + aiVector3D* pcNew = new aiVector3D[pMesh->mNumVertices]; + for (unsigned int i = 0; i < pMesh->mNumVertices;++i) + { + const aiVector3D& posThis = pMesh->mVertices[i]; + + // get all vertices that share this one ... + vertexFinder.FindPositions( posThis, posEpsilon, verticesFound); + + aiVector3D pcNor; + for (unsigned int a = 0; a < verticesFound.size(); ++a) + { + unsigned int vidx = verticesFound[a]; + pcNor += pMesh->mNormals[vidx]; + } + pcNor /= (float) verticesFound.size(); + pcNew[i] = pcNor; + } + delete pMesh->mNormals; + pMesh->mNormals = pcNew; + + return; +} \ No newline at end of file diff --git a/code/GenVertexNormalsProcess.h b/code/GenVertexNormalsProcess.h new file mode 100644 index 000000000..85bd7fc9b --- /dev/null +++ b/code/GenVertexNormalsProcess.h @@ -0,0 +1,47 @@ +/** @file Defines a post processing step to compute vertex normals for all loaded vertizes */ +#ifndef AI_GENVERTEXNORMALPROCESS_H_INC +#define AI_GENVERTEXNORMALPROCESS_H_INC + +#include "BaseProcess.h" +#include "../include/aiMesh.h" +namespace Assimp + { + + // --------------------------------------------------------------------------- + /** The GenFaceNormalsProcess computes vertex normals for all vertizes of all meshes + */ + class GenVertexNormalsProcess : public BaseProcess + { + friend class Importer; + + protected: + /** Constructor to be privately used by Importer */ + GenVertexNormalsProcess(); + + /** Destructor, private as well */ + ~GenVertexNormalsProcess(); + + public: + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag field. + * @param pFlags The processing flags the importer was called with. A bitwise + * combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + + + private: + void GenMeshVertexNormals (aiMesh* pcMesh); + }; + + } // end of namespace Assimp + +#endif // !!AI_GENVERTEXNORMALPROCESS_H_INC \ No newline at end of file diff --git a/code/Importer.cpp b/code/Importer.cpp new file mode 100644 index 000000000..cbf84e6c7 --- /dev/null +++ b/code/Importer.cpp @@ -0,0 +1,136 @@ +/** @file Implementation of the CPP-API class #Importer */ +#include +#include + +#include "../include/assimp.hpp" +#include "../include/aiScene.h" +#include "BaseImporter.h" +#include "BaseProcess.h" +#include "DefaultIOStream.h" +#include "DefaultIOSystem.h" +#include "XFileImporter.h" +#include "3DSLoader.h" +#include "MD3Loader.h" +#include "MD2Loader.h" +#include "PlyLoader.h" +#include "ObjFileImporter.h" +#include "CalcTangentsProcess.h" +#include "JoinVerticesProcess.h" +#include "ConvertToLHProcess.h" +#include "TriangulateProcess.h" +#include "GenFaceNormalsProcess.h" +#include "GenVertexNormalsProcess.h" +#include "KillNormalsProcess.h" +#include "SplitLargeMeshes.h" + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor. +Importer::Importer() : + mIOHandler(NULL), + mScene(NULL), + mErrorString("") +{ + // default IO handler + mIOHandler = new DefaultIOSystem; + + // add an instance of each worker class here + mImporter.push_back( new XFileImporter()); + mImporter.push_back( new ObjFileImporter()); + mImporter.push_back( new Dot3DSImporter()); + mImporter.push_back( new MD3Importer()); + mImporter.push_back( new MD2Importer()); + mImporter.push_back( new PLYImporter()); + + // add an instance of each post processing step here in the order of sequence it is executed + mPostProcessingSteps.push_back( new TriangulateProcess()); + mPostProcessingSteps.push_back( new SplitLargeMeshesProcess()); + mPostProcessingSteps.push_back( new KillNormalsProcess()); + mPostProcessingSteps.push_back( new GenFaceNormalsProcess()); + mPostProcessingSteps.push_back( new GenVertexNormalsProcess()); + mPostProcessingSteps.push_back( new CalcTangentsProcess()); + mPostProcessingSteps.push_back( new JoinVerticesProcess()); + mPostProcessingSteps.push_back( new ConvertToLHProcess()); +} + +// ------------------------------------------------------------------------------------------------ +// Destructor. +Importer::~Importer() +{ + for( unsigned int a = 0; a < mImporter.size(); a++) + delete mImporter[a]; + for( unsigned int a = 0; a < mPostProcessingSteps.size(); a++) + delete mPostProcessingSteps[a]; + + delete mIOHandler; + + // kill imported scene. Destructors should do that recursivly + delete mScene; +} + +// ------------------------------------------------------------------------------------------------ +// Supplies a custom IO handler to the importer to open and access files. +void Importer::SetIOHandler( IOSystem* pIOHandler) +{ + delete mIOHandler; + mIOHandler = pIOHandler; +} + +// ------------------------------------------------------------------------------------------------ +// Reads the given file and returns its contents if successful. +const aiScene* Importer::ReadFile( const std::string& pFile, unsigned int pFlags) +{ + // first check if the file is accessable at all + if( !mIOHandler->Exists( pFile)) + { + mErrorString = "Unable to open file \"" + pFile + "\"."; + return NULL; + } + + // find an worker class which can handle the file + BaseImporter* imp = NULL; + for( unsigned int a = 0; a < mImporter.size(); a++) + { + if( mImporter[a]->CanRead( pFile, mIOHandler)) + { + imp = mImporter[a]; + break; + } + } + + // put a proper error message if no suitable importer was found + if( !imp) + { + mErrorString = "No suitable reader found for the file format of file \"" + pFile + "\"."; + return NULL; + } + + // dispatch the reading to the worker class for this format + mScene = imp->ReadFile( pFile, mIOHandler); + // if failed, extract the error string + if( !mScene) + mErrorString = imp->GetErrorText(); + + // if successful, apply all active post processing steps to the imported data + if( mScene) + { + for( unsigned int a = 0; a < mPostProcessingSteps.size(); a++) + { + BaseProcess* process = mPostProcessingSteps[a]; + if( process->IsActive( pFlags)) + process->Execute( mScene); + } + } + + // either successful or failure - the pointer expresses it anyways + return mScene; +} + +// ------------------------------------------------------------------------------------------------ +// Empty and rpivate copy constructor +Importer::Importer(const Importer &other) +{ + // empty +} + diff --git a/code/JoinVerticesProcess.cpp b/code/JoinVerticesProcess.cpp new file mode 100644 index 000000000..30f0b4c62 --- /dev/null +++ b/code/JoinVerticesProcess.cpp @@ -0,0 +1,262 @@ +/** @file Implementation of the post processing step to join identical vertices + * for all imported meshes + */ + +#include +#include +#include "JoinVerticesProcess.h" +#include "SpatialSort.h" +#include "../include/aiPostProcess.h" +#include "../include/aiMesh.h" +#include "../include/aiScene.h" + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +JoinVerticesProcess::JoinVerticesProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +JoinVerticesProcess::~JoinVerticesProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool JoinVerticesProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_JoinIdenticalVertices) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void JoinVerticesProcess::Execute( aiScene* pScene) +{ + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) + ProcessMesh( pScene->mMeshes[a]); +} + +// ------------------------------------------------------------------------------------------------ +// Unites identical vertices in the given mesh +void JoinVerticesProcess::ProcessMesh( aiMesh* pMesh) +{ + // helper structure to hold all the data a single vertex can possibly have + typedef struct Vertex vertex; + + struct Vertex + { + aiVector3D mPosition; + aiVector3D mNormal; + aiVector3D mTangent, mBitangent; + aiColor4D mColors[AI_MAX_NUMBER_OF_COLOR_SETS]; + aiVector3D mTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; + }; + std::vector uniqueVertices; + uniqueVertices.reserve( pMesh->mNumVertices); + + // For each vertex the index of the vertex it was replaced by. + std::vector replaceIndex( pMesh->mNumVertices, 0xffffffff); + // for each vertex whether it was replaced by an existing unique vertex (true) or a new vertex was created for it (false) + std::vector isVertexUnique( pMesh->mNumVertices, false); + + // calculate the position bounds so we have a reliable epsilon to check position differences against + aiVector3D minVec( 1e10f, 1e10f, 1e10f), maxVec( -1e10f, -1e10f, -1e10f); + for( unsigned int a = 0; a < pMesh->mNumVertices; a++) + { + minVec.x = std::min( minVec.x, pMesh->mVertices[a].x); + minVec.y = std::min( minVec.y, pMesh->mVertices[a].y); + minVec.z = std::min( minVec.z, pMesh->mVertices[a].z); + maxVec.x = std::max( maxVec.x, pMesh->mVertices[a].x); + maxVec.y = std::max( maxVec.y, pMesh->mVertices[a].y); + maxVec.z = std::max( maxVec.z, pMesh->mVertices[a].z); + } + + // squared because we check against squared length of the vector difference + const float epsilon = 1e-5f; + const float posEpsilon = (maxVec - minVec).Length() * epsilon; + const float squareEpsilon = epsilon * epsilon; + + // a little helper to find locally close vertices faster + SpatialSort vertexFinder( pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D)); + std::vector verticesFound; + + // now check each vertex if it brings something new to the table + for( unsigned int a = 0; a < pMesh->mNumVertices; a++) + { + // collect the vertex data + Vertex v; + v.mPosition = pMesh->mVertices[a]; + v.mNormal = (pMesh->mNormals != NULL) ? pMesh->mNormals[a] : aiVector3D( 0, 0, 0); + v.mTangent = (pMesh->mTangents != NULL) ? pMesh->mTangents[a] : aiVector3D( 0, 0, 0); + v.mBitangent = (pMesh->mBitangents != NULL) ? pMesh->mBitangents[a] : aiVector3D( 0, 0, 0); + for( unsigned int b = 0; b < AI_MAX_NUMBER_OF_COLOR_SETS; b++) + v.mColors[b] = (pMesh->mColors[b] != NULL) ? pMesh->mColors[b][a] : aiColor4D( 0, 0, 0, 0); + for( unsigned int b = 0; b < AI_MAX_NUMBER_OF_TEXTURECOORDS; b++) + v.mTexCoords[b] = (pMesh->mTextureCoords[b] != NULL) ? pMesh->mTextureCoords[b][a] : aiVector3D( 0, 0, 0); + + // collect all vertices that are close enough to the given position + vertexFinder.FindPositions( v.mPosition, posEpsilon, verticesFound); + + unsigned int matchIndex = 0xffffffff; + // check all unique vertices close to the position if this vertex is already present among them + for( unsigned int b = 0; b < verticesFound.size(); b++) + { + unsigned int vidx = verticesFound[b]; + unsigned int uidx = replaceIndex[ vidx]; + if( uidx == 0xffffffff || !isVertexUnique[ vidx]) + continue; + + const Vertex& uv = uniqueVertices[ uidx]; + // Position mismatch is impossible - the vertex finder already discarded all non-matching positions + + // We just test the other attributes even if they're not present in the mesh. + // In this case they're initialized to 0 so the comparision succeeds. + // By this method the non-present attributes are effectively ignored in the comparision. + + if( (uv.mNormal - v.mNormal).SquareLength() > squareEpsilon) + continue; + if( (uv.mTangent - v.mTangent).SquareLength() > squareEpsilon) + continue; + if( (uv.mBitangent - v.mBitangent).SquareLength() > squareEpsilon) + continue; + // manually unrolled because continue wouldn't work as desired in an inner loop + assert( AI_MAX_NUMBER_OF_COLOR_SETS == 4); + if( GetColorDifference( uv.mColors[0], v.mColors[0]) > squareEpsilon) + continue; + if( GetColorDifference( uv.mColors[1], v.mColors[1]) > squareEpsilon) + continue; + if( GetColorDifference( uv.mColors[2], v.mColors[2]) > squareEpsilon) + continue; + if( GetColorDifference( uv.mColors[3], v.mColors[3]) > squareEpsilon) + continue; + // texture coord matching manually unrolled as well + assert( AI_MAX_NUMBER_OF_TEXTURECOORDS == 4); + if( (uv.mTexCoords[0] - v.mTexCoords[0]).SquareLength() > squareEpsilon) + continue; + if( (uv.mTexCoords[1] - v.mTexCoords[1]).SquareLength() > squareEpsilon) + continue; + if( (uv.mTexCoords[2] - v.mTexCoords[2]).SquareLength() > squareEpsilon) + continue; + if( (uv.mTexCoords[3] - v.mTexCoords[3]).SquareLength() > squareEpsilon) + continue; + + // we're still here -> this vertex perfectly matches our given vertex + matchIndex = uidx; + break; + } + + // found a replacement vertex among the uniques? + if( matchIndex != 0xffffffff) + { + // store where to found the matching unique vertex + replaceIndex[a] = matchIndex; + isVertexUnique[a] = false; + } else + { + // no unique vertex matches it upto now -> so add it + replaceIndex[a] = uniqueVertices.size(); + uniqueVertices.push_back( v); + isVertexUnique[a] = true; + } + } + + // replace vertex data with the unique data sets + pMesh->mNumVertices = uniqueVertices.size(); + // Position + delete [] pMesh->mVertices; + pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; + for( unsigned int a = 0; a < pMesh->mNumVertices; a++) + pMesh->mVertices[a] = uniqueVertices[a].mPosition; + // Normals, if present + if( pMesh->mNormals) + { + delete [] pMesh->mNormals; + pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; + for( unsigned int a = 0; a < pMesh->mNumVertices; a++) + pMesh->mNormals[a] = uniqueVertices[a].mNormal; + } + // Tangents, if present + if( pMesh->mTangents) + { + delete [] pMesh->mTangents; + pMesh->mTangents = new aiVector3D[pMesh->mNumVertices]; + for( unsigned int a = 0; a < pMesh->mNumVertices; a++) + pMesh->mTangents[a] = uniqueVertices[a].mTangent; + } + // Bitangents as well + if( pMesh->mBitangents) + { + delete [] pMesh->mBitangents; + pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices]; + for( unsigned int a = 0; a < pMesh->mNumVertices; a++) + pMesh->mBitangents[a] = uniqueVertices[a].mBitangent; + } + // Vertex colors + for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++) + { + if( !pMesh->mColors[a]) + continue; + + delete [] pMesh->mColors[a]; + pMesh->mColors[a] = new aiColor4D[pMesh->mNumVertices]; + for( unsigned int b = 0; b < pMesh->mNumVertices; b++) + pMesh->mColors[a][b] = uniqueVertices[b].mColors[a]; + } + // Texture coords + for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++) + { + if( !pMesh->mTextureCoords[a]) + continue; + + delete [] pMesh->mTextureCoords[a]; + pMesh->mTextureCoords[a] = new aiVector3D[pMesh->mNumVertices]; + for( unsigned int b = 0; b < pMesh->mNumVertices; b++) + pMesh->mTextureCoords[a][b] = uniqueVertices[b].mTexCoords[a]; + } + + // adjust the indices in all faces + for( unsigned int a = 0; a < pMesh->mNumFaces; a++) + { + aiFace& face = pMesh->mFaces[a]; + for( unsigned int b = 0; b < face.mNumIndices; b++) + { + const size_t index = face.mIndices[b]; + face.mIndices[b] = replaceIndex[face.mIndices[b]]; + } + } + + // adjust bone vertex weights. + for( unsigned int a = 0; a < pMesh->mNumBones; a++) + { + aiBone* bone = pMesh->mBones[a]; + std::vector newWeights; + newWeights.reserve( bone->mNumWeights); + + for( unsigned int b = 0; b < bone->mNumWeights; b++) + { + const aiVertexWeight& ow = bone->mWeights[b]; + // if the vertex is a unique one, translate it + if( isVertexUnique[ow.mVertexId]) + { + aiVertexWeight nw; + nw.mVertexId = replaceIndex[ow.mVertexId]; + nw.mWeight = ow.mWeight; + newWeights.push_back( nw); + } + } + + // there should be some. At least I think there should be some + assert( newWeights.size() > 0); + + // kill the old and replace them with the translated weights + delete [] bone->mWeights; + bone->mNumWeights = newWeights.size(); + bone->mWeights = new aiVertexWeight[bone->mNumWeights]; + memcpy( bone->mWeights, &newWeights[0], bone->mNumWeights * sizeof( aiVertexWeight)); + } +} diff --git a/code/JoinVerticesProcess.h b/code/JoinVerticesProcess.h new file mode 100644 index 000000000..2d44dddee --- /dev/null +++ b/code/JoinVerticesProcess.h @@ -0,0 +1,65 @@ +/** @file Defines a post processing step to join identical vertices on all imported meshes.*/ +#ifndef AI_JOINVERTICESPROCESS_H_INC +#define AI_CALCTANGENTSPROCESS_H_INC + +#include "BaseProcess.h" +#include "../include/aiTypes.h" + +struct aiMesh; + +namespace Assimp +{ + +// --------------------------------------------------------------------------- +/** The JoinVerticesProcess unites identical vertices in all imported meshes. + * By default the importer returns meshes where each face addressed its own + * set of vertices even if that means that identical vertices are stored multiple + * times. The JoinVerticesProcess finds these identical vertices and + * erases all but one of the copies. This usually reduces the number of vertices + * in a mesh by a serious amount and is the standard form to render a mesh. + */ +class JoinVerticesProcess : public BaseProcess +{ + friend class Importer; + +protected: + /** Constructor to be privately used by Importer */ + JoinVerticesProcess(); + + /** Destructor, private as well */ + ~JoinVerticesProcess(); + +public: + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag field. + * @param pFlags The processing flags the importer was called with. A bitwise + * combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + +protected: + // ------------------------------------------------------------------- + /** Unites identical vertices in the given mesh. + * @param pMesh The mesh to process. + */ + void ProcessMesh( aiMesh* pMesh); + + /** Little helper function to calculate the quadratic difference of two colours. */ + float GetColorDifference( const aiColor4D& pColor1, const aiColor4D& pColor2) const + { + aiColor4D c( pColor1.r - pColor2.r, pColor1.g - pColor2.g, pColor1.b - pColor2.b, pColor1.a - pColor2.a); + return c.r*c.r + c.g*c.g + c.b*c.b + c.a*c.a; + } +}; + +} // end of namespace Assimp + +#endif // AI_CALCTANGENTSPROCESS_H_INC \ No newline at end of file diff --git a/code/KillNormalsProcess.cpp b/code/KillNormalsProcess.cpp new file mode 100644 index 000000000..b924d8155 --- /dev/null +++ b/code/KillNormalsProcess.cpp @@ -0,0 +1,43 @@ +/** @file Implementation of the post processing step tokill mesh normals +*/ +#include "KillNormalsProcess.h" +#include "../include/aiPostProcess.h" +#include "../include/aiMesh.h" +#include "../include/aiScene.h" + +using namespace Assimp; + +// Constructor to be privately used by Importer +KillNormalsProcess::KillNormalsProcess() +{ +} + +// Destructor, private as well +KillNormalsProcess::~KillNormalsProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------- +// Returns whether the processing step is present in the given flag field. +bool KillNormalsProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_KillNormals) != 0; +} + +// ------------------------------------------------------------------- +// Executes the post processing step on the given imported data. +void KillNormalsProcess::Execute( aiScene* pScene) +{ + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) + this->KillMeshNormals( pScene->mMeshes[a]); +} + +// ------------------------------------------------------------------- +// Executes the post processing step on the given imported data. +void KillNormalsProcess::KillMeshNormals(aiMesh* pMesh) +{ + delete[] pMesh->mNormals; + pMesh->mNormals = NULL; + return; +} \ No newline at end of file diff --git a/code/KillNormalsProcess.h b/code/KillNormalsProcess.h new file mode 100644 index 000000000..23ff24bde --- /dev/null +++ b/code/KillNormalsProcess.h @@ -0,0 +1,47 @@ +/** @file Defines a post processing step to kill all loaded normals */ +#ifndef AI_KILLNORMALPROCESS_H_INC +#define AI_KILLNORMALPROCESS_H_INC + +#include "BaseProcess.h" +#include "../include/aiMesh.h" +namespace Assimp + { + + // --------------------------------------------------------------------------- + /** KillNormalsProcess: Class to kill all normals loaded + */ + class KillNormalsProcess : public BaseProcess + { + friend class Importer; + + protected: + /** Constructor to be privately used by Importer */ + KillNormalsProcess(); + + /** Destructor, private as well */ + ~KillNormalsProcess(); + + public: + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag field. + * @param pFlags The processing flags the importer was called with. A bitwise + * combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + + + private: + void KillMeshNormals (aiMesh* pcMesh); + }; + + } // end of namespace Assimp + +#endif // !!AI_KILLNORMALPROCESS_H_INC \ No newline at end of file diff --git a/code/MD2FileData.h b/code/MD2FileData.h new file mode 100644 index 000000000..b6f281688 --- /dev/null +++ b/code/MD2FileData.h @@ -0,0 +1,137 @@ +/** @file Defines the helper data structures for importing MD2 files */ +#ifndef AI_MD2FILEHELPER_H_INC +#define AI_MD2FILEHELPER_H_INC + +#include +#include +#include + +#include "../include/aiTypes.h" +#include "../include/aiMesh.h" +#include "../include/aiAnim.h" + +#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__) +# pragma pack(push,1) +# define PACK_STRUCT +#elif defined( __GNUC__ ) +# define PACK_STRUCT __attribute__((packed)) +#else +# error Compiler not supported +#endif + + +namespace Assimp +{ +//http://linux.ucla.edu/~phaethon/q3/formats/md2-schoenblum.html +namespace MD2 +{ + +#define AI_MD2_MAGIC_NUMBER_BE 'IDP2' +#define AI_MD2_MAGIC_NUMBER_LE '2PDI' + +// common limitations +#define AI_MD2_VERSION 15 +#define AI_MD2_MAXQPATH 64 +#define AI_MD2_MAX_FRAMES 512 +#define AI_MD2_MAX_SKINS 32 +#define AI_MD2_MAX_VERTS 2048 +#define AI_MD2_MAX_TRIANGLES 4096 + +// --------------------------------------------------------------------------- +/** \brief Data structure for the MD2 main header + */ +// --------------------------------------------------------------------------- +struct Header +{ + int32_t magic; + int32_t version; + int32_t skinWidth; + int32_t skinHeight; + int32_t frameSize; + int32_t numSkins; + int32_t numVertices; + int32_t numTexCoords; + int32_t numTriangles; + int32_t numGlCommands; + int32_t numFrames; + int32_t offsetSkins; + int32_t offsetTexCoords; + int32_t offsetTriangles; + int32_t offsetFrames; + int32_t offsetGlCommands; + int32_t offsetEnd; + +} PACK_STRUCT; + + +// --------------------------------------------------------------------------- +/** \brief Data structure for a MD2 OpenGl draw command + */ +// --------------------------------------------------------------------------- +struct GLCommand +{ + float s, t; + uint32_t vertexIndex; +} PACK_STRUCT; + +// --------------------------------------------------------------------------- +/** \brief Data structure for a MD2 triangle + */ +// --------------------------------------------------------------------------- +struct Triangle +{ + uint16_t vertexIndices[3]; + uint16_t textureIndices[3]; +} PACK_STRUCT; + +// --------------------------------------------------------------------------- +/** \brief Data structure for a MD2 vertex + */ +// --------------------------------------------------------------------------- +struct Vertex +{ + uint8_t vertex[3]; + uint8_t lightNormalIndex; +} PACK_STRUCT; + +// --------------------------------------------------------------------------- +/** \brief Data structure for a MD2 frame + */ +// --------------------------------------------------------------------------- +struct Frame +{ + float scale[3]; + float translate[3]; + char name[16]; + Vertex vertices[1]; +} PACK_STRUCT; + +// --------------------------------------------------------------------------- +/** \brief Data structure for a MD2 texture coordinate + */ +// --------------------------------------------------------------------------- +struct TexCoord +{ + int16_t s; + int16_t t; +} PACK_STRUCT; + +// --------------------------------------------------------------------------- +/** \brief Data structure for a MD2 skin + */ +// --------------------------------------------------------------------------- +struct Skin +{ + char name[AI_MD2_MAXQPATH]; /* texture file name */ +} PACK_STRUCT; + +// reset packing to the original value +#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__) +# pragma pack( pop ) +#endif +#undef PACK_STRUCT + +}; +}; + +#endif // !! include guard \ No newline at end of file diff --git a/code/MD2Loader.cpp b/code/MD2Loader.cpp new file mode 100644 index 000000000..634ace612 --- /dev/null +++ b/code/MD2Loader.cpp @@ -0,0 +1,310 @@ +/** @file Implementation of the MD2 importer class */ +#include "MD2Loader.h" +#include "MaterialSystem.h" + +#include "MD2NormalTable.h" + +#include "../include/IOStream.h" +#include "../include/IOSystem.h" +#include "../include/aiMesh.h" +#include "../include/aiScene.h" +#include "../include/aiAssert.h" + +#include + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +inline bool is_qnan(float p_fIn) +{ + // NOTE: Comparison against qnan is generally problematic + // because qnan == qnan is false AFAIK + union FTOINT + { + float fFloat; + int32_t iInt; + } one, two; + one.fFloat = std::numeric_limits::quiet_NaN(); + two.fFloat = p_fIn; + + return (one.iInt == two.iInt); +} +// ------------------------------------------------------------------------------------------------ +inline bool is_not_qnan(float p_fIn) +{ + return !is_qnan(p_fIn); +} + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +MD2Importer::MD2Importer() +{ +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +MD2Importer::~MD2Importer() +{ +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the class can handle the format of the given file. +bool MD2Importer::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); + + // not brilliant but working ;-) + if( extension == ".md2" || extension == ".MD2" || + extension == ".mD2" || extension == ".Md2") + return true; + + return false; +} +// ------------------------------------------------------------------------------------------------ +// Imports the given file into the given scene structure. +void MD2Importer::InternReadFile( + const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) +{ + boost::scoped_ptr file( pIOHandler->Open( pFile)); + + // Check whether we can read from the file + if( file.get() == NULL) + { + throw new ImportErrorException( "Failed to open md2 file " + pFile + "."); + } + + // check whether the md3 file is large enough to contain + // at least the file header + size_t fileSize = file->FileSize(); + if( fileSize < sizeof(MD2::Header)) + { + throw new ImportErrorException( ".md2 File is too small."); + } + + // allocate storage and copy the contents of the file to a memory buffer + this->mBuffer = new unsigned char[fileSize]; + file->Read( (void*)mBuffer, 1, fileSize); + + this->m_pcHeader = (const MD2::Header*)this->mBuffer; + + // check magic number + if (this->m_pcHeader->magic != AI_MD2_MAGIC_NUMBER_BE && + this->m_pcHeader->magic != AI_MD2_MAGIC_NUMBER_LE) + { + throw new ImportErrorException( "Invalid md2 file: Magic bytes not found"); + } + + // check file format version + if (this->m_pcHeader->version != 8) + { + throw new ImportErrorException( "Unsupported md3 file version"); + } + + // check some values whether they are valid + if (0 == this->m_pcHeader->numFrames) + { + throw new ImportErrorException( "Invalid md2 file: NUM_FRAMES is 0"); + } + if (this->m_pcHeader->offsetEnd > (int32_t)fileSize) + { + throw new ImportErrorException( "Invalid md2 file: File is too small"); + } + + // there won't be more than one mesh inside the file + pScene->mNumMaterials = 1; + pScene->mRootNode = new aiNode(); + pScene->mRootNode->mNumMeshes = 1; + pScene->mRootNode->mMeshes = new unsigned int[1]; + pScene->mRootNode->mMeshes[0] = 0; + pScene->mMaterials = new aiMaterial*[1]; + pScene->mMaterials[0] = new MaterialHelper(); + pScene->mNumMeshes = 1; + pScene->mMeshes = new aiMesh*[1]; + pScene->mMeshes[0] = new aiMesh(); + + // navigate to the begin of the frame data + const MD2::Frame* pcFrame = (const MD2::Frame*) ((unsigned char*)this->m_pcHeader + + this->m_pcHeader->offsetFrames); + + // navigate to the begin of the triangle data + MD2::Triangle* pcTriangles = (MD2::Triangle*) ((unsigned char*)this->m_pcHeader + + this->m_pcHeader->offsetTriangles); + + // navigate to the begin of the tex coords data + const MD2::TexCoord* pcTexCoords = (const MD2::TexCoord*) ((unsigned char*)this->m_pcHeader + + this->m_pcHeader->offsetTexCoords); + + // navigate to the begin of the vertex data + const MD2::Vertex* pcVerts = (const MD2::Vertex*) (pcFrame->vertices); + + pScene->mMeshes[0]->mNumFaces = this->m_pcHeader->numTriangles; + pScene->mMeshes[0]->mFaces = new aiFace[this->m_pcHeader->numTriangles]; + + // temporary vectors for position/texture coordinates/normals + std::vector vPositions; + std::vector vTexCoords; + std::vector vNormals; + + vPositions.resize(this->m_pcHeader->numVertices,aiVector3D()); + vTexCoords.resize(this->m_pcHeader->numVertices,aiVector3D( + std::numeric_limits::quiet_NaN(), + std::numeric_limits::quiet_NaN(),0.0f)); + vNormals.resize(this->m_pcHeader->numVertices,aiVector3D()); + + // not sure whether there are MD2 files without texture coordinates + if (0 != this->m_pcHeader->numTexCoords && 0 != this->m_pcHeader->numSkins) + { + // navigate to the first texture associated with the mesh + const MD2::Skin* pcSkins = (const MD2::Skin*) ((unsigned char*)this->m_pcHeader + + this->m_pcHeader->offsetSkins); + + const int iMode = (int)aiShadingMode_Gouraud; + MaterialHelper* pcHelper = (MaterialHelper*)pScene->mMaterials[0]; + pcHelper->AddProperty(&iMode, 1, AI_MATKEY_SHADING_MODEL); + + aiColor3D clr; + clr.b = clr.g = clr.r = 1.0f; + pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_DIFFUSE); + pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_SPECULAR); + + clr.b = clr.g = clr.r = 0.05f; + pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_AMBIENT); + + aiString szString; + const size_t iLen = strlen(pcSkins->name); + memcpy(szString.data,pcSkins->name,iLen+1); + szString.length = iLen-1; + + pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0)); + } + else + { + // apply a default material + const int iMode = (int)aiShadingMode_Gouraud; + MaterialHelper* pcHelper = (MaterialHelper*)pScene->mMaterials[0]; + pcHelper->AddProperty(&iMode, 1, AI_MATKEY_SHADING_MODEL); + + aiColor3D clr; + clr.b = clr.g = clr.r = 0.6f; + pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_DIFFUSE); + pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_SPECULAR); + + clr.b = clr.g = clr.r = 0.05f; + pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_AMBIENT); + } + + // now read all vertices of the frame + for (unsigned int i = 0; i < (unsigned int)this->m_pcHeader->numVertices;++i) + { + // read x,y, and z component of the vertex + + aiVector3D& vec = vPositions[i]; + + vec.x = (float)pcVerts[i].vertex[0] * pcFrame->scale[0]; + vec.x += pcFrame->translate[0]; + + // (flip z and y component) + vec.z = (float)pcVerts[i].vertex[1] * pcFrame->scale[1]; + vec.z += pcFrame->translate[1]; + + vec.y = (float)pcVerts[i].vertex[2] * pcFrame->scale[2]; + vec.y += pcFrame->translate[2]; + + // read the normal vector from the precalculated normal table + vNormals[i] = *((const aiVector3D*)(&g_avNormals[std::min( + int(pcVerts[i].lightNormalIndex), + int(sizeof(g_avNormals) / sizeof(g_avNormals[0]))-1)])); + + std::swap ( vNormals[i].y,vNormals[i].z ); + } + + // now read all triangles of the first frame, apply scaling and translation + if (0 != this->m_pcHeader->numTexCoords) + { + for (unsigned int i = 0; i < (unsigned int)this->m_pcHeader->numTriangles;++i) + { + // allocate the face + pScene->mMeshes[0]->mFaces[i].mIndices = new unsigned int[3]; + pScene->mMeshes[0]->mFaces[i].mNumIndices = 3; + + // copy texture coordinates + // check whether they are different from the previous value at this index. + // In this case, create a full separate set of vertices/normals/texcoords + for (unsigned int c = 0; c < 3;++c) + { + // validate vertex indices + if (pcTriangles[i].vertexIndices[c] >= this->m_pcHeader->numVertices) + pcTriangles[i].vertexIndices[c] = this->m_pcHeader->numVertices-1; + + // copy face indices + pScene->mMeshes[0]->mFaces[i].mIndices[c] = (unsigned int)pcTriangles[i].vertexIndices[c]; + + // validate texture coordinates + if (pcTriangles[i].textureIndices[c] >= this->m_pcHeader->numTexCoords) + pcTriangles[i].textureIndices[c] = this->m_pcHeader->numTexCoords-1; + + aiVector3D* pcOut = &vTexCoords[pScene->mMeshes[0]->mFaces[i].mIndices[c]]; + float u,v; + u = (float)pcTexCoords[pcTriangles[i].textureIndices[c]].s / this->m_pcHeader->skinWidth; + v = (float)pcTexCoords[pcTriangles[i].textureIndices[c]].t / this->m_pcHeader->skinHeight; + + if ( is_not_qnan ( pcOut->x ) && (pcOut->x != u || pcOut->y != v)) + { + // generate a separate vertex/index set + vTexCoords.push_back(aiVector3D(u,v,0.0f)); + vPositions.push_back(vPositions[pcTriangles[i].vertexIndices[c]]); + vNormals.push_back(vPositions[pcTriangles[i].vertexIndices[c]]); + unsigned int iPos = vTexCoords.size()-1; + + pScene->mMeshes[0]->mFaces[i].mIndices[c] = iPos; + } + else + { + pcOut->x = u; + pcOut->y = v; + + } + } + } + } + else + { + for (unsigned int i = 0; i < (unsigned int)this->m_pcHeader->numTriangles;++i) + { + // allocate the face + pScene->mMeshes[0]->mFaces[i].mIndices = new unsigned int[3]; + pScene->mMeshes[0]->mFaces[i].mNumIndices = 3; + + // validate vertex indices + if (pcTriangles[i].vertexIndices[0] >= this->m_pcHeader->numVertices) + pcTriangles[i].vertexIndices[0] = this->m_pcHeader->numVertices-1; + if (pcTriangles[i].vertexIndices[1] >= this->m_pcHeader->numVertices) + pcTriangles[i].vertexIndices[1] = this->m_pcHeader->numVertices-1; + if (pcTriangles[i].vertexIndices[2] >= this->m_pcHeader->numVertices) + pcTriangles[i].vertexIndices[2] = this->m_pcHeader->numVertices-1; + + // copy face indices + pScene->mMeshes[0]->mFaces[i].mIndices[0] = (unsigned int)pcTriangles[i].vertexIndices[0]; + pScene->mMeshes[0]->mFaces[i].mIndices[1] = (unsigned int)pcTriangles[i].vertexIndices[1]; + pScene->mMeshes[0]->mFaces[i].mIndices[2] = (unsigned int)pcTriangles[i].vertexIndices[2]; + } + } + + // allocate output storage + pScene->mMeshes[0]->mNumVertices = vPositions.size(); + pScene->mMeshes[0]->mVertices = new aiVector3D[vPositions.size()]; + pScene->mMeshes[0]->mNormals = new aiVector3D[vPositions.size()]; + pScene->mMeshes[0]->mTextureCoords[0] = new aiVector3D[vPositions.size()]; + + // memcpy() the data to the c-syle arrays + memcpy(pScene->mMeshes[0]->mVertices, &vPositions[0], vPositions.size() * sizeof(aiVector3D)); + memcpy(pScene->mMeshes[0]->mNormals, &vNormals[0], vPositions.size() * sizeof(aiVector3D)); + memcpy(pScene->mMeshes[0]->mTextureCoords[0], &vTexCoords[0], vPositions.size() * sizeof(aiVector3D)); + + return; +} \ No newline at end of file diff --git a/code/MD2Loader.h b/code/MD2Loader.h new file mode 100644 index 000000000..5b7f6d619 --- /dev/null +++ b/code/MD2Loader.h @@ -0,0 +1,57 @@ +/** @file Definition of the .MD2 importer class. */ +#ifndef AI_MD2LOADER_H_INCLUDED +#define AI_MD2LOADER_H_INCLUDED + +#include "BaseImporter.h" +#include "../include/aiTypes.h" + +struct aiNode; +#include "MD2FileData.h" + +namespace Assimp +{ + class MaterialHelper; + + using namespace MD2; + + // --------------------------------------------------------------------------- + /** Used to load MD2 files + */ + class MD2Importer : public BaseImporter + { + friend class Importer; + + protected: + /** Constructor to be privately used by Importer */ + MD2Importer(); + + /** Destructor, private as well */ + ~MD2Importer(); + + public: + + // ------------------------------------------------------------------- + /** Returns whether the class can handle the format of the given file. + * See BaseImporter::CanRead() for details. */ + bool CanRead( const std::string& pFile, IOSystem* pIOHandler) const; + + protected: + + // ------------------------------------------------------------------- + /** Imports the given file into the given scene structure. + * See BaseImporter::InternReadFile() for details + */ + void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); + + protected: + + /** Header of the MD2 file */ + const MD2::Header* m_pcHeader; + + /** Buffer to hold the loaded file */ + const unsigned char* mBuffer; + }; + +} // end of namespace Assimp + +#endif // AI_3DSIMPORTER_H_INC \ No newline at end of file diff --git a/code/MD2NormalTable.h b/code/MD2NormalTable.h new file mode 100644 index 000000000..0126055a6 --- /dev/null +++ b/code/MD2NormalTable.h @@ -0,0 +1,171 @@ +/* + * Slightly modified version of the anorms.h header file released by + * ID software with the Quake 2 source code. + * + * Table of normals used by MD2 models + */ + +float g_avNormals[][3] = { +{ -0.525731f, 0.000000f, 0.850651f }, +{ -0.442863f, 0.238856f, 0.864188f }, +{ -0.295242f, 0.000000f, 0.955423f }, +{ -0.309017f, 0.500000f, 0.809017f }, +{ -0.162460f, 0.262866f, 0.951056f }, +{ 0.000000f, 0.000000f, 1.000000f }, +{ 0.000000f, 0.850651f, 0.525731f }, +{ -0.147621f, 0.716567f, 0.681718f }, +{ 0.147621f, 0.716567f, 0.681718f }, +{ 0.000000f, 0.525731f, 0.850651f }, +{ 0.309017f, 0.500000f, 0.809017f }, +{ 0.525731f, 0.000000f, 0.850651f }, +{ 0.295242f, 0.000000f, 0.955423f }, +{ 0.442863f, 0.238856f, 0.864188f }, +{ 0.162460f, 0.262866f, 0.951056f }, +{ -0.681718f, 0.147621f, 0.716567f }, +{ -0.809017f, 0.309017f, 0.500000f }, +{ -0.587785f, 0.425325f, 0.688191f }, +{ -0.850651f, 0.525731f, 0.000000f }, +{ -0.864188f, 0.442863f, 0.238856f }, +{ -0.716567f, 0.681718f, 0.147621f }, +{ -0.688191f, 0.587785f, 0.425325f }, +{ -0.500000f, 0.809017f, 0.309017f }, +{ -0.238856f, 0.864188f, 0.442863f }, +{ -0.425325f, 0.688191f, 0.587785f }, +{ -0.716567f, 0.681718f, -0.147621f }, +{ -0.500000f, 0.809017f, -0.309017f }, +{ -0.525731f, 0.850651f, 0.000000f }, +{ 0.000000f, 0.850651f, -0.525731f }, +{ -0.238856f, 0.864188f, -0.442863f }, +{ 0.000000f, 0.955423f, -0.295242f }, +{ -0.262866f, 0.951056f, -0.162460f }, +{ 0.000000f, 1.000000f, 0.000000f }, +{ 0.000000f, 0.955423f, 0.295242f }, +{ -0.262866f, 0.951056f, 0.162460f }, +{ 0.238856f, 0.864188f, 0.442863f }, +{ 0.262866f, 0.951056f, 0.162460f }, +{ 0.500000f, 0.809017f, 0.309017f }, +{ 0.238856f, 0.864188f, -0.442863f }, +{ 0.262866f, 0.951056f, -0.162460f }, +{ 0.500000f, 0.809017f, -0.309017f }, +{ 0.850651f, 0.525731f, 0.000000f }, +{ 0.716567f, 0.681718f, 0.147621f }, +{ 0.716567f, 0.681718f, -0.147621f }, +{ 0.525731f, 0.850651f, 0.000000f }, +{ 0.425325f, 0.688191f, 0.587785f }, +{ 0.864188f, 0.442863f, 0.238856f }, +{ 0.688191f, 0.587785f, 0.425325f }, +{ 0.809017f, 0.309017f, 0.500000f }, +{ 0.681718f, 0.147621f, 0.716567f }, +{ 0.587785f, 0.425325f, 0.688191f }, +{ 0.955423f, 0.295242f, 0.000000f }, +{ 1.000000f, 0.000000f, 0.000000f }, +{ 0.951056f, 0.162460f, 0.262866f }, +{ 0.850651f, -0.525731f, 0.000000f }, +{ 0.955423f, -0.295242f, 0.000000f }, +{ 0.864188f, -0.442863f, 0.238856f }, +{ 0.951056f, -0.162460f, 0.262866f }, +{ 0.809017f, -0.309017f, 0.500000f }, +{ 0.681718f, -0.147621f, 0.716567f }, +{ 0.850651f, 0.000000f, 0.525731f }, +{ 0.864188f, 0.442863f, -0.238856f }, +{ 0.809017f, 0.309017f, -0.500000f }, +{ 0.951056f, 0.162460f, -0.262866f }, +{ 0.525731f, 0.000000f, -0.850651f }, +{ 0.681718f, 0.147621f, -0.716567f }, +{ 0.681718f, -0.147621f, -0.716567f }, +{ 0.850651f, 0.000000f, -0.525731f }, +{ 0.809017f, -0.309017f, -0.500000f }, +{ 0.864188f, -0.442863f, -0.238856f }, +{ 0.951056f, -0.162460f, -0.262866f }, +{ 0.147621f, 0.716567f, -0.681718f }, +{ 0.309017f, 0.500000f, -0.809017f }, +{ 0.425325f, 0.688191f, -0.587785f }, +{ 0.442863f, 0.238856f, -0.864188f }, +{ 0.587785f, 0.425325f, -0.688191f }, +{ 0.688191f, 0.587785f, -0.425325f }, +{ -0.147621f, 0.716567f, -0.681718f }, +{ -0.309017f, 0.500000f, -0.809017f }, +{ 0.000000f, 0.525731f, -0.850651f }, +{ -0.525731f, 0.000000f, -0.850651f }, +{ -0.442863f, 0.238856f, -0.864188f }, +{ -0.295242f, 0.000000f, -0.955423f }, +{ -0.162460f, 0.262866f, -0.951056f }, +{ 0.000000f, 0.000000f, -1.000000f }, +{ 0.295242f, 0.000000f, -0.955423f }, +{ 0.162460f, 0.262866f, -0.951056f }, +{ -0.442863f, -0.238856f, -0.864188f }, +{ -0.309017f, -0.500000f, -0.809017f }, +{ -0.162460f, -0.262866f, -0.951056f }, +{ 0.000000f, -0.850651f, -0.525731f }, +{ -0.147621f, -0.716567f, -0.681718f }, +{ 0.147621f, -0.716567f, -0.681718f }, +{ 0.000000f, -0.525731f, -0.850651f }, +{ 0.309017f, -0.500000f, -0.809017f }, +{ 0.442863f, -0.238856f, -0.864188f }, +{ 0.162460f, -0.262866f, -0.951056f }, +{ 0.238856f, -0.864188f, -0.442863f }, +{ 0.500000f, -0.809017f, -0.309017f }, +{ 0.425325f, -0.688191f, -0.587785f }, +{ 0.716567f, -0.681718f, -0.147621f }, +{ 0.688191f, -0.587785f, -0.425325f }, +{ 0.587785f, -0.425325f, -0.688191f }, +{ 0.000000f, -0.955423f, -0.295242f }, +{ 0.000000f, -1.000000f, 0.000000f }, +{ 0.262866f, -0.951056f, -0.162460f }, +{ 0.000000f, -0.850651f, 0.525731f }, +{ 0.000000f, -0.955423f, 0.295242f }, +{ 0.238856f, -0.864188f, 0.442863f }, +{ 0.262866f, -0.951056f, 0.162460f }, +{ 0.500000f, -0.809017f, 0.309017f }, +{ 0.716567f, -0.681718f, 0.147621f }, +{ 0.525731f, -0.850651f, 0.000000f }, +{ -0.238856f, -0.864188f, -0.442863f }, +{ -0.500000f, -0.809017f, -0.309017f }, +{ -0.262866f, -0.951056f, -0.162460f }, +{ -0.850651f, -0.525731f, 0.000000f }, +{ -0.716567f, -0.681718f, -0.147621f }, +{ -0.716567f, -0.681718f, 0.147621f }, +{ -0.525731f, -0.850651f, 0.000000f }, +{ -0.500000f, -0.809017f, 0.309017f }, +{ -0.238856f, -0.864188f, 0.442863f }, +{ -0.262866f, -0.951056f, 0.162460f }, +{ -0.864188f, -0.442863f, 0.238856f }, +{ -0.809017f, -0.309017f, 0.500000f }, +{ -0.688191f, -0.587785f, 0.425325f }, +{ -0.681718f, -0.147621f, 0.716567f }, +{ -0.442863f, -0.238856f, 0.864188f }, +{ -0.587785f, -0.425325f, 0.688191f }, +{ -0.309017f, -0.500000f, 0.809017f }, +{ -0.147621f, -0.716567f, 0.681718f }, +{ -0.425325f, -0.688191f, 0.587785f }, +{ -0.162460f, -0.262866f, 0.951056f }, +{ 0.442863f, -0.238856f, 0.864188f }, +{ 0.162460f, -0.262866f, 0.951056f }, +{ 0.309017f, -0.500000f, 0.809017f }, +{ 0.147621f, -0.716567f, 0.681718f }, +{ 0.000000f, -0.525731f, 0.850651f }, +{ 0.425325f, -0.688191f, 0.587785f }, +{ 0.587785f, -0.425325f, 0.688191f }, +{ 0.688191f, -0.587785f, 0.425325f }, +{ -0.955423f, 0.295242f, 0.000000f }, +{ -0.951056f, 0.162460f, 0.262866f }, +{ -1.000000f, 0.000000f, 0.000000f }, +{ -0.850651f, 0.000000f, 0.525731f }, +{ -0.955423f, -0.295242f, 0.000000f }, +{ -0.951056f, -0.162460f, 0.262866f }, +{ -0.864188f, 0.442863f, -0.238856f }, +{ -0.951056f, 0.162460f, -0.262866f }, +{ -0.809017f, 0.309017f, -0.500000f }, +{ -0.864188f, -0.442863f, -0.238856f }, +{ -0.951056f, -0.162460f, -0.262866f }, +{ -0.809017f, -0.309017f, -0.500000f }, +{ -0.681718f, 0.147621f, -0.716567f }, +{ -0.681718f, -0.147621f, -0.716567f }, +{ -0.850651f, 0.000000f, -0.525731f }, +{ -0.688191f, 0.587785f, -0.425325f }, +{ -0.587785f, 0.425325f, -0.688191f }, +{ -0.425325f, 0.688191f, -0.587785f }, +{ -0.425325f, -0.688191f, -0.587785f }, +{ -0.587785f, -0.425325f, -0.688191f }, +{ -0.688191f, -0.587785f, -0.425325f } +}; diff --git a/code/MD3FileData.h b/code/MD3FileData.h new file mode 100644 index 000000000..038d382b4 --- /dev/null +++ b/code/MD3FileData.h @@ -0,0 +1,273 @@ +/** @file Defines the helper data structures for importing MD3 files */ +#ifndef AI_MD3FILEHELPER_H_INC +#define AI_MD3FILEHELPER_H_INC + +#include +#include +#include + +#include "../include/aiTypes.h" +#include "../include/aiMesh.h" +#include "../include/aiAnim.h" + +#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__) +# pragma pack(push,1) +# define PACK_STRUCT +#elif defined( __GNUC__ ) +# define PACK_STRUCT __attribute__((packed)) +#else +# error Compiler not supported +#endif + + +namespace Assimp +{ +// http://linux.ucla.edu/~phaethon/q3/formats/md3format.html +namespace MD3 +{ + +#define AI_MD3_MAGIC_NUMBER_BE 'IDP3' +#define AI_MD3_MAGIC_NUMBER_LE '3PDI' + +// common limitations +#define AI_MD3_VERSION 15 +#define AI_MD3_MAXQPATH 64 +#define AI_MD3_MAX_FRAMES 1024 +#define AI_MD3_MAX_TAGS 16 +#define AI_MD3_MAX_SURFACES 32 +#define AI_MD3_MAX_SHADERS 256 +#define AI_MD3_MAX_VERTS 4096 +#define AI_MD3_MAX_TRIANGLES 8192 + +// master scale factor for all vertices in a MD3 model +#define AI_MD3_XYZ_SCALE (1.0f/64.0f) + +// --------------------------------------------------------------------------- +/** \brief Data structure for the MD3 main header + */ +// --------------------------------------------------------------------------- +struct Header +{ + // magic number + int32_t IDENT; + + // file format version + int32_t VERSION; + + // original name in .pak archive + unsigned char NAME[ AI_MD3_MAXQPATH ]; + + // unknown + int32_t FLAGS; + + // number of frames in the file + int32_t NUM_FRAMES; + + // number of tags in the file + int32_t NUM_TAGS; + + // number of surfaces in the file + int32_t NUM_SURFACES; + + // number of skins in the file + int32_t NUM_SKINS; + + // offset of the first frame + int32_t OFS_FRAMES; + + // offset of the first tag + int32_t OFS_TAGS; + + // offset of the first surface + int32_t OFS_SURFACES; + + // end of file + int32_t OFS_EOF; +} PACK_STRUCT; + + +// --------------------------------------------------------------------------- +/** \brief Data structure for the frame header + */ +// --------------------------------------------------------------------------- +struct Frame +{ + // no need to define this, we won't need +} PACK_STRUCT; + + +// --------------------------------------------------------------------------- +/** \brief Data structure for the tag header + */ +// --------------------------------------------------------------------------- +struct Tag +{ + // no need to define this, we won't need +} PACK_STRUCT; + + +// --------------------------------------------------------------------------- +/** \brief Data structure for the surface header + */ +// --------------------------------------------------------------------------- +struct Surface +{ + // magic number + int32_t IDENT; + + // original name of the surface + unsigned char NAME[ AI_MD3_MAXQPATH ]; + + // unknown + int32_t FLAGS; + + // number of frames in the surface + int32_t NUM_FRAMES; + + // number of shaders in the surface + int32_t NUM_SHADER; + + // number of vertices in the surface + int32_t NUM_VERTICES; + + // number of triangles in the surface + int32_t NUM_TRIANGLES; + + + // offset to the triangle data + int32_t OFS_TRIANGLES; + + // offset to the shader data + int32_t OFS_SHADERS; + + // offset to the texture coordinate data + int32_t OFS_ST; + + // offset to the vertex/normal data + int32_t OFS_XYZNORMAL; + + // offset to the end of the Surface object + int32_t OFS_END; +} PACK_STRUCT; + +// --------------------------------------------------------------------------- +/** \brief Data structure for a shader + */ +// --------------------------------------------------------------------------- +struct Shader +{ + // filename of the shader + unsigned char NAME[ AI_MD3_MAXQPATH ]; + + // index of the shader + int32_t SHADER_INDEX; +} PACK_STRUCT; + + +// --------------------------------------------------------------------------- +/** \brief Data structure for a triangle + */ +// --------------------------------------------------------------------------- +struct Triangle +{ + // triangle indices + int32_t INDEXES[3]; +} PACK_STRUCT; + + +// --------------------------------------------------------------------------- +/** \brief Data structure for an UV coord + */ +// --------------------------------------------------------------------------- +struct TexCoord +{ + // UV coordinates + float U,V; +} PACK_STRUCT; + + +// --------------------------------------------------------------------------- +/** \brief Data structure for a vertex + */ +// --------------------------------------------------------------------------- +struct Vertex +{ + // X/Y/Z coordinates + int16_t X,Y,Z; + + // encoded normal vector + int16_t NORMAL; +} PACK_STRUCT; + +// reset packing to the original value +#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__) +# pragma pack( pop ) +#endif +#undef PACK_STRUCT + +// --------------------------------------------------------------------------- +/** \brief Unpack a Q3 16 bit vector to its full float3 representation + * + * \param p_iNormal Input normal vector in latitude/longitude form + * \param p_afOut Pointer to an array of three floats to receive the result + * + * \note This has been taken from q3 source (misc_model.c) + */ +// --------------------------------------------------------------------------- +inline void LatLngNormalToVec3(uint16_t p_iNormal, float* p_afOut) +{ + float lat = (float)(( p_iNormal >> 8 ) & 0xff); + float lng = (float)(( p_iNormal & 0xff )); + lat *= 3.141926f/128.0f; + lng *= 3.141926f/128.0f; + + p_afOut[0] = cosf(lat) * sinf(lng); + p_afOut[1] = sinf(lat) * sinf(lng); + p_afOut[2] = cosf(lng); + return; +} + + +// --------------------------------------------------------------------------- +/** \brief Pack a Q3 normal into 16bit latitute/longitude representation + * \param p_vIn Input vector + * \param p_iOut Output normal + * + * \note This has been taken from q3 source (mathlib.c) + */ +// --------------------------------------------------------------------------- +inline void Vec3NormalToLatLng( const aiVector3D& p_vIn, uint16_t& p_iOut ) +{ + // check for singularities + if ( 0.0f == p_vIn[0] && 0.0f == p_vIn[1] ) + { + if ( p_vIn[2] > 0.0f ) + { + ((unsigned char*)&p_iOut)[0] = 0; + ((unsigned char*)&p_iOut)[1] = 0; // lat = 0, long = 0 + } + else + { + ((unsigned char*)&p_iOut)[0] = 128; + ((unsigned char*)&p_iOut)[1] = 0; // lat = 0, long = 128 + } + } + else + { + int a, b; + + a = int(57.2957795f * ( atan2f( p_vIn[1], p_vIn[0] ) ) * (255.0f / 360.0f )); + a &= 0xff; + + b = int(57.2957795f * ( acosf( p_vIn[2] ) ) * ( 255.0f / 360.0f )); + b &= 0xff; + + ((unsigned char*)&p_iOut)[0] = b; // longitude + ((unsigned char*)&p_iOut)[1] = a; // lattitude + } +} + +}; +}; + +#endif // !! AI_MD3FILEHELPER_H_INC diff --git a/code/MD3Loader.cpp b/code/MD3Loader.cpp new file mode 100644 index 000000000..d8553bd2f --- /dev/null +++ b/code/MD3Loader.cpp @@ -0,0 +1,328 @@ +/** @file Implementation of the MD3 importer class */ +#include "MD3Loader.h" +#include "MaterialSystem.h" + +#include "../include/IOStream.h" +#include "../include/IOSystem.h" +#include "../include/aiMesh.h" +#include "../include/aiScene.h" +#include "../include/aiAssert.h" + +#include + +using namespace Assimp; + + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +MD3Importer::MD3Importer() +{ +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +MD3Importer::~MD3Importer() +{ +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the class can handle the format of the given file. +bool MD3Importer::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); + + // not brilliant but working ;-) + if( extension == ".md3" || extension == ".MD3" || + extension == ".mD3" || extension == ".Md3") + return true; + + return false; +} +// ------------------------------------------------------------------------------------------------ +// Imports the given file into the given scene structure. +void MD3Importer::InternReadFile( + const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) +{ + boost::scoped_ptr file( pIOHandler->Open( pFile)); + + // Check whether we can read from the file + if( file.get() == NULL) + { + throw new ImportErrorException( "Failed to open md3 file " + pFile + "."); + } + + // check whether the md3 file is large enough to contain + // at least the file header + size_t fileSize = file->FileSize(); + if( fileSize < sizeof(MD3::Header)) + { + throw new ImportErrorException( ".md3 File is too small."); + } + + // allocate storage and copy the contents of the file to a memory buffer + this->mBuffer = new unsigned char[fileSize]; + file->Read( (void*)mBuffer, 1, fileSize); + + this->m_pcHeader = (const MD3::Header*)this->mBuffer; + + // check magic number + if (this->m_pcHeader->IDENT != AI_MD3_MAGIC_NUMBER_BE && + this->m_pcHeader->IDENT != AI_MD3_MAGIC_NUMBER_LE) + { + throw new ImportErrorException( "Invalid md3 file: Magic bytes not found"); + } + + // check file format version + if (this->m_pcHeader->VERSION > 15) + { + throw new ImportErrorException( "Unsupported md3 file version"); + } + + // check some values whether they are valid + if (0 == this->m_pcHeader->NUM_FRAMES) + { + throw new ImportErrorException( "Invalid md3 file: NUM_FRAMES is 0"); + } + if (0 == this->m_pcHeader->NUM_SURFACES) + { + throw new ImportErrorException( "Invalid md3 file: NUM_SURFACES is 0"); + } + if (this->m_pcHeader->OFS_EOF > (int32_t)fileSize) + { + throw new ImportErrorException( "Invalid md3 file: File is too small"); + } + + // now navigate to the list of surfaces + const MD3::Surface* pcSurfaces = (const MD3::Surface*) + (this->mBuffer + this->m_pcHeader->OFS_SURFACES); + + // allocate output storage + pScene->mNumMeshes = this->m_pcHeader->NUM_SURFACES; + pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; + + pScene->mNumMaterials = this->m_pcHeader->NUM_SURFACES; + pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes]; + + unsigned int iNum = this->m_pcHeader->NUM_SURFACES; + unsigned int iNumMaterials = 0; + unsigned int iDefaultMatIndex = 0xFFFFFFFF; + while (iNum-- > 0) + { + // navigate to the vertex list of the surface + const MD3::Vertex* pcVertices = (const MD3::Vertex*) + (((unsigned char*)pcSurfaces) + pcSurfaces->OFS_XYZNORMAL); + + // navigate to the triangle list of the surface + const MD3::Triangle* pcTriangles = (const MD3::Triangle*) + (((unsigned char*)pcSurfaces) + pcSurfaces->OFS_TRIANGLES); + + // navigate to the texture coordinate list of the surface + const MD3::TexCoord* pcUVs = (const MD3::TexCoord*) + (((unsigned char*)pcSurfaces) + pcSurfaces->OFS_ST); + + // navigate to the shader list of the surface + const MD3::Shader* pcShaders = (const MD3::Shader*) + (((unsigned char*)pcSurfaces) + pcSurfaces->OFS_SHADERS); + + + // if the submesh is empty ignore it + if (0 == pcSurfaces->NUM_VERTICES || 0 == pcSurfaces->NUM_TRIANGLES) + { + pcSurfaces = (const MD3::Surface*)(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_END); + pScene->mNumMeshes--; + continue; + } + + // allocate the output mesh + pScene->mMeshes[iNum] = new aiMesh(); + aiMesh* pcMesh = pScene->mMeshes[iNum]; + + pcMesh->mNumVertices = pcSurfaces->NUM_VERTICES; + pcMesh->mNumBones = 0; + pcMesh->mColors[0] = pcMesh->mColors[1] = pcMesh->mColors[2] = pcMesh->mColors[3] = NULL; + pcMesh->mNumFaces = pcSurfaces->NUM_TRIANGLES; + pcMesh->mFaces = new aiFace[pcSurfaces->NUM_TRIANGLES]; + pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices]; + pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices]; + pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices]; + pcMesh->mTextureCoords[1] = pcMesh->mTextureCoords[2] = pcMesh->mTextureCoords[3] = NULL; + pcMesh->mNumUVComponents[0] = 2; + + // fill in all vertices and normals + // fill in all texture coordinates + for (unsigned int i = 0; i < (unsigned int)pcSurfaces->NUM_VERTICES;++i) + { + pcMesh->mVertices[i].x = pcVertices->X; + pcMesh->mVertices[i].y = pcVertices->Y; + pcMesh->mVertices[i].z = -1.0f*pcVertices->Z; + + // convert the normal vector to uncompressed float3 format + LatLngNormalToVec3(pcVertices->NORMAL,(float*)&pcMesh->mNormals[i]); + + // read texture coordinates + pcMesh->mTextureCoords[0][i].x = pcUVs->U; + pcMesh->mTextureCoords[0][i].y = 1.0f - pcUVs->V; + + pcVertices++; + pcUVs++; + } + + // fill in all triangles + for (unsigned int i = 0; i < (unsigned int)pcSurfaces->NUM_TRIANGLES;++i) + { + pcMesh->mFaces[i].mIndices = new unsigned int[3]; + pcMesh->mFaces[i].mNumIndices = 3; + + pcMesh->mFaces[i].mIndices[0] = pcTriangles->INDEXES[0]; + pcMesh->mFaces[i].mIndices[1] = pcTriangles->INDEXES[1]; + pcMesh->mFaces[i].mIndices[2] = pcTriangles->INDEXES[2]; + + pcTriangles++; + } + + // get the first shader (= texture?) assigned to the surface + if (0 != pcSurfaces->NUM_SHADER) + { + // make a relative path. + // if the MD3's internal path itself and the given path are using + // the same directory remove it + const char* szEndDir1 = strrchr((const char*)this->m_pcHeader->NAME,'\\'); + if (!szEndDir1)szEndDir1 = strrchr((const char*)this->m_pcHeader->NAME,'/'); + + const char* szEndDir2 = strrchr((const char*)pcShaders->NAME,'\\'); + if (!szEndDir2)szEndDir2 = strrchr((const char*)pcShaders->NAME,'/'); + + if (szEndDir1 && szEndDir2) + { + // both of them are valid + const unsigned int iLen1 = (unsigned int)(szEndDir1 - (const char*)this->m_pcHeader->NAME); + const unsigned int iLen2 = std::min (iLen1, (unsigned int)(szEndDir2 - (const char*)pcShaders->NAME) ); + + bool bSuccess = true; + for (unsigned int a = 0; a < iLen2;++a) + { + char sz = tolower ( pcShaders->NAME[a] ); + char sz2 = tolower ( this->m_pcHeader->NAME[a] ); + if (sz != sz2) + { + bSuccess = false; + break; + } + } + if (bSuccess) + { + // use the file name only + szEndDir2++; + } + else + { + // use the full path + szEndDir2 = (const char*)pcShaders->NAME; + } + } + + // now try to find out whether we have this shader already + bool bHave = false; + for (unsigned int p = 0; p < iNumMaterials;++p) + { + if (iDefaultMatIndex == p)continue; + + aiString szOut; + if(AI_SUCCESS == aiGetMaterialString ( (aiMaterial*)pScene->mMaterials[p], + AI_MATKEY_TEXBLEND_DIFFUSE(0),&szOut)) + { + if (0 == ASSIMP_stricmp(szOut.data,szEndDir2)) + { + // equal. reuse this material (texture) + bHave = true; + pcMesh->mMaterialIndex = p; + break; + } + } + } + + if (!bHave) + { + MaterialHelper* pcHelper = new MaterialHelper(); + + aiString szString; + const size_t iLen = strlen(szEndDir2); + memcpy(szString.data,szEndDir2,iLen+1); + szString.length = iLen-1; + + pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0)); + + int iMode = (int)aiShadingMode_Gouraud; + pcHelper->AddProperty(&iMode, 1, AI_MATKEY_SHADING_MODEL); + + aiColor3D clr; + clr.b = clr.g = clr.r = 1.0f; + pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_DIFFUSE); + pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_SPECULAR); + + clr.b = clr.g = clr.r = 0.05f; + pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_AMBIENT); + + pScene->mMaterials[iNumMaterials] = (aiMaterial*)pcHelper; + pcMesh->mMaterialIndex = iNumMaterials++; + } + } + else + { + if (0xFFFFFFFF != iDefaultMatIndex) + { + pcMesh->mMaterialIndex = iDefaultMatIndex; + } + else + { + MaterialHelper* pcHelper = new MaterialHelper(); + + // fill in a default material + int iMode = (int)aiShadingMode_Gouraud; + pcHelper->AddProperty(&iMode, 1, AI_MATKEY_SHADING_MODEL); + + aiColor3D clr; + clr.b = clr.g = clr.r = 0.6f; + pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_DIFFUSE); + pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_SPECULAR); + + clr.b = clr.g = clr.r = 0.05f; + pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_AMBIENT); + + pScene->mMaterials[iNumMaterials] = (aiMaterial*)pcHelper; + pcMesh->mMaterialIndex = iNumMaterials++; + } + } + pcSurfaces = (const MD3::Surface*)(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_END); + } + + if (0 == pScene->mNumMeshes) + { + // cleanup before returning + delete pScene; + throw new ImportErrorException( "Invalid md3 file: File contains no valid mesh"); + } + pScene->mNumMaterials = iNumMaterials; + + // now we need to generate an empty node graph + pScene->mRootNode = new aiNode(); + pScene->mRootNode->mNumChildren = pScene->mNumMeshes; + pScene->mRootNode->mChildren = new aiNode*[pScene->mNumMeshes]; + + for (unsigned int i = 0; i < pScene->mNumMeshes;++i) + { + pScene->mRootNode->mChildren[i] = new aiNode(); + pScene->mRootNode->mChildren[i]->mParent = pScene->mRootNode; + pScene->mRootNode->mChildren[i]->mNumMeshes = 1; + pScene->mRootNode->mChildren[i]->mMeshes = new unsigned int[1]; + pScene->mRootNode->mChildren[i]->mMeshes[0] = i; + } + + delete[] this->mBuffer; + return; +} \ No newline at end of file diff --git a/code/MD3Loader.h b/code/MD3Loader.h new file mode 100644 index 000000000..2b669bae7 --- /dev/null +++ b/code/MD3Loader.h @@ -0,0 +1,60 @@ +/** @file Definition of the .MD3 importer class. */ +#ifndef AI_MD3LOADER_H_INCLUDED +#define AI_MD3LOADER_H_INCLUDED + +#include + +#include "BaseImporter.h" + +#include "../include/aiTypes.h" + +struct aiNode; + +#include "MD3FileData.h" +namespace Assimp +{ + class MaterialHelper; + + using namespace MD3; + + // --------------------------------------------------------------------------- + /** Used to load MD3 files + */ + class MD3Importer : public BaseImporter + { + friend class Importer; + + protected: + /** Constructor to be privately used by Importer */ + MD3Importer(); + + /** Destructor, private as well */ + ~MD3Importer(); + + public: + + // ------------------------------------------------------------------- + /** Returns whether the class can handle the format of the given file. + * See BaseImporter::CanRead() for details. */ + bool CanRead( const std::string& pFile, IOSystem* pIOHandler) const; + + protected: + + // ------------------------------------------------------------------- + /** Imports the given file into the given scene structure. + * See BaseImporter::InternReadFile() for details + */ + void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); + + protected: + + /** Header of the MD3 file */ + const MD3::Header* m_pcHeader; + + /** Buffer to hold the loaded file */ + const unsigned char* mBuffer; + }; + +} // end of namespace Assimp + +#endif // AI_3DSIMPORTER_H_INC \ No newline at end of file diff --git a/code/MD4FileData.h b/code/MD4FileData.h new file mode 100644 index 000000000..e69de29bb diff --git a/code/MD4Loader.h b/code/MD4Loader.h new file mode 100644 index 000000000..5ae65b62d --- /dev/null +++ b/code/MD4Loader.h @@ -0,0 +1,57 @@ +/** @file Definition of the .MD4 importer class. */ +#ifndef AI_MD4LOADER_H_INCLUDED +#define AI_MD4LOADER_H_INCLUDED + +#include "BaseImporter.h" +#include "../include/aiTypes.h" + +struct aiNode; +#include "MD4FileData.h" + +namespace Assimp +{ + class MaterialHelper; + + using namespace MD4; + + // --------------------------------------------------------------------------- + /** Used to load MD4 files + */ + class MD4Importer : public BaseImporter + { + friend class Importer; + + protected: + /** Constructor to be privately used by Importer */ + MD4Importer(); + + /** Destructor, private as well */ + ~MD4Importer(); + + public: + + // ------------------------------------------------------------------- + /** Returns whether the class can handle the format of the given file. + * See BaseImporter::CanRead() for details. */ + bool CanRead( const std::string& pFile, IOSystem* pIOHandler) const; + + protected: + + // ------------------------------------------------------------------- + /** Imports the given file into the given scene structure. + * See BaseImporter::InternReadFile() for details + */ + void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); + + protected: + + /** Header of the MD4 file */ + const MD4::Header* m_pcHeader; + + /** Buffer to hold the loaded file */ + const unsigned char* mBuffer; + }; + +} // end of namespace Assimp + +#endif // AI_3DSIMPORTER_H_INC \ No newline at end of file diff --git a/code/MD5Loader.h b/code/MD5Loader.h new file mode 100644 index 000000000..cfa8b9f5b --- /dev/null +++ b/code/MD5Loader.h @@ -0,0 +1,57 @@ +/** @file Definition of the .MD5 importer class. */ +#ifndef AI_MD5LOADER_H_INCLUDED +#define AI_MD5LOADER_H_INCLUDED + +#include "BaseImporter.h" +#include "../include/aiTypes.h" + +struct aiNode; +#include "MD5FileData.h" + +namespace Assimp +{ + class MaterialHelper; + + using namespace MD5; + + // --------------------------------------------------------------------------- + /** Used to load MD5 files + */ + class MD5Importer : public BaseImporter + { + friend class Importer; + + protected: + /** Constructor to be privately used by Importer */ + MD5Importer(); + + /** Destructor, private as well */ + ~MD5Importer(); + + public: + + // ------------------------------------------------------------------- + /** Returns whether the class can handle the format of the given file. + * See BaseImporter::CanRead() for details. */ + bool CanRead( const std::string& pFile, IOSystem* pIOHandler) const; + + protected: + + // ------------------------------------------------------------------- + /** Imports the given file into the given scene structure. + * See BaseImporter::InternReadFile() for details + */ + void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); + + protected: + + /** Header of the MD5 file */ + const MD5::Header* m_pcHeader; + + /** Buffer to hold the loaded file */ + const unsigned char* mBuffer; + }; + +} // end of namespace Assimp + +#endif // AI_3DSIMPORTER_H_INC \ No newline at end of file diff --git a/code/MaterialSystem.cpp b/code/MaterialSystem.cpp new file mode 100644 index 000000000..751dc0053 --- /dev/null +++ b/code/MaterialSystem.cpp @@ -0,0 +1,285 @@ + + +#include "assimp.h" +#include "aiMaterial.h" +#include "assimp.hpp" +#include "MaterialSystem.h" + + +#include "../include/aiAssert.h" + +using namespace Assimp; + + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +aiReturn aiGetMaterialProperty(const aiMaterial* pMat, + const char* pKey, + const aiMaterialProperty** pPropOut) +{ +#if (defined DEBUG) + + ai_assert (pMat != NULL); + ai_assert (pKey != NULL); + ai_assert (pPropOut != NULL); + +#endif // ASSIMP_DEBUG + + for (unsigned int i = 0; i < pMat->mNumProperties;++i) + { + if (NULL != pMat->mProperties[i]) + { + if (0 == ASSIMP_stricmp( pMat->mProperties[i]->mKey->data, pKey )) + { + *pPropOut = pMat->mProperties[i]; + return AI_SUCCESS; + } + } + } + *pPropOut = NULL; + return AI_FAILURE; +} + + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +aiReturn aiGetMaterialFloatArray(const aiMaterial* pMat, + const char* pKey, + float* pOut, + unsigned int* pMax) +{ +#if (defined DEBUG) + + ai_assert (pMat != NULL); + ai_assert (pKey != NULL); + ai_assert (pOut != NULL); + +#endif // ASSIMP_DEBUG + + for (unsigned int i = 0; i < pMat->mNumProperties;++i) + { + if (NULL != pMat->mProperties[i]) + { + if (0 == ASSIMP_stricmp( pMat->mProperties[i]->mKey->data, pKey )) + { + // data is given in floats, simply copy it + if( aiPTI_Float == pMat->mProperties[i]->mType || + aiPTI_Buffer == pMat->mProperties[i]->mType) + { + unsigned int iWrite = pMat->mProperties[i]-> + mDataLength / sizeof(float); + + if (NULL != pMax) + iWrite = *pMax < iWrite ? *pMax : iWrite; + + memcpy (pOut, pMat->mProperties[i]->mData, iWrite * sizeof (float)); + + if (NULL != pMax) + *pMax = iWrite; + } + // data is given in ints, convert to float + else if( aiPTI_Integer == pMat->mProperties[i]->mType) + { + unsigned int iWrite = pMat->mProperties[i]-> + mDataLength / sizeof(int); + + if (NULL != pMax) + iWrite = *pMax < iWrite ? *pMax : iWrite; + + for (unsigned int a = 0; a < iWrite;++a) + { + pOut[a] = (float) ((int*)pMat->mProperties[i]->mData)[a]; + } + if (NULL != pMax) + *pMax = iWrite; + } + // it is a string ... no way to read something out of this + else + { + if (NULL != pMax) + *pMax = 0; + return AI_FAILURE; + } + return AI_SUCCESS; + } + } + } + return AI_FAILURE; +} + + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +aiReturn aiGetMaterialIntegerArray(const aiMaterial* pMat, + const char* pKey, + int* pOut, + unsigned int* pMax) +{ +#if (defined DEBUG) + + ai_assert (pMat != NULL); + ai_assert (pKey != NULL); + ai_assert (pOut != NULL); + +#endif // ASSIMP_DEBUG + + for (unsigned int i = 0; i < pMat->mNumProperties;++i) + { + if (NULL != pMat->mProperties[i]) + { + if (0 == ASSIMP_stricmp( pMat->mProperties[i]->mKey->data, pKey )) + { + // data is given in ints, simply copy it + if( aiPTI_Integer == pMat->mProperties[i]->mType || + aiPTI_Buffer == pMat->mProperties[i]->mType) + { + unsigned int iWrite = pMat->mProperties[i]-> + mDataLength / sizeof(int); + + if (NULL != pMax) + iWrite = *pMax < iWrite ? *pMax : iWrite; + + memcpy (pOut, pMat->mProperties[i]->mData, iWrite * sizeof (int)); + + if (NULL != pMax) + *pMax = iWrite; + } + // data is given in floats convert to int (lossy!) + else if( aiPTI_Float == pMat->mProperties[i]->mType) + { + unsigned int iWrite = pMat->mProperties[i]-> + mDataLength / sizeof(float); + + if (NULL != pMax) + iWrite = *pMax < iWrite ? *pMax : iWrite; + + for (unsigned int a = 0; a < iWrite;++a) + { + pOut[a] = (int) ((float*)pMat->mProperties[i]->mData)[a]; + } + if (NULL != pMax) + *pMax = iWrite; + } + // it is a string ... no way to read something out of this + else + { + if (NULL != pMax) + *pMax = 0; + return AI_FAILURE; + } + return AI_SUCCESS; + } + } + } + return AI_FAILURE; +} + + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +aiReturn aiGetMaterialColor(const aiMaterial* pMat, + const char* pKey, + aiColor4D* pOut) +{ + unsigned int iMax = 4; + aiReturn eRet = aiGetMaterialFloatArray(pMat,pKey,(float*)pOut,&iMax); + + // if no alpha channel is provided set it to 1.0 by default + if (3 == iMax)pOut->a = 1.0f; + return eRet; +} + + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +aiReturn aiGetMaterialString(const aiMaterial* pMat, + const char* pKey, + aiString* pOut) +{ +#if (defined DEBUG) + + ai_assert (pMat != NULL); + ai_assert (pKey != NULL); + ai_assert (pOut != NULL); + +#endif // ASSIMP_DEBUG + + for (unsigned int i = 0; i < pMat->mNumProperties;++i) + { + if (NULL != pMat->mProperties[i]) + { + if (0 == ASSIMP_stricmp( pMat->mProperties[i]->mKey->data, pKey )) + { + if( aiPTI_String == pMat->mProperties[i]->mType) + { + memcpy (pOut, pMat->mProperties[i]->mData, + sizeof(aiString)); + } + // wrong type + else return AI_FAILURE; + return AI_SUCCESS; + } + } + } + return AI_FAILURE; +} + + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +aiReturn MaterialHelper::AddBinaryProperty (const void* pInput, + const unsigned int pSizeInBytes, + const char* pKey, + aiPropertyTypeInfo pType) +{ +#if (defined DEBUG) + + ai_assert (pInput != NULL); + ai_assert (pKey != NULL); + ai_assert (0 != pSizeInBytes); + +#endif // ASSIMP_DEBUG + + aiMaterialProperty* pcNew = new aiMaterialProperty(); + + // fill this + pcNew->mKey = new aiString(); + pcNew->mType = pType; + + pcNew->mDataLength = pSizeInBytes; + pcNew->mData = new char[pSizeInBytes]; + memcpy (pcNew->mData,pInput,pSizeInBytes); + + pcNew->mKey->length = strlen(pKey); + ai_assert ( MAXLEN > pcNew->mKey->length); + strcpy( pcNew->mKey->data, pKey ); + + // resize the array ... allocate + // storage for 5 other properties + if (this->mNumProperties == this->mNumAllocated) + { + unsigned int iOld = this->mNumAllocated; + this->mNumAllocated += 5; + + aiMaterialProperty** ppTemp = new aiMaterialProperty*[this->mNumAllocated]; + if (NULL == ppTemp)return AI_OUTOFMEMORY; + + memcpy (ppTemp,this->mProperties,iOld * sizeof(void*)); + + delete[] this->mProperties; + this->mProperties = ppTemp; + } + // push back ... + this->mProperties[this->mNumProperties++] = pcNew; + return AI_SUCCESS; +} + + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +aiReturn MaterialHelper::AddProperty (const aiString* pInput, + const char* pKey) +{ + return this->AddBinaryProperty(pInput, + sizeof(aiString),pKey,aiPTI_String); +} diff --git a/code/MaterialSystem.h b/code/MaterialSystem.h new file mode 100644 index 000000000..8afe0c1ad --- /dev/null +++ b/code/MaterialSystem.h @@ -0,0 +1,187 @@ +/** @file Definition of the base class for all importer worker classes. */ +#ifndef AI_MATERIALSYSTEM_H_INC +#define AI_MATERIALSYSTEM_H_INC + +#include "../include/aiMaterial.h" + +namespace Assimp +{ + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +inline int ASSIMP_stricmp(const char *s1, const char *s2) +{ + const char *a1, *a2; + a1 = s1; + a2 = s2; + + while (true) + { + char c1 = (char)tolower(*a1); + char c2 = (char)tolower(*a2); + if ((0 == c1) && (0 == c2)) return 0; + if (c1 < c2) return-1; + if (c1 > c2) return 1; + ++a1; + ++a2; + } +} + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +inline int ASSIMP_strincmp(const char *s1, const char *s2, unsigned int n) +{ + const char *a1, *a2; + a1 = s1; + a2 = s2; + + unsigned int p = 0; + + while (true) + { + if (p >= n)return 0; + + char c1 = (char)tolower(*a1); + char c2 = (char)tolower(*a2); + if ((0 == c1) && (0 == c2)) return 0; + if (c1 < c2) return-1; + if (c1 > c2) return 1; + ++a1; + ++a2; + ++p; + } +} + + +// --------------------------------------------------------------------------- +/** Internal material helper class. Can be used to fill an aiMaterial + structure easily. */ +class MaterialHelper : public ::aiMaterial +{ + public: + + inline MaterialHelper(); + inline ~MaterialHelper(); + + // ------------------------------------------------------------------- + /** Add a property with a given key and type info to the material + structure */ + aiReturn AddBinaryProperty (const void* pInput, + const unsigned int pSizeInBytes, + const char* pKey, + aiPropertyTypeInfo pType); + + + // ------------------------------------------------------------------- + /** Add a string property with a given key and type info to the + material structure */ + aiReturn AddProperty (const aiString* pInput, + const char* pKey); + + + // ------------------------------------------------------------------- + /** Add a property with a given key to the material structure */ + template + aiReturn AddProperty (const TYPE* pInput, + const unsigned int pNumValues, + const char* pKey); +}; + + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +inline MaterialHelper::MaterialHelper() + { + // allocate 5 entries by default + this->mNumProperties = 0; + this->mNumAllocated = 5; + this->mProperties = new aiMaterialProperty*[5]; + return; + } + + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +inline MaterialHelper::~MaterialHelper() + { + for (unsigned int i = 0; i < this->mNumProperties;++i) + { + // be careful ... + if(NULL != this->mProperties[i]) + { + delete[] this->mProperties[i]->mKey; + delete[] this->mProperties[i]->mData; + delete this->mProperties[i]; + } + } + return; + } + + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +template +aiReturn MaterialHelper::AddProperty (const TYPE* pInput, + const unsigned int pNumValues, + const char* pKey) +{ + return this->AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(TYPE), + pKey,aiPTI_Buffer); +} + + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +template<> +inline aiReturn MaterialHelper::AddProperty (const float* pInput, + const unsigned int pNumValues, + const char* pKey) +{ + return this->AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(float), + pKey,aiPTI_Float); +} + + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +template<> +inline aiReturn MaterialHelper::AddProperty (const aiColor4D* pInput, + const unsigned int pNumValues, + const char* pKey) +{ + return this->AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(aiColor4D), + pKey,aiPTI_Float); +} + + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +template<> +inline aiReturn MaterialHelper::AddProperty (const aiColor3D* pInput, + const unsigned int pNumValues, + const char* pKey) +{ + return this->AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(aiColor3D), + pKey,aiPTI_Float); +} + + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +template<> +inline aiReturn MaterialHelper::AddProperty (const int* pInput, + const unsigned int pNumValues, + const char* pKey) +{ + return this->AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(int), + pKey,aiPTI_Integer); +} +} + + +#endif //!! AI_MATERIALSYSTEM_H_INC \ No newline at end of file diff --git a/code/ObjFileData.h b/code/ObjFileData.h new file mode 100644 index 000000000..8361c7b1a --- /dev/null +++ b/code/ObjFileData.h @@ -0,0 +1,170 @@ +#ifndef OBJ_FILEDATA_H_INC +#define OBJ_FILEDATA_H_INC + +#include +#include +#include "aiTypes.h" + +namespace Assimp +{ + +namespace ObjFile +{ + +struct Object; +struct Face; +struct Material; + +// ------------------------------------------------------------------------------------------------ +//! \struct Face +//! \brief Datastructure for a simple obj-face, descripes discredisation and materials +struct Face +{ + typedef std::vector IndexArray; + + //! Primitive type + int m_PrimitiveType; + //! Vertex indices + IndexArray *m_pVertices; + //! Normal indices + IndexArray *m_pNormals; + //! Texture coordinates indices + IndexArray *m_pTexturCoords; + //! Pointer to assigned material + Material *m_pMaterial; + + //! \brief Default constructor + //! \param pVertices Pointer to assigned vertex indexbuffer + //! \param pNormals Pointer to assigned normals indexbuffer + //! \param pTexCoords Pointer to assigned texture indexbuffer + Face(std::vector *pVertices, + std::vector *pNormals, + std::vector *pTexCoords) : + m_PrimitiveType(2), + m_pVertices(pVertices), + m_pNormals(pNormals), + m_pTexturCoords(pTexCoords), + m_pMaterial(0L) + { + // empty + } + + //! \brief Destructor + ~Face() + { + // empty + } +}; + +// ------------------------------------------------------------------------------------------------ +//! \struct Object +//! \brief Stores all objects of an objfile object definition +struct Object +{ + //! Obejct name + std::string m_strObjName; + //! Assigend face instances + std::vector m_Faces; + //! Transformation matrix, stored in OpenGL format + aiMatrix4x4 m_Transformation; + //! All subobjects references by this object + std::vector m_SubObjects; + + //! \brief Default constructor + Object() : + m_strObjName("") + { + // empty + } + + //! \brief Destructor + ~Object() + { + for (std::vector::iterator it = m_SubObjects.begin(); + it != m_SubObjects.end(); ++it) + { + delete *it; + } + m_SubObjects.clear(); + } +}; + +// ------------------------------------------------------------------------------------------------ +//! \struct Material +//! \brief Data structure to store all material specific data +struct Material +{ + aiString MaterialName; + aiString texture; + aiColor3D ambient; + aiColor3D diffuse; + aiColor3D specular; + float alpha; + float shineness; + int illumination_model; +}; + +// ------------------------------------------------------------------------------------------------ +//! \struct Model +//! \brief Data structure to store all obj-specific model datas +struct Model +{ + typedef std::map* > GroupMap; + typedef std::map* >::iterator GroupMapIt; + typedef std::map* >::const_iterator ConstGroupMapIt; + + //! Model name + std::string m_ModelName; + //! List ob assigned objects + std::vector m_Objects; + //! Pointer to current object + ObjFile::Object *m_pCurrent; + //! Pointer to current material + ObjFile::Material *m_pCurrentMaterial; + //! Pointer to default material + ObjFile::Material *m_pDefaultMaterial; + //! Vector with all generated materials + std::vector m_MaterialLib; + //! Vector with all generated group + std::vector m_GroupLib; + //! Vector with all generated vertices + std::vector m_Vertices; + //! vector with all generated normals + std::vector m_Normals; + //! Groupmap + GroupMap m_Groups; + std::vector *m_pGroupFaceIDs; + //! Active group + std::string m_strActiveGroup; + //! Vector with generated texture coordinates + std::vector m_TextureCoord; + //! Material map + std::map m_MaterialMap; + + //! \brief Default constructor + Model() : + m_ModelName(""), + m_pCurrent(NULL), + m_pCurrentMaterial(NULL), + m_pDefaultMaterial(NULL), + m_strActiveGroup("") + { + // empty + } + + //! \brief DEstructor + ~Model() + { + for (std::vector::iterator it = m_Objects.begin(); + it != m_Objects.end(); ++it) + { + delete *it; + } + m_Objects.clear(); + } +}; + +} // Namespace ObjFile +} // Namespace Assimp + +#endif diff --git a/code/ObjFileImporter.cpp b/code/ObjFileImporter.cpp new file mode 100644 index 000000000..4dc2cafa2 --- /dev/null +++ b/code/ObjFileImporter.cpp @@ -0,0 +1,354 @@ +#include "ObjFileImporter.h" +#include "ObjFileParser.h" +#include "ObjFileData.h" +#include "../include/IOStream.h" +#include "../include/IOSystem.h" +#include "../include/aiMesh.h" +#include "../include/aiScene.h" +#include "aiAssert.h" +#include "MaterialSystem.h" + +#include +#include + +namespace Assimp +{ +// ------------------------------------------------------------------------------------------------ + +using namespace std; + +//! Obj-file-format extention +const string ObjFileImporter::OBJ_EXT = "obj"; + +// ------------------------------------------------------------------------------------------------ +// Default constructor +ObjFileImporter::ObjFileImporter() : + m_pRootObject(NULL), + m_strAbsPath("\\") +{ + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Destructor +ObjFileImporter::~ObjFileImporter() +{ + // Release root object instance + if (NULL != m_pRootObject) + { + delete m_pRootObject; + m_pRootObject = NULL; + } +} + +// ------------------------------------------------------------------------------------------------ +// Returns true, fi file is an obj file +bool ObjFileImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const +{ + if (pFile.empty()) + return false; + + string::size_type pos = pFile.find_last_of("."); + if (string::npos == pos) + return false; + + const string ext = pFile.substr(pos+1, pFile.size() - pos - 1); + if (ext == OBJ_EXT) + return true; + + return false; +} + +// ------------------------------------------------------------------------------------------------ +// Obj-file import implementation +void ObjFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) +{ + // Read file into memory + const std::string mode = "rb"; + boost::scoped_ptr file( pIOHandler->Open( pFile, mode)); + if (NULL == file.get()) + throw new ImportErrorException( "Failed to open file " + pFile + "."); + + // Get the filesize and vaslidate it, throwing an exception when failes + size_t fileSize = file->FileSize(); + if( fileSize < 16) + throw new ImportErrorException( "OBJ-file is too small."); + + // Allocate buffer and read file into it + m_Buffer.resize( fileSize ); + const size_t readsize = file->Read(&m_Buffer.front(), sizeof(char), fileSize); + assert (readsize == fileSize); + + // + std::string strDirectory("\\"), strModelName; + std::string::size_type pos = pFile.find_last_of("\\"); + if (pos != std::string::npos) + { + strDirectory = pFile.substr(0, pos); + strModelName = pFile.substr(pos+1, pFile.size() - pos - 1); + } + else + { + strModelName = pFile; + } + + // parse the file into a temporary representation + ObjFileParser parser(m_Buffer, strDirectory, strModelName); + + // And create the proper return structures out of it + CreateDataFromImport(parser.GetModel(), pScene); +} + +// ------------------------------------------------------------------------------------------------ +// Create the data from parsed obj-file +void ObjFileImporter::CreateDataFromImport(const ObjFile::Model* pModel, aiScene* pScene) +{ + if (0L == pModel) + return; + + // Create the root node of the scene + pScene->mRootNode = new aiNode(); + if (!pModel->m_ModelName.empty()) + { + // Set the name of the scene + pScene->mRootNode->mName.Set(pModel->m_ModelName); + } + else + { + // This is an error, so break down the application + assert (false); + } + + // Create nodes for the whole scene + std::vector MeshArray; + for (size_t index = 0; index < pModel->m_Objects.size(); index++) + { + createNodes(pModel, pModel->m_Objects[ index ], pScene->mRootNode, pScene, MeshArray); + } + + // Create mesh pointer buffer for this scene + if (pScene->mNumMeshes > 0) + { + pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes ]; + for (size_t index =0; index < pScene->mNumMeshes; index++) + { + pScene->mMeshes [ index ] = MeshArray[ index ]; + } + } + + // Create all materials + for (size_t index = 0; index < pModel->m_Objects.size(); index++) + { + createMaterial(pModel, pModel->m_Objects[ index ], pScene); + } +} + +// ------------------------------------------------------------------------------------------------ +// Creates all nodes of the model +aiNode *ObjFileImporter::createNodes(const ObjFile::Model* pModel, const ObjFile::Object* pData, + aiNode *pParent, aiScene* pScene, + std::vector &MeshArray) +{ + if (NULL == pData) + return NULL; + + // Store older mesh size to be able to computate mesh offsets for new mesh instances + size_t oldMeshSize = MeshArray.size(); + aiNode *pNode = new aiNode(); + + if (pParent != NULL) + this->appendChildToParentNode(pParent, pNode); + + aiMesh *pMesh = new aiMesh(); + MeshArray.push_back(pMesh); + createTopology(pModel, pData, pMesh); + + // Create all nodes from the subobjects stored in the current object + if (!pData->m_SubObjects.empty()) + { + pNode->mNumChildren = pData->m_SubObjects.size(); + pNode->mChildren = new aiNode*[pData->m_SubObjects.size()]; + pNode->mNumMeshes = 1; + pNode->mMeshes = new unsigned int[1]; + + // Loop over all child objects + for (size_t index = 0; index < pData->m_SubObjects.size(); index++) + { + // Create all child nodes + pNode->mChildren[index] = createNodes(pModel, pData, pNode, pScene, MeshArray); + + // Create meshes of this object + pMesh = new aiMesh(); + MeshArray.push_back(pMesh); + createTopology(pModel, pData->m_SubObjects[ index ], pMesh); + + // Create material of this object + createMaterial(pModel, pData->m_SubObjects[ index ], pScene); + } + } + + // Set mesh instances into scene- and node-instances + const size_t meshSizeDiff = MeshArray.size()- oldMeshSize; + if (meshSizeDiff > 0 ) + { + pNode->mMeshes = new unsigned int[ meshSizeDiff ]; + pNode->mNumMeshes++; + size_t index = 0; + for (size_t i = oldMeshSize; i < MeshArray.size(); i++) + { + pNode->mMeshes[ index ] = pScene->mNumMeshes; + pScene->mNumMeshes++; + index++; + } + } + + return pNode; +} + +// ------------------------------------------------------------------------------------------------ +// Create topology data +void ObjFileImporter::createTopology(const ObjFile::Model* pModel, const ObjFile::Object* pData, + aiMesh* pMesh) +{ + if (NULL == pData) + return; + + // Create mesh vertices + createVertexArray(pModel, pData, pMesh); + + // Create faces + pMesh->mNumFaces = pData->m_Faces.size(); + pMesh->mFaces = new aiFace[pMesh->mNumFaces]; + for (size_t index = 0; index < pMesh->mNumFaces; index++) + { + aiFace *pFace = &pMesh->mFaces[ index ]; + pFace->mNumIndices = pData->m_Faces[index]->m_pVertices->size(); + if (pFace->mNumIndices > 0) + { + pFace->mIndices = new unsigned int[pMesh->mFaces[index].mNumIndices]; + ObjFile::Face::IndexArray *pArray = pData->m_Faces[index]->m_pVertices; + + // TODO: Should be implement much better + //memcpy(pFace->mIndices, &pData->m_Faces[index]->m_pVertices[0], pFace->mNumIndices * sizeof(unsigned int)); + if (pArray != NULL) + { + for (size_t a=0; amNumIndices; a++) + { + pFace->mIndices[a] = pArray->at( a ); + } + } + else + { + ai_assert (false); + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +void ObjFileImporter::createVertexArray(const ObjFile::Model* pModel, + const ObjFile::Object* pCurrentObject, + aiMesh* pMesh) +{ + // Break, if no faces are stored in object + if (pCurrentObject->m_Faces.empty()) + return; + + // Copy all stored vertices, normals and so on + pMesh->mNumVertices = pModel->m_Vertices.size(); + pMesh->mVertices = new aiVector3D_t[pMesh->mNumVertices]; + for (size_t index=0; index < pModel->m_Vertices.size(); index++) + { + pMesh->mVertices[ index ] = *pModel->m_Vertices[ index ]; + } + + if (!pModel->m_Normals.empty()) + { + pMesh->mNormals = new aiVector3D_t[pModel->m_Normals.size()]; + for (size_t index = 0; index < pModel->m_Normals.size(); index++) + { + pMesh->mNormals[ index ] = *pModel->m_Normals[ index ]; + } + } + + if (!pModel->m_TextureCoord.empty()) + { + // TODO: Implement texture coodinates + } +} + +// ------------------------------------------------------------------------------------------------ +void ObjFileImporter::countObjects(const std::vector &rObjects, int &iNumMeshes) +{ + iNumMeshes = 0; + if (rObjects.empty()) + return; + + iNumMeshes += rObjects.size(); + for (std::vector::const_iterator it = rObjects.begin(); + it != rObjects.end(); + ++it) + { + if (!(*it)->m_SubObjects.empty()) + { + countObjects((*it)->m_SubObjects, iNumMeshes); + } + } +} + +// ------------------------------------------------------------------------------------------------ +void ObjFileImporter::createMaterial(const ObjFile::Model* pModel, const ObjFile::Object* pData, + aiScene* pScene) +{ + ai_assert (NULL != pScene); + if (NULL == pData) + return; + + // Create only a dumy material to enshure a running viewer + pScene->mNumMaterials = 1; + Assimp::MaterialHelper* mat = new Assimp::MaterialHelper; + + // Create a new material + pScene->mMaterials = new aiMaterial*[1]; + pScene->mMaterials[0] = mat; +} + +// ------------------------------------------------------------------------------------------------ +// Appends this node to the parent node +void ObjFileImporter::appendChildToParentNode(aiNode *pParent, aiNode *pChild) +{ + // Checking preconditions + ai_assert (NULL != pParent); + ai_assert (NULL != pChild); + + // Assign parent to child + pChild->mParent = pParent; + size_t sNumChildren = 0; + + // If already children was assigned to the parent node, store them in a + std::vector temp; + if (pParent->mChildren != NULL) + { + sNumChildren = pParent->mNumChildren; + ai_assert (0 != sNumChildren); + for (size_t index = 0; index < pParent->mNumChildren; index++) + { + temp.push_back(pParent->mChildren [ index ] ); + } + delete [] pParent->mChildren; + } + + // Copy node instances into parent node + pParent->mNumChildren++; + pParent->mChildren = new aiNode*[ pParent->mNumChildren ]; + for (size_t index = 0; index < pParent->mNumChildren-1; index++) + { + pParent->mChildren[ index ] = temp [ index ]; + } + pParent->mChildren[ pParent->mNumChildren-1 ] = pChild; + +} + +// ------------------------------------------------------------------------------------------------ + +} // Namespace Assimp diff --git a/code/ObjFileImporter.h b/code/ObjFileImporter.h new file mode 100644 index 000000000..5fd7dbb82 --- /dev/null +++ b/code/ObjFileImporter.h @@ -0,0 +1,78 @@ +#ifndef OBJ_FILE_IMPORTER_H_INC +#define OBJ_FILE_IMPORTER_H_INC + +#include "BaseImporter.h" +#include + +struct aiMesh; +struct aiNode; + +namespace Assimp +{ + +namespace ObjFile +{ +struct Object; +struct Model; +} + +/// \class ObjFileImporter +/// \brief IMports a waveform obj file +class ObjFileImporter : + BaseImporter +{ + friend class Importer; + + //! OB file extention + static const std::string OBJ_EXT; + +protected: + /// \brief Default constructor + ObjFileImporter(); + + /// \brief Destructor + ~ObjFileImporter(); + +public: + /// \brief Returns whether the class can handle the format of the given file. + /// \remark See BaseImporter::CanRead() for details. + bool CanRead( const std::string& pFile, IOSystem* pIOHandler) const; + +private: + //! \brief + void InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); + + //! \brief + void CreateDataFromImport(const ObjFile::Model* pModel, aiScene* pScene); + + //! \brief + aiNode *createNodes(const ObjFile::Model* pModel, const ObjFile::Object* pData, + aiNode *pParent, aiScene* pScene, std::vector &MeshArray); + + //! \brief + void createTopology(const ObjFile::Model* pModel, const ObjFile::Object* pData, + aiMesh* pMesh); + + //! \brief + void createVertexArray(const ObjFile::Model* pModel, + const ObjFile::Object* pCurrentObject, aiMesh* pMesh); + + //! \brief + void countObjects(const std::vector &rObjects, int &iNumMeshes); + + //! \brief + void createMaterial(const ObjFile::Model* pModel, const ObjFile::Object* pData, + aiScene* pScene); + + //! \brief + void appendChildToParentNode(aiNode *pParent, aiNode *pChild); + +private: + std::vector m_Buffer; + ObjFile::Object *m_pRootObject; + std::string m_strAbsPath; +}; + +} // Namespace Assimp + +#endif diff --git a/code/ObjFileMtlImporter.cpp b/code/ObjFileMtlImporter.cpp new file mode 100644 index 000000000..a335cf33f --- /dev/null +++ b/code/ObjFileMtlImporter.cpp @@ -0,0 +1,56 @@ +#include "ObjFileMtlImporter.h" + +namespace Assimp +{ + +// ------------------------------------------------------------------- +ObjFileMtlImporter::ObjFileMtlImporter() +{ + // TODO: Inplement this +} + +// ------------------------------------------------------------------- +ObjFileMtlImporter::~ObjFileMtlImporter() +{ + // empty +} + +// ------------------------------------------------------------------- +ObjFileMtlImporter::ObjFileMtlImporter(const ObjFileMtlImporter &rOther) +{ + // empty +} + +// ------------------------------------------------------------------- +ObjFileMtlImporter &ObjFileMtlImporter::operator = (const ObjFileMtlImporter &rOther) +{ + return *this; +} +// ------------------------------------------------------------------- +void ObjFileMtlImporter::getColorRGBA() +{ +} + +// ------------------------------------------------------------------- +void ObjFileMtlImporter::getIlluminationModel() +{ +} + +// ------------------------------------------------------------------- +void ObjFileMtlImporter::getFloatValue() +{ +} + +// ------------------------------------------------------------------- +void ObjFileMtlImporter::createMaterial() +{ +} + +// ------------------------------------------------------------------- +void ObjFileMtlImporter::getTexture() +{ +} + +// ------------------------------------------------------------------- + +} // Namespace Assimp diff --git a/code/ObjFileMtlImporter.h b/code/ObjFileMtlImporter.h new file mode 100644 index 000000000..7db3707fc --- /dev/null +++ b/code/ObjFileMtlImporter.h @@ -0,0 +1,36 @@ +#ifndef OBJFILEMTLIMPORTER_H_INC +#define OBJFILEMTLIMPORTER_H_INC + +namespace Assimp +{ + +/** + * @class ObjFileMtlImporter + * @brief Loads the material description from a mtl file. + */ +class ObjFileMtlImporter +{ +public: + //! \brief Default constructor + ObjFileMtlImporter(); + + //! \brief DEstructor + ~ObjFileMtlImporter(); + +private: + //! \brief Copy constructor, empty. + ObjFileMtlImporter(const ObjFileMtlImporter &rOther); + + //! \brief Assignment operator, returns only a reference of this instance. + ObjFileMtlImporter &operator = (const ObjFileMtlImporter &rOther); + + void getColorRGBA(); + void getIlluminationModel(); + void getFloatValue(); + void createMaterial(); + void getTexture(); +}; + +} // Namespace Assimp + +#endif diff --git a/code/ObjFileParser.cpp b/code/ObjFileParser.cpp new file mode 100644 index 000000000..e468416dc --- /dev/null +++ b/code/ObjFileParser.cpp @@ -0,0 +1,551 @@ +#include "ObjFileParser.h" +#include "ObjTools.h" +#include "ObjFileData.h" +#include "DefaultIOSystem.h" +#include "IOStream.h" +#include "aiTypes.h" +#include "aiVector3D.h" +#include "aiAssert.h" +#include "fast_atof.h" + +#include +#include +#include + +namespace Assimp +{ + +// ------------------------------------------------------------------- +ObjFileParser::ObjFileParser(std::vector &Data, const std::string &strAbsPath, const std::string &strModelName) : + m_strAbsPath(strAbsPath), + m_DataIt(Data.begin()), + m_DataItEnd(Data.end()), + m_pModel(NULL), + m_uiLine(0) +{ + // Create the model instance to store all the data + m_pModel = new ObjFile::Model(); + m_pModel->m_ModelName = strModelName; + + // Start parsing the file + parseFile(); +} + +// ------------------------------------------------------------------- +ObjFileParser::~ObjFileParser() +{ + // empty +} + +// ------------------------------------------------------------------- +ObjFile::Model *ObjFileParser::GetModel() const +{ + return m_pModel; +} + +// ------------------------------------------------------------------- +void ObjFileParser::parseFile() +{ + if (m_DataIt == m_DataItEnd) + return; + + while (m_DataIt != m_DataItEnd) + { + switch (*m_DataIt) + { + case 'v': // Parse a vertex texture coordinate + { + ++m_DataIt; + if (*m_DataIt == ' ') + { + // Read in vertex definition + getVector3(m_pModel->m_Vertices); + } + else if (*m_DataIt == 't') + { + // Read in texture coordinate (2D) + getVector2(m_pModel->m_TextureCoord); + } + else if (*m_DataIt == 'n') + { + // Read in normal vector definition + getVector3(m_pModel->m_Normals); + } + } + break; + + case 'f': // Parse a face + { + getFace(); + } + break; + + case '#': // Parse a comment + { + getComment(); + } + break; + + case 'u': // Parse a material desc. setter + { + getMaterialDesc(); + } + break; + + case 'm': // Parse a material library + { + getMaterialLib(); + } + break; + + case 'g': // Parse group name + { + getGroupName(); + } + break; + + case 's': // Parse group number + { + getGroupNumber(); + } + break; + + case 'o': // Parse object name + { + getObjectName(); + } + break; + + default: + { + skipLine(); + } + break; + } + } +} + +// ------------------------------------------------------------------- +// Copy the next word in a temporary buffer +void ObjFileParser::copyNextWord(char *pBuffer, size_t length) +{ + size_t index = 0; + m_DataIt = getNextWord(m_DataIt, m_DataItEnd); + while (!isSpace(*m_DataIt) && m_DataIt != m_DataItEnd) + { + pBuffer[index] = *m_DataIt; + index++; + if (index == length-1) + break; + ++m_DataIt; + } + pBuffer[index] = '\0'; +} + +// ------------------------------------------------------------------- +// Copy the next line into a temporary buffer +void ObjFileParser::copyNextLine(char *pBuffer, size_t length) +{ + size_t index = 0; + while (m_DataIt != m_DataItEnd) + { + if (*m_DataIt == '\n' || *m_DataIt == '\r') + break; + assert (index+1 <= length); + pBuffer[ index ] = *m_DataIt; + ++index; + ++m_DataIt; + } + pBuffer[ index ] = '\0'; +} + +// ------------------------------------------------------------------- +// Get values for a new 3D vector instance +void ObjFileParser::getVector3(std::vector &point3d_array) +{ + float x, y, z; + copyNextWord(m_buffer, BUFFERSIZE); + x = (float) fast_atof(m_buffer); + + copyNextWord(m_buffer, BUFFERSIZE); + y = (float) fast_atof(m_buffer); + + copyNextWord(m_buffer, BUFFERSIZE); + z = (float) fast_atof(m_buffer); + + point3d_array.push_back(new aiVector3D(x,y,z)); + skipLine(); +} + +// ------------------------------------------------------------------- +// Get values for a new 2D vector instance +void ObjFileParser::getVector2(std::vector &point2d_array) +{ + float x, y; + copyNextWord(m_buffer, BUFFERSIZE); + x = (float) fast_atof(m_buffer); + + copyNextWord(m_buffer, BUFFERSIZE); + y = (float) fast_atof(m_buffer); + + point2d_array.push_back(new aiVector2D(x, y)); + skipLine(); +} + +// ------------------------------------------------------------------- +// Skips a line +void ObjFileParser::skipLine() +{ + while (m_DataIt != m_DataItEnd && *m_DataIt != '\n') + ++m_DataIt; + if (m_DataIt != m_DataItEnd) + { + ++m_DataIt; + ++m_uiLine; + } +} + +// ------------------------------------------------------------------- +// Get values for a new face instance +void ObjFileParser::getFace() +{ + copyNextLine(m_buffer, BUFFERSIZE); + if (m_DataIt == m_DataItEnd) + return; + char *pPtr = m_buffer; + char *pEnd = &pPtr[BUFFERSIZE]; + pPtr = getNextToken(pPtr, pEnd); + if (pPtr == '\0') + return; + + std::vector *pIndices = new std::vector; + std::vector *pTexID = new std::vector; + std::vector *pNormalID = new std::vector; + bool vt = (!m_pModel->m_TextureCoord.empty()); + bool vn = (!m_pModel->m_Normals.empty()); + int iStep = 0, iPos = 0; + while (pPtr != pEnd) + { + iStep = 1; + if (*pPtr == '\0') + break; + + if (*pPtr=='\r') + break; + + if (*pPtr=='/' ) + { + if (iPos == 0) + { + //if there are no texturecoordinates in the obj file but normals + if (!vt && vn) + iPos = 1; + } + iPos++; + } + else if (isSpace(*pPtr)) + { + iPos = 0; + } + else + { + //OBJ USES 1 Base ARRAYS!!!! + const int iVal = atoi(pPtr); + int tmp = iVal; + while ((tmp = tmp / 10)!=0) + ++iStep; + + if (0 != iVal) + { + // Store parsed index + if (0 == iPos) + { + pIndices->push_back(iVal-1); + } + else if (1 == iPos) + { + pTexID->push_back(iVal-1); + } + else if (2 == iPos) + { + pNormalID->push_back(iVal-1); + } + else + { + reportErrorTokenInFace(); + } + } + } + for (int i=0; im_pCurrentMaterial) + face->m_pMaterial = m_pModel->m_pCurrentMaterial; + else + face->m_pMaterial = m_pModel->m_pDefaultMaterial; + + // Create a default object, if nothing there + if (NULL == m_pModel->m_pCurrent) + createObject("defaultobject"); + + // Store the new instance + m_pModel->m_pCurrent->m_Faces.push_back(face); + + // Skip the rest of the line + skipLine(); +} + +// ------------------------------------------------------------------- +// Get values for a new material description +void ObjFileParser::getMaterialDesc() +{ + // Get next data for material data + m_DataIt = getNextToken(m_DataIt, m_DataItEnd); + if (m_DataIt == m_DataItEnd) + return; + + char *pStart = &(*m_DataIt); + while (!isSpace(*m_DataIt) && m_DataIt != m_DataItEnd) + m_DataIt++; + + // Get name + std::string strName(pStart, &(*m_DataIt)); + if (strName.empty()) + return; + + // Search for material + std::string strFile; + std::map::iterator it = m_pModel->m_MaterialMap.find(strName); + if (it == m_pModel->m_MaterialMap.end()) + { + m_pModel->m_pCurrentMaterial = new ObjFile::Material(); + m_pModel->m_MaterialMap[strName] = m_pModel->m_pCurrentMaterial; + } + + skipLine(); +} + +// ------------------------------------------------------------------- +// Get a comment, values will be skipped +void ObjFileParser::getComment() +{ + while (true) + { + if ('\n' == (*m_DataIt) || m_DataIt == m_DataItEnd) + { + ++m_DataIt; + break; + } + else + { + ++m_DataIt; + } + } +} + +// ------------------------------------------------------------------- +// +void ObjFileParser::getMaterialLib() +{ + // Translate tuple + m_DataIt = getNextToken(m_DataIt, m_DataItEnd); + if (m_DataIt == m_DataItEnd) + return; + + char *pStart = &(*m_DataIt); + while (!isSpace(*m_DataIt)) + m_DataIt++; + + // Check for existence + DefaultIOSystem IOSystem; + std::string strMatName(pStart, &(*m_DataIt)); + std::string absName = m_strAbsPath + IOSystem.getOsSeparator() + strMatName; + if (!IOSystem.Exists(absName)) + { + skipLine(); + return; + } + + std::string strExt(""); + extractExtension(strMatName, strExt); + std::string mat = "mtl"; + + DefaultIOSystem FileSystem; + IOStream *pFile = FileSystem.Open(absName); + if (0L != pFile) + { + size_t size = pFile->FileSize(); + char *pBuffer = new char[size]; + size_t read_size = pFile->Read(pBuffer, sizeof(char), size); + FileSystem.Close(pFile); + + // TODO: Load mtl file + + delete [] pBuffer; + + } + + // Load material library (all materials will be created) + m_pModel->m_MaterialLib.push_back(strMatName); + + skipLine(); +} + +// ------------------------------------------------------------------- +// +void ObjFileParser::getNewMaterial() +{ + m_DataIt = getNextToken(m_DataIt, m_DataItEnd); + m_DataIt = getNextWord(m_DataIt, m_DataItEnd); + + char *pStart = &(*m_DataIt); + std::string strMat(pStart, *m_DataIt); + while (isSpace(*m_DataIt)) + m_DataIt++; + std::map::iterator it = m_pModel->m_MaterialMap.find(strMat); + if (it == m_pModel->m_MaterialMap.end()) + { + // Show a warning, if material was not found + std::string strWarn ("Unsupported material requested: "); + strWarn += strMat; + std::cerr << "Warning : " << strWarn << std::endl; + m_pModel->m_pCurrentMaterial = m_pModel->m_pDefaultMaterial; + } + else + { + // Set new material + m_pModel->m_pCurrentMaterial = (*it).second; + } + + skipLine(); +} + +// ------------------------------------------------------------------- +// +void ObjFileParser::getGroupName() +{ + // Get next word from data buffer + m_DataIt = getNextToken(m_DataIt, m_DataItEnd); + m_DataIt = getNextWord(m_DataIt, m_DataItEnd); + + // Store groupname in group library + char *pStart = &(*m_DataIt); + while (!isSpace(*m_DataIt)) + m_DataIt++; + std::string strGroupName(pStart, &(*m_DataIt)); + + // Change active group, if necessary + if (m_pModel->m_strActiveGroup != strGroupName) + { + // Search for already existing entry + ObjFile::Model::ConstGroupMapIt it = m_pModel->m_Groups.find(&strGroupName); + + // New group name, creating a new entry + ObjFile::Object *pObject = m_pModel->m_pCurrent; + if (it == m_pModel->m_Groups.end()) + { + std::vector *pFaceIDArray = new std::vector; + m_pModel->m_Groups[ &strGroupName ] = pFaceIDArray; + m_pModel->m_pGroupFaceIDs = (pFaceIDArray); + } + else + { + m_pModel->m_pGroupFaceIDs = (*it).second; + } + m_pModel->m_strActiveGroup = strGroupName; + } + skipLine(); +} + +// ------------------------------------------------------------------- +// Not supported +void ObjFileParser::getGroupNumber() +{ + // TODO: Implement this + + skipLine(); +} + +// ------------------------------------------------------------------- +// Stores values for a new object instance, name will be used to +// identify it. +void ObjFileParser::getObjectName() +{ + m_DataIt = getNextToken(m_DataIt, m_DataItEnd); + if (m_DataIt == m_DataItEnd) + return; + char *pStart = &(*m_DataIt); + while (!isSpace(*m_DataIt)) + m_DataIt++; + + std::string strObjectName(pStart, &(*m_DataIt)); + if (!strObjectName.empty()) + { + // Reset current object + m_pModel->m_pCurrent = NULL; + + // Search for actual object + for (std::vector::const_iterator it = m_pModel->m_Objects.begin(); + it != m_pModel->m_Objects.end(); + ++it) + { + if ((*it)->m_strObjName == strObjectName) + { + m_pModel->m_pCurrent = *it; + break; + } + } + + // Allocate a new object, if current one wasn´t found before + if (m_pModel->m_pCurrent == NULL) + { + createObject(strObjectName); + /*m_pModel->m_pCurrent = new ObjFile::Object(); + m_pModel->m_pCurrent->m_strObjName = strObjectName; + m_pModel->m_Objects.push_back(m_pModel->m_pCurrent);*/ + } + } + skipLine(); +} +// ------------------------------------------------------------------- +// Creates a new object instance +void ObjFileParser::createObject(const std::string &strObjectName) +{ + ai_assert (NULL != m_pModel); + ai_assert (!strObjectName.empty()); + + m_pModel->m_pCurrent = new ObjFile::Object(); + m_pModel->m_pCurrent->m_strObjName = strObjectName; + m_pModel->m_Objects.push_back(m_pModel->m_pCurrent); +} + +// ------------------------------------------------------------------- +// Shows an error in parsing process. +void ObjFileParser::reportErrorTokenInFace() +{ + std::string strErr(""); + skipLine(); + std::cerr << "Not supported token in face desc. detected : " << strErr << std::endl; +} + +// ------------------------------------------------------------------- +// Extracts the extention from a filename +void ObjFileParser::extractExtension(const std::string strFile, + std::string &strExt) +{ + strExt = ""; + if (strFile.empty()) + return; + + std::string::size_type pos = strFile.find_last_of("."); + if (pos == std::string::npos) + return; + strExt = strFile.substr(pos, strFile.size() - pos); +} +// ------------------------------------------------------------------- + +} // Namespace Assimp diff --git a/code/ObjTools.h b/code/ObjTools.h new file mode 100644 index 000000000..5869542b1 --- /dev/null +++ b/code/ObjTools.h @@ -0,0 +1,56 @@ +/** @file ObjTools.h + * @brief Some helpful templates for text parsing + */ +#ifndef OBJ_TOOLS_H_INC +#define OBJ_TOOLS_H_INC + +namespace Assimp +{ + +/** @brief Returns true, if token is a space on any supported platform +* @param token Token to search in +* @return true, if token is a space +*/ +inline bool isSpace(char token) +{ + return (token == ' ' || token == '\n' || token == '\f' || token == '\r' || + token == '\t'); +} + +/** @brief Returns next word separated by a space + * @param pBuffer Pointer to data buffer + * @param pEnd Pointer to end of buffer + * @return Pointer to next space + */ +template +inline Char_T getNextWord(Char_T pBuffer, Char_T pEnd) +{ + while (pBuffer != pEnd) + { + if (!isSpace(*pBuffer)) + break; + pBuffer++; + } + return pBuffer; +} + +/** @brief Returns ponter a next token + * @param pBuffer Pointer to data buffer + * @param pEnd Pointer to end of buffer + * @return Pointer to next token +*/ +template +inline Char_T getNextToken(Char_T pBuffer, Char_T pEnd) +{ + while (pBuffer != pEnd) + { + if (isSpace(*pBuffer)) + break; + pBuffer++; + } + return getNextWord(pBuffer, pEnd); +} + +} // Namespace Assimp + +#endif diff --git a/code/PlyLoader.cpp b/code/PlyLoader.cpp new file mode 100644 index 000000000..c84cb840d --- /dev/null +++ b/code/PlyLoader.cpp @@ -0,0 +1,990 @@ +/** @file Implementation of the PLY importer class */ +#include "PLYLoader.h" +#include "MaterialSystem.h" + +#include "../include/IOStream.h" +#include "../include/IOSystem.h" +#include "../include/aiMesh.h" +#include "../include/aiScene.h" +#include "../include/aiAssert.h" + +#include + +using namespace Assimp; + + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +PLYImporter::PLYImporter() +{ +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +PLYImporter::~PLYImporter() +{ +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the class can handle the format of the given file. +bool PLYImporter::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); + + if (extension.length() < 4)return false; + if (extension[0] != '.')return false; + if (extension[1] != 'p' && extension[1] != 'P')return false; + if (extension[2] != 'l' && extension[2] != 'L')return false; + if (extension[3] != 'y' && extension[3] != 'Y')return false; + + return true; +} +// ------------------------------------------------------------------------------------------------ +// Imports the given file into the given scene structure. +void PLYImporter::InternReadFile( + const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) +{ + boost::scoped_ptr file( pIOHandler->Open( pFile)); + + // Check whether we can read from the file + if( file.get() == NULL) + { + throw new ImportErrorException( "Failed to open PLY file " + pFile + "."); + } + + // check whether the ply file is large enough to contain + // at least the file header + size_t fileSize = file->FileSize(); + if( fileSize < 10) + { + throw new ImportErrorException( ".ply File is too small."); + } + + // allocate storage and copy the contents of the file to a memory buffer + // (terminate it with zero) + this->mBuffer = new unsigned char[fileSize+1]; + file->Read( (void*)mBuffer, 1, fileSize); + this->mBuffer[fileSize] = '\0'; + + // the beginning of the file must be PLY + if (this->mBuffer[0] != 'P' && this->mBuffer[0] != 'p' || + this->mBuffer[1] != 'L' && this->mBuffer[1] != 'l' || + this->mBuffer[2] != 'Y' && this->mBuffer[2] != 'y') + { + throw new ImportErrorException( "Invalid .ply file: Magic number \'ply\' is no there"); + } + char* szMe = (char*)&this->mBuffer[3]; + SkipSpacesAndLineEnd(szMe,(const char**)&szMe); + + // determine the format of the file data + PLY::DOM sPlyDom; + if (0 == ASSIMP_strincmp(szMe,"format",6) && IsSpace(*(szMe+6))) + { + szMe += 7; + if (0 == ASSIMP_strincmp(szMe,"ascii",5) && IsSpace(*(szMe+5))) + { + szMe += 6; + SkipLine(szMe,(const char**)&szMe); + if(!PLY::DOM::ParseInstance(szMe,&sPlyDom)) + { + throw new ImportErrorException( "Invalid .ply file: Unable to build DOM (#1)"); + } + } + else if (0 == ASSIMP_strincmp(szMe,"binary_",7)) + { + bool bIsBE = false; + + // binary_little_endian + // binary_big_endian + szMe += 7; +#if (defined AI_BUILD_BIG_ENDIAN) + if ('l' == *szMe || 'L' == *szMe)bIsBE = true; +#else + if ('b' == *szMe || 'B' == *szMe)bIsBE = true; +#endif // ! AI_BUILD_BIG_ENDIAN + + // skip the line, parse the rest of the header and build the DOM + SkipLine(szMe,(const char**)&szMe); + if(!PLY::DOM::ParseInstanceBinary(szMe,&sPlyDom,bIsBE)) + { + throw new ImportErrorException( "Invalid .ply file: Unable to build DOM (#2)"); + } + } + else + { + throw new ImportErrorException( "Invalid .ply file: Unknown file format"); + } + } + else + { + throw new ImportErrorException( "Invalid .ply file: Missing format specification"); + } + this->pcDOM = &sPlyDom; + + // now load a list of vertices. This must be sucessfull in order to procede + std::vector avPositions; + this->LoadVertices(&avPositions,false); + + if (avPositions.empty()) + { + throw new ImportErrorException( "Invalid .ply file: No vertices found"); + } + + // now load a list of normals. + std::vector avNormals; + this->LoadVertices(&avNormals,true); + + // load the face list + std::vector avFaces; + this->LoadFaces(&avFaces); + + // if no face list is existing we assume that the vertex + // list is containing a list of triangles + if (avFaces.empty()) + { + if (avPositions.size() < 3) + { + throw new ImportErrorException( "Invalid .ply file: Not enough vertices to build " + "a face list. "); + } + + unsigned int iNum = avPositions.size() / 3; + for (unsigned int i = 0; i< iNum;++i) + { + PLY::Face sFace; + sFace.mIndices.push_back((iNum*3)); + sFace.mIndices.push_back((iNum*3)+1); + sFace.mIndices.push_back((iNum*3)+2); + avFaces.push_back(sFace); + } + } + + // now load a list of all materials + std::vector avMaterials; + this->LoadMaterial(&avMaterials); + + // now load a list of all vertex color channels + std::vector avColors; + this->LoadVertexColor(&avColors); + + // now replace the default material in all faces and validate all material indices + this->ReplaceDefaultMaterial(&avFaces,&avMaterials); + + // now convert this to a list of aiMesh instances + std::vector avMeshes; + this->ConvertMeshes(&avFaces,&avPositions,&avNormals, + &avColors,&avMaterials,&avMeshes); + + if (avMeshes.empty()) + { + throw new ImportErrorException( "Invalid .ply file: Unable to extract mesh data "); + } + + // now generate the output scene object. Fill the material list + pScene->mNumMaterials = avMaterials.size(); + pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials]; + for (unsigned int i = 0; i < pScene->mNumMaterials;++i) + pScene->mMaterials[i] = avMaterials[i]; + + // fill the mesh list + pScene->mNumMeshes = avMeshes.size(); + pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; + for (unsigned int i = 0; i < pScene->mNumMeshes;++i) + pScene->mMeshes[i] = avMeshes[i]; + + // generate a simple node structure + pScene->mRootNode = new aiNode(); + pScene->mRootNode->mNumMeshes = pScene->mNumMeshes; + pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes]; + + for (unsigned int i = 0; i < pScene->mRootNode->mNumMeshes;++i) + pScene->mRootNode->mMeshes[i] = i; + + // delete the file buffer + delete[] this->mBuffer; + + // DOM is lying on the stack, will be deconstructed automatically + return; +} +// ------------------------------------------------------------------------------------------------ +void PLYImporter::ConvertMeshes(std::vector* avFaces, + const std::vector* avPositions, + const std::vector* avNormals, + const std::vector* avColors, + const std::vector* avMaterials, + std::vector* avOut) +{ + ai_assert(NULL != avFaces); + ai_assert(NULL != avPositions); + ai_assert(NULL != avMaterials); + + // split by materials + std::vector* aiSplit = new std::vector[ + avMaterials->size()]; + + unsigned int iNum = 0; + for (std::vector::const_iterator + i = avFaces->begin(); + i != avFaces->end();++i,++iNum) + { + // index has already been checked + aiSplit[(*i).iMaterialIndex].push_back(iNum); + } + // now generate submeshes + for (unsigned int p = 0; p < avMaterials->size();++p) + { + if (aiSplit[p].size() != 0) + { + // allocate the mesh object + aiMesh* p_pcOut = new aiMesh(); + p_pcOut->mMaterialIndex = p; + + p_pcOut->mNumFaces = aiSplit[p].size(); + p_pcOut->mFaces = new aiFace[aiSplit[p].size()]; + + // at first we need to determine the size of the output vector array + unsigned int iNum = 0; + for (unsigned int i = 0; i < aiSplit[p].size();++i) + { + iNum += (*avFaces)[aiSplit[p][i]].mIndices.size(); + } + p_pcOut->mNumVertices = iNum; + p_pcOut->mVertices = new aiVector3D[iNum]; + + if (!avColors->empty()) + p_pcOut->mColors[0] = new aiColor4D[iNum]; + if (!avNormals->empty()) + p_pcOut->mNormals = new aiVector3D[iNum]; + + // add all faces + iNum = 0; + unsigned int iVertex = 0; + for (std::vector::const_iterator + i = aiSplit[p].begin(); + i != aiSplit[p].end();++i,++iNum) + { + p_pcOut->mFaces[iNum].mNumIndices = (*avFaces)[*i].mIndices.size(); + p_pcOut->mFaces[iNum].mIndices = new unsigned int[p_pcOut->mFaces[iNum].mNumIndices]; + + // build an unique set of vertices/colors for this face + // hardcode all combinations to speedup this piece of code + if (!avColors->empty()) + { + if (!avNormals->empty()) + { + for (unsigned int q = 0; q < p_pcOut->mFaces[iNum].mNumIndices;++q) + { + p_pcOut->mFaces[iNum].mIndices[q] = iVertex; + p_pcOut->mVertices[iVertex] = (*avPositions)[(*avFaces)[*i].mIndices[q]]; + p_pcOut->mColors[0][iVertex] = (*avColors)[(*avFaces)[*i].mIndices[q]]; + p_pcOut->mNormals[iVertex] = (*avNormals)[(*avFaces)[*i].mIndices[q]]; + iVertex++; + } + } + else + { + for (unsigned int q = 0; q < p_pcOut->mFaces[iNum].mNumIndices;++q) + { + p_pcOut->mFaces[iNum].mIndices[q] = iVertex; + p_pcOut->mVertices[iVertex] = (*avPositions)[(*avFaces)[*i].mIndices[q]]; + p_pcOut->mColors[0][iVertex] = (*avColors)[(*avFaces)[*i].mIndices[q]]; + iVertex++; + } + } + } + else + { + if (!avNormals->empty()) + { + for (unsigned int q = 0; q < p_pcOut->mFaces[iNum].mNumIndices;++q) + { + p_pcOut->mFaces[iNum].mIndices[q] = iVertex; + p_pcOut->mVertices[iVertex] = (*avPositions)[(*avFaces)[*i].mIndices[q]]; + p_pcOut->mNormals[iVertex] = (*avNormals)[(*avFaces)[*i].mIndices[q]]; + iVertex++; + } + } + else + { + for (unsigned int q = 0; q < p_pcOut->mFaces[iNum].mNumIndices;++q) + { + p_pcOut->mFaces[iNum].mIndices[q] = iVertex; + p_pcOut->mVertices[iVertex] = (*avPositions)[(*avFaces)[*i].mIndices[q]]; + iVertex++; + } + } + } + } + // add the mesh to the output list + avOut->push_back(p_pcOut); + } + } + delete[] aiSplit; + return; +} +// ------------------------------------------------------------------------------------------------ +void PLYImporter::ReplaceDefaultMaterial(std::vector* avFaces, + std::vector* avMaterials) +{ + bool bNeedDefaultMat = false; + + for (std::vector::iterator + i = avFaces->begin();i != avFaces->end();++i) + { + if (0xFFFFFFFF == (*i).iMaterialIndex) + { + bNeedDefaultMat = true; + (*i).iMaterialIndex = avMaterials->size(); + } + else if ((*i).iMaterialIndex >= avMaterials->size() ) + { + // clamp the index + (*i).iMaterialIndex = avMaterials->size()-1; + } + } + + if (bNeedDefaultMat) + { + // generate a default material + MaterialHelper* pcHelper = new MaterialHelper(); + + // fill in a default material + int iMode = (int)aiShadingMode_Gouraud; + pcHelper->AddProperty(&iMode, 1, AI_MATKEY_SHADING_MODEL); + + aiColor3D clr; + clr.b = clr.g = clr.r = 0.6f; + pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_DIFFUSE); + pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_SPECULAR); + + clr.b = clr.g = clr.r = 0.05f; + pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_AMBIENT); + + avMaterials->push_back(pcHelper); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void PLYImporter::LoadVertices(std::vector* pvOut, bool p_bNormals) +{ + unsigned int aiPositions[3] = {0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF}; + PLY::EDataType aiTypes[3]; + PLY::ElementInstanceList* pcList = NULL; + unsigned int cnt = 0; + + // serach in the DOM for a vertex entry + unsigned int _i = 0; + for (std::vector::const_iterator + i = this->pcDOM->alElements.begin(); + i != this->pcDOM->alElements.end();++i,++_i) + { + if (PLY::EEST_Vertex == (*i).eSemantic) + { + pcList = &this->pcDOM->alElementData[_i]; + + // load normal vectors? + if (p_bNormals) + { + // now check whether which normal components are available + unsigned int _a = 0; + for (std::vector::const_iterator + a = (*i).alProperties.begin(); + a != (*i).alProperties.end();++a,++_a) + { + if ((*a).bIsList)continue; + if (PLY::EST_XNormal == (*a).Semantic) + { + cnt++; + aiPositions[0] = _a; + aiTypes[0] = (*a).eType; + } + else if (PLY::EST_YNormal == (*a).Semantic) + { + cnt++; + aiPositions[1] = _a; + aiTypes[1] = (*a).eType; + } + else if (PLY::EST_ZNormal == (*a).Semantic) + { + cnt++; + aiPositions[2] = _a; + aiTypes[2] = (*a).eType; + } + if (3 == cnt)break; + } + } + // load vertex coordinates + else + { + // now check whether which coordinate sets are available + unsigned int _a = 0; + for (std::vector::const_iterator + a = (*i).alProperties.begin(); + a != (*i).alProperties.end();++a,++_a) + { + if ((*a).bIsList)continue; + if (PLY::EST_XCoord == (*a).Semantic) + { + cnt++; + aiPositions[0] = _a; + aiTypes[0] = (*a).eType; + } + else if (PLY::EST_YCoord == (*a).Semantic) + { + cnt++; + aiPositions[1] = _a; + aiTypes[1] = (*a).eType; + } + else if (PLY::EST_ZCoord == (*a).Semantic) + { + cnt++; + aiPositions[2] = _a; + aiTypes[2] = (*a).eType; + } + if (3 == cnt)break; + } + } + break; + } + } + // check whether we have a valid source for the vertex data + if (NULL != pcList && 0 != cnt) + { + pvOut->reserve(pcList->alInstances.size()); + for (std::vector::const_iterator + i = pcList->alInstances.begin(); + i != pcList->alInstances.end();++i) + { + // convert the vertices to sp floats + aiVector3D vOut; + + if (0xFFFFFFFF == aiPositions[0])vOut.x = 0.0f; + else + { + vOut.x = PLY::PropertyInstance::ConvertTo( + (*i).alProperties[aiPositions[0]].avList.front(),aiTypes[0]); + } + + if (0xFFFFFFFF == aiPositions[1])vOut.y = 0.0f; + else + { + vOut.y = PLY::PropertyInstance::ConvertTo( + (*i).alProperties[aiPositions[1]].avList.front(),aiTypes[1]); + } + + if (0xFFFFFFFF == aiPositions[2])vOut.z = 0.0f; + else + { + vOut.z = PLY::PropertyInstance::ConvertTo( + (*i).alProperties[aiPositions[2]].avList.front(),aiTypes[2]); + } + + // and add them to our nice list + pvOut->push_back(vOut); + } + } + return; +} +// ------------------------------------------------------------------------------------------------ +float NormalizeColorValue (PLY::PropertyInstance::ValueUnion val,PLY::EDataType eType) +{ + switch (eType) + { + case EDT_Float: + return val.fFloat; + case EDT_Double: + return (float)val.fDouble; + + case EDT_UChar: + return (float)val.iUInt / (float)0xFF; + case EDT_Char: + return (float)(val.iInt+(0xFF/2)) / (float)0xFF; + case EDT_UShort: + return (float)val.iUInt / (float)0xFFFF; + case EDT_Short: + return (float)(val.iInt+(0xFFFF/2)) / (float)0xFFFF; + case EDT_UInt: + return (float)val.iUInt / (float)0xFFFF; + case EDT_Int: + return ((float)val.iInt / (float)0xFF) + 0.5f; + }; + return 0.0f; +} +// ------------------------------------------------------------------------------------------------ +void PLYImporter::LoadVertexColor(std::vector* pvOut) +{ + unsigned int aiPositions[4] = {0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF}; + PLY::EDataType aiTypes[4]; + unsigned int cnt = 0; + PLY::ElementInstanceList* pcList = NULL; + + // serach in the DOM for a vertex entry + unsigned int _i = 0; + for (std::vector::const_iterator + i = this->pcDOM->alElements.begin(); + i != this->pcDOM->alElements.end();++i,++_i) + { + if (PLY::EEST_Vertex == (*i).eSemantic) + { + pcList = &this->pcDOM->alElementData[_i]; + + // now check whether which coordinate sets are available + unsigned int _a = 0; + for (std::vector::const_iterator + a = (*i).alProperties.begin(); + a != (*i).alProperties.end();++a,++_a) + { + if ((*a).bIsList)continue; + if (PLY::EST_Red == (*a).Semantic) + { + cnt++; + aiPositions[0] = _a; + aiTypes[0] = (*a).eType; + } + else if (PLY::EST_Green == (*a).Semantic) + { + cnt++; + aiPositions[1] = _a; + aiTypes[1] = (*a).eType; + } + else if (PLY::EST_Blue == (*a).Semantic) + { + cnt++; + aiPositions[2] = _a; + aiTypes[2] = (*a).eType; + } + else if (PLY::EST_Alpha == (*a).Semantic) + { + cnt++; + aiPositions[3] = _a; + aiTypes[3] = (*a).eType; + } + if (4 == cnt)break; + } + break; + } + } + // check whether we have a valid source for the vertex data + if (NULL != pcList && 0 != cnt) + { + pvOut->reserve(pcList->alInstances.size()); + for (std::vector::const_iterator + i = pcList->alInstances.begin(); + i != pcList->alInstances.end();++i) + { + // convert the vertices to sp floats + aiColor4D vOut; + + if (0xFFFFFFFF == aiPositions[0])vOut.r = 0.0f; + else + { + vOut.r = NormalizeColorValue((*i).alProperties[ + aiPositions[0]].avList.front(),aiTypes[0]); + } + + if (0xFFFFFFFF == aiPositions[1])vOut.g = 0.0f; + else + { + vOut.g = NormalizeColorValue((*i).alProperties[ + aiPositions[1]].avList.front(),aiTypes[1]); + } + + if (0xFFFFFFFF == aiPositions[2])vOut.b = 0.0f; + else + { + vOut.b = NormalizeColorValue((*i).alProperties[ + aiPositions[2]].avList.front(),aiTypes[2]); + } + + // assume 1.0 for the alpha channel ifit is not set + if (0xFFFFFFFF == aiPositions[3])vOut.a = 1.0f; + else + { + vOut.a = NormalizeColorValue((*i).alProperties[ + aiPositions[3]].avList.front(),aiTypes[3]); + } + + // and add them to our nice list + pvOut->push_back(vOut); + } + } + return; +} + +// ------------------------------------------------------------------------------------------------ +void PLYImporter::LoadFaces(std::vector* pvOut) +{ + PLY::ElementInstanceList* pcList = NULL; + bool bOne = false; + + // index of the vertex index list + unsigned int iProperty = 0xFFFFFFFF; + PLY::EDataType eType; + bool bIsTristrip = false; + + // index of the material index property + unsigned int iMaterialIndex = 0xFFFFFFFF; + PLY::EDataType eType2; + + // serach in the DOM for a face entry + unsigned int _i = 0; + for (std::vector::const_iterator + i = this->pcDOM->alElements.begin(); + i != this->pcDOM->alElements.end();++i,++_i) + { + // face = unique number of vertex indices + if (PLY::EEST_Face == (*i).eSemantic) + { + pcList = &this->pcDOM->alElementData[_i]; + unsigned int _a = 0; + for (std::vector::const_iterator + a = (*i).alProperties.begin(); + a != (*i).alProperties.end();++a,++_a) + { + if (PLY::EST_VertexIndex == (*a).Semantic) + { + // must be a dynamic list! + if (!(*a).bIsList)continue; + iProperty = _a; + bOne = true; + eType = (*a).eType; + } + else if (PLY::EST_MaterialIndex == (*a).Semantic) + { + if ((*a).bIsList)continue; + iMaterialIndex = _a; + bOne = true; + eType2 = (*a).eType; + } + } + break; + } + // triangle strip + // TODO: triangle strip and material index support??? + else if (PLY::EEST_TriStrip == (*i).eSemantic) + { + // find a list property in this ... + pcList = &this->pcDOM->alElementData[_i]; + unsigned int _a = 0; + for (std::vector::const_iterator + a = (*i).alProperties.begin(); + a != (*i).alProperties.end();++a,++_a) + { + // must be a dynamic list! + if (!(*a).bIsList)continue; + iProperty = _a; + bOne = true; + bIsTristrip = true; + eType = (*a).eType; + break; + } + break; + } + } + // check whether we have at least one per-face information set + if (pcList && bOne) + { + if (!bIsTristrip) + { + pvOut->reserve(pcList->alInstances.size()); + for (std::vector::const_iterator + i = pcList->alInstances.begin(); + i != pcList->alInstances.end();++i) + { + PLY::Face sFace; + + // parse the list of vertex indices + if (0xFFFFFFFF != iProperty) + { + const unsigned int iNum = (*i).alProperties[iProperty].avList.size(); + sFace.mIndices.resize(iNum); + + std::list::const_iterator p = + (*i).alProperties[iProperty].avList.begin(); + + for (unsigned int a = 0; a < iNum;++a,++p) + { + sFace.mIndices[a] = PLY::PropertyInstance::ConvertTo(*p,eType); + } + } + + // parse the material index + if (0xFFFFFFFF != iMaterialIndex) + { + sFace.iMaterialIndex = PLY::PropertyInstance::ConvertTo( + (*i).alProperties[iMaterialIndex].avList.front(),eType2); + } + pvOut->push_back(sFace); + } + } + else // triangle strips + { + // normally we have only one triangle strip instance where + // a value of -1 indicates a restart of the strip + for (std::vector::const_iterator + i = pcList->alInstances.begin(); + i != pcList->alInstances.end();++i) + { + int aiTable[2] = {-1,-1}; + for (std::list::const_iterator + a = (*i).alProperties[iProperty].avList.begin(); + a != (*i).alProperties[iProperty].avList.end();++a) + { + int p = PLY::PropertyInstance::ConvertTo(*a,eType); + if (-1 == p) + { + // restart the strip ... + aiTable[0] = aiTable[1] = -1; + continue; + } + if (-1 == aiTable[0]) + { + aiTable[0] = p; + continue; + } + if (-1 == aiTable[1]) + { + aiTable[1] = p; + continue; + } + + PLY::Face sFace; + sFace.mIndices.push_back((unsigned int)aiTable[0]); + sFace.mIndices.push_back((unsigned int)aiTable[1]); + sFace.mIndices.push_back((unsigned int)p); + pvOut->push_back(sFace); + + aiTable[0] = aiTable[1]; + aiTable[1] = p; + } + } + } + } + return; +} +// ------------------------------------------------------------------------------------------------ +void GetMaterialColor(const std::vector& avList, + unsigned int aiPositions[4], + PLY::EDataType aiTypes[4], + aiColor4D* clrOut) +{ + ai_assert(NULL != clrOut); + + if (0xFFFFFFFF == aiPositions[0])clrOut->r = 0.0f; + else + { + clrOut->r = NormalizeColorValue(avList[ + aiPositions[0]].avList.front(),aiTypes[0]); + } + + if (0xFFFFFFFF == aiPositions[1])clrOut->g = 0.0f; + else + { + clrOut->g = NormalizeColorValue(avList[ + aiPositions[1]].avList.front(),aiTypes[1]); + } + + if (0xFFFFFFFF == aiPositions[2])clrOut->b = 0.0f; + else + { + clrOut->b = NormalizeColorValue(avList[ + aiPositions[2]].avList.front(),aiTypes[2]); + } + + // assume 1.0 for the alpha channel ifit is not set + if (0xFFFFFFFF == aiPositions[3])clrOut->a = 1.0f; + else + { + clrOut->a = NormalizeColorValue(avList[ + aiPositions[3]].avList.front(),aiTypes[3]); + } + + return; +} +// ------------------------------------------------------------------------------------------------ +void PLYImporter::LoadMaterial(std::vector* pvOut) +{ + // diffuse[4], specular[4], ambient[4] + // rgba order + unsigned int aaiPositions[3][4] = { + + {0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF}, + {0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF}, + {0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF}, + }; + + // dto. + PLY::EDataType aaiTypes[3][4]; + PLY::ElementInstanceList* pcList = NULL; + + unsigned int iPhong = 0xFFFFFFFF; + PLY::EDataType ePhong; + + unsigned int iOpacity = 0xFFFFFFFF; + PLY::EDataType eOpacity; + + // serach in the DOM for a vertex entry + unsigned int _i = 0; + for (std::vector::const_iterator + i = this->pcDOM->alElements.begin(); + i != this->pcDOM->alElements.end();++i,++_i) + { + if (PLY::EEST_Material == (*i).eSemantic) + { + pcList = &this->pcDOM->alElementData[_i]; + + // now check whether which coordinate sets are available + unsigned int _a = 0; + for (std::vector::const_iterator + a = (*i).alProperties.begin(); + a != (*i).alProperties.end();++a,++_a) + { + if ((*a).bIsList)continue; + + // pohng specularity ----------------------------------- + if (PLY::EST_PhongPower == (*a).Semantic) + { + iPhong = _a; + ePhong = (*a).eType; + } + + // general opacity ----------------------------------- + if (PLY::EST_Opacity == (*a).Semantic) + { + iOpacity = _a; + eOpacity = (*a).eType; + } + + // diffuse color channels ----------------------------------- + if (PLY::EST_DiffuseRed == (*a).Semantic) + { + aaiPositions[0][0] = _a; + aaiTypes[0][0] = (*a).eType; + } + else if (PLY::EST_DiffuseGreen == (*a).Semantic) + { + aaiPositions[0][1] = _a; + aaiTypes[0][1] = (*a).eType; + } + else if (PLY::EST_DiffuseBlue == (*a).Semantic) + { + aaiPositions[0][2] = _a; + aaiTypes[0][2] = (*a).eType; + } + else if (PLY::EST_DiffuseAlpha == (*a).Semantic) + { + aaiPositions[0][3] = _a; + aaiTypes[0][3] = (*a).eType; + } + // specular color channels ----------------------------------- + else if (PLY::EST_SpecularRed == (*a).Semantic) + { + aaiPositions[1][0] = _a; + aaiTypes[1][0] = (*a).eType; + } + else if (PLY::EST_SpecularGreen == (*a).Semantic) + { + aaiPositions[1][1] = _a; + aaiTypes[1][1] = (*a).eType; + } + else if (PLY::EST_SpecularBlue == (*a).Semantic) + { + aaiPositions[1][2] = _a; + aaiTypes[1][2] = (*a).eType; + } + else if (PLY::EST_SpecularAlpha == (*a).Semantic) + { + aaiPositions[1][3] = _a; + aaiTypes[1][3] = (*a).eType; + } + // ambient color channels ----------------------------------- + else if (PLY::EST_AmbientRed == (*a).Semantic) + { + aaiPositions[2][0] = _a; + aaiTypes[2][0] = (*a).eType; + } + else if (PLY::EST_AmbientGreen == (*a).Semantic) + { + aaiPositions[2][1] = _a; + aaiTypes[2][1] = (*a).eType; + } + else if (PLY::EST_AmbientBlue == (*a).Semantic) + { + aaiPositions[22][2] = _a; + aaiTypes[2][2] = (*a).eType; + } + else if (PLY::EST_AmbientAlpha == (*a).Semantic) + { + aaiPositions[2][3] = _a; + aaiTypes[2][3] = (*a).eType; + } + } + break; + } + } + // check whether we have a valid source for the material data + if (NULL != pcList) + { + for (std::vector::const_iterator + i = pcList->alInstances.begin(); + i != pcList->alInstances.end();++i) + { + aiColor4D clrOut; + MaterialHelper* pcHelper = new MaterialHelper(); + + // build the diffuse material color + GetMaterialColor((*i).alProperties,aaiPositions[0],aaiTypes[0],&clrOut); + pcHelper->AddProperty(&clrOut,1,AI_MATKEY_COLOR_DIFFUSE); + + // build the specular material color + GetMaterialColor((*i).alProperties,aaiPositions[1],aaiTypes[1],&clrOut); + pcHelper->AddProperty(&clrOut,1,AI_MATKEY_COLOR_SPECULAR); + + // build the ambient material color + GetMaterialColor((*i).alProperties,aaiPositions[2],aaiTypes[2],&clrOut); + pcHelper->AddProperty(&clrOut,1,AI_MATKEY_COLOR_AMBIENT); + + // handle phong power and shading mode + int iMode; + if (0xFFFFFFFF != iPhong) + { + float fSpec = PLY::PropertyInstance::ConvertTo( + (*i).alProperties[iPhong].avList.front(),ePhong); + + // if shininess is 0 (and the pow() calculation would therefore always + // become 1, not depending on the angle) use gouraud lighting + if (0.0f != fSpec) + { + + // scale this with 15 ... hopefully this is correct + fSpec += 15; + + pcHelper->AddProperty(&fSpec, 1, AI_MATKEY_SHININESS); + + iMode = (int)aiShadingMode_Phong; + } + else iMode = (int)aiShadingMode_Gouraud; + } + else iMode = (int)aiShadingMode_Gouraud; + pcHelper->AddProperty(&iMode, 1, AI_MATKEY_SHADING_MODEL); + + // handle opacity + if (0xFFFFFFFF != iOpacity) + { + float fOpacity = PLY::PropertyInstance::ConvertTo( + (*i).alProperties[iPhong].avList.front(),eOpacity); + + pcHelper->AddProperty(&fOpacity, 1, AI_MATKEY_OPACITY); + } + + // add the newly created material instance to the list + pvOut->push_back(pcHelper); + } + } + return; +} \ No newline at end of file diff --git a/code/PlyLoader.h b/code/PlyLoader.h new file mode 100644 index 000000000..6c8915126 --- /dev/null +++ b/code/PlyLoader.h @@ -0,0 +1,99 @@ +/** @file Definition of the .ply importer class. */ +#ifndef AI_PLYLOADER_H_INCLUDED +#define AI_PLYLOADER_H_INCLUDED + +#include "BaseImporter.h" +#include "../include/aiTypes.h" + +struct aiNode; + +#include "PlyParser.h" + +namespace Assimp +{ + class MaterialHelper; + + + using namespace PLY; + + // --------------------------------------------------------------------------- + /** Used to load PLY files + */ + class PLYImporter : public BaseImporter + { + friend class Importer; + + protected: + /** Constructor to be privately used by Importer */ + PLYImporter(); + + /** Destructor, private as well */ + ~PLYImporter(); + + public: + + // ------------------------------------------------------------------- + /** Returns whether the class can handle the format of the given file. + * See BaseImporter::CanRead() for details. */ + bool CanRead( const std::string& pFile, IOSystem* pIOHandler) const; + + protected: + + // ------------------------------------------------------------------- + /** Imports the given file into the given scene structure. + * See BaseImporter::InternReadFile() for details + */ + void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); + + protected: + + + // ------------------------------------------------------------------- + /** Extract vertices from the DOM + */ + void LoadVertices(std::vector* pvOut,bool p_bNormals = false); + + // ------------------------------------------------------------------- + /** Extract vertex color channels + */ + void LoadVertexColor(std::vector* pvOut); + + // ------------------------------------------------------------------- + /** Extract a face list from the DOM + */ + void LoadFaces(std::vector* pvOut); + + // ------------------------------------------------------------------- + /** Extract a material list from the DOM + */ + void LoadMaterial(std::vector* pvOut); + + + // ------------------------------------------------------------------- + /** Validate material indices, replace default material identifiers + */ + void ReplaceDefaultMaterial(std::vector* avFaces, + std::vector* avMaterials); + + + // ------------------------------------------------------------------- + /** Convert all meshes into our ourer representation + */ + void ConvertMeshes(std::vector* avFaces, + const std::vector* avPositions, + const std::vector* avNormals, + const std::vector* avColors, + const std::vector* avMaterials, + std::vector* avOut); + + + /** Buffer to hold the loaded file */ + unsigned char* mBuffer; + + /** Document object model representation extracted from the file */ + PLY::DOM* pcDOM; + }; + +} // end of namespace Assimp + +#endif // AI_3DSIMPORTER_H_INC \ No newline at end of file diff --git a/code/PlyParser.cpp b/code/PlyParser.cpp new file mode 100644 index 000000000..d82c46a0d --- /dev/null +++ b/code/PlyParser.cpp @@ -0,0 +1,874 @@ +/** @file Implementation of the PLY parser class */ +#include "PLYLoader.h" +#include "MaterialSystem.h" +#include "fast_atof.h" + +#include "../include/IOStream.h" +#include "../include/IOSystem.h" +#include "../include/aiMesh.h" +#include "../include/aiScene.h" +#include "../include/aiAssert.h" + +#include + +using namespace Assimp; + + +// ------------------------------------------------------------------------------------------------ +PLY::EDataType PLY::Property::ParseDataType(const char* p_szIn,const char** p_szOut) +{ + PLY::EDataType eOut = PLY::EDT_INVALID; + + if (0 == ASSIMP_strincmp(p_szIn,"char",4) || + 0 == ASSIMP_strincmp(p_szIn,"int8",4)) + { + p_szIn+=4; + eOut = PLY::EDT_Char; + } + else if (0 == ASSIMP_strincmp(p_szIn,"uchar",5) || + 0 == ASSIMP_strincmp(p_szIn,"uint8",5)) + { + p_szIn+=5; + eOut = PLY::EDT_UChar; + } + else if (0 == ASSIMP_strincmp(p_szIn,"short",5) || + 0 == ASSIMP_strincmp(p_szIn,"int16",5)) + { + p_szIn+=5; + eOut = PLY::EDT_Short; + } + else if (0 == ASSIMP_strincmp(p_szIn,"ushort",6) || + 0 == ASSIMP_strincmp(p_szIn,"uint16",6)) + { + p_szIn+=6; + eOut = PLY::EDT_UShort; + } + else if (0 == ASSIMP_strincmp(p_szIn,"int32",5)) + { + p_szIn+=5; + eOut = PLY::EDT_Int; + } + else if (0 == ASSIMP_strincmp(p_szIn,"uint32",6)) + { + p_szIn+=6; + eOut = PLY::EDT_UInt; + } + else if (0 == ASSIMP_strincmp(p_szIn,"int",3)) + { + p_szIn+=3; + eOut = PLY::EDT_Int; + } + else if (0 == ASSIMP_strincmp(p_szIn,"uint",4)) + { + p_szIn+=4; + eOut = PLY::EDT_UInt; + } + else if (0 == ASSIMP_strincmp(p_szIn,"float32",7)) + { + p_szIn+=7; + eOut = PLY::EDT_Float; + } + else if (0 == ASSIMP_strincmp(p_szIn,"float",5)) + { + p_szIn+=5; + eOut = PLY::EDT_Float; + } + else if (0 == ASSIMP_strincmp(p_szIn,"float64",7)) + { + p_szIn+=7; + eOut = PLY::EDT_Double; + } + else if (0 == ASSIMP_strincmp(p_szIn,"double64",8)) + { + p_szIn+=8; + eOut = PLY::EDT_Double; + } + else if (0 == ASSIMP_strincmp(p_szIn,"double",6)) + { + p_szIn+=6; + eOut = PLY::EDT_Double; + } + // either end of line or space, but no other characters allowed + if (!(IsSpace(*p_szIn) || IsLineEnd(*p_szIn))) + { + eOut = PLY::EDT_INVALID; + } + *p_szOut = p_szIn; + return eOut; +} +// ------------------------------------------------------------------------------------------------ +PLY::ESemantic PLY::Property::ParseSemantic(const char* p_szIn,const char** p_szOut) +{ + PLY::ESemantic eOut = PLY::EST_INVALID; + if (0 == ASSIMP_strincmp(p_szIn,"x",1)) + { + p_szIn++; + eOut = PLY::EST_XCoord; + } + else if (0 == ASSIMP_strincmp(p_szIn,"y",1)) + { + p_szIn++; + eOut = PLY::EST_YCoord; + } + else if (0 == ASSIMP_strincmp(p_szIn,"z",1)) + { + p_szIn++; + eOut = PLY::EST_ZCoord; + } + else if (0 == ASSIMP_strincmp(p_szIn,"red",3)) + { + p_szIn+=3; + eOut = PLY::EST_Red; + } + else if (0 == ASSIMP_strincmp(p_szIn,"green",4)) + { + p_szIn+=5; + eOut = PLY::EST_Green; + } + else if (0 == ASSIMP_strincmp(p_szIn,"blue",4)) + { + p_szIn+=4; + eOut = PLY::EST_Blue; + } + else if (0 == ASSIMP_strincmp(p_szIn,"alpha",5)) + { + p_szIn+=5; + eOut = PLY::EST_Alpha; + } + else if (0 == ASSIMP_strincmp(p_szIn,"vertex_index",12)) + { + p_szIn+=12; + eOut = PLY::EST_VertexIndex; + } + else if (0 == ASSIMP_strincmp(p_szIn,"vertex_indices",14)) + { + p_szIn+=14; + eOut = PLY::EST_VertexIndex; + } + else if (0 == ASSIMP_strincmp(p_szIn,"material_index",14)) + { + p_szIn+=14; + eOut = PLY::EST_MaterialIndex; + } + else if (0 == ASSIMP_strincmp(p_szIn,"ambient_red",11)) + { + p_szIn+=11; + eOut = PLY::EST_AmbientRed; + } + else if (0 == ASSIMP_strincmp(p_szIn,"ambient_green",13)) + { + p_szIn+=13; + eOut = PLY::EST_AmbientGreen; + } + else if (0 == ASSIMP_strincmp(p_szIn,"ambient_blue",12)) + { + p_szIn+=12; + eOut = PLY::EST_AmbientBlue; + } + else if (0 == ASSIMP_strincmp(p_szIn,"ambient_alpha",13)) + { + p_szIn+=13; + eOut = PLY::EST_AmbientAlpha; + } + else if (0 == ASSIMP_strincmp(p_szIn,"diffuse_red",11)) + { + p_szIn+=11; + eOut = PLY::EST_DiffuseRed; + } + else if (0 == ASSIMP_strincmp(p_szIn,"diffuse_green",13)) + { + p_szIn+=13; + eOut = PLY::EST_DiffuseGreen; + } + else if (0 == ASSIMP_strincmp(p_szIn,"diffuse_blue",12)) + { + p_szIn+=12; + eOut = PLY::EST_DiffuseBlue; + } + else if (0 == ASSIMP_strincmp(p_szIn,"diffuse_alpha",13)) + { + p_szIn+=13; + eOut = PLY::EST_DiffuseAlpha; + } + else if (0 == ASSIMP_strincmp(p_szIn,"specular_red",12)) + { + p_szIn+=12; + eOut = PLY::EST_SpecularRed; + } + else if (0 == ASSIMP_strincmp(p_szIn,"specular_green",14)) + { + p_szIn+=14; + eOut = PLY::EST_SpecularGreen; + } + else if (0 == ASSIMP_strincmp(p_szIn,"specular_blue",13)) + { + p_szIn+=13; + eOut = PLY::EST_SpecularBlue; + } + else if (0 == ASSIMP_strincmp(p_szIn,"specular_alpha",14)) + { + p_szIn+=14; + eOut = PLY::EST_SpecularAlpha; + } + else if (0 == ASSIMP_strincmp(p_szIn,"opacity",7)) + { + p_szIn+=7; + eOut = PLY::EST_Opacity; + } + else if (0 == ASSIMP_strincmp(p_szIn,"specular_power",6)) + { + p_szIn+=7; + eOut = PLY::EST_PhongPower; + } + else if (0 == ASSIMP_strincmp(p_szIn,"r",1)) + { + p_szIn++; + eOut = PLY::EST_Red; + } + else if (0 == ASSIMP_strincmp(p_szIn,"g",1)) + { + p_szIn++; + eOut = PLY::EST_Green; + } + else if (0 == ASSIMP_strincmp(p_szIn,"b",1)) + { + p_szIn++; + eOut = PLY::EST_Blue; + } + else + { + // ... find the next space or new line + while (*p_szIn != ' ' && *p_szIn != '\t' && + *p_szIn != '\r' && *p_szIn != '\0' && *p_szIn != '\n')p_szIn++; + } + // either end of line or space, but no other characters allowed + if (!(IsSpace(*p_szIn) || IsLineEnd(*p_szIn))) + { + eOut = PLY::EST_INVALID; + } + *p_szOut = p_szIn; + return eOut; +} +// ------------------------------------------------------------------------------------------------ +bool PLY::Property::ParseProperty (const char* p_szIn, const char** p_szOut, PLY::Property* pOut) +{ + // Forms supported: + // "property float x" + // "property list uchar int vertex_index" + *p_szOut = p_szIn; + + // skip leading spaces + if (!SkipSpaces(p_szIn,&p_szIn))return false; + + // skip the "property" string at the beginning + if (0 != ASSIMP_strincmp(p_szIn,"property",8) || !IsSpace(*(p_szIn+8))) + { + // seems not to be a valid property entry + return false; + } + // get next word + p_szIn += 9; + if (!SkipSpaces(p_szIn,&p_szIn))return false; + + if (0 == ASSIMP_strincmp(p_szIn,"list",4) && IsSpace(*(p_szIn+4))) + { + pOut->bIsList = true; + + // seems to be a list. + p_szIn += 5; + if(EDT_INVALID == (pOut->eFirstType = PLY::Property::ParseDataType(p_szIn, &p_szIn))) + { + // unable to parse list size data type + SkipLine(p_szIn,&p_szIn); + *p_szOut = p_szIn; + return false; + } + if (!SkipSpaces(p_szIn,&p_szIn))return false; + if(EDT_INVALID == (pOut->eType = PLY::Property::ParseDataType(p_szIn, &p_szIn))) + { + // unable to parse list data type + SkipLine(p_szIn,&p_szIn); + *p_szOut = p_szIn; + return false; + } + } + else + { + if(EDT_INVALID == (pOut->eType = PLY::Property::ParseDataType(p_szIn, &p_szIn))) + { + // unable to parse data type. Skip the property + SkipLine(p_szIn,&p_szIn); + *p_szOut = p_szIn; + return false; + } + } + + if (!SkipSpaces(p_szIn,&p_szIn))return false; + const char* szCur = p_szIn; + pOut->Semantic = PLY::Property::ParseSemantic(p_szIn, &p_szIn); + + if (PLY::EST_INVALID == pOut->Semantic) + { + // store the name of the semantic + uintptr_t iDiff = (uintptr_t)p_szIn - (uintptr_t)szCur; + + pOut->szName = std::string(szCur,iDiff); + } + + SkipSpacesAndLineEnd(p_szIn,&p_szIn); + *p_szOut = p_szIn; + return true; +} +// ------------------------------------------------------------------------------------------------ +PLY::EElementSemantic PLY::Element::ParseSemantic(const char* p_szIn,const char** p_szOut) +{ + PLY::EElementSemantic eOut = PLY::EEST_INVALID; + if (0 == ASSIMP_strincmp(p_szIn,"vertex",6)) + { + p_szIn+=6; + eOut = PLY::EEST_Vertex; + } + else if (0 == ASSIMP_strincmp(p_szIn,"face",4)) + { + p_szIn+=4; + eOut = PLY::EEST_Face; + } +#if 0 + else if (0 == ASSIMP_strincmp(p_szIn,"range_grid",10)) + { + p_szIn+=10; + eOut = PLY::EEST_Face; + } +#endif + else if (0 == ASSIMP_strincmp(p_szIn,"tristrips",9)) + { + p_szIn+=9; + eOut = PLY::EEST_TriStrip; + } + else if (0 == ASSIMP_strincmp(p_szIn,"edge",4)) + { + p_szIn+=4; + eOut = PLY::EEST_Edge; + } + else if (0 == ASSIMP_strincmp(p_szIn,"material",8)) + { + p_szIn+=8; + eOut = PLY::EEST_Material; + } + + // either end of line or space, but no other characters allowed + if (!(IsSpace(*p_szIn) || IsLineEnd(*p_szIn))) + { + eOut = PLY::EEST_INVALID; + } + *p_szOut = p_szIn; + return eOut; +} +// ------------------------------------------------------------------------------------------------ +bool PLY::Element::ParseElement (const char* p_szIn, const char** p_szOut, + PLY::Element* pOut) +{ + // Example format: "element vertex 8" + *p_szOut = p_szIn; + + // skip leading spaces + if (!SkipSpaces(p_szIn,&p_szIn))return false; + + // skip the "element" string at the beginning + if (0 != ASSIMP_strincmp(p_szIn,"element",7) || !IsSpace(*(p_szIn+7))) + { + // seems not to be a valid property entry + return false; + } + // get next word + p_szIn += 8; + if (!SkipSpaces(p_szIn,&p_szIn))return false; + + // parse the semantic of the element + const char* szCur = p_szIn; + pOut->eSemantic = PLY::Element::ParseSemantic(p_szIn,&p_szIn); + + if (PLY::EEST_INVALID == pOut->eSemantic) + { + // store the name of the semantic + uintptr_t iDiff = (uintptr_t)p_szIn - (uintptr_t)szCur; + + pOut->szName = std::string(szCur,iDiff); + } + + if (!SkipSpaces(p_szIn,&p_szIn))return false; + + //parse the number of occurences of this element + pOut->NumOccur = strtol10(p_szIn,&p_szIn); + + // go to the next line + SkipSpacesAndLineEnd(p_szIn,&p_szIn); + + // now parse all properties of the element + while(true) + { + // skip all comments + PLY::DOM::SkipComments(p_szIn,&p_szIn); + + Property prop; + if(!PLY::Property::ParseProperty(p_szIn,&p_szIn,&prop))break; + + // add the property to the property list + pOut->alProperties.push_back(prop); + } + *p_szOut = p_szIn; + return true; +} +// ------------------------------------------------------------------------------------------------ +bool PLY::DOM::SkipComments (const char* p_szIn,const char** p_szOut) +{ + *p_szOut = p_szIn; + + // skip spaces + if (!SkipSpaces(p_szIn,&p_szIn))return false; + + if (0 == ASSIMP_strincmp(p_szIn,"comment",7)) + { + p_szIn += 7; + + SkipLine(p_szIn,&p_szIn); + + SkipComments(p_szIn,&p_szIn); + *p_szOut = p_szIn; + return true; + } + return false; +} +// ------------------------------------------------------------------------------------------------ +bool PLY::DOM::ParseHeader (const char* p_szIn,const char** p_szOut) +{ + // after ply and format line + *p_szOut = p_szIn; + + // parse all elements + while (true) + { + // skip all comments + PLY::DOM::SkipComments(p_szIn,&p_szIn); + + Element out; + if(PLY::Element::ParseElement(p_szIn,&p_szIn,&out)) + { + // add the element to the list of elements + this->alElements.push_back(out); + } + else if (0 == ASSIMP_strincmp(p_szIn,"end_header",10) && IsSpaceOrNewLine(*(p_szIn+10))) + { + // we have reached the end of the header + p_szIn += 11; + break; + } + // ignore unknown header elements + } + SkipSpacesAndLineEnd(p_szIn,&p_szIn); + *p_szOut = p_szIn; + return true; +} +// ------------------------------------------------------------------------------------------------ +bool PLY::DOM::ParseElementInstanceLists (const char* p_szIn,const char** p_szOut) +{ + this->alElementData.resize(this->alElements.size()); + + std::vector::const_iterator i = this->alElements.begin(); + std::vector::iterator a = this->alElementData.begin(); + + // parse all element instances + for (;i != this->alElements.end();++i,++a) + { + *a = PLY::ElementInstanceList(&(*i)); // reserve enough storage + PLY::ElementInstanceList::ParseInstanceList(p_szIn,&p_szIn,&(*i),&(*a)); + } + return true; +} +// ------------------------------------------------------------------------------------------------ +bool PLY::DOM::ParseElementInstanceListsBinary (const char* p_szIn,const char** p_szOut,bool p_bBE) +{ + this->alElementData.resize(this->alElements.size()); + + std::vector::const_iterator i = this->alElements.begin(); + std::vector::iterator a = this->alElementData.begin(); + + // parse all element instances + for (;i != this->alElements.end();++i,++a) + { + *a = PLY::ElementInstanceList(&(*i)); // reserve enough storage + PLY::ElementInstanceList::ParseInstanceListBinary(p_szIn,&p_szIn,&(*i),&(*a),p_bBE); + } + return true; +} +// ------------------------------------------------------------------------------------------------ +bool PLY::DOM::ParseInstanceBinary (const char* p_szIn,DOM* p_pcOut,bool p_bBE) +{ + if(!p_pcOut->ParseHeader(p_szIn,&p_szIn)) + { + return false; + } + if(!p_pcOut->ParseElementInstanceListsBinary(p_szIn,&p_szIn,p_bBE)) + { + return false; + } + return true; +} +// ------------------------------------------------------------------------------------------------ +bool PLY::DOM::ParseInstance (const char* p_szIn,DOM* p_pcOut) +{ + if(!p_pcOut->ParseHeader(p_szIn,&p_szIn)) + { + return false; + } + if(!p_pcOut->ParseElementInstanceLists(p_szIn,&p_szIn)) + { + return false; + } + return true; +} +// ------------------------------------------------------------------------------------------------ +bool PLY::ElementInstanceList::ParseInstanceList (const char* p_szIn,const char** p_szOut, + const PLY::Element* pcElement, PLY::ElementInstanceList* p_pcOut) +{ + if (EEST_INVALID == pcElement->eSemantic) + { + // if the element has an unknown semantic we can skip all lines + // However, there could be comments + for (unsigned int i = 0; i < pcElement->NumOccur;++i) + { + PLY::DOM::SkipComments(p_szIn,&p_szIn); + SkipLine(p_szIn,&p_szIn); + } + } + else + { + // be sure to have enough storage + p_pcOut->alInstances.resize(pcElement->NumOccur); + for (unsigned int i = 0; i < pcElement->NumOccur;++i) + { + PLY::DOM::SkipComments(p_szIn,&p_szIn); + + PLY::ElementInstance out; + PLY::ElementInstance::ParseInstance(p_szIn, &p_szIn,pcElement, &out); + // add it to the list + p_pcOut->alInstances[i] = out; + } + } + *p_szOut = p_szIn; + return true; +} +// ------------------------------------------------------------------------------------------------ +bool PLY::ElementInstanceList::ParseInstanceListBinary (const char* p_szIn,const char** p_szOut, + const PLY::Element* pcElement, PLY::ElementInstanceList* p_pcOut,bool p_bBE) +{ + // we can add special handling code for unknown element semantics since + // we can't skip it as a whole block (we don't know its exact size + // due to the fact that lists could be contained in the property list + // of the unknown element) + for (unsigned int i = 0; i < pcElement->NumOccur;++i) + { + PLY::ElementInstance out; + PLY::ElementInstance::ParseInstanceBinary(p_szIn, &p_szIn,pcElement, &out, p_bBE); + // add it to the list + p_pcOut->alInstances[i] = out; + } + *p_szOut = p_szIn; + return true; +} +// ------------------------------------------------------------------------------------------------ +bool PLY::ElementInstance::ParseInstance (const char* p_szIn,const char** p_szOut, + const PLY::Element* pcElement, PLY::ElementInstance* p_pcOut) +{ + if (!SkipSpaces(p_szIn, &p_szIn))return false; + + p_pcOut->alProperties.resize(pcElement->alProperties.size()); + + *p_szOut = p_szIn; + std::vector::iterator i = p_pcOut->alProperties.begin(); + std::vector::const_iterator a = pcElement->alProperties.begin(); + for (;i != p_pcOut->alProperties.end();++i,++a) + { + if(!(PLY::PropertyInstance::ParseInstance(p_szIn, &p_szIn,&(*a),&(*i)))) + { + // skip the rest of the instance + SkipLine(p_szIn, &p_szIn); + + PLY::PropertyInstance::ValueUnion v = PLY::PropertyInstance::DefaultValue((*a).eType); + (*i).avList.push_back(v); + } + } + *p_szOut = p_szIn; + return true; +} +// ------------------------------------------------------------------------------------------------ +bool PLY::ElementInstance::ParseInstanceBinary (const char* p_szIn,const char** p_szOut, + const PLY::Element* pcElement, PLY::ElementInstance* p_pcOut, bool p_bBE) +{ + p_pcOut->alProperties.resize(pcElement->alProperties.size()); + + *p_szOut = p_szIn; + std::vector::iterator i = p_pcOut->alProperties.begin(); + std::vector::const_iterator a = pcElement->alProperties.begin(); + for (;i != p_pcOut->alProperties.end();++i,++a) + { + if(!(PLY::PropertyInstance::ParseInstance(p_szIn, &p_szIn,&(*a),&(*i)))) + { + PLY::PropertyInstance::ValueUnion v = PLY::PropertyInstance::DefaultValue((*a).eType); + (*i).avList.push_back(v); + } + } + *p_szOut = p_szIn; + return true; +} +// ------------------------------------------------------------------------------------------------ +bool PLY::PropertyInstance::ParseInstance (const char* p_szIn,const char** p_szOut, + const PLY::Property* prop, PLY::PropertyInstance* p_pcOut) +{ + *p_szOut = p_szIn; + + // skip spaces at the beginning + if (!SkipSpaces(p_szIn, &p_szIn))return false; + + if (prop->bIsList) + { + // parse the number of elements in the list + PLY::PropertyInstance::ValueUnion v; + PLY::PropertyInstance::ParseValue(p_szIn, &p_szIn,prop->eFirstType,&v); + + // convert to unsigned int + unsigned int iNum = PLY::PropertyInstance::ConvertTo(v,prop->eFirstType); + + // parse all list elements + for (unsigned int i = 0; i < iNum;++i) + { + if (!SkipSpaces(p_szIn, &p_szIn))return false; + + PLY::PropertyInstance::ParseValue(p_szIn, &p_szIn,prop->eType,&v); + p_pcOut->avList.push_back(v); + } + } + else + { + // parse the property + PLY::PropertyInstance::ValueUnion v; + PLY::PropertyInstance::ParseValue(p_szIn, &p_szIn,prop->eType,&v); + p_pcOut->avList.push_back(v); + } + SkipSpacesAndLineEnd(p_szIn, &p_szIn); + *p_szOut = p_szIn; + return true; +} +// ------------------------------------------------------------------------------------------------ +bool PLY::PropertyInstance::ParseInstanceBinary (const char* p_szIn,const char** p_szOut, + const PLY::Property* prop, PLY::PropertyInstance* p_pcOut,bool p_bBE) +{ + *p_szOut = p_szIn; + + if (prop->bIsList) + { + // parse the number of elements in the list + PLY::PropertyInstance::ValueUnion v; + PLY::PropertyInstance::ParseValueBinary(p_szIn, &p_szIn,prop->eFirstType,&v,p_bBE); + + // convert to unsigned int + unsigned int iNum = PLY::PropertyInstance::ConvertTo(v,prop->eFirstType); + + // parse all list elements + for (unsigned int i = 0; i < iNum;++i) + { + PLY::PropertyInstance::ParseValueBinary(p_szIn, &p_szIn,prop->eType,&v,p_bBE); + p_pcOut->avList.push_back(v); + } + } + else + { + // parse the property + PLY::PropertyInstance::ValueUnion v; + PLY::PropertyInstance::ParseValueBinary(p_szIn, &p_szIn,prop->eType,&v,p_bBE); + p_pcOut->avList.push_back(v); + } + *p_szOut = p_szIn; + return true; +} +// ------------------------------------------------------------------------------------------------ +PLY::PropertyInstance::ValueUnion PLY::PropertyInstance::DefaultValue( + PLY::EDataType eType) +{ + PLY::PropertyInstance::ValueUnion out; + + switch (eType) + { + case EDT_Float: + out.fFloat = 0.0f; + return out; + + case EDT_Double: + out.fDouble = 0.0; + return out; + }; + out.iUInt = 0; + return out; +} +// ------------------------------------------------------------------------------------------------ +bool PLY::PropertyInstance::ParseValue(const char* p_szIn,const char** p_szOut, + PLY::EDataType eType,PLY::PropertyInstance::ValueUnion* out) +{ + *p_szOut = p_szIn; + + switch (eType) + { + case EDT_UInt: + case EDT_UShort: + case EDT_UChar: + + // simply parse in a full uint + out->iUInt = (uint32_t)strtol10(p_szIn, &p_szIn); + + break; + + case EDT_Int: + case EDT_Short: + case EDT_Char: + + { + // simply parse in a full int + // Take care of the sign at the beginning + bool bMinus = false; + if (*p_szIn == '-') + { + p_szIn++; + bMinus = true; + } + out->iInt = (int32_t)strtol10(p_szIn, &p_szIn); + if (bMinus)out->iInt *= -1; + + break; + } + + case EDT_Float: + + // parse a simple float + p_szIn = fast_atof_move(p_szIn,out->fFloat); + break; + + case EDT_Double: + + // Parse a double float. .. TODO: support this + float f; + p_szIn = fast_atof_move(p_szIn,f); + out->fDouble = (double)f; + + default: + return false; + } + *p_szOut = p_szIn; + return true; +} +// ------------------------------------------------------------------------------------------------ +bool PLY::PropertyInstance::ParseValueBinary(const char* p_szIn,const char** p_szOut, + PLY::EDataType eType,PLY::PropertyInstance::ValueUnion* out, bool p_bBE) +{ + *p_szOut = p_szIn; + + switch (eType) + { + case EDT_UInt: + out->iUInt = (uint32_t)*((uint32_t*)p_szIn); + p_szIn += 4; + + if (p_bBE) + { + std::swap(((unsigned char*)(&out->iUInt))[0],((unsigned char*)(&out->iUInt))[3]); + std::swap(((unsigned char*)(&out->iUInt))[1],((unsigned char*)(&out->iUInt))[2]); + } + break; + + case EDT_UShort: + { + uint16_t i = *((uint16_t*)p_szIn); + if (p_bBE) + { + std::swap(((unsigned char*)(&i))[0],((unsigned char*)(&i))[1]); + } + out->iUInt = (uint32_t)i; + p_szIn += 2; + break; + } + + case EDT_UChar: + { + uint8_t i = *((uint8_t*)p_szIn); + out->iUInt = (uint32_t)i; + p_szIn += 2; + break; + } + + case EDT_Int: + out->iInt = *((int32_t*)p_szIn); + p_szIn += 4; + + if (p_bBE) + { + std::swap(((unsigned char*)(&out->iInt))[0],((unsigned char*)(&out->iInt))[3]); + std::swap(((unsigned char*)(&out->iInt))[1],((unsigned char*)(&out->iInt))[2]); + } + break; + + case EDT_Short: + { + int16_t i = *((int16_t*)p_szIn); + if (p_bBE) + { + std::swap(((unsigned char*)(&i))[0],((unsigned char*)(&i))[1]); + } + out->iInt = (int32_t)i; + p_szIn += 2; + break; + } + + case EDT_Char: + out->iInt = (int32_t)*((int8_t*)p_szIn); + p_szIn += 1; + break; + + case EDT_Float: + if (p_bBE) + { + union {char szArray[4]; float fValue; } _X; + + _X.szArray[0] = ((unsigned char*)p_szIn)[3]; + _X.szArray[1] = ((unsigned char*)p_szIn)[2]; + _X.szArray[2] = ((unsigned char*)p_szIn)[1]; + _X.szArray[3] = ((unsigned char*)p_szIn)[0]; + out->fFloat = _X.fValue; + } + else out->fFloat = *((float*)p_szIn); + p_szIn += 4; + break; + + case EDT_Double: + if (p_bBE) + { + union {char szArray[8]; double fValue; } _X; + + _X.szArray[0] = ((unsigned char*)p_szIn)[7]; + _X.szArray[1] = ((unsigned char*)p_szIn)[6]; + _X.szArray[2] = ((unsigned char*)p_szIn)[5]; + _X.szArray[3] = ((unsigned char*)p_szIn)[4]; + _X.szArray[4] = ((unsigned char*)p_szIn)[3]; + _X.szArray[5] = ((unsigned char*)p_szIn)[2]; + _X.szArray[6] = ((unsigned char*)p_szIn)[1]; + _X.szArray[7] = ((unsigned char*)p_szIn)[0]; + out->fDouble = _X.fValue; + } + else out->fDouble = *((double*)p_szIn); + p_szIn += 8; + break; + + default: + return false; + } + *p_szOut = p_szIn; + return true; +} diff --git a/code/PlyParser.h b/code/PlyParser.h new file mode 100644 index 000000000..3ae80c439 --- /dev/null +++ b/code/PlyParser.h @@ -0,0 +1,483 @@ +/** @file Defines the helper data structures for importing PLY files */ +#ifndef AI_PLYFILEHELPER_H_INC +#define AI_PLYFILEHELPER_H_INC + +#include +#include +#include +#include + +#include "../include/aiTypes.h" +#include "../include/aiMesh.h" +#include "../include/aiAnim.h" + +namespace Assimp +{ + +// http://local.wasp.uwa.edu.au/~pbourke/dataformats/ply/ +// http://w3.impa.br/~lvelho/outgoing/sossai/old/ViHAP_D4.4.2_PLY_format_v1.1.pdf +// http://www.okino.com/conv/exp_ply.htm +namespace PLY +{ + + +// --------------------------------------------------------------------------------- +/* +name type number of bytes +--------------------------------------- +char character 1 +uchar unsigned character 1 +short short integer 2 +ushort unsigned short integer 2 +int integer 4 +uint unsigned integer 4 +float single-precision float 4 +double double-precision float 8 + +int8 +int16 +uint8 ... forms are also used +*/ +enum EDataType +{ + EDT_Char = 0x0u, + EDT_UChar, + EDT_Short, + EDT_UShort, + EDT_Int, + EDT_UInt, + EDT_Float, + EDT_Double, + + // Marks invalid entries + EDT_INVALID +}; + +// --------------------------------------------------------------------------------- +/** \brief Specifies semantics for PLY element properties + * + * Semantics define the usage of a property, e.g. x coordinate +*/ +enum ESemantic +{ + //! vertex position x coordinate + EST_XCoord = 0x0u, + //! vertex position x coordinate + EST_YCoord, + //! vertex position x coordinate + EST_ZCoord, + + //! vertex normal x coordinate + EST_XNormal, + //! vertex normal y coordinate + EST_YNormal, + //! vertex normal z coordinate + EST_ZNormal, + + //! vertex colors, red channel + EST_Red, + //! vertex colors, green channel + EST_Green, + //! vertex colors, blue channel + EST_Blue, + //! vertex colors, alpha channel + EST_Alpha, + + //! vertex index list + EST_VertexIndex, + + //! texture index + EST_TextureIndex, + + //! texture coordinates (stored as element of a face) + EST_TextureCoordinates, + + //! material index + EST_MaterialIndex, + + //! ambient color, red channel + EST_AmbientRed, + //! ambient color, green channel + EST_AmbientGreen, + //! ambient color, blue channel + EST_AmbientBlue, + //! ambient color, alpha channel + EST_AmbientAlpha, + + //! diffuse color, red channel + EST_DiffuseRed, + //! diffuse color, green channel + EST_DiffuseGreen, + //! diffuse color, blue channel + EST_DiffuseBlue, + //! diffuse color, alpha channel + EST_DiffuseAlpha, + + //! specular color, red channel + EST_SpecularRed, + //! specular color, green channel + EST_SpecularGreen, + //! specular color, blue channel + EST_SpecularBlue, + //! specular color, alpha channel + EST_SpecularAlpha, + + //! specular power for phong shading + EST_PhongPower, + + //! opacity between 0 and 1 + EST_Opacity, + + //! Marks invalid entries + EST_INVALID +}; + +// --------------------------------------------------------------------------------- +/** \brief Specifies semantics for PLY elements + * + * Semantics define the usage of an element, e.g. vertex or material +*/ +enum EElementSemantic +{ + //! The element is a vertex + EEST_Vertex = 0x0u, + + //! The element is a face description (index table) + EEST_Face, + + //! The element is a tristrip description (index table) + EEST_TriStrip, + + //! The element is an edge description (ignored) + EEST_Edge, + + //! The element is a material description + EEST_Material, + + //! Marks invalid entries + EEST_INVALID +}; + +// --------------------------------------------------------------------------------- +/** \brief Helper class for a property in a PLY file. + * + * This can e.g. be a part of the vertex declaration + */ +class Property +{ +public: + + //! Default constructor + Property() + : eType (EDT_Int), bIsList(false), eFirstType(EDT_UChar) + {} + + //! Data type of the property + EDataType eType; + + //! Semantical meaning of the property + ESemantic Semantic; + + //! Of the semantic of the property could not be parsed: + //! Contains the semantic specified in the file + std::string szName; + + //! Specifies whether the data type is a list where + //! the first element specifies the size of the list + bool bIsList; + EDataType eFirstType; + + //! Parse a property from a string. The end of the + //! string is either '\n', '\r' or '\0'. Return valie is false + //! if the input string is NOT a valid property (E.g. does + //! not start with the "property" keyword) + static bool ParseProperty (const char* p_szIn, const char** p_szOut, + Property* pOut); + + //! Parse a data type from a string + static EDataType ParseDataType(const char* p_szIn,const char** p_szOut); + + //! Parse a semantic from a string + static ESemantic ParseSemantic(const char* p_szIn,const char** p_szOut); +}; + +// --------------------------------------------------------------------------------- +/** \brief Helper class for an element in a PLY file. + * + * This can e.g. be the vertex declaration. Elements contain a + * well-defined number of properties. + */ +class Element +{ +public: + + //! Default constructor + Element() + : NumOccur(0), eSemantic (EEST_INVALID) + {} + + //! List of properties assigned to the element + //! std::vector to support operator[] + std::vector alProperties; + + //! Semantic of the element + EElementSemantic eSemantic; + + //! Of the semantic of the element could not be parsed: + //! Contains the semantic specified in the file + std::string szName; + + //! How many times will the element occur? + unsigned int NumOccur; + + //! Parse an element from a string. + //! The function will parse all properties contained in the + //! element, too. + static bool ParseElement (const char* p_szIn, const char** p_szOut, + Element* pOut); + + //! Parse a semantic from a string + static EElementSemantic ParseSemantic(const char* p_szIn, + const char** p_szOut); +}; + +// --------------------------------------------------------------------------------- +/** \brief Instance of a property in a PLY file + */ +class PropertyInstance +{ +public: + + //! Default constructor + PropertyInstance () + {} + + union ValueUnion + { + + //! uInt32 representation of the property. All + // uint types are automatically converted to uint32 + uint32_t iUInt; + + //! Int32 representation of the property. All + // int types are automatically converted to int32 + int32_t iInt; + + //! Float32 representation of the property + float fFloat; + + //! Float64 representation of the property + double fDouble; + + }; + + //! List of all values parsed. Contains only one value + // for non-list propertys + std::list avList; + + //! Parse a property instance + static bool ParseInstance (const char* p_szIn,const char** p_szOut, + const Property* prop, PropertyInstance* p_pcOut); + + //! Parse a property instance in binary format + static bool ParseInstanceBinary (const char* p_szIn,const char** p_szOut, + const Property* prop, PropertyInstance* p_pcOut,bool p_bBE); + + //! Get the default value for a given data type + static ValueUnion DefaultValue(EDataType eType); + + //! Parse a value + static bool ParseValue(const char* p_szIn,const char** p_szOut, + EDataType eType,ValueUnion* out); + + //! Parse a binary value + static bool ParseValueBinary(const char* p_szIn,const char** p_szOut, + EDataType eType,ValueUnion* out,bool p_bBE); + + //! Convert a property value to a given type TYPE + template + static TYPE ConvertTo(ValueUnion v, EDataType eType); +}; + +// --------------------------------------------------------------------------------- +/** \brief Class for an element instance in a PLY file + */ +class ElementInstance +{ +public: + + //! Default constructor + ElementInstance () + {} + + //! List of all parsed properties + std::vector< PropertyInstance > alProperties; + + //! Parse an element instance + static bool ParseInstance (const char* p_szIn,const char** p_szOut, + const Element* pcElement, ElementInstance* p_pcOut); + + //! Parse a binary element instance + static bool ParseInstanceBinary (const char* p_szIn,const char** p_szOut, + const Element* pcElement, ElementInstance* p_pcOut,bool p_bBE); +}; + +// --------------------------------------------------------------------------------- +/** \brief Class for an element instance list in a PLY file + */ +class ElementInstanceList +{ +public: + + //! Default constructor + ElementInstanceList () + {} + + ElementInstanceList (const Element* pc) + { + alInstances.reserve(pc->NumOccur); + } + + //! List of all element instances + std::vector< ElementInstance > alInstances; + + //! Parse an element instance list + static bool ParseInstanceList (const char* p_szIn,const char** p_szOut, + const Element* pcElement, ElementInstanceList* p_pcOut); + + //! Parse a binary element instance list + static bool ParseInstanceListBinary (const char* p_szIn,const char** p_szOut, + const Element* pcElement, ElementInstanceList* p_pcOut,bool p_bBE); +}; +// --------------------------------------------------------------------------------- +/** \brief Class to represent the document object model of an ASCII or binary + * (both little and big-endian) PLY file + */ +class DOM +{ +public: + + //! Default constructor + DOM() + {} + + + std::vector alElements; + std::vector alElementData; + + //! Parse the DOM for a PLY file. The input string is assumed + //! to be terminated with zero + static bool ParseInstance (const char* p_szIn,DOM* p_pcOut); + static bool ParseInstanceBinary (const char* p_szIn, + DOM* p_pcOut,bool p_bBE); + + //! Skip all comment lines after this + static bool SkipComments (const char* p_szIn,const char** p_szOut); + +private: + + //! Handle the file header and read all element descriptions + bool ParseHeader (const char* p_szIn,const char** p_szOut); + + //! Read in all element instance lists + bool ParseElementInstanceLists (const char* p_szIn,const char** p_szOut); + + //! Read in all element instance lists for a binary file format + bool ParseElementInstanceListsBinary (const char* p_szIn, + const char** p_szOut,bool p_bBE); +}; + +// --------------------------------------------------------------------------------- +/** \brief Helper class to represent a loaded face + */ +class Face +{ +public: + + Face() + : iMaterialIndex(0xFFFFFFFF) + { + // set all indices to zero by default + mIndices.resize(3,0); + } + +public: + + //! List of vertex indices + std::vector mIndices; + + //! Material index + unsigned int iMaterialIndex; +}; + +// --------------------------------------------------------------------------------- +template +TYPE PLY::PropertyInstance::ConvertTo( + PLY::PropertyInstance::ValueUnion v, PLY::EDataType eType) +{ + switch (eType) + { + case EDT_Float: + return (TYPE)v.fFloat; + case EDT_Double: + return (TYPE)v.fDouble; + + case EDT_UInt: + case EDT_UShort: + case EDT_UChar: + return (TYPE)v.iUInt; + + case EDT_Int: + case EDT_Short: + case EDT_Char: + return (TYPE)v.iInt; + }; + return (TYPE)0; +} +// --------------------------------------------------------------------------------- +inline bool IsSpace( const char in) +{ + return (in == ' ' || in == '\t'); +} +// --------------------------------------------------------------------------------- +inline bool IsLineEnd( const char in) +{ + return (in == '\r' || in == '\n' || in == '\0'); +} +// --------------------------------------------------------------------------------- +inline bool IsSpaceOrNewLine( const char in) +{ + return IsSpace(in) || IsLineEnd(in); +} +// --------------------------------------------------------------------------------- +inline bool SkipSpaces( const char* in, const char** out) +{ + while (*in == ' ' || *in == '\t')in++; + *out = in; + return !IsLineEnd(*in); +} +// --------------------------------------------------------------------------------- +inline bool SkipLine( const char* in, const char** out) +{ + while (*in != '\r' && *in != '\n' && *in != '\0')in++; + + if (*in == '\0') + { + *out = in; + return false; + } + in++; + *out = in; + return true; +} +// --------------------------------------------------------------------------------- +inline void SkipSpacesAndLineEnd( const char* in, const char** out) +{ + while (*in == ' ' || *in == '\t' || *in == '\r' || *in == '\n')in++; + *out = in; +} + +}; +}; + +#endif // !! include guard \ No newline at end of file diff --git a/code/SpatialSort.cpp b/code/SpatialSort.cpp new file mode 100644 index 000000000..ea2c71f72 --- /dev/null +++ b/code/SpatialSort.cpp @@ -0,0 +1,93 @@ +/** @file Implementation of the helper class to quickly find vertices close to a given position */ +#include +#include "SpatialSort.h" + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructs a spatially sorted representation from the given position array. +SpatialSort::SpatialSort( const aiVector3D* pPositions, unsigned int pNumPositions, unsigned int pElementOffset) +{ + // define the reference plane. We choose some arbitrary vector away from all basic axises + // in the hope that no model spreads all its vertices along this plane. + mPlaneNormal.Set( 0.8523f, 0.34321f, 0.5736f); + mPlaneNormal.Normalize(); + + // store references to all given positions along with their distance to the reference plane + mPositions.reserve( pNumPositions); + for( unsigned int a = 0; a < pNumPositions; a++) + { + const char* tempPointer = reinterpret_cast (pPositions); + const aiVector3D* vec = reinterpret_cast (tempPointer + a * pElementOffset); + + // store position by index and distance + float distance = *vec * mPlaneNormal; + mPositions.push_back( Entry( a, *vec, distance)); + } + + // now sort the array ascending by distance. + std::sort( mPositions.begin(), mPositions.end()); +} + +// ------------------------------------------------------------------------------------------------ +// Destructor +SpatialSort::~SpatialSort() +{ + // nothing to do here, everything destructs automatically +} + +// ------------------------------------------------------------------------------------------------ +// Returns an iterator for all positions close to the given position. +void SpatialSort::FindPositions( const aiVector3D& pPosition, float pRadius, std::vector& poResults) const +{ + float dist = pPosition * mPlaneNormal; + float minDist = dist - pRadius, maxDist = dist + pRadius; + + // clear the array in this strange fashion because a simple clear() would also deallocate + // the array which we want to avoid + poResults.erase( poResults.begin(), poResults.end()); + + // quick check for positions outside the range + if( mPositions.size() == 0) + return; + if( maxDist < mPositions.front().mDistance) + return; + if( minDist > mPositions.back().mDistance) + return; + + // do a binary search for the minimal distance to start the iteration there + unsigned int index = mPositions.size() / 2; + unsigned int binaryStepSize = mPositions.size() / 4; + while( binaryStepSize > 1) + { + if( mPositions[index].mDistance < minDist) + index += binaryStepSize; + else + index -= binaryStepSize; + + binaryStepSize /= 2; + } + + // depending on the direction of the last step we need to single step a bit back or forth + // to find the actual beginning element of the range + while( index > 0 && mPositions[index].mDistance > minDist) + index--; + while( index < (mPositions.size() - 1) && mPositions[index].mDistance < minDist) + index++; + + // Mow start iterating from there until the first position lays outside of the distance range. + // Add all positions inside the distance range within the given radius to the result aray + std::vector::const_iterator it = mPositions.begin() + index; + float squareEpsilon = pRadius * pRadius; + while( it->mDistance < maxDist) + { + if( (it->mPosition - pPosition).SquareLength() < squareEpsilon) + poResults.push_back( it->mIndex); + ++it; + if( it == mPositions.end()) + break; + } + + // that's it +} + diff --git a/code/SpatialSort.h b/code/SpatialSort.h new file mode 100644 index 000000000..5ae9736ad --- /dev/null +++ b/code/SpatialSort.h @@ -0,0 +1,72 @@ +/** Small helper classes to optimise finding vertizes close to a given location */ +#ifndef AI_SPATIALSORT_H_INC +#define AI_SPATIALSORT_H_INC + +#include +#include "../include/aiVector3D.h" + +namespace Assimp +{ + +// ------------------------------------------------------------------------------------------------ +/** A little helper class to quickly find all vertices in the epsilon environment of a given + * position. Construct an instance with an array of positions. The class stores the given positions + * by their indices and sorts them by their distance to an arbitrary chosen plane. + * You can then query the instance for all vertices close to a given position in an average O(log n) + * time, with O(n) worst case complexity when all vertices lay on the plane. The plane is chosen + * so that it avoids common planes in usual data sets. + */ +class SpatialSort +{ +public: + + SpatialSort() {/* This is unintialized. This is evil. This is OK. */} + + /** Constructs a spatially sorted representation from the given position array. + * Supply the positions in its layout in memory, the class will only refer to them + * by index. + * @param pPositions Pointer to the first position vector of the array. + * @param pNumPositions Number of vectors to expect in that array. + * @param pElementOffset Offset in bytes from the beginning of one vector in memory to the beginning of the next vector. + */ + SpatialSort( const aiVector3D* pPositions, unsigned int pNumPositions, unsigned int pElementOffset); + + /** Destructor */ + ~SpatialSort(); + + /** Returns an iterator for all positions close to the given position. + * @param pPosition The position to look for vertices. + * @param pRadius Maximal distance from the position a vertex may have to be counted in. + * @param poResults The container to store the indices of the found positions. Will be emptied + * by the call so it may contain anything. + * @return An iterator to iterate over all vertices in the given area. + */ + void FindPositions( const aiVector3D& pPosition, float pRadius, std::vector& poResults) const; + +protected: + /** Normal of the sorting plane, normalized. The center is always at (0, 0, 0) */ + aiVector3D mPlaneNormal; + + /** An entry in a spatially sorted position array. Consists of a vertex index, + * its position and its precalculated distance from the reference plane */ + struct Entry + { + unsigned int mIndex; ///< The vertex referred by this entry + aiVector3D mPosition; ///< Position + float mDistance; ///< Distance of this vertex to the sorting plane + + Entry() { /** intentionally not initialized.*/ } + Entry( unsigned int pIndex, const aiVector3D& pPosition, float pDistance) + : mPosition( pPosition), mIndex( pIndex), mDistance( pDistance) + { } + + bool operator < (const Entry& e) const { return mDistance < e.mDistance; } + }; + + // all positions, sorted by distance to the sorting plane + std::vector mPositions; +}; + +} // end of namespace Assimp + +#endif // AI_SPATIALSORT_H_INC \ No newline at end of file diff --git a/code/SplitLargeMeshes.cpp b/code/SplitLargeMeshes.cpp new file mode 100644 index 000000000..725b756da --- /dev/null +++ b/code/SplitLargeMeshes.cpp @@ -0,0 +1,227 @@ +/** @file Implementation of the SplitLargeMeshes postprocessing step +*/ +#include "SplitLargeMeshes.h" +#include "../include/aiPostProcess.h" +#include "../include/aiMesh.h" +#include "../include/aiScene.h" + +using namespace Assimp; + +// Constructor to be privately used by Importer +SplitLargeMeshesProcess::SplitLargeMeshesProcess() + { + } + +// Destructor, private as well +SplitLargeMeshesProcess::~SplitLargeMeshesProcess() + { + // nothing to do here + } + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool SplitLargeMeshesProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_SplitLargeMeshes) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void SplitLargeMeshesProcess::Execute( aiScene* pScene) +{ + std::vector > avList; + + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) + this->SplitMesh(a, pScene->mMeshes[a],avList); + + if (avList.size() != pScene->mNumMeshes) + { + // it seems something has been splitted. rebuild the mesh list + delete[] pScene->mMeshes; + pScene->mNumMeshes = avList.size(); + pScene->mMeshes = new aiMesh*[avList.size()]; + + for (unsigned int i = 0; i < avList.size();++i) + pScene->mMeshes[i] = avList[i].first; + + // now we need to update all nodes + this->UpdateNode(pScene->mRootNode,avList); + } + return; +} +// ------------------------------------------------------------------------------------------------ +// Update a node after some meshes have been split +void SplitLargeMeshesProcess::UpdateNode(aiNode* pcNode, + const std::vector >& avList) +{ + // for every index in out list build a new entry + // TODO: Currently O(n^2) + + std::vector aiEntries; + aiEntries.reserve(pcNode->mNumMeshes + 1); + for (unsigned int i = 0; i < pcNode->mNumMeshes;++i) + { + for (unsigned int a = 0; a < avList.size();++a) + { + if (avList[a].second == pcNode->mMeshes[i]) + { + aiEntries.push_back(a); + } + } + } + + // now build the new list + delete pcNode->mMeshes; + pcNode->mNumMeshes = aiEntries.size(); + pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes]; + + for (unsigned int b = 0; b < pcNode->mNumMeshes;++b) + pcNode->mMeshes[b] = aiEntries[b]; + + // recusively update all other nodes + for (unsigned int i = 0; i < pcNode->mNumChildren;++i) + { + this->UpdateNode ( pcNode->mChildren[i], avList ); + } + return; +} +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void SplitLargeMeshesProcess::SplitMesh( + unsigned int a, + aiMesh* pMesh, + std::vector >& avList) +{ + // TODO: Mesh splitting is currently not supported for meshes + // containing bones + + if (pMesh->mNumVertices > AI_SLM_MAX_VERTICES && 0 == pMesh->mNumBones) + { + // we need to split this mesh into sub meshes + // determine the size of a submesh + const unsigned int iSubMeshes = (pMesh->mNumVertices / AI_SLM_MAX_VERTICES) + 1; + + const unsigned int iOutFaceNum = pMesh->mNumFaces / iSubMeshes; + const unsigned int iOutVertexNum = iOutFaceNum * 3; + + // now generate all submeshes + for (unsigned int i = 0; i < iSubMeshes;++i) + { + aiMesh* pcMesh = new aiMesh; + pcMesh->mNumFaces = iOutFaceNum; + pcMesh->mMaterialIndex = pMesh->mMaterialIndex; + + if (i == iSubMeshes-1) + { + pcMesh->mNumFaces = iOutFaceNum + ( + pMesh->mNumFaces - iOutFaceNum * iSubMeshes); + } + // copy the list of faces + pcMesh->mFaces = new aiFace[pcMesh->mNumFaces]; + + const unsigned int iBase = iOutFaceNum * i; + + // get the total number of indices + unsigned int iCnt = 0; + for (unsigned int p = iBase; p < pcMesh->mNumFaces + iBase;++p) + { + iCnt += pMesh->mFaces[p].mNumIndices; + } + pcMesh->mNumVertices = iCnt; + + // allocate storage + if (pMesh->mVertices != NULL) + pcMesh->mVertices = new aiVector3D[iCnt]; + + if (pMesh->HasNormals()) + pcMesh->mNormals = new aiVector3D[iCnt]; + + if (pMesh->HasTangentsAndBitangents()) + { + pcMesh->mTangents = new aiVector3D[iCnt]; + pcMesh->mBitangents = new aiVector3D[iCnt]; + } + + // texture coordinates + for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) + { + pcMesh->mNumUVComponents[c] = pMesh->mNumUVComponents[c]; + if (pMesh->HasTextureCoords( c)) + { + pcMesh->mTextureCoords[c] = new aiVector3D[iCnt]; + } + } + + // vertex colors + for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS;++c) + { + if (pMesh->HasVertexColors( c)) + { + pcMesh->mColors[c] = new aiColor4D[iCnt]; + } + } + + // (we will also need to copy the array of indices) + for (unsigned int p = 0; p < pcMesh->mNumFaces;++p) + { + pcMesh->mFaces[p].mNumIndices = 3; + + // allocate a new array + unsigned int* pi = pMesh->mFaces[p + iBase].mIndices; + pcMesh->mFaces[p].mIndices = new unsigned int[3]; + + // and copy the contents of the old array, offset by current base + for (unsigned int v = 0; v < 3;++v) + { + unsigned int iIndex = pMesh->mFaces[p+iBase].mIndices[v]; + unsigned int iIndexOut = p*3 + v; + pcMesh->mFaces[p].mIndices[v] = iIndexOut; + + // copy positions + if (pMesh->mVertices != NULL) + { + pcMesh->mVertices[iIndexOut] = pMesh->mVertices[iIndex]; + } + + // copy normals + if (pMesh->HasNormals()) + { + pcMesh->mNormals[iIndexOut] = pMesh->mNormals[iIndex]; + } + + // copy tangents/bitangents + if (pMesh->HasTangentsAndBitangents()) + { + pcMesh->mTangents[iIndexOut] = pMesh->mTangents[iIndex]; + pcMesh->mBitangents[iIndexOut] = pMesh->mBitangents[iIndex]; + } + + // texture coordinates + for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) + { + if (pMesh->HasTextureCoords( c)) + { + pcMesh->mTextureCoords[c][iIndexOut] = pMesh->mTextureCoords[c][iIndex]; + } + } + // vertex colors + for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS;++c) + { + if (pMesh->HasVertexColors( c)) + { + pcMesh->mColors[c][iIndexOut] = pMesh->mColors[c][iIndex]; + } + } + } + } + + // add the newly created mesh to the list + avList.push_back(std::pair(pcMesh,a)); + } + + // now delete the old mesh data + delete pMesh; + } + else avList.push_back(std::pair(pMesh,a)); + return; +} \ No newline at end of file diff --git a/code/SplitLargeMeshes.h b/code/SplitLargeMeshes.h new file mode 100644 index 000000000..f5edf90be --- /dev/null +++ b/code/SplitLargeMeshes.h @@ -0,0 +1,60 @@ +/** @file Defines a post processing step to split large meshes into submeshes*/ +#ifndef AI_SPLITLARGEMESHES_H_INC +#define AI_SPLITLARGEMESHES_H_INC + +#include +#include "BaseProcess.h" +#include "../include/aiMesh.h" +#include "../include/aiScene.h" + +namespace Assimp +{ + +#define AI_SLM_MAX_VERTICES 1000000 + +// --------------------------------------------------------------------------- +/** Postprocessing filter to split large meshes into submeshes +*/ +class SplitLargeMeshesProcess : public BaseProcess +{ + friend class Importer; + +protected: + /** Constructor to be privately used by Importer */ + SplitLargeMeshesProcess(); + + /** Destructor, private as well */ + ~SplitLargeMeshesProcess(); + +public: + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag field. + * @param pFlags The processing flags the importer was called with. A bitwise + * combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + + +private: + + //! Apply the algorithm to a given mesh + void SplitMesh (unsigned int a, aiMesh* pcMesh, + std::vector >& avList); + + //! Update a node in the asset after a few of its meshes + //! have been split + void UpdateNode(aiNode* pcNode, + const std::vector >& avList); +}; + +} // end of namespace Assimp + +#endif // !!AI_SPLITLARGEMESHES_H_INC \ No newline at end of file diff --git a/code/TriangulateProcess.cpp b/code/TriangulateProcess.cpp new file mode 100644 index 000000000..21a146213 --- /dev/null +++ b/code/TriangulateProcess.cpp @@ -0,0 +1,83 @@ +/** @file Implementation of the post processing step to split up + * all faces with more than three indices into triangles. + */ +#include +#include +#include "TriangulateProcess.h" +#include "../include/aiPostProcess.h" +#include "../include/aiMesh.h" +#include "../include/aiScene.h" + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +TriangulateProcess::TriangulateProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +TriangulateProcess::~TriangulateProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool TriangulateProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_Triangulate) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void TriangulateProcess::Execute( aiScene* pScene) +{ + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) + TriangulateMesh( pScene->mMeshes[a]); +} + +// ------------------------------------------------------------------------------------------------ +// Triangulates the given mesh. +void TriangulateProcess::TriangulateMesh( aiMesh* pMesh) +{ + std::vector newFaces; + newFaces.reserve( pMesh->mNumFaces*2); + for( unsigned int a = 0; a < pMesh->mNumFaces; a++) + { + const aiFace& face = pMesh->mFaces[a]; + + // if it's a simple primitive, just copy it + if( face.mNumIndices == 3) + { + newFaces.push_back( aiFace()); + aiFace& nface = newFaces.back(); + nface.mNumIndices = face.mNumIndices; + nface.mIndices = new unsigned int[nface.mNumIndices]; + memcpy( nface.mIndices, face.mIndices, nface.mNumIndices * sizeof( unsigned int)); + } else + { + assert( face.mNumIndices > 3); + for( unsigned int b = 0; b < face.mNumIndices - 2; b++) + { + newFaces.push_back( aiFace()); + aiFace& nface = newFaces.back(); + nface.mNumIndices = 3; + nface.mIndices = new unsigned int[3]; + nface.mIndices[0] = face.mIndices[0]; + nface.mIndices[1] = face.mIndices[b+1]; + nface.mIndices[2] = face.mIndices[b+2]; + } + } + } + + // kill the old faces + delete [] pMesh->mFaces; + // and insert our newly generated faces + pMesh->mNumFaces = newFaces.size(); + pMesh->mFaces = new aiFace[pMesh->mNumFaces]; + for( unsigned int a = 0; a < newFaces.size(); a++) + pMesh->mFaces[a] = newFaces[a]; +} diff --git a/code/TriangulateProcess.h b/code/TriangulateProcess.h new file mode 100644 index 000000000..83424a549 --- /dev/null +++ b/code/TriangulateProcess.h @@ -0,0 +1,54 @@ +/** @file Defines a post processing step to triangulate all faces with more than three vertices.*/ +#ifndef AI_TRIANGULATEPROCESS_H_INC +#define AI_TRIANGULATEPROCESS_H_INC + +#include "BaseProcess.h" + +struct aiMesh; + +namespace Assimp +{ + +// --------------------------------------------------------------------------- +/** The TriangulateProcess splits up all faces with more than three indices + * into triangles. You usually want this to happen because the graphics cards + * need their data as triangles. + */ +class TriangulateProcess : public BaseProcess +{ + friend class Importer; + +protected: + /** Constructor to be privately used by Importer */ + TriangulateProcess(); + + /** Destructor, private as well */ + ~TriangulateProcess(); + +public: + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag field. + * @param pFlags The processing flags the importer was called with. A bitwise + * combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + +protected: + // ------------------------------------------------------------------- + /** Triangulates the given mesh. + * @param pMesh The mesh to triangulate. + */ + void TriangulateMesh( aiMesh* pMesh); +}; + +} // end of namespace Assimp + +#endif // AI_TRIANGULATEPROCESS_H_INC \ No newline at end of file diff --git a/code/XFileHelper.h b/code/XFileHelper.h new file mode 100644 index 000000000..6143eb18a --- /dev/null +++ b/code/XFileHelper.h @@ -0,0 +1,147 @@ +/** @file Defines the helper data structures for importing XFiles */ +#ifndef AI_XFILEHELPER_H_INC +#define AI_XFILEHELPER_H_INC + +#include +#include + +#include "../include/aiTypes.h" +#include "../include/aiQuaternion.h" +#include "../include/aiMesh.h" +#include "../include/aiAnim.h" + +namespace Assimp +{ +namespace XFile +{ + +/** Helper structure representing a XFile mesh face */ +struct Face +{ + std::vector mIndices; +}; + +/** Helper structure representing a XFile material */ +struct Material +{ + std::string mName; + bool mIsReference; // if true, mName holds a name by which the actual material can be found in the material list + aiColor4D mDiffuse; + float mSpecularExponent; + aiColor3D mSpecular; + aiColor3D mEmissive; + std::vector mTextures; + + Material() { mIsReference = false; } +}; + +/** Helper structure to represent a bone weight */ +struct BoneWeight +{ + unsigned int mVertex; + float mWeight; +}; + +/** Helper structure to represent a bone in a mesh */ +struct Bone +{ + std::string mName; + std::vector mWeights; + aiMatrix4x4 mOffsetMatrix; +}; + +/** Helper structure to represent an XFile mesh */ +struct Mesh +{ + std::vector mPositions; + std::vector mPosFaces; + std::vector mNormals; + std::vector mNormFaces; + unsigned int mNumTextures; + std::vector mTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; + unsigned int mNumColorSets; + std::vector mColors[AI_MAX_NUMBER_OF_COLOR_SETS]; + + std::vector mFaceMaterials; + std::vector mMaterials; + + std::vector mBones; + + Mesh() { mNumTextures = 0; mNumColorSets = 0; } +}; + +/** Helper structure to represent a XFile frame */ +struct Node +{ + std::string mName; + aiMatrix4x4 mTrafoMatrix; + Node* mParent; + std::vector mChildren; + std::vector mMeshes; + + Node() { mParent = NULL; } + Node( Node* pParent) { mParent = pParent; } + ~Node() + { + for( unsigned int a = 0; a < mChildren.size(); a++) + delete mChildren[a]; + for( unsigned int a = 0; a < mMeshes.size(); a++) + delete mMeshes[a]; + } +}; + +struct MatrixKey +{ + double mTime; + aiMatrix4x4 mMatrix; +}; + +/** Helper structure representing a single animated bone in a XFile */ +struct AnimBone +{ + std::string mBoneName; + std::vector mPosKeys; // either three separate key sequences for position, rotation, scaling + std::vector mRotKeys; + std::vector mScaleKeys; + std::vector mTrafoKeys; // or a combined key sequence of transformation matrices. +}; + +/** Helper structure to represent an animation set in a XFile */ +struct Animation +{ + std::string mName; + std::vector mAnims; + + ~Animation() + { + for( unsigned int a = 0; a < mAnims.size(); a++) + delete mAnims[a]; + } +}; + +/** Helper structure analogue to aiScene */ +struct Scene +{ + Node* mRootNode; + + std::vector mGlobalMeshes; // global meshes found outside of any frames + std::vector mGlobalMaterials; // global materials found outside of any meshes. + + std::vector mAnims; + unsigned int mAnimTicksPerSecond; + + Scene() { mRootNode = NULL; mAnimTicksPerSecond = 0; } + ~Scene() + { + delete mRootNode; + for( unsigned int a = 0; a < mGlobalMeshes.size(); a++) + delete mGlobalMeshes[a]; + for( unsigned int a = 0; a < mAnims.size(); a++) + delete mAnims[a]; + } +}; + +} // end of namespace XFile +} // end of namespace Assimp + +#endif // AI_XFILEHELPER_H_INC \ No newline at end of file diff --git a/code/XFileImporter.cpp b/code/XFileImporter.cpp new file mode 100644 index 000000000..46a6c925a --- /dev/null +++ b/code/XFileImporter.cpp @@ -0,0 +1,703 @@ +/** @file Implementation of the XFile importer class */ +#include "XFileImporter.h" +#include "XFileParser.h" +#include "MaterialSystem.h" +#include "ConvertToLHProcess.h" + +#include "../include/IOStream.h" +#include "../include/IOSystem.h" +#include "../include/aiMesh.h" +#include "../include/aiScene.h" + +#include +#include + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +XFileImporter::XFileImporter() +{ +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +XFileImporter::~XFileImporter() +{ +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the class can handle the format of the given file. +bool XFileImporter::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); + if( extension == ".x" || extension == ".X") + return true; + + return false; +} + +// ------------------------------------------------------------------------------------------------ +// Imports the given file into the given scene structure. +void XFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) +{ + // read file into memory + boost::scoped_ptr file( pIOHandler->Open( pFile)); + if( file.get() == NULL) + throw new ImportErrorException( "Failed to open file " + pFile + "."); + + size_t fileSize = file->FileSize(); + if( fileSize < 16) + throw new ImportErrorException( "XFile is too small."); + + mBuffer.resize( fileSize); + file->Read( &mBuffer.front(), 1, fileSize); + + // parse the file into a temporary representation + XFileParser parser( mBuffer); + + // and create the proper return structures out of it + CreateDataRepresentationFromImport( pScene, parser.GetImportedData()); +} + +// ------------------------------------------------------------------------------------------------ +// Constructs the return data structure out of the imported data. +void XFileImporter::CreateDataRepresentationFromImport( aiScene* pScene, const XFile::Scene* pData) +{ + // Read the global materials first so that meshes referring to them can find them later + ConvertMaterials( pScene, pData->mGlobalMaterials); + + // copy nodes, extracting meshes and materials on the way + pScene->mRootNode = CreateNodes( pScene, NULL, pData->mRootNode); + + // extract animations + CreateAnimations( pScene, pData); + + // read the global meshes that were stored outside of any node + if( pData->mGlobalMeshes.size() > 0) + { + // create a root node to hold them if there isn't any, yet + if( pScene->mRootNode == NULL) + { + pScene->mRootNode = new aiNode; + pScene->mRootNode->mName.Set( "$dummy_node"); + } + + // convert all global meshes and store them in the root node. + // If there was one before, the global meshes now suddenly have its transformation matrix... + // Don't know what to do there, I don't want to insert another node under the present root node + // just to avoid this. + CreateMeshes( pScene, pScene->mRootNode, pData->mGlobalMeshes); + } + + // convert the root node's transformation to OGL coords + ConvertToLHProcess::ConvertToOGL( pScene->mRootNode->mTransformation); + + // finally: create a dummy material if not material was imported + if( pScene->mNumMaterials == 0) + { + pScene->mNumMaterials = 1; + // create the Material + Assimp::MaterialHelper* mat = new Assimp::MaterialHelper; + int shadeMode = (int) aiShadingMode_Gouraud; + mat->AddProperty( &shadeMode, 1, AI_MATKEY_SHADING_MODEL); + // material colours + int specExp = 1; + mat->AddProperty( &aiColor3D( 0, 0, 0), 1, AI_MATKEY_COLOR_EMISSIVE); + mat->AddProperty( &aiColor3D( 0.5f, 0.5f, 0.5f), 1, AI_MATKEY_COLOR_DIFFUSE); + mat->AddProperty( &aiColor3D( 0, 0, 0), 1, AI_MATKEY_COLOR_SPECULAR); + mat->AddProperty( &specExp, 1, AI_MATKEY_SHININESS); + + pScene->mMaterials = new aiMaterial*[1]; + pScene->mMaterials[0] = mat; + } +} + +// ------------------------------------------------------------------------------------------------ +// Recursively creates scene nodes from the imported hierarchy. +aiNode* XFileImporter::CreateNodes( aiScene* pScene, aiNode* pParent, const XFile::Node* pNode) +{ + if( !pNode) + return NULL; + + // create node + aiNode* node = new aiNode; + node->mName.length = pNode->mName.length(); + node->mParent = pParent; + memcpy( node->mName.data, pNode->mName.c_str(), pNode->mName.length()); + node->mName.data[node->mName.length] = 0; + node->mTransformation = pNode->mTrafoMatrix; + + // convert meshes from the source node + CreateMeshes( pScene, node, pNode->mMeshes); + + // handle childs + if( pNode->mChildren.size() > 0) + { + node->mNumChildren = pNode->mChildren.size(); + node->mChildren = new aiNode* [node->mNumChildren]; + + for( unsigned int a = 0; a < pNode->mChildren.size(); a++) + node->mChildren[a] = CreateNodes( pScene, node, pNode->mChildren[a]); + } + + return node; +} + +// ------------------------------------------------------------------------------------------------ +// Creates the meshes for the given node. +void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vector& pMeshes) +{ + if( pMeshes.size() == 0) + return; + + // create a mesh for each mesh-material combination in the source node + std::vector meshes; + for( unsigned int a = 0; a < pMeshes.size(); a++) + { + const XFile::Mesh* sourceMesh = pMeshes[a]; + // first convert its materials so that we can find them when searching by name afterwards + ConvertMaterials( pScene, sourceMesh->mMaterials); + + unsigned int numMaterials = std::max( sourceMesh->mMaterials.size(), 1u); + for( unsigned int b = 0; b < numMaterials; b++) + { + // collect the faces belonging to this material + std::vector faces; + unsigned int numVertices = 0; + if( sourceMesh->mFaceMaterials.size() > 0) + { + // if there is a per-face material defined, select the faces with the corresponding material + for( unsigned int c = 0; c < sourceMesh->mFaceMaterials.size(); c++) + { + if( sourceMesh->mFaceMaterials[c] == b) + { + faces.push_back( c); + numVertices += sourceMesh->mPosFaces[c].mIndices.size(); + } + } + } else + { + // if there is no per-face material, place everything into one mesh + for( unsigned int c = 0; c < sourceMesh->mPosFaces.size(); c++) + { + faces.push_back( c); + numVertices += sourceMesh->mPosFaces[c].mIndices.size(); + } + } + + // no faces/vertices using this material? strange... + if( numVertices == 0) + continue; + + // create a submesh using this material + aiMesh* mesh = new aiMesh; + meshes.push_back( mesh); + + // find the material by name in the scene's material list. Either own material + // or referenced material, it should already be found there + if( sourceMesh->mFaceMaterials.size() > 0) + { + std::map::const_iterator matIt = mImportedMats.find( sourceMesh->mMaterials[b].mName); + if( matIt == mImportedMats.end()) + mesh->mMaterialIndex = 0; + else + mesh->mMaterialIndex = matIt->second; + } else + { + mesh->mMaterialIndex = 0; + } + + // Create properly sized data arrays in the mesh. We store unique vertices per face, + // as specified + mesh->mNumVertices = numVertices; + mesh->mVertices = new aiVector3D[numVertices]; + mesh->mNumFaces = faces.size(); + mesh->mFaces = new aiFace[mesh->mNumFaces]; + + // normals? + if( sourceMesh->mNormals.size() > 0) + mesh->mNormals = new aiVector3D[numVertices]; + // texture coords + for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS; c++) + { + if( sourceMesh->mTexCoords[c].size() > 0) + mesh->mTextureCoords[c] = new aiVector3D[numVertices]; + } + // vertex colors + for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS; c++) + { + if( sourceMesh->mColors[c].size() > 0) + mesh->mColors[c] = new aiColor4D[numVertices]; + } + + // now collect the vertex data of all data streams present in the imported mesh + unsigned int newIndex = 0; + std::vector orgPoints; // from which original point each new vertex stems + orgPoints.resize( numVertices, 0); + + for( unsigned int c = 0; c < faces.size(); c++) + { + unsigned int f = faces[c]; // index of the source face + const XFile::Face& pf = sourceMesh->mPosFaces[f]; // position source face + + // create face. either triangle or triangle fan depending on the index count + aiFace& df = mesh->mFaces[c]; // destination face + df.mNumIndices = pf.mIndices.size(); + df.mIndices = new unsigned int[ df.mNumIndices]; + + // collect vertex data for indices of this face + for( unsigned int d = 0; d < df.mNumIndices; d++) + { + df.mIndices[df.mNumIndices - 1 - d] = newIndex; // inverted face orientation for OGL + orgPoints[newIndex] = pf.mIndices[d]; + + // Position + mesh->mVertices[newIndex] = sourceMesh->mPositions[pf.mIndices[d]]; + // Normal, if present + if( mesh->HasNormals()) + mesh->mNormals[newIndex] = sourceMesh->mNormals[sourceMesh->mNormFaces[f].mIndices[d]]; + // texture coord sets + for( unsigned int e = 0; e < AI_MAX_NUMBER_OF_TEXTURECOORDS; e++) + { + if( mesh->HasTextureCoords( e)) + { + aiVector2D tex = sourceMesh->mTexCoords[e][pf.mIndices[d]]; + mesh->mTextureCoords[e][newIndex] = aiVector3D( tex.x, 1.0f - tex.y, 0.0f); + } + } + // vertex color sets + for( unsigned int e = 0; e < AI_MAX_NUMBER_OF_COLOR_SETS; e++) + if( mesh->HasVertexColors( e)) + mesh->mColors[e][newIndex] = sourceMesh->mColors[e][pf.mIndices[d]]; + + newIndex++; + } + } + + // there should be as much new vertices as we calculated before + assert( newIndex == numVertices); + + // convert all bones of the source mesh which influence vertices in this newly created mesh + const std::vector& bones = sourceMesh->mBones; + std::vector newBones; + for( unsigned int c = 0; c < bones.size(); c++) + { + const XFile::Bone& obone = bones[c]; + // set up a vertex-linear array of the weights for quick searching if a bone influences a vertex + std::vector oldWeights( sourceMesh->mPositions.size(), 0.0f); + for( unsigned int d = 0; d < obone.mWeights.size(); d++) + oldWeights[obone.mWeights[d].mVertex] = obone.mWeights[d].mWeight; + + // collect all vertex weights that influence a vertex in the new mesh + std::vector newWeights; + newWeights.reserve( numVertices); + for( unsigned int d = 0; d < orgPoints.size(); d++) + { + // does the new vertex stem from an old vertex which was influenced by this bone? + float w = oldWeights[orgPoints[d]]; + if( w > 0.0f) + newWeights.push_back( aiVertexWeight( d, w)); + } + + // if the bone has no weights in the newly created mesh, ignore it + if( newWeights.size() == 0) + continue; + + // create + aiBone* nbone = new aiBone; + newBones.push_back( nbone); + // copy name and matrix + nbone->mName.Set( obone.mName); + nbone->mOffsetMatrix = obone.mOffsetMatrix; + nbone->mNumWeights = newWeights.size(); + nbone->mWeights = new aiVertexWeight[nbone->mNumWeights]; + for( unsigned int d = 0; d < newWeights.size(); d++) + nbone->mWeights[d] = newWeights[d]; + } + + // store the bones in the mesh + mesh->mNumBones = newBones.size(); + mesh->mBones = new aiBone*[mesh->mNumBones]; + for( unsigned int c = 0; c < newBones.size(); c++) + mesh->mBones[c] = newBones[c]; + } + } + + // reallocate scene mesh array to be large enough + aiMesh** prevArray = pScene->mMeshes; + pScene->mMeshes = new aiMesh*[pScene->mNumMeshes + meshes.size()]; + if( prevArray) + { + memcpy( pScene->mMeshes, prevArray, pScene->mNumMeshes * sizeof( aiMesh*)); + delete [] prevArray; + } + + // allocate mesh index array in the node + pNode->mNumMeshes = meshes.size(); + pNode->mMeshes = new unsigned int[pNode->mNumMeshes]; + + // store all meshes in the mesh library of the scene and store their indices in the node + for( unsigned int a = 0; a < meshes.size(); a++) + { + pScene->mMeshes[pScene->mNumMeshes] = meshes[a]; + pNode->mMeshes[a] = pScene->mNumMeshes; + pScene->mNumMeshes++; + } +} + +// ------------------------------------------------------------------------------------------------ +// Converts the animations from the given imported data and creates them in the scene. +void XFileImporter::CreateAnimations( aiScene* pScene, const XFile::Scene* pData) +{ + std::vector newAnims; + + for( unsigned int a = 0; a < pData->mAnims.size(); a++) + { + const XFile::Animation* anim = pData->mAnims[a]; + // create a new animation to hold the data + aiAnimation* nanim = new aiAnimation; + newAnims.push_back( nanim); + nanim->mName.Set( anim->mName); + // duration will be determined by the maximum length + nanim->mDuration = 0; + nanim->mTicksPerSecond = pData->mAnimTicksPerSecond; + nanim->mNumBones = anim->mAnims.size(); + nanim->mBones = new aiBoneAnim*[nanim->mNumBones]; + + for( unsigned int b = 0; b < anim->mAnims.size(); b++) + { + const XFile::AnimBone* bone = anim->mAnims[b]; + aiBoneAnim* nbone = new aiBoneAnim; + nbone->mBoneName.Set( bone->mBoneName); + nanim->mBones[b] = nbone; + + // apply the LH->RH conversion if the animation affects the root bone + bool isRootAnim = (bone->mBoneName == pScene->mRootNode->mName.data); + + // keyframes are given as combined transformation matrix keys + if( bone->mTrafoKeys.size() > 0) + { + nbone->mNumPositionKeys = bone->mTrafoKeys.size(); + nbone->mPositionKeys = new aiVectorKey[nbone->mNumPositionKeys]; + nbone->mNumRotationKeys = bone->mTrafoKeys.size(); + nbone->mRotationKeys = new aiQuatKey[nbone->mNumRotationKeys]; + nbone->mNumScalingKeys = bone->mTrafoKeys.size(); + nbone->mScalingKeys = new aiVectorKey[nbone->mNumScalingKeys]; + + for( unsigned int c = 0; c < bone->mTrafoKeys.size(); c++) + { + // deconstruct each matrix into separate position, rotation and scaling + double time = bone->mTrafoKeys[c].mTime; + aiMatrix4x4 trafo = bone->mTrafoKeys[c].mMatrix; + + // extract position + aiVector3D pos( trafo.a4, trafo.b4, trafo.c4); + if( isRootAnim) + ConvertToLHProcess::ConvertToOGL( pos); + + nbone->mPositionKeys[c].mTime = time; + nbone->mPositionKeys[c].mValue = pos; + + // extract scaling + aiVector3D scale; + scale.x = aiVector3D( trafo.a1, trafo.b1, trafo.c1).Length(); + scale.y = aiVector3D( trafo.a2, trafo.b2, trafo.c2).Length(); + scale.z = aiVector3D( trafo.a3, trafo.b3, trafo.c3).Length(); + nbone->mScalingKeys[c].mTime = time; + nbone->mScalingKeys[c].mValue = scale; + + // reconstruct rotation matrix without scaling + aiMatrix3x3 rotmat( + trafo.a1 / scale.x, trafo.a2 / scale.y, trafo.a3 / scale.z, + trafo.b1 / scale.x, trafo.b2 / scale.y, trafo.b3 / scale.z, + trafo.c1 / scale.x, trafo.c2 / scale.y, trafo.c3 / scale.z); + + if( isRootAnim) + ConvertToLHProcess::ConvertToOGL( rotmat); + + // and convert it into a quaternion + nbone->mRotationKeys[c].mTime = time; + nbone->mRotationKeys[c].mValue = aiQuaternion( rotmat); + } + + // longest lasting key sequence determines duration + nanim->mDuration = std::max( nanim->mDuration, bone->mTrafoKeys.back().mTime); + } else + { + // separate key sequences for position, rotation, scaling + nbone->mNumPositionKeys = bone->mPosKeys.size(); + nbone->mPositionKeys = new aiVectorKey[nbone->mNumPositionKeys]; + for( unsigned int c = 0; c < nbone->mNumPositionKeys; c++) + { + aiVector3D pos = bone->mPosKeys[c].mValue; + if( isRootAnim) + ConvertToLHProcess::ConvertToOGL( pos); + + nbone->mPositionKeys[c].mTime = bone->mPosKeys[c].mTime; + nbone->mPositionKeys[c].mValue = pos; + } + + // rotation + nbone->mNumRotationKeys = bone->mRotKeys.size(); + nbone->mRotationKeys = new aiQuatKey[nbone->mNumRotationKeys]; + for( unsigned int c = 0; c < nbone->mNumRotationKeys; c++) + { + aiMatrix3x3 rotmat = bone->mRotKeys[c].mValue.GetMatrix(); + if( isRootAnim) + ConvertToLHProcess::ConvertToOGL( rotmat); + + nbone->mRotationKeys[c].mTime = bone->mRotKeys[c].mTime; + nbone->mRotationKeys[c].mValue = aiQuaternion( rotmat); + } + + // scaling + nbone->mNumScalingKeys = bone->mScaleKeys.size(); + nbone->mScalingKeys = new aiVectorKey[nbone->mNumScalingKeys]; + for( unsigned int c = 0; c < nbone->mNumScalingKeys; c++) + nbone->mScalingKeys[c] = bone->mScaleKeys[c]; + + // longest lasting key sequence determines duration + if( bone->mPosKeys.size() > 0) + nanim->mDuration = std::max( nanim->mDuration, bone->mPosKeys.back().mTime); + if( bone->mRotKeys.size() > 0) + nanim->mDuration = std::max( nanim->mDuration, bone->mRotKeys.back().mTime); + if( bone->mScaleKeys.size() > 0) + nanim->mDuration = std::max( nanim->mDuration, bone->mScaleKeys.back().mTime); + } + } + } + + // store all converted animations in the scene + if( newAnims.size() > 0) + { + pScene->mNumAnimations = newAnims.size(); + pScene->mAnimations = new aiAnimation* [pScene->mNumAnimations]; + for( unsigned int a = 0; a < newAnims.size(); a++) + pScene->mAnimations[a] = newAnims[a]; + } +} + +// ------------------------------------------------------------------------------------------------ +// Converts all materials in the given array and stores them in the scene's material list. +void XFileImporter::ConvertMaterials( aiScene* pScene, const std::vector& pMaterials) +{ + // count the non-referrer materials in the array + unsigned int numMaterials = 0; + for( unsigned int a = 0; a < pMaterials.size(); a++) + if( !pMaterials[a].mIsReference) + numMaterials++; + + if( numMaterials == 0) + return; + + // resize the scene's material list to offer enough space for the new materials + aiMaterial** prevMats = pScene->mMaterials; + pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials + numMaterials]; + if( prevMats) + { + memcpy( pScene->mMaterials, prevMats, pScene->mNumMaterials * sizeof( aiMaterial*)); + delete [] prevMats; + } + + // convert all the materials given in the array + for( unsigned int a = 0; a < pMaterials.size(); a++) + { + const XFile::Material& oldMat = pMaterials[a]; + if( oldMat.mIsReference) + continue; + + Assimp::MaterialHelper* mat = new Assimp::MaterialHelper; + aiString name; + name.Set( oldMat.mName); + mat->AddProperty( &name, AI_MATKEY_NAME); + // Shading model: hardcoded to PHONG, there is no such information in an XFile + int shadeMode = (int) aiShadingMode_Phong; + mat->AddProperty( &shadeMode, 1, AI_MATKEY_SHADING_MODEL); + // material colours + mat->AddProperty( &oldMat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE); + mat->AddProperty( &oldMat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); + mat->AddProperty( &oldMat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR); + mat->AddProperty( &oldMat.mSpecularExponent, 1, AI_MATKEY_SHININESS); + + + // texture, if there is one + if (1 == oldMat.mTextures.size()) + { + // if there is only one texture, assume it contains the + // diffuse color + aiString tex; + tex.Set( oldMat.mTextures[0]); + + mat->AddProperty( &tex, AI_MATKEY_TEXTURE_DIFFUSE(0)); + } + else + { + // Otherwise ... try to search for typical strings in the + // texture's file name like 'bump' or 'diffuse' + unsigned int iHM = 0,iNM = 0,iDM = 0,iSM = 0,iAM = 0,iEM = 0; + for( unsigned int b = 0; b < oldMat.mTextures.size(); b++) + { + std::string sz = oldMat.mTextures[b]; + char key[256]; + + // find the file name + const size_t iLen = sz.length(); + std::string::size_type s = sz.rfind('\\',iLen-1); + if (std::string::npos == s) + { + s = sz.rfind('/',iLen-1); + if (std::string::npos == s)s = 0; + } + + // cut off the file extension + std::string::size_type sExt = sz.rfind('.',iLen-1); + if (std::string::npos != sExt) + { + sz[sExt] = '\0'; + } + + // bump map + std::string::size_type s2 = sz.find("bump",s); + if (std::string::npos == s2) + { + s2 = sz.find("BUMP",s); + if (std::string::npos == s2) + { + s2 = sz.find("Bump",s); + if (std::string::npos == s2) + { + s2 = sz.find("height",s); + if (std::string::npos == s2) + { + s2 = sz.find("HEIGHT",s); + if (std::string::npos == s2) + { + s2 = sz.find("Height",s); + } + } + } + } + } + if (std::string::npos != s2) + { + sprintf(key,AI_MATKEY_TEXTURE_BUMP_ "[%i]",iHM++); + } + else + { + // Normal map + std::string::size_type s2 = sz.find("normal",s); + if (std::string::npos == s2) + { + s2 = sz.find("NORMAL",s); + if (std::string::npos == s2) + { + s2 = sz.find("nm",s); // not really unique + if (std::string::npos == s2) + { + s2 = sz.find("Normal",s); + if (std::string::npos == s2) + { + s2 = sz.find("NM",s); + } + } + } + } + if (std::string::npos != s2) + { + sprintf(key,AI_MATKEY_TEXTURE_NORMALS_ "[%i]",iNM++); + } + else + { + + // specular color texture (not unique, too. Could + // also be the material's shininess) + std::string::size_type s2 = sz.find("spec",s); + if (std::string::npos == s2) + { + s2 = sz.find("Spec",s); + if (std::string::npos == s2) + { + s2 = sz.find("SPEC",s); + if (std::string::npos == s2) + { + s2 = sz.find("Glanz",s); + if (std::string::npos == s2) + { + s2 = sz.find("glanz",s); + } + } + } + } + if (std::string::npos != s2) + { + sprintf(key,AI_MATKEY_TEXTURE_SPECULAR_ "[%i]",iSM++); + } + else + { + // ambient color texture + std::string::size_type s2 = sz.find("ambi",s); + if (std::string::npos == s2) + { + s2 = sz.find("AMBI",s); + if (std::string::npos == s2) + { + s2 = sz.find("umgebungsfarbe",s); + if (std::string::npos == s2) + { + s2 = sz.find("Ambi",s); + } + } + } + if (std::string::npos != s2) + { + sprintf(key,AI_MATKEY_TEXTURE_AMBIENT_ "[%i]",iAM++); + } + else + { + // emissive color texture + std::string::size_type s2 = sz.find("emissive",s); + if (std::string::npos == s2) + { + s2 = sz.find("EMISSIVE",s); + if (std::string::npos == s2) + { + // self illumination + s2 = sz.find("self",s); + if (std::string::npos == s2) + { + s2 = sz.find("Emissive",s); + } + } + } + if (std::string::npos != s2) + { + sprintf(key,AI_MATKEY_TEXTURE_EMISSIVE_ "[%i]",iEM++); + } + else + { + // assume it is a diffuse texture + sprintf(key,AI_MATKEY_TEXTURE_DIFFUSE_ "[%i]",iDM++); + } + } + } + } + } + + aiString tex; + tex.Set( oldMat.mTextures[b] ); + + mat->AddProperty( &tex, key); + } + + } + pScene->mMaterials[pScene->mNumMaterials] = mat; + mImportedMats[oldMat.mName] = pScene->mNumMaterials; + pScene->mNumMaterials++; + } +} diff --git a/code/XFileImporter.h b/code/XFileImporter.h new file mode 100644 index 000000000..d89b0786e --- /dev/null +++ b/code/XFileImporter.h @@ -0,0 +1,97 @@ +/** @file Definition of the XFile importer class. */ +#ifndef AI_XFILEIMPORTER_H_INC +#define AI_XFILEIMPORTER_H_INC + +#include + +#include "XFileHelper.h" +#include "BaseImporter.h" + +#include "../include/aiTypes.h" + +struct aiNode; + +namespace Assimp +{ +struct XFile::Scene; +struct XFile::Node; + +// --------------------------------------------------------------------------- +/** The XFileImporter is a worker class capable of importing a scene from a + * DirectX file .x + */ +class XFileImporter : public BaseImporter +{ + friend class Importer; + +protected: + /** Constructor to be privately used by Importer */ + XFileImporter(); + + /** Destructor, private as well */ + ~XFileImporter(); + +public: + // ------------------------------------------------------------------- + /** Returns whether the class can handle the format of the given file. + * See BaseImporter::CanRead() for details. */ + bool CanRead( const std::string& pFile, IOSystem* pIOHandler) const; + +protected: + // ------------------------------------------------------------------- + /** Imports the given file into the given scene structure. + * See BaseImporter::InternReadFile() for details + */ + void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); + + // ------------------------------------------------------------------- + /** Constructs the return data structure out of the imported data. + * @param pScene The scene to construct the return data in. + * @param pData The imported data in the internal temporary representation. + */ + void CreateDataRepresentationFromImport( aiScene* pScene, const XFile::Scene* pData); + + // ------------------------------------------------------------------- + /** Recursively creates scene nodes from the imported hierarchy. The meshes and materials + * of the nodes will be extracted on the way. + * @param pScene The scene to construct the return data in. + * @param pParent The parent node where to create new child nodes + * @param pNode The temporary node to copy. + * @return The created node + */ + aiNode* CreateNodes( aiScene* pScene, aiNode* pParent, const XFile::Node* pNode); + + // ------------------------------------------------------------------- + /** Converts all meshes in the given mesh array. Each mesh is splitted up per material, + * the indices of the generated meshes are stored in the node structure. + * @param pScene The scene to construct the return data in. + * @param pNode The target node structure that references the constructed meshes. + * @param pMeshes The array of meshes to convert + */ + void CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vector& pMeshes); + + // ------------------------------------------------------------------- + /** Converts the animations from the given imported data and creates them in the scene. + * @param pScene The scene to hold to converted animations + * @param pData The data to read the animations from + */ + void CreateAnimations( aiScene* pScene, const XFile::Scene* pData); + + // ------------------------------------------------------------------- + /** Converts all materials in the given array and stores them in the scene's material list. + * @param pScene The scene to hold the converted materials. + * @param pMaterials The material array to convert. + */ + void ConvertMaterials( aiScene* pScene, const std::vector& pMaterials); + +protected: + /** Buffer to hold the loaded file */ + std::vector mBuffer; + + /** Imported materials: index in the scene's material list by name */ + std::map mImportedMats; +}; + +} // end of namespace Assimp + +#endif // AI_BASEIMPORTER_H_INC \ No newline at end of file diff --git a/code/XFileParser.cpp b/code/XFileParser.cpp new file mode 100644 index 000000000..001b4f481 --- /dev/null +++ b/code/XFileParser.cpp @@ -0,0 +1,1182 @@ +/** @file Implementation of the XFile parser helper class */ +#include "XFileParser.h" +#include "XFileHelper.h" +#include "BaseImporter.h" +#include "fast_atof.h" + +#include + +using namespace Assimp; +using namespace Assimp::XFile; + +// ------------------------------------------------------------------------------------------------ +// Constructor. Creates a data structure out of the XFile given in the memory block. +XFileParser::XFileParser( const std::vector& pBuffer) +{ + mMajorVersion = mMinorVersion = 0; + mIsBinaryFormat = false; + mBinaryNumCount = 0; + P = End = NULL; + mLineNumber = 0; + mScene = NULL; + + // set up memory pointers + P = &pBuffer.front(); + End = P + pBuffer.size(); + + // check header + if( strncmp( P, "xof ", 4) != 0) + throw new ImportErrorException( "Header mismatch, file is not an XFile."); + + // read version. It comes in a four byte format such as "0302" + mMajorVersion = (unsigned int)(P[4] - 48) * 10 + (unsigned int)(P[5] - 48); + mMinorVersion = (unsigned int)(P[6] - 48) * 10 + (unsigned int)(P[7] - 48); + + // read format + if( strncmp( P + 8, "txt ", 4) == 0) + mIsBinaryFormat = false; + else if( strncmp( P + 8, "bin ", 4) == 0) + mIsBinaryFormat = true; + else + ThrowException( "Unsupported xfile format"); + + // float size + mBinaryFloatSize = (unsigned int)(P[12] - 48) * 1000 + + (unsigned int)(P[13] - 48) * 100 + + (unsigned int)(P[14] - 48) * 10 + + (unsigned int)(P[15] - 48); + + if( mBinaryFloatSize != 32 && mBinaryFloatSize != 64) + ThrowException( boost::str( boost::format( "Unknown float size %1% specified in xfile header.") % mBinaryFloatSize)); + + // start reading here + P += 16; + ReadUntilEndOfLine(); + + mScene = new Scene; + ParseFile(); + + // filter the imported hierarchy for some degenerated cases + if( mScene->mRootNode) + FilterHierarchy( mScene->mRootNode); +} + +// ------------------------------------------------------------------------------------------------ +// Destructor. Destroys all imported data along with it +XFileParser::~XFileParser() +{ + // kill everything we created + delete mScene; +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::ParseFile() +{ + while( 1) + { + // read name of next object + std::string objectName = GetNextToken(); + if (objectName.length() == 0) + break; + + // parse specific object + if( objectName == "template") + ParseDataObjectTemplate(); + else + if( objectName == "Frame") + ParseDataObjectFrame( NULL); + else + if( objectName == "Mesh") + { + // some meshes have no frames at all + Mesh* mesh = new Mesh; + ParseDataObjectMesh( mesh); + mScene->mGlobalMeshes.push_back( mesh); + } else + if( objectName == "AnimTicksPerSecond") + ParseDataObjectAnimTicksPerSecond(); + else + if( objectName == "AnimationSet") + ParseDataObjectAnimationSet(); + else + if( objectName == "Material") + { + // Material outside of a mesh or node + Material material; + ParseDataObjectMaterial( &material); + mScene->mGlobalMaterials.push_back( material); + } else + if( objectName == "}") + { + // whatever? + // os::Printer::log("} found in dataObject", ELL_WARNING); + } else + { + // unknown format + //os::Printer::log("Unknown data object in animation of .x file", objectName.c_str(), ELL_WARNING); + ParseUnknownDataObject(); + } + } +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::ParseDataObjectTemplate() +{ + // parse a template data object. Currently not stored. + std::string name; + readHeadOfDataObject( &name); + + // read GUID + std::string guid = GetNextToken(); + + // read and ignore data members + while(true) + { + std::string s = GetNextToken(); + + if( s == "}") + break; + + if( s.length() == 0) + ThrowException( "Unexpected end of file reached while parsing template definition"); + } +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::ParseDataObjectFrame( Node* pParent) +{ + // A coordinate frame, or "frame of reference." The Frame template + // is open and can contain any object. The Direct3D extensions (D3DX) + // mesh-loading functions recognize Mesh, FrameTransformMatrix, and + // Frame template instances as child objects when loading a Frame + // instance. + std::string name; + readHeadOfDataObject(&name); + + // create a named node and place it at its parent, if given + Node* node = new Node( pParent); + node->mName = name; + if( pParent) + { + pParent->mChildren.push_back( node); + } else + { + // there might be multiple root nodes + if( mScene->mRootNode != NULL) + { + // place a dummy root if not there + if( mScene->mRootNode->mName != "$dummy_root") + { + Node* exroot = mScene->mRootNode; + mScene->mRootNode = new Node( NULL); + mScene->mRootNode->mName = "$dummy_root"; + mScene->mRootNode->mChildren.push_back( exroot); + exroot->mParent = mScene->mRootNode; + } + // put the new node as its child instead + mScene->mRootNode->mChildren.push_back( node); + node->mParent = mScene->mRootNode; + } else + { + // it's the first node imported. place it as root + mScene->mRootNode = node; + } + } + + // Now inside a frame. + // read tokens until closing brace is reached. + while(true) + { + std::string objectName = GetNextToken(); + if (objectName.size() == 0) + ThrowException( "Unexpected end of file reached while parsing frame"); + + if( objectName == "}") + break; // frame finished + else + if( objectName == "Frame") + ParseDataObjectFrame( node); // child frame + else + if( objectName == "FrameTransformMatrix") + ParseDataObjectTransformationMatrix( node->mTrafoMatrix); + else + if( objectName == "Mesh") + { + Mesh* mesh = new Mesh; + node->mMeshes.push_back( mesh); + ParseDataObjectMesh( mesh); + } else + { + // os::Printer::log("Unknown data object in frame in x file", objectName.c_str(), ELL_WARNING); + ParseUnknownDataObject(); + } + } +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::ParseDataObjectTransformationMatrix( aiMatrix4x4& pMatrix) +{ + // read header, we're not interested if it has a name + readHeadOfDataObject(); + + // read its components + pMatrix.a1 = ReadFloat(); pMatrix.b1 = ReadFloat(); + pMatrix.c1 = ReadFloat(); pMatrix.d1 = ReadFloat(); + pMatrix.a2 = ReadFloat(); pMatrix.b2 = ReadFloat(); + pMatrix.c2 = ReadFloat(); pMatrix.d2 = ReadFloat(); + pMatrix.a3 = ReadFloat(); pMatrix.b3 = ReadFloat(); + pMatrix.c3 = ReadFloat(); pMatrix.d3 = ReadFloat(); + pMatrix.a4 = ReadFloat(); pMatrix.b4 = ReadFloat(); + pMatrix.c4 = ReadFloat(); pMatrix.d4 = ReadFloat(); + + // trailing symbols + CheckForSemicolon(); + CheckForClosingBrace(); +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::ParseDataObjectMesh( Mesh* pMesh) +{ + std::string name; + readHeadOfDataObject( &name); + + // read vertex count + unsigned int numVertices = ReadInt(); + pMesh->mPositions.resize( numVertices); + + // read vertices + for( unsigned int a = 0; a < numVertices; a++) + pMesh->mPositions[a] = ReadVector3(); + + // read position faces + unsigned int numPosFaces = ReadInt(); + pMesh->mPosFaces.resize( numPosFaces); + for( unsigned int a = 0; a < numPosFaces; a++) + { + unsigned int numIndices = ReadInt(); + if( numIndices < 3) + ThrowException( boost::str( boost::format( "Invalid index count %1% for face %2%.") % numIndices % a)); + + // read indices + Face& face = pMesh->mPosFaces[a]; + for( unsigned int b = 0; b < numIndices; b++) + face.mIndices.push_back( ReadInt()); + CheckForSeparator(); + } + + // here, other data objects may follow + while(true) + { + std::string objectName = GetNextToken(); + + if( objectName.size() == 0) + ThrowException( "Unexpected end of file while parsing mesh structure"); + else + if( objectName == "}") + break; // mesh finished + else + if( objectName == "MeshNormals") + ParseDataObjectMeshNormals( pMesh); + else + if( objectName == "MeshTextureCoords") + ParseDataObjectMeshTextureCoords( pMesh); + else + if( objectName == "MeshVertexColors") + ParseDataObjectMeshVertexColors( pMesh); + else + if( objectName == "MeshMaterialList") + ParseDataObjectMeshMaterialList( pMesh); + else + if( objectName == "VertexDuplicationIndices") + ParseUnknownDataObject(); // we'll ignore vertex duplication indices + else + if( objectName == "XSkinMeshHeader") + ParseDataObjectSkinMeshHeader( pMesh); + else + if( objectName == "SkinWeights") + ParseDataObjectSkinWeights( pMesh); + else + { + //os::Printer::log("Unknown data object in mesh in x file", objectName.c_str(), ELL_WARNING); + ParseUnknownDataObject(); + } + } +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::ParseDataObjectSkinWeights( Mesh *pMesh) +{ + readHeadOfDataObject(); + + std::string transformNodeName; + GetNextTokenAsString( transformNodeName); + + pMesh->mBones.push_back( Bone()); + Bone& bone = pMesh->mBones.back(); + bone.mName = transformNodeName; + + // read vertex weights + unsigned int numWeights = ReadInt(); + bone.mWeights.reserve( numWeights); + + for( unsigned int a = 0; a < numWeights; a++) + { + BoneWeight weight; + weight.mVertex = ReadInt(); + bone.mWeights.push_back( weight); + } + + // read vertex weights + for( unsigned int a = 0; a < numWeights; a++) + bone.mWeights[a].mWeight = ReadFloat(); + + // read matrix offset + bone.mOffsetMatrix.a1 = ReadFloat(); bone.mOffsetMatrix.b1 = ReadFloat(); + bone.mOffsetMatrix.c1 = ReadFloat(); bone.mOffsetMatrix.d1 = ReadFloat(); + bone.mOffsetMatrix.a2 = ReadFloat(); bone.mOffsetMatrix.b2 = ReadFloat(); + bone.mOffsetMatrix.c2 = ReadFloat(); bone.mOffsetMatrix.d2 = ReadFloat(); + bone.mOffsetMatrix.a3 = ReadFloat(); bone.mOffsetMatrix.b3 = ReadFloat(); + bone.mOffsetMatrix.c3 = ReadFloat(); bone.mOffsetMatrix.d3 = ReadFloat(); + bone.mOffsetMatrix.a4 = ReadFloat(); bone.mOffsetMatrix.b4 = ReadFloat(); + bone.mOffsetMatrix.c4 = ReadFloat(); bone.mOffsetMatrix.d4 = ReadFloat(); + + CheckForSemicolon(); + CheckForClosingBrace(); +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::ParseDataObjectSkinMeshHeader( Mesh* pMesh) +{ + readHeadOfDataObject(); + + unsigned int maxSkinWeightsPerVertex = ReadInt(); + unsigned int maxSkinWeightsPerFace = ReadInt(); + unsigned int numBonesInMesh = ReadInt(); + + CheckForClosingBrace(); +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::ParseDataObjectMeshNormals( Mesh* pMesh) +{ + readHeadOfDataObject(); + + // read count + unsigned int numNormals = ReadInt(); + pMesh->mNormals.resize( numNormals); + + // read normal vectors + for( unsigned int a = 0; a < numNormals; a++) + pMesh->mNormals[a] = ReadVector3(); + + // read normal indices + unsigned int numFaces = ReadInt(); + if( numFaces != pMesh->mPosFaces.size()) + ThrowException( "Normal face count does not match vertex face count."); + + for( unsigned int a = 0; a < numFaces; a++) + { + unsigned int numIndices = ReadInt(); + pMesh->mNormFaces.push_back( Face()); + Face& face = pMesh->mNormFaces.back(); + + for( unsigned int b = 0; b < numIndices; b++) + face.mIndices.push_back( ReadInt()); + + CheckForSeparator(); + } + + CheckForClosingBrace(); +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::ParseDataObjectMeshTextureCoords( Mesh* pMesh) +{ + readHeadOfDataObject(); + std::vector& coords = pMesh->mTexCoords[pMesh->mNumTextures++]; + + unsigned int numCoords = ReadInt(); + if( numCoords != pMesh->mPositions.size()) + ThrowException( "Texture coord count does not match vertex count"); + + coords.resize( numCoords); + for( unsigned int a = 0; a < numCoords; a++) + coords[a] = ReadVector2(); + + CheckForClosingBrace(); +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::ParseDataObjectMeshVertexColors( Mesh* pMesh) +{ + readHeadOfDataObject(); + std::vector& colors = pMesh->mColors[pMesh->mNumColorSets++]; + + unsigned int numColors = ReadInt(); + if( numColors != pMesh->mPositions.size()) + ThrowException( "Vertex color count does not match vertex count"); + + colors.resize( numColors, aiColor4D( 0, 0, 0, 1)); + for( unsigned int a = 0; a < numColors; a++) + { + unsigned int index = ReadInt(); + if( index >= pMesh->mPositions.size()) + ThrowException( "Vertex color index out of bounds"); + + colors[index] = ReadRGBA(); + } + + CheckForSemicolon(); + CheckForClosingBrace(); +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::ParseDataObjectMeshMaterialList( Mesh* pMesh) +{ + readHeadOfDataObject(); + + // read material count + unsigned int numMaterials = ReadInt(); + // read non triangulated face material index count + unsigned int numMatIndices = ReadInt(); + + // some models have a material index count of 1... to be able to read them we + // replicate this single material index on every face + if( numMatIndices != pMesh->mPosFaces.size() && numMatIndices != 1) + ThrowException( "Per-Face material index count does not match face count."); + + // read per-face material indices + for( unsigned int a = 0; a < numMatIndices; a++) + pMesh->mFaceMaterials.push_back( ReadInt()); + + // in version 03.02, the face indices end with two semicolons. + // commented out version check, as version 03.03 exported from blender also has 2 semicolons + if( !mIsBinaryFormat) // && MajorVersion == 3 && MinorVersion <= 2) + { + if( *P == ';') + ++P; + } + + // if there was only a single material index, replicate it on all faces + while( pMesh->mFaceMaterials.size() < pMesh->mPosFaces.size()) + pMesh->mFaceMaterials.push_back( pMesh->mFaceMaterials.front()); + + // read following data objects + while(true) + { + std::string objectName = GetNextToken(); + if( objectName.size() == 0) + ThrowException( "Unexpected end of file while parsing mesh material list."); + else + if( objectName == "}") + break; // material list finished + else + if( objectName == "{") + { + // template materials + std::string matName = GetNextToken(); + Material material; + material.mIsReference = true; + material.mName = matName; + pMesh->mMaterials.push_back( material); + + CheckForClosingBrace(); // skip } + } else + if( objectName == "Material") + { + pMesh->mMaterials.push_back( Material()); + ParseDataObjectMaterial( &pMesh->mMaterials.back()); + } else + if( objectName == ";") + { + // ignore + } else + { + // os::Printer::log("Unknown data object in material list in x file", objectName.c_str(), ELL_WARNING); + ParseUnknownDataObject(); + } + } +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::ParseDataObjectMaterial( Material* pMaterial) +{ + std::string matName; + readHeadOfDataObject( &matName); + pMaterial->mName = matName; + pMaterial->mIsReference = false; + + // read material values + pMaterial->mDiffuse = ReadRGBA(); + pMaterial->mSpecularExponent = ReadFloat(); + pMaterial->mSpecular = ReadRGB(); + pMaterial->mEmissive = ReadRGB(); + + // read other data objects + while(true) + { + std::string objectName = GetNextToken(); + if( objectName.size() == 0) + ThrowException( "Unexpected end of file while parsing mesh material"); + else + if( objectName == "}") + break; // material finished + else + if( objectName == "TextureFilename" || objectName == "TextureFileName") + { + // some exporters write "TextureFileName" instead. + std::string texname; + ParseDataObjectTextureFilename( texname); + pMaterial->mTextures.push_back( texname); + } else + { + // os::Printer::log("Unknown data object in material in .x file", objectName.c_str(), ELL_WARNING); + ParseUnknownDataObject(); + } + } +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::ParseDataObjectAnimTicksPerSecond() +{ + readHeadOfDataObject(); + mScene->mAnimTicksPerSecond = ReadInt(); + CheckForClosingBrace(); +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::ParseDataObjectAnimationSet() +{ + std::string animName; + readHeadOfDataObject( &animName); + + Animation* anim = new Animation; + mScene->mAnims.push_back( anim); + anim->mName = animName; + + while(true) + { + std::string objectName = GetNextToken(); + if( objectName.length() == 0) + ThrowException( "Unexpected end of file while parsing animation set."); + else + if( objectName == "}") + break; // animation set finished + else + if( objectName == "Animation") + ParseDataObjectAnimation( anim); + else + { + // os::Printer::log("Unknown data object in animation set in x file", objectName.c_str(), ELL_WARNING); + ParseUnknownDataObject(); + } + } +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::ParseDataObjectAnimation( Animation* pAnim) +{ + readHeadOfDataObject(); + AnimBone* banim = new AnimBone; + pAnim->mAnims.push_back( banim); + + while(true) + { + std::string objectName = GetNextToken(); + + if( objectName.length() == 0) + ThrowException( "Unexpected end of file while parsing animation."); + else + if( objectName == "}") + break; // animation finished + else + if( objectName == "AnimationKey") + ParseDataObjectAnimationKey( banim); + else + if( objectName == "AnimationOptions") + ParseUnknownDataObject(); // not interested + else + if( objectName == "{") + { + // read frame name + banim->mBoneName = GetNextToken(); + CheckForClosingBrace(); + } else + { + //os::Printer::log("Unknown data object in animation in x file", objectName.c_str(), ELL_WARNING); + ParseUnknownDataObject(); + } + } +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::ParseDataObjectAnimationKey( AnimBone* pAnimBone) +{ + readHeadOfDataObject(); + + // read key type + unsigned int keyType = ReadInt(); + + // read number of keys + unsigned int numKeys = ReadInt(); + + for( unsigned int a = 0; a < numKeys; a++) + { + // read time + unsigned int time = ReadInt(); + + // read keys + switch( keyType) + { + case 0: // rotation quaternion + { + // read count + if( ReadInt() != 4) + ThrowException( "Invalid number of arguments for quaternion key in animation"); + + aiQuatKey key; + key.mTime = double( time); + key.mValue.w = ReadFloat(); + key.mValue.x = ReadFloat(); + key.mValue.y = ReadFloat(); + key.mValue.z = ReadFloat(); + pAnimBone->mRotKeys.push_back( key); + + CheckForSemicolon(); + break; + } + + case 1: // scale vector + case 2: // position vector + { + // read count + if( ReadInt() != 3) + ThrowException( "Invalid number of arguments for vector key in animation"); + + aiVectorKey key; + key.mTime = double( time); + key.mValue = ReadVector3(); + + if( keyType == 2) + pAnimBone->mPosKeys.push_back( key); + else + pAnimBone->mScaleKeys.push_back( key); + + break; + } + + case 3: // combined transformation matrix + case 4: // denoted both as 3 or as 4 + { + // read count + if( ReadInt() != 16) + ThrowException( "Invalid number of arguments for matrix key in animation"); + + // read matrix + MatrixKey key; + key.mTime = double( time); + key.mMatrix.a1 = ReadFloat(); key.mMatrix.b1 = ReadFloat(); + key.mMatrix.c1 = ReadFloat(); key.mMatrix.d1 = ReadFloat(); + key.mMatrix.a2 = ReadFloat(); key.mMatrix.b2 = ReadFloat(); + key.mMatrix.c2 = ReadFloat(); key.mMatrix.d2 = ReadFloat(); + key.mMatrix.a3 = ReadFloat(); key.mMatrix.b3 = ReadFloat(); + key.mMatrix.c3 = ReadFloat(); key.mMatrix.d3 = ReadFloat(); + key.mMatrix.a4 = ReadFloat(); key.mMatrix.b4 = ReadFloat(); + key.mMatrix.c4 = ReadFloat(); key.mMatrix.d4 = ReadFloat(); + pAnimBone->mTrafoKeys.push_back( key); + + CheckForSemicolon(); + break; + } + + default: + ThrowException( boost::str( boost::format( "Unknown key type %1% in animation.") % keyType)); + break; + } // end switch + + // key separator + CheckForSeparator(); + } + + CheckForClosingBrace(); +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::ParseDataObjectTextureFilename( std::string& pName) +{ + readHeadOfDataObject(); + GetNextTokenAsString( pName); + CheckForClosingBrace(); +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::ParseUnknownDataObject() +{ + // find opening delimiter + while( true) + { + std::string t = GetNextToken(); + if( t.length() == 0) + ThrowException( "Unexpected end of file while parsing unknown segment."); + + if( t == "{") + break; + } + + unsigned int counter = 1; + + // parse until closing delimiter + while( counter > 0) + { + std::string t = GetNextToken(); + + if( t.length() == 0) + ThrowException( "Unexpected end of file while parsing unknown segment."); + + if( t == "{") + ++counter; + else + if( t == "}") + --counter; + } +} + +// ------------------------------------------------------------------------------------------------ +//! checks for closing curly brace +void XFileParser::CheckForClosingBrace() +{ + if( GetNextToken() != "}") + ThrowException( "Closing brace expected."); +} + +// ------------------------------------------------------------------------------------------------ +//! checks for one following semicolon +void XFileParser::CheckForSemicolon() +{ + if( mIsBinaryFormat) + return; + + if( GetNextToken() != ";") + ThrowException( "Semicolon expected."); +} + +// ------------------------------------------------------------------------------------------------ +//! checks for a separator char, either a ',' or a ';' +void XFileParser::CheckForSeparator() +{ + if( mIsBinaryFormat) + return; + + std::string token = GetNextToken(); + if( token != "," && token != ";") + ThrowException( "Separator character (';' or ',') expected."); +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::readHeadOfDataObject( std::string* poName) +{ + std::string nameOrBrace = GetNextToken(); + if( nameOrBrace != "{") + { + if( poName) + *poName = nameOrBrace; + + if( GetNextToken() != "{") + ThrowException( "Opening brace expected."); + } +} + +// ------------------------------------------------------------------------------------------------ +std::string XFileParser::GetNextToken() +{ + std::string s; + + // process binary-formatted file + if( mIsBinaryFormat) + { + // in binary mode it will only return NAME and STRING token + // and (correctly) skip over other tokens. + + unsigned int tok = ReadBinWord(); + unsigned int len; + + // standalone tokens + switch( tok) + { + case 1: + // name token + len = ReadBinDWord(); + s = std::string(P, len); + P += len; + return s; + case 2: + // string token + len = ReadBinDWord(); + s = std::string(P, len); + P += (len + 2); + return s; + case 3: + // integer token + P += 4; + return ""; + case 5: + // GUID token + P += 16; + return ""; + case 6: + len = ReadBinDWord(); + P += (len * 4); + return ""; + case 7: + len = ReadBinDWord(); + P += (len * mBinaryFloatSize); + return ""; + case 0x0a: + return "{"; + case 0x0b: + return "}"; + case 0x0c: + return "("; + case 0x0d: + return ")"; + case 0x0e: + return "["; + case 0x0f: + return "]"; + case 0x10: + return "<"; + case 0x11: + return ">"; + case 0x12: + return "."; + case 0x13: + return ","; + case 0x14: + return ";"; + case 0x1f: + return "template"; + case 0x28: + return "WORD"; + case 0x29: + return "DWORD"; + case 0x2a: + return "FLOAT"; + case 0x2b: + return "DOUBLE"; + case 0x2c: + return "CHAR"; + case 0x2d: + return "UCHAR"; + case 0x2e: + return "SWORD"; + case 0x2f: + return "SDWORD"; + case 0x30: + return "void"; + case 0x31: + return "string"; + case 0x32: + return "unicode"; + case 0x33: + return "cstring"; + case 0x34: + return "array"; + } + } + // process text-formatted file + else + { + FindNextNoneWhiteSpace(); + if( P >= End) + return s; + + while( (P < End) && !isspace( (unsigned char) *P)) + { + // either keep token delimiters when already holding a token, or return if first valid char + if( *P == ';' || *P == '}' || *P == '{' || *P == ',') + { + if( !s.size()) + s.append( P++, 1); + break; // stop for delimiter + } + s.append( P++, 1); + } + } + return s; +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::FindNextNoneWhiteSpace() +{ + if( mIsBinaryFormat) + return; + + while( true) + { + while( P < End && isspace( (unsigned char) *P)) + { + if( *P == '\n') + mLineNumber++; + ++P; + } + + if( P >= End) + return; + + // check if this is a comment + if( (P[0] == '/' && P[1] == '/') || P[0] == '#') + ReadUntilEndOfLine(); + else + break; + } +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::GetNextTokenAsString( std::string& poString) +{ + if( mIsBinaryFormat) + { + poString = GetNextToken(); + return; + } + + FindNextNoneWhiteSpace(); + if( P >= End) + ThrowException( "Unexpected end of file while parsing string"); + + if( *P != '"') + ThrowException( "Expected quotation mark."); + ++P; + + while( P < End && *P != '"') + poString.append( P++, 1); + + if( P >= End-1) + ThrowException( "Unexpected end of file while parsing string"); + + if( P[1] != ';' || P[0] != '"') + ThrowException( "Expected quotation mark and semicolon at the end of a string."); + P+=2; +} + +// ------------------------------------------------------------------------------------------------ +void XFileParser::ReadUntilEndOfLine() +{ + if( mIsBinaryFormat) + return; + + while( P < End) + { + if( *P == '\n' || *P == '\r') + { + ++P; mLineNumber++; + return; + } + + ++P; + } +} + +// ------------------------------------------------------------------------------------------------ +unsigned short XFileParser::ReadBinWord() +{ + const unsigned char* q = (const unsigned char*) P; + unsigned short tmp = q[0] | (q[1] << 8); + P += 2; + return tmp; +} + +// ------------------------------------------------------------------------------------------------ +unsigned int XFileParser::ReadBinDWord() +{ + const unsigned char* q = (const unsigned char*) P; + unsigned int tmp = q[0] | (q[1] << 8) | (q[2] << 16) | (q[3] << 24); + P += 4; + return tmp; +} + +// ------------------------------------------------------------------------------------------------ +unsigned int XFileParser::ReadInt() +{ + if( mIsBinaryFormat) + { + if( mBinaryNumCount == 0) + { + unsigned short tmp = ReadBinWord(); // 0x06 or 0x03 + if( tmp == 0x06) // array of ints follows + mBinaryNumCount = ReadBinDWord(); + else // single int follows + mBinaryNumCount = 1; + } + + --mBinaryNumCount; + return ReadBinDWord(); + } else + { + FindNextNoneWhiteSpace(); + // check preceeding minus sign + bool isNegative = false; + if( *P == '-') + { + isNegative = true; + P++; + } + + // at least one digit expected + if( !isdigit( *P)) + ThrowException( "Number expected."); + + // read digits + unsigned int number = 0; + while( P < End) + { + if( !isdigit( *P)) + break; + number = number * 10 + (*P - 48); + P++; + } + + CheckForSeparator(); + + return isNegative ? ((unsigned int) -int( number)) : number; + } +} + +// ------------------------------------------------------------------------------------------------ +float XFileParser::ReadFloat() +{ + if( mIsBinaryFormat) + { + if( mBinaryNumCount == 0) + { + unsigned short tmp = ReadBinWord(); // 0x07 or 0x42 + if( tmp == 0x07) // array of floats following + mBinaryNumCount = ReadBinDWord(); + else // single float following + mBinaryNumCount = 1; + } + + --mBinaryNumCount; + if( mBinaryFloatSize == 8) + { + float result = (float) (*(double*) P); + P += 8; + return result; + } else + { + float result = *(float*) P; + P += 4; + return result; + } + } + + // text version + FindNextNoneWhiteSpace(); + // check for various special strings to allow reading files from faulty exporters + // I mean you, Blender! + if( strncmp( P, "-1.#IND00", 9) == 0) + { + P += 9; + CheckForSeparator(); + return 0.0f; + } else + if( strncmp( P, "1.#QNAN0", 8) == 0) + { + P += 8; + CheckForSeparator(); + return 0.0f; + } + + float result = 0.0f; + P = fast_atof_move( P, result); + + CheckForSeparator(); + + return result; +} + +// ------------------------------------------------------------------------------------------------ +aiVector2D XFileParser::ReadVector2() +{ + aiVector2D vector; + vector.x = ReadFloat(); + vector.y = ReadFloat(); + CheckForSeparator(); + + return vector; +} + +// ------------------------------------------------------------------------------------------------ +aiVector3D XFileParser::ReadVector3() +{ + aiVector3D vector; + vector.x = ReadFloat(); + vector.y = ReadFloat(); + vector.z = ReadFloat(); + CheckForSeparator(); + + return vector; +} + +// ------------------------------------------------------------------------------------------------ +aiColor4D XFileParser::ReadRGBA() +{ + aiColor4D color; + color.r = ReadFloat(); + color.g = ReadFloat(); + color.b = ReadFloat(); + color.a = ReadFloat(); + CheckForSeparator(); + + return color; +} + +// ------------------------------------------------------------------------------------------------ +aiColor3D XFileParser::ReadRGB() +{ + aiColor3D color; + color.r = ReadFloat(); + color.g = ReadFloat(); + color.b = ReadFloat(); + CheckForSeparator(); + + return color; +} + +// Throws an exception with a line number and the given text. +void XFileParser::ThrowException( const std::string& pText) +{ + if( mIsBinaryFormat) + throw new ImportErrorException( pText); + else + throw new ImportErrorException( boost::str( boost::format( "Line %d: %s") % mLineNumber % pText)); +} + + +// ------------------------------------------------------------------------------------------------ +// Filters the imported hierarchy for some degenerated cases that some exporters produce. +void XFileParser::FilterHierarchy( XFile::Node* pNode) +{ + // if the node has just a single unnamed child containing a mesh, remove + // the anonymous node inbetween. The 3DSMax kwXport plugin seems to produce this + // mess in some cases + if( pNode->mChildren.size() == 1) + { + XFile::Node* child = pNode->mChildren.front(); + if( child->mName.length() == 0 && child->mMeshes.size() > 0) + { + // transfer its meshes to us + for( unsigned int a = 0; a < child->mMeshes.size(); a++) + pNode->mMeshes.push_back( child->mMeshes[a]); + child->mMeshes.clear(); + + // then kill it + delete child; + pNode->mChildren.clear(); + } + } + + // recurse + for( unsigned int a = 0; a < pNode->mChildren.size(); a++) + FilterHierarchy( pNode->mChildren[a]); +} diff --git a/code/XFileParser.h b/code/XFileParser.h new file mode 100644 index 000000000..6a098b8d2 --- /dev/null +++ b/code/XFileParser.h @@ -0,0 +1,119 @@ +/** @file Helper class to parse a XFile into a temporary structure */ +#ifndef AI_XFILEPARSER_H_INC +#define AI_XFILEPARSER_H_INC + +#include +#include + +#include "../include/aiTypes.h" + +namespace Assimp +{ + namespace XFile + { + struct Node; + struct Mesh; + struct Scene; + struct Material; + struct Animation; + struct AnimBone; + } + +/** The XFileParser reads a XFile either in text or binary form and builds a temporary + * data structure out of it. + */ +class XFileParser +{ +public: + /** Constructor. Creates a data structure out of the XFile given in the memory block. + * @param pBuffer Memory buffer containing the XFile + */ + XFileParser( const std::vector& pBuffer); + + /** Destructor. Destroys all imported data along with it */ + ~XFileParser(); + + /** Returns the temporary representation of the imported data */ + const XFile::Scene* GetImportedData() const { return mScene; } + +protected: + void ParseFile(); + void ParseDataObjectTemplate(); + void ParseDataObjectFrame( XFile::Node *pParent); + void ParseDataObjectTransformationMatrix( aiMatrix4x4& pMatrix); + void ParseDataObjectMesh( XFile::Mesh* pMesh); + void ParseDataObjectSkinWeights( XFile::Mesh* pMesh); + void ParseDataObjectSkinMeshHeader( XFile::Mesh* pMesh); + void ParseDataObjectMeshNormals( XFile::Mesh* pMesh); + void ParseDataObjectMeshTextureCoords( XFile::Mesh* pMesh); + void ParseDataObjectMeshVertexColors( XFile::Mesh* pMesh); + void ParseDataObjectMeshMaterialList( XFile::Mesh* pMesh); + void ParseDataObjectMaterial( XFile::Material* pMaterial); + void ParseDataObjectAnimTicksPerSecond(); + void ParseDataObjectAnimationSet(); + void ParseDataObjectAnimation( XFile::Animation* pAnim); + void ParseDataObjectAnimationKey( XFile::AnimBone *pAnimBone); + void ParseDataObjectTextureFilename( std::string& pName); + void ParseUnknownDataObject(); + + //! places pointer to next begin of a token, and ignores comments + void FindNextNoneWhiteSpace(); + + //! returns next parseable token. Returns empty string if no token there + std::string GetNextToken(); + + //! reads header of dataobject including the opening brace. + //! returns false if error happened, and writes name of object + //! if there is one + void readHeadOfDataObject( std::string* poName = NULL); + + //! checks for closing curly brace, throws exception if not there + void CheckForClosingBrace(); + + //! checks for one following semicolon, throws exception if not there + void CheckForSemicolon(); + + //! checks for a separator char, either a ',' or a ';' + void CheckForSeparator(); + + //! reads a x file style string + void GetNextTokenAsString( std::string& poString); + + void ReadUntilEndOfLine(); + + unsigned short ReadBinWord(); + unsigned int ReadBinDWord(); + unsigned int ReadInt(); + float ReadFloat(); + aiVector2D ReadVector2(); + aiVector3D ReadVector3(); + aiColor3D ReadRGB(); + aiColor4D ReadRGBA(); + + /** Throws an exception with a line number and the given text. */ + void ThrowException( const std::string& pText); + + /** Filters the imported hierarchy for some degenerated cases that some exporters produce. + * @param pData The sub-hierarchy to filter + */ + void FilterHierarchy( XFile::Node* pNode); + +protected: + unsigned int mMajorVersion, mMinorVersion; ///< version numbers + bool mIsBinaryFormat; ///< true if the file is in binary, false if it's in text form + unsigned int mBinaryFloatSize; ///< float size, either 32 or 64 bits + // counter for number arrays in binary format + unsigned int mBinaryNumCount; + + const char* P; + const char* End; + + /// Line number when reading in text format + unsigned int mLineNumber; + + /// Imported data + XFile::Scene* mScene; +}; + +} +#endif // AI_XFILEPARSER_H_INC \ No newline at end of file diff --git a/code/aiAssert.cpp b/code/aiAssert.cpp new file mode 100644 index 000000000..7a5b50fdf --- /dev/null +++ b/code/aiAssert.cpp @@ -0,0 +1,26 @@ +#include +#include "aiAssert.h" +#ifdef _WIN32 +#ifndef __GNUC__ +# include "crtdbg.h" +#endif //ndef gcc +#endif + +// Set a breakpoint using win32, else line, file and message will be returned and progam ends with +// errrocode = 1 +void Assimp::aiAssert (bool expression, const std::string &message, unsigned int uiLine, const std::string &file) +{ + if (!expression) + { + std::cerr << "File :" << file << ", line " << uiLine << " : " << message << std::endl; + +#ifdef _WIN32 +#ifndef __GNUC__ + // Set breakpoint + __debugbreak(); +#endif //ndef gcc +#else + exit (1); +#endif + } +} \ No newline at end of file diff --git a/code/fast_atof.h b/code/fast_atof.h new file mode 100644 index 000000000..7d60b2a81 --- /dev/null +++ b/code/fast_atof.h @@ -0,0 +1,114 @@ +// Copyright (C) 2002-2007 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine" and the "irrXML" project. +// For conditions of distribution and use, see copyright notice in irrlicht.h and irrXML.h +// Adapted to the ASSIMP library because the builtin atof indeed takes AGES to parse a +// float inside a large string. Before parsing, it does a strlen on the given point. + +#ifndef __FAST_A_TO_F_H_INCLUDED__ +#define __FAST_A_TO_F_H_INCLUDED__ + +#include + +namespace Assimp +{ + +const float fast_atof_table[16] = { // we write [16] here instead of [] to work around a swig bug + 0.f, + 0.1f, + 0.01f, + 0.001f, + 0.0001f, + 0.00001f, + 0.000001f, + 0.0000001f, + 0.00000001f, + 0.000000001f, + 0.0000000001f, + 0.00000000001f, + 0.000000000001f, + 0.0000000000001f, + 0.00000000000001f, + 0.000000000000001f +}; + +inline unsigned int strtol10( const char* in, const char** out=0) +{ + unsigned int value = 0; + + while ( 1 ) + { + if ( *in < '0' || *in > '9' ) + break; + + value = ( value * 10 ) + ( *in - '0' ); + ++in; + } + if (out) + *out = in; + return value; +} + +//! Provides a fast function for converting a string into a float, +//! about 6 times faster than atof in win32. +// If you find any bugs, please send them to me, niko (at) irrlicht3d.org. +inline const char* fast_atof_move( const char* c, float& out) +{ + bool inv = false; + const char *t; + float f; + + if (*c=='-') + { + ++c; + inv = true; + } + + //f = (float)strtol(c, &t, 10); + f = (float) strtol10 ( c, &c ); + + if (*c == '.') + { + ++c; + + //float pl = (float)strtol(c, &t, 10); + float pl = (float) strtol10 ( c, &t ); + pl *= fast_atof_table[t-c]; + + f += pl; + + c = t; + + if (*c == 'e') + { + ++c; + //float exp = (float)strtol(c, &t, 10); + bool einv = (*c=='-'); + if (einv) + ++c; + + float exp = (float)strtol10(c, &c); + if (einv) + exp *= -1.0f; + + f *= pow(10.0f, exp); + } + } + + if (inv) + f *= -1.0f; + + out = f; + return c; +} + +inline float fast_atof(const char* c) +{ + float ret; + fast_atof_move(c, ret); + return ret; +} + +} // end of namespace Assimp + +#endif + diff --git a/doc/ImporterNotes.rtf b/doc/ImporterNotes.rtf new file mode 100644 index 000000000..0c1cb54c5 Binary files /dev/null and b/doc/ImporterNotes.rtf differ diff --git a/doc/datastructure.xml b/doc/datastructure.xml new file mode 100644 index 000000000..6328197a5 --- /dev/null +++ b/doc/datastructure.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ... + + + + + ... + + + + + ... + + + + ... + + + + + + + + + + + + + + \ No newline at end of file diff --git a/include/Compiler/VisualStudio/stdint.h b/include/Compiler/VisualStudio/stdint.h new file mode 100644 index 000000000..8b68744e1 --- /dev/null +++ b/include/Compiler/VisualStudio/stdint.h @@ -0,0 +1,222 @@ +// ISO C9x compliant stdint.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. The name of the author may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_STDINT_H_ // [ +#define _MSC_STDINT_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include + +// For Visual Studio 6 in C++ mode wrap include with 'extern "C++" {}' +// or compiler give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#if (_MSC_VER < 1300) && defined(__cplusplus) + extern "C++" { +#endif +# include +#if (_MSC_VER < 1300) && defined(__cplusplus) + } +#endif + +// 7.18.1 Integer types + +// 7.18.1.1 Exact-width integer types +typedef __int8 int8_t; +typedef __int16 int16_t; +typedef __int32 int32_t; +typedef __int64 int64_t; +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; + +// 7.18.1.2 Minimum-width integer types +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +// 7.18.1.3 Fastest minimum-width integer types +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +// 7.18.1.4 Integer types capable of holding object pointers +#ifdef _WIN64 // [ + typedef __int64 intptr_t; + typedef unsigned __int64 uintptr_t; +#else // _WIN64 ][ + typedef int intptr_t; + typedef unsigned int uintptr_t; +#endif // _WIN64 ] + +// 7.18.1.5 Greatest-width integer types +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + + +// 7.18.2 Limits of specified-width integer types + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 + +// 7.18.2.1 Limits of exact-width integer types +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +// 7.18.2.2 Limits of minimum-width integer types +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +// 7.18.2.3 Limits of fastest minimum-width integer types +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +// 7.18.2.4 Limits of integer types capable of holding object pointers +#ifdef _WIN64 // [ +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else // _WIN64 ][ +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif // _WIN64 ] + +// 7.18.2.5 Limits of greatest-width integer types +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +// 7.18.3 Limits of other integer types + +#ifdef _WIN64 // [ +# define PTRDIFF_MIN _I64_MIN +# define PTRDIFF_MAX _I64_MAX +#else // _WIN64 ][ +# define PTRDIFF_MIN _I32_MIN +# define PTRDIFF_MAX _I32_MAX +#endif // _WIN64 ] + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX // [ +# ifdef _WIN64 // [ +# define SIZE_MAX _UI64_MAX +# else // _WIN64 ][ +# define SIZE_MAX _UI32_MAX +# endif // _WIN64 ] +#endif // SIZE_MAX ] + +// WCHAR_MIN and WCHAR_MAX are also defined in +#ifndef WCHAR_MIN // [ +# define WCHAR_MIN 0 +#endif // WCHAR_MIN ] +#ifndef WCHAR_MAX // [ +# define WCHAR_MAX _UI16_MAX +#endif // WCHAR_MAX ] + +#define WINT_MIN 0 +#define WINT_MAX _UI16_MAX + +#endif // __STDC_LIMIT_MACROS ] + + +// 7.18.4 Limits of other integer types + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +#define INTMAX_C INT64_C +#define UINTMAX_C UINT64_C + +#endif // __STDC_CONSTANT_MACROS ] + + +#endif // _MSC_STDINT_H_ ] \ No newline at end of file diff --git a/include/IOStream.h b/include/IOStream.h new file mode 100644 index 000000000..68c8aea67 --- /dev/null +++ b/include/IOStream.h @@ -0,0 +1,111 @@ + +/** @file File I/O wrappers for C++. Use interfaces instead of function +* pointers to be sure even the silliest men on earth can work with this +*/ + +#ifndef AI_IOSTREAM_H_INC +#define AI_IOSTREAM_H_INC + +#include +#include + +#include "aiTypes.h" +#include "aiFileIO.h" + +#ifndef __cplusplus +#error This header requires C++ to be used. +#endif + +namespace Assimp +{ + +// --------------------------------------------------------------------------- +/** Class to handle file I/O for C++ +* +* Derive an own implementation from this interface to provide custom IO handling +* to the Importer. If you implement this interface, be sure to also provide an +* implementation for IOSystem that creates instances of your custom IO class. +*/ +// --------------------------------------------------------------------------- +class IOStream +{ +protected: + /** Constructor protected, use IOSystem::Open() to create an instance. */ + IOStream(void); + +public: + // ------------------------------------------------------------------- + /** Destructor. Deleting the object closes the underlying file, + * alternatively you may use IOSystem::Close() to release the file. + */ + virtual ~IOStream(void); + + // ------------------------------------------------------------------- + /** Read from the file + * + * See fread() for more details + * This fails for write-only files + */ + // ------------------------------------------------------------------- + virtual size_t Read( + void* pvBuffer, + size_t pSize, + size_t pCount) = 0; + + + // ------------------------------------------------------------------- + /** Write to the file + * + * See fwrite() for more details + * This fails for read-only files + */ + // ------------------------------------------------------------------- + virtual size_t Write( + const void* pvBuffer, + size_t pSize, + size_t pCount) = 0; + + + // ------------------------------------------------------------------- + /** Set the read/write cursor of the file + * + * See fseek() for more details + */ + // ------------------------------------------------------------------- + virtual aiReturn Seek( + size_t pOffset, + aiOrigin pOrigin) = 0; + + + // ------------------------------------------------------------------- + /** Get the current position of the read/write cursor + * + * See ftell() for more details + */ + // ------------------------------------------------------------------- + virtual size_t Tell(void) const = 0; + + // ------------------------------------------------------------------- + /** Returns filesize + * + * Returns the filesize + */ + // ------------------------------------------------------------------- + virtual size_t FileSize() const = 0; +}; +// ---------------------------------------------------------------------------- +inline IOStream::IOStream() +{ + // empty +} + +// ---------------------------------------------------------------------------- +inline IOStream::~IOStream() +{ + // empty +} +// ---------------------------------------------------------------------------- + +} //!ns Assimp + +#endif //!!AI_IOSTREAM_H_INC diff --git a/include/IOSystem.h b/include/IOSystem.h new file mode 100644 index 000000000..2ff354dd1 --- /dev/null +++ b/include/IOSystem.h @@ -0,0 +1,90 @@ +/** @file Filesystem wrapper for C++. Inherit this class to supply custom file handling + * logic to the Import library. +*/ + +#ifndef AI_IOSYSTEM_H_INC +#define AI_IOSYSTEM_H_INC + +#ifndef __cplusplus +#error This header requires C++ to be used. +#endif + +#include + +namespace Assimp +{ + +class IOStream; + +// --------------------------------------------------------------------------- +/** Interface to the file system. +* +* Derive an own implementation from this interface to supply custom file handling +* to the importer library. If you implement this interface, you also want to +* supply a custom implementation for IOStream. +*/ +class IOSystem +{ +public: + /** Constructor. Create an instance of your derived class and assign it to + * the #Importer instance by calling Importer::SetIOHandler(). + */ + IOSystem(); + + /** Destructor. */ + virtual ~IOSystem(); + + // ------------------------------------------------------------------- + /** Tests for the existence of a file at the given path. + * + * @param pFile Path to the file + * @return true if there is a file with this path, else false. + */ + virtual bool Exists( const std::string& pFile) const = 0; + + // ------------------------------------------------------------------- + /** Returns the system specific directory separator + * @return System specific directory separator + */ + virtual std::string getOsSeparator() const = 0; + + // ------------------------------------------------------------------- + /** Open a new file with a given path. When the access to the file is finished, + * call Close() to release all associated resources. + * + * @param pFile Path to the file + * @param pMode Desired file I/O mode. Required are: "wb", "w", "wt", + * "rb", "r", "rt". + * + * @return New IOStream interface allowing the lib to access + * the underlying file. + * @note When implementing this class to provide custom IO handling, you propably + * have to supply an own implementation of IOStream as well. + */ + virtual IOStream* Open( + const std::string& pFile, + const std::string& pMode = std::string("rb")) = 0; + + // ------------------------------------------------------------------- + /** Closes the given file and releases all resources associated with it. + * @param pFile The file instance previously created by Open(). + */ + virtual void Close( IOStream* pFile) = 0; +}; + +// ---------------------------------------------------------------------------- +inline IOSystem::IOSystem() +{ + // empty +} + +// ---------------------------------------------------------------------------- +inline IOSystem::~IOSystem() +{ + // empty +} +// ---------------------------------------------------------------------------- + +} //!ns Assimp + +#endif //AI_IOSYSTEM_H_INC diff --git a/include/ObjFileParser.h b/include/ObjFileParser.h new file mode 100644 index 000000000..6ff4c7c1a --- /dev/null +++ b/include/ObjFileParser.h @@ -0,0 +1,68 @@ +#ifndef OBJ_FILEPARSER_H_INC +#define OBJ_FILEPARSER_H_INC + +#include +#include +#include "aiTypes.h" + +/*struct aiVector2D_t; +struct aiVector3D_t;*/ + +namespace Assimp +{ + +namespace ObjFile +{ +struct Model; +struct Object; +struct Material; +struct Point3; +struct Point2; +} +class ObjFileImporter; + +class ObjFileParser +{ +public: + static const size_t BUFFERSIZE = 1024; + typedef std::vector DataArray; + typedef std::vector::iterator DataArrayIt; + typedef std::vector::const_iterator ConstDataArrayIt; + +public: + ObjFileParser(std::vector &Data, const std::string &strAbsPath, const std::string &strModelName); + ~ObjFileParser(); + ObjFile::Model *GetModel() const; + +private: + void parseFile(); + void copyNextWord(char *pBuffer, size_t length); + void copyNextLine(char *pBuffer, size_t length); + void getVector3(std::vector &point3d_array); + void getVector2(std::vector &point2d_array); + void skipLine(); + void getFace(); + void getMaterialDesc(); + void getComment(); + void getMaterialLib(); + void getNewMaterial(); + void getGroupName(); + void getGroupNumber(); + void getObjectName(); + void createObject(const std::string &strObjectName); + void reportErrorTokenInFace(); + void extractExtension(const std::string strFile, std::string &strExt); + +private: + std::string m_strAbsPath; + DataArrayIt m_DataIt; + DataArrayIt m_DataItEnd; + ObjFile::Model *m_pModel; + unsigned int m_uiLine; + char m_buffer[BUFFERSIZE]; + +}; + +} // Namespace Assimp + +#endif diff --git a/include/aiAnim.h b/include/aiAnim.h new file mode 100644 index 000000000..1a1ead0d6 --- /dev/null +++ b/include/aiAnim.h @@ -0,0 +1,118 @@ +/** @file Defines the data structures in which the imported animations are returned. */ +#ifndef AI_ANIM_H_INC +#define AI_ANIM_H_INC + +#include "aiTypes.h" +#include "aiQuaternion.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** A time-value pair specifying a certain 3D vector for the given time. */ +struct aiVectorKey +{ + double mTime; ///< The time of this key + aiVector3D_t mValue; ///< The value of this key +}; + +/** A time-value pair specifying a rotation for the given time. For joint animations + * the rotation is usually expressed using a quaternion. + */ +struct aiQuatKey +{ + double mTime; ///< The time of this key + aiQuaternion_t mValue; ///< The value of this key +}; + +/** Describes the animation of a single bone. The name specifies the bone which is affected by this + * animation channel. The keyframes are given in three separate series of values, one each for + * position, rotation and scaling. + */ +struct aiBoneAnim +{ + /** The name of the bone affected by this animation. */ + aiString mBoneName; + + /** The number of position keys */ + unsigned int mNumPositionKeys; + /** The position keys of this animation channel. Positions are specified as 3D vector. + * The array is mNumPositionKeys in size. + */ + aiVectorKey* mPositionKeys; + + /** The number of rotation keys */ + unsigned int mNumRotationKeys; + /** The rotation keys of this animation channel. Rotations are given as quaternions, + * which are 4D vectors. The array is mNumRotationKeys in size. + */ + aiQuatKey* mRotationKeys; + + /** The number of scaling keys */ + unsigned int mNumScalingKeys; + /** The scaling keys of this animation channel. Scalings are specified as 3D vector. + * The array is mNumScalingKeys in size. + */ + aiVectorKey* mScalingKeys; + +#ifdef __cplusplus + aiBoneAnim() + { + mNumPositionKeys = 0; mPositionKeys = NULL; + mNumRotationKeys= 0; mRotationKeys = NULL; + mNumScalingKeys = 0; mScalingKeys = NULL; + } + + ~aiBoneAnim() + { + delete [] mPositionKeys; + delete [] mRotationKeys; + delete [] mScalingKeys; + } +#endif // __cplusplus +}; + +/** An animation consists of keyframe data for a number of bones. For each bone affected by the animation + * a separate series of data is given. + */ +struct aiAnimation +{ + /** The name of the animation. If the modelling package this data was exported from does support + * only a single animation channel, this name is usually empty (length is zero). + */ + aiString mName; + + /** Duration of the animation in ticks. */ + double mDuration; + /** Ticks per second. 0 if not specified in the imported file */ + double mTicksPerSecond; + + /** The number of bone animation channels. Each channel affects a single bone. */ + unsigned int mNumBones; + /** The bone animation channels. Each channel affects a single bone. The array + * is mNumBones in size. + */ + aiBoneAnim** mBones; + +#ifdef __cplusplus + aiAnimation() + { + mDuration = 0; + mTicksPerSecond = 0; + mNumBones = 0; mBones = NULL; + } + + ~aiAnimation() + { + for( unsigned int a = 0; a < mNumBones; a++) + delete mBones[a]; + delete [] mBones; + } +#endif // __cplusplus +}; + +#ifdef __cplusplus +} +#endif + +#endif // AI_ANIM_H_INC diff --git a/include/aiAssert.h b/include/aiAssert.h new file mode 100644 index 000000000..6f7c84110 --- /dev/null +++ b/include/aiAssert.h @@ -0,0 +1,28 @@ +#ifndef AI_DEBUG_H_INC +#define AI_DEBUG_H_INC + +#include + +#ifndef __cplusplus +#error This header requires C++ to be used. +#endif + +namespace Assimp { + +//! \brief ASSIMP specific assertion test, just works in debug mode +//! \param uiLine Line in file +//! \param file Source file +void aiAssert (bool expression, const std::string &message, unsigned int uiLine, const std::string &file); + + +//! \def ai_assert +//! \brief ASSIM specific assertion test +#ifdef DEBUG +# define ai_assert(expression) aiAssert (expression, #expression, __LINE__, __FILE__); +#else +# define ai_assert(expression) +#endif + +} // Namespace Assimp + +#endif diff --git a/include/aiDefs.h b/include/aiDefs.h new file mode 100644 index 000000000..22efe89cb --- /dev/null +++ b/include/aiDefs.h @@ -0,0 +1,23 @@ + + +/** @file Helper macros for the library + */ +#ifndef AI_DEF_H_INC +#define AI_DEF_H_INC + + +/** Namespace helper macros for c++ compilers + */ +#ifdef __cplusplus + #define AI_NAMESPACE_START namespace Assimp { + #define AI_NAMESPACE_END }; +#else + #define AI_NAMESPACE_START + #define AI_NAMESPACE_END +#endif + +#ifdef _DEBUG +# define ASSIMP_DEBUG +#endif + +#endif //!!AI_DEF_H_INC diff --git a/include/aiFileIO.h b/include/aiFileIO.h new file mode 100644 index 000000000..7378da061 --- /dev/null +++ b/include/aiFileIO.h @@ -0,0 +1,63 @@ + +/** @file Defines generic routines to access memory-mapped files + * + */ + +#ifndef AI_FILEIO_H_INC +#define AI_FILEIO_H_INC + +#include "aiTypes.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +struct aiFileIO; +//enum aiOrigin; +typedef aiFileIO (*aiFileOpenProc)(aiFileIO*, const char*, const char*); +typedef aiReturn (*aiFileCloseProc)(aiFileIO*); +typedef unsigned long (*aiFileReadWriteProc)(aiFileIO*, char*, unsigned int, unsigned int); +typedef unsigned long (*aiFileTellProc)(aiFileIO*); + +// --------------------------------------------------------------------------- +/** Define seek origins in fseek()-style. +*/ +// --------------------------------------------------------------------------- +enum aiOrigin +{ + aiOrigin_SET = 0x0, //!< Set position + aiOrigin_CUR = 0x1, //!< Current position + aiOrigin_END = 0x2 //!< End of file +}; + +typedef aiReturn (*aiFileSeek)(aiFileIO*, unsigned long, aiOrigin); + +typedef char* aiUserData; + +// --------------------------------------------------------------------------- +/** Data structure to wrap a set of fXXXX (e.g fopen) replacement functions +* +* The functions behave the same way as their appropriate fXXXX +* counterparts in the CRT. +*/ +// --------------------------------------------------------------------------- +struct aiFileIO +{ + aiUserData UserData; + + aiFileOpenProc OpenFunc; + aiFileCloseProc CloseFunc; + aiFileReadWriteProc ReadFunc; + aiFileReadWriteProc WriteFunc; + aiFileTellProc TellProc; + aiFileSeek SeekProc; +}; + + +#ifdef __cplusplus +} +#endif + + +#endif // AI_FILEIO_H_INC diff --git a/include/aiMaterial.h b/include/aiMaterial.h new file mode 100644 index 000000000..aad47d8d4 --- /dev/null +++ b/include/aiMaterial.h @@ -0,0 +1,476 @@ + + +/** @file Defines the material system of the library + * + */ + +#ifndef AI_MATERIAL_H_INC +#define AI_MATERIAL_H_INC + +#include "aiTypes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// --------------------------------------------------------------------------- +/** Defines type identifiers for use within the material system. +* +*/ +// --------------------------------------------------------------------------- +enum aiPropertyTypeInfo +{ + /** Array of single-precision floats + */ + aiPTI_Float = 0x1, + + + /** aiString data structure + */ + aiPTI_String = 0x3, + + + /** Array of Integers + */ + aiPTI_Integer = 0x4, + + + /** Simple binary buffer + */ + aiPTI_Buffer = 0x5, +}; + + +// --------------------------------------------------------------------------- +/** Defines algorithms for generating UVW-coords (for texture sampling) +* procedurally. +*/ +// --------------------------------------------------------------------------- +enum aiTexUVWGen +{ + /** The view vector will be reflected to a pixel's normal. + * + * The result is used as UVW-coordinate for + * accessing a cubemap + */ + aiTexUVWGen_VIEWREFLEFT = 0x800001, + + + /** The view vector will be used as UVW-src + * + * The view vector is used as UVW-coordinate for + * accessing a cubemap + */ + aiTexUVWGen_VIEW = 0x800002, + + + /** The view vector will be refracted to the pixel's normal. + * + * If this is used, the refraction index to be applied should + * also be contained in the material description. + * The result is used as UVW-coordinate for + * accessing a cubemap. + */ + aiTexUVWGen_VIEWREFRACT = 0x800003 +}; + + +// --------------------------------------------------------------------------- +/** Defines all shading models supported by the library +* +* @note The list of shading modes has been taken from Blender3D. +* See Blender3D documentation for more information. The API does +* not distinguish between "specular" and "diffuse" shaders (thus the +* specular term for diffuse shading models like Oren-Nayar remains +* undefined) +*/ +// --------------------------------------------------------------------------- +enum aiShadingMode +{ + /** Flat shading. Shading is done on per-face base, + * diffuse only. + */ + aiShadingMode_Flat = 0x1, + + + /** Diffuse gouraud shading. Shading on per-vertex base + */ + aiShadingMode_Gouraud = 0x2, + + + /** Diffuse/Specular Phong-Shading + * + * Shading is applied on per-pixel base. This is the + * slowest algorithm, but generates the best results. + */ + aiShadingMode_Phong = 0x3, + + + /** Diffuse/Specular Phong-Blinn-Shading + * + * Shading is applied on per-pixel base. This is a little + * bit faster than phong and in some cases even + * more realistic + */ + aiShadingMode_Blinn = 0x4, + + + /** Toon-Shading per pixel + * + * Shading is applied on per-pixel base. The output looks + * like a comic. Often combined with edge detection. + */ + aiShadingMode_Toon = 0x5, + + + /** OrenNayar-Shading per pixel + * + * Extension to standard lambertian shading, taking the + * roughness of the material into account + * + */ + aiShadingMode_OrenNayar = 0x6, + + + /** Minnaert-Shading per pixel + * + * Extension to standard lambertian shading, taking the + * "darkness" of the material into account + */ + aiShadingMode_Minnaert = 0x7, + + + /** CookTorrance-Shading per pixel + */ + aiShadingMode_CookTorrance = 0x8, + + + /** No shading at all + */ + aiShadingMode_NoShading = 0x8 +}; + + +// --------------------------------------------------------------------------- +/** Data structure for a single property inside a material +* +* @see aiMaterial +*/ +// --------------------------------------------------------------------------- +struct aiMaterialProperty +{ + /** Specifies the name of the property (key) + * + * Keys are case insensitive. + */ + aiString* mKey; + + + /** Size of the buffer mData is pointing to, in bytes + */ + unsigned int mDataLength; + + + /** Type information for the property. + * + * Defines the data layout inside the + * data buffer. This is used by the library + * internally to perform debug checks. + */ + aiPropertyTypeInfo mType; + + + /** Binary buffer to hold the property's value + * + * The buffer has no terminal character. However, + * if a string is stored inside it may use 0 as terminal, + * but it would be contained in mDataLength. + */ + char* mData; +}; + + +// --------------------------------------------------------------------------- +/** Data structure for a material +* +* Material data is stored using a key-value structure, called property +* (to guarant that the system is maximally flexible). +* The library defines a set of standard keys, which should be enough +* for nearly all purposes. +*/ +// --------------------------------------------------------------------------- +#ifdef __cplusplus +class aiMaterial +{ +protected: + aiMaterial() {} +public: +#else +struct aiMaterial +{ +#endif // __cplusplus + /** List of all material properties loaded. + */ + aiMaterialProperty** mProperties; + + /** Number of properties loaded + */ + unsigned int mNumProperties; + unsigned int mNumAllocated; +}; + + +// --------------------------------------------------------------------------- +/** @def AI_MATKEY_NAME +* Defines the name of the material (aiString) +*/ +#define AI_MATKEY_NAME "$mat.name" + +/** @def AI_MATKEY_SHADING_MODE +* Defines the shading model to use (aiShadingMode) +*/ +#define AI_MATKEY_SHADING_MODEL "$mat.shadingm" + +/** @def AI_MATKEY_OPACITY +* Defines the base opacity of the material +*/ +#define AI_MATKEY_OPACITY "$mat.opacity" + +/** @def AI_MATKEY_BUMPSCALING +* Defines the height scaling of a bump map (for stuff like Parallax +* Occlusion Mapping) +*/ +#define AI_MATKEY_BUMPSCALING "$mat.bumpscaling" + +/** @def AI_MATKEY_SHININESS +* Defines the base shininess of the material +*/ +#define AI_MATKEY_SHININESS "$mat.shininess" + +/** @def AI_MATKEY_COLOR_DIFFUSE +* Defines the diffuse base color of the material +*/ +#define AI_MATKEY_COLOR_DIFFUSE "$clr.diffuse" + +/** @def AI_MATKEY_COLOR_AMBIENT +* Defines the ambient base color of the material +*/ +#define AI_MATKEY_COLOR_AMBIENT "$clr.ambient" + +/** @def AI_MATKEY_COLOR_SPECULAR +* Defines the specular base color of the material +*/ +#define AI_MATKEY_COLOR_SPECULAR "$clr.specular" + +/** @def AI_MATKEY_COLOR_EMISSIVE +* Defines the emissive base color of the material +*/ +#define AI_MATKEY_COLOR_EMISSIVE "$clr.emissive" + +/** @def AI_MATKEY_TEXTURE_DIFFUSE +* Defines a specified diffuse texture channel of the material +*/ +#define AI_MATKEY_TEXTURE_DIFFUSE(N) "$tex.file.diffuse["#N"]" +#define AI_MATKEY_TEXTURE_DIFFUSE_ "$tex.file.diffuse" + +/** @def AI_MATKEY_TEXTURE_AMBIENT +* Defines a specified ambient texture channel of the material +*/ +#define AI_MATKEY_TEXTURE_AMBIENT(N) "$tex.file.ambient["#N"]" +#define AI_MATKEY_TEXTURE_AMBIENT_ "$tex.file.ambient" + +/** @def AI_MATKEY_TEXTURE_SPECULAR +* Defines a specified specular texture channel of the material +*/ +#define AI_MATKEY_TEXTURE_SPECULAR(N) "$tex.file.specular["#N"]" +#define AI_MATKEY_TEXTURE_SPECULAR_ "$tex.file.specular" + +/** @def AI_MATKEY_TEXTURE_EMISSIVE +* Defines a specified emissive texture channel of the material +*/ +#define AI_MATKEY_TEXTURE_EMISSIVE(N) "$tex.file.emissive["#N"]" +#define AI_MATKEY_TEXTURE_EMISSIVE_ "$tex.file.emissive" + +/** @def AI_MATKEY_TEXTURE_NORMALS +* Defines a specified normal texture channel of the material +*/ +#define AI_MATKEY_TEXTURE_NORMALS(N) "$tex.file.normals["#N"]" +#define AI_MATKEY_TEXTURE_NORMALS_ "$tex.file.normals" + +/** @def AI_MATKEY_TEXTURE_BUMP +* Defines a specified bumpmap texture (=heightmap) channel of the material +* This is very similar to #AI_MATKEY_TEXTURE_NORMALS. It is provided +* to allow applications to determine whether the input data for +* normal mapping is already a normal map or needs to be converted to +* a heightmap. +*/ +#define AI_MATKEY_TEXTURE_BUMP(N) "$tex.file.bump["#N"]" +#define AI_MATKEY_TEXTURE_BUMP_ "$tex.file.bump" + +/** @def AI_MATKEY_TEXTURE_SHININESS +* Defines a specified shininess texture channel of the material +*/ +#define AI_MATKEY_TEXTURE_SHININESS(N) "$tex.file.shininess["#N"]" +#define AI_MATKEY_TEXTURE_SHININESS_ "$tex.file.shininess" + +/** @def AI_MATKEY_TEXTURE_OPACITY +* Defines a specified opacity texture channel of the material +*/ +#define AI_MATKEY_TEXTURE_OPACITY(N) "$tex.file.opacity["#N"]" +#define AI_MATKEY_TEXTURE_OPACITY_ "$tex.file.opacity" + + +#define AI_MATKEY_TEXOP_DIFFUSE(N) "$tex.op.diffuse["#N"]" +#define AI_MATKEY_TEXOP_AMBIENT(N) "$tex.op.ambient["#N"]" +#define AI_MATKEY_TEXOP_SPECULAR(N) "$tex.op.specular["#N"]" +#define AI_MATKEY_TEXOP_EMISSIVE(N) "$tex.op.emissive["#N"]" +#define AI_MATKEY_TEXOP_NORMALS(N) "$tex.op.normals["#N"]" +#define AI_MATKEY_TEXOP_BUMP(N) "$tex.op.bump["#N"]" +#define AI_MATKEY_TEXOP_SHININESS(N) "$tex.op.shininess["#N"]" +#define AI_MATKEY_TEXOP_OPACITY(N) "$tex.op.opacity["#N"]" + +#define AI_MATKEY_UVWSRC_DIFFUSE(N) "$tex.uvw.diffuse["#N"]" +#define AI_MATKEY_UVWSRC_AMBIENT(N) "$tex.uvw.ambient["#N"]" +#define AI_MATKEY_UVWSRC_SPECULAR(N) "$tex.uvw.specular["#N"]" +#define AI_MATKEY_UVWSRC_EMISSIVE(N) "$tex.uvw.emissive["#N"]" +#define AI_MATKEY_UVWSRC_NORMALS(N) "$tex.uvw.normals["#N"]" +#define AI_MATKEY_UVWSRC_BUMP(N) "$tex.uvw.bump["#N"]" +#define AI_MATKEY_UVWSRC_SHININESS(N) "$tex.uvw.shininess["#N"]" +#define AI_MATKEY_UVWSRC_OPACITY(N) "$tex.uvw.opacity["#N"]" + +#define AI_MATKEY_REFRACTI_DIFFUSE(N) "$tex.refracti.diffuse["#N"]" +#define AI_MATKEY_REFRACTI_AMBIENT(N) "$tex.refracti.ambient["#N"]" +#define AI_MATKEY_REFRACTI_SPECULAR(N) "$tex.refracti.specular["#N"]" +#define AI_MATKEY_REFRACTI_EMISSIVE(N) "$tex.refracti.emissive["#N"]" +#define AI_MATKEY_REFRACTI_NORMALS(N) "$tex.refracti.normals["#N"]" +#define AI_MATKEY_REFRACTI_BUMP(N) "$tex.refracti.bump["#N"]" +#define AI_MATKEY_REFRACTI_SHININESS(N) "$tex.refracti.shininess["#N"]" +#define AI_MATKEY_REFRACTI_OPACITY(N) "$tex.refracti.opacity["#N"]" + +#define AI_MATKEY_TEXBLEND_DIFFUSE(N) "$tex.blend.diffuse["#N"]" +#define AI_MATKEY_TEXBLEND_AMBIENT(N) "$tex.blend.ambient["#N"]" +#define AI_MATKEY_TEXBLEND_SPECULAR(N) "$tex.blend.specular["#N"]" +#define AI_MATKEY_TEXBLEND_EMISSIVE(N) "$tex.blend.emissive["#N"]" +#define AI_MATKEY_TEXBLEND_NORMALS(N) "$tex.blend.normals["#N"]" +#define AI_MATKEY_TEXBLEND_BUMP(N) "$tex.blend.bump["#N"]" +#define AI_MATKEY_TEXBLEND_SHININESS(N) "$tex.blend.shininess["#N"]" +#define AI_MATKEY_TEXBLEND_OPACITY(N) "$tex.blend.opacity["#N"]" + + +#define AI_MATKEY_ORENNAYAR_ROUGHNESS "$shading.orennayar.roughness" +#define AI_MATKEY_MINNAERT_DARKNESS "$shading.minnaert.darkness" +#define AI_MATKEY_COOK_TORRANCE_REFRACTI "$shading.cookt.refracti" +#define AI_MATKEY_COOK_TORRANCE_PARAM "$shading.cookt.param" + + +/** @def AI_MATKEY_GLOBAL_BACKGROUND_IMAGE +* Global property defined by some loaders. Contains the path to +* the image file to be used as background image. +*/ +#define AI_MATKEY_GLOBAL_BACKGROUND_IMAGE "$global.bg.image2d" + + +// --------------------------------------------------------------------------- +/** Retrieve a material property with a specific key from the material +* +* @param pMat Pointer to the input material. May not be NULL +* @param pKey Key to search for. One of the AI_MATKEY_XXX constants. +* @param pPropOut Pointer to receive a pointer to a valid aiMaterialProperty +* structure or NULL if the key has not been found. +*/ +// --------------------------------------------------------------------------- +aiReturn aiGetMaterialProperty(const aiMaterial* pMat, + const char* pKey, + const aiMaterialProperty** pPropOut); + + +// --------------------------------------------------------------------------- +/** Retrieve an array of float values with a specific key +* from the material +* +* @param pMat Pointer to the input material. May not be NULL +* @param pKey Key to search for. One of the AI_MATKEY_XXX constants. +* @param pOut Pointer to a buffer to receive the result. +* @param pMax Specifies the size of the given buffer, in float's. +* Receives the number of values (not bytes!) read. +*/ +// --------------------------------------------------------------------------- +aiReturn aiGetMaterialFloatArray(const aiMaterial* pMat, + const char* pKey, + float* pOut, + unsigned int* pMax); + +#ifdef __cplusplus +// inline it +inline aiReturn aiGetMaterialFloat(const aiMaterial* pMat, + const char* pKey, + float* pOut) + {return aiGetMaterialFloatArray(pMat,pKey,pOut,(unsigned int*)0x0);} +#else +// use our friend, the C preprocessor +#define aiGetMaterialFloat (pMat, pKey, pOut) \ + aiGetMaterialFloatArray(pMat, pKey, pOut, NULL) +#endif //!__cplusplus + + +// --------------------------------------------------------------------------- +/** Retrieve an array of integer values with a specific key +* from the material +* +* @param pMat Pointer to the input material. May not be NULL +* @param pKey Key to search for. One of the AI_MATKEY_XXX constants. +* @param pOut Pointer to a buffer to receive the result. +* @param pMax Specifies the size of the given buffer, in int's. +* Receives the number of values (not bytes!) read. +*/ +// --------------------------------------------------------------------------- +aiReturn aiGetMaterialIntegerArray(const aiMaterial* pMat, + const char* pKey, + int* pOut, + unsigned int* pMax); + +#ifdef __cplusplus +// inline it +inline aiReturn aiGetMaterialInteger(const aiMaterial* pMat, + const char* pKey, + int* pOut) + {return aiGetMaterialIntegerArray(pMat,pKey,pOut,(unsigned int*)0x0);} +#else +// use our friend, the C preprocessor +#define aiGetMaterialInteger (pMat, pKey, pOut) \ + aiGetMaterialIntegerArray(pMat, pKey, pOut, NULL) +#endif //!__cplusplus + + + +// --------------------------------------------------------------------------- +/** Retrieve a color value from the material property table +* +* @param pMat Pointer to the input material. May not be NULL +* @param pKey Key to search for. One of the AI_MATKEY_XXX constants. +* @param pOut Pointer to a buffer to receive the result. +*/ +// --------------------------------------------------------------------------- +aiReturn aiGetMaterialColor(const aiMaterial* pMat, + const char* pKey, + aiColor4D* pOut); + + +// --------------------------------------------------------------------------- +/** Retrieve a string from the material property table +* +* @param pMat Pointer to the input material. May not be NULL +* @param pKey Key to search for. One of the AI_MATKEY_XXX constants. +* @param pOut Pointer to a buffer to receive the result. +*/ +// --------------------------------------------------------------------------- +aiReturn aiGetMaterialString(const aiMaterial* pMat, + const char* pKey, + aiString* pOut); + + +#ifdef __cplusplus +} +#endif //!__cplusplus + +#endif //!!AI_MATERIAL_H_INC diff --git a/include/aiMatrix3x3.h b/include/aiMatrix3x3.h new file mode 100644 index 000000000..02a7b9365 --- /dev/null +++ b/include/aiMatrix3x3.h @@ -0,0 +1,50 @@ +/** @file Definition of a 3x3 matrix, including operators when compiling in C++ */ +#ifndef AI_MATRIX3x3_H_INC +#define AI_MATRIX3x3_H_INC + +#ifdef __cplusplus +extern "C" { +#endif + +struct aiMatrix4x4; + +// --------------------------------------------------------------------------- +/** Represents a column-major 3x3 matrix +*/ +// --------------------------------------------------------------------------- +typedef struct aiMatrix3x3 +{ +#ifdef __cplusplus + aiMatrix3x3 () : + a1(1.0f), a2(0.0f), a3(0.0f), + b1(0.0f), b2(1.0f), b3(0.0f), + c1(0.0f), c2(0.0f), c3(1.0f) {} + + aiMatrix3x3 ( float _a1, float _a2, float _a3, + float _b1, float _b2, float _b3, + float _c1, float _c2, float _c3) : + a1(_a1), a2(_a2), a3(_a3), + b1(_b1), b2(_b2), b3(_b3), + c1(_c1), c2(_c2), c3(_c3) + {} + + /** Construction from a 4x4 matrix. The remaining parts of the matrix are ignored. */ + explicit aiMatrix3x3( const aiMatrix4x4& pMatrix); + + aiMatrix3x3& operator *= (const aiMatrix3x3& m); + aiMatrix3x3 operator* (const aiMatrix3x3& m) const; + aiMatrix3x3& Transpose(); + +#endif // __cplusplus + + + float a1, a2, a3; + float b1, b2, b3; + float c1, c2, c3; +} aiMatrix3x3_t; + +#ifdef __cplusplus +} // end of extern C +#endif + +#endif // AI_MATRIX3x3_H_INC \ No newline at end of file diff --git a/include/aiMatrix3x3.inl b/include/aiMatrix3x3.inl new file mode 100644 index 000000000..015588aa2 --- /dev/null +++ b/include/aiMatrix3x3.inl @@ -0,0 +1,54 @@ +/** @file Inline implementation of the 3x3 matrix operators */ +#ifndef AI_MATRIX3x3_INL_INC +#define AI_MATRIX3x3_INL_INC + +#include "aiMatrix3x3.h" + +#ifdef __cplusplus +#include "aiMatrix4x4.h" +#include + +// ------------------------------------------------------------------------------------------------ +// Construction from a 4x4 matrix. The remaining parts of the matrix are ignored. +inline aiMatrix3x3::aiMatrix3x3( const aiMatrix4x4& pMatrix) +{ + a1 = pMatrix.a1; a2 = pMatrix.a2; a3 = pMatrix.a3; + b1 = pMatrix.b1; b2 = pMatrix.b2; b3 = pMatrix.b3; + c1 = pMatrix.c1; c2 = pMatrix.c2; c3 = pMatrix.c3; +} + +// ------------------------------------------------------------------------------------------------ +inline aiMatrix3x3& aiMatrix3x3::operator *= (const aiMatrix3x3& m) +{ + *this = aiMatrix3x3( + m.a1 * a1 + m.b1 * a2 + m.c1 * a3, + m.a2 * a1 + m.b2 * a2 + m.c2 * a3, + m.a3 * a1 + m.b3 * a2 + m.c3 * a3, + m.a1 * b1 + m.b1 * b2 + m.c1 * b3, + m.a2 * b1 + m.b2 * b2 + m.c2 * b3, + m.a3 * b1 + m.b3 * b2 + m.c3 * b3, + m.a1 * c1 + m.b1 * c2 + m.c1 * c3, + m.a2 * c1 + m.b2 * c2 + m.c2 * c3, + m.a3 * c1 + m.b3 * c2 + m.c3 * c3); + return *this; +} + +// ------------------------------------------------------------------------------------------------ +inline aiMatrix3x3 aiMatrix3x3::operator* (const aiMatrix3x3& m) const +{ + aiMatrix3x3 temp( *this); + temp *= m; + return temp; +} + +// ------------------------------------------------------------------------------------------------ +inline aiMatrix3x3& aiMatrix3x3::Transpose() +{ + std::swap( a2, b1); + std::swap( a3, c1); + std::swap( b3, c2); +} + + +#endif // __cplusplus +#endif // AI_MATRIX3x3_INL_INC \ No newline at end of file diff --git a/include/aiMatrix4x4.h b/include/aiMatrix4x4.h new file mode 100644 index 000000000..71f70254c --- /dev/null +++ b/include/aiMatrix4x4.h @@ -0,0 +1,77 @@ +/** @file 4x4 matrix structure, including operators when compiling in C++ */ +#ifndef AI_MATRIX4X4_H_INC +#define AI_MATRIX4X4_H_INC + +#ifdef __cplusplus +extern "C" { +#endif + +struct aiMatrix3x3; + +// Set packing to 4 +#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__) + #pragma pack(push,4) + #define PACK_STRUCT +#elif defined( __GNUC__ ) + #define PACK_STRUCT __attribute__((packed)) +#else + #error Compiler not supported +#endif + +// --------------------------------------------------------------------------- +/** Represents a column-major 4x4 matrix, +* use this for homogenious coordinates +*/ +// --------------------------------------------------------------------------- +typedef struct aiMatrix4x4 +{ +#ifdef __cplusplus + aiMatrix4x4 () : + a1(1.0f), a2(0.0f), a3(0.0f), a4(0.0f), + b1(0.0f), b2(1.0f), b3(0.0f), b4(0.0f), + c1(0.0f), c2(0.0f), c3(1.0f), c4(0.0f), + d1(0.0f), d2(0.0f), d3(0.0f), d4(1.0f){} + + aiMatrix4x4 ( float _a1, float _a2, float _a3, float _a4, + float _b1, float _b2, float _b3, float _b4, + float _c1, float _c2, float _c3, float _c4, + float _d1, float _d2, float _d3, float _d4) : + a1(_a1), a2(_a2), a3(_a3), a4(_a4), + b1(_b1), b2(_b2), b3(_b3), b4(_b4), + c1(_c1), c2(_c2), c3(_c3), c4(_c4), + d1(_d1), d2(_d2), d3(_d3), d4(_d4) + {} + + /** Constructor from 3x3 matrix. The remaining elements are set to identity. */ + explicit aiMatrix4x4( const aiMatrix3x3& m); + + aiMatrix4x4& operator *= (const aiMatrix4x4& m); + aiMatrix4x4 operator* (const aiMatrix4x4& m) const; + aiMatrix4x4& Transpose(); + aiMatrix4x4& Inverse(); + float Determinant() const; + + float* operator[](unsigned int p_iIndex); + const float* operator[](unsigned int p_iIndex) const; +#endif // __cplusplus + + float a1, a2, a3, a4; + float b1, b2, b3, b4; + float c1, c2, c3, c4; + float d1, d2, d3, d4; + +} PACK_STRUCT aiMatrix4x4_t; + + +// Reset packing +#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__) +#pragma pack( pop ) +#endif +#undef PACK_STRUCT + +#ifdef __cplusplus +} // end extern "C" + + +#endif // __cplusplus +#endif // AI_MATRIX4X4_H_INC diff --git a/include/aiMatrix4x4.inl b/include/aiMatrix4x4.inl new file mode 100644 index 000000000..41af9c66a --- /dev/null +++ b/include/aiMatrix4x4.inl @@ -0,0 +1,136 @@ +/** @file Inline implementation of the 4x4 matrix operators */ +#ifndef AI_MATRIX4x4_INL_INC +#define AI_MATRIX4x4_INL_INC + +#include "aiMatrix4x4.h" + +#ifdef __cplusplus +#include "aiMatrix3x3.h" + +#include +#include +#include + +// --------------------------------------------------------------------------- +inline aiMatrix4x4::aiMatrix4x4( const aiMatrix3x3& m) +{ + a1 = m.a1; a2 = m.a2; a3 = m.a3; a4 = 0.0f; + b1 = m.b1; b2 = m.b2; b3 = m.b3; b4 = 0.0f; + c1 = m.c1; c2 = m.c2; c3 = m.c3; c4 = 0.0f; + d1 = 0.0f; d2 = 0.0f; d3 = 0.0f; d4 = 1.0f; +} + +// --------------------------------------------------------------------------- +inline aiMatrix4x4& aiMatrix4x4::operator *= (const aiMatrix4x4& m) +{ + *this = aiMatrix4x4( + m.a1 * a1 + m.b1 * a2 + m.c1 * a3 + m.d1 * a4, + m.a2 * a1 + m.b2 * a2 + m.c2 * a3 + m.d2 * a4, + m.a3 * a1 + m.b3 * a2 + m.c3 * a3 + m.d3 * a4, + m.a4 * a1 + m.b4 * a2 + m.c4 * a3 + m.d4 * a4, + m.a1 * b1 + m.b1 * b2 + m.c1 * b3 + m.d1 * b4, + m.a2 * b1 + m.b2 * b2 + m.c2 * b3 + m.d2 * b4, + m.a3 * b1 + m.b3 * b2 + m.c3 * b3 + m.d3 * b4, + m.a4 * b1 + m.b4 * b2 + m.c4 * b3 + m.d4 * b4, + m.a1 * c1 + m.b1 * c2 + m.c1 * c3 + m.d1 * c4, + m.a2 * c1 + m.b2 * c2 + m.c2 * c3 + m.d2 * c4, + m.a3 * c1 + m.b3 * c2 + m.c3 * c3 + m.d3 * c4, + m.a4 * c1 + m.b4 * c2 + m.c4 * c3 + m.d4 * c4, + m.a1 * d1 + m.b1 * d2 + m.c1 * d3 + m.d1 * d4, + m.a2 * d1 + m.b2 * d2 + m.c2 * d3 + m.d2 * d4, + m.a3 * d1 + m.b3 * d2 + m.c3 * d3 + m.d3 * d4, + m.a4 * d1 + m.b4 * d2 + m.c4 * d3 + m.d4 * d4); + return *this; +} + +// --------------------------------------------------------------------------- +inline aiMatrix4x4 aiMatrix4x4::operator* (const aiMatrix4x4& m) const +{ + aiMatrix4x4 temp( *this); + temp *= m; + return temp; +} + + +// --------------------------------------------------------------------------- +inline aiMatrix4x4& aiMatrix4x4::Transpose() +{ + std::swap( (float&)b1, (float&)a2); + std::swap( (float&)c1, (float&)a3); + std::swap( (float&)c2, (float&)b3); + std::swap( (float&)d1, (float&)a4); + std::swap( (float&)d2, (float&)b4); + std::swap( (float&)d3, (float&)c4); + return *this; +} + + +// --------------------------------------------------------------------------- +inline float aiMatrix4x4::Determinant() const +{ + return a1*b2*c3*d4 - a1*b2*c4*d3 + a1*b3*c4*d2 - a1*b3*c2*d4 + + a1*b4*c2*d3 - a1*b4*c3*d2 - a2*b3*c4*d1 + a2*b3*c1*d4 + - a2*b4*c1*d3 + a2*b4*c3*d1 - a2*b1*c3*d4 + a2*b1*c4*d3 + + a3*b4*c1*d2 - a3*b4*c2*d1 + a3*b1*c2*d4 - a3*b1*c4*d2 + + a3*b2*c4*d1 - a3*b2*c1*d4 - a4*b1*c2*d3 + a4*b1*c3*d2 + - a4*b2*c3*d1 + a4*b2*c1*d3 - a4*b3*c1*d2 + a4*b3*c2*d1; +} + +// --------------------------------------------------------------------------- +inline aiMatrix4x4& aiMatrix4x4::Inverse() +{ + // Compute the reciprocal determinant + float det = Determinant(); + if(det == 0.0f) + { + *this = aiMatrix4x4( + std::numeric_limits::quiet_NaN(),std::numeric_limits::quiet_NaN(), + std::numeric_limits::quiet_NaN(),std::numeric_limits::quiet_NaN(), + std::numeric_limits::quiet_NaN(),std::numeric_limits::quiet_NaN(), + std::numeric_limits::quiet_NaN(),std::numeric_limits::quiet_NaN(), + std::numeric_limits::quiet_NaN(),std::numeric_limits::quiet_NaN(), + std::numeric_limits::quiet_NaN(),std::numeric_limits::quiet_NaN(), + std::numeric_limits::quiet_NaN(),std::numeric_limits::quiet_NaN(), + std::numeric_limits::quiet_NaN(),std::numeric_limits::quiet_NaN()); + return *this; + } + + float invdet = 1.0f / det; + + aiMatrix4x4 res; + res.a1 = invdet * (b2 * (c3 * d4 - c4 * d3) + b3 * (c4 * d2 - c2 * d4) + b4 * (c2 * d3 - c3 * d2)); + res.a2 = -invdet * (a2 * (c3 * d4 - c4 * d3) + a3 * (c4 * d2 - c2 * d4) + a4 * (c2 * d3 - c3 * d2)); + res.a3 = invdet * (a2 * (b3 * d4 - b4 * d3) + a3 * (b4 * d2 - b2 * d4) + a4 * (b2 * d3 - b3 * d2)); + res.a4 = -invdet * (a2 * (b3 * c4 - b4 * c3) + a3 * (b4 * c2 - b2 * c4) + a4 * (b2 * c3 - b3 * c2)); + res.b1 = -invdet * (b1 * (c3 * d4 - c4 * d3) + b3 * (c4 * d1 - c1 * d4) + b4 * (c1 * d3 - c3 * d1)); + res.b2 = invdet * (a1 * (c3 * d4 - c4 * d3) + a3 * (c4 * d1 - c1 * d4) + a4 * (c1 * d3 - c3 * d1)); + res.b3 = -invdet * (a1 * (b3 * d4 - b4 * d3) + a3 * (b4 * d1 - b1 * d4) + a4 * (b1 * d3 - b3 * d1)); + res.b4 = invdet * (a1 * (b3 * c4 - b4 * c3) + a3 * (b4 * c1 - b1 * c4) + a4 * (b1 * c3 - b3 * c1)); + res.c1 = invdet * (b1 * (c2 * d4 - c4 * d2) + b2 * (c4 * d1 - c1 * d4) + b4 * (c1 * d2 - c2 * d1)); + res.c2 = -invdet * (a1 * (c2 * d4 - c4 * d2) + a2 * (c4 * d1 - c1 * d4) + a4 * (c1 * d2 - c2 * d1)); + res.c3 = invdet * (a1 * (b2 * d4 - b4 * d2) + a2 * (b4 * d1 - b1 * d4) + a4 * (b1 * d2 - b2 * d1)); + res.c4 = -invdet * (a1 * (b2 * c4 - b4 * c2) + a2 * (b4 * c1 - b1 * c4) + a4 * (b1 * c2 - b2 * c1)); + res.d1 = -invdet * (b1 * (c2 * d3 - c3 * d2) + b2 * (c3 * d1 - c1 * d3) + b3 * (c1 * d2 - c2 * d1)); + res.d2 = invdet * (a1 * (c2 * d3 - c3 * d2) + a2 * (c3 * d1 - c1 * d3) + a3 * (c1 * d2 - c2 * d1)); + res.d3 = -invdet * (a1 * (b2 * d3 - b3 * d2) + a2 * (b3 * d1 - b1 * d3) + a3 * (b1 * d2 - b2 * d1)); + res.d4 = invdet * (a1 * (b2 * c3 - b3 * c2) + a2 * (b3 * c1 - b1 * c3) + a3 * (b1 * c2 - b2 * c1)); + *this = res; + + return *this; +} + +// --------------------------------------------------------------------------- +inline float* aiMatrix4x4::operator[](unsigned int p_iIndex) +{ + return &this->a1 + p_iIndex * 4; +} + + +// --------------------------------------------------------------------------- +inline const float* aiMatrix4x4::operator[](unsigned int p_iIndex) const +{ + return &this->a1 + p_iIndex * 4; +} + +#endif // __cplusplus +#endif // AI_MATRIX4x4_INL_INC diff --git a/include/aiMesh.h b/include/aiMesh.h new file mode 100644 index 000000000..fa0ba2050 --- /dev/null +++ b/include/aiMesh.h @@ -0,0 +1,268 @@ +/** @file Defines the data structures in which the imported geometry is returned. */ +#ifndef AI_MESH_H_INC +#define AI_MESH_H_INC + +#include "aiTypes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// --------------------------------------------------------------------------- +/** A single face in a mesh, referring to multiple vertices. +* If mNumIndices is 3, the face is a triangle, for mNumIndices > 3 it's a polygon. +*/ +// --------------------------------------------------------------------------- +struct aiFace +{ + unsigned int mNumIndices; ///< Number of indices defining this face. 3 for a triangle, >3 for polygon + unsigned int* mIndices; ///< Pointer to the indices array. Size of the array is given in numIndices. + +#ifdef __cplusplus + aiFace() + { + mNumIndices = 0; mIndices = NULL; + } + + ~aiFace() + { + delete [] mIndices; + } + + aiFace( const aiFace& o) + { + mIndices = NULL; + *this = o; + } + + const aiFace& operator = ( const aiFace& o) + { + if (&o == this) + return *this; + + delete mIndices; + mNumIndices = o.mNumIndices; + mIndices = new unsigned int[mNumIndices]; + memcpy( mIndices, o.mIndices, mNumIndices * sizeof( unsigned int)); + return *this; + } + +#endif // __cplusplus +}; + + +// --------------------------------------------------------------------------- +/** A single influence of a bone on a vertex. */ +// --------------------------------------------------------------------------- +struct aiVertexWeight +{ + unsigned int mVertexId; ///< Index of the vertex which is influenced by the bone. + float mWeight; ///< The strength of the influence in the range (0...1). The influence from all bones at one vertex amounts to 1. + +#ifdef __cplusplus + aiVertexWeight() { } + aiVertexWeight( unsigned int pID, float pWeight) : mVertexId( pID), mWeight( pWeight) { } +#endif // __cplusplus +}; + + +// --------------------------------------------------------------------------- +/** A single bone of a mesh. A bone has a name by which it can be found +* in the frame hierarchy and by which it can be addressed by animations. +* In addition it has a number of influences on vertices. +*/ +// --------------------------------------------------------------------------- +struct aiBone +{ + aiString mName; ///< The name of the bone. + unsigned int mNumWeights; ///< The number of vertices affected by this bone + aiVertexWeight* mWeights; ///< The vertices affected by this bone + aiMatrix4x4 mOffsetMatrix; ///< Matrix that transforms from mesh space to bone space in bind pose + +#ifdef __cplusplus + aiBone() + { + mNumWeights = 0; mWeights = NULL; + } + + ~aiBone() + { + delete [] mWeights; + } +#endif // __cplusplus +}; + + +/** Maximum number of vertex color sets per mesh. +* +* Diffuse, specular, ambient and emissive +*/ +#define AI_MAX_NUMBER_OF_COLOR_SETS 0x4 + + +/** Maximum number of texture coord sets (UV channels) per mesh +*/ +#define AI_MAX_NUMBER_OF_TEXTURECOORDS 0x4 + +// --------------------------------------------------------------------------- +/** A mesh represents a geometry or model with a single material. +* +* It usually consists of a number of vertices and a series of primitives/faces +* referencing the vertices. In addition there might be a series of bones, each +* of them addressing a number of vertices with a certain weight. Vertex data is +* presented in channels with each channel containing a single per-vertex +* information such as a set of texture coords or a normal vector. +* If a data pointer is non-null, the corresponding data stream is present. +* From C++-programs you can also use the comfort functions Has*() to +* test for the presence of various data streams. +* +* A Mesh uses only a single material which is referenced by a material ID. +*/ +struct aiMesh +{ + /** The number of vertices in this mesh. + * This is also the size of all of the per-vertex data arrays + */ + unsigned int mNumVertices; + + /** The number of primitives (triangles, polygones, lines) in this mesh. + * This is also the size of the mFaces array + */ + unsigned int mNumFaces; + + /** Vertex positions. + * This array is always present in a mesh. The array is + * mNumVertices in size. + */ + aiVector3D_t* mVertices; + + /** Vertex normals. + * The array contains normalized vectors, NULL if not present. + * The array is mNumVertices in size. + */ + aiVector3D_t* mNormals; + + /** Vertex tangents. + * The tangent of a vertex points in the direction of the positive + * X texture axis. The array contains normalized vectors, NULL if + * not present. The array is mNumVertices in size. + * @note If the mesh contains tangents, it automatically also + * contains bitangents. + */ + aiVector3D_t* mTangents; + + /** Vertex bitangents. + * The bitangent of a vertex points in the direction of the positive + * Y texture axis. The array contains normalized vectors, NULL if not + * present. The array is mNumVertices in size. + * @note If the mesh contains tangents, it automatically also contains + * bitangents. + */ + aiVector3D_t* mBitangents; + + /** Vertex color sets. + * A mesh may contain 0 to #AI_MAX_NUMBER_OF_COLOR_SETS vertex + * colors per vertex. NULL if not present. Each array is + * mNumVertices in size if present. + */ + aiColor4D_t* mColors[AI_MAX_NUMBER_OF_COLOR_SETS]; + + /** Vertex texture coords, also known as UV channels. + * A mesh may contain 0 to AI_MAX_NUMBER_OF_TEXTURECOORDS per + * vertex. NULL if not present. The array is mNumVertices in size. + */ + aiVector3D_t* mTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; + + /** Specifies the number of components for a given UV channel. + * Up to three channels are supported (UVW, for accessing volume + * or cube maps). If the value is 2 for a given channel n, the + * component p.z of mTextureCoords[n][p] is set to 0.0f. + * If the value is 1 for a given channel, p.y is set to 0.0f, too. + * @note 4D coords are not supported + */ + unsigned int mNumUVComponents[AI_MAX_NUMBER_OF_TEXTURECOORDS]; + + /** The faces the mesh is contstructed from. + * Each face referres to a number of vertices by their indices. + * This array is always present in a mesh, its size is given + * in mNumFaces. + */ + aiFace* mFaces; + + /** The number of bones this mesh contains. + * Can be 0, in which case the mBones array is NULL. + */ + unsigned int mNumBones; + + /** The bones of this mesh. + * A bone consists of a name by which it can be found in the + * frame hierarchy and a set of vertex weights. + */ + aiBone** mBones; + + /** The material used by this mesh. + * A mesh does use only a single material. If an imported model uses multiple materials, + * the import splits up the mesh. Use this value as index into the scene's material list. + */ + unsigned int mMaterialIndex; + +#ifdef __cplusplus + aiMesh() + { + mNumVertices = 0; mNumFaces = 0; + mVertices = NULL; mFaces = NULL; + mNormals = NULL; mTangents = NULL; + mBitangents = NULL; + for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++) + { + mNumUVComponents[a] = 0; + mTextureCoords[a] = NULL; + } + for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++) + mColors[a] = NULL; + mNumBones = 0; mBones = NULL; + mMaterialIndex = 0; + } + + ~aiMesh() + { + delete [] mVertices; + delete [] mFaces; + delete [] mNormals; + delete [] mTangents; + delete [] mBitangents; + for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++) + delete [] mTextureCoords[a]; + for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++) + delete [] mColors[a]; + for( unsigned int a = 0; a < mNumBones; a++) + delete mBones[a]; + delete [] mBones; + } + + bool HasNormals() const { return mNormals != NULL; } + bool HasTangentsAndBitangents() const { return mTangents != NULL && mBitangents != NULL; } + bool HasVertexColors( unsigned int pIndex) + { + if( pIndex >= AI_MAX_NUMBER_OF_COLOR_SETS) + return false; + else + return mColors[pIndex] != NULL; + } + bool HasTextureCoords( unsigned int pIndex) + { + if( pIndex > AI_MAX_NUMBER_OF_TEXTURECOORDS) + return false; + else + return mTextureCoords[pIndex] != NULL; + } + bool HasBones() const { return mBones != NULL; } + +#endif // __cplusplus +}; + +#ifdef __cplusplus +} +#endif + +#endif // AI_MESH_H_INC \ No newline at end of file diff --git a/include/aiPostProcess.h b/include/aiPostProcess.h new file mode 100644 index 000000000..acf5a9998 --- /dev/null +++ b/include/aiPostProcess.h @@ -0,0 +1,78 @@ +/** @file Definitions for import post processing steps */ +#ifndef AI_POSTPROCESS_H_INC +#define AI_POSTPROCESS_H_INC + +#ifdef __cplusplus +extern "C" { +#endif + +/** Defines the flags for all possible post processing steps. */ +enum aiPostProcessSteps +{ + /** Calculates the binormals and tangents for the imported meshes. Does nothing + * if a mesh does not have normals. You might want this post processing step to be + * executed if you plan to use tangent space calculations such as normal mapping + * applied to the meshes. + */ + aiProcess_CalcTangentSpace = 1, + + /** Identifies and joins identical vertex data sets within all imported meshes. + * After this step is run each mesh does contain only unique vertices anymore, + * so a vertex is possibly used by multiple faces. You propably always want + * to use this post processing step.*/ + aiProcess_JoinIdenticalVertices = 2, + + /** Converts all the imported data to a left-handed coordinate space such as + * the DirectX coordinate system. By default the data is returned in a right-handed + * coordinate space which for example OpenGL preferres. In this space, +X points to the + * right, +Y points upwards and +Z points to the viewer. In the DirectX coordinate space + * +X points to the right, +Y points upwards and +Z points away from the viewer + * into the screen. + */ + aiProcess_ConvertToLeftHanded = 4, + + /** Triangulates all faces of all meshes. By default the imported mesh data might + * contain faces with more than 3 indices. For rendering a mesh you usually need + * all faces to be triangles. This post processing step splits up all higher faces + * to triangles. + */ + aiProcess_Triangulate = 8, + + + /** Omits all normals found in the file. This can be used together + * with either the aiProcess_GenNormals or the aiProcess_GenSmoothNormals + * flag to force the recomputation of the normals. + */ + aiProcess_KillNormals = 0x10, + + + /** Generates normals for all faces of all meshes. The normals are shared + * between the three vertices of a face. This is ignored + * if normals are already existing. This flag may not be specified together + * with aiProcess_GenSmoothNormals + */ + aiProcess_GenNormals = 0x20, + + + /** Generates smooth normals for all vertices in the mesh. This is ignored + * if normals are already existing. This flag may not be specified together + * with aiProcess_GenNormals + */ + aiProcess_GenSmoothNormals = 0x40, + + + /** Splits large meshes into submeshes + * This is quite useful for realtime rendering where the number of vertices + * is usually limited by the video driver. + * + * A mesh is split if it consists of more than 1 * 10^6 vertices. This is defined + * in the internal SplitLargeMeshes.h header as AI_SLM_MAX_VERTICES. + */ + aiProcess_SplitLargeMeshes = 0x80 +}; + +#ifdef __cplusplus +} // end of extern "C" +#endif + +#endif // AI_POSTPROCESS_H_INC \ No newline at end of file diff --git a/include/aiQuaternion.h b/include/aiQuaternion.h new file mode 100644 index 000000000..153aa495f --- /dev/null +++ b/include/aiQuaternion.h @@ -0,0 +1,96 @@ +/** @file Quaternion structure, including operators when compiling in C++ */ +#ifndef AI_QUATERNION_H_INC +#define AI_QUATERNION_H_INC + +#include +#include "aiTypes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// --------------------------------------------------------------------------- +/** Represents a quaternion in a 4D vector. */ +typedef struct aiQuaternion +{ +#ifdef __cplusplus + aiQuaternion() : w(0.0f), x(0.0f), y(0.0f), z(0.0f) {} + aiQuaternion(float _w, float _x, float _y, float _z) : w(_w), x(_x), y(_y), z(_z) {} + /** Construct from rotation matrix. Result is undefined if the matrix is not orthonormal. */ + aiQuaternion( const aiMatrix3x3& pRotMatrix); + + /** Returns a matrix representation of the quaternion */ + aiMatrix3x3 GetMatrix() const; +#endif // __cplusplus + + float w, x, y, z; +} aiQuaternion_t; + + +#ifdef __cplusplus + +// --------------------------------------------------------------------------- +// Constructs a quaternion from a rotation matrix +inline aiQuaternion::aiQuaternion( const aiMatrix3x3 &pRotMatrix) +{ + float t = 1 + pRotMatrix.a1 + pRotMatrix.b2 + pRotMatrix.c3; + + // large enough + if( t > 0.00001f) + { + float s = sqrt( t) * 2.0f; + x = (pRotMatrix.b3 - pRotMatrix.c2) / s; + y = (pRotMatrix.c1 - pRotMatrix.a3) / s; + z = (pRotMatrix.a2 - pRotMatrix.b1) / s; + w = 0.25f * s; + } // else we have to check several cases + else if( pRotMatrix.a1 > pRotMatrix.b2 && pRotMatrix.a1 > pRotMatrix.c3 ) + { + // Column 0: + float s = sqrt( 1.0f + pRotMatrix.a1 - pRotMatrix.b2 - pRotMatrix.c3) * 2.0f; + x = 0.25f * s; + y = (pRotMatrix.a2 + pRotMatrix.b1) / s; + z = (pRotMatrix.c1 + pRotMatrix.a3) / s; + w = (pRotMatrix.b3 - pRotMatrix.c2) / s; + } else + if( pRotMatrix.b2 > pRotMatrix.c3) + { + // Column 1: + float s = sqrt( 1.0f + pRotMatrix.b2 - pRotMatrix.a1 - pRotMatrix.c3) * 2.0f; + x = (pRotMatrix.a2 + pRotMatrix.b1) / s; + y = 0.25f * s; + z = (pRotMatrix.b3 + pRotMatrix.c2) / s; + w = (pRotMatrix.c1 - pRotMatrix.a3) / s; + } else + { + // Column 2: + float s = sqrt( 1.0f + pRotMatrix.c3 - pRotMatrix.a1 - pRotMatrix.b2) * 2.0f; + x = (pRotMatrix.c1 + pRotMatrix.a3) / s; + y = (pRotMatrix.b3 + pRotMatrix.c2) / s; + z = 0.25f * s; + w = (pRotMatrix.a2 - pRotMatrix.b1) / s; + } +} + +// --------------------------------------------------------------------------- +// Returns a matrix representation of the quaternion +inline aiMatrix3x3 aiQuaternion::GetMatrix() const +{ + aiMatrix3x3 resMatrix; + resMatrix.a1 = 1.0f - 2.0f * (y * y + z * z); + resMatrix.a2 = 2.0f * (x * y + z * w); + resMatrix.a3 = 2.0f * (x * z - y * w); + resMatrix.b1 = 2.0f * (x * y - z * w); + resMatrix.b2 = 1.0f - 2.0f * (x * x + z * z); + resMatrix.b3 = 2.0f * (y * z + x * w); + resMatrix.c1 = 2.0f * (x * z + y * w); + resMatrix.c2 = 2.0f * (y * z - x * w); + resMatrix.c3 = 1.0f - 2.0f * (x * x + y * y); + + return resMatrix; +} + +} // end extern "C" +#endif // __cplusplus + +#endif // AI_QUATERNION_H_INC \ No newline at end of file diff --git a/include/aiScene.h b/include/aiScene.h new file mode 100644 index 000000000..6eb747359 --- /dev/null +++ b/include/aiScene.h @@ -0,0 +1,147 @@ +/** @file Defines the data structures in which the imported scene is returned. */ +#ifndef AI_SCENE_H_INC +#define AI_SCENE_H_INC + +#include "aiTypes.h" +#include "aiMesh.h" +#include "aiMaterial.h" +#include "aiAnim.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +// --------------------------------------------------------------------------- +/** A node in the imported hierarchy. +* +* Each node has name, a parent node (except for the root node), +* a transformation relative to its parent and possibly several child nodes. +* Simple file formats don't support hierarchical structures, for these formats +* the imported scene does consist of only a single root node with no childs. +*/ +// --------------------------------------------------------------------------- +struct aiNode +{ + /** The name of the node. + * + * The name might be empty (length of zero) but all nodes which + * need to be accessed afterwards by bones or anims are usually named. + */ + aiString mName; + + /** The transformation relative to the node's parent. */ + aiMatrix4x4 mTransformation; + + /** Parent node. NULL if this node is the root node. */ + aiNode* mParent; + + /** The number of child nodes of this node. */ + unsigned int mNumChildren; + /** The child nodes of this node. NULL if mNumChildren is 0. */ + + aiNode** mChildren; + + /** The number of meshes of this node. */ + unsigned int mNumMeshes; + + /** The meshes of this node. Each entry is an index into the mesh */ + unsigned int* mMeshes; + +#ifdef __cplusplus + /** Constructor */ + aiNode() + { + mParent = NULL; + mNumChildren = 0; mChildren = NULL; + mNumMeshes = 0; mMeshes = NULL; + } + + /** Destructor */ + ~aiNode() + { + for( unsigned int a = 0; a < mNumChildren; a++) + delete mChildren[a]; + delete [] mChildren; + delete [] mMeshes; + } +#endif // __cplusplus +}; + + +// --------------------------------------------------------------------------- +/** The root structure of the imported data. +* +* Everything that was imported from the given file can be accessed from here. +*/ +// --------------------------------------------------------------------------- +struct aiScene +{ + /** The root node of the hierarchy. + * + * There will always be at least the root node if the import + * was successful. Presence of further nodes depends on the + * format and content of the imported file. + */ + aiNode* mRootNode; + + /** The number of meshes in the scene. */ + unsigned int mNumMeshes; + + /** The array of meshes. + * + * Use the indices given in the aiNode structure to access + * this array. The array is mNumMeshes in size. + */ + aiMesh** mMeshes; + + /** The number of materials in the scene. */ + unsigned int mNumMaterials; + + /** The array of materials. + * + * Use the index given in each aiMesh structure to access this + * array. The array is mNumMaterials in size. + */ + aiMaterial** mMaterials; + + /** The number of animations in the scene. */ + unsigned int mNumAnimations; + + /** The array of animations. + * + * All animations imported from the given file are listed here. + * The array is mNumAnimations in size. + */ + aiAnimation** mAnimations; + +#ifdef __cplusplus + aiScene() + { + mRootNode = NULL; + mNumMeshes = 0; mMeshes = NULL; + mNumMaterials = 0; mMaterials = NULL; + mNumAnimations = 0; mAnimations = NULL; + } + + ~aiScene() + { + delete mRootNode; + for( unsigned int a = 0; a < mNumMeshes; a++) + delete mMeshes[a]; + delete [] mMeshes; + for( unsigned int a = 0; a < mNumMaterials; a++) + delete mMaterials[a]; + delete [] mMaterials; + for( unsigned int a = 0; a < mNumAnimations; a++) + delete mAnimations[a]; + delete [] mAnimations; + } +#endif // __cplusplus +}; + +#ifdef __cplusplus +} +#endif + +#endif // AI_SCENE_H_INC diff --git a/include/aiTypes.h b/include/aiTypes.h new file mode 100644 index 000000000..cfaed04fd --- /dev/null +++ b/include/aiTypes.h @@ -0,0 +1,140 @@ +#ifndef AI_TYPES_H_INC +#define AI_TYPES_H_INC + +#include +#include + +#if (defined _MSC_VER) +# include "Compiler/VisualStudio/stdint.h" +#endif // (defined _MSC_VER) + +#include "aiVector3D.h" +#include "aiMatrix3x3.h" +#include "aiMatrix4x4.h" +#include "aiVector3D.inl" +#include "aiMatrix3x3.inl" +#include "aiMatrix4x4.inl" + +#ifdef __cplusplus +#include +extern "C" { +#endif + +/** Maximum dimension for strings, ASSIMP strings are zero terminated */ +const size_t MAXLEN = 1024; + +// --------------------------------------------------------------------------- +/** Represents a two-dimensional vector. +*/ +// --------------------------------------------------------------------------- +typedef struct aiVector2D +{ +#ifdef __cplusplus + aiVector2D () : x(0.0f), y(0.0f) {} + aiVector2D (float _x, float _y) : x(_x), y(_y) {} + aiVector2D (const aiVector2D& o) : x(o.x), y(o.y) {} + +#endif // __cplusplus + + float x, y; +} aiVector2D_t; + +// aiVector3D type moved to separate header due to size of operators + +// aiQuaternion type moved to separate header due to size of operators + +// aiMatrix4x4 type moved to separate header due to size of operators + +// --------------------------------------------------------------------------- +/** Represents a color in Red-Green-Blue space. +*/ +// --------------------------------------------------------------------------- +typedef struct aiColor3D +{ +#ifdef __cplusplus + aiColor3D () : r(0.0f), g(0.0f), b(0.0f) {} + aiColor3D (float _r, float _g, float _b) : r(_r), g(_g), b(_b) {} + aiColor3D (const aiColor3D& o) : r(o.r), g(o.g), b(o.b) {} + +#endif // __cplusplus + + float r, g, b; +} aiColor3D_t; + + +// --------------------------------------------------------------------------- +/** Represents a color in Red-Green-Blue space including an +* alpha component. +*/ +// --------------------------------------------------------------------------- +typedef struct aiColor4D +{ +#ifdef __cplusplus + aiColor4D () : r(0.0f), g(0.0f), b(0.0f), a(0.0f) {} + aiColor4D (float _r, float _g, float _b, float _a) + : r(_r), g(_g), b(_b), a(_a) {} + aiColor4D (const aiColor4D& o) + : r(o.r), g(o.g), b(o.b), a(o.a) {} + +#endif // __cplusplus + + float r, g, b, a; +} aiColor4D_t; + + +// --------------------------------------------------------------------------- +/** Represents a string, zero byte terminated +*/ +// --------------------------------------------------------------------------- +typedef struct aiString +{ +#ifdef __cplusplus + inline aiString() : + length(0) + { + // empty + } + + inline aiString(const aiString& rOther) : + length(rOther.length) + { + memcpy( data, rOther.data, rOther.length); + this->data[this->length] = '\0'; + } + + void Set( const std::string& pString) + { + if( pString.length() > MAXLEN - 1) + return; + length = pString.length(); + memcpy( data, pString.c_str(), length); + data[length] = 0; + } +#endif // __cplusplus + + size_t length; + char data[MAXLEN]; +} aiString_t; + + +// --------------------------------------------------------------------------- +/** Standard return type for all library functions. +* +* To check whether a function failed or not check against +* AI_SUCCESS. +*/ +// --------------------------------------------------------------------------- +enum aiReturn +{ + AI_SUCCESS = 0x0, + AI_FAILURE = -0x1, + AI_INVALIDFILE = -0x2, + AI_OUTOFMEMORY = -0x3, + AI_INVALIDARG = -0x4 +}; + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/include/aiVector3D.h b/include/aiVector3D.h new file mode 100644 index 000000000..bbd8911ae --- /dev/null +++ b/include/aiVector3D.h @@ -0,0 +1,99 @@ +/** @file 3D vector structure, including operators when compiling in C++ */ +#ifndef AI_VECTOR3D_H_INC +#define AI_VECTOR3D_H_INC + +#include + +#include "aiAssert.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// --------------------------------------------------------------------------- +/** Represents a three-dimensional vector. */ +typedef struct aiVector3D +{ +#ifdef __cplusplus + aiVector3D () : x(0.0f), y(0.0f), z(0.0f) {} + aiVector3D (float _x, float _y, float _z) : x(_x), y(_y), z(_z) {} + aiVector3D (const aiVector3D& o) : x(o.x), y(o.y), z(o.z) {} + + void Set( float pX, float pY, float pZ) { x = pX; y = pY; z = pZ; } + float SquareLength() const { return x*x + y*y + z*z; } + float Length() const { return sqrt( SquareLength()); } + aiVector3D& Normalize() { *this /= Length(); return *this; } + const aiVector3D& operator += (const aiVector3D& o) { x += o.x; y += o.y; z += o.z; return *this; } + const aiVector3D& operator -= (const aiVector3D& o) { x -= o.x; y -= o.y; z -= o.z; return *this; } + const aiVector3D& operator *= (float f) { x *= f; y *= f; z *= f; return *this; } + const aiVector3D& operator /= (float f) { x /= f; y /= f; z /= f; return *this; } + + inline float operator[](unsigned int i) const {return *(&x + i);} + inline float& operator[](unsigned int i) {return *(&x + i);} +#endif // __cplusplus + + float x, y, z; +} aiVector3D_t; + +#ifdef __cplusplus +} // end extern "C" + +// symmetric addition +inline aiVector3D operator + (const aiVector3D& v1, const aiVector3D& v2) +{ + return aiVector3D( v1.x + v2.x, v1.y + v2.y, v1.z + v2.z); +} + +// symmetric subtraction +inline aiVector3D operator - (const aiVector3D& v1, const aiVector3D& v2) +{ + return aiVector3D( v1.x - v2.x, v1.y - v2.y, v1.z - v2.z); +} + +// scalar product +inline float operator * (const aiVector3D& v1, const aiVector3D& v2) +{ + return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z; +} + +// scalar multiplication +inline aiVector3D operator * ( float f, const aiVector3D& v) +{ + return aiVector3D( f*v.x, f*v.y, f*v.z); +} + +// and the other way around +inline aiVector3D operator * ( const aiVector3D& v, float f) +{ + return aiVector3D( f*v.x, f*v.y, f*v.z); +} + +// scalar division +inline aiVector3D operator / ( const aiVector3D& v, float f) +{ + //ai_assert(0.0f != f); + return v * (1/f); +} + +// vector division +inline aiVector3D operator / ( const aiVector3D& v, const aiVector3D& v2) + { + //ai_assert(0.0f != v2.x && 0.0f != v2.y && 0.0f != v2.z); + return aiVector3D(v.x / v2.x,v.y / v2.y,v.z / v2.z); + } + +// cross product +inline aiVector3D operator ^ ( const aiVector3D& v1, const aiVector3D& v2) +{ + return aiVector3D( v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x); +} + +// vector inversion +inline aiVector3D operator - ( const aiVector3D& v) +{ + return aiVector3D( -v.x, -v.y, -v.z); +} + +#endif // __cplusplus + +#endif // AI_VECTOR3D_H_INC \ No newline at end of file diff --git a/include/aiVector3D.inl b/include/aiVector3D.inl new file mode 100644 index 000000000..e59e4614b --- /dev/null +++ b/include/aiVector3D.inl @@ -0,0 +1,33 @@ +/** @file Inline implementation of vector3D operators */ +#ifndef AI_VECTOR3D_INL_INC +#define AI_VECTOR3D_INL_INC + +#include "aiVector3D.h" + +#ifdef __cplusplus +#include "aiMatrix3x3.h" +#include "aiMatrix4x4.h" + +/** Transformation of a vector by a 3x3 matrix */ +inline aiVector3D operator * (const aiMatrix3x3& pMatrix, const aiVector3D& pVector) +{ + aiVector3D res; + res.x = pMatrix.a1 * pVector.x + pMatrix.a2 * pVector.y + pMatrix.a3 * pVector.z; + res.y = pMatrix.b1 * pVector.x + pMatrix.b2 * pVector.y + pMatrix.b3 * pVector.z; + res.z = pMatrix.c1 * pVector.x + pMatrix.c2 * pVector.y + pMatrix.c3 * pVector.z; + return res; +} + +/** Transformation of a vector by a 4x4 matrix */ +inline aiVector3D operator * (const aiMatrix4x4& pMatrix, const aiVector3D& pVector) +{ + aiVector3D res; + res.x = pMatrix.a1 * pVector.x + pMatrix.a2 * pVector.y + pMatrix.a3 * pVector.z + pMatrix.a4; + res.y = pMatrix.b1 * pVector.x + pMatrix.b2 * pVector.y + pMatrix.b3 * pVector.z + pMatrix.b4; + res.z = pMatrix.c1 * pVector.x + pMatrix.c2 * pVector.y + pMatrix.c3 * pVector.z + pMatrix.c4; + return res; +} + + +#endif // __cplusplus +#endif // AI_VECTOR3D_INL_INC \ No newline at end of file diff --git a/include/assimp.h b/include/assimp.h new file mode 100644 index 000000000..a3ae202dd --- /dev/null +++ b/include/assimp.h @@ -0,0 +1,77 @@ +/** @file Defines the C-API to the Asset Import Library. */ +#ifndef AI_ASSIMP_H_INC +#define AI_ASSIMP_H_INC + +#ifdef __cplusplus +extern "C" { +#endif + +struct aiScene; +struct aiFileIO; +//enum aiOrigin; + +// --------------------------------------------------------------------------- +/** Reads the given file and returns its content. +* +* If the call succeeds, the imported data is returned in an aiScene structure. +* The data is intended to be read-only, it stays property of the ASSIMP +* library and will be stable until aiReleaseImport() is called. After you're +* done with it, call aiReleaseImport() to free the resources associated with +* this file. If the import fails, NULL is returned instead. Call +* aiGetErrorString() to retrieve a human-readable error text. +* @param pFile Path and filename of the file to be imported, +* expected to be a null-terminated c-string. +* @param pFlags Optional post processing steps to be executed after +* a successful import. Provide a bitwise combination of the #aiPostProcessSteps +* flags. +* @return Pointer to the imported data or NULL if the import failed. +*/ +// --------------------------------------------------------------------------- +const aiScene* aiImportFile( const char* pFile, unsigned int pFlags); + + +// --------------------------------------------------------------------------- +/** Reads the given file using user-defined I/O functions and returns +* its content. +* +* If the call succeeds, the imported data is returned in an aiScene structure. +* The data is intended to be read-only, it stays property of the ASSIMP +* library and will be stable until aiReleaseImport() is called. After you're +* done with it, call aiReleaseImport() to free the resources associated with +* this file. If the import fails, NULL is returned instead. Call +* aiGetErrorString() to retrieve a human-readable error text. +* @param pFile aiFileIO structure. All functions pointers must be +* initialized. aiFileIO::OpenFunc() and aiFileIO::CloseFunc() +* will be used to open other files in the fs if the asset to be +* loaded depends on them. +* @return Pointer to the imported data or NULL if the import failed. +*/ +// --------------------------------------------------------------------------- +const aiScene* aiImportFileEx( const aiFileIO* pFile); + + + +// --------------------------------------------------------------------------- +/** Releases all resources associated with the given import process. +* +* Call this function after you're done with the imported data. +* @param pScene The imported data to release. +*/ +// --------------------------------------------------------------------------- +void aiReleaseImport( const aiScene* pScene); + + +// --------------------------------------------------------------------------- +/** Returns the error text of the last failed import process. +* +* @return A textual description of the error that occured at the last +* import process. NULL if there was no error. +*/ +// --------------------------------------------------------------------------- +const char* aiGetErrorString(); + +#ifdef __cplusplus +} +#endif + +#endif // AI_ASSIMP_H_INC \ No newline at end of file diff --git a/include/assimp.hpp b/include/assimp.hpp new file mode 100644 index 000000000..f2c56ed49 --- /dev/null +++ b/include/assimp.hpp @@ -0,0 +1,123 @@ +/** @file Defines the CPP-API to the Asset Import Library. */ +#ifndef AI_ASSIMP_HPP_INC +#define AI_ASSIMP_HPP_INC + +#ifndef __cplusplus +#error This header requires C++ to be used. +#endif + +#include +#include + +struct aiScene; + +namespace Assimp +{ + +class BaseImporter; +class BaseProcess; +class IOStream; +class IOSystem; + +// --------------------------------------------------------------------------- +/** The Importer class forms an C++ interface to the functionality of the +* Asset Import library. +* +* Create an object of this class and call ReadFile() to import a file. +* If the import succeeds, the function returns a pointer to the imported data. +* The data remains property of the object, it is intended to be accessed +* read-only. The imported data will be destroyed along with the Importer +* object. If the import failes, ReadFile() returns a NULL pointer. In this +* case you can retrieve a human-readable error description be calling +* GetErrorString(). +* +* If you need the Importer to do custom file handling to access the files, +* implement IOSystem and IOStream and supply an instance of your custom IOSystem +* implementation by calling SetIOHandler() before calling ReadFile(). If you +* do not assign a custion IO handler, a default handler using the standard C++ +* IO logic will be used. +*/ +class Importer +{ +public: + + // ------------------------------------------------------------------- + /** Constructor. Creates an empty importer object. + * + * Call ReadFile() to start the import process. + */ + Importer(); + + // ------------------------------------------------------------------- + /** Destructor. The object kept ownership of the imported data, + * which now will be destroyed along with the object. + */ + ~Importer(); + + // ------------------------------------------------------------------- + /** Supplies a custom IO handler to the importer to open and access files. + * If you need the importer to use custion IO logic to access the files, + * you need to provide a custom implementation of IOSystem and IOFile + * to the importer. Then create an instance of your custion IOSystem + * implementation and supply it by this function. + * + * The Importer takes ownership of the object and will destroy it afterwards. + * The previously assigned handler will be deleted. + * + * @param pIOHandler The IO handler to be used in all file accesses of the Importer. + */ + void SetIOHandler( IOSystem* pIOHandler); + + // ------------------------------------------------------------------- + /** Reads the given file and returns its contents if successful. + * + * If the call succeeds, the contents of the file are returned as a + * pointer to an aiScene object. The returned data is intended to be + * read-only, the importer object keeps ownership of the data and will + * destroy it upon destruction. If the import failes, NULL is returned. + * A human-readable error description can be retrieved by calling + * GetErrorString(). + * @param pFile Path and filename to the file to be imported. + * @param pFlags Optional post processing steps to be executed after + * a successful import. Provide a bitwise combination of the #aiPostProcessSteps + * flags. + * @return A pointer to the imported data, NULL if the import failed. + */ + const aiScene* ReadFile( const std::string& pFile, unsigned int pFlags); + + // ------------------------------------------------------------------- + /** Returns an error description of an error that occured in ReadFile(). + * + * Returns an empty string if no error occured. + * @return A description of the last error, an empty string if no + * error occured. + */ + inline const std::string& GetErrorString() const + { return mErrorString; } + +private: + /** Empty copy constructor. */ + Importer(const Importer &other); + +protected: + /** IO handler to use for all file accesses. */ + IOSystem* mIOHandler; + + /** Format-specific importer worker objects - + * one for each format we can read. */ + std::vector mImporter; + + /** Post processing steps we can apply at the imported data. */ + std::vector mPostProcessingSteps; + + /** The imported data, if ReadFile() was successful, + * NULL otherwise. */ + aiScene* mScene; + + /** The error description, if there was one. */ + std::string mErrorString; +}; + +} // End of namespace Assimp + +#endif // AI_ASSIMP_HPP_INC \ No newline at end of file diff --git a/test/3DSFiles/CWALL02.jpg b/test/3DSFiles/CWALL02.jpg new file mode 100644 index 000000000..93364a0bd Binary files /dev/null and b/test/3DSFiles/CWALL02.jpg differ diff --git a/test/3DSFiles/IMAGE1.bmp b/test/3DSFiles/IMAGE1.bmp new file mode 100644 index 000000000..5a88d5647 Binary files /dev/null and b/test/3DSFiles/IMAGE1.bmp differ diff --git a/test/3DSFiles/IMAGE2.jpg b/test/3DSFiles/IMAGE2.jpg new file mode 100644 index 000000000..d7ce7a64c Binary files /dev/null and b/test/3DSFiles/IMAGE2.jpg differ diff --git a/test/3DSFiles/cube_with_diffuse_texture.3DS b/test/3DSFiles/cube_with_diffuse_texture.3DS new file mode 100644 index 000000000..e68b92f51 Binary files /dev/null and b/test/3DSFiles/cube_with_diffuse_texture.3DS differ diff --git a/test/3DSFiles/cube_with_specular_texture.3DS b/test/3DSFiles/cube_with_specular_texture.3DS new file mode 100644 index 000000000..4844c12c3 Binary files /dev/null and b/test/3DSFiles/cube_with_specular_texture.3DS differ diff --git a/test/3DSFiles/test.png b/test/3DSFiles/test.png new file mode 100644 index 000000000..501fcdecf Binary files /dev/null and b/test/3DSFiles/test.png differ diff --git a/test/3DSFiles/test1.3ds b/test/3DSFiles/test1.3ds new file mode 100644 index 000000000..17bbb47d3 Binary files /dev/null and b/test/3DSFiles/test1.3ds differ diff --git a/test/3DSFiles/textures.txt b/test/3DSFiles/textures.txt new file mode 100644 index 000000000..18bbd8f90 --- /dev/null +++ b/test/3DSFiles/textures.txt @@ -0,0 +1 @@ +All textures are from cgtextures.com and are free for commercial use \ No newline at end of file diff --git a/tools/assimp_view/AssetHelper.h b/tools/assimp_view/AssetHelper.h new file mode 100644 index 000000000..76557570b --- /dev/null +++ b/tools/assimp_view/AssetHelper.h @@ -0,0 +1,135 @@ +//------------------------------------------------------------------------------- +/** +* This program is distributed under the terms of the GNU Lesser General +* Public License (LGPL). +* +* ASSIMP Viewer Utility +* +*/ +//------------------------------------------------------------------------------- + +#if (!defined AV_ASSET_HELPER_H_INCLUDED) +#define AV_ASSET_HELPER_H_INCLUDED + + +//------------------------------------------------------------------------------- +/** \brief Class to wrap ASSIMP's asset output structures +*/ +//------------------------------------------------------------------------------- +class AssetHelper + { + public: + + //--------------------------------------------------------------- + // default vertex data structure + // (even if tangents, bitangents or normals aren't + // required by the shader they will be committed to the GPU) + //--------------------------------------------------------------- + struct Vertex + { + aiVector3D vPosition; + aiVector3D vNormal; + + D3DCOLOR dColorDiffuse; + aiVector3D vTangent; + aiVector3D vBitangent; + aiVector2D vTextureUV; + + // retrieves the FVF code of the vertex type + static DWORD GetFVF() + { + return D3DFVF_DIFFUSE | D3DFVF_XYZ | D3DFVF_NORMAL | + D3DFVF_TEX1 | D3DFVF_TEX2 | D3DFVF_TEX3 | + D3DFVF_TEXCOORDSIZE3(0) | D3DFVF_TEXCOORDSIZE3(1); + } + }; + + //--------------------------------------------------------------- + // FVF vertex structure used for normals + //--------------------------------------------------------------- + struct LineVertex + { + aiVector3D vPosition; + DWORD dColorDiffuse; + + // retrieves the FVF code of the vertex type + static DWORD GetFVF() + { + return D3DFVF_DIFFUSE | D3DFVF_XYZ; + } + }; + + //--------------------------------------------------------------- + // Helper class to store GPU related resources created for + // a given aiMesh + //--------------------------------------------------------------- + class MeshHelper + { + public: + + MeshHelper () + : + piVB (NULL), + piIB (NULL), + piVBNormals (NULL), + piDiffuseTexture (NULL), + piSpecularTexture (NULL), + piAmbientTexture (NULL), + piNormalTexture (NULL), + piEmissiveTexture (NULL), + piOpacityTexture (NULL), + bSharedFX(false) {} + + ~MeshHelper () + { + // NOTE: This is done in DeleteAssetData() + // TODO: Make this a proper d'tor + } + + // shading mode to use. Either Lambert or otherwise phong + // will be used in every case + aiShadingMode eShadingMode; + + // vertex buffer + IDirect3DVertexBuffer9* piVB; + + // index buffer + IDirect3DIndexBuffer9* piIB; + + // vertex buffer to be used to draw vertex normals + // (vertex normals are generated in every case) + IDirect3DVertexBuffer9* piVBNormals; + + // shader to be used + ID3DXEffect* piEffect; + bool bSharedFX; + + // material textures + IDirect3DTexture9* piDiffuseTexture; + IDirect3DTexture9* piSpecularTexture; + IDirect3DTexture9* piAmbientTexture; + IDirect3DTexture9* piEmissiveTexture; + IDirect3DTexture9* piNormalTexture; + IDirect3DTexture9* piOpacityTexture; + + // material colors + D3DXVECTOR4 vDiffuseColor; + D3DXVECTOR4 vSpecularColor; + D3DXVECTOR4 vAmbientColor; + D3DXVECTOR4 vEmissiveColor; + + // opacity for the material + float fOpacity; + + // shininess for the material + float fShininess; + }; + + // One instance per aiMesh in the globally loaded asset + MeshHelper** apcMeshes; + + // Scene wrapper instance + const aiScene* pcScene; + }; + +#endif // !! IG \ No newline at end of file diff --git a/tools/assimp_view/Background.cpp b/tools/assimp_view/Background.cpp new file mode 100644 index 000000000..459b5f6a0 --- /dev/null +++ b/tools/assimp_view/Background.cpp @@ -0,0 +1,430 @@ +//------------------------------------------------------------------------------- +/** +* This program is distributed under the terms of the GNU Lesser General +* Public License (LGPL). +* +* ASSIMP Viewer Utility +* +*/ +//------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "assimp_view.h" + + +namespace AssimpView { + + +// From: U3D build 1256 (src\kernel\graphic\scenegraph\SkyBox.cpp) +// ------------------------------------------------------------------------------ +/** \brief Vertex structure for the skybox + */ +// ------------------------------------------------------------------------------ +struct SkyBoxVertex + { + float x,y,z; + float u,v,w; + }; + + +// ------------------------------------------------------------------------------ +/** \brief Vertices for the skybox + */ +// ------------------------------------------------------------------------------ +SkyBoxVertex g_cubeVertices_indexed[] = + { + { -1.0f, 1.0f, -1.0f, -1.0f,1.0f,-1.0f }, // 0 + { 1.0f, 1.0f, -1.0f, 1.0f,1.0f,-1.0f }, // 1 + { -1.0f, -1.0f, -1.0f, -1.0f,-1.0f,-1.0f }, // 2 + { 1.0f,-1.0f,-1.0f, 1.0f,-1.0f,-1.0f }, // 3 + {-1.0f, 1.0f, 1.0f, -1.0f,1.0f,1.0f }, // 4 + {-1.0f,-1.0f, 1.0f, -1.0f,-1.0f,1.0f }, // 5 + { 1.0f, 1.0f, 1.0f, 1.0f,1.0f,1.0f }, // 6 + { 1.0f,-1.0f, 1.0f, 1.0f,-1.0f,1.0f } // 7 + }; + + +// ------------------------------------------------------------------------------ +/** \brief Indices for the skybox + */ +// ------------------------------------------------------------------------------ +unsigned short g_cubeIndices[] = + { + 0, 1, 2, 3, 2, 1,4, 5, 6, + 7, 6, 5, 4, 6, 0, 1, 6, 0, + 5, 2, 7,3, 2, 7, 1, 6, 3, + 7, 3, 6, 0, 2, 4, 5, 4, 2, + }; + +CBackgroundPainter CBackgroundPainter::s_cInstance; + +//------------------------------------------------------------------------------- +void CBackgroundPainter::SetColor (D3DCOLOR p_clrNew) + { + if (TEXTURE_CUBE == this->eMode)this->RemoveSBDeps(); + + this->clrColor = p_clrNew; + this->eMode = SIMPLE_COLOR; + + if (this->pcTexture) + { + this->pcTexture->Release(); + this->pcTexture = NULL; + } + } +//------------------------------------------------------------------------------- +void CBackgroundPainter::RemoveSBDeps() + { + MODE e = this->eMode; + this->eMode = SIMPLE_COLOR; + if (g_pcAsset && g_pcAsset->pcScene) + { + for (unsigned int i = 0; i < g_pcAsset->pcScene->mNumMeshes;++i) + { + if (aiShadingMode_Gouraud != g_pcAsset->apcMeshes[i]->eShadingMode) + { + DeleteMaterial(g_pcAsset->apcMeshes[i]); + CreateMaterial(g_pcAsset->apcMeshes[i],g_pcAsset->pcScene->mMeshes[i]); + } + } + } + this->eMode = e; + } +//------------------------------------------------------------------------------- +void CBackgroundPainter::ResetSB() + { + this->mMatrix = aiMatrix4x4(); + } +//------------------------------------------------------------------------------- +void CBackgroundPainter::SetCubeMapBG (const char* p_szPath) + { + bool bHad = false; + if (this->pcTexture) + { + this->pcTexture->Release(); + this->pcTexture = NULL; + if(TEXTURE_CUBE ==this->eMode)bHad = true; + } + + this->eMode = TEXTURE_CUBE; + + this->szPath = std::string( p_szPath ); + + // ARRRGHH... ugly. TODO: Rewrite this! + aiString sz; + sz.Set(this->szPath); + FindValidPath(&sz); + this->szPath = std::string( sz.data ); + + // now recreate all native resources + this->RecreateNativeResource(); + + if (SIMPLE_COLOR != this->eMode) + { + // this influences all material with specular components + if (!bHad) + { + if (g_pcAsset && g_pcAsset->pcScene) + { + for (unsigned int i = 0; i < g_pcAsset->pcScene->mNumMeshes;++i) + { + if (aiShadingMode_Phong == g_pcAsset->apcMeshes[i]->eShadingMode) + { + DeleteMaterial(g_pcAsset->apcMeshes[i]); + CreateMaterial(g_pcAsset->apcMeshes[i],g_pcAsset->pcScene->mMeshes[i]); + } + } + } + } + else + { + if (g_pcAsset && g_pcAsset->pcScene) + { + for (unsigned int i = 0; i < g_pcAsset->pcScene->mNumMeshes;++i) + { + if (aiShadingMode_Phong == g_pcAsset->apcMeshes[i]->eShadingMode) + { + g_pcAsset->apcMeshes[i]->piEffect->SetTexture( + "lw_tex_envmap",CBackgroundPainter::Instance().GetTexture()); + } + } + } + } + } + } +//------------------------------------------------------------------------------- +void CBackgroundPainter::RotateSB(const aiMatrix4x4* pm) + { + this->mMatrix = this->mMatrix * (*pm); + } +//------------------------------------------------------------------------------- +void CBackgroundPainter::SetTextureBG (const char* p_szPath) + { + if (TEXTURE_CUBE == this->eMode)this->RemoveSBDeps(); + + if (this->pcTexture) + { + this->pcTexture->Release(); + this->pcTexture = NULL; + } + + this->eMode = TEXTURE_2D; + this->szPath = std::string( p_szPath ); + + // ARRRGHH... ugly. TODO: Rewrite this! + aiString sz; + sz.Set(this->szPath); + FindValidPath(&sz); + this->szPath = std::string( sz.data ); + + // now recreate all native resources + this->RecreateNativeResource(); + } +//------------------------------------------------------------------------------- +void CBackgroundPainter::OnPreRender() + { + if (SIMPLE_COLOR != this->eMode) + { + // clear the z-buffer only (in wireframe mode we must also clear + // the color buffer ) + if (g_sOptions.eDrawMode == RenderOptions::WIREFRAME) + { + g_piDevice->Clear(0,NULL,D3DCLEAR_ZBUFFER | D3DCLEAR_TARGET, + D3DCOLOR_ARGB(0xff,100,100,100),1.0f,0); + } + else + { + g_piDevice->Clear(0,NULL,D3DCLEAR_ZBUFFER,0,1.0f,0); + } + + if (TEXTURE_2D == this->eMode) + { + RECT sRect; + GetWindowRect(GetDlgItem(g_hDlg,IDC_RT),&sRect); + sRect.right -= sRect.left; + sRect.bottom -= sRect.top; + + struct SVertex + { + float x,y,z,w,u,v; + }; + + UINT dw; + this->piSkyBoxEffect->Begin(&dw,0); + this->piSkyBoxEffect->BeginPass(0); + + SVertex as[4]; + as[1].x = 0.0f; + as[1].y = 0.0f; + as[1].z = 0.2f; + as[1].w = 1.0f; + as[1].u = 0.0f; + as[1].v = 0.0f; + + as[3].x = (float)sRect.right; + as[3].y = 0.0f; + as[3].z = 0.2f; + as[3].w = 1.0f; + as[3].u = 1.0f; + as[3].v = 0.0f; + + as[0].x = 0.0f; + as[0].y = (float)sRect.bottom; + as[0].z = 0.2f; + as[0].w = 1.0f; + as[0].u = 0.0f; + as[0].v = 1.0f; + + as[2].x = (float)sRect.right; + as[2].y = (float)sRect.bottom; + as[2].z = 0.2f; + as[2].w = 1.0f; + as[2].u = 1.0f; + as[2].v = 1.0f; + + as[0].x -= 0.5f;as[1].x -= 0.5f;as[2].x -= 0.5f;as[3].x -= 0.5f; + as[0].y -= 0.5f;as[1].y -= 0.5f;as[2].y -= 0.5f;as[3].y -= 0.5f; + + DWORD dw2;g_piDevice->GetFVF(&dw2); + g_piDevice->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1); + + g_piDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP,2, + &as,sizeof(SVertex)); + + this->piSkyBoxEffect->EndPass(); + this->piSkyBoxEffect->End(); + + g_piDevice->SetFVF(dw2); + } + return; + } + // clear both the render target and the z-buffer + g_piDevice->Clear(0,NULL,D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, + this->clrColor,1.0f,0); + } +//------------------------------------------------------------------------------- +void CBackgroundPainter::OnPostRender() + { + if (TEXTURE_CUBE == this->eMode) + { + aiMatrix4x4 pcProj; + GetProjectionMatrix(pcProj); + + aiMatrix4x4 pcCam; + aiVector3D vPos = GetCameraMatrix(pcCam); + + aiMatrix4x4 aiMe; + aiMe[3][0] = vPos.x; + aiMe[3][1] = vPos.y; + aiMe[3][2] = vPos.z; + aiMe = this->mMatrix * aiMe; + + pcProj = (aiMe * pcCam) * pcProj; + + this->piSkyBoxEffect->SetMatrix("WorldViewProjection", + (const D3DXMATRIX*)&pcProj); + + UINT dwPasses; + this->piSkyBoxEffect->Begin(&dwPasses,0); + this->piSkyBoxEffect->BeginPass(0); + + DWORD dw2; + g_piDevice->GetFVF(&dw2); + g_piDevice->SetFVF(D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE3(0)); + + g_piDevice->DrawIndexedPrimitiveUP( + D3DPT_TRIANGLELIST,0,8,12,g_cubeIndices,D3DFMT_INDEX16, + g_cubeVertices_indexed,sizeof(SkyBoxVertex)); + + g_piDevice->SetFVF(dw2); + + this->piSkyBoxEffect->EndPass(); + this->piSkyBoxEffect->End(); + } + } +//------------------------------------------------------------------------------- +void CBackgroundPainter::ReleaseNativeResource() + { + if (this->piSkyBoxEffect) + { + this->piSkyBoxEffect->Release(); + this->piSkyBoxEffect = NULL; + } + if (this->pcTexture) + { + this->pcTexture->Release(); + this->pcTexture = NULL; + } + } +//------------------------------------------------------------------------------- +void CBackgroundPainter::RecreateNativeResource() + { + if (SIMPLE_COLOR == this->eMode)return; + if (TEXTURE_CUBE == this->eMode) + { + + // many skyboxes are 16bit FP format which isn't supported + // with bilinear filtering on older cards + D3DFORMAT eFmt = D3DFMT_UNKNOWN; + if(FAILED(g_piD3D->CheckDeviceFormat(0,D3DDEVTYPE_HAL, + D3DFMT_X8R8G8B8,D3DUSAGE_QUERY_FILTER,D3DRTYPE_CUBETEXTURE,D3DFMT_A16B16G16R16F))) + { + eFmt = D3DFMT_A8R8G8B8; + } + + if (FAILED(D3DXCreateCubeTextureFromFileEx( + g_piDevice, + this->szPath.c_str(), + D3DX_DEFAULT, + 0, + 0, + eFmt, + D3DPOOL_MANAGED, + D3DX_DEFAULT, + D3DX_DEFAULT, + 0, + NULL, + NULL, + (IDirect3DCubeTexture9**)&this->pcTexture))) + { + const char* szEnd = strrchr(this->szPath.c_str(),'\\'); + if (!szEnd)szEnd = strrchr(this->szPath.c_str(),'/'); + if (!szEnd)szEnd = this->szPath.c_str()-1; + + char szTemp[1024]; + sprintf(szTemp,"[ERROR] Unable to load background cubemap %s",szEnd+1); + + CLogDisplay::Instance().AddEntry(szTemp, + D3DCOLOR_ARGB(0xFF,0xFF,0,0)); + + this->eMode = SIMPLE_COLOR; + return; + } + else CLogDisplay::Instance().AddEntry("[OK] The skybox has been imported successfully", + D3DCOLOR_ARGB(0xFF,0,0xFF,0)); + } + else + { + if (FAILED(D3DXCreateTextureFromFileEx( + g_piDevice, + this->szPath.c_str(), + D3DX_DEFAULT, + D3DX_DEFAULT, + 0, + 0, + D3DFMT_A8R8G8B8, + D3DPOOL_MANAGED, + D3DX_DEFAULT, + D3DX_DEFAULT, + 0, + NULL, + NULL, + (IDirect3DTexture9**)&this->pcTexture))) + { + const char* szEnd = strrchr(this->szPath.c_str(),'\\'); + if (!szEnd)szEnd = strrchr(this->szPath.c_str(),'/'); + if (!szEnd)szEnd = this->szPath.c_str()-1; + + char szTemp[1024]; + sprintf(szTemp,"[ERROR] Unable to load background texture %s",szEnd+1); + + CLogDisplay::Instance().AddEntry(szTemp, + D3DCOLOR_ARGB(0xFF,0xFF,0,0)); + + this->eMode = SIMPLE_COLOR; + return; + } + else CLogDisplay::Instance().AddEntry("[OK] The background texture has been imported successfully", + D3DCOLOR_ARGB(0xFF,0,0xFF,0)); + } + if (!piSkyBoxEffect) + { + if(FAILED( D3DXCreateEffect( + g_piDevice, + g_szSkyboxShader.c_str(), + (UINT)g_szSkyboxShader.length(), + NULL, + NULL, + D3DXSHADER_USE_LEGACY_D3DX9_31_DLL, + NULL, + &this->piSkyBoxEffect,NULL))) + { + CLogDisplay::Instance().AddEntry("[ERROR] Unable to compile skybox shader", + D3DCOLOR_ARGB(0xFF,0xFF,0,0)); + this->eMode = SIMPLE_COLOR; + return ; + } + } + // commit the correct textures to the shader + if (TEXTURE_CUBE == this->eMode) + { + this->piSkyBoxEffect->SetTexture("lw_tex_envmap",this->pcTexture); + this->piSkyBoxEffect->SetTechnique("RenderSkyBox"); + } + else if (TEXTURE_2D == this->eMode) + { + this->piSkyBoxEffect->SetTexture("TEXTURE_2D",this->pcTexture); + this->piSkyBoxEffect->SetTechnique("RenderImage2D"); + } + } +}; \ No newline at end of file diff --git a/tools/assimp_view/Background.h b/tools/assimp_view/Background.h new file mode 100644 index 000000000..20e9fd9fd --- /dev/null +++ b/tools/assimp_view/Background.h @@ -0,0 +1,97 @@ +//------------------------------------------------------------------------------- +/** +* This program is distributed under the terms of the GNU Lesser General +* Public License (LGPL). +* +* ASSIMP Viewer Utility +* +*/ +//------------------------------------------------------------------------------- + +#if (!defined AV_BACKGROUND_H_INCLUDED) +#define AV_BACKGROUND_H_INCLUDED + + +class CBackgroundPainter + { + CBackgroundPainter() + : + pcTexture(NULL), + clrColor(D3DCOLOR_ARGB(0xFF,100,100,100)), + eMode(SIMPLE_COLOR), + piSkyBoxEffect(NULL) + {} + +public: + + // Supported background draw modi + enum MODE {SIMPLE_COLOR, TEXTURE_2D, TEXTURE_CUBE}; + + // Singleton accessors + static CBackgroundPainter s_cInstance; + inline static CBackgroundPainter& Instance () + { + return s_cInstance; + } + + // set the current background color + // (this removes any textures loaded) + void SetColor (D3DCOLOR p_clrNew); + + // Setup a cubemap/a 2d texture as background + void SetCubeMapBG (const char* p_szPath); + void SetTextureBG (const char* p_szPath); + + // Called by the render loop + void OnPreRender(); + void OnPostRender(); + + // Release any native resources associated with the instance + void ReleaseNativeResource(); + + // Recreate any native resources associated with the instance + void RecreateNativeResource(); + + // Rotate the skybox + void RotateSB(const aiMatrix4x4* pm); + + // Reset the state of the skybox + void ResetSB(); + + inline MODE GetMode() const + { + return this->eMode; + } + + inline IDirect3DBaseTexture9* GetTexture() + { + return this->pcTexture; + } + + inline ID3DXBaseEffect* GetEffect() + { + return this->piSkyBoxEffect; + } + +private: + + void RemoveSBDeps(); + + // current background color + D3DCOLOR clrColor; + + // current background texture + IDirect3DBaseTexture9* pcTexture; + ID3DXEffect* piSkyBoxEffect; + + // current background mode + MODE eMode; + + // path to the texture + std::string szPath; + + // transformation matrix for the skybox + aiMatrix4x4 mMatrix; + }; + +#endif // !! AV_BACKGROUND_H_INCLUDED \ No newline at end of file diff --git a/tools/assimp_view/Camera.h b/tools/assimp_view/Camera.h new file mode 100644 index 000000000..45af9fdc0 --- /dev/null +++ b/tools/assimp_view/Camera.h @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------- +/** +* This program is distributed under the terms of the GNU Lesser General +* Public License (LGPL). +* +* ASSIMP Viewer Utility +* +*/ +//------------------------------------------------------------------------------- + +#if (!defined AV_CAMERA_H_INCLUDED) +#define AV_CAMERA_H_INCLUDED + +//------------------------------------------------------------------------------- +/** \brief Camera class +*/ +//------------------------------------------------------------------------------- +class Camera + { + public: + + + Camera () + : + + vPos(0.0f,0.0f,-10.0f), + vLookAt(0.0f,0.0f,1.0f), + vUp(0.0f,1.0f,0.0f), + vRight(0.0f,1.0f,0.0f) + { + + } + public: + + // position of the camera + aiVector3D vPos; + + // up-vector of the camera + aiVector3D vUp; + + // camera's looking point is vPos + vLookAt + aiVector3D vLookAt; + + // right vector of the camera + aiVector3D vRight; + + + // Equation + // (vRight ^ vUp) - vLookAt == 0 + // needn't apply + + } ; + +#endif // !!IG \ No newline at end of file diff --git a/tools/assimp_view/HUD.png b/tools/assimp_view/HUD.png new file mode 100644 index 000000000..fe649fc16 Binary files /dev/null and b/tools/assimp_view/HUD.png differ diff --git a/tools/assimp_view/HUDMask.png b/tools/assimp_view/HUDMask.png new file mode 100644 index 000000000..98578dc53 Binary files /dev/null and b/tools/assimp_view/HUDMask.png differ diff --git a/tools/assimp_view/HelpDialog.cpp b/tools/assimp_view/HelpDialog.cpp new file mode 100644 index 000000000..f13f3b94a --- /dev/null +++ b/tools/assimp_view/HelpDialog.cpp @@ -0,0 +1,80 @@ +//------------------------------------------------------------------------------- +/** +* This program is distributed under the terms of the GNU Lesser General +* Public License (LGPL). +* +* ASSIMP Viewer Utility +* +*/ +//------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "assimp_view.h" + +#include "RICHEDIT.H" + +namespace AssimpView { + + +//------------------------------------------------------------------------------- +// Message procedure for the help dialog +//------------------------------------------------------------------------------- +INT_PTR CALLBACK HelpDialogProc(HWND hwndDlg,UINT uMsg, + WPARAM wParam,LPARAM lParam) + { + lParam; + switch (uMsg) + { + case WM_INITDIALOG: + { + // load the help file ... + HRSRC res = FindResource(NULL,MAKEINTRESOURCE(IDR_TEXT1),"TEXT"); + HGLOBAL hg = LoadResource(NULL,res); + void* pData = LockResource(hg); + + SETTEXTEX sInfo; + sInfo.flags = ST_DEFAULT; + sInfo.codepage = CP_ACP; + + SendDlgItemMessage(hwndDlg,IDC_RICHEDIT21, + EM_SETTEXTEX,(WPARAM)&sInfo,( LPARAM) pData); + + UnlockResource(hg); + FreeResource(hg); + return TRUE; + } + + case WM_CLOSE: + EndDialog(hwndDlg,0); + return TRUE; + + case WM_COMMAND: + + if (IDOK == LOWORD(wParam)) + { + EndDialog(hwndDlg,0); + return TRUE; + } + + case WM_PAINT: + { + PAINTSTRUCT sPaint; + HDC hdc = BeginPaint(hwndDlg,&sPaint); + + HBRUSH hBrush = CreateSolidBrush(RGB(0xFF,0xFF,0xFF)); + + RECT sRect; + sRect.left = 0; + sRect.top = 26; + sRect.right = 1000; + sRect.bottom = 507; + FillRect(hdc, &sRect, hBrush); + + EndPaint(hwndDlg,&sPaint); + return TRUE; + } + }; + return FALSE; + } + +}; \ No newline at end of file diff --git a/tools/assimp_view/Input.cpp b/tools/assimp_view/Input.cpp new file mode 100644 index 000000000..8e77cb85a --- /dev/null +++ b/tools/assimp_view/Input.cpp @@ -0,0 +1,292 @@ +//------------------------------------------------------------------------------- +/** +* This program is distributed under the terms of the GNU Lesser General +* Public License (LGPL). +* +* ASSIMP Viewer Utility +* +*/ +//------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "assimp_view.h" + + +namespace AssimpView { + +//------------------------------------------------------------------------------- +// Handle mouse input for the FPS input behaviour +// +// Movement in x and y axis is possible +//------------------------------------------------------------------------------- +void HandleMouseInputFPS( void ) + { + + POINT mousePos; + GetCursorPos( &mousePos ); + ScreenToClient( GetDlgItem(g_hDlg,IDC_RT), &mousePos ); + + g_mousePos.x = mousePos.x; + g_mousePos.y = mousePos.y; + + D3DXMATRIX matRotation; + + if (g_bMousePressed) + { + int nXDiff = (g_mousePos.x - g_LastmousePos.x); + int nYDiff = (g_mousePos.y - g_LastmousePos.y); + + if( 0 != nYDiff) + { + D3DXMatrixRotationAxis( &matRotation, (D3DXVECTOR3*)& g_sCamera.vRight, D3DXToRadian((float)nYDiff / 3.0f)); + D3DXVec3TransformCoord( (D3DXVECTOR3*)&g_sCamera.vLookAt, (D3DXVECTOR3*)& g_sCamera.vLookAt, &matRotation ); + D3DXVec3TransformCoord( (D3DXVECTOR3*)&g_sCamera.vUp, (D3DXVECTOR3*)&g_sCamera.vUp, &matRotation ); + } + + if( 0 != nXDiff ) + { + D3DXVECTOR3 v(0,1,0); + D3DXMatrixRotationAxis( &matRotation, (D3DXVECTOR3*)&v, D3DXToRadian((float)nXDiff / 3.0f) ); + D3DXVec3TransformCoord( (D3DXVECTOR3*)&g_sCamera.vLookAt, (D3DXVECTOR3*)&g_sCamera.vLookAt, &matRotation ); + D3DXVec3TransformCoord( (D3DXVECTOR3*)&g_sCamera.vUp,(D3DXVECTOR3*) &g_sCamera.vUp, &matRotation ); + } + } + + g_LastmousePos.x = g_mousePos.x; + g_LastmousePos.y = g_mousePos.y; + } + + +//------------------------------------------------------------------------------- +// handle mouse input for the light rotation +// +// Axes: global x/y axis +//------------------------------------------------------------------------------- +void HandleMouseInputLightRotate( void ) + { + POINT mousePos; + GetCursorPos( &mousePos ); + ScreenToClient( GetDlgItem(g_hDlg,IDC_RT), &mousePos ); + + g_mousePos.x = mousePos.x; + g_mousePos.y = mousePos.y; + + if (g_bMousePressedR) + { + int nXDiff = -(g_mousePos.x - g_LastmousePos.x); + int nYDiff = -(g_mousePos.y - g_LastmousePos.y); + + aiVector3D v = aiVector3D(1.0f,0.0f,0.0f); + aiMatrix4x4 mTemp; + D3DXMatrixRotationAxis( (D3DXMATRIX*) &mTemp, (D3DXVECTOR3*)&v, D3DXToRadian((float)nYDiff / 2.0f)); + D3DXVec3TransformCoord((D3DXVECTOR3*)&g_avLightDirs[0], + (const D3DXVECTOR3*)&g_avLightDirs[0],(const D3DXMATRIX*)&mTemp); + + v = aiVector3D(0.0f,1.0f,0.0f); + D3DXMatrixRotationAxis( (D3DXMATRIX*) &mTemp, (D3DXVECTOR3*)&v, D3DXToRadian((float)nXDiff / 2.0f)); + D3DXVec3TransformCoord((D3DXVECTOR3*)&g_avLightDirs[0], + (const D3DXVECTOR3*)&g_avLightDirs[0],(const D3DXMATRIX*)&mTemp); + } + return; + } + + +//------------------------------------------------------------------------------- +// Handle mouse input for movements of the skybox +// +// The skybox can be moved by holding both the left and the right mouse button +// pressed. Rotation is possible in x and y direction. +//------------------------------------------------------------------------------- +void HandleMouseInputSkyBox( void ) + { + POINT mousePos; + GetCursorPos( &mousePos ); + ScreenToClient( GetDlgItem(g_hDlg,IDC_RT), &mousePos ); + + g_mousePos.x = mousePos.x; + g_mousePos.y = mousePos.y; + + aiMatrix4x4 matRotation; + + if (g_bMousePressedBoth ) + { + int nXDiff = -(g_mousePos.x - g_LastmousePos.x); + int nYDiff = -(g_mousePos.y - g_LastmousePos.y); + + aiMatrix4x4 matWorld; + + if( 0 != nYDiff) + { + aiVector3D v = aiVector3D(1.0f,0.0f,0.0f); + D3DXMatrixRotationAxis( (D3DXMATRIX*) &matWorld, (D3DXVECTOR3*)&v, D3DXToRadian((float)nYDiff / 2.0f)); + CBackgroundPainter::Instance().RotateSB(&matWorld); + } + + if( 0 != nXDiff) + { + aiMatrix4x4 matWorldOld; + if( 0 != nYDiff) + { + matWorldOld = matWorld; + } + + aiVector3D v = aiVector3D(0.0f,1.0f,0.0f); + D3DXMatrixRotationAxis( (D3DXMATRIX*)&matWorld, (D3DXVECTOR3*)&v, D3DXToRadian((float)nXDiff / 2.0f) ); + matWorld = matWorldOld * matWorld; + CBackgroundPainter::Instance().RotateSB(&matWorld); + } + } + } + +//------------------------------------------------------------------------------- +//------------------------------------------------------------------------------- +void HandleMouseInputLightIntensityAndColor( void ) + { + POINT mousePos; + GetCursorPos( &mousePos ); + ScreenToClient( GetDlgItem(g_hDlg,IDC_RT), &mousePos ); + + g_mousePos.x = mousePos.x; + g_mousePos.y = mousePos.y; + + if (g_bMousePressedM) + { + int nXDiff = -(g_mousePos.x - g_LastmousePos.x); + int nYDiff = -(g_mousePos.y - g_LastmousePos.y); + + g_fLightIntensity -= (float)nXDiff / 400.0f; + + if ((nYDiff > 2 || nYDiff < -2) && (nXDiff < 20 && nXDiff > -20)) + { + if (!g_bFPSView) + { + g_sCamera.vPos.z += nYDiff / 120.0f; + } + else + { + g_sCamera.vPos += (nYDiff / 120.0f) * g_sCamera.vLookAt.Normalize(); + } + } + } + return; + } + +//------------------------------------------------------------------------------- +//------------------------------------------------------------------------------- +void HandleMouseInputLocal( void ) + { + POINT mousePos; + GetCursorPos( &mousePos ); + ScreenToClient( GetDlgItem(g_hDlg,IDC_RT), &mousePos ); + + g_mousePos.x = mousePos.x; + g_mousePos.y = mousePos.y; + + aiMatrix4x4 matRotation; + + if (g_bMousePressed) + { + int nXDiff = -(g_mousePos.x - g_LastmousePos.x); + int nYDiff = -(g_mousePos.y - g_LastmousePos.y); + + aiMatrix4x4 matWorld; + if (g_eClick != EClickPos_Outside) + { + if( 0 != nYDiff && g_eClick != EClickPos_CircleHor) + { + aiVector3D v = aiVector3D(1.0f,0.0f,0.0f); + D3DXMatrixRotationAxis( (D3DXMATRIX*) &matWorld, (D3DXVECTOR3*)&v, D3DXToRadian((float)nYDiff / 2.0f)); + g_mWorldRotate = g_mWorldRotate * matWorld; + } + + if( 0 != nXDiff && g_eClick != EClickPos_CircleVert) + { + aiVector3D v = aiVector3D(0.0f,1.0f,0.0f); + D3DXMatrixRotationAxis( (D3DXMATRIX*)&matWorld, (D3DXVECTOR3*)&v, D3DXToRadian((float)nXDiff / 2.0f) ); + g_mWorldRotate = g_mWorldRotate * matWorld; + } + } + else + { + if(0 != nYDiff || 0 != nXDiff) + { + // rotate around the z-axis + RECT sRect; + GetWindowRect(GetDlgItem(g_hDlg,IDC_RT),&sRect); + sRect.right -= sRect.left; + sRect.bottom -= sRect.top; + + int xPos = g_mousePos.x - sRect.right/2; + int yPos = g_mousePos.y - sRect.bottom/2; + float fXDist = (float)xPos; + float fYDist = (float)yPos / sqrtf((float)(yPos * yPos + xPos * xPos)); + + bool bSign1; + if (fXDist < 0.0f)bSign1 = false; + else bSign1 = true; + float fAngle = asin(fYDist); + + xPos = g_LastmousePos.x - sRect.right/2; + yPos = g_LastmousePos.y - sRect.bottom/2; + + fXDist = (float)xPos; + fYDist = (float)yPos / sqrtf((float)(yPos * yPos + xPos * xPos)); + + bool bSign2; + if (fXDist < 0.0f)bSign2 = false; + else bSign2 = true; + float fAngle2 = asin(fYDist); + fAngle -= fAngle2; + + if (bSign1 != bSign2) + { + g_bInvert = !g_bInvert; + } + if (g_bInvert)fAngle *= -1.0f; + + aiVector3D v = aiVector3D(0.0f,0.0f,1.0f); + D3DXMatrixRotationAxis( (D3DXMATRIX*)&matWorld, (D3DXVECTOR3*)&v, (float) (fAngle * 1.2) ); + g_mWorldRotate = g_mWorldRotate * matWorld; + } + } + } + + g_LastmousePos.x = g_mousePos.x; + g_LastmousePos.y = g_mousePos.y; + } + +//------------------------------------------------------------------------------- +//------------------------------------------------------------------------------- +void HandleKeyboardInputFPS( void ) + { + unsigned char keys[256]; + GetKeyboardState( keys ); + + aiVector3D tmpLook = g_sCamera.vLookAt; + aiVector3D tmpRight = g_sCamera.vRight; + + // Up Arrow Key - View moves forward + if( keys[VK_UP] & 0x80 ) + g_sCamera.vPos -= (tmpLook*-MOVE_SPEED)*g_fElpasedTime; + + // Down Arrow Key - View moves backward + if( keys[VK_DOWN] & 0x80 ) + g_sCamera.vPos += (tmpLook*-MOVE_SPEED)*g_fElpasedTime; + + // Left Arrow Key - View side-steps or strafes to the left + if( keys[VK_LEFT] & 0x80 ) + g_sCamera.vPos -= (tmpRight*MOVE_SPEED)*g_fElpasedTime; + + // Right Arrow Key - View side-steps or strafes to the right + if( keys[VK_RIGHT] & 0x80 ) + g_sCamera.vPos += (tmpRight*MOVE_SPEED)*g_fElpasedTime; + + // Home Key - View elevates up + if( keys[VK_HOME] & 0x80 ) + g_sCamera.vPos .y += MOVE_SPEED*g_fElpasedTime; + + // End Key - View elevates down + if( keys[VK_END] & 0x80 ) + g_sCamera.vPos.y -= MOVE_SPEED*g_fElpasedTime; + } +}; \ No newline at end of file diff --git a/tools/assimp_view/LogDisplay.cpp b/tools/assimp_view/LogDisplay.cpp new file mode 100644 index 000000000..529ad4866 --- /dev/null +++ b/tools/assimp_view/LogDisplay.cpp @@ -0,0 +1,204 @@ +//------------------------------------------------------------------------------- +/** +* This program is distributed under the terms of the GNU Lesser General +* Public License (LGPL). +* +* ASSIMP Viewer Utility +* +*/ +//------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "assimp_view.h" + + +namespace AssimpView { + + +/* extern */ CLogDisplay CLogDisplay::s_cInstance; + +//------------------------------------------------------------------------------- +void CLogDisplay::AddEntry(const std::string& szText, + const D3DCOLOR clrColor) + { + SEntry sNew; + sNew.clrColor = clrColor; + sNew.szText = szText; + sNew.dwStartTicks = (DWORD)GetTickCount(); + + this->asEntries.push_back(sNew); + } + +//------------------------------------------------------------------------------- +void CLogDisplay::ReleaseNativeResource() + { + if (this->piFont) + { + this->piFont->Release(); + this->piFont = NULL; + } + } + +//------------------------------------------------------------------------------- +void CLogDisplay::RecreateNativeResource() + { + if (!this->piFont) + { + if (FAILED(D3DXCreateFont(g_piDevice, + 16, //Font height + 0, //Font width + FW_BOLD, //Font Weight + 1, //MipLevels + false, //Italic + DEFAULT_CHARSET, //CharSet + OUT_DEFAULT_PRECIS, //OutputPrecision + CLEARTYPE_QUALITY, //Quality + DEFAULT_PITCH|FF_DONTCARE, //PitchAndFamily + "Verdana", //pFacename, + &this->piFont))) + { + CLogDisplay::Instance().AddEntry("Unable to load font",D3DCOLOR_ARGB(0xFF,0xFF,0,0)); + + this->piFont = NULL; + return; + } + } + return; + } + +//------------------------------------------------------------------------------- +void CLogDisplay::OnRender() + { + DWORD dwTick = (DWORD) GetTickCount(); + DWORD dwLimit = dwTick - 8000; + DWORD dwLimit2 = dwLimit + 3000; + + unsigned int iCnt = 0; + RECT sRect; + sRect.left = 0; + sRect.top = 10; + + RECT sWndRect; + GetWindowRect(GetDlgItem(g_hDlg,IDC_RT),&sWndRect); + sWndRect.right -= sWndRect.left; + sWndRect.bottom -= sWndRect.top; + sWndRect.left = sWndRect.top = 0; + + // if no asset is loaded draw a "no asset loaded" text in the center + if (!g_pcAsset) + { + const char* szText = "No asset loaded\r\nUse [Viewer | Open asset] to load one"; + + // shadow + RECT sCopy; + sCopy.left = sWndRect.left+1; + sCopy.top = sWndRect.top+1; + sCopy.bottom = sWndRect.bottom+1; + sCopy.right = sWndRect.right+1; + this->piFont->DrawText(NULL,szText , + -1,&sCopy,DT_CENTER | DT_VCENTER,D3DCOLOR_ARGB(100,0x0,0x0,0x0)); + sCopy.left = sWndRect.left+1; + sCopy.top = sWndRect.top+1; + sCopy.bottom = sWndRect.bottom-1; + sCopy.right = sWndRect.right-1; + this->piFont->DrawText(NULL,szText , + -1,&sCopy,DT_CENTER | DT_VCENTER,D3DCOLOR_ARGB(100,0x0,0x0,0x0)); + sCopy.left = sWndRect.left-1; + sCopy.top = sWndRect.top-1; + sCopy.bottom = sWndRect.bottom+1; + sCopy.right = sWndRect.right+1; + this->piFont->DrawText(NULL,szText , + -1,&sCopy,DT_CENTER | DT_VCENTER,D3DCOLOR_ARGB(100,0x0,0x0,0x0)); + sCopy.left = sWndRect.left-1; + sCopy.top = sWndRect.top-1; + sCopy.bottom = sWndRect.bottom-1; + sCopy.right = sWndRect.right-1; + this->piFont->DrawText(NULL,szText , + -1,&sCopy,DT_CENTER | DT_VCENTER,D3DCOLOR_ARGB(100,0x0,0x0,0x0)); + + // text + this->piFont->DrawText(NULL,szText , + -1,&sWndRect,DT_CENTER | DT_VCENTER,D3DCOLOR_ARGB(0xFF,0xFF,0xFF,0xFF)); + } + + sRect.right = sWndRect.right - 30; + sRect.bottom = sWndRect.bottom; + + // update all elements in the queue and render them + for (std::list::iterator + i = this->asEntries.begin(); + i != this->asEntries.end();++i,++iCnt) + { + if ((*i).dwStartTicks < dwLimit) + { + i = this->asEntries.erase(i); + + if(i == this->asEntries.end())break; + } + else if (NULL != this->piFont) + { + float fAlpha = 1.0f; + if ((*i).dwStartTicks <= dwLimit2) + { + // linearly interpolate to create the fade out effect + fAlpha = 1.0f - (float)(dwLimit2 - (*i).dwStartTicks) / 3000.0f; + } + D3DCOLOR& clrColor = (*i).clrColor; + clrColor &= ~(0xFFu << 24); + clrColor |= (((unsigned char)(fAlpha * 255.0f)) & 0xFFu) << 24; + + const char* szText = (*i).szText.c_str(); + if (sRect.top + 30 > sWndRect.bottom) + { + // end of window. send a special message + szText = "... too many errors"; + clrColor = D3DCOLOR_ARGB(0xFF,0xFF,100,0x0); + } + + // draw the black shadow + RECT sCopy; + sCopy.left = sRect.left+1; + sCopy.top = sRect.top+1; + sCopy.bottom = sRect.bottom+1; + sCopy.right = sRect.right+1; + this->piFont->DrawText(NULL,szText, + -1,&sCopy,DT_RIGHT | DT_TOP,D3DCOLOR_ARGB( + (unsigned char)(fAlpha * 100.0f),0x0,0x0,0x0)); + + sCopy.left = sRect.left-1; + sCopy.top = sRect.top-1; + sCopy.bottom = sRect.bottom-1; + sCopy.right = sRect.right-1; + this->piFont->DrawText(NULL,szText, + -1,&sCopy,DT_RIGHT | DT_TOP,D3DCOLOR_ARGB( + (unsigned char)(fAlpha * 100.0f),0x0,0x0,0x0)); + + sCopy.left = sRect.left-1; + sCopy.top = sRect.top-1; + sCopy.bottom = sRect.bottom+1; + sCopy.right = sRect.right+1; + this->piFont->DrawText(NULL,szText, + -1,&sCopy,DT_RIGHT | DT_TOP,D3DCOLOR_ARGB( + (unsigned char)(fAlpha * 100.0f),0x0,0x0,0x0)); + + sCopy.left = sRect.left+1; + sCopy.top = sRect.top+1; + sCopy.bottom = sRect.bottom-1; + sCopy.right = sRect.right-1; + this->piFont->DrawText(NULL,szText, + -1,&sCopy,DT_RIGHT | DT_TOP,D3DCOLOR_ARGB( + (unsigned char)(fAlpha * 100.0f),0x0,0x0,0x0)); + + // draw the text itself + int iPX = this->piFont->DrawText(NULL,szText, + -1,&sRect,DT_RIGHT | DT_TOP,clrColor); + + sRect.top += iPX; + sRect.bottom += iPX; + + if (szText != (*i).szText.c_str())break; + } + } + return; + } +}; \ No newline at end of file diff --git a/tools/assimp_view/LogDisplay.h b/tools/assimp_view/LogDisplay.h new file mode 100644 index 000000000..ea0913609 --- /dev/null +++ b/tools/assimp_view/LogDisplay.h @@ -0,0 +1,66 @@ +//------------------------------------------------------------------------------- +/** +* This program is distributed under the terms of the GNU Lesser General +* Public License (LGPL). +* +* ASSIMP Viewer Utility +* +*/ +//------------------------------------------------------------------------------- + +#if (!defined AV_LOG_DISPLAY_H_INCLUDED) +#define AV_LOG_DISPLAY_H_INCLUDE + +//------------------------------------------------------------------------------- +/** \brief Class to display log strings in the upper right corner of the view +*/ +//------------------------------------------------------------------------------- +class CLogDisplay + { +private: + + CLogDisplay() {} + +public: + + // data structure for an entry in the log queue + struct SEntry + { + SEntry () + : + clrColor(D3DCOLOR_ARGB(0xFF,0xFF,0xFF,0x00)), dwStartTicks(0) + {} + + std::string szText; + D3DCOLOR clrColor; + DWORD dwStartTicks; + }; + + // Singleton accessors + static CLogDisplay s_cInstance; + inline static CLogDisplay& Instance () + { + return s_cInstance; + } + + // Add an entry to the log queue + void AddEntry(const std::string& szText, + const D3DCOLOR clrColor = D3DCOLOR_ARGB(0xFF,0xFF,0xFF,0x00)); + + // Release any native resources associated with the instance + void ReleaseNativeResource(); + + // Recreate any native resources associated with the instance + void RecreateNativeResource(); + + // Called during the render loop + void OnRender(); + +private: + + std::list asEntries; + ID3DXFont* piFont; + + }; + +#endif // AV_LOG_DISPLAY_H_INCLUDE \ No newline at end of file diff --git a/tools/assimp_view/Material.cpp b/tools/assimp_view/Material.cpp new file mode 100644 index 000000000..62a1a7308 --- /dev/null +++ b/tools/assimp_view/Material.cpp @@ -0,0 +1,975 @@ +//------------------------------------------------------------------------------- +/** +* This program is distributed under the terms of the GNU Lesser General +* Public License (LGPL). +* +* ASSIMP Viewer Utility +* +*/ +//------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "assimp_view.h" + + +namespace AssimpView { + +// +// Specifies the number of different shaders generated for +// the current asset. This number is incremented by CreateMaterial() +// each time a shader isn't found in cache and needs to be created +// +unsigned int g_iShaderCount = 0 ; + +//------------------------------------------------------------------------------- +// Compiler idependent stricmp() function. +// +// Used for case insensitive string comparison +//------------------------------------------------------------------------------- +inline int ASSIMP_stricmp(const char *s1, const char *s2) + { + const char *a1, *a2; + a1 = s1; + a2 = s2; + + while (true) + { + char c1 = (char)tolower(*a1); + char c2 = (char)tolower(*a2); + if ((0 == c1) && (0 == c2)) return 0; + if (c1 < c2) return-1; + if (c1 > c2) return 1; + ++a1; + ++a2; + } + } + +//------------------------------------------------------------------------------- +// D3DX callback function to fill a texture with a checkers pattern +// +// This pattern is used to mark textures which could not be loaded +//------------------------------------------------------------------------------- +VOID WINAPI FillFunc(D3DXVECTOR4* pOut, + CONST D3DXVECTOR2* pTexCoord, + CONST D3DXVECTOR2* pTexelSize, + LPVOID pData) + { + UNREFERENCED_PARAMETER(pData); + UNREFERENCED_PARAMETER(pTexelSize); + + // generate a nice checker pattern (yellow/black) + // size of a square: 32 * 32 px + unsigned int iX = (unsigned int)(pTexCoord->x * 256.0f); + unsigned int iY = (unsigned int)(pTexCoord->y * 256.0f); + + bool bBlack = false; + if ((iX / 32) % 2 == 0) + { + if ((iY / 32) % 2 == 0)bBlack = true; + } + else + { + if ((iY / 32) % 2 != 0)bBlack = true; + } + pOut->w = 1.0f; + if (bBlack) + { + pOut->x = pOut->y = pOut->z = 0.0f; + } + else + { + pOut->x = pOut->y = 1.0f; + pOut->z = 0.0f; + } + return; + } + +//------------------------------------------------------------------------------- +// Setup the default texture for a texture channel +// +// Generates a default checker pattern for a texture +//------------------------------------------------------------------------------- +int SetDefaultTexture(IDirect3DTexture9** p_ppiOut) + { + if(FAILED(g_piDevice->CreateTexture( + 256, + 256, + 0, + 0, + D3DFMT_A8R8G8B8, + D3DPOOL_MANAGED, + p_ppiOut, + NULL))) + { + CLogDisplay::Instance().AddEntry("[ERROR] Unable to create default texture", + D3DCOLOR_ARGB(0xFF,0xFF,0,0)); + } + D3DXFillTexture(*p_ppiOut,&FillFunc,NULL); + return 1; + } + + +//------------------------------------------------------------------------------- +// find a valid path to a texture file +// +// Handle 8.3 syntax correctly, search the environment of the +// executable and the asset for a texture with a name very similar to a given one +//------------------------------------------------------------------------------- +bool TryLongerPath(char* szTemp,aiString* p_szString) + { + char szTempB[MAX_PATH]; + + strcpy(szTempB,szTemp); + + // go to the beginning of the file name + char* szFile = strrchr(szTempB,'\\'); + if (!szFile)szFile = strrchr(szTempB,'/'); + + char* szFile2 = szTemp + (szFile - szTempB)+1; + szFile++; + char* szExt = strrchr(szFile,'.')+1; + *szFile = 0; + + strcat(szTempB,"*.*"); + const unsigned int iSize = (const unsigned int) ( szExt - 1 - szFile ); + + HANDLE h; + WIN32_FIND_DATA info; + + // build a list of files + h = FindFirstFile(szTempB, &info); + if (h != INVALID_HANDLE_VALUE) + { + do + { + if (!(strcmp(info.cFileName, ".") == 0 || strcmp(info.cFileName, "..") == 0)) + { + char* szExtFound = strrchr(info.cFileName, '.')+1; + if ((char*)0x1 != szExtFound) + { + if (0 == ASSIMP_stricmp(szExtFound,szExt)) + { + const unsigned int iSizeFound = (const unsigned int) ( + szExtFound - 1 - info.cFileName); + + for (unsigned int i = 0; i < iSizeFound;++i) + info.cFileName[i] = (CHAR)tolower(info.cFileName[i]); + + if (0 == memcmp(info.cFileName,szFile2, std::min(iSizeFound,iSize))) + { + // we have it. Build the full path ... + char* sz = strrchr(szTempB,'*'); + *(sz-2) = 0x0; + + strcat(szTempB,info.cFileName); + + // copy the result string back to the aiString + const size_t iLen = strlen(szTempB); + size_t iLen2 = iLen+1; + iLen2 = iLen2 > MAXLEN ? MAXLEN : iLen2; + memcpy(p_szString->data,szTempB,iLen2); + p_szString->length = iLen; + return true; + } + } + // check whether the 8.3 DOS name is matching + if (0 == ASSIMP_stricmp(info.cAlternateFileName,p_szString->data)) + { + strcat(szTempB,info.cAlternateFileName); + + // copy the result string back to the aiString + const size_t iLen = strlen(szTempB); + size_t iLen2 = iLen+1; + iLen2 = iLen2 > MAXLEN ? MAXLEN : iLen2; + memcpy(p_szString->data,szTempB,iLen2); + p_szString->length = iLen; + return true; + } + } + } + } + while (FindNextFile(h, &info)); + + FindClose(h); + } + return false; + } + +//------------------------------------------------------------------------------- +// find a valid path to a texture file +// +// Handle 8.3 syntax correctly, search the environment of the +// executable and the asset for a texture with a name very similar to a given one +//------------------------------------------------------------------------------- +int FindValidPath(aiString* p_szString) + { + // first check whether we can directly load the file + FILE* pFile = fopen(p_szString->data,"rb"); + if (pFile)fclose(pFile); + else + { + // check whether we can use the directory of + // the asset as relative base + char szTemp[MAX_PATH*2]; + strcpy(szTemp, g_szFileName); + + char* szData = p_szString->data; + if (*szData == '\\' || *szData == '/')++szData; + + char* szEnd = strrchr(szTemp,'\\'); + if (!szEnd) + { + szEnd = strrchr(szTemp,'/'); + if (!szEnd)szEnd = szTemp; + } + szEnd++; + *szEnd = 0; + strcat(szEnd,szData); + + pFile = fopen(szTemp,"rb"); + if (!pFile) + { + // convert the string to lower case + for (unsigned int i = 0;;++i) + { + if ('\0' == szTemp[i])break; + szTemp[i] = (char)tolower(szTemp[i]); + } + + if(TryLongerPath(szTemp,p_szString))return 1; + *szEnd = 0; + + // search common sub directories + strcat(szEnd,"tex\\"); + strcat(szEnd,szData); + + pFile = fopen(szTemp,"rb"); + if (!pFile) + { + if(TryLongerPath(szTemp,p_szString))return 1; + + *szEnd = 0; + + strcat(szEnd,"textures\\"); + strcat(szEnd,szData); + + pFile = fopen(szTemp,"rb"); + if (!pFile) + { + if(TryLongerPath(szTemp, p_szString))return 1; + + // still unable to load ... however, don't spew + // an error message here, simply let it and wait for + // D3DXCreateTextureFromFileEx() to fail ;-) + } + return 0; + } + } + fclose(pFile); + + // copy the result string back to the aiString + const size_t iLen = strlen(szTemp); + size_t iLen2 = iLen+1; + iLen2 = iLen2 > MAXLEN ? MAXLEN : iLen2; + memcpy(p_szString->data,szTemp,iLen2); + p_szString->length = iLen; + } + return 1; + } + +//------------------------------------------------------------------------------- +// Load a texture into memory and create a native D3D texture resource +// +// The function tries to find a valid path for a texture +//------------------------------------------------------------------------------- +int LoadTexture(IDirect3DTexture9** p_ppiOut,aiString* szPath) + { + // first get a valid path to the texture + FindValidPath(szPath); + + *p_ppiOut = NULL; + + // then call D3DX to load the texture + if (FAILED(D3DXCreateTextureFromFileEx( + g_piDevice, + szPath->data, + D3DX_DEFAULT, + D3DX_DEFAULT, + 0, + 0, + D3DFMT_A8R8G8B8, + D3DPOOL_MANAGED, + D3DX_DEFAULT, + D3DX_DEFAULT, + 0, + NULL, + NULL, + p_ppiOut))) + { + // error ... use the default texture instead + std::string sz = "[ERROR] Unable to load texture: "; + sz.append(szPath->data); + CLogDisplay::Instance().AddEntry(sz,D3DCOLOR_ARGB(0xFF,0xFF,0x0,0x0)); + + SetDefaultTexture(p_ppiOut); + } + return 1; + } + + + +//------------------------------------------------------------------------------- +// Delete all resources of a given material +// +// Must be called before CreateMaterial() to prevent memory leaking +//------------------------------------------------------------------------------- +void DeleteMaterial(AssetHelper::MeshHelper* pcIn) + { + if (!pcIn || !pcIn->piEffect)return; + pcIn->piEffect->Release(); + + // release all textures associated with the material + if (pcIn->piDiffuseTexture) + { + pcIn->piDiffuseTexture->Release(); + pcIn->piDiffuseTexture = NULL; + } + if (pcIn->piSpecularTexture) + { + pcIn->piSpecularTexture->Release(); + pcIn->piSpecularTexture = NULL; + } + if (pcIn->piEmissiveTexture) + { + pcIn->piEmissiveTexture->Release(); + pcIn->piEmissiveTexture = NULL; + } + if (pcIn->piAmbientTexture) + { + pcIn->piAmbientTexture->Release(); + pcIn->piAmbientTexture = NULL; + } + if (pcIn->piOpacityTexture) + { + pcIn->piOpacityTexture->Release(); + pcIn->piOpacityTexture = NULL; + } + if (pcIn->piNormalTexture) + { + pcIn->piNormalTexture->Release(); + pcIn->piNormalTexture = NULL; + } + } + + +//------------------------------------------------------------------------------- +// Convert a height map to a normal map if necessary +// +// The function tries to detect the type of a texture automatically. +// However, this wont work in every case. +//------------------------------------------------------------------------------- +void HMtoNMIfNecessary(IDirect3DTexture9* piTexture,IDirect3DTexture9** piTextureOut, + bool bWasOriginallyHM = true) + { + bool bMustConvert = false; + uintptr_t iElement = 3; + + *piTextureOut = piTexture; + + // Lock the input texture and try to determine its type. + // Criterias: + // - If r,g,b channel are identical it MUST be a height map + // - If one of the rgb channels is used and the others are empty it + // must be a height map, too. + // - If the average color of the whole image is something inside the + // purple range we can be sure it is a normal map + // + // - Otherwise we assume it is a normal map + // To increase performance we take not every pixel + + D3DLOCKED_RECT sRect; + D3DSURFACE_DESC sDesc; + piTexture->GetLevelDesc(0,&sDesc); + if (FAILED(piTexture->LockRect(0,&sRect,NULL,D3DLOCK_READONLY))) + { + return; + } + const int iPitchDiff = (int)sRect.Pitch - (int)(sDesc.Width * 4); + + struct SColor + { + union + { + struct {unsigned char b,g,r,a;}; + char _array[4]; + }; + }; + const SColor* pcData = (const SColor*)sRect.pBits; + + union + { + const SColor* pcPointer; + const unsigned char* pcCharPointer; + }; + pcPointer = pcData; + + // 1. If r,g,b channel are identical it MUST be a height map + bool bIsEqual = true; + for (unsigned int y = 0; y < sDesc.Height;++y) + { + for (unsigned int x = 0; x < sDesc.Width;++x) + { + if (pcPointer->b != pcPointer->r || pcPointer->b != pcPointer->g) + { + bIsEqual = false; + break; + } + pcPointer++; + } + pcCharPointer += iPitchDiff; + } + if (bIsEqual)bMustConvert = true; + else + { + // 2. If one of the rgb channels is used and the others are empty it + // must be a height map, too. + pcPointer = pcData; + while (*pcCharPointer == 0)pcCharPointer++; + + iElement = (uintptr_t)(pcCharPointer - (unsigned char*)pcData) % 4; + unsigned int aiIndex[3] = {0,1,2}; + if (3 != iElement)aiIndex[iElement] = 3; + + pcPointer = pcData; + + bIsEqual = true; + if (3 != iElement) + { + for (unsigned int y = 0; y < sDesc.Height;++y) + { + for (unsigned int x = 0; x < sDesc.Width;++x) + { + for (unsigned int ii = 0; ii < 3;++ii) + { + // don't take the alpha channel into account. + // if the texture was stored n RGB888 format D3DX has + // converted it to ARGB8888 format with a fixed alpha channel + if (aiIndex[ii] != 3 && pcPointer->_array[aiIndex[ii]] != 0) + { + bIsEqual = false; + break; + } + } + pcPointer++; + } + pcCharPointer += iPitchDiff; + } + if (bIsEqual)bMustConvert = true; + else + { + // If the average color of the whole image is something inside the + // purple range we can be sure it is a normal map + + // (calculate the average color line per line to prevent overflows!) + pcPointer = pcData; + aiColor3D clrColor; + for (unsigned int y = 0; y < sDesc.Height;++y) + { + aiColor3D clrColorLine; + for (unsigned int x = 0; x < sDesc.Width;++x) + { + clrColorLine.r += pcPointer->r; + clrColorLine.g += pcPointer->g; + clrColorLine.b += pcPointer->b; + pcPointer++; + } + clrColor.r += clrColorLine.r /= (float)sDesc.Width; + clrColor.g += clrColorLine.g /= (float)sDesc.Width; + clrColor.b += clrColorLine.b /= (float)sDesc.Width; + pcCharPointer += iPitchDiff; + } + clrColor.r /= (float)sDesc.Height; + clrColor.g /= (float)sDesc.Height; + clrColor.b /= (float)sDesc.Height; + + if (!(clrColor.b > 215 && + clrColor.r > 100 && clrColor.r < 140 && + clrColor.g > 100 && clrColor.g < 140)) + { + // Unable to detect. Believe the original value obtained from the loader + if (bWasOriginallyHM) + { + bMustConvert = true; + } + } + } + } + } + + piTexture->UnlockRect(0); + + // if the input data is assumed to be a height map we'll + // need to convert it NOW + if (bMustConvert) + { + D3DSURFACE_DESC sDesc; + piTexture->GetLevelDesc(0, &sDesc); + + IDirect3DTexture9* piTempTexture; + if(FAILED(g_piDevice->CreateTexture( + sDesc.Width, + sDesc.Height, + piTexture->GetLevelCount(), + sDesc.Usage, + sDesc.Format, + sDesc.Pool, &piTempTexture, NULL))) + { + CLogDisplay::Instance().AddEntry( + "[ERROR] Unable to create normal map texture", + D3DCOLOR_ARGB(0xFF,0xFF,0x0,0x0)); + return; + } + + DWORD dwFlags; + if (3 == iElement)dwFlags = D3DX_CHANNEL_LUMINANCE; + else if (2 == iElement)dwFlags = D3DX_CHANNEL_RED; + else if (1 == iElement)dwFlags = D3DX_CHANNEL_GREEN; + else /*if (0 == iElement)*/dwFlags = D3DX_CHANNEL_BLUE; + + if(FAILED(D3DXComputeNormalMap(piTempTexture, + piTexture,NULL,0,dwFlags,1.0f))) + { + CLogDisplay::Instance().AddEntry( + "[ERROR] Unable to compute normal map from height map", + D3DCOLOR_ARGB(0xFF,0xFF,0x0,0x0)); + + piTempTexture->Release(); + return; + } + *piTextureOut = piTempTexture; + piTexture->Release(); + } + } + + +//------------------------------------------------------------------------------- +// Search for non-opaque pixels in a texture +// +// A pixel is considered to be non-opaque if its alpha value s less than 255 +//------------------------------------------------------------------------------- +bool HasAlphaPixels(IDirect3DTexture9* piTexture) + { + D3DLOCKED_RECT sRect; + D3DSURFACE_DESC sDesc; + piTexture->GetLevelDesc(0,&sDesc); + if (FAILED(piTexture->LockRect(0,&sRect,NULL,D3DLOCK_READONLY))) + { + return false; + } + const int iPitchDiff = (int)sRect.Pitch - (int)(sDesc.Width * 4); + + struct SColor + { + unsigned char b,g,r,a;; + }; + const SColor* pcData = (const SColor*)sRect.pBits; + + union + { + const SColor* pcPointer; + const unsigned char* pcCharPointer; + }; + pcPointer = pcData; + for (unsigned int y = 0; y < sDesc.Height;++y) + { + for (unsigned int x = 0; x < sDesc.Width;++x) + { + if (pcPointer->a != 0xFF) + { + piTexture->UnlockRect(0); + return true; + } + pcPointer++; + } + pcCharPointer += iPitchDiff; + } + piTexture->UnlockRect(0); + return false; + } + + +//------------------------------------------------------------------------------- +// Create the material for a mesh. +// +// The function checks whether an identical shader is already in use. +// A shader is considered to be identical if it has the same input signature +// and takes the same number of texture channels. +//------------------------------------------------------------------------------- +int CreateMaterial(AssetHelper::MeshHelper* pcMesh,const aiMesh* pcSource) + { + ID3DXBuffer* piBuffer; + + D3DXMACRO sMacro[32]; + + // extract all properties from the ASSIMP material structure + const aiMaterial* pcMat = g_pcAsset->pcScene->mMaterials[pcSource->mMaterialIndex]; + + // + // DIFFUSE COLOR -------------------------------------------------- + // + if(AI_SUCCESS != aiGetMaterialColor(pcMat,AI_MATKEY_COLOR_DIFFUSE, + (aiColor4D*)&pcMesh->vDiffuseColor)) + { + pcMesh->vDiffuseColor.x = 1.0f; + pcMesh->vDiffuseColor.y = 1.0f; + pcMesh->vDiffuseColor.z = 1.0f; + pcMesh->vDiffuseColor.w = 1.0f; + } + // + // SPECULAR COLOR -------------------------------------------------- + // + if(AI_SUCCESS != aiGetMaterialColor(pcMat,AI_MATKEY_COLOR_SPECULAR, + (aiColor4D*)&pcMesh->vSpecularColor)) + { + pcMesh->vSpecularColor.x = 1.0f; + pcMesh->vSpecularColor.y = 1.0f; + pcMesh->vSpecularColor.z = 1.0f; + pcMesh->vSpecularColor.w = 1.0f; + } + // + // AMBIENT COLOR -------------------------------------------------- + // + if(AI_SUCCESS != aiGetMaterialColor(pcMat,AI_MATKEY_COLOR_AMBIENT, + (aiColor4D*)&pcMesh->vAmbientColor)) + { + pcMesh->vAmbientColor.x = 0.0f; + pcMesh->vAmbientColor.y = 0.0f; + pcMesh->vAmbientColor.z = 0.0f; + pcMesh->vAmbientColor.w = 1.0f; + } + // + // EMISSIVE COLOR ------------------------------------------------- + // + if(AI_SUCCESS != aiGetMaterialColor(pcMat,AI_MATKEY_COLOR_EMISSIVE, + (aiColor4D*)&pcMesh->vEmissiveColor)) + { + pcMesh->vEmissiveColor.x = 0.0f; + pcMesh->vEmissiveColor.y = 0.0f; + pcMesh->vEmissiveColor.z = 0.0f; + pcMesh->vEmissiveColor.w = 1.0f; + } + + // + // Opacity -------------------------------------------------------- + // + if(AI_SUCCESS != aiGetMaterialFloat(pcMat,AI_MATKEY_OPACITY,&pcMesh->fOpacity)) + { + pcMesh->fOpacity = 1.0f; + } + + // + // Shading Model -------------------------------------------------- + // + bool bDefault = false; + if(AI_SUCCESS != aiGetMaterialInteger(pcMat,AI_MATKEY_SHADING_MODEL,(int*)&pcMesh->eShadingMode )) + { + bDefault = true; + pcMesh->eShadingMode = aiShadingMode_Gouraud; + } + + + // + // Shininess ------------------------------------------------------ + // + if(AI_SUCCESS != aiGetMaterialFloat(pcMat,AI_MATKEY_SHININESS,&pcMesh->fShininess)) + { + // assume 15 as default shininess + pcMesh->fShininess = 15.0f; + } + else if (bDefault)pcMesh->eShadingMode = aiShadingMode_Phong; + + aiString szPath; + + // + // DIFFUSE TEXTURE ------------------------------------------------ + // + if(AI_SUCCESS == aiGetMaterialString(pcMat,AI_MATKEY_TEXTURE_DIFFUSE(0),&szPath)) + { + LoadTexture(&pcMesh->piDiffuseTexture,&szPath); + } + + // + // SPECULAR TEXTURE ------------------------------------------------ + // + if(AI_SUCCESS == aiGetMaterialString(pcMat,AI_MATKEY_TEXTURE_SPECULAR(0),&szPath)) + { + LoadTexture(&pcMesh->piSpecularTexture,&szPath); + } + + // + // OPACITY TEXTURE ------------------------------------------------ + // + if(AI_SUCCESS == aiGetMaterialString(pcMat,AI_MATKEY_TEXTURE_OPACITY(0),&szPath)) + { + LoadTexture(&pcMesh->piOpacityTexture,&szPath); + } + else + { + // try to find out whether the diffuse texture has any + // non-opaque pixels. If we find a few use it as opacity texture + if (pcMesh->piDiffuseTexture && HasAlphaPixels(pcMesh->piDiffuseTexture)) + { + pcMesh->piOpacityTexture = pcMesh->piDiffuseTexture; + pcMesh->piOpacityTexture->AddRef(); + } + } + + // + // AMBIENT TEXTURE ------------------------------------------------ + // + if(AI_SUCCESS == aiGetMaterialString(pcMat,AI_MATKEY_TEXTURE_AMBIENT(0),&szPath)) + { + LoadTexture(&pcMesh->piAmbientTexture,&szPath); + } + + // + // EMISSIVE TEXTURE ------------------------------------------------ + // + if(AI_SUCCESS == aiGetMaterialString(pcMat,AI_MATKEY_TEXTURE_EMISSIVE(0),&szPath)) + { + LoadTexture(&pcMesh->piEmissiveTexture,&szPath); + } + + // + // NORMAL/HEIGHT MAP ------------------------------------------------ + // + bool bHM = false; + if(AI_SUCCESS == aiGetMaterialString(pcMat,AI_MATKEY_TEXTURE_NORMALS(0),&szPath)) + { + LoadTexture(&pcMesh->piNormalTexture,&szPath); + } + else + { + if(AI_SUCCESS == aiGetMaterialString(pcMat,AI_MATKEY_TEXTURE_BUMP(0),&szPath)) + { + LoadTexture(&pcMesh->piNormalTexture,&szPath); + } + bHM = true; + } + // normal/height maps are sometimes mixed up. Try to detect the type + // of the texture automatically + if (pcMesh->piNormalTexture) + { + HMtoNMIfNecessary(pcMesh->piNormalTexture, &pcMesh->piNormalTexture,bHM); + } + + // check whether a global background texture is contained + // in this material. Some loaders set this value ... + if(AI_SUCCESS == aiGetMaterialString(pcMat,AI_MATKEY_GLOBAL_BACKGROUND_IMAGE,&szPath)) + { + CBackgroundPainter::Instance().SetTextureBG(szPath.data); + } + + // BUGFIX: If the shininess is 0.0f disable phong lighting + // This is a workaround for some meshes in the DX SDK (e.g. tiny.x) + if (0.0f == pcMesh->fShininess) + { + pcMesh->eShadingMode = aiShadingMode_Gouraud; + } + + // check whether we have already a material using the same + // shader. This will decrease loading time rapidly ... + for (unsigned int i = 0; i < g_pcAsset->pcScene->mNumMeshes;++i) + { + if (g_pcAsset->pcScene->mMeshes[i] == pcSource) + { + break; + } + AssetHelper::MeshHelper* pc = g_pcAsset->apcMeshes[i]; + + if ((pcMesh->piDiffuseTexture != NULL ? true : false) != + (pc->piDiffuseTexture != NULL ? true : false)) + continue; + if ((pcMesh->piSpecularTexture != NULL ? true : false) != + (pc->piSpecularTexture != NULL ? true : false)) + continue; + if ((pcMesh->piAmbientTexture != NULL ? true : false) != + (pc->piAmbientTexture != NULL ? true : false)) + continue; + if ((pcMesh->piEmissiveTexture != NULL ? true : false) != + (pc->piEmissiveTexture != NULL ? true : false)) + continue; + if ((pcMesh->piNormalTexture != NULL ? true : false) != + (pc->piNormalTexture != NULL ? true : false)) + continue; + if ((pcMesh->piOpacityTexture != NULL ? true : false) != + (pc->piOpacityTexture != NULL ? true : false)) + continue; + if ((pcMesh->eShadingMode != aiShadingMode_Gouraud ? true : false) != + (pc->eShadingMode != aiShadingMode_Gouraud ? true : false)) + continue; + + if ((pcMesh->fOpacity != 1.0f ? true : false) != (pc->fOpacity != 1.0f ? true : false)) + continue; + + // we can reuse this material + if (pc->piEffect) + { + pcMesh->piEffect = pc->piEffect; + pc->bSharedFX = pcMesh->bSharedFX = true; + pcMesh->piEffect->AddRef(); + return 2; + } + } + g_iShaderCount++; + + // build macros for the HLSL compiler + unsigned int iCurrent = 0; + if (pcMesh->piDiffuseTexture) + { + sMacro[iCurrent].Name = "AV_DIFFUSE_TEXTURE"; + sMacro[iCurrent].Definition = "1"; + ++iCurrent; + } + if (pcMesh->piSpecularTexture) + { + sMacro[iCurrent].Name = "AV_SPECULAR_TEXTURE"; + sMacro[iCurrent].Definition = "1"; + ++iCurrent; + } + if (pcMesh->piAmbientTexture) + { + sMacro[iCurrent].Name = "AV_AMBIENT_TEXTURE"; + sMacro[iCurrent].Definition = "1"; + ++iCurrent; + } + if (pcMesh->piEmissiveTexture) + { + sMacro[iCurrent].Name = "AV_EMISSIVE_TEXTURE"; + sMacro[iCurrent].Definition = "1"; + ++iCurrent; + } + if (pcMesh->piNormalTexture) + { + sMacro[iCurrent].Name = "AV_NORMAL_TEXTURE"; + sMacro[iCurrent].Definition = "1"; + ++iCurrent; + } + if (pcMesh->piOpacityTexture) + { + sMacro[iCurrent].Name = "AV_OPACITY_TEXTURE"; + sMacro[iCurrent].Definition = "1"; + ++iCurrent; + + if (pcMesh->piOpacityTexture == pcMesh->piDiffuseTexture) + { + sMacro[iCurrent].Name = "AV_OPACITY_TEXTURE_REGISTER_MASK"; + sMacro[iCurrent].Definition = "a"; + ++iCurrent; + } + else + { + sMacro[iCurrent].Name = "AV_OPACITY_TEXTURE_REGISTER_MASK"; + sMacro[iCurrent].Definition = "r"; + ++iCurrent; + } + } + + + if (pcMesh->eShadingMode != aiShadingMode_Gouraud && !g_sOptions.bNoSpecular) + { + sMacro[iCurrent].Name = "AV_SPECULAR_COMPONENT"; + sMacro[iCurrent].Definition = "1"; + ++iCurrent; + } + if (1.0f != pcMesh->fOpacity) + { + sMacro[iCurrent].Name = "AV_OPACITY"; + sMacro[iCurrent].Definition = "1"; + ++iCurrent; + } + + + + // If a cubemap is active, we'll need to lookup it for calculating + // a physically correct reflection + if (CBackgroundPainter::TEXTURE_CUBE == CBackgroundPainter::Instance().GetMode()) + { + sMacro[iCurrent].Name = "AV_SKYBOX_LOOKUP"; + sMacro[iCurrent].Definition = "1"; + ++iCurrent; + } + sMacro[iCurrent].Name = NULL; + sMacro[iCurrent].Definition = NULL; + + // compile the shader + if(FAILED( D3DXCreateEffect(g_piDevice, + g_szMaterialShader.c_str(),(UINT)g_szMaterialShader.length(), + (const D3DXMACRO*)sMacro,NULL,0,NULL,&pcMesh->piEffect,&piBuffer))) + { + // failed to compile the shader + if( piBuffer) + { + MessageBox(g_hDlg,(LPCSTR)piBuffer->GetBufferPointer(),"HLSL",MB_OK); + piBuffer->Release(); + } + // use the default material instead + if (g_piDefaultEffect) + { + pcMesh->piEffect = g_piDefaultEffect; + g_piDefaultEffect->AddRef(); + } + + // get the name of the material and use it in the log message + if(AI_SUCCESS == aiGetMaterialString(pcMat,AI_MATKEY_NAME,&szPath) && + '\0' != szPath.data[0]) + { + std::string sz = "[ERROR] Unable to load material: "; + sz.append(szPath.data); + CLogDisplay::Instance().AddEntry(sz); + } + else + { + CLogDisplay::Instance().AddEntry("Unable to load material: UNNAMED"); + } + return 0; + } + if( piBuffer) piBuffer->Release(); + + + // now commit all constants to the shader + // + // This is not necessary for shared shader. Shader constants for + // shared shaders are automatically recommited before the shader + // is being used for a particular mesh + + if (1.0f != pcMesh->fOpacity) + pcMesh->piEffect->SetFloat("TRANSPARENCY",pcMesh->fOpacity); + if (pcMesh->eShadingMode != aiShadingMode_Gouraud && !g_sOptions.bNoSpecular) + pcMesh->piEffect->SetFloat("SPECULARITY",pcMesh->fShininess); + + pcMesh->piEffect->SetVector("DIFFUSE_COLOR",&pcMesh->vDiffuseColor); + pcMesh->piEffect->SetVector("SPECULAR_COLOR",&pcMesh->vSpecularColor); + pcMesh->piEffect->SetVector("AMBIENT_COLOR",&pcMesh->vAmbientColor); + pcMesh->piEffect->SetVector("EMISSIVE_COLOR",&pcMesh->vEmissiveColor); + + if (pcMesh->piDiffuseTexture) + pcMesh->piEffect->SetTexture("DIFFUSE_TEXTURE",pcMesh->piDiffuseTexture); + if (pcMesh->piOpacityTexture) + pcMesh->piEffect->SetTexture("OPACITY_TEXTURE",pcMesh->piOpacityTexture); + if (pcMesh->piSpecularTexture) + pcMesh->piEffect->SetTexture("SPECULAR_TEXTURE",pcMesh->piSpecularTexture); + if (pcMesh->piAmbientTexture) + pcMesh->piEffect->SetTexture("AMBIENT_TEXTURE",pcMesh->piAmbientTexture); + if (pcMesh->piEmissiveTexture) + pcMesh->piEffect->SetTexture("EMISSIVE_TEXTURE",pcMesh->piEmissiveTexture); + if (pcMesh->piNormalTexture) + pcMesh->piEffect->SetTexture("NORMAL_TEXTURE",pcMesh->piNormalTexture); + + if (CBackgroundPainter::TEXTURE_CUBE == CBackgroundPainter::Instance().GetMode()) + { + pcMesh->piEffect->SetTexture("lw_tex_envmap",CBackgroundPainter::Instance().GetTexture()); + } + return 1; + } +}; \ No newline at end of file diff --git a/tools/assimp_view/MessageProc.cpp b/tools/assimp_view/MessageProc.cpp new file mode 100644 index 000000000..ef857840a --- /dev/null +++ b/tools/assimp_view/MessageProc.cpp @@ -0,0 +1,1121 @@ +//------------------------------------------------------------------------------- +/** +* This program is distributed under the terms of the GNU Lesser General +* Public License (LGPL). +* +* ASSIMP Viewer Utility +* +*/ +//------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "assimp_view.h" + + +namespace AssimpView { + +// Static array to keep custom color values +COLORREF g_aclCustomColors[16] = + {0}; + + +//------------------------------------------------------------------------------- +// Setup file associations for all formats supported by the library +// +// File associations are registered in HKCU\Software\Classes. They might +// be overwritten by global file associations. +//------------------------------------------------------------------------------- +void MakeFileAssociations() + { + /* + ; .wscript + root: HKCR; Flags: deletekey; Subkey: ".uscript"; ValueType: string; ValueData: "UE_WSCRIPT_CLASS"; Components: rt + root: HKCR; Flags: deletekey; Subkey: "UE_WSCRIPT_CLASS"; ValueName:; ValueType: string; ValueData: "UtopicEngine Console Script"; Components: rt + root: HKCR; Flags: deletekey; Subkey: "UE_WSCRIPT_CLASS\shell\open\command"; ValueName:; ValueType: string; ValueData: "notepad.exe %1"; Components: rt + */ + char szTemp2[MAX_PATH]; + char szTemp[MAX_PATH + 10]; + + GetModuleFileName(NULL,szTemp2,MAX_PATH); + sprintf(szTemp,"%s %%1",szTemp2); + + HKEY hTemp; + + // ------------------------------------------------- + // .3ds + // ------------------------------------------------- + RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\Classes\\.3ds",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + RegSetValueEx(hTemp,"",0,REG_SZ,(const BYTE*)"AV3DSCLASS",(DWORD)strlen("AV3DSCLASS")+1); + RegCloseKey(hTemp); + + RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\Classes\\AV3DSCLASS",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + RegCloseKey(hTemp); + + RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\Classes\\AV3DSCLASS\\shell\\open\\command",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + RegSetValueEx(hTemp,"",0,REG_SZ,(const BYTE*)szTemp,(DWORD)strlen(szTemp)+1); + RegCloseKey(hTemp); + + // ------------------------------------------------- + // .x + // ------------------------------------------------- + RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\Classes\\.x",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + RegSetValueEx(hTemp,"",0,REG_SZ,(const BYTE*)"AVXCLASS",(DWORD)strlen("AVXCLASS")+1); + RegCloseKey(hTemp); + + RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\Classes\\AVXCLASS",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + RegCloseKey(hTemp); + + RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\Classes\\AVXCLASS\\shell\\open\\command",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + RegSetValueEx(hTemp,"",0,REG_SZ,(const BYTE*)szTemp,(DWORD)strlen(szTemp)+1); + RegCloseKey(hTemp); + + // ------------------------------------------------- + // .obj + // ------------------------------------------------- + RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\Classes\\.obj",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + RegSetValueEx(hTemp,"",0,REG_SZ,(const BYTE*)"AVOBJCLASS",(DWORD)strlen("AVOBJCLASS")+1); + RegCloseKey(hTemp); + + RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\Classes\\AVOBJCLASS",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + RegCloseKey(hTemp); + + RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\Classes\\AVOBJCLASS\\shell\\open\\command",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + RegSetValueEx(hTemp,"",0,REG_SZ,(const BYTE*)szTemp,(DWORD)strlen(szTemp)+1); + RegCloseKey(hTemp); + + // ------------------------------------------------- + // .ms3d + // ------------------------------------------------- + RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\Classes\\.ms3d",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + RegSetValueEx(hTemp,"",0,REG_SZ,(const BYTE*)"AVMS3DCLASS",(DWORD)strlen("AVMS3DCLASS")+1); + RegCloseKey(hTemp); + + RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\Classes\\AVMS3DCLASS",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + RegCloseKey(hTemp); + + RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\Classes\\AVMS3DCLASS\\shell\\open\\command",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + RegSetValueEx(hTemp,"",0,REG_SZ,(const BYTE*)szTemp,(DWORD)strlen(szTemp)+1); + RegCloseKey(hTemp); + + // ------------------------------------------------- + // .md3 + // ------------------------------------------------- + RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\Classes\\.md3",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + RegSetValueEx(hTemp,"",0,REG_SZ,(const BYTE*)"AVMD3CLASS",(DWORD)strlen("AVMD3CLASS")+1); + RegCloseKey(hTemp); + + RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\Classes\\AVMD3CLASS",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + RegCloseKey(hTemp); + + RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\Classes\\AVMD3CLASS\\shell\\open\\command",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + RegSetValueEx(hTemp,"",0,REG_SZ,(const BYTE*)szTemp,(DWORD)strlen(szTemp)+1); + RegCloseKey(hTemp); + + // ------------------------------------------------- + // .md2 + // ------------------------------------------------- + RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\Classes\\.md2",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + RegSetValueEx(hTemp,"",0,REG_SZ,(const BYTE*)"AVMD2CLASS",(DWORD)strlen("AVMD2CLASS")+1); + RegCloseKey(hTemp); + + RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\Classes\\AVMD3CLASS",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + RegCloseKey(hTemp); + + RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\Classes\\AVMD2CLASS\\shell\\open\\command",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + RegSetValueEx(hTemp,"",0,REG_SZ,(const BYTE*)szTemp,(DWORD)strlen(szTemp)+1); + RegCloseKey(hTemp); + + // ------------------------------------------------- + // .md4 + // ------------------------------------------------- + RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\Classes\\.md4",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + RegSetValueEx(hTemp,"",0,REG_SZ,(const BYTE*)"AVMD4CLASS",(DWORD)strlen("AVMD4CLASS")+1); + RegCloseKey(hTemp); + + RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\Classes\\AVMD4CLASS",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + RegCloseKey(hTemp); + + RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\Classes\\AVMD4CLASS\\shell\\open\\command",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + RegSetValueEx(hTemp,"",0,REG_SZ,(const BYTE*)szTemp,(DWORD)strlen(szTemp)+1); + RegCloseKey(hTemp); + + // ------------------------------------------------- + // .md5 + // ------------------------------------------------- + RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\Classes\\.md5",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + RegSetValueEx(hTemp,"",0,REG_SZ,(const BYTE*)"AVMD5CLASS",(DWORD)strlen("AVMD5CLASS")+1); + RegCloseKey(hTemp); + + RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\Classes\\AVMD5CLASS",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + RegCloseKey(hTemp); + + RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\Classes\\AVMD5CLASS\\shell\\open\\command",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + RegSetValueEx(hTemp,"",0,REG_SZ,(const BYTE*)szTemp,(DWORD)strlen(szTemp)+1); + RegCloseKey(hTemp); + + // ------------------------------------------------- + // .ply + // ------------------------------------------------- + RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\Classes\\.ply",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + RegSetValueEx(hTemp,"",0,REG_SZ,(const BYTE*)"AVPLYCLASS",(DWORD)strlen("AVPLYCLASS")+1); + RegCloseKey(hTemp); + + RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\Classes\\AVPLYCLASS",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + RegCloseKey(hTemp); + + RegCreateKeyEx(HKEY_CURRENT_USER,"Software\\Classes\\AVPLYCLASS\\shell\\open\\command",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + RegSetValueEx(hTemp,"",0,REG_SZ,(const BYTE*)szTemp,(DWORD)strlen(szTemp)+1); + RegCloseKey(hTemp); + + CLogDisplay::Instance().AddEntry("[OK] File assocations have been registered", + D3DCOLOR_ARGB(0xFF,0,0xFF,0)); + } + + + +//------------------------------------------------------------------------------- +// Recreate all specular materials depending on the current specularity settings +// +// Diffuse-only materials are ignored. +// Must be called after specular highlights have been toggled +//------------------------------------------------------------------------------- +void UpdateSpecularMaterials() + { + if (g_pcAsset && g_pcAsset->pcScene) + { + for (unsigned int i = 0; i < g_pcAsset->pcScene->mNumMeshes;++i) + { + if (aiShadingMode_Phong == g_pcAsset->apcMeshes[i]->eShadingMode) + { + DeleteMaterial(g_pcAsset->apcMeshes[i]); + CreateMaterial(g_pcAsset->apcMeshes[i],g_pcAsset->pcScene->mMeshes[i]); + } + } + } + } + +//------------------------------------------------------------------------------- +// Handle command line parameters +// +// The function loads an asset specified on the command line as first argument +// Other command line parameters are not handled +//------------------------------------------------------------------------------- +void HandleCommandLine(char* p_szCommand) + { + char* sz = p_szCommand; + //bool bQuak = false; + + if (strlen(sz) < 2)return; + + if (*sz == '\"') + { + char* sz2 = strrchr(sz,'\"'); + if (sz2)*sz2 = 0; + } + strcpy( g_szFileName, sz ); + LoadAsset(); + } + +//------------------------------------------------------------------------------- +// Main message procedure of the application +// +// The function handles all incoming messages for the main window. +// However, if does not directly process input commands. +// NOTE: Due to the impossibility to process WM_CHAR messages in dialogs +// properly the code for all hotkeys has been moved to the WndMain +//------------------------------------------------------------------------------- +INT_PTR CALLBACK MessageProc(HWND hwndDlg,UINT uMsg, + WPARAM wParam,LPARAM lParam) + { + UNREFERENCED_PARAMETER(lParam); + UNREFERENCED_PARAMETER(wParam); + + int xPos,yPos; + int xPos2,yPos2; + int fHalfX; + int fHalfY; + + TRACKMOUSEEVENT sEvent; + switch (uMsg) + { + case WM_INITDIALOG: + + CheckDlgButton(hwndDlg,IDC_TOGGLEMS,BST_CHECKED); + CheckDlgButton(hwndDlg,IDC_ZOOM,BST_CHECKED); + CheckDlgButton(hwndDlg,IDC_AUTOROTATE,BST_CHECKED); + + SetDlgItemText(hwndDlg,IDC_EVERT,"0"); + SetDlgItemText(hwndDlg,IDC_EFACE,"0"); + SetDlgItemText(hwndDlg,IDC_EMAT,"0"); + SetDlgItemText(hwndDlg,IDC_ESHADER,"0"); + return TRUE; + + case WM_MOUSEWHEEL: + + if (!g_bFPSView) + { + g_sCamera.vPos.z += GET_WHEEL_DELTA_WPARAM(wParam) / 50.0f; + } + else + { + g_sCamera.vPos += (GET_WHEEL_DELTA_WPARAM(wParam) / 50.0f) * + g_sCamera.vLookAt.Normalize(); + } + return TRUE; + + case WM_MOUSELEAVE: + + g_bMousePressed = false; + g_bMousePressedR = false; + g_bMousePressedM = false; + g_bMousePressedBoth = false; + return TRUE; + + case WM_LBUTTONDBLCLK: + + CheckDlgButton(hwndDlg,IDC_AUTOROTATE, + IsDlgButtonChecked(hwndDlg,IDC_AUTOROTATE) == BST_CHECKED + ? BST_UNCHECKED : BST_CHECKED); + + g_sOptions.bRotate = !g_sOptions.bRotate; + return TRUE; + + + case WM_CLOSE: + PostQuitMessage(0); + DestroyWindow(hwndDlg); + return TRUE; + + case WM_LBUTTONDOWN: + g_bMousePressed = true; + + // register a mouse track handler to be sure we'll know + // when the mouse leaves the display view again + sEvent.cbSize = sizeof(TRACKMOUSEEVENT); + sEvent.dwFlags = TME_LEAVE; + sEvent.hwndTrack = g_hDlg; + sEvent.dwHoverTime = HOVER_DEFAULT; + TrackMouseEvent(&sEvent); + + if (g_bMousePressedR) + { + g_bMousePressed = false; + g_bMousePressedR = false; + g_bMousePressedBoth = true; + return TRUE; + } + + // need to determine the position of the mouse and the + // distance from the center + xPos = (int)(short)LOWORD(lParam); + yPos = (int)(short)HIWORD(lParam); + xPos -= 10; + yPos -= 10; + xPos2 = xPos-3; + yPos2 = yPos-5; + + RECT sRect; + GetWindowRect(GetDlgItem(g_hDlg,IDC_RT),&sRect); + sRect.right -= sRect.left; + sRect.bottom -= sRect.top; + + // if the mouse klick was inside the viewer panel + // give the focus to it + if (xPos > 0 && xPos < sRect.right && yPos > 0 && yPos < sRect.bottom) + { + SetFocus(GetDlgItem(g_hDlg,IDC_RT)); + } + + // g_bInvert stores whether the mouse has started on the negative + // x or on the positive x axis of the imaginary coordinate system + // with origin p at the center of the HUD texture + xPos -= sRect.right/2; + yPos -= sRect.bottom/2; + + if (xPos > 0)g_bInvert = true; + else g_bInvert = false; + + D3DSURFACE_DESC sDesc; + g_pcTexture->GetLevelDesc(0,&sDesc); + + fHalfX = (int)(((float)sRect.right-(float)sDesc.Width) / 2.0f); + fHalfY = (int)(((float)sRect.bottom-(float)sDesc.Height) / 2.0f); + + // Determine the input operation to perform for this position + g_eClick = EClickPos_Outside; + if (xPos2 >= fHalfX && xPos2 < fHalfX + (int)sDesc.Width && + yPos2 >= fHalfY && yPos2 < fHalfY + (int)sDesc.Height && + NULL != g_szImageMask) + { + // inside the texture. Lookup the grayscale value from it + xPos2 -= fHalfX; + yPos2 -= fHalfY; + + unsigned char chValue = g_szImageMask[xPos2 + yPos2 * sDesc.Width]; + if (chValue > 0xFF-20) + { + g_eClick = EClickPos_Circle; + } + else if (chValue < 0xFF-20 && chValue > 185) + { + g_eClick = EClickPos_CircleHor; + } + else if (chValue > 0x10 && chValue < 185) + { + g_eClick = EClickPos_CircleVert; + } + } + + // OLD version of this code. Not using a texture lookup to + // determine the exact position, but using maths and the fact + // that we have a circle f(x) = m +rx ;-) +#if 0 + g_eClick = EClickPos_Circle; + if (yPos < 10 && yPos > -10) + { + if ((xPos2 > fHalfX-5 && xPos2 < fHalfX+15) || + (xPos2 > fHalfX+(int)sDesc.Width-10 && xPos2 < fHalfX+(int)sDesc.Width+10)) + { + g_eClick = EClickPos_CircleHor; + } + } + else if (xPos < 10 && xPos > -10) + { + if ((yPos2 > fHalfY-5 && yPos2 < fHalfY+15) || + (yPos2 > fHalfY+(int)sDesc.Height-10 && yPos2 < fHalfY+(int)sDesc.Height+10)) + { + g_eClick = EClickPos_CircleVert; + } + } + else if (sqrtf((float)(xPos * xPos + yPos * yPos)) > (float)(sDesc.Width/2)) + { + g_eClick = EClickPos_Outside; + } +#endif + + return TRUE; + + case WM_RBUTTONDOWN: + g_bMousePressedR = true; + + sEvent.cbSize = sizeof(TRACKMOUSEEVENT); + sEvent.dwFlags = TME_LEAVE; + sEvent.hwndTrack = g_hDlg; + sEvent.dwHoverTime = HOVER_DEFAULT; + TrackMouseEvent(&sEvent); + + if (g_bMousePressed) + { + g_bMousePressedR = false; + g_bMousePressed = false; + g_bMousePressedBoth = true; + } + + return TRUE; + + case WM_MBUTTONDOWN: + + g_bMousePressedM = true; + + sEvent.cbSize = sizeof(TRACKMOUSEEVENT); + sEvent.dwFlags = TME_LEAVE; + sEvent.hwndTrack = g_hDlg; + sEvent.dwHoverTime = HOVER_DEFAULT; + TrackMouseEvent(&sEvent); + return TRUE; + + case WM_LBUTTONUP: + g_bMousePressed = false; + g_bMousePressedBoth = false; + return TRUE; + + case WM_RBUTTONUP: + g_bMousePressedR = false; + g_bMousePressedBoth = false; + return TRUE; + + case WM_MBUTTONUP: + g_bMousePressedM = false; + return TRUE; + + case WM_COMMAND: + + if (ID_VIEWER_QUIT == LOWORD(wParam)) + { + PostQuitMessage(0); + DestroyWindow(hwndDlg); + } + else if (ID_VIEWER_RESETVIEW == LOWORD(wParam)) + { + g_sCamera.vPos = aiVector3D(0.0f,0.0f,-10.0f); + g_sCamera.vLookAt = aiVector3D(0.0f,0.0f,1.0f); + g_sCamera.vUp = aiVector3D(0.0f,1.0f,0.0f); + g_sCamera.vRight = aiVector3D(0.0f,1.0f,0.0f); + g_mWorldRotate = aiMatrix4x4(); + g_mWorld = aiMatrix4x4(); + + // don't forget to reset the st + CBackgroundPainter::Instance().ResetSB(); + } + else if (ID__HELP == LOWORD(wParam)) + { + DialogBox(g_hInstance,MAKEINTRESOURCE(IDD_AVHELP), + hwndDlg,&HelpDialogProc); + } + else if (ID__ABOUT == LOWORD(wParam)) + { + DialogBox(g_hInstance,MAKEINTRESOURCE(IDD_ABOUTBOX), + hwndDlg,&AboutMessageProc); + } + else if (ID_VIEWER_H == LOWORD(wParam)) + { + MakeFileAssociations(); + } + else if (ID_BACKGROUND_CLEAR == LOWORD(wParam)) + { + D3DCOLOR clrColor = D3DCOLOR_ARGB(0xFF,100,100,100); + CBackgroundPainter::Instance().SetColor(clrColor); + + HKEY hTemp; + RegCreateKeyEx(HKEY_CURRENT_USER, + "Software\\ASSIMP\\Viewer",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + RegSetValueExA(hTemp,"LastSkyBoxSrc",0,REG_SZ,(const BYTE*)"",MAX_PATH); + RegSetValueExA(hTemp,"LastTextureSrc",0,REG_SZ,(const BYTE*)"",MAX_PATH); + + RegSetValueExA(hTemp,"Color",0,REG_DWORD,(const BYTE*)&clrColor,4); + RegCloseKey(hTemp); + } + else if (ID_BACKGROUND_SETCOLOR == LOWORD(wParam)) + { + HKEY hTemp; + RegCreateKeyEx(HKEY_CURRENT_USER, + "Software\\ASSIMP\\Viewer",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + RegSetValueExA(hTemp,"LastSkyBoxSrc",0,REG_SZ,(const BYTE*)"",MAX_PATH); + RegSetValueExA(hTemp,"LastTextureSrc",0,REG_SZ,(const BYTE*)"",MAX_PATH); + + CHOOSECOLOR clr; + clr.lStructSize = sizeof(CHOOSECOLOR); + clr.hwndOwner = hwndDlg; + clr.Flags = CC_RGBINIT | CC_FULLOPEN; + clr.rgbResult = RGB(100,100,100); + clr.lpCustColors = g_aclCustomColors; + clr.lpfnHook = NULL; + clr.lpTemplateName = NULL; + clr.lCustData = NULL; + + ChooseColor(&clr); + + D3DCOLOR clrColor = D3DCOLOR_ARGB(0xFF, + GetRValue(clr.rgbResult), + GetGValue(clr.rgbResult), + GetBValue(clr.rgbResult)); + CBackgroundPainter::Instance().SetColor(clrColor); + + RegSetValueExA(hTemp,"Color",0,REG_DWORD,(const BYTE*)&clrColor,4); + RegCloseKey(hTemp); + } + else if (ID_BACKGROUND_LOADTEXTURE == LOWORD(wParam)) + { + char szFileName[MAX_PATH]; + + DWORD dwTemp = MAX_PATH; + HKEY hTemp; + RegCreateKeyEx(HKEY_CURRENT_USER, + "Software\\ASSIMP\\Viewer",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + if(ERROR_SUCCESS != RegQueryValueEx(hTemp,"TextureSrc",NULL,NULL, + (BYTE*)szFileName,&dwTemp)) + { + // Key was not found. Use C: + strcpy(szFileName,""); + } + else + { + // need to remove the file name + char* sz = strrchr(szFileName,'\\'); + if (!sz)sz = strrchr(szFileName,'/'); + if (!sz)*sz = 0; + } + OPENFILENAME sFilename1 = { + sizeof(OPENFILENAME), + g_hDlg,GetModuleHandle(NULL), + "Textures\0*.png;*.dds;*.tga;*.bmp;*.tif;*.ppm;*.ppx;*.jpg;*.jpeg;*.exr\0*.*\0", NULL, 0, 1, + szFileName, MAX_PATH, NULL, 0, NULL, + "Open texture as background", + OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY | OFN_NOCHANGEDIR, + 0, 1, ".jpg", 0, NULL, NULL + }; + if(GetOpenFileName(&sFilename1) == 0) return TRUE; + + // Now store the file in the registry + RegSetValueExA(hTemp,"TextureSrc",0,REG_SZ,(const BYTE*)szFileName,MAX_PATH); + RegSetValueExA(hTemp,"LastTextureSrc",0,REG_SZ,(const BYTE*)szFileName,MAX_PATH); + RegSetValueExA(hTemp,"LastSkyBoxSrc",0,REG_SZ,(const BYTE*)"",MAX_PATH); + RegCloseKey(hTemp); + + CBackgroundPainter::Instance().SetTextureBG(szFileName); + } + else if (ID_BACKGROUND_LOADSKYBOX == LOWORD(wParam)) + { + char szFileName[MAX_PATH]; + + DWORD dwTemp = MAX_PATH; + HKEY hTemp; + RegCreateKeyEx(HKEY_CURRENT_USER, + "Software\\ASSIMP\\Viewer",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + if(ERROR_SUCCESS != RegQueryValueEx(hTemp,"SkyBoxSrc",NULL,NULL, + (BYTE*)szFileName,&dwTemp)) + { + // Key was not found. Use C: + strcpy(szFileName,""); + } + else + { + // need to remove the file name + char* sz = strrchr(szFileName,'\\'); + if (!sz)sz = strrchr(szFileName,'/'); + if (!sz)*sz = 0; + } + OPENFILENAME sFilename1 = { + sizeof(OPENFILENAME), + g_hDlg,GetModuleHandle(NULL), + "Skyboxes\0*.dds\0*.*\0", NULL, 0, 1, + szFileName, MAX_PATH, NULL, 0, NULL, + "Open skybox as background", + OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY | OFN_NOCHANGEDIR, + 0, 1, ".dds", 0, NULL, NULL + }; + if(GetOpenFileName(&sFilename1) == 0) return TRUE; + + // Now store the file in the registry + RegSetValueExA(hTemp,"SkyBoxSrc",0,REG_SZ,(const BYTE*)szFileName,MAX_PATH); + RegSetValueExA(hTemp,"LastSkyBoxSrc",0,REG_SZ,(const BYTE*)szFileName,MAX_PATH); + RegSetValueExA(hTemp,"LastTextureSrc",0,REG_SZ,(const BYTE*)"",MAX_PATH); + RegCloseKey(hTemp); + + CBackgroundPainter::Instance().SetCubeMapBG(szFileName); + } + else if (ID_VIEWER_SAVESCREENSHOTTOFILE == LOWORD(wParam)) + { + char szFileName[MAX_PATH]; + + DWORD dwTemp = MAX_PATH; + HKEY hTemp; + RegCreateKeyEx(HKEY_CURRENT_USER, + "Software\\ASSIMP\\Viewer",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + if(ERROR_SUCCESS != RegQueryValueEx(hTemp,"ScreenShot",NULL,NULL, + (BYTE*)szFileName,&dwTemp)) + { + // Key was not found. Use C: + strcpy(szFileName,""); + } + else + { + // need to remove the file name + char* sz = strrchr(szFileName,'\\'); + if (!sz)sz = strrchr(szFileName,'/'); + if (!sz)*sz = 0; + } + OPENFILENAME sFilename1 = { + sizeof(OPENFILENAME), + g_hDlg,GetModuleHandle(NULL), + "PNG Images\0*.png", NULL, 0, 1, + szFileName, MAX_PATH, NULL, 0, NULL, + "Save Screenshot to file", + OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY | OFN_NOCHANGEDIR, + 0, 1, ".png", 0, NULL, NULL + }; + if(GetSaveFileName(&sFilename1) == 0) return TRUE; + + // Now store the file in the registry + RegSetValueExA(hTemp,"ScreenShot",0,REG_SZ,(const BYTE*)szFileName,MAX_PATH); + RegCloseKey(hTemp); + + IDirect3DSurface9* pi; + g_piDevice->GetRenderTarget(0,&pi); + D3DXSaveSurfaceToFile(szFileName,D3DXIFF_PNG, + pi,NULL,NULL); + pi->Release(); + } + else if (ID_VIEWER_OPEN == LOWORD(wParam)) + { + char szFileName[MAX_PATH]; + + DWORD dwTemp = MAX_PATH; + HKEY hTemp; + RegCreateKeyEx(HKEY_CURRENT_USER, + "Software\\ASSIMP\\Viewer",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + if(ERROR_SUCCESS != RegQueryValueEx(hTemp,"CurrentApp",NULL,NULL, + (BYTE*)szFileName,&dwTemp)) + { + // Key was not found. Use C: + strcpy(szFileName,""); + } + else + { + // need to remove the file name + char* sz = strrchr(szFileName,'\\'); + if (!sz)sz = strrchr(szFileName,'/'); + if (!sz)*sz = 0; + } + OPENFILENAME sFilename1 = { + sizeof(OPENFILENAME), + g_hDlg,GetModuleHandle(NULL), + "ASSIMP assets\0*.x;*.obj;*.ms3d;*.3ds;*.md3;*.md1;*.md2;*.md4;*.md5;*.ply\0All files\0*.*", NULL, 0, 1, + szFileName, MAX_PATH, NULL, 0, NULL, + "Import Asset into ASSIMP", + OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY | OFN_NOCHANGEDIR, + 0, 1, ".x", 0, NULL, NULL + }; + if(GetOpenFileName(&sFilename1) == 0) return TRUE; + + // Now store the file in the registry + RegSetValueExA(hTemp,"CurrentApp",0,REG_SZ,(const BYTE*)szFileName,MAX_PATH); + RegCloseKey(hTemp); + + if (0 != strcmp(g_szFileName,szFileName)) + { + strcpy(g_szFileName, szFileName); + DeleteAssetData(); + DeleteAsset(); + LoadAsset(); + } + + } + else if (ID_VIEWER_CLOSEASSET == LOWORD(wParam)) + { + DeleteAssetData(); + DeleteAsset(); + } + else if (BN_CLICKED == HIWORD(wParam)) + { + if (IDC_TOGGLEMS == LOWORD(wParam)) + { + g_sOptions.bMultiSample = !g_sOptions.bMultiSample; + DeleteAssetData(); + ShutdownDevice(); + if (0 == CreateDevice()) + { + CLogDisplay::Instance().AddEntry( + "[ERROR] Failed to toggle MultiSampling mode"); + g_sOptions.bMultiSample = !g_sOptions.bMultiSample; + CreateDevice(); + } + CreateAssetData(); + + if (g_sOptions.bMultiSample) + { + CLogDisplay::Instance().AddEntry( + "[OK] Changed MultiSampling mode to the maximum value for this device"); + } + else + { + CLogDisplay::Instance().AddEntry( + "[OK] MultiSampling has been disabled"); + } + } + else if (IDC_TOGGLEMAT == LOWORD(wParam)) + { + g_sOptions.bRenderMats = !g_sOptions.bRenderMats; + } + else if (IDC_NOSPECULAR == LOWORD(wParam)) + { + g_sOptions.bNoSpecular = !g_sOptions.bNoSpecular; + UpdateSpecularMaterials(); + } + else if (IDC_ZOOM == LOWORD(wParam)) + { + g_bFPSView = !g_bFPSView; + + SetupFPSView(); + } + else if (IDC_TOGGLENORMALS == LOWORD(wParam)) + { + g_sOptions.bRenderNormals = !g_sOptions.bRenderNormals; + } + else if (IDC_LOWQUALITY == LOWORD(wParam)) + { + g_sOptions.bLowQuality = !g_sOptions.bLowQuality; + } + else if (IDC_3LIGHTS == LOWORD(wParam)) + { + g_sOptions.b3Lights = !g_sOptions.b3Lights; + } + else if (IDC_LIGHTROTATE == LOWORD(wParam)) + { + g_sOptions.bLightRotate = !g_sOptions.bLightRotate; + } + else if (IDC_AUTOROTATE == LOWORD(wParam)) + { + g_sOptions.bRotate = !g_sOptions.bRotate; + } + else if (IDC_TOGGLEWIRE == LOWORD(wParam)) + { + if (g_sOptions.eDrawMode == RenderOptions::WIREFRAME) + g_sOptions.eDrawMode = RenderOptions::NORMAL; + else g_sOptions.eDrawMode = RenderOptions::WIREFRAME; + } + } + + return TRUE; + }; + return FALSE; + } + + +//------------------------------------------------------------------------------- +// Message prcoedure for the progress dialog +//------------------------------------------------------------------------------- +INT_PTR CALLBACK ProgressMessageProc(HWND hwndDlg,UINT uMsg, + WPARAM wParam,LPARAM lParam) + { + UNREFERENCED_PARAMETER(lParam); + switch (uMsg) + { + case WM_INITDIALOG: + + SendDlgItemMessage(hwndDlg,IDC_PROGRESS,PBM_SETRANGE,0, + MAKELPARAM(0,500)); + + SetTimer(hwndDlg,0,40,NULL); + return TRUE; + + case WM_CLOSE: + EndDialog(hwndDlg,0); + return TRUE; + + case WM_COMMAND: + + if (IDOK == LOWORD(wParam)) + { +#if 0 + g_bLoadingCanceled = true; + TerminateThread(g_hThreadHandle,5); + g_pcAsset = NULL; + + EndDialog(hwndDlg,0); +#endif + + // PROBLEM: If we terminate the loader thread, ASSIMP's state + // is undefined. Any further attempts to load assets will + // fail. + exit(5); +// return TRUE; + } + case WM_TIMER: + + UINT iPos = (UINT)SendDlgItemMessage(hwndDlg,IDC_PROGRESS,PBM_GETPOS,0,0); + iPos += 10; + if (iPos > 490)iPos = 0; + SendDlgItemMessage(hwndDlg,IDC_PROGRESS,PBM_SETPOS,iPos,0); + + if (g_bLoadingFinished) + { + EndDialog(hwndDlg,0); + return TRUE; + } + + return TRUE; + } + return FALSE; + } + + +//------------------------------------------------------------------------------- +// Message procedure for the about dialog +//------------------------------------------------------------------------------- +INT_PTR CALLBACK AboutMessageProc(HWND hwndDlg,UINT uMsg, + WPARAM wParam,LPARAM lParam) + { + UNREFERENCED_PARAMETER(lParam); + switch (uMsg) + { + case WM_CLOSE: + EndDialog(hwndDlg,0); + return TRUE; + + case WM_COMMAND: + + if (IDOK == LOWORD(wParam)) + { + EndDialog(hwndDlg,0); + return TRUE; + } + } + return FALSE; + } +}; + +using namespace AssimpView; + +//------------------------------------------------------------------------------- +// Entry point to the application +//------------------------------------------------------------------------------- +int APIENTRY _tWinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPTSTR lpCmdLine, + int nCmdShow) + { + UNREFERENCED_PARAMETER(hPrevInstance); + UNREFERENCED_PARAMETER(lpCmdLine); + + // needed for the RichEdit control in the about/help dialog + LoadLibrary( "riched20.dll" ); + + InitCommonControls(); + + g_hInstance = hInstance; + if (0 == InitD3D()) + { + MessageBox(NULL,"Failed to initialize Direct3D 9", + "ASSIMP ModelViewer",MB_OK); + return -6; + } + + HWND hDlg = CreateDialog(hInstance,MAKEINTRESOURCE(IDD_DIALOGMAIN), + NULL,&MessageProc); + + if (NULL == hDlg) + { + MessageBox(NULL,"Failed to create dialog from resource", + "ASSIMP ModelViewer",MB_OK); + return -5; + } + + g_hDlg = hDlg; + + MSG uMsg; + memset(&uMsg,0,sizeof( MSG)); + + ShowWindow( hDlg, nCmdShow ); + UpdateWindow( hDlg ); + + if (0 == CreateDevice(true,false,true)) + { + MessageBox(NULL,"Failed to initialize Direct3D 9 (2)", + "ASSIMP ModelViewer",MB_OK); + return -4; + } + + CLogDisplay::Instance().AddEntry("[OK] The viewer has been initialized successfully"); + + // recover background skyboxes/textures from the last session + HKEY hTemp; + union + { + char szFileName[MAX_PATH]; + D3DCOLOR clrColor; + }; + DWORD dwTemp = MAX_PATH; + RegCreateKeyEx(HKEY_CURRENT_USER, + "Software\\ASSIMP\\Viewer",NULL,NULL,0,KEY_ALL_ACCESS, NULL, &hTemp,NULL); + if(ERROR_SUCCESS == RegQueryValueEx(hTemp,"LastSkyBoxSrc",NULL,NULL, + (BYTE*)szFileName,&dwTemp) && '\0' != szFileName[0]) + { + CBackgroundPainter::Instance().SetCubeMapBG(szFileName); + } + else if(ERROR_SUCCESS == RegQueryValueEx(hTemp,"LastTextureSrc",NULL,NULL, + (BYTE*)szFileName,&dwTemp) && '\0' != szFileName[0]) + { + CBackgroundPainter::Instance().SetTextureBG(szFileName); + } + else if(ERROR_SUCCESS == RegQueryValueEx(hTemp,"Color",NULL,NULL, + (BYTE*)&clrColor,&dwTemp)) + { + CBackgroundPainter::Instance().SetColor(clrColor); + } + RegCloseKey(hTemp); + + // now handle command line arguments + HandleCommandLine(lpCmdLine); + + + double adLast[30]; + for (int i = 0; i < 30;++i)adLast[i] = 0.0f; + int iCurrent = 0; + + double g_dCurTime = 0; + double g_dLastTime = 0; + while( uMsg.message != WM_QUIT ) + { + if( PeekMessage( &uMsg, NULL, 0, 0, PM_REMOVE ) ) + { + TranslateMessage( &uMsg ); + DispatchMessage( &uMsg ); + + if (WM_CHAR == uMsg.message) + { + + switch ((char)uMsg.wParam) + { + case 'M': + case 'm': + + CheckDlgButton(g_hDlg,IDC_TOGGLEMS, + IsDlgButtonChecked(g_hDlg,IDC_TOGGLEMS) == BST_CHECKED + ? BST_UNCHECKED : BST_CHECKED); + + g_sOptions.bMultiSample = !g_sOptions.bMultiSample; + DeleteAssetData(); + ShutdownDevice(); + if (0 == CreateDevice()) + { + CLogDisplay::Instance().AddEntry( + "[ERROR] Failed to toggle MultiSampling mode"); + g_sOptions.bMultiSample = !g_sOptions.bMultiSample; + CreateDevice(); + } + CreateAssetData(); + + if (g_sOptions.bMultiSample) + { + CLogDisplay::Instance().AddEntry( + "[OK] Changed MultiSampling mode to the maximum value for this device"); + } + else + { + CLogDisplay::Instance().AddEntry( + "[OK] MultiSampling has been disabled"); + } + break; + + case 'L': + case 'l': + + CheckDlgButton(g_hDlg,IDC_3LIGHTS, + IsDlgButtonChecked(g_hDlg,IDC_3LIGHTS) == BST_CHECKED + ? BST_UNCHECKED : BST_CHECKED); + + g_sOptions.b3Lights = !g_sOptions.b3Lights; + + break; + + case 'P': + case 'p': + + CheckDlgButton(g_hDlg,IDC_LOWQUALITY, + IsDlgButtonChecked(g_hDlg,IDC_LOWQUALITY) == BST_CHECKED + ? BST_UNCHECKED : BST_CHECKED); + + g_sOptions.bLowQuality = !g_sOptions.bLowQuality; + + break; + + case 'D': + case 'd': + + CheckDlgButton(g_hDlg,IDC_TOGGLEMAT, + IsDlgButtonChecked(g_hDlg,IDC_TOGGLEMAT) == BST_CHECKED + ? BST_UNCHECKED : BST_CHECKED); + + g_sOptions.bRenderMats = !g_sOptions.bRenderMats; + + break; + + + case 'N': + case 'n': + + CheckDlgButton(g_hDlg,IDC_TOGGLENORMALS, + IsDlgButtonChecked(g_hDlg,IDC_TOGGLENORMALS) == BST_CHECKED + ? BST_UNCHECKED : BST_CHECKED); + + g_sOptions.bRenderNormals = !g_sOptions.bRenderNormals; + + break; + + + case 'S': + case 's': + + CheckDlgButton(g_hDlg,IDC_NOSPECULAR, + IsDlgButtonChecked(g_hDlg,IDC_NOSPECULAR) == BST_CHECKED + ? BST_UNCHECKED : BST_CHECKED); + + g_sOptions.bNoSpecular = !g_sOptions.bNoSpecular; + UpdateSpecularMaterials(); + + break; + + case 'A': + case 'a': + + CheckDlgButton(g_hDlg,IDC_AUTOROTATE, + IsDlgButtonChecked(g_hDlg,IDC_AUTOROTATE) == BST_CHECKED + ? BST_UNCHECKED : BST_CHECKED); + + g_sOptions.bRotate = !g_sOptions.bRotate; + + break; + + + case 'R': + case 'r': + + CheckDlgButton(g_hDlg,IDC_LIGHTROTATE, + IsDlgButtonChecked(g_hDlg,IDC_LIGHTROTATE) == BST_CHECKED + ? BST_UNCHECKED : BST_CHECKED); + + g_sOptions.bLightRotate = !g_sOptions.bLightRotate; + + break; + + case 'Z': + case 'z': + + CheckDlgButton(g_hDlg,IDC_ZOOM, + IsDlgButtonChecked(g_hDlg,IDC_ZOOM) == BST_CHECKED + ? BST_UNCHECKED : BST_CHECKED); + + g_bFPSView = !g_bFPSView; + SetupFPSView(); + break; + + + case 'W': + case 'w': + + CheckDlgButton(g_hDlg,IDC_TOGGLEWIRE, + IsDlgButtonChecked(g_hDlg,IDC_TOGGLEWIRE) == BST_CHECKED + ? BST_UNCHECKED : BST_CHECKED); + + if (g_sOptions.eDrawMode == RenderOptions::NORMAL) + g_sOptions.eDrawMode = RenderOptions::WIREFRAME; + else g_sOptions.eDrawMode = RenderOptions::NORMAL; + + break; + } + } + } + + // render the scene + Render(); + + g_dCurTime = timeGetTime(); + g_fElpasedTime = (float)((g_dCurTime - g_dLastTime) * 0.001); + g_dLastTime = g_dCurTime; + + adLast[iCurrent++] = 1.0f / g_fElpasedTime; + + double dFPS = 0.0; + for (int i = 0;i < 30;++i) + dFPS += adLast[i]; + dFPS /= 30.0; + + if (30 == iCurrent) + { + iCurrent = 0; + if (dFPS != g_fFPS) + { + g_fFPS = dFPS; + char szOut[256]; + + sprintf(szOut,"%i",(int)floorf((float)dFPS+0.5f)); + SetDlgItemText(g_hDlg,IDC_EFPS,szOut); + } + } + } + DeleteAsset(); + ShutdownDevice(); + ShutdownD3D(); + return 0; + } \ No newline at end of file diff --git a/tools/assimp_view/NOTE@help.rtf.txt b/tools/assimp_view/NOTE@help.rtf.txt new file mode 100644 index 000000000..26eb96510 --- /dev/null +++ b/tools/assimp_view/NOTE@help.rtf.txt @@ -0,0 +1,2 @@ +text1.bin is the corresponding bin file to be included with the executable file. +When updating the rich formatted text inside Visual Studio, a terminating 0 character must be appended \ No newline at end of file diff --git a/tools/assimp_view/RenderOptions.h b/tools/assimp_view/RenderOptions.h new file mode 100644 index 000000000..37a66a8d0 --- /dev/null +++ b/tools/assimp_view/RenderOptions.h @@ -0,0 +1,69 @@ +//------------------------------------------------------------------------------- +/** +* This program is distributed under the terms of the GNU Lesser General +* Public License (LGPL). +* +* ASSIMP Viewer Utility +* +*/ +//------------------------------------------------------------------------------- + +#if (!defined AV_RO_H_INCLUDED) +#define AV_RO_H_INCLUDED + + +//------------------------------------------------------------------------------- +/** \brief Class to manage render options. One global instance +*/ +//------------------------------------------------------------------------------- +class RenderOptions + { + public: + + // enumerates different drawing modi. POINT is currently + // not supported and probably will never be. + enum DrawMode {NORMAL, WIREFRAME, POINT}; + + inline RenderOptions (void) : + bMultiSample (true), + bSuperSample (false), + bRenderMats (true), + bRenderNormals (false), + eDrawMode (NORMAL), + b3Lights (false), + bLightRotate (false), + bRotate (true), + bLowQuality (false), + bNoSpecular (false) {} + + bool bMultiSample; + + // SuperSampling has not yet been implemented + bool bSuperSample; + + // Display the real material of the object + bool bRenderMats; + + // Render the normals + bool bRenderNormals; + + // Use 2 directional light sources + bool b3Lights; + + // Automatically rotate the light source(s) + bool bLightRotate; + + // Automatically rotate the asset around its origin + bool bRotate; + + // use standard lambertian lighting + bool bLowQuality; + + // disable specular lighting got all elements in the scene + bool bNoSpecular; + + // wireframe or solid rendering? + DrawMode eDrawMode; + }; + +#endif // !! IG \ No newline at end of file diff --git a/tools/assimp_view/Shaders.cpp b/tools/assimp_view/Shaders.cpp new file mode 100644 index 000000000..1c02b9805 --- /dev/null +++ b/tools/assimp_view/Shaders.cpp @@ -0,0 +1,1213 @@ +//------------------------------------------------------------------------------- +/** +* This program is distributed under the terms of the GNU Lesser General +* Public License (LGPL). +* +* ASSIMP Viewer Utility +* +*/ +//------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "assimp_view.h" + + +namespace AssimpView { + +std::string g_szNormalsShader = std::string( + //-------------------------------------------------------------------------------\n" + /**\n" + * This program is distributed under the terms of the GNU Lesser General\n + * Public License (LGPL). \n + *\n + * ASSIMP Viewer Utility\n + *\n" + */ + //-------------------------------------------------------------------------------\n" + + + // World * View * Projection matrix\n" + // NOTE: Assume that the material uses a WorldViewProjection matrix\n" + "float4x4 WorldViewProjection : WORLDVIEWPROJECTION;\n" + "float4 OUTPUT_COLOR;\n" + + + // ----------------------------------------------------------------------------\n" + // Vertex shader input structure\n" + // ----------------------------------------------------------------------------\n" + "struct VS_INPUT\n" + "{\n" + "// Position\n" + "float3 Position : POSITION;\n" + "};\n" + + + // ----------------------------------------------------------------------------\n" + // Vertex shader output structure\n" + // ----------------------------------------------------------------------------\n" + "struct VS_OUTPUT\n" + "{\n" + "// Position\n" + "float4 Position : POSITION;\n" + "};\n" + + // ----------------------------------------------------------------------------\n" + // Vertex shader\n" + // ----------------------------------------------------------------------------\n" + "VS_OUTPUT RenderNormalsVS(VS_INPUT IN)\n" + "{\n" + "// Initialize the output structure with zero\n" + "VS_OUTPUT Out = (VS_OUTPUT)0;\n" + + "// Multiply with the WorldViewProjection matrix\n" + "Out.Position = mul(float4(IN.Position,1.0f),WorldViewProjection);\n" + + "return Out;\n" + "}\n" + + + // ----------------------------------------------------------------------------\n" + // Pixel shader\n" + // ----------------------------------------------------------------------------\n" + "float4 RenderNormalsPS() : COLOR\n" + "{\n" + "return OUTPUT_COLOR;\n" + "}\n" + + + // ----------------------------------------------------------------------------\n" + // Technique for the normal rendering effect (ps_2_0)\n" + // ----------------------------------------------------------------------------\n" + "technique RenderNormals\n" + "{\n" + "pass p0\n" + "{\n" + "CullMode=none;\n" + "PixelShader = compile ps_2_0 RenderNormalsPS();\n" + "VertexShader = compile vs_2_0 RenderNormalsVS();\n" + "}\n" + "};\n" + ); + +std::string g_szSkyboxShader = std::string( + //-------------------------------------------------------------------------------\n" + /**\n" + * This program is distributed under the terms of the GNU Lesser General\n + * Public License (LGPL). \n + *\n + * ASSIMP Viewer Utility\n + *\n" + */ + //-------------------------------------------------------------------------------\n" + + + // ----------------------------------------------------------------------------\n" + // Sampler and texture for the skybox\n" + // ----------------------------------------------------------------------------\n" + "textureCUBE lw_tex_envmap;\n" + "samplerCUBE EnvironmentMapSampler = sampler_state\n" + "{\n" + "Texture = (lw_tex_envmap);\n" + "AddressU = CLAMP;\n" + "AddressV = CLAMP;\n" + "AddressW = CLAMP;\n" + + "MAGFILTER = linear;\n" + "MINFILTER = linear;\n" + "};\n" + + // World * View * Projection matrix\n" + // NOTE: Assume that the material uses a WorldViewProjection matrix\n" + "float4x4 WorldViewProjection : WORLDVIEWPROJECTION;\n" + + + // ----------------------------------------------------------------------------\n" + // Vertex shader input structure\n" + // ----------------------------------------------------------------------------\n" + "struct VS_INPUT\n" + "{\n" + // Position\n" + "float3 Position : POSITION;\n" + + // 3D-Texture coordinate\n" + "float3 Texture0 : TEXCOORD0;\n" + "};\n" + + + // ----------------------------------------------------------------------------\n" + // Vertex shader output structure\n" + // ----------------------------------------------------------------------------\n" + "struct VS_OUTPUT\n" + "{\n" + // Position\n" + "float4 Position : POSITION;\n" + + // 3D-Texture coordinate\n" + "float3 Texture0 : TEXCOORD0;\n" + "};\n" + + // ----------------------------------------------------------------------------\n" + // Vertex shader\n" + // ----------------------------------------------------------------------------\n" + "VS_OUTPUT RenderSkyBoxVS(VS_INPUT IN)\n" + "{\n" + // Initialize the output structure with zero\n" + "VS_OUTPUT Out = (VS_OUTPUT)0;\n" + + // Multiply with the WorldViewProjection matrix\n" + "Out.Position = mul(float4(IN.Position,1.0f),WorldViewProjection);\n" + + // Set z to w to ensure z becomes 1.0 after the division through\n" + // w occurs\n" + "Out.Position.z = Out.Position.w;\n" + + // Simply pass through texture coordinates\n" + "Out.Texture0 = IN.Texture0;\n" + + "return Out;\n" + "}\n" + + + // ----------------------------------------------------------------------------\n" + // Pixel shader\n" + // ----------------------------------------------------------------------------\n" + "float4 RenderSkyBoxPS(float3 Texture0 : TEXCOORD0) : COLOR\n" + "{\n" + // Lookup the skybox texture\n" + "return texCUBE(EnvironmentMapSampler,Texture0) ;\n" + "}\n" + + + // ----------------------------------------------------------------------------\n" + // Technique for the skybox shader (ps_2_0)\n" + // ----------------------------------------------------------------------------\n" + "technique RenderSkyBox\n" + "{\n" + "pass p0\n" + "{\n" + "ZWriteEnable = FALSE;\n" + "FogEnable = FALSE;\n" + "CullMode = NONE;\n" + + "PixelShader = compile ps_2_0 RenderSkyBoxPS();\n" + "VertexShader = compile vs_2_0 RenderSkyBoxVS();\n" + "}\n" + "};\n" + + "texture TEXTURE_2D;\n" + "sampler TEXTURE_SAMPLER = sampler_state\n" + "{\n" + "Texture = (TEXTURE_2D);\n" + "};\n" + + + // ----------------------------------------------------------------------------\n" + "struct VS_OUTPUT2\n" + "{\n" + "// Position\n" + "float4 _Position : POSITION;\n" + + "// Texture coordinate\n" + "float2 _TexCoord0 : TEXCOORD0;\n" + "};\n" + + + // ----------------------------------------------------------------------------\n" + "VS_OUTPUT2 RenderImageVS(float4 INPosition : POSITION,\n" + "float2 INTexCoord0 : TEXCOORD0 )\n" + "{\n" + // Initialize the output structure with zero\n" + "VS_OUTPUT2 Out = (VS_OUTPUT2)0;\n" + + "Out._Position.xy = INPosition.xy;\n" + "Out._Position.z = Out._Position.w = 1.0f;\n" + + "Out._TexCoord0 = INTexCoord0;\n" + + "return Out;\n" + "}\n" + + // ----------------------------------------------------------------------------\n" + "float4 RenderImagePS(float2 IN : TEXCOORD0) : COLOR\n" + "{\n" + "return tex2D(TEXTURE_SAMPLER,IN);\n" + "}\n" + + + // ----------------------------------------------------------------------------\n" + // Technique for the background image shader (ps_2_0)\n" + // ----------------------------------------------------------------------------\n" + "technique RenderImage2D\n" + "{\n" + "pass p0\n" + "{\n" + "ZWriteEnable = FALSE;\n" + "FogEnable = FALSE;\n" + "CullMode = NONE;\n" + + "PixelShader = compile ps_2_0 RenderImagePS();\n" + "VertexShader = compile vs_2_0 RenderImageVS();\n" + "}\n" + "};\n" + ); + +std::string g_szDefaultShader = std::string( + //-------------------------------------------------------------------------------\n" + /**\n" + * This program is distributed under the terms of the GNU Lesser General\n + * Public License (LGPL). \n + *\n + * ASSIMP Viewer Utility\n + *\n" + */ + //-------------------------------------------------------------------------------\n" + + + // World * View * Projection matrix\n" + // NOTE: Assume that the material uses a WorldViewProjection matrix\n" + "float4x4 WorldViewProjection : WORLDVIEWPROJECTION;\n" + "float4x4 World : WORLD;\n" + "float4x3 WorldInverseTranspose : WORLDINVERSETRANSPOSE;\n" + + + // light colors\n" + "float3 afLightColor[5];\n" + + // light direction \n" + "float3 afLightDir[5];\n" + + // position of the camera in worldspace\n" + "float3 vCameraPos : CAMERAPOSITION;\n" + + + // ----------------------------------------------------------------------------\n" + // Vertex shader input structure\n" + // ----------------------------------------------------------------------------\n" + "struct VS_INPUT\n" + "{\n" + "// Position\n" + "float3 Position : POSITION;\n" + "float3 Normal : NORMAL;\n" + "};\n" + + + // ----------------------------------------------------------------------------\n" + // Vertex shader output structure\n" + // ----------------------------------------------------------------------------\n" + "struct VS_OUTPUT\n" + "{\n" + // Position\n" + "float4 Position : POSITION;\n" + + "float3 ViewDir : TEXCOORD0;\n" + "float3 Normal : TEXCOORD1;\n" + "};\n" + + // ----------------------------------------------------------------------------\n" + // Vertex shader\n" + // ----------------------------------------------------------------------------\n" + "VS_OUTPUT DefaultVShader(VS_INPUT IN)\n" + "{\n" + // Initialize the output structure with zero\n" + "VS_OUTPUT Out = (VS_OUTPUT)0;\n" + + // Multiply with the WorldViewProjection matrix\n" + "Out.Position = mul(float4(IN.Position,1.0f),WorldViewProjection);\n" + "float3 WorldPos = mul(float4(IN.Position,1.0f),World);\n" + "Out.ViewDir = vCameraPos - WorldPos;\n" + "Out.Normal = mul(IN.Normal,WorldInverseTranspose);\n" + + "return Out;\n" + "}\n" + + + // ----------------------------------------------------------------------------\n" + // Pixel shader\n" + // ----------------------------------------------------------------------------\n" + "float4 DefaultPShaderSpecular_D1(VS_OUTPUT IN) : COLOR\n" + "{\n" + "float4 OUT = float4(0.0f,0.0f,0.0f,1.0f);\n" + + "float3 Normal = normalize(IN.Normal);\n" + "float3 ViewDir = normalize(IN.ViewDir);\n" + + "{\n" + "float L1 = dot(Normal,afLightDir[0]) * 0.5f + 0.5f;\n" + "float3 Reflect = reflect (Normal,afLightDir[0]);\n" + "float fHalfLambert = L1*L1;\n" + "OUT.rgb += afLightColor[0] * (fHalfLambert +\n" + "saturate(fHalfLambert * 4.0f) * pow(dot(Reflect,ViewDir),9));\n" + "}\n" + "return OUT;\n" + "}\n" + // ----------------------------------------------------------------------------\n" + "float4 DefaultPShaderSpecular_D2(VS_OUTPUT IN) : COLOR\n" + "{\n" + "float4 OUT = float4(0.0f,0.0f,0.0f,1.0f);\n" + + "float3 Normal = normalize(IN.Normal);\n" + "float3 ViewDir = normalize(IN.ViewDir);\n" + + "{\n" + "float L1 = dot(Normal,afLightDir[0]) * 0.5f + 0.5f;\n" + "float3 Reflect = reflect (ViewDir,Normal);\n" + "float fHalfLambert = L1*L1;\n" + "OUT.rgb += afLightColor[0] * (fHalfLambert +\n" + "saturate(fHalfLambert * 4.0f) * pow(dot(Reflect,afLightDir[0]),9));\n" + "}\n" + "{\n" + "float L1 = dot(Normal,afLightDir[1]) * 0.5f + 0.5f;\n" + "float3 Reflect = reflect (ViewDir,Normal);\n" + "float fHalfLambert = L1*L1;\n" + "OUT.rgb += afLightColor[1] * (fHalfLambert +\n" + "saturate(fHalfLambert * 4.0f) * pow(dot(Reflect,afLightDir[1]),9));\n" + "}\n" + "return OUT;\n" + "}\n" + // ----------------------------------------------------------------------------\n" + "float4 DefaultPShaderSpecular_PS20_D1(VS_OUTPUT IN) : COLOR\n" + "{\n" + "float4 OUT = float4(0.0f,0.0f,0.0f,1.0f);\n" + + "float3 Normal = normalize(IN.Normal);\n" + "float3 ViewDir = normalize(IN.ViewDir);\n" + + "{\n" + "float L1 = dot(Normal,afLightDir[0]);\n" + "float3 Reflect = reflect (Normal,afLightDir[0]);\n" + "OUT.rgb += afLightColor[0] * ((L1) +\n" + "pow(dot(Reflect,ViewDir),9));\n" + "}\n" + + "return OUT;\n" + "}\n" + // ----------------------------------------------------------------------------\n" + "float4 DefaultPShaderSpecular_PS20_D2(VS_OUTPUT IN) : COLOR\n" + "{\n" + "float4 OUT = float4(0.0f,0.0f,0.0f,1.0f);\n" + + "float3 Normal = normalize(IN.Normal);\n" + "float3 ViewDir = normalize(IN.ViewDir);\n" + + "{\n" + "float L1 = dot(Normal,afLightDir[0]);\n" + "float3 Reflect = reflect (Normal,afLightDir[0]);\n" + "OUT.rgb += afLightColor[0] * ((L1) +\n" + "pow(dot(Reflect,ViewDir),9));\n" + "}\n" + "{\n" + "float L1 = dot(Normal,afLightDir[1]);\n" + "float3 Reflect = reflect (Normal,afLightDir[1]);\n" + "OUT.rgb += afLightColor[1] * ((L1) +\n" + "pow(dot(Reflect,ViewDir),9));\n" + "}\n" + "return OUT;\n" + "}\n" + + + // ----------------------------------------------------------------------------\n" + // Technique for the default effect\n" + // ----------------------------------------------------------------------------\n" + "technique DefaultFXSpecular_D1\n" + "{\n" + "pass p0\n" + "{\n" + "CullMode=none;\n" + "PixelShader = compile ps_3_0 DefaultPShaderSpecular_D1();\n" + "VertexShader = compile vs_3_0 DefaultVShader();\n" + "}\n" + "};\n" + "technique DefaultFXSpecular_D2\n" + "{\n" + "pass p0\n" + "{\n" + "CullMode=none;\n" + "PixelShader = compile ps_3_0 DefaultPShaderSpecular_D2();\n" + "VertexShader = compile vs_3_0 DefaultVShader();\n" + "}\n" + "};\n" + + + // ----------------------------------------------------------------------------\n" + // Technique for the default effect (ps_2_0)\n" + // ----------------------------------------------------------------------------\n" + "technique DefaultFXSpecular_PS20_D1\n" + "{\n" + "pass p0\n" + "{\n" + "CullMode=none;\n" + "PixelShader = compile ps_2_0 DefaultPShaderSpecular_PS20_D1();\n" + "VertexShader = compile vs_2_0 DefaultVShader();\n" + "}\n" + "};\n" + "technique DefaultFXSpecular_PS20_D2\n" + "{\n" + "pass p0\n" + "{\n" + "CullMode=none;\n" + "PixelShader = compile ps_2_0 DefaultPShaderSpecular_PS20_D2();\n" + "VertexShader = compile vs_2_0 DefaultVShader();\n" + "}\n" + "};\n" + ); + + +std::string g_szMaterialShader = std::string( + //-------------------------------------------------------------------------------\n" + /**\n" + * This program is distributed under the terms of the GNU Lesser General\n + * Public License (LGPL). \n + *\n + * ASSIMP Viewer Utility\n + *\n" + */ + //-------------------------------------------------------------------------------\n" + + + // World * View * Projection matrix\n" + // NOTE: Assume that the material uses a WorldViewProjection matrix\n" + "float4x4 WorldViewProjection : WORLDVIEWPROJECTION;\n" + "float4x4 World : WORLD;\n" + "float4x3 WorldInverseTranspose : WORLDINVERSETRANSPOSE;\n" + + "#ifndef AV_DISABLESSS\n" + "float4x3 ViewProj;\n" + "float4x3 InvViewProj;\n" + "#endif\n" + + // light colors (diffuse and specular)\n" + "float4 afLightColor[5];\n" + "float4 afLightColorAmbient[5];\n" + + // light direction \n" + "float3 afLightDir[5];\n" + + // position of the camera in worldspace\n" + "float3 vCameraPos : CAMERAPOSITION;\n" + + "#ifdef AV_DIFFUSE_TEXTURE\n" + "texture DIFFUSE_TEXTURE;\n" + "sampler DIFFUSE_SAMPLER\n" + "{\n" + "Texture = ;\n" + "MinFilter=LINEAR;\n" + "MagFilter=LINEAR;\n" + "MipFilter=LINEAR;\n" + "};\n" + "#endif // AV_DIFFUSE_TEXTUR\n" + + "#ifdef AV_SPECULAR_TEXTURE\n" + "texture SPECULAR_TEXTURE;\n" + "sampler SPECULAR_SAMPLER\n" + "{\n" + "Texture = ;\n" + "MinFilter=LINEAR;\n" + "MagFilter=LINEAR;\n" + "MipFilter=LINEAR;\n" + "};\n" + "#endif // AV_SPECULAR_TEXTUR\n" + + "#ifdef AV_AMBIENT_TEXTURE\n" + "texture AMBIENT_TEXTURE;\n" + "sampler AMBIENT_SAMPLER\n" + "{\n" + "Texture = ;\n" + "MinFilter=LINEAR;\n" + "MagFilter=LINEAR;\n" + "MipFilter=LINEAR;\n" + "};\n" + "#endif // AV_AMBIENT_TEXTUR\n" + + "#ifdef AV_OPACITY_TEXTURE\n" + "texture OPACITY_TEXTURE;\n" + "sampler OPACITY_SAMPLER\n" + "{\n" + "Texture = ;\n" + "MinFilter=LINEAR;\n" + "MagFilter=LINEAR;\n" + "MipFilter=LINEAR;\n" + "};\n" + "#endif // AV_OPACITY_TEXTURE\n" + + "#ifdef AV_EMISSIVE_TEXTURE\n" + "texture EMISSIVE_TEXTURE;\n" + "sampler EMISSIVE_SAMPLER\n" + "{\n" + "Texture = ;\n" + "MinFilter=LINEAR;\n" + "MagFilter=LINEAR;\n" + "MipFilter=LINEAR;\n" + "};\n" + "#endif // AV_EMISSIVE_TEXTUR\n" + + "#ifdef AV_NORMAL_TEXTURE\n" + "texture NORMAL_TEXTURE;\n" + "sampler NORMAL_SAMPLER\n" + "{\n" + "Texture = ;\n" + "MinFilter=LINEAR;\n" + "MagFilter=LINEAR;\n" + "MipFilter=LINEAR;\n" + "};\n" + "#endif // AV_NORMAL_TEXTURE\n" + + "#ifdef AV_SKYBOX_LOOKUP\n" + "textureCUBE lw_tex_envmap;\n" + "samplerCUBE EnvironmentMapSampler = sampler_state\n" + "{\n" + "Texture = (lw_tex_envmap);\n" + "AddressU = CLAMP;\n" + "AddressV = CLAMP;\n" + "AddressW = CLAMP;\n" + + "MAGFILTER = linear;\n" + "MINFILTER = linear;\n" + "};\n" + "#endif // AV_SKYBOX_LOOKUP\n" + + "float4 DIFFUSE_COLOR;\n" + "float4 SPECULAR_COLOR;\n" + "float4 AMBIENT_COLOR;\n" + "float4 EMISSIVE_COLOR;\n" + + "#ifdef AV_SPECULAR_COMPONENT\n" + "float SPECULARITY;\n" + "#endif\n" + "#ifdef AV_OPACITY\n" + "float TRANSPARENCY;\n" + "#endif\n" + + // ----------------------------------------------------------------------------\n" + // Vertex shader input structure\n" + // ----------------------------------------------------------------------------\n" + "struct VS_INPUT\n" + "{\n" + // Position\n" + "float3 Position : POSITION;\n" + "float3 Normal : NORMAL;\n" + + // NOTE: Tangents and bitangents are passed to the shader + // in every case, even if not required. This saves a few lines + // of code ... + + "float3 Tangent : TEXCOORD0;\n" + "float3 Bitangent : TEXCOORD1;\n" + "float2 TexCoord0 : TEXCOORD2;\n" + "};\n" + + + // ----------------------------------------------------------------------------\n" + // Vertex shader output structure\n" + // ----------------------------------------------------------------------------\n" + "struct VS_OUTPUT\n" + "{\n" + // Position\n" + "float4 Position : POSITION;\n" + + "float3 ViewDir : TEXCOORD0;\n" + + "#ifndef AV_NORMAL_TEXTURE\n" + "float3 Normal : TEXCOORD1;\n" + "#endif\n" + + "float2 TexCoord0 : TEXCOORD2;\n" + + "#ifdef AV_NORMAL_TEXTURE\n" + + "float3 Light0 : TEXCOORD3;\n" + "float3 Light1 : TEXCOORD4;\n" + + "#endif\n" + "};\n" + + + // ----------------------------------------------------------------------------\n" + // Selective SuperSampling in screenspace for reflection lookups\n" + // ----------------------------------------------------------------------------\n" + "#ifndef AV_SKYBOX_LOOKUP\n" + "#define AV_DISABLESSS\n" + "#endif\n" + "#ifndef AV_DISABLESSS\n" + "float3 GetSSSCubeMap(float3 Reflect)\n" + "{\n" + // compute the reflection vector in screen space\n" + "float3 ScreenReflect = mul(Reflect,ViewProj);\n" + + // compute the gradients of the reflection vector\n" + "float3 fDX = ddx(ScreenReflect);\n" + "float3 fDY = ddy(ScreenReflect);\n" + + // take the center step and calculate gradients for it\n" + "float3 fColor = texCUBE(EnvironmentMapSampler,Reflect).rgb;\n" + + // Take 10 samples around the center step \n" + "fColor += texCUBEgrad(EnvironmentMapSampler,mul( ScreenReflect + (0.4f * 2.0 / 3.5) * fDX + (0.4f * 2.0 / 3.5) * fDY, InvViewProj),fDX,fDY).rgb;\n" + "fColor += texCUBEgrad(EnvironmentMapSampler,mul( ScreenReflect + (0.4f * 3.0 / 3.5) * fDX + (0.4f *-1.0 / 3.5) * fDY, InvViewProj),fDX,fDY).rgb;\n" + "fColor += texCUBEgrad(EnvironmentMapSampler,mul( ScreenReflect + (0.4f * 1.0 / 3.5) * fDX + (0.4f *-3.0 / 3.5) * fDY, InvViewProj),fDX,fDY).rgb;\n" + "fColor += texCUBEgrad(EnvironmentMapSampler,mul( ScreenReflect + (0.4f *-2.0 / 3.5) * fDX + (0.4f *-2.0 / 3.5) * fDY, InvViewProj),fDX,fDY).rgb;\n" + "fColor += texCUBEgrad(EnvironmentMapSampler,mul( ScreenReflect + (0.4f *-3.0 / 3.5) * fDX + (0.4f * 1.0 / 3.5) * fDY, InvViewProj),fDX,fDY).rgb;\n" + "fColor += texCUBEgrad(EnvironmentMapSampler,mul( ScreenReflect + (0.4f *-1.0 / 3.5) * fDX + (0.4f * 3.0 / 3.5) * fDY, InvViewProj),fDX,fDY).rgb;\n" + "fColor /= 7;\n" + "return fColor;\n" + "}\n" + "#else\n" + "#define GetSSSCubeMap(_refl) (texCUBElod(EnvironmentMapSampler,float4(_refl,0.0f)).rgb) \n" + "#endif\n" + + // bugfix: if normal mapping is active we have the reflection + // vector in tangent, not in world space. Would need the inverse + // of the TSM matrix in the pixel shader (or world space tangent mapping) + // Simply disable realtime reflection for normal mapping. + "#ifdef AV_NORMAL_TEXTURE\n" + "#undef GetSSSCubeMap\n" + "#define GetSSSCubeMap(_refl) (float3(1.0f,1.0f,1.0f))\n" + "#endif\n" + + + // ----------------------------------------------------------------------------\n" + // Vertex shader\n" + // ----------------------------------------------------------------------------\n" + "VS_OUTPUT MaterialVShader_D1(VS_INPUT IN)\n" + "{\n" + // Initialize the output structure with zero\n" + "VS_OUTPUT Out = (VS_OUTPUT)0;\n" + + // Multiply with the WorldViewProjection matrix\n" + "Out.Position = mul(float4(IN.Position,1.0f),WorldViewProjection);\n" + "float3 WorldPos = mul(float4(IN.Position,1.0f),World);\n" + "Out.TexCoord0 = IN.TexCoord0;\n" + + "#ifndef AV_NORMAL_TEXTURE\n" + "Out.ViewDir = vCameraPos - WorldPos;\n" + "Out.Normal = mul(IN.Normal,WorldInverseTranspose);\n" + "#endif\n" + + "#ifdef AV_NORMAL_TEXTURE\n" + "float3x3 TBNMatrix = float3x3(IN.Tangent, IN.Bitangent, IN.Normal);\n" + "float3x3 WTTS = mul(TBNMatrix, (float3x3)WorldInverseTranspose);\n" + "Out.Light0 = normalize(mul(WTTS, afLightDir[0] ));\n" + "Out.ViewDir = normalize(mul(WTTS, (vCameraPos - WorldPos)));\n" + "#endif\n" + "return Out;\n" + "}\n" + "// ----------------------------------------------------------------------------\n" + "VS_OUTPUT MaterialVShader_D2(VS_INPUT IN)\n" + "{\n" + // Initialize the output structure with zero\n" + "VS_OUTPUT Out = (VS_OUTPUT)0;\n" + + // Multiply with the WorldViewProjection matrix\n" + "Out.Position = mul(float4(IN.Position,1.0f),WorldViewProjection);\n" + "float3 WorldPos = mul(float4(IN.Position,1.0f),World);\n" + "Out.TexCoord0 = IN.TexCoord0;\n" + + "#ifndef AV_NORMAL_TEXTURE\n" + "Out.ViewDir = vCameraPos - WorldPos;\n" + "Out.Normal = mul(IN.Normal,WorldInverseTranspose);\n" + "#endif\n" + + "#ifdef AV_NORMAL_TEXTURE\n" + "float3x3 TBNMatrix = float3x3(IN.Tangent, IN.Bitangent, IN.Normal);\n" + "float3x3 WTTS = mul(TBNMatrix, (float3x3)WorldInverseTranspose);\n" + "Out.Light0 = normalize(mul(WTTS, afLightDir[0] ));\n" + "Out.Light1 = normalize(mul(WTTS, afLightDir[1] ));\n" + "Out.ViewDir = normalize(mul(WTTS, (vCameraPos - WorldPos)));\n" + "#endif\n" + "return Out;\n" + "}\n" + + + // ----------------------------------------------------------------------------\n" + // Pixel shader\n" + // ----------------------------------------------------------------------------\n" + "float4 MaterialPShaderSpecular_D1(VS_OUTPUT IN) : COLOR\n" + "{\n" + "float4 OUT = float4(0.0f,0.0f,0.0f,1.0f);\n" + + "#ifdef AV_NORMAL_TEXTURE\n" + "float3 IN_Light0 = normalize(IN.Light0);\n" + "float3 Normal = normalize(2.0f * tex2D(NORMAL_SAMPLER, IN.TexCoord0).rgb - 1.0f);\n" + "#else\n" + "float3 Normal = normalize(IN.Normal);\n" + "#endif \n" + "float3 ViewDir = normalize(IN.ViewDir);\n" + "#ifdef AV_SPECULAR_COMPONENT\n" + "float3 Reflect = -normalize(reflect (ViewDir,Normal));\n" + "#endif // !AV_SPECULAR_COMPONENT\n" + + "{\n" + "#ifdef AV_NORMAL_TEXTURE\n" + "float L1 = dot(Normal,IN_Light0) * 0.5f + 0.5f;\n" + "#define AV_LIGHT_0 IN_Light0\n" + // would need to convert the reflection vector into world space .... + // simply let it ... + "#else\n" + "float L1 = dot(Normal,afLightDir[0]) * 0.5f + 0.5f;\n" + "#define AV_LIGHT_0 afLightDir[0]\n" + "#endif\n" + "float fHalfLambert = L1*L1;\n" + "#ifdef AV_DIFFUSE_TEXTURE\n" + "OUT.rgb += afLightColor[0].rgb * DIFFUSE_COLOR.rgb * tex2D(DIFFUSE_SAMPLER,IN.TexCoord0).rgb * fHalfLambert +\n" + "#else\n" + "OUT.rgb += afLightColor[0].rgb * DIFFUSE_COLOR.rgb * fHalfLambert +\n" + "#endif // !AV_DIFFUSE_TEXTURE\n" + + "#ifdef AV_SPECULAR_COMPONENT\n" + "#ifndef AV_SKYBOX_LOOKUP\n" + "#ifdef AV_SPECULAR_TEXTURE\n" + "SPECULAR_COLOR.rgb * afLightColor[0].rgb * tex2D(SPECULAR_SAMPLER,IN.TexCoord0).rgb * (saturate(fHalfLambert * 4.0f) * pow(dot(Reflect,AV_LIGHT_0),SPECULARITY)) + \n" + "#else\n" + "SPECULAR_COLOR.rgb * afLightColor[0].rgb * (saturate(fHalfLambert * 4.0f) * pow(dot(Reflect,AV_LIGHT_0),SPECULARITY)) + \n" + "#endif // !AV_SPECULAR_TEXTURE\n" + "#else\n" + "#ifdef AV_SPECULAR_TEXTURE\n" + "SPECULAR_COLOR.rgb * afLightColor[0].rgb * GetSSSCubeMap(Reflect) * tex2D(SPECULAR_SAMPLER,IN.TexCoord0).rgb * (saturate(fHalfLambert * 4.0f) * pow(dot(Reflect,AV_LIGHT_0),SPECULARITY)) + \n" + "#else\n" + "SPECULAR_COLOR.rgb * afLightColor[0].rgb * GetSSSCubeMap(Reflect) * (saturate(fHalfLambert * 4.0f) * pow(dot(Reflect,AV_LIGHT_0),SPECULARITY)) + \n" + "#endif // !AV_SPECULAR_TEXTURE\n" + "#endif // !AV_SKYBOX_LOOKUP\n" + "#endif // !AV_SPECULAR_COMPONENT\n" + + "#ifdef AV_AMBIENT_TEXTURE\n" + "AMBIENT_COLOR.rgb * afLightColorAmbient[0].rgb * tex2D(AMBIENT_SAMPLER,IN.TexCoord0).rgb +\n" + "#else\n" + "AMBIENT_COLOR.rgb * afLightColorAmbient[0].rgb + \n" + "#endif // !AV_AMBIENT_TEXTURE\n" + "#ifdef AV_EMISSIVE_TEXTURE\n" + "EMISSIVE_COLOR.rgb * tex2D(EMISSIVE_SAMPLER,IN.TexCoord0).rgb;\n" + "#else \n" + "EMISSIVE_COLOR.rgb;\n" + "#endif // !AV_EMISSIVE_TEXTURE\n" + "}\n" + "#ifdef AV_OPACITY\n" + "OUT.a = TRANSPARENCY;\n" + "#endif\n" + "#ifdef AV_OPACITY_TEXTURE\n" + "OUT.a *= tex2D(OPACITY_SAMPLER,IN.TexCoord0). AV_OPACITY_TEXTURE_REGISTER_MASK;\n" + "#endif\n" + "return OUT;\n" + + "#undef AV_LIGHT_0\n" + "}\n" + // ----------------------------------------------------------------------------\n" + "float4 MaterialPShaderSpecular_D2(VS_OUTPUT IN) : COLOR\n" + "{\n" + "float4 OUT = float4(0.0f,0.0f,0.0f,1.0f);\n" + + "#ifdef AV_NORMAL_TEXTURE\n" + "float3 IN_Light0 = normalize(IN.Light0);\n" + "float3 IN_Light1 = normalize(IN.Light1);\n" + "float3 Normal = normalize(2.0f * tex2D(NORMAL_SAMPLER, IN.TexCoord0).rgb - 1.0f);\n" + "#else\n" + "float3 Normal = normalize(IN.Normal);\n" + "#endif \n" + "float3 ViewDir = normalize(IN.ViewDir);\n" + "#ifdef AV_SPECULAR_COMPONENT\n" + "float3 Reflect = -normalize(reflect (ViewDir,Normal));\n" + "#endif // !AV_SPECULAR_COMPONENT\n" + + "{\n" + + "#ifdef AV_NORMAL_TEXTURE\n" + "float L1 = dot(Normal,IN_Light0) * 0.5f + 0.5f;\n" + "#define AV_LIGHT_0 IN_Light0\n" + "#else\n" + "float L1 = dot(Normal,afLightDir[0]) * 0.5f + 0.5f;\n" + "#define AV_LIGHT_0 afLightDir[0]\n" + "#endif\n" + "float fHalfLambert = L1*L1;\n" + + "#ifdef AV_DIFFUSE_TEXTURE\n" + "OUT.rgb += afLightColor[0].rgb * DIFFUSE_COLOR.rgb * tex2D(DIFFUSE_SAMPLER,IN.TexCoord0).rgb * fHalfLambert +\n" + "#else\n" + "OUT.rgb += afLightColor[0].rgb * DIFFUSE_COLOR.rgb * fHalfLambert +\n" + "#endif // !AV_DIFFUSE_TEXTURE\n" + + "#ifdef AV_SPECULAR_COMPONENT\n" + "#ifndef AV_SKYBOX_LOOKUP\n" + "#ifdef AV_SPECULAR_TEXTURE\n" + "SPECULAR_COLOR.rgb * afLightColor[0].rgb * tex2D(SPECULAR_SAMPLER,IN.TexCoord0).rgb * (saturate(fHalfLambert * 4.0f) * pow(dot(Reflect,AV_LIGHT_0),SPECULARITY)) + \n" + "#else\n" + "SPECULAR_COLOR.rgb * afLightColor[0].rgb * (saturate(fHalfLambert * 4.0f) * pow(dot(Reflect,AV_LIGHT_0),SPECULARITY)) + \n" + "#endif // !AV_SPECULAR_TEXTURE\n" + "#else\n" + "#ifdef AV_SPECULAR_TEXTURE\n" + "SPECULAR_COLOR.rgb * afLightColor[0].rgb * GetSSSCubeMap(Reflect) * tex2D(SPECULAR_SAMPLER,IN.TexCoord0).rgb * (saturate(fHalfLambert * 4.0f) * pow(dot(Reflect,AV_LIGHT_0),SPECULARITY)) + \n" + "#else\n" + "SPECULAR_COLOR.rgb * afLightColor[0].rgb * GetSSSCubeMap(Reflect) * (saturate(fHalfLambert * 4.0f) * pow(dot(Reflect,AV_LIGHT_0),SPECULARITY)) + \n" + "#endif // !AV_SPECULAR_TEXTURE\n" + "#endif // !AV_SKYBOX_LOOKUP\n" + "#endif // !AV_SPECULAR_COMPONENT\n" + "#ifdef AV_AMBIENT_TEXTURE\n" + "AMBIENT_COLOR.rgb * afLightColorAmbient[0].rgb * tex2D(AMBIENT_SAMPLER,IN.TexCoord0).rgb + \n" + "#else\n" + "AMBIENT_COLOR.rgb * afLightColorAmbient[0].rgb + \n" + "#endif // !AV_AMBIENT_TEXTURE\n" + "#ifdef AV_EMISSIVE_TEXTURE\n" + "EMISSIVE_COLOR.rgb * tex2D(EMISSIVE_SAMPLER,IN.TexCoord0).rgb;\n" + "#else \n" + "EMISSIVE_COLOR.rgb;\n" + "#endif // !AV_EMISSIVE_TEXTURE\n" + "}\n" + "{\n" + "#ifdef AV_NORMAL_TEXTURE\n" + "float L1 = dot(Normal,IN_Light1) * 0.5f + 0.5f;\n" + "#define AV_LIGHT_1 IN_Light1\n" + "#else\n" + "float L1 = dot(Normal,afLightDir[1]) * 0.5f + 0.5f;\n" + "#define AV_LIGHT_1 afLightDir[1]\n" + "#endif\n" + "float fHalfLambert = L1*L1;\n" + "#ifdef AV_DIFFUSE_TEXTURE\n" + "OUT.rgb += afLightColor[1].rgb * DIFFUSE_COLOR.rgb * tex2D(DIFFUSE_SAMPLER,IN.TexCoord0).rgb * fHalfLambert +\n" + "#else\n" + "OUT.rgb += afLightColor[1].rgb * DIFFUSE_COLOR.rgb * fHalfLambert +\n" + "#endif // !AV_DIFFUSE_TEXTURE\n" + + "#ifdef AV_SPECULAR_COMPONENT\n" + "#ifndef AV_SKYBOX_LOOKUP\n" + "#ifdef AV_SPECULAR_TEXTURE\n" + "SPECULAR_COLOR.rgb * afLightColor[1].rgb * tex2D(SPECULAR_SAMPLER,IN.TexCoord0).rgb * (saturate(fHalfLambert * 4.0f) * pow(dot(Reflect,AV_LIGHT_1),SPECULARITY)) + \n" + "#else\n" + "SPECULAR_COLOR.rgb * afLightColor[1].rgb * (saturate(fHalfLambert * 4.0f) * pow(dot(Reflect,AV_LIGHT_1),SPECULARITY)) + \n" + "#endif // !AV_SPECULAR_TEXTURE\n" + "#else\n" + "#ifdef AV_SPECULAR_TEXTURE\n" + "SPECULAR_COLOR.rgb * afLightColor[1].rgb * GetSSSCubeMap(Reflect) * tex2D(SPECULAR_SAMPLER,IN.TexCoord0).rgb * (saturate(fHalfLambert * 4.0f) * pow(dot(Reflect,AV_LIGHT_1),SPECULARITY)) + \n" + "#else\n" + "SPECULAR_COLOR.rgb * afLightColor[1].rgb * GetSSSCubeMap(Reflect) * (saturate(fHalfLambert * 4.0f) * pow(dot(Reflect,AV_LIGHT_1),SPECULARITY)) + \n" + "#endif // !AV_SPECULAR_TEXTURE\n" + "#endif // !AV_SKYBOX_LOOKUP\n" + "#endif // !AV_SPECULAR_COMPONENT\n" + "#ifdef AV_AMBIENT_TEXTURE\n" + "AMBIENT_COLOR.rgb * afLightColorAmbient[1].rgb * tex2D(AMBIENT_SAMPLER,IN.TexCoord0).rgb + \n" + "#else\n" + "AMBIENT_COLOR.rgb * afLightColorAmbient[1].rgb + \n" + "#endif // !AV_AMBIENT_TEXTURE\n" + "#ifdef AV_EMISSIVE_TEXTURE\n" + "EMISSIVE_COLOR.rgb * tex2D(EMISSIVE_SAMPLER,IN.TexCoord0).rgb;\n" + "#else \n" + "EMISSIVE_COLOR.rgb;\n" + "#endif // !AV_EMISSIVE_TEXTURE\n" + "}\n" + "#ifdef AV_OPACITY\n" + "OUT.a = TRANSPARENCY;\n" + "#endif\n" + "#ifdef AV_OPACITY_TEXTURE\n" + "OUT.a *= tex2D(OPACITY_SAMPLER,IN.TexCoord0). AV_OPACITY_TEXTURE_REGISTER_MASK;\n" + "#endif\n" + "return OUT;\n" + + "#undef AV_LIGHT_0\n" + "#undef AV_LIGHT_1\n" + "}\n" + // ----------------------------------------------------------------------------\n" + "float4 MaterialPShaderSpecular_PS20_D1(VS_OUTPUT IN) : COLOR\n" + "{\n" + "float4 OUT = float4(0.0f,0.0f,0.0f,1.0f);\n" + + "#ifdef AV_NORMAL_TEXTURE\n" + "float3 IN_Light0 = normalize(IN.Light0);\n" + "float3 Normal = normalize(2.0f * tex2D(NORMAL_SAMPLER, IN.TexCoord0).rgb - 1.0f);\n" + "#else\n" + "float3 Normal = normalize(IN.Normal);\n" + "#endif \n" + "float3 ViewDir = normalize(IN.ViewDir);\n" + + "{\n" + "#ifdef AV_NORMAL_TEXTURE\n" + "float L1 = dot(Normal,IN_Light0) * 0.5f + 0.5f;\n" + "float3 Reflect = reflect (Normal,IN_Light0);\n" + "#else\n" + "float L1 = dot(Normal,afLightDir[0]) * 0.5f + 0.5f;\n" + "float3 Reflect = reflect (Normal,afLightDir[0]);\n" + "#endif\n" + "#ifdef AV_DIFFUSE_TEXTURE\n" + "OUT.rgb += afLightColor[0].rgb * DIFFUSE_COLOR.rgb * tex2D(DIFFUSE_SAMPLER,IN.TexCoord0).rgb * L1 +\n" + "#else\n" + "OUT.rgb += afLightColor[0].rgb * DIFFUSE_COLOR.rgb * L1 +\n" + "#endif // !AV_DIFFUSE_TEXTURE\n" + + "#ifdef AV_SPECULAR_COMPONENT\n" + "#ifdef AV_SPECULAR_TEXTURE\n" + "SPECULAR_COLOR.rgb * afLightColor[0].rgb * tex2D(SPECULAR_SAMPLER,IN.TexCoord0).rgb * (saturate(L1 * 4.0f) * pow(dot(Reflect,ViewDir),SPECULARITY)) + \n" + "#else\n" + "SPECULAR_COLOR.rgb * afLightColor[0].rgb * (saturate(L1 * 4.0f) * pow(dot(Reflect,ViewDir),SPECULARITY)) + \n" + "#endif // !AV_SPECULAR_TEXTURE\n" + "#endif // !AV_SPECULAR_COMPONENT\n" + "#ifdef AV_AMBIENT_TEXTURE\n" + "AMBIENT_COLOR.rgb * afLightColorAmbient[0].rgb * tex2D(AMBIENT_SAMPLER,IN.TexCoord0).rgb +\n" + "#else\n" + "AMBIENT_COLOR.rgb * afLightColorAmbient[0].rgb +\n" + "#endif // !AV_AMBIENT_TEXTURE\n" + "#ifdef AV_EMISSIVE_TEXTURE\n" + "EMISSIVE_COLOR.rgb * tex2D(EMISSIVE_SAMPLER,IN.TexCoord0).rgb;\n" + "#else \n" + "EMISSIVE_COLOR.rgb;\n" + "#endif // !AV_EMISSIVE_TEXTURE\n" + "}\n" + + "#ifdef AV_OPACITY\n" + "OUT.a = TRANSPARENCY;\n" + "#endif\n" + "#ifdef AV_OPACITY_TEXTURE\n" + "OUT.a *= tex2D(OPACITY_SAMPLER,IN.TexCoord0). AV_OPACITY_TEXTURE_REGISTER_MASK;\n" + "#endif\n" + "return OUT;\n" + "}\n" + // ----------------------------------------------------------------------------\n" + "float4 MaterialPShaderSpecular_PS20_D2(VS_OUTPUT IN) : COLOR\n" + "{\n" + "float4 OUT = float4(0.0f,0.0f,0.0f,1.0f);\n" + + "#ifdef AV_NORMAL_TEXTURE\n" + "float3 IN_Light0 = normalize(IN.Light0);\n" + "float3 IN_Light1 = normalize(IN.Light1);\n" + "float3 Normal = normalize(2.0f * tex2D(NORMAL_SAMPLER, IN.TexCoord0) - 1.0f);\n" + "#else\n" + "float3 Normal = normalize(IN.Normal);\n" + "#endif \n" + "float3 ViewDir = normalize(IN.ViewDir);\n" + + "{\n" + "#ifdef AV_NORMAL_TEXTURE\n" + "float L1 = dot(Normal,IN_Light0) * 0.5f + 0.5f;\n" + "float3 Reflect = reflect (Normal,IN_Light0);\n" + "#else\n" + "float L1 = dot(Normal,afLightDir[0]) * 0.5f + 0.5f;\n" + "float3 Reflect = reflect (Normal,afLightDir[0]);\n" + "#endif\n" + "#ifdef AV_DIFFUSE_TEXTURE\n" + "OUT.rgb += afLightColor[0].rgb * DIFFUSE_COLOR.rgb * tex2D(DIFFUSE_SAMPLER,IN.TexCoord0).rgb * L1 +\n" + "#else\n" + "OUT.rgb += afLightColor[0].rgb * DIFFUSE_COLOR.rgb * L1 +\n" + "#endif // !AV_DIFFUSE_TEXTURE\n" + + "#ifdef AV_SPECULAR_COMPONENT\n" + "#ifdef AV_SPECULAR_TEXTURE\n" + "SPECULAR_COLOR.rgb * afLightColor[0].rgb * tex2D(SPECULAR_SAMPLER,IN.TexCoord0).rgb * (saturate(L1 * 4.0f) * pow(dot(Reflect,ViewDir),SPECULARITY)) + \n" + "#else\n" + "SPECULAR_COLOR.rgb * afLightColor[0].rgb * (saturate(L1 * 4.0f) * pow(dot(Reflect,ViewDir),SPECULARITY)) + \n" + "#endif // !AV_SPECULAR_TEXTURE\n" + "#endif // !AV_SPECULAR_COMPONENT\n" + "#ifdef AV_AMBIENT_TEXTURE\n" + "AMBIENT_COLOR.rgb * afLightColorAmbient[0].rgb * tex2D(AMBIENT_SAMPLER,IN.TexCoord0).rgb +\n" + "#else\n" + "AMBIENT_COLOR.rgb * afLightColorAmbient[0].rgb +\n" + "#endif // !AV_AMBIENT_TEXTURE\n" + "#ifdef AV_EMISSIVE_TEXTURE\n" + "EMISSIVE_COLOR.rgb * tex2D(EMISSIVE_SAMPLER,IN.TexCoord0).rgb;\n" + "#else \n" + "EMISSIVE_COLOR.rgb;\n" + "#endif // !AV_EMISSIVE_TEXTURE\n" + "}\n" + "{\n" + "#ifdef AV_NORMAL_TEXTURE\n" + "float L1 = dot(Normal,IN_Light1) * 0.5f + 0.5f;\n" + "float3 Reflect = reflect (Normal,IN_Light1);\n" + "#else\n" + "float L1 = dot(Normal,afLightDir[1]) * 0.5f + 0.5f;\n" + "float3 Reflect = reflect (Normal,afLightDir[1]);\n" + "#endif\n" + "#ifdef AV_DIFFUSE_TEXTURE\n" + "OUT.rgb += afLightColor[1].rgb * DIFFUSE_COLOR.rgb * tex2D(DIFFUSE_SAMPLER,IN.TexCoord0).rgb * L1 +\n" + "#else\n" + "OUT.rgb += afLightColor[1].rgb * DIFFUSE_COLOR.rgb * L1 +\n" + "#endif // !AV_DIFFUSE_TEXTURE\n" + + "#ifdef AV_SPECULAR_COMPONENT\n" + "#ifdef AV_SPECULAR_TEXTURE\n" + "SPECULAR_COLOR.rgb * afLightColor[1].rgb * tex2D(SPECULAR_SAMPLER,IN.TexCoord0).rgb * (saturate(L1 * 4.0f) * pow(dot(Reflect,ViewDir),SPECULARITY)) + \n" + "#else\n" + "SPECULAR_COLOR.rgb * afLightColor[1].rgb * (saturate(L1 * 4.0f) * pow(dot(Reflect,ViewDir),SPECULARITY)) + \n" + "#endif // !AV_SPECULAR_TEXTURE\n" + "#endif // !AV_SPECULAR_COMPONENT\n" + "#ifdef AV_AMBIENT_TEXTURE\n" + "AMBIENT_COLOR.rgb * afLightColorAmbient[1].rgb * tex2D(AMBIENT_SAMPLER,IN.TexCoord0).rgb +\n" + "#else\n" + "AMBIENT_COLOR.rgb * afLightColorAmbient[1].rgb + \n" + "#endif // !AV_AMBIENT_TEXTURE\n" + "#ifdef AV_EMISSIVE_TEXTURE\n" + "EMISSIVE_COLOR.rgb * tex2D(EMISSIVE_SAMPLER,IN.TexCoord0).rgb;\n" + "#else \n" + "EMISSIVE_COLOR.rgb;\n" + "#endif // !AV_EMISSIVE_TEXTURE\n" + "}\n" + + "#ifdef AV_OPACITY\n" + "OUT.a = TRANSPARENCY;\n" + "#endif\n" + "#ifdef AV_OPACITY_TEXTURE\n" + "OUT.a *= tex2D(OPACITY_SAMPLER,IN.TexCoord0). AV_OPACITY_TEXTURE_REGISTER_MASK;\n" + "#endif\n" + "return OUT;\n" + "}\n" + + + // ----------------------------------------------------------------------------\n" + // Technique for the material effect\n" + // ----------------------------------------------------------------------------\n" + "technique MaterialFXSpecular_D1\n" + "{\n" + "pass p0\n" + "{\n" + "CullMode=none;\n" + + "#ifdef AV_OPACITY_TEXTURE\n" + "AlphaBlendEnable=TRUE;" + "SrcBlend = srcalpha;\n" + "DestBlend = invsrcalpha;\n" + "#else\n" + "#ifdef AV_OPACITY\n" + "AlphaBlendEnable=TRUE;" + "SrcBlend = srcalpha;\n" + "DestBlend = invsrcalpha;\n" + "#endif \n" + "#endif\n" + + "PixelShader = compile ps_3_0 MaterialPShaderSpecular_D1();\n" + "VertexShader = compile vs_3_0 MaterialVShader_D1();\n" + "}\n" + "};\n" + "technique MaterialFXSpecular_D2\n" + "{\n" + "pass p0\n" + "{\n" + "CullMode=none;\n" + + "#ifdef AV_OPACITY_TEXTURE\n" + "AlphaBlendEnable=TRUE;" + "SrcBlend = srcalpha;\n" + "DestBlend = invsrcalpha;\n" + "#else\n" + "#ifdef AV_OPACITY\n" + "AlphaBlendEnable=TRUE;" + "SrcBlend = srcalpha;\n" + "DestBlend = invsrcalpha;\n" + "#endif \n" + "#endif\n" + + "PixelShader = compile ps_3_0 MaterialPShaderSpecular_D2();\n" + "VertexShader = compile vs_3_0 MaterialVShader_D2();\n" + "}\n" + "};\n" + + + // ----------------------------------------------------------------------------\n" + // Technique for the material effect (ps_2_0)\n" + // ----------------------------------------------------------------------------\n" + "technique MaterialFXSpecular_PS20_D1\n" + "{\n" + "pass p0\n" + "{\n" + "CullMode=none;\n" + + "#ifdef AV_OPACITY_TEXTURE\n" + "AlphaBlendEnable=TRUE;" + "SrcBlend = srcalpha;\n" + "DestBlend = invsrcalpha;\n" + "#else\n" + "#ifdef AV_OPACITY\n" + "AlphaBlendEnable=TRUE;" + "SrcBlend = srcalpha;\n" + "DestBlend = invsrcalpha;\n" + "#endif \n" + "#endif\n" + + "PixelShader = compile ps_2_0 MaterialPShaderSpecular_PS20_D1();\n" + "VertexShader = compile vs_2_0 MaterialVShader_D1();\n" + "}\n" + "};\n" + "technique MaterialFXSpecular_PS20_D2\n" + "{\n" + "pass p0\n" + "{\n" + "CullMode=none;\n" + + "#ifdef AV_OPACITY_TEXTURE\n" + "AlphaBlendEnable=TRUE;" + "SrcBlend = srcalpha;\n" + "DestBlend = invsrcalpha;\n" + "#else\n" + "#ifdef AV_OPACITY\n" + "AlphaBlendEnable=TRUE;" + "SrcBlend = srcalpha;\n" + "DestBlend = invsrcalpha;\n" + "#endif \n" + "#endif\n" + + "PixelShader = compile ps_2_0 MaterialPShaderSpecular_PS20_D2();\n" + "VertexShader = compile vs_2_0 MaterialVShader_D2();\n" + "}\n" + "};\n" + ); + +std::string g_szPassThroughShader = std::string( + //-------------------------------------------------------------------------------\n" + /**\n" + * This program is distributed under the terms of the GNU Lesser General\n + * Public License (LGPL). \n + *\n + * ASSIMP Viewer Utility\n + *\n" + */ + //-------------------------------------------------------------------------------\n" + + "texture TEXTURE_2D;\n" + "sampler TEXTURE_SAMPLER = sampler_state\n" + "{\n" + "Texture = (TEXTURE_2D);\n" + "};\n" + + + // ----------------------------------------------------------------------------\n" + "struct VS_OUTPUT\n" + "{\n" + "// Position\n" + "float4 _Position : POSITION;\n" + + "// Texture coordinate\n" + "float2 _TexCoord0 : TEXCOORD0;\n" + "};\n" + + + // ----------------------------------------------------------------------------\n" + "VS_OUTPUT DefaultVShader(float4 INPosition : POSITION,\n" + "float2 INTexCoord0 : TEXCOORD0 )\n" + "{\n" + "// Initialize the output structure with zero\n" + "VS_OUTPUT Out = (VS_OUTPUT)0;\n" + + "Out._Position = INPosition;\n" + "Out._TexCoord0 = INTexCoord0;\n" + + "return Out;\n" + "}\n" + + + // ----------------------------------------------------------------------------\n" + "float4 PassThrough_PS(float2 IN : TEXCOORD0) : COLOR\n" + "{\n" + "return tex2D(TEXTURE_SAMPLER,IN);\n" + "}\n" + + + // ----------------------------------------------------------------------------\n" + // Simple pass-through technique\n" + // ----------------------------------------------------------------------------\n" + "technique PassThrough\n" + "{\n" + "pass p0\n" + "{\n" + "FillMode=Solid;\n" + "ZEnable = FALSE;\n" + "CullMode = none;\n" + "AlphaBlendEnable = TRUE;\n" + "SrcBlend =srcalpha;\n" + "DestBlend =invsrcalpha;\n" + "PixelShader = compile ps_2_0 PassThrough_PS();\n" + "VertexShader = compile vs_2_0 DefaultVShader();\n" + "}\n" + "};\n" + ); + }; \ No newline at end of file diff --git a/tools/assimp_view/Shaders.h b/tools/assimp_view/Shaders.h new file mode 100644 index 000000000..7ceec5ede --- /dev/null +++ b/tools/assimp_view/Shaders.h @@ -0,0 +1,29 @@ +//------------------------------------------------------------------------------- +/** +* This program is distributed under the terms of the GNU Lesser General +* Public License (LGPL). +* +* ASSIMP Viewer Utility +* +*/ +//------------------------------------------------------------------------------- + +#if (!defined AV_SHADERS_H_INCLUDED) +#define AV_SHADERS_H_INCLUDED + +// Shader used for rendering a skybox background +extern std::string g_szSkyboxShader; + +// Shader used for visualizing normal vectors +extern std::string g_szNormalsShader; + +// Default shader +extern std::string g_szDefaultShader; + +// Material shader +extern std::string g_szMaterialShader; + +// Shader used to draw the yellow circle on top of everything +extern std::string g_szPassThroughShader; + +#endif // !! AV_SHADERS_H_INCLUDED diff --git a/tools/assimp_view/assimp_view.cpp b/tools/assimp_view/assimp_view.cpp new file mode 100644 index 000000000..c26a4e8a5 --- /dev/null +++ b/tools/assimp_view/assimp_view.cpp @@ -0,0 +1,1483 @@ + +//------------------------------------------------------------------------------- +/** + * This program is distributed under the terms of the GNU Lesser General + * Public License (LGPL). + * + * ASSIMP Viewer Utility + * + */ +//------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "assimp_view.h" + + +namespace AssimpView { + + +//------------------------------------------------------------------------------- +// evil globals +//------------------------------------------------------------------------------- +HINSTANCE g_hInstance = NULL; +HWND g_hDlg = NULL; +IDirect3D9* g_piD3D = NULL; +IDirect3DDevice9* g_piDevice = NULL; +double g_fFPS = 0.0f; +char g_szFileName[MAX_PATH]; +ID3DXEffect* g_piDefaultEffect = NULL; +ID3DXEffect* g_piNormalsEffect = NULL; +ID3DXEffect* g_piPassThroughEffect = NULL; +bool g_bMousePressed = false; +bool g_bMousePressedR = false; +bool g_bMousePressedM = false; +bool g_bMousePressedBoth = false; +float g_fElpasedTime = 0.0f; +D3DCAPS9 g_sCaps; +bool g_bLoadingFinished = false; +HANDLE g_hThreadHandle = NULL; +float g_fWheelPos = -10.0f; +bool g_bLoadingCanceled = false; +IDirect3DTexture9* g_pcTexture = NULL; + +aiMatrix4x4 g_mWorld; +aiMatrix4x4 g_mWorldRotate; +aiVector3D g_vRotateSpeed = aiVector3D(0.5f,0.5f,0.5f); + +aiVector3D g_avLightDirs[1] = { aiVector3D(-0.5f,0.6f,0.2f) /*, + aiVector3D(-0.5f,0.5f,0.5f)*/}; +POINT g_mousePos; +POINT g_LastmousePos; +bool g_bFPSView = false; +bool g_bInvert = false; +EClickPos g_eClick = EClickPos_Circle; +unsigned int g_iCurrentColor = 0; + +float g_fLightIntensity = 1.0f; +float g_fLightColor = 1.0f; + +RenderOptions g_sOptions; +Camera g_sCamera; +AssetHelper *g_pcAsset = NULL; + +// +// Contains the mask image for the HUD +// (used to determine the position of a click) +// +unsigned char* g_szImageMask = NULL; + +//------------------------------------------------------------------------------- +// table of colors used for normal vectors. +//------------------------------------------------------------------------------- +D3DXVECTOR4 g_aclNormalColors[14] = + { + D3DXVECTOR4(0xFF / 255.0f,0xFF / 255.0f,0xFF / 255.0f, 1.0f), // white + + D3DXVECTOR4(0xFF / 255.0f,0x00 / 255.0f,0x00 / 255.0f,1.0f), // red + D3DXVECTOR4(0x00 / 255.0f,0xFF / 255.0f,0x00 / 255.0f,1.0f), // green + D3DXVECTOR4(0x00 / 255.0f,0x00 / 255.0f,0xFF / 255.0f,1.0f), // blue + + D3DXVECTOR4(0xFF / 255.0f,0xFF / 255.0f,0x00 / 255.0f,1.0f), // yellow + D3DXVECTOR4(0xFF / 255.0f,0x00 / 255.0f,0xFF / 255.0f,1.0f), // magenta + D3DXVECTOR4(0x00 / 255.0f,0xFF / 255.0f,0xFF / 255.0f,1.0f), // wtf + + D3DXVECTOR4(0xFF / 255.0f,0x60 / 255.0f,0x60 / 255.0f,1.0f), // light red + D3DXVECTOR4(0x60 / 255.0f,0xFF / 255.0f,0x60 / 255.0f,1.0f), // light green + D3DXVECTOR4(0x60 / 255.0f,0x60 / 255.0f,0xFF / 255.0f,1.0f), // light blue + + D3DXVECTOR4(0xA0 / 255.0f,0x00 / 255.0f,0x00 / 255.0f,1.0f), // dark red + D3DXVECTOR4(0x00 / 255.0f,0xA0 / 255.0f,0x00 / 255.0f,1.0f), // dark green + D3DXVECTOR4(0x00 / 255.0f,0x00 / 255.0f,0xA0 / 255.0f,1.0f), // dark blue + + D3DXVECTOR4(0x88 / 255.0f,0x88 / 255.0f,0x88 / 255.0f, 1.0f) // gray + }; + + +//------------------------------------------------------------------------------- +//! \brief Entry point for loader thread +//------------------------------------------------------------------------------- +DWORD WINAPI LoadThreadProc(LPVOID lpParameter) + { + UNREFERENCED_PARAMETER(lpParameter); + double fCur = (double)timeGetTime(); + + g_pcAsset->pcScene = aiImportFile(g_szFileName, + aiProcess_CalcTangentSpace | aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | + aiProcess_GenSmoothNormals | aiProcess_ConvertToLeftHanded | aiProcess_SplitLargeMeshes); + + double fEnd = (double)timeGetTime(); + double dTime = (fEnd - fCur) / 1000; + char szTemp[128]; + sprintf(szTemp,"%.5f",(float)dTime); + SetDlgItemText(g_hDlg,IDC_ELOAD,szTemp); + g_bLoadingFinished = true; + if (NULL == g_pcAsset->pcScene) + { + CLogDisplay::Instance().AddEntry("[ERROR] Unable to load this asset:", + D3DCOLOR_ARGB(0xFF,0xFF,0,0)); + CLogDisplay::Instance().AddEntry(aiGetErrorString(), + D3DCOLOR_ARGB(0xFF,0xFF,0,0)); + return 1; + } + return 0; + } + +//------------------------------------------------------------------------------- +//------------------------------------------------------------------------------- +int LoadAsset(void) + { + g_mWorldRotate = aiMatrix4x4(); + g_mWorld = aiMatrix4x4(); + + DWORD dwID; + g_bLoadingCanceled = false; + g_pcAsset = new AssetHelper(); + g_hThreadHandle = CreateThread(NULL,0,&LoadThreadProc,NULL,0,&dwID); + DialogBox(g_hInstance,MAKEINTRESOURCE(IDD_LOADDIALOG), + g_hDlg,&ProgressMessageProc); + + g_bLoadingFinished = false; + if (!g_pcAsset || !g_pcAsset->pcScene) + { + if (g_pcAsset) + { + delete g_pcAsset; + g_pcAsset = NULL; + } + return 0; + } + + g_pcAsset->apcMeshes = new AssetHelper::MeshHelper*[ + g_pcAsset->pcScene->mNumMeshes](); + + unsigned int iNumVert = 0; + unsigned int iNumFaces = 0; + for (unsigned int i = 0; i < g_pcAsset->pcScene->mNumMeshes;++i) + { + iNumVert += g_pcAsset->pcScene->mMeshes[i]->mNumVertices; + iNumFaces += g_pcAsset->pcScene->mMeshes[i]->mNumFaces; + g_pcAsset->apcMeshes[i] = new AssetHelper::MeshHelper(); + } + char szOut[120]; + sprintf(szOut,"%i",(int)iNumVert); + SetDlgItemText(g_hDlg,IDC_EVERT,szOut); + sprintf(szOut,"%i",(int)iNumFaces); + SetDlgItemText(g_hDlg,IDC_EFACE,szOut); + sprintf(szOut,"%i",(int)g_pcAsset->pcScene->mNumMaterials); + SetDlgItemText(g_hDlg,IDC_EMAT,szOut); + + ScaleAsset(); + + g_sCamera.vPos = aiVector3D(0.0f,0.0f,-10.0f); + g_sCamera.vLookAt = aiVector3D(0.0f,0.0f,1.0f); + g_sCamera.vUp = aiVector3D(0.0f,1.0f,0.0f); + g_sCamera.vRight = aiVector3D(0.0f,1.0f,0.0f); + + return CreateAssetData(); + } + + +//------------------------------------------------------------------------------- +//------------------------------------------------------------------------------- +int DeleteAsset(void) + { + if (!g_pcAsset)return 0; + + Render(); + + DeleteAssetData(); + for (unsigned int i = 0; i < g_pcAsset->pcScene->mNumMeshes;++i) + { + delete g_pcAsset->apcMeshes[i]; + } + aiReleaseImport(g_pcAsset->pcScene); + delete[] g_pcAsset->apcMeshes; + delete g_pcAsset; + g_pcAsset = NULL; + + SetDlgItemText(g_hDlg,IDC_EVERT,"0"); + SetDlgItemText(g_hDlg,IDC_EFACE,"0"); + SetDlgItemText(g_hDlg,IDC_EMAT,"0"); + return 1; + } + + +//------------------------------------------------------------------------------- +//------------------------------------------------------------------------------- +int CalculateBounds(aiNode* piNode, aiVector3D* p_avOut, + const aiMatrix4x4& piMatrix) + { + aiMatrix4x4 mTemp = piNode->mTransformation; + mTemp.Transpose(); + aiMatrix4x4 aiMe = mTemp * piMatrix; + + for (unsigned int i = 0; i < piNode->mNumMeshes;++i) + { + for( unsigned int a = 0; a < g_pcAsset->pcScene->mMeshes[ + piNode->mMeshes[i]]->mNumVertices;++a) + { + aiVector3D pc =g_pcAsset->pcScene->mMeshes[ + piNode->mMeshes[i]]->mVertices[a]; + + aiVector3D pc1; + D3DXVec3TransformCoord((D3DXVECTOR3*)&pc1,(D3DXVECTOR3*)&pc, + (D3DXMATRIX*)&aiMe); + + p_avOut[0].x = std::min( p_avOut[0].x, pc1.x); + p_avOut[0].y = std::min( p_avOut[0].y, pc1.y); + p_avOut[0].z = std::min( p_avOut[0].z, pc1.z); + p_avOut[1].x = std::max( p_avOut[1].x, pc1.x); + p_avOut[1].y = std::max( p_avOut[1].y, pc1.y); + p_avOut[1].z = std::max( p_avOut[1].z, pc1.z); + } + } + for (unsigned int i = 0; i < piNode->mNumChildren;++i) + { + CalculateBounds( piNode->mChildren[i], p_avOut, aiMe ); + } + return 1; + } + + +//------------------------------------------------------------------------------- +//------------------------------------------------------------------------------- +int ScaleAsset(void) + { + aiVector3D aiVecs[2] = {aiVector3D( 1e10f, 1e10f, 1e10f), + aiVector3D( -1e10f, -1e10f, -1e10f) }; + + if (g_pcAsset->pcScene->mRootNode) + { + aiMatrix4x4 m; + CalculateBounds(g_pcAsset->pcScene->mRootNode,aiVecs,m); + } + + aiVector3D vDelta = aiVecs[1]-aiVecs[0]; + aiVector3D vHalf = aiVecs[0] + (vDelta / 2.0f); + float fScale = 10.0f / vDelta.Length(); + + g_mWorld = aiMatrix4x4( + 1.0f,0.0f,0.0f,0.0f, + 0.0f,1.0f,0.0f,0.0f, + 0.0f,0.0f,1.0f,0.0f, + -vHalf.x,-vHalf.y,-vHalf.z,1.0f) * + aiMatrix4x4( + fScale,0.0f,0.0f,0.0f, + 0.0f,fScale,0.0f,0.0f, + 0.0f,0.0f,fScale,0.0f, + 0.0f,0.0f,0.0f,1.0f); +#if 0 + // now handle the fact that the asset might have its + // own transformation matrix (handle scaling and translation) + if (NULL != g_pcAsset->pcScene->mRootNode) + { + if (0.0f != g_pcAsset->pcScene->mRootNode->mTransformation[0][0] && + 0.0f != g_pcAsset->pcScene->mRootNode->mTransformation[1][1] && + 0.0f != g_pcAsset->pcScene->mRootNode->mTransformation[2][2] && + 0.0f != g_pcAsset->pcScene->mRootNode->mTransformation[3][3]) + { + g_mWorld[0][0] /= g_pcAsset->pcScene->mRootNode->mTransformation[0][0]; + g_mWorld[1][1] /= g_pcAsset->pcScene->mRootNode->mTransformation[1][1]; + g_mWorld[2][2] /= g_pcAsset->pcScene->mRootNode->mTransformation[2][2]; + g_mWorld[3][3] /= g_pcAsset->pcScene->mRootNode->mTransformation[3][3]; + } + g_mWorld[3][0] -= g_pcAsset->pcScene->mRootNode->mTransformation[3][0]; + g_mWorld[3][1] -= g_pcAsset->pcScene->mRootNode->mTransformation[3][1]; + g_mWorld[3][2] -= g_pcAsset->pcScene->mRootNode->mTransformation[3][2]; + + aiMatrix4x4 m; + if ( 0 == memcmp(&m,&g_pcAsset->pcScene->mRootNode->mTransformation,sizeof(aiMatrix4x4)) && + 1 <= g_pcAsset->pcScene->mRootNode->mNumChildren) + { + if (0.0f != g_pcAsset->pcScene->mRootNode->mChildren[0]->mTransformation[0][0] && + 0.0f != g_pcAsset->pcScene->mRootNode->mChildren[0]->mTransformation[1][1] && + 0.0f != g_pcAsset->pcScene->mRootNode->mChildren[0]->mTransformation[2][2] && + 0.0f != g_pcAsset->pcScene->mRootNode->mChildren[0]->mTransformation[3][3]) + { + g_mWorld[0][0] /= g_pcAsset->pcScene->mRootNode->mChildren[0]->mTransformation[0][0]; + g_mWorld[1][1] /= g_pcAsset->pcScene->mRootNode->mChildren[0]->mTransformation[1][1]; + g_mWorld[2][2] /= g_pcAsset->pcScene->mRootNode->mChildren[0]->mTransformation[2][2]; + g_mWorld[3][3] /= g_pcAsset->pcScene->mRootNode->mChildren[0]->mTransformation[3][3]; + } + g_mWorld[3][0] -= g_pcAsset->pcScene->mRootNode->mChildren[0]->mTransformation[3][0]; + g_mWorld[3][1] -= g_pcAsset->pcScene->mRootNode->mChildren[0]->mTransformation[3][1]; + g_mWorld[3][2] -= g_pcAsset->pcScene->mRootNode->mChildren[0]->mTransformation[3][2]; + } + } +#endif + return 1; + } + +//------------------------------------------------------------------------------- +//------------------------------------------------------------------------------- +int GenerateNormalsAsLineList(AssetHelper::MeshHelper* pcMesh,const aiMesh* pcSource) + { + if (!pcSource->mNormals)return 0; + + // create vertex buffer + if(FAILED( g_piDevice->CreateVertexBuffer(sizeof(AssetHelper::LineVertex) * + pcSource->mNumVertices * 2, + D3DUSAGE_WRITEONLY, + AssetHelper::LineVertex::GetFVF(), + D3DPOOL_DEFAULT, &pcMesh->piVBNormals,NULL))) + { + MessageBox(g_hDlg,"Failed to create vertex buffer for the normal list", + "ASSIMP Viewer Utility",MB_OK); + return 2; + } + + // now fill the vertex buffer + AssetHelper::LineVertex* pbData2; + pcMesh->piVBNormals->Lock(0,0,(void**)&pbData2,0); + for (unsigned int x = 0; x < pcSource->mNumVertices;++x) + { + pbData2->vPosition = pcSource->mVertices[x]; + + ++pbData2; + + aiVector3D vNormal = pcSource->mNormals[x]; + vNormal.Normalize(); + + vNormal.x /= g_mWorld.a1*4; + vNormal.y /= g_mWorld.b2*4; + vNormal.z /= g_mWorld.c3*4; + + pbData2->vPosition = pcSource->mVertices[x] + vNormal; + + ++pbData2; + } + pcMesh->piVBNormals->Unlock(); + return 1; + } + + +//------------------------------------------------------------------------------- +// Fill the UI combobox with a list of all supported animations +// +// The animations are added in order +//------------------------------------------------------------------------------- +int FillAnimList(void) + { + // clear the combo box + SendDlgItemMessage(g_hDlg,IDC_COMBO1,CB_RESETCONTENT,0,0); + + if (0 == g_pcAsset->pcScene->mNumAnimations) + { + // disable all UI components related to animations + EnableWindow(GetDlgItem(g_hDlg,IDC_PLAYANIM),FALSE); + EnableWindow(GetDlgItem(g_hDlg,IDC_SPEED),FALSE); + EnableWindow(GetDlgItem(g_hDlg,IDC_PINORDER),FALSE); + + EnableWindow(GetDlgItem(g_hDlg,IDC_SSPEED),FALSE); + EnableWindow(GetDlgItem(g_hDlg,IDC_SANIMGB),FALSE); + EnableWindow(GetDlgItem(g_hDlg,IDC_SANIM),FALSE); + EnableWindow(GetDlgItem(g_hDlg,IDC_COMBO1),FALSE); + } + else + { + // reenable all animation components if they have been + // disabled for a previous mesh + EnableWindow(GetDlgItem(g_hDlg,IDC_PLAYANIM),TRUE); + EnableWindow(GetDlgItem(g_hDlg,IDC_SPEED),TRUE); + EnableWindow(GetDlgItem(g_hDlg,IDC_PINORDER),TRUE); + + EnableWindow(GetDlgItem(g_hDlg,IDC_SSPEED),TRUE); + EnableWindow(GetDlgItem(g_hDlg,IDC_SANIMGB),TRUE); + EnableWindow(GetDlgItem(g_hDlg,IDC_SANIM),TRUE); + EnableWindow(GetDlgItem(g_hDlg,IDC_COMBO1),TRUE); + + // now fill in all animation names + for (unsigned int i = 0; i < g_pcAsset->pcScene->mNumAnimations;++i) + { + SendDlgItemMessage(g_hDlg,IDC_COMBO1,CB_ADDSTRING,0, + ( LPARAM ) g_pcAsset->pcScene->mAnimations[i]->mName.data); + } + } + return 1; + } + +//------------------------------------------------------------------------------- +//------------------------------------------------------------------------------- +int CreateAssetData(void) + { + if (!g_pcAsset)return 0; + + g_iShaderCount = 0; + + for (unsigned int i = 0; i < g_pcAsset->pcScene->mNumMeshes;++i) + { + // create vertex buffer + if(FAILED( g_piDevice->CreateVertexBuffer(sizeof(AssetHelper::Vertex) * + g_pcAsset->pcScene->mMeshes[i]->mNumVertices, + D3DUSAGE_WRITEONLY, + AssetHelper::Vertex::GetFVF(), + D3DPOOL_DEFAULT, &g_pcAsset->apcMeshes[i]->piVB,NULL))) + { + MessageBox(g_hDlg,"Failed to create vertex buffer", + "ASSIMP Viewer Utility",MB_OK); + return 2; + } + // create index buffer + if(FAILED( g_piDevice->CreateIndexBuffer( 4 * + g_pcAsset->pcScene->mMeshes[i]->mNumFaces * 3, + D3DUSAGE_WRITEONLY, + D3DFMT_INDEX32, + D3DPOOL_DEFAULT, &g_pcAsset->apcMeshes[i]->piIB,NULL))) + { + MessageBox(g_hDlg,"Failed to create index buffer", + "ASSIMP Viewer Utility",MB_OK); + return 2; + } + + // now fill the index buffer + unsigned int* pbData; + g_pcAsset->apcMeshes[i]->piIB->Lock(0,0,(void**)&pbData,0); + for (unsigned int x = 0; x < g_pcAsset->pcScene->mMeshes[i]->mNumFaces;++x) + { + for (unsigned int a = 0; a < 3;++a) + { + *pbData++ = g_pcAsset->pcScene->mMeshes[i]->mFaces[x].mIndices[a]; + } + } + g_pcAsset->apcMeshes[i]->piIB->Unlock(); + + // now fill the vertex buffer + AssetHelper::Vertex* pbData2; + g_pcAsset->apcMeshes[i]->piVB->Lock(0,0,(void**)&pbData2,0); + for (unsigned int x = 0; x < g_pcAsset->pcScene->mMeshes[i]->mNumVertices;++x) + { + pbData2->vPosition = g_pcAsset->pcScene->mMeshes[i]->mVertices[x]; + + if (NULL == g_pcAsset->pcScene->mMeshes[i]->mNormals) + pbData2->vNormal = aiVector3D(0.0f,0.0f,0.0f); + else pbData2->vNormal = g_pcAsset->pcScene->mMeshes[i]->mNormals[x]; + + if (NULL == g_pcAsset->pcScene->mMeshes[i]->mTangents) + { + pbData2->vTangent = aiVector3D(0.0f,0.0f,0.0f); + pbData2->vBitangent = aiVector3D(0.0f,0.0f,0.0f); + } + else + { + pbData2->vTangent = g_pcAsset->pcScene->mMeshes[i]->mTangents[x]; + pbData2->vBitangent = g_pcAsset->pcScene->mMeshes[i]->mBitangents[x]; + } + + if (g_pcAsset->pcScene->mMeshes[i]->HasVertexColors( 0)) + { + pbData2->dColorDiffuse = D3DCOLOR_ARGB( + ((unsigned char)std::max( std::min( g_pcAsset->pcScene-> + mMeshes[i]->mColors[0][x].a * 255.0f, 255.0f),0.0f)), + ((unsigned char)std::max( std::min( g_pcAsset->pcScene-> + mMeshes[i]->mColors[0][x].r * 255.0f, 255.0f),0.0f)), + ((unsigned char)std::max( std::min( g_pcAsset->pcScene-> + mMeshes[i]->mColors[0][x].g * 255.0f, 255.0f),0.0f)), + ((unsigned char)std::max( std::min( g_pcAsset->pcScene-> + mMeshes[i]->mColors[0][x].b * 255.0f, 255.0f),0.0f))); + } + else pbData2->dColorDiffuse = D3DCOLOR_ARGB(0xFF,0,0,0); + + // ignore a third texture coordinate component + if (g_pcAsset->pcScene->mMeshes[i]->HasTextureCoords( 0)) + { + pbData2->vTextureUV = aiVector2D( + g_pcAsset->pcScene->mMeshes[i]->mTextureCoords[0][x].x, + g_pcAsset->pcScene->mMeshes[i]->mTextureCoords[0][x].y); + } + else pbData2->vTextureUV = aiVector2D(0.0f,0.0f); + ++pbData2; + } + g_pcAsset->apcMeshes[i]->piVB->Unlock(); + + // now generate the second vertex buffer, holding all normals + GenerateNormalsAsLineList(g_pcAsset->apcMeshes[i],g_pcAsset->pcScene->mMeshes[i]); + + // create the material for the mesh + CreateMaterial(g_pcAsset->apcMeshes[i],g_pcAsset->pcScene->mMeshes[i]); + } + CLogDisplay::Instance().AddEntry("[OK] The asset has been loaded successfully"); + + + // now get the number of unique shaders generated for the asset + // (even if the environment changes this number won't change) + char szTemp[32]; + sprintf(szTemp,"%i", g_iShaderCount); + SetDlgItemText(g_hDlg,IDC_ESHADER,szTemp); + + return FillAnimList(); + } + +//------------------------------------------------------------------------------- +//------------------------------------------------------------------------------- +int DeleteAssetData(void) + { + if (!g_pcAsset)return 0; + + // TODO: Move this to a proper destructor + for (unsigned int i = 0; i < g_pcAsset->pcScene->mNumMeshes;++i) + { + if(g_pcAsset->apcMeshes[i]->piVB) + { + g_pcAsset->apcMeshes[i]->piVB->Release(); + g_pcAsset->apcMeshes[i]->piVB = NULL; + } + if(g_pcAsset->apcMeshes[i]->piVBNormals) + { + g_pcAsset->apcMeshes[i]->piVBNormals->Release(); + g_pcAsset->apcMeshes[i]->piVBNormals = NULL; + } + if(g_pcAsset->apcMeshes[i]->piIB) + { + g_pcAsset->apcMeshes[i]->piIB->Release(); + g_pcAsset->apcMeshes[i]->piIB = NULL; + } + if(g_pcAsset->apcMeshes[i]->piEffect) + { + g_pcAsset->apcMeshes[i]->piEffect->Release(); + g_pcAsset->apcMeshes[i]->piEffect = NULL; + } + if(g_pcAsset->apcMeshes[i]->piDiffuseTexture) + { + g_pcAsset->apcMeshes[i]->piDiffuseTexture->Release(); + g_pcAsset->apcMeshes[i]->piDiffuseTexture = NULL; + } + if(g_pcAsset->apcMeshes[i]->piNormalTexture) + { + g_pcAsset->apcMeshes[i]->piNormalTexture->Release(); + g_pcAsset->apcMeshes[i]->piNormalTexture = NULL; + } + if(g_pcAsset->apcMeshes[i]->piSpecularTexture) + { + g_pcAsset->apcMeshes[i]->piSpecularTexture->Release(); + g_pcAsset->apcMeshes[i]->piSpecularTexture = NULL; + } + if(g_pcAsset->apcMeshes[i]->piAmbientTexture) + { + g_pcAsset->apcMeshes[i]->piAmbientTexture->Release(); + g_pcAsset->apcMeshes[i]->piAmbientTexture = NULL; + } + if(g_pcAsset->apcMeshes[i]->piEmissiveTexture) + { + g_pcAsset->apcMeshes[i]->piEmissiveTexture->Release(); + g_pcAsset->apcMeshes[i]->piEmissiveTexture = NULL; + } + } + return 1; + } + + +//------------------------------------------------------------------------------- +//------------------------------------------------------------------------------- +int SetupFPSView() + { + if (!g_bFPSView) + { + g_sCamera.vPos = aiVector3D(0.0f,0.0f,g_fWheelPos); + g_sCamera.vLookAt = aiVector3D(0.0f,0.0f,1.0f); + g_sCamera.vUp = aiVector3D(0.0f,1.0f,0.0f); + g_sCamera.vRight = aiVector3D(0.0f,1.0f,0.0f); + } + else + { + g_fWheelPos = g_sCamera.vPos.z; + g_sCamera.vPos = aiVector3D(0.0f,0.0f,-10.0f); + g_sCamera.vLookAt = aiVector3D(0.0f,0.0f,1.0f); + g_sCamera.vUp = aiVector3D(0.0f,1.0f,0.0f); + g_sCamera.vRight = aiVector3D(0.0f,1.0f,0.0f); + } + return 1; + } + +//------------------------------------------------------------------------------- +//------------------------------------------------------------------------------- +int InitD3D(void) + { + if (NULL == g_piD3D) + { + g_piD3D = Direct3DCreate9(D3D_SDK_VERSION); + if (NULL == g_piD3D)return 0; + } + return 1; + } + + +//------------------------------------------------------------------------------- +//------------------------------------------------------------------------------- +int ShutdownD3D(void) + { + ShutdownDevice(); + if (NULL != g_piD3D) + { + g_piD3D->Release(); + g_piD3D = NULL; + } + return 1; + } + + +//------------------------------------------------------------------------------- +//------------------------------------------------------------------------------- +int ShutdownDevice(void) + { + if (NULL != g_piDevice) + { + g_piDevice->Release(); + g_piDevice = NULL; + } + if (NULL != g_piDefaultEffect) + { + g_piDefaultEffect->Release(); + g_piDefaultEffect = NULL; + } + if (NULL != g_piNormalsEffect) + { + g_piNormalsEffect->Release(); + g_piNormalsEffect = NULL; + } + if (NULL != g_piPassThroughEffect) + { + g_piPassThroughEffect->Release(); + g_piPassThroughEffect = NULL; + } + if (NULL != g_pcTexture) + { + g_pcTexture->Release(); + g_pcTexture = NULL; + } + delete[] g_szImageMask; + g_szImageMask = NULL; + CBackgroundPainter::Instance().ReleaseNativeResource(); + CLogDisplay::Instance().ReleaseNativeResource(); + return 1; + } + + +//------------------------------------------------------------------------------- +//------------------------------------------------------------------------------- +int CreateHUDTexture() + { + // lock the memory resource ourselves + HRSRC res = FindResource(NULL,MAKEINTRESOURCE(IDR_HUD),RT_RCDATA); + HGLOBAL hg = LoadResource(NULL,res); + void* pData = LockResource(hg); + + if(FAILED(D3DXCreateTextureFromFileInMemoryEx(g_piDevice, + pData,SizeofResource(NULL,res), + D3DX_DEFAULT_NONPOW2, + D3DX_DEFAULT_NONPOW2, + 1, + 0, + D3DFMT_A8R8G8B8, + D3DPOOL_MANAGED, + D3DX_DEFAULT, + D3DX_DEFAULT, + 0, + NULL, + NULL, + &g_pcTexture))) + { + CLogDisplay::Instance().AddEntry("[ERROR] Unable to load HUD texture", + D3DCOLOR_ARGB(0xFF,0xFF,0,0)); + + g_pcTexture = NULL; + g_szImageMask = NULL; + + UnlockResource(hg); + FreeResource(hg); + return 0; + } + + UnlockResource(hg); + FreeResource(hg); + + D3DSURFACE_DESC sDesc; + g_pcTexture->GetLevelDesc(0,&sDesc); + + + // lock the memory resource ourselves + res = FindResource(NULL,MAKEINTRESOURCE(IDR_HUDMASK),RT_RCDATA); + hg = LoadResource(NULL,res); + pData = LockResource(hg); + + IDirect3DTexture9* pcTex; + if(FAILED(D3DXCreateTextureFromFileInMemoryEx(g_piDevice, + pData,SizeofResource(NULL,res), + sDesc.Width, + sDesc.Height, + 1, + 0, + D3DFMT_L8, + D3DPOOL_MANAGED, // unnecessary + D3DX_DEFAULT, + D3DX_DEFAULT, + 0, + NULL, + NULL, + &pcTex))) + { + CLogDisplay::Instance().AddEntry("[ERROR] Unable to load HUD mask texture", + D3DCOLOR_ARGB(0xFF,0xFF,0,0)); + g_szImageMask = NULL; + + UnlockResource(hg); + FreeResource(hg); + return 0; + } + + UnlockResource(hg); + FreeResource(hg); + + // lock the texture and copy it to get a pointer + D3DLOCKED_RECT sRect; + pcTex->LockRect(0,&sRect,NULL,D3DLOCK_READONLY); + + unsigned char* szOut = new unsigned char[sDesc.Width * sDesc.Height]; + unsigned char* _szOut = szOut; + + unsigned char* szCur = (unsigned char*) sRect.pBits; + for (unsigned int y = 0; y < sDesc.Height;++y) + { + memcpy(_szOut,szCur,sDesc.Width); + + szCur += sRect.Pitch; + _szOut += sDesc.Width; + } + pcTex->UnlockRect(0); + pcTex->Release(); + + g_szImageMask = szOut; + return 1; + } + +//------------------------------------------------------------------------------- +//------------------------------------------------------------------------------- +int CreateDevice (bool p_bMultiSample,bool p_bSuperSample,bool bHW /*= true*/) + { + D3DDEVTYPE eType = bHW ? D3DDEVTYPE_HAL : D3DDEVTYPE_REF; + + RECT sRect; + GetWindowRect(GetDlgItem(g_hDlg,IDC_RT),&sRect); + sRect.right -= sRect.left; + sRect.bottom -= sRect.top; + + D3DPRESENT_PARAMETERS sParams; + memset(&sParams,0,sizeof(D3DPRESENT_PARAMETERS)); + + D3DDISPLAYMODE sMode; + g_piD3D->GetAdapterDisplayMode(0,&sMode); + + sParams.Windowed = TRUE; + sParams.hDeviceWindow = GetDlgItem( g_hDlg, IDC_RT ); + sParams.EnableAutoDepthStencil = TRUE; + sParams.PresentationInterval = D3DPRESENT_INTERVAL_ONE; + sParams.BackBufferWidth = (UINT)sRect.right; + sParams.BackBufferHeight = (UINT)sRect.bottom; + sParams.AutoDepthStencilFormat = D3DFMT_D24X8; + sParams.SwapEffect = D3DSWAPEFFECT_DISCARD; + + // find the highest multisample type available on this device + D3DMULTISAMPLE_TYPE sMS = D3DMULTISAMPLE_2_SAMPLES; + D3DMULTISAMPLE_TYPE sMSOut = D3DMULTISAMPLE_NONE; + DWORD dwQuality = 0; + if (p_bMultiSample) + { + while ((D3DMULTISAMPLE_TYPE)(D3DMULTISAMPLE_16_SAMPLES + 1) != + (sMS = (D3DMULTISAMPLE_TYPE)(sMS + 1))) + { + if(SUCCEEDED( g_piD3D->CheckDeviceMultiSampleType(0,eType, + sMode.Format,TRUE,sMS,&dwQuality))) + { + sMSOut = sMS; + } + } + if (0 != dwQuality)dwQuality -= 1; + + + sParams.MultiSampleQuality = dwQuality; + sParams.MultiSampleType = sMSOut; + } + + if(FAILED(g_piD3D->CreateDevice(0,eType, + g_hDlg,D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED,&sParams,&g_piDevice))) + { + if(FAILED(g_piD3D->CreateDevice(0,eType, + g_hDlg,D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED,&sParams,&g_piDevice))) + { + // if hardware fails use software rendering instead + if (bHW)return CreateDevice(p_bMultiSample,p_bSuperSample,false); + return 0; + } + } + g_piDevice->SetFVF(AssetHelper::Vertex::GetFVF()); + + ID3DXBuffer* piBuffer = NULL; + if(FAILED( D3DXCreateEffect(g_piDevice, + g_szDefaultShader.c_str(), + (UINT)g_szDefaultShader.length(), + NULL, + NULL, + D3DXSHADER_USE_LEGACY_D3DX9_31_DLL, + NULL, + &g_piDefaultEffect,&piBuffer))) + { + if( piBuffer) + { + MessageBox(g_hDlg,(LPCSTR)piBuffer->GetBufferPointer(),"HLSL",MB_OK); + piBuffer->Release(); + } + return 0; + } + if( piBuffer) + { + piBuffer->Release(); + piBuffer = NULL; + } + + if(FAILED( D3DXCreateEffect(g_piDevice, + g_szPassThroughShader.c_str(),(UINT)g_szPassThroughShader.length(), + NULL,NULL,D3DXSHADER_USE_LEGACY_D3DX9_31_DLL,NULL,&g_piPassThroughEffect,&piBuffer))) + { + if( piBuffer) + { + MessageBox(g_hDlg,(LPCSTR + )piBuffer->GetBufferPointer(),"HLSL",MB_OK); + piBuffer->Release(); + } + return 0; + } + if( piBuffer) + { + piBuffer->Release(); + piBuffer = NULL; + } + if(FAILED( D3DXCreateEffect(g_piDevice, + g_szNormalsShader.c_str(),(UINT)g_szNormalsShader.length(), + NULL,NULL,D3DXSHADER_USE_LEGACY_D3DX9_31_DLL,NULL,&g_piNormalsEffect, &piBuffer))) + { + if( piBuffer) + { + MessageBox(g_hDlg,(LPCSTR + )piBuffer->GetBufferPointer(),"HLSL",MB_OK); + piBuffer->Release(); + } + return 0; + } + if( piBuffer) + { + piBuffer->Release(); + piBuffer = NULL; + } + + g_piDevice->GetDeviceCaps(&g_sCaps); + if(g_sCaps.PixelShaderVersion < D3DPS_VERSION(3,0)) + { + EnableWindow(GetDlgItem(g_hDlg,IDC_LOWQUALITY),FALSE); + } + + // create the texture for the HUD + CreateHUDTexture(); + CBackgroundPainter::Instance().RecreateNativeResource(); + CLogDisplay::Instance().RecreateNativeResource(); + + g_piPassThroughEffect->SetTexture("TEXTURE_2D",g_pcTexture); + return 1; + } + + +//------------------------------------------------------------------------------- +//------------------------------------------------------------------------------- +int CreateDevice (void) + { + return CreateDevice(g_sOptions.bMultiSample, + g_sOptions.bSuperSample); + } + + +//------------------------------------------------------------------------------- +//------------------------------------------------------------------------------- +int GetProjectionMatrix (aiMatrix4x4& p_mOut) + { + const float fFarPlane = 100.0f; + const float fNearPlane = 0.1f; + const float fFOV = (float)(45.0 * 0.0174532925); + + const float s = 1.0f / tanf(fFOV * 0.5f); + const float Q = fFarPlane / (fFarPlane - fNearPlane); + + RECT sRect; + GetWindowRect(GetDlgItem(g_hDlg,IDC_RT),&sRect); + sRect.right -= sRect.left; + sRect.bottom -= sRect.top; + const float fAspect = (float)sRect.right / (float)sRect.bottom; + + p_mOut = aiMatrix4x4( + s / fAspect, 0.0f, 0.0f, 0.0f, + 0.0f, s, 0.0f, 0.0f, + 0.0f, 0.0f, Q, 1.0f, + 0.0f, 0.0f, -Q * fNearPlane, 0.0f); + return 1; + } + + +//------------------------------------------------------------------------------- +//------------------------------------------------------------------------------- +aiVector3D GetCameraMatrix (aiMatrix4x4& p_mOut) + { + D3DXMATRIX view; + D3DXMatrixIdentity( &view ); + + D3DXVec3Normalize( (D3DXVECTOR3*)&g_sCamera.vLookAt, (D3DXVECTOR3*)&g_sCamera.vLookAt ); + D3DXVec3Cross( (D3DXVECTOR3*)&g_sCamera.vRight, (D3DXVECTOR3*)&g_sCamera.vUp, (D3DXVECTOR3*)&g_sCamera.vLookAt ); + D3DXVec3Normalize( (D3DXVECTOR3*)&g_sCamera.vRight, (D3DXVECTOR3*)&g_sCamera.vRight ); + D3DXVec3Cross( (D3DXVECTOR3*)&g_sCamera.vUp, (D3DXVECTOR3*)&g_sCamera.vLookAt, (D3DXVECTOR3*)&g_sCamera.vRight ); + D3DXVec3Normalize( (D3DXVECTOR3*)&g_sCamera.vUp, (D3DXVECTOR3*)&g_sCamera.vUp ); + + view._11 = g_sCamera.vRight.x; + view._12 = g_sCamera.vUp.x; + view._13 = g_sCamera.vLookAt.x; + view._14 = 0.0f; + + view._21 = g_sCamera.vRight.y; + view._22 = g_sCamera.vUp.y; + view._23 = g_sCamera.vLookAt.y; + view._24 = 0.0f; + + view._31 = g_sCamera.vRight.z; + view._32 = g_sCamera.vUp.z; + view._33 = g_sCamera.vLookAt.z; + view._34 = 0.0f; + + view._41 = -D3DXVec3Dot( (D3DXVECTOR3*)&g_sCamera.vPos, (D3DXVECTOR3*)&g_sCamera.vRight ); + view._42 = -D3DXVec3Dot( (D3DXVECTOR3*)&g_sCamera.vPos, (D3DXVECTOR3*)&g_sCamera.vUp ); + view._43 = -D3DXVec3Dot( (D3DXVECTOR3*)&g_sCamera.vPos, (D3DXVECTOR3*)&g_sCamera.vLookAt ); + view._44 = 1.0f; + + memcpy(&p_mOut,&view,sizeof(aiMatrix4x4)); + + return g_sCamera.vPos; + } + +//------------------------------------------------------------------------------- +//------------------------------------------------------------------------------- +int SetupMaterial (AssetHelper::MeshHelper* pcMesh, + const aiMatrix4x4& pcProj, + const aiMatrix4x4& aiMe, + const aiMatrix4x4& pcCam, + const aiVector3D& vPos) + { + if (!pcMesh->piEffect)return 0; + + ID3DXEffect* piEnd = pcMesh->piEffect; + + piEnd->SetMatrix("WorldViewProjection", + (const D3DXMATRIX*)&pcProj); + + piEnd->SetMatrix("World",(const D3DXMATRIX*)&aiMe); + piEnd->SetMatrix("WorldInverseTranspose", + (const D3DXMATRIX*)&pcCam); + + D3DXVECTOR4 apcVec[5]; + memset(apcVec,0,sizeof(apcVec)); + apcVec[0].x = g_avLightDirs[0].x; + apcVec[0].y = g_avLightDirs[0].y; + apcVec[0].z = g_avLightDirs[0].z; + apcVec[1].x = g_avLightDirs[0].x * -1.0f; + apcVec[1].y = g_avLightDirs[0].y * -1.0f; + apcVec[1].z = g_avLightDirs[0].z * -1.0f; + D3DXVec4Normalize(&apcVec[0],&apcVec[0]); + D3DXVec4Normalize(&apcVec[1],&apcVec[1]); + piEnd->SetVectorArray("afLightDir",apcVec,5); + + if(g_sOptions.b3Lights) + { + apcVec[0].x = 1.0f; + apcVec[0].y = 1.0f; + apcVec[0].z = 1.0f; + apcVec[0].w = 1.0f; + + apcVec[1].x = 0.1f; + apcVec[1].y = 1.0f; + apcVec[1].z = 0.1f; + apcVec[1].w = 1.0f; + } + else + { + apcVec[0].x = 1.0f; + apcVec[0].y = 1.0f; + apcVec[0].z = 1.0f; + apcVec[0].w = 1.0f; + + apcVec[1].x = 0.0f; + apcVec[1].y = 0.0f; + apcVec[1].z = 0.0f; + apcVec[1].w = 0.0f; + } + apcVec[0] *= g_fLightIntensity; + apcVec[1] *= g_fLightIntensity; + piEnd->SetVectorArray("afLightColor",apcVec,5); + + if(g_sOptions.b3Lights) + { + apcVec[0].x = 0.05f; + apcVec[0].y = 0.05f; + apcVec[0].z = 0.05f; + apcVec[0].w = 1.0f; + + apcVec[1].x = 0.05f; + apcVec[1].y = 0.05f; + apcVec[1].z = 0.05f; + apcVec[1].w = 1.0f; + } + else + { + apcVec[0].x = 0.05f; + apcVec[0].y = 0.05f; + apcVec[0].z = 0.05f; + apcVec[0].w = 1.0f; + + apcVec[1].x = 0.0f; + apcVec[1].y = 0.0f; + apcVec[1].z = 0.0f; + apcVec[1].w = 0.0f; + } + apcVec[0] *= g_fLightIntensity; + apcVec[1] *= g_fLightIntensity; + piEnd->SetVectorArray("afLightColorAmbient",apcVec,5); + + + apcVec[0].x = vPos.x; + apcVec[0].y = vPos.y; + apcVec[0].z = vPos.z; + piEnd->SetVector( "vCameraPos",&apcVec[0]); + + if (pcMesh->bSharedFX) + { + // now commit all constants to the shader + if (1.0f != pcMesh->fOpacity) + pcMesh->piEffect->SetFloat("TRANSPARENCY",pcMesh->fOpacity); + if (pcMesh->eShadingMode != aiShadingMode_Gouraud) + pcMesh->piEffect->SetFloat("SPECULARITY",pcMesh->fShininess); + + pcMesh->piEffect->SetVector("DIFFUSE_COLOR",&pcMesh->vDiffuseColor); + pcMesh->piEffect->SetVector("SPECULAR_COLOR",&pcMesh->vSpecularColor); + pcMesh->piEffect->SetVector("AMBIENT_COLOR",&pcMesh->vAmbientColor); + pcMesh->piEffect->SetVector("EMISSIVE_COLOR",&pcMesh->vEmissiveColor); + + if (pcMesh->piOpacityTexture) + pcMesh->piEffect->SetTexture("OPACITY_TEXTURE",pcMesh->piOpacityTexture); + if (pcMesh->piDiffuseTexture) + pcMesh->piEffect->SetTexture("DIFFUSE_TEXTURE",pcMesh->piDiffuseTexture); + if (pcMesh->piSpecularTexture) + pcMesh->piEffect->SetTexture("SPECULAR_TEXTURE",pcMesh->piSpecularTexture); + if (pcMesh->piAmbientTexture) + pcMesh->piEffect->SetTexture("AMBIENT_TEXTURE",pcMesh->piAmbientTexture); + if (pcMesh->piEmissiveTexture) + pcMesh->piEffect->SetTexture("EMISSIVE_TEXTURE",pcMesh->piEmissiveTexture); + if (pcMesh->piNormalTexture) + pcMesh->piEffect->SetTexture("NORMAL_TEXTURE",pcMesh->piNormalTexture); + + if (CBackgroundPainter::TEXTURE_CUBE == CBackgroundPainter::Instance().GetMode()) + { + piEnd->SetTexture("lw_tex_envmap",CBackgroundPainter::Instance().GetTexture()); + } + } + + if (g_sCaps.PixelShaderVersion < D3DPS_VERSION(3,0) || g_sOptions.bLowQuality) + { + if (g_sOptions.b3Lights) + piEnd->SetTechnique("MaterialFXSpecular_PS20_D2"); + else piEnd->SetTechnique("MaterialFXSpecular_PS20_D1"); + } + else + { + if (g_sOptions.b3Lights) + piEnd->SetTechnique("MaterialFXSpecular_D2"); + else piEnd->SetTechnique("MaterialFXSpecular_D1"); + } + + UINT dwPasses = 0; + piEnd->Begin(&dwPasses,0); + piEnd->BeginPass(0); + return 1; + } + + +//------------------------------------------------------------------------------- +//------------------------------------------------------------------------------- +int EndMaterial (AssetHelper::MeshHelper* pcMesh) + { + if (!pcMesh->piEffect)return 0; + + pcMesh->piEffect->EndPass(); + pcMesh->piEffect->End(); + + return 1; + } + +//------------------------------------------------------------------------------- +//------------------------------------------------------------------------------- +int RenderNode (aiNode* piNode,const aiMatrix4x4& piMatrix, bool bAlpha = false) + { + aiMatrix4x4 mTemp = piNode->mTransformation; + mTemp.Transpose(); + aiMatrix4x4 aiMe = mTemp * piMatrix; + + aiMatrix4x4 pcProj; + GetProjectionMatrix(pcProj); + + aiMatrix4x4 pcCam; + aiVector3D vPos = GetCameraMatrix(pcCam); + pcProj = (aiMe * pcCam) * pcProj; + + pcCam = aiMe; + pcCam.Inverse().Transpose(); + + // VERY UNOPTIMIZED, much stuff is redundant. Who cares? + if (!g_sOptions.bRenderMats && !bAlpha) + { + // this is very similar to the code in SetupMaterial() + ID3DXEffect* piEnd = g_piDefaultEffect; + + piEnd->SetMatrix("WorldViewProjection", + (const D3DXMATRIX*)&pcProj); + + piEnd->SetMatrix("World",(const D3DXMATRIX*)&aiMe); + piEnd->SetMatrix("WorldInverseTranspose", + (const D3DXMATRIX*)&pcCam); + + if ( CBackgroundPainter::TEXTURE_CUBE == CBackgroundPainter::Instance().GetMode()) + { + pcCam = pcCam * pcProj; + piEnd->SetMatrix("ViewProj",(const D3DXMATRIX*)&pcCam); + pcCam.Inverse(); + piEnd->SetMatrix("InvViewProj", + (const D3DXMATRIX*)&pcCam); + } + + D3DXVECTOR4 apcVec[5]; + apcVec[0].x = g_avLightDirs[0].x; + apcVec[0].y = g_avLightDirs[0].y; + apcVec[0].z = g_avLightDirs[0].z; + apcVec[1].x = g_avLightDirs[0].x * -1.0f; + apcVec[1].y = g_avLightDirs[0].y * -1.0f; + apcVec[1].z = g_avLightDirs[0].z * -1.0f; + + D3DXVec4Normalize(&apcVec[0],&apcVec[0]); + D3DXVec4Normalize(&apcVec[1],&apcVec[1]); + piEnd->SetVectorArray("afLightDir",apcVec,5); + + if(g_sOptions.b3Lights) + { + apcVec[0].x = 0.6f; + apcVec[0].y = 0.6f; + apcVec[0].z = 0.6f; + apcVec[0].w = 1.0f; + + apcVec[1].x = 0.3f; + apcVec[1].y = 0.0f; + apcVec[1].z = 0.0f; + apcVec[1].w = 1.0f; + } + else + { + apcVec[0].x = 1.0f; + apcVec[0].y = 1.0f; + apcVec[0].z = 1.0f; + apcVec[0].w = 1.0f; + + apcVec[1].x = 0.0f; + apcVec[1].y = 0.0f; + apcVec[1].z = 0.0f; + apcVec[1].w = 0.0f; + } + apcVec[0] *= g_fLightIntensity; + apcVec[1] *= g_fLightIntensity; + piEnd->SetVectorArray("afLightColor",apcVec,5); + + apcVec[0].x = vPos.x; + apcVec[0].y = vPos.y; + apcVec[0].z = vPos.z; + piEnd->SetVector( "vCameraPos",&apcVec[0]); + + if (g_sCaps.PixelShaderVersion < D3DPS_VERSION(3,0) || g_sOptions.bLowQuality) + { + if (g_sOptions.b3Lights) + piEnd->SetTechnique("DefaultFXSpecular_PS20_D2"); + else piEnd->SetTechnique("DefaultFXSpecular_PS20_D1"); + } + else + { + if (g_sOptions.b3Lights) + piEnd->SetTechnique("DefaultFXSpecular_D2"); + else piEnd->SetTechnique("DefaultFXSpecular_D1"); + } + + UINT dwPasses = 0; + piEnd->Begin(&dwPasses,0); + piEnd->BeginPass(0); + } + D3DXVECTOR4 vVector = g_aclNormalColors[g_iCurrentColor]; + if (++g_iCurrentColor == 14) + { + g_iCurrentColor = 0; + } + if (! (!g_sOptions.bRenderMats && bAlpha)) + { + for (unsigned int i = 0; i < piNode->mNumMeshes;++i) + { + // don't render the mesh if the render pass is incorrect + if (g_sOptions.bRenderMats && ( + g_pcAsset->apcMeshes[piNode->mMeshes[i]]->piOpacityTexture || + g_pcAsset->apcMeshes[piNode->mMeshes[i]]->fOpacity != 1.0f)) + { + if (!bAlpha)continue; + } + else if (bAlpha)continue; + + // set vertex and index buffer and the material ... + g_piDevice->SetStreamSource(0, + g_pcAsset->apcMeshes[piNode->mMeshes[i]]->piVB,0, + sizeof(AssetHelper::Vertex)); + + // now setup the material + if (g_sOptions.bRenderMats) + SetupMaterial(g_pcAsset->apcMeshes[piNode->mMeshes[i]],pcProj,aiMe,pcCam,vPos); + + g_piDevice->SetIndices(g_pcAsset->apcMeshes[piNode->mMeshes[i]]->piIB); + g_piDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, + 0,0, + g_pcAsset->pcScene->mMeshes[piNode->mMeshes[i]]->mNumVertices,0, + g_pcAsset->pcScene->mMeshes[piNode->mMeshes[i]]->mNumFaces); + + // now end the material + if (g_sOptions.bRenderMats) + EndMaterial(g_pcAsset->apcMeshes[piNode->mMeshes[i]]); + + // render normal vectors? + if (g_sOptions.bRenderNormals && g_pcAsset->apcMeshes[piNode->mMeshes[i]]->piVBNormals) + { + // this is very similar to the code in SetupMaterial() + ID3DXEffect* piEnd = g_piNormalsEffect; + + piEnd->SetVector("OUTPUT_COLOR",&vVector); + + piEnd->SetMatrix("WorldViewProjection", + (const D3DXMATRIX*)&pcProj); + + UINT dwPasses = 0; + piEnd->Begin(&dwPasses,0); + piEnd->BeginPass(0); + + g_piDevice->SetStreamSource(0, + g_pcAsset->apcMeshes[piNode->mMeshes[i]]->piVBNormals,0, + sizeof(AssetHelper::LineVertex)); + + g_piDevice->DrawPrimitive(D3DPT_LINELIST,0, + g_pcAsset->pcScene->mMeshes[piNode->mMeshes[i]]->mNumVertices); + + piEnd->EndPass(); + piEnd->End(); + } + } + if (!g_sOptions.bRenderMats) + { + g_piDefaultEffect->EndPass(); + g_piDefaultEffect->End(); + } + } + for (unsigned int i = 0; i < piNode->mNumChildren;++i) + { + RenderNode(piNode->mChildren[i],aiMe,bAlpha ); + } + return 1; + } + + +//------------------------------------------------------------------------------- +//------------------------------------------------------------------------------- +int Render (void) + { + g_iCurrentColor = 0; + + // setup wireframe/solid rendering mode + if (g_sOptions.eDrawMode == RenderOptions::WIREFRAME) + { + g_piDevice->SetRenderState(D3DRS_FILLMODE,D3DFILL_WIREFRAME); + } + else g_piDevice->SetRenderState(D3DRS_FILLMODE,D3DFILL_SOLID); + + g_piDevice->BeginScene(); + + // draw the scene background (clear and texture 2d) + CBackgroundPainter::Instance().OnPreRender(); + + // draw all opaque objects in the scene + aiMatrix4x4 m; + if (NULL != g_pcAsset && NULL != g_pcAsset->pcScene->mRootNode) + { + if(CBackgroundPainter::TEXTURE_CUBE == CBackgroundPainter::Instance().GetMode()) + HandleMouseInputSkyBox(); + + // handle input commands + HandleMouseInputLightRotate(); + HandleMouseInputLightIntensityAndColor(); + if(g_bFPSView) + { + HandleMouseInputFPS(); + HandleKeyboardInputFPS(); + } + else + { + HandleMouseInputLocal(); + } + + // compute auto rotation depending on the time passed + if (g_sOptions.bRotate) + { + aiMatrix4x4 mMat; + D3DXMatrixRotationYawPitchRoll((D3DXMATRIX*)&mMat, + g_vRotateSpeed.x * g_fElpasedTime, + g_vRotateSpeed.y * g_fElpasedTime, + g_vRotateSpeed.z * g_fElpasedTime); + g_mWorldRotate = g_mWorldRotate * mMat; + } + + // Handle rotations of light source(s) + if (g_sOptions.bLightRotate) + { + aiMatrix4x4 mMat; + D3DXMatrixRotationYawPitchRoll((D3DXMATRIX*)&mMat, + g_vRotateSpeed.x * g_fElpasedTime * 0.5f, + g_vRotateSpeed.y * g_fElpasedTime * 0.5f, + g_vRotateSpeed.z * g_fElpasedTime * 0.5f); + + D3DXVec3TransformNormal((D3DXVECTOR3*)&g_avLightDirs[0], + (D3DXVECTOR3*)&g_avLightDirs[0],(D3DXMATRIX*)&mMat); + + // 2 lights to rotate? + if (g_sOptions.b3Lights) + { + D3DXVec3TransformNormal((D3DXVECTOR3*)&g_avLightDirs[1], + (D3DXVECTOR3*)&g_avLightDirs[1],(D3DXMATRIX*)&mMat); + + g_avLightDirs[1].Normalize(); + } + g_avLightDirs[0].Normalize(); + } + + m = g_mWorld * g_mWorldRotate ; + RenderNode(g_pcAsset->pcScene->mRootNode,m,false); + } + + // if a cube texture is loaded as background image, the user + // should be able to rotate it even if no asset is loaded + else if(CBackgroundPainter::TEXTURE_CUBE == CBackgroundPainter::Instance().GetMode()) + { + if (g_bFPSView) + { + HandleMouseInputFPS(); + HandleKeyboardInputFPS(); + } + HandleMouseInputSkyBox(); + + // need to store the last mouse position in the global variable + // HandleMouseInputFPS() is doing this internally + if (!g_bFPSView) + { + g_LastmousePos.x = g_mousePos.x; + g_LastmousePos.y = g_mousePos.y; + } + } + + // draw the scene background + CBackgroundPainter::Instance().OnPostRender(); + + // draw all non-opaque objects in the scene + if (NULL != g_pcAsset && NULL != g_pcAsset->pcScene->mRootNode) + { + RenderNode(g_pcAsset->pcScene->mRootNode,m,true); + } + + // draw the HUD texture on top of the rendered scene using + // pre-projected vertices + if (!g_bFPSView && g_pcAsset && g_pcTexture) + { + RECT sRect; + GetWindowRect(GetDlgItem(g_hDlg,IDC_RT),&sRect); + sRect.right -= sRect.left; + sRect.bottom -= sRect.top; + + struct SVertex + { + float x,y,z,w,u,v; + }; + + UINT dw; + g_piPassThroughEffect->Begin(&dw,0); + g_piPassThroughEffect->BeginPass(0); + + D3DSURFACE_DESC sDesc; + g_pcTexture->GetLevelDesc(0,&sDesc); + SVertex as[4]; + float fHalfX = ((float)sRect.right-(float)sDesc.Width) / 2.0f; + float fHalfY = ((float)sRect.bottom-(float)sDesc.Height) / 2.0f; + as[1].x = fHalfX; + as[1].y = fHalfY; + as[1].z = 0.2f; + as[1].w = 1.0f; + as[1].u = 0.0f; + as[1].v = 0.0f; + + as[3].x = (float)sRect.right-fHalfX; + as[3].y = fHalfY; + as[3].z = 0.2f; + as[3].w = 1.0f; + as[3].u = 1.0f; + as[3].v = 0.0f; + + as[0].x = fHalfX; + as[0].y = (float)sRect.bottom-fHalfY; + as[0].z = 0.2f; + as[0].w = 1.0f; + as[0].u = 0.0f; + as[0].v = 1.0f; + + as[2].x = (float)sRect.right-fHalfX; + as[2].y = (float)sRect.bottom-fHalfY; + as[2].z = 0.2f; + as[2].w = 1.0f; + as[2].u = 1.0f; + as[2].v = 1.0f; + + as[0].x -= 0.5f;as[1].x -= 0.5f;as[2].x -= 0.5f;as[3].x -= 0.5f; + as[0].y -= 0.5f;as[1].y -= 0.5f;as[2].y -= 0.5f;as[3].y -= 0.5f; + + DWORD dw2;g_piDevice->GetFVF(&dw2); + g_piDevice->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1); + g_piDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP,2, + &as,sizeof(SVertex)); + + g_piPassThroughEffect->EndPass(); + g_piPassThroughEffect->End(); + + g_piDevice->SetFVF(dw2); + } + + // Now render the log display in the upper right corner of the window + CLogDisplay::Instance().OnRender(); + + // present the backbuffer + g_piDevice->EndScene(); + g_piDevice->Present(NULL,NULL,NULL,NULL); + + // don't remove this, problems on some older machines (AMD timing bug) + Sleep(10); + return 1; + } +}; + + + + + + diff --git a/tools/assimp_view/assimp_view.h b/tools/assimp_view/assimp_view.h new file mode 100644 index 000000000..d30fafd82 --- /dev/null +++ b/tools/assimp_view/assimp_view.h @@ -0,0 +1,228 @@ +//------------------------------------------------------------------------------- +/** +* This program is distributed under the terms of the GNU Lesser General +* Public License (LGPL). +* +* ASSIMP Viewer Utility +* +*/ +//------------------------------------------------------------------------------- + +#if (!defined AV_MAIN_H_INCLUDED) +#define AV_MAIN_H_INCLUDED + +// include resource definitions +#include "resource.h" + +// Include ASSIMP headers +#include "aiDefs.h" +#include "aiAnim.h" +#include "aiAssert.h" +#include "aiFileIO.h" +#include "aiMaterial.h" +#include "aiPostProcess.h" +#include "aiMesh.h" +#include "aiScene.h" +#include "aiTypes.h" +#include "IOSystem.h" +#include "IOStream.h" +#include "assimp.h" +#include "assimp.hpp" + +// in order for std::min and std::max to behave properly +#ifdef min +#undef min +#endif // min +#ifdef max +#undef max +#endif // min + +// default movement speed +#define MOVE_SPEED 10.0f + +namespace AssimpView { + +#include "AssetHelper.h" +#include "Camera.h" +#include "RenderOptions.h" +#include "Shaders.h" +#include "Background.h" +#include "LogDisplay.h" + +//------------------------------------------------------------------------------- +// Function prototypes +//------------------------------------------------------------------------------- + int InitD3D(void); + int ShutdownD3D(void); + int CreateDevice (bool p_bMultiSample,bool p_bSuperSample, bool bHW = true); + int CreateDevice (void); + int Render (void); + int ShutdownDevice(void); + int GetProjectionMatrix (aiMatrix4x4& p_mOut); + int LoadAsset(void); + int CreateAssetData(void); + int DeleteAssetData(void); + int ScaleAsset(void); + int DeleteAsset(void); + int SetupFPSView(); + aiVector3D GetCameraMatrix (aiMatrix4x4& p_mOut); + int CreateMaterial(AssetHelper::MeshHelper* pcMesh,const aiMesh* pcSource); + + void HandleMouseInputFPS( void ); + void HandleMouseInputLightRotate( void ); + void HandleMouseInputLocal( void ); + void HandleKeyboardInputFPS( void ); + void HandleMouseInputLightIntensityAndColor( void ); + void HandleMouseInputSkyBox( void ); + + +//------------------------------------------------------------------------------- +// +// Dialog procedure for the progress bar window +// +//------------------------------------------------------------------------------- +INT_PTR CALLBACK ProgressMessageProc(HWND hwndDlg,UINT uMsg, + WPARAM wParam,LPARAM lParam); + +//------------------------------------------------------------------------------- +// Main message procedure of the application +// +// The function handles all incoming messages for the main window. +// However, if does not directly process input commands. +// NOTE: Due to the impossibility to process WM_CHAR messages in dialogs +// properly the code for all hotkeys has been moved to the WndMain +//------------------------------------------------------------------------------- +INT_PTR CALLBACK MessageProc(HWND hwndDlg,UINT uMsg, + WPARAM wParam,LPARAM lParam); + +//------------------------------------------------------------------------------- +// +// Dialog procedure for the about dialog +// +//------------------------------------------------------------------------------- +INT_PTR CALLBACK AboutMessageProc(HWND hwndDlg,UINT uMsg, + WPARAM wParam,LPARAM lParam); + +//------------------------------------------------------------------------------- +// +// Dialog prcoedure for the help dialog +// +//------------------------------------------------------------------------------- +INT_PTR CALLBACK HelpDialogProc(HWND hwndDlg,UINT uMsg, + WPARAM wParam,LPARAM lParam); + + +//------------------------------------------------------------------------------- +// find a valid path to a texture file +// +// Handle 8.3 syntax correctly, search the environment of the +// executable and the asset for a texture with a name very similar to a given one +//------------------------------------------------------------------------------- +int FindValidPath(aiString* p_szString); + + +//------------------------------------------------------------------------------- +// Delete all resources of a given material +// +// Must be called before CreateMaterial() to prevent memory leaking +//------------------------------------------------------------------------------- +void DeleteMaterial(AssetHelper::MeshHelper* pcIn); + + +//------------------------------------------------------------------------------- +// Recreate all specular materials depending on the current specularity settings +// +// Diffuse-only materials are ignored. +// Must be called after specular highlights have been toggled +//------------------------------------------------------------------------------- +void UpdateSpecularMaterials(); + + +//------------------------------------------------------------------------------- +// Handle command line parameters +// +// The function loads an asset specified on the command line as first argument +// Other command line parameters are not handled +//------------------------------------------------------------------------------- +void HandleCommandLine(char* p_szCommand); + + +//------------------------------------------------------------------------------- +// Position of the cursor relative to the 3ds max' like control circle +//------------------------------------------------------------------------------- +enum EClickPos + { + EClickPos_Circle, + EClickPos_CircleVert, + EClickPos_CircleHor, + EClickPos_Outside + }; + + +//------------------------------------------------------------------------------- +// Evil globals +//------------------------------------------------------------------------------- + extern HINSTANCE g_hInstance /*= NULL*/; + extern HWND g_hDlg /*= NULL*/; + extern IDirect3D9* g_piD3D /*= NULL*/; + extern IDirect3DDevice9* g_piDevice /*= NULL*/; + extern double g_fFPS /*= 0.0f*/; + extern char g_szFileName[MAX_PATH]; + extern ID3DXEffect* g_piDefaultEffect /*= NULL*/; + extern ID3DXEffect* g_piNormalsEffect /*= NULL*/; + extern ID3DXEffect* g_piPassThroughEffect /*= NULL*/; + extern bool g_bMousePressed /*= false*/; + extern bool g_bMousePressedR /*= false*/; + extern bool g_bMousePressedM /*= false*/; + extern bool g_bMousePressedBoth /*= false*/; + extern float g_fElpasedTime /*= 0.0f*/; + extern D3DCAPS9 g_sCaps; + extern bool g_bLoadingFinished /*= false*/; + extern HANDLE g_hThreadHandle /*= NULL*/; + extern float g_fWheelPos /*= -10.0f*/; + extern bool g_bLoadingCanceled /*= false*/; + extern IDirect3DTexture9* g_pcTexture /*= NULL*/; + + extern aiMatrix4x4 g_mWorld; + extern aiMatrix4x4 g_mWorldRotate; + extern aiVector3D g_vRotateSpeed /*= aiVector3D(0.5f,0.5f,0.5f)*/; + + extern aiVector3D g_avLightDirs[1] /* = + { aiVector3D(-0.5f,0.6f,0.2f) , + aiVector3D(-0.5f,0.5f,0.5f)} */; + + + extern POINT g_mousePos /*= {0,0};*/; + extern POINT g_LastmousePos /*= {0,0}*/; + extern bool g_bFPSView /*= false*/; + extern bool g_bInvert /*= false*/; + extern EClickPos g_eClick; + extern unsigned int g_iCurrentColor /*= 0*/; + + extern float g_fLightIntensity /*=0.0f*/; + extern float g_fLightColor /*=0.0f*/; + + extern RenderOptions g_sOptions; + extern Camera g_sCamera; + extern AssetHelper *g_pcAsset /*= NULL*/; + + + // + // Contains the mask image for the HUD + // (used to determine the position of a click) + // + // The size of the image is identical to the size of the main + // HUD texture + // + extern unsigned char* g_szImageMask /*= NULL*/; + + + // + // Specifies the number of different shaders generated for + // the current asset. This number is incremented by CreateMaterial() + // each time a shader isn't found in cache and needs to be created + // + extern unsigned int g_iShaderCount /* = 0 */; + }; + +#endif // !! AV_MAIN_H_INCLUDED \ No newline at end of file diff --git a/tools/assimp_view/assimp_view.ico b/tools/assimp_view/assimp_view.ico new file mode 100644 index 000000000..d551aa3aa Binary files /dev/null and b/tools/assimp_view/assimp_view.ico differ diff --git a/tools/assimp_view/assimp_view.rc b/tools/assimp_view/assimp_view.rc new file mode 100644 index 000000000..9d2913bad --- /dev/null +++ b/tools/assimp_view/assimp_view.rc @@ -0,0 +1,307 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#define APSTUDIO_HIDDEN_SYMBOLS +#include "windows.h" +#undef APSTUDIO_HIDDEN_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Deutsch (Deutschland) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU) +#ifdef _WIN32 +LANGUAGE LANG_GERMAN, SUBLANG_GERMAN +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ASSIMP_VIEW ICON "assimp_view.ico" +IDI_SMALL ICON "small.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUTBOX DIALOGEX 22, 17, 283, 170 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_CAPTION | WS_SYSMENU +CAPTION "About ASSIMP" +FONT 10, "MS Shell Dlg", 400, 0, 0x0 +BEGIN + LTEXT "ASSIMP is the free, OpenSource ASSet IMPort Library",IDC_STATIC,42,12,204,12 + LTEXT "Developed by members of the german game development community www.zfx.info",IDC_STATIC,42,30,204,24 + LTEXT "Thomas Schulze \t\taka ",IDC_STATIC,42,66,192,8 + LTEXT "Kim Kulling \t\t\taka ",IDC_STATIC,42,78,186,8 + LTEXT "Rainer Schmidt \t\t\taka ",IDC_STATIC,42,90,180,8 + LTEXT "Alexander Gessler\t\taka ",IDC_STATIC,42,102,186,8 + CONTROL "",IDC_STATIC,"Static",SS_ETCHEDHORZ,0,60,282,1 + LTEXT "http://zfxce.svn.sourceforge.net/viewvc/zfxce/trunk/ASSIMP",IDC_STATIC,42,120,198,8 + DEFPUSHBUTTON "Love this library",IDOK,199,148,67,14 + CONTROL "",IDC_STATIC,"Static",SS_ETCHEDHORZ,0,138,281,1 +END + +IDD_DIALOGMAIN DIALOGEX 0, 0, 583, 384 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Free Asset Import Library - ModelViewer " +MENU IDR_MENU1 +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_RT,"Static",SS_OWNERDRAW,10,10,452,344 + GROUPBOX "Rendering Options",IDC_STATIC,468,108,108,96 + CONTROL "MultiSample image",IDC_TOGGLEMS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,478,123,80,10 + CONTROL "Toggle Wireframe",IDC_TOGGLEWIRE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,478,133,73,10 + CONTROL "Disable Materials",IDC_TOGGLEMAT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,478,143,69,10 + GROUPBOX "Statistics",IDC_STATIC,468,6,108,96 + LTEXT "NumVertices:\t 0",IDC_NUMVERTS,477,24,47,8 + LTEXT "NumFaces:\t 0",IDC_NUMFACES,477,36,45,8 + LTEXT "NumMaterials:\t 0",IDC_NUMMATS,477,48,48,8 + LTEXT "FPS:\t\t 0",IDC_FPS,477,73,34,8 + CONTROL "Display Normals",IDC_TOGGLENORMALS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,478,153,66,10 + GROUPBOX "Interaction",IDC_STATIC,468,210,108,48 + CONTROL "Toggle AutoRotate",IDC_AUTOROTATE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,478,223,74,10 + CONTROL 130,IDC_STATIC,"Static",SS_BITMAP | SS_CENTERIMAGE,0,360,583,24 + GROUPBOX "Animation",IDC_SANIMGB,468,264,108,90 + EDITTEXT IDC_EVERT,531,21,36,12,ES_AUTOHSCROLL | ES_READONLY + EDITTEXT IDC_EFACE,531,34,36,12,ES_AUTOHSCROLL | ES_READONLY + EDITTEXT IDC_EMAT,531,47,36,12,ES_AUTOHSCROLL | ES_READONLY + EDITTEXT IDC_EFPS,531,72,36,12,ES_AUTOHSCROLL | ES_READONLY + CONTROL "Rotate light sources",IDC_LIGHTROTATE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,478,242,74,10 + CONTROL "2 directional lights",IDC_3LIGHTS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,478,163,73,10 + LTEXT "LoadTime:",IDC_LOADTIME,477,85,34,8 + EDITTEXT IDC_ELOAD,531,85,36,12,ES_AUTOHSCROLL | ES_READONLY + CONTROL "Zoom/Rotate",IDC_ZOOM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,478,232,58,10 + CONTROL "Low quality lighting",IDC_LOWQUALITY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,478,173,74,10 + CONTROL "No specular lighting",IDC_NOSPECULAR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,478,183,74,10 + CONTROL "Play [OK]",IDC_PLAYANIM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,480,276,42,10 + CONTROL "",IDC_SPEED,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | TBS_TOOLTIPS | WS_TABSTOP,480,288,90,12 + LTEXT "Speed: [+/-]",IDC_SSPEED,528,277,42,8 + LTEXT "NumShaders:\t 0",IDC_NUMMATS2,477,61,48,8 + EDITTEXT IDC_ESHADER,531,59,36,12,ES_AUTOHSCROLL | ES_READONLY + LTEXT "[M]",IDC_STATIC,557,123,11,8 + LTEXT "[W]",IDC_STATIC,557,133,11,8 + LTEXT "[D]",IDC_STATIC,557,143,11,8 + LTEXT "[N]",IDC_STATIC,557,153,11,8 + LTEXT "[L]",IDC_STATIC,557,163,11,8 + LTEXT "[P]",IDC_STATIC,557,173,11,8 + LTEXT "[S]",IDC_STATIC,557,183,11,8 + LTEXT "[A]",IDC_STATIC,557,223,11,8 + LTEXT "[Z]",IDC_STATIC,557,232,11,8 + LTEXT "[R]",IDC_STATIC,557,242,11,8 + COMBOBOX IDC_COMBO1,480,315,90,30,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "Anim: [Pg up/Pg down]",IDC_SANIM,483,302,74,8 + CONTROL "Play all in order [O]",IDC_PINORDER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,479,334,77,10 +END + +IDD_LOADDIALOG DIALOGEX 0, 0, 278, 99 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_BORDER | WS_SYSMENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "Cancel",IDOK,206,78,65,14 + CONTROL "",IDC_PROGRESS,"msctls_progress32",WS_BORDER,11,46,260,14 + LTEXT "Please wait while ASSIMP is loading the asset. This might take a few seconds\nSit down and drink a cup of coffee or similar. Relax.\nThe force is with you...",IDC_STATIC,15,15,246,27 +END + +IDD_AVHELP DIALOGEX 0, 0, 481, 346 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "ASSIMP Viewer: Help" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,420,324,50,14 + CONTROL "",IDC_RICHEDIT21,"RichEdit20A",ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY | WS_VSCROLL | WS_TABSTOP,19,18,462,294 + CONTROL "",IDC_STATIC,"Static",SS_ETCHEDHORZ,0,312,490,1 + CONTROL "",IDC_STATIC,"Static",SS_ETCHEDHORZ,0,16,490,1 +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""windows.h""\r\n" + "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x0L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040704b0" + BEGIN + VALUE "CompanyName", "ASSIMP Development Team" + VALUE "FileDescription", "ASSIMP Viewer Application" + VALUE "FileVersion", "1, 0, 0, 1" + VALUE "InternalName", "assimp_view" + VALUE "LegalCopyright", "Licensed under the LGPL" + VALUE "OriginalFilename", "assimpview32.exe" + VALUE "ProductName", "ASSIMP Viewer Application" + VALUE "ProductVersion", "1, 0, 0, 1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x407, 1200 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_ABOUTBOX, DIALOG + BEGIN + BOTTOMMARGIN, 158 + END + + IDD_DIALOGMAIN, DIALOG + BEGIN + RIGHTMARGIN, 576 + TOPMARGIN, 7 + BOTTOMMARGIN, 307 + END + + IDD_LOADDIALOG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 271 + TOPMARGIN, 7 + BOTTOMMARGIN, 92 + END + + IDD_AVHELP, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 474 + TOPMARGIN, 7 + BOTTOMMARGIN, 339 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_BITMAP1 BITMAP "banner.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_MENU1 MENU +BEGIN + POPUP "Viewer" + BEGIN + MENUITEM "Open Asset", ID_VIEWER_OPEN + MENUITEM "Close Asset", ID_VIEWER_CLOSEASSET + MENUITEM SEPARATOR + MENUITEM "Screenshot", ID_VIEWER_SAVESCREENSHOTTOFILE + MENUITEM "Reset view", ID_VIEWER_RESETVIEW + MENUITEM SEPARATOR + MENUITEM "Setup file associations", ID_VIEWER_H + MENUITEM SEPARATOR + MENUITEM "Quit", ID_VIEWER_QUIT + END + POPUP "Background" + BEGIN + MENUITEM "Set color", ID_BACKGROUND_SETCOLOR + MENUITEM "Load skybox", ID_BACKGROUND_LOADSKYBOX + MENUITEM "Load texture", ID_BACKGROUND_LOADTEXTURE + MENUITEM SEPARATOR + MENUITEM "Clear", ID_BACKGROUND_CLEAR + END + POPUP "?" + BEGIN + MENUITEM "About", ID__ABOUT + MENUITEM "Help", ID__HELP + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// TEXT +// + +IDR_TEXT1 TEXT "text1.bin" + +///////////////////////////////////////////////////////////////////////////// +// +// RCDATA +// + +IDR_HUD RCDATA "HUD.png" +IDR_HUDMASK RCDATA "HUDMask.png" +#endif // Deutsch (Deutschland) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/tools/assimp_view/banner.bmp b/tools/assimp_view/banner.bmp new file mode 100644 index 000000000..36ceed902 Binary files /dev/null and b/tools/assimp_view/banner.bmp differ diff --git a/tools/assimp_view/banner_pure.bmp b/tools/assimp_view/banner_pure.bmp new file mode 100644 index 000000000..aced0f934 Binary files /dev/null and b/tools/assimp_view/banner_pure.bmp differ diff --git a/tools/assimp_view/help.rtf b/tools/assimp_view/help.rtf new file mode 100644 index 000000000..aae67e0d9 --- /dev/null +++ b/tools/assimp_view/help.rtf @@ -0,0 +1,370 @@ +{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff31507\deff0\stshfdbch31505\stshfloch31506\stshfhich31506\stshfbi0\deflang1031\deflangfe1031\themelang1031\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f2\fbidi \fmodern\fcharset0\fprq1{\*\panose 02070309020205020404}Courier New;} +{\f3\fbidi \froman\fcharset2\fprq2{\*\panose 05050102010706020507}Symbol;}{\f10\fbidi \fnil\fcharset2\fprq2{\*\panose 05000000000000000000}Wingdings;}{\f34\fbidi \froman\fcharset0\fprq2{\*\panose 02040503050406030204}Cambria Math;} +{\f38\fbidi \fswiss\fcharset0\fprq2{\*\panose 020b0604030504040204}Tahoma;}{\f39\fbidi \fswiss\fcharset0\fprq2{\*\panose 00000000000000000000}MS Reference Sans Serif;} +{\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbmajor\f31501\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\fhimajor\f31502\fbidi \froman\fcharset0\fprq2{\*\panose 02040503050406030204}Cambria;}{\fbimajor\f31503\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\flominor\f31504\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbminor\f31505\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\fhiminor\f31506\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;}{\fbiminor\f31507\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f41\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} +{\f42\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\f44\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f45\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f46\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\f47\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f48\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f49\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f61\fbidi \fmodern\fcharset238\fprq1 Courier New CE;} +{\f62\fbidi \fmodern\fcharset204\fprq1 Courier New Cyr;}{\f64\fbidi \fmodern\fcharset161\fprq1 Courier New Greek;}{\f65\fbidi \fmodern\fcharset162\fprq1 Courier New Tur;}{\f66\fbidi \fmodern\fcharset177\fprq1 Courier New (Hebrew);} +{\f67\fbidi \fmodern\fcharset178\fprq1 Courier New (Arabic);}{\f68\fbidi \fmodern\fcharset186\fprq1 Courier New Baltic;}{\f69\fbidi \fmodern\fcharset163\fprq1 Courier New (Vietnamese);}{\f381\fbidi \froman\fcharset238\fprq2 Cambria Math CE;} +{\f382\fbidi \froman\fcharset204\fprq2 Cambria Math Cyr;}{\f384\fbidi \froman\fcharset161\fprq2 Cambria Math Greek;}{\f385\fbidi \froman\fcharset162\fprq2 Cambria Math Tur;}{\f388\fbidi \froman\fcharset186\fprq2 Cambria Math Baltic;} +{\f421\fbidi \fswiss\fcharset238\fprq2 Tahoma CE;}{\f422\fbidi \fswiss\fcharset204\fprq2 Tahoma Cyr;}{\f424\fbidi \fswiss\fcharset161\fprq2 Tahoma Greek;}{\f425\fbidi \fswiss\fcharset162\fprq2 Tahoma Tur;} +{\f426\fbidi \fswiss\fcharset177\fprq2 Tahoma (Hebrew);}{\f427\fbidi \fswiss\fcharset178\fprq2 Tahoma (Arabic);}{\f428\fbidi \fswiss\fcharset186\fprq2 Tahoma Baltic;}{\f429\fbidi \fswiss\fcharset163\fprq2 Tahoma (Vietnamese);} +{\f430\fbidi \fswiss\fcharset222\fprq2 Tahoma (Thai);}{\f431\fbidi \fswiss\fcharset238\fprq2 MS Reference Sans Serif CE;}{\f432\fbidi \fswiss\fcharset204\fprq2 MS Reference Sans Serif Cyr;} +{\f434\fbidi \fswiss\fcharset161\fprq2 MS Reference Sans Serif Greek;}{\f435\fbidi \fswiss\fcharset162\fprq2 MS Reference Sans Serif Tur;}{\f438\fbidi \fswiss\fcharset186\fprq2 MS Reference Sans Serif Baltic;} +{\f439\fbidi \fswiss\fcharset163\fprq2 MS Reference Sans Serif (Vietnamese);}{\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} +{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} +{\fdbmajor\f31518\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} +{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} +{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhimajor\f31528\fbidi \froman\fcharset238\fprq2 Cambria CE;} +{\fhimajor\f31529\fbidi \froman\fcharset204\fprq2 Cambria Cyr;}{\fhimajor\f31531\fbidi \froman\fcharset161\fprq2 Cambria Greek;}{\fhimajor\f31532\fbidi \froman\fcharset162\fprq2 Cambria Tur;} +{\fhimajor\f31535\fbidi \froman\fcharset186\fprq2 Cambria Baltic;}{\fbimajor\f31538\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} +{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} +{\flominor\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} +{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} +{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbminor\f31558\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} +{\fdbminor\f31559\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} +{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} +{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhiminor\f31568\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;} +{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;} +{\fbiminor\f31578\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} +{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} +{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0; +\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128; +\red192\green192\blue192;}{\*\defchp \fs22\loch\af31506\hich\af31506\dbch\af31505 }{\*\defpap \ql \li0\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{ +\ql \li0\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \fs22\lang1031\langfe1031\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1031\langfenp1031 +\snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive \sunhideused \spriority1 Default Paragraph Font;}{\* +\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tscellwidthfts0\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa200\sl276\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \fs22\lang1031\langfe1031\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1031\langfenp1031 +\snext11 \ssemihidden \sunhideused \sqformat Normal Table;}{\s15\ql \li720\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin720\itap0\contextualspace \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 +\fs22\lang1031\langfe1031\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1031\langfenp1031 \sbasedon0 \snext15 \sqformat \spriority34 \styrsid3683098 List Paragraph;}{ +\s16\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs16\alang1025 \ltrch\fcs0 \fs16\lang1031\langfe1031\loch\f38\hich\af38\dbch\af31505\cgrid\langnp1031\langfenp1031 +\sbasedon0 \snext16 \slink17 \ssemihidden \sunhideused \styrsid8273515 Balloon Text;}{\*\cs17 \additive \rtlch\fcs1 \af38\afs16 \ltrch\fcs0 \f38\fs16 \sbasedon10 \slink16 \slocked \ssemihidden \styrsid8273515 Balloon Text Char;}}{\*\listtable +{\list\listtemplateid-2111801688\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid1242219378\'01-;}{\levelnumbers;} +\loch\af39\hich\af39\dbch\af31505\fbias0\hres0\chhres0 \fi-360\li700\lin700 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567619\'01o;}{\levelnumbers;} +\f2\fbias0\hres0\chhres0 \fi-360\li1420\lin1420 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567621\'01\u-3929 ?;}{\levelnumbers;} +\f10\fbias0\hres0\chhres0 \fi-360\li2140\lin2140 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567617\'01\u-3913 ?;}{\levelnumbers;} +\f3\fbias0\hres0\chhres0 \fi-360\li2860\lin2860 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567619\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 +\fi-360\li3580\lin3580 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567621\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 +\fi-360\li4300\lin4300 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567617\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 +\fi-360\li5020\lin5020 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567619\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li5740\lin5740 } +{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567621\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li6460\lin6460 }{\listname +;}\listid256792316}{\list\listtemplateid1110873036\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat0\levelspace0\levelindent0{\leveltext\leveltemplateid680798618\'01-;}{\levelnumbers;} +\fs20\loch\af39\hich\af39\dbch\af31505\fbias0\hres0\chhres0 \fi-360\li700\lin700 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567619 +\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li1420\lin1420 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567621\'01\u-3929 ?;}{\levelnumbers +;}\f10\fbias0\hres0\chhres0 \fi-360\li2140\lin2140 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567617\'01\u-3913 ?;}{\levelnumbers;} +\f3\fbias0\hres0\chhres0 \fi-360\li2860\lin2860 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567619\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 +\fi-360\li3580\lin3580 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567621\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 +\fi-360\li4300\lin4300 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567617\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 +\fi-360\li5020\lin5020 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567619\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li5740\lin5740 } +{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567621\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li6460\lin6460 }{\listname +;}\listid483863136}{\list\listtemplateid338825902\listhybrid{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid-1769051580\'02\'00);}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \fbias0\hres0\chhres0 \fi-360\li700\lin700 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567641\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-360\li1420\lin1420 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567643\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-180\li2140\lin2140 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567631\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-360\li2860\lin2860 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567641\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-360\li3580\lin3580 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567643\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-180\li4300\lin4300 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567631\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-360\li5020\lin5020 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567641\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-360\li5740\lin5740 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567643\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-180\li6460\lin6460 }{\listname ;}\listid649408911}{\list\listtemplateid-371049066\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat0\levelspace360\levelindent0{\leveltext +\leveltemplateid680798618\'01-;}{\levelnumbers;}\fs20\loch\af39\hich\af39\dbch\af31505\fbias0\hres0\chhres0 \fi-360\li720\lin720 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0 +{\leveltext\leveltemplateid67567641\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0 +{\leveltext\leveltemplateid67567643\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li2160\lin2160 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0 +{\leveltext\leveltemplateid67567631\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li2880\lin2880 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0 +{\leveltext\leveltemplateid67567641\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li3600\lin3600 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0 +{\leveltext\leveltemplateid67567643\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li4320\lin4320 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0 +{\leveltext\leveltemplateid67567631\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0 +{\leveltext\leveltemplateid67567641\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0 +{\leveltext\leveltemplateid67567643\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li6480\lin6480 }{\listname ;}\listid780539952}{\list\listtemplateid1158591540\listhybrid{\listlevel\levelnfc1\levelnfcn1\leveljc2 +\leveljcn2\levelfollow0\levelstartat1\levelspace360\levelindent0{\leveltext\leveltemplateid67567635\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0\hres0\chhres0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc4\levelnfcn4\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567641\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li2160\lin2160 }{\listlevel\levelnfc2\levelnfcn2\leveljc2 +\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567643\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li2880\lin2880 }{\listlevel\levelnfc0\levelnfcn0\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567631\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li3600\lin3600 }{\listlevel\levelnfc4\levelnfcn4\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567641\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li4320\lin4320 }{\listlevel\levelnfc2\levelnfcn2\leveljc2 +\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567643\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li5040\lin5040 }{\listlevel\levelnfc0\levelnfcn0\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567631\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc4\levelnfcn4\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567641\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li6480\lin6480 }{\listlevel\levelnfc2\levelnfcn2\leveljc2 +\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567643\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li7200\lin7200 }{\listname ;}\listid823008862} +{\list\listtemplateid1985747632\listhybrid{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid-1722268864\'02\'00);}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 +\fbias0\hres0\chhres0 \fi-720\li1080\lin1080 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567641\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567643\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-180\li2160\lin2160 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567631\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-360\li2880\lin2880 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567641\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-360\li3600\lin3600 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567643\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-180\li4320\lin4320 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567631\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567641\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567643\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-180\li6480\lin6480 }{\listname ;}\listid1527327967}}{\*\listoverridetable{\listoverride\listid1527327967\listoverridecount0\ls1}{\listoverride\listid823008862\listoverridecount0\ls2}{\listoverride\listid256792316 +\listoverridecount0\ls3}{\listoverride\listid483863136\listoverridecount0\ls4}{\listoverride\listid649408911\listoverridecount0\ls5}{\listoverride\listid780539952\listoverridecount0\ls6}}{\*\rsidtbl \rsid67262\rsid74094\rsid602054\rsid1405492\rsid2175529 +\rsid2186087\rsid2961835\rsid3277197\rsid3683098\rsid5404161\rsid5460125\rsid5513066\rsid5860386\rsid7360491\rsid7429996\rsid7677524\rsid8273515\rsid8350123\rsid8745149\rsid9194405\rsid9529660\rsid9575707\rsid9635109\rsid9712802\rsid10617294\rsid10752424 +\rsid10762577\rsid11029790\rsid11880680\rsid12914594\rsid14115297\rsid14187505\rsid14293319}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\author Alexander Gessler} +{\operator Alexander Gessler}{\creatim\yr2008\mo4\dy12\hr23\min23}{\revtim\yr2008\mo4\dy13\hr18\min11}{\version32}{\edmins0}{\nofpages2}{\nofwords446}{\nofchars2811}{\*\company UtopicTechnologies}{\nofcharsws3251}{\vern32893}}{\*\xmlnstbl {\xmlns1 http:// +schemas.microsoft.com/office/word/2003/wordml}}\paperw11906\paperh16838\margl1417\margr1417\margt1417\margb1134\gutter0\ltrsect +\deftab708\widowctrl\ftnbj\aenddoc\hyphhotz425\trackmoves1\trackformatting1\donotembedsysfont1\relyonvml0\donotembedlingdata0\grfdocevents0\validatexml1\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0 +\showxmlerrors1\noxlattoyen\expshrtn\noultrlspc\dntblnsbdb\nospaceforul\formshade\horzdoc\dgmargin\dghspace180\dgvspace180\dghorigin1417\dgvorigin1417\dghshow1\dgvshow1 +\jexpand\viewkind1\viewscale100\pgbrdrhead\pgbrdrfoot\splytwnine\ftnlytwnine\htmautsp\nolnhtadjtbl\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\allowfieldendsel\wrppunct +\asianbrkrule\rsidroot3683098\newtblstyruls\nogrowautofit\usenormstyforlist\noindnmbrts\felnbrelev\nocxsptable\indrlsweleven\noafcnsttbl\afelev\utinl\hwelev\spltpgpar\notcvasp\notbrkcnstfrctbl\notvatxbx\krnprsnet\cachedcolbal \nouicompat \fet0 +{\*\wgrffmtfilter 2450}\nofeaturethrottle1\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\headery708\footery708\colsx708\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2 +\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6 +\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang +{\pntxtb (}{\pntxta )}}\pard\plain \ltrpar\ql \li0\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 +\fs22\lang1031\langfe1031\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1031\langfenp1031 {\rtlch\fcs1 \af0\afs32 \ltrch\fcs0 \b\f39\fs32\lang1033\langfe1031\langnp1033\insrsid3683098\charrsid3683098 \hich\af39\dbch\af31505\loch\f39 +ASSIMP Viewer Utility}{\rtlch\fcs1 \af0\afs32 \ltrch\fcs0 \b\f39\fs32\lang1033\langfe1031\langnp1033\insrsid10617294 \hich\af39\dbch\af31505\loch\f39 }{\rtlch\fcs1 \af0\afs32 \ltrch\fcs0 \b\f39\fs32\lang1033\langfe1031\langnp1033\insrsid11029790 +\par }{\rtlch\fcs1 \af0\afs16 \ltrch\fcs0 \b\f39\fs16\lang1033\langfe1031\langnp1033\insrsid10617294 \hich\af39\dbch\af31505\loch\f39 Document version 1.0, April 2008}{\rtlch\fcs1 \af0\afs16 \ltrch\fcs0 +\b\f39\fs16\lang1033\langfe1031\langnp1033\insrsid10617294\charrsid10617294 +\par }{\rtlch\fcs1 \af0\afs32 \ltrch\fcs0 \b\f39\fs32\lang1033\langfe1031\langnp1033\insrsid12914594 +\par {\listtext\pard\plain\ltrpar \s15 \rtlch\fcs1 \af0 \ltrch\fcs0 \f39\lang1033\langfe1031\langnp1033\insrsid12914594 \hich\af39\dbch\af31505\loch\f39 I.\tab}}\pard\plain \ltrpar\s15\ql \fi-360\li340\ri0\sa200\sl276\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls2\adjustright\rin0\lin340\itap0\pararsid3683098\contextualspace \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 +\fs22\lang1031\langfe1031\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1031\langfenp1031 {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid12914594 \hich\af39\dbch\af31505\loch\f39 Usage}{\rtlch\fcs1 \af0\afs20 +\ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid12914594\charrsid12914594 \hich\af39\dbch\af31505\loch\f39 }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid12914594\charrsid12914594 +\par }\pard \ltrpar\s15\ql \li340\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin340\itap0\pararsid12914594\contextualspace {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid12914594\charrsid3683098 \hich\af39\dbch\af31505\loch\f39 The }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid12914594 \hich\af39\dbch\af31505\loch\f39 ASSIMP Viewer +\hich\af39\dbch\af31505\loch\f39 Utility is a small and fast stand-alone viewer utility which is using the ASSIMP library to import assets from files. It consists of a si}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid9575707 \hich\af39\dbch\af31505\loch\f39 ngle executable file and has no}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid12914594 \hich\af39\dbch\af31505\loch\f39 + external dependencies.}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9529660 \hich\af39\dbch\af31505\loch\f39 It will display assets with complex materials and animations correctly.}{\rtlch\fcs1 \af0\afs20 +\ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid12914594 +\par }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9575707 +\par }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \b\f39\fs20\lang1033\langfe1031\langnp1033\insrsid12914594\charrsid12914594 \hich\af39\dbch\af31505\loch\f39 System requirements}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid12914594 \hich\af39\dbch\af31505\loch\f39 : +\par {\listtext\pard\plain\ltrpar \s15 \rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5860386 \hich\af39\dbch\af31505\loch\f39 -\tab}}\pard \ltrpar\s15\ql \fi-360\li700\ri0\sa200\sl276\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls4\adjustright\rin0\lin700\itap0\pararsid5860386\contextualspace {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5860386 \hich\af39\dbch\af31505\loch\f39 +A Direct3D 9.0c compliant video card with at least support for Shader Model 2.0.}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid5860386\charrsid9575707 +\par {\listtext\pard\plain\ltrpar \s15 \rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9575707 \hich\af39\dbch\af31505\loch\f39 -\tab}}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid9575707 \hich\af39\dbch\af31505\loch\f39 Shader Model 3.0 cards are recommended}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid9575707\charrsid5860386 +\par {\listtext\pard\plain\ltrpar \s15 \rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5860386 \hich\af39\dbch\af31505\loch\f39 -\tab}}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid5860386 \hich\af39\dbch\af31505\loch\f39 Windows 2000 or higher}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid5860386\charrsid5860386 +\par }\pard \ltrpar\s15\ql \li340\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin340\itap0\pararsid12914594\contextualspace {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 +\f39\fs24\lang1033\langfe1031\langnp1033\insrsid3683098\charrsid3683098 \hich\af39\dbch\af31505\loch\f39 }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid3683098 +\par {\listtext\pard\plain\ltrpar \s15 \rtlch\fcs1 \af0 \ltrch\fcs0 \f39\lang1033\langfe1031\langnp1033\insrsid5460125 \hich\af39\dbch\af31505\loch\f39 II.\tab}}\pard \ltrpar\s15\ql \fi-360\li340\ri0\sa200\sl276\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls2\adjustright\rin0\lin340\itap0\pararsid3683098\contextualspace {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid5460125 \hich\af39\dbch\af31505\loch\f39 User i +\hich\af39\dbch\af31505\loch\f39 nterface}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid12914594 +\par }\pard \ltrpar\s15\ql \li340\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin340\itap0\pararsid9712802\contextualspace {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid5460125\charrsid3683098 \hich\af39\dbch\af31505\loch\f39 The }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5460125 \hich\af39\dbch\af31505\loch\f39 viewer\hich\f39 +\rquote \loch\f39 s user interface mainly consists}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5513066 \hich\af39\dbch\af31505\loch\f39 of three components:}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid602054 \hich\af39\dbch\af31505\loch\f39 }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5460125\charrsid9712802 +\par }\pard\plain \ltrpar\ql \fi340\li0\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid2186087 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 +\fs22\lang1031\langfe1031\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1031\langfenp1031 {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5404161 \hich\af39\dbch\af31505\loch\f39 +The menu bar provides access to the viewer\hich\f39 \rquote \loch\f39 s basic options.}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9712802\charrsid2186087 +\par {\listtext\pard\plain\ltrpar \s15 \rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5404161 \hich\af39\dbch\af31505\loch\f39 -\tab}}\pard\plain \ltrpar\s15\ql \fi-360\li720\ri0\sa200\sl276\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls6\adjustright\rin0\lin720\itap0\pararsid5404161\contextualspace \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 +\fs22\lang1031\langfe1031\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1031\langfenp1031 {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5404161 \loch\af39\dbch\af31505\hich\f39 \'93}{\rtlch\fcs1 \af0\afs20 +\ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid5404161\charrsid9712802 \hich\af39\dbch\af31505\loch\f39 Viewer}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5404161 \loch\af39\dbch\af31505\hich\f39 \'94 +\loch\f39 : Items to load / unload assets, take screenshots and open the viewer\hich\f39 \rquote \loch\f39 s options dialog. +\par {\listtext\pard\plain\ltrpar \s15 \rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5404161 \hich\af39\dbch\af31505\loch\f39 -\tab}}\pard \ltrpar\s15\ql \fi-360\li720\ri0\sa200\sl276\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls6\adjustright\rin0\lin720\itap0\pararsid3277197\contextualspace {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5404161 \loch\af39\dbch\af31505\hich\f39 \'93}{\rtlch\fcs1 +\af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid5404161\charrsid9712802 \hich\af39\dbch\af31505\loch\f39 Background}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5404161 +\loch\af39\dbch\af31505\hich\f39 \'94\loch\f39 : \hich\af39\dbch\af31505\loch\f39 Set the background color or use a texture (cubemaps are supported}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid11880680 +\hich\af39\dbch\af31505\loch\f39 as skyboxes}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5404161 \hich\af39\dbch\af31505\loch\f39 ) as background image for the viewer.}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid9194405\charrsid3277197 +\par }\pard \ltrpar\s15\ql \li340\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin340\itap0\pararsid5460125\contextualspace {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid602054 + +\par }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid3277197 \hich\af39\dbch\af31505\loch\f39 \hich\f39 The side panel \'93}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid3277197\charrsid3277197 \hich\af39\dbch\af31505\loch\f39 Statistics}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid3277197 \loch\af39\dbch\af31505\hich\f39 \'94 +\loch\f39 displays rendering statistics (\hich\af39\dbch\af31505\loch\f39 number of vertices loaded, number of faces, number of materials, frames per second, time required for loading).}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid602054\charrsid9194405 +\par }\pard \ltrpar\s15\ql \li340\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin340\itap0\pararsid2186087\contextualspace {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid3277197 +\hich\af39\dbch\af31505\loch\f39 \hich\f39 The \'93}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid3277197\charrsid3277197 \hich\af39\dbch\af31505\loch\f39 Rendering}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid3277197 \loch\af39\dbch\af31505\hich\f39 \'94\loch\f39 \hich\f39 panel provides rendering-related options, such as wireframe/normal visualization. \'93}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid3277197\charrsid3277197 \hich\af39\dbch\af31505\loch\f39 Interaction}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid3277197 \loch\af39\dbch\af31505\hich\f39 \'94 +\loch\f39 bundles all input related option\hich\af39\dbch\af31505\loch\f39 s.}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid2186087\charrsid2186087 +\par }\pard \ltrpar\s15\ql \li340\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin340\itap0\pararsid5460125\contextualspace {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid2175529 +\hich\af39\dbch\af31505\loch\f39 The main component is the large viewer panel where the assets are displayed. Status messages (e.g. if a texture could not be loaded) are displayed in the upper-left edge.}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid9194405\charrsid2175529 +\par }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid9194405 +\par {\listtext\pard\plain\ltrpar \s15 \rtlch\fcs1 \af0 \ltrch\fcs0 \f39\lang1033\langfe1031\langnp1033\insrsid5460125 \hich\af39\dbch\af31505\loch\f39 III.\tab}}\pard \ltrpar\s15\ql \fi-360\li340\ri0\sa200\sl276\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls2\adjustright\rin0\lin340\itap0\pararsid3683098\contextualspace {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid5460125 \hich\af39\dbch\af31505\loch\f39 Input}{ +\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid5460125\charrsid3683098 +\par }\pard \ltrpar\s15\ql \li340\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin340\itap0\pararsid9635109\contextualspace {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid3683098\charrsid3683098 \hich\af39\dbch\af31505\loch\f39 The input mode depends on the selected input }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid8350123\charrsid3683098 \hich\af39\dbch\af31505\loch\f39 behavior}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid3683098\charrsid3683098 \hich\af39\dbch\af31505\loch\f39 +\hich\f39 . If \'84}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid3683098\charrsid8350123 \hich\af39\dbch\af31505\loch\f39 Zoom/Rotate}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid8350123 \loch\af39\dbch\af31505\hich\f39 \'94\loch\f39 }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid3683098\charrsid3683098 \hich\af39\dbch\af31505\loch\f39 is }{ +\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \b\f39\fs20\lang1033\langfe1031\langnp1033\insrsid3683098\charrsid8350123 \hich\af39\dbch\af31505\loch\f39 NOT}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid3683098\charrsid3683098 +\hich\af39\dbch\af31505\loch\f39 selected the following scheme applies:}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109 +\par }\pard \ltrpar\s15\ql \fi368\li340\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin340\itap0\pararsid9635109\contextualspace {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid9635109 \loch\af39\dbch\af31505\hich\f39 \lquote }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid8745149 \hich\af39\dbch\af31505\loch\f39 Arrow up}{ +\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid9635109 \loch\af39\dbch\af31505\hich\f39 \rquote \loch\f39 }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109 +\tab }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid9635109 \hich\af39\dbch\af31505\loch\f39 = Move forward}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109 +\hich\af39\dbch\af31505\loch\f39 s +\par }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid9635109 \loch\af39\dbch\af31505\hich\f39 \lquote }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid74094 \hich\af39\dbch\af31505\loch\f39 A}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid8745149 \hich\af39\dbch\af31505\loch\f39 rrow left}{ +\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid74094 \loch\af39\dbch\af31505\hich\f39 \rquote }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid9635109 \hich\af39\dbch\af31505\loch\f39 }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109 \tab }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid9635109 \hich\af39\dbch\af31505\loch\f39 = Move }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109 \hich\af39\dbch\af31505\loch\f39 to the left + +\par }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid9635109 \loch\af39\dbch\af31505\hich\f39 \lquote }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid8745149 +\hich\af39\dbch\af31505\loch\f39 Arrow down}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid9635109 \loch\af39\dbch\af31505\hich\f39 \rquote \loch\f39 }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109 \tab }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid9635109 \hich\af39\dbch\af31505\loch\f39 = }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109 \hich\af39\dbch\af31505\loch\f39 Move backwards +\par }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid9635109 \loch\af39\dbch\af31505\hich\f39 \lquote }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid8745149 +\hich\af39\dbch\af31505\loch\f39 Arrow right}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid9635109 \loch\af39\dbch\af31505\hich\f39 \rquote \loch\f39 }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109 \tab }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid9635109 \hich\af39\dbch\af31505\loch\f39 = }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109 \hich\af39\dbch\af31505\loch\f39 Move to the right +\par }\pard\plain \ltrpar\ql \fi340\li0\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid9635109 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 +\fs22\lang1031\langfe1031\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1031\langfenp1031 {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid9635109 \hich\af39\dbch\af31505\loch\f39 The}{\rtlch\fcs1 +\af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109 \hich\af39\dbch\af31505\loch\f39 mouse specifies the view direction. This is the typical FPS input behavior. +\par }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \b\f39\fs20\lang1033\langfe1031\langnp1033\insrsid14293319\charrsid67262 \hich\af39\dbch\af31505\loch\f39 \hich\f39 Otherwise, if \'84}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\b\i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid14293319\charrsid67262 \hich\af39\dbch\af31505\loch\f39 Zoom/Rotate}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \b\f39\fs20\lang1033\langfe1031\langnp1033\insrsid14293319\charrsid67262 +\loch\af39\dbch\af31505\hich\f39 \'94\loch\f39 is enabled: +\par }\pard \ltrpar\ql \li340\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin340\itap0\pararsid14293319 {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid14293319 +\loch\af39\dbch\af31505\hich\f39 \'93}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid14293319\charrsid74094 \hich\af39\dbch\af31505\loch\f39 Left Mouse Button}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid14293319 \loch\af39\dbch\af31505\hich\f39 \'94\loch\f39 = Keep pressed and move the mouse to rotate the asset around its local axes. Inside the yellow circle: x/y-axis, outside: Z-axis}{\rtlch\fcs1 +\af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid11029790 \hich\af39\dbch\af31505\loch\f39 . To rotate around one axis only use the axis snap-ins (yellow squar\hich\af39\dbch\af31505\loch\f39 es with a cross inside).}{\rtlch\fcs1 +\af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid14293319 +\par \loch\af39\dbch\af31505\hich\f39 \'93}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid14293319\charrsid74094 \hich\af39\dbch\af31505\loch\f39 Right Mouse Button}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid14293319 \loch\af39\dbch\af31505\hich\f39 \'94\loch\f39 = Keep pressed and move the mouse to rotate the light source(s) around their x and y axes. +\par \loch\af39\dbch\af31505\hich\f39 \'93}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid14293319\charrsid74094 \hich\af39\dbch\af31505\loch\f39 Middle Mouse Button}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid14293319 \loch\af39\dbch\af31505\hich\f39 \'94\loch\f39 = Keep pressed and move the mouse }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid1405492 +\hich\af39\dbch\af31505\loch\f39 from the left to the right or the other way round to increase/decrease the intensity of the light source(s)}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid14293319 +\par }\pard \ltrpar\ql \li340\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin340\itap0\pararsid7677524 {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid7677524 +\loch\af39\dbch\af31505\hich\f39 \'93}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid7677524\charrsid74094 \hich\af39\dbch\af31505\loch\f39 Right }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\b\i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid7677524\charrsid7677524 \hich\af39\dbch\af31505\loch\f39 AND}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid7677524 \hich\af39\dbch\af31505\loch\f39 Left }{ +\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid7677524\charrsid74094 \hich\af39\dbch\af31505\loch\f39 Mouse Button}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid7677524 +\loch\af39\dbch\af31505\hich\f39 \'94\loch\f39 = Keep pressed and move the mouse to rotate \hich\af39\dbch\af31505\loch\f39 skybox (\hich\af39\dbch\af31505\loch\f39 if existing\hich\af39\dbch\af31505\loch\f39 )\hich\af39\dbch\af31505\loch\f39 + around the global x/y axes. +\par }\pard \ltrpar\ql \li340\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin340\itap0\pararsid14293319 {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid7360491 +\loch\af39\dbch\af31505\hich\f39 \'93}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid7360491 \hich\af39\dbch\af31505\loch\f39 Mouse wheel}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid7360491 \loch\af39\dbch\af31505\hich\f39 \'94\loch\f39 = Zoom in/out +\par {\listtext\pard\plain\ltrpar \s15 \rtlch\fcs1 \af0 \ltrch\fcs0 \f39\lang1033\langfe1031\langnp1033\insrsid10752424 \hich\af39\dbch\af31505\loch\f39 IV.\tab}}\pard\plain \ltrpar\s15\ql \fi-360\li340\ri0\sa200\sl276\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls2\adjustright\rin0\lin340\itap0\pararsid10752424\contextualspace \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 +\fs22\lang1031\langfe1031\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1031\langfenp1031 {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid10752424 \hich\af39\dbch\af31505\loch\f39 Rendering +\par }\pard \ltrpar\s15\ql \li340\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin340\itap0\pararsid10752424\contextualspace {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid10752424 +\hich\af39\dbch\af31505\loch\f39 Enabling MultiSampling improves rendering quality. MultiSampling is activated by default. The highest qual\hich\af39\dbch\af31505\loch\f39 ity MultiSampling mode supported by the video card is used. +\par }\pard \ltrpar\s15\ql \li340\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin340\itap0\pararsid2961835\contextualspace {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid10752424 +\hich\af39\dbch\af31505\loch\f39 MultiSampling is especially useful to remove line artifacts in wireframe/normals mode. Note that the transition between normal and multisampled rendering may take a few seconds.}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid14187505\charrsid2961835 +\par }\pard \ltrpar\s15\ql \li340\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin340\itap0\pararsid10752424\contextualspace {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid14187505 +\hich\af39\dbch\af31505\loch\f39 Rendering is done \hich\af39\dbch\af31505\loch\f39 via Direct3D}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid14187505\charrsid14187505 \loch\af39\dbch\af31505\hich\f39 \'99}{ +\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5860386 \hich\af39\dbch\af31505\loch\f39 9.0c.}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid14187505 +\hich\af39\dbch\af31505\loch\f39 \hich\f39 Note that the \'93}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid14187505\charrsid14187505 \hich\af39\dbch\af31505\loch\f39 Low-quality lighting}{\rtlch\fcs1 \af0\afs20 +\ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid14187505 \loch\af39\dbch\af31505\hich\f39 \'94\loch\f39 -Option is not available for PS 2.0 cards. Lighting on PS 2.0 cards is always low quality. +\par }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid14115297 +\par {\listtext\pard\plain\ltrpar \s15 \rtlch\fcs1 \af0 \ltrch\fcs0 \f39\lang1033\langfe1031\langnp1033\insrsid14115297 \hich\af39\dbch\af31505\loch\f39 V.\tab}}\pard \ltrpar\s15\ql \fi-360\li340\ri0\sa200\sl276\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls2\adjustright\rin0\lin340\itap0\pararsid14115297\contextualspace {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid14115297 \hich\af39\dbch\af31505\loch\f39 Known issues}{ +\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid14115297\charrsid3683098 \hich\af39\dbch\af31505\loch\f39 }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid14115297 +\par {\listtext\pard\plain\ltrpar \s15 \rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid14115297 \hich\af39\dbch\af31505\loch\f39 -\tab}}\pard \ltrpar\s15\ql \fi-360\li700\ri0\sa200\sl276\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls4\adjustright\rin0\lin700\itap0\pararsid2961835\contextualspace {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid14115297 \hich\af39\dbch\af31505\loch\f39 +If a loading process is canceled it is not guaranteed that further loading processes will succeed. ASSIMP Viewer might even crash in this case. }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid14115297\charrsid2961835 + +\par {\listtext\pard\plain\ltrpar \s15 \rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid2961835 \hich\af39\dbch\af31505\loch\f39 -\tab}}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid2961835 \hich\af39\dbch\af31505\loch\f39 Some very complex materials involving real time reflection/refraction are not displayed properly.}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 +\f39\fs24\lang1033\langfe1031\langnp1033\insrsid2961835\charrsid2961835 +\par {\listtext\pard\plain\ltrpar \s15 \rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid2961835 \hich\af39\dbch\af31505\loch\f39 -\tab}}\pard \ltrpar\s15\ql \fi-360\li700\ri0\sa200\sl276\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls4\adjustright\rin0\lin700\itap0\pararsid9635109\contextualspace {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid2961835 \hich\af39\dbch\af31505\loch\f39 When rend +\hich\af39\dbch\af31505\loch\f39 ering non-opaque objects some triangle artifacts might occur}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid10617294 .}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 +\f39\fs24\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid10617294 +\par }{\*\themedata 504b030414000600080000002100828abc13fa0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb6ac3301045f785fe83d0b6d8 +72ba28a5d8cea249777d2cd20f18e4b12d6a8f843409c9df77ecb850ba082d74231062ce997b55ae8fe3a00e1893f354e9555e6885647de3a8abf4fbee29bbd7 +2a3150038327acf409935ed7d757e5ee14302999a654e99e393c18936c8f23a4dc072479697d1c81e51a3b13c07e4087e6b628ee8cf5c4489cf1c4d075f92a0b +44d7a07a83c82f308ac7b0a0f0fbf90c2480980b58abc733615aa2d210c2e02cb04430076a7ee833dfb6ce62e3ed7e14693e8317d8cd0433bf5c60f53fea2fe7 +065bd80facb647e9e25c7fc421fd2ddb526b2e9373fed4bb902e182e97b7b461e6bfad3f010000ffff0300504b030414000600080000002100a5d6a7e7c00000 +00360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4fc7060abb08 +84a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b63095120f88d94fbc +52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462a1a82fe353 +bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f7468656d652f7468 +656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b4b0d592c9c +070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b4757e8d3f7 +29e245eb2b260a0238fd010000ffff0300504b03041400060008000000210096b5ade296060000501b0000160000007468656d652f7468656d652f7468656d65 +312e786d6cec594f6fdb3614bf0fd87720746f6327761a07758ad8b19b2d4d1bc46e871e698996d850a240d2497d1bdae38001c3ba618715d86d87615b8116d8 +a5fb34d93a6c1dd0afb0475292c5585e9236d88aad3e2412f9e3fbff1e1fa9abd7eec70c1d1221294fda5efd72cd4324f1794093b0eddd1ef62fad79482a9c04 +98f184b4bd2991deb58df7dfbb8ad755446282607d22d771db8b944ad79796a40fc3585ee62949606ecc458c15bc8a702910f808e8c66c69b9565b5d8a314d3c +94e018c8de1a8fa94fd05093f43672e23d06af89927ac06762a049136785c10607758d9053d965021d62d6f6804fc08f86e4bef210c352c144dbab999fb7b471 +7509af678b985ab0b6b4ae6f7ed9ba6c4170b06c788a705430adf71bad2b5b057d03606a1ed7ebf5babd7a41cf00b0ef83a6569632cd467faddec9699640f671 +9e76b7d6ac355c7c89feca9cccad4ea7d36c65b258a206641f1b73f8b5da6a6373d9c11b90c537e7f08dce66b7bbeae00dc8e257e7f0fd2badd5868b37a088d1 +e4600ead1ddaef67d40bc898b3ed4af81ac0d76a197c86826828a24bb318f3442d8ab518dfe3a20f000d6458d104a9694ac6d88728eee2782428d60cf03ac1a5 +193be4cbb921cd0b495fd054b5bd0f530c1931a3f7eaf9f7af9e3f45c70f9e1d3ff8e9f8e1c3e3073f5a42ceaa6d9c84e5552fbffdeccfc71fa33f9e7ef3f2d1 +17d57859c6fffac327bffcfc793510d26726ce8b2f9ffcf6ecc98baf3efdfdbb4715f04d814765f890c644a29be408edf3181433567125272371be15c308d3f2 +8acd249438c19a4b05fd9e8a1cf4cd296699771c393ac4b5e01d01e5a30a787d72cf1178108989a2159c77a2d801ee72ce3a5c545a6147f32a99793849c26ae6 +6252c6ed637c58c5bb8b13c7bfbd490a75330f4b47f16e441c31f7184e140e494214d273fc80900aedee52ead87597fa824b3e56e82e451d4c2b4d32a423279a +668bb6690c7e9956e90cfe766cb37b077538abd27a8b1cba48c80acc2a841f12e698f13a9e281c57911ce298950d7e03aba84ac8c154f8655c4f2af074481847 +bd804859b5e696007d4b4edfc150b12addbecba6b18b148a1e54d1bc81392f23b7f84137c2715a851dd0242a633f900710a218ed715505dfe56e86e877f0034e +16bafb0e258ebb4faf06b769e888340b103d3311da9750aa9d0a1cd3e4efca31a3508f6d0c5c5c398602f8e2ebc71591f5b616e24dd893aa3261fb44f95d843b +5974bb5c04f4edafb95b7892ec1108f3f98de75dc97d5772bdff7cc95d94cf672db4b3da0a6557f70db629362d72bcb0431e53c6066acac80d699a6409fb44d0 +8741bdce9c0e4971624a2378cceaba830b05366b90e0ea23aaa241845368b0eb9e2612ca8c742851ca251ceccc70256d8d87265dd96361531f186c3d9058edf2 +c00eafe8e1fc5c509031bb4d680e9f39a3154de0accc56ae644441edd76156d7429d995bdd88664a9dc3ad50197c38af1a0c16d684060441db02565e85f3b966 +0d0713cc48a0ed6ef7dedc2dc60b17e92219e180643ed27acffba86e9c94c78ab90980d8a9f0913ee49d62b512b79626fb06dccee2a432bbc60276b9f7dec44b +7904cfbca4f3f6443ab2a49c9c2c41476dafd55c6e7ac8c769db1bc399161ee314bc2e75cf8759081743be1236ec4f4d6693e5336fb672c5dc24a8c33585b5fb +9cc24e1d4885545b58463634cc5416022cd19cacfccb4d30eb45296023fd35a458598360f8d7a4003bbaae25e331f155d9d9a5116d3bfb9a95523e51440ca2e0 +088dd844ec6370bf0e55d027a012ae264c45d02f708fa6ad6da6dce29c255df9f6cae0ec38666984b372ab5334cf640b37795cc860de4ae2816e95b21be5ceaf +8a49f90b52a51cc6ff3355f47e0237052b81f6800fd7b802239daf6d8f0b1571a8426944fdbe80c6c1d40e8816b88b8569082ab84c36ff0539d4ff6dce591a26 +ade1c0a7f669880485fd484582903d284b26fa4e2156cff62e4b9265844c4495c495a9157b440e091bea1ab8aaf7760f4510eaa69a6465c0e04ec69ffb9e65d0 +28d44d4e39df9c1a52ecbd3607fee9cec7263328e5d661d3d0e4f62f44acd855ed7ab33cdf7bcb8ae889599bd5c8b3029895b6825696f6af29c239b75a5bb1e6 +345e6ee6c28117e73586c1a2214ae1be07e93fb0ff51e133fb65426fa843be0fb515c187064d0cc206a2fa926d3c902e907670048d931db4c1a44959d366ad93 +b65abe595f70a75bf03d616c2dd959fc7d4e6317cd99cbcec9c58b34766661c7d6766ca1a9c1b327531486c6f941c638c67cd22a7f75e2a37be0e82db8df9f30 +254d30c1372581a1f51c983c80e4b71ccdd28dbf000000ffff0300504b0304140006000800000021000dd1909fb60000001b010000270000007468656d652f74 +68656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f78277086f6fd3ba109126dd88d0add40384e4350d363f24 +51eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89d93b64b060828e6f37ed1567914b284d262452282e3198 +720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5001996509affb3fd381a89672f1f165dfe514173d9850528 +a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100828abc13fa0000001c0200001300000000000000000000000000 +000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6a7e7c0000000360100000b000000000000000000000000 +002b0100005f72656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a0000001c00000000000000000000000000140200007468 +656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d001400060008000000210096b5ade296060000501b000016000000000000000000 +00000000d10200007468656d652f7468656d652f7468656d65312e786d6c504b01022d00140006000800000021000dd1909fb60000001b010000270000000000 +00000000000000009b0900007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000960a00000000} +{\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d +617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169 +6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363 +656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e} +{\*\latentstyles\lsdstimax267\lsdlockeddef0\lsdsemihiddendef1\lsdunhideuseddef1\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4; +\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9; +\lsdpriority39 \lsdlocked0 toc 1;\lsdpriority39 \lsdlocked0 toc 2;\lsdpriority39 \lsdlocked0 toc 3;\lsdpriority39 \lsdlocked0 toc 4;\lsdpriority39 \lsdlocked0 toc 5;\lsdpriority39 \lsdlocked0 toc 6;\lsdpriority39 \lsdlocked0 toc 7; +\lsdpriority39 \lsdlocked0 toc 8;\lsdpriority39 \lsdlocked0 toc 9;\lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdpriority1 \lsdlocked0 Default Paragraph Font; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority22 \lsdlocked0 Strong;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority59 \lsdlocked0 Table Grid;\lsdunhideused0 \lsdlocked0 Placeholder Text;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 1;\lsdunhideused0 \lsdlocked0 Revision; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdpriority37 \lsdlocked0 Bibliography;\lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;}}{\*\datastore 010500000200000018000000 +4d73786d6c322e534158584d4c5265616465722e352e3000000000000000000000060000 +d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff090006000000000000000000000001000000010000000000000000100000feffffff00000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffffec69d9888b8b3d4c859eaf6cd158be0f00000000000000000000000060e3 +1310819dc801feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000105000000000000}} \ No newline at end of file diff --git a/tools/assimp_view/resource.h b/tools/assimp_view/resource.h new file mode 100644 index 000000000..efaba4434 --- /dev/null +++ b/tools/assimp_view/resource.h @@ -0,0 +1,90 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by assimp_view.rc +// +#define IDC_MYICON 2 +#define IDD_ASSIMP_VIEW_DIALOG 102 +#define IDD_ABOUTBOX 103 +#define IDI_ASSIMP_VIEW 107 +#define IDI_SMALL 108 +#define IDR_MAINFRAME 128 +#define IDD_DIALOGMAIN 129 +#define IDB_BITMAP1 130 +#define IDR_MENU1 131 +#define IDD_LOADDIALOG 132 +#define IDB_BITMAP2 134 +#define IDD_AVHELP 135 +#define IDR_TEXT1 138 +#define IDR_MAX 140 +#define IDB_MAXCIRCLEMASK 141 +#define IDB_MAXCIRCLE 142 +#define IDR_HUD 143 +#define IDR_HUDMASK 144 +#define IDC_CHECK1 1000 +#define IDC_TOGGLEMS 1000 +#define IDC_CHECK2 1001 +#define IDC_TOGGLEWIRE 1001 +#define IDC_CHECK3 1002 +#define IDC_TOGGLEMAT 1002 +#define IDC_CHECK4 1003 +#define IDC_TOGGLENORMALS 1003 +#define IDC_CHECK5 1004 +#define IDC_AUTOROTATE 1004 +#define IDC_RT 1006 +#define IDC_NUMVERTS 1007 +#define IDC_NUMFACES 1008 +#define IDC_NUMMATS 1009 +#define IDC_FPS 1010 +#define IDC_EFPS 1011 +#define IDC_EMAT 1012 +#define IDC_EFACE 1013 +#define IDC_EVERT 1014 +#define IDC_CHECK6 1015 +#define IDC_LIGHTROTATE 1015 +#define IDC_3LIGHTS 1016 +#define IDC_PROGRESS 1016 +#define IDC_LOADTIME 1017 +#define IDC_ELOAD 1018 +#define IDC_CHECK7 1019 +#define IDC_ZOOM 1019 +#define IDC_CHECK8 1020 +#define IDC_LOWQUALITY 1020 +#define IDC_NUMMATS2 1021 +#define IDC_ESHADER 1022 +#define IDC_RICHEDIT21 1023 +#define IDC_CHECK9 1024 +#define IDC_NOSPECULAR 1024 +#define IDC_PLAYANIM 1025 +#define IDC_SPEED 1026 +#define IDC_COMBO1 1027 +#define IDC_PINORDER 1028 +#define IDC_SSPEED 1029 +#define IDC_SANIM 1030 +#define IDC_SANIMGB 1031 +#define ID_VIEWER_OPEN 32771 +#define ID_VIEWER_CLOSETHIS 32772 +#define ID_VIEWER_CLOSEASSET 32773 +#define ID_VIEWER_QUIT 32774 +#define ID__ABOUT 32775 +#define ID__HELP 32776 +#define ID_VIEWER_SAVESCREENSHOTTOFILE 32777 +#define ID_VIEWER_RESETVIEW 32778 +#define ID_BACKGROUND_LOADTEXTURE 32779 +#define ID_BACKGROUND_CLEAR 32780 +#define ID_BACKGROUND_SETCOLOR 32781 +#define ID_Menu 32782 +#define ID_BACKGROUND_LOADSKYBOX 32783 +#define ID_VIEWER_H 32784 +#define IDC_STATIC -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 145 +#define _APS_NEXT_COMMAND_VALUE 32785 +#define _APS_NEXT_CONTROL_VALUE 1032 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif diff --git a/tools/assimp_view/small.ico b/tools/assimp_view/small.ico new file mode 100644 index 000000000..d551aa3aa Binary files /dev/null and b/tools/assimp_view/small.ico differ diff --git a/tools/assimp_view/stdafx.cpp b/tools/assimp_view/stdafx.cpp new file mode 100644 index 000000000..c7402619a --- /dev/null +++ b/tools/assimp_view/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : Quelldatei, die nur die Standard-Includes einbindet. +// assimp_view.pch ist der vorkompilierte Header. +// stdafx.obj enthält die vorkompilierten Typinformationen. + +#include "stdafx.h" + +// TODO: Auf zusätzliche Header verweisen, die in STDAFX.H +// und nicht in dieser Datei erforderlich sind. diff --git a/tools/assimp_view/stdafx.h b/tools/assimp_view/stdafx.h new file mode 100644 index 000000000..3e8b68247 --- /dev/null +++ b/tools/assimp_view/stdafx.h @@ -0,0 +1,70 @@ +// stdafx.h : Includedatei für Standardsystem-Includedateien +// oder häufig verwendete projektspezifische Includedateien, +// die nur in unregelmäßigen Abständen geändert werden. +// + +#pragma once + +// Ändern Sie folgende Definitionen für Plattformen, die älter als die unten angegebenen sind. +// In MSDN finden Sie die neuesten Informationen über die entsprechenden Werte für die unterschiedlichen Plattformen. +#ifndef WINVER // Lassen Sie die Verwendung spezifischer Features von Windows XP oder später zu. +# define WINVER 0x0501 // Ändern Sie dies in den geeigneten Wert für andere Versionen von Windows. +#endif + +#ifndef _WIN32_WINNT // Lassen Sie die Verwendung spezifischer Features von Windows XP oder später zu. +# define _WIN32_WINNT 0x0501 // Ändern Sie dies in den geeigneten Wert für andere Versionen von Windows. +#endif + +#ifndef _WIN32_WINDOWS // Lassen Sie die Verwendung spezifischer Features von Windows 98 oder später zu. +# define _WIN32_WINDOWS 0x0410 // Ändern Sie dies in den geeigneten Wert für Windows Me oder höher. +#endif + +#ifndef _WIN32_IE // Lassen Sie die Verwendung spezifischer Features von IE 6.0 oder später zu. +#define _WIN32_IE 0x0600 // Ändern Sie dies in den geeigneten Wert für andere Versionen von IE. +#endif + +#define WIN32_LEAN_AND_MEAN // Selten verwendete Teile der Windows-Header nicht einbinden. +// Windows-Headerdateien: +#include + +// C RunTime-Headerdateien +#include +#include +#include +#include +#include + +// D3D9 includes + +#if (defined _DEBUG) +# define D3D_DEBUG_INFO +#endif + +#include +#include +#include + +// ShellExecute() +#include +#include + +// GetOpenFileName() +#include +#include +#include + +#include +#include +#include +#include + +// Windows CommonControls 6.0 Manifest Extensions +#if defined _M_IX86 +# pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") +#elif defined _M_IA64 +# pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"") +#elif defined _M_X64 +# pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") +#else +# pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") +#endif diff --git a/tools/assimp_view/test.xcf b/tools/assimp_view/test.xcf new file mode 100644 index 000000000..3f93df5bf Binary files /dev/null and b/tools/assimp_view/test.xcf differ diff --git a/tools/assimp_view/text1.bin b/tools/assimp_view/text1.bin new file mode 100644 index 000000000..82d87095a --- /dev/null +++ b/tools/assimp_view/text1.bin @@ -0,0 +1,370 @@ +{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff31507\deff0\stshfdbch31505\stshfloch31506\stshfhich31506\stshfbi0\deflang1031\deflangfe1031\themelang1031\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f2\fbidi \fmodern\fcharset0\fprq1{\*\panose 02070309020205020404}Courier New;} +{\f3\fbidi \froman\fcharset2\fprq2{\*\panose 05050102010706020507}Symbol;}{\f10\fbidi \fnil\fcharset2\fprq2{\*\panose 05000000000000000000}Wingdings;}{\f34\fbidi \froman\fcharset0\fprq2{\*\panose 02040503050406030204}Cambria Math;} +{\f38\fbidi \fswiss\fcharset0\fprq2{\*\panose 020b0604030504040204}Tahoma;}{\f39\fbidi \fswiss\fcharset0\fprq2{\*\panose 00000000000000000000}MS Reference Sans Serif;} +{\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbmajor\f31501\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\fhimajor\f31502\fbidi \froman\fcharset0\fprq2{\*\panose 02040503050406030204}Cambria;}{\fbimajor\f31503\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\flominor\f31504\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbminor\f31505\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\fhiminor\f31506\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;}{\fbiminor\f31507\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f41\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} +{\f42\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\f44\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f45\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f46\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\f47\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f48\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f49\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f61\fbidi \fmodern\fcharset238\fprq1 Courier New CE;} +{\f62\fbidi \fmodern\fcharset204\fprq1 Courier New Cyr;}{\f64\fbidi \fmodern\fcharset161\fprq1 Courier New Greek;}{\f65\fbidi \fmodern\fcharset162\fprq1 Courier New Tur;}{\f66\fbidi \fmodern\fcharset177\fprq1 Courier New (Hebrew);} +{\f67\fbidi \fmodern\fcharset178\fprq1 Courier New (Arabic);}{\f68\fbidi \fmodern\fcharset186\fprq1 Courier New Baltic;}{\f69\fbidi \fmodern\fcharset163\fprq1 Courier New (Vietnamese);}{\f381\fbidi \froman\fcharset238\fprq2 Cambria Math CE;} +{\f382\fbidi \froman\fcharset204\fprq2 Cambria Math Cyr;}{\f384\fbidi \froman\fcharset161\fprq2 Cambria Math Greek;}{\f385\fbidi \froman\fcharset162\fprq2 Cambria Math Tur;}{\f388\fbidi \froman\fcharset186\fprq2 Cambria Math Baltic;} +{\f421\fbidi \fswiss\fcharset238\fprq2 Tahoma CE;}{\f422\fbidi \fswiss\fcharset204\fprq2 Tahoma Cyr;}{\f424\fbidi \fswiss\fcharset161\fprq2 Tahoma Greek;}{\f425\fbidi \fswiss\fcharset162\fprq2 Tahoma Tur;} +{\f426\fbidi \fswiss\fcharset177\fprq2 Tahoma (Hebrew);}{\f427\fbidi \fswiss\fcharset178\fprq2 Tahoma (Arabic);}{\f428\fbidi \fswiss\fcharset186\fprq2 Tahoma Baltic;}{\f429\fbidi \fswiss\fcharset163\fprq2 Tahoma (Vietnamese);} +{\f430\fbidi \fswiss\fcharset222\fprq2 Tahoma (Thai);}{\f431\fbidi \fswiss\fcharset238\fprq2 MS Reference Sans Serif CE;}{\f432\fbidi \fswiss\fcharset204\fprq2 MS Reference Sans Serif Cyr;} +{\f434\fbidi \fswiss\fcharset161\fprq2 MS Reference Sans Serif Greek;}{\f435\fbidi \fswiss\fcharset162\fprq2 MS Reference Sans Serif Tur;}{\f438\fbidi \fswiss\fcharset186\fprq2 MS Reference Sans Serif Baltic;} +{\f439\fbidi \fswiss\fcharset163\fprq2 MS Reference Sans Serif (Vietnamese);}{\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} +{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} +{\fdbmajor\f31518\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} +{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} +{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhimajor\f31528\fbidi \froman\fcharset238\fprq2 Cambria CE;} +{\fhimajor\f31529\fbidi \froman\fcharset204\fprq2 Cambria Cyr;}{\fhimajor\f31531\fbidi \froman\fcharset161\fprq2 Cambria Greek;}{\fhimajor\f31532\fbidi \froman\fcharset162\fprq2 Cambria Tur;} +{\fhimajor\f31535\fbidi \froman\fcharset186\fprq2 Cambria Baltic;}{\fbimajor\f31538\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} +{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} +{\flominor\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} +{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} +{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbminor\f31558\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} +{\fdbminor\f31559\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} +{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} +{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhiminor\f31568\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;} +{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;} +{\fbiminor\f31578\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} +{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} +{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0; +\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128; +\red192\green192\blue192;}{\*\defchp \fs22\loch\af31506\hich\af31506\dbch\af31505 }{\*\defpap \ql \li0\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{ +\ql \li0\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \fs22\lang1031\langfe1031\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1031\langfenp1031 +\snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive \sunhideused \spriority1 Default Paragraph Font;}{\* +\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tscellwidthfts0\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa200\sl276\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \fs22\lang1031\langfe1031\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1031\langfenp1031 +\snext11 \ssemihidden \sunhideused \sqformat Normal Table;}{\s15\ql \li720\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin720\itap0\contextualspace \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 +\fs22\lang1031\langfe1031\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1031\langfenp1031 \sbasedon0 \snext15 \sqformat \spriority34 \styrsid3683098 List Paragraph;}{ +\s16\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs16\alang1025 \ltrch\fcs0 \fs16\lang1031\langfe1031\loch\f38\hich\af38\dbch\af31505\cgrid\langnp1031\langfenp1031 +\sbasedon0 \snext16 \slink17 \ssemihidden \sunhideused \styrsid8273515 Balloon Text;}{\*\cs17 \additive \rtlch\fcs1 \af38\afs16 \ltrch\fcs0 \f38\fs16 \sbasedon10 \slink16 \slocked \ssemihidden \styrsid8273515 Balloon Text Char;}}{\*\listtable +{\list\listtemplateid-2111801688\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid1242219378\'01-;}{\levelnumbers;} +\loch\af39\hich\af39\dbch\af31505\fbias0\hres0\chhres0 \fi-360\li700\lin700 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567619\'01o;}{\levelnumbers;} +\f2\fbias0\hres0\chhres0 \fi-360\li1420\lin1420 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567621\'01\u-3929 ?;}{\levelnumbers;} +\f10\fbias0\hres0\chhres0 \fi-360\li2140\lin2140 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567617\'01\u-3913 ?;}{\levelnumbers;} +\f3\fbias0\hres0\chhres0 \fi-360\li2860\lin2860 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567619\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 +\fi-360\li3580\lin3580 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567621\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 +\fi-360\li4300\lin4300 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567617\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 +\fi-360\li5020\lin5020 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567619\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li5740\lin5740 } +{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567621\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li6460\lin6460 }{\listname +;}\listid256792316}{\list\listtemplateid1110873036\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat0\levelspace0\levelindent0{\leveltext\leveltemplateid680798618\'01-;}{\levelnumbers;} +\fs20\loch\af39\hich\af39\dbch\af31505\fbias0\hres0\chhres0 \fi-360\li700\lin700 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567619 +\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li1420\lin1420 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567621\'01\u-3929 ?;}{\levelnumbers +;}\f10\fbias0\hres0\chhres0 \fi-360\li2140\lin2140 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567617\'01\u-3913 ?;}{\levelnumbers;} +\f3\fbias0\hres0\chhres0 \fi-360\li2860\lin2860 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567619\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 +\fi-360\li3580\lin3580 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567621\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 +\fi-360\li4300\lin4300 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567617\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 +\fi-360\li5020\lin5020 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567619\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li5740\lin5740 } +{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567621\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li6460\lin6460 }{\listname +;}\listid483863136}{\list\listtemplateid338825902\listhybrid{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid-1769051580\'02\'00);}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \fbias0\hres0\chhres0 \fi-360\li700\lin700 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567641\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-360\li1420\lin1420 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567643\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-180\li2140\lin2140 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567631\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-360\li2860\lin2860 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567641\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-360\li3580\lin3580 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567643\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-180\li4300\lin4300 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567631\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-360\li5020\lin5020 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567641\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-360\li5740\lin5740 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567643\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-180\li6460\lin6460 }{\listname ;}\listid649408911}{\list\listtemplateid-371049066\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat0\levelspace360\levelindent0{\leveltext +\leveltemplateid680798618\'01-;}{\levelnumbers;}\fs20\loch\af39\hich\af39\dbch\af31505\fbias0\hres0\chhres0 \fi-360\li720\lin720 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0 +{\leveltext\leveltemplateid67567641\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0 +{\leveltext\leveltemplateid67567643\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li2160\lin2160 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0 +{\leveltext\leveltemplateid67567631\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li2880\lin2880 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0 +{\leveltext\leveltemplateid67567641\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li3600\lin3600 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0 +{\leveltext\leveltemplateid67567643\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li4320\lin4320 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0 +{\leveltext\leveltemplateid67567631\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0 +{\leveltext\leveltemplateid67567641\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0 +{\leveltext\leveltemplateid67567643\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li6480\lin6480 }{\listname ;}\listid780539952}{\list\listtemplateid1158591540\listhybrid{\listlevel\levelnfc1\levelnfcn1\leveljc2 +\leveljcn2\levelfollow0\levelstartat1\levelspace360\levelindent0{\leveltext\leveltemplateid67567635\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0\hres0\chhres0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc4\levelnfcn4\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567641\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li2160\lin2160 }{\listlevel\levelnfc2\levelnfcn2\leveljc2 +\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567643\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li2880\lin2880 }{\listlevel\levelnfc0\levelnfcn0\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567631\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li3600\lin3600 }{\listlevel\levelnfc4\levelnfcn4\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567641\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li4320\lin4320 }{\listlevel\levelnfc2\levelnfcn2\leveljc2 +\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567643\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li5040\lin5040 }{\listlevel\levelnfc0\levelnfcn0\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567631\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc4\levelnfcn4\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567641\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li6480\lin6480 }{\listlevel\levelnfc2\levelnfcn2\leveljc2 +\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567643\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li7200\lin7200 }{\listname ;}\listid823008862} +{\list\listtemplateid1985747632\listhybrid{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid-1722268864\'02\'00);}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 +\fbias0\hres0\chhres0 \fi-720\li1080\lin1080 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567641\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567643\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-180\li2160\lin2160 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567631\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-360\li2880\lin2880 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567641\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-360\li3600\lin3600 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567643\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-180\li4320\lin4320 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567631\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567641\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67567643\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \hres0\chhres0 \fi-180\li6480\lin6480 }{\listname ;}\listid1527327967}}{\*\listoverridetable{\listoverride\listid1527327967\listoverridecount0\ls1}{\listoverride\listid823008862\listoverridecount0\ls2}{\listoverride\listid256792316 +\listoverridecount0\ls3}{\listoverride\listid483863136\listoverridecount0\ls4}{\listoverride\listid649408911\listoverridecount0\ls5}{\listoverride\listid780539952\listoverridecount0\ls6}}{\*\rsidtbl \rsid67262\rsid74094\rsid602054\rsid1405492\rsid2175529 +\rsid2186087\rsid2961835\rsid3277197\rsid3683098\rsid5404161\rsid5460125\rsid5513066\rsid5860386\rsid7360491\rsid7429996\rsid7677524\rsid8273515\rsid8350123\rsid8745149\rsid9194405\rsid9529660\rsid9575707\rsid9635109\rsid9712802\rsid10617294\rsid10752424 +\rsid10762577\rsid11029790\rsid11880680\rsid12914594\rsid14115297\rsid14187505\rsid14293319}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\author Alexander Gessler} +{\operator Alexander Gessler}{\creatim\yr2008\mo4\dy12\hr23\min23}{\revtim\yr2008\mo4\dy13\hr18\min11}{\version32}{\edmins0}{\nofpages2}{\nofwords446}{\nofchars2811}{\*\company UtopicTechnologies}{\nofcharsws3251}{\vern32893}}{\*\xmlnstbl {\xmlns1 http:// +schemas.microsoft.com/office/word/2003/wordml}}\paperw11906\paperh16838\margl1417\margr1417\margt1417\margb1134\gutter0\ltrsect +\deftab708\widowctrl\ftnbj\aenddoc\hyphhotz425\trackmoves1\trackformatting1\donotembedsysfont1\relyonvml0\donotembedlingdata0\grfdocevents0\validatexml1\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0 +\showxmlerrors1\noxlattoyen\expshrtn\noultrlspc\dntblnsbdb\nospaceforul\formshade\horzdoc\dgmargin\dghspace180\dgvspace180\dghorigin1417\dgvorigin1417\dghshow1\dgvshow1 +\jexpand\viewkind1\viewscale100\pgbrdrhead\pgbrdrfoot\splytwnine\ftnlytwnine\htmautsp\nolnhtadjtbl\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\allowfieldendsel\wrppunct +\asianbrkrule\rsidroot3683098\newtblstyruls\nogrowautofit\usenormstyforlist\noindnmbrts\felnbrelev\nocxsptable\indrlsweleven\noafcnsttbl\afelev\utinl\hwelev\spltpgpar\notcvasp\notbrkcnstfrctbl\notvatxbx\krnprsnet\cachedcolbal \nouicompat \fet0 +{\*\wgrffmtfilter 2450}\nofeaturethrottle1\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\headery708\footery708\colsx708\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2 +\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6 +\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang +{\pntxtb (}{\pntxta )}}\pard\plain \ltrpar\ql \li0\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 +\fs22\lang1031\langfe1031\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1031\langfenp1031 {\rtlch\fcs1 \af0\afs32 \ltrch\fcs0 \b\f39\fs32\lang1033\langfe1031\langnp1033\insrsid3683098\charrsid3683098 \hich\af39\dbch\af31505\loch\f39 +ASSIMP Viewer Utility}{\rtlch\fcs1 \af0\afs32 \ltrch\fcs0 \b\f39\fs32\lang1033\langfe1031\langnp1033\insrsid10617294 \hich\af39\dbch\af31505\loch\f39 }{\rtlch\fcs1 \af0\afs32 \ltrch\fcs0 \b\f39\fs32\lang1033\langfe1031\langnp1033\insrsid11029790 +\par }{\rtlch\fcs1 \af0\afs16 \ltrch\fcs0 \b\f39\fs16\lang1033\langfe1031\langnp1033\insrsid10617294 \hich\af39\dbch\af31505\loch\f39 Document version 1.0, April 2008}{\rtlch\fcs1 \af0\afs16 \ltrch\fcs0 +\b\f39\fs16\lang1033\langfe1031\langnp1033\insrsid10617294\charrsid10617294 +\par }{\rtlch\fcs1 \af0\afs32 \ltrch\fcs0 \b\f39\fs32\lang1033\langfe1031\langnp1033\insrsid12914594 +\par {\listtext\pard\plain\ltrpar \s15 \rtlch\fcs1 \af0 \ltrch\fcs0 \f39\lang1033\langfe1031\langnp1033\insrsid12914594 \hich\af39\dbch\af31505\loch\f39 I.\tab}}\pard\plain \ltrpar\s15\ql \fi-360\li340\ri0\sa200\sl276\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls2\adjustright\rin0\lin340\itap0\pararsid3683098\contextualspace \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 +\fs22\lang1031\langfe1031\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1031\langfenp1031 {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid12914594 \hich\af39\dbch\af31505\loch\f39 Usage}{\rtlch\fcs1 \af0\afs20 +\ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid12914594\charrsid12914594 \hich\af39\dbch\af31505\loch\f39 }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid12914594\charrsid12914594 +\par }\pard \ltrpar\s15\ql \li340\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin340\itap0\pararsid12914594\contextualspace {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid12914594\charrsid3683098 \hich\af39\dbch\af31505\loch\f39 The }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid12914594 \hich\af39\dbch\af31505\loch\f39 ASSIMP Viewer +\hich\af39\dbch\af31505\loch\f39 Utility is a small and fast stand-alone viewer utility which is using the ASSIMP library to import assets from files. It consists of a si}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid9575707 \hich\af39\dbch\af31505\loch\f39 ngle executable file and has no}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid12914594 \hich\af39\dbch\af31505\loch\f39 + external dependencies.}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9529660 \hich\af39\dbch\af31505\loch\f39 It will display assets with complex materials and animations correctly.}{\rtlch\fcs1 \af0\afs20 +\ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid12914594 +\par }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9575707 +\par }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \b\f39\fs20\lang1033\langfe1031\langnp1033\insrsid12914594\charrsid12914594 \hich\af39\dbch\af31505\loch\f39 System requirements}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid12914594 \hich\af39\dbch\af31505\loch\f39 : +\par {\listtext\pard\plain\ltrpar \s15 \rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5860386 \hich\af39\dbch\af31505\loch\f39 -\tab}}\pard \ltrpar\s15\ql \fi-360\li700\ri0\sa200\sl276\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls4\adjustright\rin0\lin700\itap0\pararsid5860386\contextualspace {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5860386 \hich\af39\dbch\af31505\loch\f39 +A Direct3D 9.0c compliant video card with at least support for Shader Model 2.0.}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid5860386\charrsid9575707 +\par {\listtext\pard\plain\ltrpar \s15 \rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9575707 \hich\af39\dbch\af31505\loch\f39 -\tab}}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid9575707 \hich\af39\dbch\af31505\loch\f39 Shader Model 3.0 cards are recommended}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid9575707\charrsid5860386 +\par {\listtext\pard\plain\ltrpar \s15 \rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5860386 \hich\af39\dbch\af31505\loch\f39 -\tab}}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid5860386 \hich\af39\dbch\af31505\loch\f39 Windows 2000 or higher}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid5860386\charrsid5860386 +\par }\pard \ltrpar\s15\ql \li340\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin340\itap0\pararsid12914594\contextualspace {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 +\f39\fs24\lang1033\langfe1031\langnp1033\insrsid3683098\charrsid3683098 \hich\af39\dbch\af31505\loch\f39 }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid3683098 +\par {\listtext\pard\plain\ltrpar \s15 \rtlch\fcs1 \af0 \ltrch\fcs0 \f39\lang1033\langfe1031\langnp1033\insrsid5460125 \hich\af39\dbch\af31505\loch\f39 II.\tab}}\pard \ltrpar\s15\ql \fi-360\li340\ri0\sa200\sl276\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls2\adjustright\rin0\lin340\itap0\pararsid3683098\contextualspace {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid5460125 \hich\af39\dbch\af31505\loch\f39 User i +\hich\af39\dbch\af31505\loch\f39 nterface}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid12914594 +\par }\pard \ltrpar\s15\ql \li340\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin340\itap0\pararsid9712802\contextualspace {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid5460125\charrsid3683098 \hich\af39\dbch\af31505\loch\f39 The }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5460125 \hich\af39\dbch\af31505\loch\f39 viewer\hich\f39 +\rquote \loch\f39 s user interface mainly consists}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5513066 \hich\af39\dbch\af31505\loch\f39 of three components:}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid602054 \hich\af39\dbch\af31505\loch\f39 }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5460125\charrsid9712802 +\par }\pard\plain \ltrpar\ql \fi340\li0\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid2186087 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 +\fs22\lang1031\langfe1031\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1031\langfenp1031 {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5404161 \hich\af39\dbch\af31505\loch\f39 +The menu bar provides access to the viewer\hich\f39 \rquote \loch\f39 s basic options.}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9712802\charrsid2186087 +\par {\listtext\pard\plain\ltrpar \s15 \rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5404161 \hich\af39\dbch\af31505\loch\f39 -\tab}}\pard\plain \ltrpar\s15\ql \fi-360\li720\ri0\sa200\sl276\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls6\adjustright\rin0\lin720\itap0\pararsid5404161\contextualspace \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 +\fs22\lang1031\langfe1031\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1031\langfenp1031 {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5404161 \loch\af39\dbch\af31505\hich\f39 \'93}{\rtlch\fcs1 \af0\afs20 +\ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid5404161\charrsid9712802 \hich\af39\dbch\af31505\loch\f39 Viewer}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5404161 \loch\af39\dbch\af31505\hich\f39 \'94 +\loch\f39 : Items to load / unload assets, take screenshots and open the viewer\hich\f39 \rquote \loch\f39 s options dialog. +\par {\listtext\pard\plain\ltrpar \s15 \rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5404161 \hich\af39\dbch\af31505\loch\f39 -\tab}}\pard \ltrpar\s15\ql \fi-360\li720\ri0\sa200\sl276\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls6\adjustright\rin0\lin720\itap0\pararsid3277197\contextualspace {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5404161 \loch\af39\dbch\af31505\hich\f39 \'93}{\rtlch\fcs1 +\af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid5404161\charrsid9712802 \hich\af39\dbch\af31505\loch\f39 Background}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5404161 +\loch\af39\dbch\af31505\hich\f39 \'94\loch\f39 : \hich\af39\dbch\af31505\loch\f39 Set the background color or use a texture (cubemaps are supported}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid11880680 +\hich\af39\dbch\af31505\loch\f39 as skyboxes}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5404161 \hich\af39\dbch\af31505\loch\f39 ) as background image for the viewer.}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid9194405\charrsid3277197 +\par }\pard \ltrpar\s15\ql \li340\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin340\itap0\pararsid5460125\contextualspace {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid602054 + +\par }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid3277197 \hich\af39\dbch\af31505\loch\f39 \hich\f39 The side panel \'93}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid3277197\charrsid3277197 \hich\af39\dbch\af31505\loch\f39 Statistics}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid3277197 \loch\af39\dbch\af31505\hich\f39 \'94 +\loch\f39 displays rendering statistics (\hich\af39\dbch\af31505\loch\f39 number of vertices loaded, number of faces, number of materials, frames per second, time required for loading).}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid602054\charrsid9194405 +\par }\pard \ltrpar\s15\ql \li340\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin340\itap0\pararsid2186087\contextualspace {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid3277197 +\hich\af39\dbch\af31505\loch\f39 \hich\f39 The \'93}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid3277197\charrsid3277197 \hich\af39\dbch\af31505\loch\f39 Rendering}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid3277197 \loch\af39\dbch\af31505\hich\f39 \'94\loch\f39 \hich\f39 panel provides rendering-related options, such as wireframe/normal visualization. \'93}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid3277197\charrsid3277197 \hich\af39\dbch\af31505\loch\f39 Interaction}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid3277197 \loch\af39\dbch\af31505\hich\f39 \'94 +\loch\f39 bundles all input related option\hich\af39\dbch\af31505\loch\f39 s.}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid2186087\charrsid2186087 +\par }\pard \ltrpar\s15\ql \li340\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin340\itap0\pararsid5460125\contextualspace {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid2175529 +\hich\af39\dbch\af31505\loch\f39 The main component is the large viewer panel where the assets are displayed. Status messages (e.g. if a texture could not be loaded) are displayed in the upper-left edge.}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid9194405\charrsid2175529 +\par }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid9194405 +\par {\listtext\pard\plain\ltrpar \s15 \rtlch\fcs1 \af0 \ltrch\fcs0 \f39\lang1033\langfe1031\langnp1033\insrsid5460125 \hich\af39\dbch\af31505\loch\f39 III.\tab}}\pard \ltrpar\s15\ql \fi-360\li340\ri0\sa200\sl276\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls2\adjustright\rin0\lin340\itap0\pararsid3683098\contextualspace {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid5460125 \hich\af39\dbch\af31505\loch\f39 Input}{ +\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid5460125\charrsid3683098 +\par }\pard \ltrpar\s15\ql \li340\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin340\itap0\pararsid9635109\contextualspace {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid3683098\charrsid3683098 \hich\af39\dbch\af31505\loch\f39 The input mode depends on the selected input }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid8350123\charrsid3683098 \hich\af39\dbch\af31505\loch\f39 behavior}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid3683098\charrsid3683098 \hich\af39\dbch\af31505\loch\f39 +\hich\f39 . If \'84}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid3683098\charrsid8350123 \hich\af39\dbch\af31505\loch\f39 Zoom/Rotate}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid8350123 \loch\af39\dbch\af31505\hich\f39 \'94\loch\f39 }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid3683098\charrsid3683098 \hich\af39\dbch\af31505\loch\f39 is }{ +\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \b\f39\fs20\lang1033\langfe1031\langnp1033\insrsid3683098\charrsid8350123 \hich\af39\dbch\af31505\loch\f39 NOT}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid3683098\charrsid3683098 +\hich\af39\dbch\af31505\loch\f39 selected the following scheme applies:}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109 +\par }\pard \ltrpar\s15\ql \fi368\li340\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin340\itap0\pararsid9635109\contextualspace {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid9635109 \loch\af39\dbch\af31505\hich\f39 \lquote }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid8745149 \hich\af39\dbch\af31505\loch\f39 Arrow up}{ +\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid9635109 \loch\af39\dbch\af31505\hich\f39 \rquote \loch\f39 }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109 +\tab }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid9635109 \hich\af39\dbch\af31505\loch\f39 = Move forward}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109 +\hich\af39\dbch\af31505\loch\f39 s +\par }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid9635109 \loch\af39\dbch\af31505\hich\f39 \lquote }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid74094 \hich\af39\dbch\af31505\loch\f39 A}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid8745149 \hich\af39\dbch\af31505\loch\f39 rrow left}{ +\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid74094 \loch\af39\dbch\af31505\hich\f39 \rquote }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid9635109 \hich\af39\dbch\af31505\loch\f39 }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109 \tab }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid9635109 \hich\af39\dbch\af31505\loch\f39 = Move }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109 \hich\af39\dbch\af31505\loch\f39 to the left + +\par }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid9635109 \loch\af39\dbch\af31505\hich\f39 \lquote }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid8745149 +\hich\af39\dbch\af31505\loch\f39 Arrow down}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid9635109 \loch\af39\dbch\af31505\hich\f39 \rquote \loch\f39 }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109 \tab }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid9635109 \hich\af39\dbch\af31505\loch\f39 = }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109 \hich\af39\dbch\af31505\loch\f39 Move backwards +\par }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid9635109 \loch\af39\dbch\af31505\hich\f39 \lquote }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid8745149 +\hich\af39\dbch\af31505\loch\f39 Arrow right}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid9635109 \loch\af39\dbch\af31505\hich\f39 \rquote \loch\f39 }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109 \tab }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid9635109 \hich\af39\dbch\af31505\loch\f39 = }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109 \hich\af39\dbch\af31505\loch\f39 Move to the right +\par }\pard\plain \ltrpar\ql \fi340\li0\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid9635109 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 +\fs22\lang1031\langfe1031\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1031\langfenp1031 {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid9635109 \hich\af39\dbch\af31505\loch\f39 The}{\rtlch\fcs1 +\af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid9635109 \hich\af39\dbch\af31505\loch\f39 mouse specifies the view direction. This is the typical FPS input behavior. +\par }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \b\f39\fs20\lang1033\langfe1031\langnp1033\insrsid14293319\charrsid67262 \hich\af39\dbch\af31505\loch\f39 \hich\f39 Otherwise, if \'84}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\b\i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid14293319\charrsid67262 \hich\af39\dbch\af31505\loch\f39 Zoom/Rotate}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \b\f39\fs20\lang1033\langfe1031\langnp1033\insrsid14293319\charrsid67262 +\loch\af39\dbch\af31505\hich\f39 \'94\loch\f39 is enabled: +\par }\pard \ltrpar\ql \li340\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin340\itap0\pararsid14293319 {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid14293319 +\loch\af39\dbch\af31505\hich\f39 \'93}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid14293319\charrsid74094 \hich\af39\dbch\af31505\loch\f39 Left Mouse Button}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid14293319 \loch\af39\dbch\af31505\hich\f39 \'94\loch\f39 = Keep pressed and move the mouse to rotate the asset around its local axes. Inside the yellow circle: x/y-axis, outside: Z-axis}{\rtlch\fcs1 +\af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid11029790 \hich\af39\dbch\af31505\loch\f39 . To rotate around one axis only use the axis snap-ins (yellow squar\hich\af39\dbch\af31505\loch\f39 es with a cross inside).}{\rtlch\fcs1 +\af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid14293319 +\par \loch\af39\dbch\af31505\hich\f39 \'93}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid14293319\charrsid74094 \hich\af39\dbch\af31505\loch\f39 Right Mouse Button}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid14293319 \loch\af39\dbch\af31505\hich\f39 \'94\loch\f39 = Keep pressed and move the mouse to rotate the light source(s) around their x and y axes. +\par \loch\af39\dbch\af31505\hich\f39 \'93}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid14293319\charrsid74094 \hich\af39\dbch\af31505\loch\f39 Middle Mouse Button}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid14293319 \loch\af39\dbch\af31505\hich\f39 \'94\loch\f39 = Keep pressed and move the mouse }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid1405492 +\hich\af39\dbch\af31505\loch\f39 from the left to the right or the other way round to increase/decrease the intensity of the light source(s)}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid14293319 +\par }\pard \ltrpar\ql \li340\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin340\itap0\pararsid7677524 {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid7677524 +\loch\af39\dbch\af31505\hich\f39 \'93}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid7677524\charrsid74094 \hich\af39\dbch\af31505\loch\f39 Right }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\b\i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid7677524\charrsid7677524 \hich\af39\dbch\af31505\loch\f39 AND}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid7677524 \hich\af39\dbch\af31505\loch\f39 Left }{ +\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid7677524\charrsid74094 \hich\af39\dbch\af31505\loch\f39 Mouse Button}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid7677524 +\loch\af39\dbch\af31505\hich\f39 \'94\loch\f39 = Keep pressed and move the mouse to rotate \hich\af39\dbch\af31505\loch\f39 skybox (\hich\af39\dbch\af31505\loch\f39 if existing\hich\af39\dbch\af31505\loch\f39 )\hich\af39\dbch\af31505\loch\f39 + around the global x/y axes. +\par }\pard \ltrpar\ql \li340\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin340\itap0\pararsid14293319 {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid7360491 +\loch\af39\dbch\af31505\hich\f39 \'93}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid7360491 \hich\af39\dbch\af31505\loch\f39 Mouse wheel}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid7360491 \loch\af39\dbch\af31505\hich\f39 \'94\loch\f39 = Zoom in/out +\par {\listtext\pard\plain\ltrpar \s15 \rtlch\fcs1 \af0 \ltrch\fcs0 \f39\lang1033\langfe1031\langnp1033\insrsid10752424 \hich\af39\dbch\af31505\loch\f39 IV.\tab}}\pard\plain \ltrpar\s15\ql \fi-360\li340\ri0\sa200\sl276\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls2\adjustright\rin0\lin340\itap0\pararsid10752424\contextualspace \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 +\fs22\lang1031\langfe1031\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1031\langfenp1031 {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid10752424 \hich\af39\dbch\af31505\loch\f39 Rendering +\par }\pard \ltrpar\s15\ql \li340\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin340\itap0\pararsid10752424\contextualspace {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid10752424 +\hich\af39\dbch\af31505\loch\f39 Enabling MultiSampling improves rendering quality. MultiSampling is activated by default. The highest qual\hich\af39\dbch\af31505\loch\f39 ity MultiSampling mode supported by the video card is used. +\par }\pard \ltrpar\s15\ql \li340\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin340\itap0\pararsid2961835\contextualspace {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid10752424 +\hich\af39\dbch\af31505\loch\f39 MultiSampling is especially useful to remove line artifacts in wireframe/normals mode. Note that the transition between normal and multisampled rendering may take a few seconds.}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid14187505\charrsid2961835 +\par }\pard \ltrpar\s15\ql \li340\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin340\itap0\pararsid10752424\contextualspace {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid14187505 +\hich\af39\dbch\af31505\loch\f39 Rendering is done \hich\af39\dbch\af31505\loch\f39 via Direct3D}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid14187505\charrsid14187505 \loch\af39\dbch\af31505\hich\f39 \'99}{ +\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid5860386 \hich\af39\dbch\af31505\loch\f39 9.0c.}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid14187505 +\hich\af39\dbch\af31505\loch\f39 \hich\f39 Note that the \'93}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \i\f39\fs20\lang1033\langfe1031\langnp1033\insrsid14187505\charrsid14187505 \hich\af39\dbch\af31505\loch\f39 Low-quality lighting}{\rtlch\fcs1 \af0\afs20 +\ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid14187505 \loch\af39\dbch\af31505\hich\f39 \'94\loch\f39 -Option is not available for PS 2.0 cards. Lighting on PS 2.0 cards is always low quality. +\par }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid14115297 +\par {\listtext\pard\plain\ltrpar \s15 \rtlch\fcs1 \af0 \ltrch\fcs0 \f39\lang1033\langfe1031\langnp1033\insrsid14115297 \hich\af39\dbch\af31505\loch\f39 V.\tab}}\pard \ltrpar\s15\ql \fi-360\li340\ri0\sa200\sl276\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls2\adjustright\rin0\lin340\itap0\pararsid14115297\contextualspace {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid14115297 \hich\af39\dbch\af31505\loch\f39 Known issues}{ +\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid14115297\charrsid3683098 \hich\af39\dbch\af31505\loch\f39 }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid14115297 +\par {\listtext\pard\plain\ltrpar \s15 \rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid14115297 \hich\af39\dbch\af31505\loch\f39 -\tab}}\pard \ltrpar\s15\ql \fi-360\li700\ri0\sa200\sl276\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls4\adjustright\rin0\lin700\itap0\pararsid2961835\contextualspace {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid14115297 \hich\af39\dbch\af31505\loch\f39 +If a loading process is canceled it is not guaranteed that further loading processes will succeed. ASSIMP Viewer might even crash in this case. }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f39\fs24\lang1033\langfe1031\langnp1033\insrsid14115297\charrsid2961835 + +\par {\listtext\pard\plain\ltrpar \s15 \rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid2961835 \hich\af39\dbch\af31505\loch\f39 -\tab}}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 +\f39\fs20\lang1033\langfe1031\langnp1033\insrsid2961835 \hich\af39\dbch\af31505\loch\f39 Some very complex materials involving real time reflection/refraction are not displayed properly.}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 +\f39\fs24\lang1033\langfe1031\langnp1033\insrsid2961835\charrsid2961835 +\par {\listtext\pard\plain\ltrpar \s15 \rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid2961835 \hich\af39\dbch\af31505\loch\f39 -\tab}}\pard \ltrpar\s15\ql \fi-360\li700\ri0\sa200\sl276\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls4\adjustright\rin0\lin700\itap0\pararsid9635109\contextualspace {\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid2961835 \hich\af39\dbch\af31505\loch\f39 When rend +\hich\af39\dbch\af31505\loch\f39 ering non-opaque objects some triangle artifacts might occur}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f39\fs20\lang1033\langfe1031\langnp1033\insrsid10617294 .}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 +\f39\fs24\lang1033\langfe1031\langnp1033\insrsid9635109\charrsid10617294 +\par }{\*\themedata 504b030414000600080000002100828abc13fa0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb6ac3301045f785fe83d0b6d8 +72ba28a5d8cea249777d2cd20f18e4b12d6a8f843409c9df77ecb850ba082d74231062ce997b55ae8fe3a00e1893f354e9555e6885647de3a8abf4fbee29bbd7 +2a3150038327acf409935ed7d757e5ee14302999a654e99e393c18936c8f23a4dc072479697d1c81e51a3b13c07e4087e6b628ee8cf5c4489cf1c4d075f92a0b +44d7a07a83c82f308ac7b0a0f0fbf90c2480980b58abc733615aa2d210c2e02cb04430076a7ee833dfb6ce62e3ed7e14693e8317d8cd0433bf5c60f53fea2fe7 +065bd80facb647e9e25c7fc421fd2ddb526b2e9373fed4bb902e182e97b7b461e6bfad3f010000ffff0300504b030414000600080000002100a5d6a7e7c00000 +00360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4fc7060abb08 +84a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b63095120f88d94fbc +52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462a1a82fe353 +bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f7468656d652f7468 +656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b4b0d592c9c +070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b4757e8d3f7 +29e245eb2b260a0238fd010000ffff0300504b03041400060008000000210096b5ade296060000501b0000160000007468656d652f7468656d652f7468656d65 +312e786d6cec594f6fdb3614bf0fd87720746f6327761a07758ad8b19b2d4d1bc46e871e698996d850a240d2497d1bdae38001c3ba618715d86d87615b8116d8 +a5fb34d93a6c1dd0afb0475292c5585e9236d88aad3e2412f9e3fbff1e1fa9abd7eec70c1d1221294fda5efd72cd4324f1794093b0eddd1ef62fad79482a9c04 +98f184b4bd2991deb58df7dfbb8ad755446282607d22d771db8b944ad79796a40fc3585ee62949606ecc458c15bc8a702910f808e8c66c69b9565b5d8a314d3c +94e018c8de1a8fa94fd05093f43672e23d06af89927ac06762a049136785c10607758d9053d965021d62d6f6804fc08f86e4bef210c352c144dbab999fb7b471 +7509af678b985ab0b6b4ae6f7ed9ba6c4170b06c788a705430adf71bad2b5b057d03606a1ed7ebf5babd7a41cf00b0ef83a6569632cd467faddec9699640f671 +9e76b7d6ac355c7c89feca9cccad4ea7d36c65b258a206641f1b73f8b5da6a6373d9c11b90c537e7f08dce66b7bbeae00dc8e257e7f0fd2badd5868b37a088d1 +e4600ead1ddaef67d40bc898b3ed4af81ac0d76a197c86826828a24bb318f3442d8ab518dfe3a20f000d6458d104a9694ac6d88728eee2782428d60cf03ac1a5 +193be4cbb921cd0b495fd054b5bd0f530c1931a3f7eaf9f7af9e3f45c70f9e1d3ff8e9f8e1c3e3073f5a42ceaa6d9c84e5552fbffdeccfc71fa33f9e7ef3f2d1 +17d57859c6fffac327bffcfc793510d26726ce8b2f9ffcf6ecc98baf3efdfdbb4715f04d814765f890c644a29be408edf3181433567125272371be15c308d3f2 +8acd249438c19a4b05fd9e8a1cf4cd296699771c393ac4b5e01d01e5a30a787d72cf1178108989a2159c77a2d801ee72ce3a5c545a6147f32a99793849c26ae6 +6252c6ed637c58c5bb8b13c7bfbd490a75330f4b47f16e441c31f7184e140e494214d273fc80900aedee52ead87597fa824b3e56e82e451d4c2b4d32a423279a +668bb6690c7e9956e90cfe766cb37b077538abd27a8b1cba48c80acc2a841f12e698f13a9e281c57911ce298950d7e03aba84ac8c154f8655c4f2af074481847 +bd804859b5e696007d4b4edfc150b12addbecba6b18b148a1e54d1bc81392f23b7f84137c2715a851dd0242a633f900710a218ed715505dfe56e86e877f0034e +16bafb0e258ebb4faf06b769e888340b103d3311da9750aa9d0a1cd3e4efca31a3508f6d0c5c5c398602f8e2ebc71591f5b616e24dd893aa3261fb44f95d843b +5974bb5c04f4edafb95b7892ec1108f3f98de75dc97d5772bdff7cc95d94cf672db4b3da0a6557f70db629362d72bcb0431e53c6066acac80d699a6409fb44d0 +8741bdce9c0e4971624a2378cceaba830b05366b90e0ea23aaa241845368b0eb9e2612ca8c742851ca251ceccc70256d8d87265dd96361531f186c3d9058edf2 +c00eafe8e1fc5c509031bb4d680e9f39a3154de0accc56ae644441edd76156d7429d995bdd88664a9dc3ad50197c38af1a0c16d684060441db02565e85f3b966 +0d0713cc48a0ed6ef7dedc2dc60b17e92219e180643ed27acffba86e9c94c78ab90980d8a9f0913ee49d62b512b79626fb06dccee2a432bbc60276b9f7dec44b +7904cfbca4f3f6443ab2a49c9c2c41476dafd55c6e7ac8c769db1bc399161ee314bc2e75cf8759081743be1236ec4f4d6693e5336fb672c5dc24a8c33585b5fb +9cc24e1d4885545b58463634cc5416022cd19cacfccb4d30eb45296023fd35a458598360f8d7a4003bbaae25e331f155d9d9a5116d3bfb9a95523e51440ca2e0 +088dd844ec6370bf0e55d027a012ae264c45d02f708fa6ad6da6dce29c255df9f6cae0ec38666984b372ab5334cf640b37795cc860de4ae2816e95b21be5ceaf +8a49f90b52a51cc6ff3355f47e0237052b81f6800fd7b802239daf6d8f0b1571a8426944fdbe80c6c1d40e8816b88b8569082ab84c36ff0539d4ff6dce591a26 +ade1c0a7f669880485fd484582903d284b26fa4e2156cff62e4b9265844c4495c495a9157b440e091bea1ab8aaf7760f4510eaa69a6465c0e04ec69ffb9e65d0 +28d44d4e39df9c1a52ecbd3607fee9cec7263328e5d661d3d0e4f62f44acd855ed7ab33cdf7bcb8ae889599bd5c8b3029895b6825696f6af29c239b75a5bb1e6 +345e6ee6c28117e73586c1a2214ae1be07e93fb0ff51e133fb65426fa843be0fb515c187064d0cc206a2fa926d3c902e907670048d931db4c1a44959d366ad93 +b65abe595f70a75bf03d616c2dd959fc7d4e6317cd99cbcec9c58b34766661c7d6766ca1a9c1b327531486c6f941c638c67cd22a7f75e2a37be0e82db8df9f30 +254d30c1372581a1f51c983c80e4b71ccdd28dbf000000ffff0300504b0304140006000800000021000dd1909fb60000001b010000270000007468656d652f74 +68656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f78277086f6fd3ba109126dd88d0add40384e4350d363f24 +51eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89d93b64b060828e6f37ed1567914b284d262452282e3198 +720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5001996509affb3fd381a89672f1f165dfe514173d9850528 +a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100828abc13fa0000001c0200001300000000000000000000000000 +000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6a7e7c0000000360100000b000000000000000000000000 +002b0100005f72656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a0000001c00000000000000000000000000140200007468 +656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d001400060008000000210096b5ade296060000501b000016000000000000000000 +00000000d10200007468656d652f7468656d652f7468656d65312e786d6c504b01022d00140006000800000021000dd1909fb60000001b010000270000000000 +00000000000000009b0900007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000960a00000000} +{\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d +617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169 +6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363 +656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e} +{\*\latentstyles\lsdstimax267\lsdlockeddef0\lsdsemihiddendef1\lsdunhideuseddef1\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4; +\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9; +\lsdpriority39 \lsdlocked0 toc 1;\lsdpriority39 \lsdlocked0 toc 2;\lsdpriority39 \lsdlocked0 toc 3;\lsdpriority39 \lsdlocked0 toc 4;\lsdpriority39 \lsdlocked0 toc 5;\lsdpriority39 \lsdlocked0 toc 6;\lsdpriority39 \lsdlocked0 toc 7; +\lsdpriority39 \lsdlocked0 toc 8;\lsdpriority39 \lsdlocked0 toc 9;\lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdpriority1 \lsdlocked0 Default Paragraph Font; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority22 \lsdlocked0 Strong;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority59 \lsdlocked0 Table Grid;\lsdunhideused0 \lsdlocked0 Placeholder Text;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 1;\lsdunhideused0 \lsdlocked0 Revision; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdpriority37 \lsdlocked0 Bibliography;\lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;}}{\*\datastore 010500000200000018000000 +4d73786d6c322e534158584d4c5265616465722e352e3000000000000000000000060000 +d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff090006000000000000000000000001000000010000000000000000100000feffffff00000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffffec69d9888b8b3d4c859eaf6cd158be0f00000000000000000000000060e3 +1310819dc801feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000105000000000000}} \ No newline at end of file diff --git a/workspaces/vc8/assimp.sln b/workspaces/vc8/assimp.sln new file mode 100644 index 000000000..30127cbf2 --- /dev/null +++ b/workspaces/vc8/assimp.sln @@ -0,0 +1,29 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "assimp", "assimp.vcproj", "{5691E159-2D9B-407F-971F-EA5C592DC524}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "assimp_view", "assimp_view.vcproj", "{B17B959B-BB8A-4596-AF0F-A8C8DBBC3C5E}" + ProjectSection(ProjectDependencies) = postProject + {5691E159-2D9B-407F-971F-EA5C592DC524} = {5691E159-2D9B-407F-971F-EA5C592DC524} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5691E159-2D9B-407F-971F-EA5C592DC524}.Debug|Win32.ActiveCfg = Debug|Win32 + {5691E159-2D9B-407F-971F-EA5C592DC524}.Debug|Win32.Build.0 = Debug|Win32 + {5691E159-2D9B-407F-971F-EA5C592DC524}.Release|Win32.ActiveCfg = Release|Win32 + {5691E159-2D9B-407F-971F-EA5C592DC524}.Release|Win32.Build.0 = Release|Win32 + {B17B959B-BB8A-4596-AF0F-A8C8DBBC3C5E}.Debug|Win32.ActiveCfg = Debug|Win32 + {B17B959B-BB8A-4596-AF0F-A8C8DBBC3C5E}.Debug|Win32.Build.0 = Debug|Win32 + {B17B959B-BB8A-4596-AF0F-A8C8DBBC3C5E}.Release|Win32.ActiveCfg = Release|Win32 + {B17B959B-BB8A-4596-AF0F-A8C8DBBC3C5E}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/workspaces/vc8/assimp.vcproj b/workspaces/vc8/assimp.vcproj new file mode 100644 index 000000000..37d685fc1 --- /dev/null +++ b/workspaces/vc8/assimp.vcproj @@ -0,0 +1,570 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/workspaces/vc8/assimp_view.vcproj b/workspaces/vc8/assimp_view.vcproj new file mode 100644 index 000000000..66edd3bc3 --- /dev/null +++ b/workspaces/vc8/assimp_view.vcproj @@ -0,0 +1,325 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +