action-engine-js 1.0.3 → 1.0.4
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.
|
@@ -1,864 +1,869 @@
|
|
|
1
|
-
// actionengine/display/graphics/lighting/actiondirectionalshadowlight.js
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Directional light with shadow mapping capability
|
|
5
|
-
* This light type simulates light coming from a distance in a specific direction,
|
|
6
|
-
* like sunlight, with parallel light rays.
|
|
7
|
-
*/
|
|
8
|
-
class ActionDirectionalShadowLight extends ActionLight {
|
|
9
|
-
/**
|
|
10
|
-
* Constructor for a directional shadow light
|
|
11
|
-
* @param {WebGLRenderingContext} gl - The WebGL rendering context
|
|
12
|
-
* @param {boolean} isWebGL2 - Flag indicating if WebGL2 is available
|
|
13
|
-
* @param {ProgramManager} programManager - Reference to the program manager for shader access
|
|
14
|
-
*/
|
|
15
|
-
constructor(gl, isWebGL2, programManager) {
|
|
16
|
-
super(gl, isWebGL2);
|
|
17
|
-
|
|
18
|
-
this.programManager = programManager;
|
|
19
|
-
|
|
20
|
-
// Directional light specific properties
|
|
21
|
-
this.direction = new Vector3(0, -1, 0);
|
|
22
|
-
|
|
23
|
-
// Enable shadows by default for directional lights
|
|
24
|
-
this.castsShadows = true;
|
|
25
|
-
|
|
26
|
-
// Shadow map settings from constants
|
|
27
|
-
this.shadowMapSize = this.constants.SHADOW_MAP.SIZE.value;
|
|
28
|
-
this.shadowBias = this.constants.SHADOW_MAP.BIAS.value;
|
|
29
|
-
|
|
30
|
-
// For tracking direction changes
|
|
31
|
-
this._lastDirection = undefined;
|
|
32
|
-
|
|
33
|
-
// Create matrices for shadow calculations
|
|
34
|
-
this.lightProjectionMatrix = Matrix4.create();
|
|
35
|
-
this.lightViewMatrix = Matrix4.create();
|
|
36
|
-
this.lightSpaceMatrix = Matrix4.create();
|
|
37
|
-
|
|
38
|
-
// Initialize shadow map resources and shader program
|
|
39
|
-
if (this.castsShadows) {
|
|
40
|
-
this.setupShadowMap();
|
|
41
|
-
this.setupShadowShaderProgram();
|
|
42
|
-
this.createReusableBuffers();
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Set the light direction
|
|
48
|
-
* @param {Vector3} direction - The new direction vector (will be normalized)
|
|
49
|
-
*/
|
|
50
|
-
setDirection(direction) {
|
|
51
|
-
// Use copy if it exists, otherwise fall back to direct assignment
|
|
52
|
-
if (typeof this.direction.copy === 'function') {
|
|
53
|
-
this.direction.copy(direction);
|
|
54
|
-
} else {
|
|
55
|
-
this.direction.x = direction.x;
|
|
56
|
-
this.direction.y = direction.y;
|
|
57
|
-
this.direction.z = direction.z;
|
|
58
|
-
}
|
|
59
|
-
this.direction.normalizeInPlace();
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Get the light direction
|
|
64
|
-
* @returns {Vector3} - The current direction
|
|
65
|
-
*/
|
|
66
|
-
getDirection() {
|
|
67
|
-
return this.direction;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Override the update method to check for direction changes
|
|
72
|
-
* @returns {boolean} - Whether any properties changed this frame
|
|
73
|
-
*/
|
|
74
|
-
update() {
|
|
75
|
-
let changed = super.update();
|
|
76
|
-
|
|
77
|
-
// Check if direction has changed
|
|
78
|
-
if (this._lastDirection === undefined ||
|
|
79
|
-
this._lastDirection.x !== this.direction.x ||
|
|
80
|
-
this._lastDirection.y !== this.direction.y ||
|
|
81
|
-
this._lastDirection.z !== this.direction.z) {
|
|
82
|
-
|
|
83
|
-
// Cache current direction to detect changes
|
|
84
|
-
this._lastDirection = {
|
|
85
|
-
x: this.direction.x,
|
|
86
|
-
y: this.direction.y,
|
|
87
|
-
z: this.direction.z
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
changed = true;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// If any properties changed and shadows are enabled,
|
|
94
|
-
// update the light space matrix
|
|
95
|
-
if (changed && this.castsShadows) {
|
|
96
|
-
this.updateLightSpaceMatrix();
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return changed;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
syncWithConstants() {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
this.
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
gl.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
gl.
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
gl.
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
//
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
geometry.
|
|
282
|
-
|
|
283
|
-
//
|
|
284
|
-
geometry.
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
//
|
|
298
|
-
this.
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
gl
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
gl.
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
this.constants.SHADOW_PROJECTION.
|
|
368
|
-
this.constants.SHADOW_PROJECTION.
|
|
369
|
-
this.constants.SHADOW_PROJECTION.
|
|
370
|
-
)
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
//
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
gl.
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
//
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
gl.
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
//
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
this.
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
//
|
|
616
|
-
gl.
|
|
617
|
-
gl.
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
gl.
|
|
622
|
-
gl.
|
|
623
|
-
|
|
624
|
-
//
|
|
625
|
-
gl.
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
Matrix4.
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
Matrix4.
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
//
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
this.
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
gl.
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
gl.
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
//
|
|
757
|
-
//
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
const
|
|
764
|
-
const
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
// Set light
|
|
772
|
-
if (
|
|
773
|
-
gl.uniform3f(
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
// Set light
|
|
777
|
-
if (
|
|
778
|
-
gl.
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
//
|
|
782
|
-
if (
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
// Set
|
|
789
|
-
if (
|
|
790
|
-
gl.
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
// Set
|
|
794
|
-
if (
|
|
795
|
-
gl.
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
//
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
if (this.
|
|
839
|
-
gl.
|
|
840
|
-
this.
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
if (this.
|
|
844
|
-
gl.
|
|
845
|
-
this.
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
if (this.
|
|
849
|
-
gl.
|
|
850
|
-
this.
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
1
|
+
// actionengine/display/graphics/lighting/actiondirectionalshadowlight.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Directional light with shadow mapping capability
|
|
5
|
+
* This light type simulates light coming from a distance in a specific direction,
|
|
6
|
+
* like sunlight, with parallel light rays.
|
|
7
|
+
*/
|
|
8
|
+
class ActionDirectionalShadowLight extends ActionLight {
|
|
9
|
+
/**
|
|
10
|
+
* Constructor for a directional shadow light
|
|
11
|
+
* @param {WebGLRenderingContext} gl - The WebGL rendering context
|
|
12
|
+
* @param {boolean} isWebGL2 - Flag indicating if WebGL2 is available
|
|
13
|
+
* @param {ProgramManager} programManager - Reference to the program manager for shader access
|
|
14
|
+
*/
|
|
15
|
+
constructor(gl, isWebGL2, programManager) {
|
|
16
|
+
super(gl, isWebGL2);
|
|
17
|
+
|
|
18
|
+
this.programManager = programManager;
|
|
19
|
+
|
|
20
|
+
// Directional light specific properties
|
|
21
|
+
this.direction = new Vector3(0, -1, 0);
|
|
22
|
+
|
|
23
|
+
// Enable shadows by default for directional lights
|
|
24
|
+
this.castsShadows = true;
|
|
25
|
+
|
|
26
|
+
// Shadow map settings from constants
|
|
27
|
+
this.shadowMapSize = this.constants.SHADOW_MAP.SIZE.value;
|
|
28
|
+
this.shadowBias = this.constants.SHADOW_MAP.BIAS.value;
|
|
29
|
+
|
|
30
|
+
// For tracking direction changes
|
|
31
|
+
this._lastDirection = undefined;
|
|
32
|
+
|
|
33
|
+
// Create matrices for shadow calculations
|
|
34
|
+
this.lightProjectionMatrix = Matrix4.create();
|
|
35
|
+
this.lightViewMatrix = Matrix4.create();
|
|
36
|
+
this.lightSpaceMatrix = Matrix4.create();
|
|
37
|
+
|
|
38
|
+
// Initialize shadow map resources and shader program
|
|
39
|
+
if (this.castsShadows) {
|
|
40
|
+
this.setupShadowMap();
|
|
41
|
+
this.setupShadowShaderProgram();
|
|
42
|
+
this.createReusableBuffers();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Set the light direction
|
|
48
|
+
* @param {Vector3} direction - The new direction vector (will be normalized)
|
|
49
|
+
*/
|
|
50
|
+
setDirection(direction) {
|
|
51
|
+
// Use copy if it exists, otherwise fall back to direct assignment
|
|
52
|
+
if (typeof this.direction.copy === 'function') {
|
|
53
|
+
this.direction.copy(direction);
|
|
54
|
+
} else {
|
|
55
|
+
this.direction.x = direction.x;
|
|
56
|
+
this.direction.y = direction.y;
|
|
57
|
+
this.direction.z = direction.z;
|
|
58
|
+
}
|
|
59
|
+
this.direction.normalizeInPlace();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get the light direction
|
|
64
|
+
* @returns {Vector3} - The current direction
|
|
65
|
+
*/
|
|
66
|
+
getDirection() {
|
|
67
|
+
return this.direction;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Override the update method to check for direction changes
|
|
72
|
+
* @returns {boolean} - Whether any properties changed this frame
|
|
73
|
+
*/
|
|
74
|
+
update() {
|
|
75
|
+
let changed = super.update();
|
|
76
|
+
|
|
77
|
+
// Check if direction has changed
|
|
78
|
+
if (this._lastDirection === undefined ||
|
|
79
|
+
this._lastDirection.x !== this.direction.x ||
|
|
80
|
+
this._lastDirection.y !== this.direction.y ||
|
|
81
|
+
this._lastDirection.z !== this.direction.z) {
|
|
82
|
+
|
|
83
|
+
// Cache current direction to detect changes
|
|
84
|
+
this._lastDirection = {
|
|
85
|
+
x: this.direction.x,
|
|
86
|
+
y: this.direction.y,
|
|
87
|
+
z: this.direction.z
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
changed = true;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// If any properties changed and shadows are enabled,
|
|
94
|
+
// update the light space matrix
|
|
95
|
+
if (changed && this.castsShadows) {
|
|
96
|
+
this.updateLightSpaceMatrix();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return changed;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Update properties from global lighting constants
|
|
104
|
+
*/
|
|
105
|
+
syncWithConstants() {
|
|
106
|
+
// Update position from constants
|
|
107
|
+
this.position.x = this.constants.LIGHT_POSITION.x;
|
|
108
|
+
this.position.y = this.constants.LIGHT_POSITION.y;
|
|
109
|
+
this.position.z = this.constants.LIGHT_POSITION.z;
|
|
110
|
+
|
|
111
|
+
// Update direction from constants
|
|
112
|
+
this.direction.x = this.constants.LIGHT_DIRECTION.x;
|
|
113
|
+
this.direction.y = this.constants.LIGHT_DIRECTION.y;
|
|
114
|
+
this.direction.z = this.constants.LIGHT_DIRECTION.z;
|
|
115
|
+
|
|
116
|
+
// Update intensity from constants
|
|
117
|
+
this.intensity = this.constants.LIGHT_INTENSITY.value;
|
|
118
|
+
|
|
119
|
+
// Check if shadow map size has changed
|
|
120
|
+
if (this.shadowMapSize !== this.constants.SHADOW_MAP.SIZE.value) {
|
|
121
|
+
this.shadowMapSize = this.constants.SHADOW_MAP.SIZE.value;
|
|
122
|
+
if (this.castsShadows) {
|
|
123
|
+
this.setupShadowMap(); // Recreate shadow map with new size
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Update bias value
|
|
128
|
+
this.shadowBias = this.constants.SHADOW_MAP.BIAS.value;
|
|
129
|
+
|
|
130
|
+
// Recalculate light space matrix with updated frustum bounds
|
|
131
|
+
if (this.castsShadows) {
|
|
132
|
+
this.updateLightSpaceMatrix();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Set up shadow map framebuffer and texture
|
|
138
|
+
*/
|
|
139
|
+
setupShadowMap() {
|
|
140
|
+
const gl = this.gl;
|
|
141
|
+
|
|
142
|
+
// Delete any existing shadow framebuffer and texture
|
|
143
|
+
if (this.shadowFramebuffer) {
|
|
144
|
+
gl.deleteFramebuffer(this.shadowFramebuffer);
|
|
145
|
+
}
|
|
146
|
+
if (this.shadowTexture) {
|
|
147
|
+
gl.deleteTexture(this.shadowTexture);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Create and bind the framebuffer
|
|
151
|
+
this.shadowFramebuffer = gl.createFramebuffer();
|
|
152
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, this.shadowFramebuffer);
|
|
153
|
+
|
|
154
|
+
// Create the shadow texture
|
|
155
|
+
this.shadowTexture = gl.createTexture();
|
|
156
|
+
gl.bindTexture(gl.TEXTURE_2D, this.shadowTexture);
|
|
157
|
+
|
|
158
|
+
// Simple color texture - no depth texture!
|
|
159
|
+
gl.texImage2D(
|
|
160
|
+
gl.TEXTURE_2D,
|
|
161
|
+
0,
|
|
162
|
+
gl.RGBA, // Color format, not depth!
|
|
163
|
+
this.shadowMapSize,
|
|
164
|
+
this.shadowMapSize,
|
|
165
|
+
0,
|
|
166
|
+
gl.RGBA,
|
|
167
|
+
gl.UNSIGNED_BYTE, // Regular 8-bit colors
|
|
168
|
+
null
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
// Set up texture parameters
|
|
172
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
173
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
174
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
175
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
176
|
+
|
|
177
|
+
// Attach color texture to framebuffer
|
|
178
|
+
gl.framebufferTexture2D(
|
|
179
|
+
gl.FRAMEBUFFER,
|
|
180
|
+
gl.COLOR_ATTACHMENT0, // COLOR not DEPTH
|
|
181
|
+
gl.TEXTURE_2D,
|
|
182
|
+
this.shadowTexture,
|
|
183
|
+
0
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
// Create and attach a renderbuffer for depth (we're not reading this)
|
|
187
|
+
const depthBuffer = gl.createRenderbuffer();
|
|
188
|
+
gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
|
|
189
|
+
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, this.shadowMapSize, this.shadowMapSize);
|
|
190
|
+
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
|
|
191
|
+
|
|
192
|
+
// Check framebuffer is complete
|
|
193
|
+
const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
|
|
194
|
+
if (status !== gl.FRAMEBUFFER_COMPLETE) {
|
|
195
|
+
console.error(`Shadow framebuffer is incomplete: ${status}`);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Unbind the framebuffer
|
|
199
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Create reusable buffers for shadow rendering
|
|
204
|
+
*/
|
|
205
|
+
createReusableBuffers() {
|
|
206
|
+
// OPTIMIZED: Create shared shadow geometry buffer for static triangles
|
|
207
|
+
this.maxShadowTriangles = 500000; // Increased to 500k triangles to handle large scenes
|
|
208
|
+
this.maxShadowVertices = this.maxShadowTriangles * 3;
|
|
209
|
+
|
|
210
|
+
// Create shared static geometry buffer
|
|
211
|
+
this.staticShadowGeometry = {
|
|
212
|
+
positions: new Float32Array(this.maxShadowVertices * 3),
|
|
213
|
+
indices: new Uint16Array(this.maxShadowVertices),
|
|
214
|
+
currentVertexOffset: 0,
|
|
215
|
+
currentIndexOffset: 0
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
// Create GL buffers for static geometry
|
|
219
|
+
this.shadowBuffers = {
|
|
220
|
+
position: this.gl.createBuffer(),
|
|
221
|
+
index: this.gl.createBuffer()
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
// Object geometry tracking
|
|
225
|
+
this.objectGeometry = new Map(); // object -> {vertexOffset, indexOffset, indexCount, originalTriangles}
|
|
226
|
+
|
|
227
|
+
console.log(`[ActionDirectionalShadowLight] Initialized static shadow geometry system for ${this.maxShadowTriangles} triangles`);
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Initialize static shadow geometry for an object (called once per object)
|
|
231
|
+
* This uploads the object's original triangles to the shared static geometry buffer
|
|
232
|
+
* @param {Object} object - The object to initialize
|
|
233
|
+
*/
|
|
234
|
+
initializeObjectShadowGeometry(object) {
|
|
235
|
+
// Skip if already initialized or no triangles
|
|
236
|
+
if (this.objectGeometry.has(object) || !object.triangles || object.triangles.length === 0) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Use original triangles if available (for transform via model matrix)
|
|
241
|
+
// Otherwise fall back to current triangles
|
|
242
|
+
let sourceTriangles;
|
|
243
|
+
if (object._originalTriangles && object._originalTriangles.length > 0) {
|
|
244
|
+
sourceTriangles = object._originalTriangles; // Use untransformed triangles for physics objects
|
|
245
|
+
} else if (object.characterModel && object.characterModel.triangles) {
|
|
246
|
+
sourceTriangles = object.characterModel.triangles; // Use character model triangles
|
|
247
|
+
} else {
|
|
248
|
+
sourceTriangles = object.triangles; // Fallback to current triangles
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const triangleCount = sourceTriangles.length;
|
|
252
|
+
const vertexCount = triangleCount * 3;
|
|
253
|
+
|
|
254
|
+
// Check if we have space in the static buffer
|
|
255
|
+
if (this.staticShadowGeometry.currentVertexOffset + vertexCount > this.maxShadowVertices) {
|
|
256
|
+
console.warn(`[DirectionalShadowLight] Not enough space in static shadow buffer for object with ${triangleCount} triangles. Using fallback rendering.`);
|
|
257
|
+
|
|
258
|
+
// Mark this object to use fallback rendering (old method)
|
|
259
|
+
this.objectGeometry.set(object, { useFallback: true });
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const gl = this.gl;
|
|
264
|
+
const geometry = this.staticShadowGeometry;
|
|
265
|
+
|
|
266
|
+
// Store geometry info for this object
|
|
267
|
+
const geometryInfo = {
|
|
268
|
+
vertexOffset: geometry.currentVertexOffset,
|
|
269
|
+
indexOffset: geometry.currentIndexOffset,
|
|
270
|
+
indexCount: vertexCount,
|
|
271
|
+
triangleCount: triangleCount,
|
|
272
|
+
needsModelMatrix: true // Flag indicating this object needs model matrix transforms
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
// Fill geometry arrays with original triangle data
|
|
276
|
+
for (let i = 0; i < triangleCount; i++) {
|
|
277
|
+
const triangle = sourceTriangles[i];
|
|
278
|
+
|
|
279
|
+
for (let j = 0; j < 3; j++) {
|
|
280
|
+
const vertex = triangle.vertices[j];
|
|
281
|
+
const vertexIndex = (geometry.currentVertexOffset + i * 3 + j) * 3;
|
|
282
|
+
|
|
283
|
+
// Store original vertex positions (before any transformations)
|
|
284
|
+
geometry.positions[vertexIndex] = vertex.x;
|
|
285
|
+
geometry.positions[vertexIndex + 1] = vertex.y;
|
|
286
|
+
geometry.positions[vertexIndex + 2] = vertex.z;
|
|
287
|
+
|
|
288
|
+
// Set up indices
|
|
289
|
+
geometry.indices[geometry.currentIndexOffset + i * 3 + j] = geometry.currentVertexOffset + i * 3 + j;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Update offsets for next object
|
|
294
|
+
geometry.currentVertexOffset += vertexCount;
|
|
295
|
+
geometry.currentIndexOffset += vertexCount;
|
|
296
|
+
|
|
297
|
+
// Store geometry info
|
|
298
|
+
this.objectGeometry.set(object, geometryInfo);
|
|
299
|
+
|
|
300
|
+
console.log(`[DirectionalShadowLight] Initialized shadow geometry for object: ${triangleCount} triangles at offset ${geometryInfo.indexOffset}`);
|
|
301
|
+
|
|
302
|
+
// Mark that we need to upload the updated geometry buffer
|
|
303
|
+
this._geometryBufferDirty = true;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Upload the static geometry buffer to GPU (called when geometry changes)
|
|
308
|
+
*/
|
|
309
|
+
uploadStaticGeometry() {
|
|
310
|
+
if (!this._geometryBufferDirty) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const gl = this.gl;
|
|
315
|
+
const geometry = this.staticShadowGeometry;
|
|
316
|
+
|
|
317
|
+
// Upload position data
|
|
318
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, this.shadowBuffers.position);
|
|
319
|
+
gl.bufferData(gl.ARRAY_BUFFER, geometry.positions.subarray(0, geometry.currentVertexOffset * 3), gl.STATIC_DRAW);
|
|
320
|
+
|
|
321
|
+
// Upload index data
|
|
322
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.shadowBuffers.index);
|
|
323
|
+
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, geometry.indices.subarray(0, geometry.currentIndexOffset), gl.STATIC_DRAW);
|
|
324
|
+
|
|
325
|
+
this._geometryBufferDirty = false;
|
|
326
|
+
console.log(`[DirectionalShadowLight] Uploaded static shadow geometry: ${geometry.currentVertexOffset} vertices, ${geometry.currentIndexOffset} indices`);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Set up shadow shader program and get all necessary locations
|
|
331
|
+
*/
|
|
332
|
+
setupShadowShaderProgram() {
|
|
333
|
+
try {
|
|
334
|
+
const shadowShader = new ShadowShader();
|
|
335
|
+
|
|
336
|
+
// Create shadow map program using directional-specific shaders
|
|
337
|
+
this.shadowProgram = this.programManager.createShaderProgram(
|
|
338
|
+
shadowShader.getDirectionalShadowVertexShader(this.isWebGL2),
|
|
339
|
+
shadowShader.getDirectionalShadowFragmentShader(this.isWebGL2),
|
|
340
|
+
"directional_shadow_pass" // Use distinct name to avoid conflicts
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
// Get attribute and uniform locations
|
|
344
|
+
this.shadowLocations = {
|
|
345
|
+
position: this.gl.getAttribLocation(this.shadowProgram, "aPosition"),
|
|
346
|
+
lightSpaceMatrix: this.gl.getUniformLocation(this.shadowProgram, "uLightSpaceMatrix"),
|
|
347
|
+
modelMatrix: this.gl.getUniformLocation(this.shadowProgram, "uModelMatrix"),
|
|
348
|
+
debugShadowMap: this.gl.getUniformLocation(this.shadowProgram, "uDebugShadowMap"),
|
|
349
|
+
forceShadowMapTest: this.gl.getUniformLocation(this.shadowProgram, "uForceShadowMapTest"),
|
|
350
|
+
shadowMapSize: this.gl.getUniformLocation(this.shadowProgram, "uShadowMapSize")
|
|
351
|
+
};
|
|
352
|
+
} catch (error) {
|
|
353
|
+
console.error("Error setting up shadow shader program:", error);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Updates light space matrices based on light position and direction
|
|
359
|
+
* This creates the view and projection matrices needed for shadow mapping
|
|
360
|
+
* @param {Object} sceneBounds - Optional scene bounding box (min, max vectors) for automatic fitting
|
|
361
|
+
*/
|
|
362
|
+
updateLightSpaceMatrix(sceneBounds) {
|
|
363
|
+
// Default scene bounds if not provided
|
|
364
|
+
if (!sceneBounds) {
|
|
365
|
+
sceneBounds = {
|
|
366
|
+
min: new Vector3(
|
|
367
|
+
this.constants.SHADOW_PROJECTION.LEFT.value,
|
|
368
|
+
this.constants.SHADOW_PROJECTION.BOTTOM.value,
|
|
369
|
+
this.constants.SHADOW_PROJECTION.NEAR.value
|
|
370
|
+
),
|
|
371
|
+
max: new Vector3(
|
|
372
|
+
this.constants.SHADOW_PROJECTION.RIGHT.value,
|
|
373
|
+
this.constants.SHADOW_PROJECTION.TOP.value,
|
|
374
|
+
this.constants.SHADOW_PROJECTION.FAR.value
|
|
375
|
+
)
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Automatically fit shadow frustum to scene if enabled
|
|
380
|
+
if (this.constants.SHADOW_PROJECTION.AUTO_FIT) {
|
|
381
|
+
// Auto-fit logic would go here
|
|
382
|
+
// For now, we'll use the constants directly
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// For directional light, use orthographic projection
|
|
386
|
+
const left = this.constants.SHADOW_PROJECTION.LEFT.value;
|
|
387
|
+
const right = this.constants.SHADOW_PROJECTION.RIGHT.value;
|
|
388
|
+
const bottom = this.constants.SHADOW_PROJECTION.BOTTOM.value;
|
|
389
|
+
const top = this.constants.SHADOW_PROJECTION.TOP.value;
|
|
390
|
+
const near = this.constants.SHADOW_PROJECTION.NEAR.value;
|
|
391
|
+
const far = this.constants.SHADOW_PROJECTION.FAR.value;
|
|
392
|
+
|
|
393
|
+
// Create light projection matrix (orthographic for directional light)
|
|
394
|
+
Matrix4.ortho(this.lightProjectionMatrix, left, right, bottom, top, near, far);
|
|
395
|
+
|
|
396
|
+
// Create light view matrix - looking from light position toward center
|
|
397
|
+
const lightTarget = new Vector3(0, 0, 0);
|
|
398
|
+
|
|
399
|
+
// Use a fixed distance value
|
|
400
|
+
const fixedDistance = 100.0;
|
|
401
|
+
|
|
402
|
+
// Calculate target position based on light direction
|
|
403
|
+
lightTarget.x = this.position.x + this.direction.x * fixedDistance;
|
|
404
|
+
lightTarget.y = this.position.y + this.direction.y * fixedDistance;
|
|
405
|
+
lightTarget.z = this.position.z + this.direction.z * fixedDistance;
|
|
406
|
+
|
|
407
|
+
// Choose an appropriate up vector that avoids collinearity with light direction
|
|
408
|
+
let upVector = [0, 1, 0]; // Default up vector
|
|
409
|
+
|
|
410
|
+
// Check if light direction is too closely aligned with the default up vector
|
|
411
|
+
if (Math.abs(this.direction.y) > 0.99) {
|
|
412
|
+
// If pointing almost straight up/down, use Z axis as up vector instead
|
|
413
|
+
upVector = [0, 0, 1];
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
Matrix4.lookAt(
|
|
417
|
+
this.lightViewMatrix,
|
|
418
|
+
this.position.toArray(),
|
|
419
|
+
lightTarget.toArray(),
|
|
420
|
+
upVector
|
|
421
|
+
);
|
|
422
|
+
|
|
423
|
+
// Combine into light space matrix
|
|
424
|
+
Matrix4.multiply(this.lightSpaceMatrix, this.lightProjectionMatrix, this.lightViewMatrix);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Begin shadow map rendering pass
|
|
429
|
+
*/
|
|
430
|
+
beginShadowPass() {
|
|
431
|
+
const gl = this.gl;
|
|
432
|
+
|
|
433
|
+
// Save current viewport
|
|
434
|
+
this._savedViewport = gl.getParameter(gl.VIEWPORT);
|
|
435
|
+
|
|
436
|
+
// Reset static geometry binding flag for this shadow pass
|
|
437
|
+
this._staticGeometryBound = false;
|
|
438
|
+
|
|
439
|
+
// Bind shadow framebuffer and set viewport to shadow map size
|
|
440
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, this.shadowFramebuffer);
|
|
441
|
+
gl.viewport(0, 0, this.shadowMapSize, this.shadowMapSize);
|
|
442
|
+
|
|
443
|
+
// Check for debug force test mode
|
|
444
|
+
if (this.constants.DEBUG.FORCE_SHADOW_MAP_TEST) {
|
|
445
|
+
// Use a color framebuffer instead for debug visualization
|
|
446
|
+
if (!this._debugColorFramebuffer) {
|
|
447
|
+
this._debugColorFramebuffer = gl.createFramebuffer();
|
|
448
|
+
this._debugColorTexture = gl.createTexture();
|
|
449
|
+
|
|
450
|
+
gl.bindTexture(gl.TEXTURE_2D, this._debugColorTexture);
|
|
451
|
+
gl.texImage2D(
|
|
452
|
+
gl.TEXTURE_2D,
|
|
453
|
+
0,
|
|
454
|
+
gl.RGBA,
|
|
455
|
+
this.shadowMapSize,
|
|
456
|
+
this.shadowMapSize,
|
|
457
|
+
0,
|
|
458
|
+
gl.RGBA,
|
|
459
|
+
gl.UNSIGNED_BYTE,
|
|
460
|
+
null
|
|
461
|
+
);
|
|
462
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
463
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
464
|
+
|
|
465
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, this._debugColorFramebuffer);
|
|
466
|
+
gl.framebufferTexture2D(
|
|
467
|
+
gl.FRAMEBUFFER,
|
|
468
|
+
gl.COLOR_ATTACHMENT0,
|
|
469
|
+
gl.TEXTURE_2D,
|
|
470
|
+
this._debugColorTexture,
|
|
471
|
+
0
|
|
472
|
+
);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Use this framebuffer instead
|
|
476
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, this._debugColorFramebuffer);
|
|
477
|
+
gl.clearColor(1.0, 0.0, 0.0, 1.0); // Bright red
|
|
478
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
479
|
+
|
|
480
|
+
// Save this texture for visualization
|
|
481
|
+
this._lastDebugTexture = this._debugColorTexture;
|
|
482
|
+
|
|
483
|
+
// Skip shadow rendering in debug mode
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Always clear both color and depth buffers regardless of WebGL version
|
|
488
|
+
gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black (far depth)
|
|
489
|
+
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
490
|
+
|
|
491
|
+
// Use shadow mapping program
|
|
492
|
+
gl.useProgram(this.shadowProgram);
|
|
493
|
+
|
|
494
|
+
// Set light space matrix uniform
|
|
495
|
+
gl.uniformMatrix4fv(this.shadowLocations.lightSpaceMatrix, false, this.lightSpaceMatrix);
|
|
496
|
+
|
|
497
|
+
// Set debug shadow map uniform if available
|
|
498
|
+
if (this.shadowLocations.debugShadowMap !== null) {
|
|
499
|
+
const debugMode = this.constants.DEBUG.VISUALIZE_SHADOW_MAP ? 1 : 0;
|
|
500
|
+
gl.uniform1i(this.shadowLocations.debugShadowMap, debugMode);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// Set force shadow map test uniform if available
|
|
504
|
+
if (this.shadowLocations.forceShadowMapTest !== null) {
|
|
505
|
+
const forceTest = this.constants.DEBUG.FORCE_SHADOW_MAP_TEST ? 1 : 0;
|
|
506
|
+
gl.uniform1i(this.shadowLocations.forceShadowMapTest, forceTest);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Set shadow map size uniform
|
|
510
|
+
if (this.shadowLocations.shadowMapSize !== null) {
|
|
511
|
+
gl.uniform1f(this.shadowLocations.shadowMapSize, this.shadowMapSize);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* End shadow map rendering pass and restore previous state
|
|
517
|
+
*/
|
|
518
|
+
endShadowPass() {
|
|
519
|
+
const gl = this.gl;
|
|
520
|
+
|
|
521
|
+
// Unbind shadow framebuffer
|
|
522
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
523
|
+
|
|
524
|
+
// Restore viewport if it was saved
|
|
525
|
+
if (this._savedViewport) {
|
|
526
|
+
gl.viewport(this._savedViewport[0], this._savedViewport[1], this._savedViewport[2], this._savedViewport[3]);
|
|
527
|
+
} else {
|
|
528
|
+
// Fallback to default viewport
|
|
529
|
+
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Render a single object to the shadow map
|
|
535
|
+
* @param {Object} object - The object to render
|
|
536
|
+
*/
|
|
537
|
+
/**
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Helper method to fill batched shadow data
|
|
542
|
+
* @param {Array} validObjects - Array of valid objects with metadata
|
|
543
|
+
*/
|
|
544
|
+
fillBatchedShadowData(validObjects) {
|
|
545
|
+
let vertexOffset = 0;
|
|
546
|
+
|
|
547
|
+
for (const { object, triangleCount } of validObjects) {
|
|
548
|
+
const triangles = object.triangles;
|
|
549
|
+
|
|
550
|
+
for (let i = 0; i < triangles.length; i++) {
|
|
551
|
+
const triangle = triangles[i];
|
|
552
|
+
|
|
553
|
+
for (let j = 0; j < 3; j++) {
|
|
554
|
+
const vertex = triangle.vertices[j];
|
|
555
|
+
const baseIndex = (vertexOffset + i * 3 + j) * 3;
|
|
556
|
+
|
|
557
|
+
this.persistentShadowArrays.positions[baseIndex] = vertex.x;
|
|
558
|
+
this.persistentShadowArrays.positions[baseIndex + 1] = vertex.y;
|
|
559
|
+
this.persistentShadowArrays.positions[baseIndex + 2] = vertex.z;
|
|
560
|
+
|
|
561
|
+
this.persistentShadowArrays.indices[vertexOffset + i * 3 + j] = vertexOffset + i * 3 + j;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
vertexOffset += triangleCount * 3;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Render a single object to the shadow map
|
|
571
|
+
* @param {Object} object - Single object to render
|
|
572
|
+
*/
|
|
573
|
+
renderObjectToShadowMap(object) {
|
|
574
|
+
const gl = this.gl;
|
|
575
|
+
const triangles = object.triangles;
|
|
576
|
+
|
|
577
|
+
// Skip if object has no triangles
|
|
578
|
+
if (!triangles || triangles.length === 0) {
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// Use identity model matrix since triangles are already in world space
|
|
583
|
+
const modelMatrix = Matrix4.create();
|
|
584
|
+
gl.uniformMatrix4fv(this.shadowLocations.modelMatrix, false, modelMatrix);
|
|
585
|
+
|
|
586
|
+
// Calculate total vertices and indices
|
|
587
|
+
const totalVertices = triangles.length * 3;
|
|
588
|
+
|
|
589
|
+
// Only allocate new arrays if needed or if size has changed
|
|
590
|
+
if (!this._positionsArray || this._positionsArray.length < totalVertices * 3) {
|
|
591
|
+
this._positionsArray = new Float32Array(totalVertices * 3);
|
|
592
|
+
}
|
|
593
|
+
if (!this._indicesArray || this._indicesArray.length < totalVertices) {
|
|
594
|
+
this._indicesArray = new Uint16Array(totalVertices);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// Fill position and index arrays
|
|
598
|
+
for (let i = 0; i < triangles.length; i++) {
|
|
599
|
+
const triangle = triangles[i];
|
|
600
|
+
|
|
601
|
+
// Process vertices
|
|
602
|
+
for (let j = 0; j < 3; j++) {
|
|
603
|
+
const vertex = triangle.vertices[j];
|
|
604
|
+
const baseIndex = (i * 3 + j) * 3;
|
|
605
|
+
|
|
606
|
+
this._positionsArray[baseIndex] = vertex.x;
|
|
607
|
+
this._positionsArray[baseIndex + 1] = vertex.y;
|
|
608
|
+
this._positionsArray[baseIndex + 2] = vertex.z;
|
|
609
|
+
|
|
610
|
+
// Set up indices
|
|
611
|
+
this._indicesArray[i * 3 + j] = i * 3 + j;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// Use buffer orphaning to avoid stalls
|
|
616
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, this.shadowBuffers.position);
|
|
617
|
+
gl.bufferData(gl.ARRAY_BUFFER, this._positionsArray.byteLength, gl.DYNAMIC_DRAW);
|
|
618
|
+
gl.bufferSubData(gl.ARRAY_BUFFER, 0, this._positionsArray.subarray(0, totalVertices * 3));
|
|
619
|
+
|
|
620
|
+
// Set up position attribute
|
|
621
|
+
gl.vertexAttribPointer(this.shadowLocations.position, 3, gl.FLOAT, false, 0, 0);
|
|
622
|
+
gl.enableVertexAttribArray(this.shadowLocations.position);
|
|
623
|
+
|
|
624
|
+
// Upload index data
|
|
625
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.shadowBuffers.index);
|
|
626
|
+
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this._indicesArray.byteLength, gl.DYNAMIC_DRAW);
|
|
627
|
+
gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, this._indicesArray.subarray(0, totalVertices));
|
|
628
|
+
|
|
629
|
+
// Draw object
|
|
630
|
+
gl.drawElements(gl.TRIANGLES, totalVertices, gl.UNSIGNED_SHORT, 0);
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Get the model matrix for an object based on its current physics state
|
|
634
|
+
* @param {Object} object - The object to get matrix for
|
|
635
|
+
* @returns {Float32Array} - The model matrix
|
|
636
|
+
*/
|
|
637
|
+
getObjectModelMatrix(object) {
|
|
638
|
+
const modelMatrix = Matrix4.create();
|
|
639
|
+
|
|
640
|
+
// For physics objects, use the body's current position and rotation
|
|
641
|
+
if (object.body) {
|
|
642
|
+
const pos = object.body.position;
|
|
643
|
+
const rot = object.body.rotation;
|
|
644
|
+
|
|
645
|
+
// Apply translation
|
|
646
|
+
Matrix4.translate(modelMatrix, modelMatrix, [pos.x, pos.y, pos.z]);
|
|
647
|
+
|
|
648
|
+
// Apply rotation from physics body quaternion
|
|
649
|
+
const rotationMatrix = Matrix4.create();
|
|
650
|
+
Matrix4.fromQuat(rotationMatrix, [rot.x, rot.y, rot.z, rot.w]);
|
|
651
|
+
Matrix4.multiply(modelMatrix, modelMatrix, rotationMatrix);
|
|
652
|
+
}
|
|
653
|
+
// For objects with manual position/rotation
|
|
654
|
+
else if (object.position) {
|
|
655
|
+
Matrix4.translate(modelMatrix, modelMatrix, [object.position.x, object.position.y, object.position.z]);
|
|
656
|
+
|
|
657
|
+
if (object.rotation !== undefined) {
|
|
658
|
+
Matrix4.rotateY(modelMatrix, modelMatrix, object.rotation);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
return modelMatrix;
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Fallback rendering method for objects that don't fit in static buffer
|
|
666
|
+
* Uses the original dynamic triangle upload approach
|
|
667
|
+
* @param {Object} object - The object to render
|
|
668
|
+
*/
|
|
669
|
+
renderObjectToShadowMapFallback(object) {
|
|
670
|
+
const gl = this.gl;
|
|
671
|
+
const triangles = object.triangles;
|
|
672
|
+
|
|
673
|
+
// Skip if object has no triangles
|
|
674
|
+
if (!triangles || triangles.length === 0) {
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// Set model matrix for this object (identity since triangles are already transformed)
|
|
679
|
+
const modelMatrix = Matrix4.create();
|
|
680
|
+
gl.uniformMatrix4fv(this.shadowLocations.modelMatrix, false, modelMatrix);
|
|
681
|
+
|
|
682
|
+
// Calculate total vertices and indices
|
|
683
|
+
const totalVertices = triangles.length * 3;
|
|
684
|
+
|
|
685
|
+
// Only allocate new arrays if needed or if size has changed
|
|
686
|
+
if (!this._fallbackPositionsArray || this._fallbackPositionsArray.length < totalVertices * 3) {
|
|
687
|
+
this._fallbackPositionsArray = new Float32Array(totalVertices * 3);
|
|
688
|
+
}
|
|
689
|
+
if (!this._fallbackIndicesArray || this._fallbackIndicesArray.length < totalVertices) {
|
|
690
|
+
this._fallbackIndicesArray = new Uint16Array(totalVertices);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// Fill position and index arrays
|
|
694
|
+
for (let i = 0; i < triangles.length; i++) {
|
|
695
|
+
const triangle = triangles[i];
|
|
696
|
+
|
|
697
|
+
// Process vertices
|
|
698
|
+
for (let j = 0; j < 3; j++) {
|
|
699
|
+
const vertex = triangle.vertices[j];
|
|
700
|
+
const baseIndex = (i * 3 + j) * 3;
|
|
701
|
+
|
|
702
|
+
this._fallbackPositionsArray[baseIndex] = vertex.x;
|
|
703
|
+
this._fallbackPositionsArray[baseIndex + 1] = vertex.y;
|
|
704
|
+
this._fallbackPositionsArray[baseIndex + 2] = vertex.z;
|
|
705
|
+
|
|
706
|
+
// Set up indices
|
|
707
|
+
this._fallbackIndicesArray[i * 3 + j] = i * 3 + j;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// Create temporary buffers for fallback rendering
|
|
712
|
+
if (!this._fallbackBuffers) {
|
|
713
|
+
this._fallbackBuffers = {
|
|
714
|
+
position: gl.createBuffer(),
|
|
715
|
+
index: gl.createBuffer()
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// Bind and upload position data to fallback buffer
|
|
720
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, this._fallbackBuffers.position);
|
|
721
|
+
gl.bufferData(gl.ARRAY_BUFFER, this._fallbackPositionsArray, gl.DYNAMIC_DRAW);
|
|
722
|
+
|
|
723
|
+
// Set up position attribute
|
|
724
|
+
gl.vertexAttribPointer(this.shadowLocations.position, 3, gl.FLOAT, false, 0, 0);
|
|
725
|
+
gl.enableVertexAttribArray(this.shadowLocations.position);
|
|
726
|
+
|
|
727
|
+
// Bind and upload index data to fallback buffer
|
|
728
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._fallbackBuffers.index);
|
|
729
|
+
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this._fallbackIndicesArray, gl.DYNAMIC_DRAW);
|
|
730
|
+
|
|
731
|
+
// Draw object using fallback method
|
|
732
|
+
gl.drawElements(gl.TRIANGLES, totalVertices, gl.UNSIGNED_SHORT, 0);
|
|
733
|
+
|
|
734
|
+
// Reset static geometry binding flag since we used different buffers
|
|
735
|
+
this._staticGeometryBound = false;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* Get the light space matrix for passing to shaders
|
|
742
|
+
* @returns {Float32Array} - The light space transformation matrix
|
|
743
|
+
*/
|
|
744
|
+
getLightSpaceMatrix() {
|
|
745
|
+
return this.lightSpaceMatrix;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
/**
|
|
749
|
+
* Apply this light's uniforms to a shader program
|
|
750
|
+
* @param {WebGLProgram} program - The shader program
|
|
751
|
+
* @param {number} index - Index of this light in an array of lights (for future multi-light support)
|
|
752
|
+
*/
|
|
753
|
+
applyToShader(program, index = 0) {
|
|
754
|
+
const gl = this.gl;
|
|
755
|
+
|
|
756
|
+
// Get light uniform locations
|
|
757
|
+
// For now we just use the standard uniforms, but in the future we'd use arrays
|
|
758
|
+
// like uDirectionalLights[index].direction, etc.
|
|
759
|
+
// tbh idk if this is working cause we had wrong names and it doesn't matter
|
|
760
|
+
// like these were uLightDirection and uLightPosition which don't exist anywhere
|
|
761
|
+
// else in the codebase but theres no error or known issues here so idk what this
|
|
762
|
+
// even does
|
|
763
|
+
const lightDirLoc = gl.getUniformLocation(program, "uLightDir");
|
|
764
|
+
const lightPosLoc = gl.getUniformLocation(program, "uLightPos");
|
|
765
|
+
const lightIntensityLoc = gl.getUniformLocation(program, "uLightIntensity");
|
|
766
|
+
const shadowMapLoc = gl.getUniformLocation(program, "uShadowMap");
|
|
767
|
+
const lightSpaceMatrixLoc = gl.getUniformLocation(program, "uLightSpaceMatrix");
|
|
768
|
+
const shadowsEnabledLoc = gl.getUniformLocation(program, "uShadowsEnabled");
|
|
769
|
+
const shadowBiasLoc = gl.getUniformLocation(program, "uShadowBias");
|
|
770
|
+
|
|
771
|
+
// Set light direction
|
|
772
|
+
if (lightDirLoc !== null) {
|
|
773
|
+
gl.uniform3f(lightDirLoc, this.direction.x, this.direction.y, this.direction.z);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// Set light position
|
|
777
|
+
if (lightPosLoc !== null) {
|
|
778
|
+
gl.uniform3f(lightPosLoc, this.position.x, this.position.y, this.position.z);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// Set light intensity
|
|
782
|
+
if (lightIntensityLoc !== null) {
|
|
783
|
+
gl.uniform1f(lightIntensityLoc, this.intensity);
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// Apply shadow mapping uniforms if shadows are enabled
|
|
787
|
+
if (this.castsShadows) {
|
|
788
|
+
// Set light space matrix
|
|
789
|
+
if (lightSpaceMatrixLoc !== null) {
|
|
790
|
+
gl.uniformMatrix4fv(lightSpaceMatrixLoc, false, this.lightSpaceMatrix);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// Set shadows enabled flag
|
|
794
|
+
if (shadowsEnabledLoc !== null) {
|
|
795
|
+
gl.uniform1i(shadowsEnabledLoc, 1); // 1 = true
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// Set shadow bias
|
|
799
|
+
if (shadowBiasLoc !== null) {
|
|
800
|
+
gl.uniform1f(shadowBiasLoc, this.shadowBias);
|
|
801
|
+
}
|
|
802
|
+
} else if (shadowsEnabledLoc !== null) {
|
|
803
|
+
// Shadows are disabled for this light
|
|
804
|
+
gl.uniform1i(shadowsEnabledLoc, 0); // 0 = false
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
/**
|
|
809
|
+
* Apply shadow quality preset
|
|
810
|
+
* @param {number} presetIndex - Index of the preset to apply
|
|
811
|
+
*/
|
|
812
|
+
setQualityPreset(presetIndex) {
|
|
813
|
+
const presets = this.constants.SHADOW_QUALITY_PRESETS;
|
|
814
|
+
if (presetIndex < 0 || presetIndex >= presets.length) {
|
|
815
|
+
console.warn(`Invalid shadow quality preset index: ${presetIndex}`);
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
const preset = presets[presetIndex];
|
|
820
|
+
this.shadowMapSize = preset.mapSize;
|
|
821
|
+
this.shadowBias = preset.bias;
|
|
822
|
+
|
|
823
|
+
// Recreate shadow map with new settings
|
|
824
|
+
if (this.castsShadows) {
|
|
825
|
+
this.setupShadowMap();
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
console.log(`Applied shadow quality preset: ${preset.name}`);
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
/**
|
|
832
|
+
* Cleanup resources used by this light
|
|
833
|
+
*/
|
|
834
|
+
dispose() {
|
|
835
|
+
const gl = this.gl;
|
|
836
|
+
|
|
837
|
+
// Clean up shadow map resources
|
|
838
|
+
if (this.shadowFramebuffer) {
|
|
839
|
+
gl.deleteFramebuffer(this.shadowFramebuffer);
|
|
840
|
+
this.shadowFramebuffer = null;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
if (this.shadowTexture) {
|
|
844
|
+
gl.deleteTexture(this.shadowTexture);
|
|
845
|
+
this.shadowTexture = null;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
if (this._debugColorFramebuffer) {
|
|
849
|
+
gl.deleteFramebuffer(this._debugColorFramebuffer);
|
|
850
|
+
this._debugColorFramebuffer = null;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
if (this._debugColorTexture) {
|
|
854
|
+
gl.deleteTexture(this._debugColorTexture);
|
|
855
|
+
this._debugColorTexture = null;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
// Clean up buffers
|
|
859
|
+
if (this.shadowBuffers) {
|
|
860
|
+
if (this.shadowBuffers.position) {
|
|
861
|
+
gl.deleteBuffer(this.shadowBuffers.position);
|
|
862
|
+
}
|
|
863
|
+
if (this.shadowBuffers.index) {
|
|
864
|
+
gl.deleteBuffer(this.shadowBuffers.index);
|
|
865
|
+
}
|
|
866
|
+
this.shadowBuffers = null;
|
|
867
|
+
}
|
|
868
|
+
}
|
|
864
869
|
}
|