src/ascension/scene_node.h

Fri, 08 Aug 2025 20:51:14 +0200

author
Mike Becker <universe@uap-core.de>
date
Fri, 08 Aug 2025 20:51:14 +0200
changeset 259
1315ac4d1368
parent 219
62508d957a22
permissions
-rw-r--r--

add show/hide functions for scene nodes

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * Copyright 2025 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.
 */

#ifndef ASCENSION_SCENE_NODE_H
#define ASCENSION_SCENE_NODE_H

#include <cx/allocator.h>
#include <cx/list.h>
#include <cx/string.h>

#include "datatypes.h"
#include "transform.h"

typedef struct asc_scene_node_s AscSceneNode;
typedef struct asc_camera_s AscCamera; // avoids full include of camera.h

typedef void(*asc_scene_node_destroy_func)(AscSceneNode*);
typedef void(*asc_scene_node_update_func)(AscSceneNode*);
typedef void(*asc_scene_node_draw_func)(const AscCamera*,const AscSceneNode*);

// TODO: rework the concept of render groups, because currently it is only half abstract, half hard-coded
enum AscRenderGroup {
    ASC_RENDER_GROUP_NONE = -1,
    ASC_RENDER_GROUP_2D_OPAQUE,
    ASC_RENDER_GROUP_2D_BLEND,
    ASC_RENDER_GROUP_COUNT
};

struct asc_scene_node_s {
    AscSceneNode *parent;
    AscSceneNode *prev;
    AscSceneNode *next;
    AscSceneNode *children;
    AscSceneNode *last_child;
    cxmutstr name;
    /**
     * List of AscBehavior structs.
     * Not a pointer list!
     */
    CxList *behaviors;
    asc_scene_node_destroy_func destroy_func;
    asc_scene_node_update_func update_func;
    asc_scene_node_draw_func draw_func;
    asc_transform transform;
    asc_transform world_transform;
    asc_vec3f position;
    asc_vec3f scale;
    asc_vec3f origin;
    asc_transform rotation;
    enum AscRenderGroup render_group;
    /**
     * Custom flags for this node.
     * The #ASC_SCENE_NODE_FLAGS_MASK bits are reserved for general flags.
     */
    uint32_t flags;
    void *user_data;
    const CxAllocator *user_data_allocator;
    /**
     * A free function that takes the allocator as the first argument and the data as the second.
     */
    cx_destructor_func2 user_data_free_func;
};


/**
 * The reserved bits for general flags.
 */
#define ASC_SCENE_NODE_FLAGS_MASK             0xFF000000
/**
 * Set when a graphics update is needed in this frame.
 */
#define ASC_SCENE_NODE_UPDATE_GRAPHICS        0x01000000
/**
 * Set when a graphics updated happened last frame.
 */
#define ASC_SCENE_NODE_GRAPHICS_UPDATED       0x02000000
/**
 * Set when a transform update is needed in this frame.
 */
#define ASC_SCENE_NODE_UPDATE_TRANSFORM       0x04000000
/**
 * Set when a transform update happened last frame.
 */
#define ASC_SCENE_NODE_TRANSFORM_UPDATED      0x08000000
/**
 * Set when the node is not supposed to be shown on screen.
 */
#define ASC_SCENE_NODE_HIDDEN                 0x80000000


struct asc_scene_node_init_args {
    asc_scene_node_draw_func draw_func;
    asc_scene_node_update_func update_func;
    asc_scene_node_destroy_func destroy_func;
    asc_transform rotation;
    asc_vec3f pos3d;
    asc_vec3f scale3d;
    asc_vec3f origin3d;
    asc_vec2i pos2d;
    asc_vec2u scale2d;
    asc_vec2i origin2d;
    enum AscRenderGroup render_group;
    const char *name;
};

/**
 * Convenience macro to specify draw, update, and destroy func with a common prefix.
 *
 * @param prefix prefix for the draw, update, destroy funcs
 */
#define ASC_SCENE_NODE_FUNCS(prefix) .draw_func = prefix##_draw, .update_func = prefix##_update, .destroy_func = prefix##_destroy

/**
 * See asc_scene_node_init().
 *
 * @param node the node to initialize
 * @param args the arguments
 */
void asc_scene_node_init_(AscSceneNode *node, struct asc_scene_node_init_args args);

/**
 * Initializes a node with default arguments.
 *
 * The minimum set of mandatory arguments are the draw, update, and destroy functions, as well as the render group.
 *
 * @param node the node to initialize
 * @param ... the arguments
 */
#define asc_scene_node_init(node, ...) asc_scene_node_init_(node, (struct asc_scene_node_init_args){__VA_ARGS__})

/**
 * Creates an empty node that may serve as a container for other nodes.
 *
 * The free_func of this node will be a simple free().
 *
 * @return the new node
 */
AscSceneNode *asc_scene_node_empty(void);

/**
 * Unlinks the node from its parent and frees the entire subtree.
 *
 * The free_func of this node and all child nodes is called, starting
 * with the leaf nodes and terminating with \p node.
 *
 * @param node the node to unlink
 */
void asc_scene_node_free(AscSceneNode *node);

/**
 * Calculates the transformation matrix from components.
 *
 * Used internally, usually you never need to call this.
 * Use asc_scene_node_update_transform() to trigger a recalculation.
 *
 * @param node the node
 * @see asc_scene_node_update_transform()
 */
void asc_scene_node_calculate_transform(AscSceneNode *node);

/**
 * Sets the name of a node.
 *
 * @param node the node
 * @param name the new name of the node
 */
void asc_scene_node_name(AscSceneNode *node, const char *name);

/**
 * Returns the name of the node.
 *
 * Names are not necessarily unique.
 *
 * @param node the node
 * @return the node's name or a calculated name if it does not have one
 */
cxstring asc_scene_node_get_name(AscSceneNode *node);

/**
 * Links a node to a (new) parent.
 *
 * @param parent the (new) parent
 * @param node the node to link
 */
void asc_scene_node_link(
        AscSceneNode *restrict parent,
        AscSceneNode *restrict node
);

/**
 * Unlinks a node from its parent.
 *
 * This might be useful to temporarily remove a subtree from a scene.
 * To permanently remove the node use asc_scene_node_free().
 *
 * @param node the node to unlink
 */
void asc_scene_node_unlink(AscSceneNode *node);

void asc_scene_node_update(AscSceneNode *node);

void asc_scene_node_update_transform(AscSceneNode *node);

/**
 * Allocates user data for the specified node.
 *
 * Also registers a corresponding free-function.
 *
 * @param node the node
 * @param n the size of the user data
 * @return the user data pointer
 */
void *asc_scene_node_allocate_data(AscSceneNode *node, size_t n);

/**
 * This is the z-position a simple 2D element should have to allow
 * stacking 2D elements with depth-test.
 */
#define ASC_SCENE_2D_DEPTH_OFFSET 0.0078125f

static inline void asc_scene_node_set_position(AscSceneNode *node, asc_vec3f position) {
    node->position = position;
    asc_scene_node_update_transform(node);
}

static inline void asc_scene_node_move(AscSceneNode *node, asc_vec3f offset) {
    node->position.x += offset.x;
    node->position.y += offset.y;
    node->position.z += offset.z;
    asc_scene_node_update_transform(node);
}

static inline void asc_scene_node_set_scale(AscSceneNode *node, asc_vec3f scale) {
    node->scale = scale;
    asc_scene_node_update_transform(node);
}

static inline void asc_scene_node_set_origin(AscSceneNode *node, asc_vec3f origin) {
    node->origin = origin;
    asc_scene_node_update_transform(node);
}

static inline void asc_scene_node_set_position2f(AscSceneNode *node, asc_vec2f position) {
    node->position.x = position.x;
    node->position.y = position.y;
    asc_scene_node_update_transform(node);
}

/**
 * Sets the z-index of a 2D scene node.
 *
 * The index is relative to the parent nodes and by default one.
 * This means, children of a 2D node by default stack onto the parent node.
 *
 * @param node the 2D node
 * @param n the local z-index (default: 1)
 */
static inline void asc_scene_node_set_zindex(AscSceneNode *node, int n) {
    node->position.z = n*ASC_SCENE_2D_DEPTH_OFFSET;
    asc_scene_node_update_transform(node);
}

static inline void asc_scene_node_move2f(AscSceneNode *node, asc_vec2f offset) {
    node->position.x += offset.x;
    node->position.y += offset.y;
    asc_scene_node_update_transform(node);
}

static inline void asc_scene_node_set_scale2f(AscSceneNode *node, asc_vec2f scale) {
    node->scale.width = scale.width;
    node->scale.height = scale.height;
    asc_scene_node_update_transform(node);
}

static inline void asc_scene_node_set_origin2f(AscSceneNode *node, asc_vec2f origin) {
    node->origin.x = origin.x;
    node->origin.y = origin.y;
    asc_scene_node_update_transform(node);
}

static inline void asc_scene_node_set_rotation(AscSceneNode *node, asc_transform rotation) {
    memcpy(node->rotation, rotation, ASC_TRANSFORM_SIZE);
    asc_scene_node_update_transform(node);
}

static inline void asc_scene_node_roll_deg(AscSceneNode *node, float angle) {
    asc_transform r, d;
    asc_transform_roll(r, asc_rad(angle));
    asc_transform_apply(d, r, node->rotation);
    asc_transform_copy(node->rotation, d);
    asc_scene_node_update_transform(node);
}

static inline void asc_scene_node_hide(AscSceneNode *node) {
    asc_set_flag(node->flags, ASC_SCENE_NODE_HIDDEN);
}

static inline void asc_scene_node_show(AscSceneNode *node) {
    asc_clear_flag(node->flags, ASC_SCENE_NODE_HIDDEN);
}

static inline void asc_scene_node_toggle_visibility(AscSceneNode *node) {
    asc_toggle_flag(node->flags, ASC_SCENE_NODE_HIDDEN);
}

static inline bool asc_scene_node_is_hidden(AscSceneNode *node) {
    return asc_test_flag(node->flags, ASC_SCENE_NODE_HIDDEN);
}

#endif

mercurial