3d-spinner 0.9.3 → 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.
Files changed (62) hide show
  1. package/dist/animations/charged-orb.js +4 -2
  2. package/dist/animations/ghost-train.d.ts +72 -0
  3. package/dist/animations/ghost-train.js +249 -0
  4. package/dist/animations/grid-assembly.d.ts +11 -8
  5. package/dist/animations/grid-assembly.js +44 -30
  6. package/dist/animations/rocket-launch.d.ts +58 -0
  7. package/dist/animations/rocket-launch.js +375 -0
  8. package/dist/animations/spin.d.ts +3 -1
  9. package/dist/animations/spin.js +6 -1
  10. package/dist/cjs/animations/charged-orb.cjs +170 -28
  11. package/dist/cjs/animations/grid-assembly.cjs +192 -172
  12. package/dist/cjs/animations/object-motion.cjs +156 -24
  13. package/dist/cjs/animations/particles.cjs +167 -26
  14. package/dist/cjs/animations/spin.cjs +169 -27
  15. package/dist/cjs/engines/little-3d-engine/little-3d-engine.cjs +215 -55
  16. package/dist/cjs/engines/little-3d-engine/loaders/obj.cjs +51 -13
  17. package/dist/cjs/engines/little-3d-engine/renderers/canvas2d-textured.cjs +56 -6
  18. package/dist/cjs/engines/little-3d-engine/renderers/webgl-textured.cjs +55 -6
  19. package/dist/cjs/engines/little-3d-engine/renderers/webgpu-textured.cjs +64 -11
  20. package/dist/cjs/prefabs/prefabs.cjs +817 -205
  21. package/dist/engines/little-3d-engine/core/geometry.d.ts +15 -2
  22. package/dist/engines/little-3d-engine/core/geometry.js +25 -3
  23. package/dist/engines/little-3d-engine/core/light.d.ts +28 -4
  24. package/dist/engines/little-3d-engine/core/light.js +48 -7
  25. package/dist/engines/little-3d-engine/core/mesh.d.ts +33 -0
  26. package/dist/engines/little-3d-engine/core/mesh.js +12 -0
  27. package/dist/engines/little-3d-engine/little-3d-engine.d.ts +2 -2
  28. package/dist/engines/little-3d-engine/little-3d-engine.js +1 -1
  29. package/dist/engines/little-3d-engine/loaders/obj.d.ts +8 -7
  30. package/dist/engines/little-3d-engine/loaders/obj.js +74 -20
  31. package/dist/engines/little-3d-engine/renderers/canvas2d.js +23 -1
  32. package/dist/engines/little-3d-engine/renderers/webgl.js +34 -5
  33. package/dist/engines/little-3d-engine/renderers/webgpu.js +43 -10
  34. package/dist/engines/little-3d-engine/shapes/complex/plane.d.ts +8 -3
  35. package/dist/engines/little-3d-engine/shapes/complex/plane.js +10 -4
  36. package/dist/engines/little-3d-engine/shapes/primitives/cube.d.ts +3 -2
  37. package/dist/engines/little-3d-engine/shapes/primitives/cube.js +4 -2
  38. package/dist/engines/little-3d-engine/shapes/primitives/octahedron.d.ts +3 -2
  39. package/dist/engines/little-3d-engine/shapes/primitives/octahedron.js +4 -2
  40. package/dist/engines/little-3d-engine/shapes/primitives/pyramid.d.ts +3 -2
  41. package/dist/engines/little-3d-engine/shapes/primitives/pyramid.js +4 -2
  42. package/dist/engines/little-3d-engine/shapes/primitives/quad.d.ts +3 -2
  43. package/dist/engines/little-3d-engine/shapes/primitives/quad.js +4 -2
  44. package/dist/engines/little-3d-engine/shapes/primitives/spheres/cube-sphere.d.ts +3 -2
  45. package/dist/engines/little-3d-engine/shapes/primitives/spheres/cube-sphere.js +4 -2
  46. package/dist/engines/little-3d-engine/shapes/primitives/spheres/icosphere.d.ts +3 -2
  47. package/dist/engines/little-3d-engine/shapes/primitives/spheres/icosphere.js +4 -2
  48. package/dist/engines/little-3d-engine/shapes/primitives/spheres/octa-sphere.d.ts +3 -2
  49. package/dist/engines/little-3d-engine/shapes/primitives/spheres/octa-sphere.js +4 -2
  50. package/dist/engines/little-3d-engine/shapes/primitives/spheres/uv-sphere.d.ts +3 -2
  51. package/dist/engines/little-3d-engine/shapes/primitives/spheres/uv-sphere.js +4 -2
  52. package/dist/engines/little-3d-engine/shapes/primitives/tetrahedron.d.ts +3 -2
  53. package/dist/engines/little-3d-engine/shapes/primitives/tetrahedron.js +4 -2
  54. package/dist/engines/little-3d-engine/textures/dynamic/star.d.ts +14 -2
  55. package/dist/engines/little-3d-engine/textures/dynamic/star.js +26 -11
  56. package/dist/prefabs/ghost-train.d.ts +5 -4
  57. package/dist/prefabs/ghost-train.js +17 -24
  58. package/dist/prefabs/rocket-launch.d.ts +7 -5
  59. package/dist/prefabs/rocket-launch.js +10 -53
  60. package/dist/umd/spinner.global.js +877 -167
  61. package/dist/umd/spinner.global.min.js +58 -17
  62. package/package.json +2 -2
@@ -136,7 +136,10 @@ function expandToTriangles(mesh) {
136
136
  const positions = new Float32Array(triangles * 9);
137
137
  const normals = new Float32Array(triangles * 9);
138
138
  const colors = new Float32Array(triangles * 9);
139
+ const emissives = new Float32Array(triangles * 9);
140
+ const speculars = new Float32Array(triangles * 12);
139
141
  let o = 0;
142
+ let so = 0;
140
143
  for (const face of mesh.faces) {
141
144
  const v0 = mesh.vertices[face.indices[0]];
142
145
  const v1 = mesh.vertices[face.indices[1]];
@@ -146,6 +149,15 @@ function expandToTriangles(mesh) {
146
149
  const cr = r / 255;
147
150
  const cg = g / 255;
148
151
  const cb = b / 255;
152
+ const emissive = face.material?.emissive;
153
+ const er = emissive ? emissive[0] : 0;
154
+ const eg = emissive ? emissive[1] : 0;
155
+ const eb = emissive ? emissive[2] : 0;
156
+ const specular = face.material?.specular;
157
+ const sr = specular ? specular[0] : 0;
158
+ const sg = specular ? specular[1] : 0;
159
+ const sb = specular ? specular[2] : 0;
160
+ const sn = specular ? face.material?.shininess ?? 32 : 1;
149
161
  for (let k = 1; k < face.indices.length - 1; k++) {
150
162
  const tri = [face.indices[0], face.indices[k], face.indices[k + 1]];
151
163
  for (const index of tri) {
@@ -159,11 +171,19 @@ function expandToTriangles(mesh) {
159
171
  colors[o] = cr;
160
172
  colors[o + 1] = cg;
161
173
  colors[o + 2] = cb;
174
+ emissives[o] = er;
175
+ emissives[o + 1] = eg;
176
+ emissives[o + 2] = eb;
177
+ speculars[so] = sr;
178
+ speculars[so + 1] = sg;
179
+ speculars[so + 2] = sb;
180
+ speculars[so + 3] = sn;
162
181
  o += 3;
182
+ so += 4;
163
183
  }
164
184
  }
165
185
  }
166
- return { positions, normals, colors, count: positions.length / 3 };
186
+ return { positions, normals, colors, emissives, speculars, count: positions.length / 3 };
167
187
  }
168
188
  function midpoint(a, b) {
169
189
  return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2, z: (a.z + b.z) / 2 };
@@ -201,13 +221,42 @@ var init_geometry = __esm({
201
221
  function clamp01(value) {
202
222
  return Math.min(1, Math.max(0, value));
203
223
  }
204
- function shadeColor(normal, color, light) {
224
+ function clamp255(value) {
225
+ return Math.round(Math.min(255, Math.max(0, value)));
226
+ }
227
+ function shade(normal, color, light, surface) {
205
228
  const lambert = Math.max(0, dot(normal, light.toLight));
206
229
  const brightness = clamp01(light.ambient + light.intensity * lambert);
207
- const [r, g, b] = parseColor(color);
208
- return `rgb(${Math.round(r * brightness)}, ${Math.round(g * brightness)}, ${Math.round(
209
- b * brightness
210
- )})`;
230
+ const [baseR, baseG, baseB] = parseColor(color);
231
+ let r = baseR * brightness;
232
+ let g = baseG * brightness;
233
+ let b = baseB * brightness;
234
+ const material = surface?.material;
235
+ const specular = material?.specular;
236
+ const viewDir = surface?.viewDir;
237
+ if (specular && viewDir && lambert > 0) {
238
+ const half = normalize({
239
+ x: light.toLight.x + viewDir.x,
240
+ y: light.toLight.y + viewDir.y,
241
+ z: light.toLight.z + viewDir.z
242
+ });
243
+ const shininess = material?.shininess ?? 32;
244
+ const highlight = Math.pow(Math.max(0, dot(normal, half)), shininess) * light.intensity * 255;
245
+ r += highlight * specular[0];
246
+ g += highlight * specular[1];
247
+ b += highlight * specular[2];
248
+ }
249
+ const emissive = material?.emissive;
250
+ if (emissive) {
251
+ r += emissive[0] * 255;
252
+ g += emissive[1] * 255;
253
+ b += emissive[2] * 255;
254
+ }
255
+ return [clamp255(r), clamp255(g), clamp255(b)];
256
+ }
257
+ function shadeColor(normal, color, light, surface) {
258
+ const [r, g, b] = shade(normal, color, light, surface);
259
+ return `rgb(${r}, ${g}, ${b})`;
211
260
  }
212
261
  var DEFAULTS2, Light;
213
262
  var init_light = __esm({
@@ -230,8 +279,8 @@ var init_light = __esm({
230
279
  };
231
280
  }
232
281
  /** Convenience wrapper around {@link shadeColor} using this light. */
233
- shade(normal, color) {
234
- return shadeColor(normal, color, this.params);
282
+ shade(normal, color, surface) {
283
+ return shadeColor(normal, color, this.params, surface);
235
284
  }
236
285
  };
237
286
  }
@@ -271,28 +320,51 @@ var init_webgl = __esm({
271
320
  in vec3 aPos;
272
321
  in vec3 aNormal;
273
322
  in vec3 aColor;
323
+ in vec3 aEmissive;
324
+ in vec4 aSpecular;
274
325
  uniform mat4 uViewProj;
275
326
  uniform mat4 uModel;
276
327
  out vec3 vNormal;
277
328
  out vec3 vColor;
329
+ out vec3 vEmissive;
330
+ out vec4 vSpecular;
331
+ out vec3 vWorldPos;
278
332
  void main() {
279
333
  vNormal = mat3(uModel) * aNormal;
280
334
  vColor = aColor;
281
- gl_Position = uViewProj * uModel * vec4(aPos, 1.0);
335
+ vEmissive = aEmissive;
336
+ vSpecular = aSpecular;
337
+ vec4 world = uModel * vec4(aPos, 1.0);
338
+ vWorldPos = world.xyz;
339
+ gl_Position = uViewProj * world;
282
340
  }`;
283
341
  FRAGMENT_SHADER = `#version 300 es
284
342
  precision mediump float;
285
343
  in vec3 vNormal;
286
344
  in vec3 vColor;
345
+ in vec3 vEmissive;
346
+ in vec4 vSpecular;
347
+ in vec3 vWorldPos;
287
348
  uniform vec3 uToLight;
349
+ uniform vec3 uEye;
288
350
  uniform float uIntensity;
289
351
  uniform float uAmbient;
290
352
  uniform float uOpacity;
291
353
  out vec4 fragColor;
292
354
  void main() {
293
- float lambert = max(dot(normalize(vNormal), normalize(uToLight)), 0.0);
355
+ vec3 normal = normalize(vNormal);
356
+ vec3 toLight = normalize(uToLight);
357
+ float lambert = max(dot(normal, toLight), 0.0);
294
358
  float brightness = clamp(uAmbient + uIntensity * lambert, 0.0, 1.0);
295
- fragColor = vec4(vColor * brightness, uOpacity);
359
+ vec3 lit = vColor * brightness;
360
+ if (lambert > 0.0) {
361
+ vec3 viewDir = normalize(uEye - vWorldPos);
362
+ vec3 halfVec = normalize(toLight + viewDir);
363
+ float highlight = pow(max(dot(normal, halfVec), 0.0), vSpecular.w) * uIntensity;
364
+ lit += highlight * vSpecular.xyz;
365
+ }
366
+ lit += vEmissive;
367
+ fragColor = vec4(lit, uOpacity);
296
368
  }`;
297
369
  WebGLRenderer = class {
298
370
  constructor(options = {}) {
@@ -313,9 +385,12 @@ void main() {
313
385
  aPos: gl.getAttribLocation(this.program, "aPos"),
314
386
  aNormal: gl.getAttribLocation(this.program, "aNormal"),
315
387
  aColor: gl.getAttribLocation(this.program, "aColor"),
388
+ aEmissive: gl.getAttribLocation(this.program, "aEmissive"),
389
+ aSpecular: gl.getAttribLocation(this.program, "aSpecular"),
316
390
  uViewProj: gl.getUniformLocation(this.program, "uViewProj"),
317
391
  uModel: gl.getUniformLocation(this.program, "uModel"),
318
392
  uToLight: gl.getUniformLocation(this.program, "uToLight"),
393
+ uEye: gl.getUniformLocation(this.program, "uEye"),
319
394
  uIntensity: gl.getUniformLocation(this.program, "uIntensity"),
320
395
  uAmbient: gl.getUniformLocation(this.program, "uAmbient"),
321
396
  uOpacity: gl.getUniformLocation(this.program, "uOpacity")
@@ -339,17 +414,19 @@ void main() {
339
414
  const data = expandToTriangles(mesh);
340
415
  const vao = gl.createVertexArray();
341
416
  gl.bindVertexArray(vao);
342
- const attribute = (location, array) => {
417
+ const attribute = (location, array, size = 3) => {
343
418
  if (location < 0) return;
344
419
  const buffer = gl.createBuffer();
345
420
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
346
421
  gl.bufferData(gl.ARRAY_BUFFER, array, gl.STATIC_DRAW);
347
422
  gl.enableVertexAttribArray(location);
348
- gl.vertexAttribPointer(location, 3, gl.FLOAT, false, 0, 0);
423
+ gl.vertexAttribPointer(location, size, gl.FLOAT, false, 0, 0);
349
424
  };
350
425
  attribute(loc.aPos, data.positions);
351
426
  attribute(loc.aNormal, data.normals);
352
427
  attribute(loc.aColor, data.colors);
428
+ attribute(loc.aEmissive, data.emissives);
429
+ attribute(loc.aSpecular, data.speculars, 4);
353
430
  gl.bindVertexArray(null);
354
431
  const result = { vao, count: data.count };
355
432
  this.cache.set(mesh, result);
@@ -364,6 +441,7 @@ void main() {
364
441
  gl.useProgram(this.program);
365
442
  gl.uniformMatrix4fv(loc.uViewProj, false, new Float32Array(frame.viewProjection));
366
443
  gl.uniform3f(loc.uToLight, frame.light.toLight.x, frame.light.toLight.y, frame.light.toLight.z);
444
+ gl.uniform3f(loc.uEye, frame.eye.x, frame.eye.y, frame.eye.z);
367
445
  gl.uniform1f(loc.uIntensity, frame.light.intensity);
368
446
  gl.uniform1f(loc.uAmbient, frame.light.ambient);
369
447
  gl.disable(gl.BLEND);
@@ -438,6 +516,7 @@ struct Uniforms {
438
516
  model: mat4x4<f32>,
439
517
  toLight: vec4<f32>,
440
518
  params: vec4<f32>,
519
+ eye: vec4<f32>,
441
520
  };
442
521
  @group(0) @binding(0) var<uniform> u: Uniforms;
443
522
 
@@ -445,23 +524,40 @@ struct VSOut {
445
524
  @builtin(position) position: vec4<f32>,
446
525
  @location(0) normal: vec3<f32>,
447
526
  @location(1) color: vec3<f32>,
527
+ @location(2) emissive: vec3<f32>,
528
+ @location(3) specular: vec4<f32>,
529
+ @location(4) worldPos: vec3<f32>,
448
530
  };
449
531
 
450
532
  @vertex
451
- fn vs(@location(0) pos: vec3<f32>, @location(1) normal: vec3<f32>, @location(2) color: vec3<f32>) -> VSOut {
533
+ 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 {
452
534
  var out: VSOut;
453
535
  let m = mat3x3<f32>(u.model[0].xyz, u.model[1].xyz, u.model[2].xyz);
454
536
  out.normal = m * normal;
455
537
  out.color = color;
456
- out.position = u.viewProj * u.model * vec4<f32>(pos, 1.0);
538
+ out.emissive = emissive;
539
+ out.specular = specular;
540
+ let world = u.model * vec4<f32>(pos, 1.0);
541
+ out.worldPos = world.xyz;
542
+ out.position = u.viewProj * world;
457
543
  return out;
458
544
  }
459
545
 
460
546
  @fragment
461
547
  fn fs(in: VSOut) -> @location(0) vec4<f32> {
462
- let lambert = max(dot(normalize(in.normal), normalize(u.toLight.xyz)), 0.0);
548
+ let normal = normalize(in.normal);
549
+ let toLight = normalize(u.toLight.xyz);
550
+ let lambert = max(dot(normal, toLight), 0.0);
463
551
  let brightness = clamp(u.params.y + u.params.x * lambert, 0.0, 1.0);
464
- return vec4<f32>(in.color * brightness, u.params.z);
552
+ var lit = in.color * brightness;
553
+ if (lambert > 0.0) {
554
+ let viewDir = normalize(u.eye.xyz - in.worldPos);
555
+ let halfVec = normalize(toLight + viewDir);
556
+ let highlight = pow(max(dot(normal, halfVec), 0.0), in.specular.w) * u.params.x;
557
+ lit = lit + highlight * in.specular.xyz;
558
+ }
559
+ lit = lit + in.emissive;
560
+ return vec4<f32>(lit, u.params.z);
465
561
  }
466
562
  `;
467
563
  CLIP_Z_FIX = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0.5, 0, 0, 0, 0.5, 1];
@@ -502,13 +598,15 @@ fn fs(in: VSOut) -> @location(0) vec4<f32> {
502
598
  {
503
599
  binding: 0,
504
600
  visibility: stage.VERTEX | stage.FRAGMENT,
505
- buffer: { type: "uniform", hasDynamicOffset: true, minBindingSize: 160 }
601
+ buffer: { type: "uniform", hasDynamicOffset: true, minBindingSize: 176 }
506
602
  }
507
603
  ]
508
604
  });
509
- const vertexBuffer = (location) => ({
510
- arrayStride: 12,
511
- attributes: [{ shaderLocation: location, offset: 0, format: "float32x3" }]
605
+ const vertexBuffer = (location, components = 3) => ({
606
+ arrayStride: components * 4,
607
+ attributes: [
608
+ { shaderLocation: location, offset: 0, format: `float32x${components}` }
609
+ ]
512
610
  });
513
611
  const pipelineLayout = device.createPipelineLayout({ bindGroupLayouts: [layout] });
514
612
  const blend = {
@@ -524,7 +622,13 @@ fn fs(in: VSOut) -> @location(0) vec4<f32> {
524
622
  vertex: {
525
623
  module: module2,
526
624
  entryPoint: "vs",
527
- buffers: [vertexBuffer(0), vertexBuffer(1), vertexBuffer(2)]
625
+ buffers: [
626
+ vertexBuffer(0),
627
+ vertexBuffer(1),
628
+ vertexBuffer(2),
629
+ vertexBuffer(3),
630
+ vertexBuffer(4, 4)
631
+ ]
528
632
  },
529
633
  fragment: {
530
634
  module: module2,
@@ -577,6 +681,8 @@ fn fs(in: VSOut) -> @location(0) vec4<f32> {
577
681
  position: upload(data.positions),
578
682
  normal: upload(data.normals),
579
683
  color: upload(data.colors),
684
+ emissive: upload(data.emissives),
685
+ specular: upload(data.speculars),
580
686
  count: data.count
581
687
  };
582
688
  this.cache.set(mesh, result);
@@ -628,7 +734,7 @@ fn fs(in: VSOut) -> @location(0) vec4<f32> {
628
734
  const bindGroup = this.device.createBindGroup({
629
735
  layout,
630
736
  entries: [
631
- { binding: 0, resource: { buffer: this.uniformBuffer, offset: 0, size: 160 } }
737
+ { binding: 0, resource: { buffer: this.uniformBuffer, offset: 0, size: 176 } }
632
738
  ]
633
739
  });
634
740
  draws.forEach((draw, i) => {
@@ -637,6 +743,7 @@ fn fs(in: VSOut) -> @location(0) vec4<f32> {
637
743
  data.set(draw.item.model, 16);
638
744
  data.set([frame.light.toLight.x, frame.light.toLight.y, frame.light.toLight.z, 0], 32);
639
745
  data.set([frame.light.intensity, frame.light.ambient, draw.opacity, 0], 36);
746
+ data.set([frame.eye.x, frame.eye.y, frame.eye.z, 0], 40);
640
747
  this.device.queue.writeBuffer(this.uniformBuffer, i * UNIFORM_STRIDE, data);
641
748
  });
642
749
  const encoder = this.device.createCommandEncoder();
@@ -663,6 +770,8 @@ fn fs(in: VSOut) -> @location(0) vec4<f32> {
663
770
  pass.setVertexBuffer(0, mesh.position);
664
771
  pass.setVertexBuffer(1, mesh.normal);
665
772
  pass.setVertexBuffer(2, mesh.color);
773
+ pass.setVertexBuffer(3, mesh.emissive);
774
+ pass.setVertexBuffer(4, mesh.specular);
666
775
  pass.draw(mesh.count);
667
776
  });
668
777
  pass.end();
@@ -674,6 +783,8 @@ fn fs(in: VSOut) -> @location(0) vec4<f32> {
674
783
  mesh.position.destroy?.();
675
784
  mesh.normal.destroy?.();
676
785
  mesh.color.destroy?.();
786
+ mesh.emissive.destroy?.();
787
+ mesh.specular.destroy?.();
677
788
  }
678
789
  this.cache.clear();
679
790
  this.uniformBuffer?.destroy?.();
@@ -756,9 +867,30 @@ var init_canvas2d = __esm({
756
867
  depth += dot(d, d);
757
868
  }
758
869
  depth /= face.indices.length;
870
+ let surface;
871
+ const material = face.material;
872
+ if (material) {
873
+ if (material.specular) {
874
+ let cx = 0;
875
+ let cy = 0;
876
+ let cz = 0;
877
+ for (const i of face.indices) {
878
+ cx += world[i].x;
879
+ cy += world[i].y;
880
+ cz += world[i].z;
881
+ }
882
+ const inv = 1 / face.indices.length;
883
+ const viewDir = normalize(
884
+ subtract(frame.eye, { x: cx * inv, y: cy * inv, z: cz * inv })
885
+ );
886
+ surface = { material, viewDir };
887
+ } else {
888
+ surface = { material };
889
+ }
890
+ }
759
891
  polygons.push({
760
892
  points,
761
- color: shadeColor(normal, face.color, frame.light),
893
+ color: shadeColor(normal, face.color, frame.light, surface),
762
894
  depth,
763
895
  opacity: faceOpacity
764
896
  });
@@ -888,6 +1020,12 @@ init_light();
888
1020
  init_math();
889
1021
 
890
1022
  // src/engines/little-3d-engine/core/mesh.ts
1023
+ function attachMaterial(mesh, material) {
1024
+ if (material) {
1025
+ for (const face of mesh.faces) face.material = material;
1026
+ }
1027
+ return mesh;
1028
+ }
891
1029
  function transform(init) {
892
1030
  return {
893
1031
  position: init?.position ?? { x: 0, y: 0, z: 0 },
@@ -940,8 +1078,11 @@ var SEED_FACES = [
940
1078
  [8, 6, 7],
941
1079
  [9, 8, 1]
942
1080
  ];
943
- function icosphere(size = 1, detail = 1, colors = DEFAULT_COLORS) {
944
- return sphereFromTriangles(SEED_VERTICES, SEED_FACES, size, detail, colors);
1081
+ function icosphere(size = 1, detail = 1, colors = DEFAULT_COLORS, material) {
1082
+ return attachMaterial(
1083
+ sphereFromTriangles(SEED_VERTICES, SEED_FACES, size, detail, colors),
1084
+ material
1085
+ );
945
1086
  }
946
1087
 
947
1088
  // src/engines/little-3d-engine/shapes/primitives/spheres/octa-sphere.ts
@@ -1148,6 +1289,7 @@ var REENTER_STAGGER_MS = 45;
1148
1289
  var CENTER_POP_OUT_AT = REENTER_MS + (MINIS - 1) * REENTER_STAGGER_MS + 150;
1149
1290
  var CENTER_POP_OUT_MS = 420;
1150
1291
  var PARKED = { x: 0, y: 0, z: 50 };
1292
+ var ORB_MATERIAL = { specular: [1, 1, 1], shininess: 28 };
1151
1293
  var CENTER_COLORS = ["#67e8f9", "#22d3ee", "#0ea5e9", "#38bdf8", "#7dd3fc"];
1152
1294
  var MINI_COLORS = [
1153
1295
  ["#e0f2fe", "#bae6fd", "#7dd3fc"],
@@ -1176,9 +1318,9 @@ var ChargedOrbAnimation = class {
1176
1318
  backend: this.backend,
1177
1319
  camera: { position: { x: 0, y: 0, z: CAMERA_Z } }
1178
1320
  });
1179
- this.center = engine.add(icosphere(1, 2, CENTER_COLORS), { scale: 0 });
1321
+ this.center = engine.add(icosphere(1, 2, CENTER_COLORS, ORB_MATERIAL), { scale: 0 });
1180
1322
  for (let i = 0; i < MINIS; i++) {
1181
- const mesh = icosphere(1, 1, MINI_COLORS[i % MINI_COLORS.length]);
1323
+ const mesh = icosphere(1, 1, MINI_COLORS[i % MINI_COLORS.length], ORB_MATERIAL);
1182
1324
  this.minis.push(engine.add(mesh, { scale: 0, transparency: { ...MINI_TRANSPARENCY } }));
1183
1325
  }
1184
1326
  this.engine = engine;