quakeforge/libs/scene/test/test-hierarchy.c
Bill Currie ba3879c6e0 [scene] Make transforms owned by the scene
This actually has at least two benefits: the transform id is managed by
the scene and thus does not need separate management by the Ruamoko
wrapper functions, and better memory handling of the transform objects.
Another benefit that isn't realized yet is that this is a step towards
breaking the renderers free of quake and quakeworld: although the
clients don't actually use the scene yet, it will be a good place to
store the rendering information (functions to run, etc).
2022-02-14 20:01:36 +09:00

717 lines
25 KiB
C

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "QF/scene/hierarchy.h"
#include "QF/scene/scene.h"
#include "QF/scene/transform.h"
scene_t *scene;
// NOTE: these are the columns of the matrix! (not that it matters for a
// symmetrical matrix, but...)
mat4f_t identity = {
{ 1, 0, 0, 0 },
{ 0, 1, 0, 0 },
{ 0, 0, 1, 0 },
{ 0, 0, 0, 1 },
};
vec4f_t one = { 1, 1, 1, 1 };
static int
vec4_equal (vec4f_t a, vec4f_t b)
{
vec4i_t res = a != b;
return !(res[0] || res[1] || res[2] || res[3]);
}
static int
mat4_equal (const mat4f_t a, const mat4f_t b)
{
vec4i_t res = {};
for (int i = 0; i < 4; i++) {
res |= a[i] != b[i];
}
return !(res[0] || res[1] || res[2] || res[3]);
}
static int
check_hierarchy_size (hierarchy_t *h, uint32_t size)
{
if (h->transform.size != size
|| h->entity.size != size
|| h->childCount.size != size
|| h->childIndex.size != size
|| h->parentIndex.size != size
|| h->name.size != size
|| h->tag.size != size
|| h->modified.size != size
|| h->localMatrix.size != size
|| h->localInverse.size != size
|| h->worldMatrix.size != size
|| h->worldInverse.size != size
|| h->localRotation.size != size
|| h->localScale.size != size
|| h->worldRotation.size != size
|| h->worldScale.size != size) {
printf ("hierarchy does not have exactly %u"
" transform or array sizes are inconsistent\n", size);
return 0;
}
for (size_t i = 0; i < h->transform.size; i++) {
if (h->transform.a[i]->hierarchy != h) {
printf ("transform %zd (%s) does not point to hierarchy\n",
i, h->name.a[i]);
}
}
return 1;
}
static void
dump_hierarchy (hierarchy_t *h)
{
for (size_t i = 0; i < h->transform.size; i++) {
printf ("%2zd: %5s %2u %2u %2u %2u\n", i, h->name.a[i],
h->transform.a[i]->index, h->parentIndex.a[i],
h->childIndex.a[i], h->childCount.a[i]);
}
puts ("");
}
static int
check_indices (transform_t *transform, uint32_t index, uint32_t parentIndex,
uint32_t childIndex, uint32_t childCount)
{
hierarchy_t *h = transform->hierarchy;
if (transform->index != index) {
printf ("%s/%s index incorrect: expect %u got %u\n",
h->name.a[transform->index], h->name.a[index],
index, transform->index);
return 0;
}
if (h->parentIndex.a[index] != parentIndex) {
printf ("%s parent index incorrect: expect %u got %u\n",
h->name.a[index], parentIndex, h->parentIndex.a[index]);
return 0;
}
if (h->childIndex.a[index] != childIndex) {
printf ("%s child index incorrect: expect %u got %u\n",
h->name.a[index], childIndex, h->childIndex.a[index]);
return 0;
}
if (h->childCount.a[index] != childCount) {
printf ("%s child count incorrect: expect %u got %u\n",
h->name.a[index], childCount, h->childCount.a[index]);
return 0;
}
return 1;
}
static int
test_single_transform (void)
{
transform_t *transform = Transform_New (scene, 0);
hierarchy_t *h;
if (!transform) {
printf ("Transform_New returned null\n");
return 1;
}
if (!(h = transform->hierarchy)) {
printf ("New transform has no hierarchy\n");
return 1;
}
if (!check_hierarchy_size (h, 1)) { return 1; }
if (!check_indices (transform, 0, null_transform, 1, 0)) { return 1; }
if (!mat4_equal (h->localMatrix.a[0], identity)
|| !mat4_equal (h->localInverse.a[0], identity)
|| !mat4_equal (h->worldMatrix.a[0], identity)
|| !mat4_equal (h->worldInverse.a[0], identity)) {
printf ("New transform matrices not identity\n");
return 1;
}
if (!vec4_equal (h->localRotation.a[0], identity[3])
|| !vec4_equal (h->localScale.a[0], one)) {
printf ("New transform rotation or scale not identity\n");
return 1;
}
// Delete the hierarchy directly as setparent isn't fully tested
Hierarchy_Delete (transform->hierarchy);
return 0;
}
static int
test_parent_child_init (void)
{
transform_t *parent = Transform_New (scene, 0);
transform_t *child = Transform_New (scene, parent);
if (parent->hierarchy != child->hierarchy) {
printf ("parent and child transforms have separate hierarchies\n");
return 1;
}
if (!check_hierarchy_size (parent->hierarchy, 2)) { return 1; }
if (!check_indices (parent, 0, null_transform, 1, 1)) { return 1; }
if (!check_indices (child, 1, 0, 2, 0)) { return 1; }
hierarchy_t *h = parent->hierarchy;
if (!mat4_equal (h->localMatrix.a[0], identity)
|| !mat4_equal (h->localInverse.a[0], identity)
|| !mat4_equal (h->worldMatrix.a[0], identity)
|| !mat4_equal (h->worldInverse.a[0], identity)) {
printf ("Parent transform matrices not identity\n");
return 1;
}
if (!vec4_equal (h->localRotation.a[0], identity[3])
|| !vec4_equal (h->localScale.a[0], one)) {
printf ("Parent transform rotation or scale not identity\n");
return 1;
}
if (!mat4_equal (h->localMatrix.a[1], identity)
|| !mat4_equal (h->localInverse.a[1], identity)
|| !mat4_equal (h->worldMatrix.a[1], identity)
|| !mat4_equal (h->worldInverse.a[1], identity)) {
printf ("Child transform matrices not identity\n");
return 1;
}
if (!vec4_equal (h->localRotation.a[1], identity[3])
|| !vec4_equal (h->localScale.a[1], one)) {
printf ("Child transform rotation or scale not identity\n");
return 1;
}
// Delete the hierarchy directly as setparent isn't fully tested
Hierarchy_Delete (parent->hierarchy);
return 0;
}
static int
test_parent_child_setparent (void)
{
transform_t *parent = Transform_New (scene, 0);
transform_t *child = Transform_New (scene, 0);
Transform_SetName (parent, "parent");
Transform_SetName (child, "child");
if (!check_indices (parent, 0, null_transform, 1, 0)) { return 1; }
if (!check_indices (child, 0, null_transform, 1, 0)) { return 1; }
if (parent->hierarchy == child->hierarchy) {
printf ("parent and child transforms have same hierarchy before"
" set paret\n");
return 1;
}
Transform_SetParent (child, parent);
if (parent->hierarchy != child->hierarchy) {
printf ("parent and child transforms have separate hierarchies\n");
return 1;
}
if (!check_hierarchy_size (parent->hierarchy, 2)) { return 1; }
if (!check_indices (parent, 0, null_transform, 1, 1)) { return 1; }
if (!check_indices (child, 1, 0, 2, 0)) { return 1; }
hierarchy_t *h = parent->hierarchy;
if (!mat4_equal (h->localMatrix.a[0], identity)
|| !mat4_equal (h->localInverse.a[0], identity)
|| !mat4_equal (h->worldMatrix.a[0], identity)
|| !mat4_equal (h->worldInverse.a[0], identity)) {
printf ("Parent transform matrices not identity\n");
return 1;
}
if (!vec4_equal (h->localRotation.a[0], identity[3])
|| !vec4_equal (h->localScale.a[0], one)) {
printf ("Parent transform rotation or scale not identity\n");
return 1;
}
if (!mat4_equal (h->localMatrix.a[1], identity)
|| !mat4_equal (h->localInverse.a[1], identity)
|| !mat4_equal (h->worldMatrix.a[1], identity)
|| !mat4_equal (h->worldInverse.a[1], identity)) {
printf ("Child transform matrices not identity\n");
return 1;
}
if (!vec4_equal (h->localRotation.a[1], identity[3])
|| !vec4_equal (h->localScale.a[1], one)) {
printf ("Child transform rotation or scale not identity\n");
return 1;
}
// Delete the hierarchy directly as setparent isn't fully tested
Hierarchy_Delete (parent->hierarchy);
return 0;
}
static int
test_build_hierarchy (void)
{
printf ("test_build_hierarchy\n");
transform_t *root = Transform_NewNamed (scene, 0, "root");
transform_t *A = Transform_NewNamed (scene, root, "A");
transform_t *B = Transform_NewNamed (scene, root, "B");
transform_t *C = Transform_NewNamed (scene, root, "C");
if (!check_indices (root, 0, null_transform, 1, 3)) { return 1; }
if (!check_indices (A, 1, 0, 4, 0)) { return 1; }
if (!check_indices (B, 2, 0, 4, 0)) { return 1; }
if (!check_indices (C, 3, 0, 4, 0)) { return 1; }
transform_t *B1 = Transform_NewNamed (scene, B, "B1");
if (!check_indices (root, 0, null_transform, 1, 3)) { return 1; }
if (!check_indices ( A, 1, 0, 4, 0)) { return 1; }
if (!check_indices ( B, 2, 0, 4, 1)) { return 1; }
if (!check_indices ( C, 3, 0, 5, 0)) { return 1; }
if (!check_indices (B1, 4, 2, 5, 0)) { return 1; }
transform_t *A1 = Transform_NewNamed (scene, A, "A1");
if (!check_indices (root, 0, null_transform, 1, 3)) { return 1; }
if (!check_indices ( A, 1, 0, 4, 1)) { return 1; }
if (!check_indices ( B, 2, 0, 5, 1)) { return 1; }
if (!check_indices ( C, 3, 0, 6, 0)) { return 1; }
if (!check_indices (A1, 4, 1, 6, 0)) { return 1; }
if (!check_indices (B1, 5, 2, 6, 0)) { return 1; }
transform_t *A1a = Transform_NewNamed (scene, A1, "A1a");
transform_t *B2 = Transform_NewNamed (scene, B, "B2");
transform_t *A2 = Transform_NewNamed (scene, A, "A2");
transform_t *B3 = Transform_NewNamed (scene, B, "B3");
transform_t *B2a = Transform_NewNamed (scene, B2, "B2a");
if (!check_hierarchy_size (root->hierarchy, 11)) { return 1; }
if (!check_indices (root, 0, null_transform, 1, 3)) { return 1; }
if (!check_indices ( A, 1, 0, 4, 2)) { return 1; }
if (!check_indices ( B, 2, 0, 6, 3)) { return 1; }
if (!check_indices ( C, 3, 0, 9, 0)) { return 1; }
if (!check_indices ( A1, 4, 1, 9, 1)) { return 1; }
if (!check_indices ( A2, 5, 1, 10, 0)) { return 1; }
if (!check_indices ( B1, 6, 2, 10, 0)) { return 1; }
if (!check_indices ( B2, 7, 2, 10, 1)) { return 1; }
if (!check_indices ( B3, 8, 2, 11, 0)) { return 1; }
if (!check_indices (A1a, 9, 4, 11, 0)) { return 1; }
if (!check_indices (B2a, 10, 7, 11, 0)) { return 1; }
transform_t *D = Transform_NewNamed (scene, root, "D");
if (!check_hierarchy_size (root->hierarchy, 12)) { return 1; }
if (!check_indices (root, 0, null_transform, 1, 4)) { return 1; }
if (!check_indices ( A, 1, 0, 5, 2)) { return 1; }
if (!check_indices ( B, 2, 0, 7, 3)) { return 1; }
if (!check_indices ( C, 3, 0, 10, 0)) { return 1; }
if (!check_indices ( D, 4, 0, 10, 0)) { return 1; }
if (!check_indices ( A1, 5, 1, 10, 1)) { return 1; }
if (!check_indices ( A2, 6, 1, 11, 0)) { return 1; }
if (!check_indices ( B1, 7, 2, 11, 0)) { return 1; }
if (!check_indices ( B2, 8, 2, 11, 1)) { return 1; }
if (!check_indices ( B3, 9, 2, 12, 0)) { return 1; }
if (!check_indices (A1a, 10, 5, 12, 0)) { return 1; }
if (!check_indices (B2a, 11, 8, 12, 0)) { return 1; }
dump_hierarchy (root->hierarchy);
transform_t *C1 = Transform_NewNamed (scene, C, "C1");
dump_hierarchy (root->hierarchy);
if (!check_hierarchy_size (root->hierarchy, 13)) { return 1; }
if (!check_indices (root, 0, null_transform, 1, 4)) { return 1; }
if (!check_indices ( A, 1, 0, 5, 2)) { return 1; }
if (!check_indices ( B, 2, 0, 7, 3)) { return 1; }
if (!check_indices ( C, 3, 0, 10, 1)) { return 1; }
if (!check_indices ( D, 4, 0, 11, 0)) { return 1; }
if (!check_indices ( A1, 5, 1, 11, 1)) { return 1; }
if (!check_indices ( A2, 6, 1, 12, 0)) { return 1; }
if (!check_indices ( B1, 7, 2, 12, 0)) { return 1; }
if (!check_indices ( B2, 8, 2, 12, 1)) { return 1; }
if (!check_indices ( B3, 9, 2, 13, 0)) { return 1; }
if (!check_indices ( C1, 10, 3, 13, 0)) { return 1; }
if (!check_indices (A1a, 11, 5, 13, 0)) { return 1; }
if (!check_indices (B2a, 12, 8, 13, 0)) { return 1; }
// Delete the hierarchy directly as setparent isn't fully tested
Hierarchy_Delete (root->hierarchy);
return 0;
}
static int
test_build_hierarchy2 (void)
{
printf ("test_build_hierarchy2\n");
transform_t *root = Transform_NewNamed (scene, 0, "root");
transform_t *A = Transform_NewNamed (scene, root, "A");
transform_t *B = Transform_NewNamed (scene, root, "B");
transform_t *C = Transform_NewNamed (scene, root, "C");
transform_t *B1 = Transform_NewNamed (scene, B, "B1");
transform_t *A1 = Transform_NewNamed (scene, A, "A1");
transform_t *A1a = Transform_NewNamed (scene, A1, "A1a");
transform_t *B2 = Transform_NewNamed (scene, B, "B2");
transform_t *A2 = Transform_NewNamed (scene, A, "A2");
transform_t *B3 = Transform_NewNamed (scene, B, "B3");
transform_t *B2a = Transform_NewNamed (scene, B2, "B2a");
transform_t *D = Transform_NewNamed (scene, root, "D");
transform_t *C1 = Transform_NewNamed (scene, C, "C1");
if (!check_hierarchy_size (root->hierarchy, 13)) { return 1; }
if (!check_indices (root, 0, null_transform, 1, 4)) { return 1; }
if (!check_indices ( A, 1, 0, 5, 2)) { return 1; }
if (!check_indices ( B, 2, 0, 7, 3)) { return 1; }
if (!check_indices ( C, 3, 0, 10, 1)) { return 1; }
if (!check_indices ( D, 4, 0, 11, 0)) { return 1; }
if (!check_indices ( A1, 5, 1, 11, 1)) { return 1; }
if (!check_indices ( A2, 6, 1, 12, 0)) { return 1; }
if (!check_indices ( B1, 7, 2, 12, 0)) { return 1; }
if (!check_indices ( B2, 8, 2, 12, 1)) { return 1; }
if (!check_indices ( B3, 9, 2, 13, 0)) { return 1; }
if (!check_indices ( C1, 10, 3, 13, 0)) { return 1; }
if (!check_indices (A1a, 11, 5, 13, 0)) { return 1; }
if (!check_indices (B2a, 12, 8, 13, 0)) { return 1; }
transform_t *T = Transform_NewNamed (scene, 0, "T");
transform_t *X = Transform_NewNamed (scene, T, "X");
transform_t *Y = Transform_NewNamed (scene, T, "Y");
transform_t *Z = Transform_NewNamed (scene, T, "Z");
transform_t *Y1 = Transform_NewNamed (scene, Y, "Y1");
transform_t *X1 = Transform_NewNamed (scene, X, "X1");
transform_t *X1a = Transform_NewNamed (scene, X1, "X1a");
transform_t *Y2 = Transform_NewNamed (scene, Y, "Y2");
transform_t *X2 = Transform_NewNamed (scene, X, "X2");
transform_t *Y3 = Transform_NewNamed (scene, Y, "Y3");
transform_t *Y2a = Transform_NewNamed (scene, Y2, "Y2a");
transform_t *Z1 = Transform_NewNamed (scene, Z, "Z1");
dump_hierarchy (T->hierarchy);
if (!check_hierarchy_size (T->hierarchy, 12)) { return 1; }
if (!check_indices ( T, 0, null_transform, 1, 3)) { return 1; }
if (!check_indices ( X, 1, 0, 4, 2)) { return 1; }
if (!check_indices ( Y, 2, 0, 6, 3)) { return 1; }
if (!check_indices ( Z, 3, 0, 9, 1)) { return 1; }
if (!check_indices ( X1, 4, 1, 10, 1)) { return 1; }
if (!check_indices ( X2, 5, 1, 11, 0)) { return 1; }
if (!check_indices ( Y1, 6, 2, 11, 0)) { return 1; }
if (!check_indices ( Y2, 7, 2, 11, 1)) { return 1; }
if (!check_indices ( Y3, 8, 2, 12, 0)) { return 1; }
if (!check_indices ( Z1, 9, 3, 12, 0)) { return 1; }
if (!check_indices (X1a, 10, 4, 12, 0)) { return 1; }
if (!check_indices (Y2a, 11, 7, 12, 0)) { return 1; }
Transform_SetParent (T, B);
dump_hierarchy (root->hierarchy);
if (!check_hierarchy_size (root->hierarchy, 25)) { return 1; }
if (!check_indices (root, 0, null_transform, 1, 4)) { return 1; }
if (!check_indices ( A, 1, 0, 5, 2)) { return 1; }
if (!check_indices ( B, 2, 0, 7, 4)) { return 1; }
if (!check_indices ( C, 3, 0, 11, 1)) { return 1; }
if (!check_indices ( D, 4, 0, 12, 0)) { return 1; }
if (!check_indices ( A1, 5, 1, 12, 1)) { return 1; }
if (!check_indices ( A2, 6, 1, 13, 0)) { return 1; }
if (!check_indices ( B1, 7, 2, 13, 0)) { return 1; }
if (!check_indices ( B2, 8, 2, 13, 1)) { return 1; }
if (!check_indices ( B3, 9, 2, 14, 0)) { return 1; }
if (!check_indices ( T, 10, 2, 14, 3)) { return 1; }
if (!check_indices ( C1, 11, 3, 17, 0)) { return 1; }
if (!check_indices (A1a, 12, 5, 17, 0)) { return 1; }
if (!check_indices (B2a, 13, 8, 17, 0)) { return 1; }
if (!check_indices ( X, 14, 10, 17, 2)) { return 1; }
if (!check_indices ( Y, 15, 10, 19, 3)) { return 1; }
if (!check_indices ( Z, 16, 10, 22, 1)) { return 1; }
if (!check_indices ( X1, 17, 14, 23, 1)) { return 1; }
if (!check_indices ( X2, 18, 14, 24, 0)) { return 1; }
if (!check_indices ( Y1, 19, 15, 24, 0)) { return 1; }
if (!check_indices ( Y2, 20, 15, 24, 1)) { return 1; }
if (!check_indices ( Y3, 21, 15, 25, 0)) { return 1; }
if (!check_indices ( Z1, 22, 16, 25, 0)) { return 1; }
if (!check_indices (X1a, 23, 17, 25, 0)) { return 1; }
if (!check_indices (Y2a, 24, 20, 25, 0)) { return 1; }
Transform_SetParent (Y, 0);
dump_hierarchy (root->hierarchy);
dump_hierarchy (Y->hierarchy);
if (!check_hierarchy_size (root->hierarchy, 20)) { return 1; }
if (!check_hierarchy_size (Y->hierarchy, 5)) { return 1; }
if (!check_indices (root, 0, null_transform, 1, 4)) { return 1; }
if (!check_indices ( A, 1, 0, 5, 2)) { return 1; }
if (!check_indices ( B, 2, 0, 7, 4)) { return 1; }
if (!check_indices ( C, 3, 0, 11, 1)) { return 1; }
if (!check_indices ( D, 4, 0, 12, 0)) { return 1; }
if (!check_indices ( A1, 5, 1, 12, 1)) { return 1; }
if (!check_indices ( A2, 6, 1, 13, 0)) { return 1; }
if (!check_indices ( B1, 7, 2, 13, 0)) { return 1; }
if (!check_indices ( B2, 8, 2, 13, 1)) { return 1; }
if (!check_indices ( B3, 9, 2, 14, 0)) { return 1; }
if (!check_indices ( T, 10, 2, 14, 3)) { return 1; }
if (!check_indices ( C1, 11, 3, 17, 0)) { return 1; }
if (!check_indices (A1a, 12, 5, 17, 0)) { return 1; }
if (!check_indices (B2a, 13, 8, 17, 0)) { return 1; }
if (!check_indices ( X, 14, 10, 16, 2)) { return 1; }
if (!check_indices ( Z, 15, 10, 18, 1)) { return 1; }
if (!check_indices ( X1, 16, 14, 19, 1)) { return 1; }
if (!check_indices ( X2, 17, 14, 20, 0)) { return 1; }
if (!check_indices ( Z1, 18, 15, 20, 0)) { return 1; }
if (!check_indices (X1a, 19, 16, 20, 0)) { return 1; }
if (!check_indices ( Y, 0, null_transform, 1, 3)) { return 1; }
if (!check_indices ( Y1, 1, 0, 4, 0)) { return 1; }
if (!check_indices ( Y2, 2, 0, 4, 1)) { return 1; }
if (!check_indices ( Y3, 3, 0, 5, 0)) { return 1; }
if (!check_indices (Y2a, 4, 2, 5, 0)) { return 1; }
// Delete the hierarchy directly as setparent isn't fully tested
Hierarchy_Delete (root->hierarchy);
Hierarchy_Delete (Y->hierarchy);
return 0;
}
static int
check_vector (const transform_t *transform,
vec4f_t (*func) (const transform_t *t),
vec4f_t expect, const char *msg)
{
vec4f_t res = func(transform);
if (!vec4_equal (res, expect)) {
printf ("%s %s: expected "VEC4F_FMT" got "VEC4F_FMT"\n",
Transform_GetName (transform), msg,
VEC4_EXP (expect), VEC4_EXP (res));
return 0;
}
return 1;
}
static int
test_frames (void)
{
transform_t *root = Transform_NewNamed (scene, 0, "root");
transform_t *A = Transform_NewNamed (scene, root, "A");
transform_t *B = Transform_NewNamed (scene, root, "B");
transform_t *A1 = Transform_NewNamed (scene, A, "A1");
transform_t *B1 = Transform_NewNamed (scene, B, "B1");
Transform_SetLocalPosition (root, (vec4f_t) { 0, 0, 1, 1 });
Transform_SetLocalPosition (A, (vec4f_t) { 1, 0, 0, 1 });
Transform_SetLocalRotation (A, (vec4f_t) { 0.5, 0.5, 0.5, 0.5 });
Transform_SetLocalPosition (B, (vec4f_t) { 0, 1, 0, 1 });
Transform_SetLocalRotation (B, (vec4f_t) { 0.5, -0.5, 0.5, 0.5 });
Transform_SetLocalPosition (A1, (vec4f_t) { 1, 0, 0, 1 });
Transform_SetLocalRotation (A1, (vec4f_t) { -0.5, -0.5, -0.5, 0.5 });
Transform_SetLocalPosition (B1, (vec4f_t) { 0, 1, 0, 1 });
Transform_SetLocalRotation (B1, (vec4f_t) { -0.5, 0.5, -0.5, 0.5 });
hierarchy_t *h = root->hierarchy;
for (size_t i = 0; i < h->transform.size; i++) {
mat4f_t res;
mmulf (res, h->localMatrix.a[i], h->localInverse.a[i]);
if (!mat4_equal (res, identity)) {
printf ("%s: localInverse not inverse of localMatrix\n",
h->name.a[i]);
printf ("l: " VEC4F_FMT "\n", MAT4_ROW(h->localMatrix.a[i], 0));
printf (" " VEC4F_FMT "\n", MAT4_ROW(h->localMatrix.a[i], 1));
printf (" " VEC4F_FMT "\n", MAT4_ROW(h->localMatrix.a[i], 2));
printf (" " VEC4F_FMT "\n", MAT4_ROW(h->localMatrix.a[i], 3));
printf ("i: " VEC4F_FMT "\n", MAT4_ROW(h->localInverse.a[i], 0));
printf (" " VEC4F_FMT "\n", MAT4_ROW(h->localInverse.a[i], 1));
printf (" " VEC4F_FMT "\n", MAT4_ROW(h->localInverse.a[i], 2));
printf (" " VEC4F_FMT "\n", MAT4_ROW(h->localInverse.a[i], 3));
printf ("r: " VEC4F_FMT "\n", MAT4_ROW(res, 0));
printf (" " VEC4F_FMT "\n", MAT4_ROW(res, 1));
printf (" " VEC4F_FMT "\n", MAT4_ROW(res, 2));
printf (" " VEC4F_FMT "\n", MAT4_ROW(res, 3));
return 1;
}
puts (h->name.a[i]);
printf ("l: " VEC4F_FMT "\n", MAT4_ROW(h->localMatrix.a[i], 0));
printf (" " VEC4F_FMT "\n", MAT4_ROW(h->localMatrix.a[i], 1));
printf (" " VEC4F_FMT "\n", MAT4_ROW(h->localMatrix.a[i], 2));
printf (" " VEC4F_FMT "\n", MAT4_ROW(h->localMatrix.a[i], 3));
printf ("i: " VEC4F_FMT "\n", MAT4_ROW(h->localInverse.a[i], 0));
printf (" " VEC4F_FMT "\n", MAT4_ROW(h->localInverse.a[i], 1));
printf (" " VEC4F_FMT "\n", MAT4_ROW(h->localInverse.a[i], 2));
printf (" " VEC4F_FMT "\n", MAT4_ROW(h->localInverse.a[i], 3));
}
for (size_t i = 0; i < h->transform.size; i++) {
mat4f_t res;
mmulf (res, h->worldMatrix.a[i], h->worldInverse.a[i]);
if (!mat4_equal (res, identity)) {
printf ("%s: worldInverse not inverse of worldMatrix\n",
h->name.a[i]);
printf ("l: " VEC4F_FMT "\n", MAT4_ROW(h->worldMatrix.a[i], 0));
printf (" " VEC4F_FMT "\n", MAT4_ROW(h->worldMatrix.a[i], 1));
printf (" " VEC4F_FMT "\n", MAT4_ROW(h->worldMatrix.a[i], 2));
printf (" " VEC4F_FMT "\n", MAT4_ROW(h->worldMatrix.a[i], 3));
printf ("i: " VEC4F_FMT "\n", MAT4_ROW(h->worldInverse.a[i], 0));
printf (" " VEC4F_FMT "\n", MAT4_ROW(h->worldInverse.a[i], 1));
printf (" " VEC4F_FMT "\n", MAT4_ROW(h->worldInverse.a[i], 2));
printf (" " VEC4F_FMT "\n", MAT4_ROW(h->worldInverse.a[i], 3));
printf ("r: " VEC4F_FMT "\n", MAT4_ROW(res, 0));
printf (" " VEC4F_FMT "\n", MAT4_ROW(res, 1));
printf (" " VEC4F_FMT "\n", MAT4_ROW(res, 2));
printf (" " VEC4F_FMT "\n", MAT4_ROW(res, 3));
return 1;
}
puts (h->name.a[i]);
printf ("l: " VEC4F_FMT "\n", MAT4_ROW(h->worldMatrix.a[i], 0));
printf (" " VEC4F_FMT "\n", MAT4_ROW(h->worldMatrix.a[i], 1));
printf (" " VEC4F_FMT "\n", MAT4_ROW(h->worldMatrix.a[i], 2));
printf (" " VEC4F_FMT "\n", MAT4_ROW(h->worldMatrix.a[i], 3));
printf ("i: " VEC4F_FMT "\n", MAT4_ROW(h->worldInverse.a[i], 0));
printf (" " VEC4F_FMT "\n", MAT4_ROW(h->worldInverse.a[i], 1));
printf (" " VEC4F_FMT "\n", MAT4_ROW(h->worldInverse.a[i], 2));
printf (" " VEC4F_FMT "\n", MAT4_ROW(h->worldInverse.a[i], 3));
}
if (!check_vector (root, Transform_GetLocalPosition,
(vec4f_t) { 0, 0, 1, 1 }, "local position")) {
return 1;
}
if (!check_vector (root, Transform_GetWorldPosition,
(vec4f_t) { 0, 0, 1, 1 }, "world position")) {
return 1;
}
if (!check_vector (root, Transform_Forward, (vec4f_t) { 1, 0, 0, 0 },
"forward")) {
return 1;
}
if (!check_vector (root, Transform_Right, (vec4f_t) { 0, -1, 0, 0 },
"right")) {
return 1;
}
if (!check_vector (root, Transform_Up, (vec4f_t) { 0, 0, 1, 0 },
"up")) {
return 1;
}
if (!check_vector (A, Transform_GetLocalPosition, (vec4f_t) { 1, 0, 0, 1 },
"local position")) {
return 1;
}
if (!check_vector (A, Transform_GetWorldPosition, (vec4f_t) { 1, 0, 1, 1 },
"world position")) {
return 1;
}
if (!check_vector (A, Transform_Forward, (vec4f_t) { 0, 1, 0, 0 },
"forward")) {
return 1;
}
if (!check_vector (A, Transform_Right, (vec4f_t) { 0, 0, -1, 0 },
"right")) {
return 1;
}
if (!check_vector (A, Transform_Up, (vec4f_t) { 1, 0, 0, 0 },
"up")) {
return 1;
}
if (!check_vector (A1, Transform_GetLocalPosition, (vec4f_t) { 1, 0, 0, 1 },
"local position")) {
return 1;
}
if (!check_vector (A1, Transform_GetWorldPosition, (vec4f_t) { 1, 1, 1, 1 },
"world position")) {
return 1;
}
if (!check_vector (A1, Transform_Forward, (vec4f_t) { 1, 0, 0, 0 },
"forward")) {
return 1;
}
if (!check_vector (A1, Transform_Right, (vec4f_t) { 0, -1, 0, 0 },
"right")) {
return 1;
}
if (!check_vector (A1, Transform_Up, (vec4f_t) { 0, 0, 1, 0 },
"up")) {
return 1;
}
if (!check_vector (B, Transform_GetLocalPosition, (vec4f_t) { 0, 1, 0, 1 },
"local position")) {
return 1;
}
if (!check_vector (B, Transform_GetWorldPosition, (vec4f_t) { 0, 1, 1, 1 },
"world position")) {
return 1;
}
if (!check_vector (B, Transform_Forward, (vec4f_t) { 0, 0, 1, 0 },
"forward")) {
return 1;
}
if (!check_vector (B, Transform_Right, (vec4f_t) { 1, 0, 0, 0 },
"right")) {
return 1;
}
if (!check_vector (B, Transform_Up, (vec4f_t) { 0,-1, 0, 0 },
"up")) {
return 1;
}
if (!check_vector (B1, Transform_GetLocalPosition, (vec4f_t) { 0, 1, 0, 1 },
"local position")) {
return 1;
}
if (!check_vector (B1, Transform_GetWorldPosition, (vec4f_t) {-1, 1, 1, 1 },
"world position")) {
return 1;
}
if (!check_vector (B1, Transform_Forward, (vec4f_t) { 1, 0, 0, 0 },
"forward")) {
return 1;
}
if (!check_vector (B1, Transform_Right, (vec4f_t) { 0, -1, 0, 0 },
"right")) {
return 1;
}
if (!check_vector (B1, Transform_Up, (vec4f_t) { 0, 0, 1, 0 },
"up")) {
return 1;
}
Transform_Delete (root);
return 0;
}
int
main (void)
{
scene = Scene_NewScene ();
if (test_single_transform ()) { return 1; }
if (test_parent_child_init ()) { return 1; }
if (test_parent_child_setparent ()) { return 1; }
if (test_build_hierarchy ()) { return 1; }
if (test_build_hierarchy2 ()) { return 1; }
if (test_frames ()) { return 1; }
Scene_DeleteScene (scene);
return 0;
}