/*************************************************************************/ /* procedural_tree_mesh.cpp */ /*************************************************************************/ /* This file is part of: */ /* PANDEMONIUM ENGINE */ /* https://github.com/Relintai/pandemonium_engine */ /*************************************************************************/ /* Copyright (c) 2022-present Péter Magyar. */ /* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ /* "Software"), to deal in the Software without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of the Software, and to */ /* permit persons to whom the Software is furnished to do so, subject to */ /* the following conditions: */ /* */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "procedural_tree_mesh.h" #include "core/containers/pool_vector.h" #include "core/object/class_db.h" #include "core/object/object.h" #include "servers/rendering_server.h" #include "proctree/proctree.h" // General int ProceduralTreeMesh::get_seed() const { return _seed; } void ProceduralTreeMesh::set_seed(const int p_value) { _seed = p_value; _request_update(); } int ProceduralTreeMesh::get_branch_segments() const { return _branch_segments; } void ProceduralTreeMesh::set_branch_segments(const int p_value) { _branch_segments = p_value; _request_update(); } int ProceduralTreeMesh::get_branch_levels() const { return _branch_levels; } void ProceduralTreeMesh::set_branch_levels(const int p_value) { _branch_levels = p_value; _request_update(); } int ProceduralTreeMesh::get_trunk_forks() const { return _trunk_forks; } void ProceduralTreeMesh::set_trunk_forks(const int p_value) { _trunk_forks = p_value; _request_update(); } float ProceduralTreeMesh::get_texture_v_multiplier() const { return _texture_v_multiplier; } void ProceduralTreeMesh::set_texture_v_multiplier(const float p_value) { _texture_v_multiplier = p_value; _request_update(); } float ProceduralTreeMesh::get_twig_scale() const { return _twig_scale; } void ProceduralTreeMesh::set_twig_scale(const float p_value) { _twig_scale = p_value; _request_update(); } // Branching float ProceduralTreeMesh::branching_get_initial_length() const { return _branching_initial_length; } void ProceduralTreeMesh::branching_set_initial_length(const float p_value) { _branching_initial_length = p_value; _request_update(); } float ProceduralTreeMesh::branching_get_length_falloff_rate() const { return _branching_length_falloff_rate; } void ProceduralTreeMesh::branching_set_length_falloff_rate(const float p_value) { _branching_length_falloff_rate = p_value; _request_update(); } float ProceduralTreeMesh::branching_get_length_falloff_power() const { return _branching_length_falloff_power; } void ProceduralTreeMesh::branching_set_length_falloff_power(const float p_value) { _branching_length_falloff_power = p_value; _request_update(); } float ProceduralTreeMesh::branching_get_max_clumping() const { return _branching_max_clumping; } void ProceduralTreeMesh::branching_set_max_clumping(const float p_value) { _branching_max_clumping = p_value; _request_update(); } float ProceduralTreeMesh::branching_get_min_clumping() const { return _branching_min_clumping; } void ProceduralTreeMesh::branching_set_min_clumping(const float p_value) { _branching_min_clumping = p_value; _request_update(); } float ProceduralTreeMesh::branching_get_symmetry() const { return _branching_symmetry; } void ProceduralTreeMesh::branching_set_symmetry(const float p_value) { _branching_symmetry = p_value; _request_update(); } float ProceduralTreeMesh::branching_get_droop() const { return _branching_droop; } void ProceduralTreeMesh::branching_set_droop(const float p_value) { _branching_droop = p_value; _request_update(); } float ProceduralTreeMesh::branching_get_growth() const { return _branching_growth; } void ProceduralTreeMesh::branching_set_growth(const float p_value) { _branching_growth = p_value; _request_update(); } float ProceduralTreeMesh::branching_get_sweep() const { return _branching_sweep; } void ProceduralTreeMesh::branching_set_sweep(const float p_value) { _branching_sweep = p_value; _request_update(); } // Trunk float ProceduralTreeMesh::trunk_get_radius() const { return _trunk_radius; } void ProceduralTreeMesh::trunk_set_radius(const float p_value) { _trunk_radius = p_value; _request_update(); } float ProceduralTreeMesh::trunk_get_radius_falloff() const { return _trunk_radius_falloff; } void ProceduralTreeMesh::trunk_set_radius_falloff(const float p_value) { _trunk_radius_falloff = p_value; _request_update(); } float ProceduralTreeMesh::trunk_get_climb_rate() const { return _trunk_climb_rate; } void ProceduralTreeMesh::trunk_set_climb_rate(const float p_value) { _trunk_climb_rate = p_value; _request_update(); } float ProceduralTreeMesh::trunk_get_kink() const { return _trunk_kink; } void ProceduralTreeMesh::trunk_set_kink(const float p_value) { _trunk_kink = p_value; _request_update(); } float ProceduralTreeMesh::trunk_get_taper_rate() const { return _trunk_taper_rate; } void ProceduralTreeMesh::trunk_set_taper_rate(const float p_value) { _trunk_taper_rate = p_value; _request_update(); } float ProceduralTreeMesh::trunk_get_twists() const { return _trunk_twists; } void ProceduralTreeMesh::trunk_set_twists(const float p_value) { _trunk_twists = p_value; _request_update(); } float ProceduralTreeMesh::trunk_get_length() const { return _trunk_length; } void ProceduralTreeMesh::trunk_set_length(const float p_value) { _trunk_length = p_value; _request_update(); } // Meshes bool ProceduralTreeMesh::get_enable_branch_mesh() const { return _enable_branch_mesh; } void ProceduralTreeMesh::set_enable_branch_mesh(const bool p_value) { _enable_branch_mesh = p_value; _request_update(); } bool ProceduralTreeMesh::get_enable_twig_mesh() const { return _enable_twig_mesh; } void ProceduralTreeMesh::set_enable_twig_mesh(const bool p_value) { _enable_twig_mesh = p_value; _request_update(); } bool ProceduralTreeMesh::get_flip_branch_mesh_faces() const { return _flip_branch_mesh_faces; } void ProceduralTreeMesh::set_flip_branch_mesh_faces(const bool p_value) { _flip_branch_mesh_faces = p_value; _request_update(); } bool ProceduralTreeMesh::get_flip_twig_mesh_faces() const { return _flip_twig_mesh_faces; } void ProceduralTreeMesh::set_flip_twig_mesh_faces(const bool p_value) { _flip_twig_mesh_faces = p_value; _request_update(); } void ProceduralTreeMesh::_update() const { RenderingServer::get_singleton()->mesh_clear(mesh); for (int i = 0; i < TREE_SURFACE_COUNT; ++i) { _surfaces[i].surface_index = -1; } aabb = AABB(); if (!_enable_branch_mesh && !_enable_twig_mesh) { return; } Proctree::Tree tree; // Grneral tree.mProperties.mSeed = _seed; tree.mProperties.mSegments = _branch_segments; tree.mProperties.mLevels = _branch_levels; tree.mProperties.mTreeSteps = _trunk_forks; tree.mProperties.mVMultiplier = _texture_v_multiplier; tree.mProperties.mTwigScale = _twig_scale; // Branching tree.mProperties.mInitialBranchLength = _branching_initial_length; tree.mProperties.mLengthFalloffFactor = _branching_length_falloff_rate; tree.mProperties.mLengthFalloffPower = _branching_length_falloff_power; tree.mProperties.mClumpMax = _branching_max_clumping; tree.mProperties.mClumpMin = _branching_min_clumping; tree.mProperties.mBranchFactor = _branching_symmetry; tree.mProperties.mDropAmount = _branching_droop; tree.mProperties.mGrowAmount = _branching_growth; tree.mProperties.mSweepAmount = _branching_sweep; // Trunk tree.mProperties.mMaxRadius = _trunk_radius; tree.mProperties.mRadiusFalloffRate = _trunk_radius_falloff; tree.mProperties.mClimbRate = _trunk_climb_rate; tree.mProperties.mTrunkKink = _trunk_kink; tree.mProperties.mTaperRate = _trunk_taper_rate; tree.mProperties.mTwistRate = _trunk_twists; tree.mProperties.mTrunkLength = _trunk_length; tree.generate(); if (_enable_twig_mesh) { int vert_count = tree.mTwigVertCount; PoolVector uvs; PoolVector normals; PoolVector verts; uvs.resize(vert_count); normals.resize(vert_count); verts.resize(vert_count); if (!_flip_twig_mesh_faces) { PoolVector::Write uvw = uvs.write(); PoolVector::Write nw = normals.write(); PoolVector::Write vw = verts.write(); for (int i = 0; i < vert_count; ++i) { Proctree::fvec2 tuv = tree.mTwigUV[i]; Proctree::fvec3 tnormal = tree.mTwigNormal[i]; Proctree::fvec3 tvert = tree.mTwigVert[i]; uvw[i] = Vector2(tuv.u, tuv.v); nw[i] = Vector3(tnormal.x, tnormal.y, tnormal.z); vw[i] = Vector3(tvert.x, tvert.y, tvert.z); } } else { PoolVector::Write uvw = uvs.write(); PoolVector::Write nw = normals.write(); PoolVector::Write vw = verts.write(); for (int i = 0; i < vert_count; ++i) { Proctree::fvec2 tuv = tree.mTwigUV[i]; Proctree::fvec3 tnormal = tree.mTwigNormal[i]; Proctree::fvec3 tvert = tree.mTwigVert[i]; uvw[i] = Vector2(tuv.u, tuv.v); nw[i] = -Vector3(tnormal.x, tnormal.y, tnormal.z); vw[i] = Vector3(tvert.x, tvert.y, tvert.z); } } PoolVector indices; int face_count = tree.mTwigFaceCount; indices.resize(face_count * 3); if (!_flip_twig_mesh_faces) { PoolVector::Write iw = indices.write(); for (int i = 0; i < face_count; ++i) { Proctree::ivec3 tface = tree.mTwigFace[i]; int ind = i * 3; iw[ind] = tface.y; iw[ind + 1] = tface.x; iw[ind + 2] = tface.z; } } else { PoolVector::Write iw = indices.write(); for (int i = 0; i < face_count; ++i) { Proctree::ivec3 tface = tree.mTwigFace[i]; int ind = i * 3; iw[ind] = tface.x; iw[ind + 1] = tface.y; iw[ind + 2] = tface.z; } } { PoolVector::Read r = verts.read(); if (vert_count > 0) { aabb.position = r[0]; } for (int i = 1; i < vert_count; ++i) { aabb.expand_to(r[i]); } } Array arr; arr.resize(RS::ARRAY_MAX); arr[RS::ARRAY_VERTEX] = verts; arr[RS::ARRAY_TEX_UV] = uvs; arr[RS::ARRAY_NORMAL] = normals; arr[RS::ARRAY_INDEX] = indices; RenderingServer::get_singleton()->mesh_add_surface_from_arrays(mesh, RenderingServer::PRIMITIVE_TRIANGLES, arr); int sc = RenderingServer::get_singleton()->mesh_get_surface_count(mesh) - 1; _surfaces[TREE_SURFACE_TWIG].surface_index = sc; RenderingServer::get_singleton()->mesh_surface_set_material(mesh, sc, _surfaces[TREE_SURFACE_TWIG].material.is_null() ? RID() : _surfaces[TREE_SURFACE_TWIG].material->get_rid()); } if (_enable_branch_mesh) { int vert_count = tree.mVertCount; PoolVector uvs; PoolVector normals; PoolVector verts; uvs.resize(vert_count); normals.resize(vert_count); verts.resize(vert_count); if (!_flip_branch_mesh_faces) { PoolVector::Write uvw = uvs.write(); PoolVector::Write nw = normals.write(); PoolVector::Write vw = verts.write(); for (int i = 0; i < vert_count; ++i) { Proctree::fvec2 tuv = tree.mUV[i]; Proctree::fvec3 tnormal = tree.mNormal[i]; Proctree::fvec3 tvert = tree.mVert[i]; uvw[i] = Vector2(tuv.u, tuv.v); nw[i] = Vector3(tnormal.x, tnormal.y, tnormal.z); vw[i] = Vector3(tvert.x, tvert.y, tvert.z); } } else { PoolVector::Write uvw = uvs.write(); PoolVector::Write nw = normals.write(); PoolVector::Write vw = verts.write(); for (int i = 0; i < vert_count; ++i) { Proctree::fvec2 tuv = tree.mUV[i]; Proctree::fvec3 tnormal = tree.mNormal[i]; Proctree::fvec3 tvert = tree.mVert[i]; uvw[i] = Vector2(tuv.u, tuv.v); nw[i] = -Vector3(tnormal.x, tnormal.y, tnormal.z); vw[i] = Vector3(tvert.x, tvert.y, tvert.z); } } PoolVector indices; int face_count = tree.mFaceCount; indices.resize(face_count * 3); if (!_flip_branch_mesh_faces) { PoolVector::Write iw = indices.write(); for (int i = 0; i < face_count; ++i) { Proctree::ivec3 tface = tree.mFace[i]; int ind = i * 3; iw[ind] = tface.y; iw[ind + 1] = tface.x; iw[ind + 2] = tface.z; } } else { PoolVector::Write iw = indices.write(); for (int i = 0; i < face_count; ++i) { Proctree::ivec3 tface = tree.mFace[i]; int ind = i * 3; iw[ind] = tface.x; iw[ind + 1] = tface.y; iw[ind + 2] = tface.z; } } { PoolVector::Read r = verts.read(); if (!_enable_twig_mesh && vert_count > 0) { aabb.position = r[0]; } for (int i = 0; i < vert_count; ++i) { aabb.expand_to(r[i]); } } Array arr; arr.resize(RS::ARRAY_MAX); arr[RS::ARRAY_VERTEX] = verts; arr[RS::ARRAY_TEX_UV] = uvs; arr[RS::ARRAY_NORMAL] = normals; arr[RS::ARRAY_INDEX] = indices; RenderingServer::get_singleton()->mesh_add_surface_from_arrays(mesh, RenderingServer::PRIMITIVE_TRIANGLES, arr); int sc = RenderingServer::get_singleton()->mesh_get_surface_count(mesh) - 1; _surfaces[TREE_SURFACE_TRUNK].surface_index = sc; RenderingServer::get_singleton()->mesh_surface_set_material(mesh, sc, _surfaces[TREE_SURFACE_TRUNK].material.is_null() ? RID() : _surfaces[TREE_SURFACE_TRUNK].material->get_rid()); } pending_request = false; clear_cache(); const_cast(this)->emit_changed(); } void ProceduralTreeMesh::_request_update() { if (pending_request) { return; } _update(); } int ProceduralTreeMesh::get_surface_count() const { if (pending_request) { _update(); } return TREE_SURFACE_COUNT; } int ProceduralTreeMesh::surface_get_array_len(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, TREE_SURFACE_COUNT, -1); if (pending_request) { _update(); } int si = _surfaces[p_idx].surface_index; if (si == -1) { return 0; } return RenderingServer::get_singleton()->mesh_surface_get_array_len(mesh, si); } int ProceduralTreeMesh::surface_get_array_index_len(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, TREE_SURFACE_COUNT, -1); if (pending_request) { _update(); } int si = _surfaces[p_idx].surface_index; if (si == -1) { return 0; } return RenderingServer::get_singleton()->mesh_surface_get_array_index_len(mesh, si); } Array ProceduralTreeMesh::surface_get_arrays(int p_surface) const { ERR_FAIL_INDEX_V(p_surface, TREE_SURFACE_COUNT, Array()); if (pending_request) { _update(); } int si = _surfaces[p_surface].surface_index; if (si == -1) { return Array(); } return RenderingServer::get_singleton()->mesh_surface_get_arrays(mesh, si); } Array ProceduralTreeMesh::surface_get_blend_shape_arrays(int p_surface) const { ERR_FAIL_INDEX_V(p_surface, TREE_SURFACE_COUNT, Array()); if (pending_request) { _update(); } return Array(); } uint32_t ProceduralTreeMesh::surface_get_format(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, TREE_SURFACE_COUNT, 0); if (pending_request) { _update(); } int si = _surfaces[p_idx].surface_index; if (si == -1) { return 0; } return RenderingServer::get_singleton()->mesh_surface_get_format(mesh, si); } Mesh::PrimitiveType ProceduralTreeMesh::surface_get_primitive_type(int p_idx) const { return Mesh::PRIMITIVE_TRIANGLES; } void ProceduralTreeMesh::surface_set_material(int p_idx, const Ref &p_material) { ERR_FAIL_INDEX(p_idx, TREE_SURFACE_COUNT); switch (p_idx) { case TREE_SURFACE_TWIG: set_twig_material(p_material); break; case TREE_SURFACE_TRUNK: set_trunk_material(p_material); break; case TREE_SURFACE_COUNT: default: break; } } Ref ProceduralTreeMesh::surface_get_material(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, TREE_SURFACE_COUNT, nullptr); return _surfaces[p_idx].material; } int ProceduralTreeMesh::get_blend_shape_count() const { return 0; } StringName ProceduralTreeMesh::get_blend_shape_name(int p_index) const { return StringName(); } void ProceduralTreeMesh::set_blend_shape_name(int p_index, const StringName &p_name) { } AABB ProceduralTreeMesh::get_aabb() const { if (pending_request) { _update(); } return aabb; } RID ProceduralTreeMesh::get_rid() const { if (pending_request) { _update(); } return mesh; } void ProceduralTreeMesh::set_twig_material(const Ref &p_material) { _surfaces[TREE_SURFACE_TWIG].material = p_material; if (!pending_request) { int si = _surfaces[TREE_SURFACE_TWIG].surface_index; if (si != -1) { // just apply it, else it'll happen when _update is called. RenderingServer::get_singleton()->mesh_surface_set_material(mesh, si, _surfaces[TREE_SURFACE_TWIG].material.is_null() ? RID() : _surfaces[TREE_SURFACE_TWIG].material->get_rid()); } _change_notify(); emit_changed(); }; } Ref ProceduralTreeMesh::get_twig_material() const { return _surfaces[TREE_SURFACE_TWIG].material; } void ProceduralTreeMesh::set_trunk_material(const Ref &p_material) { _surfaces[TREE_SURFACE_TRUNK].material = p_material; if (!pending_request) { int si = _surfaces[TREE_SURFACE_TRUNK].surface_index; if (si != -1) { // just apply it, else it'll happen when _update is called. RenderingServer::get_singleton()->mesh_surface_set_material(mesh, si, _surfaces[TREE_SURFACE_TRUNK].material.is_null() ? RID() : _surfaces[TREE_SURFACE_TRUNK].material->get_rid()); } _change_notify(); emit_changed(); }; } Ref ProceduralTreeMesh::get_trunk_material() const { return _surfaces[TREE_SURFACE_TRUNK].material; } void ProceduralTreeMesh::set_custom_aabb(const AABB &p_custom) { custom_aabb = p_custom; RS::get_singleton()->mesh_set_custom_aabb(mesh, custom_aabb); emit_changed(); } AABB ProceduralTreeMesh::get_custom_aabb() const { return custom_aabb; } Array ProceduralTreeMesh::get_mesh_arrays() const { Array arr; for (int i = 0; i < TREE_SURFACE_COUNT; ++i) { int si = _surfaces[i].surface_index; if (si == -1) { arr.push_back(Array()); continue; } arr.push_back(surface_get_arrays(i)); } return arr; } Ref ProceduralTreeMesh::to_array_mesh() const { Ref mesh; mesh.instance(); if (!_enable_twig_mesh && !_enable_branch_mesh) { return mesh; } for (int i = 0; i < TREE_SURFACE_COUNT; ++i) { int si = _surfaces[i].surface_index; if (si == -1) { continue; } Array arr = surface_get_arrays(i); mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arr); int msi = mesh->get_surface_count() - 1; mesh->surface_set_material(msi, surface_get_material(i)); } return mesh; } ProceduralTreeMesh::ProceduralTreeMesh() { // defaults mesh = RID_PRIME(RenderingServer::get_singleton()->mesh_create()); // make sure we do an update after we've finished constructing our object pending_request = true; // General _seed = 262; _branch_segments = 6; _branch_levels = 5; _twig_scale = 0.39; _trunk_forks = 5; _texture_v_multiplier = 0.36; // Branching _branching_initial_length = 0.49; _branching_length_falloff_rate = 0.85; _branching_length_falloff_power = 0.99; _branching_max_clumping = 0.454; _branching_min_clumping = 0.404; _branching_symmetry = 2.45; _branching_droop = -0.1; _branching_growth = 0.235; _branching_sweep = 0.01; // Trunk _trunk_radius = 0.139; _trunk_radius_falloff = 0.73; _trunk_climb_rate = 0.371; _trunk_kink = 0.093; _trunk_taper_rate = 0.947; _trunk_twists = 3.02; _trunk_length = 2.4; _enable_branch_mesh = true; _enable_twig_mesh = true; _flip_branch_mesh_faces = false; _flip_twig_mesh_faces = false; } ProceduralTreeMesh::~ProceduralTreeMesh() { RenderingServer::get_singleton()->free(mesh); } void ProceduralTreeMesh::_bind_methods() { ADD_GROUP("General", ""); ClassDB::bind_method(D_METHOD("get_seed"), &ProceduralTreeMesh::get_seed); ClassDB::bind_method(D_METHOD("set_seed", "value"), &ProceduralTreeMesh::set_seed); ADD_PROPERTY(PropertyInfo(Variant::INT, "seed"), "set_seed", "get_seed"); ClassDB::bind_method(D_METHOD("get_branch_segments"), &ProceduralTreeMesh::get_branch_segments); ClassDB::bind_method(D_METHOD("set_branch_segments", "value"), &ProceduralTreeMesh::set_branch_segments); ADD_PROPERTY(PropertyInfo(Variant::INT, "branch_segments", PROPERTY_HINT_RANGE, "2,32,2"), "set_branch_segments", "get_branch_segments"); ClassDB::bind_method(D_METHOD("get_branch_levels"), &ProceduralTreeMesh::get_branch_levels); ClassDB::bind_method(D_METHOD("set_branch_levels", "value"), &ProceduralTreeMesh::set_branch_levels); ADD_PROPERTY(PropertyInfo(Variant::INT, "branch_levels", PROPERTY_HINT_RANGE, "1,10,1"), "set_branch_levels", "get_branch_levels"); ClassDB::bind_method(D_METHOD("get_trunk_forks"), &ProceduralTreeMesh::get_trunk_forks); ClassDB::bind_method(D_METHOD("set_trunk_forks", "value"), &ProceduralTreeMesh::set_trunk_forks); ADD_PROPERTY(PropertyInfo(Variant::INT, "trunk_forks", PROPERTY_HINT_RANGE, "0,32,1"), "set_trunk_forks", "get_trunk_forks"); ClassDB::bind_method(D_METHOD("get_texture_v_multiplier"), &ProceduralTreeMesh::get_texture_v_multiplier); ClassDB::bind_method(D_METHOD("set_texture_v_multiplier", "value"), &ProceduralTreeMesh::set_texture_v_multiplier); ADD_PROPERTY(PropertyInfo(Variant::REAL, "texture_v_multiplier", PROPERTY_HINT_RANGE, "0.01,10,0.01"), "set_texture_v_multiplier", "get_texture_v_multiplier"); ClassDB::bind_method(D_METHOD("get_twig_scale"), &ProceduralTreeMesh::get_twig_scale); ClassDB::bind_method(D_METHOD("set_twig_scale", "value"), &ProceduralTreeMesh::set_twig_scale); ADD_PROPERTY(PropertyInfo(Variant::REAL, "twig_scale", PROPERTY_HINT_RANGE, "0.01,2,0.01"), "set_twig_scale", "get_twig_scale"); ADD_GROUP("Branching", "branching"); ClassDB::bind_method(D_METHOD("branching_get_initial_length"), &ProceduralTreeMesh::branching_get_initial_length); ClassDB::bind_method(D_METHOD("branching_set_initial_length", "value"), &ProceduralTreeMesh::branching_set_initial_length); ADD_PROPERTY(PropertyInfo(Variant::REAL, "branching_initial_length", PROPERTY_HINT_RANGE, "0.01,5,0.001"), "branching_set_initial_length", "branching_get_initial_length"); ClassDB::bind_method(D_METHOD("branching_get_length_falloff_rate"), &ProceduralTreeMesh::branching_get_length_falloff_rate); ClassDB::bind_method(D_METHOD("branching_set_length_falloff_rate", "value"), &ProceduralTreeMesh::branching_set_length_falloff_rate); ADD_PROPERTY(PropertyInfo(Variant::REAL, "branching_length_falloff_rate", PROPERTY_HINT_RANGE, "0.01,1.5,0.001"), "branching_set_length_falloff_rate", "branching_get_length_falloff_rate"); ClassDB::bind_method(D_METHOD("branching_get_length_falloff_power"), &ProceduralTreeMesh::branching_get_length_falloff_power); ClassDB::bind_method(D_METHOD("branching_set_length_falloff_power", "value"), &ProceduralTreeMesh::branching_set_length_falloff_power); ADD_PROPERTY(PropertyInfo(Variant::REAL, "branching_length_falloff_power", PROPERTY_HINT_RANGE, "-2,2,0.001"), "branching_set_length_falloff_power", "branching_get_length_falloff_power"); ClassDB::bind_method(D_METHOD("branching_get_max_clumping"), &ProceduralTreeMesh::branching_get_max_clumping); ClassDB::bind_method(D_METHOD("branching_set_max_clumping", "value"), &ProceduralTreeMesh::branching_set_max_clumping); ADD_PROPERTY(PropertyInfo(Variant::REAL, "branching_max_clumping", PROPERTY_HINT_RANGE, "0.01,10,0.001"), "branching_set_max_clumping", "branching_get_max_clumping"); ClassDB::bind_method(D_METHOD("branching_get_min_clumping"), &ProceduralTreeMesh::branching_get_min_clumping); ClassDB::bind_method(D_METHOD("branching_set_min_clumping", "value"), &ProceduralTreeMesh::branching_set_min_clumping); ADD_PROPERTY(PropertyInfo(Variant::REAL, "branching_min_clumping", PROPERTY_HINT_RANGE, "0.01,10,0.001"), "branching_set_min_clumping", "branching_get_min_clumping"); ClassDB::bind_method(D_METHOD("branching_get_symmetry"), &ProceduralTreeMesh::branching_get_symmetry); ClassDB::bind_method(D_METHOD("branching_set_symmetry", "value"), &ProceduralTreeMesh::branching_set_symmetry); ADD_PROPERTY(PropertyInfo(Variant::REAL, "branching_symmetry", PROPERTY_HINT_RANGE, "2,4,0.001"), "branching_set_symmetry", "branching_get_symmetry"); ClassDB::bind_method(D_METHOD("branching_get_droop"), &ProceduralTreeMesh::branching_get_droop); ClassDB::bind_method(D_METHOD("branching_set_droop", "value"), &ProceduralTreeMesh::branching_set_droop); ADD_PROPERTY(PropertyInfo(Variant::REAL, "branching_droop", PROPERTY_HINT_RANGE, "-2,2,0.001"), "branching_set_droop", "branching_get_droop"); ClassDB::bind_method(D_METHOD("branching_get_growth"), &ProceduralTreeMesh::branching_get_growth); ClassDB::bind_method(D_METHOD("branching_set_growth", "value"), &ProceduralTreeMesh::branching_set_growth); ADD_PROPERTY(PropertyInfo(Variant::REAL, "branching_growth", PROPERTY_HINT_RANGE, "-4,4,0.001"), "branching_set_growth", "branching_get_growth"); ClassDB::bind_method(D_METHOD("branching_get_sweep"), &ProceduralTreeMesh::branching_get_sweep); ClassDB::bind_method(D_METHOD("branching_set_sweep", "value"), &ProceduralTreeMesh::branching_set_sweep); ADD_PROPERTY(PropertyInfo(Variant::REAL, "branching_sweep", PROPERTY_HINT_RANGE, "-1,1,0.001"), "branching_set_sweep", "branching_get_sweep"); ADD_GROUP("Trunk", "trunk"); ClassDB::bind_method(D_METHOD("trunk_get_radius"), &ProceduralTreeMesh::trunk_get_radius); ClassDB::bind_method(D_METHOD("trunk_set_radius", "value"), &ProceduralTreeMesh::trunk_set_radius); ADD_PROPERTY(PropertyInfo(Variant::REAL, "trunk_radius", PROPERTY_HINT_RANGE, "0.01,0.5,0.001"), "trunk_set_radius", "trunk_get_radius"); ClassDB::bind_method(D_METHOD("trunk_get_radius_falloff"), &ProceduralTreeMesh::trunk_get_radius_falloff); ClassDB::bind_method(D_METHOD("trunk_set_radius_falloff", "value"), &ProceduralTreeMesh::trunk_set_radius_falloff); ADD_PROPERTY(PropertyInfo(Variant::REAL, "trunk_radius_falloff", PROPERTY_HINT_RANGE, "0.1,1,0.001"), "trunk_set_radius_falloff", "trunk_get_radius_falloff"); ClassDB::bind_method(D_METHOD("trunk_get_climb_rate"), &ProceduralTreeMesh::trunk_get_climb_rate); ClassDB::bind_method(D_METHOD("trunk_set_climb_rate", "value"), &ProceduralTreeMesh::trunk_set_climb_rate); ADD_PROPERTY(PropertyInfo(Variant::REAL, "trunk_climb_rate", PROPERTY_HINT_RANGE, "0.01,1,0.001"), "trunk_set_climb_rate", "trunk_get_climb_rate"); ClassDB::bind_method(D_METHOD("trunk_get_kink"), &ProceduralTreeMesh::trunk_get_kink); ClassDB::bind_method(D_METHOD("trunk_set_kink", "value"), &ProceduralTreeMesh::trunk_set_kink); ADD_PROPERTY(PropertyInfo(Variant::REAL, "trunk_kink", PROPERTY_HINT_RANGE, "-2,2,0.001"), "trunk_set_kink", "trunk_get_kink"); ClassDB::bind_method(D_METHOD("trunk_get_taper_rate"), &ProceduralTreeMesh::trunk_get_taper_rate); ClassDB::bind_method(D_METHOD("trunk_set_taper_rate", "value"), &ProceduralTreeMesh::trunk_set_taper_rate); ADD_PROPERTY(PropertyInfo(Variant::REAL, "trunk_taper_rate", PROPERTY_HINT_RANGE, "0.5,2,0.001"), "trunk_set_taper_rate", "trunk_get_taper_rate"); ClassDB::bind_method(D_METHOD("trunk_get_twists"), &ProceduralTreeMesh::trunk_get_twists); ClassDB::bind_method(D_METHOD("trunk_set_twists", "value"), &ProceduralTreeMesh::trunk_set_twists); ADD_PROPERTY(PropertyInfo(Variant::REAL, "trunk_twists", PROPERTY_HINT_RANGE, "0.01,10,0.001"), "trunk_set_twists", "trunk_get_twists"); ClassDB::bind_method(D_METHOD("trunk_get_length"), &ProceduralTreeMesh::trunk_get_length); ClassDB::bind_method(D_METHOD("trunk_set_length", "value"), &ProceduralTreeMesh::trunk_set_length); ADD_PROPERTY(PropertyInfo(Variant::REAL, "trunk_length", PROPERTY_HINT_RANGE, "0.01,50,0.001"), "trunk_set_length", "trunk_get_length"); ADD_GROUP("Mesh", ""); ClassDB::bind_method(D_METHOD("_update"), &ProceduralTreeMesh::_update); ClassDB::bind_method(D_METHOD("set_twig_material", "material"), &ProceduralTreeMesh::set_twig_material); ClassDB::bind_method(D_METHOD("get_twig_material"), &ProceduralTreeMesh::get_twig_material); ClassDB::bind_method(D_METHOD("set_trunk_material", "material"), &ProceduralTreeMesh::set_trunk_material); ClassDB::bind_method(D_METHOD("get_trunk_material"), &ProceduralTreeMesh::get_trunk_material); ClassDB::bind_method(D_METHOD("set_custom_aabb", "aabb"), &ProceduralTreeMesh::set_custom_aabb); ClassDB::bind_method(D_METHOD("get_custom_aabb"), &ProceduralTreeMesh::get_custom_aabb); ClassDB::bind_method(D_METHOD("get_enable_branch_mesh"), &ProceduralTreeMesh::get_enable_branch_mesh); ClassDB::bind_method(D_METHOD("set_enable_branch_mesh", "value"), &ProceduralTreeMesh::set_enable_branch_mesh); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enable_branch_mesh"), "set_enable_branch_mesh", "get_enable_branch_mesh"); ClassDB::bind_method(D_METHOD("get_enable_twig_mesh"), &ProceduralTreeMesh::get_enable_twig_mesh); ClassDB::bind_method(D_METHOD("set_enable_twig_mesh", "value"), &ProceduralTreeMesh::set_enable_twig_mesh); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enable_twig_mesh"), "set_enable_twig_mesh", "get_enable_twig_mesh"); ClassDB::bind_method(D_METHOD("get_flip_branch_mesh_faces"), &ProceduralTreeMesh::get_flip_branch_mesh_faces); ClassDB::bind_method(D_METHOD("set_flip_branch_mesh_faces", "value"), &ProceduralTreeMesh::set_flip_branch_mesh_faces); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_branch_mesh_faces"), "set_flip_branch_mesh_faces", "get_flip_branch_mesh_faces"); ClassDB::bind_method(D_METHOD("get_flip_twig_mesh_faces"), &ProceduralTreeMesh::get_flip_twig_mesh_faces); ClassDB::bind_method(D_METHOD("set_flip_twig_mesh_faces", "value"), &ProceduralTreeMesh::set_flip_twig_mesh_faces); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_twig_mesh_faces"), "set_flip_twig_mesh_faces", "get_flip_twig_mesh_faces"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "twig_material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_twig_material", "get_twig_material"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "trunk_material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_trunk_material", "get_trunk_material"); ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, ""), "set_custom_aabb", "get_custom_aabb"); ClassDB::bind_method(D_METHOD("get_mesh_arrays"), &ProceduralTreeMesh::get_mesh_arrays); ClassDB::bind_method(D_METHOD("to_array_mesh"), &ProceduralTreeMesh::to_array_mesh); BIND_ENUM_CONSTANT(TREE_SURFACE_TRUNK); BIND_ENUM_CONSTANT(TREE_SURFACE_TWIG); BIND_ENUM_CONSTANT(TREE_SURFACE_COUNT); }