/* Copyright (C) 2005-2007 Feeling Software Inc. Portions of the code are: Copyright (C) 2005-2007 Sony Computer Entertainment America MIT License: http://www.opensource.org/licenses/mit-license.php */ #include "StdAfx.h" #include "FArchiveXML.h" #include "FCDocument/FCDocument.h" #include "FCDocument/FCDAsset.h" #include "FCDocument/FCDExtra.h" #include "FCDocument/FCDAnimated.h" #include "FCDocument/FCDEntityInstance.h" #include "FCDocument/FCDEntity.h" #include "FCDocument/FCDTargetedEntity.h" #include "FCDocument/FCDSceneNode.h" #include "FCDocument/FCDTransform.h" #include "FCDocument/FCDControllerInstance.h" #include "FCDocument/FCDGeometryInstance.h" #include "FCDocument/FCDEmitterInstance.h" #include "FCDocument/FCDEntityInstance.h" xmlNode* FArchiveXML::WriteEntity(FCDObject* object, xmlNode* parentNode) { FCDEntity* entity = (FCDEntity*)object; return FArchiveXML::WriteToEntityXMLFCDEntity(entity, parentNode, DAEERR_UNKNOWN_ELEMENT); } xmlNode* FArchiveXML::WriteTargetedEntity(FCDObject* UNUSED(object), xmlNode* UNUSED(parentNode)) { // Currently not reachable FUBreak; return NULL; } xmlNode* FArchiveXML::WriteSceneNode(FCDObject* object, xmlNode* parentNode) { FCDSceneNode* sceneNode = (FCDSceneNode*)object; xmlNode* node = NULL; bool isVisualScene = false; FCDENodeList extraParameters; FCDETechnique* extraTechnique = NULL; if (sceneNode->GetParentCount() == 0) { node = FArchiveXML::WriteToEntityXMLFCDEntity(sceneNode, parentNode, DAE_VSCENE_ELEMENT); isVisualScene = true; } else { node = FArchiveXML::WriteToEntityXMLFCDEntity(sceneNode, parentNode, DAE_NODE_ELEMENT); if (sceneNode->GetSubId().length() > 0) AddAttribute(node, DAE_SID_ATTRIBUTE, sceneNode->GetSubId()); // Set the scene node's type. const char* nodeType = sceneNode->GetJointFlag() ? DAE_JOINT_NODE_TYPE : DAE_NODE_NODE_TYPE; AddAttribute(node, DAE_TYPE_ATTRIBUTE, nodeType); // Write out the visibility of this node, if it is not visible or if it is animated. if (sceneNode->GetVisibility().IsAnimated() || !sceneNode->IsVisible()) { extraTechnique = const_cast(sceneNode->GetExtra())->GetDefaultType()->AddTechnique(DAE_FCOLLADA_PROFILE); FCDENode* visibilityNode = extraTechnique->AddParameter(DAEFC_VISIBILITY_PARAMETER, sceneNode->GetVisibility() >= 0.5f); visibilityNode->GetAnimated()->Copy(sceneNode->GetVisibility().GetAnimated()); extraParameters.push_back(visibilityNode); } } // Write out the transforms size_t transformCount = sceneNode->GetTransformCount(); for (size_t t = 0; t < transformCount; ++t) { FCDTransform* transform = sceneNode->GetTransform(t); FArchiveXML::LetWriteObject(transform, node); } // Write out the instances // Some of the FCollada instance types are not a part of COLLADA, so buffer them to export in the . FCDENodeList extraInstanceNodes; FCDETechnique* extraInstanceTechnique = NULL; size_t instanceCount = sceneNode->GetInstanceCount(); for (size_t i = 0; i < instanceCount; ++i) { FCDEntityInstance* instance = sceneNode->GetInstance(i); if (instance->GetEntityType() == FCDEntity::FORCE_FIELD || instance->GetEntityType() == FCDEntity::EMITTER) { if (extraInstanceTechnique == NULL) { FCDExtra* extra = const_cast(sceneNode->GetExtra()); FCDEType* extraType = extra->AddType(DAEFC_INSTANCES_TYPE); extraInstanceTechnique = extraType->AddTechnique(DAE_FCOLLADA_PROFILE); } xmlNode* base = FUXmlWriter::CreateNode("_temp_"); xmlNode* instanceNode = FArchiveXML::LetWriteObject(instance, base); FCDENode* instanceAsExtra = extraInstanceTechnique->AddChildNode(); bool loadSuccess = FArchiveXML::LoadExtraNode(instanceAsExtra, instanceNode); xmlFreeNodeList(base); if (loadSuccess) { extraInstanceNodes.push_back(instanceAsExtra); continue; } // If we fail to create the extra tree, by default we fallback to writing to XML } FArchiveXML::LetWriteObject(instance, node); } // First, write out the child nodes that we consider instances: there is more than one // parent node and we aren't the first one. size_t childCount = sceneNode->GetChildrenCount(); for (size_t c = 0; c < childCount; ++c) { FCDSceneNode* child = sceneNode->GetChild(c); if (child->GetParent() != sceneNode) { bool alreadyInstantiated = false; for (size_t i = 0; i < instanceCount; ++i) { FCDEntityInstance* instance = sceneNode->GetInstance(i); if (!instance->IsExternalReference()) { alreadyInstantiated |= instance->GetEntity() == child; } } if (!alreadyInstantiated) { FCDEntityInstance* instance = FCDEntityInstanceFactory::CreateInstance(const_cast(sceneNode->GetDocument()), NULL, FCDEntity::SCENE_NODE); instance->SetEntity(const_cast(child)); FArchiveXML::LetWriteObject(instance, node); } } } // Then, hierarchically write out the child nodes. for (size_t c = 0; c < childCount; ++c) { FCDSceneNode* child = sceneNode->GetChild(c); if (child->GetParent() == sceneNode) { FArchiveXML::LetWriteObject(child, node); } } // This only writes extra-related stuff, so execute last. if (isVisualScene) FArchiveXML::WriteVisualScene(sceneNode, node); // Write out the extra information and release the temporarily added extra parameters FArchiveXML::WriteEntityExtra(sceneNode, node); if (extraTechnique != NULL) { CLEAR_POINTER_VECTOR(extraParameters); if (extraTechnique->GetChildNodeCount() == 0) SAFE_RELEASE(extraTechnique); } CLEAR_POINTER_VECTOR(extraInstanceNodes); return parentNode; } xmlNode* FArchiveXML::WriteTransform(FCDObject* UNUSED(object), xmlNode* UNUSED(parentNode)) { // // Currently not reachable // FUBreak; return NULL; } xmlNode* FArchiveXML::WriteTransformLookAt(FCDObject* object, xmlNode* parentNode) { FCDTLookAt* tLookAt = (FCDTLookAt*)object; FUSStringBuilder builder; FUStringConversion::ToString(builder, tLookAt->GetPosition()); builder.append(' '); FUStringConversion::ToString(builder, tLookAt->GetTarget()); builder.append(' '); FUStringConversion::ToString(builder, tLookAt->GetUp()); xmlNode* transformNode = FUDaeWriter::AddChild(parentNode, DAE_LOOKAT_ELEMENT, builder); FArchiveXML::WriteTransformBase(tLookAt, transformNode, "transform"); return transformNode; } xmlNode* FArchiveXML::WriteTransformMatrix(FCDObject* object, xmlNode* parentNode) { FCDTMatrix* tMatrix = (FCDTMatrix*)object; fm::string content = FUStringConversion::ToString((FMMatrix44&) tMatrix->GetTransform()); xmlNode* transformNode = FUDaeWriter::AddChild(parentNode, DAE_MATRIX_ELEMENT, content); FArchiveXML::WriteTransformBase(tMatrix, transformNode, "transform"); return transformNode; } xmlNode* FArchiveXML::WriteTransformRotation(FCDObject* object, xmlNode* parent) { FCDTRotation* tRotation = (FCDTRotation*)object; FUSStringBuilder builder; FUStringConversion::ToString(builder, tRotation->GetAxis()); builder += ' '; builder += tRotation->GetAngle(); xmlNode* transformNode = FUDaeWriter::AddChild(parent, DAE_ROTATE_ELEMENT); FUXmlWriter::AddContentUnprocessed(transformNode, builder); FArchiveXML::WriteTransformBase(tRotation, transformNode, "rotation"); return transformNode; } xmlNode* FArchiveXML::WriteTransformScale(FCDObject* object, xmlNode* parentNode) { FCDTScale* tScale = (FCDTScale*)object; fm::string content = FUStringConversion::ToString(tScale->GetScale()); xmlNode* transformNode = FUDaeWriter::AddChild(parentNode, DAE_SCALE_ELEMENT); FUXmlWriter::AddContentUnprocessed(transformNode, content); FArchiveXML::WriteTransformBase(tScale, transformNode, "scale"); return transformNode; } xmlNode* FArchiveXML::WriteTransformSkew(FCDObject* object, xmlNode* parentNode) { FCDTSkew* tSkew = (FCDTSkew*)object; FUSStringBuilder builder; builder.set(tSkew->GetAngle()); builder += ' '; FUStringConversion::ToString(builder, tSkew->GetRotateAxis()); builder += ' '; FUStringConversion::ToString(builder, tSkew->GetAroundAxis()); xmlNode* transformNode = FUDaeWriter::AddChild(parentNode, DAE_SKEW_ELEMENT); FUXmlWriter::AddContentUnprocessed(transformNode, builder); FArchiveXML::WriteTransformBase(tSkew, transformNode, "skew"); return transformNode; } xmlNode* FArchiveXML::WriteTransformTranslation(FCDObject* object, xmlNode* parentNode) { FCDTTranslation* tTranslation = (FCDTTranslation*)object; fm::string content = FUStringConversion::ToString(tTranslation->GetTranslation()); xmlNode* transformNode = FUDaeWriter::AddChild(parentNode, DAE_TRANSLATE_ELEMENT); FUXmlWriter::AddContentUnprocessed(transformNode, content); FArchiveXML::WriteTransformBase(tTranslation, transformNode, "translation"); return transformNode; } xmlNode* FArchiveXML::WriteToEntityXMLFCDEntity(FCDEntity* entity, xmlNode* parentNode, const char* nodeName, bool writeId) { // Create the entity node and write out the id and name attributes xmlNode* entityNode = AddChild(parentNode, nodeName); if (writeId) { AddAttribute(entityNode, DAE_ID_ATTRIBUTE, entity->GetDaeId()); } if (!entity->GetName().empty()) { AddAttribute(entityNode, DAE_NAME_ATTRIBUTE, entity->GetName()); } // Write out the asset information. if (const_cast(entity)->GetAsset() != NULL) FArchiveXML::LetWriteObject(entity->GetAsset(), entityNode); return entityNode; } void FArchiveXML::WriteEntityExtra(FCDEntity* entity, xmlNode* entityNode) { if (entity->GetExtra() != NULL) { FCDENodeList extraParameters; FCDETechnique* extraTechnique = NULL; // Add the note to the extra information if (entity->HasNote()) { extraTechnique = const_cast(*entity->GetExtra()).GetDefaultType()->AddTechnique(DAE_FCOLLADA_PROFILE); FCDENode* noteNode = extraTechnique->AddParameter(DAEMAX_USERPROPERTIES_NODE_PARAMETER, entity->GetNote()); extraParameters.push_back(noteNode); } // Write out all the typed and untyped extra information and release the temporarily-added extra parameters. FArchiveXML::LetWriteObject(entity->GetExtra(), entityNode); if (extraTechnique != NULL) { CLEAR_POINTER_VECTOR(extraParameters); if (extraTechnique->GetChildNodeCount() == 0) SAFE_RELEASE(extraTechnique); } } } void FArchiveXML::WriteEntityInstanceExtra(FCDEntityInstance* entityInstance, xmlNode* instanceNode) { if (entityInstance->GetExtra() != NULL) { FArchiveXML::LetWriteObject(entityInstance->GetExtra(), instanceNode); } } void FArchiveXML::WriteTargetedEntityExtra(FCDTargetedEntity* targetedEntity, xmlNode* entityNode) { FCDETechnique* technique = NULL; FCDENode* parameter = NULL; if (targetedEntity->GetTargetNode() != NULL) { // Just for the export-time, add to the extra tree, the target information. FCDExtra* extra = const_cast(targetedEntity->GetExtra()); technique = extra->GetDefaultType()->AddTechnique(DAE_FCOLLADA_PROFILE); parameter = technique->AddParameter(DAEFC_TARGET_PARAMETER, FS("#") + TO_FSTRING(targetedEntity->GetTargetNode()->GetDaeId())); } // Export the extra tree to XML FArchiveXML::WriteEntityExtra(targetedEntity, entityNode); if (targetedEntity->GetTargetNode() != NULL) { // Delete the created extra tree nodes. SAFE_RELEASE(parameter); if (technique->GetChildNodeCount() == 0) SAFE_RELEASE(technique); } } void FArchiveXML::WriteVisualScene(FCDSceneNode* sceneNode, xmlNode* parentNode) { // Only one of the visual scenes should write this. if (sceneNode->GetDocument()->GetVisualSceneInstance() == sceneNode) { // For the main visual scene: export the layer information const FCDLayerList& layers = sceneNode->GetDocument()->GetLayers(); if (!layers.empty()) { xmlNode* techniqueNode = AddExtraTechniqueChild(parentNode, DAEMAYA_MAYA_PROFILE); for (FCDLayerList::const_iterator itL = layers.begin(); itL != layers.end(); ++itL) { xmlNode* layerNode = AddChild(techniqueNode, DAEMAYA_LAYER_PARAMETER); if (!(*itL)->name.empty()) AddAttribute(layerNode, DAE_NAME_ATTRIBUTE, (*itL)->name); FUSStringBuilder layerObjects; for (StringList::const_iterator itO = (*itL)->objects.begin(); itO != (*itL)->objects.end(); ++itO) { layerObjects.append(*itO); layerObjects.append(' '); } layerObjects.pop_back(); AddContent(layerNode, layerObjects); } } // Export the start/end time. if (sceneNode->GetDocument()->HasStartTime() || sceneNode->GetDocument()->HasEndTime()) { xmlNode* techniqueNode = AddExtraTechniqueChild(parentNode, DAE_FCOLLADA_PROFILE); if (sceneNode->GetDocument()->HasStartTime()) AddChild(techniqueNode, DAEMAYA_STARTTIME_PARAMETER, sceneNode->GetDocument()->GetStartTime()); if (sceneNode->GetDocument()->HasEndTime()) AddChild(techniqueNode, DAEMAYA_ENDTIME_PARAMETER, sceneNode->GetDocument()->GetEndTime()); } } } void FArchiveXML::WriteTransformBase(FCDTransform* transform, xmlNode* transformNode, const char* wantedSid) { // Add the sub-id to the node. if (!transform->GetSubId()->empty()) { fm::string& _sid = const_cast(transform->GetSubId()); FUDaeWriter::AddNodeSid(transformNode, _sid); wantedSid = _sid.c_str(); } // Process the animation of the transform. if (transform->IsAnimated()) { FArchiveXML::WriteAnimatedValue(transform->GetAnimated(), transformNode, wantedSid); } }