Torque3D/Engine/source/ts/collada/colladaAppNode.cpp

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 (S32 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().mElement = node->getContents()[iChild];
break;
}
}
}
// Get all child nodes
void ColladaAppNode::buildChildList()
{
// Process children: collect <node> and <instance_node> elements
for (S32 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 (S32 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 (S32 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().mUnit);
if (!isBounds())
ColladaUtils::convertTransform(lastTransform); // don't convert bounds node transform (or upAxis won't work!)
}
// Multiply by local node transform elements
for (S32 iTxfm = 0; iTxfm < nodeTransforms.size(); iTxfm++) {
MatrixF mat(true);
// Convert the transform element to a MatrixF
switch (nodeTransforms[iTxfm].mElement->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().mIgnoreNodeScale)
{
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;
}