Added random tick API for blocky voxels
parent
b95f47b018
commit
0cdf5ea550
|
@ -2,6 +2,10 @@
|
|||
#include "../terrain/voxel_map.h"
|
||||
#include "../terrain/voxel_terrain.h"
|
||||
#include "../util/voxel_raycast.h"
|
||||
#include <core/func_ref.h>
|
||||
|
||||
VoxelToolTerrain::VoxelToolTerrain() {
|
||||
}
|
||||
|
||||
VoxelToolTerrain::VoxelToolTerrain(VoxelTerrain *terrain, Ref<VoxelMap> map) {
|
||||
ERR_FAIL_COND(terrain == nullptr);
|
||||
|
@ -16,7 +20,6 @@ bool VoxelToolTerrain::is_area_editable(const Rect3i &box) const {
|
|||
}
|
||||
|
||||
Ref<VoxelRaycastResult> VoxelToolTerrain::raycast(Vector3 pos, Vector3 dir, float max_distance) {
|
||||
|
||||
// TODO Transform input if the terrain is rotated (in the future it can be made a Spatial node)
|
||||
|
||||
struct RaycastPredicate {
|
||||
|
@ -90,3 +93,75 @@ void VoxelToolTerrain::_post_edit(const Rect3i &box) {
|
|||
ERR_FAIL_COND(_terrain == nullptr);
|
||||
_terrain->make_area_dirty(box);
|
||||
}
|
||||
|
||||
// Executes a function on random voxels in the provided area, using the type channel.
|
||||
// This allows to implement slow "natural" cellular automata behavior, as can be seen in Minecraft.
|
||||
void VoxelToolTerrain::run_blocky_random_tick(AABB voxel_area, int voxel_count, Ref<FuncRef> callback, int batch_count) const {
|
||||
ERR_FAIL_COND(_terrain == nullptr);
|
||||
ERR_FAIL_COND(_terrain->get_voxel_library().is_null());
|
||||
ERR_FAIL_COND(callback.is_null());
|
||||
ERR_FAIL_COND(batch_count <= 0);
|
||||
ERR_FAIL_COND(voxel_count < 0);
|
||||
|
||||
if (voxel_count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const VoxelLibrary &lib = **_terrain->get_voxel_library();
|
||||
|
||||
const Vector3i min_pos = Vector3i(voxel_area.position);
|
||||
const Vector3i max_pos = min_pos + Vector3i(voxel_area.size);
|
||||
|
||||
const Vector3i min_block_pos = _map->voxel_to_block(min_pos);
|
||||
const Vector3i max_block_pos = _map->voxel_to_block(max_pos);
|
||||
const Vector3i block_area_size = max_block_pos - min_block_pos;
|
||||
|
||||
const int block_count = voxel_count / batch_count;
|
||||
const int bs_mask = _map->get_block_size_mask();
|
||||
|
||||
// Choose blocks at random
|
||||
for (int bi = 0; bi < block_count; ++bi) {
|
||||
const Vector3i block_pos = min_block_pos + Vector3i(
|
||||
Math::rand() % block_area_size.x,
|
||||
Math::rand() % block_area_size.y,
|
||||
Math::rand() % block_area_size.z);
|
||||
const Vector3i block_origin = _map->block_to_voxel(block_pos);
|
||||
|
||||
const VoxelBlock *block = _map->get_block(block_pos);
|
||||
if (block != nullptr) {
|
||||
// Choose a bunch of voxels at random within the block.
|
||||
// Batching this way improves performance a little by reducing block lookups.
|
||||
for (int vi = 0; vi < batch_count; ++vi) {
|
||||
const Vector3i rpos(
|
||||
Math::rand() & bs_mask,
|
||||
Math::rand() & bs_mask,
|
||||
Math::rand() & bs_mask);
|
||||
|
||||
const unsigned int v = block->voxels->get_voxel(rpos, VoxelBuffer::CHANNEL_TYPE);
|
||||
|
||||
if (lib.has_voxel(v)) {
|
||||
const Voxel &vt = lib.get_voxel_const(v);
|
||||
|
||||
if (vt.is_random_tickable()) {
|
||||
const Variant vpos = (rpos + block_origin).to_vec3();
|
||||
const Variant vv = v;
|
||||
const Variant *args[2];
|
||||
args[0] = &vpos;
|
||||
args[1] = &vv;
|
||||
Variant::CallError error;
|
||||
callback->call_func(args, 2, error);
|
||||
// TODO I would really like to know what's the correct way to report such errors...
|
||||
// Examples I found in the engine are inconsistent
|
||||
ERR_FAIL_COND(error.error != Variant::CallError::CALL_OK);
|
||||
// Return if it fails, we don't want an error spam
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelToolTerrain::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("run_blocky_random_tick", "voxel_count", "callback", "batch_count"),
|
||||
&VoxelToolTerrain::run_blocky_random_tick, DEFVAL(16));
|
||||
}
|
||||
|
|
|
@ -5,15 +5,19 @@
|
|||
|
||||
class VoxelTerrain;
|
||||
class VoxelMap;
|
||||
class FuncRef;
|
||||
|
||||
class VoxelToolTerrain : public VoxelTool {
|
||||
GDCLASS(VoxelToolTerrain, VoxelTool)
|
||||
public:
|
||||
VoxelToolTerrain();
|
||||
VoxelToolTerrain(VoxelTerrain *terrain, Ref<VoxelMap> map);
|
||||
|
||||
bool is_area_editable(const Rect3i &box) const override;
|
||||
Ref<VoxelRaycastResult> raycast(Vector3 pos, Vector3 dir, float max_distance) override;
|
||||
|
||||
void run_blocky_random_tick(AABB voxel_area, int voxel_count, Ref<FuncRef> callback, int block_batch_count) const;
|
||||
|
||||
protected:
|
||||
int _get_voxel(Vector3i pos) override;
|
||||
float _get_voxel_f(Vector3i pos) override;
|
||||
|
@ -22,6 +26,8 @@ protected:
|
|||
void _post_edit(const Rect3i &box) override;
|
||||
|
||||
private:
|
||||
static void _bind_methods();
|
||||
|
||||
VoxelTerrain *_terrain = nullptr;
|
||||
Ref<VoxelMap> _map;
|
||||
};
|
||||
|
|
|
@ -352,6 +352,10 @@ Ref<Voxel> Voxel::set_cube_geometry(float sy) {
|
|||
return Ref<Voxel>(this);
|
||||
}
|
||||
|
||||
void Voxel::set_random_tickable(bool rt) {
|
||||
_random_tickable = rt;
|
||||
}
|
||||
|
||||
void Voxel::set_cube_uv_side(int side, Vector2 tile_pos) {
|
||||
_cube_tiles[side] = tile_pos;
|
||||
// TODO Better have a dirty flag, otherwise UVs will be needlessly updated at least 6 times everytime a Voxel resource is loaded!
|
||||
|
@ -445,6 +449,9 @@ void Voxel::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_transparent", "transparent"), &Voxel::set_transparent);
|
||||
ClassDB::bind_method(D_METHOD("is_transparent"), &Voxel::is_transparent);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_random_tickable", "rt"), &Voxel::set_random_tickable);
|
||||
ClassDB::bind_method(D_METHOD("is_random_tickable"), &Voxel::is_random_tickable);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_material_id", "id"), &Voxel::set_material_id);
|
||||
ClassDB::bind_method(D_METHOD("get_material_id"), &Voxel::get_material_id);
|
||||
|
||||
|
@ -461,6 +468,7 @@ void Voxel::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "voxel_name"), "set_voxel_name", "get_voxel_name");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transparent"), "set_transparent", "is_transparent");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "random_tickable"), "set_random_tickable", "is_random_tickable");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "material_id"), "set_material_id", "get_material_id");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry_type", PROPERTY_HINT_ENUM, "None,Cube,CustomMesh"), "set_geometry_type", "get_geometry_type");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "custom_mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_custom_mesh", "get_custom_mesh");
|
||||
|
|
|
@ -47,6 +47,9 @@ public:
|
|||
void set_custom_mesh(Ref<Mesh> mesh);
|
||||
Ref<Mesh> get_custom_mesh() const { return _custom_mesh; }
|
||||
|
||||
void set_random_tickable(bool rt);
|
||||
inline bool is_random_tickable() const { return _random_tickable; }
|
||||
|
||||
//-------------------------------------------
|
||||
// Built-in geometry generators
|
||||
|
||||
|
@ -121,6 +124,7 @@ private:
|
|||
Ref<Mesh> _custom_mesh;
|
||||
std::vector<AABB> _collision_aabbs;
|
||||
bool _contributes_to_ao = false;
|
||||
bool _random_tickable = false;
|
||||
|
||||
FixedArray<uint32_t, Cube::SIDE_COUNT> _side_pattern_index;
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "register_types.h"
|
||||
#include "edition/voxel_tool.h"
|
||||
#include "edition/voxel_tool_terrain.h"
|
||||
#include "editor/editor_plugin.h"
|
||||
#include "editor/voxel_graph_editor_plugin.h"
|
||||
#include "generators/graph/voxel_generator_graph.h"
|
||||
|
@ -58,6 +59,7 @@ void register_voxel_types() {
|
|||
ClassDB::register_class<VoxelBoxMover>();
|
||||
ClassDB::register_class<VoxelRaycastResult>();
|
||||
ClassDB::register_class<VoxelTool>();
|
||||
ClassDB::register_class<VoxelToolTerrain>();
|
||||
ClassDB::register_class<VoxelBlockSerializer>();
|
||||
|
||||
// Meshers
|
||||
|
|
Loading…
Reference in New Issue