diff --git a/modules/world/density_map.cpp b/modules/world/density_map.cpp index 6c647a5..281ada7 100644 --- a/modules/world/density_map.cpp +++ b/modules/world/density_map.cpp @@ -4,37 +4,26 @@ void DensityMap::_get_property_list(List *p_list) const { - p_list->push_back(PropertyInfo(Variant::INT, "world/grid_size")); - p_list->push_back(PropertyInfo(Variant::INT, "world/clusters/count")); - if (grid_size == 0 || num_clusters == 0) - return; - p_list->push_back(PropertyInfo(Variant::INT, "world/size/x")); - p_list->push_back(PropertyInfo(Variant::INT, "world/size/y")); - p_list->push_back(PropertyInfo(Variant::INT, "world/size/z")); - if (world_x_size * world_y_size * world_z_size == 0U) - return; - p_list->push_back(PropertyInfo(Variant::INT, "world/clusters/seed")); + // p_list->push_back(PropertyInfo(Variant::INT, "world/grid_size")); + p_list->push_back(PropertyInfo(Variant::OBJECT, "noise", PROPERTY_HINT_RESOURCE_TYPE, "OpenSimplexNoise")); + p_list->push_back(PropertyInfo(Variant::INT, "rnd_seed")); + p_list->push_back(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve")); + p_list->push_back(PropertyInfo(Variant::OBJECT, "height_map", PROPERTY_HINT_RESOURCE_TYPE, "WorldHeightMap")); } bool DensityMap::_get(const StringName &p_name, Variant &r_ret) const { - if (p_name == "world/clusters/count") { - r_ret = num_clusters; + if (p_name == "noise") { + r_ret = noise; return true; - } else if (p_name == "world/clusters/seed") { + } else if (p_name == "rnd_seed") { r_ret = seed; return true; - } else if (p_name == "world/grid_size") { - r_ret = grid_size; + } else if (p_name == "curve") { + r_ret = curve; return true; - } else if (p_name == "world/size/x") { - r_ret = world_x_size; - return true; - } else if (p_name == "world/size/y") { - r_ret = world_y_size; - return true; - } else if (p_name == "world/size/z") { - r_ret = world_z_size; + } else if (p_name == "height_map") { + r_ret = height_map; return true; } @@ -44,189 +33,66 @@ bool DensityMap::_get(const StringName &p_name, Variant &r_ret) const bool DensityMap::_set(const StringName &p_name, const Variant &p_value) { bool update = false; - if (p_name == "world/clusters/count") { - num_clusters = p_value; + if (p_name == "noise") { + noise = p_value; update = true; - } else if (p_name == "world/clusters/seed") { + } else if (p_name == "rnd_seed") { seed = p_value; update = true; - } else if (p_name == "world/grid_size") { - grid_size = p_value; + } else if (p_name == "curve") { + curve = p_value; update = true; - } else if (p_name == "world/size/x") { - world_x_size = p_value; - update = true; - } else if (p_name == "world/size/y") { - world_y_size = p_value; - update = true; - } else if (p_name == "world/size/z") { - world_z_size = p_value; + } else if (p_name == "height_map") { + curve = p_value; update = true; } if (update) { - update_clusters(); + update_all(); _change_notify(); } return update; } -void DensityMap::populate_grid(List &list) +void DensityMap::update_all() { -} - -void DensityMap::update_clusters() -{ - int i; - clusters.clear(); - regions.clear(); - counties.clear(); - cities.clear(); - districts.clear(); - circle_grid.clear(); - if (num_clusters < 1) + if (!curve.ptr() || !rnd.ptr() || !noise.ptr() || !height_map.ptr()) return; rnd->set_seed(seed); - world_center.x = world_x_size / 2; - world_center.y = world_y_size / 2; - world_center.z = world_z_size / 2; - if (world_x_size * world_y_size * world_z_size == 0) - return; - split_area(NULL, num_clusters, clusters); - for (i = 0; i < clusters.size(); i++) { - split_area(&clusters[i], num_regions, regions); - } - for (i = 0; i < regions.size(); i++) { - split_area(®ions[i], num_counties, counties); - } - for (i = 0; i < counties.size(); i++) { - split_area(&counties[i], num_cities, cities); - } - for (i = 0; i < cities.size(); i++) { - split_area(&cities[i], num_districts, districts); - } - WorldMapData *wmd = WorldMapData::get_singleton(); - wmd->clear(); - wmd->set_world_size(world_x_size, world_z_size); -#if 0 - for (i = 0; i < clusters.size(); i++) { - wmd->add_circle(clusters[i].pos.x, clusters[i].pos.y, clusters[i].radius); - printf("cluster: %d x: %d y: %d radius: %f\n", i, clusters[i].pos.x, clusters[i].pos.y, clusters[i].radius); - } -#endif - for (i = 0; i < districts.size(); i++) { - wmd->add_circle(districts[i].pos.x, districts[i].pos.y, districts[i].radius); - printf("districts: %d x: %d y: %d radius: %f\n", i, districts[i].pos.x, districts[i].pos.y, districts[i].radius); - } - wmd->save_debug_image(); - printf("num_clusters: %d - %d\n", (int)num_clusters, (int)clusters.size()); - printf("num_regions: %d - %d\n", (int)num_regions, (int)regions.size()); - printf("num_counties: %d - %d\n", (int)num_counties, (int)counties.size()); - printf("num_cities: %d - %d\n", (int)num_cities, (int)cities.size()); - printf("num_districts: %d - %d\n", (int)num_districts, (int)districts.size()); } - -void DensityMap::split_area(struct area *area, int num_split, List &list) +void DensityMap::_bind_methods() +{ + ClassDB::bind_method(D_METHOD("get_population_density", "x", "y"), &DensityMap::get_population_density); +} +float DensityMap::get_population_density(float x, float y) { - Vector2i pstart; - struct area astart; - float mrad; - int count = 500 * num_split; - int orig_size = (int)list.size(); - if (area) { - pstart.x = area->pos.x; - pstart.y = area->pos.y; #if 0 - while (true) { - float angle = rnd->randf() * M_PI * 2.0f; - float off = rnd->randf() * area->radius; - float tx = cosf(angle) * off; - float ty = cosf(angle) * off; - int px = area->pos.x + (int)tx; - int py = area->pos.y + (int)ty; - if (px < 0 || px >= (int)world_x_size) - continue; - if (px < 0 || px >= (int)world_x_size) - continue; - pstart.x = px; - pstart.y = py; - break; - } + float n = (noise->get_noise_2d(x, y) + 1.0f) * 0.5f; + float d = curve->interpolate_baked(n); #endif - mrad = area->radius / (float)num_split; - } else { - pstart.x = rnd->randi() % world_x_size; - pstart.y = rnd->randi() % world_z_size; - mrad = sqrtf((float)world_x_size * (float)world_z_size) / (float)num_split; - mrad *= 0.9f; - pstart.x -= (int)mrad + 1; - pstart.y -= (int)mrad + 1; - } - astart.pos = pstart; - astart.radius = mrad; - astart.parent = area; - list.push_back(astart); - Vector2i cur(pstart.x, pstart.y); - while ((int)list.size() - orig_size < num_split && count-- > 0) { - float angle = rnd->randf() * M_PI * 2.0f; - float offt_x = cosf(angle) * mrad; - float offt_y = sinf(angle) * mrad; - int ox = (int)(cur.x + offt_x); - int oy = (int)(cur.y + offt_y); - if (ox < 0 || ox >= (int)world_x_size) - ox = (int)(cur.x - offt_x); - if (oy < 0 || ox >= (int)world_y_size) - oy = (int)(cur.y - offt_y); - if (area && !area->has_point(Vector2i(ox, oy))) - continue; - if (ox < 0 || ox >= (int)world_x_size) - continue; - if (oy < 0 || oy >= (int)world_z_size) - continue; - // printf("sample: %d %d %f\n", ox, oy, mrad); - const List::Element *e = list.front(); - bool good = true; - while (e) { - struct area a = e->get(); - int oxd = ox - a.pos.x; - int oyd = oy - a.pos.y; - int r = oxd * oxd + oyd * oyd; - if (r < mrad * mrad) { - good = false; - break; - } - e = e->next(); - } - if (good) { - struct area anext; - anext.pos.x = ox; - anext.pos.y = oy; - anext.radius = mrad; - anext.parent = area; - list.push_back(anext); - printf("result: %d %d %f\n", anext.pos.x, anext.pos.y, anext.radius); - } - } - if (count <= 0) - printf("list count %d\n", (int)list.size()); + float h = height_map->get_surface_height(x, y); + if (h < 0.0f || h > max_height) + return 0.0f; + float s = height_map->get_base_steepness(x, y); + if (s > 0.3f) + return 0.0f; + float d = 0.0f; + /* Still use curve for it? */ + if (h < max_height * 0.3f) + d = 1.0f; + else + d = max_height * 0.3f / h; + return d * s; } DensityMap::DensityMap() { rnd.instance(); - rnd->randomize(); seed = rnd->get_seed(); - world_x_size = 400000; - world_y_size = 1000; - world_z_size = 400000; - grid_size = 100; - num_clusters = 5; - num_regions = 15; - num_counties = 30; - num_cities = 5; - num_districts = 5; + max_height = 300.0f; } DensityMap::~DensityMap() { } + diff --git a/modules/world/density_map.h b/modules/world/density_map.h index 2d3c9dc..1ae2115 100644 --- a/modules/world/density_map.h +++ b/modules/world/density_map.h @@ -2,7 +2,9 @@ #define DENSITY_MAP_H #include #include +#include #include +#include "world_height_map.h" class DensityMap: public Resource { GDCLASS(DensityMap, Resource); @@ -10,47 +12,19 @@ public: DensityMap(); ~DensityMap(); protected: + Ref height_map; Ref rnd; - struct area { - Vector2i pos; - float radius; - struct area *parent; - inline bool has_point(const Vector2i &pt) - { - int tox = pos.x - pt.x; - int toy = pos.y - pt.y; - int tdst = tox * tox + toy * toy; - int rsq = (int)(radius * radius); - return tdst < rsq; - } - }; - List clusters; - List regions; - List counties; - List cities; - List districts; - int num_clusters; - int num_regions; - int num_counties; - int num_cities; - int num_districts; - HashMap > circle_grid; - void split_area(struct area *area, int num_split, List &list); - void populate_grid(List &list); - - void set_num_clusters(int num); - int get_num_clusters() const; - /* 100m grid */ - int grid_size; - uint32_t world_x_size; - uint32_t world_y_size; - uint32_t world_z_size; - Vector3i world_center; - int seed; + Ref noise; + Ref curve; + float max_height; bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List *p_list) const; + static void _bind_methods(); public: - void update_clusters(); + void update_all(); + float get_population_density(float x, float y); +private: + int seed; }; #endif diff --git a/modules/world/register_types.cpp b/modules/world/register_types.cpp index 52ed283..d597418 100644 --- a/modules/world/register_types.cpp +++ b/modules/world/register_types.cpp @@ -8,6 +8,9 @@ #include "characters.h" #include "smart_object.h" #include "road_map.h" +#include "world_height_map.h" +#include "road_grid.h" +#include "roads.h" void register_world_types() { @@ -15,7 +18,10 @@ void register_world_types() Engine::get_singleton()->add_singleton(Engine::Singleton("RoadMap", RoadMap::get_singleton())); WorldMapData::create_singleton(); Engine::get_singleton()->add_singleton(Engine::Singleton("WorldMapData", WorldMapData::get_singleton())); + RoadsData::create_singleton(); + Engine::get_singleton()->add_singleton(Engine::Singleton("RoadsData", RoadsData::get_singleton())); ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); @@ -23,6 +29,8 @@ void register_world_types() ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_virtual_class(); + ClassDB::register_class(); #if TOOLS_ENABLED EditorPlugins::add_by_type(); #endif diff --git a/modules/world/road_grid.cpp b/modules/world/road_grid.cpp new file mode 100644 index 0000000..4563343 --- /dev/null +++ b/modules/world/road_grid.cpp @@ -0,0 +1,331 @@ +#include +#include +#include +#include +#include +#include +#include +#include "road_grid.h" + +RoadGrid::RoadGrid() +{ + grid_width = 16; + grid_height = 16; + class_sizes[SITE_EMPTY] = 10000; + class_sizes[SITE_TOWN] = 100000; + class_sizes[SITE_FARM] = 500000; + class_sizes[SITE_FOREST] = 1000000; + class_sizes[SITE_UNASSIGNED] = 2000000; +} + +RoadGrid::~RoadGrid() +{ +} + +/* TODO: constants, configuration */ +Dictionary RoadGrid::build_diagram(int npatches, int center_count, int center_step, int spread, int dim) +{ + printf("build_diagram %d %d %d %d %d\n", npatches, center_count, center_step, spread, dim); + Vector centers; + /* zero is always used */ + centers.push_back(Vector2i(0, 0)); + float sa = rnd->randf() * 2.0 * Math_PI; + int i, bad = 0, cx, cp; + while (centers.size() < center_count) { + int center_x = CLAMP(center_step * (int)((rnd->randi() % spread) - spread / 2), -dim, dim); + int center_y = CLAMP(center_step * (int)((rnd->randi() % spread) - spread / 2), -dim, dim); + Vector2i c(center_x, center_y); + if (centers.find(c) < 0) { + centers.push_back(c); + bad = 0; + } else + bad++; + if (bad > 1000) + break; + } + assert(centers.size() > 1); + Vector points; + for (cx = 0; cx < centers.size(); cx++) { + float maxr = 0.0f; + for (cp = 0; cp < npatches * 8; cp++) { + float a = sa + sqrtf((float)cp) * 8.0f; + float r = (cp == 0) ? 0.0f : 100.0f + (float)cp * 100.0f + 50.0f * rnd->randf(); + float x = floor(cosf(a) * r + (float)centers[cx].x); + float y = floor(sinf(a) * r + (float)centers[cx].y); + if (maxr < r) + maxr = r; + Vector2 p(x, y); + if (points.find(p) < 0) + points.push_back(p); + } + struct cluster cst; + cst.c = centers[cx]; + cst.r = maxr + 50.0f; + clusters.push_back(cst); + } + PoolVector cpoints; + cpoints.resize(points.size()); + memcpy(cpoints.write().ptr(), points.ptr(), points.size() * sizeof(Vector2)); + + Dictionary diagram = Voronoi::get_singleton()->generate_diagram(cpoints, 11); + return diagram; +} +bool RoadGrid::segment_intersects_rect(const Vector2 &a, const Vector2 &b, const Rect2 &rect) +{ + real_t min = 0, max = 1; + const Vector2 &p_from = a; + const Vector2 &p_to = b; + + for (int i = 0; i < 2; i++) { + real_t seg_from = p_from[i]; + real_t seg_to = p_to[i]; + real_t box_begin = rect.position[i]; + real_t box_end = box_begin + rect.size[i]; + real_t cmin, cmax; + + if (seg_from < seg_to) { + if (seg_from > box_end || seg_to < box_begin) { + return false; + } + real_t length = seg_to - seg_from; + cmin = (seg_from < box_begin) ? ((box_begin - seg_from) / length) : 0; + cmax = (seg_to > box_end) ? ((box_end - seg_from) / length) : 1; + + } else { + if (seg_to > box_end || seg_from < box_begin) { + return false; + } + real_t length = seg_to - seg_from; + cmin = (seg_from > box_end) ? (box_end - seg_from) / length : 0; + cmax = (seg_to < box_begin) ? (box_begin - seg_from) / length : 1; + } + + if (cmin > min) { + min = cmin; + } + if (cmax < max) { + max = cmax; + } + if (max < min) { + return false; + } + } + + return true; +} + +void RoadGrid::draw_debug(Node *drawable, int size_x, int size_y) const +{ + int i, j; + CanvasItem *ci = Object::cast_to(drawable); + if (!ci) + return; + if (bounds.size.x <= 0.0f || bounds.size.y <= 0.0f) { + printf("zero bounds %f %f\n", bounds.size.x, bounds.size.y); + return; + } + for (i = 0; i < map_sites.size(); i++) { + Vector2 a = (map_sites[i].pos - bounds.position) * Vector2(size_x, size_y) / bounds.size; + ci->draw_circle(a, 4.0f, Color(0.0f, 0.5f, 0.5f, 1.0f)); + } + for (i = 0; i < map_hedges.size(); i++) { + Vector2 xa = diagram_vertices[map_hedges[i]->a]; + Vector2 xb = diagram_vertices[map_hedges[i]->b]; + Vector2 a = (xa - bounds.position) * Vector2(size_x, size_y) / bounds.size; + Vector2 b = (xb - bounds.position) * Vector2(size_x, size_y) / bounds.size; + ci->draw_line(a, b, Color(0.0f, 0.7f, 0.7f, 1.0f), 3.0f, true); + } + Rect2i g = rect_to_grid(bounds); + for (i = g.position.y - 1; i < g.position.y + g.size.y + 1; i++) { + for (j = g.position.x - 1; j < g.position.x + g.size.x + 1; j++) { + if (hedge_grid.has(j) && hedge_grid[j].has(i)) { + List items = hedge_grid[j][i]; + List::Element *e; + for (e = items.front(); e; e = e->next()) { + struct half_edge *he = e->get(); + + assert(he->a >= 0); + assert(he->b >= 0); + assert(he->a != he->b); + assert(he->site >= 0); + + Vector2 xa = diagram_vertices[he->a]; + Vector2 xb = diagram_vertices[he->b]; + const struct map_site *site = &map_sites[he->site]; + Vector2 pos = (site->pos - bounds.position) * Vector2(size_x, size_y) / bounds.size; + ci->draw_circle(pos, 3.0f, Color(0.2f, 0.2f, 0.5f, 1.0f)); + Vector2 a = (xa - bounds.position) * Vector2(size_x, size_y) / bounds.size; + Vector2 b = (xb - bounds.position) * Vector2(size_x, size_y) / bounds.size; + ci->draw_line(a, b, Color(0.2f, 0.2f, 0.7f, 1.0f), 2.0f, true); + } + } + } + } +} + +void RoadGrid::index_site(struct map_site *site) +{ + int i; + site->vertices_ind.resize(site->vertices.size()); + site->polygon_ind.resize(site->polygon.size()); + /* slow as fuck */ + for (i = 0; i < site->vertices.size(); i++) { + int idx = diagram_vertices.find(site->vertices[i]); + if (idx < 0) { + idx = diagram_vertices.size(); + diagram_vertices.push_back(site->vertices[i]); + } + site->vertices_ind.write[i] = idx; + } + for (i = 0; i < site->polygon.size(); i++) { + int idx = diagram_vertices.find(site->polygon[i]); + if (idx < 0) { + idx = diagram_vertices.size(); + diagram_vertices.push_back(site->polygon[i]); + } + site->polygon_ind.write[i] = idx; + } + site->hedges.resize(site->polygon.size()); + for (i = 0; i < site->polygon.size(); i++) { + int idx1 = site->polygon_ind[i]; + int idx2 = site->polygon_ind[(i + 1) % site->polygon.size()]; + struct half_edge he; + he.a = idx1; + he.b = idx2; + he.site = site->index; + /* use length to decide */ + he.depth = 6.0f; + he.length = diagram_vertices[idx1].distance_to(diagram_vertices[idx2]); + site->hedges.write[i] = he; + } +} + +void RoadGrid::process_diagram(const Dictionary &diagram) +{ + const Array &sites = diagram["sites"]; + int i, j; + map_sites.resize(sites.size()); + int hedge_count = 0, hedge_idx; + printf("start processing sites\n"); + for (i = 0; i < sites.size(); i++) { + struct map_site site; + const Dictionary &site_data = sites[i]; + const Array &graphedges = site_data["graphedges"]; + printf("processing site: %d\n", i); + site.graphedges.resize(graphedges.size()); + for (j = 0; j < graphedges.size(); j++) { + const Dictionary &ge_data = graphedges[j]; + struct graphedge ge; + ge.a = ge_data["a"]; + ge.b = ge_data["b"]; + ge.edge = ge_data["edge"]; + site.graphedges.write[j] = ge; + } + site.index = site_data["index"]; + site.pos = site_data["pos"]; + site.polygon = site_data["polygon"]; + site.vertices = site_data["vertices"]; + site.site_type = SITE_UNASSIGNED; + site.cluster = -1; + index_site(&site); + hedge_count += site.hedges.size(); + map_sites.write[i] = site; + } + printf("processing sites done\n"); + /* Fill global half edges array and put in grid */ + printf("processing %d half edges\n", hedge_count); + map_hedges.resize(hedge_count); + hedge_idx = 0; + for (i = 0; i < map_sites.size(); i++) { + for (j = 0; j < map_sites[i].hedges.size(); j++) { + /* bad bad constness */ + struct half_edge *hedge = &map_sites.write[i].hedges.write[j]; + map_hedges.write[hedge_idx] = hedge; + add_hedge_to_grid(hedge); + hedge_idx++; + } + } + printf("processing half edges done\n"); + classify_sites(); + printf("processing done, sites count: %d\n", map_sites.size()); +} + +void RoadGrid::build(Ref curve, Ref noise) +{ + rnd.instance(); + rnd->randomize(); + printf("build_diagram\n"); + // Dictionary diagram = build_diagram(8, 2 + (rnd->randi() % 2), 100, 100, 50); + Dictionary diagram = build_diagram(8, 2, 100, 100, 30); + printf("build_diagram done\n"); + printf("process_diagram\n"); + process_diagram(diagram); + printf("process_diagram done\n"); + printf("%d %d\n", curve.is_valid(), noise.is_valid()); + assert(curve.is_valid() && noise.is_valid()); + int i; + if (curve.is_valid() && noise.is_valid()) { + printf("building 3rd dimention\n"); + diagram_vertex_heights.resize(diagram_vertices.size()); + for (i = 0; i < diagram_vertices.size(); i++) { + float n = noise->get_noise_2dv(diagram_vertices[i]); + float t = (n + 1.0f) * 0.5f; + float d = MAX(1.0f, curve->interpolate_baked(t)); + diagram_vertex_heights.write[i] = d; + } + for (i = 0; i < map_hedges.size(); i++) { + int x1 = map_hedges[i]->a; + int x2 = map_hedges[i]->b; + float xd = diagram_vertices[x1].distance_squared_to(diagram_vertices[x2]); + float dh = fabsf(diagram_vertex_heights[x2] - diagram_vertex_heights[x1]); + if (fabsf(dh / xd) > 0.02f) + diagram_vertex_heights.write[x2] = diagram_vertex_heights[x1] + dh / fabsf(dh) * 0.02f * xd; + } + printf("building 3rd dimention done\n"); + } +} + +Vector2 RoadGrid::get_influence(int x, int y) const +{ + static int mind = 1000000; + static int maxd = 0; + List hlist; + if (hedge_grid.has(x / grid_width) && hedge_grid[x / grid_width].has(y / grid_height)) + hlist = hedge_grid[x / grid_width][y / grid_height]; + if (hlist.size() == 0) + return Vector2(); + List::Element *e; + for (e = hlist.front(); e; e = e->next()) { + struct half_edge *he = e->get(); + Vector2 a = diagram_vertices[he->a]; + Vector2 b = diagram_vertices[he->b]; + Vector2 p(x, y); + Vector2 seg[] = {a, b}; + Vector2 pt = Geometry::get_closest_point_to_segment_2d(p, seg); + float d = pt.distance_squared_to(p); + if (d < he->depth * he->depth) { + Vector2 ret; + ret.x = 1.0f; + assert(diagram_vertex_heights.size() > he->a); + assert(diagram_vertex_heights.size() > he->b); + float h1 = diagram_vertex_heights[he->a]; + float h2 = diagram_vertex_heights[he->b]; + float l = he->length; + assert(l > 0.0f); + float m1 = pt.distance_to(a) / l; + float m2 = CLAMP(1.0f - m1, 0.0f, 1.0f); + float h = h1 * (1.0f - m1) + h2 * (1.0f - m2); + ret.y = h; + return ret; + } + } + return Vector2(); +} + +void RoadGrid::_bind_methods() +{ + ClassDB::bind_method(D_METHOD("draw_debug", "drawable", "size_x", "size_y"), &RoadGrid::draw_debug); + ClassDB::bind_method(D_METHOD("get_influence", "x", "y"), &RoadGrid::get_influence); + ClassDB::bind_method(D_METHOD("build", "curve", "noise"), &RoadGrid::build); +} + diff --git a/modules/world/road_grid.h b/modules/world/road_grid.h new file mode 100644 index 0000000..906ac12 --- /dev/null +++ b/modules/world/road_grid.h @@ -0,0 +1,197 @@ +#ifndef ROAD_GRID_H +#define ROAD_GRID_H +#include +#include +#include +#include +#include + +class CanvasItem; + +/* TODO: + * a + * - Implement diagram edges with site reference. + * - Implement draw edges according to current position, + * indicating which are drawn. + * - Implement removal of draw edges by distance + * + * - Rewrite debug draw using distance logic. + * - Implement 3D position for road vertices based on heightmap object; + * - Implement SDF influence calculation based on distance to road + * - Implement SDF calculation for roads (height cutoff by segment) + * b + * - Implement nodes (intersections) for drawing road intersections + * - Implement draw edge splitting and position improvement. + * - Find "wall" edges? + * c + * - Implement lot system based on sites + * - Split lots to place buildings and other environment + * objects + * d + * - Implement 3D positions and geometry generation + */ + +class RoadGrid: public Reference { + GDCLASS(RoadGrid, Object) +protected: + Ref rnd; + struct cluster { + Vector2i c; + float r; + }; + List clusters; + Dictionary build_diagram(int npatches, int center_count, int center_step, + int spread, int dim); + HashMap class_sizes; + struct half_edge; + HashMap > > hedge_grid; + Rect2 bounds; + void set_class_size(int cl, int sz) + { + class_sizes[cl] = sz; + } + static void _bind_methods(); + enum { + SITE_UNASSIGNED = 0, + SITE_FOREST, + SITE_FARM, + SITE_TOWN, + SITE_EMPTY, + SITE_MAX + }; + struct graphedge { + int a, b; + int edge; + }; + struct half_edge { + int a, b; + int site; + float depth; + float length; + }; + struct map_site { + int index; + Vector2 pos; + Vector graphedges; + Vector vertices; + Vector polygon; + Vector vertices_ind; + Vector polygon_ind; + int site_type; + int cluster; + Vector hedges; + }; + void index_site(struct map_site *site); + Vector diagram_vertices; + Vector diagram_vertex_heights; + Vector map_sites; + Vector map_hedges; + void classify_sites() + { + int i, j; + for (j = 0; j < map_sites.size(); j++) { + Rect2 r; + for (i = 0; i < map_sites[j].polygon.size(); i++) { + if (i == 0) { + r.position = map_sites[j].polygon[0]; + r.size = Vector2(); + } else { + r.expand_to(map_sites[j].polygon[i]); + } + } + int cl_area = (int)(r.get_area() + 1.0f); + for (i = 0; i < SITE_MAX; i++) { + if (class_sizes.has(i)) + if (cl_area <= class_sizes[i]) { + map_sites.write[j].site_type = i; + break; + } + } + printf("area: %d class: %d\n", cl_area, map_sites[j].site_type); + } + } + void process_diagram(const Dictionary &diagram); + bool segment_intersects_rect(const Vector2 &a, const Vector2 &b, const Rect2 &rect); + inline bool segment_in_grid_rect(const Vector2 &a, const Vector2 &b, int x, int y) + { + Rect2 r((float)(x * grid_width), (float)(y * grid_height), grid_width, grid_height); + return segment_intersects_rect(a, b, r); + } + inline Rect2 segment_to_rect(const Vector2 &a, const Vector2 &b) const + { + Rect2 r(a, Vector2()); + r.expand_to(b); + return r; + } + inline Rect2i rect_to_grid(const Rect2 &r) const + { + Rect2i ret; + ret.position.x = get_grid_x(r.position.x); + ret.position.y = get_grid_y(r.position.y); + ret.size.x = get_grid_x(r.size.x) + 1; + ret.size.y = get_grid_y(r.size.y) + 1; + return ret; + } + inline Rect2 get_grid_rect(int x, int y) const + { + Rect2 rect; + rect.position.x = (float)x * grid_width; + rect.position.y = (float)y * grid_height; + rect.size.x = grid_width; + rect.size.x = grid_height; + return rect; + } + inline void insert_hedge_to_grid_cell(int x, int y, struct half_edge *hedge) + { + if (hedge_grid.has(x) && hedge_grid[x].has(y)) + hedge_grid[x][y].push_back(hedge); + else { + List items; + items.push_back(hedge); + hedge_grid[x][y] = items; + } + } + inline void add_hedge_to_grid(struct half_edge *hedge) + { + Vector2 a = diagram_vertices[hedge->a]; + Vector2 b = diagram_vertices[hedge->b]; + + if (bounds.position.length_squared() == 0.0f && bounds.position.length_squared() == 0.0f) { + bounds.position.x = a.x; + bounds.position.y = a.y; + } else + bounds.expand_to(a); + + bounds.expand_to(b); + + Rect2 r = segment_to_rect(a, b).grow(1.0f); + Rect2i rgrid = rect_to_grid(r); + int x, y; + for (y = 0; y < rgrid.size.y; y++) { + for (x = 0; x < rgrid.size.x; x++) { + int px = rgrid.position.x + x; + int py = rgrid.position.y + y; + Rect2 xr = get_grid_rect(px, py).grow(16.0f); + if (segment_intersects_rect(a, b, xr)) + insert_hedge_to_grid_cell(px, py, hedge); + } + } + } + inline int get_grid_x(float x) const + { + return (int)(x / grid_width); + } + inline int get_grid_y(float y) const + { + return (int)(y / grid_height); + } + float grid_width, grid_height; + friend class Roads; +public: + void build(Ref curve, Ref noise); + void draw_debug(Node *drawable, int size_x, int size_y) const; + Vector2 get_influence(int x, int y) const; + RoadGrid(); + ~RoadGrid(); +}; +#endif diff --git a/modules/world/road_map.cpp b/modules/world/road_map.cpp index f28a1cf..ea97bed 100644 --- a/modules/world/road_map.cpp +++ b/modules/world/road_map.cpp @@ -1,13 +1,13 @@ #include "road_map.h" -#define ROAD_MAP_TESTS - -#define MIN_X -10000 -#define MAX_X 10000 -#define MIN_Z -10000 -#define MAX_Z 10000 -#define MIN_Y -1000 -#define MAX_Y 1000 +#define MIN_X (-10000) +#define MAX_X (10000) +#define MIN_Z (-10000) +#define MAX_Z (10000) +#define MIN_Y (-100) +#define MAX_Y (300) +#define HIGHWAY_LENGTH_MIN 100 +#define HIGHWAY_LENGTH_MAX 300 static RoadMap *g_road_map_data = NULL; RoadMap *RoadMap::get_singleton() @@ -25,9 +25,40 @@ void RoadMap::destroy_singleton() } RoadMap::RoadMap() { + noise.instance(); + rnd.instance(); } RoadMap::~RoadMap() { } +class PrimaryNetwork { + Vector points; + friend class RoadMap; + void start(); +}; + +void PrimaryNetwork::start() +{ +#if 0 + Vector3 start_pos, start_dir; + float l, a; + Transform xform; + + start_pos.x = MIN_X + ((float)(MAX_X - MIN_X)) * rnd.randf(); + start_pos.y = MIN_Y + ((float)(MAX_X - MIN_Y)) * rnd.randf(); + start_pos.z = MIN_Z + ((float)(MAX_X - MIN_Z)) * rnd.randf(); + l = HIGHWAY_LENGTH_MIN + ((float)(HIGHWAY_LENGTH_MAX - HIGHWAY_LENGTH_MIN)) * rnd.randf(); + start_dir = Vector3(l, 0.0f, 0.0f); + start_pos.y = CLAMP(start_pos.y, 1.0f, MAX_Y); + xform = Transform(); +#endif +} + +void RoadMap::gen_primary_network() +{ + PrimaryNetwork p; + p.start(); +} + diff --git a/modules/world/road_map.h b/modules/world/road_map.h index 40f12c9..5d0af4a 100644 --- a/modules/world/road_map.h +++ b/modules/world/road_map.h @@ -1,4 +1,7 @@ #include +#include +#include +#include class RoadMap: public Object { GDCLASS(RoadMap, Object) @@ -10,26 +13,47 @@ public: static void destroy_singleton(); protected: - Vector vertices; - struct segment { - int v1, v2; - uint32_t flags; + Ref noise; + Ref rnd; + struct Segment { + Vector3 p1, p2; }; - Vector segments; - struct intersection { -#define MAX_NEIGHBORS 4 - int neighbors[MAX_NEIGHBORS]; - int ncount; - uint32_t flags; + Vector segments; + struct Pq { + struct PqItem { + struct Segment data; + int priority; + }; + List queue; + void push(int priority, const struct Segment &seg) + { + List::Element *e; + PqItem it; + it.data = seg; + it.priority = priority; + + for (e = queue.front(); e; e = e->next()) { + PqItem item = e->get(); + if (item.priority > priority) { + queue.insert_before(e, it); + break; + } + if (!e->next()) + queue.push_back(it); + + } + } + Segment peek() + { + return queue.front()->get().data; + } + Segment pop() + { + Segment seg = queue.front()->get().data; + queue.pop_front(); + return seg; + } }; - Vector intersections; - /* cylindric area to define road */ - struct area { - int x, z; - int radius; - int type; - }; - Vector areas; + struct Pq queue; + void gen_primary_network(); }; - - diff --git a/modules/world/road_map2.h b/modules/world/road_map2.h new file mode 100644 index 0000000..b3ba3f2 --- /dev/null +++ b/modules/world/road_map2.h @@ -0,0 +1,23 @@ +#ifndef ROAD_MAP2 +#define ROAD_MAP2 +#include +#include +#include +#include "density_map.h" + +class RoadMap2: public Resource { + GDCLASS(RoadMap2, Resource); +public: + RoadMap2(); + ~RoadMap2(); +protected: + Ref rnd; + Ref noise; + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List *p_list) const; + static void _bind_methods(); +private: + int seed; +}; +#endif diff --git a/modules/world/roads.cpp b/modules/world/roads.cpp new file mode 100644 index 0000000..9a8c76a --- /dev/null +++ b/modules/world/roads.cpp @@ -0,0 +1,600 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "roads.h" + +void Roads::_bind_methods() +{ + ClassDB::bind_method(D_METHOD("curve_mesh", "points", "width", "flags", "sidewalk_width"), &Roads::curve_mesh); + ClassDB::bind_method(D_METHOD("add_scene_element", "root", "surface", "p2", "shape"), &Roads::add_scene_element); +} +void Roads::_get_property_list(List *p_list) const +{ + p_list->push_back(PropertyInfo(Variant::OBJECT, "road_data", PROPERTY_HINT_RESOURCE_TYPE, "PackedScene")); + p_list->push_back(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve")); + p_list->push_back(PropertyInfo(Variant::OBJECT, "noise", PROPERTY_HINT_RESOURCE_TYPE, "OpenSimplexNoise")); +} +bool Roads::_get(const StringName &p_name, Variant &r_ret) const +{ + if (p_name == "road_data") { + r_ret = road_data; + return true; + } else if (p_name == "curve") { + r_ret = curve; + return true; + } else if (p_name == "noise") { + r_ret = noise; + return true; + } + return false; +} +bool Roads::_set(const StringName &p_name, const Variant &p_value) +{ + bool update = false; + if (p_name == "road_data") { + road_data = p_value; + update = true; + } else if (p_name == "curve") { + curve = p_value; + update = true; + } else if (p_name == "noise") { + noise = p_value; + update = true; + } + if (update) { + update_all(); + _change_notify(); + } + + return update; +} +void Roads::sort_angle(Vector &sort_data) +{ + struct comparator { + Vector3 *vertices; + bool operator()(int a, int b) const { + Vector3 p1 = vertices[a]; + Vector3 p2 = vertices[b]; + Vector2 rp1(p1.x, p1.z); + Vector2 rp2(p2.x, p2.z); + return rp1.angle() < rp2.angle(); + } + }; + SortArray sorter; + sorter.compare.vertices = vertices.write().ptr(); + sorter.sort(sort_data.ptrw(), sort_data.size()); +} +int Roads::find_edge(int a, int b) +{ + int i; + RoadGrid *rg = RoadsData::get_singleton()->get_road_grid(); + for (i = 0; i < rg->map_hedges.size(); i++) { + if (rg->map_hedges[i]->a == a && + rg->map_hedges[i]->b == b) + return i; + } + return -1; +} +void Roads::setup_vshapes() +{ + int i, j; + RoadGrid *rg = RoadsData::get_singleton()->get_road_grid(); + vertices.resize(rg->diagram_vertices.size()); + for (i = 0; i < vertices.size(); i++) { + vertices.write()[i].x = rg->diagram_vertices[i].x; + vertices.write()[i].y = rg->diagram_vertex_heights[i]; + vertices.write()[i].z = rg->diagram_vertices[i].y; + } + List vdata_list; + for (i = 0; i < rg->map_hedges.size(); i++) { + for (j = 0; j < rg->map_hedges.size(); j++) { + if (i == j) + continue; + if (rg->map_hedges[i]->b != + rg->map_hedges[j]->a) + continue; + if (rg->map_hedges[i]->site != + rg->map_hedges[j]->site) + continue; + int a, b1, b2; + struct vshape v; + /* star topology */ + a = rg->map_hedges[i]->b; + b1 = rg->map_hedges[i]->a; + b2 = rg->map_hedges[j]->b; + v.e1 = i; + v.e2 = j; + v.site = rg->map_hedges[i]->site; + v.area.position = vertices[a]; + v.area.expand_to(vertices[b1] + Vector3(0, 1, 0)); + v.area.expand_to(vertices[b2] + Vector3(0, -1, 0)); + v.instance = -1; + Vector3 p1 = vertices[rg->map_hedges[v.e1]->a]; + Vector3 p2 = vertices[rg->map_hedges[v.e1]->b]; + Vector3 p3 = vertices[rg->map_hedges[v.e2]->b]; + p1 = (p2 + (p1 - p2) * 0.5f).snapped(Vector3(4.0f, 0.1f, 4.0f)); + p3 = (p2 + (p3 - p2) * 0.5f).snapped(Vector3(4.0f, 0.1f, 4.0f)); + p2 = p2.snapped(Vector3(4.0f, 0.1f, 4.0f)); + v.p1 = p1; + v.p2 = p2; + v.p3 = p3; + /* add v-shape only if we can actually generate it */ + if (v.p1.distance_squared_to(v.p2) > 2.0f && + v.p2.distance_squared_to(v.p3) > 2.0f && + v.p1.distance_squared_to(v.p3) > 2.0f) + vdata_list.push_back(v); + } + } + vshapes.resize(vdata_list.size()); + for (i = 0; i < vdata_list.size(); i++) + vshapes.write()[i] = vdata_list[i]; + for (i = 0; i < vshapes.size(); i++) { + for (j = 0; j < vshapes.size(); j++) { + if (i == j) + continue; + if (vshapes[i].e1 == vshapes[j].e1) + vshapes.write()[j].p1 = vshapes[i].p1; + if (vshapes[i].e2 == vshapes[j].e1) + vshapes.write()[j].p1 = vshapes[i].p3; + if (vshapes[i].e1 == vshapes[j].e2) + vshapes.write()[j].p3 = vshapes[i].p1; + if (vshapes[i].e2 == vshapes[j].e2) + vshapes.write()[j].p3 = vshapes[i].p3; + } + } + + for (i = 0; i < vshapes.size(); i++) { + const struct vshape &v = vshapes[i]; + assert(rg->map_hedges[v.e1]->site == rg->map_hedges[v.e2]->site); + assert(v.e1 >= 0 && v.e2 >= 0 && v.e1 != v.e2); + int e1a = rg->map_hedges[vshapes[i].e1]->a; + int e1b = rg->map_hedges[vshapes[i].e1]->b; + int e2a = rg->map_hedges[vshapes[i].e2]->a; + int e2b = rg->map_hedges[vshapes[i].e2]->b; + printf("vshape %d: %d: %d: %f %f %f -> %d: %f %f %f -> %d: %d: %f %f %f -> %d: %f %f %f\n", + i, + vshapes[i].e1, + e1a, + vertices[e1a].x, + vertices[e1a].y, + vertices[e1a].z, + e1b, + vertices[e1b].x, + vertices[e1b].y, + vertices[e1b].z, + vshapes[i].e2, + e2a, + vertices[e2a].x, + vertices[e2a].y, + vertices[e2a].z, + e2b, + vertices[e2b].x, + vertices[e2b].y, + vertices[e2b].z + ); + } +} +void Roads::update_all() +{ + int i; + RoadGrid *rg = RoadsData::get_singleton()->get_road_grid(); + if (road_data.is_valid()) { + Node *tmp = road_data->instance(); + for (i = 0; i < tmp->get_child_count(); i++) { + Node *c = tmp->get_child(i); + MeshInstance *mi = Object::cast_to(c); + if (mi) { + String name = mi->get_name(); + Ref mesh = mi->get_mesh(); + Array data = mesh->surface_get_arrays(0); + mesh_data[name] = data; + if (name == "road_main") + mat = mesh->surface_get_material(0); + } + } + if (curve.is_valid() && noise.is_valid()) + rg->build(curve, noise); + printf("vertices: %d\n", rg->diagram_vertices.size()); + printf("heights: %d\n", rg->diagram_vertex_heights.size()); + printf("edges: %d\n", rg->map_hedges.size()); + setup_vshapes(); + } +} + +Roads::Roads() +{ + body = memnew(StaticBody); +} +Roads::~Roads() +{ + memdelete(body); +} +Vector3 Roads::calculate_offsets(const Array &data) const +{ + float minx = 0.0f, maxx = 0.0f, minz = 0.0f, maxz = 0.0f; + PoolVector verts = data[Mesh::ARRAY_VERTEX]; + int i; + Vector3 ret; + for (i = 0; i < verts.size(); i++) { + if (minx > verts.read()[i].x) + minx = verts.read()[i].x; + if (maxx < verts.read()[i].x) + maxx = verts.read()[i].x; + if (minz > verts.read()[i].z) + minz = verts.read()[i].z; + if (maxz < verts.read()[i].z) + maxz = verts.read()[i].z; + } + ret.x = fabsf(maxx - minx); + ret.z = fabsf(maxz - minz); + return ret; +} +Vector3 Roads::quadratic_bezier(const Vector3 &p0, const Vector3 &p1, + const Vector3 &p2, float t) const +{ + Vector3 q0 = p0.linear_interpolate(p1, t); + Vector3 q1 = p1.linear_interpolate(p2, t); + Vector3 r = q0.linear_interpolate(q1, t); + return r; +} +void Roads::all_offsets() +{ + const String *k; + for (k = mesh_data.next(NULL); k; k = mesh_data.next(k)) + offsets[*k] = calculate_offsets(mesh_data[*k]); +} +enum { + FLAGS_SIDEWALK = (1 << 0), + FLAGS_INTERSECTION = (1 << 1), + FLAGS_WALL = (1 << 2), +}; + +PoolVector Roads::build_item_list(float width, int flags, float sidewalk_width) const +{ + PoolVector ret; + float tx = 0.0f; + while (tx < width) { + ret.push_back("road_main"); + tx += offsets["road_main"].x; + } + if (flags & FLAGS_SIDEWALK) { + ret.push_back("sidewalk_start"); + tx = 0.0f; + while (tx < sidewalk_width) { + ret.push_back("sidewalk"); + tx += offsets["sidewalk"].x; + } + ret.push_back("sidewalk_end"); + } + if (flags & FLAGS_WALL) + ret.push_back("wall"); + return ret; +} +Ref Roads::build_curve(Vector3 p1, Vector3 p2, Vector3 p3, float total_width) const +{ + Ref curve3; + curve3.instance(); + Vector3 dir1 = (p1 - p2).normalized(); + Vector3 dir2 = (p3 - p2).normalized(); + float qt = 0.0f; + curve3->add_point(p1); + while (qt <= 1.0f) { + Vector3 p = quadratic_bezier(p2 + dir1 * total_width * 1.8f, p2, p2 + dir2 * total_width * 1.8f, qt); + curve3->add_point(p.snapped(Vector3(0.1, 0.1, 0.1))); + qt += 0.1f; + } + curve3->add_point(p3); + curve3->set_bake_interval(4.0f); + assert(curve3->get_baked_length() > 0); + return curve3; +} + +Array Roads::curve_mesh(PoolVector points, float width, int flags, float sidewalk_sidth) +{ + float tx = 0.0f, total_width = 0.0f, t = 0.0f, l; + int i; + Array ret; + all_offsets(); + PoolVector parts_list = build_item_list(width, flags, sidewalk_sidth); + for (i = 0; i < parts_list.size(); i++) + total_width += offsets[parts_list[i]].x; + assert(total_width >= 3.0f); + Ref curve3 = build_curve(points[0], points[1], points[2], total_width); + l = curve3->get_baked_length(); + assert(l > 0.0f); + PoolVector new_verts, new_normals; + PoolVector new_uvs; + PoolVector new_index; + ret.resize(Mesh::ARRAY_MAX); + + while (t <= l) { + tx = 0.0f; + int part = 0; + while (tx < total_width) { + int k; + Array data = mesh_data[parts_list[part]]; + int b = new_verts.size(); + PoolVector verts = data[Mesh::ARRAY_VERTEX]; + PoolVector normals = data[Mesh::ARRAY_NORMAL]; + new_verts.resize(b + verts.size()); + new_normals.resize(b + normals.size()); + Transform xform; + Vector3 offt1, offt2; + for (k = 0; k < verts.size(); k++) { + Vector3 base = verts[k]; + float point = t + 2.0 + base.z; + float right = verts[k].x + tx; + if (t <= l - 2.0) { + offt1 = curve3->interpolate_baked(point, true); + offt2 = curve3->interpolate_baked(point + 2.0, true); + assert(offt1.distance_squared_to(offt2) > 0.0f); + xform = Transform(Basis(), offt1).looking_at(offt2, Vector3(0.0f, 1.0f, 0.0f)); + } else { + offt1 = curve3->interpolate_baked(point - 2.0, true); + offt2 = curve3->interpolate_baked(point, true); + assert(offt1.distance_squared_to(offt2) > 0.0f); + xform = Transform(Basis(), offt1).looking_at(offt2, Vector3(0.0f, 1.0f, 0.0f)); + offt1 = offt2; + } + xform.origin = Vector3(); + if (right < 0.25f) + right -= 0.15f; + Vector3 nvert = offt1 + xform.xform(Vector3(right, verts[k].y, 0.0)); + Vector3 n = xform.xform(normals[k]); + if (right < 0.15f && + ((flags & FLAGS_INTERSECTION) != 0) && + nvert.distance_squared_to(points[1]) < total_width * total_width * 2.5f) { + new_verts.write()[b + k] = points[1]; + new_normals.write()[b + k] = Vector3(0.0f, 1.0f, 0.0f); + } else { + new_verts.write()[b + k] = nvert; + new_normals.write()[b + k] = n; + } + } + new_uvs.append_array(data[Mesh::ARRAY_TEX_UV]); + int idx = new_index.size(); + PoolVector index = data[Mesh::ARRAY_INDEX]; + new_index.resize(idx + index.size()); + for (k = 0; k < index.size(); k++) + new_index.write()[idx + k] = index[k] + b; + tx += offsets[parts_list[part]].x; + part += 1; + if (part >= parts_list.size()) + break; + } + t += 2.0f; + } + ret[Mesh::ARRAY_VERTEX] = new_verts; + ret[Mesh::ARRAY_NORMAL] = new_normals; + ret[Mesh::ARRAY_TEX_UV] = new_uvs; + ret[Mesh::ARRAY_INDEX] = new_index; + + return ret; +} + +static Ref create_concave_polygon_shape(Vector surfaces) { + PoolVector face_points; + int face_points_size = 0; + + //find the correct size for face_points + for (int i = 0; i < surfaces.size(); i++) { + const Array &surface_arrays = surfaces[i]; + if (surface_arrays.size() == 0) { + // That surface is empty + continue; + } + // If the surface is not empty then it must have an expected amount of data arrays + ERR_CONTINUE(surface_arrays.size() != Mesh::ARRAY_MAX); + PoolVector indices = surface_arrays[Mesh::ARRAY_INDEX]; + face_points_size += indices.size(); + } + face_points.resize(face_points_size); + + if (face_points_size < 3) { + return Ref(); + } + + //copy the points into it + int face_points_offset = 0; + for (int i = 0; i < surfaces.size(); i++) { + const Array &surface_arrays = surfaces[i]; + if (surface_arrays.size() == 0) { + continue; + } + PoolVector positions = surface_arrays[Mesh::ARRAY_VERTEX]; + PoolVector indices = surface_arrays[Mesh::ARRAY_INDEX]; + + ERR_FAIL_COND_V(positions.size() < 3, Ref()); + ERR_FAIL_COND_V(indices.size() < 3, Ref()); + ERR_FAIL_COND_V(indices.size() % 3 != 0, Ref()); + + int face_points_count = face_points_offset + indices.size(); + + { + PoolVector::Write w = face_points.write(); + PoolVector::Read index_r = indices.read(); + PoolVector::Read position_r = positions.read(); + + for (int p = face_points_offset; p < face_points_count; ++p) { + w[p] = position_r[index_r[p - face_points_offset]]; + } + } + + face_points_offset += indices.size(); + } + + Ref shape = memnew(ConcavePolygonShape); + shape->set_faces(face_points); + return shape; +} + +int Roads::make_vmesh(Node *root, Ref mat, Ref mesh, MeshInstance *xmi, Vector3 p1, Vector3 p2, + Vector3 p3, float width, int flags, float sidewalk_width) +{ + Vector3 m1 = p1 - p2; + Vector3 m2 = Vector3(); + Vector3 m3 = p3 - p2; + Vector3 pts[] = {m1, m2, m3}; + int i; + PoolVector points; + assert(p1.distance_squared_to(p2) > 2.0f); + assert(p2.distance_squared_to(p3) > 2.0f); + assert(p1.distance_squared_to(p3) > 2.0f); + assert(m1.distance_squared_to(m2) > 2.0f); + assert(m2.distance_squared_to(m3) > 2.0f); + assert(m1.distance_squared_to(m3) > 2.0f); + points.resize(3); + for (i = 0; i < 3; i++) + points.write()[i] = pts[i].snapped(Vector3(4.0f, 0.1f, 4.0f)); + Array rdata = curve_mesh(points, width, flags, sidewalk_width); + Ref mdata = mesh; + assert(mdata.is_valid()); + mdata->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, rdata); + assert(mdata->get_surface_count() > 0); + Vector surfaces; + surfaces.push_back(rdata); + Ref shape = create_concave_polygon_shape(surfaces); + mdata->surface_set_material(0, mat); + xmi->set_mesh(mdata); + call_deferred("add_scene_element", root, xmi, p2, shape); + return xmi->get_instance_id(); +} + +void Roads::add_scene_element(Node *root, Node *xnode, const Vector3 &p2, Ref shape) +{ + MeshInstance *xmi = Object::cast_to(xnode); + if (!xmi) + return; + root->add_child(xmi); + Transform xform(Basis(), p2); + assert(xmi->get_mesh().is_valid() && xmi->get_mesh()->get_surface_count() > 0); + xmi->set_global_transform(xform); + CollisionShape *cs = memnew(CollisionShape); + cs->set_shape(shape); + body->add_child(cs); +} + +void Roads::process_vshapes() +{ + Transform xform = get_viewport()->get_camera()->get_global_transform(); + AABB camarea; + camarea.position = xform.origin; + camarea.grow_by(550.0f); + int i; + List active_vshapes; + printf("camera %f %f %f\n", xform.origin.x, xform.origin.y, xform.origin.z); + for (i = 0; i < vshapes.size(); i++) { + if (active_vshapes.size() > 32) + break; + if (vshapes[i].instance >= 0) + continue; +#if 0 + active_vshapes.push_back(i); +#else + if (vshapes[i].area.intersects(camarea)) + active_vshapes.push_back(i); + else if (vshapes[i].area.encloses(camarea)) + active_vshapes.push_back(i); + else if (camarea.encloses(vshapes[i].area)) + active_vshapes.push_back(i); + else if (camarea.intersects(vshapes[i].area)) + active_vshapes.push_back(i); +#endif + } + printf("active vshapes %d\n", active_vshapes.size()); + List::Element *e; + for (e = active_vshapes.front(); e; e = e->next()) { + i = e->get(); + const struct vshape &v = vshapes[i]; + assert(v.p1.distance_squared_to(v.p2) > 2.0f); + assert(v.p2.distance_squared_to(v.p3) > 2.0f); + assert(v.p1.distance_squared_to(v.p3) > 2.0f); + if (vshapes[i].instance < 0) { + if (thread.thread.is_started()) + thread.thread.wait_to_finish(); + thread.mat = mat; + thread.vshape = i; + thread.width = 6.0; + thread.sidewalk_width = 3.0; + thread.flags = FLAGS_SIDEWALK|FLAGS_INTERSECTION; + thread.root = this; + Ref mesh; + mesh.instance(); + thread.mesh = mesh; + thread.xmi = memnew(MeshInstance); + thread.thread.start(generate_threaded, &thread); + } + } +} + +void Roads::_notification(int p_what) +{ + switch(p_what) { + case NOTIFICATION_PROCESS: + if ((counter % 100) == 0) + process_vshapes(); + counter++; + break; + case NOTIFICATION_READY: + counter = 0; + set_process(true); + add_child(body); + break; + } +} +void Roads::generate_threaded(void *p_userdata) +{ + struct thread_data *data = (struct thread_data *)p_userdata; + Roads *obj = Object::cast_to(data->root); + obj->mutex.lock(); + Vector3 p1 = obj->vshapes[data->vshape].p1; + Vector3 p2 = obj->vshapes[data->vshape].p2; + Vector3 p3 = obj->vshapes[data->vshape].p3; + obj->mutex.unlock(); + int instance = obj->make_vmesh(obj, data->mat, data->mesh, data->xmi, p1, p2, p3, data->width, data->flags, data->sidewalk_width); + assert(instance >= 0); + obj->mutex.lock(); + obj->vshapes.write()[data->vshape].instance = instance; + obj->mutex.unlock(); +} + + +RoadsData::RoadsData() +{ + rg = memnew(RoadGrid); +} +RoadsData::~RoadsData() +{ + memdelete(rg); + rg = NULL; +} +static RoadsData *g_roads_data = NULL; +RoadsData* RoadsData::get_singleton() +{ + return g_roads_data; +} + +void RoadsData::create_singleton() +{ + g_roads_data = memnew(RoadsData); +} +void RoadsData::destroy_singleton() +{ + memdelete(g_roads_data); + g_roads_data = NULL; +} +RoadGrid *RoadsData::get_road_grid() +{ + return rg; +} +void RoadsData::_bind_methods() +{ + ClassDB::bind_method(D_METHOD("get_road_grid"), &RoadsData::get_road_grid); +} + + diff --git a/modules/world/roads.h b/modules/world/roads.h new file mode 100644 index 0000000..e7395eb --- /dev/null +++ b/modules/world/roads.h @@ -0,0 +1,104 @@ +#include + +#include +#include +#include +#include + +class StaticBody; + +class Roads: public MeshInstance { + GDCLASS(Roads, MeshInstance); +protected: + Mutex mutex; + Ref curve; + Ref noise; + Ref mat; + Ref road_data; + HashMap mesh_data; + HashMap offsets; + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List *p_list) const; + static void _bind_methods(); + void update_all(); + PoolVector vertices; + struct thread_data { + Thread thread; + Ref mat; + int vshape; + float width; + float sidewalk_width; + int flags; + Node *root; + Ref mesh; + MeshInstance *xmi; + }; + struct thread_data thread; + static void generate_threaded(void *p_userdata); + StaticBody *body; +#if 0 + struct edge_data { + int a; + int b; + float width; + bool sidewalk; + float sidewalk_width; + edge_data(): a(-1), b(-1), width(1.0f), sidewalk(false), sidewalk_width(1.0f) + {} + }; +#endif + struct vshape { + AABB area; + int instance; + int e1, e2; + int site; + Vector3 p1, p2, p3; + }; +#if 0 + PoolVector edges; +#endif + PoolVector vshapes; + void setup_vshapes(); + void sort_angle(Vector &sort_data); +#if 0 + void extrude_direct(Array &out, const Array &arrays, const struct edge_data *data) const; + void extrude_vshape(Array &out, const Array &arrays, const struct vshape *data) const; +#endif + int find_edge(int a, int b); + void _notification(int p_what); + int counter; + friend class RoadsData; +public: +#if 0 + void update(Ref roads, Vector3 where, float radius); +#endif + Roads(); + ~Roads(); + Vector3 calculate_offsets(const Array &data) const; + Vector3 quadratic_bezier(const Vector3 &p1, const Vector3 &p2, + const Vector3 &p3, float t) const; + void all_offsets(); + PoolVector build_item_list(float width, int flags, float sidewalk_width) const; + Ref build_curve(Vector3 p1, Vector3 p2, Vector3 p3, float total_width) const; + Array curve_mesh(PoolVector points, float width, int flags, float sidewalk_width); + int make_vmesh(Node *root, Ref mat, Ref mesh, MeshInstance *xmi, Vector3 p1, Vector3 p2, + Vector3 p3, float width, int flags, float sidewalk_width); + void process_vshapes(); + void add_scene_element(Node *root, Node *xnode, const Vector3 &p2, Ref shape); +}; + +class RoadsData: public Object { + GDCLASS(RoadsData, Object); +protected: + RoadGrid *rg; + static void _bind_methods(); +public: + RoadsData(); + ~RoadsData(); + static RoadsData *get_singleton(); + static void create_singleton(); + static void destroy_singleton(); + RoadGrid *get_road_grid(); +}; + diff --git a/modules/world/world_generator.cpp b/modules/world/world_generator.cpp index bd0c035..1038cd6 100644 --- a/modules/world/world_generator.cpp +++ b/modules/world/world_generator.cpp @@ -88,9 +88,10 @@ Ref WorldGenerator::get_density_map() const return density_map; } -void WorldGenerator::generate_block(VoxelBlockRequest &input) { +VoxelGenerator::Result WorldGenerator::generate_block(VoxelBlockRequest &input) { - ERR_FAIL_COND(_noise.is_null()); + ERR_FAIL_COND_V(input.voxel_buffer.is_null(), Result()); + ERR_FAIL_COND_V(_noise.is_null(), Result()); #ifdef WORLD_MAP_TESTS WorldMapData *wmd = WorldMapData::get_singleton(); if (!wmd->tests_run) { @@ -98,6 +99,7 @@ void WorldGenerator::generate_block(VoxelBlockRequest &input) { wmd->tests_run = true; } #endif + Result result; VoxelBuffer &out_buffer = **input.voxel_buffer; WorldGenerator::generate( @@ -106,6 +108,7 @@ void WorldGenerator::generate_block(VoxelBlockRequest &input) { input.origin_in_voxels, input.lod); out_buffer.compress_uniform_channels(); + return result; } void WorldGenerator::_bind_methods() { diff --git a/modules/world/world_generator.h b/modules/world/world_generator.h index e10118c..9e8329c 100644 --- a/modules/world/world_generator.h +++ b/modules/world/world_generator.h @@ -45,7 +45,7 @@ public: void set_density_map(Ref map); Ref get_density_map() const; - void generate_block(VoxelBlockRequest &input) override; + Result generate_block(VoxelBlockRequest &input) override; float height_func(int x, int y, int z); private: diff --git a/modules/world/world_height_map.cpp b/modules/world/world_height_map.cpp new file mode 100644 index 0000000..614185e --- /dev/null +++ b/modules/world/world_height_map.cpp @@ -0,0 +1,97 @@ +#include +#include "world_height_map.h" +void WorldHeightMap::_get_property_list(List *p_list) const +{ + p_list->push_back(PropertyInfo(Variant::OBJECT, "noise", PROPERTY_HINT_RESOURCE_TYPE, "OpenSimplexNoise")); + p_list->push_back(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve")); +} +bool WorldHeightMap::_get(const StringName &p_name, Variant &r_ret) const +{ + if (p_name == "noise") { + r_ret = noise; + return true; + } else if (p_name == "curve") { + r_ret = curve; + return true; + } + + const String pv = p_name.operator String(); + return false; +} + +bool WorldHeightMap::_set(const StringName &p_name, const Variant &p_value) +{ + bool update = false; + if (p_name == "noise") { + noise = p_value; + update = true; + } else if (p_name == "curve") { + curve = p_value; + update = true; + } + if (update) { + update_all(); + _change_notify(); + } + + return update; +} + +float WorldHeightMap::get_base_steepness(float x, float y) +{ + float tstep = 10.0f; + float xp = ceil(x / tstep) * tstep; + float xm = floor(x / tstep) * tstep; + float yp = ceil(y / tstep) * tstep; + float ym = floor(y / tstep) * tstep; + float hp = curve->interpolate_baked((noise->get_noise_2d(xp, yp) + 1.0f) * 0.5f); + float hm = curve->interpolate_baked((noise->get_noise_2d(xp, yp) + 1.0f) * 0.5f); + Vector3 d = Vector3(xp, hp, yp) - Vector3(xm, hm, ym); + return fabs(cosf(d.angle_to(Vector3(0.0f, 1.0f, 0.0f)))); +} +float WorldHeightMap::get_surface_height(float x, float y) +{ + float s = get_base_steepness(x, y); + if (s > 0.5f) { + float n = (noise->get_noise_2d(x, y) + 1.0f) * 0.5f; + float d = curve->interpolate_baked(n); + return d; + } else { + float tstep = 10.0f; + float xp = ceil(x / tstep) * tstep; + float xm = floor(x / tstep) * tstep; + float yp = ceil(y / tstep) * tstep; + float ym = floor(y / tstep) * tstep; + float hp = curve->interpolate_baked((noise->get_noise_2d(xp, yp) + 1.0f) * 0.5f); + float hm = curve->interpolate_baked((noise->get_noise_2d(xp, yp) + 1.0f) * 0.5f); + float d = Vector2(xm, ym).distance_to(Vector2(x, y)) / Vector2(xm, ym).distance_to(Vector2(xp, yp)); + return hp * d + hm * (1.0f - d); + } +} +float WorldHeightMap::get_base_height(float x, float y) +{ + float n = noise->get_noise_2d(x, y); + return n; +} + +void WorldHeightMap::update_all() +{ +} + +WorldHeightMap::WorldHeightMap() +{ +} + +WorldHeightMap::~WorldHeightMap() +{ +} + +void WorldHeightMap::_bind_methods() +{ + ClassDB::bind_method(D_METHOD("get_surface_height", "x", "y"), &WorldHeightMap::get_surface_height); + ClassDB::bind_method(D_METHOD("draw_height_map", "draw", "draw_rect", "world_rect"), &WorldHeightMap::draw_height_map); +} +void WorldHeightMap::draw_height_map(Node *draw, const Rect2 &draw_rect, const Rect2 &world_rect) +{ +} + diff --git a/modules/world/world_height_map.h b/modules/world/world_height_map.h new file mode 100644 index 0000000..d08d135 --- /dev/null +++ b/modules/world/world_height_map.h @@ -0,0 +1,29 @@ +#ifndef WORLD_HEIGHT_MAP_H +#define WORLD_HEIGHT_MAP_H +#include +#include +#include +#include +#include +class WorldHeightMap: public Resource { + GDCLASS(WorldHeightMap, Resource); +public: + WorldHeightMap(); + ~WorldHeightMap(); +protected: + Ref noise; + Ref curve; + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List *p_list) const; + static void _bind_methods(); +public: + void update_all(); + float get_surface_height(float x, float y); + float get_base_height(float x, float y); + float get_base_steepness(float x, float y); + void draw_height_map(Node *draw, const Rect2 &draw_rect, const Rect2 &world_rect); +private: + int seed; +}; +#endif