Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Engine/source/T3D/assets/assetImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2011,7 +2011,7 @@ void AssetImporter::processShapeAsset(AssetImportObject* assetItem)
{
enumColladaForImport(filePath, shapeInfo, false);
}
else if ((fileExt.compare("dts") == 0) || (fileExt.compare("dsq") == 0))
else if ((fileExt.compare("dts") == 0))
{
enumDTSForImport(filePath, shapeInfo);
}
Expand Down
259 changes: 103 additions & 156 deletions Engine/source/ts/assimp/assimpShapeLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,25 +56,89 @@
#undef new
#endif
#endif
// assimp include files.
// assimp include files.
#include <assimp/cimport.h>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include <assimp/types.h>
#include <assimp/config.h>
#include <assimp/Exporter.hpp>
#include <exception>

#if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
# define _new new(__FILE__, __LINE__)
# define new _new
#endif

extern bool gTryUseDSQs;
static bool sReadAssimp(const Torque::Path& path, TSShape*& shape);

static struct _privateRegisterAssimp
{
_privateRegisterAssimp()
{
TSShape::ShapeRegistration reg;

Assimp::Importer importer;
for (U32 i = 0; i < importer.GetImporterCount(); i++)
{
const aiImporterDesc* desc = importer.GetImporterInfo(i);

String extensions(desc->mFileExtensions);

Vector<String> tokens;
extensions.split(" ", tokens);
for (U32 t = 0; t < tokens.size(); ++t)
{
const String& ext = tokens[t];

if (ext.isEmpty() ||
ext.equal("dae", String::NoCase) || // filter out collada importer formats (for now).
ext.equal("zae", String::NoCase) ||
ext.equal("xml", String::NoCase)
)
continue;

reg.extensions.push_back({
String(desc->mName), // convert from const char*
ext
});
}

}

Assimp::Exporter exporter;

for (U32 i = 0; i < exporter.GetExportFormatCount(); ++i)
{
const aiExportFormatDesc* desc = exporter.GetExportFormatDescription(i);
String ext(desc->fileExtension);

if (ext.isEmpty() ||
ext.equal("dae", String::NoCase) || // filter out collada importer formats (for now).
ext.equal("zae", String::NoCase) ||
ext.equal("xml", String::NoCase)
)
continue;

reg.export_extensions.push_back({
String(desc->description),
ext
});
}

reg.readFunc = sReadAssimp;
reg.writeFunc = NULL;

TSShape::sRegisterFormat(reg);
}
} sStaticRegisterAssimp;

MODULE_BEGIN( AssimpShapeLoader )
MODULE_INIT_AFTER( ShapeLoader )
MODULE_INIT
{
// These are only ever used from script. with the TSShapeLoader::isSupportedFormat.
// Handy to have and should probably be a think for the other loaders to register formats.
TSShapeLoader::addFormat("DirectX X", "x");
TSShapeLoader::addFormat("Autodesk FBX", "fbx");
TSShapeLoader::addFormat("Blender 3D", "blend" );
Expand Down Expand Up @@ -391,54 +455,34 @@ void AssimpShapeLoader::getRootAxisTransform()

MatrixF rot(true);

// ===== Y-UP SOURCE =====
if (upAxis == 1)
// Build source basis
auto axisToVector = [](int axis, int sign) -> Point3F
{
if (frontAxis == 2)
{
// Y-up, Z-forward → Z-up, Y-forward
// Rotate 180° Y, then 90° X
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;
}
else if (frontAxis == 0)
{
// Y-up, X-forward → Z-up, Y-forward
// Rotate -90° around Z then 90° around X
rot(0, 0) = 0.0f; rot(0, 1) = -1.0f;
rot(1, 0) = 1.0f; rot(1, 1) = 0.0f;
rot(2, 2) = 1.0f;
}
}
Point3F v(0, 0, 0);
v[axis] = (F32)sign;
return v;
};

// ===== Z-UP SOURCE =====
if (upAxis == 2)
{
if (frontAxis == 1)
{
// Already Z-up, Y-forward → no change
}
else if (frontAxis == 0)
{
// Z-up, X-forward → rotate -90° around Z
rot(0, 0) = 0.0f; rot(0, 1) = -1.0f;
rot(1, 0) = 1.0f; rot(1, 1) = 0.0f;
}
}
Point3F forward = axisToVector(frontAxis, frontSign);
Point3F up = axisToVector(upAxis, upSign);
Point3F right = mCross(forward, up);

// ===== X-UP SOURCE =====
if (upAxis == 0)
{
if (frontAxis == 2)
{
// X-up, Z-forward → Z-up, Y-forward
// Rotate -90° around Y then -90° around Z
rot(0, 0) = 0.0f; rot(0, 1) = 0.0f; rot(0, 2) = -1.0f;
rot(1, 0) = 1.0f; rot(1, 1) = 0.0f; rot(1, 2) = 0.0f;
rot(2, 0) = 0.0f; rot(2, 1) = -1.0f; rot(2, 2) = 0.0f;
}
}
// Recompute forward
forward = mCross(up, right);

// Normalize (defensive, though they should already be unit)
right.normalize();
forward.normalize();
up.normalize();

MatrixF srcBasis(true);
srcBasis.setColumn(0, right);
srcBasis.setColumn(1, forward);
srcBasis.setColumn(2, up);

// Convert to Torque space
rot = srcBasis;
rot.inverse();

ColladaUtils::getOptions().axisCorrectionMat = rot;
}
Expand Down Expand Up @@ -689,30 +733,6 @@ bool AssimpShapeLoader::canLoadCachedDTS(const Torque::Path& path)
return false;
}

/// Check if an up-to-date cached DSQ is available for this file
bool AssimpShapeLoader::canLoadCachedDSQ(const Torque::Path& path)
{
// Generate the cached filename
Torque::Path cachedPath(path);
cachedPath.setExtension("dsq");

// Check if a cached DTS newer than this file is available
FileTime cachedModifyTime;
if (Platform::getFileTimes(cachedPath.getFullPath(), NULL, &cachedModifyTime))
{
bool forceLoad = Con::getBoolVariable("$assimp::forceLoad", false);

FileTime daeModifyTime;
if (!Platform::getFileTimes(path.getFullPath(), NULL, &daeModifyTime) ||
(!forceLoad && (Platform::compareFileTimes(cachedModifyTime, daeModifyTime) >= 0)))
{
// Original file not found, or cached DTS is newer
return true;
}
}
return false;
}

void AssimpShapeLoader::assimpLogCallback(const char* message, char* user)
{
Con::printf("[Assimp log message] %s", StringUnit::getUnit(message, 0, "\n"));
Expand Down Expand Up @@ -992,65 +1012,12 @@ bool AssimpShapeLoader::getMetaString(const char* key, String& stringVal)
}
//-----------------------------------------------------------------------------
/// This function is invoked by the resource manager based on file extension.
TSShape* assimpLoadShape(const Torque::Path &path)
static bool sReadAssimp(const Torque::Path &path, TSShape*& res_shape)
{
// TODO: add .cached.dts generation.
// Generate the cached filename
Torque::Path cachedPath(path);
bool canLoadCached = false;
bool canLoadDSQ = false;

// Check if an up-to-date cached DTS version of this file exists, and
// if so, use that instead.
if (AssimpShapeLoader::canLoadCachedDTS(path))
{
cachedPath.setExtension("cached.dts");
canLoadCached = true;
}
else if (gTryUseDSQs && AssimpShapeLoader::canLoadCachedDSQ(path))
{
cachedPath.setExtension("dsq");
canLoadDSQ = true;
}
if (canLoadCached || canLoadDSQ)
{
FileStream cachedStream;
cachedStream.open(cachedPath.getFullPath(), Torque::FS::File::Read);
if (cachedStream.getStatus() == Stream::Ok)
{
TSShape *shape = new TSShape;
bool readSuccess = false;
if (canLoadCached)
{
readSuccess = shape->read(&cachedStream);
}
else
{
readSuccess = shape->importSequences(&cachedStream, cachedPath);
}
cachedStream.close();

if (readSuccess)
{
#ifdef TORQUE_DEBUG
Con::printf("Loaded cached shape from %s", cachedPath.getFullPath().c_str());
#endif
return shape;
}
else
{
#ifdef TORQUE_DEBUG
Con::errorf("assimpLoadShape: Load sequence file '%s' failed", cachedPath.getFullPath().c_str());
#endif
delete shape;
}
}
}

if (!Torque::FS::IsFile(path))
{
// File does not exist, bail.
return NULL;
return false;
}

// Allow TSShapeConstructor object to override properties
Expand All @@ -1068,40 +1035,20 @@ TSShape* assimpLoadShape(const Torque::Path &path)
TSShapeLoader::updateProgress(TSShapeLoader::Load_Complete, "Import complete");
Con::printf("[ASSIMP] Shape created successfully.");

bool realMesh = false;
for (U32 i = 0; i < tss->meshes.size(); ++i)
{
if (tss->meshes[i] && tss->meshes[i]->getMeshType() != TSMesh::NullMeshType)
realMesh = true;
}

if (!realMesh && gTryUseDSQs)
{
Torque::Path dsqPath(cachedPath);
dsqPath.setExtension("dsq");
FileStream animOutStream;
dsqPath.setFileName(cachedPath.getFileName());
if (animOutStream.open(dsqPath.getFullPath(), Torque::FS::File::Write))
{
Con::printf("Writing DSQ Animation File for '%s'", dsqPath.getFileName().c_str());
tss->exportSequences(&animOutStream);
}
}
else
Torque::Path cachedPath(path);
// Cache the model to a DTS file for faster loading next time.
cachedPath.setExtension("cached.dts");
// Cache the model to a DTS file for faster loading next time.
FileStream dtsStream;
if (dtsStream.open(cachedPath.getFullPath(), Torque::FS::File::Write))
{
// Cache the model to a DTS file for faster loading next time.
cachedPath.setExtension("cached.dts");
// Cache the model to a DTS file for faster loading next time.
FileStream dtsStream;
if (dtsStream.open(cachedPath.getFullPath(), Torque::FS::File::Write))
{
Con::printf("Writing cached shape to %s", cachedPath.getFullPath().c_str());
tss->write(&dtsStream);
}
Con::printf("Writing cached shape to %s", cachedPath.getFullPath().c_str());
tss->write(&dtsStream);
}
}
loader.releaseImport();
return tss;
res_shape = tss;
return true;
}

DefineEngineFunction(GetShapeInfo, bool, (const char* shapePath, const char* ctrl, bool loadCachedDts), ("", "", true),
Expand Down
3 changes: 0 additions & 3 deletions Engine/source/ts/assimp/assimpShapeLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ struct aiMetadata;
//-----------------------------------------------------------------------------
class AssimpShapeLoader : public TSShapeLoader
{
friend TSShape* assimpLoadShape(const Torque::Path &path);

protected:
Assimp::Importer mImporter;
const aiScene* mScene;
Expand Down Expand Up @@ -79,7 +77,6 @@ class AssimpShapeLoader : public TSShapeLoader
bool fillGuiTreeView(const char* shapePath, GuiTreeViewCtrl* tree);

static bool canLoadCachedDTS(const Torque::Path& path);
static bool canLoadCachedDSQ(const Torque::Path& path);
static void assimpLogCallback(const char* message, char* user);
};

Expand Down
Loading
Loading