ris-framework 0.2.0-dev.12 → 0.2.0-dev.13
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/CHANGELOG.md +1 -2
- package/README.md +96 -159
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -19,7 +19,6 @@ Compared with `main` (0.1.0).
|
|
|
19
19
|
|
|
20
20
|
### Added
|
|
21
21
|
|
|
22
|
-
- WebGPU backend (preview): set `renderingBackend` and call `framework.initializeAsync()`. Only unlit materials are supported so far; WebGL stays the default and is used as a fallback.
|
|
23
22
|
- `Scene` with `add`/`remove` for meshes, lights and the camera, plus `SceneMesh` for owning a mesh and its materials.
|
|
24
23
|
- Lights: `AmbientLight`, `DirectionalLight` and `PointLight`.
|
|
25
24
|
- `ForwardBlinnPhongMaterial`, `SpriteMaterial`, and the `ICameraMaterial`/`ILightsMaterial` interfaces.
|
|
@@ -27,7 +26,7 @@ Compared with `main` (0.1.0).
|
|
|
27
26
|
- OBJ/MTL loading through `framework.objLoader`, `ObjParser`, and `content.loadMeshAsync`, `loadUnlitMeshAsync` and `loadBlinnPhongMeshAsync`.
|
|
28
27
|
- `geometryBuilder.create(descriptor)` and the exported `BaseGeometry`.
|
|
29
28
|
- sRGB compressed texture formats (BC1, BC3, BC7, ETC2), with KTX2 loading choosing sRGB automatically.
|
|
30
|
-
- Examples: `unlit_mesh`, `
|
|
29
|
+
- Examples: `unlit_mesh`, `blinn_phong_mesh` and `sprite_batch`.
|
|
31
30
|
|
|
32
31
|
### Changed
|
|
33
32
|
|
package/README.md
CHANGED
|
@@ -1,212 +1,149 @@
|
|
|
1
1
|
# ris-framework
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
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) ship from this package. They used to live in `ris-framework-api`. This repository implements them.
|
|
6
|
-
|
|
7
|
-
## Layout
|
|
8
|
-
|
|
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/index.ts` | Package entry. Exports `Framework`, `FrameworkConfiguration`, `TextureSamplerFilteringPreset`, and everything under `src/api`. |
|
|
14
|
-
| `src/Framework.ts` | `Framework`, which wires the renderer, factories, managers, and the frame loop together. |
|
|
15
|
-
| `src/api` | Interfaces and value types formerly published as `ris-framework-api` (input, textures, cameras, `TextureUtilities`, and the rest). It also holds `FrameworkConfiguration` and the backend-independent implementations: `WindowManager`, `InputManager`, `ContentManager`, `SpriteBatch`, `GeometryBuilder`, and `BaseGeometry`. `src/api/content/ShaderModuleContent.ts` maps the built-in shader module ids to GLSL from `shaders/glsl`. |
|
|
16
|
-
| `src/core` | Renderer and graphics-device base classes (`ARenderer`, `AGraphicsDevice`), the texture base class, blend and sampler descriptors, `VertexBufferLayout`, and `TextureSamplerFilteringPreset`. |
|
|
17
|
-
| `src/webgl` | The WebGL2 graphics device, renderer, textures, buffers, samplers, and render pipelines. |
|
|
18
|
-
| `shaders/glsl` | Vertex and fragment shaders used by the built-in shader modules. |
|
|
19
|
-
| `tests` | Vitest tests for the types under `src/api`. |
|
|
20
|
-
|
|
21
|
-
Runtime dependencies are `ris-ktx2` and `gl-matrix`. The KTX2 types (`IKtx2Texture`, `VkFormat`, `KtxTranscodeFormat`, and the rest) are imported from `ris-ktx2`. The library build includes the browser portion of `ris-ktx2`, with its Node loader replaced by a stub, so the published entry does not import Node built-ins. TypeScript, Vite, and Vitest are dev dependencies. Notices for the bundled KTX loader are in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md). The package license is Apache License 2.0. Copyright 2026 Luka Erkapic. See [LICENSE](LICENSE) and [NOTICE](NOTICE).
|
|
22
|
-
|
|
23
|
-
## Prerequisites
|
|
24
|
-
|
|
25
|
-
- Node.js 22. Continuous integration uses Node.js 22. Vite 7 in this repo requires `^20.19.0` or `>=22.12.0`.
|
|
26
|
-
- npm.
|
|
27
|
-
- A browser that can create a WebGL2 context. If `canvas.getContext("webgl2")` fails, startup throws `WebGL not supported.`
|
|
28
|
-
|
|
29
|
-
## Install
|
|
30
|
-
|
|
31
|
-
Install the package in an application:
|
|
3
|
+
A browser WebGL2 framework for TypeScript, published as an ES module (version 0.2.0). Runtime dependencies are `gl-matrix` and `ris-ktx2`.
|
|
32
4
|
|
|
33
5
|
```sh
|
|
34
6
|
npm install ris-framework
|
|
35
7
|
```
|
|
36
8
|
|
|
37
|
-
|
|
9
|
+
Everything public is exported from the package entry: `Framework`, `TextureSamplerFilteringPreset`, and the API types (scenes, meshes, materials, lights, cameras, textures, loaders, input).
|
|
38
10
|
|
|
39
|
-
|
|
40
|
-
import { Framework, FrameworkConfiguration, TextureSamplerFilteringPreset } from "ris-framework";
|
|
41
|
-
```
|
|
11
|
+
## Create and start a framework
|
|
42
12
|
|
|
43
|
-
|
|
13
|
+
Pass an `IFrameworkConfiguration` object. Every field is optional.
|
|
44
14
|
|
|
45
15
|
```ts
|
|
46
|
-
import { Color,
|
|
47
|
-
```
|
|
16
|
+
import { Color, Framework, type IFrameworkConfiguration, TextureSamplerFilteringPreset } from "ris-framework";
|
|
48
17
|
|
|
49
|
-
|
|
18
|
+
const config: IFrameworkConfiguration = {
|
|
19
|
+
canvas: document.querySelector<HTMLCanvasElement>("#game-canvas")!,
|
|
20
|
+
textureFiltering: TextureSamplerFilteringPreset.BILINEAR,
|
|
21
|
+
useKtx2: true,
|
|
22
|
+
};
|
|
50
23
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
```sh
|
|
54
|
-
npm ci
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
`Framework` creates its services itself and passes dependencies through their constructors; no dependency-injection container or `reflect-metadata` import is needed.
|
|
58
|
-
|
|
59
|
-
## Create a framework instance
|
|
60
|
-
|
|
61
|
-
Construct `FrameworkConfiguration`, then pass it to `Framework`. The constructor is `constructor(options?: IFrameworkConfiguration)`. Omitting the argument uses `new FrameworkConfiguration()`. The constructor writes the resolved `backBufferSize` back onto the object you pass.
|
|
24
|
+
const framework = new Framework(config);
|
|
62
25
|
|
|
63
|
-
|
|
26
|
+
framework.addOnLoadContentListener(async () => { /* load content */ });
|
|
27
|
+
framework.addOnUpdateListener(time => { /* update, time.deltaTimeSec */ });
|
|
28
|
+
framework.addOnRenderListener(() => { /* draw */ });
|
|
64
29
|
|
|
65
|
-
|
|
30
|
+
framework.initialize();
|
|
31
|
+
framework.renderer.clearColor = Color.cornerFlowerBlue();
|
|
32
|
+
```
|
|
66
33
|
|
|
67
|
-
| Field | Default |
|
|
34
|
+
| Field | Default | Meaning |
|
|
68
35
|
| --- | --- | --- |
|
|
69
|
-
| `canvas` |
|
|
70
|
-
| `backBufferSize` | `
|
|
71
|
-
| `textureFiltering` | `
|
|
72
|
-
| `
|
|
73
|
-
| `
|
|
74
|
-
| `
|
|
75
|
-
|
|
76
|
-
Every field is optional on `IFrameworkConfiguration`. A new `FrameworkConfiguration` sets `useKtx2`, `textureFiltering`, `alpha`, and `powerPreference`. `canvas` and `backBufferSize` start unset.
|
|
36
|
+
| `canvas` | created and appended | The canvas to render into. |
|
|
37
|
+
| `backBufferSize` | `800 × 600` | Back buffer size. |
|
|
38
|
+
| `textureFiltering` | `BILINEAR` | Default sampler preset. |
|
|
39
|
+
| `textureColorSpace` | `SRGB` | Default texture colour space. |
|
|
40
|
+
| `useKtx2` | `false` | Creates `framework.ktx2Factory`. |
|
|
41
|
+
| `alpha` | `false` | Whether the back buffer has an alpha channel. |
|
|
42
|
+
| `powerPreference` | `DEFAULT` | GPU preference on multi-GPU systems. |
|
|
77
43
|
|
|
78
|
-
|
|
79
|
-
import { Framework, FrameworkConfiguration, TextureSamplerFilteringPreset } from "ris-framework";
|
|
80
|
-
|
|
81
|
-
const config = new FrameworkConfiguration();
|
|
82
|
-
config.canvas = document.getElementById("game-canvas") as HTMLCanvasElement;
|
|
83
|
-
config.textureFiltering = TextureSamplerFilteringPreset.BILINEAR;
|
|
44
|
+
Register listeners before `initialize()`. Load-content and initialized listeners run inside that call.
|
|
84
45
|
|
|
85
|
-
|
|
86
|
-
```
|
|
46
|
+
## Scenes, cameras and lights
|
|
87
47
|
|
|
88
|
-
|
|
48
|
+
```ts
|
|
49
|
+
import { Color, DirectionalLight, PointLight, Scene } from "ris-framework";
|
|
89
50
|
|
|
90
|
-
|
|
51
|
+
const scene = new Scene(framework);
|
|
91
52
|
|
|
53
|
+
// Passing the scene makes this the scene's camera.
|
|
54
|
+
const camera = framework.cameraFactory.createPerspectiveCamera({
|
|
55
|
+
aspectRatio: canvas.width / canvas.height,
|
|
56
|
+
eye: [0, 0, 3],
|
|
57
|
+
}, scene);
|
|
92
58
|
|
|
93
|
-
|
|
59
|
+
scene.activeAmbientLight.color = new Color(0.4, 0.4, 0.4);
|
|
60
|
+
// Lights take an optional scene; passing it adds them.
|
|
61
|
+
new DirectionalLight({ color: new Color(1, 1, 1), intensity: 0.8, direction: [-0.4, -0.6, 1] }, scene);
|
|
62
|
+
new PointLight({ color: new Color(1, 0.5, 0.2), intensity: 1.2, linear: 0.35, quadratic: 0.44 }, scene);
|
|
94
63
|
|
|
95
|
-
|
|
64
|
+
framework.addOnUpdateListener(time => {
|
|
65
|
+
camera.update(time);
|
|
66
|
+
scene.update(time);
|
|
67
|
+
});
|
|
68
|
+
framework.addOnRenderListener(() => scene.render());
|
|
69
|
+
```
|
|
96
70
|
|
|
97
|
-
|
|
71
|
+
`scene.add(item)` and `scene.remove(item)` accept a renderable mesh, a `SceneMesh`, a light or a camera. Blinn-Phong materials support up to 3 directional and 5 point lights.
|
|
98
72
|
|
|
99
|
-
|
|
100
|
-
2. Creates the WebGL2 context, swap chain, and default sampler, blend state, and primitive state.
|
|
101
|
-
3. Initializes the sprite batch (a default orthographic camera and a 1×1 white texture).
|
|
102
|
-
4. Initializes input. Mouse move, mouse buttons, and wheel events on the canvas update `framework.input.getMouseState()`. Touch start, move, end, and cancel events on the canvas update `framework.input.getTouchCollection()`, using the same viewport `clientX` / `clientY` coordinates as the mouse. A cancel is `TouchLocationState.CANCELLED`, which is not a finger lift. The canvas `touch-action` is set to `none`, and touch start and move call `preventDefault`, so the browser does not scroll or pinch-zoom that canvas.
|
|
103
|
-
5. Prepares the time manager.
|
|
104
|
-
6. Calls every `addOnLoadContentListener` callback. Async callbacks are not awaited.
|
|
105
|
-
7. Calls every `addOnInitializedListener` callback.
|
|
106
|
-
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.
|
|
107
|
-
9. Subscribes an update callback and a render callback, then runs both once before returning. Later frames are scheduled with `requestAnimationFrame`.
|
|
73
|
+
## Meshes and materials
|
|
108
74
|
|
|
109
|
-
|
|
75
|
+
- `IMesh` / `Mesh` hold geometry and the vertex and index buffers.
|
|
76
|
+
- `IRenderableMesh` / `RenderableMesh` add a material, an index range and sub meshes (`mesh.addSubMesh(material, start, count)` or `meshFactory.createWithSubMeshes`). The root mesh owns the transform and one model buffer, which all its sub meshes share.
|
|
77
|
+
- Transforms: `mesh.position`, `mesh.rotation` (Euler radians, applied Y, then X, then Z) and `mesh.scale`. `mesh.modelMatrix` is recomputed only when one of them changes.
|
|
78
|
+
- `SceneMesh`: `scene.add(mesh)` wraps a renderable mesh in a `SceneMesh`, which owns its materials. `sceneMesh.dispose()` and `scene.dispose()` dispose the mesh and each material once.
|
|
79
|
+
- Materials come from `framework.materialFactory`. They include unlit, `ForwardBlinnPhongMaterial` and `SpriteMaterial`.
|
|
110
80
|
|
|
111
|
-
|
|
81
|
+
## Loading OBJ/MTL
|
|
112
82
|
|
|
113
83
|
```ts
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
SamplerFilter,
|
|
119
|
-
TextureDescriptor,
|
|
120
|
-
TextureFormat,
|
|
121
|
-
type ISampler,
|
|
122
|
-
type ITexture2D,
|
|
123
|
-
} from "ris-framework";
|
|
124
|
-
|
|
125
|
-
const rect = new Rect(200, 200, 200, 200);
|
|
126
|
-
const texRect = new Rect(500, 200, 200, 200);
|
|
127
|
-
const color = Color.blue();
|
|
128
|
-
const white = Color.white();
|
|
129
|
-
let tex: ITexture2D;
|
|
130
|
-
let sampler: ISampler;
|
|
131
|
-
|
|
132
|
-
framework.addOnLoadContentListener(async () => {
|
|
133
|
-
const texDesc = new TextureDescriptor();
|
|
134
|
-
texDesc.generateMipmaps = true;
|
|
135
|
-
texDesc.textureFormat = TextureFormat.RGBA_8_UNORM;
|
|
136
|
-
|
|
137
|
-
tex = await framework.content.loadTexture2DAsync("/assets/test.png", texDesc);
|
|
138
|
-
});
|
|
84
|
+
// Unlit or Blinn-Phong materials (one per usemtl), coloured from the .mtl.
|
|
85
|
+
const cube = await framework.content.loadBlinnPhongMeshAsync("/assets/cube.obj", camera);
|
|
86
|
+
const flat = await framework.content.loadUnlitMeshAsync("/assets/cube.obj", camera);
|
|
87
|
+
scene.add(cube);
|
|
139
88
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
sampler = framework.graphicsDevice.createSampler(desc);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
framework.addOnRenderListener(() => {
|
|
148
|
-
const spriteBatch = framework.spriteBatch;
|
|
149
|
-
spriteBatch.begin(undefined, sampler);
|
|
150
|
-
spriteBatch.drawRect(rect, color);
|
|
151
|
-
if (tex != null) {
|
|
152
|
-
spriteBatch.draw(tex, texRect, white);
|
|
153
|
-
}
|
|
154
|
-
spriteBatch.end();
|
|
89
|
+
// Lower level: pass a Blinn-Phong descriptor or a material factory per sub mesh.
|
|
90
|
+
const mesh = await framework.objLoader.loadAsync("/assets/cube.obj", {
|
|
91
|
+
projectionViewBuffer: camera.projectionViewBuffer,
|
|
92
|
+
cameraPositionBuffer: camera.positionBuffer,
|
|
155
93
|
});
|
|
156
94
|
|
|
157
|
-
framework.
|
|
158
|
-
framework.
|
|
95
|
+
// Geometry only, with no materials (GeometryFormat is exported from "ris-framework").
|
|
96
|
+
const geometry = await framework.content.loadMeshAsync("/assets/cube.obj", GeometryFormat.POS3_COLOR4_TEXTURECOORDS2);
|
|
159
97
|
```
|
|
160
98
|
|
|
161
|
-
`
|
|
99
|
+
`ObjLoader.parseAsync(path)` and `ObjParser` return plain data without creating GPU resources.
|
|
162
100
|
|
|
163
|
-
|
|
101
|
+
## Textures and KTX2
|
|
164
102
|
|
|
165
|
-
|
|
103
|
+
```ts
|
|
104
|
+
const png = await framework.content.loadTexture2DAsync("/assets/test.png");
|
|
105
|
+
const ktx = await framework.content.loadTexture2DAsync("/assets/kodim23.ktx2");
|
|
106
|
+
```
|
|
166
107
|
|
|
167
|
-
|
|
108
|
+
Paths ending in `.ktx2` are loaded through `ris-ktx2`.
|
|
168
109
|
|
|
169
|
-
|
|
110
|
+
- **Basis Universal files** are transcoded to the best format the GPU supports, in this order: BC7, ASTC 4×4, BC3, ETC2, PVRTC, and RGBA8 as a last resort.
|
|
111
|
+
- **Other KTX2 files** are uploaded in their stored format.
|
|
112
|
+
- **sRGB:** sRGB files load as sRGB formats (`BC7_RGBA_SRGB`, `BC3_RGBA_SRGB`, `BC1_RGB_SRGB`, `ETC2_RGBA8_SRGB`, `ETC2_RGB8_SRGB`, `ASTC_*_RGBA_SRGB`, `RGBA_8_UNORM_SRGB`), so the GPU decodes them to linear.
|
|
113
|
+
- **sRGB fallback:** if `WEBGL_compressed_texture_s3tc_srgb` is missing, sRGB BC1 and BC3 textures fall back to linear with a warning.
|
|
170
114
|
|
|
171
|
-
`
|
|
115
|
+
`framework.graphicsDevice.features` reports compression support: `supportsTextureCompressionBC` (BPTC/BC7), `supportsTextureCompressionS3TC`, `supportsTextureCompressionS3TCSrgb`, `supportsTextureCompressionETC2`, `supportsTextureCompressionASTC` and `supportsTextureCompressionPVRTC`.
|
|
172
116
|
|
|
173
|
-
|
|
117
|
+
## Sprites
|
|
174
118
|
|
|
175
119
|
```ts
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
scene.add(mesh);
|
|
120
|
+
framework.addOnRenderListener(() => {
|
|
121
|
+
framework.spriteBatch.begin();
|
|
122
|
+
framework.spriteBatch.draw(texture, new Rect(200, 200, 200, 200), Color.white());
|
|
123
|
+
framework.spriteBatch.drawRect(new Rect(500, 200, 100, 100), Color.blue());
|
|
124
|
+
framework.spriteBatch.end();
|
|
125
|
+
});
|
|
183
126
|
```
|
|
184
127
|
|
|
185
|
-
## Examples
|
|
128
|
+
## Examples and scripts
|
|
186
129
|
|
|
187
|
-
|
|
130
|
+
Examples live in [`examples/`](examples/): `unlit_mesh`, `blinn_phong_mesh` and `sprite_batch` (see [`examples/README.md`](examples/README.md)).
|
|
188
131
|
|
|
189
132
|
```sh
|
|
190
|
-
npm
|
|
191
|
-
npm run
|
|
133
|
+
npm ci
|
|
134
|
+
npm run dev:examples # serve the examples
|
|
135
|
+
npm run build:examples # build them into examples/dist/
|
|
136
|
+
npm run dev # src/main.ts playground
|
|
137
|
+
npm test # type-check and Vitest
|
|
138
|
+
npm run build # library (dist/) and app build
|
|
192
139
|
```
|
|
193
140
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
These throw, or they accept a call and do not do the work the name suggests:
|
|
141
|
+
Requires Node.js 22 (or `^20.19.0`) and a WebGL2-capable browser.
|
|
197
142
|
|
|
198
|
-
|
|
199
|
-
- `Framework.dispose()` is empty. It does not release the window, device, or GPU resources.
|
|
200
|
-
- `renderer.beginComputePass()` and `renderer.endComputePass()` throw `Method not implemented.` `ARenderer.limits` throws the same error.
|
|
201
|
-
- `graphicsDevice.setupDebugCallback()`, `frameEnd()`, `pushDebugGroup()`, and `popDebugGroup()` throw `Method not implemented.`
|
|
202
|
-
- `ITexture2D.createView()` throws `Method not implemented.`
|
|
203
|
-
- `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`.
|
|
204
|
-
- `SpriteBatch.draw` and `drawRect` accept rotation arguments and do not use them. `layerDepth` is written as the sprite's z position.
|
|
205
|
-
- Keyboard and gamepad queries return empty state. `getKeyboardState()` is an empty `KeyboardState`. `getGamePadState()` is a disconnected `GamePadState`. `thumbstickDeadZone` is stored and not applied.
|
|
206
|
-
- A render pass that enables stencil without depth throws `Not implemented`.
|
|
207
|
-
- Paths ending in `.ktx2` go through `content.loadKtx2Async`, which uses its own `Ktx2Factory` and does not call `initializeAsync`. The libktx module is shared by every `Ktx2Factory`, so `loadAsync` works only after some factory has finished `initializeAsync()`. `useKtx2` starts that call in `initialize()` but does not wait for it. To load a `.ktx2` file, set `useKtx2`, await `framework.ktx2Factory.initializeAsync()` before `initialize()`, and then call `loadTexture2DAsync` from a load-content listener. `src/main.ts` does not load a `.ktx2` file.
|
|
208
|
-
- 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.
|
|
143
|
+
## Known limitations
|
|
209
144
|
|
|
210
|
-
|
|
145
|
+
- Window resize listeners and compute passes throw `Method not implemented.`
|
|
146
|
+
- Keyboard and gamepad state are stubs.
|
|
147
|
+
- `SpriteBatch` ignores rotation.
|
|
211
148
|
|
|
212
|
-
|
|
149
|
+
Branch and publish rules are in [CONTRIBUTING.md](CONTRIBUTING.md). Changes are listed in [CHANGELOG.md](CHANGELOG.md).
|