Files
bullet3/Extras/FCollada/FCDocument/FCDSceneNode.cpp
2006-05-25 19:18:29 +00:00

492 lines
16 KiB
C++

/*
Copyright (C) 2005-2006 Feeling Software Inc.
MIT License: http://www.opensource.org/licenses/mit-license.php
*/
/*
Based on the FS Import classes:
Copyright (C) 2005-2006 Feeling Software Inc
Copyright (C) 2005-2006 Autodesk Media Entertainment
MIT License: http://www.opensource.org/licenses/mit-license.php
*/
#include "StdAfx.h"
#include "FCDocument/FCDocument.h"
#include "FCDocument/FCDAnimated.h"
#include "FCDocument/FCDAnimationCurve.h"
#include "FCDocument/FCDCamera.h"
#include "FCDocument/FCDController.h"
#include "FCDocument/FCDEntityInstance.h"
#include "FCDocument/FCDExternalReference.h"
#include "FCDocument/FCDExtra.h"
#include "FCDocument/FCDGeometry.h"
#include "FCDocument/FCDGeometryInstance.h"
#include "FCDocument/FCDLight.h"
#include "FCDocument/FCDPhysicsModelInstance.h"
#include "FCDocument/FCDPhysicsRigidBodyInstance.h"
#include "FCDocument/FCDSceneNode.h"
#include "FCDocument/FCDTransform.h"
#include "FUtils/FUDaeParser.h"
#include "FUtils/FUDaeWriter.h"
#include "FUtils/FUFileManager.h"
#include "FUtils/FUStringConversion.h"
using namespace FUDaeParser;
using namespace FUDaeWriter;
FCDSceneNode::FCDSceneNode(FCDocument* document) : FCDEntity(document, "VisualSceneNode")
{
isJoint = false;
targetCount = 0;
visibility = 1.0f;
}
FCDSceneNode::~FCDSceneNode()
{
CLEAR_POINTER_VECTOR(instances);
CLEAR_POINTER_VECTOR(transforms);
parents.clear();
// Delete the children, be watchful for the instantiated node
for (FCDSceneNodeList::iterator it = children.begin(); it != children.end(); ++it)
{
FCDSceneNode* child = (*it);
for (FCDSceneNodeList::iterator itParent = child->parents.begin(); itParent != child->parents.end(); ++itParent)
{
if ((*itParent) == this)
{
child->parents.erase(itParent);
break;
}
}
if (child->parents.empty()) delete child;
}
children.clear();
}
// Add this scene node to the list of children scene node
bool FCDSceneNode::AddChildNode(FCDSceneNode* sceneNode)
{
if (this == sceneNode || sceneNode == NULL)
{
return false;
}
// Verify that we don't already contain this child node.
FCDSceneNodeList::iterator it = std::find(children.begin(), children.end(), sceneNode);
if (it != children.end()) return false;
// Verify that this node is not one of the parents in the full hierarchically.
FCDSceneNodeList queue = parents;
while (!queue.empty())
{
FCDSceneNode* parent = queue.back();
queue.pop_back();
if (parent == sceneNode) return false;
queue.insert(queue.end(), parent->parents.begin(), parent->parents.end());
}
children.push_back(sceneNode);
sceneNode->parents.push_back(this);
return true;
}
FCDSceneNode* FCDSceneNode::AddChildNode()
{
FCDSceneNode* node = new FCDSceneNode(GetDocument());
AddChildNode(node);
return node;
}
FCDTransform* FCDSceneNode::AddTransform(FCDTransform::Type type, size_t index)
{
FCDTransform* transform = FCDTFactory::CreateTransform(GetDocument(), this, type);
if (transform != NULL)
{
if (index > transforms.size()) transforms.push_back(transform);
else transforms.insert(transforms.begin() + index, transform);
}
return transform;
}
// Traverse the scene graph, searching for a node with the given COLLADA id
FCDEntity* FCDSceneNode::FindDaeId(const string& daeId)
{
if (GetDaeId() == daeId) return this;
for (FCDSceneNodeList::iterator it = children.begin(); it != children.end(); ++it)
{
FCDEntity* found = (*it)->FindDaeId(daeId);
if (found != NULL) return found;
}
return NULL;
}
// Calculate the transform matrix for a given scene node
FMMatrix44 FCDSceneNode::ToMatrix() const
{
FMMatrix44 localTransform = FMMatrix44::Identity;
for (FCDTransformList::const_iterator it = transforms.begin(); it != transforms.end(); ++it)
{
localTransform = localTransform * (*it)->ToMatrix();
}
return localTransform;
}
void FCDSceneNode::GenerateSampledMatrixAnimation(FloatList& sampleKeys, FMMatrix44List& sampleValues)
{
FCDAnimatedList animateds;
// Collect all the animation curves
for (FCDTransformList::iterator it = transforms.begin(); it != transforms.end(); ++it)
{
FCDAnimated* animated = (*it)->GetAnimated();
if (animated != NULL && animated->HasCurve()) animateds.push_back(animated);
}
if (animateds.empty()) return;
// Make a list of the ordered key times to sample
for (FCDAnimatedList::iterator it = animateds.begin(); it != animateds.end(); ++it)
{
const FCDAnimationCurveList& curves = (*it)->GetCurves();
for (FCDAnimationCurveList::const_iterator curveIt = curves.begin(); curveIt != curves.end(); ++curveIt)
{
if ((*curveIt) == NULL) continue;
const FloatList& curveKeys = (*curveIt)->GetKeys();
size_t sampleKeyCount = sampleKeys.size();
size_t curveKeyCount = curveKeys.size();
// Merge this curve's keys in with the sample keys
// This assumes both key lists are in increasing order
size_t s = 0, c = 0;
while (s < sampleKeyCount && c < curveKeyCount)
{
float sampleKey = sampleKeys[s], curveKey = curveKeys[c];
if (IsEquivalent(sampleKey, curveKey)) { ++s; ++c; }
else if (sampleKey < curveKey) { ++s; }
else
{
// Add this curve key to the sampling key list
sampleKeys.insert(sampleKeys.begin() + (s++), curveKeys[c++]);
sampleKeyCount++;
}
}
// Add all the left-over curve keys to the sampling key list
if (c < curveKeyCount) sampleKeys.insert(sampleKeys.end(), curveKeys.begin() + c, curveKeys.end());
}
}
size_t sampleKeyCount = sampleKeys.size();
if (sampleKeyCount == 0) return;
// Pre-allocate the value array;
sampleValues.reserve(sampleKeyCount);
// Sample the scene node transform
for (size_t i = 0; i < sampleKeyCount; ++i)
{
float sampleTime = sampleKeys[i];
for (FCDAnimatedList::iterator it = animateds.begin(); it != animateds.end(); ++it)
{
// Sample each animated, which changes the transform values directly
(*it)->Evaluate(sampleTime);
}
// Retrieve the new transform matrix for the COLLADA scene node
sampleValues.push_back(ToMatrix());
}
}
// Parse a <scene> or a <node> node from a COLLADA document
FUStatus FCDSceneNode::LoadFromXML(xmlNode* sceneNode)
{
FUStatus status = FCDEntity::LoadFromXML(sceneNode);
if (!status) return status;
if (!IsEquivalent(sceneNode->name, DAE_VSCENE_ELEMENT) && !IsEquivalent(sceneNode->name, DAE_NODE_ELEMENT)
&& !IsEquivalent(sceneNode->name, DAE_SCENE_ELEMENT))
{
// The <scene> element is accepted here only as COLLADA 1.3 backward compatibility
return status.Fail(FS("Unknown scene node element with id: ") + TO_FSTRING(GetDaeId()), sceneNode->line);
}
// look up the visual scene to extrace bind info
if (IsEquivalent(sceneNode->name, DAE_VSCENE_ELEMENT)) BindMaterial(sceneNode);
// Read in the <node> element's type
string nodeType = ReadNodeProperty(sceneNode, DAE_TYPE_ATTRIBUTE);
if (nodeType == DAE_JOINT_NODE_TYPE) SetJointFlag(true);
else if (nodeType.length() == 0 || nodeType == DAE_NODE_NODE_TYPE) {} // No special consideration
else
{
status.Warning(FS("Unknown node type for scene's <node> element: ") + TO_FSTRING(GetDaeId()), sceneNode->line);
}
// The scene node has ordered elements, so process them directly and in order.
for (xmlNode* child = sceneNode->children; child != NULL; child = child->next)
{
if (child->type != XML_ELEMENT_NODE) continue;
if (IsEquivalent(child->name, DAE_NODE_ELEMENT))
{
// Load the child scene node
FCDSceneNode* node = AddChildNode();
status = node->LoadFromXML(child);
if (!status) break;
}
// Look for instantiation elements
# define INSTANTIATE(name, instanceType) { \
FUUri url = ReadNodeUrl(child); \
if (url.prefix.empty()) { \
FCD##name* entity = GetDocument()->Find##name(url.suffix); \
if (entity != NULL) { \
instanceType* instance = new instanceType(GetDocument(), entity); \
instances.push_back(instance); \
status.AppendStatus(instance->LoadFromXML(child)); \
continue; } } \
status.Warning(fstring(FC("Unable to retrieve '") FC(#name) FC("' instance for scene node: ")) + TO_FSTRING(GetDaeId()), child->line); }
else if (IsEquivalent(child->name, DAE_INSTANCE_CAMERA_ELEMENT)) { INSTANTIATE(Camera, FCDEntityInstance); }
else if (IsEquivalent(child->name, DAE_INSTANCE_CONTROLLER_ELEMENT)) { INSTANTIATE(Controller, FCDGeometryInstance); }
else if (IsEquivalent(child->name, DAE_INSTANCE_GEOMETRY_ELEMENT)) { INSTANTIATE(Geometry, FCDGeometryInstance); }
else if (IsEquivalent(child->name, DAE_INSTANCE_LIGHT_ELEMENT)) { INSTANTIATE(Light, FCDEntityInstance); }
else if (IsEquivalent(child->name, DAE_INSTANCE_NODE_ELEMENT))
{
FUUri url = ReadNodeUrl(child);
if (url.prefix.empty())
{
FCDSceneNode* node = GetDocument()->FindSceneNode(url.suffix);
if (node != NULL)
{
if (!AddChildNode(node))
{
status.Warning(FS("A cycle was found in the visual scene at node: ") + TO_FSTRING(GetDaeId()), child->line);
}
}
else
{
status.Warning(FS("Unable to retrieve node instance for scene node: ") + TO_FSTRING(GetDaeId()), child->line);
}
}
}
# undef INSTANTIATE
else if (IsEquivalent(child->name, DAE_INSTANCE_ELEMENT))
{
// COLLADA 1.3 backward compatibility: Weakly-typed instantiation
// Might be a geometry, controller, camera or light.
FUUri url = ReadNodeUrl(child);
if (url.prefix.empty())
{
# define INSTANTIATE(name, instanceType) { \
FCD##name* entity = GetDocument()->Find##name(url.suffix); \
if (entity != NULL) { \
instanceType* instance = new instanceType(GetDocument(), entity); \
instances.push_back(instance); \
instance->LoadFromXML(child); \
continue; } }
INSTANTIATE(Geometry, FCDGeometryInstance);
INSTANTIATE(Controller, FCDGeometryInstance);
INSTANTIATE(Camera, FCDEntityInstance);
INSTANTIATE(Light, FCDEntityInstance);
# undef INSTANTIATE
FCDSceneNode* node = GetDocument()->FindSceneNode(url.suffix);
if (node != NULL)
{
if (!AddChildNode(node))
{
status.Warning(FS("A cycle was found in the visual scene at node: ") + TO_FSTRING(GetDaeId()), child->line);
}
}
else
{
status.Warning(FS("Unable to retrieve node instance for scene node: ") + TO_FSTRING(GetDaeId()), child->line);
}
status.Warning(FS("Unable to retrieve weakly-typed instance for scene node: ") + TO_FSTRING(GetDaeId()), child->line);
}
else
{
GetDocument()->GetFileManager()->GetFilePath(url.prefix);
instances.push_back(new FCDExternalReference(GetDocument(), url));
}
}
else if (IsEquivalent(child->name, DAE_EXTRA_ELEMENT)) {}
else if (IsEquivalent(child->name, DAE_ASSET_ELEMENT)) {}
else
{
FCDTransform* transform = FCDTFactory::CreateTransform(GetDocument(), this, child);
if (transform != NULL)
{
transforms.push_back(transform);
status.AppendStatus(transform->LoadFromXML(child));
}
else
{
status.Warning(FS("Unknown element or bad transform in scene node: ") + TO_FSTRING(GetDaeId()), child->line);
}
}
}
status.AppendStatus(LoadFromExtra());
return status;
}
FUStatus FCDSceneNode::LoadFromExtra()
{
FUStatus status;
FCDENodeList parameterNodes;
StringList parameterNames;
// Retrieve the extra information from the base entity class
FCDExtra* extra = GetExtra();
// Read the Maya-specific technique
FCDETechnique* mayaTechnique = extra->FindTechnique(DAEMAYA_MAYA_PROFILE);
if (mayaTechnique == NULL) return status;
mayaTechnique->FindParameters(parameterNodes, parameterNames);
size_t parameterCount = parameterNodes.size();
for (size_t i = 0; i < parameterCount; ++i)
{
FCDENode* parameterNode = parameterNodes[i];
const string& parameterName = parameterNames[i];
FCDEAttribute* parameterType = parameterNode->FindAttribute(DAE_TYPE_ATTRIBUTE);
if (parameterName == DAEMAYA_STARTTIME_PARAMETER || parameterName == DAEMAYA_STARTTIME_PARAMETER1_3)
{
GetDocument()->SetStartTime(FUStringConversion::ToFloat(parameterNode->GetContent()));
parameterNode->Release();
}
else if (parameterName == DAEMAYA_ENDTIME_PARAMETER || parameterName == DAEMAYA_ENDTIME_PARAMETER1_3)
{
GetDocument()->SetEndTime(FUStringConversion::ToFloat(parameterNode->GetContent()));
parameterNode->Release();
}
else if (parameterName == DAEMAYA_VISIBILITY_PARAMETER || parameterName == DAEMAYA_VISIBILITY_PARAMETER1_3)
{
visibility = FUStringConversion::ToBoolean(parameterNode->GetContent()) ? 1.0f : 0.0f;
FCDAnimatedCustom* animated = parameterNode->GetAnimated();
if (animated != NULL)
{
FCDAnimatedFloat::Clone(GetDocument(), &animated->GetDummy(), &visibility);
}
parameterNode->Release();
}
else if (parameterName == DAEMAYA_LAYER_PARAMETER || (parameterType != NULL && FUStringConversion::ToString(parameterType->value) == DAEMAYA_LAYER_PARAMETER))
{
FCDEAttribute* nameAttribute = parameterNode->FindAttribute(DAE_NAME_ATTRIBUTE);
if (nameAttribute == NULL) continue;
// Create a new layer object list
FCDLayerList& layers = GetDocument()->GetLayers();
FCDLayer* layer = new FCDLayer(); layers.push_back(layer);
// Parse in the layer
layer->name = FUStringConversion::ToString(nameAttribute->value);
FUStringConversion::ToStringList(parameterNode->GetContent(), layer->objects);
parameterNode->Release();
}
}
return status;
}
// bind material info
void FCDSceneNode::BindMaterial(xmlNode* node)
{
xmlNodeList nodelist;
FindChildrenByType(node, DAE_NODE_ELEMENT, nodelist);
if(nodelist.size() == 0) return;
for(xmlNodeList::iterator itN = nodelist.begin(); itN != nodelist.end(); ++itN)
{
BindMaterial(*itN);
xmlNode* geometry = FindChildByType(*itN, DAE_INSTANCE_GEOMETRY_ELEMENT);
if(geometry == NULL) continue;
xmlNode* bindmatNode = FindChildByType(geometry, DAE_BINDMATERIAL_ELEMENT);
xmlNode* techniqueNode = FindChildByType(bindmatNode, DAE_TECHNIQUE_COMMON_ELEMENT);
xmlNode* materialNode = FindChildByType(techniqueNode, DAE_INSTANCE_MATERIAL_ELEMENT);
string shadername = ReadNodeProperty(materialNode, DAE_SYMBOL_ATTRIBUTE);
xmlNodeList bindNodes;
FindChildrenByType( materialNode, DAE_BIND_ELEMENT, bindNodes);
for (xmlNodeList::iterator itB = bindNodes.begin(); itB != bindNodes.end(); ++itB)
{
string semantic = ReadNodeSemantic(*itB);
string target = ReadNodeProperty(*itB, DAE_TARGET_ATTRIBUTE);
GetPostProcessCmds().push_back(shadername);
GetPostProcessCmds().push_back(target);
GetPostProcessCmds().push_back(semantic);
}
}
}
// Write out a <visual_scene> element to a COLLADA xml document
xmlNode* FCDSceneNode::WriteToXML(xmlNode* parentNode) const
{
xmlNode* visualSceneNode = WriteToEntityXML(parentNode, DAE_VSCENE_ELEMENT);
if (visualSceneNode != NULL)
{
WriteToNodeXML(visualSceneNode, true);
}
return visualSceneNode;
}
// Write out a <visual_scene> or a <node> element to a COLLADA xml document
void FCDSceneNode::WriteToNodeXML(xmlNode* node, bool isVisualScene) const
{
// Write out the extra attributes
if (!isVisualScene)
{
if (isJoint)
{
AddAttribute(node, DAE_SID_ATTRIBUTE, GetDaeId());
AddAttribute(node, DAE_TYPE_ATTRIBUTE, DAE_JOINT_NODE_TYPE);
}
else
{
AddAttribute(node, DAE_TYPE_ATTRIBUTE, DAE_NODE_NODE_TYPE);
}
}
// Write out the transforms
for (FCDTransformList::const_iterator itT = transforms.begin(); itT != transforms.end(); ++itT)
{
FCDTransform* transform = (*itT);
transform->WriteToXML(node);
}
// Write out the instantiation
for (FCDEntityInstanceList::const_iterator itI = instances.begin(); itI != instances.end(); ++itI)
{
FCDEntityInstance* instance = (*itI);
instance->WriteToXML(node);
}
// Write out the child scene graph nodes as <node> elements
if (!isVisualScene || !children.empty())
{
for (FCDSceneNodeList::const_iterator itC = children.begin(); itC != children.end(); ++itC)
{
FCDSceneNode* child = (*itC);
xmlNode* nodeNode = child->WriteToEntityXML(node, DAE_NODE_ELEMENT);
if (nodeNode != NULL) child->WriteToNodeXML(nodeNode, false);
}
}
else
{
// In COLLADA 1.4, the visual scene must contain at least one <node>.
UNUSED(xmlNode* dummyNodeNode =) FUXmlWriter::AddChild(node, DAE_NODE_ELEMENT);
}
// Write out the extra information
FCDEntity::WriteToExtraXML(node);
}