776 lines
28 KiB
C++
776 lines
28 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/FCDAnimation.h"
|
|
#include "FCDocument/FCDAnimationChannel.h"
|
|
#include "FCDocument/FCDAnimationClip.h"
|
|
#include "FCDocument/FCDAnimationCurve.h"
|
|
#include "FCDocument/FCDAsset.h"
|
|
#include "FCDocument/FCDCamera.h"
|
|
#include "FCDocument/FCDController.h"
|
|
#include "FCDocument/FCDGeometry.h"
|
|
#include "FCDocument/FCDImage.h"
|
|
#include "FCDocument/FCDLight.h"
|
|
#include "FCDocument/FCDLibrary.h"
|
|
#include "FCDocument/FCDMaterial.h"
|
|
#include "FCDocument/FCDMaterialLibrary.h"
|
|
#include "FCDocument/FCDPhysicsMaterial.h"
|
|
#include "FCDocument/FCDPhysicsModel.h"
|
|
#include "FCDocument/FCDPhysicsSceneNode.h"
|
|
#include "FCDocument/FCDSceneNode.h"
|
|
#include "FCDocument/FCDTexture.h"
|
|
#include "FUtils/FUDaeParser.h"
|
|
#include "FUtils/FUDaeWriter.h"
|
|
#include "FUtils/FUFileManager.h"
|
|
#include "FUtils/FUUniqueStringMap.h"
|
|
using namespace FUDaeParser;
|
|
using namespace FUDaeWriter;
|
|
|
|
FCDocument::FCDocument()
|
|
{
|
|
fileManager = new FUFileManager();
|
|
asset = new FCDAsset(this);
|
|
uniqueNameMap = new FUSUniqueStringMap();
|
|
|
|
animationLibrary = new FCDLibrary<FCDAnimation>(this);
|
|
animationClipLibrary = new FCDLibrary<FCDAnimationClip>(this);
|
|
cameraLibrary = new FCDLibrary<FCDCamera>(this);
|
|
controllerLibrary = new FCDLibrary<FCDController>(this);
|
|
geometryLibrary = new FCDLibrary<FCDGeometry>(this);
|
|
imageLibrary = new FCDLibrary<FCDImage>(this);
|
|
lightLibrary = new FCDLibrary<FCDLight>(this);
|
|
materialLibrary = new FCDMaterialLibrary(this);
|
|
visualSceneLibrary = new FCDLibrary<FCDSceneNode>(this);
|
|
|
|
//physics
|
|
physicsMaterialLibrary = new FCDLibrary<FCDPhysicsMaterial>(this);
|
|
physicsModelLibrary = new FCDLibrary<FCDPhysicsModel>(this);
|
|
physicsSceneLibrary = new FCDLibrary<FCDPhysicsSceneNode>(this);
|
|
physicsSceneRoot = NULL;
|
|
|
|
animatedValues.reserve(1024);
|
|
visualSceneRoot = NULL;
|
|
|
|
// Document global parameters
|
|
lengthUnitConversion = 1.0f;
|
|
lengthUnitWanted = -1.0f;
|
|
hasStartTime = hasEndTime = false;
|
|
startTime = endTime = 0.0f;
|
|
}
|
|
|
|
FCDocument::~FCDocument()
|
|
{
|
|
// Must be released first
|
|
CLEAR_POINTER_VECTOR(layers);
|
|
CLEAR_POINTER_VECTOR(animatedValues);
|
|
animatedValueMap.clear();
|
|
SAFE_DELETE(visualSceneLibrary);
|
|
|
|
SAFE_DELETE(animationLibrary);
|
|
SAFE_DELETE(animationClipLibrary);
|
|
SAFE_DELETE(cameraLibrary);
|
|
SAFE_DELETE(controllerLibrary);
|
|
SAFE_DELETE(geometryLibrary);
|
|
SAFE_DELETE(imageLibrary);
|
|
SAFE_DELETE(lightLibrary);
|
|
SAFE_DELETE(materialLibrary);
|
|
SAFE_DELETE(physicsModelLibrary);
|
|
SAFE_DELETE(physicsMaterialLibrary);
|
|
SAFE_DELETE(physicsSceneLibrary);
|
|
|
|
physicsSceneRoot = NULL;
|
|
visualSceneRoot = NULL;
|
|
|
|
SAFE_DELETE(fileManager);
|
|
SAFE_DELETE(asset);
|
|
}
|
|
|
|
// Retrieve the list of all the COLLADA materials in this document
|
|
const FCDMaterialList& FCDocument::GetMaterialList()
|
|
{
|
|
return materialLibrary->GetMaterials();
|
|
}
|
|
|
|
// These two parameters now belong to FCDAsset. Keep them here for a few version, for backward compatibility
|
|
const FMVector3& FCDocument::GetUpAxis() const { return asset->GetUpAxis(); }
|
|
float FCDocument::GetLengthUnit() const { return asset->GetUnitConversionFactor(); }
|
|
|
|
// Search for a driven curve that needs this animated value as a driver
|
|
bool FCDocument::LinkDriver(FCDAnimated* animated)
|
|
{
|
|
if (animated->GetTargetPointer().empty()) return false;
|
|
|
|
bool driven = false;
|
|
size_t animationCount = animationLibrary->GetEntityCount();
|
|
for (size_t i = 0; i < animationCount; ++i)
|
|
{
|
|
FCDAnimation* animation = animationLibrary->GetEntity(i);
|
|
driven |= animation->LinkDriver(animated);
|
|
}
|
|
return driven;
|
|
}
|
|
|
|
// Search for an animation channel targeting the given pointer
|
|
void FCDocument::FindAnimationChannels(const string& pointer, FCDAnimationChannelList& channels)
|
|
{
|
|
if (pointer.empty()) return;
|
|
|
|
size_t animationCount = (uint32) animationLibrary->GetEntityCount();
|
|
for (size_t i = 0; i < animationCount; ++i)
|
|
{
|
|
FCDAnimation* animation = animationLibrary->GetEntity(i);
|
|
animation->FindAnimationChannels(pointer, channels);
|
|
}
|
|
}
|
|
|
|
// Gather a list of the indices of animated array element belonging to the node
|
|
void FCDocument::FindAnimationChannelsArrayIndices(xmlNode* targetArray, Int32List& animatedIndices)
|
|
{
|
|
// Calculte the node's pointer
|
|
string pointer;
|
|
CalculateNodeTargetPointer(targetArray, pointer);
|
|
if (pointer.empty()) return;
|
|
|
|
// Retrieve the channels for this pointer and extract their matrix indices.
|
|
FCDAnimationChannelList channels;
|
|
FindAnimationChannels(pointer, channels);
|
|
for (vector<FCDAnimationChannel*>::iterator it = channels.begin(); it != channels.end(); ++it)
|
|
{
|
|
string qualifier = (*it)->GetTargetQualifier();
|
|
int32 animatedIndex = ReadTargetMatrixElement(qualifier);
|
|
if (animatedIndex != -1) animatedIndices.push_back(animatedIndex);
|
|
}
|
|
}
|
|
|
|
// Search for a specific COLLADA library items with a given COLLADA id.
|
|
FCDAnimation* FCDocument::FindAnimation(const string& daeId) { return animationLibrary->FindDaeId(daeId); }
|
|
FCDAnimationClip* FCDocument::FindAnimationClip(const string& daeId) { return animationClipLibrary->FindDaeId(daeId); }
|
|
FCDCamera* FCDocument::FindCamera(const string& daeId) { return cameraLibrary->FindDaeId(daeId); }
|
|
FCDController* FCDocument::FindController(const string& daeId) { return controllerLibrary->FindDaeId(daeId); }
|
|
FCDGeometry* FCDocument::FindGeometry(const string& daeId) { return geometryLibrary->FindDaeId(daeId); }
|
|
FCDImage* FCDocument::FindImage(const string& daeId) { return imageLibrary->FindDaeId(daeId); }
|
|
FCDLight* FCDocument::FindLight(const string& daeId) { return lightLibrary->FindDaeId(daeId); }
|
|
FCDTexture* FCDocument::FindTexture(const string& daeId) { return materialLibrary->FindTexture(daeId); }
|
|
FCDMaterial* FCDocument::FindMaterial(const string& daeId) { return materialLibrary->FindMaterial(daeId); }
|
|
FCDEffect* FCDocument::FindEffect(const string& daeId) { return materialLibrary->FindEffect(daeId); }
|
|
FCDSceneNode* FCDocument::FindVisualScene(const string& daeId) { return visualSceneLibrary->FindDaeId(daeId); }
|
|
FCDPhysicsSceneNode* FCDocument::FindPhysicsScene(const string& daeId) { return physicsSceneLibrary->FindDaeId(daeId); }
|
|
FCDPhysicsMaterial* FCDocument::FindPhysicsMaterial(const string& daeId) { return physicsMaterialLibrary->FindDaeId(daeId); }
|
|
FCDPhysicsModel* FCDocument::FindPhysicsModel(const string& daeId) { return physicsModelLibrary->FindDaeId(daeId); }
|
|
FCDSceneNode* FCDocument::FindSceneNode(const string& daeId)
|
|
{
|
|
// Nasty special case: look through all the visual_scenes for the scene node
|
|
size_t visualSceneCount = visualSceneLibrary->GetEntityCount();
|
|
for (size_t i = 0; i < visualSceneCount; ++i)
|
|
{
|
|
FCDSceneNode* visualScene = visualSceneLibrary->GetEntity(i);
|
|
FCDEntity* found = visualScene->FindDaeId(daeId);
|
|
if (found != NULL) return (FCDSceneNode*) found;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// Add an animated value to the list
|
|
void FCDocument::RegisterAnimatedValue(FCDAnimated* animated)
|
|
{
|
|
// Look for a duplicate in order to avoid memory loss
|
|
//if (animated->GetValueCount() == 0 || FindAnimatedValue(animated->GetValue(0)) != NULL)
|
|
if (animated->GetValueCount() == 0)
|
|
{
|
|
SAFE_DELETE(animated);
|
|
return;
|
|
}
|
|
|
|
// List the new animated value
|
|
animatedValues.push_back(animated);
|
|
|
|
// Also add to the map the individual values for easy retrieval
|
|
size_t count = animated->GetValueCount();
|
|
for (size_t i = 0; i < count; ++i)
|
|
{
|
|
const float* value = animated->GetValue(i);
|
|
animatedValueMap[value] = animated;
|
|
}
|
|
}
|
|
|
|
// Unregisters an animated value of the document.
|
|
void FCDocument::UnregisterAnimatedValue(FCDAnimated* animated)
|
|
{
|
|
if (animated != NULL)
|
|
{
|
|
FCDAnimatedList::iterator it = std::find(animatedValues.begin(), animatedValues.end(), animated);
|
|
if (it != animatedValues.end())
|
|
{
|
|
animatedValues.erase(it);
|
|
|
|
// Also remove to the map the individual values contained
|
|
size_t count = animated->GetValueCount();
|
|
for (size_t i = 0; i < count; ++i)
|
|
{
|
|
const float* value = animated->GetValue(i);
|
|
FCDAnimatedValueMap::iterator itV = animatedValueMap.find(value);
|
|
if (itV != animatedValueMap.end() && (*itV).second == animated)
|
|
{
|
|
animatedValueMap.erase(itV);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Retrieve an animated value, given a value pointer
|
|
FCDAnimated* FCDocument::FindAnimatedValue(float* ptr)
|
|
{
|
|
FCDAnimatedValueMap::iterator it = animatedValueMap.find((const float*) ptr);
|
|
return (it != animatedValueMap.end()) ? (*it).second : NULL;
|
|
}
|
|
|
|
// Retrieve an animated value, given a value pointer
|
|
const FCDAnimated* FCDocument::FindAnimatedValue(const float* ptr) const
|
|
{
|
|
FCDAnimatedValueMap::const_iterator it = animatedValueMap.find(ptr);
|
|
return (it != animatedValueMap.end()) ? (*it).second : NULL;
|
|
}
|
|
|
|
const FCDAnimated* FCDocument::FindNamedAnimated(const string& shader, const string& attribute) const
|
|
{
|
|
string::size_type loc = attribute.find('.', 0);
|
|
string attrname = attribute.substr(loc+1);
|
|
string shdname = attribute.substr(0, loc);
|
|
|
|
string name0 = shader;
|
|
name0.append(string("-fx/"));
|
|
name0.append(attrname);
|
|
|
|
string name1 = shader;
|
|
name1.append(string("-fx/"));
|
|
name1.append(shdname);
|
|
name1.append(attrname);
|
|
|
|
for(FCDAnimatedList::const_iterator itA = animatedValues.begin(); itA != animatedValues.end(); ++itA)
|
|
{
|
|
if(strcmp((*itA)->GetTargetPointer().c_str(), name0.c_str()) == 0 || strcmp((*itA)->GetTargetPointer().c_str(), name1.c_str()) == 0) return (*itA);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// Retrieve an animated float value for a given fully qualified target
|
|
const float* FCDocument::FindAnimatedTarget(const string& fullyQualifiedTarget)
|
|
{
|
|
if (fullyQualifiedTarget.empty()) return NULL;
|
|
string target = (fullyQualifiedTarget[0] == '#') ? fullyQualifiedTarget.substr(1) : fullyQualifiedTarget;
|
|
string pointer, qualifier;
|
|
SplitTarget(target, pointer, qualifier);
|
|
|
|
// Find the pointer
|
|
FCDAnimated* animatedValue = NULL;
|
|
for (FCDAnimatedList::iterator itA = animatedValues.begin(); itA != animatedValues.end(); ++itA)
|
|
{
|
|
FCDAnimated* animated = (*itA);
|
|
if (animated->GetTargetPointer() == pointer) { animatedValue = animated; break; }
|
|
}
|
|
if (animatedValue == NULL) return NULL;
|
|
|
|
// Return the qualified value
|
|
size_t index = animatedValue->FindQualifier(qualifier);
|
|
if (index == size_t(-1)) return NULL;
|
|
return animatedValue->GetValue(index);
|
|
}
|
|
|
|
// Returns whether a given value pointer is animated
|
|
bool FCDocument::IsValueAnimated(const float* ptr) const
|
|
{
|
|
const FCDAnimated* animated = FindAnimatedValue(ptr);
|
|
return (animated != NULL) ? animated->HasCurve() : false;
|
|
}
|
|
|
|
// Insert new library elements
|
|
FCDSceneNode* FCDocument::AddVisualScene()
|
|
{
|
|
return visualSceneRoot = visualSceneLibrary->AddEntity();
|
|
}
|
|
FCDPhysicsSceneNode* FCDocument::AddPhysicsScene()
|
|
{
|
|
return physicsSceneRoot = physicsSceneLibrary->AddEntity();
|
|
}
|
|
|
|
// Structure and enumeration used to order the libraries
|
|
enum nodeOrder { ANIMATION=0, ANIMATION_CLIP, IMAGE, TEXTURE, EFFECT, MATERIAL, GEOMETRY, CONTROLLER, CAMERA, LIGHT, VISUAL_SCENE, PHYSICS_MATERIAL, PHYSICS_MODEL, PHYSICS_SCENE, UNKNOWN };
|
|
struct xmlOrderedNode { xmlNode* node; nodeOrder order; };
|
|
typedef vector<xmlOrderedNode> xmlOrderedNodeList;
|
|
|
|
// Loads an entire COLLADA document file
|
|
FUStatus FCDocument::LoadFromFile(const fstring& filename)
|
|
{
|
|
FUStatus status;
|
|
|
|
// Push the filename's path unto the file manager's stack
|
|
fileManager->PushRootFile(filename);
|
|
|
|
#if FCOLLADA_EXCEPTION
|
|
try {
|
|
#endif
|
|
|
|
// Parse the document into a XML tree
|
|
string xmlFilename = FUStringConversion::ToString(filename);
|
|
xmlDoc* daeDocument = xmlParseFile(xmlFilename.c_str());
|
|
if (daeDocument != NULL)
|
|
{
|
|
xmlNode* rootNode = xmlDocGetRootElement(daeDocument);
|
|
|
|
// Read in the whole document from the root node
|
|
status.AppendStatus(LoadDocumentFromXML(rootNode));
|
|
|
|
// Free the XML document
|
|
xmlFreeDoc(daeDocument);
|
|
}
|
|
else
|
|
{
|
|
status.Fail(FS("Corrupted COLLADA document: malformed XML."));
|
|
}
|
|
|
|
// Clean-up the XML reader
|
|
xmlCleanupParser();
|
|
|
|
#if FCOLLADA_EXCEPTION
|
|
} catch(const char* sz) {
|
|
status.Fail(FS("Exception caught while parsing a COLLADA document from file: ") + TO_FSTRING(sz));
|
|
#ifdef UNICODE
|
|
} catch(const fchar* sz) {
|
|
status.Fail(FS("Exception caught while parsing a COLLADA document from file: ") + sz);
|
|
#endif
|
|
} catch(...) {
|
|
status.Fail(FS("Exception caught while parsing a COLLADA document from file."));
|
|
}
|
|
#endif
|
|
|
|
// Restore the orignal OS current folder
|
|
fileManager->PopRootFile();
|
|
|
|
if (status.IsSuccessful()) status.AppendString(FC("COLLADA document loaded successfully."));
|
|
return status;
|
|
}
|
|
|
|
// Loads an entire COLLADA document from a given NULL-terminated fstring
|
|
FUStatus FCDocument::LoadFromText(const fstring& basePath, const fchar* text)
|
|
{
|
|
FUStatus status;
|
|
|
|
// Push the given path unto the file manager's stack
|
|
fileManager->PushRootPath(basePath);
|
|
|
|
#if FCOLLADA_EXCEPTION
|
|
try {
|
|
#endif
|
|
|
|
#ifdef UNICODE
|
|
// Downsize the text document into something 8-bit
|
|
string xmlTextString = FUStringConversion::ToString(text);
|
|
const xmlChar* xmlText = (const xmlChar*) xmlTextString.c_str();
|
|
#else
|
|
const xmlChar* xmlText = (const xmlChar*) text;
|
|
#endif
|
|
|
|
// Parse the document into a XML tree
|
|
xmlDoc* daeDocument = xmlParseDoc(const_cast<xmlChar*>(xmlText));
|
|
if (daeDocument != NULL)
|
|
{
|
|
xmlNode* rootNode = xmlDocGetRootElement(daeDocument);
|
|
|
|
// Read in the whole document from the root node
|
|
status.AppendStatus(LoadDocumentFromXML(rootNode));
|
|
|
|
// Free the XML document
|
|
xmlFreeDoc(daeDocument);
|
|
}
|
|
else
|
|
{
|
|
status.Fail(FS("Corrupted COLLADA document: malformed XML."));
|
|
}
|
|
|
|
// Clean-up the XML reader
|
|
xmlCleanupParser();
|
|
|
|
#if FCOLLADA_EXCEPTION
|
|
} catch(const char* sz) {
|
|
status.Fail(FS("Exception caught while parsing a COLLADA document from a string: ") + TO_FSTRING(sz));
|
|
#ifdef UNICODE
|
|
} catch(const fchar* sz) {
|
|
status.Fail(FS("Exception caught while parsing a COLLADA document from a string: ") + sz);
|
|
#endif
|
|
} catch(...) {
|
|
status.Fail(FC("Exception caught while parsing a COLLADA document from a string."));
|
|
}
|
|
#endif
|
|
|
|
// Restore the orignal OS current folder
|
|
fileManager->PopRootPath();
|
|
|
|
if (status.IsSuccessful()) status.AppendString(FC("COLLADA document loaded successfully."));
|
|
return status;
|
|
}
|
|
|
|
FUStatus FCDocument::LoadDocumentFromXML(xmlNode* colladaNode)
|
|
{
|
|
FUStatus status;
|
|
|
|
// The only root node supported is "COLLADA"
|
|
if (!IsEquivalent(colladaNode->name, DAE_COLLADA_ELEMENT))
|
|
{
|
|
return status.Fail(FS("Valid document contain only the <COLLADA> root element."), colladaNode->line);
|
|
}
|
|
|
|
// Bucket the libraries, so that we can read them in our specific order
|
|
// COLLADA 1.4: the libraries are now strongly-typed, so process all the elements
|
|
xmlNode* sceneNode = NULL;
|
|
xmlOrderedNodeList orderedLibraryNodes;
|
|
for (xmlNode* child = colladaNode->children; child != NULL; child = child->next)
|
|
{
|
|
if (child->type != XML_ELEMENT_NODE) continue;
|
|
|
|
xmlOrderedNode n;
|
|
n.node = child;
|
|
n.order = UNKNOWN;
|
|
if (IsEquivalent(child->name, DAE_LIBRARY_ELEMENT))
|
|
{
|
|
// COLLADA 1.3: Read in the type attribute of the library to know its content
|
|
string libraryType = ReadNodeProperty(n.node, DAE_TYPE_ATTRIBUTE);
|
|
if (libraryType == DAE_ANIMATION_TYPE) n.order = ANIMATION;
|
|
else if (libraryType == DAE_EFFECT_TYPE) n.order = EFFECT;
|
|
else if (libraryType == DAE_IMAGE_TYPE) n.order = IMAGE;
|
|
else if (libraryType == DAE_TEXTURE_TYPE) n.order = TEXTURE;
|
|
else if (libraryType == DAE_MATERIAL_TYPE) n.order = MATERIAL;
|
|
else if (libraryType == DAE_GEOMETRY_TYPE) n.order = GEOMETRY;
|
|
else if (libraryType == DAE_CAMERA_TYPE) n.order = CAMERA;
|
|
else if (libraryType == DAE_CONTROLLER_TYPE) n.order = CONTROLLER;
|
|
else if (libraryType == DAE_LIGHT_TYPE) n.order = LIGHT;
|
|
}
|
|
else if (IsEquivalent(child->name, DAE_LIBRARY_ANIMATION_ELEMENT)) n.order = ANIMATION;
|
|
else if (IsEquivalent(child->name, DAE_LIBRARY_ANIMATION_CLIP_ELEMENT)) n.order = ANIMATION_CLIP;
|
|
else if (IsEquivalent(child->name, DAE_LIBRARY_CAMERA_ELEMENT)) n.order = CAMERA;
|
|
else if (IsEquivalent(child->name, DAE_LIBRARY_CONTROLLER_ELEMENT)) n.order = CONTROLLER;
|
|
else if (IsEquivalent(child->name, DAE_LIBRARY_EFFECT_ELEMENT)) n.order = EFFECT;
|
|
else if (IsEquivalent(child->name, DAE_LIBRARY_GEOMETRY_ELEMENT)) n.order = GEOMETRY;
|
|
else if (IsEquivalent(child->name, DAE_LIBRARY_IMAGE_ELEMENT)) n.order = IMAGE;
|
|
else if (IsEquivalent(child->name, DAE_LIBRARY_LIGHT_ELEMENT)) n.order = LIGHT;
|
|
else if (IsEquivalent(child->name, DAE_LIBRARY_MATERIAL_ELEMENT)) n.order = MATERIAL;
|
|
else if (IsEquivalent(child->name, DAE_LIBRARY_VSCENE_ELEMENT)) n.order = VISUAL_SCENE;
|
|
else if (IsEquivalent(child->name, DAE_LIBRARY_FFIELDS_ELEMENT)) continue; // Valid, but not processed
|
|
else if (IsEquivalent(child->name, DAE_LIBRARY_NODE_ELEMENT)) continue; // Valid, but not processed
|
|
else if (IsEquivalent(child->name, DAE_LIBRARY_PMATERIAL_ELEMENT)) n.order = PHYSICS_MATERIAL;
|
|
else if (IsEquivalent(child->name, DAE_LIBRARY_PMODEL_ELEMENT)) n.order = PHYSICS_MODEL;
|
|
else if (IsEquivalent(child->name, DAE_LIBRARY_PSCENE_ELEMENT)) n.order = PHYSICS_SCENE;
|
|
else if (IsEquivalent(child->name, DAE_ASSET_ELEMENT))
|
|
{
|
|
// Read in the asset information
|
|
status.AppendStatus(asset->LoadFromXML(child));
|
|
|
|
// Calculate the length conversion unit
|
|
// If the wanted unit length is negative, it implies that no conversion is wanted or that the flag was not set
|
|
if (lengthUnitWanted > 0.0f) lengthUnitConversion = asset->GetUnitConversionFactor() / lengthUnitWanted;
|
|
continue;
|
|
}
|
|
else if (IsEquivalent(child->name, DAE_SCENE_ELEMENT))
|
|
{
|
|
// The <scene> element should be the last element of the document
|
|
sceneNode = child;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
status.Warning(FS("Unknown base node type: ") + TO_FSTRING((const char*) child->name), child->line);
|
|
continue;
|
|
}
|
|
|
|
xmlOrderedNodeList::iterator it;
|
|
for (it = orderedLibraryNodes.begin(); it != orderedLibraryNodes.end(); ++it)
|
|
{
|
|
if ((uint32) n.order < (uint32) (*it).order) break;
|
|
}
|
|
orderedLibraryNodes.insert(it, n);
|
|
}
|
|
|
|
// Process the ordered libraries
|
|
size_t libraryNodeCount = orderedLibraryNodes.size();
|
|
for (size_t i = 0; i < libraryNodeCount; ++i)
|
|
{
|
|
xmlOrderedNode& n = orderedLibraryNodes[i];
|
|
switch (n.order)
|
|
{
|
|
case ANIMATION: status.AppendStatus(animationLibrary->LoadFromXML(n.node)); break;
|
|
case ANIMATION_CLIP: status.AppendStatus(animationClipLibrary->LoadFromXML(n.node)); break;
|
|
case CAMERA: status.AppendStatus(cameraLibrary->LoadFromXML(n.node)); break;
|
|
case CONTROLLER: status.AppendStatus(controllerLibrary->LoadFromXML(n.node)); break;
|
|
case GEOMETRY: status.AppendStatus(geometryLibrary->LoadFromXML(n.node)); break;
|
|
case EFFECT: status.AppendStatus(materialLibrary->LoadFromXML(n.node)); break;
|
|
case IMAGE: status.AppendStatus(imageLibrary->LoadFromXML(n.node)); break;
|
|
case LIGHT: status.AppendStatus(lightLibrary->LoadFromXML(n.node)); break;
|
|
case MATERIAL: status.AppendStatus(materialLibrary->LoadFromXML(n.node)); break;
|
|
case TEXTURE: status.AppendStatus(materialLibrary->LoadFromXML(n.node)); break;
|
|
case PHYSICS_MODEL: status.AppendStatus(physicsModelLibrary->LoadFromXML(n.node)); break;
|
|
case PHYSICS_MATERIAL: status.AppendStatus(physicsMaterialLibrary->LoadFromXML(n.node)); break;
|
|
case PHYSICS_SCENE: status.AppendStatus(physicsSceneLibrary->LoadFromXML(n.node)); break;
|
|
case VISUAL_SCENE: status.AppendStatus(visualSceneLibrary->LoadFromXML(n.node)); break;
|
|
case UNKNOWN: default: break;
|
|
}
|
|
}
|
|
|
|
// Read in the <scene> element
|
|
if (sceneNode == NULL)
|
|
{
|
|
return status.Warning(FS("No base <scene> element found."), colladaNode->line);
|
|
}
|
|
|
|
// COLLADA 1.4: Look for a <instance_physics_scene> element
|
|
xmlNode* instancePhysicsNode = FindChildByType(sceneNode, DAE_INSTANCE_PHYSICS_SCENE_ELEMENT);
|
|
if (instancePhysicsNode != NULL)
|
|
{
|
|
FUUri instanceUri = ReadNodeUrl(instancePhysicsNode);
|
|
if (instanceUri.prefix.length() > 0)
|
|
{
|
|
status.Fail(FS("Cannot externally reference a <physics_scene> element."), sceneNode->line);
|
|
}
|
|
else if (instanceUri.suffix.length() == 0)
|
|
{
|
|
status.Fail(FS("No valid URI fragment for the instantiation of the physics scene."), sceneNode->line);
|
|
}
|
|
else
|
|
{
|
|
// Look for the correct physics scene to instantiate in the libraries
|
|
physicsSceneRoot = FindPhysicsScene(instanceUri.suffix);
|
|
if (physicsSceneRoot == NULL)
|
|
{
|
|
status.Fail(FS("Cannot find the correct <physics_scene> element to instantiate."), sceneNode->line);
|
|
}
|
|
}
|
|
}
|
|
|
|
// COLLADA 1.4: Look for a <instance_visual_scene> element
|
|
xmlNode* instanceSceneNode = FindChildByType(sceneNode, DAE_INSTANCE_VSCENE_ELEMENT);
|
|
if (instanceSceneNode != NULL)
|
|
{
|
|
FUUri instanceUri = ReadNodeUrl(instanceSceneNode);
|
|
if (instanceUri.prefix.length() > 0)
|
|
{
|
|
status.Fail(FS("Cannot externally reference a <visual_scene> element."), sceneNode->line);
|
|
}
|
|
else if (instanceUri.suffix.length() == 0)
|
|
{
|
|
status.Fail(FS("No valid URI fragment for the instantiation of the visual scene."), sceneNode->line);
|
|
}
|
|
else
|
|
{
|
|
// Look for the correct visual scene to instantiate in the libraries
|
|
visualSceneRoot = FindVisualScene(instanceUri.suffix);
|
|
if (visualSceneRoot == NULL)
|
|
{
|
|
status.Fail(FS("Cannot find the correct <visual_scene> element to instantiate."), sceneNode->line);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// COLLADA 1.3 backward-compatibility, use this <scene> as the <visual_scene> element
|
|
visualSceneRoot = visualSceneLibrary->AddEntity();
|
|
status.AppendStatus(visualSceneRoot->LoadFromXML(sceneNode));
|
|
}
|
|
|
|
|
|
if (visualSceneRoot != NULL)
|
|
{
|
|
// Link the controllers and the joints
|
|
size_t controllerCount = controllerLibrary->GetEntityCount();
|
|
for (size_t i = 0; i < controllerCount; ++i)
|
|
{
|
|
FCDController* controller = controllerLibrary->GetEntity(i);
|
|
status.AppendStatus(controller->Link());
|
|
}
|
|
|
|
// Link the targeted entities, for 3dsMax cameras and lights
|
|
size_t cameraCount = cameraLibrary->GetEntityCount();
|
|
for (size_t i = 0; i < cameraCount; ++i)
|
|
{
|
|
FCDCamera* camera = cameraLibrary->GetEntity(i);
|
|
status.AppendStatus(camera->LinkTarget(visualSceneRoot));
|
|
}
|
|
size_t lightCount = lightLibrary->GetEntityCount();
|
|
for (size_t i = 0; i < lightCount; ++i)
|
|
{
|
|
FCDLight* light = lightLibrary->GetEntity(i);
|
|
status.AppendStatus(light->LinkTarget(visualSceneRoot));
|
|
}
|
|
}
|
|
|
|
// Check that all the animation curves that need them, have found drivers
|
|
size_t animationCount = animationLibrary->GetEntityCount();
|
|
for (size_t i = 0; i < animationCount; ++i)
|
|
{
|
|
FCDAnimation* animation = animationLibrary->GetEntity(i);
|
|
status.AppendStatus(animation->Link());
|
|
}
|
|
|
|
// store the post process commands
|
|
postCmds = visualSceneLibrary->GetPostProcessCmds();
|
|
|
|
return status;
|
|
}
|
|
|
|
// Writes out the COLLADA document to a file
|
|
FUStatus FCDocument::WriteToFile(const fstring& filename) const
|
|
{
|
|
FUStatus status;
|
|
|
|
// Push the filename's path unto the file manager's stack
|
|
fileManager->PushRootFile(filename);
|
|
|
|
#if FCOLLADA_EXCEPTION
|
|
try {
|
|
#endif
|
|
// Create a new xml root node from this COLLADA document
|
|
xmlNode* rootNode = CreateNode(DAE_COLLADA_ELEMENT);
|
|
status = WriteDocumentToXML(rootNode);
|
|
if (status.IsSuccessful())
|
|
{
|
|
// Create the XML document and write it out to the given filename
|
|
xmlDoc* daeDocument = xmlNewDoc(NULL); // NULL implies version 1.0
|
|
xmlDocSetRootElement(daeDocument, rootNode);
|
|
intptr_t bytesWritten = xmlSaveFormatFileEnc(FUStringConversion::ToString(filename).c_str(), daeDocument, "utf-8", 1);
|
|
if (bytesWritten < 0)
|
|
{
|
|
status.Fail(FS("Unable to write COLLADA document to file '") + filename + FS("'. Verify that the folder exists and the file is writable."), rootNode->line);
|
|
}
|
|
else if (status.IsSuccessful())
|
|
{
|
|
status.AppendString(FC("COLLADA document written successfully."));
|
|
}
|
|
xmlFreeDoc(daeDocument);
|
|
}
|
|
else
|
|
{
|
|
xmlFreeNode(rootNode);
|
|
}
|
|
|
|
// Clean-up
|
|
xmlCleanupParser();
|
|
|
|
#if FCOLLADA_EXCEPTION
|
|
} catch(const char* sz) {
|
|
status.Fail(FS("Exception caught while parsing a COLLADA document from a string: ") + TO_FSTRING(sz));
|
|
#ifdef UNICODE
|
|
} catch(const fchar* sz) {
|
|
status.Fail(FS("Exception caught while parsing a COLLADA document from a string: ") + sz);
|
|
#endif
|
|
} catch(...) {
|
|
status.Fail(FC("Exception caught while parsing a COLLADA document from a string."));
|
|
}
|
|
#endif
|
|
|
|
fileManager->PopRootFile();
|
|
return status;
|
|
}
|
|
|
|
// Writes out the entire COLLADA document to the given XML root node.
|
|
FUStatus FCDocument::WriteDocumentToXML(xmlNode* colladaNode) const
|
|
{
|
|
FUStatus status;
|
|
if (colladaNode != NULL)
|
|
{
|
|
// Write the COLLADA document version and namespace: schema-required attributes
|
|
AddAttribute(colladaNode, DAE_NAMESPACE_ATTRIBUTE, DAE_SCHEMA_LOCATION);
|
|
AddAttribute(colladaNode, DAE_VERSION_ATTRIBUTE, DAE_SCHEMA_VERSION);
|
|
|
|
// Write out the asset tag
|
|
asset->WriteToXML(colladaNode);
|
|
|
|
// Record the animation library. This library is built at the end, but should appear before the <scene> element.
|
|
xmlNode* animationLibraryNode = NULL;
|
|
if (!animationLibrary->IsEmpty())
|
|
{
|
|
animationLibraryNode = AddChild(colladaNode, DAE_LIBRARY_ANIMATION_ELEMENT);
|
|
}
|
|
|
|
// Export the libraries
|
|
#define EXPORT_LIBRARY(memberName, daeElementName) if (!(memberName)->IsEmpty()) { \
|
|
xmlNode* libraryNode = AddChild(colladaNode, daeElementName); \
|
|
memberName->WriteToXML(libraryNode); }
|
|
|
|
EXPORT_LIBRARY(animationClipLibrary, DAE_LIBRARY_ANIMATION_CLIP_ELEMENT);
|
|
EXPORT_LIBRARY(cameraLibrary, DAE_LIBRARY_CAMERA_ELEMENT);
|
|
EXPORT_LIBRARY(lightLibrary, DAE_LIBRARY_LIGHT_ELEMENT);
|
|
EXPORT_LIBRARY(imageLibrary, DAE_LIBRARY_IMAGE_ELEMENT);
|
|
EXPORT_LIBRARY(materialLibrary, DAE_LIBRARY_MATERIAL_ELEMENT);
|
|
EXPORT_LIBRARY(geometryLibrary, DAE_LIBRARY_GEOMETRY_ELEMENT);
|
|
EXPORT_LIBRARY(controllerLibrary, DAE_LIBRARY_CONTROLLER_ELEMENT);
|
|
EXPORT_LIBRARY(visualSceneLibrary, DAE_LIBRARY_VSCENE_ELEMENT);
|
|
|
|
#undef EXPORT_LIBRARY
|
|
|
|
// Create the <scene> element and instantiate the selected visual scene.
|
|
xmlNode* sceneNode = AddChild(colladaNode, DAE_SCENE_ELEMENT);
|
|
if (visualSceneRoot != NULL)
|
|
{
|
|
xmlNode* instanceVisualSceneNode = AddChild(sceneNode, DAE_INSTANCE_VSCENE_ELEMENT);
|
|
AddAttribute(instanceVisualSceneNode, DAE_URL_ATTRIBUTE, string("#") + visualSceneRoot->GetDaeId());
|
|
}
|
|
if (physicsSceneRoot != NULL)
|
|
{
|
|
xmlNode* instancePhysicsSceneNode = AddChild(sceneNode, DAE_INSTANCE_PHYSICS_SCENE_ELEMENT);
|
|
AddAttribute(instancePhysicsSceneNode, DAE_URL_ATTRIBUTE, string("#") + physicsSceneRoot->GetDaeId());
|
|
}
|
|
|
|
// Write out the animations
|
|
if (animationLibraryNode != NULL)
|
|
{
|
|
animationLibrary->WriteToXML(animationLibraryNode);
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
// Writes out a value's animations, if any, to the animation library of a COLLADA xml document.
|
|
void FCDocument::WriteAnimatedValueToXML(const float* value, xmlNode* valueNode, const char* wantedSid, int32 arrayElement) const
|
|
{
|
|
// Find the value's animations
|
|
FCDAnimated* animated = const_cast<FCDAnimated*>(FindAnimatedValue(value));
|
|
if (animated != NULL && animated->HasCurve() && valueNode != NULL)
|
|
{
|
|
animated->SetArrayElement(arrayElement);
|
|
|
|
// Set a sid unto the xml tree node, in order to support animations
|
|
if (!HasNodeProperty(valueNode, DAE_SID_ATTRIBUTE))
|
|
{
|
|
AddNodeSid(valueNode, wantedSid);
|
|
}
|
|
|
|
// Calculate the xml tree node's target for the animation channel and write the animation out
|
|
string target;
|
|
CalculateNodeTargetPointer(valueNode, target);
|
|
if (!target.empty())
|
|
{
|
|
for (uint32 i = 0; i < animated->GetValueCount(); ++i)
|
|
{
|
|
FCDAnimationCurve* curve = animated->GetCurve(i);
|
|
if (curve == NULL) continue;
|
|
curve->SetTargetElement(arrayElement);
|
|
|
|
FCDAnimationChannel* channel = curve->GetParent();
|
|
FUAssert(channel != NULL, continue);
|
|
|
|
channel->SetTargetPointer(target);
|
|
}
|
|
}
|
|
}
|
|
}
|