mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-16 00:54:54 +00:00
Merge pull request #1724 from marauder2k9-torque/TSShapeResource-Loaders-Exporters-Assimp
Groundwork for exporters and standardizing tsshape resource loading
This commit is contained in:
commit
92e32aa40a
19 changed files with 631 additions and 445 deletions
|
|
@ -143,8 +143,15 @@ IF(MSVC)
|
||||||
advanced_option( ASSIMP_INSTALL_PDB "Install MSVC debug files." ON )
|
advanced_option( ASSIMP_INSTALL_PDB "Install MSVC debug files." ON )
|
||||||
endif()
|
endif()
|
||||||
advanced_option(ASSIMP_RAPIDJSON_NO_MEMBER_ITERATOR "Suppress rapidjson warning on MSVC (NOTE: breaks android build)" ON )
|
advanced_option(ASSIMP_RAPIDJSON_NO_MEMBER_ITERATOR "Suppress rapidjson warning on MSVC (NOTE: breaks android build)" ON )
|
||||||
advanced_option(ASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT "default value of all ASSIMP_BUILD_XXX_IMPORTER values" ON)
|
advanced_option(ASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT "default value of all ASSIMP_BUILD_XXX_IMPORTER values" OFF)
|
||||||
advanced_option(ASSIMP_BUILD_ALL_EXPORTERS_BY_DEFAULT "default value of all ASSIMP_BUILD_XXX_EXPORTER values" ON)
|
advanced_option(ASSIMP_BUILD_FBX_IMPORTER "FBX Importer" ON)
|
||||||
|
advanced_option(ASSIMP_BUILD_GLTF_IMPORTER "GLTF Importer" ON)
|
||||||
|
advanced_option(ASSIMP_BUILD_OBJ_IMPORTER "OBJ Importer" ON)
|
||||||
|
advanced_option(ASSIMP_BUILD_BLEND_IMPORTER "BLENDER Importer" ON)
|
||||||
|
advanced_option(ASSIMP_BUILD_ALL_EXPORTERS_BY_DEFAULT "default value of all ASSIMP_BUILD_XXX_EXPORTER values" OFF)
|
||||||
|
advanced_option(ASSIMP_BUILD_FBX_EXPORTER "FBX Importer" ON)
|
||||||
|
advanced_option(ASSIMP_BUILD_GLTF_EXPORTER "GLTF Importer" ON)
|
||||||
|
advanced_option(ASSIMP_BUILD_OBJ_EXPORTER "OBJ Importer" ON)
|
||||||
mark_as_advanced(ASSIMP_ARCHIVE_OUTPUT_DIRECTORY)
|
mark_as_advanced(ASSIMP_ARCHIVE_OUTPUT_DIRECTORY)
|
||||||
mark_as_advanced(ASSIMP_BIN_INSTALL_DIR)
|
mark_as_advanced(ASSIMP_BIN_INSTALL_DIR)
|
||||||
mark_as_advanced(ASSIMP_LIB_INSTALL_DIR)
|
mark_as_advanced(ASSIMP_LIB_INSTALL_DIR)
|
||||||
|
|
|
||||||
|
|
@ -2012,7 +2012,7 @@ void AssetImporter::processShapeAsset(AssetImportObject* assetItem)
|
||||||
{
|
{
|
||||||
enumColladaForImport(filePath, shapeInfo, false);
|
enumColladaForImport(filePath, shapeInfo, false);
|
||||||
}
|
}
|
||||||
else if ((fileExt.compare("dts") == 0) || (fileExt.compare("dsq") == 0))
|
else if ((fileExt.compare("dts") == 0))
|
||||||
{
|
{
|
||||||
enumDTSForImport(filePath, shapeInfo);
|
enumDTSForImport(filePath, shapeInfo);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -405,6 +405,53 @@ bool GuiShapeEdPreview::setObjectModel(const char* modelName)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GuiShapeEdPreview::findCompanionShape(const Torque::Path& dsqPath, Torque::Path& outShapePath)
|
||||||
|
{
|
||||||
|
// AssimpLoader and ColladaLoader exports as "modelname_sequencename.dsq" alongside "modelname.cached.dts"
|
||||||
|
// so strip everything from the last underscore to find the base name
|
||||||
|
String fileName = dsqPath.getFileName();
|
||||||
|
String::SizeType sep = fileName.find('_',0, String::Right);
|
||||||
|
|
||||||
|
if (sep != String::NPos)
|
||||||
|
{
|
||||||
|
Torque::Path candidate(dsqPath);
|
||||||
|
candidate.setFileName(fileName.substr(0, sep));
|
||||||
|
|
||||||
|
candidate.setExtension("cached.dts");
|
||||||
|
if (Torque::FS::IsFile(candidate.getFullPath()))
|
||||||
|
{
|
||||||
|
outShapePath = candidate;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
candidate.setExtension("dts");
|
||||||
|
if (Torque::FS::IsFile(candidate.getFullPath()))
|
||||||
|
{
|
||||||
|
outShapePath = candidate;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback: same filename, just swap extension
|
||||||
|
Torque::Path direct(dsqPath);
|
||||||
|
|
||||||
|
direct.setExtension("cached.dts");
|
||||||
|
if (Torque::FS::IsFile(direct.getFullPath()))
|
||||||
|
{
|
||||||
|
outShapePath = direct;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
direct.setExtension("dts");
|
||||||
|
if (Torque::FS::IsFile(direct.getFullPath()))
|
||||||
|
{
|
||||||
|
outShapePath = direct;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool GuiShapeEdPreview::setObjectShapeAsset(const char* assetId)
|
bool GuiShapeEdPreview::setObjectShapeAsset(const char* assetId)
|
||||||
{
|
{
|
||||||
SAFE_DELETE(mModel);
|
SAFE_DELETE(mModel);
|
||||||
|
|
@ -422,16 +469,74 @@ bool GuiShapeEdPreview::setObjectShapeAsset(const char* assetId)
|
||||||
ShapeAsset* asset = AssetDatabase.acquireAsset<ShapeAsset>(id);
|
ShapeAsset* asset = AssetDatabase.acquireAsset<ShapeAsset>(id);
|
||||||
modelName = asset->getShapeFile();
|
modelName = asset->getShapeFile();
|
||||||
AssetDatabase.releaseAsset(id);
|
AssetDatabase.releaseAsset(id);
|
||||||
|
return setObjectModel(modelName);
|
||||||
}
|
}
|
||||||
else if (assetType == StringTable->insert("ShapeAnimationAsset"))
|
else if (assetType == StringTable->insert("ShapeAnimationAsset"))
|
||||||
{
|
{
|
||||||
ShapeAnimationAsset* asset = AssetDatabase.acquireAsset<ShapeAnimationAsset>(id);
|
ShapeAnimationAsset* asset = AssetDatabase.acquireAsset<ShapeAnimationAsset>(id);
|
||||||
modelName = asset->getAnimationPath();
|
StringTableEntry animPath = asset->getAnimationPath();
|
||||||
AssetDatabase.releaseAsset(id);
|
AssetDatabase.releaseAsset(id);
|
||||||
|
Torque::Path dsqPath(animPath);
|
||||||
|
String fileExt = String::ToLower(dsqPath.getExtension());
|
||||||
|
|
||||||
|
if (fileExt != String("dsq"))
|
||||||
|
{
|
||||||
|
return setObjectModel(animPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
Torque::Path shapePath;
|
||||||
|
if (!findCompanionShape(dsqPath, shapePath))
|
||||||
|
{
|
||||||
|
Con::warnf("GuiShapeEdPreview::setObjectShapeAsset - "
|
||||||
|
"No companion shape found for '%s'", animPath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!setObjectModel(shapePath.getFullPath()))
|
||||||
|
{
|
||||||
|
Con::warnf("GuiShapeEdPreview::setObjectShapeAsset - "
|
||||||
|
"Could not load companion shape for '%s'", animPath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileStream dsqStream;
|
||||||
|
if (!dsqStream.open(animPath, Torque::FS::File::Read))
|
||||||
|
{
|
||||||
|
Con::warnf("GuiShapeEdPreview::setObjectShapeAsset - "
|
||||||
|
"Could not open '%s'", animPath);
|
||||||
|
SAFE_DELETE(mModel);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TSShape* shape = mModel->getShape();
|
||||||
|
|
||||||
|
bool ok = shape->importSequences(&dsqStream, String(animPath));
|
||||||
|
dsqStream.close();
|
||||||
|
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
Con::warnf("GuiShapeEdPreview::setObjectShapeAsset - "
|
||||||
|
"importSequences failed for '%s'", animPath);
|
||||||
|
SAFE_DELETE(mModel);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
setAllMeshesHidden(true);
|
||||||
|
mRenderNodes = true;
|
||||||
|
if (shape->sequences.size() > 0)
|
||||||
|
{
|
||||||
|
// importSequences appends, so the new one is always last
|
||||||
|
const String& seqName = shape->getSequenceName(shape->sequences.size() - 1);
|
||||||
|
addThread();
|
||||||
|
mActiveThread = 0;
|
||||||
|
setActiveThreadSequence(seqName.c_str(), 0.0f, 0.0f, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return setObjectModel(modelName);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GuiShapeEdPreview::_onResourceChanged(const Torque::Path& path)
|
void GuiShapeEdPreview::_onResourceChanged(const Torque::Path& path)
|
||||||
|
|
|
||||||
|
|
@ -199,6 +199,7 @@ public:
|
||||||
|
|
||||||
void setCurrentDetail(S32 dl);
|
void setCurrentDetail(S32 dl);
|
||||||
bool setObjectModel(const char * modelName);
|
bool setObjectModel(const char * modelName);
|
||||||
|
bool findCompanionShape(const Torque::Path& dsqPath, Torque::Path& outShapePath);
|
||||||
bool setObjectShapeAsset(const char* assetId);
|
bool setObjectShapeAsset(const char* assetId);
|
||||||
|
|
||||||
void _onResourceChanged(const Torque::Path& path);
|
void _onResourceChanged(const Torque::Path& path);
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@
|
||||||
|
|
||||||
bool AssimpAppMesh::fixedSizeEnabled = false;
|
bool AssimpAppMesh::fixedSizeEnabled = false;
|
||||||
S32 AssimpAppMesh::fixedSize = 2;
|
S32 AssimpAppMesh::fixedSize = 2;
|
||||||
|
Vector<S32> AssimpAppMesh::sMaterialRemap;
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
@ -164,7 +165,8 @@ void AssimpAppMesh::lockMesh(F32 t, const MatrixF& objOffset)
|
||||||
primitives.increment();
|
primitives.increment();
|
||||||
TSDrawPrimitive& primitive = primitives.last();
|
TSDrawPrimitive& primitive = primitives.last();
|
||||||
primitive.start = 0;
|
primitive.start = 0;
|
||||||
primitive.matIndex = (TSDrawPrimitive::Triangles | TSDrawPrimitive::Indexed) | (S32)mMeshData->mMaterialIndex;
|
S32 mappedMat = (mMeshData->mMaterialIndex < (U32)AssimpAppMesh::sMaterialRemap.size()) ? AssimpAppMesh::sMaterialRemap[mMeshData->mMaterialIndex] : TSDrawPrimitive::NoMaterial;
|
||||||
|
primitive.matIndex = (TSDrawPrimitive::Triangles | TSDrawPrimitive::Indexed) | mappedMat;
|
||||||
primitive.numElements = indicesCount;
|
primitive.numElements = indicesCount;
|
||||||
|
|
||||||
for ( U32 n = 0; n < mMeshData->mNumFaces; ++n)
|
for ( U32 n = 0; n < mMeshData->mNumFaces; ++n)
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,8 @@ public:
|
||||||
/// @return The mesh transform at the specified time
|
/// @return The mesh transform at the specified time
|
||||||
MatrixF getMeshTransform(F32 time) override;
|
MatrixF getMeshTransform(F32 time) override;
|
||||||
F32 getVisValue(F32 t) override;
|
F32 getVisValue(F32 t) override;
|
||||||
|
|
||||||
|
static Vector<S32> sMaterialRemap;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _COLLADA_APPMESH_H_
|
#endif // _COLLADA_APPMESH_H_
|
||||||
|
|
|
||||||
|
|
@ -56,12 +56,13 @@
|
||||||
#undef new
|
#undef new
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
// assimp include files.
|
// assimp include files.
|
||||||
#include <assimp/cimport.h>
|
#include <assimp/cimport.h>
|
||||||
#include <assimp/scene.h>
|
#include <assimp/scene.h>
|
||||||
#include <assimp/postprocess.h>
|
#include <assimp/postprocess.h>
|
||||||
#include <assimp/types.h>
|
#include <assimp/types.h>
|
||||||
#include <assimp/config.h>
|
#include <assimp/config.h>
|
||||||
|
#include <assimp/Exporter.hpp>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
|
||||||
#if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
|
#if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
|
||||||
|
|
@ -69,56 +70,68 @@
|
||||||
# define new _new
|
# define new _new
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern bool gTryUseDSQs;
|
static bool sReadAssimp(const Torque::Path& path, TSShape*& shape);
|
||||||
|
|
||||||
MODULE_BEGIN( AssimpShapeLoader )
|
static struct _privateRegisterAssimp
|
||||||
MODULE_INIT_AFTER( ShapeLoader )
|
{
|
||||||
MODULE_INIT
|
_privateRegisterAssimp()
|
||||||
{
|
{
|
||||||
TSShapeLoader::addFormat("DirectX X", "x");
|
TSShape::ShapeRegistration reg;
|
||||||
TSShapeLoader::addFormat("Autodesk FBX", "fbx");
|
|
||||||
TSShapeLoader::addFormat("Blender 3D", "blend" );
|
Assimp::Importer importer;
|
||||||
TSShapeLoader::addFormat("3ds Max 3DS", "3ds");
|
for (U32 i = 0; i < importer.GetImporterCount(); i++)
|
||||||
TSShapeLoader::addFormat("3ds Max ASE", "ase");
|
{
|
||||||
TSShapeLoader::addFormat("Wavefront Object", "obj");
|
const aiImporterDesc* desc = importer.GetImporterInfo(i);
|
||||||
TSShapeLoader::addFormat("Industry Foundation Classes (IFC/Step)", "ifc");
|
|
||||||
TSShapeLoader::addFormat("Stanford Polygon Library", "ply");
|
String extensions(desc->mFileExtensions);
|
||||||
TSShapeLoader::addFormat("AutoCAD DXF", "dxf");
|
|
||||||
TSShapeLoader::addFormat("LightWave", "lwo");
|
Vector<String> tokens;
|
||||||
TSShapeLoader::addFormat("LightWave Scene", "lws");
|
extensions.split(" ", tokens);
|
||||||
TSShapeLoader::addFormat("Modo", "lxo");
|
for (U32 t = 0; t < tokens.size(); ++t)
|
||||||
TSShapeLoader::addFormat("Stereolithography", "stl");
|
{
|
||||||
TSShapeLoader::addFormat("AC3D", "ac");
|
const String& ext = tokens[t];
|
||||||
TSShapeLoader::addFormat("Milkshape 3D", "ms3d");
|
if (ext.isEmpty() ||
|
||||||
TSShapeLoader::addFormat("TrueSpace COB", "cob");
|
ext.equal("dae", String::NoCase) || // filter out collada importer formats (for now).
|
||||||
TSShapeLoader::addFormat("TrueSpace SCN", "scn");
|
ext.equal("zae", String::NoCase) ||
|
||||||
TSShapeLoader::addFormat("Ogre XML", "xml");
|
ext.equal("xml", String::NoCase)
|
||||||
TSShapeLoader::addFormat("Irrlicht Mesh", "irrmesh");
|
)
|
||||||
TSShapeLoader::addFormat("Irrlicht Scene", "irr");
|
continue;
|
||||||
TSShapeLoader::addFormat("Quake I", "mdl" );
|
|
||||||
TSShapeLoader::addFormat("Quake II", "md2" );
|
reg.extensions.push_back({
|
||||||
TSShapeLoader::addFormat("Quake III Mesh", "md3");
|
String(desc->mName), // convert from const char*
|
||||||
TSShapeLoader::addFormat("Quake III Map/BSP", "pk3");
|
ext
|
||||||
TSShapeLoader::addFormat("Return to Castle Wolfenstein", "mdc");
|
});
|
||||||
TSShapeLoader::addFormat("Doom 3", "md5" );
|
}
|
||||||
TSShapeLoader::addFormat("Valve SMD", "smd");
|
|
||||||
TSShapeLoader::addFormat("Valve VTA", "vta");
|
}
|
||||||
TSShapeLoader::addFormat("Starcraft II M3", "m3");
|
|
||||||
TSShapeLoader::addFormat("Unreal", "3d");
|
Assimp::Exporter exporter;
|
||||||
TSShapeLoader::addFormat("BlitzBasic 3D", "b3d" );
|
|
||||||
TSShapeLoader::addFormat("Quick3D Q3D", "q3d");
|
for (U32 i = 0; i < exporter.GetExportFormatCount(); ++i)
|
||||||
TSShapeLoader::addFormat("Quick3D Q3S", "q3s");
|
{
|
||||||
TSShapeLoader::addFormat("Neutral File Format", "nff");
|
const aiExportFormatDesc* desc = exporter.GetExportFormatDescription(i);
|
||||||
TSShapeLoader::addFormat("Object File Format", "off");
|
String ext(desc->fileExtension);
|
||||||
TSShapeLoader::addFormat("PovRAY Raw", "raw");
|
|
||||||
TSShapeLoader::addFormat("Terragen Terrain", "ter");
|
if (ext.isEmpty() ||
|
||||||
TSShapeLoader::addFormat("3D GameStudio (3DGS)", "mdl");
|
ext.equal("dae", String::NoCase) || // filter out collada importer formats (for now).
|
||||||
TSShapeLoader::addFormat("3D GameStudio (3DGS) Terrain", "hmp");
|
ext.equal("zae", String::NoCase) ||
|
||||||
TSShapeLoader::addFormat("Izware Nendo", "ndo");
|
ext.equal("xml", String::NoCase)
|
||||||
TSShapeLoader::addFormat("gltf", "gltf");
|
)
|
||||||
TSShapeLoader::addFormat("gltf binary", "glb");
|
continue;
|
||||||
|
|
||||||
|
reg.export_extensions.push_back({
|
||||||
|
String(desc->description),
|
||||||
|
ext
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
reg.readFunc = sReadAssimp;
|
||||||
|
reg.writeFunc = NULL;
|
||||||
|
|
||||||
|
TSShape::sRegisterFormat(reg);
|
||||||
}
|
}
|
||||||
MODULE_END;
|
} sStaticRegisterAssimp;
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
@ -272,7 +285,16 @@ void AssimpShapeLoader::enumerateScene()
|
||||||
|
|
||||||
// Load all materials
|
// Load all materials
|
||||||
AssimpAppMaterial::sDefaultMatNumber = 0;
|
AssimpAppMaterial::sDefaultMatNumber = 0;
|
||||||
for (U32 i = 0; i < mScene->mNumMaterials; ++i) {
|
AssimpAppMesh::sMaterialRemap.setSize(mScene->mNumMaterials);
|
||||||
|
for (U32 i = 0; i < mScene->mNumMaterials; ++i)
|
||||||
|
{
|
||||||
|
if (FindMatch::isMatchMultipleExprs(ColladaUtils::getOptions().neverImportMat,
|
||||||
|
mScene->mMaterials[i]->GetName().C_Str(), false))
|
||||||
|
{
|
||||||
|
AssimpAppMesh::sMaterialRemap[i] = TSDrawPrimitive::NoMaterial; // TSDrawPrimitive::NoMaterial
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
AssimpAppMesh::sMaterialRemap[i] = AppMesh::appMaterials.size();
|
||||||
AppMesh::appMaterials.push_back(new AssimpAppMaterial(mScene->mMaterials[i]));
|
AppMesh::appMaterials.push_back(new AssimpAppMaterial(mScene->mMaterials[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -391,54 +413,34 @@ void AssimpShapeLoader::getRootAxisTransform()
|
||||||
|
|
||||||
MatrixF rot(true);
|
MatrixF rot(true);
|
||||||
|
|
||||||
// ===== Y-UP SOURCE =====
|
// Build source basis
|
||||||
if (upAxis == 1)
|
auto axisToVector = [](int axis, int sign) -> Point3F
|
||||||
{
|
{
|
||||||
if (frontAxis == 2)
|
Point3F v(0, 0, 0);
|
||||||
{
|
v[axis] = (F32)sign;
|
||||||
// Y-up, Z-forward → Z-up, Y-forward
|
return v;
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===== Z-UP SOURCE =====
|
Point3F forward = axisToVector(frontAxis, -frontSign);
|
||||||
if (upAxis == 2)
|
Point3F up = axisToVector(upAxis, upSign);
|
||||||
{
|
Point3F right = mCross(forward, up);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===== X-UP SOURCE =====
|
// Recompute forward
|
||||||
if (upAxis == 0)
|
forward = mCross(up, right);
|
||||||
{
|
|
||||||
if (frontAxis == 2)
|
// Normalize (defensive, though they should already be unit)
|
||||||
{
|
right.normalize();
|
||||||
// X-up, Z-forward → Z-up, Y-forward
|
forward.normalize();
|
||||||
// Rotate -90° around Y then -90° around Z
|
up.normalize();
|
||||||
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;
|
MatrixF srcBasis(true);
|
||||||
rot(2, 0) = 0.0f; rot(2, 1) = -1.0f; rot(2, 2) = 0.0f;
|
srcBasis.setColumn(0, right);
|
||||||
}
|
srcBasis.setColumn(1, forward);
|
||||||
}
|
srcBasis.setColumn(2, up);
|
||||||
|
|
||||||
|
// Convert to Torque space
|
||||||
|
rot = srcBasis;
|
||||||
|
rot.inverse();
|
||||||
|
|
||||||
ColladaUtils::getOptions().axisCorrectionMat = rot;
|
ColladaUtils::getOptions().axisCorrectionMat = rot;
|
||||||
}
|
}
|
||||||
|
|
@ -689,30 +691,6 @@ bool AssimpShapeLoader::canLoadCachedDTS(const Torque::Path& path)
|
||||||
return false;
|
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)
|
void AssimpShapeLoader::assimpLogCallback(const char* message, char* user)
|
||||||
{
|
{
|
||||||
Con::printf("[Assimp log message] %s", StringUnit::getUnit(message, 0, "\n"));
|
Con::printf("[Assimp log message] %s", StringUnit::getUnit(message, 0, "\n"));
|
||||||
|
|
@ -992,65 +970,12 @@ bool AssimpShapeLoader::getMetaString(const char* key, String& stringVal)
|
||||||
}
|
}
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
/// This function is invoked by the resource manager based on file extension.
|
/// 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))
|
if (!Torque::FS::IsFile(path))
|
||||||
{
|
{
|
||||||
// File does not exist, bail.
|
// File does not exist, bail.
|
||||||
return NULL;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow TSShapeConstructor object to override properties
|
// Allow TSShapeConstructor object to override properties
|
||||||
|
|
@ -1066,42 +991,58 @@ TSShape* assimpLoadShape(const Torque::Path &path)
|
||||||
if (tss)
|
if (tss)
|
||||||
{
|
{
|
||||||
TSShapeLoader::updateProgress(TSShapeLoader::Load_Complete, "Import complete");
|
TSShapeLoader::updateProgress(TSShapeLoader::Load_Complete, "Import complete");
|
||||||
Con::printf("[ASSIMP] Shape created successfully.");
|
|
||||||
|
|
||||||
bool realMesh = false;
|
bool realMesh = false;
|
||||||
for (U32 i = 0; i < tss->meshes.size(); ++i)
|
for (U32 i = 0; i < tss->meshes.size(); ++i)
|
||||||
{
|
{
|
||||||
if (tss->meshes[i] && tss->meshes[i]->getMeshType() != TSMesh::NullMeshType)
|
if (tss->meshes[i] && tss->meshes[i]->getMeshType() != TSMesh::NullMeshType)
|
||||||
|
{
|
||||||
realMesh = true;
|
realMesh = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!realMesh && gTryUseDSQs)
|
|
||||||
|
if (realMesh)
|
||||||
|
Con::printf("[ASSIMP] Shape created successfully.");
|
||||||
|
else
|
||||||
|
Con::printf("[ASSIMP] Animation created successfully.");
|
||||||
|
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
Con::printf("Writing cached shape to %s", cachedPath.getFullPath().c_str());
|
||||||
|
tss->write(&dtsStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
// only save dsq if we have a real mesh to pair with it.
|
||||||
|
if (tss->sequences.size() > 0 && realMesh)
|
||||||
{
|
{
|
||||||
Torque::Path dsqPath(cachedPath);
|
Torque::Path dsqPath(cachedPath);
|
||||||
dsqPath.setExtension("dsq");
|
dsqPath.setExtension("dsq");
|
||||||
FileStream animOutStream;
|
FileStream animOutStream;
|
||||||
dsqPath.setFileName(cachedPath.getFileName());
|
|
||||||
if (animOutStream.open(dsqPath.getFullPath(), Torque::FS::File::Write))
|
for (S32 i = 0; i < tss->sequences.size(); i++)
|
||||||
{
|
{
|
||||||
Con::printf("Writing DSQ Animation File for '%s'", dsqPath.getFileName().c_str());
|
const String& seqName = tss->getName(tss->sequences[i].nameIndex);
|
||||||
tss->exportSequences(&animOutStream);
|
Con::printf("Writing DSQ Animation File for sequence '%s'", seqName.c_str());
|
||||||
}
|
|
||||||
}
|
dsqPath.setFileName(cachedPath.getFileName() + "_" + seqName);
|
||||||
else
|
if (animOutStream.open(dsqPath.getFullPath(), Torque::FS::File::Write))
|
||||||
{
|
{
|
||||||
// Cache the model to a DTS file for faster loading next time.
|
tss->exportSequence(&animOutStream, tss->sequences[i], false);
|
||||||
cachedPath.setExtension("cached.dts");
|
animOutStream.close();
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loader.releaseImport();
|
loader.releaseImport();
|
||||||
return tss;
|
res_shape = tss;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
DefineEngineFunction(GetShapeInfo, bool, (const char* shapePath, const char* ctrl, bool loadCachedDts), ("", "", true),
|
DefineEngineFunction(GetShapeInfo, bool, (const char* shapePath, const char* ctrl, bool loadCachedDts), ("", "", true),
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,6 @@ struct aiMetadata;
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
class AssimpShapeLoader : public TSShapeLoader
|
class AssimpShapeLoader : public TSShapeLoader
|
||||||
{
|
{
|
||||||
friend TSShape* assimpLoadShape(const Torque::Path &path);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Assimp::Importer mImporter;
|
Assimp::Importer mImporter;
|
||||||
const aiScene* mScene;
|
const aiScene* mScene;
|
||||||
|
|
@ -79,7 +77,6 @@ public:
|
||||||
bool fillGuiTreeView(const char* shapePath, GuiTreeViewCtrl* tree);
|
bool fillGuiTreeView(const char* shapePath, GuiTreeViewCtrl* tree);
|
||||||
|
|
||||||
static bool canLoadCachedDTS(const Torque::Path& path);
|
static bool canLoadCachedDTS(const Torque::Path& path);
|
||||||
static bool canLoadCachedDSQ(const Torque::Path& path);
|
|
||||||
static void assimpLogCallback(const char* message, char* user);
|
static void assimpLogCallback(const char* message, char* user);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,15 +50,23 @@
|
||||||
#include "core/util/zip/zipVolume.h"
|
#include "core/util/zip/zipVolume.h"
|
||||||
#include "gfx/bitmap/gBitmap.h"
|
#include "gfx/bitmap/gBitmap.h"
|
||||||
|
|
||||||
extern bool gTryUseDSQs;
|
static bool sReadCollada(const Torque::Path& path, TSShape*& shape);
|
||||||
MODULE_BEGIN( ColladaShapeLoader )
|
|
||||||
MODULE_INIT_AFTER( ShapeLoader )
|
static struct _privateRegisterCollada
|
||||||
MODULE_INIT
|
{
|
||||||
|
_privateRegisterCollada()
|
||||||
{
|
{
|
||||||
TSShapeLoader::addFormat("Collada", "dae");
|
TSShape::ShapeRegistration reg;
|
||||||
TSShapeLoader::addFormat("Google Earth", "kmz");
|
reg.extensions.push_back({ "Collada", "dae" });
|
||||||
|
reg.export_extensions.push_back({ "Collada", "dae" });
|
||||||
|
reg.extensions.push_back({ "Google Earth", "kmz" });
|
||||||
|
|
||||||
|
reg.readFunc = sReadCollada;
|
||||||
|
reg.writeFunc = NULL;
|
||||||
|
|
||||||
|
TSShape::sRegisterFormat(reg);
|
||||||
}
|
}
|
||||||
MODULE_END;
|
} sStaticRegisterCollada;
|
||||||
|
|
||||||
//
|
//
|
||||||
static DAE sDAE; // Collada model database (holds the last loaded file)
|
static DAE sDAE; // Collada model database (holds the last loaded file)
|
||||||
|
|
@ -549,39 +557,6 @@ bool ColladaShapeLoader::canLoadCachedDTS(const Torque::Path& path)
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool ColladaShapeLoader::canLoadCachedDSQ(const Torque::Path& path)
|
|
||||||
{
|
|
||||||
// Generate the cached filename
|
|
||||||
Torque::Path cachedPath(path);
|
|
||||||
cachedPath.setExtension("dsq");
|
|
||||||
|
|
||||||
// Check if a cached DSQ newer than this file is available
|
|
||||||
FileTime cachedModifyTime;
|
|
||||||
if (Platform::getFileTimes(cachedPath.getFullPath(), NULL, &cachedModifyTime))
|
|
||||||
{
|
|
||||||
bool forceLoadDAE = Con::getBoolVariable("$collada::forceLoadDAE", false);
|
|
||||||
|
|
||||||
FileTime daeModifyTime;
|
|
||||||
if (!Platform::getFileTimes(path.getFullPath(), NULL, &daeModifyTime) ||
|
|
||||||
(!forceLoadDAE && (Platform::compareFileTimes(cachedModifyTime, daeModifyTime) >= 0)))
|
|
||||||
{
|
|
||||||
// DAE not found, or cached DTS is newer
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//assume the dts is good since it was zipped on purpose
|
|
||||||
Torque::FS::FileSystemRef ref = Torque::FS::GetFileSystem(cachedPath);
|
|
||||||
if (ref && !String::compare("Zip", ref->getTypeStr().c_str()))
|
|
||||||
{
|
|
||||||
bool forceLoadDAE = Con::getBoolVariable("$collada::forceLoadDAE", false);
|
|
||||||
|
|
||||||
if (!forceLoadDAE && Torque::FS::IsFile(cachedPath))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool ColladaShapeLoader::checkAndMountSketchup(const Torque::Path& path, String& mountPoint, Torque::Path& daePath)
|
bool ColladaShapeLoader::checkAndMountSketchup(const Torque::Path& path, String& mountPoint, Torque::Path& daePath)
|
||||||
{
|
{
|
||||||
bool isSketchup = path.getExtension().equal("kmz", String::NoCase);
|
bool isSketchup = path.getExtension().equal("kmz", String::NoCase);
|
||||||
|
|
@ -683,61 +658,8 @@ domCOLLADA* ColladaShapeLoader::readColladaFile(const String& path)
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
/// This function is invoked by the resource manager based on file extension.
|
/// This function is invoked by the resource manager based on file extension.
|
||||||
TSShape* loadColladaShape(const Torque::Path &path)
|
static bool sReadCollada(const Torque::Path& path, TSShape*& res_shape)
|
||||||
{
|
{
|
||||||
#ifndef DAE2DTS_TOOL
|
|
||||||
// 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 (ColladaShapeLoader::canLoadCachedDTS(path))
|
|
||||||
{
|
|
||||||
cachedPath.setExtension("cached.dts");
|
|
||||||
canLoadCached = true;
|
|
||||||
}
|
|
||||||
else if (gTryUseDSQs && ColladaShapeLoader::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 Collada shape from %s", cachedPath.getFullPath().c_str());
|
|
||||||
#endif
|
|
||||||
return shape;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
#ifdef TORQUE_DEBUG
|
|
||||||
Con::errorf("loadColladaShape: Load sequence file '%s' failed", cachedPath.getFullPath().c_str());
|
|
||||||
#endif
|
|
||||||
delete shape;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif // DAE2DTS_TOOL
|
|
||||||
|
|
||||||
if (!Torque::FS::IsFile(path))
|
if (!Torque::FS::IsFile(path))
|
||||||
{
|
{
|
||||||
// DAE file does not exist, bail.
|
// DAE file does not exist, bail.
|
||||||
|
|
@ -777,58 +699,65 @@ TSShape* loadColladaShape(const Torque::Path &path)
|
||||||
tss = loader.generateShape(daePath);
|
tss = loader.generateShape(daePath);
|
||||||
if (tss)
|
if (tss)
|
||||||
{
|
{
|
||||||
#ifndef DAE2DTS_TOOL
|
TSShapeLoader::updateProgress(TSShapeLoader::Load_Complete, "Import complete");
|
||||||
|
|
||||||
bool realMesh = false;
|
bool realMesh = false;
|
||||||
for (U32 i = 0; i < tss->meshes.size(); ++i)
|
for (U32 i = 0; i < tss->meshes.size(); ++i)
|
||||||
{
|
{
|
||||||
if (tss->meshes[i] && tss->meshes[i]->getMeshType() != TSMesh::NullMeshType)
|
if (tss->meshes[i] && tss->meshes[i]->getMeshType() != TSMesh::NullMeshType)
|
||||||
|
{
|
||||||
realMesh = true;
|
realMesh = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!realMesh && gTryUseDSQs)
|
if(realMesh)
|
||||||
|
Con::printf("[COLLADA] Shape created successfully.");
|
||||||
|
else
|
||||||
|
Con::printf("[COLLADA] Animation created successfully.");
|
||||||
|
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
Con::printf("Writing cached shape to %s", cachedPath.getFullPath().c_str());
|
||||||
|
tss->write(&dtsStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add collada materials to materials.tscript
|
||||||
|
updateMaterialsScript(path, isSketchup);
|
||||||
|
|
||||||
|
if (tss->sequences.size() > 0 && realMesh)
|
||||||
{
|
{
|
||||||
Torque::Path dsqPath(cachedPath);
|
Torque::Path dsqPath(cachedPath);
|
||||||
dsqPath.setExtension("dsq");
|
dsqPath.setExtension("dsq");
|
||||||
FileStream animOutStream;
|
FileStream animOutStream;
|
||||||
dsqPath.setFileName(cachedPath.getFileName());
|
|
||||||
if (animOutStream.open(dsqPath.getFullPath(), Torque::FS::File::Write))
|
for (S32 i = 0; i < tss->sequences.size(); i++)
|
||||||
{
|
{
|
||||||
Con::printf("Writing DSQ Animation File for '%s'", dsqPath.getFileName().c_str());
|
const String& seqName = tss->getName(tss->sequences[i].nameIndex);
|
||||||
tss->exportSequences(&animOutStream);
|
Con::printf("Writing DSQ Animation File for sequence '%s'", seqName.c_str());
|
||||||
animOutStream.close();
|
|
||||||
|
dsqPath.setFileName(cachedPath.getFileName() + "_" + seqName);
|
||||||
|
if (animOutStream.open(dsqPath.getFullPath(), Torque::FS::File::Write))
|
||||||
|
{
|
||||||
|
tss->exportSequence(&animOutStream, tss->sequences[i], false);
|
||||||
|
animOutStream.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
// Cache the Collada model to a DTS file for faster loading next time.
|
|
||||||
cachedPath.setExtension("cached.dts");
|
|
||||||
FileStream dtsStream;
|
|
||||||
if (dtsStream.open(cachedPath.getFullPath(), Torque::FS::File::Write))
|
|
||||||
{
|
|
||||||
Torque::FS::FileSystemRef ref = Torque::FS::GetFileSystem(daePath);
|
|
||||||
if (ref && !String::compare("Zip", ref->getTypeStr().c_str()))
|
|
||||||
Con::errorf("No cached dts file found in archive for %s. Forcing cache to disk.", daePath.getFullFileName().c_str());
|
|
||||||
|
|
||||||
Con::printf("Writing cached COLLADA shape to %s", cachedPath.getFullPath().c_str());
|
|
||||||
tss->write(&dtsStream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif // DAE2DTS_TOOL
|
|
||||||
|
|
||||||
// Add collada materials to materials.tscript
|
|
||||||
updateMaterialsScript(path, isSketchup);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close progress dialog
|
|
||||||
TSShapeLoader::updateProgress(TSShapeLoader::Load_Complete, "Import complete");
|
|
||||||
|
|
||||||
if (isSketchup)
|
if (isSketchup)
|
||||||
{
|
{
|
||||||
// Unmount the zip if we mounted it
|
// Unmount the zip if we mounted it
|
||||||
Torque::FS::Unmount(mountPoint);
|
Torque::FS::Unmount(mountPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
return tss;
|
res_shape = tss;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,8 +34,6 @@ struct AnimChannels;
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
class ColladaShapeLoader : public TSShapeLoader
|
class ColladaShapeLoader : public TSShapeLoader
|
||||||
{
|
{
|
||||||
friend TSShape* loadColladaShape(const Torque::Path &path);
|
|
||||||
|
|
||||||
domCOLLADA* root;
|
domCOLLADA* root;
|
||||||
Vector<AnimChannels*> animations; ///< Holds all animation channels for deletion after loading
|
Vector<AnimChannels*> animations; ///< Holds all animation channels for deletion after loading
|
||||||
|
|
||||||
|
|
@ -53,7 +51,6 @@ public:
|
||||||
void computeBounds(Box3F& bounds) override;
|
void computeBounds(Box3F& bounds) override;
|
||||||
|
|
||||||
static bool canLoadCachedDTS(const Torque::Path& path);
|
static bool canLoadCachedDTS(const Torque::Path& path);
|
||||||
static bool canLoadCachedDSQ(const Torque::Path& path);
|
|
||||||
static bool checkAndMountSketchup(const Torque::Path& path, String& mountPoint, Torque::Path& daePath);
|
static bool checkAndMountSketchup(const Torque::Path& path, String& mountPoint, Torque::Path& daePath);
|
||||||
static domCOLLADA* getDomCOLLADA(const Torque::Path& path);
|
static domCOLLADA* getDomCOLLADA(const Torque::Path& path);
|
||||||
static domCOLLADA* readColladaFile(const String& path);
|
static domCOLLADA* readColladaFile(const String& path);
|
||||||
|
|
|
||||||
|
|
@ -36,11 +36,9 @@ MODULE_BEGIN( ShapeLoader )
|
||||||
MODULE_INIT
|
MODULE_INIT
|
||||||
{
|
{
|
||||||
TSShapeLoader::addFormat("Torque DTS", "dts");
|
TSShapeLoader::addFormat("Torque DTS", "dts");
|
||||||
TSShapeLoader::addFormat("Torque DSQ", "dsq");
|
|
||||||
}
|
}
|
||||||
MODULE_END;
|
MODULE_END;
|
||||||
|
|
||||||
bool gTryUseDSQs = false;
|
|
||||||
const F32 TSShapeLoader::DefaultTime = -1.0f;
|
const F32 TSShapeLoader::DefaultTime = -1.0f;
|
||||||
const F64 TSShapeLoader::MinFrameRate = 15.0f;
|
const F64 TSShapeLoader::MinFrameRate = 15.0f;
|
||||||
const F64 TSShapeLoader::MaxFrameRate = 60.0f;
|
const F64 TSShapeLoader::MaxFrameRate = 60.0f;
|
||||||
|
|
@ -1304,11 +1302,16 @@ String TSShapeLoader::getFormatExtensions()
|
||||||
{
|
{
|
||||||
// "*.dsq TAB *.dae TAB
|
// "*.dsq TAB *.dae TAB
|
||||||
StringBuilder output;
|
StringBuilder output;
|
||||||
for(U32 n = 0; n < smFormats.size(); ++n)
|
for(U32 n = 0; n < TSShape::sRegistrations.size(); ++n)
|
||||||
{
|
{
|
||||||
output.append("*.");
|
TSShape::ShapeRegistration reg = TSShape::sRegistrations[n];
|
||||||
output.append(smFormats[n].mExtension);
|
for (U32 i = 0; i < reg.extensions.size(); i++)
|
||||||
output.append("\t");
|
{
|
||||||
|
TSShape::ShapeFormat format = reg.extensions[i];
|
||||||
|
output.append("*.");
|
||||||
|
output.append(format.mExtension);
|
||||||
|
output.append("\t");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return output.end();
|
return output.end();
|
||||||
}
|
}
|
||||||
|
|
@ -1317,12 +1320,17 @@ String TSShapeLoader::getFormatFilters()
|
||||||
{
|
{
|
||||||
// "DSQ Files|*.dsq|COLLADA Files|*.dae|"
|
// "DSQ Files|*.dsq|COLLADA Files|*.dae|"
|
||||||
StringBuilder output;
|
StringBuilder output;
|
||||||
for(U32 n = 0; n < smFormats.size(); ++n)
|
for (U32 n = 0; n < TSShape::sRegistrations.size(); ++n)
|
||||||
{
|
{
|
||||||
output.append(smFormats[n].mName);
|
TSShape::ShapeRegistration reg = TSShape::sRegistrations[n];
|
||||||
output.append("|*.");
|
for (U32 i = 0; i < reg.extensions.size(); i++)
|
||||||
output.append(smFormats[n].mExtension);
|
{
|
||||||
output.append("|");
|
TSShape::ShapeFormat format = reg.extensions[i];
|
||||||
|
output.append(format.mName);
|
||||||
|
output.append("|*.");
|
||||||
|
output.append(format.mExtension);
|
||||||
|
output.append("|");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return output.end();
|
return output.end();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,16 +38,33 @@
|
||||||
#include "core/stream/fileStream.h"
|
#include "core/stream/fileStream.h"
|
||||||
#include "core/fileObject.h"
|
#include "core/fileObject.h"
|
||||||
|
|
||||||
#ifdef TORQUE_COLLADA
|
Vector<TSShape::ShapeRegistration> TSShape::sRegistrations(__FILE__, __LINE__);
|
||||||
extern TSShape* loadColladaShape(const Torque::Path &path);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef TORQUE_ASSIMP
|
void TSShape::sRegisterFormat(const ShapeRegistration& reg)
|
||||||
extern TSShape* assimpLoadShape(const Torque::Path &path);
|
{
|
||||||
#endif
|
U32 insert = sRegistrations.size();
|
||||||
|
sRegistrations.insert(insert, reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
const TSShape::ShapeRegistration* TSShape::sFindRegInfo(const String& extension, bool exporting)
|
||||||
|
{
|
||||||
|
for (U32 i = 0; i < TSShape::sRegistrations.size(); i++)
|
||||||
|
{
|
||||||
|
const TSShape::ShapeRegistration& reg = TSShape::sRegistrations[i];
|
||||||
|
const Vector<ShapeFormat>& extensions = exporting ? reg.export_extensions : reg.extensions;
|
||||||
|
|
||||||
|
for (U32 j = 0; j < extensions.size(); j++)
|
||||||
|
{
|
||||||
|
if (extensions[j].mExtension.equal(extension, String::NoCase))
|
||||||
|
return ®
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/// most recent version -- this is the version we write
|
/// most recent version -- this is the version we write
|
||||||
S32 TSShape::smVersion = 28;
|
S32 TSShape::smVersion = TORQUE_DTS_VERSION;
|
||||||
/// the version currently being read...valid only during a read
|
/// the version currently being read...valid only during a read
|
||||||
S32 TSShape::smReadVersion = -1;
|
S32 TSShape::smReadVersion = -1;
|
||||||
const U32 TSShape::smMostRecentExporterVersion = DTS_EXPORTER_CURRENT_VERSION;
|
const U32 TSShape::smMostRecentExporterVersion = DTS_EXPORTER_CURRENT_VERSION;
|
||||||
|
|
@ -168,6 +185,34 @@ TSShape::~TSShape()
|
||||||
delete[] mShapeData;
|
delete[] mShapeData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TSShape::compressionKey(U8* keyOut, U32 keyLen)
|
||||||
|
{
|
||||||
|
// Concatenate app name and zip password at compile time
|
||||||
|
static const char kSeed[] = TORQUE_APP_NAME DEFAULT_ZIP_PASSWORD;
|
||||||
|
|
||||||
|
// djb2 hash to collapse the seed string into a 32-bit value
|
||||||
|
U32 state = 5381u;
|
||||||
|
for (const char* c = kSeed; *c; ++c)
|
||||||
|
state = ((state << 5) + state) ^ (U8)*c;
|
||||||
|
|
||||||
|
// Expand into a key stream via a linear-congruential generator
|
||||||
|
// (Numerical Recipes coefficients)
|
||||||
|
for (U32 i = 0; i < keyLen; ++i)
|
||||||
|
{
|
||||||
|
state = state * 1664525u + 1013904223u;
|
||||||
|
keyOut[i] = (U8)(state >> 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSShape::xorBufferAtOffset(void* buffer, U32 byteCount,
|
||||||
|
U32 startOffset,
|
||||||
|
const U8* key, U32 keyLen)
|
||||||
|
{
|
||||||
|
U8* p = (U8*)buffer;
|
||||||
|
for (U32 i = 0; i < byteCount; ++i)
|
||||||
|
p[i] ^= key[(startOffset + i) % keyLen];
|
||||||
|
}
|
||||||
|
|
||||||
const String& TSShape::getName( S32 nameIndex ) const
|
const String& TSShape::getName( S32 nameIndex ) const
|
||||||
{
|
{
|
||||||
AssertFatal(nameIndex>=0 && nameIndex<names.size(),"TSShape::getName");
|
AssertFatal(nameIndex>=0 && nameIndex<names.size(),"TSShape::getName");
|
||||||
|
|
@ -1892,6 +1937,20 @@ void TSShape::write(Stream * s, bool saveOldFormat)
|
||||||
size8 += 4;
|
size8 += 4;
|
||||||
size8 >>= 2;
|
size8 >>= 2;
|
||||||
|
|
||||||
|
if (smVersion >= 29)
|
||||||
|
{
|
||||||
|
const U32 KEY_LEN = 256;
|
||||||
|
U8 key[KEY_LEN];
|
||||||
|
compressionKey(key, KEY_LEN);
|
||||||
|
|
||||||
|
U32 offset = 0;
|
||||||
|
xorBufferAtOffset(buffer32, size32 * 4, offset, key, KEY_LEN);
|
||||||
|
offset += size32 * 4;
|
||||||
|
xorBufferAtOffset(buffer16, size16 * 4, offset, key, KEY_LEN);
|
||||||
|
offset += size16 * 4;
|
||||||
|
xorBufferAtOffset(buffer8, size8 * 4, offset, key, KEY_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
S32 sizeMemBuffer, start16, start8;
|
S32 sizeMemBuffer, start16, start8;
|
||||||
sizeMemBuffer = size32 + size16 + size8;
|
sizeMemBuffer = size32 + size16 + size8;
|
||||||
start16 = size32;
|
start16 = size32;
|
||||||
|
|
@ -1971,7 +2030,18 @@ bool TSShape::read(Stream * s)
|
||||||
}
|
}
|
||||||
|
|
||||||
S32 * tmp = new S32[sizeMemBuffer];
|
S32 * tmp = new S32[sizeMemBuffer];
|
||||||
s->read(sizeof(S32)*sizeMemBuffer,(U8*)tmp);
|
s->read(sizeof(S32) * sizeMemBuffer, (U8*)tmp);
|
||||||
|
|
||||||
|
if (smReadVersion >= 29)
|
||||||
|
{
|
||||||
|
const U32 KEY_LEN = 256;
|
||||||
|
U8 key[KEY_LEN];
|
||||||
|
compressionKey(key, KEY_LEN);
|
||||||
|
|
||||||
|
// The whole tmp block is one contiguous encrypted region
|
||||||
|
xorBufferAtOffset(tmp, sizeMemBuffer * 4, 0, key, KEY_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
memBuffer32 = tmp;
|
memBuffer32 = tmp;
|
||||||
memBuffer16 = (S16*)(tmp+startU16);
|
memBuffer16 = (S16*)(tmp+startU16);
|
||||||
memBuffer8 = (S8*)(tmp+startU8);
|
memBuffer8 = (S8*)(tmp+startU8);
|
||||||
|
|
@ -2166,11 +2236,41 @@ template<> void *Resource<TSShape>::create(const Torque::Path &path)
|
||||||
TSShape * ret = 0;
|
TSShape * ret = 0;
|
||||||
bool readSuccess = false;
|
bool readSuccess = false;
|
||||||
const String extension = path.getExtension();
|
const String extension = path.getExtension();
|
||||||
|
bool canLoadCached = false;
|
||||||
|
|
||||||
if ( extension.equal( "dts", String::NoCase ) )
|
// Generate the cached filename
|
||||||
|
Torque::Path cachedPath(path);
|
||||||
|
cachedPath.setExtension("cached.dts");
|
||||||
|
|
||||||
|
// Check if a cached DTS newer than this file is available
|
||||||
|
FileTime cachedModifyTime;
|
||||||
|
if (Platform::getFileTimes(cachedPath.getFullPath(), NULL, &cachedModifyTime))
|
||||||
|
{
|
||||||
|
bool forceLoadDAE = Con::getBoolVariable("$collada::forceLoadDAE", false);
|
||||||
|
|
||||||
|
FileTime daeModifyTime;
|
||||||
|
if (!Platform::getFileTimes(path.getFullPath(), NULL, &daeModifyTime) ||
|
||||||
|
(!forceLoadDAE && (Platform::compareFileTimes(cachedModifyTime, daeModifyTime) >= 0)))
|
||||||
|
{
|
||||||
|
// Non DTS not found, or cached DTS is newer
|
||||||
|
canLoadCached = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//assume the dts is good since it was zipped on purpose
|
||||||
|
Torque::FS::FileSystemRef ref = Torque::FS::GetFileSystem(cachedPath);
|
||||||
|
if (ref && !String::compare("Zip", ref->getTypeStr().c_str()))
|
||||||
|
{
|
||||||
|
bool forceLoadDAE = Con::getBoolVariable("$collada::forceLoadDAE", false);
|
||||||
|
|
||||||
|
if (!forceLoadDAE && Torque::FS::IsFile(cachedPath))
|
||||||
|
canLoadCached = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extension.equal("dts", String::NoCase) || canLoadCached)
|
||||||
{
|
{
|
||||||
FileStream stream;
|
FileStream stream;
|
||||||
stream.open( path.getFullPath(), Torque::FS::File::Read );
|
stream.open(canLoadCached ? cachedPath.getFullPath() : path.getFullPath(), Torque::FS::File::Read);
|
||||||
if ( stream.getStatus() != Stream::Ok )
|
if ( stream.getStatus() != Stream::Ok )
|
||||||
{
|
{
|
||||||
Con::errorf( "Resource<TSShape>::create - Could not open '%s'", path.getFullPath().c_str() );
|
Con::errorf( "Resource<TSShape>::create - Could not open '%s'", path.getFullPath().c_str() );
|
||||||
|
|
@ -2180,46 +2280,92 @@ template<> void *Resource<TSShape>::create(const Torque::Path &path)
|
||||||
ret = new TSShape;
|
ret = new TSShape;
|
||||||
readSuccess = ret->read(&stream);
|
readSuccess = ret->read(&stream);
|
||||||
}
|
}
|
||||||
else if ( extension.equal( "dae", String::NoCase ) || extension.equal( "kmz", String::NoCase ) )
|
else if (extension.equal("dsq", String::NoCase))
|
||||||
{
|
{
|
||||||
#ifdef TORQUE_COLLADA
|
// DSQ is an animation-only file. Try to find a companion shape to load
|
||||||
// Attempt to load the DAE file
|
// sequences into; if none exists, create a minimal empty shape instead.
|
||||||
ret = loadColladaShape(path);
|
Torque::Path companionPath(path);
|
||||||
readSuccess = (ret != NULL);
|
bool foundCompanion = false;
|
||||||
#else
|
|
||||||
// No COLLADA support => attempt to load the cached DTS file instead
|
// Look for basename.cached.dts or basename.dts alongside the DSQ
|
||||||
Torque::Path cachedPath = path;
|
String fileName = path.getFileName();
|
||||||
cachedPath.setExtension("cached.dts");
|
String::SizeType sep = fileName.find('_', 0, String::Right);
|
||||||
|
if (sep != String::NPos)
|
||||||
FileStream stream;
|
|
||||||
stream.open( cachedPath.getFullPath(), Torque::FS::File::Read );
|
|
||||||
if ( stream.getStatus() != Stream::Ok )
|
|
||||||
{
|
{
|
||||||
Con::errorf( "Resource<TSShape>::create - Could not open '%s'", cachedPath.getFullPath().c_str() );
|
companionPath.setFileName(fileName.substr(0, sep));
|
||||||
|
companionPath.setExtension("cached.dts");
|
||||||
|
if (Torque::FS::IsFile(companionPath.getFullPath()))
|
||||||
|
foundCompanion = true;
|
||||||
|
|
||||||
|
if (!foundCompanion)
|
||||||
|
{
|
||||||
|
companionPath.setExtension("dts");
|
||||||
|
if (Torque::FS::IsFile(companionPath.getFullPath()))
|
||||||
|
foundCompanion = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundCompanion)
|
||||||
|
{
|
||||||
|
companionPath.setFileName(fileName);
|
||||||
|
companionPath.setExtension("cached.dts");
|
||||||
|
if (Torque::FS::IsFile(companionPath.getFullPath()))
|
||||||
|
foundCompanion = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundCompanion)
|
||||||
|
{
|
||||||
|
companionPath.setExtension("dts");
|
||||||
|
if (Torque::FS::IsFile(companionPath.getFullPath()))
|
||||||
|
foundCompanion = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundCompanion)
|
||||||
|
{
|
||||||
|
FileStream shapeStream;
|
||||||
|
shapeStream.open(companionPath.getFullPath(), Torque::FS::File::Read);
|
||||||
|
if (shapeStream.getStatus() == Stream::Ok)
|
||||||
|
{
|
||||||
|
ret = new TSShape;
|
||||||
|
if (!ret->read(&shapeStream))
|
||||||
|
{
|
||||||
|
delete ret;
|
||||||
|
ret = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ret || !foundCompanion)
|
||||||
|
{
|
||||||
|
// No companion shape found; build a minimal empty shape so sequences
|
||||||
|
// have somewhere to live (matches ShapeAnimationAsset's expectations).
|
||||||
|
ret = new TSShape;
|
||||||
|
ret->createEmptyShape();
|
||||||
|
}
|
||||||
|
|
||||||
|
FileStream dsqStream;
|
||||||
|
dsqStream.open(path.getFullPath(), Torque::FS::File::Read);
|
||||||
|
if (dsqStream.getStatus() != Stream::Ok)
|
||||||
|
{
|
||||||
|
Con::errorf("Resource<TSShape>::create - Could not open DSQ '%s'", path.getFullPath().c_str());
|
||||||
|
delete ret;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
ret = new TSShape;
|
|
||||||
readSuccess = ret->read(&stream);
|
readSuccess = ret->importSequences(&dsqStream, path);
|
||||||
#endif
|
if (!readSuccess)
|
||||||
|
Con::errorf("Resource<TSShape>::create - Failed to import sequences from '%s'", path.getFullPath().c_str());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//Con::errorf( "Resource<TSShape>::create - '%s' has an unknown file format", path.getFullPath().c_str() );
|
const TSShape::ShapeRegistration* regInfo = TSShape::sFindRegInfo(extension);
|
||||||
//delete ret;
|
if (regInfo == NULL)
|
||||||
//return NULL;
|
|
||||||
|
|
||||||
// andrewmac: Open Asset Import Library
|
|
||||||
#ifdef TORQUE_ASSIMP
|
|
||||||
ret = assimpLoadShape(path);
|
|
||||||
readSuccess = (ret != NULL);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// andrewmac : I could have used another conditional macro but I think this is suffice:
|
|
||||||
if (!readSuccess)
|
|
||||||
{
|
{
|
||||||
Con::errorf("Resource<TSShape>::create - '%s' has an unknown file format", path.getFullPath().c_str());
|
readSuccess = false;
|
||||||
delete ret;
|
}
|
||||||
return NULL;
|
else
|
||||||
|
{
|
||||||
|
readSuccess = regInfo->readFunc(path, ret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -83,18 +83,47 @@ struct TSShapeVertexArray
|
||||||
/// @see TSShapeInstance for a further discussion of the 3space system.
|
/// @see TSShapeInstance for a further discussion of the 3space system.
|
||||||
class TSShape
|
class TSShape
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum
|
enum
|
||||||
|
{
|
||||||
|
UniformScale = BIT(0),
|
||||||
|
AlignedScale = BIT(1),
|
||||||
|
ArbitraryScale = BIT(2),
|
||||||
|
Blend = BIT(3),
|
||||||
|
Cyclic = BIT(4),
|
||||||
|
MakePath = BIT(5),
|
||||||
|
HasTranslucency= BIT(6),
|
||||||
|
AnyScale = UniformScale | AlignedScale | ArbitraryScale
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ShapeFormat
|
||||||
|
{
|
||||||
|
String mName;
|
||||||
|
String mExtension;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ShapeRegistration
|
||||||
|
{
|
||||||
|
typedef bool(*ReadShape)(const Torque::Path& path, TSShape*& shape);
|
||||||
|
typedef bool(*WriteShape)(const Torque::Path& path, TSShape* shape);
|
||||||
|
Vector<ShapeFormat> extensions; ///< the list of file extensions for this Loader [these should be lower case]
|
||||||
|
Vector<ShapeFormat> export_extensions; ///< the list of file extensions for this Loader [these should be lower case]
|
||||||
|
|
||||||
|
ReadShape readFunc; ///< the read function to read from a file.
|
||||||
|
WriteShape writeFunc; ///< the write function to write to a file.
|
||||||
|
|
||||||
|
ShapeRegistration()
|
||||||
{
|
{
|
||||||
UniformScale = BIT(0),
|
readFunc = NULL;
|
||||||
AlignedScale = BIT(1),
|
writeFunc = NULL;
|
||||||
ArbitraryScale = BIT(2),
|
VECTOR_SET_ASSOCIATION(extensions);
|
||||||
Blend = BIT(3),
|
VECTOR_SET_ASSOCIATION(export_extensions);
|
||||||
Cyclic = BIT(4),
|
}
|
||||||
MakePath = BIT(5),
|
};
|
||||||
HasTranslucency= BIT(6),
|
|
||||||
AnyScale = UniformScale | AlignedScale | ArbitraryScale
|
static void sRegisterFormat(const ShapeRegistration& reg);
|
||||||
};
|
static const ShapeRegistration* sFindRegInfo(const String& extension, bool exporting = false);
|
||||||
|
static Vector<ShapeRegistration> sRegistrations;
|
||||||
|
|
||||||
/// Nodes hold the transforms in the shape's tree. They are the bones of the skeleton.
|
/// Nodes hold the transforms in the shape's tree. They are the bones of the skeleton.
|
||||||
struct Node
|
struct Node
|
||||||
|
|
@ -422,6 +451,8 @@ class TSShape
|
||||||
// constructor/destructor
|
// constructor/destructor
|
||||||
TSShape();
|
TSShape();
|
||||||
~TSShape();
|
~TSShape();
|
||||||
|
void compressionKey(U8* keyOut, U32 keyLen);
|
||||||
|
void xorBufferAtOffset(void* data, U32 byteCount, U32 startOffset, const U8* key, U32 keyLen);
|
||||||
void init();
|
void init();
|
||||||
void initMaterialList(); ///< you can swap in a new material list, but call this if you do
|
void initMaterialList(); ///< you can swap in a new material list, but call this if you do
|
||||||
void finalizeEditable();
|
void finalizeEditable();
|
||||||
|
|
|
||||||
|
|
@ -1361,16 +1361,7 @@ bool TSShape::isShapeFileType(Torque::Path filePath)
|
||||||
{
|
{
|
||||||
String fileExt = filePath.getExtension();
|
String fileExt = filePath.getExtension();
|
||||||
|
|
||||||
if (
|
if (TSShape::sFindRegInfo(fileExt))
|
||||||
fileExt.equal("dts", String::NoCase) ||
|
|
||||||
fileExt.equal("dsq", String::NoCase) ||
|
|
||||||
fileExt.equal("dae", String::NoCase) ||
|
|
||||||
fileExt.equal("fbx", String::NoCase) ||
|
|
||||||
fileExt.equal("blend", String::NoCase) ||
|
|
||||||
fileExt.equal("obj", String::NoCase) ||
|
|
||||||
fileExt.equal("gltf", String::NoCase) ||
|
|
||||||
fileExt.equal("glb", String::NoCase)
|
|
||||||
)
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -435,7 +435,8 @@ bool TSShape::importSequences(Stream * s, const String& sequencePath)
|
||||||
{
|
{
|
||||||
// error -- don't support future version yet :>
|
// error -- don't support future version yet :>
|
||||||
Con::errorf(ConsoleLogEntry::General,
|
Con::errorf(ConsoleLogEntry::General,
|
||||||
"Sequence import failed: shape exporter newer than running executable.");
|
"Sequence import failed: attempt to load a version %i dsq-animation, can currently only load version %i and before.",
|
||||||
|
smReadVersion, smVersion);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (smReadVersion<19)
|
if (smReadVersion<19)
|
||||||
|
|
|
||||||
|
|
@ -79,26 +79,30 @@ bool EventManagerListener::onMessageReceived( StringTableEntry queue, const char
|
||||||
if( subscribers == NULL )
|
if( subscribers == NULL )
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
for (U32 i = 0; i < subscribers->size(); i++)
|
for (U32 i = 0; i < subscribers->size();)
|
||||||
{
|
{
|
||||||
Subscriber* itter = &((*subscribers)[i]);
|
Subscriber& itter = (*subscribers)[i];
|
||||||
if (itter->listener == NULL)
|
|
||||||
|
if (itter.listener == NULL)
|
||||||
{
|
{
|
||||||
(*subscribers).erase(i);
|
subscribers->erase(i);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
else if (!itter->removeFlag)
|
|
||||||
|
if (!itter.removeFlag)
|
||||||
{
|
{
|
||||||
itter->callDepth++;
|
itter.callDepth++;
|
||||||
Con::executef(itter->listener, itter->callback, data);
|
Con::executef(itter.listener, itter.callback, data);
|
||||||
itter->callDepth--;
|
itter.callDepth--;
|
||||||
if (itter->removeFlag && itter->callDepth == 0)
|
|
||||||
|
if (itter.removeFlag && itter.callback == 0)
|
||||||
{
|
{
|
||||||
(*subscribers).erase(i);
|
subscribers->erase(i);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subscribers->size() == 0)
|
i++;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -270,7 +274,7 @@ bool EventManager::postEvent( const char* event, const char* data )
|
||||||
|
|
||||||
// CodeReview [tom, 2/20/2007] The "listener" argument was an IMessageListener,
|
// CodeReview [tom, 2/20/2007] The "listener" argument was an IMessageListener,
|
||||||
// but it was actually used as a SimObject and never a listener. Thus, it is now a SimObject.
|
// but it was actually used as a SimObject and never a listener. Thus, it is now a SimObject.
|
||||||
bool EventManager::subscribe(SimObject *callbackObj, const char* event, const char* callback /*= NULL */)
|
bool EventManager::subscribe(SimObject *callbackObj, const char* event, const char* callback /*= NULL */, S32 priority /*=0*/)
|
||||||
{
|
{
|
||||||
// Make sure the event is valid.
|
// Make sure the event is valid.
|
||||||
if( !isRegisteredEvent( event ) )
|
if( !isRegisteredEvent( event ) )
|
||||||
|
|
@ -300,6 +304,7 @@ bool EventManager::subscribe(SimObject *callbackObj, const char* event, const ch
|
||||||
subscriber.listener = callbackObj;
|
subscriber.listener = callbackObj;
|
||||||
subscriber.event = StringTable->insert( event );
|
subscriber.event = StringTable->insert( event );
|
||||||
subscriber.callback = StringTable->insert( cb );
|
subscriber.callback = StringTable->insert( cb );
|
||||||
|
subscriber.priority = priority;
|
||||||
subscriber.callDepth = 0;
|
subscriber.callDepth = 0;
|
||||||
subscriber.removeFlag = false;
|
subscriber.removeFlag = false;
|
||||||
|
|
||||||
|
|
@ -311,8 +316,22 @@ bool EventManager::subscribe(SimObject *callbackObj, const char* event, const ch
|
||||||
// If the event exists, there should always be a valid subscriber list.
|
// If the event exists, there should always be a valid subscriber list.
|
||||||
AssertFatal( subscribers, "Invalid event subscriber list." );
|
AssertFatal( subscribers, "Invalid event subscriber list." );
|
||||||
|
|
||||||
// Add the subscriber.
|
// Add the subscriber at the appropriate index based on priority.
|
||||||
subscribers->push_back( subscriber );
|
S32 insertIndex = subscribers->size();
|
||||||
|
|
||||||
|
for (S32 i = 0; i < subscribers->size(); i++)
|
||||||
|
{
|
||||||
|
EventManagerListener::Subscriber& cur = (*subscribers)[i];
|
||||||
|
|
||||||
|
if (subscriber.priority > cur.priority)
|
||||||
|
{
|
||||||
|
insertIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribers->insert(insertIndex);
|
||||||
|
(*subscribers)[insertIndex] = subscriber;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -342,10 +361,10 @@ void EventManager::remove(SimObject *cbObj, const char* event)
|
||||||
// Erase the event.
|
// Erase the event.
|
||||||
if( iter->listener == cbObj )
|
if( iter->listener == cbObj )
|
||||||
{
|
{
|
||||||
if (iter->callDepth > 0)
|
if (iter->callDepth > 0)
|
||||||
iter->removeFlag = true;
|
iter->removeFlag = true;
|
||||||
else
|
else
|
||||||
subscribers->erase_fast( iter );
|
subscribers->erase_fast( iter );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -356,21 +375,21 @@ void EventManager::removeAll(SimObject *cbObj)
|
||||||
// Iterate over all events.
|
// Iterate over all events.
|
||||||
for( Vector<StringTableEntry>::const_iterator iter1 = mEvents.begin(); iter1 != mEvents.end(); iter1++ )
|
for( Vector<StringTableEntry>::const_iterator iter1 = mEvents.begin(); iter1 != mEvents.end(); iter1++ )
|
||||||
{
|
{
|
||||||
Vector<EventManagerListener::Subscriber>* subscribers = mListener.mSubscribers.retreive( *iter1 );
|
Vector<EventManagerListener::Subscriber>* subscribers = mListener.mSubscribers.retreive( *iter1 );
|
||||||
if( !subscribers )
|
if( !subscribers )
|
||||||
continue;
|
continue;
|
||||||
for( Vector<EventManagerListener::Subscriber>::iterator iter2 = subscribers->begin(); iter2 != subscribers->end(); iter2++ )
|
for( Vector<EventManagerListener::Subscriber>::iterator iter2 = subscribers->begin(); iter2 != subscribers->end(); iter2++ )
|
||||||
{
|
{
|
||||||
// Erase the event.
|
// Erase the event.
|
||||||
if( iter2->listener == cbObj )
|
if( iter2->listener == cbObj )
|
||||||
{
|
{
|
||||||
if (iter2->callDepth > 0)
|
if (iter2->callDepth > 0)
|
||||||
iter2->removeFlag = true;
|
iter2->removeFlag = true;
|
||||||
else
|
else
|
||||||
subscribers->erase_fast( iter2 );
|
subscribers->erase_fast( iter2 );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -460,7 +479,7 @@ DefineEngineMethod( EventManager, postEvent, bool, ( const char * evt, const cha
|
||||||
return object->postEvent( evt, data );
|
return object->postEvent( evt, data );
|
||||||
}
|
}
|
||||||
|
|
||||||
DefineEngineMethod( EventManager, subscribe, bool, ( const char * listenerName, const char * evt, const char * callback ), (""), "( SimObject listener, String event, String callback )\n\n"
|
DefineEngineMethod( EventManager, subscribe, bool, ( const char * listenerName, const char * evt, const char * callback, S32 priority ), ("", 0), "( SimObject listener, String event, String callback, int priority = 0 )\n\n"
|
||||||
"Subscribe a listener to an event.\n"
|
"Subscribe a listener to an event.\n"
|
||||||
"@param listener The listener to subscribe.\n"
|
"@param listener The listener to subscribe.\n"
|
||||||
"@param event The event to subscribe to.\n"
|
"@param event The event to subscribe to.\n"
|
||||||
|
|
@ -475,7 +494,7 @@ DefineEngineMethod( EventManager, subscribe, bool, ( const char * listenerName,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return object->subscribe( cbObj, evt, callback );
|
return object->subscribe( cbObj, evt, callback, priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
DefineEngineMethod( EventManager, remove, void, ( const char * listenerName, const char * evt), , "( SimObject listener, String event )\n\n"
|
DefineEngineMethod( EventManager, remove, void, ( const char * listenerName, const char * evt), , "( SimObject listener, String event )\n\n"
|
||||||
|
|
|
||||||
|
|
@ -47,11 +47,13 @@ class EventManagerListener : public Dispatcher::IMessageListener
|
||||||
/// Stores subscription information for a subscriber.
|
/// Stores subscription information for a subscriber.
|
||||||
struct Subscriber
|
struct Subscriber
|
||||||
{
|
{
|
||||||
SimObjectPtr< SimObject > listener; ///< The listener object.
|
SimObjectPtr< SimObject > listener; ///< The listener object.
|
||||||
StringTableEntry callback; ///< The callback to execute when the event is triggered.
|
StringTableEntry callback; ///< The callback to execute when the event is triggered.
|
||||||
StringTableEntry event; ///< The event being listened for.
|
StringTableEntry event; ///< The event being listened for.
|
||||||
U32 callDepth;
|
|
||||||
bool removeFlag;
|
S32 priority;
|
||||||
|
U32 callDepth;
|
||||||
|
bool removeFlag;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Subscriber table hashed by event name.
|
/// Subscriber table hashed by event name.
|
||||||
|
|
@ -154,7 +156,7 @@ public:
|
||||||
/// Triggers an event.
|
/// Triggers an event.
|
||||||
bool postEvent( const char* eventName, const char* data );
|
bool postEvent( const char* eventName, const char* data );
|
||||||
/// Adds a subscription to an event.
|
/// Adds a subscription to an event.
|
||||||
bool subscribe( SimObject *callbackObj, const char* event, const char* callback = NULL );
|
bool subscribe( SimObject *callbackObj, const char* event, const char* callback = NULL, S32 priority = 0 );
|
||||||
/// Remove a subscriber from an event.
|
/// Remove a subscriber from an event.
|
||||||
void remove( SimObject *cbObj, const char* event );
|
void remove( SimObject *cbObj, const char* event );
|
||||||
void removeAll( SimObject *cbObj );
|
void removeAll( SimObject *cbObj );
|
||||||
|
|
|
||||||
|
|
@ -231,3 +231,4 @@
|
||||||
|
|
||||||
/// Password to use when opening encrypted zip files. Change this to whatever the password is for your zips.
|
/// Password to use when opening encrypted zip files. Change this to whatever the password is for your zips.
|
||||||
#define DEFAULT_ZIP_PASSWORD "@TORQUE_APP_PASSWORD@"
|
#define DEFAULT_ZIP_PASSWORD "@TORQUE_APP_PASSWORD@"
|
||||||
|
#define TORQUE_DTS_VERSION @TORQUE_DTS_VERSION@
|
||||||
|
|
@ -114,6 +114,12 @@ advanced_option(TORQUE_DISABLE_MEMORY_MANAGER "Disable memory manager" ON)
|
||||||
set(TORQUE_ENTRY_FUNCTION "" CACHE STRING "Specify a console function to execute instead of looking for a main.tscript file")
|
set(TORQUE_ENTRY_FUNCTION "" CACHE STRING "Specify a console function to execute instead of looking for a main.tscript file")
|
||||||
mark_as_advanced(TORQUE_ENTRY_FUNCTION)
|
mark_as_advanced(TORQUE_ENTRY_FUNCTION)
|
||||||
|
|
||||||
|
set(TORQUE_DTS_VERSION "29" CACHE STRING "Device Tree Source version")
|
||||||
|
|
||||||
|
set_property(CACHE TORQUE_DTS_VERSION PROPERTY STRINGS
|
||||||
|
28 29
|
||||||
|
)
|
||||||
|
|
||||||
#fileIO
|
#fileIO
|
||||||
set(TORQUE_APP_PASSWORD "changeme" CACHE STRING "zip file password")
|
set(TORQUE_APP_PASSWORD "changeme" CACHE STRING "zip file password")
|
||||||
advanced_option(TORQUE_DISABLE_VIRTUAL_MOUNT_SYSTEM "Disable virtual mount system" OFF)
|
advanced_option(TORQUE_DISABLE_VIRTUAL_MOUNT_SYSTEM "Disable virtual mount system" OFF)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue