128 lines
8.3 KiB
GDScript
128 lines
8.3 KiB
GDScript
class_name UrbanArchitecture
|
|||
|
|
extends RefCounted
|
||
|
|
|
||
|
|
const FRAME := Color("343b3d")
|
||
|
|
const TRIM := Color("92938a")
|
||
|
|
const GLAZING := Color("71838a")
|
||
|
|
const WARNING := Color("c7aa64")
|
||
|
|
|
||
|
|
static func build(x: float, z: float, _tint: Color, title: String) -> Node3D:
|
||
|
|
var root := Node3D.new()
|
||
|
|
root.name = "Architecture_" + title.validate_node_name()
|
||
|
|
root.position = Vector3(x, 0, z)
|
||
|
|
var batch := UrbanMeshBatch.new()
|
||
|
|
var shop := "MARKET" in title
|
||
|
|
var workshop := "MOTOR" in title
|
||
|
|
var residence := "RELAY" in title
|
||
|
|
var render_tint := Color("b7b4a7") if workshop else Color("c5c3b9")
|
||
|
|
var accent := Color("707a75") if x < 0 else Color("827d70")
|
||
|
|
batch.bevel_box(Vector3(0, 0.055, 0), Vector3(9.08, 0.11, 11.08), "floor", Color("b9bbb6"), 0.025)
|
||
|
|
# These four wall runs align exactly with the existing collision shell.
|
||
|
|
_facade(batch, Vector3(0, 0, 5.5), 0, 9.0, 3.2, render_tint, accent, shop, residence)
|
||
|
|
_facade(batch, Vector3(0, 0, -5.5), PI, 9.0, 3.2, render_tint, accent, false, residence)
|
||
|
|
_facade(batch, Vector3(4.5, 0, 0), PI * 0.5, 11.0, 4.2, render_tint, accent, false, false)
|
||
|
|
_facade(batch, Vector3(-4.5, 0, 0), -PI * 0.5, 11.0, 4.2, render_tint, accent, false, false)
|
||
|
|
# The old sealed roof is replaced by UrbanFloors' cut-out floor slabs.
|
||
|
|
for side in [-1.0, 1.0]:
|
||
|
|
batch.cylinder(Vector3(side * 4.69, 1.77, -5.31), 0.065, 3.4, "metal", TRIM)
|
||
|
|
for y in [0.55, 1.95, 3.1]:
|
||
|
|
batch.box(Vector3(side * 4.68, y, -5.31), Vector3(0.19, 0.035, 0.18), "metal", FRAME)
|
||
|
|
_interior(batch, workshop, shop)
|
||
|
|
# Real trim and supports frame the entrances, while the opening stays clear.
|
||
|
|
var depot_name := "STORES / 03" if shop else ("MACHINE SHOP / 02" if workshop else ("PUMP HOUSE / 01" if residence else "PROCESSING / 04"))
|
||
|
|
if "SECTOR" in title:
|
||
|
|
depot_name = "LOADING / %02d" % (5 + int(x > 0) + 2 * int(z > 0))
|
||
|
|
batch.bevel_box(Vector3(0, 3.0, 5.76), Vector3(5.5, 0.43, 0.085), "paint", FRAME, 0.018)
|
||
|
|
_add_sign(root, depot_name, Vector3(0, 3.04, 5.809), 0, Color("d5d0b8"), 36, 0.007)
|
||
|
|
_add_sign(root, "QUARRY OPERATIONS / AUTHORISED PERSONNEL", Vector3(0, 2.89, 5.811), 0, TRIM, 16, 0.004)
|
||
|
|
# Powder-coated canopy with a thin metal edge rather than a solid slab.
|
||
|
|
batch.box(Vector3(0, 3.36, 5.97), Vector3(6.32, 0.09, 1.06), "metal", FRAME)
|
||
|
|
batch.box(Vector3(0, 3.3, 6.43), Vector3(6.32, 0.16, 0.055), "metal", TRIM)
|
||
|
|
for side in [-1.0, 1.0]:
|
||
|
|
batch.box(Vector3(side * 2.85, 3.18, 5.94), Vector3(0.055, 0.32, 0.7), "metal", FRAME, Vector3(0.18, 0, 0))
|
||
|
|
batch.box(Vector3(side * 3.78, 2.62, 5.73), Vector3(0.13, 0.26, 0.11), "metal", FRAME)
|
||
|
|
batch.box(Vector3(side * 3.78, 2.59, 5.8), Vector3(0.1, 0.13, 0.045), "paint", Color("dbe8df"))
|
||
|
|
# Service conduits sit on solid piers, clear of all four entries.
|
||
|
|
for side in [-1.0, 1.0]:
|
||
|
|
batch.box(Vector3(side * 4.16, 1.83, 5.715), Vector3(0.037, 2.9, 0.035), "metal", TRIM)
|
||
|
|
batch.bevel_box(Vector3(side * 4.16, 1.18, 5.79), Vector3(0.27, 0.42, 0.14), "metal", FRAME, 0.015)
|
||
|
|
batch.box(Vector3(side * 4.16, 1.25, 5.866), Vector3(0.09, 0.1, 0.008), "paint", WARNING)
|
||
|
|
_add_sign(root, "HIGH\nVOLTAGE", Vector3(side * 4.16, 1.14, 5.868), 0, TRIM, 15, 0.0025)
|
||
|
|
# Service hatch and exterior cooling compressor are above walking clearance.
|
||
|
|
batch.bevel_box(Vector3(3.31, 2.42, -5.88), Vector3(1.1, 0.59, 0.46), "metal", Color("b0b3a7"), 0.035)
|
||
|
|
batch.cylinder(Vector3(3.12, 2.42, -6.118), 0.215, 0.025, "metal", FRAME, Vector3(PI * 0.5, 0, 0))
|
||
|
|
for index in 7:
|
||
|
|
batch.box(Vector3(3.63, 2.21 + index * 0.065, -6.12), Vector3(0.34, 0.018, 0.035), "metal", FRAME)
|
||
|
|
batch.finish(root, "Facade")
|
||
|
|
var interior_light := OmniLight3D.new()
|
||
|
|
interior_light.name = "IndustrialInteriorBounce"
|
||
|
|
interior_light.position = Vector3(0, 2.8, 0)
|
||
|
|
interior_light.light_color = Color("c9dfe1")
|
||
|
|
interior_light.light_energy = 0.32
|
||
|
|
interior_light.omni_range = 6.0
|
||
|
|
interior_light.omni_attenuation = 1.6
|
||
|
|
interior_light.shadow_enabled = false
|
||
|
|
root.add_child(interior_light)
|
||
|
|
return root
|
||
|
|
|
||
|
|
static func _face_box(batch: UrbanMeshBatch, origin: Vector3, yaw: float, position: Vector3, size: Vector3, kind: String, tint: Color) -> void:
|
||
|
|
var rotation := Vector3(0, yaw, 0)
|
||
|
|
batch.box(origin + Basis.from_euler(rotation) * position, size, kind, tint, rotation)
|
||
|
|
|
||
|
|
static func _facade(batch: UrbanMeshBatch, origin: Vector3, yaw: float, width: float, opening: float, tint: Color, accent: Color, shop: bool, masonry: bool) -> void:
|
||
|
|
var panel_width := (width - opening) * 0.5
|
||
|
|
var panel_center := opening * 0.5 + panel_width * 0.5
|
||
|
|
for side in [-1.0, 1.0]:
|
||
|
|
var u: float = side * panel_center
|
||
|
|
_face_box(batch, origin, yaw, Vector3(u, 1.7, 0), Vector3(panel_width, 3.4, 0.35), "brick" if masonry else "plaster", Color("c5c0b4") if masonry else tint)
|
||
|
|
_face_box(batch, origin, yaw, Vector3(u, 0.3, 0.19), Vector3(panel_width, 0.5, 0.065), "concrete", TRIM)
|
||
|
|
var window_width := panel_width - 0.78
|
||
|
|
var window_height := 1.2 if shop else 1.34
|
||
|
|
var window_y := 1.8
|
||
|
|
_face_box(batch, origin, yaw, Vector3(u, window_y, 0.209), Vector3(window_width + 0.17, window_height + 0.16, 0.12), "metal", FRAME)
|
||
|
|
_face_box(batch, origin, yaw, Vector3(u, window_y, 0.28), Vector3(window_width, window_height, 0.035), "glass", GLAZING)
|
||
|
|
for edge in [-1.0, 1.0]:
|
||
|
|
_face_box(batch, origin, yaw, Vector3(u + edge * (window_width * 0.5 + 0.038), window_y, 0.303), Vector3(0.074, window_height + 0.12, 0.074), "metal", FRAME)
|
||
|
|
_face_box(batch, origin, yaw, Vector3(u, window_y + edge * (window_height * 0.5 + 0.035), 0.303), Vector3(window_width + 0.2, 0.072, 0.074), "metal", FRAME)
|
||
|
|
for division in [-1.0, 1.0]:
|
||
|
|
_face_box(batch, origin, yaw, Vector3(u + division * window_width / 6.0, window_y, 0.318), Vector3(0.028, window_height, 0.045), "metal", FRAME)
|
||
|
|
_face_box(batch, origin, yaw, Vector3(u, window_y, 0.318), Vector3(window_width, 0.032, 0.047), "metal", FRAME)
|
||
|
|
_face_box(batch, origin, yaw, Vector3(u, window_y - window_height * 0.5 - 0.1, 0.33), Vector3(window_width + 0.31, 0.12, 0.39), "concrete", TRIM)
|
||
|
|
# Narrow lintels, vents and accent panels break the featureless wall mass.
|
||
|
|
_face_box(batch, origin, yaw, Vector3(u, 2.96, 0.201), Vector3(panel_width - 0.27, 0.52, 0.06), "corrugated", accent)
|
||
|
|
_face_box(batch, origin, yaw, Vector3(u, 2.68, 0.235), Vector3(panel_width - 0.18, 0.075, 0.09), "metal", FRAME)
|
||
|
|
# Steel reveals and corner columns attach to the existing solid walls.
|
||
|
|
_face_box(batch, origin, yaw, Vector3(side * (opening * 0.5 + 0.035), 1.38, 0.03), Vector3(0.11, 2.76, 0.46), "metal", FRAME)
|
||
|
|
_face_box(batch, origin, yaw, Vector3(side * (width * 0.5 - 0.075), 1.7, 0.21), Vector3(0.15, 3.4, 0.115), "metal", FRAME)
|
||
|
|
_face_box(batch, origin, yaw, Vector3(side * (opening * 0.5 + 0.12), 0.62, 0.25), Vector3(0.095, 1.1, 0.02), "paint", WARNING)
|
||
|
|
_face_box(batch, origin, yaw, Vector3(0, 3.05, 0), Vector3(opening + 0.06, 0.7, 0.35), "plaster", tint)
|
||
|
|
_face_box(batch, origin, yaw, Vector3(0, 2.726, 0.03), Vector3(opening + 0.14, 0.1, 0.46), "metal", FRAME)
|
||
|
|
_face_box(batch, origin, yaw, Vector3(0, 3.31, 0.215), Vector3(width + 0.08, 0.09, 0.13), "metal", FRAME)
|
||
|
|
# Raised roller-door cassette occupies only the pre-existing lintel volume.
|
||
|
|
_face_box(batch, origin, yaw, Vector3(0, 3.035, 0.225), Vector3(opening - 0.06, 0.46, 0.095), "corrugated", accent)
|
||
|
|
# Flush transition: neither an invisible step nor a decorative door collider.
|
||
|
|
_face_box(batch, origin, yaw, Vector3(0, 0.075, 0), Vector3(opening - 0.08, 0.04, 0.48), "metal", Color("838d87"))
|
||
|
|
|
||
|
|
static func _interior(batch: UrbanMeshBatch, _workshop: bool, _shop: bool) -> void:
|
||
|
|
# The existing cover collider is dressed with a scanned generator by QuarryRealism.
|
||
|
|
# Ceiling fixture has no gameplay collision and keeps the interior readable.
|
||
|
|
batch.box(Vector3(-0.35, 3.365, 0), Vector3(0.34, 0.09, 2.35), "metal", FRAME)
|
||
|
|
for x in [-0.42, -0.28]:
|
||
|
|
batch.cylinder(Vector3(x, 3.292, 0), 0.022, 2.12, "paint", Color("e2ede5"), Vector3(PI * 0.5, 0, 0))
|
||
|
|
# Exposed joists remain wholly outside the ground-floor stairwell at x=2.
|
||
|
|
for x in [-3.83, -0.54]:
|
||
|
|
batch.box(Vector3(x, 3.46, 0), Vector3(0.12, 0.1, 10.8), "metal", FRAME)
|
||
|
|
batch.box(Vector3(x, 3.405, 0), Vector3(0.25, 0.03, 10.8), "metal", FRAME)
|
||
|
|
|
||
|
|
static func _add_sign(parent: Node3D, words: String, position: Vector3, yaw: float, color: Color, size: int, pixel: float) -> void:
|
||
|
|
var label := Label3D.new()
|
||
|
|
label.text = words
|
||
|
|
label.font_size = size
|
||
|
|
label.pixel_size = pixel
|
||
|
|
label.modulate = color
|
||
|
|
label.outline_size = 0
|
||
|
|
label.no_depth_test = false
|
||
|
|
label.position = position
|
||
|
|
label.rotation.y = yaw
|
||
|
|
parent.add_child(label)
|