diff --git a/manifest.json b/manifest.json index d92e594..eee6729 100644 --- a/manifest.json +++ b/manifest.json @@ -1865,6 +1865,52 @@ "collider": { "type": "none" } + }, + { + "id": "trolley", + "src": "props/trolley.glb", + "category": "furniture", + "desc": "abandoned shopping trolley \u2014 gap bait", + "size": [ + 0.936, + 1.022, + 0.552 + ], + "tris": 384, + "grinds": [ + { + "a": [ + -0.52, + -0.25 + ], + "b": [ + -0.52, + 0.25 + ], + "ya": 1.0, + "yb": 1.0, + "kind": "rail" + }, + { + "a": [ + 0.38, + -0.26 + ], + "b": [ + 0.38, + 0.26 + ], + "ya": 0.95, + "yb": 0.95, + "kind": "rail" + } + ], + "collider": { + "type": "box", + "hw": 0.52, + "hd": 0.3, + "h": 1.0 + } } ] } \ No newline at end of file diff --git a/props/trolley.glb b/props/trolley.glb new file mode 100644 index 0000000..9fae131 Binary files /dev/null and b/props/trolley.glb differ diff --git a/tools/prop_author.py b/tools/prop_author.py index c4f4df5..702545c 100644 --- a/tools/prop_author.py +++ b/tools/prop_author.py @@ -750,6 +750,48 @@ def bleachers(): p.collider = {'type': 'box', 'hw': L / 2, 'hd': 0.75, 'h': 0.92} return p +def trolley(): + """Shopping trolley. Lives in every Brisbane park whether the park likes it + or not. Wire basket faked with translucent panels; handle bar grinds.""" + p = Prop('trolley', 'furniture', 'abandoned shopping trolley — gap bait') + WIRE = Mat('trolley_wire', tint=(0.72, 0.75, 0.78, 0.45), rough=0.4, + metal=0.8, double=True) + # basket: tapered shell, small base -> wide mouth + b = {'x0': -0.28, 'x1': 0.30, 'z': 0.20, 'y': 0.50} + t = {'x0': -0.44, 'x1': 0.38, 'z': 0.26, 'y': 0.95} + q = lambda a, bb, c, d: quad(a, bb, c, d) + p.add(WIRE, q((b['x0'], b['y'], -b['z']), (b['x1'], b['y'], -b['z']), + (t['x1'], t['y'], -t['z']), (t['x0'], t['y'], -t['z']))) + p.add(WIRE, q((b['x1'], b['y'], b['z']), (b['x0'], b['y'], b['z']), + (t['x0'], t['y'], t['z']), (t['x1'], t['y'], t['z']))) + p.add(WIRE, q((b['x1'], b['y'], -b['z']), (b['x1'], b['y'], b['z']), + (t['x1'], t['y'], t['z']), (t['x1'], t['y'], -t['z']))) + p.add(WIRE, q((b['x0'], b['y'], b['z']), (b['x0'], b['y'], -b['z']), + (t['x0'], t['y'], -t['z']), (t['x0'], t['y'], t['z']))) + p.add(WIRE, box(( b['x0'] + b['x1']) / 2, b['y'], 0, + b['x1'] - b['x0'], 0.015, 2 * b['z'])) + # top rim + handle + frame + rim = [(t['x0'], t['y'], -t['z']), (t['x1'], t['y'], -t['z']), + (t['x1'], t['y'], t['z']), (t['x0'], t['y'], t['z']), + (t['x0'], t['y'], -t['z'])] + p.add(GALV, tube(rim, 0.016, seg=6)) + p.add(GALV, tube([(-0.52, 1.0, -0.25), (-0.52, 1.0, 0.25)], 0.022, seg=8)) + for sz in (-1, 1): + p.add(GALV, tube([(-0.42, 0.05, sz * 0.22), (-0.52, 0.98, sz * 0.25)], + 0.016, seg=6)) + p.add(GALV, tube([(0.34, 0.05, sz * 0.22), (t['x1'], t['y'] - 0.02, sz * t['z'])], + 0.016, seg=6)) + p.add(GALV, tube([(-0.42, 0.08, sz * 0.22), (0.34, 0.08, sz * 0.22)], + 0.014, seg=6)) + for wx in (-0.42, 0.34): # casters + for sz in (-1, 1): + p.add(STEEL, tube([(wx, 0.045, sz * 0.22 - 0.015), (wx, 0.045, sz * 0.22 + 0.015)], + 0.045, seg=8)) + p.grind(-0.52, -0.25, -0.52, 0.25, 1.0, 1.0, 'rail') # handle bar + p.grind(t['x1'], -t['z'], t['x1'], t['z'], t['y'], t['y'], 'rail') # front rim + p.collider = {'type': 'box', 'hw': 0.52, 'hd': 0.3, 'h': 1.0} + 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), @@ -793,7 +835,7 @@ BUILDERS = [ 'inclined manual pad', graf=True), pyramid, euro_gap, volcano, roller, jersey_barrier, parking_block, hydrant, pool_edge, - fence_chain, floodlight, bleachers, drain_grate, + fence_chain, floodlight, bleachers, drain_grate, trolley, ] def main():