mirror of
https://github.com/godotengine/godot.git
synced 2024-11-22 04:06:14 +00:00
Add HeightMapShape3D update with Image data
Adds HeightMapShape3D update with Image data.
This commit is contained in:
parent
29b3d9e9e5
commit
924a5c6573
@ -6,6 +6,19 @@
|
|||||||
<description>
|
<description>
|
||||||
A 3D heightmap shape, intended for use in physics. Usually used to provide a shape for a [CollisionShape3D]. This is useful for terrain, but it is limited as overhangs (such as caves) cannot be stored. Holes in a [HeightMapShape3D] are created by assigning very low values to points in the desired area.
|
A 3D heightmap shape, intended for use in physics. Usually used to provide a shape for a [CollisionShape3D]. This is useful for terrain, but it is limited as overhangs (such as caves) cannot be stored. Holes in a [HeightMapShape3D] are created by assigning very low values to points in the desired area.
|
||||||
[b]Performance:[/b] [HeightMapShape3D] is faster to check collisions against than [ConcavePolygonShape3D], but it is significantly slower than primitive shapes like [BoxShape3D].
|
[b]Performance:[/b] [HeightMapShape3D] is faster to check collisions against than [ConcavePolygonShape3D], but it is significantly slower than primitive shapes like [BoxShape3D].
|
||||||
|
A heightmap collision shape can also be build by using an [Image] reference:
|
||||||
|
[codeblocks]
|
||||||
|
[gdscript]
|
||||||
|
var heightmap_texture: Texture = ResourceLoader.load("res://heightmap_image.exr")
|
||||||
|
var heightmap_image: Image = heightmap_texture.get_image()
|
||||||
|
heightmap_image.convert(Image.FORMAT_RF)
|
||||||
|
|
||||||
|
var height_min: float = 0.0
|
||||||
|
var height_max: float = 10.0
|
||||||
|
|
||||||
|
update_map_data_from_image(heightmap_image, height_min, height_max)
|
||||||
|
[/gdscript]
|
||||||
|
[/codeblocks]
|
||||||
</description>
|
</description>
|
||||||
<tutorials>
|
<tutorials>
|
||||||
</tutorials>
|
</tutorials>
|
||||||
@ -22,6 +35,17 @@
|
|||||||
Returns the smallest height value found in [member map_data]. Recalculates only when [member map_data] changes.
|
Returns the smallest height value found in [member map_data]. Recalculates only when [member map_data] changes.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
<method name="update_map_data_from_image">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="image" type="Image" />
|
||||||
|
<param index="1" name="height_min" type="float" />
|
||||||
|
<param index="2" name="height_max" type="float" />
|
||||||
|
<description>
|
||||||
|
Updates [member map_data] with data read from an [Image] reference. Automatically resizes heightmap [member map_width] and [member map_depth] to fit the full image width and height.
|
||||||
|
The image needs to be in either [constant Image.FORMAT_RF] (32 bit), [constant Image.FORMAT_RH] (16 bit), or [constant Image.FORMAT_R8] (8 bit).
|
||||||
|
Each image pixel is read in as a float on the range from [code]0.0[/code] (black pixel) to [code]1.0[/code] (white pixel). This range value gets remapped to [param height_min] and [param height_max] to form the final height value.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
</methods>
|
</methods>
|
||||||
<members>
|
<members>
|
||||||
<member name="map_data" type="PackedFloat32Array" setter="set_map_data" getter="get_map_data" default="PackedFloat32Array(0, 0, 0, 0)">
|
<member name="map_data" type="PackedFloat32Array" setter="set_map_data" getter="get_map_data" default="PackedFloat32Array(0, 0, 0, 0)">
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
|
|
||||||
#include "height_map_shape_3d.h"
|
#include "height_map_shape_3d.h"
|
||||||
|
|
||||||
|
#include "core/io/image.h"
|
||||||
#include "servers/physics_server_3d.h"
|
#include "servers/physics_server_3d.h"
|
||||||
|
|
||||||
Vector<Vector3> HeightMapShape3D::get_debug_mesh_lines() const {
|
Vector<Vector3> HeightMapShape3D::get_debug_mesh_lines() const {
|
||||||
@ -187,6 +188,104 @@ real_t HeightMapShape3D::get_max_height() const {
|
|||||||
return max_height;
|
return max_height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HeightMapShape3D::update_map_data_from_image(const Ref<Image> &p_image, real_t p_height_min, real_t p_height_max) {
|
||||||
|
ERR_FAIL_COND_MSG(p_image.is_null(), "Heightmap update image requires a valid Image reference.");
|
||||||
|
ERR_FAIL_COND_MSG(p_image->get_format() != Image::FORMAT_RF && p_image->get_format() != Image::FORMAT_RH && p_image->get_format() != Image::FORMAT_R8, "Heightmap update image requires Image in format FORMAT_RF (32 bit), FORMAT_RH (16 bit), or FORMAT_R8 (8 bit).");
|
||||||
|
ERR_FAIL_COND_MSG(p_image->get_width() < 2, "Heightmap update image requires a minimum Image width of 2.");
|
||||||
|
ERR_FAIL_COND_MSG(p_image->get_height() < 2, "Heightmap update image requires a minimum Image height of 2.");
|
||||||
|
ERR_FAIL_COND_MSG(p_height_min > p_height_max, "Heightmap update image requires height_max to be greater than height_min.");
|
||||||
|
|
||||||
|
map_width = p_image->get_width();
|
||||||
|
map_depth = p_image->get_height();
|
||||||
|
map_data.resize(map_width * map_depth);
|
||||||
|
|
||||||
|
real_t new_min_height = FLT_MAX;
|
||||||
|
real_t new_max_height = -FLT_MAX;
|
||||||
|
|
||||||
|
float remap_height_min = float(p_height_min);
|
||||||
|
float remap_height_max = float(p_height_max);
|
||||||
|
|
||||||
|
real_t *map_data_ptrw = map_data.ptrw();
|
||||||
|
|
||||||
|
switch (p_image->get_format()) {
|
||||||
|
case Image::FORMAT_RF: {
|
||||||
|
const float *image_data_ptr = (float *)p_image->get_data().ptr();
|
||||||
|
|
||||||
|
for (int i = 0; i < map_data.size(); i++) {
|
||||||
|
float pixel_value = image_data_ptr[i];
|
||||||
|
|
||||||
|
DEV_ASSERT(pixel_value >= 0.0 && pixel_value <= 1.0);
|
||||||
|
|
||||||
|
real_t height_value = Math::remap(pixel_value, 0.0f, 1.0f, remap_height_min, remap_height_max);
|
||||||
|
|
||||||
|
if (height_value < new_min_height) {
|
||||||
|
new_min_height = height_value;
|
||||||
|
}
|
||||||
|
if (height_value > new_max_height) {
|
||||||
|
new_max_height = height_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
map_data_ptrw[i] = height_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case Image::FORMAT_RH: {
|
||||||
|
const uint16_t *image_data_ptr = (uint16_t *)p_image->get_data().ptr();
|
||||||
|
|
||||||
|
for (int i = 0; i < map_data.size(); i++) {
|
||||||
|
float pixel_value = Math::half_to_float(image_data_ptr[i]);
|
||||||
|
|
||||||
|
DEV_ASSERT(pixel_value >= 0.0 && pixel_value <= 1.0);
|
||||||
|
|
||||||
|
real_t height_value = Math::remap(pixel_value, 0.0f, 1.0f, remap_height_min, remap_height_max);
|
||||||
|
|
||||||
|
if (height_value < new_min_height) {
|
||||||
|
new_min_height = height_value;
|
||||||
|
}
|
||||||
|
if (height_value > new_max_height) {
|
||||||
|
new_max_height = height_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
map_data_ptrw[i] = height_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case Image::FORMAT_R8: {
|
||||||
|
const uint8_t *image_data_ptr = (uint8_t *)p_image->get_data().ptr();
|
||||||
|
|
||||||
|
for (int i = 0; i < map_data.size(); i++) {
|
||||||
|
float pixel_value = float(image_data_ptr[i] / 255.0);
|
||||||
|
|
||||||
|
DEV_ASSERT(pixel_value >= 0.0 && pixel_value <= 1.0);
|
||||||
|
|
||||||
|
real_t height_value = Math::remap(pixel_value, 0.0f, 1.0f, remap_height_min, remap_height_max);
|
||||||
|
|
||||||
|
if (height_value < new_min_height) {
|
||||||
|
new_min_height = height_value;
|
||||||
|
}
|
||||||
|
if (height_value > new_max_height) {
|
||||||
|
new_max_height = height_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
map_data_ptrw[i] = height_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
min_height = new_min_height;
|
||||||
|
max_height = new_max_height;
|
||||||
|
|
||||||
|
_update_shape();
|
||||||
|
emit_changed();
|
||||||
|
}
|
||||||
|
|
||||||
void HeightMapShape3D::_bind_methods() {
|
void HeightMapShape3D::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("set_map_width", "width"), &HeightMapShape3D::set_map_width);
|
ClassDB::bind_method(D_METHOD("set_map_width", "width"), &HeightMapShape3D::set_map_width);
|
||||||
ClassDB::bind_method(D_METHOD("get_map_width"), &HeightMapShape3D::get_map_width);
|
ClassDB::bind_method(D_METHOD("get_map_width"), &HeightMapShape3D::get_map_width);
|
||||||
@ -197,6 +296,8 @@ void HeightMapShape3D::_bind_methods() {
|
|||||||
ClassDB::bind_method(D_METHOD("get_min_height"), &HeightMapShape3D::get_min_height);
|
ClassDB::bind_method(D_METHOD("get_min_height"), &HeightMapShape3D::get_min_height);
|
||||||
ClassDB::bind_method(D_METHOD("get_max_height"), &HeightMapShape3D::get_max_height);
|
ClassDB::bind_method(D_METHOD("get_max_height"), &HeightMapShape3D::get_max_height);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("update_map_data_from_image", "image", "height_min", "height_max"), &HeightMapShape3D::update_map_data_from_image);
|
||||||
|
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "map_width", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), "set_map_width", "get_map_width");
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "map_width", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), "set_map_width", "get_map_width");
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "map_depth", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), "set_map_depth", "get_map_depth");
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "map_depth", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), "set_map_depth", "get_map_depth");
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "map_data"), "set_map_data", "get_map_data");
|
ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "map_data"), "set_map_data", "get_map_data");
|
||||||
|
@ -33,6 +33,8 @@
|
|||||||
|
|
||||||
#include "scene/resources/3d/shape_3d.h"
|
#include "scene/resources/3d/shape_3d.h"
|
||||||
|
|
||||||
|
class Image;
|
||||||
|
|
||||||
class HeightMapShape3D : public Shape3D {
|
class HeightMapShape3D : public Shape3D {
|
||||||
GDCLASS(HeightMapShape3D, Shape3D);
|
GDCLASS(HeightMapShape3D, Shape3D);
|
||||||
|
|
||||||
@ -57,6 +59,8 @@ public:
|
|||||||
real_t get_min_height() const;
|
real_t get_min_height() const;
|
||||||
real_t get_max_height() const;
|
real_t get_max_height() const;
|
||||||
|
|
||||||
|
void update_map_data_from_image(const Ref<Image> &p_image, real_t p_height_min, real_t p_height_max);
|
||||||
|
|
||||||
virtual Vector<Vector3> get_debug_mesh_lines() const override;
|
virtual Vector<Vector3> get_debug_mesh_lines() const override;
|
||||||
virtual real_t get_enclosing_radius() const override;
|
virtual real_t get_enclosing_radius() const override;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user