Merge branch 'master' into depth
This commit is contained in:
commit
aec06b214b
2
SCsub
2
SCsub
@ -11,9 +11,11 @@ files = [
|
||||
"meshers/mc/*.cpp",
|
||||
"meshers/*.cpp",
|
||||
"streams/*.cpp",
|
||||
"generators/*.cpp",
|
||||
"util/*.cpp",
|
||||
"terrain/*.cpp",
|
||||
"math/*.cpp",
|
||||
"edition/*.cpp",
|
||||
"thirdparty/lz4/*.c"
|
||||
]
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include "voxel_tool.h"
|
||||
#include "terrain/voxel_lod_terrain.h"
|
||||
#include "voxel_buffer.h"
|
||||
#include "../terrain/voxel_lod_terrain.h"
|
||||
#include "../voxel_buffer.h"
|
||||
|
||||
Vector3 VoxelRaycastResult::_b_get_position() const {
|
||||
return position.to_vec3();
|
@ -1,7 +1,7 @@
|
||||
#ifndef VOXEL_TOOL_H
|
||||
#define VOXEL_TOOL_H
|
||||
|
||||
#include "math/rect3i.h"
|
||||
#include "../math/rect3i.h"
|
||||
#include <core/reference.h>
|
||||
|
||||
class VoxelBuffer;
|
@ -1,5 +1,5 @@
|
||||
#include "voxel_tool_buffer.h"
|
||||
#include "voxel_buffer.h"
|
||||
#include "../voxel_buffer.h"
|
||||
|
||||
VoxelToolBuffer::VoxelToolBuffer(Ref<VoxelBuffer> vb) {
|
||||
ERR_FAIL_COND(vb.is_null());
|
@ -1,6 +1,6 @@
|
||||
#include "voxel_tool_lod_terrain.h"
|
||||
#include "terrain/voxel_lod_terrain.h"
|
||||
#include "terrain/voxel_map.h"
|
||||
#include "../terrain/voxel_lod_terrain.h"
|
||||
#include "../terrain/voxel_map.h"
|
||||
|
||||
VoxelToolLodTerrain::VoxelToolLodTerrain(VoxelLodTerrain *terrain, Ref<VoxelMap> map) {
|
||||
ERR_FAIL_COND(terrain == nullptr);
|
@ -1,7 +1,7 @@
|
||||
#include "voxel_tool_terrain.h"
|
||||
#include "terrain/voxel_map.h"
|
||||
#include "terrain/voxel_terrain.h"
|
||||
#include "util/voxel_raycast.h"
|
||||
#include "../terrain/voxel_map.h"
|
||||
#include "../terrain/voxel_terrain.h"
|
||||
#include "../util/voxel_raycast.h"
|
||||
|
||||
VoxelToolTerrain::VoxelToolTerrain(VoxelTerrain *terrain, Ref<VoxelMap> map) {
|
||||
ERR_FAIL_COND(terrain == nullptr);
|
||||
@ -15,53 +15,42 @@ bool VoxelToolTerrain::is_area_editable(const Rect3i &box) const {
|
||||
return _map->is_area_fully_loaded(box.padded(1));
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct _VoxelTerrainRaycastContext {
|
||||
VoxelTerrain &terrain;
|
||||
//unsigned int channel_mask;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
static bool cb_raycast_predicate(Vector3i pos, void *context_ptr) {
|
||||
// TODO This is really not the best way.
|
||||
// This needs lambda goodness and made available to other volume types (generic?)
|
||||
|
||||
ERR_FAIL_COND_V(context_ptr == NULL, false);
|
||||
_VoxelTerrainRaycastContext *context = (_VoxelTerrainRaycastContext *)context_ptr;
|
||||
VoxelTerrain &terrain = context->terrain;
|
||||
|
||||
//unsigned int channel = context->channel;
|
||||
|
||||
Ref<VoxelMap> map = terrain.get_storage();
|
||||
int v0 = map->get_voxel(pos, VoxelBuffer::CHANNEL_TYPE);
|
||||
|
||||
Ref<VoxelLibrary> lib_ref = terrain.get_voxel_library();
|
||||
if (lib_ref.is_null())
|
||||
return false;
|
||||
const VoxelLibrary &lib = **lib_ref;
|
||||
|
||||
if (lib.has_voxel(v0) == false)
|
||||
return false;
|
||||
|
||||
const Voxel &voxel = lib.get_voxel_const(v0);
|
||||
if (voxel.is_transparent() == false)
|
||||
return true;
|
||||
|
||||
float v1 = map->get_voxel_f(pos, VoxelBuffer::CHANNEL_SDF);
|
||||
return v1 < 0;
|
||||
}
|
||||
|
||||
Ref<VoxelRaycastResult> VoxelToolTerrain::raycast(Vector3 pos, Vector3 dir, float max_distance) {
|
||||
|
||||
// TODO Transform input if the terrain is rotated (in the future it can be made a Spatial node)
|
||||
|
||||
struct RaycastPredicate {
|
||||
const VoxelTerrain &terrain;
|
||||
|
||||
bool operator()(Vector3i pos) {
|
||||
//unsigned int channel = context->channel;
|
||||
|
||||
Ref<VoxelMap> map = terrain.get_storage();
|
||||
int v0 = map->get_voxel(pos, VoxelBuffer::CHANNEL_TYPE);
|
||||
|
||||
Ref<VoxelLibrary> lib_ref = terrain.get_voxel_library();
|
||||
if (lib_ref.is_null())
|
||||
return false;
|
||||
const VoxelLibrary &lib = **lib_ref;
|
||||
|
||||
if (lib.has_voxel(v0) == false)
|
||||
return false;
|
||||
|
||||
const Voxel &voxel = lib.get_voxel_const(v0);
|
||||
if (voxel.is_transparent() == false)
|
||||
return true;
|
||||
|
||||
float v1 = map->get_voxel_f(pos, VoxelBuffer::CHANNEL_SDF);
|
||||
return v1 < 0;
|
||||
}
|
||||
};
|
||||
|
||||
Vector3i hit_pos;
|
||||
Vector3i prev_pos;
|
||||
|
||||
_VoxelTerrainRaycastContext context = { *_terrain };
|
||||
Ref<VoxelRaycastResult> res;
|
||||
|
||||
if (voxel_raycast(pos, dir, cb_raycast_predicate, &context, max_distance, hit_pos, prev_pos)) {
|
||||
RaycastPredicate predicate = { *_terrain };
|
||||
if (voxel_raycast(pos, dir, predicate, max_distance, hit_pos, prev_pos)) {
|
||||
|
||||
res.instance();
|
||||
res->position = hit_pos;
|
55
generators/voxel_generator.cpp
Normal file
55
generators/voxel_generator.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
#include "voxel_generator.h"
|
||||
#include "../voxel_string_names.h"
|
||||
|
||||
VoxelGenerator::VoxelGenerator() {
|
||||
}
|
||||
|
||||
void VoxelGenerator::generate_block(VoxelBlockRequest &input) {
|
||||
|
||||
ERR_FAIL_COND(input.voxel_buffer.is_null());
|
||||
ScriptInstance *script = get_script_instance();
|
||||
|
||||
if (script) {
|
||||
|
||||
// Call script to generate buffer
|
||||
Variant arg1 = input.voxel_buffer;
|
||||
Variant arg2 = input.origin_in_voxels.to_vec3();
|
||||
Variant arg3 = input.lod;
|
||||
|
||||
const Variant *args[3] = { &arg1, &arg2, &arg3 };
|
||||
Variant::CallError err;
|
||||
script->call(VoxelStringNames::get_singleton()->generate_block, args, 3, err);
|
||||
|
||||
ERR_FAIL_COND_MSG(err.error != Variant::CallError::CALL_OK,
|
||||
"voxel_generator.cpp:emerge_block gave an error: " + String::num(err.error) +
|
||||
", Argument: " + String::num(err.argument) +
|
||||
", Expected type: " + Variant::get_type_name(err.expected));
|
||||
|
||||
// This had to be explicitely logged due to the usual GD debugger not working with threads
|
||||
}
|
||||
}
|
||||
|
||||
//bool VoxelGenerator::is_thread_safe() const {
|
||||
// return false;
|
||||
//}
|
||||
|
||||
//bool VoxelGenerator::is_cloneable() const {
|
||||
// return false;
|
||||
//}
|
||||
|
||||
void VoxelGenerator::emerge_block(Ref<VoxelBuffer> out_buffer, Vector3i origin_in_voxels, int lod) {
|
||||
VoxelBlockRequest r = { out_buffer, Vector3i(origin_in_voxels), lod };
|
||||
generate_block(r);
|
||||
}
|
||||
|
||||
void VoxelGenerator::_b_generate_block(Ref<VoxelBuffer> out_buffer, Vector3 origin_in_voxels, int lod) {
|
||||
ERR_FAIL_COND(lod < 0);
|
||||
VoxelBlockRequest r = { out_buffer, Vector3i(origin_in_voxels), lod };
|
||||
generate_block(r);
|
||||
}
|
||||
|
||||
void VoxelGenerator::_bind_methods() {
|
||||
// Note: C++ inheriting classes don't need to re-bind these, because they are bindings that call the actual virtual methods
|
||||
|
||||
ClassDB::bind_method(D_METHOD("generate_block", "out_buffer", "origin_in_voxels", "lod"), &VoxelGenerator::_b_generate_block);
|
||||
}
|
31
generators/voxel_generator.h
Normal file
31
generators/voxel_generator.h
Normal file
@ -0,0 +1,31 @@
|
||||
#ifndef VOXEL_GENERATOR_H
|
||||
#define VOXEL_GENERATOR_H
|
||||
|
||||
#include "../streams/voxel_stream.h"
|
||||
|
||||
// TODO I would like VoxelGenerator to not inherit VoxelStream
|
||||
// because it gets members that make no sense with generators
|
||||
|
||||
// Provides access to read-only generated voxels.
|
||||
// Must be implemented in a multi-thread-safe way.
|
||||
class VoxelGenerator : public VoxelStream {
|
||||
GDCLASS(VoxelGenerator, VoxelStream)
|
||||
public:
|
||||
VoxelGenerator();
|
||||
|
||||
virtual void generate_block(VoxelBlockRequest &input);
|
||||
// TODO Single sample
|
||||
|
||||
// virtual bool is_thread_safe() const;
|
||||
// virtual bool is_cloneable() const;
|
||||
|
||||
private:
|
||||
void emerge_block(Ref<VoxelBuffer> out_buffer, Vector3i origin_in_voxels, int lod) override;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
void _b_generate_block(Ref<VoxelBuffer> out_buffer, Vector3 origin_in_voxels, int lod);
|
||||
};
|
||||
|
||||
#endif // VOXEL_GENERATOR_H
|
59
generators/voxel_generator_heightmap.cpp
Normal file
59
generators/voxel_generator_heightmap.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
#include "voxel_generator_heightmap.h"
|
||||
#include "../util/array_slice.h"
|
||||
#include "../util/fixed_array.h"
|
||||
|
||||
VoxelGeneratorHeightmap::VoxelGeneratorHeightmap() {
|
||||
}
|
||||
|
||||
void VoxelGeneratorHeightmap::set_channel(VoxelBuffer::ChannelId channel) {
|
||||
ERR_FAIL_INDEX(channel, VoxelBuffer::MAX_CHANNELS);
|
||||
_channel = channel;
|
||||
}
|
||||
|
||||
VoxelBuffer::ChannelId VoxelGeneratorHeightmap::get_channel() const {
|
||||
return _channel;
|
||||
}
|
||||
|
||||
void VoxelGeneratorHeightmap::set_height_start(float start) {
|
||||
_range.start = start;
|
||||
}
|
||||
|
||||
float VoxelGeneratorHeightmap::get_height_start() const {
|
||||
return _range.start;
|
||||
}
|
||||
|
||||
void VoxelGeneratorHeightmap::set_height_range(float range) {
|
||||
_range.height = range;
|
||||
}
|
||||
|
||||
float VoxelGeneratorHeightmap::get_height_range() const {
|
||||
return _range.height;
|
||||
}
|
||||
|
||||
void VoxelGeneratorHeightmap::set_iso_scale(float iso_scale) {
|
||||
_iso_scale = iso_scale;
|
||||
}
|
||||
|
||||
float VoxelGeneratorHeightmap::get_iso_scale() const {
|
||||
return _iso_scale;
|
||||
}
|
||||
|
||||
void VoxelGeneratorHeightmap::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_channel", "channel"), &VoxelGeneratorHeightmap::set_channel);
|
||||
ClassDB::bind_method(D_METHOD("get_channel"), &VoxelGeneratorHeightmap::get_channel);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_height_start", "start"), &VoxelGeneratorHeightmap::set_height_start);
|
||||
ClassDB::bind_method(D_METHOD("get_height_start"), &VoxelGeneratorHeightmap::get_height_start);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_height_range", "range"), &VoxelGeneratorHeightmap::set_height_range);
|
||||
ClassDB::bind_method(D_METHOD("get_height_range"), &VoxelGeneratorHeightmap::get_height_range);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_iso_scale", "scale"), &VoxelGeneratorHeightmap::set_iso_scale);
|
||||
ClassDB::bind_method(D_METHOD("get_iso_scale"), &VoxelGeneratorHeightmap::get_iso_scale);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "channel", PROPERTY_HINT_ENUM, VoxelBuffer::CHANNEL_ID_HINT_STRING), "set_channel", "get_channel");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "height_start"), "set_height_start", "get_height_start");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "height_range"), "set_height_range", "get_height_range");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "iso_scale"), "set_iso_scale", "get_iso_scale");
|
||||
}
|
106
generators/voxel_generator_heightmap.h
Normal file
106
generators/voxel_generator_heightmap.h
Normal file
@ -0,0 +1,106 @@
|
||||
#ifndef VOXEL_GENERATOR_HEIGHTMAP_H
|
||||
#define VOXEL_GENERATOR_HEIGHTMAP_H
|
||||
|
||||
#include "../voxel_buffer.h"
|
||||
#include "voxel_generator.h"
|
||||
#include <core/image.h>
|
||||
|
||||
class VoxelGeneratorHeightmap : public VoxelGenerator {
|
||||
GDCLASS(VoxelGeneratorHeightmap, VoxelGenerator)
|
||||
public:
|
||||
VoxelGeneratorHeightmap();
|
||||
|
||||
void set_channel(VoxelBuffer::ChannelId channel);
|
||||
VoxelBuffer::ChannelId get_channel() const;
|
||||
|
||||
void set_height_start(float start);
|
||||
float get_height_start() const;
|
||||
|
||||
void set_height_range(float range);
|
||||
float get_height_range() const;
|
||||
|
||||
void set_iso_scale(float iso_scale);
|
||||
float get_iso_scale() const;
|
||||
|
||||
protected:
|
||||
template <typename Height_F>
|
||||
void generate(VoxelBuffer &out_buffer, Height_F height_func, Vector3i origin, int lod) {
|
||||
|
||||
const int channel = _channel;
|
||||
const Vector3i bs = out_buffer.get_size();
|
||||
bool use_sdf = channel == VoxelBuffer::CHANNEL_SDF;
|
||||
|
||||
if (origin.y > get_height_start() + get_height_range()) {
|
||||
// The bottom of the block is above the highest ground can go (default is air)
|
||||
return;
|
||||
}
|
||||
if (origin.y + (bs.y << lod) < get_height_start()) {
|
||||
// The top of the block is below the lowest ground can go
|
||||
out_buffer.clear_channel(_channel, use_sdf ? 0 : _matter_type);
|
||||
return;
|
||||
}
|
||||
|
||||
const int stride = 1 << lod;
|
||||
|
||||
if (use_sdf) {
|
||||
|
||||
int gz = origin.z;
|
||||
for (int z = 0; z < bs.z; ++z, gz += stride) {
|
||||
|
||||
int gx = origin.x;
|
||||
for (int x = 0; x < bs.x; ++x, gx += stride) {
|
||||
|
||||
float h = _range.xform(height_func(gx, gz));
|
||||
int gy = origin.y;
|
||||
for (int y = 0; y < bs.y; ++y, gy += stride) {
|
||||
float sdf = _iso_scale * (gy - h);
|
||||
out_buffer.set_voxel_f(sdf, x, y, z, channel);
|
||||
}
|
||||
|
||||
} // for x
|
||||
} // for z
|
||||
|
||||
} else {
|
||||
// Blocky
|
||||
|
||||
int gz = origin.z;
|
||||
for (int z = 0; z < bs.z; ++z, gz += stride) {
|
||||
|
||||
int gx = origin.x;
|
||||
for (int x = 0; x < bs.x; ++x, gx += stride) {
|
||||
|
||||
// Output is blocky, so we can go for just one sample
|
||||
float h = _range.xform(height_func(gx, gz));
|
||||
h -= origin.y;
|
||||
int ih = int(h);
|
||||
if (ih > 0) {
|
||||
if (ih > bs.y) {
|
||||
ih = bs.y;
|
||||
}
|
||||
out_buffer.fill_area(_matter_type, Vector3i(x, 0, z), Vector3i(x + 1, ih, z + 1), channel);
|
||||
}
|
||||
|
||||
} // for x
|
||||
} // for z
|
||||
} // use_sdf
|
||||
}
|
||||
|
||||
private:
|
||||
static void _bind_methods();
|
||||
|
||||
struct Range {
|
||||
float start = -50;
|
||||
float height = 200;
|
||||
|
||||
inline float xform(float x) const {
|
||||
return x * height + start;
|
||||
}
|
||||
};
|
||||
|
||||
VoxelBuffer::ChannelId _channel = VoxelBuffer::CHANNEL_TYPE;
|
||||
int _matter_type = 1;
|
||||
Range _range;
|
||||
float _iso_scale = 0.1;
|
||||
};
|
||||
|
||||
#endif // VOXEL_GENERATOR_HEIGHTMAP_H
|
75
generators/voxel_generator_image.cpp
Normal file
75
generators/voxel_generator_image.cpp
Normal file
@ -0,0 +1,75 @@
|
||||
#include "voxel_generator_image.h"
|
||||
#include "../util/array_slice.h"
|
||||
#include "../util/fixed_array.h"
|
||||
|
||||
namespace {
|
||||
|
||||
inline float get_height_repeat(Image &im, int x, int y) {
|
||||
return im.get_pixel(wrap(x, im.get_width()), wrap(y, im.get_height())).r;
|
||||
}
|
||||
|
||||
inline float get_height_blurred(Image &im, int x, int y) {
|
||||
float h = get_height_repeat(im, x, y);
|
||||
h += get_height_repeat(im, x + 1, y);
|
||||
h += get_height_repeat(im, x - 1, y);
|
||||
h += get_height_repeat(im, x, y + 1);
|
||||
h += get_height_repeat(im, x, y - 1);
|
||||
return h * 0.2f;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
VoxelGeneratorImage::VoxelGeneratorImage() {
|
||||
}
|
||||
|
||||
void VoxelGeneratorImage::set_image(Ref<Image> im) {
|
||||
_image = im;
|
||||
}
|
||||
|
||||
Ref<Image> VoxelGeneratorImage::get_image() const {
|
||||
return _image;
|
||||
}
|
||||
|
||||
void VoxelGeneratorImage::set_blur_enabled(bool enable) {
|
||||
_blur_enabled = enable;
|
||||
}
|
||||
|
||||
bool VoxelGeneratorImage::is_blur_enabled() const {
|
||||
return _blur_enabled;
|
||||
}
|
||||
|
||||
void VoxelGeneratorImage::generate_block(VoxelBlockRequest &input) {
|
||||
|
||||
ERR_FAIL_COND(_image.is_null());
|
||||
|
||||
VoxelBuffer &out_buffer = **input.voxel_buffer;
|
||||
Image &image = **_image;
|
||||
|
||||
image.lock();
|
||||
|
||||
if (_blur_enabled) {
|
||||
VoxelGeneratorHeightmap::generate(out_buffer,
|
||||
[&image](int x, int z) { return get_height_blurred(image, x, z); },
|
||||
input.origin_in_voxels, input.lod);
|
||||
} else {
|
||||
VoxelGeneratorHeightmap::generate(out_buffer,
|
||||
[&image](int x, int z) { return get_height_repeat(image, x, z); },
|
||||
input.origin_in_voxels, input.lod);
|
||||
}
|
||||
|
||||
image.unlock();
|
||||
|
||||
out_buffer.compress_uniform_channels();
|
||||
}
|
||||
|
||||
void VoxelGeneratorImage::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_image", "image"), &VoxelGeneratorImage::set_image);
|
||||
ClassDB::bind_method(D_METHOD("get_image"), &VoxelGeneratorImage::get_image);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("blur_enabled", "enable"), &VoxelGeneratorImage::set_blur_enabled);
|
||||
ClassDB::bind_method(D_METHOD("blur_enabled"), &VoxelGeneratorImage::is_blur_enabled);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "image", PROPERTY_HINT_RESOURCE_TYPE, "Image"), "set_image", "get_image");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "blur_enabled"), "set_blur_enabled", "is_blur_enabled");
|
||||
}
|
30
generators/voxel_generator_image.h
Normal file
30
generators/voxel_generator_image.h
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef HEADER_VOXEL_GENERATOR_IMAGE
|
||||
#define HEADER_VOXEL_GENERATOR_IMAGE
|
||||
|
||||
#include "voxel_generator_heightmap.h"
|
||||
#include <core/image.h>
|
||||
|
||||
// Provides infinite tiling heightmap based on an image
|
||||
class VoxelGeneratorImage : public VoxelGeneratorHeightmap {
|
||||
GDCLASS(VoxelGeneratorImage, VoxelGeneratorHeightmap)
|
||||
public:
|
||||
VoxelGeneratorImage();
|
||||
|
||||
void set_image(Ref<Image> im);
|
||||
Ref<Image> get_image() const;
|
||||
|
||||
void set_blur_enabled(bool enable);
|
||||
bool is_blur_enabled() const;
|
||||
|
||||
void generate_block(VoxelBlockRequest &input) override;
|
||||
|
||||
private:
|
||||
static void _bind_methods();
|
||||
|
||||
private:
|
||||
Ref<Image> _image;
|
||||
// Mostly here as demo/tweak. It's better recommended to use an EXR/float image.
|
||||
bool _blur_enabled = false;
|
||||
};
|
||||
|
||||
#endif // HEADER_VOXEL_GENERATOR_IMAGE
|
@ -1,38 +1,38 @@
|
||||
#include "voxel_stream_noise.h"
|
||||
#include "voxel_generator_noise.h"
|
||||
|
||||
void VoxelStreamNoise::set_channel(VoxelBuffer::ChannelId channel) {
|
||||
void VoxelGeneratorNoise::set_channel(VoxelBuffer::ChannelId channel) {
|
||||
ERR_FAIL_INDEX(channel, VoxelBuffer::MAX_CHANNELS);
|
||||
_channel = channel;
|
||||
}
|
||||
|
||||
VoxelBuffer::ChannelId VoxelStreamNoise::get_channel() const {
|
||||
VoxelBuffer::ChannelId VoxelGeneratorNoise::get_channel() const {
|
||||
return _channel;
|
||||
}
|
||||
|
||||
void VoxelStreamNoise::set_noise(Ref<OpenSimplexNoise> noise) {
|
||||
void VoxelGeneratorNoise::set_noise(Ref<OpenSimplexNoise> noise) {
|
||||
_noise = noise;
|
||||
}
|
||||
|
||||
Ref<OpenSimplexNoise> VoxelStreamNoise::get_noise() const {
|
||||
Ref<OpenSimplexNoise> VoxelGeneratorNoise::get_noise() const {
|
||||
return _noise;
|
||||
}
|
||||
|
||||
void VoxelStreamNoise::set_height_start(real_t y) {
|
||||
void VoxelGeneratorNoise::set_height_start(real_t y) {
|
||||
_height_start = y;
|
||||
}
|
||||
|
||||
real_t VoxelStreamNoise::get_height_start() const {
|
||||
real_t VoxelGeneratorNoise::get_height_start() const {
|
||||
return _height_start;
|
||||
}
|
||||
|
||||
void VoxelStreamNoise::set_height_range(real_t hrange) {
|
||||
void VoxelGeneratorNoise::set_height_range(real_t hrange) {
|
||||
if (hrange < 0.1f) {
|
||||
hrange = 0.1f;
|
||||
}
|
||||
_height_range = hrange;
|
||||
}
|
||||
|
||||
real_t VoxelStreamNoise::get_height_range() const {
|
||||
real_t VoxelGeneratorNoise::get_height_range() const {
|
||||
return _height_range;
|
||||
}
|
||||
|
||||
@ -70,13 +70,15 @@ static inline float get_shaped_noise(OpenSimplexNoise &noise, float x, float y,
|
||||
return sum / max;
|
||||
}
|
||||
|
||||
void VoxelStreamNoise::emerge_block(Ref<VoxelBuffer> out_buffer, Vector3i origin_in_voxels, int lod) {
|
||||
void VoxelGeneratorNoise::generate_block(VoxelBlockRequest &input) {
|
||||
|
||||
ERR_FAIL_COND(out_buffer.is_null());
|
||||
ERR_FAIL_COND(input.voxel_buffer.is_null());
|
||||
ERR_FAIL_COND(_noise.is_null());
|
||||
|
||||
OpenSimplexNoise &noise = **_noise;
|
||||
VoxelBuffer &buffer = **out_buffer;
|
||||
VoxelBuffer &buffer = **input.voxel_buffer;
|
||||
Vector3i origin_in_voxels = input.origin_in_voxels;
|
||||
int lod = input.lod;
|
||||
|
||||
int isosurface_lower_bound = static_cast<int>(Math::floor(_height_start));
|
||||
int isosurface_upper_bound = static_cast<int>(Math::ceil(_height_start + _height_range));
|
||||
@ -157,19 +159,19 @@ void VoxelStreamNoise::emerge_block(Ref<VoxelBuffer> out_buffer, Vector3i origin
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelStreamNoise::_bind_methods() {
|
||||
void VoxelGeneratorNoise::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_noise", "noise"), &VoxelStreamNoise::set_noise);
|
||||
ClassDB::bind_method(D_METHOD("get_noise"), &VoxelStreamNoise::get_noise);
|
||||
ClassDB::bind_method(D_METHOD("set_noise", "noise"), &VoxelGeneratorNoise::set_noise);
|
||||
ClassDB::bind_method(D_METHOD("get_noise"), &VoxelGeneratorNoise::get_noise);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_height_start", "hstart"), &VoxelStreamNoise::set_height_start);
|
||||
ClassDB::bind_method(D_METHOD("get_height_start"), &VoxelStreamNoise::get_height_start);
|
||||
ClassDB::bind_method(D_METHOD("set_height_start", "hstart"), &VoxelGeneratorNoise::set_height_start);
|
||||
ClassDB::bind_method(D_METHOD("get_height_start"), &VoxelGeneratorNoise::get_height_start);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_height_range", "hrange"), &VoxelStreamNoise::set_height_range);
|
||||
ClassDB::bind_method(D_METHOD("get_height_range"), &VoxelStreamNoise::get_height_range);
|
||||
ClassDB::bind_method(D_METHOD("set_height_range", "hrange"), &VoxelGeneratorNoise::set_height_range);
|
||||
ClassDB::bind_method(D_METHOD("get_height_range"), &VoxelGeneratorNoise::get_height_range);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_channel", "channel"), &VoxelStreamNoise::set_channel);
|
||||
ClassDB::bind_method(D_METHOD("get_channel"), &VoxelStreamNoise::get_channel);
|
||||
ClassDB::bind_method(D_METHOD("set_channel", "channel"), &VoxelGeneratorNoise::set_channel);
|
||||
ClassDB::bind_method(D_METHOD("get_channel"), &VoxelGeneratorNoise::get_channel);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "noise", PROPERTY_HINT_RESOURCE_TYPE, "OpenSimplexNoise"), "set_noise", "get_noise");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "height_start"), "set_height_start", "get_height_start");
|
@ -1,12 +1,12 @@
|
||||
#ifndef VOXEL_STREAM_NOISE_H
|
||||
#define VOXEL_STREAM_NOISE_H
|
||||
#ifndef VOXEL_GENERATOR_NOISE_H
|
||||
#define VOXEL_GENERATOR_NOISE_H
|
||||
|
||||
#include "../util/float_buffer_3d.h"
|
||||
#include "voxel_stream.h"
|
||||
#include "voxel_generator.h"
|
||||
#include <modules/opensimplex/open_simplex_noise.h>
|
||||
|
||||
class VoxelStreamNoise : public VoxelStream {
|
||||
GDCLASS(VoxelStreamNoise, VoxelStream)
|
||||
class VoxelGeneratorNoise : public VoxelGenerator {
|
||||
GDCLASS(VoxelGeneratorNoise, VoxelGenerator)
|
||||
|
||||
public:
|
||||
void set_channel(VoxelBuffer::ChannelId channel);
|
||||
@ -21,7 +21,7 @@ public:
|
||||
void set_height_range(real_t hrange);
|
||||
real_t get_height_range() const;
|
||||
|
||||
void emerge_block(Ref<VoxelBuffer> out_buffer, Vector3i origin_in_voxels, int lod);
|
||||
void generate_block(VoxelBlockRequest &input) override;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
@ -34,4 +34,4 @@ private:
|
||||
float _height_range = 300;
|
||||
};
|
||||
|
||||
#endif // VOXEL_STREAM_NOISE_H
|
||||
#endif // VOXEL_GENERATOR_NOISE_H
|
53
generators/voxel_generator_noise_2d.cpp
Normal file
53
generators/voxel_generator_noise_2d.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
#include "voxel_generator_noise_2d.h"
|
||||
|
||||
VoxelGeneratorNoise2D::VoxelGeneratorNoise2D() {
|
||||
}
|
||||
|
||||
void VoxelGeneratorNoise2D::set_noise(Ref<OpenSimplexNoise> noise) {
|
||||
_noise = noise;
|
||||
}
|
||||
|
||||
Ref<OpenSimplexNoise> VoxelGeneratorNoise2D::get_noise() const {
|
||||
return _noise;
|
||||
}
|
||||
|
||||
void VoxelGeneratorNoise2D::set_curve(Ref<Curve> curve) {
|
||||
_curve = curve;
|
||||
}
|
||||
|
||||
Ref<Curve> VoxelGeneratorNoise2D::get_curve() const {
|
||||
return _curve;
|
||||
}
|
||||
|
||||
void VoxelGeneratorNoise2D::generate_block(VoxelBlockRequest &input) {
|
||||
|
||||
ERR_FAIL_COND(_noise.is_null());
|
||||
|
||||
VoxelBuffer &out_buffer = **input.voxel_buffer;
|
||||
OpenSimplexNoise &noise = **_noise;
|
||||
|
||||
if (_curve.is_null()) {
|
||||
VoxelGeneratorHeightmap::generate(out_buffer,
|
||||
[&noise](int x, int z) { return 0.5 + 0.5 * noise.get_noise_2d(x, z); },
|
||||
input.origin_in_voxels, input.lod);
|
||||
} else {
|
||||
Curve &curve = **_curve;
|
||||
VoxelGeneratorHeightmap::generate(out_buffer,
|
||||
[&noise, &curve](int x, int z) { return curve.interpolate_baked(0.5 + 0.5 * noise.get_noise_2d(x, z)); },
|
||||
input.origin_in_voxels, input.lod);
|
||||
}
|
||||
|
||||
out_buffer.compress_uniform_channels();
|
||||
}
|
||||
|
||||
void VoxelGeneratorNoise2D::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_noise", "noise"), &VoxelGeneratorNoise2D::set_noise);
|
||||
ClassDB::bind_method(D_METHOD("get_noise"), &VoxelGeneratorNoise2D::get_noise);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_curve", "curve"), &VoxelGeneratorNoise2D::set_curve);
|
||||
ClassDB::bind_method(D_METHOD("get_curve"), &VoxelGeneratorNoise2D::get_curve);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "noise", PROPERTY_HINT_RESOURCE_TYPE, "OpenSimplexNoise"), "set_noise", "get_noise");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve");
|
||||
}
|
28
generators/voxel_generator_noise_2d.h
Normal file
28
generators/voxel_generator_noise_2d.h
Normal file
@ -0,0 +1,28 @@
|
||||
#ifndef VOXEL_GENERATOR_NOISE_2D_H
|
||||
#define VOXEL_GENERATOR_NOISE_2D_H
|
||||
|
||||
#include "voxel_generator_heightmap.h"
|
||||
#include <modules/opensimplex/open_simplex_noise.h>
|
||||
|
||||
class VoxelGeneratorNoise2D : public VoxelGeneratorHeightmap {
|
||||
GDCLASS(VoxelGeneratorNoise2D, VoxelGeneratorHeightmap)
|
||||
public:
|
||||
VoxelGeneratorNoise2D();
|
||||
|
||||
void set_noise(Ref<OpenSimplexNoise> noise);
|
||||
Ref<OpenSimplexNoise> get_noise() const;
|
||||
|
||||
void set_curve(Ref<Curve> curve);
|
||||
Ref<Curve> get_curve() const;
|
||||
|
||||
void generate_block(VoxelBlockRequest &input) override;
|
||||
|
||||
private:
|
||||
static void _bind_methods();
|
||||
|
||||
private:
|
||||
Ref<OpenSimplexNoise> _noise;
|
||||
Ref<Curve> _curve;
|
||||
};
|
||||
|
||||
#endif // VOXEL_GENERATOR_NOISE_2D_H
|
@ -1,38 +1,39 @@
|
||||
#include "voxel_stream_test.h"
|
||||
#include "voxel_generator_test.h"
|
||||
|
||||
VARIANT_ENUM_CAST(VoxelStreamTest::Mode)
|
||||
VARIANT_ENUM_CAST(VoxelGeneratorTest::Mode)
|
||||
|
||||
VoxelStreamTest::VoxelStreamTest() {
|
||||
VoxelGeneratorTest::VoxelGeneratorTest() {
|
||||
_mode = MODE_WAVES;
|
||||
_voxel_type = 1;
|
||||
_pattern_size = Vector3i(10, 10, 10);
|
||||
}
|
||||
|
||||
void VoxelStreamTest::set_mode(Mode mode) {
|
||||
void VoxelGeneratorTest::set_mode(Mode mode) {
|
||||
ERR_FAIL_INDEX(mode, MODE_COUNT)
|
||||
_mode = mode;
|
||||
}
|
||||
|
||||
void VoxelStreamTest::set_voxel_type(int t) {
|
||||
void VoxelGeneratorTest::set_voxel_type(int t) {
|
||||
_voxel_type = t;
|
||||
}
|
||||
|
||||
int VoxelStreamTest::get_voxel_type() const {
|
||||
int VoxelGeneratorTest::get_voxel_type() const {
|
||||
return _voxel_type;
|
||||
}
|
||||
|
||||
void VoxelStreamTest::set_pattern_size(Vector3i size) {
|
||||
void VoxelGeneratorTest::set_pattern_size(Vector3i size) {
|
||||
ERR_FAIL_COND(size.x < 1 || size.y < 1 || size.z < 1);
|
||||
_pattern_size = size;
|
||||
}
|
||||
|
||||
void VoxelStreamTest::set_pattern_offset(Vector3i offset) {
|
||||
void VoxelGeneratorTest::set_pattern_offset(Vector3i offset) {
|
||||
_pattern_offset = offset;
|
||||
}
|
||||
|
||||
void VoxelStreamTest::emerge_block(Ref<VoxelBuffer> out_buffer, Vector3i origin, int lod) {
|
||||
ERR_FAIL_COND(out_buffer.is_null());
|
||||
void VoxelGeneratorTest::generate_block(VoxelBlockRequest &input) {
|
||||
ERR_FAIL_COND(input.voxel_buffer.is_null());
|
||||
|
||||
if (lod != 0) {
|
||||
if (input.lod != 0) {
|
||||
// TODO Handle higher lods
|
||||
return;
|
||||
}
|
||||
@ -40,16 +41,16 @@ void VoxelStreamTest::emerge_block(Ref<VoxelBuffer> out_buffer, Vector3i origin,
|
||||
switch (_mode) {
|
||||
|
||||
case MODE_FLAT:
|
||||
generate_block_flat(**out_buffer, origin, lod);
|
||||
generate_block_flat(**input.voxel_buffer, input.origin_in_voxels, input.lod);
|
||||
break;
|
||||
|
||||
case MODE_WAVES:
|
||||
generate_block_waves(**out_buffer, origin, lod);
|
||||
generate_block_waves(**input.voxel_buffer, input.origin_in_voxels, input.lod);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelStreamTest::generate_block_flat(VoxelBuffer &out_buffer, Vector3i origin, int lod) {
|
||||
void VoxelGeneratorTest::generate_block_flat(VoxelBuffer &out_buffer, Vector3i origin, int lod) {
|
||||
|
||||
// TODO Don't expect a block pos, but a voxel pos!
|
||||
Vector3i size = out_buffer.get_size();
|
||||
@ -67,7 +68,7 @@ void VoxelStreamTest::generate_block_flat(VoxelBuffer &out_buffer, Vector3i orig
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelStreamTest::generate_block_waves(VoxelBuffer &out_buffer, Vector3i origin, int lod) {
|
||||
void VoxelGeneratorTest::generate_block_waves(VoxelBuffer &out_buffer, Vector3i origin, int lod) {
|
||||
|
||||
// TODO Don't expect a block pos, but a voxel pos!
|
||||
Vector3i size = out_buffer.get_size();
|
||||
@ -108,19 +109,19 @@ void VoxelStreamTest::generate_block_waves(VoxelBuffer &out_buffer, Vector3i ori
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelStreamTest::_bind_methods() {
|
||||
void VoxelGeneratorTest::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_mode", "mode"), &VoxelStreamTest::set_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_mode"), &VoxelStreamTest::get_mode);
|
||||
ClassDB::bind_method(D_METHOD("set_mode", "mode"), &VoxelGeneratorTest::set_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_mode"), &VoxelGeneratorTest::get_mode);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_voxel_type", "id"), &VoxelStreamTest::set_voxel_type);
|
||||
ClassDB::bind_method(D_METHOD("get_voxel_type"), &VoxelStreamTest::get_voxel_type);
|
||||
ClassDB::bind_method(D_METHOD("set_voxel_type", "id"), &VoxelGeneratorTest::set_voxel_type);
|
||||
ClassDB::bind_method(D_METHOD("get_voxel_type"), &VoxelGeneratorTest::get_voxel_type);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_pattern_size", "size"), &VoxelStreamTest::_set_pattern_size);
|
||||
ClassDB::bind_method(D_METHOD("get_pattern_size"), &VoxelStreamTest::_get_pattern_size);
|
||||
ClassDB::bind_method(D_METHOD("set_pattern_size", "size"), &VoxelGeneratorTest::_set_pattern_size);
|
||||
ClassDB::bind_method(D_METHOD("get_pattern_size"), &VoxelGeneratorTest::_get_pattern_size);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_pattern_offset", "offset"), &VoxelStreamTest::_set_pattern_offset);
|
||||
ClassDB::bind_method(D_METHOD("get_pattern_offset"), &VoxelStreamTest::_get_pattern_offset);
|
||||
ClassDB::bind_method(D_METHOD("set_pattern_offset", "offset"), &VoxelGeneratorTest::_set_pattern_offset);
|
||||
ClassDB::bind_method(D_METHOD("get_pattern_offset"), &VoxelGeneratorTest::_get_pattern_offset);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Flat,Waves"), "set_mode", "get_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "voxel_type", PROPERTY_HINT_RANGE, "0,255,1"), "set_voxel_type", "get_voxel_type");
|
@ -1,20 +1,21 @@
|
||||
#ifndef VOXEL_STREAM_TEST_H
|
||||
#define VOXEL_STREAM_TEST_H
|
||||
#ifndef VOXEL_GENERATOR_TEST_H
|
||||
#define VOXEL_GENERATOR_TEST_H
|
||||
|
||||
#include "voxel_stream.h"
|
||||
#include "voxel_generator.h"
|
||||
|
||||
class VoxelStreamTest : public VoxelStream {
|
||||
GDCLASS(VoxelStreamTest, VoxelStream)
|
||||
class VoxelGeneratorTest : public VoxelGenerator {
|
||||
GDCLASS(VoxelGeneratorTest, VoxelGenerator)
|
||||
|
||||
public:
|
||||
enum Mode {
|
||||
MODE_FLAT,
|
||||
MODE_WAVES
|
||||
MODE_WAVES,
|
||||
MODE_COUNT
|
||||
};
|
||||
|
||||
VoxelStreamTest();
|
||||
VoxelGeneratorTest();
|
||||
|
||||
virtual void emerge_block(Ref<VoxelBuffer> out_buffer, Vector3i origin, int lod);
|
||||
void generate_block(VoxelBlockRequest &input) override;
|
||||
|
||||
void set_mode(Mode mode);
|
||||
Mode get_mode() const { return _mode; }
|
||||
@ -47,4 +48,4 @@ private:
|
||||
Vector3i _pattern_size;
|
||||
};
|
||||
|
||||
#endif // VOXEL_STREAM_TEST_H
|
||||
#endif // VOXEL_GENERATOR_TEST_H
|
@ -1,458 +0,0 @@
|
||||
#include "voxel_mesher_mc.h"
|
||||
#include "../transvoxel/transvoxel_tables.cpp"
|
||||
#include <core/os/os.h>
|
||||
|
||||
namespace {
|
||||
|
||||
inline float tof(int8_t v) {
|
||||
return static_cast<float>(v) / 256.f;
|
||||
}
|
||||
|
||||
inline int8_t tos(uint8_t v) {
|
||||
return v - 128;
|
||||
}
|
||||
|
||||
// Values considered negative have a sign bit of 1
|
||||
inline uint8_t sign(int8_t v) {
|
||||
return (v >> 7) & 1;
|
||||
}
|
||||
|
||||
//
|
||||
// 6-------7
|
||||
// /| /|
|
||||
// / | / | Corners
|
||||
// 4-------5 |
|
||||
// | 2----|--3
|
||||
// | / | / z y
|
||||
// |/ |/ |/
|
||||
// 0-------1 o--x
|
||||
//
|
||||
|
||||
// The fact it follows a binary pattern is important
|
||||
const Vector3i g_corner_dirs[8] = {
|
||||
Vector3i(0, 0, 0),
|
||||
Vector3i(1, 0, 0),
|
||||
Vector3i(0, 1, 0),
|
||||
Vector3i(1, 1, 0),
|
||||
Vector3i(0, 0, 1),
|
||||
Vector3i(1, 0, 1),
|
||||
Vector3i(0, 1, 1),
|
||||
Vector3i(1, 1, 1)
|
||||
};
|
||||
|
||||
inline Vector3i dir_to_prev_vec(uint8_t dir) {
|
||||
//return g_corner_dirs[mask] - Vector3(1,1,1);
|
||||
return Vector3i(
|
||||
-(dir & 1),
|
||||
-((dir >> 1) & 1),
|
||||
-((dir >> 2) & 1));
|
||||
}
|
||||
|
||||
// Wrapped to invert SDF data, Transvoxel apparently works backwards?
|
||||
inline uint8_t get_voxel(const VoxelBuffer &vb, int x, int y, int z, int channel) {
|
||||
return 255 - vb.get_voxel(x, y, z, channel);
|
||||
}
|
||||
|
||||
inline uint8_t get_voxel(const VoxelBuffer &vb, Vector3i pos, int channel) {
|
||||
return get_voxel(vb, pos.x, pos.y, pos.z, channel);
|
||||
}
|
||||
|
||||
inline int8_t increase(int8_t v, int8_t a) {
|
||||
// Actually decreasing...
|
||||
int8_t res = v - a;
|
||||
if (res > v) {
|
||||
// Underflowed, clamp to min
|
||||
return -128;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
VoxelMesherMC::VoxelMesherMC() {
|
||||
set_padding(MIN_PADDING, MAX_PADDING);
|
||||
}
|
||||
|
||||
void VoxelMesherMC::build(VoxelMesher::Output &output, const VoxelMesher::Input &input) {
|
||||
|
||||
int channel = VoxelBuffer::CHANNEL_SDF;
|
||||
|
||||
// Initialize dynamic memory:
|
||||
// These vectors are re-used.
|
||||
// We don't know in advance how much geometry we are going to produce.
|
||||
// Once capacity is big enough, no more memory should be allocated
|
||||
_output_vertices.clear();
|
||||
_output_normals.clear();
|
||||
_output_indices.clear();
|
||||
|
||||
const VoxelBuffer &voxels = input.voxels;
|
||||
build_internal(voxels, channel);
|
||||
// OS::get_singleton()->print("vertices: %i, normals: %i, indices: %i\n",
|
||||
// m_output_vertices.size(),
|
||||
// m_output_normals.size(),
|
||||
// m_output_indices.size());
|
||||
|
||||
if (_output_vertices.size() == 0) {
|
||||
// The mesh can be empty
|
||||
return;
|
||||
}
|
||||
|
||||
PoolVector<Vector3> vertices;
|
||||
PoolVector<Vector3> normals;
|
||||
PoolVector<int> indices;
|
||||
|
||||
raw_copy_to(vertices, _output_vertices);
|
||||
raw_copy_to(normals, _output_normals);
|
||||
raw_copy_to(indices, _output_indices);
|
||||
|
||||
Array arrays;
|
||||
arrays.resize(Mesh::ARRAY_MAX);
|
||||
arrays[Mesh::ARRAY_VERTEX] = vertices;
|
||||
if (_output_normals.size() != 0) {
|
||||
arrays[Mesh::ARRAY_NORMAL] = normals;
|
||||
}
|
||||
arrays[Mesh::ARRAY_INDEX] = indices;
|
||||
|
||||
output.surfaces.push_back(arrays);
|
||||
output.primitive_type = Mesh::PRIMITIVE_TRIANGLES;
|
||||
}
|
||||
|
||||
void VoxelMesherMC::set_seam_mode(SeamMode mode) {
|
||||
_seam_mode = mode;
|
||||
}
|
||||
|
||||
VoxelMesherMC::SeamMode VoxelMesherMC::get_seam_mode() const {
|
||||
return _seam_mode;
|
||||
}
|
||||
|
||||
void VoxelMesherMC::build_internal(const VoxelBuffer &voxels, unsigned int channel) {
|
||||
|
||||
// Each 2x2 voxel group is a "cell"
|
||||
|
||||
if (voxels.is_uniform(channel)) {
|
||||
// Nothing to extract, because constant isolevels never cross the threshold and describe no surface
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3i block_size = voxels.get_size();
|
||||
|
||||
// Iterate all cells, with expected padding.
|
||||
// The algorithm works with a 2x2 kernel and needs extra neighbors for normals,
|
||||
// so it looks 1 voxel away in negative axes, and 2 voxels away in positive axes.
|
||||
Vector3i pos;
|
||||
Vector3i min_pos(get_minimum_padding());
|
||||
Vector3i max_pos(block_size - Vector3i(get_maximum_padding()));
|
||||
|
||||
if (_seam_mode == SEAM_OVERLAP) {
|
||||
// When this is enabled, the algorithm may detect if it's on a border,
|
||||
// and will avoid looking an extra neighbor for normals, while polygonizing an extra cell.
|
||||
min_pos -= Vector3i(1);
|
||||
max_pos += Vector3i(1);
|
||||
block_size += Vector3i(2);
|
||||
}
|
||||
|
||||
// Prepare vertex reuse cache:
|
||||
// We'll iterate deck by deck in deterministic order, so we can link vertices together from the previous deck.
|
||||
_block_size = block_size;
|
||||
unsigned int deck_area = block_size.x * block_size.y;
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
_cache[i].clear();
|
||||
_cache[i].resize(deck_area);
|
||||
}
|
||||
|
||||
bool overpoly_bx[2] = { false };
|
||||
bool overpoly_by[2] = { false };
|
||||
bool overpoly_bz[2] = { false };
|
||||
|
||||
for (pos.z = min_pos.z; pos.z < max_pos.z; ++pos.z) {
|
||||
for (pos.y = min_pos.y; pos.y < max_pos.y; ++pos.y) {
|
||||
for (pos.x = min_pos.x; pos.x < max_pos.x; ++pos.x) {
|
||||
|
||||
// Get the value of cells.
|
||||
// Negative values are "solid" and positive are "air".
|
||||
// Due to raw cells being unsigned 8-bit, they get converted to signed.
|
||||
int8_t cell_samples[8] = {
|
||||
tos(get_voxel(voxels, pos.x, pos.y, pos.z, channel)),
|
||||
tos(get_voxel(voxels, pos.x + 1, pos.y, pos.z, channel)),
|
||||
tos(get_voxel(voxels, pos.x, pos.y + 1, pos.z, channel)),
|
||||
tos(get_voxel(voxels, pos.x + 1, pos.y + 1, pos.z, channel)),
|
||||
tos(get_voxel(voxels, pos.x, pos.y, pos.z + 1, channel)),
|
||||
tos(get_voxel(voxels, pos.x + 1, pos.y, pos.z + 1, channel)),
|
||||
tos(get_voxel(voxels, pos.x, pos.y + 1, pos.z + 1, channel)),
|
||||
tos(get_voxel(voxels, pos.x + 1, pos.y + 1, pos.z + 1, channel))
|
||||
};
|
||||
|
||||
if (_seam_mode == SEAM_OVERLAP) {
|
||||
// In overpoly, we extend the polygonized area, but the extended vertices will have increased distance samples.
|
||||
// The intented effect is that the isosurface will be slightly contracted,
|
||||
// so if we stitch two chunks of different LOD with overpolys,
|
||||
// they will overlap and will fill cracks by crossing over each other.
|
||||
|
||||
overpoly_bx[0] = pos.x == min_pos.x;
|
||||
overpoly_bx[1] = pos.x == max_pos.x - 1;
|
||||
overpoly_by[0] = pos.y == min_pos.y;
|
||||
overpoly_by[1] = pos.y == max_pos.y - 1;
|
||||
overpoly_bz[0] = pos.z == min_pos.z;
|
||||
overpoly_bz[1] = pos.z == max_pos.z - 1;
|
||||
|
||||
const int8_t inc = 32;
|
||||
|
||||
for (unsigned int i = 0; i < 8; ++i) {
|
||||
if (overpoly_bx[i & 1] || overpoly_by[(i >> 1) & 1] || overpoly_bz[(i >> 2) & 1]) {
|
||||
cell_samples[i] = increase(cell_samples[i], inc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Concatenate the sign of cell values to obtain the case code.
|
||||
// Index 0 is the less significant bit, and index 7 is the most significant bit.
|
||||
uint8_t case_code = sign(cell_samples[0]);
|
||||
case_code |= (sign(cell_samples[1]) << 1);
|
||||
case_code |= (sign(cell_samples[2]) << 2);
|
||||
case_code |= (sign(cell_samples[3]) << 3);
|
||||
case_code |= (sign(cell_samples[4]) << 4);
|
||||
case_code |= (sign(cell_samples[5]) << 5);
|
||||
case_code |= (sign(cell_samples[6]) << 6);
|
||||
case_code |= (sign(cell_samples[7]) << 7);
|
||||
|
||||
{
|
||||
ReuseCell &rc = get_reuse_cell(pos);
|
||||
rc.case_index = case_code;
|
||||
}
|
||||
|
||||
if (case_code == 0 || case_code == 255) {
|
||||
// If the case_code is 0 or 255, there is no triangulation to do
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO We might not always need all of them
|
||||
// Compute normals
|
||||
Vector3 corner_normals[8];
|
||||
for (unsigned int i = 0; i < 8; ++i) {
|
||||
|
||||
Vector3i p = pos + g_corner_dirs[i];
|
||||
|
||||
if (_seam_mode == SEAM_OVERLAP) {
|
||||
// In case of overpoly, we keep the same normals as the connected vertex within the "normal" area
|
||||
|
||||
if (overpoly_bx[0]) {
|
||||
++p.x;
|
||||
}
|
||||
if (overpoly_bx[1]) {
|
||||
--p.x;
|
||||
}
|
||||
if (overpoly_by[0]) {
|
||||
++p.y;
|
||||
}
|
||||
if (overpoly_by[1]) {
|
||||
--p.y;
|
||||
}
|
||||
if (overpoly_bz[0]) {
|
||||
++p.z;
|
||||
}
|
||||
if (overpoly_bz[1]) {
|
||||
--p.z;
|
||||
}
|
||||
}
|
||||
|
||||
float nx = tof(tos(get_voxel(voxels, p - Vector3i(1, 0, 0), channel))) - tof(tos(get_voxel(voxels, p + Vector3i(1, 0, 0), channel)));
|
||||
float ny = tof(tos(get_voxel(voxels, p - Vector3i(0, 1, 0), channel))) - tof(tos(get_voxel(voxels, p + Vector3i(0, 1, 0), channel)));
|
||||
float nz = tof(tos(get_voxel(voxels, p - Vector3i(0, 0, 1), channel))) - tof(tos(get_voxel(voxels, p + Vector3i(0, 0, 1), channel)));
|
||||
|
||||
corner_normals[i] = Vector3(nx, ny, nz);
|
||||
corner_normals[i].normalize();
|
||||
}
|
||||
|
||||
// For cells occurring along the minimal boundaries of a block,
|
||||
// the preceding cells needed for vertex reuse may not exist.
|
||||
// In these cases, we allow new vertex creation on additional edges of a cell.
|
||||
// While iterating through the cells in a block, a 3-bit mask is maintained whose bits indicate
|
||||
// whether corresponding bits in a direction code are valid
|
||||
uint8_t direction_validity_mask =
|
||||
(pos.x > min_pos.x ? 1 : 0) |
|
||||
((pos.y > min_pos.y ? 1 : 0) << 1) |
|
||||
((pos.z > min_pos.z ? 1 : 0) << 2);
|
||||
|
||||
uint8_t regular_cell_class_index = Transvoxel::regularCellClass[case_code];
|
||||
Transvoxel::RegularCellData regular_cell_class = Transvoxel::regularCellData[regular_cell_class_index];
|
||||
uint8_t triangle_count = regular_cell_class.geometryCounts & 0x0f;
|
||||
uint8_t vertex_count = (regular_cell_class.geometryCounts & 0xf0) >> 4;
|
||||
|
||||
int cell_mesh_indices[12];
|
||||
|
||||
// For each vertex in the case
|
||||
for (unsigned int i = 0; i < vertex_count; ++i) {
|
||||
|
||||
// The case index maps to a list of 16-bit codes providing information about the edges on which the vertices lie.
|
||||
// The low byte of each 16-bit code contains the corner indexes of the edge’s endpoints in one nibble each,
|
||||
// and the high byte contains the mapping code shown in Figure 3.8(b)
|
||||
unsigned short rvd = Transvoxel::regularVertexData[case_code][i];
|
||||
unsigned short edge_code_low = rvd & 0xff;
|
||||
unsigned short edge_code_high = (rvd >> 8) & 0xff;
|
||||
|
||||
// Get corner indexes in the low nibble (always ordered so the higher comes last)
|
||||
uint8_t v0 = (edge_code_low >> 4) & 0xf;
|
||||
uint8_t v1 = edge_code_low & 0xf;
|
||||
|
||||
ERR_FAIL_COND(v1 <= v0);
|
||||
|
||||
// Get voxel values at the corners
|
||||
int sample0 = cell_samples[v0]; // called d0 in the paper
|
||||
int sample1 = cell_samples[v1]; // called d1 in the paper
|
||||
|
||||
// TODO Zero-division is not mentionned in the paper??
|
||||
ERR_FAIL_COND(sample1 == sample0);
|
||||
ERR_FAIL_COND(sample1 == 0 && sample0 == 0);
|
||||
|
||||
// Get interpolation position
|
||||
// We use an 8-bit fraction, allowing the new vertex to be located at one of 257 possible
|
||||
// positions along the edge when both endpoints are included.
|
||||
int t = (sample1 << 8) / (sample1 - sample0);
|
||||
|
||||
float t0 = static_cast<float>(t) / 256.f;
|
||||
float t1 = static_cast<float>(0x0100 - t) / 256.f;
|
||||
|
||||
Vector3i p0 = pos + g_corner_dirs[v0];
|
||||
Vector3i p1 = pos + g_corner_dirs[v1];
|
||||
|
||||
if (t & 0xff) {
|
||||
// Vertex lies in the interior of the edge.
|
||||
|
||||
// Each edge of a cell is assigned an 8-bit code, as shown in Figure 3.8(b),
|
||||
// that provides a mapping to a preceding cell and the coincident edge on that preceding cell
|
||||
// for which new vertex creation was allowed.
|
||||
// The high nibble of this code indicates which direction to go in order to reach the correct preceding cell.
|
||||
// The bit values 1, 2, and 4 in this nibble indicate that we must subtract one
|
||||
// from the x, y, and/or z coordinate, respectively.
|
||||
uint8_t reuse_dir = (edge_code_high >> 4) & 0xf;
|
||||
uint8_t reuse_vertex_index = edge_code_high & 0xf;
|
||||
|
||||
bool can_reuse = (reuse_dir & direction_validity_mask) == reuse_dir;
|
||||
|
||||
if (can_reuse) {
|
||||
Vector3i cache_pos = pos + dir_to_prev_vec(reuse_dir);
|
||||
ReuseCell &prev_cell = get_reuse_cell(cache_pos);
|
||||
|
||||
if (prev_cell.case_index == 0 || prev_cell.case_index == 255) {
|
||||
// TODO I don't think this can happen for non-corner vertices.
|
||||
cell_mesh_indices[i] = -1;
|
||||
} else {
|
||||
// Will reuse a previous vertice
|
||||
cell_mesh_indices[i] = prev_cell.vertices[reuse_vertex_index];
|
||||
}
|
||||
}
|
||||
|
||||
if (!can_reuse || cell_mesh_indices[i] == -1) {
|
||||
// Going to create a new vertice
|
||||
|
||||
cell_mesh_indices[i] = _output_vertices.size();
|
||||
|
||||
Vector3 pi = p0.to_vec3() * t0 + p1.to_vec3() * t1;
|
||||
|
||||
Vector3 primary = pi; //pos.to_vec3() + pi;
|
||||
Vector3 normal = corner_normals[v0] * t0 + corner_normals[v1] * t1;
|
||||
|
||||
emit_vertex(primary, normal);
|
||||
|
||||
if (reuse_dir & 8) {
|
||||
// Store the generated vertex so that other cells can reuse it.
|
||||
ReuseCell &rc = get_reuse_cell(pos);
|
||||
rc.vertices[reuse_vertex_index] = cell_mesh_indices[i];
|
||||
}
|
||||
}
|
||||
|
||||
} else if (t == 0 && v1 == 7) {
|
||||
|
||||
// This cell owns the vertex, so it should be created.
|
||||
|
||||
cell_mesh_indices[i] = _output_vertices.size();
|
||||
|
||||
Vector3 pi = p0.to_vec3() * t0 + p1.to_vec3() * t1;
|
||||
Vector3 primary = pi; //pos.to_vec3() + pi;
|
||||
Vector3 normal = corner_normals[v0] * t0 + corner_normals[v1] * t1;
|
||||
|
||||
emit_vertex(primary, normal);
|
||||
|
||||
ReuseCell &rc = get_reuse_cell(pos);
|
||||
rc.vertices[0] = cell_mesh_indices[i];
|
||||
|
||||
} else {
|
||||
// Always try to reuse previous vertices in these cases
|
||||
|
||||
// A 3-bit direction code leading to the proper cell can easily be obtained by
|
||||
// inverting the 3-bit corner index (bitwise, by exclusive ORing with the number 7).
|
||||
// The corner index depends on the value of t, t = 0 means that we're at the higher
|
||||
// numbered endpoint.
|
||||
uint8_t reuse_dir = (t == 0 ? v1 ^ 7 : v0 ^ 7);
|
||||
bool can_reuse = (reuse_dir & direction_validity_mask) == reuse_dir;
|
||||
|
||||
// Note: the only difference with similar code above is that we take vertice 0 in the `else`
|
||||
if (can_reuse) {
|
||||
Vector3i cache_pos = pos + dir_to_prev_vec(reuse_dir);
|
||||
ReuseCell prev_cell = get_reuse_cell(cache_pos);
|
||||
|
||||
// The previous cell might not have any geometry, and we
|
||||
// might therefore have to create a new vertex anyway.
|
||||
if (prev_cell.case_index == 0 || prev_cell.case_index == 255) {
|
||||
cell_mesh_indices[i] = -1;
|
||||
} else {
|
||||
cell_mesh_indices[i] = prev_cell.vertices[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (!can_reuse || cell_mesh_indices[i] < 0) {
|
||||
cell_mesh_indices[i] = _output_vertices.size();
|
||||
|
||||
Vector3 pi = p0.to_vec3() * t0 + p1.to_vec3() * t1;
|
||||
Vector3 primary = pi; //pos.to_vec3() + pi;
|
||||
Vector3 normal = corner_normals[v0] * t0 + corner_normals[v1] * t1;
|
||||
|
||||
emit_vertex(primary, normal);
|
||||
}
|
||||
}
|
||||
|
||||
} // for each cell vertice
|
||||
|
||||
for (int t = 0; t < triangle_count; ++t) {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
int index = cell_mesh_indices[regular_cell_class.vertexIndex[t * 3 + i]];
|
||||
_output_indices.push_back(index);
|
||||
}
|
||||
}
|
||||
|
||||
} // x
|
||||
} // y
|
||||
} // z
|
||||
}
|
||||
|
||||
VoxelMesherMC::ReuseCell &VoxelMesherMC::get_reuse_cell(Vector3i pos) {
|
||||
// CRASH_COND(pos.x < 0);
|
||||
// CRASH_COND(pos.y < 0);
|
||||
// CRASH_COND(pos.z < 0);
|
||||
// CRASH_COND(pos.x >= _block_size.x);
|
||||
// CRASH_COND(pos.y >= _block_size.y);
|
||||
// CRASH_COND(pos.z >= _block_size.z);
|
||||
int j = pos.z & 1;
|
||||
int i = pos.y * _block_size.y + pos.x;
|
||||
return _cache[j][i];
|
||||
}
|
||||
|
||||
void VoxelMesherMC::emit_vertex(Vector3 primary, Vector3 normal) {
|
||||
_output_vertices.push_back(primary - Vector3(MIN_PADDING, MIN_PADDING, MIN_PADDING));
|
||||
_output_normals.push_back(normal);
|
||||
}
|
||||
|
||||
VoxelMesher *VoxelMesherMC::clone() {
|
||||
VoxelMesherMC *c = memnew(VoxelMesherMC);
|
||||
c->_seam_mode = _seam_mode;
|
||||
return c;
|
||||
}
|
||||
|
||||
void VoxelMesherMC::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_seam_mode", "mode"), &VoxelMesherMC::set_seam_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_seam_mode"), &VoxelMesherMC::get_seam_mode);
|
||||
|
||||
BIND_ENUM_CONSTANT(SEAM_NONE);
|
||||
BIND_ENUM_CONSTANT(SEAM_OVERLAP);
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
#ifndef VOXEL_MESHER_MC_H
|
||||
#define VOXEL_MESHER_MC_H
|
||||
|
||||
#include "../voxel_mesher.h"
|
||||
|
||||
// TODO Remove it.
|
||||
// Simple marching cubes.
|
||||
// Implementation is simplified from old Transvoxel code.
|
||||
class VoxelMesherMC : public VoxelMesher {
|
||||
GDCLASS(VoxelMesherMC, VoxelMesher)
|
||||
|
||||
public:
|
||||
static const int MIN_PADDING = 1;
|
||||
static const int MAX_PADDING = 2;
|
||||
|
||||
enum SeamMode {
|
||||
SEAM_NONE,
|
||||
SEAM_OVERLAP
|
||||
};
|
||||
|
||||
VoxelMesherMC();
|
||||
|
||||
void build(VoxelMesher::Output &output, const VoxelMesher::Input &input) override;
|
||||
|
||||
void set_seam_mode(SeamMode mode);
|
||||
SeamMode get_seam_mode() const;
|
||||
|
||||
VoxelMesher *clone() override;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
private:
|
||||
struct ReuseCell {
|
||||
int vertices[4] = { -1 };
|
||||
int case_index = 0;
|
||||
};
|
||||
|
||||
void build_internal(const VoxelBuffer &voxels, unsigned int channel);
|
||||
ReuseCell &get_reuse_cell(Vector3i pos);
|
||||
void emit_vertex(Vector3 primary, Vector3 normal);
|
||||
|
||||
private:
|
||||
std::vector<ReuseCell> _cache[2];
|
||||
Vector3i _block_size;
|
||||
SeamMode _seam_mode = SEAM_NONE;
|
||||
|
||||
std::vector<Vector3> _output_vertices;
|
||||
std::vector<Vector3> _output_normals;
|
||||
std::vector<int> _output_indices;
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(VoxelMesherMC::SeamMode)
|
||||
|
||||
#endif // VOXEL_MESHER_MC_H
|
@ -1,16 +1,16 @@
|
||||
#include "register_types.h"
|
||||
#include "edition/voxel_tool.h"
|
||||
#include "generators/voxel_generator_heightmap.h"
|
||||
#include "generators/voxel_generator_image.h"
|
||||
#include "generators/voxel_generator_noise.h"
|
||||
#include "generators/voxel_generator_noise_2d.h"
|
||||
#include "generators/voxel_generator_test.h"
|
||||
#include "meshers/blocky/voxel_mesher_blocky.h"
|
||||
#include "meshers/dmc/voxel_mesher_dmc.h"
|
||||
#include "meshers/mc/voxel_mesher_mc.h"
|
||||
#include "meshers/transvoxel/voxel_mesher_transvoxel.h"
|
||||
#include "streams/voxel_stream_block_files.h"
|
||||
#include "streams/voxel_stream_file.h"
|
||||
#include "streams/voxel_stream_heightmap.h"
|
||||
#include "streams/voxel_stream_image.h"
|
||||
#include "streams/voxel_stream_noise.h"
|
||||
#include "streams/voxel_stream_noise_2d.h"
|
||||
#include "streams/voxel_stream_region_files.h"
|
||||
#include "streams/voxel_stream_test.h"
|
||||
#include "terrain/voxel_box_mover.h"
|
||||
#include "terrain/voxel_lod_terrain.h"
|
||||
#include "terrain/voxel_map.h"
|
||||
@ -19,7 +19,6 @@
|
||||
#include "voxel_library.h"
|
||||
#include "voxel_memory_pool.h"
|
||||
#include "voxel_string_names.h"
|
||||
#include "voxel_tool.h"
|
||||
|
||||
void register_voxel_types() {
|
||||
|
||||
@ -37,15 +36,18 @@ void register_voxel_types() {
|
||||
|
||||
// Streams
|
||||
ClassDB::register_class<VoxelStream>();
|
||||
ClassDB::register_class<VoxelStreamTest>();
|
||||
ClassDB::register_class<VoxelStreamHeightmap>();
|
||||
ClassDB::register_class<VoxelStreamImage>();
|
||||
ClassDB::register_class<VoxelStreamNoise>();
|
||||
ClassDB::register_class<VoxelStreamNoise2D>();
|
||||
ClassDB::register_class<VoxelStreamFile>();
|
||||
ClassDB::register_class<VoxelStreamBlockFiles>();
|
||||
ClassDB::register_class<VoxelStreamRegionFiles>();
|
||||
|
||||
// Generators
|
||||
ClassDB::register_class<VoxelGenerator>();
|
||||
ClassDB::register_class<VoxelGeneratorTest>();
|
||||
ClassDB::register_class<VoxelGeneratorHeightmap>();
|
||||
ClassDB::register_class<VoxelGeneratorImage>();
|
||||
ClassDB::register_class<VoxelGeneratorNoise2D>();
|
||||
ClassDB::register_class<VoxelGeneratorNoise>();
|
||||
|
||||
// Helpers
|
||||
ClassDB::register_class<VoxelBoxMover>();
|
||||
ClassDB::register_class<VoxelRaycastResult>();
|
||||
@ -56,7 +58,6 @@ void register_voxel_types() {
|
||||
ClassDB::register_class<VoxelMesherBlocky>();
|
||||
ClassDB::register_class<VoxelMesherTransvoxel>();
|
||||
ClassDB::register_class<VoxelMesherDMC>();
|
||||
ClassDB::register_class<VoxelMesherMC>();
|
||||
|
||||
VoxelMemoryPool::create_singleton();
|
||||
VoxelStringNames::create_singleton();
|
||||
|
15
streams/voxel_block_request.h
Normal file
15
streams/voxel_block_request.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef VOXEL_BLOCK_REQUEST_H
|
||||
#define VOXEL_BLOCK_REQUEST_H
|
||||
|
||||
#include "../math/vector3i.h"
|
||||
#include "../voxel_buffer.h"
|
||||
|
||||
class VoxelBuffer;
|
||||
|
||||
struct VoxelBlockRequest {
|
||||
Ref<VoxelBuffer> voxel_buffer;
|
||||
Vector3i origin_in_voxels;
|
||||
int lod;
|
||||
};
|
||||
|
||||
#endif // VOXEL_BLOCK_REQUEST_H
|
@ -43,17 +43,17 @@ void VoxelStream::immerge_block(Ref<VoxelBuffer> buffer, Vector3i origin_in_voxe
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelStream::emerge_blocks(Vector<VoxelStream::BlockRequest> &p_blocks) {
|
||||
void VoxelStream::emerge_blocks(Vector<VoxelBlockRequest> &p_blocks) {
|
||||
// Default implementation. May matter for some stream types to optimize loading.
|
||||
for (int i = 0; i < p_blocks.size(); ++i) {
|
||||
BlockRequest &r = p_blocks.write[i];
|
||||
VoxelBlockRequest &r = p_blocks.write[i];
|
||||
emerge_block(r.voxel_buffer, r.origin_in_voxels, r.lod);
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelStream::immerge_blocks(Vector<VoxelStream::BlockRequest> &p_blocks) {
|
||||
void VoxelStream::immerge_blocks(Vector<VoxelBlockRequest> &p_blocks) {
|
||||
for (int i = 0; i < p_blocks.size(); ++i) {
|
||||
BlockRequest &r = p_blocks.write[i];
|
||||
VoxelBlockRequest &r = p_blocks.write[i];
|
||||
immerge_block(r.voxel_buffer, r.origin_in_voxels, r.lod);
|
||||
}
|
||||
}
|
||||
|
@ -2,20 +2,15 @@
|
||||
#define VOXEL_STREAM_H
|
||||
|
||||
#include "../util/zprofiling.h"
|
||||
#include "../voxel_buffer.h"
|
||||
#include "voxel_block_request.h"
|
||||
#include <core/resource.h>
|
||||
|
||||
// Provides access to a source of paged voxel data.
|
||||
// Provides access to a source of paged voxel data, which may load and save.
|
||||
// Must be implemented in a multi-thread-safe way.
|
||||
// If you are looking for a more specialized API to generate voxels, use VoxelGenerator.
|
||||
class VoxelStream : public Resource {
|
||||
GDCLASS(VoxelStream, Resource)
|
||||
public:
|
||||
struct BlockRequest {
|
||||
Ref<VoxelBuffer> voxel_buffer;
|
||||
Vector3i origin_in_voxels;
|
||||
int lod;
|
||||
};
|
||||
|
||||
struct Stats {
|
||||
int file_openings = 0;
|
||||
int time_spent_opening_files = 0;
|
||||
@ -31,12 +26,12 @@ public:
|
||||
virtual void immerge_block(Ref<VoxelBuffer> buffer, Vector3i origin_in_voxels, int lod);
|
||||
|
||||
// Note: vector is passed by ref for performance. Don't reorder it.
|
||||
virtual void emerge_blocks(Vector<BlockRequest> &p_blocks);
|
||||
virtual void emerge_blocks(Vector<VoxelBlockRequest> &p_blocks);
|
||||
|
||||
// Returns multiple blocks of voxels to the stream.
|
||||
// Generators usually don't implement it.
|
||||
// This function is recommended if you save to files, because you can batch their access.
|
||||
virtual void immerge_blocks(Vector<BlockRequest> &p_blocks);
|
||||
virtual void immerge_blocks(Vector<VoxelBlockRequest> &p_blocks);
|
||||
|
||||
virtual bool is_thread_safe() const;
|
||||
virtual bool is_cloneable() const;
|
||||
|
@ -23,18 +23,18 @@ void VoxelStreamFile::emerge_block_fallback(Ref<VoxelBuffer> out_buffer, Vector3
|
||||
|
||||
// This function is just a helper around the true thing, really. I might remove it in the future.
|
||||
|
||||
BlockRequest r;
|
||||
VoxelBlockRequest r;
|
||||
r.voxel_buffer = out_buffer;
|
||||
r.origin_in_voxels = origin_in_voxels;
|
||||
r.lod = lod;
|
||||
|
||||
Vector<BlockRequest> requests;
|
||||
Vector<VoxelBlockRequest> requests;
|
||||
requests.push_back(r);
|
||||
|
||||
emerge_blocks_fallback(requests);
|
||||
}
|
||||
|
||||
void VoxelStreamFile::emerge_blocks_fallback(Vector<VoxelStreamFile::BlockRequest> &requests) {
|
||||
void VoxelStreamFile::emerge_blocks_fallback(Vector<VoxelBlockRequest> &requests) {
|
||||
VOXEL_PROFILE_SCOPE(profile_scope);
|
||||
|
||||
if (_fallback_stream.is_valid()) {
|
||||
|
@ -28,7 +28,7 @@ protected:
|
||||
static void _bind_methods();
|
||||
|
||||
void emerge_block_fallback(Ref<VoxelBuffer> out_buffer, Vector3i origin_in_voxels, int lod);
|
||||
void emerge_blocks_fallback(Vector<BlockRequest> &requests);
|
||||
void emerge_blocks_fallback(Vector<VoxelBlockRequest> &requests);
|
||||
|
||||
FileAccess *open_file(const String &fpath, int mode_flags, Error *err);
|
||||
|
||||
|
@ -1,70 +0,0 @@
|
||||
#include "voxel_stream_heightmap.h"
|
||||
#include "../util/array_slice.h"
|
||||
#include "../util/fixed_array.h"
|
||||
|
||||
VoxelStreamHeightmap::VoxelStreamHeightmap() {
|
||||
_heightmap.settings.range.base = -50.0;
|
||||
_heightmap.settings.range.span = 200.0;
|
||||
_heightmap.settings.mode = HeightmapSdf::SDF_VERTICAL_AVERAGE;
|
||||
}
|
||||
|
||||
void VoxelStreamHeightmap::set_channel(VoxelBuffer::ChannelId channel) {
|
||||
ERR_FAIL_INDEX(channel, VoxelBuffer::MAX_CHANNELS);
|
||||
_channel = channel;
|
||||
if (_channel != VoxelBuffer::CHANNEL_SDF) {
|
||||
_heightmap.clear_cache();
|
||||
}
|
||||
}
|
||||
|
||||
VoxelBuffer::ChannelId VoxelStreamHeightmap::get_channel() const {
|
||||
return _channel;
|
||||
}
|
||||
|
||||
void VoxelStreamHeightmap::set_sdf_mode(SdfMode mode) {
|
||||
ERR_FAIL_INDEX(mode, SDF_MODE_COUNT);
|
||||
_heightmap.settings.mode = (HeightmapSdf::Mode)mode;
|
||||
}
|
||||
|
||||
VoxelStreamHeightmap::SdfMode VoxelStreamHeightmap::get_sdf_mode() const {
|
||||
return (VoxelStreamHeightmap::SdfMode)_heightmap.settings.mode;
|
||||
}
|
||||
|
||||
void VoxelStreamHeightmap::set_height_start(float start) {
|
||||
_heightmap.settings.range.base = start;
|
||||
}
|
||||
|
||||
float VoxelStreamHeightmap::get_height_start() const {
|
||||
return _heightmap.settings.range.base;
|
||||
}
|
||||
|
||||
void VoxelStreamHeightmap::set_height_range(float range) {
|
||||
_heightmap.settings.range.span = range;
|
||||
}
|
||||
|
||||
float VoxelStreamHeightmap::get_height_range() const {
|
||||
return _heightmap.settings.range.span;
|
||||
}
|
||||
|
||||
void VoxelStreamHeightmap::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_channel", "channel"), &VoxelStreamHeightmap::set_channel);
|
||||
ClassDB::bind_method(D_METHOD("get_channel"), &VoxelStreamHeightmap::get_channel);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_sdf_mode", "mode"), &VoxelStreamHeightmap::set_sdf_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_sdf_mode"), &VoxelStreamHeightmap::get_sdf_mode);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_height_start", "start"), &VoxelStreamHeightmap::set_height_start);
|
||||
ClassDB::bind_method(D_METHOD("get_height_start"), &VoxelStreamHeightmap::get_height_start);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_height_range", "range"), &VoxelStreamHeightmap::set_height_range);
|
||||
ClassDB::bind_method(D_METHOD("get_height_range"), &VoxelStreamHeightmap::get_height_range);
|
||||
|
||||
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, HeightmapSdf::MODE_HINT_STRING), "set_sdf_mode", "get_sdf_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "height_start"), "set_height_start", "get_height_start");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "height_range"), "set_height_range", "get_height_range");
|
||||
|
||||
BIND_ENUM_CONSTANT(SDF_VERTICAL);
|
||||
BIND_ENUM_CONSTANT(SDF_VERTICAL_AVERAGE);
|
||||
BIND_ENUM_CONSTANT(SDF_SEGMENT);
|
||||
}
|
@ -1,117 +0,0 @@
|
||||
#ifndef VOXEL_STREAM_HEIGHTMAP_H
|
||||
#define VOXEL_STREAM_HEIGHTMAP_H
|
||||
|
||||
#include "../util/heightmap_sdf.h"
|
||||
#include "voxel_stream.h"
|
||||
#include <core/image.h>
|
||||
|
||||
class VoxelStreamHeightmap : public VoxelStream {
|
||||
GDCLASS(VoxelStreamHeightmap, VoxelStream)
|
||||
public:
|
||||
VoxelStreamHeightmap();
|
||||
|
||||
enum SdfMode {
|
||||
SDF_VERTICAL = HeightmapSdf::SDF_VERTICAL,
|
||||
SDF_VERTICAL_AVERAGE = HeightmapSdf::SDF_VERTICAL_AVERAGE,
|
||||
SDF_SEGMENT = HeightmapSdf::SDF_SEGMENT,
|
||||
SDF_MODE_COUNT = HeightmapSdf::SDF_MODE_COUNT
|
||||
};
|
||||
|
||||
void set_channel(VoxelBuffer::ChannelId channel);
|
||||
VoxelBuffer::ChannelId get_channel() const;
|
||||
|
||||
void set_sdf_mode(SdfMode mode);
|
||||
SdfMode get_sdf_mode() const;
|
||||
|
||||
void set_height_start(float start);
|
||||
float get_height_start() const;
|
||||
|
||||
void set_height_range(float range);
|
||||
float get_height_range() const;
|
||||
|
||||
protected:
|
||||
template <typename Height_F>
|
||||
void generate(VoxelBuffer &out_buffer, Height_F height_func, int ox, int oy, int oz, int lod) {
|
||||
|
||||
const int channel = _channel;
|
||||
const Vector3i bs = out_buffer.get_size();
|
||||
bool use_sdf = channel == VoxelBuffer::CHANNEL_SDF;
|
||||
|
||||
if (oy > get_height_start() + get_height_range()) {
|
||||
// The bottom of the block is above the highest ground can go (default is air)
|
||||
return;
|
||||
}
|
||||
if (oy + (bs.y << lod) < get_height_start()) {
|
||||
// The top of the block is below the lowest ground can go
|
||||
out_buffer.clear_channel(_channel, use_sdf ? 0 : _matter_type);
|
||||
return;
|
||||
}
|
||||
|
||||
const int stride = 1 << lod;
|
||||
|
||||
if (use_sdf) {
|
||||
|
||||
if (lod == 0) {
|
||||
// When sampling SDF, we may need to precompute values to speed it up depending on the chosen mode.
|
||||
// Unfortunately, only LOD0 can use a cache. lower lods would require a much larger one,
|
||||
// otherwise it would interpolate along higher stride, thus voxel values depend on LOD, and then cause discontinuities.
|
||||
_heightmap.build_cache(height_func, bs.x, bs.z, ox, oz, stride);
|
||||
}
|
||||
|
||||
for (int z = 0; z < bs.z; ++z) {
|
||||
for (int x = 0; x < bs.x; ++x) {
|
||||
|
||||
// SDF may vary along the column so we use a helper for more precision
|
||||
|
||||
if (lod == 0) {
|
||||
_heightmap.get_column_from_cache(
|
||||
[&out_buffer, x, z, channel](int ly, float v) { out_buffer.set_voxel_f(v, x, ly, z, channel); },
|
||||
x, oy, z, bs.y, stride);
|
||||
} else {
|
||||
HeightmapSdf::get_column_stateless(
|
||||
[&out_buffer, x, z, channel](int ly, float v) { out_buffer.set_voxel_f(v, x, ly, z, channel); },
|
||||
[&height_func, this](int lx, int lz) { return _heightmap.settings.range.xform(height_func(lx, lz)); },
|
||||
_heightmap.settings.mode,
|
||||
ox + (x << lod), oy, oz + (z << lod), stride, bs.y);
|
||||
}
|
||||
|
||||
} // for x
|
||||
} // for z
|
||||
|
||||
} else {
|
||||
// Blocky
|
||||
|
||||
int gz = oz;
|
||||
for (int z = 0; z < bs.z; ++z, gz += stride) {
|
||||
|
||||
int gx = ox;
|
||||
for (int x = 0; x < bs.x; ++x, gx += stride) {
|
||||
|
||||
// Output is blocky, so we can go for just one sample
|
||||
float h = _heightmap.settings.range.xform(height_func(gx, gz));
|
||||
h -= oy;
|
||||
int ih = int(h);
|
||||
if (ih > 0) {
|
||||
if (ih > bs.y) {
|
||||
ih = bs.y;
|
||||
}
|
||||
out_buffer.fill_area(_matter_type, Vector3i(x, 0, z), Vector3i(x + 1, ih, z + 1), channel);
|
||||
}
|
||||
|
||||
} // for x
|
||||
} // for z
|
||||
} // use_sdf
|
||||
}
|
||||
|
||||
private:
|
||||
static void _bind_methods();
|
||||
|
||||
private:
|
||||
HeightmapSdf _heightmap;
|
||||
VoxelBuffer::ChannelId _channel = VoxelBuffer::CHANNEL_TYPE;
|
||||
int _matter_type = 1;
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(VoxelStreamHeightmap::SdfMode)
|
||||
|
||||
#endif // VOXEL_STREAM_HEIGHTMAP_H
|
@ -1,48 +0,0 @@
|
||||
#include "voxel_stream_image.h"
|
||||
#include "../util/array_slice.h"
|
||||
#include "../util/fixed_array.h"
|
||||
|
||||
namespace {
|
||||
|
||||
inline float get_height_repeat(Image &im, int x, int y) {
|
||||
return im.get_pixel(wrap(x, im.get_width()), wrap(y, im.get_height())).r;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
VoxelStreamImage::VoxelStreamImage() {
|
||||
}
|
||||
|
||||
void VoxelStreamImage::set_image(Ref<Image> im) {
|
||||
_image = im;
|
||||
}
|
||||
|
||||
Ref<Image> VoxelStreamImage::get_image() const {
|
||||
return _image;
|
||||
}
|
||||
|
||||
void VoxelStreamImage::emerge_block(Ref<VoxelBuffer> p_out_buffer, Vector3i origin_in_voxels, int lod) {
|
||||
|
||||
ERR_FAIL_COND(_image.is_null());
|
||||
|
||||
VoxelBuffer &out_buffer = **p_out_buffer;
|
||||
Image &image = **_image;
|
||||
|
||||
image.lock();
|
||||
|
||||
VoxelStreamHeightmap::generate(out_buffer,
|
||||
[&image](int x, int z) { return get_height_repeat(image, x, z); },
|
||||
origin_in_voxels.x, origin_in_voxels.y, origin_in_voxels.z, lod);
|
||||
|
||||
image.unlock();
|
||||
|
||||
out_buffer.compress_uniform_channels();
|
||||
}
|
||||
|
||||
void VoxelStreamImage::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_image", "image"), &VoxelStreamImage::set_image);
|
||||
ClassDB::bind_method(D_METHOD("get_image"), &VoxelStreamImage::get_image);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "image", PROPERTY_HINT_RESOURCE_TYPE, "Image"), "set_image", "get_image");
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
#ifndef HEADER_VOXEL_STREAM_IMAGE
|
||||
#define HEADER_VOXEL_STREAM_IMAGE
|
||||
|
||||
#include "voxel_stream_heightmap.h"
|
||||
#include <core/image.h>
|
||||
|
||||
// Provides infinite tiling heightmap based on an image
|
||||
class VoxelStreamImage : public VoxelStreamHeightmap {
|
||||
GDCLASS(VoxelStreamImage, VoxelStreamHeightmap)
|
||||
public:
|
||||
VoxelStreamImage();
|
||||
|
||||
void set_image(Ref<Image> im);
|
||||
Ref<Image> get_image() const;
|
||||
|
||||
void emerge_block(Ref<VoxelBuffer> p_out_buffer, Vector3i origin_in_voxels, int lod);
|
||||
|
||||
private:
|
||||
static void _bind_methods();
|
||||
|
||||
private:
|
||||
Ref<Image> _image;
|
||||
};
|
||||
|
||||
#endif // HEADER_VOXEL_STREAM_IMAGE
|
@ -1,53 +0,0 @@
|
||||
#include "voxel_stream_noise_2d.h"
|
||||
|
||||
VoxelStreamNoise2D::VoxelStreamNoise2D() {
|
||||
}
|
||||
|
||||
void VoxelStreamNoise2D::set_noise(Ref<OpenSimplexNoise> noise) {
|
||||
_noise = noise;
|
||||
}
|
||||
|
||||
Ref<OpenSimplexNoise> VoxelStreamNoise2D::get_noise() const {
|
||||
return _noise;
|
||||
}
|
||||
|
||||
void VoxelStreamNoise2D::set_curve(Ref<Curve> curve) {
|
||||
_curve = curve;
|
||||
}
|
||||
|
||||
Ref<Curve> VoxelStreamNoise2D::get_curve() const {
|
||||
return _curve;
|
||||
}
|
||||
|
||||
void VoxelStreamNoise2D::emerge_block(Ref<VoxelBuffer> p_out_buffer, Vector3i origin_in_voxels, int lod) {
|
||||
|
||||
ERR_FAIL_COND(_noise.is_null());
|
||||
|
||||
VoxelBuffer &out_buffer = **p_out_buffer;
|
||||
OpenSimplexNoise &noise = **_noise;
|
||||
|
||||
if (_curve.is_null()) {
|
||||
VoxelStreamHeightmap::generate(out_buffer,
|
||||
[&noise](int x, int z) { return 0.5 + 0.5 * noise.get_noise_2d(x, z); },
|
||||
origin_in_voxels.x, origin_in_voxels.y, origin_in_voxels.z, lod);
|
||||
} else {
|
||||
Curve &curve = **_curve;
|
||||
VoxelStreamHeightmap::generate(out_buffer,
|
||||
[&noise, &curve](int x, int z) { return curve.interpolate_baked(0.5 + 0.5 * noise.get_noise_2d(x, z)); },
|
||||
origin_in_voxels.x, origin_in_voxels.y, origin_in_voxels.z, lod);
|
||||
}
|
||||
|
||||
out_buffer.compress_uniform_channels();
|
||||
}
|
||||
|
||||
void VoxelStreamNoise2D::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_noise", "noise"), &VoxelStreamNoise2D::set_noise);
|
||||
ClassDB::bind_method(D_METHOD("get_noise"), &VoxelStreamNoise2D::get_noise);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_curve", "curve"), &VoxelStreamNoise2D::set_curve);
|
||||
ClassDB::bind_method(D_METHOD("get_curve"), &VoxelStreamNoise2D::get_curve);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "noise", PROPERTY_HINT_RESOURCE_TYPE, "OpenSimplexNoise"), "set_noise", "get_noise");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve");
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
#ifndef VOXEL_STREAM_NOISE_2D_H
|
||||
#define VOXEL_STREAM_NOISE_2D_H
|
||||
|
||||
#include "voxel_stream_heightmap.h"
|
||||
#include <modules/opensimplex/open_simplex_noise.h>
|
||||
|
||||
class VoxelStreamNoise2D : public VoxelStreamHeightmap {
|
||||
GDCLASS(VoxelStreamNoise2D, VoxelStreamHeightmap)
|
||||
public:
|
||||
VoxelStreamNoise2D();
|
||||
|
||||
void set_noise(Ref<OpenSimplexNoise> noise);
|
||||
Ref<OpenSimplexNoise> get_noise() const;
|
||||
|
||||
void set_curve(Ref<Curve> curve);
|
||||
Ref<Curve> get_curve() const;
|
||||
|
||||
void emerge_block(Ref<VoxelBuffer> p_out_buffer, Vector3i origin_in_voxels, int lod);
|
||||
|
||||
private:
|
||||
static void _bind_methods();
|
||||
|
||||
private:
|
||||
Ref<OpenSimplexNoise> _noise;
|
||||
Ref<Curve> _curve;
|
||||
};
|
||||
|
||||
#endif // VOXEL_STREAM_NOISE_2D_H
|
@ -32,42 +32,42 @@ VoxelStreamRegionFiles::~VoxelStreamRegionFiles() {
|
||||
}
|
||||
|
||||
void VoxelStreamRegionFiles::emerge_block(Ref<VoxelBuffer> out_buffer, Vector3i origin_in_voxels, int lod) {
|
||||
BlockRequest r;
|
||||
VoxelBlockRequest r;
|
||||
r.voxel_buffer = out_buffer;
|
||||
r.origin_in_voxels = origin_in_voxels;
|
||||
r.lod = lod;
|
||||
Vector<BlockRequest> requests;
|
||||
Vector<VoxelBlockRequest> requests;
|
||||
requests.push_back(r);
|
||||
emerge_blocks(requests);
|
||||
}
|
||||
|
||||
void VoxelStreamRegionFiles::immerge_block(Ref<VoxelBuffer> buffer, Vector3i origin_in_voxels, int lod) {
|
||||
BlockRequest r;
|
||||
VoxelBlockRequest r;
|
||||
r.voxel_buffer = buffer;
|
||||
r.origin_in_voxels = origin_in_voxels;
|
||||
r.lod = lod;
|
||||
Vector<BlockRequest> requests;
|
||||
Vector<VoxelBlockRequest> requests;
|
||||
requests.push_back(r);
|
||||
immerge_blocks(requests);
|
||||
}
|
||||
|
||||
void VoxelStreamRegionFiles::emerge_blocks(Vector<BlockRequest> &p_blocks) {
|
||||
void VoxelStreamRegionFiles::emerge_blocks(Vector<VoxelBlockRequest> &p_blocks) {
|
||||
VOXEL_PROFILE_SCOPE(profile_scope);
|
||||
|
||||
// In order to minimize opening/closing files, requests are grouped according to their region.
|
||||
|
||||
// Had to copy input to sort it, as some areas in the module break if they get responses in different order
|
||||
Vector<BlockRequest> sorted_blocks;
|
||||
Vector<VoxelBlockRequest> sorted_blocks;
|
||||
sorted_blocks.append_array(p_blocks);
|
||||
|
||||
SortArray<BlockRequest, BlockRequestComparator> sorter;
|
||||
SortArray<VoxelBlockRequest, BlockRequestComparator> sorter;
|
||||
sorter.compare.self = this;
|
||||
sorter.sort(sorted_blocks.ptrw(), sorted_blocks.size());
|
||||
|
||||
Vector<BlockRequest> fallback_requests;
|
||||
Vector<VoxelBlockRequest> fallback_requests;
|
||||
|
||||
for (int i = 0; i < sorted_blocks.size(); ++i) {
|
||||
BlockRequest &r = sorted_blocks.write[i];
|
||||
VoxelBlockRequest &r = sorted_blocks.write[i];
|
||||
EmergeResult result = _emerge_block(r.voxel_buffer, r.origin_in_voxels, r.lod);
|
||||
if (result == EMERGE_OK_FALLBACK) {
|
||||
fallback_requests.push_back(r);
|
||||
@ -77,19 +77,19 @@ void VoxelStreamRegionFiles::emerge_blocks(Vector<BlockRequest> &p_blocks) {
|
||||
emerge_blocks_fallback(fallback_requests);
|
||||
}
|
||||
|
||||
void VoxelStreamRegionFiles::immerge_blocks(Vector<BlockRequest> &p_blocks) {
|
||||
void VoxelStreamRegionFiles::immerge_blocks(Vector<VoxelBlockRequest> &p_blocks) {
|
||||
VOXEL_PROFILE_SCOPE(profile_scope);
|
||||
|
||||
// Had to copy input to sort it, as some areas in the module break if they get responses in different order
|
||||
Vector<BlockRequest> sorted_blocks;
|
||||
Vector<VoxelBlockRequest> sorted_blocks;
|
||||
sorted_blocks.append_array(p_blocks);
|
||||
|
||||
SortArray<BlockRequest, BlockRequestComparator> sorter;
|
||||
SortArray<VoxelBlockRequest, BlockRequestComparator> sorter;
|
||||
sorter.compare.self = this;
|
||||
sorter.sort(sorted_blocks.ptrw(), sorted_blocks.size());
|
||||
|
||||
for (int i = 0; i < sorted_blocks.size(); ++i) {
|
||||
BlockRequest &r = sorted_blocks.write[i];
|
||||
VoxelBlockRequest &r = sorted_blocks.write[i];
|
||||
_immerge_block(r.voxel_buffer, r.origin_in_voxels, r.lod);
|
||||
}
|
||||
}
|
||||
|
@ -22,8 +22,8 @@ public:
|
||||
void emerge_block(Ref<VoxelBuffer> out_buffer, Vector3i origin_in_voxels, int lod) override;
|
||||
void immerge_block(Ref<VoxelBuffer> buffer, Vector3i origin_in_voxels, int lod) override;
|
||||
|
||||
void emerge_blocks(Vector<BlockRequest> &p_blocks) override;
|
||||
void immerge_blocks(Vector<BlockRequest> &p_blocks) override;
|
||||
void emerge_blocks(Vector<VoxelBlockRequest> &p_blocks) override;
|
||||
void immerge_blocks(Vector<VoxelBlockRequest> &p_blocks) override;
|
||||
|
||||
String get_directory() const;
|
||||
void set_directory(String dirpath);
|
||||
@ -96,7 +96,7 @@ private:
|
||||
VoxelStreamRegionFiles *self = nullptr;
|
||||
|
||||
// operator<
|
||||
_FORCE_INLINE_ bool operator()(const VoxelStreamRegionFiles::BlockRequest &a, const VoxelStreamRegionFiles::BlockRequest &b) const {
|
||||
_FORCE_INLINE_ bool operator()(const VoxelBlockRequest &a, const VoxelBlockRequest &b) const {
|
||||
if (a.lod < b.lod) {
|
||||
return true;
|
||||
} else if (a.lod > b.lod) {
|
||||
|
@ -58,8 +58,8 @@ void VoxelDataLoader::process_blocks_thread_func(const ArraySlice<InputBlock> in
|
||||
|
||||
CRASH_COND(inputs.size() != outputs.size());
|
||||
|
||||
Vector<VoxelStream::BlockRequest> emerge_requests;
|
||||
Vector<VoxelStream::BlockRequest> immerge_requests;
|
||||
Vector<VoxelBlockRequest> emerge_requests;
|
||||
Vector<VoxelBlockRequest> immerge_requests;
|
||||
|
||||
for (size_t i = 0; i < inputs.size(); ++i) {
|
||||
|
||||
@ -70,7 +70,7 @@ void VoxelDataLoader::process_blocks_thread_func(const ArraySlice<InputBlock> in
|
||||
|
||||
if (ib.data.voxels_to_save.is_null()) {
|
||||
|
||||
VoxelStream::BlockRequest r;
|
||||
VoxelBlockRequest r;
|
||||
r.voxel_buffer.instance();
|
||||
r.voxel_buffer->create(bs, bs, bs);
|
||||
r.origin_in_voxels = block_origin_in_voxels;
|
||||
@ -79,7 +79,7 @@ void VoxelDataLoader::process_blocks_thread_func(const ArraySlice<InputBlock> in
|
||||
|
||||
} else {
|
||||
|
||||
VoxelStream::BlockRequest r;
|
||||
VoxelBlockRequest r;
|
||||
r.voxel_buffer = ib.data.voxels_to_save;
|
||||
r.origin_in_voxels = block_origin_in_voxels;
|
||||
r.lod = ib.lod;
|
||||
|
@ -1,9 +1,9 @@
|
||||
#include "voxel_lod_terrain.h"
|
||||
#include "../edition/voxel_tool_lod_terrain.h"
|
||||
#include "../math/rect3i.h"
|
||||
#include "../streams/voxel_stream_file.h"
|
||||
#include "../util/profiling_clock.h"
|
||||
#include "../voxel_string_names.h"
|
||||
#include "../voxel_tool_lod_terrain.h"
|
||||
#include "voxel_map.h"
|
||||
|
||||
#include <core/core_string_names.h>
|
||||
|
@ -1,9 +1,8 @@
|
||||
#include "voxel_terrain.h"
|
||||
#include "../edition/voxel_tool_terrain.h"
|
||||
#include "../streams/voxel_stream_file.h"
|
||||
#include "../util/profiling_clock.h"
|
||||
#include "../util/utility.h"
|
||||
#include "../util/voxel_raycast.h"
|
||||
#include "../voxel_tool_terrain.h"
|
||||
#include "voxel_block.h"
|
||||
#include "voxel_map.h"
|
||||
|
||||
|
@ -52,7 +52,7 @@ public:
|
||||
bool is_smooth_meshing_enabled() const;
|
||||
void set_smooth_meshing_enabled(bool enabled);
|
||||
|
||||
Ref<VoxelMap> get_storage() { return _map; }
|
||||
Ref<VoxelMap> get_storage() const { return _map; }
|
||||
Ref<VoxelTool> get_voxel_tool();
|
||||
|
||||
struct Stats {
|
||||
|
@ -1,42 +0,0 @@
|
||||
#include "heightmap_sdf.h"
|
||||
|
||||
const char *HeightmapSdf::MODE_HINT_STRING = "Vertical,VerticalAverage,Segment";
|
||||
|
||||
float HeightmapSdf::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);
|
||||
}
|
@ -1,244 +0,0 @@
|
||||
#ifndef HEIGHTMAP_SDF_H
|
||||
#define HEIGHTMAP_SDF_H
|
||||
|
||||
#include "array_slice.h"
|
||||
#include <core/math/vector2.h>
|
||||
#include <vector>
|
||||
|
||||
// Utility class to sample a heightmap as a 3D distance field.
|
||||
// Provides a stateless function, or an accelerated area method using a cache.
|
||||
// Note: this isn't general-purpose, it has been made for several use cases found in this module.
|
||||
class HeightmapSdf {
|
||||
public:
|
||||
enum Mode {
|
||||
SDF_VERTICAL = 0, // Lowest quality, fastest
|
||||
SDF_VERTICAL_AVERAGE,
|
||||
SDF_SEGMENT,
|
||||
SDF_MODE_COUNT
|
||||
};
|
||||
|
||||
static const char *MODE_HINT_STRING;
|
||||
|
||||
struct Range {
|
||||
float base = -50;
|
||||
float span = 200;
|
||||
|
||||
inline float xform(float x) const {
|
||||
return x * span + base;
|
||||
}
|
||||
};
|
||||
|
||||
struct Settings {
|
||||
Mode mode = SDF_VERTICAL;
|
||||
Range range;
|
||||
};
|
||||
|
||||
struct Cache {
|
||||
std::vector<float> heights;
|
||||
int size_z = 0;
|
||||
|
||||
inline float get_local(int x, int z) const {
|
||||
const int i = x + z * size_z;
|
||||
#ifdef TOOLS_ENABLED
|
||||
CRASH_COND(i >= heights.size());
|
||||
#endif
|
||||
return heights[i];
|
||||
}
|
||||
};
|
||||
|
||||
Settings settings;
|
||||
|
||||
// Precomputes data to accelerate the next area fetch.
|
||||
// ox, oz and stride are in world space.
|
||||
// Coordinates sent to the height function are in world space.
|
||||
template <typename Height_F>
|
||||
void build_cache(Height_F height_func, int cache_size_x, int cache_size_z, int ox, int oz, int stride) {
|
||||
|
||||
CRASH_COND(cache_size_x < 0);
|
||||
CRASH_COND(cache_size_z < 0);
|
||||
|
||||
if (settings.mode == SDF_SEGMENT) {
|
||||
// Pad
|
||||
cache_size_x += 2;
|
||||
cache_size_z += 2;
|
||||
ox -= stride;
|
||||
oz -= stride;
|
||||
}
|
||||
|
||||
unsigned int area = cache_size_x * cache_size_z;
|
||||
if (area != _cache.heights.size()) {
|
||||
_cache.heights.resize(area);
|
||||
}
|
||||
_cache.size_z = cache_size_z;
|
||||
|
||||
int i = 0;
|
||||
int gz = oz;
|
||||
|
||||
for (int z = 0; z < cache_size_z; ++z, gz += stride) {
|
||||
int gx = ox;
|
||||
|
||||
for (int x = 0; x < cache_size_x; ++x, gx += stride) {
|
||||
|
||||
switch (settings.mode) {
|
||||
|
||||
case SDF_VERTICAL:
|
||||
case SDF_SEGMENT:
|
||||
_cache.heights[i++] = settings.range.xform(height_func(gx, gz));
|
||||
break;
|
||||
|
||||
case SDF_VERTICAL_AVERAGE:
|
||||
_cache.heights[i++] = settings.range.xform(get_height_blurred(height_func, gx, gz));
|
||||
break;
|
||||
|
||||
default:
|
||||
CRASH_NOW();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clear_cache() {
|
||||
_cache.heights.clear();
|
||||
}
|
||||
|
||||
// Core functionality is here.
|
||||
// Slower than using a cache, but doesn't rely on heap memory.
|
||||
// fx and fz use the same coordinate space as the height function.
|
||||
// gy0 and stride are world space.
|
||||
// Coordinates sent to the output function are in grid space.
|
||||
template <typename Height_F, typename Output_F>
|
||||
static void get_column_stateless(Output_F output_func, Height_F height_func, Mode mode, int fx, int gy0, int fz, int stride, int size_y) {
|
||||
|
||||
switch (mode) {
|
||||
|
||||
case SDF_VERTICAL: {
|
||||
float h = height_func(fx, fz);
|
||||
int gy = gy0;
|
||||
for (int y = 0; y < size_y; ++y, gy += stride) {
|
||||
float sdf = gy - h;
|
||||
output_func(y, sdf);
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDF_VERTICAL_AVERAGE: {
|
||||
float h = get_height_blurred(height_func, fx, fz);
|
||||
int gy = gy0;
|
||||
for (int y = 0; y < size_y; ++y, gy += stride) {
|
||||
float sdf = gy - h;
|
||||
output_func(y, sdf);
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDF_SEGMENT: {
|
||||
// Calculate distance to 8 segments going from the point at XZ to its neighbor points,
|
||||
// and pick the smallest distance.
|
||||
// Note: stride is intentionally not used for neighbor sampling.
|
||||
// More than 1 isn't really supported, because it causes inconsistencies when nearest-neighbor LOD is used.
|
||||
|
||||
float h0 = height_func(fx - 1, fz - 1);
|
||||
float h1 = height_func(fx, fz - 1);
|
||||
float h2 = height_func(fx + 1, fz - 1);
|
||||
|
||||
float h3 = height_func(fx - 1, fz);
|
||||
float h4 = height_func(fx, fz);
|
||||
float h5 = height_func(fx + 1, fz);
|
||||
|
||||
float h6 = height_func(fx - 1, fz + 1);
|
||||
float h7 = height_func(fx, fz + 1);
|
||||
float h8 = height_func(fx + 1, fz + 1);
|
||||
|
||||
const float sqrt2 = 1.414213562373095;
|
||||
|
||||
int gy = gy0;
|
||||
for (int y = 0; y < size_y; ++y, gy += stride) {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
output_func(y, sdf);
|
||||
}
|
||||
} break;
|
||||
|
||||
default:
|
||||
CRASH_NOW();
|
||||
break;
|
||||
|
||||
} // sdf mode
|
||||
}
|
||||
|
||||
// Fastest if a cache has been built before. Prefer this when fetching areas.
|
||||
// Coordinates sent to the output function are in grid space.
|
||||
template <typename Output_F>
|
||||
inline void get_column_from_cache(Output_F output_func, int grid_x, int world_y0, int grid_z, int grid_size_y, int stride) {
|
||||
|
||||
Mode mode = settings.mode;
|
||||
|
||||
if (mode == SDF_VERTICAL_AVERAGE) {
|
||||
// Precomputed in cache, sample directly
|
||||
mode = SDF_VERTICAL;
|
||||
|
||||
} else if (mode == SDF_SEGMENT) {
|
||||
// Pad
|
||||
++grid_x;
|
||||
++grid_z;
|
||||
}
|
||||
|
||||
get_column_stateless(output_func,
|
||||
[&](int x, int z) { return _cache.get_local(x, z); },
|
||||
mode, grid_x, world_y0, grid_z, stride, grid_size_y);
|
||||
}
|
||||
|
||||
private:
|
||||
static float get_constrained_segment_sdf(float p_yp, float p_ya, float p_yb, float p_xb);
|
||||
|
||||
template <typename Height_F>
|
||||
static inline float get_height_blurred(Height_F height_func, int x, int y) {
|
||||
float h = height_func(x, y);
|
||||
h += height_func(x + 1, y);
|
||||
h += height_func(x - 1, y);
|
||||
h += height_func(x, y + 1);
|
||||
h += height_func(x, y - 1);
|
||||
return h * 0.2f;
|
||||
}
|
||||
|
||||
Cache _cache;
|
||||
};
|
||||
|
||||
#endif // HEIGHTMAP_SDF_H
|
@ -1,123 +0,0 @@
|
||||
#include "voxel_raycast.h"
|
||||
#include <core/math/math_funcs.h>
|
||||
|
||||
bool voxel_raycast(
|
||||
Vector3 ray_origin,
|
||||
Vector3 ray_direction,
|
||||
VoxelPredicate predicate,
|
||||
void *predicate_context,
|
||||
real_t max_distance,
|
||||
Vector3i &out_hit_pos,
|
||||
Vector3i &out_prev_pos) {
|
||||
|
||||
const float g_infinite = 9999999;
|
||||
|
||||
// Equation : p + v*t
|
||||
// p : ray start position (ray.pos)
|
||||
// v : ray orientation vector (ray.dir)
|
||||
// t : parametric variable = a distance if v is normalized
|
||||
|
||||
// This raycasting technique is described here :
|
||||
// http://www.cse.yorku.ca/~amana/research/grid.pdf
|
||||
|
||||
// Note : the grid is assumed to have 1-unit square cells.
|
||||
|
||||
ERR_FAIL_COND_V(predicate == 0, false);
|
||||
ERR_FAIL_COND_V(ray_direction.is_normalized() == false, false); // Must be normalized
|
||||
|
||||
/* Initialisation */
|
||||
|
||||
// Voxel position
|
||||
Vector3i hit_pos(
|
||||
Math::floor(ray_origin.x),
|
||||
Math::floor(ray_origin.y),
|
||||
Math::floor(ray_origin.z));
|
||||
Vector3i hit_prev_pos = hit_pos;
|
||||
|
||||
// Voxel step
|
||||
const int xi_step = ray_direction.x > 0 ? 1 : ray_direction.x < 0 ? -1 : 0;
|
||||
const int yi_step = ray_direction.y > 0 ? 1 : ray_direction.y < 0 ? -1 : 0;
|
||||
const int zi_step = ray_direction.z > 0 ? 1 : ray_direction.z < 0 ? -1 : 0;
|
||||
|
||||
// Parametric voxel step
|
||||
const real_t tdelta_x = xi_step != 0 ? 1.f / Math::abs(ray_direction.x) : g_infinite;
|
||||
const real_t tdelta_y = yi_step != 0 ? 1.f / Math::abs(ray_direction.y) : g_infinite;
|
||||
const real_t tdelta_z = zi_step != 0 ? 1.f / Math::abs(ray_direction.z) : g_infinite;
|
||||
|
||||
// Parametric grid-cross
|
||||
real_t tcross_x; // At which value of T we will cross a vertical line?
|
||||
real_t tcross_y; // At which value of T we will cross a horizontal line?
|
||||
real_t tcross_z; // At which value of T we will cross a depth line?
|
||||
|
||||
// X initialization
|
||||
if (xi_step != 0) {
|
||||
if (xi_step == 1)
|
||||
tcross_x = (Math::ceil(ray_origin.x) - ray_origin.x) * tdelta_x;
|
||||
else
|
||||
tcross_x = (ray_origin.x - Math::floor(ray_origin.x)) * tdelta_x;
|
||||
} else
|
||||
tcross_x = g_infinite; // Will never cross on X
|
||||
|
||||
// Y initialization
|
||||
if (yi_step != 0) {
|
||||
if (yi_step == 1)
|
||||
tcross_y = (Math::ceil(ray_origin.y) - ray_origin.y) * tdelta_y;
|
||||
else
|
||||
tcross_y = (ray_origin.y - Math::floor(ray_origin.y)) * tdelta_y;
|
||||
} else
|
||||
tcross_y = g_infinite; // Will never cross on X
|
||||
|
||||
// Z initialization
|
||||
if (zi_step != 0) {
|
||||
if (zi_step == 1)
|
||||
tcross_z = (Math::ceil(ray_origin.z) - ray_origin.z) * tdelta_z;
|
||||
else
|
||||
tcross_z = (ray_origin.z - Math::floor(ray_origin.z)) * tdelta_z;
|
||||
} else
|
||||
tcross_z = g_infinite; // Will never cross on X
|
||||
|
||||
/* Iteration */
|
||||
|
||||
do {
|
||||
hit_prev_pos = hit_pos;
|
||||
if (tcross_x < tcross_y) {
|
||||
if (tcross_x < tcross_z) {
|
||||
// X collision
|
||||
//hit.prevPos.x = hit.pos.x;
|
||||
hit_pos.x += xi_step;
|
||||
if (tcross_x > max_distance)
|
||||
return false;
|
||||
tcross_x += tdelta_x;
|
||||
} else {
|
||||
// Z collision (duplicate code)
|
||||
//hit.prevPos.z = hit.pos.z;
|
||||
hit_pos.z += zi_step;
|
||||
if (tcross_z > max_distance)
|
||||
return false;
|
||||
tcross_z += tdelta_z;
|
||||
}
|
||||
} else {
|
||||
if (tcross_y < tcross_z) {
|
||||
// Y collision
|
||||
//hit.prevPos.y = hit.pos.y;
|
||||
hit_pos.y += yi_step;
|
||||
if (tcross_y > max_distance)
|
||||
return false;
|
||||
tcross_y += tdelta_y;
|
||||
} else {
|
||||
// Z collision (duplicate code)
|
||||
//hit.prevPos.z = hit.pos.z;
|
||||
hit_pos.z += zi_step;
|
||||
if (tcross_z > max_distance)
|
||||
return false;
|
||||
tcross_z += tdelta_z;
|
||||
}
|
||||
}
|
||||
|
||||
} while (!predicate(hit_pos, predicate_context));
|
||||
|
||||
out_hit_pos = hit_pos;
|
||||
out_prev_pos = hit_prev_pos;
|
||||
|
||||
return true;
|
||||
}
|
@ -1,16 +1,122 @@
|
||||
#include "../math/vector3i.h"
|
||||
#include <core/math/vector3.h>
|
||||
|
||||
// TODO that could be a template function
|
||||
// pos: voxel position
|
||||
// context: arguments to carry (as a lamdbda capture)
|
||||
typedef bool (*VoxelPredicate)(Vector3i pos, void *context);
|
||||
|
||||
template <typename Predicate_F> // f(Vector3i position) -> bool
|
||||
bool voxel_raycast(
|
||||
Vector3 ray_origin,
|
||||
Vector3 ray_direction,
|
||||
VoxelPredicate predicate,
|
||||
void *predicate_context, // Handle that one with care
|
||||
Predicate_F predicate,
|
||||
real_t max_distance,
|
||||
Vector3i &out_hit_pos,
|
||||
Vector3i &out_prev_pos);
|
||||
Vector3i &out_prev_pos) {
|
||||
|
||||
const float g_infinite = 9999999;
|
||||
|
||||
// Equation : p + v*t
|
||||
// p : ray start position (ray.pos)
|
||||
// v : ray orientation vector (ray.dir)
|
||||
// t : parametric variable = a distance if v is normalized
|
||||
|
||||
// This raycasting technique is described here :
|
||||
// http://www.cse.yorku.ca/~amana/research/grid.pdf
|
||||
|
||||
// Note : the grid is assumed to have 1-unit square cells.
|
||||
|
||||
ERR_FAIL_COND_V(ray_direction.is_normalized() == false, false); // Must be normalized
|
||||
|
||||
/* Initialisation */
|
||||
|
||||
// Voxel position
|
||||
Vector3i hit_pos(
|
||||
Math::floor(ray_origin.x),
|
||||
Math::floor(ray_origin.y),
|
||||
Math::floor(ray_origin.z));
|
||||
Vector3i hit_prev_pos = hit_pos;
|
||||
|
||||
// Voxel step
|
||||
const int xi_step = ray_direction.x > 0 ? 1 : ray_direction.x < 0 ? -1 : 0;
|
||||
const int yi_step = ray_direction.y > 0 ? 1 : ray_direction.y < 0 ? -1 : 0;
|
||||
const int zi_step = ray_direction.z > 0 ? 1 : ray_direction.z < 0 ? -1 : 0;
|
||||
|
||||
// Parametric voxel step
|
||||
const real_t tdelta_x = xi_step != 0 ? 1.f / Math::abs(ray_direction.x) : g_infinite;
|
||||
const real_t tdelta_y = yi_step != 0 ? 1.f / Math::abs(ray_direction.y) : g_infinite;
|
||||
const real_t tdelta_z = zi_step != 0 ? 1.f / Math::abs(ray_direction.z) : g_infinite;
|
||||
|
||||
// Parametric grid-cross
|
||||
real_t tcross_x; // At which value of T we will cross a vertical line?
|
||||
real_t tcross_y; // At which value of T we will cross a horizontal line?
|
||||
real_t tcross_z; // At which value of T we will cross a depth line?
|
||||
|
||||
// X initialization
|
||||
if (xi_step != 0) {
|
||||
if (xi_step == 1)
|
||||
tcross_x = (Math::ceil(ray_origin.x) - ray_origin.x) * tdelta_x;
|
||||
else
|
||||
tcross_x = (ray_origin.x - Math::floor(ray_origin.x)) * tdelta_x;
|
||||
} else
|
||||
tcross_x = g_infinite; // Will never cross on X
|
||||
|
||||
// Y initialization
|
||||
if (yi_step != 0) {
|
||||
if (yi_step == 1)
|
||||
tcross_y = (Math::ceil(ray_origin.y) - ray_origin.y) * tdelta_y;
|
||||
else
|
||||
tcross_y = (ray_origin.y - Math::floor(ray_origin.y)) * tdelta_y;
|
||||
} else
|
||||
tcross_y = g_infinite; // Will never cross on X
|
||||
|
||||
// Z initialization
|
||||
if (zi_step != 0) {
|
||||
if (zi_step == 1)
|
||||
tcross_z = (Math::ceil(ray_origin.z) - ray_origin.z) * tdelta_z;
|
||||
else
|
||||
tcross_z = (ray_origin.z - Math::floor(ray_origin.z)) * tdelta_z;
|
||||
} else
|
||||
tcross_z = g_infinite; // Will never cross on X
|
||||
|
||||
/* Iteration */
|
||||
|
||||
do {
|
||||
hit_prev_pos = hit_pos;
|
||||
if (tcross_x < tcross_y) {
|
||||
if (tcross_x < tcross_z) {
|
||||
// X collision
|
||||
//hit.prevPos.x = hit.pos.x;
|
||||
hit_pos.x += xi_step;
|
||||
if (tcross_x > max_distance)
|
||||
return false;
|
||||
tcross_x += tdelta_x;
|
||||
} else {
|
||||
// Z collision (duplicate code)
|
||||
//hit.prevPos.z = hit.pos.z;
|
||||
hit_pos.z += zi_step;
|
||||
if (tcross_z > max_distance)
|
||||
return false;
|
||||
tcross_z += tdelta_z;
|
||||
}
|
||||
} else {
|
||||
if (tcross_y < tcross_z) {
|
||||
// Y collision
|
||||
//hit.prevPos.y = hit.pos.y;
|
||||
hit_pos.y += yi_step;
|
||||
if (tcross_y > max_distance)
|
||||
return false;
|
||||
tcross_y += tdelta_y;
|
||||
} else {
|
||||
// Z collision (duplicate code)
|
||||
//hit.prevPos.z = hit.pos.z;
|
||||
hit_pos.z += zi_step;
|
||||
if (tcross_z > max_distance)
|
||||
return false;
|
||||
tcross_z += tdelta_z;
|
||||
}
|
||||
}
|
||||
|
||||
} while (!predicate(hit_pos));
|
||||
|
||||
out_hit_pos = hit_pos;
|
||||
out_prev_pos = hit_prev_pos;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -4,8 +4,8 @@
|
||||
#include "voxel_memory_pool.h"
|
||||
#endif
|
||||
|
||||
#include "edition/voxel_tool_buffer.h"
|
||||
#include "voxel_buffer.h"
|
||||
#include "voxel_tool_buffer.h"
|
||||
|
||||
#include <core/io/marshalls.h>
|
||||
#include <core/math/math_funcs.h>
|
||||
|
@ -17,6 +17,7 @@ VoxelStringNames::VoxelStringNames() {
|
||||
|
||||
emerge_block = StaticCString::create("emerge_block");
|
||||
immerge_block = StaticCString::create("immerge_block");
|
||||
generate_block = StaticCString::create("generate_block");
|
||||
|
||||
u_transition_mask = StaticCString::create("u_transition_mask");
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ public:
|
||||
|
||||
StringName emerge_block;
|
||||
StringName immerge_block;
|
||||
StringName generate_block;
|
||||
|
||||
StringName u_transition_mask;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user