feat: original kitbashed hero assets built in Blender
First bespoke art authored for the project (tools/build_hero_assets.py, headless Blender 5.1 -> assets/props/hero/): - konbini.glb: 8m convenience-store front — fascia + teal sign band with ink trim, framed windows with sills and window ads, recessed double glass door, angled awning, drink machine, hedge planter. Placed on ~1/3 of wide buildings (glass-tagged collider) - station.glb: viaduct station entrance — canopy on pillars, real stair steps with handrails and balusters, sign totem; placed at the central avenue crossing of the rail line - clock.glb: 10m plaza clock tower — tiered base, ringed column, 4 clock faces with hands, teal crown; anchors the south-west plaza cell - torii.glb: proper torii with segmented curved kasagi, shimaki, nuki beam and plaque — replaces the box torii in every shrine courtyard (invisible pillar colliders keep gameplay identical) All hero pieces run through the toon pipeline with fine ink outlines. Every part beveled; flat cel palette matches the city. Verified: city renders clean with hero pieces placed; movement 11/11, smoke 0 failures, acoustics ALL PASSED. Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Fable 5
parent
6b3e363dfd
commit
7f9a579b63
@@ -96,6 +96,28 @@ func _kit_scene(key: String) -> PackedScene:
|
||||
return _kit_cache[key]
|
||||
|
||||
|
||||
# ── Hero assets (original kitbash, tools/build_hero_assets.py) ──────────────
|
||||
|
||||
## Hero piece with a box collider. Front faces -Z before yaw (glTF Y-up).
|
||||
func _hero(key: String, pos: Vector3, yaw_deg: float, col_size: Vector3,
|
||||
col_center_y: float, acoustic: String = "concrete") -> void:
|
||||
var body := StaticBody3D.new()
|
||||
body.name = "Hero_" + key + "_%d" % _rng.randi()
|
||||
body.set_meta("acoustic_material", acoustic)
|
||||
add_child(body)
|
||||
body.global_position = pos
|
||||
body.rotation_degrees.y = yaw_deg
|
||||
var shape := CollisionShape3D.new()
|
||||
var bs := BoxShape3D.new()
|
||||
bs.size = col_size
|
||||
shape.shape = bs
|
||||
shape.position = Vector3(0, col_center_y, 0)
|
||||
body.add_child(shape)
|
||||
var inst: Node3D = _kit_scene_at("res://assets/props/hero/" + key + ".glb").instantiate()
|
||||
body.add_child(inst)
|
||||
LevelMaterials.apply_toon_recursive(inst, 0.015)
|
||||
|
||||
|
||||
## Small visual-only kit prop (parasols, awnings): no collider needed.
|
||||
func _kit_prop(key: String, pos: Vector3, yaw_deg: float, scale_f: float) -> void:
|
||||
var inst: Node3D = _kit_scene(key).instantiate()
|
||||
@@ -177,8 +199,12 @@ func _kit_fill_side(front_x: float, dir: int, z0: float, z1: float, seed_id: Str
|
||||
var h: float = KIT_BUILDINGS[key].y * KIT_SCALE
|
||||
_kit_building(key, Vector3(front_x, 0, zc), yaw, "K%s_%d" % [seed_id, idx])
|
||||
var face_yaw := -90.0 if dir > 0 else 90.0 # ad quads face the street
|
||||
# Storefront awning on some buildings (kit detail piece)
|
||||
if _rng.randi() % 5 < 2:
|
||||
# Kitbashed konbini storefront module on wide buildings
|
||||
if w >= 12.0 and _rng.randi() % 3 == 0:
|
||||
_hero("konbini", Vector3(front_x - float(dir) * 0.75, 0.02, zc), 90.0 * float(dir),
|
||||
Vector3(8.2, 4.2, 1.4), 2.1, "glass")
|
||||
# Storefront awning on some other buildings (kit detail piece)
|
||||
elif _rng.randi() % 5 < 2:
|
||||
_kit_prop_path(KIT_DIR + "detail-awning-wide.glb",
|
||||
Vector3(front_x, 3.1, zc), face_yaw, minf(w * 0.9, 11.0))
|
||||
# Street-level posters plastered on the facade (graphic density)
|
||||
@@ -501,12 +527,11 @@ func _shrine_block(c: Vector3, bx: int, bz: int) -> void:
|
||||
_box_static(c + Vector3(0.0 if horiz else off, 0.7, off if horiz else 0.0),
|
||||
Vector3(BLOCK - 6.0 if horiz else 0.6, 1.4, 0.6 if horiz else BLOCK - 6.0),
|
||||
Color(0.55, 0.55, 0.52), n + "_Wall%d" % i, "brick")
|
||||
# Torii gate at the south entry
|
||||
# Kitbashed torii (curved kasagi) at the south entry; pillar colliders
|
||||
var tz := c.z + (BLOCK - 6.0) * 0.5
|
||||
_box_static(c + Vector3(-4.0, 3.5, tz), Vector3(0.8, 7.0, 0.8), VERMILION, n + "_PillarW", "wood")
|
||||
_box_static(c + Vector3(4.0, 3.5, tz), Vector3(0.8, 7.0, 0.8), VERMILION, n + "_PillarE", "wood")
|
||||
_box_static(c + Vector3(0, 7.0, tz), Vector3(11.0, 0.7, 1.1), VERMILION, n + "_BeamTop", "wood")
|
||||
_box_static(c + Vector3(0, 5.8, tz), Vector3(9.4, 0.5, 0.9), VERMILION.darkened(0.15), n + "_BeamLow", "wood")
|
||||
_hero("torii", c + Vector3(0, 0.1, tz), 0.0, Vector3(0.1, 0.1, 0.1), 0.05, "wood")
|
||||
_box_static(c + Vector3(-4.0, 3.5, tz), Vector3(0.9, 7.0, 0.9), VERMILION, n + "_PillarW", "wood").visible = false
|
||||
_box_static(c + Vector3(4.0, 3.5, tz), Vector3(0.9, 7.0, 0.9), VERMILION, n + "_PillarE", "wood").visible = false
|
||||
# Hall with slide roof
|
||||
_box_static(c + Vector3(0, 2.0, -10.0), Vector3(12.0, 4.0, 8.0), WOOD.darkened(0.2), n + "_Hall", "wood")
|
||||
_ramp_static(c + Vector3(0, 4.9, -8.2), Vector3(13.0, 0.3, 5.6), Vector3(-24, 0, 0), VERMILION.darkened(0.35), n + "_Roof", "wood")
|
||||
@@ -571,6 +596,9 @@ func _plaza_block(c: Vector3, bx: int, bz: int) -> void:
|
||||
var p := c + Vector3(cos(a), 0, sin(a)) * (BLOCK * 0.32)
|
||||
_box_static(Vector3(p.x, 0.55, p.z), Vector3(4.0, 1.1, 4.0), Color(0.5, 0.48, 0.52), "%s_Pl%d" % [n, i], "brick")
|
||||
_kit_prop_path("res://assets/props/nature/tree_oak.glb", Vector3(p.x, 1.0, p.z), float(i) * 90.0, 4.5)
|
||||
# Kitbashed clock tower on the south-west plaza cell
|
||||
if bx == 4 and bz == 5:
|
||||
_hero("clock", c + Vector3(0, 0.1, 0), 0.0, Vector3(3.2, 10.5, 3.2), 5.25, "concrete")
|
||||
# The Crossing screen tower on the single centre-most plaza cell
|
||||
if bx == 5 and bz == 4:
|
||||
_box_static(c + Vector3(0, 9.0, 0), Vector3(6.0, 18.0, 6.0), METAL.darkened(0.2), n + "_ScreenTower", "metal")
|
||||
@@ -637,6 +665,10 @@ func _build_rail_line() -> void:
|
||||
_ad_quad(Vector3(x - 1.35, 2.0, z), Vector2(1.2, 1.8), -90.0, _poster_tex())
|
||||
i += 1
|
||||
x += PITCH
|
||||
# Kitbashed station entrance beside the central avenue crossing
|
||||
_hero("station", Vector3(ROAD_W * 0.5 + 8.0, 0.02, z + 7.5), 90.0,
|
||||
Vector3(6.0, 3.6, 5.0), 1.8, "concrete")
|
||||
|
||||
# Slide ramps to the deck at the two main-avenue crossings
|
||||
for sx in [-PITCH * 0.5 - 4.0, PITCH * 0.5 + 4.0]:
|
||||
var ang := rad_to_deg(atan2(RAIL_TOP, 16.0))
|
||||
|
||||
Reference in New Issue
Block a user