summaryrefslogtreecommitdiff
path: root/src/transform_hierarchy.c
blob: 25ace6c0b3b4d27ea95853b731ec605fcbdc0270 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120

/**
 * @file transform_hierarchy.h
 */
#pragma once
#include "transform_hierarchy.h"
#include <stdlib.h>
#include <string.h>

#include "log.h"
#include "maths.h"
#include "maths_types.h"
#include "render_types.h"

struct transform_hierarchy {
  transform_node root;
};

transform_hierarchy* transform_hierarchy_create() {
  transform_hierarchy* tfh = malloc(sizeof(transform_hierarchy));

  tfh->root = (transform_node){ .model = { ABSENT_MODEL_HANDLE },
                                .tf = TRANSFORM_DEFAULT,
                                .local_matrix_tf = mat4_ident(),
                                .world_matrix_tf = mat4_ident(),
                                .parent = NULL,
                                .children = { 0 },
                                .n_children = 0,
                                .tfh = tfh };

  return tfh;
}

bool free_node(transform_node* node, void* _ctx_data) {
  if (!node) return true;  // leaf node
  if (node == &node->tfh->root) {
    WARN("You can't free the root node!");
    return false;
  }

  printf("Freed node\n");
  free(node);
  return true;
}

void transform_hierarchy_free(transform_hierarchy* tfh) {
  transform_hierarchy_dfs(&tfh->root, free_node, false, NULL);
  free(tfh);
}

transform_node* transform_hierarchy_root_node(transform_hierarchy* tfh) { return &tfh->root; }

void transform_hierarchy_add_node(transform_node* parent, model_handle model, transform tf) {
  if (!parent) {
    WARN("You tried to add a node to a bad parent (NULL?)");
    return;
  }
  transform_node* node = malloc(sizeof(transform_node));
  node->model = model;
  node->tf = tf;
  node->local_matrix_tf = mat4_ident();
  node->world_matrix_tf = mat4_ident();
  node->parent = parent;
  memset(node->children, 0, sizeof(node->children));
  node->n_children = 0;
  node->tfh = parent->tfh;

  // push into parent's children array
  u32 next_index = parent->n_children;
  if (next_index == MAX_TF_NODE_CHILDREN) {
    ERROR("This transform hierarchy node already has MAX children. Dropping.");
    free(node);
  } else {
    parent->children[next_index] = node;
    parent->n_children++;
  }
}

void transform_hierarchy_delete_node(transform_node* node) {
  // delete all children
  for (u32 i = 0; i < node->n_children; i++) {
    transform_node* child = node->children[i];
    transform_hierarchy_dfs(child, free_node, false, NULL);
  }

  if (node->parent) {
    for (u32 i = 0; i < node->parent->n_children; i++) {
      transform_node* child = node->parent->children[i];
      if (child == node) {
        node->parent->children[i] = NULL;  // HACK: this will leave behind empty slots in the
                                           // children array of the parent. oh well.
      }
    }
  }

  free(node);
}

void transform_hierarchy_dfs(transform_node* start_node,
                             bool (*visit_node)(transform_node* node, void* ctx_data),
                             bool is_pre_order, void* ctx_data) {
  if (!start_node) return;

  bool continue_traversal = true;
  if (is_pre_order) {
    continue_traversal = visit_node(start_node, ctx_data);
  }

  if (continue_traversal) {
    for (u32 i = 0; i < start_node->n_children; i++) {
      transform_node* child = start_node->children[i];
      transform_hierarchy_dfs(child, visit_node, is_pre_order, ctx_data);
    }
  }

  if (!is_pre_order) {
    // post-order
    visit_node(start_node, ctx_data);
  }
}