#include #include #include #include #include #include // C++ importer interface #include // Output data structure #include // Post processing flags #include #pragma comment (lib, "assimp.lib") // One instance per aiMesh in the globally loaded asset MeshHelper** apcMeshes; const aiScene* scene; //////////////////////////////////////////////////////////// // Load //////////////////////////////////////////////////////////// bool Load(const std::string& pFile) { // Create an instance of the Importer class Assimp::Importer importer; // And have it read the given file with some example postprocessing // Usually - if speed is not the most important aspect for you - you'll // propably to request more postprocessing than we do in this example. scene = importer.ReadFile(pFile, aiProcess_CalcTangentSpace | aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_FlipUVs); // If the import failed, report it if(!scene) { std::string s = "Error in load the " + pFile; MessageBoxA(NULL, s.c_str(), "Error mesh", MB_OK); return false; } // allocate a new MeshHelper array and build a new instance // for each mesh in the original asset apcMeshes = new MeshHelper*[scene->mNumMeshes](); for (unsigned int i = 0; i < scene->mNumMeshes;++i) apcMeshes[i] = new MeshHelper(); for (unsigned int i = 0; i < scene->mNumMeshes;++i) { const aiMesh* mesh = scene->mMeshes[i]; /* // create the material for the mesh if (!apcMeshes[i]->piEffect) { CMaterialManager::Instance().CreateMaterial(apcMeshes[i], mesh); } */ // create vertex buffer if(FAILED( devices.d3ddev->CreateVertexBuffer(sizeof(Vertex) * mesh->mNumVertices, D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &apcMeshes[i]->piVB,NULL))) { MessageBoxA(NULL, "Failed to create vertex buffer", "Error Model", MB_OK); return false; } DWORD dwUsage = 0; if (apcMeshes[i]->piOpacityTexture || 1.0f != apcMeshes[i]->fOpacity) dwUsage |= D3DUSAGE_DYNAMIC; unsigned int nidx; switch (mesh->mPrimitiveTypes) { case aiPrimitiveType_POINT: nidx = 1; break; case aiPrimitiveType_LINE: nidx = 2; break; case aiPrimitiveType_TRIANGLE: nidx = 3; break; default: ai_assert(false); }; // check whether we can use 16 bit indices if (mesh->mNumFaces * 3 >= 65536) { // create 32 bit index buffer if(FAILED( devices.d3ddev->CreateIndexBuffer( 4 * mesh->mNumFaces * nidx, D3DUSAGE_WRITEONLY | dwUsage, D3DFMT_INDEX32, D3DPOOL_DEFAULT, &apcMeshes[i]->piIB, NULL))) { MessageBoxA(NULL, "Failed to create 32 Bit index buffer", "Error model", MB_OK); return false; } // now fill the index buffer unsigned int* pbData; 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( devices.d3ddev->CreateIndexBuffer( 2 * mesh->mNumFaces * nidx, D3DUSAGE_WRITEONLY | dwUsage, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &apcMeshes[i]->piIB, NULL))) { MessageBoxA(NULL, "Failed to create 16 Bit index buffer", "Error model", MB_OK); return false; } // now fill the index buffer uint16_t* pbData; 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]; } } } apcMeshes[i]->piIB->Unlock(); // collect weights on all vertices. Quick and careless std::vector> 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 Vertex* pbData2; apcMeshes[i]->piVB->Lock(0,0,(void**)&pbData2,0); for (unsigned int x = 0; x < mesh->mNumVertices;++x) { pbData2->vPosition = mesh->mVertices[x]; if (NULL == mesh->mNormals) pbData2->vNormal = aiVector3D(0.0f,0.0f,0.0f); else pbData2->vNormal = mesh->mNormals[x]; if (NULL == 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 )) { using std::min; using std::max; unsigned char m_argb[4]; m_argb[0] = (unsigned char)max( min( mesh->mColors[0][x].a * 255.0f, 255.0f), 0.0f); m_argb[1] = (unsigned char)max( min( mesh->mColors[0][x].r * 255.0f, 255.0f), 0.0f); m_argb[2] = (unsigned char)max( min( mesh->mColors[0][x].g * 255.0f, 255.0f), 0.0f); m_argb[3] = (unsigned char)max( min( mesh->mColors[0][x].b * 255.0f, 255.0f), 0.0f); pbData2->dColorDiffuse = D3DCOLOR_ARGB(m_argb[0],m_argb[1],m_argb[2],m_argb[3]); } 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] = 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; } apcMeshes[i]->piVB->Unlock(); // now generate the second vertex buffer, holding all normals if (!apcMeshes[i]->piVBNormals) { //GenerateNormalsAsLineList(apcMeshes[i],mesh); ai_assert(NULL != apcMeshes[i]); ai_assert(NULL != mesh); if (!mesh->mNormals)return 0; // create vertex buffer if(FAILED( devices.d3ddev->CreateVertexBuffer(sizeof(LineVertex) * mesh->mNumVertices * 2, D3DUSAGE_WRITEONLY, LineVertex::GetFVF(), D3DPOOL_DEFAULT, &apcMeshes[i]->piVBNormals,NULL))) { MessageBoxA(NULL,"Failed to create vertex buffer for the normal list", "Error model", MB_OK); return false; } // now fill the vertex buffer with data LineVertex* pbData2; apcMeshes[i]->piVBNormals->Lock(0,0,(void**)&pbData2,0); for (unsigned int x = 0; x < mesh->mNumVertices;++x) { pbData2->vPosition = mesh->mVertices[x]; ++pbData2; aiVector3D vNormal = mesh->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 = mesh->mVertices[x] + vNormal; ++pbData2; } apcMeshes[i]->piVBNormals->Unlock(); } } // get the number of vertices/faces in the model unsigned int iNumVert = 0; unsigned int iNumFaces = 0; for (unsigned int i = 0; i < scene->mNumMeshes;++i) { iNumVert += scene->mMeshes[i]->mNumVertices; iNumFaces += scene->mMeshes[i]->mNumFaces; } meshrender = new MeshRender(devices, scene, apcMeshes); return true; } //////////////////////////////////////////////////////////// // Render //////////////////////////////////////////////////////////// void Render(bool wireframe) { if(wireframe) { devices.d3ddev->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME); } aiMatrix4x4 m; RenderNode(scene->mRootNode, true); // render all child nodes for (unsigned int i = 0; i < scene->mRootNode->mNumChildren;++i) RenderNode(scene->mRootNode->mChildren[i], true ); if(wireframe) { devices.d3ddev->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); } } //////////////////////////////////////////////////////////// // Render a single node //////////////////////////////////////////////////////////// bool RenderNode(aiNode* piNode, bool bAlpha) { aiMatrix4x4 aiMe; aiMatrix4x4* piMatrix = new aiMatrix4x4(); for (unsigned int i = 0; i < piNode->mNumMeshes;++i) { if (bAlpha) meshrender->DrawSorted(piNode->mMeshes[i], aiMe); else meshrender->DrawUnsorted(piNode->mMeshes[i]); } return true; } //////////////////////////////////////////////////////////// // Release //////////////////////////////////////////////////////////// void Release() { bool bNoMaterials = false; if (!scene) return; // TODO: Move this to a proper destructor for (unsigned int i = 0; i < scene->mNumMeshes;++i) { if(apcMeshes[i]->piVB) { apcMeshes[i]->piVB->Release(); apcMeshes[i]->piVB = NULL; } if(apcMeshes[i]->piVBNormals) { apcMeshes[i]->piVBNormals->Release(); apcMeshes[i]->piVBNormals = NULL; } if(apcMeshes[i]->piIB) { apcMeshes[i]->piIB->Release(); apcMeshes[i]->piIB = NULL; } if (!bNoMaterials) { if(apcMeshes[i]->piEffect) { apcMeshes[i]->piEffect->Release(); apcMeshes[i]->piEffect = NULL; } if(apcMeshes[i]->piDiffuseTexture) { apcMeshes[i]->piDiffuseTexture->Release(); apcMeshes[i]->piDiffuseTexture = NULL; } if(apcMeshes[i]->piNormalTexture) { apcMeshes[i]->piNormalTexture->Release(); apcMeshes[i]->piNormalTexture = NULL; } if(apcMeshes[i]->piSpecularTexture) { apcMeshes[i]->piSpecularTexture->Release(); apcMeshes[i]->piSpecularTexture = NULL; } if(apcMeshes[i]->piAmbientTexture) { apcMeshes[i]->piAmbientTexture->Release(); apcMeshes[i]->piAmbientTexture = NULL; } if(apcMeshes[i]->piEmissiveTexture) { apcMeshes[i]->piEmissiveTexture->Release(); apcMeshes[i]->piEmissiveTexture = NULL; } if(apcMeshes[i]->piOpacityTexture) { apcMeshes[i]->piOpacityTexture->Release(); apcMeshes[i]->piOpacityTexture = NULL; } if(apcMeshes[i]->piShininessTexture) { apcMeshes[i]->piShininessTexture->Release(); apcMeshes[i]->piShininessTexture = NULL; } } } }