Sun, 01 Jun 2025 16:35:23 +0200
remove pre-defined dummy textures by introducing conditional compilation for shaders
and by the way resolves #645
/* * 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/error.h" #include "ascension/context.h" #include "ascension/scene.h" #include "ascension/behavior.h" #include "ascension/shader.h" #include "ascension/2d.h" #include <cx/tree.h> #include <cx/array_list.h> #include <GL/glew.h> #include <assert.h> void asc_scene_init_(AscScene *scene, struct asc_camera_init_args args) { if (scene->root != NULL) { asc_wprintf("Scene %"PRIxPTR" is already initialized - initialization skipped.", (uintptr_t) scene); return; } asc_camera_init_(&scene->camera, args); scene->root = asc_scene_node_empty(); for (unsigned i = 0 ; i < ASC_RENDER_GROUP_COUNT ; i++) { scene->internal.render_groups[i] = cxArrayListCreateSimple(CX_STORE_POINTERS, 32); } asc_dprintf("Initialized scene %"PRIxPTR, (uintptr_t) scene); } void asc_scene_destroy(AscScene *scene) { if (scene->root == NULL) return; for (unsigned i = 0 ; i < ASC_RENDER_GROUP_COUNT ; i++) { cxListFree(scene->internal.render_groups[i]); scene->internal.render_groups[i] = NULL; } asc_dprintf("Destroyed scene %"PRIxPTR, (uintptr_t) scene); asc_scene_node_free(scene->root); } void asc_scene_execute_behaviors(AscScene *scene) { CxTreeVisitor iter = cx_tree_visitor(scene->root, offsetof(AscSceneNode, children), offsetof(AscSceneNode, next) ); cx_foreach(AscSceneNode*, node, iter) { CxIterator behavior_iter = cxListIterator(node->behaviors); cx_foreach(AscBehavior*, behavior, behavior_iter) { if (behavior->enabled) { behavior->func(behavior); } } } } void asc_scene_draw_sprites( const AscScene *scene, const CxList *opaque_rect, const CxList *opaque_uv, const CxList *blend_rect, const CxList *blend_uv ) { // render opaque sprites from front to back CxIterator iter_opaque_rect = cxListBackwardsIterator(opaque_rect); CxIterator iter_opaque_uv = cxListBackwardsIterator(opaque_uv); // render sprites with alpha value from back to front CxIterator iter_blend_rect = cxListIterator(blend_rect); CxIterator iter_blend_uv = cxListIterator(blend_uv); // TODO: implement interleaving by depth if (cxIteratorValid(iter_opaque_rect)) { glDisable(GL_BLEND); AscShaderSprite *shader = ASC_SHADER_SPRITE_RECT; asc_shader_program_use(&shader->program, &scene->camera); cx_foreach(const AscSprite*, node, iter_opaque_rect) { asc_sprite_draw(shader, node); } } if (cxIteratorValid(iter_opaque_uv)) { glDisable(GL_BLEND); AscShaderSprite *shader = ASC_SHADER_SPRITE_UV; asc_shader_program_use(&shader->program, &scene->camera); cx_foreach(const AscSprite*, node, iter_opaque_uv) { asc_sprite_draw(shader, node); } } if (cxIteratorValid(iter_blend_rect)) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); AscShaderSprite *shader = ASC_SHADER_SPRITE_RECT; asc_shader_program_use(&shader->program, &scene->camera); cx_foreach(const AscSprite*, node, iter_blend_rect) { asc_sprite_draw(shader, node); } } if (cxIteratorValid(iter_blend_uv)) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); AscShaderSprite *shader = ASC_SHADER_SPRITE_UV; asc_shader_program_use(&shader->program, &scene->camera); cx_foreach(const AscSprite*, node, iter_blend_uv) { asc_sprite_draw(shader, node); } } } void asc_scene_draw(AscScene *scene) { if (scene->root == NULL) return; // if the window resized, we must update the viewport if (asc_active_window->resized) { asc_vec2u window_size = asc_active_window->dimensions; if (scene->camera.viewport_update_func == NULL) { // this assumes the viewport was initialized with zeros! scene->camera.viewport.size = window_size; } else { scene->camera.viewport = scene->camera.viewport_update_func(window_size); } if (scene->camera.projection_update_func != NULL) { scene->camera.projection_update_func(&scene->camera, window_size); } } // reset render groups CxList **render_group = scene->internal.render_groups; for (unsigned i = 0 ; i < ASC_RENDER_GROUP_COUNT ; i++) { cxListClear(render_group[i]); } // update the scene graph and add nodes to their render groups CxTreeVisitor iter = cx_tree_visitor(scene->root, offsetof(AscSceneNode, children), offsetof(AscSceneNode, next) ); cx_foreach(AscSceneNode*, node, iter) { // skip hidden nodes (and all their children) if (asc_test_flag(node->flags, ASC_SCENE_NODE_HIDDEN)) { cxTreeVisitorContinue(iter); } // TODO: implement culling // check if geometry needs update asc_clear_flag(node->flags, ASC_SCENE_NODE_GRAPHICS_UPDATED | ASC_SCENE_NODE_TRANSFORM_UPDATED); if (asc_test_flag(node->flags, ASC_SCENE_NODE_UPDATE_GRAPHICS)) { asc_set_flag(node->flags, ASC_SCENE_NODE_GRAPHICS_UPDATED); asc_clear_flag(node->flags, ASC_SCENE_NODE_UPDATE_GRAPHICS); assert(node->update_func != NULL); node->update_func(node); } if (asc_test_flag(node->flags, ASC_SCENE_NODE_UPDATE_TRANSFORM)) { asc_set_flag(node->flags, ASC_SCENE_NODE_TRANSFORM_UPDATED); asc_clear_flag(node->flags, ASC_SCENE_NODE_UPDATE_TRANSFORM); // Only recalculate from components if not using custom transform if (!asc_test_flag(node->flags, ASC_SCENE_NODE_CUSTOM_TRANSFORM)) { asc_transform_from_vec3f( node->transform, node->position, node->scale, node->rotation ); } asc_mat4f_mulst( node->world_transform, node->transform, node->parent->world_transform ); } // add to render group if (node->render_group >= 0) { cxListAdd(render_group[node->render_group], node); } } // set the viewport glViewport( scene->camera.viewport.pos.x, scene->camera.viewport.pos.y, scene->camera.viewport.size.width, scene->camera.viewport.size.height ); if (scene->camera.viewport_clear) { glScissor( scene->camera.viewport.pos.x, scene->camera.viewport.pos.y, scene->camera.viewport.size.width, scene->camera.viewport.size.height ); glEnable(GL_SCISSOR_TEST); const asc_col4f col = scene->camera.clear_color; glClearColor(col.red, col.green, col.blue, col.alpha); glClear(GL_COLOR_BUFFER_BIT); glDisable(GL_SCISSOR_TEST); } // ------------------------- // process the render groups // ------------------------- // 2D Elements // =========== glEnable(GL_DEPTH_TEST); glClear(GL_DEPTH_BUFFER_BIT); // Sprites // ------- asc_scene_draw_sprites(scene, render_group[ASC_RENDER_GROUP_SPRITE_OPAQUE_RECT], render_group[ASC_RENDER_GROUP_SPRITE_OPAQUE_UV], render_group[ASC_RENDER_GROUP_SPRITE_BLEND_RECT], render_group[ASC_RENDER_GROUP_SPRITE_BLEND_UV] ); } void asc_scene_add_node(AscScene *scene, AscSceneNode *node) { asc_scene_node_link(scene->root, node); } void asc_scene_remove_node(AscSceneNode *node) { asc_scene_node_unlink(node); }