mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-23 04:42:32 +00:00
0360e33a00
As I had long suspected, building large hierarchies is fiendishly expensive (at least O(N^2)). However, this is because the hierarchies are structured such that adding high-level nodes results in a lot of copying due to the flattened (breadth-first) layout (which does make for excellent breadth-first performance when working with a hierarchy). Using tree mode allows adding new nodes to be O(1) (I guess O(N) for the size of the sub-tree being added, but that's not supported yet) and costs only an additional 8 bytes per node. Switching from flat mode to tree mode is very cheap as only the additional tree-related indices need to be fixed up (they're almost entirely ignored in flat mode). Switching from tree to flat mode is a little more expensive as the entire tree needs to be copied, but it seems to be an O(N) (size of the tree). With this, building the style editor window went from about 25% to about 5% (and most of that is realloc!), with a 1.3% cost for switching from tree mode to flat mode. There's still a lot of work to do (supporting removal and tree inserts).
706 lines
21 KiB
C
706 lines
21 KiB
C
/*
|
|
hierarchy.c
|
|
|
|
ECS hierarchy 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
|
|
|
|
#include "QF/sys.h"
|
|
|
|
#include "QF/ecs.h"
|
|
|
|
static component_t ent_component = { .size = sizeof (uint32_t) };
|
|
static component_t childCount_component = { .size = sizeof (uint32_t) };
|
|
static component_t childIndex_component = { .size = sizeof (uint32_t) };
|
|
static component_t parentIndex_component = { .size = sizeof (uint32_t) };
|
|
static component_t nextIndex_component = { .size = sizeof (uint32_t) };
|
|
static component_t lastIndex_component = { .size = sizeof (uint32_t) };
|
|
|
|
|
|
|
|
static void
|
|
hierarchy_UpdateTransformIndices (hierarchy_t *hierarchy, uint32_t start,
|
|
int offset)
|
|
{
|
|
ecs_registry_t *reg = hierarchy->reg;
|
|
uint32_t href = hierarchy->href_comp;
|
|
for (size_t i = start; i < hierarchy->num_objects; i++) {
|
|
if (ECS_EntValid (hierarchy->ent[i], reg)) {
|
|
hierref_t *ref = Ent_GetComponent (hierarchy->ent[i], href, reg);
|
|
ref->index += offset;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
hierarchy_InvalidateReferences (hierarchy_t *hierarchy, uint32_t start,
|
|
uint32_t count)
|
|
{
|
|
ecs_registry_t *reg = hierarchy->reg;
|
|
uint32_t href = hierarchy->href_comp;
|
|
for (size_t i = start; count-- > 0; i++) {
|
|
if (ECS_EntValid (hierarchy->ent[i], reg)) {
|
|
hierref_t *ref = Ent_GetComponent (hierarchy->ent[i], href, reg);
|
|
ref->hierarchy = 0;
|
|
ref->index = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
hierarchy_UpdateChildIndices (hierarchy_t *hierarchy, uint32_t start,
|
|
int offset)
|
|
{
|
|
for (size_t i = start; i < hierarchy->num_objects; i++) {
|
|
hierarchy->childIndex[i] += offset;
|
|
}
|
|
}
|
|
|
|
static void
|
|
hierarchy_UpdateParentIndices (hierarchy_t *hierarchy, uint32_t start,
|
|
int offset)
|
|
{
|
|
for (size_t i = start; i < hierarchy->num_objects; i++) {
|
|
hierarchy->parentIndex[i] += offset;
|
|
}
|
|
}
|
|
|
|
void
|
|
Hierarchy_Reserve (hierarchy_t *hierarchy, uint32_t count)
|
|
{
|
|
if (hierarchy->num_objects + count > hierarchy->max_objects) {
|
|
uint32_t new_max = hierarchy->num_objects + count;
|
|
new_max += 15;
|
|
new_max &= ~15;
|
|
|
|
Component_ResizeArray (&ent_component,
|
|
(void **) &hierarchy->ent, new_max);
|
|
Component_ResizeArray (&childCount_component,
|
|
(void **) &hierarchy->childCount, new_max);
|
|
Component_ResizeArray (&childIndex_component,
|
|
(void **) &hierarchy->childIndex, new_max);
|
|
Component_ResizeArray (&parentIndex_component,
|
|
(void **) &hierarchy->parentIndex, new_max);
|
|
Component_ResizeArray (&nextIndex_component,
|
|
(void **) &hierarchy->nextIndex, new_max);
|
|
Component_ResizeArray (&lastIndex_component,
|
|
(void **) &hierarchy->lastIndex, new_max);
|
|
|
|
if (hierarchy->type) {
|
|
for (uint32_t i = 0; i < hierarchy->type->num_components; i++) {
|
|
Component_ResizeArray (&hierarchy->type->components[i],
|
|
&hierarchy->components[i], new_max);
|
|
}
|
|
}
|
|
hierarchy->max_objects = new_max;
|
|
}
|
|
}
|
|
|
|
static void
|
|
hierarchy_open (hierarchy_t *hierarchy, uint32_t index, uint32_t count)
|
|
{
|
|
Hierarchy_Reserve (hierarchy, count);
|
|
|
|
hierarchy->num_objects += count;
|
|
uint32_t dstIndex = index + count;
|
|
count = hierarchy->num_objects - index - count;
|
|
if (!count) {
|
|
return;
|
|
}
|
|
Component_MoveElements (&ent_component,
|
|
hierarchy->ent, dstIndex, index, count);
|
|
Component_MoveElements (&childCount_component,
|
|
hierarchy->childCount, dstIndex, index, count);
|
|
Component_MoveElements (&childIndex_component,
|
|
hierarchy->childIndex, dstIndex, index, count);
|
|
Component_MoveElements (&parentIndex_component,
|
|
hierarchy->parentIndex, dstIndex, index, count);
|
|
Component_MoveElements (&nextIndex_component,
|
|
hierarchy->nextIndex, dstIndex, index, count);
|
|
Component_MoveElements (&lastIndex_component,
|
|
hierarchy->lastIndex, dstIndex, index, count);
|
|
if (hierarchy->type) {
|
|
for (uint32_t i = 0; i < hierarchy->type->num_components; i++) {
|
|
Component_MoveElements (&hierarchy->type->components[i],
|
|
hierarchy->components[i],
|
|
dstIndex, index, count);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
hierarchy_close (hierarchy_t *hierarchy, uint32_t index, uint32_t count)
|
|
{
|
|
if (!count) {
|
|
return;
|
|
}
|
|
hierarchy->num_objects -= count;
|
|
uint32_t srcIndex = index + count;
|
|
count = hierarchy->num_objects - index;
|
|
Component_MoveElements (&ent_component,
|
|
hierarchy->ent, index, srcIndex, count);
|
|
Component_MoveElements (&childCount_component,
|
|
hierarchy->childCount, index, srcIndex, count);
|
|
Component_MoveElements (&childIndex_component,
|
|
hierarchy->childIndex, index, srcIndex, count);
|
|
Component_MoveElements (&parentIndex_component,
|
|
hierarchy->parentIndex, index, srcIndex, count);
|
|
Component_MoveElements (&nextIndex_component,
|
|
hierarchy->nextIndex, index, srcIndex, count);
|
|
Component_MoveElements (&lastIndex_component,
|
|
hierarchy->lastIndex, index, srcIndex, count);
|
|
if (hierarchy->type) {
|
|
for (uint32_t i = 0; i < hierarchy->type->num_components; i++) {
|
|
Component_MoveElements (&hierarchy->type->components[i],
|
|
hierarchy->components[i],
|
|
index, srcIndex, count);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
hierarchy_move (hierarchy_t *dst, const hierarchy_t *src,
|
|
uint32_t dstIndex, uint32_t srcIndex, uint32_t count)
|
|
{
|
|
ecs_registry_t *reg = dst->reg;
|
|
uint32_t href = dst->href_comp;
|
|
Component_CopyElements (&ent_component,
|
|
dst->ent, dstIndex,
|
|
src->ent, srcIndex, count);
|
|
// Actually move (as in C++ move semantics) source hierarchy object
|
|
// references so that their indices do not get updated when the objects
|
|
// are removed from the source hierarchy
|
|
memset (&src->ent[srcIndex], nullent, count * sizeof(dst->ent[0]));
|
|
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
if (dst->ent[dstIndex + i] != nullent) {
|
|
uint32_t ent = dst->ent[dstIndex + i];
|
|
hierref_t *ref = Ent_GetComponent (ent, href, reg);
|
|
ref->hierarchy = dst;
|
|
ref->index = dstIndex + i;
|
|
}
|
|
}
|
|
if (dst->type) {
|
|
for (uint32_t i = 0; i < dst->type->num_components; i++) {
|
|
Component_CopyElements (&dst->type->components[i],
|
|
dst->components[i], dstIndex,
|
|
src->components[i], srcIndex, count);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
hierarchy_init (hierarchy_t *dst, uint32_t index,
|
|
uint32_t parentIndex, uint32_t childIndex, uint32_t count)
|
|
{
|
|
memset (&dst->ent[index], nullent, count * sizeof(uint32_t));
|
|
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
dst->parentIndex[index + i] = parentIndex;
|
|
dst->childCount[index + i] = 0;
|
|
dst->childIndex[index + i] = childIndex;
|
|
dst->lastIndex[index + i] = nullindex;
|
|
}
|
|
if (dst->type) {
|
|
for (uint32_t i = 0; i < dst->type->num_components; i++) {
|
|
Component_CreateElements (&dst->type->components[i],
|
|
dst->components[i], index, count);
|
|
}
|
|
}
|
|
}
|
|
|
|
static uint32_t
|
|
hierarchy_insert_flat (hierarchy_t *dst, const hierarchy_t *src,
|
|
uint32_t dstParent, uint32_t *srcRoot, uint32_t count)
|
|
{
|
|
uint32_t insertIndex; // where the objects will be inserted
|
|
uint32_t childIndex; // where the objects' children will inserted
|
|
|
|
// The newly added objects are always last children of the parent
|
|
// object
|
|
insertIndex = dst->childIndex[dstParent] + dst->childCount[dstParent];
|
|
// By design, all of an object's children are in one contiguous block,
|
|
// and the blocks of children for each object are ordered by their
|
|
// parents. Thus the child index of each object increases monotonically
|
|
// for each child index in the array, regardless of the level of the owning
|
|
// object (higher levels always come before lower levels).
|
|
uint32_t neighbor = insertIndex - 1; // insertIndex never zero
|
|
childIndex = dst->childIndex[neighbor] + dst->childCount[neighbor];
|
|
|
|
// Any objects that come after the inserted objects need to have
|
|
// thier indices adjusted.
|
|
hierarchy_UpdateTransformIndices (dst, insertIndex, count);
|
|
// The parent object's child index is not affected, but the child
|
|
// indices of all objects immediately after the parent object are.
|
|
hierarchy_UpdateChildIndices (dst, dstParent + 1, count);
|
|
hierarchy_UpdateParentIndices (dst, childIndex, count);
|
|
|
|
// The beginning of the block of children for the new objects was
|
|
// computed from the pre-insert indices of the related objects, thus
|
|
// the index must be updated by the number of objects being inserted
|
|
// (it would have been updated thusly if the insert was done before
|
|
// updating the indices of the other objects).
|
|
childIndex += count;
|
|
|
|
hierarchy_open (dst, insertIndex, count);
|
|
if (dst == src && insertIndex <= *srcRoot) {
|
|
*srcRoot += count;
|
|
}
|
|
if (src) {
|
|
hierarchy_move (dst, src, insertIndex, *srcRoot, count);
|
|
} else {
|
|
hierarchy_init (dst, insertIndex, dstParent, childIndex, count);
|
|
}
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
dst->parentIndex[insertIndex + i] = dstParent;
|
|
dst->childIndex[insertIndex + i] = childIndex;
|
|
dst->childCount[insertIndex + i] = 0;
|
|
}
|
|
|
|
dst->childCount[dstParent] += count;
|
|
return insertIndex;
|
|
}
|
|
|
|
static uint32_t
|
|
hierarchy_insert_tree (hierarchy_t *dst, const hierarchy_t *src,
|
|
uint32_t dstParent, uint32_t *srcRoot, uint32_t count)
|
|
{
|
|
uint32_t insertIndex;
|
|
if (dst == src) {
|
|
// reparenting within the hierarchy, so need only to update indices
|
|
// of course, easier said than done
|
|
insertIndex = *srcRoot;
|
|
uint32_t srcParent = dst->parentIndex[insertIndex];
|
|
uint32_t *next = &dst->nextIndex[srcParent];
|
|
while (*next != insertIndex) {
|
|
next = &dst->nextIndex[*next];
|
|
}
|
|
if (dst->lastIndex[srcParent] == insertIndex) {
|
|
// removing src from the end of srcParent's child chain
|
|
dst->lastIndex[srcParent] = next - dst->nextIndex;
|
|
}
|
|
*next = dst->nextIndex[insertIndex];
|
|
dst->nextIndex[insertIndex] = nullindex;
|
|
|
|
dst->nextIndex[dst->lastIndex[dstParent]] = insertIndex;
|
|
dst->lastIndex[dstParent] = insertIndex;
|
|
} else {
|
|
// new objecs are always appended
|
|
insertIndex = dst->num_objects;
|
|
hierarchy_open (dst, insertIndex, count);
|
|
if (dst->childCount[dstParent]) {
|
|
dst->nextIndex[dst->lastIndex[dstParent]] = insertIndex;
|
|
} else {
|
|
dst->childIndex[dstParent] = insertIndex;
|
|
}
|
|
dst->childCount[dstParent] += count;
|
|
dst->lastIndex[dstParent] = insertIndex;
|
|
dst->nextIndex[insertIndex] = nullindex;
|
|
|
|
if (src) {
|
|
hierarchy_move (dst, src, insertIndex, *srcRoot, count);
|
|
} else {
|
|
hierarchy_init (dst, insertIndex, dstParent, nullindex, count);
|
|
}
|
|
}
|
|
return insertIndex;
|
|
}
|
|
|
|
static uint32_t
|
|
hierarchy_insert (hierarchy_t *dst, const hierarchy_t *src,
|
|
uint32_t dstParent, uint32_t *srcRoot, uint32_t count)
|
|
{
|
|
if (dst->tree_mode) {
|
|
return hierarchy_insert_tree (dst, src, dstParent, srcRoot, count);
|
|
} else {
|
|
return hierarchy_insert_flat (dst, src, dstParent, srcRoot, count);
|
|
}
|
|
}
|
|
|
|
static void
|
|
hierarchy_insert_children (hierarchy_t *dst, const hierarchy_t *src,
|
|
uint32_t dstParent, uint32_t *srcRoot)
|
|
{
|
|
uint32_t insertIndex;
|
|
uint32_t childIndex = src->childIndex[*srcRoot];
|
|
uint32_t childCount = src->childCount[*srcRoot];
|
|
|
|
if (childCount) {
|
|
insertIndex = hierarchy_insert (dst, src, dstParent,
|
|
&childIndex, childCount);
|
|
if (dst == src && insertIndex <= *srcRoot) {
|
|
*srcRoot += childCount;
|
|
}
|
|
for (uint32_t i = 0; i < childCount; i++, childIndex++) {
|
|
hierarchy_insert_children (dst, src, insertIndex + i, &childIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
static uint32_t
|
|
hierarchy_insertHierarchy (hierarchy_t *dst, const hierarchy_t *src,
|
|
uint32_t dstParent, uint32_t *srcRoot)
|
|
{
|
|
uint32_t insertIndex;
|
|
|
|
if (dstParent == nullindex) {
|
|
if (dst->num_objects) {
|
|
Sys_Error ("attempt to insert root in non-empty hierarchy");
|
|
}
|
|
hierarchy_open (dst, 0, 1);
|
|
if (src) {
|
|
hierarchy_move (dst, src, 0, *srcRoot, 1);
|
|
}
|
|
dst->parentIndex[0] = nullindex;
|
|
dst->childIndex[0] = 1;
|
|
dst->childCount[0] = 0;
|
|
insertIndex = 0;
|
|
} else {
|
|
if (!dst->num_objects) {
|
|
Sys_Error ("attempt to insert non-root in empty hierarchy");
|
|
}
|
|
insertIndex = hierarchy_insert (dst, src, dstParent, srcRoot, 1);
|
|
}
|
|
// if src is null, then inserting a new object which has no children
|
|
if (src) {
|
|
hierarchy_insert_children (dst, src, insertIndex, srcRoot);
|
|
}
|
|
return insertIndex;
|
|
}
|
|
|
|
uint32_t
|
|
Hierarchy_InsertHierarchy (hierarchy_t *dst, const hierarchy_t *src,
|
|
uint32_t dstParent, uint32_t srcRoot)
|
|
{
|
|
return hierarchy_insertHierarchy (dst, src, dstParent, &srcRoot);
|
|
}
|
|
|
|
static void
|
|
hierarchy_remove_children (hierarchy_t *hierarchy, uint32_t index,
|
|
int delEntities)
|
|
{
|
|
uint32_t childIndex = hierarchy->childIndex[index];
|
|
uint32_t childCount = hierarchy->childCount[index];
|
|
|
|
for (uint32_t i = childCount; i-- > 0; ) {
|
|
hierarchy_remove_children (hierarchy, childIndex + i, delEntities);
|
|
}
|
|
if (delEntities) {
|
|
hierarchy_InvalidateReferences (hierarchy, childIndex, childCount);
|
|
for (uint32_t i = 0; i < childCount; i++) {
|
|
ECS_DelEntity (hierarchy->reg, hierarchy->ent[childIndex + i]);
|
|
}
|
|
}
|
|
hierarchy_close (hierarchy, childIndex, childCount);
|
|
hierarchy->childCount[index] = 0;
|
|
|
|
if (childCount) {
|
|
hierarchy_UpdateTransformIndices (hierarchy, childIndex, -childCount);
|
|
hierarchy_UpdateChildIndices (hierarchy, index, -childCount);
|
|
}
|
|
if (childIndex < hierarchy->num_objects) {
|
|
hierarchy_UpdateParentIndices (hierarchy, childIndex, -1);
|
|
}
|
|
}
|
|
|
|
void
|
|
Hierarchy_RemoveHierarchy (hierarchy_t *hierarchy, uint32_t index,
|
|
int delEntities)
|
|
{
|
|
if (hierarchy->tree_mode) {
|
|
Sys_Error ("Hierarchy_RemoveHierarchy tree mode not implemented");
|
|
}
|
|
uint32_t parentIndex = hierarchy->parentIndex[index];
|
|
|
|
hierarchy_remove_children (hierarchy, index, delEntities);
|
|
if (delEntities) {
|
|
hierarchy_InvalidateReferences (hierarchy, index, 1);
|
|
ECS_DelEntity (hierarchy->reg, hierarchy->ent[index]);
|
|
}
|
|
hierarchy_close (hierarchy, index, 1);
|
|
|
|
hierarchy_UpdateTransformIndices (hierarchy, index, -1);
|
|
if (parentIndex != nullindex) {
|
|
hierarchy_UpdateChildIndices (hierarchy, parentIndex + 1, -1);
|
|
hierarchy->childCount[parentIndex] -= 1;
|
|
}
|
|
}
|
|
|
|
hierarchy_t *
|
|
Hierarchy_New (ecs_registry_t *reg, uint32_t href_comp,
|
|
const hierarchy_type_t *type, int createRoot)
|
|
{
|
|
hierarchy_t *hierarchy = PR_RESNEW (reg->hierarchies);
|
|
hierarchy->reg = reg;
|
|
hierarchy->href_comp = href_comp;
|
|
|
|
hierarchy->components = 0;
|
|
hierarchy->type = type;
|
|
if (type) {
|
|
hierarchy->components = calloc (hierarchy->type->num_components,
|
|
sizeof (void *));
|
|
}
|
|
|
|
if (createRoot) {
|
|
hierarchy_open (hierarchy, 0, 1);
|
|
hierarchy_init (hierarchy, 0, nullindex, 1, 1);
|
|
}
|
|
|
|
return hierarchy;
|
|
}
|
|
|
|
static void
|
|
hierarchy_delete (hierarchy_t *hierarchy)
|
|
{
|
|
free (hierarchy->ent);
|
|
free (hierarchy->childCount);
|
|
free (hierarchy->childIndex);
|
|
free (hierarchy->parentIndex);
|
|
free (hierarchy->nextIndex);
|
|
free (hierarchy->lastIndex);
|
|
if (hierarchy->type) {
|
|
for (uint32_t i = 0; i < hierarchy->type->num_components; i++) {
|
|
free (hierarchy->components[i]);
|
|
}
|
|
free (hierarchy->components);
|
|
}
|
|
|
|
ecs_registry_t *reg = hierarchy->reg;
|
|
PR_RESFREE (reg->hierarchies, hierarchy);
|
|
}
|
|
|
|
void
|
|
Hierarchy_Delete (hierarchy_t *hierarchy)
|
|
{
|
|
hierarchy_InvalidateReferences (hierarchy, 0, hierarchy->num_objects);
|
|
hierarchy_delete (hierarchy);
|
|
}
|
|
|
|
static uint32_t
|
|
copy_one_node (hierarchy_t *dst, const hierarchy_t *src,
|
|
uint32_t dstIndex, uint32_t childIndex)
|
|
{
|
|
uint32_t srcIndex = dst->parentIndex[dstIndex];
|
|
uint32_t childCount = src->childCount[srcIndex];
|
|
|
|
dst->childIndex[dstIndex] = childIndex;
|
|
dst->childCount[dstIndex] = childCount;
|
|
dst->ent[dstIndex] = src->ent[srcIndex];
|
|
if (dst->type) {
|
|
for (uint32_t i = 0; i < dst->type->num_components; i++) {
|
|
Component_CopyElements (&dst->type->components[i],
|
|
dst->components[i], dstIndex,
|
|
src->components[i], srcIndex, 1);
|
|
}
|
|
}
|
|
return srcIndex;
|
|
}
|
|
|
|
static uint32_t
|
|
queue_tree_nodes (hierarchy_t *dst, const hierarchy_t *src,
|
|
uint32_t queueIndex, uint32_t srcIndex)
|
|
{
|
|
uint32_t srcChild = src->childIndex[srcIndex];
|
|
uint32_t childCount = src->childCount[srcIndex];
|
|
|
|
for (uint32_t i = 0; i < childCount; i++) {
|
|
dst->parentIndex[queueIndex + i] = srcChild;
|
|
srcChild = src->nextIndex[srcChild];
|
|
}
|
|
return childCount;
|
|
}
|
|
|
|
static void
|
|
copy_tree_nodes (hierarchy_t *dst, const hierarchy_t *src,
|
|
uint32_t dstIndex, uint32_t *queueIndex)
|
|
{
|
|
auto ind = copy_one_node (dst, src, dstIndex, *queueIndex);
|
|
auto count = queue_tree_nodes (dst, src, *queueIndex, ind);
|
|
*queueIndex += count;
|
|
}
|
|
|
|
static void
|
|
swap_pointers (void *a, void *b)
|
|
{
|
|
void *t = *(void **)a;
|
|
*(void **)a = *(void **) b;
|
|
*(void **)b = t;
|
|
}
|
|
|
|
void
|
|
Hierarchy_SetTreeMode (hierarchy_t *hierarchy, bool tree_mode)
|
|
{
|
|
if (!hierarchy->tree_mode == !tree_mode) {
|
|
// no change
|
|
return;
|
|
}
|
|
hierarchy->tree_mode = tree_mode;
|
|
if (tree_mode) {
|
|
// switching from a cononical hierarchy to tree mode, noed only to
|
|
// ensure next/last indices are correct
|
|
|
|
// root node has no siblings
|
|
hierarchy->nextIndex[0] = nullindex;
|
|
for (uint32_t i = 0; i < hierarchy->num_objects; i++) {
|
|
uint32_t count = hierarchy->childCount[i];
|
|
uint32_t child = hierarchy->childIndex[i];
|
|
for (uint32_t j = 0; count && j < count - 1; j++) {
|
|
hierarchy->nextIndex[child + j] = child + j + 1;
|
|
}
|
|
hierarchy->lastIndex[i] = count ? child + count - 1 : nullindex;
|
|
if (count) {
|
|
hierarchy->nextIndex[hierarchy->lastIndex[i]] = nullindex;
|
|
} else {
|
|
hierarchy->childIndex[i] = nullindex;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
auto src = hierarchy;
|
|
auto tmp = Hierarchy_New (src->reg, src->href_comp, src->type, 0);
|
|
Hierarchy_Reserve (tmp, src->num_objects);
|
|
tmp->num_objects = src->num_objects;
|
|
|
|
// treat parentIndex as a queue for breadth-first traversal
|
|
tmp->parentIndex[0] = 0; // start at root of src
|
|
uint32_t queueIndex = 1;
|
|
for (uint32_t i = 0; i < src->num_objects; i++) {
|
|
copy_tree_nodes (tmp, src, i, &queueIndex);
|
|
}
|
|
tmp->parentIndex[0] = nullindex;
|
|
for (uint32_t i = 0; i < src->num_objects; i++) {
|
|
for (uint32_t j = 0; j < tmp->childCount[i]; j++) {
|
|
tmp->parentIndex[tmp->childIndex[i] + j] = i;
|
|
}
|
|
}
|
|
auto href_comp = src->href_comp;
|
|
for (uint32_t i = 0; i < src->num_objects; i++) {
|
|
hierref_t *ref = Ent_GetComponent (tmp->ent[i], href_comp, tmp->reg);
|
|
ref->index = i;
|
|
}
|
|
|
|
swap_pointers (&tmp->ent, &src->ent);
|
|
swap_pointers (&tmp->childCount, &src->childCount);
|
|
swap_pointers (&tmp->childIndex, &src->childIndex);
|
|
swap_pointers (&tmp->parentIndex, &src->parentIndex);
|
|
swap_pointers (&tmp->nextIndex, &src->nextIndex);
|
|
swap_pointers (&tmp->lastIndex, &src->lastIndex);
|
|
if (src->type) {
|
|
for (uint32_t i = 0; i < src->type->num_components; i++) {
|
|
swap_pointers (&tmp->components[i], &src->components[i]);
|
|
}
|
|
}
|
|
hierarchy_delete (tmp);
|
|
}
|
|
|
|
hierarchy_t *
|
|
Hierarchy_Copy (ecs_registry_t *dstReg, uint32_t href_comp,
|
|
const hierarchy_t *src)
|
|
{
|
|
if (src->tree_mode) {
|
|
Sys_Error ("Hierarchy_Copy tree mode not implemented");
|
|
}
|
|
hierarchy_t *dst = Hierarchy_New (dstReg, href_comp, src->type, 0);
|
|
size_t count = src->num_objects;
|
|
|
|
Hierarchy_Reserve (dst, count);
|
|
|
|
for (size_t i = 0; i < count; i++) {
|
|
dst->ent[i] = ECS_NewEntity (dstReg);
|
|
hierref_t *ref = Ent_AddComponent (dst->ent[i], href_comp, dstReg);
|
|
ref->hierarchy = dst;
|
|
ref->index = i;
|
|
}
|
|
|
|
Component_CopyElements (&childCount_component,
|
|
dst->childCount, 0, src->childCount, 0, count);
|
|
Component_CopyElements (&childIndex_component,
|
|
dst->childIndex, 0, src->childIndex, 0, count);
|
|
Component_CopyElements (&parentIndex_component,
|
|
dst->parentIndex, 0, src->parentIndex, 0, count);
|
|
Component_CopyElements (&nextIndex_component,
|
|
dst->nextIndex, 0, src->nextIndex, 0, count);
|
|
Component_CopyElements (&lastIndex_component,
|
|
dst->lastIndex, 0, src->lastIndex, 0, count);
|
|
if (dst->type) {
|
|
for (uint32_t i = 0; i < dst->type->num_components; i++) {
|
|
Component_CopyElements (&dst->type->components[i],
|
|
dst->components[i], 0,
|
|
src->components[i], 0, count);
|
|
}
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
hierref_t
|
|
Hierarchy_SetParent (hierarchy_t *dst, uint32_t dstParent,
|
|
hierarchy_t *src, uint32_t srcRoot)
|
|
{
|
|
if (src->tree_mode) {
|
|
Sys_Error ("Hierarchy_SetParent tree mode not implemented");
|
|
}
|
|
hierref_t r = {};
|
|
if (dst && dstParent != nullindex) {
|
|
if (dst->type != src->type) {
|
|
Sys_Error ("Can't set parent in hierarchy of different type");
|
|
}
|
|
} else {
|
|
if (!srcRoot) {
|
|
r.hierarchy = src;
|
|
r.index = 0;
|
|
return r;
|
|
}
|
|
dst = Hierarchy_New (src->reg, src->href_comp, src->type, 0);
|
|
}
|
|
r.hierarchy = dst;
|
|
r.index = hierarchy_insertHierarchy (dst, src, dstParent, &srcRoot);
|
|
Hierarchy_RemoveHierarchy (src, srcRoot, 0);
|
|
if (!src->num_objects) {
|
|
Hierarchy_Delete (src);
|
|
}
|
|
return r;
|
|
}
|
|
|
|
void
|
|
Hierref_DestroyComponent (void *href)
|
|
{
|
|
hierref_t ref = *(hierref_t *) href;
|
|
if (ref.hierarchy) {
|
|
ref.hierarchy->ent[ref.index] = -1;
|
|
Hierarchy_RemoveHierarchy (ref.hierarchy, ref.index, 1);
|
|
if (!ref.hierarchy->num_objects) {
|
|
Hierarchy_Delete (ref.hierarchy);
|
|
}
|
|
}
|
|
}
|