src/scene.c

Thu, 18 Apr 2024 21:53:53 +0200

author
Mike Becker <universe@uap-core.de>
date
Thu, 18 Apr 2024 21:53:53 +0200
changeset 64
f18dc427f86f
parent 61
b7954818a6b7
child 65
9c44c55d327a
permissions
-rw-r--r--

make use of the asc_window_active macro

universe@21 1 /*
universe@21 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
universe@21 3 * Copyright 2023 Mike Becker. All rights reserved.
universe@21 4 *
universe@21 5 * Redistribution and use in source and binary forms, with or without
universe@21 6 * modification, are permitted provided that the following conditions are met:
universe@21 7 *
universe@21 8 * 1. Redistributions of source code must retain the above copyright
universe@21 9 * notice, this list of conditions and the following disclaimer.
universe@21 10 *
universe@21 11 * 2. Redistributions in binary form must reproduce the above copyright
universe@21 12 * notice, this list of conditions and the following disclaimer in the
universe@21 13 * documentation and/or other materials provided with the distribution.
universe@21 14 *
universe@21 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
universe@21 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
universe@21 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
universe@21 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
universe@21 19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
universe@21 20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
universe@21 21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
universe@21 22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
universe@21 23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
universe@21 24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
universe@21 25 * POSSIBILITY OF SUCH DAMAGE.
universe@21 26 */
universe@21 27
universe@21 28 #include "ascension/scene.h"
universe@21 29
universe@37 30 #include "ascension/context.h"
universe@61 31 #include "ascension/utils.h"
universe@37 32
universe@47 33 #include <cx/linked_list.h>
universe@47 34 #include <cx/array_list.h>
universe@29 35 #include <cx/tree.h>
universe@37 36 #include <cx/utils.h>
universe@37 37
universe@37 38 #include "ascension/shader.h"
universe@37 39 #include <GL/glew.h>
universe@29 40
universe@21 41 #include <assert.h>
universe@21 42
universe@38 43 static CxTreeIterator asc_scene_node_iterator(
universe@38 44 AscSceneNode *node,
universe@38 45 bool visit_on_exit
universe@38 46 ) {
universe@38 47 return cx_tree_iterator(
universe@38 48 node, visit_on_exit,
universe@38 49 offsetof(AscSceneNode, children),
universe@38 50 offsetof(AscSceneNode, next)
universe@38 51 );
universe@38 52 }
universe@38 53
universe@43 54 static CxTreeVisitor asc_scene_node_visitor(AscSceneNode *node) {
universe@43 55 return cx_tree_visitor(node,
universe@43 56 offsetof(AscSceneNode, children),
universe@43 57 offsetof(AscSceneNode, next)
universe@43 58 );
universe@43 59 }
universe@43 60
universe@47 61 struct asc_render_group_entry {
universe@47 62 asc_scene_draw_func draw;
universe@47 63 AscSceneNode const *node;
universe@47 64 };
universe@47 65
universe@47 66 #define asc_draw_render_group(iter) \
universe@47 67 cx_foreach(struct asc_render_group_entry*, entry, iter) { \
universe@47 68 entry->draw(entry->node); \
universe@21 69 }
universe@37 70
universe@47 71 void asc_scene_draw(AscSceneNode *root, asc_recti viewport, AscCamera *camera) {
universe@47 72 // create render groups
universe@47 73 CxList *render_group[ASC_RENDER_GROUP_COUNT];
universe@47 74 cx_for_n(i, ASC_RENDER_GROUP_COUNT) {
universe@47 75 render_group[i] = cxArrayListCreateSimple(
universe@47 76 sizeof(struct asc_render_group_entry), 32);
universe@47 77 }
universe@32 78
universe@32 79 // skip the root node deliberately, we know it's just the container
universe@47 80 CxTreeVisitor iter = asc_scene_node_visitor(root);
universe@32 81 cxIteratorNext(iter);
universe@32 82
universe@37 83 // update the children and add them to the render groups
universe@30 84 cx_foreach(AscSceneNode*, node, iter) {
universe@41 85 node->depth = iter.depth;
universe@41 86
universe@55 87 // skip hidden nodes (and all their children)
universe@61 88 if (asc_test_flag(node->flags, ASC_SCENE_NODE_HIDDEN)) {
universe@55 89 cxTreeVisitorContinue(iter);
universe@55 90 }
universe@55 91
universe@33 92 // execute behaviors, first
universe@47 93 if (node->behaviors != NULL) {
universe@47 94 CxIterator behavior_iter = cxListIterator(node->behaviors);
universe@47 95 cx_foreach(asc_scene_update_func, behavior, behavior_iter) {
universe@47 96 behavior(node);
universe@47 97 }
universe@33 98 }
universe@33 99
universe@47 100 // TODO: implement culling
universe@47 101
universe@33 102 // check if geometry needs update
universe@61 103 if (asc_test_flag(node->flags, ASC_SCENE_NODE_UPDATE_GRAPHICS)) {
universe@37 104 assert(node->update_func != NULL);
universe@61 105 asc_clear_flag(node->flags, ASC_SCENE_NODE_UPDATE_GRAPHICS);
universe@32 106 node->update_func(node);
universe@32 107 }
universe@61 108 if (asc_test_flag(node->flags, ASC_SCENE_NODE_UPDATE_TRANSFORM)) {
universe@61 109 asc_test_flag(node->flags, ASC_SCENE_NODE_UPDATE_TRANSFORM);
universe@45 110 asc_transform_from_parts(
universe@45 111 node->transform,
universe@45 112 node->position,
universe@45 113 node->scale,
universe@45 114 node->rotation
universe@45 115 );
universe@45 116 asc_mat4f_mulst(
universe@45 117 node->world_transform,
universe@45 118 node->transform,
universe@45 119 node->parent->world_transform
universe@45 120 );
universe@37 121 }
universe@33 122
universe@37 123 // add to render group
universe@30 124 if (node->draw_func != NULL) {
universe@37 125 struct asc_render_group_entry entry = {
universe@37 126 node->draw_func, node
universe@37 127 };
universe@47 128 cxListAdd(render_group[node->render_group], &entry);
universe@30 129 }
universe@29 130 }
universe@37 131
universe@37 132 // set the viewport (in OpenGL we need to invert the Y axis)
universe@37 133 glViewport(
universe@47 134 viewport.pos.x,
universe@47 135 -viewport.pos.y,
universe@47 136 viewport.size.width,
universe@47 137 viewport.size.height
universe@37 138 );
universe@37 139
universe@47 140 // -------------------------
universe@47 141 // process the render groups
universe@47 142 // -------------------------
universe@44 143 AscShaderProgram *shader;
universe@47 144 CxIterator render_iter;
universe@37 145
universe@47 146 // 2D Elements
universe@47 147 // ===========
universe@47 148 glEnable(GL_DEPTH_TEST);
universe@47 149 glClear(GL_DEPTH_BUFFER_BIT);
universe@37 150
universe@47 151 // Sprites
universe@47 152 // -------
universe@47 153 // TODO: implement view matrix for 2D worlds
universe@64 154 shader = &asc_window_active->glctx.shader.sprite.base;
universe@47 155 glUseProgram(shader->id);
universe@47 156 glUniformMatrix4fv(shader->projection, 1,
universe@47 157 GL_FALSE, camera->projection);
universe@41 158
universe@47 159 // render opaque sprites from front to back
universe@47 160 glDisable(GL_BLEND);
universe@47 161 render_iter = cxListBackwardsIterator(render_group[ASC_RENDER_GROUP_SPRITE_OPAQUE]);
universe@47 162 asc_draw_render_group(render_iter);
universe@47 163
universe@47 164 // render sprites with alpha value from back to front
universe@47 165 glEnable(GL_BLEND);
universe@47 166 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
universe@47 167 render_iter = cxListIterator(render_group[ASC_RENDER_GROUP_SPRITE_BLEND]);
universe@47 168 asc_draw_render_group(render_iter);
universe@47 169
universe@47 170 // destroy render groups
universe@47 171 cx_for_n(i, ASC_RENDER_GROUP_COUNT) {
universe@47 172 cxListDestroy(render_group[i]);
universe@37 173 }
universe@29 174 }
universe@29 175
universe@29 176 AscSceneNode *asc_scene_node_empty(void) {
universe@27 177 AscSceneNode *node = calloc(1, sizeof(AscSceneNode));
universe@29 178 node->free_func = (asc_scene_free_func) free;
universe@51 179 node->scale.x = node->scale.y = node->scale.z = 1;
universe@45 180 asc_transform_identity(node->transform);
universe@38 181 asc_transform_identity(node->world_transform);
universe@21 182 return node;
universe@21 183 }
universe@21 184
universe@21 185 void asc_scene_node_free(AscSceneNode *node) {
universe@21 186 if (node == NULL) return;
universe@27 187
universe@27 188 // remove this node from its parent
universe@27 189 asc_scene_node_unlink(node);
universe@27 190
universe@31 191 // free the entire subtree
universe@38 192 CxTreeIterator iter = asc_scene_node_iterator(node, true);
universe@31 193 cx_foreach(AscSceneNode*, child, iter) {
universe@31 194 if (!iter.exiting) continue;
universe@47 195 if (child->behaviors != NULL) {
universe@47 196 cxListDestroy(child->behaviors);
universe@47 197 }
universe@31 198 if (child->free_func != NULL) {
universe@31 199 child->free_func(child);
universe@31 200 } else {
universe@31 201 free(child);
universe@31 202 }
universe@21 203 }
universe@21 204 }
universe@21 205
universe@29 206 void asc_scene_node_link(AscSceneNode * restrict parent, AscSceneNode * restrict node) {
universe@33 207 cx_tree_link(
universe@33 208 parent, node,
universe@33 209 offsetof(AscSceneNode, parent),
universe@33 210 offsetof(AscSceneNode, children),
universe@33 211 offsetof(AscSceneNode, prev),
universe@33 212 offsetof(AscSceneNode, next)
universe@33 213 );
universe@29 214 }
universe@29 215
universe@21 216 void asc_scene_node_unlink(AscSceneNode *node) {
universe@33 217 cx_tree_unlink(
universe@33 218 node,
universe@33 219 offsetof(AscSceneNode, parent),
universe@33 220 offsetof(AscSceneNode, children),
universe@33 221 offsetof(AscSceneNode, prev),
universe@33 222 offsetof(AscSceneNode, next)
universe@33 223 );
universe@33 224 }
universe@33 225
universe@47 226 void asc_scene_add_behavior(
universe@47 227 AscSceneNode *node,
universe@47 228 asc_scene_update_func behavior
universe@47 229 ) {
universe@47 230 if (node->behaviors == NULL) {
universe@47 231 node->behaviors = cxLinkedListCreateSimple(CX_STORE_POINTERS);
universe@47 232 }
universe@47 233 cxListAdd(node->behaviors, behavior);
universe@33 234 }
universe@33 235
universe@47 236 void asc_scene_remove_behavior(
universe@47 237 AscSceneNode *node,
universe@47 238 asc_scene_update_func behavior
universe@47 239 ) {
universe@47 240 if (node->behaviors != NULL) {
universe@47 241 cxListFindRemove(node->behaviors, behavior);
universe@47 242 }
universe@33 243 }
universe@38 244
universe@61 245 void asc_node_update(AscSceneNode *node) {
universe@61 246 asc_set_flag(node->flags, ASC_SCENE_NODE_UPDATE_GRAPHICS);
universe@61 247 }
universe@61 248
universe@61 249 void asc_node_update_transform(AscSceneNode *node) {
universe@61 250 // fast skip if node is already marked
universe@61 251 if (asc_test_flag(node->flags, ASC_SCENE_NODE_UPDATE_TRANSFORM)) {
universe@61 252 return;
universe@61 253 }
universe@45 254
universe@38 255 CxTreeIterator iter = asc_scene_node_iterator(node, false);
universe@38 256 cx_foreach(AscSceneNode*, n, iter) {
universe@61 257 if (asc_test_flag(n->flags, ASC_SCENE_NODE_UPDATE_TRANSFORM)) {
universe@61 258 cxTreeIteratorContinue(iter);
universe@61 259 }
universe@61 260 asc_set_flag(n->flags, ASC_SCENE_NODE_UPDATE_TRANSFORM);
universe@38 261 }
universe@45 262 }

mercurial