Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ add_library(iridescence
src/guik/imgui_application.cpp
src/guik/screen_capture.cpp
src/guik/recent_files.cpp
src/guik/drawable_tree.cpp
src/guik/camera/camera_control.cpp
src/guik/camera/orbit_camera_control_xy.cpp
src/guik/camera/orbit_camera_control_xz.cpp
Expand Down Expand Up @@ -251,6 +252,7 @@ if(BUILD_EXAMPLES)
${example_src}
)
target_link_libraries(${example_name}
fmt
iridescence
)
endforeach()
Expand Down
110 changes: 110 additions & 0 deletions include/guik/drawable_tree.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#ifndef GUIK_DRAWABLE_TREE_HPP
#define GUIK_DRAWABLE_TREE_HPP

#include <glk/drawable.hpp>
#include <guik/viewer/shader_setting.hpp>

namespace guik {

/**
@brief Hierarchical drawable tree.
Drawables are managed with slash-separated path names (e.g., "tree/child/grandchild/obj1").
The model matrix of a node is hierarchically applied to its children.
For example, for a tree "A/B/C", the model matrix of "C" becomes "A * B * C".
@example
auto tree = std::make_shared<DrawableTree>();
// Register drawables to the tree.
tree->update_drawable("parent/child/node1", drawable1, guik::VertexColor(node1_matrix));
tree->update_drawable("parent/child/node2", drawable2, guik::VertexColor(node2_matrix));
// Update shader settings. This matrix is applied to both "node1" and "node2".
tree->update_setting("parent/child", guik::ShaderSetting(child_matrix));

// Overwrite the color settings of all children of "parent/child".
tree->update_tree_color_mode("parent/child", guik::ColorMode::FLAT_COLOR);
tree->update_tree_color("parent/child", Eigen::Vector4f(0.0f, 0.0f, 1.0f, 1.0f));

// Overwrite the point scale of all children of "parent/child".
tree->update_tree_setting("parent/child", "point_scale", 2.0f);

// Output the tree structure to stdout for debugging.
tree->print_tree();

viewer->update_drawable("tree", tree);
*/
class DrawableTree : public glk::Drawable {
public:
using Ptr = std::shared_ptr<DrawableTree>;
using ConstPtr = std::shared_ptr<const DrawableTree>;

DrawableTree();

/// @brief Update drawable at a tree node.
/// @param name Slash-separated path name (e.g., "parent/child/node1")
/// @param drawable Drawable object
/// @param setting Shader setting
void update_drawable(const std::string& name, glk::Drawable::ConstPtr drawable, const guik::ShaderSetting& setting);

/// @brief Update shader setting of a node.
void update_setting(const std::string& name, const guik::ShaderSetting& setting);

/// @brief Update shader setting of all children of a node.
template <typename T>
void update_tree_setting(const std::string& name, const std::string& param_name, const T& val) {
const auto names = split_path(name);
update_tree_setting(names.data(), names.data() + names.size(), param_name, val);
}

/// @brief Update color mode of all children of a node.
void update_tree_color_mode(const std::string& name, guik::ColorMode::MODE color_mode);

/// @brief Update color of all children of a node.
void update_tree_color(const std::string& name, const Eigen::Vector4f& color);

/// @brief Remove a subtree.
bool remove(const std::string& name);

/// @brief Print the tree structure to stdout.
void print_tree(int depth = 0, const Eigen::Matrix4f& parent_model_matrix = Eigen::Matrix4f::Identity()) const;

/// @brief Draw the tree.
virtual void draw(glk::GLSLShader& shader) const override;

private:
DrawableTree(const std::string& name);

void update_drawable(const std::string* first, const std::string* last, glk::Drawable::ConstPtr drawable, const guik::ShaderSetting& setting);

bool remove(const std::string* first, const std::string* last);

template <typename T>
void update_tree_setting(const std::string* first, const std::string* last, const std::string& param_name, const T& val) {
if (first == last) {
setting.add<T>(param_name, val);
for (auto child : children) {
child.second->update_tree_setting<T>(first, last, param_name, val);
}
} else {
auto found = children.find(*first);
if (found == children.end()) {
return;
}

found->second->update_tree_setting<T>(first + 1, last, param_name, val);
}
}

void draw(glk::GLSLShader& shader, const Eigen::Matrix4f& parent_model_matrix) const;

private:
std::vector<std::string> split_path(const std::string& path);

private:
const std::string name; // Node name
guik::ShaderSetting setting; // Shader setting associated with the node
glk::Drawable::ConstPtr drawable; // Drawable object associated with the node
std::unordered_map<std::string, DrawableTree::Ptr> children; // Child nodes
};

} // namespace guik

#endif
11 changes: 9 additions & 2 deletions include/guik/viewer/shader_setting.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ struct ShaderSetting {
/// @param name Parameter name
/// @return Parameter value if found, otherwise std::nullopt
template <typename T>
std::optional<T> get(const std::string& name) {
std::optional<T> get(const std::string& name) const {
for (const auto& param : params) {
if (param->name != name) {
continue;
Expand All @@ -139,7 +139,7 @@ struct ShaderSetting {
/// @param name Parameter name
/// @return Parameter value
template <typename T>
const T& cast(const std::string& name) {
const T& cast(const std::string& name) const {
for (const auto& param : params) {
if (param->name != name) {
continue;
Expand Down Expand Up @@ -170,6 +170,11 @@ struct ShaderSetting {
ShaderSetting& dymamic_object() { return add("dynamic_object", 1); }

// Color
int color_mode() const {
auto p = static_cast<ShaderParameter<int>*>(params[0].get());
return p->value;
}

ShaderSetting& set_color(float r, float g, float b, float a) {
if (a < 0.999f) {
transparent = true;
Expand Down Expand Up @@ -233,6 +238,8 @@ struct ShaderSetting {
return *this;
}

bool has_model_matrix() const { return params[2] != nullptr; }

Eigen::Matrix4f model_matrix() const {
auto p = static_cast<ShaderParameter<Eigen::Matrix4f>*>(params[2].get());
return p->value;
Expand Down
137 changes: 137 additions & 0 deletions src/guik/drawable_tree.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
#include <guik/drawable_tree.hpp>

namespace guik {

DrawableTree::DrawableTree() : name("root") {}

DrawableTree::DrawableTree(const std::string& name) : name(name) {}

void DrawableTree::update_drawable(const std::string& name, glk::Drawable::ConstPtr drawable, const guik::ShaderSetting& setting) {
const auto names = split_path(name);
update_drawable(names.data(), names.data() + names.size(), drawable, setting);
}

void DrawableTree::update_setting(const std::string& name, const guik::ShaderSetting& setting) {
const auto names = split_path(name);
update_drawable(names.data(), names.data() + names.size(), nullptr, setting);
}

void DrawableTree::update_tree_color_mode(const std::string& name, guik::ColorMode::MODE color_mode) {
update_tree_setting(name, "color_mode", color_mode);
}
void DrawableTree::update_tree_color(const std::string& name, const Eigen::Vector4f& color) {
update_tree_setting(name, "material_color", color);
}

bool DrawableTree::remove(const std::string& name) {
const auto names = split_path(name);
if (!remove(names.data(), names.data() + names.size())) {
std::cerr << "warning: failed to remove subtree : " << name << std::endl;
return false;
}
return true;
}

void DrawableTree::draw(glk::GLSLShader& shader) const {
draw(shader, Eigen::Matrix4f::Identity());
}

void DrawableTree::print_tree(int depth, const Eigen::Matrix4f& parent_model_matrix) const {
Eigen::Matrix4f model_matrix = setting.has_model_matrix() ? parent_model_matrix * setting.model_matrix() : parent_model_matrix;

for (int i = 0; i < depth; i++) {
std::cout << " ";
}

Eigen::Quaternionf quat(model_matrix.block<3, 3>(0, 0));
Eigen::Vector3f trans(model_matrix.block<3, 1>(0, 3));

std::cout << name << " : trans=(" << trans.x() << "," << trans.y() << "," << trans.z() << ") quat=(" << quat.x() << "," << quat.y() << "," << quat.z() << "," << quat.w() << ")"
<< (drawable ? " D" : "") << std::endl;

for (const auto& child : children) {
child.second->print_tree(depth + 1, model_matrix);
}
}

void DrawableTree::update_drawable(const std::string* first, const std::string* last, glk::Drawable::ConstPtr drawable, const guik::ShaderSetting& setting) {
if (first == last) {
this->setting = setting;
if (drawable) {
this->drawable = drawable;
}
return;
}

auto found = children.find(*first);
if (found == children.end()) {
auto child = DrawableTree::Ptr(new DrawableTree(*first));
found = children.emplace_hint(found, *first, child);
}

found->second->update_drawable(first + 1, last, drawable, setting);
}

bool DrawableTree::remove(const std::string* first, const std::string* last) {
if (first == last) {
return false;
}

auto found = children.find(*first);
if (found == children.end()) {
return false;
}

if (first + 1 == last) {
children.erase(found);
return true;
}

return found->second->remove(first + 1, last);
}

void DrawableTree::draw(glk::GLSLShader& shader, const Eigen::Matrix4f& parent_model_matrix) const {
Eigen::Matrix4f model_matrix = parent_model_matrix;
if (setting.has_model_matrix()) {
model_matrix = model_matrix * setting.model_matrix();
}

if (drawable) {
setting.set(shader);
shader.set_uniform("model_matrix", model_matrix);
drawable->draw(shader);
}

for (const auto& child : children) {
setting.set(shader);
child.second->draw(shader, model_matrix);
}
}

std::vector<std::string> DrawableTree::split_path(const std::string& path) {
if (path.empty()) {
std::cerr << "warning: empty path" << std::endl;
return {};
}

std::vector<std::string> names;
int cursor = path.find_first_not_of('/');
while (cursor < path.size()) {
int next = path.find('/', cursor);
if (next == cursor) {
std::cout << "warning: skip empty name : " << path.substr(0, cursor) << "()" << path.substr(cursor) << std::endl;
cursor++;
continue;
}

if (next == std::string::npos) {
names.push_back(path.substr(cursor));
break;
}
names.push_back(path.substr(cursor, next - cursor));
cursor = next + 1;
}

return names;
}
} // namespace guik