2009-04-18 19:00:33 +02:00
|
|
|
/* Copyright (C) 2009 Wildfire Games.
|
|
|
|
* This file is part of 0 A.D.
|
|
|
|
*
|
|
|
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* 0 A.D. is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2006-12-26 23:43:09 +01:00
|
|
|
#include "precompiled.h"
|
|
|
|
|
|
|
|
#include "CommonConvert.h"
|
|
|
|
|
2007-03-16 19:00:58 +01:00
|
|
|
#include "StdSkeletons.h"
|
2008-08-25 00:22:25 +02:00
|
|
|
#include "XMLFix.h"
|
2007-03-16 19:00:58 +01:00
|
|
|
|
2006-12-26 23:43:09 +01:00
|
|
|
#include "FCollada.h"
|
|
|
|
#include "FCDocument/FCDSceneNode.h"
|
|
|
|
#include "FCDocument/FCDSkinController.h"
|
2007-03-09 14:40:28 +01:00
|
|
|
#include "FUtils/FUDaeSyntax.h"
|
|
|
|
#include "FUtils/FUFileManager.h"
|
2006-12-26 23:43:09 +01:00
|
|
|
|
|
|
|
#include <cassert>
|
2008-08-25 00:22:25 +02:00
|
|
|
#include <algorithm>
|
2006-12-26 23:43:09 +01:00
|
|
|
|
|
|
|
void require_(int line, bool value, const char* type, const char* message)
|
|
|
|
{
|
|
|
|
if (value) return;
|
|
|
|
char linestr[16];
|
2009-11-06 19:35:32 +01:00
|
|
|
sprintf(linestr, "%d", line);
|
2006-12-26 23:43:09 +01:00
|
|
|
throw ColladaException(std::string(type) + " (line " + linestr + "): " + message);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Error handler for libxml2 */
|
|
|
|
void errorHandler(void* ctx, const char* msg, ...)
|
|
|
|
{
|
|
|
|
char buffer[1024];
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, msg);
|
|
|
|
vsnprintf(buffer, sizeof(buffer), msg, ap);
|
|
|
|
buffer[sizeof(buffer)-1] = '\0';
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
*((std::string*)ctx) += buffer;
|
|
|
|
}
|
|
|
|
|
2007-03-01 01:24:34 +01:00
|
|
|
FColladaErrorHandler::FColladaErrorHandler(std::string& xmlErrors_)
|
|
|
|
: xmlErrors(xmlErrors_)
|
|
|
|
{
|
|
|
|
// Grab all the error output from libxml2, for useful error reporting
|
|
|
|
xmlSetGenericErrorFunc(&xmlErrors, &errorHandler);
|
|
|
|
|
2008-08-25 00:22:25 +02:00
|
|
|
FUError::AddErrorCallback(FUError::DEBUG_LEVEL, this, &FColladaErrorHandler::OnError);
|
|
|
|
FUError::AddErrorCallback(FUError::WARNING_LEVEL, this, &FColladaErrorHandler::OnError);
|
|
|
|
FUError::AddErrorCallback(FUError::ERROR_LEVEL, this, &FColladaErrorHandler::OnError);
|
2007-03-01 01:24:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
FColladaErrorHandler::~FColladaErrorHandler()
|
|
|
|
{
|
|
|
|
xmlSetGenericErrorFunc(NULL, NULL);
|
|
|
|
|
2008-08-25 00:22:25 +02:00
|
|
|
FUError::RemoveErrorCallback(FUError::DEBUG_LEVEL, this, &FColladaErrorHandler::OnError);
|
|
|
|
FUError::RemoveErrorCallback(FUError::WARNING_LEVEL, this, &FColladaErrorHandler::OnError);
|
|
|
|
FUError::RemoveErrorCallback(FUError::ERROR_LEVEL, this, &FColladaErrorHandler::OnError);
|
2007-03-01 01:24:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void FColladaErrorHandler::OnError(FUError::Level errorLevel, uint32 errorCode, uint32 UNUSED(lineNumber))
|
|
|
|
{
|
|
|
|
const char* errorString = FUError::GetErrorString((FUError::Code) errorCode);
|
|
|
|
if (! errorString)
|
|
|
|
errorString = "Unknown error code";
|
|
|
|
|
2008-08-25 00:22:25 +02:00
|
|
|
if (errorLevel == FUError::DEBUG_LEVEL)
|
2009-11-06 11:59:10 +01:00
|
|
|
Log(LOG_INFO, "FCollada message %d: %s", errorCode, errorString);
|
2008-08-25 00:22:25 +02:00
|
|
|
else if (errorLevel == FUError::WARNING_LEVEL)
|
2009-11-06 11:59:10 +01:00
|
|
|
Log(LOG_WARNING, "FCollada warning %d: %s", errorCode, errorString);
|
2007-03-01 01:24:34 +01:00
|
|
|
else
|
|
|
|
throw ColladaException(errorString);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-03-09 14:40:28 +01:00
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void FColladaDocument::LoadFromText(const char *text)
|
|
|
|
{
|
|
|
|
document.reset(FCollada::NewTopDocument());
|
|
|
|
|
2008-08-25 00:22:25 +02:00
|
|
|
const char* newText = NULL;
|
|
|
|
size_t newTextSize = 0;
|
|
|
|
FixBrokenXML(text, &newText, &newTextSize);
|
2007-03-09 14:40:28 +01:00
|
|
|
|
2009-11-06 11:59:10 +01:00
|
|
|
// Log(LOG_INFO, "%s", newText);
|
2008-08-25 00:22:25 +02:00
|
|
|
bool status = FCollada::LoadDocumentFromMemory("unknown.dae", document.get(), (void*)newText, newTextSize);
|
2007-03-09 14:40:28 +01:00
|
|
|
|
2008-08-25 00:22:25 +02:00
|
|
|
if (newText != text)
|
2009-07-26 11:48:10 +02:00
|
|
|
xmlFree((void*)newText);
|
2007-03-09 14:40:28 +01:00
|
|
|
|
|
|
|
REQUIRE_SUCCESS(status);
|
|
|
|
}
|
|
|
|
|
2008-09-18 13:31:12 +02:00
|
|
|
void FColladaDocument::ReadExtras(xmlNode* UNUSED(colladaNode))
|
2007-03-09 14:40:28 +01:00
|
|
|
{
|
2008-08-25 00:22:25 +02:00
|
|
|
// TODO: This was needed to recognise and load XSI models.
|
|
|
|
// XSI support should be reintroduced some time, but this function
|
|
|
|
// may not be necessary since FCollada might now provide access to the
|
|
|
|
// 'extra' data via a proper API.
|
|
|
|
|
|
|
|
/*
|
2007-03-09 14:40:28 +01:00
|
|
|
if (! IsEquivalent(colladaNode->name, DAE_COLLADA_ELEMENT))
|
|
|
|
return;
|
|
|
|
|
|
|
|
extra.reset(new FCDExtra(document.get()));
|
|
|
|
|
|
|
|
xmlNodeList extraNodes;
|
|
|
|
FUXmlParser::FindChildrenByType(colladaNode, DAE_EXTRA_ELEMENT, extraNodes);
|
|
|
|
for (xmlNodeList::iterator it = extraNodes.begin(); it != extraNodes.end(); ++it)
|
|
|
|
{
|
|
|
|
xmlNode* extraNode = (*it);
|
|
|
|
extra->LoadFromXML(extraNode);
|
|
|
|
}
|
2008-08-25 00:22:25 +02:00
|
|
|
*/
|
2007-03-09 14:40:28 +01:00
|
|
|
}
|
2007-03-01 01:24:34 +01:00
|
|
|
|
2006-12-26 23:43:09 +01:00
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2007-03-16 19:00:58 +01:00
|
|
|
CommonConvert::CommonConvert(const char* text, std::string& xmlErrors)
|
|
|
|
: m_Err(xmlErrors)
|
|
|
|
{
|
|
|
|
m_Doc.LoadFromText(text);
|
|
|
|
FCDSceneNode* root = m_Doc.GetDocument()->GetVisualSceneRoot();
|
|
|
|
REQUIRE(root != NULL, "has root object");
|
|
|
|
|
|
|
|
// Find the instance to convert
|
|
|
|
if (! FindSingleInstance(root, m_Instance, m_EntityTransform))
|
|
|
|
throw ColladaException("Couldn't find object to convert");
|
|
|
|
|
|
|
|
assert(m_Instance);
|
2009-11-06 11:59:10 +01:00
|
|
|
Log(LOG_INFO, "Converting '%s'", m_Instance->GetEntity()->GetName().c_str());
|
2007-03-16 19:00:58 +01:00
|
|
|
|
|
|
|
m_IsXSI = false;
|
|
|
|
FCDAsset* asset = m_Doc.GetDocument()->GetAsset();
|
|
|
|
if (asset && asset->GetContributorCount() >= 1)
|
|
|
|
{
|
2008-09-17 17:55:04 +02:00
|
|
|
std::string tool (asset->GetContributor(0)->GetAuthoringTool());
|
2007-03-16 19:00:58 +01:00
|
|
|
if (tool.find("XSI") != tool.npos)
|
|
|
|
m_IsXSI = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
FMVector3 upAxis = m_Doc.GetDocument()->GetAsset()->GetUpAxis();
|
|
|
|
m_YUp = (upAxis.y != 0); // assume either Y_UP or Z_UP (TODO: does anyone ever do X_UP?)
|
|
|
|
}
|
|
|
|
|
2008-08-25 00:22:25 +02:00
|
|
|
CommonConvert::~CommonConvert()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2007-03-16 19:00:58 +01:00
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2007-03-20 02:06:34 +01:00
|
|
|
// HACK: The originals don't get exported properly from FCollada (3.02, DLL), so define
|
2007-03-09 14:40:28 +01:00
|
|
|
// them here instead of fixing it correctly.
|
2007-03-20 02:06:34 +01:00
|
|
|
const FMVector3 FMVector3_XAxis(1.0f, 0.0f, 0.0f);
|
2007-03-01 01:24:34 +01:00
|
|
|
static float identity[] = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };
|
2007-03-20 02:06:34 +01:00
|
|
|
FMMatrix44 FMMatrix44_Identity(identity);
|
2007-03-01 01:24:34 +01:00
|
|
|
|
2006-12-26 23:43:09 +01:00
|
|
|
struct FoundInstance
|
|
|
|
{
|
|
|
|
FCDEntityInstance* instance;
|
|
|
|
FMMatrix44 transform;
|
|
|
|
};
|
|
|
|
|
2007-03-09 14:40:28 +01:00
|
|
|
static bool IsVisible_XSI(FCDSceneNode* node, bool& visible)
|
|
|
|
{
|
|
|
|
// Look for <extra><technique profile="XSI"><SI_Visibility><xsi_param sid="visibility">
|
|
|
|
|
|
|
|
FCDExtra* extra = node->GetExtra();
|
|
|
|
if (! extra) return false;
|
|
|
|
|
|
|
|
FCDEType* type = extra->GetDefaultType();
|
|
|
|
if (! type) return false;
|
|
|
|
|
|
|
|
FCDETechnique* technique = type->FindTechnique("XSI");
|
|
|
|
if (! technique) return false;
|
|
|
|
|
|
|
|
FCDENode* visibility1 = technique->FindChildNode("SI_Visibility");
|
|
|
|
if (! visibility1) return false;
|
|
|
|
|
|
|
|
FCDENode* visibility2 = visibility1->FindChildNode("xsi_param");
|
|
|
|
if (! visibility2) return false;
|
|
|
|
|
|
|
|
if (IsEquivalent(visibility2->GetContent(), "TRUE"))
|
|
|
|
visible = true;
|
|
|
|
else if (IsEquivalent(visibility2->GetContent(), "FALSE"))
|
|
|
|
visible = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool IsVisible(FCDSceneNode* node)
|
|
|
|
{
|
2009-07-28 18:59:19 +02:00
|
|
|
bool visible = false;
|
2007-03-09 14:40:28 +01:00
|
|
|
|
|
|
|
// Try the XSI visibility property
|
|
|
|
if (IsVisible_XSI(node, visible))
|
|
|
|
return visible;
|
|
|
|
|
|
|
|
// Else fall back to the FCollada-specific setting
|
|
|
|
visible = (node->GetVisibility() != 0.0);
|
|
|
|
return visible;
|
|
|
|
}
|
|
|
|
|
2006-12-26 23:43:09 +01:00
|
|
|
/**
|
|
|
|
* Recursively finds all entities under the current node. If onlyMarked is
|
|
|
|
* set, only matches entities where the user-defined property was set to
|
|
|
|
* "export" in the modelling program.
|
|
|
|
*
|
|
|
|
* @param node root of subtree to search
|
|
|
|
* @param instances output - appends matching entities
|
|
|
|
* @param transform transform matrix of current subtree
|
|
|
|
* @param onlyMarked only match entities with "export" property
|
|
|
|
*/
|
|
|
|
static void FindInstances(FCDSceneNode* node, std::vector<FoundInstance>& instances, const FMMatrix44& transform, bool onlyMarked)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < node->GetChildrenCount(); ++i)
|
|
|
|
{
|
|
|
|
FCDSceneNode* child = node->GetChild(i);
|
|
|
|
FindInstances(child, instances, transform * node->ToMatrix(), onlyMarked);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < node->GetInstanceCount(); ++i)
|
|
|
|
{
|
|
|
|
if (onlyMarked)
|
|
|
|
{
|
|
|
|
if (node->GetNote() != "export")
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only accept instances of appropriate types, and not e.g. lights
|
|
|
|
FCDEntity::Type type = node->GetInstance(i)->GetEntityType();
|
|
|
|
if (! (type == FCDEntity::GEOMETRY || type == FCDEntity::CONTROLLER))
|
|
|
|
continue;
|
|
|
|
|
2007-03-09 14:40:28 +01:00
|
|
|
// Ignore invisible objects, because presumably nobody wanted to export them
|
|
|
|
if (! IsVisible(node))
|
|
|
|
continue;
|
|
|
|
|
2006-12-26 23:43:09 +01:00
|
|
|
FoundInstance f;
|
|
|
|
f.transform = transform * node->ToMatrix();
|
|
|
|
f.instance = node->GetInstance(i);
|
|
|
|
instances.push_back(f);
|
2009-11-06 11:59:10 +01:00
|
|
|
Log(LOG_INFO, "Found convertible object '%s'", node->GetName().c_str());
|
2006-12-26 23:43:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FindSingleInstance(FCDSceneNode* node, FCDEntityInstance*& instance, FMMatrix44& transform)
|
|
|
|
{
|
|
|
|
std::vector<FoundInstance> instances;
|
|
|
|
|
2007-03-20 02:06:34 +01:00
|
|
|
FindInstances(node, instances, FMMatrix44_Identity, true);
|
2006-12-26 23:43:09 +01:00
|
|
|
if (instances.size() > 1)
|
|
|
|
{
|
|
|
|
Log(LOG_ERROR, "Found too many export-marked objects");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (instances.empty())
|
|
|
|
{
|
2007-03-20 02:06:34 +01:00
|
|
|
FindInstances(node, instances, FMMatrix44_Identity, false);
|
2006-12-26 23:43:09 +01:00
|
|
|
if (instances.size() > 1)
|
|
|
|
{
|
|
|
|
Log(LOG_ERROR, "Found too many possible objects to convert - try adding the 'export' property to disambiguate one");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (instances.empty())
|
|
|
|
{
|
|
|
|
Log(LOG_ERROR, "Didn't find any objects in the scene");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(instances.size() == 1); // if we got this far
|
|
|
|
instance = instances[0].instance;
|
|
|
|
transform = instances[0].transform;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
static bool ReverseSortWeight(const FCDJointWeightPair& a, const FCDJointWeightPair& b)
|
|
|
|
{
|
|
|
|
return (a.weight > b.weight);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SkinReduceInfluences(FCDSkinController* skin, size_t maxInfluenceCount, float minimumWeight)
|
|
|
|
{
|
2008-08-25 00:22:25 +02:00
|
|
|
// Approximately equivalent to:
|
|
|
|
// skin->ReduceInfluences(maxInfluenceCount, minimumWeight);
|
|
|
|
// except this version merges multiple weights for the same joint
|
|
|
|
|
|
|
|
for (size_t i = 0; i < skin->GetInfluenceCount(); ++i)
|
2006-12-26 23:43:09 +01:00
|
|
|
{
|
2008-08-25 00:22:25 +02:00
|
|
|
FCDSkinControllerVertex& influence = *skin->GetVertexInfluence(i);
|
2006-12-26 23:43:09 +01:00
|
|
|
|
2008-08-25 00:22:25 +02:00
|
|
|
std::vector<FCDJointWeightPair> newWeights;
|
|
|
|
for (size_t i = 0; i < influence.GetPairCount(); ++i)
|
2006-12-26 23:43:09 +01:00
|
|
|
{
|
2008-08-25 00:22:25 +02:00
|
|
|
FCDJointWeightPair* weight = influence.GetPair(i);
|
|
|
|
|
|
|
|
for (size_t j = 0; j < newWeights.size(); ++j)
|
2006-12-26 23:43:09 +01:00
|
|
|
{
|
2008-08-25 00:22:25 +02:00
|
|
|
FCDJointWeightPair& newWeight = newWeights[j];
|
|
|
|
if (weight->jointIndex == newWeight.jointIndex)
|
2006-12-26 23:43:09 +01:00
|
|
|
{
|
2008-08-25 00:22:25 +02:00
|
|
|
newWeight.weight += weight->weight;
|
|
|
|
goto MERGED_WEIGHTS;
|
2006-12-26 23:43:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-25 00:22:25 +02:00
|
|
|
newWeights.push_back(*weight);
|
|
|
|
MERGED_WEIGHTS: ;
|
2006-12-26 23:43:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Put highest-weighted influences at the front of the list
|
2008-08-25 00:22:25 +02:00
|
|
|
std::sort(newWeights.begin(), newWeights.end(), ReverseSortWeight);
|
2006-12-26 23:43:09 +01:00
|
|
|
|
|
|
|
// Limit the maximum number of influences
|
|
|
|
if (newWeights.size() > maxInfluenceCount)
|
|
|
|
newWeights.resize(maxInfluenceCount);
|
|
|
|
|
|
|
|
// Enforce the minimum weight per influence
|
2007-03-16 19:00:58 +01:00
|
|
|
// (This is done here rather than in the earlier loop, because several
|
|
|
|
// small weights for the same bone might add up to a value above the
|
|
|
|
// threshold)
|
2006-12-26 23:43:09 +01:00
|
|
|
while (!newWeights.empty() && newWeights.back().weight < minimumWeight)
|
|
|
|
newWeights.pop_back();
|
|
|
|
|
|
|
|
// Renormalise, so sum(weights)=1
|
|
|
|
float totalWeight = 0;
|
2008-08-25 00:22:25 +02:00
|
|
|
for (std::vector<FCDJointWeightPair>::iterator itNW = newWeights.begin(); itNW != newWeights.end(); ++itNW)
|
2006-12-26 23:43:09 +01:00
|
|
|
totalWeight += itNW->weight;
|
2008-08-25 00:22:25 +02:00
|
|
|
for (std::vector<FCDJointWeightPair>::iterator itNW = newWeights.begin(); itNW != newWeights.end(); ++itNW)
|
2006-12-26 23:43:09 +01:00
|
|
|
itNW->weight /= totalWeight;
|
|
|
|
|
|
|
|
// Copy new weights into the skin
|
2008-08-25 00:22:25 +02:00
|
|
|
influence.SetPairCount(0);
|
|
|
|
for (std::vector<FCDJointWeightPair>::iterator itNW = newWeights.begin(); itNW != newWeights.end(); ++itNW)
|
|
|
|
influence.AddPair(itNW->jointIndex, itNW->weight);
|
2006-12-26 23:43:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
skin->SetDirtyFlag();
|
|
|
|
}
|
2007-03-09 14:40:28 +01:00
|
|
|
|
|
|
|
|
2008-09-18 13:31:12 +02:00
|
|
|
void FixSkeletonRoots(FCDControllerInstance& UNUSED(controllerInstance))
|
2007-03-09 14:40:28 +01:00
|
|
|
{
|
2008-08-25 00:22:25 +02:00
|
|
|
// TODO: Need to reintroduce XSI support at some point
|
|
|
|
#if 0
|
2007-03-09 14:40:28 +01:00
|
|
|
// HACK: The XSI exporter doesn't do a <skeleton> and FCollada doesn't
|
|
|
|
// seem to know where else to look, so just guess that it's somewhere
|
|
|
|
// under Scene_Root
|
2007-03-16 19:00:58 +01:00
|
|
|
if (controllerInstance.GetSkeletonRoots().empty())
|
2007-03-09 14:40:28 +01:00
|
|
|
{
|
|
|
|
// HACK (evil): SetSkeletonRoot is declared but not defined, and there's
|
|
|
|
// no other proper way to modify the skeleton-roots list, so cheat horribly
|
2007-03-16 19:00:58 +01:00
|
|
|
FUUriList& uriList = const_cast<FUUriList&>(controllerInstance.GetSkeletonRoots());
|
2007-03-09 14:40:28 +01:00
|
|
|
uriList.push_back(FUUri("Scene_Root"));
|
2007-03-16 19:00:58 +01:00
|
|
|
controllerInstance.LinkImport();
|
|
|
|
}
|
2008-08-25 00:22:25 +02:00
|
|
|
#endif
|
2007-03-16 19:00:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const Skeleton& FindSkeleton(const FCDControllerInstance& controllerInstance)
|
|
|
|
{
|
|
|
|
// I can't see any proper way to determine the real root of the skeleton,
|
|
|
|
// so just choose an arbitrary bone and search upwards until we find a
|
|
|
|
// recognised ancestor (or until we fall off the top of the tree)
|
|
|
|
|
|
|
|
const Skeleton* skeleton = NULL;
|
|
|
|
const FCDSceneNode* joint = controllerInstance.GetJoint(0);
|
2008-08-25 00:22:25 +02:00
|
|
|
while (joint && (skeleton = Skeleton::FindSkeleton(joint->GetName().c_str())) == NULL)
|
2007-03-16 19:00:58 +01:00
|
|
|
{
|
|
|
|
joint = joint->GetParent();
|
|
|
|
}
|
|
|
|
REQUIRE(skeleton != NULL, "recognised skeleton structure");
|
|
|
|
return *skeleton;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TransformBones(std::vector<BoneTransform>& bones, const FMMatrix44& scaleTransform, bool yUp)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < bones.size(); ++i)
|
|
|
|
{
|
|
|
|
// Apply the desired transformation to the bone coordinates
|
|
|
|
FMVector3 trans(bones[i].translation, 0);
|
|
|
|
trans = scaleTransform.TransformCoordinate(trans);
|
|
|
|
bones[i].translation[0] = trans.x;
|
|
|
|
bones[i].translation[1] = trans.y;
|
|
|
|
bones[i].translation[2] = trans.z;
|
|
|
|
|
|
|
|
// DON'T apply the transformation to orientation, because I can't get
|
|
|
|
// that kind of thing to work in practice (interacting nicely between
|
|
|
|
// the models and animations), so this function assumes the transform
|
|
|
|
// just does scaling, so there's no need to rotate anything. (But I think
|
|
|
|
// this code would work for rotation, though not very efficiently.)
|
|
|
|
/*
|
|
|
|
FMMatrix44 m = FMQuaternion(bones[i].orientation[0], bones[i].orientation[1], bones[i].orientation[2], bones[i].orientation[3]).ToMatrix();
|
|
|
|
m *= scaleTransform;
|
|
|
|
HMatrix matrix;
|
|
|
|
memcpy(matrix, m.Transposed().m, sizeof(matrix));
|
|
|
|
AffineParts parts;
|
|
|
|
decomp_affine(matrix, &parts);
|
|
|
|
|
|
|
|
bones[i].orientation[0] = parts.q.x;
|
|
|
|
bones[i].orientation[1] = parts.q.y;
|
|
|
|
bones[i].orientation[2] = parts.q.z;
|
|
|
|
bones[i].orientation[3] = parts.q.w;
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (yUp)
|
|
|
|
{
|
|
|
|
// TODO: this is all just guesses which seem to work for data
|
|
|
|
// exported from XSI, rather than having been properly thought
|
|
|
|
// through
|
|
|
|
bones[i].translation[2] = -bones[i].translation[2];
|
|
|
|
bones[i].orientation[2] = -bones[i].orientation[2];
|
|
|
|
bones[i].orientation[3] = -bones[i].orientation[3];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Convert bone translations from xyz into xzy axes:
|
|
|
|
std::swap(bones[i].translation[1], bones[i].translation[2]);
|
|
|
|
|
|
|
|
// To convert the quaternions: imagine you're using the axis/angle
|
|
|
|
// representation, then swap the y,z basis vectors and change the
|
|
|
|
// direction of rotation by negating the angle ( => negating sin(angle)
|
|
|
|
// => negating x,y,z => changing (x,y,z,w) to (-x,-z,-y,w)
|
|
|
|
// but then (-x,-z,-y,w) == (x,z,y,-w) so do that instead)
|
|
|
|
std::swap(bones[i].orientation[1], bones[i].orientation[2]);
|
|
|
|
bones[i].orientation[3] = -bones[i].orientation[3];
|
|
|
|
}
|
2007-03-09 14:40:28 +01:00
|
|
|
}
|
|
|
|
}
|