FLOORED LAZZA 1.5: John's actual first car, '82 KA Laser hatch
Built from his photos: white 5-door wedge, black bumpers + belt-line rubbing strip, amber corner indicators, wide dark grille bezel (pale headlights vanish on white paint without it), and the crown jewel -- rear hatch louvres, five slats that shed one by one on impact like a dropped pie. New optional spec keys: louvres, indicators, side_strip, wide_grille; build_cars.py now takes `-- <id>` to rebuild a single car; screenshot harness gains cam=rear. Reversed-stats rule applied: 54 kW carby IRL -> 21.0 power / 860 kg / 7.8 grip here. Second-quickest thing in the game behind the Excel, and the chuckable-est. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
da63829e9e
commit
81ed13b1ff
@ -46,8 +46,10 @@ Model convention (any GLB works, hand-modelled or generated):
|
||||
- Node origin 0.75 m above ground level (the controller's ride height).
|
||||
- Objects named `det_*` are detachable — the game rips them off on impact
|
||||
(doors, mirrors, bumpers, bonnet, boot).
|
||||
- Current fleet: Datto 120Y, Gemmy TX, Kingswood HQ, VL Terbo, XF Fungoon, Excel Hilton.
|
||||
Piss-take names on purpose: real model names and badges are trademarks.
|
||||
- Current fleet: 30 shitboxes, from the Datto 120Y to the FLOORED LAZZA 1.5 ('82
|
||||
Laser hatch, rear louvres that shed slat-by-slat — John's actual first car,
|
||||
built from his photos). Piss-take names on purpose: real model names and
|
||||
badges are trademarks.
|
||||
|
||||
To improve a silhouette: supply a side-on photo + front three-quarter photo, tweak the
|
||||
profile points in the spec, rebuild, screenshot.
|
||||
|
||||
BIN
cars/lazza_ka/car.glb
Normal file
BIN
cars/lazza_ka/car.glb
Normal file
Binary file not shown.
45
cars/lazza_ka/car.glb.import
Normal file
45
cars/lazza_ka/car.glb.import
Normal file
@ -0,0 +1,45 @@
|
||||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://bbottmecxqhu1"
|
||||
path="res://.godot/imported/car.glb-edc9533c079c72a53d09ab51d6c7dcf4.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://cars/lazza_ka/car.glb"
|
||||
dest_files=["res://.godot/imported/car.glb-edc9533c079c72a53d09ab51d6c7dcf4.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type=""
|
||||
nodes/root_name=""
|
||||
nodes/root_script=null
|
||||
mesh_library/use_node_names_as_mesh_names=false
|
||||
array_mesh/deduplicate_surfaces=true
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_name_suffixes=true
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=false
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
materials/extract=0
|
||||
materials/extract_format=0
|
||||
materials/extract_path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=2
|
||||
gltf/embedded_image_handling=1
|
||||
gltf/texture_map_mode=1
|
||||
13
cars/lazza_ka/stats.tres
Normal file
13
cars/lazza_ka/stats.tres
Normal file
@ -0,0 +1,13 @@
|
||||
[gd_resource type="Resource" script_class="CarStats" load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://src/car_stats.gd" id="1"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1")
|
||||
display_name = "FLOORED LAZZA 1.5"
|
||||
model_path = "res://cars/lazza_ka/car.glb"
|
||||
engine_power = 21.0
|
||||
mass = 860.0
|
||||
grip = 7.8
|
||||
length = 3.96
|
||||
width = 1.61
|
||||
@ -35,6 +35,12 @@ func _run() -> void:
|
||||
game.cam.look_at(game.car.global_position + Vector3.UP * 0.4)
|
||||
await process_frame
|
||||
await process_frame
|
||||
elif opts.get("cam", "") == "rear":
|
||||
game.set_process(false)
|
||||
game.cam.global_position = game.car.global_position + game.car.global_basis * Vector3(2.6, 1.6, 6.5)
|
||||
game.cam.look_at(game.car.global_position + Vector3.UP * 0.4)
|
||||
await process_frame
|
||||
await process_frame
|
||||
elif opts.get("cam", "") == "top":
|
||||
game.set_process(false)
|
||||
var alt := float(opts.get("alt", "150"))
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
"""Procedural Aussie shitbox factory.
|
||||
|
||||
Run: /Applications/Blender.app/Contents/MacOS/Blender -b -P tools/build_cars.py
|
||||
(append `-- <id> [<id>...]` to rebuild only those cars)
|
||||
|
||||
Each car: side-silhouette profile extruded across the width (body + greenhouse),
|
||||
cylinder wheels, and separate det_* panel objects (doors, mirrors, bumpers,
|
||||
@ -15,6 +16,7 @@ side-on photos per car when a shape needs to get closer.
|
||||
"""
|
||||
import os
|
||||
import math
|
||||
import sys
|
||||
import bpy
|
||||
import bmesh
|
||||
from mathutils import Matrix
|
||||
@ -30,7 +32,9 @@ LIGHT_R = (0.7, 0.1, 0.08, 1)
|
||||
|
||||
# L, W, heights: nose/belt/tail, lengths: bonnet/boot, roof height + rear roof end,
|
||||
# axle overhangs, wheel radius, paint, chrome bumpers, hatch rear.
|
||||
# Optional: doors=2, bonnet_color (two-tone accent), roof_cap (vinyl roof), spoiler.
|
||||
# Optional: doors=2, bonnet_color (two-tone accent), roof_cap (vinyl roof), spoiler,
|
||||
# louvres (hatch-glass slat stack, each slat sheds on impact), indicators (amber
|
||||
# corner blinkers), side_strip (black rubbing strip along the belt line).
|
||||
CARS = {
|
||||
"datto_120y": dict(L=3.92, W=1.50, nose=0.62, belt=0.78, tail=0.72,
|
||||
bonnet=1.00, boot=0.85, roof=1.32, roof_rear=0.75,
|
||||
@ -84,6 +88,13 @@ CARS = {
|
||||
ohang_f=0.85, ohang_r=0.95, wheel=0.29,
|
||||
paint=(0.70, 0.72, 0.58, 1), chrome_bumpers=True, hatch=False,
|
||||
doors=2, van=True),
|
||||
# John's actual first car: '82 KA Laser 1.5 hatch, white, rear louvres.
|
||||
# Gutless IRL (54 kW carby) -> reversed-stats rule makes it a weapon here.
|
||||
"lazza_ka": dict(L=3.96, W=1.61, nose=0.58, belt=0.79, tail=0.90,
|
||||
bonnet=1.02, boot=0.35, roof=1.37, roof_rear=0.72,
|
||||
ohang_f=0.79, ohang_r=0.72, wheel=0.26,
|
||||
paint=(0.92, 0.92, 0.89, 1), chrome_bumpers=False, hatch=True,
|
||||
louvres=True, indicators=True, side_strip=True, wide_grille=True),
|
||||
# mokes use the open-tub builder: style="moke"
|
||||
"mini_merk": dict(style="moke", L=3.05, W=1.42, nose=0.60, tub=0.66, canopy=1.38,
|
||||
bonnet=0.85, ohang_f=0.55, ohang_r=0.55, wheel=0.26,
|
||||
@ -215,6 +226,19 @@ def build(car_id, s):
|
||||
parts.append(box("det_bumper_f", (0, f + 0.05, bottom + 0.16), (W + 0.06, 0.15, 0.16), bumper_mat))
|
||||
parts.append(box("det_bumper_r", (0, r - 0.05, bottom + 0.16), (W + 0.06, 0.15, 0.16), bumper_mat))
|
||||
|
||||
if s.get("louvres") and s["hatch"]:
|
||||
# venetian-blind slat stack over the hatch glass; every slat is its own
|
||||
# det_ part, so a rear-ender sheds them one by one like a dropped pie
|
||||
ty, tz = boot_y + s["roof_rear"], s["roof"] # glass top edge
|
||||
ly, lz = rr_y, rr_z # glass bottom edge
|
||||
g_ang = math.atan2(tz - lz, ty - ly)
|
||||
for i in range(5):
|
||||
t = 0.14 + i * 0.18
|
||||
sy = ty + (ly - ty) * t
|
||||
sz = tz + (lz - tz) * t + 0.045
|
||||
parts.append(box("det_louvre_%d" % i, (0, sy, sz),
|
||||
(W * 0.80, 0.20, 0.022), dark, rot_x=-g_ang * 0.45))
|
||||
|
||||
if s.get("van"): # cargo box from cab rear to tail, plus rip-off tailgate
|
||||
parts.append(box("van_box", (0, (boot_y + r) / 2, (s["belt"] + s["roof"]) / 2),
|
||||
(W * 0.94, boot_y - r - 0.05, s["roof"] - s["belt"]), paint))
|
||||
@ -240,13 +264,23 @@ def build(car_id, s):
|
||||
(W * 0.86, cap_f - cap_r + 0.1, 0.035), material("vinyl", s["roof_cap"], rough=0.9)))
|
||||
if s.get("spoiler"): # bootlid ducktail
|
||||
trim.append(box("trim_spoiler", (0, r + 0.22, s["tail"] + 0.05), (W * 0.8, 0.28, 0.09), paint))
|
||||
grille_w = W * (0.78 if s.get("wide_grille") else 0.5) # wide = lights sit in a dark bezel band
|
||||
trim += [
|
||||
box("trim_grille", (0, f + 0.01, s["nose"] - 0.14), (W * 0.5, 0.05, 0.12), dark),
|
||||
box("trim_grille", (0, f + 0.01, s["nose"] - 0.14), (grille_w, 0.05, 0.14), dark),
|
||||
box("trim_light_fl", (-W * 0.33, f + 0.02, s["nose"] - 0.13), (0.24, 0.05, 0.13), material("lf", LIGHT_F)),
|
||||
box("trim_light_fr", (W * 0.33, f + 0.02, s["nose"] - 0.13), (0.24, 0.05, 0.13), material("lf2", LIGHT_F)),
|
||||
box("trim_light_rl", (-W * 0.32, r - 0.02, s["tail"] - 0.14), (0.26, 0.05, 0.12), material("lr", LIGHT_R)),
|
||||
box("trim_light_rr", (W * 0.32, r - 0.02, s["tail"] - 0.14), (0.26, 0.05, 0.12), material("lr2", LIGHT_R)),
|
||||
]
|
||||
if s.get("indicators"):
|
||||
amber = material("amber", (0.95, 0.55, 0.05, 1))
|
||||
trim.append(box("trim_ind_l", (-W * 0.44, f + 0.02, s["nose"] - 0.13), (0.11, 0.05, 0.12), amber))
|
||||
trim.append(box("trim_ind_r", (W * 0.44, f + 0.02, s["nose"] - 0.13), (0.11, 0.05, 0.12), amber))
|
||||
if s.get("side_strip"):
|
||||
strip_len = L * 0.80
|
||||
for sx in (-1, 1):
|
||||
trim.append(box("trim_strip_%s" % ("l" if sx < 0 else "r"),
|
||||
(sx * (hw + 0.015), -L * 0.02, 0.52), (0.03, strip_len, 0.055), dark))
|
||||
|
||||
ax_f, ax_r = f - s["ohang_f"], r + s["ohang_r"]
|
||||
wx = hw - 0.10
|
||||
@ -337,9 +371,14 @@ def build_moke(car_id, s):
|
||||
print("built", car_id, "->", path)
|
||||
|
||||
|
||||
only = set(sys.argv[sys.argv.index("--") + 1:]) if "--" in sys.argv else set()
|
||||
built = 0
|
||||
for car_id, spec in CARS.items():
|
||||
if only and car_id not in only:
|
||||
continue
|
||||
if spec.get("style") == "moke":
|
||||
build_moke(car_id, spec)
|
||||
else:
|
||||
build(car_id, spec)
|
||||
print("done: %d cars" % len(CARS))
|
||||
built += 1
|
||||
print("done: %d cars" % built)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user