Compare commits

...

3 Commits

Author SHA1 Message Date
6622e1a6ac Started terrain editor 2025-01-02 04:16:30 +03:00
c3720f69c8 Finally shrinking looks sane 2024-12-29 00:06:10 +03:00
2f8031fa8e Fixed iteration finish code 2024-12-17 14:50:37 +03:00
24 changed files with 1205 additions and 362 deletions

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=15 format=2]
[gd_scene load_steps=16 format=2]
[ext_resource path="res://terrain/terrain_draw.png" type="Image" id=2]
[ext_resource path="res://terrain/terrain.png" type="Image" id=3]
@@ -44,7 +44,11 @@ extents = Vector3( 50, 1, 50 )
size = Vector3( 4, 4, 120 )
[sub_resource type="CubeMesh" id=14]
size = Vector3( 3, 60, 3 )
size = Vector3( 1.5, 60, 1.5 )
[sub_resource type="TorusMesh" id=15]
inner_radius = 5.5
outer_radius = 7.0
[node name="editor" type="Spatial"]
@@ -67,10 +71,17 @@ focus_mode = 2
unique_name_in_owner = true
margin_top = 52.0
margin_right = 248.0
margin_bottom = 524.0
margin_bottom = 128.0
[node name="v_terrain" type="VBoxContainer" parent="VBoxContainer"]
unique_name_in_owner = true
margin_top = 132.0
margin_right = 248.0
margin_bottom = 132.0
[node name="v_buildings" type="VBoxContainer" parent="VBoxContainer"]
unique_name_in_owner = true
visible = false
margin_top = 528.0
margin_right = 248.0
margin_bottom = 528.0
@@ -520,3 +531,25 @@ shape = SubResource( 12 )
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 30, 0 )
mesh = SubResource( 14 )
material/0 = SubResource( 11 )
[node name="terrain_cursor" type="Spatial" parent="."]
unique_name_in_owner = true
[node name="Area" type="Area" parent="terrain_cursor"]
collision_layer = 32768
collision_mask = 32768
monitoring = false
[node name="CollisionShape" type="CollisionShape" parent="terrain_cursor/Area"]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2, 0 )
shape = SubResource( 12 )
[node name="mi" type="MeshInstance" parent="terrain_cursor"]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 30, 0 )
mesh = SubResource( 14 )
material/0 = SubResource( 11 )
[node name="mi2" type="MeshInstance" parent="terrain_cursor"]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 30, 0 )
mesh = SubResource( 15 )
material/0 = SubResource( 11 )

View File

@@ -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");

View File

@@ -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

View File

@@ -8,6 +8,7 @@ Import("env_modules")
module_obj = []
SConscript("buildings/SCsub")
SConscript("rtree/SCsub")
SConscript("ui/SCsub")
env_stream = env_modules.Clone()

View File

@@ -0,0 +1,17 @@
Import("env")
Import("env_modules")
#env_modules.stream_building_sources = []
#
#env_modules.add_source_files(env_modules.stream_building_sources, "*.cpp")
#
#lib = env_modules.add_library("buildings", env_modules.stream_building_sources)
#env_modules.Prepend(LIBS=[lib])
env.stream_building_sources = []
env.add_source_files(env.stream_building_sources, "*.cpp")
lib = env.add_library("rtree", env.stream_building_sources)
env.Prepend(LIBS=[lib])
env.Prepend(CPPPATH=["..", ".", "../ui"])

View File

@@ -197,6 +197,10 @@ struct RegionRect2i {
position = begin;
size = end - begin;
}
inline bool intersects_or_encloses(const RegionRect2i &other)
{
return encloses(other) || intersects(other);
}
operator String() const
{

View File

@@ -7,18 +7,32 @@
#include "building_layout_graph.h"
#include "region_tree.h"
#define SHRINK_RIGHT (1 << 0)
#define SHRINK_LEFT (1 << 1)
#define SHRINK_BACKWARD (1 << 2)
#define SHRINK_FORWARD (1 << 3)
static inline bool check_rtree_split(const struct region_tree *rt,
flecs::entity grid_floor_e,
const List<struct region> &regions)
{
assert((rt->region.rect.size.x > 1 || rt->region.rect.size.y > 1) &&
rt->region.rect.get_area() > regions.size());
assert(rt->children.size() == 0);
const region_tree *base_rtree = grid_floor_e.get<region_tree>();
assert(base_rtree->check(grid_floor_e));
assert(!rt->is_a_room(grid_floor_e));
assert(!rt->flag_room);
return true;
}
void region_tree::split(flecs::entity grid_floor_e,
const List<struct region> &regions)
{
assert((region.rect.size.x > 1 || region.rect.size.y > 1) &&
region.rect.get_area() > regions.size());
assert(children.size() == 0);
check_rtree_split(this, grid_floor_e, regions);
const region_tree *base_rtree = grid_floor_e.get<region_tree>();
const List<struct region>::Element *e = regions.front();
int count = 0;
assert(base_rtree->check(grid_floor_e));
assert(!is_a_room(grid_floor_e));
assert(!flag_room);
flecs::entity room_c = grid_floor_e.world().lookup(
"::graph_module::buildings_layout_room");
assert(room_c.is_valid());
@@ -495,23 +509,41 @@ bool region_tree::check(flecs::entity grid_floor_e) const
"some leaf regions intersect other leaf regions");
return ok;
}
static inline bool can_shrink_rooms(const struct region_tree *rtree)
{
int i;
List<const struct region_tree *> queue;
queue.push_back(rtree);
while (!queue.empty()) {
const struct region_tree *item = queue.front()->get();
queue.pop_front();
if (item->region.can_grow)
return false;
if (item->is_leaf() && item->region.can_move)
return false;
for (i = 0; i < (int)item->children.size(); i++)
queue.push_back(item->children[i]);
}
return true;
}
// TODO: calculate if rooms touch each other and shrink smaller one
void region_tree::shrink_rooms(flecs::entity grid_floor_e)
{
int i, accepted = 0;
assert(!parent);
flecs::log::dbg("====== start shrinking");
dump(grid_floor_e);
find_neighbors(grid_floor_e);
List<struct region_tree *> queue, shrinkable;
List<struct region_tree *> left_rooms, right_rooms, forward_rooms,
backward_rooms, inside_rooms;
List<struct region_tree *> shrink_rooms;
List<struct region_tree *>::Element *rp, *rp1, *rp2;
struct touching_result {
struct region_tree *first;
struct region_tree *second;
int how;
};
const struct region_tree *base_rtree = grid_floor_e.get<region_tree>();
struct region_tree *base_rtree = grid_floor_e.get_mut<region_tree>();
assert(base_rtree);
assert(base_rtree->check(grid_floor_e));
List<struct touching_result> touching;
@@ -519,17 +551,8 @@ void region_tree::shrink_rooms(flecs::entity grid_floor_e)
.get<WorldEditor::components::
buildings_layout_grid_floor>()
->grid_size;
queue.push_back(this);
while (!queue.empty()) {
const struct region_tree *item = queue.front()->get();
queue.pop_front();
if (item->region.can_grow)
return;
if (item->is_leaf() && item->region.can_move)
return;
for (i = 0; i < (int)item->children.size(); i++)
queue.push_back(item->children[i]);
}
if (!can_shrink_rooms(base_rtree))
return;
queue.clear();
queue.push_back(this);
while (!queue.empty()) {
@@ -540,174 +563,158 @@ void region_tree::shrink_rooms(flecs::entity grid_floor_e)
acceptable = 0xf;
flecs::log::dbg("acceptable: %d", acceptable);
if ((acceptable & 0xf) == 0xf) {
shrinkable.push_back(item);
accepted++;
int rx, px, ry, py;
rx = item->region.rect.position.x;
px = item->parent->region.rect.position.x;
if (rx == px) {
left_rooms.push_back(item);
acceptable |= (1 << 4);
}
rx = item->region.rect.position.x +
item->region.rect.size.x - 1;
px = item->parent->region.rect.position.x +
item->parent->region.rect.size.x - 1;
if (rx == px) {
right_rooms.push_back(item);
acceptable |= (1 << 5);
}
ry = item->region.rect.position.y;
py = item->parent->region.rect.position.y;
if (ry == py) {
forward_rooms.push_back(item);
acceptable |= (1 << 6);
}
ry = item->region.rect.position.y +
item->region.rect.size.y - 1;
py = item->parent->region.rect.position.y +
item->parent->region.rect.size.y - 1;
if (ry == py) {
backward_rooms.push_back(item);
acceptable |= (1 << 7);
}
if ((acceptable & 0xf0) == 0)
inside_rooms.push_back(item);
/* allocate rooms into proper buckets */
flecs::log::dbg("acceptable: %d", acceptable);
if (acceptable != 0)
shrink_rooms.push_back(item);
}
for (i = 0; i < (int)item->children.size(); i++)
queue.push_back(item->children[i]);
}
#if 0
rp1 = rooms.front();
while (rp1) {
rp2 = rooms.front();
while (rp2) {
if (rp1 == rp2) {
rp2 = rp2->next();
continue;
}
int style = rp1->get()->what_touching(rp2->get());
struct region_tree *item1 = rp1->get(),
*item2 = rp2->get();
if (style & TOUCH_RIGHT) {
if (shrinkable.find(item2)) {
if (!right_rooms.find(item2))
right_rooms.push_back(item2);
} else if (shrinkable.find(item1)) {
if (!left_rooms.find(item1))
left_rooms.push_back(item1);
}
}
if (style & TOUCH_LEFT) {
if (shrinkable.find(item2)) {
if (!left_rooms.find(item2))
left_rooms.push_back(item2);
} else if (shrinkable.find(item1)) {
if (!right_rooms.find(item1))
right_rooms.push_back(item1);
}
}
if (style & TOUCH_FORWARD) {
if (shrinkable.find(item2)) {
if (!forward_rooms.find(item2))
forward_rooms.push_back(item2);
} else if (shrinkable.find(item1)) {
if (!backward_rooms.find(item1))
backward_rooms.push_back(item1);
}
}
if (style & TOUCH_BACKWARD) {
if (shrinkable.find(item2)) {
if (!backward_rooms.find(item2))
backward_rooms.push_back(item2);
} else if (shrinkable.find(item1)) {
if (!forward_rooms.find(item1))
forward_rooms.push_back(item1);
}
}
rp2 = rp2->next();
}
rp1 = rp1->next();
}
#endif
int shrunk = 0;
int shrink_mode = SHRINK_LEFT_ROOM;
while (1) {
switch (shrink_mode) {
case SHRINK_LEFT_ROOM:
rp = left_rooms.front();
break;
case SHRINK_RIGHT_ROOM:
rp = right_rooms.front();
break;
case SHRINK_FORWARD_ROOM:
rp = forward_rooms.front();
break;
case SHRINK_BACKWARD_ROOM:
rp = backward_rooms.front();
break;
case SHRINK_INSIDE_ROOM:
rp = inside_rooms.front();
break;
}
while (rp) {
struct region backup = rp->get()->region;
List<struct region_tree *> neighbors;
switch (shrink_mode) {
case SHRINK_LEFT_ROOM:
get_neighbors(grid_floor_e, RIGHT, &neighbors);
if (neighbors.size() > 0) {
rp->get()->region.rect.size.x -= 1;
shrunk++;
}
break;
case SHRINK_RIGHT_ROOM:
get_neighbors(grid_floor_e, LEFT, &neighbors);
if (neighbors.size() > 0) {
rp->get()->region.rect.position.x += 1;
rp->get()->region.rect.size.x -= 1;
shrunk++;
}
break;
case SHRINK_FORWARD_ROOM:
get_neighbors(grid_floor_e, BACKWARD,
&neighbors);
if (neighbors.size() > 0) {
rp->get()->region.rect.size.y -= 1;
shrunk++;
}
break;
case SHRINK_BACKWARD_ROOM:
get_neighbors(grid_floor_e, FORWARD,
&neighbors);
if (neighbors.size() > 0) {
rp->get()->region.rect.position.y += 1;
rp->get()->region.rect.size.y -= 1;
shrunk++;
}
break;
case SHRINK_INSIDE_ROOM:
// TODO: check neighbors
rp->get()->region.rect =
rp->get()->region.rect.grow(-1);
shrunk++;
break;
default:
while (!shrink_rooms.empty()) {
struct region_tree *item = shrink_rooms.front()->get();
shrink_rooms.pop_front();
struct region backup = item->region;
List<struct region_tree *> neighbors;
int shrink_flags = 0;
if (item->neighbors_left.size() > 0)
shrink_flags |= SHRINK_LEFT;
if (item->neighbors_right.size() > 0)
shrink_flags |= SHRINK_RIGHT;
if (item->neighbors_backward.size() > 0)
shrink_flags |= SHRINK_BACKWARD;
if (item->neighbors_forward.size() > 0)
shrink_flags |= SHRINK_FORWARD;
flecs::log::dbg("shrink_flags: %08x", shrink_flags);
if (shrink_flags & SHRINK_RIGHT) {
item->region.rect.size.x -= 1;
shrunk++;
if (!base_rtree->check(grid_floor_e)) {
item->region.rect.size.x = backup.rect.size.x;
assert(false);
}
if (!base_rtree->check(grid_floor_e))
rp->get()->region = backup;
assert(base_rtree->check(grid_floor_e));
rp = rp->next();
}
if (shrink_mode >= SHRINK_INSIDE_ROOM)
break;
shrink_mode++;
if (shrink_flags & SHRINK_LEFT) {
item->region.rect.position.x += 1;
item->region.rect.size.x -= 1;
shrunk++;
if (!base_rtree->check(grid_floor_e)) {
item->region.rect.position.x =
backup.rect.position.x;
item->region.rect.size.x = backup.rect.size.x;
assert(false);
}
}
if (shrink_flags & SHRINK_BACKWARD) {
item->region.rect.size.y -= 1;
shrunk++;
if (!base_rtree->check(grid_floor_e)) {
item->region.rect.size.y = backup.rect.size.y;
assert(false);
}
}
if (shrink_flags & SHRINK_FORWARD) {
item->region.rect.position.y += 1;
item->region.rect.size.y -= 1;
shrunk++;
if (!base_rtree->check(grid_floor_e)) {
item->region.rect.position.y =
backup.rect.position.y;
item->region.rect.size.y = backup.rect.size.y;
assert(false);
}
}
if (shrink_flags)
base_rtree->find_neighbors(grid_floor_e);
}
assert(base_rtree->check(grid_floor_e));
// assert(accepted == 0 || shrunk > 0);
grid_floor_e.modified<region_tree>();
flecs::log::dbg("====== end shrinking");
}
template <class T>
static inline void list2vector(const List<T> &data, Vector<T> &v)
{
int idx = v.size();
v.resize(idx + data.size());
const typename List<T>::Element *e = data.front();
while (e) {
v.write[idx++] = e->get();
e = e->next();
}
}
template <class T>
static inline void append_if_none(Vector<T> &v, const T &data)
{
if (v.find(data) < 0)
v.push_back(data);
}
void region_tree::find_neighbors(flecs::entity grid_floor_e)
{
int i, j;
Vector<struct region_tree *> leaf_regions;
List<struct region_tree *> node_list;
struct region_tree *base_rtree = grid_floor_e.get_mut<region_tree>();
base_rtree->get_leaf_nodes(&node_list);
list2vector(node_list, leaf_regions);
for (i = 0; i < leaf_regions.size(); i++) {
leaf_regions[i]->neighbors_left.clear();
leaf_regions[i]->neighbors_right.clear();
leaf_regions[i]->neighbors_forward.clear();
leaf_regions[i]->neighbors_backward.clear();
}
for (i = 0; i < leaf_regions.size(); i++) {
for (j = 0; j < leaf_regions.size(); j++) {
if (i == j)
continue;
RegionRect2i left = leaf_regions[i]->left_rect();
RegionRect2i right = leaf_regions[i]->right_rect();
RegionRect2i forward = leaf_regions[i]->forward_rect();
RegionRect2i backward =
leaf_regions[i]->backward_rect();
if (left.intersects_or_encloses(
leaf_regions[j]->region.rect)) {
append_if_none(leaf_regions[i]->neighbors_left,
leaf_regions[j]);
append_if_none(leaf_regions[j]->neighbors_right,
leaf_regions[i]);
}
if (right.intersects_or_encloses(
leaf_regions[j]->region.rect)) {
append_if_none(leaf_regions[i]->neighbors_right,
leaf_regions[j]);
append_if_none(leaf_regions[j]->neighbors_left,
leaf_regions[i]);
}
if (backward.intersects_or_encloses(
leaf_regions[j]->region.rect)) {
append_if_none(
leaf_regions[i]->neighbors_backward,
leaf_regions[j]);
append_if_none(
leaf_regions[j]->neighbors_forward,
leaf_regions[i]);
}
if (forward.intersects_or_encloses(
leaf_regions[j]->region.rect)) {
append_if_none(
leaf_regions[i]->neighbors_forward,
leaf_regions[j]);
append_if_none(
leaf_regions[j]->neighbors_backward,
leaf_regions[i]);
}
}
int neighbors_count = leaf_regions[i]->neighbors_left.size();
neighbors_count += leaf_regions[i]->neighbors_right.size();
neighbors_count += leaf_regions[i]->neighbors_forward.size();
neighbors_count += leaf_regions[i]->neighbors_backward.size();
flecs::log::dbg("%d: neighbors: %d", i, neighbors_count);
}
grid_floor_e.modified<region_tree>();
}
void region_tree::place(flecs::entity grid_floor_e) const
{
@@ -726,19 +733,6 @@ void region_tree::place(flecs::entity grid_floor_e) const
for (i = 0; i < (int)delete_cells.size(); i++)
delete_cells[i].destruct();
delete_cells.clear();
#if 0
for (i = region.rect.position.x;
i < region.rect.position.x + region.rect.size.x; i++)
for (j = region.rect.position.y;
j < region.rect.position.y + region.rect.size.y; j++) {
int cell_id = i + grid_size * j;
String cname = "cell_" + itos(cell_id);
flecs::entity cell_e =
grid_floor_e.lookup(cname.ascii().ptr());
if (cell_e.is_valid())
cell_e.destruct();
}
#endif
queue.push_back(this);
bool result = true;
while (!queue.empty()) {
@@ -807,11 +801,6 @@ void region_tree::place(flecs::entity grid_floor_e) const
cname.ascii().ptr());
if (cell_e.is_valid())
continue;
#if 0
update_cell(grid_floor_e,
grid_floor_e.parent().id(),
grid_floor_e.id(), id);
#endif
create_corridoor_cell(
grid_floor_e,
grid_floor_e.parent().id(), id);
@@ -1168,6 +1157,34 @@ int region_tree::area() const
{
return region.rect.get_area();
}
RegionRect2i region_tree::left_rect() const
{
RegionRect2i ret = region.rect;
ret.position.x -= 1;
ret.size.x = 2;
return ret;
}
RegionRect2i region_tree::right_rect() const
{
RegionRect2i ret = region.rect;
ret.position.x += ret.size.x - 1;
ret.size.x = 2;
return ret;
}
RegionRect2i region_tree::forward_rect() const
{
RegionRect2i ret = region.rect;
ret.position.y -= 1;
ret.size.y = 2;
return ret;
}
RegionRect2i region_tree::backward_rect() const
{
RegionRect2i ret = region.rect;
ret.position.y += ret.size.y - 1;
ret.size.y = 2;
return ret;
}
bool region_tree::check_candidate(int i, const RegionRect2i &candidate) const
{
int j;

View File

@@ -5,6 +5,8 @@
struct region_tree {
struct region region;
Vector<struct region_tree *> children;
Vector<struct region_tree *> neighbors_left, neighbors_right,
neighbors_forward, neighbors_backward;
struct region_tree *parent;
bool flag_room;
bool flag_special;
@@ -23,6 +25,7 @@ struct region_tree {
bool check(flecs::entity grid_floor_e) const;
void place(flecs::entity grid_floor_e) const;
void shrink_rooms(flecs::entity grid_floor_e);
void find_neighbors(flecs::entity grid_floor_e);
void move(flecs::entity grid_floor_e);
void get_rects(List<RegionRect2i> *rect_list) const;
void get_leaf_nodes(List<const struct region_tree *> *node_list) const;
@@ -59,6 +62,10 @@ struct region_tree {
Vector2i get_global_center() const;
Vector2i get_center() const;
int area() const;
RegionRect2i left_rect() const;
RegionRect2i right_rect() const;
RegionRect2i forward_rect() const;
RegionRect2i backward_rect() const;
private:
bool check_candidate(int i, const RegionRect2i &candidate) const;

View File

@@ -234,6 +234,11 @@ void StreamWorld::remove_building(const String &key)
update_items();
}
VoxelLodTerrain *StreamWorld::get_terrain()
{
return terrain;
}
void StreamWorld::run_command(const String &command,
const Vector<Variant> &args)
{

View File

@@ -53,6 +53,7 @@ private:
static void _bind_methods();
public:
VoxelLodTerrain *get_terrain();
void run_command(const String &command, const Vector<Variant> &args);
StreamWorld();
~StreamWorld();

View File

@@ -0,0 +1,152 @@
#include <core/ustring.h>
#include <core/math/vector2.h>
#include <core/math/vector3.h>
#include <scene/3d/camera.h>
#include "editor_event.h"
#include "world_editor.h"
#include "terrain_editor.h"
void TerrainEditor::exit()
{
if (active)
deactivate();
}
void TerrainEditor::update(float delta)
{
if (!active)
activate();
if (!cursor_enabled && get_camera_mode() == 3) {
cursor_enabled = true;
get_as_node<Spatial>(cursor_name)->show();
} else if (cursor_enabled && get_camera_mode() != 3) {
cursor_enabled = false;
get_as_node<Spatial>(cursor_name)->hide();
}
if (camera_moved) {
Camera *cam = editor->get_viewport()->get_camera();
assert(cam);
camera_moved = false;
Transform cam_xform = cam->get_global_transform();
float h = cam_xform.origin.y;
cam_xform.origin.z += Math::abs(h) * camera_motion.z * delta;
cam_xform.origin.x += Math::abs(h) * camera_motion.x * delta;
cam_xform.origin.y += camera_motion.y * delta;
camera_motion = Vector3();
cam->set_global_transform(cam_xform);
Array move_args;
move_args.push_back(cam_xform);
editor->emit_signal("editor_event", "editor_camera_moved",
move_args);
}
}
TerrainEditor::TerrainEditor(WorldEditor *editor)
: editor(editor)
, active(false)
, cursor_enabled(false)
, cursor_name("%terrain_cursor")
, camera_moved(false)
{
}
TerrainEditor::~TerrainEditor()
{
}
void TerrainEditor::activate()
{
EditorEvent::get_singleton()->event.add_listener(
this, &TerrainEditor::event_handler);
active = true;
}
void TerrainEditor::deactivate()
{
EditorEvent::get_singleton()->event.remove_listener(
this, &TerrainEditor::event_handler);
active = false;
}
void TerrainEditor::event_handler(const String &event,
const Vector<Variant> &args)
{
print_line("TerrainEditor::event: " + event);
if (event == "mouse_press" || event == "mouse_drag") {
if (cursor_enabled) {
/* Raycasting outside physics process */
Vector2 position = args[0];
Camera *cam = editor->get_viewport()->get_camera();
Vector3 start = cam->project_ray_origin(position);
Vector3 normal = cam->project_ray_normal(position);
Vector3 end = start + normal * cam->get_zfar();
PhysicsDirectSpaceState *space_state =
editor->get_world()->get_direct_space_state();
PhysicsDirectSpaceState::RayResult result;
Set<RID> exclude;
space_state->intersect_ray(start, end, result, exclude,
(1 << 15) | (1 << 0), true,
true);
Vector3 result_pre;
if (result.rid == RID())
goto end;
result_pre = result.position;
result_pre.x = Math::stepify(result_pre.x, 2.0f);
result_pre.z = Math::stepify(result_pre.z, 2.0f);
start = result_pre + Vector3(0.0f, 200.0f, 0.0f);
end = result_pre - Vector3(0.0f, 200.0f, 0.0f);
space_state->intersect_ray(start, end, result, exclude,
(1 << 15) | (1 << 0), true,
true);
if (result.rid != RID()) {
set_cursor_position(result.position);
set_ui_cursor_position(result.position);
}
end:;
}
}
}
Vector3 TerrainEditor::get_cursor_position()
{
Spatial *cursor = get_as_node<Spatial>(cursor_name);
return cursor->get_global_transform().origin;
}
void TerrainEditor::set_cursor_position(const Vector3 &cursor_position)
{
Spatial *cursor = get_as_node<Spatial>(cursor_name);
cursor->set_global_transform(Transform(Basis(), cursor_position));
Array pargs;
pargs.push_back(cursor_position);
editor->emit_signal("editor_event", "line_cursor_motion", pargs);
}
void TerrainEditor::set_ui_cursor_position(const Vector3 &cursor_position)
{
#if 0
LineEdit *cursor_x = get_as_node<LineEdit>("%cursor_x");
LineEdit *cursor_y = get_as_node<LineEdit>("%cursor_y");
LineEdit *cursor_z = get_as_node<LineEdit>("%cursor_z");
cursor_x->set_text(String::num(cursor_position.x));
cursor_y->set_text(String::num(cursor_position.y));
cursor_z->set_text(String::num(cursor_position.z));
#endif
}
int TerrainEditor::get_camera_mode() const
{
return editor->get_camera_mode();
}
Node *TerrainEditor::scene()
{
return editor->get_tree()->get_current_scene();
}
template <class T> T *TerrainEditor::get_as_node(const String &path)
{
Node *node = scene()->get_node(NodePath(path));
assert(node);
T *ret = Object::cast_to<T>(node);
assert(ret);
return ret;
}

View File

@@ -0,0 +1,30 @@
/* ~/godot-projects/streaming_world/src/modules/stream/ui/terrain_editor.h */
#ifndef TERRAIN_EDITOR_H_
#define TERRAIN_EDITOR_H_
class WorldEditor;
class TerrainEditor {
WorldEditor *editor;
bool active;
bool cursor_enabled;
String cursor_name;
bool camera_moved;
Vector3 camera_motion;
public:
void exit();
void update(float delta);
TerrainEditor(WorldEditor *editor);
virtual ~TerrainEditor();
protected:
void activate();
void deactivate();
void event_handler(const String &event, const Vector<Variant> &args);
Vector3 get_cursor_position();
void set_cursor_position(const Vector3 &cursor_position);
void set_ui_cursor_position(const Vector3 &cursor_position);
template <class T> T *get_as_node(const String &path);
int get_camera_mode() const;
Node *scene();
};
#endif // TERRAIN_EDITOR_H_

View File

@@ -14,5 +14,6 @@ env.add_source_files(env.stream_building_sources, "*.cpp")
lib = env.add_library("ui", env.stream_building_sources)
env.Prepend(LIBS=[lib])
env.Prepend(CPPPATH=["../rtree"])
env.Prepend(CPPPATH=[".."])
env.Prepend(CPPPATH=["../../../meshoptimizer/src"])

View File

@@ -33,10 +33,25 @@ grow_job_queue::grow_job_queue(
, grid_size(size.grid_size)
, subregions(subregions)
, module_name(module_name)
, is_valid(false)
{
int floor_count = 0;
const List<Pair<int, flecs::entity_t> >::Element *me;
List<Pair<flecs::entity_t, flecs::entity_t> > job_create_queue;
me = size.floors.front();
flecs::query<growth_regions> q =
grid_e.world()
.query_builder<growth_regions>()
.with(flecs::ChildOf, grid_e)
.build();
q.each([this, &floor_count](flecs::entity e, growth_regions &g) {
flecs::log::warn("pre-pre floor: %s", e.path().c_str());
floor_count++;
});
flecs::log::dbg("floor count: %d %d", size.floors.size(), floor_count);
if (size.floors.size() != floor_count)
return;
assert(size.floors.size() == floor_count);
while (me) {
// graph entity
job_create_queue.push_back(
@@ -49,8 +64,10 @@ grow_job_queue::grow_job_queue(
get_grid_floor(grid_e, base_floor_e);
grid_floor_e.get_mut<growth_regions>()->job_list =
List<grow_job>();
grid_floor_e.modified<growth_regions>();
me = me->next();
}
assert(!job_create_queue.empty());
while (!job_create_queue.empty()) {
// graph entity
flecs::entity_t et = job_create_queue.front()->get().first;
@@ -97,13 +114,21 @@ grow_job_queue::grow_job_queue(
flecs::log::dbg(
"no subregions for: %s",
grid_e.world().entity(et).path().c_str());
assert(!grid_floor_e.get_mut<growth_regions>()
->job_list.empty());
}
q.each([this](flecs::entity e, growth_regions &g) {
flecs::log::warn("pre floor: %s", e.path().c_str());
assert(!g.job_list.empty());
});
is_valid = true;
}
/* Unlimited growth for squares */
void grow_job_queue::job_initial(struct grow_job *job)
{
flecs::entity base_floor_e = grid_e.world().entity(job->base_floor_id);
flecs::entity grid_floor_e = grid_e.world().entity(job->grid_floor_id);
assert(is_valid);
flecs::log::dbg("create: base_floor: %s", base_floor_e.path().c_str());
flecs::log::dbg("create: grid_floor: %s", grid_floor_e.path().c_str());
growth_regions *g = grid_floor_e.get_mut<growth_regions>();
@@ -119,6 +144,10 @@ void grow_job_queue::job_initial(struct grow_job *job)
clip_rect.grow(-1), grid_size * grid_size, false, false,
false, true },
Vector<struct region_tree *>(),
{},
{},
{},
{},
nullptr,
false,
false,
@@ -171,6 +200,7 @@ void grow_job_queue::job_initial(struct grow_job *job)
}
void grow_job_queue::job_common(struct grow_job *job)
{
assert(is_valid);
make_random r(172);
flecs::entity base_floor_e = grid_e.world().entity(job->base_floor_id);
flecs::entity grid_floor_e = grid_e.world().entity(job->grid_floor_id);
@@ -220,6 +250,7 @@ void grow_job_queue::job_common(struct grow_job *job)
}
void grow_job_queue::commit_common_queue()
{
assert(is_valid);
grid_e.world()
.query_builder<
WorldEditor::components::buildings_layout_grid_floor,
@@ -290,6 +321,7 @@ void grow_job_queue::create_region_list(
region_tree *rtree, const List<Pair<flecs::entity, float> > &subregions,
List<struct region> &region_list)
{
assert(is_valid);
int i, j;
const List<Pair<flecs::entity, float> >::Element *fe =
subregions.front();
@@ -471,26 +503,31 @@ void grow_job_queue::create_region_list(
}
void grow_job_queue::iterate()
{
int badness = 0;
assert(is_valid);
assert(grid_e.is_valid());
assert(this);
/* once for each grid floor */
flecs::query<growth_regions> q =
grid_e.world().query_builder<growth_regions>().build();
#if 0
flecs::query<region_tree> qm =
grid_e.world().query_builder<region_tree>().build();
#endif
q.each([this](flecs::entity e, growth_regions &g) {
flecs::log::warn("pre floor: %s", e.path().c_str());
});
#if 0
qm.each([this](flecs::entity em, region_tree &rt) {
rt.dump(em);
assert(rt.check(em));
});
#endif
// q.each([this, &badness](flecs::entity e, growth_regions &g) {
// if (g.job_list.empty()) {
// flecs::log::err("bad grid: %s floor: %s",
// e.parent().path().c_str(),
// e.path().c_str());
// badness++;
// } else
// flecs::log::dbg("ok grid %s floor %s",
// e.parent().path().c_str(),
// e.path().c_str());
// });
// assert(badness == 0);
q.each([this](flecs::entity e, growth_regions &g) {
flecs::log::warn("floor: %s", e.path().c_str());
flecs::log::warn("job count: %d", g.job_list.size());
if (g.job_list.empty())
return;
assert(!g.job_list.empty());
while (!g.job_list.empty()) {
List<struct grow_job>::Element *job_e =
g.job_list.front();
@@ -541,20 +578,28 @@ void grow_job_queue::iterate()
// if (common_count > 0)
// break;
}
struct region_tree *rtree = e.get_mut<region_tree>();
assert(rtree);
flecs::log::dbg(
"processed jobs (created region initial positions): %d",
g.job_list.size());
assert(rtree->check(e));
g.job_list.clear();
flecs::query<region_tree> qm =
grid_e.world().query_builder<region_tree>().build();
qm.each([this](flecs::entity em, region_tree &rt) {
assert(rt.check(em));
});
qm.each([this](flecs::entity em, region_tree &rt) {
rt.shrink_rooms(em);
});
qm.each([this](flecs::entity em, region_tree &rt) {
rt.place(em);
});
});
}
}
void grow_job_queue::finalize()
{
assert(is_valid);
#if 0
flecs::query<growth_regions> q =
grid_e.world().query_builder<growth_regions>().build();
q.each([this](flecs::entity e, growth_regions &g) {
struct region_tree *rtree = e.get_mut<region_tree>();
assert(rtree->check(e));
rtree->shrink_rooms(e);
rtree->place(e);
e.modified<region_tree>();
});
#endif
}

View File

@@ -15,11 +15,13 @@ struct grow_job_queue {
int grid_size;
struct subregions &subregions;
const String &module_name;
bool is_valid;
grow_job_queue(
flecs::entity grid_e, struct subregions &subregions,
const WorldEditor::components::buildings_layout_grid_size &size,
const String &module_name);
void iterate();
void finalize();
void job_initial(struct grow_job *job);
void job_common(struct grow_job *job);
void commit_common_queue();

View File

@@ -1,3 +1,4 @@
#include <map>
#include <core/math/a_star.h>
#include "growth_regions.h"
#include "region_tree.h"
@@ -9,6 +10,7 @@
growth_module::growth_module(flecs::world &ecs)
{
grow_count = 0;
ecs.module<growth_module>();
ecs.component<growth_regions>();
ecs.component<region_tree>();
@@ -44,7 +46,9 @@ growth_module::growth_module(flecs::world &ecs)
.write<growth_regions>()
.kind(GraphSolve)
.run([module_name, this, GraphFilter](flecs::iter &it) {
flecs::log::dbg("RunGrow");
flecs::world &&ecs_ = it.world();
assert(grow_count == 0);
it.world().defer_suspend();
flecs::log::dbg("Assembling skeleton done...");
flecs::entity GraphSolveZones = it.world().lookup(
@@ -56,15 +60,16 @@ growth_module::growth_module(flecs::world &ecs)
EditorEvent::get_singleton()->event.emit(
"update_layout_view", varray());
// assert(false);
struct subregions subregions(ecs_);
struct queries queries(ecs_);
flecs::log::dbg("Creating regions grow...");
/* for each grid build subregions */
std::map<uint64_t, struct subregions> subreg;
queries.get_qp().each(
[this, &subregions, &queries,
module_name](flecs::iter &it2, size_t count,
const WorldEditor::components::
buildings_layout_grid_size
&size) {
[this, &subreg, &queries, module_name,
&ecs_](flecs::iter &it2, size_t count,
const WorldEditor::components::
buildings_layout_grid_size
&size) {
struct grid_misc grid;
// grid.subregions_init(it2.world());
flecs::entity graph_e =
@@ -78,18 +83,131 @@ growth_module::growth_module(flecs::world &ecs)
grid_base_e.lookup(
graph_e.name());
assert(grid_e.is_valid());
subreg.emplace(grid_e.id(),
subregions(ecs_));
flecs::log::warn(
"creating grid for: %s: %s",
graph_e.path().c_str(),
grid_e.path().c_str());
/* starting at grid center */
subregions.build_subregions(grid_e,
grid, size);
struct grow_job_queue job_queue(
grid_e, subregions, size,
module_name);
job_queue.iterate();
/* prevent default constructor from running */
subreg.at(grid_e.id())
.build_subregions(grid_e, grid,
size);
});
/* for each grid */
std::map<flecs::entity_t, struct grow_job_queue>
job_queues;
queries.get_qp().each([this, &subreg, &queries,
module_name, &job_queues](
flecs::iter &it2,
size_t count,
const WorldEditor::components::
buildings_layout_grid_size
&size) {
int floor_count = 0;
flecs::entity graph_e = it2.entity(count);
flecs::entity grid_base_e =
queries.get_layout_grid_base();
flecs::entity grid_e =
grid_base_e.lookup(graph_e.name());
assert(grid_e.is_valid());
/* creating queue for each grid floor */
struct grow_job_queue job_queue(
grid_e, subreg.at(grid_e.id()), size,
module_name);
job_queues.insert(
std::make_pair(grid_e, job_queue));
/* each floor in grid */
flecs::query<growth_regions> grid_floor_q =
it2.world()
.query_builder<growth_regions>()
.with(flecs::ChildOf, grid_e)
.build();
grid_floor_q.each([grid_e](flecs::entity me,
const growth_regions
&g) {
flecs::log::dbg(
"processed: grid: %s: floor: %s",
grid_e.path().c_str(),
me.path().c_str());
assert(!g.job_list.empty());
});
flecs::log::warn("grid processed %s",
grid_e.path().c_str());
});
{
int badness = 0;
flecs::query<growth_regions> q =
it.world()
.query_builder<growth_regions>()
.build();
q.each([this, &badness](flecs::entity e,
growth_regions &g) {
if (g.job_list.empty()) {
flecs::log::err(
"bad grid: %s floor: %s",
e.parent()
.path()
.c_str(),
e.path().c_str());
badness++;
} else
flecs::log::dbg(
"ok grid %s floor %s",
e.parent()
.path()
.c_str(),
e.path().c_str());
});
assert(badness == 0);
}
{
flecs::query<growth_regions> test_q =
it.world()
.query_builder<growth_regions>()
.build();
test_q.each([this](flecs::entity e,
growth_regions &g) {
flecs::log::warn("test floor: %s",
e.path().c_str());
assert(!g.job_list.empty());
});
}
queries.get_qp().each(
[this, &queries, module_name,
&job_queues](flecs::iter &it2, size_t count,
const WorldEditor::components::
buildings_layout_grid_size
&size) {
flecs::entity graph_e =
it2.entity(count);
flecs::entity grid_base_e =
queries.get_layout_grid_base();
flecs::entity grid_e =
grid_base_e.lookup(
graph_e.name());
assert(grid_e.is_valid());
assert(job_queues.at(grid_e).is_valid);
flecs::log::dbg("grid iteration %s",
grid_e.path().c_str());
job_queues.at(grid_e).iterate();
job_queues.at(grid_e).finalize();
});
flecs::query<growth_regions> q =
it.world()
.query_builder<growth_regions>()
.build();
q.each([this](flecs::entity e, growth_regions &g) {
struct region_tree *rtree =
e.get_mut<region_tree>();
assert(rtree->check(e));
rtree->shrink_rooms(e);
rtree->place(e);
e.modified<region_tree>();
});
commit_growth_queue(it.world());
mark_cells(it.world());
mark_doors(it.world());
@@ -97,6 +215,7 @@ growth_module::growth_module(flecs::world &ecs)
.lookup("::growth_module::GraphFilter")
.disable();
it.world().defer_resume();
grow_count++;
});
}

View File

@@ -4,6 +4,7 @@
#include "base_data.h"
struct growth_module {
int grow_count;
void grow_cell(flecs::entity seed_e, int id);
void commit_growth_queue(flecs::world &&world);
void mark_cells(flecs::world &&ecs_);

View File

@@ -93,6 +93,7 @@ void MainTabs::_notification(int which)
String title, command, header;
};
struct menu_data items[] = {
{ "Terrain", "select_terrain", "Terrain mode" },
{ "Buildings", "select_buildings", "Buildings mode" },
{ "Navigation", "select_navigation",
"Navigation mode" },
@@ -176,6 +177,15 @@ void MainTabs::_notification(int which)
add_child(tab);
switch (i) {
case 0: {
std::vector<Variant> args_data = {
items[i].header
};
ui_field::ui_field_builder(this, tab,
"l_p{v{}}",
args_data.data(),
args_data.size());
} break;
case 1: {
// VBoxContainer *v = memnew(VBoxContainer);
//panel->add_child(v);
//v->set_owner(this);
@@ -239,15 +249,6 @@ void MainTabs::_notification(int which)
args_data.size());
}
} break;
case 1: {
std::vector<Variant> args_data = {
items[i].header
};
ui_field::ui_field_builder(this, tab,
"l_p{v{}}",
args_data.data(),
args_data.size());
} break;
case 2: {
std::vector<Variant> args_data = {
items[i].header
@@ -258,6 +259,15 @@ void MainTabs::_notification(int which)
args_data.size());
} break;
case 3: {
std::vector<Variant> args_data = {
items[i].header
};
ui_field::ui_field_builder(this, tab,
"l_p{v{}}",
args_data.data(),
args_data.size());
} break;
case 4: {
std::vector<Variant> args_data = {
/* clang-format off */
"road_lines_base",
@@ -321,7 +331,7 @@ void MainTabs::_notification(int which)
// "p{v{lh#!{m#!m#!m#!m#!m#!}_lh{le#!}i.#!x#!_lv#!{h{e#!e#!e#!b#!}}_lv#!{h{e#!e#!e#!b#!}}}}",
args_data.data(), args_data.size());
} break;
case 4: {
case 5: {
std::vector<Variant> args_data = {
items[i].header
};
@@ -330,7 +340,7 @@ void MainTabs::_notification(int which)
args_data.data(),
args_data.size());
} break;
case 5: {
case 6: {
if (!Engine::get_singleton()->is_editor_hint()) {
assert(pv);
pv->setup_layout_tab(tab,

View File

@@ -155,6 +155,7 @@ struct subregions {
subregions->push_back({ ce, area.area });
});
}
/** build_subregions() - leaf regions have empty subregions */
void build_subregions(
flecs::entity grid_e, struct grid_misc &grid,
const WorldEditor::components::buildings_layout_grid_size &size)

View File

@@ -11,11 +11,13 @@
#include <scene/main/viewport.h>
#include <scene/3d/camera.h>
#include <scene/scene_string_names.h>
#include <modules/voxel/terrain/voxel_lod_terrain.h>
#include "road_lines_editor.h"
#include "world_editor.h"
#include "editor_event.h"
#include "base_data.h"
#include "buildings_editor.h"
#include "terrain_editor.h"
#include "world_editor.h"
class HandleCommandButton : public Object {
GDCLASS(HandleCommandButton, Object)
@@ -76,6 +78,7 @@ WorldEditor::WorldEditor()
, drag_delay(0.2f)
, road_lines_editor(memnew(RoadLinesEditor(this)))
, buildings_editor(memnew(BuildingsEditor(this)))
, terrain_editor(memnew(TerrainEditor(this)))
{
flecs::entity e;
flecs::world ecs = BaseData::get_singleton()->get();
@@ -216,6 +219,11 @@ void WorldEditor::mode_npc()
disable_all();
print_line("NPC");
}
void WorldEditor::mode_terrain()
{
disable_all();
print_line("TERRAIN");
}
struct StringHasher {
std::size_t operator()(const String &s) const
{
@@ -227,14 +235,14 @@ static std::unordered_map<String, int, StringHasher> modes = {
{ "select_navigation", WorldEditor::MODE_NAVIGATION },
{ "select_poi", WorldEditor::MODE_POI },
{ "select_road_lines", WorldEditor::MODE_ROAD_LINES },
{ "select_npc", WorldEditor::MODE_NPC }
{ "select_npc", WorldEditor::MODE_NPC },
{ "select_terrain", WorldEditor::MODE_TERRAIN },
};
static std::unordered_map<int, String> vmode = { { 2, "%v_buildings" },
{ 3, "%v_navigation" },
{ 5, "%v_poi" },
{ 6, "%v_road_lines" },
{ 7, "%v_npc" } };
static std::unordered_map<int, String> vmode = {
{ 2, "%v_buildings" }, { 3, "%v_navigation" }, { 5, "%v_poi" },
{ 6, "%v_road_lines" }, { 7, "%v_npc" }, { 8, "%v_terrain" },
};
void WorldEditor::tools_button(const String &button)
{
@@ -257,6 +265,8 @@ void WorldEditor::tools_button(const String &button)
case MODE_BUILDINGS:
buildings_editor->exit();
break;
case MODE_TERRAIN:
terrain_editor->exit();
}
switch (modes[button]) {
case MODE_BUILDINGS:
@@ -274,6 +284,9 @@ void WorldEditor::tools_button(const String &button)
case MODE_NPC:
mode_npc();
break;
case MODE_TERRAIN:
mode_terrain();
break;
}
EditorEvent::get_singleton()->event.emit(
"mode_change_post", varray(current_mode, modes[button]));
@@ -404,6 +417,14 @@ void WorldEditor::event_signal_handler(const String &event,
emit_signal("editor_event", event, args);
}
Ref<VoxelGeneratorImgMapper> WorldEditor::get_heightmap()
{
StreamWorld *world = get_stream_world();
VoxelLodTerrain *terrain = world->get_terrain();
assert(terrain);
return terrain->get_generator();
}
StreamWorld *WorldEditor::get_stream_world()
{
return stream_world;
@@ -417,8 +438,9 @@ void WorldEditor::world_command_result(const String &what,
}
static std::vector<String> tool_buttons = {
"%select_buildings", "%select_navigation", "%select_poi",
"%select_road_lines", "%select_npc", "%buildings_save",
"%select_terrain", "%select_buildings", "%select_navigation",
"%select_poi", "%select_road_lines", "%select_npc",
"%buildings_save",
};
std::vector<HandleCommandButton *> tool_handlers;
void WorldEditor::_notification(int which)
@@ -469,6 +491,10 @@ void WorldEditor::_notification(int which)
get_node(NodePath("%line_cursor")));
assert(line_cursor);
line_cursor->hide();
Spatial *terrain_cursor = Object::cast_to<Spatial>(
get_node(NodePath("%terrain_cursor")));
assert(terrain_cursor);
terrain_cursor->hide();
set_process_unhandled_input(true);
set_physics_process(true);
@@ -529,6 +555,9 @@ void WorldEditor::_notification(int which)
case MODE_ROAD_LINES:
road_lines_editor->update(delta);
break;
case MODE_TERRAIN:
terrain_editor->update(delta);
break;
}
} break;
}

View File

@@ -4,10 +4,12 @@
#include <cassert>
#include <list>
#include <scene/3d/spatial.h>
#include <modules/imgmapper/voxel_generator_imgmapper.h>
#include "stream.h"
#include "base_data.h"
class RoadLinesEditor;
class BuildingsEditor;
class TerrainEditor;
class WorldEditor : public Spatial {
GDCLASS(WorldEditor, Spatial)
protected:
@@ -20,6 +22,7 @@ protected:
void mode_poi();
void mode_road_lines();
void mode_npc();
void mode_terrain();
void tools_button(const String &button);
StreamWorld *get_stream_world();
void world_command_result(const String &what,
@@ -38,6 +41,7 @@ private:
float drag_delay;
RoadLinesEditor *road_lines_editor;
BuildingsEditor *buildings_editor;
TerrainEditor *terrain_editor;
public:
enum {
@@ -46,6 +50,7 @@ public:
MODE_POI = 5,
MODE_ROAD_LINES = 6,
MODE_NPC = 7,
MODE_TERRAIN = 8,
};
WorldEditor();
virtual ~WorldEditor();
@@ -54,6 +59,7 @@ public:
int get_current_mode() const;
void event_signal_handler(const String &event,
const Vector<Variant> &args);
Ref<VoxelGeneratorImgMapper> get_heightmap();
struct components {
struct world_editor_node {
WorldEditor *node;