diff options
Diffstat (limited to 'libs/assimp/tools/assimp_view/assimp_view.cpp')
-rw-r--r-- | libs/assimp/tools/assimp_view/assimp_view.cpp | 1086 |
1 files changed, 1086 insertions, 0 deletions
diff --git a/libs/assimp/tools/assimp_view/assimp_view.cpp b/libs/assimp/tools/assimp_view/assimp_view.cpp new file mode 100644 index 0000000..c5c48fd --- /dev/null +++ b/libs/assimp/tools/assimp_view/assimp_view.cpp @@ -0,0 +1,1086 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +#include "assimp_view.h" +#include <assimp/StringUtils.h> +#include <map> + +#ifdef __MINGW32__ +#include <mmsystem.h> +#else +#include <timeapi.h> +#endif + +using namespace std; + +namespace AssimpView { + +extern std::string g_szNormalsShader; +extern std::string g_szDefaultShader; +extern std::string g_szPassThroughShader; + +//------------------------------------------------------------------------------- +HINSTANCE g_hInstance = nullptr; +HWND g_hDlg = nullptr; +IDirect3D9 *g_piD3D = nullptr; +IDirect3DDevice9 *g_piDevice = nullptr; +IDirect3DVertexDeclaration9 *gDefaultVertexDecl = nullptr; +double g_fFPS = 0.0f; +char g_szFileName[MAX_PATH]; +ID3DXEffect *g_piDefaultEffect = nullptr; +ID3DXEffect *g_piNormalsEffect = nullptr; +ID3DXEffect *g_piPassThroughEffect = nullptr; +ID3DXEffect *g_piPatternEffect = nullptr; +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 = nullptr; +float g_fWheelPos = -10.0f; +bool g_bLoadingCanceled = false; +IDirect3DTexture9 *g_pcTexture = nullptr; +bool g_bPlay = false; +double g_dCurrent = 0.; + +// default pp steps +unsigned int ppsteps = aiProcess_CalcTangentSpace | // calculate tangents and bitangents if possible + aiProcess_JoinIdenticalVertices | // join identical vertices/ optimize indexing + aiProcess_ValidateDataStructure | // perform a full validation of the loader's output + aiProcess_ImproveCacheLocality | // improve the cache locality of the output vertices + aiProcess_RemoveRedundantMaterials | // remove redundant materials + aiProcess_FindDegenerates | // remove degenerated polygons from the import + aiProcess_FindInvalidData | // detect invalid model data, such as invalid normal vectors + aiProcess_GenUVCoords | // convert spherical, cylindrical, box and planar mapping to proper UVs + aiProcess_TransformUVCoords | // preprocess UV transformations (scaling, translation ...) + aiProcess_FindInstances | // search for instanced meshes and remove them by references to one master + aiProcess_LimitBoneWeights | // limit bone weights to 4 per vertex + aiProcess_OptimizeMeshes | // join small meshes, if possible; + aiProcess_SplitByBoneCount | // split meshes with too many bones. Necessary for our (limited) hardware skinning shader + 0; + +unsigned int ppstepsdefault = ppsteps; + +bool nopointslines = false; + +extern bool g_bWasFlipped /*= false*/; + +aiMatrix4x4 g_mWorld; +aiMatrix4x4 g_mWorldRotate; +aiVector3D g_vRotateSpeed = aiVector3D(0.5f, 0.5f, 0.5f); + +// NOTE: The second light direction is now computed from the first +aiVector3D g_avLightDirs[1] = { aiVector3D(-0.5f, 0.6f, 0.2f) }; + + D3DCOLOR g_avLightColors[3] = { + D3DCOLOR_ARGB(0xFF, 0xFF, 0xFF, 0xFF), + D3DCOLOR_ARGB(0xFF, 0xFF, 0x00, 0x00), + D3DCOLOR_ARGB(0xFF, 0x05, 0x05, 0x05), +}; + +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 = nullptr; + +// +// Contains the mask image for the HUD +// (used to determine the position of a click) +// +unsigned char *g_szImageMask = nullptr; + +float g_fLoadTime = 0.0f; + +//------------------------------------------------------------------------------- +// Entry point for the loader thread +// The loader thread loads the asset while the progress dialog displays the +// smart progress bar +//------------------------------------------------------------------------------- +DWORD WINAPI LoadThreadProc(LPVOID lpParameter) { + UNREFERENCED_PARAMETER(lpParameter); + + // get current time + double fCur = (double)timeGetTime(); + + aiPropertyStore *props = aiCreatePropertyStore(); + aiSetImportPropertyInteger(props, AI_CONFIG_IMPORT_TER_MAKE_UVS, 1); + aiSetImportPropertyFloat(props, AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE, g_smoothAngle); + aiSetImportPropertyInteger(props, AI_CONFIG_PP_SBP_REMOVE, nopointslines ? aiPrimitiveType_LINE | aiPrimitiveType_POINT : 0); + + aiSetImportPropertyInteger(props, AI_CONFIG_GLOB_MEASURE_TIME, 1); + //aiSetImportPropertyInteger(props,AI_CONFIG_PP_PTV_KEEP_HIERARCHY,1); + + // Call ASSIMPs C-API to load the file + g_pcAsset->pcScene = (aiScene *)aiImportFileExWithProperties(g_szFileName, + ppsteps | /* configurable pp steps */ + aiProcess_GenSmoothNormals | // generate smooth normal vectors if not existing + aiProcess_SplitLargeMeshes | // split large, unrenderable meshes into submeshes + aiProcess_Triangulate | // triangulate polygons with more than 3 edges + aiProcess_ConvertToLeftHanded | // convert everything to D3D left handed space + aiProcess_SortByPType | // make 'clean' meshes which consist of a single typ of primitives + 0, + nullptr, + props); + + aiReleasePropertyStore(props); + + // get the end time of zje operation, calculate delta t + double fEnd = (double)timeGetTime(); + g_fLoadTime = (float)((fEnd - fCur) / 1000); + g_bLoadingFinished = true; + + // check whether the loading process has failed ... + if (nullptr == g_pcAsset->pcScene) { + CLogDisplay::Instance().AddEntry("[ERROR] Unable to load this asset:", + D3DCOLOR_ARGB(0xFF, 0xFF, 0, 0)); + + // print ASSIMPs error string to the log display + CLogDisplay::Instance().AddEntry(aiGetErrorString(), + D3DCOLOR_ARGB(0xFF, 0xFF, 0, 0)); + return 1; + } + + return 0; +} + +//------------------------------------------------------------------------------- +// load the current asset +// THe path to the asset is specified in the global path variable +//------------------------------------------------------------------------------- +int LoadAsset() { + // set the world and world rotation matrices to the identity + g_mWorldRotate = aiMatrix4x4(); + g_mWorld = aiMatrix4x4(); + + // char szTemp[MAX_PATH+64]; + // sprintf(szTemp,"Starting to load %s",g_szFileName); + CLogWindow::Instance().WriteLine( + "----------------------------------------------------------------------------"); + // CLogWindow::Instance().WriteLine(szTemp); + // CLogWindow::Instance().WriteLine( + // "----------------------------------------------------------------------------"); + CLogWindow::Instance().SetAutoUpdate(false); + + // create a helper thread to load the asset + DWORD dwID; + g_bLoadingCanceled = false; + g_pcAsset = new AssetHelper(); + g_hThreadHandle = CreateThread(nullptr, 0, &LoadThreadProc, nullptr, 0, &dwID); + + if (!g_hThreadHandle) { + CLogDisplay::Instance().AddEntry( + "[ERROR] Unable to create helper thread for loading", + D3DCOLOR_ARGB(0xFF, 0xFF, 0, 0)); + return 0; + } + + // show the progress bar dialog + DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_LOADDIALOG), + g_hDlg, &ProgressMessageProc); + + // update the log window + CLogWindow::Instance().SetAutoUpdate(true); + CLogWindow::Instance().Update(); + + // now we should have loaded the asset. Check this ... + g_bLoadingFinished = false; + if (!g_pcAsset || !g_pcAsset->pcScene) { + if (g_pcAsset) { + delete g_pcAsset; + g_pcAsset = nullptr; + } + return 0; + } + + // allocate a new MeshHelper array and build a new instance + // for each mesh in the original asset + g_pcAsset->apcMeshes = new AssetHelper::MeshHelper *[g_pcAsset->pcScene->mNumMeshes](); + for (unsigned int i = 0; i < g_pcAsset->pcScene->mNumMeshes; ++i) + g_pcAsset->apcMeshes[i] = new AssetHelper::MeshHelper(); + + // create animator + g_pcAsset->mAnimator = new SceneAnimator(g_pcAsset->pcScene); + + // build a new caption string for the viewer + static const size_t Size = MAX_PATH + 10; + char szOut[Size]; + ai_snprintf(szOut, Size, AI_VIEW_CAPTION_BASE " [%s]", g_szFileName); + SetWindowText(g_hDlg, szOut); + + // scale the asset vertices to fit into the viewer window + ScaleAsset(); + + // reset the camera view to the default position + 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); + + // build native D3D vertex/index buffers, textures, materials + if (1 != CreateAssetData()) + return 0; + + if (!g_pcAsset->pcScene->HasAnimations()) { + EnableWindow(GetDlgItem(g_hDlg, IDC_PLAY), FALSE); + EnableWindow(GetDlgItem(g_hDlg, IDC_SLIDERANIM), FALSE); + } else { + EnableWindow(GetDlgItem(g_hDlg, IDC_PLAY), TRUE); + EnableWindow(GetDlgItem(g_hDlg, IDC_SLIDERANIM), TRUE); + } + + CLogDisplay::Instance().AddEntry("[OK] The asset has been loaded successfully"); + CDisplay::Instance().FillDisplayList(); + CDisplay::Instance().FillAnimList(); + + CDisplay::Instance().FillDefaultStatistics(); + + // render the scene once + CDisplay::Instance().OnRender(); + + g_pcAsset->iNormalSet = AssetHelper::ORIGINAL; + g_bWasFlipped = false; + return 1; +} + +//------------------------------------------------------------------------------- +// Delete the loaded asset +// The function does nothing is no asset is loaded +//------------------------------------------------------------------------------- +int DeleteAsset(void) { + if (!g_pcAsset) { + return 0; + } + + // don't anymore know why this was necessary ... + CDisplay::Instance().OnRender(); + + // delete everything + 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->mAnimator; + delete g_pcAsset; + g_pcAsset = nullptr; + + // reset the caption of the viewer window + SetWindowText(g_hDlg, AI_VIEW_CAPTION_BASE); + + // clear UI + CDisplay::Instance().ClearAnimList(); + CDisplay::Instance().ClearDisplayList(); + + CMaterialManager::Instance().Reset(); + UpdateWindow(g_hDlg); + return 1; +} + +//------------------------------------------------------------------------------- +// Calculate the boundaries of a given node and all of its children +// The boundaries are in Worldspace (AABB) +// piNode Input node +// p_avOut Receives the min/max boundaries. Must point to 2 vec3s +// piMatrix Transformation matrix of the graph at this position +//------------------------------------------------------------------------------- +int CalculateBounds(aiNode *piNode, aiVector3D *p_avOut, const aiMatrix4x4 &piMatrix) { + ai_assert(nullptr != piNode); + ai_assert(nullptr != p_avOut); + + 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 = min(p_avOut[0].x, pc1.x); + p_avOut[0].y = min(p_avOut[0].y, pc1.y); + p_avOut[0].z = min(p_avOut[0].z, pc1.z); + p_avOut[1].x = max(p_avOut[1].x, pc1.x); + p_avOut[1].y = max(p_avOut[1].y, pc1.y); + p_avOut[1].z = 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; +} +//------------------------------------------------------------------------------- +// Scale the asset that it fits perfectly into the viewer window +// The function calculates the boundaries of the mesh and modifies the +// global world transformation matrix according to the aset AABB +//------------------------------------------------------------------------------- +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); + return 1; +} + +//------------------------------------------------------------------------------- +// Generate a vertex buffer which holds the normals of the asset as +// a list of unconnected lines +// pcMesh Input mesh +// pcSource Source mesh from ASSIMP +//------------------------------------------------------------------------------- +int GenerateNormalsAsLineList(AssetHelper::MeshHelper *pcMesh, const aiMesh *pcSource) { + ai_assert(nullptr != pcMesh); + ai_assert(nullptr != 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, nullptr))) { + CLogDisplay::Instance().AddEntry("Failed to create vertex buffer for the normal list", + D3DCOLOR_ARGB(0xFF, 0xFF, 0, 0)); + return 2; + } + + // now fill the vertex buffer with data + 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(); + + // scalo with the inverse of the world scaling to make sure + // the normals have equal length in each case + // TODO: Check whether this works in every case, I don't think so + 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; +} + +//------------------------------------------------------------------------------- +// Create the native D3D representation of the asset: vertex buffers, +// index buffers, materials ... +//------------------------------------------------------------------------------- +int CreateAssetData() { + if (!g_pcAsset) return 0; + + // reset all subsystems + CMaterialManager::Instance().Reset(); + CDisplay::Instance().Reset(); + + for (unsigned int i = 0; i < g_pcAsset->pcScene->mNumMeshes; ++i) { + const aiMesh *mesh = g_pcAsset->pcScene->mMeshes[i]; + + // create the material for the mesh + if (!g_pcAsset->apcMeshes[i]->piEffect) { + CMaterialManager::Instance().CreateMaterial( + g_pcAsset->apcMeshes[i], mesh); + } + + // create vertex buffer + if (FAILED(g_piDevice->CreateVertexBuffer(sizeof(AssetHelper::Vertex) * + mesh->mNumVertices, + D3DUSAGE_WRITEONLY, + 0, + D3DPOOL_DEFAULT, &g_pcAsset->apcMeshes[i]->piVB, nullptr))) { + MessageBox(g_hDlg, "Failed to create vertex buffer", + "ASSIMP Viewer Utility", MB_OK); + return 2; + } + + DWORD dwUsage = 0; + if (g_pcAsset->apcMeshes[i]->piOpacityTexture || 1.0f != g_pcAsset->apcMeshes[i]->fOpacity) + dwUsage |= D3DUSAGE_DYNAMIC; + + unsigned int nidx = 0; + switch (mesh->mPrimitiveTypes) { + case aiPrimitiveType_POINT: + nidx = 1; + break; + case aiPrimitiveType_LINE: + nidx = 2; + break; + case aiPrimitiveType_TRIANGLE: + nidx = 3; + break; + default: + CLogWindow::Instance().WriteLine("Unknown primitive type"); + break; + }; + + unsigned int numIndices = mesh->mNumFaces * 3; + if (0 == numIndices && nidx == 1) { + numIndices = mesh->mNumVertices; + } + // check whether we can use 16 bit indices + if (numIndices >= 65536) { + // create 32 bit index buffer + if (FAILED(g_piDevice->CreateIndexBuffer(4 * numIndices, + D3DUSAGE_WRITEONLY | dwUsage, + D3DFMT_INDEX32, + D3DPOOL_DEFAULT, + &g_pcAsset->apcMeshes[i]->piIB, + nullptr))) { + MessageBox(g_hDlg, "Failed to create 32 Bit 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 < mesh->mNumFaces; ++x) { + for (unsigned int a = 0; a < nidx; ++a) { + *pbData++ = mesh->mFaces[x].mIndices[a]; + } + } + } else { + // create 16 bit index buffer + if (FAILED(g_piDevice->CreateIndexBuffer(2 * +numIndices, + D3DUSAGE_WRITEONLY | dwUsage, + D3DFMT_INDEX16, + D3DPOOL_DEFAULT, + &g_pcAsset->apcMeshes[i]->piIB, + nullptr))) { + MessageBox(g_hDlg, "Failed to create 16 Bit index buffer", + "ASSIMP Viewer Utility", MB_OK); + return 2; + } + + // now fill the index buffer + uint16_t *pbData; + g_pcAsset->apcMeshes[i]->piIB->Lock(0, 0, (void **)&pbData, 0); + for (unsigned int x = 0; x < mesh->mNumFaces; ++x) { + for (unsigned int a = 0; a < nidx; ++a) { + *pbData++ = (uint16_t)mesh->mFaces[x].mIndices[a]; + } + } + } + g_pcAsset->apcMeshes[i]->piIB->Unlock(); + + // collect weights on all vertices. Quick and careless + std::vector<std::vector<aiVertexWeight>> weightsPerVertex(mesh->mNumVertices); + for (unsigned int a = 0; a < mesh->mNumBones; a++) { + const aiBone *bone = mesh->mBones[a]; + for (unsigned int b = 0; b < bone->mNumWeights; b++) + weightsPerVertex[bone->mWeights[b].mVertexId].push_back(aiVertexWeight(a, bone->mWeights[b].mWeight)); + } + + // 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 < mesh->mNumVertices; ++x) { + pbData2->vPosition = mesh->mVertices[x]; + + if (nullptr == mesh->mNormals) + pbData2->vNormal = aiVector3D(0.0f, 0.0f, 0.0f); + else + pbData2->vNormal = mesh->mNormals[x]; + + if (nullptr == mesh->mTangents) { + pbData2->vTangent = aiVector3D(0.0f, 0.0f, 0.0f); + pbData2->vBitangent = aiVector3D(0.0f, 0.0f, 0.0f); + } else { + pbData2->vTangent = mesh->mTangents[x]; + pbData2->vBitangent = mesh->mBitangents[x]; + } + + if (mesh->HasVertexColors(0)) { + pbData2->dColorDiffuse = D3DCOLOR_ARGB( + ((unsigned char)max(min(mesh->mColors[0][x].a * 255.0f, 255.0f), 0.0f)), + ((unsigned char)max(min(mesh->mColors[0][x].r * 255.0f, 255.0f), 0.0f)), + ((unsigned char)max(min(mesh->mColors[0][x].g * 255.0f, 255.0f), 0.0f)), + ((unsigned char)max(min(mesh->mColors[0][x].b * 255.0f, 255.0f), 0.0f))); + } else + pbData2->dColorDiffuse = D3DCOLOR_ARGB(0xFF, 0xff, 0xff, 0xff); + + // ignore a third texture coordinate component + if (mesh->HasTextureCoords(0)) { + pbData2->vTextureUV = aiVector2D( + mesh->mTextureCoords[0][x].x, + mesh->mTextureCoords[0][x].y); + } else + pbData2->vTextureUV = aiVector2D(0.5f, 0.5f); + + if (mesh->HasTextureCoords(1)) { + pbData2->vTextureUV2 = aiVector2D( + mesh->mTextureCoords[1][x].x, + mesh->mTextureCoords[1][x].y); + } else + pbData2->vTextureUV2 = aiVector2D(0.5f, 0.5f); + + // Bone indices and weights + if (mesh->HasBones()) { + unsigned char boneIndices[4] = { 0, 0, 0, 0 }; + unsigned char boneWeights[4] = { 0, 0, 0, 0 }; + ai_assert(weightsPerVertex[x].size() <= 4); + for (unsigned int a = 0; a < weightsPerVertex[x].size(); a++) { + boneIndices[a] = static_cast<unsigned char>(weightsPerVertex[x][a].mVertexId); + boneWeights[a] = (unsigned char)(weightsPerVertex[x][a].mWeight * 255.0f); + } + + memcpy(pbData2->mBoneIndices, boneIndices, sizeof(boneIndices)); + memcpy(pbData2->mBoneWeights, boneWeights, sizeof(boneWeights)); + } else { + memset(pbData2->mBoneIndices, 0, sizeof(pbData2->mBoneIndices)); + memset(pbData2->mBoneWeights, 0, sizeof(pbData2->mBoneWeights)); + } + + ++pbData2; + } + g_pcAsset->apcMeshes[i]->piVB->Unlock(); + + // now generate the second vertex buffer, holding all normals + if (!g_pcAsset->apcMeshes[i]->piVBNormals) { + GenerateNormalsAsLineList(g_pcAsset->apcMeshes[i], mesh); + } + } + return 1; +} + +//------------------------------------------------------------------------------- +// Delete all effects, textures, vertex buffers ... associated with +// an asset +//------------------------------------------------------------------------------- +int DeleteAssetData(bool bNoMaterials) { + 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 = nullptr; + } + if (g_pcAsset->apcMeshes[i]->piVBNormals) { + g_pcAsset->apcMeshes[i]->piVBNormals->Release(); + g_pcAsset->apcMeshes[i]->piVBNormals = nullptr; + } + if (g_pcAsset->apcMeshes[i]->piIB) { + g_pcAsset->apcMeshes[i]->piIB->Release(); + g_pcAsset->apcMeshes[i]->piIB = nullptr; + } + + // TODO ... unfixed memory leak + // delete storage eventually allocated to hold a copy + // of the original vertex normals + //if (g_pcAsset->apcMeshes[i]->pvOriginalNormals) + //{ + // delete[] g_pcAsset->apcMeshes[i]->pvOriginalNormals; + //} + + if (!bNoMaterials) { + if (g_pcAsset->apcMeshes[i]->piEffect) { + g_pcAsset->apcMeshes[i]->piEffect->Release(); + g_pcAsset->apcMeshes[i]->piEffect = nullptr; + } + if (g_pcAsset->apcMeshes[i]->piDiffuseTexture) { + g_pcAsset->apcMeshes[i]->piDiffuseTexture->Release(); + g_pcAsset->apcMeshes[i]->piDiffuseTexture = nullptr; + } + if (g_pcAsset->apcMeshes[i]->piNormalTexture) { + g_pcAsset->apcMeshes[i]->piNormalTexture->Release(); + g_pcAsset->apcMeshes[i]->piNormalTexture = nullptr; + } + if (g_pcAsset->apcMeshes[i]->piSpecularTexture) { + g_pcAsset->apcMeshes[i]->piSpecularTexture->Release(); + g_pcAsset->apcMeshes[i]->piSpecularTexture = nullptr; + } + if (g_pcAsset->apcMeshes[i]->piAmbientTexture) { + g_pcAsset->apcMeshes[i]->piAmbientTexture->Release(); + g_pcAsset->apcMeshes[i]->piAmbientTexture = nullptr; + } + if (g_pcAsset->apcMeshes[i]->piEmissiveTexture) { + g_pcAsset->apcMeshes[i]->piEmissiveTexture->Release(); + g_pcAsset->apcMeshes[i]->piEmissiveTexture = nullptr; + } + if (g_pcAsset->apcMeshes[i]->piOpacityTexture) { + g_pcAsset->apcMeshes[i]->piOpacityTexture->Release(); + g_pcAsset->apcMeshes[i]->piOpacityTexture = nullptr; + } + if (g_pcAsset->apcMeshes[i]->piShininessTexture) { + g_pcAsset->apcMeshes[i]->piShininessTexture->Release(); + g_pcAsset->apcMeshes[i]->piShininessTexture = nullptr; + } + } + } + return 1; +} + +//------------------------------------------------------------------------------- +// Switch between zoom/rotate view and the standard FPS view +// g_bFPSView specifies the view mode to setup +//------------------------------------------------------------------------------- +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; +} + +//------------------------------------------------------------------------------- +// Initialize the IDIrect3D interface +// Called by the WinMain +//------------------------------------------------------------------------------- +int InitD3D(void) { + if (nullptr == g_piD3D) { + g_piD3D = Direct3DCreate9(D3D_SDK_VERSION); + if (nullptr == g_piD3D) return 0; + } + return 1; +} + +//------------------------------------------------------------------------------- +// Release the IDirect3D interface. +// NOTE: Assumes that the device has already been deleted +//------------------------------------------------------------------------------- +int ShutdownD3D(void) { + ShutdownDevice(); + if (nullptr != g_piD3D) { + g_piD3D->Release(); + g_piD3D = nullptr; + } + return 1; +} + +template <class TComPtr> +inline void SafeRelease(TComPtr *&ptr) { + if (nullptr != ptr) { + ptr->Release(); + ptr = nullptr; + } +} + +//------------------------------------------------------------------------------- +// Shutdown the D3D device object and all resources associated with it +// NOTE: Assumes that the asset has already been deleted +//------------------------------------------------------------------------------- +int ShutdownDevice(void) { + // release other subsystems + CBackgroundPainter::Instance().ReleaseNativeResource(); + CLogDisplay::Instance().ReleaseNativeResource(); + + // release global shaders that have been allocated + SafeRelease(g_piDefaultEffect); + SafeRelease(g_piNormalsEffect); + SafeRelease(g_piPassThroughEffect); + SafeRelease(g_piPatternEffect); + SafeRelease(g_pcTexture); + SafeRelease(gDefaultVertexDecl); + + // delete the main D3D device object + SafeRelease(g_piDevice); + + // deleted the one channel image allocated to hold the HUD mask + delete[] g_szImageMask; + g_szImageMask = nullptr; + + return 1; +} + +//------------------------------------------------------------------------------- +//------------------------------------------------------------------------------- +int CreateHUDTexture() { + // lock the memory resource ourselves + HRSRC res = FindResource(nullptr, MAKEINTRESOURCE(IDR_HUD), RT_RCDATA); + HGLOBAL hg = LoadResource(nullptr, res); + void *pData = LockResource(hg); + + if (FAILED(D3DXCreateTextureFromFileInMemoryEx(g_piDevice, + pData, SizeofResource(nullptr, res), + D3DX_DEFAULT_NONPOW2, + D3DX_DEFAULT_NONPOW2, + 1, + 0, + D3DFMT_A8R8G8B8, + D3DPOOL_MANAGED, + D3DX_DEFAULT, + D3DX_DEFAULT, + 0, + nullptr, + nullptr, + &g_pcTexture))) { + CLogDisplay::Instance().AddEntry("[ERROR] Unable to load HUD texture", + D3DCOLOR_ARGB(0xFF, 0xFF, 0, 0)); + + g_pcTexture = nullptr; + g_szImageMask = nullptr; + + FreeResource(hg); + return 0; + } + + FreeResource(hg); + + D3DSURFACE_DESC sDesc; + g_pcTexture->GetLevelDesc(0, &sDesc); + + // lock the memory resource ourselves + res = FindResource(nullptr, MAKEINTRESOURCE(IDR_HUDMASK), RT_RCDATA); + hg = LoadResource(nullptr, res); + pData = LockResource(hg); + + IDirect3DTexture9 *pcTex; + if (FAILED(D3DXCreateTextureFromFileInMemoryEx(g_piDevice, + pData, SizeofResource(nullptr, res), + sDesc.Width, + sDesc.Height, + 1, + 0, + D3DFMT_L8, + D3DPOOL_MANAGED, // unnecessary + D3DX_DEFAULT, + D3DX_DEFAULT, + 0, + nullptr, + nullptr, + &pcTex))) { + CLogDisplay::Instance().AddEntry("[ERROR] Unable to load HUD mask texture", + D3DCOLOR_ARGB(0xFF, 0xFF, 0, 0)); + g_szImageMask = nullptr; + + FreeResource(hg); + return 0; + } + + FreeResource(hg); + + // lock the texture and copy it to get a pointer + D3DLOCKED_RECT sRect; + pcTex->LockRect(0, &sRect, nullptr, 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; + + // get the client rectangle of the window. + 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)); + + // get the current display mode + D3DDISPLAYMODE sMode; + g_piD3D->GetAdapterDisplayMode(0, &sMode); + + // fill the presentation parameter structure + 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.SwapEffect = D3DSWAPEFFECT_DISCARD; + sParams.BackBufferCount = 1; + + // check whether we can use a D32 depth buffer format + if (SUCCEEDED(g_piD3D->CheckDepthStencilMatch(0, eType, + D3DFMT_X8R8G8B8, D3DFMT_X8R8G8B8, D3DFMT_D32))) { + sParams.AutoDepthStencilFormat = D3DFMT_D32; + } else + sParams.AutoDepthStencilFormat = D3DFMT_D24X8; + + // 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; + } + + // get the device capabilities. If the hardware vertex shader is too old, we prefer software vertex processing + g_piD3D->GetDeviceCaps(0, D3DDEVTYPE_HAL, &g_sCaps); + DWORD creationFlags = D3DCREATE_MULTITHREADED; + if (g_sCaps.VertexShaderVersion >= D3DVS_VERSION(2, 0)) + creationFlags |= D3DCREATE_HARDWARE_VERTEXPROCESSING; + else + creationFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING; + + // create the D3D9 device object. with software-vertexprocessing if VS2.0 isn`t supported in hardware + if (FAILED(g_piD3D->CreateDevice(0, eType, g_hDlg, creationFlags, &sParams, &g_piDevice))) { + // if hardware fails use software rendering instead + if (bHW) return CreateDevice(p_bMultiSample, p_bSuperSample, false); + return 0; + } + + // create a vertex declaration to match the vertex + D3DVERTEXELEMENT9 *vdecl = AssetHelper::Vertex::GetDeclarationElements(); + if (FAILED(g_piDevice->CreateVertexDeclaration(vdecl, &gDefaultVertexDecl))) { + MessageBox(g_hDlg, "Failed to create vertex declaration", "Init", MB_OK); + return 0; + } + g_piDevice->SetVertexDeclaration(gDefaultVertexDecl); + + // get the capabilities of the device object + g_piDevice->GetDeviceCaps(&g_sCaps); + if (g_sCaps.PixelShaderVersion < D3DPS_VERSION(3, 0)) { + EnableWindow(GetDlgItem(g_hDlg, IDC_LOWQUALITY), FALSE); + } + + // compile the default material shader (gray gouraud/phong) + ID3DXBuffer *piBuffer = nullptr; + if (FAILED(D3DXCreateEffect(g_piDevice, + g_szDefaultShader.c_str(), + (UINT)g_szDefaultShader.length(), + nullptr, + nullptr, + AI_SHADER_COMPILE_FLAGS, + nullptr, + &g_piDefaultEffect, &piBuffer))) { + if (piBuffer) { + MessageBox(g_hDlg, (LPCSTR)piBuffer->GetBufferPointer(), "HLSL", MB_OK); + piBuffer->Release(); + } + return 0; + } + if (piBuffer) { + piBuffer->Release(); + piBuffer = nullptr; + } + + // use Fixed Function effect when working with shaderless cards + if (g_sCaps.PixelShaderVersion < D3DPS_VERSION(2, 0)) + g_piDefaultEffect->SetTechnique("DefaultFXSpecular_FF"); + + // create the shader used to draw the HUD + if (FAILED(D3DXCreateEffect(g_piDevice, + g_szPassThroughShader.c_str(), (UINT)g_szPassThroughShader.length(), + nullptr, nullptr, AI_SHADER_COMPILE_FLAGS, nullptr, &g_piPassThroughEffect, &piBuffer))) { + if (piBuffer) { + MessageBox(g_hDlg, (LPCSTR)piBuffer->GetBufferPointer(), "HLSL", MB_OK); + piBuffer->Release(); + } + return 0; + } + if (piBuffer) { + piBuffer->Release(); + piBuffer = nullptr; + } + + // use Fixed Function effect when working with shaderless cards + if (g_sCaps.PixelShaderVersion < D3DPS_VERSION(2, 0)) + g_piPassThroughEffect->SetTechnique("PassThrough_FF"); + + // create the shader used to visualize normal vectors + if (FAILED(D3DXCreateEffect(g_piDevice, + g_szNormalsShader.c_str(), (UINT)g_szNormalsShader.length(), + nullptr, nullptr, AI_SHADER_COMPILE_FLAGS, nullptr, &g_piNormalsEffect, &piBuffer))) { + if (piBuffer) { + MessageBox(g_hDlg, (LPCSTR)piBuffer->GetBufferPointer(), "HLSL", MB_OK); + piBuffer->Release(); + } + return 0; + } + if (piBuffer) { + piBuffer->Release(); + piBuffer = nullptr; + } + + //MessageBox( g_hDlg, "Failed to create vertex declaration", "Init", MB_OK); + + // use Fixed Function effect when working with shaderless cards + if (g_sCaps.PixelShaderVersion < D3DPS_VERSION(2, 0)) + g_piNormalsEffect->SetTechnique("RenderNormals_FF"); + + g_piDevice->SetRenderState(D3DRS_DITHERENABLE, TRUE); + + // create the texture for the HUD + CreateHUDTexture(); + CBackgroundPainter::Instance().RecreateNativeResource(); + CLogDisplay::Instance().RecreateNativeResource(); + + g_piPassThroughEffect->SetTexture("TEXTURE_2D", g_pcTexture); + return 1; +} + +//------------------------------------------------------------------------------- +int CreateDevice() { + 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; +} + +} // namespace AssimpView |