mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-20 12:44:46 +00:00
261 lines
9.2 KiB
C++
261 lines
9.2 KiB
C++
|
|
//-----------------------------------------------------------------------------
|
||
|
|
// Copyright (c) 2012 GarageGames, LLC
|
||
|
|
//
|
||
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
|
// of this software and associated documentation files (the "Software"), to
|
||
|
|
// deal in the Software without restriction, including without limitation the
|
||
|
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||
|
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||
|
|
// furnished to do so, subject to the following conditions:
|
||
|
|
//
|
||
|
|
// The above copyright notice and this permission notice shall be included in
|
||
|
|
// all copies or substantial portions of the Software.
|
||
|
|
//
|
||
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||
|
|
// IN THE SOFTWARE.
|
||
|
|
//-----------------------------------------------------------------------------
|
||
|
|
|
||
|
|
#include "platform/platform.h"
|
||
|
|
|
||
|
|
#ifdef _MSC_VER
|
||
|
|
#pragma warning(disable : 4706) // disable warning about assignment within conditional
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#include "ts/loader/appSequence.h"
|
||
|
|
#include "ts/collada/colladaExtensions.h"
|
||
|
|
#include "ts/collada/colladaAppNode.h"
|
||
|
|
#include "ts/collada/colladaAppMesh.h"
|
||
|
|
#include "ts/collada/colladaAppMesh.h"
|
||
|
|
|
||
|
|
#include "core/stringTable.h"
|
||
|
|
|
||
|
|
// Trim leading and trailing whitespace from the first word in the string
|
||
|
|
// Note that the string is modified.
|
||
|
|
static char* TrimFirstWord(char* str)
|
||
|
|
{
|
||
|
|
char* value = str;
|
||
|
|
|
||
|
|
// Trim leading whitespace
|
||
|
|
while ( value && *value && dIsspace( *value ) )
|
||
|
|
value++;
|
||
|
|
|
||
|
|
// Trim trailing whitespace
|
||
|
|
if ( value && *value )
|
||
|
|
{
|
||
|
|
char* end = value + 1;
|
||
|
|
while ( *end && !dIsspace( *end ) )
|
||
|
|
end++;
|
||
|
|
*end = '\0';
|
||
|
|
}
|
||
|
|
|
||
|
|
return value;
|
||
|
|
}
|
||
|
|
|
||
|
|
ColladaAppNode::ColladaAppNode(const domNode* node, ColladaAppNode* parent)
|
||
|
|
: p_domNode(node), appParent(parent), nodeExt(new ColladaExtension_node(node)),
|
||
|
|
lastTransformTime(TSShapeLoader::DefaultTime-1), defaultTransformValid(false),
|
||
|
|
invertMeshes(false)
|
||
|
|
{
|
||
|
|
mName = dStrdup(_GetNameOrId(node));
|
||
|
|
mParentName = dStrdup(parent ? parent->getName() : "ROOT");
|
||
|
|
|
||
|
|
// Extract user properties from the <node> extension as whitespace separated
|
||
|
|
// "name=value" pairs
|
||
|
|
char* properties = dStrdup(nodeExt->user_properties);
|
||
|
|
char* pos = properties;
|
||
|
|
char* end = properties + dStrlen( properties );
|
||
|
|
while ( pos < end )
|
||
|
|
{
|
||
|
|
// Find the '=' character to separate the name and value pair
|
||
|
|
char* split = dStrchr( pos, '=' );
|
||
|
|
if ( !split )
|
||
|
|
break;
|
||
|
|
|
||
|
|
// Get the name (whitespace trimmed string up to the '=')
|
||
|
|
// and value (whitespace trimmed string after the '=')
|
||
|
|
*split = '\0';
|
||
|
|
char* name = TrimFirstWord( pos );
|
||
|
|
char* value = TrimFirstWord( split + 1 );
|
||
|
|
|
||
|
|
mProps.insert(StringTable->insert(name), dAtof(value));
|
||
|
|
|
||
|
|
pos = value + dStrlen( value ) + 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
dFree( properties );
|
||
|
|
|
||
|
|
// Create vector of transform elements
|
||
|
|
for (int iChild = 0; iChild < node->getContents().getCount(); iChild++) {
|
||
|
|
switch (node->getContents()[iChild]->getElementType()) {
|
||
|
|
case COLLADA_TYPE::TRANSLATE:
|
||
|
|
case COLLADA_TYPE::ROTATE:
|
||
|
|
case COLLADA_TYPE::SCALE:
|
||
|
|
case COLLADA_TYPE::SKEW:
|
||
|
|
case COLLADA_TYPE::MATRIX:
|
||
|
|
case COLLADA_TYPE::LOOKAT:
|
||
|
|
nodeTransforms.increment();
|
||
|
|
nodeTransforms.last().element = node->getContents()[iChild];
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get all child nodes
|
||
|
|
void ColladaAppNode::buildChildList()
|
||
|
|
{
|
||
|
|
// Process children: collect <node> and <instance_node> elements
|
||
|
|
for (int iChild = 0; iChild < p_domNode->getContents().getCount(); iChild++) {
|
||
|
|
|
||
|
|
daeElement* child = p_domNode->getContents()[iChild];
|
||
|
|
switch (child->getElementType()) {
|
||
|
|
|
||
|
|
case COLLADA_TYPE::NODE:
|
||
|
|
{
|
||
|
|
domNode* node = daeSafeCast<domNode>(child);
|
||
|
|
mChildNodes.push_back(new ColladaAppNode(node, this));
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
case COLLADA_TYPE::INSTANCE_NODE:
|
||
|
|
{
|
||
|
|
domInstance_node* instanceNode = daeSafeCast<domInstance_node>(child);
|
||
|
|
domNode* node = daeSafeCast<domNode>(instanceNode->getUrl().getElement());
|
||
|
|
if (node)
|
||
|
|
mChildNodes.push_back(new ColladaAppNode(node, this));
|
||
|
|
else
|
||
|
|
Con::warnf("Failed to resolve instance_node with url=%s", instanceNode->getUrl().originalStr().c_str());
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get all geometry attached to this node
|
||
|
|
void ColladaAppNode::buildMeshList()
|
||
|
|
{
|
||
|
|
// Process children: collect <instance_geometry> and <instance_controller> elements
|
||
|
|
for (int iChild = 0; iChild < p_domNode->getContents().getCount(); iChild++) {
|
||
|
|
|
||
|
|
daeElement* child = p_domNode->getContents()[iChild];
|
||
|
|
switch (child->getElementType()) {
|
||
|
|
|
||
|
|
case COLLADA_TYPE::INSTANCE_GEOMETRY:
|
||
|
|
{
|
||
|
|
// Only <geometry>.<mesh> instances are supported
|
||
|
|
domInstance_geometry* instanceGeom = daeSafeCast<domInstance_geometry>(child);
|
||
|
|
if (instanceGeom) {
|
||
|
|
domGeometry* geometry = daeSafeCast<domGeometry>(instanceGeom->getUrl().getElement());
|
||
|
|
if (geometry && geometry->getMesh())
|
||
|
|
mMeshes.push_back(new ColladaAppMesh(instanceGeom, this));
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
case COLLADA_TYPE::INSTANCE_CONTROLLER:
|
||
|
|
mMeshes.push_back(new ColladaAppMesh(daeSafeCast<domInstance_controller>(child), this));
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bool ColladaAppNode::animatesTransform(const AppSequence* appSeq)
|
||
|
|
{
|
||
|
|
// Check if any of this node's transform elements are animated during the
|
||
|
|
// sequence interval
|
||
|
|
for (int iTxfm = 0; iTxfm < nodeTransforms.size(); iTxfm++) {
|
||
|
|
if (nodeTransforms[iTxfm].isAnimated(appSeq->getStart(), appSeq->getEnd()))
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Get the world transform of the node at the specified time
|
||
|
|
MatrixF ColladaAppNode::getNodeTransform(F32 time)
|
||
|
|
{
|
||
|
|
// Avoid re-computing the default transform if possible
|
||
|
|
if (defaultTransformValid && time == TSShapeLoader::DefaultTime)
|
||
|
|
{
|
||
|
|
return defaultNodeTransform;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
MatrixF nodeTransform = getTransform(time);
|
||
|
|
|
||
|
|
// Check for inverted node coordinate spaces => can happen when modelers
|
||
|
|
// use the 'mirror' tool in their 3d app. Shows up as negative <scale>
|
||
|
|
// transforms in the collada model.
|
||
|
|
if (m_matF_determinant(nodeTransform) < 0.0f)
|
||
|
|
{
|
||
|
|
// Mark this node as inverted so we can mirror mesh geometry, then
|
||
|
|
// de-invert the transform matrix
|
||
|
|
invertMeshes = true;
|
||
|
|
nodeTransform.scale(Point3F(1, 1, -1));
|
||
|
|
}
|
||
|
|
|
||
|
|
// Cache the default transform
|
||
|
|
if (time == TSShapeLoader::DefaultTime)
|
||
|
|
{
|
||
|
|
defaultTransformValid = true;
|
||
|
|
defaultNodeTransform = nodeTransform;
|
||
|
|
}
|
||
|
|
|
||
|
|
return nodeTransform;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
MatrixF ColladaAppNode::getTransform(F32 time)
|
||
|
|
{
|
||
|
|
// Check if we can use the last computed transform
|
||
|
|
if (time == lastTransformTime)
|
||
|
|
return lastTransform;
|
||
|
|
|
||
|
|
if (appParent) {
|
||
|
|
// Get parent node's transform
|
||
|
|
lastTransform = appParent->getTransform(time);
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
// no parent (ie. root level) => scale by global shape <unit>
|
||
|
|
lastTransform.identity();
|
||
|
|
lastTransform.scale(ColladaUtils::getOptions().unit);
|
||
|
|
if (!isBounds())
|
||
|
|
ColladaUtils::convertTransform(lastTransform); // don't convert bounds node transform (or upAxis won't work!)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Multiply by local node transform elements
|
||
|
|
for (int iTxfm = 0; iTxfm < nodeTransforms.size(); iTxfm++) {
|
||
|
|
|
||
|
|
MatrixF mat(true);
|
||
|
|
|
||
|
|
// Convert the transform element to a MatrixF
|
||
|
|
switch (nodeTransforms[iTxfm].element->getElementType()) {
|
||
|
|
case COLLADA_TYPE::TRANSLATE: mat = vecToMatrixF<domTranslate>(nodeTransforms[iTxfm].getValue(time)); break;
|
||
|
|
case COLLADA_TYPE::SCALE: mat = vecToMatrixF<domScale>(nodeTransforms[iTxfm].getValue(time)); break;
|
||
|
|
case COLLADA_TYPE::ROTATE: mat = vecToMatrixF<domRotate>(nodeTransforms[iTxfm].getValue(time)); break;
|
||
|
|
case COLLADA_TYPE::MATRIX: mat = vecToMatrixF<domMatrix>(nodeTransforms[iTxfm].getValue(time)); break;
|
||
|
|
case COLLADA_TYPE::SKEW: mat = vecToMatrixF<domSkew>(nodeTransforms[iTxfm].getValue(time)); break;
|
||
|
|
case COLLADA_TYPE::LOOKAT: mat = vecToMatrixF<domLookat>(nodeTransforms[iTxfm].getValue(time)); break;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Remove node scaling (but keep reflections) if desired
|
||
|
|
if (ColladaUtils::getOptions().ignoreNodeScale)
|
||
|
|
{
|
||
|
|
Point3F invScale = mat.getScale();
|
||
|
|
invScale.x = invScale.x ? (1.0f / invScale.x) : 0;
|
||
|
|
invScale.y = invScale.y ? (1.0f / invScale.y) : 0;
|
||
|
|
invScale.z = invScale.z ? (1.0f / invScale.z) : 0;
|
||
|
|
mat.scale(invScale);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Post multiply the animated transform
|
||
|
|
lastTransform.mul(mat);
|
||
|
|
}
|
||
|
|
|
||
|
|
lastTransformTime = time;
|
||
|
|
return lastTransform;
|
||
|
|
}
|