--- a/src/scene.c Thu Apr 24 18:41:42 2025 +0200 +++ b/src/scene.c Thu Apr 24 19:53:40 2025 +0200 @@ -28,46 +28,41 @@ #include "ascension/scene.h" #include "ascension/context.h" -#include "ascension/utils.h" #include "ascension/2d.h" +#include <cx/tree.h> #include <cx/linked_list.h> #include <cx/array_list.h> -#include <cx/tree.h> #include <cx/utils.h> #include <GL/glew.h> #include <assert.h> -static CxTreeIterator asc_scene_node_iterator( - AscSceneNode *node, - bool visit_on_exit -) { - return cx_tree_iterator( - node, visit_on_exit, - offsetof(AscSceneNode, children), - offsetof(AscSceneNode, next) - ); +void asc_scene_init(AscScene *scene) { + asc_dprintf("Initialized scene %"PRIxPTR, (uintptr_t) scene); + // TODO: how should we initialize the camera? + scene->root = asc_scene_node_empty(); } -static CxTreeVisitor asc_scene_node_visitor(AscSceneNode *node) { - return cx_tree_visitor(node, - offsetof(AscSceneNode, children), - offsetof(AscSceneNode, next) - ); +void asc_scene_destroy(AscScene *scene) { + asc_dprintf("Destroyed scene %"PRIxPTR, (uintptr_t) scene); + asc_scene_node_free(scene->root); } -void asc_scene_draw(AscSceneNode *root, asc_recti viewport, const AscCamera *camera) { +void asc_scene_draw(AscScene *scene, asc_recti viewport) { // create render groups CxList *render_group[ASC_RENDER_GROUP_COUNT]; cx_for_n(i, ASC_RENDER_GROUP_COUNT) { render_group[i] = cxArrayListCreateSimple(CX_STORE_POINTERS, 32); } - // skip the root node deliberately, we know it's just the container - CxTreeVisitor iter = asc_scene_node_visitor(root); + // skip the root node deliberately; we know it's just the container + CxTreeVisitor iter = cx_tree_visitor(scene->root, + offsetof(AscSceneNode, children), + offsetof(AscSceneNode, next) + ); cxIteratorNext(iter); // update the children and add them to the render groups @@ -116,7 +111,9 @@ } // add to render group - cxListAdd(render_group[node->render_group], node); + if (node->render_group >= 0) { + cxListAdd(render_group[node->render_group], node); + } } // set the viewport (in OpenGL we need to invert the Y axis) @@ -145,9 +142,9 @@ AscShaderProgram *shader = &ASC_SHADER_SPRITE->program; glUseProgram(shader->id); glUniformMatrix4fv(shader->projection, 1, - GL_FALSE, camera->projection); + GL_FALSE, scene->camera.projection); glUniformMatrix4fv(shader->view, 1, - GL_FALSE, camera->view); + GL_FALSE, scene->camera.view); // render opaque sprites from front to back glDisable(GL_BLEND); @@ -171,92 +168,10 @@ } } -AscSceneNode *asc_scene_node_empty(void) { - AscSceneNode *node = calloc(1, sizeof(AscSceneNode)); - node->free_func = (asc_scene_free_func) free; - node->scale.x = node->scale.y = node->scale.z = 1; - asc_transform_identity(node->transform); - asc_transform_identity(node->world_transform); - return node; -} - -void asc_scene_node_free(AscSceneNode *node) { - if (node == NULL) return; - - // remove this node from its parent - asc_scene_node_unlink(node); - - // free the entire subtree - CxTreeIterator iter = asc_scene_node_iterator(node, true); - cx_foreach(AscSceneNode*, child, iter) { - if (!iter.exiting) continue; - if (child->behaviors != NULL) { - cxListFree(child->behaviors); - } - if (child->free_func != NULL) { - child->free_func(child); - } else { - free(child); - } - } -} - -void asc_scene_node_link(AscSceneNode * restrict parent, AscSceneNode * restrict node) { - cx_tree_link( - parent, node, - offsetof(AscSceneNode, parent), - offsetof(AscSceneNode, children), - offsetof(AscSceneNode, last_child), - offsetof(AscSceneNode, prev), - offsetof(AscSceneNode, next) - ); +void asc_scene_add_node(AscScene *scene, AscSceneNode *node) { + asc_scene_node_link(scene->root, node); } -void asc_scene_node_unlink(AscSceneNode *node) { - cx_tree_unlink( - node, - offsetof(AscSceneNode, parent), - offsetof(AscSceneNode, children), - offsetof(AscSceneNode, last_child), - offsetof(AscSceneNode, prev), - offsetof(AscSceneNode, next) - ); -} - -void asc_scene_add_behavior( - AscSceneNode *node, - asc_scene_update_func behavior -) { - if (node->behaviors == NULL) { - node->behaviors = cxLinkedListCreateSimple(CX_STORE_POINTERS); - } - cxListAdd(node->behaviors, behavior); +void asc_scene_remove_node(AscSceneNode *node) { + asc_scene_node_unlink(node); } - -void asc_scene_remove_behavior( - AscSceneNode *node, - asc_scene_update_func behavior -) { - if (node->behaviors != NULL) { - cxListFindRemove(node->behaviors, behavior); - } -} - -void asc_node_update(AscSceneNode *node) { - asc_set_flag(node->flags, ASC_SCENE_NODE_UPDATE_GRAPHICS); -} - -void asc_node_update_transform(AscSceneNode *node) { - // fast skip if node is already marked - if (asc_test_flag(node->flags, ASC_SCENE_NODE_UPDATE_TRANSFORM)) { - return; - } - - CxTreeIterator iter = asc_scene_node_iterator(node, false); - cx_foreach(AscSceneNode*, n, iter) { - if (asc_test_flag(n->flags, ASC_SCENE_NODE_UPDATE_TRANSFORM)) { - cxTreeIteratorContinue(iter); - } - asc_set_flag(n->flags, ASC_SCENE_NODE_UPDATE_TRANSFORM); - } -}