Updated systems handling

This commit is contained in:
2025-02-08 22:32:21 +03:00
parent 7518a6a360
commit 2780fd300a
31 changed files with 1172 additions and 539 deletions

View File

@@ -15,7 +15,7 @@ godot-main: patch
scons platform=server target=release_debug tools=yes custom_modules=../modules -j16
godot-editor-main: patch
cd src/godot; \
scons platform=x11 arch=$(ARCH) target=release_debug tools=yes custom_modules=../modules -j16;
scons platform=x11 arch=$(ARCH) verbose=yes target=release_debug tools=yes custom_modules=../modules -j16;
patch: ./src/godot/scene/animation/skeleton_ik.cpp
cd ./src/godot && git reset --hard HEAD && for p in ../patches/*.patch; do git apply $$p; done
sed -e 's/ERR_FAIL_COND_V(-1 == p_task->root_bone, false);//g' -i ./src/godot/scene/animation/skeleton_ik.cpp

View File

@@ -225,7 +225,7 @@ for mapping in [ExportMappingFemale(), ExportMappingMale(), ExportMappingMaleBab
obj.name = obj.name + "-noimp"
bpy.ops.wm.save_as_mainfile(filepath=(basepath + "/assets/blender/scripts/" + mapping.outfile))
bpy.ops.export_scene.gltf(filepath=mapping.gltf_path.replace(".npc", ".gltf"),
bpy.ops.export_scene.gltf(filepath=mapping.gltf_path.replace(".npcshape", ".gltf").replace(".npc", ".gltf"),
use_selection=False,
check_existing=False,
export_format='GLTF_SEPARATE',
@@ -252,8 +252,8 @@ for mapping in [ExportMappingFemale(), ExportMappingMale(), ExportMappingMaleBab
export_morph_normal=True,
export_morph_tangent=True,
export_lights=False,
export_skins=False)
shutil.move(mapping.gltf_path.replace(".npc", ".gltf"), mapping.gltf_path)
export_skins=True)
shutil.move(mapping.gltf_path.replace(".npcshape", ".gltf").replace(".npc", ".gltf"), mapping.gltf_path)
bpy.ops.wm.read_homefile(use_empty=True)

View File

@@ -17,6 +17,7 @@ if bpy.app.version[0] == 3:
from mixamo import mixamo_rig
from mixamo.lib.armature import *
from settings import VRMDataFemale, VRMDataMale, VRMDataMaleBabyShape, basepath
from geometry import tris2quads
imports = [VRMDataFemale(), VRMDataMale(), VRMDataMaleBabyShape()]
@@ -81,6 +82,9 @@ for imp in imports:
result = bpy.ops.import_scene.vrm(filepath=(basepath + "/assets/vroid/" + imp.path))
if result != {"FINISHED"}:
raise Exception(f"Failed to import vrm: {result}")
for o in bpy.data.objects:
if o.type == 'MESH':
tris2quads.tris2quads(o)
armature_count = 0
for obj in bpy.data.objects:
if (obj.type == "ARMATURE"):

View File

@@ -53,7 +53,7 @@ class ExportMappingMale:
class ExportMappingMaleBabyShape:
blend_path = "assets/blender/shapes/male/" + "vrm-vroid-normal-male-chibi.blend"
gltf_path = "godot/character-data/vroid-normal-male-chibi.npc"
gltf_path = "godot/character-data/vroid-normal-male-chibi.npcshape"
inner_path = "Object"
objs = ["male", "Body", "Hair", "Face"]
armature_name = "male"

View File

@@ -133,166 +133,6 @@ margin_top = 8.0
margin_right = 248.0
margin_bottom = 316.0
[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/v_road_lines/road_lines_base"]
margin_left = 7.0
margin_top = 7.0
margin_right = 241.0
margin_bottom = 301.0
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/v_road_lines/road_lines_base/VBoxContainer"]
margin_top = 50.0
margin_right = 234.0
margin_bottom = 74.0
[node name="Label" type="Label" parent="VBoxContainer/v_road_lines/road_lines_base/VBoxContainer/HBoxContainer"]
margin_top = 5.0
margin_right = 41.0
margin_bottom = 19.0
text = "Filter: "
[node name="road_lines_filter" type="LineEdit" parent="VBoxContainer/v_road_lines/road_lines_base/VBoxContainer/HBoxContainer"]
unique_name_in_owner = true
margin_left = 45.0
margin_right = 234.0
margin_bottom = 24.0
size_flags_horizontal = 3
[node name="lines_list" type="ItemList" parent="VBoxContainer/v_road_lines/road_lines_base/VBoxContainer"]
unique_name_in_owner = true
margin_top = 78.0
margin_right = 234.0
margin_bottom = 158.0
rect_min_size = Vector2( 0, 80 )
size_flags_horizontal = 3
[node name="line_index" type="SpinBox" parent="VBoxContainer/v_road_lines/road_lines_base/VBoxContainer"]
unique_name_in_owner = true
margin_top = 162.0
margin_right = 234.0
margin_bottom = 186.0
[node name="HSeparator" type="HSeparator" parent="VBoxContainer/v_road_lines/road_lines_base/VBoxContainer"]
margin_top = 190.0
margin_right = 234.0
margin_bottom = 194.0
[node name="Label2" type="Label" parent="VBoxContainer/v_road_lines/road_lines_base/VBoxContainer"]
margin_top = 198.0
margin_right = 234.0
margin_bottom = 212.0
text = "Cursor position"
[node name="road_lines_cursor_position" type="VBoxContainer" parent="VBoxContainer/v_road_lines/road_lines_base/VBoxContainer"]
unique_name_in_owner = true
margin_top = 216.0
margin_right = 234.0
margin_bottom = 240.0
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/v_road_lines/road_lines_base/VBoxContainer/road_lines_cursor_position"]
margin_right = 234.0
margin_bottom = 24.0
[node name="cursor_x" type="LineEdit" parent="VBoxContainer/v_road_lines/road_lines_base/VBoxContainer/road_lines_cursor_position/HBoxContainer"]
unique_name_in_owner = true
margin_right = 63.0
margin_bottom = 24.0
size_flags_horizontal = 3
[node name="cursor_y" type="LineEdit" parent="VBoxContainer/v_road_lines/road_lines_base/VBoxContainer/road_lines_cursor_position/HBoxContainer"]
unique_name_in_owner = true
margin_left = 67.0
margin_right = 130.0
margin_bottom = 24.0
size_flags_horizontal = 3
[node name="cursor_z" type="LineEdit" parent="VBoxContainer/v_road_lines/road_lines_base/VBoxContainer/road_lines_cursor_position/HBoxContainer"]
unique_name_in_owner = true
margin_left = 134.0
margin_right = 197.0
margin_bottom = 24.0
size_flags_horizontal = 3
[node name="road_lines_set_cursor_position" type="Button" parent="VBoxContainer/v_road_lines/road_lines_base/VBoxContainer/road_lines_cursor_position/HBoxContainer"]
unique_name_in_owner = true
margin_left = 201.0
margin_right = 233.0
margin_bottom = 24.0
text = "Set"
[node name="HSeparator4" type="HSeparator" parent="VBoxContainer/v_road_lines/road_lines_base/VBoxContainer"]
margin_top = 244.0
margin_right = 234.0
margin_bottom = 248.0
[node name="Label3" type="Label" parent="VBoxContainer/v_road_lines/road_lines_base/VBoxContainer"]
margin_top = 252.0
margin_right = 234.0
margin_bottom = 266.0
text = "Point position"
[node name="road_lines_point_position" type="VBoxContainer" parent="VBoxContainer/v_road_lines/road_lines_base/VBoxContainer"]
unique_name_in_owner = true
margin_top = 270.0
margin_right = 234.0
margin_bottom = 294.0
[node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer/v_road_lines/road_lines_base/VBoxContainer/road_lines_point_position"]
margin_right = 234.0
margin_bottom = 24.0
[node name="point_x" type="LineEdit" parent="VBoxContainer/v_road_lines/road_lines_base/VBoxContainer/road_lines_point_position/HBoxContainer2"]
unique_name_in_owner = true
margin_right = 63.0
margin_bottom = 24.0
size_flags_horizontal = 3
[node name="point_y" type="LineEdit" parent="VBoxContainer/v_road_lines/road_lines_base/VBoxContainer/road_lines_point_position/HBoxContainer2"]
unique_name_in_owner = true
margin_left = 67.0
margin_right = 130.0
margin_bottom = 24.0
size_flags_horizontal = 3
[node name="point_z" type="LineEdit" parent="VBoxContainer/v_road_lines/road_lines_base/VBoxContainer/road_lines_point_position/HBoxContainer2"]
unique_name_in_owner = true
margin_left = 134.0
margin_right = 197.0
margin_bottom = 24.0
size_flags_horizontal = 3
[node name="road_lines_set_point_position" type="Button" parent="VBoxContainer/v_road_lines/road_lines_base/VBoxContainer/road_lines_point_position/HBoxContainer2"]
unique_name_in_owner = true
margin_left = 201.0
margin_right = 233.0
margin_bottom = 24.0
text = "Set"
[node name="road_lines_create_new_line_dlg" type="PanelContainer" parent="VBoxContainer/v_road_lines"]
unique_name_in_owner = true
margin_top = 320.0
margin_right = 248.0
margin_bottom = 382.0
[node name="v" type="VBoxContainer" parent="VBoxContainer/v_road_lines/road_lines_create_new_line_dlg"]
margin_left = 7.0
margin_top = 7.0
margin_right = 241.0
margin_bottom = 55.0
[node name="road_lines_create_new_line_name" type="LineEdit" parent="VBoxContainer/v_road_lines/road_lines_create_new_line_dlg/v"]
unique_name_in_owner = true
margin_right = 234.0
margin_bottom = 24.0
[node name="road_lines_create_new_cancel" type="Button" parent="VBoxContainer/v_road_lines/road_lines_create_new_line_dlg/v"]
unique_name_in_owner = true
margin_top = 28.0
margin_right = 234.0
margin_bottom = 48.0
text = "Cancel"
[node name="road_lines_edit_metadata_dlg" type="PanelContainer" parent="VBoxContainer/v_road_lines"]
unique_name_in_owner = true
margin_top = 386.0

View File

@@ -5,17 +5,19 @@ Import("env_modules")
# Godot source files
module_obj = []
env_stream = env_modules.Clone()
env_stream.module_obj = []
env_stream.Prepend(CPPPATH=["../../meshoptimizer/src"])
env_stream.Prepend(CPPPATH=["event"])
env_stream.add_source_files(env_stream.module_obj, "*.cpp")
env_stream.add_source_files(env_stream.module_obj, "flecs/*.c")
env.modules_sources += env_stream.module_obj
SConscript("buildings/SCsub")
SConscript("rtree/SCsub")
SConscript("ui/SCsub")
SConscript("npc/SCsub")
SConscript("event/SCsub")
env_stream = env_modules.Clone()
env_stream.Prepend(CPPPATH=["../../meshoptimizer/src"])
env_stream.add_source_files(module_obj, "*.cpp")
env_stream.add_source_files(module_obj, "flecs/*.c")
env.modules_sources += module_obj

View File

@@ -1,32 +0,0 @@
#undef NDEBUG
#include <cassert>
#include "editor_event.h"
EditorEvent *EditorEvent::singleton = nullptr;
EditorEvent::EditorEvent()
{
}
EditorEvent::~EditorEvent()
{
}
EditorEvent *EditorEvent::get_singleton()
{
if (!singleton)
singleton = memnew(EditorEvent);
return singleton;
}
void EditorEvent::EventHelper::emit(const String &event,
const Vector<Variant> &args)
{
auto evl = listeners.begin();
while (evl != listeners.end()) {
const event_listener_ptrs &xev = *evl;
xev.execute(event, args);
evl++;
}
}

View File

@@ -1,81 +0,0 @@
#ifndef EDITOR_EVENT_H
#define EDITOR_EVENT_H
#include <core/ustring.h>
#include <list>
class EditorEvent {
public:
class EventHelper {
class event_listener_ptrs {
public:
class H {};
H *obj;
void (H::*method)(const String &event,
const Vector<Variant> &args);
void execute(const String &event,
const Vector<Variant> &args) const
{
(obj->*method)(event, args);
}
};
std::list<event_listener_ptrs> listeners;
typedef event_listener_ptrs::H *obj_t;
typedef void (event_listener_ptrs::H::*method_t)(
const String &event, const Vector<Variant> &args);
public:
template <class T>
void
add_listener(T *obj,
void (T::*method)(const String &event,
const Vector<Variant> &args));
template <class T>
void
remove_listener(T *obj,
void (T::*method)(const String &event,
const Vector<Variant> &args));
void emit(const String &event, const Vector<Variant> &args);
};
EventHelper event;
private:
static EditorEvent *singleton;
EditorEvent();
virtual ~EditorEvent();
public:
static EditorEvent *get_singleton();
};
template <class T>
void EditorEvent::EventHelper::remove_listener(
T *obj,
void (T::*method)(const String &event, const Vector<Variant> &args))
{
listeners.remove_if([obj, method](const event_listener_ptrs &e) {
return e.obj == reinterpret_cast<obj_t>(obj) &&
e.method == reinterpret_cast<method_t>(method);
});
}
template <class T>
void EditorEvent::EventHelper::add_listener(
T *obj,
void (T::*method)(const String &event, const Vector<Variant> &args))
{
auto evl = listeners.begin();
bool bad = false;
while (evl != listeners.end()) {
const event_listener_ptrs &xev = *evl;
if (xev.obj == reinterpret_cast<obj_t>(obj) &&
xev.method == reinterpret_cast<method_t>(method)) {
bad = true;
break;
}
evl++;
}
if (bad)
return;
event_listener_ptrs ev;
ev.obj = reinterpret_cast<obj_t>(obj);
ev.method = reinterpret_cast<method_t>(method);
listeners.push_back(ev);
}
#endif

View File

@@ -1,11 +1,14 @@
Import("env")
Import("env_modules")
env.stream_building_sources = []
env_stream = env_modules.Clone()
env_stream.stream_building_sources = []
env.add_source_files(env.stream_building_sources, "*.cpp")
env_stream.add_source_files(env_stream.stream_building_sources, "*.cpp")
lib = env.add_library("npc", env.stream_building_sources)
env.Prepend(LIBS=[lib])
env.Prepend(CPPPATH=[".."])
env.Prepend(CPPPATH=["../../../meshoptimizer/src"])
lib = env_stream.add_library("npc", env_stream.stream_building_sources)
env.Append(LIBS=[lib])
env_stream.Prepend(CPPPATH=[".."])
env_stream.Prepend(CPPPATH=["../../../meshoptimizer/src"])
env_stream.Prepend(CPPPATH=["../event"])
env_stream.Prepend(CPPPATH=["../persistent_data"])

View File

@@ -3,6 +3,9 @@
#include <cassert>
#include <modules/gltf/packed_scene_gltf.h>
#include <core/io/config_file.h>
#include <core/io/compression.h>
#include <scene/resources/mesh_library.h>
#include "skeleton_data.h"
#include "importer.h"
#ifdef TOOLS_ENABLED
@@ -18,6 +21,7 @@ uint32_t EditorImportNPC::get_import_flags() const
void EditorImportNPC::get_extensions(List<String> *r_extensions) const
{
r_extensions->push_back("npc");
r_extensions->push_back("npcshape");
}
Node *EditorImportNPC::import_scene(const String &p_path, uint32_t p_flags,
int p_bake_fps, uint32_t p_compress_flags,
@@ -27,13 +31,26 @@ Node *EditorImportNPC::import_scene(const String &p_path, uint32_t p_flags,
List<Node *> queue;
Ref<PackedSceneGLTF> importer;
ConfigFile config;
String config_path =
p_path.replace(".npc", ".conf")
.replace("/character-data/", "/character/");
Error err = config.load(config_path);
print_line("config path: " + config_path);
if (err == OK)
print_line("config loaded...");
String config_path;
bool is_shape = false;
if (p_path.ends_with(".npc")) {
config_path =
p_path.replace(".npc", ".conf")
.replace("/character-data/", "/character/");
Error err = config.load(config_path);
print_line("config path: " + config_path);
if (err == OK)
print_line("config loaded...");
} else if (p_path.ends_with(".npcshape")) {
is_shape = true;
config_path =
p_path.replace(".npcshape", ".conf")
.replace("/character-data/", "/character/");
Error err = config.load(config_path);
print_line("config path: " + config_path);
if (err == OK)
print_line("config loaded...");
}
importer.instance();
result = importer->import_scene(p_path, p_flags, p_bake_fps,
p_compress_flags, r_missing_deps, r_err,
@@ -92,6 +109,8 @@ Node *EditorImportNPC::import_scene(const String &p_path, uint32_t p_flags,
ArrayMesh::PrimitiveType ptype;
Array surface, bshapes;
String split_name = split[0];
print_line("split_name: " +
split_name);
MeshInstance *split_mi;
if (split_meshes.has(
split_name))
@@ -131,138 +150,155 @@ Node *EditorImportNPC::import_scene(const String &p_path, uint32_t p_flags,
new_mesh->get_surface_count() -
1,
mat);
}
}
#if 0
if (mesh_name == "body") {
if (mesh.is_valid()) {
Ref<ArrayMesh> new_body,
clothes;
new_body.instance();
new_body->set_storage_mode(
ArrayMesh::
STORAGE_MODE_CPU);
int surf_count =
mesh->get_surface_count();
if (surf_count > 0) {
MeshInstance *clothes_mi =
memnew(MeshInstance);
mi->get_parent()->add_child(
clothes_mi);
clothes_mi->set_owner(
mi->get_owner());
clothes.instance();
clothes->set_storage_mode(
print_line(
"split mesh created");
if (!is_shape &&
config.has_section_key(
surf_section,
"shape_path")) {
print_line("not shape");
String shape_path =
config.get_value(
surf_section,
"shape_path");
if (shape_path.find(
".meshlib") <
0)
shape_path +=
".meshlib";
Ref<ArrayMesh>
shape_mesh;
shape_mesh.instance();
shape_mesh->set_storage_mode(
ArrayMesh::
STORAGE_MODE_CPU);
clothes_mi->set_mesh(
clothes);
}
for (i = 0; i < surf_count;
i++) {
uint32_t sformat =
mesh->surface_get_format(
i);
ArrayMesh::PrimitiveType ptype =
mesh->surface_get_primitive_type(
i);
Array surface =
mesh->surface_get_arrays(
i);
Array bshapes =
mesh->surface_get_blend_shape_arrays(
i);
if (i == 0)
new_body->add_surface_from_arrays(
shape_mesh
->add_surface_from_arrays(
ptype,
surface,
bshapes);
else
clothes->add_surface_from_arrays(
ptype,
surface,
bshapes);
Ref<SpatialMaterial> mat =
mesh->surface_get_material(
i);
if (i == 0)
new_body->surface_set_material(
i, mat);
else
clothes->surface_set_material(
i - 1,
mat);
if (mat.is_valid()) {
String pname =
mat->get_name();
assert(pname.size() >
0);
if (i == 0)
new_body->surface_set_name(
i,
pname);
else
clothes->surface_set_name(
i - 1,
pname);
}
}
for (i = 0; i < surf_count;
i++) {
Ref<SpatialMaterial> mat =
mesh->surface_get_material(
i);
if (mat.is_valid()) {
mat->set_feature(
SpatialMaterial::
FEATURE_TRANSPARENT,
false);
if (i >= 2)
mat->set_flag(
SpatialMaterial::
FLAG_USE_ALPHA_SCISSOR,
true);
else
mat->set_flag(
SpatialMaterial::
FLAG_USE_ALPHA_SCISSOR,
false);
}
}
mi->set_mesh(new_body);
new_body->set_storage_mode(
ArrayMesh::
STORAGE_MODE_GPU);
if (clothes.is_valid())
clothes->set_storage_mode(
surface);
shape_mesh->set_storage_mode(
ArrayMesh::
STORAGE_MODE_GPU);
}
}
if (mesh_name == "hair") {
if (mesh.is_valid()) {
int surf_count =
mesh->get_surface_count();
for (i = 0; i < surf_count;
i++) {
Ref<SpatialMaterial> mat =
mesh->surface_get_material(
i);
if (mat.is_valid()) {
mat->set_feature(
SpatialMaterial::
FEATURE_TRANSPARENT,
false);
mat->set_flag(
SpatialMaterial::
FLAG_USE_ALPHA_SCISSOR,
false);
Ref<MeshLibrary> meshlib;
String meshlib_path =
shape_path;
if (ResourceLoader::exists(
meshlib_path,
"MeshLibrary")) {
meshlib = ResourceLoader::load(
shape_path,
"MeshLibrary",
true);
}
if (!meshlib.is_valid())
meshlib.instance();
int item_id =
meshlib->find_item_by_name(
split_name);
if (item_id < 0) {
item_id =
meshlib->get_last_unused_item_id();
meshlib->create_item(
item_id);
meshlib->set_item_name(
item_id,
split_name);
}
meshlib->set_item_mesh(
item_id,
shape_mesh);
ResourceSaver::save(
shape_path,
meshlib);
#if 0
if (shape_path.find(".bshape") < 0)
shape_path += ".bshape";
build_shape_base(
surface,
shape_path);
#endif
} else if (is_shape &&
config.has_section_key(
surf_section,
"shape_path")) {
print_line(
"is a shape");
String shape_path =
config.get_value(
surf_section,
"shape_path");
if (shape_path.find(
".meshlib") <
0)
shape_path +=
".meshlib";
Ref<MeshLibrary> meshlib;
String meshlib_path =
shape_path;
if (ResourceLoader::exists(
meshlib_path,
"MeshLibrary")) {
meshlib = ResourceLoader::load(
shape_path,
"MeshLibrary",
true);
} else
print_error(
shape_path +
"does not exist");
if (meshlib.is_valid() &&
config.has_section_key(
surf_section,
"base_mesh")) {
String item_name =
config.get_value(
surf_section,
"base_mesh");
int item_id = meshlib->find_item_by_name(
item_name);
if (!config.has_section_key(
surf_section,
"shape"))
goto out;
if (item_id <
0) {
print_error(
"Item not found: " +
item_name);
goto out;
} else {
Ref<ArrayMesh> base_mesh =
meshlib->get_item_mesh(
item_id);
if (!base_mesh
.is_valid()) {
print_error(
"Mesh is not valid");
goto out;
}
Array base_surface =
base_mesh
->surface_get_arrays(
0);
String out_path = config.get_value(
surf_section,
"shape",
String());
if (out_path.find(
".bshape") <
0)
out_path +=
".bshape";
build_shape_base(
base_surface,
surface,
out_path);
}
}
out:;
}
}
}
#endif
List<String> mesh_keys;
split_meshes.get_key_list(&mesh_keys);
List<String>::Element *e = mesh_keys.front();
@@ -274,6 +310,51 @@ Node *EditorImportNPC::import_scene(const String &p_path, uint32_t p_flags,
}
do_destroy =
config.get_value(section, "destroy", false);
} else {
Skeleton *skeleton = Object::cast_to<Skeleton>(item);
if (skeleton && !is_shape) {
if (config.has_section_key("skeleton",
"path")) {
Ref<SkeletonData> skeleton_data;
String skpath = config.get_value(
"skeleton", "path", String());
if (!skpath.ends_with(".res"))
skpath += ".res";
skeleton_data.instance();
skeleton_data->save_skeleton(skeleton);
ResourceSaver::save(skpath,
skeleton_data);
}
} else if (skeleton && is_shape) {
if (config.has_section_key("skeleton",
"path") &&
config.has_section_key("skeleton",
"bone_delta")) {
Ref<SkeletonData> skeleton_data;
String skpath = config.get_value(
"skeleton", "path", String());
if (!skpath.ends_with(".res"))
skpath += ".res";
skeleton_data = ResourceLoader::load(
skpath, "SkeletonData");
String bone_delta_path =
config.get_value("skeleton",
"bone_delta",
String());
if (!bone_delta_path.ends_with(
".bdelta"))
bone_delta_path += ".bdelta";
if (skeleton_data.is_valid()) {
Skeleton *base_skeleton =
memnew(Skeleton);
skeleton_data->load_skeleton(
base_skeleton);
build_bones_delta(
base_skeleton, skeleton,
bone_delta_path);
}
}
}
}
count = item->get_child_count();
for (i = 0; i < count; i++)
@@ -281,12 +362,231 @@ Node *EditorImportNPC::import_scene(const String &p_path, uint32_t p_flags,
if (do_destroy)
item->queue_delete();
}
print_line("all done");
return result;
}
typedef struct tri_v {
float x, y, cx, cy, cz;
} tri_p;
float edge_cross(const tri_p *a, const tri_p *b, const tri_p *p)
{
tri_p ab = { b->x - a->x, b->y - a->y };
tri_p ap = { p->x - a->x, p->y - a->y };
return ab.x * ap.y - ab.y * ap.x;
}
bool is_top_left(const tri_p *start, const tri_p *end)
{
tri_p edge = { end->x - start->x, end->y - start->y };
bool is_top_edge = edge.y == 0 && edge.x > 0;
bool is_left_edge = edge.y < 0;
return is_left_edge || is_top_edge;
}
static void triangle_fill(const tri_p &v0, const tri_p &v1, const tri_p &v2,
int stride, float *buffer)
{
// Finds the bounding box with all candidate pixels
float x_min = Math::floor(MIN(MIN(v0.x, v1.x), v2.x));
float y_min = Math::floor(MIN(MIN(v0.y, v1.y), v2.y));
float x_max = Math::ceil(MAX(MAX(v0.x, v1.x), v2.x));
float y_max = Math::ceil(MAX(MAX(v0.y, v1.y), v2.y));
float area = edge_cross(&v0, &v1, &v2);
// Compute the constant delta_s that will be used for the horizontal and vertical steps
float delta_w0_col = (v1.y - v2.y);
float delta_w1_col = (v2.y - v0.y);
float delta_w2_col = (v0.y - v1.y);
float delta_w0_row = (v2.x - v1.x);
float delta_w1_row = (v0.x - v2.x);
float delta_w2_row = (v1.x - v0.x);
// Rasterization fill rule, not 100% precise due to floating point innacuracy
float bias0 = is_top_left(&v1, &v2) ? 0 : -0.0001;
float bias1 = is_top_left(&v2, &v0) ? 0 : -0.0001;
float bias2 = is_top_left(&v0, &v1) ? 0 : -0.0001;
// Compute the edge functions for the fist (top-left) point
tri_p p0 = { x_min + 0.5f, y_min + 0.5f };
float w0_row = edge_cross(&v1, &v2, &p0) + bias0;
float w1_row = edge_cross(&v2, &v0, &p0) + bias1;
float w2_row = edge_cross(&v0, &v1, &p0) + bias2;
// Loop all candidate pixels inside the bounding box
for (int y = y_min; y <= y_max; y++) {
float w0 = w0_row;
float w1 = w1_row;
float w2 = w2_row;
for (int x = x_min; x <= x_max; x++) {
bool is_inside = w0 >= 0 && w1 >= 0 && w2 >= 0;
if (is_inside) {
float alpha = w0 / area;
float beta = w1 / area;
float gamma = w2 / area;
// draw_pixel(x, y, color);
buffer[y * stride + x * 4 + 0] =
(alpha * v0.cx + beta * v1.cx +
gamma * v2.cx);
buffer[y * stride + x * 4 + 1] =
(alpha * v0.cy + beta * v1.cy +
gamma * v2.cy);
buffer[y * stride + x * 4 + 2] =
(alpha * v0.cz + beta * v1.cz +
gamma * v2.cz);
buffer[y * stride + x * 4 + 0] = 0;
}
w0 += delta_w0_col;
w1 += delta_w1_col;
w2 += delta_w2_col;
}
w0_row += delta_w0_row;
w1_row += delta_w1_row;
w2_row += delta_w2_row;
}
}
void EditorImportNPC::build_shape_base(const Array &base_surface,
const Array &surfaces,
const String &path)
{
Error err = OK;
int size = 1024;
const PoolVector<Vector3> &vertices_base =
base_surface[ArrayMesh::ARRAY_VERTEX];
const PoolVector<Vector3> &vertices = surfaces[ArrayMesh::ARRAY_VERTEX];
const PoolVector<Vector3> &normals_base =
base_surface[ArrayMesh::ARRAY_NORMAL];
const PoolVector<Vector3> &normals = surfaces[ArrayMesh::ARRAY_NORMAL];
const PoolVector<Vector2> &uvs_base = surfaces[ArrayMesh::ARRAY_TEX_UV];
const PoolVector<Vector2> &uvs = surfaces[ArrayMesh::ARRAY_TEX_UV];
const PoolVector<int> &indices_base =
base_surface[ArrayMesh::ARRAY_INDEX];
const PoolVector<int> &indices = surfaces[ArrayMesh::ARRAY_INDEX];
float *buffer;
int i, j;
if (vertices.size() != vertices_base.size()) {
print_error("Bad shape mesh");
return;
}
if (normals.size() != normals_base.size()) {
print_error("Bad shape mesh");
return;
}
if (indices.size() != indices_base.size()) {
print_error("Bad shape mesh");
return;
}
buffer = memnew_arr(float, size *size * 4);
for (i = 0; i < size * size * 4; i++)
buffer[i] = 0.0f;
for (i = 0; i < indices.size(); i += 3) {
int i0 = indices[i + 0];
int i1 = indices[i + 1];
int i2 = indices[i + 2];
if (!uvs[i0].is_equal_approx(uvs_base[i0]) ||
!uvs[i1].is_equal_approx(uvs_base[i1]) ||
!uvs[i2].is_equal_approx(uvs_base[i2])) {
print_error("Bad shape mesh");
memdelete_arr(buffer);
return;
}
Vector3 delta0 = vertices[i0] - vertices_base[i0];
Vector3 delta1 = vertices[i1] - vertices_base[i1];
Vector3 delta2 = vertices[i2] - vertices_base[i2];
int amp = size - 1;
tri_p v0 = { uvs[i0].x * amp, uvs[i0].y * amp, delta0.x,
delta0.y, delta0.z };
tri_p v1 = { uvs[i1].x * amp, uvs[i1].y * amp, delta1.x,
delta1.y, delta1.z };
tri_p v2 = { uvs[i2].x * amp, uvs[i2].y * amp, delta2.x,
delta2.y, delta2.z };
triangle_fill(v0, v1, v2, size * 4, buffer);
}
float max_value = -Math_INF;
float min_value = Math_INF;
for (i = 0; i < size * size * 4; i++) {
if (max_value < buffer[i])
max_value = buffer[i];
if (min_value > buffer[i])
min_value = buffer[i];
}
float delta_value = max_value - min_value;
print_line("min_value: " + String::num(min_value) +
" max_value: " + String::num(max_value) +
" delta_value: " + String::num(delta_value));
FileAccess *fd = FileAccess::open(path, FileAccess::WRITE, &err);
if (fd) {
uint8_t *dst_buffer = memnew_arr(uint8_t, size * 3);
uint8_t *dbuffer = memnew_arr(uint8_t, size * 3);
fd->store_float(min_value);
fd->store_float(delta_value);
fd->store_32(size);
fd->store_32(size);
for (i = 0; i < size; i++) {
for (j = 0; j < size; j++) {
dbuffer[j * 3 + 0] =
((double)buffer[i * size + j * 4 + 0] -
(double)min_value) *
255.0 / (double)delta_value;
dbuffer[j * 3 + 1] =
((double)buffer[i * size + j * 4 + 1] -
(double)min_value) *
255.0 / (double)delta_value;
dbuffer[j * 3 + 2] =
((double)buffer[i * size + j * 4 + 2] -
(double)min_value) *
255.0 / (double)delta_value;
}
int cmp_size = Compression::compress(
dst_buffer, dbuffer, size * 3,
Compression::MODE_FASTLZ);
fd->store_32(cmp_size);
fd->store_buffer(dst_buffer, cmp_size);
}
fd->close();
memdelete_arr(dst_buffer);
memdelete_arr(dbuffer);
print_line("Stored shape data to: " + path);
}
memdelete_arr(buffer);
}
Ref<Animation> EditorImportNPC::import_animation(const String &p_path,
uint32_t p_flags,
int p_bake_fps)
{
return Ref<Animation>();
}
void EditorImportNPC::build_bones_delta(Skeleton *base_skeleton,
Skeleton *skeleton, const String &path)
{
int i;
Error err = OK;
if (base_skeleton->get_bone_count() != skeleton->get_bone_count()) {
print_error("bad skeleton");
return;
}
FileAccess *fd = FileAccess::open(path, FileAccess::WRITE, &err);
if (fd) {
Vector<String> delta_names;
Vector<Transform> delta_xforms;
for (i = 0; i < skeleton->get_bone_count(); i++) {
Transform r1 = base_skeleton->get_bone_rest(i);
Transform r2 = skeleton->get_bone_rest(i);
if (!r1.is_equal_approx(r2)) {
delta_names.push_back(
skeleton->get_bone_name(i));
delta_xforms.push_back(
skeleton->get_bone_rest(i));
}
}
for (i = 0; i < delta_names.size(); i++) {
fd->store_pascal_string(delta_names[i]);
fd->store_buffer((uint8_t *)&delta_xforms[i],
sizeof(Transform));
}
}
fd->close();
}
#endif

View File

@@ -15,6 +15,10 @@ protected:
int p_bake_fps, uint32_t p_compress_flags,
List<String> *r_missing_deps = nullptr,
Error *r_err = nullptr);
void build_shape_base(const Array &base_surface, const Array &surfaces,
const String &path);
void build_bones_delta(Skeleton *base_skeleton, Skeleton *skeleton,
const String &path);
virtual Ref<Animation> import_animation(const String &p_path,
uint32_t p_flags,
int p_bake_fps);

View File

@@ -1,20 +1,50 @@
#include <vector>
#include <algorithm>
#include <core/os/memory.h>
#include <core/vector.h>
#include <core/ustring.h>
#include <core/math/transform.h>
#include <core/object.h>
#include <scene/main/scene_tree.h>
#include "flecs/flecs.h"
#include <core/os/dir_access.h>
#include <scene/3d/skeleton.h>
#include <core/io/config_file.h>
#include "skeleton_data.h"
#include "base_data.h"
#include "editor_event.h"
#include "game_event.h"
#include "persistent_data.h"
#include "npc.h"
class NPCSaveData : public PersistentData::SaveHandler {
public:
NPC *npc;
NPCSaveData(NPC *npc)
: npc(npc)
{
}
void save(const String &slot_path)
{
}
void load(const String &slot_path)
{
}
};
NPC *NPC::singleton = nullptr;
namespace NPCModule
{
struct NPCBase {
int id;
String name;
};
struct NPCSkeleton {
String skeleton_path;
};
struct NPCNode {
Skeleton *skeleton;
};
struct NPCLocation {
@@ -67,6 +97,7 @@ struct module {
ecs.module<struct module>();
ecs.component<struct NPCBase>();
ecs.component<struct NPCLocation>();
ecs.component<struct NPCSkeleton>();
ecs.component<struct NPCTarget>();
ecs.component<struct NPCTargetKey>();
ecs.component<struct NPCBody>();
@@ -74,6 +105,59 @@ struct module {
ecs.component<struct NPCResidence>();
ecs.component<struct NPCOccupation>();
ecs.component<struct NPCLeisure>();
ecs.observer<struct NPCSkeleton>()
.event(flecs::OnSet)
.with<struct NPCBase>()
.with<struct NPCLocation>()
.write<struct NPCNode>()
.each([](flecs::entity e, const NPCSkeleton &s) {
Ref<SkeletonData> skeleton_data;
String path = s.skeleton_path;
if (e.has<NPCNode>()) {
if (e.get<NPCNode>()->skeleton) {
e.get_mut<NPCNode>()
->skeleton
->queue_delete();
e.get_mut<NPCNode>()->skeleton =
nullptr;
e.remove<NPCNode>();
}
}
Node *root = SceneTree::get_singleton()
->get_current_scene();
if (!root) {
print_error("No scene");
return;
}
skeleton_data = ResourceLoader::load(
path, "SkeletonData");
if (skeleton_data.is_valid()) {
Skeleton *skeleton = memnew(Skeleton);
skeleton_data->load_skeleton(skeleton);
e.set<NPCNode>({ skeleton });
root->call_deferred("add_child",
skeleton);
} else
print_error(
"ERROR: Could not load skeleton: " +
path);
});
ecs.observer<struct NPCSkeleton>()
.event(flecs::OnRemove)
.write<struct NPCNode>()
.each([](flecs::entity e, const NPCSkeleton &s) {
if (e.has<NPCNode>()) {
if (e.get<NPCNode>()->skeleton) {
e.get_mut<NPCNode>()
->skeleton
->queue_delete();
e.get_mut<NPCNode>()->skeleton =
nullptr;
e.remove<NPCNode>();
}
}
});
/* moving NPC despawned NPC */
ecs.system<NPCBase>("Update").each(
[](flecs::entity e, NPCBase &b) {
@@ -84,12 +168,18 @@ struct module {
}
NPC::NPC()
{
flecs::world ecs = BaseData::get_singleton()->get();
ecs.import <NPCModule::module>();
flecs::entity npcs_e = ecs.entity("npc");
flecs::entity base = ecs.entity("characters");
if (base.is_valid()) {
/* create components for top level */
}
EditorEvent::get_singleton()->event.add_listener(
this, &NPC::editor_event_handler);
GameEvent::get_singleton()->event.add_listener(
this, &NPC::game_event_handler);
}
NPC *NPC::get_singleton()
@@ -111,3 +201,71 @@ NPC::~NPC()
void NPC::update(float delta)
{
}
flecs::entity NPC::foreground_character(int id)
{
String ch_name = "ch_" + itos(id);
flecs::world ecs = BaseData::get_singleton()->get();
flecs::entity base = ecs.lookup("characters");
flecs::entity ch = base.lookup(ch_name.ascii().ptr());
if (!ch.is_valid())
ch = ecs.entity(ch_name.ascii().ptr()).child_of(base);
assert(ch.is_valid());
if (!ch.has<NPCModule::NPCBase>())
ch.set<NPCModule::NPCBase>({ id });
return ch;
}
void NPC::background_character(int id)
{
String ch_name = "ch_" + itos(id);
flecs::world ecs = BaseData::get_singleton()->get();
flecs::entity base = ecs.lookup("characters");
flecs::entity ch = base.lookup(ch_name.ascii().ptr());
assert(ch.is_valid());
/* remove all components */
std::vector<flecs::entity_t> keep_components = {
ecs.component<NPCModule::NPCBase>()
};
ch.each([&ch, keep_components](flecs::entity_t c) {
if (std::find(keep_components.begin(), keep_components.end(),
c) == keep_components.end())
ch.remove(c);
});
}
void NPC::get_character_list(List<int> *character_list)
{
Error err;
DirAccess *d = DirAccess::open("res://character/config", &err);
d->list_dir_begin();
String conf_path;
while ((conf_path = d->get_next()) != "") {
ConfigFile config;
config.load(conf_path);
int id = config.get_value("base", "id");
}
d->list_dir_end();
}
void NPC::editor_event_handler(const String &event, const Vector<Variant> &args)
{
if (event == "npc_foreground") {
int npc_id = args[0];
foreground_character(npc_id);
} else if (event == "npc_background") {
int npc_id = args[0];
background_character(npc_id);
}
}
void NPC::game_event_handler(const String &event, const Vector<Variant> &args)
{
if (event == "npc_foreground") {
int npc_id = args[0];
foreground_character(npc_id);
} else if (event == "npc_background") {
int npc_id = args[0];
background_character(npc_id);
}
}

View File

@@ -1,5 +1,6 @@
#ifndef NPC_H_
#define NPC_H_
class ConfigFile;
class NPC {
static NPC *singleton;
NPC();
@@ -9,5 +10,14 @@ public:
static void cleanup();
virtual ~NPC();
void update(float delta);
flecs::entity foreground_character(int id);
void background_character(int id);
static void get_character_list(List<int> *character_list);
private:
void editor_event_handler(const String &event,
const Vector<Variant> &args);
void game_event_handler(const String &event,
const Vector<Variant> &args);
};
#endif

View File

@@ -0,0 +1,38 @@
#include <core/object.h>
#include <core/os/dir_access.h>
#include <core/io/config_file.h>
#include "editor_event.h"
#include "npc_editor.h"
void NPCEditor::handle_event(const String &event, const Vector<Variant> &args)
{
if (event == "npc_list") {
#if 0
Error err;
DirAccess *d = DirAccess::open("res://character/config", &err);
d->list_dir_begin();
String mdata;
while ((mdata = d->get_next()) != "") {
ConfigFile config;
String config_path = "res://character/config/" + mdata;
config.load(config_path);
}
d->list_dir_end();
memdelete(d);
#endif
EditorEvent::get_singleton()->event.emit("npc_list_report",
varray());
}
}
NPCEditor::NPCEditor(WorldEditor *editor)
: editor(editor)
{
EditorEvent::get_singleton()->event.add_listener(
this, &NPCEditor::handle_event);
}
NPCEditor::~NPCEditor()
{
}

View File

@@ -0,0 +1,9 @@
class NPCEditor {
class WorldEditor *editor;
void handle_event(const String &event, const Vector<Variant> &args);
public:
NPCEditor(WorldEditor *editor);
virtual ~NPCEditor();
};

View File

@@ -0,0 +1,71 @@
// skeleton_data.cpp
#include "skeleton_data.h"
void SkeletonData::_bind_methods()
{
ClassDB::bind_method(D_METHOD("set_bone_data", "data"),
&SkeletonData::set_bone_data);
ClassDB::bind_method(D_METHOD("get_bone_data"),
&SkeletonData::get_bone_data);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "bone_data"), "set_bone_data",
"get_bone_data");
}
void SkeletonData::set_bone_data(const Dictionary &data)
{
bone_data = data;
}
Dictionary SkeletonData::get_bone_data() const
{
return bone_data;
}
void SkeletonData::save_skeleton(Skeleton *skeleton)
{
if (!skeleton)
return;
bone_data.clear();
int bone_count = skeleton->get_bone_count();
for (int i = 0; i < bone_count; i++) {
String bone_name = skeleton->get_bone_name(i);
Dictionary b;
b["id"] = i;
b["enabled"] = skeleton->is_bone_enabled(i);
b["parent"] = skeleton->get_bone_parent(i);
b["rest"] = skeleton->get_bone_rest(i);
b["pose"] = skeleton->get_bone_pose(i);
b["custom"] = skeleton->get_bone_custom_pose(i);
bone_data[bone_name] = b;
}
}
void SkeletonData::load_skeleton(Skeleton *skeleton)
{
if (!skeleton)
return;
List<Variant> keys;
List<Variant>::Element *e;
bone_data.get_key_list(&keys);
e = keys.front();
int bsize = keys.size();
skeleton->clear_bones();
while (e) {
String bone_name = e->get();
const Dictionary &b = bone_data[bone_name];
skeleton->add_bone(bone_name);
bool enabled = b["enabled"];
int parent = b["parent"];
int id = b["id"];
const Transform &rest = b["rest"];
const Transform &pose = b["pose"];
const Transform &custom = b["custom"];
skeleton->set_bone_enabled(id, enabled);
skeleton->set_bone_parent(id, parent);
skeleton->set_bone_rest(id, rest);
skeleton->set_bone_pose(id, pose);
skeleton->set_bone_custom_pose(id, custom);
e = e->next();
}
}

View File

@@ -0,0 +1,26 @@
// skeleton_data.h
#ifndef SKELETON_DATA_H
#define SKELETON_DATA_H
#include <core/math/transform.h>
#include <core/resource.h>
#include <scene/3d/skeleton.h>
class SkeletonData : public Resource {
GDCLASS(SkeletonData, Resource);
private:
Dictionary bone_data;
protected:
static void _bind_methods();
public:
void set_bone_data(const Dictionary &data);
Dictionary get_bone_data() const;
void save_skeleton(Skeleton *skeleton);
void load_skeleton(Skeleton *skeleton);
};
#endif // SKELETON_DATA_H

View File

@@ -0,0 +1,13 @@
Import("env")
Import("env_modules")
env_stream = env_modules.Clone()
env_stream.stream_building_sources = []
env_stream.add_source_files(env_stream.stream_building_sources, "*.cpp")
lib = env_stream.add_library("lersistent-data", env_stream.stream_building_sources)
env.Append(LIBS=[lib])
env_stream.Prepend(CPPPATH=[".."])
env_stream.Prepend(CPPPATH=["../../../meshoptimizer/src"])
env_stream.Prepend(CPPPATH=["../event"])

View File

@@ -0,0 +1,34 @@
#include "persistent_data.h"
PersistentData *PersistentData::singleton;
PersistentData::PersistentData()
{
singleton = this;
}
void PersistentData::save_data(const String &save_slot_path)
{
int i;
for (i = 0; i < handlers.size(); i++)
handlers.ptrw()[i]->save(save_slot_path);
}
void PersistentData::load_data(const String &save_slot_path)
{
int i;
for (i = 0; i < handlers.size(); i++)
handlers.ptrw()[i]->load(save_slot_path);
}
PersistentData *PersistentData::get_singleton()
{
if (!singleton)
singleton = memnew(PersistentData);
return singleton;
}
void PersistentData::cleanup()
{
if (singleton)
memdelete(singleton);
}

View File

@@ -0,0 +1,21 @@
#include <core/vector.h>
class PersistentData {
static PersistentData *singleton;
PersistentData();
public:
class SaveHandler {
public:
virtual void save(const String &slot_path) = 0;
virtual void load(const String &slot_path) = 0;
};
Vector<SaveHandler *> handlers;
void register_handler(SaveHandler *handler)
{
handlers.push_back(handler);
}
void save_data(const String &save_slot_path);
void load_data(const String &save_slot_path);
static PersistentData *get_singleton();
static void cleanup();
};

View File

@@ -8,6 +8,7 @@
#include "buildings/building_layout_editor.h"
#include "ui/main_tabs.h"
#include "npc/importer.h"
#include "npc/skeleton_data.h"
#include "base_data.h"
#ifdef TOOLS_ENABLED
@@ -28,6 +29,7 @@ void register_stream_types()
ClassDB::register_class<LineMetadataEditor>();
ClassDB::register_class<BuildingLayoutEditor>();
ClassDB::register_class<MainTabs>();
ClassDB::register_class<SkeletonData>();
#ifdef TOOLS_ENABLED
ClassDB::APIType prev_api = ClassDB::get_current_api();
ClassDB::set_current_api(ClassDB::API_EDITOR);

View File

@@ -78,27 +78,32 @@ public:
: Object()
, editor(editor)
{
#if 0
LineEdit *filter =
editor->get_as_node<LineEdit>("%road_lines_filter");
filter->connect("text_changed", this, "filter_handler");
filter->connect("text_entered", this, "filter_handler");
#endif
#if 0
ItemList *lines_list =
editor->get_as_node<ItemList>("%lines_list");
lines_list->connect("item_selected", this, "handler");
Button *cursor_set = editor->get_as_node<Button>(
"%road_lines_set_cursor_position");
Button *cursor_set = editor->cursor_set;
assert(cursor_set);
cursor_set->connect("pressed", this, "set_cursor_handler");
Button *point_set = editor->get_as_node<Button>(
"%road_lines_set_point_position");
Button *point_set = editor->cursor_set;
assert(point_set);
point_set->connect("pressed", this, "set_point_handler");
#endif
}
virtual ~HandleSelection()
{
Button *cursor_set = editor->get_as_node<Button>(
"%road_lines_set_cursor_position");
#if 0
Button *cursor_set = editor->cursor_set;
assert(cursor_set);
cursor_set->disconnect("pressed", this, "set_cursor_handler");
Button *point_set = editor->get_as_node<Button>(
"%road_lines_set_point_position");
Button *point_set = editor->cursor_set;
assert(point_set);
point_set->disconnect("pressed", this, "set_point_handler");
ItemList *lines_list =
editor->get_as_node<ItemList>("%lines_list");
@@ -107,6 +112,7 @@ public:
editor->get_as_node<LineEdit>("%road_lines_filter");
filter->disconnect("text_entered", this, "filter_handler");
filter->disconnect("text_changed", this, "filter_handler");
#endif
}
protected:
@@ -122,12 +128,12 @@ protected:
{
editor->line_list_filter_changed(text);
}
#if 0
void set_cursor_handler()
{
LineEdit *cursor_x = editor->get_as_node<LineEdit>("%cursor_x");
LineEdit *cursor_y = editor->get_as_node<LineEdit>("%cursor_y");
LineEdit *cursor_z = editor->get_as_node<LineEdit>("%cursor_z");
LineEdit *cursor[] = { cursor_x, cursor_y, cursor_z };
LineEdit *cursor_x = editor->cursor_pos[0];
LineEdit *cursor_y = editor->cursor_pos[1];
LineEdit *cursor_z = editor->cursor_pos[2];
Vector3 position;
position.x = cursor_x->get_text().to_float();
position.y = cursor_y->get_text().to_float();
@@ -136,26 +142,28 @@ protected:
}
void set_point_handler()
{
LineEdit *point_x = editor->get_as_node<LineEdit>("%point_x");
LineEdit *point_y = editor->get_as_node<LineEdit>("%point_y");
LineEdit *point_z = editor->get_as_node<LineEdit>("%point_z");
LineEdit *point[] = { point_x, point_y, point_z };
LineEdit *point_x = editor->point_pos[0];
LineEdit *point_y = editor->point_pos[1];
LineEdit *point_z = editor->point_pos[2];
Vector3 position;
position.x = point_x->get_text().to_float();
position.y = point_y->get_text().to_float();
position.z = point_z->get_text().to_float();
editor->set_point_position(position);
}
#endif
static void _bind_methods()
{
ClassDB::bind_method(D_METHOD("handler", "index"),
&HandleSelection::handler);
ClassDB::bind_method(D_METHOD("filter_handler", "text"),
&HandleSelection::filter_handler);
#if 0
ClassDB::bind_method(D_METHOD("set_cursor_handler"),
&HandleSelection::set_cursor_handler);
ClassDB::bind_method(D_METHOD("set_point_handler"),
&HandleSelection::set_point_handler);
#endif
}
};
@@ -170,17 +178,21 @@ public:
: Object()
, editor(editor)
{
#if 0
SpinBox *sp_line_point =
editor->get_as_node<SpinBox>("%line_index");
sp_line_point->connect("value_changed", this,
"handle_value_change");
#endif
}
virtual ~HandlePointSelection()
{
#if 0
SpinBox *sp_line_point =
editor->get_as_node<SpinBox>("%line_index");
sp_line_point->disconnect("value_changed", this,
"handle_value_change");
#endif
}
protected:
@@ -218,10 +230,6 @@ public:
: Object()
, editor(editor)
{
Button *cancel_button = editor->get_as_node<Button>(
"%road_lines_create_new_cancel");
LineEdit *line_name = editor->get_as_node<LineEdit>(
"%road_lines_create_new_line_name");
Button *cancel_metadata_button = editor->get_as_node<Button>(
"%road_lines_metadata_cancel");
Button *update_metadata_button = editor->get_as_node<Button>(
@@ -230,9 +238,6 @@ public:
"%road_lines_metadata_edit");
Button *line_buildings_close =
editor->get_as_node<Button>("%line_buildings_close");
cancel_button->connect("pressed", this, "cancel_handler");
line_name->connect("text_entered", this, "entered_handler");
line_name->connect("text_changed", this, "changed_handler");
cancel_metadata_button->connect("pressed", this,
"cancel_metadata_handler");
update_metadata_button->connect("pressed", this,
@@ -252,10 +257,6 @@ public:
"%road_lines_metadata_update");
Button *cancel_metadata_button = editor->get_as_node<Button>(
"%road_lines_metadata_cancel");
Button *cancel_button = editor->get_as_node<Button>(
"%road_lines_create_new_cancel");
LineEdit *line_name = editor->get_as_node<LineEdit>(
"%road_lines_create_new_line_name");
line_buildings_close->disconnect(
"pressed", this, "line_buildings_close_handler");
metadata_edit->disconnect("text_changed", this,
@@ -264,9 +265,6 @@ public:
"update_metadata_handler");
cancel_metadata_button->disconnect("pressed", this,
"cancel_metadata_handler");
line_name->disconnect("text_changed", this, "changed_handler");
line_name->disconnect("text_entered", this, "entered_handler");
cancel_button->disconnect("pressed", this, "cancel_handler");
}
protected:
@@ -298,53 +296,6 @@ protected:
items->add_item(key);
}
}
void cancel_handler()
{
LineEdit *line_name = editor->get_as_node<LineEdit>(
"%road_lines_create_new_line_name");
editor->get_as_node<Control>("%road_lines_base")->show();
editor->get_as_node<Control>("%road_lines_create_new_line_dlg")
->hide();
line_name->set_text("");
}
bool check_line_name(const String &line_name)
{
String text = line_name.strip_edges();
if (editor->line_exists(text) || text.length() < 5 ||
text.find("_") < 0 || text.ends_with("_"))
return false;
return true;
}
void entered_handler(const String &new_text)
{
String text = new_text.strip_edges();
LineEdit *line_name = editor->get_as_node<LineEdit>(
"%road_lines_create_new_line_name");
if (!check_line_name(text)) {
line_name->add_color_override(
"font_color", Color(1.0f, 0.0f, 0.0f, 1.0f));
return;
} else
line_name->remove_color_override("font_color");
editor->create_new_line_at_cursor(text);
/* clean text */
line_name->set_text("");
editor->get_as_node<Control>("%road_lines_base")->show();
editor->get_as_node<Control>("%road_lines_create_new_line_dlg")
->hide();
}
void changed_handler(const String &new_text)
{
LineEdit *line_name = editor->get_as_node<LineEdit>(
"%road_lines_create_new_line_name");
String text = new_text.strip_edges();
if (!check_line_name(text)) {
line_name->add_color_override(
"font_color", Color(1.0f, 0.0f, 0.0f, 1.0f));
return;
} else
line_name->remove_color_override("font_color");
}
void cancel_metadata_handler()
{
TextEdit *metadata_text = editor->get_as_node<TextEdit>(
@@ -404,12 +355,6 @@ protected:
}
static void _bind_methods()
{
ClassDB::bind_method(D_METHOD("cancel_handler"),
&HandleCreateNewLine::cancel_handler);
ClassDB::bind_method(D_METHOD("entered_handler", "new_text"),
&HandleCreateNewLine::entered_handler);
ClassDB::bind_method(D_METHOD("changed_handler", "new_text"),
&HandleCreateNewLine::changed_handler);
ClassDB::bind_method(
D_METHOD("cancel_metadata_handler"),
&HandleCreateNewLine::cancel_metadata_handler);
@@ -442,6 +387,12 @@ RoadLinesEditor::RoadLinesEditor(WorldEditor *editor)
, filter_text("")
, camera_moved(false)
, update_roads(false)
#if 0
, cursor_pos{ 0, 0, 0 }
, cursor_set(nullptr)
, point_pos{ 0, 0, 0 }
, point_set(nullptr)
#endif
, debug_road_nodes(false)
, debug_road_edges(false)
, debug_road_wedges(false)
@@ -533,10 +484,8 @@ void RoadLinesEditor::update_line_geometry()
}
void RoadLinesEditor::update_line_index_ui()
{
RoadLinesData *rld = RoadLinesData::get_singleton();
SpinBox *sp_line_index = get_as_node<SpinBox>("%line_index");
sp_line_index->set_max(rld->lines(current_line).points.size() - 1);
sp_line_index->set_min(0);
EditorEvent::get_singleton()->event.emit(
"road_lines_editor_update_line_index_ui", varray(current_line));
}
void RoadLinesEditor::select_line(const String &line_name)
{
@@ -546,8 +495,8 @@ void RoadLinesEditor::select_line(const String &line_name)
if (current_line != line_name) {
current_line = line_name;
update_line_index_ui();
SpinBox *sp_line_index = get_as_node<SpinBox>("%line_index");
sp_line_index->set_value(0);
EditorEvent::get_singleton()->event.emit(
"road_lines_editor_clear_selected_line_ui", varray());
/* as actual index of SpinBox might not change
call point selection explicitly */
set_line_index(0);
@@ -577,9 +526,8 @@ void RoadLinesEditor::line_create_point()
varray(current_line));
update_line_geometry();
update_line_index_ui();
SpinBox *sp_line_index = get_as_node<SpinBox>("%line_index");
sp_line_index->set_value(index + 1);
// move_cursor_to_point();
EditorEvent::get_singleton()->event.emit(
"road_lines_editor_set_line_index_ui", varray(line_index + 1));
}
void RoadLinesEditor::line_delete_point()
@@ -595,10 +543,11 @@ void RoadLinesEditor::line_delete_point()
varray(current_line));
update_line_geometry();
update_line_index_ui();
SpinBox *sp_line_index = get_as_node<SpinBox>("%line_index");
sp_line_index->set_value(0);
EditorEvent::get_singleton()->event.emit(
"road_lines_editor_update_line_index_ui", varray(current_line));
index = MAX(0, index - 1);
sp_line_index->set_value(index);
EditorEvent::get_singleton()->event.emit(
"road_lines_editor_set_line_index_ui", varray(index));
}
static const String cursor_name = "%line_cursor";
@@ -619,10 +568,7 @@ void RoadLinesEditor::set_point_to_cursor()
int RoadLinesEditor::get_line_index()
{
print_line("get_line_index");
SpinBox *sp_line_index = get_as_node<SpinBox>("%line_index");
int index = (int)sp_line_index->get_value();
return index;
return line_index;
}
void RoadLinesEditor::move_cursor_to_point()
@@ -739,14 +685,12 @@ void RoadLinesEditor::update_ui()
{
RoadLinesData *rld = RoadLinesData::get_singleton();
get_as_node<Control>("%road_lines_base")->show();
get_as_node<Control>("%road_lines_create_new_line_dlg")->hide();
get_as_node<Control>("%road_lines_edit_metadata_dlg")->hide();
get_as_node<Control>("%road_lines_buildings")->hide();
ItemList *lines_list = get_as_node<ItemList>("%lines_list");
PoolVector<String> elements;
List<String> line_keys;
rld->get_lines_key_list(&line_keys);
List<String>::Element *e = line_keys.front();
lines_list->clear();
if (!re.is_valid())
re.instance();
re->compile(filter_text);
@@ -763,14 +707,19 @@ void RoadLinesEditor::update_ui()
}
if (key == current_line)
selected_index = index;
lines_list->add_item(key);
elements.push_back(key);
e = e->next();
index++;
}
#if 0
if (selected_index >= 0)
lines_list->set_current(selected_index);
else
lines_list->set_current(0);
#endif
EditorEvent::get_singleton()->event.emit(
"road_lines_editor_update_lines_list",
varray(elements, selected_index));
}
void RoadLinesEditor::create_new_line_at_cursor(const String &line_name)
@@ -788,6 +737,7 @@ void RoadLinesEditor::create_new_line_at_cursor(const String &line_name)
Transform cursor_position(Basis(), get_cursor_position());
rline.points.push_back(cursor_position);
rld->set_line(line_name, rline);
assert(rld->has_line(line_name));
update_line_index_ui();
update_ui();
}
@@ -827,21 +777,31 @@ void RoadLinesEditor::set_point_position(const Vector3 &position)
}
void RoadLinesEditor::set_ui_cursor_position(const Vector3 &cursor_position)
{
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");
EditorEvent::get_singleton()->event.emit(
"road_lines_editor_set_ui_cursor_position",
varray(cursor_position));
#if 0
LineEdit *cursor_x = cursor_pos[0];
LineEdit *cursor_y = cursor_pos[1];
LineEdit *cursor_z = cursor_pos[2];
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
}
void RoadLinesEditor::set_ui_point_position(const Vector3 &point_position)
{
LineEdit *point_x = get_as_node<LineEdit>("%point_x");
LineEdit *point_y = get_as_node<LineEdit>("%point_y");
LineEdit *point_z = get_as_node<LineEdit>("%point_z");
EditorEvent::get_singleton()->event.emit(
"road_lines_editor_set_ui_point_position",
varray(point_position));
#if 0
LineEdit *point_x = point_pos[0];
LineEdit *point_y = point_pos[1];
LineEdit *point_z = point_pos[2];
point_x->set_text(String::num(point_position.x));
point_y->set_text(String::num(point_position.y));
point_z->set_text(String::num(point_position.z));
#endif
}
void RoadLinesEditor::set_line_index(int index)
@@ -851,6 +811,7 @@ void RoadLinesEditor::set_line_index(int index)
assert(index < (int)rld->lines(current_line).points.size());
Vector3 point_position = rld->lines(current_line).points[index].origin;
Vector3 cursor_position = point_position;
line_index = index;
set_cursor_position(cursor_position);
set_ui_cursor_position(cursor_position);
set_ui_point_position(point_position);
@@ -1150,5 +1111,55 @@ void RoadLinesEditor::event_handler(const String &event,
rebuild_roads();
} else if (event == "result:get_building_types") {
/* TODO:: implement */
#if 0
} else if (event == "road_lines_setup_cursor_ui") {
cursor_pos[0] = Object::cast_to<LineEdit>(args[0]);
cursor_pos[1] = Object::cast_to<LineEdit>(args[1]);
cursor_pos[2] = Object::cast_to<LineEdit>(args[2]);
cursor_set = Object::cast_to<Button>(args[3]);
assert(cursor_set);
} else if (event == "road_lines_setup_point_ui") {
point_pos[0] = Object::cast_to<LineEdit>(args[0]);
point_pos[1] = Object::cast_to<LineEdit>(args[1]);
point_pos[2] = Object::cast_to<LineEdit>(args[2]);
point_set = Object::cast_to<Button>(args[3]);
assert(cursor_set);
#endif
} else if (event == "road_lines_editor_set_cursor_position") {
Vector3 position = args[0];
set_cursor_position(position);
} else if (event == "road_lines_editor_set_point_position") {
Vector3 position = args[0];
set_point_position(position);
} else if (event == "road_lines_filter") {
String text = args[1];
line_list_filter_changed(text);
} else if (event == "road_lines_editor_set_line_index") {
int index = (int)args[1];
set_line_index(index);
} else if (event == "road_lines_create_new_line_name:entered") {
String new_text = args[1];
String text = new_text.strip_edges();
LineEdit *le = Object::cast_to<LineEdit>(args[0]);
if (line_exists(text) || text.length() < 5 ||
text.find("_") < 0 || text.ends_with("_")) {
le->add_color_override("font_color",
Color(1.0f, 0.0f, 0.0f, 1.0f));
return;
} else {
le->remove_color_override("font_color");
create_new_line_at_cursor(text);
}
} else if (event == "road_lines_create_new_line_name:changed") {
String new_text = args[1];
String text = new_text.strip_edges();
LineEdit *le = Object::cast_to<LineEdit>(args[0]);
if (line_exists(text) || text.length() < 5 ||
text.find("_") < 0 || text.ends_with("_")) {
le->add_color_override("font_color",
Color(1.0f, 0.0f, 0.0f, 1.0f));
return;
} else
le->remove_color_override("font_color");
}
}

View File

@@ -3,6 +3,8 @@
#include "world_editor.h"
class ItemList;
class RegEx;
class LineEdit;
class Button;
class RoadLinesEditor {
bool active;
WorldEditor *editor;
@@ -11,6 +13,17 @@ class RoadLinesEditor {
Ref<RegEx> re;
bool camera_moved;
Vector3 camera_motion;
int line_index;
#if 0
public:
LineEdit *cursor_pos[3];
Button *cursor_set;
LineEdit *point_pos[3];
Button *point_set;
#endif
private:
void event_handler(const String &event, const Vector<Variant> &args);
bool update_roads; // 201
bool debug_road_nodes; // 210

View File

@@ -5,7 +5,9 @@
#include <scene/gui/option_button.h>
#include <scene/gui/button.h>
#include <scene/gui/line_edit.h>
#include <scene/gui/item_list.h>
#include <scene/gui/check_box.h>
#include <scene/gui/spin_box.h>
#include "editor_event.h"
#define _GODOT_HANDLER_METHOD(class_name, obj_name, sig_name, event_name, \
@@ -97,9 +99,16 @@ GODOT_HANDLER_EVENT_METHOD(ButtonPressHandler, Button, pressed, (), (),
GODOT_HANDLER_EVENT_METHOD(LineEditString, LineEdit, text_changed,
(const String &text), (text),
D_METHOD("handler", "text"))
GODOT_HANDLER_EVENT_METHOD(LineEditEnterString, LineEdit, text_entered,
(const String &text), (text),
D_METHOD("handler", "text"))
GODOT_HANDLER_EVENT_METHOD(CheckBoxPressHandler, CheckBox, pressed, (), (),
D_METHOD("handler"))
GODOT_HANDLER_EVENT_METHOD(PopupMenuSelectHandler, PopupMenu, id_pressed,
(int id), (id), D_METHOD("handler", "id"))
GODOT_HANDLER_EVENT_METHOD(ItemListHandler, ItemList, item_selected, (int id),
(id), D_METHOD("handler", "id"))
GODOT_HANDLER_EVENT_METHOD(SpinBoxHandler, SpinBox, value_changed,
(float value), (value), D_METHOD("handler", "value"))
#endif // SIGNAL_HANDLER_H_

View File

@@ -10,6 +10,8 @@
#include "road_processing.h"
#include "road_debug.h"
#include "buildings_data.h"
#include "base_data.h"
#include "npc/npc.h"
#include "stream.h"
#define data() (BuildingsData::get_singleton())
@@ -455,6 +457,10 @@ StreamWorld::StreamWorld()
map_it++;
}
view_distance = config.get_value("world", "view_distance");
NPC *npc = NPC::get_singleton();
if (!npc) {
assert(false);
}
initialized = true;
}
void StreamWorld::cleanup()

View File

@@ -15,5 +15,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=["../event"])
env.Prepend(CPPPATH=[".."])
env.Prepend(CPPPATH=["../../../meshoptimizer/src"])

View File

@@ -88,6 +88,114 @@ void MainTabs::handle_event(const String &event, const Vector<Variant> &args)
brushes->add_item(brush_data[id], id);
be = be->next();
}
} else if (event == "road_lines_editor_set_ui_cursor_position") {
LineEdit *cursor_x =
Object::cast_to<LineEdit>(ui_data["cursor_x"]);
LineEdit *cursor_y =
Object::cast_to<LineEdit>(ui_data["cursor_y"]);
LineEdit *cursor_z =
Object::cast_to<LineEdit>(ui_data["cursor_z"]);
Vector3 position = args[0];
cursor_x->set_text(String::num(position.x));
cursor_y->set_text(String::num(position.y));
cursor_z->set_text(String::num(position.z));
} else if (event == "road_lines_editor_set_ui_point_position") {
LineEdit *point_x =
Object::cast_to<LineEdit>(ui_data["point_x"]);
LineEdit *point_y =
Object::cast_to<LineEdit>(ui_data["point_y"]);
LineEdit *point_z =
Object::cast_to<LineEdit>(ui_data["point_z"]);
Vector3 position = args[0];
point_x->set_text(String::num(position.x));
point_y->set_text(String::num(position.y));
point_z->set_text(String::num(position.z));
} else if (event == "road_lines_set_cursor_position") {
LineEdit *cursor_x =
Object::cast_to<LineEdit>(ui_data["cursor_x"]);
LineEdit *cursor_y =
Object::cast_to<LineEdit>(ui_data["cursor_y"]);
LineEdit *cursor_z =
Object::cast_to<LineEdit>(ui_data["cursor_z"]);
Vector3 position;
position.x = cursor_x->get_text().to_float();
position.y = cursor_y->get_text().to_float();
position.z = cursor_z->get_text().to_float();
EditorEvent::get_singleton()->event.emit(
"road_lines_editor_set_cursor_position",
varray(position));
} else if (event == "road_lines_set_point_position") {
LineEdit *point_x =
Object::cast_to<LineEdit>(ui_data["point_x"]);
LineEdit *point_y =
Object::cast_to<LineEdit>(ui_data["point_y"]);
LineEdit *point_z =
Object::cast_to<LineEdit>(ui_data["point_z"]);
Vector3 position;
position.x = point_x->get_text().to_float();
position.y = point_y->get_text().to_float();
position.z = point_z->get_text().to_float();
EditorEvent::get_singleton()->event.emit(
"road_lines_editor_set_point_position",
varray(position));
} else if (event == "road_lines_editor_update_lines_list") {
const PoolVector<String> &elements = args[0];
int selected_index = args[1];
assert(ui_data.has("lines_list"));
ItemList *lines_list =
Object::cast_to<ItemList>(ui_data["lines_list"]);
assert(lines_list);
lines_list->clear();
for (i = 0; i < (int)elements.size(); i++)
lines_list->add_item(elements.read()[i]);
if (selected_index >= 0)
lines_list->set_current(selected_index);
else
lines_list->set_current(0);
} else if (event == "road_lines_editor_update_line_index_ui") {
String current_line = args[0];
if (current_line.length() > 0) {
RoadLinesData *rld = RoadLinesData::get_singleton();
SpinBox *sp_line_index =
Object::cast_to<SpinBox>(ui_data["line_index"]);
int index = sp_line_index->get_value();
sp_line_index->set_value(0);
sp_line_index->set_max(
rld->lines(current_line).points.size() - 1);
sp_line_index->set_min(0);
index = MIN(
index,
(int)rld->lines(current_line).points.size() -
1);
sp_line_index->set_value(index);
}
} else if (event == "road_lines_editor_clear_selected_line_ui") {
SpinBox *sp_line_index =
Object::cast_to<SpinBox>(ui_data["line_index"]);
sp_line_index->set_value(0);
} else if (event == "road_lines_editor_set_line_index_ui") {
int index = args[0];
SpinBox *sp_line_index =
Object::cast_to<SpinBox>(ui_data["line_index"]);
sp_line_index->set_value(0);
sp_line_index->set_value(index);
} else if (event == "road_lines_create_new_line_name:entered") {
Control *dlg = Object::cast_to<Control>(
ui_data["road_lines_create_new_line_dlg"]);
dlg->hide();
LineEdit *le = Object::cast_to<LineEdit>(args[0]);
le->set_text("");
} else if (event == "road_lines_create_new_cancel") {
Control *dlg = Object::cast_to<Control>(
ui_data["road_lines_create_new_line_dlg"]);
dlg->hide();
LineEdit *le = Object::cast_to<LineEdit>(
ui_data["road_lines_create_new_line_name"]);
le->set_text("");
} else if (event == "road_lines_create_new_line_show") {
Control *dlg = Object::cast_to<Control>(
ui_data["road_lines_create_new_line_dlg"]);
dlg->show();
}
}
@@ -375,13 +483,73 @@ void MainTabs::_notification(int which)
"", "point_y",
"", "point_z",
"Set", "road_lines_set_point_position",
"road_lines_create_new_line_dlg",
"", "road_lines_create_new_line_name",
"Cancel", "road_lines_create_new_cancel"
/* clang-format on */
};
HashMap<String, Object *> save_data;
ui_field::ui_field_builder(
this, tab,
"v{p#!{v{lh#!{m#!m#!m#!m#!m#!}_lh{le#!}i.#!x#!_lv#!{h{e#!e#!e#!b#!}}_lv#!{h{e#!e#!e#!b#!}}}}}",
"v{p#!{v{lh#!{m#!m#!m#!m#!m#!}_lh{le#!$}i.#!$x#!$_lv#!{h{e#!$e#!$e#!$b#$}}_lv#!{h{e#!$e#!$e#!$b#$}}}}p#!${v{e#!$b#!$}}}",
// "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());
args_data.data(), args_data.size(),
&save_data);
std::vector<String> elements = {
"cursor_x",
"cursor_y",
"cursor_z",
"road_lines_set_cursor_position",
"point_x",
"point_y",
"point_z",
"road_lines_set_point_position",
"lines_list",
"road_lines_filter",
"line_index",
"road_lines_create_new_line_dlg",
"road_lines_create_new_line_name",
"road_lines_create_new_cancel"
};
int el;
for (el = 0; el < (int)elements.size(); el++) {
String elname = elements[el];
assert(save_data.has(elname));
ui_data[elname] = save_data[elname];
Button *button =
Object::cast_to<Button>(
ui_data[elname]);
if (button)
memnew(ButtonPressHandler(
button, elname));
if (elname == "road_lines_filter") {
LineEdit *le = Object::cast_to<
LineEdit>(
ui_data[elname]);
memnew(LineEditString(le,
elname));
}
if (elname == "line_index") {
SpinBox *sb =
Object::cast_to<SpinBox>(
ui_data[elname]);
memnew(SpinBoxHandler(
sb,
"road_lines_editor_set_line_index"));
}
if (elname ==
"road_lines_create_new_line_name") {
LineEdit *le = Object::cast_to<
LineEdit>(
ui_data[elname]);
memnew(LineEditEnterString(
le,
elname + ":entered"));
memnew(LineEditString(
le,
elname + ":changed"));
}
};
} break;
case 5: {
std::vector<Variant> args_data = {

View File

@@ -8,6 +8,7 @@ class MainTabs : public TabContainer {
Vector<Object *> handlers;
WindowDialog *npc_editor;
WindowDialog *building_layouts_editor;
HashMap<String, Object *> ui_data;
template <class T>
inline void mode_visibility(int mode, const String &path);
void handle_event(const String &event, const Vector<Variant> &args);

View File

@@ -97,10 +97,8 @@ public:
break;
case 21:
/* Create line */
editor->get_as_node<Control>("%road_lines_base")->hide();
editor->get_as_node<Control>(
"%road_lines_create_new_line_dlg")
->show();
EditorEvent::get_singleton()->event.emit(
"road_lines_create_new_line_show", varray());
break;
case 22:
/* delete line */

View File

@@ -17,6 +17,7 @@
#include "base_data.h"
#include "buildings_editor.h"
#include "terrain_editor.h"
#include "npc/npc_editor.h"
#include "world_editor.h"
class HandleCommandButton : public Object {
@@ -79,6 +80,7 @@ WorldEditor::WorldEditor()
, road_lines_editor(memnew(RoadLinesEditor(this)))
, buildings_editor(memnew(BuildingsEditor(this)))
, terrain_editor(memnew(TerrainEditor(this)))
, npc_editor(memnew(NPCEditor(this)))
{
flecs::entity e;
flecs::world ecs = BaseData::get_singleton()->get();

View File

@@ -10,6 +10,7 @@
class RoadLinesEditor;
class BuildingsEditor;
class TerrainEditor;
class NPCEditor;
class WorldEditor : public Spatial {
GDCLASS(WorldEditor, Spatial)
protected:
@@ -42,6 +43,7 @@ private:
RoadLinesEditor *road_lines_editor;
BuildingsEditor *buildings_editor;
TerrainEditor *terrain_editor;
NPCEditor *npc_editor;
public:
enum {