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.
- package/dist/animations/charged-orb.js +4 -2
- package/dist/animations/ghost-train.d.ts +72 -0
- package/dist/animations/ghost-train.js +249 -0
- package/dist/animations/grid-assembly.d.ts +11 -8
- package/dist/animations/grid-assembly.js +44 -30
- package/dist/animations/rocket-launch.d.ts +58 -0
- package/dist/animations/rocket-launch.js +375 -0
- 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 +192 -172
- 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 +215 -55
- 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 +817 -205
- 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/engines/little-3d-engine/textures/dynamic/star.d.ts +14 -2
- package/dist/engines/little-3d-engine/textures/dynamic/star.js +26 -11
- package/dist/prefabs/ghost-train.d.ts +5 -4
- package/dist/prefabs/ghost-train.js +17 -24
- package/dist/prefabs/rocket-launch.d.ts +7 -5
- package/dist/prefabs/rocket-launch.js +10 -53
- package/dist/umd/spinner.global.js +877 -167
- package/dist/umd/spinner.global.min.js +58 -17
- 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,36 +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 };
|
|
167
|
-
}
|
|
168
|
-
function midpoint(a, b) {
|
|
169
|
-
return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2, z: (a.z + b.z) / 2 };
|
|
170
|
-
}
|
|
171
|
-
function sphereFromTriangles(seedVertices, seedFaces, size, detail, colors) {
|
|
172
|
-
const radius = size / 2;
|
|
173
|
-
let triangles = seedFaces.map((f) => f.map((i) => normalize(seedVertices[i])));
|
|
174
|
-
const levels = Math.max(0, Math.floor(detail) - 1);
|
|
175
|
-
for (let level = 0; level < levels; level++) {
|
|
176
|
-
const next = [];
|
|
177
|
-
for (const [a, b, c] of triangles) {
|
|
178
|
-
const ab = normalize(midpoint(a, b));
|
|
179
|
-
const bc = normalize(midpoint(b, c));
|
|
180
|
-
const ca = normalize(midpoint(c, a));
|
|
181
|
-
next.push([a, ab, ca], [b, bc, ab], [c, ca, bc], [ab, bc, ca]);
|
|
182
|
-
}
|
|
183
|
-
triangles = next;
|
|
184
|
-
}
|
|
185
|
-
const vertices = [];
|
|
186
|
-
const faces = triangles.map((tri, i) => {
|
|
187
|
-
const base = vertices.length;
|
|
188
|
-
vertices.push(scale(tri[0], radius), scale(tri[1], radius), scale(tri[2], radius));
|
|
189
|
-
return { indices: [base, base + 1, base + 2], color: colors[i % colors.length] };
|
|
190
|
-
});
|
|
191
|
-
return { vertices, faces };
|
|
186
|
+
return { positions, normals, colors, emissives, speculars, count: positions.length / 3 };
|
|
192
187
|
}
|
|
193
188
|
var init_geometry = __esm({
|
|
194
189
|
"src/engines/little-3d-engine/core/geometry.ts"() {
|
|
@@ -201,13 +196,42 @@ var init_geometry = __esm({
|
|
|
201
196
|
function clamp01(value) {
|
|
202
197
|
return Math.min(1, Math.max(0, value));
|
|
203
198
|
}
|
|
204
|
-
function
|
|
199
|
+
function clamp255(value) {
|
|
200
|
+
return Math.round(Math.min(255, Math.max(0, value)));
|
|
201
|
+
}
|
|
202
|
+
function shade(normal, color, light, surface) {
|
|
205
203
|
const lambert = Math.max(0, dot(normal, light.toLight));
|
|
206
204
|
const brightness = clamp01(light.ambient + light.intensity * lambert);
|
|
207
|
-
const [
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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})`;
|
|
211
235
|
}
|
|
212
236
|
var DEFAULTS2, Light;
|
|
213
237
|
var init_light = __esm({
|
|
@@ -230,8 +254,8 @@ var init_light = __esm({
|
|
|
230
254
|
};
|
|
231
255
|
}
|
|
232
256
|
/** Convenience wrapper around {@link shadeColor} using this light. */
|
|
233
|
-
shade(normal, color) {
|
|
234
|
-
return shadeColor(normal, color, this.params);
|
|
257
|
+
shade(normal, color, surface) {
|
|
258
|
+
return shadeColor(normal, color, this.params, surface);
|
|
235
259
|
}
|
|
236
260
|
};
|
|
237
261
|
}
|
|
@@ -271,28 +295,51 @@ var init_webgl = __esm({
|
|
|
271
295
|
in vec3 aPos;
|
|
272
296
|
in vec3 aNormal;
|
|
273
297
|
in vec3 aColor;
|
|
298
|
+
in vec3 aEmissive;
|
|
299
|
+
in vec4 aSpecular;
|
|
274
300
|
uniform mat4 uViewProj;
|
|
275
301
|
uniform mat4 uModel;
|
|
276
302
|
out vec3 vNormal;
|
|
277
303
|
out vec3 vColor;
|
|
304
|
+
out vec3 vEmissive;
|
|
305
|
+
out vec4 vSpecular;
|
|
306
|
+
out vec3 vWorldPos;
|
|
278
307
|
void main() {
|
|
279
308
|
vNormal = mat3(uModel) * aNormal;
|
|
280
309
|
vColor = aColor;
|
|
281
|
-
|
|
310
|
+
vEmissive = aEmissive;
|
|
311
|
+
vSpecular = aSpecular;
|
|
312
|
+
vec4 world = uModel * vec4(aPos, 1.0);
|
|
313
|
+
vWorldPos = world.xyz;
|
|
314
|
+
gl_Position = uViewProj * world;
|
|
282
315
|
}`;
|
|
283
316
|
FRAGMENT_SHADER = `#version 300 es
|
|
284
317
|
precision mediump float;
|
|
285
318
|
in vec3 vNormal;
|
|
286
319
|
in vec3 vColor;
|
|
320
|
+
in vec3 vEmissive;
|
|
321
|
+
in vec4 vSpecular;
|
|
322
|
+
in vec3 vWorldPos;
|
|
287
323
|
uniform vec3 uToLight;
|
|
324
|
+
uniform vec3 uEye;
|
|
288
325
|
uniform float uIntensity;
|
|
289
326
|
uniform float uAmbient;
|
|
290
327
|
uniform float uOpacity;
|
|
291
328
|
out vec4 fragColor;
|
|
292
329
|
void main() {
|
|
293
|
-
|
|
330
|
+
vec3 normal = normalize(vNormal);
|
|
331
|
+
vec3 toLight = normalize(uToLight);
|
|
332
|
+
float lambert = max(dot(normal, toLight), 0.0);
|
|
294
333
|
float brightness = clamp(uAmbient + uIntensity * lambert, 0.0, 1.0);
|
|
295
|
-
|
|
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);
|
|
296
343
|
}`;
|
|
297
344
|
WebGLRenderer = class {
|
|
298
345
|
constructor(options = {}) {
|
|
@@ -313,9 +360,12 @@ void main() {
|
|
|
313
360
|
aPos: gl.getAttribLocation(this.program, "aPos"),
|
|
314
361
|
aNormal: gl.getAttribLocation(this.program, "aNormal"),
|
|
315
362
|
aColor: gl.getAttribLocation(this.program, "aColor"),
|
|
363
|
+
aEmissive: gl.getAttribLocation(this.program, "aEmissive"),
|
|
364
|
+
aSpecular: gl.getAttribLocation(this.program, "aSpecular"),
|
|
316
365
|
uViewProj: gl.getUniformLocation(this.program, "uViewProj"),
|
|
317
366
|
uModel: gl.getUniformLocation(this.program, "uModel"),
|
|
318
367
|
uToLight: gl.getUniformLocation(this.program, "uToLight"),
|
|
368
|
+
uEye: gl.getUniformLocation(this.program, "uEye"),
|
|
319
369
|
uIntensity: gl.getUniformLocation(this.program, "uIntensity"),
|
|
320
370
|
uAmbient: gl.getUniformLocation(this.program, "uAmbient"),
|
|
321
371
|
uOpacity: gl.getUniformLocation(this.program, "uOpacity")
|
|
@@ -339,17 +389,19 @@ void main() {
|
|
|
339
389
|
const data = expandToTriangles(mesh);
|
|
340
390
|
const vao = gl.createVertexArray();
|
|
341
391
|
gl.bindVertexArray(vao);
|
|
342
|
-
const attribute = (location, array) => {
|
|
392
|
+
const attribute = (location, array, size = 3) => {
|
|
343
393
|
if (location < 0) return;
|
|
344
394
|
const buffer = gl.createBuffer();
|
|
345
395
|
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
346
396
|
gl.bufferData(gl.ARRAY_BUFFER, array, gl.STATIC_DRAW);
|
|
347
397
|
gl.enableVertexAttribArray(location);
|
|
348
|
-
gl.vertexAttribPointer(location,
|
|
398
|
+
gl.vertexAttribPointer(location, size, gl.FLOAT, false, 0, 0);
|
|
349
399
|
};
|
|
350
400
|
attribute(loc.aPos, data.positions);
|
|
351
401
|
attribute(loc.aNormal, data.normals);
|
|
352
402
|
attribute(loc.aColor, data.colors);
|
|
403
|
+
attribute(loc.aEmissive, data.emissives);
|
|
404
|
+
attribute(loc.aSpecular, data.speculars, 4);
|
|
353
405
|
gl.bindVertexArray(null);
|
|
354
406
|
const result = { vao, count: data.count };
|
|
355
407
|
this.cache.set(mesh, result);
|
|
@@ -364,6 +416,7 @@ void main() {
|
|
|
364
416
|
gl.useProgram(this.program);
|
|
365
417
|
gl.uniformMatrix4fv(loc.uViewProj, false, new Float32Array(frame.viewProjection));
|
|
366
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);
|
|
367
420
|
gl.uniform1f(loc.uIntensity, frame.light.intensity);
|
|
368
421
|
gl.uniform1f(loc.uAmbient, frame.light.ambient);
|
|
369
422
|
gl.disable(gl.BLEND);
|
|
@@ -438,6 +491,7 @@ struct Uniforms {
|
|
|
438
491
|
model: mat4x4<f32>,
|
|
439
492
|
toLight: vec4<f32>,
|
|
440
493
|
params: vec4<f32>,
|
|
494
|
+
eye: vec4<f32>,
|
|
441
495
|
};
|
|
442
496
|
@group(0) @binding(0) var<uniform> u: Uniforms;
|
|
443
497
|
|
|
@@ -445,23 +499,40 @@ struct VSOut {
|
|
|
445
499
|
@builtin(position) position: vec4<f32>,
|
|
446
500
|
@location(0) normal: vec3<f32>,
|
|
447
501
|
@location(1) color: vec3<f32>,
|
|
502
|
+
@location(2) emissive: vec3<f32>,
|
|
503
|
+
@location(3) specular: vec4<f32>,
|
|
504
|
+
@location(4) worldPos: vec3<f32>,
|
|
448
505
|
};
|
|
449
506
|
|
|
450
507
|
@vertex
|
|
451
|
-
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 {
|
|
452
509
|
var out: VSOut;
|
|
453
510
|
let m = mat3x3<f32>(u.model[0].xyz, u.model[1].xyz, u.model[2].xyz);
|
|
454
511
|
out.normal = m * normal;
|
|
455
512
|
out.color = color;
|
|
456
|
-
out.
|
|
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;
|
|
457
518
|
return out;
|
|
458
519
|
}
|
|
459
520
|
|
|
460
521
|
@fragment
|
|
461
522
|
fn fs(in: VSOut) -> @location(0) vec4<f32> {
|
|
462
|
-
let
|
|
523
|
+
let normal = normalize(in.normal);
|
|
524
|
+
let toLight = normalize(u.toLight.xyz);
|
|
525
|
+
let lambert = max(dot(normal, toLight), 0.0);
|
|
463
526
|
let brightness = clamp(u.params.y + u.params.x * lambert, 0.0, 1.0);
|
|
464
|
-
|
|
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);
|
|
465
536
|
}
|
|
466
537
|
`;
|
|
467
538
|
CLIP_Z_FIX = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0.5, 0, 0, 0, 0.5, 1];
|
|
@@ -502,13 +573,15 @@ fn fs(in: VSOut) -> @location(0) vec4<f32> {
|
|
|
502
573
|
{
|
|
503
574
|
binding: 0,
|
|
504
575
|
visibility: stage.VERTEX | stage.FRAGMENT,
|
|
505
|
-
buffer: { type: "uniform", hasDynamicOffset: true, minBindingSize:
|
|
576
|
+
buffer: { type: "uniform", hasDynamicOffset: true, minBindingSize: 176 }
|
|
506
577
|
}
|
|
507
578
|
]
|
|
508
579
|
});
|
|
509
|
-
const vertexBuffer = (location) => ({
|
|
510
|
-
arrayStride:
|
|
511
|
-
attributes: [
|
|
580
|
+
const vertexBuffer = (location, components = 3) => ({
|
|
581
|
+
arrayStride: components * 4,
|
|
582
|
+
attributes: [
|
|
583
|
+
{ shaderLocation: location, offset: 0, format: `float32x${components}` }
|
|
584
|
+
]
|
|
512
585
|
});
|
|
513
586
|
const pipelineLayout = device.createPipelineLayout({ bindGroupLayouts: [layout] });
|
|
514
587
|
const blend = {
|
|
@@ -524,7 +597,13 @@ fn fs(in: VSOut) -> @location(0) vec4<f32> {
|
|
|
524
597
|
vertex: {
|
|
525
598
|
module: module2,
|
|
526
599
|
entryPoint: "vs",
|
|
527
|
-
buffers: [
|
|
600
|
+
buffers: [
|
|
601
|
+
vertexBuffer(0),
|
|
602
|
+
vertexBuffer(1),
|
|
603
|
+
vertexBuffer(2),
|
|
604
|
+
vertexBuffer(3),
|
|
605
|
+
vertexBuffer(4, 4)
|
|
606
|
+
]
|
|
528
607
|
},
|
|
529
608
|
fragment: {
|
|
530
609
|
module: module2,
|
|
@@ -577,6 +656,8 @@ fn fs(in: VSOut) -> @location(0) vec4<f32> {
|
|
|
577
656
|
position: upload(data.positions),
|
|
578
657
|
normal: upload(data.normals),
|
|
579
658
|
color: upload(data.colors),
|
|
659
|
+
emissive: upload(data.emissives),
|
|
660
|
+
specular: upload(data.speculars),
|
|
580
661
|
count: data.count
|
|
581
662
|
};
|
|
582
663
|
this.cache.set(mesh, result);
|
|
@@ -628,7 +709,7 @@ fn fs(in: VSOut) -> @location(0) vec4<f32> {
|
|
|
628
709
|
const bindGroup = this.device.createBindGroup({
|
|
629
710
|
layout,
|
|
630
711
|
entries: [
|
|
631
|
-
{ binding: 0, resource: { buffer: this.uniformBuffer, offset: 0, size:
|
|
712
|
+
{ binding: 0, resource: { buffer: this.uniformBuffer, offset: 0, size: 176 } }
|
|
632
713
|
]
|
|
633
714
|
});
|
|
634
715
|
draws.forEach((draw, i) => {
|
|
@@ -637,6 +718,7 @@ fn fs(in: VSOut) -> @location(0) vec4<f32> {
|
|
|
637
718
|
data.set(draw.item.model, 16);
|
|
638
719
|
data.set([frame.light.toLight.x, frame.light.toLight.y, frame.light.toLight.z, 0], 32);
|
|
639
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);
|
|
640
722
|
this.device.queue.writeBuffer(this.uniformBuffer, i * UNIFORM_STRIDE, data);
|
|
641
723
|
});
|
|
642
724
|
const encoder = this.device.createCommandEncoder();
|
|
@@ -663,6 +745,8 @@ fn fs(in: VSOut) -> @location(0) vec4<f32> {
|
|
|
663
745
|
pass.setVertexBuffer(0, mesh.position);
|
|
664
746
|
pass.setVertexBuffer(1, mesh.normal);
|
|
665
747
|
pass.setVertexBuffer(2, mesh.color);
|
|
748
|
+
pass.setVertexBuffer(3, mesh.emissive);
|
|
749
|
+
pass.setVertexBuffer(4, mesh.specular);
|
|
666
750
|
pass.draw(mesh.count);
|
|
667
751
|
});
|
|
668
752
|
pass.end();
|
|
@@ -674,6 +758,8 @@ fn fs(in: VSOut) -> @location(0) vec4<f32> {
|
|
|
674
758
|
mesh.position.destroy?.();
|
|
675
759
|
mesh.normal.destroy?.();
|
|
676
760
|
mesh.color.destroy?.();
|
|
761
|
+
mesh.emissive.destroy?.();
|
|
762
|
+
mesh.specular.destroy?.();
|
|
677
763
|
}
|
|
678
764
|
this.cache.clear();
|
|
679
765
|
this.uniformBuffer?.destroy?.();
|
|
@@ -756,9 +842,30 @@ var init_canvas2d = __esm({
|
|
|
756
842
|
depth += dot(d, d);
|
|
757
843
|
}
|
|
758
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
|
+
}
|
|
759
866
|
polygons.push({
|
|
760
867
|
points,
|
|
761
|
-
color: shadeColor(normal, face.color, frame.light),
|
|
868
|
+
color: shadeColor(normal, face.color, frame.light, surface),
|
|
762
869
|
depth,
|
|
763
870
|
opacity: faceOpacity
|
|
764
871
|
});
|
|
@@ -930,6 +1037,12 @@ init_light();
|
|
|
930
1037
|
init_math();
|
|
931
1038
|
|
|
932
1039
|
// src/engines/little-3d-engine/core/mesh.ts
|
|
1040
|
+
function attachMaterial(mesh, material) {
|
|
1041
|
+
if (material) {
|
|
1042
|
+
for (const face of mesh.faces) face.material = material;
|
|
1043
|
+
}
|
|
1044
|
+
return mesh;
|
|
1045
|
+
}
|
|
933
1046
|
function transform(init) {
|
|
934
1047
|
return {
|
|
935
1048
|
position: init?.position ?? { x: 0, y: 0, z: 0 },
|
|
@@ -944,7 +1057,7 @@ init_light();
|
|
|
944
1057
|
|
|
945
1058
|
// src/engines/little-3d-engine/shapes/primitives/cube.ts
|
|
946
1059
|
var DEFAULT_COLORS = ["#3b82f6", "#8b5cf6", "#ec4899", "#f59e0b", "#10b981", "#ef4444"];
|
|
947
|
-
function cube(size = 1, colors = DEFAULT_COLORS) {
|
|
1060
|
+
function cube(size = 1, colors = DEFAULT_COLORS, material) {
|
|
948
1061
|
const h = size / 2;
|
|
949
1062
|
const vertices = [
|
|
950
1063
|
{ x: -h, y: -h, z: h },
|
|
@@ -964,86 +1077,11 @@ function cube(size = 1, colors = DEFAULT_COLORS) {
|
|
|
964
1077
|
{ indices: [1, 5, 6, 2], color: colors[4 % colors.length] },
|
|
965
1078
|
{ indices: [4, 0, 3, 7], color: colors[5 % colors.length] }
|
|
966
1079
|
];
|
|
967
|
-
return { vertices, faces };
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
// src/engines/little-3d-engine/shapes/primitives/tetrahedron.ts
|
|
971
|
-
var DEFAULT_COLORS2 = ["#3b82f6", "#8b5cf6", "#ec4899", "#f59e0b"];
|
|
972
|
-
function tetrahedron(size = 1, colors = DEFAULT_COLORS2) {
|
|
973
|
-
const s = size / 2;
|
|
974
|
-
const vertices = [
|
|
975
|
-
{ x: s, y: s, z: s },
|
|
976
|
-
{ x: s, y: -s, z: -s },
|
|
977
|
-
{ x: -s, y: s, z: -s },
|
|
978
|
-
{ x: -s, y: -s, z: s }
|
|
979
|
-
];
|
|
980
|
-
const faces = [
|
|
981
|
-
{ indices: [0, 1, 2], color: colors[0 % colors.length] },
|
|
982
|
-
{ indices: [0, 3, 1], color: colors[1 % colors.length] },
|
|
983
|
-
{ indices: [0, 2, 3], color: colors[2 % colors.length] },
|
|
984
|
-
{ indices: [1, 3, 2], color: colors[3 % colors.length] }
|
|
985
|
-
];
|
|
986
|
-
return { vertices, faces };
|
|
987
|
-
}
|
|
988
|
-
|
|
989
|
-
// src/engines/little-3d-engine/shapes/primitives/octahedron.ts
|
|
990
|
-
var DEFAULT_COLORS3 = [
|
|
991
|
-
"#3b82f6",
|
|
992
|
-
"#8b5cf6",
|
|
993
|
-
"#ec4899",
|
|
994
|
-
"#f59e0b",
|
|
995
|
-
"#10b981",
|
|
996
|
-
"#ef4444",
|
|
997
|
-
"#06b6d4",
|
|
998
|
-
"#eab308"
|
|
999
|
-
];
|
|
1000
|
-
function octahedron(size = 1, colors = DEFAULT_COLORS3) {
|
|
1001
|
-
const r = size / 2;
|
|
1002
|
-
const vertices = [
|
|
1003
|
-
{ x: r, y: 0, z: 0 },
|
|
1004
|
-
{ x: -r, y: 0, z: 0 },
|
|
1005
|
-
{ x: 0, y: r, z: 0 },
|
|
1006
|
-
{ x: 0, y: -r, z: 0 },
|
|
1007
|
-
{ x: 0, y: 0, z: r },
|
|
1008
|
-
{ x: 0, y: 0, z: -r }
|
|
1009
|
-
];
|
|
1010
|
-
const faces = [
|
|
1011
|
-
{ indices: [4, 0, 2], color: colors[0 % colors.length] },
|
|
1012
|
-
{ indices: [4, 2, 1], color: colors[1 % colors.length] },
|
|
1013
|
-
{ indices: [4, 1, 3], color: colors[2 % colors.length] },
|
|
1014
|
-
{ indices: [4, 3, 0], color: colors[3 % colors.length] },
|
|
1015
|
-
{ indices: [5, 2, 0], color: colors[4 % colors.length] },
|
|
1016
|
-
{ indices: [5, 1, 2], color: colors[5 % colors.length] },
|
|
1017
|
-
{ indices: [5, 3, 1], color: colors[6 % colors.length] },
|
|
1018
|
-
{ indices: [5, 0, 3], color: colors[7 % colors.length] }
|
|
1019
|
-
];
|
|
1020
|
-
return { vertices, faces };
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
// src/engines/little-3d-engine/shapes/primitives/pyramid.ts
|
|
1024
|
-
var DEFAULT_COLORS4 = ["#3b82f6", "#8b5cf6", "#ec4899", "#f59e0b", "#10b981"];
|
|
1025
|
-
function pyramid(size = 1, colors = DEFAULT_COLORS4) {
|
|
1026
|
-
const h = size / 2;
|
|
1027
|
-
const vertices = [
|
|
1028
|
-
{ x: -h, y: -h, z: h },
|
|
1029
|
-
{ x: h, y: -h, z: h },
|
|
1030
|
-
{ x: h, y: -h, z: -h },
|
|
1031
|
-
{ x: -h, y: -h, z: -h },
|
|
1032
|
-
{ x: 0, y: h, z: 0 }
|
|
1033
|
-
];
|
|
1034
|
-
const faces = [
|
|
1035
|
-
{ indices: [0, 3, 2, 1], color: colors[0 % colors.length] },
|
|
1036
|
-
{ indices: [4, 0, 1], color: colors[1 % colors.length] },
|
|
1037
|
-
{ indices: [4, 1, 2], color: colors[2 % colors.length] },
|
|
1038
|
-
{ indices: [4, 2, 3], color: colors[3 % colors.length] },
|
|
1039
|
-
{ indices: [4, 3, 0], color: colors[4 % colors.length] }
|
|
1040
|
-
];
|
|
1041
|
-
return { vertices, faces };
|
|
1080
|
+
return attachMaterial({ vertices, faces }, material);
|
|
1042
1081
|
}
|
|
1043
1082
|
|
|
1044
1083
|
// src/engines/little-3d-engine/shapes/primitives/spheres/icosphere.ts
|
|
1045
1084
|
init_geometry();
|
|
1046
|
-
var DEFAULT_COLORS5 = ["#3b82f6", "#8b5cf6", "#ec4899", "#f59e0b", "#10b981", "#ef4444"];
|
|
1047
1085
|
var T = (1 + Math.sqrt(5)) / 2;
|
|
1048
1086
|
var SEED_VERTICES = [
|
|
1049
1087
|
{ x: -1, y: T, z: 0 },
|
|
@@ -1059,31 +1097,6 @@ var SEED_VERTICES = [
|
|
|
1059
1097
|
{ x: -T, y: 0, z: -1 },
|
|
1060
1098
|
{ x: -T, y: 0, z: 1 }
|
|
1061
1099
|
];
|
|
1062
|
-
var SEED_FACES = [
|
|
1063
|
-
[0, 11, 5],
|
|
1064
|
-
[0, 5, 1],
|
|
1065
|
-
[0, 1, 7],
|
|
1066
|
-
[0, 7, 10],
|
|
1067
|
-
[0, 10, 11],
|
|
1068
|
-
[1, 5, 9],
|
|
1069
|
-
[5, 11, 4],
|
|
1070
|
-
[11, 10, 2],
|
|
1071
|
-
[10, 7, 6],
|
|
1072
|
-
[7, 1, 8],
|
|
1073
|
-
[3, 9, 4],
|
|
1074
|
-
[3, 4, 2],
|
|
1075
|
-
[3, 2, 6],
|
|
1076
|
-
[3, 6, 8],
|
|
1077
|
-
[3, 8, 9],
|
|
1078
|
-
[4, 9, 5],
|
|
1079
|
-
[2, 4, 11],
|
|
1080
|
-
[6, 2, 10],
|
|
1081
|
-
[8, 6, 7],
|
|
1082
|
-
[9, 8, 1]
|
|
1083
|
-
];
|
|
1084
|
-
function icosphere(size = 1, detail = 1, colors = DEFAULT_COLORS5) {
|
|
1085
|
-
return sphereFromTriangles(SEED_VERTICES, SEED_FACES, size, detail, colors);
|
|
1086
|
-
}
|
|
1087
1100
|
|
|
1088
1101
|
// src/engines/little-3d-engine/shapes/primitives/spheres/octa-sphere.ts
|
|
1089
1102
|
init_geometry();
|
|
@@ -1263,6 +1276,7 @@ var FOV = 55 * Math.PI / 180;
|
|
|
1263
1276
|
var TWO_PI = Math.PI * 2;
|
|
1264
1277
|
var INTRO_MS = 900;
|
|
1265
1278
|
var INTRO_STAGGER_MS = 60;
|
|
1279
|
+
var INTRO_DONE_MS = (COUNT - 1) * INTRO_STAGGER_MS + INTRO_MS;
|
|
1266
1280
|
var GATE_DOCK = 0.35;
|
|
1267
1281
|
var GATE_UNDOCK = 0.65;
|
|
1268
1282
|
var EXIT_HURRY = 2.5;
|
|
@@ -1271,15 +1285,11 @@ var SPIN_MS = 380;
|
|
|
1271
1285
|
var SPIN_STAGGER_MS = 80;
|
|
1272
1286
|
var HOLD_MS = 1e3;
|
|
1273
1287
|
var COLLAPSE_MS = 700;
|
|
1288
|
+
var COLLAPSE_SPREAD_MS = 500;
|
|
1274
1289
|
var POP_MS = 170;
|
|
1275
1290
|
var LABEL_FADE_MS = 600;
|
|
1276
|
-
var
|
|
1277
|
-
|
|
1278
|
-
() => tetrahedron(1, ["#f472b6", "#ec4899", "#db2777", "#f9a8d4"]),
|
|
1279
|
-
() => octahedron(1, ["#34d399", "#10b981", "#059669", "#6ee7b7", "#a7f3d0", "#047857", "#4ade80", "#065f46"]),
|
|
1280
|
-
() => pyramid(1, ["#fbbf24", "#f59e0b", "#d97706", "#fde68a", "#fcd34d"]),
|
|
1281
|
-
() => icosphere(1, 1, ["#a78bfa", "#8b5cf6", "#7c3aed", "#c4b5fd"])
|
|
1282
|
-
];
|
|
1291
|
+
var CUBE_COLORS = ["#8397c6", "#7186b8", "#6176a8", "#93a6cf", "#556a9c", "#7a8fc0"];
|
|
1292
|
+
var DEFAULT_MESHES = [() => cube(1, CUBE_COLORS)];
|
|
1283
1293
|
function clamp012(value) {
|
|
1284
1294
|
return Math.max(0, Math.min(1, value));
|
|
1285
1295
|
}
|
|
@@ -1307,6 +1317,9 @@ var GridAssemblyAnimation = class {
|
|
|
1307
1317
|
this.dockedAt = new Array(COUNT).fill(Infinity);
|
|
1308
1318
|
this.tumbleX = [];
|
|
1309
1319
|
this.tumbleY = [];
|
|
1320
|
+
this.collapseDelay = [];
|
|
1321
|
+
this.popStarted = new Array(COUNT).fill(false);
|
|
1322
|
+
this.maxCollapseDelay = 0;
|
|
1310
1323
|
this.fades = [];
|
|
1311
1324
|
this.slots = [];
|
|
1312
1325
|
this.aspect = 16 / 9;
|
|
@@ -1315,7 +1328,6 @@ var GridAssemblyAnimation = class {
|
|
|
1315
1328
|
this.allDockedAt = Infinity;
|
|
1316
1329
|
this.collapseAt = Infinity;
|
|
1317
1330
|
this.lastNow = 0;
|
|
1318
|
-
this.popFading = false;
|
|
1319
1331
|
this.finished = false;
|
|
1320
1332
|
const sources = options.meshes && options.meshes.length > 0 ? options.meshes : DEFAULT_MESHES;
|
|
1321
1333
|
this.meshes = sources.map(resolveMesh);
|
|
@@ -1332,7 +1344,9 @@ var GridAssemblyAnimation = class {
|
|
|
1332
1344
|
this.slots.push({ x: (col - 2) * spacing, y: (2 - row) * spacing, z: 0 });
|
|
1333
1345
|
this.tumbleX.push(TWO_PI * hash01(i, 2) - Math.PI);
|
|
1334
1346
|
this.tumbleY.push(TWO_PI * hash01(i, 4) - Math.PI);
|
|
1347
|
+
this.collapseDelay.push(hash01(i, 7) * COLLAPSE_SPREAD_MS);
|
|
1335
1348
|
}
|
|
1349
|
+
this.maxCollapseDelay = Math.max(...this.collapseDelay);
|
|
1336
1350
|
}
|
|
1337
1351
|
mount(target) {
|
|
1338
1352
|
if (!target.style.position) target.style.position = "relative";
|
|
@@ -1393,7 +1407,7 @@ var GridAssemblyAnimation = class {
|
|
|
1393
1407
|
COLLAPSE_MS
|
|
1394
1408
|
));
|
|
1395
1409
|
}
|
|
1396
|
-
if (this.collapseAt !== Infinity && now >= this.collapseAt + COLLAPSE_MS + POP_MS) {
|
|
1410
|
+
if (this.collapseAt !== Infinity && now >= this.collapseAt + this.maxCollapseDelay + COLLAPSE_MS + POP_MS) {
|
|
1397
1411
|
this.finished = true;
|
|
1398
1412
|
}
|
|
1399
1413
|
this.engine.render();
|
|
@@ -1410,7 +1424,8 @@ var GridAssemblyAnimation = class {
|
|
|
1410
1424
|
}
|
|
1411
1425
|
updateBlends(dt, progress, now) {
|
|
1412
1426
|
const exiting = this.exitAt !== Infinity;
|
|
1413
|
-
const
|
|
1427
|
+
const ringComplete = now - this.enterAt >= INTRO_DONE_MS;
|
|
1428
|
+
const want = !ringComplete ? 0 : exiting ? COUNT : Math.min(COUNT, Math.floor(progress * COUNT + 1e-9));
|
|
1414
1429
|
const rate = dt / this.dockMs * (exiting ? EXIT_HURRY : 1);
|
|
1415
1430
|
for (let i = 0; i < COUNT; i++) {
|
|
1416
1431
|
const target = i < want ? 1 : 0;
|
|
@@ -1473,25 +1488,30 @@ var GridAssemblyAnimation = class {
|
|
|
1473
1488
|
if (!this.captured) {
|
|
1474
1489
|
this.captured = this.handles.map((handle) => ({ ...handle.transform.position }));
|
|
1475
1490
|
}
|
|
1476
|
-
const u = clamp012((now - this.collapseAt) / COLLAPSE_MS);
|
|
1477
|
-
const pull = easeInCubic(u);
|
|
1478
1491
|
for (let i = 0; i < COUNT; i++) {
|
|
1479
1492
|
const transform2 = this.handles[i].transform;
|
|
1480
1493
|
const from = this.captured[i];
|
|
1494
|
+
const local = now - this.collapseAt - this.collapseDelay[i];
|
|
1495
|
+
if (local <= 0) {
|
|
1496
|
+
transform2.position.x = from.x;
|
|
1497
|
+
transform2.position.y = from.y;
|
|
1498
|
+
transform2.position.z = from.z;
|
|
1499
|
+
transform2.scale = this.size;
|
|
1500
|
+
continue;
|
|
1501
|
+
}
|
|
1502
|
+
const pull = easeInCubic(clamp012(local / COLLAPSE_MS));
|
|
1481
1503
|
transform2.position.x = from.x * (1 - pull);
|
|
1482
1504
|
transform2.position.y = from.y * (1 - pull);
|
|
1483
1505
|
transform2.position.z = from.z * (1 - pull);
|
|
1484
1506
|
transform2.scale = this.size * (1 - 0.99 * pull);
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
const v = clamp012((now - this.collapseAt - COLLAPSE_MS) / POP_MS);
|
|
1492
|
-
for (let i = 0; i < COUNT; i++) {
|
|
1507
|
+
if (local >= COLLAPSE_MS) {
|
|
1508
|
+
if (!this.popStarted[i]) {
|
|
1509
|
+
this.popStarted[i] = true;
|
|
1510
|
+
this.handles[i].transparency = this.fades[i];
|
|
1511
|
+
}
|
|
1512
|
+
const v = clamp012((local - COLLAPSE_MS) / POP_MS);
|
|
1493
1513
|
this.fades[i].opacity = 1 - v;
|
|
1494
|
-
|
|
1514
|
+
transform2.scale = v >= 1 ? 0 : this.size * 0.01 * (1 + 1.6 * Math.sin(Math.PI * v));
|
|
1495
1515
|
}
|
|
1496
1516
|
}
|
|
1497
1517
|
}
|