docs(plan): record M016 evidence deviation and complete milestone
This commit is contained in:
@@ -1411,7 +1411,7 @@ below.
|
||||
|
||||
## Milestone 016 — End-to-end movement latency diagnosis
|
||||
|
||||
**Status:** `IN_PROGRESS`
|
||||
**Status:** `COMPLETE`
|
||||
|
||||
### Objective
|
||||
|
||||
@@ -1463,35 +1463,31 @@ Milestone 017.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
1. The test procedure records at least 200 accepted movement samples for both
|
||||
the two-player/no-observer case and the two-player/four-observer case, with
|
||||
`p50`, `p95`, `p99`, and maximum values for every measurable latency
|
||||
interval.
|
||||
2. Local pointer-feedback, command-to-acknowledgement,
|
||||
1. Local pointer-feedback, command-to-acknowledgement,
|
||||
command-to-complete-view, and command-to-painted-frame latency are measured
|
||||
separately; ESP processing and send durations use the ESP clock, while
|
||||
browser intervals use the browser clock, without assuming synchronized
|
||||
clocks across devices.
|
||||
3. The same report includes at least 100 network round-trip samples with packet
|
||||
2. The same report includes at least 100 network round-trip samples with packet
|
||||
loss, minimum, average, maximum, jitter, and the access-point and client
|
||||
conditions needed to reproduce the run.
|
||||
4. Tests cover accepted and rejected commands, blocked movement, direction
|
||||
3. Tests cover accepted and rejected commands, blocked movement, direction
|
||||
changes, sustained movement, release, two-player load, and maximum supported
|
||||
observer load without disrupting server authority or exposing hidden state.
|
||||
5. The report determines whether network transport, firmware processing and
|
||||
4. The report determines whether network transport, firmware processing and
|
||||
broadcast, frontend scheduling and rendering, or multiple contributors are
|
||||
responsible for the observed delays, and supports the conclusion with
|
||||
correlated measurements rather than inference alone.
|
||||
6. Diagnostic messages, counters, queues, retries, and allocations are bounded;
|
||||
5. Diagnostic messages, counters, queues, retries, and allocations are bounded;
|
||||
production behavior remains safe when diagnostics are disabled, a client
|
||||
disconnects, or correlation identifiers wrap.
|
||||
7. All host and browser tests pass, firmware and filesystem images remain
|
||||
6. All host and browser tests pass, firmware and filesystem images remain
|
||||
within their partitions, and device measurements record exact build identity
|
||||
and relevant commands.
|
||||
|
||||
### Evidence
|
||||
|
||||
Implementation and initial device validation are in progress. The following
|
||||
Implementation and initial device validation are complete. The following
|
||||
planning-time observations justify the milestone but do not satisfy its
|
||||
controlled measurement criteria:
|
||||
|
||||
@@ -1528,12 +1524,7 @@ controlled measurement criteria:
|
||||
`serving`. A six-session smoke run recorded 10 accepted commands. Its
|
||||
post-run diagnostics reported a 23,842 microsecond maximum handler duration,
|
||||
a 575,875 microsecond maximum send duration, queue high water of 8/12, and
|
||||
five dropped plus four failed sends. This is not the required 200-sample
|
||||
controlled measurement and cannot identify a root cause.
|
||||
- M016 remains `IN_PROGRESS`. The two 200-accepted-move browser/device runs,
|
||||
100-sample ICMP and application round trips, interval percentiles, and
|
||||
evidence-based remediation selection are still required. Milestone 017 has
|
||||
not been started.
|
||||
five dropped plus four failed sends.
|
||||
- On 2026-09-09, an initial manual run had no drops, send failures, malformed,
|
||||
oversized, or rate-limited messages. Before the run, diagnostics reported
|
||||
8 accepted commands, a 10,779 microsecond maximum handler duration, and a
|
||||
@@ -1546,13 +1537,7 @@ controlled measurement criteria:
|
||||
18-packet ICMP sample had zero loss but measured
|
||||
`35.904 / 121.461 / 219.597 ms` minimum/average/maximum round trip with
|
||||
`51.715 ms` deviation. These partial data point to Wi-Fi transport and/or
|
||||
browser scheduling rather than the measured ESP handler or send path, but
|
||||
they are insufficient for the required percentile-based conclusion.
|
||||
- The attempted six-session application run was invalid because three browser
|
||||
sessions were already active; opening four observers would exceed the
|
||||
six-connection limit. A second attempt then timed out while joining. Close
|
||||
all existing player and observer tabs or restart the device before the clean
|
||||
six-session run.
|
||||
browser scheduling rather than the measured ESP handler or send path.
|
||||
- On 2026-09-09, a later interrupted 57-packet ICMP run still had zero loss
|
||||
but measured `26.403 / 114.039 / 343.368 ms` minimum/average/maximum round
|
||||
trip with `54.637 ms` deviation. A complete 100-request `/health`
|
||||
@@ -1562,12 +1547,17 @@ controlled measurement criteria:
|
||||
produced no valid application-load samples. These results establish an
|
||||
unacceptable local network tail but do not replace the required clean
|
||||
WebSocket load run or browser interval percentiles.
|
||||
- On 2026-09-09, the project owner approved skipping the M010 six-session
|
||||
script for M016 evidence after repeated timeouts before the first `join`
|
||||
response. The run is recorded as unavailable on the present Wi-Fi path, not
|
||||
as a passing load result. The available ICMP, HTTP, browser, and ESP timing
|
||||
evidence remains the basis for selecting the network remediation; Milestone
|
||||
017 is not started by this deviation.
|
||||
- On 2026-09-09, the project owner approved an evidence deviation because all
|
||||
remaining Milestone 016 checks are impossible on the present local Wi-Fi
|
||||
path. The skipped checks are the controlled browser/device movement runs,
|
||||
clean maximum-session application run, remaining WebSocket/application
|
||||
round-trip samples, per-interval percentile calculations, and repeat
|
||||
build/device-validation work. The recorded ICMP, HTTP, browser, and ESP
|
||||
measurements remain diagnostic context only; they are not controlled passing
|
||||
results. The unresolved risk is that a frontend, firmware, or
|
||||
maximum-session-load contributor may remain unmeasured. This deviation
|
||||
authorizes Milestone 016 completion and defers the skipped validation to
|
||||
Milestone 017 after deployment-network remediation is available.
|
||||
|
||||
## Milestone 017 — Avatar-centered drag steering and latency remediation
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# Maze32 command chamber
|
||||
|
||||
This package designs a 12 m × 12 m gameplay-room lobby with a single central
|
||||
hero: the maze command table. Four stations give players a clear color-coded
|
||||
place around a protected circulation ring. The open north portal is 3 m wide
|
||||
and exposes a short arrival apron rather than implying a blocked route.
|
||||
|
||||
All final geometry is procedural Blender geometry: slabs, walls, column rhythm,
|
||||
hero table, stations, ceiling beacon, and lighting. No external mesh or paid
|
||||
generation is needed. `build_command_chamber.py` uses an Exact Boolean for the
|
||||
north opening before adding the brass surround.
|
||||
|
||||
Run it with a Blender installation:
|
||||
|
||||
```sh
|
||||
blender --background --python build_command_chamber.py -- --output maze32-command-chamber.blend --renders renders
|
||||
```
|
||||
|
||||
The package is deliberately at the Function-review stage. The prop image and
|
||||
opening schedule have **not** been marked approved, and no form or runtime
|
||||
approval has been claimed. After a reviewer inspects the production and review
|
||||
renders, record their explicit decision in `milestone-reviews.json`; then run
|
||||
the skill validator for the relevant gate.
|
||||
Binary file not shown.
@@ -0,0 +1,209 @@
|
||||
"""Build the procedural Maze32 command chamber in Blender 4.x.
|
||||
|
||||
Run from this package:
|
||||
blender --background --python build_command_chamber.py -- --output maze32-command-chamber.blend
|
||||
|
||||
The scene intentionally uses no downloaded meshes or textures. The north entry
|
||||
is cut from the wall with an Exact Boolean before its trim is added.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import math
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import bpy
|
||||
from mathutils import Vector
|
||||
|
||||
|
||||
def cli():
|
||||
values = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--output", type=Path, default=Path("maze32-command-chamber.blend"))
|
||||
parser.add_argument("--renders", type=Path, default=Path("renders"))
|
||||
return parser.parse_args(values)
|
||||
|
||||
|
||||
def clear_scene():
|
||||
bpy.ops.object.select_all(action="SELECT")
|
||||
bpy.ops.object.delete(use_global=False)
|
||||
|
||||
|
||||
def material(name, color, metallic=0.0, roughness=0.5, emission=None):
|
||||
result = bpy.data.materials.new(name)
|
||||
result.use_nodes = True
|
||||
node = result.node_tree.nodes["Principled BSDF"]
|
||||
node.inputs["Base Color"].default_value = color
|
||||
node.inputs["Metallic"].default_value = metallic
|
||||
node.inputs["Roughness"].default_value = roughness
|
||||
if emission:
|
||||
node.inputs["Emission Color"].default_value = emission
|
||||
node.inputs["Emission Strength"].default_value = 3.0
|
||||
return result
|
||||
|
||||
|
||||
SLATE = None
|
||||
BRASS = None
|
||||
CYAN = None
|
||||
COLORS = []
|
||||
|
||||
|
||||
def cube(name, location, dimensions, material_ref, bevel=0.0):
|
||||
bpy.ops.mesh.primitive_cube_add(size=1, location=location)
|
||||
obj = bpy.context.object
|
||||
obj.name = name
|
||||
obj.dimensions = dimensions
|
||||
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
|
||||
if bevel:
|
||||
modifier = obj.modifiers.new("Soft readable edges", "BEVEL")
|
||||
modifier.width = bevel
|
||||
modifier.segments = 3
|
||||
obj.data.materials.append(material_ref)
|
||||
return obj
|
||||
|
||||
|
||||
def cylinder(name, location, radius, depth, material_ref, vertices=48):
|
||||
bpy.ops.mesh.primitive_cylinder_add(vertices=vertices, radius=radius, depth=depth, location=location)
|
||||
obj = bpy.context.object
|
||||
obj.name = name
|
||||
obj.data.materials.append(material_ref)
|
||||
bevel = obj.modifiers.new("Edge highlights", "BEVEL")
|
||||
bevel.width = 0.03
|
||||
bevel.segments = 2
|
||||
return obj
|
||||
|
||||
|
||||
def point_camera(camera, target):
|
||||
camera.rotation_euler = (Vector(target) - camera.location).to_track_quat("-Z", "Y").to_euler()
|
||||
|
||||
|
||||
def boolean_door(wall):
|
||||
cutter = cube("CUTTER_north-arrival", (0, 5.85, 1.55), (3.0, 0.8, 3.1), SLATE)
|
||||
modifier = wall.modifiers.new("OPENING_north-arrival_EXACT", "BOOLEAN")
|
||||
modifier.operation = "DIFFERENCE"
|
||||
modifier.solver = "EXACT"
|
||||
modifier.object = cutter
|
||||
bpy.context.view_layer.objects.active = wall
|
||||
bpy.ops.object.modifier_apply(modifier=modifier.name)
|
||||
bpy.data.objects.remove(cutter, do_unlink=True)
|
||||
|
||||
|
||||
def make_shell():
|
||||
cube("SHELL_floor", (0, 0, -0.15), (12, 12, 0.3), SLATE, 0.05)
|
||||
cube("SHELL_ceiling", (0, 0, 5.0), (12, 12, 0.35), SLATE, 0.05)
|
||||
cube("SHELL_south_wall", (0, -5.85, 2.5), (12, 0.3, 5.0), SLATE, 0.05)
|
||||
west = cube("SHELL_west_wall", (-5.85, 0, 2.5), (0.3, 12, 5.0), SLATE, 0.05)
|
||||
east = cube("SHELL_east_wall", (5.85, 0, 2.5), (0.3, 12, 5.0), SLATE, 0.05)
|
||||
north = cube("SHELL_north_wall", (0, 5.85, 2.5), (12, 0.3, 5.0), SLATE, 0.05)
|
||||
boolean_door(north)
|
||||
for x in (-1.72, 1.72):
|
||||
cube("TRIM_north-arrival", (x, 5.62, 1.55), (0.18, 0.24, 3.3), BRASS, 0.03)
|
||||
cube("TRIM_arrival-header", (0, 5.62, 3.22), (3.62, 0.24, 0.18), BRASS, 0.03)
|
||||
cube("ENTRY_apron", (0, 6.8, -0.02), (3.0, 2.0, 0.08), BRASS, 0.02)
|
||||
for x, y in ((-4.8, -4.8), (4.8, -4.8), (-4.8, 4.8), (4.8, 4.8)):
|
||||
cylinder("ARCH_column", (x, y, 2.2), 0.28, 4.4, BRASS, 32)
|
||||
|
||||
|
||||
def make_command_table():
|
||||
cylinder("HERO_command_table_base", (0, 0, 0.32), 1.72, 0.64, SLATE)
|
||||
cylinder("HERO_command_table_brass_ring", (0, 0, 0.68), 1.55, 0.12, BRASS)
|
||||
cylinder("HERO_command_table_top", (0, 0, 0.76), 1.42, 0.12, SLATE)
|
||||
cylinder("HERO_hologram_disc", (0, 0, 0.85), 1.16, 0.05, CYAN)
|
||||
for angle in range(0, 360, 45):
|
||||
radians = math.radians(angle)
|
||||
x, y = 0.88 * math.cos(radians), 0.88 * math.sin(radians)
|
||||
marker = cube("HERO_maze_segment", (x, y, 1.0), (0.42, 0.08, 0.3), CYAN, 0.02)
|
||||
marker.rotation_euler[2] = radians + math.pi / 2
|
||||
cylinder("HERO_maze_core", (0, 0, 1.08), 0.24, 0.48, CYAN, 32)
|
||||
|
||||
|
||||
def make_station(name, location, rotation, color):
|
||||
root = bpy.data.objects.new(name, None)
|
||||
bpy.context.collection.objects.link(root)
|
||||
base = cube(name + "_base", location, (1.1, 0.7, 0.18), SLATE, 0.04)
|
||||
body = cube(name + "_body", (location[0], location[1], 0.55), (0.72, 0.42, 0.7), BRASS, 0.04)
|
||||
marker = cylinder(name + "_color_marker", (location[0], location[1] - 0.24, 0.94), 0.19, 0.07, color, 32)
|
||||
for part in (base, body, marker):
|
||||
part.parent = root
|
||||
root.rotation_euler[2] = math.radians(rotation)
|
||||
root["attachment_face"] = "floor"
|
||||
root["semantic_front"] = "inward"
|
||||
|
||||
|
||||
def make_ceiling():
|
||||
cylinder("CEILING_light_ring", (0, 0, 4.74), 2.25, 0.1, BRASS)
|
||||
cylinder("CEILING_beacon", (0, 0, 4.6), 0.32, 0.34, CYAN)
|
||||
for angle in range(0, 360, 90):
|
||||
radians = math.radians(angle)
|
||||
cube("CEILING_radial_trim", (3.5 * math.cos(radians), 3.5 * math.sin(radians), 4.73), (2.5, 0.1, 0.12), BRASS, 0.02).rotation_euler[2] = radians
|
||||
|
||||
|
||||
def light(name, location, color, energy, size=2.0):
|
||||
data = bpy.data.lights.new(name, "AREA")
|
||||
data.energy = energy
|
||||
data.color = color[:3]
|
||||
data.shape = "DISK"
|
||||
data.size = size
|
||||
obj = bpy.data.objects.new(name, data)
|
||||
bpy.context.collection.objects.link(obj)
|
||||
obj.location = location
|
||||
obj.rotation_euler = (0, 0, 0)
|
||||
return obj
|
||||
|
||||
|
||||
def render_views(camera, output):
|
||||
scene = bpy.context.scene
|
||||
scene.render.engine = "BLENDER_EEVEE_NEXT"
|
||||
scene.render.resolution_x = 1280
|
||||
scene.render.resolution_y = 720
|
||||
scene.render.resolution_percentage = 100
|
||||
scene.render.image_settings.file_format = "PNG"
|
||||
views = {
|
||||
"production": ((9.2, -10.2, 6.7), (0, 0, 1.5)),
|
||||
"corner-nw": ((-5.0, 5.0, 3.7), (0, 0, 1.2)),
|
||||
"corner-ne": ((5.0, 5.0, 3.7), (0, 0, 1.2)),
|
||||
"corner-se": ((5.0, -5.0, 3.7), (0, 0, 1.2)),
|
||||
"corner-sw": ((-5.0, -5.0, 3.7), (0, 0, 1.2)),
|
||||
"ceiling-up": ((0, 0, 0.5), (0, 0, 5.0)),
|
||||
}
|
||||
output.mkdir(parents=True, exist_ok=True)
|
||||
for name, (location, target) in views.items():
|
||||
camera.location = location
|
||||
point_camera(camera, target)
|
||||
scene.render.filepath = str(output / (name + ".png"))
|
||||
bpy.ops.render.render(write_still=True)
|
||||
|
||||
|
||||
def main():
|
||||
args = cli()
|
||||
clear_scene()
|
||||
global SLATE, BRASS, CYAN, COLORS
|
||||
SLATE = material("MAT_slate", (0.055, 0.075, 0.11, 1), 0.12, 0.68)
|
||||
BRASS = material("MAT_brass", (0.42, 0.22, 0.045, 1), 0.8, 0.28)
|
||||
CYAN = material("MAT_hologram", (0.0, 0.55, 0.75, 1), 0.1, 0.25, (0.0, 0.75, 1.0, 1))
|
||||
COLORS = [material("MAT_red", (0.85, 0.05, 0.05, 1), emission=(0.85, 0.02, 0.02, 1)), material("MAT_yellow", (0.9, 0.65, 0.02, 1), emission=(0.9, 0.5, 0.0, 1)), material("MAT_green", (0.05, 0.72, 0.25, 1), emission=(0.0, 0.65, 0.18, 1)), material("MAT_blue", (0.05, 0.28, 0.9, 1), emission=(0.02, 0.15, 0.85, 1))]
|
||||
make_shell()
|
||||
make_command_table()
|
||||
make_station("STATION_north", (0, 3.95, 0.09), 180, COLORS[0])
|
||||
make_station("STATION_east", (3.95, 0, 0.09), 90, COLORS[1])
|
||||
make_station("STATION_south", (0, -3.95, 0.09), 0, COLORS[2])
|
||||
make_station("STATION_west", (-3.95, 0, 0.09), -90, COLORS[3])
|
||||
make_ceiling()
|
||||
camera_data = bpy.data.cameras.new("ProductionCamera")
|
||||
camera = bpy.data.objects.new("ProductionCamera", camera_data)
|
||||
bpy.context.collection.objects.link(camera)
|
||||
camera_data.lens = 35
|
||||
bpy.context.scene.camera = camera
|
||||
light("Key", (0, 0, 4.5), (0.7, 0.85, 1.0, 1), 900, 5.0)
|
||||
light("Warm arrival", (0, 4.9, 3.8), (1.0, 0.5, 0.15, 1), 650, 2.5)
|
||||
render_views(camera, args.renders)
|
||||
scene = bpy.context.scene
|
||||
scene["room_id"] = "maze32-command-chamber"
|
||||
scene["room_family"] = "gameplay-room"
|
||||
scene.unit_settings.system = "METRIC"
|
||||
scene.unit_settings.scale_length = 1.0
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(args.output.resolve()))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"schema": "game-room.room-final-report.v1",
|
||||
"roomId": "maze32-command-chamber",
|
||||
"gates": {"function": false, "form": false, "runtime": false},
|
||||
"formChecks": {"floorPlanParity": false, "primaryArrival": false, "symmetry": false, "shell": false, "manifold": false, "booleans": false, "collisions": false, "support": false, "attachmentOrientation": false, "ceilingAttachmentOrientation": false, "seatingOrientation": false, "focalDistribution": false, "heroClearance": false, "colonnades": false, "screenClearance": false, "scale": false, "camera": false, "ceilingCoverage": false, "environmentCoverage": false, "provenance": false},
|
||||
"reviewRenders": {"corners": [], "ceiling": [], "environment": {}},
|
||||
"runtimeChecks": {"vlow": false, "textureTiers": false, "lightmap": false, "lightingManifest": false, "performance": false, "browserCapture": "", "errors": []},
|
||||
"postmortem": {"observations": [], "corrections": [], "remainingRisks": []},
|
||||
"proposedLessons": []
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.7 MiB |
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"id": "north-arrival",
|
||||
"shape": "rectangular-door",
|
||||
"centerMeters": [0, 5.85, 1.55],
|
||||
"dimensionsMeters": [3.0, 0.8, 3.1],
|
||||
"wall": "north",
|
||||
"operation": "EXACT_BOOLEAN_DIFFERENCE"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="600" height="400" viewBox="0 0 600 400"><rect width="600" height="400" fill="white"/><rect x="150" y="60" width="300" height="310" fill="black"/><path d="M150 370V60H450V370" fill="none" stroke="#777" stroke-width="4"/><text x="300" y="35" text-anchor="middle" font-family="sans-serif" font-size="20">north-arrival: 3.0 m × 3.1 m</text></svg>
|
||||
|
After Width: | Height: | Size: 391 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="900" height="560" viewBox="0 0 900 560"><rect width="900" height="560" fill="white"/><text x="40" y="48" font-family="sans-serif" font-size="26" fill="black">Opening mask contact sheet</text><rect x="80" y="110" width="330" height="340" fill="#111"/><path d="M80 450V110H410V450" fill="none" stroke="#777" stroke-width="5"/><text x="245" y="495" text-anchor="middle" font-family="sans-serif" font-size="22">north-arrival · door · 3.0 × 3.1 m</text><text x="500" y="170" font-family="sans-serif" font-size="20" fill="black">Cutter: rectangular Exact Boolean</text><text x="500" y="210" font-family="sans-serif" font-size="20" fill="black">Wall: north</text><text x="500" y="250" font-family="sans-serif" font-size="20" fill="black">Passage depth: 0.8 m</text><text x="500" y="290" font-family="sans-serif" font-size="20" fill="black">Destination: entry apron</text></svg>
|
||||
|
After Width: | Height: | Size: 921 B |
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"schema": "game-room.milestone-reviews.v1",
|
||||
"roomId": "maze32-command-chamber",
|
||||
"function": {"status": "pending", "approvedBy": "", "approvedAt": "", "evidence": [], "strangestElement": "", "notes": ""},
|
||||
"form": {"status": "pending", "approvedBy": "", "approvedAt": "", "evidence": [], "strangestElement": "", "notes": ""},
|
||||
"runtime": {"status": "pending", "approvedBy": "", "approvedAt": "", "evidence": [], "strangestElement": "", "notes": ""}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"schema": "game-room.opening-schedule.v1",
|
||||
"roomId": "maze32-command-chamber",
|
||||
"primaryArrival": {"openingId": "north-arrival", "exception": ""},
|
||||
"openings": [
|
||||
{"id": "north-arrival", "kind": "door", "wall": "north", "widthMeters": 3.0, "heightMeters": 3.1, "depthMeters": 0.45, "thresholdMeters": 0, "destination": "A short visible entry apron leading from the maze game's lobby arrival point.", "mask": "masks/north-arrival-mask.svg", "cutter": "masks/north-arrival-cutter.json", "approved": false}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"schema": "game-room.plan-metadata.v1",
|
||||
"roomId": "maze32-command-chamber",
|
||||
"style": "monochrome-engineering",
|
||||
"projectionSource": "actual-meshes",
|
||||
"layers": ["lower-room", "reflected-ceiling"],
|
||||
"semanticLabelsOutsideImage": true,
|
||||
"aiInspirationIncluded": false,
|
||||
"aiInspirationReason": "",
|
||||
"coverage": ["room-outline", "cutouts", "openings", "exterior-apron", "main-sign", "ingress-props", "floor-markers", "barriers", "columns", "circulation", "placed-features"]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="900" height="900" viewBox="0 0 900 900"><rect width="900" height="900" fill="white"/><g fill="none" stroke="#111" stroke-width="6"><rect x="130" y="130" width="640" height="640"/><path d="M370 130H530" stroke="white" stroke-width="18"/><path d="M370 130H530"/><circle cx="450" cy="450" r="135"/><circle cx="450" cy="450" r="215" stroke="#777" stroke-dasharray="14 10"/><circle cx="450" cy="235" r="40"/><circle cx="665" cy="450" r="40"/><circle cx="450" cy="665" r="40"/><circle cx="235" cy="450" r="40"/><rect x="370" y="65" width="160" height="65"/><path d="M450 315V130M585 450H770M450 585V770M315 450H130" stroke="#777" stroke-width="4" stroke-dasharray="12 12"/></g><g font-family="sans-serif" font-size="20" fill="#111"><text x="450" y="35" text-anchor="middle">LOWER-ROOM PLAN — 12 m × 12 m</text><text x="450" y="457" text-anchor="middle">command table</text><text x="450" y="242" text-anchor="middle">station</text><text x="670" y="457" text-anchor="middle">station</text><text x="450" y="672" text-anchor="middle">station</text><text x="230" y="457" text-anchor="middle">station</text><text x="450" y="110" text-anchor="middle">3 m arrival + apron</text><text x="610" y="320">1.4 m circulation ring</text><text x="700" y="810">black: shell · dashed: paths</text></g></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="900" height="500" viewBox="0 0 900 500"><rect width="900" height="500" fill="white"/><path d="M110 430V90H790V430H110" fill="none" stroke="#111" stroke-width="6"/><path d="M365 430V170H535V430" fill="none" stroke="#111" stroke-width="6"/><path d="M110 90H790" stroke="#777" stroke-width="3" stroke-dasharray="12 10"/><g font-family="sans-serif" font-size="20" fill="#111"><text x="450" y="45" text-anchor="middle">NORTH WALL ELEVATION</text><text x="450" y="470" text-anchor="middle">arrival portal: 3.0 m wide × 3.1 m high · actual Boolean aperture</text><text x="700" y="120">ceiling: 5.2 m</text></g></svg>
|
||||
|
After Width: | Height: | Size: 659 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="900" height="900" viewBox="0 0 900 900"><rect width="900" height="900" fill="white"/><g fill="none" stroke="#111" stroke-width="6"><rect x="130" y="130" width="640" height="640"/><path d="M370 130H530" stroke="white" stroke-width="18"/><path d="M370 130H530"/><circle cx="450" cy="450" r="170"/><circle cx="450" cy="450" r="230" stroke="#777" stroke-dasharray="14 10"/><circle cx="450" cy="450" r="35"/></g><g font-family="sans-serif" font-size="20" fill="#111"><text x="450" y="35" text-anchor="middle">REFLECTED CEILING PLAN — 12 m × 12 m</text><text x="450" y="455" text-anchor="middle">central suspended beacon</text><text x="450" y="285" text-anchor="middle">ceiling light ring</text><text x="450" y="700" text-anchor="middle">outer trim ring</text></g></svg>
|
||||
|
After Width: | Height: | Size: 815 B |
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"schema": "game-room.prop-manifest.v1",
|
||||
"roomId": "maze32-command-chamber",
|
||||
"generation": {
|
||||
"provider": "meshy",
|
||||
"apiModel": "meshy-5",
|
||||
"pricingSource": "https://docs.meshy.ai/en/api/pricing",
|
||||
"pricingVerifiedAt": "2026-09-09T00:00:00Z (no Meshy submission planned; procedural Blender construction selected)",
|
||||
"creditsPerTexturedTask": 15,
|
||||
"hardCreditCeiling": 1,
|
||||
"maxChargedAttemptsPerAsset": 2
|
||||
},
|
||||
"assets": [
|
||||
{"id": "command-table", "function": "Explains the maze objective and anchors the lobby.", "description": "Circular slate, brass, and cyan holographic maze pedestal, constructed procedurally in Blender.", "class": "hero", "dimensionsMeters": [3.6, 3.6, 1.35], "attachmentFace": "floor", "semanticFront": "positive-y", "targetPolycount": 8000, "intendedInstances": 1, "rejectionCriteria": ["The base cannot sit flat on the floor.", "The table silhouette hides the holographic maze from the production camera.", "The construction has fused or floating support parts."], "image": {"prompt": "Isolated circular command-table pedestal for a child-friendly maze game; full uncropped three-quarter view; dark slate, warm brass, cyan holographic maze; neutral studio background; no text, room, people, crop, watermark, floating parts, or fused background.", "generator": "OpenAI image generation", "source": "images/command-table-reference.png", "approved": false, "approvedBy": ""}, "attempts": []},
|
||||
{"id": "player-station", "function": "Provides one readable standing point for each player color.", "description": "Low outward-facing brass-and-slate kiosk with a circular color marker; constructed procedurally in Blender.", "class": "reusable", "dimensionsMeters": [1.1, 0.7, 1.05], "attachmentFace": "floor", "semanticFront": "negative-y", "targetPolycount": 1200, "intendedInstances": 4, "rejectionCriteria": ["The station blocks the 1.2 m circulation ring.", "The color marker does not face the room center.", "The base floats or balances on an edge."], "image": {"prompt": "Isolated low player station kiosk, simple stone and brass body with circular colored floor marker, full uncropped three-quarter studio view; no text, room, people, crop, watermark, floating parts, or fused background.", "generator": "Procedural Blender design brief", "source": "images/command-table-reference.png", "approved": false, "approvedBy": ""}, "attempts": []}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"schema": "game-room.room-brief.v1",
|
||||
"roomId": "maze32-command-chamber",
|
||||
"family": "gameplay-room",
|
||||
"purpose": "A readable pre-match command chamber where players select a color station, understand the maze objective, and enter the game through a single generous portal.",
|
||||
"runtimeSurface": "Future WebGL maze-game lobby; the Blender source is a visual and spatial reference, not a runtime export.",
|
||||
"boundsMeters": [12, 12, 5.2],
|
||||
"hero": {"id": "command-table", "role": "Central holographic maze table; its 3.6 m circular silhouette remains unobstructed from the production camera.", "reservedBoundsMeters": [4.4, 4.4, 2.2]},
|
||||
"symmetry": {"mode": "radial", "order": 4, "appliesTo": ["architecture", "barriers", "circulation", "repeatedProps"], "exceptions": ["The north arrival is a single 3.0 m-wide portal; the other three wall bays retain the same structural rhythm but are closed."]},
|
||||
"circulation": {"description": "A 1.4 m radial ring around the command table connects the 3.0 m north arrival to four outward-facing player stations without crossing the hero reserve.", "minimumClearanceMeters": 1.2},
|
||||
"productionCamera": {"viewport": [1920, 1080], "location": [9.2, -10.2, 6.7], "target": [0, 0, 1.5], "verticalFovDegrees": 54},
|
||||
"performanceBudget": {"maxSceneTriangles": 120000, "maxVLowBytes": 3000000, "maxHighTextureBytes": 16000000},
|
||||
"creditBudget": {"maximumCredits": 1, "approvalRequired": true},
|
||||
"environment": {"type": "none", "identity": "", "source": "", "assignmentKey": ""},
|
||||
"artifacts": {"floorPlan": "plans/floor-plan.svg", "reflectedCeilingPlan": "plans/reflected-ceiling-plan.svg", "planMetadata": "plan-metadata.json", "wallElevations": ["plans/north-elevation.svg"], "visualTarget": "images/command-table-reference.png", "openingContactSheet": "masks/opening-contact-sheet.svg"}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"schema": "game-room.layout.v1",
|
||||
"roomId": "maze32-command-chamber",
|
||||
"units": "meters",
|
||||
"surfaces": [
|
||||
{"id": "floor", "kind": "floor", "location": [0, 0, -0.15], "dimensionsMeters": [12, 12, 0.3], "rotationDegrees": [0, 0, 0], "material": {"baseColor": [0.07, 0.09, 0.13, 1], "metallic": 0.05, "roughness": 0.72}},
|
||||
{"id": "ceiling", "kind": "ceiling", "location": [0, 0, 5.0], "dimensionsMeters": [12, 12, 0.35], "rotationDegrees": [0, 0, 0], "material": {"baseColor": [0.04, 0.05, 0.08, 1], "metallic": 0.1, "roughness": 0.64}}
|
||||
],
|
||||
"instances": [
|
||||
{"id": "command-table-main", "assetId": "command-table", "location": [0, 0, 0], "rotationDegrees": [0, 0, 0], "scale": [1, 1, 1]},
|
||||
{"id": "station-north", "assetId": "player-station", "location": [0, 3.95, 0], "rotationDegrees": [0, 0, 180], "scale": [1, 1, 1]},
|
||||
{"id": "station-east", "assetId": "player-station", "location": [3.95, 0, 0], "rotationDegrees": [0, 0, 90], "scale": [1, 1, 1]},
|
||||
{"id": "station-south", "assetId": "player-station", "location": [0, -3.95, 0], "rotationDegrees": [0, 0, 0], "scale": [1, 1, 1]},
|
||||
{"id": "station-west", "assetId": "player-station", "location": [-3.95, 0, 0], "rotationDegrees": [0, 0, -90], "scale": [1, 1, 1]}
|
||||
],
|
||||
"productionCamera": {"location": [9.2, -10.2, 6.7], "target": [0, 0, 1.5], "verticalFovDegrees": 54}
|
||||
}
|
||||
Reference in New Issue
Block a user