Added option to tweak how VoxelStreamImage samples the heightmap
This commit is contained in:
parent
462e453cf4
commit
3ead96f06e
@ -43,7 +43,7 @@ protected:
|
||||
|
||||
Stats _stats;
|
||||
|
||||
VOXEL_PROFILER_DECLARE;
|
||||
VOXEL_PROFILER_DECLARE
|
||||
};
|
||||
|
||||
#endif // VOXEL_STREAM_H
|
||||
|
@ -1,24 +1,5 @@
|
||||
#include "voxel_stream_image.h"
|
||||
|
||||
VoxelStreamImage::VoxelStreamImage() :
|
||||
_channel(VoxelBuffer::CHANNEL_TYPE) {
|
||||
}
|
||||
|
||||
void VoxelStreamImage::set_image(Ref<Image> im) {
|
||||
_image = im;
|
||||
}
|
||||
|
||||
Ref<Image> VoxelStreamImage::get_image() const {
|
||||
return _image;
|
||||
}
|
||||
|
||||
void VoxelStreamImage::set_channel(VoxelBuffer::ChannelId channel) {
|
||||
_channel = channel;
|
||||
}
|
||||
|
||||
VoxelBuffer::ChannelId VoxelStreamImage::get_channel() const {
|
||||
return _channel;
|
||||
}
|
||||
#include "../util/fixed_array.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@ -39,8 +20,78 @@ inline float get_height_blurred(Image &im, int x, int y) {
|
||||
return h * 0.2f;
|
||||
}
|
||||
|
||||
float get_constrained_segment_sdf(float p_yp, float p_ya, float p_yb, float p_xb) {
|
||||
|
||||
// P
|
||||
// . B
|
||||
// . /
|
||||
// . / y
|
||||
// ./ |
|
||||
// A o--x
|
||||
|
||||
float s = p_yp >= p_ya ? 1 : -1;
|
||||
|
||||
if (Math::absf(p_yp - p_ya) > 1.f && Math::absf(p_yp - p_yb) > 1.f) {
|
||||
return s;
|
||||
}
|
||||
|
||||
Vector2 p(0, p_yp);
|
||||
Vector2 a(0, p_ya);
|
||||
Vector2 b(p_xb, p_yb);
|
||||
Vector2 closest_point;
|
||||
|
||||
// TODO Optimize given the particular case we are in
|
||||
Vector2 n = b - a;
|
||||
real_t l2 = n.length_squared();
|
||||
if (l2 < 1e-20) {
|
||||
closest_point = a; // Both points are the same, just give any.
|
||||
} else {
|
||||
real_t d = n.dot(p - a) / l2;
|
||||
if (d <= 0.0) {
|
||||
closest_point = a; // Before first point.
|
||||
} else if (d >= 1.0) {
|
||||
closest_point = b; // After first point.
|
||||
} else {
|
||||
closest_point = a + n * d; // Inside.
|
||||
}
|
||||
}
|
||||
|
||||
return s * closest_point.distance_to(p);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const char *VoxelStreamImage::SDF_MODE_HINT_STRING = "Vertical,VerticalAverage,Segment";
|
||||
|
||||
VoxelStreamImage::VoxelStreamImage() {
|
||||
}
|
||||
|
||||
void VoxelStreamImage::set_image(Ref<Image> im) {
|
||||
_image = im;
|
||||
}
|
||||
|
||||
Ref<Image> VoxelStreamImage::get_image() const {
|
||||
return _image;
|
||||
}
|
||||
|
||||
void VoxelStreamImage::set_channel(VoxelBuffer::ChannelId channel) {
|
||||
ERR_FAIL_INDEX(channel, VoxelBuffer::MAX_CHANNELS);
|
||||
_channel = channel;
|
||||
}
|
||||
|
||||
VoxelBuffer::ChannelId VoxelStreamImage::get_channel() const {
|
||||
return _channel;
|
||||
}
|
||||
|
||||
void VoxelStreamImage::set_sdf_mode(SdfMode mode) {
|
||||
ERR_FAIL_INDEX(mode, SDF_MODE_COUNT);
|
||||
_sdf_mode = mode;
|
||||
}
|
||||
|
||||
VoxelStreamImage::SdfMode VoxelStreamImage::get_sdf_mode() const {
|
||||
return _sdf_mode;
|
||||
}
|
||||
|
||||
void VoxelStreamImage::emerge_block(Ref<VoxelBuffer> p_out_buffer, Vector3i origin_in_voxels, int lod) {
|
||||
|
||||
int ox = origin_in_voxels.x;
|
||||
@ -52,9 +103,6 @@ void VoxelStreamImage::emerge_block(Ref<VoxelBuffer> p_out_buffer, Vector3i orig
|
||||
|
||||
image.lock();
|
||||
|
||||
int x = 0;
|
||||
int z = 0;
|
||||
|
||||
int bs = out_buffer.get_size().x;
|
||||
|
||||
int dirt = 1;
|
||||
@ -62,22 +110,14 @@ void VoxelStreamImage::emerge_block(Ref<VoxelBuffer> p_out_buffer, Vector3i orig
|
||||
float hbase = 50.0;
|
||||
float hspan = 200.0;
|
||||
|
||||
while (z < bs) {
|
||||
while (x < bs) {
|
||||
for (int z = 0; z < out_buffer.get_size().z; ++z) {
|
||||
for (int x = 0; x < out_buffer.get_size().x; ++x) {
|
||||
|
||||
int lx = x << lod;
|
||||
int lz = z << lod;
|
||||
|
||||
if (_channel == VoxelBuffer::CHANNEL_SDF) {
|
||||
if (_channel == VoxelBuffer::CHANNEL_TYPE) {
|
||||
|
||||
float h = get_height_blurred(image, ox + lx, oz + lz) * hspan - hbase;
|
||||
|
||||
for (int y = 0; y < bs; ++y) {
|
||||
int ly = y << lod;
|
||||
out_buffer.set_voxel_f((oy + ly) - h, x, y, z, _channel);
|
||||
}
|
||||
|
||||
} else {
|
||||
float h = get_height_repeat(image, ox + lx, oz + lz) * hspan - hbase;
|
||||
h -= oy;
|
||||
int ih = int(h);
|
||||
@ -87,13 +127,107 @@ void VoxelStreamImage::emerge_block(Ref<VoxelBuffer> p_out_buffer, Vector3i orig
|
||||
}
|
||||
out_buffer.fill_area(dirt, Vector3(x, 0, z), Vector3(x + 1, ih, z + 1), _channel);
|
||||
}
|
||||
}
|
||||
|
||||
x += 1;
|
||||
}
|
||||
z += 1;
|
||||
x = 0;
|
||||
}
|
||||
} else if (_channel == VoxelBuffer::CHANNEL_SDF) {
|
||||
|
||||
switch (_sdf_mode) {
|
||||
case SDF_VERTICAL:
|
||||
case SDF_VERTICAL_AVERAGE: {
|
||||
float h;
|
||||
if (_sdf_mode == SDF_VERTICAL) {
|
||||
// Only get the height at XZ
|
||||
h = get_height_repeat(image, ox + lx, oz + lz) * hspan - hbase;
|
||||
} else {
|
||||
// Get an average of the heights around XZ
|
||||
h = get_height_blurred(image, ox + lx, oz + lz) * hspan - hbase;
|
||||
}
|
||||
for (int y = 0; y < out_buffer.get_size().y; ++y) {
|
||||
|
||||
int ly = y << lod;
|
||||
float d = (oy + ly) - h;
|
||||
out_buffer.set_voxel_f(d, x, y, z, _channel);
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDF_SEGMENT: {
|
||||
// Calculate distance to 8 segments going from the point at XZ to its neighbor points,
|
||||
// and pick the smallest distance.
|
||||
|
||||
int gx = ox + lx;
|
||||
int gz = oz + lz;
|
||||
|
||||
float h0 = get_height_repeat(image, gx - 1, gz - 1) * hspan - hbase;
|
||||
float h1 = get_height_repeat(image, gx, gz - 1) * hspan - hbase;
|
||||
float h2 = get_height_repeat(image, gx + 1, gz - 1) * hspan - hbase;
|
||||
|
||||
float h3 = get_height_repeat(image, gx - 1, gz) * hspan - hbase;
|
||||
float h4 = get_height_repeat(image, gx, gz) * hspan - hbase;
|
||||
float h5 = get_height_repeat(image, gx + 1, gz) * hspan - hbase;
|
||||
|
||||
float h6 = get_height_repeat(image, gx - 1, gz + 1) * hspan - hbase;
|
||||
float h7 = get_height_repeat(image, gx, gz + 1) * hspan - hbase;
|
||||
float h8 = get_height_repeat(image, gx + 1, gz + 1) * hspan - hbase;
|
||||
|
||||
const float sqrt2 = 1.414213562373095;
|
||||
|
||||
for (int y = 0; y < out_buffer.get_size().y; ++y) {
|
||||
|
||||
int ly = y << lod;
|
||||
int gy = oy + ly;
|
||||
|
||||
float sdf0 = get_constrained_segment_sdf(gy, h4, h0, sqrt2);
|
||||
float sdf1 = get_constrained_segment_sdf(gy, h4, h1, 1);
|
||||
float sdf2 = get_constrained_segment_sdf(gy, h4, h2, sqrt2);
|
||||
|
||||
float sdf3 = get_constrained_segment_sdf(gy, h4, h3, 1);
|
||||
float sdf4 = gy - h4;
|
||||
float sdf5 = get_constrained_segment_sdf(gy, h4, h5, 1);
|
||||
|
||||
float sdf6 = get_constrained_segment_sdf(gy, h4, h6, sqrt2);
|
||||
float sdf7 = get_constrained_segment_sdf(gy, h4, h7, 1);
|
||||
float sdf8 = get_constrained_segment_sdf(gy, h4, h8, sqrt2);
|
||||
|
||||
float sdf = sdf4;
|
||||
|
||||
if (Math::absf(sdf0) < Math::absf(sdf)) {
|
||||
sdf = sdf0;
|
||||
}
|
||||
if (Math::absf(sdf1) < Math::absf(sdf)) {
|
||||
sdf = sdf1;
|
||||
}
|
||||
if (Math::absf(sdf2) < Math::absf(sdf)) {
|
||||
sdf = sdf2;
|
||||
}
|
||||
if (Math::absf(sdf3) < Math::absf(sdf)) {
|
||||
sdf = sdf3;
|
||||
}
|
||||
if (Math::absf(sdf5) < Math::absf(sdf)) {
|
||||
sdf = sdf5;
|
||||
}
|
||||
if (Math::absf(sdf6) < Math::absf(sdf)) {
|
||||
sdf = sdf6;
|
||||
}
|
||||
if (Math::absf(sdf7) < Math::absf(sdf)) {
|
||||
sdf = sdf7;
|
||||
}
|
||||
if (Math::absf(sdf8) < Math::absf(sdf)) {
|
||||
sdf = sdf8;
|
||||
}
|
||||
|
||||
out_buffer.set_voxel_f(sdf, x, y, z, _channel);
|
||||
}
|
||||
} break;
|
||||
|
||||
default:
|
||||
CRASH_NOW();
|
||||
break;
|
||||
|
||||
} // sdf mode
|
||||
|
||||
} // channel
|
||||
|
||||
} // for x
|
||||
} // for z
|
||||
|
||||
image.unlock();
|
||||
|
||||
@ -108,6 +242,14 @@ void VoxelStreamImage::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_channel", "channel"), &VoxelStreamImage::set_channel);
|
||||
ClassDB::bind_method(D_METHOD("get_channel"), &VoxelStreamImage::get_channel);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_sdf_mode", "mode"), &VoxelStreamImage::set_sdf_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_sdf_mode"), &VoxelStreamImage::get_sdf_mode);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "image", PROPERTY_HINT_RESOURCE_TYPE, "Image"), "set_image", "get_image");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "channel", PROPERTY_HINT_ENUM, VoxelBuffer::CHANNEL_ID_HINT_STRING), "set_channel", "get_channel");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "sdf_mode", PROPERTY_HINT_ENUM, SDF_MODE_HINT_STRING), "set_sdf_mode", "get_sdf_mode");
|
||||
|
||||
BIND_ENUM_CONSTANT(SDF_VERTICAL);
|
||||
BIND_ENUM_CONSTANT(SDF_VERTICAL_AVERAGE);
|
||||
BIND_ENUM_CONSTANT(SDF_SEGMENT);
|
||||
}
|
||||
|
@ -10,12 +10,24 @@ class VoxelStreamImage : public VoxelStream {
|
||||
public:
|
||||
VoxelStreamImage();
|
||||
|
||||
enum SdfMode {
|
||||
SDF_VERTICAL = 0, // Lowest quality, fastest
|
||||
SDF_VERTICAL_AVERAGE,
|
||||
SDF_SEGMENT,
|
||||
SDF_MODE_COUNT
|
||||
};
|
||||
|
||||
static const char *SDF_MODE_HINT_STRING;
|
||||
|
||||
void set_image(Ref<Image> im);
|
||||
Ref<Image> get_image() const;
|
||||
|
||||
void set_channel(VoxelBuffer::ChannelId channel);
|
||||
VoxelBuffer::ChannelId get_channel() const;
|
||||
|
||||
void set_sdf_mode(SdfMode mode);
|
||||
SdfMode get_sdf_mode() const;
|
||||
|
||||
void emerge_block(Ref<VoxelBuffer> p_out_buffer, Vector3i origin_in_voxels, int lod);
|
||||
|
||||
private:
|
||||
@ -23,7 +35,10 @@ private:
|
||||
|
||||
private:
|
||||
Ref<Image> _image;
|
||||
VoxelBuffer::ChannelId _channel;
|
||||
VoxelBuffer::ChannelId _channel = VoxelBuffer::CHANNEL_TYPE;
|
||||
SdfMode _sdf_mode = SDF_VERTICAL_AVERAGE;
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(VoxelStreamImage::SdfMode)
|
||||
|
||||
#endif // HEADER_VOXEL_STREAM_IMAGE
|
||||
|
Loading…
x
Reference in New Issue
Block a user