Skip to content

Commit

Permalink
Finish chunked dynamic collisions
Browse files Browse the repository at this point in the history
  • Loading branch information
lw64 committed Apr 5, 2024
1 parent 90d78e9 commit 4556f8f
Show file tree
Hide file tree
Showing 10 changed files with 302 additions and 476 deletions.
28 changes: 22 additions & 6 deletions src/base_chunk.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,39 @@
#ifndef BASECHUNK_CLASS_H
#define BASECHUNK_CLASS_H

#include "chunk_manager.h"
#include <godot_cpp/templates/vector.hpp>

using namespace godot;

class ChunkManager;

class BaseChunk : public Object {
GDCLASS(BaseChunk, Object);

public:
// Constants
static inline const char *__class__ = "Terrain3DBaseChunk";

private:
Vector2i _position;
protected:
Vector2i _position = Vector2i(0, 0);
uint _size = 0;
ChunkManager *_manager = nullptr;

public:
virtual void refill() = 0;
virtual void set_enabled(bool enabled) = 0;
void set_position(Vector2i p_position) { _position = p_position; };
Vector2i get_position() { return _position; }
virtual void refill() {}
virtual void set_enabled(bool enabled) {}
virtual void set_position(Vector2i p_position) { _position = p_position; };
Vector2i get_position() { return _position; }

BaseChunk() {}
BaseChunk(ChunkManager *p_manager, uint p_size) {
_manager = p_manager;
_size = p_size;
}

protected:
static void _bind_methods() {}
};

#endif
196 changes: 105 additions & 91 deletions src/chunk_manager.cpp
Original file line number Diff line number Diff line change
@@ -1,58 +1,70 @@
// Copyright © 2024 Lorenz Wildberg

#include "chunk_manager.h"
#include "logger.h"
#include "terrain_3d_storage.h"

void ChunkManager::move(Vector3 p_camera_position) {
Vector2i pos_snapped = _snap_position(p_camera_position);
Vector2i snapped_delta = pos_snapped - _old_snapped_pos;
Vector2i pos_snapped = _snap_position(p_camera_position);
Vector2i snapped_delta = pos_snapped - _old_snapped_pos;
_old_snapped_pos = pos_snapped;

Array new_array = Array();
new_array.resize(_chunk_count);
new_array.fill(nullptr);

// iterate old array and fill new array
for (int i = 0; i < _chunks_width; i++) {
for (int j = 0; j < _chunks_width; j++) {
int index = i * _chunks_width + j;

// offset to the old camera position
Vector2i old_array_position = Vector2i(i, j) + snapped_delta;

// index for the current chunk in the new array in the old array
int old_index = old_array_position.x * _chunks_width + old_array_position.y;

if (Object::cast_to<BaseChunk>(_active_chunks[old_index]) == nullptr) {
continue;
}

bool position_still_exists_in_old =
_chunk_count > old_array_position.x && old_array_position.x >= 0 &&
_chunk_count > old_array_position.y && old_array_position.y >= 0;

// in world coordinates
Vector2i chunk_location = pos_snapped + Vector2i((i - _chunk_count * 0.5) * _chunk_size, (j - _chunk_count * 0.5) * _chunk_size);

bool too_far = Vector2(chunk_location).distance_to(Vector2(p_camera_position.x, p_camera_position.z)) > _distance;

if (position_still_exists_in_old && !too_far) {
// move chunk to new location but don't refill
new_array[index] = _active_chunks[old_index];
Object::cast_to<BaseChunk>(new_array[index])->set_position(chunk_location);
_active_chunks[old_index] = nullptr;
} else if (!position_still_exists_in_old || too_far) {
// disable chunk
Object::cast_to<BaseChunk>(_active_chunks[old_index])->set_enabled(false);
_inactive_chunks.push_back(_active_chunks[old_index]);
_active_chunks[old_index] = nullptr;

if (!too_far) {
// create new chunk from inactive
BaseChunk* new_chunk = Object::cast_to<BaseChunk>(_inactive_chunks.pop_back());
new_chunk->set_position(chunk_location);
new_chunk->set_enabled(true);
new_array[index] = new_chunk;
}
new_array.fill(Variant(1));

// 3 times:
for (int t = 1; t <= 3; t++) {
for (int i = 0; i < _chunks_width; i++) {
for (int j = 0; j < _chunks_width; j++) {
int index = i * _chunks_width + j;

// offset to the old camera position
Vector2i old_array_position = Vector2i(i, j) + snapped_delta;

// index for the current chunk in the new array in the old array
int old_index = old_array_position.x * _chunks_width + old_array_position.y;

bool position_still_exists_in_old =
_chunks_width > old_array_position.x && old_array_position.x >= 0 &&
_chunks_width > old_array_position.y && old_array_position.y >= 0;

// in world coordinates
Vector2i chunk_location = pos_snapped + Vector2i((i - _chunks_width * 0.5) * _chunk_size, (j - _chunks_width * 0.5) * _chunk_size);

bool too_far = Vector2(chunk_location).distance_to(Vector2(p_camera_position.x, p_camera_position.z)) > _distance;

switch (t) {
case 1:
if (position_still_exists_in_old && !too_far) {
// move chunk to new location but don't refill
new_array[index] = _active_chunks[old_index];
if (_active_chunks[old_index] == Variant(1)) {
new_array[index] = Object::cast_to<BaseChunk>(_inactive_chunks.pop_back());
Object::cast_to<BaseChunk>(new_array[index])->set_enabled(true);
}
Object::cast_to<BaseChunk>(new_array[index])->set_position(chunk_location);
_active_chunks[old_index] = Variant(1);
}
break;
case 2:
// disable remaining chunks
if (_active_chunks[index] != Variant(1)) {
Object::cast_to<BaseChunk>(_active_chunks[index])->set_enabled(false);
_inactive_chunks.push_back(_active_chunks[index]);
_active_chunks[index] = Variant(1);
}
break;
case 3:
if (!too_far && !position_still_exists_in_old) {
// create new chunks from inactive
BaseChunk *new_chunk = Object::cast_to<BaseChunk>(_inactive_chunks.pop_back());
new_chunk->set_position(chunk_location);
new_chunk->set_enabled(true);
new_array[index] = new_chunk;
}
break;
}
}
}
}
Expand All @@ -61,64 +73,66 @@ void ChunkManager::move(Vector3 p_camera_position) {
}

Vector2i ChunkManager::_snap_position(Vector3 p_position) {
Vector2 camera_position = Vector2(p_position.x, p_position.z);
Vector2 camera_position = Vector2(p_position.x, p_position.z);
Vector2i pos_snapped;
float positive_camera_position_x = (camera_position.x < 0.0) ? -camera_position.x : camera_position.x;
if (Math::fract(positive_camera_position_x / _chunk_size) >= 0.5) {
if (camera_position.x < 0.0) {
pos_snapped.x = Math::floor(camera_position.x / _chunk_size) * _chunk_size;
} else {
pos_snapped.x = Math::ceil(camera_position.x / _chunk_size) * _chunk_size;
}
} else {
if (camera_position.x < 0.0) {
pos_snapped.x = Math::floor(camera_position.x / _chunk_size) * _chunk_size;
} else {
pos_snapped.x = Math::floor(camera_position.x / _chunk_size) * _chunk_size;
}
}
float positive_camera_position_y = (camera_position.y < 0.0) ? -camera_position.y : camera_position.y;
if (Math::fract(positive_camera_position_y / _chunk_size) >= 0.5) {
if (camera_position.y < 0.0) {
pos_snapped.y = Math::floor(camera_position.y / _chunk_size) * _chunk_size;
} else {
pos_snapped.y = Math::ceil(camera_position.y / _chunk_size) * _chunk_size;
}
} else {
if (camera_position.y < 0.0) {
pos_snapped.y = Math::ceil(camera_position.y / _chunk_size) * _chunk_size;
} else {
pos_snapped.y = Math::floor(camera_position.y / _chunk_size) * _chunk_size;
}
}
return pos_snapped;
if (Math::fract(positive_camera_position_x / _chunk_size) >= 0.5) {
if (camera_position.x < 0.0) {
pos_snapped.x = Math::floor(camera_position.x / _chunk_size) * _chunk_size;
} else {
pos_snapped.x = Math::ceil(camera_position.x / _chunk_size) * _chunk_size;
}
} else {
if (camera_position.x < 0.0) {
pos_snapped.x = Math::floor(camera_position.x / _chunk_size) * _chunk_size;
} else {
pos_snapped.x = Math::floor(camera_position.x / _chunk_size) * _chunk_size;
}
}
float positive_camera_position_y = (camera_position.y < 0.0) ? -camera_position.y : camera_position.y;
if (Math::fract(positive_camera_position_y / _chunk_size) >= 0.5) {
if (camera_position.y < 0.0) {
pos_snapped.y = Math::floor(camera_position.y / _chunk_size) * _chunk_size;
} else {
pos_snapped.y = Math::ceil(camera_position.y / _chunk_size) * _chunk_size;
}
} else {
if (camera_position.y < 0.0) {
pos_snapped.y = Math::ceil(camera_position.y / _chunk_size) * _chunk_size;
} else {
pos_snapped.y = Math::floor(camera_position.y / _chunk_size) * _chunk_size;
}
}
return pos_snapped;
}

void ChunkManager::_build() {
_destroy();
_active_chunks.resize(_chunk_count);
_inactive_chunks.resize(_chunk_count);
for (int i = 0; i < _chunk_count; i++) {
_inactive_chunks[i] = create_chunk();
_active_chunks[i] = nullptr;
}
_destroy();
_active_chunks.resize(_chunk_count);
_inactive_chunks.resize(_chunk_count * 2);
for (int i = 0; i < _chunk_count; i++) {
_inactive_chunks[i] = create_chunk();
_inactive_chunks[_chunk_count + i] = create_chunk();
_active_chunks[i] = Variant(1);
}
LOG(INFO, "test6", _inactive_chunks);
}

void ChunkManager::_destroy() {
_active_chunks.clear();
_inactive_chunks.clear();
_active_chunks.clear();
_inactive_chunks.clear();
}

void ChunkManager::set_distance(float p_distance) {
_distance = p_distance;
_distance = p_distance;

_chunks_width = ceil((_distance + 1.0) / _chunk_size) * 2.0;
_chunk_count = _chunks_width * _chunks_width;
_chunks_width = ceil((_distance + 1.0) / _chunk_size) * 2.0;
_chunk_count = _chunks_width * _chunks_width;

_build();
_build();
}

void ChunkManager::set_chunk_size(float p_size) {
_chunk_size = p_size;
_build();
void ChunkManager::set_chunk_size(uint p_size) {
_chunk_size = p_size;
_build();
}
50 changes: 31 additions & 19 deletions src/chunk_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,47 @@
#define CHUNKMANAGER_CLASS_H

#include "base_chunk.h"
#include <godot_cpp/classes/node3d.hpp>

using namespace godot;

class ChunkManager {
class BaseChunk;

class ChunkManager : public Node3D {
GDCLASS(ChunkManager, Node3D);

public:
// Constants
static inline const char *__class__ = "Terrain3DChunkManager";

protected:
uint _chunk_size = 16;

private:
float _chunk_size;
float _distance;
float _chunks_width;
int _chunk_count;
Array _active_chunks;
Array _old_chunks;
Array _inactive_chunks;
Vector2i _old_snapped_pos;

Vector2i _snap_position(Vector3 p_position);
void _destroy();
void _build();
float _distance = 64.0;
uint _chunks_width = 0;
uint _chunk_count = 0;
Array _active_chunks = Array();
Array _old_chunks = Array();
Array _inactive_chunks = Array();
Vector2i _old_snapped_pos = Vector2i(0, 0);

Vector2i _snap_position(Vector3 p_position);
void _destroy();
void _build();

public:
void set_chunk_size(float p_size);
float get_chunk_size() { return _chunk_size; }
void set_distance(float p_distance);
float get_distance() { return _distance; }
void move(Vector3 p_camera_position);
virtual BaseChunk* create_chunk() = 0;
ChunkManager() {}

void set_chunk_size(uint p_size);
uint get_chunk_size() { return _chunk_size; }
void set_distance(float p_distance);
float get_distance() { return _distance; }
void move(Vector3 p_camera_position);

protected:
virtual BaseChunk *create_chunk() { return nullptr; }
static void _bind_methods() {}
};

#endif
38 changes: 38 additions & 0 deletions src/collision_chunk.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright © 2024 Lorenz Wildberg

#include "collision_chunk.h"
#include "logger.h"
#include <godot_cpp/classes/height_map_shape3d.hpp>

CollisionChunk::CollisionChunk(CollisionChunkManager *p_manager, uint p_size) :
BaseChunk(p_manager, p_size) {
_col_shape = memnew(CollisionShape3D);
_col_shape->set_name("CollisionShape3D");
_col_shape->set_visible(true);
Ref<HeightMapShape3D> hshape;
hshape.instantiate();
hshape->set_map_width(p_size);
hshape->set_map_depth(p_size);
_col_shape->set_shape(hshape);
((CollisionChunkManager *)_manager)->_body->add_child(_col_shape);
_col_shape->set_owner(((CollisionChunkManager *)_manager)->_body);
LOG(INFO, "new chunk");
}

CollisionChunk::~CollisionChunk() {
((CollisionChunkManager *)_manager)->_body->remove_child(_col_shape);
memdelete(_col_shape);
}

void CollisionChunk::refill() {
//
}

void CollisionChunk::set_position(Vector2i p_position) {
BaseChunk::set_position(p_position);
_col_shape->set_position(Vector3(p_position.x, 0.0, p_position.y));
}

void CollisionChunk::set_enabled(bool enabled) {
_col_shape->set_visible(enabled);
}
Loading

0 comments on commit 4556f8f

Please sign in to comment.