Add shape_mode to SphereMesh to switch between Sphere, Spheroid, and Ellipsoid
This commit is contained in:
parent
33c30b9e63
commit
38e5a30589
|
@ -9,12 +9,18 @@
|
|||
<tutorials>
|
||||
</tutorials>
|
||||
<members>
|
||||
<member name="depth" type="float" setter="set_depth" getter="get_depth" default="1.0">
|
||||
Depth of sphere.
|
||||
</member>
|
||||
<member name="height" type="float" setter="set_height" getter="get_height" default="1.0">
|
||||
Full height of the sphere.
|
||||
</member>
|
||||
<member name="is_hemisphere" type="bool" setter="set_is_hemisphere" getter="get_is_hemisphere" default="false">
|
||||
If [code]true[/code], a hemisphere is created rather than a full sphere.
|
||||
[b]Note:[/b] To get a regular hemisphere, the height and radius of the sphere must be equal.
|
||||
[b]Note:[/b] To get a regular hemisphere, one of the following must be true:
|
||||
1. Shape mode is set to SPHERE.
|
||||
2. If the shape mode is SPHEROID, the height must be equal to the radius.
|
||||
3. If shape mode is ELLIPSOID, the width and depth must be equal, and the height must be half of the width or depth.
|
||||
</member>
|
||||
<member name="radial_segments" type="int" setter="set_radial_segments" getter="get_radial_segments" default="64">
|
||||
Number of radial segments on the sphere.
|
||||
|
@ -25,5 +31,22 @@
|
|||
<member name="rings" type="int" setter="set_rings" getter="get_rings" default="32">
|
||||
Number of segments along the height of the sphere.
|
||||
</member>
|
||||
<member name="shape" type="int" setter="set_shape" getter="get_shape" enum="SphereMesh.Shape" default="1">
|
||||
Sets the shape mode to one of [enum SphereMesh.Shape].
|
||||
</member>
|
||||
<member name="width" type="float" setter="set_width" getter="get_width" default="1.0">
|
||||
Width of sphere.
|
||||
</member>
|
||||
</members>
|
||||
<constants>
|
||||
<constant name="SPHERE" value="0" enum="Shape">
|
||||
A regular sphere controlled only by radius.
|
||||
</constant>
|
||||
<constant name="SPHEROID" value="1" enum="Shape">
|
||||
A sphere with adjustable height and radius.
|
||||
</constant>
|
||||
<constant name="ELLIPSOID" value="2" enum="Shape">
|
||||
A sphere with adjustable height, width, and depth.
|
||||
</constant>
|
||||
</constants>
|
||||
</class>
|
||||
|
|
|
@ -370,7 +370,7 @@ void NavMeshGenerator3D::generator_parse_staticbody3d_node(const Ref<NavigationM
|
|||
if (sphere) {
|
||||
Array arr;
|
||||
arr.resize(RS::ARRAY_MAX);
|
||||
SphereMesh::create_mesh_array(arr, sphere->get_radius(), sphere->get_radius() * 2.0);
|
||||
SphereMesh::create_mesh_array(arr, SphereMesh::SPHERE, sphere->get_radius(), sphere->get_radius() * 2.0, 0, 0);
|
||||
p_source_geometry_data->add_mesh_array(arr, transform);
|
||||
}
|
||||
|
||||
|
@ -493,7 +493,7 @@ void NavMeshGenerator3D::generator_parse_gridmap_node(const Ref<NavigationMesh>
|
|||
real_t radius = data;
|
||||
Array arr;
|
||||
arr.resize(RS::ARRAY_MAX);
|
||||
SphereMesh::create_mesh_array(arr, radius, radius * 2.0);
|
||||
SphereMesh::create_mesh_array(arr, SphereMesh::SPHERE, radius, radius * 2.0, 0, 0);
|
||||
p_source_geometry_data->add_mesh_array(arr, shapes[i]);
|
||||
} break;
|
||||
case PhysicsServer3D::SHAPE_BOX: {
|
||||
|
|
|
@ -1765,9 +1765,28 @@ void SphereMesh::_update_lightmap_size() {
|
|||
float texel_size = get_lightmap_texel_size();
|
||||
float padding = get_uv2_padding();
|
||||
|
||||
float _width = radius * Math_TAU;
|
||||
// Determine dimensions based on shape mode
|
||||
float _width, _height;
|
||||
|
||||
switch (shape) {
|
||||
case SPHERE:
|
||||
_width = radius * Math_TAU;
|
||||
_height = (is_hemisphere ? 1.0f : 0.5f) * radius * Math_PI;
|
||||
break;
|
||||
case SPHEROID:
|
||||
_width = height * Math_TAU;
|
||||
_height = (is_hemisphere ? height : height * 0.5f) * Math_PI;
|
||||
break;
|
||||
case ELLIPSOID:
|
||||
_width = width * Math_TAU;
|
||||
_height = (is_hemisphere ? height : height * 0.5f) * Math_PI;
|
||||
break;
|
||||
default:
|
||||
_width = _height = 1.0f;
|
||||
break;
|
||||
}
|
||||
|
||||
_lightmap_size_hint.x = MAX(1.0, (_width / texel_size) + padding);
|
||||
float _height = (is_hemisphere ? 1.0 : 0.5) * height * Math_PI; // note, with hemisphere height is our radius, while with a full sphere it is the diameter..
|
||||
_lightmap_size_hint.y = MAX(1.0, (_height / texel_size) + padding);
|
||||
|
||||
set_lightmap_size_hint(_lightmap_size_hint);
|
||||
|
@ -1779,21 +1798,41 @@ void SphereMesh::_create_mesh_array(Array &p_arr) const {
|
|||
float texel_size = get_lightmap_texel_size();
|
||||
float _uv2_padding = get_uv2_padding() * texel_size;
|
||||
|
||||
create_mesh_array(p_arr, radius, height, radial_segments, rings, is_hemisphere, _add_uv2, _uv2_padding);
|
||||
create_mesh_array(p_arr, shape, radius, height, width, depth, radial_segments, rings, is_hemisphere, _add_uv2, _uv2_padding);
|
||||
}
|
||||
|
||||
void SphereMesh::create_mesh_array(Array &p_arr, float radius, float height, int radial_segments, int rings, bool is_hemisphere, bool p_add_uv2, const float p_uv2_padding) {
|
||||
void SphereMesh::create_mesh_array(Array &p_arr, SphereMesh::Shape shape, float radius, float height, float width, float depth, int radial_segments, int rings, bool is_hemisphere, bool p_add_uv2, const float p_uv2_padding) {
|
||||
int i, j, prevrow, thisrow, point;
|
||||
float x, y, z;
|
||||
|
||||
float scale = height * (is_hemisphere ? 1.0 : 0.5);
|
||||
// Determine scales based on shape mode.
|
||||
float scale_x, scale_y, scale_z;
|
||||
|
||||
switch (shape) {
|
||||
case SPHERE:
|
||||
scale_x = scale_y = scale_z = radius;
|
||||
break;
|
||||
case SPHEROID:
|
||||
scale_x = radius;
|
||||
scale_y = is_hemisphere ? height : height * 0.5;
|
||||
scale_z = radius;
|
||||
break;
|
||||
case ELLIPSOID:
|
||||
scale_x = width * 0.5;
|
||||
scale_y = is_hemisphere ? height : height * 0.5;
|
||||
scale_z = depth * 0.5;
|
||||
break;
|
||||
default:
|
||||
scale_x = scale_y = scale_z = 1.0;
|
||||
break;
|
||||
}
|
||||
|
||||
// Only used if we calculate UV2
|
||||
float circumference = radius * Math_TAU;
|
||||
float circumference = scale_x * Math_TAU;
|
||||
float horizontal_length = circumference + p_uv2_padding;
|
||||
float center_h = 0.5 * circumference / horizontal_length;
|
||||
|
||||
float height_v = scale * Math_PI / ((scale * Math_PI) + p_uv2_padding);
|
||||
float height_v = scale_y * Math_PI / ((scale_y * Math_PI) + p_uv2_padding);
|
||||
|
||||
// set our bounding box
|
||||
|
||||
|
@ -1819,7 +1858,7 @@ void SphereMesh::create_mesh_array(Array &p_arr, float radius, float height, int
|
|||
|
||||
v /= (rings + 1);
|
||||
w = sin(Math_PI * v);
|
||||
y = scale * cos(Math_PI * v);
|
||||
y = scale_y * cos(Math_PI * v);
|
||||
|
||||
for (i = 0; i <= radial_segments; i++) {
|
||||
float u = i;
|
||||
|
@ -1829,12 +1868,18 @@ void SphereMesh::create_mesh_array(Array &p_arr, float radius, float height, int
|
|||
z = cos(u * Math_TAU);
|
||||
|
||||
if (is_hemisphere && y < 0.0) {
|
||||
points.push_back(Vector3(x * radius * w, 0.0, z * radius * w));
|
||||
points.push_back(Vector3(x * scale_x * w, 0.0, z * scale_z * w));
|
||||
normals.push_back(Vector3(0.0, -1.0, 0.0));
|
||||
} else {
|
||||
Vector3 p = Vector3(x * radius * w, y, z * radius * w);
|
||||
Vector3 p = Vector3(x * scale_x * w, y, z * scale_z * w);
|
||||
points.push_back(p);
|
||||
Vector3 normal = Vector3(x * w * scale, radius * (y / scale), z * w * scale);
|
||||
|
||||
Vector3 normal;
|
||||
if (shape == SPHERE || shape == SPHEROID) {
|
||||
normal = Vector3(x * w * scale_x, radius * (y / scale_y), z * w * scale_z);
|
||||
} else { // Ellipsoid.
|
||||
normal = Vector3(x * w * scale_x, y / scale_y, z * w * scale_z);
|
||||
}
|
||||
normals.push_back(normal.normalized());
|
||||
}
|
||||
ADD_TANGENT(z, 0.0, -x, 1.0)
|
||||
|
@ -1871,10 +1916,17 @@ void SphereMesh::create_mesh_array(Array &p_arr, float radius, float height, int
|
|||
}
|
||||
|
||||
void SphereMesh::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_shape", "shape"), &SphereMesh::set_shape);
|
||||
ClassDB::bind_method(D_METHOD("get_shape"), &SphereMesh::get_shape);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &SphereMesh::set_radius);
|
||||
ClassDB::bind_method(D_METHOD("get_radius"), &SphereMesh::get_radius);
|
||||
ClassDB::bind_method(D_METHOD("set_height", "height"), &SphereMesh::set_height);
|
||||
ClassDB::bind_method(D_METHOD("get_height"), &SphereMesh::get_height);
|
||||
ClassDB::bind_method(D_METHOD("set_width", "width"), &SphereMesh::set_width);
|
||||
ClassDB::bind_method(D_METHOD("get_width"), &SphereMesh::get_width);
|
||||
ClassDB::bind_method(D_METHOD("set_depth", "depth"), &SphereMesh::set_depth);
|
||||
ClassDB::bind_method(D_METHOD("get_depth"), &SphereMesh::get_depth);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_radial_segments", "radial_segments"), &SphereMesh::set_radial_segments);
|
||||
ClassDB::bind_method(D_METHOD("get_radial_segments"), &SphereMesh::get_radial_segments);
|
||||
|
@ -1884,11 +1936,46 @@ void SphereMesh::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_is_hemisphere", "is_hemisphere"), &SphereMesh::set_is_hemisphere);
|
||||
ClassDB::bind_method(D_METHOD("get_is_hemisphere"), &SphereMesh::get_is_hemisphere);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "shape", PROPERTY_HINT_ENUM, "Sphere,Spheroid,Ellipsoid"), "set_shape", "get_shape");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_radius", "get_radius");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_height", "get_height");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_width", "get_width");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_depth", "get_depth");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_radial_segments", "get_radial_segments");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_rings", "get_rings");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "is_hemisphere"), "set_is_hemisphere", "get_is_hemisphere");
|
||||
|
||||
BIND_ENUM_CONSTANT(SPHERE);
|
||||
BIND_ENUM_CONSTANT(SPHEROID);
|
||||
BIND_ENUM_CONSTANT(ELLIPSOID);
|
||||
}
|
||||
|
||||
void SphereMesh::_validate_property(PropertyInfo &p_property) const {
|
||||
if (shape == SPHERE) {
|
||||
if (p_property.name == "height") {
|
||||
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
|
||||
}
|
||||
}
|
||||
if (shape != ELLIPSOID) {
|
||||
if (p_property.name == "width" || p_property.name == "depth") {
|
||||
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
|
||||
}
|
||||
} else {
|
||||
if (p_property.name == "radius") {
|
||||
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SphereMesh::set_shape(SphereMesh::Shape p_shape_mode) {
|
||||
shape = p_shape_mode;
|
||||
_update_lightmap_size();
|
||||
request_update();
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
SphereMesh::Shape SphereMesh::get_shape() const {
|
||||
return shape;
|
||||
}
|
||||
|
||||
void SphereMesh::set_radius(const float p_radius) {
|
||||
|
@ -1910,6 +1997,24 @@ void SphereMesh::set_height(const float p_height) {
|
|||
float SphereMesh::get_height() const {
|
||||
return height;
|
||||
}
|
||||
void SphereMesh::set_width(const float p_width) {
|
||||
width = p_width;
|
||||
_update_lightmap_size();
|
||||
request_update();
|
||||
}
|
||||
|
||||
float SphereMesh::get_width() const {
|
||||
return width;
|
||||
}
|
||||
void SphereMesh::set_depth(const float p_depth) {
|
||||
depth = p_depth;
|
||||
_update_lightmap_size();
|
||||
request_update();
|
||||
}
|
||||
|
||||
float SphereMesh::get_depth() const {
|
||||
return depth;
|
||||
}
|
||||
|
||||
void SphereMesh::set_radial_segments(const int p_radial_segments) {
|
||||
radial_segments = p_radial_segments > 4 ? p_radial_segments : 4;
|
||||
|
|
|
@ -341,27 +341,47 @@ public:
|
|||
class SphereMesh : public PrimitiveMesh {
|
||||
GDCLASS(SphereMesh, PrimitiveMesh);
|
||||
|
||||
public:
|
||||
enum Shape {
|
||||
SPHERE = 0,
|
||||
SPHEROID = 1,
|
||||
ELLIPSOID = 2,
|
||||
};
|
||||
|
||||
private:
|
||||
SphereMesh::Shape shape = SPHEROID;
|
||||
float radius = 0.5;
|
||||
float height = 1.0;
|
||||
float width = 1.0;
|
||||
float depth = 1.0;
|
||||
int radial_segments = 64;
|
||||
int rings = 32;
|
||||
bool is_hemisphere = false;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
void _validate_property(PropertyInfo &p_property) const;
|
||||
|
||||
virtual void _create_mesh_array(Array &p_arr) const override;
|
||||
|
||||
virtual void _update_lightmap_size() override;
|
||||
|
||||
public:
|
||||
static void create_mesh_array(Array &p_arr, float radius, float height, int radial_segments = 64, int rings = 32, bool is_hemisphere = false, bool p_add_uv2 = false, const float p_uv2_padding = 1.0);
|
||||
static void create_mesh_array(Array &p_arr, SphereMesh::Shape shape, float radius, float height, float width, float depth, int radial_segments = 64, int rings = 32, bool is_hemisphere = false, bool p_add_uv2 = false, const float p_uv2_padding = 1.0);
|
||||
|
||||
void set_shape(SphereMesh::Shape p_shape_mode);
|
||||
SphereMesh::Shape get_shape() const;
|
||||
|
||||
void set_radius(const float p_radius);
|
||||
float get_radius() const;
|
||||
|
||||
void set_height(const float p_height);
|
||||
float get_height() const;
|
||||
void set_width(float p_width);
|
||||
float get_width() const;
|
||||
void set_depth(float p_depth);
|
||||
float get_depth() const;
|
||||
|
||||
void set_radial_segments(const int p_radial_segments);
|
||||
int get_radial_segments() const;
|
||||
|
@ -374,6 +394,7 @@ public:
|
|||
|
||||
SphereMesh();
|
||||
};
|
||||
VARIANT_ENUM_CAST(SphereMesh::Shape);
|
||||
|
||||
/**
|
||||
Big donut
|
||||
|
|
|
@ -546,7 +546,7 @@ TEST_CASE("[SceneTree][Primitive][Sphere] Sphere Primitive") {
|
|||
real_t radius = 1.1f;
|
||||
int radial_segments = 8;
|
||||
int rings = 5;
|
||||
SphereMesh::create_mesh_array(data, radius, 2 * radius, radial_segments, rings);
|
||||
SphereMesh::create_mesh_array(data, SphereMesh::SPHEROID, radius, 2 * radius, 0, 0, radial_segments, rings);
|
||||
Vector<Vector3> points = data[RS::ARRAY_VERTEX];
|
||||
Vector<Vector3> normals = data[RS::ARRAY_NORMAL];
|
||||
|
||||
|
|
Loading…
Reference in New Issue