pelican-engine 0.0.0-stage → 0.3.1
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/AGENTS.md +147 -0
- package/LICENSE +22 -0
- package/README.md +129 -2
- package/distribution.json +197 -0
- package/docs/AI-WORKFLOW.md +517 -0
- package/docs/ARCHITECTURE.md +268 -0
- package/docs/CAPABILITIES.md +264 -0
- package/docs/DECISIONS.md +107 -0
- package/docs/M3-USAGE.md +192 -0
- package/docs/PELICAN-MIGRATION.md +38 -0
- package/docs/RENDER-BACKENDS.md +50 -0
- package/docs/ROADMAP.md +239 -0
- package/docs/THIRD-PARTY-NOTICES.md +151 -0
- package/docs/api/REFERENCE.md +1435 -0
- package/docs/api/index.json +1139 -0
- package/docs/npm/DISTRIBUTION.md +130 -0
- package/docs/npm/GAME-AUTHOR.md +147 -0
- package/engine.manifest.json +15 -0
- package/lib/assets/audio.js +67 -0
- package/lib/assets/errors.js +36 -0
- package/lib/assets/glb.js +277 -0
- package/lib/assets/index.js +53 -0
- package/lib/assets/jpeg.js +199 -0
- package/lib/assets/model.js +558 -0
- package/lib/assets/optimize.js +95 -0
- package/lib/assets/texture.js +187 -0
- package/lib/compiler/distribution.js +17 -0
- package/lib/compiler/extract.js +425 -0
- package/lib/compiler/graph.js +366 -0
- package/lib/compiler/index.js +574 -0
- package/lib/compiler/normalize.js +1194 -0
- package/lib/compiler/source-map.js +25 -0
- package/lib/compiler/types.js +2 -0
- package/lib/compiler/version.js +93 -0
- package/lib/experiments/simulation/index.js +2 -0
- package/lib/experiments/simulation/math.js +41 -0
- package/lib/experiments/simulation/probe.js +215 -0
- package/lib/experiments/simulation/scenes.js +192 -0
- package/lib/experiments/simulation/solver.js +346 -0
- package/lib/experiments/simulation/types.js +1 -0
- package/lib/experiments/world/adapters.js +30 -0
- package/lib/experiments/world/geometry.js +180 -0
- package/lib/experiments/world/index.js +2 -0
- package/lib/experiments/world/probe.js +288 -0
- package/lib/experiments/world/types.js +1 -0
- package/lib/experiments/world/world.js +599 -0
- package/lib/navigation/index.js +157 -0
- package/lib/physics/index.js +909 -0
- package/lib/protocol/index.js +92 -0
- package/lib/renderer/acceleration.js +609 -0
- package/lib/renderer/browser.js +2015 -0
- package/lib/renderer/bsdf.js +260 -0
- package/lib/renderer/checkpoint.js +88 -0
- package/lib/renderer/compute-executor.js +1034 -0
- package/lib/renderer/compute-texture-assets.js +49 -0
- package/lib/renderer/compute-view-contract.js +1 -0
- package/lib/renderer/denoiser.js +361 -0
- package/lib/renderer/dielectric.js +360 -0
- package/lib/renderer/embedded-image-pixels.js +368 -0
- package/lib/renderer/emissive-lights.js +67 -0
- package/lib/renderer/environment-importance.js +55 -0
- package/lib/renderer/environment.js +346 -0
- package/lib/renderer/gameplay.js +464 -0
- package/lib/renderer/geometry.js +103 -0
- package/lib/renderer/graph-expressions.js +485 -0
- package/lib/renderer/graph-tsl.js +141 -0
- package/lib/renderer/graph.js +214 -0
- package/lib/renderer/managed-texture-sampling.js +29 -0
- package/lib/renderer/material-graphs.js +297 -0
- package/lib/renderer/medium.js +348 -0
- package/lib/renderer/models.js +1053 -0
- package/lib/renderer/motion-shader.js +40 -0
- package/lib/renderer/path-shader.js +196 -0
- package/lib/renderer/pathtrace.js +324 -0
- package/lib/renderer/postprocess-contract.js +1 -0
- package/lib/renderer/postprocess.js +494 -0
- package/lib/renderer/render-buffers.js +132 -0
- package/lib/renderer/render-textures.js +155 -0
- package/lib/renderer/startup.js +50 -0
- package/lib/renderer/surface-capture.js +199 -0
- package/lib/renderer/surface-gpu.js +270 -0
- package/lib/renderer/surface-motion.js +84 -0
- package/lib/renderer/surface-ray-shader.js +91 -0
- package/lib/renderer/surface-shader.js +88 -0
- package/lib/renderer/surface-tracer.js +1720 -0
- package/lib/renderer/surface.js +1425 -0
- package/lib/renderer/temporal-filter.js +512 -0
- package/lib/renderer/texture-pixels.js +207 -0
- package/lib/renderer/traversal-nodes.js +54 -0
- package/lib/renderer/ui.js +446 -0
- package/lib/renderer/vertex-bounds.js +127 -0
- package/lib/renderer/vertex-contract.js +1 -0
- package/lib/renderer/vertex-graphs.js +197 -0
- package/lib/renderer/volume-shader.js +50 -0
- package/lib/runtime/compute-contract.js +1 -0
- package/lib/runtime/compute-service.js +674 -0
- package/lib/runtime/contracts.js +1 -0
- package/lib/runtime/error.js +37 -0
- package/lib/runtime/events.js +189 -0
- package/lib/runtime/game-services.js +772 -0
- package/lib/runtime/host.js +839 -0
- package/lib/runtime/material-parameters.js +145 -0
- package/lib/runtime/plugins.js +1 -0
- package/lib/runtime/postprocess-contract.js +1 -0
- package/lib/runtime/postprocess.js +189 -0
- package/lib/runtime/prefabs.js +13 -0
- package/lib/runtime/random.js +88 -0
- package/lib/runtime/render-buffer-contract.js +1 -0
- package/lib/runtime/render-buffers.js +243 -0
- package/lib/runtime/render-capabilities.js +17 -0
- package/lib/runtime/render-settings.js +249 -0
- package/lib/runtime/render-texture-contract.js +1 -0
- package/lib/runtime/render-textures.js +271 -0
- package/lib/runtime/resources.js +648 -0
- package/lib/runtime/scheduler.js +765 -0
- package/lib/runtime/storage.js +169 -0
- package/lib/runtime/world.js +594 -0
- package/lib/schema/index.js +24 -0
- package/lib/sdk/graph-values.js +16 -0
- package/lib/sdk/index.d.ts +800 -0
- package/lib/sdk/index.js +82 -0
- package/lib/tools/api-index.js +113 -0
- package/lib/tools/browser-session.js +89 -0
- package/lib/tools/cli.js +513 -0
- package/lib/tools/compress-dist.js +59 -0
- package/lib/tools/control-client.js +153 -0
- package/lib/tools/control-server.js +633 -0
- package/lib/tools/diagnostic-code.js +3 -0
- package/lib/tools/doctor.js +496 -0
- package/lib/tools/npm/browser-cli.js +6 -0
- package/lib/tools/npm/entry.js +40 -0
- package/lib/tools/npm/layout.js +195 -0
- package/lib/tools/setup.js +153 -0
- package/licenses/@dimforge/rapier2d-compat/LICENSE +201 -0
- package/licenses/@dimforge/rapier3d-compat/LICENSE +201 -0
- package/licenses/@gltf-transform/core/LICENSE.md +21 -0
- package/licenses/@gltf-transform/extensions/LICENSE.md +21 -0
- package/licenses/@gltf-transform/functions/LICENSE.md +21 -0
- package/licenses/@img/colour/LICENSE.md +82 -0
- package/licenses/@img/sharp-win32-x64/LICENSE +191 -0
- package/licenses/@oxc-project/types/LICENSE +22 -0
- package/licenses/@rolldown/pluginutils/LICENSE +21 -0
- package/licenses/@tweenjs/tween.js/LICENSE +23 -0
- package/licenses/@types/ndarray/LICENSE +21 -0
- package/licenses/@types/node/LICENSE +21 -0
- package/licenses/@types/stats.js/LICENSE +21 -0
- package/licenses/@types/three/@dimforge/rapier3d-compat/LICENSE +201 -0
- package/licenses/@types/three/LICENSE +21 -0
- package/licenses/@types/webxr/LICENSE +21 -0
- package/licenses/@webgpu/types/LICENSE +26 -0
- package/licenses/THIRD-PARTY-NOTICES.md +139 -0
- package/licenses/cwise-compiler/LICENSE +22 -0
- package/licenses/detect-libc/LICENSE +201 -0
- package/licenses/esbuild/LICENSE.md +21 -0
- package/licenses/fast-png/LICENSE +21 -0
- package/licenses/fdir/LICENSE +7 -0
- package/licenses/fflate/LICENSE +21 -0
- package/licenses/iobuffer/LICENSE +22 -0
- package/licenses/iota-array/LICENSE +22 -0
- package/licenses/is-buffer/LICENSE +21 -0
- package/licenses/ktx-parse/LICENSE +21 -0
- package/licenses/lightningcss/LICENSE +373 -0
- package/licenses/lightningcss-win32-x64-msvc/LICENSE +373 -0
- package/licenses/meshoptimizer/LICENSE.md +21 -0
- package/licenses/nanoid/LICENSE +20 -0
- package/licenses/ndarray/LICENSE +22 -0
- package/licenses/ndarray-lanczos/LICENSE +21 -0
- package/licenses/ndarray-ops/LICENSE +22 -0
- package/licenses/ndarray-pixels/LICENSE +21 -0
- package/licenses/ngraph.path/LICENSE +21 -0
- package/licenses/picocolors/LICENSE +15 -0
- package/licenses/picomatch/LICENSE +21 -0
- package/licenses/playwright/LICENSE +202 -0
- package/licenses/playwright/NOTICE +5 -0
- package/licenses/playwright-core/LICENSE +202 -0
- package/licenses/playwright-core/NOTICE +5 -0
- package/licenses/postcss/LICENSE +20 -0
- package/licenses/prettier/LICENSE +7 -0
- package/licenses/property-graph/LICENSE +21 -0
- package/licenses/rolldown/LICENSE +25 -0
- package/licenses/semver/LICENSE +15 -0
- package/licenses/sharp/LICENSE +191 -0
- package/licenses/source-map-js/LICENSE +28 -0
- package/licenses/three/LICENSE +21 -0
- package/licenses/tinyglobby/LICENSE +21 -0
- package/licenses/tsx/LICENSE +21 -0
- package/licenses/typescript/LICENSE.txt +55 -0
- package/licenses/undici-types/LICENSE +21 -0
- package/licenses/uniq/LICENSE +22 -0
- package/licenses/vite/LICENSE.md +2084 -0
- package/package.json +49 -4
- package/web/index.html +34 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Pelican Engine npm development distribution
|
|
2
|
+
|
|
3
|
+
The installed package is `pelican-engine`. Its root export is the same compiled
|
|
4
|
+
public SDK used by repository games, not a wrapper or a separate SDK. The package
|
|
5
|
+
also provides the `pelican` executable. This is an M3 development distribution,
|
|
6
|
+
not accepted M3 hardware evidence or a complete engine release.
|
|
7
|
+
|
|
8
|
+
## Windows project workflow
|
|
9
|
+
|
|
10
|
+
Use Node 24 and npm in an empty project directory. The explicit prefix keeps npm
|
|
11
|
+
in this directory even when a parent directory is also an npm project:
|
|
12
|
+
|
|
13
|
+
```powershell
|
|
14
|
+
npm --prefix . i -D pelican-engine
|
|
15
|
+
npx pelican dev
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The first installed `dev` creates the minimal `game.ts`, `pelican.project.json`
|
|
19
|
+
version lock, and game-author `AGENTS.md` when the directory contains only npm
|
|
20
|
+
installation metadata. An existing game or manifest is used as supplied; other
|
|
21
|
+
project files or author instructions prevent automatic scaffolding. Files are
|
|
22
|
+
never overwritten. Stop dev with Ctrl+C, then use the same local CLI:
|
|
23
|
+
|
|
24
|
+
```powershell
|
|
25
|
+
npx --no-install pelican api
|
|
26
|
+
npx --no-install pelican setup --install-browser
|
|
27
|
+
npx --no-install pelican check
|
|
28
|
+
npx --no-install pelican dev --port 4173
|
|
29
|
+
npx --no-install pelican capture --port 4174 --output capture.png
|
|
30
|
+
npx --no-install pelican build
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
For an exact-version install use `npm --prefix . i -D --save-exact pelican-engine@0.3.1`,
|
|
34
|
+
or substitute the local `pelican-engine-0.3.1.tgz` tarball before registry
|
|
35
|
+
publication. `pelican create --template minimal` remains available explicitly.
|
|
36
|
+
|
|
37
|
+
Without `--prefix .`, npm may select an ancestor `package.json` or `node_modules`
|
|
38
|
+
as the install root and modify that parent. The shorter `npm i -D pelican-engine`
|
|
39
|
+
works when npm selects the intended directory; verify `npm prefix` before using
|
|
40
|
+
it. The smoke harness uses an explicit prefix and does not require `npm init`.
|
|
41
|
+
|
|
42
|
+
`create --template minimal` uses the same primitive starter in npm and repository
|
|
43
|
+
workflows. npm defaults to `minimal` and reports the selected template; repository
|
|
44
|
+
default `m2` is preserved with its historical sample assets. Samples are not engine
|
|
45
|
+
capabilities. Identical authored game/asset inputs use the same API/compiler/runtime
|
|
46
|
+
in either channel. npm does not bundle the `m2` sample or any sample/game assets.
|
|
47
|
+
|
|
48
|
+
`create` writes `game.ts`, `AGENTS.md` and an exact `pelican.project.json` lock in the current
|
|
49
|
+
directory. Existing project files are never overwritten. Author TypeScript with
|
|
50
|
+
named imports from `pelican-engine` and project-local imports only. The compiler
|
|
51
|
+
reports `TC_PUBLIC_API` for renderer, Three.js, TSL, physics, Node and internal
|
|
52
|
+
package imports, including type-only and unused imports. Package subpaths are not
|
|
53
|
+
public exports. Project lock mismatches report `TC_ENGINE_VERSION_MISMATCH`;
|
|
54
|
+
mixed package/API versions report `TC_ENGINE_SDK_VERSION`. Reinstall a matching
|
|
55
|
+
exact version, or migrate through the public API before explicitly changing a lock.
|
|
56
|
+
|
|
57
|
+
After npm dependencies and Playwright Chromium have been installed, authoring,
|
|
58
|
+
API inspection, checks, builds, dev/control commands and capture need no network
|
|
59
|
+
service. `setup --offline` never installs dependencies or downloads a browser in
|
|
60
|
+
an installed package. Keep the game package-lock for repeatable future installs;
|
|
61
|
+
an offline _reinstall_ additionally requires the npm cache or a dependency mirror.
|
|
62
|
+
`TC_DISTRIBUTION_DEPENDENCY_MISSING` and
|
|
63
|
+
`TC_DISTRIBUTION_DEPENDENCY_VERSION` identify broken tool dependencies.
|
|
64
|
+
|
|
65
|
+
Serve the generated `dist/` directory over HTTP(S), including its `generated/`
|
|
66
|
+
files, license files, notices and compression manifests. No Node process, engine
|
|
67
|
+
repository, SDK installation, external AI service or development control server
|
|
68
|
+
is needed by players. Use `build --base /game/` for a nested deployment URL.
|
|
69
|
+
Capture requires installed Chromium and actual supported graphics capability.
|
|
70
|
+
No software renderer result establishes hardware acceptance.
|
|
71
|
+
|
|
72
|
+
## AI entry points
|
|
73
|
+
|
|
74
|
+
Read the game-author `AGENTS.md`, `docs/npm/GAME-AUTHOR.md`, `docs/CAPABILITIES.md` and
|
|
75
|
+
`docs/api/REFERENCE.md` inside `node_modules/pelican-engine`. The machine-readable
|
|
76
|
+
API is `docs/api/index.json`, also discoverable through `pelican api`.
|
|
77
|
+
`lib/sdk/index.d.ts` is the authoritative installed declaration surface.
|
|
78
|
+
`docs/PELICAN-MIGRATION.md` explains current names and historical provenance.
|
|
79
|
+
Repository paths and release instructions in copied documents are historical
|
|
80
|
+
context; the installed workflow above replaces source-checkout commands.
|
|
81
|
+
The JSON CLI protocol and dev control operations are shared with the repository.
|
|
82
|
+
|
|
83
|
+
## Maintainer validation and publishing
|
|
84
|
+
|
|
85
|
+
```powershell
|
|
86
|
+
npm run distribution:test
|
|
87
|
+
npm run distribution:pack
|
|
88
|
+
npm run distribution:smoke
|
|
89
|
+
npm run distribution:smoke -- --gpu
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Build output is regenerated from current sources into ignored
|
|
93
|
+
`.pelican/npm/package/`; tarballs and smoke reports go in `.pelican/npm/artifacts/`.
|
|
94
|
+
Neither staging nor tarballs are inside the deployable game `dist/`.
|
|
95
|
+
Pack that staging directory, not the private repository root. Compiled relative
|
|
96
|
+
imports use `lib/` modules and `.js`, never the TypeScript source layout. The
|
|
97
|
+
tarball allowlist excludes source maps, evidence, examples and game assets;
|
|
98
|
+
SDK declarations, engine runtime code, MIT, copied AI docs, and dependency
|
|
99
|
+
license texts are included. `distribution.json` records version and file hashes.
|
|
100
|
+
Dependencies are normal pinned npm dependencies, not vendored engine sources.
|
|
101
|
+
|
|
102
|
+
The npm workflow runs only for a published GitHub milestone release. Its gate
|
|
103
|
+
requires tag/version/SDK/manifest identity and the existing milestone acceptance
|
|
104
|
+
and source-fingerprint checks. An ordinary push or CPU-green build cannot publish.
|
|
105
|
+
M3 is deliberately blocked until its release profile and real acceptance exist.
|
|
106
|
+
The default smoke install uses npm cache only; `--online-install` allows network
|
|
107
|
+
for initial dependency installation, then runs every operation offline with a
|
|
108
|
+
disabled registry. CI uses this flag because `npm ci` alone need not populate
|
|
109
|
+
registry metadata for a fresh external install. `--gpu` additionally measures
|
|
110
|
+
the installed WebGPU backend, renders path/hybrid/raster, checks a nonblank canvas,
|
|
111
|
+
captures through both CLI routes, verifies WebGL2 fallback rejection, and loads
|
|
112
|
+
the nested-base static build without a development hook. It is not M3 acceptance.
|
|
113
|
+
|
|
114
|
+
The single unscoped package is `pelican-engine`, its only public import is
|
|
115
|
+
`pelican-engine`, and its executable is `pelican`. The GitHub target is
|
|
116
|
+
`danchar12138/pelican-engine`; main completed the public repository rename and
|
|
117
|
+
verified the updated origin. No new organization is required. The authorized
|
|
118
|
+
npm Automation Token was authenticated as `danchar12138`, and main confirmed
|
|
119
|
+
the repository Actions secret `NPM_TOKEN` is configured. `npm view pelican-engine`
|
|
120
|
+
returned `E404` before publication. The package remains unpublished pending
|
|
121
|
+
genuine M3 acceptance; this is not an authentication or organization blocker.
|
|
122
|
+
`setup-node` configures the registry;
|
|
123
|
+
only the final publication step receives `NODE_AUTH_TOKEN`. A missing secret
|
|
124
|
+
fails with `TC_NPM_AUTH_MISSING`, without printing the value. The token must have
|
|
125
|
+
permission to publish the fixed unscoped package. No OIDC/trusted-publisher setup
|
|
126
|
+
or provenance flag is required by this workflow. Prereleases use `next`; stable
|
|
127
|
+
versions use `latest`. These credentials do not bypass milestone acceptance.
|
|
128
|
+
No token value is stored in source, documentation, or probe output. No publication
|
|
129
|
+
is performed by this packaging task. See
|
|
130
|
+
`tools/npm/publish-gate.ts` in the source distribution.
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Pelican Engine Game Author
|
|
2
|
+
|
|
3
|
+
Your workspace is the game project. Use the public `pelican-engine` API and local
|
|
4
|
+
game modules. Do not modify engine internals or `node_modules`, import Three.js,
|
|
5
|
+
TSL, GPU devices, physics-library objects, compiler internals or Node APIs into
|
|
6
|
+
game code. Engine research, hardware acceptance, evidence and publishing tasks
|
|
7
|
+
are not part of game authoring.
|
|
8
|
+
|
|
9
|
+
Read the API reference at `docs/api/REFERENCE.md` and machine-readable symbols at
|
|
10
|
+
`docs/api/index.json` under the engine/package directory. Installed declarations
|
|
11
|
+
are `lib/sdk/index.d.ts`. `pelican api` discovers the index;
|
|
12
|
+
`pelican api --symbol defineGame` returns exact, case-sensitive public signatures
|
|
13
|
+
as JSON in both source and npm distributions. `pelican help`
|
|
14
|
+
lists the JSON CLI protocol. `docs/CAPABILITIES.md` records implemented scope and
|
|
15
|
+
limitations. The longer `docs/AI-WORKFLOW.md` is shared design context, not an
|
|
16
|
+
instruction to run source-only engine acceptance tasks.
|
|
17
|
+
|
|
18
|
+
The shared `docs/M3-USAGE.md` describes the current rendering and compute
|
|
19
|
+
contracts. Its `examples/`, engine `packages/`, and `npm run probe:*` references
|
|
20
|
+
are maintainer references available only in a source checkout; the npm package
|
|
21
|
+
intentionally contains no example projects, game assets, or engine source. Use
|
|
22
|
+
the same public declarations in your own game project and validate them with the
|
|
23
|
+
installed `pelican` commands. Do not try to import an internal compiled module.
|
|
24
|
+
|
|
25
|
+
Models using `KHR_animation_pointer` animation channels are currently rejected
|
|
26
|
+
with `TC_MODEL_EXTENSION_UNSUPPORTED`; pointer-driven material animation is not
|
|
27
|
+
implemented. A merely declared optional extension without such a channel does
|
|
28
|
+
not cause this runtime rejection.
|
|
29
|
+
|
|
30
|
+
## Standalone Rendering Recipe
|
|
31
|
+
|
|
32
|
+
The following complete asset-free `game.ts` works with either distribution and
|
|
33
|
+
exercises the public opaque-IOR and specular material controls:
|
|
34
|
+
|
|
35
|
+
<!-- standalone-render-recipe:start -->
|
|
36
|
+
```ts
|
|
37
|
+
import { defineGame, defineMaterial, defineScene } from "pelican-engine";
|
|
38
|
+
|
|
39
|
+
const material = defineMaterial({
|
|
40
|
+
id: "paint",
|
|
41
|
+
color: "#303030",
|
|
42
|
+
roughness: 0.35,
|
|
43
|
+
metalness: 0,
|
|
44
|
+
ior: 1.5,
|
|
45
|
+
specularIntensity: 0.7,
|
|
46
|
+
specularColor: "#ffb080",
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
export default defineGame({
|
|
50
|
+
id: "render-recipe",
|
|
51
|
+
scene: defineScene({
|
|
52
|
+
id: "main",
|
|
53
|
+
camera: { position: [0, 1, 5], target: [0, 0, 0], fov: 45 },
|
|
54
|
+
environment: { background: "#17212a", ambientIntensity: 0.4 },
|
|
55
|
+
entities: [
|
|
56
|
+
{ id: "object", mesh: { geometry: { kind: "box" }, material } },
|
|
57
|
+
{ id: "light", transform: { position: [2, 4, 5] },
|
|
58
|
+
light: { kind: "directional", intensity: 3 } },
|
|
59
|
+
],
|
|
60
|
+
}),
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
<!-- standalone-render-recipe:end -->
|
|
64
|
+
|
|
65
|
+
Use `npx --no-install pelican check` and `npx --no-install pelican dev` in an
|
|
66
|
+
installed project, then `npx --no-install pelican build`. A repository-based
|
|
67
|
+
project uses the same source and equivalent engine CLI commands listed below.
|
|
68
|
+
Imported opaque GLB materials support `KHR_materials_specular` intensity and
|
|
69
|
+
color textures with `TEXCOORD_0`. Positive transmission combined with specular
|
|
70
|
+
textures or non-default specular remains unsupported. The tracer rejects PNG
|
|
71
|
+
color maps with ICC profiles. Low-bit-depth grayscale and indexed Adam7 PNGs
|
|
72
|
+
are decoded with explicit pass/filter validation. For 16-bit PNG,
|
|
73
|
+
supported color chunks are gAMA 45455 or 100000, standard sRGB cHRM, sRGB
|
|
74
|
+
intent 0, and cICP [1,13,0,1]; other values are diagnosed. Use supported PNG
|
|
75
|
+
or JPEG and retain diagnostics rather than silently changing material semantics.
|
|
76
|
+
|
|
77
|
+
Audio voices returned by `ctx.audio.play(soundId, options)` can be paused and
|
|
78
|
+
resumed with `ctx.audio.pause(voice)` and `ctx.audio.resume(voice)`. Both are
|
|
79
|
+
idempotent; a valid unknown voice is a no-op. Playback position follows fixed
|
|
80
|
+
simulation ticks, including while browser audio is locked. Paused time does not
|
|
81
|
+
advance the voice. Snapshots restore playback intent and logical position, not
|
|
82
|
+
the browser's internal audio processing state.
|
|
83
|
+
|
|
84
|
+
Declare `audioBuses: [{ id: "music", volume: 0.5 }]` in `defineGame`, route a
|
|
85
|
+
sound with `sounds: [{ id: "theme", asset: musicAsset, bus: "music" }]`, and
|
|
86
|
+
change its bus using `ctx.audio.busVolume("music", 0.25)`. A `play` call can
|
|
87
|
+
override the sound's bus with `{ bus: "music" }`. Bus, voice and master gains
|
|
88
|
+
multiply; `ctx.audio.volume` controls master gain. Unknown buses and gains
|
|
89
|
+
outside 0–1 return diagnostics. Games with no buses keep their existing routing.
|
|
90
|
+
|
|
91
|
+
Use `ctx.audio.fadeBus("music", 0, 1.5)` for a fixed-tick linear fade in
|
|
92
|
+
seconds. A new fade or `busVolume` replaces the previous fade. `play` accepts
|
|
93
|
+
an optional integer `priority` from 0 to 255; when the voice limit is full, a
|
|
94
|
+
new voice only replaces the earliest voice with a strictly lower priority.
|
|
95
|
+
Invalid priority or a non-winning full voice returns a stable diagnostic.
|
|
96
|
+
|
|
97
|
+
For orthogonal grid routing, game systems may call `ctx.navigation.createGrid`
|
|
98
|
+
with `{ id, owner, width, height }`, then `setBlocked(id, { x, y }, true)` and
|
|
99
|
+
`findPath(id, start, end)`. Paths contain both endpoints; `destroyGrid` and
|
|
100
|
+
`destroyOwner` release grids. The default 65,536-cell `searchCellBudget` limits
|
|
101
|
+
distinct cells considered per query; set it explicitly when needed. Budget
|
|
102
|
+
exhaustion returns `TC_NAVIGATION_CAPACITY`, not an unreachable result. Owners
|
|
103
|
+
are explicit strings, not entities. This is a development-stage grid service,
|
|
104
|
+
not continuous-world, flying or underwater navigation.
|
|
105
|
+
|
|
106
|
+
Keyboard bindings use the browser's `KeyboardEvent.key` values, not physical
|
|
107
|
+
`KeyboardEvent.code` values. For example, use `input: { up: ["w", "W", "ArrowUp"],
|
|
108
|
+
restart: ["r", "R"] }`, not `"KeyW"` or `"KeyR"`. Letter case depends on the
|
|
109
|
+
actual keyboard event; Space is `" "`. Actions are read inside systems through
|
|
110
|
+
`ctx.input.held`, `pressed`, `released`, or `axis`. Always exercise real keyboard
|
|
111
|
+
input in the browser; a mocked system test does not establish browser bindings.
|
|
112
|
+
|
|
113
|
+
## Work Loop
|
|
114
|
+
|
|
115
|
+
1. Inspect `pelican.project.json`, `game.ts`, asset declarations and the public API.
|
|
116
|
+
2. Make small game-only edits. Keep the exact engine version lock.
|
|
117
|
+
3. Run `pelican check`, then `pelican dev` and exercise actual gameplay.
|
|
118
|
+
4. Use `inspect`, `input`, `step`, `pause`, `resume`, `snapshot`, `restore`,
|
|
119
|
+
`record`, `replay`, `status`, `cancel` and `reset-session` against the dev port.
|
|
120
|
+
5. Run `capture --live` against the dev port or a standalone `capture`, inspect
|
|
121
|
+
the actual screenshot, and preserve explicit failures rather than claiming success.
|
|
122
|
+
6. Run `build` and serve `dist/` over HTTP(S). Keep notices and license files.
|
|
123
|
+
|
|
124
|
+
Installed package commands use `npx --no-install pelican <command>` from the game
|
|
125
|
+
directory. Repository commands use `node --import tsx <engine>/tools/cli.ts
|
|
126
|
+
<command> --project <game-directory>` with the same operations and protocol.
|
|
127
|
+
Both `create` commands generate a project `AGENTS.md` pointing to these same docs.
|
|
128
|
+
In a new installed workspace, `npm --prefix . i -D pelican-engine` followed by
|
|
129
|
+
`npx pelican dev` automatically creates that same minimal starter when no project
|
|
130
|
+
files exist. Existing games, manifests and author instructions are preserved.
|
|
131
|
+
The explicit prefix prevents npm from choosing a parent project when one exists.
|
|
132
|
+
|
|
133
|
+
Dependencies and Chromium must be installed before offline work. Once prepared,
|
|
134
|
+
checks/builds/dev/capture use local files and loopback only. `setup --offline`
|
|
135
|
+
never downloads a missing browser. In npm projects use `setup --install-browser`
|
|
136
|
+
once while online. No Git checkout, account, external AI service or source tree
|
|
137
|
+
is required to author an installed game.
|
|
138
|
+
|
|
139
|
+
Do not assume all render modes are supported. Use `engine.render.capabilities()`
|
|
140
|
+
to inspect the actual backend. WebGL2 fallback does not silently implement
|
|
141
|
+
compute-dependent path/hybrid features. Observe structured diagnostics and keep
|
|
142
|
+
the prior valid state on rejected operations. Hardware acceptance is separate
|
|
143
|
+
from a successful software-rendered or CPU-only run.
|
|
144
|
+
|
|
145
|
+
For project-version mismatches, migrate via public API, then explicitly update
|
|
146
|
+
the lock. For package integrity/dependency errors, reinstall one exact package
|
|
147
|
+
version. Never work around a diagnostic by importing an internal dependency.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pelican-engine",
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"milestone": "M3",
|
|
5
|
+
"stage": "advanced-rendering-development",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"protocolVersion": "0.1.0",
|
|
8
|
+
"agentEntry": "AGENTS.md",
|
|
9
|
+
"developmentPlatform": "windows",
|
|
10
|
+
"renderer": "webgpu-with-webgl2-fallback",
|
|
11
|
+
"publicApi": "lib/sdk/index.d.ts",
|
|
12
|
+
"workflow": "docs/npm/GAME-AUTHOR.md",
|
|
13
|
+
"releasePolicy": "docs/npm/DISTRIBUTION.md",
|
|
14
|
+
"releaseScope": "dynamic-raytracing"
|
|
15
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
export function inspectWAV(data, ctx) {
|
|
3
|
+
if (data.toString("latin1", 0, 4) !== "RIFF") {
|
|
4
|
+
const magic = data.toString("latin1", 0, 4);
|
|
5
|
+
if (path.extname(ctx.file).toLowerCase() === ".wav" &&
|
|
6
|
+
!["RF64", "RIFX"].includes(magic))
|
|
7
|
+
ctx.invalid("WAV/header", "Missing or damaged RIFF header.");
|
|
8
|
+
ctx.unsupported("audio/format", "Only little-endian RIFF/WAVE PCM and IEEE float audio are implemented.");
|
|
9
|
+
}
|
|
10
|
+
ctx.check(data.length >= 12 && data.toString("latin1", 8, 12) === "WAVE", "WAV/header", "Missing or truncated WAVE header.");
|
|
11
|
+
ctx.check(data.readUInt32LE(4) + 8 === data.length, "WAV/length", "RIFF length does not match the file.");
|
|
12
|
+
let offset = 12;
|
|
13
|
+
let format;
|
|
14
|
+
let samples;
|
|
15
|
+
let factFrames;
|
|
16
|
+
while (offset < data.length) {
|
|
17
|
+
ctx.check(offset + 8 <= data.length, "WAV/chunk", "Truncated chunk header.");
|
|
18
|
+
const id = data.toString("latin1", offset, offset + 4), size = data.readUInt32LE(offset + 4);
|
|
19
|
+
const end = offset + 8 + size, padded = end + (size % 2);
|
|
20
|
+
ctx.check(padded <= data.length, `WAV/${id}`, "Chunk exceeds RIFF boundary or is missing padding.");
|
|
21
|
+
const chunk = data.subarray(offset + 8, end);
|
|
22
|
+
if (id === "fmt ") {
|
|
23
|
+
ctx.check(!format && !samples && size >= 16, "WAV/fmt", "Missing, duplicate, misplaced or truncated format chunk.");
|
|
24
|
+
const encoding = chunk.readUInt16LE(0), channels = chunk.readUInt16LE(2), sampleRate = chunk.readUInt32LE(4);
|
|
25
|
+
const byteRate = chunk.readUInt32LE(8), blockAlign = chunk.readUInt16LE(12), bitsPerSample = chunk.readUInt16LE(14);
|
|
26
|
+
if (![1, 3].includes(encoding))
|
|
27
|
+
ctx.unsupported("WAV/encoding", `WAVE format tag ${encoding} is not implemented (PCM=1, float=3).`);
|
|
28
|
+
ctx.check(size === 16 || (size >= 18 && chunk.readUInt16LE(16) === size - 18), "WAV/fmt", "Invalid format extension length.");
|
|
29
|
+
ctx.check(channels > 0 &&
|
|
30
|
+
sampleRate > 0 &&
|
|
31
|
+
(encoding === 1 ? [8, 16, 24, 32] : [32, 64]).includes(bitsPerSample), "WAV/fmt", "Invalid channels, sample rate or sample size.");
|
|
32
|
+
ctx.check(blockAlign === (channels * bitsPerSample) / 8 &&
|
|
33
|
+
byteRate === sampleRate * blockAlign, "WAV/fmt", "Inconsistent block alignment or byte rate.");
|
|
34
|
+
format = { encoding, channels, sampleRate, bitsPerSample, blockAlign };
|
|
35
|
+
}
|
|
36
|
+
else if (id === "fact") {
|
|
37
|
+
ctx.check(factFrames === undefined && size >= 4, "WAV/fact", "Duplicate or truncated fact chunk.");
|
|
38
|
+
factFrames = chunk.readUInt32LE(0);
|
|
39
|
+
}
|
|
40
|
+
else if (id === "data") {
|
|
41
|
+
ctx.check(format && !samples, "WAV/data", "Missing format chunk or duplicate data chunk.");
|
|
42
|
+
ctx.check(size > 0 && size % format.blockAlign === 0, "WAV/data", "Empty or incomplete sample frame.");
|
|
43
|
+
samples = chunk;
|
|
44
|
+
}
|
|
45
|
+
offset = padded;
|
|
46
|
+
}
|
|
47
|
+
ctx.check(format && samples, "WAV/chunks", "Both fmt and data chunks are required.");
|
|
48
|
+
if (format.encoding === 3) {
|
|
49
|
+
const step = format.bitsPerSample / 8;
|
|
50
|
+
for (let i = 0; i < samples.length; i += step)
|
|
51
|
+
ctx.check(Number.isFinite(step === 4 ? samples.readFloatLE(i) : samples.readDoubleLE(i)), "WAV/data", "Non-finite floating point sample.");
|
|
52
|
+
}
|
|
53
|
+
const frameCount = samples.length / format.blockAlign;
|
|
54
|
+
ctx.check(format.encoding !== 3 || factFrames !== undefined, "WAV/fact", "IEEE float WAVE requires a fact sample count.");
|
|
55
|
+
if (factFrames !== undefined)
|
|
56
|
+
ctx.check(factFrames === frameCount, "WAV/fact", "Fact sample count does not match data frames.");
|
|
57
|
+
return {
|
|
58
|
+
format: "wav",
|
|
59
|
+
encoding: format.encoding === 1 ? "pcm" : "ieee-float",
|
|
60
|
+
channels: format.channels,
|
|
61
|
+
sampleRate: format.sampleRate,
|
|
62
|
+
bitsPerSample: format.bitsPerSample,
|
|
63
|
+
frameCount,
|
|
64
|
+
duration: frameCount / format.sampleRate,
|
|
65
|
+
dataBytes: samples.length,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export class AssetInspectionError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
file;
|
|
4
|
+
resource;
|
|
5
|
+
suggestion;
|
|
6
|
+
constructor(code, file, resource, detail, suggestion) {
|
|
7
|
+
super(`${code}: ${file} [${resource}]: ${detail} ${suggestion}`);
|
|
8
|
+
this.code = code;
|
|
9
|
+
this.file = file;
|
|
10
|
+
this.resource = resource;
|
|
11
|
+
this.suggestion = suggestion;
|
|
12
|
+
this.name = "AssetInspectionError";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export class InspectionContext {
|
|
16
|
+
file;
|
|
17
|
+
constructor(file) {
|
|
18
|
+
this.file = file;
|
|
19
|
+
}
|
|
20
|
+
invalid(resource, detail) {
|
|
21
|
+
throw new AssetInspectionError("TC_ASSET_INVALID", this.file, resource, detail, "Repair or re-export the source asset and inspect it again.");
|
|
22
|
+
}
|
|
23
|
+
unsupported(resource, detail) {
|
|
24
|
+
throw new AssetInspectionError("TC_ASSET_UNSUPPORTED", this.file, resource, detail, "Convert to a supported self-contained format; see packages/assets/README.md.");
|
|
25
|
+
}
|
|
26
|
+
check(condition, resource, detail) {
|
|
27
|
+
if (!condition)
|
|
28
|
+
this.invalid(resource, detail);
|
|
29
|
+
}
|
|
30
|
+
integer(value, resource, min = 0, max = Number.MAX_SAFE_INTEGER) {
|
|
31
|
+
this.check(Number.isSafeInteger(value) &&
|
|
32
|
+
Number(value) >= min &&
|
|
33
|
+
Number(value) <= max, resource, `Expected an integer in [${min}, ${max}].`);
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
}
|