/* 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 #endif #ifdef HAVE_STRINGS_H # include #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); } } }