Skip to content

Instantly share code, notes, and snippets.

@WithinAmnesia
Created February 22, 2024 19:33
Show Gist options
  • Save WithinAmnesia/025f8d1ae7b674fc2c2d4381e9e45c8b to your computer and use it in GitHub Desktop.
Save WithinAmnesia/025f8d1ae7b674fc2c2d4381e9e45c8b to your computer and use it in GitHub Desktop.
generator_test.gd
#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(GRASS, x, relative_height +1, z, _CHANNEL)
if relative_height < block_size and rng.randf() > 0.1:
var foliage = GRASS # TALL_GRASS
#if # rng.randf() < 0.1:
# foliage = GRASS # DEAD_SHRUB
buffer.set_voxel(GRASS, 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))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment