Thu, 17 Jul 2025 20:26:39 +0200
add a tiled game field
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * Copyright 2023 Mike Becker. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "ascension/scene_node.h" #include "ascension/context.h" #include "ascension/error.h" #include <cx/tree.h> #include <cx/linked_list.h> #include <cx/printf.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) ); } AscSceneNode *asc_scene_node_empty(void) { AscSceneNode *node = cxZallocDefault(sizeof(AscSceneNode)); asc_scene_node_init(node, .render_group = ASC_RENDER_GROUP_NONE); return node; } static void asc_scene_node_destroy(AscSceneNode *node) { cxListFree(node->behaviors); if (node->user_data_free_func != NULL) { node->user_data_free_func((void*)node->user_data_allocator, node->user_data); } if (node->destroy_func != NULL) { node->destroy_func(node); } if (node->name.ptr != NULL) { asc_dprintf("Destroy node: %"CX_PRIstr, CX_SFMT(node->name)); cx_strfree(&node->name); } } 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; asc_scene_node_destroy(child); cxFreeDefault(child); } } void asc_scene_node_init_(AscSceneNode *node, struct asc_scene_node_init_args args) { if (args.name != NULL) { asc_scene_node_name(node, args.name); } node->render_group = args.render_group; if (args.render_group != ASC_RENDER_GROUP_NONE) { assert(args.update_func != NULL); assert(args.draw_func != NULL); assert(args.destroy_func != NULL); } node->update_func = args.update_func; node->destroy_func = args.destroy_func; node->draw_func = args.draw_func; if (args.pos2d.x != 0 || args.pos2d.y != 0) { node->position = ASC_VEC3F(args.pos2d.x, args.pos2d.y, ASC_SCENE_2D_DEPTH_OFFSET); } else if (args.pos3d.x != 0 || args.pos3d.y != 0 || args.pos3d.z != 0) { node->position = args.pos3d; } if (args.origin2d.x != 0 || args.origin2d.y != 0) { node->origin = ASC_VEC3F(args.origin2d.x, args.origin2d.y, 0.f); } else if (args.origin3d.x != 0 || args.origin3d.y != 0 || args.origin3d.z != 0) { node->origin = args.origin3d; } if (args.scale2d.width != 0 && args.scale2d.height != 0) { node->scale = ASC_VEC3F(args.scale2d.width, args.scale2d.height, 1.f); } else if (args.scale3d.x != 0 && args.scale3d.height != 0 && args.scale3d.depth != 0) { node->scale = args.scale3d; } else { node->scale = ASC_VEC3F_1; } if (asc_memcmpz(args.rotation, ASC_TRANSFORM_SIZE)) { asc_mat4f_unit(node->rotation); } else { asc_transform_copy(node->rotation, args.rotation); } asc_scene_node_update(node); } void asc_scene_node_calculate_transform(AscSceneNode *node) { asc_transform temp, temp2; // move the point of origin asc_transform_translate3f(temp, asc_vec3f_neg(node->origin)); // apply the rotation asc_transform_apply(node->transform, node->rotation, temp); // apply the scale asc_transform_scale3f(temp, node->scale); asc_transform_apply(temp2, temp, node->transform); // apply the translation asc_transform_translate3f(temp, node->position); asc_transform_apply(node->transform, temp, temp2); } void asc_scene_node_name(AscSceneNode *node, const char *name) { cx_strfree(&node->name); if (name == NULL) { node->name.ptr = NULL; node->name.length = 0; } else { node->name.ptr = strdup(name); node->name.length = strlen(name); } } cxstring asc_scene_node_get_name(AscSceneNode *node) { if (node->name.ptr != NULL) return cx_strcast(node->name); AscSceneNode *parent = node->parent; while (parent != NULL && parent->name.ptr == NULL) { parent = parent->parent; } if (parent == NULL) { cx_sprintf(&node->name.ptr, &node->name.length, "%"PRIxPTR " - w/o named parent", (uintptr_t)node); } else { cx_sprintf(&node->name.ptr, &node->name.length, "%"PRIxPTR " - child of %"CX_PRIstr, (uintptr_t)node, CX_SFMT(parent->name)); } return cx_strcast(node->name); } 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) ); asc_scene_node_update_transform(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) ); asc_scene_node_update_transform(node); } void asc_scene_node_update(AscSceneNode *node) { asc_set_flag(node->flags, ASC_SCENE_NODE_UPDATE_GRAPHICS); } void asc_scene_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); } } void *asc_scene_node_allocate_data(AscSceneNode *node, size_t n) { if (node->user_data != NULL) { asc_wprintf("Node %"CX_PRIstr" already has user data which is now destroyed!", CX_SFMT(node->name)); if (node->user_data_free_func != NULL) { node->user_data_free_func((void*)node->user_data_allocator, node->user_data); } } node->user_data = cxZallocDefault(n); node->user_data_allocator = cxDefaultAllocator; node->user_data_free_func = (cx_destructor_func2) cxFree; return node->user_data; }