src/scene.c

Tue, 09 Apr 2024 21:18:52 +0200

author
Mike Becker <universe@uap-core.de>
date
Tue, 09 Apr 2024 21:18:52 +0200
changeset 50
d8d2e4817db1
parent 49
77493525eac2
child 51
a656496594f9
permissions
-rw-r--r--

add texture.h

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

mercurial