Now TerrainLights are stored inside chunks (they are also automatically saved with them). Also changed TerrainLight's api to makes use of Vector3i.

This commit is contained in:
Relintai 2025-02-11 00:38:48 +01:00
parent eb3ed4fa20
commit 583298dbfe
9 changed files with 249 additions and 113 deletions

View File

@ -1208,7 +1208,8 @@ void PropMesher::bake_lights(MeshInstance *node, Vector<Ref<TerrainLight>> &ligh
for (int i = 0; i < lights.size(); ++i) {
Ref<TerrainLight> light = lights.get(i);
Vector3 lightDir = light->get_world_position() - vertex;
Vector3 light_pos = light->get_world_data_position();
Vector3 lightDir = light_pos - vertex;
float dist2 = lightDir.dot(lightDir);
//inverse sqrt

View File

@ -31,22 +31,31 @@
#include "terrain_light.h"
_FORCE_INLINE_ int TerrainLight::get_world_position_x() const {
return _world_position_x;
bool TerrainLight::get_has_owner_chunk() const {
return _has_owner_chunk;
}
_FORCE_INLINE_ int TerrainLight::get_world_position_y() const {
return _world_position_y;
void TerrainLight::set_has_owner_chunk(const bool p_value) {
_has_owner_chunk = p_value;
}
_FORCE_INLINE_ int TerrainLight::get_world_position_z() const {
return _world_position_z;
Vector2i TerrainLight::get_owner_chunk_position() const {
return _owner_chunk_position;
}
Vector3 TerrainLight::get_world_position() {
return Vector3(_world_position_x, _world_position_y, _world_position_z);
void TerrainLight::set_owner_chunk_position(const Vector2i &p_owner_chunk_position) {
_owner_chunk_position = p_owner_chunk_position;
}
void TerrainLight::set_world_position(const int x, const int y, const int z) {
_world_position_x = x;
_world_position_y = y;
_world_position_z = z;
Vector3i TerrainLight::get_world_data_position() const {
return _world_data_position;
}
void TerrainLight::set_world_data_position(const Vector3i &p_world_data_position) {
if (_world_data_position == p_world_data_position) {
return;
}
_world_data_position = p_world_data_position;
emit_signal("light_moved", Ref<TerrainLight>(this));
}
real_t TerrainLight::get_range() const {
@ -98,17 +107,6 @@ void TerrainLight::set_specular(const real_t value) {
_specular = value;
}
#ifndef DISABLE_DEPRECATED
bool TerrainLight::_set(const StringName &p_name, const Variant &p_value) {
// Convert to range
if (p_name == "light_size") {
set_range(p_value);
}
return false;
}
#endif
TerrainLight::TerrainLight() {
_range = 0;
_attenuation = 0;
@ -122,10 +120,19 @@ TerrainLight::~TerrainLight() {
}
void TerrainLight::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_world_position_x"), &TerrainLight::get_world_position_x);
ClassDB::bind_method(D_METHOD("get_world_position_y"), &TerrainLight::get_world_position_y);
ClassDB::bind_method(D_METHOD("get_world_position_z"), &TerrainLight::get_world_position_z);
ClassDB::bind_method(D_METHOD("set_world_position", "x", "y", "z"), &TerrainLight::set_world_position);
ADD_SIGNAL(MethodInfo("light_moved", PropertyInfo(Variant::OBJECT, "light", PROPERTY_HINT_RESOURCE_TYPE, "TerrainLight")));
ClassDB::bind_method(D_METHOD("get_has_owner_chunk"), &TerrainLight::get_has_owner_chunk);
ClassDB::bind_method(D_METHOD("set_has_owner_chunk", "value"), &TerrainLight::set_has_owner_chunk);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "has_owner_chunk"), "set_has_owner_chunk", "get_has_owner_chunk");
ClassDB::bind_method(D_METHOD("get_owner_chunk_position"), &TerrainLight::get_owner_chunk_position);
ClassDB::bind_method(D_METHOD("set_owner_chunk_position", "world_data_position"), &TerrainLight::set_owner_chunk_position);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "owner_chunk_position"), "set_owner_chunk_position", "get_owner_chunk_position");
ClassDB::bind_method(D_METHOD("get_world_data_position"), &TerrainLight::get_world_data_position);
ClassDB::bind_method(D_METHOD("set_world_data_position", "world_data_position"), &TerrainLight::set_world_data_position);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3I, "world_data_position"), "set_world_data_position", "get_world_data_position");
ClassDB::bind_method(D_METHOD("get_range"), &TerrainLight::get_range);
ClassDB::bind_method(D_METHOD("set_range", "value"), &TerrainLight::set_range);

View File

@ -40,14 +40,14 @@ class TerrainLight : public Reference {
GDCLASS(TerrainLight, Reference);
public:
int get_world_position_x() const;
int get_world_position_y() const;
int get_world_position_z() const;
Vector3 get_world_position();
void set_world_position(const int x, const int y, const int z);
bool get_has_owner_chunk() const;
void set_has_owner_chunk(const bool p_value);
Vector3 get_position();
void set_position(const Vector3 &pos);
Vector2i get_owner_chunk_position() const;
void set_owner_chunk_position(const Vector2i &p_owner_chunk_position);
Vector3i get_world_data_position() const;
void set_world_data_position(const Vector3i &p_world_data_position);
real_t get_range() const;
void set_range(const real_t value);
@ -74,20 +74,13 @@ public:
~TerrainLight();
private:
#ifndef DISABLE_DEPRECATED
bool _set(const StringName &p_name, const Variant &p_value);
#endif
static void _bind_methods();
private:
int _chunk_position_x;
int _chunk_position_y;
int _chunk_position_z;
bool _has_owner_chunk;
Vector2i _owner_chunk_position;
int _world_position_x;
int _world_position_y;
int _world_position_z;
Vector3i _world_data_position;
real_t _range;
real_t _attenuation;

View File

@ -626,7 +626,8 @@ void TerrainMesher::bake_lights(MeshInstance *node, Vector<Ref<TerrainLight>> &l
for (int i = 0; i < lights.size(); ++i) {
Ref<TerrainLight> light = lights.get(i);
Vector3 lightDir = light->get_world_position() - vertex;
Vector3 light_world_position = light->get_world_data_position();
Vector3 lightDir = light_world_position - vertex;
float dist2 = lightDir.dot(lightDir);
//inverse sqrt

View File

@ -679,8 +679,10 @@ void TerrainChunkDefault::draw_debug_voxel_lights() {
for (int i = 0; i < _lights.size(); ++i) {
Ref<TerrainLight> v = _lights[i];
int pos_x = v->get_world_position_x() - (_size_x * _position_x);
int pos_z = v->get_world_position_z() - (_size_z * _position_z);
Vector3i light_pos = v->get_world_data_position();
int pos_x = light_pos.x - (_size_x * _position_x);
int pos_z = light_pos.z - (_size_z * _position_z);
draw_cross_voxels_fill(Vector3(pos_x, 0, pos_z), 1.0);
}
@ -760,9 +762,11 @@ void TerrainChunkDefault::_bake_light(Ref<TerrainLight> light) {
Color color = light->get_color();
int size = light->get_range();
int local_x = light->get_world_position_x() - (_position_x * _size_x);
int local_y = light->get_world_position_y();
int local_z = light->get_world_position_z() - (_position_z * _size_z);
Vector3i light_pos = light->get_world_data_position();
int local_x = light_pos.x - (_position_x * _size_x);
int local_y = light_pos.y;
int local_z = light_pos.z - (_position_z * _size_z);
ERR_FAIL_COND(size < 0);

View File

@ -661,6 +661,113 @@ _FORCE_INLINE_ int TerrainChunk::get_data_size() const {
return _data_size_x * _data_size_z;
}
//Lights
void TerrainChunk::light_add(Ref<TerrainLight> p_light) {
if (!p_light.is_valid()) {
return;
}
p_light->set_has_owner_chunk(true);
p_light->set_owner_chunk_position(Vector2i(_position_x, _position_z));
p_light->connect("light_moved", this, "_on_light_moved");
_lights.push_back(p_light);
TerrainWorld *world = get_voxel_world();
if (ObjectDB::instance_validate(world)) {
world->world_light_added(p_light);
}
}
bool TerrainChunk::light_remove(Ref<TerrainLight> p_light) {
if (!p_light.is_valid()) {
return false;
}
if (_lights.erase(p_light)) {
p_light->set_has_owner_chunk(false);
p_light->disconnect("light_moved", this, "_on_light_moved");
TerrainWorld *world = get_voxel_world();
if (ObjectDB::instance_validate(world)) {
world->world_light_removed(p_light);
}
return true;
}
return false;
}
bool TerrainChunk::light_has(const Ref<TerrainLight> &p_light) {
return _lights.find(p_light) != -1;
}
Ref<TerrainLight> TerrainChunk::light_get_index(const int index) {
ERR_FAIL_INDEX_V(index, _lights.size(), Ref<TerrainLight>());
return _lights.get(index);
}
void TerrainChunk::light_remove_index(const int index) {
ERR_FAIL_INDEX(index, _lights.size());
Ref<TerrainLight> light = _lights[index];
TerrainWorld *world = get_voxel_world();
if (ObjectDB::instance_validate(world)) {
world->world_light_removed(light);
}
}
int TerrainChunk::light_get_count() const {
return _lights.size();
}
void TerrainChunk::lights_clear() {
TerrainWorld *world = get_voxel_world();
if (!ObjectDB::instance_validate(world)) {
world = NULL;
}
for (int i = 0; i < _lights.size(); ++i) {
Ref<TerrainLight> light = _lights[i];
if (!light.is_valid()) {
continue;
}
light->set_has_owner_chunk(false);
if (world) {
world->world_light_removed(light);
}
}
_lights.clear();
}
Vector<Variant> TerrainChunk::lights_get() {
VARIANT_ARRAY_GET(_lights);
}
void TerrainChunk::lights_set(const Vector<Variant> &chunks) {
lights_clear();
for (int i = 0; i < chunks.size(); ++i) {
Ref<TerrainLight> light = Ref<TerrainLight>(chunks[i]);
light_add(light);
}
}
void TerrainChunk::_on_light_moved(const Ref<TerrainLight> &p_light) {
TerrainWorld *world = get_voxel_world();
if (ObjectDB::instance_validate(world)) {
world->world_light_moved(p_light);
}
}
//Terra Structures
Ref<TerrainStructure> TerrainChunk::voxel_structure_get(const int index) const {
@ -1295,6 +1402,8 @@ TerrainChunk::~TerrainChunk() {
_colliders.clear();
_jobs.clear();
_lights.clear();
}
void TerrainChunk::_enter_tree() {
@ -1654,6 +1763,23 @@ void TerrainChunk::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_data_index", "x", "z"), &TerrainChunk::get_data_index);
ClassDB::bind_method(D_METHOD("get_data_size"), &TerrainChunk::get_data_size);
// Lights
ClassDB::bind_method(D_METHOD("light_add", "light"), &TerrainChunk::light_add);
ClassDB::bind_method(D_METHOD("light_remove", "light"), &TerrainChunk::light_remove);
ClassDB::bind_method(D_METHOD("light_has", "light"), &TerrainChunk::light_has);
ClassDB::bind_method(D_METHOD("light_get_index", "index"), &TerrainChunk::light_get_index);
ClassDB::bind_method(D_METHOD("light_remove_index", "index"), &TerrainChunk::light_remove_index);
ClassDB::bind_method(D_METHOD("light_get_count"), &TerrainChunk::light_get_count);
ClassDB::bind_method(D_METHOD("lights_clear"), &TerrainChunk::lights_clear);
ClassDB::bind_method(D_METHOD("lights_get"), &TerrainChunk::lights_get);
ClassDB::bind_method(D_METHOD("lights_set", "chunks"), &TerrainChunk::lights_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "lights", PROPERTY_HINT_NONE, "23/20:TerrainLight", PROPERTY_USAGE_DEFAULT, "TerrainLight"), "lights_set", "lights_get");
ClassDB::bind_method(D_METHOD("_on_light_moved"), &TerrainChunk::_on_light_moved);
// Structures
ClassDB::bind_method(D_METHOD("voxel_structure_get", "index"), &TerrainChunk::voxel_structure_get);
ClassDB::bind_method(D_METHOD("voxel_structure_add", "structure"), &TerrainChunk::voxel_structure_add);
ClassDB::bind_method(D_METHOD("voxel_structure_remove", "structure"), &TerrainChunk::voxel_structure_remove);

View File

@ -217,6 +217,21 @@ public:
int get_data_index(const int x, const int z) const;
int get_data_size() const;
//Lights
void light_add(Ref<TerrainLight> p_light);
bool light_remove(Ref<TerrainLight> p_light);
bool light_has(const Ref<TerrainLight> &p_light);
Ref<TerrainLight> light_get_index(const int index);
void light_remove_index(const int index);
int light_get_count() const;
void lights_clear();
Vector<Variant> lights_get();
void lights_set(const Vector<Variant> &chunks);
void _on_light_moved(const Ref<TerrainLight> &p_light);
//Terra Structures
Ref<TerrainStructure> voxel_structure_get(const int index) const;
void voxel_structure_add(const Ref<TerrainStructure> &structure);
@ -421,6 +436,8 @@ protected:
Ref<TerrainLibrary> _library;
Vector<Ref<TerrainLight>> _lights;
Vector<Ref<TerrainStructure>> _voxel_structures;
#ifdef MODULE_PROPS_ENABLED

View File

@ -650,7 +650,7 @@ void TerrainWorld::prop_add(Transform transform, const Ref<PropData> &prop, cons
Ref<TerrainLight> light;
light.instance();
light->set_world_position(wp.x / get_voxel_scale(), wp.y / get_voxel_scale(), wp.z / get_voxel_scale());
light->set_world_data_position(Vector3i(wp.x / get_voxel_scale(), wp.y / get_voxel_scale(), wp.z / get_voxel_scale()));
light->set_range(light_data->get_light_range());
light->set_attenuation(light_data->get_light_attenuation());
light->set_color(light_data->get_light_color());
@ -691,8 +691,44 @@ void TerrainWorld::prop_add(Transform transform, const Ref<PropData> &prop, cons
//Lights
void TerrainWorld::light_add(const Ref<TerrainLight> &light) {
_lights.push_back(light);
if (!light.is_valid()) {
return;
}
Vector3i light_world_data_position = light->get_world_data_position();
Ref<TerrainChunk> chunk = get_or_create_chunk_at_world_data_position(Vector2i(light_world_data_position.x, light_world_data_position.z));
ERR_FAIL_COND(!chunk.is_valid());
chunk->light_add(light);
}
void TerrainWorld::light_remove(const Ref<TerrainLight> &light) {
if (!light.is_valid()) {
return;
}
Vector3i light_world_data_position = light->get_world_data_position();
Ref<TerrainChunk> chunk = get_chunk_at_world_data_position(Vector2i(light_world_data_position.x, light_world_data_position.z));
if (!chunk.is_valid()) {
return;
}
chunk->light_remove(light);
}
void TerrainWorld::lights_clear() {
for (int i = 0; i < _chunks_vector.size(); ++i) {
Ref<TerrainChunk> chunk = _chunks_vector[i];
if (chunk.is_valid()) {
chunk->lights_clear();
}
}
}
void TerrainWorld::world_light_added(const Ref<TerrainLight> &light) {
for (int i = 0; i < _chunks_vector.size(); ++i) {
Ref<TerrainChunk> chunk = _chunks_vector[i];
@ -701,16 +737,7 @@ void TerrainWorld::light_add(const Ref<TerrainLight> &light) {
}
}
}
Ref<TerrainLight> TerrainWorld::light_get(const int index) {
ERR_FAIL_INDEX_V(index, _lights.size(), Ref<TerrainLight>());
return _lights.get(index);
}
void TerrainWorld::light_remove(const int index) {
ERR_FAIL_INDEX(index, _lights.size());
Ref<TerrainLight> light = _lights[index];
void TerrainWorld::world_light_removed(const Ref<TerrainLight> &light) {
for (int i = 0; i < _chunks_vector.size(); ++i) {
Ref<TerrainChunk> chunk = _chunks_vector[i];
@ -719,40 +746,10 @@ void TerrainWorld::light_remove(const int index) {
}
}
}
int TerrainWorld::light_get_count() const {
return _lights.size();
}
void TerrainWorld::lights_clear() {
for (int i = 0; i < _lights.size(); ++i) {
Ref<TerrainLight> light = _lights[i];
if (!light.is_valid()) {
continue;
}
for (int j = 0; j < _chunks_vector.size(); ++j) {
Ref<TerrainChunk> chunk = _chunks_vector[j];
if (chunk.is_valid()) {
chunk->world_light_removed(light);
}
}
}
_lights.clear();
}
Vector<Variant> TerrainWorld::lights_get() {
VARIANT_ARRAY_GET(_lights);
}
void TerrainWorld::lights_set(const Vector<Variant> &chunks) {
lights_clear();
for (int i = 0; i < chunks.size(); ++i) {
Ref<TerrainLight> light = Ref<TerrainLight>(chunks[i]);
light_add(light);
}
void TerrainWorld::world_light_moved(const Ref<TerrainLight> &light) {
// TODO better implementation
light_remove(light);
light_add(light);
}
uint8_t TerrainWorld::get_voxel_at_world_position(const Vector3 &world_position, const int channel_index) {
@ -1281,8 +1278,6 @@ TerrainWorld ::~TerrainWorld() {
_generation_queue.clear();
_generating.clear();
_lights.clear();
}
void TerrainWorld::_generate_chunk(Ref<TerrainChunk> chunk) {
@ -1583,15 +1578,10 @@ void TerrainWorld::_bind_methods() {
#endif
//Lights
ClassDB::bind_method(D_METHOD("light_add", "light"), &TerrainWorld::light_add);
ClassDB::bind_method(D_METHOD("light_get", "index"), &TerrainWorld::light_get);
ClassDB::bind_method(D_METHOD("light_remove", "index"), &TerrainWorld::light_remove);
ClassDB::bind_method(D_METHOD("light_get_count"), &TerrainWorld::light_get_count);
ClassDB::bind_method(D_METHOD("light_add", "chunk"), &TerrainWorld::light_add);
ClassDB::bind_method(D_METHOD("light_remove", "chunk"), &TerrainWorld::light_remove);
ClassDB::bind_method(D_METHOD("lights_clear"), &TerrainWorld::lights_clear);
ClassDB::bind_method(D_METHOD("lights_get"), &TerrainWorld::lights_get);
ClassDB::bind_method(D_METHOD("lights_set", "chunks"), &TerrainWorld::lights_set);
ClassDB::bind_method(D_METHOD("get_voxel_at_world_position", "world_position", "channel_index"), &TerrainWorld::get_voxel_at_world_position);
ClassDB::bind_method(D_METHOD("set_voxel_at_world_position", "world_position", "data", "channel_index", "rebuild"), &TerrainWorld::set_voxel_at_world_position, DEFVAL(true));
ClassDB::bind_method(D_METHOD("get_chunk_at_world_position", "world_position"), &TerrainWorld::get_chunk_at_world_position);

View File

@ -181,13 +181,12 @@ public:
//Lights
void light_add(const Ref<TerrainLight> &light);
Ref<TerrainLight> light_get(const int index);
void light_remove(const int index);
int light_get_count() const;
void light_remove(const Ref<TerrainLight> &light);
void lights_clear();
Vector<Variant> lights_get();
void lights_set(const Vector<Variant> &chunks);
void world_light_added(const Ref<TerrainLight> &light);
void world_light_removed(const Ref<TerrainLight> &light);
void world_light_moved(const Ref<TerrainLight> &light);
//Helpers
uint8_t get_voxel_at_world_position(const Vector3 &world_position, const int channel_index);
@ -281,8 +280,6 @@ private:
Vector<Ref<TerrainChunk>> _generating;
int _max_frame_chunk_build_steps;
int _num_frame_chunk_build_steps;
Vector<Ref<TerrainLight>> _lights;
};
_FORCE_INLINE_ bool operator==(const TerrainWorld::IntPos &a, const TerrainWorld::IntPos &b) {