diff --git a/project/multipass_generator/blocky_library.tres b/project/multipass_generator/blocky_library.tres new file mode 100644 index 0000000..6c336cf --- /dev/null +++ b/project/multipass_generator/blocky_library.tres @@ -0,0 +1,35 @@ +[gd_resource type="VoxelBlockyLibrary" load_steps=6 format=3 uid="uid://5nm1ee5xpocl"] + +[ext_resource type="Material" uid="uid://l26jp5hcwveg" path="res://multipass_generator/opaque_material.tres" id="1_xlckp"] + +[sub_resource type="VoxelBlockyModelEmpty" id="VoxelBlockyModelEmpty_7i052"] + +[sub_resource type="VoxelBlockyModelCube" id="VoxelBlockyModelCube_hrrub"] +resource_name = "stone" +material_override_0 = ExtResource("1_xlckp") +atlas_size_in_tiles = Vector2i(8, 8) + +[sub_resource type="VoxelBlockyModelCube" id="VoxelBlockyModelCube_fehag"] +resource_name = "log" +material_override_0 = ExtResource("1_xlckp") +atlas_size_in_tiles = Vector2i(8, 8) +tile_left = Vector2i(2, 0) +tile_right = Vector2i(2, 0) +tile_bottom = Vector2i(3, 0) +tile_top = Vector2i(3, 0) +tile_back = Vector2i(2, 0) +tile_front = Vector2i(2, 0) + +[sub_resource type="VoxelBlockyModelCube" id="VoxelBlockyModelCube_7mjab"] +resource_name = "leaves" +material_override_0 = ExtResource("1_xlckp") +atlas_size_in_tiles = Vector2i(8, 8) +tile_left = Vector2i(0, 2) +tile_right = Vector2i(0, 2) +tile_bottom = Vector2i(0, 2) +tile_top = Vector2i(0, 2) +tile_back = Vector2i(0, 2) +tile_front = Vector2i(0, 2) + +[resource] +models = Array[VoxelBlockyModel]([SubResource("VoxelBlockyModelEmpty_7i052"), SubResource("VoxelBlockyModelCube_hrrub"), SubResource("VoxelBlockyModelCube_fehag"), SubResource("VoxelBlockyModelCube_7mjab")]) diff --git a/project/multipass_generator/blocky_mesher.tres b/project/multipass_generator/blocky_mesher.tres new file mode 100644 index 0000000..e821e56 --- /dev/null +++ b/project/multipass_generator/blocky_mesher.tres @@ -0,0 +1,6 @@ +[gd_resource type="VoxelMesherBlocky" load_steps=2 format=3 uid="uid://m85p22yb1ft0"] + +[ext_resource type="VoxelBlockyLibrary" uid="uid://5nm1ee5xpocl" path="res://multipass_generator/blocky_library.tres" id="1_djp1m"] + +[resource] +library = ExtResource("1_djp1m") diff --git a/project/multipass_generator/multipass_demo.tscn b/project/multipass_generator/multipass_demo.tscn new file mode 100644 index 0000000..ed09c61 --- /dev/null +++ b/project/multipass_generator/multipass_demo.tscn @@ -0,0 +1,36 @@ +[gd_scene load_steps=7 format=3 uid="uid://d0dodvwrj0biu"] + +[ext_resource type="VoxelGeneratorMultipassCB" uid="uid://dqgjcqp0m4ae2" path="res://multipass_generator/multipass_generator.tres" id="1_bubjg"] +[ext_resource type="VoxelMesherBlocky" uid="uid://m85p22yb1ft0" path="res://multipass_generator/blocky_mesher.tres" id="2_pgkoq"] +[ext_resource type="PackedScene" uid="uid://dljqckrx50m8t" path="res://common/spectator_avatar.tscn" id="3_tecxx"] + +[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_8h45h"] +sky_horizon_color = Color(0.64625, 0.65575, 0.67075, 1) +ground_horizon_color = Color(0.64625, 0.65575, 0.67075, 1) + +[sub_resource type="Sky" id="Sky_rv3jx"] +sky_material = SubResource("ProceduralSkyMaterial_8h45h") + +[sub_resource type="Environment" id="Environment_vdnyq"] +background_mode = 2 +sky = SubResource("Sky_rv3jx") +tonemap_mode = 2 +glow_enabled = true + +[node name="Node" type="Node"] + +[node name="WorldEnvironment" type="WorldEnvironment" parent="."] +environment = SubResource("Environment_vdnyq") + +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] +transform = Transform3D(-0.866025, -0.433013, 0.25, 0, 0.5, 0.866025, -0.5, 0.75, -0.433013, 0, 0, 0) +shadow_enabled = true + +[node name="VoxelTerrain" type="VoxelTerrain" parent="."] +generator = ExtResource("1_bubjg") +mesher = ExtResource("2_pgkoq") +generate_collisions = false +run_stream_in_editor = false + +[node name="SpectatorAvatar" parent="." instance=ExtResource("3_tecxx")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 15.1602, 0) diff --git a/project/multipass_generator/multipass_demo_single_column.gd b/project/multipass_generator/multipass_demo_single_column.gd new file mode 100644 index 0000000..cdbeebf --- /dev/null +++ b/project/multipass_generator/multipass_demo_single_column.gd @@ -0,0 +1,62 @@ + +# Test generating a single column without using threads + +extends Node + +# TODO Can't preload script: https://github.com/godotengine/godot/issues/83125 +#const TestGenerator = preload("./multipass_generator.gd") +const GeneratorResource = preload("./multipass_generator.tres") +const BlockyMesher = preload("./blocky_mesher.tres") + + +func _ready(): + var generator := GeneratorResource + + var column = generator.debug_generate_test_column(Vector2i(2,1)) + + # TODO Need to preload script... + var channel := 0#TestGenerator.CHANNEL + + var mesher := BlockyMesher + mesher.library.bake() + var materials : Array[Material] = mesher.library.get_materials() + + var origin_in_voxels := Vector3(0, generator.column_base_y_blocks * 16, 0) + + for rby in column.size(): + var block : VoxelBuffer = column[rby] + + var padded_voxels := VoxelBuffer.new() + var bs := block.get_size() + padded_voxels.create(bs.x + 2, bs.y + 2, bs.z + 2) + padded_voxels.copy_channel_from_area(block, Vector3i(), bs, Vector3i(1, 1, 1), channel) + + if rby > 0: + var bottom_block : VoxelBuffer = column[rby - 1] + padded_voxels.copy_channel_from_area(bottom_block, + Vector3i(0, bs.y - 1, 0), bs, Vector3i(1, 0, 1), channel) + + if rby + 1 < column.size(): + var top_block : VoxelBuffer = column[rby + 1] + padded_voxels.copy_channel_from_area(top_block, + Vector3i(), Vector3i(bs.x, 1, bs.z), Vector3i(1, bs.y + 1, 1), channel) + + # print("---- Block ", rby) + # for z in [8]: + # var ds := "" + # for x in block.get_size().x: + # for y in block.get_size().y: + # ds += str(block.get_voxel(x, y, z, 0), " ") + # ds += "\n" + # print("Z ", z) + # print(ds) + + var mesh := mesher.build_mesh(padded_voxels, materials) + + var mi := MeshInstance3D.new() + mi.mesh = mesh + mi.position = origin_in_voxels + add_child(mi) + + origin_in_voxels.y += bs.y + diff --git a/project/multipass_generator/multipass_demo_single_column.tscn b/project/multipass_generator/multipass_demo_single_column.tscn new file mode 100644 index 0000000..74ce2d3 --- /dev/null +++ b/project/multipass_generator/multipass_demo_single_column.tscn @@ -0,0 +1,29 @@ +[gd_scene load_steps=5 format=3 uid="uid://c1axjmomlc7hv"] + +[ext_resource type="Script" path="res://multipass_generator/multipass_demo_single_column.gd" id="1_0w0kl"] + +[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_rd8xx"] +sky_horizon_color = Color(0.64625, 0.65575, 0.67075, 1) +ground_horizon_color = Color(0.64625, 0.65575, 0.67075, 1) + +[sub_resource type="Sky" id="Sky_gyxt2"] +sky_material = SubResource("ProceduralSkyMaterial_rd8xx") + +[sub_resource type="Environment" id="Environment_fngcp"] +background_mode = 2 +sky = SubResource("Sky_gyxt2") +tonemap_mode = 2 +glow_enabled = true + +[node name="Node" type="Node"] +script = ExtResource("1_0w0kl") + +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] +transform = Transform3D(-0.866023, -0.433016, 0.250001, 0, 0.499998, 0.866027, -0.500003, 0.749999, -0.43301, 0, 0, 0) +shadow_enabled = true + +[node name="WorldEnvironment" type="WorldEnvironment" parent="."] +environment = SubResource("Environment_fngcp") + +[node name="Camera3D" type="Camera3D" parent="."] +transform = Transform3D(-0.48255, 0.449767, -0.751568, 0, 0.858084, 0.51351, 0.875869, 0.247794, -0.414068, -33.3619, 27.9191, -17.7105) diff --git a/project/multipass_generator/multipass_generator.gd b/project/multipass_generator/multipass_generator.gd new file mode 100644 index 0000000..cee61b1 --- /dev/null +++ b/project/multipass_generator/multipass_generator.gd @@ -0,0 +1,127 @@ +extends VoxelGeneratorMultipassCB + +const CHANNEL = VoxelBuffer.CHANNEL_TYPE + +const AIR = 0 +const STONE = 1 +const LOG = 2 +const LEAVES = 3 + +const SEED = 1337 + +var _noise : FastNoiseLite + + +func _init(): + _noise = FastNoiseLite.new() + _noise.frequency = 1.0 / 128.0 + _noise.seed = SEED + + +func _generate_pass(voxel_tool: VoxelToolMultipassGenerator, pass_index: int): + voxel_tool.channel = CHANNEL + + var min_pos := voxel_tool.get_main_area_min() + var max_pos := voxel_tool.get_main_area_max() +# print("pass ", pass_index, ", min_pos: ", min_pos, ", max_pos: ", max_pos) + + if pass_index == 0: + for gz in range(min_pos.z, max_pos.z): + for gx in range(min_pos.x, max_pos.x): + var height := 20.0 * _noise.get_noise_2d(gx, gz) + voxel_tool.value = STONE + voxel_tool.do_box(Vector3i(gx, min_pos.y, gz), Vector3i(gx, height, gz)) + + # This is just for testing do_path, also creating overhangs that show how tree generation + # benefits from multipass. In practice it's quite slow without checking boundaries. + generate_spiral(voxel_tool) + + elif pass_index == 1: + var rng := RandomNumberGenerator.new() + rng.seed = hash(Vector2i(min_pos.x, min_pos.z)) + SEED + + var tree_count := 3#rng.randi_range(0, 3) + + for tree_index in tree_count: + try_plant_tree(voxel_tool, rng) + + +static func generate_spiral(voxel_tool: VoxelToolMultipassGenerator): + var positions := PackedVector3Array() + var radii := PackedFloat32Array() + + var point_count := 100 + var length := 100.0 + var begin_position := Vector3(-40.0, 20.0, 20.0) + var radius := 15.0 + var twist := 25.0 + + for i in point_count: + var t := i / float(point_count) + positions.append(begin_position + Vector3( + t * length, # X + radius * cos(t * twist), # Y + radius * sin(t * twist) # Z + )) + radii.append(lerpf(2.0, 4.0, 0.5 + 0.5 * sin(t * 100.0))) + + voxel_tool.do_path(positions, radii) + + +static func try_plant_tree(voxel_tool: VoxelToolMultipassGenerator, rng: RandomNumberGenerator): + var min_pos := voxel_tool.get_main_area_min() + var max_pos := voxel_tool.get_main_area_max() + var chunk_size = max_pos - min_pos + + var tree_rpos := Vector3i( + rng.randi_range(0, chunk_size.x), 0, + rng.randi_range(0, chunk_size.z) + ) +# print("Trying to plant a tree at ", tree_rpos) + + var tree_pos := min_pos + tree_rpos + tree_pos.y = max_pos.y - 1 + + var found_ground := false + while tree_pos.y >= min_pos.y: + var v := voxel_tool.get_voxel(tree_pos) + # Note, we could also find tree blocks that were placed earlier! + if v == STONE: + found_ground = true + break + tree_pos.y -= 1 + + if not found_ground: +# print("Ground not found") + return + + # Plant tree + var trunk_height := rng.randi_range(8, 15) + var leaves_min_y := 3 * trunk_height / 2 + var leaves_max_y := trunk_height + 2 + var leaves_min_radius := 2 + var leaves_max_radius := 5 + + if tree_pos.y + leaves_max_y + leaves_max_radius >= max_pos.y: + # Too high +# print("Too high") + return + + voxel_tool.value = LEAVES + for i in 5: + var center := tree_pos + Vector3i(0, rng.randi_range(leaves_min_y, leaves_max_y), 0) + var radius := rng.randf_range(leaves_min_radius, leaves_max_radius) + voxel_tool.do_sphere(center, radius) + + voxel_tool.value = LOG + voxel_tool.do_box(tree_pos, tree_pos + Vector3i(0, trunk_height, 0)) + + +func _generate_block_fallback(out_buffer: VoxelBuffer, origin_in_voxels: Vector3i): + pass + + +func _get_used_channels_mask() -> int: + return 1 << CHANNEL + + diff --git a/project/multipass_generator/multipass_generator.tres b/project/multipass_generator/multipass_generator.tres new file mode 100644 index 0000000..103da7a --- /dev/null +++ b/project/multipass_generator/multipass_generator.tres @@ -0,0 +1,9 @@ +[gd_resource type="VoxelGeneratorMultipassCB" load_steps=2 format=3 uid="uid://dqgjcqp0m4ae2"] + +[ext_resource type="Script" path="res://multipass_generator/multipass_generator.gd" id="1_jgwux"] + +[resource] +pass_count = 3 +pass_1_extent = 1 +pass_2_extent = 1 +script = ExtResource("1_jgwux") diff --git a/project/multipass_generator/opaque_material.tres b/project/multipass_generator/opaque_material.tres new file mode 100644 index 0000000..013f163 --- /dev/null +++ b/project/multipass_generator/opaque_material.tres @@ -0,0 +1,8 @@ +[gd_resource type="StandardMaterial3D" load_steps=2 format=3 uid="uid://l26jp5hcwveg"] + +[ext_resource type="Texture2D" uid="uid://bgxqjxpjjrbug" path="res://multipass_generator/terrain.png" id="1_2tubt"] + +[resource] +vertex_color_use_as_albedo = true +albedo_texture = ExtResource("1_2tubt") +texture_filter = 0 diff --git a/project/multipass_generator/terrain.png b/project/multipass_generator/terrain.png new file mode 100644 index 0000000..dcbdee4 Binary files /dev/null and b/project/multipass_generator/terrain.png differ diff --git a/project/multipass_generator/terrain.png.import b/project/multipass_generator/terrain.png.import new file mode 100644 index 0000000..d2f6d10 --- /dev/null +++ b/project/multipass_generator/terrain.png.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bgxqjxpjjrbug" +path.s3tc="res://.godot/imported/terrain.png-1a8a53d00d70a507f028e21c793ee320.s3tc.ctex" +metadata={ +"imported_formats": ["s3tc_bptc"], +"vram_texture": true +} + +[deps] + +source_file="res://multipass_generator/terrain.png" +dest_files=["res://.godot/imported/terrain.png-1a8a53d00d70a507f028e21c793ee320.s3tc.ctex"] + +[params] + +compress/mode=2 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0