From b76f999cb76db1c48c3c2dff33a7e4e438ec0021 Mon Sep 17 00:00:00 2001 From: kimmi Date: Mon, 5 May 2008 12:36:31 +0000 Subject: [PATCH] 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 --- CREDITS | 0 INSTALL | 0 README | 5 + code/3DSConverter.cpp | 827 ++++++++++ code/3DSGenNormals.cpp | 134 ++ code/3DSHelper.h | 472 ++++++ code/3DSLoader.cpp | 1310 ++++++++++++++++ code/3DSLoader.h | 235 +++ code/3DSSpatialSort.cpp | 148 ++ code/3DSSpatialSort.h | 83 + code/Assimp.cpp | 61 + code/BaseImporter.cpp | 45 + code/BaseImporter.h | 105 ++ code/BaseProcess.h | 59 + code/CalcTangentsProcess.cpp | 186 +++ code/CalcTangentsProcess.h | 55 + code/ConvertToLHProcess.cpp | 155 ++ code/ConvertToLHProcess.h | 88 ++ code/DefaultIOStream.cpp | 92 ++ code/DefaultIOStream.h | 82 + code/DefaultIOSystem.cpp | 64 + code/DefaultIOSystem.h | 40 + code/GenFaceNormalsProcess.cpp | 67 + code/GenFaceNormalsProcess.h | 48 + code/GenVertexNormalsProcess.cpp | 104 ++ code/GenVertexNormalsProcess.h | 47 + code/Importer.cpp | 136 ++ code/JoinVerticesProcess.cpp | 262 ++++ code/JoinVerticesProcess.h | 65 + code/KillNormalsProcess.cpp | 43 + code/KillNormalsProcess.h | 47 + code/MD2FileData.h | 137 ++ code/MD2Loader.cpp | 310 ++++ code/MD2Loader.h | 57 + code/MD2NormalTable.h | 171 ++ code/MD3FileData.h | 273 ++++ code/MD3Loader.cpp | 328 ++++ code/MD3Loader.h | 60 + code/MD4FileData.h | 0 code/MD4Loader.h | 57 + code/MD5Loader.h | 57 + code/MaterialSystem.cpp | 285 ++++ code/MaterialSystem.h | 187 +++ code/ObjFileData.h | 170 ++ code/ObjFileImporter.cpp | 354 +++++ code/ObjFileImporter.h | 78 + code/ObjFileMtlImporter.cpp | 56 + code/ObjFileMtlImporter.h | 36 + code/ObjFileParser.cpp | 551 +++++++ code/ObjTools.h | 56 + code/PlyLoader.cpp | 990 ++++++++++++ code/PlyLoader.h | 99 ++ code/PlyParser.cpp | 874 +++++++++++ code/PlyParser.h | 483 ++++++ code/SpatialSort.cpp | 93 ++ code/SpatialSort.h | 72 + code/SplitLargeMeshes.cpp | 227 +++ code/SplitLargeMeshes.h | 60 + code/TriangulateProcess.cpp | 83 + code/TriangulateProcess.h | 54 + code/XFileHelper.h | 147 ++ code/XFileImporter.cpp | 703 +++++++++ code/XFileImporter.h | 97 ++ code/XFileParser.cpp | 1182 ++++++++++++++ code/XFileParser.h | 119 ++ code/aiAssert.cpp | 26 + code/fast_atof.h | 114 ++ doc/ImporterNotes.rtf | Bin 0 -> 2335 bytes doc/datastructure.xml | 83 + include/Compiler/VisualStudio/stdint.h | 222 +++ include/IOStream.h | 111 ++ include/IOSystem.h | 90 ++ include/ObjFileParser.h | 68 + include/aiAnim.h | 118 ++ include/aiAssert.h | 28 + include/aiDefs.h | 23 + include/aiFileIO.h | 63 + include/aiMaterial.h | 476 ++++++ include/aiMatrix3x3.h | 50 + include/aiMatrix3x3.inl | 54 + include/aiMatrix4x4.h | 77 + include/aiMatrix4x4.inl | 136 ++ include/aiMesh.h | 268 ++++ include/aiPostProcess.h | 78 + include/aiQuaternion.h | 96 ++ include/aiScene.h | 147 ++ include/aiTypes.h | 140 ++ include/aiVector3D.h | 99 ++ include/aiVector3D.inl | 33 + include/assimp.h | 77 + include/assimp.hpp | 123 ++ test/3DSFiles/CWALL02.jpg | Bin 0 -> 165342 bytes test/3DSFiles/IMAGE1.bmp | Bin 0 -> 480054 bytes test/3DSFiles/IMAGE2.jpg | Bin 0 -> 283212 bytes test/3DSFiles/cube_with_diffuse_texture.3DS | Bin 0 -> 1423 bytes test/3DSFiles/cube_with_specular_texture.3DS | Bin 0 -> 1423 bytes test/3DSFiles/test.png | Bin 0 -> 23700 bytes test/3DSFiles/test1.3ds | Bin 0 -> 10654 bytes test/3DSFiles/textures.txt | 1 + tools/assimp_view/AssetHelper.h | 135 ++ tools/assimp_view/Background.cpp | 430 +++++ tools/assimp_view/Background.h | 97 ++ tools/assimp_view/Camera.h | 54 + tools/assimp_view/HUD.png | Bin 0 -> 40474 bytes tools/assimp_view/HUDMask.png | Bin 0 -> 6460 bytes tools/assimp_view/HelpDialog.cpp | 80 + tools/assimp_view/Input.cpp | 292 ++++ tools/assimp_view/LogDisplay.cpp | 204 +++ tools/assimp_view/LogDisplay.h | 66 + tools/assimp_view/Material.cpp | 975 ++++++++++++ tools/assimp_view/MessageProc.cpp | 1121 +++++++++++++ tools/assimp_view/NOTE@help.rtf.txt | 2 + tools/assimp_view/RenderOptions.h | 69 + tools/assimp_view/Shaders.cpp | 1213 ++++++++++++++ tools/assimp_view/Shaders.h | 29 + tools/assimp_view/assimp_view.cpp | 1483 ++++++++++++++++++ tools/assimp_view/assimp_view.h | 228 +++ tools/assimp_view/assimp_view.ico | Bin 0 -> 23558 bytes tools/assimp_view/assimp_view.rc | 307 ++++ tools/assimp_view/banner.bmp | Bin 0 -> 108054 bytes tools/assimp_view/banner_pure.bmp | Bin 0 -> 84054 bytes tools/assimp_view/help.rtf | 370 +++++ tools/assimp_view/resource.h | 90 ++ tools/assimp_view/small.ico | Bin 0 -> 23558 bytes tools/assimp_view/stdafx.cpp | 8 + tools/assimp_view/stdafx.h | 70 + tools/assimp_view/test.xcf | Bin 0 -> 122221 bytes tools/assimp_view/text1.bin | 370 +++++ workspaces/vc8/assimp.sln | 29 + workspaces/vc8/assimp.vcproj | 570 +++++++ workspaces/vc8/assimp_view.vcproj | 325 ++++ 131 files changed, 24989 insertions(+) create mode 100644 CREDITS create mode 100644 INSTALL create mode 100644 README create mode 100644 code/3DSConverter.cpp create mode 100644 code/3DSGenNormals.cpp create mode 100644 code/3DSHelper.h create mode 100644 code/3DSLoader.cpp create mode 100644 code/3DSLoader.h create mode 100644 code/3DSSpatialSort.cpp create mode 100644 code/3DSSpatialSort.h create mode 100644 code/Assimp.cpp create mode 100644 code/BaseImporter.cpp create mode 100644 code/BaseImporter.h create mode 100644 code/BaseProcess.h create mode 100644 code/CalcTangentsProcess.cpp create mode 100644 code/CalcTangentsProcess.h create mode 100644 code/ConvertToLHProcess.cpp create mode 100644 code/ConvertToLHProcess.h create mode 100644 code/DefaultIOStream.cpp create mode 100644 code/DefaultIOStream.h create mode 100644 code/DefaultIOSystem.cpp create mode 100644 code/DefaultIOSystem.h create mode 100644 code/GenFaceNormalsProcess.cpp create mode 100644 code/GenFaceNormalsProcess.h create mode 100644 code/GenVertexNormalsProcess.cpp create mode 100644 code/GenVertexNormalsProcess.h create mode 100644 code/Importer.cpp create mode 100644 code/JoinVerticesProcess.cpp create mode 100644 code/JoinVerticesProcess.h create mode 100644 code/KillNormalsProcess.cpp create mode 100644 code/KillNormalsProcess.h create mode 100644 code/MD2FileData.h create mode 100644 code/MD2Loader.cpp create mode 100644 code/MD2Loader.h create mode 100644 code/MD2NormalTable.h create mode 100644 code/MD3FileData.h create mode 100644 code/MD3Loader.cpp create mode 100644 code/MD3Loader.h create mode 100644 code/MD4FileData.h create mode 100644 code/MD4Loader.h create mode 100644 code/MD5Loader.h create mode 100644 code/MaterialSystem.cpp create mode 100644 code/MaterialSystem.h create mode 100644 code/ObjFileData.h create mode 100644 code/ObjFileImporter.cpp create mode 100644 code/ObjFileImporter.h create mode 100644 code/ObjFileMtlImporter.cpp create mode 100644 code/ObjFileMtlImporter.h create mode 100644 code/ObjFileParser.cpp create mode 100644 code/ObjTools.h create mode 100644 code/PlyLoader.cpp create mode 100644 code/PlyLoader.h create mode 100644 code/PlyParser.cpp create mode 100644 code/PlyParser.h create mode 100644 code/SpatialSort.cpp create mode 100644 code/SpatialSort.h create mode 100644 code/SplitLargeMeshes.cpp create mode 100644 code/SplitLargeMeshes.h create mode 100644 code/TriangulateProcess.cpp create mode 100644 code/TriangulateProcess.h create mode 100644 code/XFileHelper.h create mode 100644 code/XFileImporter.cpp create mode 100644 code/XFileImporter.h create mode 100644 code/XFileParser.cpp create mode 100644 code/XFileParser.h create mode 100644 code/aiAssert.cpp create mode 100644 code/fast_atof.h create mode 100644 doc/ImporterNotes.rtf create mode 100644 doc/datastructure.xml create mode 100644 include/Compiler/VisualStudio/stdint.h create mode 100644 include/IOStream.h create mode 100644 include/IOSystem.h create mode 100644 include/ObjFileParser.h create mode 100644 include/aiAnim.h create mode 100644 include/aiAssert.h create mode 100644 include/aiDefs.h create mode 100644 include/aiFileIO.h create mode 100644 include/aiMaterial.h create mode 100644 include/aiMatrix3x3.h create mode 100644 include/aiMatrix3x3.inl create mode 100644 include/aiMatrix4x4.h create mode 100644 include/aiMatrix4x4.inl create mode 100644 include/aiMesh.h create mode 100644 include/aiPostProcess.h create mode 100644 include/aiQuaternion.h create mode 100644 include/aiScene.h create mode 100644 include/aiTypes.h create mode 100644 include/aiVector3D.h create mode 100644 include/aiVector3D.inl create mode 100644 include/assimp.h create mode 100644 include/assimp.hpp create mode 100644 test/3DSFiles/CWALL02.jpg create mode 100644 test/3DSFiles/IMAGE1.bmp create mode 100644 test/3DSFiles/IMAGE2.jpg create mode 100644 test/3DSFiles/cube_with_diffuse_texture.3DS create mode 100644 test/3DSFiles/cube_with_specular_texture.3DS create mode 100644 test/3DSFiles/test.png create mode 100644 test/3DSFiles/test1.3ds create mode 100644 test/3DSFiles/textures.txt create mode 100644 tools/assimp_view/AssetHelper.h create mode 100644 tools/assimp_view/Background.cpp create mode 100644 tools/assimp_view/Background.h create mode 100644 tools/assimp_view/Camera.h create mode 100644 tools/assimp_view/HUD.png create mode 100644 tools/assimp_view/HUDMask.png create mode 100644 tools/assimp_view/HelpDialog.cpp create mode 100644 tools/assimp_view/Input.cpp create mode 100644 tools/assimp_view/LogDisplay.cpp create mode 100644 tools/assimp_view/LogDisplay.h create mode 100644 tools/assimp_view/Material.cpp create mode 100644 tools/assimp_view/MessageProc.cpp create mode 100644 tools/assimp_view/NOTE@help.rtf.txt create mode 100644 tools/assimp_view/RenderOptions.h create mode 100644 tools/assimp_view/Shaders.cpp create mode 100644 tools/assimp_view/Shaders.h create mode 100644 tools/assimp_view/assimp_view.cpp create mode 100644 tools/assimp_view/assimp_view.h create mode 100644 tools/assimp_view/assimp_view.ico create mode 100644 tools/assimp_view/assimp_view.rc create mode 100644 tools/assimp_view/banner.bmp create mode 100644 tools/assimp_view/banner_pure.bmp create mode 100644 tools/assimp_view/help.rtf create mode 100644 tools/assimp_view/resource.h create mode 100644 tools/assimp_view/small.ico create mode 100644 tools/assimp_view/stdafx.cpp create mode 100644 tools/assimp_view/stdafx.h create mode 100644 tools/assimp_view/test.xcf create mode 100644 tools/assimp_view/text1.bin create mode 100644 workspaces/vc8/assimp.sln create mode 100644 workspaces/vc8/assimp.vcproj create mode 100644 workspaces/vc8/assimp_view.vcproj 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 0000000000000000000000000000000000000000..0c1cb54c5b0d47cf5d71ce60bf43c803d29d5a66 GIT binary patch literal 2335 zcmcJRO^@3)5QcjWkpD2ZCPnQ?viAes)3!m;1+t5xn?nw~P|`>uLXiqdS?ezLzxSo& zuO`mcEmFWRB$G3I=jHIBAB785Dx~ph{3^OS&CW7Wa#bbqTT4@?$s*l8SG;`-GQUVJ zA4O$N2)V{XA}VKFX++hzPnoESMmoBu z8b&s9#bhiEtn~~VsSKAi$TbB^B}+|%3Jt}gRi~K(KZM>Pu7ZcwFv(c(oNg+TW_m$N zAD>hmQ+*j38eWrhOkTB}UX#amU}6j+t$ZL`ZI_ue&gp&B64!Ug(TD&t9f zf0+qIdEa(IVG}>~*k~g7v>9nw)lE1YdsRr!GXygZR!}Q`P3TC}0Q>qDreG6tg2^<+ zQY*Xpcs9^T+YZVmm1r((?!S_HcY7#iFVtAqO1-H7Q;1F%I>mltp}yl{)N#bs8b|TZ z!#Yma8q@Y#Ix4J2^_$-QcK!lq>T9z5w&uJywXdYh`9JB$dBXy1!vCe8TQC(}Nr&?G zZQ@y1ha)E&Z)lhKIp_ZgW|8`6`JWjq;)7$aMg49@IsIk_Ua|X;d|bb^C2Mv-6+D!Ayc#SymH@fI1p+Te-~EJ54|OhH zSb9CDYqN&Pow7HLLJK!`h#9=q=(o^78$Jzy2|=>!5UV6E?9b^p?!eKT@^nY{EZuZm Wnp_^geld<+-~HDaU-8f4yT1TH + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ... + + + + + ... + + + + + ... + + + + ... + + + + + + + + + + + + + + \ 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 0000000000000000000000000000000000000000..93364a0bd3d9980bc6a1ac4098eec28008d74d71 GIT binary patch literal 165342 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 z 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

#q!1Fj5Kmg=klM=~ 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 M00n^-MgG_S*{;rWj{pDw literal 0 HcmV?d00001 diff --git a/test/3DSFiles/IMAGE1.bmp b/test/3DSFiles/IMAGE1.bmp new file mode 100644 index 0000000000000000000000000000000000000000..5a88d56471b22cc217cc7f1758bb4104949adf87 GIT binary patch literal 480054 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

H1zMkih3w4g#Bv@D3aK>e2 zjnBb78`Ia|4<_I=W?0X>GYPq8pS)cj9O@j|S?-(alM4Wc_^uvhczF8*&kh#=59NDh z#0J{73~`t9a?DwdMtVZbq5p{d37jSP+nRp4L%FkP{)axCzQQcNq{R1fI=&~unIiUu zI>X7Fath4v`?7LyM$x-xq5$aEjiJI6Tl0Ke+&o^S#Rp)Dcache!AS zsi`-YAJw>W&lL4cMXs-p&mjl)CYhHN3(L(Kn;6shUYn>Vh8Uy)7i4{+F_XQpjn4m1^DJrBZyv%KBImG?pTTK3FRM#GwX;|T`_|mO zq*j5tcxps_uYsQ%(T~7^oJAi#A>NR``gLWF-Wle~^YbtG|Ig~?+TkLQb#oxjaZ121 z*rO@d9LF}*$bt5-1$k?Gct-mHl+kD&DZ1BV%}rvM6mfi#cdiw|RXMOr^lod;DM??7 z_^%89AO~u`$afcI_{y1g#D+~HZ|3n6e4kcduyq-5d=gCf1v;Qsm zp{-Jogb3yC;aY30!$T`XKof-4AVCsI5(Gd(5EMyfWoA`Z_Dr`~wORiL@3p?DdVrAx zBi!%(#1Z@Kv(H27``8ZlVM;#x$(QfuS6=>=+U;{}2>m0}GwrJtA1aE!TJPhT*6*SJ zNp8J;pLzvnW58aSKUj}S5i2gj0Z=EriS1Owt4s}DEh%TJ%K5d&R$iJslmvfQCFZV$ zJBa2Y@sP7vf;Rq5YMGqP-A;=KWHd#vaTIsSnag7r3KzKV%_(6zdZKA+14d$fzB!ypH0IJQ}0Y{RefK%r~~Yf zwnXeo9BP}hI=~(}v&vd~l3Y`5UgHRJb-93Pa_u}i?5nL!VhQHCkQeC-tv{(NY2CgE zX5l5tLY2MuiZn4hsv!uqM2km9RV{^{JpHfPk?2+lQ;kENz>g_vK?q8w{~uTEWM zcH=qu4RJMVG+j&oT+=StmK?>SM_`liw~Xt= z7wqx4`~6?%7oVVWg)PgG7j@_n`Q*jl=imJHzrxIu6t$if+L`G!{3*Up>`3kOEc@p= zh`EcYm9Q=53Gzsnh_loJk#Fj(W$l}K=Eh>`878rfW&Es~PkC%n!E?zSI;ok1GlS;` zo$GM%dcQhBYU*QdZ=Zw&Yjt0hTYBsFg@b!z;)*P z{CD%8H1^mzv}%Z!_&o7o@pOG%=yT(q1dN5BbB;~-3j0YcFawug!|rvkNqfv*RG;|V zqgTw}{s+2fFR8Iz_9_ zXM6sCN7}?j>?N^Ql=_$2P;vgV9zzi;gXEU3w|9e~eU?=u&6E})v3a%D=` zWqZri50dAte|650J;ly>+$K(&;k(xFp+iPaT*Z#&x!)sfWsc{H6X&h)eR1%FEdC+G z9wo6oZSH622?((uH2}_H0e@8Bj-Ca7xkyg*DSC*V>-ahBx19PsXHDDddYUV?;U~$z zStRaQU|+?pi`ZJ9&wKBD_BuY>n7oBO(|5LrdzSe8HJ(@Qv3ym1mRe0Io=tr(&!ZTC zdKtzX_Llr1*CX$vPlEF?^({aAFeIub3#OKRrk}q`U$A1a< zvN?!-{yFhYp4*&3u4Ed2CYE>f7*3UODd$@pG0cF*H#lq6Irh#D^&UH?sE(e7dWO-_ z5%r#LnCUb)v-ENM?Zob&KaWKZ|B41h%rX!vZmoOOgWSCcnX}O)ZmcZ4=x2* za~C_PR*3Znc?-@3c0c6S9n-hJ#(gUA|J_6N)5OT+*I2`|z9tV#{jnPBBj-Z?yZ)z& z|IoiRz!vM+QTz7fwIVq?y0trO2z_&oK)>}NDLsFR~R8&Ewq z`Gs10r8O%+(-{u@9z#4>|t!m-%=9_;>1g@Du5= z-k~ODEy-GZ4*O)^ptW`1Bh(^@MLpkQO>&dGyH(DpTqWl!eN;zV!#o^2qW$;0ncJDa z6!ZmruWKYWWu3uzJoI1VZ>)c)_vs&bOy<}{a!_a3SO=HyrJk02FK3kKGwXcz?mhGj zz{=D=X|nEeo_$8S;`V{nh&S}x*{$=eBf6FUvH!-_>cgoEyTWfN?d7Pk zKk{?!d+!maIiIwN-5G;H>=QTck~eREnPv!{v&K=jNjEyERiEb__K^ZPICw44$DimDwm(DJ^AG8ADKV<1Mw2M2{EJdEaXwBDPq5*^BxCa*VfZwh8jO7jxplQo1fId7Pa94ujKx2&_k_`iZgJg#DD|x7d3C~g-&n}D{_gj z2aC?L;P=(fmD4CUdWrRJ6Mq%>y9OARJgGW9LcV8&cv{Y~8ro{#>vuf=DRPp?1U!#e zp+;?_PL0!kjx4_7q_R*20mMS%KNWwQ;jogAHmw47vX0ze7<#B{azZ|kmcu{ z*J~Z;_RD{s?>$BXj=bY4Gs8dr;@y0KJ&A!ktEExk9%u3E>IZ6pvv+LZc z0X_b5LiPF9gVjnhmIC#KH6DtR-9g6yFCRZq$DBM1 z-0S-D*YjK7{0Jt1-2oKGI7#~xnFX#^%)IvfF^q& zMxy43GeMkr)sfT4dy2mX9s=KQJ-ul?oV-FU-$ULpaFfkpxKH*F*7^U=4OS0(m%L+) zKU7P?`Byz+j3GHpm;A*1x5@A2>09Wt=Uw{ii~*eS6CsxE!(VLTdY90jEu#iJHU3!!NNl^q{bo8F4?(Xq*^*nco+?U9N!VgC#XMPx5Aswbfmd zd#!$6fjm&%)u2(&-Po^~aYQ?}eGx+%s`X zXEjv$yArk`)b+V5O|VNjT2pM3b5PFRV+IXS}3*^A^Nn{fEc#7uIpjhF08 zNaI)SeY2*my)$02@4p0gzX~rwJXL<6x{K!E=ECAp&ct4UceVikd;$AuAJeUGf5z7y zVSDX&x{mghJ(m}-Z}c{Bk3ug$J({O_+vI+`tT!_lumSQX`#@{6^q=7@s6K`)XVqeD-?Ygd6%{ zVS@j>&7CG+#!oxT^uyW~@gM#E_!jXb>py91{aO5^9QrCI z(3g#Wf?wDKKPVFG=CJ2-H|5IKxw~Rjd*tLdu_Zm8y+E9nVDDn&LE;0(aB?iQ3HI9A z`z0?%n<{U6sty6?HHj@&pV1mh4V$0j8QavF$J|qUVAL*gUT~SS(;yyn{*}B{W5^bF z%>2L_r}fkmE)KCV{zqKC2w$qjXAQ`m7TI@uO4Y=di{vcV18jMN{4QqQi+9_8@PhSx z!Q7MYsOMOReH!wfjmL1^up=4rE%TWaF{<{b%)JtuE8(ZCX{#YA_ejk?@1uRjo@;rC z@{ZJ-a<-@0Ta=i!{^I8$MU9DY7QOe6oucwOx=`T95e8btv&{1?ngJ?9Co?bv`DpK1UuYe_agX+PA-k^;sr96KB&OoBPH% z*URMZ>SD;RxeD)f=LWr|*i!e)8in;aKkAE4eyxJ-M0K>56v>pJsot{rhqv zcHlb{{d=A}#eF#o&g?uDwZ&50QRgy5@grh(<}m7Ao4*^+gx(HphFTZmU7inngRFPR z_foT1d`j*9Dta5%67BglPpvRFL*Jq;H4kl(Q(9lD5YrCvgAKmh+NFK7_7FKo!9D_Y zSLBMW<11?FtKqK(^j0`aLq2Ti)c}hOv*^wJxd&D3u^PPA3FQ0k;dk2Fb2MtJ#8x@* zCTGdo8__WSf}0h3X;??K6F2WTHx1ou?vORiEox4K8*og>?@F8(>q=+%9ktZdH&j3N z4~af~-^88dgY}RDS;v6~08dM-Mh)^9worUcT|jY1@trd9nwp2!Y~{f_|Jax`^zHDR z>hNX3GM)LTu5^byQhd`sNx56j2zL&lwYvemQzQJMwpJdzGb0CJf$HT|i4#hk`z(7` z!8gsgYc=ehTtMTmoN+oem#nxZ{%et*#RzA=hF{6!`|OifCv(CKk0Ehi?;3Fq{%@0f z@ql<{LwzH(UF0KniQUCZ)rFAg#{!?L${wXs+sFnN?yL`I76L2JBbaOfT@|M7ft)ox*9x>Zk z?|`)ex8l4f=tofdsScmade?+nsrAU#dEzDZM6I~MGZsfDrm1p2tK|8P9ne?mtom{FJ*!I1*{}p}1xA+0@31Z19 z{YiW9s%GRrdDeady#6xJvwQz*uovzq>*sSjS153L!3oV({Ubj{T?y+g_S7x%-p0z# zUHSayf8jmwJ;Zu3-phJ$pFeUU&G(%_*!118Lw*tr7xr>(cnZ4ZI}bXKbROHr8~!V4l!BKA5}+?>bT+wiRL(X5L+{1&lim^lWA zsfZ7e+bZu({htA6(is=l$kXDR=19a)*4m77)fx7`#YD{|Zj>1)Zek+R}N5~e;Qj_i|%7%>O70wD7EY5x~ZWVMXyEfiyVAs2v|d^5znaEehwbE zbJnd}$%nH~N6u0YY^sTGSBF6iM!n0x8{?lRGkJ;IxwGcLWj@b)CT`@+2DMb|O|nLD z{i|Qm5kc>aeF;1zI0<#?wPnuHT`+?Zd98IHYhWF|b9RdypS-5y_vs%wAKC-^?6!y# z(wx&3xIOmyi`U!BuK&vMnJw(OoM~rV+P5ta%UY7QS-yoaq%}ObRN58!xz3w1z7b2Z zhg3b3BKKa+!6g34UP-aiiq8i}S8UEYgWsqATMeHm?`o4AbLSp&%g8hJLl=lQ*6_KW zS?xsQJoSfNIb7i5+{-FlLpg@drk6i%OeHtf{v)-&j025#?FkakP0>r37-5UuLw-h# z#%Dd??ry+2y+&X3{==7WU9qe9S?@=VGh?oJ_<%Vw#8vXNwulMGU@`U-T1Q^OkHo=9 z)X+13F>YQa?>=3_9@WVsLf!&r75ivy=?qxd8DatJZ|XhM?Bh_A%euM!bM}TV<5O1xj)R^z+=mX> zyn2gM&X)XQ=LpHCGj}PGU)89uSjP`K{a`W~K0nW$T;zWKQ@S5=Tl)~A#MJhJtNp*q z`Hm1*ErFScv8q#UY_DBXbJHBp*#+u)$qo1U?D_9;#ya#}ZLq#M*1d`D>;G{oJO3b7 z3Rp;%Sf&OJs*fuVvt;^wc7YtwxW3C>)-Kztapfr*bL2I*zG4P0_1}wM zz=;BHaK2s_-|GBi`+9r4_fXq_KUZ_5$X+a=L)Jfk3C-cJ=DR#s9&GItcPeFW&N)&? z+PO>G-m<=pwF-Wk8iVy~d!n=0660X~u6eq)Up~w-JR|od;JfCb%)p4TKkB>Ne=4WK z{%7ZL%4M@gCRb1W7I{@-o6e_}7pbo{ACku=XUTj?e|Uy>F~3yLsEE%~C*SzqIIqC^ zsY#;N=N4G+gmbn-Og6%<$``4^Gw&UtnLxeYxqEqH7;Dw)oj9M_`rizUe2@O|P0nZN z(4y#~n6*bzWbUb3EYg zI|oOMYDWIN36?eG+>KmM&Q;*_vPW(F_6GQIj~ak|9`2ugWcGwOb4%X2{3GYOs|D;V zls;$7-sl1Oh&I_dBj$PP16j}4w{5`l%93Lg#T$vI^y~IRZ_zK@ch9hAo+EXUO5D8+ zd5zkA&IMJUr$&rlCr9${J)U=O{}TdXImu4RSTtLuFF1KyQ}j)AAt{<4=hs$ zN%Q>9_g#lqr`^x3Q&T7J%VHxge)j=39Yx}0 z>s8Kn-T^mIn}3_Wnr+ULc(*y0I-d5psE?WCY_4FR76SeT2ZA$`;A}+c8L^ZKKq4z!`@=Y za>Vr!Vmxhqs1;GCXkyRQe;418|55|xyUcPGPHktg5`$O67d{~`9DsgJ(OXI01#wtr0hZ!u}HZ86#ue%@MP zgk1S_Yn#}Wo@n-JpBiq7c^gGO%XnJsN=}Tn*7~_VQ{89tr80NO^XSZkCTq3>N2Uk< zv;Wy!?g>7gwN?Af&mCYZ+B}`&E-fHi3y|ls^8~^;28U?A$2ZdeS1>n$l8Zu-Xpind`nzMO>OnS#3jWa z<)f#l*|ovwd(0HS`1EbSIJfR%ABfAF%xp4W)7Rd5^KbL>kKYidKbapAVy8?z+;!-{HuLg;65Hb zea}6z-V4^i`La*Ye`@!V<{|u!bzC`G&e3!Rhnl$7VH#k7&ONbaDhEscxHWvwvDk>% zRD(09_T(-c^9ix1ypuM5!M=X+ST*~{oOx%w?_=M?Jpem2C$gR|KIj?tPRJ9tE+{Wc ze`;LY4L2% zy;YN_sm7~)^jSnON{%VwwitI_T;AB2XL)3 z!~xc!&epckz=aFLSudh@+2-?|&3LLyJ%#?dl|FeV_tTl;O>B*8yj=X2sSR9x z`5Sd^`mA|Jd*7<8`-As?BS(NAO^&$-2TIM@ZR$VPwu7b={?hq;i_A4y<{U)flw0dn zE45EeNN(E*+uen;yi5(XcEnucNAKq6h%3{?68hrw`XOiOCNq#;gJ=JR_?f#*jb2^d zsC+BdSnj&>3)HQQk>5nvgD{(jbF)fL8KHib$L|>bMmR(I%sA_**5TySH{6TQnX`sX z1k1C(pv@XAb>KDd+3HUiYxnGV;+e$sTQ})j;H-?`5viT39#E6}U*o)46E9)!tc$0? zGpx4{u(3&ezjL>Y?TjnL%(Lv}2h|O*$6?S)6VtFhX#M<{T+4oL@4=XusRG^?5x3{Q znBSl6fu)+864NHRcluwS@eH*id;YBde@F}`XW{bG@4<+uk#UC45NA7MbxPc5e5Iat zsIl=~Y8o5&*ps4`rn)XM&YIl0EdE3Pq*i1b-{YSS`zSxtynl?It9Xdocb;E+e9F{a z#ptc4T=?uYoF;gd^mBPe`F9=c zdz6@cmAUUBMmA>T`J(u!z|Z17Xmf)GEca7Riv55=H-K{^Kif5u7pKmE_0MJUz*YWV zoEW`My~^Gb=dw8KFvnT){>ptSV}I)SceNzt7deBh&i*BNmIm`WpbQjRIv3ue3~|ROuoB8++;nW z!MtjF>9#+9H7|2^2lTVg&=@Z==P3;i?*3R?mV>R%p*$V2)&e=4`ZC&wIPsNT$eeY!~k7#3He%qcgFiVhuXU$kJ@^DAIz*tonH-} zb#g%S3V92DhCFKdaq_UL_@X{`_0qFfq32pn<1qV-eGJ;`tV5jNvuD+sbsXPn&w`pF z^3Te=r}ndYkvb=9(;-ePa(64(tlhiM=9mBUlAI2GH?S_(PCsW2AWt0coj4CC{)nGD zdI)BD4bBmJ=`76&>ovhHUSZbj1Gp(|e4BfG2j6n;F&aml+h)j@;V^>bXW$^7q1Plw zKIDB^nz5hOR>75yzW!x?#0=J3-~CKH%DrF>j7wbuYvN)!9b$x|&%O_OvU^X_>}4H0 z#OKaZZ4aS`A@4lIEBFWbK5B2tQ`FuMJwwE( zVFo?%ko+?_jYm((?_U2I>=;f9zhjOabksOs5&W0)_m-)&(owe@(q4d4Ou_b$O2ia^_8N2Jx*n zc33WgI)L^xn*W!`eLe40?x$MA)=dE5})!k zes>|3|(rvZT7%kb7$X%-VoM$ zfDP)A+nCcjM|}deB}Oej*&N8{l`k;@cd}PX?SHvh>ZZvv-6j4qUz@=(uuh?Fi1m^x zHB;@H+7%^Y4rdcwV(%MZ`YC$QE5wO$<~*pYsvbbljy12)4`SQW_*waWQ}TFuw<&QJ zY-Z#5OZ*3z7N2duyjuPC9A!A?_Q*Lm#Jbp!`S$*N&J*uF-&hUP!k1?`=8MvK~9YP{L!uuSeD z|8bT7Hjb6odj26;7=7I4GeO6QXYl-YS%>wzYRD3=;WM3$n&S7=WzjFI5iAGBz8>pf z+w@RBrU&TS=jbJJKQ4dq4&Ju!awp^$o8y8|tL@;4!b?`2w!w*YjJi|7Cvn&A;N`!I)XUI@q~+p!Zr_K-*_N zK4MR{`T0Y9h`kTac;2IqJ|dr1k5g`#nhkC4t#iZ1X!}{q+$5!+wDX9G)B|5@zlbZ3|E zqfeJQo;slsFg~@K%tNeS%7+jSS4XJI=jDib)l$rIS7V$>XNo!FBEh{^=R|BR2M06_ zuCvOYC~@i{HYmg0(`T8#CO9+Be`peeIgcrU4PET-gTvgSM{_p+eRXsGUkYm>=C-CK z2T}b6W0ytpz%UbN=i+<@CK2#=;(YbL?2VKw;NGj1VXU}^-4$Q0kjHj8GukUTB61R{ z%+GQrP97~nv08cVA2r~skY|avWH{5#81@}^9>3tTjpL79^akD7h1Dd)4@7*q`OTm{VH>2zC{DeIU2+tysv*2$zeaN z!Xf0WOzyt`=ceupzBeH5Qmf6pd`3K8XHV2zxBg%sNr$yx;pY1=gY>mg`OjRrVk#V9-;O0!1u$~Y+%z{ z_^}ePN`$&;gjn6PX}wbXL!D&#vVHOfd2-gyt*^-=?Bg@Vh}B-v9y!}XJk{Pbbr8iS z^@~U3=)P}~Y`ME1>Q`!@s2k|a$1Jr|V{GRk3*SJ0W<0-0Eji>g{GQw;?^>6;Zv9=1N!w%WIte;9;w;o_`2WTN+hADR%=nuU zpIGnL&a7iA>fDbu@ryNy4rjc{`mTW)JEOGB*|D$f3Y@?V;*HzS|IC>I^A*3rPO8VN zeIJ8=jX%LpQIqsD3t&SlaL~*ntzjD@iD8YvI49UnweH8}{=`G^b^8AL%w;6*&2pX+ z=-u0klAG}v;3w9A<&8N%#Msij^$dB~0%vN0ILsL&;+EoBYNb}Gk%aq#kFZZkA01|m z@cRL8!=|bKeyYCCxumy{x<}9@;pZaQCv}|~e8%0^|H|x!Z-XXWiuY7|tcIUk2V-b+ zC+yvv;fvPs8^=%SnZ8QD8t;C{xsrdc&a(LAWi(r^e@nc{o``!dz~}Ga^EV&9W=+f= zeQ)?N@G9)_6i>WFUNQuS8Q%dj`_oU%ngffZwsx7hXU_y7m#bRE zZk*$@t?jJC%_$Q{TB8j8Ti9OX5VcLkEU&-%jr%}tn$PU8H{00HJ{%0^>Dp87JdX-z zqC`!ohArP=JuZFu3XGB5mwdg=yp0m}NsSZx!o=t0Ub^OOaA0d~d(3MxF1J@Ig00FC z4~w<+@Oj#h0XFgDZ+@f(&zj=9o$r07y9a+uob^xPzRAz}9s9LxOwPV)3*?NNgI{@e zqCcrNrTv`|a`g;;#hHW7_g=(qCdf--*smzxAGFfhW1qnu!Bx)a*ccDnq(2UtWt?Ms zX7bcr^I!#Ko-Ncbhym1IvY$$ABk}vi9&>QOn*%?TJ9tD)T-e60fN6EHZ}MH$4Nwa% z_*Cr`F^#dVc!~T~>pNxcL*D-R4eIc4C-xq|g~k6j@xx4Ma3ShvUbk>%d$82YG?UuXVR_^Td5WE`{H_t`PHmci%2fId{6soSJyb-V9e@t z$-9}{dP*(qO|b29WLCIua?1Aa;#1gTdu$FK!As|S40x9oxn`O9M{*3src%W31>V&d zqr;gt_m$g_!M}+M$pIB3a=u5v2*LDL!CKXo$q}QA;o0{)#m4V&p2a=wy%NWg$B@w$ z5HoeKxjoLBvmKp-q28)Ij2h1z8ytsTN$YI#FI&Wk#xeTzA#soWPGSOe&Uy=5S;8kZ zI72hOPc6z4-)|kc2Ts+Zw{m><3t~IC3&bem5cY$pF)zw^jj&E?;4H&W{h+!% z|6^qxe+BPYPD-#}){dnlk*o+_zC-Iv}3co_+)&8y_Fr}^)9~g5Pa?0i(kkg z;r@}MIWNpOTdYiug1$h$kTqVlp@QZtdte`#TH&5e@rMOC4L+YW(Gq9B2Ts|fK5PxG z8e$o^-QfP_$tAc{+6Lzy&u$YN6GN&OsUCbE9+>%J;~4I>z6U?pk?Sj-Di0TXJ=Tu! z{Q8g)K1BPpbCb{DUfc8ENQ2$#rV{?5lN!HlHKk#2iB0O5I@fnA9cMCoWKr zYZI)tOs;Kjk36L^=S4qqDmS5~$QibCUD!6NgQSGxvzq?61)_n?ri%_sFed-HrM0 z@+Y&Z)e&2vj-VY=Q%;=5TIOBm`5mG+_{qy3vET6R@2P=D4n}^UwySubImsdZ|LW(@ z$s?a|kKu)}M;$QdJ?eMrAIsB;az{7sGSh$KQ*+|H z1!*tsU2#sUzFlo=V-$HYL1&FSQ6iplE|c%s!uQ)FV@*#Dz%<_(;v{^%=XZ^@ZsC{Z zl&*k-oI>AREq868dL;qNz&0kqO0{44fStgxWliL9SNJ~X&B>t+y{BNdK8qSV_S!D= zSeM=*@#*<6PguPaG0>pxi2YCEkL7B}1;`V7$V1Skq_9(E;ymY~REhJ=HN=~}M?LBU zYG4i6n<|{#Rk+L6%a_Rc>>teF|D5SytP$qm@~-y3W!V?w0{x;hmR4tS6sfJlyB4!F z1`6*ee!%-!KegvZ4bmNW?Q8S}nm33w*)KJeuL}mC4vl_)2p`ZseDf6NP#@9*u0E%A zo)j3UTBrkJ=OcQ7&pmhz5A(WZWgC27nK;u~SxwH0vmw>?3mn3S;+`x^%%st|9=t?h9hwXstd{^$e z^-}ZiJZI1Ok@8UOCA8md=N`2K&Yv^jH^JZJ?rw08r_Ryg4yxz2h0Pn|mpiQ82p=Q% z-{9Qahc6Ch9o%QwB(|=A+r`a+-8=DE-Y-qOU@bXkPR7q_w~d3v!PG@j%TfHO#plhi zPu57({%{Uu0=sNaq&-#Y5F~hSZM=D{_0z;&{;ftS{8JsqMmXX5Z8c$?`k))48ux&TZ%iV=e3(DZ>9N@?1^g3$aG~Y%YHOrSH}7xvB(NJNS2=oGrY(pd%Vvk_``ra z-Z@L#kH6=RaaX~`cJ4pNhtTKAceii7f&)UXL7s5s%Qw_W!3pry=H_eM(+YcQ{h)@O z=#r1zeECa=Ukb;>bNv1Y`?C4uE%7WI6=L#bVzV$qn>8-uJL~vAdqwsizhSNL*_=mf z1x4;sh{@P9`?06&jr{&B`9Kf99%eYRcFAD-iC4hn``CmI_|Y0?+P`-{zo?Mp3?f7@f0&|c$@tr5%B za#mc4@0#fISnnROhIQ8#n!Dx}8+YkDAjg{AB_3d3t!ZncO8RH^KpZUQXY$Vi*ym~& zem`+qlsS^88hiXac@#EY{jk8*#h0#udq=^@Q|KAlFWKe19)0yw;K!!9qxR1iv1Lu) zm73=1Dn5k0@VsT&uGQ#&e@n)~o%5AiXa6>YX_iI4GpQp6~#Ff(xs&|Csvcr*U9>TYEj&p_glb@f4=M`nmKI-lVylhCE z@7|=?JLk((x##0Ae@55iRj`kBxP;n(4)$KY(6#S=o9|MaIex_4*w25PUw{8UIUnQ_ z*!Dg)y2m>Xc#aLebN4Q_Z+yLV#x!?O?#Ar?cOh<8+h(1Aw@#skw|wSc?}%yTa@zAE z4_wT##AjAHr|JZ07p?27mA}YYUgaEH3)Wt!_2G*MO)hzCW;_>X%qaNIsQUxFYxS^J7C4ErCJR3AjkFokD(EEgo+0O9 z%|Z_e&#nE9;cLw~#MafWv4>PHuk~&@dMWlHN8ae(+k2ga<8Ez48)LqqmYunmwsQ#1 zJcF~KriNVkCTnRgkXm!<9GZhp;CXbwZjGhYIT@3OtEU)r)>t!h2WJgfV{ooxgf-Ka zS-+Zn{7uk6HD`ztt1M!ljOne}_viy&$L?0pUz4jNhO5?svxU`Pww}8G7(AO;WJo@8 zeCNfy&od6Gi?ql+oSP-z)qNJbl~3)O=nKq?on5Q$S%yEGH_`Rwtf`F^YQ^M35p1LW zMO`fWjGO^p;f{sahR+J~GdP1Q#OG>%*eflTEZ#CAch~__>s$sSKs%*@4cVZ!`U!EO z_RZdYHN)(Q=yHGL|B8Xj?JcohVq4Y zd`(>dTLV_rg~OvZRD8P z9QsxELoSD$Om)lEq?N1dy*FQyOaJkoUV-^BXAUed&KZs1yR+=qK`6p6?R+ z@f%?#ys_2n&bQ&Hv(t@(5R=7u4tWz|G|3Hl zgV+Yn-^hCgKDA8^VGQoK4AxL&&x|YdKlbCxt~fW>J7KJCpFx%N zRQE*vycoY{A4?2dP~pF`+DN`9^grOM)D)B#oZ|bk)MhixPLOl?@pu0gd{~iONW3LU z%(%?iila5Of?rh+Zuj0R&NFxfcWjS-9r0#+IM%fte7|cezGS`T^0Rkf;pBJJ`jgzj z#pW*O^BC^Fxe+`C_@HVCW~p0>KR9neZC~wHz}MM6xI=8U0#87GeiWawgzY=iJ;cVtjmHl*z@)<*BJR08 zKPP%%;`PMu+Ccuub*YPW(I0V!JEreY!*UbtEqe>BIgB{JVvF*3)yR!uztl9bx5iqD z^C8u#417)PBkyJpdzfLs=gGaSSSMi3U0-#;w5R40ax+(nL!DEg{<1N&_Cx#VTsh~b zTI*7Kqy8UxhP{Vk$8zVz=dF9FKeBTlE<4<)A=vB?J7=B2Sfa>UCfH+Z`f2)$mMhy} z8Yh^)Jr3%{txOK*A7JO?m#-2JuCi}x;mZBVsj-UBvCkvS;6~!=h~ii`|{5=_xNwl48Ip4-n14NYQ)?td*CYIXy$j~%*Ij~&X##<&@6Eu zu~{kBTHG_wxfK5}X3>9)xhMAV8KaMQKXZCHRjXk8W#-7Mhxs8nqPSG(OXMCpN78(& zMa{w)UFo-l{-kJzt0Db`S~K1VZ) zJJ!M0S|@42e|M(a-S2*xZJ#kcNXksm6@$Y?!CPr;!defXlhB_WPqX0ZXJ>6w@2%lmod03(i*tUO ztk;asUdP6DiCL@Kp25z%&YrCke{PWP%&7eu>(sCh>e@LIq02c>;`5yCE9YW?_dE-Z zWt}GuM=HTNGp>qYzn8%lmcbgA(ABl(7U7RrzjN%C>F146d-oiQ*JjxdwQ2RsuC1}Y|0lFg89u|xz!F}yh5UW?;EYy3ckBWzy^P0a{m-%C5WBx&t*g}7XuU^EK$pIXL zy{sFn{^>_F2)~xVmbG81&$7;l}a?#hw6-Qt`4X~^MHc8y%w3q}jl5-x# zW{2Q-J>sk(vBVaB?B+M`@ndh%lzA64l6Hx~H~5@Q{M3wIuwClvhmX;h#y8j(tUiss z5*2z<#YR(Wd)zsDH#i^scOIWAH&R^78Ccedo7~R|d5+qh6Z}?)Q^CP9+?6TMsV-EB zm{0zeeGckvw>hWI+)Cr4)VPRYtJKe0;%u$3|Mr#0y|mt5p>}KBZC;WB3$mvm2KT|5 z!{{>W?RRnStjAjuD=@SB^7CKAy!byf_Q4X#Pq1~%#Ab3=KOi|rQg`+qK06O@7mS5zrRcNgALz+ ze^IQTan2_F7#;8@HL2vfr>WU2an_y1X4!j&+`-9DQt$Q`_?dFiy>I&44XT+i{?5;V7>*Q=Qd1UU9cvTgysd`t&N%jU? zE0*K20v}ahpTO410TavguE-&_?Jv*jZkw$U1zm9)#a39kN~@~_TJ_RxM6`vv9bJ4@!~^S@(f>5ssNtIw!?2s+xVr?XMzCp5q+TjWwh&h7cn--q6p zn!ZvW%3ZW)FEVt74CjyiwJ*aN3EGL(4es!eK3!`hJae48Y@d+)5oc2;twrEJgO(}h ztb~8dfmxLK>@nxc`)Lj!-%q<$7Fz>n7S|SsJ)qVmr!3D|F>jLNWc_i9eGghW?1er1 zYEIkl5pqxduEg0ljx@&&Gr)*NG8RFiN7oJ-9`4> z`D*rsbiv2faJTp$=Q=XUS*vV@Ceq(OSbO6{oqPLW7CY1!^@W`O*Z_{CxU8`g_dw2z_t_f09KINN^CIVFowMQ`lq~U? zcdi`#(nV%FuLqrLd$p`nIv*#Gf2`t5C&U`o9>>&>)qg0#A8CM7_P85s?9+reT6>V@ zPDXjQDE)@^_%3rN^LyYOw(P1L_^-^J>3d zB_`iOvouD2?CdhR+3MPL#q&L|q-`Ks%4f zSU5q9tqqljrzY1P_^R^_hun|tdoS68Z^5996*!C7E9cy9KYmS&VP6oqB(`^rxNY0~ zgY|PRTZ1!_BhJWT*Ea6Gm_K^;=lRVq&@-bpvn$8oHr#gbxiWXaxkJw07!!|7v5)Kb zsDqIsmB0h#Cbf?3WuMqyn>ar#hEKJZ#oS+wA^XFuSub*b z7QjK(bRV(iJ=SB5bt`km%H>js-I&wHunpRBd!Xas6=$m(+*#h0cr!xmbh<%I zL_C!moeMo8a@>dLbEx~#C+Db=Yh;M?m#MeggJQg+KX!Jf*hgNU$y#UOBE-Rgt=Z@4 zv5{+B<$j-k_6yh(ykzgG9BTGK4t0Xrv~tOf-Q+ALi0>-k{p$3`v8!s*#CT?T-r7ZD ztAJav)`26#9alqCd)py*8xhAj>sQ^&4xHZx>y_UlzhkZYH{kxlr|tlr7*=-I!iA@Z>Di#4I8>;3_%!nb?kG$P3|&5?dI*nSZSgsUea>ioxWuMPa^WpW1qt zXK68?wZUwp?hZW?yWsBo_*dc#=8m`Ptobz%)5cmA z{O{86V1BB#A29en{;La>C4BjkXv}yQr;J0iYl?mh#Je`m*_Fli|>0oyUh72 zHSV>s!4+mvJC9hOYMt9>pAa{W(0*+mTnSnqYB}!Sqt1X0JOD3O?<@nyVwLAivgc}C zn@@^ai>0Z@C08yA|3n>@T{u8u`$@h#f{j@rMq0a19>E+h`K9tBM&ZfLvr7j^ZbOde0i-llgPZe8#Zva;!P8W$vQ&K5;tr`wyOc$KDe! zQ3tkv^gMRZ{Kt6&V|>uo131;(u>#L#&teb%v`}f9*QxhxrMm#+Er&mop9qu@Gg3srEU202khI(XUE=IW04Cy0k68i{v2=~ z>?5!?)knkGc}yGld21Wi3*>^T`J2!OaxT2r>W7O-dFJBaGmFHParRJ5-QHw1^WxNb z?csDboxE1(q_6Uf&R&ZQ56R=^c3Sal5%5?r=W;*|W|B!%F1%?U6VV}C^U_z{YKy1;1 zt9s%2Ps~Uo-{8&-IIk7#>Xh@>W^D?*mw8YTZ477rn%{@Hi{kIpv8;EQ8(Q-Esi{Qj4$1xhU3h#E^%-ZJl&d1L9B;!=DgQBXIq?AzH>%iBDmlh zdpyH_Sa00`OF2iaaf|+Vj7&B|rnkD{&Gr5nCxcmJt!H1~#G9?Z2 zt&+n$B=_2-HaNQuUc@@6?a~0p><}|7;NRoK6!wQ^IOpp9PvHQ|nUJe-gkK%wM@y{B zP|T3^D0@b*V{% ({|gf_KPSNQ65A-b!Ce_n1Bv?&C#r>+4^mNlmRvErAUFOwGS@ z@FtvNXMkCAZ($>H z{Cg3*DW0YF-XS$5`-|lBI!iQ53}t=a{t)|s%j6?sdFm&Mg~+XIfr*-f=r6Px1M1IW zht8VKfZ?bi9KrXRzfEsG1Iu!TA8SM1T;0he{>2#GzM&Mp$R5!GYw4OBQ_J~`aYq(7 zZ)z*rgZLrwwmR_*{PN@uvjW*0c|Q~4fvrdSWVA@|S8IHq`N}%+h4`iW+v1*8h<{Vq zNprHgcbR9jHt4KL@!o*>^89sl_tYU!KdyfjY!585$C?$0!}Ztl2-RqDmdFBi3;9IW zS*@GO!?icX8t)2cInDnQ$2yrci7n{u2fjsQ2xl2AYlR%zUSqis_SD+{7$c^ZQ)@4Y z`X%B;_S_`F@GCs4n6f#7wT}!rfSUPgC(HX0SC=y`zgi3}N{lZSqW!ndVvWOd=Xc4O zw#Jd>b8`IuLg@Y1mWYdChxm-ZRp$Ge;79}JS!ws=#|14u{B(tT7Y9dL;?1B?v-E*O`o8sv_AIK4k;FDk@dso4MTaxEjfRfA!ZCJA zop`mMrZ+hAkA8rk|AH9s4f{*HM(m}}Xpyrt(PbE-<)e<^9&uQK?^$B~BfNi~c+|Nc zE2T}i(fjNPwJdTtXSWVGo7yGygVl9E_vt%g5wy$b=eqvY+rY6X5leK*yB@#)d%&;d zsyHL1DC%;Y%`9rQP zZ@_*YIlUEPLHp{BsmveM*p(0MjKYtad(?>bPiF9eZDd)$6j+C`qFlT@-<{?&t-~1S z@8VOpuxs*g)dQQ5r>aZx$+P$KJ?w^S5tr1-cWg~=5nj_q1VAXtDGb2xW=wIxCR;4-o(=ZHogZXF_IqNBfeE*AG8^PYy!t958qMCiWtQ zPmt%G;7%=r^TpM}#z)A3J{bRWAZKIW^4C3{FpOwySz)Ay|X54&TlQXhYgU!sh0LG z>#@&SS;L+Lek1o@45-7pYCo(;oM^1Nm-K&tue#UzIcM~i)L_Quu4u3E3Fe6j{L7Nq zFVCEW_mKksH(s^InkKiG1DN99_NkoGwEI?xXbqci&@)`8>8kJ zC4Lb@`k=(DOZ?RqvCjm5YOjYrFvHJ#H;lK%XvDg$Q`l={ACU9e)ZX$=tPr28IaOQUPa@uV{I-~aGiUUz z*0s#h#E@s;B+mS^{ygS9?%zizfphH))-fD_KE6cm>WsT!JUL|l#KqKBa4z8*@q~HX z#$9q1H59>-)mkw|s1ak|CJu{HS4x9{%2QO!!M^Pz{x}7;X1~k^Hgpqij6J|=Pl&as z1tAVBm$Qn`mA@fBOMS{U@_+TV%nQVyi}=$Neqclt=eKx^|47sy|fTM#= z;Om{+)F3Ca4@azDz4romN{Mqdre3y5PSGUJbf(PE7?|FU8T}gCzC+^W8QAv*wpPE? zBaV{et6lBEeJ|q!2mH5sV`9=XHX9Hr8leuQR|EI(Z z@cYGDd(J zKnI>w9llB%+@=cd+hNa(;AZxbR9L$z?<6koycp}O>hoHga)yO@#vbR#9tC?D>?>DG zRGnz^Ftst&J#@}PjOR`f`#2ji$9meEXU+6?#C3AGUQh< z)EZG&)t&)+ah=8NynzvSEYvi))6U*2a5vY<2Rqa#x~!>Ks@$3cHe3Ck8M(i+1dTD5 zJG-1|YL|R}u(4oj>h<)f!CL>Z9__p)dpGUnvbRwjx{W=N!=EHhbbhe4gB*81MNBRy zeU%u-*vmNI8j8BEa%!C6EH77WV!2xK`^_cHL(~A5bECho-$p+Y=k8j!QgblDXF9ic zh5IF!QOyT=p)s&8^)#F}VSU&dqyJA|qYsxSXg;X^nza(2C&_m>%gtCR#+tid&J2}< z6U9E*pOs*b?6VLn)3*iOj`#8BveaJWK_$Qy(r_nYytlfw&Yy3BRoSo81Ov1`xMu$# z=cf)2W5zsf>-hE$SeKOVF~o0QfBDbz$8Z0Y8twC-(_kO!4tVM=cUC>7En=qQ&%U2O zdiPiI5Y8aBwMdNLV81%lS>@R7K6*?3|Bjr8eMT?v(xdO^kKg?=PqJPMP5P0juZG#8 z#6M}UIE4sq7sxq;1# z+!5ky^OH;DFwP9@g_@Exo4`4U*W@nSt0Z^8{R*?ChkGI3cK;*z5OFH!1L_aVO_n&H zDfZXA`O+7!(dCBALw&Tuy>4+<2H?{>a!Bxx>e#n=pQ3R&^|cDmP}xDN345w$hgyef zE)DQ8+wlE+)=qeb8n(HP-c=9mOWjWA@cDh>Dpvt?|9_30}5C~AX=+0DS2?C}fpAGwcm z(ap_`55$18azltGI-CppHnodtB2~$)#O=&SwbL>7M*U@ZeAf3aJbE=hMl*i>{`Ucw zl|Qdeg)z=2%CMJsmve0Gw`aEV@4H{o8-6o?`2MfhLvU2EM)&IJ+kYkopuZRl-I*SJ_#pO6IX6z9rxvNW zfosyj2Fsc1!wIM`@7cI*i@K$?Z0qv*P&c%`@f6)H;tkfe&i!^~NWrtuS+OSfA$C7w ze#SmTITvygHmOahZ=T@46U1BMDb~d8UvVCl+E`*q@_yAIa;~(vSeT>2=PY4gt$Cz* zU-?Y(JCoF9)RnyXJv=MwO7gz!4;rwK7r09k;>{*&rIv{`BQ>d`)FE%_iVWXT+psu1koKxTg%R`oD zh-;qHmqo3WSTN7JtBv1fkIsGm4xR|P_e1jZ+vFwO54C!_oTqiT=k^-zQA?eXdwEw| zoX>Cn{NJhn;m?WZ%kT-DBd8vuJuUi0`77@E35SujA$QY1tB0$tabEM$lXu)hVlr%> zTqre>ozEm*D(1M*pe{{JnT78Y2fGpba86bnTVfnwAE-V3<|*nG1#TAS+uE=GBtblH zf1SR={z7Mt*auMtt1vfJi>8iEPqJ?t?C0fYKL@_g;A8M7;?4s3%n1*WesALDJn@Y7 zu8WP^ffKU%3AT>!P**~(nDs;Tir1;b4!Ijea>*KZ)O@Xj&Fg{RW{E5M_)KT2G&uiB z;+j17F-Lx+_DaAiuw_f|2&=@|^4*QqO5kPAw6q68E?W-XdW^Z6AC=ZZ{b)q3!P>0$ z)_S0NCX417o?Ge-i#^uTSc-LB!uOctsA*(OX6`OlW`BDcf9l*E=gT)ar_Khk&rLfc z@4_Bk^>oy%S1(N7a}^tA-`tQjkXI!Ky8vg^TsQ}ZD?<&?m?&o5n?0*tAfC7ne~ElU z&9C#1!NtLbX87tHb}J=6iaVrcQ%XH|?rxLsvTxMc-}YEItI4^^Q)a!)s99OJlk2q3 zc~?RB+3#=QR5%|UZQ;+zM|f}h?XG{Ey#!Gy$y?F;q( zi}!}P%=lJ&oipUZ8TL-zvpUqyjV*&KB(WpL3~GXz6L^o#bm3v}IpQeJm{Q+$i8w=j zL}$?&t0#EARsK}rY8zK+b61FYlkAlR5dYYG46paK%oXC)>flmWbul>~NN69$V^+2IulQQ`%Tf zPM13CYCFpZYvP;jZLr5#-juVSJ+CohB5k`hBU`@yV zefeQ(4r+s!;jx6dr0798Us?NTUZpOSdX+`K-#(f;Ik!5AE%ri9RkZ`HspwzUsePzz zJ-Y)BlYR#4X+bxJvl%qNv4vaUhUdR{haZAJ$Gw%)Y_CFz&+fxHJ@>`0a9hx`0rQqy zG~?V4E*a~xrr6=L;ArA->Q7k9avsnU`EG%GXy2;+K2`c)wJ{a)33=*e=4@F@ZZQ|u zxI?alIfk|I7B;|o(|~^51!kOzxj17_UCa#FLR>#ZjX>QsH9XR+Sq1+kPr%xHhM!Z9 zr-=zD zQjha7A%5A%j;Q@8UaO9%oP-WogFWl|6616Giu&M&&Pr9c*I6DpY=F4FdL{NtPx)gl z%RZKc4p=jBhtGSav&Xpt2L=PlaL?Tfa~Rja-eLVrg}ba~OONke!$vuW-T7VWOf7;7 zsdMuo*wYz&QJT+}+p`2FWj|t6j10bt{2h64a#F;IhuAUuO|0b^7w7p7=Ub}Tt$yHu zbLebJ=lyiVR5**qKmM%m?>foP7bon34K{h#Bzt9jQu}PLn0>`P&Z;x))jOGC&$P`Q z;xcs?#HbV4Gx3!y`MJ26Tt#bY&N{16PxS1Isor??_xTNG_jK5c2{rEl>ufCIj0Jtf z9k{gGWcjlf9+G!o<(_h<2baK&uqASVohRgs*%UrQe7^yrHNI!khyfZUb(%J@3}QH|{>4U!(T2hwlj*n_yJV0xrniAs(M# zKb^~DTr}jIIL~D7A==vfjC!~FNA0)$WzMPPvbyDs79~ztRa$VDtI)^p!4~fn6Ka0c}eor2sl_9=WA2!Mv3v~njF4S|mQ}&WB zU?c6#h;uf~ZENHK74Ti>Q@KC(eb?xNX<;)4}3t^5KiyT9eCOF)tH|K zoR|II;XDnoqvq!3(DIoI*jjVLllT}6!kCkNGk5gv9X!U*bEd?#);NoKo~^;zTdp1i z9X0ihob%qs{(rPsp=q@XP6g6SsQZ{eY81;w%vGMuABD9_mZy_YAO7j{U7=)pUXOg zF|fT8*66$N)OM*M)HqvB?1S1j_7kW0efxagAJ$4R(I-26j)dOlp-YeEHMx5X?6tNxW zKK&`+5u5lA>mBAQMQ0w}eZ|jv2H|Xym#C4wMcg^P`4yi_Zp(Z4XBmF9w#hSP?fw*Y z%bA99?Ddh(>nal`x508I+&5>p$eXfn*B(9f5Qf}&^+b#V)cqBU5epT&>C>wve^qs%e{3Ii9PG(DtG32`Q@*8Kmm)E`zDd5^4*A%#H@^i8uE;)#Zx)Cb9d$+JpQYx2L< z@D@w9UN;QgIzcxuhCXn;~L$d&j{{ymo-(>p-y~) zbDrj2i_eQ8*^^UbuZ^cicV3eF;eT0WX3`RmJkRtn6_Uh@pNg|S?Un9RIPtYpFb zlzSB6JS-ERoF#@%@b7X|JWC1Btv-jnbe6qZ9mdhxLUs7WMyx-n?G*(hXkENHfBfPt z`t09^UO8*9=2Zuuy`u)mY(DZj?YMENdOf@F^KL)?F<_ncl8U3ZgB{0i@vIGEyr40} zx~cVMeMMcIU2?k_G0r~gzvG=FZz+N!kHA{D*w-R9ak;gLt)+j7&k)1PadwScoOPq_ zikyAtY&7v}YW^7qXpils7yETqLLHmwtW4)@+B2ihs`_Tm;j*Tb=lz`DZqK!~18wsv zv75c&_UWJE48^%S>KhiwZ4<0%coy>pepfwUG5)moQ@_l*8!y`DtA>Nz^@`kG;$L~@ z)*scLmrLy&GqtM4yY%DoNYWdeE8-JlmrZKh-a+lvfalg9<*@CZHGAyr2i4zbAJn@x zm$m=Ro)!PjyDGnIjdN;kB-CVix1ejF-{R-gr5}O8sJqhV9$kI@7w#{$3G8{D{K`D8 z!P&2{S2fOw{q@dwx5go+F7IrWdff7en1%CY9Y@U-`5X)Ms4ww+_LMAx$*f=l3)JT7 z)Ck(d>h|NQX{{D-ma{9L&pPrJ&*L3XV>xh9`3!xv8s{DQ;aBNxw$H{spfI`_!|DnbdLHL>F?MvlRiiiIB?{;H_2ooEca~Y8}2F{G2@JZnb}aJqM=)e@?<{ z>fsZd&zdH;s$2U3$Js`!+jC}JugKmvI6vY*a$^Uqkr=F;^Ih&vml|@-*p(R0xx)6P z=v(bmQJ2>lzw$wA*mvs!&b3jKR;^2QbnL}k=&j8}_%_=ek7h9W0PXdS^Xw1!FFwN_ z0<{L!2+#9gatrL8bFQ25ygtIbV-u|Y0=4uTYPD0>Iez+Te&N}VA%@+$|Ly#9@Rh;1Bj z4%7)UUvpkU$66!b5%f{98@u<>(Eua6_5H8RD*FX2oEjamS&Vf@8NzKhOb#$S4_AGI>8f9vaS}RO(etI|1b>@!A;}-kT zzpA}r4}A|iB?mBu@9@5V*xcvytrg*$c%HzA<7eds$cL5ZW54fmhnj^sGtX)*K%K`O z>Y4Hwj33okRA*itEa$CnV2j)M-QXKoTWiAlNqOZr={=ZYH|(JcdL`U(eXPBS&XbeZ z+htutT)?|qw+{15vD0!hwy-B^YZ^}kJzsoE%^ZvUP-A3z1I{CBVQt=+-X2D|{pNI` zABDb#78vdT3{XyJn)j5~r%hKU!MrKVtK!Ux5%=gXa6Q$(kw2uKu4|`;dkJ4EC*2y% zO8XqotZ&oLpUfAm;zvsRYI?~*CJv^itqy0M`xBwYt_F+s#Txmg^#c8ddUMtt62u8h z?fp=n4SYY=NE>cXPmY*p0pF0|yFHKMe9redRo@KpQfkwj>3+~0jpMh>hnDawQMmp| zYT3qdvrp8gKtmj#;#szySz{laA!crGUhfP)wWQRnsAGrB(XH(*!pZe{Pq>1_SlEUf z_D2q;ak{;vW$?OGuua%Pun%K+pF^HfgE+8`uMlr3UnIW&SPTh#gjllA&m9o^U!(70 zi+;2w-y;{?I;EJB_+B4OAhmXkPx)bfO#L>^a~XG5uqVz{2zuJY?7o{=raDR1?Bu0i zdh%+1?)KxrDcHs)%JDshJK#JL>n%m>;{ac=&%C>9%qw~D>Id$hJ)88y;-|#Zk3av3 zxPdr;&-_|^*#ksVeo#bq+uwV9Wq=;W~)OzG*1`Z-|lH9F2KEbD}N1<<7Ex{zYuk~{^l|HC!FeCVo`%6y*{w>4*lk+22)Ow70hd);&R*Lcd*5s`T zC&6^1+;4l)(yWn~n!GQ4M-=*_xp3ehV6UA$;|y~B z=NdDSj5*@m&m^Ctt(Rw8=B$ecTZ1r{md_zyE)JF;Ct0jXttz=5J#bbzwA#`p=fHjj z&z--w!u@uAt$7E1B5Ehr8Lfxa!1SC~A2`bRVE5g=Tw@G%V(bBq;%}X&KBEpOf5;xn zpc99EO%o@p+h@P-GIl|~U|b;or3jZm%*~$oz*z(nUZvKURhx{T5#K6srd#ACYF_Bu z&wcuyyYVdWuBQ0t5^G^jEibomgjNq{v&-LCJY$^ORqF85+vxmYFHhBGp?{#Q79(pQ z561pk`9t#SI^dq_r(O8$TkHqe2=>OOTE3=b24sUV1W%ki3>y!B|CI7IUEz=O)8@Xn$+;V%gv5%ptMp7B)-W`4ZnN z9@*f|C9%QcMb`7n#P-$?tmUUT4`Q^&TIzk+CnjgrUL9w+`yKwgy(={^kGS*puqAo? zomy7b%4_UP5&SH@4i5|L$sEPs3tE?(U^&L7++%Cp_J$9r%g5=jH%Cr@xy0a-WU<93 zJVU-0Uu(TKPMzv^Wq9b=lBNDR@;I~u8rz&z@`IKdKWLBe-S!WdCpo{xzK_M;9x)TX zn)feilQ?%`Sz>6$4`J>E@j-N8?T(y-HJp0ySpzkM&k%2@+2!m|xp8M3TY<|X?ri*` z|8pjhJ++>r8aAfPx)tmv#m?z3^wY+1zOz`6w#?^Lm#fIL|Bf zf%{VPFqZ3qfp3u8w>Ym2{J&Z^&YaGXM`}l{6RYPO;m=v(tO`9D_Jl>acXFAcyqk5z z`}DwChd-IK0X{%}V$Y+xvqATOb^6~l-Dj7c_m$o0?>Db!&GOjpcDp4pAyfcRIp7;rvh7XPZ7ZxgG@_ipBgrZHVVS`F;Q0eP?s--*>}Yul0_PKm4*8!(Vkz@iB_0OX`&B zVs@-IW5)bS-^sVz4S6BmP4%NX=@AbgjJ4Rb)I7;lKRa3uN3RK9$xqRFTo?JVlOkEc7Z9vAdxF?*-qf9><%p5LkN*(;Ym%m>3%={GHP&gS#) zb{WDZoLStb(dun`i)ihbub+%pW3IqH1ovo%yWCH0JY7C{t$f2?9rH$${k}i)Ef^~s zmgB%J@eJWD`s@SOHlI$nY&b8S9~?7!+TzC|&CGK8<>KM>Vj~=OJ=`9j#vVEQUoRK; zhrUWSGpe`VS@vv}JD)TG7uyYZA-f&);MFf-5jg0}UBhN}yjgyCDLhJ#eYTu;wphiF zKGyG?hAZB$R--FA-}^g#y&2n{B`qZSm}B7t@Sd^a;$rXgk%sJtVTIW@yDKj~db?Su z`1?Ec;l*dWl4p9ZC-n>IF~Jtc(!fyX&&I2I{?k97pS>S1D{i7bFdZJ;2DmM4fyHXP z!&g4*Pdm{+{oSA|dy+lej(>Oet*@#X_{8$8{4##zvmbpkU^iREMY~6aUSRLQJ_gv` z1iQ_C;YE|(*QIy`;yx`kx&!hIu|)qwZ(PkPhgvSbg(cEc+slXRDVbBF0b+JrFKxfP z$nWq|X57t>crK@6+w0BGWOobUP>aoByzyy#&M-FmulSd=c8QQ*{Pz>Vjlsmar}GnX}x?4Pxa*O-wbsJUZZ{v?as~e%DMaz zY?CJ3u#>mX(3gZmnHxHK^P_lD;Y?w^W*{efN2~c~S^%!c{0+{h*vy~Iz4aj@15SqQ~57D9`OXPgx4(>Yo^QVqz};o6t`uCdSt-XL( zJ2P{*ZN+i36H^Mq#L)OLlUtpD}M_ z-j)V7E+YPwTz|Uvb}c{0{||e5dpGc57(c&`kERbh)MxoCm=(=T+!K65SQmb+y*+re zX63~keH48YTwgQId>m|AuCQJmhi|agGnvb;Pghgo)zV;r18wBbcB+%jZqkXzk5FID z=37pCUS>sU(ae`e&_5XK`9AE zco=psw#Yxsl;~Bh^_lN}|DVs_`tt9~lgke}?{u+gT~9Xyu+WbB^=98`oS(g0U74S~ z)){=f{6SCnpm(HSPwU7Y>B;hSoDjVrJj0t|hcCyA|LNcTmqDNIpd93dYW;V<`tzU- z0;{&aeLGzEMf`yLgqdl)qW$ub!}u8V@%%AcI@E~8Q~1|G_{m1I@p{hs3$s0Mb|5#O z?>wBEi^}KGbhbMbk8AIRdYt99c0e7L_m}~OD=b!@Ep|UJmc#0vE9LD&|GB?g?6YVc z+GV>|+#P9-ZsIr}P%bbFcg!z{Q@gzry7+oJY;`8zDi*9}yPMBfn{qRejy|hCPdY3~^ z+&eIrKeivAE!^#D-}^+p4j9|`Zo3QO3YpPYBV0MPv!}fk_g_4JY%$%ia+1fJIi#(9 z{Kik}W8BTg%7^;C>3Wiv)|)S^$7e19zl1*3V*YM&sF7Z+)~I(`E_=0jV(uR|6o(Ri zEe4Ktp76YA@|t;=$`_y1gL^JK>BpaiDdnfw{8~PNJ{4|7v2jK5H7!>9(GvJwrcZ(>peEqqhoUDevIV>DkTJJ76z% z8St5Qzpa#)PjoF8^6z@9a1c6mcIiDH*M)``AA!4fuidp_O>1+==Q~6uky*JCFzWmGoeg3oWae?bi7Te(?Q_UCcSC{X`;Zuj% zm$P3i-OOj;-|>sMq~mGc%*1Il+h8V5Pw=Vof{}8*3%vvLv*s1l7W19;$$TLEm=BxI zr!9n8&%-pcN50#>=Xo)-vmx)k)^$FbclXc!)cabj-*qj2J61krrg6aW`waSA_R(6IVBfPnzqW?i}#_tDSfDL*s9&JoQ$+spmgRd$L|4J;s&pmoCRz z&qyz5xfnt>0?xeFbG-L%x{cX_vp;$IFulL{tYII6hGck{zL0oIqtgtA`q=IV`MKS> z3w<^Y6`#IQ9GfcNqZ0*B#GRrULeoV|G!rf^UhUkPF%p+A7Za{^|7v1=aPkM9 zp5+y}VP_GKaM)dvkAVNVKG^Lw+~D4!nsn}Vb!P8S{E#ccE#~gaH{+9*e{bXqS9`zX z;lnsW>PvjCrTFk_A>4$s>g>gGH2nS1K4-ODi_M8|Fg88vEBoQW#iN5an$4@nGwe|) zcZb{Bt$S47Y#wL3`yG5A{!Q=MES?>0tDWBm&Eeh2#&JAGvorOzo|4|g#!H{a5&uQ+ zp_=w6A5=baEpCon%)5XY&^y`6x507bIX_rFEvNXf{Pn@`Txer^r}xUI9yUAjY#b&t zCUoQPy!Ulj@E?aAZLl63CpiCN@$Fu_M~~u2?{r@?#WL?qu4#Y7OtmvS4#qm{HR`(g zPa3bd3QOg%r(vl$U*@rC&2Dzj!|n!}e`zPGgU!XPch=ePz=zGhPSsD}Elw}Sj*n=aP0|J)5-U7RIg|2d+C|r59yVx^z7X4Ts{Wos#Y>9I~ndany;b3WoAYG zGH@mPd^qVagPISc&r7$=J_P)^q5jUU>5w=Bb_A+Z?4n;P@1tozThiYzcfWQb(oWti zt{vAeS}kXyy){+csy4*4d1C!e+V*LK)t?`DZTOmfzF9&!u;(z8j@siZ}_1ky<mp`PJVGr2Um z@rC71m%{)?y059h55x&6Cf!Jj=WklmKPUdb z{^cLfpDAYAn>${P0As;ZrTu{8_|h-_G}KdeMe*afUbsT~LUugM-EgPZ%dubl^*{Bj z;T!4tublR-amK>zi?`daf0;ef6fY;sCaz^y;^At)yV@C|qm45@ zV0v&n+&{fo^AOE#_L(dF5&P{J)YG7E=ri@Y&2;e9qk0R~pK?dDS4%^VSG`f*dkJ=& z-&G^dgjL$Jh}(uQwwOlMXy-)^dJrBvoZoU5wT8KRwZM4qdaoYJe6<~piCS8(TRmj% zZ7o}dG1I)W`^8KaUd4K!Z)RrLkyCDbqvv%qZaJMg*ur!51@F{Dx>KL&M(^OkhhLTl zgkNW;_Lh4GQ(epD&i`EBVJ_sL{@+garpD%v@E+k#c45=nqp>_&y@Ye52Q~0tnvt?o zWUJ?epD`W&X&!E(7;P_s`BXhg&*A>Z-`98fy6ddB`PttMTAGKQ1w6ConrBp>ot1Og zfvcyu*z*_9?LL@{S7erPyK|>^F9&sOb&u%pN<;9B>vUO!^7{CU1vH}ed1sq7)(197B=7&V;TM|~0+ zj^>M3%YSFGGhA7?nAtshtmyQs&Ey@pB$GYAu`PZXhqWBG>l)_7!xh!!xbZNH)$A9( zIP^~Q1GKT>pmzQ2mVfLHXQ7^A`6*2*b^~j}nVRn&;7K%!j|Tmfbm-%m9`t$oaF@bh zaQ@6&T|R6-y}aoxZMC<)IzK6QzFh4!=*orx(e1T6hu)T$IDfm|Pxk!O>do_icXc-m zqP(_qKts!SPv;9R_0I9A=c_a9-;-B7)j9c(GsnXYg$vbBW?G)8ez{cKzf??<`R!)6A7!;1SFWWOpf5^?!ko*XCDYl*W3a#39yfK$TJdA4&!%5+ zGaDPU4?8FN+;%$dm^ZF?OOFSZCgHcvpyx8!$E@0T`kw33%%BL;9jiv zd(MzOf^gcGKmTKO{2$LhhL?Jqf(ih0dj177ui}|9AXGe6?r1&ti48 z1^LFW7{3-e&AH_YCB2n!rCiO z*3%Nh&F`)B{O|tk+jt4T>N@Z#;>E}D-Hi`AlfN{lLCfOBPktL-TVC+tXXkHz_I);+ zj>h}H?fj=R@ZN9E@Bb_viTA3jM#EjThskbPvmr3N zfqz*3BHzP>zT({S2R(cKbGBz`Uo2jr*`$enjy9WpFn9@OA5QR;x(+&icu@mHYJ+hMrJ;CH`-(f0}37>5QpmVF3?%b~igGr#$80LHUWOGyYh~EFnH{n9%4E1fU=LcuC$shAed^H~6 zR{T?(%B}DyaU2hSzy9h*dp>CMuJt`DUH7oNsW=TkHJ2iPSg0;B*Qg)$KX)e&{Jngc z9lEehT3-X#s<^4Xo2c$s$-m<#;lb;ls|mb6^MhiIJ$ajPM>hI=d<0mVy|^3Yc(lUl zsK_VuRA^4^_P*E60Cz8R3+UG_W^cm|%z6;^S`55~YIuCf0V8_YtU%YM9{`JWMlQzT z8ZX~juU5qY&_|+ij)M+UgeyEu*J`}a<747P@XT(Hn+RqJDv$U`ayF-m-{=ttBcjy7sAqDD|!$(>UfKD z)ekUYITY@zyjs7RkJme*W2pyhuaEs@KZxI9*&n9aFU?r#1W$7wa+zFe-v zzuAqhZ#Va_c;5SvvzY@peEp+A1JQ0V8v1I>Nii}UX|1}32Edg#19md5hpF5uPsa5; z>fF&;r2Vzl^_!1bD!+K+=fA5za;tL^o?c(+a?j6P&uDg!t1(eNbhV$gyLPu{e?9I# z+~|z|CPY=AvMF=GE{+&1GN82JCjYUX8(b*xw5uwBKr`ym_%}-h0I^v#^Ei z3)jZ=>+y(JYLBJP8eJ6nS#~htOr12Zb?bxX{W|CO-%XpW9Bk_B`EZ9Y-Ex^FKa)S_ zH%Gdz`EnLM#f}nuuF3eK=6TgQo8`bb0CpamCP~HJsdZCK&+wlq21gFhaZIrj@`_4UV z_PBE|&lz;si#ONb{G$KPF0&`|4P*Ym&AX4C@OwC#vkv>kMWQWA<6w7?;Ch#{s+}98~M(oa-75F zTPAMh}Hc2e0H+bvtVB<zYUdgs znIF0mUIzb}Ph)L6+nLE9t>^Rhx+eZ~`GHxY_=wpUJ%x+;Y22fc{DZmq=YR2=Y$yz- z{Ea@swUY!+9_hG8X=GAsm_}1ifstAcICnXKtuFz31$Gy8Dw~ zX064dor?#aH8B-D&Q9qB#?s>6!9Hwdq7yHtkn>SnJi5a7uc!a0qh~i`l?j zy$$=2aQOGDz0L8z`q6jokUy%|XD&0njB*~l-R=5%JLT#(!<+C(hT0{}Y^WPM+jy|% z*5o*PuZ!KobhX1+z7GaBB@gdDuT+RmRA?mf$z_epaK0ehmS0|~#%&KhV+wHS7YftxS zF?-d|qJ_`5toMFs1lfK?(zd52=8U}{X$|hjp z@+0=LlCPxuM!U+q;n_RC$$w;b`6T?Mh5Q(PxBL`84c>iuuQ=K>8|Fl+mFOJ6d*zt} zZkVmy|KPiJN~iH2R(o9Rq9ycVew|J?o1(io>pt?s@S}UxpZ2wR_I4ZCIY(ClmiNTg z{mykf+5SQ2c+iUJdyeXt@SiVz{N12?D;7NS z)9=Fh+qqny&%WiOVzQYc{g~bUc~HF3r?9t1o{3Yg?`m%c-8GmrKfT@mzuCOnLH4+m zpVjl#+uQH+ZpEeB3M=1k21=~(9_$r>gLjBdspur1Q zgB`wDPL1=eCvA6{*d~M{5x7&Y#JrX5(<|)@NI*Ua`Y`Ec?3BHIJ9$ES5*> zi_tzf%GO}(llAd7(z3sh-=}|R|Jse-+giF#c3Mo81Hbsux7FI}i!`>XH{{bxPk(g& z+RuL*AVfyvT;-D{2!Z`S9~f&9_p@sM}vM_k@I>)ckiK24kErSl)m9S$>Bco$3Mf@TSK z%DJ~YFF(e6>V3dUp8e(b=Wl%R$LzA)zw={{_4V$7Z`Rvi=pEX9&OeI*G+7Swakuib zbh+>j_RHDvcGvUmH-B8L>Fm(Zo$lG2wSphvOT< z!g=`QD{*#r;`CkJH=C0m&nLh0&3|skQhFfoe^+S83*SqlcO@KcEnHI1<|v;1;M+Q{<}UQ9r|V_O$M@U4a)*BJ zkSl#O%of66adr-0ucw}E-22(r14nu*?(1flE*>Q7ZJLinRT9E}zG+qc6f<>Yki=kqJ@ zUZ3f6@Ko`M^i6Q$%&)-b@Uuq4{>SQB!2HVJ zl-|zpYo?j>B?*qpp>1@2zUe7u4$I6MUJT+U>W$I=@@5lKoFKr)KU8-Z|T| z{^54Byv6>T?|mD`p);HAgTBh~>-GZ`s|S3;JE+#|Jo0t+r5<)2%P;hNy8qpBHF4st zZ~wlS)?U!^`J2U5JE`xz|Mj^Yc+T2!&wBIuVn=!0;M05G8`8jT>#R7n z+V!dT_sc8r=x9s8e2?>gOXZsK26=@XkDbg_=iI2*1EX2XUz=5*JgHAsZD&T^&RjZG z!*258kv)y_-@SaXIVgJ_w|akON{9XH#blVTdQVKF<3Qi|W`B23j(6DEF-zk8;HAk^ zXtvUipDrgDO=o0V>?tpq>bZ?pBje=F+-z4tz89}o{Y}Ggx!Mh9$6TR475{8zZ?srq zr=yt?TsOI@ITCX!@Q`u3F6EYIuZ5wNv+UOAID0!^5oTtW_QK7u*y@ba>d(ouG&t_{ zUb{|bL7(AT_GhoIeh>`$QD5lAW&-XEyo*cu!;${K`71NE)7k7~??ulO_flU_{h%K| z9Odb?(Fd~Z;Y#HH{xcwgWX;q|Mt(t_qc)e*|wUOu`7fo z4P3`O*JghIth)BuZ1`4li1*8B`E5KJJ0h0KHST}@hj6_xZ9Rhg)nRsEMqoXTi+TCE z?s+1dYCWF8YW2%Z&xH2bcK&oae?M6~Tkn0+7~BqTwKJEF3Y~1{3U|o7#76OaBOkKV zS%+ET;+R#r`@uKo&wcP^?-7q69(wP0t8)bx9<&O}C+rlyUA$V2JGoxIFw;C3e(8K? z^4g_+6w z@l^Efo{FDNBSZd#lWJ}cc18ON{~mvZpLN!8T_#|=VbK%uD9zRJ&p6Lx?6mkg)GU3M z+Ejmnf0iS#ovUFLb`+?|%#IKIQ+39>hz0I(|s__r+$$t{veQ zcb#vgmH2L2ise%8efyuzUx_#MupT^)11v}n_c*)R&9B`J_q7k4R>a+pzC3^Pn}2GL zakkxin9dGysdtC`q?$7OT+L4L_~g&SI|#2EtCu>HKai`)fv)$Q@WhwmQ`(_?_HKF) z*)84x4)FDA{ptbASk;XPnm79Ift!g0}@;IB5N9~0v z7EhLAA2l;We`U+L&abcbyy?bxpL&)D`4@XuZud?O!W?lFw&QNm-lqw_Uv7o(JzxI6 z7H?rMyFVV{Nj|AuPYnz|qoGRMe!e^p3?I3{nobZHO$qQ6D!P7 zU91L&ZJ8&aq2`>4X|Pwj*8Gv*T`OkM)!N9f?xkZ!$9*YZZ^!Leyt0vSGUpa=O6=L` zd+fP8%9icXwj+pUEo}p}CGEnU^hJ(~A=};aLUDDY_rBGFcI&$6b^gizO&i#uVG(salvzjOAbAA^-nntObg%L4mA6$ z?PWab`QSRjFT_Qbcybo=OKjd} zZuEP@{;u*N{Cx3OKE@W!7Mb%HsUB9h%}@#gmzM%=mNaN_g2~IKDY^S|eAhsy!Bqr}O+-wkYR6eJlIV$F7y1 zt>!!JwtD5$-&OyZ34$FpGZ&ZTnUBAz_6WDj|K2LD-3!CJ_d#}+U!%jf*K^t^e$z`I zTRThB;*H`=y~O-94fosTR>hlQ-bDDy6N^WEZ+(MoQ*V1EThP!a$ zxYUC-S^mO4_U-)UO7Ctbo1q!wyX**1NAm5v<&fLmFRX33xM7d%$o~D|%xz_Z&wldT zYOgn{1Alk^@MmA=WB9pp@9Y3)iBA5=w%$zNTl_ThfwN>L)eN)V8{Xr3wzS>5mGg-$ z`X3|NgIavxua_S_w%Y7^apFfCX&UatX|L!0gU$BUR&&A3uT`hp={1x8JE-=&+L^JN z5m)+h=U}S55tnGdYdUxM+9&m2cY3EQ+521=bd3J~o z>*-4Bk$(B-e-2}AmrlGodza~%<3`Z@<~MO-VP(UNf1i)5L*K?9Im~#Utw%c2yPC=u zPjv?I8aCVIu+}?+A3c2k+o3PNm=?ua*ue4-L;YNSr1~3=hX&vhE^pUwcFFu5o|%~- zdJQ-=_NuJ(|MgVpWzOVd&1K6WaoS;U_dfh~z|i0Qs(Hcuu9TzED<9koV=_yN^JaI{uwyX0qgh3(W|)z8G+ORRa{|ACE$VgZUF(m`b}w>i8l5zxPO6pt3@jPn(6i@DhPtG8 z0v}rt3p;OXa;xc*$>(W^7^mJm&-Yd-E!fh&iPsS_tWM6 zhxwmZtMgxN-_m9~^q%eSoZsE95f24EBX*cgo2jnB=e%+fW>#J+*Trk1$6^1WJ~o`6 z-`px6->>#x>Al&lFq*#2N`CRp;@h)73s(rMI_>9nd#1=V!Fyk|tGhFhKicj4&cYz(Ptxq~nwB2+-1DdOXC}kk^}Atx z%h{Lyj2%#LZ#p)5)nYjgulk0D%b?v_Y{H|mGZJ@{uDCo=-b7zqZF05eBWE;+ZDvcZ zj$d-QIlycA(pP`^+rcNAYZqVaDWjLBr+|}c&!asMa?Po@f^=@|t-qiDv5N&)2iCtA z-ay~!pgan1O-!NLb!jU;Zas*F&M}Qt+-G>?<*oxp0vo4+PLD~S*j%UCNA_k;ZMPcd zYM2(TuO12>mixg|x!ifB|9d%p*X3f+g?{eB#;tfp#fP3LP7I8Le;O&aTx&jv*1tJT zpCiW7jkin2zTDZ~xgN)ZV(ZQLWjA|=N1dss`plcAH*=(iW&ZkMa!v%xDS;^S{s7aY~6 zSqZaRs;BbGm;Z47>wo<3VF}-7i``2;f#1Wi9Q<_e6mNJ#U$9(zHO>tlgji2!Z?r#y zuAQ7Yo26f(*RUS;2D_tefpe(mv($NCf~RHEaz1#1orUY!vAuEXVR{*QCuXB)fY?E5 zPtVgIm7DwgxD>Q=2As0oVj;h5ris?aPMW6gfAgpF7kghz*_mf>qrN$PMmuO8{`9-f zP-nS3M((FyB^Sc|wi|4=XD(;g_oN+hDeMyl7B|~jHE#z0rnLkw*6)Cct`+O%I;(nCdU@*NOZ^$x zWHXETBEAq_Xve2rKrj6I@6O-<<9|JW?VCTJfAW|AGmYhZ>zirC)$>2;9Kc?-vXQxF zB_3bcK7VrQu;0sn=Nrej?+#oEwHKZ~KZT>Pm0zc015Xg^&6%66(&sn#b-(jRPX%r; zSDq!0H`jqvaTZ4Qe0=lcV#Hpx?45GG+i!#)ccyNIW!o=eSE?EF^{9iK9VYgL z&S$H;oz+`mW3-DFIzwwi49GUi7vM&BV65e8<}2_z)N*pgtv+|qX377^@98Oz{r7A6^|ks1lf_TJXI{d-+{t|9X0~Z2nobW* zCUp@VL|hL12>jLSJ%inF4RyEkMW^UeK5-^Y*3LkCrsZ?;p6mHzoJCq8b~@AbTWNOO z%n{uP_=SBn@L6@0d7pvnlcs}KTG<$!ra_{_a=5G)B0QyNM^$zr94$2Rf zZoxkL+&<@^UN)@OUK%^kX7ek2?@~F~T+ex?b9}Y)t;Wa4qQ7~oxrAr)gEU62g$G_M z_Nqf>ovXgz?Boo)KEv_KuXVq2sMBi1D|=6uhxI&rx9p509E=85cB-_iaY9M}ywu{`>2exe=|e(_TF zH(*~qTU1*WSC$(*N|Ea>x3R?|*at#XtSO^@!qu<>zqc_QL>AvSB#`KdXm2 zTK%wCA7u_#s9f3}u*rNC|D!f?E?!PsZn^vL4q?r+*&&SsTspG@creS^8Lp36L;Aii zWY_k=iyzzB#$x_{y6Ycyl;qyRw6deu?j0?40NyJF{wDoOwHW)eB3V2|vHk z{j7&6(-mI{&sohM(CV42hO(RNntAx%zq3b+(roHNwJ^RoE&9vF`uX^O+pqMQx64Jc z@nLqSYo$m1(I5Vg^2PAta@AYa({KLt*YV+c&iNBN^j3>|CvO)+$|cQ`oEEcJ+Ce{8 zzt8!{Bf@2XshJO@X-wA{rsf%smt)|B%QJ?ZvA8q+y=RS&CpPGdsef>1%>w^V?8-hG z&vdC84Ij>KTQvpjRNrK(vkJqbiGp*j=36hmj`wpgD_SMAEN9D?_4tQfw|%}?A||hg zXRhbZ%&)+g@R{^I?|t;Ucv{^{`C(KL`2q*YeYRGwrKuuR z(RN}xQ{^iQ#mnKD}8t`urDn@0WX4hgBchJ)!Tj-gnxKV&~^!X9_2H zzR#qmakV=2da+6FexvhcKhD7m&2rZ-eUSd-GyVOY^b`*JjCDSsy!PK;+pUHQKPwi~ zP5ka({>S;l^5kI-uXCtZ59>6uwHx1&EiS;k!&mHnKPYEcFVk|c2l?g)>HZWWk9#ij z`6Jk~noqyutk`ugjA$cUy8nK=NW&d=%g6Y4Gb;B#{3eVs-ct4j!=wYNXQ3a4kFps? zy;Dq6e=X+U_@m`=|E+kE_Tb_`;xX;iledQx$CuBCxy_Y7JLfCe3%y!7j+y}f8;^nZ z2M*5Adnk7i8>iYgHQhVhthak{`m_ubh!E&lH1ckNVN@15Z$+V$!_;c|=Fz8*Z> z;!5@_-q;;~_71wF+!TFQRH;RET4mqypyp@kTd+Vd- zaKePMp}CXJW4M4>C_6&!UDHQ(e&CWYijmIDSavyj(Dj#3Jy~xaC;HNUdeYgcylu7m zW+y)-=N|TjcXsur@ZR?0J0FzK?e~0e<+l2F*nyaYyFcu8mK$VilQ4@o3UI5b-p^{e zs@XQ@lBW3k-~3ZJRz4_R04|@J*8a#P_8m9&YP%hA-mbq^Pbd3Z56?5px0A1;$MofY z`oB6KdKcyV?G7=wv|VmCU_9B>Q9ax2bPB-~Yc- zuagGQe!OD44QX0!lv^$JS#i--(XH{x_Vkqm|rp@z0mo;{p0%kZ^oDG zz7E@?33Fc$4}p_R^c*IdDW^R$*ZhS!U%HJ8;b->anD^NXXJ78RF4ynAdLtZT>zQJL zS*FfvF+^R)uhI9g_lJ+v12Oac%m?3=7sVea&dq1v^g-_yOVy_K4Z#+#^|{{tboPmp z5C4PTF7+<)ht2g(mZ!;w%rfaO?N+ZZciroqL3{@qGHPeD`a8vu`)E}_p@(cw(Ak!u65>cWN4Jao#x>~-KQF#KR3_xWEhQF7)NaK zUvrzGHq1`_3|@yndP~mUOy|xFm|0Ng6;?LrP>0*I1zIi_(+JXc@$A&Bc3n+Wlh4DZ zis?IiMcC5GJN0jS-#A%^+0=_aPdB7zy<0!(R`|zWJ!W}~Ic&M8SWjnk(9rA5uY{4) z#2c$l!b_n|fYWT&7+xl48jT+#4;}AJjdlLL>(kEIig+(R^xW(tdNS_8wfq}?ik|x1 zt#r7$t{ZQBJ>Wg|Q;NZ-owbRD{qyx|&BJEZUi;+N%@Bpt_l{ru`R~jBK1*-prJy3I}7M zds-d1UVUHjLw-in^6GB8EwgKLmPHxuc{XvM-}m?H#X@yA4J&+u+xaZG#N9Xs=5>zh!9J|Fbhke=f8cG-hrSJc zP&=NVi{o}PJr@|UeeD^Q^ z-}$RQYj4Tte=5htnaMPp7=nz9M-hT{gT;Jfpd4 zfY-nIhkOOU9CxC;8IGmLd|WP}U;XrFzb_~Itok;*u+OFOMO#{ zga&Q``|mUDB$Ast2kYg4`qk>*)vj6Ij@N4M#kH>8+@o3CVShrly_#*ogzR9kCtBW% z`waWU_ucHhc&2#IuuZt=X7%0m>|~`_!I$E#ZPhPujdbAULi8wL!Z0)QJA;l#zQJ>W zUBS%E3E7teui1U2xR<@Glpn$!%)HMR_h}f(@#tPV`?!hDi#&x!v^~^#GW1A>Iz%ie z{!E7noAdLtTlsceJNjbwtl0N$2b1|=vD@BP`XF=#XtA2XobI3Y&K=~-apw2R32wfV z?rB)f={vuAr0bFYxcy->to7K`TdUd0Ryn0TQxkFU2QFjJ`f@eG)nevsc_yClROiUt z4^2^A#-}=yY9`le_orF;m2zWo#Xd^=$jo4j4H!W&CI8A!oCSP?>tRCtfY`NPJbyZU zfR}&yhp?jh3Y{_j{4|W%jO|%5aijcvER21wXLUFKeenPWUe3Z#^&MB!sheuY-*LE> z*-DyfYV66l9)m7o@9SZlF`9UCgwtv;TD)iN<$3YrFVA27N&TkcGG2_@P2I`I+SM^p zehA|;pNB&P(+~$v(*fBi=Q*VXUtYghFH+5eLkA}{yGPRn7wQO}(X-nN7c={XZ%6~t zEZ{+Yaa}*DTpmAgwx63We&AP|L&7b$_wLra={U%D*;LQ|$v6uC6LZ<tg0oalK1}`*Crdq*kLY^0vTy&sQx0Wba=@y37Up1vePFQJ&eO0v zuN-FRe|N3U`cvD_7r&besSkD`yU;6;@6BX8dR_WW>-CJ6o8y>?!({e)5<-zbh2199)CixHD)$UBpReoyZaPur}Ky${zqf4JvY^B-ykGe%Es zrZb!U>2J`xxe!+MczNwa*W~YE!t@?thO|}ZvcZXRY8a$mskks!FZ|+Wyxnl>C${cZ z=RKOe@Qz@^`#m!_)K>m^{ej%Eeo(QTP6P}b-bBL%4sTZX@twQHg|r*W)lchN&4$&P zTYhZm`Y@~O{W*JhEi`3b`0`KLe|bvpWvkwVUeaQ{T)W))Yxp&vM+a=D9?N{+c~DKP ze`HR=4E9rDilh5+4zr_sAOEphtKFB~H$Lc8{+E`)PCjI8Gkt(^5!jsgs`sF8We(MD zoVju;@6_DHc-ZH#gEjlz=-j^W%RdkN5;e*~?*%6bZ+1C5Q7cb`!Hsvl2hG2%WaDNQ z>FTef<+5tFEKKQQ{=z(%{ZWnUGL24HHc~OtZO|}x?*(WY1+c6 z&AjO+=%?V(*@r|vE_UAZAn6)TRJ+;psy?Pa zeQDRurt-a>$3!t@;I{RS=#s)=2Q7l08(nKXcDp*{rkmv@&lDfj3D?VS&90v0+pfpS zvXkI69OtBdj~ahHJcW(kj{7v$`8DqY6LrtSXO*8{>6zOPGuw03W7#Z5(nM3&j(1)! z_HHf~yT5H^ut#~+}7i3=d+4e?XnFWz+y@UP9+ z?G!%`;{B)(j?2N!g35hv<+ta=jXp<>KGQk4oNst?qrQA~Ada2=7Vh=d`|T6T59@pC zJK9TqmJM$7-`n*N>`t&__oC@4N8hLh6 z;riZ)-vDJ+%-teA5gp8#K+ zM&^9i&cDF|V88g6cs19;+h`cm8^?dbO~3)5)p6w@jpEL(+HI{`==KNIocYC_Z1j5g zhyAa&c*VIgZEC zrA@DIVUL=bcymbn^?rWoroMVMbD=YLDSr9mo44a8s%eTb#l4HgFguK2`Q)qcxHqfY zi_g8wd38tcWIG&up=V(>(f$!LWxM6iX6MI>DP|M(2x%X*FUoFwh%$=qOq z!7z)__2}p9gfH)w7s<=)K*ZZ#ERLBsRDZw&%puYXg&BK>G!8eaQwQ!a8*N{Uom?~> z)txZyhxy#S`UCdynAbOdGH{~$T%25ew$;v~J$l=ntBL*mRdL*|)9G* zC(2dKJ&a_xazQbKb{Vb?U5wr8TYMh&h=WQW93Pz~qCEO6-!kNw;%47LD-yN^Q(CDu zf-~67pe};#U+Eez_Z@7ImX{sy=3Hq_;Olv27uy}D7daZ%bE)g%gE!L(U3pN9?DN=@ z*mXN2m{~EX6y-}5phx+_B_X{&o+BkZ-gUn7JD9Bzt;@e>+yg8 zIGjm4yEfAOnbMAs>X zj^q7xFZeii2krNq%-A`rci$`S_T8}8%@_5W>vt7DCe1LEPYif=c7Q`qf5z^hjl$Q%RPe!Juj@acuVE`YX7|AI3$j|4@v?RlyUc4RNEGd_BKJZ*0wHbZ+c4p<#DY z4@e))4)XnC+*bLT+FJ3tSGwLS<(+tr3*~C^N4qQ73*N$JwtFMHor@nVccw3} zknPctvL6Zubv^%PUg${8eth@*Z?A3+^*;<*UPc4PUSWB#9yPx{?1?MS(pNIiXh-7x zIO)sPu>*G{d$!Yc#rcfCxY+*iH^O;%qE z`^z5l+x^_zzxwkKTk*k{@)>W5B|{zDTvq31$T@m;L+#xC&*c;G3Y;^~_2TZmp~kW+ zh~JpVZ{aBOq41(Txlwk!`)qkhadN9`(T}8eFxJms!Qp&9t?puloSBasc)tINXQ?Of zrdG0LT8Hyt4dxsbi9S+!3@!C$*p`OY{%s0|~$1|Xxpx)W*yqGDn-vfrU6UXHs&c{MI9`2QScQqGn z99+hM)7JN##cP@h`yBL2i;er?*C(oe4c?I@q{B})MQ?N>Jke~H zJ(U-GZ<2=^H4 z-7m%8G1Ix8KeESDjHly)i=k%(%fSnp>`WZK^{cem%~IoNXRBdXI4?Ud`i_U^AN=W` z&p&KG++R;@)W3SA=b)FFy%ziQJJrQ<@T<+mk9MZ$Rnxbk&E-4TCM`qHk1okUJb;CA z?8%dSQTgcD;ln|fceLKrSQz~De(|zgULP7}P9ueXpEZ9{4`!zxgSyX7l=b}mN&N}f z{k3p++&~z%9{;`c@n|-}iSScTt8<5WjQlrUg+2F}U&o)~3-Q2~`+SU@7Z#*nM zW|MSY%^sU|n+@ZmJ-ylUr7>n--+s?*vz!%goL;s3eXaa}7Ogso9Xba;+op;*lLP>Vu2)M0=j}3GB2!D0b0Hp$~a4Tb~X? zfX_dv7uj>7^Sjfzpz%JY4_}PJp`tZ0?DZ}Oqjx1wAMbYuP5$~xw0U5W;wnG-%rAc5 z97-I$FxZnHSHo8)IseD~+_N9n+pF&6>ki@o(byTZjH^BNi*a{8`ffOTIOOmq{ctma zyM69+&gz}w6)pr`k$rK;Z+|iHG!E-&ZHOQ7(2iei&OK}k zho8?GsrJX;J}SQ9WQ_C-aT_kh8^b%YJ40{T&%!zIj9-rnX&#gw$Oqs3rQPn|l*cY3mB>2C*IXg4fmue1B= zum4ic(G0Nti@gW)3wB7&I|qIK^0W0ystuOH*SDJUTk9F0hGpyR$Rn=Qvs&zo;A&5o zuhVXQV(VV{rarqli{?P;)x!JmD~`h?%<}PN8(r6Q_dV?Ete>OLGgBU-FT&5N2gdu3 z@qGDC_j~xx7ujAjG{wKI@<6^;eqjEahQ(Muajef3ch-u1Cvgbua)7JObWZ8TY=m>& zt?z~Fxl{gk5GT+1-s_#w&DiW*4ZC`}7MPrvu-a#@^qDlU4~nHX;*iiPSEsmkIi-Jx zTj7J4C9*4LrgJjvjT9UB+pg_u_13`a)MqM|(N~u1Z}mKB)zPRC<5oH&bd%XDEa?7E zzYhZngAZr9|I=^ErT>)Oo7?|Iu`*6rzI*$nFn#~s88|41TMuU$bgZ(G>3sSygWofP zQ_&S3D_^@Db}>E!0e|=1uZQbV^Uz~k>Dimv#4Wv$ z51;56<6~`h9?a1$=6lUI*}b<~{J0&*MvwjBPvYv7Cz$8kikpLLY)8(ZBTs8L`~!a% z*JQqHG@ES3>XE*5IYZwwTAgVIM2vVM{ONM_#ijZLKU%-t8LXa>>%ylxqXUPsyvm*& z^S92m9iAhde;S9^iqo(wz0RSQ&5yGmoI3R(`omW*Tn(~Qt+O4ru^sM>-{K6iH@rw% z4*Vnz#9Gh$+2;TBaq&O!y7u4bycQ?apIgNMcD+~Ksz$gP|82G;a=23;gRBiobXY-Wdim0F*E)5GEx>~cH)@53}a z9>izeEgpUR5C8T2<)44k^C^exd-Rd*YY~eF4e8D$OvJlBt$xt+q2(@y(N`MJcdS(> zZWcfJ>5XU8C$3h<`8x_emp+gV(O1_(p z!%6Sp^zCoD?(eF-%cFYFcR%_=*h6zL-6LN0c7BGw+{NP7pf^x|#*CyLog?udN6Hh& z`u}vruXHbu?bs7{XV^D7o?jR**QFVM5m!rX!arm?cJt8V9_^VwS-x=c-ZydXzRUjd z3-!95N?(ai_Q1>0|Efk=>AkNtV>@5oztI0%Ek_yIHHX+|)b~*T&UU^onnOr`kIv3k z@$lZeX)boouUAJ8`9iZTx8gU_OgSotz|9wn#4NLj<`c{>E@w+?*Z|p=yvM+ zUR^v0^J&*x{i5A|4i}i-iJD(8U+(^BPDXuE4cL1ce7$#(4dS=#>c50t`8k*p-(+{h zTzyY}M+1uH0F9#4_p3G1>@pLNYdckZgjtC-{4oD&M$Ens+@a}keR|aJ)X{pU7dP7N zntu>W9ya&z!q5NMyZ`L`*Z<|e)g${=Jrn$m=kmc}YsHFulo;JP&KYP7-_KWpuFT^vO>pSg8ytr|5z@+Tr(94$dvjrL!a$mWS9_UkH zSU>D%pW1$K{x{2K?IXMwHWv2YcV4R=Tzi-==v}Yp7w9_CXs{mk|H6y=)YwyV1K-htQj#Lj)&(vR?bp^Dj>st(U*h{VrxFbeb<`ON*V^ zqiT$mFr}Sh>sED<{n&9bXK!^!X{eh_zZrZ`5g?a_=u z*O@=ZqcZ0(pVq_KkH70VzFi!zmlOtphX-S)OSPHb({G$Brya4Uvs`hnSaG%MeX<h*X7A>7<~t`N#KqnTpRY`${5`|}QGdXD-5lU?7F{X9P;_ux03tzidfSOgsS_6O~k`=oty&15|n zkLRQ_KuhExEK#pY%o+Bz6hkJmTR9rdWf~DT!Vv7^anJnra%cD;{9XS*e`l_^toMXR z#^&&O$gM7 z?sgil=HToMHe)hTTpRDaUV&+42NQ81U}&&PbDOw{@Sv;ZpR_~=&TwZ97OszLR$)U8 zRUFWd+>%#^Y1kW2N1V3Pt6%=Pdg$}?vc4Risa(Rm+eSU~?Q$E>44;@51>2bEEMCfQ z(}^7E?CBF-+AOw}lY1{0iuF&d#JLK0py7QnJi^XKm^tqD)#i}Q8{%=`3*f}qiK~{L ztRM8Z%l4V|ec3s#XbXxv_JmMU)uFkkFIw`b9}usjDtrv%WhJ+r+Fb*_e|#me^Or(#&V^c+I;;; z@4a3bwAJuMyTlut;{EMB-c0|#BmTTXvf8zXY3>q8y;pJpvb2*4P$d#Ve zL^gI*t+bT?87Zck!QSru>P_O7sTt%Nw|@Fnxk9-_-$_FZH;|_FmFlA*)|DGC=VzY} zYn~{7g9Y0Ueg)UKXF<xNnH&i3K!#mBx6Rx{Nzovub4w6Mx?>08|HT^?1_ z>2a7_Gh+|yqLq)s`Fu4uP7)mF>GIw?)l}w7^}cC)*zauKW3}tq&OgC5&34aJr|3KF zb!K32V&;#$*L>ul9g@#f7tM$3i1WBAbk=c!JzqLa*Nb1b-~T#{w>{|b!_2=An*4D~ z>_x(>q9gZYF@YYDzK45(UHkX3e8p%SNj_({^ITC{xh6mTqOvK##>vjSI|YcSVDdx%C7{M`(bn2+Bw>>}t{J^PEV z&tLxaAI_go=W)CI3jRnBo_3a=4V_$CpUc@h%s{QkXU|ugF6S%kr-irU-kiPH&aJ-d zw7lbb&)%+*!~T1zd<_?9HCq}h{_1-i^lk^vmfm;x)_(mRzSEAziLR5Lr86R@r^8@h zt}}5EM`*v_r+tSbD2K6^gpWxxkqn0}RBkeo65Kn1RjbC0z z_wXP*@*vG`coV%97@wR&ZFDD$<(ZGZ?RV=zhJD=1|K0rXtMivW`}@vXvmfCj&wTvb za_>(Ed`NG2t9QBgW<2WdNq&zvFq=OS$7oorm!q5O#~;Px!NJ(cPRzI1>xxtIYO}yE zf0Vv{IM-VKb*fwj-^3gjY~HndFSI)Brlog-ucVhl14ay>twmQ`FIbOVUsk_eZ)mFe z<6^&W27^w=O5Y26I_oT&9lBh8Z}#F+=Z78J_p9aB$|dx&@n!Uo)pnQQ0Oj1YM~4}uY=e&D!u{&+aQ4vy zJmPSvdP3LPNEi@Y!C9V*+kEGzUk`h=XzZyu_IrlI4vK21LF>5~s9&Ytpf@((yO$5r z>$w`vbGf<=b|-(=^S*LWuHX4J;~im(aV!Va1)>sTbfL=t#{JGZ*OoXA@86C-B-YcHaNp+;QTjsw^q)`l z{qr$D{`MaSF63^uemflGsQ35&AOCrn4UiYoky$D3Eye9)cQCGv-VNOgT-1^9B0c8G z@(38~TIXb?pTj9U>pS$C?O!z409!SqZU3V@L!W)Q-m4i`{i}oOC;hKM*F;~d=YFa8 zMf(#Ui(iq$sC(7FLmk@rz@Hj{GnYeN&gWh(2Ec|!vYF9pBJp#y|MwpkZ`30R`zlX< ztbgk7`Cc08{JFl`rMQRiu8VQC>?vkDxP`;6j-H{MdL~~o)gSoIV$Viyre56bY}>!Q zSPl+X_WXuAHY^A)h7T}zKm*!*fO|1hk86i(Ht2Hlznyhj??Y`@?xz=IpUr0VJl%vh zKmGRnxp&RvW+P!wce63R?CuBkL*BPT=8K_kPT%t&8`3M^6`!(mGt749+zU6}tB%!U zr6Ydhz4Q=zw>Ljb@3p-4nV*I6ykIis+_y)7IyU@ANXFuyK*(gzfpe(XAyRABQDT>du*BOG@o9Ca<5vM=xW?dGg{hIVYo#c9t@_Y7A0_u2gD zYWGEV!o08NNQ32?TqJvfc|N(8)@;uRH)f>&z9t{e#__c6Mw_n&IcWbaKXJ8wiFvQp zaEQJB_}tlipBYNKq8EzK<_VUY^MMI(JVzrV-VK|~&c%$iFb4bDF2+e43xl2O=gmga zTf{>f&xZ9WXPf^rbLIWfj2r0;&@r7VPVu?xeb;8+t1oCq&peq}eyitv`cB$y;kDPp z){crV_C45Tfumt}75$t?=bSHy2Os`4>>aC@@x!&Vp}xFO4*7U|n+w^MJ#v@2Hak|- zievR>MzW)8;Zn2lK<2t%_0F|?wY|q9{X4DfOU1o(ph})^}bG07%#nX5C z-r{mOp}J(y_v&ox%iw(6%^#kH`Q0vGr;mPC?*4py75gFZa8B~U@Z!06fuH@uf3HW} z{)I4%)9mi7nbEuNRJ-(!Xi%}!sp=CuOqa_w=87k5M-Pw=jeL;?wt4|_zWA+dR{gUrU+KFx%6o@--Qw$wcf;F@6L>B6f7*Vn@bHD~6}Q*!cjsa=EuL3? z^?g3&5ibreYyY)R2F>0Z-6IZ?`gyb8g^%%v=7ZNdYcLq|2J&w{Mh!4hPC9w=bkC^V zu0F|5c6HV}zukMbd)5q<-Y9?K91ig?Twi~VeZ%v1^JiP}e#KDdULC9_LTeMh8@7la zsQ2+yJqkY8oE-hDCt+39nCi$2d-Msj)38ihqiPNBR4!nK<6`!_**S)1;aH4zed-Ik z|7I=l5Y@42ar}ze;^kDh0Y7h^*{m5n4Q_yQPBYX@87z9FYq2+Atj~ju-gxKho(1i@ z@~`k88eHat`5AdLJrX^V@oH$?c=7PK9_mze>D8{?&){S6A^4&6G4zq_juiuN;MJ(+ zhF9ed**_f{x@v0eo$6CD;W+HyUa{-dFsJdw;1A{t9)9}$Q0wmXIk4@Me#dMx?tnen z;=_fN)1C`V>Jhh9&u7RXdv5dL>RtQ5w&R;=IbK4}21^epE4PVs`KGCz5<`p7PNeFhp}YsFSJKUz+))a)y4 z2mbWn!)EKVZTYwuF`i~P?(0EV%vSl`Bdot3e!Cs@a_95O&XT-BZs%FpGe#@Wjsdz9 z_yhKj*`Wm6f;)`v_O%x%q^h za9;bDx64oW%SGr=Jd?)9jj)nC@28jeiP?~^;*Wnh@c+dT{c{`xeLdWvOXaf<`;48Q z%M%Mn=a-gmh82ZZY=jBrv#*3HJ~4lE{%^1Dod00qMzw77tJy7F<8nCGmEsz`wZ(dP zb{-CUd9vNho?GAWqmpzT!r;pgpN(K=g$)>O8p z2kYIN=~mO=e#pnw1?JADs_EnmGo5oa*KE(&K8@x3&7|iq)Gt%z6*HYv8WYYr{wMC4 z`2ZSwvnP5rdpYgqq>KlDMJ;tz*dL=%KZp&hJ#Q*E-s>$sigWJ=9Q?Hl>wpV>R z>;->M?N6`!LKy2vxeCoT_}F1*ZJ~H?o<{GBAI2xVbXdHAWfnhS0b~6RtW2NI{tGd8 zyt+!geWkic-{)`Zmxwh7ov*zx!j1eSdw_G`#nV;6*^+>E8Ko^9Wapsdf;ayqw=tKUXV@)sL;*tH=24FoUy~ZQ*7; z_~^UviGId8>$=q(k7fXR4*6TUa94Y$aOLBAGtd3xx8SEe`wC`{QPvY*;B{HA5S?zrLgWrTb{__0xN8eY| zHb>U`5^cLV10%!l991N_CHEp_h$wMl)WfhVSKP5=IvalgMG zW?=9Fp8x1qovnwR?|gJ;$By|QtljP%;emul)%T#Oajmm98a9dNuLo-;)iuMh#=_jp zACC+@5WSB0{~PzZznl5OdZNXOALaM$VYJU2?(tOq{ZjZK%z>YM`Lo~W4|_iKg0KB6 zp9fcOr0-X2Z&&jSIz{>AxoqV^zkjo{I;R(!{Y-_+oxEDD*n7hp!6RJi8uvTH_KKRT zpU?L3%keX4ozqh`!?qZHu&FxK;ftuHwtnO2mh@ zD@qN&SREm6rZYp^2Jgg9L^v)DZu+dSS2ZndN%as+Uhm|f^KWku{S!H(v&zQxo!Q7t zwcb>D+g_UV_@sE|huH*tc{Upv;zaeXd8_5H96g)GzFUtJmuw^6(U2DvU)3A}2L;fbyIP<_XZxG;8u*nzJ1vR&Q^tEMG9QBAD2QS;b?s^3JrUR~$< z_}7{47f$W$iXq~)-8*)c(iWL1Ke)nX^Ur#sdK*{ECESmArB*cOYcB`A`HP45&i{J( z^!!5qZr04tO^a*2M_if1Vg-F>`ptA{?J|4*({I{c@^$a^AL1Ovu_*^Stq1?yCx7f6 zd8hFP%PV~jKRE1)?mEYd!*=G{wIcSK#leqx5FTd!l|Hy0@?%R!=YKu5cm7uk$Mye? z&;Mrjc)$n6eEJ!q;UsE|DK%7B`&4y5OyjAZw;sP)dp%R!e;B~a)j_wio73`_op3;X z$iw2_i@*H4!Nv#vS2@Q>XIcKPw!QUEb8T_HZok|9kDkFs_I{(Yifgw2M*PC&UhwQ^ zdN*n^J>Bj0f8k!Ohh4#-=mDwIj^F;O|IU9`|4a>WLu|;8*QYT{aH+ke{D}V0X0dy{ zJe}R_bdLA(b@DTH<+l7jY;T7zcb0mN_7vi!(*V))voA;fZP%pPXx#Dn-lh4-rEFxe zm_tkQAbiCxx~VvzH0i||+`5ha>~zie6f0pE>*8-(h%i@snfO22M6=nCngXX1Z) z=%TJy>pcI9@0){ocs?D*Ew6BP+39@m!hO@*rxRrE4?hQgY&OhgHr?dS7eDV^nIY)! z#S?jS&sq&YZ(uy%&u=gF?_+p(`K=52B4=8ibA)f#yLv6%4O|~uq6@{Gi`#e3|F&y< zZ1v87m75QIB8>fdwd;B{C63}oG2qpY<4}e<*rmE#?sJyi@AuqpR3lt#&SSbfgWa5l zVGlj2&WXJJ#b5ogcb2cuU(dw5+9}7thj)&bvjsIA{=-oi#mS)C^?mqNHAD7>6X(3q zOn`6bl^)e+IV>Ng(SV=1S=_L9c%k=4M{~3HW)IMMzYE86#_`1DV}3_Xqz^=+)@;#i zyfiZ$w14MLo(Ut7gN3j3nKRk&z`gEy$!}KWTYVqxWqVZY(=^92UT@9J2JVj-w^WRQ zuc(to`iv*Tq;UOkhHxg}Qs&Sn!g)rTrM#S9dMrPuz8rQU_usUp^m?BR`+Tw(cPTte zt)ZW=5*N_tm=V^?(^H2%*&SjAgBI#?&qg2Lo??Dh-C`C>Pjx=*P3?fIaw|OcUh^Gr zaI+RP5bgT3r){ztLF_gsFkWwdwX+B3n(cjE$?hgP+v2@gMmKvS-wKz*m(y3Ei2*M+ z+cw3P`aAw%D!ZWj2UD}xPY!ZaPD|Hdujl{p)8E$rq~G>cwo*RVHSU(*?LJ-4Ka7wz zBwu(fOzpxJ-IwR`HPyiR6SJo{2kQKPJHFX7tA^~lSL!X!w})FC0;pLzIF8fN?v6yhNcW|Nan(n*tPGRVK<=qcI z{J!f?mpq@l8+L;eW+(kk{p0)zzoEDKe7yv7^fUQkJ&N&aN_HX-p&5$fu~>iDzS!e; zz8K=_TK;7rUw2T=v)S`m*AMEM+^S!+5#C3KTn)DWTDp?uV6)lzfOq9n&Eo3y(drrW zaDLLW83t+=$8u&y*YXEW-kvdGj{2`9~Z9 zGi9Us6Y~P{*vWbxBe3#(5lnfudh{$CreQeSnYmD&@oy%sS6|>F+f8OyUh_>oziZ(T zkFVc~`+wv7Z`YcKmcxd%n6dfsum8E)A)moNXXC4JnAK-y;ugz^XvgkVAL@%g*=NYZ zSIdvD=Y!U}&e_iGTt0fM++08X;&sB*1q#YeRftb^|LdbOv$)=z)@Re0HV z0}p2<8#2$j-t*IY+ba)S%_i_a@M|{f!Ra{;c)VO8KfTuVUe8vx^7X650UTiaSvSJJ zcEi|kuEevKfARY??(3_=EbDiy=9BNl857sd9El$^Cg;mb%(c+%!TmC`J{dny@11tN zJt0?$3wGMj!=!;}j!k?pqj8~ghd(bb*6&#Az1snUn@wX7FHjw>r>^&CR`jWA%qP~v zwWV_*OlyFuacex ze`2O+vge4SDyH5kHxh@{j(8e_&Ruz!*)@Jm>>bP3+Xq2+XF8n~^GMF#WVwYtIo#4- z5-|c7_OhC)x(=l_1@uezxz{d!e;@^`xGAeG0wRcRx#4sP{w9h$hWp zbM|_6W?ts9XL*6$!_Mx`g}U56SG$pD`K@;i`ijm2?e}{hepMau>vGe2 zKjoP;vGLdOgXqDlD`=Fgmxr#D7dqo`zoqKYh3?f{z(G0U-ViV9!NzgGzgX$Ht@Zxs zq^V=*AL@VVXYyI*xb$w=1|B$$DE&)2+U#ea$c`uU2eJitko!_!%UkJRjMM{Ue-C@M zcX2{{26mflb$@5&vnTP^Z-*t`iSGf|P>(!6Xfedy?X&ou_593>y`KYI(fs#rylPk` z?M2U1|B3d+-V5bU;a9Ye9_`X8mn|lEhLiF0uZ30N$Ihm~Nh|xYHS=UR`us@q0R7#_ zZgq?^x^I4@8Rm2{aQ^Y9AEv!)cIoQ} z>G(ExKK!D+Dc^=W#Cgwt)ZDlqx5K^f)+48zdJslTYw@(c+*vch$8S~#7ys2@*Ur+a z&9B&JwvwMxW9pUYXV51zvkGG${=Iu(=lbUIUbb%E!hSuU8`Vv6l&$JnJ44`ko8fv# zvh6%syp(LunK-|z(zZV6U}%|=L`RTn(nK~v-8T%{3o1?iI|Wy z3XRla)1iD;nyU2Pdsp6}fPx|6LHH&>2Lgm6K-=BwZb{RjxtW;Hkn{XbNf#LfRAv72 zoY!o#_S$Q&6sKJZFT{77>s;f!4K<5wB<>^aYh1&jzIB+UY<7pXo9De8Pl}ebenq}))Z^Yz2T z>Bt_cTbv6Y)9*pw@@jX{ybpH=H=MQ~J-Nm1letx9$TA zi)a5XK5uKi{4oF0pUyc%g<{K%-eLc>@VU;}SoOEbo`v2ddrhxl=zD9g zgHnG zArC57c@S3+CsllQwOEi2_g4FGw>*W`hI+}JYFuW1-{EDl4dZw$7 zWfPsX%i-2+w#PPRAvy(W&FE@{MGkvJt;)m1xg4wQmmiR#V=b%s0 zy>ecx|7y1E&WC0@emZcZ^((3)sPB$v^Ke4h8}Tl^knQfYJ{oumUqstZtfvM$aBQ>?X z8|hglPQpg>1$xcY3TTnecAnwyo=Xk^WBS4JtzzK%R@cK^+C%T|7YLDYJ+xGIS*`%#+u$ibw|7m znnP+Bc)*w3iJn~!j^j1fSu(HGoJbnbFgr1X*~5$RQ}DKYo?b58lxv-P{4biWSF@`y zT=P4;2Tc8YGpFT?@i^Z5`PsjpUC+pB~%5eaQ!zCv4W*QgI!Q(v9x_ ztrtFIT8u1F-K3W~{LZ3%Zc_G|rsM8fMPWA4OvIA?`W<3Xw z>z&a)hQc-@08qbNDKJp2lHzzW-mT=W2m( zZ!P!2g_iTlY|mQ$Yb!gk)#sU6wbUA&W)ID>!%bH&eEX}vKYQzoznwOKjtXkJ}d#Sd6z4yM9y`mFBe{d-qd#O8pDO)*P z?5Vczj(C=t&IetGjXv`zdvAu>eEYtaKe^pA9k;jp`Ph3Oe$)FmM=!3`tDT9hYR_;+ zeQ0VubOzVL2Y2I6yz%9~)KB=!coLuI^U4qNJ$kM64$*p;xD$ra-CZtETJ8Dphs{^N zkZ+ssPa66#v4!Wdv&G5kJSX9&SBjTLd*9VG%isO$|JU!;-&NmHXHE?N(x=~MBZ_Ie zAM||af6>ap#e|2^V4S$o9m@W}A+FSOM#C58!S!+c?}cYGkR@_7E2 zw%XOOqVdDD`sJ3L&y9S-+i_~=(y^X>w)2ur5I?cIr=3rDgm~BNo7K)Iy#l!%TR~4( z@A+1~=9YPE>3Xl!%RJZnPqv?Hxa8TJ3BPyaK{ zC-H5{U*zITkIHAd7vfG@L;8ZQhHudnQQK0lx>DY&zhk_(_fl(kd9xZwxQY5CzPlXb zVtgQ657@|H*W)(Ow;H~$HM~?D!1k(J`ONd)0^P}cB^DF$vSb5K&q1E>e zv&lLS!+f`Xh8D_5ckxPg-tST;r^86Uh90y!Hm)wt0ek}2k$t0ws%O-n`cSr7>*@9$ zeoA`|$KPFB%J+|#mt6^G``eYnL4)J4`)g*YxJ-OJ)PH)v0e^09Y2MS~GIwB@2`F|K zL+8KfCYwKa(%mvscC+)q?uu~+EF{bw2Z+uOO&4`f7%iQrnPNoiyw=%g_u!?pA}*Ad z!RXb4?vEk`fRvvyqJSxLGg__ zlj4l+m-?M?QI5mX2Odc=HvaosK44W&kd4$UtCk59F#{SW_z;#~+;sD;IEv+2ci#Cr zTSs@XewX|pTo#suC%x7>KmO?VXU~7|&A=IdoQB9R{`@c5fMWj6Cf@b!^tW)_)FKC+ z!uFCMT5oNQP(SbYz=hOJFNJHFO9DHlJvrNc!xmT5;g>VrO2cfu`$Z!d z-`M=P?fe@*wUcJSR`0~7$;FTJLwICztgY zbMegUnCr}{8Hp9$Ioy(K@hQjZ$rFcbpG$Z5DM_-k%($_Cut1dj$9lcMn4^AgNUOnG1lilWJ zndzhkJj}Z&S9*{i!dKoXo_YJX|1g{t8clS1Hi}W;mDAzXX0=@FoWjnR^JloDd)C+S*5-1xO>=T+E3K4^z4>Xp_|{x+0)2#weT{WzXnbDeH|~dRo#L$5d(>Qn^9OMA zdTffF>2S=11DJ_~-^ISch^^Cf{7n7~e?vc|y4s+-+4~N7XZC@v(|8#Fc(#3}y}TP9 z_%J+KZaLjO#lOc-#`zgNj!zo)JIoQRUjMMZ?i;DB&HzCdr2ywLm_dQ}rA@gdp+bzr_@;Bxi7_`v4Ii1YOy zj+K|euV7XDjNj+X)ji@czhg=p?-9I{B-8%0M3MI&QyaKbiLYdwNQ8R&iHWocW${v;8=JtJtyowO>vBC%=jVS#5HNOFL&9-KD9{!$x**wpfI|vfc># zPAg&E2jwkhl`m$mXll$?-^-eQ- zr1;%T?%DQxzw@=4zMeU0?2_1pw(myqu)4W3qaLHb_{lG-A@rO#-YfQOw)BH=I5R+L zLebp1SsaCTW4?|&OD_Xl7G}I$4sy5J@m%|Do{Rp_wQzm7D=zV2cIx5B-)2j~`Fh9s z?(TGF`>5Y@@6Gxt?`Oxt1L6E|oEJI8(>JQ7FiB)m;snzqeG{^~~lYB#$**I973 zm$K#hABMQN_;$U!ffu$h_~7!yu$_(U`BoewGvQX#oLVj>H=p~_2j8~WY*%v(s(Zq< z#d2_9np1Mc9hgLXsJ;Bdqw>p@u#2T^^GbVZmgxPD(%@@t4#J|$oz%07E6sjPwubt7 z%&64oviwNwNjs#R=XKa-T!QR0Y(Z`|XvT+E=*5wz;TCUXZ;s24XmpEnSBv+j!dG!p z_5a??4)Fym`9)fy;{W-4u>Kl&$5!#EI7SWVT4&^_{Ox|+$F1xyzxUeLe_yU#FI#bo zK6e_(EBW3V`5k?*k3ac+|Ngrn=E8Hj)Oozp{li_+ujM?y^s7IfefZUXY39Hmy4P`Z z@|`n12S2`0?12A!t6c6zb(NLQ&}#l)9A&n#d8D&pBxCu%iMY;FJ@cKnf0wNa(`nE3 zz23}*PgggaelG4xYc*HwD0bM11HInQj#t~jHT~Z&E*A&h?fKG9uO@$`&ps-Cdp;Z# zKV&=`xY5~|36q=%k67#Xt0kBvsjp2B*i3ys3(W~%EB2rXX7(kXB8?UO4m3P2w~oWS zit+?~3zO{`?D9r9F-&2Ice)R9V_a+X)=SyhOFQufsxSTI(?6cQ{K+5l&rjzi)1Dh@ z9^rBW7q>N_KaN|m*yqt2e-Qs`t9vq){hSROTFQQ(7FY5Ia8x|V-E0ZI9zK>cjnm8b zncX0UJ&22P+V5QJObs(=I{PE}wUP1$cMVo|*jc{^@9wIis$gHP+3ypI@mwC7r@Ca_qq z8g2Y5yWzvt0r#tSy`zJa#*ZI8GYv4IluaJ8ly;2W)_``nZ z{JEc(_g9;J?Thci!irz-zg%7I%b_lbzj?bl8q6Pmo3_znzRaD!7_Rr@wVTyH<0rJv zv>cCL#jz}2%9i}}%fD9BtacAi_~cL3OmGY9cZv_DjyIQmJ}LHAYg{W2U&^-AU)m_= z!ILs~cCx!7xBkydH}d^Id5RO2d+hYwr|(p^$?tvgr~lr}nDWznjd_ntY*RfV?(w!b zxIG--d$u0B&R2eGu^!?1&K?|(W|v;N(|7fX<3!~T${A==&YNxBc{DG3t21xbuH0_= zR%cnBDQ_KYNV!y)#%TL3r^oqlzx^Cu*kS&Oe&k~OV>KNBJP^4q6#T#EJX z^mU()E$5(n#24cc-OIl2m&fenKkxzMb$Zd^()xgIzyD=EBYPc=kKf~-nnz*2hCb$v zu(AF6PjMLzvlTS!Z~^eO*cdb4?}st26m#mAn(h6aW$}?dJUV=|NC#h8KA|V+sZMD4 zYBB3v=WH?`I`ApWMfUSu2mL;?$mjqagl*t{s@tmzKKjLk>96>hJZf5g)_6#@adc075ESHN1@T2jgXpq2Z%{GRkZ0A4l%j8=V)sghk zn#nC!!54&~)A7`ss84tPcD&oxK%e`iYUF;m-fVhR_(T31?;W>ytj|;Dp=mi9uC51E z{|GHYxy`&-tT*6ty)SwKKkDEy5Q^a zo46zN7zbTWxIoWujw%ke8S;3E>Pzsfn{mGNWbk?X+IV|ICw8)TGXv$~?)~ECa*x*f zQgyPW^6|Ux{W@&3S_hldS;rxr$ZyE6^fH-`i`Oyeg%n2&xs=b(FHU8XM|R55372TpL`Pcv{-zp*mF1!Va&7w*?~cWCEKf)O>N>%I17#0znQ;tcKYF0 z>0bRg+#;Qi&h>WtI_zhdzOzaH z{Xuc28Qc0*H{ApS||0{NvBla4CPe zUtD~f-N+w}m8Xk;UiihA+0b&JZ02hJ|KP*sVa6SV!Om4H)o(8z+zA(&2~SZk-}z~4 zUR}v-vFY%8Tt>E3%&`#msxCT57ot0IRIHDiEoVP@`?F@Xe4P#dvU++rOgPiM_rDta zh2COy`3Lp8z3}n3XZztL$N2zSqxqtDh=1k;;q^2>so7V))s#3&2qTq>=*2c?S7bF zTnt-T%0`JLo^r8Xujf6T=3<#wy31o>H*Agi_DuVK^!7*1=J?&&&%XGFa*uQu!z{e> z^=!j#zyI~G{%PQum@R)kTZG4cxp?PNF)u9ST=6P?z*;elI;FTz{%ZaOeNy#>nf8YU zmYNYhGA`=i6Z0ML61?u~`CYyJ;`66HfVhLz8q|owK^Hoow9(i2`0R}y_?ykyxf))+ z8YZD{U?TfC)z8p|*v?1D;prUUy6a=YA*ZjuUmm~OnG_r1Q{L@9-G8?jsaS{}gIR^N z_{F;V+IKpu=DO&A!pTtkQ|pGw!41rrfG^B+Kc4FJ_RiVG*`DJ7#!xJXyFBy(_a~f% zhUR={oPWF7onw>OUOC2~joQ9Ww?7w(`PHunUG&yXP2ytjtnPptKbG$v-Mdw6&t}ry zDWAWd#5^ydzAl}C`TX5mOPXHrk{Wp4{L+#@F`}Fsu`)vnW^>tsjaggEu6%G zd^F5s+)aafwmZ1on!@IG<0I{}h1u>6cu)55#gF2scP9C@>E2n4e7_uaKfLbN>mQvx z3=4S>cX72i-})YOCvR7arQ@=c&0vqG+b=p=awZ&+(`@qg>)GdQ1T2>Ae(lpgR9`6< zEH<(SGucrydSH8#*d8XHQ^&m#e(#ZujRuBK6SXkr8L5Cr>%Y`pSjcX>+?2S)m*HZ z)=gjkLcfy+*+y8tc}Ba%6?E|Svmxf5zwpT)hCSPCKXK2uTVuL7*YX?c_To<5AzWnk zOrCrE#_x*jzZ(4bRyhhArMF7YqWXtEda=!N_KU7L9+g>(`2Xe~;_hwqZ1Ay{e*4Fv z|KLB(?4SL+t6RmLcRJ^3T^5s^<}1v*7-k`q`>@T^#d+$4Guaq=FWcMk*}v@PV!i>kaxJ`33{2~YHtkZsQ~se3YB?V|_?>L(=J5St8SUd#d-kJ+qXECU z)ZHHm0~!f4fs>eH4d?x7_W<{fKDK%vKB1Zc-VQzwdyfl6&+DMGE>~8|w$?kH%Y~kq zo#ykMWgL#1Z*?x)BibD^og+QN@Jjm&S5SMWzxPJ7aUN95+$=uF57&G8R0mAX={%MH zbf-773v<2Od|&kIN6R_XchoKA7;2Ii$~mr-XI$+4F7<_rJ>CAy@W|Gf|Erv%Qbexr3Q}PgYw2l;Ws@yPW%1$(iF+x z-0kjcyT7sBiIX7>mv!@%I@Kt!zax^*z5CXXr6|vyzt9E6kBAk zi$Tw2FX`jxm!dT$wm5h@n^$gTX8cZjJy$->uF{=?8=sWlz52y}tDmI2u(PuI(r?ld z{(bq$7sbWt4ivkw*~jJeG}-iUANO9PY(!kVu4Jhc4GyoABIhl|DL1IJdZUtG3PpVdO^e=i%2FR5?z zIQ;aa|G)L?zZ3()G_qmY6&%S+i#O9NKS|H?`Swojr8}Grd{Uk1mi({oM4E9k-ILAi*kNZ1heJ=%O1wdM@WA5_(=c0SrI_J=)yg*NPdXM8WmDuJ z^TpzGox8=p%;3U1fb;B>m$L7;V*I3DCo>opi(!^K^RRY4N*vAK$X)TXX)G=j>(c*M zyI`x?B=(=~$hp?uY!Lb2wXlx4+b{Jqes4UfVgh_CJt4GH#Ek<7I82T170ntp?3%m; zUnHF5$Ho5Y8Rjw5V1^|Qy?x~$Fd;n)deGD#Xv^|b?7w;o9+3V*m>Uj}z7d>v{D1u! zo8iX%lw1XWg>JTW;5XIu@QcUte-q)eSL1@wzLQ_f^!wyIcwu}3UhMT^9X{krwVMIk z-+K_IndV>rF0Yy{_E*z^MQpYg@SDdU7U%vnP4nX1yKnyX?6u$ga` zXD|Fby@|f}PG?K~gtot$`>A^PY5a`pTI$@Z-Tjxd^AD>xJ{z{a)_#quRfe5B%vQhm z<=6SXd}8sSzRu%x2f&wT95Lb?t1l=?)_xvTra>_`M5duE7@8)wR#uvRMv|F z?-pa@IvvzA|ML6U)M^@!s?*#mmegxb+v|4x%!60!k?V}80ja;w-hC-$= zt=-G_n4zZ+ce`H9)o>Sa&}?>orJVY{xV`5VAMa#iXs;Y}ANYaIur7QKe8RcvaX40! z?SsC5ntqF&XLo^V{vS5#aWleR_~@H~$NOCUYBW&w@|s1u(;2%T|M|hYX;KvL z(n-~CkAI-=PX90M5w&%*+Xj3)UWa*jo{di0K^%qs^h`F2IcPFE3n#4~ohf`+*^}4g2;~ceI?YyiGrwxiqk} zVNG7FU%I`c?XaC4FlUz@3jB>7VK4X(I-am+etWu~ohz16D?TWfIgT5Br&`y3d$(Ku ziTin+_Q1*W&3DOPz545~&+ax~$vi4}|1i_R-D)rAT0guoHhRj4gZiN}NS{hBTK(`ni?v4@_PhcCkFg z9_YswQ%_`%;UKdWWZt-6};9Iby6f84X(DDS3Iyd4fBHkD&ehpow5{cf10 zm~5qZe5JUM-(Rc-z1%wC8RMzLZ0MgptzCpS^|_bxM|v$T=Qq~kaF1@)%h7v|x(0rTk_9l!HMpW8dP4zN!5a<_dIcPysaArIvHXUaur z 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�*uGO7y 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

pLGFR0awYpW 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

70@)MmotS2{+4gw!t~G=R0WdbEej* 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� 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

T$MzX76)-lE$r{L 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 z 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�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%0Dq`6U?k;nQSF?M&wB{S7ij9_xf^PBuDU42aWvb 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 literal 0 HcmV?d00001 diff --git a/test/3DSFiles/cube_with_diffuse_texture.3DS b/test/3DSFiles/cube_with_diffuse_texture.3DS new file mode 100644 index 0000000000000000000000000000000000000000..e68b92f51896c42183efeac1d26918d57f3bb093 GIT binary patch literal 1423 zcmZ`(OGs2<6#izMnZ}vY8E^KOUbi5n!0AqeQKR=UCK{-yBNVL^)CeLX5?o3INeh#N zDe1wOWQ(w_IY^62b6ZlIXjKq2yB?b`+BltaXMX)>F+ZIDJKs6~dEEcrbKBc*c>y;9 z0Ln-j8>c*g-Y9tP`8Tp6thYdz4Mp&RHL>gyl4qQwB~4$+V`>H<^*f8cma!{db6{pT&d{LPFFyAu}a z+O?PUY+staXI**{TVVSl>1(-`3);IcKR@xlg%&S(kIlbxq6fyobT@n6didf&NR$=#0!Co;heR zXH@QnpS5{wjaRF4IwN!bDws3QR=vant(5^BiM!AgnO_T`v^6w zj`9}$C%CA~Da*yURK7?MozA1Uou)wL^TPAV5-NyF$YK@*_E*vZb~Wu@v|DGBP0bz6 zph<_{=D!RtX63dRaku@HxMt@^KjegNjJXNqa*AC=M`(=*QSBP$>x(ZezvPSEZyy$B c2RBaL4*1LyE6QSgMKM?ol*~s(ollSPKSb|(A^-pY literal 0 HcmV?d00001 diff --git a/test/3DSFiles/cube_with_specular_texture.3DS b/test/3DSFiles/cube_with_specular_texture.3DS new file mode 100644 index 0000000000000000000000000000000000000000..4844c12c31b1df394d36be318daf25c90e00850e GIT binary patch literal 1423 zcmZ`(OGs2<6#izMnZ}vY8L#%3Ubmo;0_{$OQKR=UCK{-yBNVL^bRvj|NN_0;BrQyG zQPP7k$rfQaxXa4+WF+ZI9o$s9gJpTXQb2~b2`T;LO z0Ln<3nx=e!-Xys5>vkXpe>< zJu6{JtQuVs1A2;Bx`<(lPN(CEZ)!)!B;f zX-)y8r}NuM^@u>2Gjs#pL;VATm$d|7-6k5bMm=;ogumlhYvZGo+4K7~KK;p!4SQ1- z>Dtv74QyYYy=z^3oLFG{66xzs>q;Bhc@NOGGEVjs=}y7UdprgEE7D8lmt$pJ&R;54 z?nkaA>vB%{Tym^@M!A1kmvfeimHU+YmvuR(T-UUM?t93LCycds_w}DjPG@BP;Pe55 zIiqqX`n26&YrI^W(;1obSHYZdw(7ZV`D%^kzX?Mi+nEc$K=z+d7xW)NZ{vsawT8ft zu}PgdBl`uiKOUIS&xY0Hk;tW_z;B63ojD`>1+sr47fXJfxu?G$ozn%LdmA^HGqPVG z`}?d0`exgI)#s?oCBU557L)Vqjxo<&vyFU5>&e#VJJ>KboJp0Pnp;+_Hx#v*#7pxN zAfd}7^udo!ge*RiAdPc~Fid;55LVE%MhK&*f=yH$Zb(;c}&kN5dOQawwA&XfM*q=!Y*){a^qr*CrZf@yp z0ZlrBHveUKF)O#lhux}P57KkjvR2><{9 literal 0 HcmV?d00001 diff --git a/test/3DSFiles/test.png b/test/3DSFiles/test.png new file mode 100644 index 0000000000000000000000000000000000000000..501fcdecfddcd9941c41fd38942e9f1e0c1d6c32 GIT binary patch literal 23700 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/Eb)w*KBo;{E)8!l+H^8twPl%NNDU?mk{h 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 T6}oi%n{mKc-yBnfb`JhGoqlO~ literal 0 HcmV?d00001 diff --git a/test/3DSFiles/test1.3ds b/test/3DSFiles/test1.3ds new file mode 100644 index 0000000000000000000000000000000000000000..17bbb47d397c854c8a53ae633037593a8cee9044 GIT binary patch literal 10654 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..fe649fc16cc5a971d94808c4776f299bae1f6219 GIT binary patch literal 40474 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d551aa3aaf80adf9b7760e2eb8de95a5c3e53df6 GIT binary patch literal 23558 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 ",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 0000000000000000000000000000000000000000..36ceed902975750cd215f0a71206020011223f89 GIT binary patch literal 108054 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d551aa3aaf80adf9b7760e2eb8de95a5c3e53df6 GIT binary patch literal 23558 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 + +// 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 0000000000000000000000000000000000000000..3f93df5bf93ab4437414ee8bc522198972b6bc7c GIT binary patch literal 122221 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 YctDCS(I?`+Z`p^RciM-ahwKymKZd8!xc~qF literal 0 HcmV?d00001 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +