1
- const SPRITE_ATLAS_WIDTH = 2048
2
- const SPRITE_ATLAS_HEIGHT = 2048
3
- const DEF_FONT_FILTER = "nearest"
4
-
5
- import type {
6
- SpriteAnims ,
7
- NineSlice ,
8
- LoadSpriteSrc ,
9
- LoadSpriteOpt ,
10
- ImageSource ,
11
- LoadFontOpt ,
12
- TexFilter ,
13
- Outline ,
14
- BitmapFontData ,
15
- ShaderData ,
16
- } from "./types"
17
-
18
1
import {
19
2
Event ,
20
3
isDataURL ,
21
- dataURLToArrayBuffer ,
22
4
} from "./utils"
23
5
24
- import {
25
- GfxCtx ,
26
- Texture ,
27
- } from "./gfx"
28
-
29
- import {
30
- AudioCtx ,
31
- } from "./audio"
32
-
33
- import {
34
- Quad ,
35
- Color ,
36
- } from "./math"
37
-
38
- import TexPacker from "./texPacker"
39
-
40
6
export class Asset < D > {
41
7
loaded : boolean = false
42
8
data : D | null = null
@@ -134,149 +100,11 @@ export class AssetBucket<D> {
134
100
}
135
101
}
136
102
137
- // get an array of frames based on configuration on how to slice the image
138
- function slice ( x = 1 , y = 1 , dx = 0 , dy = 0 , w = 1 , h = 1 ) : Quad [ ] {
139
- const frames = [ ]
140
- const qw = w / x
141
- const qh = h / y
142
- for ( let j = 0 ; j < y ; j ++ ) {
143
- for ( let i = 0 ; i < x ; i ++ ) {
144
- frames . push ( new Quad (
145
- dx + i * qw ,
146
- dy + j * qh ,
147
- qw ,
148
- qh ,
149
- ) )
150
- }
151
- }
152
- return frames
153
- }
154
-
155
- export class SpriteData {
156
-
157
- tex : Texture
158
- frames : Quad [ ] = [ new Quad ( 0 , 0 , 1 , 1 ) ]
159
- anims : SpriteAnims = { }
160
- slice9 : NineSlice | null = null
161
-
162
- constructor (
163
- tex : Texture ,
164
- frames ?: Quad [ ] ,
165
- anims : SpriteAnims = { } ,
166
- slice9 : NineSlice = null ,
167
- ) {
168
- this . tex = tex
169
- if ( frames ) this . frames = frames
170
- this . anims = anims
171
- this . slice9 = slice9
172
- }
173
-
174
- get width ( ) {
175
- return this . tex . width * this . frames [ 0 ] . w
176
- }
177
-
178
- get height ( ) {
179
- return this . tex . height * this . frames [ 0 ] . h
180
- }
181
-
182
- static from ( ctx : AssetCtx , src : LoadSpriteSrc , opt : LoadSpriteOpt = { } ) : Promise < SpriteData > {
183
- return typeof src === "string"
184
- ? SpriteData . fromURL ( ctx , src , opt )
185
- : Promise . resolve ( SpriteData . fromImage ( ctx , src , opt ) )
186
- }
187
-
188
- static fromImage ( ctx : AssetCtx , data : ImageSource , opt : LoadSpriteOpt = { } ) : SpriteData {
189
- const [ tex , quad ] = ctx . packImg ( data )
190
- const frames = opt . frames ? opt . frames . map ( ( f ) => new Quad (
191
- quad . x + f . x * quad . w ,
192
- quad . y + f . y * quad . h ,
193
- f . w * quad . w ,
194
- f . h * quad . h ,
195
- ) ) : slice ( opt . sliceX || 1 , opt . sliceY || 1 , quad . x , quad . y , quad . w , quad . h )
196
- return new SpriteData ( tex , frames , opt . anims , opt . slice9 )
197
- }
198
-
199
- static fromURL ( ctx : AssetCtx , url : string , opt : LoadSpriteOpt = { } ) : Promise < SpriteData > {
200
- return ctx . loadImg ( url ) . then ( ( img ) => SpriteData . fromImage ( ctx , img , opt ) )
201
- }
202
-
203
- }
204
-
205
- export class FontData {
206
- fontface : FontFace
207
- filter : TexFilter = DEF_FONT_FILTER
208
- outline : Outline | null = null
209
- constructor ( face : FontFace , opt : LoadFontOpt = { } ) {
210
- this . fontface = face
211
- this . filter = opt . filter ?? DEF_FONT_FILTER
212
- if ( opt . outline ) {
213
- this . outline = {
214
- width : 1 ,
215
- color : new Color ( 0 , 0 , 0 ) ,
216
- }
217
- if ( typeof opt . outline === "number" ) {
218
- this . outline . width = opt . outline
219
- } else if ( typeof opt . outline === "object" ) {
220
- if ( opt . outline . width ) this . outline . width = opt . outline . width
221
- if ( opt . outline . color ) this . outline . color = opt . outline . color
222
- }
223
- }
224
- }
225
- }
226
-
227
- export class SoundData {
228
-
229
- buf : AudioBuffer
230
-
231
- constructor ( buf : AudioBuffer ) {
232
- this . buf = buf
233
- }
234
-
235
- static fromArrayBuffer ( ctx : AssetCtx , buf : ArrayBuffer ) : Promise < SoundData > {
236
- return new Promise ( ( resolve , reject ) =>
237
- ctx . audio . ctx . decodeAudioData ( buf , resolve , reject ) ,
238
- ) . then ( ( buf : AudioBuffer ) => new SoundData ( buf ) )
239
- }
240
-
241
- static fromURL ( ctx : AssetCtx , url : string ) : Promise < SoundData > {
242
- if ( isDataURL ( url ) ) {
243
- return SoundData . fromArrayBuffer ( ctx , dataURLToArrayBuffer ( url ) )
244
- } else {
245
- return ctx . fetchURL ( url )
246
- . then ( ( res ) => res . arrayBuffer ( ) )
247
- . then ( ( buf ) => SoundData . fromArrayBuffer ( ctx , buf ) )
248
- }
249
- }
250
-
251
- }
252
-
253
- export type AssetCtx = {
254
- gfx : GfxCtx ,
255
- audio : AudioCtx ,
256
- setURLPrefix : ( prefix : string ) => void ,
257
- getURLPrefix : ( ) => string ,
258
- loadImg : ( src : string ) => Promise < HTMLImageElement > ,
259
- fetchURL : ( url : string ) => Promise < Response > ,
260
- packImg : TexPacker [ "add" ] ,
261
- loadSprite ,
262
- }
263
-
264
- export default ( gfx : GfxCtx , audio : AudioCtx ) : AssetCtx => {
103
+ export default ( ) => {
265
104
266
105
const state = {
267
- // prefix for when loading from a url
268
106
urlPrefix : "" ,
269
- // asset holders
270
- sprites : new AssetBucket < SpriteData > ( ) ,
271
- fonts : new AssetBucket < FontData > ( ) ,
272
- bitmapFonts : new AssetBucket < BitmapFontData > ( ) ,
273
- sounds : new AssetBucket < SoundData > ( ) ,
274
- shaders : new AssetBucket < ShaderData > ( ) ,
275
- custom : new AssetBucket < any > ( ) ,
276
- packer : new TexPacker ( gfx , SPRITE_ATLAS_WIDTH , SPRITE_ATLAS_HEIGHT ) ,
277
- // if we finished initially loading all assets
278
107
loaded : false ,
279
-
280
108
}
281
109
282
110
function setURLPrefix ( prefix : string ) {
@@ -296,6 +124,18 @@ export default (gfx: GfxCtx, audio: AudioCtx): AssetCtx => {
296
124
} )
297
125
}
298
126
127
+ function fetchJSON ( path : string ) {
128
+ return fetchURL ( path ) . then ( ( res ) => res . json ( ) )
129
+ }
130
+
131
+ function fetchText ( path : string ) {
132
+ return fetchURL ( path ) . then ( ( res ) => res . text ( ) )
133
+ }
134
+
135
+ function fetchArrayBuffer ( path : string ) {
136
+ return fetchURL ( path ) . then ( ( res ) => res . arrayBuffer ( ) )
137
+ }
138
+
299
139
// wrapper around image loader to get a Promise
300
140
function loadImg ( src : string ) : Promise < HTMLImageElement > {
301
141
const img = new Image ( )
@@ -307,72 +147,14 @@ export default (gfx: GfxCtx, audio: AudioCtx): AssetCtx => {
307
147
} )
308
148
}
309
149
310
- function createSpriteSheet (
311
- images : ImageSource [ ] ,
312
- opt : LoadSpriteOpt = { } ,
313
- ) : SpriteData {
314
- const canvas = document . createElement ( "canvas" )
315
- const width = images [ 0 ] . width
316
- const height = images [ 0 ] . height
317
- canvas . width = width * images . length
318
- canvas . height = height
319
- const c2d = canvas . getContext ( "2d" )
320
- images . forEach ( ( img , i ) => {
321
- if ( img instanceof ImageData ) {
322
- c2d . putImageData ( img , i * width , 0 )
323
- } else {
324
- c2d . drawImage ( img , i * width , 0 )
325
- }
326
- } )
327
- const merged = c2d . getImageData ( 0 , 0 , images . length * width , height )
328
- return SpriteData . fromImage ( ctx , merged , {
329
- ...opt ,
330
- sliceX : images . length ,
331
- sliceY : 1 ,
332
- } )
333
- }
334
-
335
- // load a sprite to asset manager
336
- function loadSprite (
337
- name : string | null ,
338
- src : LoadSpriteSrc | LoadSpriteSrc [ ] ,
339
- opt : LoadSpriteOpt = {
340
- sliceX : 1 ,
341
- sliceY : 1 ,
342
- anims : { } ,
343
- } ,
344
- ) : Asset < SpriteData > {
345
- if ( Array . isArray ( src ) ) {
346
- if ( src . some ( ( s ) => typeof s === "string" ) ) {
347
- return state . sprites . add (
348
- name ,
349
- Promise . all ( src . map ( ( s ) => {
350
- return typeof s === "string" ? loadImg ( s ) : Promise . resolve ( s )
351
- } ) ) . then ( ( images ) => createSpriteSheet ( images , opt ) ) ,
352
- )
353
- } else {
354
- return state . sprites . addLoaded ( name , createSpriteSheet ( src as ImageSource [ ] , opt ) )
355
- }
356
- } else {
357
- if ( typeof src === "string" ) {
358
- return state . sprites . add ( name , SpriteData . from ( ctx , src , opt ) )
359
- } else {
360
- return state . sprites . addLoaded ( name , SpriteData . fromImage ( ctx , src , opt ) )
361
- }
362
- }
363
- }
364
-
365
- const ctx = {
366
- gfx,
367
- audio,
150
+ return {
368
151
setURLPrefix,
369
152
getURLPrefix,
370
153
loadImg,
371
154
fetchURL,
372
- packImg : state . packer . add ,
373
- loadSprite,
155
+ fetchJSON,
156
+ fetchText,
157
+ fetchArrayBuffer,
374
158
}
375
159
376
- return ctx
377
-
378
160
}
0 commit comments