create-gamenative-app 0.1.9 → 0.1.10
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 +68 -8
- package/bin/create.js +144 -12
- package/dist/scripts/Texture.d.ts +12 -0
- package/dist/scripts/Texture.d.ts.map +1 -0
- package/dist/scripts/Texture.js +32 -0
- package/dist/scripts/Texture.js.map +1 -0
- package/dist/scripts/index.d.ts +2 -0
- package/dist/scripts/index.d.ts.map +1 -1
- package/dist/scripts/index.js +1 -0
- package/dist/scripts/index.js.map +1 -1
- package/package.json +1 -1
- package/scripts/Texture.ts +39 -0
- package/scripts/index.ts +2 -0
package/README.md
CHANGED
|
@@ -104,13 +104,73 @@ Requires [pkg](https://github.com/vercel/pkg); target is `node18-win-x64`. For o
|
|
|
104
104
|
| `npm start` | Run `dist/main.js` (needs `game.config.json`) |
|
|
105
105
|
| `npm run build:exe` | Build then package to `release/GameNative.exe` |
|
|
106
106
|
|
|
107
|
-
## API
|
|
107
|
+
## API reference (everything you need to make a game)
|
|
108
108
|
|
|
109
|
-
|
|
110
|
-
- **`IGame`**: optional `init(ctx, config)`, required `update(dt)`, `draw()`, optional `dispose()`.
|
|
111
|
-
- **`GameContext`**: `gl`, `input`, `width`, `height`.
|
|
112
|
-
- **Input**: `ctx.input.mouseX`, `mouseY`, `mouseLeft`, `mouseRight`, `mouseMiddle`, `isKeyDown(scancode)`.
|
|
113
|
-
- **`SCANCODE`**: e.g. `SCANCODE.W`, `SCANCODE.Escape`.
|
|
114
|
-
- **`createDraw2D(gl)`**: `clear(r,g,b,a?)`, `fillRect(x,y,w,h,r,g,b,a?)`, `dispose()`.
|
|
109
|
+
Import from `../../scripts/index.js` (or your framework path). All APIs are available in your game file.
|
|
115
110
|
|
|
116
|
-
|
|
111
|
+
### Game lifecycle
|
|
112
|
+
|
|
113
|
+
- **`run(game, config?)`** — Starts the framework. Call once from your entry point. `config`: `title`, `width`, `height`, `vsync`, `resizable`, `icon`.
|
|
114
|
+
- **`IGame`** — Your game implements: optional **`init(ctx, config)`**, required **`update(dt)`**, required **`draw()`**, optional **`dispose()`**.
|
|
115
|
+
- **`GameContext`** — Passed to `init` and available each frame: **`gl`** (WebGL context), **`input`**, **`width`**, **`height`** (updates on resize).
|
|
116
|
+
|
|
117
|
+
### Input
|
|
118
|
+
|
|
119
|
+
- **`ctx.input.mouseX`**, **`ctx.input.mouseY`** — Cursor position in pixels.
|
|
120
|
+
- **`ctx.input.mouseLeft`**, **`mouseRight`**, **`mouseMiddle`** — Boolean button state.
|
|
121
|
+
- **`ctx.input.isKeyDown(scancode)`** — Returns true if key is held. Use **`SCANCODE`** for key codes: `SCANCODE.W`, `SCANCODE.A`, `SCANCODE.S`, `SCANCODE.D`, `SCANCODE.Space`, `SCANCODE.Escape`, `SCANCODE.Up`, `SCANCODE.Down`, `SCANCODE.Left`, `SCANCODE.Right`, `SCANCODE.Enter`, `SCANCODE.Shift`, `SCANCODE.Ctrl`, `SCANCODE.Tab`, and `SCANCODE.A`–`SCANCODE.Z`.
|
|
122
|
+
|
|
123
|
+
### 2D drawing (`createDraw2D`)
|
|
124
|
+
|
|
125
|
+
- **`createDraw2D(ctx.gl)`** — Returns a Draw2D helper. Create once in `init`, reuse in `draw`.
|
|
126
|
+
- **`draw2d.clear(r, g, b, a?)`** — Clear the screen (default a = 1).
|
|
127
|
+
- **`draw2d.fillRect(x, y, w, h, r, g, b, a?)`** — Filled rectangle in pixels. Origin top-left, y down.
|
|
128
|
+
- **`draw2d.fillTriangles(verts, r, g, b, a?)`** — Draw triangles. `verts` is a `Float32Array` of pixel coords: `[x,y, x,y, x,y, ...]` (3 vertices per triangle).
|
|
129
|
+
- **`draw2d.strokeRect(x, y, w, h, r, g, b, a?)`** — 1px outline rectangle (panels, buttons).
|
|
130
|
+
- **`draw2d.dispose()`** — Call in `dispose()` to free resources.
|
|
131
|
+
|
|
132
|
+
### Text (fonts, sizes, colors)
|
|
133
|
+
|
|
134
|
+
- **`loadFont(path)`** — Async. Load a TTF/OTF from path (relative to cwd or absolute). Returns a **`Font`**.
|
|
135
|
+
- **`drawText(draw2d, font, text, x, y, size, r, g, b, a?)`** — Draw a string. (x, y) = top-left; y-axis down; `size` = font size in pixels.
|
|
136
|
+
- **`getTextTriangles(font, text, x, y, size)`** — Returns `Float32Array` of triangle vertices. Use with `draw2d.fillTriangles(triangles, r, g, b, a)` for custom color or caching.
|
|
137
|
+
- **`measureText(font, text, size)`** — Returns `{ width, height }` in pixels (for layout).
|
|
138
|
+
|
|
139
|
+
### UI / hit-testing
|
|
140
|
+
|
|
141
|
+
- **`isPointInRect(px, py, x, y, w, h)`** — Returns true if point (px, py) is inside the rectangle. Use with `ctx.input.mouseX`, `mouseY`, `mouseLeft` in `update()` to implement buttons.
|
|
142
|
+
|
|
143
|
+
### Textures (images / sprites)
|
|
144
|
+
|
|
145
|
+
- **`loadTexture(gl, path)`** — Async. Load a PNG from path, upload to a WebGL texture, return **`{ texture, width, height }`**. You bind and draw with your own shader; the helper only does load + upload.
|
|
146
|
+
|
|
147
|
+
### Raw WebGL — do anything
|
|
148
|
+
|
|
149
|
+
- **`ctx.gl`** — Full WebGL 1 context. No wrapper. Use it for custom shaders, 3D, framebuffers, MSAA, texture arrays, etc. You have full control.
|
|
150
|
+
|
|
151
|
+
### Full SDL — do anything
|
|
152
|
+
|
|
153
|
+
- **`sdl`** — The full [@kmamal/sdl](https://github.com/kmamal/node-sdl) module is exported. Use it directly for everything SDL exposes:
|
|
154
|
+
- **`sdl.video`** — displays, windows (we create one; you can query others).
|
|
155
|
+
- **`sdl.keyboard`**, **`sdl.mouse`**, **`sdl.touch`** — input (we use keyboard + mouse for `ctx.input`; you can use more).
|
|
156
|
+
- **`sdl.audio`** — **`sdl.audio.getDevices()`**, **`sdl.audio.openDevice(device, options)`** for playback/recording. Open a device, enqueue buffers (WAV/samples), no high-level helper.
|
|
157
|
+
- **`sdl.joystick`**, **`sdl.controller`** — gamepads; **`sdl.sensor`** — accelerometer/gyro.
|
|
158
|
+
- **`sdl.clipboard`**, **`sdl.power`** — clipboard, battery.
|
|
159
|
+
|
|
160
|
+
### Node / OS — do anything
|
|
161
|
+
|
|
162
|
+
- Your game runs in **Node.js**. You can **`import 'fs'`**, **`import 'path'`**, use **`child_process`**, native addons, or any npm package. File I/O, save data, scripting, or calling out to other tools — nothing is locked down.
|
|
163
|
+
|
|
164
|
+
### Exports summary
|
|
165
|
+
|
|
166
|
+
| Export | Purpose |
|
|
167
|
+
|--------|--------|
|
|
168
|
+
| `run`, `sdl`, `createInput`, `SCANCODE` | Entry, full SDL ref, input factory, key codes |
|
|
169
|
+
| `IGame`, `GameConfig`, `GameContext` | Types for your game and config |
|
|
170
|
+
| `InputState` | Type for `ctx.input` |
|
|
171
|
+
| `createDraw2D`, `Draw2D` | 2D clear/rect/triangles/stroke |
|
|
172
|
+
| `loadFont`, `drawText`, `getTextTriangles`, `measureText`, `Font` | Text rendering |
|
|
173
|
+
| `loadTexture`, `LoadedTexture` | Load PNG → WebGL texture |
|
|
174
|
+
| `isPointInRect` | Button/UI hit-test |
|
|
175
|
+
|
|
176
|
+
**Summary:** You get raw **`ctx.gl`** (WebGL), full **`sdl`** (SDL2), and **Node** (fs, path, etc.). Helpers (draw2D, text, texture load, isPointInRect) are thin conveniences; they don’t hide the native layers. See **FRAMEWORK.md** in a created project for more detail and a minimal SDL audio example.
|
package/bin/create.js
CHANGED
|
@@ -47,6 +47,7 @@ async function scaffold(projectName, parentDir) {
|
|
|
47
47
|
earcutDts,
|
|
48
48
|
textTs,
|
|
49
49
|
uiTs,
|
|
50
|
+
textureTs,
|
|
50
51
|
copyConfigJs,
|
|
51
52
|
setExeIconJs,
|
|
52
53
|
] = await Promise.all([
|
|
@@ -63,6 +64,7 @@ async function scaffold(projectName, parentDir) {
|
|
|
63
64
|
readFile(join(FRAMEWORK_ROOT, 'scripts/earcut.d.ts'), 'utf-8'),
|
|
64
65
|
copyFrameworkFile('scripts/Text.ts'),
|
|
65
66
|
copyFrameworkFile('scripts/UI.ts'),
|
|
67
|
+
copyFrameworkFile('scripts/Texture.ts'),
|
|
66
68
|
readFile(join(FRAMEWORK_ROOT, 'scripts/copy-config.js'), 'utf-8'),
|
|
67
69
|
readFile(join(FRAMEWORK_ROOT, 'scripts/set-exe-icon.js'), 'utf-8'),
|
|
68
70
|
])
|
|
@@ -296,6 +298,7 @@ export default game
|
|
|
296
298
|
writeFile(join(scripts, 'earcut.d.ts'), earcutDts),
|
|
297
299
|
writeFile(join(scripts, 'Text.ts'), textTs),
|
|
298
300
|
writeFile(join(scripts, 'UI.ts'), uiTs),
|
|
301
|
+
writeFile(join(scripts, 'Texture.ts'), textureTs),
|
|
299
302
|
writeFile(join(games, 'Game.ts'), gameTemplate),
|
|
300
303
|
writeFile(join(scripts, 'copy-config.js'), copyConfigJs),
|
|
301
304
|
writeFile(join(scripts, 'set-exe-icon.js'), setExeIconJs),
|
|
@@ -320,21 +323,150 @@ Game built with [GameNative](https://github.com/your-org/GameNative).
|
|
|
320
323
|
|
|
321
324
|
See FRAMEWORK.md for 2D, 3D, lighting, sound, UI, and camera.
|
|
322
325
|
`
|
|
323
|
-
const frameworkMd = `# GameNative
|
|
326
|
+
const frameworkMd = `# GameNative API — everything you need to make a game
|
|
324
327
|
|
|
325
|
-
|
|
328
|
+
Import from \`../../scripts/index.js\` in your \`src/games/*.ts\` files.
|
|
326
329
|
|
|
327
|
-
|
|
328
|
-
- **Text**: \`loadFont(\'path/to/font.ttf\')\` (TTF/OTF), \`getTextTriangles(font, text, x, y, size)\` → triangles, \`drawText(draw2d, font, text, x, y, size, r, g, b, a)\`, \`measureText(font, text, size)\`. Any font, any size; draw with \`fillTriangles\` for full control.
|
|
329
|
-
- **3D**: Use \`ctx.gl\` directly: buffers, shaders, matrices. Implement camera (view/projection), meshes, and lighting in shaders.
|
|
330
|
-
- **Lighting**: In 3D shaders use uniforms for light position/color; in 2D use tint or custom fragment shaders.
|
|
331
|
-
- **Assets**: Load images (decode PNG with pngjs or similar), upload to \`ctx.gl\` textures; load audio (see sound).
|
|
332
|
-
- **Sound**: \`ctx.sdl.audio\` (SDL audio) — open device, enqueue buffers. Or add a small wrapper in your game.
|
|
333
|
-
- **Visuals**: Full WebGL — post-process by rendering to framebuffer, then to screen; particles, blur, etc. in shaders.
|
|
334
|
-
- **UI / Buttons**: \`fillRect\` + \`strokeRect\` for panels/buttons, \`drawText\` for labels. \`isPointInRect(px, py, x, y, w, h)\` for hit-test. Track \`ctx.input.mouseX\`, \`mouseY\`, \`mouseLeft\` in \`update\` and draw any style in code.
|
|
335
|
-
- **Camera**: 2D: store \`offsetX, offsetY, scale\` and pass to your draw calls or a uniform. 3D: \`view\` and \`projection\` matrices from position/target/up and perspective.
|
|
330
|
+
---
|
|
336
331
|
|
|
337
|
-
|
|
332
|
+
## Game lifecycle
|
|
333
|
+
|
|
334
|
+
Your game exports a default object implementing **IGame**:
|
|
335
|
+
|
|
336
|
+
- **init?(ctx, config)** — Called once. Use to create \`createDraw2D(ctx.gl)\`, load fonts, etc.
|
|
337
|
+
- **update(dt)** — Called every frame. \`dt\` = seconds since last frame. Handle input, physics, UI state.
|
|
338
|
+
- **draw()** — Called every frame after \`update\`. Draw with your draw2d, text, or \`ctx.gl\`.
|
|
339
|
+
- **dispose?()** — Called when the window closes. Free resources (draw2d.dispose(), etc.).
|
|
340
|
+
|
|
341
|
+
**GameContext** \`ctx\`: \`ctx.gl\` (WebGL 1), \`ctx.input\`, \`ctx.width\`, \`ctx.height\` (updates on resize).
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## Input
|
|
346
|
+
|
|
347
|
+
\`\`\`ts
|
|
348
|
+
ctx.input.mouseX // number (pixels)
|
|
349
|
+
ctx.input.mouseY
|
|
350
|
+
ctx.input.mouseLeft // boolean
|
|
351
|
+
ctx.input.mouseRight
|
|
352
|
+
ctx.input.mouseMiddle
|
|
353
|
+
ctx.input.isKeyDown(SCANCODE.W) // boolean — use SCANCODE.* for keys
|
|
354
|
+
\`\`\`
|
|
355
|
+
|
|
356
|
+
**SCANCODE**: \`A\`–\`Z\`, \`Space\`, \`Escape\`, \`Up\`, \`Down\`, \`Left\`, \`Right\`, \`Enter\`, \`Shift\`, \`Ctrl\`, \`Tab\`.
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
## 2D drawing
|
|
361
|
+
|
|
362
|
+
\`\`\`ts
|
|
363
|
+
const draw2d = createDraw2D(ctx.gl) // in init
|
|
364
|
+
|
|
365
|
+
draw2d.clear(0.1, 0.1, 0.15, 1)
|
|
366
|
+
draw2d.fillRect(100, 100, 80, 40, 0.2, 0.6, 0.9, 1) // x, y, w, h, r, g, b, a
|
|
367
|
+
draw2d.strokeRect(98, 98, 84, 44, 1, 1, 1, 1) // outline (e.g. button border)
|
|
368
|
+
draw2d.fillTriangles(verts, 1, 0, 0, 1) // verts: Float32Array [x,y, x,y, x,y, ...]
|
|
369
|
+
\`\`\`
|
|
370
|
+
|
|
371
|
+
Coordinates: origin top-left; y increases down. All in pixels.
|
|
372
|
+
|
|
373
|
+
---
|
|
374
|
+
|
|
375
|
+
## Text (any font, size, color)
|
|
376
|
+
|
|
377
|
+
\`\`\`ts
|
|
378
|
+
const font = await loadFont('assets/MyFont.ttf') // TTF or OTF, in init
|
|
379
|
+
|
|
380
|
+
drawText(draw2d, font, 'Score: 100', 20, 20, 24, 1, 1, 1, 1) // x, y, size, r, g, b, a
|
|
381
|
+
|
|
382
|
+
// Or get triangles and draw yourself (e.g. cache or custom color):
|
|
383
|
+
const tri = getTextTriangles(font, 'Hello', 0, 0, 32)
|
|
384
|
+
draw2d.fillTriangles(tri, 0, 1, 0, 1)
|
|
385
|
+
|
|
386
|
+
const { width, height } = measureText(font, 'Hello', 32) // for layout
|
|
387
|
+
\`\`\`
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## UI / buttons
|
|
392
|
+
|
|
393
|
+
\`\`\`ts
|
|
394
|
+
// In update():
|
|
395
|
+
if (ctx.input.mouseLeft && isPointInRect(ctx.input.mouseX, ctx.input.mouseY, 100, 100, 80, 40)) {
|
|
396
|
+
// button clicked
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// In draw(): draw the button with fillRect + strokeRect + drawText (any style you want)
|
|
400
|
+
\`\`\`
|
|
401
|
+
|
|
402
|
+
\`isPointInRect(px, py, x, y, w, h)\` — returns true if (px, py) is inside the rect. Build menus, HUDs, and custom-styled buttons in code.
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
## Textures (images / sprites)
|
|
407
|
+
|
|
408
|
+
\`\`\`ts
|
|
409
|
+
const { texture, width, height } = await loadTexture(ctx.gl, 'assets/sprite.png') // in init
|
|
410
|
+
// Bind and draw with your own shader; helper only does load + upload.
|
|
411
|
+
gl.bindTexture(gl.TEXTURE_2D, texture)
|
|
412
|
+
// ... your quad + sampler2D shader
|
|
413
|
+
\`\`\`
|
|
414
|
+
|
|
415
|
+
\`loadTexture(gl, path)\` returns \`{ texture, width, height }\`. You own the texture; use \`ctx.gl\` for the rest.
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
## Raw WebGL — do anything
|
|
420
|
+
|
|
421
|
+
\`ctx.gl\` is a **full WebGL 1** context. No wrapper. Use it for custom shaders, 3D, framebuffers, MSAA, texture arrays, etc. You have full control.
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
## Full SDL — do anything
|
|
426
|
+
|
|
427
|
+
\`sdl\` is the full [@kmamal/sdl](https://github.com/kmamal/node-sdl) module. Use it directly:
|
|
428
|
+
|
|
429
|
+
- **sdl.video** — displays, windows
|
|
430
|
+
- **sdl.keyboard**, **sdl.mouse**, **sdl.touch** — input
|
|
431
|
+
- **sdl.audio** — playback/recording (see below)
|
|
432
|
+
- **sdl.joystick**, **sdl.controller** — gamepads
|
|
433
|
+
- **sdl.sensor** — accelerometer/gyro
|
|
434
|
+
- **sdl.clipboard**, **sdl.power** — clipboard, battery
|
|
435
|
+
|
|
436
|
+
### Minimal SDL audio (playback)
|
|
437
|
+
|
|
438
|
+
\`\`\`ts
|
|
439
|
+
import { sdl } from '../../scripts/index.js'
|
|
440
|
+
|
|
441
|
+
// In init: get playback devices, open one, enqueue buffers (e.g. decoded WAV).
|
|
442
|
+
const devices = sdl.audio.getDevices().filter(d => d.type === 'playback')
|
|
443
|
+
const device = devices[0]
|
|
444
|
+
if (device) {
|
|
445
|
+
const playback = sdl.audio.openDevice(device, {
|
|
446
|
+
format: 's16',
|
|
447
|
+
frequency: 44100,
|
|
448
|
+
channels: 2,
|
|
449
|
+
samples: 1024,
|
|
450
|
+
callback: (buffer) => { /* fill buffer with samples */ }
|
|
451
|
+
})
|
|
452
|
+
// Keep playback in scope; call playback.destroy() in dispose()
|
|
453
|
+
}
|
|
454
|
+
\`\`\`
|
|
455
|
+
|
|
456
|
+
SDL expects raw PCM in the callback (e.g. 16-bit stereo). Decode WAV/MP3 yourself or use a library; then fill the buffer. No high-level \`playSound(file)\`; you get full control.
|
|
457
|
+
|
|
458
|
+
---
|
|
459
|
+
|
|
460
|
+
## Node / OS — do anything
|
|
461
|
+
|
|
462
|
+
Your game runs in **Node.js**. Use \`import 'fs'\`, \`import 'path'\`, \`child_process\`, or any npm package. File I/O, save data, scripting — nothing is locked down.
|
|
463
|
+
|
|
464
|
+
---
|
|
465
|
+
|
|
466
|
+
## Beyond this
|
|
467
|
+
|
|
468
|
+
- **Camera**: 2D — store \`offsetX\`, \`offsetY\`, \`scale\` and pass to draw calls or a uniform. 3D — \`view\` and \`projection\` matrices.
|
|
469
|
+
- **Assets**: Fonts → \`loadFont('assets/...')\`. Images → \`loadTexture(ctx.gl, 'assets/...')\`. Audio → \`sdl.audio\` + decode + buffers.
|
|
338
470
|
`
|
|
339
471
|
await writeFile(join(projectPath, 'README.md'), readme)
|
|
340
472
|
await writeFile(join(projectPath, 'FRAMEWORK.md'), frameworkMd)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { WebGLRenderingContext as GLContext } from '@kmamal/gl';
|
|
2
|
+
export interface LoadedTexture {
|
|
3
|
+
texture: WebGLTexture;
|
|
4
|
+
width: number;
|
|
5
|
+
height: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Load PNG from path (relative to cwd or absolute), upload to GL, return texture and size.
|
|
9
|
+
* You own the texture; bind and draw with your own shader or use raw gl.
|
|
10
|
+
*/
|
|
11
|
+
export declare function loadTexture(gl: GLContext, path: string): Promise<LoadedTexture>;
|
|
12
|
+
//# sourceMappingURL=Texture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Texture.d.ts","sourceRoot":"","sources":["../../scripts/Texture.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,qBAAqB,IAAI,SAAS,EAAE,MAAM,YAAY,CAAA;AAEpE,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,YAAY,CAAA;IACrB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAoBrF"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Load a PNG from path and upload to a WebGL texture. Minimal helper; use ctx.gl for everything else.
|
|
3
|
+
*/
|
|
4
|
+
import { readFile } from 'fs/promises';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
import pngjs from 'pngjs';
|
|
7
|
+
/**
|
|
8
|
+
* Load PNG from path (relative to cwd or absolute), upload to GL, return texture and size.
|
|
9
|
+
* You own the texture; bind and draw with your own shader or use raw gl.
|
|
10
|
+
*/
|
|
11
|
+
export async function loadTexture(gl, path) {
|
|
12
|
+
const abs = path.startsWith('/') || /^[A-Za-z]:/.test(path) ? path : join(process.cwd(), path);
|
|
13
|
+
const buf = await readFile(abs);
|
|
14
|
+
const pngjsMod = pngjs;
|
|
15
|
+
const PNG = pngjsMod.default?.PNG ?? pngjsMod.PNG;
|
|
16
|
+
const png = PNG.sync.read(buf);
|
|
17
|
+
const width = png.width;
|
|
18
|
+
const height = png.height;
|
|
19
|
+
const data = Buffer.isBuffer(png.data)
|
|
20
|
+
? png.data
|
|
21
|
+
: (png.data instanceof Uint8Array ? png.data : new Uint8Array(png.data));
|
|
22
|
+
const texture = gl.createTexture();
|
|
23
|
+
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
24
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
|
|
25
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
26
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
27
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
28
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
29
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
30
|
+
return { texture, width, height };
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=Texture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Texture.js","sourceRoot":"","sources":["../../scripts/Texture.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,KAAK,MAAM,OAAO,CAAA;AASzB;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,EAAa,EAAE,IAAY;IAC3D,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,CAAA;IAC9F,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAA;IAC/B,MAAM,QAAQ,GAAG,KAA6N,CAAA;IAC9O,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,EAAE,GAAG,IAAI,QAAQ,CAAC,GAAG,CAAA;IACjD,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAC9B,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAA;IACvB,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAA;IACzB,MAAM,IAAI,GAAwB,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;QACzD,CAAC,CAAC,GAAG,CAAC,IAAI;QACV,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,YAAY,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,IAAyB,CAAC,CAAC,CAAA;IAC/F,MAAM,OAAO,GAAG,EAAE,CAAC,aAAa,EAAG,CAAA;IACnC,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,OAAO,CAAC,CAAA;IACtC,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,EAAE,IAAI,CAAC,CAAA;IAC3F,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,cAAc,EAAE,EAAE,CAAC,aAAa,CAAC,CAAA;IACpE,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,cAAc,EAAE,EAAE,CAAC,aAAa,CAAC,CAAA;IACpE,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,kBAAkB,EAAE,EAAE,CAAC,MAAM,CAAC,CAAA;IACjE,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,kBAAkB,EAAE,EAAE,CAAC,MAAM,CAAC,CAAA;IACjE,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,IAA+B,CAAC,CAAA;IAC9D,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAA;AACnC,CAAC"}
|
package/dist/scripts/index.d.ts
CHANGED
|
@@ -7,4 +7,6 @@ export type { Draw2D } from './Graphics.js';
|
|
|
7
7
|
export { loadFont, getTextTriangles, drawText, measureText } from './Text.js';
|
|
8
8
|
export type { Font } from './Text.js';
|
|
9
9
|
export { isPointInRect } from './UI.js';
|
|
10
|
+
export { loadTexture } from './Texture.js';
|
|
11
|
+
export type { LoadedTexture } from './Texture.js';
|
|
10
12
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../scripts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAC3D,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAClD,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAC7C,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,YAAY,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAC3C,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAC7E,YAAY,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../scripts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAC3D,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAClD,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAC7C,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,YAAY,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAC3C,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAC7E,YAAY,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1C,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA"}
|
package/dist/scripts/index.js
CHANGED
|
@@ -2,4 +2,5 @@ export { run, sdl, createInput, SCANCODE } from './Game.js';
|
|
|
2
2
|
export { createDraw2D } from './Graphics.js';
|
|
3
3
|
export { loadFont, getTextTriangles, drawText, measureText } from './Text.js';
|
|
4
4
|
export { isPointInRect } from './UI.js';
|
|
5
|
+
export { loadTexture } from './Texture.js';
|
|
5
6
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../scripts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAI3D,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAE5C,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAE7E,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../scripts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAI3D,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAE5C,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAE7E,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Load a PNG from path and upload to a WebGL texture. Minimal helper; use ctx.gl for everything else.
|
|
3
|
+
*/
|
|
4
|
+
import { readFile } from 'fs/promises'
|
|
5
|
+
import { join } from 'path'
|
|
6
|
+
import pngjs from 'pngjs'
|
|
7
|
+
import type { WebGLRenderingContext as GLContext } from '@kmamal/gl'
|
|
8
|
+
|
|
9
|
+
export interface LoadedTexture {
|
|
10
|
+
texture: WebGLTexture
|
|
11
|
+
width: number
|
|
12
|
+
height: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Load PNG from path (relative to cwd or absolute), upload to GL, return texture and size.
|
|
17
|
+
* You own the texture; bind and draw with your own shader or use raw gl.
|
|
18
|
+
*/
|
|
19
|
+
export async function loadTexture(gl: GLContext, path: string): Promise<LoadedTexture> {
|
|
20
|
+
const abs = path.startsWith('/') || /^[A-Za-z]:/.test(path) ? path : join(process.cwd(), path)
|
|
21
|
+
const buf = await readFile(abs)
|
|
22
|
+
const pngjsMod = pngjs as { default?: { PNG: { sync: { read(b: Buffer): { width: number; height: number; data: Buffer | Uint8Array } } } }; PNG: { sync: { read(b: Buffer): { width: number; height: number; data: Buffer | Uint8Array } } } }
|
|
23
|
+
const PNG = pngjsMod.default?.PNG ?? pngjsMod.PNG
|
|
24
|
+
const png = PNG.sync.read(buf)
|
|
25
|
+
const width = png.width
|
|
26
|
+
const height = png.height
|
|
27
|
+
const data: Buffer | Uint8Array = Buffer.isBuffer(png.data)
|
|
28
|
+
? png.data
|
|
29
|
+
: (png.data instanceof Uint8Array ? png.data : new Uint8Array(png.data as ArrayLike<number>))
|
|
30
|
+
const texture = gl.createTexture()!
|
|
31
|
+
gl.bindTexture(gl.TEXTURE_2D, texture)
|
|
32
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, data)
|
|
33
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
|
|
34
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
|
|
35
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
|
|
36
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
|
|
37
|
+
gl.bindTexture(gl.TEXTURE_2D, null as unknown as WebGLTexture)
|
|
38
|
+
return { texture, width, height }
|
|
39
|
+
}
|
package/scripts/index.ts
CHANGED
|
@@ -7,3 +7,5 @@ export type { Draw2D } from './Graphics.js'
|
|
|
7
7
|
export { loadFont, getTextTriangles, drawText, measureText } from './Text.js'
|
|
8
8
|
export type { Font } from './Text.js'
|
|
9
9
|
export { isPointInRect } from './UI.js'
|
|
10
|
+
export { loadTexture } from './Texture.js'
|
|
11
|
+
export type { LoadedTexture } from './Texture.js'
|