Files
academy2/modules/world/spawner.cpp
2021-10-30 02:16:06 +03:00

179 lines
4.6 KiB
C++

#include <cassert>
#include <scene/main/viewport.h>
#include <scene/3d/camera.h>
#include "spawner.h"
static Spawner *g_spawner_data = NULL;
Spawner * Spawner::get_singleton()
{
return g_spawner_data;
}
void Spawner::create_singleton()
{
g_spawner_data = memnew(Spawner);
}
void Spawner::destroy_singleton()
{
memdelete(g_spawner_data);
}
void Spawner::_bind_methods()
{
ClassDB::bind_method(D_METHOD("add_scene", "name", "scene"), &Spawner::add_scene);
ClassDB::bind_method(D_METHOD("remove_scene", "name"), &Spawner::remove_scene);
ClassDB::bind_method(D_METHOD("place_scene", "name", "place"), &Spawner::place_scene);
ClassDB::bind_method(D_METHOD("place_scene_relative", "name", "parent", "place"), &Spawner::place_scene_relative);
ClassDB::bind_method(D_METHOD("update_view", "node", "distance"), &Spawner::update_view);
}
void Spawner::add_scene(const StringName &name, Ref<PackedScene> scene)
{
lock.lock();
if (!scenes.has(name)) {
scenes[name] = scene;
positions[name] = PoolVector<struct spawn_object>();
}
lock.unlock();
}
void Spawner::remove_scene(const StringName &name)
{
int i;
lock.lock();
PoolVector<struct spawn_object> pos = positions[name];
for (i = 0; i < pos.size(); i++) {
if (!pos[i].active)
continue;
Object *po = ObjectDB::get_instance(pos[i].instance);
if (po) {
Node *pn = Object::cast_to<Node>(po);
if (pn)
pn->queue_delete();
}
pos.write()[i].active = false;
}
positions.erase(name);
scenes.erase(name);
lock.unlock();
}
void Spawner::place_scene(const StringName &name, const Transform &place)
{
lock.lock();
if (scenes.has(name)) {
struct spawn_object obj;
obj.xform = place;
obj.active = false;
obj.pooled = false;
obj.instance = -1;
PoolVector<struct spawn_object> pos = positions[name];
pos.resize(pos.size() + 1);
pos.write()[pos.size() - 1] = obj;
positions[name] = pos;
force_update = true;
}
lock.unlock();
}
void Spawner::place_scene_relative(const StringName &name, Node *parent, const Transform &place)
{
if (!parent)
return;
lock.lock();
if (scenes.has(name)) {
Node *n = scenes[name]->instance();
Spatial *sp = Object::cast_to<Spatial>(n);
sp->set_transform(place);
parent->call_deferred("add_child", sp);
}
lock.unlock();
}
void Spawner::update_view(Node *node, float distance)
{
if (!node)
return;
Viewport *view = node->get_viewport();
if (!view)
return;
Camera *cam = view->get_camera();
if (!cam)
return;
Transform cam_xform = cam->get_global_transform();
Vector3 org = cam_xform.origin;
if (org.distance_squared_to(last_org) < 64.0f && !force_update)
return;
last_org = org;
const String *key;
lock.lock();
for (key = positions.next(NULL); key; key = positions.next(key)) {
int i;
PoolVector<struct spawn_object> pos = positions[*key];
for (i = 0; i < pos.size(); i++) {
if (!pos[i].active) {
Transform x = pos[i].xform;
float d = org.distance_squared_to(x.origin);
if (d < distance * distance && pos[i].instance < 0) {
int j;
Node *pn = NULL;
bool pooling = false;
for (j = 0; j < pos.size(); j++) {
if (j == i)
continue;
if (!pos[j].active && pos[j].instance >= 0 && pos[j].pooled) {
pos.write()[i].instance = pos[j].instance;
pos.write()[j].instance = -1;
pos.write()[j].pooled = false;
Object *po = ObjectDB::get_instance(pos[i].instance);
if (po) {
pn = Object::cast_to<Node>(po);
if (pn) {
pooling = true;
break;
}
}
}
}
if (!pn)
pn = scenes[*key]->instance();
if (pn) {
Spatial *sp = Object::cast_to<Spatial>(pn);
if (sp) {
if (!pooling)
view->add_child(sp);
sp->set_global_transform(x);
pos.write()[i].instance = sp->get_instance_id();
pos.write()[i].active = true;
pos.write()[i].pooled = false;
sp->show();
} else
memfree(pn);
}
}
} else {
Transform x = pos[i].xform;
float d = org.distance_squared_to(x.origin);
if (d > distance * distance + 2500.0f) {
Object *po = ObjectDB::get_instance(pos[i].instance);
if (po) {
Node *pn = Object::cast_to<Node>(po);
if (pn)
pn->queue_delete();
}
pos.write()[i].active = false;
pos.write()[i].instance = -1;
pos.write()[i].pooled = false;
} else if (d > distance * distance + 600.0f) {
Object *po = ObjectDB::get_instance(pos[i].instance);
if (po) {
Spatial *pn = Object::cast_to<Spatial>(po);
pn->hide();
}
pos.write()[i].active = false;
pos.write()[i].pooled = true;
}
}
}
positions[*key] = pos;
}
force_update = false;
lock.unlock();
}