extends Node3D @export var width: int = 20 @export var height: int = 10 @export var depth: int = 20 @export var scale_x: int = 10 @export var scale_y: int = 6 @export var scale_z: int = 10 @export var radius: int = 5 @export var smooth_normal: bool = false var map_grid: Array[float] = [] var random = RandomNumberGenerator.new() func map_grid_get(x: int, y: int, z: int): return map_grid[z*height*width + y*width + x] func gen_map_grid(): map_grid.resize(width*height*depth) map_grid.fill(1.0) var noise = FastNoiseLite.new() noise.noise_type = FastNoiseLite.TYPE_SIMPLEX noise.frequency = 0.06 noise.set_seed(0xDEADBEEF) for z in depth: for x in width: var mid_x = x - width/2.0 var mid_z = z - depth/2.0 var pos_vec = Vector2(mid_x, mid_z) var pos_radius = pos_vec.length() var multiplier = 1.0 / (max(1, pos_radius - radius)) var gen_h = noise.get_noise_2d(x, z) gen_h = remap(gen_h, -1, 1, 0, height) gen_h = int(gen_h*multiplier*0.5) for y in gen_h: map_grid[z*height*width + y*width + x] = -100.0 func add_normal(tri_index: int, triangles: PackedInt32Array, vertices: PackedVector3Array, normal_list: Array[Vector4]): var new_tri_index: int = 0 while new_tri_index < len(triangles): @warning_ignore("integer_division") var tri_index_1:int = int(new_tri_index/3) @warning_ignore("integer_division") var tri_index_2:int = int(tri_index/3) if tri_index_1 == tri_index_2: new_tri_index += 3 continue var orig_vert = vertices[triangles[tri_index]] var verts = [ new_tri_index+0, new_tri_index+1, new_tri_index+2] for i in verts: var vert = vertices[triangles[i]] if i == tri_index: continue if vert == orig_vert: normal_list[triangles[tri_index]] += normal_list[triangles[i]] new_tri_index += 3 func place_objects(num_objects: int, object: PackedScene): var space = get_world_3d().direct_space_state var object_num: int = 0 while object_num < num_objects: var cast_pos = Vector3(random.randf_range(0, width*scale_x), height*scale_y, random.randf_range(0, depth*scale_z)) var cast_dest = Vector3(cast_pos.x, 0, cast_pos.z) var query = PhysicsRayQueryParameters3D.create(cast_pos, cast_dest) var result = space.intersect_ray(query) if result: # If the object is water, don't place anything if result.collider == $Water: continue # Don't spawn objects on hills that are too steep if result.normal.dot(Vector3(0, 1, 0)) < 0.8: continue var pos = result.position var new_tree = object.instantiate() new_tree.position = pos add_child(new_tree) object_num += 1 # Called when the node enters the scene tree for the first time. func _ready(): random.randomize() gen_map_grid() var vertices = PackedVector3Array() var normals = PackedVector3Array() var triangles = PackedInt32Array() # Initialize the ArrayMesh. var arr_mesh = ArrayMesh.new() var arrays = [] arrays.resize(Mesh.ARRAY_MAX) arrays[Mesh.ARRAY_VERTEX] = vertices arrays[Mesh.ARRAY_NORMAL] = normals arrays[Mesh.ARRAY_INDEX] = triangles var cell = MarchingCubes.Cell.new() for z in range(-1, depth): for y in range(-1, height): for x in range(-1, width): var grid = [ [x, y, z], [x + 1, y, z], [x + 1, y + 1, z], [x, y + 1, z], [x, y, z + 1], [x + 1, y, z + 1], [x + 1, y + 1, z + 1], [x, y + 1, z + 1], ] for i in grid.size(): if grid[i][0] >= width or grid[i][1] >= height or grid[i][2] >= depth or \ grid[i][0] < 0 or grid[i][1] < 0 or grid[i][2] < 0: cell.value[i] = 1.0 else: cell.value[i] = map_grid_get(grid[i][0], grid[i][1], grid[i][2]) cell.position[i] = Vector3(grid[i][0]*scale_x, grid[i][1]*scale_y, grid[i][2]*scale_z) MarchingCubes.march_cube(cell, triangles, vertices) var tri_index = 0 var normal_list: Array[Vector4] = [] normal_list.resize(len(vertices)) normal_list.fill(Vector4(0,0,0,0)) while tri_index < len(triangles): var p1 = vertices[triangles[tri_index + 0]] var p2 = vertices[triangles[tri_index + 1]] var p3 = vertices[triangles[tri_index + 2]] var normal = (p2 - p3).cross(p1 - p3).normalized(); var normal_4 = Vector4(normal.x, normal.y, normal.z, 1) normal_list[triangles[tri_index + 0]] += normal_4 normal_list[triangles[tri_index + 1]] += normal_4 normal_list[triangles[tri_index + 2]] += normal_4 tri_index += 3 if smooth_normal: tri_index = 0 while tri_index < len(triangles): add_normal(tri_index+0, triangles, vertices, normal_list) add_normal(tri_index+1, triangles, vertices, normal_list) add_normal(tri_index+2, triangles, vertices, normal_list) tri_index += 3 for normal in normal_list: var real_normal = Vector3(normal.x, normal.y, normal.z) / normal.w normals.push_back(real_normal.normalized()) # Create the Mesh arr_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays) #var material = preload("res://materials/terrain.tres") #arr_mesh.surface_set_material(0, material) var m = MeshInstance3D.new() m.mesh = arr_mesh var col_mesh = arr_mesh.create_trimesh_shape() var collision_shape = CollisionShape3D.new() collision_shape.set_shape(col_mesh) var static_body = StaticBody3D.new() static_body.add_child(collision_shape) static_body.add_child(m) add_child(static_body) var tree_prefab = preload("res://prefab/tree.tscn") var num_trees = random.randi_range(5, 15) var rock_prefab = preload("res://prefab/rock.tscn") var num_rocks = random.randi_range(5, 15) place_objects(num_trees, tree_prefab) place_objects(num_rocks, rock_prefab) # Called every frame. 'delta' is the elapsed time since the previous frame. func _process(_delta): pass