@@ -22,78 +22,128 @@ import {
22
22
* @return TileSheet<T>
23
23
*/
24
24
25
- /** @typedef {object } TileWorld
26
- * @prop {tileMaker } makeTileSheet
27
- * @prop {(dims: FacetDims) => Facet } makeFacet
28
- */
29
-
30
- /** @typedef {object } FacetDims
31
- * @prop {number } x
32
- * @prop {number } y
33
- * @prop {number } width
34
- * @prop {number } height
35
- */
36
-
37
- /** @typedef {object } Facet
38
- * @prop {(sheet: TileSheet<unknown>, offset?: {x: number, y: number}) => Layer } makeLayer
39
- */
25
+ import {
26
+ generateCurvedTiles ,
27
+ generateSimpleTiles ,
28
+ } from './tilegen.js' ;
40
29
41
30
/**
42
- * @param {object } param
43
- * @param {HTMLCanvasElement } param.$world
44
- * @param {number } [param.tileSize]
45
- * @param {number } [param.cellSize]
46
- * @param {(world: TileWorld) => void } param.build
31
+ * @param {object } opts
32
+ * @param {HTMLCanvasElement } opts.$world
33
+ * @param {number } [opts.tileSize]
34
+ * @param {number } [opts.cellSize]
35
+ * @param {number } [opts.worldWidth]
36
+ * @param {number } [opts.worldHeight]
37
+ * @param {boolean } [opts.showCurvyTiles]
47
38
*/
48
- export default async function demo ( {
49
- $world,
50
- tileSize = 256 ,
51
- cellSize = 64 ,
52
- build,
53
- } ) {
39
+ export default async function demo ( opts ) {
40
+ const {
41
+ $world,
42
+ tileSize = 256 ,
43
+ cellSize = 64 ,
44
+
45
+ // TODO fix curvy tile layer, needs to have dims N+1 but be scissored to cut outer ring in half
46
+ worldWidth = 5 ,
47
+ worldHeight = 5 ,
48
+ showCurvyTiles = true ,
49
+ } = opts ;
50
+
54
51
const gl = $world . getContext ( 'webgl2' ) ;
55
52
if ( ! gl ) throw new Error ( 'No GL For You!' ) ;
56
53
57
54
sizeToParent ( $world ) ;
58
55
56
+ /** @type {Layer[] } */
57
+ const layers = [ ] ;
58
+
59
59
const tileRend = makeTileRenderer ( gl , await compileTileProgram ( gl ) ) ;
60
60
61
- /** @type {TileSheet<unknown>[] } */
62
- const sheets = [ ] ;
63
-
64
- /** @type {{dims: FacetDims, layers: Layer[]}[] } */
65
- const facets = [ ] ;
66
-
67
- build ( {
68
- makeTileSheet ( tiles ) {
69
- const tileSheet = makeTileSheet ( gl , tiles , { tileSize } ) ;
70
- sheets . push ( tileSheet ) ;
71
- return tileSheet ;
72
- } ,
73
-
74
- makeFacet ( dims ) {
75
- /** @type {Layer[] } */
76
- const layers = [ ] ;
77
- facets . push ( { dims, layers } ) ;
78
- return {
79
- makeLayer ( { texture } , offset = { x : 0 , y : 0 } ) {
80
- const { x, y, width, height } = dims ;
81
- const layer = makeLayer ( gl , {
82
- texture,
83
- cellSize,
84
- left : x + offset . x ,
85
- top : y + offset . y ,
86
- width, height
87
- } ) ;
88
- layers . push ( layer ) ;
89
- return layer ;
90
- } ,
91
- } ;
92
- } ,
93
- } ) ;
94
-
95
- /** @returns {Promise<number> } */
96
- const nextFrame = ( ) => new Promise ( resolve => requestAnimationFrame ( t => resolve ( t ) ) )
61
+ const landCurveTiles = makeTileSheet ( gl , generateCurvedTiles ( {
62
+ aFill : '#5c9e31' , // land
63
+ bFill : '#61b2e4' , // water
64
+ // gridLineStyle: 'red',
65
+ } ) , { tileSize } ) ;
66
+
67
+ const foreTiles = makeTileSheet ( gl , generateSimpleTiles (
68
+ { glyph : '🧚' } ,
69
+ { glyph : '🍵' } ,
70
+ { glyph : '🫖' } ,
71
+ { glyph : '⛵' }
72
+ ) , { tileSize } ) ;
73
+
74
+ /**
75
+ * @param {TileSheet<number> } sheet
76
+ * @param {{x: number, y: number} } [offset]
77
+ * @returns {Layer }
78
+ */
79
+ const makeWorldLayer = ( { texture } , offset = { x : 0 , y : 0 } ) => {
80
+ const x = 0 , y = 0 , width = worldWidth , height = worldHeight ;
81
+ const layer = makeLayer ( gl , {
82
+ texture,
83
+ cellSize,
84
+ left : x + offset . x ,
85
+ top : y + offset . y ,
86
+ width, height
87
+ } ) ;
88
+ layers . push ( layer ) ;
89
+ return layer ;
90
+ } ;
91
+
92
+ const bgSQ = makeWorldLayer ( landCurveTiles ) ;
93
+ const bg = makeWorldLayer ( landCurveTiles , { x : - 0.5 , y : - 0.5 } ) ;
94
+ const fg = makeWorldLayer ( foreTiles ) ;
95
+
96
+ // generate terrain
97
+ {
98
+ const isWater = new Uint8Array ( bg . width * bg . height ) ;
99
+ const { random } = makeRandom ( ) ;
100
+ for ( let i = 0 ; i < isWater . length ; i ++ )
101
+ isWater [ i ] = random ( ) > 0.5 ? 1 : 0 ;
102
+
103
+ const land = landCurveTiles . getLayerID ( 0b0000 ) ;
104
+ const water = landCurveTiles . getLayerID ( 0b1111 ) ;
105
+ for ( let y = 0 ; y < bg . height ; y ++ )
106
+ for ( let x = 0 ; x < bg . width ; x ++ )
107
+ bgSQ . set ( x , y , {
108
+ layerID : isWater [ y * bg . width + x ] ? water : land
109
+ } ) ;
110
+
111
+ const stride = bg . width ;
112
+ for ( let y = 0 ; y < bg . height ; y ++ ) {
113
+ for ( let x = 0 ; x < bg . width ; x ++ ) {
114
+ const nw = isWater [ ( y + 0 ) * stride + x + 0 ] ;
115
+ const ne = isWater [ ( y + 0 ) * stride + x + 1 ] ;
116
+ const sw = isWater [ ( y + 1 ) * stride + x + 0 ] ;
117
+ const se = isWater [ ( y + 1 ) * stride + x + 1 ] ;
118
+ const tileID = ( ( nw << 1 | ne ) << 1 | se ) << 1 | sw ;
119
+ const layerID = landCurveTiles . getLayerID ( tileID ) ;
120
+ bg . set ( x , y , { layerID } ) ;
121
+ }
122
+ }
123
+ }
124
+
125
+ // place fore objects
126
+ {
127
+ const { randn, random } = makeRandom ( ) ;
128
+ for ( let y = 0 ; y < fg . height ; y ++ ) {
129
+ for ( let x = 0 ; x < fg . width ; x ++ ) {
130
+ const tileID = Number ( randn ( 2n * BigInt ( foreTiles . size ) ) ) ;
131
+ const layerID = foreTiles . getLayerID ( tileID ) ;
132
+ const spin = random ( ) ;
133
+ fg . set ( x , y , { layerID, spin } ) ;
134
+ }
135
+ }
136
+ }
137
+
138
+ // send layer data to gpu; NOTE this needs to be called going forward after any update
139
+ bg . send ( ) ;
140
+ bgSQ . send ( ) ;
141
+ fg . send ( ) ;
142
+
143
+ bgSQ . visible = ! showCurvyTiles ; // boring non-curvy layer
144
+ bg . visible = showCurvyTiles ; // curvy new hotness
145
+
146
+ // forever draw loop
97
147
for (
98
148
let t = await nextFrame ( ) , lastT = t ; ;
99
149
lastT = t , t = await nextFrame ( )
@@ -107,16 +157,25 @@ export default async function demo({
107
157
gl . clear ( gl . COLOR_BUFFER_BIT ) ;
108
158
109
159
tileRend . draw ( function * ( ) {
110
- for ( const { layers } of facets ) {
111
- for ( const layer of layers ) {
112
- if ( layer . visible ) yield layer ;
113
- }
114
- }
160
+ for ( const layer of layers )
161
+ if ( layer . visible ) yield layer ;
115
162
} ( ) ) ;
116
163
}
117
164
118
165
}
119
166
167
+ function makeRandom ( seed = 0xdead_beefn ) {
168
+ let randState = seed ;
169
+ const rand = ( ) => ( randState = randState * 6364136223846793005n + 1442695040888963407n ) >> 17n & 0xffff_ffffn ;
170
+ /** @param {bigint } n */
171
+ const randn = n => rand ( ) % n ;
172
+ const random = ( ) => Number ( rand ( ) ) / 0x1_0000_0000 ;
173
+ return { rand, randn, random } ;
174
+ }
175
+
176
+ /** @returns {Promise<number> } */
177
+ const nextFrame = ( ) => new Promise ( resolve => requestAnimationFrame ( t => resolve ( t ) ) )
178
+
120
179
/** @param {HTMLCanvasElement } $canvas */
121
180
function sizeToParent ( $canvas , update = ( ) => { } ) {
122
181
const $cont = $canvas . parentElement ;
0 commit comments