@@ -34,7 +34,7 @@ export default class RaylibJs {
34
34
35
35
/**
36
36
* @typedef {(event: T) => void } EventHandler
37
- * @template {Event} T
37
+ * @template {Event} T
38
38
*/
39
39
40
40
/** @type {EventHandler<KeyEvent> = } */
@@ -48,15 +48,24 @@ export default class RaylibJs {
48
48
49
49
#reset( ) {
50
50
this . previous = undefined ;
51
+ /** @type {{instance: WebAssembly.Instance, module: WebAssembly.Module} } */
51
52
this . wasm = undefined ;
53
+ this . publicDir = undefined ;
54
+ /** @type {CanvasRenderingContext2D } */
52
55
this . ctx = undefined ;
56
+ /** @type {CanvasRenderingContext2D } */
53
57
this . dt = undefined ;
54
58
this . targetFPS = 60 ;
55
59
this . prevPressedKeyState = new Set ( ) ;
56
60
this . currentPressedKeyState = new Set ( ) ;
57
61
this . currentMouseWheelMoveState = 0 ;
58
62
this . currentMousePosition = { x : 0 , y : 0 } ;
59
63
this . quit = false ;
64
+ // FIXME: Could theoretically be an array
65
+ /** @type {Map<number, HTMLImageElement> }*/
66
+ this . textures = new Map ( ) ;
67
+ /** @type {Map<string, number> } */
68
+ this . textureIDs = new Map ( ) ;
60
69
}
61
70
62
71
constructor ( ) {
@@ -67,17 +76,20 @@ export default class RaylibJs {
67
76
this . quit = true ;
68
77
}
69
78
70
- async start ( { wasmPath, canvasId } ) {
79
+ async start ( { wasmPath, canvasId, publicDir = "./" } ) {
71
80
if ( this . wasm !== undefined ) {
72
81
console . error ( "The game is already running. Please stop() it first." ) ;
73
82
return ;
74
83
}
75
84
76
85
const canvas = document . getElementById ( canvasId ) ;
77
86
this . ctx = canvas . getContext ( "2d" ) ;
78
- if ( this . ctx === null ) {
87
+ const bgCanvas = document . createElement ( "canvas" ) ;
88
+ this . btx = bgCanvas . getContext ( "2d" ) ;
89
+ if ( this . ctx === null || this . btx === null ) {
79
90
throw new Error ( "Could not create 2d canvas context" ) ;
80
91
}
92
+ this . publicDir = publicDir ;
81
93
this . wasm = await Asyncify . instantiateStreaming ( fetch ( wasmPath ) , {
82
94
env : make_environment ( this )
83
95
} ) ;
@@ -87,6 +99,8 @@ export default class RaylibJs {
87
99
InitWindow ( width , height , title_ptr ) {
88
100
this . ctx . canvas . width = width ;
89
101
this . ctx . canvas . height = height ;
102
+ this . btx . canvas . width = width ;
103
+ this . btx . canvas . height = height ;
90
104
const buffer = this . wasm . instance . exports . memory . buffer ;
91
105
document . title = cstr_by_ptr ( buffer , title_ptr ) ;
92
106
@@ -193,6 +207,32 @@ export default class RaylibJs {
193
207
this . ctx . fillStyle = color ;
194
208
this . ctx . fillRect ( posX , posY , width , height ) ;
195
209
}
210
+
211
+ // RLAPI void DrawTexture(Texture2D texture, int posX, int posY, Color tint); // Draw a Texture2D
212
+ DrawTexture ( texture_ptr , posX , posY , tint_ptr ) {
213
+ /** @type {ArrayBuffer } */
214
+ const buffer = this . wasm . instance . exports . memory . buffer ;
215
+ const tint = getColorFromMemory ( buffer , tint_ptr ) ;
216
+ const textureID = new Uint32Array ( buffer , texture_ptr ) [ 0 ] ;
217
+ const texture = this . textures . get ( textureID ) ;
218
+ // TODO: actually use width / height from the passed struct
219
+ const width = texture . width ;
220
+ const height = texture . height ;
221
+ if ( texture === undefined ) {
222
+ // TODO: Better error reporting
223
+ throw new Error ( `textureID ${ textureID } not found.` ) ;
224
+ }
225
+ this . btx . clearRect ( 0 , 0 , width , height ) ;
226
+ this . btx . drawImage ( texture , 0 , 0 ) ;
227
+ this . btx . fillStyle = tint ;
228
+ this . btx . globalCompositeOperation = "multiply" ;
229
+ this . btx . fillRect ( 0 , 0 , width , height ) ;
230
+ this . btx . globalCompositeOperation = "destination-in" ;
231
+ this . btx . drawImage ( texture , 0 , 0 ) ;
232
+ this . btx . globalCompositeOperation = "source-over" ;
233
+ this . ctx . drawImage ( this . btx . canvas , 0 , 0 , width , height , posX , posY , width , height ) ;
234
+
235
+ }
196
236
197
237
IsKeyPressed ( key ) {
198
238
return ! this . prevPressedKeyState . has ( key ) && this . currentPressedKeyState . has ( key ) ;
@@ -260,6 +300,71 @@ export default class RaylibJs {
260
300
return this . ctx . measureText ( text ) . width ;
261
301
}
262
302
303
+ LoadTexture ( result_ptr , fileName_ptr ) {
304
+ const buffer = this . wasm . instance . exports . memory . buffer ;
305
+ const fileName = this . publicDir + cstr_by_ptr ( buffer , fileName_ptr ) ;
306
+ const result = new DataView ( buffer , result_ptr ) ;
307
+ if ( this . textureIDs . has ( fileName ) ) {
308
+ const img = this . textures . get ( this . textureIDs . get ( fileName ) ) ;
309
+ this . #setTexture( result , img , fileName ) ;
310
+ return ;
311
+ }
312
+ const img = new Image ( ) ;
313
+ // Wrap image loading in a promise
314
+ const promise = new Promise ( ( resolve , reject ) => {
315
+ function wrapResolve ( ) {
316
+ img . removeEventListener ( "error" , wrapReject ) ;
317
+ resolve ( ) ;
318
+ } ;
319
+ function wrapReject ( ) {
320
+ img . removeEventListener ( "load" , wrapResolve ) ;
321
+ reject ( ) ;
322
+ } ;
323
+ img . addEventListener ( "load" , wrapResolve , { once : true } ) ;
324
+ img . addEventListener ( "error" , wrapReject , { once : true } ) ;
325
+ } ) ;
326
+ img . src = fileName ;
327
+ return promise . then ( ( ) => {
328
+ this . #setTexture( result , img , fileName ) ;
329
+ console . log ( "Loaded texture" , fileName ) ;
330
+ } ) . catch ( ( err ) => {
331
+ // TODO: Proper image error handling
332
+ console . error ( err ) ;
333
+ throw err ;
334
+ } ) ;
335
+ }
336
+
337
+ /**
338
+ * @param {DataView } buffer
339
+ * @param {HTMLImageElement } img
340
+ * @param {string } fileName
341
+ */
342
+ #setTexture( buffer , img , fileName ) {
343
+ /*
344
+ // Texture, tex data stored in GPU memory (VRAM)
345
+ typedef struct Texture {
346
+ unsigned int id; // OpenGL texture id
347
+ int width; // Texture base width
348
+ int height; // Texture base height
349
+ int mipmaps; // Mipmap levels, 1 by default
350
+ int format; // Data format (PixelFormat type)
351
+ } Texture;
352
+ */
353
+ const id = this . textureIDs . size ;
354
+ buffer . setUint32 ( 0 , id ) ;
355
+ this . textures . set ( id , img ) ;
356
+ this . textureIDs . set ( fileName , id ) ;
357
+ const PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 = 7 ;
358
+ new Int32Array (
359
+ buffer . buffer , buffer . byteOffset + 4 , 4 * 4
360
+ ) . set ( [
361
+ img . width ,
362
+ img . height ,
363
+ 1 ,
364
+ PIXELFORMAT_UNCOMPRESSED_R8G8B8A8
365
+ ] ) ;
366
+ }
367
+
263
368
memcpy ( dest_ptr , src_ptr , count ) {
264
369
const buffer = this . wasm . instance . exports . memory . buffer ;
265
370
// TODO: Why this does not fix asyncify problems
0 commit comments