Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- extends Node3D
- @export var grid_size := Vector2(10, 10) # Number of tiles
- @export var tile_size := 1.0 # Size of each tile
- @export var max_height := 2.0 # Maximum height for raised tiles
- @export var subdivisions := 4 # Number of subdivisions per tile
- @export var transition_width := 0.4 # Width of the transition (0-1)
- @export var material: Material # Single material for the entire mesh
- var mesh_instance: MeshInstance3D
- var mesh: ArrayMesh
- var collision_shape: CollisionShape3D
- var heightmap: Array = []
- @export var camera: Camera3D
- func _ready():
- # Initialize heightmap with subdivisions
- for x in range(grid_size.x * subdivisions):
- heightmap.append([])
- for y in range(grid_size.y * subdivisions):
- heightmap[x].append(0.0)
- # Create mesh instance
- mesh_instance = MeshInstance3D.new()
- add_child(mesh_instance)
- # Assign material if provided
- if material:
- mesh_instance.material_override = material
- # Create collision shape for raycasting
- var static_body = StaticBody3D.new()
- add_child(static_body)
- collision_shape = CollisionShape3D.new()
- static_body.add_child(collision_shape)
- generate_mesh()
- func calculate_normal(x: int, z: int) -> Vector3:
- var x_prev = max(x - 1, 0)
- var x_next = min(x + 1, (grid_size.x * subdivisions) - 1)
- var z_prev = max(z - 1, 0)
- var z_next = min(z + 1, (grid_size.y * subdivisions) - 1)
- var h_left = heightmap[x_prev][z] if x_prev < heightmap.size() and z < heightmap[x_prev].size() else 0.0
- var h_right = heightmap[x_next][z] if x_next < heightmap.size() and z < heightmap[x_next].size() else 0.0
- var h_up = heightmap[x][z_prev] if x < heightmap.size() and z_prev < heightmap[x].size() else 0.0
- var h_down = heightmap[x][z_next] if x < heightmap.size() and z_next < heightmap[x].size() else 0.0
- var normal = Vector3(
- h_left - h_right,
- 2.0,
- h_up - h_down
- )
- return -normal.normalized()
- func get_smooth_height(tile_x: int, tile_y: int, sub_x: float, sub_y: float) -> float:
- var center_height = heightmap[tile_x * subdivisions][tile_y * subdivisions]
- var distance_from_center = Vector2(
- abs(sub_x - 0.5),
- abs(sub_y - 0.5)
- ).length()
- var fade = 1.0 - smoothstep(0.0, transition_width, distance_from_center)
- return center_height * fade
- func generate_mesh():
- var surface_array = []
- surface_array.resize(Mesh.ARRAY_MAX)
- var verts = PackedVector3Array()
- var uvs = PackedVector2Array()
- var indices = PackedInt32Array()
- var normals = PackedVector3Array()
- # Generate vertices with subdivisions
- for x in range(grid_size.x * subdivisions + 1):
- for z in range(grid_size.y * subdivisions + 1):
- var grid_x = x / float(subdivisions)
- var grid_z = z / float(subdivisions)
- # Calculate base tile position
- var tile_x = floor(grid_x)
- var tile_z = floor(grid_z)
- # Calculate position within tile (0-1)
- var sub_x = grid_x - tile_x
- var sub_z = grid_z - tile_z
- var height = get_smooth_height(
- min(tile_x, grid_size.x - 1),
- min(tile_z, grid_size.y - 1),
- sub_x,
- sub_z
- )
- var vertex = Vector3(
- grid_x * tile_size - (grid_size.x * tile_size) / 2,
- height,
- grid_z * tile_size - (grid_size.y * tile_size) / 2
- )
- verts.append(vertex)
- uvs.append(Vector2(grid_x / grid_size.x, grid_z / grid_size.y))
- normals.append(calculate_normal(x, z))
- # Generate indices
- var vertices_per_row = grid_size.y * subdivisions + 1
- for x in range(grid_size.x * subdivisions):
- for z in range(grid_size.y * subdivisions):
- var vertex = x * vertices_per_row + z
- indices.append(vertex)
- indices.append(vertex + vertices_per_row)
- indices.append(vertex + 1)
- indices.append(vertex + 1)
- indices.append(vertex + vertices_per_row)
- indices.append(vertex + vertices_per_row + 1)
- # Create mesh
- surface_array[Mesh.ARRAY_VERTEX] = verts
- surface_array[Mesh.ARRAY_NORMAL] = normals
- surface_array[Mesh.ARRAY_TEX_UV] = uvs
- surface_array[Mesh.ARRAY_INDEX] = indices
- mesh = ArrayMesh.new()
- mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, surface_array)
- mesh_instance.mesh = mesh
- # Update collision shape
- var collision_faces = PackedVector3Array()
- for i in range(0, indices.size(), 3):
- collision_faces.append(verts[indices[i]])
- collision_faces.append(verts[indices[i + 1]])
- collision_faces.append(verts[indices[i + 2]])
- var shape = ConcavePolygonShape3D.new()
- shape.set_faces(collision_faces)
- collision_shape.shape = shape
- func _input(event):
- if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
- var ray_length = 1000
- var from = camera.project_ray_origin(event.position)
- var to = from + camera.project_ray_normal(event.position) * ray_length
- var space_state = get_world_3d().direct_space_state
- var query = PhysicsRayQueryParameters3D.create(from, to)
- var result = space_state.intersect_ray(query)
- if result:
- var hit_pos = result.position
- var grid_pos = Vector2(
- floor((hit_pos.x + (grid_size.x * tile_size) / 2) / tile_size),
- floor((hit_pos.z + (grid_size.y * tile_size) / 2) / tile_size)
- )
- if grid_pos.x >= 0 and grid_pos.x < grid_size.x and grid_pos.y >= 0 and grid_pos.y < grid_size.y:
- var base_x = int(grid_pos.x * subdivisions)
- var base_y = int(grid_pos.y * subdivisions)
- if heightmap[base_x][base_y] == 0:
- heightmap[base_x][base_y] = max_height
- else:
- heightmap[base_x][base_y] = 0
- generate_mesh()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement