Switch generator to a custom one with grass and trees
parent
9a0f1f35fd
commit
099ad1dfcd
|
@ -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
|
||||
|
|
|
@ -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)))
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
var offset := Vector3()
|
||||
var voxels := VoxelBuffer.new()
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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 )
|
|
@ -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
|
||||
|
|
@ -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 )
|
Loading…
Reference in New Issue