|  | // flecs v4.1.1 | 
        
          |  | // raylib 5.5 | 
        
          |  | // raygui 4.0 | 
        
          |  |  | 
        
          |  | // about 97 lines for transform 3d hierarchy functions. | 
        
          |  |  | 
        
          |  | #include <stdio.h> | 
        
          |  | #include "raylib.h" | 
        
          |  | #include "raymath.h" | 
        
          |  | #include "flecs.h" | 
        
          |  |  | 
        
          |  | #define RAYGUI_IMPLEMENTATION | 
        
          |  | #include "raygui.h" | 
        
          |  |  | 
        
          |  | // Transform3D component | 
        
          |  | typedef struct { | 
        
          |  | Vector3 position;         // Local position | 
        
          |  | Quaternion rotation;      // Local rotation | 
        
          |  | Vector3 scale;            // Local scale | 
        
          |  | Matrix localMatrix;       // Local transform matrix | 
        
          |  | Matrix worldMatrix;       // World transform matrix | 
        
          |  | bool isDirty;             // Flag to indicate if transform needs updating | 
        
          |  | } Transform3D; | 
        
          |  | ECS_COMPONENT_DECLARE(Transform3D); | 
        
          |  |  | 
        
          |  | // Pointer component for raylib Model | 
        
          |  | typedef struct { | 
        
          |  | Model* model;             // Pointer to Model | 
        
          |  | } model_component_t; | 
        
          |  | ECS_COMPONENT_DECLARE(model_component_t); | 
        
          |  |  | 
        
          |  | typedef struct { | 
        
          |  | ecs_entity_t id;  // Entity to edit (e.g., cube with CubeWire) | 
        
          |  | int selectedIndex; // Index of the selected entity in the list | 
        
          |  | // Later: Add fields for other GUI controls | 
        
          |  | } transform_3d_gui_t; | 
        
          |  | ECS_COMPONENT_DECLARE(transform_3d_gui_t); | 
        
          |  |  | 
        
          |  | typedef struct { | 
        
          |  | bool isMovementMode; | 
        
          |  | bool tabPressed; | 
        
          |  | bool moveForward; | 
        
          |  | bool moveBackward; | 
        
          |  | bool moveLeft; | 
        
          |  | bool moveRight; | 
        
          |  | } player_input_t; | 
        
          |  | ECS_COMPONENT_DECLARE(player_input_t); | 
        
          |  |  | 
        
          |  | typedef struct { | 
        
          |  | Camera3D camera; | 
        
          |  | } main_context_t; | 
        
          |  | ECS_COMPONENT_DECLARE(main_context_t); | 
        
          |  |  | 
        
          |  | void transform_3D_gui_list_system(ecs_iter_t *it); | 
        
          |  |  | 
        
          |  | // Helper function to update a single transform | 
        
          |  | void UpdateTransform(ecs_world_t *world, ecs_entity_t entity, Transform3D *transform) { | 
        
          |  | // Get parent entity | 
        
          |  | ecs_entity_t parent = ecs_get_parent(world, entity); | 
        
          |  | // const char *name = ecs_get_name(world, entity) ? ecs_get_name(world, entity) : "(unnamed)"; //check name for debug | 
        
          |  | bool parentIsDirty = false; | 
        
          |  |  | 
        
          |  | // Check if parent is dirty | 
        
          |  | if (parent && ecs_is_valid(world, parent)) { | 
        
          |  | const Transform3D *parent_transform = ecs_get(world, parent, Transform3D); | 
        
          |  | if (parent_transform && parent_transform->isDirty) { | 
        
          |  | parentIsDirty = true; | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // Skip update if neither this transform nor its parent is dirty | 
        
          |  | if (!transform->isDirty && !parentIsDirty) { | 
        
          |  | // printf("Skipping update for %s (not dirty)\n", name); | 
        
          |  | return; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // Compute local transform | 
        
          |  | Matrix translation = MatrixTranslate(transform->position.x, transform->position.y, transform->position.z); | 
        
          |  | Matrix rotation = QuaternionToMatrix(transform->rotation); | 
        
          |  | Matrix scaling = MatrixScale(transform->scale.x, transform->scale.y, transform->scale.z); | 
        
          |  | transform->localMatrix = MatrixMultiply(scaling, MatrixMultiply(rotation, translation)); | 
        
          |  |  | 
        
          |  | if (!parent || !ecs_is_valid(world, parent)) { | 
        
          |  | // Root entity: world matrix = local matrix | 
        
          |  | transform->worldMatrix = transform->localMatrix; | 
        
          |  | //   printf("Root %s position (%.2f, %.2f, %.2f)\n", name, transform->position.x, transform->position.y, transform->position.z); | 
        
          |  | } else { | 
        
          |  | // Child entity: world matrix = local matrix * parent world matrix | 
        
          |  | const Transform3D *parent_transform = ecs_get(world, parent, Transform3D); | 
        
          |  | if (!parent_transform) { | 
        
          |  | //   printf("Error: Parent %s lacks Transform3D for %s\n", | 
        
          |  | //          ecs_get_name(world, parent) ? ecs_get_name(world, parent) : "(unnamed)", name); | 
        
          |  | transform->worldMatrix = transform->localMatrix; | 
        
          |  | return; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // Validate parent world matrix | 
        
          |  | float px = parent_transform->worldMatrix.m12; | 
        
          |  | float py = parent_transform->worldMatrix.m13; | 
        
          |  | float pz = parent_transform->worldMatrix.m14; | 
        
          |  | if (fabs(px) > 1e6 || fabs(py) > 1e6 || fabs(pz) > 1e6) { | 
        
          |  | //   printf("Error: Invalid parent %s world pos (%.2f, %.2f, %.2f) for %s\n", | 
        
          |  | //          ecs_get_name(world, parent) ? ecs_get_name(world, parent) : "(unnamed)", | 
        
          |  | //          px, py, pz, name); | 
        
          |  | transform->worldMatrix = transform->localMatrix; | 
        
          |  | return; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // Compute world matrix | 
        
          |  | transform->worldMatrix = MatrixMultiply(transform->localMatrix, parent_transform->worldMatrix); | 
        
          |  | // Extract world position | 
        
          |  | float wx = transform->worldMatrix.m12; | 
        
          |  | float wy = transform->worldMatrix.m13; | 
        
          |  | float wz = transform->worldMatrix.m14; | 
        
          |  |  | 
        
          |  | // Debug output | 
        
          |  | //   const char *parent_name = ecs_get_name(world, parent) ? ecs_get_name(world, parent) : "(unnamed)"; | 
        
          |  | //   printf("Child %s (ID: %llu), parent %s (ID: %llu)\n", | 
        
          |  | //          name, (unsigned long long)entity, parent_name, (unsigned long long)parent); | 
        
          |  | //   printf("Child %s position (%.2f, %.2f, %.2f), parent %s world pos (%.2f, %.2f, %.2f), world pos (%.2f, %.2f, %.2f)\n", | 
        
          |  | //          name, transform->position.x, transform->position.y, transform->position.z, | 
        
          |  | //          parent_name, px, py, pz, wx, wy, wz); | 
        
          |  | } | 
        
          |  | // Mark children as dirty to ensure they update in the next frame | 
        
          |  | ecs_iter_t it = ecs_children(world, entity); | 
        
          |  | while (ecs_children_next(&it)) { | 
        
          |  | for (int i = 0; i < it.count; i++) { | 
        
          |  | Transform3D *child_transform = ecs_get_mut(world, it.entities[i], Transform3D); | 
        
          |  | if (child_transform) { | 
        
          |  | child_transform->isDirty = true; | 
        
          |  | //ecs_set(world, it.entities[i], Transform3D, *child_transform); | 
        
          |  | } | 
        
          |  | } | 
        
          |  | } | 
        
          |  | // Reset isDirty after updating | 
        
          |  | transform->isDirty = false; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // Function to update a single entity and its descendants | 
        
          |  | void UpdateChildTransformOnly(ecs_world_t *world, ecs_entity_t entity) { | 
        
          |  | Transform3D *transform = ecs_get_mut(world, entity, Transform3D); | 
        
          |  | if (!transform) return; | 
        
          |  | // Update the entity's transform | 
        
          |  | UpdateTransform(world, entity, transform); | 
        
          |  | // ecs_modified(world, entity, Transform3D); | 
        
          |  |  | 
        
          |  | // Recursively update descendants | 
        
          |  | // note delay in update to sync. | 
        
          |  | // if comment this will delay sync since frame render tick. | 
        
          |  | ecs_iter_t it = ecs_children(world, entity); | 
        
          |  | while (ecs_children_next(&it)) { | 
        
          |  | for (int i = 0; i < it.count; i++) { | 
        
          |  | UpdateChildTransformOnly(world, it.entities[i]); | 
        
          |  | } | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // System to process specific child entities (e.g., triggered by GUI) | 
        
          |  | void UpdateTransform3DSystem(ecs_iter_t *it) { | 
        
          |  | Transform3D *transform3d = ecs_field(it, Transform3D, 0); | 
        
          |  | for (int i = 0; i < it->count; i++) { | 
        
          |  | // ecs_entity_t entity = transform3d[i].id; | 
        
          |  | ecs_entity_t entity = it->entities[i]; | 
        
          |  | if (ecs_is_valid(it->world, entity) && ecs_has(it->world, entity, Transform3D)) { | 
        
          |  | UpdateChildTransformOnly(it->world, entity); | 
        
          |  | } | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // Render begin system | 
        
          |  | void RenderBeginSystem(ecs_iter_t *it) { | 
        
          |  | // printf("RenderBeginSystem\n"); | 
        
          |  | BeginDrawing(); | 
        
          |  | ClearBackground(RAYWHITE); | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // Render begin system | 
        
          |  | void BeginCamera3DSystem(ecs_iter_t *it) { | 
        
          |  | // printf("BeginCamera3DSystem\n"); | 
        
          |  | main_context_t *main_context = ecs_field(it, main_context_t, 0); // Singleton | 
        
          |  | if (!main_context) return; | 
        
          |  | BeginMode3D(main_context->camera); | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // Camera3d system for 3d model | 
        
          |  | void Camera3DSystem(ecs_iter_t *it) { | 
        
          |  | Transform3D *t = ecs_field(it, Transform3D, 0); | 
        
          |  | model_component_t *m = ecs_field(it, model_component_t, 1); | 
        
          |  |  | 
        
          |  | for (int i = 0; i < it->count; i++) { | 
        
          |  | ecs_entity_t entity = it->entities[i]; | 
        
          |  | if (!ecs_is_valid(it->world, entity)) { | 
        
          |  | //printf("Skipping invalid entity ID: %llu\n", (unsigned long long)entity); | 
        
          |  | continue; | 
        
          |  | } | 
        
          |  | const char *name = ecs_get_name(it->world, entity) ? ecs_get_name(it->world, entity) : "(unnamed)"; | 
        
          |  | if (!ecs_has(it->world, entity, Transform3D) || !ecs_has(it->world, entity, model_component_t)) { | 
        
          |  | //printf("Skipping entity %s: missing Transform3D or model_component_t\n", name); | 
        
          |  | continue; | 
        
          |  | } | 
        
          |  | // Check for garbage values in world matrix | 
        
          |  | if (fabs(t[i].worldMatrix.m12) > 1e6 || fabs(t[i].worldMatrix.m13) > 1e6 || fabs(t[i].worldMatrix.m14) > 1e6) { | 
        
          |  | // printf("Skipping entity %s: invalid world matrix (%.2f, %.2f, %.2f)\n", | 
        
          |  | //        name, t[i].worldMatrix.m12, t[i].worldMatrix.m13, t[i].worldMatrix.m14); | 
        
          |  | continue; | 
        
          |  | } | 
        
          |  | // printf("Rendering entity %s at world pos (%.2f, %.2f, %.2f)\n", | 
        
          |  | //        name, t[i].worldMatrix.m12, t[i].worldMatrix.m13, t[i].worldMatrix.m14); | 
        
          |  | if (!m[i].model) { | 
        
          |  | // printf("Skipping entity %s: null model\n", name); | 
        
          |  | continue; | 
        
          |  | } | 
        
          |  | m[i].model->transform = t[i].worldMatrix; | 
        
          |  | bool isChild = ecs_has_pair(it->world, entity, EcsChildOf, EcsWildcard); | 
        
          |  | DrawModel(*(m[i].model), (Vector3){0, 0, 0}, 1.0f, isChild ? BLUE : RED); | 
        
          |  | } | 
        
          |  | DrawGrid(10, 1.0f); | 
        
          |  |  | 
        
          |  | // does not work here. | 
        
          |  | // Camera3D *camera = (Camera3D *)ecs_get_ctx(it->world); | 
        
          |  | // if (camera) { | 
        
          |  | //     DrawText(TextFormat("Camera Pos: %.2f, %.2f, %.2f", | 
        
          |  | //                         camera->position.x, camera->position.y, camera->position.z), 10, 90, 20, DARKGRAY); | 
        
          |  | //     DrawText(TextFormat("Entities Rendered: %d", it->count), 10, 110, 20, DARKGRAY); | 
        
          |  | // } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | void EndCamera3DSystem(ecs_iter_t *it) { | 
        
          |  | //printf("EndCamera3DSystem\n"); | 
        
          |  | main_context_t *main_context = ecs_field(it, main_context_t, 0); // Singleton | 
        
          |  | if (!main_context) return; | 
        
          |  | EndMode3D(); | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // Render end system | 
        
          |  | void EndRenderSystem(ecs_iter_t *it) { | 
        
          |  | //printf("EndRenderSystem\n"); | 
        
          |  | EndDrawing(); | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // Input handling system | 
        
          |  | void user_input_system(ecs_iter_t *it) { | 
        
          |  |  | 
        
          |  | player_input_t *pi_ctx = ecs_field(it, player_input_t, 0); | 
        
          |  | transform_3d_gui_t *transform_3d_gui = ecs_field(it, transform_3d_gui_t, 1); | 
        
          |  | Transform3D *t = ecs_field(it, Transform3D, 2); | 
        
          |  | float dt = GetFrameTime(); | 
        
          |  | //test user input | 
        
          |  | for (int i = 0; i < it->count; i++) { | 
        
          |  | const char *name = ecs_get_name(it->world, it->entities[i]); | 
        
          |  | if (name) { | 
        
          |  | bool isFound = false; | 
        
          |  | // if (strcmp(name, "NodeParent") == 0) { | 
        
          |  | if (it->entities[i] == transform_3d_gui->id) { | 
        
          |  |  | 
        
          |  | bool wasModified = false; | 
        
          |  |  | 
        
          |  | if (IsKeyPressed(KEY_TAB)) { | 
        
          |  | pi_ctx->isMovementMode = !pi_ctx->isMovementMode; | 
        
          |  | // printf("Toggled mode to: %s\n", pi_ctx->isMovementMode ? "Movement" : "Rotation"); | 
        
          |  | } | 
        
          |  |  | 
        
          |  | if (pi_ctx->isMovementMode) { | 
        
          |  | if (IsKeyDown(KEY_W)){t[i].position.z -= 2.0f * dt;wasModified = true;} | 
        
          |  | if (IsKeyDown(KEY_S)){t[i].position.z += 2.0f * dt;wasModified = true;} | 
        
          |  | if (IsKeyDown(KEY_A)){t[i].position.x -= 2.0f * dt;wasModified = true;} | 
        
          |  | if (IsKeyDown(KEY_D)){t[i].position.x += 2.0f * dt;wasModified = true;} | 
        
          |  | } else { | 
        
          |  | float rotateSpeed = 90.0f; | 
        
          |  | if (IsKeyDown(KEY_Q)) { | 
        
          |  | Quaternion rot = QuaternionFromAxisAngle((Vector3){0, 1, 0}, DEG2RAD * rotateSpeed * dt); | 
        
          |  | t[i].rotation = QuaternionMultiply(t[i].rotation, rot); | 
        
          |  | wasModified = true; | 
        
          |  | } | 
        
          |  | if (IsKeyDown(KEY_E)) { | 
        
          |  | Quaternion rot = QuaternionFromAxisAngle((Vector3){0, 1, 0}, -DEG2RAD * rotateSpeed * dt); | 
        
          |  | t[i].rotation = QuaternionMultiply(t[i].rotation, rot); | 
        
          |  | wasModified = true; | 
        
          |  | } | 
        
          |  | if (IsKeyDown(KEY_W)) { | 
        
          |  | Quaternion rot = QuaternionFromAxisAngle((Vector3){1, 0, 0}, DEG2RAD * rotateSpeed * dt); | 
        
          |  | t[i].rotation = QuaternionMultiply(t[i].rotation, rot); | 
        
          |  | wasModified = true; | 
        
          |  | } | 
        
          |  | if (IsKeyDown(KEY_S)) { | 
        
          |  | Quaternion rot = QuaternionFromAxisAngle((Vector3){1, 0, 0}, -DEG2RAD * rotateSpeed * dt); | 
        
          |  | t[i].rotation = QuaternionMultiply(t[i].rotation, rot); | 
        
          |  | wasModified = true; | 
        
          |  | } | 
        
          |  | if (IsKeyDown(KEY_A)) { | 
        
          |  | Quaternion rot = QuaternionFromAxisAngle((Vector3){0, 0, 1}, DEG2RAD * rotateSpeed * dt); | 
        
          |  | t[i].rotation = QuaternionMultiply(t[i].rotation, rot); | 
        
          |  | wasModified = true; | 
        
          |  | } | 
        
          |  | if (IsKeyDown(KEY_D)) { | 
        
          |  | Quaternion rot = QuaternionFromAxisAngle((Vector3){0, 0, 1}, -DEG2RAD * rotateSpeed * dt); | 
        
          |  | t[i].rotation = QuaternionMultiply(t[i].rotation, rot); | 
        
          |  | wasModified = true; | 
        
          |  | } | 
        
          |  | } | 
        
          |  | if (IsKeyPressed(KEY_R)) { | 
        
          |  | t[i].position = (Vector3){0.0f, 0.0f, 0.0f}; | 
        
          |  | t[i].rotation = QuaternionIdentity(); | 
        
          |  | t[i].scale = (Vector3){1.0f, 1.0f, 1.0f}; | 
        
          |  | wasModified = true; | 
        
          |  | } | 
        
          |  | if (wasModified) { | 
        
          |  | t[i].isDirty = true; | 
        
          |  | printf("Marked %s as dirty\n", name); | 
        
          |  | } | 
        
          |  | } | 
        
          |  | } | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // render 2d | 
        
          |  | void render2d_hud_system(ecs_iter_t *it){ | 
        
          |  | player_input_t *player_input = ecs_field(it, player_input_t, 0); | 
        
          |  |  | 
        
          |  | DrawText(player_input->isMovementMode ? "Mode: Movement (WASD)" : "Mode: Rotation (QWE/ASD)", 10, 30, 20, DARKGRAY); | 
        
          |  | DrawText("Tab: Toggle Mode | R: Reset", 10, 10, 20, DARKGRAY); | 
        
          |  | DrawFPS(10, 50); | 
        
          |  |  | 
        
          |  | ecs_query_t *query = ecs_query(it->world, { | 
        
          |  | .terms = {{ ecs_id(Transform3D) }} | 
        
          |  | }); | 
        
          |  |  | 
        
          |  | int entity_count = 0; | 
        
          |  | ecs_iter_t transform_it = ecs_query_iter(it->world, query); | 
        
          |  | while (ecs_query_next(&transform_it)) { | 
        
          |  | entity_count += transform_it.count; | 
        
          |  | } | 
        
          |  | DrawText(TextFormat("Entity Count: %d", entity_count), 10, 70, 20, DARKGRAY); | 
        
          |  |  | 
        
          |  | transform_it = ecs_query_iter(it->world, query); | 
        
          |  | int row_count = 0; | 
        
          |  | // note this will not over lap in case entity transform 3d since it has parrent and child add together | 
        
          |  | // note the position is zero by default for some reason | 
        
          |  | while (ecs_query_next(&transform_it)) { | 
        
          |  | Transform3D *t = ecs_field(&transform_it, Transform3D, 0); | 
        
          |  | for (int j = 0; j < transform_it.count; j++) { | 
        
          |  |  | 
        
          |  | DrawText(TextFormat("Entity %s Pos: %.2f, %.2f, %.2f", | 
        
          |  | ecs_get_name(it->world, transform_it.entities[j]) ? ecs_get_name(it->world, transform_it.entities[j]) : "unnamed", | 
        
          |  | t[j].position.x, t[j].position.y, t[j].position.z), 10, 90 + row_count * 20, 20, DARKGRAY); | 
        
          |  | row_count++; | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // main | 
        
          |  | int main(void) { | 
        
          |  | InitWindow(800, 600, "Transform Hierarchy with Flecs v4.x"); | 
        
          |  | SetTargetFPS(60); | 
        
          |  |  | 
        
          |  | ecs_world_t *world = ecs_init(); | 
        
          |  |  | 
        
          |  | ECS_COMPONENT_DEFINE(world, Transform3D); | 
        
          |  | ECS_COMPONENT_DEFINE(world, model_component_t); | 
        
          |  | ECS_COMPONENT_DEFINE(world, player_input_t); | 
        
          |  | ECS_COMPONENT_DEFINE(world, transform_3d_gui_t); | 
        
          |  | ECS_COMPONENT_DEFINE(world, main_context_t); | 
        
          |  |  | 
        
          |  | // Define custom phases | 
        
          |  | ecs_entity_t PreLogicUpdatePhase = ecs_new_w_id(world, EcsPhase); | 
        
          |  | ecs_entity_t LogicUpdatePhase = ecs_new_w_id(world, EcsPhase); | 
        
          |  | ecs_entity_t BeginRenderPhase = ecs_new_w_id(world, EcsPhase); | 
        
          |  | ecs_entity_t BeginCamera3DPhase = ecs_new_w_id(world, EcsPhase); | 
        
          |  | ecs_entity_t Camera3DPhase = ecs_new_w_id(world, EcsPhase); | 
        
          |  | ecs_entity_t EndCamera3DPhase = ecs_new_w_id(world, EcsPhase); | 
        
          |  | ecs_entity_t RenderPhase = ecs_new_w_id(world, EcsPhase); | 
        
          |  | ecs_entity_t EndRenderPhase = ecs_new_w_id(world, EcsPhase); | 
        
          |  |  | 
        
          |  | // order phase not doing correct | 
        
          |  | ecs_add_pair(world, PreLogicUpdatePhase, EcsDependsOn, EcsPreUpdate); // start game logics | 
        
          |  | ecs_add_pair(world, LogicUpdatePhase, EcsDependsOn, PreLogicUpdatePhase); // start game logics | 
        
          |  | ecs_add_pair(world, BeginRenderPhase, EcsDependsOn, LogicUpdatePhase); // BeginDrawing | 
        
          |  | ecs_add_pair(world, BeginCamera3DPhase, EcsDependsOn, BeginRenderPhase); // EcsOnUpdate, BeginMode3D | 
        
          |  | ecs_add_pair(world, Camera3DPhase, EcsDependsOn, BeginCamera3DPhase); // 3d model only | 
        
          |  | ecs_add_pair(world, EndCamera3DPhase, EcsDependsOn, Camera3DPhase); // EndMode3D | 
        
          |  | ecs_add_pair(world, RenderPhase, EcsDependsOn, EndCamera3DPhase); // 2D only | 
        
          |  | ecs_add_pair(world, EndRenderPhase, EcsDependsOn, RenderPhase); // render to screen | 
        
          |  |  | 
        
          |  | // user input | 
        
          |  | ecs_system(world, { | 
        
          |  | .entity = ecs_entity(world, { .name = "user_input_system", .add = ecs_ids(ecs_dependson(LogicUpdatePhase)) }), | 
        
          |  | .query.terms = { | 
        
          |  | { .id = ecs_id(player_input_t), .src.id = ecs_id(player_input_t) }, // Singleton | 
        
          |  | { .id = ecs_id(transform_3d_gui_t), .src.id = ecs_id(transform_3d_gui_t) }, // Singleton | 
        
          |  | { .id = ecs_id(Transform3D), .src.id = EcsSelf }, | 
        
          |  | }, | 
        
          |  | .callback = user_input_system | 
        
          |  | }); | 
        
          |  | // | 
        
          |  | ecs_system(world, { | 
        
          |  | .entity = ecs_entity(world, { .name = "render2d_hud_system", .add = ecs_ids(ecs_dependson(RenderPhase)) }), | 
        
          |  | .query.terms = { | 
        
          |  | { .id = ecs_id(player_input_t), .src.id = ecs_id(player_input_t) } // Singleton | 
        
          |  | }, | 
        
          |  | .callback = render2d_hud_system | 
        
          |  | }); | 
        
          |  | // | 
        
          |  | ecs_system(world, { | 
        
          |  | .entity = ecs_entity(world, { | 
        
          |  | .name = "UpdateTransform3DSystem", | 
        
          |  | .add = ecs_ids(ecs_dependson(PreLogicUpdatePhase)) | 
        
          |  | }), | 
        
          |  | .query.terms = { | 
        
          |  | { .id = ecs_id(Transform3D), .src.id = EcsSelf } | 
        
          |  | }, | 
        
          |  | .callback = UpdateTransform3DSystem | 
        
          |  | }); | 
        
          |  | // | 
        
          |  | ecs_system(world,{ | 
        
          |  | .entity = ecs_entity(world, { .name = "RenderBeginSystem", | 
        
          |  | .add = ecs_ids(ecs_dependson(BeginRenderPhase)) | 
        
          |  | }), | 
        
          |  | .callback = RenderBeginSystem | 
        
          |  | }); | 
        
          |  | //note this has be in order of the ECS since push into array. From I guess. | 
        
          |  | // begin camera 3d | 
        
          |  | ecs_system(world, { | 
        
          |  | .entity = ecs_entity(world, { | 
        
          |  | .name = "BeginCamera3DSystem", | 
        
          |  | .add = ecs_ids(ecs_dependson(BeginCamera3DPhase)) | 
        
          |  | }), | 
        
          |  | .query.terms = { | 
        
          |  | { .id = ecs_id(main_context_t), .src.id = ecs_id(main_context_t) } // Singleton | 
        
          |  | }, | 
        
          |  | .callback = BeginCamera3DSystem | 
        
          |  | }); | 
        
          |  | // render 3d mesh | 
        
          |  | ecs_system(world, { | 
        
          |  | .entity = ecs_entity(world, { | 
        
          |  | .name = "Camera3DSystem", | 
        
          |  | .add = ecs_ids(ecs_dependson(Camera3DPhase)) | 
        
          |  | }), | 
        
          |  | .query.terms = { | 
        
          |  | { .id = ecs_id(Transform3D), .src.id = EcsSelf }, | 
        
          |  | { .id = ecs_id(model_component_t), .src.id = EcsSelf } | 
        
          |  | }, | 
        
          |  | .callback = Camera3DSystem | 
        
          |  | }); | 
        
          |  | // end camera 3d | 
        
          |  | ecs_system(world, { | 
        
          |  | .entity = ecs_entity(world, { | 
        
          |  | .name = "EndCamera3DSystem", | 
        
          |  | .add = ecs_ids(ecs_dependson(EndCamera3DPhase)) | 
        
          |  | }), | 
        
          |  | .query.terms = { | 
        
          |  | { .id = ecs_id(main_context_t), .src.id = ecs_id(main_context_t) } // Singleton | 
        
          |  | }, | 
        
          |  | .callback = EndCamera3DSystem | 
        
          |  | }); | 
        
          |  | // | 
        
          |  | ecs_system(world, { | 
        
          |  | .entity = ecs_entity(world, { | 
        
          |  | .name = "EndRenderSystem", | 
        
          |  | .add = ecs_ids(ecs_dependson(EndRenderPhase)) | 
        
          |  | }), | 
        
          |  | .callback = EndRenderSystem | 
        
          |  | }); | 
        
          |  | // Register GUI list system in the 2D rendering phase | 
        
          |  | ecs_system(world, { | 
        
          |  | .entity = ecs_entity(world, { .name = "transform_3D_gui_list_system", .add = ecs_ids(ecs_dependson(LogicUpdatePhase)) }), | 
        
          |  | .query.terms = { | 
        
          |  | { .id = ecs_id(transform_3d_gui_t), .src.id = ecs_id(transform_3d_gui_t) } // Singleton | 
        
          |  | }, | 
        
          |  | .callback = transform_3D_gui_list_system | 
        
          |  | }); | 
        
          |  | // camera | 
        
          |  | Camera3D camera = { | 
        
          |  | .position = (Vector3){10.0f, 10.0f, 10.0f}, | 
        
          |  | .target = (Vector3){0.0f, 0.0f, 0.0f}, | 
        
          |  | .up = (Vector3){0.0f, 1.0f, 0.0f}, | 
        
          |  | .fovy = 45.0f, | 
        
          |  | .projection = CAMERA_PERSPECTIVE | 
        
          |  | }; | 
        
          |  | ecs_singleton_set(world, main_context_t, { | 
        
          |  | .camera = camera | 
        
          |  | }); | 
        
          |  | // player input | 
        
          |  | ecs_singleton_set(world, player_input_t, { | 
        
          |  | .isMovementMode=true, | 
        
          |  | .tabPressed=false | 
        
          |  | }); | 
        
          |  |  | 
        
          |  | Model cube = LoadModelFromMesh(GenMeshCube(1.0f, 1.0f, 1.0f)); | 
        
          |  |  | 
        
          |  | ecs_entity_t node1 = ecs_entity(world, { | 
        
          |  | .name = "NodeParent" | 
        
          |  | }); | 
        
          |  |  | 
        
          |  | ecs_set(world, node1, Transform3D, { | 
        
          |  | .position = (Vector3){0.0f, 0.0f, 0.0f}, | 
        
          |  | .rotation = QuaternionIdentity(), | 
        
          |  | .scale = (Vector3){1.0f, 1.0f, 1.0f}, | 
        
          |  | .localMatrix = MatrixIdentity(), | 
        
          |  | .worldMatrix = MatrixIdentity(), | 
        
          |  | .isDirty = true | 
        
          |  | }); | 
        
          |  | ecs_set(world, node1, model_component_t, {&cube}); | 
        
          |  | // printf("Node1 entity ID: %llu (%s)\n", (unsigned long long)node1, ecs_get_name(world, node1)); | 
        
          |  | // printf("- Node1 valid: %d, has Transform3D: %d\n", ecs_is_valid(world, node1), ecs_has(world, node1, Transform3D)); | 
        
          |  |  | 
        
          |  | ecs_entity_t node2 = ecs_entity(world, { | 
        
          |  | .name = "NodeChild", | 
        
          |  | .parent = node1 | 
        
          |  | }); | 
        
          |  | ecs_set(world, node2, Transform3D, { | 
        
          |  | .position = (Vector3){2.0f, 0.0f, 0.0f}, | 
        
          |  | .rotation = QuaternionIdentity(), | 
        
          |  | .scale = (Vector3){0.5f, 0.5f, 0.5f}, | 
        
          |  | .localMatrix = MatrixIdentity(), | 
        
          |  | .worldMatrix = MatrixIdentity(), | 
        
          |  | .isDirty = true | 
        
          |  | }); | 
        
          |  | ecs_set(world, node2, model_component_t, {&cube}); | 
        
          |  | // printf("Node2 entity ID: %llu (%s)\n", (unsigned long long)node2, ecs_get_name(world, node2)); | 
        
          |  | // printf("- Node2 valid: %d, has Transform3D: %d, parent: %s\n", | 
        
          |  | //        ecs_is_valid(world, node2), ecs_has(world, node2, Transform3D), | 
        
          |  | //        ecs_get_name(world, ecs_get_parent(world, node2))); | 
        
          |  |  | 
        
          |  | ecs_entity_t node3 = ecs_entity(world, { | 
        
          |  | .name = "Node3", | 
        
          |  | .parent = node1 | 
        
          |  | }); | 
        
          |  | ecs_set(world, node3, Transform3D, { | 
        
          |  | .position = (Vector3){2.0f, 0.0f, 2.0f}, | 
        
          |  | .rotation = QuaternionIdentity(), | 
        
          |  | .scale = (Vector3){0.5f, 0.5f, 0.5f}, | 
        
          |  | .localMatrix = MatrixIdentity(), | 
        
          |  | .worldMatrix = MatrixIdentity(), | 
        
          |  | .isDirty = true | 
        
          |  | }); | 
        
          |  | ecs_set(world, node3, model_component_t, {&cube}); | 
        
          |  | // printf("Node3 entity ID: %llu (%s)\n", (unsigned long long)node3, ecs_get_name(world, node3)); | 
        
          |  | // printf("- Node3 valid: %d, has Transform3D: %d, parent: %s\n", | 
        
          |  | //      ecs_is_valid(world, node3), ecs_has(world, node3, Transform3D), | 
        
          |  | //      ecs_get_name(world, ecs_get_parent(world, node3))); | 
        
          |  |  | 
        
          |  |  | 
        
          |  | ecs_entity_t node4 = ecs_entity(world, { | 
        
          |  | .name = "NodeGrandchild", | 
        
          |  | .parent = node2 | 
        
          |  | }); | 
        
          |  | ecs_set(world, node4, Transform3D, { | 
        
          |  | .position = (Vector3){1.0f, 0.0f, 1.0f}, | 
        
          |  | .rotation = QuaternionIdentity(), | 
        
          |  | .scale = (Vector3){0.5f, 0.5f, 0.5f}, | 
        
          |  | .localMatrix = MatrixIdentity(), | 
        
          |  | .worldMatrix = MatrixIdentity(), | 
        
          |  | .isDirty = true | 
        
          |  | }); | 
        
          |  | ecs_set(world, node4, model_component_t, {&cube}); | 
        
          |  | // printf("Node4 entity ID: %llu (%s)\n", (unsigned long long)node4, ecs_get_name(world, node4)); | 
        
          |  | // printf("- Node4 valid: %d, has Transform3D: %d, parent: %s\n", | 
        
          |  | //        ecs_is_valid(world, node4), ecs_has(world, node4, Transform3D), | 
        
          |  | //        ecs_get_name(world, ecs_get_parent(world, node4))); | 
        
          |  |  | 
        
          |  | ecs_entity_t node5 = ecs_entity(world, { | 
        
          |  | .name = "Node5" | 
        
          |  | }); | 
        
          |  | ecs_set(world, node5, Transform3D, { | 
        
          |  | .position = (Vector3){1.0f, 0.0f, 1.0f}, | 
        
          |  | .rotation = QuaternionIdentity(), | 
        
          |  | .scale = (Vector3){0.5f, 0.5f, 0.5f}, | 
        
          |  | .localMatrix = MatrixIdentity(), | 
        
          |  | .worldMatrix = MatrixIdentity(), | 
        
          |  | .isDirty = true | 
        
          |  | }); | 
        
          |  | ecs_set(world, node5, model_component_t, {&cube}); | 
        
          |  |  | 
        
          |  | ecs_singleton_set(world, transform_3d_gui_t, { | 
        
          |  | .id = node1  // Reference the id entity | 
        
          |  | }); | 
        
          |  |  | 
        
          |  |  | 
        
          |  | while (!WindowShouldClose()) { | 
        
          |  | ecs_progress(world, 0); | 
        
          |  | } | 
        
          |  |  | 
        
          |  | UnloadModel(cube); | 
        
          |  | ecs_fini(world); | 
        
          |  | CloseWindow(); | 
        
          |  | return 0; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | void transform_3D_gui_list_system(ecs_iter_t *it) { | 
        
          |  | transform_3d_gui_t *gui = ecs_field(it, transform_3d_gui_t, 0); | 
        
          |  |  | 
        
          |  | // Create a query for all entities with Transform3D | 
        
          |  | ecs_query_t *query = ecs_query(it->world, { | 
        
          |  | .terms = {{ ecs_id(Transform3D) }} | 
        
          |  | }); | 
        
          |  |  | 
        
          |  | int entity_count = 0; | 
        
          |  | ecs_iter_t transform_it = ecs_query_iter(it->world, query); | 
        
          |  | while (ecs_query_next(&transform_it)) { | 
        
          |  | entity_count += transform_it.count; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // Allocate buffers for entity names and IDs | 
        
          |  | ecs_entity_t *entity_ids = (ecs_entity_t *)RL_MALLOC(entity_count * sizeof(ecs_entity_t)); | 
        
          |  | char **entity_names = (char **)RL_MALLOC(entity_count * sizeof(char *)); | 
        
          |  | int index = 0; | 
        
          |  |  | 
        
          |  | // Populate entity names and IDs | 
        
          |  | transform_it = ecs_query_iter(it->world, query); | 
        
          |  | while (ecs_query_next(&transform_it)) { | 
        
          |  | for (int j = 0; j < transform_it.count; j++) { | 
        
          |  | const char *name = ecs_get_name(it->world, transform_it.entities[j]); | 
        
          |  | entity_names[index] = (char *)RL_MALLOC(256 * sizeof(char)); | 
        
          |  | snprintf(entity_names[index], 256, "%s", name ? name : "(unnamed)"); | 
        
          |  | entity_ids[index] = transform_it.entities[j]; | 
        
          |  | index++; | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // Create a single string for GuiListView | 
        
          |  | char *name_list = (char *)RL_MALLOC(entity_count * 256 * sizeof(char)); | 
        
          |  | name_list[0] = '\0'; | 
        
          |  | for (int j = 0; j < entity_count; j++) { | 
        
          |  | if (j > 0) strcat(name_list, ";"); | 
        
          |  | strcat(name_list, entity_names[j]); | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // Draw the list view on the right side | 
        
          |  | Rectangle list_rect = {520, 10, 240, 200}; // Reduced height for more controls | 
        
          |  | int scroll_index = 0; | 
        
          |  |  | 
        
          |  | GuiListView(list_rect, name_list, &scroll_index, &gui->selectedIndex); | 
        
          |  |  | 
        
          |  | // Draw transform controls if an entity is selected | 
        
          |  | if (gui->selectedIndex >= 0 && gui->selectedIndex < entity_count && ecs_is_valid(it->world, entity_ids[gui->selectedIndex])) { | 
        
          |  | gui->id = entity_ids[gui->selectedIndex]; | 
        
          |  | Transform3D *transform = ecs_get_mut(it->world, gui->id, Transform3D); | 
        
          |  | bool modified = false; | 
        
          |  |  | 
        
          |  | if (transform) { | 
        
          |  | Rectangle control_rect = {520, 220, 240, 360}; | 
        
          |  | GuiGroupBox(control_rect, "Transform Controls"); | 
        
          |  |  | 
        
          |  | // Position controls | 
        
          |  | GuiLabel((Rectangle){530, 230, 100, 20}, "Position"); | 
        
          |  | float new_pos_x = transform->position.x; | 
        
          |  | float new_pos_y = transform->position.y; | 
        
          |  | float new_pos_z = transform->position.z; | 
        
          |  | GuiSlider((Rectangle){530, 250, 200, 20}, "X", TextFormat("%.2f", new_pos_x), &new_pos_x, -10.0f, 10.0f); | 
        
          |  | GuiSlider((Rectangle){530, 270, 200, 20}, "Y", TextFormat("%.2f", new_pos_y), &new_pos_y, -10.0f, 10.0f); | 
        
          |  | GuiSlider((Rectangle){530, 290, 200, 20}, "Z", TextFormat("%.2f", new_pos_z), &new_pos_z, -10.0f, 10.0f); | 
        
          |  | if (new_pos_x != transform->position.x || new_pos_y != transform->position.y || new_pos_z != transform->position.z) { | 
        
          |  | transform->position.x = new_pos_x; | 
        
          |  | transform->position.y = new_pos_y; | 
        
          |  | transform->position.z = new_pos_z; | 
        
          |  | modified = true; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // Rotation controls (using Euler angles) | 
        
          |  | GuiLabel((Rectangle){530, 310, 100, 20}, "Rotation"); | 
        
          |  | Vector3 euler = QuaternionToEuler(transform->rotation); | 
        
          |  | euler.x = RAD2DEG * euler.x; | 
        
          |  | euler.y = RAD2DEG * euler.y; | 
        
          |  | euler.z = RAD2DEG * euler.z; | 
        
          |  | float new_rot_x = euler.x; | 
        
          |  | float new_rot_y = euler.y; | 
        
          |  | float new_rot_z = euler.z; | 
        
          |  | GuiSlider((Rectangle){530, 330, 200, 20}, "X", TextFormat("%.2f", new_rot_x), &new_rot_x, -180.0f, 180.0f); | 
        
          |  | GuiSlider((Rectangle){530, 350, 200, 20}, "Y", TextFormat("%.2f", new_rot_y), &new_rot_y, -180.0f, 180.0f); | 
        
          |  | GuiSlider((Rectangle){530, 370, 200, 20}, "Z", TextFormat("%.2f", new_rot_z), &new_rot_z, -180.0f, 180.0f); | 
        
          |  | if (new_rot_x != euler.x || new_rot_y != euler.y || new_rot_z != euler.z) { | 
        
          |  | transform->rotation = QuaternionFromEuler(DEG2RAD * new_rot_x, DEG2RAD * new_rot_y, DEG2RAD * new_rot_z); | 
        
          |  | modified = true; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // Scale controls | 
        
          |  | GuiLabel((Rectangle){530, 390, 100, 20}, "Scale"); | 
        
          |  | float new_scale_x = transform->scale.x; | 
        
          |  | float new_scale_y = transform->scale.y; | 
        
          |  | float new_scale_z = transform->scale.z; | 
        
          |  | GuiSlider((Rectangle){530, 410, 200, 20}, "X", TextFormat("%.2f", new_scale_x), &new_scale_x, 0.1f, 5.0f); | 
        
          |  | GuiSlider((Rectangle){530, 430, 200, 20}, "Y", TextFormat("%.2f", new_scale_y), &new_scale_y, 0.1f, 5.0f); | 
        
          |  | GuiSlider((Rectangle){530, 450, 200, 20}, "Z", TextFormat("%.2f", new_scale_z), &new_scale_z, 0.1f, 5.0f); | 
        
          |  | if (new_scale_x != transform->scale.x || new_scale_y != transform->scale.y || new_scale_z != transform->scale.z) { | 
        
          |  | transform->scale.x = new_scale_x; | 
        
          |  | transform->scale.y = new_scale_y; | 
        
          |  | transform->scale.z = new_scale_z; | 
        
          |  | modified = true; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // Mark transform and descendants as dirty if modified | 
        
          |  | if (modified) { | 
        
          |  | transform->isDirty = true; | 
        
          |  | UpdateChildTransformOnly(it->world, gui->id); // Update child and descendants immediately | 
        
          |  | // ecs_modified(it->world, gui->id, Transform3D); | 
        
          |  |  | 
        
          |  | // Mark all descendants as dirty | 
        
          |  | ecs_iter_t child_it = ecs_children(it->world, gui->id); | 
        
          |  | while (ecs_children_next(&child_it)) { | 
        
          |  | for (int j = 0; j < child_it.count; j++) { | 
        
          |  | if (ecs_has(child_it.world, child_it.entities[j], Transform3D)) { | 
        
          |  | Transform3D *child_transform = ecs_get_mut(child_it.world, child_it.entities[j], Transform3D); | 
        
          |  | if (child_transform) { | 
        
          |  | child_transform->isDirty = true; | 
        
          |  | ecs_modified(child_it.world, child_it.entities[j], Transform3D); | 
        
          |  | } | 
        
          |  | } | 
        
          |  | } | 
        
          |  | } | 
        
          |  | } | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | GuiGroupBox(list_rect, "Entity List"); | 
        
          |  |  | 
        
          |  | // Clean up | 
        
          |  | for (int j = 0; j < entity_count; j++) { | 
        
          |  | RL_FREE(entity_names[j]); | 
        
          |  | } | 
        
          |  | RL_FREE(entity_names); | 
        
          |  | RL_FREE(entity_ids); | 
        
          |  | RL_FREE(name_list); | 
        
          |  | ecs_query_fini(query); | 
        
          |  | } | 
        
          |  |  |