Started terrain editor
This commit is contained in:
@@ -2,44 +2,68 @@
|
||||
#include <modules/voxel/util/fixed_array.h>
|
||||
#include "modules/voxel/util/span.h"
|
||||
|
||||
namespace {
|
||||
namespace
|
||||
{
|
||||
|
||||
inline float get_height_repeat(const Image &im, int x, int y) {
|
||||
int xx = (float)x * 0.1f + im.get_width() / 2;
|
||||
int yy = (float)y * 0.1f + im.get_height() / 2;
|
||||
float r = im.get_pixel(wrap(xx, im.get_width()), wrap(yy, im.get_height())).r;
|
||||
float px = (r - 0.5) * 2.0f;
|
||||
float s = (px >= 0.0f) ? 1.0f : -1.0f;
|
||||
float m = px * px * s * 0.5f;
|
||||
return m + 0.5f;
|
||||
}
|
||||
#if 0
|
||||
inline float get_height_repeat(const Image &im, int x, int y)
|
||||
{
|
||||
int xx = (float)x * 0.1f + im.get_width() / 2;
|
||||
int yy = (float)y * 0.1f + im.get_height() / 2;
|
||||
float r = im.get_pixel(wrap(xx, im.get_width()), wrap(yy, im.get_height())).r;
|
||||
float px = (r - 0.5) * 2.0f;
|
||||
float s = (px >= 0.0f) ? 1.0f : -1.0f;
|
||||
float m = px * px * s * 0.5f;
|
||||
return m + 0.5f;
|
||||
}
|
||||
|
||||
inline float get_height_blurred(const Image &im, int x, int y) {
|
||||
int xx = x;
|
||||
int yy = y;
|
||||
float h = 0.0f;
|
||||
int i, j, count = 0;
|
||||
for (i = -24; i < 25; i += 2)
|
||||
for (j = -24; j < 25; j += 2) {
|
||||
h += get_height_repeat(im, xx + j, yy + i);
|
||||
count += 1;
|
||||
}
|
||||
return h / (float)count;
|
||||
}
|
||||
inline float get_height_blurred(const Image &im, int x, int y)
|
||||
{
|
||||
int xx = x;
|
||||
int yy = y;
|
||||
float h = 0.0f;
|
||||
int i, j, count = 0;
|
||||
for (i = -24; i < 25; i += 2)
|
||||
for (j = -24; j < 25; j += 2)
|
||||
{
|
||||
h += get_height_repeat(im, xx + j, yy + i);
|
||||
count += 1;
|
||||
}
|
||||
return h / (float)count;
|
||||
}
|
||||
#endif
|
||||
inline float get_height_linear_img(const Image &im, int x, int y)
|
||||
{
|
||||
int xx = x + im.get_width() / 2;
|
||||
int yy = y + im.get_height() / 2;
|
||||
float r = im.get_pixel(wrap(xx, im.get_width()), wrap(yy, im.get_height())).r;
|
||||
float px = (r - 0.5) * 2.0f;
|
||||
float s = (px >= 0.0f) ? 1.0f : -1.0f;
|
||||
float m = px * px * s * 0.5f;
|
||||
return m + 0.5f;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
VoxelGeneratorImgMapper::VoxelGeneratorImgMapper() {
|
||||
VoxelGeneratorImgMapper::VoxelGeneratorImgMapper()
|
||||
: VoxelGeneratorHeightmap(),
|
||||
world_size(10240),
|
||||
grid_size(0)
|
||||
{
|
||||
}
|
||||
|
||||
VoxelGeneratorImgMapper::~VoxelGeneratorImgMapper() {
|
||||
if (_parameters.image.is_valid()) {
|
||||
VoxelGeneratorImgMapper::~VoxelGeneratorImgMapper()
|
||||
{
|
||||
if (_parameters.image.is_valid())
|
||||
{
|
||||
_parameters.image->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelGeneratorImgMapper::set_image_bg(Ref<Image> im) {
|
||||
if (im == _image_bg) {
|
||||
void VoxelGeneratorImgMapper::set_image_bg(Ref<Image> im)
|
||||
{
|
||||
if (im == _image_bg)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!im.is_valid())
|
||||
@@ -48,60 +72,70 @@ void VoxelGeneratorImgMapper::set_image_bg(Ref<Image> im) {
|
||||
compose();
|
||||
}
|
||||
|
||||
Ref<Image> VoxelGeneratorImgMapper::get_image_bg() const {
|
||||
Ref<Image> VoxelGeneratorImgMapper::get_image_bg() const
|
||||
{
|
||||
return _image_bg;
|
||||
}
|
||||
|
||||
void VoxelGeneratorImgMapper::set_image_overlay(Ref<Image> im)
|
||||
{
|
||||
if (im == _image_overlay || !im.is_valid()) {
|
||||
if (im == _image_overlay || !im.is_valid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
_image_overlay = im;
|
||||
compose();
|
||||
}
|
||||
|
||||
Ref<Image> VoxelGeneratorImgMapper::get_image_overlay() const {
|
||||
Ref<Image> VoxelGeneratorImgMapper::get_image_overlay() const
|
||||
{
|
||||
return _image_overlay;
|
||||
}
|
||||
|
||||
void VoxelGeneratorImgMapper::set_image_buildings(Ref<Image> im)
|
||||
{
|
||||
if (im == _image_buildings || !im.is_valid()) {
|
||||
if (im == _image_buildings || !im.is_valid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
_image_buildings = im;
|
||||
compose();
|
||||
}
|
||||
|
||||
Ref<Image> VoxelGeneratorImgMapper::get_image_buildings() const {
|
||||
Ref<Image> VoxelGeneratorImgMapper::get_image_buildings() const
|
||||
{
|
||||
return _image_buildings;
|
||||
}
|
||||
|
||||
void VoxelGeneratorImgMapper::set_image_draw(Ref<Image> im)
|
||||
{
|
||||
if (im == _image_draw || !im.is_valid()) {
|
||||
if (im == _image_draw || !im.is_valid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
_image_draw = im;
|
||||
compose();
|
||||
}
|
||||
|
||||
Ref<Image> VoxelGeneratorImgMapper::get_image_draw() const {
|
||||
Ref<Image> VoxelGeneratorImgMapper::get_image_draw() const
|
||||
{
|
||||
return _image_draw;
|
||||
}
|
||||
|
||||
void VoxelGeneratorImgMapper::set_blur_enabled(bool enable) {
|
||||
void VoxelGeneratorImgMapper::set_blur_enabled(bool enable)
|
||||
{
|
||||
RWLockWrite wlock(_parameters_lock);
|
||||
_parameters.blur_enabled = enable;
|
||||
}
|
||||
|
||||
bool VoxelGeneratorImgMapper::is_blur_enabled() const {
|
||||
bool VoxelGeneratorImgMapper::is_blur_enabled() const
|
||||
{
|
||||
RWLockRead rlock(_parameters_lock);
|
||||
return _parameters.blur_enabled;
|
||||
}
|
||||
|
||||
VoxelGenerator::Result VoxelGeneratorImgMapper::generate_block(VoxelBlockRequest &input) {
|
||||
VoxelGenerator::Result VoxelGeneratorImgMapper::generate_block(VoxelBlockRequest &input)
|
||||
{
|
||||
VoxelBufferInternal &out_buffer = input.voxel_buffer;
|
||||
|
||||
Parameters params;
|
||||
@@ -117,16 +151,12 @@ VoxelGenerator::Result VoxelGeneratorImgMapper::generate_block(VoxelBlockRequest
|
||||
ERR_FAIL_COND_V(params.image->get_height() == 0, result);
|
||||
const Image &image = **params.image;
|
||||
|
||||
if (params.blur_enabled) {
|
||||
{
|
||||
result = VoxelGeneratorHeightmap::generate(
|
||||
out_buffer,
|
||||
[&image](int x, int z) { return get_height_blurred(image, x, z); },
|
||||
input.origin_in_voxels, input.lod);
|
||||
} else {
|
||||
result = VoxelGeneratorHeightmap::generate(
|
||||
out_buffer,
|
||||
[&image](int x, int z) { return get_height_repeat(image, x, z); },
|
||||
input.origin_in_voxels, input.lod);
|
||||
out_buffer,
|
||||
[this](int x, int z)
|
||||
{ return get_height_linear(Vector3(x, 0, z)); },
|
||||
input.origin_in_voxels, input.lod);
|
||||
}
|
||||
|
||||
out_buffer.compress_uniform_channels();
|
||||
@@ -139,7 +169,7 @@ void VoxelGeneratorImgMapper::set_height(const Vector3 &v, float height)
|
||||
float h = CLAMP(height, -100.0f, 100.0f);
|
||||
float c = (h + 100.0f) / 200.0;
|
||||
Vector2 pt = Vector2(v.x * 0.1f + _image_overlay->get_width() / 2,
|
||||
v.z * 0.1f + _image_overlay->get_height() / 2);
|
||||
v.z * 0.1f + _image_overlay->get_height() / 2);
|
||||
pt.x = CLAMP(pt.x, 1, _image_overlay->get_width() - 1);
|
||||
pt.y = CLAMP(pt.y, 1, _image_overlay->get_height() - 1);
|
||||
_image_overlay->fill_rect(Rect2(pt - Vector2(3, 3), Vector2(6, 6)), Color(c, 0, 0, 1));
|
||||
@@ -147,10 +177,37 @@ void VoxelGeneratorImgMapper::set_height(const Vector3 &v, float height)
|
||||
|
||||
float VoxelGeneratorImgMapper::get_height(const Vector3 &v)
|
||||
{
|
||||
#if 0
|
||||
if (_parameters.blur_enabled)
|
||||
return get_height_blurred(**_parameters.image, v.x, v.z);
|
||||
else
|
||||
return get_height_repeat(**_parameters.image, v.x, v.z);
|
||||
#endif
|
||||
return get_height_linear(v);
|
||||
}
|
||||
|
||||
float VoxelGeneratorImgMapper::get_height_linear(const Vector3 &v)
|
||||
{
|
||||
int px = (int)(v.x / grid_size);
|
||||
int pz = (int)(v.z / grid_size);
|
||||
float mx = (float)px * grid_size;
|
||||
float mz = (float)pz * grid_size;
|
||||
// float Mx = mx + (float)grid_size;
|
||||
// float Mz = mz + (float)grid_size;
|
||||
float x_weight = (v.x - mx) / (float)grid_size;
|
||||
float z_weight = (v.z - mz) / (float)grid_size;
|
||||
// float d0x = (v.x - mx) / (float)grid_size;
|
||||
// float d0z = (v.z - mz) / (float)grid_size;
|
||||
// float d1x = (mx + (float)grid_size - v.x) / (float)grid_size;
|
||||
// float d1z = (mz + (float)grid_size - v.z) / (float)grid_size;
|
||||
float p0 = get_height_linear_img(**_parameters.image, px, pz);
|
||||
float p1 = get_height_linear_img(**_parameters.image, px + 1, pz);
|
||||
float p2 = get_height_linear_img(**_parameters.image, px, pz + 1);
|
||||
float p3 = get_height_linear_img(**_parameters.image, px + 1, pz + 1);
|
||||
float a = (1.0f - x_weight);
|
||||
float b = (1.0f - z_weight);
|
||||
float result = p0 * a * b + p1 * x_weight * b + p2 * z_weight * a + p3 * x_weight * z_weight;
|
||||
return result;
|
||||
}
|
||||
|
||||
float VoxelGeneratorImgMapper::get_height_full(const Vector3 &v)
|
||||
@@ -158,26 +215,254 @@ float VoxelGeneratorImgMapper::get_height_full(const Vector3 &v)
|
||||
return get_height(v) * 200.0f - 100.0f;
|
||||
}
|
||||
|
||||
std::map<int, struct EditBrush *> EditBrush::brushes;
|
||||
struct Brush0 : EditBrush
|
||||
{
|
||||
Brush0(Ref<Image> &image) : EditBrush(0, image)
|
||||
{
|
||||
}
|
||||
void draw(const Vector3 &v, float r, float s)
|
||||
{
|
||||
int i, j;
|
||||
int xs = 3 * r;
|
||||
float c = 0.5f;
|
||||
for (i = -xs; i < xs + 1; i++)
|
||||
for (j = -xs; j < xs + 1; j++)
|
||||
{
|
||||
float xr = Vector2(i, j).length();
|
||||
if (xr < r)
|
||||
{
|
||||
Vector3 pos(v.x + i, v.y, v.z + j);
|
||||
Vector2 pt = Vector2(pos.x * 0.1f + draw_image->get_width() / 2,
|
||||
pos.z * 0.1f + draw_image->get_height() / 2);
|
||||
pt.x = CLAMP(pt.x, 1, draw_image->get_width() - 1);
|
||||
pt.y = CLAMP(pt.y, 1, draw_image->get_height() - 1);
|
||||
draw_image->fill_rect(Rect2(pt - Vector2(1, 1), Vector2(2, 2)), Color(c, 0, 0, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct Brush1 : EditBrush
|
||||
{
|
||||
Brush1(Ref<Image> &image) : EditBrush(1, image)
|
||||
{
|
||||
}
|
||||
void draw(const Vector3 &v, float r, float s)
|
||||
{
|
||||
int i, j;
|
||||
int xs = 3 * r;
|
||||
float c = 0.5f;
|
||||
/* erase stuff */
|
||||
for (i = -xs; i < xs + 1; i++)
|
||||
for (j = -xs; j < xs + 1; j++)
|
||||
{
|
||||
float xr = Vector2(i, j).length();
|
||||
if (xr < r)
|
||||
{
|
||||
Vector3 pos(v.x + i, v.y, v.z + j);
|
||||
Vector2 pt = Vector2(pos.x * 0.1f + draw_image->get_width() / 2,
|
||||
pos.z * 0.1f + draw_image->get_height() / 2);
|
||||
pt.x = CLAMP(pt.x, 1, draw_image->get_width() - 1);
|
||||
pt.y = CLAMP(pt.y, 1, draw_image->get_height() - 1);
|
||||
draw_image->fill_rect(Rect2(pt - Vector2(1, 1), Vector2(2, 2)), Color(c, 0, 0, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct Brush2 : EditBrush
|
||||
{
|
||||
Ref<Curve> curve;
|
||||
Brush2(Ref<Image> &image, Ref<Curve> &curve)
|
||||
: EditBrush(2, image), curve(curve)
|
||||
{
|
||||
}
|
||||
void draw(const Vector3 &v, float r, float s)
|
||||
{
|
||||
int i, j;
|
||||
int xs = 3 * r;
|
||||
float c = 0.5f;
|
||||
float scale = s / 100.0f;
|
||||
ERR_FAIL_COND(!curve.is_valid());
|
||||
for (i = -xs; i < xs + 1; i++)
|
||||
for (j = -xs; j < xs + 1; j++)
|
||||
{
|
||||
float xr = Vector2(i, j).length();
|
||||
if (xr < r)
|
||||
{
|
||||
float h = curve->interpolate_baked(xr / r) * scale;
|
||||
h = CLAMP(h, -100.0, 100.0f);
|
||||
c = (h + 100.0f) / 200.0;
|
||||
|
||||
Vector3 pos(v.x + i, v.y, v.z + j);
|
||||
Vector2 pt = Vector2(pos.x * 0.1f + draw_image->get_width() / 2,
|
||||
pos.z * 0.1f + draw_image->get_height() / 2);
|
||||
pt.x = CLAMP(pt.x, 1, draw_image->get_width() - 1);
|
||||
pt.y = CLAMP(pt.y, 1, draw_image->get_height() - 1);
|
||||
draw_image->fill_rect(Rect2(pt - Vector2(1, 1), Vector2(2, 2)), Color(c, 0, 0, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct Brush3 : EditBrush
|
||||
{
|
||||
Ref<Curve> curve;
|
||||
VoxelGeneratorImgMapper *imgmap;
|
||||
Brush3(Ref<Image> &image, Ref<Curve> &curve,
|
||||
VoxelGeneratorImgMapper *imgmap)
|
||||
: EditBrush(3, image), curve(curve), imgmap(imgmap)
|
||||
{
|
||||
}
|
||||
void draw(const Vector3 &v, float r, float s)
|
||||
{
|
||||
int i, j;
|
||||
int xs = 3 * r;
|
||||
float c = 0.5f;
|
||||
float scale = s / 100.0f;
|
||||
/* rel draw curve1 */
|
||||
ERR_FAIL_COND(!curve.is_valid());
|
||||
for (i = -xs; i < xs + 1; i++)
|
||||
for (j = -xs; j < xs + 1; j++)
|
||||
{
|
||||
float xr = Vector2(i, j).length();
|
||||
if (xr < r)
|
||||
{
|
||||
float h = curve->interpolate_baked(xr / r) * scale;
|
||||
if (h < -2.0f || h > 2.0f)
|
||||
{
|
||||
h += imgmap->get_height_full(v + Vector3(i, 0.0f, j));
|
||||
h = CLAMP(h, -100.0, 100.0f);
|
||||
c = (h + 100.0f) / 200.0;
|
||||
|
||||
Vector3 pos(v.x + i, v.y, v.z + j);
|
||||
Vector2 pt = Vector2(pos.x * 0.1f + draw_image->get_width() / 2,
|
||||
pos.z * 0.1f + draw_image->get_height() / 2);
|
||||
pt.x = CLAMP(pt.x, 1, draw_image->get_width() - 1);
|
||||
pt.y = CLAMP(pt.y, 1, draw_image->get_height() - 1);
|
||||
draw_image->fill_rect(Rect2(pt - Vector2(1, 1), Vector2(2, 2)), Color(c, 0, 0, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct Brush4 : EditBrush
|
||||
{
|
||||
Ref<Curve> curve;
|
||||
VoxelGeneratorImgMapper *imgmap;
|
||||
Brush4(Ref<Image> &image, Ref<Curve> &curve,
|
||||
VoxelGeneratorImgMapper *imgmap)
|
||||
: EditBrush(4, image), curve(curve), imgmap(imgmap)
|
||||
{
|
||||
}
|
||||
void draw(const Vector3 &v, float r, float s)
|
||||
{
|
||||
int i, j;
|
||||
int xs = 3 * r;
|
||||
float c = 0.5f;
|
||||
float scale = s / 100.0f;
|
||||
/* rel draw curve2 */
|
||||
ERR_FAIL_COND(!curve.is_valid());
|
||||
for (i = -xs; i < xs + 1; i++)
|
||||
for (j = -xs; j < xs + 1; j++)
|
||||
{
|
||||
float xr = Vector2(i, j).length();
|
||||
if (xr < r)
|
||||
{
|
||||
float h = curve->interpolate_baked(xr / r) * scale;
|
||||
if (h < -2.0f || h > 2.0f)
|
||||
{
|
||||
h += imgmap->get_height_full(v + Vector3(i, 0.0f, j));
|
||||
h = CLAMP(h, -100.0, 100.0f);
|
||||
c = (h + 100.0f) / 200.0;
|
||||
|
||||
Vector3 pos(v.x + i, v.y, v.z + j);
|
||||
Vector2 pt = Vector2(pos.x * 0.1f + draw_image->get_width() / 2,
|
||||
pos.z * 0.1f + draw_image->get_height() / 2);
|
||||
pt.x = CLAMP(pt.x, 1, draw_image->get_width() - 1);
|
||||
pt.y = CLAMP(pt.y, 1, draw_image->get_height() - 1);
|
||||
draw_image->fill_rect(Rect2(pt - Vector2(1, 1), Vector2(2, 2)), Color(c, 0, 0, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct Brush5 : EditBrush
|
||||
{
|
||||
VoxelGeneratorImgMapper *imgmap;
|
||||
Brush5(Ref<Image> &image,
|
||||
VoxelGeneratorImgMapper *imgmap)
|
||||
: EditBrush(5, image), imgmap(imgmap)
|
||||
{
|
||||
}
|
||||
void draw(const Vector3 &v, float r, float s)
|
||||
{
|
||||
int i, j;
|
||||
int xs = 3 * r;
|
||||
float c = 0.5f;
|
||||
float scale = s / 100.0f;
|
||||
float h = imgmap->get_height_full(v) + 2.0f;
|
||||
h = CLAMP(h, -100.0, 100.0f);
|
||||
for (i = -8; i < 8 + 1; i++)
|
||||
for (j = -8; j < 8 + 1; j++)
|
||||
{
|
||||
Vector3 pos(v.x + i, v.y, v.z + j);
|
||||
h = MAX(h, imgmap->get_height_full(pos) + 2.0f);
|
||||
}
|
||||
c = (h + 100.0f) / 200.0;
|
||||
for (i = -xs; i < xs + 1; i++)
|
||||
for (j = -xs; j < xs + 1; j++)
|
||||
{
|
||||
float xr = Vector2(i, j).length();
|
||||
if (xr < r)
|
||||
{
|
||||
Vector3 pos(v.x + i, v.y, v.z + j);
|
||||
Vector2 pt = Vector2(pos.x * 0.1f + draw_image->get_width() / 2,
|
||||
pos.z * 0.1f + draw_image->get_height() / 2);
|
||||
pt.x = CLAMP(pt.x, 1, draw_image->get_width() - 1);
|
||||
pt.y = CLAMP(pt.y, 1, draw_image->get_height() - 1);
|
||||
draw_image->fill_rect(Rect2(pt - Vector2(1, 1), Vector2(2, 2)), Color(c, 0, 0, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
void VoxelGeneratorImgMapper::draw_brush(const Vector3 &v, float r, float s, int id)
|
||||
{
|
||||
ERR_FAIL_COND(!_image_draw.is_valid());
|
||||
ERR_FAIL_COND(!curve1.is_valid());
|
||||
ERR_FAIL_COND(!curve2.is_valid());
|
||||
static struct Brush0 b0(_image_draw);
|
||||
static struct Brush1 b1(_image_draw);
|
||||
static struct Brush2 b2(_image_draw, curve1);
|
||||
static struct Brush3 b3(_image_draw, curve1, this);
|
||||
static struct Brush4 b4(_image_draw, curve2, this);
|
||||
static struct Brush5 b5(_image_draw, this);
|
||||
|
||||
int i, j;
|
||||
if (r < 1.0f)
|
||||
r = 1.0f;
|
||||
ERR_FAIL_COND(!_image_draw.is_valid());
|
||||
float c = 0.5f;
|
||||
int xs = 3 * r;
|
||||
float base_h = get_height_full(v);
|
||||
float scale = s / 100.0f;
|
||||
switch (id) {
|
||||
EditBrush::draw_brush(v, r, s, id);
|
||||
#if 0
|
||||
switch (id)
|
||||
{
|
||||
case 0:
|
||||
/* flat stuff */
|
||||
for (i = -xs ; i < xs + 1; i++)
|
||||
for (j = -xs ; j < xs + 1; j++) {
|
||||
for (i = -xs; i < xs + 1; i++)
|
||||
for (j = -xs; j < xs + 1; j++)
|
||||
{
|
||||
float xr = Vector2(i, j).length();
|
||||
if (xr < r) {
|
||||
if (xr < r)
|
||||
{
|
||||
Vector3 pos(v.x + i, v.y, v.z + j);
|
||||
Vector2 pt = Vector2(pos.x * 0.1f + _image_draw->get_width() / 2,
|
||||
pos.z * 0.1f + _image_draw->get_height() / 2);
|
||||
pos.z * 0.1f + _image_draw->get_height() / 2);
|
||||
pt.x = CLAMP(pt.x, 1, _image_draw->get_width() - 1);
|
||||
pt.y = CLAMP(pt.y, 1, _image_draw->get_height() - 1);
|
||||
_image_draw->fill_rect(Rect2(pt - Vector2(1, 1), Vector2(2, 2)), Color(c, 0, 0, 1));
|
||||
@@ -186,13 +471,15 @@ void VoxelGeneratorImgMapper::draw_brush(const Vector3 &v, float r, float s, int
|
||||
break;
|
||||
case 1:
|
||||
/* erase stuff */
|
||||
for (i = -xs ; i < xs + 1; i++)
|
||||
for (j = -xs ; j < xs + 1; j++) {
|
||||
for (i = -xs; i < xs + 1; i++)
|
||||
for (j = -xs; j < xs + 1; j++)
|
||||
{
|
||||
float xr = Vector2(i, j).length();
|
||||
if (xr < r) {
|
||||
if (xr < r)
|
||||
{
|
||||
Vector3 pos(v.x + i, v.y, v.z + j);
|
||||
Vector2 pt = Vector2(pos.x * 0.1f + _image_draw->get_width() / 2,
|
||||
pos.z * 0.1f + _image_draw->get_height() / 2);
|
||||
pos.z * 0.1f + _image_draw->get_height() / 2);
|
||||
pt.x = CLAMP(pt.x, 1, _image_draw->get_width() - 1);
|
||||
pt.y = CLAMP(pt.y, 1, _image_draw->get_height() - 1);
|
||||
_image_draw->fill_rect(Rect2(pt - Vector2(1, 1), Vector2(2, 2)), Color(c, 0, 0, 0));
|
||||
@@ -202,17 +489,19 @@ void VoxelGeneratorImgMapper::draw_brush(const Vector3 &v, float r, float s, int
|
||||
case 2:
|
||||
/* abs draw curve */
|
||||
ERR_FAIL_COND(!curve1.is_valid());
|
||||
for (i = -xs ; i < xs + 1; i++)
|
||||
for (j = -xs ; j < xs + 1; j++) {
|
||||
for (i = -xs; i < xs + 1; i++)
|
||||
for (j = -xs; j < xs + 1; j++)
|
||||
{
|
||||
float xr = Vector2(i, j).length();
|
||||
if (xr < r) {
|
||||
if (xr < r)
|
||||
{
|
||||
float h = curve1->interpolate_baked(xr / r) * scale;
|
||||
h = CLAMP(h, -100.0, 100.0f);
|
||||
c = (h + 100.0f) / 200.0;
|
||||
|
||||
Vector3 pos(v.x + i, v.y, v.z + j);
|
||||
Vector2 pt = Vector2(pos.x * 0.1f + _image_draw->get_width() / 2,
|
||||
pos.z * 0.1f + _image_draw->get_height() / 2);
|
||||
pos.z * 0.1f + _image_draw->get_height() / 2);
|
||||
pt.x = CLAMP(pt.x, 1, _image_draw->get_width() - 1);
|
||||
pt.y = CLAMP(pt.y, 1, _image_draw->get_height() - 1);
|
||||
_image_draw->fill_rect(Rect2(pt - Vector2(1, 1), Vector2(2, 2)), Color(c, 0, 0, 1));
|
||||
@@ -222,19 +511,22 @@ void VoxelGeneratorImgMapper::draw_brush(const Vector3 &v, float r, float s, int
|
||||
case 3:
|
||||
/* rel draw curve1 */
|
||||
ERR_FAIL_COND(!curve1.is_valid());
|
||||
for (i = -xs ; i < xs + 1; i++)
|
||||
for (j = -xs ; j < xs + 1; j++) {
|
||||
for (i = -xs; i < xs + 1; i++)
|
||||
for (j = -xs; j < xs + 1; j++)
|
||||
{
|
||||
float xr = Vector2(i, j).length();
|
||||
if (xr < r) {
|
||||
if (xr < r)
|
||||
{
|
||||
float h = curve1->interpolate_baked(xr / r) * scale;
|
||||
if (h < -2.0f || h > 2.0f) {
|
||||
if (h < -2.0f || h > 2.0f)
|
||||
{
|
||||
h += get_height_full(v + Vector3(i, 0.0f, j));
|
||||
h = CLAMP(h, -100.0, 100.0f);
|
||||
c = (h + 100.0f) / 200.0;
|
||||
|
||||
Vector3 pos(v.x + i, v.y, v.z + j);
|
||||
Vector2 pt = Vector2(pos.x * 0.1f + _image_draw->get_width() / 2,
|
||||
pos.z * 0.1f + _image_draw->get_height() / 2);
|
||||
pos.z * 0.1f + _image_draw->get_height() / 2);
|
||||
pt.x = CLAMP(pt.x, 1, _image_draw->get_width() - 1);
|
||||
pt.y = CLAMP(pt.y, 1, _image_draw->get_height() - 1);
|
||||
_image_draw->fill_rect(Rect2(pt - Vector2(1, 1), Vector2(2, 2)), Color(c, 0, 0, 1));
|
||||
@@ -245,19 +537,22 @@ void VoxelGeneratorImgMapper::draw_brush(const Vector3 &v, float r, float s, int
|
||||
case 4:
|
||||
/* rel draw curve2 */
|
||||
ERR_FAIL_COND(!curve2.is_valid());
|
||||
for (i = -xs ; i < xs + 1; i++)
|
||||
for (j = -xs ; j < xs + 1; j++) {
|
||||
for (i = -xs; i < xs + 1; i++)
|
||||
for (j = -xs; j < xs + 1; j++)
|
||||
{
|
||||
float xr = Vector2(i, j).length();
|
||||
if (xr < r) {
|
||||
if (xr < r)
|
||||
{
|
||||
float h = curve2->interpolate_baked(xr / r) * scale;
|
||||
if (h < -2.0f || h > 2.0f) {
|
||||
if (h < -2.0f || h > 2.0f)
|
||||
{
|
||||
h += get_height_full(v + Vector3(i, 0.0f, j));
|
||||
h = CLAMP(h, -100.0, 100.0f);
|
||||
c = (h + 100.0f) / 200.0;
|
||||
|
||||
Vector3 pos(v.x + i, v.y, v.z + j);
|
||||
Vector2 pt = Vector2(pos.x * 0.1f + _image_draw->get_width() / 2,
|
||||
pos.z * 0.1f + _image_draw->get_height() / 2);
|
||||
pos.z * 0.1f + _image_draw->get_height() / 2);
|
||||
pt.x = CLAMP(pt.x, 1, _image_draw->get_width() - 1);
|
||||
pt.y = CLAMP(pt.y, 1, _image_draw->get_height() - 1);
|
||||
_image_draw->fill_rect(Rect2(pt - Vector2(1, 1), Vector2(2, 2)), Color(c, 0, 0, 1));
|
||||
@@ -303,31 +598,36 @@ void VoxelGeneratorImgMapper::draw_brush(const Vector3 &v, float r, float s, int
|
||||
} break;
|
||||
#endif
|
||||
case 5:
|
||||
{
|
||||
float h = get_height_full(v) + 2.0f;
|
||||
h = CLAMP(h, -100.0, 100.0f);
|
||||
for (i = -8 ; i < 8 + 1; i++)
|
||||
for (j = -8 ; j < 8 + 1; j++) {
|
||||
{
|
||||
float h = get_height_full(v) + 2.0f;
|
||||
h = CLAMP(h, -100.0, 100.0f);
|
||||
for (i = -8; i < 8 + 1; i++)
|
||||
for (j = -8; j < 8 + 1; j++)
|
||||
{
|
||||
Vector3 pos(v.x + i, v.y, v.z + j);
|
||||
h = MAX(h, get_height_full(pos) + 2.0f);
|
||||
}
|
||||
c = (h + 100.0f) / 200.0;
|
||||
for (i = -xs; i < xs + 1; i++)
|
||||
for (j = -xs; j < xs + 1; j++)
|
||||
{
|
||||
float xr = Vector2(i, j).length();
|
||||
if (xr < r)
|
||||
{
|
||||
Vector3 pos(v.x + i, v.y, v.z + j);
|
||||
h = MAX(h, get_height_full(pos) + 2.0f);
|
||||
Vector2 pt = Vector2(pos.x * 0.1f + _image_draw->get_width() / 2,
|
||||
pos.z * 0.1f + _image_draw->get_height() / 2);
|
||||
pt.x = CLAMP(pt.x, 1, _image_draw->get_width() - 1);
|
||||
pt.y = CLAMP(pt.y, 1, _image_draw->get_height() - 1);
|
||||
_image_draw->fill_rect(Rect2(pt - Vector2(1, 1), Vector2(2, 2)), Color(c, 0, 0, 1));
|
||||
}
|
||||
c = (h + 100.0f) / 200.0;
|
||||
for (i = -xs ; i < xs + 1; i++)
|
||||
for (j = -xs ; j < xs + 1; j++) {
|
||||
float xr = Vector2(i, j).length();
|
||||
if (xr < r) {
|
||||
Vector3 pos(v.x + i, v.y, v.z + j);
|
||||
Vector2 pt = Vector2(pos.x * 0.1f + _image_draw->get_width() / 2,
|
||||
pos.z * 0.1f + _image_draw->get_height() / 2);
|
||||
pt.x = CLAMP(pt.x, 1, _image_draw->get_width() - 1);
|
||||
pt.y = CLAMP(pt.y, 1, _image_draw->get_height() - 1);
|
||||
_image_draw->fill_rect(Rect2(pt - Vector2(1, 1), Vector2(2, 2)), Color(c, 0, 0, 1));
|
||||
}
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void VoxelGeneratorImgMapper::compose()
|
||||
@@ -338,27 +638,33 @@ void VoxelGeneratorImgMapper::compose()
|
||||
ERR_FAIL_COND(!_image_draw.is_valid());
|
||||
copy = _image_bg->duplicate();
|
||||
if (copy->get_width() == _image_draw->get_width() &&
|
||||
copy->get_height() == _image_draw->get_height()) {
|
||||
copy->blend_rect(_image_draw,
|
||||
Rect2(0, 0, _image_draw->get_width(),
|
||||
_image_draw->get_height()),
|
||||
Vector2());
|
||||
copy->get_height() == _image_draw->get_height())
|
||||
{
|
||||
copy->blend_rect(_image_draw,
|
||||
Rect2(0, 0, _image_draw->get_width(),
|
||||
_image_draw->get_height()),
|
||||
Vector2());
|
||||
}
|
||||
if (copy->get_width() == _image_overlay->get_width() &&
|
||||
copy->get_height() == _image_overlay->get_height()) {
|
||||
copy->blend_rect(_image_overlay,
|
||||
Rect2(0, 0, _image_overlay->get_width(),
|
||||
_image_overlay->get_height()),
|
||||
Vector2());
|
||||
copy->get_height() == _image_overlay->get_height())
|
||||
{
|
||||
copy->blend_rect(_image_overlay,
|
||||
Rect2(0, 0, _image_overlay->get_width(),
|
||||
_image_overlay->get_height()),
|
||||
Vector2());
|
||||
}
|
||||
RWLockWrite wlock(_parameters_lock);
|
||||
// lock() prevents us from reading the same image from multiple threads, so we lock it up-front.
|
||||
// This might no longer be needed in Godot 4.
|
||||
if (_parameters.image.is_valid()) {
|
||||
if (_parameters.image.is_valid())
|
||||
{
|
||||
_parameters.image->unlock();
|
||||
}
|
||||
_parameters.image = copy;
|
||||
if (_parameters.image.is_valid()) {
|
||||
if (copy->get_width() > 0)
|
||||
grid_size = world_size / copy->get_width();
|
||||
if (_parameters.image.is_valid())
|
||||
{
|
||||
_parameters.image->lock();
|
||||
}
|
||||
}
|
||||
@@ -421,7 +727,28 @@ Ref<Curve> VoxelGeneratorImgMapper::get_curve4() const
|
||||
return curve4;
|
||||
}
|
||||
|
||||
void VoxelGeneratorImgMapper::_bind_methods() {
|
||||
void VoxelGeneratorImgMapper::set_grid_size(int value)
|
||||
{
|
||||
grid_size = value;
|
||||
}
|
||||
|
||||
int VoxelGeneratorImgMapper::get_grid_size() const
|
||||
{
|
||||
return grid_size;
|
||||
}
|
||||
|
||||
void VoxelGeneratorImgMapper::set_world_size(int value)
|
||||
{
|
||||
world_size = value;
|
||||
}
|
||||
|
||||
int VoxelGeneratorImgMapper::get_world_size() const
|
||||
{
|
||||
return world_size;
|
||||
}
|
||||
|
||||
void VoxelGeneratorImgMapper::_bind_methods()
|
||||
{
|
||||
ClassDB::bind_method(D_METHOD("set_image_bg", "image"), &VoxelGeneratorImgMapper::set_image_bg);
|
||||
ClassDB::bind_method(D_METHOD("get_image_bg"), &VoxelGeneratorImgMapper::get_image_bg);
|
||||
|
||||
@@ -450,6 +777,9 @@ void VoxelGeneratorImgMapper::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_blur_enabled", "enable"), &VoxelGeneratorImgMapper::set_blur_enabled);
|
||||
ClassDB::bind_method(D_METHOD("is_blur_enabled"), &VoxelGeneratorImgMapper::is_blur_enabled);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_world_size", "value"), &VoxelGeneratorImgMapper::set_world_size);
|
||||
ClassDB::bind_method(D_METHOD("get_world_size"), &VoxelGeneratorImgMapper::get_world_size);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "image_bg", PROPERTY_HINT_RESOURCE_TYPE, "Image"), "set_image_bg", "get_image_bg");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "image_overlay", PROPERTY_HINT_RESOURCE_TYPE, "Image"), "set_image_overlay", "get_image_overlay");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "image_draw", PROPERTY_HINT_RESOURCE_TYPE, "Image"), "set_image_draw", "get_image_draw");
|
||||
|
||||
@@ -1,12 +1,34 @@
|
||||
#ifndef HEADER_VOXEL_GENERATOR_IMGMAPPER
|
||||
#define HEADER_VOXEL_GENERATOR_IMGMAPPER
|
||||
|
||||
#include <map>
|
||||
#include <modules/voxel/generators/simple/voxel_generator_heightmap.h>
|
||||
#include <core/image.h>
|
||||
#include <scene/resources/curve.h>
|
||||
|
||||
struct EditBrush
|
||||
{
|
||||
static std::map<int, struct EditBrush *> brushes;
|
||||
int id;
|
||||
Ref<Image> &draw_image;
|
||||
virtual void draw(const Vector3 &v, float r, float s) = 0;
|
||||
EditBrush(int id, Ref<Image> &im) : id(id), draw_image(im)
|
||||
{
|
||||
brushes[id] = this;
|
||||
}
|
||||
virtual ~EditBrush()
|
||||
{
|
||||
brushes.erase(id);
|
||||
}
|
||||
static void draw_brush(const Vector3 &v, float r, float s, int id)
|
||||
{
|
||||
if (brushes.find(id) != brushes.end())
|
||||
brushes.at(id)->draw(v, r, s);
|
||||
}
|
||||
};
|
||||
|
||||
// Provides infinite tiling heightmap based on an image
|
||||
class VoxelGeneratorImgMapper : public VoxelGeneratorHeightmap {
|
||||
class VoxelGeneratorImgMapper : public VoxelGeneratorHeightmap
|
||||
{
|
||||
GDCLASS(VoxelGeneratorImgMapper, VoxelGeneratorHeightmap)
|
||||
|
||||
public:
|
||||
@@ -33,7 +55,9 @@ public:
|
||||
void set_height(const Vector3 &v, float height);
|
||||
void draw_brush(const Vector3 &v, float r, float s, int id);
|
||||
float get_height(const Vector3 &v);
|
||||
float get_height_linear(const Vector3 &v);
|
||||
float get_height_full(const Vector3 &v);
|
||||
float get_height_full_linear(const Vector3 &v);
|
||||
void compose();
|
||||
void save_png();
|
||||
|
||||
@@ -49,6 +73,9 @@ public:
|
||||
void set_curve4(const Ref<Curve> &curve);
|
||||
Ref<Curve> get_curve4() const;
|
||||
|
||||
void set_world_size(int value);
|
||||
int get_world_size() const;
|
||||
|
||||
private:
|
||||
static void _bind_methods();
|
||||
|
||||
@@ -61,7 +88,8 @@ private:
|
||||
|
||||
Ref<Curve> curve1, curve2, curve3, curve4;
|
||||
|
||||
struct Parameters {
|
||||
struct Parameters
|
||||
{
|
||||
int width, height;
|
||||
// This is a read-only copy of the image.
|
||||
// It wastes memory for sure, but Godot does not offer any way to secure this better.
|
||||
@@ -73,6 +101,10 @@ private:
|
||||
|
||||
Parameters _parameters;
|
||||
RWLock _parameters_lock;
|
||||
int grid_size;
|
||||
int world_size;
|
||||
void set_grid_size(int value);
|
||||
int get_grid_size() const;
|
||||
};
|
||||
|
||||
#endif // HEADER_VOXEL_GENERATOR_IMGMAPPER
|
||||
|
||||
Reference in New Issue
Block a user