Updated world module (now voronoi roads work
This commit is contained in:
@@ -4,37 +4,26 @@
|
|||||||
|
|
||||||
void DensityMap::_get_property_list(List<PropertyInfo> *p_list) const
|
void DensityMap::_get_property_list(List<PropertyInfo> *p_list) const
|
||||||
{
|
{
|
||||||
p_list->push_back(PropertyInfo(Variant::INT, "world/grid_size"));
|
// p_list->push_back(PropertyInfo(Variant::INT, "world/grid_size"));
|
||||||
p_list->push_back(PropertyInfo(Variant::INT, "world/clusters/count"));
|
p_list->push_back(PropertyInfo(Variant::OBJECT, "noise", PROPERTY_HINT_RESOURCE_TYPE, "OpenSimplexNoise"));
|
||||||
if (grid_size == 0 || num_clusters == 0)
|
p_list->push_back(PropertyInfo(Variant::INT, "rnd_seed"));
|
||||||
return;
|
p_list->push_back(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"));
|
||||||
p_list->push_back(PropertyInfo(Variant::INT, "world/size/x"));
|
p_list->push_back(PropertyInfo(Variant::OBJECT, "height_map", PROPERTY_HINT_RESOURCE_TYPE, "WorldHeightMap"));
|
||||||
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"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DensityMap::_get(const StringName &p_name, Variant &r_ret) const
|
bool DensityMap::_get(const StringName &p_name, Variant &r_ret) const
|
||||||
{
|
{
|
||||||
if (p_name == "world/clusters/count") {
|
if (p_name == "noise") {
|
||||||
r_ret = num_clusters;
|
r_ret = noise;
|
||||||
return true;
|
return true;
|
||||||
} else if (p_name == "world/clusters/seed") {
|
} else if (p_name == "rnd_seed") {
|
||||||
r_ret = seed;
|
r_ret = seed;
|
||||||
return true;
|
return true;
|
||||||
} else if (p_name == "world/grid_size") {
|
} else if (p_name == "curve") {
|
||||||
r_ret = grid_size;
|
r_ret = curve;
|
||||||
return true;
|
return true;
|
||||||
} else if (p_name == "world/size/x") {
|
} else if (p_name == "height_map") {
|
||||||
r_ret = world_x_size;
|
r_ret = height_map;
|
||||||
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;
|
|
||||||
return true;
|
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 DensityMap::_set(const StringName &p_name, const Variant &p_value)
|
||||||
{
|
{
|
||||||
bool update = false;
|
bool update = false;
|
||||||
if (p_name == "world/clusters/count") {
|
if (p_name == "noise") {
|
||||||
num_clusters = p_value;
|
noise = p_value;
|
||||||
update = true;
|
update = true;
|
||||||
} else if (p_name == "world/clusters/seed") {
|
} else if (p_name == "rnd_seed") {
|
||||||
seed = p_value;
|
seed = p_value;
|
||||||
update = true;
|
update = true;
|
||||||
} else if (p_name == "world/grid_size") {
|
} else if (p_name == "curve") {
|
||||||
grid_size = p_value;
|
curve = p_value;
|
||||||
update = true;
|
update = true;
|
||||||
} else if (p_name == "world/size/x") {
|
} else if (p_name == "height_map") {
|
||||||
world_x_size = p_value;
|
curve = 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;
|
|
||||||
update = true;
|
update = true;
|
||||||
}
|
}
|
||||||
if (update) {
|
if (update) {
|
||||||
update_clusters();
|
update_all();
|
||||||
_change_notify();
|
_change_notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
return update;
|
return update;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DensityMap::populate_grid(List<struct area> &list)
|
void DensityMap::update_all()
|
||||||
{
|
{
|
||||||
}
|
if (!curve.ptr() || !rnd.ptr() || !noise.ptr() || !height_map.ptr())
|
||||||
|
|
||||||
void DensityMap::update_clusters()
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
clusters.clear();
|
|
||||||
regions.clear();
|
|
||||||
counties.clear();
|
|
||||||
cities.clear();
|
|
||||||
districts.clear();
|
|
||||||
circle_grid.clear();
|
|
||||||
if (num_clusters < 1)
|
|
||||||
return;
|
return;
|
||||||
rnd->set_seed(seed);
|
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::_bind_methods()
|
||||||
void DensityMap::split_area(struct area *area, int num_split, List<struct area> &list)
|
{
|
||||||
|
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
|
#if 0
|
||||||
while (true) {
|
float n = (noise->get_noise_2d(x, y) + 1.0f) * 0.5f;
|
||||||
float angle = rnd->randf() * M_PI * 2.0f;
|
float d = curve->interpolate_baked(n);
|
||||||
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;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
mrad = area->radius / (float)num_split;
|
float h = height_map->get_surface_height(x, y);
|
||||||
} else {
|
if (h < 0.0f || h > max_height)
|
||||||
pstart.x = rnd->randi() % world_x_size;
|
return 0.0f;
|
||||||
pstart.y = rnd->randi() % world_z_size;
|
float s = height_map->get_base_steepness(x, y);
|
||||||
mrad = sqrtf((float)world_x_size * (float)world_z_size) / (float)num_split;
|
if (s > 0.3f)
|
||||||
mrad *= 0.9f;
|
return 0.0f;
|
||||||
pstart.x -= (int)mrad + 1;
|
float d = 0.0f;
|
||||||
pstart.y -= (int)mrad + 1;
|
/* Still use curve for it? */
|
||||||
}
|
if (h < max_height * 0.3f)
|
||||||
astart.pos = pstart;
|
d = 1.0f;
|
||||||
astart.radius = mrad;
|
else
|
||||||
astart.parent = area;
|
d = max_height * 0.3f / h;
|
||||||
list.push_back(astart);
|
return d * s;
|
||||||
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<struct area>::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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DensityMap::DensityMap()
|
DensityMap::DensityMap()
|
||||||
{
|
{
|
||||||
rnd.instance();
|
rnd.instance();
|
||||||
rnd->randomize();
|
|
||||||
seed = rnd->get_seed();
|
seed = rnd->get_seed();
|
||||||
world_x_size = 400000;
|
max_height = 300.0f;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DensityMap::~DensityMap()
|
DensityMap::~DensityMap()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
#define DENSITY_MAP_H
|
#define DENSITY_MAP_H
|
||||||
#include <core/resource.h>
|
#include <core/resource.h>
|
||||||
#include <core/math/random_number_generator.h>
|
#include <core/math/random_number_generator.h>
|
||||||
|
#include <modules/opensimplex/open_simplex_noise.h>
|
||||||
#include <modules/voxel/util/math/vector3i.h>
|
#include <modules/voxel/util/math/vector3i.h>
|
||||||
|
#include "world_height_map.h"
|
||||||
|
|
||||||
class DensityMap: public Resource {
|
class DensityMap: public Resource {
|
||||||
GDCLASS(DensityMap, Resource);
|
GDCLASS(DensityMap, Resource);
|
||||||
@@ -10,47 +12,19 @@ public:
|
|||||||
DensityMap();
|
DensityMap();
|
||||||
~DensityMap();
|
~DensityMap();
|
||||||
protected:
|
protected:
|
||||||
|
Ref<WorldHeightMap> height_map;
|
||||||
Ref<RandomNumberGenerator> rnd;
|
Ref<RandomNumberGenerator> rnd;
|
||||||
struct area {
|
Ref<OpenSimplexNoise> noise;
|
||||||
Vector2i pos;
|
Ref<Curve> curve;
|
||||||
float radius;
|
float max_height;
|
||||||
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<struct area> clusters;
|
|
||||||
List<struct area> regions;
|
|
||||||
List<struct area> counties;
|
|
||||||
List<struct area> cities;
|
|
||||||
List<struct area> districts;
|
|
||||||
int num_clusters;
|
|
||||||
int num_regions;
|
|
||||||
int num_counties;
|
|
||||||
int num_cities;
|
|
||||||
int num_districts;
|
|
||||||
HashMap<Vector2i, Vector<struct area> > circle_grid;
|
|
||||||
void split_area(struct area *area, int num_split, List<struct area> &list);
|
|
||||||
void populate_grid(List<struct area> &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;
|
|
||||||
bool _set(const StringName &p_name, const Variant &p_value);
|
bool _set(const StringName &p_name, const Variant &p_value);
|
||||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||||
|
static void _bind_methods();
|
||||||
public:
|
public:
|
||||||
void update_clusters();
|
void update_all();
|
||||||
|
float get_population_density(float x, float y);
|
||||||
|
private:
|
||||||
|
int seed;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
#include "characters.h"
|
#include "characters.h"
|
||||||
#include "smart_object.h"
|
#include "smart_object.h"
|
||||||
#include "road_map.h"
|
#include "road_map.h"
|
||||||
|
#include "world_height_map.h"
|
||||||
|
#include "road_grid.h"
|
||||||
|
#include "roads.h"
|
||||||
|
|
||||||
void register_world_types()
|
void register_world_types()
|
||||||
{
|
{
|
||||||
@@ -15,7 +18,10 @@ void register_world_types()
|
|||||||
Engine::get_singleton()->add_singleton(Engine::Singleton("RoadMap", RoadMap::get_singleton()));
|
Engine::get_singleton()->add_singleton(Engine::Singleton("RoadMap", RoadMap::get_singleton()));
|
||||||
WorldMapData::create_singleton();
|
WorldMapData::create_singleton();
|
||||||
Engine::get_singleton()->add_singleton(Engine::Singleton("WorldMapData", WorldMapData::get_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<WorldGenerator>();
|
ClassDB::register_class<WorldGenerator>();
|
||||||
|
ClassDB::register_class<WorldHeightMap>();
|
||||||
ClassDB::register_class<DensityMap>();
|
ClassDB::register_class<DensityMap>();
|
||||||
ClassDB::register_class<CompoundTransvoxel>();
|
ClassDB::register_class<CompoundTransvoxel>();
|
||||||
ClassDB::register_class<CompoundTransvoxelInspector>();
|
ClassDB::register_class<CompoundTransvoxelInspector>();
|
||||||
@@ -23,6 +29,8 @@ void register_world_types()
|
|||||||
ClassDB::register_class<SmartObject>();
|
ClassDB::register_class<SmartObject>();
|
||||||
ClassDB::register_class<SmartObjectManager>();
|
ClassDB::register_class<SmartObjectManager>();
|
||||||
ClassDB::register_class<SmartObjectGroup>();
|
ClassDB::register_class<SmartObjectGroup>();
|
||||||
|
ClassDB::register_virtual_class<RoadGrid>();
|
||||||
|
ClassDB::register_class<Roads>();
|
||||||
#if TOOLS_ENABLED
|
#if TOOLS_ENABLED
|
||||||
EditorPlugins::add_by_type<CompoundTransvoxelEditorPlugin>();
|
EditorPlugins::add_by_type<CompoundTransvoxelEditorPlugin>();
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
331
modules/world/road_grid.cpp
Normal file
331
modules/world/road_grid.cpp
Normal file
@@ -0,0 +1,331 @@
|
|||||||
|
#include <cassert>
|
||||||
|
#include <cmath>
|
||||||
|
#include <core/math/geometry.h>
|
||||||
|
#include <core/resource.h>
|
||||||
|
#include <scene/2d/canvas_item.h>
|
||||||
|
#include <modules/voronoi/voronoi.h>
|
||||||
|
#include <modules/opensimplex/open_simplex_noise.h>
|
||||||
|
#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<Vector2i> 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<Vector2> 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<Vector2> 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<CanvasItem>(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<struct half_edge *> items = hedge_grid[j][i];
|
||||||
|
List<struct half_edge *>::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> curve, Ref<OpenSimplexNoise> 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<struct half_edge *> 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<struct half_edge *>::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);
|
||||||
|
}
|
||||||
|
|
||||||
197
modules/world/road_grid.h
Normal file
197
modules/world/road_grid.h
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
#ifndef ROAD_GRID_H
|
||||||
|
#define ROAD_GRID_H
|
||||||
|
#include <core/object.h>
|
||||||
|
#include <core/reference.h>
|
||||||
|
#include <scene/resources/curve.h>
|
||||||
|
#include <core/math/random_number_generator.h>
|
||||||
|
#include <modules/opensimplex/open_simplex_noise.h>
|
||||||
|
|
||||||
|
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<RandomNumberGenerator> rnd;
|
||||||
|
struct cluster {
|
||||||
|
Vector2i c;
|
||||||
|
float r;
|
||||||
|
};
|
||||||
|
List<struct cluster> clusters;
|
||||||
|
Dictionary build_diagram(int npatches, int center_count, int center_step,
|
||||||
|
int spread, int dim);
|
||||||
|
HashMap<int, int> class_sizes;
|
||||||
|
struct half_edge;
|
||||||
|
HashMap<int, HashMap<int, List<struct half_edge *> > > 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<struct graphedge> graphedges;
|
||||||
|
Vector<Vector2> vertices;
|
||||||
|
Vector<Vector2> polygon;
|
||||||
|
Vector<int> vertices_ind;
|
||||||
|
Vector<int> polygon_ind;
|
||||||
|
int site_type;
|
||||||
|
int cluster;
|
||||||
|
Vector<struct half_edge> hedges;
|
||||||
|
};
|
||||||
|
void index_site(struct map_site *site);
|
||||||
|
Vector<Vector2> diagram_vertices;
|
||||||
|
Vector<float> diagram_vertex_heights;
|
||||||
|
Vector<struct map_site> map_sites;
|
||||||
|
Vector<struct half_edge *> 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<struct half_edge *> 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> curve, Ref<OpenSimplexNoise> noise);
|
||||||
|
void draw_debug(Node *drawable, int size_x, int size_y) const;
|
||||||
|
Vector2 get_influence(int x, int y) const;
|
||||||
|
RoadGrid();
|
||||||
|
~RoadGrid();
|
||||||
|
};
|
||||||
|
#endif
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
#include "road_map.h"
|
#include "road_map.h"
|
||||||
|
|
||||||
#define ROAD_MAP_TESTS
|
#define MIN_X (-10000)
|
||||||
|
#define MAX_X (10000)
|
||||||
#define MIN_X -10000
|
#define MIN_Z (-10000)
|
||||||
#define MAX_X 10000
|
#define MAX_Z (10000)
|
||||||
#define MIN_Z -10000
|
#define MIN_Y (-100)
|
||||||
#define MAX_Z 10000
|
#define MAX_Y (300)
|
||||||
#define MIN_Y -1000
|
#define HIGHWAY_LENGTH_MIN 100
|
||||||
#define MAX_Y 1000
|
#define HIGHWAY_LENGTH_MAX 300
|
||||||
|
|
||||||
static RoadMap *g_road_map_data = NULL;
|
static RoadMap *g_road_map_data = NULL;
|
||||||
RoadMap *RoadMap::get_singleton()
|
RoadMap *RoadMap::get_singleton()
|
||||||
@@ -25,9 +25,40 @@ void RoadMap::destroy_singleton()
|
|||||||
}
|
}
|
||||||
RoadMap::RoadMap()
|
RoadMap::RoadMap()
|
||||||
{
|
{
|
||||||
|
noise.instance();
|
||||||
|
rnd.instance();
|
||||||
}
|
}
|
||||||
|
|
||||||
RoadMap::~RoadMap()
|
RoadMap::~RoadMap()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PrimaryNetwork {
|
||||||
|
Vector<Vector3> 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
#include <core/object.h>
|
#include <core/object.h>
|
||||||
|
#include <core/reference.h>
|
||||||
|
#include <core/math/random_number_generator.h>
|
||||||
|
#include <modules/opensimplex/open_simplex_noise.h>
|
||||||
|
|
||||||
class RoadMap: public Object {
|
class RoadMap: public Object {
|
||||||
GDCLASS(RoadMap, Object)
|
GDCLASS(RoadMap, Object)
|
||||||
@@ -10,26 +13,47 @@ public:
|
|||||||
static void destroy_singleton();
|
static void destroy_singleton();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Vector<int> vertices;
|
Ref<OpenSimplexNoise> noise;
|
||||||
struct segment {
|
Ref<RandomNumberGenerator> rnd;
|
||||||
int v1, v2;
|
struct Segment {
|
||||||
uint32_t flags;
|
Vector3 p1, p2;
|
||||||
};
|
};
|
||||||
Vector<segment> segments;
|
Vector<Segment> segments;
|
||||||
struct intersection {
|
struct Pq {
|
||||||
#define MAX_NEIGHBORS 4
|
struct PqItem {
|
||||||
int neighbors[MAX_NEIGHBORS];
|
struct Segment data;
|
||||||
int ncount;
|
int priority;
|
||||||
uint32_t flags;
|
};
|
||||||
|
List<PqItem> queue;
|
||||||
|
void push(int priority, const struct Segment &seg)
|
||||||
|
{
|
||||||
|
List<PqItem>::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<intersection> intersections;
|
struct Pq queue;
|
||||||
/* cylindric area to define road */
|
void gen_primary_network();
|
||||||
struct area {
|
|
||||||
int x, z;
|
|
||||||
int radius;
|
|
||||||
int type;
|
|
||||||
};
|
|
||||||
Vector<area> areas;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
23
modules/world/road_map2.h
Normal file
23
modules/world/road_map2.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#ifndef ROAD_MAP2
|
||||||
|
#define ROAD_MAP2
|
||||||
|
#include <core/resource.h>
|
||||||
|
#include <core/math/random_number_generator.h>
|
||||||
|
#include <modules/opensimplex/open_simplex_noise.h>
|
||||||
|
#include "density_map.h"
|
||||||
|
|
||||||
|
class RoadMap2: public Resource {
|
||||||
|
GDCLASS(RoadMap2, Resource);
|
||||||
|
public:
|
||||||
|
RoadMap2();
|
||||||
|
~RoadMap2();
|
||||||
|
protected:
|
||||||
|
Ref<RandomNumberGenerator> rnd;
|
||||||
|
Ref<OpenSimplexNoise> 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<PropertyInfo> *p_list) const;
|
||||||
|
static void _bind_methods();
|
||||||
|
private:
|
||||||
|
int seed;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
600
modules/world/roads.cpp
Normal file
600
modules/world/roads.cpp
Normal file
@@ -0,0 +1,600 @@
|
|||||||
|
#include <cassert>
|
||||||
|
#include <core/resource.h>
|
||||||
|
#include <core/sort_array.h>
|
||||||
|
#include <scene/resources/packed_scene.h>
|
||||||
|
#include <scene/main/viewport.h>
|
||||||
|
#include <scene/3d/camera.h>
|
||||||
|
#include <scene/3d/physics_body.h>
|
||||||
|
#include <scene/3d/collision_shape.h>
|
||||||
|
#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<PropertyInfo> *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<int> &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<int, struct comparator> 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<struct vshape> 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<MeshInstance>(c);
|
||||||
|
if (mi) {
|
||||||
|
String name = mi->get_name();
|
||||||
|
Ref<ArrayMesh> 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<Vector3> 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<String> Roads::build_item_list(float width, int flags, float sidewalk_width) const
|
||||||
|
{
|
||||||
|
PoolVector<String> 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<Curve3D> Roads::build_curve(Vector3 p1, Vector3 p2, Vector3 p3, float total_width) const
|
||||||
|
{
|
||||||
|
Ref<Curve3D> 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<Vector3> 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<String> 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<Curve3D> curve3 = build_curve(points[0], points[1], points[2], total_width);
|
||||||
|
l = curve3->get_baked_length();
|
||||||
|
assert(l > 0.0f);
|
||||||
|
PoolVector<Vector3> new_verts, new_normals;
|
||||||
|
PoolVector<Vector2> new_uvs;
|
||||||
|
PoolVector<int> 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<Vector3> verts = data[Mesh::ARRAY_VERTEX];
|
||||||
|
PoolVector<Vector3> 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<int> 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<ConcavePolygonShape> create_concave_polygon_shape(Vector<Array> surfaces) {
|
||||||
|
PoolVector<Vector3> 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<int> indices = surface_arrays[Mesh::ARRAY_INDEX];
|
||||||
|
face_points_size += indices.size();
|
||||||
|
}
|
||||||
|
face_points.resize(face_points_size);
|
||||||
|
|
||||||
|
if (face_points_size < 3) {
|
||||||
|
return Ref<ConcavePolygonShape>();
|
||||||
|
}
|
||||||
|
|
||||||
|
//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<Vector3> positions = surface_arrays[Mesh::ARRAY_VERTEX];
|
||||||
|
PoolVector<int> indices = surface_arrays[Mesh::ARRAY_INDEX];
|
||||||
|
|
||||||
|
ERR_FAIL_COND_V(positions.size() < 3, Ref<ConcavePolygonShape>());
|
||||||
|
ERR_FAIL_COND_V(indices.size() < 3, Ref<ConcavePolygonShape>());
|
||||||
|
ERR_FAIL_COND_V(indices.size() % 3 != 0, Ref<ConcavePolygonShape>());
|
||||||
|
|
||||||
|
int face_points_count = face_points_offset + indices.size();
|
||||||
|
|
||||||
|
{
|
||||||
|
PoolVector<Vector3>::Write w = face_points.write();
|
||||||
|
PoolVector<int>::Read index_r = indices.read();
|
||||||
|
PoolVector<Vector3>::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<ConcavePolygonShape> shape = memnew(ConcavePolygonShape);
|
||||||
|
shape->set_faces(face_points);
|
||||||
|
return shape;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Roads::make_vmesh(Node *root, Ref<Material> mat, Ref<ArrayMesh> 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<Vector3> 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<ArrayMesh> mdata = mesh;
|
||||||
|
assert(mdata.is_valid());
|
||||||
|
mdata->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, rdata);
|
||||||
|
assert(mdata->get_surface_count() > 0);
|
||||||
|
Vector<Array> surfaces;
|
||||||
|
surfaces.push_back(rdata);
|
||||||
|
Ref<ConcavePolygonShape> 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<ConcavePolygonShape> shape)
|
||||||
|
{
|
||||||
|
MeshInstance *xmi = Object::cast_to<MeshInstance>(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<int> 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<int>::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<ArrayMesh> 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<Roads>(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
104
modules/world/roads.h
Normal file
104
modules/world/roads.h
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
#include <modules/world/road_grid.h>
|
||||||
|
|
||||||
|
#include <scene/main/node.h>
|
||||||
|
#include <scene/3d/mesh_instance.h>
|
||||||
|
#include <scene/3d/immediate_geometry.h>
|
||||||
|
#include <scene/resources/concave_polygon_shape.h>
|
||||||
|
|
||||||
|
class StaticBody;
|
||||||
|
|
||||||
|
class Roads: public MeshInstance {
|
||||||
|
GDCLASS(Roads, MeshInstance);
|
||||||
|
protected:
|
||||||
|
Mutex mutex;
|
||||||
|
Ref<Curve> curve;
|
||||||
|
Ref<OpenSimplexNoise> noise;
|
||||||
|
Ref<Material> mat;
|
||||||
|
Ref<PackedScene> road_data;
|
||||||
|
HashMap<String, Array> mesh_data;
|
||||||
|
HashMap<String, Vector3> 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<PropertyInfo> *p_list) const;
|
||||||
|
static void _bind_methods();
|
||||||
|
void update_all();
|
||||||
|
PoolVector<Vector3> vertices;
|
||||||
|
struct thread_data {
|
||||||
|
Thread thread;
|
||||||
|
Ref<Material> mat;
|
||||||
|
int vshape;
|
||||||
|
float width;
|
||||||
|
float sidewalk_width;
|
||||||
|
int flags;
|
||||||
|
Node *root;
|
||||||
|
Ref<ArrayMesh> 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<struct edge_data> edges;
|
||||||
|
#endif
|
||||||
|
PoolVector<struct vshape> vshapes;
|
||||||
|
void setup_vshapes();
|
||||||
|
void sort_angle(Vector<int> &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<RoadGrid> 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<String> build_item_list(float width, int flags, float sidewalk_width) const;
|
||||||
|
Ref<Curve3D> build_curve(Vector3 p1, Vector3 p2, Vector3 p3, float total_width) const;
|
||||||
|
Array curve_mesh(PoolVector<Vector3> points, float width, int flags, float sidewalk_width);
|
||||||
|
int make_vmesh(Node *root, Ref<Material> mat, Ref<ArrayMesh> 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<ConcavePolygonShape> 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();
|
||||||
|
};
|
||||||
|
|
||||||
@@ -88,9 +88,10 @@ Ref<DensityMap> WorldGenerator::get_density_map() const
|
|||||||
return density_map;
|
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
|
#ifdef WORLD_MAP_TESTS
|
||||||
WorldMapData *wmd = WorldMapData::get_singleton();
|
WorldMapData *wmd = WorldMapData::get_singleton();
|
||||||
if (!wmd->tests_run) {
|
if (!wmd->tests_run) {
|
||||||
@@ -98,6 +99,7 @@ void WorldGenerator::generate_block(VoxelBlockRequest &input) {
|
|||||||
wmd->tests_run = true;
|
wmd->tests_run = true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
Result result;
|
||||||
|
|
||||||
VoxelBuffer &out_buffer = **input.voxel_buffer;
|
VoxelBuffer &out_buffer = **input.voxel_buffer;
|
||||||
WorldGenerator::generate(
|
WorldGenerator::generate(
|
||||||
@@ -106,6 +108,7 @@ void WorldGenerator::generate_block(VoxelBlockRequest &input) {
|
|||||||
input.origin_in_voxels, input.lod);
|
input.origin_in_voxels, input.lod);
|
||||||
|
|
||||||
out_buffer.compress_uniform_channels();
|
out_buffer.compress_uniform_channels();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldGenerator::_bind_methods() {
|
void WorldGenerator::_bind_methods() {
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ public:
|
|||||||
void set_density_map(Ref<DensityMap> map);
|
void set_density_map(Ref<DensityMap> map);
|
||||||
Ref<DensityMap> get_density_map() const;
|
Ref<DensityMap> 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);
|
float height_func(int x, int y, int z);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
97
modules/world/world_height_map.cpp
Normal file
97
modules/world/world_height_map.cpp
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
#include <cmath>
|
||||||
|
#include "world_height_map.h"
|
||||||
|
void WorldHeightMap::_get_property_list(List<PropertyInfo> *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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
29
modules/world/world_height_map.h
Normal file
29
modules/world/world_height_map.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#ifndef WORLD_HEIGHT_MAP_H
|
||||||
|
#define WORLD_HEIGHT_MAP_H
|
||||||
|
#include <core/resource.h>
|
||||||
|
#include <core/math/random_number_generator.h>
|
||||||
|
#include <core/math/rect2.h>
|
||||||
|
#include <modules/opensimplex/open_simplex_noise.h>
|
||||||
|
#include <scene/main/node.h>
|
||||||
|
class WorldHeightMap: public Resource {
|
||||||
|
GDCLASS(WorldHeightMap, Resource);
|
||||||
|
public:
|
||||||
|
WorldHeightMap();
|
||||||
|
~WorldHeightMap();
|
||||||
|
protected:
|
||||||
|
Ref<OpenSimplexNoise> noise;
|
||||||
|
Ref<Curve> 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<PropertyInfo> *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
|
||||||
Reference in New Issue
Block a user