399 lines
10 KiB
C++
399 lines
10 KiB
C++
#include <core/io/config_file.h>
|
|
#include <core/io/json.h>
|
|
#include <core/os/file_access.h>
|
|
#include <core/set.h>
|
|
#include <scene/main/viewport.h>
|
|
#include <scene/resources/packed_scene.h>
|
|
#include <scene/resources/material.h>
|
|
#include <scene/3d/mesh_instance.h>
|
|
#include <modules/voxel/terrain/voxel_viewer.h>
|
|
#include <modules/voxel/terrain/voxel_lod_terrain.h>
|
|
#include "from_string.h"
|
|
#include "road_processing.h"
|
|
#include "stream.h"
|
|
#include "road_debug.h"
|
|
|
|
void StreamWorld::read_buildings_json(const String &buildings_path)
|
|
{
|
|
String buildings_json = FileAccess::get_file_as_string(buildings_path);
|
|
Variant json_v;
|
|
String es;
|
|
int eline;
|
|
Error status = JSON::parse(buildings_json, json_v, es, eline);
|
|
ERR_FAIL_COND_MSG(status != OK, "Can't parse json: " + es +
|
|
" at line: " + itos(eline));
|
|
|
|
Dictionary json = json_v;
|
|
List<Variant> keys;
|
|
json.get_key_list(&keys);
|
|
List<Variant>::Element *e = keys.front();
|
|
while (e) {
|
|
struct building b;
|
|
String key = e->get();
|
|
b.id = json[key].get("id");
|
|
if (b.id == "empty") {
|
|
e = e->next();
|
|
continue;
|
|
}
|
|
String aabb_s = json[key].get("aabb");
|
|
b.xform = from_string<Transform>(key);
|
|
b.aabb = from_string<AABB>(aabb_s);
|
|
buildings.push_back(b);
|
|
e = e->next();
|
|
}
|
|
#if 0
|
|
e = keys.front();
|
|
while (e) {
|
|
String key = e->get();
|
|
/* Transform */
|
|
print_line("key: " + key);
|
|
String id = json[key].get("id");
|
|
print_line("id: " + id);
|
|
String aabb = json[key].get("aabb");
|
|
print_line("aabb: " + aabb);
|
|
e = e->next();
|
|
}
|
|
#endif
|
|
print_line("entries count: " + itos(buildings.size()));
|
|
}
|
|
|
|
void StreamWorld::create_tilemap()
|
|
{
|
|
int i;
|
|
for (i = 0; i < (int)buildings.size(); i++) {
|
|
int tile_x = buildings[i].xform.origin.x / tile_size;
|
|
int tile_z = buildings[i].xform.origin.z / tile_size;
|
|
std::tuple<int, int> key = std::make_tuple(tile_x, tile_z);
|
|
if (tiles.find(key) != tiles.end())
|
|
tiles[key].push_back(i);
|
|
else {
|
|
std::vector<int> data = { i };
|
|
tiles[key] = data;
|
|
}
|
|
}
|
|
print_line("Tile count: " + itos(tiles.size()));
|
|
}
|
|
|
|
void StreamWorld::update_view()
|
|
{
|
|
int i, j;
|
|
ERR_FAIL_COND_MSG(!initialized,
|
|
"The Stream object is incorrectly initialized");
|
|
if (!viewer || !terrain) {
|
|
VoxelViewer *v = nullptr;
|
|
VoxelLodTerrain *t = nullptr;
|
|
Node *scene = current_scene;
|
|
ERR_FAIL_COND_MSG(!scene, "No current scene");
|
|
List<Node *> queue;
|
|
queue.push_back(scene);
|
|
while (!queue.empty()) {
|
|
Node *item = queue.front()->get();
|
|
ERR_FAIL_COND_MSG(!item, "Something really fucked up");
|
|
VoxelViewer *tv = Object::cast_to<VoxelViewer>(item);
|
|
VoxelLodTerrain *tt =
|
|
Object::cast_to<VoxelLodTerrain>(item);
|
|
if (tv)
|
|
v = tv;
|
|
else if (tt)
|
|
t = tt;
|
|
if (v && t)
|
|
break;
|
|
int c = item->get_child_count();
|
|
for (i = 0; i < c; i++)
|
|
queue.push_back(item->get_child(i));
|
|
queue.pop_front();
|
|
}
|
|
ERR_FAIL_COND_MSG(!v, "VoxelViewer was not found");
|
|
ERR_FAIL_COND_MSG(!t, "VoxelLodTerrain was not found");
|
|
viewer = v;
|
|
terrain = t;
|
|
viewer->connect("tree_exiting", this, "viewer_dead");
|
|
terrain->connect("tree_exiting", this, "terrain_dead");
|
|
current_x = world_extent + 1;
|
|
current_z = world_extent + 1;
|
|
}
|
|
eye = viewer->get_global_transform().origin;
|
|
int tile_x = int(eye.x / tile_size);
|
|
int tile_z = int(eye.z / tile_size);
|
|
if (current_x != tile_x || current_z != tile_z) {
|
|
print_line("tile: " + itos(tile_x) + " " + itos(tile_z));
|
|
for (i = tile_z - view_distance; i < tile_z + view_distance + 1;
|
|
i++)
|
|
for (j = tile_x - view_distance;
|
|
j < tile_x + view_distance + 1; j++) {
|
|
std::tuple<int, int> key =
|
|
std::make_tuple(j, i);
|
|
if (tiles.find(key) != tiles.end()) {
|
|
if (loaded_tiles.find(key) ==
|
|
loaded_tiles.end()) {
|
|
print_line(
|
|
"load tile: " +
|
|
itos(j) + " " +
|
|
itos(i) + " = " +
|
|
itos(tiles[key].size()));
|
|
load_tile(j, i);
|
|
loaded_tiles[key] = tiles[key];
|
|
}
|
|
}
|
|
}
|
|
auto it = loaded_tiles.begin();
|
|
int erase_distance = view_distance + 1;
|
|
int ed2 = erase_distance * erase_distance;
|
|
while (it != loaded_tiles.end()) {
|
|
std::tuple<int, int> lkey = it->first;
|
|
int kx = std::get<0>(lkey);
|
|
int kz = std::get<1>(lkey);
|
|
int lx = kx - tile_x;
|
|
int lz = kz - tile_z;
|
|
if (lx * lx > ed2 || lz * lz > ed2) {
|
|
print_line("erase tile: " + itos(kx) + " " +
|
|
itos(kz));
|
|
erase_tile(kx, kz);
|
|
it = loaded_tiles.erase(it);
|
|
} else
|
|
it++;
|
|
}
|
|
|
|
current_x = tile_x;
|
|
current_z = tile_z;
|
|
}
|
|
}
|
|
|
|
void StreamWorld::viewer_dead()
|
|
{
|
|
print_error("viewer dead");
|
|
viewer = nullptr;
|
|
set_process(false);
|
|
}
|
|
|
|
void StreamWorld::terrain_dead()
|
|
{
|
|
print_line("terrain dead");
|
|
terrain = nullptr;
|
|
set_process(false);
|
|
}
|
|
|
|
void StreamWorld::load_tile(int tx, int ty)
|
|
{
|
|
int i;
|
|
std::tuple<int, int> key = std::make_tuple(tx, ty);
|
|
const std::vector<int> &items = tiles[key];
|
|
for (i = 0; i < (int)items.size(); i++) {
|
|
print_line("load item: " + itos(i) + ": " + itos(items[i]) +
|
|
": " + buildings[items[i]].id);
|
|
load_building(items[i]);
|
|
}
|
|
}
|
|
|
|
void StreamWorld::erase_tile(int tx, int ty)
|
|
{
|
|
int i;
|
|
std::tuple<int, int> key = std::make_tuple(tx, ty);
|
|
const std::vector<int> &items = tiles[key];
|
|
for (i = 0; i < (int)items.size(); i++) {
|
|
print_line("unload item: " + itos(i) + ": " + itos(items[i]) +
|
|
": " + buildings[items[i]].id);
|
|
unload_building(items[i]);
|
|
}
|
|
}
|
|
|
|
void StreamWorld::load_building(int id)
|
|
{
|
|
request_item(0, id);
|
|
}
|
|
|
|
void StreamWorld::unload_building(int id)
|
|
{
|
|
request_item(1, id);
|
|
}
|
|
|
|
void StreamWorld::request_item(int type, int item)
|
|
{
|
|
String id = buildings[item].id;
|
|
if (id == "empty")
|
|
return;
|
|
String path = building_data[id];
|
|
switch (type) {
|
|
case 0:
|
|
if (!scenes.has(id)) {
|
|
print_line("Requesting " + itos(item) + " " + path);
|
|
struct scene_data sd;
|
|
sd.path = path;
|
|
sd.loader = ResourceLoader::load_interactive(
|
|
path, "PackedScene", true);
|
|
if (std::find(sd.buildings.begin(), sd.buildings.end(),
|
|
item) == sd.buildings.end())
|
|
sd.buildings.push_back(item);
|
|
scenes[id] = sd;
|
|
} else {
|
|
struct scene_data &sd = scenes[id];
|
|
if (std::find(sd.buildings.begin(), sd.buildings.end(),
|
|
item) == sd.buildings.end())
|
|
sd.buildings.push_back(item);
|
|
}
|
|
break;
|
|
case 1:
|
|
print_line("Removing " + itos(item) + " " + path);
|
|
if (scenes.has(id)) {
|
|
std::vector<int>::iterator b =
|
|
scenes[id].buildings.begin();
|
|
std::vector<int>::iterator e =
|
|
scenes[id].buildings.end();
|
|
if (item_nodes.has(item))
|
|
item_nodes[item]->queue_delete();
|
|
scenes[id].buildings.erase(std::remove(b, e, item),
|
|
scenes[id].buildings.end());
|
|
if (item_nodes.has(item)) {
|
|
item_nodes[item]->queue_delete();
|
|
item_nodes.erase(item);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void StreamWorld::update_items()
|
|
{
|
|
int i;
|
|
const String *key = scenes.next(nullptr);
|
|
while (key) {
|
|
if (!scenes[*key].packed_scene.is_valid()) {
|
|
key = scenes.next(key);
|
|
continue;
|
|
}
|
|
for (i = 0; i < (int)scenes[*key].buildings.size(); i++) {
|
|
if (item_nodes.has(scenes[*key].buildings[i]))
|
|
continue;
|
|
Node *psc = scenes[*key].packed_scene->instance();
|
|
Spatial *psp = Object::cast_to<Spatial>(psc);
|
|
item_nodes[scenes[*key].buildings[i]] = psc;
|
|
current_scene->add_child(psp);
|
|
psp->set_global_transform(
|
|
buildings[scenes[*key].buildings[i]].xform);
|
|
}
|
|
key = scenes.next(key);
|
|
}
|
|
}
|
|
|
|
void StreamWorld::run_command(const String &command, const Array &args)
|
|
{
|
|
}
|
|
|
|
void StreamWorld::_notification(int which)
|
|
{
|
|
switch (which) {
|
|
case NOTIFICATION_ENTER_WORLD:
|
|
break;
|
|
case NOTIFICATION_EXIT_WORLD:
|
|
break;
|
|
case NOTIFICATION_ENTER_TREE:
|
|
if (initialized) {
|
|
if (Engine::get_singleton()->is_editor_hint())
|
|
current_scene = Object::cast_to<Node>(this);
|
|
else {
|
|
current_scene = get_tree()->get_current_scene();
|
|
if (current_scene)
|
|
current_scene = get_tree()->get_root();
|
|
}
|
|
ERR_FAIL_COND_MSG(!current_scene, "No current scene");
|
|
RoadProcessing::road_setup(this);
|
|
set_process(true);
|
|
}
|
|
break;
|
|
case NOTIFICATION_EXIT_TREE:
|
|
break;
|
|
case NOTIFICATION_PROCESS: {
|
|
update_view();
|
|
const String *key = scenes.next(nullptr);
|
|
while (key) {
|
|
if (scenes[*key].packed_scene.is_valid()) {
|
|
key = scenes.next(key);
|
|
continue;
|
|
}
|
|
Error result = scenes[*key].loader->poll();
|
|
if (result == ERR_FILE_EOF) {
|
|
Ref<PackedScene> sc =
|
|
scenes[*key].loader->get_resource();
|
|
scenes[*key].packed_scene = sc;
|
|
print_line("Loaded scene: " +
|
|
scenes[*key].path + " OK");
|
|
} else if (result != OK)
|
|
print_error("Could not load the resource :( " +
|
|
scenes[*key].path + " " +
|
|
itos(result));
|
|
key = scenes.next(key);
|
|
}
|
|
update_items();
|
|
|
|
} break;
|
|
}
|
|
}
|
|
|
|
void StreamWorld::_bind_methods()
|
|
{
|
|
ClassDB::bind_method(D_METHOD("terrain_dead"),
|
|
&StreamWorld::terrain_dead);
|
|
ClassDB::bind_method(D_METHOD("viewer_dead"),
|
|
&StreamWorld::viewer_dead);
|
|
ClassDB::bind_method(D_METHOD("run_command", "command", "args"),
|
|
&StreamWorld::run_command);
|
|
}
|
|
|
|
StreamWorld::StreamWorld()
|
|
: Spatial()
|
|
, viewer(nullptr)
|
|
, terrain(nullptr)
|
|
, current_scene(nullptr)
|
|
, world_extent(0)
|
|
, tile_size(0)
|
|
, view_distance(0)
|
|
, initialized(false)
|
|
{
|
|
ConfigFile config;
|
|
Error result = config.load("res://config/stream.conf");
|
|
ERR_FAIL_COND_MSG(result != OK, "Failed to load config");
|
|
Dictionary buildings_data =
|
|
config.get_value("buildings", "building_data");
|
|
List<Variant> keys;
|
|
buildings_data.get_key_list(&keys);
|
|
List<Variant>::Element *e = keys.front();
|
|
while (e) {
|
|
String key = e->get();
|
|
building_data[key] = buildings_data[key];
|
|
e = e->next();
|
|
}
|
|
e = keys.front();
|
|
while (e) {
|
|
String key = e->get();
|
|
print_line(key + ": " + buildings_data[key]);
|
|
e = e->next();
|
|
}
|
|
String buildings_path = config.get_value("buildings", "buildings_path");
|
|
read_buildings_json(buildings_path);
|
|
RoadProcessing::load_data();
|
|
world_extent = config.get_value("world", "world_extent");
|
|
tile_size = config.get_value("world", "tile_size");
|
|
ERR_FAIL_COND_MSG(tile_size <= 0 || world_extent <= 0 ||
|
|
world_extent <= tile_size,
|
|
"Failed to configure world");
|
|
create_tilemap();
|
|
tile_map_t::iterator map_it = tiles.begin();
|
|
while (map_it != tiles.end()) {
|
|
std::tuple<int, int> key = map_it->first;
|
|
std::vector<int> &tile_buildings = map_it->second;
|
|
print_line("x: " + itos(std::get<0>(key)) +
|
|
" y: " + itos(std::get<1>(key)) + " " +
|
|
itos(tile_buildings.size()));
|
|
map_it++;
|
|
}
|
|
view_distance = config.get_value("world", "view_distance");
|
|
initialized = true;
|
|
}
|
|
void StreamWorld::cleanup()
|
|
{
|
|
RoadProcessing::cleanup();
|
|
}
|
|
StreamWorld::~StreamWorld()
|
|
{
|
|
RoadProcessing::cleanup();
|
|
} |