/* 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/FCDLibrary.h" #include "FCDocument/FCDAnimated.h" #include "FCDocument/FCDAnimation.h" #include "FCDocument/FCDAnimationChannel.h" #include "FCDocument/FCDAnimationCurve.h" #include "FCDocument/FCDTargetedEntity.h" #include "FCDocument/FCDSceneNode.h" #include "FCDocument/FCDControllerInstance.h" #include "FCDocument/FCDEmitterInstance.h" #include "FCDocument/FCDImage.h" #include "FCDocument/FCDTexture.h" #include "FCDocument/FCDMaterial.h" #include "FCDocument/FCDEffect.h" #include "FCDocument/FCDEffectCode.h" #include "FCDocument/FCDEffectParameterSampler.h" #include "FCDocument/FCDEffectParameterSurface.h" #include "FCDocument/FCDEffectParameter.h" #include "FCDocument/FCDEffectPass.h" #include "FCDocument/FCDEffectPassShader.h" #include "FCDocument/FCDEffectPassState.h" #include "FCDocument/FCDEffectProfile.h" #include "FCDocument/FCDEffectProfileFX.h" #include "FCDocument/FCDEffectStandard.h" #include "FCDocument/FCDEffectTechnique.h" #include "FCDocument/FCDController.h" #include "FCDocument/FCDSkinController.h" #include "FCDocument/FCDMorphController.h" #include "FCDocument/FCDGeometry.h" #include "FCDocument/FCDGeometryMesh.h" bool FArchiveXML::LinkDriver(FCDocument* fcdoument, FCDAnimated* animated, const fm::string& animatedTargetPointer) { bool driven = false; size_t animationCount = fcdoument->GetAnimationLibrary()->GetEntityCount(); for (size_t i = 0; i < animationCount; ++i) { FCDAnimation* animation = fcdoument->GetAnimationLibrary()->GetEntity(i); driven |= FArchiveXML::LinkDriver(animation, animated, animatedTargetPointer); } return driven; } bool FArchiveXML::LinkDriver(FCDAnimation* animation, FCDAnimated* animated, const fm::string& animatedTargetPointer) { bool driver = false; for (size_t i = 0; i < animation->GetChannelCount(); ++i) { driver |= FArchiveXML::LinkDriver(animation->GetChannel(i), animated, animatedTargetPointer); } for (size_t i = 0; i < animation->GetChildrenCount(); ++i) { driver |= FArchiveXML::LinkDriver(animation->GetChild(i), animated, animatedTargetPointer); } return driver; } bool FArchiveXML::LinkDriver(FCDAnimationChannel* animationChannel, FCDAnimated* animated, const fm::string& animatedTargetPointer) { FCDAnimationChannelDataMap::iterator it = FArchiveXML::documentLinkDataMap[animationChannel->GetDocument()].animationChannelData.find(animationChannel); FUAssert(it != FArchiveXML::documentLinkDataMap[animationChannel->GetDocument()].animationChannelData.end(),); FCDAnimationChannelData& data = it->second; bool driver = !data.driverPointer.empty(); driver &= animatedTargetPointer == data.driverPointer; driver &= data.driverQualifier >= 0 && (uint32) data.driverQualifier < animated->GetValueCount(); if (driver) { // Retrieve the value pointer for the driver size_t curveCount = animationChannel->GetCurveCount(); for (size_t c = 0; c < curveCount; ++c) { animationChannel->GetCurve(c)->SetDriver(animated, data.driverQualifier); } } return driver; } bool FArchiveXML::LinkAnimation(FCDAnimation* animation) { bool status = true; // Link the child nodes and check the curves for their drivers for (size_t i = 0; i < animation->GetChannelCount(); ++i) { FCDAnimationChannel* channel = animation->GetChannel(i); FCDAnimationChannelDataMap::iterator it = FArchiveXML::documentLinkDataMap[channel->GetDocument()].animationChannelData.find(channel); FUAssert(it != FArchiveXML::documentLinkDataMap[channel->GetDocument()].animationChannelData.end(), continue); FCDAnimationChannelData& data = it->second; if (!data.driverPointer.empty() && channel->GetCurveCount() > 0 && channel->GetCurve(0)->HasDriver()) { status &= FUError::Error(FUError::ERROR_LEVEL, FUError::ERROR_ANIM_CURVE_DRIVER_MISSING); } } for (size_t i = 0; i < animation->GetChildrenCount(); ++i) { status &= FArchiveXML::LinkAnimation(animation->GetChild(i)); } return status; } bool FArchiveXML::LinkAnimated(FCDAnimated* animated, xmlNode* node) { bool linked; if (node != NULL) { // Write down the expected target string for the given node FCDAnimatedData data; FUDaeParser::CalculateNodeTargetPointer(node, data.pointer); // Check if this animated value is used as a driver linked = FArchiveXML::LinkDriver(animated->GetDocument(), animated, data.pointer); // Retrieve the list of the channels pointing to this node FCDAnimationChannelList channels; FArchiveXML::FindAnimationChannels(animated->GetDocument(), data.pointer, channels); linked |= FArchiveXML::ProcessChannels(animated, channels); if (linked) { FArchiveXML::documentLinkDataMap[animated->GetDocument()].animatedData.insert(animated, data); } } else linked = true; if (linked) { // Register this animated value with the document animated->GetDocument()->RegisterAnimatedValue(animated); } animated->SetDirtyFlag(); return linked; } bool FArchiveXML::LinkAnimatedCustom(FCDAnimatedCustom* animatedCustom, xmlNode* node) { bool linked = false; if (node != NULL) { // Retrieve the list of the channels pointing to this node FCDAnimatedData data; FUDaeParser::CalculateNodeTargetPointer(node, data.pointer); FCDAnimationChannelList channels; FArchiveXML::FindAnimationChannels(animatedCustom->GetDocument(), data.pointer, channels); StringList& qualifiers = animatedCustom->GetQualifiers(); // Extract all the qualifiers needed to hold these channels qualifiers.clear(); for (FCDAnimationChannelList::iterator itC = channels.begin(); itC != channels.end(); ++itC) { FCDAnimationChannel* channel = *itC; size_t chanelCurveCount = channel->GetCurveCount(); if (chanelCurveCount == 0) continue; // Retrieve the channel's qualifier FCDAnimationChannelData& channelData = FArchiveXML::documentLinkDataMap[channel->GetDocument()].animationChannelData[channel]; fm::string qualifier = channelData.targetQualifier; if (qualifier.empty()) { // Implies one channel holding multiple curves qualifiers.resize(chanelCurveCount); break; } else { qualifiers.push_back(qualifier); } } // Link the curves and check if this animated value is used as a driver if (qualifiers.empty()) qualifiers.resize(1); animatedCustom->Resize(qualifiers, false); linked |= FArchiveXML::ProcessChannels(animatedCustom, channels); linked |= FArchiveXML::LinkDriver(animatedCustom->GetDocument(), animatedCustom, data.pointer); if (linked) { FArchiveXML::documentLinkDataMap[animatedCustom->GetDocument()].animatedData.insert(animatedCustom, data); } } else linked = true; if (linked) { // Register this animated value with the document animatedCustom->GetDocument()->RegisterAnimatedValue(animatedCustom); } animatedCustom->SetDirtyFlag(); return linked; } bool FArchiveXML::LinkTargetedEntity(FCDTargetedEntity* targetedEntity) { bool status = true; FCDTargetedEntityDataMap::iterator it = FArchiveXML::documentLinkDataMap[targetedEntity->GetDocument()].targetedEntityDataMap.find(targetedEntity); FUAssert(it != FArchiveXML::documentLinkDataMap[targetedEntity->GetDocument()].targetedEntityDataMap.end(),); FCDTargetedEntityData& data = it->second; if (data.targetId.empty()) return status; // Skip externally-referenced targets, for now. FUUri targetUri(TO_FSTRING(data.targetId)); if (!targetUri.IsFile() && !targetUri.GetFragment().empty()) { FCDSceneNode* target = targetedEntity->GetDocument()->FindSceneNode(TO_STRING(targetUri.GetFragment())); if (target == NULL) { FUError::Error(FUError::WARNING_LEVEL, FUError::WARNING_TARGET_SCENE_NODE_MISSING); } targetedEntity->SetTargetNode(target); } else { FUError::Error(FUError::WARNING_LEVEL, FUError::WARNING_UNSUPPORTED_EXTERN_REF); } return status; } bool FArchiveXML::LinkSceneNode(FCDSceneNode* sceneNode) { bool status = true; size_t i, size = sceneNode->GetInstanceCount(); for (i = 0; i < size; i++) { FCDEntityInstance* instance = sceneNode->GetInstance(i); if (instance->IsType(FCDControllerInstance::GetClassType())) { status &= FArchiveXML::LinkControllerInstance((FCDControllerInstance*)instance); } else if (instance->IsType(FCDEmitterInstance::GetClassType())) { status &= FArchiveXML::LinkEmitterInstance((FCDEmitterInstance*)instance); } } size = sceneNode->GetChildrenCount(); for (i = 0; i < size; i++) { status &= FArchiveXML::LinkSceneNode(sceneNode->GetChild(i)); } return status; } void FArchiveXML::LinkMaterial(FCDMaterial* material) { FCDEffectParameterList parameters; size_t parameterCount = material->GetEffectParameterCount(); for (size_t p = 0; p < parameterCount; ++p) { FCDEffectParameter* parameter = material->GetEffectParameter(p); parameters.push_back(parameter); } for (size_t p = 0; p < parameterCount; ++p) { FCDEffectParameter* parameter = material->GetEffectParameter(p); if (parameter->IsType(FCDEffectParameterSurface::GetClassType())) { FArchiveXML::LinkEffectParameterSurface((FCDEffectParameterSurface*) parameter); } else if (parameter->IsType(FCDEffectParameterSampler::GetClassType())) { FArchiveXML::LinkEffectParameterSampler((FCDEffectParameterSampler*) parameter, parameters); } } } void FArchiveXML::LinkEffect(FCDEffect* effect) { // Link up the parameters FCDEffectParameterList parameters; size_t parameterCount = effect->GetEffectParameterCount(); for (size_t p = 0; p < parameterCount; ++p) { FCDEffectParameter* parameter = effect->GetEffectParameter(p); parameters.push_back(parameter); } for (size_t p = 0; p < parameterCount; ++p) { FCDEffectParameter* parameter = effect->GetEffectParameter(p); if (parameter->IsType(FCDEffectParameterSurface::GetClassType())) { FArchiveXML::LinkEffectParameterSurface((FCDEffectParameterSurface*) parameter); } else if (parameter->IsType(FCDEffectParameterSampler::GetClassType())) { FArchiveXML::LinkEffectParameterSampler((FCDEffectParameterSampler*) parameter, parameters); } } // Link up the profiles and their parameters/textures/images size_t profileCount = effect->GetProfileCount(); for (size_t p = 0; p < profileCount; ++p) { FCDEffectProfile* profile = effect->GetProfile(p); if (profile->IsType(FCDEffectStandard::GetClassType())) { FArchiveXML::LinkEffectStandard((FCDEffectStandard*) profile); } else if (profile->IsType(FCDEffectProfileFX::GetClassType())) { FArchiveXML::LinkEffectProfileFX((FCDEffectProfileFX*) profile); } else { FArchiveXML::LinkEffectProfile(profile); } } } void FArchiveXML::LinkEffectParameterSurface(FCDEffectParameterSurface* effectParameterSurface) { for (StringList::iterator itN = effectParameterSurface->GetNames().begin(); itN != effectParameterSurface->GetNames().end(); ++itN) { FCDImage* image = effectParameterSurface->GetDocument()->FindImage(*itN); if (image != NULL) { effectParameterSurface->AddImage(image); } } } void FArchiveXML::LinkEffectParameterSampler(FCDEffectParameterSampler* effectParameterSampler, FCDEffectParameterList& parameters) { FCDEffectParameterSamplerDataMap::iterator it = FArchiveXML::documentLinkDataMap[effectParameterSampler->GetDocument()].effectParameterSamplerDataMap.find(effectParameterSampler); FUAssert(it != FArchiveXML::documentLinkDataMap[effectParameterSampler->GetDocument()].effectParameterSamplerDataMap.end(),); FCDEffectParameterSamplerData& data = it->second; FCDEffectParameter* surface = NULL; size_t count = parameters.size(); for (size_t i = 0; i < count; ++i) { if (IsEquivalent(parameters[i]->GetReference(), data.surfaceSid)) { surface = parameters[i]; break; } } FUAssert(surface == NULL || surface->HasType(FCDEffectParameterSurface::GetClassType()), return); effectParameterSampler->SetSurface((FCDEffectParameterSurface*) surface); data.surfaceSid.clear(); } void FArchiveXML::LinkEffectProfile(FCDEffectProfile* effectProfile) { // Make up a list of all the available parameters. FCDEffectParameterList parameters; size_t parameterCount = effectProfile->GetEffectParameterCount(); for (size_t p = 0; p < parameterCount; ++p) { parameters.push_back(effectProfile->GetEffectParameter(p)); } // Also retrieve the parent's parameters. size_t parentParameterCount = effectProfile->GetParent()->GetEffectParameterCount(); for (size_t p = 0; p < parentParameterCount; ++p) { parameters.push_back(effectProfile->GetParent()->GetEffectParameter(p)); } for (size_t p = 0; p < parameterCount; ++p) { FCDEffectParameter* parameter = effectProfile->GetEffectParameter(p); if (parameter->HasType(FCDEffectParameterSurface::GetClassType())) { FArchiveXML::LinkEffectParameterSurface((FCDEffectParameterSurface*) parameter); } else if (parameter->HasType(FCDEffectParameterSampler::GetClassType())) { FArchiveXML::LinkEffectParameterSampler((FCDEffectParameterSampler*) parameter, parameters); } } } void FArchiveXML::LinkEffectProfileFX(FCDEffectProfileFX* effectProfileFX) { // Call the parent to link the effect parameters FArchiveXML::LinkEffectProfile(effectProfileFX); size_t techniqueCount = effectProfileFX->GetTechniqueCount(); for (size_t t = 0; t < techniqueCount; ++t) { FArchiveXML::LinkEffectTechnique(effectProfileFX->GetTechnique(t)); } } void FArchiveXML::LinkEffectStandard(FCDEffectStandard* effectStandard) { FArchiveXML::LinkEffectProfile(effectStandard); // Make up a list of all the parameters available for linkage. FCDEffectParameterList parameters; size_t parameterCount = effectStandard->GetEffectParameterCount(); for (size_t p = 0; p < parameterCount; ++p) { parameters.push_back(effectStandard->GetEffectParameter(p)); } // Also retrieve the parent's parameters. size_t parentParameterCount = effectStandard->GetParent()->GetEffectParameterCount(); for (size_t p = 0; p < parentParameterCount; ++p) { parameters.push_back(effectStandard->GetParent()->GetEffectParameter(p)); } // Link the textures with the sampler parameters for (uint32 i = 0; i < FUDaeTextureChannel::COUNT; ++i) { size_t bucketSize = effectStandard->GetTextureCount(i); for (size_t t = 0; t < bucketSize; ++t) { FArchiveXML::LinkTexture(effectStandard->GetTexture(i, t), parameters); } } } void FArchiveXML::LinkEffectTechnique(FCDEffectTechnique* effectTechnique) { // Make up a list of all the parameters available for linkage. FCDEffectParameterList parameters; size_t parameterCount = effectTechnique->GetEffectParameterCount(); for (size_t p = 0; p < parameterCount; ++p) { parameters.push_back(effectTechnique->GetEffectParameter(p)); } // Also retrieve the parent's parameters. size_t parentParameterCount = effectTechnique->GetParent()->GetEffectParameterCount(); for (size_t p = 0; p < parentParameterCount; ++p) { parameters.push_back(effectTechnique->GetParent()->GetEffectParameter(p)); } // Also retrieve the parent's parent's parameters. parentParameterCount = effectTechnique->GetParent()->GetParent()->GetEffectParameterCount(); for (size_t p = 0; p < parentParameterCount; ++p) { parameters.push_back(effectTechnique->GetParent()->GetParent()->GetEffectParameter(p)); } for (size_t p = 0; p < parameterCount; ++p) { FCDEffectParameter* parameter = effectTechnique->GetEffectParameter(p); if (parameter->HasType(FCDEffectParameterSurface::GetClassType())) { FArchiveXML::LinkEffectParameterSurface((FCDEffectParameterSurface*) parameter); } else if (parameter->HasType(FCDEffectParameterSampler::GetClassType())) { FArchiveXML::LinkEffectParameterSampler((FCDEffectParameterSampler*) parameter, parameters); } } } void FArchiveXML::LinkTexture(FCDTexture* texture, FCDEffectParameterList& parameters) { FCDTextureDataMap::iterator it = FArchiveXML::documentLinkDataMap[texture->GetDocument()].textureDataMap.find(texture); FUAssert(it != FArchiveXML::documentLinkDataMap[texture->GetDocument()].textureDataMap.end(),); FCDTextureData& data = it->second; if (!data.samplerSid.empty()) { // Check for the sampler parameter in the parent profile and the effect. if (texture->GetParent() != NULL) { const fm::string& cleanRef = FCDObjectWithId::CleanSubId(data.samplerSid); FCDEffectParameterSampler* sampler = NULL; size_t parameterCount = parameters.size(); for (size_t p = 0; p < parameterCount; ++p) { if (IsEquivalent(parameters[p]->GetReference(), cleanRef) && parameters[p]->IsType(FCDEffectParameterSampler::GetClassType())) { sampler = (FCDEffectParameterSampler*) parameters[p]; break; } } if (sampler != NULL) texture->SetSampler(sampler); } if (!texture->HasSampler() && !data.samplerSid.empty()) { // Early COLLADA 1.4.0 backward compatibility: Also look for an image with this id. if (data.samplerSid[0] == '#') data.samplerSid.erase(0, 1); FCDImage* image = texture->GetDocument()->FindImage(data.samplerSid); texture->SetImage(image); texture->SetDirtyFlag(); FUAssert(texture->HasSampler(), FUError::Error(FUError::WARNING_LEVEL, FUError::ERROR_INVALID_TEXTURE_SAMPLER)); } data.samplerSid.clear(); } } bool FArchiveXML::LinkController(FCDController* controller) { bool ret = true; if (controller->GetBaseTarget() == NULL) { if (controller->IsSkin()) { ret &= FArchiveXML::LinkSkinController(controller->GetSkinController()); } else if (controller->IsMorph()) { ret &= FArchiveXML::LinkMorphController(controller->GetMorphController()); } else return false; // If our target is a controller, link it too. FCDEntity* entity = controller->GetBaseTarget(); if (entity != NULL && entity->GetType() == FCDEntity::CONTROLLER) { ret &= FArchiveXML::LinkController((FCDController*)entity); } } return ret; } bool FArchiveXML::LinkSkinController(FCDSkinController* UNUSED(skinController)) { return true; } bool FArchiveXML::LinkMorphController(FCDMorphController* morphController) { FCDMorphControllerDataMap::iterator it = FArchiveXML::documentLinkDataMap[morphController->GetDocument()].morphControllerDataMap.find(morphController); FUAssert(it != FArchiveXML::documentLinkDataMap[morphController->GetDocument()].morphControllerDataMap.end(),); FCDMorphControllerData& data = it->second; if (morphController->GetBaseTarget() == NULL) { fm::string targetId = SkipPound(data.targetId); FCDEntity* baseTarget = morphController->GetDocument()->FindGeometry(targetId); if (baseTarget == NULL) baseTarget = morphController->GetDocument()->FindController(data.targetId); if (baseTarget == NULL) { FUError::Error(FUError::WARNING_LEVEL, FUError::WARNING_UNKNOWN_MC_BASE_TARGET_MISSING, 0); return false; } morphController->SetBaseTarget(baseTarget); data.targetId.clear(); } return true; } bool FArchiveXML::LinkGeometryMesh(FCDGeometryMesh* geometryMesh) { bool status = true; if (!geometryMesh->IsConvex() || geometryMesh->GetConvexHullOf().empty()) return status; FCDGeometry* concaveGeom = geometryMesh->GetDocument()->FindGeometry(geometryMesh->GetConvexHullOf()); if (concaveGeom) { FCDGeometryMesh* origMesh = concaveGeom->GetMesh(); if (origMesh != NULL) { origMesh->Clone(geometryMesh); geometryMesh->SetConvexify(geometryMesh != NULL); geometryMesh->SetConvex(true); // may have been overwritten by clone } return status; } else { //return status.Fail(FS("Unknown geometry for creation of convex hull of: ") + TO_FSTRING(parent->GetDaeId())); FUError::Error(FUError::ERROR_LEVEL, FUError::ERROR_UNKNOWN_GEO_CH); return status; } }