@tool extends Polygon2D var _hash: int @export var _floor_mesh: MeshInstance3D @export var _wall_mesh: MeshInstance3D @export var _scale: float = 0.1 @export var _seed: int func _ready() -> void: if (script_changed.connect(_geometry_update) != OK): push_error("Cannot connect _geometry_update") # No idea how to make it getter, but whatever. func _process(_delta: float) -> void: var h := Array(polygon).hash() + hash(_scale) + hash(_seed) if h != _hash: _geometry_update() _hash = h func _geometry_update() -> void: print("Rebuilding level: ", name) if polygon.size() <= 2: return _prepare_polygons() _prepare_mesh() func _prepare_polygons() -> void: var polys := Geometry2D.triangulate_delaunay(polygon) var pickled := Array() for i in polys.size() / 3: pickled.push_back(PackedInt32Array([polys[i * 3 + 0], polys[i * 3 + 1], polys[i * 3 + 2]])) polygons = pickled _fix_polygons() func _fix_polygons() -> void: # Outside triangles are in reversed winding, find and eliminate. var new := Array() for poly in polygons as Array[PackedInt32Array]: var ab := polygon[poly[1]] - polygon[poly[0]] var ac := polygon[poly[2]] - polygon[poly[0]] if (ab.x * ac.y - ab.y * ac.x) * 0.5 <= 0: # Fix winding as well. new.push_back(PackedInt32Array([poly[2],poly[1],poly[0]])) polygons = new func _prepare_mesh() -> void: _prepare_floor_mesh() _prepare_wall_mesh() _prepare_floor_collision() _prepare_wall_collision() func _prepare_floor_mesh() -> void: var m := ImmediateMesh.new() m.surface_begin(Mesh.PRIMITIVE_LINES) for poly in polygons as Array[PackedInt32Array]: m.surface_add_vertex(Vector3(polygon[poly[0]].x * _scale, 0, polygon[poly[0]].y * _scale)) m.surface_add_vertex(Vector3(polygon[poly[1]].x * _scale, 0, polygon[poly[1]].y * _scale)) m.surface_add_vertex(Vector3(polygon[poly[2]].x * _scale, 0, polygon[poly[2]].y * _scale)) m.surface_end() m.surface_begin(Mesh.PRIMITIVE_TRIANGLES) for poly in polygons as Array[PackedInt32Array]: m.surface_set_uv(Vector2(polygon[poly[0]].x * _scale, polygon[poly[0]].y * _scale)) m.surface_add_vertex(Vector3(polygon[poly[0]].x * _scale, 0, polygon[poly[0]].y * _scale)) m.surface_set_uv(Vector2(polygon[poly[1]].x * _scale, polygon[poly[1]].y * _scale)) m.surface_add_vertex(Vector3(polygon[poly[1]].x * _scale, 0, polygon[poly[1]].y * _scale)) m.surface_set_uv(Vector2(polygon[poly[2]].x * _scale, polygon[poly[2]].y * _scale)) m.surface_add_vertex(Vector3(polygon[poly[2]].x * _scale, 0, polygon[poly[2]].y * _scale)) m.surface_end() _floor_mesh.mesh = m # TODO: Make last segment UV stretch to edge, tiling it. func _prepare_wall_mesh() -> void: var m := ImmediateMesh.new() m.surface_begin(Mesh.PRIMITIVE_TRIANGLES) var x := 0 var rng := RandomNumberGenerator.new() var dy := rng.randf_range(-1.0, 1.0) var ndy := rng.randf_range(-1.0, 1.0) rng.seed = _seed for ld in polygon.size(): var nd := (ld + 1) % polygon.size() var nnd := (ld + 2) % polygon.size() var nx := x + polygon[ld].distance_to(polygon[nd]) # Middle displacement var depth := Vector3(-(polygon[nd].y - polygon[ld].y) * _scale, 0, (polygon[nd].x - polygon[ld].x) * _scale) * 0.2 var depth2 := Vector3(-(polygon[nnd].y - polygon[nd].y) * _scale, 0, (polygon[nnd].x - polygon[nd].x) * _scale) * 0.2 # Lower half m.surface_set_uv(Vector2(x * _scale, 0)) m.surface_add_vertex(Vector3(polygon[ld].x * _scale, 0, polygon[ld].y * _scale)) m.surface_set_uv(Vector2(nx * _scale, 0)) m.surface_add_vertex(Vector3(polygon[nd].x * _scale, 0, polygon[nd].y * _scale)) m.surface_set_uv(Vector2(x * _scale, 3)) m.surface_add_vertex(Vector3(polygon[ld].x * _scale, 2 + dy, polygon[ld].y * _scale) + depth) m.surface_set_uv(Vector2(nx * _scale, 0)) m.surface_add_vertex(Vector3(polygon[nd].x * _scale, 0, polygon[nd].y * _scale)) m.surface_set_uv(Vector2(nx * _scale, 3)) m.surface_add_vertex(Vector3(polygon[nd].x * _scale, 2 + ndy, polygon[nd].y * _scale) + depth2) m.surface_set_uv(Vector2(x * _scale, 3)) m.surface_add_vertex(Vector3(polygon[ld].x * _scale, 2 + dy, polygon[ld].y * _scale) + depth) # Upper half m.surface_set_uv(Vector2(x * _scale, 0)) m.surface_add_vertex(Vector3(polygon[ld].x * _scale, 2 + dy, polygon[ld].y * _scale) + depth) m.surface_set_uv(Vector2(nx * _scale, 0)) m.surface_add_vertex(Vector3(polygon[nd].x * _scale, 2 + ndy, polygon[nd].y * _scale) + depth2) m.surface_set_uv(Vector2(x * _scale, 3)) m.surface_add_vertex(Vector3(polygon[ld].x * _scale, 5, polygon[ld].y * _scale)) m.surface_set_uv(Vector2(nx * _scale, 0)) m.surface_add_vertex(Vector3(polygon[nd].x * _scale, 2 + ndy, polygon[nd].y * _scale) + depth2) m.surface_set_uv(Vector2(nx * _scale, 3)) m.surface_add_vertex(Vector3(polygon[nd].x * _scale, 5, polygon[nd].y * _scale)) m.surface_set_uv(Vector2(x * _scale, 3)) m.surface_add_vertex(Vector3(polygon[ld].x * _scale, 5, polygon[ld].y * _scale)) dy = ndy ndy = rng.randf_range(-1.0, 1.0) m.surface_end() _wall_mesh.mesh = m func _prepare_floor_collision() -> void: if _floor_mesh.get_node_or_null("FloorMesh_col"): _floor_mesh.remove_child(_floor_mesh.get_node("FloorMesh_col")) _floor_mesh.create_convex_collision() (_floor_mesh.get_node("FloorMesh_col") as StaticBody3D).set_collision_layer_value(2, 1) func _prepare_wall_collision() -> void: if _wall_mesh.get_node_or_null("WallMesh_col"): _wall_mesh.remove_child(_wall_mesh.get_node("WallMesh_col")) _wall_mesh.create_trimesh_collision() _wall_mesh.get_node("WallMesh_col").get_node("CollisionShape3D").shape.backface_collision = true (_wall_mesh.get_node("WallMesh_col") as StaticBody3D).set_collision_layer_value(3, 1) #var shape := ConcavePolygonShape3D.new() #shape.backface_collision = true #var faces := PackedVector3Array() #for ld in polygon.size(): #var nd := (ld + 1) % polygon.size() #faces.push_back(Vector3(polygon[ld].x * _scale, 0, polygon[ld].y * _scale)) #faces.push_back(Vector3(polygon[nd].x * _scale, 0, polygon[nd].y * _scale)) #faces.push_back(Vector3(polygon[ld].x * _scale, 5, polygon[ld].y * _scale)) #faces.push_back(Vector3(polygon[nd].x * _scale, 0, polygon[nd].y * _scale)) #faces.push_back(Vector3(polygon[nd].x * _scale, 5, polygon[nd].y * _scale)) #faces.push_back(Vector3(polygon[ld].x * _scale, 5, polygon[ld].y * _scale)) #shape.set_faces(faces) #(_wall_mesh.get_node("Area3D").get_node("CollisionShape3D") as CollisionShape3D).shape = shape