Initial commit

This commit is contained in:
Segey Lapin
2021-07-31 03:30:12 +03:00
commit 91cf9d2d34
249 changed files with 27582 additions and 0 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

18
modules/world/SCsub Normal file
View File

@@ -0,0 +1,18 @@
Import('env')
Import('env_modules')
env_world = env_modules.Clone()
# TODO Exclude editor stuff when building an export template?
files = [
"*.cpp",
"mesher/*.cpp"
]
for f in files:
env_world.add_source_files(env.modules_sources, f)
# Ignored clang warnings because Godot's codebase is old and isn't using override yet
if env['platform'] == 'osx' or env['platform'] == 'android':
env_world.Append(CXXFLAGS=['-Wno-inconsistent-missing-override'])

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,820 @@
#include <cassert>
#include <cstdio>
#include <core/list.h>
#include <scene/3d/spatial.h>
#include <scene/3d/physics_body.h>
#include <scene/3d/immediate_geometry.h>
#include <scene/3d/vehicle_body.h>
#include <scene/animation/animation_tree.h>
#include <scene/animation/animation_node_state_machine.h>
#include <scene/resources/mesh.h>
#include <modules/detour/crowd.h>
#include "smart_object.h"
#include "characters.h"
Characters_::Characters_() : query(memnew(DetourNavigationQuery)),
initialized(false), debug(NULL),
crowd(NULL)
{
smm = NULL;
}
AnimationTree *Characters_::get_animation_tree(const Node *npc) const
{
if (!npc->has_meta("animation_tree"))
return NULL;
Object * obj = npc->get_meta("animation_tree");
if (!obj)
return NULL;
AnimationTree *result = Object::cast_to<AnimationTree>(obj);
return result;
}
bool Characters_::handle_cmdq(Node *npc)
{
Array cmds, cmd;
bool handled = false;
if (npc->has_meta("cmdqueue"))
cmds = npc->get_meta("cmdqueue");
AnimationTree *anim = get_animation_tree(npc);
if (cmds.size() > 0) {
cmd = cmds[0];
if (cmd[0] == "anim_state") {
animation_node_travel(npc, cmd[1]);
cmds.pop_front();
handled = true;
} else if (cmd[0] == "anim_param") {
String p = "parameters/";
p += String(cmd[1]);
anim->set(p, cmd[2]);
cmds.pop_front();
handled = true;
} else if (cmd[0] == "delay") {
int d = cmd[1];
cmds.pop_front();
if (d > 0) {
d--;
Array ncmd;
ncmd.push_back("delay");
ncmd.push_back(d);
cmds.push_front(ncmd);
}
handled = true;
} else if (cmd[0] == "jump_to_obj") {
Object *obj = cmd[1];
Spatial *sp = Object::cast_to<Spatial>(obj);
Spatial *npc_sp = Object::cast_to<Spatial>(npc);
Transform g = sp->get_global_transform();
npc_sp->set_global_transform(g);
Transform orientation = npc->get_meta("orientation");
orientation.basis = g.basis;
npc->set_meta("orientation", orientation);
cmds.pop_front();
handled = true;
} else if (cmd[0] == "jump_to_obj_rot") {
Object *obj = cmd[1];
Spatial *sp = Object::cast_to<Spatial>(obj);
Spatial *npc_sp = Object::cast_to<Spatial>(npc);
Transform xform;
Transform g = sp->get_global_transform();
xform.origin = g.origin;
xform.basis = g.basis.rotated(Vector3(0, 1, 0), Math_PI);
npc_sp->set_global_transform(xform);
Transform orientation = npc->get_meta("orientation");
orientation.basis = xform.basis;
npc->set_meta("orientation", orientation);
cmds.pop_front();
handled = true;
}
}
if (cmds.size() == 0 && npc->has_meta("cmdqueue"))
npc->remove_meta("cmdqueue");
else if (cmds.size() > 0 && handled)
npc->set_meta("cmdqueue", cmds);
return handled;
}
String Characters_::get_animation_node(const Node *npc)
{
AnimationTree *anim_tree = get_animation_tree(npc);
if (!anim_tree)
return "";
Ref<AnimationNodeStateMachinePlayback> playback;
playback = anim_tree->get("parameters/state/playback");
if (!playback.ptr())
return "";
return playback->get_current_node();
}
void Characters_::animation_node_travel(Node *npc, const String &anim)
{
AnimationTree *anim_tree = get_animation_tree(npc);
if (!anim_tree)
return;
Ref<AnimationNodeStateMachinePlayback> playback;
playback = anim_tree->get("parameters/state/playback");
if (!playback.ptr())
return;
playback->travel(anim);
}
void Characters_::set_walk_speed(Node *npc, float speed, float strafe)
{
AnimationTree *anim_tree = get_animation_tree(npc);
if (!anim_tree)
return;
Vector2 spd(speed, strafe);
anim_tree->set("parameters/state/locomotion/loc/blend_position", spd);
}
float Characters_::get_walk_speed(const Node *npc) const
{
AnimationTree *anim_tree = get_animation_tree(npc);
if (!anim_tree)
return 0.0f;
Vector2 spd = anim_tree->get("parameters/state/locomotion/loc/blend_position");
return spd.x;
}
void Characters_::rotate_to_agent(Spatial *obj)
{
if (!obj->has_meta("agent_id"))
return;
Transform orientation = obj->get_meta("orientation");
#if 0
if (get_animation_node(obj).begins_with("turn_"))
return;
#endif
Vector3 agent_velocity = obj->get_meta("agent_velocity");
Vector3 agent_position = obj->get_meta("agent_position");
Transform g = obj->get_global_transform();
Vector3 to = agent_position - g.origin + agent_velocity;
Vector3 dir = orientation.xform(Vector3(0.0f, 0.0f, -1.0f));
to.y = 0.0f;
Vector2 to2(to.x, to.z);
Vector2 dir2(dir.x, dir.z);
float angle = dir2.angle_to(to2);
if (fabs(angle) < Math_PI / 8.0f || to.length_squared() < 1.9f * 1.9f) {
if (get_animation_node(obj).begins_with("turn_"))
animation_node_travel(obj, "locomotion");
Transform rot = Transform().looking_at(to, Vector3(0.0f, 1.0f, 0.0f));
orientation.basis = rot.basis;
obj->set_meta("orientation", orientation);
g.basis = orientation.basis;
obj->set_global_transform(g);
} else {
if (get_animation_node(obj).begins_with("turn_"))
return;
Vector3 sk = orientation.xform_inv(to);
if (sk.z > 1.9f)
animation_node_travel(obj, "turn_left");
/* CCW */
else if (sk.x > 0.0f)
animation_node_travel(obj, "turn_left");
/* CW */
else if (sk.x < 0.0f)
animation_node_travel(obj, "turn_right");
}
}
void Characters_::speed_to_agent(Spatial *obj)
{
float delta = get_physics_process_delta_time();
float cur_speed = get_walk_speed(obj);
float new_speed;
Vector3 agent_velocity = obj->get_meta("agent_velocity");
Vector3 agent_position = obj->get_meta("agent_position");
int agent_id = obj->get_meta("agent_id");
bool update_speed = false;
Transform g = obj->get_global_transform();
#if 0
KinematicBody *kb = Object::cast_to<KinematicBody>(obj);
#endif
if (obj->has_meta("cur_speed")) {
float tmp = obj->get_meta("cur_speed");
if (cur_speed != tmp) {
#if 0
printf("something is very wrong\n");
#endif
cur_speed = cur_speed + (tmp - cur_speed) * delta;
set_walk_speed(obj, cur_speed, 0.0f);
}
}
if (agent_position.distance_squared_to(g.origin) > 0.0f) {
agent_velocity = agent_position - g.origin + agent_velocity;
Vector3 apos = g.xform_inv(agent_position);
float dinc = -apos.z;
float xinc = apos.x;
// printf("position mismatch %f\n", dinc);
if (fabs(dinc) > 0.2f || fabs(xinc) > 0.2f)
crowd->reset_agent(agent_id);
Vector3 v = agent_velocity;
v.y = 0.0f;
float spd = v.length();
#if 0
cur_speed = get_walk_speed(obj);
if (obj->has_meta("cur_speed"))
cur_speed = obj->get_meta("cur_speed");
#endif
if (spd > 40.0f)
new_speed = 0.5f;
else if (spd > 0.0f)
new_speed = 0.1f + 0.4f * (spd / 60.0f);
else
new_speed = 0.0f;
#if 0
if (dinc > 0.15f)
new_speed += 0.1f * delta;
if (dinc < -0.15f)
new_speed -= 0.1f * delta;
#endif
if (new_speed > 0.0f)
new_speed = cur_speed +
(new_speed - cur_speed) * delta;
new_speed = CLAMP(new_speed, 0.0f, 1.0f);
if (new_speed != cur_speed) {
#if 0
printf("cur_speed = %f %f spd = %f\n", cur_speed, dinc, spd);
printf("new speed = %f cur_speed = %f\n", new_speed, cur_speed);
#endif
if (new_speed == 0.0f && cur_speed > 0.0f) {
animation_node_travel(obj, "stop_walking");
printf("stopping 1\n");
obj->set_meta("cur_speed", 0.0f);
cur_speed = new_speed;
update_speed = true;
}
else if (cur_speed == 0.0f && new_speed > 0.0f) {
animation_node_travel(obj, "start_walking");
printf("starting 1\n");
obj->set_meta("cur_speed", new_speed);
cur_speed = new_speed;
update_speed = true;
} else if (cur_speed >= 0.0f && new_speed > 0.0f) {
obj->set_meta("cur_speed", new_speed);
cur_speed = new_speed;
update_speed = true;
}
}
// printf("cur_speed = %f speed = %f new_speed = %f\n", cur_speed, get_walk_speed(obj), new_speed);
if (new_speed > 0.0f) {
if (dinc > 0.15f) {
new_speed = CLAMP(new_speed + delta * 2.5f, 0.1f, 1.0f);
update_speed = true;
} else if (dinc < -0.15f) {
new_speed = CLAMP(new_speed - delta * 2.5f, 0.1f, 1.0f);
update_speed = true;
}
}
}
if (update_speed)
set_walk_speed(obj, new_speed, 0.0f);
if (cur_speed > 0.0f)
assert(get_walk_speed(obj) > 0.0f);
}
bool Characters_::has_arrived(Object *obj)
{
Spatial *sp = Object::cast_to<Spatial>(obj);
if (!obj->has_meta("agent_id"))
return false;
if (!obj->has_meta("_target"))
return false;
int agent_id = obj->get_meta("agent_id");
bool crowd_arrived = crowd->has_arrived(agent_id);
if (crowd_arrived) {
Vector3 cpos = obj->get_meta("agent_position");
Vector3 spos = sp->get_global_transform().origin;
if (cpos.distance_squared_to(spos) <
arrive_precision * arrive_precision)
return true;
}
return false;
}
void Characters_::update_arrived(Object *obj)
{
Spatial *sp = Object::cast_to<Spatial>(obj);
int agent_id = obj->get_meta("agent_id");
if (obj->has_meta("climb"))
obj->remove_meta("climb");
crowd->clear_agent_target(agent_id);
if (!sp)
return;
printf("stopping 2\n");
set_walk_speed(sp, 0.0f, 0.0f);
obj->set_meta("cur_speed", 0.0f);
animation_node_travel(sp, "stop_walking");
if (obj->has_meta("_target_jumpto")) {
Transform xform = obj->get_meta("_target_jumpto");
sp->set_global_transform(xform);
obj->remove_meta("_target_jumpto");
}
if (obj->has_meta("_target_animation")) {
String anim = obj->get_meta("_target_animation");
animation_node_travel(sp, anim);
obj->remove_meta("_target_animation");
}
if (obj->has_meta("_target_cmdq")) {
Array cmds;
if (obj->has_meta("cmdqueue"))
cmds = obj->get_meta("cmdqueue");
Array tcmds = obj->get_meta("_target_cmdq");
cmds.append_array(tcmds);
obj->set_meta("cmdqueue", cmds);
obj->remove_meta("_target_cmdq");
}
}
void Characters_::character_physics(Object *obj)
{
float delta = get_physics_process_delta_time();
Vector3 h_velocity, velocity;
AnimationTree *animtree;
Transform orientation, root_motion, g;
Object *tmp;
tmp = obj->get_meta("animation_tree");
animtree = Object::cast_to<AnimationTree>(tmp);
orientation = obj->get_meta("orientation");
root_motion = animtree->get_root_motion_transform();
orientation *= root_motion;
h_velocity = orientation.origin / delta;
velocity = h_velocity;
KinematicBody *kb = Object::cast_to<KinematicBody>(obj);
if (kb) {
bool go = false;
if (!obj->has_meta("vehicle") && !obj->has_meta("cmdqueue") /*&& !obj->has_meta("smart_object")*/) {
go = true;
} else if (obj->has_meta("cmdqueue") && obj->has_meta("cmdq_walk")) {
go = true;
} else if (obj->has_meta("cmdqueue") && obj->has_meta("climb")) {
go = true;
}
if (!kb->is_on_floor() && !obj->has_meta("climb"))
velocity += Vector3(0.0f, -9.8f, 0.0f);
if (go)
velocity = kb->move_and_slide(velocity, Vector3(0.0f, 1.0f, 0.0f), true, 4, 0.785f, false);
}
orientation.origin = Vector3();
orientation = orientation.orthonormalized();
obj->set_meta("orientation", orientation);
Spatial *sp = Object::cast_to<Spatial>(obj);
if (sp) {
g = sp->get_global_transform();
g.basis = orientation.basis;
sp->set_global_transform(g);
if (obj->has_meta("vehicle")) {
tmp = obj->get_meta("vehicle");
VehicleBody *vehicle = Object::cast_to<VehicleBody>(tmp);
if (obj->has_meta("vehicle_offset")) {
Transform xform = obj->get_meta("vehicle_offset");
g = vehicle->get_global_transform() * xform;
sp->set_global_transform(g);
}
}
}
Node *node = Object::cast_to<Node>(obj);
if (node) {
if (node->has_node(String("blackboard"))) {
Node *bb = node->get_node(String("blackboard"));
bb->call("set", "velocity", velocity);
}
}
if (!crowd)
return;
if (obj->has_meta("smart_object"))
return;
if (obj->has_meta("cmdqueue") && !obj->has_meta("cmdq_walk"))
return;
/* agent creation stuff */
if (!obj->has_meta("agent_id")) {
float agent_data_f[] = {0.35f, 1.5f, 1.8f, 40.0f};
PoolVector<float> agent_data;
agent_data.resize(4);
memcpy(agent_data.write().ptr(), agent_data_f, sizeof(agent_data_f));
crowd->add_agent(obj, 0, false, agent_data);
}
if (!obj->has_meta("agent_id"))
return;
if (!node)
return;
if (!obj->has_meta("_target"))
return;
float fwd_probe = forward_probe(node, 0.5f, 0.05f, 1.9f, 0.2f);
bool climb = false;
bool bumped = false;
if (obj->has_meta("bumped")) {
bumped = true;
float remaining = obj->get_meta("bumped");
remaining -= delta;
if (remaining >= 0.0f)
obj->set_meta("bumped", remaining);
else
obj->remove_meta("bumped");
}
if (fwd_probe < 0.1f) {
if (obj->has_meta("climb"))
climb = true;
if (get_animation_node(node) != "climb1")
climb = false;
} else if (fwd_probe < 1.0f)
climb = true;
else if (fwd_probe >= 1.0f && !bumped) {
int agent_id = obj->get_meta("agent_id");
printf("bumped into wall %f\n", fwd_probe);
bumped = true;
printf("stopping 3\n");
set_walk_speed(node, 0.0f, 0.0f);
obj->set_meta("bumped", 3.0f);
obj->set_meta("cur_speed", 0.0f);
animation_node_travel(node, "stop_walking");
crowd->reset_agent(agent_id);
}
if (climb && !obj->has_meta("climb")) {
animation_node_travel(node, "climb1");
obj->set_meta("climb", true);
} else if (!climb && obj->has_meta("climb")) {
animation_node_travel(node, "locomotion");
obj->remove_meta("climb");
}
if (climb)
return;
/* agent stuff */
Vector3 agent_velocity = obj->get_meta("agent_velocity");
Vector3 agent_position = obj->get_meta("agent_position");
g = sp->get_global_transform();
if (!bumped)
speed_to_agent(sp);
if (((agent_position - g.origin) * delta + agent_velocity).length() == 0.0f) {
float wspd = obj->get_meta("cur_speed");
if (wspd >= 0.0f) {
printf("stopping 4\n");
set_walk_speed(node, 0.0f, 0.0f);
animation_node_travel(node, "stop_walking");
obj->set_meta("cur_speed", 0.0f);
}
} else
rotate_to_agent(sp);
if (has_arrived(obj)) {
printf("ARRIVED\n");
Vector3 where;
update_arrived(obj);
bool smart = false;
SmartObject *sm = NULL;
if (obj->has_meta("_target_node")) {
Object *o = ObjectDB::get_instance(obj->get_meta("_target_node"));
if (o) {
sm = Object::cast_to<SmartObject>(o);
if (sm)
smart = true;
}
}
if (!smart) {
printf("ARRIVED simple\n");
agent_walk_stop(obj);
where = obj->get_meta("_target");
emit_signal("arrived", obj, where);
printf("arrived to %ls\n", String(where).c_str());
} else {
printf("ARRIVED smart\n");
smm->arrived(sm, sp);
}
} else {
if (obj->has_meta("_target_node")) {
Node *tn = obj->get_meta("_target_node");
if (tn) {
PhysicsBody *pbt = Object::cast_to<PhysicsBody>(tn);
if (pbt) {
Vector3 target_cur = pbt->get_global_transform().origin;
obj->set_meta("_target", target_cur);
} else {
Spatial *spt = Object::cast_to<Spatial>(tn);
if (spt) {
Vector3 target_cur = spt->get_global_transform().origin;
obj->set_meta("_target", target_cur);
}
}
}
}
}
}
void Characters_::agent_walk_stop(Object *obj)
{
if (obj->has_meta("_target"))
obj->remove_meta("_target");
if (obj->has_meta("_target_node"))
obj->remove_meta("_target_node");
}
void Characters_::walkto_agent_node(Node *ch, const Node *target)
{
if (!ch->has_meta("agent_id"))
return;
ch->set_meta("_target_node", target->get_instance_id());
const Spatial *sp = Object::cast_to<Spatial>(target);
if (sp) {
Vector3 tpos = sp->get_global_transform().origin;
walkto_agent(ch, tpos);
}
}
void Characters_::walkto_agent(Node *ch, const Vector3 &target)
{
assert(crowd);
if (ch->has_meta("_target")) {
Vector3 otarget = ch->get_meta("_target");
if (otarget == target)
return;
}
assert(ch->has_meta("agent_id"));
int agent_id = ch->get_meta("agent_id");
ch->set_meta("_target", target);
crowd->set_agent_target_position(agent_id, target);
printf("target set to %ls\n", String(target).c_str());
}
void Characters_::_notification(int p_what)
{
List<Node *> char_node_list;
List<Node *> frozen_node_list;
List<Node *>::Element *e;
switch(p_what) {
case NOTIFICATION_PROCESS:
if (!initialized)
return;
get_tree()->get_nodes_in_group("character", &char_node_list);
get_tree()->get_nodes_in_group("frozen", &frozen_node_list);
if (debug) {
debug->clear();
debug->begin(Mesh::PRIMITIVE_LINES);
debug->set_color(Color(1, 0, 0, 1));
}
for (e = char_node_list.front(); e; e = e->next()) {
debug->set_color(Color(1, 0, 0, 1));
Node *ch = e->get();
if (!ch->has_meta("animation_tree"))
continue;
process_character(ch, false);
if (debug) {
Spatial *sp = Object::cast_to<Spatial>(ch);
debug->set_color(Color(1, 0.5f, 1, 1));
Transform xf = sp->get_global_transform();
Vector3 p1 = xf.origin;
// Vector3 b = -xf.basis[2];
// b.x = -b.x;
xf.origin = Vector3();
Vector3 b = xf.xform(Vector3(0.0f, 0.0f, -1.0f));
Vector3 p2 = p1 + b * 2.0f;
debug->add_vertex(p1);
debug->add_vertex(p2);
if (ch->has_meta("agent_velocity")) {
Vector3 p3 = p1 + ch->get_meta("agent_velocity");
debug->set_color(Color(0.5f, 1, 0.5f, 1));
debug->add_vertex(p1);
debug->add_vertex(p3);
}
}
}
if (debug)
debug->set_color(Color(1, 0, 0, 1));
for (e = frozen_node_list.front(); e; e = e->next()) {
Node *ch = e->get();
if (!ch->has_meta("animation_node"))
continue;
process_character(ch, true);
}
if (debug)
debug->end();
break;
case NOTIFICATION_PHYSICS_PROCESS:
if (!initialized)
return;
get_tree()->get_nodes_in_group("character", &char_node_list);
#if 0
if (debug) {
debug->clear();
debug->begin(Mesh::PRIMITIVE_LINES);
debug->set_color(Color(1, 0, 0, 1));
}
#endif
for (e = char_node_list.front(); e; e = e->next()) {
Node *ch = e->get();
if (!ch->has_meta("animation_tree"))
continue;
if (!ch->has_meta("orientation"))
continue;
character_physics(ch);
Spatial *sp = Object::cast_to<Spatial>(ch);
Vector3 direction = -sp->get_global_transform().basis[2];
ch->set_meta("direction", direction);
}
#if 0
if (debug)
debug->end();
#endif
break;
case NOTIFICATION_READY:
arrive_precision = GLOBAL_DEF("ai/locomotion/arrive_precision", 0.2f);
set_process(true);
set_physics_process(true);
smm = memnew(SmartObjectManager);
add_child(smm);
break;
}
}
void Characters_::_bind_methods()
{
ClassDB::bind_method(D_METHOD("walkto", "ch", "target"), &Characters_::walkto);
ClassDB::bind_method(D_METHOD("walkto_node", "ch", "target"), &Characters_::walkto_node);
ClassDB::bind_method(D_METHOD("set_navmesh", "mesh", "xform"), &Characters_::set_navmesh);
ClassDB::bind_method(D_METHOD("forward_probe", "body", "lookahead", "start_height", "end_height", "height_step"), &Characters_::forward_probe);
ClassDB::bind_method(D_METHOD("get_animation_node", "npc"), &Characters_::get_animation_node);
ClassDB::bind_method(D_METHOD("handle_cmdq", "npc"), &Characters_::handle_cmdq);
ClassDB::bind_method(D_METHOD("animation_node_travel", "npc", "anim"), &Characters_::animation_node_travel);
ClassDB::bind_method(D_METHOD("set_walk_speed", "npc", "speed", "strafe"), &Characters_::set_walk_speed);
ClassDB::bind_method(D_METHOD("get_walk_speed", "npc"), &Characters_::get_walk_speed);
ClassDB::bind_method(D_METHOD("set_crowd", "crowd"), &Characters_::set_crowd_);
ClassDB::bind_method(D_METHOD("get_crowd"), &Characters_::get_crowd);
ClassDB::bind_method(D_METHOD("walkto_agent", "ch", "target"), &Characters_::walkto_agent);
ClassDB::bind_method(D_METHOD("walkto_agent_node", "ch", "target"), &Characters_::walkto_agent_node);
ClassDB::bind_method(D_METHOD("character_physics", "obj"), &Characters_::character_physics);
ADD_SIGNAL(MethodInfo("arrived", PropertyInfo(Variant::OBJECT, "obj"), PropertyInfo(Variant::VECTOR3, "where")));
}
void Characters_::process_frozen_character(Node *npc, const Vector3 &tposition)
{
float delta = npc->get_process_delta_time();
Vector3 tgt = tposition;
Spatial *sp = Object::cast_to<Spatial>(npc);
assert(sp);
Vector3 position = sp->get_global_transform().origin;
float dst = position.distance_squared_to(tgt);
tgt.y = position.y;
float tconst = delta / dst;
Transform xform = sp->get_global_transform();
xform = xform.looking_at(tgt, Vector3(0, 1, 0));
xform.origin = position.linear_interpolate(tgt, tconst);
sp->set_global_transform(xform);
}
void Characters_::process_normal_character(Node *npc, const Vector3 &tposition)
{
#if 0
Spatial *sp = Object::cast_to<Spatial>(npc);
assert(sp);
float delta = npc->get_process_delta_time();
Vector3 tgt = tposition;
Vector3 position = sp->get_global_transform().origin;
tgt.y = position.y;
#endif
}
void Characters_::process_character(Node *node, bool frozen)
{
int id = node->get_instance_id();
Spatial *sp = Object::cast_to<Spatial>(node);
if (!sp)
return;
Vector3 target;
Vector3 position = sp->get_global_transform().origin;
if (static_targets.has(id))
target = static_targets[id];
else if (dynamic_targets.has(id)) {
int target_id = dynamic_targets[id];
Object * obj = ObjectDB::get_instance(target_id);
if (!obj) {
dynamic_targets.erase(id);
return;
}
Spatial *target_node = Object::cast_to<Spatial>(obj);
if (!target_node) {
dynamic_targets.erase(id);
return;
}
target = target_node->get_global_transform().origin;
} else /* no target - no problem */
return;
float distance = position.distance_squared_to(target);
int i;
if (!paths.has(id)) {
Vector<Vector3> points;
Vector<int> flags;
Vector3 start = query->nearest_point(position, Vector3(1, 1, 1), filter);
Vector3 end = query->nearest_point(target, Vector3(1, 1, 1), filter);
query->find_path_array(start, end, Vector3(1, 1, 1), filter, points, flags);
assert(points.size() > 0);
paths[id] = points;
}
if (debug)
for (i = 0; i < paths[id].size() - 1; i++) {
debug->add_vertex(paths[id][i]);
debug->add_vertex(paths[id][i + 1]);
}
if (distance < 0.5) {
if (static_targets.has(id))
static_targets.erase(id);
if (dynamic_targets.has(id))
dynamic_targets.erase(id);
if (paths.has(id))
paths.erase(id);
emit_signal("arrived", node);
} else if (frozen) {
Vector3 tgt = target;
if (paths[id].size() > 0)
tgt = paths[id][0];
float dst = position.distance_squared_to(tgt);
while (dst < 0.0001f && paths[id].size() > 0) {
paths[id].erase(tgt);
tgt = paths[id][0];
dst = position.distance_squared_to(tgt);
}
process_frozen_character(node, tgt);
} else {
Vector3 tgt = target;
if (paths[id].size() > 0)
tgt = paths[id][0];
float dst = position.distance_squared_to(tgt);
while (dst < 0.0001f && paths[id].size() > 0) {
paths[id].erase(tgt);
tgt = paths[id][0];
dst = position.distance_squared_to(tgt);
}
process_normal_character(node, tgt);
}
}
Characters_::~Characters_()
{
memdelete(query);
}
void Characters_::walkto(const Node *ch, const Vector3 &target)
{
static_targets[ch->get_instance_id()] = target;
}
void Characters_::walkto_node(const Node *ch, const Node *target)
{
dynamic_targets[ch->get_instance_id()] = target->get_instance_id();
}
void Characters_::set_navmesh(Ref<DetourNavigationMesh> mesh, const Transform &xform)
{
query->init(mesh, xform);
filter.instance();
debug = memnew(ImmediateGeometry);
add_child(debug);
initialized = true;
Ref<SpatialMaterial> mat;
mat.instance();
mat->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
mat->set_flag(SpatialMaterial::FLAG_DISABLE_DEPTH_TEST, true);
mat->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
debug->set_material_override(mat);
}
float Characters_::forward_probe(Node *body, float lookahead,
float start_height, float end_height,
float height_step)
{
float result = 0.0f, d;
Ref<World> world;
PhysicsDirectSpaceState *space;
PhysicsDirectSpaceState::RayResult rr;
Set<RID> exclude;
PhysicsBody *pbody = Object::cast_to<PhysicsBody>(body);
assert(pbody);
world = pbody->get_world();
if (!world.ptr())
return result;
space = PhysicsServer::get_singleton()->space_get_direct_state(world->get_space());
d = start_height;
exclude.insert(pbody->get_rid());
while (d < end_height) {
Transform g = pbody->get_global_transform();
Vector3 o = g.origin + Vector3(0, 1, 0) * d;
Vector3 f = g.xform(Vector3(0, 0, -1) * lookahead + Vector3(0, 1, 0) * d);
if (space->intersect_ray(o, f, rr, exclude)) {
int against = rr.collider_id;
Object * obj = ObjectDB::get_instance(against);
if (obj) {
Node *col = Object::cast_to<Node>(obj);
if (col && !col->is_in_group("character"))
result = d;
}
}
d += height_step;
}
return result;
}
void Characters_::set_crowd(DetourCrowdManager *crowd)
{
this->crowd = crowd;
}
void Characters_::set_crowd_(Node *crowd_node)
{
DetourCrowdManager *crowd =
Object::cast_to<DetourCrowdManager>(crowd_node);
if (crowd)
set_crowd(crowd);
}
DetourCrowdManager *Characters_::get_crowd() const
{
return crowd;
}

View File

@@ -0,0 +1,60 @@
#include <scene/main/node.h>
#include <core/math/vector3.h>
#include <modules/detour/navmesh_query.h>
class Spatial;
class DetourNavigationQuery;
class DetourCrowdManager;
class ImmediateGeometry;
class PhysicsBody;
class AnimationTree;
class SmartObjectManager;
class Characters_: public Node {
GDCLASS(Characters_, Node)
public:
Characters_();
~Characters_();
void walkto(const Node *ch, const Vector3 &target);
void walkto_node(const Node *ch, const Node *target);
void walkto_agent(Node *ch, const Vector3 &target);
void walkto_agent_node(Node *ch, const Node *target);
void set_navmesh(Ref<DetourNavigationMesh> mesh, const Transform &xform);
float forward_probe(Node *body, float lookahead,
float start_height, float end_height,
float height_step);
AnimationTree *get_animation_tree(const Node *npc) const;
void animation_node_travel(Node *npc, const String &anim);
void set_walk_speed(Node *npc, float speed, float strafe = 0.0f);
float get_walk_speed(const Node *npc) const;
String get_animation_node(const Node *npc);
bool handle_cmdq(Node *npc);
void character_physics(Object *obj);
void rotate_to_agent(Spatial *obj);
void speed_to_agent(Spatial *obj);
void update_arrived(Object *obj);
void set_crowd(DetourCrowdManager *crowd);
void set_crowd_(Node *crowd_node);
DetourCrowdManager *get_crowd() const;
Object *get_crowd_();
bool has_arrived(Object *obj);
void agent_walk_stop(Object *obj);
protected:
void _notification(int p_what);
static void _bind_methods();
void process_character(Node *node, bool frozen);
void process_frozen_character(Node *npc, const Vector3 &tposition);
void process_normal_character(Node *npc, const Vector3 &tposition);
HashMap<int, Vector3> static_targets;
HashMap<int, int> dynamic_targets;
HashMap<int, Vector<Vector3>> paths;
DetourNavigationQuery *query;
Ref<DetourNavigationQueryFilter> filter;
SmartObjectManager *smm;
bool initialized;
ImmediateGeometry *debug;
DetourCrowdManager *crowd;
float arrive_precision;
};

Binary file not shown.

8
modules/world/config.py Normal file
View File

@@ -0,0 +1,8 @@
def can_build(env, platform):
return True
def configure(env):
pass

View File

@@ -0,0 +1,232 @@
#include <cstdio>
#include "world_map_data.h"
#include "density_map.h"
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/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"));
}
bool DensityMap::_get(const StringName &p_name, Variant &r_ret) const
{
if (p_name == "world/clusters/count") {
r_ret = num_clusters;
return true;
} else if (p_name == "world/clusters/seed") {
r_ret = seed;
return true;
} else if (p_name == "world/grid_size") {
r_ret = grid_size;
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;
return true;
}
const String pv = p_name.operator String();
return false;
}
bool DensityMap::_set(const StringName &p_name, const Variant &p_value)
{
bool update = false;
if (p_name == "world/clusters/count") {
num_clusters = p_value;
update = true;
} else if (p_name == "world/clusters/seed") {
seed = p_value;
update = true;
} else if (p_name == "world/grid_size") {
grid_size = 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;
update = true;
}
if (update) {
update_clusters();
_change_notify();
}
return update;
}
void DensityMap::populate_grid(List<struct area> &list)
{
}
void DensityMap::update_clusters()
{
int i;
clusters.clear();
regions.clear();
counties.clear();
cities.clear();
districts.clear();
circle_grid.clear();
if (num_clusters < 1)
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(&regions[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<struct area> &list)
{
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;
}
#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<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()
{
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;
}
DensityMap::~DensityMap()
{
}

View File

@@ -0,0 +1,56 @@
#ifndef DENSITY_MAP_H
#define DENSITY_MAP_H
#include <core/resource.h>
#include <core/math/random_number_generator.h>
#include <modules/voxel/util/math/vector3i.h>
class DensityMap: public Resource {
GDCLASS(DensityMap, Resource);
public:
DensityMap();
~DensityMap();
protected:
Ref<RandomNumberGenerator> 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<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 _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
public:
void update_clusters();
};
#endif

Binary file not shown.

View File

@@ -0,0 +1,171 @@
#include <scene/resources/world.h>
#include "direct_lod_multimesh.h"
void LodMultiMesh3D::set_mesh(int id, Ref<Mesh> mesh)
{
meshes[id] = mesh;
VisualServer::get_singleton()->instance_set_visible(multimeshes[id].instance, false);
if (!mesh.is_null())
VisualServer::get_singleton()->multimesh_set_mesh(multimeshes[id].id, mesh->get_rid());
else
VisualServer::get_singleton()->multimesh_set_mesh(multimeshes[id].id, RID());
VisualServer::get_singleton()->instance_set_visible(multimeshes[id].instance, true);
}
Ref<Mesh> LodMultiMesh3D::get_mesh(int id) const
{
return meshes[id];
}
void LodMultiMesh3D::set_instance_count(int id, int count)
{
VisualServer::get_singleton()->multimesh_allocate(multimeshes[id].id,
count, VS::MULTIMESH_TRANSFORM_3D,
VS::MultimeshColorFormat(color_format),
VS::MultimeshCustomDataFormat(custom_data_format));
multimeshes[id].instance_count = count;
}
int LodMultiMesh3D::get_instance_count(int id) const
{
return multimeshes[id].instance_count;
}
void LodMultiMesh3D::set_visible_instance_count(int id, int count)
{
ERR_FAIL_COND(count < -1);
VisualServer::get_singleton()->multimesh_set_visible_instances(multimeshes[id].id, count);
multimeshes[id].visible_instance_count = count;
}
int LodMultiMesh3D::get_visible_instance_count(int id) const
{
return multimeshes[id].visible_instance_count;
}
void LodMultiMesh3D::set_instance_transform(int id, int instance, const Transform &xform) {
VisualServer::get_singleton()->multimesh_instance_set_transform(multimeshes[id].id,
instance, xform);
}
LodMultiMesh3D::LodMultiMesh3D()
{
custom_data_format = CUSTOM_DATA_8BIT;
color_format = COLOR_FLOAT;
}
LodMultiMesh3D::~LodMultiMesh3D()
{
List<struct multimesh_data>::Element *e =
multimeshes.front();
while(e) {
const struct multimesh_data &md = e->get();
VisualServer::get_singleton()->instance_set_scenario(md.instance, RID());
VisualServer::get_singleton()->free(md.id);
e = e->next();
}
}
void LodMultiMesh3D::add_mesh(Ref<Mesh> mesh)
{
struct multimesh_data md;
md.id = VisualServer::get_singleton()->multimesh_create();
md.instance = VisualServer::get_singleton()->instance_create();
if (mesh.ptr())
VisualServer::get_singleton()->multimesh_set_mesh(md.id, mesh->get_rid());
else
VisualServer::get_singleton()->multimesh_set_mesh(md.id, RID());
VisualServer::get_singleton()->instance_set_base(md.instance, md.id);
md.instance_count = 0;
md.visible_instance_count = -1;
md.lod_decimator = 1;
VisualServer::get_singleton()->instance_set_visible(md.instance, true);
multimeshes.push_back(md);
}
void LodMultiMesh3D::set_instance_color(int id, int instance, const Color &color) {
VisualServer::get_singleton()->multimesh_instance_set_color(
multimeshes[id].id, instance, color);
}
Color LodMultiMesh3D::get_instance_color(int id, int instance) const
{
return VisualServer::get_singleton()->multimesh_instance_get_color(multimeshes[id].id, instance);
}
void LodMultiMesh3D::hide()
{
List<struct multimesh_data>::Element *e =
multimeshes.front();
while(e) {
const struct multimesh_data &md = e->get();
VS::get_singleton()->instance_set_visible(md.instance, false);
e = e->next();
}
visible = false;
}
void LodMultiMesh3D::show()
{
List<struct multimesh_data>::Element *e =
multimeshes.front();
while(e) {
const struct multimesh_data &md = e->get();
VS::get_singleton()->instance_set_visible(md.instance, true);
e = e->next();
}
visible = true;
}
bool LodMultiMesh3D::is_visible() const
{
return visible;
}
void LodMultiMesh3D::set_global_transform(const Transform &xform)
{
xf = xform;
List<struct multimesh_data>::Element *e =
multimeshes.front();
while(e) {
const struct multimesh_data &md = e->get();
VisualServer::get_singleton()->instance_set_transform(md.instance, xf);
e = e->next();
}
}
Transform LodMultiMesh3D::get_global_transform() const
{
return xf;
}
void LodMultiMesh3D::set_world(const World *world)
{
List<struct multimesh_data>::Element *e =
multimeshes.front();
RID r;
if (world)
r = world->get_scenario();
while(e) {
const struct multimesh_data &md = e->get();
VisualServer::get_singleton()->instance_set_scenario(md.instance, r);
e = e->next();
}
}
AABB LodMultiMesh3D::get_aabb(int id) const
{
return VisualServer::get_singleton()->multimesh_get_aabb(multimeshes[id].id);
}
Vector3 LodMultiMesh3D::get_center() const
{
AABB r;
int i;
for (i = 0; i < multimeshes.size(); i++)
r.merge_with(get_aabb(i));
return r.position + r.size *0.5f + get_global_transform().origin;
}

View File

@@ -0,0 +1,68 @@
#ifndef DIRECT_LOD_MULTIMESH_H
#define DIRECT_LOD_MULTIMESH_H
#include <scene/resources/mesh.h>
#include <servers/visual_server.h>
#include <modules/opensimplex/open_simplex_noise.h>
class World;
class LodMultiMesh3D {
public:
enum ColorFormat {
COLOR_NONE = VS::MULTIMESH_COLOR_NONE,
COLOR_8BIT = VS::MULTIMESH_COLOR_8BIT,
COLOR_FLOAT = VS::MULTIMESH_COLOR_FLOAT,
};
enum CustomDataFormat {
CUSTOM_DATA_NONE,
CUSTOM_DATA_8BIT,
CUSTOM_DATA_FLOAT,
};
protected:
List<Ref<Mesh> > meshes;
ColorFormat color_format;
CustomDataFormat custom_data_format;
struct multimesh_data {
RID id, instance;
int instance_count;
int visible_instance_count;
int lod_decimator;
Ref<OpenSimplexNoise> density_noise;
};
List<struct multimesh_data> multimeshes;
Transform xf;
bool visible;
public:
void add_mesh(Ref<Mesh> mesh);
int get_mesh_count() const;
void set_mesh(int id, Ref<Mesh> mesh);
Ref<Mesh> get_mesh(int id) const;
void set_color_format(ColorFormat p_color_format);
ColorFormat get_color_format() const;
void set_custom_data_format(CustomDataFormat p_custom_data_format);
CustomDataFormat get_custom_data_format() const;
void set_density_noise(int id, Ref<OpenSimplexNoise> noise);
Ref<OpenSimplexNoise> get_density_noise(int id) const;
void set_instance_count(int id, int count);
int get_instance_count(int id) const;
void set_instance_transform(int id, int instance, const Transform &xform);
void set_visible_instance_count(int id, int count);
int get_visible_instance_count(int id) const;
void set_instance_color(int id, int instance, const Color &color);
void hide();
void show();
void set_global_transform(const Transform &xform);
Transform get_global_transform() const;
Color get_instance_color(int id, int instance) const;
void set_world(const World *world);
bool is_visible() const;
AABB get_aabb(int id) const;
Vector3 get_center() const;
LodMultiMesh3D();
~LodMultiMesh3D();
};
#endif

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,14 @@
#include "impostor.h"
ImpostorMaker::ImpostorMaker():
frames_root_number(16),
image_dimentions(4096),
is_full_sphere(false),
atlas_coverage(1.0f),
shader_type(STANDARD)
{
}
ImpostorMaker::~ImpostorMaker()
{
}

19
modules/world/impostor.h Normal file
View File

@@ -0,0 +1,19 @@
#include <core/reference.h>
#include <scene/resources/packed_scene.h>
class ImpostorMaker {
Ref<PackedScene> meshes;
int frames_root_number;
int image_dimentions;
bool is_full_sphere;
float atlas_coverage;
enum {
LIGHT,
STANDARD,
TEXTURE_ARRAY
};
int shader_type;
public:
ImpostorMaker();
~ImpostorMaker();
};

Binary file not shown.

View File

@@ -0,0 +1,113 @@
#include "mesher.h"
CompoundTransvoxel::CompoundTransvoxel() : VoxelMesherTransvoxel()
{
}
CompoundTransvoxel::~CompoundTransvoxel()
{
}
void CompoundTransvoxel::build(VoxelMesher::Output &output, const VoxelMesher::Input &input)
{
VoxelMesherTransvoxel::build(output, input);
}
Ref<Resource> CompoundTransvoxel::duplicate(bool p_subresources) const
{
return VoxelMesherTransvoxel::duplicate(p_subresources);
}
int CompoundTransvoxel::get_used_channels_mask() const
{
return VoxelMesherTransvoxel::get_used_channels_mask();
}
void CompoundTransvoxelInspector::open_scene(Object *button)
{
CompoundTransvoxel *obj = Object::cast_to<CompoundTransvoxel>(button->get_meta("what"));
fd->set_meta("what", obj);
fd->popup_centered_ratio();
}
CompoundTransvoxelInspector::CompoundTransvoxelInspector(): EditorInspectorPlugin()
{
}
void CompoundTransvoxel::_bind_methods()
{
}
void CompoundTransvoxelInspector::_bind_methods()
{
ClassDB::bind_method(D_METHOD("open_scene", "button"), &CompoundTransvoxelInspector::open_scene);
}
bool CompoundTransvoxelInspector::can_handle(Object *p_object)
{
return Object::cast_to<CompoundTransvoxel>(p_object) != NULL;
}
void CompoundTransvoxelInspector::scenes_selected(const PoolVector<String> &p_paths)
{
struct CompoundTransvoxel::place_item it;
for (int i = 0; i < p_paths.size(); i++) {
Ref<PackedScene> t = Ref<PackedScene>(ResourceLoader::load(p_paths[i]));
ERR_CONTINUE_MSG(!t.is_valid(), "'" + p_paths[i] + "' is not a valid scene.");
it.scene = t;
}
}
void CompoundTransvoxelInspector::parse_begin(Object *p_object)
{
int i;
CompoundTransvoxel *obj = Object::cast_to<CompoundTransvoxel>(p_object);
if (!obj)
return;
VBoxContainer *control = memnew(VBoxContainer);
fd = memnew(EditorFileDialog);
fd->set_access(EditorFileDialog::ACCESS_RESOURCES);
fd->set_mode(EditorFileDialog::MODE_OPEN_FILE);
fd->set_resizable(true);
List<String> extensions;
ResourceLoader::get_recognized_extensions_for_type("Texture", &extensions);
for (List<String>::Element *E = extensions.front(); E; E = E->next()) {
fd->add_filter("*." + E->get() + " ; " + E->get().to_upper());
}
fd->connect("files_selected", this, "scenes_selected");
control->add_child(fd);
for (i = 0; i < obj->items.size(); i++) {
HBoxContainer *vcontrol = memnew(HBoxContainer);
Button *open_scene = memnew(Button);
open_scene->set_text("Select scene...");
open_scene->connect("pressed", this, "open_scene", Node::make_binds(open_scene));
open_scene->set_meta("what", &obj->items[i]);
vcontrol->add_child(open_scene);
Label *l1 = memnew(Label);
l1->set_text("scene path: ");
vcontrol->add_child(l1);
/* full instance */
Label *l2 = memnew(Label);
l2->set_text("full instance: ");
vcontrol->add_child(l2);
CheckBox *cb1 = memnew(CheckBox);
vcontrol->add_child(cb1);
/* use collision */
Label *l3 = memnew(Label);
l3->set_text("use collision: ");
vcontrol->add_child(l3);
CheckBox *cb2 = memnew(CheckBox);
vcontrol->add_child(cb2);
/* priority */
Label *l4 = memnew(Label);
l4->set_text("priority: ");
vcontrol->add_child(l4);
#if 0
SpinBox *sb1 = memnew(SpinBox);
vcontrol->add_child(sb1);
#endif
control->add_child(vcontrol);
}
Button *addbutton = memnew(Button);
addbutton->set_text("+");
control->add_child(addbutton);
add_custom_control(control);
}

View File

@@ -0,0 +1,54 @@
#include <modules/voxel/meshers/transvoxel/voxel_mesher_transvoxel.h>
#include <editor/editor_plugin.h>
#include <scene/resources/packed_scene.h>
class CompoundTransvoxel: public VoxelMesherTransvoxel
{
GDCLASS(CompoundTransvoxel, VoxelMesherTransvoxel)
friend class CompoundTransvoxelInspector;
protected:
struct place_item {
Ref<PackedScene> scene;
String scene_path;
bool full_instance;
bool use_collision;
int priority;
};
List<struct place_item> items;
public:
CompoundTransvoxel();
~CompoundTransvoxel();
void build(VoxelMesher::Output &output, const VoxelMesher::Input &input) override;
Ref<Resource> duplicate(bool p_subresources = false) const override;
int get_used_channels_mask() const override;
protected:
static void _bind_methods();
};
class CompoundTransvoxelInspector: public EditorInspectorPlugin {
GDCLASS(CompoundTransvoxelInspector, EditorInspectorPlugin)
public:
virtual bool can_handle(Object *p_object);
CompoundTransvoxelInspector();
private:
EditorFileDialog *fd;
protected:
void open_scene(Object *button);
void scenes_selected(const PoolVector<String> &p_paths);
void parse_begin(Object *p_object);
static void _bind_methods();
};
class CompoundTransvoxelEditorPlugin : public EditorPlugin {
GDCLASS(CompoundTransvoxelEditorPlugin, EditorPlugin)
public:
virtual String get_name() const { return "CompoundTransvoxel"; }
CompoundTransvoxelEditorPlugin(EditorNode *p_node)
{
Ref<CompoundTransvoxelInspector> plugin;
plugin.instance();
add_inspector_plugin(plugin);
}
};

Binary file not shown.

View File

@@ -0,0 +1,8 @@
#ifndef NEW_POINT_H
#define NEW_POINT_H
#include <core/math/transform.h>
struct new_point {
Transform xform;
int tag;
};
#endif

View File

@@ -0,0 +1,2 @@
#include "new_world.h"

199
modules/world/new_world.h Normal file
View File

@@ -0,0 +1,199 @@
#include <scene/3d/spatial.h>
#define MAX_ENTITIES 1000
#define MAX_COMPONENTS 16
class NewWorld: public Spatial {
GDCLASS(NewWorld, Spatial)
class EntityManager {
List<int> available_entities;
int entity_count;
uint32_t signatures[MAX_ENTITIES];
public:
EntityManager()
{
int e;
for (e = 0; e < MAX_ENTITIES; e++) {
available_entities.push_back(e);
signatures[e] = 0U;
}
entity_count = 0;
}
int create_entity()
{
int ret;
if (entity_count >= MAX_ENTITIES || available_entities.size() == 0)
return -1;
ret = available_entities.front()->get();
available_entities.pop_front();
entity_count++;
return ret;
}
void destroy_entity(int e)
{
if (e >= MAX_ENTITIES)
return;
entity_count--;
available_entities.push_back(e);
signatures[e] = 0U;
}
inline void set_signature(int e, uint32_t sig)
{
signatures[e] = sig;
}
inline uint32_t get_signature(int e)
{
return signatures[e];
}
};
class IComponentArray {
public:
virtual ~IComponentArray() = default;
virtual void destroyed(int entity) = 0;
};
template <typename T>
class ComponentArray: public IComponentArray {
Vector<T> component_array;
HashMap<int, size_t> entity_to_index;
HashMap<size_t, int> index_to_entity;
size_t size;
public:
void insert_data(int entity, T component)
{
if (entity_to_index.has(entity))
return;
size_t new_index = size;
entity_to_index[entity] = new_index;
index_to_entity[new_index] = entity;
component_array[new_index] = component;
size++;
}
void remove_data(int entity)
{
if (!entity_to_index.has(entity))
return;
size_t removed_index = entity_to_index[entity];
size_t last_index = size - 1;
component_array[removed_index] = component_array[last_index];
int last_entity = index_to_entity[last_index];
entity_to_index[last_entity] = removed_index;
index_to_entity[removed_index] = last_entity;
entity_to_index.erase(entity);
index_to_entity.erase(last_index);
size--;
}
T *get(int entity)
{
if (!entity_to_index.has(entity)) {
return NULL;
}
return &component_array[entity_to_index[entity]];
}
void destroyed(int entity) override
{
remove_data(entity);
}
};
#define __REFLECT_NAME(N) #N
class ComponentManager {
HashMap<const char *, int> component_types{};
HashMap<const char *, IComponentArray *> component_arrays{};
int next_component_type{};
template <typename T>
ComponentArray<T> *get_component_array()
{
const char* type_name = __REFLECT_NAME(T);
if (!component_types.has(type_name))
return NULL;
return static_cast<ComponentArray<T> >(component_arrays[type_name]);
}
public:
template<typename T>
void register_component()
{
const char* type_name = __REFLECT_NAME(T);
component_types[type_name] = next_component_type;
component_arrays[type_name] = memnew(ComponentArray<T>());
next_component_type++;
}
template<typename T>
int get_component_type()
{
const char* type_name = __REFLECT_NAME(T);
return component_types[type_name] *
component_types.has(type_name) -
1 * (!component_types.has(type_name));
}
template<typename T>
void add_component(int entity, T component)
{
get_component_array<T>()->insert_data(entity, component);
}
template<typename T>
void remove_component(int entity)
{
get_component_array<T>()->remove(entity);
}
template<typename T>
T &get_component(int entity)
{
get_component_array<T>()->get_data(entity);
}
void destroyed(int entity)
{
const char * const *e = component_arrays.next(NULL);
while (e) {
component_arrays[*e]->destroyed(entity);
e = component_arrays.next(e);
}
}
};
class System {
public:
Vector<uint32_t> entities;
};
class SystemManager {
HashMap<const char *, System *> systems;
HashMap<const char *, uint32_t> signatures;
public:
template<typename T>
T *register_system()
{
const char* type_name = __REFLECT_NAME(T);
T * system = memnew(T);
systems[type_name] = system;
return system;
}
template<typename T>
void set_signature(uint32_t signature)
{
const char* type_name = __REFLECT_NAME(T);
signatures[type_name] = signature;
}
void destroyed(int entity)
{
const char * const * e = systems.next(NULL);
while (e) {
systems[*e]->entities.erase(entity);
e = systems.next(e);
}
}
void signature_changed(int entity, uint32_t signature)
{
const char * const * e = systems.next(NULL);
while (e) {
if ((signature & signatures[*e]) == signatures[*e])
systems[*e]->entities.push_back(entity);
else
systems[*e]->entities.erase(entity);
e = systems.next(e);
}
}
};
ComponentManager cman;
EntityManager eman;
SystemManager sman;
};

Binary file not shown.

View File

@@ -0,0 +1,35 @@
#include <core/engine.h>
#include <modules/detour/detour-navmesh.h>
#include "register_types.h"
#include "world_generator.h"
#include "density_map.h"
#include "world_map_data.h"
#include "mesher/mesher.h"
#include "characters.h"
#include "smart_object.h"
#include "road_map.h"
void register_world_types()
{
RoadMap::create_singleton();
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()));
ClassDB::register_class<WorldGenerator>();
ClassDB::register_class<DensityMap>();
ClassDB::register_class<CompoundTransvoxel>();
ClassDB::register_class<CompoundTransvoxelInspector>();
ClassDB::register_class<Characters_>();
ClassDB::register_class<SmartObject>();
ClassDB::register_class<SmartObjectManager>();
ClassDB::register_class<SmartObjectGroup>();
#if TOOLS_ENABLED
EditorPlugins::add_by_type<CompoundTransvoxelEditorPlugin>();
#endif
}
void unregister_world_types()
{
WorldMapData::destroy_singleton();
}

View File

@@ -0,0 +1,2 @@
void register_world_types();
void unregister_world_types();

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,33 @@
#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
static RoadMap *g_road_map_data = NULL;
RoadMap *RoadMap::get_singleton()
{
return g_road_map_data;
}
void RoadMap::create_singleton()
{
g_road_map_data = memnew(RoadMap);
}
void RoadMap::destroy_singleton()
{
memdelete(g_road_map_data);
g_road_map_data = NULL;
}
RoadMap::RoadMap()
{
}
RoadMap::~RoadMap()
{
}

35
modules/world/road_map.h Normal file
View File

@@ -0,0 +1,35 @@
#include <core/object.h>
class RoadMap: public Object {
GDCLASS(RoadMap, Object)
public:
RoadMap();
~RoadMap();
static RoadMap *get_singleton();
static void create_singleton();
static void destroy_singleton();
protected:
Vector<int> vertices;
struct segment {
int v1, v2;
uint32_t flags;
};
Vector<segment> segments;
struct intersection {
#define MAX_NEIGHBORS 4
int neighbors[MAX_NEIGHBORS];
int ncount;
uint32_t flags;
};
Vector<intersection> intersections;
/* cylindric area to define road */
struct area {
int x, z;
int radius;
int type;
};
Vector<area> areas;
};

Binary file not shown.

View File

@@ -0,0 +1,371 @@
#include <cstdio>
#include <cassert>
#include <core/engine.h>
#include <scene/animation/animation_tree.h>
#include <scene/animation/animation_node_state_machine.h>
#include <scene/animation/animation_blend_tree.h>
#include "smart_object.h"
SmartObject::SmartObject(): anim_state(""),
anim_finish_state(""),
enabled(false), teleport(false),
distance(0.0f), state(STATE_INIT)
{
#ifdef TOOLS_ENABLED
run_in_editor = false;
#endif
}
void SmartObject::_notification(int p_what)
{
}
void SmartObject::_bind_methods()
{
}
#ifdef TOOLS_ENABLED
void SmartObject::draw_debug()
{
if (!character.ptr())
return;
Node *node = character->instance();
if (!node)
return;
add_child(node);
Spatial *sp = Object::cast_to<Spatial>(node);
if (sp)
sp->set_transform(Transform());
AnimationTree *anim = find_animation_tree(node);
assert(anim);
node->set_meta("animation_tree", anim);
if (anim_state.length() > 0)
SmartObjectManager::play_animation(node, anim_state);
else
SmartObjectManager::play_animation(node, "locomotion");
set_meta("debug", node);
}
void SmartObject::clear_debug()
{
if (!has_meta("debug"))
return;
Node *node = get_meta("debug");
if (node) {
remove_child(node);
node->queue_delete();
}
remove_meta("debug");
}
#endif
void SmartObject::_get_property_list(List<PropertyInfo> *p_list) const
{
#if TOOLS_ENABLED
String animations;
if (character.ptr()) {
animations = get_animation_list();
}
if (animations.length() == 0)
animations = "default";
#endif
p_list->push_back(PropertyInfo(Variant::BOOL, "enabled"));
p_list->push_back(PropertyInfo(Variant::STRING, "animation_state", PROPERTY_HINT_ENUM, animations));
p_list->push_back(PropertyInfo(Variant::STRING, "animation_finish_state", PROPERTY_HINT_ENUM, animations));
p_list->push_back(PropertyInfo(Variant::BOOL, "teleport"));
p_list->push_back(PropertyInfo(Variant::REAL, "distance"));
#ifdef TOOLS_ENABLED
p_list->push_back(PropertyInfo(Variant::OBJECT, "character", PROPERTY_HINT_RESOURCE_TYPE, "PackedScene"));
p_list->push_back(PropertyInfo(Variant::BOOL, "run_in_editor", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR));
#endif
}
bool SmartObject::_get(const StringName &p_name, Variant &r_ret) const
{
if (p_name == "enabled") {
r_ret = enabled;
return true;
} else if (p_name == "animation_state") {
r_ret = anim_state;
return true;
} else if (p_name == "animation_finish_state") {
r_ret = anim_finish_state;
return true;
} else if (p_name == "teleport") {
r_ret = teleport;
return true;
} else if (p_name == "distance") {
r_ret = distance;
return true;
#ifdef TOOLS_ENABLED
} else if (p_name == "character") {
r_ret = character;
return true;
} else if (p_name == "run_in_editor") {
r_ret = run_in_editor;
return true;
#endif
}
return false;
}
#ifdef TOOLS_ENABLED
String SmartObject::dump_subnodes(Ref<AnimationNode> anode) const
{
String animations = "";
List<AnimationNode::ChildNode> child_nodes;
anode->get_child_nodes(&child_nodes);
List<AnimationNode::ChildNode>::Element *c;
for (c = child_nodes.front(); c; c = c->next()) {
AnimationNode::ChildNode cn = c->get();
animations += cn.name;
if (c->next())
animations += ",";
}
return animations;
}
#endif
AnimationTree *SmartObject::find_animation_tree(Node *node) const
{
int i;
List<Node *> queue;
queue.push_back(node);
List<Node *>::Element *e;
while(!queue.empty()) {
e = queue.front();
Node *item = e->get();
queue.pop_front();
AnimationTree *at = Object::cast_to<AnimationTree>(item);
if (at) {
if (!at->is_in_group("hair") &&
!at->is_in_group("hair"))
return at;
}
for (i = 0; i < item->get_child_count(); i++)
queue.push_back(item->get_child(i));
}
return NULL;
}
String SmartObject::get_animation_list() const
{
Node *node = character->instance();
assert(node);
String animations;
if (node) {
AnimationTree *anim = find_animation_tree(node);
assert(anim);
if (anim) {
List<PropertyInfo> plist;
Ref<AnimationNodeBlendTree> anim_root = get_anode<AnimationNodeBlendTree>(anim->get_tree_root());
assert(anim_root.ptr());
Ref<AnimationNodeStateMachine> anim_state = get_anode<AnimationNodeStateMachine>(anim_root->get_child_by_name("state"));
assert(anim_state.ptr());
animations = dump_subnodes(anim_state);
printf("ANIMATIONS: %ls\n", animations.c_str());
#if 0
anim_root->get_property_list(&plist);
List<PropertyInfo>::Element *e;
for (e = plist.front(); e; e = e->next()) {
PropertyInfo pi = e->get();
String name = pi.name;
printf("node: %ls\n", name.c_str());
}
#endif
}
node->queue_delete();
}
return animations;
}
bool SmartObject::_set(const StringName &p_name, const Variant &p_value)
{
bool update = false;
if (p_name == "enabled") {
enabled = p_value;
update = true;
} else if (p_name == "animation_state") {
anim_state = p_value;
update = true;
} else if (p_name == "animation_finish_state") {
anim_finish_state = p_value;
update = true;
} else if (p_name == "teleport") {
teleport = p_value;
update = true;
} else if (p_name == "distance") {
distance = p_value;
update = true;
#ifdef TOOLS_ENABLED
} else if (p_name == "character") {
character = p_value;
update = true;
} else if (p_name == "run_in_editor") {
run_in_editor = p_value;
update = true;
#endif
}
if (update) {
if (enabled) {
if (!is_in_group("_smart_object"))
add_to_group("_smart_object");
} else {
if (is_in_group("_smart_object"))
remove_from_group("_smart_object");
}
_change_notify();
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
if (run_in_editor)
draw_debug();
else
clear_debug();
}
#endif
}
return update;
}
void SmartObjectManager::play_animation(Node *cnode, const String &anim_state)
{
Node *anode = cnode->get_meta("animation_tree");
assert(anode);
AnimationTree *anim = Object::cast_to<AnimationTree>(anode);
assert(anim);
Ref<AnimationNodeStateMachinePlayback> playback;
playback = anim->get("parameters/state/playback");
assert(playback.ptr());
playback->travel(anim_state);
printf("ANIMATION play %ls\n", anim_state.c_str());
}
void SmartObjectManager::update_state(SmartObject *sm)
{
if (sm->state == SmartObject::STATE_INIT)
return;
Object *obj = ObjectDB::get_instance(sm->npc_id);
assert(obj);
Node *ch = Object::cast_to<Node>(obj);
assert(ch);
Spatial *csp = Object::cast_to<Spatial>(obj);
assert(csp);
Transform oxform = sm->get_global_transform();
Transform cxform = csp->get_global_transform();
switch(sm->state) {
case SmartObject::STATE_INIT:
break;
case SmartObject::STATE_SELECTED:
printf("state == SELECTED\n");
if (sm->teleport) {
csp->set_global_transform(oxform);
Transform orientation = ch->get_meta("orientation");
orientation.basis = oxform.basis;
ch->set_meta("orientation", orientation);
printf("arrived: teleport\n");
sm->state = SmartObject::STATE_ACTIVE;
} else {
if (!cxform.origin.is_equal_approx(oxform.origin))
sm->state = SmartObject::STATE_CLOSE;
else
sm->state = SmartObject::STATE_ACTIVE;
}
break;
case SmartObject::STATE_CLOSE:
printf("state == CLOSE\n");
if (!cxform.origin.is_equal_approx(oxform.origin)) {
csp->set_global_transform(cxform.interpolate_with(oxform, get_process_delta_time()));
printf("arrived: interpolate pos\n");
} else
sm->state = SmartObject::STATE_ACTIVE;
break;
case SmartObject::STATE_ACTIVE:
printf("state == ACTIVE\n");
printf("arrived: at position\n");
printf("SMART object active\n");
ch->set_meta("smart_object", Object::cast_to<Object>(sm));
play_animation(ch, sm->anim_state);
sm->state = SmartObject::STATE_PROGRESS;
break;
case SmartObject::STATE_PROGRESS:
break;
case SmartObject::STATE_FINISH:
play_animation(ch, sm->anim_finish_state);
ch->remove_meta("smart_object");
ch->remove_meta("_target");
ch->remove_meta("_target_node");
sm->state = SmartObject::STATE_INIT;
break;
}
}
void SmartObjectManager::arrived(SmartObject *sm, Node *ch)
{
assert(sm);
assert(ch);
assert(!ch->has_meta("smart_object"));
Spatial *csp = Object::cast_to<Spatial>(ch);
assert(csp);
Node *onode = Object::cast_to<Node>(sm);
assert(onode);
Transform oxform = sm->get_global_transform();
Transform cxform = csp->get_global_transform();
switch(sm->state) {
case SmartObject::STATE_INIT:
printf("arrived: warning: STATE_INIT\n");
sm->state = SmartObject::STATE_SELECTED;
sm->npc_id = ch->get_instance_id();
/* no break - transition to next state */
case SmartObject::STATE_SELECTED:
case SmartObject::STATE_CLOSE:
case SmartObject::STATE_ACTIVE:
update_state(sm);
break;
case SmartObject::STATE_PROGRESS:
break;
}
}
void SmartObjectManager::_notification(int p_what)
{
List<Node *> char_node_list;
List<Node *> smart_object_list;
List<Node *>::Element *e, *c;
switch(p_what) {
case NOTIFICATION_PHYSICS_PROCESS:
/* don't run in editor */
if (Engine::get_singleton()->is_editor_hint())
return;
get_tree()->get_nodes_in_group("character", &char_node_list);
get_tree()->get_nodes_in_group("_smart_object", &smart_object_list);
for (e = char_node_list.front(); e; e = e->next()) {
Node *cnode = e->get();
if (!cnode->has_meta("_target"))
continue;
if (cnode->has_meta("smart_object"))
continue;
for (c = smart_object_list.front(); c; c = c->next()) {
Node *onode = c->get();
if (!onode)
continue;
if (cnode->has_meta("_target_node"))
continue;
Vector3 ctarget = cnode->get_meta("_target");
SmartObject *obj = Object::cast_to<SmartObject>(onode);
if (!obj)
continue;
cnode->set_meta("_target_node", onode);
#if 0
if (ctarget.is_equal_approx(oxform.origin)) {
float d = cxform.origin.distance_squared_to(oxform.origin);
if (d < obj->distance * obj->distance) {
arrived(obj, cnode);
}
}
#endif
}
}
break;
case NOTIFICATION_READY:
set_physics_process(true);
break;
}
}

View File

@@ -0,0 +1,61 @@
#include <scene/3d/spatial.h>
#ifdef TOOLS_ENABLED
#include <scene/resources/packed_scene.h>
#include <scene/animation/animation_node_state_machine.h>
#endif
/* Starting point of animation */
class SmartObject: public Spatial {
GDCLASS(SmartObject, Spatial)
friend class SmartObjectManager;
protected:
String anim_state;
String anim_finish_state;
bool enabled;
bool teleport;
float distance;
SmartObject();
int state;
int npc_id;
enum {STATE_INIT, STATE_SELECTED, STATE_CLOSE, STATE_ACTIVE, STATE_PROGRESS, STATE_FINISH};
void _notification(int p_what);
static void _bind_methods();
#if TOOLS_ENABLED
Ref<PackedScene> character;
void draw_debug();
void clear_debug();
bool run_in_editor;
String dump_subnodes(Ref<AnimationNode> anode) const;
template<class T>
Ref<T> get_anode(Ref<AnimationRootNode> r) const
{
T *p = Object::cast_to<T>(r.ptr());
return Ref<T>(p);
}
AnimationTree *find_animation_tree(Node *node) const;
String get_animation_list() const;
#endif
public:
void _get_property_list(List<PropertyInfo> *p_list) const;
bool _get(const StringName &p_name, Variant &r_ret) const;
bool _set(const StringName &p_name, const Variant &p_value);
bool get_busy()
{
return state != STATE_INIT;
}
};
class SmartObjectManager: public Node {
GDCLASS(SmartObjectManager, Node)
protected:
void _notification(int p_what);
void update_state(SmartObject *sm);
public:
static void play_animation(Node *cnode, const String &anim);
void arrived(SmartObject *sm, Node *ch);
};
/* Manager of group animation */
class SmartObjectGroup: public Spatial {
GDCLASS(SmartObjectGroup, Spatial)
};

Binary file not shown.

View File

@@ -0,0 +1,39 @@
#include <scene/3d/mesh_instance.h>
#include "terrain_object.h"
List<TerrainObject> TerrainObject::get_objects(const PackedScene *sc, const NodePath &path)
{
int i, j;
List<TerrainObject> obj_list;
if (sc) {
Node *tmp = sc->instance();
Node *obj = tmp->get_node_or_null(path);
if (!obj)
printf("no objects :(\n");
if (obj) {
printf("objects: %d\n", obj->get_child_count());
for (i = 0; i < obj->get_child_count(); i++) {
Node *scobj = obj->get_child(i);
if (!scobj)
continue;
TerrainObject tobj;
printf("lods: %d\n", scobj->get_child_count());
for (j = 0; j < scobj->get_child_count(); j++) {
Node *p = scobj->get_child(j);
MeshInstance *mi = Object::cast_to<MeshInstance>(p);
if (mi) {
Ref<Mesh> data = mi->get_mesh();
if (data.ptr()) {
tobj.add_mesh(data);
}
}
}
if (tobj.lods.size() > 0)
obj_list.push_back(tobj);
}
}
}
return obj_list;
}

View File

@@ -0,0 +1,23 @@
#ifndef TERRAIN_OBJECT_H
#define TERRAIN_OBJECT_H
#include <scene/resources/packed_scene.h>
class TerrainObject {
List<Ref<Mesh> > lods;
public:
inline void add_mesh(Ref<Mesh> &mesh)
{
lods.push_back(mesh);
}
inline Ref<Mesh> get_mesh(int id) const
{
id = id * (id < lods.size()) + (lods.size() - 1) * (id >= lods.size());
return lods[id];
}
inline int get_lod_count() const
{
return lods.size();
}
static List<TerrainObject> get_objects(const PackedScene *sc, const NodePath &path);
};
#endif

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,83 @@
#include "world_terrain_lod.h"
#include "world_chunk.h"
WorldChunk WorldChunk::create(const Vector3i &loc, const Vector<Vector3> &points)
{
int i;
WorldChunk ret;
ret.loc = loc;
ret.aabb.position = loc.to_vec3();
ret.aabb.size = Vector3();
ret.points.append_array(points);
for (i = 0; i < ret.points.size(); i++) {
ret.aabb.expand_to(ret.points[i]);
}
ret.lod = 0;
ret.dirty = true;
return ret;
}
AABB WorldChunk::get_aabb() const
{
return aabb;
}
void WorldChunk::update_lod(const Transform &vxform, const List<WorldTerrainLod> &lod_list)
{
float d = loc.to_vec3().distance_to(vxform.origin);
const List<WorldTerrainLod>::Element *e = lod_list.front();
int i = 0;
int prev_lod = lod;
bool ok = false;
while(e) {
const WorldTerrainLod *l = &e->get();
if (l->in_range(d)) {
lod = i;
ok = true;
break;
}
i++;
e = e->next();
}
if (!ok)
lod = lod_list.size() - 1;
if (prev_lod != lod) {
dirty = true;
printf("lod changed %d -> %d\n", prev_lod, lod);
}
}
void WorldChunk::randomize_transforms(Ref<OpenSimplexNoise> dnoise,
Ref<OpenSimplexNoise> vnoise,
Ref<RandomNumberGenerator> rnd_offset,
float density_scaler)
{
int i, j;
int max_transforms = (int)(points.size() * density_scaler / 4);
transforms.resize(max_transforms);
variance.resize(max_transforms);
int count = 0;
float tpx = vnoise->get_period() * 0.1f;
float dtpx = dnoise->get_period() * 0.15f;
for (i = 0; i < points.size(); i += 4) {
Vector3 u = points[i + 1];
Vector3 v = points[i + 2];
Vector3 w = points[i + 3];
Vector3 pt = points[i];
float density = dnoise->get_noise_3d(pt.x * dtpx, pt.y * dtpx, pt.z * dtpx) * 0.5 + 0.5f;
int mul = (int)(density * density_scaler);
mul += (mul == 0);
for (j = 0; j < mul; j++) {
float r1 = rnd_offset->randf() * 0.5f;
float r2 = rnd_offset->randf() * 0.5f;
float r3 = rnd_offset->randf() * 0.5f;
Vector3 tops = points[i] + r1 * u + r2 * v + r3 * w;
float rot = rnd_offset->randf() * 2.0 * M_PI;
Transform xform = Transform().rotated(Vector3(0, 1, 0), rot);
xform.origin = tops;
transforms.write[count] = xform;
variance.write[count] = vnoise->get_noise_3d(tops.x * tpx, tops.y * tpx, tops.z * tpx) * 0.5 + 0.5f;
count++;
}
}
transforms.resize(count);
variance.resize(count);
}

View File

@@ -0,0 +1,70 @@
#ifndef WORLD_CHUNK_H
#define WORLD_CHUNK_H
#include <core/math/vector3.h>
#include <core/math/aabb.h>
#include <core/vector.h>
#include <core/list.h>
#include <modules/opensimplex/open_simplex_noise.h>
#include <core/math/random_number_generator.h>
#include <modules/voxel/util/math/vector3i.h>
#include "world_terrain_lod.h"
class WorldTerrainLod;
class WorldChunk {
AABB aabb;
Vector3i loc;
Vector<Vector3> points;
Vector<Transform> transforms;
Vector<float> variance;
int lod;
bool dirty;
public:
static WorldChunk create(const Vector3i &loc, const Vector<Vector3> &points);
void randomize_transforms(Ref<OpenSimplexNoise> dnoise,
Ref<OpenSimplexNoise> vnoise,
Ref<RandomNumberGenerator> rnd_offset,
float density_scaler);
AABB get_aabb() const;
void update_lod(const Transform &vxform, const List<WorldTerrainLod> &lod_list);
inline const Vector3 *get_points() const
{
return points.ptr();
}
inline int get_points_count() const
{
return points.size();
}
inline int get_transforms_count() const
{
return transforms.size();
}
inline const Transform *get_transforms() const
{
return transforms.ptr();
}
inline const float *get_variance() const
{
return variance.ptr();
}
inline int get_lod() const
{
return lod;
}
inline bool is_dirty() const
{
return dirty;
}
inline void set_dirty()
{
dirty = true;
}
inline void clear_dirty()
{
dirty = false;
}
inline Vector3i get_loc() const
{
return loc;
}
};
#endif

Binary file not shown.

View File

@@ -0,0 +1,207 @@
#include <core/engine.h>
#include "world_map_data.h"
#include "world_generator.h"
WorldGenerator::WorldGenerator() {
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
// Have one by default in editor
_noise.instance();
_mount_noise.instance();
}
#endif
}
void WorldGenerator::set_noise(Ref<OpenSimplexNoise> noise) {
_noise = noise;
}
Ref<OpenSimplexNoise> WorldGenerator::get_noise() const {
return _noise;
}
void WorldGenerator::set_mount_noise(Ref<OpenSimplexNoise> noise) {
_mount_noise = noise;
}
Ref<OpenSimplexNoise> WorldGenerator::get_mount_noise() const {
return _mount_noise;
}
float WorldGenerator::height_func(int x, int y, int z)
{
float mh = _noise->get_noise_2d((float)x * main_multiplier, (float)z * main_multiplier);
float mth = _mount_noise->get_noise_2d((float)x * mount_multiplier, (float)z * mount_multiplier);
float dmt = 15.0f;
float d1 = initial_spawn_radius + 33.5f * mth;
float h1 = initial_spawn_height + 0.03f * mth;
float d2 = d1 + 20.0f + 0.05 * mth;
float h2 = _range.start + 0.15f * mth;
Vector3 pos((float)x, (float)y, (float)z);
float d = pos.distance_squared_to(initial_spawn);
float factor = (d1 * d1 + 1.0f) / (d + 1.0f);
float th = (d < d1 * d1) ? h1 : h1 * factor + h2 * (1.0f - factor);
float fh = th;
float iheight = CLAMP(fh, _range.start, initial_spawn_height) / _range.height;
float oheight = (mh + mth * 0.01 + 0.1 * mth * CLAMP(y - mount_offset, 0.0f, dmt) / dmt);
float rt = CLAMP(d2 * d2 / (d + 1.0f), 0.0f, 1.0f);
return 0.5f + 0.5f * (iheight * rt + oheight * (1.0f - rt));
}
void WorldGenerator::set_mount_offset(float offt)
{
mount_offset = offt;
}
float WorldGenerator::get_mount_offset() const
{
return mount_offset;
}
void WorldGenerator::set_main_multiplier(float mult)
{
main_multiplier = mult;
}
float WorldGenerator::get_main_multiplier() const
{
return main_multiplier;
}
void WorldGenerator::set_initial_spawn_height(float height)
{
initial_spawn_height = height;
}
float WorldGenerator::get_initial_spawn_height() const
{
return initial_spawn_height;
}
void WorldGenerator::set_density_map(Ref<DensityMap> map)
{
density_map = map;
}
Ref<DensityMap> WorldGenerator::get_density_map() const
{
return density_map;
}
void WorldGenerator::generate_block(VoxelBlockRequest &input) {
ERR_FAIL_COND(_noise.is_null());
#ifdef WORLD_MAP_TESTS
WorldMapData *wmd = WorldMapData::get_singleton();
if (!wmd->tests_run) {
wmd->unit_test();
wmd->tests_run = true;
}
#endif
VoxelBuffer &out_buffer = **input.voxel_buffer;
WorldGenerator::generate(
out_buffer,
[this](int x, int y, int z) { return height_func(x, y, z); },
input.origin_in_voxels, input.lod);
out_buffer.compress_uniform_channels();
}
void WorldGenerator::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_noise", "noise"), &WorldGenerator::set_noise);
ClassDB::bind_method(D_METHOD("get_noise"), &WorldGenerator::get_noise);
ClassDB::bind_method(D_METHOD("set_mount_noise", "noise"), &WorldGenerator::set_mount_noise);
ClassDB::bind_method(D_METHOD("get_mount_noise"), &WorldGenerator::get_mount_noise);
ClassDB::bind_method(D_METHOD("set_channel", "channel"), &WorldGenerator::set_channel);
ClassDB::bind_method(D_METHOD("get_channel"), &WorldGenerator::get_channel);
ClassDB::bind_method(D_METHOD("set_height_start", "start"), &WorldGenerator::set_height_start);
ClassDB::bind_method(D_METHOD("get_height_start"), &WorldGenerator::get_height_start);
ClassDB::bind_method(D_METHOD("set_mount_offset", "offt"), &WorldGenerator::set_mount_offset);
ClassDB::bind_method(D_METHOD("get_mount_offset"), &WorldGenerator::get_mount_offset);
ClassDB::bind_method(D_METHOD("set_main_multiplier", "mult"), &WorldGenerator::set_main_multiplier);
ClassDB::bind_method(D_METHOD("get_main_multiplier"), &WorldGenerator::get_main_multiplier);
ClassDB::bind_method(D_METHOD("set_initial_spawn_height", "height"), &WorldGenerator::set_initial_spawn_height);
ClassDB::bind_method(D_METHOD("get_initial_spawn_height"), &WorldGenerator::get_initial_spawn_height);
ClassDB::bind_method(D_METHOD("set_initial_spawn_radius", "radius"), &WorldGenerator::set_initial_spawn_radius);
ClassDB::bind_method(D_METHOD("get_initial_spawn_radius"), &WorldGenerator::get_initial_spawn_radius);
ClassDB::bind_method(D_METHOD("set_height_range", "range"), &WorldGenerator::set_height_range);
ClassDB::bind_method(D_METHOD("get_height_range"), &WorldGenerator::get_height_range);
ClassDB::bind_method(D_METHOD("set_iso_scale", "scale"), &WorldGenerator::set_iso_scale);
ClassDB::bind_method(D_METHOD("get_iso_scale"), &WorldGenerator::get_iso_scale);
ClassDB::bind_method(D_METHOD("set_density_map", "density_map"), &WorldGenerator::set_density_map);
ClassDB::bind_method(D_METHOD("get_density_map"), &WorldGenerator::get_density_map);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "noise", PROPERTY_HINT_RESOURCE_TYPE, "OpenSimplexNoise"), "set_noise", "get_noise");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mount_noise", PROPERTY_HINT_RESOURCE_TYPE, "OpenSimplexNoise"), "set_mount_noise", "get_mount_noise");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "mount_offset"), "set_mount_offset", "get_mount_offset");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "main_multiplier"), "set_main_multiplier", "get_main_multiplier");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "initial_spawn_height"), "set_initial_spawn_height", "get_initial_spawn_height");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "initial_spawn_radius"), "set_initial_spawn_radius", "get_initial_spawn_radius");
ADD_PROPERTY(PropertyInfo(Variant::INT, "channel", PROPERTY_HINT_ENUM, VoxelBuffer::CHANNEL_ID_HINT_STRING), "set_channel", "get_channel");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "height_start"), "set_height_start", "get_height_start");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "height_range"), "set_height_range", "get_height_range");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "density_map", PROPERTY_HINT_RESOURCE_TYPE, "DensityMap"), "set_density_map", "get_density_map");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "iso_scale"), "set_iso_scale", "get_iso_scale");
}
void WorldGenerator::set_channel(VoxelBuffer::ChannelId channel) {
ERR_FAIL_INDEX(channel, VoxelBuffer::MAX_CHANNELS);
if (_channel != channel) {
_channel = channel;
emit_changed();
}
}
VoxelBuffer::ChannelId WorldGenerator::get_channel() const {
return _channel;
}
int WorldGenerator::get_used_channels_mask() const {
return (1 << _channel);
}
void WorldGenerator::set_height_start(float start) {
_range.start = start;
}
float WorldGenerator::get_height_start() const {
return _range.start;
}
void WorldGenerator::set_height_range(float range) {
_range.height = range;
}
float WorldGenerator::get_height_range() const {
return _range.height;
}
void WorldGenerator::set_initial_spawn_radius(float radius)
{
initial_spawn_radius = radius;
}
float WorldGenerator::get_initial_spawn_radius() const
{
return initial_spawn_radius;
}
void WorldGenerator::set_iso_scale(float iso_scale) {
_iso_scale = iso_scale;
}
float WorldGenerator::get_iso_scale() const {
return _iso_scale;
}

View File

@@ -0,0 +1,141 @@
#ifndef VOXEL_GENERATOR_NOISE_2D_H
#define VOXEL_GENERATOR_NOISE_2D_H
#include <modules/voxel/generators/voxel_generator.h>
#include <modules/opensimplex/open_simplex_noise.h>
#include "density_map.h"
class WorldGenerator : public VoxelGenerator {
GDCLASS(WorldGenerator, VoxelGenerator)
public:
WorldGenerator();
void set_noise(Ref<OpenSimplexNoise> noise);
Ref<OpenSimplexNoise> get_noise() const;
void set_mount_noise(Ref<OpenSimplexNoise> noise);
Ref<OpenSimplexNoise> get_mount_noise() const;
void set_channel(VoxelBuffer::ChannelId channel);
VoxelBuffer::ChannelId get_channel() const;
int get_used_channels_mask() const override;
void set_height_start(float start);
float get_height_start() const;
void set_height_range(float range);
float get_height_range() const;
void set_mount_offset(float offt);
float get_mount_offset() const;
void set_main_multiplier(float mult);
float get_main_multiplier() const;
void set_initial_spawn_height(float height);
float get_initial_spawn_height() const;
void set_initial_spawn_radius(float radius);
float get_initial_spawn_radius() const;
void set_iso_scale(float iso_scale);
float get_iso_scale() const;
void set_density_map(Ref<DensityMap> map);
Ref<DensityMap> get_density_map() const;
void generate_block(VoxelBlockRequest &input) override;
float height_func(int x, int y, int z);
private:
static void _bind_methods();
protected:
template <typename Height_F>
void generate(VoxelBuffer &out_buffer, Height_F height_func, Vector3i origin, int lod) {
const int channel = _channel;
const Vector3i bs = out_buffer.get_size();
bool use_sdf = channel == VoxelBuffer::CHANNEL_SDF;
if (origin.y > get_height_start() + get_height_range()) {
// The bottom of the block is above the highest ground can go (default is air)
return;
}
if (origin.y + (bs.y << lod) < get_height_start()) {
// The top of the block is below the lowest ground can go
out_buffer.clear_channel(_channel, use_sdf ? 0 : _matter_type);
return;
}
const int stride = 1 << lod;
if (use_sdf) {
int gz = origin.z;
for (int z = 0; z < bs.z; ++z, gz += stride) {
int gx = origin.x;
for (int x = 0; x < bs.x; ++x, gx += stride) {
int gy = origin.y;
for (int y = 0; y < bs.y; ++y, gy += stride) {
float h = _range.xform(height_func(gx, gy, gz));
float sdf = _iso_scale * (gy - h);
out_buffer.set_voxel_f(sdf, x, y, z, channel);
}
} // for x
} // for z
} else {
// Blocky
int gz = origin.z;
for (int z = 0; z < bs.z; ++z, gz += stride) {
int gx = origin.x;
for (int x = 0; x < bs.x; ++x, gx += stride) {
// Output is blocky, so we can go for just one sample
float h = _range.xform(height_func(gx, origin.y, gz));
h -= origin.y;
int ih = int(h);
if (ih > 0) {
if (ih > bs.y) {
ih = bs.y;
}
out_buffer.fill_area(_matter_type, Vector3i(x, 0, z), Vector3i(x + 1, ih, z + 1), channel);
}
} // for x
} // for z
} // use_sdf
}
private:
Ref<OpenSimplexNoise> _noise, _mount_noise;
struct Range {
float start = -250;
float height = 500;
inline float xform(float x) const {
return x * height + start;
}
};
float mount_offset = 50.0f;
float main_multiplier = 0.1f;
Vector3 initial_spawn;
float initial_spawn_radius = 150.0f;
float initial_spawn_height = 20.0f;
VoxelBuffer::ChannelId _channel = VoxelBuffer::CHANNEL_SDF;
int _matter_type = 1;
Range _range;
float _iso_scale = 0.1;
float mount_multiplier = 1.5f;
Ref<DensityMap> density_map;
};
#endif // VOXEL_GENERATOR_NOISE_2D_H

Binary file not shown.

View File

@@ -0,0 +1,213 @@
#include <cmath>
#include <core/image.h>
#include <core/math/random_number_generator.h>
#include "world_map_data.h"
static WorldMapData *g_world_map_data = NULL;
WorldMapData *WorldMapData::get_singleton()
{
return g_world_map_data;
}
void WorldMapData::create_singleton()
{
g_world_map_data = memnew(WorldMapData);
}
void WorldMapData::destroy_singleton()
{
memdelete(g_world_map_data);
g_world_map_data = NULL;
}
void WorldMapData::_bind_methods()
{
}
void WorldMapData::add_circle(int x, int y, float radius)
{
struct area a;
int grid_x_min, grid_x_max, grid_x, grid_y_min, grid_y_max, grid_y;
int i;
Vector2i grid_pos;
a.x = x;
a.y = y;
a.radius = radius;
if (last_area >= areas.size())
areas.resize(areas.size() + alloc_count);
areas.write[last_area] = a;
grid_x_min = floorf(((float)x - radius) / (float)grid_size);
grid_x_max = ceilf(((float)x + radius) / (float)grid_size);
grid_y_min = floorf(((float)y - radius) / (float)grid_size);
grid_y_max = ceilf(((float)y + radius) / (float)grid_size);
for (grid_y = grid_y_min; grid_y < grid_y_max + 1; grid_y++)
for (grid_x = grid_x_min; grid_x < grid_x_max + 1; grid_x++) {
grid_pos.x = grid_x;
grid_pos.y = grid_y;
if (!grid.has(grid_pos))
grid[grid_pos] = List<int>();
if (grid[grid_pos].find(last_area) == NULL) {
int oid = get_sorted_index(grid[grid_pos], radius);
int xsize = grid[grid_pos].size();
if (oid == 0)
grid[grid_pos].push_front(last_area);
else if (oid >= xsize)
grid[grid_pos].push_back(last_area);
else {
i = 0;
List<int>::Element *e = grid[grid_pos].front();
while (i != oid && e) {
e = e->next();
i++;
}
if (e)
grid[grid_pos].insert_before(e, last_area);
}
}
}
last_area++;
}
List<int> WorldMapData::get_circles_for_pos(int x, int y)
{
Vector2i grid_pos;
grid_pos.x = x / grid_size;
grid_pos.y = y / grid_size;
if (grid.has(grid_pos))
return grid[grid_pos];
else
return List<int>();
}
bool WorldMapData::in_circle(int id, int x, int y)
{
if (id >= areas.size())
return false;
struct area a = areas[id];
int xx = x - a.x;
int yy = y - a.y;
int rsq = xx * xx + yy * yy;
if ((float)rsq < a.radius * a.radius)
return true;
return false;
}
int WorldMapData::get_sorted_index(const List<int> &items, float radius) const
{
int low = 0;
int high = items.size();
while (low < high) {
int mid = (low + high) >> 1;
if (areas[items[mid]].radius < radius)
low = mid + 1;
else
high = mid;
}
return low;
}
void WorldMapData::clear()
{
last_area = 0;
alloc_count = 1000;
areas.resize(alloc_count);
grid.clear();
}
WorldMapData::WorldMapData()
{
grid_size = 100;
last_area = 0;
alloc_count = 1000;
areas.resize(alloc_count);
#ifdef WORLD_MAP_TESTS
tests_run = false;
#endif
world_x = 2048;
world_y = 2048;
seed = 1038;
}
WorldMapData::~WorldMapData()
{
}
void WorldMapData::build_point_clusters_iteration(Vector2i seed_point, int bound, int count)
{
Ref<RandomNumberGenerator> rnd;
rnd.instance();
rnd->set_seed(seed);
int mind = MIN(bound / count / 4, 1);
int rmax = bound / mind;
if (rmax == 0)
return;
float radius = (float)bound / (float)count;
float radiussq = radius * radius;
List<Vector2i> points;
int n = 0;
int max_steps = 10000;
while (n < count && max_steps-- > 0) {
int x = seed_point.x + rnd->randi() % rmax;
int y = seed_point.y + rnd->randi() % rmax;
if (x >= world_x || y >= world_y)
continue;
bool good = true;
List<Vector2i>::Element *e = points.front();
while (e) {
Vector2i p = e->get();
int dx = x - p.x;
int dy = y - p.y;
if (dx * dx + dy * dy < (int)radiussq) {
good = false;
break;
}
e = e->next();
}
if (good) {
add_circle(x, y, radius);
points.push_back(Vector2i(x, y));
n++;
}
}
}
void WorldMapData::save_debug_image()
{
int i, j;
Ref<Image> image;
HashMap<int, Color> colors;
image.instance();
image->create(2048, 2048, false, Image::FORMAT_RGB8);
image->lock();
Color newcolor(0.1f, 0.2f, 1.0f);
printf("world %d %d\n", world_x, world_y);
for (i = 0; i < image->get_width(); i++)
for (j = 0; j < image->get_height(); j++) {
List<int> data = get_circles_for_pos(i * world_x / 2048, j * world_y / 2048);
while (data.size() > 0) {
int id = data.front()->get();
if (in_circle(id, i * world_x / 2048, j * world_y / 2048)) {
if (!colors.has(id)) {
colors[id] = newcolor;
newcolor.r = (newcolor.r + fmodf(1321.27f * (float)i, 13.0f) / 13.0f) / 1.9f + 0.1f;
newcolor.g = (newcolor.r + newcolor.g + fmodf(5321.23f * (float)i, 10.0f) / 10.0f) / 2.9f + 0.1f;
newcolor.b = (newcolor.r + newcolor.g + newcolor.b + fmodf(1121.13f * (float)i, 11.0f) / 11.0f) / 3.9f + 0.1f;
}
image->set_pixel(i, j, colors[id]);
break;
}
data.pop_front();
}
}
image->unlock();
image->save_png("world_map_test.png");
}
#ifdef WORLD_MAP_TESTS
void WorldMapData::unit_test()
{
int i;
add_circle(100, 100, 50.0f);
add_circle(200, 200, 100.0f);
add_circle(100, 300, 30.0f);
add_circle(300, 100, 30.0f);
for (i = 0; i < 1000; i++) {
int x = (i * 100) % 1000;
int y = (i * 100) / 1000;
add_circle(x, y, 90.0f);
}
save_debug_image();
}
#endif

View File

@@ -0,0 +1,68 @@
#include <core/object.h>
#define WORLD_MAP_TESTS
class WorldMapData: public Object {
GDCLASS(WorldMapData, Object)
public:
WorldMapData();
~WorldMapData();
static WorldMapData *get_singleton();
static void create_singleton();
static void destroy_singleton();
struct area {
int x;
int y;
float radius;
Vector2i to_vec2i() const
{
Vector2i pos;
pos.x = x;
pos.y = y;
return pos;
}
};
void add_circle(int x, int y, float radius);
List<int> get_circles_for_pos(int x, int y);
bool in_circle(int id, int x, int y);
void set_world_size(int x, int y)
{
world_x = x;
world_y = y;
}
#ifdef WORLD_MAP_TESTS
void unit_test();
bool tests_run;
#endif
void save_debug_image();
void clear();
void set_seed(int seed)
{
this->seed = seed;
}
int get_seed() const
{
return seed;
}
void build_point_clusters_iteration(Vector2i seed_point, int bound, int count);
protected:
int get_sorted_index(const List<int> &items, float radius) const;
inline void get_circle(int id, int &x, int &y, float &radius)
{
if (id >= areas.size())
return;
x = areas[id].x;
y = areas[id].y;
radius = areas[id].radius;
}
int grid_size;
int last_area;
int alloc_count;
Vector<struct area> areas;
HashMap<Vector2i, List<int> > grid;
static void _bind_methods();
int world_x;
int world_y;
int seed;
};

Binary file not shown.

View File

@@ -0,0 +1,178 @@
#include <core/hash_map.h>
#include <core/math/vector3.h>
#include <scene/resources/packed_scene.h>
#include <scene/resources/primitive_meshes.h>
#include <scene/3d/mesh_instance.h>
#include "direct_lod_multimesh.h"
#include "world_chunk.h"
#include "terrain_object.h"
#include "world_terrain_lod.h"
void WorldTerrainLod::init_lod(int mind, int maxd,
float density_range)
{
int i;
min_dist = mind;
max_dist = maxd;
this->density_range = density_range;
}
void WorldTerrainLod::update()
{
}
void WorldTerrainLod::create_chunk(const WorldChunk *chunk, const List<TerrainObject> &obj_list)
{
int i, j, id;
if (mesh_h.has(chunk->get_loc()))
return;
int lod_offset = 1 << chunk->get_lod();
int obj_count = obj_list.size();
printf("create_chunk started %d\n", obj_count);
LodMultiMesh3D *mi = memnew(LodMultiMesh3D);
id = 0;
int rcount = chunk->get_transforms_count() / lod_offset;
const Transform *xfptr = chunk->get_transforms();
const float *variances = chunk->get_variance();
Vector<int> ids;
ids.resize(rcount);
printf("ids size: %d rcount: %d\n", ids.size(), rcount);
HashMap<int, List<Transform> > instances;
for (j = 0; j < rcount; j++) {
float variance = variances[j * lod_offset];
int obj_id = (int)(variance * obj_count);
printf("3 %d %d", j, rcount);
ids.write[j] = obj_id;
if (!instances.has(obj_id))
instances[obj_id] = List<Transform>();
instances[obj_id].push_back(xfptr[j * lod_offset]);
printf("6");
}
const List<TerrainObject>::Element *e = obj_list.front();
int count = 0;
while (e) {
printf("obj: %d\n", count);
const TerrainObject *obj = &e->get();
if (!instances.has(count))
instances[count] = List<Transform>();
Ref<Mesh> mesh = obj->get_mesh(chunk->get_lod());
printf("lod: %d mesh: %p, instance count: %d\n", chunk->get_lod(), mesh.ptr(), instances[count].size());
mi->add_mesh(obj->get_mesh(chunk->get_lod()));
mi->set_instance_count(count, instances[count].size());
const List<Transform>::Element *ep = instances[count].front();
int pcount = 0;
while (ep) {
const Transform &pxform = ep->get();
printf("ep %d %d %ls\n", count, pcount, String(pxform.origin).c_str());
mi->set_instance_transform(count, pcount++, pxform);
ep = ep->next();
}
e = e->next();
count++;
}
#if 0
for (j = 0; j < mesh_grass.size(); j++) {
if (!new_points[*org].has(j))
continue;
const List<struct new_point> &nps = new_points[*org][j];
mi->add_mesh(mesh_grass[j]);
mi->set_instance_count(id, nps.size());
const List<struct new_point>::Element *e = nps.front();
int icount = 0;
while(e) {
mi->set_instance_transform(id, icount++, e->get().xform);
e = e->next();
}
id++;
}
#endif
mi->hide();
mi->set_world(NULL);
mi->set_global_transform(Transform(Basis(), chunk->get_loc().to_vec3()));
meshes.push_back(mi);
mesh_h[chunk->get_loc()] = mi;
mi->set_world(gworld.ptr());
mi->show();
}
void WorldTerrainLod::cleanup(const Transform &vxform)
{
if (meshes.size() > 0)
printf("meshes.size() %p %d\n", this, meshes.size());
List<LodMultiMesh3D *>::Element *e = meshes.front();
while(e) {
LodMultiMesh3D *ve = e->get();
List<LodMultiMesh3D *>::Element *ne = e->next();
Vector3 rpos = ve->get_center();
Vector3 vpos = vxform.origin;
Vector3i loc(ve->get_global_transform().origin);
if (!valid_org(vxform, ve->get_global_transform().origin, 0.5f)) {
memfree(ve);
meshes.erase(e);
mesh_h.erase(loc);
}
e = ne;
}
}
static inline bool too_close(const Transform &vxform, const Vector3 &org, float zmin)
{
Vector3 lpoint = vxform.xform_inv(org);
if (-lpoint.z < zmin)
return true;
return false;
}
bool WorldTerrainLod::valid_org(const Transform &vxform, const Vector3 &org, float margin) const
{
bool ret = true;
float view_angle = M_PI / 3.0f;
float view_s = Math::sin(view_angle);
if (too_close(vxform, org, min_dist))
return false;
// if (vxform.origin.distance_squared_to(org) > max_dist * max_dist)
// return false;
//if (min_dist > 0 && vxform.origin.distance_squared_to(org) < min_dist * min_dist)
// return false;
Vector3 lpoint = vxform.xform_inv(org);
/* View is in -Z direction */
if (lpoint.z > -min_dist + margin || lpoint.z < -max_dist - margin)
ret = false;
if (lpoint.x < -view_s * max_dist -8.0f - margin|| lpoint.x > view_s * max_dist + 8.0f + margin)
ret = false;
if (lpoint.y < -36.0f || lpoint.y > 36.0f)
ret = false;
return ret;
}
void WorldTerrainLod::update_visibility(const Transform &vxform)
{
List<LodMultiMesh3D *>::Element *e = meshes.front();
while(e) {
LodMultiMesh3D *mi = e->get();
Transform xform = mi->get_global_transform();
// Vector3 lpoint = vxform.xform_inv(xform.origin);
if (mi->is_visible() && !valid_org(vxform, xform.origin, 1.0f)) {
mi->hide();
mi->set_world(NULL);
} else if (!mi->is_visible() && valid_org(vxform, xform.origin, 0.0f)) {
mi->show();
mi->set_world(gworld.ptr());
}
e = e->next();
}
}
void WorldTerrainLod::set_world(Ref<World> world)
{
if (!world.ptr())
printf("exited world %p\n", gworld.ptr());
gworld = world;
List<LodMultiMesh3D *>::Element *e = meshes.front();
while(e) {
LodMultiMesh3D *mi = e->get();
mi->set_world(gworld.ptr());
e = e->next();
}
if (gworld.ptr())
printf("entered world %p\n", gworld.ptr());
}

View File

@@ -0,0 +1,59 @@
#ifndef WORLD_TERRAIN_LOD_H
#define WORLD_TERRAIN_LOD_H
#include <core/hash_map.h>
#include <core/reference.h>
#include <scene/resources/mesh.h>
#include <scene/resources/world.h>
#include <scene/resources/packed_scene.h>
#include <modules/voxel/util/math/vector3i.h>
#include "world_chunk.h"
#include "terrain_object.h"
#include "new_point.h"
#include "direct_lod_multimesh.h"
class LodMultiMesh3D;
class WorldChunk;
class WorldTerrainLod {
public:
List<LodMultiMesh3D *> meshes;
private:
HashMap<Vector3i, LodMultiMesh3D *, Vector3iHasher> mesh_h;
int min_dist, max_dist;
Ref<World> gworld;
float density_range = 3.0f;
public:
void set_world(Ref<World> world);
void init_lod(int mind, int maxd,
float density_range);
void create_chunk(const WorldChunk *chunk, const List<TerrainObject> &obj_list);
void update();
inline bool has(const Vector3i *loc) const
{
return mesh_h.has(*loc);
}
void cleanup(const Transform &vxform);
bool valid_org(const Transform &vxform, const Vector3 &org, float margin) const;
void update_visibility(const Transform &vxform);
inline float get_density_range() const
{
return density_range;
}
inline bool in_range(float d) const
{
if (d >= (float)min_dist && d < (float)max_dist)
return true;
return false;
}
inline void erase(const Vector3i &key)
{
if (mesh_h.has(key)) {
LodMultiMesh3D *v = mesh_h[key];
v->hide();
v->set_world(NULL);
memfree(v);
meshes.erase(v);
mesh_h.erase(key);
}
}
};
#endif

Binary file not shown.