diff --git a/README.md b/README.md index 76ef476..7409720 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # park_kit — GODVERSE park object library Reusable, textured, license-clean park objects for **bookquoy** and **skatemakerpro** -(and whatever comes next). 27 props, 18 photoreal surfaces, all original: geometry +(and whatever comes next). 52 props, 20 photoreal surfaces, all original: geometry authored parametrically in `tools/prop_author.py`, textures generated on the MODELBEAST farm (flux_local, local GPU, no cloud spend). @@ -28,14 +28,20 @@ matches across every prop. ## Catalog -- **obstacles (14):** qp_24, qp_18, bank_12, kicker, funbox_ply, spine_ply, - manual_pad, ledge_concrete, grindbox_ply, rail_flat_3m, rail_flat_6m, - rail_kink, rail_rainbow, pipe_full (graffiti-bombed concrete full pipe) -- **furniture (7):** bench_park, bench_concrete, picnic_table, bin_council, - bollard_steel, railing_ped_2m, planter_box (with gum sapling) -- **vegetation (2):** tree_fig (Moreton Bay, buttress roots), tree_gum (eucalypt) -- **dressing (4):** kerb_3m (slappy central), wall_graffiti (wallride bait), - shade_sail, mound_dirt +- **transition:** qp_24, qp_18, qp_vert (8ft), wallride_qp (to-vertical, concrete), + spine_ply, bank_12, kicker, launch_high, euro_gap, pyramid, funbox_ply, + volcano, roller, pipe_full (graffiti-bombed concrete full pipe), mound_dirt +- **rails:** rail_flat_3m, rail_flat_6m, square_flatbar, rail_down, rail_kink, + rail_kink2 (double), rail_aframe, rail_donkey (kinks up), rail_rainbow, + pole_jam (35°) +- **ledges/boxes:** ledge_concrete, grindbox_ply, box_low (10in), + box_table (24in), manual_pad, manny_incline, hubba_down (flat-to-down) +- **street furniture:** bench_park, bench_concrete, picnic_table, bin_council, + bollard_steel, railing_ped_2m, planter_box, jersey_barrier, parking_block, + hydrant, pool_edge (bullnose coping + tile band), bleachers_3 +- **vegetation:** tree_fig (Moreton Bay, buttress roots), tree_gum (eucalypt) +- **infrastructure/dressing:** kerb_3m (slappy central), wall_graffiti + (wallride bait), shade_sail, fence_chain_3m, floodlight, drain_grate ## View it diff --git a/manifest.json b/manifest.json index 6c84dcd..d92e594 100644 --- a/manifest.json +++ b/manifest.json @@ -909,6 +909,962 @@ "h": 0.55, "s": 1.4 } + }, + { + "id": "qp_vert", + "src": "props/qp_vert.glb", + "category": "obstacle", + "desc": "vert quarter 8ft", + "size": [ + 3.6, + 2.45, + 3.283 + ], + "tris": 124, + "grinds": [ + { + "a": [ + -1.8, + -2.6832815729997477 + ], + "b": [ + 1.8, + -2.6832815729997477 + ], + "ya": 2.42, + "yb": 2.42, + "kind": "rail" + } + ], + "element": { + "kind": "quarter", + "len": 3.6, + "r0": 2.7, + "r1": 2.7, + "h": 2.4 + } + }, + { + "id": "wallride_qp", + "src": "props/wallride_qp.glb", + "category": "obstacle", + "desc": "wallride wall \u2014 transition to vertical", + "size": [ + 3.0, + 1.5, + 1.62 + ], + "tris": 72, + "grinds": [ + { + "a": [ + -1.5, + -1.5 + ], + "b": [ + 1.5, + -1.5 + ], + "ya": 1.5, + "yb": 1.5, + "kind": "ledge" + } + ], + "element": { + "kind": "quarter", + "len": 3.0, + "r0": 1.5, + "r1": 1.5, + "h": 1.5 + } + }, + { + "id": "launch_high", + "src": "props/launch_high.glb", + "category": "obstacle", + "desc": "high launch ramp", + "size": [ + 1.5, + 0.9, + 1.64 + ], + "tris": 56, + "element": { + "kind": "bank", + "len": 1.5, + "h": 0.9, + "run": 1.6 + } + }, + { + "id": "square_flatbar", + "src": "props/square_flatbar.glb", + "category": "obstacle", + "desc": "square-section flatbar", + "size": [ + 2.4, + 0.4, + 0.12 + ], + "tris": 60, + "grinds": [ + { + "a": [ + -1.2, + 0 + ], + "b": [ + 1.2, + 0 + ], + "ya": 0.4, + "yb": 0.4, + "kind": "rail" + } + ], + "collider": { + "type": "none" + } + }, + { + "id": "pole_jam", + "src": "props/pole_jam.glb", + "category": "obstacle", + "desc": "bent pole jammed out of the concrete, 35 deg", + "size": [ + 1.661, + 1.003, + 0.5 + ], + "tris": 52, + "grinds": [ + { + "a": [ + 0, + 0 + ], + "b": [ + 1.392558475291286, + 0 + ], + "ya": 0.05, + "yb": 0.9750799417967783, + "kind": "rail" + } + ], + "collider": { + "type": "none" + } + }, + { + "id": "rail_down", + "src": "props/rail_down.glb", + "category": "obstacle", + "desc": "straight down rail", + "size": [ + 3.008, + 0.8, + 0.12 + ], + "tris": 128, + "grinds": [ + { + "a": [ + -1.5, + 0 + ], + "b": [ + 1.5, + 0 + ], + "ya": 0.78, + "yb": 0.18, + "kind": "rail" + } + ], + "collider": { + "type": "none" + } + }, + { + "id": "rail_kink2", + "src": "props/rail_kink2.glb", + "category": "obstacle", + "desc": "double-kink down rail", + "size": [ + 4.404, + 0.7, + 0.12 + ], + "tris": 276, + "grinds": [ + { + "a": [ + -2.2, + 0 + ], + "b": [ + -1.0, + 0 + ], + "ya": 0.68, + "yb": 0.68, + "kind": "rail" + }, + { + "a": [ + -1.0, + 0 + ], + "b": [ + 0.1, + 0 + ], + "ya": 0.68, + "yb": 0.36, + "kind": "rail" + }, + { + "a": [ + 0.1, + 0 + ], + "b": [ + 0.9, + 0 + ], + "ya": 0.36, + "yb": 0.36, + "kind": "rail" + }, + { + "a": [ + 0.9, + 0 + ], + "b": [ + 2.2, + 0 + ], + "ya": 0.36, + "yb": 0.08, + "kind": "rail" + } + ], + "collider": { + "type": "none" + } + }, + { + "id": "rail_aframe", + "src": "props/rail_aframe.glb", + "category": "obstacle", + "desc": "A-frame rail", + "size": [ + 3.613, + 0.74, + 0.12 + ], + "tris": 192, + "grinds": [ + { + "a": [ + -1.8, + 0 + ], + "b": [ + 0, + 0 + ], + "ya": 0.12, + "yb": 0.72, + "kind": "rail" + }, + { + "a": [ + 0, + 0 + ], + "b": [ + 1.8, + 0 + ], + "ya": 0.72, + "yb": 0.12, + "kind": "rail" + } + ], + "collider": { + "type": "none" + } + }, + { + "id": "rail_donkey", + "src": "props/rail_donkey.glb", + "category": "obstacle", + "desc": "donkey-kick rail \u2014 kinks up at the end", + "size": [ + 3.108, + 0.818, + 0.12 + ], + "tris": 192, + "grinds": [ + { + "a": [ + -1.6, + 0 + ], + "b": [ + 0.6, + 0 + ], + "ya": 0.42, + "yb": 0.42, + "kind": "rail" + }, + { + "a": [ + 0.6, + 0 + ], + "b": [ + 1.5, + 0 + ], + "ya": 0.42, + "yb": 0.8, + "kind": "rail" + } + ], + "collider": { + "type": "none" + } + }, + { + "id": "box_low", + "src": "props/box_low.glb", + "category": "obstacle", + "desc": "low grind box 10in", + "size": [ + 1.8, + 0.25, + 0.558 + ], + "tris": 60, + "grinds": [ + { + "a": [ + -0.9, + 0.25 + ], + "b": [ + 0.9, + 0.25 + ], + "ya": 0.25, + "yb": 0.25, + "kind": "ledge" + }, + { + "a": [ + -0.9, + -0.25 + ], + "b": [ + 0.9, + -0.25 + ], + "ya": 0.25, + "yb": 0.25, + "kind": "ledge" + } + ], + "element": { + "kind": "ledge", + "hw": 0.9, + "hd": 0.25, + "h": 0.25 + } + }, + { + "id": "box_table", + "src": "props/box_table.glb", + "category": "obstacle", + "desc": "high table box 24in", + "size": [ + 2.4, + 0.62, + 0.758 + ], + "tris": 60, + "grinds": [ + { + "a": [ + -1.2, + 0.35 + ], + "b": [ + 1.2, + 0.35 + ], + "ya": 0.62, + "yb": 0.62, + "kind": "ledge" + }, + { + "a": [ + -1.2, + -0.35 + ], + "b": [ + 1.2, + -0.35 + ], + "ya": 0.62, + "yb": 0.62, + "kind": "ledge" + } + ], + "element": { + "kind": "ledge", + "hw": 1.2, + "hd": 0.35, + "h": 0.62 + } + }, + { + "id": "hubba_down", + "src": "props/hubba_down.glb", + "category": "obstacle", + "desc": "flat-to-down hubba ledge", + "size": [ + 2.6, + 0.66, + 0.42 + ], + "tris": 14, + "grinds": [ + { + "a": [ + -1.3, + -0.21 + ], + "b": [ + -0.5, + -0.21 + ], + "ya": 0.66, + "yb": 0.66, + "kind": "ledge" + }, + { + "a": [ + -0.5, + -0.21 + ], + "b": [ + 1.3, + -0.21 + ], + "ya": 0.66, + "yb": 0.12, + "kind": "ledge" + }, + { + "a": [ + -1.3, + 0.21 + ], + "b": [ + -0.5, + 0.21 + ], + "ya": 0.66, + "yb": 0.66, + "kind": "ledge" + }, + { + "a": [ + -0.5, + 0.21 + ], + "b": [ + 1.3, + 0.21 + ], + "ya": 0.66, + "yb": 0.12, + "kind": "ledge" + } + ], + "collider": { + "type": "box", + "hw": 1.3, + "hd": 0.21, + "h": 0.66 + } + }, + { + "id": "manny_incline", + "src": "props/manny_incline.glb", + "category": "obstacle", + "desc": "inclined manual pad", + "size": [ + 2.0, + 0.4, + 1.603 + ], + "tris": 12, + "grinds": [ + { + "a": [ + -1.0, + -0.8 + ], + "b": [ + 1.0, + -0.8 + ], + "ya": 0.18, + "yb": 0.4, + "kind": "ledge" + }, + { + "a": [ + -1.0, + 0.8 + ], + "b": [ + 1.0, + 0.8 + ], + "ya": 0.18, + "yb": 0.4, + "kind": "ledge" + } + ], + "collider": { + "type": "box", + "hw": 1.0, + "hd": 0.8, + "h": 0.4 + } + }, + { + "id": "pyramid", + "src": "props/pyramid.glb", + "category": "obstacle", + "desc": "four-sided pyramid, flat top, steel edges", + "size": [ + 4.8, + 0.8, + 4.8 + ], + "tris": 116, + "grinds": [ + { + "a": [ + -0.8, + 0.8 + ], + "b": [ + 0.8, + 0.8 + ], + "ya": 0.8, + "yb": 0.8, + "kind": "ledge" + }, + { + "a": [ + 0.8, + 0.8 + ], + "b": [ + 0.8, + -0.8 + ], + "ya": 0.8, + "yb": 0.8, + "kind": "ledge" + }, + { + "a": [ + 0.8000000000000002, + -0.7999999999999999 + ], + "b": [ + -0.7999999999999999, + -0.8000000000000002 + ], + "ya": 0.8, + "yb": 0.8, + "kind": "ledge" + }, + { + "a": [ + -0.7999999999999999, + -0.8000000000000002 + ], + "b": [ + -0.8000000000000002, + 0.7999999999999999 + ], + "ya": 0.8, + "yb": 0.8, + "kind": "ledge" + } + ], + "element": { + "kind": "funbox", + "hw": 2.4, + "hd": 2.4, + "h": 0.8, + "skirt": 1.5999999999999999 + } + }, + { + "id": "euro_gap", + "src": "props/euro_gap.glb", + "category": "obstacle", + "desc": "euro gap: bank up, sheer drop off the deck", + "size": [ + 2.4, + 0.75, + 2.529 + ], + "tris": 54, + "grinds": [ + { + "a": [ + -1.2, + -0.5 + ], + "b": [ + 1.2, + -0.5 + ], + "ya": 0.75, + "yb": 0.75, + "kind": "ledge" + } + ], + "element": { + "kind": "bank", + "len": 2.4, + "h": 0.75, + "run": 1.5 + } + }, + { + "id": "volcano", + "src": "props/volcano.glb", + "category": "obstacle", + "desc": "concrete volcano cone, grindable rim", + "size": [ + 4.4, + 1.1, + 4.4 + ], + "tris": 180, + "grinds": [ + { + "a": [ + -0.36, + -0.36 + ], + "b": [ + 0.36, + -0.36 + ], + "ya": 1.1, + "yb": 1.1, + "kind": "rail" + }, + { + "a": [ + 0.36, + -0.36 + ], + "b": [ + 0.36, + 0.36 + ], + "ya": 1.1, + "yb": 1.1, + "kind": "rail" + }, + { + "a": [ + 0.36, + 0.36 + ], + "b": [ + -0.36, + 0.36 + ], + "ya": 1.1, + "yb": 1.1, + "kind": "rail" + }, + { + "a": [ + -0.36, + 0.36 + ], + "b": [ + -0.36, + -0.36 + ], + "ya": 1.1, + "yb": 1.1, + "kind": "rail" + } + ], + "element": { + "kind": "mogul", + "h": 1.1, + "s": 1.5 + } + }, + { + "id": "roller", + "src": "props/roller.glb", + "category": "obstacle", + "desc": "concrete speed roller / pump bump", + "size": [ + 2.4, + 0.32, + 2.2 + ], + "tris": 42, + "element": { + "kind": "mogul", + "h": 0.32, + "s": 0.9 + } + }, + { + "id": "jersey_barrier", + "src": "props/jersey_barrier.glb", + "category": "furniture", + "desc": "concrete K-rail jersey barrier", + "size": [ + 2.0, + 0.81, + 0.6 + ], + "tris": 26, + "grinds": [ + { + "a": [ + -1.0, + 0 + ], + "b": [ + 1.0, + 0 + ], + "ya": 0.81, + "yb": 0.81, + "kind": "ledge" + } + ], + "collider": { + "type": "box", + "hw": 1.0, + "hd": 0.3, + "h": 0.81 + } + }, + { + "id": "parking_block", + "src": "props/parking_block.glb", + "category": "furniture", + "desc": "concrete parking block \u2014 slappy bait", + "size": [ + 1.7, + 0.15, + 0.16 + ], + "tris": 12, + "grinds": [ + { + "a": [ + -0.85, + 0 + ], + "b": [ + 0.85, + 0 + ], + "ya": 0.15, + "yb": 0.15, + "kind": "ledge" + } + ], + "collider": { + "type": "none" + } + }, + { + "id": "hydrant", + "src": "props/hydrant.glb", + "category": "furniture", + "desc": "fire hydrant \u2014 bonk it", + "size": [ + 0.32, + 0.8, + 0.32 + ], + "tris": 208, + "collider": { + "type": "cylinder", + "r": 0.2, + "h": 0.8 + } + }, + { + "id": "pool_edge", + "src": "props/pool_edge.glb", + "category": "furniture", + "desc": "pool coping + tile band, 2m section", + "size": [ + 2.0, + 0.182, + 0.565 + ], + "tris": 54, + "grinds": [ + { + "a": [ + -1.0, + 0.01 + ], + "b": [ + 1.0, + 0.01 + ], + "ya": 0.185, + "yb": 0.185, + "kind": "rail" + } + ], + "collider": { + "type": "none" + } + }, + { + "id": "fence_chain_3m", + "src": "props/fence_chain_3m.glb", + "category": "dressing", + "desc": "chain-link fence section", + "size": [ + 3.048, + 1.821, + 0.048 + ], + "tris": 98, + "grinds": [ + { + "a": [ + -1.5, + 0 + ], + "b": [ + 1.5, + 0 + ], + "ya": 1.8, + "yb": 1.8, + "kind": "rail" + } + ], + "collider": { + "type": "box", + "hw": 1.5, + "hd": 0.03, + "h": 1.8 + } + }, + { + "id": "floodlight", + "src": "props/floodlight.glb", + "category": "dressing", + "desc": "park floodlight pole", + "size": [ + 1.2, + 6.23, + 0.236 + ], + "tris": 86, + "collider": { + "type": "cylinder", + "r": 0.12, + "h": 6.0 + } + }, + { + "id": "bleachers_3", + "src": "props/bleachers_3.glb", + "category": "furniture", + "desc": "three-row spectator bleachers", + "size": [ + 3.0, + 0.92, + 1.5 + ], + "tris": 78, + "grinds": [ + { + "a": [ + -1.5, + -0.225 + ], + "b": [ + 1.5, + -0.225 + ], + "ya": 0.32, + "yb": 0.32, + "kind": "ledge" + }, + { + "a": [ + -1.5, + 0.775 + ], + "b": [ + 1.5, + 0.775 + ], + "ya": 0.92, + "yb": 0.92, + "kind": "ledge" + } + ], + "collider": { + "type": "box", + "hw": 1.5, + "hd": 0.75, + "h": 0.92 + } + }, + { + "id": "drain_grate", + "src": "props/drain_grate.glb", + "category": "dressing", + "desc": "flush drainage grate", + "size": [ + 1.06, + 0.022, + 0.76 + ], + "tris": 24, + "collider": { + "type": "none" + } } ] } \ No newline at end of file diff --git a/props/bin_council.glb b/props/bin_council.glb index d067335..b13980b 100644 Binary files a/props/bin_council.glb and b/props/bin_council.glb differ diff --git a/props/bleachers_3.glb b/props/bleachers_3.glb new file mode 100644 index 0000000..172b60b Binary files /dev/null and b/props/bleachers_3.glb differ diff --git a/props/bollard_steel.glb b/props/bollard_steel.glb index fe04afa..5267fa4 100644 Binary files a/props/bollard_steel.glb and b/props/bollard_steel.glb differ diff --git a/props/box_low.glb b/props/box_low.glb new file mode 100644 index 0000000..e686ef9 Binary files /dev/null and b/props/box_low.glb differ diff --git a/props/box_table.glb b/props/box_table.glb new file mode 100644 index 0000000..a2c5668 Binary files /dev/null and b/props/box_table.glb differ diff --git a/props/drain_grate.glb b/props/drain_grate.glb new file mode 100644 index 0000000..b95005d Binary files /dev/null and b/props/drain_grate.glb differ diff --git a/props/euro_gap.glb b/props/euro_gap.glb new file mode 100644 index 0000000..56c3c58 Binary files /dev/null and b/props/euro_gap.glb differ diff --git a/props/fence_chain_3m.glb b/props/fence_chain_3m.glb new file mode 100644 index 0000000..b6e6b21 Binary files /dev/null and b/props/fence_chain_3m.glb differ diff --git a/props/floodlight.glb b/props/floodlight.glb new file mode 100644 index 0000000..a28e7df Binary files /dev/null and b/props/floodlight.glb differ diff --git a/props/hubba_down.glb b/props/hubba_down.glb new file mode 100644 index 0000000..62b3065 Binary files /dev/null and b/props/hubba_down.glb differ diff --git a/props/hydrant.glb b/props/hydrant.glb new file mode 100644 index 0000000..ecf0726 Binary files /dev/null and b/props/hydrant.glb differ diff --git a/props/jersey_barrier.glb b/props/jersey_barrier.glb new file mode 100644 index 0000000..ee5932e Binary files /dev/null and b/props/jersey_barrier.glb differ diff --git a/props/launch_high.glb b/props/launch_high.glb new file mode 100644 index 0000000..6e4b451 Binary files /dev/null and b/props/launch_high.glb differ diff --git a/props/manny_incline.glb b/props/manny_incline.glb new file mode 100644 index 0000000..e0b3f76 Binary files /dev/null and b/props/manny_incline.glb differ diff --git a/props/parking_block.glb b/props/parking_block.glb new file mode 100644 index 0000000..4ee9954 Binary files /dev/null and b/props/parking_block.glb differ diff --git a/props/planter_box.glb b/props/planter_box.glb index b88f209..1df0cc7 100644 Binary files a/props/planter_box.glb and b/props/planter_box.glb differ diff --git a/props/pole_jam.glb b/props/pole_jam.glb new file mode 100644 index 0000000..4333b2c Binary files /dev/null and b/props/pole_jam.glb differ diff --git a/props/pool_edge.glb b/props/pool_edge.glb new file mode 100644 index 0000000..1b916ca Binary files /dev/null and b/props/pool_edge.glb differ diff --git a/props/pyramid.glb b/props/pyramid.glb new file mode 100644 index 0000000..47cd468 Binary files /dev/null and b/props/pyramid.glb differ diff --git a/props/qp_vert.glb b/props/qp_vert.glb new file mode 100644 index 0000000..ae0d629 Binary files /dev/null and b/props/qp_vert.glb differ diff --git a/props/rail_aframe.glb b/props/rail_aframe.glb new file mode 100644 index 0000000..4e34b94 Binary files /dev/null and b/props/rail_aframe.glb differ diff --git a/props/rail_donkey.glb b/props/rail_donkey.glb new file mode 100644 index 0000000..98632ad Binary files /dev/null and b/props/rail_donkey.glb differ diff --git a/props/rail_down.glb b/props/rail_down.glb new file mode 100644 index 0000000..4928fb0 Binary files /dev/null and b/props/rail_down.glb differ diff --git a/props/rail_kink2.glb b/props/rail_kink2.glb new file mode 100644 index 0000000..9cd83aa Binary files /dev/null and b/props/rail_kink2.glb differ diff --git a/props/roller.glb b/props/roller.glb new file mode 100644 index 0000000..0862f59 Binary files /dev/null and b/props/roller.glb differ diff --git a/props/square_flatbar.glb b/props/square_flatbar.glb new file mode 100644 index 0000000..1bdb91c Binary files /dev/null and b/props/square_flatbar.glb differ diff --git a/props/tree_fig.glb b/props/tree_fig.glb index 0d1e408..2030234 100644 Binary files a/props/tree_fig.glb and b/props/tree_fig.glb differ diff --git a/props/tree_gum.glb b/props/tree_gum.glb index 9595e3f..3877c95 100644 Binary files a/props/tree_gum.glb and b/props/tree_gum.glb differ diff --git a/props/volcano.glb b/props/volcano.glb new file mode 100644 index 0000000..b7fa2b5 Binary files /dev/null and b/props/volcano.glb differ diff --git a/props/wallride_qp.glb b/props/wallride_qp.glb new file mode 100644 index 0000000..24c804b Binary files /dev/null and b/props/wallride_qp.glb differ diff --git a/textures/steel_grate.jpg b/textures/steel_grate.jpg new file mode 100644 index 0000000..a739c35 Binary files /dev/null and b/textures/steel_grate.jpg differ diff --git a/textures/tiles_pool.jpg b/textures/tiles_pool.jpg new file mode 100644 index 0000000..edb7527 Binary files /dev/null and b/textures/tiles_pool.jpg differ diff --git a/tools/__pycache__/propkit.cpython-314.pyc b/tools/__pycache__/propkit.cpython-314.pyc index cbbb31b..c9d9b20 100644 Binary files a/tools/__pycache__/propkit.cpython-314.pyc and b/tools/__pycache__/propkit.cpython-314.pyc differ diff --git a/tools/gen_textures.py b/tools/gen_textures.py index 6cc161a..737d44a 100644 --- a/tools/gen_textures.py +++ b/tools/gen_textures.py @@ -38,6 +38,8 @@ TEXTURES = { 'brick_qld': 'reddish brown queensland house bricks with pale mortar joints, running bond', 'graffiti_a': 'colorful graffiti burner piece with bold wildstyle letters on a concrete skatepark wall, aerosol art, blues oranges purples', 'graffiti_b': 'concrete wall covered in layered spray paint tags and simple throw-up graffiti letters, black silver white paint', + 'tiles_pool': 'vintage swimming pool tile band, small square ceramic tiles, white with pale smalt-blue accent rows, aged grout lines', + 'steel_grate': 'steel drainage grate with parallel slot openings, dark weathered galvanized metal', 'sky_day': 'clear bright queensland blue sky with a few small white cumulus clouds, wide even gradient', } diff --git a/tools/prop_author.py b/tools/prop_author.py index b8ab59c..c4f4df5 100644 --- a/tools/prop_author.py +++ b/tools/prop_author.py @@ -86,7 +86,8 @@ def arc_pts(r, th0, th1, n): for t in [th0 + (th1 - th0) * i / n for i in range(n + 1)]] # =================================================================== ramps -def quarter(pid, w, h, r, desc): +def quarter(pid, w, h, r, desc, coping=True, deck=True, mat=None): + mat = mat or PLY th = math.acos(1 - h / r) depth = r * math.sin(th) n = 14 @@ -95,21 +96,26 @@ def quarter(pid, w, h, r, desc): p = Prop(pid, 'obstacle', desc) # riding surface (two columns across width) rows = [[(-w / 2, y, z), (w / 2, y, z)] for y, z in ys_zs] - p.add(PLY, sheet(rows)) + p.add(mat, sheet(rows)) # deck platform + coping - deck_d = 0.6 - p.add(PLY, box(0, h - 0.04, -depth - deck_d / 2, w, 0.08, deck_d)) - p.add(STEEL, tube([(-w / 2, h + 0.02, -depth), (w / 2, h + 0.02, -depth)], - 0.032, seg=10)) + deck_d = 0.6 if deck else 0.12 + if deck: + p.add(mat, box(0, h - 0.04, -depth - deck_d / 2, w, 0.08, deck_d)) + if coping: + p.add(STEEL, tube([(-w / 2, h + 0.02, -depth), (w / 2, h + 0.02, -depth)], + 0.032, seg=10)) # side plates: arc boundary + deck + back-down (fan from back-bottom corner) prof = [( -depth - deck_d, 0)] + [(z, y) for y, z in ys_zs] + \ [(-depth, h), (-depth - deck_d, h)] for sx in (-w / 2, w / 2): pts = [(sx, y, z) for z, y in prof] - p.add(PLY, fan(pts, lambda pp: (pp[2], pp[1]), out=(sx, 0, 0))) + p.add(mat, fan(pts, lambda pp: (pp[2], pp[1]), out=(sx, 0, 0))) # back wall p.add(GALV, box(0, h / 2, -depth - deck_d + 0.02, w, h, 0.04)) - p.grind(-w / 2, -depth, w / 2, -depth, h + 0.02, h + 0.02, 'rail') + if coping: + p.grind(-w / 2, -depth, w / 2, -depth, h + 0.02, h + 0.02, 'rail') + else: + p.grind(-w / 2, -depth, w / 2, -depth, h, h, 'ledge') p.element = {'kind': 'quarter', 'len': w, 'r0': r, 'r1': r, 'h': h} return p @@ -127,9 +133,10 @@ def bank(pid, w, h, run, desc): p.element = {'kind': 'bank', 'len': w, 'h': h, 'run': run} return p -def kicker(): - p = Prop('kicker', 'obstacle', 'curved plywood launch kicker') - w, h, run, n = 1.2, 0.45, 1.0, 8 +def kicker(pid='kicker', w=1.2, h=0.45, run=1.0, + desc='curved plywood launch kicker'): + p = Prop(pid, 'obstacle', desc) + n = 8 rows = [] for i in range(n + 1): t = i / n @@ -484,6 +491,273 @@ def mound_dirt(): p.element = {'kind': 'mogul', 'h': H, 's': 1.4} return p +# ====================================================== expansion: modular set +# John's editor taxonomy (2026-08-03): kink variants, pole jams, hubbas, boxes, +# pyramid, euro gap, vert wall, volcano, street props, park infrastructure. + +def rail_from(pid, pts_xy, feet_x, desc, r=0.021, kind='rail'): + """Polyline rail in the xz=0 plane. pts_xy: (x, y) knots -> one grind + segment per span; feet dropped at feet_x (height lerped from the line).""" + p = Prop(pid, 'obstacle', desc) + p.add(STEEL, tube([(x, y, 0) for x, y in pts_xy], r, seg=10)) + def y_at(x): + for (x0, y0), (x1, y1) in zip(pts_xy, pts_xy[1:]): + if x0 - 1e-6 <= x <= x1 + 1e-6: + return y0 + (y1 - y0) * (x - x0) / (x1 - x0) + return pts_xy[-1][1] + for fx in feet_x: + p.add(GALV, tube([(fx, 0, 0), (fx, y_at(fx) - r, 0)], 0.018, seg=8)) + p.add(GALV, box(fx, 0.006, 0, 0.16, 0.012, 0.12)) + for (x0, y0), (x1, y1) in zip(pts_xy, pts_xy[1:]): + p.grind(x0, 0, x1, 0, y0, y1, kind) + p.collider = {'type': 'none'} + return p + +def square_flatbar(): + p = Prop('square_flatbar', 'obstacle', 'square-section flatbar') + L, h = 2.4, 0.4 + p.add(STEEL, box(0, h - 0.03, 0, L, 0.06, 0.06)) + for fx in (-L / 2 + 0.2, L / 2 - 0.2): + p.add(GALV, box(fx, (h - 0.06) / 2, 0, 0.05, h - 0.06, 0.05)) + p.add(GALV, box(fx, 0.006, 0, 0.16, 0.012, 0.12)) + p.grind(-L / 2, 0, L / 2, 0, h, h, 'rail') + p.collider = {'type': 'none'} + return p + +def pole_jam(): + p = Prop('pole_jam', 'obstacle', 'bent pole jammed out of the concrete, 35 deg') + L, ang = 1.7, math.radians(35) + tip = (L * math.cos(ang), L * math.sin(ang)) + p.add(STEEL, tube([(-0.05, 0.0, 0), (tip[0], tip[1], 0)], 0.035, seg=10)) + p.add(CONC_R, box(0, 0.04, 0, 0.5, 0.08, 0.5)) # broken footing stub + p.grind(0, 0, tip[0], 0, 0.05, tip[1], 'rail') + p.collider = {'type': 'none'} + return p + +def sloped_box(pid, L, w, y0, y1, desc, mat=None, cat='obstacle', graf=False): + """Box whose top slopes from y0 (at -x) to y1 (+x): hubbas, manny pads.""" + mat = mat or CONC + p = Prop(pid, cat, desc) + rows = [[(-L / 2, y0, -w / 2), (-L / 2, y0, w / 2)], + [(L / 2, y1, -w / 2), (L / 2, y1, w / 2)]] + p.add(mat, sheet(rows, flip=True)) + for sz in (-w / 2, w / 2): # side walls + pts = [(-L / 2, 0, sz), (L / 2, 0, sz), (L / 2, y1, sz), (-L / 2, y0, sz)] + f = fan(pts, lambda pp: (pp[0], pp[1]), out=(0, 0, sz)) + p.add(mat, f) + for sx, yy in ((-L / 2, y0), (L / 2, y1)): # end caps + p.add(mat, fan([(sx, 0, -w / 2), (sx, 0, w / 2), (sx, yy, w / 2), (sx, yy, -w / 2)], + lambda pp: (pp[2], pp[1]), out=(sx, 0, 0))) + if graf: + p.add(GRAF_B, uv_fit(fan([(-L / 2, 0, w / 2 + 0.003), (L / 2, 0, w / 2 + 0.003), + (L / 2, y1, w / 2 + 0.003), (-L / 2, y0, w / 2 + 0.003)], + lambda pp: (pp[0], pp[1]), out=(0, 0, 1)))) + for sz in (-w / 2, w / 2): + p.grind(-L / 2, sz, L / 2, sz, y0, y1, 'ledge') + p.collider = {'type': 'box', 'hw': L / 2, 'hd': w / 2, 'h': max(y0, y1)} + return p + +def hubba_down(): + # flat 0.8m at stair-top height, then down the imaginary stairs + p = Prop('hubba_down', 'obstacle', 'flat-to-down hubba ledge') + w, hf, hl = 0.42, 0.66, 0.12 + Lf, Ld = 0.8, 1.8 + top = sheet([[(-(Lf + Ld) / 2, hf, -w / 2), (-(Lf + Ld) / 2, hf, w / 2)], + [(-(Lf + Ld) / 2 + Lf, hf, -w / 2), (-(Lf + Ld) / 2 + Lf, hf, w / 2)], + [((Lf + Ld) / 2, hl, -w / 2), ((Lf + Ld) / 2, hl, w / 2)]], flip=True) + p.add(CONC, top) + x0, xk, x1 = -(Lf + Ld) / 2, -(Lf + Ld) / 2 + Lf, (Lf + Ld) / 2 + for sz in (-w / 2, w / 2): + pts = [(x0, 0, sz), (x1, 0, sz), (x1, hl, sz), (xk, hf, sz), (x0, hf, sz)] + p.add(CONC, fan(pts, lambda pp: (pp[0], pp[1]), out=(0, 0, sz))) + p.add(CONC, fan([(x0, 0, -w / 2), (x0, 0, w / 2), (x0, hf, w / 2), (x0, hf, -w / 2)], + lambda pp: (pp[2], pp[1]), out=(-1, 0, 0))) + p.add(CONC, fan([(x1, 0, -w / 2), (x1, 0, w / 2), (x1, hl, w / 2), (x1, hl, -w / 2)], + lambda pp: (pp[2], pp[1]), out=(1, 0, 0))) + for sz in (-w / 2, w / 2): + p.grind(x0, sz, xk, sz, hf, hf, 'ledge') + p.grind(xk, sz, x1, sz, hf, hl, 'ledge') + p.collider = {'type': 'box', 'hw': (Lf + Ld) / 2, 'hd': w / 2, 'h': hf} + return p + +def grind_box(pid, L, w, h, desc, mat=None): + mat = mat or PLY + p = Prop(pid, 'obstacle', desc) + p.add(mat, box(0, h / 2, 0, L, h, w)) + for sz in (1, -1): + p.add(STEEL, angle_cap(-L / 2, L / 2, h, sz * w / 2)) + p.grind(-L / 2, sz * w / 2, L / 2, sz * w / 2, h, h, 'ledge') + p.element = {'kind': 'ledge', 'hw': L / 2, 'hd': w / 2, 'h': h} + return p + +def pyramid(): + p = Prop('pyramid', 'obstacle', 'four-sided pyramid, flat top, steel edges') + B, T, H = 4.8, 1.6, 0.8 + p.add(PLY, box(0, H - 0.02, 0, T, 0.04, T)) # flat top + for i in range(4): # four banks + a = math.pi / 2 * i + pts = [(-B / 2, 0, B / 2), (B / 2, 0, B / 2), (T / 2, H, T / 2), (-T / 2, H, T / 2)] + f = fan(pts, lambda pp: (pp[0], pp[2])) + f = rotate_y(f, a) + ndir = (math.sin(a), 0.3, math.cos(a)) + d = sum(nn[k] * ndir[k] for nn in f['N'] for k in range(3)) + if d < 0: + f['I'] = [f['I'][j + o] for j in range(0, len(f['I']), 3) for o in (0, 2, 1)] + f['N'] = [(-x, -y, -z) for x, y, z in f['N']] + p.add(PLY, f) + for i in range(4): # top edge caps + grinds + a = math.pi / 2 * i + cap = rotate_y(angle_cap(-T / 2, T / 2, H, T / 2), a) + p.add(STEEL, cap) + ca, sa = math.cos(a), math.sin(a) + rot = lambda x, z: (x * ca + z * sa, -x * sa + z * ca) + ax, az = rot(-T / 2, T / 2); bx, bz = rot(T / 2, T / 2) + p.grind(ax, az, bx, bz, H, H, 'ledge') + p.element = {'kind': 'funbox', 'hw': B / 2, 'hd': B / 2, 'h': H, 'skirt': (B - T) / 2} + return p + +def euro_gap(): + p = Prop('euro_gap', 'obstacle', 'euro gap: bank up, sheer drop off the deck') + w, h, run, deck = 2.4, 0.75, 1.5, 1.0 + rows = [[(-w / 2, 0, run + deck / 2), (w / 2, 0, run + deck / 2)], + [(-w / 2, h, deck / 2), (w / 2, h, deck / 2)]] + p.add(PLY, sheet(rows)) # bank faces +z + p.add(PLY, box(0, h - 0.02, 0, w, 0.04, deck)) # deck + p.add(CONC_R, box(0, h / 2 - 0.02, -deck / 2 + 0.02, w, h - 0.04, 0.04)) # drop face + for sx in (-w / 2, w / 2): + p.add(GALV, fan([(sx, 0, -deck / 2), (sx, 0, run + deck / 2), + (sx, h, deck / 2), (sx, h, -deck / 2)], + lambda pp: (pp[2], pp[1]), out=(sx, 0, 0))) + p.add(STEEL, angle_cap(-w / 2, w / 2, h, -deck / 2)) + p.grind(-w / 2, -deck / 2, w / 2, -deck / 2, h, h, 'ledge') + p.element = {'kind': 'bank', 'len': w, 'h': h, 'run': run} + return p + +def volcano(): + p = Prop('volcano', 'obstacle', 'concrete volcano cone, grindable rim') + prof = [(2.2, 0.0), (1.55, 0.28), (1.0, 0.62), (0.64, 0.95), (0.5, 1.1)] + p.add(CONC, lathe(prof, seg=20, cap_top=True)) + r, y = 0.36, 1.1 + for (ax, az), (bx, bz) in [((-r, -r), (r, -r)), ((r, -r), (r, r)), + ((r, r), (-r, r)), ((-r, r), (-r, -r))]: + p.grind(ax, az, bx, bz, y, y, 'rail') + p.element = {'kind': 'mogul', 'h': 1.1, 's': 1.5} + return p + +def roller(): + p = Prop('roller', 'obstacle', 'concrete speed roller / pump bump') + L, R, H, n = 2.4, 1.1, 0.32, 10 + rows = [] + for i in range(n + 1): + z = -R + 2 * R * i / n + y = H * math.exp(-3.0 * (z / R) ** 2) + rows.append([(-L / 2, y, z), (L / 2, y, z)]) + p.add(CONC, sheet(rows, flip=True)) + for sx in (-L / 2, L / 2): + pts = [(sx, 0, -R)] + [(sx, H * math.exp(-3.0 * ((-R + 2 * R * i / n) / R) ** 2), + -R + 2 * R * i / n) for i in range(n + 1)] + [(sx, 0, R)] + p.add(CONC, fan(pts, lambda pp: (pp[2], pp[1]), out=(sx, 0, 0))) + p.element = {'kind': 'mogul', 'h': H, 's': 0.9} + return p + +# ------------------------------------------------------------- street props +def jersey_barrier(): + p = Prop('jersey_barrier', 'furniture', 'concrete K-rail jersey barrier') + L = 2.0 + prof = [(0.30, 0.0), (0.30, 0.06), (0.115, 0.55), (0.095, 0.81), + (-0.095, 0.81), (-0.115, 0.55), (-0.30, 0.06), (-0.30, 0.0)] + rows = [[(-L / 2, y, z), (L / 2, y, z)] for z, y in prof] + p.add(CONC_R, sheet(rows, flip=True)) + for sx in (-L / 2, L / 2): + p.add(CONC_R, fan([(sx, y, z) for z, y in prof], + lambda pp: (pp[2], pp[1]), out=(sx, 0, 0))) + p.grind(-L / 2, 0, L / 2, 0, 0.81, 0.81, 'ledge') + p.collider = {'type': 'box', 'hw': L / 2, 'hd': 0.3, 'h': 0.81} + return p + +def parking_block(): + p = Prop('parking_block', 'furniture', 'concrete parking block — slappy bait') + p.add(Mat('conc_yellow', tex='concrete_rough', uv=1.4, tint=(1.0, 0.85, 0.45, 1)), + box(0, 0.075, 0, 1.7, 0.15, 0.16)) + p.grind(-0.85, 0, 0.85, 0, 0.15, 0.15, 'ledge') + p.collider = {'type': 'none'} + return p + +def hydrant(): + p = Prop('hydrant', 'furniture', 'fire hydrant — bonk it') + RED = Mat('hydrant_red', tint=(0.72, 0.14, 0.1, 1), rough=0.55, metal=0.2) + p.add(RED, lathe([(0.14, 0.0), (0.11, 0.1), (0.11, 0.55), (0.13, 0.6), + (0.1, 0.7), (0.04, 0.78), (0.0, 0.8)], seg=12)) + p.add(RED, tube([(0, 0.45, -0.16), (0, 0.45, 0.16)], 0.05, seg=8)) + p.add(RED, tube([(-0.16, 0.45, 0), (0.16, 0.45, 0)], 0.05, seg=8)) + p.collider = {'type': 'cylinder', 'r': 0.2, 'h': 0.8} + return p + +def pool_edge(): + p = Prop('pool_edge', 'furniture', 'pool coping + tile band, 2m section') + L = 2.0 + p.add(CONC_R, box(0, 0.06, -0.25, L, 0.12, 0.5)) # deck slab + p.add(Mat('bullnose', tex='concrete_smooth', uv=0.6, rough=0.7), + tube([(-L / 2, 0.13, 0.01), (L / 2, 0.13, 0.01)], 0.055, seg=10)) + p.add(Mat('tiles', tex='tiles_pool', uv=0.62), + fan([(-L / 2, -0.32, 0.05), (L / 2, -0.32, 0.05), + (L / 2, 0.05, 0.05), (-L / 2, 0.05, 0.05)], + lambda pp: (pp[0], pp[1]), out=(0, 0, 1))) + p.grind(-L / 2, 0.01, L / 2, 0.01, 0.185, 0.185, 'rail') + p.collider = {'type': 'none'} + return p + +# ----------------------------------------------------------- infrastructure +def fence_chain(): + p = Prop('fence_chain_3m', 'dressing', 'chain-link fence section') + L, H = 3.0, 1.8 + MESH = Mat('chain_mesh', tint=(0.62, 0.66, 0.68, 0.38), rough=0.6, + metal=0.7, double=True) + p.add(MESH, fan([(-L / 2, 0.05, 0), (L / 2, 0.05, 0), + (L / 2, H - 0.05, 0), (-L / 2, H - 0.05, 0)], + lambda pp: (pp[0], pp[1]), out=(0, 0, 1))) + p.add(GALV, tube([(-L / 2, H, 0), (L / 2, H, 0)], 0.021, seg=8)) + for sx in (-L / 2, L / 2): + p.add(GALV, tube([(sx, 0, 0), (sx, H - 0.02, 0)], 0.024, seg=8)) + p.grind(-L / 2, 0, L / 2, 0, H, H, 'rail') + p.collider = {'type': 'box', 'hw': L / 2, 'hd': 0.03, 'h': H} + return p + +def floodlight(): + p = Prop('floodlight', 'dressing', 'park floodlight pole') + H = 6.0 + p.add(GALV, lathe([(0.09, 0.0), (0.07, H * 0.6), (0.05, H)], seg=10)) + p.add(GALV, box(0, H + 0.05, 0, 1.2, 0.08, 0.1)) + LAMP = Mat('lamp', tint=(1, 1, 0.92, 1), rough=0.3, emissive=(0.9, 0.9, 0.75)) + for dx in (-0.45, 0.45): + p.add(LAMP, box(dx, H + 0.14, 0.04, 0.3, 0.18, 0.22)) + p.collider = {'type': 'cylinder', 'r': 0.12, 'h': H} + return p + +def bleachers(): + p = Prop('bleachers_3', 'furniture', 'three-row spectator bleachers') + L = 3.0 + for i in range(3): + y, z = 0.32 + i * 0.3, i * 0.5 + p.add(SLATS, box(0, y - 0.025, z, L, 0.05, 0.45)) # seat plank + p.add(GALV, box(0, y - 0.15, z + 0.18, L, 0.24, 0.04)) # riser + for sx in (-L / 2 + 0.1, L / 2 - 0.1): # side frames + p.add(GALV, fan([(sx, 0, -0.25), (sx, 0, 1.25), (sx, 0.92, 1.25), (sx, 0.92, 0.75), + (sx, 0.32, -0.25)], + lambda pp: (pp[2], pp[1]), out=(sx, 0, 0))) + p.grind(-L / 2, -0.225, L / 2, -0.225, 0.32, 0.32, 'ledge') # bottom row edge + p.grind(-L / 2, 0.775, L / 2, 0.775, 0.92, 0.92, 'ledge') # top row edge + p.collider = {'type': 'box', 'hw': L / 2, 'hd': 0.75, 'h': 0.92} + return p + +def drain_grate(): + p = Prop('drain_grate', 'dressing', 'flush drainage grate') + p.add(Mat('grate', tex='steel_grate', uv=0.9, rough=0.5, metal=0.6), + box(0, 0.011, 0, 0.9, 0.022, 0.6)) + p.add(CONC, box(0, 0.008, 0, 1.06, 0.016, 0.76)) + p.collider = {'type': 'none'} + return p + # ---------------------------------------------------------------- build all BUILDERS = [ lambda: quarter('qp_24', 2.4, 1.2, 1.8, 'plywood quarter pipe 4ft, steel coping'), @@ -496,6 +770,30 @@ BUILDERS = [ bench_park, bench_concrete, picnic_table, bin_council, bollard_steel, railing_ped, planter_box, kerb_3m, wall_graffiti, shade_sail, tree_fig, tree_gum, mound_dirt, + # ---- expansion: modular editor set + lambda: quarter('qp_vert', 3.6, 2.4, 2.7, 'vert quarter 8ft'), + lambda: quarter('wallride_qp', 3.0, 1.5, 1.5, + 'wallride wall — transition to vertical', coping=False, + deck=False, mat=CONC), + lambda: kicker('launch_high', w=1.5, h=0.9, run=1.6, desc='high launch ramp'), + square_flatbar, pole_jam, + lambda: rail_from('rail_down', [(-1.5, 0.78), (1.5, 0.18)], + (-1.3, 1.3), 'straight down rail'), + lambda: rail_from('rail_kink2', [(-2.2, 0.68), (-1.0, 0.68), (0.1, 0.36), + (0.9, 0.36), (2.2, 0.08)], + (-2.0, -0.4, 0.6, 2.0), 'double-kink down rail'), + lambda: rail_from('rail_aframe', [(-1.8, 0.12), (0, 0.72), (1.8, 0.12)], + (-1.6, 0, 1.6), 'A-frame rail'), + lambda: rail_from('rail_donkey', [(-1.6, 0.42), (0.6, 0.42), (1.5, 0.8)], + (-1.4, 0.4, 1.35), 'donkey-kick rail — kinks up at the end'), + lambda: grind_box('box_low', 1.8, 0.5, 0.25, 'low grind box 10in'), + lambda: grind_box('box_table', 2.4, 0.7, 0.62, 'high table box 24in'), + hubba_down, + lambda: sloped_box('manny_incline', 2.0, 1.6, 0.18, 0.4, + 'inclined manual pad', graf=True), + pyramid, euro_gap, volcano, roller, + jersey_barrier, parking_block, hydrant, pool_edge, + fence_chain, floodlight, bleachers, drain_grate, ] def main(): diff --git a/tools/propkit.py b/tools/propkit.py index 99ae3ee..d3303f4 100644 --- a/tools/propkit.py +++ b/tools/propkit.py @@ -38,11 +38,13 @@ def write_glb(path, gjson, bin_): # ---------------------------------------------------------------- materials class Mat: - """tex: basename in textures/ (no ext) or None for flat colour.""" + """tex: basename in textures/ (no ext) or None for flat colour. + tint alpha < 1 -> BLEND (chain-link mesh); emissive -> emissiveFactor.""" def __init__(self, name, tex=None, tint=(1, 1, 1, 1), rough=0.9, metal=0.0, - uv=1.0, double=False): + uv=1.0, double=False, emissive=None): self.name, self.tex, self.tint = name, tex, tuple(tint) self.rough, self.metal, self.uv, self.double = rough, metal, uv, double + self.emissive = emissive def key(self): return self.name # ---------------------------------------------------------------- part math @@ -149,7 +151,10 @@ def lathe(profile, seg=16, jitter=None, cap_top=True, cap_bot=False): rows.append(row) # weld the seam ring so jitter matches rows[-1] = rows[0] - pt = sheet(rows, flip=True) + # rows=angle, cols=profile: unflipped winding already faces OUTWARD + # (profile-tangent x angle-tangent = radial). flip=True shipped every + # lathe inside-out — invisible on cylinders, obvious on the volcano cone. + pt = sheet(rows) # match tube(): u wraps the girth, v climbs the profile (bark ridges stay # vertical — the transposed mapping chevroned the fig trunk) pt["UV"] = [(v, u) for (u, v) in pt["UV"]] @@ -283,6 +288,8 @@ class Prop: "metallicFactor": mat.metal, "roughnessFactor": mat.rough}} if mat.double: m["doubleSided"] = True + if mat.tint[3] < 1.0: m["alphaMode"] = "BLEND" + if mat.emissive: m["emissiveFactor"] = list(mat.emissive) if mat.tex: if mat.tex not in teximg: ext = None