Advertisement
charlie_oh

Proc Terrain Gen

Oct 22nd, 2024
62
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
GDScript 5.47 KB | Software | 0 0
  1. extends Node3D
  2.  
  3. @export var grid_size := Vector2(10, 10)  # Number of tiles
  4. @export var tile_size := 1.0  # Size of each tile
  5. @export var max_height := 2.0  # Maximum height for raised tiles
  6. @export var subdivisions := 4  # Number of subdivisions per tile
  7. @export var transition_width := 0.4  # Width of the transition (0-1)
  8. @export var material: Material  # Single material for the entire mesh
  9.  
  10. var mesh_instance: MeshInstance3D
  11. var mesh: ArrayMesh
  12. var collision_shape: CollisionShape3D
  13. var heightmap: Array = []
  14. @export var camera: Camera3D
  15.  
  16. func _ready():
  17.     # Initialize heightmap with subdivisions
  18.     for x in range(grid_size.x * subdivisions):
  19.         heightmap.append([])
  20.         for y in range(grid_size.y * subdivisions):
  21.             heightmap[x].append(0.0)
  22.    
  23.     # Create mesh instance
  24.     mesh_instance = MeshInstance3D.new()
  25.     add_child(mesh_instance)
  26.    
  27.     # Assign material if provided
  28.     if material:
  29.         mesh_instance.material_override = material
  30.    
  31.     # Create collision shape for raycasting
  32.     var static_body = StaticBody3D.new()
  33.     add_child(static_body)
  34.     collision_shape = CollisionShape3D.new()
  35.     static_body.add_child(collision_shape)
  36.    
  37.     generate_mesh()
  38.  
  39. func calculate_normal(x: int, z: int) -> Vector3:
  40.     var x_prev = max(x - 1, 0)
  41.     var x_next = min(x + 1, (grid_size.x * subdivisions) - 1)
  42.     var z_prev = max(z - 1, 0)
  43.     var z_next = min(z + 1, (grid_size.y * subdivisions) - 1)
  44.    
  45.     var h_left = heightmap[x_prev][z] if x_prev < heightmap.size() and z < heightmap[x_prev].size() else 0.0
  46.     var h_right = heightmap[x_next][z] if x_next < heightmap.size() and z < heightmap[x_next].size() else 0.0
  47.     var h_up = heightmap[x][z_prev] if x < heightmap.size() and z_prev < heightmap[x].size() else 0.0
  48.     var h_down = heightmap[x][z_next] if x < heightmap.size() and z_next < heightmap[x].size() else 0.0
  49.    
  50.     var normal = Vector3(
  51.         h_left - h_right,
  52.         2.0,
  53.         h_up - h_down
  54.     )
  55.    
  56.     return -normal.normalized()
  57.  
  58. func get_smooth_height(tile_x: int, tile_y: int, sub_x: float, sub_y: float) -> float:
  59.     var center_height = heightmap[tile_x * subdivisions][tile_y * subdivisions]
  60.     var distance_from_center = Vector2(
  61.         abs(sub_x - 0.5),
  62.         abs(sub_y - 0.5)
  63.     ).length()
  64.    
  65.     var fade = 1.0 - smoothstep(0.0, transition_width, distance_from_center)
  66.     return center_height * fade
  67.  
  68. func generate_mesh():
  69.     var surface_array = []
  70.     surface_array.resize(Mesh.ARRAY_MAX)
  71.    
  72.     var verts = PackedVector3Array()
  73.     var uvs = PackedVector2Array()
  74.     var indices = PackedInt32Array()
  75.     var normals = PackedVector3Array()
  76.    
  77.     # Generate vertices with subdivisions
  78.     for x in range(grid_size.x * subdivisions + 1):
  79.         for z in range(grid_size.y * subdivisions + 1):
  80.             var grid_x = x / float(subdivisions)
  81.             var grid_z = z / float(subdivisions)
  82.            
  83.             # Calculate base tile position
  84.             var tile_x = floor(grid_x)
  85.             var tile_z = floor(grid_z)
  86.            
  87.             # Calculate position within tile (0-1)
  88.             var sub_x = grid_x - tile_x
  89.             var sub_z = grid_z - tile_z
  90.            
  91.             var height = get_smooth_height(
  92.                 min(tile_x, grid_size.x - 1),
  93.                 min(tile_z, grid_size.y - 1),
  94.                 sub_x,
  95.                 sub_z
  96.             )
  97.            
  98.             var vertex = Vector3(
  99.                 grid_x * tile_size - (grid_size.x * tile_size) / 2,
  100.                 height,
  101.                 grid_z * tile_size - (grid_size.y * tile_size) / 2
  102.             )
  103.            
  104.             verts.append(vertex)
  105.             uvs.append(Vector2(grid_x / grid_size.x, grid_z / grid_size.y))
  106.             normals.append(calculate_normal(x, z))
  107.    
  108.     # Generate indices
  109.     var vertices_per_row = grid_size.y * subdivisions + 1
  110.     for x in range(grid_size.x * subdivisions):
  111.         for z in range(grid_size.y * subdivisions):
  112.             var vertex = x * vertices_per_row + z
  113.            
  114.             indices.append(vertex)
  115.             indices.append(vertex + vertices_per_row)
  116.             indices.append(vertex + 1)
  117.            
  118.             indices.append(vertex + 1)
  119.             indices.append(vertex + vertices_per_row)
  120.             indices.append(vertex + vertices_per_row + 1)
  121.    
  122.     # Create mesh
  123.     surface_array[Mesh.ARRAY_VERTEX] = verts
  124.     surface_array[Mesh.ARRAY_NORMAL] = normals
  125.     surface_array[Mesh.ARRAY_TEX_UV] = uvs
  126.     surface_array[Mesh.ARRAY_INDEX] = indices
  127.    
  128.     mesh = ArrayMesh.new()
  129.     mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, surface_array)
  130.     mesh_instance.mesh = mesh
  131.    
  132.     # Update collision shape
  133.     var collision_faces = PackedVector3Array()
  134.     for i in range(0, indices.size(), 3):
  135.         collision_faces.append(verts[indices[i]])
  136.         collision_faces.append(verts[indices[i + 1]])
  137.         collision_faces.append(verts[indices[i + 2]])
  138.    
  139.     var shape = ConcavePolygonShape3D.new()
  140.     shape.set_faces(collision_faces)
  141.     collision_shape.shape = shape
  142.  
  143. func _input(event):
  144.     if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
  145.         var ray_length = 1000
  146.         var from = camera.project_ray_origin(event.position)
  147.         var to = from + camera.project_ray_normal(event.position) * ray_length
  148.        
  149.         var space_state = get_world_3d().direct_space_state
  150.         var query = PhysicsRayQueryParameters3D.create(from, to)
  151.         var result = space_state.intersect_ray(query)
  152.        
  153.         if result:
  154.             var hit_pos = result.position
  155.             var grid_pos = Vector2(
  156.                 floor((hit_pos.x + (grid_size.x * tile_size) / 2) / tile_size),
  157.                 floor((hit_pos.z + (grid_size.y * tile_size) / 2) / tile_size)
  158.             )
  159.            
  160.             if grid_pos.x >= 0 and grid_pos.x < grid_size.x and grid_pos.y >= 0 and grid_pos.y < grid_size.y:
  161.                 var base_x = int(grid_pos.x * subdivisions)
  162.                 var base_y = int(grid_pos.y * subdivisions)
  163.                
  164.                 if heightmap[base_x][base_y] == 0:
  165.                     heightmap[base_x][base_y] = max_height
  166.                 else:
  167.                     heightmap[base_x][base_y] = 0
  168.                
  169.                 generate_mesh()
  170.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement