Switch generator to a custom one with grass and trees

master
Marc Gilleron 2020-08-05 00:08:50 +01:00
parent 9a0f1f35fd
commit 099ad1dfcd
9 changed files with 391 additions and 1 deletions

View File

@ -3,7 +3,7 @@
[ext_resource path="res://blocky_game/blocks/voxel_library.tres" type="VoxelLibrary" id=1]
[ext_resource path="res://blocky_game/blocks/terrain_material.tres" type="Material" id=2]
[ext_resource path="res://blocky_game/blocks/terrain_material_transparent.tres" type="Material" id=3]
[ext_resource path="res://blocky_game/voxel_generator_noise_2d_blocky.tres" type="VoxelGeneratorNoise2D" id=4]
[ext_resource path="res://blocky_game/generator/world_generator.tres" type="VoxelGenerator" id=4]
[ext_resource path="res://blocky_game/blocky_game.gd" type="Script" id=5]
[ext_resource path="res://blocky_game/player/character_avatar.tscn" type="PackedScene" id=6]
[ext_resource path="res://blocky_game/blocks/stairs/stairs_nx.obj" type="ArrayMesh" id=7]
@ -73,6 +73,7 @@ mesh = ExtResource( 7 )
material/0 = null
[node name="CharacterAvatar" parent="." instance=ExtResource( 6 )]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 64, 0 )
speed = 6.0
gravity = 40.0
jump_force = 8.0

View File

@ -0,0 +1,172 @@
#tool
extends VoxelGenerator
const Structure = preload("./structure.gd")
const TreeGenerator = preload("./tree_generator.gd")
# 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 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 := -32
var _heightmap_max_y := 64
var _heightmap_range := 0
var _heightmap_noise := OpenSimplexNoise.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.period = 128
_heightmap_noise.octaves = 4
func get_used_channels_mask() -> int:
return 1 << _CHANNEL
func generate_block(buffer: VoxelBuffer, origin_in_voxels: Vector3, lod: int):
# Assuming input is cubic in our use case (it doesn't have to be!)
var block_size := int(buffer.get_size().x)
var oy := int(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(
int(origin_in_voxels.x) >> 4,
int(origin_in_voxels.y) >> 4,
int(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 := int(origin_in_voxels.z)
for z in block_size:
gx = int(origin_in_voxels.x)
for x in block_size:
var h := _get_height_at(gx, gz)
var rh := h - oy
if rh > block_size:
buffer.fill_area(DIRT,
Vector3(x, 0, z), Vector3(x + 1, block_size, z + 1), _CHANNEL)
elif rh > 0:
buffer.fill_area(DIRT,
Vector3(x, 0, z), Vector3(x + 1, rh, z + 1), _CHANNEL)
if h > 0:
buffer.set_voxel(GRASS, x, rh - 1, z, _CHANNEL)
if rh < block_size and rng.randf() < 0.2:
buffer.set_voxel(TALL_GRASS, x, rh, z, _CHANNEL)
# TODO Tall grass
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() + Vector3(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() + Vector3(1, 1, 1))
if aabb.intersects(block_aabb):
voxel_tool.paste(lower_corner_pos, structure.voxels, AIR)
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 = _get_height_at(pos.x, pos.z)
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:
return int(_heightmap_min_y + _heightmap_range \
* (0.5 + 0.5 * _heightmap_noise.get_noise_2d(x, z)))

View File

@ -0,0 +1,3 @@
var offset := Vector3()
var voxels := VoxelBuffer.new()

View File

@ -0,0 +1,62 @@
extends Node
const Generator = preload("./generator.gd")
const VoxelLibraryResource = preload("../blocks/voxel_library.tres")
const _materials = [
preload("../blocks/terrain_material.tres"),
preload("../blocks/terrain_material_foliage.tres"),
preload("../blocks/terrain_material_transparent.tres")
]
onready var _mesh_instance : MeshInstance = $MeshInstance
var _origin = Vector3()
var _generator = Generator.new()
func _ready():
VoxelLibraryResource.bake()
_generate()
func _input(event):
if event is InputEventKey:
if event.pressed:
match event.scancode:
KEY_SPACE:
_generate()
KEY_LEFT:
_origin.x -= 16
_generate()
KEY_RIGHT:
_origin.x += 16
_generate()
KEY_DOWN:
_origin.z -= 16
_generate()
KEY_UP:
_origin.z += 16
_generate()
func _generate():
var voxels = VoxelBuffer.new()
voxels.create(16, 16, 16)
_generator.generate_block(voxels, _origin, 0)
var padded_voxels = VoxelBuffer.new()
padded_voxels.create(
voxels.get_size().x + 2,
voxels.get_size().y + 2,
voxels.get_size().z + 2)
padded_voxels.copy_channel_from_area(
voxels, Vector3(), voxels.get_size(), Vector3(1, 1, 1), VoxelBuffer.CHANNEL_TYPE)
var mesher = VoxelMesherBlocky.new()
mesher.set_library(VoxelLibraryResource)
var mesh = mesher.build_mesh(padded_voxels, _materials)
_mesh_instance.mesh = mesh

View File

@ -0,0 +1,16 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://blocky_game/generator/test_generator.gd" type="Script" id=1]
[node name="Node" type="Node"]
script = ExtResource( 1 )
[node name="MeshInstance" type="MeshInstance" parent="."]
[node name="Camera" type="Camera" parent="."]
transform = Transform( 0.655427, 0.511681, -0.555516, 0, 0.735531, 0.677491, 0.755258, -0.444046, 0.482087, -3.82637, 21.7113, 20.01 )
[node name="DirectionalLight" type="DirectionalLight" parent="."]
transform = Transform( 0.962432, -0.200093, 0.183544, 0, 0.675976, 0.736924, -0.271524, -0.709238, 0.650581, 0, 6.15053, 0 )
shadow_enabled = true
directional_shadow_normal_bias = 0.1

View File

@ -0,0 +1,46 @@
extends Node
const TreeGenerator = preload("./tree_generator.gd")
const VoxelLibraryResource = preload("../blocks/voxel_library.tres")
const _materials = [
preload("../blocks/terrain_material.tres"),
preload("../blocks/terrain_material_foliage.tres"),
preload("../blocks/terrain_material_transparent.tres")
]
onready var _mesh_instance : MeshInstance = $MeshInstance
func _ready():
_generate()
func _input(event):
if event is InputEventKey:
if event.pressed:
if event.scancode == KEY_SPACE:
_generate()
func _generate():
var tree_generator := TreeGenerator.new()
tree_generator.log_type = VoxelLibraryResource.get_voxel_index_from_name("log_y")
tree_generator.leaves_type = VoxelLibraryResource.get_voxel_index_from_name("dirt")
var s = tree_generator.generate()
var padded_voxels := VoxelBuffer.new()
padded_voxels.create(
s.voxels.get_size().x + 2, s.voxels.get_size().y + 2, s.voxels.get_size().z + 2)
padded_voxels.copy_channel_from_area(
s.voxels, Vector3(), s.voxels.get_size(), Vector3(1, 1, 1), VoxelBuffer.CHANNEL_TYPE)
var mesher = VoxelMesherBlocky.new()
mesher.set_library(VoxelLibraryResource)
var mesh = mesher.build_mesh(padded_voxels, _materials)
_mesh_instance.mesh = mesh

View File

@ -0,0 +1,11 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://blocky_game/generator/test_tree_generator.gd" type="Script" id=1]
[node name="Node" type="Node"]
script = ExtResource( 1 )
[node name="MeshInstance" type="MeshInstance" parent="."]
[node name="Camera" type="Camera" parent="."]
transform = Transform( 0.655427, 0.51168, -0.555516, 0, 0.735531, 0.677491, 0.755258, -0.444046, 0.482087, -4.3852, 14.6874, 18.075 )

View File

@ -0,0 +1,73 @@
const Structure = preload("./structure.gd")
var trunk_len_min := 4
var trunk_len_max := 12
var log_type := 1
var leaves_type := 2
var channel := VoxelBuffer.CHANNEL_TYPE
func generate() -> Structure:
var voxels := {}
# Let's make crappy trees
# Trunk
var trunk_len := int(rand_range(trunk_len_min, trunk_len_max))
for y in trunk_len:
voxels[Vector3(0, y, 0)] = log_type
# Branches
var branches_start := int(rand_range(trunk_len / 3, trunk_len / 2))
for y in range(branches_start, trunk_len):
var t := float(y - branches_start) / float(trunk_len)
var branch_chance := 1.0 - pow(t - 0.5, 2)
if randf() < branch_chance:
var branch_len := int((trunk_len / 2.0) * branch_chance * randf())
var pos := Vector3(0, y, 0)
var angle := rand_range(-PI, PI)
var dir := Vector3(cos(angle), 0.45, sin(angle))
for i in branch_len:
pos += dir
var ipos = pos.round()
voxels[ipos] = log_type
# Leaves
var log_positions := voxels.keys()
log_positions.shuffle()
var leaf_count := len(log_positions) / 3
log_positions.resize(leaf_count)
var dirs := [
Vector3(-1, 0, 0),
Vector3(1, 0, 0),
Vector3(0, 0, -1),
Vector3(0, 0, 1)
]
for c in leaf_count:
var pos = log_positions[c]
if pos.y < branches_start:
continue
for di in len(dirs):
var npos = pos + dirs[di]
if not voxels.has(npos):
voxels[npos] = leaves_type
# Make structure
var aabb := AABB()
for pos in voxels:
aabb = aabb.expand(pos)
var structure := Structure.new()
structure.offset = -aabb.position
var buffer := structure.voxels
buffer.create(int(aabb.size.x) + 1, int(aabb.size.y) + 1, int(aabb.size.z) + 1)
for pos in voxels:
var rpos = pos + structure.offset
var v = voxels[pos]
buffer.set_voxel(v, rpos.x, rpos.y, rpos.z, channel)
return structure

View File

@ -0,0 +1,6 @@
[gd_resource type="VoxelGenerator" load_steps=2 format=2]
[ext_resource path="res://blocky_game/generator/generator.gd" type="Script" id=1]
[resource]
script = ExtResource( 1 )