ris-framework 0.1.0-dev.4 → 0.1.0-dev.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +182 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,11 +1,188 @@
|
|
|
1
1
|
# ris-framework
|
|
2
2
|
|
|
3
|
-
TypeScript
|
|
3
|
+
`ris-framework` is a Vite TypeScript application that renders with WebGL2 in the browser. Version 0.1.0.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Open `index.html`. The module script is `src/main.ts`. That file builds the page, constructs a `Framework`, and starts the frame loop. Shared interfaces and value types (`IFramework`, `Color`, `Rect`, `TextureDescriptor`, and the rest) come from the `ris-framework-api` package. This repository is the application that implements them.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Layout
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
| Path | What lives there |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| `index.html` | The page. It has `#app` and `<canvas id="game-canvas" width="800" height="600">`. |
|
|
12
|
+
| `src/main.ts` | Application entry. |
|
|
13
|
+
| `src/core` | `Framework`, `FrameworkConfig`, the window and input managers, content loading, the renderer base, and the sprite batch. |
|
|
14
|
+
| `src/webgl` | The WebGL2 graphics device, renderer, textures, buffers, samplers, and render pipelines. |
|
|
15
|
+
| `src/geometry` | `GeometryBuilder` and mesh geometry data. |
|
|
16
|
+
| `src/content` | Built-in shader modules. GLSL is imported from `shaders/glsl`. |
|
|
17
|
+
| `shaders/glsl` | Vertex and fragment shaders used by those built-in modules. |
|
|
10
18
|
|
|
11
|
-
`
|
|
19
|
+
Runtime dependencies are `ris-framework-api`, `ris-ktx2`, `gl-matrix`, `tsyringe`, and `reflect-metadata`. TypeScript and Vite are dev dependencies.
|
|
20
|
+
|
|
21
|
+
## Prerequisites
|
|
22
|
+
|
|
23
|
+
- Node.js 22. Continuous integration uses Node.js 22. Vite 7 in this repo requires `^20.19.0` or `>=22.12.0`.
|
|
24
|
+
- npm.
|
|
25
|
+
- A browser that can create a WebGL2 context. If `canvas.getContext("webgl2")` fails, startup throws `WebGL not supported.`
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
From the repository root:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
npm ci
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`src/main.ts` imports `reflect-metadata` before it constructs the framework. Keep that import first in the entry file. `Framework` registers itself on a tsyringe child container, and tsyringe expects `reflect-metadata` to be loaded.
|
|
36
|
+
|
|
37
|
+
## Create a framework instance
|
|
38
|
+
|
|
39
|
+
Construct `FrameworkConfig`, then pass it to `Framework`. The constructor is `constructor(options: FrameworkConfig | null = null)`. Omitting the argument, or passing `null`, uses `new FrameworkConfig()`.
|
|
40
|
+
|
|
41
|
+
No config field is required. Each field has a default.
|
|
42
|
+
|
|
43
|
+
| Field | Default | What the code does with it |
|
|
44
|
+
| --- | --- | --- |
|
|
45
|
+
| `canvas` | `null` | The canvas the WebGL2 context is created from. `null` makes `WindowManager` create a canvas and append it to `document.body`. `src/main.ts` sets this to `#game-canvas`. |
|
|
46
|
+
| `backBufferSize` | `vec2.fromValues(800, 600)` | Copied onto an internal `RenderConfiguration`. `WebGlRenderer` leaves that copy unused. The main render target starts at the canvas element's `width` and `height`. The canvas in `index.html` is 800 by 600. |
|
|
47
|
+
| `textureFiltering` | `TextureSamplerFilteringPreset.BILINEAR` | Preset for the graphics device's default texture sampler. `POINT` is nearest filtering with no mipmaps. `BILINEAR` is linear filtering with no mipmaps. `TRILINEAR` is linear filtering with linear mipmaps. `undefined` is treated as `BILINEAR`. |
|
|
48
|
+
| `useKtx2` | `false` | When `true`, the constructor sets `framework.ktx2Factory` to a `Ktx2Factory`. `initialize()` then calls `initializeAsync()` on it and does not wait. When `false`, `ktx2Factory` stays unset. |
|
|
49
|
+
|
|
50
|
+
`textureFiltering` is declared optional on `FrameworkConfig`. The other three fields are always present on a new config object.
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import "reflect-metadata";
|
|
54
|
+
|
|
55
|
+
import { Framework } from "./core/Framework.ts";
|
|
56
|
+
import { FrameworkConfig } from "./core/FrameworkConfig.ts";
|
|
57
|
+
import { TextureSamplerFilteringPreset } from "./core/rendering/enums.ts";
|
|
58
|
+
|
|
59
|
+
const config = new FrameworkConfig();
|
|
60
|
+
config.canvas = document.getElementById("game-canvas") as HTMLCanvasElement;
|
|
61
|
+
config.textureFiltering = TextureSamplerFilteringPreset.BILINEAR;
|
|
62
|
+
|
|
63
|
+
const framework = new Framework(config);
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`framework.renderingBackend` is `RenderingBackend.WEB_GL`. After `initialize()`, `framework.graphicsDevice` is the renderer's WebGL2 device.
|
|
67
|
+
|
|
68
|
+
## Run, build, and preview
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
npm run dev
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`dev` runs Vite. The served page is `index.html`.
|
|
75
|
+
|
|
76
|
+
```sh
|
|
77
|
+
npm run build
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`build` runs `tsc && vite build`. Typecheck failures stop the production bundle. Vite writes the bundle to `dist/`. The Vite config replaces `ris-ktx2`'s Node loader with a browser stub so the client bundle does not import Node built-ins.
|
|
81
|
+
|
|
82
|
+
```sh
|
|
83
|
+
npm run preview
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`preview` runs `vite preview` and serves the `dist/` build.
|
|
87
|
+
|
|
88
|
+
```sh
|
|
89
|
+
npm test
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`test` runs `tsc --noEmit`. It typechecks `src`.
|
|
93
|
+
|
|
94
|
+
## From startup to the first frame
|
|
95
|
+
|
|
96
|
+
`src/main.ts` is the current startup path. It still fills `#app` with the Vite starter page (logos and a counter) and then starts the framework on `#game-canvas`.
|
|
97
|
+
|
|
98
|
+
Register listeners before `initialize()`. Load-content and initialized listeners run only inside that call. Update and render listeners stay on the loop, so a listener added later still runs on a following frame. `initialize()` does this:
|
|
99
|
+
|
|
100
|
+
1. Starts KTX2 initialization when `useKtx2` is set, without awaiting it.
|
|
101
|
+
2. Creates the WebGL2 context, swap chain, and default sampler, blend state, and primitive state.
|
|
102
|
+
3. Initializes the sprite batch (a default orthographic camera and a 1×1 white texture).
|
|
103
|
+
4. Initializes input. Mouse move, mouse buttons, and wheel events on the canvas update `framework.input.getMouseState()`.
|
|
104
|
+
5. Prepares the time manager.
|
|
105
|
+
6. Calls every `addOnLoadContentListener` callback. Async callbacks are not awaited.
|
|
106
|
+
7. Calls every `addOnInitializedListener` callback.
|
|
107
|
+
8. Creates the main render target at the canvas size and a swap-chain pass. The clear color at this moment is `Color.lightPink()`, the renderer default.
|
|
108
|
+
9. Subscribes an update callback and a render callback, then runs both once before returning. Later frames are scheduled with `requestAnimationFrame`.
|
|
109
|
+
|
|
110
|
+
Each update calls `timeManager.frameStart()`, updates input, then calls `addOnUpdateListener` callbacks with that frame's `GameTime`. `src/main.ts` registers no update listener. Each render begins the main pass, calls `addOnRenderListener` callbacks, calls `spriteBatch.frameEnd()`, and presents.
|
|
111
|
+
|
|
112
|
+
Before that call, `src/main.ts` registers the listeners below. The sample loads images, creates a nearest sampler, and draws with the sprite batch:
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
import {
|
|
116
|
+
Color,
|
|
117
|
+
Rect,
|
|
118
|
+
SamplerDescriptor,
|
|
119
|
+
SamplerFilter,
|
|
120
|
+
TextureDescriptor,
|
|
121
|
+
TextureFormat,
|
|
122
|
+
type ISampler,
|
|
123
|
+
type ITexture2D,
|
|
124
|
+
} from "ris-framework-api";
|
|
125
|
+
|
|
126
|
+
const rect = new Rect(200, 200, 200, 200);
|
|
127
|
+
const texRect = new Rect(500, 200, 200, 200);
|
|
128
|
+
const color = Color.blue();
|
|
129
|
+
const white = Color.white();
|
|
130
|
+
let tex: ITexture2D;
|
|
131
|
+
let sampler: ISampler;
|
|
132
|
+
|
|
133
|
+
framework.addOnLoadContentListener(async () => {
|
|
134
|
+
const texDesc = new TextureDescriptor();
|
|
135
|
+
texDesc.generateMipmaps = true;
|
|
136
|
+
texDesc.textureFormat = TextureFormat.RGBA_8_UNORM;
|
|
137
|
+
|
|
138
|
+
tex = await framework.content.loadTexture2DAsync("/assets/test.png", texDesc);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
framework.addOnInitializedListener(() => {
|
|
142
|
+
const desc = new SamplerDescriptor();
|
|
143
|
+
desc.minFilter = SamplerFilter.NEAREST;
|
|
144
|
+
desc.magFilter = SamplerFilter.NEAREST;
|
|
145
|
+
sampler = framework.graphicsDevice.createSampler(desc);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
framework.addOnRenderListener(() => {
|
|
149
|
+
const spriteBatch = framework.spriteBatch;
|
|
150
|
+
spriteBatch.begin(undefined, sampler);
|
|
151
|
+
spriteBatch.drawRect(rect, color);
|
|
152
|
+
if (tex != null) {
|
|
153
|
+
spriteBatch.draw(tex, texRect, white);
|
|
154
|
+
}
|
|
155
|
+
spriteBatch.end();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
framework.initialize();
|
|
159
|
+
framework.renderer.clearColor = Color.lightPink();
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
`loadTexture2DAsync(path, textureDescriptor?, contentConfig?)` is the image path used above. For a path that does not end in `.ktx2`, the descriptor's `textureFormat` is used when it is not `TextureFormat.UNDEFINED`; otherwise the renderer's preferred format is used. `generateMipmaps` is passed through. Width, height, pixel data, usage, label, and block size on that descriptor are not applied to this load. An optional `ContentConfig` only forwards `keepDataCached` to the image loader.
|
|
163
|
+
|
|
164
|
+
The first `begin` / `drawRect` / `end` runs inside `initialize()`, before the texture promise resolves, so the blue rectangle is the first draw. Later frames draw the texture once `tex` is set. `spriteBatch.begin` with no matrix keeps the default orthographic camera. The sampler argument overrides the device's default sampler for that batch. `drawRect` draws the 1×1 white texture tinted by `color`.
|
|
165
|
+
|
|
166
|
+
`src/main.ts` also loads `/assets/cat.jpg` the same way and draws it at `new Rect(200, 500, 200, 200)`. Those two files are absent from this repository. Put them under `public/assets/` before those loads can succeed. The blue rectangle still draws when the image loads fail.
|
|
167
|
+
|
|
168
|
+
Setting `framework.renderer.clearColor` after `initialize()` updates later frames. The first frame has already cleared with the default light pink.
|
|
169
|
+
|
|
170
|
+
## What is still unfinished
|
|
171
|
+
|
|
172
|
+
These throw, or they accept a call and do not do the work the name suggests:
|
|
173
|
+
|
|
174
|
+
- `WindowManager.addOnResizeListener`, `removeOnResizeListener`, `initializeForWebGPU`, `initializeForWebGl`, and `dispose` throw `Method not implemented.` `title` and `windowBounds` are stored and never applied. There is no WebGPU backend.
|
|
175
|
+
- `Framework.dispose()` is empty. It does not release the window, device, or GPU resources.
|
|
176
|
+
- `renderer.beginComputePass()` and `renderer.endComputePass()` throw `Method not implemented.` `ARenderer.limits` throws the same error.
|
|
177
|
+
- `graphicsDevice.setupDebugCallback()`, `frameEnd()`, `pushDebugGroup()`, and `popDebugGroup()` throw `Method not implemented.`
|
|
178
|
+
- `ITexture2D.createView()` throws `Method not implemented.`
|
|
179
|
+
- `content.loadShaderModule` only resolves the built-in ids `sprite`, `unlit_material`, `inspect_texture_mips`, and `main_render_target_flip_y`. Any other id throws `Not implemented`.
|
|
180
|
+
- `SpriteBatch.draw` and `drawRect` accept rotation arguments and do not use them. `layerDepth` is written as the sprite's z position.
|
|
181
|
+
- Keyboard and gamepad queries return empty state. `getKeyboardState()` is an empty `KeyboardState`. `getGamePadState()` is a disconnected `GamePadState`. `thumbstickDeadZone` is stored and not applied.
|
|
182
|
+
- A render pass that enables stencil without depth throws `Not implemented`.
|
|
183
|
+
- Paths ending in `.ktx2` go through `content.loadKtx2Async`, which uses its own `Ktx2Factory` and does not call `initializeAsync`. `loadAsync` needs the KTX module that `useKtx2` starts, and `initialize()` does not wait for that call. The `.ktx2` branch of `loadTexture2DAsync` also drops `ContentConfig`. The current entry file does not load a `.ktx2` file.
|
|
184
|
+
- The geometry and unlit-pipeline steps at the bottom of `src/main.ts` are comments. `geometryBuilder.quadGeometry()` can build quad data; the entry file does not upload it or draw it.
|
|
185
|
+
|
|
186
|
+
## Version
|
|
187
|
+
|
|
188
|
+
`package.json` `version` stays a release version with no prerelease suffix. It is `0.1.0`. Branch and publish rules are in [CONTRIBUTING.md](CONTRIBUTING.md).
|