3d-spinner 0.9.4 → 0.9.5
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/animations/charged-orb.js +4 -2
- package/dist/animations/ghost-train.js +3 -1
- package/dist/animations/spin.d.ts +3 -1
- package/dist/animations/spin.js +6 -1
- package/dist/cjs/animations/charged-orb.cjs +170 -28
- package/dist/cjs/animations/grid-assembly.cjs +164 -26
- package/dist/cjs/animations/object-motion.cjs +156 -24
- package/dist/cjs/animations/particles.cjs +167 -26
- package/dist/cjs/animations/spin.cjs +169 -27
- package/dist/cjs/engines/little-3d-engine/little-3d-engine.cjs +193 -45
- package/dist/cjs/engines/little-3d-engine/loaders/obj.cjs +51 -13
- package/dist/cjs/engines/little-3d-engine/renderers/canvas2d-textured.cjs +56 -6
- package/dist/cjs/engines/little-3d-engine/renderers/webgl-textured.cjs +55 -6
- package/dist/cjs/engines/little-3d-engine/renderers/webgpu-textured.cjs +64 -11
- package/dist/cjs/prefabs/prefabs.cjs +186 -40
- package/dist/engines/little-3d-engine/core/geometry.d.ts +15 -2
- package/dist/engines/little-3d-engine/core/geometry.js +25 -3
- package/dist/engines/little-3d-engine/core/light.d.ts +28 -4
- package/dist/engines/little-3d-engine/core/light.js +48 -7
- package/dist/engines/little-3d-engine/core/mesh.d.ts +33 -0
- package/dist/engines/little-3d-engine/core/mesh.js +12 -0
- package/dist/engines/little-3d-engine/little-3d-engine.d.ts +2 -2
- package/dist/engines/little-3d-engine/little-3d-engine.js +1 -1
- package/dist/engines/little-3d-engine/loaders/obj.d.ts +8 -7
- package/dist/engines/little-3d-engine/loaders/obj.js +74 -20
- package/dist/engines/little-3d-engine/renderers/canvas2d.js +23 -1
- package/dist/engines/little-3d-engine/renderers/webgl.js +34 -5
- package/dist/engines/little-3d-engine/renderers/webgpu.js +43 -10
- package/dist/engines/little-3d-engine/shapes/complex/plane.d.ts +8 -3
- package/dist/engines/little-3d-engine/shapes/complex/plane.js +10 -4
- package/dist/engines/little-3d-engine/shapes/primitives/cube.d.ts +3 -2
- package/dist/engines/little-3d-engine/shapes/primitives/cube.js +4 -2
- package/dist/engines/little-3d-engine/shapes/primitives/octahedron.d.ts +3 -2
- package/dist/engines/little-3d-engine/shapes/primitives/octahedron.js +4 -2
- package/dist/engines/little-3d-engine/shapes/primitives/pyramid.d.ts +3 -2
- package/dist/engines/little-3d-engine/shapes/primitives/pyramid.js +4 -2
- package/dist/engines/little-3d-engine/shapes/primitives/quad.d.ts +3 -2
- package/dist/engines/little-3d-engine/shapes/primitives/quad.js +4 -2
- package/dist/engines/little-3d-engine/shapes/primitives/spheres/cube-sphere.d.ts +3 -2
- package/dist/engines/little-3d-engine/shapes/primitives/spheres/cube-sphere.js +4 -2
- package/dist/engines/little-3d-engine/shapes/primitives/spheres/icosphere.d.ts +3 -2
- package/dist/engines/little-3d-engine/shapes/primitives/spheres/icosphere.js +4 -2
- package/dist/engines/little-3d-engine/shapes/primitives/spheres/octa-sphere.d.ts +3 -2
- package/dist/engines/little-3d-engine/shapes/primitives/spheres/octa-sphere.js +4 -2
- package/dist/engines/little-3d-engine/shapes/primitives/spheres/uv-sphere.d.ts +3 -2
- package/dist/engines/little-3d-engine/shapes/primitives/spheres/uv-sphere.js +4 -2
- package/dist/engines/little-3d-engine/shapes/primitives/tetrahedron.d.ts +3 -2
- package/dist/engines/little-3d-engine/shapes/primitives/tetrahedron.js +4 -2
- package/dist/umd/spinner.global.js +255 -63
- package/dist/umd/spinner.global.min.js +58 -17
- package/package.json +1 -1
|
@@ -139,7 +139,10 @@ var Spinner3D = (() => {
|
|
|
139
139
|
const positions = new Float32Array(triangles * 9);
|
|
140
140
|
const normals = new Float32Array(triangles * 9);
|
|
141
141
|
const colors = new Float32Array(triangles * 9);
|
|
142
|
+
const emissives = new Float32Array(triangles * 9);
|
|
143
|
+
const speculars = new Float32Array(triangles * 12);
|
|
142
144
|
let o = 0;
|
|
145
|
+
let so = 0;
|
|
143
146
|
for (const face of mesh.faces) {
|
|
144
147
|
const v0 = mesh.vertices[face.indices[0]];
|
|
145
148
|
const v1 = mesh.vertices[face.indices[1]];
|
|
@@ -149,6 +152,15 @@ var Spinner3D = (() => {
|
|
|
149
152
|
const cr = r / 255;
|
|
150
153
|
const cg = g / 255;
|
|
151
154
|
const cb = b / 255;
|
|
155
|
+
const emissive = face.material?.emissive;
|
|
156
|
+
const er = emissive ? emissive[0] : 0;
|
|
157
|
+
const eg = emissive ? emissive[1] : 0;
|
|
158
|
+
const eb = emissive ? emissive[2] : 0;
|
|
159
|
+
const specular = face.material?.specular;
|
|
160
|
+
const sr = specular ? specular[0] : 0;
|
|
161
|
+
const sg = specular ? specular[1] : 0;
|
|
162
|
+
const sb = specular ? specular[2] : 0;
|
|
163
|
+
const sn = specular ? face.material?.shininess ?? 32 : 1;
|
|
152
164
|
for (let k = 1; k < face.indices.length - 1; k++) {
|
|
153
165
|
const tri = [face.indices[0], face.indices[k], face.indices[k + 1]];
|
|
154
166
|
for (const index of tri) {
|
|
@@ -162,11 +174,19 @@ var Spinner3D = (() => {
|
|
|
162
174
|
colors[o] = cr;
|
|
163
175
|
colors[o + 1] = cg;
|
|
164
176
|
colors[o + 2] = cb;
|
|
177
|
+
emissives[o] = er;
|
|
178
|
+
emissives[o + 1] = eg;
|
|
179
|
+
emissives[o + 2] = eb;
|
|
180
|
+
speculars[so] = sr;
|
|
181
|
+
speculars[so + 1] = sg;
|
|
182
|
+
speculars[so + 2] = sb;
|
|
183
|
+
speculars[so + 3] = sn;
|
|
165
184
|
o += 3;
|
|
185
|
+
so += 4;
|
|
166
186
|
}
|
|
167
187
|
}
|
|
168
188
|
}
|
|
169
|
-
return { positions, normals, colors, count: positions.length / 3 };
|
|
189
|
+
return { positions, normals, colors, emissives, speculars, count: positions.length / 3 };
|
|
170
190
|
}
|
|
171
191
|
function midpoint(a, b) {
|
|
172
192
|
return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2, z: (a.z + b.z) / 2 };
|
|
@@ -204,13 +224,42 @@ var Spinner3D = (() => {
|
|
|
204
224
|
function clamp012(value) {
|
|
205
225
|
return Math.min(1, Math.max(0, value));
|
|
206
226
|
}
|
|
207
|
-
function
|
|
227
|
+
function clamp255(value) {
|
|
228
|
+
return Math.round(Math.min(255, Math.max(0, value)));
|
|
229
|
+
}
|
|
230
|
+
function shade(normal, color, light, surface) {
|
|
208
231
|
const lambert = Math.max(0, dot(normal, light.toLight));
|
|
209
232
|
const brightness = clamp012(light.ambient + light.intensity * lambert);
|
|
210
|
-
const [
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
233
|
+
const [baseR, baseG, baseB] = parseColor(color);
|
|
234
|
+
let r = baseR * brightness;
|
|
235
|
+
let g = baseG * brightness;
|
|
236
|
+
let b = baseB * brightness;
|
|
237
|
+
const material = surface?.material;
|
|
238
|
+
const specular = material?.specular;
|
|
239
|
+
const viewDir = surface?.viewDir;
|
|
240
|
+
if (specular && viewDir && lambert > 0) {
|
|
241
|
+
const half = normalize({
|
|
242
|
+
x: light.toLight.x + viewDir.x,
|
|
243
|
+
y: light.toLight.y + viewDir.y,
|
|
244
|
+
z: light.toLight.z + viewDir.z
|
|
245
|
+
});
|
|
246
|
+
const shininess = material?.shininess ?? 32;
|
|
247
|
+
const highlight = Math.pow(Math.max(0, dot(normal, half)), shininess) * light.intensity * 255;
|
|
248
|
+
r += highlight * specular[0];
|
|
249
|
+
g += highlight * specular[1];
|
|
250
|
+
b += highlight * specular[2];
|
|
251
|
+
}
|
|
252
|
+
const emissive = material?.emissive;
|
|
253
|
+
if (emissive) {
|
|
254
|
+
r += emissive[0] * 255;
|
|
255
|
+
g += emissive[1] * 255;
|
|
256
|
+
b += emissive[2] * 255;
|
|
257
|
+
}
|
|
258
|
+
return [clamp255(r), clamp255(g), clamp255(b)];
|
|
259
|
+
}
|
|
260
|
+
function shadeColor(normal, color, light, surface) {
|
|
261
|
+
const [r, g, b] = shade(normal, color, light, surface);
|
|
262
|
+
return `rgb(${r}, ${g}, ${b})`;
|
|
214
263
|
}
|
|
215
264
|
var DEFAULTS2, Light;
|
|
216
265
|
var init_light = __esm({
|
|
@@ -233,8 +282,8 @@ var Spinner3D = (() => {
|
|
|
233
282
|
};
|
|
234
283
|
}
|
|
235
284
|
/** Convenience wrapper around {@link shadeColor} using this light. */
|
|
236
|
-
shade(normal, color) {
|
|
237
|
-
return shadeColor(normal, color, this.params);
|
|
285
|
+
shade(normal, color, surface) {
|
|
286
|
+
return shadeColor(normal, color, this.params, surface);
|
|
238
287
|
}
|
|
239
288
|
};
|
|
240
289
|
}
|
|
@@ -274,28 +323,51 @@ var Spinner3D = (() => {
|
|
|
274
323
|
in vec3 aPos;
|
|
275
324
|
in vec3 aNormal;
|
|
276
325
|
in vec3 aColor;
|
|
326
|
+
in vec3 aEmissive;
|
|
327
|
+
in vec4 aSpecular;
|
|
277
328
|
uniform mat4 uViewProj;
|
|
278
329
|
uniform mat4 uModel;
|
|
279
330
|
out vec3 vNormal;
|
|
280
331
|
out vec3 vColor;
|
|
332
|
+
out vec3 vEmissive;
|
|
333
|
+
out vec4 vSpecular;
|
|
334
|
+
out vec3 vWorldPos;
|
|
281
335
|
void main() {
|
|
282
336
|
vNormal = mat3(uModel) * aNormal;
|
|
283
337
|
vColor = aColor;
|
|
284
|
-
|
|
338
|
+
vEmissive = aEmissive;
|
|
339
|
+
vSpecular = aSpecular;
|
|
340
|
+
vec4 world = uModel * vec4(aPos, 1.0);
|
|
341
|
+
vWorldPos = world.xyz;
|
|
342
|
+
gl_Position = uViewProj * world;
|
|
285
343
|
}`;
|
|
286
344
|
FRAGMENT_SHADER = `#version 300 es
|
|
287
345
|
precision mediump float;
|
|
288
346
|
in vec3 vNormal;
|
|
289
347
|
in vec3 vColor;
|
|
348
|
+
in vec3 vEmissive;
|
|
349
|
+
in vec4 vSpecular;
|
|
350
|
+
in vec3 vWorldPos;
|
|
290
351
|
uniform vec3 uToLight;
|
|
352
|
+
uniform vec3 uEye;
|
|
291
353
|
uniform float uIntensity;
|
|
292
354
|
uniform float uAmbient;
|
|
293
355
|
uniform float uOpacity;
|
|
294
356
|
out vec4 fragColor;
|
|
295
357
|
void main() {
|
|
296
|
-
|
|
358
|
+
vec3 normal = normalize(vNormal);
|
|
359
|
+
vec3 toLight = normalize(uToLight);
|
|
360
|
+
float lambert = max(dot(normal, toLight), 0.0);
|
|
297
361
|
float brightness = clamp(uAmbient + uIntensity * lambert, 0.0, 1.0);
|
|
298
|
-
|
|
362
|
+
vec3 lit = vColor * brightness;
|
|
363
|
+
if (lambert > 0.0) {
|
|
364
|
+
vec3 viewDir = normalize(uEye - vWorldPos);
|
|
365
|
+
vec3 halfVec = normalize(toLight + viewDir);
|
|
366
|
+
float highlight = pow(max(dot(normal, halfVec), 0.0), vSpecular.w) * uIntensity;
|
|
367
|
+
lit += highlight * vSpecular.xyz;
|
|
368
|
+
}
|
|
369
|
+
lit += vEmissive;
|
|
370
|
+
fragColor = vec4(lit, uOpacity);
|
|
299
371
|
}`;
|
|
300
372
|
WebGLRenderer = class {
|
|
301
373
|
constructor(options = {}) {
|
|
@@ -316,9 +388,12 @@ void main() {
|
|
|
316
388
|
aPos: gl.getAttribLocation(this.program, "aPos"),
|
|
317
389
|
aNormal: gl.getAttribLocation(this.program, "aNormal"),
|
|
318
390
|
aColor: gl.getAttribLocation(this.program, "aColor"),
|
|
391
|
+
aEmissive: gl.getAttribLocation(this.program, "aEmissive"),
|
|
392
|
+
aSpecular: gl.getAttribLocation(this.program, "aSpecular"),
|
|
319
393
|
uViewProj: gl.getUniformLocation(this.program, "uViewProj"),
|
|
320
394
|
uModel: gl.getUniformLocation(this.program, "uModel"),
|
|
321
395
|
uToLight: gl.getUniformLocation(this.program, "uToLight"),
|
|
396
|
+
uEye: gl.getUniformLocation(this.program, "uEye"),
|
|
322
397
|
uIntensity: gl.getUniformLocation(this.program, "uIntensity"),
|
|
323
398
|
uAmbient: gl.getUniformLocation(this.program, "uAmbient"),
|
|
324
399
|
uOpacity: gl.getUniformLocation(this.program, "uOpacity")
|
|
@@ -342,17 +417,19 @@ void main() {
|
|
|
342
417
|
const data = expandToTriangles(mesh);
|
|
343
418
|
const vao = gl.createVertexArray();
|
|
344
419
|
gl.bindVertexArray(vao);
|
|
345
|
-
const attribute = (location, array) => {
|
|
420
|
+
const attribute = (location, array, size = 3) => {
|
|
346
421
|
if (location < 0) return;
|
|
347
422
|
const buffer = gl.createBuffer();
|
|
348
423
|
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
349
424
|
gl.bufferData(gl.ARRAY_BUFFER, array, gl.STATIC_DRAW);
|
|
350
425
|
gl.enableVertexAttribArray(location);
|
|
351
|
-
gl.vertexAttribPointer(location,
|
|
426
|
+
gl.vertexAttribPointer(location, size, gl.FLOAT, false, 0, 0);
|
|
352
427
|
};
|
|
353
428
|
attribute(loc.aPos, data.positions);
|
|
354
429
|
attribute(loc.aNormal, data.normals);
|
|
355
430
|
attribute(loc.aColor, data.colors);
|
|
431
|
+
attribute(loc.aEmissive, data.emissives);
|
|
432
|
+
attribute(loc.aSpecular, data.speculars, 4);
|
|
356
433
|
gl.bindVertexArray(null);
|
|
357
434
|
const result = { vao, count: data.count };
|
|
358
435
|
this.cache.set(mesh, result);
|
|
@@ -367,6 +444,7 @@ void main() {
|
|
|
367
444
|
gl.useProgram(this.program);
|
|
368
445
|
gl.uniformMatrix4fv(loc.uViewProj, false, new Float32Array(frame.viewProjection));
|
|
369
446
|
gl.uniform3f(loc.uToLight, frame.light.toLight.x, frame.light.toLight.y, frame.light.toLight.z);
|
|
447
|
+
gl.uniform3f(loc.uEye, frame.eye.x, frame.eye.y, frame.eye.z);
|
|
370
448
|
gl.uniform1f(loc.uIntensity, frame.light.intensity);
|
|
371
449
|
gl.uniform1f(loc.uAmbient, frame.light.ambient);
|
|
372
450
|
gl.disable(gl.BLEND);
|
|
@@ -441,6 +519,7 @@ struct Uniforms {
|
|
|
441
519
|
model: mat4x4<f32>,
|
|
442
520
|
toLight: vec4<f32>,
|
|
443
521
|
params: vec4<f32>,
|
|
522
|
+
eye: vec4<f32>,
|
|
444
523
|
};
|
|
445
524
|
@group(0) @binding(0) var<uniform> u: Uniforms;
|
|
446
525
|
|
|
@@ -448,23 +527,40 @@ struct VSOut {
|
|
|
448
527
|
@builtin(position) position: vec4<f32>,
|
|
449
528
|
@location(0) normal: vec3<f32>,
|
|
450
529
|
@location(1) color: vec3<f32>,
|
|
530
|
+
@location(2) emissive: vec3<f32>,
|
|
531
|
+
@location(3) specular: vec4<f32>,
|
|
532
|
+
@location(4) worldPos: vec3<f32>,
|
|
451
533
|
};
|
|
452
534
|
|
|
453
535
|
@vertex
|
|
454
|
-
fn vs(@location(0) pos: vec3<f32>, @location(1) normal: vec3<f32>, @location(2) color: vec3<f32>) -> VSOut {
|
|
536
|
+
fn vs(@location(0) pos: vec3<f32>, @location(1) normal: vec3<f32>, @location(2) color: vec3<f32>, @location(3) emissive: vec3<f32>, @location(4) specular: vec4<f32>) -> VSOut {
|
|
455
537
|
var out: VSOut;
|
|
456
538
|
let m = mat3x3<f32>(u.model[0].xyz, u.model[1].xyz, u.model[2].xyz);
|
|
457
539
|
out.normal = m * normal;
|
|
458
540
|
out.color = color;
|
|
459
|
-
out.
|
|
541
|
+
out.emissive = emissive;
|
|
542
|
+
out.specular = specular;
|
|
543
|
+
let world = u.model * vec4<f32>(pos, 1.0);
|
|
544
|
+
out.worldPos = world.xyz;
|
|
545
|
+
out.position = u.viewProj * world;
|
|
460
546
|
return out;
|
|
461
547
|
}
|
|
462
548
|
|
|
463
549
|
@fragment
|
|
464
550
|
fn fs(in: VSOut) -> @location(0) vec4<f32> {
|
|
465
|
-
let
|
|
551
|
+
let normal = normalize(in.normal);
|
|
552
|
+
let toLight = normalize(u.toLight.xyz);
|
|
553
|
+
let lambert = max(dot(normal, toLight), 0.0);
|
|
466
554
|
let brightness = clamp(u.params.y + u.params.x * lambert, 0.0, 1.0);
|
|
467
|
-
|
|
555
|
+
var lit = in.color * brightness;
|
|
556
|
+
if (lambert > 0.0) {
|
|
557
|
+
let viewDir = normalize(u.eye.xyz - in.worldPos);
|
|
558
|
+
let halfVec = normalize(toLight + viewDir);
|
|
559
|
+
let highlight = pow(max(dot(normal, halfVec), 0.0), in.specular.w) * u.params.x;
|
|
560
|
+
lit = lit + highlight * in.specular.xyz;
|
|
561
|
+
}
|
|
562
|
+
lit = lit + in.emissive;
|
|
563
|
+
return vec4<f32>(lit, u.params.z);
|
|
468
564
|
}
|
|
469
565
|
`;
|
|
470
566
|
CLIP_Z_FIX = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0.5, 0, 0, 0, 0.5, 1];
|
|
@@ -505,13 +601,15 @@ fn fs(in: VSOut) -> @location(0) vec4<f32> {
|
|
|
505
601
|
{
|
|
506
602
|
binding: 0,
|
|
507
603
|
visibility: stage.VERTEX | stage.FRAGMENT,
|
|
508
|
-
buffer: { type: "uniform", hasDynamicOffset: true, minBindingSize:
|
|
604
|
+
buffer: { type: "uniform", hasDynamicOffset: true, minBindingSize: 176 }
|
|
509
605
|
}
|
|
510
606
|
]
|
|
511
607
|
});
|
|
512
|
-
const vertexBuffer = (location) => ({
|
|
513
|
-
arrayStride:
|
|
514
|
-
attributes: [
|
|
608
|
+
const vertexBuffer = (location, components = 3) => ({
|
|
609
|
+
arrayStride: components * 4,
|
|
610
|
+
attributes: [
|
|
611
|
+
{ shaderLocation: location, offset: 0, format: `float32x${components}` }
|
|
612
|
+
]
|
|
515
613
|
});
|
|
516
614
|
const pipelineLayout = device.createPipelineLayout({ bindGroupLayouts: [layout] });
|
|
517
615
|
const blend = {
|
|
@@ -527,7 +625,13 @@ fn fs(in: VSOut) -> @location(0) vec4<f32> {
|
|
|
527
625
|
vertex: {
|
|
528
626
|
module,
|
|
529
627
|
entryPoint: "vs",
|
|
530
|
-
buffers: [
|
|
628
|
+
buffers: [
|
|
629
|
+
vertexBuffer(0),
|
|
630
|
+
vertexBuffer(1),
|
|
631
|
+
vertexBuffer(2),
|
|
632
|
+
vertexBuffer(3),
|
|
633
|
+
vertexBuffer(4, 4)
|
|
634
|
+
]
|
|
531
635
|
},
|
|
532
636
|
fragment: {
|
|
533
637
|
module,
|
|
@@ -580,6 +684,8 @@ fn fs(in: VSOut) -> @location(0) vec4<f32> {
|
|
|
580
684
|
position: upload(data.positions),
|
|
581
685
|
normal: upload(data.normals),
|
|
582
686
|
color: upload(data.colors),
|
|
687
|
+
emissive: upload(data.emissives),
|
|
688
|
+
specular: upload(data.speculars),
|
|
583
689
|
count: data.count
|
|
584
690
|
};
|
|
585
691
|
this.cache.set(mesh, result);
|
|
@@ -631,7 +737,7 @@ fn fs(in: VSOut) -> @location(0) vec4<f32> {
|
|
|
631
737
|
const bindGroup = this.device.createBindGroup({
|
|
632
738
|
layout,
|
|
633
739
|
entries: [
|
|
634
|
-
{ binding: 0, resource: { buffer: this.uniformBuffer, offset: 0, size:
|
|
740
|
+
{ binding: 0, resource: { buffer: this.uniformBuffer, offset: 0, size: 176 } }
|
|
635
741
|
]
|
|
636
742
|
});
|
|
637
743
|
draws.forEach((draw, i) => {
|
|
@@ -640,6 +746,7 @@ fn fs(in: VSOut) -> @location(0) vec4<f32> {
|
|
|
640
746
|
data.set(draw.item.model, 16);
|
|
641
747
|
data.set([frame.light.toLight.x, frame.light.toLight.y, frame.light.toLight.z, 0], 32);
|
|
642
748
|
data.set([frame.light.intensity, frame.light.ambient, draw.opacity, 0], 36);
|
|
749
|
+
data.set([frame.eye.x, frame.eye.y, frame.eye.z, 0], 40);
|
|
643
750
|
this.device.queue.writeBuffer(this.uniformBuffer, i * UNIFORM_STRIDE, data);
|
|
644
751
|
});
|
|
645
752
|
const encoder = this.device.createCommandEncoder();
|
|
@@ -666,6 +773,8 @@ fn fs(in: VSOut) -> @location(0) vec4<f32> {
|
|
|
666
773
|
pass.setVertexBuffer(0, mesh.position);
|
|
667
774
|
pass.setVertexBuffer(1, mesh.normal);
|
|
668
775
|
pass.setVertexBuffer(2, mesh.color);
|
|
776
|
+
pass.setVertexBuffer(3, mesh.emissive);
|
|
777
|
+
pass.setVertexBuffer(4, mesh.specular);
|
|
669
778
|
pass.draw(mesh.count);
|
|
670
779
|
});
|
|
671
780
|
pass.end();
|
|
@@ -677,6 +786,8 @@ fn fs(in: VSOut) -> @location(0) vec4<f32> {
|
|
|
677
786
|
mesh.position.destroy?.();
|
|
678
787
|
mesh.normal.destroy?.();
|
|
679
788
|
mesh.color.destroy?.();
|
|
789
|
+
mesh.emissive.destroy?.();
|
|
790
|
+
mesh.specular.destroy?.();
|
|
680
791
|
}
|
|
681
792
|
this.cache.clear();
|
|
682
793
|
this.uniformBuffer?.destroy?.();
|
|
@@ -759,9 +870,30 @@ fn fs(in: VSOut) -> @location(0) vec4<f32> {
|
|
|
759
870
|
depth += dot(d, d);
|
|
760
871
|
}
|
|
761
872
|
depth /= face.indices.length;
|
|
873
|
+
let surface;
|
|
874
|
+
const material = face.material;
|
|
875
|
+
if (material) {
|
|
876
|
+
if (material.specular) {
|
|
877
|
+
let cx = 0;
|
|
878
|
+
let cy = 0;
|
|
879
|
+
let cz = 0;
|
|
880
|
+
for (const i of face.indices) {
|
|
881
|
+
cx += world[i].x;
|
|
882
|
+
cy += world[i].y;
|
|
883
|
+
cz += world[i].z;
|
|
884
|
+
}
|
|
885
|
+
const inv = 1 / face.indices.length;
|
|
886
|
+
const viewDir = normalize(
|
|
887
|
+
subtract(frame.eye, { x: cx * inv, y: cy * inv, z: cz * inv })
|
|
888
|
+
);
|
|
889
|
+
surface = { material, viewDir };
|
|
890
|
+
} else {
|
|
891
|
+
surface = { material };
|
|
892
|
+
}
|
|
893
|
+
}
|
|
762
894
|
polygons.push({
|
|
763
895
|
points,
|
|
764
|
-
color: shadeColor(normal, face.color, frame.light),
|
|
896
|
+
color: shadeColor(normal, face.color, frame.light, surface),
|
|
765
897
|
depth,
|
|
766
898
|
opacity: faceOpacity
|
|
767
899
|
});
|
|
@@ -1512,6 +1644,7 @@ void main() {
|
|
|
1512
1644
|
SpinAnimation: () => SpinAnimation,
|
|
1513
1645
|
WebGLTexturedRenderer: () => WebGLTexturedRenderer,
|
|
1514
1646
|
WebGPUTexturedRenderer: () => WebGPUTexturedRenderer,
|
|
1647
|
+
attachMaterial: () => attachMaterial,
|
|
1515
1648
|
centerAndScaleMesh: () => centerAndScaleMesh,
|
|
1516
1649
|
chargedOrb: () => chargedOrb,
|
|
1517
1650
|
circleMotion: () => circleMotion,
|
|
@@ -1729,6 +1862,12 @@ void main() {
|
|
|
1729
1862
|
init_math();
|
|
1730
1863
|
|
|
1731
1864
|
// src/engines/little-3d-engine/core/mesh.ts
|
|
1865
|
+
function attachMaterial(mesh, material) {
|
|
1866
|
+
if (material) {
|
|
1867
|
+
for (const face of mesh.faces) face.material = material;
|
|
1868
|
+
}
|
|
1869
|
+
return mesh;
|
|
1870
|
+
}
|
|
1732
1871
|
function transform(init) {
|
|
1733
1872
|
return {
|
|
1734
1873
|
position: init?.position ?? { x: 0, y: 0, z: 0 },
|
|
@@ -1743,7 +1882,7 @@ void main() {
|
|
|
1743
1882
|
|
|
1744
1883
|
// src/engines/little-3d-engine/shapes/primitives/cube.ts
|
|
1745
1884
|
var DEFAULT_COLORS = ["#3b82f6", "#8b5cf6", "#ec4899", "#f59e0b", "#10b981", "#ef4444"];
|
|
1746
|
-
function cube(size = 1, colors = DEFAULT_COLORS) {
|
|
1885
|
+
function cube(size = 1, colors = DEFAULT_COLORS, material) {
|
|
1747
1886
|
const h = size / 2;
|
|
1748
1887
|
const vertices = [
|
|
1749
1888
|
{ x: -h, y: -h, z: h },
|
|
@@ -1763,12 +1902,12 @@ void main() {
|
|
|
1763
1902
|
{ indices: [1, 5, 6, 2], color: colors[4 % colors.length] },
|
|
1764
1903
|
{ indices: [4, 0, 3, 7], color: colors[5 % colors.length] }
|
|
1765
1904
|
];
|
|
1766
|
-
return { vertices, faces };
|
|
1905
|
+
return attachMaterial({ vertices, faces }, material);
|
|
1767
1906
|
}
|
|
1768
1907
|
|
|
1769
1908
|
// src/engines/little-3d-engine/shapes/primitives/quad.ts
|
|
1770
1909
|
var DEFAULT_COLORS2 = ["#3b82f6"];
|
|
1771
|
-
function quad(size = 1, colors = DEFAULT_COLORS2) {
|
|
1910
|
+
function quad(size = 1, colors = DEFAULT_COLORS2, material) {
|
|
1772
1911
|
const s = size / 2;
|
|
1773
1912
|
const vertices = [
|
|
1774
1913
|
{ x: -s, y: -s, z: 0 },
|
|
@@ -1776,12 +1915,15 @@ void main() {
|
|
|
1776
1915
|
{ x: s, y: s, z: 0 },
|
|
1777
1916
|
{ x: -s, y: s, z: 0 }
|
|
1778
1917
|
];
|
|
1779
|
-
return
|
|
1918
|
+
return attachMaterial(
|
|
1919
|
+
{ vertices, faces: [{ indices: [0, 1, 2, 3], color: colors[0] }] },
|
|
1920
|
+
material
|
|
1921
|
+
);
|
|
1780
1922
|
}
|
|
1781
1923
|
|
|
1782
1924
|
// src/engines/little-3d-engine/shapes/primitives/tetrahedron.ts
|
|
1783
1925
|
var DEFAULT_COLORS3 = ["#3b82f6", "#8b5cf6", "#ec4899", "#f59e0b"];
|
|
1784
|
-
function tetrahedron(size = 1, colors = DEFAULT_COLORS3) {
|
|
1926
|
+
function tetrahedron(size = 1, colors = DEFAULT_COLORS3, material) {
|
|
1785
1927
|
const s = size / 2;
|
|
1786
1928
|
const vertices = [
|
|
1787
1929
|
{ x: s, y: s, z: s },
|
|
@@ -1795,7 +1937,7 @@ void main() {
|
|
|
1795
1937
|
{ indices: [0, 2, 3], color: colors[2 % colors.length] },
|
|
1796
1938
|
{ indices: [1, 3, 2], color: colors[3 % colors.length] }
|
|
1797
1939
|
];
|
|
1798
|
-
return { vertices, faces };
|
|
1940
|
+
return attachMaterial({ vertices, faces }, material);
|
|
1799
1941
|
}
|
|
1800
1942
|
|
|
1801
1943
|
// src/engines/little-3d-engine/shapes/primitives/octahedron.ts
|
|
@@ -1809,7 +1951,7 @@ void main() {
|
|
|
1809
1951
|
"#06b6d4",
|
|
1810
1952
|
"#eab308"
|
|
1811
1953
|
];
|
|
1812
|
-
function octahedron(size = 1, colors = DEFAULT_COLORS4) {
|
|
1954
|
+
function octahedron(size = 1, colors = DEFAULT_COLORS4, material) {
|
|
1813
1955
|
const r = size / 2;
|
|
1814
1956
|
const vertices = [
|
|
1815
1957
|
{ x: r, y: 0, z: 0 },
|
|
@@ -1829,12 +1971,12 @@ void main() {
|
|
|
1829
1971
|
{ indices: [5, 3, 1], color: colors[6 % colors.length] },
|
|
1830
1972
|
{ indices: [5, 0, 3], color: colors[7 % colors.length] }
|
|
1831
1973
|
];
|
|
1832
|
-
return { vertices, faces };
|
|
1974
|
+
return attachMaterial({ vertices, faces }, material);
|
|
1833
1975
|
}
|
|
1834
1976
|
|
|
1835
1977
|
// src/engines/little-3d-engine/shapes/primitives/pyramid.ts
|
|
1836
1978
|
var DEFAULT_COLORS5 = ["#3b82f6", "#8b5cf6", "#ec4899", "#f59e0b", "#10b981"];
|
|
1837
|
-
function pyramid(size = 1, colors = DEFAULT_COLORS5) {
|
|
1979
|
+
function pyramid(size = 1, colors = DEFAULT_COLORS5, material) {
|
|
1838
1980
|
const h = size / 2;
|
|
1839
1981
|
const vertices = [
|
|
1840
1982
|
{ x: -h, y: -h, z: h },
|
|
@@ -1850,12 +1992,12 @@ void main() {
|
|
|
1850
1992
|
{ indices: [4, 2, 3], color: colors[3 % colors.length] },
|
|
1851
1993
|
{ indices: [4, 3, 0], color: colors[4 % colors.length] }
|
|
1852
1994
|
];
|
|
1853
|
-
return { vertices, faces };
|
|
1995
|
+
return attachMaterial({ vertices, faces }, material);
|
|
1854
1996
|
}
|
|
1855
1997
|
|
|
1856
1998
|
// src/engines/little-3d-engine/shapes/primitives/spheres/uv-sphere.ts
|
|
1857
1999
|
var DEFAULT_COLORS6 = ["#3b82f6", "#8b5cf6", "#ec4899", "#f59e0b", "#10b981", "#ef4444"];
|
|
1858
|
-
function uvSphere(size = 1, detail = 1, colors = DEFAULT_COLORS6) {
|
|
2000
|
+
function uvSphere(size = 1, detail = 1, colors = DEFAULT_COLORS6, material) {
|
|
1859
2001
|
const r = size / 2;
|
|
1860
2002
|
const d = Math.max(1, Math.floor(detail));
|
|
1861
2003
|
const slices = Math.max(4, d * 4);
|
|
@@ -1894,7 +2036,7 @@ void main() {
|
|
|
1894
2036
|
color: color()
|
|
1895
2037
|
});
|
|
1896
2038
|
}
|
|
1897
|
-
return { vertices, faces };
|
|
2039
|
+
return attachMaterial({ vertices, faces }, material);
|
|
1898
2040
|
}
|
|
1899
2041
|
|
|
1900
2042
|
// src/engines/little-3d-engine/shapes/primitives/spheres/icosphere.ts
|
|
@@ -1937,8 +2079,11 @@ void main() {
|
|
|
1937
2079
|
[8, 6, 7],
|
|
1938
2080
|
[9, 8, 1]
|
|
1939
2081
|
];
|
|
1940
|
-
function icosphere(size = 1, detail = 1, colors = DEFAULT_COLORS7) {
|
|
1941
|
-
return
|
|
2082
|
+
function icosphere(size = 1, detail = 1, colors = DEFAULT_COLORS7, material) {
|
|
2083
|
+
return attachMaterial(
|
|
2084
|
+
sphereFromTriangles(SEED_VERTICES, SEED_FACES, size, detail, colors),
|
|
2085
|
+
material
|
|
2086
|
+
);
|
|
1942
2087
|
}
|
|
1943
2088
|
|
|
1944
2089
|
// src/engines/little-3d-engine/shapes/primitives/spheres/octa-sphere.ts
|
|
@@ -1962,8 +2107,11 @@ void main() {
|
|
|
1962
2107
|
[5, 3, 1],
|
|
1963
2108
|
[5, 0, 3]
|
|
1964
2109
|
];
|
|
1965
|
-
function octaSphere(size = 1, detail = 1, colors = DEFAULT_COLORS8) {
|
|
1966
|
-
return
|
|
2110
|
+
function octaSphere(size = 1, detail = 1, colors = DEFAULT_COLORS8, material) {
|
|
2111
|
+
return attachMaterial(
|
|
2112
|
+
sphereFromTriangles(SEED_VERTICES2, SEED_FACES2, size, detail, colors),
|
|
2113
|
+
material
|
|
2114
|
+
);
|
|
1967
2115
|
}
|
|
1968
2116
|
|
|
1969
2117
|
// src/engines/little-3d-engine/shapes/primitives/spheres/cube-sphere.ts
|
|
@@ -1976,7 +2124,7 @@ void main() {
|
|
|
1976
2124
|
{ normal: [0, 1, 0], right: [1, 0, 0], up: [0, 0, -1] },
|
|
1977
2125
|
{ normal: [0, -1, 0], right: [1, 0, 0], up: [0, 0, 1] }
|
|
1978
2126
|
];
|
|
1979
|
-
function cubeSphere(size = 1, detail = 1, colors = DEFAULT_COLORS9) {
|
|
2127
|
+
function cubeSphere(size = 1, detail = 1, colors = DEFAULT_COLORS9, material) {
|
|
1980
2128
|
const r = size / 2;
|
|
1981
2129
|
const n = Math.max(1, Math.floor(detail));
|
|
1982
2130
|
const vertices = [];
|
|
@@ -2005,13 +2153,13 @@ void main() {
|
|
|
2005
2153
|
}
|
|
2006
2154
|
}
|
|
2007
2155
|
}
|
|
2008
|
-
return { vertices, faces };
|
|
2156
|
+
return attachMaterial({ vertices, faces }, material);
|
|
2009
2157
|
}
|
|
2010
2158
|
|
|
2011
2159
|
// src/engines/little-3d-engine/shapes/complex/plane.ts
|
|
2012
2160
|
var DEFAULT_COLORS10 = ["#e0f2fe", "#7dd3fc", "#38bdf8", "#f8fafc"];
|
|
2013
|
-
function planeMesh(colors = DEFAULT_COLORS10) {
|
|
2014
|
-
return {
|
|
2161
|
+
function planeMesh(colors = DEFAULT_COLORS10, material) {
|
|
2162
|
+
return attachMaterial({
|
|
2015
2163
|
vertices: [
|
|
2016
2164
|
{ x: 0.9, y: 0, z: 0 },
|
|
2017
2165
|
{ x: -0.2, y: 0, z: 0.82 },
|
|
@@ -2039,7 +2187,7 @@ void main() {
|
|
|
2039
2187
|
{ indices: [3, 6, 8], color: colors[0] ?? DEFAULT_COLORS10[0] },
|
|
2040
2188
|
{ indices: [3, 8, 6], color: colors[1] ?? DEFAULT_COLORS10[1] }
|
|
2041
2189
|
]
|
|
2042
|
-
};
|
|
2190
|
+
}, material);
|
|
2043
2191
|
}
|
|
2044
2192
|
|
|
2045
2193
|
// src/engines/little-3d-engine/textures/dynamic/canvas-texture.ts
|
|
@@ -2596,10 +2744,14 @@ void main() {
|
|
|
2596
2744
|
const pick = Array.isArray(color) ? (i) => color[i % color.length] : () => color;
|
|
2597
2745
|
return { vertices: mesh.vertices, faces: mesh.faces.map((f, i) => ({ ...f, color: pick(i) })) };
|
|
2598
2746
|
}
|
|
2747
|
+
function applyMaterial(mesh, material) {
|
|
2748
|
+
if (!material) return mesh;
|
|
2749
|
+
return { vertices: mesh.vertices, faces: mesh.faces.map((f) => ({ ...f, material })) };
|
|
2750
|
+
}
|
|
2599
2751
|
var SpinAnimation = class {
|
|
2600
2752
|
constructor(options = {}) {
|
|
2601
2753
|
this.exited = false;
|
|
2602
|
-
this.mesh = applyColor(resolveMesh(options.shape), options.color);
|
|
2754
|
+
this.mesh = applyMaterial(applyColor(resolveMesh(options.shape), options.color), options.material);
|
|
2603
2755
|
this.spinX = options.spinX ?? 7e-4;
|
|
2604
2756
|
this.spinY = options.spinY ?? 11e-4;
|
|
2605
2757
|
this.backend = options.backend;
|
|
@@ -3292,6 +3444,7 @@ void main() {
|
|
|
3292
3444
|
var CENTER_POP_OUT_AT = REENTER_MS + (MINIS - 1) * REENTER_STAGGER_MS + 150;
|
|
3293
3445
|
var CENTER_POP_OUT_MS = 420;
|
|
3294
3446
|
var PARKED = { x: 0, y: 0, z: 50 };
|
|
3447
|
+
var ORB_MATERIAL = { specular: [1, 1, 1], shininess: 28 };
|
|
3295
3448
|
var CENTER_COLORS = ["#67e8f9", "#22d3ee", "#0ea5e9", "#38bdf8", "#7dd3fc"];
|
|
3296
3449
|
var MINI_COLORS = [
|
|
3297
3450
|
["#e0f2fe", "#bae6fd", "#7dd3fc"],
|
|
@@ -3320,9 +3473,9 @@ void main() {
|
|
|
3320
3473
|
backend: this.backend,
|
|
3321
3474
|
camera: { position: { x: 0, y: 0, z: CAMERA_Z } }
|
|
3322
3475
|
});
|
|
3323
|
-
this.center = engine.add(icosphere(1, 2, CENTER_COLORS), { scale: 0 });
|
|
3476
|
+
this.center = engine.add(icosphere(1, 2, CENTER_COLORS, ORB_MATERIAL), { scale: 0 });
|
|
3324
3477
|
for (let i = 0; i < MINIS; i++) {
|
|
3325
|
-
const mesh = icosphere(1, 1, MINI_COLORS[i % MINI_COLORS.length]);
|
|
3478
|
+
const mesh = icosphere(1, 1, MINI_COLORS[i % MINI_COLORS.length], ORB_MATERIAL);
|
|
3326
3479
|
this.minis.push(engine.add(mesh, { scale: 0, transparency: { ...MINI_TRANSPARENCY } }));
|
|
3327
3480
|
}
|
|
3328
3481
|
this.engine = engine;
|
|
@@ -3907,6 +4060,7 @@ void main() {
|
|
|
3907
4060
|
var TRAIL_OUTRO_MS = 1200;
|
|
3908
4061
|
var TRANSPARENCY = { mode: "two-sided", opacity: 0.275 };
|
|
3909
4062
|
var CAR_COLORS = ["#bae6fd", "#7dd3fc", "#38bdf8", "#0ea5e9", "#a5f3fc", "#e0f2fe"];
|
|
4063
|
+
var CAR_MATERIAL = { emissive: [0.1, 0.2, 0.3] };
|
|
3910
4064
|
var WORLD_UP2 = { x: 0, y: 1, z: 0 };
|
|
3911
4065
|
function clamp016(value) {
|
|
3912
4066
|
return Math.max(0, Math.min(1, value));
|
|
@@ -3967,7 +4121,7 @@ void main() {
|
|
|
3967
4121
|
backend: this.backend,
|
|
3968
4122
|
camera: { position: { x: 0, y: 0, z: CAMERA_Z3 }, fov: FOV2 }
|
|
3969
4123
|
});
|
|
3970
|
-
const mesh = cube(1, CAR_COLORS);
|
|
4124
|
+
const mesh = cube(1, CAR_COLORS, CAR_MATERIAL);
|
|
3971
4125
|
for (let i = 0; i < MAX_CARS; i++) {
|
|
3972
4126
|
this.cars.push(engine.add(mesh, { scale: 0, transparency: { ...TRANSPARENCY } }));
|
|
3973
4127
|
}
|
|
@@ -4653,28 +4807,64 @@ void main() {
|
|
|
4653
4807
|
|
|
4654
4808
|
// src/engines/little-3d-engine/loaders/obj.ts
|
|
4655
4809
|
var DEFAULT_COLORS12 = ["#3b82f6", "#8b5cf6", "#ec4899", "#f59e0b", "#10b981", "#ef4444"];
|
|
4810
|
+
function clamp018(value) {
|
|
4811
|
+
return Math.min(1, Math.max(0, value));
|
|
4812
|
+
}
|
|
4656
4813
|
function channelToHex(value) {
|
|
4657
4814
|
const channel = Number.parseFloat(value);
|
|
4658
4815
|
if (!Number.isFinite(channel)) return void 0;
|
|
4659
|
-
return Math.round(
|
|
4660
|
-
}
|
|
4661
|
-
function
|
|
4662
|
-
const
|
|
4663
|
-
|
|
4816
|
+
return Math.round(clamp018(channel) * 255).toString(16).padStart(2, "0");
|
|
4817
|
+
}
|
|
4818
|
+
function parseRgb(parts) {
|
|
4819
|
+
const channels = parts.slice(1, 4).map(Number.parseFloat);
|
|
4820
|
+
if (channels.length !== 3 || !channels.every(Number.isFinite)) return void 0;
|
|
4821
|
+
return [clamp018(channels[0]), clamp018(channels[1]), clamp018(channels[2])];
|
|
4822
|
+
}
|
|
4823
|
+
function toMaterial(surface) {
|
|
4824
|
+
const material = {};
|
|
4825
|
+
if (surface.specular) material.specular = surface.specular;
|
|
4826
|
+
if (surface.shininess !== void 0) material.shininess = surface.shininess;
|
|
4827
|
+
if (surface.emissive) material.emissive = surface.emissive;
|
|
4828
|
+
return Object.keys(material).length > 0 ? material : void 0;
|
|
4829
|
+
}
|
|
4830
|
+
function parseMtl(text) {
|
|
4831
|
+
const materials = /* @__PURE__ */ new Map();
|
|
4832
|
+
const surfaces = /* @__PURE__ */ new Map();
|
|
4833
|
+
let name;
|
|
4664
4834
|
for (const line of text.split("\n")) {
|
|
4665
4835
|
const trimmed = line.trim();
|
|
4666
4836
|
if (trimmed === "" || trimmed.startsWith("#")) continue;
|
|
4667
4837
|
const parts = trimmed.split(/\s+/);
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4838
|
+
const keyword = parts[0];
|
|
4839
|
+
if (keyword === "newmtl") {
|
|
4840
|
+
name = parts.slice(1).join(" ");
|
|
4841
|
+
if (name && !materials.has(name)) {
|
|
4842
|
+
materials.set(name, {});
|
|
4843
|
+
surfaces.set(name, {});
|
|
4844
|
+
}
|
|
4845
|
+
continue;
|
|
4846
|
+
}
|
|
4847
|
+
if (!name) continue;
|
|
4848
|
+
const entry = materials.get(name);
|
|
4849
|
+
const surface = surfaces.get(name);
|
|
4850
|
+
if (keyword === "Kd") {
|
|
4671
4851
|
const channels = parts.slice(1, 4).map(channelToHex);
|
|
4672
4852
|
if (channels.length === 3 && channels.every((channel) => channel !== void 0)) {
|
|
4673
|
-
|
|
4853
|
+
entry.color = `#${channels.join("")}`;
|
|
4674
4854
|
}
|
|
4855
|
+
} else if (keyword === "Ks") {
|
|
4856
|
+
surface.specular = parseRgb(parts);
|
|
4857
|
+
} else if (keyword === "Ns") {
|
|
4858
|
+
const ns = Number.parseFloat(parts[1]);
|
|
4859
|
+
if (Number.isFinite(ns)) surface.shininess = Math.max(0, ns);
|
|
4860
|
+
} else if (keyword === "Ke") {
|
|
4861
|
+
surface.emissive = parseRgb(parts);
|
|
4675
4862
|
}
|
|
4676
4863
|
}
|
|
4677
|
-
|
|
4864
|
+
for (const [key, surface] of surfaces) {
|
|
4865
|
+
materials.get(key).material = toMaterial(surface);
|
|
4866
|
+
}
|
|
4867
|
+
return materials;
|
|
4678
4868
|
}
|
|
4679
4869
|
function resolveIndex(token, vertexCount) {
|
|
4680
4870
|
const n = parseInt(token, 10);
|
|
@@ -4682,7 +4872,7 @@ void main() {
|
|
|
4682
4872
|
}
|
|
4683
4873
|
function parseObj(text, options = {}) {
|
|
4684
4874
|
const colors = options.colors ?? DEFAULT_COLORS12;
|
|
4685
|
-
const
|
|
4875
|
+
const materials = options.useMtlColors && options.mtl ? parseMtl(options.mtl) : void 0;
|
|
4686
4876
|
const vertices = [];
|
|
4687
4877
|
const faces = [];
|
|
4688
4878
|
let material;
|
|
@@ -4706,9 +4896,11 @@ void main() {
|
|
|
4706
4896
|
indices.push(resolveIndex(vertexToken, vertices.length));
|
|
4707
4897
|
}
|
|
4708
4898
|
if (indices.length >= 3) {
|
|
4709
|
-
const
|
|
4710
|
-
const color =
|
|
4711
|
-
|
|
4899
|
+
const entry = material ? materials?.get(material) : void 0;
|
|
4900
|
+
const color = entry?.color ?? (materials ? colors[0] ?? "#888888" : colors[faces.length % colors.length]);
|
|
4901
|
+
const face = { indices, color };
|
|
4902
|
+
if (entry?.material) face.material = entry.material;
|
|
4903
|
+
faces.push(face);
|
|
4712
4904
|
}
|
|
4713
4905
|
}
|
|
4714
4906
|
}
|