@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.
- package/dist/web/origin-engine.d.ts +44 -5
- package/dist/web/origin-engine.js +279 -19
- package/dist/web/origin-engine_bg.wasm +0 -0
- package/dist/web/origin-engine_bg.wasm.d.ts +18 -5
- package/dist/web-publish/origin-engine-lanes.js +289 -48
- package/dist/web-publish/origin-engine-lanes_bg.wasm +0 -0
- package/dist/web-publish/origin-engine.d.ts +44 -5
- package/dist/web-publish/origin-engine.js +279 -38
- package/dist/web-publish/origin-engine_bg.wasm +0 -0
- package/dist/web-publish/origin-engine_bg.wasm.d.ts +18 -5
- package/dist/web-publish-2d/origin-engine-lanes.js +289 -48
- package/dist/web-publish-2d/origin-engine-lanes_bg.wasm +0 -0
- package/dist/web-publish-2d/origin-engine.d.ts +44 -5
- package/dist/web-publish-2d/origin-engine.js +279 -38
- package/dist/web-publish-2d/origin-engine_bg.wasm +0 -0
- package/dist/web-publish-2d/origin-engine_bg.wasm.d.ts +18 -5
- package/knowledge/README.md +2 -1
- package/knowledge/guides/three-to-og.md +393 -0
- package/knowledge/knowledge.json +2 -1
- package/knowledge/looks/catalogue.json +123 -2
- package/knowledge/package.json +3 -2
- package/knowledge/scaffold/origin-engine.json +2 -2
- package/knowledge/scaffold/template/package.json +1 -1
- package/knowledge/scaffold/template/project.ogt +1 -1
- package/knowledge/scripts/validate.ts +209 -27
- package/knowledge/skills/og-audio/SKILL.md +33 -7
- package/knowledge/skills/og-build/SKILL.md +66 -7
- package/knowledge/skills/og-core/SKILL.md +24 -8
- package/knowledge/skills/og-feel/SKILL.md +11 -3
- package/knowledge/skills/og-game/SKILL.md +680 -51
- package/knowledge/skills/og-i18n/SKILL.md +7 -7
- package/knowledge/skills/og-model/SKILL.md +374 -0
- package/knowledge/skills/og-platform-sdk/SKILL.md +7 -1
- package/knowledge/skills/og-taste/SKILL.md +7 -0
- package/knowledge/skills/og-ui/SKILL.md +39 -8
- package/knowledge/wiki/ai.md +20 -20
- package/knowledge/wiki/anim.md +537 -63
- package/knowledge/wiki/atmosphere.md +13 -13
- package/knowledge/wiki/audio.md +160 -20
- package/knowledge/wiki/camera.md +13 -13
- package/knowledge/wiki/commands.md +11 -11
- package/knowledge/wiki/debug.md +13 -13
- package/knowledge/wiki/game.md +8 -8
- package/knowledge/wiki/i18n.md +7 -7
- package/knowledge/wiki/index.md +81 -3
- package/knowledge/wiki/input.md +47 -47
- package/knowledge/wiki/load.md +83 -57
- package/knowledge/wiki/math.md +102 -102
- package/knowledge/wiki/nav.md +34 -34
- package/knowledge/wiki/net.md +10 -10
- package/knowledge/wiki/path.md +14 -14
- package/knowledge/wiki/physics.md +111 -100
- package/knowledge/wiki/platform.md +44 -44
- package/knowledge/wiki/probe.md +86 -86
- package/knowledge/wiki/reflect.md +30 -18
- package/knowledge/wiki/render.md +1243 -176
- package/knowledge/wiki/rng.md +14 -14
- package/knowledge/wiki/shapes.md +727 -0
- package/knowledge/wiki/stream.md +15 -15
- package/knowledge/wiki/system.md +8 -8
- package/knowledge/wiki/systems.md +2 -2
- package/knowledge/wiki/terrain.md +24 -20
- package/knowledge/wiki/tilemap.md +13 -13
- package/knowledge/wiki/time.md +4 -4
- package/knowledge/wiki/timeline.md +14 -14
- package/knowledge/wiki/transform.md +8 -8
- package/knowledge/wiki/tween.md +17 -17
- package/knowledge/wiki/ui.md +245 -269
- package/knowledge/wiki/video.md +5 -5
- package/knowledge/wiki/world.md +36 -36
- package/package.json +4 -4
- package/schema/api.schema.json +2238 -81
- package/shell/page.ts +4 -0
- package/shell/platform.ts +5 -0
- package/shell/player.ts +8 -2
- package/shell/transport.ts +6 -0
- package/src/generated/abi.ts +40 -1
- package/src/geom.ts +260 -0
- package/src/index.ts +19 -1
- package/src/node/device.ts +5 -2
- package/src/node/playtest-run.ts +1 -0
- package/src/node/polish.ts +34 -15
- package/src/node/run.ts +23 -6
- package/src/platform-local.ts +2 -0
- package/src/playtest.ts +8 -0
- package/src/runtime.ts +88 -6
- package/src/ui-kit/settings.ts +9 -0
- package/src/web/gl-state.ts +907 -0
- package/src/web/shell.ts +1 -1
- package/src/web/worker.ts +8 -2
- package/types/bindings.json +1222 -453
- package/types/index.d.ts +1868 -315
|
@@ -116,6 +116,7 @@ Mistakes the games made, or would have, before reading the right page:
|
|
|
116
116
|
| **og-feel** | how much: hit stop, shake, squash, easing, floating text, the camera, frame rates, sorting, parallax, controller tunables | time, tween, camera, systems |
|
|
117
117
|
| **og-ui** | screens and HUDs: templates, widgets, focus, phones, UI styles and motion | ui |
|
|
118
118
|
| **og-taste** | the look: a style from the premise, composition, colour, type, motion, feedback, atmosphere by intent, the done checklist | — (it uses ui and atmosphere) |
|
|
119
|
+
| **og-model** | a 3D model built as code from a reference image: the detail inventory, eight gated passes, the generators' recipes (§18 has their fields), budgets, the review frames and `og compare` | — (it uses render and shapes) |
|
|
119
120
|
| **og-ai** | enemies and NPCs: navigation and minds as data | nav, ai |
|
|
120
121
|
| **og-audio** | sound: events, presets, UI sounds, the mix, music, space | audio |
|
|
121
122
|
| **og-i18n** | languages: string tables, messages, fonts, line breaking, right to left | i18n |
|
|
@@ -155,7 +156,14 @@ beside the rebuild: the reload still happens, the `reload` event carries a
|
|
|
155
156
|
and a `check` event with the diagnostics follows; `og test --json` ends
|
|
156
157
|
with a `test` event whose `check` object has the same three. Read the
|
|
157
158
|
diagnostics and fix them before anything else; `--no-check` skips the
|
|
158
|
-
check (never for `og build --publish`). A
|
|
159
|
+
check (never for `og build --publish`). A `2d` project's publish build
|
|
160
|
+
leaves the 3D world out (terrain, planar water, scattered copies, the 3D
|
|
161
|
+
fluids, volumetric fog: `og.render.capabilities(world).world3d` is false
|
|
162
|
+
there), so `og build --publish` refuses one whose scenes hold `Terrain`,
|
|
163
|
+
`TerrainScatter`, `Water`, `WaterSurface` or `Fluid3D`, naming each
|
|
164
|
+
`scene.ogt:line`, and there `og.terrain.*` and a `SHAPE_HEIGHTFIELD`
|
|
165
|
+
collider answer a refusal naming `world3d`: remove them, or make the
|
|
166
|
+
project `profile "3d"`. A created project's
|
|
159
167
|
`tsconfig.json` resolves `@origingame/engine` (the generated
|
|
160
168
|
`types/index.d.ts`) and `@origingame/engine/playtest` from `node_modules`.
|
|
161
169
|
An example inside the engine repository has no `node_modules`: its
|
|
@@ -205,6 +213,36 @@ export default og.game({
|
|
|
205
213
|
channel and playtests can call (`0.5:spawn_wave:3`), with their usage in
|
|
206
214
|
`GameDesc.commandHelp` for `help` (og-build §1). Add one for each state a
|
|
207
215
|
test needs to reach quickly, instead of playing there.
|
|
216
|
+
- **Arguments are colon-delimited**, the fields after the name, each a
|
|
217
|
+
string (`Number(args[0])` to read a number); a space is part of its
|
|
218
|
+
field, not a separator, and `\:` writes a colon inside one. Two
|
|
219
|
+
arguments, and the usage that says so:
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
commands: { records: (world, args) => setRecords(world, Number(args[0]), Number(args[1])) },
|
|
223
|
+
commandHelp: { records: { usage: "<berries>:<seconds>", doc: "Sets the isle's records." } },
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
`0.5:records:12:34.5` hands `args` `["12", "34.5"]`; `records:12 34.5`
|
|
227
|
+
would hand one argument, `"12 34.5"`.
|
|
228
|
+
- **Reserved names.** The channel's core actions are not a game's to
|
|
229
|
+
take, and the game refuses to start on one (`'pause' is a core action;
|
|
230
|
+
register under another name`): `screenshot`, `listen`, `touch_move_to`,
|
|
231
|
+
`touch_move`, `touch_start`, `touch_end`, `touch`, `scroll`, `gamepad`,
|
|
232
|
+
`key_down`, `key_up`, `type`, `compose`, `commit`, `move_to_text`,
|
|
233
|
+
`teleport`, `frame_entity`, `dump_world`, `dump_entity`, `dump_ui`,
|
|
234
|
+
`get`, `set`, `seed`, `pause`, `resume`, `advance`, `describe`,
|
|
235
|
+
`expect`, `save_scene`, `quit` and `help`. The actions the engine's
|
|
236
|
+
modules register are refused the same way (`'view' is a registered
|
|
237
|
+
engine action; register under another name`), in every build:
|
|
238
|
+
`bake_probes`, `cover`, `debug`, `dump_ai`, `dump_desync`,
|
|
239
|
+
`dump_lanes`, `dump_nav`, `dump_platform`, `dump_pulse`,
|
|
240
|
+
`dump_render`, `dump_room`, `dump_stream`, `lanes`, `locale`,
|
|
241
|
+
`terrain_stream`, `timeline`, `turntable` and `view` (`help` marks
|
|
242
|
+
them `(registered)`). The engine's `pause`
|
|
243
|
+
freezes the tick from outside the game; the game's own pause (its menu
|
|
244
|
+
open, `Game.paused` set) is a game action under its own name,
|
|
245
|
+
`open_pause`.
|
|
208
246
|
- **Facts** (`GameDesc.facts`) return a small JSON object for the probe's
|
|
209
247
|
`state()`: score, phase, lives. Tests assert on facts and dumps.
|
|
210
248
|
|
|
@@ -247,7 +285,19 @@ registered schema and refuses a model path that does not exist.
|
|
|
247
285
|
(ETC1S for colour, UASTC for normal and linear maps), WAV into QOA, fonts
|
|
248
286
|
into MSDF atlases, scenes into component tables. Import settings live next
|
|
249
287
|
to the source as `<file>.ogt` (`texture { kind normal }`,
|
|
250
|
-
`font { charset latin chars "开始游戏" }`).
|
|
288
|
+
`font { charset latin chars "开始游戏" }`). The prose here writes a
|
|
289
|
+
block on one line for short; in a file, as in every `.ogt`, a `{` ends
|
|
290
|
+
its key's line and a `}` stands alone on its own (the parser refuses
|
|
291
|
+
`'{' must end its line`):
|
|
292
|
+
|
|
293
|
+
```text
|
|
294
|
+
type import
|
|
295
|
+
version 1
|
|
296
|
+
---
|
|
297
|
+
texture {
|
|
298
|
+
kind normal
|
|
299
|
+
}
|
|
300
|
+
```
|
|
251
301
|
- At load a cooked texture reaches the GPU block-compressed in the device's
|
|
252
302
|
format with its mips (BC7, and BC5 for normal maps, on desktop GPUs; ASTC
|
|
253
303
|
on Apple and most Android; ETC2 and EAC elsewhere), a quarter of RGBA8's
|
|
@@ -280,9 +330,13 @@ registered schema and refuses a model path that does not exist.
|
|
|
280
330
|
A `.vox` with frames (MagicaVoxel's animation) cooks to clips that swap
|
|
281
331
|
the frames' models: play them as skinned clips (§9). `voxFps 8` sets the
|
|
282
332
|
rate (default 10), `voxClip walk 0 3` names frames 0 to 3 (repeatable;
|
|
283
|
-
without it one clip `frames` holds
|
|
333
|
+
without it one clip `frames` holds the playback range set in the editor,
|
|
334
|
+
else every frame; a node marked to loop repeats its keys over the file's
|
|
335
|
+
frames). Each frame is centred on
|
|
284
336
|
its own pivot, as in the editor: give the frames one height (or pad them)
|
|
285
337
|
for a character whose feet stay on the ground.
|
|
338
|
+
- An `.ogt` of `type model` is a model written as text: og-geom parts by
|
|
339
|
+
their `og.render` names, placed, with materials and pivots (§18).
|
|
286
340
|
- Reusable production assets come through the platform's asset catalog into
|
|
287
341
|
`assets/`; record each file's licence next to it.
|
|
288
342
|
|
|
@@ -380,7 +434,13 @@ else ignores it. The stamp is world state, so a replay reproduces it.
|
|
|
380
434
|
`flipX`, `flash`, `shadow`, nine-slice `sliceX`/`sliceY`) is drawn every
|
|
381
435
|
frame at its transform, its scale multiplying the size (so squash and
|
|
382
436
|
stretch is a tween of `og.Transform.scale`). `og.render.sprite` stays for
|
|
383
|
-
one-tick, immediate drawing (HUDs, debug).
|
|
437
|
+
one-tick, immediate drawing (HUDs, debug). Sprites and og.ui's frames
|
|
438
|
+
are filtered anisotropically where the device can
|
|
439
|
+
(`og.render.capabilities(world).spriteAnisotropy`: 16, or 1 on a WebGL2
|
|
440
|
+
without `EXT_texture_filter_anisotropic` and on a software rasterizer), so a sprite or a nine-slice
|
|
441
|
+
squeezed along one axis keeps the other axis's detail (a button's
|
|
442
|
+
outline stays sharp); at 1 it blurs, and og.ui bounds a frame's middle
|
|
443
|
+
at twice its art for that reason.
|
|
384
444
|
- **Pixel sprites in a 3D set** stand on the ground with `og.Sprite.dropShadow`
|
|
385
445
|
(or `SpriteDesc.dropShadow`): 0.5 casts the sprite's shape, darkened and
|
|
386
446
|
flattened onto the ground plane (y 0) along the sun, the first
|
|
@@ -393,6 +453,22 @@ else ignores it. The stamp is world state, so a replay reproduces it.
|
|
|
393
453
|
```ts
|
|
394
454
|
world.insert(hero, og.Sprite, { texture: tex, width: 1, height: 1.5, anchor: { x: 0.5, y: 0 }, dropShadow: 0.5, shadow: 0.3 });
|
|
395
455
|
```
|
|
456
|
+
- **Billboards** (three.js `Sprite`): `og.Sprite.facing` (or
|
|
457
|
+
`SpriteDesc.facing`) turns a world sprite to the camera that draws it.
|
|
458
|
+
`"cameraY"` stands it upright and turns it about y (trees, characters,
|
|
459
|
+
props a looking-down camera must not tip back); `"camera"` lays it in
|
|
460
|
+
the view plane (smoke, glints, signs); `"none"`, the default, keeps the
|
|
461
|
+
xy plane. Every camera turns it to itself (og.CameraView, og.Mirror, reflection
|
|
462
|
+
probes), the turn is render-only (never read by systems or snapshots),
|
|
463
|
+
faced sprites in one layer draw back to front from the camera and are
|
|
464
|
+
culled off screen, and a faced sprite's `dropShadow` is its whole shape
|
|
465
|
+
from any angle. It is still a sprite: flash, glow, blend, 2D lighting,
|
|
466
|
+
arrays, flipbooks and `toneMapped` all apply. A billboard behind a
|
|
467
|
+
transparent mesh still draws over it (sprites draw after the 3D scene).
|
|
468
|
+
|
|
469
|
+
```ts
|
|
470
|
+
world.insert(tree, og.Sprite, { texture: treeTex, width: 2, height: 2.6, anchor: { x: 0.5, y: 0 }, facing: "cameraY", dropShadow: 0.6 });
|
|
471
|
+
```
|
|
396
472
|
- **World UI that must not change with the light** (speech bubbles, name
|
|
397
473
|
tags, markers) takes `og.Sprite.toneMapped: false` (or
|
|
398
474
|
`SpriteDesc.toneMapped`), three.js's `Material.toneMapped`: still placed
|
|
@@ -699,6 +775,65 @@ strike, a bite or a fall at weight 1, back to 0 when `finished`.
|
|
|
699
775
|
Transform by the root joint's xz travel and yaw, `full` by all of it,
|
|
700
776
|
`off` (the default) leaves the hips travelling inside the model; switch it
|
|
701
777
|
to `planar` for a lunge's clip and back to `off` after.
|
|
778
|
+
**Look-at and joint limits.** `og.LookAt { skeleton, joint, target }`
|
|
779
|
+
on its own entity turns a joint (a head, an eye, a turret's barrel) so its
|
|
780
|
+
`axis` (default +Z, as three.js `lookAt`) points at `target` after the
|
|
781
|
+
clips and IK, its `upAxis` (+Y) rolling towards `up` (world +Y; zero
|
|
782
|
+
keeps the animated roll). `maxYaw` and `maxPitch` (radians from the
|
|
783
|
+
animated forward) hold a head from turning round, `clamped` reads back
|
|
784
|
+
when they did; `bones: 3` spreads the turn over neck, chest and spine;
|
|
785
|
+
`weight` fades it in and out. `og.JointLimit { skeleton, joint, axis,
|
|
786
|
+
swing, twistMin, twistMax }` (radians from the rest pose) limits what
|
|
787
|
+
`og.IkChain` and `og.LookAt` may turn a joint to: a knee is a hinge,
|
|
788
|
+
`axis` +X, `swing: 0.15`, `twistMin: 0`, `twistMax: 2.6`, so a
|
|
789
|
+
foot IK target never bends it backwards; a shoulder is a cone (`swing`).
|
|
790
|
+
The defaults limit nothing, and the clip's own pose is never clamped.
|
|
791
|
+
|
|
792
|
+
```ts
|
|
793
|
+
const look = must(world.spawn({ name: "hero-look" }));
|
|
794
|
+
must(world.insert(look, og.LookAt, { skeleton: body, joint: HEAD, bones: 2, maxYaw: 1.2, maxPitch: 0.5 }));
|
|
795
|
+
const knee = must(world.spawn({ name: "hero-knee-l" }));
|
|
796
|
+
must(world.insert(knee, og.JointLimit, { skeleton: body, joint: SHIN_L, axis: { x: 1, y: 0, z: 0 }, swing: 0.15, twistMin: 0, twistMax: 2.6 }));
|
|
797
|
+
// each tick, from a system that writes og.LookAt: world.set(look, og.LookAt, { target: playerPosition })
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
**Foot locking.** `og.FootLock { skeleton, foot }` on its own entity, one
|
|
801
|
+
per foot, keeps a foot the clips plant (within 5 mm of its lowest in the
|
|
802
|
+
clip, the same test retargeting uses) where it touched the ground while
|
|
803
|
+
root motion carries the body on and clips cross-fade, bending the leg
|
|
804
|
+
(two-bone IK, `og.JointLimit`s respected); when the clips lift it the
|
|
805
|
+
foot rejoins its animation over `release` (0.15 s). `planted` and
|
|
806
|
+
`locked` read back each tick; `maxDistance` (0.5 m) lets go before a leg
|
|
807
|
+
would stretch. It needs clips whose feet actually rest: a foot that only
|
|
808
|
+
grazes its lowest for a frame is held for that frame only.
|
|
809
|
+
|
|
810
|
+
**Spring bones.** `og.SpringBone { skeleton, joint, bones }` on its own
|
|
811
|
+
entity, one per chain (a tail, ears, a ponytail, a single jiggling node),
|
|
812
|
+
swings `joint` and its `bones` − 1 parents after their parents' motion:
|
|
813
|
+
each joint's tail is a damped spring (`frequency` 3 Hz, `damping` 0.3;
|
|
814
|
+
1 settles without overshoot) towards where the animation puts it, under
|
|
815
|
+
`gravity` (`{ x: 0, y: -9.8, z: 0 }` droops it), held at the bone's
|
|
816
|
+
length. `space` world (default) lags behind the entity's motion; model
|
|
817
|
+
ignores it (a teleport does not whip the tail). Applied last, after IK
|
|
818
|
+
and look-at; `lag` reads back how far the chain trails. Deterministic and
|
|
819
|
+
saved with the world. `tipFrequency` and `tipDamping` (1) scale the last
|
|
820
|
+
joint's frequency and damping, the joints between in proportion: 0.25 is
|
|
821
|
+
a strand soft at its tip on a stiff root.
|
|
822
|
+
|
|
823
|
+
Keep hair out of the head with `og.SpringCollider { skeleton, joint,
|
|
824
|
+
shape, offset, tail, radius }` on its own entity, a sphere about `offset`
|
|
825
|
+
or a capsule from `offset` to `tail` in the joint's frame: every tail of
|
|
826
|
+
that skeleton's springs is pushed out of it to its `radius` plus the
|
|
827
|
+
spring's `radius`, keeping its bone's length. A collider's `layers` (1)
|
|
828
|
+
must meet the spring's `colliders` (all) to count.
|
|
829
|
+
|
|
830
|
+
```ts
|
|
831
|
+
const tail = must(world.spawn({ name: "hero-tail" }));
|
|
832
|
+
must(world.insert(tail, og.SpringBone, { skeleton: body, joint: TAIL_TIP, bones: 3, gravity: { x: 0, y: -4, z: 0 }, tipFrequency: 0.5, radius: 0.02 }));
|
|
833
|
+
const hips = must(world.spawn({ name: "hero-hips-collider" }));
|
|
834
|
+
must(world.insert(hips, og.SpringCollider, { skeleton: body, joint: HIPS, shape: "capsule", offset: { x: -0.15, y: 0, z: 0 }, tail: { x: 0.15, y: 0, z: 0 }, radius: 0.12 }));
|
|
835
|
+
```
|
|
836
|
+
|
|
702
837
|
`templates/archetypes/third-person` (`src/anim.ts`: a 1D blend by speed,
|
|
703
838
|
a hero retargeted at cook time and sentinels at run time) is the pattern,
|
|
704
839
|
and for a 3D action game as a whole: a CharacterController over a level,
|
|
@@ -752,6 +887,7 @@ animation {
|
|
|
752
887
|
library "anims/humanoid-clips.glb" # its clips land in this model's #anim, retargeted (repeatable)
|
|
753
888
|
rig "rigs/hero.ogt" # how this model's bones are named (`type rig`); default: detected from the node names
|
|
754
889
|
footIk true # retargeting solves the legs so planted feet stay planted (default true)
|
|
890
|
+
mirror "turnLeft" "turnRight" # also write a clip mirrored to the other side, under that name (default "<clip>.mirrored"; repeatable)
|
|
755
891
|
compress false # quantised samples and keyframe reduction (default false: f32)
|
|
756
892
|
tolerance 0.0001 # the most compression may move a sampled value (default 0.0001, at most 0.1)
|
|
757
893
|
}
|
|
@@ -771,6 +907,34 @@ stride), and its legs are solved so planted feet stay on the ground
|
|
|
771
907
|
(`footIk`, on by default). The fx-showcase `retarget` station is the
|
|
772
908
|
pattern.
|
|
773
909
|
|
|
910
|
+
Hands retarget too: the fingers are humanoid bones (VRM 1.0's
|
|
911
|
+
`leftThumbMetacarpal` … `leftLittleDistal`, read from Mixamo's
|
|
912
|
+
`LeftHandIndex1`, VRoid's `J_Bip_L_Index1`, Rigify's `DEF-f_index.01.L`,
|
|
913
|
+
Unreal's `index_01_l`, plain `index.01.L`; Unity's legacy `Left Thumb
|
|
914
|
+
Proximal` is the metacarpal), each turning from its hand as
|
|
915
|
+
the source's does, so a fist or a grip holds on an A-posed arm whatever
|
|
916
|
+
the arm does. The target's twist bones (unmapped nodes named `twist`, such
|
|
917
|
+
as Unreal's `lowerarm_twist_01_l`, or Rigify's `DEF-forearm.L.001`) take a
|
|
918
|
+
share of their segment's roll by where they sit along it: none at the
|
|
919
|
+
segment's head, the next bone's at its end, so a forearm does not
|
|
920
|
+
candy-wrap. A clip made for the rig itself that leaves its twist bones
|
|
921
|
+
still gets the same shares each tick (after IK); a clip that keys them
|
|
922
|
+
is played as keyed. A character whose rest pose does not stand on the ground (an
|
|
923
|
+
armature lifted off it, a crouched rest) says how high its ankles stand
|
|
924
|
+
in its `type rig` file, `footHeight 0.08` (or `footHeight <left>
|
|
925
|
+
<right>`); planted feet then land on that ground, not at the rest pose's
|
|
926
|
+
foot height.
|
|
927
|
+
|
|
928
|
+
**Mirroring.** `og.anim.mirror(world, { clip, skeleton })` answers a new
|
|
929
|
+
clip that plays `clip` on the other side (a left-handed swing, a limp on
|
|
930
|
+
the other leg, a turn the other way): left and right nodes are paired by
|
|
931
|
+
the side tokens every naming scheme writes (`LeftArm`/`RightArm`,
|
|
932
|
+
`thigh.L`/`thigh.R`, `J_Bip_L_`/`J_Bip_R_`, `calf_l`/`calf_r`), each
|
|
933
|
+
node takes its counterpart's motion reflected across the skeleton's
|
|
934
|
+
x = 0 plane, root motion's sideways travel and turns flip. It assumes a
|
|
935
|
+
symmetric rest pose; mirror a retargeted clip on the skeleton it was
|
|
936
|
+
retargeted for.
|
|
937
|
+
|
|
774
938
|
**Compression** shrinks a model's clips in the bundle (not in memory: they
|
|
775
939
|
are rebuilt at load). In the same `animation` block, `compress true`
|
|
776
940
|
quantises rotations to 48 bits and the rest to 16 and drops the keyframes
|
|
@@ -800,6 +964,9 @@ clip walk "walk" {
|
|
|
800
964
|
speed 1 # duration multiplier, above 0 (default 1)
|
|
801
965
|
loop true # false: holds its last frame (default true)
|
|
802
966
|
}
|
|
967
|
+
clip walkOther "walk" {
|
|
968
|
+
mirror true # plays the clip on the other side of the skeleton (default false): no og.anim.mirror needed
|
|
969
|
+
}
|
|
803
970
|
clip run "run"
|
|
804
971
|
clip waving "wave"
|
|
805
972
|
blend1d move speed { # blend1d <motion> <param>: the two children around the value blend linearly, clamped at the ends
|
|
@@ -815,12 +982,14 @@ layer base { # the first layer is the base: root motion and og.An
|
|
|
815
982
|
state still idle # state <name> <motion>
|
|
816
983
|
state move move {
|
|
817
984
|
speed 1 # playback rate, 0 or more (default 1)
|
|
985
|
+
sync gait # a sync group: states of one group play in step across layers (default none)
|
|
818
986
|
}
|
|
819
987
|
entry still # the state it starts in (default: the first)
|
|
820
988
|
transition still move { # transition <from | any> <to>; in file order, the first that holds fires
|
|
821
989
|
when speed gt 0.2 # when <param> gt | ge | lt | le | eq | ne <number>; every `when` must hold
|
|
822
990
|
exit 0.9 # and the source has played this many cycles (a `when` or an `exit` is required)
|
|
823
991
|
fade 0.25 # cross-fade seconds (default 0.2)
|
|
992
|
+
interrupt any # what may cut this fade: any (default) | none (it completes first) | source (only the state being left) | next (only the new state's own)
|
|
824
993
|
}
|
|
825
994
|
transition any still { # any: from every state but the target
|
|
826
995
|
when speed le 0.2
|
|
@@ -829,7 +998,11 @@ layer base { # the first layer is the base: root motion and og.An
|
|
|
829
998
|
layer upper {
|
|
830
999
|
mode additive # override (default): replaces the pose below | additive: adds each clip's difference from its first frame
|
|
831
1000
|
weight wave # a number 0 to 1, or a parameter clamped to it (default 1)
|
|
832
|
-
|
|
1001
|
+
mask "spine" 0.5 # mask "<node>" [weight 0 to 1, default 1]: that node and every node under it (default: no mask, all 1)
|
|
1002
|
+
mask "arm.L" # a deeper line wins for its branch; nodes under no line take 0
|
|
1003
|
+
state wave waving {
|
|
1004
|
+
sync gait
|
|
1005
|
+
}
|
|
833
1006
|
}
|
|
834
1007
|
```
|
|
835
1008
|
|
|
@@ -837,7 +1010,16 @@ Each state advances one phase by `dt × AnimationGraph.speed × state speed
|
|
|
837
1010
|
/ duration`, the duration weighted across a blend's clips, so blended
|
|
838
1011
|
clips stay in step. Per tick and layer the transitions are checked first,
|
|
839
1012
|
then the phases advance, then the poses blend; a transition during a fade
|
|
840
|
-
replaces it
|
|
1013
|
+
replaces it unless its `interrupt` says otherwise (`none` for a
|
|
1014
|
+
committed attack or landing, `source` to let an idle re-target a start
|
|
1015
|
+
that has just begun). A layer's `mask` lines confine it to branches of the
|
|
1016
|
+
skeleton (by node name, the nearest named ancestor's weight; an upper-body
|
|
1017
|
+
layer is `mask "spine"`), so a wave or an aim plays over a walk without
|
|
1018
|
+
freezing the legs. In a sync group the first layer (file order) whose
|
|
1019
|
+
current state is in it leads: every later layer's state of that group
|
|
1020
|
+
plays at the leader's phase, whatever its clip's length, and a transition
|
|
1021
|
+
between two states of one group keeps the phase (a walk-to-run blend
|
|
1022
|
+
keeps the feet in step). `og.anim.graph` refuses a parse error naming the line, a
|
|
841
1023
|
clip name `clips` lacks, a parameter not declared and a motion that
|
|
842
1024
|
contains itself. Write the parameters from a system every tick
|
|
843
1025
|
(`world.set(body, og.AnimationGraph, { p0: speed })`); `state` and
|
|
@@ -980,7 +1162,9 @@ pixels then, per bone, `bone x y weight` in that bone's space (weights
|
|
|
980
1162
|
summing to 1), or plain `x y` in the slot's bone. A bone's or region's
|
|
981
1163
|
`skew` turns its y axis that many degrees past its x axis. A slot draws
|
|
982
1164
|
`texel × color + offset`. Bone keys add to the setup pose (`rotate` and
|
|
983
|
-
`skew` degrees, `translate` pixels, `scale` multiplies);
|
|
1165
|
+
`skew` degrees, `translate` pixels, `scale` multiplies); a `rotate` key
|
|
1166
|
+
turns the shorter way to the next, or `rotate 0 0 clockwise 1` (or
|
|
1167
|
+
`counterclockwise n`) that way through n more whole turns; every key eases
|
|
984
1168
|
linearly to the next, holds with `step`, or follows `curve x1 y1 x2 y2`
|
|
985
1169
|
(a cubic Bézier from 0,0 to 1,1, x time and y value; more segments as
|
|
986
1170
|
`x y x1 y1 x2 y2`). A `deform` key offsets every influence in order, one
|
|
@@ -988,15 +1172,26 @@ linearly to the next, holds with `step`, or follows `curve x1 y1 x2 y2`
|
|
|
988
1172
|
then (alone, the file's order); an `ik` key sets the constraint's weight
|
|
989
1173
|
and bend. An `ik`'s `bones` are a chain of any length, root first: one
|
|
990
1174
|
bone points, two bend at the joint, three or more (a tail, a tentacle, a
|
|
991
|
-
three-joint leg) are solved by FABRIK and curl to the `bend` side.
|
|
1175
|
+
three-joint leg) are solved by FABRIK and curl to the `bend` side. After the IK, a
|
|
1176
|
+
`transform t { bones a; target b; mix 1 1 1 1 }` pulls bones towards
|
|
1177
|
+
another bone's world rotation, origin, scale and shear by its mix, and an
|
|
1178
|
+
`along s { path rail; bones s1 s2; position 0 percent; spacing 0 length;
|
|
1179
|
+
rotate tangent 0; mix 1 1 }` lays bones along a `path` attachment (a
|
|
1180
|
+
cubic spline, `vertex x y` three per knot, drawn as nothing) while its
|
|
1181
|
+
slot shows it. A `skin armor { swap torso torso-armor; swap hat none }`
|
|
1182
|
+
block shows other attachments of the same slots wherever the default ones
|
|
1183
|
+
would show; `og.Skeleton2D.skin` picks it (`rig.skins.indexOf("armor")`,
|
|
1184
|
+
`default` is 0). A `box` draws nothing: `og.anim.skeleton2dBoxes(world, hero)`
|
|
992
1185
|
answers its polygon (in the Transform's frame) while its slot shows it,
|
|
993
1186
|
for hit and hurt boxes a clip switches. A `child` shows an `armature`
|
|
994
1187
|
block (a rig of its own on the same texture) in its slot; it plays the
|
|
995
1188
|
parent's clip by name when it has one, else its `clip`, at the parent's
|
|
996
|
-
time
|
|
1189
|
+
time; its events arrive as og.Skeleton2DEvent with `armature` its name
|
|
1190
|
+
(empty for the skeleton's own clip). A DragonBones 5.5 export is imported instead of written: a rig2d
|
|
997
1191
|
`.ogt` with `dragonbones "hero_ske.json"` and `atlas "hero_tex.json"`
|
|
998
1192
|
(skew, colour offsets, zOrder and IK timelines, armature displays,
|
|
999
|
-
bounding boxes
|
|
1193
|
+
bounding boxes, path displays and constraints, every skin, curves and `clockwise`
|
|
1194
|
+
turns included; Spine's files are not read, convert
|
|
1000
1195
|
them to this text).
|
|
1001
1196
|
|
|
1002
1197
|
```ts
|
|
@@ -1369,8 +1564,10 @@ render sprite 12
|
|
|
1369
1564
|
`underwaterDensity` and the surface from below shows the sky only
|
|
1370
1565
|
straight up (on by default; set the density to 0 to see through).
|
|
1371
1566
|
|
|
1372
|
-
**Terrain** (`reference/terrain.md
|
|
1373
|
-
|
|
1567
|
+
**Terrain** (`reference/terrain.md`, 3D projects only: a `2d` project's
|
|
1568
|
+
publish build has none, §2) is a height field cooked from a
|
|
1569
|
+
`type terrain` .ogt beside a 16-bit grey PNG (or an EXR in metres), or
|
|
1570
|
+
generated by the cook (below):
|
|
1374
1571
|
|
|
1375
1572
|
```text
|
|
1376
1573
|
type terrain
|
|
@@ -1381,9 +1578,23 @@ height -20 120 # metres
|
|
|
1381
1578
|
size 2048 2048 # metres along x and z, centred on the entity
|
|
1382
1579
|
control "island-control.png" # optional RGBA: layer 0 in R, 1 in G, 2 in B, 3 in A
|
|
1383
1580
|
heightBlend 0.15 # sharpen the blends by each albedo's alpha (its height)
|
|
1384
|
-
layer {
|
|
1385
|
-
|
|
1386
|
-
|
|
1581
|
+
layer { # a block's '}' stands alone on its line
|
|
1582
|
+
albedo "textures/grass.png"
|
|
1583
|
+
normal "textures/grass-n.png"
|
|
1584
|
+
tile 6
|
|
1585
|
+
slope 0 30
|
|
1586
|
+
}
|
|
1587
|
+
layer {
|
|
1588
|
+
albedo "textures/rock.png"
|
|
1589
|
+
tile 12
|
|
1590
|
+
triplanar true
|
|
1591
|
+
slope 28 90
|
|
1592
|
+
}
|
|
1593
|
+
layer {
|
|
1594
|
+
albedo "textures/snow.png"
|
|
1595
|
+
altitude 90 1000
|
|
1596
|
+
altitudeFade 10
|
|
1597
|
+
}
|
|
1387
1598
|
```
|
|
1388
1599
|
|
|
1389
1600
|
A layer shows where its control channel (1 without a map) times its slope
|
|
@@ -1398,6 +1609,151 @@ world.insert(ground, og.Terrain, { data: info.data });
|
|
|
1398
1609
|
world.insert(ground, og.Collider, { shape: SHAPE_HEIGHTFIELD, mesh: info.geometry });
|
|
1399
1610
|
```
|
|
1400
1611
|
|
|
1612
|
+
**A generated terrain.** Instead of `heightmap`, a `generate` block makes
|
|
1613
|
+
the field in the cook (never both: the cook refuses the pair and names
|
|
1614
|
+
the lines). `height` and `size` mean what they mean for a heightmap: the
|
|
1615
|
+
field is stretched so its lowest sample is `height`'s low and its highest
|
|
1616
|
+
its high, and no sample leaves that range. Every line, with its default
|
|
1617
|
+
(leave out what you do not need; erosion, thermal, terraces, rivers and
|
|
1618
|
+
the sea are off until given):
|
|
1619
|
+
|
|
1620
|
+
```text
|
|
1621
|
+
type terrain
|
|
1622
|
+
version 1
|
|
1623
|
+
---
|
|
1624
|
+
height -40 160 # metres
|
|
1625
|
+
size 1024 1024 # metres along x and z
|
|
1626
|
+
generate {
|
|
1627
|
+
samples 257 257 # columns and rows (2 to 4097)
|
|
1628
|
+
seed 1 # another seed, another terrain of the same kind
|
|
1629
|
+
noise {
|
|
1630
|
+
octaves 6 # 1 to 12: more is finer detail
|
|
1631
|
+
frequency 3 # features across the longer side
|
|
1632
|
+
lacunarity 2 # each octave's frequency over the last's (1 to 8)
|
|
1633
|
+
gain 0.5 # each octave's amplitude over the last's (0 to 1): higher is rougher
|
|
1634
|
+
warp 0 # domain warp in features: 0.3 to 1 bends ridges and coasts
|
|
1635
|
+
}
|
|
1636
|
+
mask island 0.4 # land fades to `height`'s low over this share of the half size
|
|
1637
|
+
# or `mask ridge 1`: sharp ridged mountains (0 to 1); default no mask
|
|
1638
|
+
erosion { # hydraulic erosion: seeded drops carve gullies and fill valleys
|
|
1639
|
+
drops 40000 # ~0.5 to 1 per sample; more is deeper erosion and a slower cook
|
|
1640
|
+
lifetime 30
|
|
1641
|
+
radius 3 # samples each drop erodes around it
|
|
1642
|
+
inertia 0.05
|
|
1643
|
+
capacity 4
|
|
1644
|
+
deposition 0.3
|
|
1645
|
+
erosion 0.3
|
|
1646
|
+
evaporation 0.01
|
|
1647
|
+
gravity 4
|
|
1648
|
+
}
|
|
1649
|
+
thermal { # slopes steeper than talus slump into scree
|
|
1650
|
+
talus 35 # degrees
|
|
1651
|
+
iterations 20
|
|
1652
|
+
rate 0.5
|
|
1653
|
+
}
|
|
1654
|
+
terraces {
|
|
1655
|
+
step 10 # metres between steps
|
|
1656
|
+
blend 0.4 # 0 hard steps, 1 smooth ramps
|
|
1657
|
+
}
|
|
1658
|
+
sea 0 # metres above the entity: og.terrain.load's seaLevel
|
|
1659
|
+
shore 2 # metres either side of the sea flattened into beaches (needs sea)
|
|
1660
|
+
rivers { # carved where the flow gathers, down to the sea or the edge
|
|
1661
|
+
threshold 400 # samples draining through before it is a river: lower, more rivers
|
|
1662
|
+
depth 2 # metres
|
|
1663
|
+
width 6 # metres
|
|
1664
|
+
}
|
|
1665
|
+
moisture { # what biomes read, 0 to 1
|
|
1666
|
+
flow 1 # weight of the gathered flow (wet in valleys and by rivers)
|
|
1667
|
+
scale 64 # samples draining through for half of it
|
|
1668
|
+
noise 0.5 # weight of a noise
|
|
1669
|
+
frequency 6
|
|
1670
|
+
}
|
|
1671
|
+
biome { # any number; each writes its layer's control channel
|
|
1672
|
+
layer 0 # required: the layer (0 to 3, one the terrain has)
|
|
1673
|
+
weight 1
|
|
1674
|
+
altitude -1000000 1000000 # metres above the entity
|
|
1675
|
+
altitudeFade 2
|
|
1676
|
+
slope 0 90 # degrees
|
|
1677
|
+
slopeFade 4
|
|
1678
|
+
moisture 0 1
|
|
1679
|
+
moistureFade 0.1
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
layer {
|
|
1683
|
+
albedo "textures/sand.png"
|
|
1684
|
+
tile 4
|
|
1685
|
+
}
|
|
1686
|
+
layer {
|
|
1687
|
+
albedo "textures/grass.png"
|
|
1688
|
+
tile 6
|
|
1689
|
+
}
|
|
1690
|
+
layer {
|
|
1691
|
+
albedo "textures/rock.png"
|
|
1692
|
+
tile 12
|
|
1693
|
+
triplanar true
|
|
1694
|
+
}
|
|
1695
|
+
```
|
|
1696
|
+
|
|
1697
|
+
An island with beaches, rivers and rock on its cliffs, for that file's
|
|
1698
|
+
three layers (a block's `{` ends its line):
|
|
1699
|
+
|
|
1700
|
+
```text
|
|
1701
|
+
generate {
|
|
1702
|
+
seed 7
|
|
1703
|
+
noise {
|
|
1704
|
+
warp 0.5
|
|
1705
|
+
}
|
|
1706
|
+
mask island 0.45
|
|
1707
|
+
erosion {
|
|
1708
|
+
drops 40000
|
|
1709
|
+
}
|
|
1710
|
+
sea 6
|
|
1711
|
+
shore 3
|
|
1712
|
+
rivers {
|
|
1713
|
+
threshold 300
|
|
1714
|
+
depth 2.5
|
|
1715
|
+
width 8
|
|
1716
|
+
}
|
|
1717
|
+
biome {
|
|
1718
|
+
layer 0
|
|
1719
|
+
altitude -1000 9
|
|
1720
|
+
}
|
|
1721
|
+
biome {
|
|
1722
|
+
layer 1
|
|
1723
|
+
altitude 9 1000
|
|
1724
|
+
slope 0 32
|
|
1725
|
+
}
|
|
1726
|
+
biome {
|
|
1727
|
+
layer 2
|
|
1728
|
+
slope 32 90
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
```
|
|
1732
|
+
|
|
1733
|
+
With `biome` rules the generator writes the control map (a terrain with
|
|
1734
|
+
biomes names no `control`); a channel is the most any of its layer's
|
|
1735
|
+
biomes gives a sample, its `weight` times its three bands. Without biomes
|
|
1736
|
+
there is no control map and the layers' own `slope` and `altitude`
|
|
1737
|
+
place them, as for a heightmap. The same file makes the same terrain on
|
|
1738
|
+
every machine (the generator uses only exact arithmetic and its own
|
|
1739
|
+
random numbers). It is a credible terrain of a kind, never a terrain from
|
|
1740
|
+
a picture: move the seed, the mask and the erosion until it reads right.
|
|
1741
|
+
`og cook --report` gives its height histogram, the share of land above
|
|
1742
|
+
the sea and the rivers' length, and writes its height and control maps
|
|
1743
|
+
as PNGs to `og-out/terrain-maps/` to look at.
|
|
1744
|
+
|
|
1745
|
+
Its sea is the water's level; the water is the terrain's size:
|
|
1746
|
+
|
|
1747
|
+
```ts
|
|
1748
|
+
if (info.seaLevel !== null) {
|
|
1749
|
+
const sea = must(world.spawn({ name: "sea", position: { x: 0, y: info.seaLevel, z: 0 } }));
|
|
1750
|
+
world.insert(sea, og.Water, { size: info.size, segments: 128 });
|
|
1751
|
+
}
|
|
1752
|
+
```
|
|
1753
|
+
|
|
1754
|
+
(on the terrain's entity's x and z, under its parent if it has one; an
|
|
1755
|
+
`og.WaterSurface` on the same entity makes the waves move and bodies float).
|
|
1756
|
+
|
|
1401
1757
|
It is drawn around the camera at finer detail nearby (casting and
|
|
1402
1758
|
receiving shadows, under the fog), placed by its entity's world transform
|
|
1403
1759
|
(turn or scale the entity and the terrain turns and scales with it), and
|
|
@@ -1479,16 +1835,24 @@ there. `world.set(e, og.Velocity, ...)` refuses on
|
|
|
1479
1835
|
an entity without one (a body in `start`, before its first step):
|
|
1480
1836
|
`world.insert` it there, which adds and sets it.
|
|
1481
1837
|
|
|
1482
|
-
`og.CollisionStarted` is read on the tick after the physics step
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1838
|
+
`og.CollisionStarted` is read on the tick after the physics step, when
|
|
1839
|
+
the bodies' `og.Velocity` already holds the solver's answer (they may
|
|
1840
|
+
already be parting), so never weigh a hit from og.Velocity. The event
|
|
1841
|
+
carries the hit itself, taken in the step that made the contact. `a` is
|
|
1842
|
+
the lower id, and `normal` points from `a` to `b`. `point` is the deepest
|
|
1843
|
+
contact point.
|
|
1844
|
+
`relativeVelocity` (m/s) is `a`'s velocity relative to `b` at `point` as
|
|
1845
|
+
that step began: each body's linear velocity plus its spin crossed with
|
|
1846
|
+
the point's offset from its centre of mass. A static collider counts as
|
|
1847
|
+
still, and a kinematic body moves at its velocity. Its part along
|
|
1848
|
+
`normal` is the closing speed:
|
|
1849
|
+
`Math.max(0, rv.x * n.x + rv.y * n.y + rv.z * n.z)`, which is positive
|
|
1850
|
+
while they approach. `impulse` (N·s, at least 0) is the normal impulse the
|
|
1851
|
+
solver gave the pair in that step, summed over substeps and contact points:
|
|
1852
|
+
`b` took `impulse` along `normal` and `a` the same against it, so heavier
|
|
1853
|
+
bodies hitting give more. Scale feedback on the closing speed (how fast)
|
|
1854
|
+
or the impulse (how hard, mass included), as
|
|
1855
|
+
`examples/rumpus-isle/src/critters.ts` does for its bumps.
|
|
1492
1856
|
|
|
1493
1857
|
```ts
|
|
1494
1858
|
const hit = og.physics.raycast(world, { origin: eye, direction: { x: 0, y: 0, z: -1 }, maxDistance: 30, filter: { mask: 0b10, exclude: player } });
|
|
@@ -1747,6 +2111,35 @@ og.render.look(world)?.name; // the
|
|
|
1747
2111
|
stays sharper under camera motion and `taaSharpen` (0–1) restores
|
|
1748
2112
|
detail TAA softens. `RenderCapabilities.antialias` says what the device
|
|
1749
2113
|
resolved it to.
|
|
2114
|
+
- **Graphics quality** is RenderSettings' quality fields, read every
|
|
2115
|
+
frame: `rectShadows`, `shadowedLights`, `shadowMapScale`,
|
|
2116
|
+
`volumetricFog`, `softParticles`, `screenSpaceReflections`,
|
|
2117
|
+
`ambientOcclusion`, `msaaSamples` (4, or 1 for FXAA where the
|
|
2118
|
+
antialiasing multisamples). `og.render.applyQuality(world, preset)`
|
|
2119
|
+
writes a preset's row of them and records it in
|
|
2120
|
+
`RenderSettings.quality` (`none` before any):
|
|
2121
|
+
|
|
2122
|
+
| Field | low | medium | high |
|
|
2123
|
+
|---|---|---|---|
|
|
2124
|
+
| rectShadows | false | true | true |
|
|
2125
|
+
| shadowedLights | 2 | 8 | 8 |
|
|
2126
|
+
| shadowMapScale | 0.5 | 1 | 2 |
|
|
2127
|
+
| volumetricFog, softParticles, screenSpaceReflections, ambientOcclusion | false | true | true |
|
|
2128
|
+
| msaaSamples | 1 | 4 | 4 |
|
|
2129
|
+
|
|
2130
|
+
Medium is the defaults, the frame drawn before any preset. The renderer
|
|
2131
|
+
reads the fields and nothing else, so `og run --commands
|
|
2132
|
+
"0.5:get:og.RenderSettings"` shows what is drawn, and a field set after
|
|
2133
|
+
the preset wins. The switches only take away: the scene's PostEffects
|
|
2134
|
+
and lights keep their settings. The settings screen applies the
|
|
2135
|
+
player's pick when given the world (`og.ui.settingsScreen(id, { world,
|
|
2136
|
+
settings, ... })`); apply a saved preset yourself at start:
|
|
2137
|
+
|
|
2138
|
+
```ts
|
|
2139
|
+
start(world) { og.render.applyQuality(world, save.settings.quality ?? "medium"); }
|
|
2140
|
+
```
|
|
2141
|
+
|
|
2142
|
+
Inside a system, declare `og.RenderSettings` among its writes.
|
|
1750
2143
|
|
|
1751
2144
|
## 17. Saving progress
|
|
1752
2145
|
|
|
@@ -1891,6 +2284,201 @@ spaced along the length, as three.js spaces them. Rings and circles are one-side
|
|
|
1891
2284
|
standard material draws the triangle edges instead of the faces, one pixel
|
|
1892
2285
|
wide on every tier.
|
|
1893
2286
|
|
|
2287
|
+
**Shapes, extrusions, lathes and solids.** Three.js's modelling classes
|
|
2288
|
+
keep their names, parameters and defaults, so a three.js extrusion is a
|
|
2289
|
+
paste. `og.Shape` and `og.Path2` are three.js's Shape and Path:
|
|
2290
|
+
`moveTo`, `lineTo`, `quadraticCurveTo`, `bezierCurveTo`, `splineThru`,
|
|
2291
|
+
`arc`, `absarc`, `ellipse`, `absellipse`, `closePath`, `holes` and
|
|
2292
|
+
`getPoints(divisions)`. They flatten to points before they cross to the
|
|
2293
|
+
engine, so a points array works wherever a shape does
|
|
2294
|
+
(`reference/shapes.md`).
|
|
2295
|
+
- `shapeGeometry` is a flat shape with holes, one-sided like a circle.
|
|
2296
|
+
- `extrudeGeometry` pushes shapes along +Z (`depth`, `steps`) or along an
|
|
2297
|
+
og.path (`extrudePath`). `bevelEnabled`, `bevelThickness`, `bevelSize`,
|
|
2298
|
+
`bevelOffset` and `bevelSegments` round its edges, as in three.js.
|
|
2299
|
+
- `latheGeometry` turns a profile of `points` round +Y (`segments`,
|
|
2300
|
+
`phiStart`, `phiLength`), for vases, bottles and columns.
|
|
2301
|
+
- `polyhedronGeometry` takes `vertices`, `indices`, `radius` and `detail`.
|
|
2302
|
+
`icosahedronGeometry`, `dodecahedronGeometry`, `octahedronGeometry` and
|
|
2303
|
+
`tetrahedronGeometry` take `radius` and `detail`.
|
|
2304
|
+
- `sweepGeometry` sweeps a 2D `section` along an og.path. Its `scale` and
|
|
2305
|
+
`twist` are keyed over the path's length from 0 to 1, for horns, tusks,
|
|
2306
|
+
handles and twisted columns.
|
|
2307
|
+
- `mergeGeometries` places `parts` (each a geometry with `position`,
|
|
2308
|
+
`quaternion`, `scale` and a `mirror` axis) into one draw. Write an
|
|
2309
|
+
array as the same part at several placements.
|
|
2310
|
+
- `csgGeometry` is `union`, `subtract` or `intersect` of two placed
|
|
2311
|
+
closed geometries. Chain calls for more operands.
|
|
2312
|
+
|
|
2313
|
+
Every result is a geometry like `bufferGeometry`'s, with a BVH, `physics`
|
|
2314
|
+
and `dynamic`, and answered by `geometryInfo`:
|
|
2315
|
+
|
|
2316
|
+
```ts
|
|
2317
|
+
const gear = new og.Shape().absarc(0, 0, 1, 0, Math.PI * 2, false);
|
|
2318
|
+
gear.holes.push(new og.Path2().absarc(0, 0, 0.3, 0, Math.PI * 2, true));
|
|
2319
|
+
const disc = must(og.render.extrudeGeometry(world, {
|
|
2320
|
+
shapes: gear, depth: 0.2, bevelEnabled: true, bevelThickness: 0.05, bevelSize: 0.05,
|
|
2321
|
+
}));
|
|
2322
|
+
const vase = must(og.render.latheGeometry(world, {
|
|
2323
|
+
points: [{ x: 0, y: 0 }, { x: 0.5, y: 0.1 }, { x: 0.3, y: 1 }, { x: 0.4, y: 1.4 }],
|
|
2324
|
+
segments: 32,
|
|
2325
|
+
}));
|
|
2326
|
+
const cube = must(og.render.boxGeometry(world, { width: 1, height: 1, depth: 1 }));
|
|
2327
|
+
const ball = must(og.render.sphereGeometry(world, { radius: 0.15 }));
|
|
2328
|
+
const dice = must(og.render.csgGeometry(world, {
|
|
2329
|
+
op: "subtract", a: { geometry: cube }, b: { geometry: ball, position: { x: 0, y: 0, z: 0.5 } },
|
|
2330
|
+
}));
|
|
2331
|
+
```
|
|
2332
|
+
|
|
2333
|
+
The generators and the CSG decide every cut with exact predicates and use
|
|
2334
|
+
no platform trigonometry, so every host makes the same triangles. A CSG
|
|
2335
|
+
operand must be closed (every edge shared by two triangles). Its cost
|
|
2336
|
+
grows with the product of the two triangle counts where their boxes
|
|
2337
|
+
overlap, so cut low-poly operands at load, not every tick.
|
|
2338
|
+
|
|
2339
|
+
Every generator above takes `uvRepeat` as `boxGeometry` does (its uvs
|
|
2340
|
+
scaled, so a texture tiles). `bevelSize` left out (or null) is
|
|
2341
|
+
`bevelThickness - 0.1`, as in three.js: 0.1 at the default thickness.
|
|
2342
|
+
|
|
2343
|
+
**Models written as text: `type model`.** A prop made of these parts
|
|
2344
|
+
belongs in an asset, not in `start`: an `.ogt` of `type model` under
|
|
2345
|
+
`assets/` names each part's geometry by its `og.render` call, with the
|
|
2346
|
+
desc's field names and defaults, places it as `mergeGeometries` places a
|
|
2347
|
+
part, gives it a material and hangs it from a named pivot. `og cook`
|
|
2348
|
+
builds it into the same model blob a glTF cooks to: meshopt, levels of
|
|
2349
|
+
detail with their measured errors, the raycast BVH, the nodes. Load it
|
|
2350
|
+
with `og.load.gltf(world, "models/clockwork.ogt")` and `world.instantiate`,
|
|
2351
|
+
or name it in a scene (`model "models/clockwork.ogt"`), as any model.
|
|
2352
|
+
|
|
2353
|
+
```
|
|
2354
|
+
type model
|
|
2355
|
+
version 1
|
|
2356
|
+
---
|
|
2357
|
+
material "brass" {
|
|
2358
|
+
color 0.78 0.52 0.16 # linear, as glTF's baseColorFactor
|
|
2359
|
+
metalness 1
|
|
2360
|
+
roughness 0.32
|
|
2361
|
+
}
|
|
2362
|
+
material "glaze" {
|
|
2363
|
+
color 0.12 0.34 0.72
|
|
2364
|
+
roughness 0.3
|
|
2365
|
+
}
|
|
2366
|
+
pivot "axle" { # a node: code and animation find it by name
|
|
2367
|
+
position 0 1.25 0
|
|
2368
|
+
}
|
|
2369
|
+
part "gear" {
|
|
2370
|
+
pivot "axle" # hangs from the pivot (default: the root)
|
|
2371
|
+
material "brass"
|
|
2372
|
+
position 0 0 -0.1
|
|
2373
|
+
extrudeGeometry {
|
|
2374
|
+
shape { # og.Shape's calls, one a line
|
|
2375
|
+
moveTo 1 0
|
|
2376
|
+
absarc 0 0 1 0 6.283185307179586 false
|
|
2377
|
+
hole {
|
|
2378
|
+
absarc 0 0 0.28 0 6.283185307179586 true
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
depth 0.16
|
|
2382
|
+
bevelThickness 0.04
|
|
2383
|
+
bevelSize 0.03
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
part "bolt" { # one mesh under three nodes: drawn instanced
|
|
2387
|
+
pivot "axle"
|
|
2388
|
+
material "brass"
|
|
2389
|
+
cylinderGeometry {
|
|
2390
|
+
radiusTop 0.06
|
|
2391
|
+
radiusBottom 0.06
|
|
2392
|
+
height 0.36
|
|
2393
|
+
}
|
|
2394
|
+
at {
|
|
2395
|
+
position 0.55 0 0
|
|
2396
|
+
quaternion 0.7071068 0 0 0.7071068
|
|
2397
|
+
}
|
|
2398
|
+
at {
|
|
2399
|
+
position -0.275 0.476 0
|
|
2400
|
+
quaternion 0.7071068 0 0 0.7071068
|
|
2401
|
+
}
|
|
2402
|
+
at {
|
|
2403
|
+
position -0.275 -0.476 0
|
|
2404
|
+
quaternion 0.7071068 0 0 0.7071068
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
part "vase" {
|
|
2408
|
+
material "glaze"
|
|
2409
|
+
position -2.2 0 0
|
|
2410
|
+
latheGeometry {
|
|
2411
|
+
points 0 0 0.32 0.04 0.42 0.4 0.24 0.95 0.3 1.25
|
|
2412
|
+
segments 40
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
part "horn" {
|
|
2416
|
+
position 2.2 0 0
|
|
2417
|
+
sweepGeometry {
|
|
2418
|
+
section {
|
|
2419
|
+
absarc 0 0 0.22 0 6.283185307179586 false
|
|
2420
|
+
}
|
|
2421
|
+
path { # og.path's lines: kind, closed, tension, point
|
|
2422
|
+
kind catmullRom
|
|
2423
|
+
point 0 0 0
|
|
2424
|
+
point 0.15 0.6 0
|
|
2425
|
+
point -0.45 1.35 0.15
|
|
2426
|
+
}
|
|
2427
|
+
scale 0 1 # t value, repeatable (twist the same)
|
|
2428
|
+
scale 1 0.05
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
part "dice" {
|
|
2432
|
+
position 1.1 0.3 0.55
|
|
2433
|
+
csgGeometry {
|
|
2434
|
+
op subtract
|
|
2435
|
+
a { # an operand: a geometry block, placed
|
|
2436
|
+
boxGeometry {
|
|
2437
|
+
width 0.6
|
|
2438
|
+
height 0.6
|
|
2439
|
+
depth 0.6
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
b {
|
|
2443
|
+
position 0 0 0.3
|
|
2444
|
+
icosahedronGeometry {
|
|
2445
|
+
radius 0.08
|
|
2446
|
+
detail 1
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
```
|
|
2452
|
+
|
|
2453
|
+
- Geometries: `boxGeometry`, `planeGeometry`, `sphereGeometry`,
|
|
2454
|
+
`cylinderGeometry`, `coneGeometry`, `capsuleGeometry`, `torusGeometry`,
|
|
2455
|
+
`shapeGeometry`, `extrudeGeometry` (`extrudePath { }` a path block),
|
|
2456
|
+
`latheGeometry` (`points x y x y ...`), `polyhedronGeometry`, the four
|
|
2457
|
+
solids, `sweepGeometry`, `csgGeometry` (`a { }`, `b { }`) and
|
|
2458
|
+
`mergeGeometries` (`part { }` blocks), each with `uvRepeat u v`. A
|
|
2459
|
+
part holds exactly one; combine more with `csgGeometry` or
|
|
2460
|
+
`mergeGeometries`.
|
|
2461
|
+
- Shapes: `moveTo`, `lineTo`, `quadraticCurveTo`, `bezierCurveTo`,
|
|
2462
|
+
`splineThru x y ...`, `arc`, `absarc`, `ellipse`, `absellipse` (angles in
|
|
2463
|
+
radians, then `true` for clockwise), `closePath`, `points x y ...`
|
|
2464
|
+
(setFromPoints) and `hole { }`. They flatten to the points og.Shape
|
|
2465
|
+
gives in TypeScript, bit for bit.
|
|
2466
|
+
- Placement: `position`, `quaternion x y z w`, `scale`, `mirror x|y|z`,
|
|
2467
|
+
on the part or in `at { }` blocks, one node each. The same part at
|
|
2468
|
+
several placements is one mesh drawn instanced; a mirrored placement is
|
|
2469
|
+
baked into a mesh of its own.
|
|
2470
|
+
- Materials: `color`, `opacity`, `metalness` (default 0), `roughness`
|
|
2471
|
+
(default 1), `emissive`, `emissiveIntensity`, `doubleSided`,
|
|
2472
|
+
`alphaTest`. A part without one draws glTF's grey default.
|
|
2473
|
+
- `og cook --report` lists the model's `triangles`, `parts`, `draws` (the
|
|
2474
|
+
distinct meshes; instanced placements are one) and `bvhBytes`/`bvhShare`.
|
|
2475
|
+
Past `budgets.triangles` or `budgets.vertices` in project.ogt the cook
|
|
2476
|
+
refuses it, so lower `segments`, `curveSegments`, `steps` or `detail`.
|
|
2477
|
+
Import settings (`models/clockwork.ogt.ogt`, `model { lods ... raycast
|
|
2478
|
+
false }`) apply as to a glTF.
|
|
2479
|
+
- `og dev` re-cooks it on an edit and the running game draws the edit
|
|
2480
|
+
under the same asset handle.
|
|
2481
|
+
|
|
1894
2482
|
**Lines and points.** `og.render.lines(world, desc)` and
|
|
1895
2483
|
`og.render.points(world, desc)` are immediate mode, like
|
|
1896
2484
|
`og.render.sprite`: submit them every tick they should show.
|
|
@@ -2154,6 +2742,10 @@ These are the checks to use before opening frames. All of them run from
|
|
|
2154
2742
|
`totalBytes` 64 MiB, `soundSeconds` 600, `videoSeconds` 300,
|
|
2155
2743
|
`fontGlyphs` 8192, `navPolygons` 65536, `sceneEntities` 10000 and
|
|
2156
2744
|
`emitterParticles` 16384, each a default a project may move.
|
|
2745
|
+
`bvhShare` 30 is a warning, not a limit: a model whose cooked raycast
|
|
2746
|
+
BVH is over 30 % of its blob (and at least 16 KiB) is named by `og cook
|
|
2747
|
+
--report` and `og doctor`; write `model { raycast false }` in its .ogt
|
|
2748
|
+
when nothing raycasts against it.
|
|
2157
2749
|
|
|
2158
2750
|
## 22. Levels of detail, reflection probes and screen-space reflections
|
|
2159
2751
|
|
|
@@ -2172,15 +2764,21 @@ model {
|
|
|
2172
2764
|
}
|
|
2173
2765
|
```
|
|
2174
2766
|
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2767
|
+
The cook measures each level's error, the largest distance between the
|
|
2768
|
+
level and the full mesh in the model's units, and `og cook --report`
|
|
2769
|
+
lists each model's triangles (`lods`) and error (`lodErrors`) per level.
|
|
2770
|
+
The renderer draws each `og.Model` entity at the coarsest level whose
|
|
2771
|
+
error covers less than a pixel at the entity's distance, with
|
|
2772
|
+
hysteresis, so nothing flickers at a boundary: a small prop keeps its
|
|
2773
|
+
full mesh until it is far away, a large tree drops levels sooner. A
|
|
2774
|
+
level whose error is too large for its purpose (a hard-surface model
|
|
2775
|
+
melting at a distance) is cooked tighter with `lodError`, or the model
|
|
2776
|
+
keeps more detail with `lodBias`. `og.Model.lodBias` (per entity) and
|
|
2777
|
+
`RenderSettings.lodBias` (the frame) scale that choice: 2 keeps the full
|
|
2778
|
+
mesh twice as far, and 0 always draws it. Check the choice with
|
|
2779
|
+
`debug:lod`, which tints models green, yellow, orange and red by level,
|
|
2780
|
+
with `dump_entity:<name>` (`render.lod`), and with the triangles in
|
|
2781
|
+
`ProbeTimings.triangles` or `dump_render`.
|
|
2184
2782
|
|
|
2185
2783
|
**Reflection probes.** Smooth surfaces reflect the environment map, which
|
|
2186
2784
|
is the sky. Indoors that is wrong: a chrome sphere in a room would show
|
|
@@ -2212,8 +2810,11 @@ world.insert(probe, og.ReflectionProbe, {
|
|
|
2212
2810
|
the interval high.
|
|
2213
2811
|
- Size the box to the room. Each object takes the smallest box holding
|
|
2214
2812
|
its centre, so two rooms that share a model each reflect their own.
|
|
2215
|
-
The capture shows everything in the room: characters in their pose
|
|
2216
|
-
|
|
2813
|
+
The capture shows everything in the room: characters in their pose
|
|
2814
|
+
(and each one takes the probe of the room its pose stands in), terrain,
|
|
2815
|
+
grass, water (refracting what lies below it), 3D fluids, glass,
|
|
2816
|
+
particles (soft ones fading as in the frame), lines and world sprites
|
|
2817
|
+
and text. Terrain, grass and water inside the box are lit
|
|
2217
2818
|
by it too. At most 8 probes (`MAX_REFLECTION_PROBES`) are used,
|
|
2218
2819
|
nearest the camera first.
|
|
2219
2820
|
- `dump_render` lists the frame's probes and whether each was loaded from
|
|
@@ -2261,7 +2862,8 @@ default) or `hdr`:
|
|
|
2261
2862
|
`depth: true` also keeps the view's linear depth, from near (0) to far
|
|
2262
2863
|
(1), of its opaque meshes, on every tier, WebGL2 included. It costs a
|
|
2263
2864
|
second draw of the view's opaque meshes, so ask for it only when a
|
|
2264
|
-
material reads it.
|
|
2865
|
+
material reads it. (A scene with soft particles, `softDistance` above 0,
|
|
2866
|
+
pays that draw in every view anyway: its particles fade against it.)
|
|
2265
2867
|
`RenderCapabilities.maxRenderTargetSize` is the largest side.
|
|
2266
2868
|
|
|
2267
2869
|
**A second camera.** `og.CameraView` on an entity is a camera with
|
|
@@ -2285,16 +2887,18 @@ world.insert(p2, og.CameraView, { viewportX: 0.5, viewportWidth: 0.5 });
|
|
|
2285
2887
|
A view draws what the main camera draws, from its own eye and on its own
|
|
2286
2888
|
`layers`: meshes and models (skinned meshes and custom shaders included),
|
|
2287
2889
|
opaque then transparent back to front, the terrain and the sky, particle
|
|
2288
|
-
systems, og.Lines and og.Points, and world sprites and text, lit and
|
|
2890
|
+
systems, trails, og.Lines and og.Points, and world sprites and text, lit and
|
|
2289
2891
|
shadowed by the frame's lights. Each view is a second draw of everything
|
|
2290
2892
|
its layers admit, so keep it cheap with `layers` and `every`.
|
|
2291
2893
|
- An `ldr` target and a viewport are exposed with the view's `exposure`
|
|
2292
2894
|
and tone mapped with the frame's curve. An `hdr` target holds linear
|
|
2293
2895
|
radiance and is exposed by the frame that shows it.
|
|
2294
2896
|
- Bloom, AO, SSR, depth of field, colour grading and custom post passes
|
|
2295
|
-
run on the main frame only.
|
|
2296
|
-
|
|
2297
|
-
|
|
2897
|
+
run on the main frame only (the 2D glow, `PostEffects.glow`, is the
|
|
2898
|
+
exception: a view adds its own).
|
|
2899
|
+
- In a view, sprites are 2D-lit and glow and 2D fluids and soft
|
|
2900
|
+
particles draw as in the main frame. Screen sprites, the UI and debug
|
|
2901
|
+
draw stay on the main frame.
|
|
2298
2902
|
|
|
2299
2903
|
A viewport view draws over the finished image and under the UI.
|
|
2300
2904
|
|
|
@@ -2340,8 +2944,9 @@ fn fragment(f: FragmentInput, p: Params) -> SurfaceOutput // f:
|
|
|
2340
2944
|
// standard_surface(f), noise3(p).
|
|
2341
2945
|
```
|
|
2342
2946
|
An entry you leave out is the standard one.
|
|
2343
|
-
- `lit` surfaces are lit by the sun's cascades, the point and
|
|
2344
|
-
with their shadows
|
|
2947
|
+
- `lit` surfaces are lit by the sun's cascades, the point, spot and rect
|
|
2948
|
+
lights with their shadows and cookies (a rect light as an area, as on a
|
|
2949
|
+
standard material), the environment and the fog. `unlit` surfaces draw
|
|
2345
2950
|
as returned.
|
|
2346
2951
|
- The depth prepass, every shadow pass and every view run the same
|
|
2347
2952
|
entries, and alpha under the material's `alphaTest` is cut in each of
|
|
@@ -2361,11 +2966,15 @@ with up to eight `param` lines. The function reads:
|
|
|
2361
2966
|
|
|
2362
2967
|
It returns linear HDR. To use one:
|
|
2363
2968
|
1. Load it with `og.load.post(world, "post/crt.ogt")`.
|
|
2364
|
-
2. Put the
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2969
|
+
2. Put the handles in the list `PostEffects.custom`, at most
|
|
2970
|
+
`RenderCapabilities.maxCustomPosts` (8) of them. They run in its order,
|
|
2971
|
+
each over the one before it, after depth of field and motion blur and
|
|
2972
|
+
before exposure, bloom and tone mapping. Write the list whole:
|
|
2973
|
+
`post.custom = [grade, crt]`, `post.custom = [...post.custom, h]`,
|
|
2974
|
+
`post.custom = []`. Reading it answers a frozen copy, so
|
|
2975
|
+
`post.custom.push(h)` throws instead of changing nothing; nine passes
|
|
2976
|
+
are refused, never cut to eight. A look gives it on one line:
|
|
2977
|
+
`custom "post/tint.ogt" "post/crt.ogt"`.
|
|
2369
2978
|
3. Set its parameters with `og.render.setPostParam(world, post, "curve",
|
|
2370
2979
|
[0.1])`.
|
|
2371
2980
|
|
|
@@ -2384,11 +2993,22 @@ door. Mark the scene's unmoving models `static true` and put an
|
|
|
2384
2993
|
entity "hall" {
|
|
2385
2994
|
model "models/hall.glb"
|
|
2386
2995
|
static true
|
|
2387
|
-
Transform {
|
|
2996
|
+
Transform {
|
|
2997
|
+
position 0 0 0
|
|
2998
|
+
quaternion 0 0 0 1
|
|
2999
|
+
scale 1 1 1
|
|
3000
|
+
}
|
|
2388
3001
|
}
|
|
2389
3002
|
entity "hall-probes" {
|
|
2390
|
-
Transform {
|
|
2391
|
-
|
|
3003
|
+
Transform {
|
|
3004
|
+
position 0 2 0
|
|
3005
|
+
quaternion 0 0 0 1
|
|
3006
|
+
scale 1 1 1
|
|
3007
|
+
}
|
|
3008
|
+
LightVolume {
|
|
3009
|
+
size 12 4 12
|
|
3010
|
+
spacing 1
|
|
3011
|
+
}
|
|
2392
3012
|
}
|
|
2393
3013
|
```
|
|
2394
3014
|
|
|
@@ -2450,3 +3070,12 @@ textures between lanterns to stay under it.
|
|
|
2450
3070
|
`reference/index.md` (the generated wiki shipped with this skill) maps every
|
|
2451
3071
|
module (grouped by skill, as in §1), type and function to its page, with the IDL text, defaults, ranges
|
|
2452
3072
|
and units of every field.
|
|
3073
|
+
|
|
3074
|
+
`guides/three-to-og.md` (beside this skill) maps three.js's classes and
|
|
3075
|
+
idioms to the engine's: the scene graph to entities and og.Parent,
|
|
3076
|
+
Object3D to og.Transform, the geometry classes to §18's generators, the
|
|
3077
|
+
materials to StandardMaterialDesc, the loaders to og.load, the render
|
|
3078
|
+
loop to systems on the tick, Raycaster to `og.render.raycast`, and the
|
|
3079
|
+
gaps by name. `og migrate three <file.ts>` rewrites a three.js file's
|
|
3080
|
+
mechanical part with those mappings and marks every line it cannot,
|
|
3081
|
+
citing the guide's section.
|