Proper brush configuration

This commit is contained in:
2025-01-03 04:22:54 +03:00
parent b7b746d000
commit 59a99ff706
7 changed files with 312 additions and 229 deletions

View File

@@ -45,11 +45,240 @@ namespace
} // namespace
std::map<int, struct EditBrush *> EditBrushList::brushes;
std::map<int, String> EditBrushList::brush_names;
struct Brush0 : EditBrush
{
Brush0(Ref<Image> &image) : EditBrush(0, image, "Brush0")
{
}
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, "Brush1")
{
}
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, "Brush2"), 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, "Brush3"), 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, "Brush4"), 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, "Brush5"), 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));
}
}
}
};
template <typename T, class... Args>
T *RegBrush(Args &&...args)
{
static T data(std::forward<Args>(args)...);
EditBrushList::register_brush(&data);
return &data;
}
VoxelGeneratorImgMapper::VoxelGeneratorImgMapper()
: VoxelGeneratorHeightmap(),
world_size(10240),
grid_size(0)
{
RegBrush<Brush0>(_image_draw);
RegBrush<Brush1>(_image_draw);
RegBrush<Brush2>(_image_draw, curve1);
RegBrush<Brush3>(_image_draw, curve1, this);
RegBrush<Brush4>(_image_draw, curve2, this);
RegBrush<Brush5>(_image_draw, this);
}
VoxelGeneratorImgMapper::~VoxelGeneratorImgMapper()
@@ -215,232 +444,11 @@ 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;
@@ -448,7 +456,7 @@ void VoxelGeneratorImgMapper::draw_brush(const Vector3 &v, float r, float s, int
int xs = 3 * r;
float base_h = get_height_full(v);
float scale = s / 100.0f;
EditBrush::draw_brush(v, r, s, id);
EditBrushList::draw_brush(v, r, s, id);
#if 0
switch (id)
{
@@ -747,6 +755,11 @@ int VoxelGeneratorImgMapper::get_world_size() const
return world_size;
}
std::map<int, String> VoxelGeneratorImgMapper::get_brush_names() const
{
return EditBrushList::get_brush_names();
}
void VoxelGeneratorImgMapper::_bind_methods()
{
ClassDB::bind_method(D_METHOD("set_image_bg", "image"), &VoxelGeneratorImgMapper::set_image_bg);

View File

@@ -1,5 +1,7 @@
#ifndef HEADER_VOXEL_GENERATOR_IMGMAPPER
#define HEADER_VOXEL_GENERATOR_IMGMAPPER
#undef NDEBUG
#include <cassert>
#include <map>
#include <modules/voxel/generators/simple/voxel_generator_heightmap.h>
#include <core/image.h>
@@ -7,23 +9,42 @@
struct EditBrush
{
static std::map<int, struct EditBrush *> brushes;
int id;
String name;
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)
EditBrush(int id, Ref<Image> &im, const String &name) : id(id), name(name), draw_image(im)
{
brushes[id] = this;
}
virtual ~EditBrush()
{
brushes.erase(id);
}
};
struct EditBrushList
{
static std::map<int, struct EditBrush *> brushes;
static std::map<int, String> brush_names;
public:
template <typename T>
static void register_brush(T *brush)
{
int id = brush->id;
brushes[id] = brush;
brush_names[id] = brush->name;
}
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);
}
static const std::map<int, String> &get_brush_names()
{
assert(brushes.size() > 0);
assert(brush_names.size() > 0);
return brush_names;
}
};
// Provides infinite tiling heightmap based on an image
@@ -75,6 +96,7 @@ public:
void set_world_size(int value);
int get_world_size() const;
std::map<int, String> get_brush_names() const;
private:
static void _bind_methods();

View File

@@ -385,6 +385,11 @@ void StreamWorld::_notification(int which)
ERR_FAIL_COND_MSG(!current_scene, "No current scene");
RoadProcessing::road_setup(this, 0);
set_process(true);
if (Engine::get_singleton()->is_editor_hint())
break;
update_view();
assert(terrain);
assert(viewer);
}
break;
case NOTIFICATION_EXIT_TREE:

View File

@@ -60,6 +60,21 @@ void TerrainEditor::activate()
EditorEvent::get_singleton()->event.add_listener(
this, &TerrainEditor::event_handler);
assert(editor->is_inside_tree());
imgmapper = editor->get_heightmap();
assert(imgmapper.is_valid());
const std::map<int, String> &brush_names = imgmapper->get_brush_names();
assert(brush_names.size() > 0);
Dictionary brushes;
std::map<int, String>::const_iterator ib = brush_names.begin();
while (ib != brush_names.end()) {
std::pair<int, String> ibp = *ib;
brushes[ibp.first] = ibp.second;
ib++;
}
assert(brushes.size() > 0);
EditorEvent::get_singleton()->event.emit("terrain_update_brushes",
varray(brushes));
active = true;
}

View File

@@ -2,6 +2,7 @@
#ifndef TERRAIN_EDITOR_H_
#define TERRAIN_EDITOR_H_
class WorldEditor;
class VoxelGeneratorImgMapper;
class TerrainEditor {
WorldEditor *editor;
bool active;
@@ -9,6 +10,7 @@ class TerrainEditor {
String cursor_name;
bool camera_moved;
Vector3 camera_motion;
Ref<VoxelGeneratorImgMapper> imgmapper;
public:
void exit();

View File

@@ -71,6 +71,22 @@ void MainTabs::handle_event(const String &event, const Vector<Variant> &args)
if (ok)
EditorEvent::get_singleton()->event.emit(
"buildings_building_selected", varray());
} else if (event == "terrain_update_brushes") {
OptionButton *brushes = Object::cast_to<OptionButton>(
get_node(NodePath("%terrain_select_brush")));
assert(brushes);
assert(args.size() == 1);
const Dictionary &brush_data = args[0];
assert(brush_data.size() > 0);
List<Variant> brush_keys;
List<Variant>::Element *be;
brush_data.get_key_list(&brush_keys);
be = brush_keys.front();
while (be) {
int id = be->get();
brushes->add_item(brush_data[id], id);
be = be->next();
}
}
}
@@ -177,13 +193,22 @@ void MainTabs::_notification(int which)
add_child(tab);
switch (i) {
case 0: {
/* clang-format off */
std::vector<Variant> args_data = {
items[i].header
items[i].header,
/* 0 */
"Brush",
/* 1 */
"Brush", 0, "terrain_select_brush",
};
HashMap<String, Object *> save_data;
/* clang-format on */
ui_field::ui_field_builder(this, tab,
"l_p{v{}}",
"l_p{v{lo#!$}}",
args_data.data(),
args_data.size());
args_data.size(),
&save_data);
assert(save_data.has("terrain_select_brush"));
} break;
case 1: {
// VBoxContainer *v = memnew(VBoxContainer);

View File

@@ -420,6 +420,7 @@ void WorldEditor::event_signal_handler(const String &event,
Ref<VoxelGeneratorImgMapper> WorldEditor::get_heightmap()
{
StreamWorld *world = get_stream_world();
assert(world);
VoxelLodTerrain *terrain = world->get_terrain();
assert(terrain);
return terrain->get_generator();