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
  var init_geometry = __esm({
169
189
  "src/engines/little-3d-engine/core/geometry.ts"() {
@@ -176,13 +196,42 @@ var init_geometry = __esm({
176
196
  function clamp01(value) {
177
197
  return Math.min(1, Math.max(0, value));
178
198
  }
179
- function shadeColor(normal, color, light) {
199
+ function clamp255(value) {
200
+ return Math.round(Math.min(255, Math.max(0, value)));
201
+ }
202
+ function shade(normal, color, light, surface) {
180
203
  const lambert = Math.max(0, dot(normal, light.toLight));
181
204
  const brightness = clamp01(light.ambient + light.intensity * lambert);
182
- const [r, g, b] = parseColor(color);
183
- return `rgb(${Math.round(r * brightness)}, ${Math.round(g * brightness)}, ${Math.round(
184
- b * brightness
185
- )})`;
205
+ const [baseR, baseG, baseB] = parseColor(color);
206
+ let r = baseR * brightness;
207
+ let g = baseG * brightness;
208
+ let b = baseB * brightness;
209
+ const material = surface?.material;
210
+ const specular = material?.specular;
211
+ const viewDir = surface?.viewDir;
212
+ if (specular && viewDir && lambert > 0) {
213
+ const half = normalize({
214
+ x: light.toLight.x + viewDir.x,
215
+ y: light.toLight.y + viewDir.y,
216
+ z: light.toLight.z + viewDir.z
217
+ });
218
+ const shininess = material?.shininess ?? 32;
219
+ const highlight = Math.pow(Math.max(0, dot(normal, half)), shininess) * light.intensity * 255;
220
+ r += highlight * specular[0];
221
+ g += highlight * specular[1];
222
+ b += highlight * specular[2];
223
+ }
224
+ const emissive = material?.emissive;
225
+ if (emissive) {
226
+ r += emissive[0] * 255;
227
+ g += emissive[1] * 255;
228
+ b += emissive[2] * 255;
229
+ }
230
+ return [clamp255(r), clamp255(g), clamp255(b)];
231
+ }
232
+ function shadeColor(normal, color, light, surface) {
233
+ const [r, g, b] = shade(normal, color, light, surface);
234
+ return `rgb(${r}, ${g}, ${b})`;
186
235
  }
187
236
  var DEFAULTS2, Light;
188
237
  var init_light = __esm({
@@ -205,8 +254,8 @@ var init_light = __esm({
205
254
  };
206
255
  }
207
256
  /** Convenience wrapper around {@link shadeColor} using this light. */
208
- shade(normal, color) {
209
- return shadeColor(normal, color, this.params);
257
+ shade(normal, color, surface) {
258
+ return shadeColor(normal, color, this.params, surface);
210
259
  }
211
260
  };
212
261
  }
@@ -246,28 +295,51 @@ var init_webgl = __esm({
246
295
  in vec3 aPos;
247
296
  in vec3 aNormal;
248
297
  in vec3 aColor;
298
+ in vec3 aEmissive;
299
+ in vec4 aSpecular;
249
300
  uniform mat4 uViewProj;
250
301
  uniform mat4 uModel;
251
302
  out vec3 vNormal;
252
303
  out vec3 vColor;
304
+ out vec3 vEmissive;
305
+ out vec4 vSpecular;
306
+ out vec3 vWorldPos;
253
307
  void main() {
254
308
  vNormal = mat3(uModel) * aNormal;
255
309
  vColor = aColor;
256
- gl_Position = uViewProj * uModel * vec4(aPos, 1.0);
310
+ vEmissive = aEmissive;
311
+ vSpecular = aSpecular;
312
+ vec4 world = uModel * vec4(aPos, 1.0);
313
+ vWorldPos = world.xyz;
314
+ gl_Position = uViewProj * world;
257
315
  }`;
258
316
  FRAGMENT_SHADER = `#version 300 es
259
317
  precision mediump float;
260
318
  in vec3 vNormal;
261
319
  in vec3 vColor;
320
+ in vec3 vEmissive;
321
+ in vec4 vSpecular;
322
+ in vec3 vWorldPos;
262
323
  uniform vec3 uToLight;
324
+ uniform vec3 uEye;
263
325
  uniform float uIntensity;
264
326
  uniform float uAmbient;
265
327
  uniform float uOpacity;
266
328
  out vec4 fragColor;
267
329
  void main() {
268
- float lambert = max(dot(normalize(vNormal), normalize(uToLight)), 0.0);
330
+ vec3 normal = normalize(vNormal);
331
+ vec3 toLight = normalize(uToLight);
332
+ float lambert = max(dot(normal, toLight), 0.0);
269
333
  float brightness = clamp(uAmbient + uIntensity * lambert, 0.0, 1.0);
270
- fragColor = vec4(vColor * brightness, uOpacity);
334
+ vec3 lit = vColor * brightness;
335
+ if (lambert > 0.0) {
336
+ vec3 viewDir = normalize(uEye - vWorldPos);
337
+ vec3 halfVec = normalize(toLight + viewDir);
338
+ float highlight = pow(max(dot(normal, halfVec), 0.0), vSpecular.w) * uIntensity;
339
+ lit += highlight * vSpecular.xyz;
340
+ }
341
+ lit += vEmissive;
342
+ fragColor = vec4(lit, uOpacity);
271
343
  }`;
272
344
  WebGLRenderer = class {
273
345
  constructor(options = {}) {
@@ -288,9 +360,12 @@ void main() {
288
360
  aPos: gl.getAttribLocation(this.program, "aPos"),
289
361
  aNormal: gl.getAttribLocation(this.program, "aNormal"),
290
362
  aColor: gl.getAttribLocation(this.program, "aColor"),
363
+ aEmissive: gl.getAttribLocation(this.program, "aEmissive"),
364
+ aSpecular: gl.getAttribLocation(this.program, "aSpecular"),
291
365
  uViewProj: gl.getUniformLocation(this.program, "uViewProj"),
292
366
  uModel: gl.getUniformLocation(this.program, "uModel"),
293
367
  uToLight: gl.getUniformLocation(this.program, "uToLight"),
368
+ uEye: gl.getUniformLocation(this.program, "uEye"),
294
369
  uIntensity: gl.getUniformLocation(this.program, "uIntensity"),
295
370
  uAmbient: gl.getUniformLocation(this.program, "uAmbient"),
296
371
  uOpacity: gl.getUniformLocation(this.program, "uOpacity")
@@ -314,17 +389,19 @@ void main() {
314
389
  const data = expandToTriangles(mesh);
315
390
  const vao = gl.createVertexArray();
316
391
  gl.bindVertexArray(vao);
317
- const attribute = (location, array) => {
392
+ const attribute = (location, array, size = 3) => {
318
393
  if (location < 0) return;
319
394
  const buffer = gl.createBuffer();
320
395
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
321
396
  gl.bufferData(gl.ARRAY_BUFFER, array, gl.STATIC_DRAW);
322
397
  gl.enableVertexAttribArray(location);
323
- gl.vertexAttribPointer(location, 3, gl.FLOAT, false, 0, 0);
398
+ gl.vertexAttribPointer(location, size, gl.FLOAT, false, 0, 0);
324
399
  };
325
400
  attribute(loc.aPos, data.positions);
326
401
  attribute(loc.aNormal, data.normals);
327
402
  attribute(loc.aColor, data.colors);
403
+ attribute(loc.aEmissive, data.emissives);
404
+ attribute(loc.aSpecular, data.speculars, 4);
328
405
  gl.bindVertexArray(null);
329
406
  const result = { vao, count: data.count };
330
407
  this.cache.set(mesh, result);
@@ -339,6 +416,7 @@ void main() {
339
416
  gl.useProgram(this.program);
340
417
  gl.uniformMatrix4fv(loc.uViewProj, false, new Float32Array(frame.viewProjection));
341
418
  gl.uniform3f(loc.uToLight, frame.light.toLight.x, frame.light.toLight.y, frame.light.toLight.z);
419
+ gl.uniform3f(loc.uEye, frame.eye.x, frame.eye.y, frame.eye.z);
342
420
  gl.uniform1f(loc.uIntensity, frame.light.intensity);
343
421
  gl.uniform1f(loc.uAmbient, frame.light.ambient);
344
422
  gl.disable(gl.BLEND);
@@ -413,6 +491,7 @@ struct Uniforms {
413
491
  model: mat4x4<f32>,
414
492
  toLight: vec4<f32>,
415
493
  params: vec4<f32>,
494
+ eye: vec4<f32>,
416
495
  };
417
496
  @group(0) @binding(0) var<uniform> u: Uniforms;
418
497
 
@@ -420,23 +499,40 @@ struct VSOut {
420
499
  @builtin(position) position: vec4<f32>,
421
500
  @location(0) normal: vec3<f32>,
422
501
  @location(1) color: vec3<f32>,
502
+ @location(2) emissive: vec3<f32>,
503
+ @location(3) specular: vec4<f32>,
504
+ @location(4) worldPos: vec3<f32>,
423
505
  };
424
506
 
425
507
  @vertex
426
- fn vs(@location(0) pos: vec3<f32>, @location(1) normal: vec3<f32>, @location(2) color: vec3<f32>) -> VSOut {
508
+ 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 {
427
509
  var out: VSOut;
428
510
  let m = mat3x3<f32>(u.model[0].xyz, u.model[1].xyz, u.model[2].xyz);
429
511
  out.normal = m * normal;
430
512
  out.color = color;
431
- out.position = u.viewProj * u.model * vec4<f32>(pos, 1.0);
513
+ out.emissive = emissive;
514
+ out.specular = specular;
515
+ let world = u.model * vec4<f32>(pos, 1.0);
516
+ out.worldPos = world.xyz;
517
+ out.position = u.viewProj * world;
432
518
  return out;
433
519
  }
434
520
 
435
521
  @fragment
436
522
  fn fs(in: VSOut) -> @location(0) vec4<f32> {
437
- let lambert = max(dot(normalize(in.normal), normalize(u.toLight.xyz)), 0.0);
523
+ let normal = normalize(in.normal);
524
+ let toLight = normalize(u.toLight.xyz);
525
+ let lambert = max(dot(normal, toLight), 0.0);
438
526
  let brightness = clamp(u.params.y + u.params.x * lambert, 0.0, 1.0);
439
- return vec4<f32>(in.color * brightness, u.params.z);
527
+ var lit = in.color * brightness;
528
+ if (lambert > 0.0) {
529
+ let viewDir = normalize(u.eye.xyz - in.worldPos);
530
+ let halfVec = normalize(toLight + viewDir);
531
+ let highlight = pow(max(dot(normal, halfVec), 0.0), in.specular.w) * u.params.x;
532
+ lit = lit + highlight * in.specular.xyz;
533
+ }
534
+ lit = lit + in.emissive;
535
+ return vec4<f32>(lit, u.params.z);
440
536
  }
441
537
  `;
442
538
  CLIP_Z_FIX = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0.5, 0, 0, 0, 0.5, 1];
@@ -477,13 +573,15 @@ fn fs(in: VSOut) -> @location(0) vec4<f32> {
477
573
  {
478
574
  binding: 0,
479
575
  visibility: stage.VERTEX | stage.FRAGMENT,
480
- buffer: { type: "uniform", hasDynamicOffset: true, minBindingSize: 160 }
576
+ buffer: { type: "uniform", hasDynamicOffset: true, minBindingSize: 176 }
481
577
  }
482
578
  ]
483
579
  });
484
- const vertexBuffer = (location) => ({
485
- arrayStride: 12,
486
- attributes: [{ shaderLocation: location, offset: 0, format: "float32x3" }]
580
+ const vertexBuffer = (location, components = 3) => ({
581
+ arrayStride: components * 4,
582
+ attributes: [
583
+ { shaderLocation: location, offset: 0, format: `float32x${components}` }
584
+ ]
487
585
  });
488
586
  const pipelineLayout = device.createPipelineLayout({ bindGroupLayouts: [layout] });
489
587
  const blend = {
@@ -499,7 +597,13 @@ fn fs(in: VSOut) -> @location(0) vec4<f32> {
499
597
  vertex: {
500
598
  module: module2,
501
599
  entryPoint: "vs",
502
- buffers: [vertexBuffer(0), vertexBuffer(1), vertexBuffer(2)]
600
+ buffers: [
601
+ vertexBuffer(0),
602
+ vertexBuffer(1),
603
+ vertexBuffer(2),
604
+ vertexBuffer(3),
605
+ vertexBuffer(4, 4)
606
+ ]
503
607
  },
504
608
  fragment: {
505
609
  module: module2,
@@ -552,6 +656,8 @@ fn fs(in: VSOut) -> @location(0) vec4<f32> {
552
656
  position: upload(data.positions),
553
657
  normal: upload(data.normals),
554
658
  color: upload(data.colors),
659
+ emissive: upload(data.emissives),
660
+ specular: upload(data.speculars),
555
661
  count: data.count
556
662
  };
557
663
  this.cache.set(mesh, result);
@@ -603,7 +709,7 @@ fn fs(in: VSOut) -> @location(0) vec4<f32> {
603
709
  const bindGroup = this.device.createBindGroup({
604
710
  layout,
605
711
  entries: [
606
- { binding: 0, resource: { buffer: this.uniformBuffer, offset: 0, size: 160 } }
712
+ { binding: 0, resource: { buffer: this.uniformBuffer, offset: 0, size: 176 } }
607
713
  ]
608
714
  });
609
715
  draws.forEach((draw, i) => {
@@ -612,6 +718,7 @@ fn fs(in: VSOut) -> @location(0) vec4<f32> {
612
718
  data.set(draw.item.model, 16);
613
719
  data.set([frame.light.toLight.x, frame.light.toLight.y, frame.light.toLight.z, 0], 32);
614
720
  data.set([frame.light.intensity, frame.light.ambient, draw.opacity, 0], 36);
721
+ data.set([frame.eye.x, frame.eye.y, frame.eye.z, 0], 40);
615
722
  this.device.queue.writeBuffer(this.uniformBuffer, i * UNIFORM_STRIDE, data);
616
723
  });
617
724
  const encoder = this.device.createCommandEncoder();
@@ -638,6 +745,8 @@ fn fs(in: VSOut) -> @location(0) vec4<f32> {
638
745
  pass.setVertexBuffer(0, mesh.position);
639
746
  pass.setVertexBuffer(1, mesh.normal);
640
747
  pass.setVertexBuffer(2, mesh.color);
748
+ pass.setVertexBuffer(3, mesh.emissive);
749
+ pass.setVertexBuffer(4, mesh.specular);
641
750
  pass.draw(mesh.count);
642
751
  });
643
752
  pass.end();
@@ -649,6 +758,8 @@ fn fs(in: VSOut) -> @location(0) vec4<f32> {
649
758
  mesh.position.destroy?.();
650
759
  mesh.normal.destroy?.();
651
760
  mesh.color.destroy?.();
761
+ mesh.emissive.destroy?.();
762
+ mesh.specular.destroy?.();
652
763
  }
653
764
  this.cache.clear();
654
765
  this.uniformBuffer?.destroy?.();
@@ -731,9 +842,30 @@ var init_canvas2d = __esm({
731
842
  depth += dot(d, d);
732
843
  }
733
844
  depth /= face.indices.length;
845
+ let surface;
846
+ const material = face.material;
847
+ if (material) {
848
+ if (material.specular) {
849
+ let cx = 0;
850
+ let cy = 0;
851
+ let cz = 0;
852
+ for (const i of face.indices) {
853
+ cx += world[i].x;
854
+ cy += world[i].y;
855
+ cz += world[i].z;
856
+ }
857
+ const inv = 1 / face.indices.length;
858
+ const viewDir = normalize(
859
+ subtract(frame.eye, { x: cx * inv, y: cy * inv, z: cz * inv })
860
+ );
861
+ surface = { material, viewDir };
862
+ } else {
863
+ surface = { material };
864
+ }
865
+ }
734
866
  polygons.push({
735
867
  points,
736
- color: shadeColor(normal, face.color, frame.light),
868
+ color: shadeColor(normal, face.color, frame.light, surface),
737
869
  depth,
738
870
  opacity: faceOpacity
739
871
  });
@@ -1557,6 +1689,12 @@ init_light();
1557
1689
  init_math();
1558
1690
 
1559
1691
  // src/engines/little-3d-engine/core/mesh.ts
1692
+ function attachMaterial(mesh, material) {
1693
+ if (material) {
1694
+ for (const face of mesh.faces) face.material = material;
1695
+ }
1696
+ return mesh;
1697
+ }
1560
1698
  function transform(init) {
1561
1699
  return {
1562
1700
  position: init?.position ?? { x: 0, y: 0, z: 0 },
@@ -1571,7 +1709,7 @@ init_light();
1571
1709
 
1572
1710
  // src/engines/little-3d-engine/shapes/primitives/quad.ts
1573
1711
  var DEFAULT_COLORS = ["#3b82f6"];
1574
- function quad(size = 1, colors = DEFAULT_COLORS) {
1712
+ function quad(size = 1, colors = DEFAULT_COLORS, material) {
1575
1713
  const s = size / 2;
1576
1714
  const vertices = [
1577
1715
  { x: -s, y: -s, z: 0 },
@@ -1579,7 +1717,10 @@ function quad(size = 1, colors = DEFAULT_COLORS) {
1579
1717
  { x: s, y: s, z: 0 },
1580
1718
  { x: -s, y: s, z: 0 }
1581
1719
  ];
1582
- return { vertices, faces: [{ indices: [0, 1, 2, 3], color: colors[0] }] };
1720
+ return attachMaterial(
1721
+ { vertices, faces: [{ indices: [0, 1, 2, 3], color: colors[0] }] },
1722
+ material
1723
+ );
1583
1724
  }
1584
1725
 
1585
1726
  // src/engines/little-3d-engine/shapes/primitives/spheres/icosphere.ts