mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-11 15:51:36 +00:00
57cd30fca3
It should have always been here, but when I first created the hierarchy and transform objects, I didn't know where things would go. Having two chunks of code for setting an entity's parent was too already too much, and I expect to have other hierarchy types. Doesn't fix the issues encountered with sbar, of course.
414 lines
12 KiB
C
414 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"
|
|
|
|
#include "scn_internal.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",
|
|
},
|
|
[transform_type_worldScale] = {
|
|
.size = sizeof (vec4f_t),
|
|
.create = transform_scale_identity,
|
|
.name = "World Scale",
|
|
},
|
|
};
|
|
|
|
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 *localScale = h->components[transform_type_localScale];
|
|
vec4f_t *worldRotation = h->components[transform_type_worldRotation];
|
|
vec4f_t *worldScale = h->components[transform_type_worldScale];
|
|
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];
|
|
worldScale[0] = localScale[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]);
|
|
}
|
|
}
|
|
for (size_t i = 1; i < h->num_objects; i++) {
|
|
uint32_t parent = h->parentIndex[i];
|
|
if (modified[i] || modified[parent]) {
|
|
worldScale[i] = m3vmulf (worldMatrix[parent],
|
|
localScale[i]);
|
|
}
|
|
}
|
|
memset (modified, 0, h->num_objects);
|
|
}
|
|
|
|
transform_t
|
|
Transform_New (ecs_registry_t *reg, transform_t parent)
|
|
{
|
|
uint32_t transform = ECS_NewEntity (reg);
|
|
hierref_t *ref = Ent_AddComponent (transform, scene_href, reg);
|
|
|
|
if (parent.reg && parent.id != nullent) {
|
|
hierref_t *pref = Transform_GetRef (parent);
|
|
ref->hierarchy = pref->hierarchy;
|
|
ref->index = Hierarchy_InsertHierarchy (pref->hierarchy, 0,
|
|
pref->index, 0);
|
|
} else {
|
|
ref->hierarchy = Hierarchy_New (reg, &transform_type, 1);
|
|
ref->index = 0;
|
|
}
|
|
ref->hierarchy->ent[ref->index] = transform;
|
|
Transform_UpdateMatrices (ref->hierarchy);
|
|
return (transform_t) { .reg = reg, .id = transform, .comp = 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->hierarchy);
|
|
}
|
|
|
|
transform_t
|
|
Transform_NewNamed (ecs_registry_t *reg, transform_t parent, const char *name)
|
|
{
|
|
transform_t transform = Transform_New (reg, parent);
|
|
Transform_SetName (transform, name);
|
|
return transform;
|
|
}
|
|
|
|
void
|
|
Transform_SetParent (transform_t transform, transform_t parent)
|
|
{
|
|
hierarchy_t *dst = 0;
|
|
uint32_t dstParent = nullent;
|
|
hierarchy_t *src = 0;
|
|
uint32_t srcIndex = 0;
|
|
if (Transform_Valid (parent)) {
|
|
__auto_type ref = Transform_GetRef (parent);
|
|
dst = ref->hierarchy;
|
|
dstParent = ref->index;
|
|
}
|
|
{
|
|
__auto_type ref = Transform_GetRef (transform);
|
|
src = ref->hierarchy;
|
|
srcIndex = ref->index;
|
|
}
|
|
Hierarchy_SetParent (dst, dstParent, src, srcIndex);
|
|
|
|
__auto_type ref = Transform_GetRef (transform);
|
|
hierarchy_t *h = ref->hierarchy;
|
|
byte *modified = h->components[transform_type_modified];
|
|
modified[ref->index] = 1;
|
|
Transform_UpdateMatrices (h);
|
|
}
|
|
|
|
void
|
|
Transform_SetName (transform_t transform, const char *_name)
|
|
{
|
|
__auto_type ref = Transform_GetRef (transform);
|
|
hierarchy_t *h = ref->hierarchy;
|
|
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_type ref = Transform_GetRef (transform);
|
|
hierarchy_t *h = ref->hierarchy;
|
|
uint32_t *tag = h->components[transform_type_tag];
|
|
tag[ref->index] = _tag;
|
|
}
|
|
|
|
void
|
|
Transform_SetLocalPosition (transform_t transform, vec4f_t position)
|
|
{
|
|
__auto_type ref = Transform_GetRef (transform);
|
|
hierarchy_t *h = ref->hierarchy;
|
|
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_type ref = Transform_GetRef (transform);
|
|
hierarchy_t *h = ref->hierarchy;
|
|
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_type ref = Transform_GetRef (transform);
|
|
hierarchy_t *h = ref->hierarchy;
|
|
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_type ref = Transform_GetRef (transform);
|
|
if (ref->index) {
|
|
hierarchy_t *h = ref->hierarchy;
|
|
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_type ref = Transform_GetRef (transform);
|
|
if (ref->index) {
|
|
hierarchy_t *h = ref->hierarchy;
|
|
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_type ref = Transform_GetRef (transform);
|
|
hierarchy_t *h = ref->hierarchy;
|
|
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);
|
|
}
|