@1pizzateam/roostr 0.7.0 → 0.8.0
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/dist/roostr.d.ts +274 -211
- package/dist/roostr.js +1448 -1120
- package/package.json +1 -1
package/dist/roostr.js
CHANGED
|
@@ -24,8 +24,8 @@ SOFTWARE.
|
|
|
24
24
|
https://github.com/1pizzateam/RoostR.js
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
|
-
import { Mat4x3, Vec3, Frustum, Vec2,
|
|
28
|
-
export { Accumulator, Bezier, Box as BoundingBox, Sphere as BoundingSphere, Circ, Frustum, Grid, Mat3, Mat4, Mat4x3, NumArray, Quat, Rand, Ray3 as Ray, Rect, RollingAverage, Plane as SpatialPlane, Time, Trigo, Utils, Vec2, Vec3 } from '@1pizzateam/spock';
|
|
27
|
+
import { Mat4x3, Vec3, Frustum, Vec2, Utils, Time, Mat4, Ray3, Trigo, Quat, Mat3, Box, Sphere as Sphere$1, Rect } from '@1pizzateam/spock';
|
|
28
|
+
export { Accumulator, Bezier, Box as BoundingBox, Sphere as BoundingSphere, Circ, Frustum, Grid, Mat3, Mat4, Mat4x3, NumArray, Quat, Rand, Ray3 as Ray, Ray2, Rect, RollingAverage, Plane as SpatialPlane, Time, Trigo, Utils, Vec2, Vec3 } from '@1pizzateam/spock';
|
|
29
29
|
|
|
30
30
|
class GLState {
|
|
31
31
|
program = null;
|
|
@@ -42,6 +42,13 @@ class GLState {
|
|
|
42
42
|
this.elementBuffer = null;
|
|
43
43
|
this.attributeMask = null;
|
|
44
44
|
}
|
|
45
|
+
reset() {
|
|
46
|
+
this.invalidate();
|
|
47
|
+
this.maxAttributes = 0;
|
|
48
|
+
this.frameOfProgram = new WeakMap();
|
|
49
|
+
this.materialOfProgram = new WeakMap();
|
|
50
|
+
this.materialVersionOfProgram = new WeakMap();
|
|
51
|
+
}
|
|
45
52
|
}
|
|
46
53
|
const states = new WeakMap();
|
|
47
54
|
function getGLState(context) {
|
|
@@ -68,6 +75,13 @@ class SceneRenderer {
|
|
|
68
75
|
this.blendEnabled = null;
|
|
69
76
|
this.depthWrite = null;
|
|
70
77
|
}
|
|
78
|
+
reset() {
|
|
79
|
+
getGLState(this.context).reset();
|
|
80
|
+
this.blendKey = '';
|
|
81
|
+
this.blendEnabled = null;
|
|
82
|
+
this.depthWrite = null;
|
|
83
|
+
this.defaultSettings();
|
|
84
|
+
}
|
|
71
85
|
defaultSettings() {
|
|
72
86
|
this.setDepthFunc('LEQUAL');
|
|
73
87
|
this.enableDepthTest();
|
|
@@ -103,7 +117,7 @@ class SceneRenderer {
|
|
|
103
117
|
this.context.depthMask(false);
|
|
104
118
|
}
|
|
105
119
|
enableBlendMode(equation, source, destination) {
|
|
106
|
-
const key = equation
|
|
120
|
+
const key = `${equation}|${source}|${destination}`;
|
|
107
121
|
if (key !== this.blendKey) {
|
|
108
122
|
this.setBlendEquation(equation);
|
|
109
123
|
this.setBlendFunction(source, destination);
|
|
@@ -154,9 +168,105 @@ class SceneGraph {
|
|
|
154
168
|
}
|
|
155
169
|
}
|
|
156
170
|
|
|
171
|
+
const scratchAmbient = new Vec3();
|
|
172
|
+
function clearFlatArrays(flatArrays) {
|
|
173
|
+
flatArrays.position.length = 0;
|
|
174
|
+
flatArrays.diffuse.length = 0;
|
|
175
|
+
flatArrays.specular.length = 0;
|
|
176
|
+
flatArrays.constantAttenuation.length = 0;
|
|
177
|
+
flatArrays.linearAttenuation.length = 0;
|
|
178
|
+
flatArrays.quadraticAttenuation.length = 0;
|
|
179
|
+
flatArrays.cutoff.length = 0;
|
|
180
|
+
flatArrays.exponent.length = 0;
|
|
181
|
+
flatArrays.direction.length = 0;
|
|
182
|
+
flatArrays.type.length = 0;
|
|
183
|
+
}
|
|
184
|
+
function ensureLightBuffers(flatArrays, count) {
|
|
185
|
+
if (flatArrays.floatPosition && flatArrays.floatPosition.length === count * 3)
|
|
186
|
+
return;
|
|
187
|
+
flatArrays.floatPosition = new Float32Array(count * 3);
|
|
188
|
+
flatArrays.floatDiffuse = new Float32Array(count * 3);
|
|
189
|
+
flatArrays.floatSpecular = new Float32Array(count * 3);
|
|
190
|
+
flatArrays.floatDirection = new Float32Array(count * 3);
|
|
191
|
+
flatArrays.floatAttenuation = new Float32Array(count * 3);
|
|
192
|
+
flatArrays.floatCutoff = new Float32Array(count);
|
|
193
|
+
flatArrays.floatExponent = new Float32Array(count);
|
|
194
|
+
flatArrays.floatType = new Float32Array(count);
|
|
195
|
+
flatArrays.packedPosition = new Float32Array(count * 4);
|
|
196
|
+
flatArrays.packedDiffuse = new Float32Array(count * 4);
|
|
197
|
+
flatArrays.packedSpecular = new Float32Array(count * 4);
|
|
198
|
+
}
|
|
199
|
+
function pushLight(flatArrays, index, kind, light) {
|
|
200
|
+
const o = index * 3;
|
|
201
|
+
const isSpot = kind === 'spot';
|
|
202
|
+
const isDirectional = kind === 'directional';
|
|
203
|
+
const att = isDirectional ? null : light;
|
|
204
|
+
light.position.toArray(flatArrays.floatPosition, o);
|
|
205
|
+
light.diffuse.toArray(flatArrays.floatDiffuse, o);
|
|
206
|
+
light.specular.toArray(flatArrays.floatSpecular, o);
|
|
207
|
+
if (isDirectional)
|
|
208
|
+
light.direction.toArray(flatArrays.floatDirection, o);
|
|
209
|
+
else if (isSpot)
|
|
210
|
+
light.direction.toArray(flatArrays.floatDirection, o);
|
|
211
|
+
else
|
|
212
|
+
flatArrays.floatDirection.fill(0, o, o + 3);
|
|
213
|
+
const constant = att ? att.constantAttenuation : 1;
|
|
214
|
+
const linear = att ? att.linearAttenuation : 0;
|
|
215
|
+
const quadratic = att ? att.quadraticAttenuation : 0;
|
|
216
|
+
flatArrays.floatAttenuation[o] = constant;
|
|
217
|
+
flatArrays.floatAttenuation[o + 1] = linear;
|
|
218
|
+
flatArrays.floatAttenuation[o + 2] = quadratic;
|
|
219
|
+
flatArrays.floatCutoff[index] = isSpot ? light.cutoff : 0;
|
|
220
|
+
flatArrays.floatExponent[index] = isSpot ? light.exponent : 0;
|
|
221
|
+
flatArrays.floatType[index] = isDirectional ? 0 : isSpot ? 2 : 1;
|
|
222
|
+
const q = index * 4;
|
|
223
|
+
light.position.toArray(flatArrays.packedPosition, q);
|
|
224
|
+
flatArrays.packedPosition[q + 3] = flatArrays.floatType[index];
|
|
225
|
+
light.diffuse.toArray(flatArrays.packedDiffuse, q);
|
|
226
|
+
flatArrays.packedDiffuse[q + 3] = flatArrays.floatCutoff[index];
|
|
227
|
+
light.specular.toArray(flatArrays.packedSpecular, q);
|
|
228
|
+
flatArrays.packedSpecular[q + 3] = flatArrays.floatExponent[index];
|
|
229
|
+
const dir = isDirectional || isSpot ? light.direction : null;
|
|
230
|
+
flatArrays.position.push(light.position.x, light.position.y, light.position.z);
|
|
231
|
+
flatArrays.diffuse.push(light.diffuse.x, light.diffuse.y, light.diffuse.z);
|
|
232
|
+
flatArrays.specular.push(light.specular.x, light.specular.y, light.specular.z);
|
|
233
|
+
if (dir)
|
|
234
|
+
flatArrays.direction.push(dir.x, dir.y, dir.z);
|
|
235
|
+
else
|
|
236
|
+
flatArrays.direction.push(0, 0, 0);
|
|
237
|
+
flatArrays.constantAttenuation.push(constant);
|
|
238
|
+
flatArrays.linearAttenuation.push(linear);
|
|
239
|
+
flatArrays.quadraticAttenuation.push(quadratic);
|
|
240
|
+
flatArrays.cutoff.push(isSpot ? light.cutoff : 0);
|
|
241
|
+
flatArrays.exponent.push(isSpot ? light.exponent : 0);
|
|
242
|
+
flatArrays.type.push(kind);
|
|
243
|
+
}
|
|
244
|
+
function packLights(directionals, points, spots, ambients, flatArrays, maxLights, extraAmbient) {
|
|
245
|
+
clearFlatArrays(flatArrays);
|
|
246
|
+
const total = spots.length + points.length + directionals.length;
|
|
247
|
+
const count = Math.min(total, maxLights);
|
|
248
|
+
ensureLightBuffers(flatArrays, count);
|
|
249
|
+
let index = 0;
|
|
250
|
+
for (let i = 0; i < spots.length && index < count; i++)
|
|
251
|
+
pushLight(flatArrays, index++, 'spot', spots[i]);
|
|
252
|
+
for (let i = 0; i < points.length && index < count; i++)
|
|
253
|
+
pushLight(flatArrays, index++, 'point', points[i]);
|
|
254
|
+
for (let i = 0; i < directionals.length && index < count; i++)
|
|
255
|
+
pushLight(flatArrays, index++, 'directional', directionals[i]);
|
|
256
|
+
const ambient = scratchAmbient.setScalar(0, 0, 0);
|
|
257
|
+
if (extraAmbient)
|
|
258
|
+
ambient.add(extraAmbient);
|
|
259
|
+
for (let i = 0; i < ambients.length; i++)
|
|
260
|
+
ambient.addScaledVector(ambients[i].diffuse, ambients[i].intensity);
|
|
261
|
+
if (!flatArrays.floatAmbient)
|
|
262
|
+
flatArrays.floatAmbient = new Float32Array(3);
|
|
263
|
+
ambient.toArray(flatArrays.floatAmbient);
|
|
264
|
+
flatArrays.ambient = [ambient.x, ambient.y, ambient.z];
|
|
265
|
+
return flatArrays;
|
|
266
|
+
}
|
|
267
|
+
|
|
157
268
|
const MAX_LIGHTS = 8;
|
|
158
269
|
class Lights {
|
|
159
|
-
static _ambient = new Vec3();
|
|
160
270
|
warnedOverflow = false;
|
|
161
271
|
directionals;
|
|
162
272
|
points;
|
|
@@ -249,106 +359,13 @@ class Lights {
|
|
|
249
359
|
return false;
|
|
250
360
|
}
|
|
251
361
|
}
|
|
252
|
-
ClearFlatArrays() {
|
|
253
|
-
this.flatArrays.position.length = 0;
|
|
254
|
-
this.flatArrays.diffuse.length = 0;
|
|
255
|
-
this.flatArrays.specular.length = 0;
|
|
256
|
-
this.flatArrays.constantAttenuation.length = 0;
|
|
257
|
-
this.flatArrays.linearAttenuation.length = 0;
|
|
258
|
-
this.flatArrays.quadraticAttenuation.length = 0;
|
|
259
|
-
this.flatArrays.cutoff.length = 0;
|
|
260
|
-
this.flatArrays.exponent.length = 0;
|
|
261
|
-
this.flatArrays.direction.length = 0;
|
|
262
|
-
this.flatArrays.type.length = 0;
|
|
263
|
-
}
|
|
264
|
-
ensureBuffers(count) {
|
|
265
|
-
const f = this.flatArrays;
|
|
266
|
-
if (f.floatPosition && f.floatPosition.length === count * 3)
|
|
267
|
-
return;
|
|
268
|
-
f.floatPosition = new Float32Array(count * 3);
|
|
269
|
-
f.floatDiffuse = new Float32Array(count * 3);
|
|
270
|
-
f.floatSpecular = new Float32Array(count * 3);
|
|
271
|
-
f.floatDirection = new Float32Array(count * 3);
|
|
272
|
-
f.floatAttenuation = new Float32Array(count * 3);
|
|
273
|
-
f.floatCutoff = new Float32Array(count);
|
|
274
|
-
f.floatExponent = new Float32Array(count);
|
|
275
|
-
f.floatType = new Float32Array(count);
|
|
276
|
-
f.packedPosition = new Float32Array(count * 4);
|
|
277
|
-
f.packedDiffuse = new Float32Array(count * 4);
|
|
278
|
-
f.packedSpecular = new Float32Array(count * 4);
|
|
279
|
-
}
|
|
280
|
-
push(index, kind, light) {
|
|
281
|
-
const f = this.flatArrays;
|
|
282
|
-
const o = index * 3;
|
|
283
|
-
const isSpot = kind === 'spot';
|
|
284
|
-
const isDirectional = kind === 'directional';
|
|
285
|
-
const att = isDirectional ? null : light;
|
|
286
|
-
light.position.toArray(f.floatPosition, o);
|
|
287
|
-
light.diffuse.toArray(f.floatDiffuse, o);
|
|
288
|
-
light.specular.toArray(f.floatSpecular, o);
|
|
289
|
-
if (isDirectional)
|
|
290
|
-
light.direction.toArray(f.floatDirection, o);
|
|
291
|
-
else if (isSpot)
|
|
292
|
-
light.direction.toArray(f.floatDirection, o);
|
|
293
|
-
else
|
|
294
|
-
f.floatDirection.fill(0, o, o + 3);
|
|
295
|
-
const constant = att ? att.constantAttenuation : 1;
|
|
296
|
-
const linear = att ? att.linearAttenuation : 0;
|
|
297
|
-
const quadratic = att ? att.quadraticAttenuation : 0;
|
|
298
|
-
f.floatAttenuation[o] = constant;
|
|
299
|
-
f.floatAttenuation[o + 1] = linear;
|
|
300
|
-
f.floatAttenuation[o + 2] = quadratic;
|
|
301
|
-
f.floatCutoff[index] = isSpot ? light.cutoff : 0;
|
|
302
|
-
f.floatExponent[index] = isSpot ? light.exponent : 0;
|
|
303
|
-
f.floatType[index] = isDirectional ? 0 : isSpot ? 2 : 1;
|
|
304
|
-
const q = index * 4;
|
|
305
|
-
light.position.toArray(f.packedPosition, q);
|
|
306
|
-
f.packedPosition[q + 3] = f.floatType[index];
|
|
307
|
-
light.diffuse.toArray(f.packedDiffuse, q);
|
|
308
|
-
f.packedDiffuse[q + 3] = f.floatCutoff[index];
|
|
309
|
-
light.specular.toArray(f.packedSpecular, q);
|
|
310
|
-
f.packedSpecular[q + 3] = f.floatExponent[index];
|
|
311
|
-
const dir = isDirectional || isSpot ? light.direction : null;
|
|
312
|
-
f.position.push(light.position.x, light.position.y, light.position.z);
|
|
313
|
-
f.diffuse.push(light.diffuse.x, light.diffuse.y, light.diffuse.z);
|
|
314
|
-
f.specular.push(light.specular.x, light.specular.y, light.specular.z);
|
|
315
|
-
if (dir)
|
|
316
|
-
f.direction.push(dir.x, dir.y, dir.z);
|
|
317
|
-
else
|
|
318
|
-
f.direction.push(0, 0, 0);
|
|
319
|
-
f.constantAttenuation.push(constant);
|
|
320
|
-
f.linearAttenuation.push(linear);
|
|
321
|
-
f.quadraticAttenuation.push(quadratic);
|
|
322
|
-
f.cutoff.push(isSpot ? light.cutoff : 0);
|
|
323
|
-
f.exponent.push(isSpot ? light.exponent : 0);
|
|
324
|
-
f.type.push(kind);
|
|
325
|
-
}
|
|
326
362
|
flatten(extraAmbient) {
|
|
327
|
-
this.ClearFlatArrays();
|
|
328
363
|
const total = this.nbSpots + this.nbPoints + this.nbDirectionals;
|
|
329
|
-
const count = Math.min(total, MAX_LIGHTS);
|
|
330
364
|
if (total > MAX_LIGHTS && !this.warnedOverflow) {
|
|
331
365
|
this.warnedOverflow = true;
|
|
332
366
|
console.warn(`Lights: ${total} lights added, only the first ${MAX_LIGHTS} (spots, points, then directionals) are used.`);
|
|
333
367
|
}
|
|
334
|
-
this.
|
|
335
|
-
let index = 0;
|
|
336
|
-
for (let i = 0; i < this.nbSpots && index < count; i++)
|
|
337
|
-
this.push(index++, 'spot', this.spots[i]);
|
|
338
|
-
for (let i = 0; i < this.nbPoints && index < count; i++)
|
|
339
|
-
this.push(index++, 'point', this.points[i]);
|
|
340
|
-
for (let i = 0; i < this.nbDirectionals && index < count; i++)
|
|
341
|
-
this.push(index++, 'directional', this.directionals[i]);
|
|
342
|
-
const ambient = Lights._ambient.setScalar(0, 0, 0);
|
|
343
|
-
if (extraAmbient)
|
|
344
|
-
ambient.add(extraAmbient);
|
|
345
|
-
for (let i = 0; i < this.nbAmbients; i++)
|
|
346
|
-
ambient.addScaledVector(this.ambients[i].diffuse, this.ambients[i].intensity);
|
|
347
|
-
if (!this.flatArrays.floatAmbient)
|
|
348
|
-
this.flatArrays.floatAmbient = new Float32Array(3);
|
|
349
|
-
ambient.toArray(this.flatArrays.floatAmbient);
|
|
350
|
-
this.flatArrays.ambient = [ambient.x, ambient.y, ambient.z];
|
|
351
|
-
return this.flatArrays;
|
|
368
|
+
return packLights(this.directionals, this.points, this.spots, this.ambients, this.flatArrays, MAX_LIGHTS, extraAmbient);
|
|
352
369
|
}
|
|
353
370
|
clear() {
|
|
354
371
|
this.directionals = [];
|
|
@@ -359,7 +376,7 @@ class Lights {
|
|
|
359
376
|
this.nbPoints = 0;
|
|
360
377
|
this.nbSpots = 0;
|
|
361
378
|
this.nbAmbients = 0;
|
|
362
|
-
this.
|
|
379
|
+
clearFlatArrays(this.flatArrays);
|
|
363
380
|
}
|
|
364
381
|
getFlatArray(property) {
|
|
365
382
|
if (this.flatArrays.hasOwnProperty(property))
|
|
@@ -368,12 +385,70 @@ class Lights {
|
|
|
368
385
|
}
|
|
369
386
|
}
|
|
370
387
|
|
|
371
|
-
function
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
388
|
+
function raycastScene(meshes, traversalStamp, ray, precise = false) {
|
|
389
|
+
const hits = [];
|
|
390
|
+
const visit = (mesh) => {
|
|
391
|
+
if (!mesh.isActive())
|
|
392
|
+
return;
|
|
393
|
+
const hit = mesh.raycast(ray, precise);
|
|
394
|
+
if (hit)
|
|
395
|
+
hits.push({ mesh, distance: hit.distance, point: hit.point });
|
|
396
|
+
for (const child of mesh.children)
|
|
397
|
+
visit(child);
|
|
398
|
+
};
|
|
399
|
+
for (const mesh of meshes)
|
|
400
|
+
if (mesh.traversalStamp !== traversalStamp)
|
|
401
|
+
visit(mesh);
|
|
402
|
+
hits.sort((a, b) => a.distance - b.distance);
|
|
403
|
+
return hits;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
class SceneCollector {
|
|
407
|
+
opaqueList = [];
|
|
408
|
+
blendedList = [];
|
|
409
|
+
scratchFrustum = new Frustum();
|
|
410
|
+
scratchVec = new Vec3();
|
|
411
|
+
collect(meshes, camera, frustum, opaque, blended, stamp) {
|
|
412
|
+
this.opaqueList.length = 0;
|
|
413
|
+
this.blendedList.length = 0;
|
|
414
|
+
const visit = (mesh) => {
|
|
415
|
+
if (!mesh.isActive())
|
|
416
|
+
return;
|
|
417
|
+
const wanted = mesh.blendMode ? blended : opaque;
|
|
418
|
+
if (wanted && !(frustum && !frustum.intersectsSphere(mesh.computeBoundingSphere()))) {
|
|
419
|
+
if (mesh.blendMode) {
|
|
420
|
+
mesh.sortKey = mesh.getWorldPosition(this.scratchVec).getDistance(camera.position, true);
|
|
421
|
+
this.blendedList.push(mesh);
|
|
422
|
+
}
|
|
423
|
+
else
|
|
424
|
+
this.opaqueList.push(mesh);
|
|
425
|
+
}
|
|
426
|
+
for (const child of mesh.children)
|
|
427
|
+
visit(child);
|
|
428
|
+
};
|
|
429
|
+
for (const mesh of meshes)
|
|
430
|
+
if (mesh.traversalStamp !== stamp)
|
|
431
|
+
visit(mesh);
|
|
432
|
+
}
|
|
433
|
+
drawBlended(scene, camera, time, flatLights, frame) {
|
|
434
|
+
const list = this.blendedList;
|
|
435
|
+
if (list.length === 0)
|
|
436
|
+
return 0;
|
|
437
|
+
if (list.length > 1)
|
|
438
|
+
list.sort((a, b) => b.sortKey - a.sortKey);
|
|
439
|
+
const projMatrix = camera.getProjectionMatrix();
|
|
440
|
+
const viewMatrix = camera.getViewMatrix();
|
|
441
|
+
const camPos = camera.position;
|
|
442
|
+
for (const mesh of list) {
|
|
443
|
+
const blend = mesh.blendOptions;
|
|
444
|
+
scene.enableBlendMode(blend.equation, blend.source, blend.destination);
|
|
445
|
+
scene.renderer.setDepthWrite(mesh.depthWrite);
|
|
446
|
+
mesh.renderSelf(projMatrix, viewMatrix, flatLights, time, true, camPos, scene.fog, frame);
|
|
447
|
+
}
|
|
448
|
+
scene.disableBlendMode();
|
|
449
|
+
scene.renderer.setDepthWrite(true);
|
|
450
|
+
return list.length;
|
|
451
|
+
}
|
|
377
452
|
}
|
|
378
453
|
|
|
379
454
|
class Scene {
|
|
@@ -386,14 +461,11 @@ class Scene {
|
|
|
386
461
|
context;
|
|
387
462
|
renderer;
|
|
388
463
|
graph;
|
|
464
|
+
collector;
|
|
389
465
|
resolution;
|
|
390
|
-
scratchFrustum = new Frustum();
|
|
391
|
-
scratchVec = new Vec3();
|
|
392
466
|
frame = { id: 0 };
|
|
393
467
|
frameCounter = 0;
|
|
394
468
|
traversalStamp = 0;
|
|
395
|
-
opaqueList = [];
|
|
396
|
-
blendedList = [];
|
|
397
469
|
constructor(contextOrOptions) {
|
|
398
470
|
this.meshes = [];
|
|
399
471
|
this.nbMeshes = 0;
|
|
@@ -401,6 +473,7 @@ class Scene {
|
|
|
401
473
|
this.ambientColor = new Vec3(0, 0, 0);
|
|
402
474
|
this.fog = null;
|
|
403
475
|
this.enableFrustumCulling = false;
|
|
476
|
+
this.collector = new SceneCollector();
|
|
404
477
|
let ctx = null;
|
|
405
478
|
let opts = null;
|
|
406
479
|
if (contextOrOptions && typeof contextOrOptions.createBuffer === 'function')
|
|
@@ -427,15 +500,14 @@ class Scene {
|
|
|
427
500
|
this.renderer = new SceneRenderer(this.context);
|
|
428
501
|
this.graph = new SceneGraph();
|
|
429
502
|
if (opts && this.context) {
|
|
430
|
-
if (opts.viewport)
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
this.context.clearColor(c[0] ?? 0, c[1] ?? 0, c[2] ?? 0, c[3] ?? 1);
|
|
436
|
-
else if ('x' in c && 'y' in c && 'z' in c)
|
|
437
|
-
this.context.clearColor(c.x, c.y, c.z, 1.0);
|
|
503
|
+
if (opts.viewport) {
|
|
504
|
+
if (opts.viewport instanceof Vec2)
|
|
505
|
+
this.setViewport(opts.viewport);
|
|
506
|
+
else
|
|
507
|
+
this.setViewport(opts.viewport.size, opts.viewport.position);
|
|
438
508
|
}
|
|
509
|
+
if (opts.clearColor)
|
|
510
|
+
this.setClearColor(opts.clearColor);
|
|
439
511
|
if (opts.depthTest !== false && this.context.DEPTH_TEST) {
|
|
440
512
|
this.context.enable(this.context.DEPTH_TEST);
|
|
441
513
|
if (this.context.LEQUAL)
|
|
@@ -460,20 +532,32 @@ class Scene {
|
|
|
460
532
|
return null;
|
|
461
533
|
return (canvas.getContext('webgl') || canvas.getContext('experimental-webgl'));
|
|
462
534
|
}
|
|
463
|
-
setViewport(
|
|
464
|
-
|
|
535
|
+
setViewport(size, position) {
|
|
536
|
+
const x = position?.x ?? 0;
|
|
537
|
+
const y = position?.y ?? 0;
|
|
538
|
+
const w = size.x;
|
|
539
|
+
const h = size.y;
|
|
540
|
+
this.resolution.x = w;
|
|
541
|
+
this.resolution.y = h;
|
|
465
542
|
if (this.context && this.context.viewport)
|
|
466
|
-
this.context.viewport(x, y,
|
|
543
|
+
this.context.viewport(x, y, w, h);
|
|
544
|
+
}
|
|
545
|
+
getSize(target = new Vec2()) {
|
|
546
|
+
target.copy(this.resolution);
|
|
547
|
+
return target;
|
|
548
|
+
}
|
|
549
|
+
setClearColor(color, alpha = 1.0) {
|
|
550
|
+
if (!this.context)
|
|
551
|
+
return;
|
|
552
|
+
this.context.clearColor(Utils.clamp(color.x, 0, 1), Utils.clamp(color.y, 0, 1), Utils.clamp(color.z, 0, 1), Utils.clamp(alpha, 0, 1));
|
|
467
553
|
}
|
|
468
|
-
setSize(
|
|
469
|
-
const w = sizeOrWidth instanceof Vec2 ? sizeOrWidth.x : sizeOrWidth;
|
|
470
|
-
const h = sizeOrWidth instanceof Vec2 ? sizeOrWidth.y : (height ?? 0);
|
|
554
|
+
setSize(size) {
|
|
471
555
|
if (this.context && this.context.canvas) {
|
|
472
556
|
const canvas = this.context.canvas;
|
|
473
|
-
canvas.width =
|
|
474
|
-
canvas.height =
|
|
557
|
+
canvas.width = size.x;
|
|
558
|
+
canvas.height = size.y;
|
|
475
559
|
}
|
|
476
|
-
this.setViewport(
|
|
560
|
+
this.setViewport(size);
|
|
477
561
|
}
|
|
478
562
|
clear(color = true, depth = true) {
|
|
479
563
|
if (!this.context)
|
|
@@ -507,26 +591,16 @@ class Scene {
|
|
|
507
591
|
removeLight(light) {
|
|
508
592
|
return this.lights.removeLight(light);
|
|
509
593
|
}
|
|
510
|
-
setAmbientColor(
|
|
511
|
-
|
|
512
|
-
this.ambientColor.setScalar(rOrVec, g ?? 0, b ?? 0);
|
|
513
|
-
else
|
|
514
|
-
assignVec3(this.ambientColor, rOrVec);
|
|
594
|
+
setAmbientColor(color) {
|
|
595
|
+
this.ambientColor.copy(color);
|
|
515
596
|
return this;
|
|
516
597
|
}
|
|
517
|
-
setFog(color,
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
};
|
|
524
|
-
else
|
|
525
|
-
this.fog = {
|
|
526
|
-
color: assignVec3(new Vec3(), color),
|
|
527
|
-
near: nearOrG,
|
|
528
|
-
far: farOrB ?? 1000
|
|
529
|
-
};
|
|
598
|
+
setFog(color, near, far = 1000) {
|
|
599
|
+
this.fog = {
|
|
600
|
+
color: new Vec3(color.x, color.y, color.z),
|
|
601
|
+
near,
|
|
602
|
+
far
|
|
603
|
+
};
|
|
530
604
|
return this;
|
|
531
605
|
}
|
|
532
606
|
clearFog() {
|
|
@@ -540,6 +614,11 @@ class Scene {
|
|
|
540
614
|
clearLights() {
|
|
541
615
|
this.lights.clear();
|
|
542
616
|
}
|
|
617
|
+
restore() {
|
|
618
|
+
this.renderer.reset();
|
|
619
|
+
for (const mesh of this.meshes)
|
|
620
|
+
mesh.restore();
|
|
621
|
+
}
|
|
543
622
|
destroy(destroyMeshes = true) {
|
|
544
623
|
if (destroyMeshes)
|
|
545
624
|
for (const mesh of this.meshes)
|
|
@@ -562,41 +641,27 @@ class Scene {
|
|
|
562
641
|
}
|
|
563
642
|
raycast(ray, precise = false) {
|
|
564
643
|
this.computeWorldMatrices();
|
|
565
|
-
|
|
566
|
-
const visit = (mesh) => {
|
|
567
|
-
if (!mesh.isActive())
|
|
568
|
-
return;
|
|
569
|
-
const hit = mesh.raycast(ray, precise);
|
|
570
|
-
if (hit)
|
|
571
|
-
hits.push({ mesh, distance: hit.distance, point: hit.point });
|
|
572
|
-
for (const child of mesh.children)
|
|
573
|
-
visit(child);
|
|
574
|
-
};
|
|
575
|
-
for (const mesh of this.meshes)
|
|
576
|
-
if (mesh.traversalStamp !== this.traversalStamp)
|
|
577
|
-
visit(mesh);
|
|
578
|
-
hits.sort((a, b) => a.distance - b.distance);
|
|
579
|
-
return hits;
|
|
644
|
+
return raycastScene(this.meshes, this.traversalStamp, ray, precise);
|
|
580
645
|
}
|
|
581
646
|
render(camera, time = Time.now()) {
|
|
582
647
|
this.computeWorldMatrices();
|
|
583
648
|
const flatLights = this.lights.flatten(this.ambientColor);
|
|
584
649
|
const frustum = this.enableFrustumCulling
|
|
585
|
-
? this.scratchFrustum.setFromProjectionViewMatrix(camera.getProjectionViewMatrix())
|
|
650
|
+
? this.collector.scratchFrustum.setFromProjectionViewMatrix(camera.getProjectionViewMatrix())
|
|
586
651
|
: null;
|
|
587
|
-
this.collect(camera, frustum, true, true, this.traversalStamp);
|
|
652
|
+
this.collector.collect(this.meshes, camera, frustum, true, true, this.traversalStamp);
|
|
588
653
|
const frame = this.beginFrame();
|
|
589
654
|
this.disableBlendMode();
|
|
590
655
|
const projMatrix = camera.getProjectionMatrix();
|
|
591
656
|
const viewMatrix = camera.getViewMatrix();
|
|
592
657
|
const camPos = camera.position;
|
|
593
658
|
let renderedCount = 0;
|
|
594
|
-
for (const mesh of this.opaqueList) {
|
|
659
|
+
for (const mesh of this.collector.opaqueList) {
|
|
595
660
|
this.renderer.setDepthWrite(mesh.depthWrite);
|
|
596
661
|
mesh.renderSelf(projMatrix, viewMatrix, flatLights, time, false, camPos, this.fog, frame);
|
|
597
662
|
renderedCount++;
|
|
598
663
|
}
|
|
599
|
-
renderedCount += this.drawBlended(camera, time, flatLights, frame);
|
|
664
|
+
renderedCount += this.collector.drawBlended(this, camera, time, flatLights, frame);
|
|
600
665
|
this.renderer.setDepthWrite(true);
|
|
601
666
|
return renderedCount;
|
|
602
667
|
}
|
|
@@ -624,52 +689,105 @@ class Scene {
|
|
|
624
689
|
this.frame.resolution = this.resolution;
|
|
625
690
|
return this.frame;
|
|
626
691
|
}
|
|
627
|
-
|
|
628
|
-
this.
|
|
629
|
-
this.
|
|
630
|
-
const visit = (mesh) => {
|
|
631
|
-
if (!mesh.isActive())
|
|
632
|
-
return;
|
|
633
|
-
const wanted = mesh.blendMode ? blended : opaque;
|
|
634
|
-
if (wanted && !(frustum && !frustum.intersectsSphere(mesh.computeBoundingSphere()))) {
|
|
635
|
-
if (mesh.blendMode) {
|
|
636
|
-
mesh.sortKey = mesh.getWorldPosition(this.scratchVec).getDistance(camera.position, true);
|
|
637
|
-
this.blendedList.push(mesh);
|
|
638
|
-
}
|
|
639
|
-
else
|
|
640
|
-
this.opaqueList.push(mesh);
|
|
641
|
-
}
|
|
642
|
-
for (const child of mesh.children)
|
|
643
|
-
visit(child);
|
|
644
|
-
};
|
|
645
|
-
for (const mesh of this.meshes)
|
|
646
|
-
if (mesh.traversalStamp !== stamp)
|
|
647
|
-
visit(mesh);
|
|
692
|
+
renderBlended(camera, time = Time.now(), flatLights = this.lights.flatten(this.ambientColor), frustum = null) {
|
|
693
|
+
this.collector.collect(this.meshes, camera, frustum, false, true, this.markDescendants());
|
|
694
|
+
return this.collector.drawBlended(this, camera, time, flatLights, this.beginFrame());
|
|
648
695
|
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
mesh.renderSelf(projMatrix, viewMatrix, flatLights, time, true, camPos, this.fog, frame);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
const Shader = {
|
|
699
|
+
create(context, str, type) {
|
|
700
|
+
const shader = context.createShader(context[type]);
|
|
701
|
+
if (!shader)
|
|
702
|
+
return null;
|
|
703
|
+
context.shaderSource(shader, str);
|
|
704
|
+
context.compileShader(shader);
|
|
705
|
+
if (!context.getShaderParameter(shader, context.COMPILE_STATUS)) {
|
|
706
|
+
console.error(`${type} compilation failed:`, context.getShaderInfoLog(shader));
|
|
707
|
+
context.deleteShader(shader);
|
|
708
|
+
return null;
|
|
663
709
|
}
|
|
664
|
-
|
|
665
|
-
this.renderer.setDepthWrite(true);
|
|
666
|
-
return list.length;
|
|
710
|
+
return shader;
|
|
667
711
|
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
712
|
+
};
|
|
713
|
+
|
|
714
|
+
const caches = new WeakMap();
|
|
715
|
+
const owners = new WeakMap();
|
|
716
|
+
const Program = {
|
|
717
|
+
create(context, vertexShader, fragmentShader) {
|
|
718
|
+
const program = context.createProgram();
|
|
719
|
+
if (program) {
|
|
720
|
+
const vshader = Shader.create(context, vertexShader, 'VERTEX_SHADER');
|
|
721
|
+
const fshader = Shader.create(context, fragmentShader, 'FRAGMENT_SHADER');
|
|
722
|
+
if (!vshader || !fshader) {
|
|
723
|
+
if (vshader)
|
|
724
|
+
context.deleteShader(vshader);
|
|
725
|
+
if (fshader)
|
|
726
|
+
context.deleteShader(fshader);
|
|
727
|
+
context.deleteProgram(program);
|
|
728
|
+
return null;
|
|
729
|
+
}
|
|
730
|
+
context.attachShader(program, vshader);
|
|
731
|
+
context.attachShader(program, fshader);
|
|
732
|
+
context.linkProgram(program);
|
|
733
|
+
if (!context.getProgramParameter(program, context.LINK_STATUS)) {
|
|
734
|
+
console.error('Program link failed:', context.getProgramInfoLog(program));
|
|
735
|
+
context.deleteShader(vshader);
|
|
736
|
+
context.deleteShader(fshader);
|
|
737
|
+
context.deleteProgram(program);
|
|
738
|
+
return null;
|
|
739
|
+
}
|
|
740
|
+
context.detachShader(program, vshader);
|
|
741
|
+
context.detachShader(program, fshader);
|
|
742
|
+
context.deleteShader(vshader);
|
|
743
|
+
context.deleteShader(fshader);
|
|
744
|
+
}
|
|
745
|
+
return program;
|
|
746
|
+
},
|
|
747
|
+
acquire(context, vertexShader, fragmentShader) {
|
|
748
|
+
let cache = caches.get(context);
|
|
749
|
+
if (!cache) {
|
|
750
|
+
cache = new Map();
|
|
751
|
+
caches.set(context, cache);
|
|
752
|
+
}
|
|
753
|
+
const key = `${vertexShader}\u0000${fragmentShader}`;
|
|
754
|
+
let entry = cache.get(key);
|
|
755
|
+
if (!entry) {
|
|
756
|
+
const program = Program.create(context, vertexShader, fragmentShader);
|
|
757
|
+
if (!program)
|
|
758
|
+
return null;
|
|
759
|
+
entry = { program, key, refs: 0 };
|
|
760
|
+
cache.set(key, entry);
|
|
761
|
+
owners.set(program, entry);
|
|
762
|
+
}
|
|
763
|
+
entry.refs++;
|
|
764
|
+
return entry.program;
|
|
765
|
+
},
|
|
766
|
+
release(context, program) {
|
|
767
|
+
const entry = owners.get(program);
|
|
768
|
+
if (!entry) {
|
|
769
|
+
context.deleteProgram(program);
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
if (--entry.refs > 0)
|
|
773
|
+
return;
|
|
774
|
+
const cache = caches.get(context);
|
|
775
|
+
if (cache?.get(entry.key) === entry)
|
|
776
|
+
cache.delete(entry.key);
|
|
777
|
+
owners.delete(program);
|
|
778
|
+
context.deleteProgram(program);
|
|
779
|
+
},
|
|
780
|
+
sources(program) {
|
|
781
|
+
const entry = owners.get(program);
|
|
782
|
+
if (!entry)
|
|
783
|
+
return null;
|
|
784
|
+
const [vertex, fragment] = entry.key.split('\u0000');
|
|
785
|
+
return { vertex, fragment };
|
|
786
|
+
},
|
|
787
|
+
reset(context) {
|
|
788
|
+
caches.delete(context);
|
|
671
789
|
}
|
|
672
|
-
}
|
|
790
|
+
};
|
|
673
791
|
|
|
674
792
|
class Renderer {
|
|
675
793
|
canvas;
|
|
@@ -687,14 +805,19 @@ class Renderer {
|
|
|
687
805
|
};
|
|
688
806
|
this.onContextRestoredBound = () => {
|
|
689
807
|
this.isContextLost = false;
|
|
808
|
+
if (this.context) {
|
|
809
|
+
Program.reset(this.context);
|
|
810
|
+
getGLState(this.context).reset();
|
|
811
|
+
}
|
|
690
812
|
this.defaultSettings();
|
|
691
813
|
this.options.onContextRestored?.();
|
|
692
814
|
};
|
|
693
815
|
this.canvas = typeof canvasID === 'string' ? this.findById(canvasID) : canvasID;
|
|
694
816
|
this.context = null;
|
|
695
817
|
if (this.canvas) {
|
|
696
|
-
|
|
697
|
-
this.canvas.
|
|
818
|
+
const size = options.size ?? new Vec2(1280, 720);
|
|
819
|
+
this.canvas.width = size.x;
|
|
820
|
+
this.canvas.height = size.y;
|
|
698
821
|
const attributes = { alpha: false, ...options.contextAttributes };
|
|
699
822
|
this.context = (this.canvas.getContext("webgl", attributes) ||
|
|
700
823
|
this.canvas.getContext("experimental-webgl", attributes)) || null;
|
|
@@ -720,8 +843,8 @@ class Renderer {
|
|
|
720
843
|
this.setFrontFace('CW');
|
|
721
844
|
this.enable('CULL_FACE');
|
|
722
845
|
this.setCullFace('BACK');
|
|
723
|
-
this.setViewport(this.context.drawingBufferWidth, this.context.drawingBufferHeight);
|
|
724
|
-
this.setClearColor(0.0, 0.0, 0.0, 1.0);
|
|
846
|
+
this.setViewport(new Vec2(this.context.drawingBufferWidth, this.context.drawingBufferHeight));
|
|
847
|
+
this.setClearColor(new Vec3(0.0, 0.0, 0.0), 1.0);
|
|
725
848
|
}
|
|
726
849
|
setFrontFace(mode) {
|
|
727
850
|
if (this.context)
|
|
@@ -739,22 +862,14 @@ class Renderer {
|
|
|
739
862
|
if (this.context)
|
|
740
863
|
this.context.cullFace(this.context[mode]);
|
|
741
864
|
}
|
|
742
|
-
setViewport(
|
|
865
|
+
setViewport(size) {
|
|
743
866
|
if (this.context)
|
|
744
|
-
this.context.viewport(0, 0,
|
|
867
|
+
this.context.viewport(0, 0, size.x, size.y);
|
|
745
868
|
}
|
|
746
|
-
setClearColor(
|
|
747
|
-
if (this.context)
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
this.context.clearColor(Utils.clamp(rOrVec.x, 0, 1), Utils.clamp(rOrVec.y, 0, 1), Utils.clamp(rOrVec.z, 0, 1), alpha);
|
|
751
|
-
}
|
|
752
|
-
else {
|
|
753
|
-
const green = g !== undefined ? Utils.clamp(g, 0, 1) : 0;
|
|
754
|
-
const blue = b !== undefined ? Utils.clamp(b, 0, 1) : 0;
|
|
755
|
-
const alpha = a !== undefined ? Utils.clamp(a, 0, 1) : 1.0;
|
|
756
|
-
this.context.clearColor(Utils.clamp(rOrVec, 0, 1), green, blue, alpha);
|
|
757
|
-
}
|
|
869
|
+
setClearColor(color, alpha = 1.0) {
|
|
870
|
+
if (!this.context)
|
|
871
|
+
return;
|
|
872
|
+
this.context.clearColor(Utils.clamp(color.x, 0, 1), Utils.clamp(color.y, 0, 1), Utils.clamp(color.z, 0, 1), Utils.clamp(alpha, 0, 1));
|
|
758
873
|
}
|
|
759
874
|
clearFrame() {
|
|
760
875
|
if (this.context)
|
|
@@ -766,14 +881,19 @@ class Renderer {
|
|
|
766
881
|
getCanvas() {
|
|
767
882
|
return this.canvas;
|
|
768
883
|
}
|
|
769
|
-
setSize(
|
|
770
|
-
|
|
771
|
-
|
|
884
|
+
setSize(size) {
|
|
885
|
+
if (this.canvas) {
|
|
886
|
+
this.canvas.width = size.x;
|
|
887
|
+
this.canvas.height = size.y;
|
|
888
|
+
}
|
|
889
|
+
this.setViewport(size);
|
|
890
|
+
}
|
|
891
|
+
getSize(target = new Vec2()) {
|
|
772
892
|
if (this.canvas) {
|
|
773
|
-
this.canvas.width
|
|
774
|
-
this.canvas.height
|
|
893
|
+
target.x = this.canvas.width;
|
|
894
|
+
target.y = this.canvas.height;
|
|
775
895
|
}
|
|
776
|
-
|
|
896
|
+
return target;
|
|
777
897
|
}
|
|
778
898
|
findById(id) {
|
|
779
899
|
return document.getElementById(id);
|
|
@@ -787,22 +907,18 @@ class RenderTarget {
|
|
|
787
907
|
framebuffer;
|
|
788
908
|
texture;
|
|
789
909
|
depthBuffer;
|
|
790
|
-
constructor(
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
this.context = heightOrContext;
|
|
795
|
-
}
|
|
796
|
-
else {
|
|
797
|
-
this.width = sizeOrWidth;
|
|
798
|
-
this.height = heightOrContext;
|
|
799
|
-
this.context = context;
|
|
800
|
-
}
|
|
910
|
+
constructor(size, context) {
|
|
911
|
+
this.width = size.x;
|
|
912
|
+
this.height = size.y;
|
|
913
|
+
this.context = context;
|
|
801
914
|
this.framebuffer = null;
|
|
802
915
|
this.texture = null;
|
|
803
916
|
this.depthBuffer = null;
|
|
804
917
|
this.init();
|
|
805
918
|
}
|
|
919
|
+
restore() {
|
|
920
|
+
this.init();
|
|
921
|
+
}
|
|
806
922
|
init() {
|
|
807
923
|
const gl = this.context;
|
|
808
924
|
this.framebuffer = gl.createFramebuffer();
|
|
@@ -828,20 +944,20 @@ class RenderTarget {
|
|
|
828
944
|
gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
|
|
829
945
|
gl.viewport(0, 0, this.width, this.height);
|
|
830
946
|
}
|
|
831
|
-
unbind(
|
|
947
|
+
unbind(viewportSize) {
|
|
832
948
|
const gl = this.context;
|
|
833
949
|
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
834
|
-
if (
|
|
835
|
-
gl.viewport(0, 0,
|
|
836
|
-
else if (viewportWidthOrSize !== undefined && viewportHeight !== undefined)
|
|
837
|
-
gl.viewport(0, 0, viewportWidthOrSize, viewportHeight);
|
|
950
|
+
if (viewportSize)
|
|
951
|
+
gl.viewport(0, 0, viewportSize.x, viewportSize.y);
|
|
838
952
|
}
|
|
839
953
|
getSize(target = new Vec2()) {
|
|
840
|
-
|
|
954
|
+
target.x = this.width;
|
|
955
|
+
target.y = this.height;
|
|
956
|
+
return target;
|
|
841
957
|
}
|
|
842
|
-
setSize(
|
|
843
|
-
const w =
|
|
844
|
-
const h =
|
|
958
|
+
setSize(size) {
|
|
959
|
+
const w = size.x;
|
|
960
|
+
const h = size.y;
|
|
845
961
|
if (this.width === w && this.height === h)
|
|
846
962
|
return;
|
|
847
963
|
this.width = w;
|
|
@@ -932,25 +1048,16 @@ class Camera {
|
|
|
932
1048
|
}
|
|
933
1049
|
return this.inverseProjectionViewMatrix;
|
|
934
1050
|
}
|
|
935
|
-
setPosition(
|
|
936
|
-
|
|
937
|
-
this.position.copy(xOrVec);
|
|
938
|
-
else
|
|
939
|
-
this.position.setScalar(xOrVec, y, z);
|
|
1051
|
+
setPosition(position) {
|
|
1052
|
+
this.position.copy(position);
|
|
940
1053
|
this.setViewMatrix();
|
|
941
1054
|
}
|
|
942
|
-
setTarget(
|
|
943
|
-
|
|
944
|
-
this.target.copy(xOrVec);
|
|
945
|
-
else
|
|
946
|
-
this.target.setScalar(xOrVec, y, z);
|
|
1055
|
+
setTarget(target) {
|
|
1056
|
+
this.target.copy(target);
|
|
947
1057
|
this.setViewMatrix();
|
|
948
1058
|
}
|
|
949
|
-
setUp(
|
|
950
|
-
|
|
951
|
-
this.up.copy(xOrVec);
|
|
952
|
-
else
|
|
953
|
-
this.up.setScalar(xOrVec, y, z);
|
|
1059
|
+
setUp(up) {
|
|
1060
|
+
this.up.copy(up);
|
|
954
1061
|
this.setViewMatrix();
|
|
955
1062
|
}
|
|
956
1063
|
lookAt(target) {
|
|
@@ -978,26 +1085,13 @@ class Camera {
|
|
|
978
1085
|
getProjectionMatrixObject() {
|
|
979
1086
|
return this.projectionMatrix;
|
|
980
1087
|
}
|
|
981
|
-
getRay(
|
|
982
|
-
let ndcX;
|
|
983
|
-
let ndcY;
|
|
984
|
-
let outRay = targetRay;
|
|
985
|
-
if (ndcOrX instanceof Vec2) {
|
|
986
|
-
ndcX = ndcOrX.x;
|
|
987
|
-
ndcY = ndcOrX.y;
|
|
988
|
-
if (ndcYOrTarget instanceof Ray3)
|
|
989
|
-
outRay = ndcYOrTarget;
|
|
990
|
-
}
|
|
991
|
-
else {
|
|
992
|
-
ndcX = ndcOrX;
|
|
993
|
-
ndcY = typeof ndcYOrTarget === 'number' ? ndcYOrTarget : 0;
|
|
994
|
-
}
|
|
1088
|
+
getRay(ndc, targetRay = new Ray3()) {
|
|
995
1089
|
const inverse = this.getInverseProjectionViewMatrix();
|
|
996
|
-
const near = inverse.transformPoint(Camera._scratchNdc.setScalar(
|
|
997
|
-
const far = inverse.transformPoint(Camera._scratchNdc.setScalar(
|
|
1090
|
+
const near = inverse.transformPoint(Camera._scratchNdc.setScalar(ndc.x, ndc.y, -1), Camera._scratchNear);
|
|
1091
|
+
const far = inverse.transformPoint(Camera._scratchNdc.setScalar(ndc.x, ndc.y, 1.0), Camera._scratchFar);
|
|
998
1092
|
const dir = Camera._scratchDir.subVectors(far, near).normalize();
|
|
999
|
-
|
|
1000
|
-
return
|
|
1093
|
+
targetRay.set(near, dir);
|
|
1094
|
+
return targetRay;
|
|
1001
1095
|
}
|
|
1002
1096
|
}
|
|
1003
1097
|
|
|
@@ -1006,35 +1100,24 @@ class PerspectiveCamera extends Camera {
|
|
|
1006
1100
|
ratio;
|
|
1007
1101
|
zNear;
|
|
1008
1102
|
zFar;
|
|
1009
|
-
constructor(fov,
|
|
1103
|
+
constructor(fov, nearFar = new Vec2(0.1, 1000), context) {
|
|
1010
1104
|
super(new Vec3(), new Vec3(), new Vec3(0.0, 1.0, 0.0));
|
|
1011
|
-
let zNear = 0.1;
|
|
1012
|
-
let zFar = 1000;
|
|
1013
|
-
let context = null;
|
|
1014
|
-
if (zNearOrContext && typeof zNearOrContext !== 'number') {
|
|
1015
|
-
context = zNearOrContext;
|
|
1016
|
-
zNear = typeof zFarOrNear === 'number' ? zFarOrNear : 0.1;
|
|
1017
|
-
zFar = typeof contextOrFar === 'number' ? contextOrFar : 1000;
|
|
1018
|
-
}
|
|
1019
|
-
else {
|
|
1020
|
-
zNear = typeof zNearOrContext === 'number' ? zNearOrContext : 0.1;
|
|
1021
|
-
zFar = typeof zFarOrNear === 'number' ? zFarOrNear : 1000;
|
|
1022
|
-
if (contextOrFar && typeof contextOrFar !== 'number')
|
|
1023
|
-
context = contextOrFar;
|
|
1024
|
-
}
|
|
1025
1105
|
this.fov = fov;
|
|
1026
1106
|
this.ratio = 0;
|
|
1027
|
-
this.zNear =
|
|
1028
|
-
this.zFar =
|
|
1029
|
-
|
|
1107
|
+
this.zNear = nearFar.x;
|
|
1108
|
+
this.zFar = nearFar.y;
|
|
1109
|
+
let viewport = null;
|
|
1110
|
+
if (context && typeof context.getParameter === 'function') {
|
|
1111
|
+
const vp = context.getParameter(context.VIEWPORT);
|
|
1112
|
+
if (vp && vp.length >= 4)
|
|
1113
|
+
viewport = new Vec2(vp[2], vp[3]);
|
|
1114
|
+
}
|
|
1030
1115
|
this.setProjectionMatrix(viewport);
|
|
1031
1116
|
super.setViewMatrix();
|
|
1032
1117
|
}
|
|
1033
1118
|
setProjectionMatrix(viewport) {
|
|
1034
|
-
if (viewport
|
|
1119
|
+
if (viewport)
|
|
1035
1120
|
this.ratio = viewport.x / Math.max(1, viewport.y);
|
|
1036
|
-
else if (viewport && viewport.length >= 4)
|
|
1037
|
-
this.ratio = viewport[2] / Math.max(1, viewport[3]);
|
|
1038
1121
|
else
|
|
1039
1122
|
this.ratio = this.ratio || 1;
|
|
1040
1123
|
this.projectionMatrix.perspective(this.fov, this.ratio, this.zNear, this.zFar);
|
|
@@ -1044,11 +1127,16 @@ class PerspectiveCamera extends Camera {
|
|
|
1044
1127
|
this.fov = fov;
|
|
1045
1128
|
this.setProjectionMatrix();
|
|
1046
1129
|
}
|
|
1047
|
-
setNearFar(
|
|
1048
|
-
this.zNear =
|
|
1049
|
-
this.zFar =
|
|
1130
|
+
setNearFar(nearFar) {
|
|
1131
|
+
this.zNear = nearFar.x;
|
|
1132
|
+
this.zFar = nearFar.y;
|
|
1050
1133
|
this.setProjectionMatrix();
|
|
1051
1134
|
}
|
|
1135
|
+
getNearFar(target = new Vec2()) {
|
|
1136
|
+
target.x = this.zNear;
|
|
1137
|
+
target.y = this.zFar;
|
|
1138
|
+
return target;
|
|
1139
|
+
}
|
|
1052
1140
|
}
|
|
1053
1141
|
|
|
1054
1142
|
class OrthographicCamera extends Camera {
|
|
@@ -1058,24 +1146,14 @@ class OrthographicCamera extends Camera {
|
|
|
1058
1146
|
bottom;
|
|
1059
1147
|
near;
|
|
1060
1148
|
far;
|
|
1061
|
-
constructor(
|
|
1149
|
+
constructor(bounds, nearFar = new Vec2(-1e3, 1000)) {
|
|
1062
1150
|
super(new Vec3(), new Vec3(), new Vec3(0.0, 1.0, 0.0));
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
this.far = topOrFar !== undefined ? topOrFar : 1000;
|
|
1070
|
-
}
|
|
1071
|
-
else {
|
|
1072
|
-
this.left = boundsOrLeft;
|
|
1073
|
-
this.right = rightOrNear;
|
|
1074
|
-
this.top = topOrFar;
|
|
1075
|
-
this.bottom = bottom;
|
|
1076
|
-
this.near = near !== undefined ? near : -1e3;
|
|
1077
|
-
this.far = far !== undefined ? far : 1000;
|
|
1078
|
-
}
|
|
1151
|
+
this.left = bounds.topLeftCorner.x;
|
|
1152
|
+
this.right = bounds.bottomRightCorner.x;
|
|
1153
|
+
this.bottom = bounds.topLeftCorner.y;
|
|
1154
|
+
this.top = bounds.bottomRightCorner.y;
|
|
1155
|
+
this.near = nearFar.x;
|
|
1156
|
+
this.far = nearFar.y;
|
|
1079
1157
|
this.setProjectionMatrix();
|
|
1080
1158
|
super.setViewMatrix();
|
|
1081
1159
|
}
|
|
@@ -1083,29 +1161,22 @@ class OrthographicCamera extends Camera {
|
|
|
1083
1161
|
this.projectionMatrix.orthographic(this.left, this.right, this.top, this.bottom, this.near, this.far);
|
|
1084
1162
|
super.setProjectionMatrix();
|
|
1085
1163
|
}
|
|
1086
|
-
setBounds(
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
if (topOrFar !== undefined)
|
|
1095
|
-
this.far = topOrFar;
|
|
1096
|
-
}
|
|
1097
|
-
else {
|
|
1098
|
-
this.left = boundsOrLeft;
|
|
1099
|
-
this.right = rightOrNear;
|
|
1100
|
-
this.top = topOrFar;
|
|
1101
|
-
this.bottom = bottom;
|
|
1102
|
-
if (near !== undefined)
|
|
1103
|
-
this.near = near;
|
|
1104
|
-
if (far !== undefined)
|
|
1105
|
-
this.far = far;
|
|
1164
|
+
setBounds(bounds, nearFar) {
|
|
1165
|
+
this.left = bounds.topLeftCorner.x;
|
|
1166
|
+
this.right = bounds.bottomRightCorner.x;
|
|
1167
|
+
this.bottom = bounds.topLeftCorner.y;
|
|
1168
|
+
this.top = bounds.bottomRightCorner.y;
|
|
1169
|
+
if (nearFar) {
|
|
1170
|
+
this.near = nearFar.x;
|
|
1171
|
+
this.far = nearFar.y;
|
|
1106
1172
|
}
|
|
1107
1173
|
this.setProjectionMatrix();
|
|
1108
1174
|
}
|
|
1175
|
+
getNearFar(target = new Vec2()) {
|
|
1176
|
+
target.x = this.near;
|
|
1177
|
+
target.y = this.far;
|
|
1178
|
+
return target;
|
|
1179
|
+
}
|
|
1109
1180
|
}
|
|
1110
1181
|
|
|
1111
1182
|
class OrbitControls {
|
|
@@ -1326,88 +1397,273 @@ class OrbitControls {
|
|
|
1326
1397
|
}
|
|
1327
1398
|
}
|
|
1328
1399
|
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1400
|
+
class Node {
|
|
1401
|
+
position;
|
|
1402
|
+
scale;
|
|
1403
|
+
quaternion;
|
|
1404
|
+
modelMatrix;
|
|
1405
|
+
worldMatrix;
|
|
1406
|
+
normalMatrix;
|
|
1407
|
+
boundingBox;
|
|
1408
|
+
boundingSphere;
|
|
1409
|
+
localBoundingBox;
|
|
1410
|
+
localBoundingSphere;
|
|
1411
|
+
parent = null;
|
|
1412
|
+
children = [];
|
|
1413
|
+
traversalStamp = 0;
|
|
1414
|
+
isBoundsDirty = true;
|
|
1415
|
+
isNormalMatrixDirty = true;
|
|
1416
|
+
isWorldDirty = true;
|
|
1417
|
+
_rotation;
|
|
1418
|
+
isRotationStale = false;
|
|
1419
|
+
isTransformDirty = false;
|
|
1420
|
+
static UP = new Vec3(0, 1, 0);
|
|
1421
|
+
static scratchQuat = new Quat();
|
|
1422
|
+
static scratchVec = new Vec3();
|
|
1423
|
+
constructor() {
|
|
1424
|
+
this.position = new Vec3();
|
|
1425
|
+
this._rotation = new Vec3();
|
|
1426
|
+
this.scale = new Vec3(1.0, 1.0, 1.0);
|
|
1427
|
+
this.quaternion = new Quat();
|
|
1428
|
+
this.modelMatrix = new Mat4x3();
|
|
1429
|
+
this.worldMatrix = new Mat4x3();
|
|
1430
|
+
this.normalMatrix = new Mat3();
|
|
1431
|
+
this.modelMatrix.identity();
|
|
1432
|
+
this.worldMatrix.identity();
|
|
1433
|
+
this.normalMatrix.identity();
|
|
1434
|
+
this.localBoundingBox = new Box();
|
|
1435
|
+
this.localBoundingSphere = new Sphere$1();
|
|
1436
|
+
this.boundingBox = new Box();
|
|
1437
|
+
this.boundingSphere = new Sphere$1();
|
|
1438
|
+
}
|
|
1439
|
+
addChild(node) {
|
|
1440
|
+
if (this.children.indexOf(node) === -1) {
|
|
1441
|
+
if (node.parent && node.parent !== this)
|
|
1442
|
+
node.parent.removeChild(node);
|
|
1443
|
+
this.children.push(node);
|
|
1444
|
+
node.parent = this;
|
|
1445
|
+
node.isWorldDirty = true;
|
|
1375
1446
|
}
|
|
1376
|
-
return program;
|
|
1377
1447
|
}
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
if (
|
|
1381
|
-
|
|
1382
|
-
|
|
1448
|
+
removeChild(node) {
|
|
1449
|
+
const index = this.children.indexOf(node);
|
|
1450
|
+
if (index !== -1) {
|
|
1451
|
+
this.children.splice(index, 1);
|
|
1452
|
+
node.parent = null;
|
|
1453
|
+
node.isWorldDirty = true;
|
|
1454
|
+
return true;
|
|
1383
1455
|
}
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1456
|
+
return false;
|
|
1457
|
+
}
|
|
1458
|
+
markDirty() {
|
|
1459
|
+
this.isTransformDirty = true;
|
|
1460
|
+
this.isBoundsDirty = true;
|
|
1461
|
+
this.isNormalMatrixDirty = true;
|
|
1462
|
+
}
|
|
1463
|
+
get rotation() {
|
|
1464
|
+
if (this.isRotationStale) {
|
|
1465
|
+
this.quaternion.getEuler(this._rotation);
|
|
1466
|
+
this.isRotationStale = false;
|
|
1393
1467
|
}
|
|
1394
|
-
|
|
1395
|
-
return entry.program;
|
|
1468
|
+
return this._rotation;
|
|
1396
1469
|
}
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1470
|
+
set rotation(euler) {
|
|
1471
|
+
this.setRotation(euler);
|
|
1472
|
+
}
|
|
1473
|
+
rotationChanged() {
|
|
1474
|
+
this.isRotationStale = true;
|
|
1475
|
+
this.markDirty();
|
|
1476
|
+
}
|
|
1477
|
+
setPosition(position) {
|
|
1478
|
+
this.position.copy(position);
|
|
1479
|
+
this.markDirty();
|
|
1480
|
+
return this;
|
|
1481
|
+
}
|
|
1482
|
+
setRotation(rotation) {
|
|
1483
|
+
this._rotation.copy(rotation);
|
|
1484
|
+
this.quaternion.setFromEuler(this._rotation);
|
|
1485
|
+
this.isRotationStale = false;
|
|
1486
|
+
this.markDirty();
|
|
1487
|
+
return this;
|
|
1488
|
+
}
|
|
1489
|
+
setScale(scale) {
|
|
1490
|
+
this.scale.copy(scale);
|
|
1491
|
+
this.markDirty();
|
|
1492
|
+
return this;
|
|
1493
|
+
}
|
|
1494
|
+
scaleBy(factor) {
|
|
1495
|
+
this.scale.multiply(factor);
|
|
1496
|
+
this.markDirty();
|
|
1497
|
+
return this;
|
|
1498
|
+
}
|
|
1499
|
+
setQuaternion(q) {
|
|
1500
|
+
this.quaternion.copy(q);
|
|
1501
|
+
this.rotationChanged();
|
|
1502
|
+
return this;
|
|
1503
|
+
}
|
|
1504
|
+
slerp(targetQuat, t) {
|
|
1505
|
+
this.quaternion.slerp(targetQuat, t);
|
|
1506
|
+
this.rotationChanged();
|
|
1507
|
+
return this;
|
|
1508
|
+
}
|
|
1509
|
+
rotateX(angle) {
|
|
1510
|
+
this.quaternion.rotateX(angle);
|
|
1511
|
+
this.rotationChanged();
|
|
1512
|
+
return this;
|
|
1513
|
+
}
|
|
1514
|
+
rotateY(angle) {
|
|
1515
|
+
this.quaternion.rotateY(angle);
|
|
1516
|
+
this.rotationChanged();
|
|
1517
|
+
return this;
|
|
1518
|
+
}
|
|
1519
|
+
rotateZ(angle) {
|
|
1520
|
+
this.quaternion.rotateZ(angle);
|
|
1521
|
+
this.rotationChanged();
|
|
1522
|
+
return this;
|
|
1523
|
+
}
|
|
1524
|
+
rotateOnAxis(axis, angle) {
|
|
1525
|
+
this.quaternion.multiply(Node.scratchQuat.setAxisAngle(axis, angle));
|
|
1526
|
+
this.rotationChanged();
|
|
1527
|
+
return this;
|
|
1528
|
+
}
|
|
1529
|
+
translate(translation) {
|
|
1530
|
+
this.position.add(translation);
|
|
1531
|
+
this.markDirty();
|
|
1532
|
+
return this;
|
|
1533
|
+
}
|
|
1534
|
+
lookAt(target, up = Node.UP) {
|
|
1535
|
+
if (this.position.equals(target))
|
|
1536
|
+
return this;
|
|
1537
|
+
this.quaternion.lookAt(this.position, target, up);
|
|
1538
|
+
this.rotationChanged();
|
|
1539
|
+
return this;
|
|
1540
|
+
}
|
|
1541
|
+
updateModelMatrix() {
|
|
1542
|
+
if (!this.isTransformDirty)
|
|
1543
|
+
return false;
|
|
1544
|
+
this.modelMatrix.compose(this.position, this.quaternion, this.scale);
|
|
1545
|
+
this.isTransformDirty = false;
|
|
1546
|
+
this.isBoundsDirty = true;
|
|
1547
|
+
this.isNormalMatrixDirty = true;
|
|
1548
|
+
return true;
|
|
1549
|
+
}
|
|
1550
|
+
computeNormalMatrix() {
|
|
1551
|
+
if (!this.isNormalMatrixDirty)
|
|
1552
|
+
return this.normalMatrix;
|
|
1553
|
+
this.worldMatrix.toNormalMatrix(this.normalMatrix.toArray());
|
|
1554
|
+
this.isNormalMatrixDirty = false;
|
|
1555
|
+
return this.normalMatrix;
|
|
1556
|
+
}
|
|
1557
|
+
getWorldPosition(target = new Vec3()) {
|
|
1558
|
+
return this.worldMatrix.getTranslation(target);
|
|
1559
|
+
}
|
|
1560
|
+
getWorldDirection(target = new Vec3()) {
|
|
1561
|
+
return this.worldMatrix.getColumn(2, target).opposite().normalize();
|
|
1562
|
+
}
|
|
1563
|
+
distanceTo(target) {
|
|
1564
|
+
const targetPos = target instanceof Vec3 ? target : target.getWorldPosition();
|
|
1565
|
+
return this.getWorldPosition(Node.scratchVec).getDistance(targetPos);
|
|
1566
|
+
}
|
|
1567
|
+
isInsideBounds(box) {
|
|
1568
|
+
return box.containsPoint(this.getWorldPosition(Node.scratchVec));
|
|
1569
|
+
}
|
|
1570
|
+
setLocalBounds(box) {
|
|
1571
|
+
this.localBoundingBox.copy(box);
|
|
1572
|
+
this.localBoundingBox.getCenter(Node.scratchVec);
|
|
1573
|
+
this.localBoundingSphere.set(Node.scratchVec, box.min.getDistance(box.max) / 2);
|
|
1574
|
+
this.isBoundsDirty = true;
|
|
1575
|
+
return this;
|
|
1576
|
+
}
|
|
1577
|
+
computeBounds() {
|
|
1578
|
+
if (!this.isBoundsDirty)
|
|
1401
1579
|
return;
|
|
1580
|
+
if (!this.localBoundingBox.isEmpty()) {
|
|
1581
|
+
this.boundingBox.copy(this.localBoundingBox).applyMatrix4(this.worldMatrix);
|
|
1582
|
+
this.boundingSphere.copy(this.localBoundingSphere).applyMatrix4(this.worldMatrix);
|
|
1402
1583
|
}
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1584
|
+
else {
|
|
1585
|
+
const pos = this.getWorldPosition(Node.scratchVec);
|
|
1586
|
+
this.boundingBox.set(pos, pos);
|
|
1587
|
+
this.boundingSphere.set(pos, 0);
|
|
1588
|
+
}
|
|
1589
|
+
this.isBoundsDirty = false;
|
|
1590
|
+
}
|
|
1591
|
+
computeBoundingBox() {
|
|
1592
|
+
this.computeBounds();
|
|
1593
|
+
return this.boundingBox;
|
|
1594
|
+
}
|
|
1595
|
+
computeBoundingSphere() {
|
|
1596
|
+
this.computeBounds();
|
|
1597
|
+
return this.boundingSphere;
|
|
1598
|
+
}
|
|
1599
|
+
setWorldMatrix(worldMatrix) {
|
|
1600
|
+
this.worldMatrix.copy(worldMatrix).multiply(this.modelMatrix);
|
|
1601
|
+
this.isBoundsDirty = true;
|
|
1602
|
+
this.isNormalMatrixDirty = true;
|
|
1603
|
+
this.isWorldDirty = false;
|
|
1604
|
+
}
|
|
1605
|
+
computeWorldMatrix(graph) {
|
|
1606
|
+
const modelChanged = this.updateModelMatrix();
|
|
1607
|
+
const changed = this.isWorldDirty || modelChanged;
|
|
1608
|
+
if (changed)
|
|
1609
|
+
this.setWorldMatrix(graph.getWorldMatrix());
|
|
1610
|
+
graph.pushModelMatrix(this.worldMatrix);
|
|
1611
|
+
for (const child of this.children) {
|
|
1612
|
+
if (changed)
|
|
1613
|
+
child.isWorldDirty = true;
|
|
1614
|
+
child.computeWorldMatrix(graph);
|
|
1615
|
+
}
|
|
1616
|
+
graph.popModelMatrix();
|
|
1408
1617
|
}
|
|
1409
1618
|
}
|
|
1410
1619
|
|
|
1620
|
+
const scratchInverse = new Mat4x3();
|
|
1621
|
+
const scratchRay = new Ray3();
|
|
1622
|
+
const scratchVec = new Vec3();
|
|
1623
|
+
const scratchDir = new Vec3();
|
|
1624
|
+
const scratchA = new Vec3();
|
|
1625
|
+
const scratchB = new Vec3();
|
|
1626
|
+
const scratchC = new Vec3();
|
|
1627
|
+
const scratchHit = new Vec3();
|
|
1628
|
+
const scratchWorldHit = new Vec3();
|
|
1629
|
+
function getVertex(targetMesh, index, target) {
|
|
1630
|
+
const vertices = targetMesh.vertices;
|
|
1631
|
+
const size = targetMesh.itemSize || 3;
|
|
1632
|
+
const offset = (targetMesh.indices ? targetMesh.indices[index] : index) * size;
|
|
1633
|
+
return target.setScalar(vertices[offset], vertices[offset + 1], size > 2 ? vertices[offset + 2] : 0);
|
|
1634
|
+
}
|
|
1635
|
+
function raycastTriangles(mesh, ray) {
|
|
1636
|
+
const inverse = scratchInverse.copy(mesh.worldMatrix).invertAffine();
|
|
1637
|
+
const localRay = scratchRay.set(inverse.transformPoint(ray.origin, scratchVec), inverse.transformDirection(ray.direction, scratchDir));
|
|
1638
|
+
const strip = mesh.primitive === 'TRIANGLE_STRIP';
|
|
1639
|
+
let best = null;
|
|
1640
|
+
let bestDistance = Infinity;
|
|
1641
|
+
for (const subMesh of mesh.subMeshes) {
|
|
1642
|
+
const end = subMesh.start + subMesh.count;
|
|
1643
|
+
for (let i = subMesh.start; i + 2 < end; i += strip ? 1 : 3) {
|
|
1644
|
+
getVertex(mesh, i, scratchA);
|
|
1645
|
+
if (strip && (i - subMesh.start) % 2 === 1) {
|
|
1646
|
+
getVertex(mesh, i + 2, scratchB);
|
|
1647
|
+
getVertex(mesh, i + 1, scratchC);
|
|
1648
|
+
}
|
|
1649
|
+
else {
|
|
1650
|
+
getVertex(mesh, i + 1, scratchB);
|
|
1651
|
+
getVertex(mesh, i + 2, scratchC);
|
|
1652
|
+
}
|
|
1653
|
+
const local = localRay.intersectTriangle(scratchA, scratchB, scratchC, false, scratchHit);
|
|
1654
|
+
if (!local)
|
|
1655
|
+
continue;
|
|
1656
|
+
const world = mesh.worldMatrix.transformPoint(local, scratchWorldHit);
|
|
1657
|
+
const distance = ray.origin.getDistance(world);
|
|
1658
|
+
if (distance < bestDistance) {
|
|
1659
|
+
bestDistance = distance;
|
|
1660
|
+
best = (best ?? new Vec3()).copy(world);
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
return best ? { distance: bestDistance, point: best } : null;
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1411
1667
|
const isPowerOfTwo = (n) => (n & (n - 1)) === 0;
|
|
1412
1668
|
function canGenerateMipmaps(context, width, height) {
|
|
1413
1669
|
if (typeof WebGL2RenderingContext !== 'undefined' && context instanceof WebGL2RenderingContext)
|
|
@@ -1416,7 +1672,7 @@ function canGenerateMipmaps(context, width, height) {
|
|
|
1416
1672
|
}
|
|
1417
1673
|
const Texture = {
|
|
1418
1674
|
create(source, context, options) {
|
|
1419
|
-
|
|
1675
|
+
const webGLTexture = context.createTexture();
|
|
1420
1676
|
if (!webGLTexture)
|
|
1421
1677
|
return null;
|
|
1422
1678
|
const flipY = options?.flipY !== undefined ? options.flipY : true;
|
|
@@ -1430,13 +1686,16 @@ const Texture = {
|
|
|
1430
1686
|
context.texParameteri(context.TEXTURE_2D, context.TEXTURE_MIN_FILTER, minFilter);
|
|
1431
1687
|
context.texParameteri(context.TEXTURE_2D, context.TEXTURE_WRAP_S, wrapS);
|
|
1432
1688
|
context.texParameteri(context.TEXTURE_2D, context.TEXTURE_WRAP_T, wrapT);
|
|
1433
|
-
if (source instanceof Uint8Array)
|
|
1434
|
-
|
|
1689
|
+
if (source instanceof Uint8Array) {
|
|
1690
|
+
const width = options?.size ? options.size.x : 1;
|
|
1691
|
+
const height = options?.size ? options.size.y : 1;
|
|
1692
|
+
context.texImage2D(context.TEXTURE_2D, 0, context.RGBA, width, height, 0, context.RGBA, context.UNSIGNED_BYTE, source);
|
|
1693
|
+
}
|
|
1435
1694
|
else
|
|
1436
1695
|
context.texImage2D(context.TEXTURE_2D, 0, context.RGBA, context.RGBA, context.UNSIGNED_BYTE, source);
|
|
1437
1696
|
if (options?.generateMipmaps) {
|
|
1438
|
-
const width = source instanceof Uint8Array ? options.
|
|
1439
|
-
const height = source instanceof Uint8Array ? options.
|
|
1697
|
+
const width = source instanceof Uint8Array ? (options?.size ? options.size.x : 1) : source.width;
|
|
1698
|
+
const height = source instanceof Uint8Array ? (options?.size ? options.size.y : 1) : source.height;
|
|
1440
1699
|
if (canGenerateMipmaps(context, width, height))
|
|
1441
1700
|
context.generateMipmap(context.TEXTURE_2D);
|
|
1442
1701
|
else
|
|
@@ -1454,11 +1713,15 @@ const Texture = {
|
|
|
1454
1713
|
img.src = url;
|
|
1455
1714
|
});
|
|
1456
1715
|
},
|
|
1457
|
-
update(texture, source, context,
|
|
1716
|
+
update(texture, source, context, offset = new Vec2(), size, flipY = true) {
|
|
1458
1717
|
context.bindTexture(context.TEXTURE_2D, texture);
|
|
1459
1718
|
context.pixelStorei(context.UNPACK_FLIP_Y_WEBGL, flipY ? 1 : 0);
|
|
1719
|
+
const xOffset = offset.x;
|
|
1720
|
+
const yOffset = offset.y;
|
|
1721
|
+
const width = size ? size.x : 1;
|
|
1722
|
+
const height = size ? size.y : 1;
|
|
1460
1723
|
if (source instanceof Uint8Array)
|
|
1461
|
-
context.texSubImage2D(context.TEXTURE_2D, 0, xOffset, yOffset, width
|
|
1724
|
+
context.texSubImage2D(context.TEXTURE_2D, 0, xOffset, yOffset, width, height, context.RGBA, context.UNSIGNED_BYTE, source);
|
|
1462
1725
|
else
|
|
1463
1726
|
context.texSubImage2D(context.TEXTURE_2D, 0, xOffset, yOffset, context.RGBA, context.UNSIGNED_BYTE, source);
|
|
1464
1727
|
context.bindTexture(context.TEXTURE_2D, null);
|
|
@@ -1494,6 +1757,95 @@ function uploadUniform(context, location, uniform) {
|
|
|
1494
1757
|
context[type](location, value);
|
|
1495
1758
|
}
|
|
1496
1759
|
|
|
1760
|
+
const scratchView = new Mat4x3();
|
|
1761
|
+
const scratchCamera = new Vec3();
|
|
1762
|
+
function lightTypeValue(type) {
|
|
1763
|
+
return type === 'directional' ? 0 : type === 'point' ? 1 : 2;
|
|
1764
|
+
}
|
|
1765
|
+
function packVec4(xyz, w) {
|
|
1766
|
+
const count = Math.floor(xyz.length / 3);
|
|
1767
|
+
const packed = new Float32Array(count * 4);
|
|
1768
|
+
for (let i = 0; i < count; i++) {
|
|
1769
|
+
packed[i * 4] = xyz[i * 3];
|
|
1770
|
+
packed[i * 4 + 1] = xyz[i * 3 + 1];
|
|
1771
|
+
packed[i * 4 + 2] = xyz[i * 3 + 2];
|
|
1772
|
+
packed[i * 4 + 3] = w[i] || 0;
|
|
1773
|
+
}
|
|
1774
|
+
return packed;
|
|
1775
|
+
}
|
|
1776
|
+
function uploadFrameUniforms(context, program, projectionMatrix, viewMatrix, lights, time, cameraPosition, fog, frame) {
|
|
1777
|
+
if (program.projectionMatrix)
|
|
1778
|
+
context.uniformMatrix4fv(program.projectionMatrix, false, projectionMatrix);
|
|
1779
|
+
if (program.viewMatrix)
|
|
1780
|
+
context.uniformMatrix4fv(program.viewMatrix, false, viewMatrix);
|
|
1781
|
+
if (program.time)
|
|
1782
|
+
context.uniform1f(program.time, time);
|
|
1783
|
+
if (frame && frame.resolution && program.screenResolution)
|
|
1784
|
+
context.uniform2f(program.screenResolution, frame.resolution.x, frame.resolution.y);
|
|
1785
|
+
if (lights) {
|
|
1786
|
+
if (program.nbLights && lights.type)
|
|
1787
|
+
context.uniform1i(program.nbLights, lights.type.length);
|
|
1788
|
+
if (program.lightPosition && lights.position && lights.position.length > 0)
|
|
1789
|
+
context.uniform3fv(program.lightPosition, lights.floatPosition || new Float32Array(lights.position));
|
|
1790
|
+
if (program.lightDiffuse && lights.diffuse && lights.diffuse.length > 0)
|
|
1791
|
+
context.uniform3fv(program.lightDiffuse, lights.floatDiffuse || new Float32Array(lights.diffuse));
|
|
1792
|
+
if (program.lightSpecular && lights.specular && lights.specular.length > 0)
|
|
1793
|
+
context.uniform3fv(program.lightSpecular, lights.floatSpecular || new Float32Array(lights.specular));
|
|
1794
|
+
if (program.lightDirection && lights.direction && lights.direction.length > 0)
|
|
1795
|
+
context.uniform3fv(program.lightDirection, lights.floatDirection || new Float32Array(lights.direction));
|
|
1796
|
+
if (program.lightAttenuation && (lights.floatAttenuation || lights.constantAttenuation?.length)) {
|
|
1797
|
+
let attArr = lights.floatAttenuation;
|
|
1798
|
+
if (!attArr) {
|
|
1799
|
+
const att = [];
|
|
1800
|
+
for (let k = 0; k < lights.constantAttenuation.length; k++)
|
|
1801
|
+
att.push(lights.constantAttenuation[k] || 0, lights.linearAttenuation[k] || 0, lights.quadraticAttenuation[k] || 0);
|
|
1802
|
+
attArr = new Float32Array(att);
|
|
1803
|
+
}
|
|
1804
|
+
context.uniform3fv(program.lightAttenuation, attArr);
|
|
1805
|
+
}
|
|
1806
|
+
if (program.lightCutoff && lights.cutoff && lights.cutoff.length > 0)
|
|
1807
|
+
context.uniform1fv(program.lightCutoff, lights.floatCutoff || new Float32Array(lights.cutoff));
|
|
1808
|
+
if (program.lightExponent && lights.exponent && lights.exponent.length > 0)
|
|
1809
|
+
context.uniform1fv(program.lightExponent, lights.floatExponent || new Float32Array(lights.exponent));
|
|
1810
|
+
if (program.lightType && (lights.floatType || lights.type?.length)) {
|
|
1811
|
+
let typeArr = lights.floatType;
|
|
1812
|
+
if (!typeArr)
|
|
1813
|
+
typeArr = new Float32Array(lights.type.map(t => t === 'directional' ? 0 : t === 'point' ? 1 : 2));
|
|
1814
|
+
context.uniform1fv(program.lightType, typeArr);
|
|
1815
|
+
}
|
|
1816
|
+
if (program.lightPositionType && lights.position && lights.position.length > 0)
|
|
1817
|
+
context.uniform4fv(program.lightPositionType, lights.packedPosition || packVec4(lights.position, (lights.type || []).map(lightTypeValue)));
|
|
1818
|
+
if (program.lightDiffuseCutoff && lights.diffuse && lights.diffuse.length > 0)
|
|
1819
|
+
context.uniform4fv(program.lightDiffuseCutoff, lights.packedDiffuse || packVec4(lights.diffuse, lights.cutoff || []));
|
|
1820
|
+
if (program.lightSpecularExponent && lights.specular && lights.specular.length > 0)
|
|
1821
|
+
context.uniform4fv(program.lightSpecularExponent, lights.packedSpecular || packVec4(lights.specular, lights.exponent || []));
|
|
1822
|
+
if (program.ambientLight && lights.ambient)
|
|
1823
|
+
context.uniform3fv(program.ambientLight, lights.floatAmbient || new Float32Array(lights.ambient));
|
|
1824
|
+
}
|
|
1825
|
+
if (program.cameraPosition) {
|
|
1826
|
+
let camera = cameraPosition;
|
|
1827
|
+
if (!camera && viewMatrix) {
|
|
1828
|
+
scratchView.toArray().set(viewMatrix);
|
|
1829
|
+
camera = scratchView.invertAffine().getTranslation(scratchCamera);
|
|
1830
|
+
}
|
|
1831
|
+
if (camera)
|
|
1832
|
+
context.uniform3f(program.cameraPosition, camera.x, camera.y, camera.z);
|
|
1833
|
+
}
|
|
1834
|
+
if (program.hasFog) {
|
|
1835
|
+
if (fog) {
|
|
1836
|
+
context.uniform1i(program.hasFog, 1);
|
|
1837
|
+
if (program.fogColor)
|
|
1838
|
+
context.uniform3f(program.fogColor, fog.color.x, fog.color.y, fog.color.z);
|
|
1839
|
+
if (program.fogNear)
|
|
1840
|
+
context.uniform1f(program.fogNear, fog.near);
|
|
1841
|
+
if (program.fogFar)
|
|
1842
|
+
context.uniform1f(program.fogFar, fog.far);
|
|
1843
|
+
}
|
|
1844
|
+
else
|
|
1845
|
+
context.uniform1i(program.hasFog, 0);
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1497
1849
|
class MeshRenderer {
|
|
1498
1850
|
context;
|
|
1499
1851
|
state;
|
|
@@ -1505,7 +1857,7 @@ class MeshRenderer {
|
|
|
1505
1857
|
defaultSettings() {
|
|
1506
1858
|
}
|
|
1507
1859
|
createBuffer(target, size, drawMethod) {
|
|
1508
|
-
|
|
1860
|
+
const buffer = this.context.createBuffer();
|
|
1509
1861
|
this.bindBuffer(target, buffer);
|
|
1510
1862
|
this.context.bufferData(this.context[target], size, this.context[drawMethod]);
|
|
1511
1863
|
return buffer;
|
|
@@ -1564,6 +1916,70 @@ class MeshRenderer {
|
|
|
1564
1916
|
const mode = typeof primitive === 'number' ? primitive : (this.context[primitive] ?? this.context.TRIANGLES);
|
|
1565
1917
|
this.context.drawArrays(mode, subMesh.start, subMesh.count);
|
|
1566
1918
|
}
|
|
1919
|
+
drawMesh(mesh, projectionMatrix, viewMatrix, lights, time, cameraPosition, fog, frame) {
|
|
1920
|
+
const context = this.context;
|
|
1921
|
+
const state = this.state;
|
|
1922
|
+
for (let i = 0; i < mesh.nbSubMeshes; i++) {
|
|
1923
|
+
const program = mesh.programs[i] || mesh.programs[mesh.nbPrograms - 1];
|
|
1924
|
+
this.useProgram(program);
|
|
1925
|
+
if (!frame || state.frameOfProgram.get(program) !== frame.id) {
|
|
1926
|
+
if (frame)
|
|
1927
|
+
state.frameOfProgram.set(program, frame.id);
|
|
1928
|
+
uploadFrameUniforms(context, program, projectionMatrix, viewMatrix, lights, time, cameraPosition, fog, frame);
|
|
1929
|
+
}
|
|
1930
|
+
const material = mesh.materials[i] || mesh.materials[mesh.materials.length - 1];
|
|
1931
|
+
if (material) {
|
|
1932
|
+
material.updateUniforms();
|
|
1933
|
+
if (state.materialOfProgram.get(program) !== material || state.materialVersionOfProgram.get(program) !== material.version) {
|
|
1934
|
+
state.materialOfProgram.set(program, material);
|
|
1935
|
+
state.materialVersionOfProgram.set(program, material.version);
|
|
1936
|
+
const materialUniforms = material.uniforms;
|
|
1937
|
+
for (const property in materialUniforms)
|
|
1938
|
+
if (materialUniforms.hasOwnProperty(property) && program[property])
|
|
1939
|
+
uploadUniform(context, program[property], materialUniforms[property]);
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
let attributeMask = 0;
|
|
1943
|
+
if (mesh.indices)
|
|
1944
|
+
this.bindBuffer('ELEMENT_ARRAY_BUFFER', mesh.indexBuffer);
|
|
1945
|
+
if (program.vertexPosition !== -1 && program.vertexPosition !== undefined && mesh.vertexBuffer) {
|
|
1946
|
+
this.bindBuffer('ARRAY_BUFFER', mesh.vertexBuffer);
|
|
1947
|
+
this.vertexAttribPointer(program.vertexPosition, mesh.itemSize, 'FLOAT', false, 0, 0);
|
|
1948
|
+
attributeMask |= 1 << program.vertexPosition;
|
|
1949
|
+
}
|
|
1950
|
+
if (mesh.normals && program.vertexNormal !== -1 && program.vertexNormal !== undefined) {
|
|
1951
|
+
this.bindBuffer('ARRAY_BUFFER', mesh.normalBuffer);
|
|
1952
|
+
this.vertexAttribPointer(program.vertexNormal, 3, 'FLOAT', false, 0, 0);
|
|
1953
|
+
attributeMask |= 1 << program.vertexNormal;
|
|
1954
|
+
}
|
|
1955
|
+
if (mesh.uvs && program.textureCoord !== -1 && program.textureCoord !== undefined) {
|
|
1956
|
+
this.bindBuffer('ARRAY_BUFFER', mesh.texCoordBuffer);
|
|
1957
|
+
this.vertexAttribPointer(program.textureCoord, 2, 'FLOAT', false, 0, 0);
|
|
1958
|
+
attributeMask |= 1 << program.textureCoord;
|
|
1959
|
+
}
|
|
1960
|
+
this.setAttributeMask(attributeMask);
|
|
1961
|
+
if (mesh.WebGLTexture) {
|
|
1962
|
+
context.activeTexture(context.TEXTURE0);
|
|
1963
|
+
context.bindTexture(context.TEXTURE_2D, mesh.WebGLTexture);
|
|
1964
|
+
if (program.sampler)
|
|
1965
|
+
context.uniform1i(program.sampler, 0);
|
|
1966
|
+
if (program.hasTexture)
|
|
1967
|
+
context.uniform1i(program.hasTexture, 1);
|
|
1968
|
+
}
|
|
1969
|
+
else if (program.hasTexture)
|
|
1970
|
+
context.uniform1i(program.hasTexture, 0);
|
|
1971
|
+
if (program.modelMatrix)
|
|
1972
|
+
context.uniformMatrix4fv(program.modelMatrix, false, mesh.worldMatrix.toArray());
|
|
1973
|
+
if (program.normalMatrix) {
|
|
1974
|
+
mesh.computeNormalMatrix();
|
|
1975
|
+
context.uniformMatrix3fv(program.normalMatrix, false, mesh.normalMatrix.toArray());
|
|
1976
|
+
}
|
|
1977
|
+
for (const property in mesh.customUniforms)
|
|
1978
|
+
if (mesh.customUniforms.hasOwnProperty(property) && program[property])
|
|
1979
|
+
uploadUniform(context, program[property], mesh.customUniforms[property]);
|
|
1980
|
+
this[mesh.drawMethod](mesh.primitive, mesh.subMeshes[i], mesh.indexType);
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1567
1983
|
}
|
|
1568
1984
|
|
|
1569
1985
|
class SubMesh {
|
|
@@ -1604,16 +2020,16 @@ class Material {
|
|
|
1604
2020
|
this.setShininess(options.shininess);
|
|
1605
2021
|
}
|
|
1606
2022
|
}
|
|
1607
|
-
setAmbient(
|
|
1608
|
-
|
|
2023
|
+
setAmbient(ambient) {
|
|
2024
|
+
this.ambient.copy(ambient).toArray(this.uniforms.materialAmbient.value);
|
|
1609
2025
|
this.version++;
|
|
1610
2026
|
}
|
|
1611
|
-
setDiffuse(
|
|
1612
|
-
|
|
2027
|
+
setDiffuse(diffuse) {
|
|
2028
|
+
this.diffuse.copy(diffuse).toArray(this.uniforms.materialDiffuse.value);
|
|
1613
2029
|
this.version++;
|
|
1614
2030
|
}
|
|
1615
|
-
setSpecular(
|
|
1616
|
-
|
|
2031
|
+
setSpecular(specular) {
|
|
2032
|
+
this.specular.copy(specular).toArray(this.uniforms.materialSpecular.value);
|
|
1617
2033
|
this.version++;
|
|
1618
2034
|
}
|
|
1619
2035
|
setShininess(shininess) {
|
|
@@ -1661,54 +2077,25 @@ void main(void) {
|
|
|
1661
2077
|
fragment: `
|
|
1662
2078
|
precision mediump float;
|
|
1663
2079
|
|
|
1664
|
-
varying vec2 vTextureCoord;
|
|
1665
|
-
|
|
1666
|
-
uniform sampler2D uSampler;
|
|
1667
|
-
uniform vec3 uMaterialDiffuse;
|
|
1668
|
-
uniform int uHasTexture;
|
|
1669
|
-
|
|
1670
|
-
void main(void) {
|
|
1671
|
-
vec4 color = vec4(uMaterialDiffuse, 1.0);
|
|
1672
|
-
if (uHasTexture == 1) {
|
|
1673
|
-
color *= texture2D(uSampler, vTextureCoord);
|
|
1674
|
-
}
|
|
1675
|
-
gl_FragColor = color;
|
|
1676
|
-
}
|
|
1677
|
-
`,
|
|
1678
|
-
create(context) {
|
|
1679
|
-
return Program.create(context, BasicShader.vertex, BasicShader.fragment);
|
|
1680
|
-
}
|
|
1681
|
-
};
|
|
1682
|
-
const NormalShader = {
|
|
1683
|
-
vertex: `
|
|
1684
|
-
attribute vec3 aVertexPosition;
|
|
1685
|
-
attribute vec3 aVertexNormal;
|
|
1686
|
-
|
|
1687
|
-
uniform mat4 uModelMatrix;
|
|
1688
|
-
uniform mat4 uViewMatrix;
|
|
1689
|
-
uniform mat4 uProjectionMatrix;
|
|
1690
|
-
uniform mat3 uNormalMatrix;
|
|
1691
|
-
|
|
1692
|
-
varying vec3 vNormal;
|
|
1693
|
-
|
|
1694
|
-
void main(void) {
|
|
1695
|
-
vNormal = normalize(uNormalMatrix * aVertexNormal);
|
|
1696
|
-
gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * vec4(aVertexPosition, 1.0);
|
|
1697
|
-
}
|
|
1698
|
-
`,
|
|
1699
|
-
fragment: `
|
|
1700
|
-
precision mediump float;
|
|
1701
|
-
|
|
1702
|
-
varying vec3 vNormal;
|
|
2080
|
+
varying vec2 vTextureCoord;
|
|
2081
|
+
|
|
2082
|
+
uniform sampler2D uSampler;
|
|
2083
|
+
uniform vec3 uMaterialDiffuse;
|
|
2084
|
+
uniform int uHasTexture;
|
|
1703
2085
|
|
|
1704
2086
|
void main(void) {
|
|
1705
|
-
|
|
2087
|
+
vec4 color = vec4(uMaterialDiffuse, 1.0);
|
|
2088
|
+
if (uHasTexture == 1) {
|
|
2089
|
+
color *= texture2D(uSampler, vTextureCoord);
|
|
2090
|
+
}
|
|
2091
|
+
gl_FragColor = color;
|
|
1706
2092
|
}
|
|
1707
2093
|
`,
|
|
1708
2094
|
create(context) {
|
|
1709
|
-
return Program.create(context,
|
|
2095
|
+
return Program.create(context, BasicShader.vertex, BasicShader.fragment);
|
|
1710
2096
|
}
|
|
1711
2097
|
};
|
|
2098
|
+
|
|
1712
2099
|
const PhongShader = {
|
|
1713
2100
|
vertex: `
|
|
1714
2101
|
attribute vec3 aVertexPosition;
|
|
@@ -1846,14 +2233,124 @@ void main(void) {
|
|
|
1846
2233
|
}
|
|
1847
2234
|
};
|
|
1848
2235
|
|
|
2236
|
+
const NormalShader = {
|
|
2237
|
+
vertex: `
|
|
2238
|
+
attribute vec3 aVertexPosition;
|
|
2239
|
+
attribute vec3 aVertexNormal;
|
|
2240
|
+
|
|
2241
|
+
uniform mat4 uModelMatrix;
|
|
2242
|
+
uniform mat4 uViewMatrix;
|
|
2243
|
+
uniform mat4 uProjectionMatrix;
|
|
2244
|
+
uniform mat3 uNormalMatrix;
|
|
2245
|
+
|
|
2246
|
+
varying vec3 vNormal;
|
|
2247
|
+
|
|
2248
|
+
void main(void) {
|
|
2249
|
+
vNormal = normalize(uNormalMatrix * aVertexNormal);
|
|
2250
|
+
gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * vec4(aVertexPosition, 1.0);
|
|
2251
|
+
}
|
|
2252
|
+
`,
|
|
2253
|
+
fragment: `
|
|
2254
|
+
precision mediump float;
|
|
2255
|
+
|
|
2256
|
+
varying vec3 vNormal;
|
|
2257
|
+
|
|
2258
|
+
void main(void) {
|
|
2259
|
+
gl_FragColor = vec4(vNormal * 0.5 + 0.5, 1.0);
|
|
2260
|
+
}
|
|
2261
|
+
`,
|
|
2262
|
+
create(context) {
|
|
2263
|
+
return Program.create(context, NormalShader.vertex, NormalShader.fragment);
|
|
2264
|
+
}
|
|
2265
|
+
};
|
|
2266
|
+
|
|
2267
|
+
const initializedPrograms = new WeakSet();
|
|
2268
|
+
function ucfirst(string) {
|
|
2269
|
+
return string.charAt(0).toUpperCase() + string.slice(1);
|
|
2270
|
+
}
|
|
2271
|
+
function addProgramAttribute(context, program, name) {
|
|
2272
|
+
program[name] = context.getAttribLocation(program, `a${ucfirst(name)}`);
|
|
2273
|
+
}
|
|
2274
|
+
function addProgramUniform(context, program, name) {
|
|
2275
|
+
const ucName = `u${ucfirst(name)}`;
|
|
2276
|
+
let loc = context.getUniformLocation(program, ucName);
|
|
2277
|
+
if (loc === null)
|
|
2278
|
+
loc = context.getUniformLocation(program, `${ucName}[0]`);
|
|
2279
|
+
program[name] = loc;
|
|
2280
|
+
}
|
|
2281
|
+
function initProgramLocations(context, program, customUniforms, materials) {
|
|
2282
|
+
if (!initializedPrograms.has(program)) {
|
|
2283
|
+
initializedPrograms.add(program);
|
|
2284
|
+
addProgramAttribute(context, program, 'vertexPosition');
|
|
2285
|
+
addProgramAttribute(context, program, 'vertexNormal');
|
|
2286
|
+
addProgramAttribute(context, program, 'textureCoord');
|
|
2287
|
+
addProgramUniform(context, program, 'modelMatrix');
|
|
2288
|
+
addProgramUniform(context, program, 'normalMatrix');
|
|
2289
|
+
addProgramUniform(context, program, 'viewMatrix');
|
|
2290
|
+
addProgramUniform(context, program, 'projectionMatrix');
|
|
2291
|
+
addProgramUniform(context, program, 'time');
|
|
2292
|
+
addProgramUniform(context, program, 'screenResolution');
|
|
2293
|
+
addProgramUniform(context, program, 'sampler');
|
|
2294
|
+
addProgramUniform(context, program, 'hasTexture');
|
|
2295
|
+
addProgramUniform(context, program, 'nbLights');
|
|
2296
|
+
addProgramUniform(context, program, 'lightPosition');
|
|
2297
|
+
addProgramUniform(context, program, 'lightDiffuse');
|
|
2298
|
+
addProgramUniform(context, program, 'lightSpecular');
|
|
2299
|
+
addProgramUniform(context, program, 'lightDirection');
|
|
2300
|
+
addProgramUniform(context, program, 'lightAttenuation');
|
|
2301
|
+
addProgramUniform(context, program, 'lightCutoff');
|
|
2302
|
+
addProgramUniform(context, program, 'lightExponent');
|
|
2303
|
+
addProgramUniform(context, program, 'lightType');
|
|
2304
|
+
addProgramUniform(context, program, 'lightPositionType');
|
|
2305
|
+
addProgramUniform(context, program, 'lightDiffuseCutoff');
|
|
2306
|
+
addProgramUniform(context, program, 'lightSpecularExponent');
|
|
2307
|
+
addProgramUniform(context, program, 'ambientLight');
|
|
2308
|
+
addProgramUniform(context, program, 'ambientColor');
|
|
2309
|
+
addProgramUniform(context, program, 'cameraPosition');
|
|
2310
|
+
addProgramUniform(context, program, 'hasFog');
|
|
2311
|
+
addProgramUniform(context, program, 'fogColor');
|
|
2312
|
+
addProgramUniform(context, program, 'fogNear');
|
|
2313
|
+
addProgramUniform(context, program, 'fogFar');
|
|
2314
|
+
if (program.screenResolution) {
|
|
2315
|
+
context.useProgram(program);
|
|
2316
|
+
const viewport = context.getParameter(context.VIEWPORT);
|
|
2317
|
+
if (viewport && viewport.length >= 4)
|
|
2318
|
+
context.uniform2f(program.screenResolution, viewport[2], viewport[3]);
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
for (const property in customUniforms)
|
|
2322
|
+
if (customUniforms.hasOwnProperty(property) && program[property] === undefined)
|
|
2323
|
+
addProgramUniform(context, program, property);
|
|
2324
|
+
for (const mat of materials)
|
|
2325
|
+
for (const property in mat.uniforms)
|
|
2326
|
+
if (mat.uniforms.hasOwnProperty(property) && program[property] === undefined)
|
|
2327
|
+
addProgramUniform(context, program, property);
|
|
2328
|
+
}
|
|
2329
|
+
function syncMaterialUniforms(context, programs, material) {
|
|
2330
|
+
const materialUniforms = material.uniforms;
|
|
2331
|
+
for (const property in materialUniforms)
|
|
2332
|
+
if (materialUniforms.hasOwnProperty(property))
|
|
2333
|
+
for (const program of programs)
|
|
2334
|
+
if (program[property] === undefined)
|
|
2335
|
+
program[property] = context.getUniformLocation(program, `u${ucfirst(property)}`);
|
|
2336
|
+
}
|
|
2337
|
+
function syncCustomUniform(context, programs, name) {
|
|
2338
|
+
for (const program of programs)
|
|
2339
|
+
if (program[name] === undefined)
|
|
2340
|
+
program[name] = context.getUniformLocation(program, `u${ucfirst(name)}`);
|
|
2341
|
+
}
|
|
2342
|
+
function syncTextureSampler(context, programs) {
|
|
2343
|
+
for (const program of programs)
|
|
2344
|
+
if (program.sampler === undefined)
|
|
2345
|
+
program.sampler = context.getUniformLocation(program, 'uSampler');
|
|
2346
|
+
}
|
|
2347
|
+
|
|
1849
2348
|
var eDrawMethod;
|
|
1850
2349
|
(function (eDrawMethod) {
|
|
1851
2350
|
eDrawMethod["drawElements"] = "drawElements";
|
|
1852
2351
|
eDrawMethod["drawArrays"] = "drawArrays";
|
|
1853
2352
|
})(eDrawMethod || (eDrawMethod = {}));
|
|
1854
|
-
class Mesh {
|
|
1855
|
-
boundingBox;
|
|
1856
|
-
boundingSphere;
|
|
2353
|
+
class Mesh extends Node {
|
|
1857
2354
|
vertices;
|
|
1858
2355
|
indices;
|
|
1859
2356
|
normals;
|
|
@@ -1870,49 +2367,23 @@ class Mesh {
|
|
|
1870
2367
|
indexBuffer;
|
|
1871
2368
|
normalBuffer;
|
|
1872
2369
|
texCoordBuffer;
|
|
1873
|
-
position;
|
|
1874
|
-
scale;
|
|
1875
|
-
quaternion;
|
|
1876
|
-
modelMatrix;
|
|
1877
|
-
worldMatrix;
|
|
1878
|
-
normalMatrix;
|
|
1879
|
-
parent = null;
|
|
1880
|
-
traversalStamp = 0;
|
|
1881
2370
|
sortKey = 0;
|
|
1882
|
-
_rotation;
|
|
1883
|
-
isRotationStale;
|
|
1884
|
-
isTransformDirty;
|
|
1885
|
-
isWorldDirty = true;
|
|
1886
2371
|
ownsTexture = false;
|
|
2372
|
+
textureSource = null;
|
|
2373
|
+
textureOptions;
|
|
1887
2374
|
indexType;
|
|
1888
|
-
static UP = new Vec3(0, 1, 0);
|
|
1889
|
-
static scratchQuat = new Quat();
|
|
1890
|
-
static scratchView = new Mat4x3();
|
|
1891
|
-
static scratchVec = new Vec3();
|
|
1892
|
-
static scratchCamera = new Vec3();
|
|
1893
|
-
static scratchDir = new Vec3();
|
|
1894
|
-
static scratchA = new Vec3();
|
|
1895
|
-
static scratchB = new Vec3();
|
|
1896
|
-
static scratchC = new Vec3();
|
|
1897
2375
|
static scratchHit = new Vec3();
|
|
1898
|
-
static scratchWorldHit = new Vec3();
|
|
1899
|
-
static scratchInverse = new Mat4x3();
|
|
1900
|
-
static scratchRay = new Ray3();
|
|
1901
|
-
isBoundsDirty = true;
|
|
1902
|
-
isNormalMatrixDirty = true;
|
|
1903
|
-
localBoundingBox;
|
|
1904
|
-
localBoundingSphere;
|
|
1905
2376
|
active;
|
|
1906
2377
|
drawMethod;
|
|
1907
2378
|
programs;
|
|
1908
2379
|
nbPrograms;
|
|
1909
2380
|
materials;
|
|
1910
|
-
children;
|
|
1911
2381
|
blendMode;
|
|
1912
2382
|
blendOptions = { equation: 'FUNC_ADD', source: 'SRC_ALPHA', destination: 'ONE' };
|
|
1913
2383
|
depthWrite = true;
|
|
1914
2384
|
zOrder;
|
|
1915
2385
|
constructor(meshOrContext, contextOrMesh) {
|
|
2386
|
+
super();
|
|
1916
2387
|
let mesh;
|
|
1917
2388
|
let context;
|
|
1918
2389
|
if (meshOrContext && ('drawingBufferWidth' in meshOrContext || typeof meshOrContext.createBuffer === 'function')) {
|
|
@@ -1937,37 +2408,12 @@ class Mesh {
|
|
|
1937
2408
|
this.context = context;
|
|
1938
2409
|
this.renderer = new MeshRenderer(this.context);
|
|
1939
2410
|
this.WebGLTexture = null;
|
|
1940
|
-
this.vertexBuffer =
|
|
1941
|
-
|
|
1942
|
-
|
|
2411
|
+
this.vertexBuffer = null;
|
|
2412
|
+
this.indexBuffer = null;
|
|
2413
|
+
this.normalBuffer = null;
|
|
2414
|
+
this.texCoordBuffer = null;
|
|
1943
2415
|
this.indexType = this.context.UNSIGNED_SHORT;
|
|
1944
|
-
this.
|
|
1945
|
-
? this.renderer.createBuffer('ELEMENT_ARRAY_BUFFER', this.createIndexData(this.indices), 'STATIC_DRAW')
|
|
1946
|
-
: null;
|
|
1947
|
-
this.normalBuffer = this.normals
|
|
1948
|
-
? this.renderer.createBuffer('ARRAY_BUFFER', this.normals instanceof Float32Array ? this.normals : new Float32Array(this.normals), 'STATIC_DRAW')
|
|
1949
|
-
: null;
|
|
1950
|
-
this.texCoordBuffer = this.uvs
|
|
1951
|
-
? this.renderer.createBuffer('ARRAY_BUFFER', this.uvs instanceof Float32Array ? this.uvs : new Float32Array(this.uvs), 'STATIC_DRAW')
|
|
1952
|
-
: null;
|
|
1953
|
-
this.position = new Vec3();
|
|
1954
|
-
this._rotation = new Vec3();
|
|
1955
|
-
this.scale = new Vec3(1.0, 1.0, 1.0);
|
|
1956
|
-
this.quaternion = new Quat();
|
|
1957
|
-
this.modelMatrix = new Mat4x3();
|
|
1958
|
-
this.worldMatrix = new Mat4x3();
|
|
1959
|
-
this.normalMatrix = new Mat3();
|
|
1960
|
-
this.isRotationStale = false;
|
|
1961
|
-
this.isTransformDirty = false;
|
|
1962
|
-
this.modelMatrix.identity();
|
|
1963
|
-
this.worldMatrix.identity();
|
|
1964
|
-
this.normalMatrix.identity();
|
|
1965
|
-
this.isBoundsDirty = true;
|
|
1966
|
-
this.isNormalMatrixDirty = true;
|
|
1967
|
-
this.localBoundingBox = new Box();
|
|
1968
|
-
this.localBoundingSphere = new Sphere$1();
|
|
1969
|
-
this.boundingBox = new Box();
|
|
1970
|
-
this.boundingSphere = new Sphere$1();
|
|
2416
|
+
this.createBuffers();
|
|
1971
2417
|
if (this.vertices && this.vertices.length > 0) {
|
|
1972
2418
|
this.localBoundingBox.setFromPoints(this.vertices, this.itemSize || 3);
|
|
1973
2419
|
this.localBoundingSphere.setFromPoints(this.vertices, undefined, this.itemSize || 3);
|
|
@@ -1977,7 +2423,6 @@ class Mesh {
|
|
|
1977
2423
|
this.programs = [];
|
|
1978
2424
|
this.nbPrograms = 0;
|
|
1979
2425
|
this.materials = [];
|
|
1980
|
-
this.children = [];
|
|
1981
2426
|
this.blendMode = false;
|
|
1982
2427
|
this.zOrder = 0;
|
|
1983
2428
|
}
|
|
@@ -1994,256 +2439,52 @@ class Mesh {
|
|
|
1994
2439
|
isActive() {
|
|
1995
2440
|
return this.active;
|
|
1996
2441
|
}
|
|
1997
|
-
addChild(mesh) {
|
|
1998
|
-
if (this.children.indexOf(mesh) === -1) {
|
|
1999
|
-
if (mesh.parent && mesh.parent !== this)
|
|
2000
|
-
mesh.parent.removeChild(mesh);
|
|
2001
|
-
this.children.push(mesh);
|
|
2002
|
-
mesh.parent = this;
|
|
2003
|
-
mesh.isWorldDirty = true;
|
|
2004
|
-
}
|
|
2005
|
-
}
|
|
2006
|
-
removeChild(mesh) {
|
|
2007
|
-
const index = this.children.indexOf(mesh);
|
|
2008
|
-
if (index !== -1) {
|
|
2009
|
-
this.children.splice(index, 1);
|
|
2010
|
-
mesh.parent = null;
|
|
2011
|
-
mesh.isWorldDirty = true;
|
|
2012
|
-
return true;
|
|
2013
|
-
}
|
|
2014
|
-
return false;
|
|
2015
|
-
}
|
|
2016
2442
|
addProgram(vertexShaderOrProgram, fragmentShaderOrMaterial, material) {
|
|
2017
|
-
if (this.nbPrograms < this.nbSubMeshes) {
|
|
2018
|
-
let program;
|
|
2019
|
-
let mat;
|
|
2020
|
-
if (typeof vertexShaderOrProgram !== 'string') {
|
|
2021
|
-
program = vertexShaderOrProgram;
|
|
2022
|
-
mat = (fragmentShaderOrMaterial instanceof Material) ? fragmentShaderOrMaterial : new Material();
|
|
2023
|
-
}
|
|
2024
|
-
else {
|
|
2025
|
-
program = Program.acquire(this.context, vertexShaderOrProgram, fragmentShaderOrMaterial);
|
|
2026
|
-
mat = (material instanceof Material) ? material : (fragmentShaderOrMaterial instanceof Material ? fragmentShaderOrMaterial : new Material());
|
|
2027
|
-
}
|
|
2028
|
-
if (!program)
|
|
2029
|
-
return false;
|
|
2030
|
-
this.programs.push(program);
|
|
2031
|
-
this.addMaterial(mat);
|
|
2032
|
-
this.createProgram();
|
|
2033
|
-
return true;
|
|
2034
|
-
}
|
|
2035
|
-
return false;
|
|
2036
|
-
}
|
|
2037
|
-
clearPrograms() {
|
|
2038
|
-
for (let program of this.programs)
|
|
2039
|
-
Program.release(this.context, program);
|
|
2040
|
-
this.programs = [];
|
|
2041
|
-
this.nbPrograms = 0;
|
|
2042
|
-
this.materials = [];
|
|
2043
|
-
}
|
|
2044
|
-
addMaterial(material) {
|
|
2045
|
-
if (material && this.materials.length < this.nbSubMeshes) {
|
|
2046
|
-
this.materials.push(material);
|
|
2047
|
-
let materialUniforms = material.uniforms;
|
|
2048
|
-
for (let property in materialUniforms)
|
|
2049
|
-
if (materialUniforms.hasOwnProperty(property))
|
|
2050
|
-
for (let program of this.programs)
|
|
2051
|
-
if (program[property] === undefined)
|
|
2052
|
-
program[property] = this.context.getUniformLocation(program, 'u' + this.ucfirst(property));
|
|
2053
|
-
return true;
|
|
2054
|
-
}
|
|
2055
|
-
return false;
|
|
2056
|
-
}
|
|
2057
|
-
markDirty() {
|
|
2058
|
-
this.isTransformDirty = true;
|
|
2059
|
-
this.isBoundsDirty = true;
|
|
2060
|
-
this.isNormalMatrixDirty = true;
|
|
2061
|
-
}
|
|
2062
|
-
get rotation() {
|
|
2063
|
-
if (this.isRotationStale) {
|
|
2064
|
-
this.quaternion.getEuler(this._rotation);
|
|
2065
|
-
this.isRotationStale = false;
|
|
2066
|
-
}
|
|
2067
|
-
return this._rotation;
|
|
2068
|
-
}
|
|
2069
|
-
set rotation(euler) {
|
|
2070
|
-
this.setRotation(euler);
|
|
2071
|
-
}
|
|
2072
|
-
rotationChanged() {
|
|
2073
|
-
this.isRotationStale = true;
|
|
2074
|
-
this.markDirty();
|
|
2075
|
-
}
|
|
2076
|
-
setPosition(xOrVec, y, z) {
|
|
2077
|
-
assignVec3(this.position, xOrVec, y, z);
|
|
2078
|
-
this.markDirty();
|
|
2079
|
-
return this;
|
|
2080
|
-
}
|
|
2081
|
-
setRotation(xOrVec, y, z) {
|
|
2082
|
-
assignVec3(this._rotation, xOrVec, y, z);
|
|
2083
|
-
this.quaternion.setFromEuler(this._rotation);
|
|
2084
|
-
this.isRotationStale = false;
|
|
2085
|
-
this.markDirty();
|
|
2086
|
-
return this;
|
|
2087
|
-
}
|
|
2088
|
-
setScale(xOrVec, y, z) {
|
|
2089
|
-
if (typeof xOrVec === 'number' && y === undefined && z === undefined)
|
|
2090
|
-
this.scale.setScalar(xOrVec, xOrVec, xOrVec);
|
|
2091
|
-
else
|
|
2092
|
-
assignVec3(this.scale, xOrVec, y, z);
|
|
2093
|
-
this.markDirty();
|
|
2094
|
-
return this;
|
|
2095
|
-
}
|
|
2096
|
-
scaleBy(factorOrVec) {
|
|
2097
|
-
if (factorOrVec instanceof Vec3)
|
|
2098
|
-
this.scale.multiply(factorOrVec);
|
|
2099
|
-
else
|
|
2100
|
-
this.scale.scale(factorOrVec);
|
|
2101
|
-
this.markDirty();
|
|
2102
|
-
return this;
|
|
2103
|
-
}
|
|
2104
|
-
setQuaternion(q) {
|
|
2105
|
-
this.quaternion.copy(q);
|
|
2106
|
-
this.rotationChanged();
|
|
2107
|
-
return this;
|
|
2108
|
-
}
|
|
2109
|
-
slerp(targetQuat, t) {
|
|
2110
|
-
this.quaternion.slerp(targetQuat, t);
|
|
2111
|
-
this.rotationChanged();
|
|
2112
|
-
return this;
|
|
2113
|
-
}
|
|
2114
|
-
rotateX(angle) {
|
|
2115
|
-
this.quaternion.rotateX(angle);
|
|
2116
|
-
this.rotationChanged();
|
|
2117
|
-
return this;
|
|
2118
|
-
}
|
|
2119
|
-
rotateY(angle) {
|
|
2120
|
-
this.quaternion.rotateY(angle);
|
|
2121
|
-
this.rotationChanged();
|
|
2122
|
-
return this;
|
|
2123
|
-
}
|
|
2124
|
-
rotateZ(angle) {
|
|
2125
|
-
this.quaternion.rotateZ(angle);
|
|
2126
|
-
this.rotationChanged();
|
|
2127
|
-
return this;
|
|
2128
|
-
}
|
|
2129
|
-
rotateOnAxis(axis, angle) {
|
|
2130
|
-
this.quaternion.multiply(Mesh.scratchQuat.setAxisAngle(axis, angle));
|
|
2131
|
-
this.rotationChanged();
|
|
2132
|
-
return this;
|
|
2133
|
-
}
|
|
2134
|
-
translate(xOrVec, y, z) {
|
|
2135
|
-
if (typeof xOrVec === 'number')
|
|
2136
|
-
this.position.add(Mesh.scratchVec.setScalar(xOrVec, y ?? 0, z ?? 0));
|
|
2137
|
-
else
|
|
2138
|
-
this.position.add(xOrVec instanceof Vec3 ? xOrVec : Mesh.scratchVec.setArray(xOrVec));
|
|
2139
|
-
this.markDirty();
|
|
2140
|
-
return this;
|
|
2141
|
-
}
|
|
2142
|
-
lookAt(target, up = Mesh.UP) {
|
|
2143
|
-
if (this.position.equals(target))
|
|
2144
|
-
return this;
|
|
2145
|
-
this.quaternion.lookAt(this.position, target, up);
|
|
2146
|
-
this.rotationChanged();
|
|
2147
|
-
return this;
|
|
2148
|
-
}
|
|
2149
|
-
updateModelMatrix() {
|
|
2150
|
-
if (!this.isTransformDirty)
|
|
2151
|
-
return false;
|
|
2152
|
-
this.modelMatrix.compose(this.position, this.quaternion, this.scale);
|
|
2153
|
-
this.isTransformDirty = false;
|
|
2154
|
-
this.isBoundsDirty = true;
|
|
2155
|
-
this.isNormalMatrixDirty = true;
|
|
2156
|
-
return true;
|
|
2157
|
-
}
|
|
2158
|
-
computeNormalMatrix() {
|
|
2159
|
-
if (!this.isNormalMatrixDirty)
|
|
2160
|
-
return this.normalMatrix;
|
|
2161
|
-
this.worldMatrix.toNormalMatrix(this.normalMatrix.toArray());
|
|
2162
|
-
this.isNormalMatrixDirty = false;
|
|
2163
|
-
return this.normalMatrix;
|
|
2164
|
-
}
|
|
2165
|
-
getWorldPosition(target = new Vec3()) {
|
|
2166
|
-
return this.worldMatrix.getTranslation(target);
|
|
2167
|
-
}
|
|
2168
|
-
getWorldDirection(target = new Vec3()) {
|
|
2169
|
-
return this.worldMatrix.getColumn(2, target).opposite().normalize();
|
|
2170
|
-
}
|
|
2171
|
-
distanceTo(target) {
|
|
2172
|
-
const targetPos = target instanceof Vec3 ? target : target.getWorldPosition();
|
|
2173
|
-
return this.getWorldPosition(Mesh.scratchVec).getDistance(targetPos);
|
|
2174
|
-
}
|
|
2175
|
-
isInsideBounds(min, max) {
|
|
2176
|
-
return this.getWorldPosition(Mesh.scratchVec).isInBounds(min, max);
|
|
2177
|
-
}
|
|
2178
|
-
computeBounds() {
|
|
2179
|
-
if (!this.isBoundsDirty)
|
|
2180
|
-
return;
|
|
2181
|
-
if (this.vertices && this.vertices.length > 0) {
|
|
2182
|
-
this.boundingBox.copy(this.localBoundingBox).applyMatrix4(this.worldMatrix);
|
|
2183
|
-
this.boundingSphere.copy(this.localBoundingSphere).applyMatrix4(this.worldMatrix);
|
|
2184
|
-
}
|
|
2185
|
-
else {
|
|
2186
|
-
const pos = this.getWorldPosition(Mesh.scratchVec);
|
|
2187
|
-
this.boundingBox.set(pos, pos);
|
|
2188
|
-
this.boundingSphere.set(pos, 0);
|
|
2443
|
+
if (this.nbPrograms < this.nbSubMeshes) {
|
|
2444
|
+
let program;
|
|
2445
|
+
let mat;
|
|
2446
|
+
if (typeof vertexShaderOrProgram !== 'string') {
|
|
2447
|
+
program = vertexShaderOrProgram;
|
|
2448
|
+
mat = (fragmentShaderOrMaterial instanceof Material) ? fragmentShaderOrMaterial : new Material();
|
|
2449
|
+
}
|
|
2450
|
+
else {
|
|
2451
|
+
program = Program.acquire(this.context, vertexShaderOrProgram, fragmentShaderOrMaterial);
|
|
2452
|
+
mat = (material instanceof Material) ? material : (fragmentShaderOrMaterial instanceof Material ? fragmentShaderOrMaterial : new Material());
|
|
2453
|
+
}
|
|
2454
|
+
if (!program)
|
|
2455
|
+
return false;
|
|
2456
|
+
this.programs.push(program);
|
|
2457
|
+
this.addMaterial(mat);
|
|
2458
|
+
this.createProgram();
|
|
2459
|
+
return true;
|
|
2189
2460
|
}
|
|
2190
|
-
|
|
2461
|
+
return false;
|
|
2191
2462
|
}
|
|
2192
|
-
|
|
2193
|
-
this.
|
|
2194
|
-
|
|
2463
|
+
clearPrograms() {
|
|
2464
|
+
for (const program of this.programs)
|
|
2465
|
+
Program.release(this.context, program);
|
|
2466
|
+
this.programs = [];
|
|
2467
|
+
this.nbPrograms = 0;
|
|
2468
|
+
this.materials = [];
|
|
2195
2469
|
}
|
|
2196
|
-
|
|
2197
|
-
this.
|
|
2198
|
-
|
|
2470
|
+
addMaterial(material) {
|
|
2471
|
+
if (material && this.materials.length < this.nbSubMeshes) {
|
|
2472
|
+
this.materials.push(material);
|
|
2473
|
+
syncMaterialUniforms(this.context, this.programs, material);
|
|
2474
|
+
return true;
|
|
2475
|
+
}
|
|
2476
|
+
return false;
|
|
2199
2477
|
}
|
|
2200
2478
|
raycast(ray, precise = false) {
|
|
2201
2479
|
if (!this.isActive())
|
|
2202
2480
|
return null;
|
|
2203
2481
|
const sphere = this.computeBoundingSphere();
|
|
2204
|
-
const hitPoint = ray.intersectSphere(sphere);
|
|
2482
|
+
const hitPoint = ray.intersectSphere(sphere, Mesh.scratchHit);
|
|
2205
2483
|
if (!hitPoint)
|
|
2206
2484
|
return null;
|
|
2207
2485
|
if (precise && this.vertices && (this.primitive === 'TRIANGLES' || this.primitive === 'TRIANGLE_STRIP'))
|
|
2208
|
-
return
|
|
2209
|
-
return { distance: ray.origin.getDistance(hitPoint), point: hitPoint };
|
|
2210
|
-
}
|
|
2211
|
-
raycastTriangles(ray) {
|
|
2212
|
-
const inverse = Mesh.scratchInverse.copy(this.worldMatrix).invertAffine();
|
|
2213
|
-
const localRay = Mesh.scratchRay.set(inverse.transformPoint(ray.origin, Mesh.scratchVec), inverse.transformDirection(ray.direction, Mesh.scratchDir));
|
|
2214
|
-
const strip = this.primitive === 'TRIANGLE_STRIP';
|
|
2215
|
-
let best = null;
|
|
2216
|
-
let bestDistance = Infinity;
|
|
2217
|
-
for (const subMesh of this.subMeshes) {
|
|
2218
|
-
const end = subMesh.start + subMesh.count;
|
|
2219
|
-
for (let i = subMesh.start; i + 2 < end; i += strip ? 1 : 3) {
|
|
2220
|
-
this.getVertex(i, Mesh.scratchA);
|
|
2221
|
-
if (strip && (i - subMesh.start) % 2 === 1) {
|
|
2222
|
-
this.getVertex(i + 2, Mesh.scratchB);
|
|
2223
|
-
this.getVertex(i + 1, Mesh.scratchC);
|
|
2224
|
-
}
|
|
2225
|
-
else {
|
|
2226
|
-
this.getVertex(i + 1, Mesh.scratchB);
|
|
2227
|
-
this.getVertex(i + 2, Mesh.scratchC);
|
|
2228
|
-
}
|
|
2229
|
-
const local = localRay.intersectTriangle(Mesh.scratchA, Mesh.scratchB, Mesh.scratchC, false, Mesh.scratchHit);
|
|
2230
|
-
if (!local)
|
|
2231
|
-
continue;
|
|
2232
|
-
const world = this.worldMatrix.transformPoint(local, Mesh.scratchWorldHit);
|
|
2233
|
-
const distance = ray.origin.getDistance(world);
|
|
2234
|
-
if (distance < bestDistance) {
|
|
2235
|
-
bestDistance = distance;
|
|
2236
|
-
best = (best ?? new Vec3()).copy(world);
|
|
2237
|
-
}
|
|
2238
|
-
}
|
|
2239
|
-
}
|
|
2240
|
-
return best ? { distance: bestDistance, point: best } : null;
|
|
2241
|
-
}
|
|
2242
|
-
getVertex(index, target) {
|
|
2243
|
-
const vertices = this.vertices;
|
|
2244
|
-
const size = this.itemSize || 3;
|
|
2245
|
-
const offset = (this.indices ? this.indices[index] : index) * size;
|
|
2246
|
-
return target.setScalar(vertices[offset], vertices[offset + 1], size > 2 ? vertices[offset + 2] : 0);
|
|
2486
|
+
return raycastTriangles(this, ray);
|
|
2487
|
+
return { distance: ray.origin.getDistance(hitPoint), point: new Vec3().copy(hitPoint) };
|
|
2247
2488
|
}
|
|
2248
2489
|
useBasicMaterial(options) {
|
|
2249
2490
|
const mat = new Material();
|
|
@@ -2287,12 +2528,8 @@ class Mesh {
|
|
|
2287
2528
|
this.context.deleteTexture(this.WebGLTexture);
|
|
2288
2529
|
this.WebGLTexture = null;
|
|
2289
2530
|
this.ownsTexture = false;
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
this.worldMatrix.copy(worldMatrix).multiply(this.modelMatrix);
|
|
2293
|
-
this.isBoundsDirty = true;
|
|
2294
|
-
this.isNormalMatrixDirty = true;
|
|
2295
|
-
this.isWorldDirty = false;
|
|
2531
|
+
this.textureSource = null;
|
|
2532
|
+
this.textureOptions = undefined;
|
|
2296
2533
|
}
|
|
2297
2534
|
setTexture(img, options) {
|
|
2298
2535
|
const texture = Texture.create(img, this.context, options);
|
|
@@ -2301,16 +2538,14 @@ class Mesh {
|
|
|
2301
2538
|
this.releaseTexture();
|
|
2302
2539
|
this.WebGLTexture = texture;
|
|
2303
2540
|
this.ownsTexture = true;
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2541
|
+
this.textureSource = img;
|
|
2542
|
+
this.textureOptions = options;
|
|
2543
|
+
syncTextureSampler(this.context, this.programs);
|
|
2307
2544
|
}
|
|
2308
2545
|
addCustomUniform(name, type, value) {
|
|
2309
2546
|
if (!this.customUniforms.hasOwnProperty(name)) {
|
|
2310
2547
|
this.customUniforms[name] = new Uniform(type, value);
|
|
2311
|
-
|
|
2312
|
-
if (program[name] === undefined)
|
|
2313
|
-
program[name] = this.context.getUniformLocation(program, 'u' + this.ucfirst(name));
|
|
2548
|
+
syncCustomUniform(this.context, this.programs, name);
|
|
2314
2549
|
}
|
|
2315
2550
|
}
|
|
2316
2551
|
setCustomUniform(name, value) {
|
|
@@ -2331,66 +2566,48 @@ class Mesh {
|
|
|
2331
2566
|
}
|
|
2332
2567
|
return indices instanceof Uint16Array ? indices : new Uint16Array(indices);
|
|
2333
2568
|
}
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
this.
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
this.
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
this.
|
|
2347
|
-
|
|
2348
|
-
this.addProgramUniform('sampler');
|
|
2349
|
-
this.addProgramUniform('hasTexture');
|
|
2350
|
-
this.addProgramUniform('nbLights');
|
|
2351
|
-
this.addProgramUniform('lightPosition');
|
|
2352
|
-
this.addProgramUniform('lightDiffuse');
|
|
2353
|
-
this.addProgramUniform('lightSpecular');
|
|
2354
|
-
this.addProgramUniform('lightDirection');
|
|
2355
|
-
this.addProgramUniform('lightAttenuation');
|
|
2356
|
-
this.addProgramUniform('lightCutoff');
|
|
2357
|
-
this.addProgramUniform('lightExponent');
|
|
2358
|
-
this.addProgramUniform('lightType');
|
|
2359
|
-
this.addProgramUniform('lightPositionType');
|
|
2360
|
-
this.addProgramUniform('lightDiffuseCutoff');
|
|
2361
|
-
this.addProgramUniform('lightSpecularExponent');
|
|
2362
|
-
this.addProgramUniform('ambientLight');
|
|
2363
|
-
this.addProgramUniform('ambientColor');
|
|
2364
|
-
this.addProgramUniform('cameraPosition');
|
|
2365
|
-
this.addProgramUniform('hasFog');
|
|
2366
|
-
this.addProgramUniform('fogColor');
|
|
2367
|
-
this.addProgramUniform('fogNear');
|
|
2368
|
-
this.addProgramUniform('fogFar');
|
|
2369
|
-
if (program.screenResolution) {
|
|
2370
|
-
this.renderer.useProgram(program);
|
|
2371
|
-
let viewport = this.context.getParameter(this.context.VIEWPORT);
|
|
2372
|
-
if (viewport && viewport.length >= 4)
|
|
2373
|
-
this.context.uniform2f(program.screenResolution, viewport[2], viewport[3]);
|
|
2374
|
-
}
|
|
2375
|
-
}
|
|
2376
|
-
for (let property in this.customUniforms)
|
|
2377
|
-
if (this.customUniforms.hasOwnProperty(property) && program[property] === undefined)
|
|
2378
|
-
this.addProgramUniform(property);
|
|
2379
|
-
for (let mat of this.materials)
|
|
2380
|
-
for (let property in mat.uniforms)
|
|
2381
|
-
if (mat.uniforms.hasOwnProperty(property) && program[property] === undefined)
|
|
2382
|
-
this.addProgramUniform(property);
|
|
2383
|
-
this.nbPrograms++;
|
|
2569
|
+
createBuffers() {
|
|
2570
|
+
this.vertexBuffer = this.vertices
|
|
2571
|
+
? this.renderer.createBuffer('ARRAY_BUFFER', this.vertices instanceof Float32Array ? this.vertices : new Float32Array(this.vertices), 'STATIC_DRAW')
|
|
2572
|
+
: null;
|
|
2573
|
+
this.indexType = this.context.UNSIGNED_SHORT;
|
|
2574
|
+
this.indexBuffer = this.indices
|
|
2575
|
+
? this.renderer.createBuffer('ELEMENT_ARRAY_BUFFER', this.createIndexData(this.indices), 'STATIC_DRAW')
|
|
2576
|
+
: null;
|
|
2577
|
+
this.normalBuffer = this.normals
|
|
2578
|
+
? this.renderer.createBuffer('ARRAY_BUFFER', this.normals instanceof Float32Array ? this.normals : new Float32Array(this.normals), 'STATIC_DRAW')
|
|
2579
|
+
: null;
|
|
2580
|
+
this.texCoordBuffer = this.uvs
|
|
2581
|
+
? this.renderer.createBuffer('ARRAY_BUFFER', this.uvs instanceof Float32Array ? this.uvs : new Float32Array(this.uvs), 'STATIC_DRAW')
|
|
2582
|
+
: null;
|
|
2384
2583
|
}
|
|
2385
|
-
|
|
2386
|
-
this.
|
|
2584
|
+
restore() {
|
|
2585
|
+
this.createBuffers();
|
|
2586
|
+
const sources = this.programs.map((program) => Program.sources(program));
|
|
2587
|
+
this.programs = [];
|
|
2588
|
+
this.nbPrograms = 0;
|
|
2589
|
+
sources.forEach((source) => {
|
|
2590
|
+
if (!source)
|
|
2591
|
+
return;
|
|
2592
|
+
const program = Program.acquire(this.context, source.vertex, source.fragment);
|
|
2593
|
+
if (!program)
|
|
2594
|
+
return;
|
|
2595
|
+
this.programs.push(program);
|
|
2596
|
+
this.createProgram();
|
|
2597
|
+
});
|
|
2598
|
+
if (this.programs.length !== sources.length)
|
|
2599
|
+
this.setInactive();
|
|
2600
|
+
if (this.ownsTexture && this.textureSource) {
|
|
2601
|
+
const texture = Texture.create(this.textureSource, this.context, this.textureOptions);
|
|
2602
|
+
this.WebGLTexture = texture;
|
|
2603
|
+
this.ownsTexture = texture !== null;
|
|
2604
|
+
}
|
|
2605
|
+
for (const child of this.children)
|
|
2606
|
+
child.restore();
|
|
2387
2607
|
}
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
if (loc === null)
|
|
2392
|
-
loc = this.context.getUniformLocation(this.programs[this.nbPrograms], ucName + '[0]');
|
|
2393
|
-
this.programs[this.nbPrograms][name] = loc;
|
|
2608
|
+
createProgram() {
|
|
2609
|
+
initProgramLocations(this.context, this.programs[this.nbPrograms], this.customUniforms, this.materials);
|
|
2610
|
+
this.nbPrograms++;
|
|
2394
2611
|
}
|
|
2395
2612
|
activateBlendMode() {
|
|
2396
2613
|
this.blendMode = true;
|
|
@@ -2409,178 +2626,17 @@ class Mesh {
|
|
|
2409
2626
|
this.depthWrite = enabled;
|
|
2410
2627
|
return this;
|
|
2411
2628
|
}
|
|
2412
|
-
computeWorldMatrix(graph) {
|
|
2413
|
-
const modelChanged = this.updateModelMatrix();
|
|
2414
|
-
const changed = this.isWorldDirty || modelChanged;
|
|
2415
|
-
if (changed)
|
|
2416
|
-
this.setWorldMatrix(graph.getWorldMatrix());
|
|
2417
|
-
graph.pushModelMatrix(this.worldMatrix);
|
|
2418
|
-
for (let child of this.children) {
|
|
2419
|
-
if (changed)
|
|
2420
|
-
child.isWorldDirty = true;
|
|
2421
|
-
child.computeWorldMatrix(graph);
|
|
2422
|
-
}
|
|
2423
|
-
graph.popModelMatrix();
|
|
2424
|
-
}
|
|
2425
2629
|
render(projectionMatrix, viewMatrix, lights, time, blendMode, cameraPosition, fog, frame) {
|
|
2426
2630
|
if (!this.isActive())
|
|
2427
2631
|
return;
|
|
2428
2632
|
this.renderSelf(projectionMatrix, viewMatrix, lights, time, blendMode, cameraPosition, fog, frame);
|
|
2429
|
-
for (
|
|
2633
|
+
for (const child of this.children)
|
|
2430
2634
|
child.render(projectionMatrix, viewMatrix, lights, time, blendMode, cameraPosition, fog, frame);
|
|
2431
2635
|
}
|
|
2432
2636
|
renderSelf(projectionMatrix, viewMatrix, lights, time, blendMode, cameraPosition, fog, frame) {
|
|
2433
2637
|
if (!this.isActive() || this.nbPrograms === 0 || this.blendMode !== blendMode)
|
|
2434
2638
|
return;
|
|
2435
|
-
|
|
2436
|
-
const renderer = this.renderer;
|
|
2437
|
-
const state = renderer.state;
|
|
2438
|
-
for (let i = 0; i < this.nbSubMeshes; i++) {
|
|
2439
|
-
const program = this.programs[i] || this.programs[this.nbPrograms - 1];
|
|
2440
|
-
renderer.useProgram(program);
|
|
2441
|
-
if (!frame || state.frameOfProgram.get(program) !== frame.id) {
|
|
2442
|
-
if (frame)
|
|
2443
|
-
state.frameOfProgram.set(program, frame.id);
|
|
2444
|
-
this.uploadFrameUniforms(program, projectionMatrix, viewMatrix, lights, time, cameraPosition, fog, frame);
|
|
2445
|
-
}
|
|
2446
|
-
const material = this.materials[i] || this.materials[this.materials.length - 1];
|
|
2447
|
-
if (material) {
|
|
2448
|
-
material.updateUniforms();
|
|
2449
|
-
if (state.materialOfProgram.get(program) !== material || state.materialVersionOfProgram.get(program) !== material.version) {
|
|
2450
|
-
state.materialOfProgram.set(program, material);
|
|
2451
|
-
state.materialVersionOfProgram.set(program, material.version);
|
|
2452
|
-
const materialUniforms = material.uniforms;
|
|
2453
|
-
for (let property in materialUniforms)
|
|
2454
|
-
if (materialUniforms.hasOwnProperty(property) && program[property])
|
|
2455
|
-
uploadUniform(context, program[property], materialUniforms[property]);
|
|
2456
|
-
}
|
|
2457
|
-
}
|
|
2458
|
-
let attributeMask = 0;
|
|
2459
|
-
if (this.indices)
|
|
2460
|
-
renderer.bindBuffer('ELEMENT_ARRAY_BUFFER', this.indexBuffer);
|
|
2461
|
-
if (program.vertexPosition !== -1 && program.vertexPosition !== undefined && this.vertexBuffer) {
|
|
2462
|
-
renderer.bindBuffer('ARRAY_BUFFER', this.vertexBuffer);
|
|
2463
|
-
renderer.vertexAttribPointer(program.vertexPosition, this.itemSize, 'FLOAT', false, 0, 0);
|
|
2464
|
-
attributeMask |= 1 << program.vertexPosition;
|
|
2465
|
-
}
|
|
2466
|
-
if (this.normals && program.vertexNormal !== -1 && program.vertexNormal !== undefined) {
|
|
2467
|
-
renderer.bindBuffer('ARRAY_BUFFER', this.normalBuffer);
|
|
2468
|
-
renderer.vertexAttribPointer(program.vertexNormal, 3, 'FLOAT', false, 0, 0);
|
|
2469
|
-
attributeMask |= 1 << program.vertexNormal;
|
|
2470
|
-
}
|
|
2471
|
-
if (this.uvs && program.textureCoord !== -1 && program.textureCoord !== undefined) {
|
|
2472
|
-
renderer.bindBuffer('ARRAY_BUFFER', this.texCoordBuffer);
|
|
2473
|
-
renderer.vertexAttribPointer(program.textureCoord, 2, 'FLOAT', false, 0, 0);
|
|
2474
|
-
attributeMask |= 1 << program.textureCoord;
|
|
2475
|
-
}
|
|
2476
|
-
renderer.setAttributeMask(attributeMask);
|
|
2477
|
-
if (this.WebGLTexture) {
|
|
2478
|
-
context.activeTexture(context.TEXTURE0);
|
|
2479
|
-
context.bindTexture(context.TEXTURE_2D, this.WebGLTexture);
|
|
2480
|
-
if (program.sampler)
|
|
2481
|
-
context.uniform1i(program.sampler, 0);
|
|
2482
|
-
if (program.hasTexture)
|
|
2483
|
-
context.uniform1i(program.hasTexture, 1);
|
|
2484
|
-
}
|
|
2485
|
-
else if (program.hasTexture)
|
|
2486
|
-
context.uniform1i(program.hasTexture, 0);
|
|
2487
|
-
if (program.modelMatrix)
|
|
2488
|
-
context.uniformMatrix4fv(program.modelMatrix, false, this.worldMatrix.toArray());
|
|
2489
|
-
if (program.normalMatrix) {
|
|
2490
|
-
this.computeNormalMatrix();
|
|
2491
|
-
context.uniformMatrix3fv(program.normalMatrix, false, this.normalMatrix.toArray());
|
|
2492
|
-
}
|
|
2493
|
-
for (let property in this.customUniforms)
|
|
2494
|
-
if (this.customUniforms.hasOwnProperty(property) && program[property])
|
|
2495
|
-
uploadUniform(context, program[property], this.customUniforms[property]);
|
|
2496
|
-
renderer[this.drawMethod](this.primitive, this.subMeshes[i], this.indexType);
|
|
2497
|
-
}
|
|
2498
|
-
}
|
|
2499
|
-
static lightTypeValue(type) {
|
|
2500
|
-
return type === 'directional' ? 0 : type === 'point' ? 1 : 2;
|
|
2501
|
-
}
|
|
2502
|
-
static packVec4(xyz, w) {
|
|
2503
|
-
const count = Math.floor(xyz.length / 3);
|
|
2504
|
-
const packed = new Float32Array(count * 4);
|
|
2505
|
-
for (let i = 0; i < count; i++) {
|
|
2506
|
-
packed[i * 4] = xyz[i * 3];
|
|
2507
|
-
packed[i * 4 + 1] = xyz[i * 3 + 1];
|
|
2508
|
-
packed[i * 4 + 2] = xyz[i * 3 + 2];
|
|
2509
|
-
packed[i * 4 + 3] = w[i] || 0;
|
|
2510
|
-
}
|
|
2511
|
-
return packed;
|
|
2512
|
-
}
|
|
2513
|
-
uploadFrameUniforms(program, projectionMatrix, viewMatrix, lights, time, cameraPosition, fog, frame) {
|
|
2514
|
-
const context = this.context;
|
|
2515
|
-
if (program.projectionMatrix)
|
|
2516
|
-
context.uniformMatrix4fv(program.projectionMatrix, false, projectionMatrix);
|
|
2517
|
-
if (program.viewMatrix)
|
|
2518
|
-
context.uniformMatrix4fv(program.viewMatrix, false, viewMatrix);
|
|
2519
|
-
if (program.time)
|
|
2520
|
-
context.uniform1f(program.time, time);
|
|
2521
|
-
if (frame && frame.resolution && program.screenResolution)
|
|
2522
|
-
context.uniform2f(program.screenResolution, frame.resolution.x, frame.resolution.y);
|
|
2523
|
-
if (lights) {
|
|
2524
|
-
if (program.nbLights && lights.type)
|
|
2525
|
-
context.uniform1i(program.nbLights, lights.type.length);
|
|
2526
|
-
if (program.lightPosition && lights.position && lights.position.length > 0)
|
|
2527
|
-
context.uniform3fv(program.lightPosition, lights.floatPosition || new Float32Array(lights.position));
|
|
2528
|
-
if (program.lightDiffuse && lights.diffuse && lights.diffuse.length > 0)
|
|
2529
|
-
context.uniform3fv(program.lightDiffuse, lights.floatDiffuse || new Float32Array(lights.diffuse));
|
|
2530
|
-
if (program.lightSpecular && lights.specular && lights.specular.length > 0)
|
|
2531
|
-
context.uniform3fv(program.lightSpecular, lights.floatSpecular || new Float32Array(lights.specular));
|
|
2532
|
-
if (program.lightDirection && lights.direction && lights.direction.length > 0)
|
|
2533
|
-
context.uniform3fv(program.lightDirection, lights.floatDirection || new Float32Array(lights.direction));
|
|
2534
|
-
if (program.lightAttenuation && (lights.floatAttenuation || lights.constantAttenuation?.length)) {
|
|
2535
|
-
let attArr = lights.floatAttenuation;
|
|
2536
|
-
if (!attArr) {
|
|
2537
|
-
const att = [];
|
|
2538
|
-
for (let k = 0; k < lights.constantAttenuation.length; k++)
|
|
2539
|
-
att.push(lights.constantAttenuation[k] || 0, lights.linearAttenuation[k] || 0, lights.quadraticAttenuation[k] || 0);
|
|
2540
|
-
attArr = new Float32Array(att);
|
|
2541
|
-
}
|
|
2542
|
-
context.uniform3fv(program.lightAttenuation, attArr);
|
|
2543
|
-
}
|
|
2544
|
-
if (program.lightCutoff && lights.cutoff && lights.cutoff.length > 0)
|
|
2545
|
-
context.uniform1fv(program.lightCutoff, lights.floatCutoff || new Float32Array(lights.cutoff));
|
|
2546
|
-
if (program.lightExponent && lights.exponent && lights.exponent.length > 0)
|
|
2547
|
-
context.uniform1fv(program.lightExponent, lights.floatExponent || new Float32Array(lights.exponent));
|
|
2548
|
-
if (program.lightType && (lights.floatType || lights.type?.length)) {
|
|
2549
|
-
let typeArr = lights.floatType;
|
|
2550
|
-
if (!typeArr)
|
|
2551
|
-
typeArr = new Float32Array(lights.type.map(t => t === 'directional' ? 0 : t === 'point' ? 1 : 2));
|
|
2552
|
-
context.uniform1fv(program.lightType, typeArr);
|
|
2553
|
-
}
|
|
2554
|
-
if (program.lightPositionType && lights.position && lights.position.length > 0)
|
|
2555
|
-
context.uniform4fv(program.lightPositionType, lights.packedPosition || Mesh.packVec4(lights.position, (lights.type || []).map(Mesh.lightTypeValue)));
|
|
2556
|
-
if (program.lightDiffuseCutoff && lights.diffuse && lights.diffuse.length > 0)
|
|
2557
|
-
context.uniform4fv(program.lightDiffuseCutoff, lights.packedDiffuse || Mesh.packVec4(lights.diffuse, lights.cutoff || []));
|
|
2558
|
-
if (program.lightSpecularExponent && lights.specular && lights.specular.length > 0)
|
|
2559
|
-
context.uniform4fv(program.lightSpecularExponent, lights.packedSpecular || Mesh.packVec4(lights.specular, lights.exponent || []));
|
|
2560
|
-
if (program.ambientLight && lights.ambient)
|
|
2561
|
-
context.uniform3fv(program.ambientLight, lights.floatAmbient || new Float32Array(lights.ambient));
|
|
2562
|
-
}
|
|
2563
|
-
if (program.cameraPosition) {
|
|
2564
|
-
let camera = cameraPosition;
|
|
2565
|
-
if (!camera && viewMatrix) {
|
|
2566
|
-
Mesh.scratchView.toArray().set(viewMatrix);
|
|
2567
|
-
camera = Mesh.scratchView.invertAffine().getTranslation(Mesh.scratchCamera);
|
|
2568
|
-
}
|
|
2569
|
-
if (camera)
|
|
2570
|
-
context.uniform3f(program.cameraPosition, camera.x, camera.y, camera.z);
|
|
2571
|
-
}
|
|
2572
|
-
if (program.hasFog)
|
|
2573
|
-
if (fog) {
|
|
2574
|
-
context.uniform1i(program.hasFog, 1);
|
|
2575
|
-
if (program.fogColor)
|
|
2576
|
-
context.uniform3f(program.fogColor, fog.color.x, fog.color.y, fog.color.z);
|
|
2577
|
-
if (program.fogNear)
|
|
2578
|
-
context.uniform1f(program.fogNear, fog.near);
|
|
2579
|
-
if (program.fogFar)
|
|
2580
|
-
context.uniform1f(program.fogFar, fog.far);
|
|
2581
|
-
}
|
|
2582
|
-
else
|
|
2583
|
-
context.uniform1i(program.hasFog, 0);
|
|
2639
|
+
this.renderer.drawMesh(this, projectionMatrix, viewMatrix, lights, time, cameraPosition, fog, frame);
|
|
2584
2640
|
}
|
|
2585
2641
|
destroy() {
|
|
2586
2642
|
this.clearPrograms();
|
|
@@ -2593,15 +2649,12 @@ class Mesh {
|
|
|
2593
2649
|
if (this.texCoordBuffer)
|
|
2594
2650
|
this.renderer.deleteBuffer(this.texCoordBuffer), this.texCoordBuffer = null;
|
|
2595
2651
|
this.releaseTexture();
|
|
2596
|
-
for (
|
|
2652
|
+
for (const child of this.children) {
|
|
2597
2653
|
child.parent = null;
|
|
2598
2654
|
child.destroy();
|
|
2599
2655
|
}
|
|
2600
2656
|
this.children = [];
|
|
2601
2657
|
}
|
|
2602
|
-
ucfirst(string) {
|
|
2603
|
-
return string.charAt(0).toUpperCase() + string.slice(1);
|
|
2604
|
-
}
|
|
2605
2658
|
}
|
|
2606
2659
|
|
|
2607
2660
|
class BasicMesh {
|
|
@@ -2625,22 +2678,23 @@ class BasicMesh {
|
|
|
2625
2678
|
|
|
2626
2679
|
class Line extends BasicMesh {
|
|
2627
2680
|
thickness;
|
|
2628
|
-
constructor(vertices, thickness) {
|
|
2681
|
+
constructor(vertices = [], thickness = 2.0) {
|
|
2629
2682
|
super();
|
|
2630
|
-
this.thickness = thickness
|
|
2631
|
-
|
|
2632
|
-
|
|
2683
|
+
this.thickness = (thickness || 2.0) * 0.5;
|
|
2684
|
+
this.itemSize = 3;
|
|
2685
|
+
this.primitive = 'LINE_STRIP';
|
|
2686
|
+
this.setPoints(vertices);
|
|
2687
|
+
}
|
|
2688
|
+
setPoints(vertices) {
|
|
2689
|
+
const flat = [];
|
|
2690
|
+
if (vertices)
|
|
2633
2691
|
for (const v of vertices)
|
|
2634
2692
|
flat.push(v.x, v.y, v.z);
|
|
2635
|
-
|
|
2636
|
-
}
|
|
2637
|
-
else
|
|
2638
|
-
this.vertices = vertices || [];
|
|
2639
|
-
this.itemSize = 3;
|
|
2693
|
+
this.vertices = flat;
|
|
2640
2694
|
this.subMeshes = [
|
|
2641
2695
|
new SubMesh(0, this.vertices.length / this.itemSize)
|
|
2642
2696
|
];
|
|
2643
|
-
this
|
|
2697
|
+
return this;
|
|
2644
2698
|
}
|
|
2645
2699
|
}
|
|
2646
2700
|
|
|
@@ -2666,21 +2720,17 @@ class FullscreenQuad extends BasicMesh {
|
|
|
2666
2720
|
}
|
|
2667
2721
|
|
|
2668
2722
|
class Quad extends BasicMesh {
|
|
2669
|
-
constructor(
|
|
2723
|
+
constructor(size = new Vec2(2.0, 2.0)) {
|
|
2670
2724
|
super();
|
|
2671
2725
|
let width;
|
|
2672
2726
|
let h;
|
|
2673
|
-
if (
|
|
2674
|
-
width =
|
|
2675
|
-
h =
|
|
2676
|
-
}
|
|
2677
|
-
else if (sizeOrWidth instanceof Rect) {
|
|
2678
|
-
width = sizeOrWidth.size.x * 0.5;
|
|
2679
|
-
h = sizeOrWidth.size.y * 0.5;
|
|
2727
|
+
if (size instanceof Rect) {
|
|
2728
|
+
width = size.size.x * 0.5;
|
|
2729
|
+
h = size.size.y * 0.5;
|
|
2680
2730
|
}
|
|
2681
2731
|
else {
|
|
2682
|
-
width =
|
|
2683
|
-
h =
|
|
2732
|
+
width = size.x * 0.5;
|
|
2733
|
+
h = size.y * 0.5;
|
|
2684
2734
|
}
|
|
2685
2735
|
this.vertices = [width, -h, 0.0,
|
|
2686
2736
|
-width, -h, 0.0,
|
|
@@ -2702,21 +2752,17 @@ class Quad extends BasicMesh {
|
|
|
2702
2752
|
|
|
2703
2753
|
class MultiQuad extends BasicMesh {
|
|
2704
2754
|
quad;
|
|
2705
|
-
constructor(
|
|
2755
|
+
constructor(size = new Vec2(2.0, 2.0)) {
|
|
2706
2756
|
super();
|
|
2707
2757
|
let width;
|
|
2708
2758
|
let h;
|
|
2709
|
-
if (
|
|
2710
|
-
width =
|
|
2711
|
-
h =
|
|
2712
|
-
}
|
|
2713
|
-
else if (sizeOrWidth instanceof Rect) {
|
|
2714
|
-
width = sizeOrWidth.size.x * 0.5;
|
|
2715
|
-
h = sizeOrWidth.size.y * 0.5;
|
|
2759
|
+
if (size instanceof Rect) {
|
|
2760
|
+
width = size.size.x * 0.5;
|
|
2761
|
+
h = size.size.y * 0.5;
|
|
2716
2762
|
}
|
|
2717
2763
|
else {
|
|
2718
|
-
width =
|
|
2719
|
-
h =
|
|
2764
|
+
width = size.x * 0.5;
|
|
2765
|
+
h = size.y * 0.5;
|
|
2720
2766
|
}
|
|
2721
2767
|
this.quad = {
|
|
2722
2768
|
vertices: new Float32Array([width, -h, 0.0,
|
|
@@ -2758,21 +2804,14 @@ class MultiQuad extends BasicMesh {
|
|
|
2758
2804
|
}
|
|
2759
2805
|
|
|
2760
2806
|
class Cube extends BasicMesh {
|
|
2761
|
-
constructor(
|
|
2807
|
+
constructor(size = new Vec3(2.0, 2.0, 2.0), flat = false) {
|
|
2762
2808
|
super();
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
}
|
|
2770
|
-
else {
|
|
2771
|
-
const s = sizeOrDimensions ? sizeOrDimensions * 0.5 : 1.0;
|
|
2772
|
-
sx = s;
|
|
2773
|
-
sy = s;
|
|
2774
|
-
sz = s;
|
|
2775
|
-
}
|
|
2809
|
+
const halfX = (size ? size.x : 2.0) * 0.5;
|
|
2810
|
+
const halfY = (size ? size.y : 2.0) * 0.5;
|
|
2811
|
+
const halfZ = (size ? size.z : 2.0) * 0.5;
|
|
2812
|
+
const sx = halfX || 1.0;
|
|
2813
|
+
const sy = halfY || 1.0;
|
|
2814
|
+
const sz = halfZ || 1.0;
|
|
2776
2815
|
if (flat) {
|
|
2777
2816
|
this.vertices = [
|
|
2778
2817
|
-sx, -sy, sz, sx, -sy, sz, sx, sy, sz, -sx, sy, sz,
|
|
@@ -2835,7 +2874,7 @@ class Cube extends BasicMesh {
|
|
|
2835
2874
|
}
|
|
2836
2875
|
|
|
2837
2876
|
class Sphere extends BasicMesh {
|
|
2838
|
-
constructor() {
|
|
2877
|
+
constructor(scale = new Vec3(1.0, 1.0, 1.0)) {
|
|
2839
2878
|
super();
|
|
2840
2879
|
this.vertices = [-0.7071, -0.7071, 0, -0.5449, -0.8315, 0.1084, -0.5556, -0.8315, 0, -0.8315, 0.5556, 0, -0.9061, 0.3827, 0.1802, -0.9239, 0.3827, 0, -0.3753, -0.9239, 0.0747, -0.3827, -0.9239, 0, -0.9619, 0.1951, 0.1913, -0.9808, 0.1951, 0, -0.1913, -0.9808, 0.0381, -0.1951, -0.9808, 0, -0.9808, 0, 0.1951, -1, 0, 0, -0.1951, 0.9808, 0, 0, 1, 0, -0.1913, 0.9808, 0.0381, 0, -1, 0, -0.9619, -0.1951, 0.1913, -0.9808, -0.1951, 0, -0.3753, 0.9239, 0.0747, -0.3827, 0.9239, 0, -0.9239, -0.3827, 0, -0.9061, -0.3827, 0.1802, -0.5556, 0.8315, 0, -0.5449, 0.8315, 0.1084, -0.8155, -0.5556, 0.1622, -0.8315, -0.5556, 0, -0.6935, 0.7071, 0.1379, -0.7071, 0.7071, 0, -0.6935, -0.7071, 0.1379, -0.8155, 0.5556, 0.1622, -0.7682, -0.5556, 0.3182, -0.6533, -0.7071, 0.2706, -0.7682, 0.5556, 0.3182, -0.5133, -0.8315, 0.2126, -0.8536, 0.3827, 0.3536, -0.3536, -0.9239, 0.1464, -0.9061, 0.1951, 0.3753, -0.1802, -0.9808, 0.0747, -0.9239, 0, 0.3827, -0.1802, 0.9808, 0.0747, -0.9061, -0.1951, 0.3753, -0.3536, 0.9239, 0.1464, -0.8536, -0.3827, 0.3536, -0.5133, 0.8315, 0.2126, -0.6533, 0.7071, 0.2706, -0.1622, 0.9808, 0.1084, -0.1622, -0.9808, 0.1084, -0.8155, -0.1951, 0.5449, -0.3182, 0.9239, 0.2126, -0.7682, -0.3827, 0.5133, -0.4619, 0.8315, 0.3087, -0.6913, -0.5556, 0.4619, -0.5879, 0.7071, 0.3928, -0.5879, -0.7071, 0.3928, -0.6913, 0.5556, 0.4619, -0.4619, -0.8315, 0.3087, -0.7682, 0.3827, 0.5133, -0.3182, -0.9239, 0.2126, -0.8155, 0.1951, 0.5449, -0.8315, 0, 0.5556, -0.5879, 0.5556, 0.5879, -0.3928, -0.8315, 0.3928, -0.6533, 0.3827, 0.6533, -0.2706, -0.9239, 0.2706, -0.6935, 0.1951, 0.6935, -0.1379, -0.9808, 0.1379, -0.7071, 0, 0.7071, -0.1379, 0.9808, 0.1379, -0.6935, -0.1951, 0.6935, -0.2706, 0.9239, 0.2706, -0.6533, -0.3827, 0.6533, -0.3928, 0.8315, 0.3928, -0.5879, -0.5556, 0.5879, -0.5, 0.7071, 0.5, -0.5, -0.7071, 0.5, -0.5449, -0.1951, 0.8155, -0.2126, 0.9239, 0.3182, -0.5133, -0.3827, 0.7682, -0.3087, 0.8315, 0.4619, -0.4619, -0.5556, 0.6913, -0.3928, 0.7071, 0.5879, -0.3928, -0.7071, 0.5879, -0.4619, 0.5556, 0.6913, -0.3087, -0.8315, 0.4619, -0.5133, 0.3827, 0.7682, -0.2126, -0.9239, 0.3182, -0.5449, 0.1951, 0.8155, -0.1084, -0.9808, 0.1622, -0.5556, 0, 0.8315, -0.1084, 0.9808, 0.1622, -0.2126, -0.8315, 0.5133, -0.3536, 0.3827, 0.8536, -0.1464, -0.9239, 0.3536, -0.3753, 0.1951, 0.9061, -0.0747, -0.9808, 0.1802, -0.3827, 0, 0.9239, -0.0747, 0.9808, 0.1802, -0.3753, -0.1951, 0.9061, -0.1464, 0.9239, 0.3536, -0.3536, -0.3827, 0.8536, -0.2126, 0.8315, 0.5133, -0.3182, -0.5556, 0.7682, -0.2706, 0.7071, 0.6533, -0.2706, -0.7071, 0.6533, -0.3182, 0.5556, 0.7682, -0.1802, -0.3827, 0.9061, -0.1084, 0.8315, 0.5449, -0.1622, -0.5556, 0.8155, -0.1379, 0.7071, 0.6935, -0.1379, -0.7071, 0.6935, -0.1622, 0.5556, 0.8155, -0.1084, -0.8315, 0.5449, -0.1802, 0.3827, 0.9061, -0.0747, -0.9239, 0.3753, -0.1913, 0.1951, 0.9619, -0.0381, -0.9808, 0.1913, -0.1951, 0, 0.9808, -0.0381, 0.9808, 0.1913, -0.1913, -0.1951, 0.9619, -0.0747, 0.9239, 0.3753, 0, -0.8315, 0.5556, 0, -0.9239, 0.3827, 0, 0.3827, 0.9239, 0, 0.1951, 0.9808, 0, -0.9808, 0.1951, 0, 0, 1, 0, 0.9808, 0.1951, 0, -0.1951, 0.9808, 0, 0.9239, 0.3827, 0, -0.3827, 0.9239, 0, 0.8315, 0.5556, 0, -0.5556, 0.8315, 0, 0.7071, 0.7071, 0, -0.7071, 0.7071, 0, 0.5556, 0.8315, 0.1084, 0.8315, 0.5449, 0.1802, -0.3827, 0.9061, 0.1622, -0.5556, 0.8155, 0.138, 0.7071, 0.6935, 0.138, -0.7071, 0.6935, 0.1622, 0.5556, 0.8155, 0.1084, -0.8315, 0.5449, 0.1802, 0.3827, 0.9061, 0.0747, -0.9239, 0.3753, 0.1913, 0.1951, 0.9619, 0.0381, -0.9808, 0.1913, 0.1951, 0, 0.9808, 0.0381, 0.9808, 0.1913, 0.1913, -0.1951, 0.9619, 0.0747, 0.9239, 0.3753, 0.3536, 0.3827, 0.8536, 0.3753, 0.1951, 0.9061, 0.0747, -0.9808, 0.1802, 0.3827, 0, 0.9239, 0.0747, 0.9808, 0.1802, 0.3753, -0.1951, 0.9061, 0.1464, 0.9239, 0.3536, 0.3536, -0.3827, 0.8536, 0.2126, 0.8315, 0.5133, 0.3182, -0.5556, 0.7682, 0.2706, 0.7071, 0.6533, 0.2706, -0.7071, 0.6533, 0.3182, 0.5556, 0.7682, 0.2126, -0.8315, 0.5133, 0.1464, -0.9239, 0.3536, 0.5133, -0.3827, 0.7682, 0.4619, -0.5556, 0.6913, 0.3928, 0.7071, 0.5879, 0.3928, -0.7071, 0.5879, 0.4619, 0.5556, 0.6913, 0.3087, -0.8315, 0.4619, 0.5133, 0.3827, 0.7682, 0.2126, -0.9239, 0.3182, 0.5449, 0.1951, 0.8155, 0.1084, -0.9808, 0.1622, 0.5556, 0, 0.8315, 0.1084, 0.9808, 0.1622, 0.5449, -0.1951, 0.8155, 0.2126, 0.9239, 0.3182, 0.3087, 0.8315, 0.4619, 0.2706, -0.9239, 0.2706, 0.138, -0.9808, 0.1379, 0.7071, 0, 0.7071, 0.138, 0.9808, 0.1379, 0.6935, -0.1951, 0.6935, 0.2706, 0.9239, 0.2706, 0.6533, -0.3827, 0.6533, 0.3928, 0.8315, 0.3928, 0.5879, -0.5556, 0.5879, 0.5, 0.7071, 0.5, 0.5, -0.7071, 0.5, 0.5879, 0.5556, 0.5879, 0.3928, -0.8315, 0.3928, 0.6533, 0.3827, 0.6533, 0.6935, 0.1951, 0.6935, 0.4619, 0.8315, 0.3087, 0.5879, 0.7071, 0.3928, 0.5879, -0.7071, 0.3928, 0.6913, 0.5556, 0.4619, 0.4619, -0.8315, 0.3087, 0.7682, 0.3827, 0.5133, 0.3182, -0.9239, 0.2126, 0.8155, 0.1951, 0.5449, 0.1622, -0.9808, 0.1084, 0.8315, 0, 0.5556, 0.1622, 0.9808, 0.1084, 0.8155, -0.1951, 0.5449, 0.3182, 0.9239, 0.2126, 0.7682, -0.3827, 0.5133, 0.6913, -0.5556, 0.4619, 0.9239, 0, 0.3827, 0.1802, 0.9808, 0.0747, 0.1802, -0.9808, 0.0747, 0.9061, -0.1951, 0.3753, 0.3536, 0.9239, 0.1464, 0.8536, -0.3827, 0.3536, 0.5133, 0.8315, 0.2126, 0.7682, -0.5556, 0.3182, 0.6533, 0.7071, 0.2706, 0.6533, -0.7071, 0.2706, 0.7682, 0.5556, 0.3182, 0.5133, -0.8315, 0.2126, 0.8536, 0.3827, 0.3536, 0.3536, -0.9239, 0.1464, 0.9061, 0.1951, 0.3753, 0.6935, -0.7071, 0.1379, 0.8155, 0.5556, 0.1622, 0.5449, -0.8315, 0.1084, 0.9061, 0.3827, 0.1802, 0.3753, -0.9239, 0.0747, 0.9619, 0.1951, 0.1913, 0.1913, -0.9808, 0.0381, 0.9808, 0, 0.1951, 0.1913, 0.9808, 0.0381, 0.9619, -0.1951, 0.1913, 0.3753, 0.9239, 0.0747, 0.9061, -0.3827, 0.1802, 0.5449, 0.8315, 0.1084, 0.8155, -0.5556, 0.1622, 0.6935, 0.7071, 0.1379, 0.1951, -0.9808, 0, 0.9808, -0.1951, 0, 0.3827, 0.9239, 0, 0.9239, -0.3827, 0, 0.5556, 0.8315, 0, 0.8315, -0.5556, 0, 0.7071, 0.7071, 0, 0.7071, -0.7071, 0, 0.8315, 0.5556, 0, 0.5556, -0.8315, 0, 0.9239, 0.3827, 0, 0.3827, -0.9239, 0, 0.9808, 0.1951, 0, 1, 0, 0, 0.1951, 0.9808, 0, 0.5449, -0.8315, -0.1084, 0.9061, 0.3827, -0.1802, 0.3753, -0.9239, -0.0747, 0.9619, 0.1951, -0.1913, 0.1913, -0.9808, -0.0381, 0.9808, 0, -0.1951, 0.1913, 0.9808, -0.0381, 0.9619, -0.1951, -0.1913, 0.3753, 0.9239, -0.0747, 0.9061, -0.3827, -0.1802, 0.5449, 0.8315, -0.1084, 0.8155, -0.5556, -0.1622, 0.6935, 0.7071, -0.1379, 0.6935, -0.7071, -0.1379, 0.8155, 0.5556, -0.1622, 0.3536, 0.9239, -0.1464, 0.8536, -0.3827, -0.3536, 0.5133, 0.8315, -0.2126, 0.7682, -0.5556, -0.3182, 0.6533, 0.7071, -0.2706, 0.6533, -0.7071, -0.2706, 0.7682, 0.5556, -0.3182, 0.5133, -0.8315, -0.2126, 0.8536, 0.3827, -0.3536, 0.3536, -0.9239, -0.1464, 0.9061, 0.1951, -0.3753, 0.1802, -0.9808, -0.0747, 0.9239, 0, -0.3827, 0.1802, 0.9808, -0.0747, 0.9061, -0.1951, -0.3753, 0.7682, 0.3827, -0.5133, 0.3182, -0.9239, -0.2126, 0.8155, 0.1951, -0.5449, 0.1622, -0.9808, -0.1084, 0.8315, 0, -0.5556, 0.1622, 0.9808, -0.1084, 0.8155, -0.1951, -0.5449, 0.3182, 0.9239, -0.2126, 0.7682, -0.3827, -0.5133, 0.4619, 0.8315, -0.3087, 0.6913, -0.5556, -0.4619, 0.5879, 0.7071, -0.3928, 0.5879, -0.7071, -0.3928, 0.6913, 0.5556, -0.4619, 0.4619, -0.8315, -0.3087, 0.6533, -0.3827, -0.6533, 0.3928, 0.8315, -0.3928, 0.5879, -0.5556, -0.5879, 0.5, 0.7071, -0.5, 0.5, -0.7071, -0.5, 0.5879, 0.5556, -0.5879, 0.3928, -0.8315, -0.3928, 0.6533, 0.3827, -0.6533, 0.2706, -0.9239, -0.2706, 0.6935, 0.1951, -0.6935, 0.1379, -0.9808, -0.1379, 0.7071, 0, -0.7071, 0.138, 0.9808, -0.1379, 0.6935, -0.1951, -0.6935, 0.2706, 0.9239, -0.2706, 0.3087, -0.8315, -0.4619, 0.2126, -0.9239, -0.3182, 0.5133, 0.3827, -0.7682, 0.5449, 0.1951, -0.8155, 0.1084, -0.9808, -0.1622, 0.5556, 0, -0.8315, 0.1084, 0.9808, -0.1622, 0.5449, -0.1951, -0.8155, 0.2126, 0.9239, -0.3182, 0.5133, -0.3827, -0.7682, 0.3087, 0.8315, -0.4619, 0.4619, -0.5556, -0.6913, 0.3928, 0.7071, -0.5879, 0.3928, -0.7071, -0.5879, 0.4619, 0.5556, -0.6913, 0.2126, 0.8315, -0.5133, 0.3536, -0.3827, -0.8536, 0.3182, -0.5556, -0.7682, 0.2706, 0.7071, -0.6533, 0.2706, -0.7071, -0.6533, 0.3182, 0.5556, -0.7682, 0.2126, -0.8315, -0.5133, 0.3536, 0.3827, -0.8536, 0.1464, -0.9239, -0.3536, 0.3753, 0.1951, -0.9061, 0.0747, -0.9808, -0.1802, 0.3827, 0, -0.9239, 0.0747, 0.9808, -0.1802, 0.3753, -0.1951, -0.9061, 0.1464, 0.9239, -0.3536, 0.1802, 0.3827, -0.9061, 0.1913, 0.1951, -0.9619, 0.0381, -0.9808, -0.1913, 0.1951, 0, -0.9808, 0.0381, 0.9808, -0.1913, 0.1913, -0.1951, -0.9619, 0.0747, 0.9239, -0.3753, 0.1802, -0.3827, -0.9061, 0.1084, 0.8315, -0.5449, 0.1622, -0.5556, -0.8155, 0.1379, 0.7071, -0.6935, 0.1379, -0.7071, -0.6935, 0.1622, 0.5556, -0.8155, 0.1084, -0.8315, -0.5449, 0.0747, -0.9239, -0.3753, 0, -0.3827, -0.9239, 0, -0.5556, -0.8315, 0, 0.8315, -0.5556, 0, 0.7071, -0.7071, 0, -0.7071, -0.7071, 0, 0.5556, -0.8315, 0, -0.8315, -0.5556, 0, 0.3827, -0.9239, 0, -0.9239, -0.3827, 0, 0.1951, -0.9808, 0, -0.9808, -0.1951, 0, 0, -1, 0, 0.9808, -0.1951, 0, -0.1951, -0.9808, 0, 0.9239, -0.3827, -0.0381, -0.9808, -0.1913, -0.1951, 0, -0.9808, -0.0381, 0.9808, -0.1913, -0.1913, -0.1951, -0.9619, -0.0747, 0.9239, -0.3753, -0.1802, -0.3827, -0.9061, -0.1084, 0.8315, -0.5449, -0.1622, -0.5556, -0.8155, -0.1379, 0.7071, -0.6935, -0.1379, -0.7071, -0.6935, -0.1622, 0.5556, -0.8155, -0.1084, -0.8315, -0.5449, -0.1802, 0.3827, -0.9061, -0.0747, -0.9239, -0.3753, -0.1913, 0.1951, -0.9619, -0.2706, -0.7071, -0.6533, -0.3182, 0.5556, -0.7682, -0.2126, -0.8315, -0.5133, -0.3536, 0.3827, -0.8536, -0.1464, -0.9239, -0.3536, -0.3753, 0.1951, -0.9061, -0.0747, -0.9808, -0.1802, -0.3827, 0, -0.9239, -0.0747, 0.9808, -0.1802, -0.3753, -0.1951, -0.9061, -0.1464, 0.9239, -0.3536, -0.3536, -0.3827, -0.8536, -0.2126, 0.8315, -0.5133, -0.3182, -0.5556, -0.7682, -0.2706, 0.7071, -0.6533, -0.1084, 0.9808, -0.1622, -0.1084, -0.9808, -0.1622, -0.5556, 0, -0.8315, -0.5449, -0.1951, -0.8155, -0.2126, 0.9239, -0.3182, -0.5133, -0.3827, -0.7682, -0.3087, 0.8315, -0.4619, -0.4619, -0.5556, -0.6913, -0.3928, 0.7071, -0.5879, -0.3928, -0.7071, -0.5879, -0.4619, 0.5556, -0.6913, -0.3087, -0.8315, -0.4619, -0.5133, 0.3827, -0.7682, -0.2126, -0.9239, -0.3182, -0.5449, 0.1951, -0.8155, -0.5, 0.7071, -0.5, -0.5879, 0.5556, -0.5879, -0.3928, -0.8315, -0.3928, -0.6533, 0.3827, -0.6533, -0.2706, -0.9239, -0.2706, -0.6935, 0.1951, -0.6935, -0.1379, -0.9808, -0.1379, -0.7071, 0, -0.7071, -0.1379, 0.9808, -0.1379, -0.6935, -0.1951, -0.6935, -0.2706, 0.9239, -0.2706, -0.6533, -0.3827, -0.6533, -0.3928, 0.8315, -0.3928, -0.5879, -0.5556, -0.5879, -0.5, -0.7071, -0.5, -0.8155, -0.1951, -0.5449, -0.3182, 0.9239, -0.2126, -0.7682, -0.3827, -0.5133, -0.4619, 0.8315, -0.3087, -0.6913, -0.5556, -0.4619, -0.5879, 0.7071, -0.3928, -0.5879, -0.7071, -0.3928, -0.6913, 0.5556, -0.4619, -0.4619, -0.8315, -0.3087, -0.7682, 0.3827, -0.5133, -0.3182, -0.9239, -0.2126, -0.8155, 0.1951, -0.5449, -0.1622, -0.9808, -0.1084, -0.8315, 0, -0.5556, -0.1622, 0.9808, -0.1084, -0.5133, -0.8315, -0.2126, -0.8536, 0.3827, -0.3536, -0.3536, -0.9239, -0.1464, -0.9061, 0.1951, -0.3753, -0.1802, -0.9808, -0.0747, -0.9239, 0, -0.3827, -0.1802, 0.9808, -0.0747, -0.9061, -0.1951, -0.3753, -0.3536, 0.9239, -0.1464, -0.8536, -0.3827, -0.3536, -0.5133, 0.8315, -0.2126, -0.7682, -0.5556, -0.3182, -0.6533, 0.7071, -0.2706, -0.6533, -0.7071, -0.2706, -0.7682, 0.5556, -0.3182, -0.3753, 0.9239, -0.0747, -0.9061, -0.3827, -0.1802, -0.5449, 0.8315, -0.1084, -0.8155, -0.5556, -0.1622, -0.6935, 0.7071, -0.1379, -0.6935, -0.7071, -0.1379, -0.8155, 0.5556, -0.1622, -0.5449, -0.8315, -0.1084, -0.9061, 0.3827, -0.1802, -0.3753, -0.9239, -0.0747, -0.9619, 0.1951, -0.1913, -0.1913, -0.9808, -0.0381, -0.9808, 0, -0.1951, -0.1913, 0.9808, -0.0381, -0.9619, -0.1951, -0.1913
|
|
2841
2880
|
];
|
|
@@ -2846,6 +2885,28 @@ class Sphere extends BasicMesh {
|
|
|
2846
2885
|
this.subMeshes = [
|
|
2847
2886
|
new SubMesh(0, 2880)
|
|
2848
2887
|
];
|
|
2888
|
+
if (scale !== undefined) {
|
|
2889
|
+
const sx = scale.x;
|
|
2890
|
+
const sy = scale.y;
|
|
2891
|
+
const sz = scale.z;
|
|
2892
|
+
if (sx !== 1 || sy !== 1 || sz !== 1) {
|
|
2893
|
+
for (let i = 0; i < this.vertices.length; i += 3) {
|
|
2894
|
+
this.vertices[i] *= sx;
|
|
2895
|
+
this.vertices[i + 1] *= sy;
|
|
2896
|
+
this.vertices[i + 2] *= sz;
|
|
2897
|
+
}
|
|
2898
|
+
if (sx !== sy || sy !== sz)
|
|
2899
|
+
for (let i = 0; i < this.normals.length; i += 3) {
|
|
2900
|
+
const nx = this.normals[i] / sx;
|
|
2901
|
+
const ny = this.normals[i + 1] / sy;
|
|
2902
|
+
const nz = this.normals[i + 2] / sz;
|
|
2903
|
+
const len = Math.hypot(nx, ny, nz) || 1.0;
|
|
2904
|
+
this.normals[i] = nx / len;
|
|
2905
|
+
this.normals[i + 1] = ny / len;
|
|
2906
|
+
this.normals[i + 2] = nz / len;
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2849
2910
|
}
|
|
2850
2911
|
}
|
|
2851
2912
|
|
|
@@ -2865,27 +2926,13 @@ class Hemisphere extends BasicMesh {
|
|
|
2865
2926
|
}
|
|
2866
2927
|
|
|
2867
2928
|
class Plane extends BasicMesh {
|
|
2868
|
-
constructor(
|
|
2929
|
+
constructor(size = new Vec2(1.0, 1.0), segments = new Vec2(1, 1), orientation = 'xz') {
|
|
2869
2930
|
super();
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
if (sizeOrWidth instanceof Vec2) {
|
|
2876
|
-
w = sizeOrWidth.x || 1.0;
|
|
2877
|
-
h = sizeOrWidth.y || 1.0;
|
|
2878
|
-
gridX = Utils.clamp(Utils.floor(heightOrWidthSegments || 1, 0), 1, Infinity);
|
|
2879
|
-
gridY = Utils.clamp(Utils.floor(widthOrHeightSegments || 1, 0), 1, Infinity);
|
|
2880
|
-
orient = (typeof heightSegmentsOrOrientation === 'string' ? heightSegmentsOrOrientation : orientation) || 'xz';
|
|
2881
|
-
}
|
|
2882
|
-
else {
|
|
2883
|
-
w = sizeOrWidth || 1.0;
|
|
2884
|
-
h = heightOrWidthSegments || 1.0;
|
|
2885
|
-
gridX = Utils.clamp(Utils.floor(widthOrHeightSegments || 1, 0), 1, Infinity);
|
|
2886
|
-
gridY = Utils.clamp(Utils.floor((typeof heightSegmentsOrOrientation === 'number' ? heightSegmentsOrOrientation : 1) || 1, 0), 1, Infinity);
|
|
2887
|
-
orient = orientation || 'xz';
|
|
2888
|
-
}
|
|
2931
|
+
const w = size.x || 1.0;
|
|
2932
|
+
const h = size.y || 1.0;
|
|
2933
|
+
const gridX = Utils.clamp(Utils.floor(segments.x || 1, 0), 1, Infinity);
|
|
2934
|
+
const gridY = Utils.clamp(Utils.floor(segments.y || 1, 0), 1, Infinity);
|
|
2935
|
+
const orient = orientation || 'xz';
|
|
2889
2936
|
const gridX1 = gridX + 1;
|
|
2890
2937
|
const gridY1 = gridY + 1;
|
|
2891
2938
|
const segmentWidth = w / gridX;
|
|
@@ -2931,13 +2978,14 @@ class Plane extends BasicMesh {
|
|
|
2931
2978
|
}
|
|
2932
2979
|
|
|
2933
2980
|
class Cylinder extends BasicMesh {
|
|
2934
|
-
constructor(
|
|
2981
|
+
constructor(radii = new Vec2(1.0, 1.0), height = 1.0, radialSegments = 16, heightSegments = 1, openEnded = false) {
|
|
2935
2982
|
super();
|
|
2936
|
-
const rTop =
|
|
2937
|
-
const rBottom =
|
|
2938
|
-
const h = height
|
|
2939
|
-
const radSeg = Utils.clamp(Utils.floor(radialSegments
|
|
2940
|
-
const hSeg = Utils.clamp(Utils.floor(heightSegments
|
|
2983
|
+
const rTop = radii.x;
|
|
2984
|
+
const rBottom = radii.y;
|
|
2985
|
+
const h = height;
|
|
2986
|
+
const radSeg = Utils.clamp(Utils.floor(radialSegments, 0), 3, Infinity);
|
|
2987
|
+
const hSeg = Utils.clamp(Utils.floor(heightSegments, 0), 1, Infinity);
|
|
2988
|
+
const open = openEnded;
|
|
2941
2989
|
const vertices = [];
|
|
2942
2990
|
const normals = [];
|
|
2943
2991
|
const uvs = [];
|
|
@@ -2978,7 +3026,7 @@ class Cylinder extends BasicMesh {
|
|
|
2978
3026
|
indices.push(a, b, d);
|
|
2979
3027
|
indices.push(b, c, d);
|
|
2980
3028
|
}
|
|
2981
|
-
if (!
|
|
3029
|
+
if (!open) {
|
|
2982
3030
|
if (rTop > 0) {
|
|
2983
3031
|
const centerIndexTop = index++;
|
|
2984
3032
|
vertices.push(0, halfH, 0);
|
|
@@ -3086,13 +3134,10 @@ class CustomMesh {
|
|
|
3086
3134
|
];
|
|
3087
3135
|
}
|
|
3088
3136
|
static toFloat32(array) {
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
return flat;
|
|
3094
|
-
}
|
|
3095
|
-
return array instanceof Float32Array ? array.slice(0) : new Float32Array(array);
|
|
3137
|
+
const flat = new Float32Array(array.length * 3);
|
|
3138
|
+
for (let i = 0; i < array.length; i++)
|
|
3139
|
+
array[i].toArray(flat, i * 3);
|
|
3140
|
+
return flat;
|
|
3096
3141
|
}
|
|
3097
3142
|
setVertices(array) {
|
|
3098
3143
|
this.vertices = CustomMesh.toFloat32(array);
|
|
@@ -3172,6 +3217,284 @@ class CustomMesh {
|
|
|
3172
3217
|
}
|
|
3173
3218
|
}
|
|
3174
3219
|
|
|
3220
|
+
const SpriteShader = {
|
|
3221
|
+
vertex: `
|
|
3222
|
+
attribute vec3 aVertexPosition;
|
|
3223
|
+
attribute vec2 aTextureCoord;
|
|
3224
|
+
|
|
3225
|
+
uniform mat4 uModelMatrix;
|
|
3226
|
+
uniform mat4 uViewMatrix;
|
|
3227
|
+
uniform mat4 uProjectionMatrix;
|
|
3228
|
+
uniform float uFrame;
|
|
3229
|
+
uniform float uFrames;
|
|
3230
|
+
uniform float uBillboard;
|
|
3231
|
+
uniform float uDepthBias;
|
|
3232
|
+
|
|
3233
|
+
varying vec2 vTextureCoord;
|
|
3234
|
+
varying float vDepth;
|
|
3235
|
+
|
|
3236
|
+
void main(void) {
|
|
3237
|
+
vTextureCoord = vec2((uFrame + aTextureCoord.x) / uFrames, aTextureCoord.y);
|
|
3238
|
+
vec3 view;
|
|
3239
|
+
if (uBillboard < 0.5) {
|
|
3240
|
+
view = (uViewMatrix * uModelMatrix * vec4(aVertexPosition, 1.0)).xyz;
|
|
3241
|
+
} else {
|
|
3242
|
+
vec3 origin = (uViewMatrix * uModelMatrix * vec4(0.0, 0.0, 0.0, 1.0)).xyz;
|
|
3243
|
+
vec3 up = vec3(0.0, 1.0, 0.0);
|
|
3244
|
+
vec3 right = vec3(1.0, 0.0, 0.0);
|
|
3245
|
+
if (uBillboard > 1.5) {
|
|
3246
|
+
up = normalize((uViewMatrix * vec4(0.0, 1.0, 0.0, 0.0)).xyz);
|
|
3247
|
+
vec3 across = cross(origin, up);
|
|
3248
|
+
if (length(across) > 0.0001)
|
|
3249
|
+
right = normalize(across);
|
|
3250
|
+
}
|
|
3251
|
+
float scaleX = length(uModelMatrix[0].xyz);
|
|
3252
|
+
float scaleY = length(uModelMatrix[1].xyz);
|
|
3253
|
+
view = origin + right * aVertexPosition.x * scaleX + up * aVertexPosition.y * scaleY;
|
|
3254
|
+
}
|
|
3255
|
+
vDepth = -view.z;
|
|
3256
|
+
vec4 clip = uProjectionMatrix * vec4(view, 1.0);
|
|
3257
|
+
// only the depth moves: the picture stays where it is and as big as it is
|
|
3258
|
+
float bias = min(uDepthBias, max(0.0, vDepth - 1.0));
|
|
3259
|
+
if (bias > 0.0) {
|
|
3260
|
+
vec4 nearer = uProjectionMatrix * vec4(view.xy, view.z + bias, 1.0);
|
|
3261
|
+
clip.z = nearer.z / nearer.w * clip.w;
|
|
3262
|
+
}
|
|
3263
|
+
gl_Position = clip;
|
|
3264
|
+
}
|
|
3265
|
+
`,
|
|
3266
|
+
fragment: `
|
|
3267
|
+
precision mediump float;
|
|
3268
|
+
|
|
3269
|
+
uniform sampler2D uSampler;
|
|
3270
|
+
uniform vec3 uMaterialDiffuse;
|
|
3271
|
+
uniform float uAlphaCutoff;
|
|
3272
|
+
uniform int uHasFog;
|
|
3273
|
+
uniform vec3 uFogColor;
|
|
3274
|
+
uniform float uFogNear;
|
|
3275
|
+
uniform float uFogFar;
|
|
3276
|
+
|
|
3277
|
+
varying vec2 vTextureCoord;
|
|
3278
|
+
varying float vDepth;
|
|
3279
|
+
|
|
3280
|
+
void main(void) {
|
|
3281
|
+
vec4 texel = texture2D(uSampler, vTextureCoord);
|
|
3282
|
+
if (texel.a < uAlphaCutoff)
|
|
3283
|
+
discard;
|
|
3284
|
+
vec3 color = texel.rgb * uMaterialDiffuse;
|
|
3285
|
+
if (uHasFog == 1) {
|
|
3286
|
+
float fade = clamp((vDepth - uFogNear) / (uFogFar - uFogNear), 0.0, 1.0);
|
|
3287
|
+
color = mix(color, uFogColor, fade);
|
|
3288
|
+
}
|
|
3289
|
+
gl_FragColor = vec4(color, texel.a);
|
|
3290
|
+
}
|
|
3291
|
+
`,
|
|
3292
|
+
create(context) {
|
|
3293
|
+
return Program.create(context, SpriteShader.vertex, SpriteShader.fragment);
|
|
3294
|
+
}
|
|
3295
|
+
};
|
|
3296
|
+
|
|
3297
|
+
const scratchSpritePos = new Vec3();
|
|
3298
|
+
const scratchSpriteScale = new Vec3();
|
|
3299
|
+
const scratchSpriteNormal = new Vec3();
|
|
3300
|
+
const scratchSpriteUp = new Vec3();
|
|
3301
|
+
const scratchSpriteRight = new Vec3();
|
|
3302
|
+
const scratchSpriteHit = new Vec3();
|
|
3303
|
+
const scratchSpriteC0 = new Vec3();
|
|
3304
|
+
const scratchSpriteC1 = new Vec3();
|
|
3305
|
+
const scratchSpriteC2 = new Vec3();
|
|
3306
|
+
const scratchSpriteC3 = new Vec3();
|
|
3307
|
+
function raycastSprite(sprite, ray) {
|
|
3308
|
+
const worldPos = sprite.getWorldPosition(scratchSpritePos);
|
|
3309
|
+
const scale = sprite.worldMatrix.getScale(scratchSpriteScale);
|
|
3310
|
+
const normal = scratchSpriteNormal;
|
|
3311
|
+
const up = scratchSpriteUp;
|
|
3312
|
+
const right = scratchSpriteRight;
|
|
3313
|
+
if (sprite.billboard === 'spherical') {
|
|
3314
|
+
normal.copy(ray.direction).opposite().normalize();
|
|
3315
|
+
up.setScalar(0, 1, 0);
|
|
3316
|
+
const dotUp = up.dotProduct(normal);
|
|
3317
|
+
up.subtractScaledVector(normal, dotUp);
|
|
3318
|
+
if (up.getMagnitude(true) < 0.0001) {
|
|
3319
|
+
up.setScalar(0, 0, 1);
|
|
3320
|
+
const dotZ = up.dotProduct(normal);
|
|
3321
|
+
up.subtractScaledVector(normal, dotZ);
|
|
3322
|
+
}
|
|
3323
|
+
up.normalize();
|
|
3324
|
+
right.crossVectors(up, normal).normalize();
|
|
3325
|
+
}
|
|
3326
|
+
else {
|
|
3327
|
+
up.setScalar(0, 1, 0);
|
|
3328
|
+
normal.setScalar(-ray.direction.x, 0, -ray.direction.z);
|
|
3329
|
+
if (normal.getMagnitude(true) < 0.0001)
|
|
3330
|
+
normal.setScalar(ray.origin.x - worldPos.x, 0, ray.origin.z - worldPos.z);
|
|
3331
|
+
if (normal.getMagnitude(true) < 0.0001)
|
|
3332
|
+
normal.setScalar(0, 0, 1);
|
|
3333
|
+
else
|
|
3334
|
+
normal.normalize();
|
|
3335
|
+
right.crossVectors(up, normal).normalize();
|
|
3336
|
+
}
|
|
3337
|
+
const hw = (sprite.width / 2) * scale.x;
|
|
3338
|
+
const y0 = (sprite.anchor === 'bottom' ? 0 : -sprite.height / 2) * scale.y;
|
|
3339
|
+
const y1 = (sprite.anchor === 'bottom' ? sprite.height : sprite.height / 2) * scale.y;
|
|
3340
|
+
const c0 = scratchSpriteC0.setScalar(worldPos.x - right.x * hw + up.x * y0, worldPos.y - right.y * hw + up.y * y0, worldPos.z - right.z * hw + up.z * y0);
|
|
3341
|
+
const c1 = scratchSpriteC1.setScalar(worldPos.x + right.x * hw + up.x * y0, worldPos.y + right.y * hw + up.y * y0, worldPos.z + right.z * hw + up.z * y0);
|
|
3342
|
+
const c2 = scratchSpriteC2.setScalar(worldPos.x + right.x * hw + up.x * y1, worldPos.y + right.y * hw + up.y * y1, worldPos.z + right.z * hw + up.z * y1);
|
|
3343
|
+
const c3 = scratchSpriteC3.setScalar(worldPos.x - right.x * hw + up.x * y1, worldPos.y - right.y * hw + up.y * y1, worldPos.z - right.z * hw + up.z * y1);
|
|
3344
|
+
let hit = ray.intersectTriangle(c0, c1, c2, false, scratchSpriteHit);
|
|
3345
|
+
if (!hit)
|
|
3346
|
+
hit = ray.intersectTriangle(c0, c2, c3, false, scratchSpriteHit);
|
|
3347
|
+
if (!hit)
|
|
3348
|
+
return null;
|
|
3349
|
+
return { distance: ray.origin.getDistance(hit), point: new Vec3().copy(hit) };
|
|
3350
|
+
}
|
|
3351
|
+
|
|
3352
|
+
const BILLBOARD_VALUES = { none: 0, spherical: 1, cylindrical: 2 };
|
|
3353
|
+
class Sprite extends Mesh {
|
|
3354
|
+
width;
|
|
3355
|
+
height;
|
|
3356
|
+
frames;
|
|
3357
|
+
anchor;
|
|
3358
|
+
billboard;
|
|
3359
|
+
currentFrame = 0;
|
|
3360
|
+
alphaCutoff = 0.5;
|
|
3361
|
+
depthBias = 0;
|
|
3362
|
+
static scratchSpriteMin = new Vec3();
|
|
3363
|
+
static scratchSpriteMax = new Vec3();
|
|
3364
|
+
static scratchSpriteBox = new Box();
|
|
3365
|
+
static scratchSpriteHit = new Vec3();
|
|
3366
|
+
constructor(context, options) {
|
|
3367
|
+
const size = options.size ?? new Vec2(1, 1);
|
|
3368
|
+
const width = size.x;
|
|
3369
|
+
const height = size.y;
|
|
3370
|
+
const anchor = options.anchor ?? 'center';
|
|
3371
|
+
const billboard = options.billboard ?? 'spherical';
|
|
3372
|
+
const frames = Math.max(1, Math.floor(options.frames ?? 1));
|
|
3373
|
+
const x = width / 2;
|
|
3374
|
+
const y0 = anchor === 'bottom' ? 0 : -height / 2;
|
|
3375
|
+
const y1 = anchor === 'bottom' ? height : height / 2;
|
|
3376
|
+
const geometry = {
|
|
3377
|
+
vertices: [-x, y0, 0, x, y0, 0, x, y1, 0, -x, y0, 0, x, y1, 0, -x, y1, 0],
|
|
3378
|
+
uvs: [0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1],
|
|
3379
|
+
indices: null,
|
|
3380
|
+
normals: null,
|
|
3381
|
+
subMeshes: [],
|
|
3382
|
+
itemSize: 3,
|
|
3383
|
+
primitive: 'TRIANGLES'
|
|
3384
|
+
};
|
|
3385
|
+
super(geometry, context);
|
|
3386
|
+
this.width = width;
|
|
3387
|
+
this.height = height;
|
|
3388
|
+
this.frames = frames;
|
|
3389
|
+
this.anchor = anchor;
|
|
3390
|
+
this.billboard = billboard;
|
|
3391
|
+
this.alphaCutoff = options.alphaCutoff ?? 0.5;
|
|
3392
|
+
this.depthBias = options.depthBias ?? 0;
|
|
3393
|
+
const material = options.material ?? new Material({ diffuse: new Vec3(1, 1, 1) });
|
|
3394
|
+
this.addProgram(SpriteShader.vertex, options.fragment ?? SpriteShader.fragment, material);
|
|
3395
|
+
this.addCustomUniform('frame', 'uniform1f', 0);
|
|
3396
|
+
this.addCustomUniform('frames', 'uniform1f', frames);
|
|
3397
|
+
this.addCustomUniform('billboard', 'uniform1f', BILLBOARD_VALUES[billboard]);
|
|
3398
|
+
this.addCustomUniform('alphaCutoff', 'uniform1f', this.alphaCutoff);
|
|
3399
|
+
this.addCustomUniform('depthBias', 'uniform1f', this.depthBias);
|
|
3400
|
+
this.updateLocalBounds();
|
|
3401
|
+
if (options.texture)
|
|
3402
|
+
this.setTexture(options.texture, options.textureOptions);
|
|
3403
|
+
}
|
|
3404
|
+
updateLocalBounds() {
|
|
3405
|
+
const x = this.width / 2;
|
|
3406
|
+
const y0 = this.anchor === 'bottom' ? 0 : -this.height / 2;
|
|
3407
|
+
const y1 = this.anchor === 'bottom' ? this.height : this.height / 2;
|
|
3408
|
+
if (this.billboard !== 'none') {
|
|
3409
|
+
const reach = Math.hypot(x, Math.max(Math.abs(y0), Math.abs(y1)));
|
|
3410
|
+
if (this.billboard === 'spherical') {
|
|
3411
|
+
Sprite.scratchSpriteMin.setScalar(-reach, -reach, -reach);
|
|
3412
|
+
Sprite.scratchSpriteMax.setScalar(reach, reach, reach);
|
|
3413
|
+
}
|
|
3414
|
+
else {
|
|
3415
|
+
Sprite.scratchSpriteMin.setScalar(-x, y0, -x);
|
|
3416
|
+
Sprite.scratchSpriteMax.setScalar(x, y1, x);
|
|
3417
|
+
}
|
|
3418
|
+
}
|
|
3419
|
+
else {
|
|
3420
|
+
Sprite.scratchSpriteMin.setScalar(-x, y0, 0);
|
|
3421
|
+
Sprite.scratchSpriteMax.setScalar(x, y1, 0);
|
|
3422
|
+
}
|
|
3423
|
+
Sprite.scratchSpriteBox.set(Sprite.scratchSpriteMin, Sprite.scratchSpriteMax);
|
|
3424
|
+
this.setLocalBounds(Sprite.scratchSpriteBox);
|
|
3425
|
+
}
|
|
3426
|
+
setFrame(frame) {
|
|
3427
|
+
this.currentFrame = ((Math.floor(frame) % this.frames) + this.frames) % this.frames;
|
|
3428
|
+
this.setCustomUniform('frame', this.currentFrame);
|
|
3429
|
+
return this;
|
|
3430
|
+
}
|
|
3431
|
+
getFrame() {
|
|
3432
|
+
return this.currentFrame;
|
|
3433
|
+
}
|
|
3434
|
+
setFrames(frames) {
|
|
3435
|
+
this.frames = Math.max(1, Math.floor(frames));
|
|
3436
|
+
this.setCustomUniform('frames', this.frames);
|
|
3437
|
+
return this.setFrame(this.currentFrame);
|
|
3438
|
+
}
|
|
3439
|
+
getFrames() {
|
|
3440
|
+
return this.frames;
|
|
3441
|
+
}
|
|
3442
|
+
setBillboard(billboard) {
|
|
3443
|
+
if (this.billboard === billboard)
|
|
3444
|
+
return this;
|
|
3445
|
+
this.billboard = billboard;
|
|
3446
|
+
this.setCustomUniform('billboard', BILLBOARD_VALUES[billboard]);
|
|
3447
|
+
this.updateLocalBounds();
|
|
3448
|
+
return this;
|
|
3449
|
+
}
|
|
3450
|
+
getBillboard() {
|
|
3451
|
+
return this.billboard;
|
|
3452
|
+
}
|
|
3453
|
+
setAlphaCutoff(cutoff) {
|
|
3454
|
+
this.alphaCutoff = cutoff;
|
|
3455
|
+
this.setCustomUniform('alphaCutoff', cutoff);
|
|
3456
|
+
return this;
|
|
3457
|
+
}
|
|
3458
|
+
getAlphaCutoff() {
|
|
3459
|
+
return this.alphaCutoff;
|
|
3460
|
+
}
|
|
3461
|
+
setDepthBias(depthBias) {
|
|
3462
|
+
this.depthBias = depthBias;
|
|
3463
|
+
this.setCustomUniform('depthBias', depthBias);
|
|
3464
|
+
return this;
|
|
3465
|
+
}
|
|
3466
|
+
getDepthBias() {
|
|
3467
|
+
return this.depthBias;
|
|
3468
|
+
}
|
|
3469
|
+
getWidth() {
|
|
3470
|
+
return this.width;
|
|
3471
|
+
}
|
|
3472
|
+
getHeight() {
|
|
3473
|
+
return this.height;
|
|
3474
|
+
}
|
|
3475
|
+
getSize(target = new Vec2()) {
|
|
3476
|
+
target.x = this.width;
|
|
3477
|
+
target.y = this.height;
|
|
3478
|
+
return target;
|
|
3479
|
+
}
|
|
3480
|
+
getAnchor() {
|
|
3481
|
+
return this.anchor;
|
|
3482
|
+
}
|
|
3483
|
+
raycast(ray, precise = false) {
|
|
3484
|
+
if (!this.isActive())
|
|
3485
|
+
return null;
|
|
3486
|
+
if (this.billboard === 'none')
|
|
3487
|
+
return super.raycast(ray, precise);
|
|
3488
|
+
const sphere = this.computeBoundingSphere();
|
|
3489
|
+
const hitSphere = ray.intersectSphere(sphere, Sprite.scratchSpriteHit);
|
|
3490
|
+
if (!hitSphere)
|
|
3491
|
+
return null;
|
|
3492
|
+
if (!precise)
|
|
3493
|
+
return { distance: ray.origin.getDistance(hitSphere), point: new Vec3().copy(hitSphere) };
|
|
3494
|
+
return raycastSprite(this, ray);
|
|
3495
|
+
}
|
|
3496
|
+
}
|
|
3497
|
+
|
|
3175
3498
|
class DirectionalLight {
|
|
3176
3499
|
position;
|
|
3177
3500
|
diffuse;
|
|
@@ -3185,9 +3508,9 @@ class DirectionalLight {
|
|
|
3185
3508
|
this.specular = new Vec3(0.8, 0.8, 0.8);
|
|
3186
3509
|
this.type = 'directional';
|
|
3187
3510
|
if (directionOrOptions)
|
|
3188
|
-
if (directionOrOptions instanceof Vec3
|
|
3511
|
+
if (directionOrOptions instanceof Vec3)
|
|
3189
3512
|
this.setDirection(directionOrOptions);
|
|
3190
|
-
else
|
|
3513
|
+
else {
|
|
3191
3514
|
if (directionOrOptions.direction)
|
|
3192
3515
|
this.setDirection(directionOrOptions.direction);
|
|
3193
3516
|
if (directionOrOptions.diffuse)
|
|
@@ -3198,17 +3521,17 @@ class DirectionalLight {
|
|
|
3198
3521
|
this.setPosition(directionOrOptions.position);
|
|
3199
3522
|
}
|
|
3200
3523
|
}
|
|
3201
|
-
setPosition(
|
|
3202
|
-
|
|
3524
|
+
setPosition(position) {
|
|
3525
|
+
this.position.copy(position);
|
|
3203
3526
|
}
|
|
3204
|
-
setDirection(
|
|
3205
|
-
|
|
3527
|
+
setDirection(direction) {
|
|
3528
|
+
this.direction.copy(direction).normalize();
|
|
3206
3529
|
}
|
|
3207
|
-
setDiffuse(
|
|
3208
|
-
|
|
3530
|
+
setDiffuse(diffuse) {
|
|
3531
|
+
this.diffuse.copy(diffuse);
|
|
3209
3532
|
}
|
|
3210
|
-
setSpecular(
|
|
3211
|
-
|
|
3533
|
+
setSpecular(specular) {
|
|
3534
|
+
this.specular.copy(specular);
|
|
3212
3535
|
}
|
|
3213
3536
|
}
|
|
3214
3537
|
|
|
@@ -3223,9 +3546,9 @@ class PointLight extends DirectionalLight {
|
|
|
3223
3546
|
this.quadraticAttenuation = 0;
|
|
3224
3547
|
this.type = 'point';
|
|
3225
3548
|
if (positionOrOptions)
|
|
3226
|
-
if (positionOrOptions instanceof Vec3
|
|
3549
|
+
if (positionOrOptions instanceof Vec3)
|
|
3227
3550
|
this.setPosition(positionOrOptions);
|
|
3228
|
-
else
|
|
3551
|
+
else {
|
|
3229
3552
|
if (positionOrOptions.position)
|
|
3230
3553
|
this.setPosition(positionOrOptions.position);
|
|
3231
3554
|
if (positionOrOptions.diffuse)
|
|
@@ -3261,7 +3584,7 @@ class SpotLight extends PointLight {
|
|
|
3261
3584
|
this.exponent = 0;
|
|
3262
3585
|
this.direction = new Vec3(1.0, 0.0, 0.0);
|
|
3263
3586
|
this.type = 'spot';
|
|
3264
|
-
if (positionOrOptions &&
|
|
3587
|
+
if (positionOrOptions && !(positionOrOptions instanceof Vec3)) {
|
|
3265
3588
|
if (positionOrOptions.direction)
|
|
3266
3589
|
this.setDirection(positionOrOptions.direction);
|
|
3267
3590
|
if (positionOrOptions.cutoff !== undefined)
|
|
@@ -3276,8 +3599,8 @@ class SpotLight extends PointLight {
|
|
|
3276
3599
|
setExponent(value) {
|
|
3277
3600
|
this.exponent = value;
|
|
3278
3601
|
}
|
|
3279
|
-
setDirection(
|
|
3280
|
-
|
|
3602
|
+
setDirection(direction) {
|
|
3603
|
+
this.direction.copy(direction);
|
|
3281
3604
|
}
|
|
3282
3605
|
}
|
|
3283
3606
|
|
|
@@ -3285,31 +3608,21 @@ class AmbientLight {
|
|
|
3285
3608
|
diffuse;
|
|
3286
3609
|
intensity;
|
|
3287
3610
|
type;
|
|
3288
|
-
constructor(
|
|
3289
|
-
this.diffuse = new Vec3();
|
|
3290
|
-
|
|
3291
|
-
this.diffuse.copy(rOrVec);
|
|
3292
|
-
this.intensity = g !== undefined ? g : 1.0;
|
|
3293
|
-
}
|
|
3294
|
-
else {
|
|
3295
|
-
this.diffuse.setScalar(rOrVec, g, b);
|
|
3296
|
-
this.intensity = intensity;
|
|
3297
|
-
}
|
|
3611
|
+
constructor(diffuse = new Vec3(1.0, 1.0, 1.0), intensity = 1.0) {
|
|
3612
|
+
this.diffuse = new Vec3().copy(diffuse);
|
|
3613
|
+
this.intensity = intensity;
|
|
3298
3614
|
this.type = 'ambient';
|
|
3299
3615
|
}
|
|
3300
|
-
setColor(
|
|
3301
|
-
|
|
3302
|
-
this.diffuse.setScalar(xOrVec, y ?? 0, z ?? 0);
|
|
3303
|
-
else
|
|
3304
|
-
assignVec3(this.diffuse, xOrVec);
|
|
3616
|
+
setColor(diffuse) {
|
|
3617
|
+
this.diffuse.copy(diffuse);
|
|
3305
3618
|
return this;
|
|
3306
3619
|
}
|
|
3307
3620
|
setIntensity(intensity) {
|
|
3308
3621
|
this.intensity = intensity;
|
|
3309
3622
|
return this;
|
|
3310
3623
|
}
|
|
3311
|
-
getColor() {
|
|
3312
|
-
return this.diffuse;
|
|
3624
|
+
getColor(target = new Vec3()) {
|
|
3625
|
+
return target.copy(this.diffuse);
|
|
3313
3626
|
}
|
|
3314
3627
|
get color() {
|
|
3315
3628
|
return this.diffuse;
|
|
@@ -3319,4 +3632,19 @@ class AmbientLight {
|
|
|
3319
3632
|
}
|
|
3320
3633
|
}
|
|
3321
3634
|
|
|
3322
|
-
|
|
3635
|
+
function assignVec3(target, xOrVec, y, z) {
|
|
3636
|
+
if (xOrVec instanceof Vec3)
|
|
3637
|
+
return target.copy(xOrVec);
|
|
3638
|
+
if (typeof xOrVec === 'number')
|
|
3639
|
+
return target.setScalar(xOrVec, y, z);
|
|
3640
|
+
return target.setArray(xOrVec);
|
|
3641
|
+
}
|
|
3642
|
+
function assignVec2(target, xOrVec, y) {
|
|
3643
|
+
if (xOrVec instanceof Vec2)
|
|
3644
|
+
return target.copy(xOrVec);
|
|
3645
|
+
if (typeof xOrVec === 'number')
|
|
3646
|
+
return target.setScalar(xOrVec, y);
|
|
3647
|
+
return target.setArray(xOrVec);
|
|
3648
|
+
}
|
|
3649
|
+
|
|
3650
|
+
export { AmbientLight, BasicMesh, BasicShader, Camera, Cannon, Cube, CustomMesh, Cylinder, DirectionalLight, FullscreenQuad, Hemisphere, Lights, Line, MAX_LIGHTS, Material, Mesh, MultiQuad, Node, NormalShader, OrbitControls, OrthographicCamera, PerspectiveCamera, PhongShader, Plane, PointLight, Program, Quad, RenderTarget, Renderer, Scene, Shader, Sphere, SpotLight, Sprite, SpriteShader, SubMesh, Texture, Uniform, VWing, assignVec2, assignVec3, eDrawMethod };
|