@origingame/engine 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/dist/web/origin-engine.d.ts +44 -5
  2. package/dist/web/origin-engine.js +279 -19
  3. package/dist/web/origin-engine_bg.wasm +0 -0
  4. package/dist/web/origin-engine_bg.wasm.d.ts +18 -5
  5. package/dist/web-publish/origin-engine-lanes.js +289 -48
  6. package/dist/web-publish/origin-engine-lanes_bg.wasm +0 -0
  7. package/dist/web-publish/origin-engine.d.ts +44 -5
  8. package/dist/web-publish/origin-engine.js +279 -38
  9. package/dist/web-publish/origin-engine_bg.wasm +0 -0
  10. package/dist/web-publish/origin-engine_bg.wasm.d.ts +18 -5
  11. package/dist/web-publish-2d/origin-engine-lanes.js +289 -48
  12. package/dist/web-publish-2d/origin-engine-lanes_bg.wasm +0 -0
  13. package/dist/web-publish-2d/origin-engine.d.ts +44 -5
  14. package/dist/web-publish-2d/origin-engine.js +279 -38
  15. package/dist/web-publish-2d/origin-engine_bg.wasm +0 -0
  16. package/dist/web-publish-2d/origin-engine_bg.wasm.d.ts +18 -5
  17. package/knowledge/README.md +2 -1
  18. package/knowledge/guides/three-to-og.md +393 -0
  19. package/knowledge/knowledge.json +2 -1
  20. package/knowledge/looks/catalogue.json +123 -2
  21. package/knowledge/package.json +3 -2
  22. package/knowledge/scaffold/origin-engine.json +2 -2
  23. package/knowledge/scaffold/template/package.json +1 -1
  24. package/knowledge/scaffold/template/project.ogt +1 -1
  25. package/knowledge/scripts/validate.ts +209 -27
  26. package/knowledge/skills/og-audio/SKILL.md +33 -7
  27. package/knowledge/skills/og-build/SKILL.md +66 -7
  28. package/knowledge/skills/og-core/SKILL.md +24 -8
  29. package/knowledge/skills/og-feel/SKILL.md +11 -3
  30. package/knowledge/skills/og-game/SKILL.md +680 -51
  31. package/knowledge/skills/og-i18n/SKILL.md +7 -7
  32. package/knowledge/skills/og-model/SKILL.md +374 -0
  33. package/knowledge/skills/og-platform-sdk/SKILL.md +7 -1
  34. package/knowledge/skills/og-taste/SKILL.md +7 -0
  35. package/knowledge/skills/og-ui/SKILL.md +39 -8
  36. package/knowledge/wiki/ai.md +20 -20
  37. package/knowledge/wiki/anim.md +537 -63
  38. package/knowledge/wiki/atmosphere.md +13 -13
  39. package/knowledge/wiki/audio.md +160 -20
  40. package/knowledge/wiki/camera.md +13 -13
  41. package/knowledge/wiki/commands.md +11 -11
  42. package/knowledge/wiki/debug.md +13 -13
  43. package/knowledge/wiki/game.md +8 -8
  44. package/knowledge/wiki/i18n.md +7 -7
  45. package/knowledge/wiki/index.md +81 -3
  46. package/knowledge/wiki/input.md +47 -47
  47. package/knowledge/wiki/load.md +83 -57
  48. package/knowledge/wiki/math.md +102 -102
  49. package/knowledge/wiki/nav.md +34 -34
  50. package/knowledge/wiki/net.md +10 -10
  51. package/knowledge/wiki/path.md +14 -14
  52. package/knowledge/wiki/physics.md +111 -100
  53. package/knowledge/wiki/platform.md +44 -44
  54. package/knowledge/wiki/probe.md +86 -86
  55. package/knowledge/wiki/reflect.md +30 -18
  56. package/knowledge/wiki/render.md +1243 -176
  57. package/knowledge/wiki/rng.md +14 -14
  58. package/knowledge/wiki/shapes.md +727 -0
  59. package/knowledge/wiki/stream.md +15 -15
  60. package/knowledge/wiki/system.md +8 -8
  61. package/knowledge/wiki/systems.md +2 -2
  62. package/knowledge/wiki/terrain.md +24 -20
  63. package/knowledge/wiki/tilemap.md +13 -13
  64. package/knowledge/wiki/time.md +4 -4
  65. package/knowledge/wiki/timeline.md +14 -14
  66. package/knowledge/wiki/transform.md +8 -8
  67. package/knowledge/wiki/tween.md +17 -17
  68. package/knowledge/wiki/ui.md +245 -269
  69. package/knowledge/wiki/video.md +5 -5
  70. package/knowledge/wiki/world.md +36 -36
  71. package/package.json +4 -4
  72. package/schema/api.schema.json +2238 -81
  73. package/shell/page.ts +4 -0
  74. package/shell/platform.ts +5 -0
  75. package/shell/player.ts +8 -2
  76. package/shell/transport.ts +6 -0
  77. package/src/generated/abi.ts +40 -1
  78. package/src/geom.ts +260 -0
  79. package/src/index.ts +19 -1
  80. package/src/node/device.ts +5 -2
  81. package/src/node/playtest-run.ts +1 -0
  82. package/src/node/polish.ts +34 -15
  83. package/src/node/run.ts +23 -6
  84. package/src/platform-local.ts +2 -0
  85. package/src/playtest.ts +8 -0
  86. package/src/runtime.ts +88 -6
  87. package/src/ui-kit/settings.ts +9 -0
  88. package/src/web/gl-state.ts +907 -0
  89. package/src/web/shell.ts +1 -1
  90. package/src/web/worker.ts +8 -2
  91. package/types/bindings.json +1222 -453
  92. package/types/index.d.ts +1868 -315
@@ -17,6 +17,7 @@
17
17
  "bytes": "bytes",
18
18
  "kg": "kilograms",
19
19
  "newton": "newtons: the force that gives a kilogram one metre per second squared",
20
+ "newtonSecond": "newton-seconds: an impulse, the momentum that changes a kilogram's velocity by one metre per second",
20
21
  "mps": "metres per second",
21
22
  "mps2": "metres per second squared",
22
23
  "radps": "radians per second",
@@ -1614,7 +1615,7 @@
1614
1615
  },
1615
1616
  {
1616
1617
  "name": "reflect",
1617
- "doc": "Reflection: components and resources are declared once with their field layout and metadata, and registered by name.\nThe inspector, Tune, the probe, scene text and the wiki read this one schema. Each column factory below makes one field.\nA field holds one value: there is no array or list column. A few values are numbered fields (`p0` to `p7`, as og.CameraRail does); a list of things is an entity each, with its own component, looped over by a query (a ring of skid marks, the laps of a race); a long record (a lap's taped inputs) is text in `str` fields of at most 4,096 bytes each, chunked over entities. Keep none of it in module variables: they are not world state, so a snapshot, a replay and a hot reload lose them.\nA handle (an og.load answer, a texture, a sound, a geometry, a material, an entity) is a 64-bit number: keep it in a field of its own kind (`Asset()`, `Texture()`, `Sound()`, `Geometry()`, `Material()`, `Entity()`, the factories of the API's handle types), in a component or a resource. A `u32` field refuses one (handles pass 2^32: `4294967298 is not a u32`); an `f64` holds the number but loses its type.",
1618
+ "doc": "Reflection: components and resources are declared once with their field layout and metadata, and registered by name.\nThe inspector, Tune, the probe, scene text and the wiki read this one schema. Each column factory below makes one field.\nA field holds one value, or a list of at most a fixed number of one number type or handle: `list(u32(), 8)`, `list(Asset(), 4)` (the list factory below). A list is read as a frozen copy (`push` throws) and written whole (`chain.passes = [...chain.passes, h]`); one over its capacity is refused, never truncated. A few vectors are numbered fields (`p0` to `p7`, as og.CameraRail does); a list of things with fields of their own is an entity each, with its own component, looped over by a query (a ring of skid marks, the laps of a race); a long record (a lap's taped inputs) is text in `str` fields of at most 4,096 bytes each, chunked over entities. Keep none of it in module variables: they are not world state, so a snapshot, a replay and a hot reload lose them.\nA handle (an og.load answer, a texture, a sound, a geometry, a material, an entity) is a 64-bit number: keep it in a field of its own kind (`Asset()`, `Texture()`, `Sound()`, `Font()`, `Style()`, `Geometry()`, `Material()`, `Entity()`: every handle type of the API has its factory, named as the type), never cast to another kind, in a component or a resource. A `u32` field refuses one (handles pass 2^32: `4294967298 is not a u32`); an `f64` holds the number but loses its type.",
1618
1619
  "place": "top",
1619
1620
  "publish": true,
1620
1621
  "types": [
@@ -1629,7 +1630,8 @@
1629
1630
  "Quaternion",
1630
1631
  "Matrix4",
1631
1632
  "Color",
1632
- "str"
1633
+ "str",
1634
+ "list"
1633
1635
  ],
1634
1636
  "consts": [],
1635
1637
  "functions": [
@@ -2753,6 +2755,9 @@
2753
2755
  "SpotLight",
2754
2756
  "ToneMapping",
2755
2757
  "ToneMapping",
2758
+ "GraphicsQuality",
2759
+ "QualityPreset",
2760
+ "QualityPreset",
2756
2761
  "RenderSettings",
2757
2762
  "Lut",
2758
2763
  "EnvironmentMap",
@@ -2768,12 +2773,15 @@
2768
2773
  "DofQuality",
2769
2774
  "SpriteBlend",
2770
2775
  "SpriteBlend",
2776
+ "SpriteFacing",
2777
+ "SpriteFacing",
2771
2778
  "StripBlend",
2772
2779
  "StripBlend",
2773
2780
  "PostEffects",
2774
2781
  "Palette",
2775
2782
  "Look",
2776
2783
  "Palette",
2784
+ "Look",
2777
2785
  "DitherPattern",
2778
2786
  "DitherPattern",
2779
2787
  "StyliseSpace",
@@ -2805,6 +2813,7 @@
2805
2813
  "ReflectionProbeMode",
2806
2814
  "ReflectionProbe",
2807
2815
  "Shader",
2816
+ "Shader",
2808
2817
  "BufferGeometryDesc",
2809
2818
  "GeometryInfo",
2810
2819
  "CylinderGeometryDesc",
@@ -2814,6 +2823,19 @@
2814
2823
  "RingGeometryDesc",
2815
2824
  "CircleGeometryDesc",
2816
2825
  "TubeGeometryDesc",
2826
+ "ShapePoints",
2827
+ "ShapeGeometryDesc",
2828
+ "ExtrudeGeometryDesc",
2829
+ "LatheGeometryDesc",
2830
+ "PolyhedronGeometryDesc",
2831
+ "SolidGeometryDesc",
2832
+ "SweepKey",
2833
+ "SweepGeometryDesc",
2834
+ "MirrorAxis",
2835
+ "GeometryPart",
2836
+ "CsgOp",
2837
+ "CsgGeometryDesc",
2838
+ "MergeGeometriesDesc",
2817
2839
  "LineMode",
2818
2840
  "LineMode",
2819
2841
  "LinesDesc",
@@ -3170,6 +3192,27 @@
3170
3192
  "returnsDoc": "Refused for a look not loaded in this world, one in a streaming group that is not resident (the reason names it), or a resource the system did not declare.",
3171
3193
  "publish": true
3172
3194
  },
3195
+ {
3196
+ "name": "applyQuality",
3197
+ "doc": "Apply a graphics preset: write its row of RenderSettings' quality fields (the rest of RenderSettings keeps its values) and record it in RenderSettings.quality. The renderer reads those fields and nothing else, so what the probe and dump_world show of RenderSettings is what the next frame draws. The settings screen calls it when the player picks a preset (SettingsDesc.world); a game applies a saved preset itself at start. Inside a system, the system must declare RenderSettings among its writes. The rows:\n| Field | low | medium | high |\n|---|---|---|---|\n| rectShadows | false | true | true |\n| shadowedLights | 2 | 8 | 8 |\n| shadowMapScale | 0.5 | 1 | 2 |\n| volumetricFog | false | true | true |\n| softParticles | false | true | true |\n| screenSpaceReflections | false | true | true |\n| ambientOcclusion | false | true | true |\n| msaaSamples | 1 | 4 | 4 |\nMedium is every field's default, the frame drawn before any preset.",
3198
+ "params": [
3199
+ {
3200
+ "name": "world",
3201
+ "type": "World",
3202
+ "optional": false,
3203
+ "doc": "The world whose RenderSettings change."
3204
+ },
3205
+ {
3206
+ "name": "preset",
3207
+ "type": "GraphicsQuality",
3208
+ "optional": false,
3209
+ "doc": "The preset."
3210
+ }
3211
+ ],
3212
+ "returns": "Result<Void>",
3213
+ "returnsDoc": "Refused for a world that is not alive, or inside a system that did not declare RenderSettings among its writes.",
3214
+ "publish": true
3215
+ },
3173
3216
  {
3174
3217
  "name": "look",
3175
3218
  "doc": "The look last applied (by og.render.applyLook or GameDesc.look), as og.load.lookInfo describes it; null before any.",
@@ -3429,6 +3472,233 @@
3429
3472
  "returnsDoc": "Refused for fewer than two points or a point that is not finite (without a path), or a path that is not alive.",
3430
3473
  "publish": true
3431
3474
  },
3475
+ {
3476
+ "name": "shapeGeometry",
3477
+ "doc": "Make a flat shape for og.Mesh (one-sided, as circleGeometry): an og.Shape with holes, or a points array. Physics can use it as a triangle-mesh Collider.mesh.",
3478
+ "params": [
3479
+ {
3480
+ "name": "world",
3481
+ "type": "World",
3482
+ "optional": false,
3483
+ "doc": "The world whose renderer owns the geometry."
3484
+ },
3485
+ {
3486
+ "name": "desc",
3487
+ "type": "ShapeGeometryDesc",
3488
+ "optional": true,
3489
+ "doc": "The shapes and their tessellation."
3490
+ }
3491
+ ],
3492
+ "returns": "Result<Geometry>",
3493
+ "returnsDoc": "Refused for a shape whose points are not finite or that triangulates to nothing.",
3494
+ "publish": true
3495
+ },
3496
+ {
3497
+ "name": "extrudeGeometry",
3498
+ "doc": "Make an extruded solid for og.Mesh: a three.js extrusion is a paste (`og.render.extrudeGeometry(world, { shapes: shape, depth: 0.2, bevelEnabled: true })`). Gears, signs, keys, logos, cookie cutters; along a path, a rail or a moulding.",
3499
+ "params": [
3500
+ {
3501
+ "name": "world",
3502
+ "type": "World",
3503
+ "optional": false,
3504
+ "doc": "The world whose renderer owns the geometry."
3505
+ },
3506
+ {
3507
+ "name": "desc",
3508
+ "type": "ExtrudeGeometryDesc",
3509
+ "optional": true,
3510
+ "doc": "The shapes, depth, bevel and path."
3511
+ }
3512
+ ],
3513
+ "returns": "Result<Geometry>",
3514
+ "returnsDoc": "Refused for a shape whose points are not finite or that triangulates to nothing, or an extrudePath that is not alive.",
3515
+ "publish": true
3516
+ },
3517
+ {
3518
+ "name": "latheGeometry",
3519
+ "doc": "Make a solid of revolution for og.Mesh: a profile turned round +Y.",
3520
+ "params": [
3521
+ {
3522
+ "name": "world",
3523
+ "type": "World",
3524
+ "optional": false,
3525
+ "doc": "The world whose renderer owns the geometry."
3526
+ },
3527
+ {
3528
+ "name": "desc",
3529
+ "type": "LatheGeometryDesc",
3530
+ "optional": true,
3531
+ "doc": "The profile and the turn."
3532
+ }
3533
+ ],
3534
+ "returns": "Result<Geometry>",
3535
+ "returnsDoc": "Refused for fewer than two points or one that is not finite.",
3536
+ "publish": true
3537
+ },
3538
+ {
3539
+ "name": "polyhedronGeometry",
3540
+ "doc": "Make a polyhedron from vertices and faces for og.Mesh, subdivided onto a sphere.",
3541
+ "params": [
3542
+ {
3543
+ "name": "world",
3544
+ "type": "World",
3545
+ "optional": false,
3546
+ "doc": "The world whose renderer owns the geometry."
3547
+ },
3548
+ {
3549
+ "name": "desc",
3550
+ "type": "PolyhedronGeometryDesc",
3551
+ "optional": true,
3552
+ "doc": "The vertices, faces, radius and detail."
3553
+ }
3554
+ ],
3555
+ "returns": "Result<Geometry>",
3556
+ "returnsDoc": "Refused for no faces, an index past the vertices, or a count that is not a multiple of three.",
3557
+ "publish": true
3558
+ },
3559
+ {
3560
+ "name": "icosahedronGeometry",
3561
+ "doc": "Make an icosahedron (twenty faces) for og.Mesh; with detail, a geodesic sphere.",
3562
+ "params": [
3563
+ {
3564
+ "name": "world",
3565
+ "type": "World",
3566
+ "optional": false,
3567
+ "doc": "The world whose renderer owns the geometry."
3568
+ },
3569
+ {
3570
+ "name": "desc",
3571
+ "type": "SolidGeometryDesc",
3572
+ "optional": true,
3573
+ "doc": "Its radius and detail."
3574
+ }
3575
+ ],
3576
+ "returns": "Result<Geometry>",
3577
+ "publish": true
3578
+ },
3579
+ {
3580
+ "name": "dodecahedronGeometry",
3581
+ "doc": "Make a dodecahedron (twelve pentagons, thirty-six triangles) for og.Mesh.",
3582
+ "params": [
3583
+ {
3584
+ "name": "world",
3585
+ "type": "World",
3586
+ "optional": false,
3587
+ "doc": "The world whose renderer owns the geometry."
3588
+ },
3589
+ {
3590
+ "name": "desc",
3591
+ "type": "SolidGeometryDesc",
3592
+ "optional": true,
3593
+ "doc": "Its radius and detail."
3594
+ }
3595
+ ],
3596
+ "returns": "Result<Geometry>",
3597
+ "publish": true
3598
+ },
3599
+ {
3600
+ "name": "octahedronGeometry",
3601
+ "doc": "Make an octahedron (eight faces) for og.Mesh.",
3602
+ "params": [
3603
+ {
3604
+ "name": "world",
3605
+ "type": "World",
3606
+ "optional": false,
3607
+ "doc": "The world whose renderer owns the geometry."
3608
+ },
3609
+ {
3610
+ "name": "desc",
3611
+ "type": "SolidGeometryDesc",
3612
+ "optional": true,
3613
+ "doc": "Its radius and detail."
3614
+ }
3615
+ ],
3616
+ "returns": "Result<Geometry>",
3617
+ "publish": true
3618
+ },
3619
+ {
3620
+ "name": "tetrahedronGeometry",
3621
+ "doc": "Make a tetrahedron (four faces) for og.Mesh.",
3622
+ "params": [
3623
+ {
3624
+ "name": "world",
3625
+ "type": "World",
3626
+ "optional": false,
3627
+ "doc": "The world whose renderer owns the geometry."
3628
+ },
3629
+ {
3630
+ "name": "desc",
3631
+ "type": "SolidGeometryDesc",
3632
+ "optional": true,
3633
+ "doc": "Its radius and detail."
3634
+ }
3635
+ ],
3636
+ "returns": "Result<Geometry>",
3637
+ "publish": true
3638
+ },
3639
+ {
3640
+ "name": "sweepGeometry",
3641
+ "doc": "Make a section swept along a path for og.Mesh, tapered and twisted by keys.",
3642
+ "params": [
3643
+ {
3644
+ "name": "world",
3645
+ "type": "World",
3646
+ "optional": false,
3647
+ "doc": "The world whose renderer owns the geometry."
3648
+ },
3649
+ {
3650
+ "name": "desc",
3651
+ "type": "SweepGeometryDesc",
3652
+ "optional": true,
3653
+ "doc": "The section, path, keys and caps."
3654
+ }
3655
+ ],
3656
+ "returns": "Result<Geometry>",
3657
+ "returnsDoc": "Refused for a path that is not alive, a section of fewer than three points or of no area, or a value that is not finite.",
3658
+ "publish": true
3659
+ },
3660
+ {
3661
+ "name": "csgGeometry",
3662
+ "doc": "Make the union, difference or intersection of two placed geometries for og.Mesh. Chain calls for more operands (a cube minus six spheres is six calls); each result is a geometry like any other.",
3663
+ "params": [
3664
+ {
3665
+ "name": "world",
3666
+ "type": "World",
3667
+ "optional": false,
3668
+ "doc": "The world whose renderer owns the geometry."
3669
+ },
3670
+ {
3671
+ "name": "desc",
3672
+ "type": "CsgGeometryDesc",
3673
+ "optional": true,
3674
+ "doc": "The operation and its operands."
3675
+ }
3676
+ ],
3677
+ "returns": "Result<Geometry>",
3678
+ "returnsDoc": "Refused for a dead geometry, a placement with a zero scale, or a position that is not finite.",
3679
+ "publish": true
3680
+ },
3681
+ {
3682
+ "name": "mergeGeometries",
3683
+ "doc": "Make one geometry of several placed ones for og.Mesh: one draw instead of many, mirrored halves, arrays.",
3684
+ "params": [
3685
+ {
3686
+ "name": "world",
3687
+ "type": "World",
3688
+ "optional": false,
3689
+ "doc": "The world whose renderer owns the geometry."
3690
+ },
3691
+ {
3692
+ "name": "desc",
3693
+ "type": "MergeGeometriesDesc",
3694
+ "optional": true,
3695
+ "doc": "The parts and their placements."
3696
+ }
3697
+ ],
3698
+ "returns": "Result<Geometry>",
3699
+ "returnsDoc": "Refused for no parts, a dead geometry, or a placement with a zero scale.",
3700
+ "publish": true
3701
+ },
3432
3702
  {
3433
3703
  "name": "lines",
3434
3704
  "doc": "Draw lines this frame. Systems and commands may call it; the list is cleared at every tick, so submit them every tick they should show (og.Lines keeps a static set).",
@@ -3554,7 +3824,7 @@
3554
3824
  },
3555
3825
  {
3556
3826
  "name": "postErrors",
3557
- "doc": "The custom post passes PostEffects.custom1 to custom8 name that failed to compile on this device, each skipped (the frame draws as without it). The cook validates every pass for each back end, so this is a device's own refusal.",
3827
+ "doc": "The custom post passes PostEffects.custom names that failed to compile on this device, each skipped (the frame draws as without it). The cook validates every pass for each back end, so this is a device's own refusal.",
3558
3828
  "params": [
3559
3829
  {
3560
3830
  "name": "world",
@@ -3584,6 +3854,518 @@
3584
3854
  "interfaces": [],
3585
3855
  "callbacks": []
3586
3856
  },
3857
+ {
3858
+ "name": "shapes",
3859
+ "doc": "Two-dimensional outlines in three.js's words: og.Shape and og.Path2 are three.js's Shape and Path, built by the same calls (moveTo, lineTo, quadraticCurveTo, bezierCurveTo, splineThru, arc, absarc, ellipse, absellipse, closePath) and flattened to points by getPoints, so a three.js outline is a paste. og.render.shapeGeometry, extrudeGeometry and sweepGeometry take them; only their points cross to the engine.\nThey are TypeScript values, not world state: make them in start or a command and let the geometry made from them be what the world keeps. Points come from basic arithmetic (no Math.sin), so they are the same on every host.\nA prop of these parts belongs in an asset: an `.ogt` of `type model` names each part's geometry by its og.render call (`extrudeGeometry { shape { absarc 0 0 1 0 6.283185307179586 false } depth 0.2 }`), its shapes as og.Shape's calls one a line with `hole { }` blocks, places parts as mergeGeometries does (`position`, `quaternion`, `scale`, `mirror`, or several `at { }` blocks: one mesh drawn instanced), gives each a `material` and hangs it from a named `pivot` (a node). og cook builds it into the model blob a glTF cooks to (levels of detail, raycast BVH, nodes), refuses it past project.ogt's triangle and vertex budgets, and og dev re-cooks it on an edit; og.load.gltf and a scene's `model` line take it as any model. The og-game skill §18 has a whole example.",
3860
+ "place": "root",
3861
+ "publish": true,
3862
+ "types": [],
3863
+ "consts": [],
3864
+ "values": [
3865
+ {
3866
+ "name": "Path2",
3867
+ "type": "Path2Class",
3868
+ "doc": "three.js's Path (named Path2 beside og.path's 3D paths): `new og.Path2().moveTo(0, 0).lineTo(1, 0)`."
3869
+ },
3870
+ {
3871
+ "name": "Shape",
3872
+ "type": "ShapeClass",
3873
+ "doc": "three.js's Shape: `const s = new og.Shape(); s.moveTo(0, 0).lineTo(1, 0).lineTo(0, 1); s.holes.push(hole)`."
3874
+ }
3875
+ ],
3876
+ "functions": [],
3877
+ "interfaces": [
3878
+ {
3879
+ "name": "PathList",
3880
+ "doc": "A shape's holes, as three.js Shape.holes: an array; push a path to add a hole.",
3881
+ "members": [
3882
+ {
3883
+ "name": "length",
3884
+ "prop": "u32",
3885
+ "doc": "How many holes.",
3886
+ "publish": true
3887
+ },
3888
+ {
3889
+ "name": "push",
3890
+ "doc": "Add a hole (Array.push).",
3891
+ "params": [
3892
+ {
3893
+ "name": "path",
3894
+ "type": "Path2",
3895
+ "optional": false,
3896
+ "doc": "The hole's outline, either winding."
3897
+ }
3898
+ ],
3899
+ "returns": "u32",
3900
+ "publish": true,
3901
+ "host": false
3902
+ }
3903
+ ]
3904
+ },
3905
+ {
3906
+ "name": "Path2",
3907
+ "doc": "An outline of curves, as three.js Path: each call adds a curve from the current point (moveTo only moves it). Every method but getPoints returns the path, so calls chain.",
3908
+ "members": [
3909
+ {
3910
+ "name": "moveTo",
3911
+ "doc": "Move the current point without drawing, as three.js Path.moveTo.",
3912
+ "params": [
3913
+ {
3914
+ "name": "x",
3915
+ "type": "f32",
3916
+ "optional": false,
3917
+ "doc": "x."
3918
+ },
3919
+ {
3920
+ "name": "y",
3921
+ "type": "f32",
3922
+ "optional": false,
3923
+ "doc": "y."
3924
+ }
3925
+ ],
3926
+ "returns": "This",
3927
+ "publish": true,
3928
+ "host": false
3929
+ },
3930
+ {
3931
+ "name": "lineTo",
3932
+ "doc": "A straight line to (x, y), as three.js Path.lineTo.",
3933
+ "params": [
3934
+ {
3935
+ "name": "x",
3936
+ "type": "f32",
3937
+ "optional": false,
3938
+ "doc": "x."
3939
+ },
3940
+ {
3941
+ "name": "y",
3942
+ "type": "f32",
3943
+ "optional": false,
3944
+ "doc": "y."
3945
+ }
3946
+ ],
3947
+ "returns": "This",
3948
+ "publish": true,
3949
+ "host": false
3950
+ },
3951
+ {
3952
+ "name": "quadraticCurveTo",
3953
+ "doc": "A quadratic Bézier curve through one control point to (x, y), as three.js Path.quadraticCurveTo.",
3954
+ "params": [
3955
+ {
3956
+ "name": "cpX",
3957
+ "type": "f32",
3958
+ "optional": false,
3959
+ "doc": "The control point's x."
3960
+ },
3961
+ {
3962
+ "name": "cpY",
3963
+ "type": "f32",
3964
+ "optional": false,
3965
+ "doc": "The control point's y."
3966
+ },
3967
+ {
3968
+ "name": "x",
3969
+ "type": "f32",
3970
+ "optional": false,
3971
+ "doc": "The end's x."
3972
+ },
3973
+ {
3974
+ "name": "y",
3975
+ "type": "f32",
3976
+ "optional": false,
3977
+ "doc": "The end's y."
3978
+ }
3979
+ ],
3980
+ "returns": "This",
3981
+ "publish": true,
3982
+ "host": false
3983
+ },
3984
+ {
3985
+ "name": "bezierCurveTo",
3986
+ "doc": "A cubic Bézier curve through two control points to (x, y), as three.js Path.bezierCurveTo.",
3987
+ "params": [
3988
+ {
3989
+ "name": "cp1X",
3990
+ "type": "f32",
3991
+ "optional": false,
3992
+ "doc": "The first control point's x."
3993
+ },
3994
+ {
3995
+ "name": "cp1Y",
3996
+ "type": "f32",
3997
+ "optional": false,
3998
+ "doc": "The first control point's y."
3999
+ },
4000
+ {
4001
+ "name": "cp2X",
4002
+ "type": "f32",
4003
+ "optional": false,
4004
+ "doc": "The second control point's x."
4005
+ },
4006
+ {
4007
+ "name": "cp2Y",
4008
+ "type": "f32",
4009
+ "optional": false,
4010
+ "doc": "The second control point's y."
4011
+ },
4012
+ {
4013
+ "name": "x",
4014
+ "type": "f32",
4015
+ "optional": false,
4016
+ "doc": "The end's x."
4017
+ },
4018
+ {
4019
+ "name": "y",
4020
+ "type": "f32",
4021
+ "optional": false,
4022
+ "doc": "The end's y."
4023
+ }
4024
+ ],
4025
+ "returns": "This",
4026
+ "publish": true,
4027
+ "host": false
4028
+ },
4029
+ {
4030
+ "name": "splineThru",
4031
+ "doc": "A Catmull-Rom spline from the current point through the points, as three.js Path.splineThru (a SplineCurve).",
4032
+ "params": [
4033
+ {
4034
+ "name": "points",
4035
+ "type": "List<Vector2>",
4036
+ "optional": false,
4037
+ "doc": "The points it passes through, in order."
4038
+ }
4039
+ ],
4040
+ "returns": "This",
4041
+ "publish": true,
4042
+ "host": false
4043
+ },
4044
+ {
4045
+ "name": "arc",
4046
+ "doc": "A circular arc centred at (x, y) from the current point, as three.js Path.arc.",
4047
+ "params": [
4048
+ {
4049
+ "name": "x",
4050
+ "type": "f32",
4051
+ "optional": false,
4052
+ "doc": "The centre's x, from the current point."
4053
+ },
4054
+ {
4055
+ "name": "y",
4056
+ "type": "f32",
4057
+ "optional": false,
4058
+ "doc": "The centre's y, from the current point."
4059
+ },
4060
+ {
4061
+ "name": "radius",
4062
+ "type": "f32",
4063
+ "optional": false,
4064
+ "doc": "The radius."
4065
+ },
4066
+ {
4067
+ "name": "startAngle",
4068
+ "type": "f32",
4069
+ "optional": false,
4070
+ "doc": "Where it starts, from +x towards +y."
4071
+ },
4072
+ {
4073
+ "name": "endAngle",
4074
+ "type": "f32",
4075
+ "optional": false,
4076
+ "doc": "Where it ends."
4077
+ },
4078
+ {
4079
+ "name": "clockwise",
4080
+ "type": "bool",
4081
+ "optional": true,
4082
+ "default": false,
4083
+ "doc": "Go clockwise."
4084
+ }
4085
+ ],
4086
+ "returns": "This",
4087
+ "publish": true,
4088
+ "host": false
4089
+ },
4090
+ {
4091
+ "name": "absarc",
4092
+ "doc": "A circular arc centred at (x, y), as three.js Path.absarc: a line joins it to the current point when they differ.",
4093
+ "params": [
4094
+ {
4095
+ "name": "x",
4096
+ "type": "f32",
4097
+ "optional": false,
4098
+ "doc": "The centre's x."
4099
+ },
4100
+ {
4101
+ "name": "y",
4102
+ "type": "f32",
4103
+ "optional": false,
4104
+ "doc": "The centre's y."
4105
+ },
4106
+ {
4107
+ "name": "radius",
4108
+ "type": "f32",
4109
+ "optional": false,
4110
+ "doc": "The radius."
4111
+ },
4112
+ {
4113
+ "name": "startAngle",
4114
+ "type": "f32",
4115
+ "optional": false,
4116
+ "doc": "Where it starts, from +x towards +y."
4117
+ },
4118
+ {
4119
+ "name": "endAngle",
4120
+ "type": "f32",
4121
+ "optional": false,
4122
+ "doc": "Where it ends."
4123
+ },
4124
+ {
4125
+ "name": "clockwise",
4126
+ "type": "bool",
4127
+ "optional": true,
4128
+ "default": false,
4129
+ "doc": "Go clockwise."
4130
+ }
4131
+ ],
4132
+ "returns": "This",
4133
+ "publish": true,
4134
+ "host": false
4135
+ },
4136
+ {
4137
+ "name": "ellipse",
4138
+ "doc": "An elliptic arc centred at (x, y) from the current point, as three.js Path.ellipse.",
4139
+ "params": [
4140
+ {
4141
+ "name": "x",
4142
+ "type": "f32",
4143
+ "optional": false,
4144
+ "doc": "The centre's x, from the current point."
4145
+ },
4146
+ {
4147
+ "name": "y",
4148
+ "type": "f32",
4149
+ "optional": false,
4150
+ "doc": "The centre's y, from the current point."
4151
+ },
4152
+ {
4153
+ "name": "xRadius",
4154
+ "type": "f32",
4155
+ "optional": false,
4156
+ "doc": "The radius along x."
4157
+ },
4158
+ {
4159
+ "name": "yRadius",
4160
+ "type": "f32",
4161
+ "optional": false,
4162
+ "doc": "The radius along y."
4163
+ },
4164
+ {
4165
+ "name": "startAngle",
4166
+ "type": "f32",
4167
+ "optional": false,
4168
+ "doc": "Where it starts."
4169
+ },
4170
+ {
4171
+ "name": "endAngle",
4172
+ "type": "f32",
4173
+ "optional": false,
4174
+ "doc": "Where it ends."
4175
+ },
4176
+ {
4177
+ "name": "clockwise",
4178
+ "type": "bool",
4179
+ "optional": true,
4180
+ "default": false,
4181
+ "doc": "Go clockwise."
4182
+ },
4183
+ {
4184
+ "name": "rotation",
4185
+ "type": "f32",
4186
+ "optional": true,
4187
+ "default": 0,
4188
+ "doc": "The ellipse's turn round its centre."
4189
+ }
4190
+ ],
4191
+ "returns": "This",
4192
+ "publish": true,
4193
+ "host": false
4194
+ },
4195
+ {
4196
+ "name": "absellipse",
4197
+ "doc": "An elliptic arc centred at (x, y), as three.js Path.absellipse.",
4198
+ "params": [
4199
+ {
4200
+ "name": "x",
4201
+ "type": "f32",
4202
+ "optional": false,
4203
+ "doc": "The centre's x."
4204
+ },
4205
+ {
4206
+ "name": "y",
4207
+ "type": "f32",
4208
+ "optional": false,
4209
+ "doc": "The centre's y."
4210
+ },
4211
+ {
4212
+ "name": "xRadius",
4213
+ "type": "f32",
4214
+ "optional": false,
4215
+ "doc": "The radius along x."
4216
+ },
4217
+ {
4218
+ "name": "yRadius",
4219
+ "type": "f32",
4220
+ "optional": false,
4221
+ "doc": "The radius along y."
4222
+ },
4223
+ {
4224
+ "name": "startAngle",
4225
+ "type": "f32",
4226
+ "optional": false,
4227
+ "doc": "Where it starts."
4228
+ },
4229
+ {
4230
+ "name": "endAngle",
4231
+ "type": "f32",
4232
+ "optional": false,
4233
+ "doc": "Where it ends."
4234
+ },
4235
+ {
4236
+ "name": "clockwise",
4237
+ "type": "bool",
4238
+ "optional": true,
4239
+ "default": false,
4240
+ "doc": "Go clockwise."
4241
+ },
4242
+ {
4243
+ "name": "rotation",
4244
+ "type": "f32",
4245
+ "optional": true,
4246
+ "default": 0,
4247
+ "doc": "The ellipse's turn round its centre."
4248
+ }
4249
+ ],
4250
+ "returns": "This",
4251
+ "publish": true,
4252
+ "host": false
4253
+ },
4254
+ {
4255
+ "name": "setFromPoints",
4256
+ "doc": "Start at the first point and draw lines through the rest, as three.js Path.setFromPoints.",
4257
+ "params": [
4258
+ {
4259
+ "name": "points",
4260
+ "type": "List<Vector2>",
4261
+ "optional": false,
4262
+ "doc": "The points."
4263
+ }
4264
+ ],
4265
+ "returns": "This",
4266
+ "publish": true,
4267
+ "host": false
4268
+ },
4269
+ {
4270
+ "name": "closePath",
4271
+ "doc": "A line back to the first point when the outline does not end there, as three.js CurvePath.closePath.",
4272
+ "params": [],
4273
+ "returns": "This",
4274
+ "publish": true,
4275
+ "host": false
4276
+ },
4277
+ {
4278
+ "name": "getPoints",
4279
+ "doc": "The outline as points, as three.js CurvePath.getPoints: each curve divided into `divisions` (an ellipse twice as many, a spline as many per point, a line one), repeated points dropped.",
4280
+ "params": [
4281
+ {
4282
+ "name": "divisions",
4283
+ "type": "u32",
4284
+ "optional": true,
4285
+ "default": 12,
4286
+ "doc": "Divisions per curve."
4287
+ }
4288
+ ],
4289
+ "returns": "List<Vector2>",
4290
+ "publish": true,
4291
+ "host": false
4292
+ }
4293
+ ]
4294
+ },
4295
+ {
4296
+ "name": "Shape",
4297
+ "doc": "An outline with holes, as three.js Shape.",
4298
+ "extends": "Path2",
4299
+ "members": [
4300
+ {
4301
+ "name": "holes",
4302
+ "prop": "PathList",
4303
+ "doc": "The holes: `shape.holes.push(hole)`.",
4304
+ "publish": true
4305
+ },
4306
+ {
4307
+ "name": "extractPoints",
4308
+ "doc": "The outline's and the holes' points, as three.js Shape.extractPoints.",
4309
+ "params": [
4310
+ {
4311
+ "name": "divisions",
4312
+ "type": "u32",
4313
+ "optional": true,
4314
+ "default": 12,
4315
+ "doc": "Divisions per curve."
4316
+ }
4317
+ ],
4318
+ "returns": "ShapePoints",
4319
+ "publish": true,
4320
+ "host": false
4321
+ }
4322
+ ]
4323
+ },
4324
+ {
4325
+ "name": "Path2Class",
4326
+ "doc": "og.Path2: `new og.Path2(points?)`.",
4327
+ "members": [
4328
+ {
4329
+ "name": "new",
4330
+ "doc": "A path, through the points when given (setFromPoints).",
4331
+ "params": [
4332
+ {
4333
+ "name": "points",
4334
+ "type": "Option<List<Vector2>>",
4335
+ "optional": true,
4336
+ "doc": "Points to start from."
4337
+ }
4338
+ ],
4339
+ "returns": "Path2",
4340
+ "publish": true,
4341
+ "host": false
4342
+ }
4343
+ ]
4344
+ },
4345
+ {
4346
+ "name": "ShapeClass",
4347
+ "doc": "og.Shape: `new og.Shape(points?)`.",
4348
+ "members": [
4349
+ {
4350
+ "name": "new",
4351
+ "doc": "A shape, through the points when given (setFromPoints).",
4352
+ "params": [
4353
+ {
4354
+ "name": "points",
4355
+ "type": "Option<List<Vector2>>",
4356
+ "optional": true,
4357
+ "doc": "Points to start from."
4358
+ }
4359
+ ],
4360
+ "returns": "Shape",
4361
+ "publish": true,
4362
+ "host": false
4363
+ }
4364
+ ]
4365
+ }
4366
+ ],
4367
+ "callbacks": []
4368
+ },
3587
4369
  {
3588
4370
  "name": "load",
3589
4371
  "doc": "Asset loading from cooked output: `og cook` turns assets/ into cooked/ (.ogb blobs and the manifest cooked/.cook.ogt), and the runtime reads only cooked/.\nPaths are asset ids: the path under assets/ (`models/ship.glb`, `scenes/main.ogt`); an `assets/` prefix is accepted. Every blob is checked against the manifest's type, schema version and cook stamp.\nThe game's bundle (`cooked`) is opened before the first tick and GameDesc.scene is spawned then. Loads from an open bundle complete at once on both hosts (the web host fetches the bundle's files before the engine starts); only og.load.bundle of another bundle can be pending, and it completes at a tick boundary.",
@@ -3598,6 +4380,8 @@
3598
4380
  "TextureArray",
3599
4381
  "Asset",
3600
4382
  "Sound",
4383
+ "Font",
4384
+ "Style",
3601
4385
  "LoadStatus",
3602
4386
  "GltfDesc",
3603
4387
  "GltfInfo",
@@ -3670,7 +4454,7 @@
3670
4454
  },
3671
4455
  {
3672
4456
  "name": "gltf",
3673
- "doc": "Load a cooked glTF (.gltf or .glb), as three.js GLTFLoader.load: its meshes, materials and textures join the renderer once.\nA MagicaVoxel `.vox` cooks to a glTF and loads here too: greedy-meshed, flat-shaded, its palette as vertex colours, z-up turned y-up, each model about its centre (MagicaVoxel's pivot, with a scene graph or without). Its import settings (`models/hut.vox.ogt`, `type import`) size and place it: `model { voxScale 0.1 voxOrigin bottom }` is 0.1 m a voxel (default 1) and the lowest voxel on y = 0 (default `center`, the editor's origin). A `.vox` with frames (MagicaVoxel's animation) cooks to clips that swap the frames' models, played by og.AnimationPlayer as any skinned clip: `voxFps 8` (default 10) and `voxClip walk 0 3` (frames 0 to 3; default one clip `frames` of every frame).",
4457
+ "doc": "Load a cooked glTF (.gltf or .glb), as three.js GLTFLoader.load: its meshes, materials and textures join the renderer once.\nA MagicaVoxel `.vox` cooks to a glTF and loads here too: greedy-meshed, flat-shaded, its palette as vertex colours, z-up turned y-up, each model about its centre (MagicaVoxel's pivot, with a scene graph or without). Its import settings (`models/hut.vox.ogt`, `type import`) size and place it: `model { voxScale 0.1 voxOrigin bottom }` is 0.1 m a voxel (default 1) and the lowest voxel on y = 0 (default `center`, the editor's origin). A `.vox` with frames (MagicaVoxel's animation) cooks to clips that swap the frames' models, played by og.AnimationPlayer as any skinned clip: `voxFps 8` (default 10) and `voxClip walk 0 3` (frames 0 to 3; default one clip `frames` of the file's playback range, else of every frame); a node's `_loop` repeats its keys as the editor plays them.",
3674
4458
  "params": [
3675
4459
  {
3676
4460
  "name": "world",
@@ -3862,7 +4646,7 @@
3862
4646
  },
3863
4647
  {
3864
4648
  "name": "post",
3865
- "doc": "A custom post pass for PostEffects.custom1 to custom8, from a cooked `.ogt` of `type post`: `code` or `source` (a `.wgsl` beside it) defining `fn post(f: PostInput, p: Params) -> vec4<f32>` and `param <name> float|vec2|vec3|vec4|color <default...>` lines (at most eight). The function reads previous(uv), scene_color(uv), depth(uv), normal(uv) and f.uv, f.pixel, f.size, f.time, and returns linear HDR colour (the og-game skill's Custom post passes section has the contract). The cook compiles it for every back end. Loaded once per path.",
4649
+ "doc": "A custom post pass for PostEffects.custom, from a cooked `.ogt` of `type post`: `code` or `source` (a `.wgsl` beside it) defining `fn post(f: PostInput, p: Params) -> vec4<f32>` and `param <name> float|vec2|vec3|vec4|color <default...>` lines (at most eight). The function reads previous(uv), scene_color(uv), depth(uv), normal(uv) and f.uv, f.pixel, f.size, f.time, and returns linear HDR colour (the og-game skill's Custom post passes section has the contract). The cook compiles it for every back end. Loaded once per path.",
3866
4650
  "params": [
3867
4651
  {
3868
4652
  "name": "world",
@@ -5071,15 +5855,26 @@
5071
5855
  "AnimationGraph",
5072
5856
  "Blendshapes",
5073
5857
  "IkChain",
5858
+ "LookAt",
5859
+ "JointLimit",
5860
+ "FootLock",
5861
+ "SpringSpace",
5862
+ "SpringSpace",
5863
+ "SpringBone",
5864
+ "SpringColliderShape",
5865
+ "SpringColliderShape",
5866
+ "SpringCollider",
5074
5867
  "AnimationEvent",
5075
5868
  "ClipInfo",
5076
5869
  "Animation",
5077
5870
  "GraphDesc",
5078
5871
  "Rig",
5872
+ "Rig",
5079
5873
  "HumanBone",
5080
5874
  "Humanoid",
5081
5875
  "HumanoidDesc",
5082
5876
  "RetargetDesc",
5877
+ "MirrorDesc",
5083
5878
  "FlipbookSheet",
5084
5879
  "FlipbookSheet",
5085
5880
  "FlipMode",
@@ -5142,7 +5937,7 @@
5142
5937
  },
5143
5938
  {
5144
5939
  "name": "retarget",
5145
- "doc": "A new clip for `target` from a clip of `source`: each animated humanoid bone turns as the source's does from its rest pose (after a rest correction, so a T-pose drives an A-pose and other bone frames agree), the target keeps its bone lengths, the hips travel the source's distance scaled by the leg ratio (root motion kept, hip height scaled), and with footIk the feet land where the source's do, scaled. Same name, duration and markers. The cook does the same for a model's `animation { library }` import setting; this is for clips chosen at run time.",
5940
+ "doc": "A new clip for `target` from a clip of `source`: each animated humanoid bone turns as the source's does from its rest pose (after a rest correction, so a T-pose drives an A-pose and other bone frames agree; fingers turn from their hand, so a hand pose holds whatever the arm does), the target keeps its bone lengths, the hips travel the source's distance scaled by the leg ratio (root motion kept, hip height scaled), and with footIk the feet land where the source's do, scaled, on the ground each rig's `footHeight` names (else its rest pose's foot heights). The target's twist bones (unmapped nodes named `twist`, or Rigify's `DEF-….001`, on an upper or lower arm or leg) take a share of their segment's twist by how far along it they sit. Same name, duration and markers. The cook does the same for a model's `animation { library }` import setting; this is for clips chosen at run time.",
5146
5941
  "params": [
5147
5942
  {
5148
5943
  "name": "world",
@@ -5161,6 +5956,27 @@
5161
5956
  "returnsDoc": "The new clip (what AnimationPlayer.clip takes). Refused for an unregistered clip, skeleton or rig, a skeleton without recognisable hips (name them in a `type rig` file), or a rig naming a missing node.",
5162
5957
  "publish": true
5163
5958
  },
5959
+ {
5960
+ "name": "mirror",
5961
+ "doc": "A new clip that plays `clip` on the other side: every frame reflected in the skeleton's x = 0 plane, each node taking its counterpart's motion (a left swing becomes the right's), rotations moved in model space so left and right bone frames need not mirror each other, root motion's sideways travel and turns flipped. Assumes a symmetric rest pose; a sided node without a counterpart keeps its rest pose. Same name, duration and markers (markers keep their names). Mirroring twice gives the clip back. A clip known ahead needs no call: a graph's `clip` line with `mirror true` plays it mirrored, and the cook's `animation { mirror \"<clip>\" }` writes the mirrored clip into the model.",
5962
+ "params": [
5963
+ {
5964
+ "name": "world",
5965
+ "type": "World",
5966
+ "optional": false,
5967
+ "doc": "The world whose animation owns the skeleton and the clip."
5968
+ },
5969
+ {
5970
+ "name": "desc",
5971
+ "type": "MirrorDesc",
5972
+ "optional": true,
5973
+ "doc": "The clip and its skeleton."
5974
+ }
5975
+ ],
5976
+ "returns": "Result<ClipInfo>",
5977
+ "returnsDoc": "The new clip (what AnimationPlayer.clip takes). Refused for an unregistered clip or skeleton.",
5978
+ "publish": true
5979
+ },
5164
5980
  {
5165
5981
  "name": "clip",
5166
5982
  "doc": "The index of a flipbook clip by name, for Flipbook.clip: `og.anim.clip(hero, \"walk_down\")`. -1 when the sheet has no such clip.",
@@ -5608,12 +6424,16 @@
5608
6424
  },
5609
6425
  {
5610
6426
  "name": "audio",
5611
- "doc": "Sound: inside the tick, the engine's og.audio system (after the game's systems and physics) turns AudioSource changes into a deterministic, time-stamped command stream; outside the tick the host's mixer renders it (an AudioWorklet on the web, the output device, a capture or a null sink natively).\nThe stream, not the PCM, is world state: snapshots, restore and the world hash cover it, and nothing a sink does reaches the world. Load sounds with og.load.sound; inserting a source plays it, setting `playing` false stops it, and a one-shot sets `playing` back to false when it ends.\nSpatial gain and pan are computed in the tick from the source's world transform relative to the first AudioListener's (three.js convention: the listener looks down -Z, +X is right; with none, the origin looking down -Z). A source, listener or zone on a child entity is placed at its og.Transform composed down og.Parent, as og.MatrixWorld is: a sound on a child is heard where its parent carries it, a listener under a camera rig hears from the rig.",
6427
+ "doc": "Sound: inside the tick, the engine's og.audio system (after the game's systems and physics) turns AudioSource changes into a deterministic, time-stamped command stream; outside the tick the host's mixer renders it (an AudioWorklet on the web, the output device, a capture or a null sink natively).\nThe stream, not the PCM, is world state: snapshots, restore and the world hash cover it, and nothing a sink does reaches the world. Load sounds with og.load.sound; inserting a source plays it, setting `playing` false stops it, and a one-shot sets `playing` back to false when it ends.\nSpatial gain and pan are computed in the tick from the source's world transform relative to the first AudioListener's (three.js convention: the listener looks down -Z, +X is right; with none, the origin looking down -Z). A source, listener or zone on a child entity is placed at its og.Transform composed down og.Parent, as og.MatrixWorld is: a sound on a child is heard where its parent carries it, a listener under a camera rig hears from the rig.\nPropagation, all in the tick and so in the stream: a spatial source or listener that moves shifts the pitch (Doppler, from their tick velocities: AudioSource.dopplerFactor, AudioListener.dopplerFactor, Audio.speedOfSound), a source with a cone is turned down away from its +Z (coneInnerAngle, coneOuterAngle, coneOuterGain, as three.js's setDirectionalCone), and a listener whose panningModel is hrtf places sources for headphones (interaural delay and head shadow) instead of the stereo panner.",
5612
6428
  "place": "namespace",
5613
6429
  "publish": true,
5614
6430
  "types": [
5615
6431
  "AudioBus",
5616
6432
  "AudioBus",
6433
+ "PanningModel",
6434
+ "AudioResampler",
6435
+ "AudioResampler",
6436
+ "PanningModel",
5617
6437
  "AudioSource",
5618
6438
  "AudioListener",
5619
6439
  "Audio",
@@ -5654,6 +6474,12 @@
5654
6474
  "type": "u32",
5655
6475
  "value": 4,
5656
6476
  "doc": "SoundEnded.reason: more sources played than Audio.maxVoices and this one stood lowest (priority, then volume, then age)."
6477
+ },
6478
+ {
6479
+ "name": "SOUND_RETRIGGERED",
6480
+ "type": "u32",
6481
+ "value": 5,
6482
+ "doc": "SoundEnded.reason: AudioSource.retrigger started the sound again from the top; a new voice plays it."
5657
6483
  }
5658
6484
  ],
5659
6485
  "functions": [
@@ -6969,7 +7795,6 @@
6969
7795
  "WipeSide",
6970
7796
  "ScreenTransitionDesc",
6971
7797
  "ScreenTransitionResult",
6972
- "GraphicsQuality",
6973
7798
  "MenuItem",
6974
7799
  "MenuResult",
6975
7800
  "MainMenuDesc",
@@ -9758,7 +10583,7 @@
9758
10583
  },
9759
10584
  {
9760
10585
  "name": "terrain",
9761
- "doc": "Terrain (phase 2): a height field over a rectangle, cooked by `og cook` from a `type terrain` .ogt (a 16-bit grey PNG or an EXR heightmap, an optional control map, up to four layered materials with slope and altitude rules, their maps separate textures or layers of two texture arrays: `layers` and `layerNormals` with each layer's `albedoLayer` and `normalLayer`, two sampled textures instead of up to eight) into tiles; drawn as a geometry clipmap around the camera (nested levels by distance, no cracks) that casts and receives shadows under the fog; collided with as a height field; baked into navmeshes by a navmesh file's `terrain` entry.\nLoad it with og.terrain.load, then place it: an og.Terrain on an entity draws it placed by the entity's world transform (centred on its position, turned and scaled with it), and an og.Collider of shape SHAPE_HEIGHTFIELD naming the TerrainInfo's geometry makes it solid. og.TerrainScatter grows grass, flowers or stones over a layer near the camera. og.terrain.sample answers the surface's height and normal anywhere (pass it the entity's position, quaternion and scale); og.terrain.edit raises, lowers, flattens, smooths or paints it at runtime, and og.terrain.setLayer retunes a layer. Edits are world state: a snapshot keeps them, the world hash covers them, and a restore brings the ground back as it was (rebuilt from the cook with the restored edits replayed, for the renderer, physics and sample alike).\nIts tiles stream (phase 3): the cook stores the field as a pyramid of tiles, each compressed on its own, and every frame the renderer keeps on the GPU only the tiles the camera's clipmap wants, within each terrain's tile budget (16 MiB by default), decoded off the frame (a worker thread natively, the browser's DecompressionStream on the Web) a few at a time, the most needed first; until a tile arrives its area draws from the coarser level that holds it, then fades in. Nothing a tick reads depends on which tiles are resident (collision and og.terrain.sample read the whole field), so a run's world hash is the same streamed or not. The probe's `terrain_stream:resident` decodes every wanted tile before each frame instead (`terrain_stream:stream[:<MiB>]` streams again, within that budget), and `dump_stream`'s `terrain` gives the tiles resident, waiting, decoding and cached.",
10586
+ "doc": "Terrain (phase 2): a height field over a rectangle, cooked by `og cook` from a `type terrain` .ogt (a 16-bit grey PNG or an EXR heightmap, an optional control map, up to four layered materials with slope and altitude rules, their maps separate textures or layers of two texture arrays: `layers` and `layerNormals` with each layer's `albedoLayer` and `normalLayer`, two sampled textures instead of up to eight) into tiles; drawn as a geometry clipmap around the camera (nested levels by distance, no cracks) that casts and receives shadows under the fog; collided with as a height field; baked into navmeshes by a navmesh file's `terrain` entry.\nLoad it with og.terrain.load, then place it: an og.Terrain on an entity draws it placed by the entity's world transform (centred on its position, turned and scaled with it), and an og.Collider of shape SHAPE_HEIGHTFIELD naming the TerrainInfo's geometry makes it solid. og.TerrainScatter grows grass, flowers or stones over a layer near the camera. og.terrain.sample answers the surface's height and normal anywhere (pass it the entity's position, quaternion and scale); og.terrain.edit raises, lowers, flattens, smooths or paints it at runtime, and og.terrain.setLayer retunes a layer. Terrain is 3D: the 2D profile's publish and lanes modules leave it out (RenderCapabilities.world3d is false), where every call here answers a refusal naming that field. Edits are world state: a snapshot keeps them, the world hash covers them, and a restore brings the ground back as it was (rebuilt from the cook with the restored edits replayed, for the renderer, physics and sample alike).\nIts tiles stream (phase 3): the cook stores the field as a pyramid of tiles, each compressed on its own, and every frame the renderer keeps on the GPU only the tiles the camera's clipmap wants, within each terrain's tile budget (16 MiB by default), decoded off the frame (a worker thread natively, the browser's DecompressionStream on the Web) a few at a time, the most needed first; until a tile arrives its area draws from the coarser level that holds it, then fades in. Nothing a tick reads depends on which tiles are resident (collision and og.terrain.sample read the whole field), so a run's world hash is the same streamed or not. The probe's `terrain_stream:resident` decodes every wanted tile before each frame instead (`terrain_stream:stream[:<MiB>]` streams again, within that budget), and `dump_stream`'s `terrain` gives the tiles resident, waiting, decoding and cached.",
9762
10587
  "place": "namespace",
9763
10588
  "publish": true,
9764
10589
  "types": [
@@ -14521,10 +15346,44 @@
14521
15346
  "none": "No curve: the exposed linear colour is clamped to 0..1, so 2D colours come out exactly as authored at exposure 1 (the same as toneMapped false)."
14522
15347
  }
14523
15348
  },
15349
+ "GraphicsQuality": {
15350
+ "x-og-kind": "enum",
15351
+ "x-og-module": "render",
15352
+ "description": "A graphics preset: a row of RenderSettings' quality fields that og.render.applyQuality writes (its doc has the table). The settings screen (og.ui.settingsScreen) offers the three.",
15353
+ "type": "string",
15354
+ "enum": [
15355
+ "medium",
15356
+ "low",
15357
+ "high"
15358
+ ],
15359
+ "x-og-variants": {
15360
+ "medium": "The full frame: every quality field at its default.",
15361
+ "low": "The cheapest frame: no rect-light shadows, two shadowed lights, half-size shadow maps, plain fog, hard particles, no screen-space reflections or ambient occlusion, FXAA for MSAA.",
15362
+ "high": "The full frame with the shadow maps at twice their side."
15363
+ }
15364
+ },
15365
+ "QualityPreset": {
15366
+ "x-og-kind": "enum",
15367
+ "x-og-module": "render",
15368
+ "description": "The preset RenderSettings' quality fields were last written with (RenderSettings.quality).",
15369
+ "type": "string",
15370
+ "enum": [
15371
+ "none",
15372
+ "low",
15373
+ "medium",
15374
+ "high"
15375
+ ],
15376
+ "x-og-variants": {
15377
+ "none": "No preset applied: the fields are their defaults or the game's own.",
15378
+ "low": "og.render.applyQuality(world, \"low\").",
15379
+ "medium": "og.render.applyQuality(world, \"medium\").",
15380
+ "high": "og.render.applyQuality(world, \"high\")."
15381
+ }
15382
+ },
14524
15383
  "RenderSettings": {
14525
15384
  "x-og-kind": "resource",
14526
15385
  "x-og-module": "render",
14527
- "description": "The frame's look after lighting: environment light, sky, bloom, exposure metering and tone mapping. Read after each tick; it never feeds back into the simulation. Camera.exposure is the manual exposure.",
15386
+ "description": "The frame's look after lighting: environment light, sky, bloom, exposure metering and tone mapping, and what the frame may cost: the quality fields (rectShadows to msaaSamples) that og.render.applyQuality sets from a graphics preset. Read after each tick; it never feeds back into the simulation. Camera.exposure is the manual exposure.",
14528
15387
  "type": "object",
14529
15388
  "properties": {
14530
15389
  "environmentIntensity": {
@@ -14612,7 +15471,7 @@
14612
15471
  },
14613
15472
  "lodBias": {
14614
15473
  "type": "number",
14615
- "description": "A multiplier on every model's projected size when its level of detail is picked (og.Model.lodBias multiplies it again): 1 is the engine's choice, 2 keeps full meshes twice as far, 0.5 simplifies sooner; 0 draws every model at its full mesh.",
15474
+ "description": "A multiplier on every model's projected level-of-detail error when its level is picked (og.Model.lodBias multiplies it again): a level is drawn while its error, measured by the cook against the full mesh, covers less than a pixel on the screen. 1 is the engine's choice, 2 keeps full meshes twice as far, 0.5 simplifies sooner; 0 draws every model at its full mesh.",
14616
15475
  "default": 1,
14617
15476
  "minimum": 0,
14618
15477
  "maximum": 16,
@@ -14622,6 +15481,61 @@
14622
15481
  "type": "boolean",
14623
15482
  "description": "Draw the baked lighting of the scenes that have it (og bake lightmaps): static models with their lightmaps, everything else inside a LightVolume with its probes. False draws every scene as if unbaked, its ambient light from the sky or the environment map: to compare, or to see the realtime lights alone.",
14624
15483
  "default": true
15484
+ },
15485
+ "quality": {
15486
+ "$ref": "#/$defs/QualityPreset",
15487
+ "description": "The graphics preset og.render.applyQuality last wrote into the quality fields below (rectShadows to msaaSamples), or none. A record for the settings screen and the probe: the renderer reads the fields, never this, so a field the game sets after the preset is drawn as set.",
15488
+ "default": "none"
15489
+ },
15490
+ "rectShadows": {
15491
+ "type": "boolean",
15492
+ "description": "Rect lights whose castShadow is on cast their soft shadows; false draws every rect light unshadowed, each keeping its castShadow for when this is on again. The low preset turns it off: a rect light's shadow is the costliest light in the frame.",
15493
+ "default": true
15494
+ },
15495
+ "shadowedLights": {
15496
+ "type": "integer",
15497
+ "minimum": 0,
15498
+ "maximum": 4294967295,
15499
+ "description": "At most this many point, spot and rect lights cast shadows in a frame, the brightest at the camera kept (RenderCapabilities.shadowedLights caps it); 0 casts none. The directional light's cascades are not counted.",
15500
+ "default": 8,
15501
+ "minimum": 0,
15502
+ "maximum": 8
15503
+ },
15504
+ "shadowMapScale": {
15505
+ "type": "number",
15506
+ "description": "The shadow maps at this fraction of their full side: the directional light's cascades (2048 texels by default) and the local lights' layers (512), each kept within 64 texels and the device's limit (4096 for the cascades). Changing it makes the maps again at the next frame; RenderCapabilities.shadowMapSize is the cascades' side drawn.",
15507
+ "default": 1,
15508
+ "minimum": 0.125,
15509
+ "maximum": 4
15510
+ },
15511
+ "volumetricFog": {
15512
+ "type": "boolean",
15513
+ "description": "An og.VolumetricFog is drawn lit, with its light shafts; false draws the same medium as plain distance and height fog.",
15514
+ "default": true
15515
+ },
15516
+ "softParticles": {
15517
+ "type": "boolean",
15518
+ "description": "Particles with a softDistance fade against the scene behind them; false draws them hard-edged and asks no depth prepass for them.",
15519
+ "default": true
15520
+ },
15521
+ "screenSpaceReflections": {
15522
+ "type": "boolean",
15523
+ "description": "PostEffects.ssr draws (on the full tier); false leaves smooth surfaces the reflection probes' and the environment's reflection. PostEffects keeps its settings either way.",
15524
+ "default": true
15525
+ },
15526
+ "ambientOcclusion": {
15527
+ "type": "boolean",
15528
+ "description": "PostEffects.ao darkens creases; false draws none. PostEffects keeps its settings either way.",
15529
+ "default": true
15530
+ },
15531
+ "msaaSamples": {
15532
+ "type": "integer",
15533
+ "minimum": 0,
15534
+ "maximum": 4294967295,
15535
+ "description": "Samples per pixel where the antialiasing multisamples (msaa, the WebGL2 tier's choice; RenderCapabilities.antialias): 4, or 1, which draws FXAA instead. TAA, FXAA and no antialiasing take one sample whatever it says. Changing it makes the multisampled targets and pipelines again at the next frame.",
15536
+ "default": 4,
15537
+ "minimum": 1,
15538
+ "maximum": 4
14625
15539
  }
14626
15540
  },
14627
15541
  "additionalProperties": false
@@ -14731,6 +15645,22 @@
14731
15645
  "additive": "Added (light, fire, magic): order-independent within a layer."
14732
15646
  }
14733
15647
  },
15648
+ "SpriteFacing": {
15649
+ "x-og-kind": "enum",
15650
+ "x-og-module": "render",
15651
+ "description": "How a world sprite turns towards the camera that draws it: a billboard (three.js Sprite). Decided as a mode of og.Sprite and SpriteDesc rather than a component of its own: a billboard is the same quad, texture, region, flash, glow, blend, layer, drop shadow and texture array layer as any sprite, drawn by the same sprite batch and shader, which only stands its corners differently. The turn is computed on the GPU from each camera's own axes (the main camera, every og.CameraView and og.Mirror, every face of a reflection probe), so it never feeds back into the simulation and a snapshot holds only the mode. The sprite turns about its anchor (a nine-slice turns as one panel), `rotation` still turns it within its own plane, and its drop shadow is cast by the quad stood upright facing the sun, so it shows the whole shape from every camera. Within its layer a faced sprite without sortY draws back to front from the camera's eye (by view depth; a probe's six faces by distance from the probe), after the layer's drop shadows and before its unfaced sprites; and it is culled by its bounds (a sphere round the anchor through the farthest corner) against the camera's view. Sprites draw over the 3D scene after its transparent meshes and particles, depth tested and not depth writing, so a billboard behind a transparent mesh still draws over it.",
15652
+ "type": "string",
15653
+ "enum": [
15654
+ "none",
15655
+ "camera",
15656
+ "cameraY"
15657
+ ],
15658
+ "x-og-variants": {
15659
+ "none": "In the world's xy plane facing +z, as every sprite before billboards.",
15660
+ "camera": "In the camera's view plane: its x along the camera's right, its y along its up, as particle billboards (smoke, glints, impostors seen from any side).",
15661
+ "cameraY": "Upright, turned about the world's y axis towards the camera: its y stays +y, its x the camera's right laid level. Trees, characters and props that must stand while the camera looks down on them."
15662
+ }
15663
+ },
14734
15664
  "StripBlend": {
14735
15665
  "x-og-kind": "enum",
14736
15666
  "x-og-module": "render",
@@ -15107,45 +16037,14 @@
15107
16037
  "maximum": 1,
15108
16038
  "x-og-tunable": true
15109
16039
  },
15110
- "custom1": {
15111
- "$ref": "#/$defs/Post",
15112
- "description": "The first custom post pass (og.load.post, a `type post` asset; 0 is none). PostEffects.custom1 to custom8 are an ordered list, as many as og-render runs (RenderCapabilities.maxCustomPosts): each runs over the one before it (the lit image for the first), after depth of field and motion blur and before exposure, bloom and the tone curve, and reads the lit image, depth and normals too (it turns the prepass on). Its parameters are set with og.render.setPostParam. A pass the device fails to compile is reported (og.render.postErrors) and skipped.",
15113
- "default": 0
15114
- },
15115
- "custom2": {
15116
- "$ref": "#/$defs/Post",
15117
- "description": "The second custom post pass, over the first's output (0 skips the slot).",
15118
- "default": 0
15119
- },
15120
- "custom3": {
15121
- "$ref": "#/$defs/Post",
15122
- "description": "The third custom post pass.",
15123
- "default": 0
15124
- },
15125
- "custom4": {
15126
- "$ref": "#/$defs/Post",
15127
- "description": "The fourth custom post pass.",
15128
- "default": 0
15129
- },
15130
- "custom5": {
15131
- "$ref": "#/$defs/Post",
15132
- "description": "The fifth custom post pass.",
15133
- "default": 0
15134
- },
15135
- "custom6": {
15136
- "$ref": "#/$defs/Post",
15137
- "description": "The sixth custom post pass.",
15138
- "default": 0
15139
- },
15140
- "custom7": {
15141
- "$ref": "#/$defs/Post",
15142
- "description": "The seventh custom post pass.",
15143
- "default": 0
15144
- },
15145
- "custom8": {
15146
- "$ref": "#/$defs/Post",
15147
- "description": "The eighth and last custom post pass, over every one before it.",
15148
- "default": 0
16040
+ "custom": {
16041
+ "type": "array",
16042
+ "items": {
16043
+ "$ref": "#/$defs/Post"
16044
+ },
16045
+ "maxItems": 8,
16046
+ "description": "The custom post passes, in order (og.load.post, `type post` assets): at most RenderCapabilities.maxCustomPosts (8, og-render's MAX_CUSTOM_POSTS). Each runs over the one before it (the lit image for the first), after depth of field and motion blur and before exposure, bloom and the tone curve, and reads the lit image, depth and normals too (any pass turns the prepass on). Write the whole list: `post.custom = [grade, crt]`, `post.custom = []`; a look gives it on one line (`custom \"post/tint.ogt\" \"post/crt.ogt\"`). A 0 in it is skipped. Its parameters are set with og.render.setPostParam. A pass the device fails to compile is reported (og.render.postErrors) and skipped.",
16047
+ "default": []
15149
16048
  }
15150
16049
  },
15151
16050
  "additionalProperties": false
@@ -16391,7 +17290,7 @@
16391
17290
  "SpriteDesc": {
16392
17291
  "x-og-kind": "desc",
16393
17292
  "x-og-module": "render",
16394
- "description": "A textured or solid quad in the world's xy plane facing +z, drawn this frame only (immediate mode, as og.render.text): submit it every tick it should show. Sprites and text are sorted by layer and drawn over the 3D scene.",
17293
+ "description": "A textured or solid quad in the world's xy plane facing +z (or turned to the camera, `facing`), drawn this frame only (immediate mode, as og.render.text): submit it every tick it should show. Sprites and text are sorted by layer and drawn over the 3D scene.",
16395
17294
  "type": "object",
16396
17295
  "properties": {
16397
17296
  "texture": {
@@ -16553,6 +17452,11 @@
16553
17452
  "description": "As three.js Material.toneMapped: true draws the sprite into the lit scene, carried by exposure (auto exposure included), bloom and the tone curve like any surface. false keeps its colours as given: still placed in the world, depth tested and sorted with the other sprites, but shown unexposed and uncurved (clamped to 0 to 1), outside bloom and fog; the grade, vignette, grain and screen flash still apply. For world UI that must read the same at noon and at dusk: speech bubbles, name tags, markers. Alpha blending only: an additive sprite adds light and is always tone mapped.",
16554
17453
  "default": true
16555
17454
  },
17455
+ "facing": {
17456
+ "$ref": "#/$defs/SpriteFacing",
17457
+ "description": "A billboard: none keeps the quad in the world's xy plane; camera turns it into the view plane of each camera that draws it; cameraY stands it upright turned about y towards the camera (trees, characters). World sprites only (a screen sprite ignores it); see SpriteFacing for sorting, culling and its drop shadow.",
17458
+ "default": "none"
17459
+ },
16556
17460
  "flipX": {
16557
17461
  "type": "boolean",
16558
17462
  "description": "Mirror the region left to right.",
@@ -16619,7 +17523,7 @@
16619
17523
  "Sprite": {
16620
17524
  "x-og-kind": "component",
16621
17525
  "x-og-module": "render",
16622
- "description": "A sprite drawn every frame at the entity's MatrixWorld (its xy and z, its rotation about z; its scale multiplies width and height, so squash and stretch is a tween of og.Transform.scale). With an og.Flipbook on the entity, the texture (or an `array true` sheet's texture array layer), region and anchor come from the flipbook's current frame.\nSorted with the immediate sprites and text by layer, then (sortY) by y, then texture: one draw per run of one texture. Tile layers draw before the sprites of their layer.",
17526
+ "description": "A sprite drawn every frame at the entity's MatrixWorld (its xy and z, its rotation about z, in the xy plane or turned to the camera by `facing`; its scale multiplies width and height, so squash and stretch is a tween of og.Transform.scale). With an og.Flipbook on the entity, the texture (or an `array true` sheet's texture array layer), region and anchor come from the flipbook's current frame.\nSorted with the immediate sprites and text by layer, then (sortY) by y, then texture: one draw per run of one texture. Tile layers draw before the sprites of their layer.",
16623
17527
  "type": "object",
16624
17528
  "properties": {
16625
17529
  "texture": {
@@ -16708,6 +17612,11 @@
16708
17612
  "description": "Within its layer, lower y draws in front: top-down characters, props and trees overlap by their feet.",
16709
17613
  "default": false
16710
17614
  },
17615
+ "facing": {
17616
+ "$ref": "#/$defs/SpriteFacing",
17617
+ "description": "A billboard (SpriteDesc.facing): none keeps the quad in the world's xy plane; camera turns it into each camera's view plane; cameraY stands it upright turned about y towards the camera, for trees and characters in a 3D set. Its afterimages face as it does; its blob shadow stays flat.",
17618
+ "default": "none"
17619
+ },
16711
17620
  "flipX": {
16712
17621
  "type": "boolean",
16713
17622
  "description": "Mirror left to right (a character facing left).",
@@ -17194,7 +18103,7 @@
17194
18103
  "type": "integer",
17195
18104
  "minimum": 0,
17196
18105
  "maximum": 4294967295,
17197
- "description": "The most custom post passes a frame runs: 8 on every tier, PostEffects.custom1 to custom8 (numbered fields, as a reflected record has no list field)."
18106
+ "description": "The most custom post passes a frame runs: 8 on every tier, PostEffects.custom's capacity."
17198
18107
  },
17199
18108
  "volumetricFog": {
17200
18109
  "$ref": "#/$defs/VolumetricFog",
@@ -17214,7 +18123,17 @@
17214
18123
  },
17215
18124
  "gpuTimestamps": {
17216
18125
  "type": "boolean",
17217
- "description": "Whether the device times each render pass on the GPU (timestamp queries: Vulkan, Metal and D3D12 adapters that have them, WebGPU with `timestamp-query`; never WebGL2): ProbeTimings.gpuPasses and the trace's gpu thread are filled only where it is true."
18126
+ "description": "Whether the device times each render pass on the GPU (timestamp queries: Vulkan, Metal and D3D12 adapters that have them, WebGPU with `timestamp-query`; never WebGL2, and never in a publish build, which leaves the queries out): ProbeTimings.gpuPasses and the trace's gpu thread are filled only where it is true."
18127
+ },
18128
+ "spriteAnisotropy": {
18129
+ "type": "integer",
18130
+ "minimum": 0,
18131
+ "maximum": 4294967295,
18132
+ "description": "The anisotropic filtering of sprites and og.ui's images (text is not filtered anisotropically: its distance field would lose its edge): 16 where the device filters anisotropically (WebGPU, Vulkan, Metal, WebGL2 with EXT_texture_filter_anisotropic), 1 where it has none and on a software rasterizer (the vulkan-cpu tier: lavapipe's anisotropic filter softens even an image drawn 1:1), 0 without a renderer. Above 1, an image squeezed along one axis keeps the other axis's detail: a style's nine-slice middle squeezed into a short button keeps its outline as crisp as the corners. At 1 the squeezed piece reads a smaller mip level and blurs; og.ui bounds a middle's squeeze at twice its art, so the outline there is one mip level softer than the corners."
18133
+ },
18134
+ "world3d": {
18135
+ "type": "boolean",
18136
+ "description": "Whether the renderer draws the 3D world subsystems: terrain, planar water (og.Water), the 3D fluids, scattered copies and volumetric fog. False in the 2D profile's publish and lanes modules, which leave them out (a scene's are not drawn, og.terrain's calls and a SHAPE_HEIGHTFIELD collider answer a refusal naming this field, and `og build --publish` refuses a 2D project whose scenes hold og.Terrain, og.TerrainScatter, og.Water, og.WaterSurface or og.Fluid3D, by scene file and line); true in the 3D modules, the dev module and the native host."
17218
18137
  }
17219
18138
  },
17220
18139
  "required": [
@@ -17235,7 +18154,9 @@
17235
18154
  "volumetricFog",
17236
18155
  "volumetricLights",
17237
18156
  "maxLightCookies",
17238
- "gpuTimestamps"
18157
+ "gpuTimestamps",
18158
+ "spriteAnisotropy",
18159
+ "world3d"
17239
18160
  ],
17240
18161
  "additionalProperties": false
17241
18162
  },
@@ -17800,6 +18721,659 @@
17800
18721
  },
17801
18722
  "additionalProperties": false
17802
18723
  },
18724
+ "ShapePoints": {
18725
+ "x-og-kind": "struct",
18726
+ "x-og-module": "render",
18727
+ "description": "A shape as points, what og.Shape.extractPoints returns and what shapeGeometry, extrudeGeometry and sweepGeometry read: the outline, then each hole. A repeated closing point is dropped, as three.js drops it.",
18728
+ "type": "object",
18729
+ "properties": {
18730
+ "shape": {
18731
+ "type": "array",
18732
+ "items": {
18733
+ "$ref": "#/$defs/Vector2"
18734
+ },
18735
+ "description": "The outline's points, in order."
18736
+ },
18737
+ "holes": {
18738
+ "type": "array",
18739
+ "items": {
18740
+ "type": "array",
18741
+ "items": {
18742
+ "$ref": "#/$defs/Vector2"
18743
+ }
18744
+ },
18745
+ "description": "Each hole's points, in order."
18746
+ }
18747
+ },
18748
+ "required": [
18749
+ "shape",
18750
+ "holes"
18751
+ ],
18752
+ "additionalProperties": false
18753
+ },
18754
+ "ShapeGeometryDesc": {
18755
+ "x-og-kind": "desc",
18756
+ "x-og-module": "render",
18757
+ "description": "A flat shape with holes in the xy plane facing +Z, as three.js ShapeGeometry(shapes, curveSegments): triangulated by earcut (three.js's own port), the outline made clockwise and the holes counter-clockwise first, so its vertices and triangles are three.js's for the same shape. uvs are world x and y (v flipped to run down the image).",
18758
+ "type": "object",
18759
+ "properties": {
18760
+ "shapes": {
18761
+ "anyOf": [
18762
+ {
18763
+ "anyOf": [
18764
+ {
18765
+ "x-og-type": "Path2"
18766
+ },
18767
+ {
18768
+ "anyOf": [
18769
+ {
18770
+ "type": "array",
18771
+ "items": {
18772
+ "x-og-type": "Path2"
18773
+ }
18774
+ },
18775
+ {
18776
+ "type": "array",
18777
+ "items": {
18778
+ "$ref": "#/$defs/Vector2"
18779
+ }
18780
+ }
18781
+ ]
18782
+ }
18783
+ ]
18784
+ },
18785
+ {
18786
+ "type": "null"
18787
+ }
18788
+ ],
18789
+ "description": "One og.Shape (an og.Path2 is a shape without holes), a list of them, or the outline's points. Without it, three.js's default triangle (0, 0.5), (-0.5, -0.5), (0.5, -0.5).",
18790
+ "default": null
18791
+ },
18792
+ "curveSegments": {
18793
+ "type": "integer",
18794
+ "minimum": 0,
18795
+ "maximum": 4294967295,
18796
+ "description": "Points per curve when an og.Shape's curves are flattened (three.js extractPoints(curveSegments); an ellipse takes twice as many, a spline as many per point).",
18797
+ "default": 12,
18798
+ "minimum": 1
18799
+ },
18800
+ "uvRepeat": {
18801
+ "$ref": "#/$defs/Vector2",
18802
+ "description": "How many times a texture repeats across it, along u and v: its UVs scaled by uvRepeat (three.js texture.repeat, here on the geometry; material textures sample with repeat wrapping).",
18803
+ "default": {
18804
+ "x": 1,
18805
+ "y": 1
18806
+ }
18807
+ },
18808
+ "physics": {
18809
+ "type": "boolean",
18810
+ "description": "Register it as a physics mesh too (BufferGeometryDesc.physics).",
18811
+ "default": true
18812
+ },
18813
+ "dynamic": {
18814
+ "type": "boolean",
18815
+ "description": "Whether og.render.updateGeometry may rewrite it later (BufferGeometryDesc.dynamic).",
18816
+ "default": false
18817
+ }
18818
+ },
18819
+ "additionalProperties": false
18820
+ },
18821
+ "ExtrudeGeometryDesc": {
18822
+ "x-og-kind": "desc",
18823
+ "x-og-module": "render",
18824
+ "description": "Shapes pushed along +Z, or along a path, as three.js ExtrudeGeometry(shapes, options) with its option names and defaults: a closed solid, its lids from earcut and its walls ring by ring, rounded by a bevel of bevelSegments rings at each face. Unindexed as three.js's (every triangle its own vertices, flat normals), three.js's vertex order and counts; uvs from three.js's WorldUVGenerator (v flipped to run down the image).",
18825
+ "type": "object",
18826
+ "properties": {
18827
+ "shapes": {
18828
+ "anyOf": [
18829
+ {
18830
+ "anyOf": [
18831
+ {
18832
+ "x-og-type": "Path2"
18833
+ },
18834
+ {
18835
+ "anyOf": [
18836
+ {
18837
+ "type": "array",
18838
+ "items": {
18839
+ "x-og-type": "Path2"
18840
+ }
18841
+ },
18842
+ {
18843
+ "type": "array",
18844
+ "items": {
18845
+ "$ref": "#/$defs/Vector2"
18846
+ }
18847
+ }
18848
+ ]
18849
+ }
18850
+ ]
18851
+ },
18852
+ {
18853
+ "type": "null"
18854
+ }
18855
+ ],
18856
+ "description": "One og.Shape, a list of them, or the outline's points. Without it, three.js's default square of side 1.",
18857
+ "default": null
18858
+ },
18859
+ "depth": {
18860
+ "type": "number",
18861
+ "description": "How far the shape is pushed along +Z (without extrudePath).",
18862
+ "default": 1,
18863
+ "minimum": 0,
18864
+ "x-og-unit": "m"
18865
+ },
18866
+ "steps": {
18867
+ "type": "integer",
18868
+ "minimum": 0,
18869
+ "maximum": 4294967295,
18870
+ "description": "Rings along the depth or the path.",
18871
+ "default": 1,
18872
+ "minimum": 1
18873
+ },
18874
+ "bevelEnabled": {
18875
+ "type": "boolean",
18876
+ "description": "Round the edges of both faces.",
18877
+ "default": true
18878
+ },
18879
+ "bevelThickness": {
18880
+ "type": "number",
18881
+ "description": "How far the bevel reaches beyond each face, along z.",
18882
+ "default": 0.2,
18883
+ "minimum": 0,
18884
+ "x-og-unit": "m"
18885
+ },
18886
+ "bevelSize": {
18887
+ "anyOf": [
18888
+ {
18889
+ "type": "number"
18890
+ },
18891
+ {
18892
+ "type": "null"
18893
+ }
18894
+ ],
18895
+ "description": "How far the bevel reaches out from the outline. Null or unset, bevelThickness - 0.1, as three.js derives it (0.1 at the default thickness, 0 at a thickness of 0.1).",
18896
+ "default": null,
18897
+ "x-og-unit": "m"
18898
+ },
18899
+ "bevelOffset": {
18900
+ "type": "number",
18901
+ "description": "How far the bevel starts out from the outline.",
18902
+ "default": 0,
18903
+ "x-og-unit": "m"
18904
+ },
18905
+ "bevelSegments": {
18906
+ "type": "integer",
18907
+ "minimum": 0,
18908
+ "maximum": 4294967295,
18909
+ "description": "Rings in each bevel.",
18910
+ "default": 3,
18911
+ "minimum": 1
18912
+ },
18913
+ "curveSegments": {
18914
+ "type": "integer",
18915
+ "minimum": 0,
18916
+ "maximum": 4294967295,
18917
+ "description": "Points per curve when an og.Shape's curves are flattened.",
18918
+ "default": 12,
18919
+ "minimum": 1
18920
+ },
18921
+ "extrudePath": {
18922
+ "$ref": "#/$defs/Path",
18923
+ "description": "A path (og.path.create or og.path.find) to extrude along instead of +Z: the shape's x along the path's normal and y along its binormal (three.js's Frenet frames), steps rings evenly spaced along it. A path turns the bevel off, as three.js. 0 extrudes along +Z.",
18924
+ "default": 0
18925
+ },
18926
+ "uvRepeat": {
18927
+ "$ref": "#/$defs/Vector2",
18928
+ "description": "How many times a texture repeats across it, along u and v: its UVs scaled by uvRepeat (three.js texture.repeat, here on the geometry; material textures sample with repeat wrapping).",
18929
+ "default": {
18930
+ "x": 1,
18931
+ "y": 1
18932
+ }
18933
+ },
18934
+ "physics": {
18935
+ "type": "boolean",
18936
+ "description": "Register it as a physics mesh too.",
18937
+ "default": true
18938
+ },
18939
+ "dynamic": {
18940
+ "type": "boolean",
18941
+ "description": "Whether og.render.updateGeometry may rewrite it later.",
18942
+ "default": false
18943
+ }
18944
+ },
18945
+ "additionalProperties": false
18946
+ },
18947
+ "LatheGeometryDesc": {
18948
+ "x-og-kind": "desc",
18949
+ "x-og-module": "render",
18950
+ "description": "A profile turned round +Y, as three.js LatheGeometry(points, segments, phiStart, phiLength): vases, bottles, columns, wheels. three.js's vertices, indices and smooth normals; u round the axis, v along the profile (flipped to run down the image). A whole turn's seam meets exactly.",
18951
+ "type": "object",
18952
+ "properties": {
18953
+ "points": {
18954
+ "type": "array",
18955
+ "items": {
18956
+ "$ref": "#/$defs/Vector2"
18957
+ },
18958
+ "description": "The profile, x the distance from the axis and y the height, in order (at least two). Without it, three.js's default (0, -0.5), (0.5, 0), (0, 0.5).",
18959
+ "default": [],
18960
+ "x-og-unit": "m"
18961
+ },
18962
+ "segments": {
18963
+ "type": "integer",
18964
+ "minimum": 0,
18965
+ "maximum": 4294967295,
18966
+ "description": "Segments round the axis.",
18967
+ "default": 12,
18968
+ "minimum": 1
18969
+ },
18970
+ "phiStart": {
18971
+ "type": "number",
18972
+ "description": "Where the turn starts, from +Z towards +X.",
18973
+ "default": 0,
18974
+ "x-og-unit": "rad"
18975
+ },
18976
+ "phiLength": {
18977
+ "type": "number",
18978
+ "description": "How far it turns; 2 pi is whole (clamped to it).",
18979
+ "default": 6.283185307179586,
18980
+ "x-og-unit": "rad"
18981
+ },
18982
+ "uvRepeat": {
18983
+ "$ref": "#/$defs/Vector2",
18984
+ "description": "How many times a texture repeats across it, along u and v: its UVs scaled by uvRepeat (three.js texture.repeat, here on the geometry; material textures sample with repeat wrapping).",
18985
+ "default": {
18986
+ "x": 1,
18987
+ "y": 1
18988
+ }
18989
+ },
18990
+ "physics": {
18991
+ "type": "boolean",
18992
+ "description": "Register it as a physics mesh too.",
18993
+ "default": true
18994
+ },
18995
+ "dynamic": {
18996
+ "type": "boolean",
18997
+ "description": "Whether og.render.updateGeometry may rewrite it later.",
18998
+ "default": false
18999
+ }
19000
+ },
19001
+ "additionalProperties": false
19002
+ },
19003
+ "PolyhedronGeometryDesc": {
19004
+ "x-og-kind": "desc",
19005
+ "x-og-module": "render",
19006
+ "description": "A polyhedron from vertices and faces, every face subdivided `detail` times and every vertex pushed onto the sphere of `radius`, as three.js PolyhedronGeometry(vertices, indices, radius, detail): unindexed, flat normals at detail 0 and the sphere's above, three.js's spherical uvs.",
19007
+ "type": "object",
19008
+ "properties": {
19009
+ "vertices": {
19010
+ "type": "array",
19011
+ "items": {
19012
+ "type": "number"
19013
+ },
19014
+ "description": "The corners, x y z each (three.js's flat array).",
19015
+ "default": []
19016
+ },
19017
+ "indices": {
19018
+ "type": "array",
19019
+ "items": {
19020
+ "type": "integer",
19021
+ "minimum": 0,
19022
+ "maximum": 4294967295
19023
+ },
19024
+ "description": "The faces, three corner indices each, counter-clockwise seen from outside.",
19025
+ "default": []
19026
+ },
19027
+ "radius": {
19028
+ "type": "number",
19029
+ "description": "The sphere the vertices are pushed onto.",
19030
+ "default": 1,
19031
+ "minimum": 0,
19032
+ "x-og-unit": "m"
19033
+ },
19034
+ "detail": {
19035
+ "type": "integer",
19036
+ "minimum": 0,
19037
+ "maximum": 4294967295,
19038
+ "description": "Subdivisions of each face: (detail + 1)² triangles a face.",
19039
+ "default": 0,
19040
+ "maximum": 64
19041
+ },
19042
+ "uvRepeat": {
19043
+ "$ref": "#/$defs/Vector2",
19044
+ "description": "How many times a texture repeats across it, along u and v: its UVs scaled by uvRepeat (three.js texture.repeat, here on the geometry; material textures sample with repeat wrapping).",
19045
+ "default": {
19046
+ "x": 1,
19047
+ "y": 1
19048
+ }
19049
+ },
19050
+ "physics": {
19051
+ "type": "boolean",
19052
+ "description": "Register it as a physics mesh too.",
19053
+ "default": true
19054
+ },
19055
+ "dynamic": {
19056
+ "type": "boolean",
19057
+ "description": "Whether og.render.updateGeometry may rewrite it later.",
19058
+ "default": false
19059
+ }
19060
+ },
19061
+ "additionalProperties": false
19062
+ },
19063
+ "SolidGeometryDesc": {
19064
+ "x-og-kind": "desc",
19065
+ "x-og-module": "render",
19066
+ "description": "A regular solid, as three.js IcosahedronGeometry, DodecahedronGeometry, OctahedronGeometry and TetrahedronGeometry(radius, detail): PolyhedronGeometryDesc with the solid's own vertices and faces. An icosahedron at detail 2 or more is a sphere with even triangles.",
19067
+ "type": "object",
19068
+ "properties": {
19069
+ "radius": {
19070
+ "type": "number",
19071
+ "description": "The radius.",
19072
+ "default": 1,
19073
+ "minimum": 0,
19074
+ "x-og-unit": "m"
19075
+ },
19076
+ "detail": {
19077
+ "type": "integer",
19078
+ "minimum": 0,
19079
+ "maximum": 4294967295,
19080
+ "description": "Subdivisions of each face.",
19081
+ "default": 0,
19082
+ "maximum": 64
19083
+ },
19084
+ "uvRepeat": {
19085
+ "$ref": "#/$defs/Vector2",
19086
+ "description": "How many times a texture repeats across it, along u and v: its UVs scaled by uvRepeat (three.js texture.repeat, here on the geometry; material textures sample with repeat wrapping).",
19087
+ "default": {
19088
+ "x": 1,
19089
+ "y": 1
19090
+ }
19091
+ },
19092
+ "physics": {
19093
+ "type": "boolean",
19094
+ "description": "Register it as a physics mesh too.",
19095
+ "default": true
19096
+ },
19097
+ "dynamic": {
19098
+ "type": "boolean",
19099
+ "description": "Whether og.render.updateGeometry may rewrite it later.",
19100
+ "default": false
19101
+ }
19102
+ },
19103
+ "additionalProperties": false
19104
+ },
19105
+ "SweepKey": {
19106
+ "x-og-kind": "struct",
19107
+ "x-og-module": "render",
19108
+ "description": "A value at a fraction of a sweep's path: linear between keys, held before the first and past the last.",
19109
+ "type": "object",
19110
+ "properties": {
19111
+ "t": {
19112
+ "type": "number",
19113
+ "description": "The fraction of the path's length, 0 to 1.",
19114
+ "x-og-unit": "ratio"
19115
+ },
19116
+ "value": {
19117
+ "type": "number",
19118
+ "description": "The value there."
19119
+ }
19120
+ },
19121
+ "required": [
19122
+ "t",
19123
+ "value"
19124
+ ],
19125
+ "additionalProperties": false
19126
+ },
19127
+ "SweepGeometryDesc": {
19128
+ "x-og-kind": "desc",
19129
+ "x-og-module": "render",
19130
+ "description": "A section swept along a path, scaled and twisted over it: horns, tusks, handles, blades, tapered pipes, twisted columns (img2threejs's tapered sweep; three.js has no class for it). The section's x lies along the path's normal and y along its binormal (three.js's Frenet frames), rings evenly spaced along the path's length; u runs along the path, v round the section (flipped to run down the image), the sides smooth. An open path's ends are capped.",
19131
+ "type": "object",
19132
+ "properties": {
19133
+ "section": {
19134
+ "anyOf": [
19135
+ {
19136
+ "anyOf": [
19137
+ {
19138
+ "x-og-type": "Path2"
19139
+ },
19140
+ {
19141
+ "type": "array",
19142
+ "items": {
19143
+ "$ref": "#/$defs/Vector2"
19144
+ }
19145
+ }
19146
+ ]
19147
+ },
19148
+ {
19149
+ "type": "null"
19150
+ }
19151
+ ],
19152
+ "description": "The section's outline: an og.Shape or og.Path2 (flattened by curveSegments; holes are not swept) or its points, at least three, either winding.",
19153
+ "default": null
19154
+ },
19155
+ "path": {
19156
+ "$ref": "#/$defs/Path",
19157
+ "description": "The path (og.path.create or og.path.find).",
19158
+ "default": 0
19159
+ },
19160
+ "steps": {
19161
+ "type": "integer",
19162
+ "minimum": 0,
19163
+ "maximum": 4294967295,
19164
+ "description": "Rings along the path, less one.",
19165
+ "default": 64,
19166
+ "minimum": 1
19167
+ },
19168
+ "scale": {
19169
+ "type": "array",
19170
+ "items": {
19171
+ "$ref": "#/$defs/SweepKey"
19172
+ },
19173
+ "description": "The section's scale over the path (none: 1 throughout). A horn: [{ t: 0, value: 1 }, { t: 1, value: 0.05 }].",
19174
+ "default": []
19175
+ },
19176
+ "twist": {
19177
+ "type": "array",
19178
+ "items": {
19179
+ "$ref": "#/$defs/SweepKey"
19180
+ },
19181
+ "description": "The section's turn round the path over its length, in radians (none: 0).",
19182
+ "default": []
19183
+ },
19184
+ "caps": {
19185
+ "type": "boolean",
19186
+ "description": "Close the ends of an open path.",
19187
+ "default": true
19188
+ },
19189
+ "curveSegments": {
19190
+ "type": "integer",
19191
+ "minimum": 0,
19192
+ "maximum": 4294967295,
19193
+ "description": "Points per curve when an og.Shape section is flattened.",
19194
+ "default": 12,
19195
+ "minimum": 1
19196
+ },
19197
+ "uvRepeat": {
19198
+ "$ref": "#/$defs/Vector2",
19199
+ "description": "How many times a texture repeats across it, along u and v: its UVs scaled by uvRepeat (three.js texture.repeat, here on the geometry; material textures sample with repeat wrapping).",
19200
+ "default": {
19201
+ "x": 1,
19202
+ "y": 1
19203
+ }
19204
+ },
19205
+ "physics": {
19206
+ "type": "boolean",
19207
+ "description": "Register it as a physics mesh too.",
19208
+ "default": true
19209
+ },
19210
+ "dynamic": {
19211
+ "type": "boolean",
19212
+ "description": "Whether og.render.updateGeometry may rewrite it later.",
19213
+ "default": false
19214
+ }
19215
+ },
19216
+ "additionalProperties": false
19217
+ },
19218
+ "MirrorAxis": {
19219
+ "x-og-kind": "enum",
19220
+ "x-og-module": "render",
19221
+ "description": "The world axis a part is mirrored along, after its placement.",
19222
+ "type": "string",
19223
+ "enum": [
19224
+ "none",
19225
+ "x",
19226
+ "y",
19227
+ "z"
19228
+ ],
19229
+ "x-og-variants": {
19230
+ "none": "Not mirrored.",
19231
+ "x": "x negated: the mirror image across the yz plane.",
19232
+ "y": "y negated: across the xz plane.",
19233
+ "z": "z negated: across the xy plane."
19234
+ }
19235
+ },
19236
+ "GeometryPart": {
19237
+ "x-og-kind": "desc",
19238
+ "x-og-module": "render",
19239
+ "description": "A geometry placed as an Object3D is (scale, then rotate, then move), then mirrored: one part of og.render.mergeGeometries or an operand of og.render.csgGeometry.",
19240
+ "type": "object",
19241
+ "properties": {
19242
+ "geometry": {
19243
+ "$ref": "#/$defs/Geometry",
19244
+ "description": "Any geometry: made, a primitive or a loaded glTF's.",
19245
+ "default": 0
19246
+ },
19247
+ "position": {
19248
+ "$ref": "#/$defs/Vector3",
19249
+ "description": "Where it goes.",
19250
+ "default": {
19251
+ "x": 0,
19252
+ "y": 0,
19253
+ "z": 0
19254
+ },
19255
+ "x-og-unit": "m"
19256
+ },
19257
+ "quaternion": {
19258
+ "$ref": "#/$defs/Quaternion",
19259
+ "description": "How it turns.",
19260
+ "default": {
19261
+ "x": 0,
19262
+ "y": 0,
19263
+ "z": 0,
19264
+ "w": 1
19265
+ }
19266
+ },
19267
+ "scale": {
19268
+ "$ref": "#/$defs/Vector3",
19269
+ "description": "Its scale (a negative component mirrors it as `mirror` does).",
19270
+ "default": {
19271
+ "x": 1,
19272
+ "y": 1,
19273
+ "z": 1
19274
+ }
19275
+ },
19276
+ "mirror": {
19277
+ "$ref": "#/$defs/MirrorAxis",
19278
+ "description": "Mirror it along a world axis after placing it: a symmetric prop is one half twice, the second mirrored.",
19279
+ "default": "none"
19280
+ }
19281
+ },
19282
+ "additionalProperties": false
19283
+ },
19284
+ "CsgOp": {
19285
+ "x-og-kind": "enum",
19286
+ "x-og-module": "render",
19287
+ "description": "A boolean operation on two solids.",
19288
+ "type": "string",
19289
+ "enum": [
19290
+ "union",
19291
+ "subtract",
19292
+ "intersect"
19293
+ ],
19294
+ "x-og-variants": {
19295
+ "union": "Everything in either: a + b.",
19296
+ "subtract": "What of a is not in b: a - b (holes, notches, a dice's pips).",
19297
+ "intersect": "What is in both."
19298
+ }
19299
+ },
19300
+ "CsgGeometryDesc": {
19301
+ "x-og-kind": "desc",
19302
+ "x-og-module": "render",
19303
+ "description": "Two closed geometries combined by a boolean operation, each placed first: a box with a hole bored through it, a dice, a window cut in a wall. The result is closed when both are (every edge shared by two triangles, wound opposite), every piece keeps its source's normals, uvs and colours (b's turned inside out where it is subtracted). Where a plane side or an edge crossing is decided, the decision is exact (Shewchuk's predicates), so every host makes the same cuts; faces that coincide keep one copy or none, as the operation says.",
19304
+ "type": "object",
19305
+ "properties": {
19306
+ "op": {
19307
+ "$ref": "#/$defs/CsgOp",
19308
+ "description": "The operation.",
19309
+ "default": "union"
19310
+ },
19311
+ "a": {
19312
+ "$ref": "#/$defs/GeometryPart",
19313
+ "description": "The first operand.",
19314
+ "default": {}
19315
+ },
19316
+ "b": {
19317
+ "$ref": "#/$defs/GeometryPart",
19318
+ "description": "The second operand.",
19319
+ "default": {}
19320
+ },
19321
+ "uvRepeat": {
19322
+ "$ref": "#/$defs/Vector2",
19323
+ "description": "How many times a texture repeats across it, along u and v: its UVs scaled by uvRepeat (three.js texture.repeat, here on the geometry; material textures sample with repeat wrapping).",
19324
+ "default": {
19325
+ "x": 1,
19326
+ "y": 1
19327
+ }
19328
+ },
19329
+ "physics": {
19330
+ "type": "boolean",
19331
+ "description": "Register it as a physics mesh too.",
19332
+ "default": true
19333
+ },
19334
+ "dynamic": {
19335
+ "type": "boolean",
19336
+ "description": "Whether og.render.updateGeometry may rewrite it later.",
19337
+ "default": false
19338
+ }
19339
+ },
19340
+ "additionalProperties": false
19341
+ },
19342
+ "MergeGeometriesDesc": {
19343
+ "x-og-kind": "desc",
19344
+ "x-og-module": "render",
19345
+ "description": "Geometries placed and merged into one, as three.js BufferGeometryUtils.mergeGeometries after applying each part's matrix: a prop of parts drawn in one draw, a mirrored half, an array (the same part at several placements). Normals follow each placement; a mirrored part turns its triangles so they still face out. Vertex colours are kept when any part has them.",
19346
+ "type": "object",
19347
+ "properties": {
19348
+ "parts": {
19349
+ "type": "array",
19350
+ "items": {
19351
+ "$ref": "#/$defs/GeometryPart"
19352
+ },
19353
+ "description": "The parts, in order (at least one).",
19354
+ "default": []
19355
+ },
19356
+ "uvRepeat": {
19357
+ "$ref": "#/$defs/Vector2",
19358
+ "description": "How many times a texture repeats across it, along u and v: its UVs scaled by uvRepeat (three.js texture.repeat, here on the geometry; material textures sample with repeat wrapping).",
19359
+ "default": {
19360
+ "x": 1,
19361
+ "y": 1
19362
+ }
19363
+ },
19364
+ "physics": {
19365
+ "type": "boolean",
19366
+ "description": "Register it as a physics mesh too.",
19367
+ "default": true
19368
+ },
19369
+ "dynamic": {
19370
+ "type": "boolean",
19371
+ "description": "Whether og.render.updateGeometry may rewrite it later.",
19372
+ "default": false
19373
+ }
19374
+ },
19375
+ "additionalProperties": false
19376
+ },
17803
19377
  "LineMode": {
17804
19378
  "x-og-kind": "enum",
17805
19379
  "x-og-module": "render",
@@ -19002,7 +20576,7 @@
19002
20576
  },
19003
20577
  "lodBias": {
19004
20578
  "type": "number",
19005
- "description": "This instance's level of detail bias, multiplied with RenderSettings.lodBias: the cook simplifies every mesh into levels (a model's import settings, `model { lods 1 0.5 0.25 }` by default, the triangle fraction of each level; a glTF with MSFT_lod keeps its own levels), and the renderer draws the level its projected size calls for, with hysteresis so it does not flicker at the boundary. Above 1 keeps detail farther (a hero), below 1 drops it sooner (a crowd); 0 always draws the full mesh. dump_entity shows the level drawn.",
20579
+ "description": "This instance's level of detail bias, multiplied with RenderSettings.lodBias: the cook simplifies every mesh into levels (a model's import settings, `model { lods 1 0.5 0.25 }` by default, the triangle fraction of each level; a glTF with MSFT_lod keeps its own levels) and measures each level's error against the full mesh, and the renderer draws the coarsest level whose error covers less than a pixel at the model's distance, with hysteresis so it does not flicker at the boundary. Above 1 keeps detail farther (a hero), below 1 drops it sooner (a crowd); 0 always draws the full mesh. dump_entity shows the level drawn.",
19006
20580
  "default": 1,
19007
20581
  "minimum": 0,
19008
20582
  "maximum": 16,
@@ -19364,7 +20938,7 @@
19364
20938
  "CollisionStarted": {
19365
20939
  "x-og-kind": "event",
19366
20940
  "x-og-module": "physics",
19367
- "description": "Two solid colliders started touching during the tick.",
20941
+ "description": "Two solid colliders started touching during the tick. Read in the tick after, when og.Velocity already holds the solver's answer (the bodies parting): how hard they met is `impulse` and `relativeVelocity`, taken in the step itself.",
19368
20942
  "type": "object",
19369
20943
  "properties": {
19370
20944
  "a": {
@@ -19377,18 +20951,32 @@
19377
20951
  },
19378
20952
  "point": {
19379
20953
  "$ref": "#/$defs/Vector3",
19380
- "description": "The deepest contact point in world space (z is 0 in 2D)."
20954
+ "description": "The deepest contact point in world space (z is 0 in 2D).",
20955
+ "x-og-unit": "m"
19381
20956
  },
19382
20957
  "normal": {
19383
20958
  "$ref": "#/$defs/Vector3",
19384
20959
  "description": "The contact normal, from a towards b."
20960
+ },
20961
+ "impulse": {
20962
+ "type": "number",
20963
+ "description": "The normal impulse the solver gave the pair in the step that made the contact (every substep, every contact point), in newton-seconds: b took `impulse` along `normal`, a the same against it. Its momentum answer to the hit, at least 0; heavier and faster hits give more.",
20964
+ "minimum": 0,
20965
+ "x-og-unit": "newtonSecond"
20966
+ },
20967
+ "relativeVelocity": {
20968
+ "$ref": "#/$defs/Vector3",
20969
+ "description": "a's velocity relative to b at `point` as that step began, before the solver answered: each body's linear velocity plus its angular velocity crossed with the point's offset from its centre of mass (a static collider is still; z is 0 in 2D). Along `normal` it is the closing speed, positive while they approach: max(0, relativeVelocity · normal) is how hard they hit, in m/s.",
20970
+ "x-og-unit": "mps"
19385
20971
  }
19386
20972
  },
19387
20973
  "required": [
19388
20974
  "a",
19389
20975
  "b",
19390
20976
  "point",
19391
- "normal"
20977
+ "normal",
20978
+ "impulse",
20979
+ "relativeVelocity"
19392
20980
  ],
19393
20981
  "additionalProperties": false
19394
20982
  },
@@ -22869,6 +24457,427 @@
22869
24457
  },
22870
24458
  "additionalProperties": false
22871
24459
  },
24460
+ "LookAt": {
24461
+ "x-og-kind": "component",
24462
+ "x-og-module": "anim",
24463
+ "description": "An aim constraint on a skeleton, applied after its clips, graph and IK chains: `joint` turns so its `axis` points at `target` and its `upAxis` leans towards `up` (three.js Object3D.lookAt: +Z at the target, +Y up). The direction is held within maxYaw and maxPitch of the animated forward; with `bones` above 1 the joint's parents share the turn. Put it on its own entity (a head, the eyes, a turret's barrel); constraints apply in query order, and og.JointLimit clamps the joints it turns.",
24464
+ "type": "object",
24465
+ "properties": {
24466
+ "skeleton": {
24467
+ "$ref": "#/$defs/Entity",
24468
+ "description": "The entity with the og.Skeleton this constraint turns.",
24469
+ "default": 0
24470
+ },
24471
+ "joint": {
24472
+ "type": "integer",
24473
+ "minimum": 0,
24474
+ "maximum": 4294967295,
24475
+ "description": "The joint that aims (an index into the skin's joints): the head, an eye.",
24476
+ "default": 0,
24477
+ "minimum": 0
24478
+ },
24479
+ "bones": {
24480
+ "type": "integer",
24481
+ "minimum": 0,
24482
+ "maximum": 4294967295,
24483
+ "description": "How many joints share the turn: `joint` and its parents (3 spreads a look over neck, chest and spine), each parent taking an even share and `joint` the rest.",
24484
+ "default": 1,
24485
+ "minimum": 1,
24486
+ "maximum": 8
24487
+ },
24488
+ "target": {
24489
+ "$ref": "#/$defs/Vector3",
24490
+ "description": "The point to look at, in `space`.",
24491
+ "default": {
24492
+ "x": 0,
24493
+ "y": 0,
24494
+ "z": 0
24495
+ },
24496
+ "x-og-unit": "m"
24497
+ },
24498
+ "space": {
24499
+ "$ref": "#/$defs/IkSpace",
24500
+ "description": "What `target` and `up` are relative to: world (the frame the skeleton entity's og.Transform is in) or model.",
24501
+ "default": "world"
24502
+ },
24503
+ "axis": {
24504
+ "$ref": "#/$defs/Vector3",
24505
+ "description": "The joint's forward, in its own frame: the axis turned onto the target.",
24506
+ "default": {
24507
+ "x": 0,
24508
+ "y": 0,
24509
+ "z": 1
24510
+ }
24511
+ },
24512
+ "upAxis": {
24513
+ "$ref": "#/$defs/Vector3",
24514
+ "description": "The joint's up, in its own frame: yaw turns about it, pitch towards it, and it rolls towards `up`.",
24515
+ "default": {
24516
+ "x": 0,
24517
+ "y": 1,
24518
+ "z": 0
24519
+ }
24520
+ },
24521
+ "up": {
24522
+ "$ref": "#/$defs/Vector3",
24523
+ "description": "The up hint, a direction in `space` the joint's `upAxis` leans to (three.js Object3D.up); the zero vector keeps the animated roll.",
24524
+ "default": {
24525
+ "x": 0,
24526
+ "y": 1,
24527
+ "z": 0
24528
+ }
24529
+ },
24530
+ "maxYaw": {
24531
+ "type": "number",
24532
+ "description": "The largest turn about `upAxis`, left or right of the animated forward.",
24533
+ "default": 3.1416,
24534
+ "minimum": 0,
24535
+ "maximum": 3.1416,
24536
+ "x-og-unit": "rad",
24537
+ "x-og-tunable": true
24538
+ },
24539
+ "maxPitch": {
24540
+ "type": "number",
24541
+ "description": "The largest turn towards or away from `upAxis`, from the animated forward.",
24542
+ "default": 1.5708,
24543
+ "minimum": 0,
24544
+ "maximum": 1.5708,
24545
+ "x-og-unit": "rad",
24546
+ "x-og-tunable": true
24547
+ },
24548
+ "weight": {
24549
+ "type": "number",
24550
+ "description": "How much of the turn is applied (0 = the animated pose, 1 = aimed).",
24551
+ "default": 1,
24552
+ "minimum": 0,
24553
+ "maximum": 1,
24554
+ "x-og-tunable": true
24555
+ },
24556
+ "clamped": {
24557
+ "type": "boolean",
24558
+ "description": "Written: the target lay outside maxYaw or maxPitch, so the joint aims at the nearest direction inside them.",
24559
+ "default": false,
24560
+ "readOnly": true
24561
+ }
24562
+ },
24563
+ "additionalProperties": false
24564
+ },
24565
+ "JointLimit": {
24566
+ "x-og-kind": "component",
24567
+ "x-og-module": "anim",
24568
+ "description": "How far a joint may turn from its rest rotation when og.anim's solvers (og.IkChain, og.LookAt, og.FootLock) turn it: a swing of `axis` within a cone and a twist about it within a range. A hinge (a knee, an elbow) is `axis` along the hinge, `swing` 0 (or a little play) and the bend range as twistMin...twistMax. Put it on its own entity, one per joint; the animated pose itself is never clamped.",
24569
+ "type": "object",
24570
+ "properties": {
24571
+ "skeleton": {
24572
+ "$ref": "#/$defs/Entity",
24573
+ "description": "The entity with the og.Skeleton whose joint this limits.",
24574
+ "default": 0
24575
+ },
24576
+ "joint": {
24577
+ "type": "integer",
24578
+ "minimum": 0,
24579
+ "maximum": 4294967295,
24580
+ "description": "The limited joint (an index into the skin's joints).",
24581
+ "default": 0,
24582
+ "minimum": 0
24583
+ },
24584
+ "axis": {
24585
+ "$ref": "#/$defs/Vector3",
24586
+ "description": "The twist axis in the joint's rest frame: along the bone for a ball joint, along the hinge for a hinge.",
24587
+ "default": {
24588
+ "x": 0,
24589
+ "y": 1,
24590
+ "z": 0
24591
+ }
24592
+ },
24593
+ "swing": {
24594
+ "type": "number",
24595
+ "description": "The cone half-angle the axis may swing away from its rest direction (0 locks it: a hinge; pi is free).",
24596
+ "default": 3.1416,
24597
+ "minimum": 0,
24598
+ "maximum": 3.1416,
24599
+ "x-og-unit": "rad",
24600
+ "x-og-tunable": true
24601
+ },
24602
+ "twistMin": {
24603
+ "type": "number",
24604
+ "description": "The least twist about `axis` from rest (a hinge's bend one way).",
24605
+ "default": -3.1416,
24606
+ "minimum": -3.1416,
24607
+ "maximum": 3.1416,
24608
+ "x-og-unit": "rad",
24609
+ "x-og-tunable": true
24610
+ },
24611
+ "twistMax": {
24612
+ "type": "number",
24613
+ "description": "The most twist about `axis` from rest (a hinge's bend the other way).",
24614
+ "default": 3.1416,
24615
+ "minimum": -3.1416,
24616
+ "maximum": 3.1416,
24617
+ "x-og-unit": "rad",
24618
+ "x-og-tunable": true
24619
+ }
24620
+ },
24621
+ "additionalProperties": false
24622
+ },
24623
+ "FootLock": {
24624
+ "x-og-kind": "component",
24625
+ "x-og-module": "anim",
24626
+ "description": "Foot locking: while the playing clips plant `foot` (within 5 mm of its lowest in each clip, the notion retargeting uses, weighed across blends and cross-fades: planted when the planting clips hold at least half the pose), the foot stays where it touched the ground, through clip blends and root motion, its leg bent by two-bone IK and the foot keeping its animated orientation; when the clips lift it, the foot rejoins its animation over `release`. Put it on its own entity, one per foot; locks apply after root motion and before og.IkChain, in query order, and og.JointLimit clamps the leg. The lock is world state (the `og.anim` state block).",
24627
+ "type": "object",
24628
+ "properties": {
24629
+ "skeleton": {
24630
+ "$ref": "#/$defs/Entity",
24631
+ "description": "The entity with the og.Skeleton whose foot this holds.",
24632
+ "default": 0
24633
+ },
24634
+ "foot": {
24635
+ "type": "integer",
24636
+ "minimum": 0,
24637
+ "maximum": 4294967295,
24638
+ "description": "The foot joint (an index into the skin's joints); the two bones above it are the leg that bends.",
24639
+ "default": 0,
24640
+ "minimum": 0
24641
+ },
24642
+ "weight": {
24643
+ "type": "number",
24644
+ "description": "How much of the lock is applied (0 = the animated foot, 1 = held).",
24645
+ "default": 1,
24646
+ "minimum": 0,
24647
+ "maximum": 1,
24648
+ "x-og-tunable": true
24649
+ },
24650
+ "release": {
24651
+ "type": "number",
24652
+ "description": "Seconds the foot takes to rejoin its animation once lifted (0: at once).",
24653
+ "default": 0.15,
24654
+ "minimum": 0,
24655
+ "maximum": 2,
24656
+ "x-og-unit": "s",
24657
+ "x-og-tunable": true
24658
+ },
24659
+ "maxDistance": {
24660
+ "type": "number",
24661
+ "description": "Farther than this from where the animation puts the foot, the lock lets go (a leg cannot stretch).",
24662
+ "default": 0.5,
24663
+ "minimum": 0,
24664
+ "x-og-unit": "m",
24665
+ "x-og-tunable": true
24666
+ },
24667
+ "planted": {
24668
+ "type": "boolean",
24669
+ "description": "Written: the playing clips plant the foot this tick.",
24670
+ "default": false,
24671
+ "readOnly": true
24672
+ },
24673
+ "locked": {
24674
+ "type": "boolean",
24675
+ "description": "Written: the foot is held at its lock this tick.",
24676
+ "default": false,
24677
+ "readOnly": true
24678
+ }
24679
+ },
24680
+ "additionalProperties": false
24681
+ },
24682
+ "SpringSpace": {
24683
+ "x-og-kind": "enum",
24684
+ "x-og-module": "anim",
24685
+ "description": "Where a spring bone's tails move.",
24686
+ "type": "string",
24687
+ "enum": [
24688
+ "world",
24689
+ "model"
24690
+ ],
24691
+ "x-og-variants": {
24692
+ "world": "The frame the skeleton entity's og.Transform is written in: the chain lags behind the entity's motion (walking, root motion, a jump).",
24693
+ "model": "The skeleton's model space: only the animation moves the chain (a teleport or a vehicle carrying the entity does not)."
24694
+ }
24695
+ },
24696
+ "SpringBone": {
24697
+ "x-og-kind": "component",
24698
+ "x-og-module": "anim",
24699
+ "description": "A spring (jiggle) bone chain, three-vrm's VRMSpringBone: `joint` and its `bones` - 1 parents swing after their parents' motion. Each joint's tail (the next joint of the chain; for the last joint its first child, or its parent bone continued at the same length) is a point pulled towards where the animated pose puts it by a damped spring (`frequency`, `damping`), under `gravity`, held at the bone's length, and the joint turns to point at it. Applied after og.IkChain and og.LookAt, top joint first; put it on its own entity (a tail, an ear, a strand of hair, a single jiggling node), one per chain. Deterministic under the fixed tick; the tails' positions and velocities are world state (the `og.anim` state block), so a restore continues a swing exactly. A changed chain (another skeleton, length or space) restarts at the animated pose.",
24700
+ "type": "object",
24701
+ "properties": {
24702
+ "skeleton": {
24703
+ "$ref": "#/$defs/Entity",
24704
+ "description": "The entity with the og.Skeleton whose joints this swings.",
24705
+ "default": 0
24706
+ },
24707
+ "joint": {
24708
+ "type": "integer",
24709
+ "minimum": 0,
24710
+ "maximum": 4294967295,
24711
+ "description": "The chain's last joint (an index into the skin's joints): the tip of a tail, an ear.",
24712
+ "default": 0,
24713
+ "minimum": 0
24714
+ },
24715
+ "bones": {
24716
+ "type": "integer",
24717
+ "minimum": 0,
24718
+ "maximum": 4294967295,
24719
+ "description": "How many joints swing: `joint` and its parents (1 is the joint alone).",
24720
+ "default": 1,
24721
+ "minimum": 1,
24722
+ "maximum": 16
24723
+ },
24724
+ "frequency": {
24725
+ "type": "number",
24726
+ "description": "How fast a tail springs back to its animated place: the spring's natural frequency (0: no spring, it only hangs and swings).",
24727
+ "default": 3,
24728
+ "minimum": 0,
24729
+ "maximum": 30,
24730
+ "x-og-unit": "hz",
24731
+ "x-og-tunable": true
24732
+ },
24733
+ "damping": {
24734
+ "type": "number",
24735
+ "description": "The damping ratio: 0 swings on, 1 settles without overshooting (critical damping).",
24736
+ "default": 0.3,
24737
+ "minimum": 0,
24738
+ "maximum": 2,
24739
+ "x-og-tunable": true
24740
+ },
24741
+ "tipFrequency": {
24742
+ "type": "number",
24743
+ "description": "The last joint's frequency as a multiple of `frequency`, the joints between in proportion along the chain (1: every joint alike; 0.25: the tip springs back four times slower than the top, a soft strand on a stiff root).",
24744
+ "default": 1,
24745
+ "minimum": 0,
24746
+ "maximum": 4,
24747
+ "x-og-tunable": true
24748
+ },
24749
+ "tipDamping": {
24750
+ "type": "number",
24751
+ "description": "The last joint's damping ratio as a multiple of `damping`, the joints between in proportion along the chain (1: every joint alike).",
24752
+ "default": 1,
24753
+ "minimum": 0,
24754
+ "maximum": 4,
24755
+ "x-og-tunable": true
24756
+ },
24757
+ "gravity": {
24758
+ "$ref": "#/$defs/Vector3",
24759
+ "description": "An acceleration on every tail, in `space` (three-vrm's gravityDir times gravityPower): (0, -9.8, 0) makes a chain droop.",
24760
+ "default": {
24761
+ "x": 0,
24762
+ "y": 0,
24763
+ "z": 0
24764
+ },
24765
+ "x-og-unit": "mps2"
24766
+ },
24767
+ "space": {
24768
+ "$ref": "#/$defs/SpringSpace",
24769
+ "description": "Where the tails move: world (the frame the skeleton entity's og.Transform is in, so the chain lags behind the entity's motion) or model (only the animation moves it).",
24770
+ "default": "world"
24771
+ },
24772
+ "weight": {
24773
+ "type": "number",
24774
+ "description": "How much of the swing is applied (0 = the animated pose, 1 = the spring's).",
24775
+ "default": 1,
24776
+ "minimum": 0,
24777
+ "maximum": 1,
24778
+ "x-og-tunable": true
24779
+ },
24780
+ "radius": {
24781
+ "type": "number",
24782
+ "description": "The tails' radius against og.SpringCollider (three-vrm's hitRadius): a tail stays this far outside every collider it meets (0: a point).",
24783
+ "default": 0,
24784
+ "minimum": 0,
24785
+ "x-og-unit": "m",
24786
+ "x-og-tunable": true
24787
+ },
24788
+ "colliders": {
24789
+ "type": "integer",
24790
+ "minimum": 0,
24791
+ "maximum": 4294967295,
24792
+ "description": "The og.SpringCollider layers the tails are pushed out of, as bits: a collider on the same skeleton counts when its `layers` meet these.",
24793
+ "default": 4294967295
24794
+ },
24795
+ "lag": {
24796
+ "type": "number",
24797
+ "description": "Written: the farthest a tail is from its animated place this tick.",
24798
+ "default": 0,
24799
+ "x-og-unit": "m",
24800
+ "readOnly": true
24801
+ }
24802
+ },
24803
+ "additionalProperties": false
24804
+ },
24805
+ "SpringColliderShape": {
24806
+ "x-og-kind": "enum",
24807
+ "x-og-module": "anim",
24808
+ "description": "The shape of a spring collider.",
24809
+ "type": "string",
24810
+ "enum": [
24811
+ "sphere",
24812
+ "capsule"
24813
+ ],
24814
+ "x-og-variants": {
24815
+ "sphere": "A ball of `radius` about `offset`.",
24816
+ "capsule": "A capsule of `radius` about the segment from `offset` to `tail`."
24817
+ }
24818
+ },
24819
+ "SpringCollider": {
24820
+ "x-og-kind": "component",
24821
+ "x-og-module": "anim",
24822
+ "description": "A collider spring bones are kept out of, three-vrm's VRMSpringBoneCollider: a sphere or a capsule riding on a joint of a skeleton (a head, a chest, an upper arm), so hair, ears and tails do not swing through the body. Each tick, after a tail of an og.SpringBone of the same skeleton has stepped and before its joint turns, the tail is pushed out of every collider whose `layers` meet the spring's `colliders` (in query order) to `radius` plus the spring's `radius`, keeping its bone's length. Put it on its own entity, one per shape; colliders follow the pose as it is when a chain is swung, so one on a joint an earlier chain swung follows that swing.",
24823
+ "type": "object",
24824
+ "properties": {
24825
+ "skeleton": {
24826
+ "$ref": "#/$defs/Entity",
24827
+ "description": "The entity with the og.Skeleton whose spring bones this keeps out.",
24828
+ "default": 0
24829
+ },
24830
+ "joint": {
24831
+ "type": "integer",
24832
+ "minimum": 0,
24833
+ "maximum": 4294967295,
24834
+ "description": "The joint the collider rides on (an index into the skin's joints).",
24835
+ "default": 0,
24836
+ "minimum": 0
24837
+ },
24838
+ "shape": {
24839
+ "$ref": "#/$defs/SpringColliderShape",
24840
+ "description": "A sphere about `offset`, or a capsule from `offset` to `tail`.",
24841
+ "default": "sphere"
24842
+ },
24843
+ "offset": {
24844
+ "$ref": "#/$defs/Vector3",
24845
+ "description": "The sphere's centre, or the capsule's first end, in the joint's frame.",
24846
+ "default": {
24847
+ "x": 0,
24848
+ "y": 0,
24849
+ "z": 0
24850
+ },
24851
+ "x-og-unit": "m"
24852
+ },
24853
+ "tail": {
24854
+ "$ref": "#/$defs/Vector3",
24855
+ "description": "The capsule's other end, in the joint's frame (a sphere ignores it).",
24856
+ "default": {
24857
+ "x": 0,
24858
+ "y": 0,
24859
+ "z": 0
24860
+ },
24861
+ "x-og-unit": "m"
24862
+ },
24863
+ "radius": {
24864
+ "type": "number",
24865
+ "description": "The radius, in metres of the skeleton's model space (the joint's own scale does not apply).",
24866
+ "default": 0.1,
24867
+ "minimum": 0,
24868
+ "x-og-unit": "m",
24869
+ "x-og-tunable": true
24870
+ },
24871
+ "layers": {
24872
+ "type": "integer",
24873
+ "minimum": 0,
24874
+ "maximum": 4294967295,
24875
+ "description": "The layers this collider is on, as bits (a spring bone's `colliders` picks them).",
24876
+ "default": 1
24877
+ }
24878
+ },
24879
+ "additionalProperties": false
24880
+ },
22872
24881
  "AnimationEvent": {
22873
24882
  "x-og-kind": "event",
22874
24883
  "x-og-module": "anim",
@@ -23015,7 +25024,7 @@
23015
25024
  "Rig": {
23016
25025
  "x-og-kind": "handle",
23017
25026
  "x-og-module": "anim",
23018
- "description": "A humanoid map from og.load.rig (a cooked `.ogt` of `type rig`): the naming scheme a skeleton's node names follow and the standard bones it names outright.",
25027
+ "description": "A humanoid map from og.load.rig (a cooked `.ogt` of `type rig`): the naming scheme a skeleton's node names follow, the standard bones it names outright and, with `footHeight`, how high its ankles stand above the ground.",
23019
25028
  "type": "integer",
23020
25029
  "minimum": 0
23021
25030
  },
@@ -23027,7 +25036,7 @@
23027
25036
  "properties": {
23028
25037
  "bone": {
23029
25038
  "type": "string",
23030
- "description": "The standard bone: hips, spine, chest, upperChest, neck, head, then leftShoulder, leftUpperArm, leftLowerArm, leftHand, the right arm, leftUpperLeg, leftLowerLeg, leftFoot, leftToes and the right leg."
25039
+ "description": "The standard bone: hips, spine, chest, upperChest, neck, head, then leftShoulder, leftUpperArm, leftLowerArm, leftHand, the right arm, leftUpperLeg, leftLowerLeg, leftFoot, leftToes and the right leg, then each hand's fingers (VRM 1.0 names): leftThumbMetacarpal, leftThumbProximal, leftThumbDistal, leftIndexProximal, leftIndexIntermediate, leftIndexDistal, the same for Middle, Ring and Little, and the right hand's."
23031
25040
  },
23032
25041
  "node": {
23033
25042
  "type": "string",
@@ -23122,6 +25131,25 @@
23122
25131
  },
23123
25132
  "additionalProperties": false
23124
25133
  },
25134
+ "MirrorDesc": {
25135
+ "x-og-kind": "desc",
25136
+ "x-og-module": "anim",
25137
+ "description": "A clip to play on the other side of the skeleton it was made for.",
25138
+ "type": "object",
25139
+ "properties": {
25140
+ "clip": {
25141
+ "$ref": "#/$defs/ClipAsset",
25142
+ "description": "The clip, made for `skeleton`.",
25143
+ "default": 0
25144
+ },
25145
+ "skeleton": {
25146
+ "$ref": "#/$defs/SkeletonAsset",
25147
+ "description": "The skeleton the clip was made for (og.load.animation's `skeleton`); its left and right nodes are paired by name, the side tokens every rig naming scheme writes swapped (LeftArm and RightArm, thigh.L and thigh.R, J_Bip_L_ and J_Bip_R_, calf_l and calf_r).",
25148
+ "default": 0
25149
+ }
25150
+ },
25151
+ "additionalProperties": false
25152
+ },
23125
25153
  "FlipbookSheet": {
23126
25154
  "x-og-kind": "handle",
23127
25155
  "x-og-module": "anim",
@@ -23406,6 +25434,14 @@
23406
25434
  "description": "The sprite layer it draws in, sorted with the sprites there (after them when they share the layer).",
23407
25435
  "default": 0
23408
25436
  },
25437
+ "skin": {
25438
+ "type": "integer",
25439
+ "minimum": 0,
25440
+ "maximum": 4294967295,
25441
+ "description": "The skin it wears: an index into Rig2DInfo.skins (0 is the rig's default; `rig.skins.indexOf(\"armor\")` finds one by name). A skin shows its own attachment wherever the setup pose or a clip shows the default's; a slot the skin leaves alone shows the default's. Change it any tick: the pose is drawn from it.",
25442
+ "default": 0,
25443
+ "minimum": 0
25444
+ },
23409
25445
  "finished": {
23410
25446
  "type": "boolean",
23411
25447
  "description": "Written: a clip that does not loop reached its end (or its start, played backwards).",
@@ -23418,7 +25454,7 @@
23418
25454
  "Skeleton2DEvent": {
23419
25455
  "x-og-kind": "event",
23420
25456
  "x-og-module": "anim",
23421
- "description": "A 2D skeleton's clip passed one of its events (`event 0.2 \"step\"` in the rig) in the last tick: footsteps, the frame a swing lands. Read in the tick after, like every event; a clip started at 0 fires its events at 0, a looping clip fires each once per pass.",
25457
+ "description": "A 2D skeleton's clip, or the clip a child armature in one of its slots plays, passed one of its events (`event 0.2 \"step\"` in the rig) in the last tick: footsteps, the frame a swing lands. Read in the tick after, like every event; a clip started at 0 fires its events at 0, a looping clip fires each once per pass.",
23422
25458
  "type": "object",
23423
25459
  "properties": {
23424
25460
  "entity": {
@@ -23439,13 +25475,18 @@
23439
25475
  "type": "number",
23440
25476
  "description": "The event's time in the clip.",
23441
25477
  "x-og-unit": "s"
25478
+ },
25479
+ "armature": {
25480
+ "type": "string",
25481
+ "description": "Empty for the skeleton's own clip; for an event of a child armature shown in one of its slots, that armature's name (`clip` and `time` are then the child's clip and its time): a flame's crackle apart from the keeper's footsteps (at most 63 bytes)."
23442
25482
  }
23443
25483
  },
23444
25484
  "required": [
23445
25485
  "entity",
23446
25486
  "clip",
23447
25487
  "name",
23448
- "time"
25488
+ "time",
25489
+ "armature"
23449
25490
  ],
23450
25491
  "additionalProperties": false
23451
25492
  },
@@ -23589,6 +25630,13 @@
23589
25630
  },
23590
25631
  "description": "The IK constraints' names: an index here is IkTarget2D.constraint."
23591
25632
  },
25633
+ "skins": {
25634
+ "type": "array",
25635
+ "items": {
25636
+ "type": "string"
25637
+ },
25638
+ "description": "Its skins' names, `default` (the rig's own attachments) first: an index here is Skeleton2D.skin."
25639
+ },
23592
25640
  "clips": {
23593
25641
  "type": "array",
23594
25642
  "items": {
@@ -23604,6 +25652,7 @@
23604
25652
  "bones",
23605
25653
  "slots",
23606
25654
  "constraints",
25655
+ "skins",
23607
25656
  "clips"
23608
25657
  ],
23609
25658
  "additionalProperties": false
@@ -24271,6 +26320,34 @@
24271
26320
  "ui": "Interface sounds."
24272
26321
  }
24273
26322
  },
26323
+ "PanningModel": {
26324
+ "x-og-kind": "enum",
26325
+ "x-og-module": "audio",
26326
+ "description": "How an AudioListener places spatial sources between the ears, as Web Audio's PannerNode.panningModel.",
26327
+ "type": "string",
26328
+ "enum": [
26329
+ "equalpower",
26330
+ "hrtf"
26331
+ ],
26332
+ "x-og-variants": {
26333
+ "equalpower": "The stereo panner (Web Audio's equal-power law): for speakers.",
26334
+ "hrtf": "For headphones: a spherical-head model (Brown and Duda, 1998) that delays the far ear by the interaural time difference (up to 0.66 ms) and shadows its highs (up to -20 dB), so a source moves between the ears; front and back, up and down are not told apart (no pinna)."
26335
+ }
26336
+ },
26337
+ "AudioResampler": {
26338
+ "x-og-kind": "enum",
26339
+ "x-og-module": "audio",
26340
+ "description": "How the mixer resamples a voice whose pitch or sound rate differs from the output's.",
26341
+ "type": "string",
26342
+ "enum": [
26343
+ "linear",
26344
+ "sinc"
26345
+ ],
26346
+ "x-og-variants": {
26347
+ "linear": "Linear interpolation: the engine's mix before round 4, cheaper, with duller highs (-3.4 dB at 15 kHz from 44.1 kHz) and images (-11.5 dB there).",
26348
+ "sinc": "A Kaiser-windowed sinc (beta 7, 16 taps, 2048 phases; widened up to 64 taps for a voice pitched up to 4x): flat to the highs, images below -70 dB."
26349
+ }
26350
+ },
24274
26351
  "AudioSource": {
24275
26352
  "x-og-kind": "component",
24276
26353
  "x-og-module": "audio",
@@ -24417,6 +26494,50 @@
24417
26494
  "maximum": 30,
24418
26495
  "x-og-unit": "s",
24419
26496
  "x-og-tunable": true
26497
+ },
26498
+ "dopplerFactor": {
26499
+ "type": "number",
26500
+ "description": "Doppler: how much a spatial source's and the listener's motion along the line between them shifts its pitch, from their tick velocities (world positions down og.Parent): pitch times (c - DF * vListener) / (c - DF * vSource), c being Audio.speedOfSound and DF this times AudioListener.dopplerFactor (OpenAL's model). 1 is physical, 0 is off. A jump at or past the speed of sound in a tick is a teleport and shifts nothing; DF * v is held within c/2, so the shift stays within a factor of 3.",
26501
+ "default": 1,
26502
+ "minimum": 0,
26503
+ "maximum": 10,
26504
+ "x-og-tunable": true
26505
+ },
26506
+ "coneInnerAngle": {
26507
+ "type": "number",
26508
+ "description": "Directivity, as three.js PositionalAudio.setDirectionalCone and Web Audio's PannerNode: a spatial source plays at full volume while the listener is within this full angle about the source's +Z (its world transform's, three.js's PositionalAudio orientation); 360 with coneOuterAngle 360 is omnidirectional.",
26509
+ "default": 360,
26510
+ "minimum": 0,
26511
+ "maximum": 360,
26512
+ "x-og-unit": "deg",
26513
+ "x-og-tunable": true
26514
+ },
26515
+ "coneOuterAngle": {
26516
+ "type": "number",
26517
+ "description": "Past this full angle about the source's +Z it plays at coneOuterGain; between coneInnerAngle and this the gain falls linearly with the angle.",
26518
+ "default": 360,
26519
+ "minimum": 0,
26520
+ "maximum": 360,
26521
+ "x-og-unit": "deg",
26522
+ "x-og-tunable": true
26523
+ },
26524
+ "coneOuterGain": {
26525
+ "type": "number",
26526
+ "description": "The gain outside coneOuterAngle.",
26527
+ "default": 0,
26528
+ "minimum": 0,
26529
+ "maximum": 1,
26530
+ "x-og-tunable": true
26531
+ },
26532
+ "virtualize": {
26533
+ "type": "boolean",
26534
+ "description": "Past Audio.maxVoices it goes virtual instead of being culled: silent, its playhead running on (a one-shot still ends on its tick), and heard again from where it would be, fading in, once it stands within the cap. Without it the source is culled (SOUND_CULLED, playing false).",
26535
+ "default": false
26536
+ },
26537
+ "retrigger": {
26538
+ "type": "boolean",
26539
+ "description": "Set true to start the sound again from the top (its startOffset, its startAt) this tick, playing or not, without a level change on `playing`: the voice playing ends (SOUND_RETRIGGERED) and a new one starts in the same tick. The engine sets it back to false and `playing` true.",
26540
+ "default": false
24420
26541
  }
24421
26542
  },
24422
26543
  "additionalProperties": false
@@ -24434,6 +26555,19 @@
24434
26555
  "minimum": 0,
24435
26556
  "maximum": 4,
24436
26557
  "x-og-tunable": true
26558
+ },
26559
+ "dopplerFactor": {
26560
+ "type": "number",
26561
+ "description": "Scales every spatial source's Doppler shift (AudioSource.dopplerFactor); 0 turns Doppler off for this listener.",
26562
+ "default": 1,
26563
+ "minimum": 0,
26564
+ "maximum": 10,
26565
+ "x-og-tunable": true
26566
+ },
26567
+ "panningModel": {
26568
+ "$ref": "#/$defs/PanningModel",
26569
+ "description": "How spatial sources are placed between the ears: equalpower (speakers) or hrtf (headphones). Non-spatial sources keep their pan either way; the stereo path is unchanged while it is equalpower.",
26570
+ "default": "equalpower"
24437
26571
  }
24438
26572
  },
24439
26573
  "additionalProperties": false
@@ -24525,6 +26659,20 @@
24525
26659
  "minimum": 0,
24526
26660
  "x-og-unit": "s",
24527
26661
  "readOnly": true
26662
+ },
26663
+ "speedOfSound": {
26664
+ "type": "number",
26665
+ "description": "The speed of sound Doppler shifts are computed against (AudioSource.dopplerFactor).",
26666
+ "default": 343.3,
26667
+ "minimum": 1,
26668
+ "maximum": 10000,
26669
+ "x-og-unit": "mps",
26670
+ "x-og-tunable": true
26671
+ },
26672
+ "resampler": {
26673
+ "$ref": "#/$defs/AudioResampler",
26674
+ "description": "How voices are resampled: sinc (the default) or linear, the mix before round 4, for a game that relies on its exact output. A sound at the output's rate played at pitch 1 is its own samples either way.",
26675
+ "default": "sinc"
24528
26676
  }
24529
26677
  },
24530
26678
  "additionalProperties": false
@@ -24882,7 +27030,7 @@
24882
27030
  "type": "integer",
24883
27031
  "minimum": 0,
24884
27032
  "maximum": 4294967295,
24885
- "description": "Why: SOUND_FINISHED (0), SOUND_STOPPED (1), SOUND_REMOVED (2), SOUND_REPLACED (3) or SOUND_CULLED (4)."
27033
+ "description": "Why: SOUND_FINISHED (0), SOUND_STOPPED (1), SOUND_REMOVED (2), SOUND_REPLACED (3), SOUND_CULLED (4) or SOUND_RETRIGGERED (5)."
24886
27034
  }
24887
27035
  },
24888
27036
  "required": [
@@ -30525,22 +32673,6 @@
30525
32673
  ],
30526
32674
  "additionalProperties": false
30527
32675
  },
30528
- "GraphicsQuality": {
30529
- "x-og-kind": "enum",
30530
- "x-og-module": "ui",
30531
- "description": "A graphics quality preset.",
30532
- "type": "string",
30533
- "enum": [
30534
- "medium",
30535
- "low",
30536
- "high"
30537
- ],
30538
- "x-og-variants": {
30539
- "medium": "Medium.",
30540
- "low": "Low: the fewest effects.",
30541
- "high": "High: every effect."
30542
- }
30543
- },
30544
32676
  "MenuItem": {
30545
32677
  "x-og-kind": "desc",
30546
32678
  "x-og-module": "ui",
@@ -30860,6 +32992,18 @@
30860
32992
  "description": "Called when the screen closes after changes (persist here).",
30861
32993
  "default": null
30862
32994
  },
32995
+ "world": {
32996
+ "anyOf": [
32997
+ {
32998
+ "x-og-type": "World"
32999
+ },
33000
+ {
33001
+ "type": "null"
33002
+ }
33003
+ ],
33004
+ "description": "The world the graphics preset is drawn in: when the player picks another preset the screen calls og.render.applyQuality(world, preset) before onChange. Null leaves applying it to the game. Inside a system, the system declares RenderSettings among its writes.",
33005
+ "default": null
33006
+ },
30863
33007
  "title": {
30864
33008
  "type": "string",
30865
33009
  "description": "The dialog's title. Every word of the screen is a field with its English default: a localised game passes its string table's (og.i18n.t).",
@@ -35387,6 +37531,18 @@
35387
37531
  "castShadows": {
35388
37532
  "type": "boolean",
35389
37533
  "description": "Its terrain file's `castShadows`."
37534
+ },
37535
+ "seaLevel": {
37536
+ "anyOf": [
37537
+ {
37538
+ "type": "number"
37539
+ },
37540
+ {
37541
+ "type": "null"
37542
+ }
37543
+ ],
37544
+ "description": "A generated terrain's sea level (`generate { sea <m> }`) above its entity: place an og.Water there, the terrain's size; null without a sea.",
37545
+ "x-og-unit": "m"
35390
37546
  }
35391
37547
  },
35392
37548
  "required": [
@@ -35399,7 +37555,8 @@
35399
37555
  "maxHeight",
35400
37556
  "layers",
35401
37557
  "collision",
35402
- "castShadows"
37558
+ "castShadows",
37559
+ "seaLevel"
35403
37560
  ],
35404
37561
  "additionalProperties": false
35405
37562
  },