-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[transform_hierarchy] added tz::transform_hierarchy
- Loading branch information
Showing
5 changed files
with
282 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
#ifndef TZ_CORE_DATA_TRANSFORM_HIERARCHY_HPP | ||
#define TZ_CORE_DATA_TRANSFORM_HIERARCHY_HPP | ||
#include "tz/core/data/trs.hpp" | ||
#include "tz/core/data/handle.hpp" | ||
#include <optional> | ||
#include <cstddef> | ||
|
||
namespace tz | ||
{ | ||
template<typename T> | ||
struct transform_node | ||
{ | ||
mutable T data; | ||
mutable tz::trs local_transform = {}; | ||
std::optional<unsigned int> parent = std::nullopt; | ||
std::vector<unsigned int> children = {}; | ||
}; | ||
|
||
template<typename T = void*> | ||
class transform_hierarchy | ||
{ | ||
public: | ||
transform_hierarchy() = default; | ||
std::size_t size() const; | ||
bool empty() const{return this->size() == 0;} | ||
std::vector<unsigned int> get_root_node_ids() const; | ||
|
||
// returns id of the new node. | ||
unsigned int add_node(tz::trs local_transform = {}, T data = {}, std::optional<unsigned int> parent = std::nullopt); | ||
// returns offset to be applied to the previous hierarchy's set of nodes to get their corresponding node ids | ||
// within this hierarchy | ||
unsigned int add_hierarchy(const transform_hierarchy<T>& tree); | ||
unsigned int add_hierarchy_onto(const transform_hierarchy<T>& tree, unsigned int node_id); | ||
const transform_node<T>& get_node(unsigned int id) const; | ||
tz::trs get_global_transform(unsigned int id) const; | ||
|
||
void dbgui(); | ||
private: | ||
void dbgui_node(unsigned int node_id); | ||
std::vector<transform_node<T>> nodes = {}; | ||
}; | ||
} | ||
|
||
#include "tz/core/data/transform_hierarchy.inl" | ||
#endif // TZ_CORE_DATA_TRANSFORM_HIERARCHY_HPP |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
#include "tz/dbgui/dbgui.hpp" | ||
#include "tz/core/matrix_transform.hpp" | ||
#include <type_traits> | ||
|
||
namespace tz | ||
{ | ||
template<typename T> | ||
concept has_dbgui_method = requires(T t) | ||
{ | ||
{t.dbgui()} -> std::same_as<void>; | ||
}; | ||
|
||
template<typename T> | ||
std::size_t transform_hierarchy<T>::size() const | ||
{ | ||
return this->nodes.size(); | ||
} | ||
|
||
template<typename T> | ||
std::vector<unsigned int> transform_hierarchy<T>::get_root_node_ids() const | ||
{ | ||
std::vector<unsigned int> ret; | ||
// for each element, check if any element has that as a child. | ||
// if nobody does, its a root node. | ||
for(std::size_t i = 0; i < this->size(); i++) | ||
{ | ||
bool found_child = false; | ||
for(std::size_t j = 0; j < this->size(); j++) | ||
{ | ||
if(i == j) continue; | ||
const auto& jnode = this->nodes[j]; | ||
auto iter = std::find(jnode.children.begin(), jnode.children.end(), i); | ||
if(iter != jnode.children.end()) | ||
{ | ||
// i is a child of j | ||
// skip this one, its not a root node. | ||
found_child = true; | ||
break; | ||
} | ||
} | ||
if(!found_child) | ||
{ | ||
ret.push_back(i); | ||
} | ||
} | ||
return ret; | ||
} | ||
|
||
template<typename T> | ||
unsigned int transform_hierarchy<T>::add_node(tz::trs local_transform, T data, std::optional<unsigned int> parent) | ||
{ | ||
auto id = this->nodes.size(); | ||
this->nodes.push_back({.data = data, .local_transform = local_transform, .parent = parent}); | ||
if(parent.has_value()) | ||
{ | ||
this->nodes[parent.value()].children.push_back(id); | ||
} | ||
return id; | ||
} | ||
|
||
template<typename T> | ||
unsigned int transform_hierarchy<T>::add_hierarchy(const transform_hierarchy<T>& tree) | ||
{ | ||
unsigned int offset = this->size(); | ||
for(std::size_t i = 0; i < tree.size(); i++) | ||
{ | ||
auto node = tree.get_node(i); | ||
if(node.parent.has_value()) | ||
{ | ||
node.parent.value() += offset; | ||
} | ||
this->add_node(node.local_transform, node.data, node.parent); | ||
} | ||
return offset; | ||
} | ||
|
||
template<typename T> | ||
unsigned int transform_hierarchy<T>::add_hierarchy_onto(const transform_hierarchy<T>& tree, unsigned int node_id) | ||
{ | ||
unsigned int offset = this->size(); | ||
for(std::size_t i = 0; i < tree.size(); i++) | ||
{ | ||
auto node = tree.get_node(i); | ||
if(node.parent.has_value()) | ||
{ | ||
node.parent.value() += offset; | ||
} | ||
else | ||
{ | ||
node.parent = node_id; | ||
} | ||
this->add_node(node.local_transform, node.data, node.parent); | ||
} | ||
return offset; | ||
} | ||
|
||
template<typename T> | ||
const transform_node<T>& transform_hierarchy<T>::get_node(unsigned int id) const | ||
{ | ||
return this->nodes[id]; | ||
} | ||
|
||
template<typename T> | ||
tz::trs transform_hierarchy<T>::get_global_transform(unsigned int id) const | ||
{ | ||
const auto& n = this->get_node(id); | ||
tz::trs global = n.local_transform; | ||
std::optional<unsigned int> p = n.parent; | ||
while(p.has_value()) | ||
{ | ||
const auto& pn = this->get_node(p.value()); | ||
global = get_global_transform(p.value()).combined(global); | ||
p = pn.parent; | ||
} | ||
return global; | ||
} | ||
|
||
template<typename T> | ||
void transform_hierarchy<T>::dbgui() | ||
{ | ||
for(unsigned int root : this->get_root_node_ids()) | ||
{ | ||
this->dbgui_node(root); | ||
} | ||
} | ||
|
||
template<typename T> | ||
void transform_hierarchy<T>::dbgui_node(unsigned int node_id) | ||
{ | ||
const transform_node<T>& node = this->get_node(node_id); | ||
std::string node_name = "Node " + std::to_string(node_id); | ||
if(ImGui::TreeNode(node_name.c_str())) | ||
{ | ||
if constexpr(has_dbgui_method<T>) | ||
{ | ||
node.data.dbgui(); | ||
} | ||
tz::mat4 local = node.local_transform.matrix(); | ||
tz::mat4 global = this->get_global_transform(node_id).matrix(); | ||
ImGui::Text("Local Transform"); | ||
tz::dbgui_model(local); | ||
ImGui::Text("Global Transform"); | ||
tz::dbgui_model(global); | ||
for(unsigned int child_idx : node.children) | ||
{ | ||
this->dbgui_node(child_idx); | ||
} | ||
ImGui::TreePop(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
#include "tz/core/data/transform_hierarchy.hpp" | ||
#include "tz/core/debug.hpp" | ||
|
||
void empty_hierarchy_tests() | ||
{ | ||
tz::transform_hierarchy empty; | ||
tz::assert(empty.empty()); | ||
} | ||
|
||
void root_nodes_local_equals_global() | ||
{ | ||
tz::transform_hierarchy t; | ||
unsigned int root1 = t.add_node(); | ||
unsigned int root2 = t.add_node(); | ||
unsigned int child1 = t.add_node({}, {}, root1); | ||
unsigned int child11 = t.add_node({}, {}, child1); | ||
unsigned int child2 = t.add_node({}, {}, root2); | ||
|
||
tz::assert(t.get_root_node_ids().size() == 2, "Expected 2 root nodes, but got %zu", t.get_root_node_ids().size()); | ||
tz::assert(t.get_root_node_ids() == std::vector<unsigned int>{0u, 1u} || t.get_root_node_ids() == std::vector<unsigned int>{1u, 0u}); | ||
} | ||
|
||
void parent_moves_child() | ||
{ | ||
tz::transform_hierarchy t; | ||
unsigned int parent = t.add_node(); | ||
unsigned int child = t.add_node({.translate = {1.0f, 0.0f, 0.0f}}, {}, parent); | ||
tz::assert(t.get_global_transform(child).translate == tz::vec3{1.0f, 0.0f, 0.0f}); | ||
t.get_node(child).local_transform.translate[0] += 1.0f; | ||
tz::assert(t.get_global_transform(child).translate == tz::vec3{2.0f, 0.0f, 0.0f}); | ||
t.get_node(parent).local_transform.translate[0]+= 5.0f; | ||
tz::assert(t.get_global_transform(child).translate == tz::vec3{7.0f, 0.0f, 0.0f}); | ||
} | ||
|
||
void combine_hierarchies() | ||
{ | ||
tz::transform_hierarchy<std::string> lhs; | ||
unsigned int lhsroot = lhs.add_node({}, "LHS ROOT"); | ||
|
||
tz::transform_hierarchy<std::string> rhs; | ||
unsigned int rhsroot = rhs.add_node({}, "RHS ROOT"); | ||
unsigned int rhschild = rhs.add_node({}, "RHS CHILD", rhsroot); | ||
tz::assert(rhs.get_node(rhschild).parent == rhsroot); | ||
|
||
unsigned int offset = lhs.add_hierarchy(rhs); | ||
tz::assert(lhs.get_node(lhsroot).data == "LHS ROOT"); | ||
tz::assert(lhs.get_node(rhsroot + offset).data == "RHS ROOT"); | ||
tz::assert(lhs.get_node(rhschild + offset).data == "RHS CHILD"); | ||
tz::assert(lhs.get_node(rhschild + offset).parent == (rhsroot + offset)); | ||
} | ||
|
||
void combine_hierarchy_into_node() | ||
{ | ||
tz::transform_hierarchy<std::string> lhs; | ||
unsigned int lhsroot = lhs.add_node({}, "LHS ROOT"); | ||
unsigned int lhschild = lhs.add_node({}, "LHS CHILD", lhsroot); | ||
|
||
tz::transform_hierarchy<std::string> rhs; | ||
unsigned int rhsroot = rhs.add_node({}, "RHS ROOT"); | ||
unsigned int rhschild = rhs.add_node({}, "RHS CHILD", rhsroot); | ||
tz::assert(!rhs.get_node(rhsroot).parent.has_value()); | ||
tz::assert(rhs.get_node(rhschild).parent == rhsroot); | ||
|
||
unsigned int offset = lhs.add_hierarchy_onto(rhs, lhschild); | ||
tz::assert(lhs.get_node(lhsroot).data == "LHS ROOT"); | ||
tz::assert(lhs.get_node(rhsroot + offset).data == "RHS ROOT"); | ||
tz::assert(lhs.get_node(rhschild + offset).data == "RHS CHILD"); | ||
tz::assert(lhs.get_node(rhschild + offset).parent == (rhsroot + offset)); | ||
tz::assert(lhs.get_node(rhsroot + offset).parent.has_value()); | ||
tz::assert(lhs.get_node(rhsroot + offset).parent == lhschild); | ||
} | ||
|
||
int main() | ||
{ | ||
empty_hierarchy_tests(); | ||
root_nodes_local_equals_global(); | ||
parent_moves_child(); | ||
combine_hierarchies(); | ||
combine_hierarchy_into_node(); | ||
} |