diff --git a/.agent/HANDOFF.md b/.agent/HANDOFF.md new file mode 100644 index 0000000..74a2268 --- /dev/null +++ b/.agent/HANDOFF.md @@ -0,0 +1,88 @@ +# Handoff + +Last updated: 2026-10-01 + +## Current task + +Move the repository from GocciaScript 0.10.0 to 0.14.0 and replace the +workarounds that 0.10.0 forced with what 0.11.0 to 0.14.0 provide natively. + +State: implemented and validated locally on the branch `t3code/4b7db725`, and +published as a pull request. Merging is left to Johannes. After the merge the +repository gets its first tag; the tag name is not decided yet. + +## What changed + +- **Runtime pin**: `metadata/gocciascript.json`, CI, `tests/linux/Dockerfile`, + npm scripts, the Node test harness and the DOOM Makefile target 0.14.0 and + `GocciaRunner` (`GOCCIA_LOADER` is now `GOCCIA_RUNNER`). +- **Permissions**: `"unsafe-ffi"` is a hard error on 0.14.0. Both `goccia.json` + files use a `permissions` block. The DOOM config also requests read access to + `bindings/raylib.ts`, because that file is outside its project directory. + Unit and palette tests pass `--ignore-config-permissions`; native-call tests + and CI pass `-P`. +- **Generator**: all 600 functions are bound (was 488). `TraceLog` and + `TextFormat` are variadic, the seven `*Raw` duplicates became + `FFI.nullable("utf8string")`, and `AudioStream.buffer` keeps its native name. + The declarations gained a branded `FFIAggregate`, `FFIAggregateMetadata`, + `FFIVarargs` and `GocciaFFI`. +- **Examples**: Bunnymark uses `GenMeshPlane` and direct imports; DOOM scales + with `DrawTextureEx`. Frames are byte-identical before and after. +- **Package entry point**: `package.json` has an `exports` map, so an installed + copy is imported as `@frostney/gocciascript-raylib`; every other packaged file + stays reachable by subpath. +- **Tests**: `tests/types/consumer.ts` is compiled by `npm run test:types`; + `tests/fixtures/ffi-smoke.ts` covers mixed-float, variadic, nullable and + exact-field-name calls; CI and Docker now run `make test-palette`. +- **Docs**: both READMEs, `SKIPPED.md`, and the new + `benchmarks/import-binding-aliases.md`. + +## Decisions made + +- `allow-ffi` stays unscoped: library discovery falls back to a bare loader + name and to `RAYLIB_LIBRARY_PATH`, which a path scope cannot describe. +- `Goccia.gc()` every 10 frames stays in `examples/doom-gpl/run.ts`. Without it + the heap grows about 1.7 MB per frame until the memory ceiling forces a + collection; a `max-memory` setting was no faster than the explicit call. +- The Node `.mjs` adapters stay: `GocciaRunner` has no host process or hashing + API. +- `for...of` stays. On 0.14.0 a classic `for` is about 8 to 10% faster in the + isolated update loop, which does not change the whole-frame conclusion. +- The package version stays `0.1.0`; the package is not on npm and the + repository has no tags. +- The benchmark note keeps its software-rendered numbers for now. Johannes + wants numbers from several machines before it is updated. + +## Validation + +Run on Linux x86-64 against the 0.14.0 release binaries, raylib at the pinned +commit, and a private Xvfb with Mesa llvmpipe: `npm run generate`, `npm test`, +`check:generated`, `test:types`, `test:abi`, `test:ffi`, `pack:check`, the five +window smokes, `test:bunnymark:visual`, `make test-palette` and +`make smoke GOCCIA_FLAGS=-P` all pass. + +Not run locally: the macOS lane and the Docker build. + +A scratch consumer project that installed the packed tarball imported the +bindings by name, by subpath, and as types under `tsc`. + +On the Radeon RX 6600M the instanced Bunnymark path spends about 27.6 ms of a +28.2 ms frame in the JavaScript update loop at 10,000 sprites; drawing and +presenting take under 1 ms. The `DrawTextureV` path spends about 72 ms per +frame in its 10,000 native calls. + +## Open questions + +- Callback-taking raylib functions accept `FFI.callback(...).create(fn)` handles + at run time, but `FFIPointerInput` does not type them. This predates the + update. +- Which name the first tag gets. `github:` provider imports and npm + publication both depend on it. + +## Next steps + +1. Merge the pull request once CI is green on both lanes. +2. Tag the merge commit. +3. Collect Bunnymark numbers across machines, then update + `benchmarks/import-binding-aliases.md`. +4. Optimise the Bunnymark update loop, which now bounds the instanced path. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index de155cf..ceab81e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,16 +76,16 @@ jobs: run: | case "$RUNNER_OS-$RUNNER_ARCH" in macOS-ARM64) - asset="gocciascript-0.10.0-macos-arm64.zip" + asset="gocciascript-0.14.0-macos-arm64.zip" ;; macOS-X64) - asset="gocciascript-0.10.0-macos-x64.zip" + asset="gocciascript-0.14.0-macos-x64.zip" ;; Linux-ARM64) - asset="gocciascript-0.10.0-linux-arm64.tar.gz" + asset="gocciascript-0.14.0-linux-arm64.tar.gz" ;; Linux-X64) - asset="gocciascript-0.10.0-linux-x64.tar.gz" + asset="gocciascript-0.14.0-linux-x64.tar.gz" ;; *) echo "Unsupported runner: $RUNNER_OS-$RUNNER_ARCH" >&2 @@ -93,7 +93,7 @@ jobs: ;; esac curl --fail --location \ - "https://github.com/frostney/GocciaScript/releases/download/0.10.0/$asset" \ + "https://github.com/frostney/GocciaScript/releases/download/0.14.0/$asset" \ --output "$RUNNER_TEMP/$asset" mkdir -p "$RUNNER_TEMP/gocciascript" if [[ "$asset" == *.zip ]]; then @@ -101,10 +101,10 @@ jobs: else tar -xzf "$RUNNER_TEMP/$asset" -C "$RUNNER_TEMP/gocciascript" fi - loader="$(find "$RUNNER_TEMP/gocciascript" -type f -name GocciaScriptLoader -print -quit)" + runner="$(find "$RUNNER_TEMP/gocciascript" -type f -name GocciaRunner -print -quit)" test_runner="$(find "$RUNNER_TEMP/gocciascript" -type f -name GocciaTestRunner -print -quit)" - chmod +x "$loader" "$test_runner" - echo "GOCCIA_LOADER=$loader" >> "$GITHUB_ENV" + chmod +x "$runner" "$test_runner" + echo "GOCCIA_RUNNER=$runner" >> "$GITHUB_ENV" echo "GOCCIA_TEST_RUNNER=$test_runner" >> "$GITHUB_ENV" - run: npm ci @@ -122,15 +122,18 @@ jobs: shell: bash run: | mkdir -p .cache - xvfb-run -a "$GOCCIA_LOADER" tests/fixtures/basic-window-smoke.ts - xvfb-run -a "$GOCCIA_LOADER" tests/fixtures/basic-input-smoke.ts - xvfb-run -a "$GOCCIA_LOADER" tests/fixtures/image-loading-smoke.ts - xvfb-run -a "$GOCCIA_LOADER" examples/smoke.ts + # -P accepts the ffi request in goccia.json for this run only. + xvfb-run -a "$GOCCIA_RUNNER" -P tests/fixtures/basic-window-smoke.ts + xvfb-run -a "$GOCCIA_RUNNER" -P tests/fixtures/basic-input-smoke.ts + xvfb-run -a "$GOCCIA_RUNNER" -P tests/fixtures/image-loading-smoke.ts + xvfb-run -a "$GOCCIA_RUNNER" -P examples/smoke.ts xvfb-run -a npm run test:bunnymark:visual - xvfb-run -a "$GOCCIA_LOADER" tests/fixtures/doom-smoke.ts + xvfb-run -a "$GOCCIA_RUNNER" -P tests/fixtures/doom-smoke.ts test -s .cache/doom-smoke.png - name: Goccia-executed DOOM GPL smoke if: runner.os == 'Linux' shell: bash - run: xvfb-run -a make -C examples/doom-gpl smoke + run: | + make -C examples/doom-gpl test-palette + xvfb-run -a make -C examples/doom-gpl smoke GOCCIA_FLAGS=-P diff --git a/.gitignore b/.gitignore index e1c5fc7..ca34ba8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .cache/ +.agent/waits/ dist/ node_modules/ *.tgz diff --git a/README.md b/README.md index 4183224..f468927 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ # GocciaScript raylib Generated, ABI-checked [raylib 6.0](https://github.com/raysan5/raylib/releases/tag/6.0) -bindings for [GocciaScript 0.10.0](https://github.com/frostney/GocciaScript/releases/tag/0.10.0). +bindings for [GocciaScript 0.14.0](https://github.com/frostney/GocciaScript/releases/tag/0.14.0). The bindings load raylib as a dynamic library and keep unsafe FFI use explicit. ## What is included -- deterministic TypeScript bindings and declarations generated from the pinned - official `raylib_api.json`; +- deterministic TypeScript bindings and declarations for all 600 functions of + the pinned official `raylib_api.json`; - all 35 native struct layouts checked against `raylib.h`; -- a machine-readable stable-runtime skip report; +- a machine-readable skip report, currently empty; - macOS and Linux dynamic-library discovery; - typed basic-window, keyboard-input, embedded-image, Bunnymark, and playable raycasting examples, all with an on-screen FPS counter; @@ -17,9 +17,10 @@ The bindings load raylib as a dynamic library and keep unsafe FFI use explicit. execute inside GocciaScript, with dynamic raylib used only for platform I/O; - package, generator, ABI, and representative native-call validation. -GocciaScript reserves aggregate properties named `buffer` and `byteOffset`. -raylib's `AudioStream.buffer` is therefore exposed as `nativeBuffer`; the -generated `RAYLIB_FIELD_ALIASES` object records this sole 6.0 rename. +Struct fields keep their exact native names. `AudioStream.buffer` is therefore +raylib's pointer field rather than the aggregate's backing store, which +`FFI.metadata(value)` returns as `buffer`, `byteOffset`, and `size` for every +aggregate. `const char *` values and non-owned text returns use GocciaScript's UTF-8 string descriptor. Mutable `char *` arguments and owned `char *` returns remain @@ -27,24 +28,31 @@ raw pointers so callers can mutate them and return the exact allocation to `UnloadFileText`, `UnloadUTF8`, or `MemFree`. Pointer arguments accept native pointers, buffers, typed arrays, aggregates, and `null`. -Stable FFI cannot express a parameter that accepts either a JavaScript string -or a null pointer with one descriptor. `LoadShaderRaw`, -`LoadShaderFromMemoryRaw`, and `LoadAutomationEventListRaw` are therefore -generated as additional raw-pointer bindings for their documented nullable -C-string parameters. The four text-replacement APIs likewise expose `Raw` -variants for their nullable replacement argument. The normal exports remain -convenient for non-null strings. - -The official raylib 6.0 API describes 600 functions. Three are intentionally -outside the 0.1 scope (`TraceLog`, `TextFormat`, and `DrawBillboardPro`). -GocciaScript 0.10.0 also rejects functions that mix a top-level `f32` argument -with other top-level argument types. Every other function is generated, and -[SKIPPED.md](SKIPPED.md) records the exact stable-version boundary. No shim is -presented as native coverage. +The documented nullable C-string parameters of `LoadShader`, +`LoadShaderFromMemory`, `LoadAutomationEventList`, and the four +text-replacement APIs use `FFI.nullable("utf8string")`, so they accept a string +or `null`. + +`TraceLog` and `TextFormat` are bound as variadic functions. GocciaScript +requires the C variadic tail as exactly one typed `FFI.varargs` argument, even +when it is empty: + +```ts +TextFormat("%s %03d", FFI.varargs(["utf8string", "i32"], ["raylib", 7])); +TraceLog(LOG_INFO, "ready", FFI.varargs([], [])); +``` + +GocciaScript ships no TypeScript declarations for its `FFI` global. Code that +is type-checked against `bindings/raylib.d.ts` declares it with the exported +`GocciaFFI` interface: `declare const FFI: GocciaFFI;`. + +The official raylib 6.0 API describes 600 functions and all 600 are generated. +[SKIPPED.md](SKIPPED.md) remains the place where a future runtime or generator +limit is recorded. No shim is presented as native coverage. ## Requirements -- GocciaScript 0.10.0 +- GocciaScript 0.14.0 - raylib 6.0 built as a shared/dynamic library - Node.js 24 or newer for host filesystem, hashing, compiler, and packaging orchestration @@ -64,17 +72,56 @@ bindings when raylib lives elsewhere. ## Run an example -The repository `goccia.json` opts into only the unsafe FFI extension. The -examples are TypeScript, which GocciaScript parses as types-as-comments, and +GocciaScript denies host access by default. The repository `goccia.json` +requests only the `ffi` capability, and a config's request applies once you +have reviewed and trusted it: + +```sh +GocciaRunner --trust goccia.json +``` + +Pass `-P` instead to accept the request for a single run without storing +trust. + +## Use from another project + +The package entry point is the generated bindings, so an installed copy is +imported by name: + +```ts +import { InitWindow, closeRaylib } from "@frostney/gocciascript-raylib"; +``` + +GocciaScript resolves a package name only when the importing project grants +`import` for `node_modules`, and it installs the `FFI` global only with the +`ffi` grant. That project's own `goccia.json` therefore needs both, trusted +or accepted with `-P` like the request above: + +```json +{ + "source-type": "module", + "permissions": { + "allow-ffi": true, + "allow-import": ["node_modules=."] + } +} +``` + +Other files in the package, such as `SKIPPED.json`, stay importable by subpath. +Bindings that are instead imported by relative path from outside the importing +project need an `allow-read` grant for `bindings/raylib.ts`. Without the `ffi` +grant the bindings throw a `TypeError` that names it. + +The examples are TypeScript, which GocciaScript parses as types-as-comments, and use `for...of`; neither the traditional-`for` nor `while` compatibility flag is required: ```sh -GocciaScriptLoader examples/basic-window.ts -GocciaScriptLoader examples/basic-input.ts -GocciaScriptLoader examples/image-loading.ts -GocciaScriptLoader examples/bunnymark.ts -GocciaScriptLoader examples/doom-clone.ts +GocciaRunner examples/basic-window.ts +GocciaRunner examples/basic-input.ts +GocciaRunner examples/image-loading.ts +GocciaRunner examples/bunnymark.ts +GocciaRunner examples/doom-clone.ts ``` Bunnymark uses an immutable ESM byte import of the pinned `raybunny.png`; it @@ -85,6 +132,7 @@ contains [an actual DOOM engine example](examples/doom-gpl/README.md) in the separately licensed `examples/doom-gpl/` subproject: ```sh +GocciaRunner --trust examples/doom-gpl/goccia.json make -C examples/doom-gpl run IWAD=/absolute/path/to/doom.wad # Or download checksummed, freely redistributable Freedoom data: make -C examples/doom-gpl run-freedoom @@ -98,8 +146,10 @@ upload, and FPS overlay. No proprietary IWAD is committed, and the entire GPL directory is excluded from the MIT npm tarball. The full engine legitimately uses classic `for`, `while`, and `do...while` -loops. Their compatibility flags are scoped to `examples/doom-gpl/goccia.json`; -the bindings and smaller MIT examples do not enable them. The initial +loops. Their compatibility flags are scoped to `examples/doom-gpl/goccia.json`, +which also requests `ffi` and read access to `bindings/raylib.ts`, because the +bindings live outside the subproject's own directory. The bindings and smaller MIT +examples do not enable those flags. The initial GocciaScript 0.10.0 implementation rendered at roughly 3 FPS on an Apple M1 Max. The packed-palette fallback reaches 4.97 FPS, and presenting the indexed framebuffer through a palette-texture shader reaches 6.32 FPS in controlled @@ -117,15 +167,21 @@ performance case for compatibility loops: | indexed `while` | 4.02 | 6.3692 s | The current `for...of` loop stays, and both legacy loop flags remain disabled. -That loop experiment predates the application-side import aliases and the -public-raylib instanced drawing path. With the import-binding fast path held -constant, the 10,000-sprite workload now measures 30.35 FPS versus the -12.99-FPS `DrawTextureV` baseline. The renderer uses `DrawMeshInstanced` and a -custom shader through the generated bindings; it has no companion native -library. [Loop methodology](benchmarks/bunnymark-loop-syntax.md), +That loop experiment predates the public-raylib instanced drawing path. With +GocciaScript's import-binding fast path held constant, the 10,000-sprite +workload measured 30.35 FPS versus the 12.99-FPS `DrawTextureV` baseline. The +renderer uses `DrawMeshInstanced` and a custom shader through the generated +bindings; it has no companion native library. + +On GocciaScript 0.10.0 every read of an imported binding was slow enough that +the example copied its imports into local constants. GocciaScript 0.11.0 and +later retain resolved import bindings, so the example now calls its imports +directly. +[Loop methodology](benchmarks/bunnymark-loop-syntax.md), [the preceding application baseline](benchmarks/application-performance.md), -and [the instancing validation](benchmarks/bunnymark-instancing.md) are -recorded in the repository. +[the instancing validation](benchmarks/bunnymark-instancing.md), and +[the import-alias removal](benchmarks/import-binding-aliases.md) are recorded +in the repository. ## Project scripts and tests @@ -133,12 +189,11 @@ The pure API-repair and normalization core is GocciaScript-compatible TypeScript in `scripts/lib/api-core.ts`, and the unit suite runs in GocciaScript's own test runner in both interpreter and bytecode modes. -The thin `.mjs` host adapters remain Node scripts because regeneration must -read and write the real checkout, compute SHA-256 digests, invoke the C -compiler, and inspect `npm pack`. Stable `GocciaScriptLoader` intentionally -does not expose ambient host filesystem or process APIs; the sandbox runner -uses a virtual filesystem whose writes are returned as a diff rather than -materialized into the checkout. +The thin `.mjs` host adapters remain Node scripts because regeneration and +validation must compute SHA-256 digests, invoke the C compiler, and inspect +`npm pack`. `GocciaRunner` exposes no host process or hashing API. Its sandbox +mode can write regenerated files back to the checkout through `--copy-rw`, but +that would move only the file I/O into GocciaScript, not those steps. ## Regenerate and validate @@ -149,14 +204,20 @@ npm test npm run check:generated npm run test:types -GOCCIA_LOADER=/path/to/GocciaScriptLoader npm run test:abi -GOCCIA_LOADER=/path/to/GocciaScriptLoader npm run test:ffi +GOCCIA_RUNNER=/path/to/GocciaRunner npm run test:abi +GOCCIA_RUNNER=/path/to/GocciaRunner npm run test:ffi npm run pack:check ``` -`RAYLIB_INCLUDE_DIR` may point at a non-standard header directory for the ABI -test. Regeneration verifies the pinned commit, checksums, repaired JSON defect, -inventory, and output determinism before writing artifacts. +`npm run test:types` checks the declarations and a small consumer program +against them. `RAYLIB_INCLUDE_DIR` may point at a non-standard header directory +for the ABI test. `GOCCIA_TEST_RUNNER` selects the `GocciaTestRunner` binary +for `npm test` in the same way. The unit suite runs with +`--ignore-config-permissions` because it needs no capability, and the +native-call tests accept the config's `ffi` request for their own run, so +neither depends on a trust store. Regeneration verifies the pinned commit, +checksums, repaired JSON defect, inventory, and output determinism before +writing artifacts. The Linux release lane can also be reproduced from macOS or Linux with: diff --git a/SKIPPED.json b/SKIPPED.json index a951f40..a608440 100644 --- a/SKIPPED.json +++ b/SKIPPED.json @@ -1,684 +1,10 @@ { - "schemaVersion": 1, + "schemaVersion": 2, "raylibVersion": "6.0", "raylibCommit": "dbc56a87da87d973a9c5baa4e7438a9d20121d28", - "gocciaScriptVersion": "0.10.0", + "gocciaScriptVersion": "0.14.0", "apiFunctions": 600, - "callableTargetAfterUpstreamSupport": 597, - "generatedFunctions": 488, - "skippedFunctions": 112, - "skipped": [ - { - "name": "TraceLog", - "reason": "varargs", - "category": "intentional-0.1-scope", - "signature": "void TraceLog(int, const char *, ...)" - }, - { - "name": "SetGamepadVibration", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void SetGamepadVibration(int, float, float, float)" - }, - { - "name": "UpdateCameraPro", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void UpdateCameraPro(Camera *, Vector3, Vector3, float)" - }, - { - "name": "DrawLineEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawLineEx(Vector2, Vector2, float, Color)" - }, - { - "name": "DrawLineBezier", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawLineBezier(Vector2, Vector2, float, Color)" - }, - { - "name": "DrawCircle", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCircle(int, int, float, Color)" - }, - { - "name": "DrawCircleV", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCircleV(Vector2, float, Color)" - }, - { - "name": "DrawCircleGradient", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCircleGradient(Vector2, float, Color, Color)" - }, - { - "name": "DrawCircleSector", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCircleSector(Vector2, float, float, float, int, Color)" - }, - { - "name": "DrawCircleSectorLines", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCircleSectorLines(Vector2, float, float, float, int, Color)" - }, - { - "name": "DrawCircleLines", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCircleLines(int, int, float, Color)" - }, - { - "name": "DrawCircleLinesV", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCircleLinesV(Vector2, float, Color)" - }, - { - "name": "DrawEllipse", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawEllipse(int, int, float, float, Color)" - }, - { - "name": "DrawEllipseV", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawEllipseV(Vector2, float, float, Color)" - }, - { - "name": "DrawEllipseLines", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawEllipseLines(int, int, float, float, Color)" - }, - { - "name": "DrawEllipseLinesV", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawEllipseLinesV(Vector2, float, float, Color)" - }, - { - "name": "DrawRing", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawRing(Vector2, float, float, float, float, int, Color)" - }, - { - "name": "DrawRingLines", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawRingLines(Vector2, float, float, float, float, int, Color)" - }, - { - "name": "DrawRectanglePro", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawRectanglePro(Rectangle, Vector2, float, Color)" - }, - { - "name": "DrawRectangleLinesEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawRectangleLinesEx(Rectangle, float, Color)" - }, - { - "name": "DrawRectangleRounded", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawRectangleRounded(Rectangle, float, int, Color)" - }, - { - "name": "DrawRectangleRoundedLines", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawRectangleRoundedLines(Rectangle, float, int, Color)" - }, - { - "name": "DrawRectangleRoundedLinesEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawRectangleRoundedLinesEx(Rectangle, float, int, float, Color)" - }, - { - "name": "DrawPoly", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawPoly(Vector2, int, float, float, Color)" - }, - { - "name": "DrawPolyLines", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawPolyLines(Vector2, int, float, float, Color)" - }, - { - "name": "DrawPolyLinesEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawPolyLinesEx(Vector2, int, float, float, float, Color)" - }, - { - "name": "DrawSplineLinear", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawSplineLinear(const Vector2 *, int, float, Color)" - }, - { - "name": "DrawSplineBasis", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawSplineBasis(const Vector2 *, int, float, Color)" - }, - { - "name": "DrawSplineCatmullRom", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawSplineCatmullRom(const Vector2 *, int, float, Color)" - }, - { - "name": "DrawSplineBezierQuadratic", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawSplineBezierQuadratic(const Vector2 *, int, float, Color)" - }, - { - "name": "DrawSplineBezierCubic", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawSplineBezierCubic(const Vector2 *, int, float, Color)" - }, - { - "name": "DrawSplineSegmentLinear", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawSplineSegmentLinear(Vector2, Vector2, float, Color)" - }, - { - "name": "DrawSplineSegmentBasis", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawSplineSegmentBasis(Vector2, Vector2, Vector2, Vector2, float, Color)" - }, - { - "name": "DrawSplineSegmentCatmullRom", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawSplineSegmentCatmullRom(Vector2, Vector2, Vector2, Vector2, float, Color)" - }, - { - "name": "DrawSplineSegmentBezierQuadratic", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawSplineSegmentBezierQuadratic(Vector2, Vector2, Vector2, float, Color)" - }, - { - "name": "DrawSplineSegmentBezierCubic", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawSplineSegmentBezierCubic(Vector2, Vector2, Vector2, Vector2, float, Color)" - }, - { - "name": "GetSplinePointLinear", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Vector2 GetSplinePointLinear(Vector2, Vector2, float)" - }, - { - "name": "GetSplinePointBasis", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Vector2 GetSplinePointBasis(Vector2, Vector2, Vector2, Vector2, float)" - }, - { - "name": "GetSplinePointCatmullRom", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Vector2 GetSplinePointCatmullRom(Vector2, Vector2, Vector2, Vector2, float)" - }, - { - "name": "GetSplinePointBezierQuad", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Vector2 GetSplinePointBezierQuad(Vector2, Vector2, Vector2, float)" - }, - { - "name": "GetSplinePointBezierCubic", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Vector2 GetSplinePointBezierCubic(Vector2, Vector2, Vector2, Vector2, float)" - }, - { - "name": "CheckCollisionCircles", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "bool CheckCollisionCircles(Vector2, float, Vector2, float)" - }, - { - "name": "CheckCollisionCircleRec", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "bool CheckCollisionCircleRec(Vector2, float, Rectangle)" - }, - { - "name": "CheckCollisionCircleLine", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "bool CheckCollisionCircleLine(Vector2, float, Vector2, Vector2)" - }, - { - "name": "CheckCollisionPointCircle", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "bool CheckCollisionPointCircle(Vector2, Vector2, float)" - }, - { - "name": "GenImageGradientRadial", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Image GenImageGradientRadial(int, int, float, Color, Color)" - }, - { - "name": "GenImageGradientSquare", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Image GenImageGradientSquare(int, int, float, Color, Color)" - }, - { - "name": "GenImageWhiteNoise", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Image GenImageWhiteNoise(int, int, float)" - }, - { - "name": "GenImagePerlinNoise", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Image GenImagePerlinNoise(int, int, int, int, float)" - }, - { - "name": "ImageTextEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Image ImageTextEx(Font, const char *, float, float, Color)" - }, - { - "name": "ImageAlphaCrop", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void ImageAlphaCrop(Image *, float)" - }, - { - "name": "ImageAlphaClear", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void ImageAlphaClear(Image *, Color, float)" - }, - { - "name": "ImageColorContrast", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void ImageColorContrast(Image *, float)" - }, - { - "name": "GetImageAlphaBorder", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Rectangle GetImageAlphaBorder(Image, float)" - }, - { - "name": "ImageDrawTextEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void ImageDrawTextEx(Image *, Font, const char *, Vector2, float, float, Color)" - }, - { - "name": "DrawTextureEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawTextureEx(Texture2D, Vector2, float, float, Color)" - }, - { - "name": "DrawTexturePro", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawTexturePro(Texture2D, Rectangle, Rectangle, Vector2, float, Color)" - }, - { - "name": "DrawTextureNPatch", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawTextureNPatch(Texture2D, NPatchInfo, Rectangle, Vector2, float, Color)" - }, - { - "name": "Fade", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Color Fade(Color, float)" - }, - { - "name": "ColorBrightness", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Color ColorBrightness(Color, float)" - }, - { - "name": "ColorContrast", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Color ColorContrast(Color, float)" - }, - { - "name": "ColorAlpha", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Color ColorAlpha(Color, float)" - }, - { - "name": "ColorLerp", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Color ColorLerp(Color, Color, float)" - }, - { - "name": "DrawTextEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawTextEx(Font, const char *, Vector2, float, float, Color)" - }, - { - "name": "DrawTextPro", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawTextPro(Font, const char *, Vector2, Vector2, float, float, float, Color)" - }, - { - "name": "DrawTextCodepoint", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawTextCodepoint(Font, int, Vector2, float, Color)" - }, - { - "name": "DrawTextCodepoints", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawTextCodepoints(Font, const int *, int, Vector2, float, float, Color)" - }, - { - "name": "MeasureTextEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Vector2 MeasureTextEx(Font, const char *, float, float)" - }, - { - "name": "MeasureTextCodepoints", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Vector2 MeasureTextCodepoints(Font, const int *, int, float, float)" - }, - { - "name": "TextFormat", - "reason": "varargs", - "category": "intentional-0.1-scope", - "signature": "const char * TextFormat(const char *, ...)" - }, - { - "name": "DrawCircle3D", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCircle3D(Vector3, float, Vector3, float, Color)" - }, - { - "name": "DrawCube", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCube(Vector3, float, float, float, Color)" - }, - { - "name": "DrawCubeWires", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCubeWires(Vector3, float, float, float, Color)" - }, - { - "name": "DrawSphere", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawSphere(Vector3, float, Color)" - }, - { - "name": "DrawSphereEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawSphereEx(Vector3, float, int, int, Color)" - }, - { - "name": "DrawSphereWires", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawSphereWires(Vector3, float, int, int, Color)" - }, - { - "name": "DrawCylinder", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCylinder(Vector3, float, float, float, int, Color)" - }, - { - "name": "DrawCylinderEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCylinderEx(Vector3, Vector3, float, float, int, Color)" - }, - { - "name": "DrawCylinderWires", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCylinderWires(Vector3, float, float, float, int, Color)" - }, - { - "name": "DrawCylinderWiresEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCylinderWiresEx(Vector3, Vector3, float, float, int, Color)" - }, - { - "name": "DrawCapsule", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCapsule(Vector3, Vector3, float, int, int, Color)" - }, - { - "name": "DrawCapsuleWires", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCapsuleWires(Vector3, Vector3, float, int, int, Color)" - }, - { - "name": "DrawGrid", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawGrid(int, float)" - }, - { - "name": "DrawModel", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawModel(Model, Vector3, float, Color)" - }, - { - "name": "DrawModelEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawModelEx(Model, Vector3, Vector3, float, Vector3, Color)" - }, - { - "name": "DrawModelWires", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawModelWires(Model, Vector3, float, Color)" - }, - { - "name": "DrawModelWiresEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawModelWiresEx(Model, Vector3, Vector3, float, Vector3, Color)" - }, - { - "name": "DrawBillboard", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawBillboard(Camera, Texture2D, Vector3, float, Color)" - }, - { - "name": "DrawBillboardPro", - "reason": "more-than-8-arguments", - "category": "intentional-0.1-scope", - "signature": "void DrawBillboardPro(Camera, Texture2D, Rectangle, Vector3, Vector3, Vector2, Vector2, float, Color)" - }, - { - "name": "GenMeshPoly", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Mesh GenMeshPoly(int, float)" - }, - { - "name": "GenMeshPlane", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Mesh GenMeshPlane(float, float, int, int)" - }, - { - "name": "GenMeshSphere", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Mesh GenMeshSphere(float, int, int)" - }, - { - "name": "GenMeshHemiSphere", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Mesh GenMeshHemiSphere(float, int, int)" - }, - { - "name": "GenMeshCylinder", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Mesh GenMeshCylinder(float, float, int)" - }, - { - "name": "GenMeshCone", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Mesh GenMeshCone(float, float, int)" - }, - { - "name": "GenMeshTorus", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Mesh GenMeshTorus(float, float, int, int)" - }, - { - "name": "GenMeshKnot", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Mesh GenMeshKnot(float, float, int, int)" - }, - { - "name": "UpdateModelAnimation", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void UpdateModelAnimation(Model, ModelAnimation, float)" - }, - { - "name": "UpdateModelAnimationEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void UpdateModelAnimationEx(Model, ModelAnimation, float, ModelAnimation, float, float)" - }, - { - "name": "CheckCollisionSpheres", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "bool CheckCollisionSpheres(Vector3, float, Vector3, float)" - }, - { - "name": "CheckCollisionBoxSphere", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "bool CheckCollisionBoxSphere(BoundingBox, Vector3, float)" - }, - { - "name": "GetRayCollisionSphere", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "RayCollision GetRayCollisionSphere(Ray, Vector3, float)" - }, - { - "name": "SetSoundVolume", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void SetSoundVolume(Sound, float)" - }, - { - "name": "SetSoundPitch", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void SetSoundPitch(Sound, float)" - }, - { - "name": "SetSoundPan", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void SetSoundPan(Sound, float)" - }, - { - "name": "SeekMusicStream", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void SeekMusicStream(Music, float)" - }, - { - "name": "SetMusicVolume", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void SetMusicVolume(Music, float)" - }, - { - "name": "SetMusicPitch", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void SetMusicPitch(Music, float)" - }, - { - "name": "SetMusicPan", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void SetMusicPan(Music, float)" - }, - { - "name": "SetAudioStreamVolume", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void SetAudioStreamVolume(AudioStream, float)" - }, - { - "name": "SetAudioStreamPitch", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void SetAudioStreamPitch(AudioStream, float)" - }, - { - "name": "SetAudioStreamPan", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void SetAudioStreamPan(AudioStream, float)" - } - ] + "generatedFunctions": 600, + "skippedFunctions": 0, + "skipped": [] } diff --git a/SKIPPED.md b/SKIPPED.md index e9af94f..c2bb30d 100644 --- a/SKIPPED.md +++ b/SKIPPED.md @@ -1,134 +1,11 @@ # Skipped raylib functions Generated against raylib 6.0 at `dbc56a87da87d973a9c5baa4e7438a9d20121d28` and -GocciaScript 0.10.0. +GocciaScript 0.14.0. - Official API functions: 600 -- Generated and callable on the pinned stable runtime: 488 -- Deterministically skipped: 112 -- Target after mixed-top-level-`f32` support: 597 +- Generated and callable on the pinned stable runtime: 600 +- Deterministically skipped: 0 -The report is a compatibility boundary, not a claim that skipped functions are -implemented. Re-run the generator after the upstream FFI restriction is removed. -The three intentional 0.1 exclusions remain out of scope. - -## intentional-0.1-scope: varargs (2) - -- `void TraceLog(int, const char *, ...)` -- `const char * TextFormat(const char *, ...)` - -## stable-runtime-limit: mixed-top-level-f32 (109) - -- `void SetGamepadVibration(int, float, float, float)` -- `void UpdateCameraPro(Camera *, Vector3, Vector3, float)` -- `void DrawLineEx(Vector2, Vector2, float, Color)` -- `void DrawLineBezier(Vector2, Vector2, float, Color)` -- `void DrawCircle(int, int, float, Color)` -- `void DrawCircleV(Vector2, float, Color)` -- `void DrawCircleGradient(Vector2, float, Color, Color)` -- `void DrawCircleSector(Vector2, float, float, float, int, Color)` -- `void DrawCircleSectorLines(Vector2, float, float, float, int, Color)` -- `void DrawCircleLines(int, int, float, Color)` -- `void DrawCircleLinesV(Vector2, float, Color)` -- `void DrawEllipse(int, int, float, float, Color)` -- `void DrawEllipseV(Vector2, float, float, Color)` -- `void DrawEllipseLines(int, int, float, float, Color)` -- `void DrawEllipseLinesV(Vector2, float, float, Color)` -- `void DrawRing(Vector2, float, float, float, float, int, Color)` -- `void DrawRingLines(Vector2, float, float, float, float, int, Color)` -- `void DrawRectanglePro(Rectangle, Vector2, float, Color)` -- `void DrawRectangleLinesEx(Rectangle, float, Color)` -- `void DrawRectangleRounded(Rectangle, float, int, Color)` -- `void DrawRectangleRoundedLines(Rectangle, float, int, Color)` -- `void DrawRectangleRoundedLinesEx(Rectangle, float, int, float, Color)` -- `void DrawPoly(Vector2, int, float, float, Color)` -- `void DrawPolyLines(Vector2, int, float, float, Color)` -- `void DrawPolyLinesEx(Vector2, int, float, float, float, Color)` -- `void DrawSplineLinear(const Vector2 *, int, float, Color)` -- `void DrawSplineBasis(const Vector2 *, int, float, Color)` -- `void DrawSplineCatmullRom(const Vector2 *, int, float, Color)` -- `void DrawSplineBezierQuadratic(const Vector2 *, int, float, Color)` -- `void DrawSplineBezierCubic(const Vector2 *, int, float, Color)` -- `void DrawSplineSegmentLinear(Vector2, Vector2, float, Color)` -- `void DrawSplineSegmentBasis(Vector2, Vector2, Vector2, Vector2, float, Color)` -- `void DrawSplineSegmentCatmullRom(Vector2, Vector2, Vector2, Vector2, float, Color)` -- `void DrawSplineSegmentBezierQuadratic(Vector2, Vector2, Vector2, float, Color)` -- `void DrawSplineSegmentBezierCubic(Vector2, Vector2, Vector2, Vector2, float, Color)` -- `Vector2 GetSplinePointLinear(Vector2, Vector2, float)` -- `Vector2 GetSplinePointBasis(Vector2, Vector2, Vector2, Vector2, float)` -- `Vector2 GetSplinePointCatmullRom(Vector2, Vector2, Vector2, Vector2, float)` -- `Vector2 GetSplinePointBezierQuad(Vector2, Vector2, Vector2, float)` -- `Vector2 GetSplinePointBezierCubic(Vector2, Vector2, Vector2, Vector2, float)` -- `bool CheckCollisionCircles(Vector2, float, Vector2, float)` -- `bool CheckCollisionCircleRec(Vector2, float, Rectangle)` -- `bool CheckCollisionCircleLine(Vector2, float, Vector2, Vector2)` -- `bool CheckCollisionPointCircle(Vector2, Vector2, float)` -- `Image GenImageGradientRadial(int, int, float, Color, Color)` -- `Image GenImageGradientSquare(int, int, float, Color, Color)` -- `Image GenImageWhiteNoise(int, int, float)` -- `Image GenImagePerlinNoise(int, int, int, int, float)` -- `Image ImageTextEx(Font, const char *, float, float, Color)` -- `void ImageAlphaCrop(Image *, float)` -- `void ImageAlphaClear(Image *, Color, float)` -- `void ImageColorContrast(Image *, float)` -- `Rectangle GetImageAlphaBorder(Image, float)` -- `void ImageDrawTextEx(Image *, Font, const char *, Vector2, float, float, Color)` -- `void DrawTextureEx(Texture2D, Vector2, float, float, Color)` -- `void DrawTexturePro(Texture2D, Rectangle, Rectangle, Vector2, float, Color)` -- `void DrawTextureNPatch(Texture2D, NPatchInfo, Rectangle, Vector2, float, Color)` -- `Color Fade(Color, float)` -- `Color ColorBrightness(Color, float)` -- `Color ColorContrast(Color, float)` -- `Color ColorAlpha(Color, float)` -- `Color ColorLerp(Color, Color, float)` -- `void DrawTextEx(Font, const char *, Vector2, float, float, Color)` -- `void DrawTextPro(Font, const char *, Vector2, Vector2, float, float, float, Color)` -- `void DrawTextCodepoint(Font, int, Vector2, float, Color)` -- `void DrawTextCodepoints(Font, const int *, int, Vector2, float, float, Color)` -- `Vector2 MeasureTextEx(Font, const char *, float, float)` -- `Vector2 MeasureTextCodepoints(Font, const int *, int, float, float)` -- `void DrawCircle3D(Vector3, float, Vector3, float, Color)` -- `void DrawCube(Vector3, float, float, float, Color)` -- `void DrawCubeWires(Vector3, float, float, float, Color)` -- `void DrawSphere(Vector3, float, Color)` -- `void DrawSphereEx(Vector3, float, int, int, Color)` -- `void DrawSphereWires(Vector3, float, int, int, Color)` -- `void DrawCylinder(Vector3, float, float, float, int, Color)` -- `void DrawCylinderEx(Vector3, Vector3, float, float, int, Color)` -- `void DrawCylinderWires(Vector3, float, float, float, int, Color)` -- `void DrawCylinderWiresEx(Vector3, Vector3, float, float, int, Color)` -- `void DrawCapsule(Vector3, Vector3, float, int, int, Color)` -- `void DrawCapsuleWires(Vector3, Vector3, float, int, int, Color)` -- `void DrawGrid(int, float)` -- `void DrawModel(Model, Vector3, float, Color)` -- `void DrawModelEx(Model, Vector3, Vector3, float, Vector3, Color)` -- `void DrawModelWires(Model, Vector3, float, Color)` -- `void DrawModelWiresEx(Model, Vector3, Vector3, float, Vector3, Color)` -- `void DrawBillboard(Camera, Texture2D, Vector3, float, Color)` -- `Mesh GenMeshPoly(int, float)` -- `Mesh GenMeshPlane(float, float, int, int)` -- `Mesh GenMeshSphere(float, int, int)` -- `Mesh GenMeshHemiSphere(float, int, int)` -- `Mesh GenMeshCylinder(float, float, int)` -- `Mesh GenMeshCone(float, float, int)` -- `Mesh GenMeshTorus(float, float, int, int)` -- `Mesh GenMeshKnot(float, float, int, int)` -- `void UpdateModelAnimation(Model, ModelAnimation, float)` -- `void UpdateModelAnimationEx(Model, ModelAnimation, float, ModelAnimation, float, float)` -- `bool CheckCollisionSpheres(Vector3, float, Vector3, float)` -- `bool CheckCollisionBoxSphere(BoundingBox, Vector3, float)` -- `RayCollision GetRayCollisionSphere(Ray, Vector3, float)` -- `void SetSoundVolume(Sound, float)` -- `void SetSoundPitch(Sound, float)` -- `void SetSoundPan(Sound, float)` -- `void SeekMusicStream(Music, float)` -- `void SetMusicVolume(Music, float)` -- `void SetMusicPitch(Music, float)` -- `void SetMusicPan(Music, float)` -- `void SetAudioStreamVolume(AudioStream, float)` -- `void SetAudioStreamPitch(AudioStream, float)` -- `void SetAudioStreamPan(AudioStream, float)` - -## intentional-0.1-scope: more-than-8-arguments (1) - -- `void DrawBillboardPro(Camera, Texture2D, Rectangle, Vector3, Vector3, Vector2, Vector2, float, Color)` +The pinned runtime binds the complete official API. A function that a future +raylib or runtime version cannot bind is listed here rather than shimmed. diff --git a/benchmarks/import-binding-aliases.md b/benchmarks/import-binding-aliases.md new file mode 100644 index 0000000..6b30477 --- /dev/null +++ b/benchmarks/import-binding-aliases.md @@ -0,0 +1,79 @@ +# Import-binding alias removal + +Measured on 2026-10-01 while updating the bindings from GocciaScript 0.10.0 to +0.14.0, to decide whether Bunnymark should keep copying its raylib imports +into local constants. + +## Setup + +- AMD Ryzen 9 6900HX, Linux Mint 22.3 (x86-64) +- GocciaScript 0.10.0 and 0.14.0 release binaries, bytecode mode +- dynamically loaded raylib 6.0 at the pinned commit +- Xvfb with Mesa llvmpipe 25.2.8 (software OpenGL 4.5), so absolute frame rates + are not comparable with the Apple M1 Max results in the other reports + +## Imported binding versus local alias + +A module exports `add` and a numeric constant. A second module calls +`add(sum, K)` 300,000 times, once through the imported bindings and once +through module-level `const` copies of them. Each variant ran three times in +one process; the three runs agreed within 0.4%. + +| GocciaScript | Through imports | Through local aliases | Ratio | +|---|---:|---:|---:| +| 0.10.0 | 5773.1 ms | 192.6 ms | 29.97x | +| 0.14.0 | 218.7 ms | 149.1 ms | 1.47x | + +Each iteration reads two imported bindings. Compared with a local read, one +imported read cost about 9.3 microseconds more on 0.10.0 and costs about +0.12 microseconds more on 0.14.0. GocciaScript 0.11.0 lists the change as "retain resolved import +bindings". + +## Bunnymark with and without aliases + +The 0.14.0 example was run with the alias block from the previous revision and +with direct imports: 10,000 fixed-seed sprites (`1234`), uncapped target, +25 frames per run, three interleaved runs per variant. The values are the +example's own 25-frame time after startup. + +| Draw path | Direct imports | Local aliases | +|---|---|---| +| `DrawTextureV` | 2803, 2784, 2908 ms (mean 2831.7) | 2733, 2852, 2831 ms (mean 2805.3) | +| `DrawMeshInstanced` | 1118, 1142, 1196 ms (mean 1152.0) | 1125, 1168, 1184 ms (mean 1159.0) | + +The means differ by 0.9% on the per-sprite `DrawTextureV` path and by 0.6% in +the other direction on the instanced path. Both differences are smaller than +the spread between runs of one variant. + +For the whole update, the previous revision on 0.10.0 (aliases, heightmap +quad) was compared with this revision on 0.14.0 (direct imports, plane quad) +under the same workload, three interleaved runs each: + +| Draw path | Previous revision, 0.10.0 | This revision, 0.14.0 | +|---|---|---| +| `DrawTextureV` | 2946, 2916, 2940 ms (mean 2934.0) | 2875, 2877, 2877 ms (mean 2876.3) | +| `DrawMeshInstanced` | 1260, 1290, 1268 ms (mean 1272.7) | 1182, 1181, 1176 ms (mean 1179.7) | + +## Decision + +The alias block is removed. It no longer changes the measured frame time, and +the instanced path, which is the default, reads no imported binding per +sprite. + +## Rendering equivalence + +The same update replaced two other 0.10.0 workarounds with functions that +0.10.0 could not bind because they mix `float` with other argument types. +Both replacements were checked against the previous rendering: + +- Bunnymark's instanced quad now comes from `GenMeshPlane` instead of a + `GenMeshHeightmap` call on a throwaway 2x2 image. The graphical integration + test still finds the instanced and `DrawTextureV` frames byte-identical, with + the SHA-256 recorded for Linux in + [the instancing report](bunnymark-instancing.md): + `e22537b001590b497bad6c8da318d38339bef0fb4cdb95a8c8f449584e47d1a0`. +- DOOM scales its 320x200 texture with `DrawTextureEx` instead of a zoomed + `Camera2D`. Frame 8 of the Freedoom smoke scene, with the FPS overlay + disabled, is byte-identical before and after on both the indexed-shader and + RGBA display paths: + `2fdda7ef7a9f42f95e17a33e5e955618af5afd6b19a282781316024893c506e0`. diff --git a/bindings/raylib.d.ts b/bindings/raylib.d.ts index 6f5df10..cec82b5 100644 --- a/bindings/raylib.d.ts +++ b/bindings/raylib.d.ts @@ -1,5 +1,8 @@ // Generated from official raylib 6.0. Do not edit. +// Only the declarations marked export are part of the module. +export {}; + export interface FFIPointer { readonly address: number; readonly isNull: boolean; @@ -31,29 +34,58 @@ export type FFIPointerInput = | ArrayBuffer | SharedArrayBuffer | FFITypedArray - | FFIAggregateValue + | FFIAggregate | null; -export interface FFIAggregateValue { +declare const ffiAggregate: unique symbol; +/** Any GocciaScript FFI aggregate value. */ +export interface FFIAggregate { + readonly [ffiAggregate]: true; +} +/** Backing store of an aggregate, as returned by GocciaScript's `FFI.metadata(value)`. */ +export interface FFIAggregateMetadata { readonly buffer: ArrayBuffer; readonly byteOffset: number; readonly size: number; } +/** + * Aggregate that also exposes its backing store directly. A native field named + * like a metadata property takes precedence; `FFI.metadata(value)` always works. + */ +export interface FFIAggregateValue extends FFIAggregate, FFIAggregateMetadata {} export interface FFIArrayValue extends FFIAggregateValue { readonly length: number; [index: number]: T; } -export interface FFIStructDescriptor { +export interface FFIStructDescriptor { readonly kind: "struct"; readonly size: number; readonly alignment: number; create(initializer?: Partial): T; } +declare const ffiVarargs: unique symbol; +/** Typed variadic tail created by GocciaScript's `FFI.varargs(types, values)`. */ +export interface FFIVarargs { + readonly [ffiVarargs]: true; +} +/** + * The members of GocciaScript's `FFI` global that callers of these bindings + * use. GocciaScript ships no declarations, so type-checked code declares the + * global itself: `declare const FFI: GocciaFFI;`. + */ +export interface GocciaFFI { + varargs(types: readonly unknown[], values: readonly unknown[]): FFIVarargs; + metadata(value: FFIAggregate): FFIAggregateMetadata; +} export interface FFILibrary { readonly path: string; readonly closed: boolean; bind( name: string, - signature: { args: readonly unknown[]; returns: unknown }, + signature: { + args: readonly unknown[]; + returns: unknown; + variadic?: boolean; + }, ): (...args: unknown[]) => unknown; symbol(name: string): FFIPointer; close(): void; @@ -64,9 +96,6 @@ export declare const RAYLIB_BINDING_INFO: { gocciaScriptVersion: string; linkage: "dynamic"; }; -export declare const RAYLIB_FIELD_ALIASES: { - AudioStream: { buffer: "nativeBuffer" }; -}; export declare const raylibLibrary: FFILibrary; export declare function closeRaylib(): void; @@ -336,9 +365,9 @@ export interface WaveValue extends FFIAggregateValue { export declare const Wave: FFIStructDescriptor; /** AudioStream, custom audio stream */ -export interface AudioStreamValue extends FFIAggregateValue { - /** Native C field: buffer. */ - "nativeBuffer": FFIPointer; +export interface AudioStreamValue extends FFIAggregate, Omit { + /** Native C field; read the backing store with `FFI.metadata(value).buffer`. */ + "buffer": FFIPointer; "processor": FFIPointer; "sampleRate": number; "sampleSize": number; @@ -1374,16 +1403,10 @@ export declare function LoadVrStereoConfig(device: VrDeviceInfoValue): VrStereoC export declare function UnloadVrStereoConfig(config: VrStereoConfigValue): void; /** Load shader from files and bind default locations */ -export declare function LoadShader(vsFileName: string, fsFileName: string): ShaderValue; - -/** Raw-pointer variant for nullable C string parameters. */ -export declare function LoadShaderRaw(vsFileName: FFIPointerInput, fsFileName: FFIPointerInput): ShaderValue; +export declare function LoadShader(vsFileName: string | null, fsFileName: string | null): ShaderValue; /** Load shader from code strings and bind default locations */ -export declare function LoadShaderFromMemory(vsCode: string, fsCode: string): ShaderValue; - -/** Raw-pointer variant for nullable C string parameters. */ -export declare function LoadShaderFromMemoryRaw(vsCode: FFIPointerInput, fsCode: FFIPointerInput): ShaderValue; +export declare function LoadShaderFromMemory(vsCode: string | null, fsCode: string | null): ShaderValue; /** Check if a shader is valid (loaded on GPU) */ export declare function IsShaderValid(shader: ShaderValue): boolean; @@ -1478,6 +1501,9 @@ export declare function OpenURL(url: string): void; /** Set the current threshold (minimum) log level */ export declare function SetTraceLogLevel(logLevel: number): void; +/** Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...) */ +export declare function TraceLog(logLevel: number, text: string, args: FFIVarargs): void; + /** Set custom trace log */ export declare function SetTraceLogCallback(callback: FFIPointerInput): void; @@ -1638,10 +1664,7 @@ export declare function ComputeSHA1(data: FFIPointerInput, dataSize: number): FF export declare function ComputeSHA256(data: FFIPointerInput, dataSize: number): FFIPointer; /** Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS */ -export declare function LoadAutomationEventList(fileName: string): AutomationEventListValue; - -/** Raw-pointer variant for nullable C string parameters. */ -export declare function LoadAutomationEventListRaw(fileName: FFIPointerInput): AutomationEventListValue; +export declare function LoadAutomationEventList(fileName: string | null): AutomationEventListValue; /** Unload automation events list from file */ export declare function UnloadAutomationEventList(list: AutomationEventListValue): void; @@ -1721,6 +1744,9 @@ export declare function GetGamepadAxisMovement(gamepad: number, axis: number): n /** Set internal gamepad mappings (SDL_GameControllerDB) */ export declare function SetGamepadMappings(mappings: string): number; +/** Set gamepad vibration for both motors (duration in seconds) */ +export declare function SetGamepadVibration(gamepad: number, leftMotor: number, rightMotor: number, duration: number): void; + /** Check if a mouse button has been pressed once */ export declare function IsMouseButtonPressed(button: number): boolean; @@ -1805,6 +1831,9 @@ export declare function GetGesturePinchAngle(): number; /** Update camera position for selected mode */ export declare function UpdateCamera(camera: FFIPointerInput, mode: number): void; +/** Update camera movement/rotation */ +export declare function UpdateCameraPro(camera: FFIPointerInput, movement: Vector3Value, rotation: Vector3Value, zoom: number): void; + /** Set texture and rectangle to be used on shapes drawing */ export declare function SetShapesTexture(texture: Texture2DValue, source: RectangleValue): void; @@ -1826,12 +1855,57 @@ export declare function DrawLine(startPosX: number, startPosY: number, endPosX: /** Draw a line (using gl lines) */ export declare function DrawLineV(startPos: Vector2Value, endPos: Vector2Value, color: ColorValue): void; +/** Draw a line (using triangles/quads) */ +export declare function DrawLineEx(startPos: Vector2Value, endPos: Vector2Value, thick: number, color: ColorValue): void; + /** Draw lines sequence (using gl lines) */ export declare function DrawLineStrip(points: FFIPointerInput, pointCount: number, color: ColorValue): void; +/** Draw line segment cubic-bezier in-out interpolation */ +export declare function DrawLineBezier(startPos: Vector2Value, endPos: Vector2Value, thick: number, color: ColorValue): void; + /** Draw a dashed line */ export declare function DrawLineDashed(startPos: Vector2Value, endPos: Vector2Value, dashSize: number, spaceSize: number, color: ColorValue): void; +/** Draw a color-filled circle */ +export declare function DrawCircle(centerX: number, centerY: number, radius: number, color: ColorValue): void; + +/** Draw a color-filled circle (Vector version) */ +export declare function DrawCircleV(center: Vector2Value, radius: number, color: ColorValue): void; + +/** Draw a gradient-filled circle */ +export declare function DrawCircleGradient(center: Vector2Value, radius: number, inner: ColorValue, outer: ColorValue): void; + +/** Draw a piece of a circle */ +export declare function DrawCircleSector(center: Vector2Value, radius: number, startAngle: number, endAngle: number, segments: number, color: ColorValue): void; + +/** Draw circle sector outline */ +export declare function DrawCircleSectorLines(center: Vector2Value, radius: number, startAngle: number, endAngle: number, segments: number, color: ColorValue): void; + +/** Draw circle outline */ +export declare function DrawCircleLines(centerX: number, centerY: number, radius: number, color: ColorValue): void; + +/** Draw circle outline (Vector version) */ +export declare function DrawCircleLinesV(center: Vector2Value, radius: number, color: ColorValue): void; + +/** Draw ellipse */ +export declare function DrawEllipse(centerX: number, centerY: number, radiusH: number, radiusV: number, color: ColorValue): void; + +/** Draw ellipse (Vector version) */ +export declare function DrawEllipseV(center: Vector2Value, radiusH: number, radiusV: number, color: ColorValue): void; + +/** Draw ellipse outline */ +export declare function DrawEllipseLines(centerX: number, centerY: number, radiusH: number, radiusV: number, color: ColorValue): void; + +/** Draw ellipse outline (Vector version) */ +export declare function DrawEllipseLinesV(center: Vector2Value, radiusH: number, radiusV: number, color: ColorValue): void; + +/** Draw ring */ +export declare function DrawRing(center: Vector2Value, innerRadius: number, outerRadius: number, startAngle: number, endAngle: number, segments: number, color: ColorValue): void; + +/** Draw ring outline */ +export declare function DrawRingLines(center: Vector2Value, innerRadius: number, outerRadius: number, startAngle: number, endAngle: number, segments: number, color: ColorValue): void; + /** Draw a color-filled rectangle */ export declare function DrawRectangle(posX: number, posY: number, width: number, height: number, color: ColorValue): void; @@ -1841,6 +1915,9 @@ export declare function DrawRectangleV(position: Vector2Value, size: Vector2Valu /** Draw a color-filled rectangle */ export declare function DrawRectangleRec(rec: RectangleValue, color: ColorValue): void; +/** Draw a color-filled rectangle with pro parameters */ +export declare function DrawRectanglePro(rec: RectangleValue, origin: Vector2Value, rotation: number, color: ColorValue): void; + /** Draw a vertical-gradient-filled rectangle */ export declare function DrawRectangleGradientV(posX: number, posY: number, width: number, height: number, top: ColorValue, bottom: ColorValue): void; @@ -1853,6 +1930,18 @@ export declare function DrawRectangleGradientEx(rec: RectangleValue, topLeft: Co /** Draw rectangle outline */ export declare function DrawRectangleLines(posX: number, posY: number, width: number, height: number, color: ColorValue): void; +/** Draw rectangle outline with extended parameters */ +export declare function DrawRectangleLinesEx(rec: RectangleValue, lineThick: number, color: ColorValue): void; + +/** Draw rectangle with rounded edges */ +export declare function DrawRectangleRounded(rec: RectangleValue, roundness: number, segments: number, color: ColorValue): void; + +/** Draw rectangle lines with rounded edges */ +export declare function DrawRectangleRoundedLines(rec: RectangleValue, roundness: number, segments: number, color: ColorValue): void; + +/** Draw rectangle with rounded edges outline */ +export declare function DrawRectangleRoundedLinesEx(rec: RectangleValue, roundness: number, segments: number, lineThick: number, color: ColorValue): void; + /** Draw a color-filled triangle (vertex in counter-clockwise order!) */ export declare function DrawTriangle(v1: Vector2Value, v2: Vector2Value, v3: Vector2Value, color: ColorValue): void; @@ -1865,12 +1954,78 @@ export declare function DrawTriangleFan(points: FFIPointerInput, pointCount: num /** Draw a triangle strip defined by points */ export declare function DrawTriangleStrip(points: FFIPointerInput, pointCount: number, color: ColorValue): void; +/** Draw a regular polygon (Vector version) */ +export declare function DrawPoly(center: Vector2Value, sides: number, radius: number, rotation: number, color: ColorValue): void; + +/** Draw a polygon outline of n sides */ +export declare function DrawPolyLines(center: Vector2Value, sides: number, radius: number, rotation: number, color: ColorValue): void; + +/** Draw a polygon outline of n sides with extended parameters */ +export declare function DrawPolyLinesEx(center: Vector2Value, sides: number, radius: number, rotation: number, lineThick: number, color: ColorValue): void; + +/** Draw spline: Linear, minimum 2 points */ +export declare function DrawSplineLinear(points: FFIPointerInput, pointCount: number, thick: number, color: ColorValue): void; + +/** Draw spline: B-Spline, minimum 4 points */ +export declare function DrawSplineBasis(points: FFIPointerInput, pointCount: number, thick: number, color: ColorValue): void; + +/** Draw spline: Catmull-Rom, minimum 4 points */ +export declare function DrawSplineCatmullRom(points: FFIPointerInput, pointCount: number, thick: number, color: ColorValue): void; + +/** Draw spline: Quadratic Bezier, minimum 3 points (1 control point): [p1, c2, p3, c4...] */ +export declare function DrawSplineBezierQuadratic(points: FFIPointerInput, pointCount: number, thick: number, color: ColorValue): void; + +/** Draw spline: Cubic Bezier, minimum 4 points (2 control points): [p1, c2, c3, p4, c5, c6...] */ +export declare function DrawSplineBezierCubic(points: FFIPointerInput, pointCount: number, thick: number, color: ColorValue): void; + +/** Draw spline segment: Linear, 2 points */ +export declare function DrawSplineSegmentLinear(p1: Vector2Value, p2: Vector2Value, thick: number, color: ColorValue): void; + +/** Draw spline segment: B-Spline, 4 points */ +export declare function DrawSplineSegmentBasis(p1: Vector2Value, p2: Vector2Value, p3: Vector2Value, p4: Vector2Value, thick: number, color: ColorValue): void; + +/** Draw spline segment: Catmull-Rom, 4 points */ +export declare function DrawSplineSegmentCatmullRom(p1: Vector2Value, p2: Vector2Value, p3: Vector2Value, p4: Vector2Value, thick: number, color: ColorValue): void; + +/** Draw spline segment: Quadratic Bezier, 2 points, 1 control point */ +export declare function DrawSplineSegmentBezierQuadratic(p1: Vector2Value, c2: Vector2Value, p3: Vector2Value, thick: number, color: ColorValue): void; + +/** Draw spline segment: Cubic Bezier, 2 points, 2 control points */ +export declare function DrawSplineSegmentBezierCubic(p1: Vector2Value, c2: Vector2Value, c3: Vector2Value, p4: Vector2Value, thick: number, color: ColorValue): void; + +/** Get (evaluate) spline point: Linear */ +export declare function GetSplinePointLinear(startPos: Vector2Value, endPos: Vector2Value, t: number): Vector2Value; + +/** Get (evaluate) spline point: B-Spline */ +export declare function GetSplinePointBasis(p1: Vector2Value, p2: Vector2Value, p3: Vector2Value, p4: Vector2Value, t: number): Vector2Value; + +/** Get (evaluate) spline point: Catmull-Rom */ +export declare function GetSplinePointCatmullRom(p1: Vector2Value, p2: Vector2Value, p3: Vector2Value, p4: Vector2Value, t: number): Vector2Value; + +/** Get (evaluate) spline point: Quadratic Bezier */ +export declare function GetSplinePointBezierQuad(p1: Vector2Value, c2: Vector2Value, p3: Vector2Value, t: number): Vector2Value; + +/** Get (evaluate) spline point: Cubic Bezier */ +export declare function GetSplinePointBezierCubic(p1: Vector2Value, c2: Vector2Value, c3: Vector2Value, p4: Vector2Value, t: number): Vector2Value; + /** Check collision between two rectangles */ export declare function CheckCollisionRecs(rec1: RectangleValue, rec2: RectangleValue): boolean; +/** Check collision between two circles */ +export declare function CheckCollisionCircles(center1: Vector2Value, radius1: number, center2: Vector2Value, radius2: number): boolean; + +/** Check collision between circle and rectangle */ +export declare function CheckCollisionCircleRec(center: Vector2Value, radius: number, rec: RectangleValue): boolean; + +/** Check if circle collides with a line created betweeen two points [p1] and [p2] */ +export declare function CheckCollisionCircleLine(center: Vector2Value, radius: number, p1: Vector2Value, p2: Vector2Value): boolean; + /** Check if point is inside rectangle */ export declare function CheckCollisionPointRec(point: Vector2Value, rec: RectangleValue): boolean; +/** Check if point is inside circle */ +export declare function CheckCollisionPointCircle(point: Vector2Value, center: Vector2Value, radius: number): boolean; + /** Check if point is inside a triangle */ export declare function CheckCollisionPointTriangle(point: Vector2Value, p1: Vector2Value, p2: Vector2Value, p3: Vector2Value): boolean; @@ -1928,9 +2083,21 @@ export declare function GenImageColor(width: number, height: number, color: Colo /** Generate image: linear gradient, direction in degrees [0..360], 0=Vertical gradient */ export declare function GenImageGradientLinear(width: number, height: number, direction: number, start: ColorValue, end: ColorValue): ImageValue; +/** Generate image: radial gradient */ +export declare function GenImageGradientRadial(width: number, height: number, density: number, inner: ColorValue, outer: ColorValue): ImageValue; + +/** Generate image: square gradient */ +export declare function GenImageGradientSquare(width: number, height: number, density: number, inner: ColorValue, outer: ColorValue): ImageValue; + /** Generate image: checked */ export declare function GenImageChecked(width: number, height: number, checksX: number, checksY: number, col1: ColorValue, col2: ColorValue): ImageValue; +/** Generate image: white noise */ +export declare function GenImageWhiteNoise(width: number, height: number, factor: number): ImageValue; + +/** Generate image: perlin noise */ +export declare function GenImagePerlinNoise(width: number, height: number, offsetX: number, offsetY: number, scale: number): ImageValue; + /** Generate image: cellular algorithm, bigger tileSize means bigger cells */ export declare function GenImageCellular(width: number, height: number, tileSize: number): ImageValue; @@ -1949,6 +2116,9 @@ export declare function ImageFromChannel(image: ImageValue, selectedChannel: num /** Create an image from text (default font) */ export declare function ImageText(text: string, fontSize: number, color: ColorValue): ImageValue; +/** Create an image from text (custom sprite font) */ +export declare function ImageTextEx(font: FontValue, text: string, fontSize: number, spacing: number, tint: ColorValue): ImageValue; + /** Convert image data to desired format */ export declare function ImageFormat(image: FFIPointerInput, newFormat: number): void; @@ -1958,6 +2128,12 @@ export declare function ImageToPOT(image: FFIPointerInput, fill: ColorValue): vo /** Crop an image to a defined rectangle */ export declare function ImageCrop(image: FFIPointerInput, crop: RectangleValue): void; +/** Crop image depending on alpha value */ +export declare function ImageAlphaCrop(image: FFIPointerInput, threshold: number): void; + +/** Clear alpha channel to desired color */ +export declare function ImageAlphaClear(image: FFIPointerInput, color: ColorValue, threshold: number): void; + /** Apply alpha mask to image */ export declare function ImageAlphaMask(image: FFIPointerInput, alphaMask: ImageValue): void; @@ -2009,6 +2185,9 @@ export declare function ImageColorInvert(image: FFIPointerInput): void; /** Modify image color: grayscale */ export declare function ImageColorGrayscale(image: FFIPointerInput): void; +/** Modify image color: contrast (-100 to 100) */ +export declare function ImageColorContrast(image: FFIPointerInput, contrast: number): void; + /** Modify image color: brightness (-255 to 255) */ export declare function ImageColorBrightness(image: FFIPointerInput, brightness: number): void; @@ -2027,6 +2206,9 @@ export declare function UnloadImageColors(colors: FFIPointerInput): void; /** Unload colors palette loaded with LoadImagePalette() */ export declare function UnloadImagePalette(colors: FFIPointerInput): void; +/** Get image alpha border rectangle */ +export declare function GetImageAlphaBorder(image: ImageValue, threshold: number): RectangleValue; + /** Get image pixel color at (x, y) position */ export declare function GetImageColor(image: ImageValue, x: number, y: number): ColorValue; @@ -2093,6 +2275,9 @@ export declare function ImageDraw(dst: FFIPointerInput, src: ImageValue, srcRec: /** Draw text (using default font) within an image (destination) */ export declare function ImageDrawText(dst: FFIPointerInput, text: string, posX: number, posY: number, fontSize: number, color: ColorValue): void; +/** Draw text (custom sprite font) within an image (destination) */ +export declare function ImageDrawTextEx(dst: FFIPointerInput, font: FontValue, text: string, position: Vector2Value, fontSize: number, spacing: number, tint: ColorValue): void; + /** Load texture from file into GPU memory (VRAM) */ export declare function LoadTexture(fileName: string): Texture2DValue; @@ -2138,12 +2323,24 @@ export declare function DrawTexture(texture: Texture2DValue, posX: number, posY: /** Draw a Texture2D with position defined as Vector2 */ export declare function DrawTextureV(texture: Texture2DValue, position: Vector2Value, tint: ColorValue): void; +/** Draw a Texture2D with extended parameters */ +export declare function DrawTextureEx(texture: Texture2DValue, position: Vector2Value, rotation: number, scale: number, tint: ColorValue): void; + /** Draw a part of a texture defined by a rectangle */ export declare function DrawTextureRec(texture: Texture2DValue, source: RectangleValue, position: Vector2Value, tint: ColorValue): void; +/** Draw a part of a texture defined by a rectangle with 'pro' parameters */ +export declare function DrawTexturePro(texture: Texture2DValue, source: RectangleValue, dest: RectangleValue, origin: Vector2Value, rotation: number, tint: ColorValue): void; + +/** Draws a texture (or part of it) that stretches or shrinks nicely */ +export declare function DrawTextureNPatch(texture: Texture2DValue, nPatchInfo: NPatchInfoValue, dest: RectangleValue, origin: Vector2Value, rotation: number, tint: ColorValue): void; + /** Check if two colors are equal */ export declare function ColorIsEqual(col1: ColorValue, col2: ColorValue): boolean; +/** Get color with alpha applied, alpha goes from 0.0f to 1.0f */ +export declare function Fade(color: ColorValue, alpha: number): ColorValue; + /** Get hexadecimal value for a Color (0xRRGGBBAA) */ export declare function ColorToInt(color: ColorValue): number; @@ -2162,9 +2359,21 @@ export declare function ColorFromHSV(hue: number, saturation: number, value: num /** Get color multiplied with another color */ export declare function ColorTint(color: ColorValue, tint: ColorValue): ColorValue; +/** Get color with brightness correction, brightness factor goes from -1.0f to 1.0f */ +export declare function ColorBrightness(color: ColorValue, factor: number): ColorValue; + +/** Get color with contrast correction, contrast values between -1.0f and 1.0f */ +export declare function ColorContrast(color: ColorValue, contrast: number): ColorValue; + +/** Get color with alpha applied, alpha goes from 0.0f to 1.0f */ +export declare function ColorAlpha(color: ColorValue, alpha: number): ColorValue; + /** Get src alpha-blended into dst color with tint */ export declare function ColorAlphaBlend(dst: ColorValue, src: ColorValue, tint: ColorValue): ColorValue; +/** Get color lerp interpolation between two colors, factor [0.0f..1.0f] */ +export declare function ColorLerp(color1: ColorValue, color2: ColorValue, factor: number): ColorValue; + /** Get Color structure from hexadecimal value */ export declare function GetColor(hexValue: number): ColorValue; @@ -2216,12 +2425,30 @@ export declare function DrawFPS(posX: number, posY: number): void; /** Draw text (using default font) */ export declare function DrawText(text: string, posX: number, posY: number, fontSize: number, color: ColorValue): void; +/** Draw text using font and additional parameters */ +export declare function DrawTextEx(font: FontValue, text: string, position: Vector2Value, fontSize: number, spacing: number, tint: ColorValue): void; + +/** Draw text using Font and pro parameters (rotation) */ +export declare function DrawTextPro(font: FontValue, text: string, position: Vector2Value, origin: Vector2Value, rotation: number, fontSize: number, spacing: number, tint: ColorValue): void; + +/** Draw one character (codepoint) */ +export declare function DrawTextCodepoint(font: FontValue, codepoint: number, position: Vector2Value, fontSize: number, tint: ColorValue): void; + +/** Draw multiple character (codepoint) */ +export declare function DrawTextCodepoints(font: FontValue, codepoints: FFIPointerInput, codepointCount: number, position: Vector2Value, fontSize: number, spacing: number, tint: ColorValue): void; + /** Set vertical line spacing when drawing with line-breaks */ export declare function SetTextLineSpacing(spacing: number): void; /** Measure string width for default font */ export declare function MeasureText(text: string, fontSize: number): number; +/** Measure string size for Font */ +export declare function MeasureTextEx(font: FontValue, text: string, fontSize: number, spacing: number): Vector2Value; + +/** Measure string size for an existing array of codepoints for Font */ +export declare function MeasureTextCodepoints(font: FontValue, codepoints: FFIPointerInput, length: number, fontSize: number, spacing: number): Vector2Value; + /** Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found */ export declare function GetGlyphIndex(font: FontValue, codepoint: number): number; @@ -2273,6 +2500,9 @@ export declare function TextIsEqual(text1: string, text2: string): boolean; /** Get text length, checks for '\0' ending */ export declare function TextLength(text: string): number; +/** Text formatting with variables (sprintf() style) */ +export declare function TextFormat(text: string, args: FFIVarargs): string; + /** Get a piece of a text string */ export declare function TextSubtext(text: string, position: number, length: number): string; @@ -2283,28 +2513,16 @@ export declare function TextRemoveSpaces(text: string): string; export declare function GetTextBetween(text: string, begin: string, end: string): string; /** Replace text string with new string */ -export declare function TextReplace(text: string, search: string, replacement: string): string; - -/** Raw-pointer variant for nullable C string parameters. */ -export declare function TextReplaceRaw(text: string, search: string, replacement: FFIPointerInput): string; +export declare function TextReplace(text: string, search: string, replacement: string | null): string; /** Replace text string with new string, memory must be MemFree() */ -export declare function TextReplaceAlloc(text: string, search: string, replacement: string): FFIPointer; - -/** Raw-pointer variant for nullable C string parameters. */ -export declare function TextReplaceAllocRaw(text: string, search: string, replacement: FFIPointerInput): FFIPointer; +export declare function TextReplaceAlloc(text: string, search: string, replacement: string | null): FFIPointer; /** Replace text between two specific strings */ -export declare function TextReplaceBetween(text: string, begin: string, end: string, replacement: string): string; - -/** Raw-pointer variant for nullable C string parameters. */ -export declare function TextReplaceBetweenRaw(text: string, begin: string, end: string, replacement: FFIPointerInput): string; +export declare function TextReplaceBetween(text: string, begin: string, end: string, replacement: string | null): string; /** Replace text between two specific strings, memory must be MemFree() */ -export declare function TextReplaceBetweenAlloc(text: string, begin: string, end: string, replacement: string): FFIPointer; - -/** Raw-pointer variant for nullable C string parameters. */ -export declare function TextReplaceBetweenAllocRaw(text: string, begin: string, end: string, replacement: FFIPointerInput): FFIPointer; +export declare function TextReplaceBetweenAlloc(text: string, begin: string, end: string, replacement: string | null): FFIPointer; /** Insert text in a defined byte position */ export declare function TextInsert(text: string, insert: string, position: number): string; @@ -2351,24 +2569,63 @@ export declare function DrawLine3D(startPos: Vector3Value, endPos: Vector3Value, /** Draw a point in 3D space, actually a small line */ export declare function DrawPoint3D(position: Vector3Value, color: ColorValue): void; +/** Draw a circle in 3D world space */ +export declare function DrawCircle3D(center: Vector3Value, radius: number, rotationAxis: Vector3Value, rotationAngle: number, color: ColorValue): void; + /** Draw a color-filled triangle (vertex in counter-clockwise order!) */ export declare function DrawTriangle3D(v1: Vector3Value, v2: Vector3Value, v3: Vector3Value, color: ColorValue): void; /** Draw a triangle strip defined by points */ export declare function DrawTriangleStrip3D(points: FFIPointerInput, pointCount: number, color: ColorValue): void; +/** Draw cube */ +export declare function DrawCube(position: Vector3Value, width: number, height: number, length: number, color: ColorValue): void; + /** Draw cube (Vector version) */ export declare function DrawCubeV(position: Vector3Value, size: Vector3Value, color: ColorValue): void; +/** Draw cube wires */ +export declare function DrawCubeWires(position: Vector3Value, width: number, height: number, length: number, color: ColorValue): void; + /** Draw cube wires (Vector version) */ export declare function DrawCubeWiresV(position: Vector3Value, size: Vector3Value, color: ColorValue): void; +/** Draw sphere */ +export declare function DrawSphere(centerPos: Vector3Value, radius: number, color: ColorValue): void; + +/** Draw sphere with extended parameters */ +export declare function DrawSphereEx(centerPos: Vector3Value, radius: number, rings: number, slices: number, color: ColorValue): void; + +/** Draw sphere wires */ +export declare function DrawSphereWires(centerPos: Vector3Value, radius: number, rings: number, slices: number, color: ColorValue): void; + +/** Draw a cylinder/cone */ +export declare function DrawCylinder(position: Vector3Value, radiusTop: number, radiusBottom: number, height: number, slices: number, color: ColorValue): void; + +/** Draw a cylinder with base at startPos and top at endPos */ +export declare function DrawCylinderEx(startPos: Vector3Value, endPos: Vector3Value, startRadius: number, endRadius: number, sides: number, color: ColorValue): void; + +/** Draw a cylinder/cone wires */ +export declare function DrawCylinderWires(position: Vector3Value, radiusTop: number, radiusBottom: number, height: number, slices: number, color: ColorValue): void; + +/** Draw a cylinder wires with base at startPos and top at endPos */ +export declare function DrawCylinderWiresEx(startPos: Vector3Value, endPos: Vector3Value, startRadius: number, endRadius: number, sides: number, color: ColorValue): void; + +/** Draw a capsule with the center of its sphere caps at startPos and endPos */ +export declare function DrawCapsule(startPos: Vector3Value, endPos: Vector3Value, radius: number, slices: number, rings: number, color: ColorValue): void; + +/** Draw capsule wireframe with the center of its sphere caps at startPos and endPos */ +export declare function DrawCapsuleWires(startPos: Vector3Value, endPos: Vector3Value, radius: number, slices: number, rings: number, color: ColorValue): void; + /** Draw a plane XZ */ export declare function DrawPlane(centerPos: Vector3Value, size: Vector2Value, color: ColorValue): void; /** Draw a ray line */ export declare function DrawRay(ray: RayValue, color: ColorValue): void; +/** Draw a grid (centered at (0, 0, 0)) */ +export declare function DrawGrid(slices: number, spacing: number): void; + /** Load model from files (meshes and materials) */ export declare function LoadModel(fileName: string): ModelValue; @@ -2384,12 +2641,30 @@ export declare function UnloadModel(model: ModelValue): void; /** Compute model bounding box limits (considers all meshes) */ export declare function GetModelBoundingBox(model: ModelValue): BoundingBoxValue; +/** Draw a model (with texture if set) */ +export declare function DrawModel(model: ModelValue, position: Vector3Value, scale: number, tint: ColorValue): void; + +/** Draw a model with extended parameters */ +export declare function DrawModelEx(model: ModelValue, position: Vector3Value, rotationAxis: Vector3Value, rotationAngle: number, scale: Vector3Value, tint: ColorValue): void; + +/** Draw a model wires (with texture if set) */ +export declare function DrawModelWires(model: ModelValue, position: Vector3Value, scale: number, tint: ColorValue): void; + +/** Draw a model wires (with texture if set) with extended parameters */ +export declare function DrawModelWiresEx(model: ModelValue, position: Vector3Value, rotationAxis: Vector3Value, rotationAngle: number, scale: Vector3Value, tint: ColorValue): void; + /** Draw bounding box (wires) */ export declare function DrawBoundingBox(box: BoundingBoxValue, color: ColorValue): void; +/** Draw a billboard texture */ +export declare function DrawBillboard(camera: CameraValue, texture: Texture2DValue, position: Vector3Value, scale: number, tint: ColorValue): void; + /** Draw a billboard texture defined by source */ export declare function DrawBillboardRec(camera: CameraValue, texture: Texture2DValue, source: RectangleValue, position: Vector3Value, size: Vector2Value, tint: ColorValue): void; +/** Draw a billboard texture defined by source and rotation */ +export declare function DrawBillboardPro(camera: CameraValue, texture: Texture2DValue, source: RectangleValue, position: Vector3Value, up: Vector3Value, size: Vector2Value, origin: Vector2Value, rotation: number, tint: ColorValue): void; + /** Upload mesh vertex data in GPU and provide VAO/VBO ids */ export declare function UploadMesh(mesh: FFIPointerInput, dynamic: boolean): void; @@ -2417,9 +2692,33 @@ export declare function ExportMesh(mesh: MeshValue, fileName: string): boolean; /** Export mesh as code file (.h) defining multiple arrays of vertex attributes */ export declare function ExportMeshAsCode(mesh: MeshValue, fileName: string): boolean; +/** Generate polygonal mesh */ +export declare function GenMeshPoly(sides: number, radius: number): MeshValue; + +/** Generate plane mesh (with subdivisions) */ +export declare function GenMeshPlane(width: number, length: number, resX: number, resZ: number): MeshValue; + /** Generate cuboid mesh */ export declare function GenMeshCube(width: number, height: number, length: number): MeshValue; +/** Generate sphere mesh (standard sphere) */ +export declare function GenMeshSphere(radius: number, rings: number, slices: number): MeshValue; + +/** Generate half-sphere mesh (no bottom cap) */ +export declare function GenMeshHemiSphere(radius: number, rings: number, slices: number): MeshValue; + +/** Generate cylinder mesh */ +export declare function GenMeshCylinder(radius: number, height: number, slices: number): MeshValue; + +/** Generate cone/pyramid mesh */ +export declare function GenMeshCone(radius: number, height: number, slices: number): MeshValue; + +/** Generate torus mesh */ +export declare function GenMeshTorus(radius: number, size: number, radSeg: number, sides: number): MeshValue; + +/** Generate trefoil knot mesh */ +export declare function GenMeshKnot(radius: number, size: number, radSeg: number, sides: number): MeshValue; + /** Generate heightmap mesh from image data */ export declare function GenMeshHeightmap(heightmap: ImageValue, size: Vector3Value): MeshValue; @@ -2447,15 +2746,30 @@ export declare function SetModelMeshMaterial(model: FFIPointerInput, meshId: num /** Load model animations from file */ export declare function LoadModelAnimations(fileName: string, animCount: FFIPointerInput): FFIPointer; +/** Update model animation pose (vertex buffers and bone matrices) */ +export declare function UpdateModelAnimation(model: ModelValue, anim: ModelAnimationValue, frame: number): void; + +/** Update model animation pose, blending two animations */ +export declare function UpdateModelAnimationEx(model: ModelValue, animA: ModelAnimationValue, frameA: number, animB: ModelAnimationValue, frameB: number, blend: number): void; + /** Unload animation array data */ export declare function UnloadModelAnimations(animations: FFIPointerInput, animCount: number): void; /** Check model animation skeleton match */ export declare function IsModelAnimationValid(model: ModelValue, anim: ModelAnimationValue): boolean; +/** Check collision between two spheres */ +export declare function CheckCollisionSpheres(center1: Vector3Value, radius1: number, center2: Vector3Value, radius2: number): boolean; + /** Check collision between two bounding boxes */ export declare function CheckCollisionBoxes(box1: BoundingBoxValue, box2: BoundingBoxValue): boolean; +/** Check collision between box and sphere */ +export declare function CheckCollisionBoxSphere(box: BoundingBoxValue, center: Vector3Value, radius: number): boolean; + +/** Get collision info between ray and sphere */ +export declare function GetRayCollisionSphere(ray: RayValue, center: Vector3Value, radius: number): RayCollisionValue; + /** Get collision info between ray and box */ export declare function GetRayCollisionBox(ray: RayValue, box: BoundingBoxValue): RayCollisionValue; @@ -2537,6 +2851,15 @@ export declare function ResumeSound(sound: SoundValue): void; /** Check if a sound is currently playing */ export declare function IsSoundPlaying(sound: SoundValue): boolean; +/** Set volume for a sound (1.0 is max level) */ +export declare function SetSoundVolume(sound: SoundValue, volume: number): void; + +/** Set pitch for a sound (1.0 is base level) */ +export declare function SetSoundPitch(sound: SoundValue, pitch: number): void; + +/** Set pan for a sound (-1.0 left, 0.0 center, 1.0 right) */ +export declare function SetSoundPan(sound: SoundValue, pan: number): void; + /** Copy a wave to a new wave */ export declare function WaveCopy(wave: WaveValue): WaveValue; @@ -2582,6 +2905,18 @@ export declare function PauseMusicStream(music: MusicValue): void; /** Resume playing paused music */ export declare function ResumeMusicStream(music: MusicValue): void; +/** Seek music to a position (in seconds) */ +export declare function SeekMusicStream(music: MusicValue, position: number): void; + +/** Set volume for music (1.0 is max level) */ +export declare function SetMusicVolume(music: MusicValue, volume: number): void; + +/** Set pitch for a music (1.0 is base level) */ +export declare function SetMusicPitch(music: MusicValue, pitch: number): void; + +/** Set pan for a music (-1.0 left, 0.0 center, 1.0 right) */ +export declare function SetMusicPan(music: MusicValue, pan: number): void; + /** Get music time length (in seconds) */ export declare function GetMusicTimeLength(music: MusicValue): number; @@ -2618,6 +2953,15 @@ export declare function IsAudioStreamPlaying(stream: AudioStreamValue): boolean; /** Stop audio stream */ export declare function StopAudioStream(stream: AudioStreamValue): void; +/** Set volume for audio stream (1.0 is max level) */ +export declare function SetAudioStreamVolume(stream: AudioStreamValue, volume: number): void; + +/** Set pitch for audio stream (1.0 is base level) */ +export declare function SetAudioStreamPitch(stream: AudioStreamValue, pitch: number): void; + +/** Set pan for audio stream (-1.0 to 1.0 range, 0.0 is centered) */ +export declare function SetAudioStreamPan(stream: AudioStreamValue, pan: number): void; + /** Default size for new audio streams */ export declare function SetAudioStreamBufferSizeDefault(size: number): void; diff --git a/bindings/raylib.ts b/bindings/raylib.ts index afb3c0d..c0a24db 100644 --- a/bindings/raylib.ts +++ b/bindings/raylib.ts @@ -4,12 +4,19 @@ export const RAYLIB_BINDING_INFO = { raylibVersion: "6.0", raylibCommit: "dbc56a87da87d973a9c5baa4e7438a9d20121d28", - gocciaScriptVersion: "0.10.0", + gocciaScriptVersion: "0.14.0", linkage: "dynamic", }; -export const RAYLIB_FIELD_ALIASES = { - AudioStream: { buffer: "nativeBuffer" }, -}; + +if (typeof FFI === "undefined") { + throw new TypeError( + "The raylib bindings need GocciaScript's ffi capability. Grant it with " + + '--allow-ffi, or with "permissions": { "allow-ffi": true } in a ' + + "trusted goccia.json.", + ); +} + +const nullableUtf8String = FFI.nullable("utf8string"); const raylibCandidates = (): string[] => { const suffix = FFI.suffix; @@ -303,7 +310,7 @@ export const Wave = FFI.struct({ /** AudioStream, custom audio stream */ export const AudioStream = FFI.struct({ - "nativeBuffer": "pointer", + "buffer": "pointer", "processor": "pointer", "sampleRate": "u32", "sampleSize": "u32", @@ -1319,16 +1326,10 @@ export const LoadVrStereoConfig = raylibLibrary.bind("LoadVrStereoConfig", { arg export const UnloadVrStereoConfig = raylibLibrary.bind("UnloadVrStereoConfig", { args: [VrStereoConfig], returns: "void" }); /** Load shader from files and bind default locations */ -export const LoadShader = raylibLibrary.bind("LoadShader", { args: ["utf8string", "utf8string"], returns: Shader }); - -/** Raw-pointer variant for nullable C string parameters. */ -export const LoadShaderRaw = raylibLibrary.bind("LoadShader", { args: ["pointer", "pointer"], returns: Shader }); +export const LoadShader = raylibLibrary.bind("LoadShader", { args: [nullableUtf8String, nullableUtf8String], returns: Shader }); /** Load shader from code strings and bind default locations */ -export const LoadShaderFromMemory = raylibLibrary.bind("LoadShaderFromMemory", { args: ["utf8string", "utf8string"], returns: Shader }); - -/** Raw-pointer variant for nullable C string parameters. */ -export const LoadShaderFromMemoryRaw = raylibLibrary.bind("LoadShaderFromMemory", { args: ["pointer", "pointer"], returns: Shader }); +export const LoadShaderFromMemory = raylibLibrary.bind("LoadShaderFromMemory", { args: [nullableUtf8String, nullableUtf8String], returns: Shader }); /** Check if a shader is valid (loaded on GPU) */ export const IsShaderValid = raylibLibrary.bind("IsShaderValid", { args: [Shader], returns: "bool" }); @@ -1423,6 +1424,9 @@ export const OpenURL = raylibLibrary.bind("OpenURL", { args: ["utf8string"], ret /** Set the current threshold (minimum) log level */ export const SetTraceLogLevel = raylibLibrary.bind("SetTraceLogLevel", { args: ["i32"], returns: "void" }); +/** Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...) */ +export const TraceLog = raylibLibrary.bind("TraceLog", { args: ["i32", "utf8string"], variadic: true, returns: "void" }); + /** Set custom trace log */ export const SetTraceLogCallback = raylibLibrary.bind("SetTraceLogCallback", { args: ["pointer"], returns: "void" }); @@ -1583,10 +1587,7 @@ export const ComputeSHA1 = raylibLibrary.bind("ComputeSHA1", { args: ["pointer", export const ComputeSHA256 = raylibLibrary.bind("ComputeSHA256", { args: ["pointer", "i32"], returns: "pointer" }); /** Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS */ -export const LoadAutomationEventList = raylibLibrary.bind("LoadAutomationEventList", { args: ["utf8string"], returns: AutomationEventList }); - -/** Raw-pointer variant for nullable C string parameters. */ -export const LoadAutomationEventListRaw = raylibLibrary.bind("LoadAutomationEventList", { args: ["pointer"], returns: AutomationEventList }); +export const LoadAutomationEventList = raylibLibrary.bind("LoadAutomationEventList", { args: [nullableUtf8String], returns: AutomationEventList }); /** Unload automation events list from file */ export const UnloadAutomationEventList = raylibLibrary.bind("UnloadAutomationEventList", { args: [AutomationEventList], returns: "void" }); @@ -1666,6 +1667,9 @@ export const GetGamepadAxisMovement = raylibLibrary.bind("GetGamepadAxisMovement /** Set internal gamepad mappings (SDL_GameControllerDB) */ export const SetGamepadMappings = raylibLibrary.bind("SetGamepadMappings", { args: ["utf8string"], returns: "i32" }); +/** Set gamepad vibration for both motors (duration in seconds) */ +export const SetGamepadVibration = raylibLibrary.bind("SetGamepadVibration", { args: ["i32", "f32", "f32", "f32"], returns: "void" }); + /** Check if a mouse button has been pressed once */ export const IsMouseButtonPressed = raylibLibrary.bind("IsMouseButtonPressed", { args: ["i32"], returns: "bool" }); @@ -1750,6 +1754,9 @@ export const GetGesturePinchAngle = raylibLibrary.bind("GetGesturePinchAngle", { /** Update camera position for selected mode */ export const UpdateCamera = raylibLibrary.bind("UpdateCamera", { args: ["pointer", "i32"], returns: "void" }); +/** Update camera movement/rotation */ +export const UpdateCameraPro = raylibLibrary.bind("UpdateCameraPro", { args: ["pointer", Vector3, Vector3, "f32"], returns: "void" }); + /** Set texture and rectangle to be used on shapes drawing */ export const SetShapesTexture = raylibLibrary.bind("SetShapesTexture", { args: [Texture, Rectangle], returns: "void" }); @@ -1771,12 +1778,57 @@ export const DrawLine = raylibLibrary.bind("DrawLine", { args: ["i32", "i32", "i /** Draw a line (using gl lines) */ export const DrawLineV = raylibLibrary.bind("DrawLineV", { args: [Vector2, Vector2, Color], returns: "void" }); +/** Draw a line (using triangles/quads) */ +export const DrawLineEx = raylibLibrary.bind("DrawLineEx", { args: [Vector2, Vector2, "f32", Color], returns: "void" }); + /** Draw lines sequence (using gl lines) */ export const DrawLineStrip = raylibLibrary.bind("DrawLineStrip", { args: ["pointer", "i32", Color], returns: "void" }); +/** Draw line segment cubic-bezier in-out interpolation */ +export const DrawLineBezier = raylibLibrary.bind("DrawLineBezier", { args: [Vector2, Vector2, "f32", Color], returns: "void" }); + /** Draw a dashed line */ export const DrawLineDashed = raylibLibrary.bind("DrawLineDashed", { args: [Vector2, Vector2, "i32", "i32", Color], returns: "void" }); +/** Draw a color-filled circle */ +export const DrawCircle = raylibLibrary.bind("DrawCircle", { args: ["i32", "i32", "f32", Color], returns: "void" }); + +/** Draw a color-filled circle (Vector version) */ +export const DrawCircleV = raylibLibrary.bind("DrawCircleV", { args: [Vector2, "f32", Color], returns: "void" }); + +/** Draw a gradient-filled circle */ +export const DrawCircleGradient = raylibLibrary.bind("DrawCircleGradient", { args: [Vector2, "f32", Color, Color], returns: "void" }); + +/** Draw a piece of a circle */ +export const DrawCircleSector = raylibLibrary.bind("DrawCircleSector", { args: [Vector2, "f32", "f32", "f32", "i32", Color], returns: "void" }); + +/** Draw circle sector outline */ +export const DrawCircleSectorLines = raylibLibrary.bind("DrawCircleSectorLines", { args: [Vector2, "f32", "f32", "f32", "i32", Color], returns: "void" }); + +/** Draw circle outline */ +export const DrawCircleLines = raylibLibrary.bind("DrawCircleLines", { args: ["i32", "i32", "f32", Color], returns: "void" }); + +/** Draw circle outline (Vector version) */ +export const DrawCircleLinesV = raylibLibrary.bind("DrawCircleLinesV", { args: [Vector2, "f32", Color], returns: "void" }); + +/** Draw ellipse */ +export const DrawEllipse = raylibLibrary.bind("DrawEllipse", { args: ["i32", "i32", "f32", "f32", Color], returns: "void" }); + +/** Draw ellipse (Vector version) */ +export const DrawEllipseV = raylibLibrary.bind("DrawEllipseV", { args: [Vector2, "f32", "f32", Color], returns: "void" }); + +/** Draw ellipse outline */ +export const DrawEllipseLines = raylibLibrary.bind("DrawEllipseLines", { args: ["i32", "i32", "f32", "f32", Color], returns: "void" }); + +/** Draw ellipse outline (Vector version) */ +export const DrawEllipseLinesV = raylibLibrary.bind("DrawEllipseLinesV", { args: [Vector2, "f32", "f32", Color], returns: "void" }); + +/** Draw ring */ +export const DrawRing = raylibLibrary.bind("DrawRing", { args: [Vector2, "f32", "f32", "f32", "f32", "i32", Color], returns: "void" }); + +/** Draw ring outline */ +export const DrawRingLines = raylibLibrary.bind("DrawRingLines", { args: [Vector2, "f32", "f32", "f32", "f32", "i32", Color], returns: "void" }); + /** Draw a color-filled rectangle */ export const DrawRectangle = raylibLibrary.bind("DrawRectangle", { args: ["i32", "i32", "i32", "i32", Color], returns: "void" }); @@ -1786,6 +1838,9 @@ export const DrawRectangleV = raylibLibrary.bind("DrawRectangleV", { args: [Vect /** Draw a color-filled rectangle */ export const DrawRectangleRec = raylibLibrary.bind("DrawRectangleRec", { args: [Rectangle, Color], returns: "void" }); +/** Draw a color-filled rectangle with pro parameters */ +export const DrawRectanglePro = raylibLibrary.bind("DrawRectanglePro", { args: [Rectangle, Vector2, "f32", Color], returns: "void" }); + /** Draw a vertical-gradient-filled rectangle */ export const DrawRectangleGradientV = raylibLibrary.bind("DrawRectangleGradientV", { args: ["i32", "i32", "i32", "i32", Color, Color], returns: "void" }); @@ -1798,6 +1853,18 @@ export const DrawRectangleGradientEx = raylibLibrary.bind("DrawRectangleGradient /** Draw rectangle outline */ export const DrawRectangleLines = raylibLibrary.bind("DrawRectangleLines", { args: ["i32", "i32", "i32", "i32", Color], returns: "void" }); +/** Draw rectangle outline with extended parameters */ +export const DrawRectangleLinesEx = raylibLibrary.bind("DrawRectangleLinesEx", { args: [Rectangle, "f32", Color], returns: "void" }); + +/** Draw rectangle with rounded edges */ +export const DrawRectangleRounded = raylibLibrary.bind("DrawRectangleRounded", { args: [Rectangle, "f32", "i32", Color], returns: "void" }); + +/** Draw rectangle lines with rounded edges */ +export const DrawRectangleRoundedLines = raylibLibrary.bind("DrawRectangleRoundedLines", { args: [Rectangle, "f32", "i32", Color], returns: "void" }); + +/** Draw rectangle with rounded edges outline */ +export const DrawRectangleRoundedLinesEx = raylibLibrary.bind("DrawRectangleRoundedLinesEx", { args: [Rectangle, "f32", "i32", "f32", Color], returns: "void" }); + /** Draw a color-filled triangle (vertex in counter-clockwise order!) */ export const DrawTriangle = raylibLibrary.bind("DrawTriangle", { args: [Vector2, Vector2, Vector2, Color], returns: "void" }); @@ -1810,12 +1877,78 @@ export const DrawTriangleFan = raylibLibrary.bind("DrawTriangleFan", { args: ["p /** Draw a triangle strip defined by points */ export const DrawTriangleStrip = raylibLibrary.bind("DrawTriangleStrip", { args: ["pointer", "i32", Color], returns: "void" }); +/** Draw a regular polygon (Vector version) */ +export const DrawPoly = raylibLibrary.bind("DrawPoly", { args: [Vector2, "i32", "f32", "f32", Color], returns: "void" }); + +/** Draw a polygon outline of n sides */ +export const DrawPolyLines = raylibLibrary.bind("DrawPolyLines", { args: [Vector2, "i32", "f32", "f32", Color], returns: "void" }); + +/** Draw a polygon outline of n sides with extended parameters */ +export const DrawPolyLinesEx = raylibLibrary.bind("DrawPolyLinesEx", { args: [Vector2, "i32", "f32", "f32", "f32", Color], returns: "void" }); + +/** Draw spline: Linear, minimum 2 points */ +export const DrawSplineLinear = raylibLibrary.bind("DrawSplineLinear", { args: ["pointer", "i32", "f32", Color], returns: "void" }); + +/** Draw spline: B-Spline, minimum 4 points */ +export const DrawSplineBasis = raylibLibrary.bind("DrawSplineBasis", { args: ["pointer", "i32", "f32", Color], returns: "void" }); + +/** Draw spline: Catmull-Rom, minimum 4 points */ +export const DrawSplineCatmullRom = raylibLibrary.bind("DrawSplineCatmullRom", { args: ["pointer", "i32", "f32", Color], returns: "void" }); + +/** Draw spline: Quadratic Bezier, minimum 3 points (1 control point): [p1, c2, p3, c4...] */ +export const DrawSplineBezierQuadratic = raylibLibrary.bind("DrawSplineBezierQuadratic", { args: ["pointer", "i32", "f32", Color], returns: "void" }); + +/** Draw spline: Cubic Bezier, minimum 4 points (2 control points): [p1, c2, c3, p4, c5, c6...] */ +export const DrawSplineBezierCubic = raylibLibrary.bind("DrawSplineBezierCubic", { args: ["pointer", "i32", "f32", Color], returns: "void" }); + +/** Draw spline segment: Linear, 2 points */ +export const DrawSplineSegmentLinear = raylibLibrary.bind("DrawSplineSegmentLinear", { args: [Vector2, Vector2, "f32", Color], returns: "void" }); + +/** Draw spline segment: B-Spline, 4 points */ +export const DrawSplineSegmentBasis = raylibLibrary.bind("DrawSplineSegmentBasis", { args: [Vector2, Vector2, Vector2, Vector2, "f32", Color], returns: "void" }); + +/** Draw spline segment: Catmull-Rom, 4 points */ +export const DrawSplineSegmentCatmullRom = raylibLibrary.bind("DrawSplineSegmentCatmullRom", { args: [Vector2, Vector2, Vector2, Vector2, "f32", Color], returns: "void" }); + +/** Draw spline segment: Quadratic Bezier, 2 points, 1 control point */ +export const DrawSplineSegmentBezierQuadratic = raylibLibrary.bind("DrawSplineSegmentBezierQuadratic", { args: [Vector2, Vector2, Vector2, "f32", Color], returns: "void" }); + +/** Draw spline segment: Cubic Bezier, 2 points, 2 control points */ +export const DrawSplineSegmentBezierCubic = raylibLibrary.bind("DrawSplineSegmentBezierCubic", { args: [Vector2, Vector2, Vector2, Vector2, "f32", Color], returns: "void" }); + +/** Get (evaluate) spline point: Linear */ +export const GetSplinePointLinear = raylibLibrary.bind("GetSplinePointLinear", { args: [Vector2, Vector2, "f32"], returns: Vector2 }); + +/** Get (evaluate) spline point: B-Spline */ +export const GetSplinePointBasis = raylibLibrary.bind("GetSplinePointBasis", { args: [Vector2, Vector2, Vector2, Vector2, "f32"], returns: Vector2 }); + +/** Get (evaluate) spline point: Catmull-Rom */ +export const GetSplinePointCatmullRom = raylibLibrary.bind("GetSplinePointCatmullRom", { args: [Vector2, Vector2, Vector2, Vector2, "f32"], returns: Vector2 }); + +/** Get (evaluate) spline point: Quadratic Bezier */ +export const GetSplinePointBezierQuad = raylibLibrary.bind("GetSplinePointBezierQuad", { args: [Vector2, Vector2, Vector2, "f32"], returns: Vector2 }); + +/** Get (evaluate) spline point: Cubic Bezier */ +export const GetSplinePointBezierCubic = raylibLibrary.bind("GetSplinePointBezierCubic", { args: [Vector2, Vector2, Vector2, Vector2, "f32"], returns: Vector2 }); + /** Check collision between two rectangles */ export const CheckCollisionRecs = raylibLibrary.bind("CheckCollisionRecs", { args: [Rectangle, Rectangle], returns: "bool" }); +/** Check collision between two circles */ +export const CheckCollisionCircles = raylibLibrary.bind("CheckCollisionCircles", { args: [Vector2, "f32", Vector2, "f32"], returns: "bool" }); + +/** Check collision between circle and rectangle */ +export const CheckCollisionCircleRec = raylibLibrary.bind("CheckCollisionCircleRec", { args: [Vector2, "f32", Rectangle], returns: "bool" }); + +/** Check if circle collides with a line created betweeen two points [p1] and [p2] */ +export const CheckCollisionCircleLine = raylibLibrary.bind("CheckCollisionCircleLine", { args: [Vector2, "f32", Vector2, Vector2], returns: "bool" }); + /** Check if point is inside rectangle */ export const CheckCollisionPointRec = raylibLibrary.bind("CheckCollisionPointRec", { args: [Vector2, Rectangle], returns: "bool" }); +/** Check if point is inside circle */ +export const CheckCollisionPointCircle = raylibLibrary.bind("CheckCollisionPointCircle", { args: [Vector2, Vector2, "f32"], returns: "bool" }); + /** Check if point is inside a triangle */ export const CheckCollisionPointTriangle = raylibLibrary.bind("CheckCollisionPointTriangle", { args: [Vector2, Vector2, Vector2, Vector2], returns: "bool" }); @@ -1873,9 +2006,21 @@ export const GenImageColor = raylibLibrary.bind("GenImageColor", { args: ["i32", /** Generate image: linear gradient, direction in degrees [0..360], 0=Vertical gradient */ export const GenImageGradientLinear = raylibLibrary.bind("GenImageGradientLinear", { args: ["i32", "i32", "i32", Color, Color], returns: Image }); +/** Generate image: radial gradient */ +export const GenImageGradientRadial = raylibLibrary.bind("GenImageGradientRadial", { args: ["i32", "i32", "f32", Color, Color], returns: Image }); + +/** Generate image: square gradient */ +export const GenImageGradientSquare = raylibLibrary.bind("GenImageGradientSquare", { args: ["i32", "i32", "f32", Color, Color], returns: Image }); + /** Generate image: checked */ export const GenImageChecked = raylibLibrary.bind("GenImageChecked", { args: ["i32", "i32", "i32", "i32", Color, Color], returns: Image }); +/** Generate image: white noise */ +export const GenImageWhiteNoise = raylibLibrary.bind("GenImageWhiteNoise", { args: ["i32", "i32", "f32"], returns: Image }); + +/** Generate image: perlin noise */ +export const GenImagePerlinNoise = raylibLibrary.bind("GenImagePerlinNoise", { args: ["i32", "i32", "i32", "i32", "f32"], returns: Image }); + /** Generate image: cellular algorithm, bigger tileSize means bigger cells */ export const GenImageCellular = raylibLibrary.bind("GenImageCellular", { args: ["i32", "i32", "i32"], returns: Image }); @@ -1894,6 +2039,9 @@ export const ImageFromChannel = raylibLibrary.bind("ImageFromChannel", { args: [ /** Create an image from text (default font) */ export const ImageText = raylibLibrary.bind("ImageText", { args: ["utf8string", "i32", Color], returns: Image }); +/** Create an image from text (custom sprite font) */ +export const ImageTextEx = raylibLibrary.bind("ImageTextEx", { args: [Font, "utf8string", "f32", "f32", Color], returns: Image }); + /** Convert image data to desired format */ export const ImageFormat = raylibLibrary.bind("ImageFormat", { args: ["pointer", "i32"], returns: "void" }); @@ -1903,6 +2051,12 @@ export const ImageToPOT = raylibLibrary.bind("ImageToPOT", { args: ["pointer", C /** Crop an image to a defined rectangle */ export const ImageCrop = raylibLibrary.bind("ImageCrop", { args: ["pointer", Rectangle], returns: "void" }); +/** Crop image depending on alpha value */ +export const ImageAlphaCrop = raylibLibrary.bind("ImageAlphaCrop", { args: ["pointer", "f32"], returns: "void" }); + +/** Clear alpha channel to desired color */ +export const ImageAlphaClear = raylibLibrary.bind("ImageAlphaClear", { args: ["pointer", Color, "f32"], returns: "void" }); + /** Apply alpha mask to image */ export const ImageAlphaMask = raylibLibrary.bind("ImageAlphaMask", { args: ["pointer", Image], returns: "void" }); @@ -1954,6 +2108,9 @@ export const ImageColorInvert = raylibLibrary.bind("ImageColorInvert", { args: [ /** Modify image color: grayscale */ export const ImageColorGrayscale = raylibLibrary.bind("ImageColorGrayscale", { args: ["pointer"], returns: "void" }); +/** Modify image color: contrast (-100 to 100) */ +export const ImageColorContrast = raylibLibrary.bind("ImageColorContrast", { args: ["pointer", "f32"], returns: "void" }); + /** Modify image color: brightness (-255 to 255) */ export const ImageColorBrightness = raylibLibrary.bind("ImageColorBrightness", { args: ["pointer", "i32"], returns: "void" }); @@ -1972,6 +2129,9 @@ export const UnloadImageColors = raylibLibrary.bind("UnloadImageColors", { args: /** Unload colors palette loaded with LoadImagePalette() */ export const UnloadImagePalette = raylibLibrary.bind("UnloadImagePalette", { args: ["pointer"], returns: "void" }); +/** Get image alpha border rectangle */ +export const GetImageAlphaBorder = raylibLibrary.bind("GetImageAlphaBorder", { args: [Image, "f32"], returns: Rectangle }); + /** Get image pixel color at (x, y) position */ export const GetImageColor = raylibLibrary.bind("GetImageColor", { args: [Image, "i32", "i32"], returns: Color }); @@ -2038,6 +2198,9 @@ export const ImageDraw = raylibLibrary.bind("ImageDraw", { args: ["pointer", Ima /** Draw text (using default font) within an image (destination) */ export const ImageDrawText = raylibLibrary.bind("ImageDrawText", { args: ["pointer", "utf8string", "i32", "i32", "i32", Color], returns: "void" }); +/** Draw text (custom sprite font) within an image (destination) */ +export const ImageDrawTextEx = raylibLibrary.bind("ImageDrawTextEx", { args: ["pointer", Font, "utf8string", Vector2, "f32", "f32", Color], returns: "void" }); + /** Load texture from file into GPU memory (VRAM) */ export const LoadTexture = raylibLibrary.bind("LoadTexture", { args: ["utf8string"], returns: Texture }); @@ -2083,12 +2246,24 @@ export const DrawTexture = raylibLibrary.bind("DrawTexture", { args: [Texture, " /** Draw a Texture2D with position defined as Vector2 */ export const DrawTextureV = raylibLibrary.bind("DrawTextureV", { args: [Texture, Vector2, Color], returns: "void" }); +/** Draw a Texture2D with extended parameters */ +export const DrawTextureEx = raylibLibrary.bind("DrawTextureEx", { args: [Texture, Vector2, "f32", "f32", Color], returns: "void" }); + /** Draw a part of a texture defined by a rectangle */ export const DrawTextureRec = raylibLibrary.bind("DrawTextureRec", { args: [Texture, Rectangle, Vector2, Color], returns: "void" }); +/** Draw a part of a texture defined by a rectangle with 'pro' parameters */ +export const DrawTexturePro = raylibLibrary.bind("DrawTexturePro", { args: [Texture, Rectangle, Rectangle, Vector2, "f32", Color], returns: "void" }); + +/** Draws a texture (or part of it) that stretches or shrinks nicely */ +export const DrawTextureNPatch = raylibLibrary.bind("DrawTextureNPatch", { args: [Texture, NPatchInfo, Rectangle, Vector2, "f32", Color], returns: "void" }); + /** Check if two colors are equal */ export const ColorIsEqual = raylibLibrary.bind("ColorIsEqual", { args: [Color, Color], returns: "bool" }); +/** Get color with alpha applied, alpha goes from 0.0f to 1.0f */ +export const Fade = raylibLibrary.bind("Fade", { args: [Color, "f32"], returns: Color }); + /** Get hexadecimal value for a Color (0xRRGGBBAA) */ export const ColorToInt = raylibLibrary.bind("ColorToInt", { args: [Color], returns: "i32" }); @@ -2107,9 +2282,21 @@ export const ColorFromHSV = raylibLibrary.bind("ColorFromHSV", { args: ["f32", " /** Get color multiplied with another color */ export const ColorTint = raylibLibrary.bind("ColorTint", { args: [Color, Color], returns: Color }); +/** Get color with brightness correction, brightness factor goes from -1.0f to 1.0f */ +export const ColorBrightness = raylibLibrary.bind("ColorBrightness", { args: [Color, "f32"], returns: Color }); + +/** Get color with contrast correction, contrast values between -1.0f and 1.0f */ +export const ColorContrast = raylibLibrary.bind("ColorContrast", { args: [Color, "f32"], returns: Color }); + +/** Get color with alpha applied, alpha goes from 0.0f to 1.0f */ +export const ColorAlpha = raylibLibrary.bind("ColorAlpha", { args: [Color, "f32"], returns: Color }); + /** Get src alpha-blended into dst color with tint */ export const ColorAlphaBlend = raylibLibrary.bind("ColorAlphaBlend", { args: [Color, Color, Color], returns: Color }); +/** Get color lerp interpolation between two colors, factor [0.0f..1.0f] */ +export const ColorLerp = raylibLibrary.bind("ColorLerp", { args: [Color, Color, "f32"], returns: Color }); + /** Get Color structure from hexadecimal value */ export const GetColor = raylibLibrary.bind("GetColor", { args: ["u32"], returns: Color }); @@ -2161,12 +2348,30 @@ export const DrawFPS = raylibLibrary.bind("DrawFPS", { args: ["i32", "i32"], ret /** Draw text (using default font) */ export const DrawText = raylibLibrary.bind("DrawText", { args: ["utf8string", "i32", "i32", "i32", Color], returns: "void" }); +/** Draw text using font and additional parameters */ +export const DrawTextEx = raylibLibrary.bind("DrawTextEx", { args: [Font, "utf8string", Vector2, "f32", "f32", Color], returns: "void" }); + +/** Draw text using Font and pro parameters (rotation) */ +export const DrawTextPro = raylibLibrary.bind("DrawTextPro", { args: [Font, "utf8string", Vector2, Vector2, "f32", "f32", "f32", Color], returns: "void" }); + +/** Draw one character (codepoint) */ +export const DrawTextCodepoint = raylibLibrary.bind("DrawTextCodepoint", { args: [Font, "i32", Vector2, "f32", Color], returns: "void" }); + +/** Draw multiple character (codepoint) */ +export const DrawTextCodepoints = raylibLibrary.bind("DrawTextCodepoints", { args: [Font, "pointer", "i32", Vector2, "f32", "f32", Color], returns: "void" }); + /** Set vertical line spacing when drawing with line-breaks */ export const SetTextLineSpacing = raylibLibrary.bind("SetTextLineSpacing", { args: ["i32"], returns: "void" }); /** Measure string width for default font */ export const MeasureText = raylibLibrary.bind("MeasureText", { args: ["utf8string", "i32"], returns: "i32" }); +/** Measure string size for Font */ +export const MeasureTextEx = raylibLibrary.bind("MeasureTextEx", { args: [Font, "utf8string", "f32", "f32"], returns: Vector2 }); + +/** Measure string size for an existing array of codepoints for Font */ +export const MeasureTextCodepoints = raylibLibrary.bind("MeasureTextCodepoints", { args: [Font, "pointer", "i32", "f32", "f32"], returns: Vector2 }); + /** Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found */ export const GetGlyphIndex = raylibLibrary.bind("GetGlyphIndex", { args: [Font, "i32"], returns: "i32" }); @@ -2218,6 +2423,9 @@ export const TextIsEqual = raylibLibrary.bind("TextIsEqual", { args: ["utf8strin /** Get text length, checks for '\0' ending */ export const TextLength = raylibLibrary.bind("TextLength", { args: ["utf8string"], returns: "u32" }); +/** Text formatting with variables (sprintf() style) */ +export const TextFormat = raylibLibrary.bind("TextFormat", { args: ["utf8string"], variadic: true, returns: "utf8string" }); + /** Get a piece of a text string */ export const TextSubtext = raylibLibrary.bind("TextSubtext", { args: ["utf8string", "i32", "i32"], returns: "utf8string" }); @@ -2228,28 +2436,16 @@ export const TextRemoveSpaces = raylibLibrary.bind("TextRemoveSpaces", { args: [ export const GetTextBetween = raylibLibrary.bind("GetTextBetween", { args: ["utf8string", "utf8string", "utf8string"], returns: "utf8string" }); /** Replace text string with new string */ -export const TextReplace = raylibLibrary.bind("TextReplace", { args: ["utf8string", "utf8string", "utf8string"], returns: "utf8string" }); - -/** Raw-pointer variant for nullable C string parameters. */ -export const TextReplaceRaw = raylibLibrary.bind("TextReplace", { args: ["utf8string", "utf8string", "pointer"], returns: "utf8string" }); +export const TextReplace = raylibLibrary.bind("TextReplace", { args: ["utf8string", "utf8string", nullableUtf8String], returns: "utf8string" }); /** Replace text string with new string, memory must be MemFree() */ -export const TextReplaceAlloc = raylibLibrary.bind("TextReplaceAlloc", { args: ["utf8string", "utf8string", "utf8string"], returns: "pointer" }); - -/** Raw-pointer variant for nullable C string parameters. */ -export const TextReplaceAllocRaw = raylibLibrary.bind("TextReplaceAlloc", { args: ["utf8string", "utf8string", "pointer"], returns: "pointer" }); +export const TextReplaceAlloc = raylibLibrary.bind("TextReplaceAlloc", { args: ["utf8string", "utf8string", nullableUtf8String], returns: "pointer" }); /** Replace text between two specific strings */ -export const TextReplaceBetween = raylibLibrary.bind("TextReplaceBetween", { args: ["utf8string", "utf8string", "utf8string", "utf8string"], returns: "utf8string" }); - -/** Raw-pointer variant for nullable C string parameters. */ -export const TextReplaceBetweenRaw = raylibLibrary.bind("TextReplaceBetween", { args: ["utf8string", "utf8string", "utf8string", "pointer"], returns: "utf8string" }); +export const TextReplaceBetween = raylibLibrary.bind("TextReplaceBetween", { args: ["utf8string", "utf8string", "utf8string", nullableUtf8String], returns: "utf8string" }); /** Replace text between two specific strings, memory must be MemFree() */ -export const TextReplaceBetweenAlloc = raylibLibrary.bind("TextReplaceBetweenAlloc", { args: ["utf8string", "utf8string", "utf8string", "utf8string"], returns: "pointer" }); - -/** Raw-pointer variant for nullable C string parameters. */ -export const TextReplaceBetweenAllocRaw = raylibLibrary.bind("TextReplaceBetweenAlloc", { args: ["utf8string", "utf8string", "utf8string", "pointer"], returns: "pointer" }); +export const TextReplaceBetweenAlloc = raylibLibrary.bind("TextReplaceBetweenAlloc", { args: ["utf8string", "utf8string", "utf8string", nullableUtf8String], returns: "pointer" }); /** Insert text in a defined byte position */ export const TextInsert = raylibLibrary.bind("TextInsert", { args: ["utf8string", "utf8string", "i32"], returns: "utf8string" }); @@ -2296,24 +2492,63 @@ export const DrawLine3D = raylibLibrary.bind("DrawLine3D", { args: [Vector3, Vec /** Draw a point in 3D space, actually a small line */ export const DrawPoint3D = raylibLibrary.bind("DrawPoint3D", { args: [Vector3, Color], returns: "void" }); +/** Draw a circle in 3D world space */ +export const DrawCircle3D = raylibLibrary.bind("DrawCircle3D", { args: [Vector3, "f32", Vector3, "f32", Color], returns: "void" }); + /** Draw a color-filled triangle (vertex in counter-clockwise order!) */ export const DrawTriangle3D = raylibLibrary.bind("DrawTriangle3D", { args: [Vector3, Vector3, Vector3, Color], returns: "void" }); /** Draw a triangle strip defined by points */ export const DrawTriangleStrip3D = raylibLibrary.bind("DrawTriangleStrip3D", { args: ["pointer", "i32", Color], returns: "void" }); +/** Draw cube */ +export const DrawCube = raylibLibrary.bind("DrawCube", { args: [Vector3, "f32", "f32", "f32", Color], returns: "void" }); + /** Draw cube (Vector version) */ export const DrawCubeV = raylibLibrary.bind("DrawCubeV", { args: [Vector3, Vector3, Color], returns: "void" }); +/** Draw cube wires */ +export const DrawCubeWires = raylibLibrary.bind("DrawCubeWires", { args: [Vector3, "f32", "f32", "f32", Color], returns: "void" }); + /** Draw cube wires (Vector version) */ export const DrawCubeWiresV = raylibLibrary.bind("DrawCubeWiresV", { args: [Vector3, Vector3, Color], returns: "void" }); +/** Draw sphere */ +export const DrawSphere = raylibLibrary.bind("DrawSphere", { args: [Vector3, "f32", Color], returns: "void" }); + +/** Draw sphere with extended parameters */ +export const DrawSphereEx = raylibLibrary.bind("DrawSphereEx", { args: [Vector3, "f32", "i32", "i32", Color], returns: "void" }); + +/** Draw sphere wires */ +export const DrawSphereWires = raylibLibrary.bind("DrawSphereWires", { args: [Vector3, "f32", "i32", "i32", Color], returns: "void" }); + +/** Draw a cylinder/cone */ +export const DrawCylinder = raylibLibrary.bind("DrawCylinder", { args: [Vector3, "f32", "f32", "f32", "i32", Color], returns: "void" }); + +/** Draw a cylinder with base at startPos and top at endPos */ +export const DrawCylinderEx = raylibLibrary.bind("DrawCylinderEx", { args: [Vector3, Vector3, "f32", "f32", "i32", Color], returns: "void" }); + +/** Draw a cylinder/cone wires */ +export const DrawCylinderWires = raylibLibrary.bind("DrawCylinderWires", { args: [Vector3, "f32", "f32", "f32", "i32", Color], returns: "void" }); + +/** Draw a cylinder wires with base at startPos and top at endPos */ +export const DrawCylinderWiresEx = raylibLibrary.bind("DrawCylinderWiresEx", { args: [Vector3, Vector3, "f32", "f32", "i32", Color], returns: "void" }); + +/** Draw a capsule with the center of its sphere caps at startPos and endPos */ +export const DrawCapsule = raylibLibrary.bind("DrawCapsule", { args: [Vector3, Vector3, "f32", "i32", "i32", Color], returns: "void" }); + +/** Draw capsule wireframe with the center of its sphere caps at startPos and endPos */ +export const DrawCapsuleWires = raylibLibrary.bind("DrawCapsuleWires", { args: [Vector3, Vector3, "f32", "i32", "i32", Color], returns: "void" }); + /** Draw a plane XZ */ export const DrawPlane = raylibLibrary.bind("DrawPlane", { args: [Vector3, Vector2, Color], returns: "void" }); /** Draw a ray line */ export const DrawRay = raylibLibrary.bind("DrawRay", { args: [Ray, Color], returns: "void" }); +/** Draw a grid (centered at (0, 0, 0)) */ +export const DrawGrid = raylibLibrary.bind("DrawGrid", { args: ["i32", "f32"], returns: "void" }); + /** Load model from files (meshes and materials) */ export const LoadModel = raylibLibrary.bind("LoadModel", { args: ["utf8string"], returns: Model }); @@ -2329,12 +2564,30 @@ export const UnloadModel = raylibLibrary.bind("UnloadModel", { args: [Model], re /** Compute model bounding box limits (considers all meshes) */ export const GetModelBoundingBox = raylibLibrary.bind("GetModelBoundingBox", { args: [Model], returns: BoundingBox }); +/** Draw a model (with texture if set) */ +export const DrawModel = raylibLibrary.bind("DrawModel", { args: [Model, Vector3, "f32", Color], returns: "void" }); + +/** Draw a model with extended parameters */ +export const DrawModelEx = raylibLibrary.bind("DrawModelEx", { args: [Model, Vector3, Vector3, "f32", Vector3, Color], returns: "void" }); + +/** Draw a model wires (with texture if set) */ +export const DrawModelWires = raylibLibrary.bind("DrawModelWires", { args: [Model, Vector3, "f32", Color], returns: "void" }); + +/** Draw a model wires (with texture if set) with extended parameters */ +export const DrawModelWiresEx = raylibLibrary.bind("DrawModelWiresEx", { args: [Model, Vector3, Vector3, "f32", Vector3, Color], returns: "void" }); + /** Draw bounding box (wires) */ export const DrawBoundingBox = raylibLibrary.bind("DrawBoundingBox", { args: [BoundingBox, Color], returns: "void" }); +/** Draw a billboard texture */ +export const DrawBillboard = raylibLibrary.bind("DrawBillboard", { args: [Camera3D, Texture, Vector3, "f32", Color], returns: "void" }); + /** Draw a billboard texture defined by source */ export const DrawBillboardRec = raylibLibrary.bind("DrawBillboardRec", { args: [Camera3D, Texture, Rectangle, Vector3, Vector2, Color], returns: "void" }); +/** Draw a billboard texture defined by source and rotation */ +export const DrawBillboardPro = raylibLibrary.bind("DrawBillboardPro", { args: [Camera3D, Texture, Rectangle, Vector3, Vector3, Vector2, Vector2, "f32", Color], returns: "void" }); + /** Upload mesh vertex data in GPU and provide VAO/VBO ids */ export const UploadMesh = raylibLibrary.bind("UploadMesh", { args: ["pointer", "bool"], returns: "void" }); @@ -2362,9 +2615,33 @@ export const ExportMesh = raylibLibrary.bind("ExportMesh", { args: [Mesh, "utf8s /** Export mesh as code file (.h) defining multiple arrays of vertex attributes */ export const ExportMeshAsCode = raylibLibrary.bind("ExportMeshAsCode", { args: [Mesh, "utf8string"], returns: "bool" }); +/** Generate polygonal mesh */ +export const GenMeshPoly = raylibLibrary.bind("GenMeshPoly", { args: ["i32", "f32"], returns: Mesh }); + +/** Generate plane mesh (with subdivisions) */ +export const GenMeshPlane = raylibLibrary.bind("GenMeshPlane", { args: ["f32", "f32", "i32", "i32"], returns: Mesh }); + /** Generate cuboid mesh */ export const GenMeshCube = raylibLibrary.bind("GenMeshCube", { args: ["f32", "f32", "f32"], returns: Mesh }); +/** Generate sphere mesh (standard sphere) */ +export const GenMeshSphere = raylibLibrary.bind("GenMeshSphere", { args: ["f32", "i32", "i32"], returns: Mesh }); + +/** Generate half-sphere mesh (no bottom cap) */ +export const GenMeshHemiSphere = raylibLibrary.bind("GenMeshHemiSphere", { args: ["f32", "i32", "i32"], returns: Mesh }); + +/** Generate cylinder mesh */ +export const GenMeshCylinder = raylibLibrary.bind("GenMeshCylinder", { args: ["f32", "f32", "i32"], returns: Mesh }); + +/** Generate cone/pyramid mesh */ +export const GenMeshCone = raylibLibrary.bind("GenMeshCone", { args: ["f32", "f32", "i32"], returns: Mesh }); + +/** Generate torus mesh */ +export const GenMeshTorus = raylibLibrary.bind("GenMeshTorus", { args: ["f32", "f32", "i32", "i32"], returns: Mesh }); + +/** Generate trefoil knot mesh */ +export const GenMeshKnot = raylibLibrary.bind("GenMeshKnot", { args: ["f32", "f32", "i32", "i32"], returns: Mesh }); + /** Generate heightmap mesh from image data */ export const GenMeshHeightmap = raylibLibrary.bind("GenMeshHeightmap", { args: [Image, Vector3], returns: Mesh }); @@ -2392,15 +2669,30 @@ export const SetModelMeshMaterial = raylibLibrary.bind("SetModelMeshMaterial", { /** Load model animations from file */ export const LoadModelAnimations = raylibLibrary.bind("LoadModelAnimations", { args: ["utf8string", "pointer"], returns: "pointer" }); +/** Update model animation pose (vertex buffers and bone matrices) */ +export const UpdateModelAnimation = raylibLibrary.bind("UpdateModelAnimation", { args: [Model, ModelAnimation, "f32"], returns: "void" }); + +/** Update model animation pose, blending two animations */ +export const UpdateModelAnimationEx = raylibLibrary.bind("UpdateModelAnimationEx", { args: [Model, ModelAnimation, "f32", ModelAnimation, "f32", "f32"], returns: "void" }); + /** Unload animation array data */ export const UnloadModelAnimations = raylibLibrary.bind("UnloadModelAnimations", { args: ["pointer", "i32"], returns: "void" }); /** Check model animation skeleton match */ export const IsModelAnimationValid = raylibLibrary.bind("IsModelAnimationValid", { args: [Model, ModelAnimation], returns: "bool" }); +/** Check collision between two spheres */ +export const CheckCollisionSpheres = raylibLibrary.bind("CheckCollisionSpheres", { args: [Vector3, "f32", Vector3, "f32"], returns: "bool" }); + /** Check collision between two bounding boxes */ export const CheckCollisionBoxes = raylibLibrary.bind("CheckCollisionBoxes", { args: [BoundingBox, BoundingBox], returns: "bool" }); +/** Check collision between box and sphere */ +export const CheckCollisionBoxSphere = raylibLibrary.bind("CheckCollisionBoxSphere", { args: [BoundingBox, Vector3, "f32"], returns: "bool" }); + +/** Get collision info between ray and sphere */ +export const GetRayCollisionSphere = raylibLibrary.bind("GetRayCollisionSphere", { args: [Ray, Vector3, "f32"], returns: RayCollision }); + /** Get collision info between ray and box */ export const GetRayCollisionBox = raylibLibrary.bind("GetRayCollisionBox", { args: [Ray, BoundingBox], returns: RayCollision }); @@ -2482,6 +2774,15 @@ export const ResumeSound = raylibLibrary.bind("ResumeSound", { args: [Sound], re /** Check if a sound is currently playing */ export const IsSoundPlaying = raylibLibrary.bind("IsSoundPlaying", { args: [Sound], returns: "bool" }); +/** Set volume for a sound (1.0 is max level) */ +export const SetSoundVolume = raylibLibrary.bind("SetSoundVolume", { args: [Sound, "f32"], returns: "void" }); + +/** Set pitch for a sound (1.0 is base level) */ +export const SetSoundPitch = raylibLibrary.bind("SetSoundPitch", { args: [Sound, "f32"], returns: "void" }); + +/** Set pan for a sound (-1.0 left, 0.0 center, 1.0 right) */ +export const SetSoundPan = raylibLibrary.bind("SetSoundPan", { args: [Sound, "f32"], returns: "void" }); + /** Copy a wave to a new wave */ export const WaveCopy = raylibLibrary.bind("WaveCopy", { args: [Wave], returns: Wave }); @@ -2527,6 +2828,18 @@ export const PauseMusicStream = raylibLibrary.bind("PauseMusicStream", { args: [ /** Resume playing paused music */ export const ResumeMusicStream = raylibLibrary.bind("ResumeMusicStream", { args: [Music], returns: "void" }); +/** Seek music to a position (in seconds) */ +export const SeekMusicStream = raylibLibrary.bind("SeekMusicStream", { args: [Music, "f32"], returns: "void" }); + +/** Set volume for music (1.0 is max level) */ +export const SetMusicVolume = raylibLibrary.bind("SetMusicVolume", { args: [Music, "f32"], returns: "void" }); + +/** Set pitch for a music (1.0 is base level) */ +export const SetMusicPitch = raylibLibrary.bind("SetMusicPitch", { args: [Music, "f32"], returns: "void" }); + +/** Set pan for a music (-1.0 left, 0.0 center, 1.0 right) */ +export const SetMusicPan = raylibLibrary.bind("SetMusicPan", { args: [Music, "f32"], returns: "void" }); + /** Get music time length (in seconds) */ export const GetMusicTimeLength = raylibLibrary.bind("GetMusicTimeLength", { args: [Music], returns: "f32" }); @@ -2563,6 +2876,15 @@ export const IsAudioStreamPlaying = raylibLibrary.bind("IsAudioStreamPlaying", { /** Stop audio stream */ export const StopAudioStream = raylibLibrary.bind("StopAudioStream", { args: [AudioStream], returns: "void" }); +/** Set volume for audio stream (1.0 is max level) */ +export const SetAudioStreamVolume = raylibLibrary.bind("SetAudioStreamVolume", { args: [AudioStream, "f32"], returns: "void" }); + +/** Set pitch for audio stream (1.0 is base level) */ +export const SetAudioStreamPitch = raylibLibrary.bind("SetAudioStreamPitch", { args: [AudioStream, "f32"], returns: "void" }); + +/** Set pan for audio stream (-1.0 to 1.0 range, 0.0 is centered) */ +export const SetAudioStreamPan = raylibLibrary.bind("SetAudioStreamPan", { args: [AudioStream, "f32"], returns: "void" }); + /** Default size for new audio streams */ export const SetAudioStreamBufferSizeDefault = raylibLibrary.bind("SetAudioStreamBufferSizeDefault", { args: ["i32"], returns: "void" }); diff --git a/examples/doom-gpl/Makefile b/examples/doom-gpl/Makefile index a5ac672..7f8ddc4 100644 --- a/examples/doom-gpl/Makefile +++ b/examples/doom-gpl/Makefile @@ -17,8 +17,12 @@ FREEDOOM_SHA256 := 3f9b264f3e3ce503b4fb7f6bdcb1f419d93c7b546f4df3e874dd878db9688 FREEDOOM_WAD := $(CACHE_DIR)/freedoom-$(FREEDOOM_VERSION)/freedoom1.wad CACHED_IWAD := $(CACHE_DIR)/iwad.wad -GOCCIA_LOADER ?= GocciaScriptLoader +GOCCIA_RUNNER ?= GocciaRunner GOCCIA_TEST_RUNNER ?= GocciaTestRunner +# goccia.json requests the ffi and read capabilities. Trust it once with +# "GocciaRunner --trust examples/doom-gpl/goccia.json", or pass GOCCIA_FLAGS=-P +# to accept the request for a single run. +GOCCIA_FLAGS ?= GOCCIA_CONFIG := examples/doom-gpl/goccia.json .PHONY: all clean fetch freedoom prepare run run-freedoom smoke test-palette verify-source @@ -75,20 +79,20 @@ run: prepare fi @test -s "$(IWAD)" cp "$(IWAD)" "$(CACHED_IWAD)" - cd "$(PROJECT_ROOT)" && "$(GOCCIA_LOADER)" examples/doom-gpl/run.ts --config="$(GOCCIA_CONFIG)" + cd "$(PROJECT_ROOT)" && "$(GOCCIA_RUNNER)" $(GOCCIA_FLAGS) examples/doom-gpl/run.ts --config="$(GOCCIA_CONFIG)" run-freedoom: prepare freedoom cp "$(FREEDOOM_WAD)" "$(CACHED_IWAD)" - cd "$(PROJECT_ROOT)" && "$(GOCCIA_LOADER)" examples/doom-gpl/run.ts --config="$(GOCCIA_CONFIG)" + cd "$(PROJECT_ROOT)" && "$(GOCCIA_RUNNER)" $(GOCCIA_FLAGS) examples/doom-gpl/run.ts --config="$(GOCCIA_CONFIG)" smoke: prepare freedoom cp "$(FREEDOOM_WAD)" "$(CACHED_IWAD)" - cd "$(PROJECT_ROOT)" && "$(GOCCIA_LOADER)" examples/doom-gpl/smoke.ts --config="$(GOCCIA_CONFIG)" + cd "$(PROJECT_ROOT)" && "$(GOCCIA_RUNNER)" $(GOCCIA_FLAGS) examples/doom-gpl/smoke.ts --config="$(GOCCIA_CONFIG)" @test -s "$(CACHE_DIR)/doom-goccia-smoke.png" test-palette: - cd "$(PROJECT_ROOT)" && "$(GOCCIA_TEST_RUNNER)" examples/doom-gpl/tests --config="$(GOCCIA_CONFIG)" --mode=interpreted - cd "$(PROJECT_ROOT)" && "$(GOCCIA_TEST_RUNNER)" examples/doom-gpl/tests --config="$(GOCCIA_CONFIG)" --mode=bytecode + cd "$(PROJECT_ROOT)" && "$(GOCCIA_TEST_RUNNER)" examples/doom-gpl/tests --config="$(GOCCIA_CONFIG)" --mode=interpreted --ignore-config-permissions + cd "$(PROJECT_ROOT)" && "$(GOCCIA_TEST_RUNNER)" examples/doom-gpl/tests --config="$(GOCCIA_CONFIG)" --mode=bytecode --ignore-config-permissions clean: @if [ "$(CACHE_DIR)" = "$(CURDIR)/.cache" ]; then \ diff --git a/examples/doom-gpl/README.md b/examples/doom-gpl/README.md index 8997791..0101c1b 100644 --- a/examples/doom-gpl/README.md +++ b/examples/doom-gpl/README.md @@ -28,16 +28,23 @@ checkout explicitly excludes its bundled shareware WAD and music pack. Requirements: -- GocciaScript 0.10.0 +- GocciaScript 0.14.0 - raylib 6.0 as a dynamic library - Node.js, Git, Make, curl, shasum, and unzip -From the repository root: +This subproject's `goccia.json` requests the `ffi` capability and read access +to the repository's `bindings/raylib.ts`, which lies outside the subproject. +GocciaScript applies a config's requests once you have reviewed and trusted +them. From the repository root: ```sh +GocciaRunner --trust examples/doom-gpl/goccia.json make -C examples/doom-gpl run-freedoom ``` +Append `GOCCIA_FLAGS=-P` to any run target to accept the requests for that one +run instead of storing trust. + From this directory: ```sh @@ -71,8 +78,8 @@ Those compatibility features are enabled only by this subproject's default syntax. On an Apple M1 Max with GocciaScript 0.10.0 bytecode, the first working -320x200 build renders Freedoom E1M1 at roughly 3 FPS. This is a real engine -port, but it is not yet a real-time port. Stable 0.10.0 spends most of its time +320x200 build rendered Freedoom E1M1 at roughly 3 FPS. This is a real engine +port, but it is not yet a real-time port. Stable 0.10.0 spent most of its time in the software renderer and indexed-to-RGBA pixel loops. Audio, menus, save files, and full Doom II parity are not wired into this first diff --git a/examples/doom-gpl/goccia.json b/examples/doom-gpl/goccia.json index d94520d..3d1e78e 100644 --- a/examples/doom-gpl/goccia.json +++ b/examples/doom-gpl/goccia.json @@ -1,7 +1,10 @@ { "source-type": "module", "mode": "bytecode", - "unsafe-ffi": true, + "permissions": { + "allow-ffi": true, + "allow-read": ["../../bindings/raylib.ts"] + }, "compat-asi": true, "compat-function": true, "compat-traditional-for-loop": true, diff --git a/examples/doom-gpl/src/i_video.ts b/examples/doom-gpl/src/i_video.ts index da1f3e4..cff112d 100644 --- a/examples/doom-gpl/src/i_video.ts +++ b/examples/doom-gpl/src/i_video.ts @@ -3,15 +3,12 @@ import { BLACK, BeginDrawing, - BeginMode2D, BeginShaderMode, - Camera2D, ClearBackground, CloseWindow, DrawFPS, - DrawTexture, + DrawTextureEx, EndDrawing, - EndMode2D, EndShaderMode, GenImageColor, GetShaderLocation, @@ -76,12 +73,7 @@ void main() { vec2 paletteCoord = vec2((paletteIndex + 0.5)/256.0, 0.5); finalColor = texture(paletteTexture, paletteCoord)*colDiffuse*fragColor; }`; -const camera = Camera2D.create({ - offset: Vector2.create({ x: 0, y: 0 }), - target: Vector2.create({ x: 0, y: 0 }), - rotation: 0, - zoom: scale, -}); +const origin = Vector2.create({ x: 0, y: 0 }); let video = null; let playpal = null; @@ -212,16 +204,14 @@ const I_FinishUpdate = (): void => { BeginDrawing(); ClearBackground(BLACK); - BeginMode2D(camera); if (indexedShaderActive) { BeginShaderMode(shader); SetShaderValueTexture(shader, paletteLocation, paletteTexture); - DrawTexture(indexedTexture, 0, 0, WHITE); + DrawTextureEx(indexedTexture, origin, 0, scale, WHITE); EndShaderMode(); } else { - DrawTexture(rgbaTexture, 0, 0, WHITE); + DrawTextureEx(rgbaTexture, origin, 0, scale, WHITE); } - EndMode2D(); if (showFps) DrawFPS(8, 8); EndDrawing(); }; diff --git a/examples/lib/bunny-instancing.ts b/examples/lib/bunny-instancing.ts index d05b855..fc08030 100644 --- a/examples/lib/bunny-instancing.ts +++ b/examples/lib/bunny-instancing.ts @@ -2,20 +2,16 @@ import { BeginShaderMode, DrawMeshInstanced, EndShaderMode, - GenImageColor, - GenMeshHeightmap, + GenMeshPlane, IsShaderValid, LoadMaterialDefault, LoadShaderFromMemory, MATERIAL_MAP_DIFFUSE, SetMaterialTexture, - UnloadImage, UnloadMaterial, UnloadMesh, UnloadShader, UpdateMeshBuffer, - Vector3, - WHITE, } from "../../bindings/raylib.ts"; const vertexShaderSource = `#version 330 @@ -42,24 +38,6 @@ void main() { finalColor = texture(texture0, fragTexCoord)*colDiffuse*fragColor; }`; -const quadVertices = new Float32Array([ - 0, 0, 0, - 0, 32, 0, - 32, 32, 0, - 0, 0, 0, - 32, 32, 0, - 32, 0, 0, -]); - -const quadTexcoords = new Float32Array([ - 0, 0, - 0, 1, - 1, 1, - 0, 0, - 1, 1, - 1, 0, -]); - export const createBunnyInstancing = (texture) => { const shader = LoadShaderFromMemory( vertexShaderSource, @@ -70,16 +48,17 @@ export const createBunnyInstancing = (texture) => { return null; } - const meshImage = GenImageColor(2, 2, WHITE); - // GenMeshHeightmap supplies an uploaded 6-vertex mesh; both useful buffers - // are completely replaced below to form the sprite quad. - const mesh = GenMeshHeightmap( - meshImage, - Vector3.create({ x: texture.width, y: 0, z: texture.height }), - ); - UnloadImage(meshImage); + // GenMeshPlane supplies an uploaded one-quad mesh centred on the XZ plane. + // Its texture coordinates already match the sprite; its positions move to + // the screen plane with the sprite origin at the top-left corner. + const mesh = GenMeshPlane(texture.width, texture.height, 1, 1); + const quadVertices = new Float32Array([ + 0, 0, 0, + texture.width, 0, 0, + 0, texture.height, 0, + texture.width, texture.height, 0, + ]); UpdateMeshBuffer(mesh, 0, quadVertices, quadVertices.byteLength, 0); - UpdateMeshBuffer(mesh, 1, quadTexcoords, quadTexcoords.byteLength, 0); const material = LoadMaterialDefault(); material.shader = shader; diff --git a/examples/lib/run-bunnymark.ts b/examples/lib/run-bunnymark.ts index 2be9f8a..c8950fb 100644 --- a/examples/lib/run-bunnymark.ts +++ b/examples/lib/run-bunnymark.ts @@ -39,36 +39,6 @@ import { } from "./bunny-instancing.ts"; import { frameNumbers } from "./frames.ts"; -// Generated raylib bindings are immutable const exports. Reading them once -// avoids repeated import resolution while preserving the binding API. -const black = BLACK; -const beginDrawing = BeginDrawing; -const clearBackground = ClearBackground; -const closeWindow = CloseWindow; -const colorType = Color; -const drawFps = DrawFPS; -const drawText = DrawText; -const drawTextureV = DrawTextureV; -const endDrawing = EndDrawing; -const getFrameTime = GetFrameTime; -const getRandomValue = GetRandomValue; -const initWindow = InitWindow; -const isKeyDown = IsKeyDown; -const isMouseButtonDown = IsMouseButtonDown; -const keySpace = KEY_SPACE; -const loadImageFromMemory = LoadImageFromMemory; -const loadTextureFromImage = LoadTextureFromImage; -const mouseButtonLeft = MOUSE_BUTTON_LEFT; -const raywhite = RAYWHITE; -const setRandomSeed = SetRandomSeed; -const setTargetFps = SetTargetFPS; -const takeScreenshot = TakeScreenshot; -const unloadImage = UnloadImage; -const unloadTexture = UnloadTexture; -const vector2Type = Vector2; -const windowShouldClose = WindowShouldClose; -const closeRaylibBinding = closeRaylib; - const screenWidth: number = 1280; const screenHeight: number = 720; const maxBunnies: number = 50000; @@ -78,14 +48,14 @@ export const runBunnymark = ( maxFrames: number, ): void => { const startedAt = Date.now(); - const image = loadImageFromMemory(".png", bunnyBytes, bunnyBytes.length); + const image = LoadImageFromMemory(".png", bunnyBytes, bunnyBytes.length); if (image.width !== 32 || image.height !== 32) { throw new Error("raybunny PNG failed to decode"); } - initWindow(screenWidth, screenHeight, "GocciaScript + raylib: Bunnymark"); - const texture = loadTextureFromImage(image); - unloadImage(image); + InitWindow(screenWidth, screenHeight, "GocciaScript + raylib: Bunnymark"); + const texture = LoadTextureFromImage(image); + UnloadImage(image); const configuredSeed = globalThis.BUNNYMARK_RANDOM_SEED; const configuredInitialSprites = @@ -111,8 +81,8 @@ export const runBunnymark = ( : 0; const reportFinal = globalThis.BUNNYMARK_REPORT_FINAL === true; - if (typeof configuredSeed === "number") setRandomSeed(configuredSeed); - setTargetFps(targetFps); + if (typeof configuredSeed === "number") SetRandomSeed(configuredSeed); + SetTargetFPS(targetFps); const bunnies = createBunnyState(maxBunnies); addBunnies( @@ -120,14 +90,14 @@ export const runBunnymark = ( initialSprites, screenWidth / 2, screenHeight / 2, - getRandomValue, + GetRandomValue, ); const instancing = requestedDrawPath === "instanced" ? createBunnyInstancing(texture) : null; const drawPath = instancing === null ? "direct" : "instanced"; - const drawPosition = vector2Type.create({ x: 0, y: 0 }); - const drawColor = colorType.create({ r: 0, g: 0, b: 0, a: 255 }); + const drawPosition = Vector2.create({ x: 0, y: 0 }); + const drawColor = Color.create({ r: 0, g: 0, b: 0, a: 255 }); const startupMilliseconds = Date.now() - startedAt; console.log( "Bunnymark startup_ms=" + @@ -145,20 +115,20 @@ export const runBunnymark = ( try { for (const frame of frameNumbers(maxFrames)) { - if (windowShouldClose()) break; + if (WindowShouldClose()) break; const deltaScale = fixedDeltaScale === null - ? getFrameTime() * 60 + ? GetFrameTime() * 60 : fixedDeltaScale; if ( - isMouseButtonDown(mouseButtonLeft) || - isKeyDown(keySpace) + IsMouseButtonDown(MOUSE_BUTTON_LEFT) || + IsKeyDown(KEY_SPACE) ) { addBunnies( bunnies, 100, screenWidth / 2, screenHeight / 2, - getRandomValue, + GetRandomValue, ); } @@ -171,8 +141,8 @@ export const runBunnymark = ( screenHeight, ); - beginDrawing(); - clearBackground(black); + BeginDrawing(); + ClearBackground(BLACK); if (instancing !== null) { drawBunnyInstances(instancing, bunnies); } else { @@ -184,23 +154,23 @@ export const runBunnymark = ( drawColor.g = bunnies.colors[colorOffset + 1]; drawColor.b = bunnies.colors[colorOffset + 2]; drawColor.a = bunnies.colors[colorOffset + 3]; - drawTextureV(texture, drawPosition, drawColor); + DrawTextureV(texture, drawPosition, drawColor); } } if (showOverlay) { - drawText("bunnies: " + bunnies.length, 12, 10, 20, raywhite); - drawText( + DrawText("bunnies: " + bunnies.length, 12, 10, 20, RAYWHITE); + DrawText( "hold mouse-left or Space to add 100", 12, 34, 20, - raywhite, + RAYWHITE, ); - drawFps(screenWidth - 100, 10); + DrawFPS(screenWidth - 100, 10); } - endDrawing(); + EndDrawing(); if (typeof capturePath === "string" && frame === captureFrame) { - takeScreenshot(capturePath); + TakeScreenshot(capturePath); } framesRendered += 1; @@ -242,9 +212,9 @@ export const runBunnymark = ( observedFps.toFixed(2), ); } - if (instancing === null) unloadTexture(texture); + if (instancing === null) UnloadTexture(texture); else unloadBunnyInstancing(instancing); - closeWindow(); - closeRaylibBinding(); + CloseWindow(); + closeRaylib(); } }; diff --git a/goccia.json b/goccia.json index 886149b..9ccdb37 100644 --- a/goccia.json +++ b/goccia.json @@ -1,5 +1,7 @@ { "source-type": "module", "mode": "bytecode", - "unsafe-ffi": true + "permissions": { + "allow-ffi": true + } } diff --git a/metadata/gocciascript.json b/metadata/gocciascript.json index d7bc310..bdda7c8 100644 --- a/metadata/gocciascript.json +++ b/metadata/gocciascript.json @@ -1,10 +1,8 @@ { - "version": "0.10.0", - "commit": "c477011ed305be05b371ff5f54ad0b54de9ac0cc", - "tagObject": "776d31393b625ef24c190c9b7d5fba5bad100a8f", - "release": "https://github.com/frostney/GocciaScript/releases/tag/0.10.0", + "version": "0.14.0", + "commit": "72cb20505832bd2e0f4db0ef8d46d2a4ca85140c", + "release": "https://github.com/frostney/GocciaScript/releases/tag/0.14.0", "ffi": { - "maxArguments": 8, - "mixedTopLevelF32": false + "maxArguments": 64 } } diff --git a/package.json b/package.json index 5a5ee08..88e8430 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,15 @@ { "name": "@frostney/gocciascript-raylib", "version": "0.1.0", - "description": "Generated raylib 6.0 dynamic-library bindings for GocciaScript 0.10.0", + "description": "Generated raylib 6.0 dynamic-library bindings for GocciaScript 0.14.0", "type": "module", + "exports": { + ".": { + "types": "./bindings/raylib.d.ts", + "default": "./bindings/raylib.ts" + }, + "./*": "./*" + }, "license": "MIT", "engines": { "node": ">=24" @@ -30,9 +37,9 @@ "generate": "node scripts/normalize-api.mjs && node scripts/generate-bindings.mjs && node scripts/generate-abi.mjs", "check:generated": "node scripts/check-generated.mjs", "test": "npm run test:goccia:interpreter && npm run test:goccia:bytecode", - "test:goccia:interpreter": "\"${GOCCIA_TEST_RUNNER:-GocciaTestRunner}\" tests/unit", - "test:goccia:bytecode": "\"${GOCCIA_TEST_RUNNER:-GocciaTestRunner}\" tests/unit --mode=bytecode", - "test:types": "tsc --noEmit --strict --skipLibCheck false --lib ES2022 bindings/raylib.d.ts", + "test:goccia:interpreter": "\"${GOCCIA_TEST_RUNNER:-GocciaTestRunner}\" tests/unit --mode=interpreted --ignore-config-permissions", + "test:goccia:bytecode": "\"${GOCCIA_TEST_RUNNER:-GocciaTestRunner}\" tests/unit --mode=bytecode --ignore-config-permissions", + "test:types": "tsc --noEmit --strict --skipLibCheck false --lib ES2022 --module esnext --moduleResolution bundler --moduleSuffixes .d bindings/raylib.d.ts tests/types/consumer.ts", "test:abi": "node tests/integration/abi.test.mjs", "test:ffi": "node tests/integration/ffi.test.mjs", "test:bunnymark:visual": "node tests/integration/bunnymark-visual.test.mjs", diff --git a/scripts/generate-bindings.mjs b/scripts/generate-bindings.mjs index 18b68db..4821f2d 100644 --- a/scripts/generate-bindings.mjs +++ b/scripts/generate-bindings.mjs @@ -13,5 +13,5 @@ await Promise.all([ console.log( `Generated ${artifacts.stats.generated} raylib functions; ` + - `skipped ${artifacts.stats.skipped}; post-upstream target ${artifacts.stats.target}.`, + `skipped ${artifacts.stats.skipped}.`, ); diff --git a/scripts/lib/abi.mjs b/scripts/lib/abi.mjs index ab7ea7a..48aca24 100644 --- a/scripts/lib/abi.mjs +++ b/scripts/lib/abi.mjs @@ -1,4 +1,4 @@ -import { ffiFieldName, renderBindingArtifacts } from "./bindings.mjs"; +import { renderBindingArtifacts } from "./bindings.mjs"; function cString(value) { return JSON.stringify(value); @@ -137,18 +137,17 @@ const probePointer = raylibLibrary.symbol("InitWindow"); const fieldOffset = (Type, field, kind) => { const value = Type.create(); - if (kind === "aggregate") return value[field].byteOffset; + // FFI.metadata stays valid when a native field is itself named "buffer". + if (kind === "aggregate") return FFI.metadata(value[field]).byteOffset; + const storage = new Uint8Array(FFI.metadata(value).buffer); if (kind === "pointer") { value[field] = probePointer; const width = Type.size > 4 ? 8 : 4; - return findBytes( - new Uint8Array(value.buffer), - pointerBytes(probePointer).subarray(0, width), - ); + return findBytes(storage, pointerBytes(probePointer).subarray(0, width)); } const probe = bytesFor(kind); value[field] = probe.value; - return findBytes(new Uint8Array(value.buffer), probe.bytes); + return findBytes(storage, probe.bytes); }; `; @@ -157,7 +156,7 @@ const fieldOffset = (Type, field, kind) => { source += `console.log("STRUCT\\t${struct.name}\\t" + ${struct.name}.size + "\\t" + ${struct.name}.alignment);\n`; for (const field of struct.fields) { const resolved = resolveType(field.type, "field"); - source += `console.log("FIELD\\t${struct.name}\\t${field.name}\\t" + fieldOffset(${struct.name}, ${cString(ffiFieldName(field.name))}, ${cString(probeKind(resolved))}));\n`; + source += `console.log("FIELD\\t${struct.name}\\t${field.name}\\t" + fieldOffset(${struct.name}, ${cString(field.name)}, ${cString(probeKind(resolved))}));\n`; } } diff --git a/scripts/lib/bindings.mjs b/scripts/lib/bindings.mjs index 604e521..0821e24 100644 --- a/scripts/lib/bindings.mjs +++ b/scripts/lib/bindings.mjs @@ -28,12 +28,6 @@ const scalarTypes = new Map([ ["void", "void"], ]); -const intentionalSkips = new Map([ - ["TraceLog", "varargs"], - ["TextFormat", "varargs"], - ["DrawBillboardPro", "more-than-8-arguments"], -]); - const nullableStringParameters = new Map([ ["LoadShader", [0, 1]], ["LoadShaderFromMemory", [0, 1]], @@ -53,11 +47,9 @@ const ownedTextReturns = new Set([ "TextInsertAlloc", ]); -export function ffiFieldName(name) { - if (name === "buffer") return "nativeBuffer"; - if (name === "byteOffset") return "nativeByteOffset"; - return name; -} +// Aggregate values expose these backing-store properties directly unless a +// native field of the same name takes precedence; FFI.metadata always works. +const aggregateMetadataNames = ["buffer", "byteOffset", "size"]; function cleanType(type) { return type @@ -178,9 +170,6 @@ function resolveType(type, env, context = "argument", stack = []) { aliasTarget: resolved, }; } - if (name === "...") { - throw new Error("varargs are unsupported"); - } throw new Error(`Unsupported C type: ${type}`); } @@ -244,9 +233,16 @@ export const RAYLIB_BINDING_INFO = { gocciaScriptVersion: "${goccia.version}", linkage: "dynamic", }; -export const RAYLIB_FIELD_ALIASES = { - AudioStream: { buffer: "nativeBuffer" }, -}; + +if (typeof FFI === "undefined") { + throw new TypeError( + "The raylib bindings need GocciaScript's ffi capability. Grant it with " + + '--allow-ffi, or with "permissions": { "allow-ffi": true } in a ' + + "trusted goccia.json.", + ); +} + +const nullableUtf8String = FFI.nullable("utf8string"); const raylibCandidates = (): string[] => { const suffix = FFI.suffix; @@ -307,6 +303,9 @@ function renderDefinitions(api, env, orderedStructs) { let js = ""; let dts = `// Generated from official raylib ${api.source.version}. Do not edit. +// Only the declarations marked export are part of the module. +export {}; + export interface FFIPointer { readonly address: number; readonly isNull: boolean; @@ -338,29 +337,58 @@ export type FFIPointerInput = | ArrayBuffer | SharedArrayBuffer | FFITypedArray - | FFIAggregateValue + | FFIAggregate | null; -export interface FFIAggregateValue { +declare const ffiAggregate: unique symbol; +/** Any GocciaScript FFI aggregate value. */ +export interface FFIAggregate { + readonly [ffiAggregate]: true; +} +/** Backing store of an aggregate, as returned by GocciaScript's \`FFI.metadata(value)\`. */ +export interface FFIAggregateMetadata { readonly buffer: ArrayBuffer; readonly byteOffset: number; readonly size: number; } +/** + * Aggregate that also exposes its backing store directly. A native field named + * like a metadata property takes precedence; \`FFI.metadata(value)\` always works. + */ +export interface FFIAggregateValue extends FFIAggregate, FFIAggregateMetadata {} export interface FFIArrayValue extends FFIAggregateValue { readonly length: number; [index: number]: T; } -export interface FFIStructDescriptor { +export interface FFIStructDescriptor { readonly kind: "struct"; readonly size: number; readonly alignment: number; create(initializer?: Partial): T; } +declare const ffiVarargs: unique symbol; +/** Typed variadic tail created by GocciaScript's \`FFI.varargs(types, values)\`. */ +export interface FFIVarargs { + readonly [ffiVarargs]: true; +} +/** + * The members of GocciaScript's \`FFI\` global that callers of these bindings + * use. GocciaScript ships no declarations, so type-checked code declares the + * global itself: \`declare const FFI: GocciaFFI;\`. + */ +export interface GocciaFFI { + varargs(types: readonly unknown[], values: readonly unknown[]): FFIVarargs; + metadata(value: FFIAggregate): FFIAggregateMetadata; +} export interface FFILibrary { readonly path: string; readonly closed: boolean; bind( name: string, - signature: { args: readonly unknown[]; returns: unknown }, + signature: { + args: readonly unknown[]; + returns: unknown; + variadic?: boolean; + }, ): (...args: unknown[]) => unknown; symbol(name: string): FFIPointer; close(): void; @@ -371,9 +399,6 @@ export declare const RAYLIB_BINDING_INFO: { gocciaScriptVersion: string; linkage: "dynamic"; }; -export declare const RAYLIB_FIELD_ALIASES: { - AudioStream: { buffer: "nativeBuffer" }; -}; export declare const raylibLibrary: FFILibrary; export declare function closeRaylib(): void; @@ -390,17 +415,26 @@ export declare function closeRaylib(): void; js += docComment(struct.description); js += `export const ${struct.name} = FFI.struct({\n`; for (const field of fields) { - js += ` ${JSON.stringify(ffiFieldName(field.name))}: ${field.resolved.descriptor},\n`; + js += ` ${JSON.stringify(field.name)}: ${field.resolved.descriptor},\n`; } js += "});\n\n"; + const shadowed = aggregateMetadataNames.filter((name) => + fields.some((field) => field.name === name), + ); + const base = + shadowed.length === 0 + ? "FFIAggregateValue" + : `FFIAggregate, Omit JSON.stringify(name)) + .join(" | ")}>`; dts += docComment(struct.description); - dts += `export interface ${struct.name}Value extends FFIAggregateValue {\n`; + dts += `export interface ${struct.name}Value extends ${base} {\n`; for (const field of fields) { - if (ffiFieldName(field.name) !== field.name) { - dts += ` /** Native C field: ${field.name}. */\n`; + if (shadowed.includes(field.name)) { + dts += ` /** Native C field; read the backing store with \`FFI.metadata(value).${field.name}\`. */\n`; } - dts += ` ${JSON.stringify(ffiFieldName(field.name))}: ${field.resolved.ts};\n`; + dts += ` ${JSON.stringify(field.name)}: ${field.resolved.ts};\n`; } dts += "}\n"; dts += `export declare const ${struct.name}: FFIStructDescriptor<${struct.name}Value>;\n\n`; @@ -469,23 +503,27 @@ export declare function closeRaylib(): void; } function classifyFunction(fn, env, goccia) { - if (intentionalSkips.has(fn.name)) { + const variadic = fn.params.at(-1)?.type === "..."; + const params = variadic ? fn.params.slice(0, -1) : fn.params; + if (params.length > goccia.ffi.maxArguments) { return { supported: false, - reason: intentionalSkips.get(fn.name), - category: "intentional-0.1-scope", - }; - } - if (fn.params.length > goccia.ffi.maxArguments) { - return { - supported: false, - reason: "more-than-8-arguments", - category: "stable-runtime-limit", + reason: `more-than-${goccia.ffi.maxArguments}-arguments`, + category: "runtime-limit", }; } try { - const args = fn.params.map((param) => resolveType(param.type, env, "argument")); + const nullableParameters = nullableStringParameters.get(fn.name) ?? []; + const args = params.map((param, index) => + nullableParameters.includes(index) + ? { + kind: "utf8string", + descriptor: "nullableUtf8String", + ts: "string | null", + } + : resolveType(param.type, env, "argument"), + ); let returns = resolveType(fn.returnType, env, "return"); if (ownedTextReturns.has(fn.name)) { returns = { @@ -494,18 +532,7 @@ function classifyFunction(fn, env, goccia) { ts: "FFIPointer", }; } - const hasF32 = args.some((arg) => arg.kind === "f32"); - const hasOther = args.some((arg) => arg.kind !== "f32"); - if (!goccia.ffi.mixedTopLevelF32 && hasF32 && hasOther) { - return { - supported: false, - reason: "mixed-top-level-f32", - category: "stable-runtime-limit", - args, - returns, - }; - } - return { supported: true, args, returns }; + return { supported: true, params, args, returns, variadic }; } catch (error) { return { supported: false, @@ -516,7 +543,24 @@ function classifyFunction(fn, env, goccia) { } } +// A stale entry would silently drop a nullable parameter, so drift in the +// pinned API fails generation instead. +function assertNullableStringParameters(api, env) { + const functions = new Map(api.functions.map((fn) => [fn.name, fn])); + for (const [name, indices] of nullableStringParameters) { + const fn = functions.get(name); + if (!fn) throw new Error(`Nullable string function is not in the API: ${name}`); + for (const index of indices) { + const type = fn.params[index]?.type; + if (!type || resolveType(type, env, "argument").kind !== "utf8string") { + throw new Error(`${name} parameter ${index} is not a C string`); + } + } + } +} + function renderFunctions(api, env, goccia, deferredDefines) { + assertNullableStringParameters(api, env); let js = ""; let dts = ""; const generated = []; @@ -537,28 +581,18 @@ function renderFunctions(api, env, goccia, deferredDefines) { generated.push(fn.name); const args = classification.args.map((arg) => arg.descriptor).join(", "); + const variadic = classification.variadic ? "variadic: true, " : ""; js += docComment(fn.description); - js += `export const ${fn.name} = raylibLibrary.bind("${fn.name}", { args: [${args}], returns: ${classification.returns.descriptor} });\n\n`; - + js += `export const ${fn.name} = raylibLibrary.bind("${fn.name}", { args: [${args}], ${variadic}returns: ${classification.returns.descriptor} });\n\n`; + + const parameters = classification.params.map( + (param, index) => + `${param.name || `arg${index}`}: ${classification.args[index].ts}`, + ); + // GocciaScript requires exactly one FFI.varargs(types, values) tail. + if (classification.variadic) parameters.push("args: FFIVarargs"); dts += docComment(fn.description); - dts += `export declare function ${fn.name}(${fn.params - .map((param, index) => `${param.name || `arg${index}`}: ${classification.args[index].ts}`) - .join(", ")}): ${classification.returns.ts};\n\n`; - - const nullableParameters = nullableStringParameters.get(fn.name); - if (nullableParameters) { - const rawArgs = classification.args.map((arg, index) => - nullableParameters.includes(index) - ? { descriptor: '"pointer"', ts: "FFIPointerInput" } - : arg, - ); - js += `/** Raw-pointer variant for nullable C string parameters. */\n`; - js += `export const ${fn.name}Raw = raylibLibrary.bind("${fn.name}", { args: [${rawArgs.map((arg) => arg.descriptor).join(", ")}], returns: ${classification.returns.descriptor} });\n\n`; - dts += `/** Raw-pointer variant for nullable C string parameters. */\n`; - dts += `export declare function ${fn.name}Raw(${fn.params - .map((param, index) => `${param.name || `arg${index}`}: ${rawArgs[index].ts}`) - .join(", ")}): ${classification.returns.ts};\n\n`; - } + dts += `export declare function ${fn.name}(${parameters.join(", ")}): ${classification.returns.ts};\n\n`; } for (const define of deferredDefines) { @@ -570,16 +604,12 @@ function renderFunctions(api, env, goccia, deferredDefines) { } function renderSkipped(api, goccia, generated, skipped) { - const intentionalCount = skipped.filter( - (item) => item.category === "intentional-0.1-scope", - ).length; const report = { - schemaVersion: 1, + schemaVersion: 2, raylibVersion: api.source.version, raylibCommit: api.source.commit, gocciaScriptVersion: goccia.version, apiFunctions: api.inventory.functions, - callableTargetAfterUpstreamSupport: api.inventory.functions - intentionalCount, generatedFunctions: generated.length, skippedFunctions: skipped.length, skipped, @@ -600,11 +630,14 @@ GocciaScript ${goccia.version}. - Official API functions: ${report.apiFunctions} - Generated and callable on the pinned stable runtime: ${report.generatedFunctions} - Deterministically skipped: ${report.skippedFunctions} -- Target after mixed-top-level-\`f32\` support: ${report.callableTargetAfterUpstreamSupport} -The report is a compatibility boundary, not a claim that skipped functions are -implemented. Re-run the generator after the upstream FFI restriction is removed. -The three intentional 0.1 exclusions remain out of scope. +${ + skipped.length === 0 + ? `The pinned runtime binds the complete official API. A function that a future +raylib or runtime version cannot bind is listed here rather than shimmed.` + : `The report is a compatibility boundary, not a claim that skipped functions are +implemented. Re-run the generator after the limit below is removed.` + } `; for (const [key, items] of groups) { @@ -651,7 +684,6 @@ export async function renderBindingArtifacts() { stats: { generated: functions.generated.length, skipped: functions.skipped.length, - target: skipped.report.callableTargetAfterUpstreamSupport, }, api, env, diff --git a/scripts/validate-package.mjs b/scripts/validate-package.mjs index f8901ae..7ba9ebf 100644 --- a/scripts/validate-package.mjs +++ b/scripts/validate-package.mjs @@ -1,6 +1,6 @@ import assert from "node:assert/strict"; import { spawnSync } from "node:child_process"; -import { fromRoot } from "./lib/project.mjs"; +import { fromRoot, readJson } from "./lib/project.mjs"; const result = spawnSync("npm", ["pack", "--dry-run", "--json"], { cwd: fromRoot(), @@ -54,4 +54,16 @@ assert.ok( ![...files].some((path) => path.toLowerCase().endsWith(".wad")), "IWAD data must never ship in the npm package", ); +const { exports: entryPoints } = await readJson(fromRoot("package.json")); +assert.deepEqual( + entryPoints["."], + { types: "./bindings/raylib.d.ts", default: "./bindings/raylib.ts" }, + "The package entry point must be the generated bindings", +); +for (const target of Object.values(entryPoints["."])) { + assert.ok( + files.has(target.slice(2)), + `Package entry point ${target} is not in the tarball`, + ); +} console.log(`Package contents validated (${manifest.files.length} files).`); diff --git a/tests/abi/layout-probe.ts b/tests/abi/layout-probe.ts index 2abe21f..0a5373f 100644 --- a/tests/abi/layout-probe.ts +++ b/tests/abi/layout-probe.ts @@ -120,18 +120,17 @@ const probePointer = raylibLibrary.symbol("InitWindow"); const fieldOffset = (Type, field, kind) => { const value = Type.create(); - if (kind === "aggregate") return value[field].byteOffset; + // FFI.metadata stays valid when a native field is itself named "buffer". + if (kind === "aggregate") return FFI.metadata(value[field]).byteOffset; + const storage = new Uint8Array(FFI.metadata(value).buffer); if (kind === "pointer") { value[field] = probePointer; const width = Type.size > 4 ? 8 : 4; - return findBytes( - new Uint8Array(value.buffer), - pointerBytes(probePointer).subarray(0, width), - ); + return findBytes(storage, pointerBytes(probePointer).subarray(0, width)); } const probe = bytesFor(kind); value[field] = probe.value; - return findBytes(new Uint8Array(value.buffer), probe.bytes); + return findBytes(storage, probe.bytes); }; console.log("STRUCT\tVector2\t" + Vector2.size + "\t" + Vector2.alignment); @@ -292,7 +291,7 @@ console.log("FIELD\tWave\tsampleSize\t" + fieldOffset(Wave, "sampleSize", "u32") console.log("FIELD\tWave\tchannels\t" + fieldOffset(Wave, "channels", "u32")); console.log("FIELD\tWave\tdata\t" + fieldOffset(Wave, "data", "pointer")); console.log("STRUCT\tAudioStream\t" + AudioStream.size + "\t" + AudioStream.alignment); -console.log("FIELD\tAudioStream\tbuffer\t" + fieldOffset(AudioStream, "nativeBuffer", "pointer")); +console.log("FIELD\tAudioStream\tbuffer\t" + fieldOffset(AudioStream, "buffer", "pointer")); console.log("FIELD\tAudioStream\tprocessor\t" + fieldOffset(AudioStream, "processor", "pointer")); console.log("FIELD\tAudioStream\tsampleRate\t" + fieldOffset(AudioStream, "sampleRate", "u32")); console.log("FIELD\tAudioStream\tsampleSize\t" + fieldOffset(AudioStream, "sampleSize", "u32")); diff --git a/tests/fixtures/ffi-smoke.ts b/tests/fixtures/ffi-smoke.ts index 67c0f2d..500347c 100644 --- a/tests/fixtures/ffi-smoke.ts +++ b/tests/fixtures/ffi-smoke.ts @@ -1,13 +1,16 @@ import bunnyBytes from "../../examples/assets/raybunny.png" with { type: "bytes" }; import { + AudioStream, + CheckCollisionCircles, Color, + ColorAlpha, ColorToInt, EncodeDataBase64, GetColor, GetFileExtension, GetRandomValue, IsPathFile, - LoadAutomationEventListRaw, + LoadAutomationEventList, LoadFileText, LoadImageFromMemory, MemAlloc, @@ -15,10 +18,13 @@ import { RAYLIB_BINDING_INFO, SetRandomSeed, TextCopy, + TextFormat, TextLength, + TextReplace, UnloadAutomationEventList, UnloadFileText, UnloadImage, + Vector2, closeRaylib, } from "../../bindings/raylib.ts"; @@ -58,9 +64,38 @@ const encoded = EncodeDataBase64( assert(encoded.isNull === false && outputSize[0] > 0, "owned encoded text pointer"); MemFree(encoded); -const emptyEvents = LoadAutomationEventListRaw(null); -assert(emptyEvents.count === 0, "nullable C string raw binding"); +const emptyEvents = LoadAutomationEventList(null); +// The integration test checks raylib's log to prove that NULL, not the text +// "null", reached the native call. +assert(emptyEvents.count === 0, "empty automation event list"); UnloadAutomationEventList(emptyEvents); +assert(TextReplace("a-b-c", "-", null) === "abc", "null replacement text"); +assert(TextReplace("a-b-c", "-", "+") === "a+b+c", "nullable C string text"); + +const faded = ColorAlpha(Color.create({ r: 18, g: 52, b: 86, a: 255 }), 0.5); +assert( + faded.r === 18 && faded.g === 52 && faded.b === 86 && faded.a === 127, + "aggregate argument mixed with a float argument", +); +const origin = Vector2.create({ x: 0, y: 0 }); +const apart = Vector2.create({ x: 10, y: 0 }); +assert(CheckCollisionCircles(origin, 6.5, apart, 4.5) === true, "overlapping circles"); +assert(CheckCollisionCircles(origin, 4.5, apart, 4.5) === false, "separate circles"); + +const formatted = TextFormat( + "%s %03d %.2f", + FFI.varargs(["utf8string", "i32", "f32"], ["raylib", 7, 1.5]), +); +assert(formatted === "raylib 007 1.50", "variadic native call"); + +const stream = AudioStream.create({ sampleRate: 44100, channels: 2 }); +assert(stream.buffer.isNull === true, "exact native field named buffer"); +const streamStorage = FFI.metadata(stream); +assert( + streamStorage.buffer instanceof ArrayBuffer && + streamStorage.size === AudioStream.size, + "aggregate backing store through FFI.metadata", +); const image = LoadImageFromMemory(".png", bunnyBytes, bunnyBytes.length); assert(image.width === 32 && image.height === 32, "byte-pointer image load"); diff --git a/tests/integration/abi.test.mjs b/tests/integration/abi.test.mjs index 3a551cb..57e3807 100644 --- a/tests/integration/abi.test.mjs +++ b/tests/integration/abi.test.mjs @@ -5,13 +5,14 @@ import { join } from "node:path"; import { tmpdir } from "node:os"; import { fromRoot } from "../../scripts/lib/project.mjs"; import { - findGocciaLoader, + acceptConfigPermissions, + findGocciaRunner, findRaylibInclude, parseLayoutLines, } from "./helpers.mjs"; -const [loader, includeDir] = await Promise.all([ - findGocciaLoader(), +const [runner, includeDir] = await Promise.all([ + findGocciaRunner(), findRaylibInclude(), ]); const temporaryDirectory = await mkdtemp(join(tmpdir(), "goccia-raylib-abi-")); @@ -41,11 +42,15 @@ assert.equal( const native = spawnSync(oracle, [], { encoding: "utf8" }); assert.equal(native.status, 0, `Native ABI oracle failed:\n${native.stderr}`); -const goccia = spawnSync(loader, [fromRoot("tests/abi/layout-probe.ts")], { - cwd: fromRoot(), - encoding: "utf8", - env: process.env, -}); +const goccia = spawnSync( + runner, + [acceptConfigPermissions, fromRoot("tests/abi/layout-probe.ts")], + { + cwd: fromRoot(), + encoding: "utf8", + env: process.env, + }, +); assert.equal( goccia.status, 0, diff --git a/tests/integration/bunnymark-visual.test.mjs b/tests/integration/bunnymark-visual.test.mjs index 1e6f332..7ad59e5 100644 --- a/tests/integration/bunnymark-visual.test.mjs +++ b/tests/integration/bunnymark-visual.test.mjs @@ -3,9 +3,9 @@ import { createHash } from "node:crypto"; import { mkdir, readFile, unlink } from "node:fs/promises"; import { spawnSync } from "node:child_process"; import { fromRoot } from "../../scripts/lib/project.mjs"; -import { findGocciaLoader } from "./helpers.mjs"; +import { acceptConfigPermissions, findGocciaRunner } from "./helpers.mjs"; -const loader = await findGocciaLoader(); +const runner = await findGocciaRunner(); const cacheDirectory = fromRoot(".cache"); const directPath = fromRoot(".cache/bunnymark-direct.png"); const instancedPath = fromRoot(".cache/bunnymark-instanced.png"); @@ -20,11 +20,15 @@ for (const path of [directPath, instancedPath]) { } const runFixture = (fixture, expectedPath) => { - const result = spawnSync(loader, [fromRoot(fixture)], { - cwd: fromRoot(), - encoding: "utf8", - env: process.env, - }); + const result = spawnSync( + runner, + [acceptConfigPermissions, fromRoot(fixture)], + { + cwd: fromRoot(), + encoding: "utf8", + env: process.env, + }, + ); assert.equal( result.status, 0, diff --git a/tests/integration/ffi.test.mjs b/tests/integration/ffi.test.mjs index 61f794a..6017ee2 100644 --- a/tests/integration/ffi.test.mjs +++ b/tests/integration/ffi.test.mjs @@ -1,20 +1,29 @@ import assert from "node:assert/strict"; import { spawnSync } from "node:child_process"; import { fromRoot } from "../../scripts/lib/project.mjs"; -import { findGocciaLoader } from "./helpers.mjs"; +import { acceptConfigPermissions, findGocciaRunner } from "./helpers.mjs"; -const loader = await findGocciaLoader(); -const result = spawnSync(loader, [fromRoot("tests/fixtures/ffi-smoke.ts")], { - cwd: fromRoot(), - encoding: "utf8", - env: process.env, -}); +const runner = await findGocciaRunner(); +const result = spawnSync( + runner, + [acceptConfigPermissions, fromRoot("tests/fixtures/ffi-smoke.ts")], + { + cwd: fromRoot(), + encoding: "utf8", + env: process.env, + }, +); assert.equal( result.status, 0, `Representative raylib FFI calls failed:\n${result.stdout}\n${result.stderr}`, ); assert.match(result.stdout, /raylib ffi smoke ok/); +assert.match( + result.stdout, + /AUTOMATION: New empty events list loaded successfully/, + "LoadAutomationEventList(null) did not pass a native NULL", +); console.log( - "Representative scalar, bool, pointer, aggregate, UTF-8, owned-text, and nullable calls passed.", + "Representative scalar, bool, pointer, aggregate, mixed-float, UTF-8, owned-text, nullable, and variadic calls passed.", ); diff --git a/tests/integration/helpers.mjs b/tests/integration/helpers.mjs index 28840b2..0f2ccc3 100644 --- a/tests/integration/helpers.mjs +++ b/tests/integration/helpers.mjs @@ -12,11 +12,15 @@ async function executable(path) { } } -export async function findGocciaLoader() { - const configured = await executable(process.env.GOCCIA_LOADER); +// The repository goccia.json requests the ffi capability. The fixtures accept +// that request for one run instead of depending on a per-user trust store. +export const acceptConfigPermissions = "--accept-config-permissions"; + +export async function findGocciaRunner() { + const configured = await executable(process.env.GOCCIA_RUNNER); if (configured) return configured; try { - const discovered = execFileSync("sh", ["-c", "command -v GocciaScriptLoader"], { + const discovered = execFileSync("sh", ["-c", "command -v GocciaRunner"], { encoding: "utf8", }).trim(); if (await executable(discovered)) return discovered; @@ -24,7 +28,7 @@ export async function findGocciaLoader() { // Report one actionable error below. } throw new Error( - "GocciaScriptLoader 0.10.0 was not found; set GOCCIA_LOADER to the release binary", + "GocciaRunner 0.14.0 was not found; set GOCCIA_RUNNER to the release binary", ); } diff --git a/tests/linux/Dockerfile b/tests/linux/Dockerfile index 37fd1ea..318c15f 100644 --- a/tests/linux/Dockerfile +++ b/tests/linux/Dockerfile @@ -44,18 +44,18 @@ RUN case "$TARGETARCH" in \ esac \ && mkdir -p /opt/gocciascript \ && curl --fail --location \ - "https://github.com/frostney/GocciaScript/releases/download/0.10.0/gocciascript-0.10.0-linux-${goccia_arch}.tar.gz" \ + "https://github.com/frostney/GocciaScript/releases/download/0.14.0/gocciascript-0.14.0-linux-${goccia_arch}.tar.gz" \ --output /tmp/gocciascript.tar.gz \ && tar -xzf /tmp/gocciascript.tar.gz -C /opt/gocciascript --strip-components=1 \ && chmod +x \ - /opt/gocciascript/GocciaScriptLoader \ + /opt/gocciascript/GocciaRunner \ /opt/gocciascript/GocciaTestRunner \ - && ln -s /opt/gocciascript/GocciaScriptLoader /usr/local/bin/GocciaScriptLoader \ + && ln -s /opt/gocciascript/GocciaRunner /usr/local/bin/GocciaRunner \ && ln -s /opt/gocciascript/GocciaTestRunner /usr/local/bin/GocciaTestRunner WORKDIR /workspace COPY . . -ENV GOCCIA_LOADER=/usr/local/bin/GocciaScriptLoader +ENV GOCCIA_RUNNER=/usr/local/bin/GocciaRunner ENV GOCCIA_TEST_RUNNER=/usr/local/bin/GocciaTestRunner ENV RAYLIB_INCLUDE_DIR=/usr/local/include @@ -68,11 +68,12 @@ RUN npm ci \ && npm run test:ffi \ && npm run pack:check \ && mkdir -p .cache \ - && xvfb-run -a GocciaScriptLoader tests/fixtures/basic-window-smoke.ts \ - && xvfb-run -a GocciaScriptLoader tests/fixtures/basic-input-smoke.ts \ - && xvfb-run -a GocciaScriptLoader tests/fixtures/image-loading-smoke.ts \ - && xvfb-run -a GocciaScriptLoader examples/smoke.ts \ + && xvfb-run -a GocciaRunner -P tests/fixtures/basic-window-smoke.ts \ + && xvfb-run -a GocciaRunner -P tests/fixtures/basic-input-smoke.ts \ + && xvfb-run -a GocciaRunner -P tests/fixtures/image-loading-smoke.ts \ + && xvfb-run -a GocciaRunner -P examples/smoke.ts \ && xvfb-run -a npm run test:bunnymark:visual \ - && xvfb-run -a GocciaScriptLoader tests/fixtures/doom-smoke.ts \ + && xvfb-run -a GocciaRunner -P tests/fixtures/doom-smoke.ts \ && test -s .cache/doom-smoke.png \ - && xvfb-run -a make -C examples/doom-gpl smoke + && make -C examples/doom-gpl test-palette \ + && xvfb-run -a make -C examples/doom-gpl smoke GOCCIA_FLAGS=-P diff --git a/tests/types/consumer.ts b/tests/types/consumer.ts new file mode 100644 index 0000000..b75d6be --- /dev/null +++ b/tests/types/consumer.ts @@ -0,0 +1,47 @@ +// Type-only consumer of the generated declarations; `npm run test:types` +// compiles it and never runs it. That script passes `--moduleSuffixes .d` so +// the import resolves to raylib.d.ts rather than the untyped runtime module. +import type * as Raylib from "../../bindings/raylib"; +import type { + AudioStreamValue, + ColorValue, + FFIPointer, + FFIPointerInput, + GocciaFFI, +} from "../../bindings/raylib"; + +declare const raylib: typeof Raylib; +declare const FFI: GocciaFFI; + +const stream: AudioStreamValue = raylib.AudioStream.create({ + sampleRate: 44100, +}); +export const nativeField: FFIPointer = stream.buffer; +export const streamStorage: ArrayBuffer = FFI.metadata(stream).buffer; +export const streamSize: number = stream.size; +export const streamPointer: FFIPointerInput = stream; + +const color: ColorValue = raylib.Color.create({ r: 1, g: 2, b: 3, a: 4 }); +export const colorStorage: ArrayBuffer = color.buffer; + +raylib.LoadShader(null, "fragment.glsl"); +export const replaced: string = raylib.TextReplace("a-b", "-", null); +export const formatted: string = raylib.TextFormat( + "%d", + FFI.varargs(["i32"], [1]), +); +raylib.TraceLog(raylib.LOG_INFO, "ready", FFI.varargs([], [])); +raylib.DrawCircle(20, 20, 10.5, raylib.RED); + +// @ts-expect-error the variadic tail is required +raylib.TextFormat("%d"); +// @ts-expect-error a plain array is not a typed variadic tail +raylib.TextFormat("%d", [1]); +// @ts-expect-error only documented nullable C strings accept null +raylib.TextLength(null); +// @ts-expect-error AudioStream.buffer is the native pointer field +export const wrongStorage: ArrayBuffer = stream.buffer; +// @ts-expect-error plain objects are not FFI aggregates +export const notAggregate: FFIPointerInput = { size: 4 }; +// @ts-expect-error the aggregate brand is not an export +export type Brand = typeof Raylib.ffiAggregate; diff --git a/tests/unit/bindings.test.ts b/tests/unit/bindings.test.ts index 9a4fe6b..f6470e5 100644 --- a/tests/unit/bindings.test.ts +++ b/tests/unit/bindings.test.ts @@ -3,12 +3,11 @@ import declarations from "../../bindings/raylib.d.ts" with { type: "text" }; import skipped from "../../SKIPPED.json" with { type: "json" }; describe("generated raylib bindings", () => { - test("exposes the stable-safe dynamic API", () => { + test("exposes the complete dynamic API", () => { expect(skipped.apiFunctions).toBe(600); - expect(skipped.callableTargetAfterUpstreamSupport).toBe(597); - expect(skipped.generatedFunctions + skipped.skippedFunctions).toBe( - skipped.apiFunctions, - ); + expect(skipped.generatedFunctions).toBe(600); + expect(skipped.skipped).toEqual([]); + expect(raylibSource.match(/ = raylibLibrary\.bind\(/g)).toHaveLength(600); expect(raylibSource).toContain('linkage: "dynamic"'); for (const name of [ @@ -29,12 +28,18 @@ describe("generated raylib bindings", () => { expect(raylibSource).toContain("export const KEY_W = 87;"); expect(raylibSource).toContain("export const Quaternion = Vector4;"); expect(raylibSource).toContain('export const AudioCallback = "pointer";'); + expect(raylibSource).toMatch( + /export const AudioStream = FFI\.struct\(\{\n "buffer": "pointer",/, + ); + expect(declarations).toContain( + 'interface AudioStreamValue extends FFIAggregate, Omit', + ); expect(declarations).toContain("interface VrStereoConfigValue"); expect(declarations).toContain("export type FFITypedArray ="); expect(declarations).not.toContain("| ArrayBufferView"); }); - test("preserves owned pointers and nullable string variants", () => { + test("preserves owned pointers and nullable string parameters", () => { expect(raylibSource).toMatch( /export const LoadFileText = .*returns: "pointer"/, ); @@ -51,30 +56,36 @@ describe("generated raylib bindings", () => { /function LoadImageFromMemory\(fileType: string, fileData: FFIPointerInput/, ); - for (const name of [ - "LoadShaderRaw", - "LoadShaderFromMemoryRaw", - "LoadAutomationEventListRaw", - "TextReplaceRaw", - "TextReplaceAllocRaw", - "TextReplaceBetweenRaw", - "TextReplaceBetweenAllocRaw", - ]) { - expect(raylibSource).toContain(`export const ${name} =`); - expect(declarations).toContain(`function ${name}(`); - } + expect(raylibSource.match(/nullableUtf8String[,\]]/g)).toHaveLength(9); + expect(raylibSource).toMatch( + /export const LoadShader = .*args: \[nullableUtf8String, nullableUtf8String\]/, + ); + expect(raylibSource).toMatch( + /export const TextReplaceBetween = .*"utf8string", nullableUtf8String\]/, + ); + expect(declarations).toContain( + "function LoadAutomationEventList(fileName: string | null)", + ); + expect(raylibSource).not.toContain("export const LoadShaderRaw ="); + expect(raylibSource).not.toContain("export const TextReplaceRaw ="); }); - test("reports stable FFI and intentional exclusions explicitly", () => { - const byName = new Map( - skipped.skipped.map((item) => [item.name, item]), + test("binds variadic, high-arity, and mixed float signatures", () => { + expect(raylibSource).toMatch( + /export const TraceLog = .*args: \["i32", "utf8string"\], variadic: true, returns: "void"/, ); - - expect(byName.get("TraceLog").reason).toBe("varargs"); - expect(byName.get("TextFormat").reason).toBe("varargs"); - expect(byName.get("DrawBillboardPro").reason).toBe( - "more-than-8-arguments", + expect(raylibSource).toMatch( + /export const TextFormat = .*args: \["utf8string"\], variadic: true, returns: "utf8string"/, + ); + expect(raylibSource.match(/variadic: true/g)).toHaveLength(2); + expect(declarations).toContain( + "function TextFormat(text: string, args: FFIVarargs): string;", + ); + expect(raylibSource).toMatch( + /export const DrawBillboardPro = .*args: \[Camera3D, Texture, Rectangle, Vector3, Vector3, Vector2, Vector2, "f32", Color\]/, + ); + expect(raylibSource).toMatch( + /export const DrawCircle = .*args: \["i32", "i32", "f32", Color\]/, ); - expect(byName.get("DrawCircle").reason).toBe("mixed-top-level-f32"); }); });