From abe2d4834eb2784109aec3336697f81d3eb68a6a Mon Sep 17 00:00:00 2001 From: aramis_acg Date: Sun, 15 Feb 2009 20:29:07 +0000 Subject: [PATCH] MD3 - shaders are now processed - multi-part player models are handled correctly Material system - added flags for 'usealpha' or 'ignorealpha' setting of textures LWO - fixed texture assignment bug due to invalid tag list 3DS - improved handling of dummy nodes Viewer: - lines&points are now displayed - improved animation control via slider (still some stuff missing) - skeleton is now displayed - some other minor fixes Validator: - some material issues are warnings now (no errors anymore) git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@349 67173fc5-114c-0410-ac8e-9d2fd5bffc1f --- code/3DSConverter.cpp | 4 +- code/3DSLoader.cpp | 14 +- code/GenVertexNormalsProcess.cpp | 36 +- code/LWOLoader.cpp | 86 +++-- code/LWOLoader.h | 2 +- code/LWOMaterial.cpp | 3 +- code/MD3Loader.cpp | 515 +++++++++++++++++++++-------- code/MD3Loader.h | 47 ++- code/MaterialSystem.cpp | 18 +- code/MaterialSystem.h | 5 +- code/PretransformVertices.cpp | 16 +- code/RemoveRedundantMaterials.cpp | 17 +- code/ValidateDataStructure.cpp | 72 ++-- include/aiConfig.h | 18 + include/aiMaterial.h | 49 ++- include/aiScene.h | 7 +- tools/assimp_view/AssetHelper.h | 6 +- tools/assimp_view/Display.cpp | 412 ++++++++++++----------- tools/assimp_view/Display.h | 15 + tools/assimp_view/Material.cpp | 26 +- tools/assimp_view/MeshRenderer.cpp | 13 +- tools/assimp_view/MessageProc.cpp | 6 +- tools/assimp_view/SceneAnimator.h | 7 + tools/assimp_view/Shaders.cpp | 26 +- tools/assimp_view/assimp_view.aps | Bin 392220 -> 392220 bytes tools/assimp_view/assimp_view.cpp | 64 ++-- tools/assimp_view/assimp_view.rc | 2 +- 27 files changed, 902 insertions(+), 584 deletions(-) diff --git a/code/3DSConverter.cpp b/code/3DSConverter.cpp index 7336b9251..3d5acfdc2 100644 --- a/code/3DSConverter.cpp +++ b/code/3DSConverter.cpp @@ -438,11 +438,11 @@ void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut, iArray.reserve(3); aiMatrix4x4 abs; - if (pcIn->mName == "$$$DUMMY") { + /*if (pcIn->mName == "$$$DUMMY") { // FIX: Append the "real" name of the dummy to the string pcIn->mName = "Dummy." + pcIn->mDummyName; } - else // if (pcIn->mName != "$$$DUMMY") + else*/ // if (pcIn->mName != "$$$DUMMY") { // Find all meshes with the same name as the node for (unsigned int a = 0; a < pcSOut->mNumMeshes;++a) diff --git a/code/3DSLoader.cpp b/code/3DSLoader.cpp index 5f63e8fce..64f3fd8d6 100644 --- a/code/3DSLoader.cpp +++ b/code/3DSLoader.cpp @@ -669,14 +669,16 @@ void Discreet3DSImporter::ParseHierarchyChunk(uint16_t parent) // This is the "real" name of a $$$DUMMY object { - if (mCurrentNode->mName != "$$$DUMMY") { - DefaultLogger::get()->warn("3DS: Skipping dummy object name for non-dummy object"); + const char* sz = (const char*) stream->GetPtr(); + while (stream->GetI1()); + // mCurrentNode->mDummyName = std::string(sz); + + // FIX: if object name is DUMMY, take this one instead + if (mCurrentNode->mName == "$$$DUMMY") { + //DefaultLogger::get()->warn("3DS: Skipping dummy object name for non-dummy object"); + mCurrentNode->mName = std::string(sz); break; } - - const char* sz = (const char*)stream->GetPtr(); - while (stream->GetI1()); - mCurrentNode->mDummyName = std::string(sz); } break; diff --git a/code/GenVertexNormalsProcess.cpp b/code/GenVertexNormalsProcess.cpp index 9231bdae6..6c84d031d 100644 --- a/code/GenVertexNormalsProcess.cpp +++ b/code/GenVertexNormalsProcess.cpp @@ -76,10 +76,9 @@ bool GenVertexNormalsProcess::IsActive( unsigned int pFlags) const // Executes the post processing step on the given imported data. void GenVertexNormalsProcess::SetupProperties(const Importer* pImp) { - // get the current value of the property - this->configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE,175.f); - this->configMaxAngle = std::max(std::min(this->configMaxAngle,175.0f),0.0f); - this->configMaxAngle = AI_DEG_TO_RAD(this->configMaxAngle); + // Get the current value of the AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE property + configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE,175.f); + configMaxAngle = AI_DEG_TO_RAD(std::max(std::min(configMaxAngle,175.0f),0.0f)); } // ------------------------------------------------------------------------------------------------ @@ -98,8 +97,7 @@ void GenVertexNormalsProcess::Execute( aiScene* pScene) bHas = true; } - if (bHas) - { + if (bHas) { DefaultLogger::get()->info("GenVertexNormalsProcess finished. " "Vertex normals have been calculated"); } @@ -154,8 +152,7 @@ bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh, unsigned int SpatialSort _vertexFinder; float posEpsilon; const float epsilon = 1e-5f; - if (shared) - { + if (shared) { std::vector >* avf; shared->GetProperty(AI_SPP_SPATIAL_SORT,avf); if (avf) @@ -165,8 +162,7 @@ bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh, unsigned int posEpsilon = blubb.second; } } - if (!vertexFinder) - { + if (!vertexFinder) { _vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D)); vertexFinder = &_vertexFinder; posEpsilon = ComputePositionEpsilon(pMesh); @@ -174,22 +170,19 @@ bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh, unsigned int std::vector verticesFound; aiVector3D* pcNew = new aiVector3D[pMesh->mNumVertices]; - if (configMaxAngle >= AI_DEG_TO_RAD( 175.f )) - { + if (configMaxAngle >= AI_DEG_TO_RAD( 175.f )) { // There is no angle limit. Thus all vertices with positions close // to each other will receive the same vertex normal. This allows us // to optimize the whole algorithm a little bit ... std::vector abHad(pMesh->mNumVertices,false); - for (unsigned int i = 0; i < pMesh->mNumVertices;++i) - { + for (unsigned int i = 0; i < pMesh->mNumVertices;++i) { if (abHad[i])continue; // Get all vertices that share this one ... vertexFinder->FindPositions( pMesh->mVertices[i], posEpsilon, verticesFound); aiVector3D pcNor; - for (unsigned int a = 0; a < verticesFound.size(); ++a) - { + for (unsigned int a = 0; a < verticesFound.size(); ++a) { const aiVector3D& v = pMesh->mNormals[verticesFound[a]]; if (is_not_qnan(v.x))pcNor += v; } @@ -204,17 +197,16 @@ bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh, unsigned int } } } - else - { + // Slower code path if a smooth angle is set. There are many ways to achieve + // the effect, this one is the most straightforward one. + else { const float fLimit = ::cos(configMaxAngle); - for (unsigned int i = 0; i < pMesh->mNumVertices;++i) - { + for (unsigned int i = 0; i < pMesh->mNumVertices;++i) { // Get all vertices that share this one ... vertexFinder->FindPositions( pMesh->mVertices[i] , posEpsilon, verticesFound); aiVector3D pcNor; - for (unsigned int a = 0; a < verticesFound.size(); ++a) - { + for (unsigned int a = 0; a < verticesFound.size(); ++a) { const aiVector3D& v = pMesh->mNormals[verticesFound[a]]; // check whether the angle between the two normals is not too large diff --git a/code/LWOLoader.cpp b/code/LWOLoader.cpp index 106b34a01..ff72d82a7 100644 --- a/code/LWOLoader.cpp +++ b/code/LWOLoader.cpp @@ -39,7 +39,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -/** @file Implementation of the LWO importer class */ +/** @file LWOLoader.cpp + * @brief Implementation of the LWO importer class + */ #include "AssimpPCH.h" #ifndef ASSIMP_BUILD_NO_LWO_IMPORTER @@ -230,6 +232,7 @@ void LWOImporter::InternReadFile( const std::string& pFile, mSurfaces->push_back(LWO::Surface()); LWO::Surface& surf = mSurfaces->back(); surf.mColor.r = surf.mColor.g = surf.mColor.b = 0.6f; + surf.mName = "LWODefaultSurface"; } idx = iDefaultSurface; } @@ -329,8 +332,7 @@ void LWOImporter::InternReadFile( const std::string& pFile, } // process normals (MODO extension) - if (nrm) - { + if (nrm) { *nrm++ = ((aiVector3D*)&layer.mNormals.rawData[0])[idx]; } @@ -401,7 +403,7 @@ void LWOImporter::InternReadFile( const std::string& pFile, } // copy the meshes to the output structure - if (apcMeshes.size()) // shouldn't occur, just to be sure we don't crash + if (apcMeshes.size()) // shouldn't happen, just to be sure we don't crash { pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes = (unsigned int)apcMeshes.size() ]; ::memcpy(pScene->mMeshes,&apcMeshes[0],pScene->mNumMeshes*sizeof(void*)); @@ -415,24 +417,23 @@ void LWOImporter::InternReadFile( const std::string& pFile, void LWOImporter::ComputeNormals(aiMesh* mesh, const std::vector& smoothingGroups, const LWO::Surface& surface) { - // allocate output storage + // Allocate output storage mesh->mNormals = new aiVector3D[mesh->mNumVertices]; // First generate per-face normals aiVector3D* out; std::vector faceNormals; + // ... in some cases that's already enough if (!surface.mMaximumSmoothAngle) out = mesh->mNormals; - else - { + else { faceNormals.resize(mesh->mNumVertices); out = &faceNormals[0]; } aiFace* begin = mesh->mFaces, *const end = mesh->mFaces+mesh->mNumFaces; - for (; begin != end; ++begin) - { + for (; begin != end; ++begin) { aiFace& face = *begin; // LWO doc: "the normal is defined as the cross product of the first and last edges" @@ -447,7 +448,7 @@ void LWOImporter::ComputeNormals(aiMesh* mesh, const std::vector& if (!surface.mMaximumSmoothAngle)return; const float posEpsilon = ComputePositionEpsilon(mesh); - // now generate the spatial sort tree + // Now generate the spatial sort tree SGSpatialSort sSort; std::vector::const_iterator it = smoothingGroups.begin(); for( begin = mesh->mFaces; begin != end; ++begin, ++it) @@ -459,70 +460,57 @@ void LWOImporter::ComputeNormals(aiMesh* mesh, const std::vector& sSort.Add(mesh->mVertices[tt],tt,*it); } } - // sort everything - this takes O(nlogn) time + // Sort everything - this takes O(nlogn) time sSort.Prepare(); std::vector poResult; poResult.reserve(20); - // generate vertex normals. We have O(logn) for the binary lookup, which we need + // Generate vertex normals. We have O(logn) for the binary lookup, which we need // for n elements, thus the EXPECTED complexity is O(nlogn) - if (surface.mMaximumSmoothAngle < 3.f && !configSpeedFlag) - { + if (surface.mMaximumSmoothAngle < 3.f && !configSpeedFlag) { const float fLimit = cos(surface.mMaximumSmoothAngle); - for( begin = mesh->mFaces, it = smoothingGroups.begin(); begin != end; ++begin, ++it) - { - register unsigned int sg = *it; - - aiFace& face = *begin; + for( begin = mesh->mFaces, it = smoothingGroups.begin(); begin != end; ++begin, ++it) { + const aiFace& face = *begin; unsigned int* beginIdx = face.mIndices, *const endIdx = face.mIndices+face.mNumIndices; for (; beginIdx != endIdx; ++beginIdx) { register unsigned int idx = *beginIdx; - sSort.FindPositions(mesh->mVertices[idx],sg,posEpsilon,poResult,true); - + sSort.FindPositions(mesh->mVertices[idx],*it,posEpsilon,poResult,true); std::vector::const_iterator a, end = poResult.end(); aiVector3D vNormals; - for (a = poResult.begin();a != end;++a) - { + for (a = poResult.begin();a != end;++a) { const aiVector3D& v = faceNormals[*a]; - if (v * faceNormals[idx] < fLimit)continue; + if (v * faceNormals[idx] < fLimit) + continue; vNormals += v; } - vNormals.Normalize(); - mesh->mNormals[idx] = vNormals; + mesh->mNormals[idx] = vNormals.Normalize(); } } } - else // faster code path in case there is no smooth angle - { + // faster code path in case there is no smooth angle + else { std::vector vertexDone(mesh->mNumVertices,false); - - for( begin = mesh->mFaces, it = smoothingGroups.begin(); begin != end; ++begin, ++it) - { - register unsigned int sg = *it; - - aiFace& face = *begin; + for( begin = mesh->mFaces, it = smoothingGroups.begin(); begin != end; ++begin, ++it) { + const aiFace& face = *begin; unsigned int* beginIdx = face.mIndices, *const endIdx = face.mIndices+face.mNumIndices; for (; beginIdx != endIdx; ++beginIdx) { register unsigned int idx = *beginIdx; - - if (vertexDone[idx])continue; - sSort.FindPositions(mesh->mVertices[idx],sg,posEpsilon,poResult,true); - + if (vertexDone[idx]) + continue; + sSort.FindPositions(mesh->mVertices[idx],*it,posEpsilon,poResult,true); std::vector::const_iterator a, end = poResult.end(); aiVector3D vNormals; - for (a = poResult.begin();a != end;++a) - { + for (a = poResult.begin();a != end;++a) { const aiVector3D& v = faceNormals[*a]; vNormals += v; } vNormals.Normalize(); - for (a = poResult.begin();a != end;++a) - { + for (a = poResult.begin();a != end;++a) { mesh->mNormals[*a] = vNormals; vertexDone[*a] = true; } @@ -617,9 +605,9 @@ void LWOImporter::ResolveTags() mMapping->resize(mTags->size(),0xffffffff); for (unsigned int a = 0; a < mTags->size();++a) { + const std::string& c = (*mTags)[a]; for (unsigned int i = 0; i < mSurfaces->size();++i) { - const std::string& c = (*mTags)[a]; const std::string& d = (*mSurfaces)[i].mName; if (!ASSIMP_stricmp(c,d)) { @@ -688,9 +676,11 @@ void LWOImporter::LoadLWOTags(unsigned int size) { if (!(*szCur)) { - const unsigned int len = (unsigned int)(szCur-szLast); - mTags->push_back(std::string(szLast,len)); - szCur += len & 1; + const size_t len = (size_t)(szCur-szLast); + // FIX: skip empty-sized tags + if (len) + mTags->push_back(std::string(szLast,len)); + szCur += (len&0x1 ? 1 : 2); szLast = szCur; } szCur++; @@ -881,7 +871,7 @@ inline void CreateNewEntry(std::vector< T >& list, unsigned int srcIdx) } // ------------------------------------------------------------------------------------------------ -void LWOImporter::DoRecursiveVMAPAssignment(VMapEntry* base, unsigned int numRead, +inline void LWOImporter::DoRecursiveVMAPAssignment(VMapEntry* base, unsigned int numRead, unsigned int idx, float* data) { ai_assert(NULL != data); @@ -897,7 +887,7 @@ void LWOImporter::DoRecursiveVMAPAssignment(VMapEntry* base, unsigned int numRea } // ------------------------------------------------------------------------------------------------ -void AddToSingleLinkedList(ReferrerList& refList, unsigned int srcIdx, unsigned int destIdx) +inline void AddToSingleLinkedList(ReferrerList& refList, unsigned int srcIdx, unsigned int destIdx) { if(0xffffffff == refList[srcIdx]) { diff --git a/code/LWOLoader.h b/code/LWOLoader.h index 03a1d2e4c..cf05ed9f6 100644 --- a/code/LWOLoader.h +++ b/code/LWOLoader.h @@ -455,7 +455,7 @@ inline void LWOImporter::GetS0(std::string& out,unsigned int max) } ++mFileBuffer; } - unsigned int len = (unsigned int) ((const char*)mFileBuffer-sz); + size_t len = (size_t) ((const char*)mFileBuffer-sz); out = std::string(sz,len); mFileBuffer += (len&0x1 ? 1 : 2); } diff --git a/code/LWOMaterial.cpp b/code/LWOMaterial.cpp index 1f0af20d2..8afdeb8a9 100644 --- a/code/LWOMaterial.cpp +++ b/code/LWOMaterial.cpp @@ -663,8 +663,7 @@ void LWOImporter::LoadLWO2Surface(unsigned int size) for (SurfaceList::iterator it = mSurfaces->begin(), end = mSurfaces->end()-1; it != end; ++it) { - if ((*it).mName == derived) - { + if ((*it).mName == derived) { // we have it ... surf = *it; derived.clear(); diff --git a/code/MD3Loader.cpp b/code/MD3Loader.cpp index 20b813e4f..ed17302d8 100644 --- a/code/MD3Loader.cpp +++ b/code/MD3Loader.cpp @@ -60,13 +60,36 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace Assimp; +// ------------------------------------------------------------------------------------------------ +// Convert a Q3 shader blend function to the appropriate enum value +Q3Shader::BlendFunc StringToBlendFunc(const std::string& m) +{ + if (m == "GL_ONE") { + return Q3Shader::BLEND_GL_ONE; + } + if (m == "GL_ZERO") { + return Q3Shader::BLEND_GL_ZERO; + } + if (m == "GL_SRC_ALPHA") { + return Q3Shader::BLEND_GL_SRC_ALPHA; + } + if (m == "GL_ONE_MINUS_SRC_ALPHA") { + return Q3Shader::BLEND_GL_ONE_MINUS_SRC_ALPHA; + } + if (m == "GL_ONE_MINUS_DST_COLOR") { + return Q3Shader::BLEND_GL_ONE_MINUS_DST_COLOR; + } + DefaultLogger::get()->error("Q3Shader: Unknown blend function: " + m); + return Q3Shader::BLEND_NONE; +} + // ------------------------------------------------------------------------------------------------ // Load a Quake 3 shader -void Q3Shader::LoadShader(ShaderData& fill, const std::string& pFile,IOSystem* io) +bool Q3Shader::LoadShader(ShaderData& fill, const std::string& pFile,IOSystem* io) { boost::scoped_ptr file( io->Open( pFile, "rt")); if (!file.get()) - return; // if we can't access the file, don't worry and return + return false; // if we can't access the file, don't worry and return DefaultLogger::get()->info("Loading Quake3 shader file " + pFile); @@ -84,59 +107,93 @@ void Q3Shader::LoadShader(ShaderData& fill, const std::string& pFile,IOSystem* i Q3Shader::ShaderMapBlock* curMap = NULL; // read line per line - for (;;SkipLine(&buff)) { + for (;SkipSpacesAndLineEnd(&buff);SkipLine(&buff)) { - if(!SkipSpacesAndLineEnd(&buff)) - break; - if (*buff == '{') { + ++buff; + // append to last section, if any if (!curData) { DefaultLogger::get()->error("Q3Shader: Unexpected shader section token \'{\'"); - return; + return true; // still no failure, the file is there } - // read this map section - for (;;SkipLine(&buff)) { - if(!SkipSpacesAndLineEnd(&buff)) - break; - + // read this data section + for (;SkipSpacesAndLineEnd(&buff);SkipLine(&buff)) { if (*buff == '{') { + ++buff; // add new map section curData->maps.push_back(Q3Shader::ShaderMapBlock()); curMap = &curData->maps.back(); + for (;SkipSpacesAndLineEnd(&buff);SkipLine(&buff)) { + // 'map' - Specifies texture file name + if (TokenMatchI(buff,"map",3) || TokenMatchI(buff,"clampmap",8)) { + curMap->name = GetNextToken(buff); + } + // 'blendfunc' - Alpha blending mode + else if (TokenMatchI(buff,"blendfunc",9)) { + const std::string blend_src = GetNextToken(buff); + if (blend_src == "add") { + curMap->blend_src = Q3Shader::BLEND_GL_ONE; + curMap->blend_dest = Q3Shader::BLEND_GL_ONE; + } + else if (blend_src == "filter") { + curMap->blend_src = Q3Shader::BLEND_GL_DST_COLOR; + curMap->blend_dest = Q3Shader::BLEND_GL_ZERO; + } + else if (blend_src == "blend") { + curMap->blend_src = Q3Shader::BLEND_GL_SRC_ALPHA; + curMap->blend_dest = Q3Shader::BLEND_GL_ONE_MINUS_SRC_ALPHA; + } + else { + curMap->blend_src = StringToBlendFunc(blend_src); + curMap->blend_dest = StringToBlendFunc(GetNextToken(buff)); + } + } + // 'alphafunc' - Alpha testing mode + else if (TokenMatchI(buff,"alphafunc",9)) { + const std::string at = GetNextToken(buff); + if (at == "GT0") { + curMap->alpha_test = Q3Shader::AT_GT0; + } + else if (at == "LT128") { + curMap->alpha_test = Q3Shader::AT_LT128; + } + else if (at == "GE128") { + curMap->alpha_test = Q3Shader::AT_GE128; + } + } + else if (*buff == '}') { + ++buff; + // close this map section + curMap = NULL; + break; + } + } + } else if (*buff == '}') { - // close this map section - if (curMap) - curMap = NULL; - else { - curData = NULL; - break; - } + ++buff; + curData = NULL; + break; } - // 'map' - Specifies texture file name - else if (TokenMatchI(buff,"map",3) || TokenMatchI(buff,"clampmap",8)) { - curMap->name = GetNextToken(buff); - } - // 'blendfunc' - Alpha blending mode - else if (TokenMatchI(buff,"blendfunc",9)) { - // fixme - } - } - } - // 'cull' specifies culling behaviour for the model - else if (TokenMatch(buff,"cull",4)) { - SkipSpaces(&buff); - if (!ASSIMP_strincmp(buff,"back",4)) { - curData->cull = Q3Shader::CULL_CCW; + // 'cull' specifies culling behaviour for the model + else if (TokenMatchI(buff,"cull",4)) { + SkipSpaces(&buff); + if (!ASSIMP_strincmp(buff,"back",4)) { + curData->cull = Q3Shader::CULL_CCW; + } + else if (!ASSIMP_strincmp(buff,"front",5)) { + curData->cull = Q3Shader::CULL_CW; + } + else if (!ASSIMP_strincmp(buff,"none",4) || !ASSIMP_strincmp(buff,"disable",7)) { + curData->cull = Q3Shader::CULL_NONE; + } + else DefaultLogger::get()->error("Q3Shader: Unrecognized cull mode"); + } } - else if (!ASSIMP_strincmp(buff,"front",5)) { - curData->cull = Q3Shader::CULL_CW; - } - //else curData->cull = Q3Shader::CULL_NONE; } else { @@ -148,15 +205,16 @@ void Q3Shader::LoadShader(ShaderData& fill, const std::string& pFile,IOSystem* i curData->name = GetNextToken(buff); } } + return true; } // ------------------------------------------------------------------------------------------------ // Load a Quake 3 skin -void Q3Shader::LoadSkin(SkinData& fill, const std::string& pFile,IOSystem* io) +bool Q3Shader::LoadSkin(SkinData& fill, const std::string& pFile,IOSystem* io) { boost::scoped_ptr file( io->Open( pFile, "rt")); if (!file.get()) - return; // if we can't access the file, don't worry and return + return false; // if we can't access the file, don't worry and return DefaultLogger::get()->info("Loading Quake3 skin file " + pFile); @@ -177,7 +235,7 @@ void Q3Shader::LoadSkin(SkinData& fill, const std::string& pFile,IOSystem* io) std::string ss = GetNextToken(buff); // ignore tokens starting with tag_ - if (!::strncmp(&ss[0],"_tag",std::min((size_t)4, ss.length()))) + if (!::strncmp(&ss[0],"tag_",std::min((size_t)4, ss.length()))) continue; fill.textures.push_back(SkinData::TextureEntry()); @@ -186,6 +244,90 @@ void Q3Shader::LoadSkin(SkinData& fill, const std::string& pFile,IOSystem* io) s.first = ss; s.second = GetNextToken(buff); } + return true; +} + +// ------------------------------------------------------------------------------------------------ +// Convert Q3Shader to material +void Q3Shader::ConvertShaderToMaterial(MaterialHelper* out, const ShaderDataBlock& shader) +{ + ai_assert(NULL != out); + + /* IMPORTANT: This is not a real conversion. Actually we're just guessing and + * hacking around to build an aiMaterial that looks nearly equal to the + * original Quake 3 shader. We're missing some important features like + * animatable material properties in our material system, but at least + * multiple textures should be handled correctly. + */ + + // Two-sided material? + if (shader.cull == Q3Shader::CULL_NONE) { + const int twosided = 1; + out->AddProperty(&twosided,1,AI_MATKEY_TWOSIDED); + } + + unsigned int cur_emissive = 0, cur_diffuse = 0, cur_lm =0; + + // Iterate through all textures + for (std::list< Q3Shader::ShaderMapBlock >::const_iterator it = shader.maps.begin(); it != shader.maps.end();++it) { + + // CONVERSION BEHAVIOUR: + // + // + // If the texture is additive + // - if it is the first texture, assume additive blending for the whole material + // - otherwise register it as emissive texture. + // + // If the texture is using standard blend (or if the blend mode is unknown) + // - if first texture: assume default blending for material + // - in any case: set it as diffuse texture + // + // If the texture is using 'filter' blending + // - take as lightmap + // + // Textures with alpha funcs + // - aiTextureFlags_UseAlpha is set (otherwise aiTextureFlags_NoAlpha is explicitly set) + aiString s((*it).name); + aiTextureType type; unsigned int index; + + if ((*it).blend_src == Q3Shader::BLEND_GL_ONE && (*it).blend_dest == Q3Shader::BLEND_GL_ONE) { + if (it == shader.maps.begin()) { + const int additive = aiBlendMode_Additive; + out->AddProperty(&additive,1,AI_MATKEY_BLEND_FUNC); + + index = cur_diffuse++; + type = aiTextureType_DIFFUSE; + } + else { + index = cur_emissive++; + type = aiTextureType_EMISSIVE; + } + } + else if ((*it).blend_src == Q3Shader::BLEND_GL_DST_COLOR && Q3Shader::BLEND_GL_ZERO) { + index = cur_lm++; + type = aiTextureType_LIGHTMAP; + } + else { + const int blend = aiBlendMode_Default; + out->AddProperty(&blend,1,AI_MATKEY_BLEND_FUNC); + + index = cur_diffuse++; + type = aiTextureType_DIFFUSE; + } + + // setup texture + out->AddProperty(&s,AI_MATKEY_TEXTURE(type,index)); + + // setup texture flags + const int use_alpha = ((*it).alpha_test != Q3Shader::AT_NONE ? aiTextureFlags_UseAlpha : aiTextureFlags_IgnoreAlpha); + out->AddProperty(&use_alpha,1,AI_MATKEY_TEXFLAGS(type,index)); + } + // If at least one emissive texture was set, set the emissive base color to 1 to ensure + // the texture is actually displayed. + if (0 != cur_emissive) { + aiColor3D one(1.f,1.f,1.f); + out->AddProperty(&one,1,AI_MATKEY_COLOR_EMISSIVE); + } } // ------------------------------------------------------------------------------------------------ @@ -291,11 +433,14 @@ void MD3Importer::SetupProperties(const Importer* pImp) // AI_CONFIG_IMPORT_MD3_SKIN_NAME configSkinFile = (pImp->GetPropertyString(AI_CONFIG_IMPORT_MD3_SKIN_NAME,"default")); + + // AI_CONFIG_IMPORT_MD3_SHADER_SRC + configShaderFile = (pImp->GetPropertyString(AI_CONFIG_IMPORT_MD3_SHADER_SRC,"")); } // ------------------------------------------------------------------------------------------------ // Try to read the skin for a MD3 file -void MD3Importer::ReadSkin(Q3Shader::SkinData& fill) +void MD3Importer::ReadSkin(Q3Shader::SkinData& fill) const { // skip any postfixes (e.g. lower_1.md3) std::string::size_type s = filename.find_last_of('_'); @@ -308,6 +453,39 @@ void MD3Importer::ReadSkin(Q3Shader::SkinData& fill) Q3Shader::LoadSkin(fill,skin_file,mIOHandler); } +// ------------------------------------------------------------------------------------------------ +// Try to read the shader for a MD3 file +void MD3Importer::ReadShader(Q3Shader::ShaderData& fill) const +{ + // Determine Q3 model name from given path + std::string::size_type s = path.find_last_of('\\',path.length()-2); + if (s == std::string::npos) + s = path.find_last_of('/',path.length()-2); + + const std::string model_file = path.substr(s+1,path.length()-(s+2)); + + // If no specific dir or file is given, use our default search behaviour + if (!configShaderFile.length()) { + if(!Q3Shader::LoadShader(fill,path + "..\\..\\..\\scripts\\" + model_file + ".shader",mIOHandler)) { + Q3Shader::LoadShader(fill,path + "..\\..\\..\\scripts\\" + filename + ".shader",mIOHandler); + } + } + else { + // If the given string specifies a file, load this file. + // Otherwise it's a directory. + std::string::size_type st = configShaderFile.find_last_of('.'); + if (st == std::string::npos) { + + if(!Q3Shader::LoadShader(fill,configShaderFile + model_file + ".shader",mIOHandler)) { + Q3Shader::LoadShader(fill,configShaderFile + filename + ".shader",mIOHandler); + } + } + else { + Q3Shader::LoadShader(fill,configShaderFile,mIOHandler); + } + } +} + // ------------------------------------------------------------------------------------------------ // Read a multi-part Q3 player model bool MD3Importer::ReadMultipartFile() @@ -416,6 +594,45 @@ error_cleanup: return false; } +// ------------------------------------------------------------------------------------------------ +// Convert a MD3 path to a proper value +void MD3Importer::ConvertPath(const char* texture_name, const char* header_name, std::string& out) const +{ + // If the MD3's internal path itself and the given path are using + // the same directory, remove it completely to get right output paths. + const char* end1 = ::strrchr(header_name,'\\'); + if (!end1)end1 = ::strrchr(header_name,'/'); + + const char* end2 = ::strrchr(texture_name,'\\'); + if (!end2)end2 = ::strrchr(texture_name,'/'); + + // HACK: If the paths starts with "models", ignore the + // next two hierarchy levels, it specifies just the model name. + // Ignored by Q3, it might be not equal to the real model location. + if (end2) { + + size_t len2; + const size_t len1 = (size_t)(end1 - header_name); + if (!ASSIMP_strincmp(texture_name,"models",6) && (texture_name[6] == '/' || texture_name[6] == '\\')) { + len2 = 6; // ignore the seventh - could be slash or backslash + + if (!header_name[0]) { + // Use the file name only + out = end2+1; + return; + } + } + else len2 = std::min (len1, (size_t)(end2 - texture_name )); + if (!ASSIMP_strincmp(texture_name,header_name,len2)) { + // Use the file name only + out = end2+1; + return; + } + } + // Use the full path + out = texture_name; +} + // ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. void MD3Importer::InternReadFile( const std::string& pFile, @@ -504,12 +721,27 @@ void MD3Importer::InternReadFile( const std::string& pFile, Q3Shader::SkinData skins; ReadSkin(skins); + // And check whether we can locate a shader file for this model + Q3Shader::ShaderData shaders; + ReadShader(shaders); + + // Adjust all texture paths in the shader + const char* header_name = pcHeader->NAME; + if (shaders.blocks.size()) { + for (std::list< Q3Shader::ShaderDataBlock >::iterator dit = shaders.blocks.begin(); dit != shaders.blocks.end(); ++dit) { + ConvertPath((*dit).name.c_str(),header_name,(*dit).name); + + for (std::list< Q3Shader::ShaderMapBlock >::iterator mit = (*dit).maps.begin(); mit != (*dit).maps.end(); ++mit) { + ConvertPath((*mit).name.c_str(),header_name,(*mit).name); + } + } + } + // Read all surfaces from the file unsigned int iNum = pcHeader->NUM_SURFACES; unsigned int iNumMaterials = 0; unsigned int iDefaultMatIndex = 0xFFFFFFFF; - while (iNum-- > 0) - { + while (iNum-- > 0) { // Ensure correct endianess #ifdef AI_BUILD_BIG_ENDIAN @@ -555,7 +787,96 @@ void MD3Importer::InternReadFile( const std::string& pFile, continue; } - // Ensure correct endianess + // Allocate output mesh + pScene->mMeshes[iNum] = new aiMesh(); + aiMesh* pcMesh = pScene->mMeshes[iNum]; + + std::string _texture_name; + const char* texture_name = NULL; + + // Check whether we have a texture record for this surface in the .skin file + std::list< Q3Shader::SkinData::TextureEntry >::iterator it = std::find( + skins.textures.begin(), skins.textures.end(), pcSurfaces->NAME ); + + if (it != skins.textures.end()) { + texture_name = &*( _texture_name = (*it).second).begin(); + DefaultLogger::get()->debug("MD3: Assigning skin texture " + (*it).second + " to surface " + pcSurfaces->NAME); + (*it).resolved = true; // mark entry as resolved + } + + // Get the first shader (= texture?) assigned to the surface + if (!texture_name && pcSurfaces->NUM_SHADER) { + texture_name = pcShaders->NAME; + } + + std::string convertedPath; + if (texture_name) { + ConvertPath(texture_name,header_name,convertedPath); + } + + const Q3Shader::ShaderDataBlock* shader = NULL; + + // Now search the current shader for a record with this name ( + // excluding texture file extension) + if (shaders.blocks.size()) { + + std::string::size_type s = convertedPath.find_last_of('.'); + if (s == std::string::npos) + s = convertedPath.length(); + + const std::string without_ext = convertedPath.substr(0,s); + std::list< Q3Shader::ShaderDataBlock >::const_iterator dit = std::find(shaders.blocks.begin(),shaders.blocks.end(),without_ext); + if (dit != shaders.blocks.end()) { + // Hurra, wir haben einen. Tolle Sache. + shader = &*dit; + DefaultLogger::get()->info("Found shader record for " +without_ext ); + } + else DefaultLogger::get()->warn("Unable to find shader record for " +without_ext ); + } + + MaterialHelper* pcHelper = new MaterialHelper(); + + const int iMode = (int)aiShadingMode_Gouraud; + pcHelper->AddProperty(&iMode, 1, AI_MATKEY_SHADING_MODEL); + + // Add a small ambient color value - Quake 3 seems to have one + aiColor3D clr; + clr.b = clr.g = clr.r = 0.05f; + pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_AMBIENT); + + clr.b = clr.g = clr.r = 1.0f; + pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_DIFFUSE); + pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_SPECULAR); + + // use surface name + skin_name as material name + aiString name; + name.Set("MD3_[" + configSkinFile + "][" + pcSurfaces->NAME + "]"); + pcHelper->AddProperty(&name,AI_MATKEY_NAME); + + if (!shader) { + // Setup dummy texture file name to ensure UV coordinates are kept during postprocessing + aiString szString; + if (convertedPath.length()) { + szString.Set(convertedPath); + } + else { + DefaultLogger::get()->warn("Texture file name has zero length. Using default name"); + szString.Set("dummy_texture.bmp"); + } + pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0)); + + // prevent transparency by default + int no_alpha = aiTextureFlags_IgnoreAlpha; + pcHelper->AddProperty(&no_alpha,1,AI_MATKEY_TEXFLAGS_DIFFUSE(0)); + } + else { + Q3Shader::ConvertShaderToMaterial(pcHelper,*shader); + } + + pScene->mMaterials[iNumMaterials] = (aiMaterial*)pcHelper; + pcMesh->mMaterialIndex = iNumMaterials++; + + // Ensure correct endianess #ifdef AI_BUILD_BIG_ENDIAN for (uint32_t i = 0; i < pcSurfaces->NUM_VERTICES;++i) @@ -577,9 +898,7 @@ void MD3Importer::InternReadFile( const std::string& pFile, #endif - // Allocate the output mesh - pScene->mMeshes[iNum] = new aiMesh(); - aiMesh* pcMesh = pScene->mMeshes[iNum]; + // Fill mesh information pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; pcMesh->mNumVertices = pcSurfaces->NUM_TRIANGLES*3; @@ -600,6 +919,8 @@ void MD3Importer::InternReadFile( const std::string& pFile, unsigned int iTemp = iCurrent; for (unsigned int c = 0; c < 3;++c,++iCurrent) { + pcMesh->mFaces[i].mIndices[c] = iCurrent; + // Read vertices pcMesh->mVertices[iCurrent].x = pcVertices[ pcTriangles->INDEXES[c]].X*AI_MD3_XYZ_SCALE; pcMesh->mVertices[iCurrent].y = pcVertices[ pcTriangles->INDEXES[c]].Y*AI_MD3_XYZ_SCALE; @@ -613,100 +934,14 @@ void MD3Importer::InternReadFile( const std::string& pFile, pcMesh->mTextureCoords[0][iCurrent].x = pcUVs[ pcTriangles->INDEXES[c]].U; pcMesh->mTextureCoords[0][iCurrent].y = 1.0f-pcUVs[ pcTriangles->INDEXES[c]].V; } - // FIX: flip the face ordering for use with OpenGL - pcMesh->mFaces[i].mIndices[0] = iTemp+2; - pcMesh->mFaces[i].mIndices[1] = iTemp+1; - pcMesh->mFaces[i].mIndices[2] = iTemp+0; + // Flip face order if necessary + if (!shader || shader->cull == Q3Shader::CULL_CCW) { + pcMesh->mFaces[i].mIndices[0] = iTemp+2; + pcMesh->mFaces[i].mIndices[1] = iTemp+1; + pcMesh->mFaces[i].mIndices[2] = iTemp+0; + } pcTriangles++; } - - std::string _texture_name; - const char* texture_name = NULL, *header_name = pcHeader->NAME; - - // Check whether we have a texture record for this surface in the .skin file - std::list< Q3Shader::SkinData::TextureEntry >::iterator it = std::find( - skins.textures.begin(), skins.textures.end(), pcSurfaces->NAME ); - - if (it != skins.textures.end()) { - texture_name = &*( _texture_name = (*it).second).begin(); - DefaultLogger::get()->debug("MD3: Assigning skin texture " + (*it).second + " to surface " + pcSurfaces->NAME); - (*it).resolved = true; // mark entry as resolved - } - - // Get the first shader (= texture?) assigned to the surface - if (!texture_name && pcSurfaces->NUM_SHADER) { - texture_name = pcShaders->NAME; - } - - const char* end2 = NULL; - if (texture_name) { - - // If the MD3's internal path itself and the given path are using - // the same directory, remove it completely to get right output paths. - const char* end1 = ::strrchr(header_name,'\\'); - if (!end1)end1 = ::strrchr(header_name,'/'); - - end2 = ::strrchr(texture_name,'\\'); - if (!end2)end2 = ::strrchr(texture_name,'/'); - - // HACK: If the paths starts with "models/players", ignore the - // next hierarchy level, it specifies just the model name. - // Ignored by Q3, it might be not equal to the real model location. - if (end1 && end2) { - - size_t len2; - const size_t len1 = (size_t)(end1 - header_name); - if (!ASSIMP_strincmp(header_name,"models/players/",15)) { - len2 = 15; - } - else len2 = std::min (len1, (size_t)(end2 - texture_name )); - - if (!ASSIMP_strincmp(texture_name,header_name,len2)) { - // Use the file name only - end2++; - } - else { - // Use the full path - end2 = (const char*)texture_name; - } - } - } - - MaterialHelper* pcHelper = new MaterialHelper(); - - // Setup dummy texture file name to ensure UV coordinates are kept during postprocessing - aiString szString; - if (end2 && end2[0]) { - const size_t iLen = ::strlen(end2); - ::memcpy(szString.data,end2,iLen); - szString.data[iLen] = '\0'; - szString.length = iLen; - } - else { - DefaultLogger::get()->warn("Texture file name has zero length. Using default name"); - szString.Set("dummy_texture.bmp"); - } - pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0)); - - const int iMode = (int)aiShadingMode_Gouraud; - pcHelper->AddProperty(&iMode, 1, AI_MATKEY_SHADING_MODEL); - - // Add a small ambient color value - Quake 3 seems to have one - aiColor3D clr; - clr.b = clr.g = clr.r = 0.05f; - pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_AMBIENT); - - clr.b = clr.g = clr.r = 1.0f; - pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_DIFFUSE); - pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_SPECULAR); - - // use surface name + skin_name as material name - aiString name; - name.Set("MD3_[" + configSkinFile + "][" + pcSurfaces->NAME + "]"); - pcHelper->AddProperty(&name,AI_MATKEY_NAME); - - pScene->mMaterials[iNumMaterials] = (aiMaterial*)pcHelper; - pcMesh->mMaterialIndex = iNumMaterials++; // Go to the next surface pcSurfaces = (BE_NCONST MD3::Surface*)(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_END); diff --git a/code/MD3Loader.h b/code/MD3Loader.h index 3aaca52e8..57cd14f4d 100644 --- a/code/MD3Loader.h +++ b/code/MD3Loader.h @@ -131,6 +131,12 @@ struct ShaderMapBlock //! Blend and alpha test settings for texture BlendFunc blend_src,blend_dest; AlphaTestFunc alpha_test; + + + //! For std::find() + bool operator== (const std::string& o) const { + return !ASSIMP_stricmp(o,name); + } }; // --------------------------------------------------------------------------- @@ -150,6 +156,12 @@ struct ShaderDataBlock //! Maps defined in the shader std::list maps; + + + //! For std::find() + bool operator== (const std::string& o) const { + return !ASSIMP_stricmp(o,name); + } }; // --------------------------------------------------------------------------- @@ -168,8 +180,18 @@ struct ShaderData * @param fill Receives output data * @param file File to be read. * @param io IOSystem to be used for reading + * @return false if file is not accessible */ -void LoadShader(ShaderData& fill, const std::string& file,IOSystem* io); +bool LoadShader(ShaderData& fill, const std::string& file,IOSystem* io); + + +// --------------------------------------------------------------------------- +/** @brief Convert a Q3Shader to an aiMaterial + * + * @param[out] out Material structure to be filled. + * @param[in] shader Input shader + */ +void ConvertShaderToMaterial(MaterialHelper* out, const ShaderDataBlock& shader); // --------------------------------------------------------------------------- /** @brief Load a skin file @@ -178,8 +200,9 @@ void LoadShader(ShaderData& fill, const std::string& file,IOSystem* io); * @param fill Receives output data * @param file File to be read. * @param io IOSystem to be used for reading + * @return false if file is not accessible */ -void LoadSkin(SkinData& fill, const std::string& file,IOSystem* io); +bool LoadSkin(SkinData& fill, const std::string& file,IOSystem* io); } // ! namespace Q3SHader @@ -243,7 +266,22 @@ protected: /** Try to read the skin for a MD3 file * @param fill Receives output information */ - void ReadSkin(Q3Shader::SkinData& fill); + void ReadSkin(Q3Shader::SkinData& fill) const; + + // ------------------------------------------------------------------- + /** Try to read the shader for a MD3 file + * @param fill Receives output information + */ + void ReadShader(Q3Shader::ShaderData& fill) const; + + // ------------------------------------------------------------------- + /** Convert a texture path in a MD3 file to a proper value + * @param[in] texture_name Path to be converted + * @param[in] header_path Base path specified in MD3 header + * @param[out] out Receives the converted output string + */ + void ConvertPath(const char* texture_name, const char* header_path, + std::string& out) const; protected: @@ -256,6 +294,9 @@ protected: /** Configuration option: name of skin file to be read */ std::string configSkinFile; + /** Configuration option: name or path of shader */ + std::string configShaderFile; + /** Header of the MD3 file */ BE_NCONST MD3::Header* pcHeader; diff --git a/code/MaterialSystem.cpp b/code/MaterialSystem.cpp index f1a16ee06..9c0c920f8 100644 --- a/code/MaterialSystem.cpp +++ b/code/MaterialSystem.cpp @@ -288,24 +288,16 @@ uint32_t MaterialHelper::ComputeHash(bool includeMatName /*= false*/) { aiMaterialProperty* prop; - // If specified, exclude the material name from the hash - if ((prop = mProperties[i]) && (includeMatName || ::strcmp(prop->mKey.data,"$mat.name"))) + // Exclude all properties whose first character is '?' from the hash + // See doc for aiMaterialProperty. + if ((prop = mProperties[i]) && (includeMatName || prop->mKey.data[0] != '?')) { hash = SuperFastHash(prop->mKey.data,(unsigned int)prop->mKey.length,hash); hash = SuperFastHash(prop->mData,prop->mDataLength,hash); // Combine the semantic and the index with the hash - // We print them to a string to make sure the quality - // of the hashing state isn't affected (our hashing - // procedure was originally intended for plaintest). - char buff[32]; - unsigned int len; - - len = ASSIMP_itoa10(buff,prop->mSemantic); - hash = SuperFastHash(buff,len-1,hash); - - len = ASSIMP_itoa10(buff,prop->mIndex); - hash = SuperFastHash(buff,len-1,hash); + hash = SuperFastHash((const char*)&prop->mSemantic,sizeof(unsigned int),hash); + hash = SuperFastHash((const char*)&prop->mIndex,sizeof(unsigned int),hash); } } return hash; diff --git a/code/MaterialSystem.h b/code/MaterialSystem.h index 7aa06763e..78ca20239 100644 --- a/code/MaterialSystem.h +++ b/code/MaterialSystem.h @@ -131,8 +131,9 @@ public: * proprty and call this method again, the resulting hash value will be * different. * - * @param includeMatName Set to 'true' to take the #AI_MATKEY_NAME property - * into account. The default value is false. + * @param includeMatName Set to 'true' to take all properties with + * '?' as initial character in their name into account. + * Currently #AI_MATKEY_NAME is the only example. * @return Unique hash */ uint32_t ComputeHash(bool includeMatName = false); diff --git a/code/PretransformVertices.cpp b/code/PretransformVertices.cpp index 8764dec04..181aaa13d 100644 --- a/code/PretransformVertices.cpp +++ b/code/PretransformVertices.cpp @@ -201,7 +201,7 @@ void CollectData( aiScene* pcScene, aiNode* pcNode, unsigned int iMat, // aiFace destructor ... pcMesh->mFaces[planck].mIndices = NULL; - // FIX: update the mPrimitiveTypes member of the mesh + // Update the mPrimitiveTypes member of the mesh switch (pcMesh->mFaces[planck].mNumIndices) { case 0x1: @@ -249,13 +249,11 @@ void GetVFormatList( aiScene* pcScene, unsigned int iMat, // Compute the absolute transformation matrices of each node void ComputeAbsoluteTransform( aiNode* pcNode ) { - if (pcNode->mParent) - { + if (pcNode->mParent) { pcNode->mTransformation = pcNode->mParent->mTransformation*pcNode->mTransformation; } - for (unsigned int i = 0;i < pcNode->mNumChildren;++i) - { + for (unsigned int i = 0;i < pcNode->mNumChildren;++i) { ComputeAbsoluteTransform(pcNode->mChildren[i]); } } @@ -291,17 +289,13 @@ void PretransformVertices::Execute( aiScene* pScene) std::vector apcOutMeshes; apcOutMeshes.reserve(pScene->mNumMaterials<<1u); std::list aiVFormats; - for (unsigned int i = 0; i < pScene->mNumMaterials;++i) - { + for (unsigned int i = 0; i < pScene->mNumMaterials;++i) { // get the list of all vertex formats for this material aiVFormats.clear(); GetVFormatList(pScene,i,aiVFormats); aiVFormats.sort(); aiVFormats.unique(); - for (std::list::const_iterator - j = aiVFormats.begin(); - j != aiVFormats.end();++j) - { + for (std::list::const_iterator j = aiVFormats.begin();j != aiVFormats.end();++j) { unsigned int iVertices = 0; unsigned int iFaces = 0; CountVerticesAndFaces(pScene,pScene->mRootNode,i,*j,&iFaces,&iVertices); diff --git a/code/RemoveRedundantMaterials.cpp b/code/RemoveRedundantMaterials.cpp index 5cfc45609..70611a0e8 100644 --- a/code/RemoveRedundantMaterials.cpp +++ b/code/RemoveRedundantMaterials.cpp @@ -95,8 +95,7 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene) for (unsigned int i = 0; i < pScene->mNumMaterials;++i) { // if the material is not referenced ... remove it - if (!abReferenced[i]) - { + if (!abReferenced[i]) { ++unreferenced; continue; } @@ -104,8 +103,7 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene) uint32_t me = aiHashes[i] = ((MaterialHelper*)pScene->mMaterials[i])->ComputeHash(); for (unsigned int a = 0; a < i;++a) { - if (me == aiHashes[a]) - { + if (me == aiHashes[a]) { ++iCnt; me = 0; aiMappingTable[i] = aiMappingTable[a]; @@ -113,27 +111,26 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene) break; } } - if (me) - { + if (me) { aiMappingTable[i] = iNewNum++; } } - if (iCnt) - { + if (iCnt) { // build an output material list aiMaterial** ppcMaterials = new aiMaterial*[iNewNum]; ::memset(ppcMaterials,0,sizeof(void*)*iNewNum); for (unsigned int p = 0; p < pScene->mNumMaterials;++p) { // if the material is not referenced ... remove it - if (!abReferenced[p])continue; + if (!abReferenced[p]) + continue; // generate new names for all modified materials const unsigned int idx = aiMappingTable[p]; if (ppcMaterials[idx]) { aiString sz; - sz.length = ::sprintf(sz.data,"aiMaterial #%i",p); + sz.length = ::sprintf(sz.data,"JoinedMaterial_#%i",p); ((MaterialHelper*)ppcMaterials[idx])->AddProperty(&sz,AI_MATKEY_NAME); } else ppcMaterials[idx] = pScene->mMaterials[p]; diff --git a/code/ValidateDataStructure.cpp b/code/ValidateDataStructure.cpp index 885588bcf..d888b9efc 100644 --- a/code/ValidateDataStructure.cpp +++ b/code/ValidateDataStructure.cpp @@ -635,17 +635,15 @@ void ValidateDSProcess::SearchForInvalidTextures(const aiMaterial* pMaterial, for (unsigned int i = 0; i < pMaterial->mNumProperties;++i) { aiMaterialProperty* prop = pMaterial->mProperties[i]; - if (!::strcmp(prop->mKey.data,"$tex.file") && prop->mSemantic == type) - { + if (!::strcmp(prop->mKey.data,"$tex.file") && prop->mSemantic == type) { iIndex = std::max(iIndex, (int) prop->mIndex); ++iNumIndices; if (aiPTI_String != prop->mType) - this->ReportError("Material property %s is expected to be a string",prop->mKey.data); + ReportError("Material property %s is expected to be a string",prop->mKey.data); } } - if (iIndex +1 != iNumIndices) - { + if (iIndex +1 != iNumIndices) { ReportError("%s #%i is set, but there are only %i %s textures", szType,iIndex,iNumIndices,szType); } @@ -666,8 +664,7 @@ void ValidateDSProcess::SearchForInvalidTextures(const aiMaterial* pMaterial, prop->mIndex, iNumIndices, szType); } - if (!::strcmp(prop->mKey.data,"$tex.mapping")) - { + if (!::strcmp(prop->mKey.data,"$tex.mapping")) { if (aiPTI_Integer != prop->mType || prop->mDataLength < sizeof(aiTextureMapping)) { ReportError("Material property %s%i is expected to be an integer (size is %i)", @@ -675,8 +672,7 @@ void ValidateDSProcess::SearchForInvalidTextures(const aiMaterial* pMaterial, } mappings[prop->mIndex] = *((aiTextureMapping*)prop->mData); } - else if (!::strcmp(prop->mKey.data,"$tex.uvtrafo")) - { + else if (!::strcmp(prop->mKey.data,"$tex.uvtrafo")) { if (aiPTI_Float != prop->mType || prop->mDataLength < sizeof(aiUVTransform)) { ReportError("Material property %s%i is expected to be 5 floats large (size is %i)", @@ -684,8 +680,7 @@ void ValidateDSProcess::SearchForInvalidTextures(const aiMaterial* pMaterial, } mappings[prop->mIndex] = *((aiTextureMapping*)prop->mData); } - else if (!::strcmp(prop->mKey.data,"$tex.uvwsrc")) - { + else if (!::strcmp(prop->mKey.data,"$tex.uvwsrc")) { if (aiPTI_Integer != prop->mType || sizeof(int) > prop->mDataLength) { ReportError("Material property %s%i is expected to be an integer (size is %i)", @@ -709,7 +704,7 @@ void ValidateDSProcess::SearchForInvalidTextures(const aiMaterial* pMaterial, while (mesh->HasTextureCoords(iChannels))++iChannels; if (iIndex >= iChannels) { - ReportError("Invalid UV index: %i (key %s). Mesh %i has only %i UV channels", + ReportWarning("Invalid UV index: %i (key %s). Mesh %i has only %i UV channels", iIndex,prop->mKey.data,a,iChannels); } } @@ -742,42 +737,37 @@ void ValidateDSProcess::Validate( const aiMaterial* pMaterial) for (unsigned int i = 0; i < pMaterial->mNumProperties;++i) { const aiMaterialProperty* prop = pMaterial->mProperties[i]; - if (!prop) - { - this->ReportError("aiMaterial::mProperties[%i] is NULL (aiMaterial::mNumProperties is %i)", + if (!prop) { + ReportError("aiMaterial::mProperties[%i] is NULL (aiMaterial::mNumProperties is %i)", i,pMaterial->mNumProperties); } - if (!prop->mDataLength || !prop->mData) - { - this->ReportError("aiMaterial::mProperties[%i].mDataLength or " + if (!prop->mDataLength || !prop->mData) { + ReportError("aiMaterial::mProperties[%i].mDataLength or " "aiMaterial::mProperties[%i].mData is 0",i,i); } // check all predefined types if (aiPTI_String == prop->mType) { // FIX: strings are now stored in a less expensive way ... - if (prop->mDataLength < sizeof(size_t) + ((const aiString*)prop->mData)->length + 1) - { - this->ReportError("aiMaterial::mProperties[%i].mDataLength is " + if (prop->mDataLength < sizeof(size_t) + ((const aiString*)prop->mData)->length + 1) { + ReportError("aiMaterial::mProperties[%i].mDataLength is " "too small to contain a string (%i, needed: %i)", i,prop->mDataLength,sizeof(aiString)); } - this->Validate((const aiString*)prop->mData); + Validate((const aiString*)prop->mData); } else if (aiPTI_Float == prop->mType) { - if (prop->mDataLength < sizeof(float)) - { - this->ReportError("aiMaterial::mProperties[%i].mDataLength is " + if (prop->mDataLength < sizeof(float)) { + ReportError("aiMaterial::mProperties[%i].mDataLength is " "too small to contain a float (%i, needed: %i)", i,prop->mDataLength,sizeof(float)); } } else if (aiPTI_Integer == prop->mType) { - if (prop->mDataLength < sizeof(int)) - { - this->ReportError("aiMaterial::mProperties[%i].mDataLength is " + if (prop->mDataLength < sizeof(int)) { + ReportError("aiMaterial::mProperties[%i].mDataLength is " "too small to contain an integer (%i, needed: %i)", i,prop->mDataLength,sizeof(int)); } @@ -788,22 +778,19 @@ void ValidateDSProcess::Validate( const aiMaterial* pMaterial) // make some more specific tests float fTemp; int iShading; - if (AI_SUCCESS == aiGetMaterialInteger( pMaterial,AI_MATKEY_SHADING_MODEL,&iShading)) - { + if (AI_SUCCESS == aiGetMaterialInteger( pMaterial,AI_MATKEY_SHADING_MODEL,&iShading)) { switch ((aiShadingMode)iShading) { case aiShadingMode_Blinn: case aiShadingMode_CookTorrance: case aiShadingMode_Phong: - if (AI_SUCCESS != aiGetMaterialFloat(pMaterial,AI_MATKEY_SHININESS,&fTemp)) - { - this->ReportWarning("A specular shading model is specified but there is no " + if (AI_SUCCESS != aiGetMaterialFloat(pMaterial,AI_MATKEY_SHININESS,&fTemp)) { + ReportWarning("A specular shading model is specified but there is no " "AI_MATKEY_SHININESS key"); } - if (AI_SUCCESS == aiGetMaterialFloat(pMaterial,AI_MATKEY_SHININESS_STRENGTH,&fTemp) && !fTemp) - { - this->ReportWarning("A specular shading model is specified but the value of the " + if (AI_SUCCESS == aiGetMaterialFloat(pMaterial,AI_MATKEY_SHININESS_STRENGTH,&fTemp) && !fTemp) { + ReportWarning("A specular shading model is specified but the value of the " "AI_MATKEY_SHININESS_STRENGTH key is 0.0"); } break; @@ -811,13 +798,13 @@ void ValidateDSProcess::Validate( const aiMaterial* pMaterial) }; } - if (AI_SUCCESS == aiGetMaterialFloat( pMaterial,AI_MATKEY_OPACITY,&fTemp)) - { - if (!fTemp) - ReportWarning("Material is fully transparent ... are you sure you REALLY want this?"); + if (AI_SUCCESS == aiGetMaterialFloat( pMaterial,AI_MATKEY_OPACITY,&fTemp) && (!fTemp || fTemp > 1.01f)) { + ReportWarning("Invalid opacity value (must be 0 < opacity < 1.0)"); } - // check whether there are invalid texture keys + // Check whether there are invalid texture keys + // TODO: that's a relict of the past, where texture type and index were baked + // into the material string ... we could do that in one single pass. SearchForInvalidTextures(pMaterial,aiTextureType_DIFFUSE); SearchForInvalidTextures(pMaterial,aiTextureType_SPECULAR); SearchForInvalidTextures(pMaterial,aiTextureType_AMBIENT); @@ -826,6 +813,9 @@ void ValidateDSProcess::Validate( const aiMaterial* pMaterial) SearchForInvalidTextures(pMaterial,aiTextureType_SHININESS); SearchForInvalidTextures(pMaterial,aiTextureType_HEIGHT); SearchForInvalidTextures(pMaterial,aiTextureType_NORMALS); + SearchForInvalidTextures(pMaterial,aiTextureType_DISPLACEMENT); + SearchForInvalidTextures(pMaterial,aiTextureType_LIGHTMAP); + SearchForInvalidTextures(pMaterial,aiTextureType_REFLECTION); } // ------------------------------------------------------------------------------------------------ diff --git a/include/aiConfig.h b/include/aiConfig.h index e4b8ebb38..ffd9d66c2 100644 --- a/include/aiConfig.h +++ b/include/aiConfig.h @@ -159,6 +159,24 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define AI_CONFIG_IMPORT_MD3_SKIN_NAME \ "IMPORT_MD3_SKIN_NAME" +// --------------------------------------------------------------------------- +/** @brief Specify the Quake 3 shader file to be used for a particular + * MD3 file. This can also be a search path. + * + * By default Assimp's behaviour is as follows: If a MD3 file + * /models///.md3 is + * loaded, the library tries to locate the corresponding shader file in + * /scripts/.shader. This property overrides this + * behaviour. It can either specify a full path to the shader to be loaded + * or alternatively the path (relative or absolute) to the directory where + * the shaders for all MD3s to be loaded reside. Assimp attempts to open + * /.shader first, /.shader + * is the fallback file. Note that should have a terminal (back)slash. + * Property type: String. Default value: n/a. + */ +#define AI_CONFIG_IMPORT_MD3_SHADER_SRC \ + "IMPORT_MD3_SHADER_SRC" + // --------------------------------------------------------------------------- /** @brief Configures the LWO loader to load just one layer from the model. diff --git a/include/aiMaterial.h b/include/aiMaterial.h index 8b53df139..fe4d1604d 100644 --- a/include/aiMaterial.h +++ b/include/aiMaterial.h @@ -97,7 +97,8 @@ enum aiPropertyTypeInfo }; // --------------------------------------------------------------------------- -/** @brief Defines how the Nth texture is combined with the N-1th texture. +/** @brief Defines how the Nth texture of a specific type is combined with + * the result of all previous layers. * * Example (left: key, right: value):
* @code @@ -418,8 +419,10 @@ enum aiShadingMode // --------------------------------------------------------------------------- /** @brief Defines some mixed flags for a particular texture. * - * Usually you'll tell your artists how textures have to look like ... - * however, if you use Assimp for completely generic loadeing purposes you + * Usually you'll tell your cg artists how textures have to look like ... + * and hopefully the follow these rules. If they don't, restrict access + * to the coffee machine for them. That should help. + * However, if you use Assimp for completely generic loading purposes you * might also need to process these flags in order to display as many * 'unknown' 3D models as possible correctly. * @@ -432,6 +435,25 @@ enum aiTextureFlags aiTextureFlags_Invert = 0x1, + /** Explicit request to the application to process the alpha channel + * of the texture. + * + * Mutually exclusive with #aiTextureFlags_IgnoreAlpha. These + * flags are set if the library can say for sure that the alpha + * channel is used/is not used. If the model format does not + * define this, it is left to the application to decide whether + * the texture alpha channel - if any - is evaluated or not. + */ + aiTextureFlags_UseAlpha = 0x2, + + /** Explicit request to the application to ignore the alpha channel + * of the texture. + * + * Mutually exclusive with #aiTextureFlags_IgnoreAlpha. + */ + aiTextureFlags_IgnoreAlpha = 0x4, + + /** @cond never * This value is not used. It forces the compiler to use at least @@ -539,6 +561,25 @@ struct aiUVTransform // --------------------------------------------------------------------------- /** @brief Data structure for a single material property + * + * As an user, you'll probably never need to deal with this data structure. + * Just use the provided aiGetMaterialXXX() or aiMaterial::Get() family + * of functions to query material properties easily. Processing them + * manually is faster, but it is not the recommended way. It isn't worth + * the effort.
+ * Material property names follow a simple scheme: + * @code + * $ + * ? + * A public property, there must be corresponding AI_MATKEY_XXX define + * 2nd: Public, but ignored by the #aiProcess_RemoveRedundantMaterials + * post-processing step. + * ~ + * A temporary property for internal use. If someone forgets to + * cleanup, some of these might still be contained in the output. + * Don't complain, if you understood what the first paragraph tried + * to tell you, you wouldn't even know. + * @endcode * @see aiMaterial */ struct aiMaterialProperty @@ -710,7 +751,7 @@ extern "C" { * Type: string (aiString)
* Default value: none
*/ -#define AI_MATKEY_NAME "$mat.name",0,0 +#define AI_MATKEY_NAME "?mat.name",0,0 // --------------------------------------------------------------------------- /** @def AI_MATKEY_TWOSIDED diff --git a/include/aiScene.h b/include/aiScene.h index ee6196f21..cd921df87 100644 --- a/include/aiScene.h +++ b/include/aiScene.h @@ -171,7 +171,6 @@ struct aiNode */ #define AI_SCENE_FLAGS_INCOMPLETE 0x1 - /** @def AI_SCENE_FLAGS_VALIDATED * This flag is set by the validation postprocess-step (aiPostProcess_ValidateDS) * if the validation is successful. In a validated scene you can be sure that @@ -179,7 +178,6 @@ struct aiNode */ #define AI_SCENE_FLAGS_VALIDATED 0x2 - /** @def AI_SCENE_FLAGS_VALIDATION_WARNING * This flag is set by the validation postprocess-step (aiPostProcess_ValidateDS) * if the validation is successful but some issues have been found. @@ -190,7 +188,6 @@ struct aiNode */ #define AI_SCENE_FLAGS_VALIDATION_WARNING 0x4 - /** @def AI_SCENE_FLAGS_NON_VERBOSE_FORMAT * This flag is currently only set by the aiProcess_JoinIdenticalVertices step. * It indicates that the vertices of the output meshes aren't in the internal @@ -199,7 +196,6 @@ struct aiNode */ #define AI_SCENE_FLAGS_NON_VERBOSE_FORMAT 0x8 - /** @def AI_SCENE_FLAGS_TERRAIN * Denotes pure height-map terrain data. Pure terrains usually consist of quads, * sometimes triangles, in a regular grid. The x,y coordinates of all vertex @@ -212,7 +208,8 @@ struct aiNode * as long as possible (typically you'll do the triangulation when you actually * need to render it). */ -#define AI_SCENE_FLAGS_TERRAIN 0x16 +#define AI_SCENE_FLAGS_TERRAIN 0x10 + // ------------------------------------------------------------------------------- /** The root structure of the imported data. diff --git a/tools/assimp_view/AssetHelper.h b/tools/assimp_view/AssetHelper.h index 37fa4593a..4dbefdd9a 100644 --- a/tools/assimp_view/AssetHelper.h +++ b/tools/assimp_view/AssetHelper.h @@ -150,7 +150,8 @@ class AssetHelper piShininessTexture (NULL), piLightmapTexture (NULL), pvOriginalNormals (NULL), - bSharedFX(false) {} + bSharedFX(false), + twosided (false){} ~MeshHelper () { @@ -203,6 +204,9 @@ class AssetHelper // strength of the specular highlight float fSpecularStrength; + // two-sided? + bool twosided; + // Stores a pointer to the original normal set of the asset aiVector3D* pvOriginalNormals; }; diff --git a/tools/assimp_view/Display.cpp b/tools/assimp_view/Display.cpp index b91699839..0cab7c077 100644 --- a/tools/assimp_view/Display.cpp +++ b/tools/assimp_view/Display.cpp @@ -45,6 +45,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace AssimpView { +struct SVertex +{ + float x,y,z,w,u,v; +}; CDisplay CDisplay::s_cInstance; @@ -91,43 +95,40 @@ void GetNodeCount(aiNode* pcNode, unsigned int* piCnt) } //------------------------------------------------------------------------------- +int CDisplay::EnableAnimTools(BOOL hm) +{ + EnableWindow(GetDlgItem(g_hDlg,IDC_PLAY),hm); + EnableWindow(GetDlgItem(g_hDlg,IDC_SLIDERANIM),hm); + return 1; +} + +//------------------------------------------------------------------------------- +// Fill animation combo box int CDisplay::FillAnimList(void) { - if (0 == g_pcAsset->pcScene->mNumAnimations) + 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) - { + 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); } + + // also add a dummy - 'none' + SendDlgItemMessage(g_hDlg,IDC_COMBO1,CB_ADDSTRING,0,(LPARAM)"none"); + + // select first + SendDlgItemMessage(g_hDlg,IDC_COMBO1,CB_SETCURSEL,0,0); + + EnableAnimTools(TRUE); } + else // tools remain disabled + EnableAnimTools(FALSE); + return 1; } //------------------------------------------------------------------------------- +// Clear the list of animations int CDisplay::ClearAnimList(void) { // clear the combo box @@ -135,6 +136,7 @@ int CDisplay::ClearAnimList(void) return 1; } //------------------------------------------------------------------------------- +// Clear the tree view int CDisplay::ClearDisplayList(void) { // clear the combo box @@ -143,6 +145,7 @@ int CDisplay::ClearDisplayList(void) return 1; } //------------------------------------------------------------------------------- +// Add a specific node to the display list int CDisplay::AddNodeToDisplayList( unsigned int iIndex, unsigned int iDepth, @@ -154,10 +157,8 @@ int CDisplay::AddNodeToDisplayList( char chTemp[512]; - if(0 == pcNode->mName.length) - { - if (iIndex >= 100) - { + if(0 == pcNode->mName.length) { + if (iIndex >= 100) { iIndex += iDepth * 1000; } else if (iIndex >= 10) @@ -190,8 +191,7 @@ int CDisplay::AddNodeToDisplayList( // recursively add all child nodes ++iDepth; - for (unsigned int i = 0; i< pcNode->mNumChildren;++i) - { + for (unsigned int i = 0; i< pcNode->mNumChildren;++i){ AddNodeToDisplayList(i,iDepth,pcNode->mChildren[i],hTexture); } @@ -203,6 +203,7 @@ int CDisplay::AddNodeToDisplayList( return 1; } //------------------------------------------------------------------------------- +// Replace the currently selected texture by another one int CDisplay::ReplaceCurrentTexture(const char* szPath) { ai_assert(NULL != szPath); @@ -214,8 +215,7 @@ int CDisplay::ReplaceCurrentTexture(const char* szPath) szString.length = strlen(szPath); CMaterialManager::Instance().LoadTexture(&piTexture,&szString); - if (!piTexture) - { + if (!piTexture) { CLogDisplay::Instance().AddEntry("[ERROR] Unable to load this texture", D3DCOLOR_ARGB(0xFF,0xFF,0x0,0x0)); return 0; @@ -225,15 +225,15 @@ int CDisplay::ReplaceCurrentTexture(const char* szPath) // view item if the default texture was previously set TVITEMEX tvi; tvi.mask = TVIF_SELECTEDIMAGE | TVIF_IMAGE; - tvi.iImage = this->m_aiImageList[AI_VIEW_IMGLIST_MATERIAL]; - tvi.iSelectedImage = this->m_aiImageList[AI_VIEW_IMGLIST_MATERIAL]; + tvi.iImage = m_aiImageList[AI_VIEW_IMGLIST_MATERIAL]; + tvi.iSelectedImage = m_aiImageList[AI_VIEW_IMGLIST_MATERIAL]; TreeView_SetItem(GetDlgItem(g_hDlg,IDC_TREE1), - this->m_pcCurrentTexture->hTreeItem); + m_pcCurrentTexture->hTreeItem); // change this in the old aiMaterial structure, too Assimp::MaterialHelper* pcMat = (Assimp::MaterialHelper*) - g_pcAsset->pcScene->mMaterials[this->m_pcCurrentTexture->iMatIndex]; + g_pcAsset->pcScene->mMaterials[m_pcCurrentTexture->iMatIndex]; // update all meshes referencing this material for (unsigned int i = 0; i < g_pcAsset->pcScene->mNumMeshes;++i) @@ -242,137 +242,73 @@ int CDisplay::ReplaceCurrentTexture(const char* szPath) continue; AssetHelper::MeshHelper* pcMesh = g_pcAsset->apcMeshes[i]; + IDirect3DTexture9** tex = NULL; + const char* tex_string = NULL; + switch (this->m_pcCurrentTexture->iType) { case aiTextureType_DIFFUSE: - if (pcMesh->piDiffuseTexture && pcMesh->piDiffuseTexture != piTexture) - { - pcMesh->piDiffuseTexture->Release(); - pcMesh->piDiffuseTexture = piTexture; - this->m_pcCurrentTexture->piTexture = &pcMesh->piDiffuseTexture; - - if (!pcMesh->bSharedFX) - { - pcMesh->piEffect->SetTexture("DIFFUSE_TEXTURE",piTexture); - } - } + tex = &pcMesh->piDiffuseTexture; + tex_string = "DIFFUSE_TEXTURE"; break; case aiTextureType_AMBIENT: - if (pcMesh->piAmbientTexture && pcMesh->piAmbientTexture != piTexture) - { - pcMesh->piAmbientTexture->Release(); - pcMesh->piAmbientTexture = piTexture; - this->m_pcCurrentTexture->piTexture = &pcMesh->piAmbientTexture; - - if (!pcMesh->bSharedFX) - { - pcMesh->piEffect->SetTexture("AMBIENT_TEXTURE",piTexture); - } - } + tex = &pcMesh->piAmbientTexture; + tex_string = "AMBIENT_TEXTURE"; break; case aiTextureType_SPECULAR: - if (pcMesh->piSpecularTexture && pcMesh->piSpecularTexture != piTexture) - { - pcMesh->piSpecularTexture->Release(); - pcMesh->piSpecularTexture = piTexture; - this->m_pcCurrentTexture->piTexture = &pcMesh->piSpecularTexture; - - if (!pcMesh->bSharedFX) - { - pcMesh->piEffect->SetTexture("SPECULAR_TEXTURE",piTexture); - } - } + tex = &pcMesh->piSpecularTexture; + tex_string = "SPECULAR_TEXTURE"; break; case aiTextureType_EMISSIVE: - if (pcMesh->piEmissiveTexture && pcMesh->piEmissiveTexture != piTexture) - { - pcMesh->piEmissiveTexture->Release(); - pcMesh->piEmissiveTexture = piTexture; - this->m_pcCurrentTexture->piTexture = &pcMesh->piEmissiveTexture; - - if (!pcMesh->bSharedFX) - { - pcMesh->piEffect->SetTexture("EMISSIVE_TEXTURE",piTexture); - } - } + tex = &pcMesh->piEmissiveTexture; + tex_string = "EMISSIVE_TEXTURE"; + break; + case aiTextureType_LIGHTMAP: + tex = &pcMesh->piLightmapTexture; + tex_string = "LIGHTMAP_TEXTURE"; + break; + case aiTextureType_DISPLACEMENT: + case aiTextureType_REFLECTION: + case aiTextureType_UNKNOWN: break; case aiTextureType_SHININESS: - if (pcMesh->piShininessTexture && pcMesh->piShininessTexture != piTexture) - { - pcMesh->piShininessTexture->Release(); - pcMesh->piShininessTexture = piTexture; - this->m_pcCurrentTexture->piTexture = &pcMesh->piShininessTexture; - - if (!pcMesh->bSharedFX) - { - pcMesh->piEffect->SetTexture("SHININESS_TEXTURE",piTexture); - } - } + tex = &pcMesh->piShininessTexture; + tex_string = "SHININESS_TEXTURE"; break; case aiTextureType_NORMALS: case aiTextureType_HEIGHT: - if (pcMesh->piNormalTexture && pcMesh->piNormalTexture != piTexture) - { + + // special handling here + if (pcMesh->piNormalTexture && pcMesh->piNormalTexture != piTexture) { pcMesh->piNormalTexture->Release(); pcMesh->piNormalTexture = piTexture; - CMaterialManager::Instance().HMtoNMIfNecessary(pcMesh->piNormalTexture, - &pcMesh->piNormalTexture,true); - this->m_pcCurrentTexture->piTexture = &pcMesh->piNormalTexture; + CMaterialManager::Instance().HMtoNMIfNecessary(pcMesh->piNormalTexture,&pcMesh->piNormalTexture,true); + m_pcCurrentTexture->piTexture = &pcMesh->piNormalTexture; - if (!pcMesh->bSharedFX) - { + if (!pcMesh->bSharedFX) { pcMesh->piEffect->SetTexture("NORMAL_TEXTURE",piTexture); } } break; default: //case aiTextureType_OPACITY && case aiTextureType_OPACITY | 0x40000000: - if (pcMesh->piOpacityTexture && pcMesh->piOpacityTexture != piTexture) - { - pcMesh->piOpacityTexture->Release(); - pcMesh->piOpacityTexture = piTexture; - this->m_pcCurrentTexture->piTexture = &pcMesh->piOpacityTexture; - - if (!pcMesh->bSharedFX) - { - pcMesh->piEffect->SetTexture("OPACITY_TEXTURE",piTexture); - } - } + + tex = &pcMesh->piOpacityTexture; + tex_string = "OPACITY_TEXTURE"; break; }; + if (tex && *tex && *tex != piTexture) + { + (**tex).Release(); + *tex = piTexture; + m_pcCurrentTexture->piTexture = tex; + + if (!pcMesh->bSharedFX){ + pcMesh->piEffect->SetTexture(tex_string,piTexture); + } + } } // now update the material itself aiString szOld; - const char* szKey = NULL; -#if 0 - switch (this->m_pcCurrentTexture->iType) - { - case aiTextureType_DIFFUSE: - szKey = AI_MATKEY_TEXTURE_DIFFUSE(0); - break; - case aiTextureType_AMBIENT: - szKey = AI_MATKEY_TEXTURE_AMBIENT(0); - break; - case aiTextureType_SPECULAR: - szKey = AI_MATKEY_TEXTURE_SPECULAR(0); - break; - case aiTextureType_EMISSIVE: - szKey = AI_MATKEY_TEXTURE_EMISSIVE(0); - break; - case aiTextureType_NORMALS: - szKey = AI_MATKEY_TEXTURE_NORMALS(0); - break; - case aiTextureType_HEIGHT: - szKey = AI_MATKEY_TEXTURE_HEIGHT(0); - break; - case aiTextureType_SHININESS: - szKey = AI_MATKEY_TEXTURE_SHININESS(0); - break; - default: //case aiTextureType_OPACITY && case aiTextureType_OPACITY | 0x40000000: - szKey = AI_MATKEY_TEXTURE_OPACITY(0); - break; - }; -#endif - ai_assert(NULL != szKey); aiGetMaterialString(pcMat,AI_MATKEY_TEXTURE(m_pcCurrentTexture->iType,0),&szOld); pcMat->AddProperty(&szString,AI_MATKEY_TEXTURE(m_pcCurrentTexture->iType,0)); @@ -478,8 +414,7 @@ int CDisplay::AddTextureToDisplayList(unsigned int iType, szType = "Opacity"; break; }; - if (bIsExtraOpacity) - { + if (bIsExtraOpacity) { sprintf(chTemp,"%s %i ()",szType,iIndex+1); } else @@ -494,8 +429,7 @@ int CDisplay::AddTextureToDisplayList(unsigned int iType, // find out whether this is the default texture or not - if (piTexture && *piTexture) - { + if (piTexture && *piTexture) { // {9785DA94-1D96-426b-B3CB-BADC36347F5E} static const GUID guidPrivateData = { 0x9785da94, 0x1d96, 0x426b, @@ -651,6 +585,7 @@ int CDisplay::AddMaterialToDisplayList(HTREEITEM hRoot, return 1; } //------------------------------------------------------------------------------- +// Expand all elements in the treeview int CDisplay::ExpandTree() { // expand all materials @@ -671,6 +606,7 @@ int CDisplay::ExpandTree() return 1; } //------------------------------------------------------------------------------- +// Get image list for tree view int CDisplay::LoadImageList(void) { if (!m_hImageList) @@ -708,6 +644,7 @@ int CDisplay::LoadImageList(void) return 1; } //------------------------------------------------------------------------------- +// Fill tree view int CDisplay::FillDisplayList(void) { LoadImageList(); @@ -750,6 +687,7 @@ int CDisplay::FillDisplayList(void) return 1; } //------------------------------------------------------------------------------- +// Main render loop int CDisplay::OnRender() { // update possible animation @@ -759,10 +697,17 @@ int CDisplay::OnRender() ai_assert( g_pcAsset->mAnimator); if (g_bPlay) { - - g_dCurrent += clock()/ double( CLOCKS_PER_SEC) -lastPlaying; - g_pcAsset->mAnimator->Calculate( g_dCurrent ); + + double time = g_dCurrent; + aiAnimation* mAnim = g_pcAsset->mAnimator->CurrentAnim(); + if( mAnim && mAnim->mDuration > 0.0) { + double tps = mAnim->mTicksPerSecond ? mAnim->mTicksPerSecond : 25.f; + time = fmod( time, mAnim->mDuration/tps); + SendDlgItemMessage(g_hDlg,IDC_SLIDERANIM,TBM_SETPOS,TRUE,LPARAM(10000 * (time/(mAnim->mDuration/tps)))); + } + + g_pcAsset->mAnimator->Calculate( time ); lastPlaying = g_dCurrent; } } @@ -795,6 +740,7 @@ int CDisplay::OnRender() return 1; } //------------------------------------------------------------------------------- +// Update UI void UpdateColorFieldsInUI() { InvalidateRect(GetDlgItem(g_hDlg,IDC_LCOLOR1),NULL,TRUE); @@ -806,6 +752,7 @@ void UpdateColorFieldsInUI() UpdateWindow(GetDlgItem(g_hDlg,IDC_LCOLOR3)); } //------------------------------------------------------------------------------- +// FIll statistics UI int CDisplay::FillDefaultStatistics(void) { if (!g_pcAsset) @@ -858,6 +805,7 @@ int CDisplay::FillDefaultStatistics(void) return 1; } //------------------------------------------------------------------------------- +// Reset UI int CDisplay::Reset(void) { // clear all lists @@ -870,6 +818,7 @@ int CDisplay::Reset(void) return OnSetupNormalView(); } //------------------------------------------------------------------------------- +// reset to standard statistics view void ShowNormalUIComponents() { ShowWindow(GetDlgItem(g_hDlg,IDC_NUMNODES),SW_SHOW); @@ -1089,60 +1038,45 @@ int CDisplay::OnSetupTextureView(TextureInfo* pcNew) int CDisplay::OnSetup(HTREEITEM p_hTreeItem) { // search in our list for the item - union { TextureInfo* pcNew; NodeInfo* pcNew2; - MaterialInfo* pcNew3; }; + MaterialInfo* pcNew3; + }; pcNew = NULL; - for (std::vector::iterator - i = this->m_asTextures.begin(); - i != this->m_asTextures.end();++i) - { - if (p_hTreeItem == (*i).hTreeItem) - { + for (std::vector::iterator i = m_asTextures.begin();i != m_asTextures.end();++i){ + if (p_hTreeItem == (*i).hTreeItem) { pcNew = &(*i); break; } } - if (pcNew) - { - return this->OnSetupTextureView(pcNew); + if (pcNew) { + return OnSetupTextureView(pcNew); } // seach the node list - for (std::vector::iterator - i = this->m_asNodes.begin(); - i != this->m_asNodes.end();++i) - { - if (p_hTreeItem == (*i).hTreeItem) - { + for (std::vector::iterator i = m_asNodes.begin(); i != m_asNodes.end();++i){ + if (p_hTreeItem == (*i).hTreeItem) { pcNew2 = &(*i); break; } } - if (pcNew2) - { - return this->OnSetupNodeView(pcNew2); + if (pcNew2) { + return OnSetupNodeView(pcNew2); } // seach the material list - for (std::vector::iterator - i = this->m_asMaterials.begin(); - i != this->m_asMaterials.end();++i) - { - if (p_hTreeItem == (*i).hTreeItem) - { + for (std::vector::iterator i = m_asMaterials.begin();i != m_asMaterials.end();++i){ + if (p_hTreeItem == (*i).hTreeItem){ pcNew3 = &(*i); break; } } - if (pcNew3) - { - return this->OnSetupMaterialView(pcNew3); + if (pcNew3) { + return OnSetupMaterialView(pcNew3); } - return this->OnSetupNormalView(); + return OnSetupNormalView(); } //------------------------------------------------------------------------------- int CDisplay::ShowTreeViewContextMenu(HTREEITEM hItem) @@ -1499,6 +1433,7 @@ int CDisplay::HandleTreeViewPopup2(WPARAM wParam,LPARAM lParam) return 0; } //------------------------------------------------------------------------------- +// Setup stereo view int CDisplay::SetupStereoView() { if (NULL != g_pcAsset && NULL != g_pcAsset->pcScene->mRootNode) @@ -1515,6 +1450,7 @@ int CDisplay::SetupStereoView() return 1; } //------------------------------------------------------------------------------- +// Do the actual rendering pass for the stereo view int CDisplay::RenderStereoView(const aiMatrix4x4& m) { // and rerender the scene @@ -1550,6 +1486,7 @@ int CDisplay::RenderStereoView(const aiMatrix4x4& m) return 1; } //------------------------------------------------------------------------------- +// Process input for the texture view int CDisplay::HandleInputTextureView() { HandleMouseInputTextureView(); @@ -1557,6 +1494,7 @@ int CDisplay::HandleInputTextureView() return 1; } //------------------------------------------------------------------------------- +// Get input for the current state int CDisplay::HandleInput() { if(CBackgroundPainter::TEXTURE_CUBE == CBackgroundPainter::Instance().GetMode()) @@ -1608,6 +1546,7 @@ int CDisplay::HandleInput() return 1; } //------------------------------------------------------------------------------- +// Process input for an empty scen view to allow for skybox rotations int CDisplay::HandleInputEmptyScene() { if(CBackgroundPainter::TEXTURE_CUBE == CBackgroundPainter::Instance().GetMode()) @@ -1630,6 +1569,7 @@ int CDisplay::HandleInputEmptyScene() return 1; } //------------------------------------------------------------------------------- +// Draw the HUD on top of the scene int CDisplay::DrawHUD() { // HACK: (thom) can't get the effect to work on non-shader cards, therefore deactivated for the moment @@ -1642,21 +1582,16 @@ int CDisplay::DrawHUD() sRect.right -= sRect.left; sRect.bottom -= sRect.top; - struct SVertex - { - float x,y,z,w,u,v; - }; - // commit the texture to the shader // FIX: Necessary because the texture view is also using this shader g_piPassThroughEffect->SetTexture("TEXTURE_2D",g_pcTexture); // NOTE: The shader might be used for other purposes, too. // So ensure the right technique is there - if( g_sCaps.PixelShaderVersion < D3DPS_VERSION(2,0)) - g_piPassThroughEffect->SetTechnique( "PassThrough_FF"); - else - g_piPassThroughEffect->SetTechnique("PassThrough"); + if( g_sCaps.PixelShaderVersion < D3DPS_VERSION(2,0)) + g_piPassThroughEffect->SetTechnique( "PassThrough_FF"); + else + g_piPassThroughEffect->SetTechnique("PassThrough"); // build vertices for drawing from system memory UINT dw; @@ -1713,11 +1648,18 @@ int CDisplay::DrawHUD() return 1; } //------------------------------------------------------------------------------- +// Render the full scene, all nodes int CDisplay::RenderFullScene() { // reset the color index used for drawing normals g_iCurrentColor = 0; + aiMatrix4x4 pcProj; + GetProjectionMatrix(pcProj); + + vPos = GetCameraMatrix(mViewProjection); + mViewProjection = mViewProjection * pcProj; + // setup wireframe/solid rendering mode if (g_sOptions.eDrawMode == RenderOptions::WIREFRAME) g_piDevice->SetRenderState(D3DRS_FILLMODE,D3DFILL_WIREFRAME); @@ -1727,6 +1669,24 @@ int CDisplay::RenderFullScene() g_piDevice->SetRenderState(D3DRS_CULLMODE,D3DCULL_CCW); else g_piDevice->SetRenderState(D3DRS_CULLMODE,D3DCULL_NONE); + // for high-quality mode, enable anisotropic texture filtering + if (g_sOptions.bLowQuality) { + for (DWORD d = 0; d < 8;++d) { + g_piDevice->SetSamplerState(d,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR); + g_piDevice->SetSamplerState(d,D3DSAMP_MINFILTER,D3DTEXF_LINEAR); + g_piDevice->SetSamplerState(d,D3DSAMP_MIPFILTER,D3DTEXF_LINEAR); + } + } + else { + for (DWORD d = 0; d < 8;++d) { + g_piDevice->SetSamplerState(d,D3DSAMP_MAGFILTER,D3DTEXF_ANISOTROPIC); + g_piDevice->SetSamplerState(d,D3DSAMP_MINFILTER,D3DTEXF_ANISOTROPIC); + g_piDevice->SetSamplerState(d,D3DSAMP_MIPFILTER,D3DTEXF_LINEAR); + + g_piDevice->SetSamplerState(d,D3DSAMP_MAXANISOTROPY,g_sCaps.MaxAnisotropy); + } + } + // draw the scene background (clear and texture 2d) CBackgroundPainter::Instance().OnPreRender(); @@ -1761,9 +1721,37 @@ int CDisplay::RenderFullScene() } // setup the stereo view if necessary - if (g_sOptions.bStereoView) + if (g_sOptions.bStereoView) RenderStereoView(m); + // render the skeleton if necessary + if (g_sOptions.bSkeleton && NULL != g_pcAsset && NULL != g_pcAsset->pcScene->mRootNode) { + // disable the z-buffer + g_piDevice->SetRenderState(D3DRS_ZWRITEENABLE,FALSE); + + if (g_sOptions.eDrawMode != RenderOptions::WIREFRAME) { + g_piDevice->SetRenderState(D3DRS_ZENABLE,FALSE); + } + + g_piDevice->SetVertexDeclaration( gDefaultVertexDecl); + // this is very similar to the code in SetupMaterial() + ID3DXEffect* piEnd = g_piNormalsEffect; + aiMatrix4x4 pcProj = m * mViewProjection; + + D3DXVECTOR4 vVector(1.f,0.f,0.f,1.f); + piEnd->SetVector("OUTPUT_COLOR",&vVector); + piEnd->SetMatrix("WorldViewProjection", (const D3DXMATRIX*)&pcProj); + + UINT dwPasses = 0; + piEnd->Begin(&dwPasses,0); + piEnd->BeginPass(0); + + RenderSkeleton(g_pcAsset->pcScene->mRootNode,m,m); + + piEnd->EndPass();piEnd->End(); + g_piDevice->SetRenderState(D3DRS_ZWRITEENABLE,TRUE); + g_piDevice->SetRenderState(D3DRS_ZENABLE,TRUE); + } // draw the HUD texture on top of the rendered scene using // pre-projected vertices @@ -1778,6 +1766,37 @@ int CDisplay::RenderMaterialView() return 1; } //------------------------------------------------------------------------------- +// Render animation skeleton +int CDisplay::RenderSkeleton (aiNode* piNode,const aiMatrix4x4& piMatrix, const aiMatrix4x4& parent) +{ + aiMatrix4x4 me = g_pcAsset->mAnimator->GetGlobalTransform( piNode); + + me.Transpose(); + //me *= piMatrix; + + if (piNode->mParent) { + AssetHelper::LineVertex data[2]; + data[0].dColorDiffuse = data[1].dColorDiffuse = D3DCOLOR_ARGB(0xff,0xff,0,0); + + data[0].vPosition.x = parent.d1; + data[0].vPosition.y = parent.d2; + data[0].vPosition.z = parent.d3; + + data[1].vPosition.x = me.d1; + data[1].vPosition.y = me.d2; + data[1].vPosition.z = me.d3; + + g_piDevice->DrawPrimitiveUP(D3DPT_LINELIST,1,&data,sizeof(AssetHelper::LineVertex)); + } + + // render all child nodes + for (unsigned int i = 0; i < piNode->mNumChildren;++i) + RenderSkeleton(piNode->mChildren[i],piMatrix, me ); + + return 1; +} +//------------------------------------------------------------------------------- +// Render a single node int CDisplay::RenderNode (aiNode* piNode,const aiMatrix4x4& piMatrix, bool bAlpha /*= false*/) { @@ -1801,14 +1820,9 @@ int CDisplay::RenderNode (aiNode* piNode,const aiMatrix4x4& piMatrix, bChangedVM = true; } - aiMatrix4x4 pcProj; - GetProjectionMatrix(pcProj); + aiMatrix4x4 pcProj = aiMe * mViewProjection; - aiMatrix4x4 pcCam; - aiVector3D vPos = GetCameraMatrix(pcCam); - pcProj = (aiMe * pcCam) * pcProj; - - pcCam = aiMe; + aiMatrix4x4 pcCam = aiMe; pcCam.Inverse().Transpose(); // VERY UNOPTIMIZED, much stuff is redundant. Who cares? @@ -1911,11 +1925,6 @@ int CDisplay::RenderNode (aiNode* piNode,const aiMatrix4x4& piMatrix, const aiMesh* mesh = g_pcAsset->pcScene->mMeshes[piNode->mMeshes[i]]; AssetHelper::MeshHelper* helper = g_pcAsset->apcMeshes[piNode->mMeshes[i]]; - - // fix: Render triangle meshes only - if (mesh->mPrimitiveTypes != aiPrimitiveType_TRIANGLE) - continue; - // don't render the mesh if the render pass is incorrect if (g_sOptions.bRenderMats && (helper->piOpacityTexture || helper->fOpacity != 1.0f) && !mesh->HasBones()) { @@ -1986,7 +1995,6 @@ int CDisplay::RenderNode (aiNode* piNode,const aiMatrix4x4& piMatrix, ID3DXEffect* piEnd = g_piNormalsEffect; piEnd->SetVector("OUTPUT_COLOR",&vVector); - piEnd->SetMatrix("WorldViewProjection", (const D3DXMATRIX*)&pcProj); UINT dwPasses = 0; diff --git a/tools/assimp_view/Display.h b/tools/assimp_view/Display.h index a254450ff..261f091ff 100644 --- a/tools/assimp_view/Display.h +++ b/tools/assimp_view/Display.h @@ -295,6 +295,10 @@ public: // Event handling for pop-up menus displayed by th tree view int HandleTreeViewPopup(WPARAM wParam,LPARAM lParam); + //------------------------------------------------------------------ + // Enable animation-related parts of the UI + int EnableAnimTools(BOOL hm) ; + //------------------------------------------------------------------ // setter for m_iViewMode inline void SetViewMode(unsigned int p_iNew) @@ -444,6 +448,13 @@ private: // Used by HandleTreeViewPopup(). int HandleTreeViewPopup2(WPARAM wParam,LPARAM lParam); + //------------------------------------------------------------------ + // Render skeleton + int RenderSkeleton (aiNode* piNode,const aiMatrix4x4& piMatrix, + const aiMatrix4x4& parent); + + + private: // view mode @@ -485,6 +496,10 @@ private: // Colors used to draw the checker pattern (for the // texture viewer as background ) D3DXVECTOR4 m_avCheckerColors[2]; + + // View projection matrix + aiMatrix4x4 mViewProjection; + aiVector3D vPos; }; #endif // AV_DISPLAY_H_INCLUDE \ No newline at end of file diff --git a/tools/assimp_view/Material.cpp b/tools/assimp_view/Material.cpp index 01353dfcb..9e3ce7dc5 100644 --- a/tools/assimp_view/Material.cpp +++ b/tools/assimp_view/Material.cpp @@ -825,9 +825,12 @@ int CMaterialManager::CreateMaterial( } else { + int flags = 0; + aiGetMaterialInteger(pcMat,AI_MATKEY_TEXFLAGS_DIFFUSE(0),&flags); + // 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)) + if (pcMesh->piDiffuseTexture && !(flags & aiTextureFlags_IgnoreAlpha) && HasAlphaPixels(pcMesh->piDiffuseTexture)) { int iVal; @@ -911,11 +914,14 @@ int CMaterialManager::CreateMaterial( // This is a workaround for some meshes in the DX SDK (e.g. tiny.x) // FIX: Added this check to the x-loader, but the line remains to // catch other loader doing the same ... - if (0.0f == pcMesh->fShininess) - { + if (0.0f == pcMesh->fShininess){ pcMesh->eShadingMode = aiShadingMode_Gouraud; } + int two_sided = 0; + aiGetMaterialInteger(pcMat,AI_MATKEY_TWOSIDED,&two_sided); + pcMesh->twosided = (two_sided != 0); + // 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) @@ -969,7 +975,7 @@ int CMaterialManager::CreateMaterial( return 2; } } - this->m_iShaderCount++; + m_iShaderCount++; // build macros for the HLSL compiler unsigned int iCurrent = 0; @@ -990,7 +996,6 @@ int CMaterialManager::CreateMaterial( ++iCurrent; - if (mapV == aiTextureMapMode_Wrap) sMacro[iCurrent].Name = "AV_WRAPV"; else if (mapV == aiTextureMapMode_Mirror) @@ -1303,8 +1308,10 @@ int CMaterialManager::SetupMaterial ( } } - - + // disable culling, if necessary + if (pcMesh->twosided && g_sOptions.bCulling) { + g_piDevice->SetRenderState(D3DRS_CULLMODE,D3DCULL_NONE); + } // setup the correct shader technique to be used for drawing if( g_sCaps.PixelShaderVersion < D3DPS_VERSION(2,0)) @@ -1340,6 +1347,11 @@ int CMaterialManager::EndMaterial (AssetHelper::MeshHelper* pcMesh) pcMesh->piEffect->EndPass(); pcMesh->piEffect->End(); + // reenable culling if necessary + if (pcMesh->twosided && g_sOptions.bCulling) { + g_piDevice->SetRenderState(D3DRS_CULLMODE,D3DCULL_CCW); + } + return 1; } }; // end namespace AssimpView \ No newline at end of file diff --git a/tools/assimp_view/MeshRenderer.cpp b/tools/assimp_view/MeshRenderer.cpp index f3730dc11..0efcf8a20 100644 --- a/tools/assimp_view/MeshRenderer.cpp +++ b/tools/assimp_view/MeshRenderer.cpp @@ -60,8 +60,17 @@ int CMeshRenderer::DrawUnsorted(unsigned int iIndex) g_piDevice->SetIndices(g_pcAsset->apcMeshes[iIndex]->piIB); + D3DPRIMITIVETYPE type; + switch (g_pcAsset->pcScene->mMeshes[iIndex]->mPrimitiveTypes) { + case aiPrimitiveType_POINT: + type = D3DPT_POINTLIST;break; + case aiPrimitiveType_LINE: + type = D3DPT_LINELIST;break; + case aiPrimitiveType_TRIANGLE: + type = D3DPT_TRIANGLELIST;break; + } // and draw the mesh - g_piDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, + g_piDevice->DrawIndexedPrimitive(type, 0,0, g_pcAsset->pcScene->mMeshes[iIndex]->mNumVertices,0, g_pcAsset->pcScene->mMeshes[iIndex]->mNumFaces); @@ -76,6 +85,8 @@ int CMeshRenderer::DrawSorted(unsigned int iIndex,const aiMatrix4x4& mWorld) AssetHelper::MeshHelper* pcHelper = g_pcAsset->apcMeshes[iIndex]; const aiMesh* pcMesh = g_pcAsset->pcScene->mMeshes[iIndex]; + if (pcMesh->mPrimitiveTypes != aiPrimitiveType_TRIANGLE) + return DrawUnsorted(iIndex); if (pcMesh->HasBones()) return DrawUnsorted(iIndex); diff --git a/tools/assimp_view/MessageProc.cpp b/tools/assimp_view/MessageProc.cpp index e3a353d69..2c30d7264 100644 --- a/tools/assimp_view/MessageProc.cpp +++ b/tools/assimp_view/MessageProc.cpp @@ -1180,7 +1180,7 @@ INT_PTR CALLBACK SMMessageProc(HWND hwndDlg,UINT uMsg, if (IDOK == LOWORD(wParam)) { char s[30]; GetDlgItemText(hwndDlg,IDC_EDITSM,s,30); - g_smoothAngle = atof(s); + g_smoothAngle = (float)atof(s); EndDialog(hwndDlg,0); } @@ -1936,6 +1936,10 @@ __DRUNKEN_ALIEN_FROM_MARS: { g_bPlay = !g_bPlay; SetDlgItemText(g_hDlg,IDC_PLAY,(g_bPlay ? "Stop" : "Play")); + + if (g_bPlay) + EnableWindow(GetDlgItem(g_hDlg,IDC_SLIDERANIM),FALSE); + else EnableWindow(GetDlgItem(g_hDlg,IDC_SLIDERANIM),TRUE); } } // check the file history diff --git a/tools/assimp_view/SceneAnimator.h b/tools/assimp_view/SceneAnimator.h index 6bb3e2a20..2907f2471 100644 --- a/tools/assimp_view/SceneAnimator.h +++ b/tools/assimp_view/SceneAnimator.h @@ -186,6 +186,13 @@ public: return mCurrentAnimIndex; } + // ---------------------------------------------------------------------------- + /** @brief Get the current animation or NULL + */ + aiAnimation* CurrentAnim() const { + return mCurrentAnimIndex < mScene->mNumAnimations ? mScene->mAnimations[ mCurrentAnimIndex ] : NULL; + } + protected: /** Recursively creates an internal node structure matching the diff --git a/tools/assimp_view/Shaders.cpp b/tools/assimp_view/Shaders.cpp index 6ab437587..6fccb12b8 100644 --- a/tools/assimp_view/Shaders.cpp +++ b/tools/assimp_view/Shaders.cpp @@ -548,9 +548,6 @@ std::string g_szMaterialShader = std::string( "#ifdef AV_CLAMPV\n" "AddressV = CLAMP;\n" "#endif\n" - "MinFilter=LINEAR;\n" - "MagFilter=LINEAR;\n" - "MipFilter=LINEAR;\n" "};\n" "#endif // AV_DIFFUSE_TEXTUR\n" @@ -559,9 +556,6 @@ std::string g_szMaterialShader = std::string( "sampler DIFFUSE_SAMPLER2\n" "{\n" "Texture = ;\n" - "MinFilter=LINEAR;\n" - "MagFilter=LINEAR;\n" - "MipFilter=LINEAR;\n" "};\n" "#endif // AV_DIFFUSE_TEXTUR2\n" @@ -570,9 +564,6 @@ std::string g_szMaterialShader = std::string( "sampler SPECULAR_SAMPLER\n" "{\n" "Texture = ;\n" - "MinFilter=LINEAR;\n" - "MagFilter=LINEAR;\n" - "MipFilter=LINEAR;\n" "};\n" "#endif // AV_SPECULAR_TEXTUR\n" @@ -581,9 +572,6 @@ std::string g_szMaterialShader = std::string( "sampler AMBIENT_SAMPLER\n" "{\n" "Texture = ;\n" - "MinFilter=LINEAR;\n" - "MagFilter=LINEAR;\n" - "MipFilter=LINEAR;\n" "};\n" "#endif // AV_AMBIENT_TEXTUR\n" @@ -592,9 +580,6 @@ std::string g_szMaterialShader = std::string( "sampler LIGHTMAP_SAMPLER\n" "{\n" "Texture = ;\n" - "MinFilter=LINEAR;\n" - "MagFilter=LINEAR;\n" - "MipFilter=LINEAR;\n" "};\n" "#endif // AV_LIGHTMAP_TEXTURE\n" @@ -603,9 +588,6 @@ std::string g_szMaterialShader = std::string( "sampler OPACITY_SAMPLER\n" "{\n" "Texture = ;\n" - "MinFilter=LINEAR;\n" - "MagFilter=LINEAR;\n" - "MipFilter=LINEAR;\n" "};\n" "#endif // AV_OPACITY_TEXTURE\n" @@ -614,9 +596,6 @@ std::string g_szMaterialShader = std::string( "sampler EMISSIVE_SAMPLER\n" "{\n" "Texture = ;\n" - "MinFilter=LINEAR;\n" - "MagFilter=LINEAR;\n" - "MipFilter=LINEAR;\n" "};\n" "#endif // AV_EMISSIVE_TEXTUR\n" @@ -625,9 +604,6 @@ std::string g_szMaterialShader = std::string( "sampler NORMAL_SAMPLER\n" "{\n" "Texture = ;\n" - "MinFilter=LINEAR;\n" - "MagFilter=LINEAR;\n" - "MipFilter=LINEAR;\n" "};\n" "#endif // AV_NORMAL_TEXTURE\n" @@ -1278,6 +1254,8 @@ std::string g_szPassThroughShader = std::string( "sampler TEXTURE_SAMPLER = sampler_state\n" "{\n" "Texture = (TEXTURE_2D);\n" + "MinFilter = POINT;\n" + "MagFilter = POINT;\n" "};\n" // Vertex Shader output for pixel shader usage diff --git a/tools/assimp_view/assimp_view.aps b/tools/assimp_view/assimp_view.aps index 711cafaa1f521954826c3d8a4b2fe251b09a7366..d736f0da9dd790006e3e9383063a955ad806384b 100644 GIT binary patch delta 314 zcmX}kJ8J?_5C!0S?moPTMLK&66H;ghA|cgcYiA(~HmQPDD!Xm$w3IAiEw&LPO@fFP zTKZE&ENuk~_u>pYIB@uw2a{gP>7~3pToY?hj8E5&Rw@p%S7wlXGsZqM4f`4YXT^AK z->F^iZ58B?L6`YJlbPa|NCKeB_Qi>~75Adi`Q3{cibJxb?xj_-(s<*KCyfgG;N&*M z&_Rpmsq{PXC>j?F-fvxeu}>aqY$%RBRG^YS4?Uvb%4yq3(yHG#K#HU2^Rxw(zX7X1L4~Md9?T?Km(_o a39*41Z-uB~e;9|*kjB&apK%yG|Na4bGjYHG diff --git a/tools/assimp_view/assimp_view.cpp b/tools/assimp_view/assimp_view.cpp index 8d4b6c756..cc3b99eda 100644 --- a/tools/assimp_view/assimp_view.cpp +++ b/tools/assimp_view/assimp_view.cpp @@ -128,10 +128,6 @@ DWORD WINAPI LoadThreadProc(LPVOID lpParameter) // get current time double fCur = (double)timeGetTime(); - // Remove all line and point meshes from the import - aiSetImportPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, - aiPrimitiveType_LINE | aiPrimitiveType_POINT); - // Call ASSIMPs C-API to load the file g_pcAsset->pcScene = (aiScene*)aiImportFile(g_szFileName, aiProcess_CalcTangentSpace | // calculate tangents and bitangents if possible @@ -456,24 +452,17 @@ int CreateAssetData() const aiMesh* mesh = g_pcAsset->pcScene->mMeshes[i]; // create the material for the mesh - if (!g_pcAsset->apcMeshes[i]->piEffect) - { + if (!g_pcAsset->apcMeshes[i]->piEffect) { CMaterialManager::Instance().CreateMaterial( g_pcAsset->apcMeshes[i],mesh); } - if (mesh->mPrimitiveTypes != aiPrimitiveType_TRIANGLE) - { - continue; - } - // create vertex buffer if(FAILED( g_piDevice->CreateVertexBuffer(sizeof(AssetHelper::Vertex) * mesh->mNumVertices, D3DUSAGE_WRITEONLY, 0, - D3DPOOL_DEFAULT, &g_pcAsset->apcMeshes[i]->piVB,NULL))) - { + D3DPOOL_DEFAULT, &g_pcAsset->apcMeshes[i]->piVB,NULL))) { MessageBox(g_hDlg,"Failed to create vertex buffer", "ASSIMP Viewer Utility",MB_OK); return 2; @@ -483,12 +472,22 @@ int CreateAssetData() if (g_pcAsset->apcMeshes[i]->piOpacityTexture || 1.0f != g_pcAsset->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: assert(false); + }; + // check whether we can use 16 bit indices - if (mesh->mNumFaces * 3 >= 65536) - { + if (mesh->mNumFaces * 3 >= 65536) { // create 32 bit index buffer if(FAILED( g_piDevice->CreateIndexBuffer( 4 * - mesh->mNumFaces * 3, + mesh->mNumFaces * nidx, D3DUSAGE_WRITEONLY | dwUsage, D3DFMT_INDEX32, D3DPOOL_DEFAULT, @@ -505,17 +504,16 @@ int CreateAssetData() 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 < 3;++a) + for (unsigned int a = 0; a < nidx;++a) { *pbData++ = mesh->mFaces[x].mIndices[a]; } } } - else - { + else { // create 16 bit index buffer if(FAILED( g_piDevice->CreateIndexBuffer( 2 * - mesh->mNumFaces * 3, + mesh->mNumFaces * nidx, D3DUSAGE_WRITEONLY | dwUsage, D3DFMT_INDEX16, D3DPOOL_DEFAULT, @@ -532,7 +530,7 @@ int CreateAssetData() 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 < 3;++a) + for (unsigned int a = 0; a < nidx;++a) { *pbData++ = (uint16_t)mesh->mFaces[x].mIndices[a]; } @@ -542,8 +540,7 @@ int CreateAssetData() // collect weights on all vertices. Quick and careless std::vector > weightsPerVertex( mesh->mNumVertices); - for( unsigned int a = 0; a < mesh->mNumBones; a++) - { + 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)); @@ -560,19 +557,16 @@ int CreateAssetData() pbData2->vNormal = aiVector3D(0.0f,0.0f,0.0f); else pbData2->vNormal = mesh->mNormals[x]; - if (NULL == mesh->mTangents) - { + if (NULL == mesh->mTangents) { pbData2->vTangent = aiVector3D(0.0f,0.0f,0.0f); pbData2->vBitangent = aiVector3D(0.0f,0.0f,0.0f); } - else - { + else { pbData2->vTangent = mesh->mTangents[x]; pbData2->vBitangent = mesh->mBitangents[x]; } - if (mesh->HasVertexColors( 0)) - { + if (mesh->HasVertexColors( 0)) { pbData2->dColorDiffuse = D3DCOLOR_ARGB( ((unsigned char)std::max( std::min( mesh->mColors[0][x].a * 255.0f, 255.0f),0.0f)), ((unsigned char)std::max( std::min( mesh->mColors[0][x].r * 255.0f, 255.0f),0.0f)), @@ -582,16 +576,14 @@ int CreateAssetData() else pbData2->dColorDiffuse = D3DCOLOR_ARGB(0xFF,0xff,0xff,0xff); // ignore a third texture coordinate component - if (mesh->HasTextureCoords( 0)) - { + 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)) - { + if (mesh->HasTextureCoords( 1)) { pbData2->vTextureUV2 = aiVector2D( mesh->mTextureCoords[1][x].x, mesh->mTextureCoords[1][x].y); @@ -599,8 +591,7 @@ int CreateAssetData() else pbData2->vTextureUV2 = aiVector2D(0.5f,0.5f); // Bone indices and weights - if( mesh->HasBones()) - { + 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); @@ -623,8 +614,7 @@ int CreateAssetData() g_pcAsset->apcMeshes[i]->piVB->Unlock(); // now generate the second vertex buffer, holding all normals - if (!g_pcAsset->apcMeshes[i]->piVBNormals) - { + if (!g_pcAsset->apcMeshes[i]->piVBNormals) { GenerateNormalsAsLineList(g_pcAsset->apcMeshes[i],mesh); } } diff --git a/tools/assimp_view/assimp_view.rc b/tools/assimp_view/assimp_view.rc index b7b89f9c7..116be7620 100644 --- a/tools/assimp_view/assimp_view.rc +++ b/tools/assimp_view/assimp_view.rc @@ -96,7 +96,7 @@ BEGIN LTEXT "[A]",IDC_STATIC,552,348,11,8 LTEXT "[Z]",IDC_STATIC,552,358,11,8 LTEXT "[R]",IDC_STATIC,552,368,11,8 - COMBOBOX IDC_COMBO1,353,361,84,30,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_COMBO1,353,361,84,30,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP CONTROL 149,IDC_STATIC,"Static",SS_BITMAP | SS_CENTERIMAGE,470,0,114,9 CONTROL 148,IDC_STATIC,"Static",SS_BITMAP | SS_CENTERIMAGE,470,229,114,9 CONTROL 147,IDC_STATIC,"Static",SS_BITMAP | SS_CENTERIMAGE,470,336,114,9