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: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
5_3_0
5_3_1
2 changes: 1 addition & 1 deletion blender_manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ schema_version = "1.0.0"

id = "modular_tree"
name = "Modular Tree"
version = "5.3.0"
version = "5.3.1"
website = "https://github.com/GoodPie/modular_tree"
tagline = "Procedural node based 3D tree generation"
maintainer = "GoodPie <brandynbb96@gmail.com>"
Expand Down
4 changes: 2 additions & 2 deletions m_tree/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.5...3.27)
cmake_minimum_required(VERSION 3.15...3.31)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

Expand Down
2 changes: 1 addition & 1 deletion m_tree/dependencies/pybind11
Submodule pybind11 updated 257 files
2 changes: 1 addition & 1 deletion m_tree/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def build():
if not os.path.exists(build_dir):
os.makedirs(build_dir)

subprocess.check_call(["cmake", "../", "-DCMAKE_POLICY_VERSION_MINIMUM=3.5"], cwd=build_dir)
subprocess.check_call(["cmake", "../"], cwd=build_dir)
subprocess.check_call(["cmake", "--build", ".", "--config", "Release"], cwd=build_dir)

print([i for i in os.listdir(os.path.join(os.path.dirname(__file__), "binaries"))])
Expand Down
2 changes: 1 addition & 1 deletion m_tree/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"

[project]
name = "m_tree"
version = "5.3.0"
version = "5.3.1"
description = "Procedural 3D tree generation library"
requires-python = ">=3.11"

Expand Down
4 changes: 2 additions & 2 deletions m_tree/python_bindings/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.5...3.27)
cmake_minimum_required(VERSION 3.15...3.31)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

Expand Down
4 changes: 2 additions & 2 deletions m_tree/source/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.5...3.27)
cmake_minimum_required(VERSION 3.15...3.31)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -fPIC")
Expand Down
7 changes: 7 additions & 0 deletions m_tree/source/meshers/base_types/TreeMesher.hpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
#pragma once
#include "source/mesh/Mesh.hpp"
#include "source/tree/Tree.hpp"
#include <concepts>

namespace Mtree
{

template <typename T>
concept Mesher = requires(T& mesher, Tree& tree) {
{ mesher.mesh_tree(tree) } -> std::same_as<Mesh>;
};

class TreeMesher
{
public:
Expand Down
52 changes: 32 additions & 20 deletions m_tree/source/meshers/manifold_mesher/ManifoldMesher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "source/utilities/NodeUtilities.hpp"
#include <algorithm>
#include <iostream>
#include <numbers>

using namespace Mtree;
using namespace Mtree::NodeUtilities;
Expand Down Expand Up @@ -81,7 +82,7 @@ CircleDesignator add_circle(const Vector3& node_position, const Node& node, floa

for (size_t i = 0; i < radial_n_points; i++)
{
float angle = (float)i / radial_n_points * 2 * M_PI;
float angle = (float)i / radial_n_points * 2 * std::numbers::pi_v<float>;
Vector3 point = cos(angle) * right + sin(angle) * up;
point = point * radius + circle_position;
int index = mesh.add_vertex(point);
Expand All @@ -96,7 +97,8 @@ CircleDesignator add_circle(const Vector3& node_position, const Node& node, floa
mesh.uvs.emplace_back((float)i / radial_n_points, uv_y);
}
mesh.uvs.emplace_back(1, uv_y);
return CircleDesignator{vertex_index, uv_index, radial_n_points};
return CircleDesignator{
.vertex_index = vertex_index, .uv_index = uv_index, .radial_n = radial_n_points};
}

bool is_index_in_branch_mask(const std::vector<IndexRange>& mask, const int index,
Expand Down Expand Up @@ -149,17 +151,22 @@ float get_branch_angle_around_parent(const Node& parent, const Node& branch)
Vector3 up = right.cross(parent.direction);
float cos_angle = projected_branch_dir.dot(right);
float sin_angle = projected_branch_dir.dot(up);
return std::fmod(std::atan2(sin_angle, cos_angle) + 2 * M_PI, 2 * M_PI);
return std::fmod(std::atan2(sin_angle, cos_angle) + 2 * std::numbers::pi_v<float>,
2 * std::numbers::pi_v<float>);
}

IndexRange get_branch_indices_on_circle(const int radial_n_points, const float circle_radius,
const float branch_radius, const float branch_angle)
{
float angle_delta = std::asin(std::clamp(branch_radius / circle_radius, -1.f, 1.f));
float increment = 2 * M_PI / radial_n_points;
int min_index = (int)(std::fmod(branch_angle - angle_delta + 2 * M_PI, 2 * M_PI) / increment);
float increment = 2 * std::numbers::pi_v<float> / radial_n_points;
int min_index = (int)(std::fmod(branch_angle - angle_delta + 2 * std::numbers::pi_v<float>,
2 * std::numbers::pi_v<float>) /
increment);
int max_index =
(int)(std::fmod(branch_angle + angle_delta + increment + 2 * M_PI, 2 * M_PI) / increment);
(int)(std::fmod(branch_angle + angle_delta + increment + 2 * std::numbers::pi_v<float>,
2 * std::numbers::pi_v<float>) /
increment);
return IndexRange{min_index, max_index};
}

Expand Down Expand Up @@ -265,14 +272,15 @@ float get_child_twist(const Node& child, const Node& parent)
Vector3 up = right.cross(child.direction);
float cos_angle = child.tangent.dot(right);
float sin_angle = child.tangent.dot(up);
return std::fmod(std::atan2(sin_angle, cos_angle) + 2 * M_PI, 2 * M_PI);
return std::fmod(std::atan2(sin_angle, cos_angle) + 2 * std::numbers::pi_v<float>,
2 * std::numbers::pi_v<float>);
}

int add_child_base_uvs(float parent_uv_y, const Node& parent, const NodeChild& child,
const IndexRange child_range, const int child_radial_n,
const int parent_radial_n, Mesh& mesh)
{
float uv_growth = parent.length / (parent.radius + .001f) / (2 * M_PI);
float uv_growth = parent.length / (parent.radius + .001f) / (2 * std::numbers::pi_v<float>);
for (size_t i = 0; i < 2;
i++) // recreating outer uvs (but without continuous (no looping back to x=0)
{
Expand All @@ -292,7 +300,8 @@ int add_child_base_uvs(float parent_uv_y, const Node& parent, const NodeChild& c
float uv_circle_radius = std::min((float)child_radial_n / parent_radial_n, uv_growth / 2) * .6f;
for (size_t i = 0; i < child_radial_n; i++) // inner uvs
{
float angle = (float)i / (child_radial_n - 1) * 2 * M_PI + M_PI;
float angle = (float)i / (child_radial_n - 1) * 2 * std::numbers::pi_v<float> +
std::numbers::pi_v<float>;
Vector2 uv_position = Vector2{cos(angle), sin(angle)} * uv_circle_radius + uv_circle_center;
mesh.uvs.push_back(uv_position);
}
Expand Down Expand Up @@ -322,9 +331,9 @@ CircleDesignator add_child_circle(const Node& parent, const NodeChild& child,
get_child_index_order(parent_base, child_radial_n, child_range, child, parent, mesh);

float child_twist = get_child_twist(child.node, parent);
int offset =
(int)(child_twist / (2 * M_PI) * child_radial_n - child_radial_n / 4 + child_radial_n) %
child_radial_n;
int offset = (int)(child_twist / (2 * std::numbers::pi_v<float>)*child_radial_n -
child_radial_n / 4 + child_radial_n) %
child_radial_n;

CircleDesignator child_base{(int)mesh.vertices.size(), (int)mesh.uvs.size(), child_radial_n};
child_base.uv_index = add_child_base_uvs(uv_y, parent, child, child_range, child_radial_n,
Expand Down Expand Up @@ -357,11 +366,12 @@ bool has_side_branches(const Node& node)
}

void mesh_node_rec(const Node& node, const Vector3& node_position, const CircleDesignator& base,
Mesh& mesh, const float uv_y, const PivotPainterContext& pp_ctx, int& stem_id_counter)
Mesh& mesh, const float uv_y, const PivotPainterContext& pp_ctx,
int& stem_id_counter)
{
if (node.children.size() < 2)
{
float uv_growth = node.length / (node.radius + .001f) / (2 * M_PI);
float uv_growth = node.length / (node.radius + .001f) / (2 * std::numbers::pi_v<float>);
auto child_circle =
add_circle(node_position, node, 1, base.radial_n, mesh, uv_y + uv_growth, pp_ctx);
bridge_circles(base, child_circle, base.radial_n, mesh);
Expand All @@ -375,8 +385,9 @@ void mesh_node_rec(const Node& node, const Vector3& node_position, const CircleD
}
else
{
float uv_growth = node.length / (node.radius + .001f) / (2 * M_PI);
auto end_circle = add_circle(node_position, node, 1, base.radial_n, mesh, uv_y + uv_growth, pp_ctx);
float uv_growth = node.length / (node.radius + .001f) / (2 * std::numbers::pi_v<float>);
auto end_circle =
add_circle(node_position, node, 1, base.radial_n, mesh, uv_y + uv_growth, pp_ctx);
std::vector<IndexRange> children_ranges = get_children_ranges(node, base.radial_n);
bridge_circles(base, end_circle, base.radial_n, mesh, &children_ranges);
for (int i = 0; i < node.children.size(); i++)
Expand All @@ -402,10 +413,11 @@ void mesh_node_rec(const Node& node, const Vector3& node_position, const CircleD
child_pp_ctx.pivot_position = child_pos;
child_pp_ctx.branch_extent = calculate_branch_extent(child.node);

auto child_base = add_child_circle(node, child, child_pos, node_position, base,
children_ranges[i - 1], uv_y, mesh, child_pp_ctx);
mesh_node_rec(node.children[i]->node, child_pos, child_base, mesh,
uv_y + uv_growth, child_pp_ctx, stem_id_counter);
auto child_base =
add_child_circle(node, child, child_pos, node_position, base,
children_ranges[i - 1], uv_y, mesh, child_pp_ctx);
mesh_node_rec(node.children[i]->node, child_pos, child_base, mesh, uv_y + uv_growth,
child_pp_ctx, stem_id_counter);
}
}
}
Expand Down
15 changes: 8 additions & 7 deletions m_tree/source/meshers/manifold_mesher/ManifoldMesher.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once
#include "../base_types/TreeMesher.hpp"
#include <string>
#include <tuple>

namespace Mtree
Expand All @@ -10,14 +11,14 @@ class ManifoldMesher : public TreeMesher
public:
struct AttributeNames
{
inline static std::string smooth_amount = "smooth_amount";
inline static std::string radius = "radius";
inline static std::string direction = "direction";
inline static const std::string smooth_amount = "smooth_amount";
inline static const std::string radius = "radius";
inline static const std::string direction = "direction";
// Pivot Painter 2.0 attributes
inline static std::string stem_id = "stem_id";
inline static std::string hierarchy_depth = "hierarchy_depth";
inline static std::string pivot_position = "pivot_position";
inline static std::string branch_extent = "branch_extent";
inline static const std::string stem_id = "stem_id";
inline static const std::string hierarchy_depth = "hierarchy_depth";
inline static const std::string pivot_position = "pivot_position";
inline static const std::string branch_extent = "branch_extent";
};

int radial_resolution = 8;
Expand Down
46 changes: 43 additions & 3 deletions m_tree/source/tree/GrowthInfo.hpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,50 @@
#pragma once
#include <Eigen/Core>
#include <variant>

namespace Mtree
{
class GrowthInfo
using Vector3 = Eigen::Vector3f;

struct BranchGrowthInfo
{
public:
virtual ~GrowthInfo() {}
float desired_length;
float origin_radius;
Vector3 position;
float current_length = 0;
float deviation_from_rest_pose = 0;
float cumulated_weight = 0;
float age = 0;
bool inactive = false;
};

struct BioNodeInfo
{
enum class NodeType
{
Meristem,
Branch,
Cut,
Ignored,
Dormant,
Flower
} type;
float branch_weight = 0;
Vector3 center_of_mass;
Vector3 absolute_position;
float vigor_ratio = 1;
float vigor = 0;
int age = 0;
float philotaxis_angle = 0;
bool is_lateral = false;

BioNodeInfo(NodeType type = NodeType::Ignored, int age = 0, float philotaxis_angle = 0,
bool is_lateral = false)
: type(type), age(age), philotaxis_angle(philotaxis_angle), is_lateral(is_lateral)
{
}
};

using GrowthInfo = std::variant<std::monostate, BranchGrowthInfo, BioNodeInfo>;

} // namespace Mtree
2 changes: 1 addition & 1 deletion m_tree/source/tree/Node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class Node
float length;
float radius;
int creator_id = 0;
std::unique_ptr<GrowthInfo> growthInfo = nullptr;
GrowthInfo growthInfo;

bool is_leaf() const;

Expand Down
Loading