Skip to content

Commit

Permalink
Infinite Worlds Multiplayer Seamless Chunk System
Browse files Browse the repository at this point in the history
Used with BlockyGame to generate infinite 3D three dirt voxel blocks in thickness flat planes that can be used to build on top of including massive multiplayer games: Zylann/voxelgame#101 + Zylann/voxelgame@2147d81
  • Loading branch information
WithinAmnesia authored Feb 23, 2024
1 parent 1fb1bc7 commit 6eeb73e
Show file tree
Hide file tree
Showing 2 changed files with 209 additions and 0 deletions.
203 changes: 203 additions & 0 deletions world_generator_test3.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
#tool
extends VoxelGeneratorScript

const Structure = preload("./structure.gd")
const TreeGenerator = preload("./tree_generator.gd")
const HeightmapCurve = preload("./heightmap_curve.tres")

# TODO Don't hardcode, get by name from library somehow
const AIR = 0
const DIRT = 1
const GRASS = 2
const WATER_FULL = 14
const WATER_TOP = 13
const LOG = 4
const LEAVES = 25
const TALL_GRASS = 8
const DEAD_SHRUB = 26
#const STONE = 8

const _CHANNEL = VoxelBuffer.CHANNEL_TYPE

const _moore_dirs = [
Vector3(-1, 0, -1),
Vector3(0, 0, -1),
Vector3(1, 0, -1),
Vector3(-1, 0, 0),
Vector3(1, 0, 0),
Vector3(-1, 0, 1),
Vector3(0, 0, 1),
Vector3(1, 0, 1)
]


var _tree_structures := []

var _heightmap_min_y := 1 #int(HeightmapCurve.min_value)
var _heightmap_max_y := 1 #int(HeightmapCurve.max_value)
var _heightmap_range := 0
var _heightmap_noise := 0 #FastNoiseLite.new()
var _trees_min_y := 0
var _trees_max_y := 0


func _init():
# TODO Even this must be based on a seed, but I'm lazy
var tree_generator = TreeGenerator.new()
tree_generator.log_type = LOG
tree_generator.leaves_type = LEAVES
for i in 16:
var s = tree_generator.generate()
_tree_structures.append(s)

var tallest_tree_height = 0
for structure in _tree_structures:
var h = int(structure.voxels.get_size().y)
if tallest_tree_height < h:
tallest_tree_height = h
_trees_min_y = _heightmap_min_y
_trees_max_y = _heightmap_max_y + tallest_tree_height

#_heightmap_noise.seed = 131183
#_heightmap_noise.frequency = 1.0 / 128.0
#_heightmap_noise.fractal_octaves = 4

# IMPORTANT
# If we don't do this `Curve` could bake itself when interpolated,
# and this causes crashes when used in multiple threads
HeightmapCurve.bake()


func _get_used_channels_mask() -> int:
return 1 << _CHANNEL


func _generate_block(buffer: VoxelBuffer, origin_in_voxels: Vector3i, lod: int):
# TODO There is an issue doing this, need to investigate why because it should be supported
# Saves from this demo used 8-bit, which is no longer the default
# buffer.set_channel_depth(_CHANNEL, VoxelBuffer.DEPTH_8_BIT)

# Assuming input is cubic in our use case (it doesn't have to be!)
var block_size := int(buffer.get_size().x)
var oy := origin_in_voxels.y
# TODO This hardcodes a cubic block size of 16, find a non-ugly way...
# Dividing is a false friend because of negative values
var chunk_pos := Vector3(
origin_in_voxels.x >> 4,
origin_in_voxels.y >> 4,
origin_in_voxels.z >> 4)

_heightmap_range = _heightmap_max_y - _heightmap_min_y

# Ground

if origin_in_voxels.y > _heightmap_max_y:
buffer.fill(AIR, _CHANNEL)

elif origin_in_voxels.y + block_size < _heightmap_min_y:
buffer.fill(DIRT, _CHANNEL)

else:
var rng := RandomNumberGenerator.new()
rng.seed = _get_chunk_seed_2d(chunk_pos)

var gx : int
var gz := origin_in_voxels.z

for z in block_size:
gx = origin_in_voxels.x

for x in block_size:
var height := 1
var relative_height := 1

# Dirt and grass
if relative_height > block_size:
buffer.fill_area(DIRT,
Vector3(x, 0, z), Vector3(x + 1, block_size, z + 1), _CHANNEL)
elif relative_height > 0:
buffer.fill_area(DIRT,
Vector3(x, 0, z), Vector3(x + 1, relative_height, z + 1), _CHANNEL)
if height >= 0:
buffer.set_voxel(DIRT, x, relative_height +1, z, _CHANNEL)
if relative_height < block_size and rng.randf() > 0.1:
var foliage = DIRT # TALL_GRASS
#if # rng.randf() < 0.1:
# foliage = GRASS # DEAD_SHRUB
buffer.set_voxel(DIRT, x, relative_height, z, _CHANNEL)

# Water
if height < 0 and oy < 0:
var start_relative_height := 0
if relative_height > 0:
start_relative_height = relative_height
buffer.fill_area(WATER_FULL,
Vector3(x, start_relative_height, z),
Vector3(x + 1, block_size, z + 1), _CHANNEL)
if oy + block_size == 0:
# Surface block
buffer.set_voxel(WATER_TOP, x, block_size - 1, z, _CHANNEL)

gx += 1

gz += 1

# Trees

# if origin_in_voxels.y <= _trees_max_y and origin_in_voxels.y + block_size >= _trees_min_y:
# var voxel_tool := buffer.get_voxel_tool()
# var structure_instances := []
#
# _get_tree_instances_in_chunk(chunk_pos, origin_in_voxels, block_size, structure_instances)
#
# # Relative to current block
# var block_aabb := AABB(Vector3(), buffer.get_size() + Vector3i(1, 1, 1))

# for dir in _moore_dirs:
# var ncpos : Vector3 = (chunk_pos + dir).round()
# _get_tree_instances_in_chunk(ncpos, origin_in_voxels, block_size, structure_instances)

# for structure_instance in structure_instances:
# var pos : Vector3 = structure_instance[0]
# var structure : Structure = structure_instance[1]
# var lower_corner_pos := pos - structure.offset
# var aabb := AABB(lower_corner_pos, structure.voxels.get_size() + Vector3i(1, 1, 1))

# if aabb.intersects(block_aabb):
# voxel_tool.paste_masked(lower_corner_pos,
# structure.voxels, 1 << VoxelBuffer.CHANNEL_TYPE,
# # Masking
# VoxelBuffer.CHANNEL_TYPE, AIR)

# buffer.compress_uniform_channels()


#func _get_tree_instances_in_chunk(
# cpos: Vector3, offset: Vector3, chunk_size: int, tree_instances: Array):
#
# var rng := RandomNumberGenerator.new()
# rng.seed = _get_chunk_seed_2d(cpos)

# for i in 4:
# var pos := Vector3(rng.randi() % chunk_size, 0, rng.randi() % chunk_size)
# pos += cpos * chunk_size
# pos.y = 1
#
# if pos.y > 0:
# pos -= offset
# var si := rng.randi() % len(_tree_structures)
# var structure : Structure = _tree_structures[si]
# tree_instances.append([pos.round(), structure])


#static func get_chunk_seed(cpos: Vector3) -> int:
# return cpos.x ^ (13 * int(cpos.y)) ^ (31 * int(cpos.z))


static func _get_chunk_seed_2d(cpos: Vector3) -> int:
return int(cpos.x) ^ (31 * int(cpos.z))


#func _get_height_at(x: int, z: int) -> int:
# var t = 0.5 + 0.5 * _heightmap_noise.get_noise_2d(x, z)
# return int(HeightmapCurve.sample_baked(t))
6 changes: 6 additions & 0 deletions world_generator_test3.tres
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[gd_resource type="VoxelGeneratorScript" load_steps=2 format=3 uid="uid://b7s8gfvrf0gc4"]

[ext_resource type="Script" path="res://blocky_game/generator/world_generator_test3.gd" id="1"]

[resource]
script = ExtResource("1")

0 comments on commit 6eeb73e

Please sign in to comment.