{
  "box": {
    "file": "box.glb.gz",
    "generator": "fal-ai/hunyuan-3d/v3.1/pro/image-to-3d (generate_type Normal, enable_pbr, face_count 100000) from a gpt-image-2.5-flare product reference (background removed with fal-ai/birefnet/v2); normalized with @gltf-transform (work/box/pipeline/normalize.mjs --flatten --despeckle), then flaps shortened toward their hinges (work/box/pipeline/flaps.mjs: side flaps x0.7, back flap x0.5) and the right flap opened further about its hinge, to 15 deg above horizontal at the front of the hinge and 25 deg at the back (work/box/pipeline/right-flap.mjs)",
    "triangles": 50000,
    "bbox": {
      "min": [
        -0.451,
        0,
        -0.25
      ],
      "max": [
        0.485,
        0.667,
        0.251
      ]
    },
    "up": "y",
    "front": "+z",
    "units": "normalize.mjs units (flap tip to flap tip was 1.0 before flaps.mjs shortened the flaps); bottom at y = 0; centred on x/z",
    "body": {
      "x": [
        -0.337,
        0.336
      ],
      "width": 0.673,
      "rimY": 0.54,
      "frontZ": 0.251,
      "backZ": -0.25,
      "depth": 0.501,
      "innerFloorY": 0.013,
      "backFlapTopY": 0.667
    },
    "loader": {
      "extensionsRequired": [
        "EXT_meshopt_compression",
        "EXT_texture_webp",
        "KHR_mesh_quantization"
      ],
      "meshoptDecoder": "import { MeshoptDecoder } from \"three/addons/libs/meshopt_decoder.module.js\"; new GLTFLoader().setMeshoptDecoder(MeshoptDecoder)",
      "draco": false
    },
    "material": "one MeshStandardMaterial named kraft_box: 2048 WebP baked albedo atlas (flat kraft walls, corrugated flute pattern on flap edges, occlusion baked into the interior), 2048 WebP normal map, roughness in the G channel of metallicRoughness (about 0.84); metallicFactor 0; KHR_materials_specular removed; double-sided because flaps are thin shells",
    "notes": "Empty regular slotted carton matching the concept: chunky body (front face width/height about 1.25; concept about 1.3), left flap open outward about 45 deg above horizontal, right flap opened further (15 deg at the front of its hinge, 25 deg at the back, a slight twist) so its inner face shows from the stage camera, back flap upright (all three shortened toward their hinges to the concept proportions by flaps.mjs), no front flap so the front rim is a clean straight edge. Outer walls were snapped to true planes (dents up to 0.009 removed) and small blemishes were removed from the wall texels, so the front face at z = 0.251 takes a decal cleanly. The texture is a UV atlas: tileable kraft textures cannot replace it without new UVs; tint via material.color if needed.",
    "packing": "tools/models/pack.mjs from work/box/pipeline/out/box.glb: simplified to 50000 triangles (meshopt simplifyWithAttributes, error 0.001, normal and texcoord weights 1, 1, 1, 2, 2), meshopt level high, gzipped (466672 B GLB, 391419 B shipped; src/assets.js inflates it with DecompressionStream)"
  },
  "paper": {
    "file": "paper.glb.gz",
    "generator": "meshy/v7.1/image-to-3d (standard, triangle, target_polycount 100000, remesh, enable_pbr, geometry_resolution 2k, symmetry off) from a gpt-image-2.5-flare reference of crumpled brown kraft packing paper lifted out of a box in one piece (background removed with fal-ai/birefnet/v2); lightly smoothed, billowed, fitted into box.glb with wads pushed up over the front rim, simplified (meshopt, error 0.001) and given crease-split normals (32 deg) by work/paper/gen/fit.mjs; compressed with @gltf-transform/cli 4.5 optimize --compress meshopt --texture-compress webp --texture-size 1024 --simplify false",
    "triangles": 48197,
    "bbox": {
      "min": [
        -0.343,
        0.331,
        -0.224
      ],
      "max": [
        0.328,
        0.64,
        0.231
      ]
    },
    "up": "y",
    "front": "+z",
    "frame": "box.glb model frame (same origin, units and axes as the box entry). Add the paper scene as a child of the box glTF root (the gltf.scene that src/box.js scales and positions), so it inherits the box normalisation including HEIGHT_SCALE and sits in the opening.",
    "fit": {
      "interior": {
        "x0": -0.327,
        "x1": 0.321,
        "z0": -0.226,
        "z1": 0.233,
        "rim": 0.542,
        "outerFront": 0.251,
        "outerX": 0.344
      },
      "smoothing": "1 Taubin pass (lambda 0.5, mu -0.53) on the raw mesh",
      "billow": "folds above rim -0.03 stretched x1.35; crests stand about 3-6 cm over the rim",
      "spill": "above the rim the paper swells out over the walls by 0.1, onto the left flap and just over the right rim; below the rim it stays inside the walls; it rests on the side flaps (the right one at its opened angle, work/box/pipeline/right-flap.mjs) and stays under the leaning back flap",
      "collars": {
        "note": "the paper's top rises to book 0.045, drive 0.06, card 0.05 over the rim within 0.08 of each prop, for the arrangement in src/prop-layout.js (read by gen/props.mjs); re-run work/paper/refit.sh if that arrangement changes",
        "centres": [
          {
            "name": "book",
            "centre": [
              -0.142,
              -0.05
            ]
          },
          {
            "name": "drive",
            "centre": [
              0.208,
              -0.1
            ]
          },
          {
            "name": "card",
            "centre": [
              0.127,
              0.101
            ]
          }
        ]
      },
      "wads": "3 stiff wads push the paper up over the front rim: at x -0.26, rising 0.045, leaning out 0.014; at x -0.06, rising 0.035, leaning out 0.012; at x 0.21, rising 0.04, leaning out 0.014",
      "normals": "crease-split: faces meeting sharper than 32 deg keep separate vertex normals, so folds stay crisp and facets flat"
    },
    "loader": {
      "extensionsRequired": [
        "EXT_meshopt_compression",
        "EXT_texture_webp",
        "KHR_mesh_quantization"
      ],
      "meshoptDecoder": "same GLTFLoader setup as the box: new GLTFLoader().setMeshoptDecoder(MeshoptDecoder)",
      "draco": false
    },
    "material": "one matte MeshStandardMaterial (Material_0): the Meshy base colour scan at 1024 WebP, baseColorFactor [1, 1, 1] (mean about sRGB 178, 146, 114: lighter and less saturated than the box board); roughnessFactor 0.88, metallicFactor 0, no normal or metallic-roughness textures; double-sided. COLOR_0 holds ambient occlusion baked against the paper and box.glb (64 rays over 0.1, raised to the power 1.6 to deepen the gaps between wads), which GLTFLoader applies through vertexColors.",
    "rendering": "castShadow and receiveShadow on; apply the studio environment like the other subject meshes. src/paper.js adds the paper_crinkle texture (triplanar normal and facet tone) and lowers the specular. The key light uses VSMShadowMap: with the default normalBias the steep crease facets show stepped shadow-acne stripes, which key.shadow.normalBias = 0.01 removes (work/paper/shots/a7, a8).",
    "notes": "The mesh body was chosen over a procedural route (subdivided heightfield with multi-scale fold creases plus swept sheet strips, textured with kraft_outer and baked AO), which read as dough or rock at hero scale because a heightfield cannot fold over itself. Among generated candidates meshy on the rectangular \"block\" reference beat hunyuan-3d v3.1 pro (softer, cloth-like folds), the round \"nest\" references (left the box corners empty) and meshy at 4k geometry (softer creases). Comparison page: work/paper/compare/.",
    "packing": "tools/models/pack.mjs from work/paper/glb/paper.glb: every triangle kept, meshopt level high, gzipped (1007316 B GLB, 753606 B shipped; src/assets.js inflates it with DecompressionStream)"
  }
}
