Files

140 lines
9.6 KiB
GDScript

class_name UrbanVehicles
extends RefCounted
const DARK := Color("252a2a")
const RUBBER := Color("191d1d")
const ALLOY := Color("a2aaa8")
const WINDOW := Color("9aafb3")
static func build(tint: Color, long_vehicle: bool = false) -> Node3D:
var root := Node3D.new()
root.name = "CourierVan" if long_vehicle else "UrbanSaloon"
var batch := UrbanMeshBatch.new()
if long_vehicle:
_van(batch, tint)
else:
_sedan(batch, tint)
batch.finish(root, "Bodywork")
return root
static func _sedan(batch: UrbanMeshBatch, tint: Color) -> void:
# Chamfered sill, bonnet and boot create a readable automotive silhouette.
batch.profile(PackedVector2Array([Vector2(-2.28, 0.38), Vector2(2.22, 0.38),
Vector2(2.3, 0.56), Vector2(2.24, 0.98), Vector2(1.77, 1.15),
Vector2(-1.53, 1.17), Vector2(-2.2, 1.0), Vector2(-2.3, 0.67)]), 2.29, Vector3.ZERO, "paint", tint, 0.09)
batch.profile(PackedVector2Array([Vector2(-1.62, 1.02), Vector2(1.22, 1.02),
Vector2(1.16, 1.36), Vector2(0.65, 1.91), Vector2(-0.76, 1.91),
Vector2(-1.62, 1.14)]), 2.04, Vector3.ZERO, "paint", tint, 0.095)
# The bonnet has a shallow crown, with physical shut lines and a grille.
batch.bevel_box(Vector3(0, 1.08, -1.91), Vector3(1.95, 0.073, 0.59), "paint", tint.lightened(0.035), 0.026)
batch.bevel_box(Vector3(0, 0.56, -2.255), Vector3(1.98, 0.24, 0.11), "rubber", DARK, 0.032)
batch.bevel_box(Vector3(0, 0.75, -2.292), Vector3(0.93, 0.22, 0.055), "metal", DARK, 0.02)
for slat in 5:
batch.box(Vector3(0, 0.67 + slat * 0.035, -2.326), Vector3(0.85, 0.012, 0.021), "metal", ALLOY.darkened(0.26))
batch.box(Vector3(0, 0.94, -2.27), Vector3(0.14, 0.1, 0.035), "metal", ALLOY)
batch.bevel_box(Vector3(0, 0.48, 2.26), Vector3(2.05, 0.17, 0.1), "rubber", DARK, 0.024)
# Windshield and backlight follow the body slope instead of a vertical box.
batch.quad(Vector3(-0.89, 1.23, -1.51), Vector3(0.89, 1.23, -1.51),
Vector3(0.835, 1.822, -0.823), Vector3(-0.835, 1.822, -0.823), "glass", WINDOW, true)
batch.quad(Vector3(0.86, 1.401, 1.12), Vector3(-0.86, 1.401, 1.12),
Vector3(-0.82, 1.819, 0.722), Vector3(0.82, 1.819, 0.722), "glass", WINDOW, true)
for side in [-1.0, 1.0]:
var x: float = side * 1.025
batch.quad(Vector3(x, 1.2, -1.38), Vector3(x, 1.2, -0.19),
Vector3(x, 1.779, -0.19), Vector3(x, 1.779, -0.7), "glass", WINDOW, true)
batch.quad(Vector3(x, 1.2, -0.085), Vector3(x, 1.2, 1.045),
Vector3(x, 1.779, 0.57), Vector3(x, 1.779, -0.085), "glass", WINDOW, true)
batch.box(Vector3(side * 1.087, 1.154, -0.18), Vector3(0.028, 0.04, 2.55), "metal", ALLOY.darkened(0.17))
# Door seams, flush handles, side skirts and indicators.
for z in [-1.37, -0.12, 1.18]:
batch.box(Vector3(side * 1.151, 0.84, z), Vector3(0.015, 0.46, 0.014), "rubber", DARK)
for z in [-0.39, 0.87]:
batch.bevel_box(Vector3(side * 1.158, 1.022, z), Vector3(0.027, 0.058, 0.22), "metal", ALLOY, 0.008)
batch.bevel_box(Vector3(side * 1.142, 0.44, 0), Vector3(0.1, 0.11, 3.82), "rubber", DARK, 0.028)
batch.box(Vector3(side * 1.065, 1.262, -1.14), Vector3(0.2, 0.055, 0.07), "rubber", DARK, Vector3(0, side * 0.3, 0))
batch.bevel_box(Vector3(side * 1.205, 1.304, -1.12), Vector3(0.25, 0.145, 0.29), "paint", tint, 0.04)
batch.box(Vector3(side * 1.22, 1.305, -0.969), Vector3(0.185, 0.096, 0.021), "glass", Color.WHITE)
batch.bevel_box(Vector3(side * 0.8, 0.9, -2.252), Vector3(0.49, 0.175, 0.073), "glass", Color("d9e3d5"), 0.025)
batch.box(Vector3(side * 0.81, 0.966, -2.299), Vector3(0.4, 0.033, 0.019), "paint", Color("f2e9ca"))
batch.bevel_box(Vector3(side * 0.82, 0.895, 2.263), Vector3(0.49, 0.175, 0.068), "paint", Color("993b31"), 0.022)
batch.box(Vector3(side * 0.82, 0.915, 2.304), Vector3(0.37, 0.035, 0.013), "paint", Color("d96348"))
batch.cylinder(Vector3(side * 0.75, 0.35, 2.245), 0.072, 0.25, "metal", ALLOY, Vector3(PI * 0.5, 0, 0))
for axle in [-1.48, 1.44]:
_wheel(batch, side, axle, 0.4)
_number_plate(batch, Vector3(0, 0.593, -2.331))
_number_plate(batch, Vector3(0, 0.8, 2.317))
# Two subtle windscreen wipers, with no animated nodes per parked car.
for side in [-1.0, 1.0]:
batch.box(Vector3(side * 0.43, 1.259, -1.465), Vector3(0.54, 0.024, 0.028), "rubber", DARK, Vector3(0, side * 0.14, 0))
batch.cylinder(Vector3(0, 1.98, 0.49), 0.035, 0.15, "rubber", DARK, Vector3(0.25, 0, 0), 0.012)
static func _van(batch: UrbanMeshBatch, tint: Color) -> void:
# A compact courier van keeps the original cover height and long footprint.
batch.profile(PackedVector2Array([Vector2(-3.2, 0.38), Vector2(3.17, 0.38),
Vector2(3.25, 0.57), Vector2(3.2, 1.16), Vector2(-2.36, 1.2),
Vector2(-3.19, 1.01), Vector2(-3.25, 0.61)]), 2.29, Vector3.ZERO, "paint", tint, 0.09)
batch.profile(PackedVector2Array([Vector2(-2.13, 1.0), Vector2(1.53, 1.0),
Vector2(1.5, 1.81), Vector2(1.39, 1.9), Vector2(-1.47, 1.9),
Vector2(-2.13, 1.32)]), 2.04, Vector3.ZERO, "paint", tint.lightened(0.035), 0.075)
# An exposed loading bed behind the cab explains the long, low silhouette.
batch.bevel_box(Vector3(0, 1.17, 2.28), Vector3(2.13, 0.085, 1.8), "rubber", Color("424a45"), 0.03)
for side in [-1.0, 1.0]:
batch.box(Vector3(side * 1.06, 1.25, 2.37), Vector3(0.1, 0.19, 1.64), "paint", tint)
batch.box(Vector3(side * 1.12, 0.67, 0.3), Vector3(0.09, 0.095, 4.76), "rubber", DARK)
batch.box(Vector3(side * 1.026, 1.456, 0.69), Vector3(0.015, 0.6, 0.018), "rubber", DARK)
batch.box(Vector3(side * 1.047, 1.265, 0.37), Vector3(0.04, 0.055, 0.24), "rubber", DARK)
batch.box(Vector3(side * 1.026, 1.739, 0.3), Vector3(0.035, 0.043, 2.03), "metal", ALLOY.darkened(0.24))
batch.quad(Vector3(side * 1.025, 1.284, -1.98), Vector3(side * 1.025, 1.284, -0.71),
Vector3(side * 1.025, 1.774, -0.71), Vector3(side * 1.025, 1.774, -1.455), "glass", WINDOW, true)
batch.box(Vector3(side * 1.07, 1.277, -0.56), Vector3(0.025, 0.049, 0.19), "rubber", DARK)
batch.box(Vector3(side * 1.14, 1.43, -1.82), Vector3(0.26, 0.052, 0.055), "metal", DARK)
batch.bevel_box(Vector3(side * 1.27, 1.47, -1.8), Vector3(0.19, 0.24, 0.25), "rubber", DARK, 0.045)
batch.box(Vector3(side * 1.28, 1.48, -1.665), Vector3(0.137, 0.174, 0.02), "glass", Color.WHITE)
batch.bevel_box(Vector3(side * 0.83, 0.89, -3.207), Vector3(0.4, 0.23, 0.09), "glass", Color("d3d9cb"), 0.024)
batch.box(Vector3(side * 0.85, 0.912, -3.26), Vector3(0.29, 0.033, 0.019), "paint", Color("efe3bb"))
batch.bevel_box(Vector3(side * 0.96, 0.91, 3.203), Vector3(0.19, 0.27, 0.055), "paint", Color("a33c31"), 0.02)
for axle in [-2.16, 2.08]:
_wheel(batch, side, axle, 0.43)
batch.quad(Vector3(-0.9, 1.391, -2.066), Vector3(0.9, 1.391, -2.066),
Vector3(0.86, 1.809, -1.544), Vector3(-0.86, 1.809, -1.544), "glass", WINDOW, true)
batch.bevel_box(Vector3(0, 0.56, -3.243), Vector3(2.15, 0.22, 0.11), "rubber", DARK, 0.035)
batch.bevel_box(Vector3(0, 0.81, -3.234), Vector3(1.11, 0.25, 0.084), "rubber", DARK, 0.028)
for slat in 5:
batch.box(Vector3(0, 0.715 + slat * 0.04, -3.282), Vector3(0.99, 0.018, 0.019), "metal", ALLOY.darkened(0.22))
batch.box(Vector3(0, 0.88, 3.256), Vector3(1.77, 0.045, 0.025), "metal", ALLOY)
batch.box(Vector3(0, 0.47, 3.266), Vector3(1.96, 0.14, 0.1), "rubber", DARK)
_number_plate(batch, Vector3(0, 0.535, -3.308))
_number_plate(batch, Vector3(0, 0.72, 3.29))
# Thin roof rails and a ribbed loading surface give the work vehicle purpose.
for side in [-1.0, 1.0]:
batch.cylinder(Vector3(side * 0.77, 1.952, -0.08), 0.03, 2.47, "metal", DARK, Vector3(PI * 0.5, 0, 0))
for z in [-1.12, 0.96]:
batch.box(Vector3(side * 0.77, 1.923, z), Vector3(0.085, 0.11, 0.14), "rubber", DARK)
for rib in range(-4, 5):
batch.box(Vector3(rib * 0.2, 1.225, 2.35), Vector3(0.022, 0.022, 1.57), "metal", Color("626d65"))
batch.box(Vector3(0, 1.251, 3.15), Vector3(2.12, 0.19, 0.11), "paint", tint)
static func _wheel(batch: UrbanMeshBatch, side: float, z: float, radius: float) -> void:
var x := side * 1.132
# Dark wheel-well backing, rounded tyre shoulders, brake disc and five spokes.
batch.cylinder(Vector3(side * 1.156, radius + 0.02, z), radius * 1.13, 0.025, "rubber", Color("101615"), Vector3(0, 0, PI * 0.5))
batch.cylinder(Vector3(x, radius, z), radius, 0.235, "rubber", RUBBER, Vector3(0, 0, PI * 0.5))
batch.cylinder(Vector3(side * 1.26, radius, z), radius * 0.94, 0.043, "rubber", Color("242a27"), Vector3(0, 0, PI * 0.5), radius * 0.89)
batch.cylinder(Vector3(side * 1.286, radius, z), radius * 0.7, 0.038, "metal", ALLOY, Vector3(0, 0, PI * 0.5))
batch.cylinder(Vector3(side * 1.309, radius, z), radius * 0.58, 0.02, "metal", Color("36423f"), Vector3(0, 0, PI * 0.5))
batch.cylinder(Vector3(side * 1.328, radius, z), radius * 0.23, 0.042, "metal", ALLOY, Vector3(0, 0, PI * 0.5))
for spoke in 5:
var angle := TAU * spoke / 5.0
batch.box(Vector3(side * 1.33, radius + cos(angle) * radius * 0.37, z + sin(angle) * radius * 0.37), Vector3(0.035, radius * 0.7, 0.055), "metal", ALLOY, Vector3(angle, 0, 0))
batch.cylinder(Vector3(side * 1.356, radius + cos(angle) * 0.054, z + sin(angle) * 0.054), 0.018, 0.015, "metal", DARK, Vector3(0, 0, PI * 0.5))
# Tread blocks give tyres side detail without dozens of separate scene nodes.
for tread in 20:
var angle := TAU * tread / 20.0
batch.box(Vector3(x, radius + cos(angle) * radius * 0.975, z + sin(angle) * radius * 0.975), Vector3(0.2, 0.025, 0.041), "rubber", Color("292e2a"), Vector3(angle, 0, 0))
static func _number_plate(batch: UrbanMeshBatch, position: Vector3) -> void:
batch.bevel_box(position, Vector3(0.53, 0.123, 0.028), "paint", Color("d4d4ba"), 0.011)
batch.box(position + Vector3(-0.229, 0, -0.017 if position.z < 0 else 0.017), Vector3(0.052, 0.105, 0.009), "paint", Color("446677"))
for digit in 6:
batch.box(position + Vector3(-0.15 + digit * 0.057, 0, -0.019 if position.z < 0 else 0.019), Vector3(0.019, 0.06, 0.009), "paint", Color("43534e"))