Merge branch 'Assimp_Settings' of https://github.com/OTHGMars/Torque3D into Preview4_0

This commit is contained in:
Areloch 2019-05-25 01:08:16 -05:00
commit ef226f6a65
25 changed files with 4541 additions and 1508 deletions

View file

@ -27,6 +27,7 @@
#include "ts/assimp/assimpAppMesh.h"
#include "materials/materialManager.h"
#include "ts/tsMaterialList.h"
#include "core/stream/fileStream.h"
// assimp include files.
#include <assimp/cimport.h>
@ -34,6 +35,8 @@
#include <assimp/postprocess.h>
#include <assimp/types.h>
U32 AssimpAppMaterial::sDefaultMatNumber = 0;
String AppMaterial::cleanString(const String& str)
{
String cleanStr(str);
@ -52,7 +55,8 @@ String AppMaterial::cleanString(const String& str)
AssimpAppMaterial::AssimpAppMaterial(const char* matName)
{
name = matName;
name = ColladaUtils::getOptions().matNamePrefix;
name += matName;
// Set some defaults
flags |= TSMaterialList::S_Wrap;
@ -67,9 +71,12 @@ AssimpAppMaterial::AssimpAppMaterial(aiMaterial* mtl) :
name = matName.C_Str();
if (name.isEmpty())
{
name = cleanString(TSShapeLoader::getShapePath().getFileName());;
name = cleanString(TSShapeLoader::getShapePath().getFileName());
name += "_defMat";
name += String::ToString("%d", sDefaultMatNumber);
sDefaultMatNumber++;
}
name = ColladaUtils::getOptions().matNamePrefix + name;
Con::printf("[ASSIMP] Loading Material: %s", name.c_str());
#ifdef TORQUE_DEBUG
enumerateMaterialProperties(mtl);
@ -125,7 +132,7 @@ void AssimpAppMaterial::initMaterial(const Torque::Path& path, Material* mat) co
if (dStrcmp("MASK", opacityMode.C_Str()) == 0)
{
translucent = true;
blendOp = Material::LerpAlpha;
blendOp = Material::None;
float cutoff;
if (AI_SUCCESS == mAIMat->Get("$mat.gltf.alphaCutoff", 0, 0, cutoff))
@ -134,10 +141,16 @@ void AssimpAppMaterial::initMaterial(const Torque::Path& path, Material* mat) co
mat->mAlphaTest = true;
}
}
else if (dStrcmp("OPAQUE", opacityMode.C_Str()) != 0)
else if (dStrcmp("BLEND", opacityMode.C_Str()) == 0)
{
translucent = true;
blendOp = Material::LerpAlpha;
mat->mAlphaTest = false;
}
else
{ // OPAQUE
translucent = false;
blendOp = Material::LerpAlpha; // Make default so it doesn't get written to materials.cs
}
}
}
@ -157,14 +170,14 @@ void AssimpAppMaterial::initMaterial(const Torque::Path& path, Material* mat) co
{
torquePath = texName.C_Str();
if (!torquePath.isEmpty())
mat->mDiffuseMapFilename[0] = cleanTextureName(torquePath, cleanFile);
mat->mDiffuseMapFilename[0] = cleanTextureName(torquePath, cleanFile, path, false);
}
if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_TEXTURE(aiTextureType_NORMALS, 0), texName))
{
torquePath = texName.C_Str();
if (!torquePath.isEmpty())
mat->mNormalMapFilename[0] = cleanTextureName(torquePath, cleanFile);
mat->mNormalMapFilename[0] = cleanTextureName(torquePath, cleanFile, path, false);
}
#ifdef TORQUE_PBR_MATERIALS
@ -177,27 +190,25 @@ void AssimpAppMaterial::initMaterial(const Torque::Path& path, Material* mat) co
if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_TEXTURE(aiTextureType_UNKNOWN, 0), texName))
rmName = texName.C_Str();
//if (aoName.isNotEmpty() && (aoName == rmName))
// mat->mOrmMapFilename[0] = cleanTextureName(aoName, cleanFile); // It's an ORM map
//else if (aoName.isNotEmpty() || rmName.isNotEmpty())
mat->mIsSRGb[0] = true;
if (aoName.isNotEmpty() || rmName.isNotEmpty())
{ // If we have either map, fill all three slots
if (rmName.isNotEmpty())
{
mat->mRoughMapFilename[0] = cleanTextureName(rmName, cleanFile); // Roughness
mat->mRoughMapFilename[0] = cleanTextureName(rmName, cleanFile, path, false); // Roughness
mat->mSmoothnessChan[0] = 1.0f;
mat->mInvertSmoothness = (floatVal == 1.0f);
mat->mMetalMapFilename[0] = cleanTextureName(rmName, cleanFile); // Metallic
mat->mMetalMapFilename[0] = cleanTextureName(rmName, cleanFile, path, false); // Metallic
mat->mMetalChan[0] = 2.0f;
}
if (aoName.isNotEmpty())
{
mat->mAOMapFilename[0] = cleanTextureName(aoName, cleanFile); // occlusion
mat->mAOMapFilename[0] = cleanTextureName(aoName, cleanFile, path, false); // occlusion
mat->mAOChan[0] = 0.0f;
}
else
{
mat->mAOMapFilename[0] = cleanTextureName(rmName, cleanFile); // occlusion
mat->mAOMapFilename[0] = cleanTextureName(rmName, cleanFile, path, false); // occlusion
mat->mAOChan[0] = 0.0f;
}
}
@ -207,7 +218,7 @@ void AssimpAppMaterial::initMaterial(const Torque::Path& path, Material* mat) co
{
torquePath = texName.C_Str();
if (!torquePath.isEmpty())
mat->mSpecularMapFilename[0] = cleanTextureName(torquePath, cleanFile);
mat->mSpecularMapFilename[0] = cleanTextureName(torquePath, cleanFile, path, false);
}
/*LinearColorF specularColor(1.0f, 1.0f, 1.0f, 1.0f);
@ -234,22 +245,70 @@ void AssimpAppMaterial::initMaterial(const Torque::Path& path, Material* mat) co
mat->mDoubleSided = doubleSided;
}
String AssimpAppMaterial::cleanTextureName(String& texName, String& shapeName)
String AssimpAppMaterial::cleanTextureName(String& texName, String& shapeName, const Torque::Path& path, bool nameOnly /*= false*/)
{
Torque::Path foundPath;
String cleanStr;
if (texName[0] == '*')
{
{ // It's an embedded texture reference. Make the cached name and return
cleanStr = shapeName;
cleanStr += "_cachedTex";
cleanStr += texName.substr(1);
return cleanStr;
}
// See if the file exists
bool fileFound = false;
String testPath = path.getPath();
testPath += '/';
testPath += texName;
testPath.replace('\\', '/');
fileFound = Torque::FS::IsFile(testPath);
cleanStr = texName;
cleanStr.replace('\\', '/');
if (fileFound)
{
if (cleanStr.equal(texName))
return cleanStr;
foundPath = testPath;
}
else
{
cleanStr = texName;
cleanStr.replace('\\', '/');
// See if the file is in a sub-directory of the shape
Vector<String> foundFiles;
Torque::Path inPath(cleanStr);
String mainDotCsDir = Platform::getMainDotCsDir();
mainDotCsDir += "/";
S32 results = Torque::FS::FindByPattern(Torque::Path(mainDotCsDir + path.getPath() + "/"), inPath.getFullFileName(), true, foundFiles);
if (results == 0 || foundFiles.size() == 0) // Not under shape directory, try the full tree
results = Torque::FS::FindByPattern(Torque::Path(mainDotCsDir), inPath.getFullFileName(), true, foundFiles);
if (results > 0 && foundFiles.size() > 0)
{
fileFound = true;
foundPath = foundFiles[0];
}
}
if (fileFound)
{
if (nameOnly)
cleanStr = foundPath.getFullFileName();
else
{ // Unless the file is in the same directory as the materials.cs (covered above)
// we need to set the full path from the root directory. If we use "subdirectory/file.ext",
// the material manager won't find the image file, but it will be found the next time the
// material is loaded from file. If we use "./subdirectory/file.ext", the image will be found
// now, but not the next time it's loaded from file...
S32 rootLength = dStrlen(Platform::getMainDotCsDir());
cleanStr = foundPath.getFullPathWithoutRoot().substr(rootLength-1);
}
}
else if (nameOnly)
cleanStr += " (Not Found)";
return cleanStr;
}

View file

@ -40,7 +40,6 @@ class AssimpAppMaterial : public AppMaterial
#ifdef TORQUE_DEBUG
void enumerateMaterialProperties(aiMaterial* mtl);
#endif
static String cleanTextureName(String& texName, String& shapeName);
public:
@ -51,6 +50,9 @@ public:
String getName() const { return name; }
Material* createMaterial(const Torque::Path& path) const;
void initMaterial(const Torque::Path& path, Material* mat) const;
static String cleanTextureName(String& texName, String& shapeName, const Torque::Path& path, bool nameOnly = false);
static U32 sDefaultMatNumber;
};
#endif // _ASSIMP_APPMATERIAL_H_

View file

@ -79,7 +79,7 @@ void AssimpAppMesh::lockMesh(F32 t, const MatrixF& objOffset)
uvs.reserve(mMeshData->mNumVertices);
normals.reserve(mMeshData->mNumVertices);
bool flipNormals = Con::getBoolVariable("$Assimp::FlipNormals", false);
bool flipNormals = ColladaUtils::getOptions().invertNormals;
bool noUVFound = false;
for (U32 i = 0; i<mMeshData->mNumVertices; i++)
@ -203,14 +203,17 @@ void AssimpAppMesh::lockMesh(F32 t, const MatrixF& objOffset)
MatrixF boneTransform;
AssimpAppNode::assimpToTorqueMat(mMeshData->mBones[b]->mOffsetMatrix, boneTransform);
Point3F boneScale = boneTransform.getScale();
if (boneScale != Point3F::One)
Point3F bonePos = boneTransform.getPosition();
if (boneScale != Point3F::One && ColladaUtils::getOptions().ignoreNodeScale)
{
Point3F scaleMult = Point3F::One / boneScale;
Point3F scalePos = boneTransform.getPosition();
boneTransform.scale(scaleMult);
scalePos /= scaleMult;
boneTransform.setPosition(scalePos);
bonePos /= scaleMult;
}
bonePos *= ColladaUtils::getOptions().unit;
boneTransform.setPosition(bonePos);
initialTransforms.push_back(boneTransform);
//Weights

View file

@ -91,22 +91,30 @@ MatrixF AssimpAppNode::getTransform(F32 time)
else {
// no parent (ie. root level) => scale by global shape <unit>
mLastTransform.identity();
mLastTransform.scale(ColladaUtils::getOptions().unit);
if (!isBounds())
convertMat(mLastTransform);
//mLastTransform.scale(ColladaUtils::getOptions().unit);
}
// If this node is animated in the active sequence, fetch the animated transform
MatrixF mat(true);
if (sActiveSequence)
{
MatrixF mat(true);
getAnimatedTransform(mat, time, sActiveSequence);
mLastTransform.mul(mat);
}
else
mLastTransform.mul(mNodeTransform);
mat = mNodeTransform;
// Remove node scaling?
Point3F nodeScale = mat.getScale();
if (nodeScale != Point3F::One && appParent && ColladaUtils::getOptions().ignoreNodeScale)
{
nodeScale.x = nodeScale.x ? (1.0f / nodeScale.x) : 0;
nodeScale.y = nodeScale.y ? (1.0f / nodeScale.y) : 0;
nodeScale.z = nodeScale.z ? (1.0f / nodeScale.z) : 0;
mat.scale(nodeScale);
}
mLastTransform.mul(mat);
mLastTransformTime = time;
return mLastTransform;
}
@ -280,12 +288,9 @@ void AssimpAppNode::convertMat(MatrixF& outMat)
{
MatrixF rot(true);
// This is copied directly from ColladaUtils::convertTransform()
// ColladaUtils::getOptions().upAxis has been temporarily replaced with $Assimp::OverrideUpAxis for testing
// We need a plan for how the full set of assimp import options and settings is going to be managed.
switch (Con::getIntVariable("$Assimp::OverrideUpAxis", 2))
switch (ColladaUtils::getOptions().upAxis)
{
case 0: //UPAXISTYPE_X_UP:
case UPAXISTYPE_X_UP:
// rotate 90 around Y-axis, then 90 around Z-axis
rot(0, 0) = 0.0f; rot(1, 0) = 1.0f;
rot(1, 1) = 0.0f; rot(2, 1) = 1.0f;
@ -295,7 +300,7 @@ void AssimpAppNode::convertMat(MatrixF& outMat)
outMat.mulL(rot);
break;
case 1: //UPAXISTYPE_Y_UP:
case UPAXISTYPE_Y_UP:
// rotate 180 around Y-axis, then 90 around X-axis
rot(0, 0) = -1.0f;
rot(1, 1) = 0.0f; rot(2, 1) = 1.0f;
@ -305,7 +310,7 @@ void AssimpAppNode::convertMat(MatrixF& outMat)
outMat.mulL(rot);
break;
case 2: //UPAXISTYPE_Z_UP:
case UPAXISTYPE_Z_UP:
default:
// nothing to do
break;

View file

@ -58,8 +58,8 @@ AssimpAppSequence::AssimpAppSequence(aiAnimation *a) :
}
}
S32 timeFactor = Con::getIntVariable("$Assimp::AnimTiming", 1);
S32 fpsRequest = Con::getIntVariable("$Assimp::AnimFPS", 30);
S32 timeFactor = ColladaUtils::getOptions().animTiming;
S32 fpsRequest = ColladaUtils::getOptions().animFPS;
if (timeFactor == 0)
{ // Timing specified in frames
fps = mClamp(fpsRequest, 5 /*TSShapeLoader::MinFrameRate*/, TSShapeLoader::MaxFrameRate);
@ -70,10 +70,7 @@ AssimpAppSequence::AssimpAppSequence(aiAnimation *a) :
else
{ // Timing specified in seconds or ms depending on format
if (maxEndTime > 1000.0f || mAnim->mDuration > 1000.0f)
{
timeFactor = 1000.0f; // If it's more than 1000 seconds, assume it's ms.
Con::setIntVariable("$Assimp::AnimTiming", 1000);
}
timeFactor = mClamp(timeFactor, 1, 1000);
minFrameTime /= (F32)timeFactor;

View file

@ -39,6 +39,7 @@
#include "core/util/tVector.h"
#include "core/strings/findMatch.h"
#include "core/strings/stringUnit.h"
#include "core/stream/fileStream.h"
#include "core/fileObject.h"
#include "ts/tsShape.h"
@ -133,29 +134,16 @@ void AssimpShapeLoader::enumerateScene()
// Post-Processing
unsigned int ppsteps =
Con::getBoolVariable("$Assimp::ConvertToLeftHanded", false) ? aiProcess_ConvertToLeftHanded : 0 |
Con::getBoolVariable("$Assimp::CalcTangentSpace", false) ? aiProcess_CalcTangentSpace : 0 |
Con::getBoolVariable("$Assimp::JoinIdenticalVertices", false) ? aiProcess_JoinIdenticalVertices : 0 |
Con::getBoolVariable("$Assimp::ValidateDataStructure", false) ? aiProcess_ValidateDataStructure : 0 |
Con::getBoolVariable("$Assimp::ImproveCacheLocality", false) ? aiProcess_ImproveCacheLocality : 0 |
Con::getBoolVariable("$Assimp::RemoveRedundantMaterials", false) ? aiProcess_RemoveRedundantMaterials : 0 |
Con::getBoolVariable("$Assimp::FindDegenerates", false) ? aiProcess_FindDegenerates : 0 |
Con::getBoolVariable("$Assimp::FindInvalidData", false) ? aiProcess_FindInvalidData : 0 |
Con::getBoolVariable("$Assimp::GenUVCoords", false) ? aiProcess_GenUVCoords : 0 |
Con::getBoolVariable("$Assimp::TransformUVCoords", false) ? aiProcess_TransformUVCoords : 0 |
Con::getBoolVariable("$Assimp::FindInstances", false) ? aiProcess_FindInstances : 0 |
Con::getBoolVariable("$Assimp::LimitBoneWeights", false) ? aiProcess_LimitBoneWeights : 0 |
Con::getBoolVariable("$Assimp::OptimizeMeshes", false) ? aiProcess_OptimizeMeshes | aiProcess_OptimizeGraph : 0 |
0;
if(Con::getBoolVariable("$Assimp::FlipUVs", true))
ppsteps |= aiProcess_FlipUVs;
if(Con::getBoolVariable("$Assimp::FlipWindingOrder", false))
ppsteps |= aiProcess_FlipWindingOrder;
if(Con::getBoolVariable("$Assimp::Triangulate", true))
ppsteps |= aiProcess_Triangulate;
(ColladaUtils::getOptions().convertLeftHanded ? aiProcess_MakeLeftHanded : 0) |
(ColladaUtils::getOptions().reverseWindingOrder ? aiProcess_FlipWindingOrder : 0) |
(ColladaUtils::getOptions().calcTangentSpace ? aiProcess_CalcTangentSpace : 0) |
(ColladaUtils::getOptions().joinIdenticalVerts ? aiProcess_JoinIdenticalVertices : 0) |
(ColladaUtils::getOptions().removeRedundantMats ? aiProcess_RemoveRedundantMaterials : 0) |
(ColladaUtils::getOptions().genUVCoords ? aiProcess_GenUVCoords : 0) |
(ColladaUtils::getOptions().transformUVCoords ? aiProcess_TransformUVCoords : 0) |
(ColladaUtils::getOptions().flipUVCoords ? aiProcess_FlipUVs : 0) |
(ColladaUtils::getOptions().findInstances ? aiProcess_FindInstances : 0) |
(ColladaUtils::getOptions().limitBoneWeights ? aiProcess_LimitBoneWeights : 0);
if (Con::getBoolVariable("$Assimp::OptimizeMeshes", false))
ppsteps |= aiProcess_OptimizeMeshes | aiProcess_OptimizeGraph;
@ -163,34 +151,21 @@ void AssimpShapeLoader::enumerateScene()
if (Con::getBoolVariable("$Assimp::SplitLargeMeshes", false))
ppsteps |= aiProcess_SplitLargeMeshes;
// Mandatory options
//ppsteps |= aiProcess_ValidateDataStructure | aiProcess_Triangulate | aiProcess_ImproveCacheLocality;
ppsteps |= aiProcess_Triangulate;
//aiProcess_SortByPType | // make 'clean' meshes which consist of a single typ of primitives
aiPropertyStore* props = aiCreatePropertyStore();
//aiSetImportPropertyInteger(props, AI_CONFIG_IMPORT_TER_MAKE_UVS, 1);
//aiSetImportPropertyInteger(props, AI_CONFIG_PP_SBP_REMOVE, (aiProcessPreset_TargetRealtime_Quality
// | aiProcess_FlipWindingOrder | aiProcess_FlipUVs
// | aiProcess_CalcTangentSpace
// | aiProcess_FixInfacingNormals)
// & ~aiProcess_RemoveRedundantMaterials);
//aiSetImportPropertyInteger(props, AI_CONFIG_GLOB_MEASURE_TIME, 1);
//aiSetImportPropertyFloat(props, AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE, 80.f);
//aiSetImportPropertyInteger(props,AI_CONFIG_PP_PTV_KEEP_HIERARCHY,1);
struct aiLogStream shapeLog;
shapeLog = aiGetPredefinedLogStream(aiDefaultLogStream_FILE, "assimp.log");
struct aiLogStream shapeLog = aiGetPredefinedLogStream(aiDefaultLogStream_STDOUT, NULL);
shapeLog.callback = assimpLogCallback;
shapeLog.user = 0;
aiAttachLogStream(&shapeLog);
#ifdef TORQUE_DEBUG
aiEnableVerboseLogging(true);
#endif
//c = aiGetPredefinedLogStream(aiDefaultLogStream_STDOUT, NULL);
//aiAttachLogStream(&c);
// Attempt to import with Assimp.
//mScene = importer.ReadFile(shapePath.getFullPath().c_str(), (aiProcessPreset_TargetRealtime_Quality | aiProcess_FlipWindingOrder | aiProcess_FlipUVs | aiProcess_CalcTangentSpace)
// & ~aiProcess_RemoveRedundantMaterials);
mScene = (aiScene*)aiImportFileExWithProperties(shapePath.getFullPath().c_str(), ppsteps, NULL, props);
aiReleasePropertyStore(props);
@ -200,11 +175,38 @@ void AssimpShapeLoader::enumerateScene()
Con::printf("[ASSIMP] Mesh Count: %d", mScene->mNumMeshes);
Con::printf("[ASSIMP] Material Count: %d", mScene->mNumMaterials);
// Set import options (if they are not set to override)
if (ColladaUtils::getOptions().unit <= 0.0f)
{
F64 unit;
if (!getMetaDouble("UnitScaleFactor", unit))
{
F32 floatVal;
S32 intVal;
if (getMetaFloat("UnitScaleFactor", floatVal))
unit = (F64)floatVal;
else if (getMetaInt("UnitScaleFactor", intVal))
unit = (F64)intVal;
else
unit = 1.0;
}
ColladaUtils::getOptions().unit = (F32)unit;
}
if (ColladaUtils::getOptions().upAxis == UPAXISTYPE_COUNT)
{
S32 upAxis;
if (!getMetaInt("UpAxis", upAxis))
upAxis = UPAXISTYPE_Z_UP;
ColladaUtils::getOptions().upAxis = (domUpAxisType) upAxis;
}
// Extract embedded textures
for (U32 i = 0; i < mScene->mNumTextures; ++i)
extractTexture(i, mScene->mTextures[i]);
// Load all the materials.
AssimpAppMaterial::sDefaultMatNumber = 0;
for ( U32 i = 0; i < mScene->mNumMaterials; i++ )
AppMesh::appMaterials.push_back(new AssimpAppMaterial(mScene->mMaterials[i]));
@ -245,8 +247,8 @@ void AssimpShapeLoader::computeBounds(Box3F& bounds)
TSShapeLoader::computeBounds(bounds);
// Check if the model origin needs adjusting
bool adjustCenter = Con::getBoolVariable("$Assimp::adjustCenter", false); //ColladaUtils::getOptions().adjustCenter
bool adjustFloor = Con::getBoolVariable("$Assimp::adjustFloor", false); //ColladaUtils::getOptions().adjustFloor
bool adjustCenter = ColladaUtils::getOptions().adjustCenter;
bool adjustFloor = ColladaUtils::getOptions().adjustFloor;
if (bounds.isValidBox() && (adjustCenter || adjustFloor))
{
// Compute shape offset
@ -289,6 +291,126 @@ void AssimpShapeLoader::computeBounds(Box3F& bounds)
}
}
bool AssimpShapeLoader::fillGuiTreeView(const char* sourceShapePath, GuiTreeViewCtrl* tree)
{
Assimp::Importer importer;
Torque::Path path(sourceShapePath);
String cleanFile = AppMaterial::cleanString(path.getFileName());
// Attempt to import with Assimp.
const aiScene* shapeScene = importer.ReadFile(path.getFullPath().c_str(), (aiProcessPreset_TargetRealtime_Quality | aiProcess_CalcTangentSpace)
& ~aiProcess_RemoveRedundantMaterials & ~aiProcess_GenSmoothNormals);
if (!shapeScene)
return false;
mScene = shapeScene;
// Initialize tree
tree->removeItem(0);
S32 meshItem = tree->insertItem(0, "Meshes", String::ToString("%i", shapeScene->mNumMeshes));
S32 matItem = tree->insertItem(0, "Materials", String::ToString("%i", shapeScene->mNumMaterials));
S32 animItem = tree->insertItem(0, "Animations", String::ToString("%i", shapeScene->mNumAnimations));
//S32 lightsItem = tree->insertItem(0, "Lights", String::ToString("%i", shapeScene->mNumLights));
//S32 texturesItem = tree->insertItem(0, "Textures", String::ToString("%i", shapeScene->mNumTextures));
//Details!
U32 numPolys = 0;
U32 numVerts = 0;
for (U32 i = 0; i < shapeScene->mNumMeshes; i++)
{
tree->insertItem(meshItem, String::ToString("%s", shapeScene->mMeshes[i]->mName.C_Str()));
numPolys += shapeScene->mMeshes[i]->mNumFaces;
numVerts += shapeScene->mMeshes[i]->mNumVertices;
}
U32 defaultMatNumber = 0;
for (U32 i = 0; i < shapeScene->mNumMaterials; i++)
{
aiMaterial* aiMat = shapeScene->mMaterials[i];
aiString matName;
aiMat->Get(AI_MATKEY_NAME, matName);
String name = matName.C_Str();
if (name.isEmpty())
{
name = AppMaterial::cleanString(path.getFileName());
name += "_defMat";
name += String::ToString("%d", defaultMatNumber);
defaultMatNumber++;
}
aiString texPath;
aiMat->GetTexture(aiTextureType::aiTextureType_DIFFUSE, 0, &texPath);
String texName = texPath.C_Str();
if (texName.isEmpty())
{
aiColor3D read_color(1.f, 1.f, 1.f);
if (AI_SUCCESS == aiMat->Get(AI_MATKEY_COLOR_DIFFUSE, read_color))
texName = String::ToString("Color: (%0.3f, %0.3f, %0.3f)", (F32)read_color.r, (F32)read_color.g, (F32)read_color.b);
else
texName = "No Texture";
}
else
texName = AssimpAppMaterial::cleanTextureName(texName, cleanFile, sourceShapePath, true);
tree->insertItem(matItem, String::ToString("%s", name.c_str()), String::ToString("%s", texName.c_str()));
}
for (U32 i = 0; i < shapeScene->mNumAnimations; i++)
{
String sequenceName = shapeScene->mAnimations[i]->mName.C_Str();
if (sequenceName.isEmpty())
sequenceName = "ambient";
tree->insertItem(animItem, sequenceName.c_str());
}
U32 numNodes = 0;
if (shapeScene->mRootNode)
{
S32 nodesItem = tree->insertItem(0, "Nodes", "");
addNodeToTree(nodesItem, shapeScene->mRootNode, tree, numNodes);
tree->setItemValue(nodesItem, String::ToString("%i", numNodes));
}
U32 numMetaTags = shapeScene->mMetaData ? shapeScene->mMetaData->mNumProperties : 0;
if (numMetaTags)
addMetaDataToTree(shapeScene->mMetaData, tree);
F64 unit;
if (!getMetaDouble("UnitScaleFactor", unit))
unit = 1.0f;
S32 upAxis;
if (!getMetaInt("UpAxis", upAxis))
upAxis = UPAXISTYPE_Z_UP;
/*for (U32 i = 0; i < shapeScene->mNumLights; i++)
{
treeObj->insertItem(lightsItem, String::ToString("%s", shapeScene->mLights[i]->mType));
}*/
// Store shape information in the tree control
tree->setDataField(StringTable->insert("_nodeCount"), 0, avar("%d", numNodes));
tree->setDataField(StringTable->insert("_meshCount"), 0, avar("%d", shapeScene->mNumMeshes));
tree->setDataField(StringTable->insert("_polygonCount"), 0, avar("%d", numPolys));
tree->setDataField(StringTable->insert("_materialCount"), 0, avar("%d", shapeScene->mNumMaterials));
tree->setDataField(StringTable->insert("_lightCount"), 0, avar("%d", shapeScene->mNumLights));
tree->setDataField(StringTable->insert("_animCount"), 0, avar("%d", shapeScene->mNumAnimations));
tree->setDataField(StringTable->insert("_textureCount"), 0, avar("%d", shapeScene->mNumTextures));
tree->setDataField(StringTable->insert("_vertCount"), 0, avar("%d", numVerts));
tree->setDataField(StringTable->insert("_metaTagCount"), 0, avar("%d", numMetaTags));
tree->setDataField(StringTable->insert("_unit"), 0, avar("%g", (F32)unit));
if (upAxis == UPAXISTYPE_X_UP)
tree->setDataField(StringTable->insert("_upAxis"), 0, "X_AXIS");
else if (upAxis == UPAXISTYPE_Y_UP)
tree->setDataField(StringTable->insert("_upAxis"), 0, "Y_AXIS");
else
tree->setDataField(StringTable->insert("_upAxis"), 0, "Z_AXIS");
return true;
}
void AssimpShapeLoader::updateMaterialsScript(const Torque::Path &path)
{
Torque::Path scriptPath(path);
@ -306,7 +428,7 @@ void AssimpShapeLoader::updateMaterialsScript(const Torque::Path &path)
if ( Sim::findObject( MATMGR->getMapEntry( mat->getName() ), mappedMat ) )
{
// Only update existing materials if forced to
if (Con::getBoolVariable("$Assimp::ForceUpdateMats", false))
if (ColladaUtils::getOptions().forceUpdateMaterials)
{
mat->initMaterial(scriptPath, mappedMat);
persistMgr.setDirty(mappedMat);
@ -351,20 +473,37 @@ bool AssimpShapeLoader::canLoadCachedDTS(const Torque::Path& path)
return false;
}
void AssimpShapeLoader::assimpLogCallback(const char* message, char* user)
{
Con::printf("[Assimp log message] %s", StringUnit::getUnit(message, 0, "\n"));
}
bool AssimpShapeLoader::ignoreNode(const String& name)
{
// Do not add AssimpFbx dummy nodes to the TSShape. See: Assimp::FBX::ImportSettings::preservePivots
// https://github.com/assimp/assimp/blob/master/code/FBXImportSettings.h#L116-L135
if (name.find("_$AssimpFbx$_") != String::NPos)
return true;
return false;
if (FindMatch::isMatchMultipleExprs(ColladaUtils::getOptions().alwaysImport, name, false))
return false;
return FindMatch::isMatchMultipleExprs(ColladaUtils::getOptions().neverImport, name, false);
}
bool AssimpShapeLoader::ignoreMesh(const String& name)
{
if (FindMatch::isMatchMultipleExprs(ColladaUtils::getOptions().alwaysImportMesh, name, false))
return false;
else
return FindMatch::isMatchMultipleExprs(ColladaUtils::getOptions().neverImportMesh, name, false);
}
void AssimpShapeLoader::detectDetails()
{
// Set LOD option
bool singleDetail = true;
switch (Con::getIntVariable("$Assimp::lodType", 0))
switch (ColladaUtils::getOptions().lodType)
{
case ColladaUtils::ImportOptions::DetectDTS:
// Check for a baseXX->startXX hierarchy at the top-level, if we find
@ -395,7 +534,7 @@ void AssimpShapeLoader::detectDetails()
break;
}
AssimpAppMesh::fixDetailSize(singleDetail, Con::getIntVariable("$Assimp::singleDetailSize", 2));
AssimpAppMesh::fixDetailSize(singleDetail, ColladaUtils::getOptions().singleDetailSize);
}
void AssimpShapeLoader::extractTexture(U32 index, aiTexture* pTex)
@ -448,6 +587,148 @@ void AssimpShapeLoader::extractTexture(U32 index, aiTexture* pTex)
}
}
void AssimpShapeLoader::addNodeToTree(S32 parentItem, aiNode* node, GuiTreeViewCtrl* tree, U32& nodeCount)
{
// Add this node
S32 nodeItem = parentItem;
String nodeName = node->mName.C_Str();
if (!ignoreNode(nodeName))
{
if (nodeName.isEmpty())
nodeName = "null";
nodeItem = tree->insertItem(parentItem, nodeName.c_str(), String::ToString("%i", node->mNumChildren));
nodeCount++;
}
// Add any child nodes
for (U32 n = 0; n < node->mNumChildren; ++n)
addNodeToTree(nodeItem, node->mChildren[n], tree, nodeCount);
}
void AssimpShapeLoader::addMetaDataToTree(const aiMetadata* metaData, GuiTreeViewCtrl* tree)
{
S32 metaItem = tree->insertItem(0, "MetaData", String::ToString("%i", metaData->mNumProperties));
aiString valString;
aiVector3D valVec;
for (U32 n = 0; n < metaData->mNumProperties; ++n)
{
String keyStr = metaData->mKeys[n].C_Str();
keyStr += ": ";
switch (metaData->mValues[n].mType)
{
case AI_BOOL:
keyStr += ((bool)metaData->mValues[n].mData) ? "true" : "false";
break;
case AI_INT32:
keyStr += String::ToString(*((S32*)(metaData->mValues[n].mData)));
break;
case AI_UINT64:
keyStr += String::ToString("%I64u", *((U64*)metaData->mValues[n].mData));
break;
case AI_FLOAT:
keyStr += String::ToString(*((F32*)metaData->mValues[n].mData));
break;
case AI_DOUBLE:
keyStr += String::ToString(*((F64*)metaData->mValues[n].mData));
break;
case AI_AISTRING:
metaData->Get<aiString>(metaData->mKeys[n], valString);
keyStr += valString.C_Str();
break;
case AI_AIVECTOR3D:
metaData->Get<aiVector3D>(metaData->mKeys[n], valVec);
keyStr += String::ToString("%f, %f, %f", valVec.x, valVec.y, valVec.z);
break;
default:
break;
}
tree->insertItem(metaItem, keyStr.c_str(), String::ToString("%i", n));
}
}
bool AssimpShapeLoader::getMetabool(const char* key, bool& boolVal)
{
if (!mScene || !mScene->mMetaData)
return false;
String keyStr = key;
for (U32 n = 0; n < mScene->mMetaData->mNumProperties; ++n)
{
if (keyStr.equal(mScene->mMetaData->mKeys[n].C_Str(), String::NoCase))
{
if (mScene->mMetaData->mValues[n].mType == AI_BOOL)
{
boolVal = (bool)mScene->mMetaData->mValues[n].mData;
return true;
}
}
}
return false;
}
bool AssimpShapeLoader::getMetaInt(const char* key, S32& intVal)
{
if (!mScene || !mScene->mMetaData)
return false;
String keyStr = key;
for (U32 n = 0; n < mScene->mMetaData->mNumProperties; ++n)
{
if (keyStr.equal(mScene->mMetaData->mKeys[n].C_Str(), String::NoCase))
{
if (mScene->mMetaData->mValues[n].mType == AI_INT32)
{
intVal = *((S32*)(mScene->mMetaData->mValues[n].mData));
return true;
}
}
}
return false;
}
bool AssimpShapeLoader::getMetaFloat(const char* key, F32& floatVal)
{
if (!mScene || !mScene->mMetaData)
return false;
String keyStr = key;
for (U32 n = 0; n < mScene->mMetaData->mNumProperties; ++n)
{
if (keyStr.equal(mScene->mMetaData->mKeys[n].C_Str(), String::NoCase))
{
if (mScene->mMetaData->mValues[n].mType == AI_FLOAT)
{
floatVal = *((F32*)mScene->mMetaData->mValues[n].mData);
return true;
}
}
}
return false;
}
bool AssimpShapeLoader::getMetaDouble(const char* key, F64& doubleVal)
{
if (!mScene || !mScene->mMetaData)
return false;
String keyStr = key;
for (U32 n = 0; n < mScene->mMetaData->mNumProperties; ++n)
{
if (keyStr.equal(mScene->mMetaData->mKeys[n].C_Str(), String::NoCase))
{
if (mScene->mMetaData->mValues[n].mType == AI_DOUBLE)
{
doubleVal = *((F64*)mScene->mMetaData->mValues[n].mData);
return true;
}
}
}
return false;
}
//-----------------------------------------------------------------------------
/// This function is invoked by the resource manager based on file extension.
TSShape* assimpLoadShape(const Torque::Path &path)
@ -489,6 +770,14 @@ TSShape* assimpLoadShape(const Torque::Path &path)
return NULL;
}
// Allow TSShapeConstructor object to override properties
ColladaUtils::getOptions().reset();
TSShapeConstructor* tscon = TSShapeConstructor::findShapeConstructor(path.getFullPath());
if (tscon)
{
ColladaUtils::getOptions() = tscon->mOptions;
}
AssimpShapeLoader loader;
TSShape* tss = loader.generateShape(path);
if (tss)
@ -510,57 +799,30 @@ TSShape* assimpLoadShape(const Torque::Path &path)
return tss;
}
DefineEngineFunction(GetShapeInfo, GuiTreeViewCtrl*, (String filePath), ,
"Returns a list of supported shape formats in filter form.\n"
"Example output: DSQ Files|*.dsq|COLLADA Files|*.dae|")
DefineEngineFunction(GetShapeInfo, bool, (const char* shapePath, const char* ctrl), ,
"(string shapePath, GuiTreeViewCtrl ctrl) Collect scene information from "
"a shape file and store it in a GuiTreeView control. This function is "
"used by the assimp import gui to show a preview of the scene contents "
"prior to import, and is probably not much use for anything else.\n"
"@param shapePath shape filename\n"
"@param ctrl GuiTreeView control to add elements to\n"
"@return true if successful, false otherwise\n"
"@ingroup Editors\n"
"@internal")
{
Assimp::Importer importer;
GuiTreeViewCtrl* treeObj = new GuiTreeViewCtrl();
treeObj->registerObject();
Torque::Path path = Torque::Path(filePath);
// Attempt to import with Assimp.
const aiScene* shapeScene = importer.ReadFile(path.getFullPath().c_str(), (aiProcessPreset_TargetRealtime_Quality | aiProcess_CalcTangentSpace)
& ~aiProcess_RemoveRedundantMaterials & ~aiProcess_GenSmoothNormals);
//Populate info
S32 meshItem = treeObj->insertItem(0, "Shape", String::ToString("%i", shapeScene->mNumMeshes));
S32 matItem = treeObj->insertItem(0, "Materials", String::ToString("%i", shapeScene->mNumMaterials));
S32 animItem = treeObj->insertItem(0, "Animations", String::ToString("%i", shapeScene->mNumAnimations));
S32 lightsItem = treeObj->insertItem(0, "Lights", String::ToString("%i", shapeScene->mNumLights));
S32 texturesItem = treeObj->insertItem(0, "Textures", String::ToString("%i", shapeScene->mNumTextures));
//S32 meshItem = ->insertItem(0, "Cameras", String::ToString("%s", shapeScene->mNumCameras));
//Details!
for (U32 i = 0; i < shapeScene->mNumMeshes; i++)
GuiTreeViewCtrl* tree;
if (!Sim::findObject(ctrl, tree))
{
treeObj->insertItem(meshItem, String::ToString("%s", shapeScene->mMeshes[i]->mName.C_Str()));
Con::errorf("enumColladaScene::Could not find GuiTreeViewCtrl '%s'", ctrl);
return false;
}
for (U32 i = 0; i < shapeScene->mNumMaterials; i++)
{
aiMaterial* aiMat = shapeScene->mMaterials[i];
// Check if a cached DTS is available => no need to import the source file
// if we can load the DTS instead
Torque::Path path(shapePath);
if (AssimpShapeLoader::canLoadCachedDTS(path))
return false;
aiString matName;
aiMat->Get(AI_MATKEY_NAME, matName);
aiString texPath;
aiMat->GetTexture(aiTextureType::aiTextureType_DIFFUSE, 0, &texPath);
treeObj->insertItem(matItem, String::ToString("%s", matName.C_Str()), String::ToString("%s", texPath.C_Str()));
}
for (U32 i = 0; i < shapeScene->mNumAnimations; i++)
{
treeObj->insertItem(animItem, String::ToString("%s", shapeScene->mAnimations[i]->mName.C_Str()));
}
/*for (U32 i = 0; i < shapeScene->mNumLights; i++)
{
treeObj->insertItem(lightsItem, String::ToString("%s", shapeScene->mLights[i]->mType));
}*/
return treeObj;
}
AssimpShapeLoader loader;
return loader.fillGuiTreeView(shapePath, tree);
}

View file

@ -28,6 +28,9 @@
#endif
#include <assimp/texture.h>
class GuiTreeViewCtrl;
struct aiNode;
struct aiMetadata;
//-----------------------------------------------------------------------------
class AssimpShapeLoader : public TSShapeLoader
{
@ -37,9 +40,18 @@ protected:
const struct aiScene* mScene;
virtual bool ignoreNode(const String& name);
virtual bool ignoreMesh(const String& name);
void detectDetails();
void extractTexture(U32 index, aiTexture* pTex);
private:
void addNodeToTree(S32 parentItem, aiNode* node, GuiTreeViewCtrl* tree, U32& nodeCount);
void addMetaDataToTree(const aiMetadata* metaData, GuiTreeViewCtrl* tree);
bool getMetabool(const char* key, bool& boolVal);
bool getMetaInt(const char* key, S32& intVal);
bool getMetaFloat(const char* key, F32& floatVal);
bool getMetaDouble(const char* key, F64& doubleVal);
public:
AssimpShapeLoader();
~AssimpShapeLoader();
@ -51,7 +63,10 @@ public:
void computeBounds(Box3F& bounds);
bool fillGuiTreeView(const char* shapePath, GuiTreeViewCtrl* tree);
static bool canLoadCachedDTS(const Torque::Path& path);
static void assimpLogCallback(const char* message, char* user);
};
#endif // _ASSIMP_SHAPELOADER_H_

View file

@ -81,6 +81,13 @@ namespace ColladaUtils
NumLodTypes
};
enum eAnimTimingType
{
FrameCount = 0,
Seconds = 1,
Milliseconds = 1000
};
domUpAxisType upAxis; // Override for the collada <up_axis> element
F32 unit; // Override for the collada <unit> element
eLodType lodType; // LOD type option
@ -97,6 +104,22 @@ namespace ColladaUtils
bool forceUpdateMaterials; // Force update of materials.cs
bool useDiffuseNames; // Use diffuse texture as the material name
// Assimp specific preprocess import options
bool convertLeftHanded; // Convert to left handed coordinate system.
bool calcTangentSpace; // Calculate tangents and bitangents, if possible.
bool genUVCoords; // Convert spherical, cylindrical, box and planar mapping to proper UVs.
bool transformUVCoords; // Preprocess UV transformations (scaling, translation ...)
bool flipUVCoords; // This step flips all UV coordinates along the y-axis and adjusts material settings
// and bitangents accordingly.\nAssimp uses TL(0,0):BR(1,1). T3D uses TL(0,1):BR(1,0).
bool findInstances; // Search for instanced meshes and remove them by references to one master.
bool limitBoneWeights; // Limit bone weights to 4 per vertex.
bool joinIdenticalVerts; // Identifies and joins identical vertex data sets within all imported meshes.
bool reverseWindingOrder; // This step adjusts the output face winding order to be clockwise. The default face winding order is counter clockwise.
bool invertNormals; // Reverse the normal vector direction for all normals.
bool removeRedundantMats; // Removes redundant materials.
eAnimTimingType animTiming; // How to import timing data as frames, seconds or milliseconds
S32 animFPS; // FPS value to use if timing is set in frames and the animations does not have an fps set
ImportOptions()
{
reset();
@ -119,6 +142,20 @@ namespace ColladaUtils
adjustFloor = false;
forceUpdateMaterials = false;
useDiffuseNames = false;
convertLeftHanded = false;
calcTangentSpace = false;
genUVCoords = false;
transformUVCoords = false;
flipUVCoords = true;
findInstances = false;
limitBoneWeights = false;
joinIdenticalVerts = true;
reverseWindingOrder = true;
invertNormals = false;
removeRedundantMats = true;
animTiming = Seconds;
animFPS = 30;
}
};

View file

@ -71,6 +71,14 @@ ImplementEnumType( TSShapeConstructorLodType,
{ ColladaUtils::ImportOptions::TrailingNumber, "TrailingNumber" },
EndImplementEnumType;
ImplementEnumType(TSShapeConstructorAnimType,
"\n\n"
"@ingroup TSShapeConstructor" )
{ ColladaUtils::ImportOptions::FrameCount, "Frames" },
{ ColladaUtils::ImportOptions::Seconds, "Seconds" },
{ ColladaUtils::ImportOptions::Milliseconds, "Milliseconds" },
EndImplementEnumType;
//-----------------------------------------------------------------------------
@ -150,6 +158,21 @@ TSShapeConstructor::TSShapeConstructor()
mOptions.adjustFloor = false;
mOptions.forceUpdateMaterials = false;
mOptions.useDiffuseNames = false;
mOptions.convertLeftHanded = false;
mOptions.calcTangentSpace = false;
mOptions.genUVCoords = false;
mOptions.transformUVCoords = false;
mOptions.flipUVCoords = true;
mOptions.findInstances = false;
mOptions.limitBoneWeights = false;
mOptions.joinIdenticalVerts = true;
mOptions.reverseWindingOrder = true;
mOptions.invertNormals = false;
mOptions.removeRedundantMats = true;
mOptions.animTiming = ColladaUtils::ImportOptions::Seconds;
mOptions.animFPS = 30;
mShape = NULL;
}
@ -292,6 +315,35 @@ void TSShapeConstructor::initPersistFields()
"Forces update of the materials.cs file in the same folder as the COLLADA "
"(.dae) file, even if Materials already exist. No effect for DTS files.\n"
"Normally only Materials that are not already defined are written to materials.cs." );
// Fields added for assimp options
addField( "convertLeftHanded", TypeBool, Offset(mOptions.convertLeftHanded, TSShapeConstructor),
"Convert to left handed coordinate system." );
addField( "calcTangentSpace", TypeBool, Offset(mOptions.calcTangentSpace, TSShapeConstructor),
"Calculate tangents and bitangents, if possible." );
addField( "genUVCoords", TypeBool, Offset(mOptions.genUVCoords, TSShapeConstructor),
"Convert spherical, cylindrical, box and planar mapping to proper UVs." );
addField( "transformUVCoords", TypeBool, Offset(mOptions.transformUVCoords, TSShapeConstructor),
"Preprocess UV transformations (scaling, translation ...)." );
addField( "flipUVCoords", TypeBool, Offset(mOptions.flipUVCoords, TSShapeConstructor),
"This step flips all UV coordinates along the y-axis and adjusts material settings and bitangents accordingly.\n"
"Assimp uses TL(0,0):BR(1,1). T3D uses TL(0,1):BR(1,0). This will be needed for most textured models." );
addField( "findInstances", TypeBool, Offset(mOptions.findInstances, TSShapeConstructor),
"Search for instanced meshes and remove them by references to one master." );
addField( "limitBoneWeights", TypeBool, Offset(mOptions.limitBoneWeights, TSShapeConstructor),
"Limit bone weights to 4 per vertex." );
addField( "joinIdenticalVerts", TypeBool, Offset(mOptions.joinIdenticalVerts, TSShapeConstructor),
"Identifies and joins identical vertex data sets within all imported meshes." );
addField( "reverseWindingOrder", TypeBool, Offset(mOptions.reverseWindingOrder, TSShapeConstructor),
"This step adjusts the output face winding order to be clockwise. The default assimp face winding order is counter clockwise." );
addField( "invertNormals", TypeBool, Offset(mOptions.invertNormals, TSShapeConstructor),
"Reverse the normal vector direction for all normals." );
addField( "removeRedundantMats", TypeBool, Offset(mOptions.removeRedundantMats, TSShapeConstructor),
"Removes redundant materials." );
addField( "animTiming", TYPEID< ColladaUtils::ImportOptions::eAnimTimingType >(), Offset(mOptions.animTiming, TSShapeConstructor),
"How to import timing data as frames, seconds or milliseconds." );
addField("animFPS", TypeS32, Offset(mOptions.animFPS, TSShapeConstructor),
"FPS value to use if timing is set in frames and the animations does not have an fps set.");
endGroup( "Collada" );
addGroup( "Sequences" );

View file

@ -328,9 +328,11 @@ public:
typedef domUpAxisType TSShapeConstructorUpAxis;
typedef ColladaUtils::ImportOptions::eLodType TSShapeConstructorLodType;
typedef ColladaUtils::ImportOptions::eAnimTimingType TSShapeConstructorAnimType;
DefineEnumType( TSShapeConstructorUpAxis );
DefineEnumType(TSShapeConstructorLodType);
DefineEnumType(TSShapeConstructorAnimType);
class TSShapeConstructorMethodActionCallback
{