Initial commit
This commit is contained in:
BIN
modules/world/.characters.cpp.swp
Normal file
BIN
modules/world/.characters.cpp.swp
Normal file
Binary file not shown.
BIN
modules/world/.road_map.cpp.swp
Normal file
BIN
modules/world/.road_map.cpp.swp
Normal file
Binary file not shown.
BIN
modules/world/.road_map.h.swp
Normal file
BIN
modules/world/.road_map.h.swp
Normal file
Binary file not shown.
BIN
modules/world/.smart_object.cpp.swp
Normal file
BIN
modules/world/.smart_object.cpp.swp
Normal file
Binary file not shown.
BIN
modules/world/.smart_object.h.swp
Normal file
BIN
modules/world/.smart_object.h.swp
Normal file
Binary file not shown.
18
modules/world/SCsub
Normal file
18
modules/world/SCsub
Normal 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'])
|
||||
|
||||
BIN
modules/world/__pycache__/config.cpython-37.pyc
Normal file
BIN
modules/world/__pycache__/config.cpython-37.pyc
Normal file
Binary file not shown.
BIN
modules/world/__pycache__/config.cpython-38.pyc
Normal file
BIN
modules/world/__pycache__/config.cpython-38.pyc
Normal file
Binary file not shown.
BIN
modules/world/__pycache__/config.cpython-39.pyc
Normal file
BIN
modules/world/__pycache__/config.cpython-39.pyc
Normal file
Binary file not shown.
820
modules/world/characters.cpp
Normal file
820
modules/world/characters.cpp
Normal 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;
|
||||
}
|
||||
|
||||
60
modules/world/characters.h
Normal file
60
modules/world/characters.h
Normal 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;
|
||||
};
|
||||
|
||||
BIN
modules/world/characters.x11.opt.tools.64.o
Normal file
BIN
modules/world/characters.x11.opt.tools.64.o
Normal file
Binary file not shown.
8
modules/world/config.py
Normal file
8
modules/world/config.py
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
def can_build(env, platform):
|
||||
return True
|
||||
|
||||
|
||||
def configure(env):
|
||||
pass
|
||||
|
||||
232
modules/world/density_map.cpp
Normal file
232
modules/world/density_map.cpp
Normal 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(®ions[i], num_counties, counties);
|
||||
}
|
||||
for (i = 0; i < counties.size(); i++) {
|
||||
split_area(&counties[i], num_cities, cities);
|
||||
}
|
||||
for (i = 0; i < cities.size(); i++) {
|
||||
split_area(&cities[i], num_districts, districts);
|
||||
}
|
||||
WorldMapData *wmd = WorldMapData::get_singleton();
|
||||
wmd->clear();
|
||||
wmd->set_world_size(world_x_size, world_z_size);
|
||||
#if 0
|
||||
for (i = 0; i < clusters.size(); i++) {
|
||||
wmd->add_circle(clusters[i].pos.x, clusters[i].pos.y, clusters[i].radius);
|
||||
printf("cluster: %d x: %d y: %d radius: %f\n", i, clusters[i].pos.x, clusters[i].pos.y, clusters[i].radius);
|
||||
}
|
||||
#endif
|
||||
for (i = 0; i < districts.size(); i++) {
|
||||
wmd->add_circle(districts[i].pos.x, districts[i].pos.y, districts[i].radius);
|
||||
printf("districts: %d x: %d y: %d radius: %f\n", i, districts[i].pos.x, districts[i].pos.y, districts[i].radius);
|
||||
}
|
||||
wmd->save_debug_image();
|
||||
printf("num_clusters: %d - %d\n", (int)num_clusters, (int)clusters.size());
|
||||
printf("num_regions: %d - %d\n", (int)num_regions, (int)regions.size());
|
||||
printf("num_counties: %d - %d\n", (int)num_counties, (int)counties.size());
|
||||
printf("num_cities: %d - %d\n", (int)num_cities, (int)cities.size());
|
||||
printf("num_districts: %d - %d\n", (int)num_districts, (int)districts.size());
|
||||
}
|
||||
|
||||
void DensityMap::split_area(struct area *area, int num_split, List<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()
|
||||
{
|
||||
}
|
||||
56
modules/world/density_map.h
Normal file
56
modules/world/density_map.h
Normal 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
|
||||
BIN
modules/world/density_map.x11.opt.tools.64.o
Normal file
BIN
modules/world/density_map.x11.opt.tools.64.o
Normal file
Binary file not shown.
171
modules/world/direct_lod_multimesh.cpp
Normal file
171
modules/world/direct_lod_multimesh.cpp
Normal 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;
|
||||
}
|
||||
|
||||
68
modules/world/direct_lod_multimesh.h
Normal file
68
modules/world/direct_lod_multimesh.h
Normal 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
|
||||
BIN
modules/world/direct_lod_multimesh.x11.opt.tools.64.o
Normal file
BIN
modules/world/direct_lod_multimesh.x11.opt.tools.64.o
Normal file
Binary file not shown.
BIN
modules/world/direct_lod_multimesh.x11.tools.64.o
Normal file
BIN
modules/world/direct_lod_multimesh.x11.tools.64.o
Normal file
Binary file not shown.
14
modules/world/impostor.cpp
Normal file
14
modules/world/impostor.cpp
Normal 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
19
modules/world/impostor.h
Normal 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();
|
||||
};
|
||||
|
||||
BIN
modules/world/impostor.x11.opt.tools.64.o
Normal file
BIN
modules/world/impostor.x11.opt.tools.64.o
Normal file
Binary file not shown.
113
modules/world/mesher/mesher.cpp
Normal file
113
modules/world/mesher/mesher.cpp
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
54
modules/world/mesher/mesher.h
Normal file
54
modules/world/mesher/mesher.h
Normal 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);
|
||||
}
|
||||
};
|
||||
|
||||
BIN
modules/world/mesher/mesher.x11.opt.tools.64.o
Normal file
BIN
modules/world/mesher/mesher.x11.opt.tools.64.o
Normal file
Binary file not shown.
8
modules/world/new_point.h
Normal file
8
modules/world/new_point.h
Normal 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
|
||||
2
modules/world/new_world.cpp
Normal file
2
modules/world/new_world.cpp
Normal file
@@ -0,0 +1,2 @@
|
||||
#include "new_world.h"
|
||||
|
||||
199
modules/world/new_world.h
Normal file
199
modules/world/new_world.h
Normal 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;
|
||||
};
|
||||
|
||||
BIN
modules/world/new_world.x11.opt.tools.64.o
Normal file
BIN
modules/world/new_world.x11.opt.tools.64.o
Normal file
Binary file not shown.
35
modules/world/register_types.cpp
Normal file
35
modules/world/register_types.cpp
Normal 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();
|
||||
}
|
||||
|
||||
2
modules/world/register_types.h
Normal file
2
modules/world/register_types.h
Normal file
@@ -0,0 +1,2 @@
|
||||
void register_world_types();
|
||||
void unregister_world_types();
|
||||
BIN
modules/world/register_types.x11.opt.tools.64.o
Normal file
BIN
modules/world/register_types.x11.opt.tools.64.o
Normal file
Binary file not shown.
BIN
modules/world/register_types.x11.tools.64.o
Normal file
BIN
modules/world/register_types.x11.tools.64.o
Normal file
Binary file not shown.
33
modules/world/road_map.cpp
Normal file
33
modules/world/road_map.cpp
Normal 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
35
modules/world/road_map.h
Normal 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;
|
||||
};
|
||||
|
||||
|
||||
BIN
modules/world/road_map.x11.opt.tools.64.o
Normal file
BIN
modules/world/road_map.x11.opt.tools.64.o
Normal file
Binary file not shown.
371
modules/world/smart_object.cpp
Normal file
371
modules/world/smart_object.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
61
modules/world/smart_object.h
Normal file
61
modules/world/smart_object.h
Normal 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)
|
||||
};
|
||||
BIN
modules/world/smart_object.x11.opt.tools.64.o
Normal file
BIN
modules/world/smart_object.x11.opt.tools.64.o
Normal file
Binary file not shown.
39
modules/world/terrain_object.cpp
Normal file
39
modules/world/terrain_object.cpp
Normal 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;
|
||||
}
|
||||
|
||||
23
modules/world/terrain_object.h
Normal file
23
modules/world/terrain_object.h
Normal 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
|
||||
|
||||
BIN
modules/world/terrain_object.x11.opt.tools.64.o
Normal file
BIN
modules/world/terrain_object.x11.opt.tools.64.o
Normal file
Binary file not shown.
BIN
modules/world/world.x11.opt.tools.64.o
Normal file
BIN
modules/world/world.x11.opt.tools.64.o
Normal file
Binary file not shown.
BIN
modules/world/world.x11.tools.64.o
Normal file
BIN
modules/world/world.x11.tools.64.o
Normal file
Binary file not shown.
83
modules/world/world_chunk.cpp
Normal file
83
modules/world/world_chunk.cpp
Normal 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);
|
||||
}
|
||||
|
||||
70
modules/world/world_chunk.h
Normal file
70
modules/world/world_chunk.h
Normal 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
|
||||
|
||||
BIN
modules/world/world_chunk.x11.opt.tools.64.o
Normal file
BIN
modules/world/world_chunk.x11.opt.tools.64.o
Normal file
Binary file not shown.
207
modules/world/world_generator.cpp
Normal file
207
modules/world/world_generator.cpp
Normal 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;
|
||||
}
|
||||
|
||||
141
modules/world/world_generator.h
Normal file
141
modules/world/world_generator.h
Normal 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
|
||||
BIN
modules/world/world_generator.x11.opt.tools.64.o
Normal file
BIN
modules/world/world_generator.x11.opt.tools.64.o
Normal file
Binary file not shown.
213
modules/world/world_map_data.cpp
Normal file
213
modules/world/world_map_data.cpp
Normal 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
|
||||
68
modules/world/world_map_data.h
Normal file
68
modules/world/world_map_data.h
Normal 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;
|
||||
};
|
||||
|
||||
BIN
modules/world/world_map_data.x11.opt.tools.64.o
Normal file
BIN
modules/world/world_map_data.x11.opt.tools.64.o
Normal file
Binary file not shown.
178
modules/world/world_terrain_lod.cpp
Normal file
178
modules/world/world_terrain_lod.cpp
Normal 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());
|
||||
}
|
||||
|
||||
59
modules/world/world_terrain_lod.h
Normal file
59
modules/world/world_terrain_lod.h
Normal 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
|
||||
|
||||
BIN
modules/world/world_terrain_lod.x11.opt.tools.64.o
Normal file
BIN
modules/world/world_terrain_lod.x11.opt.tools.64.o
Normal file
Binary file not shown.
Reference in New Issue
Block a user