#ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include "QF/ecs/component.h" #include "QF/ecs/hierarchy.h" #include "QF/scene/scene.h" #include "QF/scene/transform.h" ecs_registry_t *reg; // 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 (transform_t t, uint32_t size) { hierarchy_t *h = Transform_GetRef (t)->hierarchy; if (h->num_objects != size) { printf ("hierarchy does not have exactly %u transform\n", size); return 0; } char **name = h->components[transform_type_name]; ecs_registry_t *reg = h->reg; for (uint32_t i = 0; i < h->num_objects; i++) { hierref_t *ref = Ent_GetComponent (h->ent[i], scene_href, reg); if (ref->hierarchy != h) { printf ("transform %d (%s) does not point to hierarchy\n", i, name[i]); } } return 1; } static int check_indices (transform_t transform, uint32_t index, uint32_t parentIndex, uint32_t childIndex, uint32_t childCount) { __auto_type ref = Transform_GetRef (transform); hierarchy_t *h = ref->hierarchy; char **name = h->components[transform_type_name]; if (ref->index != index) { printf ("%s/%s index incorrect: expect %u got %u\n", name[ref->index], name[index], index, ref->index); return 0; } if (h->parentIndex[index] != parentIndex) { printf ("%s parent index incorrect: expect %u got %u\n", name[index], parentIndex, h->parentIndex[index]); return 0; } if (h->childIndex[index] != childIndex) { printf ("%s child index incorrect: expect %u got %u\n", name[index], childIndex, h->childIndex[index]); return 0; } if (h->childCount[index] != childCount) { printf ("%s child count incorrect: expect %u got %u\n", name[index], childCount, h->childCount[index]); return 0; } return 1; } static int test_single_transform (void) { transform_t transform = Transform_New (reg, (transform_t) {}); hierarchy_t *h; if (!transform.reg || transform.id == nullent) { printf ("Transform_New returned null\n"); return 1; } if (!Transform_GetRef (transform)) { printf ("Transform_GetRef returned null\n"); return 1; } if (!(h = Transform_GetRef (transform)->hierarchy)) { printf ("New transform has no hierarchy\n"); return 1; } mat4f_t *localMatrix = h->components[transform_type_localMatrix]; mat4f_t *localInverse = h->components[transform_type_localInverse]; mat4f_t *worldMatrix = h->components[transform_type_worldMatrix]; mat4f_t *worldInverse = h->components[transform_type_worldInverse]; vec4f_t *localRotation = h->components[transform_type_localRotation]; vec4f_t *localScale = h->components[transform_type_localScale]; if (!check_hierarchy_size (transform, 1)) { return 1; } if (!check_indices (transform, 0, nullent, 1, 0)) { return 1; } if (!mat4_equal (localMatrix[0], identity) || !mat4_equal (localInverse[0], identity) || !mat4_equal (worldMatrix[0], identity) || !mat4_equal (worldInverse[0], identity)) { printf ("New transform matrices not identity\n"); return 1; } if (!vec4_equal (localRotation[0], identity[3]) || !vec4_equal (localScale[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 (h); return 0; } static int test_parent_child_init (void) { transform_t parent = Transform_New (reg, (transform_t) {}); transform_t child = Transform_New (reg, parent); if (Transform_GetRef (parent)->hierarchy != Transform_GetRef (child)->hierarchy) { printf ("parent and child transforms have separate hierarchies\n"); return 1; } if (!check_hierarchy_size (parent, 2)) { return 1; } if (!check_indices (parent, 0, nullent, 1, 1)) { return 1; } if (!check_indices (child, 1, 0, 2, 0)) { return 1; } hierarchy_t *h = Transform_GetRef (parent)->hierarchy; mat4f_t *localMatrix = h->components[transform_type_localMatrix]; mat4f_t *localInverse = h->components[transform_type_localInverse]; mat4f_t *worldMatrix = h->components[transform_type_worldMatrix]; mat4f_t *worldInverse = h->components[transform_type_worldInverse]; vec4f_t *localRotation = h->components[transform_type_localRotation]; vec4f_t *localScale = h->components[transform_type_localScale]; if (!mat4_equal (localMatrix[0], identity) || !mat4_equal (localInverse[0], identity) || !mat4_equal (worldMatrix[0], identity) || !mat4_equal (worldInverse[0], identity)) { printf ("Parent transform matrices not identity\n"); return 1; } if (!vec4_equal (localRotation[0], identity[3]) || !vec4_equal (localScale[0], one)) { printf ("Parent transform rotation or scale not identity\n"); return 1; } if (!mat4_equal (localMatrix[1], identity) || !mat4_equal (localInverse[1], identity) || !mat4_equal (worldMatrix[1], identity) || !mat4_equal (worldInverse[1], identity)) { printf ("Child transform matrices not identity\n"); return 1; } if (!vec4_equal (localRotation[1], identity[3]) || !vec4_equal (localScale[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 (Transform_GetRef (parent)->hierarchy); return 0; } static int test_parent_child_setparent (void) { transform_t parent = Transform_New (reg, (transform_t) {}); transform_t child = Transform_New (reg, (transform_t) {}); Transform_SetName (parent, "parent"); Transform_SetName (child, "child"); if (!check_indices (parent, 0, nullent, 1, 0)) { return 1; } if (!check_indices (child, 0, nullent, 1, 0)) { return 1; } if (Transform_GetRef (parent)->hierarchy == Transform_GetRef (child)->hierarchy) { printf ("parent and child transforms have same hierarchy before" " set paret\n"); return 1; } Transform_SetParent (child, parent); if (Transform_GetRef (parent)->hierarchy != Transform_GetRef (child)->hierarchy) { printf ("parent and child transforms have separate hierarchies\n"); return 1; } if (!check_hierarchy_size (parent, 2)) { return 1; } if (!check_indices (parent, 0, nullent, 1, 1)) { return 1; } if (!check_indices (child, 1, 0, 2, 0)) { return 1; } hierarchy_t *h = Transform_GetRef (parent)->hierarchy; mat4f_t *localMatrix = h->components[transform_type_localMatrix]; mat4f_t *localInverse = h->components[transform_type_localInverse]; mat4f_t *worldMatrix = h->components[transform_type_worldMatrix]; mat4f_t *worldInverse = h->components[transform_type_worldInverse]; vec4f_t *localRotation = h->components[transform_type_localRotation]; vec4f_t *localScale = h->components[transform_type_localScale]; if (!mat4_equal (localMatrix[0], identity) || !mat4_equal (localInverse[0], identity) || !mat4_equal (worldMatrix[0], identity) || !mat4_equal (worldInverse[0], identity)) { printf ("Parent transform matrices not identity\n"); return 1; } if (!vec4_equal (localRotation[0], identity[3]) || !vec4_equal (localScale[0], one)) { printf ("Parent transform rotation or scale not identity\n"); return 1; } if (!mat4_equal (localMatrix[1], identity) || !mat4_equal (localInverse[1], identity) || !mat4_equal (worldMatrix[1], identity) || !mat4_equal (worldInverse[1], identity)) { printf ("Child transform matrices not identity\n"); return 1; } if (!vec4_equal (localRotation[1], identity[3]) || !vec4_equal (localScale[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 (Transform_GetRef (parent)->hierarchy); return 0; } static int check_vector (transform_t transform, vec4f_t (*func) (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 (reg, (transform_t) {}, "root"); transform_t A = Transform_NewNamed (reg, root, "A"); transform_t B = Transform_NewNamed (reg, root, "B"); transform_t A1 = Transform_NewNamed (reg, A, "A1"); transform_t B1 = Transform_NewNamed (reg, 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 = Transform_GetRef (root)->hierarchy; mat4f_t *localMatrix = h->components[transform_type_localMatrix]; mat4f_t *localInverse = h->components[transform_type_localInverse]; mat4f_t *worldMatrix = h->components[transform_type_worldMatrix]; mat4f_t *worldInverse = h->components[transform_type_worldInverse]; char **name = h->components[transform_type_name]; for (uint32_t i = 0; i < h->num_objects; i++) { mat4f_t res; mmulf (res, localMatrix[i], localInverse[i]); if (!mat4_equal (res, identity)) { printf ("%s: localInverse not inverse of localMatrix\n", name[i]); printf ("l: " VEC4F_FMT "\n", MAT4_ROW(localMatrix[i], 0)); printf (" " VEC4F_FMT "\n", MAT4_ROW(localMatrix[i], 1)); printf (" " VEC4F_FMT "\n", MAT4_ROW(localMatrix[i], 2)); printf (" " VEC4F_FMT "\n", MAT4_ROW(localMatrix[i], 3)); printf ("i: " VEC4F_FMT "\n", MAT4_ROW(localInverse[i], 0)); printf (" " VEC4F_FMT "\n", MAT4_ROW(localInverse[i], 1)); printf (" " VEC4F_FMT "\n", MAT4_ROW(localInverse[i], 2)); printf (" " VEC4F_FMT "\n", MAT4_ROW(localInverse[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 (name[i]); printf ("l: " VEC4F_FMT "\n", MAT4_ROW(localMatrix[i], 0)); printf (" " VEC4F_FMT "\n", MAT4_ROW(localMatrix[i], 1)); printf (" " VEC4F_FMT "\n", MAT4_ROW(localMatrix[i], 2)); printf (" " VEC4F_FMT "\n", MAT4_ROW(localMatrix[i], 3)); printf ("i: " VEC4F_FMT "\n", MAT4_ROW(localInverse[i], 0)); printf (" " VEC4F_FMT "\n", MAT4_ROW(localInverse[i], 1)); printf (" " VEC4F_FMT "\n", MAT4_ROW(localInverse[i], 2)); printf (" " VEC4F_FMT "\n", MAT4_ROW(localInverse[i], 3)); } for (uint32_t i = 0; i < h->num_objects; i++) { mat4f_t res; mmulf (res, worldMatrix[i], worldInverse[i]); if (!mat4_equal (res, identity)) { printf ("%s: worldInverse not inverse of worldMatrix\n", name[i]); printf ("l: " VEC4F_FMT "\n", MAT4_ROW(worldMatrix[i], 0)); printf (" " VEC4F_FMT "\n", MAT4_ROW(worldMatrix[i], 1)); printf (" " VEC4F_FMT "\n", MAT4_ROW(worldMatrix[i], 2)); printf (" " VEC4F_FMT "\n", MAT4_ROW(worldMatrix[i], 3)); printf ("i: " VEC4F_FMT "\n", MAT4_ROW(worldInverse[i], 0)); printf (" " VEC4F_FMT "\n", MAT4_ROW(worldInverse[i], 1)); printf (" " VEC4F_FMT "\n", MAT4_ROW(worldInverse[i], 2)); printf (" " VEC4F_FMT "\n", MAT4_ROW(worldInverse[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 (name[i]); printf ("l: " VEC4F_FMT "\n", MAT4_ROW(worldMatrix[i], 0)); printf (" " VEC4F_FMT "\n", MAT4_ROW(worldMatrix[i], 1)); printf (" " VEC4F_FMT "\n", MAT4_ROW(worldMatrix[i], 2)); printf (" " VEC4F_FMT "\n", MAT4_ROW(worldMatrix[i], 3)); printf ("i: " VEC4F_FMT "\n", MAT4_ROW(worldInverse[i], 0)); printf (" " VEC4F_FMT "\n", MAT4_ROW(worldInverse[i], 1)); printf (" " VEC4F_FMT "\n", MAT4_ROW(worldInverse[i], 2)); printf (" " VEC4F_FMT "\n", MAT4_ROW(worldInverse[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_t *scene = Scene_NewScene (); reg = scene->reg; if (test_single_transform ()) { return 1; } if (test_parent_child_init ()) { return 1; } if (test_parent_child_setparent ()) { return 1; } if (test_frames ()) { return 1; } Scene_DeleteScene (scene); return 0; }