mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-02-24 20:51:35 +00:00
The main goal of this change was to make it easier to tell when a hierarchy has been deleted, but as a side benefit, it got rid of the use of PR_RESMAP. Also, it's easy to track the number of hierarchies. Unfortunately, it showed how brittle the component side of the ECS is (scene and canvas registries assumed their components were the first (no long the case), thus the sweeping changes). Centerprint doesn't work (but it hasn't for a while).
393 lines
12 KiB
C
393 lines
12 KiB
C
/*
|
|
transform.c
|
|
|
|
General transform handling
|
|
|
|
Copyright (C) 2021 Bill Currke
|
|
|
|
This program 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.
|
|
|
|
This program 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 this program; if not, write to:
|
|
|
|
Free Software Foundation, Inc.
|
|
59 Temple Place - Suite 330
|
|
Boston, MA 02111-1307, USA
|
|
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#ifdef HAVE_STRING_H
|
|
# include <string.h>
|
|
#endif
|
|
#ifdef HAVE_STRINGS_H
|
|
# include <strings.h>
|
|
#endif
|
|
|
|
#define IMPLEMENT_TRANSFORM_Funcs
|
|
|
|
#include "QF/scene/scene.h"
|
|
#include "QF/scene/transform.h"
|
|
|
|
static void
|
|
transform_mat4f_identity (void *_mat)
|
|
{
|
|
vec4f_t *mat = _mat;
|
|
mat4fidentity (mat);
|
|
}
|
|
|
|
static void
|
|
transform_rotation_identity (void *_rot)
|
|
{
|
|
vec4f_t *rot = _rot;
|
|
*rot = (vec4f_t) { 0, 0, 0, 1 };
|
|
}
|
|
|
|
static void
|
|
transform_scale_identity (void *_scale)
|
|
{
|
|
vec4f_t *scale = _scale;
|
|
*scale = (vec4f_t) { 1, 1, 1, 1 };
|
|
}
|
|
|
|
static void
|
|
transform_modified_init (void *_modified)
|
|
{
|
|
byte *modified = _modified;
|
|
*modified = 1;
|
|
}
|
|
|
|
static const component_t transform_components[transform_type_count] = {
|
|
[transform_type_name] = {
|
|
.size = sizeof (char *),
|
|
.name = "Name",
|
|
},
|
|
[transform_type_tag] = {
|
|
.size = sizeof (uint32_t),
|
|
.name = "Tag",
|
|
},
|
|
[transform_type_modified] = {
|
|
.size = sizeof (byte),
|
|
.create = transform_modified_init,
|
|
.name = "Modified",
|
|
},
|
|
[transform_type_localMatrix] = {
|
|
.size = sizeof (mat4f_t),
|
|
.create = transform_mat4f_identity,
|
|
.name = "Local Matrix",
|
|
},
|
|
[transform_type_localInverse] = {
|
|
.size = sizeof (mat4f_t),
|
|
.create = transform_mat4f_identity,
|
|
.name = "Local Inverse",
|
|
},
|
|
[transform_type_worldMatrix] = {
|
|
.size = sizeof (mat4f_t),
|
|
.create = transform_mat4f_identity,
|
|
.name = "World Matrix",
|
|
},
|
|
[transform_type_worldInverse] = {
|
|
.size = sizeof (mat4f_t),
|
|
.create = transform_mat4f_identity,
|
|
.name = "World Inverse",
|
|
},
|
|
[transform_type_localRotation] = {
|
|
.size = sizeof (vec4f_t),
|
|
.create = transform_rotation_identity,
|
|
.name = "Local Rotation",
|
|
},
|
|
[transform_type_localScale] = {
|
|
.size = sizeof (vec4f_t),
|
|
.create = transform_scale_identity,
|
|
.name = "Local Scale",
|
|
},
|
|
[transform_type_worldRotation] = {
|
|
.size = sizeof (vec4f_t),
|
|
.create = transform_rotation_identity,
|
|
.name = "World Rotation",
|
|
},
|
|
};
|
|
|
|
static const hierarchy_type_t transform_type = {
|
|
.num_components = transform_type_count,
|
|
.components = transform_components,
|
|
};
|
|
|
|
static void
|
|
transform_calcLocalInverse (hierarchy_t *h, uint32_t index)
|
|
{
|
|
mat4f_t *localMatrix = h->components[transform_type_localMatrix];
|
|
mat4f_t *localInverse = h->components[transform_type_localInverse];
|
|
// This takes advantage of the fact that localMatrix is a simple
|
|
// homogenous scale/rotate/translate matrix with no shear
|
|
vec4f_t x = localMatrix[index][0];
|
|
vec4f_t y = localMatrix[index][1];
|
|
vec4f_t z = localMatrix[index][2];
|
|
vec4f_t t = localMatrix[index][3];
|
|
|
|
// "one" is to ensure both the scalar and translation have 1 in their
|
|
// fourth components
|
|
vec4f_t one = { 0, 0, 0, 1 };
|
|
vec4f_t nx = { x[0], y[0], z[0], 0 };
|
|
vec4f_t ny = { x[1], y[1], z[1], 0 };
|
|
vec4f_t nz = { x[2], y[2], z[2], 0 };
|
|
vec4f_t nt = one - t[0] * nx - t[1] * ny - t[2] * nz;
|
|
// vertical dot product!!!
|
|
vec4f_t s = 1 / (nx * nx + ny * ny + nz * nz + one);
|
|
localInverse[index][0] = nx * s;
|
|
localInverse[index][1] = ny * s;
|
|
localInverse[index][2] = nz * s;
|
|
localInverse[index][3] = nt * s;
|
|
}
|
|
|
|
static void
|
|
Transform_UpdateMatrices (hierarchy_t *h)
|
|
{
|
|
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 *worldRotation = h->components[transform_type_worldRotation];
|
|
byte *modified = h->components[transform_type_modified];
|
|
|
|
for (uint32_t i = 0; i < h->num_objects; i++) {
|
|
if (modified[i]) {
|
|
transform_calcLocalInverse (h, i);
|
|
}
|
|
}
|
|
if (modified[0]) {
|
|
memcpy (worldMatrix[0],
|
|
localMatrix[0], sizeof (mat4_t));
|
|
memcpy (worldInverse[0],
|
|
localInverse[0], sizeof (mat4_t));
|
|
worldRotation[0] = localRotation[0];
|
|
}
|
|
for (size_t i = 1; i < h->num_objects; i++) {
|
|
uint32_t parent = h->parentIndex[i];
|
|
|
|
if (modified[i] || modified[parent]) {
|
|
mmulf (worldMatrix[i],
|
|
worldMatrix[parent], localMatrix[i]);
|
|
modified[i] = 1;
|
|
}
|
|
}
|
|
for (size_t i = 1; i < h->num_objects; i++) {
|
|
uint32_t parent = h->parentIndex[i];
|
|
|
|
if (modified[i] || modified[parent]) {
|
|
mmulf (worldInverse[i],
|
|
localInverse[i], worldInverse[parent]);
|
|
}
|
|
}
|
|
for (size_t i = 1; i < h->num_objects; i++) {
|
|
uint32_t parent = h->parentIndex[i];
|
|
if (modified[i] || modified[parent]) {
|
|
worldRotation[i] = qmulf (worldRotation[parent],
|
|
localRotation[i]);
|
|
}
|
|
}
|
|
memset (modified, 0, h->num_objects);
|
|
}
|
|
|
|
transform_t
|
|
Transform_New (ecs_system_t ssys, transform_t parent)
|
|
{
|
|
uint32_t transform = ECS_NewEntity (ssys.reg);
|
|
hierref_t *ref = Ent_AddComponent (transform, ssys.base + scene_href,
|
|
ssys.reg);
|
|
|
|
if (parent.reg && parent.id != nullent) {
|
|
hierref_t pref = Transform_GetRef (parent);
|
|
*ref = Hierarchy_InsertHierarchy (pref, nullhref, ssys.reg);
|
|
} else {
|
|
ref->id = Hierarchy_New (ssys.reg, ssys.base + scene_href,
|
|
&transform_type, 1);
|
|
ref->index = 0;
|
|
}
|
|
hierarchy_t *h = Ent_GetComponent (ref->id, ecs_hierarchy, ssys.reg);
|
|
h->ent[ref->index] = transform;
|
|
Transform_UpdateMatrices (h);
|
|
return (transform_t) {
|
|
.reg = ssys.reg,
|
|
.id = transform,
|
|
.comp = ssys.base + scene_href,
|
|
};
|
|
}
|
|
|
|
void
|
|
Transform_Delete (transform_t transform)
|
|
{
|
|
hierref_t ref = Transform_GetRef (transform);
|
|
if (ref.index != 0) {
|
|
// The transform is not the root, so pull it out of its current
|
|
// hierarchy so deleting it is easier
|
|
Transform_SetParent (transform, (transform_t) {});
|
|
}
|
|
// Takes care of freeing the transforms
|
|
Hierarchy_Delete (ref.id, transform.reg);
|
|
}
|
|
|
|
transform_t
|
|
Transform_NewNamed (ecs_system_t ssys, transform_t parent, const char *name)
|
|
{
|
|
transform_t transform = Transform_New (ssys, parent);
|
|
Transform_SetName (transform, name);
|
|
return transform;
|
|
}
|
|
|
|
void
|
|
Transform_SetParent (transform_t transform, transform_t parent)
|
|
{
|
|
hierref_t dref = nullhref;
|
|
hierref_t sref = Transform_GetRef (transform);
|
|
if (Transform_Valid (parent)) {
|
|
dref = Transform_GetRef (parent);
|
|
}
|
|
Hierarchy_SetParent (dref, sref, transform.reg);
|
|
|
|
auto ref = Transform_GetRef (transform);
|
|
hierarchy_t *h = Ent_GetComponent (ref.id, ecs_hierarchy, transform.reg);
|
|
byte *modified = h->components[transform_type_modified];
|
|
modified[ref.index] = 1;
|
|
Transform_UpdateMatrices (h);
|
|
}
|
|
|
|
void
|
|
Transform_SetName (transform_t transform, const char *_name)
|
|
{
|
|
auto ref = Transform_GetRef (transform);
|
|
hierarchy_t *h = Ent_GetComponent (ref.id, ecs_hierarchy, transform.reg);
|
|
char **name = h->components[transform_type_name];
|
|
//FIXME create a string pool (similar to qfcc's, or even move that to util)
|
|
if (name[ref.index]) {
|
|
free (name[ref.index]);
|
|
}
|
|
name[ref.index] = strdup (_name);
|
|
}
|
|
|
|
void
|
|
Transform_SetTag (transform_t transform, uint32_t _tag)
|
|
{
|
|
auto ref = Transform_GetRef (transform);
|
|
hierarchy_t *h = Ent_GetComponent (ref.id, ecs_hierarchy, transform.reg);
|
|
uint32_t *tag = h->components[transform_type_tag];
|
|
tag[ref.index] = _tag;
|
|
}
|
|
|
|
void
|
|
Transform_SetLocalPosition (transform_t transform, vec4f_t position)
|
|
{
|
|
auto ref = Transform_GetRef (transform);
|
|
hierarchy_t *h = Ent_GetComponent (ref.id, ecs_hierarchy, transform.reg);
|
|
mat4f_t *localMatrix = h->components[transform_type_localMatrix];
|
|
byte *modified = h->components[transform_type_modified];
|
|
localMatrix[ref.index][3] = position;
|
|
modified[ref.index] = 1;
|
|
Transform_UpdateMatrices (h);
|
|
}
|
|
|
|
void
|
|
Transform_SetLocalRotation (transform_t transform, vec4f_t rotation)
|
|
{
|
|
auto ref = Transform_GetRef (transform);
|
|
hierarchy_t *h = Ent_GetComponent (ref.id, ecs_hierarchy, transform.reg);
|
|
mat4f_t *localMatrix = h->components[transform_type_localMatrix];
|
|
vec4f_t *localRotation = h->components[transform_type_localRotation];
|
|
vec4f_t *localScale = h->components[transform_type_localScale];
|
|
byte *modified = h->components[transform_type_modified];
|
|
vec4f_t scale = localScale[ref.index];
|
|
|
|
mat4f_t mat;
|
|
mat4fquat (mat, rotation);
|
|
|
|
localRotation[ref.index] = rotation;
|
|
localMatrix[ref.index][0] = mat[0] * scale[0];
|
|
localMatrix[ref.index][1] = mat[1] * scale[1];
|
|
localMatrix[ref.index][2] = mat[2] * scale[2];
|
|
modified[ref.index] = 1;
|
|
Transform_UpdateMatrices (h);
|
|
}
|
|
|
|
void
|
|
Transform_SetLocalScale (transform_t transform, vec4f_t scale)
|
|
{
|
|
auto ref = Transform_GetRef (transform);
|
|
hierarchy_t *h = Ent_GetComponent (ref.id, ecs_hierarchy, transform.reg);
|
|
mat4f_t *localMatrix = h->components[transform_type_localMatrix];
|
|
vec4f_t *localRotation = h->components[transform_type_localRotation];
|
|
vec4f_t *localScale = h->components[transform_type_localScale];
|
|
byte *modified = h->components[transform_type_modified];
|
|
vec4f_t rotation = localRotation[ref.index];
|
|
|
|
mat4f_t mat;
|
|
mat4fquat (mat, rotation);
|
|
|
|
localScale[ref.index] = scale;
|
|
localMatrix[ref.index][0] = mat[0] * scale[0];
|
|
localMatrix[ref.index][1] = mat[1] * scale[1];
|
|
localMatrix[ref.index][2] = mat[2] * scale[2];
|
|
modified[ref.index] = 1;
|
|
Transform_UpdateMatrices (h);
|
|
}
|
|
|
|
void
|
|
Transform_SetWorldPosition (transform_t transform, vec4f_t position)
|
|
{
|
|
auto ref = Transform_GetRef (transform);
|
|
hierarchy_t *h = Ent_GetComponent (ref.id, ecs_hierarchy, transform.reg);
|
|
if (ref.index) {
|
|
mat4f_t *worldInverse = h->components[transform_type_worldInverse];
|
|
uint32_t parent = h->parentIndex[ref.index];
|
|
position = mvmulf (worldInverse[parent], position);
|
|
}
|
|
Transform_SetLocalPosition (transform, position);
|
|
}
|
|
|
|
void
|
|
Transform_SetWorldRotation (transform_t transform, vec4f_t rotation)
|
|
{
|
|
auto ref = Transform_GetRef (transform);
|
|
hierarchy_t *h = Ent_GetComponent (ref.id, ecs_hierarchy, transform.reg);
|
|
if (ref.index) {
|
|
vec4f_t *worldRotation = h->components[transform_type_worldRotation];
|
|
uint32_t parent = h->parentIndex[ref.index];
|
|
rotation = qmulf (qconjf (worldRotation[parent]), rotation);
|
|
}
|
|
Transform_SetLocalRotation (transform, rotation);
|
|
}
|
|
|
|
void
|
|
Transform_SetLocalTransform (transform_t transform, vec4f_t scale,
|
|
vec4f_t rotation, vec4f_t position)
|
|
{
|
|
auto ref = Transform_GetRef (transform);
|
|
hierarchy_t *h = Ent_GetComponent (ref.id, ecs_hierarchy, transform.reg);
|
|
mat4f_t *localMatrix = h->components[transform_type_localMatrix];
|
|
vec4f_t *localRotation = h->components[transform_type_localRotation];
|
|
vec4f_t *localScale = h->components[transform_type_localScale];
|
|
byte *modified = h->components[transform_type_modified];
|
|
mat4f_t mat;
|
|
mat4fquat (mat, rotation);
|
|
|
|
position[3] = 1;
|
|
localRotation[ref.index] = rotation;
|
|
localScale[ref.index] = scale;
|
|
localMatrix[ref.index][0] = mat[0] * scale[0];
|
|
localMatrix[ref.index][1] = mat[1] * scale[1];
|
|
localMatrix[ref.index][2] = mat[2] * scale[2];
|
|
localMatrix[ref.index][3] = position;
|
|
modified[ref.index] = 1;
|
|
Transform_UpdateMatrices (h);
|
|
}
|