mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-04-21 12:25:30 +00:00
Merge pull request #1338 from marauder2k9-torque/AssimpImporter-update
Assimp importer update
This commit is contained in:
commit
70f546aafe
1741 changed files with 196865 additions and 62422 deletions
|
|
@ -23,7 +23,6 @@
|
|||
#include "platform/platform.h"
|
||||
#include "ts/collada/colladaExtensions.h"
|
||||
#include "ts/assimp/assimpAppMesh.h"
|
||||
#include "ts/assimp/assimpAppNode.h"
|
||||
|
||||
// assimp include files.
|
||||
#include <assimp/cimport.h>
|
||||
|
|
@ -36,10 +35,107 @@ S32 AssimpAppMesh::fixedSize = 2;
|
|||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void AssimpAppMesh::computeBounds(Box3F& bounds)
|
||||
{
|
||||
bounds = Box3F::Invalid;
|
||||
|
||||
if (isSkin())
|
||||
{
|
||||
// Compute bounds for skinned mesh
|
||||
Vector<MatrixF> boneTransforms;
|
||||
boneTransforms.setSize(nodeIndex.size());
|
||||
|
||||
// Calculate bone transformations
|
||||
for (S32 iBone = 0; iBone < boneTransforms.size(); iBone++) {
|
||||
MatrixF nodeMat = bones[iBone]->getNodeTransform(TSShapeLoader::DefaultTime);
|
||||
TSShapeLoader::zapScale(nodeMat); // Remove scaling to ensure uniform transformation
|
||||
boneTransforms[iBone].mul(nodeMat, initialTransforms[iBone]);
|
||||
}
|
||||
|
||||
// Transform vertices using weighted bone transformations
|
||||
Vector<Point3F> transformedVerts;
|
||||
transformedVerts.setSize(initialVerts.size());
|
||||
transformedVerts.fill(Point3F::Zero);
|
||||
|
||||
for (S32 iWeight = 0; iWeight < vertexIndex.size(); iWeight++) {
|
||||
const S32 vertIndex = vertexIndex[iWeight];
|
||||
const MatrixF& deltaTransform = boneTransforms[boneIndex[iWeight]];
|
||||
|
||||
Point3F weightedVert;
|
||||
deltaTransform.mulP(initialVerts[vertIndex], &weightedVert);
|
||||
weightedVert *= weight[iWeight];
|
||||
|
||||
transformedVerts[vertIndex] += weightedVert;
|
||||
}
|
||||
|
||||
// Extend bounds using the transformed vertices
|
||||
for (const auto& vert : transformedVerts) {
|
||||
bounds.extend(vert);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MatrixF transform = getMeshTransform(TSShapeLoader::DefaultTime);
|
||||
TSShapeLoader::zapScale(transform);
|
||||
|
||||
for (S32 iVert = 0; iVert < points.size(); iVert++)
|
||||
{
|
||||
Point3F p;
|
||||
transform.mulP(points[iVert], &p);
|
||||
bounds.extend(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TSMesh* AssimpAppMesh::constructTSMesh()
|
||||
{
|
||||
TSMesh* tsmesh;
|
||||
if (isSkin())
|
||||
{
|
||||
TSSkinMesh* tsskin = new TSSkinMesh();
|
||||
tsmesh = tsskin;
|
||||
|
||||
// Copy skin elements
|
||||
tsskin->weight = weight;
|
||||
tsskin->boneIndex = boneIndex;
|
||||
tsskin->vertexIndex = vertexIndex;
|
||||
tsskin->batchData.nodeIndex = nodeIndex;
|
||||
tsskin->batchData.initialTransforms = initialTransforms;
|
||||
tsskin->batchData.initialVerts = initialVerts;
|
||||
tsskin->batchData.initialNorms = initialNorms;
|
||||
}
|
||||
else
|
||||
{
|
||||
tsmesh = new TSMesh();
|
||||
}
|
||||
|
||||
// Copy mesh elements
|
||||
tsmesh->mVerts = points;
|
||||
tsmesh->mNorms = normals;
|
||||
tsmesh->mTverts = uvs;
|
||||
tsmesh->mPrimitives = primitives;
|
||||
tsmesh->mIndices = indices;
|
||||
tsmesh->mColors = colors;
|
||||
tsmesh->mTverts2 = uv2s;
|
||||
|
||||
// Finish initializing the shape
|
||||
computeBounds(tsmesh->mBounds);
|
||||
tsmesh->setFlags(flags);
|
||||
tsmesh->updateMeshFlags();
|
||||
//tsmesh->computeBounds();
|
||||
tsmesh->numFrames = numFrames;
|
||||
tsmesh->numMatFrames = numMatFrames;
|
||||
tsmesh->vertsPerFrame = vertsPerFrame;
|
||||
tsmesh->createTangents(tsmesh->mVerts, tsmesh->mNorms);
|
||||
tsmesh->mEncodedNorms.set(NULL, 0);
|
||||
|
||||
return tsmesh;
|
||||
}
|
||||
|
||||
AssimpAppMesh::AssimpAppMesh(const struct aiMesh* mesh, AssimpAppNode* node)
|
||||
: mMeshData(mesh), appNode(node)
|
||||
{
|
||||
Con::printf("[ASSIMP] Mesh Created: %s", getName());
|
||||
Con::printf("[ASSIMP] Mesh Created: %s for Node: %s", getName(), node->getName());
|
||||
|
||||
// See if it's a skinned mesh
|
||||
mIsSkinMesh = false;
|
||||
|
|
@ -191,14 +287,18 @@ void AssimpAppMesh::lockMesh(F32 t, const MatrixF& objOffset)
|
|||
tmpBoneIndex.setSize(totalWeights);
|
||||
tmpVertexIndex.setSize(totalWeights);
|
||||
|
||||
// Count the total number of weights for all of the bones.
|
||||
Map<String, aiNode*> boneLookup;
|
||||
for (U32 b = 0; b < boneCount; b++) {
|
||||
boneLookup[mMeshData->mBones[b]->mName.C_Str()] =
|
||||
AssimpAppNode::findChildNodeByName(mMeshData->mBones[b]->mName.C_Str(), appNode->mScene->mRootNode);
|
||||
}
|
||||
|
||||
for (U32 b = 0; b < boneCount; b++)
|
||||
{
|
||||
String name = mMeshData->mBones[b]->mName.C_Str();
|
||||
aiNode* nodePtr = AssimpAppNode::findChildNodeByName(mMeshData->mBones[b]->mName.C_Str(), appNode->mScene->mRootNode);
|
||||
if (!nodePtr)
|
||||
bones[b] = new AssimpAppNode(appNode->mScene, appNode->mNode);
|
||||
else
|
||||
bones[b] = new AssimpAppNode(appNode->mScene, nodePtr);
|
||||
const aiBone* bone = mMeshData->mBones[b];
|
||||
aiNode* nodePtr = boneLookup[bone->mName.C_Str()];
|
||||
bones[b] = nodePtr ? new AssimpAppNode(appNode->mScene, nodePtr) : new AssimpAppNode(appNode->mScene, appNode->mNode);
|
||||
|
||||
MatrixF boneTransform;
|
||||
AssimpAppNode::assimpToTorqueMat(mMeshData->mBones[b]->mOffsetMatrix, boneTransform);
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ protected:
|
|||
static S32 fixedSize; ///< The fixed detail size value for all geometry
|
||||
|
||||
public:
|
||||
void computeBounds(Box3F& bounds) override;
|
||||
TSMesh* constructTSMesh() override;
|
||||
|
||||
AssimpAppMesh(const struct aiMesh* mesh, AssimpAppNode* node);
|
||||
~AssimpAppMesh()
|
||||
|
|
|
|||
|
|
@ -34,55 +34,34 @@
|
|||
aiAnimation* AssimpAppNode::sActiveSequence = NULL;
|
||||
F32 AssimpAppNode::sTimeMultiplier = 1.0f;
|
||||
|
||||
AssimpAppNode::AssimpAppNode(const struct aiScene* scene, const struct aiNode* node, AssimpAppNode* parent)
|
||||
: mInvertMeshes(false),
|
||||
mLastTransformTime(TSShapeLoader::DefaultTime - 1),
|
||||
mDefaultTransformValid(false)
|
||||
AssimpAppNode::AssimpAppNode(const aiScene* scene, const aiNode* node, AssimpAppNode* parentNode)
|
||||
: mScene(scene),
|
||||
mNode(node ? node : scene->mRootNode),
|
||||
mInvertMeshes(false),
|
||||
mLastTransformTime(TSShapeLoader::DefaultTime - 1),
|
||||
mDefaultTransformValid(false)
|
||||
{
|
||||
mScene = scene;
|
||||
mNode = node;
|
||||
appParent = parent;
|
||||
|
||||
appParent = parentNode;
|
||||
// Initialize node and parent names.
|
||||
mName = dStrdup(mNode->mName.C_Str());
|
||||
if ( dStrlen(mName) == 0 )
|
||||
{
|
||||
const char* defaultName = "null";
|
||||
mName = dStrdup(defaultName);
|
||||
}
|
||||
|
||||
mParentName = dStrdup(parent ? parent->getName() : "ROOT");
|
||||
mParentName = dStrdup(parentNode ? parentNode->mName : "ROOT");
|
||||
// Convert transformation matrix
|
||||
assimpToTorqueMat(node->mTransformation, mNodeTransform);
|
||||
Con::printf("[ASSIMP] Node Created: %s, Parent: %s", mName, mParentName);
|
||||
}
|
||||
|
||||
// Get all child nodes
|
||||
void AssimpAppNode::buildChildList()
|
||||
{
|
||||
if (!mNode)
|
||||
{
|
||||
mNode = mScene->mRootNode;
|
||||
}
|
||||
|
||||
for (U32 n = 0; n < mNode->mNumChildren; ++n) {
|
||||
mChildNodes.push_back(new AssimpAppNode(mScene, mNode->mChildren[n], this));
|
||||
}
|
||||
}
|
||||
|
||||
// Get all geometry attached to this node
|
||||
void AssimpAppNode::buildMeshList()
|
||||
{
|
||||
for (U32 n = 0; n < mNode->mNumMeshes; ++n)
|
||||
{
|
||||
const struct aiMesh* mesh = mScene->mMeshes[mNode->mMeshes[n]];
|
||||
mMeshes.push_back(new AssimpAppMesh(mesh, this));
|
||||
}
|
||||
}
|
||||
|
||||
MatrixF AssimpAppNode::getTransform(F32 time)
|
||||
{
|
||||
// Check if we can use the last computed transform
|
||||
if (time == mLastTransformTime)
|
||||
{
|
||||
return mLastTransform;
|
||||
}
|
||||
|
||||
if (appParent) {
|
||||
// Get parent node's transform
|
||||
|
|
@ -92,8 +71,8 @@ MatrixF AssimpAppNode::getTransform(F32 time)
|
|||
// no parent (ie. root level) => scale by global shape <unit>
|
||||
mLastTransform.identity();
|
||||
mLastTransform.scale(ColladaUtils::getOptions().unit * ColladaUtils::getOptions().formatScaleFactor);
|
||||
if (!isBounds())
|
||||
convertMat(mLastTransform);
|
||||
/*if (!isBounds())
|
||||
convertMat(mLastTransform);*/
|
||||
}
|
||||
|
||||
// If this node is animated in the active sequence, fetch the animated transform
|
||||
|
|
@ -121,115 +100,104 @@ MatrixF AssimpAppNode::getTransform(F32 time)
|
|||
|
||||
void AssimpAppNode::getAnimatedTransform(MatrixF& mat, F32 t, aiAnimation* animSeq)
|
||||
{
|
||||
// Find the channel for this node
|
||||
// Convert time `t` (in seconds) to a frame index
|
||||
const F32 frameTime = (t * animSeq->mTicksPerSecond + 0.5f) + 1.0f;
|
||||
|
||||
// Loop through animation channels to find the matching node
|
||||
for (U32 k = 0; k < animSeq->mNumChannels; ++k)
|
||||
{
|
||||
if (dStrcmp(mName, animSeq->mChannels[k]->mNodeName.C_Str()) == 0)
|
||||
const aiNodeAnim* nodeAnim = animSeq->mChannels[k];
|
||||
if (dStrcmp(mName, nodeAnim->mNodeName.C_Str()) != 0)
|
||||
continue;
|
||||
|
||||
Point3F translation(Point3F::Zero);
|
||||
QuatF rotation(QuatF::Identity);
|
||||
Point3F scale(Point3F::One);
|
||||
|
||||
// Interpolate Translation Keys
|
||||
if (nodeAnim->mNumPositionKeys > 0)
|
||||
{
|
||||
aiNodeAnim *nodeAnim = animSeq->mChannels[k];
|
||||
Point3F trans(Point3F::Zero);
|
||||
Point3F scale(Point3F::One);
|
||||
QuatF rot;
|
||||
rot.identity();
|
||||
// T is in seconds, convert to frames.
|
||||
F32 frame = (t * animSeq->mTicksPerSecond + 0.5f) + 1.0f;
|
||||
translation = interpolateVectorKey(nodeAnim->mPositionKeys, nodeAnim->mNumPositionKeys, frameTime);
|
||||
}
|
||||
|
||||
// Transform
|
||||
if (nodeAnim->mNumPositionKeys == 1)
|
||||
trans.set(nodeAnim->mPositionKeys[0].mValue.x, nodeAnim->mPositionKeys[0].mValue.y, nodeAnim->mPositionKeys[0].mValue.z);
|
||||
else
|
||||
{
|
||||
Point3F curPos, lastPos;
|
||||
F32 lastT = 0.0;
|
||||
for (U32 key = 0; key < nodeAnim->mNumPositionKeys; ++key)
|
||||
{
|
||||
F32 curT = sTimeMultiplier * (F32)nodeAnim->mPositionKeys[key].mTime;
|
||||
curPos.set(nodeAnim->mPositionKeys[key].mValue.x, nodeAnim->mPositionKeys[key].mValue.y, nodeAnim->mPositionKeys[key].mValue.z);
|
||||
if ((curT > frame) && (key > 0))
|
||||
{
|
||||
F32 factor = (frame - lastT) / (curT - lastT);
|
||||
trans.interpolate(lastPos, curPos, factor);
|
||||
break;
|
||||
}
|
||||
else if ((curT >= frame) || (key == nodeAnim->mNumPositionKeys - 1))
|
||||
{
|
||||
trans = curPos;
|
||||
break;
|
||||
}
|
||||
// Interpolate Rotation Keys
|
||||
if (nodeAnim->mNumRotationKeys > 0)
|
||||
{
|
||||
rotation = interpolateQuaternionKey(nodeAnim->mRotationKeys, nodeAnim->mNumRotationKeys, frameTime);
|
||||
}
|
||||
|
||||
lastT = curT;
|
||||
lastPos = curPos;
|
||||
}
|
||||
}
|
||||
// Interpolate Scaling Keys
|
||||
if (nodeAnim->mNumScalingKeys > 0)
|
||||
{
|
||||
scale = interpolateVectorKey(nodeAnim->mScalingKeys, nodeAnim->mNumScalingKeys, frameTime);
|
||||
}
|
||||
|
||||
// Rotation
|
||||
if (nodeAnim->mNumRotationKeys == 1)
|
||||
rot.set(nodeAnim->mRotationKeys[0].mValue.x, nodeAnim->mRotationKeys[0].mValue.y,
|
||||
nodeAnim->mRotationKeys[0].mValue.z, nodeAnim->mRotationKeys[0].mValue.w);
|
||||
else
|
||||
{
|
||||
QuatF curRot, lastRot;
|
||||
F32 lastT = 0.0;
|
||||
for (U32 key = 0; key < nodeAnim->mNumRotationKeys; ++key)
|
||||
{
|
||||
F32 curT = sTimeMultiplier * (F32)nodeAnim->mRotationKeys[key].mTime;
|
||||
curRot.set(nodeAnim->mRotationKeys[key].mValue.x, nodeAnim->mRotationKeys[key].mValue.y,
|
||||
nodeAnim->mRotationKeys[key].mValue.z, nodeAnim->mRotationKeys[key].mValue.w);
|
||||
if ((curT > frame) && (key > 0))
|
||||
{
|
||||
F32 factor = (frame - lastT) / (curT - lastT);
|
||||
rot.interpolate(lastRot, curRot, factor);
|
||||
break;
|
||||
}
|
||||
else if ((curT >= frame) || (key == nodeAnim->mNumRotationKeys - 1))
|
||||
{
|
||||
rot = curRot;
|
||||
break;
|
||||
}
|
||||
// Apply the interpolated transform components to the matrix
|
||||
rotation.setMatrix(&mat);
|
||||
mat.inverse();
|
||||
mat.setPosition(translation);
|
||||
mat.scale(scale);
|
||||
|
||||
lastT = curT;
|
||||
lastRot = curRot;
|
||||
}
|
||||
}
|
||||
return; // Exit after processing the matching node
|
||||
}
|
||||
|
||||
// Scale
|
||||
if (nodeAnim->mNumScalingKeys == 1)
|
||||
scale.set(nodeAnim->mScalingKeys[0].mValue.x, nodeAnim->mScalingKeys[0].mValue.y, nodeAnim->mScalingKeys[0].mValue.z);
|
||||
else
|
||||
{
|
||||
Point3F curScale, lastScale;
|
||||
F32 lastT = 0.0;
|
||||
for (U32 key = 0; key < nodeAnim->mNumScalingKeys; ++key)
|
||||
{
|
||||
F32 curT = sTimeMultiplier * (F32)nodeAnim->mScalingKeys[key].mTime;
|
||||
curScale.set(nodeAnim->mScalingKeys[key].mValue.x, nodeAnim->mScalingKeys[key].mValue.y, nodeAnim->mScalingKeys[key].mValue.z);
|
||||
if ((curT > frame) && (key > 0))
|
||||
{
|
||||
F32 factor = (frame - lastT) / (curT - lastT);
|
||||
scale.interpolate(lastScale, curScale, factor);
|
||||
break;
|
||||
}
|
||||
else if ((curT >= frame) || (key == nodeAnim->mNumScalingKeys - 1))
|
||||
{
|
||||
scale = curScale;
|
||||
break;
|
||||
}
|
||||
// Default to the static node transformation if no animation data is found
|
||||
mat = mNodeTransform;
|
||||
}
|
||||
|
||||
lastT = curT;
|
||||
lastScale = curScale;
|
||||
}
|
||||
}
|
||||
Point3F AssimpAppNode::interpolateVectorKey(const aiVectorKey* keys, U32 numKeys, F32 frameTime)
|
||||
{
|
||||
if (numKeys == 1) // Single keyframe: use it directly
|
||||
return Point3F(keys[0].mValue.x, keys[0].mValue.y, keys[0].mValue.z);
|
||||
|
||||
rot.setMatrix(&mat);
|
||||
mat.inverse();
|
||||
mat.setPosition(trans);
|
||||
mat.scale(scale);
|
||||
return;
|
||||
// Clamp frameTime to the bounds of the keyframes
|
||||
if (frameTime <= keys[0].mTime) {
|
||||
// Before the first keyframe, return the first key
|
||||
return Point3F(keys[0].mValue.x, keys[0].mValue.y, keys[0].mValue.z);
|
||||
}
|
||||
if (frameTime >= keys[numKeys - 1].mTime) {
|
||||
// After the last keyframe, return the last key
|
||||
return Point3F(keys[numKeys - 1].mValue.x, keys[numKeys - 1].mValue.y, keys[numKeys - 1].mValue.z);
|
||||
}
|
||||
|
||||
// Interpolate between the two nearest keyframes
|
||||
for (U32 i = 1; i < numKeys; ++i)
|
||||
{
|
||||
if (frameTime < keys[i].mTime)
|
||||
{
|
||||
const F32 factor = (frameTime - keys[i - 1].mTime) / (keys[i].mTime - keys[i - 1].mTime);
|
||||
Point3F start(keys[i - 1].mValue.x, keys[i - 1].mValue.y, keys[i - 1].mValue.z);
|
||||
Point3F end(keys[i].mValue.x, keys[i].mValue.y, keys[i].mValue.z);
|
||||
Point3F result;
|
||||
result.interpolate(start, end, factor);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Node not found in the animation channels
|
||||
mat = mNodeTransform;
|
||||
// Default to the last keyframe
|
||||
return Point3F(keys[numKeys - 1].mValue.x, keys[numKeys - 1].mValue.y, keys[numKeys - 1].mValue.z);
|
||||
}
|
||||
|
||||
QuatF AssimpAppNode::interpolateQuaternionKey(const aiQuatKey* keys, U32 numKeys, F32 frameTime)
|
||||
{
|
||||
if (numKeys == 1) // Single keyframe: use it directly
|
||||
return QuatF(keys[0].mValue.x, keys[0].mValue.y, keys[0].mValue.z, keys[0].mValue.w);
|
||||
|
||||
for (U32 i = 1; i < numKeys; ++i)
|
||||
{
|
||||
if (frameTime < keys[i].mTime)
|
||||
{
|
||||
const F32 factor = (frameTime - keys[i - 1].mTime) / (keys[i].mTime - keys[i - 1].mTime);
|
||||
QuatF start(keys[i - 1].mValue.x, keys[i - 1].mValue.y, keys[i - 1].mValue.z, keys[i - 1].mValue.w);
|
||||
QuatF end(keys[i].mValue.x, keys[i].mValue.y, keys[i].mValue.z, keys[i].mValue.w);
|
||||
QuatF result;
|
||||
result.interpolate(start, end, factor);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Default to the last keyframe
|
||||
return QuatF(keys[numKeys - 1].mValue.x, keys[numKeys - 1].mValue.y, keys[numKeys - 1].mValue.z, keys[numKeys - 1].mValue.w);
|
||||
}
|
||||
|
||||
bool AssimpAppNode::animatesTransform(const AppSequence* appSeq)
|
||||
|
|
@ -283,7 +251,7 @@ void AssimpAppNode::assimpToTorqueMat(const aiMatrix4x4& inAssimpMat, MatrixF& o
|
|||
(F32)inAssimpMat.c3, (F32)inAssimpMat.c4));
|
||||
|
||||
outMat.setRow(3, Point4F((F32)inAssimpMat.d1, (F32)inAssimpMat.d2,
|
||||
(F32)inAssimpMat.d3, ColladaUtils::getOptions().formatScaleFactor));// (F32)inAssimpMat.d4));
|
||||
(F32)inAssimpMat.d3, (F32)inAssimpMat.d4));
|
||||
}
|
||||
|
||||
void AssimpAppNode::convertMat(MatrixF& outMat)
|
||||
|
|
@ -304,9 +272,11 @@ void AssimpAppNode::convertMat(MatrixF& outMat)
|
|||
|
||||
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;
|
||||
rot(1, 2) = 1.0f; rot(2, 2) = 0.0f;
|
||||
rot(0, 0) = 1.0f;
|
||||
rot(1, 1) = 0.0f;
|
||||
rot(1, 2) = -1.0f;
|
||||
rot(2, 1) = 1.0f;
|
||||
rot(2, 2) = 0.0f;
|
||||
|
||||
// pre-multiply the transform by the rotation matrix
|
||||
outMat.mulL(rot);
|
||||
|
|
@ -333,3 +303,13 @@ aiNode* AssimpAppNode::findChildNodeByName(const char* nodeName, aiNode* rootNod
|
|||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AssimpAppNode::addChild(AssimpAppNode* child)
|
||||
{
|
||||
mChildNodes.push_back(child);
|
||||
}
|
||||
|
||||
void AssimpAppNode::addMesh(AssimpAppMesh* child)
|
||||
{
|
||||
mMeshes.push_back(child);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@
|
|||
#endif
|
||||
#include <assimp/scene.h>
|
||||
|
||||
class AssimpAppMesh;
|
||||
|
||||
class AssimpAppNode : public AppNode
|
||||
{
|
||||
typedef AppNode Parent;
|
||||
|
|
@ -45,25 +47,26 @@ class AssimpAppNode : public AppNode
|
|||
|
||||
MatrixF getTransform(F32 time);
|
||||
void getAnimatedTransform(MatrixF& mat, F32 t, aiAnimation* animSeq);
|
||||
void buildMeshList() override;
|
||||
void buildChildList() override;
|
||||
|
||||
Point3F interpolateVectorKey(const aiVectorKey* keys, U32 numKeys, F32 frameTime);
|
||||
QuatF interpolateQuaternionKey(const aiQuatKey* keys, U32 numKeys, F32 frameTime);
|
||||
void buildMeshList() override {};
|
||||
void buildChildList() override {};
|
||||
protected:
|
||||
|
||||
const struct aiScene* mScene;
|
||||
const struct aiNode* mNode; ///< Pointer to the assimp scene node
|
||||
AssimpAppNode* appParent; ///< Parent node
|
||||
MatrixF mNodeTransform; ///< Scene node transform converted to TorqueSpace (filled for ALL nodes)
|
||||
const aiScene* mScene;
|
||||
const aiNode* mNode; ///< Pointer to the assimp scene node
|
||||
AssimpAppNode* appParent; ///< Parent node
|
||||
MatrixF mNodeTransform; ///< Scene node transform converted to TorqueSpace (filled for ALL nodes)
|
||||
|
||||
bool mInvertMeshes; ///< True if this node's coordinate space is inverted (left handed)
|
||||
F32 mLastTransformTime; ///< Time of the last transform lookup (getTransform)
|
||||
MatrixF mLastTransform; ///< Last transform lookup (getTransform) (Only Non-Dummy Nodes)
|
||||
bool mDefaultTransformValid; ///< Flag indicating whether the defaultNodeTransform is valid
|
||||
MatrixF mDefaultNodeTransform; ///< Transform at DefaultTime (Only Non-Dummy Nodes)
|
||||
bool mInvertMeshes; ///< True if this node's coordinate space is inverted (left handed)
|
||||
F32 mLastTransformTime; ///< Time of the last transform lookup (getTransform)
|
||||
MatrixF mLastTransform; ///< Last transform lookup (getTransform) (Only Non-Dummy Nodes)
|
||||
bool mDefaultTransformValid; ///< Flag indicating whether the defaultNodeTransform is valid
|
||||
MatrixF mDefaultNodeTransform; ///< Transform at DefaultTime (Only Non-Dummy Nodes)
|
||||
|
||||
public:
|
||||
|
||||
AssimpAppNode(const struct aiScene* scene, const struct aiNode* node, AssimpAppNode* parent = 0);
|
||||
AssimpAppNode(const aiScene* scene, const aiNode* node, AssimpAppNode* parentNode = nullptr);
|
||||
virtual ~AssimpAppNode()
|
||||
{
|
||||
//
|
||||
|
|
@ -113,6 +116,9 @@ public:
|
|||
static void assimpToTorqueMat(const aiMatrix4x4& inAssimpMat, MatrixF& outMat);
|
||||
static void convertMat(MatrixF& outMat);
|
||||
static aiNode* findChildNodeByName(const char* nodeName, aiNode* rootNode);
|
||||
|
||||
void addChild(AssimpAppNode* child);
|
||||
void addMesh(AssimpAppMesh* child);
|
||||
};
|
||||
|
||||
#endif // _ASSIMP_APPNODE_H_
|
||||
|
|
|
|||
|
|
@ -12,94 +12,103 @@
|
|||
#include "ts/assimp/assimpAppSequence.h"
|
||||
#include "ts/assimp/assimpAppNode.h"
|
||||
|
||||
AssimpAppSequence::AssimpAppSequence(aiAnimation *a) :
|
||||
seqStart(0.0f),
|
||||
seqEnd(0.0f)
|
||||
AssimpAppSequence::AssimpAppSequence(aiAnimation* a)
|
||||
: seqStart(0.0f), seqEnd(0.0f), mTimeMultiplier(1.0f)
|
||||
{
|
||||
fps = 30.0f;
|
||||
// Deep copy animation structure
|
||||
mAnim = new aiAnimation(*a);
|
||||
// Deep copy channels
|
||||
mAnim->mChannels = new aiNodeAnim * [a->mNumChannels];
|
||||
for (U32 i = 0; i < a->mNumChannels; ++i) {
|
||||
mAnim->mChannels[i] = new aiNodeAnim(*a->mChannels[i]);
|
||||
}
|
||||
|
||||
// Deep copy meshes
|
||||
mAnim->mMeshChannels = new aiMeshAnim * [a->mNumMeshChannels];
|
||||
for (U32 i = 0; i < a->mNumMeshChannels; ++i) {
|
||||
mAnim->mMeshChannels[i] = new aiMeshAnim(*a->mMeshChannels[i]);
|
||||
}
|
||||
|
||||
// Deep copy name
|
||||
mAnim->mName = a->mName;
|
||||
|
||||
mSequenceName = mAnim->mName.C_Str();
|
||||
if (mSequenceName.isEmpty())
|
||||
mSequenceName = "ambient";
|
||||
Con::printf("\n[Assimp] Adding %s animation", mSequenceName.c_str());
|
||||
|
||||
fps = (a->mTicksPerSecond > 0) ? a->mTicksPerSecond : 30.0f;
|
||||
Con::printf("\n[Assimp] Adding animation: %s", mSequenceName.c_str());
|
||||
|
||||
if (a->mDuration > 0)
|
||||
{
|
||||
// torques seqEnd is in seconds, this then gets generated into frames in generateSequences()
|
||||
seqEnd = (F32)a->mDuration / fps;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (U32 i = 0; i < a->mNumChannels; ++i)
|
||||
{
|
||||
aiNodeAnim* nodeAnim = a->mChannels[i];
|
||||
// Determine the maximum keyframe time for this animation
|
||||
F32 maxKeyTime = 0.0f;
|
||||
for (U32 k = 0; k < nodeAnim->mNumPositionKeys; k++) {
|
||||
maxKeyTime = getMax(maxKeyTime, (F32)nodeAnim->mPositionKeys[k].mTime);
|
||||
}
|
||||
for (U32 k = 0; k < nodeAnim->mNumRotationKeys; k++) {
|
||||
maxKeyTime = getMax(maxKeyTime, (F32)nodeAnim->mRotationKeys[k].mTime);
|
||||
}
|
||||
for (U32 k = 0; k < nodeAnim->mNumScalingKeys; k++) {
|
||||
maxKeyTime = getMax(maxKeyTime, (F32)nodeAnim->mScalingKeys[k].mTime);
|
||||
}
|
||||
|
||||
seqEnd = getMax(seqEnd, maxKeyTime);
|
||||
}
|
||||
}
|
||||
|
||||
mTimeMultiplier = 1.0f;
|
||||
|
||||
S32 timeFactor = ColladaUtils::getOptions().animTiming;
|
||||
S32 fpsRequest = (S32)a->mTicksPerSecond;
|
||||
if (timeFactor == 0)
|
||||
{ // Timing specified in frames
|
||||
fps = mClamp(fpsRequest, 5 /*TSShapeLoader::MinFrameRate*/, TSShapeLoader::MaxFrameRate);
|
||||
mTimeMultiplier = 1.0f / fps;
|
||||
}
|
||||
else
|
||||
{ // Timing specified in seconds or ms depending on format
|
||||
if (seqEnd > 1000.0f || a->mDuration > 1000.0f)
|
||||
timeFactor = 1000.0f; // If it's more than 1000 seconds, assume it's ms.
|
||||
|
||||
timeFactor = mClamp(timeFactor, 1, 1000);
|
||||
mTimeMultiplier = 1.0f / timeFactor;
|
||||
}
|
||||
// Determine the FPS and Time Multiplier
|
||||
determineTimeMultiplier(a);
|
||||
|
||||
// Calculate sequence end time based on keyframes and multiplier
|
||||
calculateSequenceEnd(a);
|
||||
}
|
||||
|
||||
AssimpAppSequence::~AssimpAppSequence()
|
||||
{
|
||||
}
|
||||
|
||||
void AssimpAppSequence::determineTimeMultiplier(aiAnimation* a)
|
||||
{
|
||||
// Set fps from the file or use default
|
||||
fps = (a->mTicksPerSecond > 0) ? a->mTicksPerSecond : 30.0f;
|
||||
|
||||
if (fps >= 1000.0f) { // Indicates milliseconds (GLTF or similar formats)
|
||||
mTimeMultiplier = 1.0f / 1000.0f; // Convert milliseconds to seconds
|
||||
Con::printf("[Assimp] Detected milliseconds timing (FPS >= 1000). Time Multiplier: %f", mTimeMultiplier);
|
||||
}
|
||||
else if (fps > 0.0f) { // Standard FPS
|
||||
fps = mClamp(fps, 5 /*TSShapeLoader::MinFrameRate*/, TSShapeLoader::MaxFrameRate);
|
||||
mTimeMultiplier = 1.0f / fps;
|
||||
Con::printf("[Assimp] Standard FPS detected. Time Multiplier: %f", mTimeMultiplier);
|
||||
}
|
||||
else {
|
||||
// Fall back to 30 FPS as default
|
||||
mTimeMultiplier = 1.0f / 30.0f;
|
||||
Con::printf("[Assimp] FPS not specified. Using default 30 FPS. Time Multiplier: %f", mTimeMultiplier);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void AssimpAppSequence::calculateSequenceEnd(aiAnimation* a)
|
||||
{
|
||||
for (U32 i = 0; i < a->mNumChannels; ++i) {
|
||||
aiNodeAnim* nodeAnim = a->mChannels[i];
|
||||
F32 maxKeyTime = 0.0f;
|
||||
|
||||
// Calculate the maximum time across all keyframes for this channel
|
||||
for (U32 k = 0; k < nodeAnim->mNumPositionKeys; ++k) {
|
||||
maxKeyTime = getMax(maxKeyTime, (F32)nodeAnim->mPositionKeys[k].mTime);
|
||||
}
|
||||
for (U32 k = 0; k < nodeAnim->mNumRotationKeys; ++k) {
|
||||
maxKeyTime = getMax(maxKeyTime, (F32)nodeAnim->mRotationKeys[k].mTime);
|
||||
}
|
||||
for (U32 k = 0; k < nodeAnim->mNumScalingKeys; ++k) {
|
||||
maxKeyTime = getMax(maxKeyTime, (F32)nodeAnim->mScalingKeys[k].mTime);
|
||||
}
|
||||
|
||||
// Use the multiplier to convert to real sequence time
|
||||
seqEnd = mTimeMultiplier * getMax(seqEnd, maxKeyTime);
|
||||
}
|
||||
|
||||
Con::printf("[Assimp] Sequence End Time: %f seconds", seqEnd);
|
||||
}
|
||||
|
||||
|
||||
void AssimpAppSequence::setActive(bool active)
|
||||
{
|
||||
if (active)
|
||||
{
|
||||
AssimpAppNode::sActiveSequence = mAnim;
|
||||
AssimpAppNode::sTimeMultiplier = mTimeMultiplier;
|
||||
Con::printf("[Assimp] Activating sequence: %s with Time Multiplier: %f", mSequenceName.c_str(), mTimeMultiplier);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AssimpAppNode::sActiveSequence == mAnim)
|
||||
{
|
||||
AssimpAppNode::sActiveSequence = NULL;
|
||||
Con::printf("[Assimp] Deactivating sequence: %s", mSequenceName.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,11 +27,14 @@ class AssimpAppSequence : public AppSequence
|
|||
F32 seqEnd;
|
||||
F32 mTimeMultiplier; // The factor needed to convert the sequence data timestamp to seconds
|
||||
|
||||
void determineTimeMultiplier(aiAnimation* a);
|
||||
void calculateSequenceEnd(aiAnimation* a);
|
||||
|
||||
public:
|
||||
|
||||
AssimpAppSequence(aiAnimation *a);
|
||||
~AssimpAppSequence();
|
||||
|
||||
|
||||
aiAnimation *mAnim;
|
||||
|
||||
void setActive(bool active) override;
|
||||
|
|
|
|||
|
|
@ -59,8 +59,6 @@
|
|||
#include <assimp/config.h>
|
||||
#include <exception>
|
||||
|
||||
#include <assimp/Importer.hpp>
|
||||
|
||||
MODULE_BEGIN( AssimpShapeLoader )
|
||||
MODULE_INIT_AFTER( ShapeLoader )
|
||||
MODULE_INIT
|
||||
|
|
@ -124,7 +122,67 @@ AssimpShapeLoader::~AssimpShapeLoader()
|
|||
|
||||
void AssimpShapeLoader::releaseImport()
|
||||
{
|
||||
aiReleaseImport(mScene);
|
||||
}
|
||||
|
||||
void applyTransformation(aiNode* node, const aiMatrix4x4& transform) {
|
||||
node->mTransformation = transform * node->mTransformation; // Apply transformation to the node
|
||||
}
|
||||
|
||||
void applyRootTransformation(aiNode* node, const aiMatrix4x4& transform) {
|
||||
node->mTransformation = transform * node->mTransformation; // Apply transformation to the node
|
||||
|
||||
// Recursively apply to all child nodes
|
||||
for (unsigned int i = 0; i < node->mNumChildren; ++i) {
|
||||
applyRootTransformation(node->mChildren[i], transform);
|
||||
}
|
||||
}
|
||||
|
||||
void scaleScene(const aiScene* scene, F32 scaleFactor) {
|
||||
aiMatrix4x4 scaleMatrix;
|
||||
scaleMatrix = aiMatrix4x4::Scaling(aiVector3D(scaleFactor, scaleFactor, scaleFactor), scaleMatrix);
|
||||
applyTransformation(scene->mRootNode, scaleMatrix);
|
||||
}
|
||||
|
||||
void debugSceneMetaData(const aiScene* scene) {
|
||||
if (!scene->mMetaData) {
|
||||
Con::printf("[ASSIMP] No metadata available.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (U32 i = 0; i < scene->mMetaData->mNumProperties; ++i) {
|
||||
const char* key = scene->mMetaData->mKeys[i].C_Str();
|
||||
aiMetadataType type = scene->mMetaData->mValues[i].mType;
|
||||
Con::printf("[ASSIMP] Metadata key: %s", key);
|
||||
|
||||
switch (type) {
|
||||
case AI_BOOL:
|
||||
Con::printf(" Value: %d (bool)", *(bool*)scene->mMetaData->mValues[i].mData);
|
||||
break;
|
||||
case AI_INT32:
|
||||
Con::printf(" Value: %d (int)", *(S32*)scene->mMetaData->mValues[i].mData);
|
||||
break;
|
||||
case AI_UINT64:
|
||||
Con::printf(" Value: %llu (uint64)", *(U64*)scene->mMetaData->mValues[i].mData);
|
||||
break;
|
||||
case AI_FLOAT:
|
||||
Con::printf(" Value: %f (float)", *(F32*)scene->mMetaData->mValues[i].mData);
|
||||
break;
|
||||
case AI_DOUBLE:
|
||||
Con::printf(" Value: %f (double)", *(F64*)scene->mMetaData->mValues[i].mData);
|
||||
break;
|
||||
case AI_AISTRING:
|
||||
Con::printf(" Value: %s (string)", ((aiString*)scene->mMetaData->mValues[i].mData)->C_Str());
|
||||
break;
|
||||
case AI_AIVECTOR3D:
|
||||
{
|
||||
aiVector3D* vec = (aiVector3D*)scene->mMetaData->mValues[i].mData;
|
||||
Con::printf(" Value: (%f, %f, %f) (vector3d)", vec->x, vec->y, vec->z);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Con::printf(" Unknown metadata type.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AssimpShapeLoader::enumerateScene()
|
||||
|
|
@ -132,31 +190,26 @@ void AssimpShapeLoader::enumerateScene()
|
|||
TSShapeLoader::updateProgress(TSShapeLoader::Load_ReadFile, "Reading File");
|
||||
Con::printf("[ASSIMP] Attempting to load file: %s", shapePath.getFullPath().c_str());
|
||||
|
||||
// Post-Processing
|
||||
unsigned int ppsteps =
|
||||
(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);
|
||||
// Define post-processing steps
|
||||
U32 ppsteps = aiProcess_Triangulate | /*aiProcess_PreTransformVertices |*/ aiProcess_ConvertToLeftHanded & ~aiProcess_MakeLeftHanded;
|
||||
|
||||
if (Con::getBoolVariable("$Assimp::OptimizeMeshes", false))
|
||||
const auto& options = ColladaUtils::getOptions();
|
||||
if (options.calcTangentSpace) ppsteps |= aiProcess_CalcTangentSpace;
|
||||
if (options.joinIdenticalVerts) ppsteps |= aiProcess_JoinIdenticalVertices;
|
||||
if (options.removeRedundantMats) ppsteps |= aiProcess_RemoveRedundantMaterials;
|
||||
if (options.genUVCoords) ppsteps |= aiProcess_GenUVCoords;
|
||||
if (options.transformUVCoords) ppsteps |= aiProcess_TransformUVCoords;
|
||||
if (options.findInstances) ppsteps |= aiProcess_FindInstances;
|
||||
if (options.limitBoneWeights) ppsteps |= aiProcess_LimitBoneWeights;
|
||||
|
||||
if (Con::getBoolVariable("$Assimp::OptimizeMeshes", false)) {
|
||||
ppsteps |= aiProcess_OptimizeMeshes | aiProcess_OptimizeGraph;
|
||||
|
||||
if (Con::getBoolVariable("$Assimp::SplitLargeMeshes", false))
|
||||
}
|
||||
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();
|
||||
ppsteps |= aiProcess_ValidateDataStructure;
|
||||
|
||||
struct aiLogStream shapeLog = aiGetPredefinedLogStream(aiDefaultLogStream_STDOUT, NULL);
|
||||
shapeLog.callback = assimpLogCallback;
|
||||
|
|
@ -166,93 +219,155 @@ void AssimpShapeLoader::enumerateScene()
|
|||
aiEnableVerboseLogging(true);
|
||||
#endif
|
||||
|
||||
mScene = (aiScene*)aiImportFileExWithProperties(shapePath.getFullPath().c_str(), ppsteps, NULL, props);
|
||||
/*mImporter.SetPropertyInteger(AI_CONFIG_PP_PTV_KEEP_HIERARCHY, 1);
|
||||
mImporter.SetPropertyInteger(AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION, 1);
|
||||
mImporter.SetPropertyMatrix(AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION, aiMatrix4x4(1, 0, 0, 0,
|
||||
0, 0, -1, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 0, 1));*/
|
||||
|
||||
aiReleasePropertyStore(props);
|
||||
// Read the file
|
||||
mScene = mImporter.ReadFile(shapePath.getFullPath().c_str(), ppsteps);
|
||||
|
||||
if ( mScene )
|
||||
{
|
||||
Con::printf("[ASSIMP] Mesh Count: %d", mScene->mNumMeshes);
|
||||
Con::printf("[ASSIMP] Material Count: %d", mScene->mNumMaterials);
|
||||
|
||||
// Setup default units for shape format
|
||||
String importFormat;
|
||||
|
||||
String fileExt = String::ToLower(shapePath.getExtension());
|
||||
const aiImporterDesc* importerDescription = aiGetImporterDesc(fileExt.c_str());
|
||||
if (importerDescription && StringTable->insert(importerDescription->mName) == StringTable->insert("Autodesk FBX Importer"))
|
||||
{
|
||||
ColladaUtils::getOptions().formatScaleFactor = 0.01f;
|
||||
}
|
||||
|
||||
// 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]));
|
||||
|
||||
// Setup LOD checks
|
||||
detectDetails();
|
||||
|
||||
// Define the root node, and process down the chain.
|
||||
AssimpAppNode* node = new AssimpAppNode(mScene, mScene->mRootNode, 0);
|
||||
|
||||
if (!processNode(node))
|
||||
delete node;
|
||||
|
||||
// add bounds node.
|
||||
if (!boundsNode)
|
||||
{
|
||||
aiNode* req[1];
|
||||
req[0] = new aiNode("bounds");
|
||||
mScene->mRootNode->addChildren(1, req);
|
||||
|
||||
AssimpAppNode* appBounds = new AssimpAppNode(mScene, req[0]);
|
||||
if (!processNode(appBounds))
|
||||
delete appBounds;
|
||||
}
|
||||
|
||||
// Check for animations and process those.
|
||||
processAnimations();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!mScene || (mScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE) || !mScene->mRootNode) {
|
||||
Con::errorf("[ASSIMP] ERROR: Could not load file: %s", shapePath.getFullPath().c_str());
|
||||
Con::errorf("[ASSIMP] Importer error: %s", mImporter.GetErrorString());
|
||||
TSShapeLoader::updateProgress(TSShapeLoader::Load_Complete, "Import failed");
|
||||
Con::printf("[ASSIMP] Import Error: %s", aiGetErrorString());
|
||||
return;
|
||||
}
|
||||
|
||||
Con::printf("[ASSIMP] Mesh Count: %d", mScene->mNumMeshes);
|
||||
Con::printf("[ASSIMP] Material Count: %d", mScene->mNumMaterials);
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
debugSceneMetaData(mScene);
|
||||
#endif
|
||||
|
||||
ColladaUtils::getOptions().upAxis = UPAXISTYPE_Y_UP; // default to Y up for assimp.
|
||||
// Handle scaling
|
||||
configureImportUnits();
|
||||
|
||||
// Format-specific adjustments
|
||||
String fileExt = String::ToLower(shapePath.getExtension());
|
||||
const aiImporterDesc* importerDescription = aiGetImporterDesc(fileExt.c_str());
|
||||
if (importerDescription && dStrcmp(importerDescription->mName, "Autodesk FBX Importer") == 0) {
|
||||
Con::printf("[ASSIMP] Detected FBX format, checking unit scale...");
|
||||
F32 scaleFactor = ColladaUtils::getOptions().unit;
|
||||
if (scaleFactor != 1.0f) {
|
||||
Con::printf("[ASSIMP] Applying FBX scale factor: %f", scaleFactor);
|
||||
scaleScene(mScene, scaleFactor);
|
||||
}
|
||||
else
|
||||
{
|
||||
scaleScene(mScene, 0.01f);
|
||||
}
|
||||
}
|
||||
|
||||
if (fileExt == String::ToString("glb"))
|
||||
ColladaUtils::getOptions().upAxis = UPAXISTYPE_X_UP;
|
||||
|
||||
for (U32 i = 0; i < mScene->mNumTextures; ++i) {
|
||||
extractTexture(i, mScene->mTextures[i]);
|
||||
}
|
||||
|
||||
// Load all materials
|
||||
AssimpAppMaterial::sDefaultMatNumber = 0;
|
||||
for (U32 i = 0; i < mScene->mNumMaterials; ++i) {
|
||||
AppMesh::appMaterials.push_back(new AssimpAppMaterial(mScene->mMaterials[i]));
|
||||
}
|
||||
|
||||
// Setup LOD checks
|
||||
detectDetails();
|
||||
|
||||
aiMatrix4x4 sceneRoot = aiMatrix4x4(1, 0, 0, 0,
|
||||
0, 0, -1, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 0, 1);
|
||||
|
||||
applyTransformation(mScene->mRootNode, sceneRoot);
|
||||
|
||||
// Process the scene graph
|
||||
AssimpAppNode* rootNode = new AssimpAppNode(mScene, mScene->mRootNode, 0);
|
||||
if (!processNode(rootNode)) {
|
||||
delete rootNode;
|
||||
}
|
||||
|
||||
processAssimpNode(mScene->mRootNode, mScene, rootNode);
|
||||
|
||||
// Add a bounds node if none exists
|
||||
if (!boundsNode) {
|
||||
aiNode* reqNode = new aiNode("bounds");
|
||||
mScene->mRootNode->addChildren(1, &reqNode);
|
||||
reqNode->mTransformation = aiMatrix4x4();// *sceneRoot;
|
||||
AssimpAppNode* appBoundsNode = new AssimpAppNode(mScene, reqNode);
|
||||
if (!processNode(appBoundsNode)) {
|
||||
delete appBoundsNode;
|
||||
}
|
||||
}
|
||||
|
||||
// Process animations if available
|
||||
processAnimations();
|
||||
|
||||
// Clean up log stream
|
||||
aiDetachLogStream(&shapeLog);
|
||||
}
|
||||
|
||||
void AssimpShapeLoader::configureImportUnits() {
|
||||
auto& options = ColladaUtils::getOptions();
|
||||
|
||||
// Configure unit scaling
|
||||
if (options.unit <= 0.0f) {
|
||||
F64 unitScaleFactor = 1.0;
|
||||
if (!getMetaDouble("UnitScaleFactor", unitScaleFactor)) {
|
||||
F32 floatVal;
|
||||
S32 intVal;
|
||||
if (getMetaFloat("UnitScaleFactor", floatVal)) {
|
||||
unitScaleFactor = static_cast<F64>(floatVal);
|
||||
}
|
||||
else if (getMetaInt("UnitScaleFactor", intVal)) {
|
||||
unitScaleFactor = static_cast<F64>(intVal);
|
||||
}
|
||||
}
|
||||
options.unit = static_cast<F32>(unitScaleFactor);
|
||||
}
|
||||
|
||||
int upAxis = UPAXISTYPE_Z_UP;
|
||||
if (getMetaInt("UpAxis", upAxis)) {
|
||||
options.upAxis = static_cast<domUpAxisType>(upAxis);
|
||||
}
|
||||
}
|
||||
|
||||
void AssimpShapeLoader::processAssimpNode(const aiNode* node, const aiScene* scene, AssimpAppNode* parentNode)
|
||||
{
|
||||
AssimpAppNode* currNode;
|
||||
if (node == scene->mRootNode)
|
||||
{
|
||||
currNode = parentNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
currNode = new AssimpAppNode(scene, node, parentNode);
|
||||
|
||||
if (parentNode)
|
||||
{
|
||||
parentNode->addChild(currNode);
|
||||
}
|
||||
|
||||
for (U32 i = 0; i < node->mNumMeshes; i++)
|
||||
{
|
||||
U32 meshIdx = node->mMeshes[i];
|
||||
const aiMesh* mesh = scene->mMeshes[meshIdx];
|
||||
AssimpAppMesh* curMesh = new AssimpAppMesh(mesh, currNode);
|
||||
currNode->addMesh(curMesh);
|
||||
}
|
||||
}
|
||||
// Recursively process child nodes
|
||||
for (U32 i = 0; i < node->mNumChildren; i++)
|
||||
{
|
||||
processAssimpNode(node->mChildren[i], scene, currNode);
|
||||
}
|
||||
}
|
||||
|
||||
void AssimpShapeLoader::processAnimations()
|
||||
{
|
||||
// add all animations into 1 ambient animation.
|
||||
|
|
@ -261,11 +376,16 @@ void AssimpShapeLoader::processAnimations()
|
|||
|
||||
Vector<aiNodeAnim*> ambientChannels;
|
||||
F32 duration = 0.0f;
|
||||
F32 ticks = 0.0f;
|
||||
if (mScene->mNumAnimations > 0)
|
||||
{
|
||||
for (U32 i = 0; i < mScene->mNumAnimations; ++i)
|
||||
{
|
||||
aiAnimation* anim = mScene->mAnimations[i];
|
||||
|
||||
ticks = anim->mTicksPerSecond;
|
||||
|
||||
duration = 0.0f;
|
||||
for (U32 j = 0; j < anim->mNumChannels; j++)
|
||||
{
|
||||
aiNodeAnim* nodeAnim = anim->mChannels[j];
|
||||
|
|
@ -290,7 +410,7 @@ void AssimpShapeLoader::processAnimations()
|
|||
ambientSeq->mNumChannels = ambientChannels.size();
|
||||
ambientSeq->mChannels = ambientChannels.address();
|
||||
ambientSeq->mDuration = duration;
|
||||
ambientSeq->mTicksPerSecond = 24.0;
|
||||
ambientSeq->mTicksPerSecond = ticks;
|
||||
|
||||
AssimpAppSequence* defaultAssimpSeq = new AssimpAppSequence(ambientSeq);
|
||||
appSequences.push_back(defaultAssimpSeq);
|
||||
|
|
|
|||
|
|
@ -26,6 +26,13 @@
|
|||
#ifndef _TSSHAPELOADER_H_
|
||||
#include "ts/loader/tsShapeLoader.h"
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef _ASSIMP_APPNODE_H_
|
||||
#include "ts/assimp/assimpAppNode.h"
|
||||
#endif
|
||||
|
||||
#include <assimp/Importer.hpp>
|
||||
#include <assimp/texture.h>
|
||||
|
||||
class GuiTreeViewCtrl;
|
||||
|
|
@ -37,14 +44,17 @@ class AssimpShapeLoader : public TSShapeLoader
|
|||
friend TSShape* assimpLoadShape(const Torque::Path &path);
|
||||
|
||||
protected:
|
||||
const struct aiScene* mScene;
|
||||
Assimp::Importer mImporter;
|
||||
const aiScene* mScene;
|
||||
|
||||
//bool processNode(AppNode* node) override;
|
||||
bool ignoreNode(const String& name) override;
|
||||
bool ignoreMesh(const String& name) override;
|
||||
void detectDetails();
|
||||
void extractTexture(U32 index, aiTexture* pTex);
|
||||
|
||||
private:
|
||||
void processAssimpNode(const aiNode* node, const aiScene* scene, AssimpAppNode* parentNode = nullptr);
|
||||
void addNodeToTree(S32 parentItem, aiNode* node, GuiTreeViewCtrl* tree, U32& nodeCount);
|
||||
void addMetaDataToTree(const aiMetadata* metaData, GuiTreeViewCtrl* tree);
|
||||
bool getMetabool(const char* key, bool& boolVal);
|
||||
|
|
@ -59,6 +69,7 @@ public:
|
|||
|
||||
void releaseImport();
|
||||
void enumerateScene() override;
|
||||
void configureImportUnits();
|
||||
void updateMaterialsScript(const Torque::Path &path);
|
||||
void processAnimations();
|
||||
|
||||
|
|
|
|||
|
|
@ -74,11 +74,11 @@ public:
|
|||
AppMesh();
|
||||
virtual ~AppMesh();
|
||||
|
||||
void computeBounds(Box3F& bounds);
|
||||
virtual void computeBounds(Box3F& bounds);
|
||||
void computeNormals();
|
||||
|
||||
// Create a TSMesh object
|
||||
TSMesh* constructTSMesh();
|
||||
virtual TSMesh* constructTSMesh();
|
||||
|
||||
virtual const char * getName(bool allowFixed=true) = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -41,8 +41,8 @@ class AppNode
|
|||
// the reason these are tracked by AppNode is that
|
||||
// AppNode is responsible for deleting all it's children
|
||||
// and attached meshes.
|
||||
virtual void buildMeshList() = 0;
|
||||
virtual void buildChildList() = 0;
|
||||
virtual void buildMeshList() {};
|
||||
virtual void buildChildList() {};
|
||||
|
||||
protected:
|
||||
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ protected:
|
|||
|
||||
// Collect the nodes, objects and sequences for the scene
|
||||
virtual void enumerateScene() = 0;
|
||||
bool processNode(AppNode* node);
|
||||
virtual bool processNode(AppNode* node);
|
||||
virtual bool ignoreNode(const String& name) { return false; }
|
||||
virtual bool ignoreMesh(const String& name) { return false; }
|
||||
|
||||
|
|
|
|||
|
|
@ -252,7 +252,7 @@ public:
|
|||
protected:
|
||||
|
||||
U32 mMeshType;
|
||||
Box3F mBounds;
|
||||
|
||||
Point3F mCenter;
|
||||
F32 mRadius;
|
||||
F32 mVisibility;
|
||||
|
|
@ -272,7 +272,7 @@ public:
|
|||
S32 numFrames;
|
||||
S32 numMatFrames;
|
||||
S32 vertsPerFrame;
|
||||
|
||||
Box3F mBounds;
|
||||
U32 mVertOffset;
|
||||
U32 mVertSize;
|
||||
|
||||
|
|
|
|||
|
|
@ -57,9 +57,7 @@ IMPLEMENT_CALLBACK(TSShapeConstructor, onUnload, void, (), (),
|
|||
ImplementEnumType(TSShapeConstructorUpAxis,
|
||||
"Axis to use for upwards direction when importing from Collada.\n\n"
|
||||
"@ingroup TSShapeConstructor")
|
||||
{
|
||||
UPAXISTYPE_X_UP, "X_AXIS"
|
||||
},
|
||||
{ UPAXISTYPE_X_UP, "X_AXIS" },
|
||||
{ UPAXISTYPE_Y_UP, "Y_AXIS" },
|
||||
{ UPAXISTYPE_Z_UP, "Z_AXIS" },
|
||||
{ UPAXISTYPE_COUNT, "DEFAULT" }
|
||||
|
|
@ -68,9 +66,7 @@ EndImplementEnumType;
|
|||
ImplementEnumType(TSShapeConstructorLodType,
|
||||
"\n\n"
|
||||
"@ingroup TSShapeConstructor")
|
||||
{
|
||||
ColladaUtils::ImportOptions::DetectDTS, "DetectDTS"
|
||||
},
|
||||
{ ColladaUtils::ImportOptions::DetectDTS, "DetectDTS" },
|
||||
{ ColladaUtils::ImportOptions::SingleSize, "SingleSize" },
|
||||
{ ColladaUtils::ImportOptions::TrailingNumber, "TrailingNumber" },
|
||||
EndImplementEnumType;
|
||||
|
|
@ -78,9 +74,7 @@ ImplementEnumType(TSShapeConstructorLodType,
|
|||
ImplementEnumType(TSShapeConstructorAnimType,
|
||||
"\n\n"
|
||||
"@ingroup TSShapeConstructor")
|
||||
{
|
||||
ColladaUtils::ImportOptions::FrameCount, "Frames"
|
||||
},
|
||||
{ ColladaUtils::ImportOptions::FrameCount, "Frames" },
|
||||
{ ColladaUtils::ImportOptions::Seconds, "Seconds" },
|
||||
{ ColladaUtils::ImportOptions::Milliseconds, "Milliseconds" },
|
||||
EndImplementEnumType;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue