# Initial COLLADA support.

Just does static geometry at the moment.

This was SVN commit r4680.
This commit is contained in:
Ykkrosh 2006-12-06 00:06:05 +00:00
parent 34d62318f5
commit 8fa6b12568
11 changed files with 1093 additions and 3 deletions

View File

@ -0,0 +1,38 @@
from ctypes import *
import sys
import os
if len(sys.argv) != 3:
print "Incorrect command-line syntax. Use"
print " " + sys.argv[0] + " input.dae output.pmd"
sys.exit(-1)
input_filename, output_filename = sys.argv[1:]
if not os.path.exists(input_filename):
print "Cannot find input file '%s'" % input_filename
sys.exit(-1)
library = cdll.LoadLibrary('Collada.dll')
def log(severity, message):
print '[%s] %s' % (('INFO', 'WARNING', 'ERROR')[severity], message)
clog = CFUNCTYPE(None, c_int, c_char_p)(log)
# (the CFUNCTYPE must not be GC'd, so try to keep a reference)
library.set_logger(clog)
def convert_dae_to_pmd(filename):
output = ['']
def cb(str, len):
output[0] += string_at(str, len)
cbtype = CFUNCTYPE(None, POINTER(c_char), c_uint)
status = library.convert_dae_to_pmd(filename, cbtype(cb))
assert(status == 0)
return output[0]
input = open(input_filename).read()
output = convert_dae_to_pmd(input)
open(output_filename, 'wb').write(output)

View File

@ -62,6 +62,11 @@ extern_lib_defs = {
win_names = { "ddraw", "dsound" }, win_names = { "ddraw", "dsound" },
dbg_suffix = "", dbg_suffix = "",
}, },
fcollada = {
win_names = { "FColladaS" },
dbg_suffix = "D",
no_delayload = 1,
},
ffmpeg = { ffmpeg = {
win_names = { "avcodec-51", "avformat-51", "avutil-49" }, win_names = { "avcodec-51", "avformat-51", "avutil-49" },
dbg_suffix = "", dbg_suffix = "",

View File

@ -1,4 +1,5 @@
addoption("atlas", "Include Atlas scenario editor packages") addoption("atlas", "Include Atlas scenario editor packages")
addoption("collada", "Include COLLADA packages (requires FCollada library)")
addoption("outpath", "Location for generated project files") addoption("outpath", "Location for generated project files")
addoption("without-tests", "Disable generation of test projects") addoption("without-tests", "Disable generation of test projects")
addoption("without-pch", "Disable generation and usage of precompiled headers") addoption("without-pch", "Disable generation and usage of precompiled headers")
@ -200,7 +201,7 @@ static_lib_names = {}
-- set up one of the static libraries into which the main engine code is split. -- set up one of the static libraries into which the main engine code is split.
-- extra_params: see package_add_contents(). -- extra_params: see package_add_contents().
-- note: rel_source_dirs and rel_include_dirs are relative to global source_root. -- note: rel_source_dirs and rel_include_dirs are relative to global source_root.
local function setup_static_lib_package (package_name, rel_source_dirs, extern_libs, extra_params) function setup_static_lib_package (package_name, rel_source_dirs, extern_libs, extra_params)
package_create(package_name, "lib") package_create(package_name, "lib")
package_add_contents(source_root, rel_source_dirs, {}, extra_params) package_add_contents(source_root, rel_source_dirs, {}, extra_params)
@ -456,7 +457,7 @@ end
-- setup a typical Atlas component package -- setup a typical Atlas component package
-- extra_params: as in package_add_contents; also zero or more of the following: -- extra_params: as in package_add_contents; also zero or more of the following:
-- * pch: (any type) set stdafx.h and .cpp as PCH -- * pch: (any type) set stdafx.h and .cpp as PCH
local function setup_atlas_package(package_name, target_type, rel_source_dirs, rel_include_dirs, extern_libs, extra_params) function setup_atlas_package(package_name, target_type, rel_source_dirs, rel_include_dirs, extern_libs, extra_params)
local source_root = "../../../source/tools/atlas/" .. package_name .. "/" local source_root = "../../../source/tools/atlas/" .. package_name .. "/"
package_create(package_name, target_type) package_create(package_name, target_type)
@ -574,7 +575,7 @@ end
-- Atlas 'frontend' tool-launching packages -- Atlas 'frontend' tool-launching packages
local function setup_atlas_frontend_package (package_name) function setup_atlas_frontend_package (package_name)
package_create(package_name, "winexe") package_create(package_name, "winexe")
@ -610,6 +611,57 @@ function setup_atlas_frontends()
end end
--------------------------------------------------------------------------------
-- collada
--------------------------------------------------------------------------------
function setup_collada_package(package_name, target_type, rel_source_dirs, rel_include_dirs, extern_libs, extra_params)
package_create(package_name, target_type)
-- Don't add the default 'sourceroot/pch/projectname' for finding PCH files
extra_params["no_default_pch"] = 1
package_add_contents(source_root, rel_source_dirs, rel_include_dirs, extra_params)
package_add_extern_libs(extern_libs)
if extra_params["pch"] then
package_setup_pch(nil, "precompiled.h", "precompiled.cpp");
end
-- Platform Specifics
if OS == "windows" then
-- required to use WinMain() on Windows, otherwise will default to main()
tinsert(package.buildflags, "no-main")
if extra_params["extra_links"] then
listconcat(package.links, extra_params["extra_links"])
end
else -- Non-Windows, = Unix
tinsert(package.buildoptions, "-rdynamic")
tinsert(package.linkoptions, "-rdynamic")
end
end
-- build all Collada component packages
function setup_collada_packages()
setup_collada_package("Collada", "dll",
{ -- src
"collada"
},{ -- include
},{ -- extern_libs
"fcollada",
},{ -- extra_params
pch = 1,
})
end
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- tests -- tests
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -736,6 +788,10 @@ if options["atlas"] then
setup_atlas_frontends() setup_atlas_frontends()
end end
if options["collada"] then
setup_collada_packages()
end
if not options["without-tests"] then if not options["without-tests"] then
setup_tests() setup_tests()
end end

View File

@ -0,0 +1,336 @@
#include "precompiled.h"
#include "Converter.h"
#include "FCollada.h"
#include "FCDocument/FCDocument.h"
#include "FCDocument/FCDSceneNode.h"
#include "FCDocument/FCDGeometry.h"
#include "FCDocument/FCDGeometryMesh.h"
#include "FCDocument/FCDGeometryPolygons.h"
#include "FCDocument/FCDGeometrySource.h"
#include <cassert>
#include <vector>
/** Throws a ColladaException unless the value is true. */
#define REQUIRE(value, message) require_(__LINE__, value, "Failed assertion", message)
/** Throws a ColladaException unless the status is successful. */
#define REQUIRE_SUCCESS(status) require_(__LINE__, status)
void require_(int line, bool value, const char* type, const char* message)
{
if (value) return;
char linestr[16];
sprintf(linestr, "%d", line);
throw ColladaException(std::string(type) + " (line " + linestr + "): " + message);
}
void require_(int line, const FUStatus& status)
{
require_(line, status, "FCollada error", status.GetErrorString());
}
/** Outputs a structure, using sizeof to get the size. */
template<typename T> void write(OutputFn output, const T& data)
{
output((char*)&data, sizeof(T));
}
struct VertexBlend
{
uint8 bones[4];
float weights[4];
};
struct BoneTransform
{
float translation[3];
float orientation[4];
};
/** 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;
}
class Converter
{
public:
/**
* Converts a COLLADA XML document into the PMD format.
*
* @param input XML document to parse
* @param output callback for writing the PMD data; called lots of times
* with small strings
* @param xmlErrors output - errors reported by the XML parser
* @throws ColladaException on failure
*/
static void ColladaToPMD(const char* input, OutputFn output, std::string& xmlErrors)
{
FUStatus ret;
// Grab all the error output from libxml2. Be careful to never use
// libxml2 outside this function without having first set/reset the
// errorfunc (since xmlErrors won't be valid any more).
xmlSetGenericErrorFunc(&xmlErrors, &errorHandler);
std::auto_ptr<FCDocument> doc (FCollada::NewTopDocument());
REQUIRE_SUCCESS(doc->LoadFromText("", input));
FCDSceneNode* root = doc->GetVisualSceneRoot();
// Find the instance to convert
FCDEntityInstance* instance;
FMMatrix44 transform;
if (! FindSingleInstance(root, instance, transform))
throw ColladaException("Couldn't find object to convert");
assert(instance);
Log(LOG_INFO, "Converting '%s'", instance->GetEntity()->GetName().c_str());
if (instance->GetEntity()->GetType() == FCDEntity::GEOMETRY)
{
Log(LOG_INFO, "Found static geometry");
FCDGeometry* geom = (FCDGeometry*)instance->GetEntity();
REQUIRE(geom->IsMesh(), "geometry is mesh");
FCDGeometryMesh* mesh = geom->GetMesh();
REQUIRE(mesh->IsTriangles(), "mesh is made of triangles");
REQUIRE(mesh->GetPolygonsCount() == 1, "mesh has single set of polygons");
FCDGeometryPolygons* polys = mesh->GetPolygons(0);
size_t vertices = polys->GetFaceVertexCount();
FloatList position, normal, texcoord;
DeindexInput(polys, FUDaeGeometryInput::POSITION, position, 3);
DeindexInput(polys, FUDaeGeometryInput::NORMAL, normal, 3);
if (polys->FindInput(FUDaeGeometryInput::TEXCOORD))
{
DeindexInput(polys, FUDaeGeometryInput::TEXCOORD, texcoord, 2);
}
else
{
// Accept untextured models
texcoord.resize(vertices*2, 0.f);
}
assert(position.size() == vertices*3);
assert(normal.size() == vertices*3);
assert(texcoord.size() == vertices*2);
TransformVertices(position, normal, transform);
// TODO: optimise at least enough to merge identical vertices
WritePMD(output, vertices, 0, &position[0], &normal[0], &texcoord[0], NULL, NULL);
}
else
{
throw ColladaException("Unrecognised object type");
}
}
/**
* Writes the model data in the PMD format.
*/
static void WritePMD(OutputFn output, size_t vertexCount, size_t boneCount,
float* position, float* normal, float* texcoord,
VertexBlend* boneWeights, BoneTransform* boneTransforms)
{
static const VertexBlend noBlend = { 0xFF, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0 };
assert(position);
assert(normal);
assert(texcoord);
if (boneCount) assert(boneWeights && boneTransforms);
output("PSMD", 4); // magic number
write<uint32>(output, 2); // version number
write<uint32>(output, (uint32)(
4 + 13*4*vertexCount + // vertices
4 + 6*vertexCount/3 + // faces
4 + 7*4*boneCount + // bones
4 + 0 // props
)); // data size
// Vertex data
write<uint32>(output, (uint32)vertexCount);
for (size_t i = 0; i < vertexCount; ++i)
{
output((char*)&position[i*3], 12);
output((char*)&normal [i*3], 12);
output((char*)&texcoord[i*2], 8);
if (boneWeights)
write(output, boneWeights[i]);
else
write(output, noBlend);
}
// Face data
// (TODO: this is really very rubbish and inefficient)
write<uint32>(output, (uint32)vertexCount/3);
for (uint16 i = 0; i < vertexCount/3; ++i)
{
uint16 vertexCount[3] = { i*3, i*3+1, i*3+2 };
write(output, vertexCount);
}
// Bones data
write<uint32>(output, (uint32)boneCount);
for (size_t i = 0; i < boneCount; ++i)
{
write(output, boneTransforms[i]);
}
// Prop points data
write<uint32>(output, 0);
}
/**
* Converts from value-array plus indexes-into-array-per-vertex, into
* values-per-vertex (because that's what PMD wants).
*/
static void DeindexInput(FCDGeometryPolygons* polys, FUDaeGeometryInput::Semantic semantic, FloatList& out, size_t outStride)
{
FCDGeometryPolygonsInput* input = polys->FindInput(semantic);
UInt32List* indices = polys->FindIndices(input);
REQUIRE(input && indices, "has expected polygon input");
FCDGeometrySource* source = input->GetSource();
FloatList& data = source->GetSourceData();
size_t stride = source->GetSourceStride();
for (size_t i = 0; i < indices->size(); ++i)
for (size_t j = 0; j < outStride; ++j)
out.push_back(data[(*indices)[i]*stride + j]);
}
/**
* Applies world-space transform to vertex data, and flips into other-handed
* coordinate space.
*/
static void TransformVertices(FloatList& position, FloatList& normal, const FMMatrix44& transform)
{
for (size_t i = 0; i < position.size(); i += 3)
{
FMVector3 pos (position[i], position[i+1], position[i+2]);
FMVector3 norm (normal[i], normal[i+1], normal[i+2]);
// Apply the scene-node transforms
pos = transform.TransformCoordinate(pos);
norm = transform.TransformVector(norm).Normalize();
// Copy back to array, while switching the coordinate system around
position[i+0] = pos.x;
position[i+1] = pos.z;
position[i+2] = pos.y;
normal[i+0] = norm.x;
normal[i+1] = norm.z;
normal[i+2] = norm.y;
}
}
//////////////////////////////////////////////////////////////////////////
struct FoundInstance
{
FCDEntityInstance* instance;
FMMatrix44 transform;
};
/**
* Tries to find a single suitable entity instance in the scene. Fails if there
* are none, or if there are too many and it's not clear which one should
* be converted.
*
* @param node root scene node to search under
* @param instance output - the found entity instance (if any)
* @param transform - the world-space transform of the found entity
*
* @return true if one was found
*/
static bool FindSingleInstance(FCDSceneNode* node, FCDEntityInstance*& instance, FMMatrix44& transform)
{
std::vector<FoundInstance> instances;
FindInstances(node, instances, FMMatrix44::Identity, true);
if (instances.size() > 1)
{
Log(LOG_ERROR, "Found too many export-marked objects");
return false;
}
if (instances.empty())
{
FindInstances(node, instances, FMMatrix44::Identity, false);
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;
}
/**
* 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;
}
FoundInstance f;
f.transform = transform * node->ToMatrix();
f.instance = node->GetInstance(i);
instances.push_back(f);
}
}
};
// The above stuff is just in a class since I don't like having to bother
// with forward declarations of functions - but provide the plain function
// interface here:
void ColladaToPMD(const char* input, OutputFn output, std::string& xmlErrors)
{
Converter::ColladaToPMD(input, output, xmlErrors);
}

View File

@ -0,0 +1,18 @@
#ifndef CONVERTER_H__
#define CONVERTER_H__
#include <exception>
#include <string>
class ColladaException : public std::exception
{
public:
ColladaException(const std::string& msg)
: std::exception(msg.c_str())
{
}
};
void ColladaToPMD(const char* input, OutputFn output, std::string& xmlErrors);
#endif // CONVERTER_H__

58
source/collada/DLL.cpp Normal file
View File

@ -0,0 +1,58 @@
#include "precompiled.h"
#include "Converter.h"
#include <cstdarg>
void default_logger(int severity, const char* message)
{
fprintf(stderr, "[%d] %s\n", severity, message);
}
static LogFn g_Logger = &default_logger;
void set_logger(LogFn logger)
{
g_Logger = logger;
}
void Log(int severity, 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);
g_Logger(severity, buffer);
}
int convert_dae_to_pmd(const char* dae, OutputFn pmd_writer)
{
Log(LOG_INFO, "Starting conversion");
std::string xmlErrors;
try
{
ColladaToPMD(dae, pmd_writer, xmlErrors);
}
catch (ColladaException e)
{
if (! xmlErrors.empty())
Log(LOG_ERROR, "%s", xmlErrors.c_str());
Log(LOG_ERROR, "%s", e.what());
return -2;
}
if (! xmlErrors.empty())
{
Log(LOG_ERROR, "%s", xmlErrors.c_str());
return -1;
}
return 0;
}

33
source/collada/DLL.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef COLLADA_DLL_H__
#define COLLADA_DLL_H__
#ifdef __cplusplus
extern "C"
{
#endif
#ifdef _WIN32
# ifdef COLLADA_DLL
# define EXPORT extern __declspec(dllexport)
# else
# define EXPORT extern __declspec(dllimport)
# endif
#else
# define EXPORT extern
#endif
#define LOG_INFO 0
#define LOG_WARNING 1
#define LOG_ERROR 2
typedef void (*LogFn) (int severity, const char* text);
typedef void (*OutputFn) (const char* data, unsigned int length);
EXPORT void set_logger(LogFn logger);
EXPORT int convert_dae_to_pmd(const char* dae, OutputFn pmd_writer);
#ifdef __cplusplus
};
#endif
#endif /* COLLADA_DLL_H__ */

View File

@ -0,0 +1,507 @@
#include "precompiled.h"
#ifdef _MSC_VER
# pragma warning(disable: 4244 4305 4127 4701)
#endif
/**** Decompose.c ****/
/* Ken Shoemake, 1993 */
#include <math.h>
#include "Decompose.h"
/******* Matrix Preliminaries *******/
/** Fill out 3x3 matrix to 4x4 **/
#define mat_pad(A) (A[W][X]=A[X][W]=A[W][Y]=A[Y][W]=A[W][Z]=A[Z][W]=0,A[W][W]=1)
/** Copy nxn matrix A to C using "gets" for assignment **/
#define mat_copy(C,gets,A,n) {int i,j; for(i=0;i<n;i++) for(j=0;j<n;j++)\
C[i][j] gets (A[i][j]);}
/** Copy transpose of nxn matrix A to C using "gets" for assignment **/
#define mat_tpose(AT,gets,A,n) {int i,j; for(i=0;i<n;i++) for(j=0;j<n;j++)\
AT[i][j] gets (A[j][i]);}
/** Assign nxn matrix C the element-wise combination of A and B using "op" **/
#define mat_binop(C,gets,A,op,B,n) {int i,j; for(i=0;i<n;i++) for(j=0;j<n;j++)\
C[i][j] gets (A[i][j]) op (B[i][j]);}
/** Multiply the upper left 3x3 parts of A and B to get AB **/
void mat_mult(HMatrix A, HMatrix B, HMatrix AB)
{
int i, j;
for (i=0; i<3; i++) for (j=0; j<3; j++)
AB[i][j] = A[i][0]*B[0][j] + A[i][1]*B[1][j] + A[i][2]*B[2][j];
}
/** Return dot product of length 3 vectors va and vb **/
float vdot(float *va, float *vb)
{
return (va[0]*vb[0] + va[1]*vb[1] + va[2]*vb[2]);
}
/** Set v to cross product of length 3 vectors va and vb **/
void vcross(float *va, float *vb, float *v)
{
v[0] = va[1]*vb[2] - va[2]*vb[1];
v[1] = va[2]*vb[0] - va[0]*vb[2];
v[2] = va[0]*vb[1] - va[1]*vb[0];
}
/** Set MadjT to transpose of inverse of M times determinant of M **/
void adjoint_transpose(HMatrix M, HMatrix MadjT)
{
vcross(M[1], M[2], MadjT[0]);
vcross(M[2], M[0], MadjT[1]);
vcross(M[0], M[1], MadjT[2]);
}
/******* Quaternion Preliminaries *******/
/* Construct a (possibly non-unit) quaternion from real components. */
Quat Qt_(float x, float y, float z, float w)
{
Quat qq;
qq.x = x; qq.y = y; qq.z = z; qq.w = w;
return (qq);
}
/* Return conjugate of quaternion. */
Quat Qt_Conj(Quat q)
{
Quat qq;
qq.x = -q.x; qq.y = -q.y; qq.z = -q.z; qq.w = q.w;
return (qq);
}
/* Return quaternion product qL * qR. Note: order is important!
* To combine rotations, use the product Mul(qSecond, qFirst),
* which gives the effect of rotating by qFirst then qSecond. */
Quat Qt_Mul(Quat qL, Quat qR)
{
Quat qq;
qq.w = qL.w*qR.w - qL.x*qR.x - qL.y*qR.y - qL.z*qR.z;
qq.x = qL.w*qR.x + qL.x*qR.w + qL.y*qR.z - qL.z*qR.y;
qq.y = qL.w*qR.y + qL.y*qR.w + qL.z*qR.x - qL.x*qR.z;
qq.z = qL.w*qR.z + qL.z*qR.w + qL.x*qR.y - qL.y*qR.x;
return (qq);
}
/* Return product of quaternion q by scalar w. */
Quat Qt_Scale(Quat q, float w)
{
Quat qq;
qq.w = q.w*w; qq.x = q.x*w; qq.y = q.y*w; qq.z = q.z*w;
return (qq);
}
/* Construct a unit quaternion from rotation matrix. Assumes matrix is
* used to multiply column vector on the left: vnew = mat vold. Works
* correctly for right-handed coordinate system and right-handed rotations.
* Translation and perspective components ignored. */
Quat Qt_FromMatrix(HMatrix mat)
{
/* This algorithm avoids near-zero divides by looking for a large component
* - first w, then x, y, or z. When the trace is greater than zero,
* |w| is greater than 1/2, which is as small as a largest component can be.
* Otherwise, the largest diagonal entry corresponds to the largest of |x|,
* |y|, or |z|, one of which must be larger than |w|, and at least 1/2. */
Quat qu;
register double tr, s;
tr = mat[X][X] + mat[Y][Y]+ mat[Z][Z];
if (tr >= 0.0) {
s = sqrt(tr + mat[W][W]);
qu.w = s*0.5;
s = 0.5 / s;
qu.x = (mat[Z][Y] - mat[Y][Z]) * s;
qu.y = (mat[X][Z] - mat[Z][X]) * s;
qu.z = (mat[Y][X] - mat[X][Y]) * s;
} else {
int h = X;
if (mat[Y][Y] > mat[X][X]) h = Y;
if (mat[Z][Z] > mat[h][h]) h = Z;
switch (h) {
#define caseMacro(i,j,k,I,J,K) \
case I:\
s = sqrt( (mat[I][I] - (mat[J][J]+mat[K][K])) + mat[W][W] );\
qu.i = s*0.5;\
s = 0.5 / s;\
qu.j = (mat[I][J] + mat[J][I]) * s;\
qu.k = (mat[K][I] + mat[I][K]) * s;\
qu.w = (mat[K][J] - mat[J][K]) * s;\
break
caseMacro(x,y,z,X,Y,Z);
caseMacro(y,z,x,Y,Z,X);
caseMacro(z,x,y,Z,X,Y);
}
}
if (mat[W][W] != 1.0) qu = Qt_Scale(qu, 1/sqrt(mat[W][W]));
return (qu);
}
/******* Decomp Auxiliaries *******/
static HMatrix mat_id = {{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}};
/** Compute either the 1 or infinity norm of M, depending on tpose **/
float mat_norm(HMatrix M, int tpose)
{
int i;
float sum, max;
max = 0.0;
for (i=0; i<3; i++) {
if (tpose) sum = fabs(M[0][i])+fabs(M[1][i])+fabs(M[2][i]);
else sum = fabs(M[i][0])+fabs(M[i][1])+fabs(M[i][2]);
if (max<sum) max = sum;
}
return max;
}
float norm_inf(HMatrix M) {return mat_norm(M, 0);}
float norm_one(HMatrix M) {return mat_norm(M, 1);}
/** Return index of column of M containing maximum abs entry, or -1 if M=0 **/
int find_max_col(HMatrix M)
{
float abs, max;
int i, j, col;
max = 0.0; col = -1;
for (i=0; i<3; i++) for (j=0; j<3; j++) {
abs = M[i][j]; if (abs<0.0) abs = -abs;
if (abs>max) {max = abs; col = j;}
}
return col;
}
/** Setup u for Household reflection to zero all v components but first **/
void make_reflector(float *v, float *u)
{
float s = sqrt(vdot(v, v));
u[0] = v[0]; u[1] = v[1];
u[2] = v[2] + ((v[2]<0.0) ? -s : s);
s = sqrt(2.0/vdot(u, u));
u[0] = u[0]*s; u[1] = u[1]*s; u[2] = u[2]*s;
}
/** Apply Householder reflection represented by u to column vectors of M **/
void reflect_cols(HMatrix M, float *u)
{
int i, j;
for (i=0; i<3; i++) {
float s = u[0]*M[0][i] + u[1]*M[1][i] + u[2]*M[2][i];
for (j=0; j<3; j++) M[j][i] -= u[j]*s;
}
}
/** Apply Householder reflection represented by u to row vectors of M **/
void reflect_rows(HMatrix M, float *u)
{
int i, j;
for (i=0; i<3; i++) {
float s = vdot(u, M[i]);
for (j=0; j<3; j++) M[i][j] -= u[j]*s;
}
}
/** Find orthogonal factor Q of rank 1 (or less) M **/
void do_rank1(HMatrix M, HMatrix Q)
{
float v1[3], v2[3], s;
int col;
mat_copy(Q,=,mat_id,4);
/* If rank(M) is 1, we should find a non-zero column in M */
col = find_max_col(M);
if (col<0) return; /* Rank is 0 */
v1[0] = M[0][col]; v1[1] = M[1][col]; v1[2] = M[2][col];
make_reflector(v1, v1); reflect_cols(M, v1);
v2[0] = M[2][0]; v2[1] = M[2][1]; v2[2] = M[2][2];
make_reflector(v2, v2); reflect_rows(M, v2);
s = M[2][2];
if (s<0.0) Q[2][2] = -1.0;
reflect_cols(Q, v1); reflect_rows(Q, v2);
}
/** Find orthogonal factor Q of rank 2 (or less) M using adjoint transpose **/
void do_rank2(HMatrix M, HMatrix MadjT, HMatrix Q)
{
float v1[3], v2[3];
float w, x, y, z, c, s, d;
int col;
/* If rank(M) is 2, we should find a non-zero column in MadjT */
col = find_max_col(MadjT);
if (col<0) {do_rank1(M, Q); return;} /* Rank<2 */
v1[0] = MadjT[0][col]; v1[1] = MadjT[1][col]; v1[2] = MadjT[2][col];
make_reflector(v1, v1); reflect_cols(M, v1);
vcross(M[0], M[1], v2);
make_reflector(v2, v2); reflect_rows(M, v2);
w = M[0][0]; x = M[0][1]; y = M[1][0]; z = M[1][1];
if (w*z>x*y) {
c = z+w; s = y-x; d = sqrt(c*c+s*s); c = c/d; s = s/d;
Q[0][0] = Q[1][1] = c; Q[0][1] = -(Q[1][0] = s);
} else {
c = z-w; s = y+x; d = sqrt(c*c+s*s); c = c/d; s = s/d;
Q[0][0] = -(Q[1][1] = c); Q[0][1] = Q[1][0] = s;
}
Q[0][2] = Q[2][0] = Q[1][2] = Q[2][1] = 0.0; Q[2][2] = 1.0;
reflect_cols(Q, v1); reflect_rows(Q, v2);
}
/******* Polar Decomposition *******/
/* Polar Decomposition of 3x3 matrix in 4x4,
* M = QS. See Nicholas Higham and Robert S. Schreiber,
* Fast Polar Decomposition of An Arbitrary Matrix,
* Technical Report 88-942, October 1988,
* Department of Computer Science, Cornell University.
*/
float polar_decomp(HMatrix M, HMatrix Q, HMatrix S)
{
#define TOL 1.0e-6
HMatrix Mk, MadjTk, Ek;
float det, M_one, M_inf, MadjT_one, MadjT_inf, E_one, gamma, g1, g2;
int i, j;
mat_tpose(Mk,=,M,3);
M_one = norm_one(Mk); M_inf = norm_inf(Mk);
do {
adjoint_transpose(Mk, MadjTk);
det = vdot(Mk[0], MadjTk[0]);
if (det==0.0) {do_rank2(Mk, MadjTk, Mk); break;}
MadjT_one = norm_one(MadjTk); MadjT_inf = norm_inf(MadjTk);
gamma = sqrt(sqrt((MadjT_one*MadjT_inf)/(M_one*M_inf))/fabs(det));
g1 = gamma*0.5;
g2 = 0.5/(gamma*det);
mat_copy(Ek,=,Mk,3);
mat_binop(Mk,=,g1*Mk,+,g2*MadjTk,3);
mat_copy(Ek,-=,Mk,3);
E_one = norm_one(Ek);
M_one = norm_one(Mk); M_inf = norm_inf(Mk);
} while (E_one>(M_one*TOL));
mat_tpose(Q,=,Mk,3); mat_pad(Q);
mat_mult(Mk, M, S); mat_pad(S);
for (i=0; i<3; i++) for (j=i; j<3; j++)
S[i][j] = S[j][i] = 0.5*(S[i][j]+S[j][i]);
return (det);
}
/******* Spectral Decomposition *******/
/* Compute the spectral decomposition of symmetric positive semi-definite S.
* Returns rotation in U and scale factors in result, so that if K is a diagonal
* matrix of the scale factors, then S = U K (U transpose). Uses Jacobi method.
* See Gene H. Golub and Charles F. Van Loan. Matrix Computations. Hopkins 1983.
*/
HVect spect_decomp(HMatrix S, HMatrix U)
{
HVect kv;
double Diag[3],OffD[3]; /* OffD is off-diag (by omitted index) */
double g,h,fabsh,fabsOffDi,t,theta,c,s,tau,ta,OffDq,a,b;
static char nxt[] = {Y,Z,X};
int sweep, i, j;
mat_copy(U,=,mat_id,4);
Diag[X] = S[X][X]; Diag[Y] = S[Y][Y]; Diag[Z] = S[Z][Z];
OffD[X] = S[Y][Z]; OffD[Y] = S[Z][X]; OffD[Z] = S[X][Y];
for (sweep=20; sweep>0; sweep--) {
float sm = fabs(OffD[X])+fabs(OffD[Y])+fabs(OffD[Z]);
if (sm==0.0) break;
for (i=Z; i>=X; i--) {
int p = nxt[i]; int q = nxt[p];
fabsOffDi = fabs(OffD[i]);
g = 100.0*fabsOffDi;
if (fabsOffDi>0.0) {
h = Diag[q] - Diag[p];
fabsh = fabs(h);
if (fabsh+g==fabsh) {
t = OffD[i]/h;
} else {
theta = 0.5*h/OffD[i];
t = 1.0/(fabs(theta)+sqrt(theta*theta+1.0));
if (theta<0.0) t = -t;
}
c = 1.0/sqrt(t*t+1.0); s = t*c;
tau = s/(c+1.0);
ta = t*OffD[i]; OffD[i] = 0.0;
Diag[p] -= ta; Diag[q] += ta;
OffDq = OffD[q];
OffD[q] -= s*(OffD[p] + tau*OffD[q]);
OffD[p] += s*(OffDq - tau*OffD[p]);
for (j=Z; j>=X; j--) {
a = U[j][p]; b = U[j][q];
U[j][p] -= s*(b + tau*a);
U[j][q] += s*(a - tau*b);
}
}
}
}
kv.x = Diag[X]; kv.y = Diag[Y]; kv.z = Diag[Z]; kv.w = 1.0;
return (kv);
}
/******* Spectral Axis Adjustment *******/
/* Given a unit quaternion, q, and a scale vector, k, find a unit quaternion, p,
* which permutes the axes and turns freely in the plane of duplicate scale
* factors, such that q p has the largest possible w component, i.e. the
* smallest possible angle. Permutes k's components to go with q p instead of q.
* See Ken Shoemake and Tom Duff. Matrix Animation and Polar Decomposition.
* Proceedings of Graphics Interface 1992. Details on p. 262-263.
*/
Quat snuggle(Quat q, HVect *k)
{
#define SQRTHALF (0.7071067811865475244)
#define sgn(n,v) ((n)?-(v):(v))
#define swap(a,i,j) {a[3]=a[i]; a[i]=a[j]; a[j]=a[3];}
#define cycle(a,p) if (p) {a[3]=a[0]; a[0]=a[1]; a[1]=a[2]; a[2]=a[3];}\
else {a[3]=a[2]; a[2]=a[1]; a[1]=a[0]; a[0]=a[3];}
Quat p;
float ka[4];
int i, turn = -1;
ka[X] = k->x; ka[Y] = k->y; ka[Z] = k->z;
if (ka[X]==ka[Y]) {if (ka[X]==ka[Z]) turn = W; else turn = Z;}
else {if (ka[X]==ka[Z]) turn = Y; else if (ka[Y]==ka[Z]) turn = X;}
if (turn>=0) {
Quat qtoz, qp;
unsigned neg[3], win;
double mag[3], t;
static Quat qxtoz = {0,SQRTHALF,0,SQRTHALF};
static Quat qytoz = {SQRTHALF,0,0,SQRTHALF};
static Quat qppmm = { 0.5, 0.5,-0.5,-0.5};
static Quat qpppp = { 0.5, 0.5, 0.5, 0.5};
static Quat qmpmm = {-0.5, 0.5,-0.5,-0.5};
static Quat qpppm = { 0.5, 0.5, 0.5,-0.5};
static Quat q0001 = { 0.0, 0.0, 0.0, 1.0};
static Quat q1000 = { 1.0, 0.0, 0.0, 0.0};
switch (turn) {
default: return (Qt_Conj(q));
case X: q = Qt_Mul(q, qtoz = qxtoz); swap(ka,X,Z) break;
case Y: q = Qt_Mul(q, qtoz = qytoz); swap(ka,Y,Z) break;
case Z: qtoz = q0001; break;
}
q = Qt_Conj(q);
mag[0] = (double)q.z*q.z+(double)q.w*q.w-0.5;
mag[1] = (double)q.x*q.z-(double)q.y*q.w;
mag[2] = (double)q.y*q.z+(double)q.x*q.w;
for (i=0; i<3; i++) if (neg[i] = (mag[i]<0.0)) mag[i] = -mag[i];
if (mag[0]>mag[1]) {if (mag[0]>mag[2]) win = 0; else win = 2;}
else {if (mag[1]>mag[2]) win = 1; else win = 2;}
switch (win) {
case 0: if (neg[0]) p = q1000; else p = q0001; break;
case 1: if (neg[1]) p = qppmm; else p = qpppp; cycle(ka,0) break;
case 2: if (neg[2]) p = qmpmm; else p = qpppm; cycle(ka,1) break;
}
qp = Qt_Mul(q, p);
t = sqrt(mag[win]+0.5);
p = Qt_Mul(p, Qt_(0.0,0.0,-qp.z/t,qp.w/t));
p = Qt_Mul(qtoz, Qt_Conj(p));
} else {
float qa[4], pa[4];
unsigned lo, hi, neg[4], par = 0;
double all, big, two;
qa[0] = q.x; qa[1] = q.y; qa[2] = q.z; qa[3] = q.w;
for (i=0; i<4; i++) {
pa[i] = 0.0;
if (neg[i] = (qa[i]<0.0)) qa[i] = -qa[i];
par ^= neg[i];
}
/* Find two largest components, indices in hi and lo */
if (qa[0]>qa[1]) lo = 0; else lo = 1;
if (qa[2]>qa[3]) hi = 2; else hi = 3;
if (qa[lo]>qa[hi]) {
if (qa[lo^1]>qa[hi]) {hi = lo; lo ^= 1;}
else {hi ^= lo; lo ^= hi; hi ^= lo;}
} else {if (qa[hi^1]>qa[lo]) lo = hi^1;}
all = (qa[0]+qa[1]+qa[2]+qa[3])*0.5;
two = (qa[hi]+qa[lo])*SQRTHALF;
big = qa[hi];
if (all>two) {
if (all>big) {/*all*/
{int i; for (i=0; i<4; i++) pa[i] = sgn(neg[i], 0.5);}
cycle(ka,par)
} else {/*big*/ pa[hi] = sgn(neg[hi],1.0);}
} else {
if (two>big) {/*two*/
pa[hi] = sgn(neg[hi],SQRTHALF); pa[lo] = sgn(neg[lo], SQRTHALF);
if (lo>hi) {hi ^= lo; lo ^= hi; hi ^= lo;}
if (hi==W) {hi = "\001\002\000"[lo]; lo = 3-hi-lo;}
swap(ka,hi,lo)
} else {/*big*/ pa[hi] = sgn(neg[hi],1.0);}
}
p.x = -pa[0]; p.y = -pa[1]; p.z = -pa[2]; p.w = pa[3];
}
k->x = ka[X]; k->y = ka[Y]; k->z = ka[Z];
return (p);
}
/******* Decompose Affine Matrix *******/
/* Decompose 4x4 affine matrix A as TFRUK(U transpose), where t contains the
* translation components, q contains the rotation R, u contains U, k contains
* scale factors, and f contains the sign of the determinant.
* Assumes A transforms column vectors in right-handed coordinates.
* See Ken Shoemake and Tom Duff. Matrix Animation and Polar Decomposition.
* Proceedings of Graphics Interface 1992.
*/
void decomp_affine(HMatrix A, AffineParts *parts)
{
HMatrix Q, S, U;
Quat p;
float det;
parts->t = Qt_(A[X][W], A[Y][W], A[Z][W], 0);
det = polar_decomp(A, Q, S);
if (det<0.0) {
mat_copy(Q,=,-Q,3);
parts->f = -1;
} else parts->f = 1;
parts->q = Qt_FromMatrix(Q);
parts->k = spect_decomp(S, U);
parts->u = Qt_FromMatrix(U);
p = snuggle(parts->u, &parts->k);
parts->u = Qt_Mul(parts->u, p);
}
/******* Invert Affine Decomposition *******/
/* Compute inverse of affine decomposition.
*/
void invert_affine(AffineParts *parts, AffineParts *inverse)
{
Quat t, p;
inverse->f = parts->f;
inverse->q = Qt_Conj(parts->q);
inverse->u = Qt_Mul(parts->q, parts->u);
inverse->k.x = (parts->k.x==0.0) ? 0.0 : 1.0/parts->k.x;
inverse->k.y = (parts->k.y==0.0) ? 0.0 : 1.0/parts->k.y;
inverse->k.z = (parts->k.z==0.0) ? 0.0 : 1.0/parts->k.z;
inverse->k.w = parts->k.w;
t = Qt_(-parts->t.x, -parts->t.y, -parts->t.z, 0);
t = Qt_Mul(Qt_Conj(inverse->u), Qt_Mul(t, inverse->u));
t = Qt_(inverse->k.x*t.x, inverse->k.y*t.y, inverse->k.z*t.z, 0);
p = Qt_Mul(inverse->q, inverse->u);
t = Qt_Mul(p, Qt_Mul(t, Qt_Conj(p)));
inverse->t = (inverse->f>0.0) ? t : Qt_(-t.x, -t.y, -t.z, 0);
}

View File

@ -0,0 +1,20 @@
/**** Decompose.h - Basic declarations ****/
#ifndef _H_Decompose
#define _H_Decompose
typedef struct {float x, y, z, w;} Quat; /* Quaternion */
enum QuatPart {X, Y, Z, W};
typedef Quat HVect; /* Homogeneous 3D vector */
typedef float HMatrix[4][4]; /* Right-handed, for column vectors */
typedef struct {
HVect t; /* Translation components */
Quat q; /* Essential rotation */
Quat u; /* Stretch rotation */
HVect k; /* Stretch factors */
float f; /* Sign of determinant */
} AffineParts;
float polar_decomp(HMatrix M, HMatrix Q, HMatrix S);
HVect spect_decomp(HMatrix S, HMatrix U);
Quat snuggle(Quat q, HVect *k);
void decomp_affine(HMatrix A, AffineParts *parts);
void invert_affine(AffineParts *parts, AffineParts *inverse);
#endif

View File

@ -0,0 +1 @@
#include "precompiled.h"

View File

@ -0,0 +1,18 @@
#define COLLADA_DLL
#include "DLL.h"
extern void Log(int severity, const char* fmt, ...);
#ifdef _WIN32
# define WIN32
# define WIN32_LEAN_AND_MEAN
# pragma warning(disable: 4996)
#endif
#include "FCollada.h"
#include "FCDocument/FCDocument.h"
#include "FCDocument/FCDSceneNode.h"
#include "FCDocument/FCDGeometry.h"
#include "FCDocument/FCDGeometryMesh.h"
#include "FCDocument/FCDGeometryPolygons.h"
#include "FCDocument/FCDGeometrySource.h"