reze-engine 0.27.3 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -9
- package/dist/engine.d.ts +2 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +19 -7
- package/dist/ik-solver.d.ts +1 -1
- package/dist/ik-solver.d.ts.map +1 -1
- package/dist/ik-solver.js +4 -29
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/math.d.ts +26 -0
- package/dist/math.d.ts.map +1 -1
- package/dist/math.js +328 -0
- package/dist/shaders/passes/shadow.d.ts +1 -1
- package/dist/shaders/passes/shadow.d.ts.map +1 -1
- package/dist/shaders/passes/shadow.js +26 -3
- package/package.json +1 -1
- package/src/engine.ts +20 -7
- package/src/ik-solver.ts +5 -34
- package/src/index.ts +7 -2
- package/src/math.ts +345 -0
- package/src/shaders/passes/shadow.ts +26 -3
package/src/math.ts
CHANGED
|
@@ -3,6 +3,10 @@ export function easeInOut(t: number): number {
|
|
|
3
3
|
return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
+
// Euler rotation order, read as intrinsic axes left to right:
|
|
7
|
+
// "YXZ" = rotate about Y, then local X, then local Z (equals extrinsic ZXY — the MMD/PMX convention).
|
|
8
|
+
export type EulerOrder = "XYZ" | "XZY" | "YXZ" | "YZX" | "ZXY" | "ZYX"
|
|
9
|
+
|
|
6
10
|
export class Vec3 {
|
|
7
11
|
x: number
|
|
8
12
|
y: number
|
|
@@ -113,6 +117,18 @@ export class Vec3 {
|
|
|
113
117
|
return out
|
|
114
118
|
}
|
|
115
119
|
|
|
120
|
+
// out = (x, y, -z): right-handed Y-up ↔ left-handed Y-up (involutive). Safe when out === v.
|
|
121
|
+
static mirrorZInto(v: Vec3, out: Vec3): Vec3 {
|
|
122
|
+
out.x = v.x
|
|
123
|
+
out.y = v.y
|
|
124
|
+
out.z = -v.z
|
|
125
|
+
return out
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
static mirrorZ(v: Vec3): Vec3 {
|
|
129
|
+
return new Vec3(v.x, v.y, -v.z)
|
|
130
|
+
}
|
|
131
|
+
|
|
116
132
|
// In-place normalize returning length squared info via Vec3. Alias for normalize() but explicit.
|
|
117
133
|
normalizeInPlace(): Vec3 {
|
|
118
134
|
return this.normalize()
|
|
@@ -333,6 +349,328 @@ export class Quat {
|
|
|
333
349
|
|
|
334
350
|
return new Quat(x, y, z, w).normalize()
|
|
335
351
|
}
|
|
352
|
+
|
|
353
|
+
// 4D dot product. Negative means a and b are on opposite hemispheres (same rotation
|
|
354
|
+
// when |dot| ≈ 1 either way — quaternion double cover).
|
|
355
|
+
static dot(a: Quat, b: Quat): number {
|
|
356
|
+
return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Rotation angle between a and b in radians, insensitive to double cover.
|
|
360
|
+
static angleTo(a: Quat, b: Quat): number {
|
|
361
|
+
const d = Math.abs(a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w)
|
|
362
|
+
return 2 * Math.acos(Math.min(1, d))
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// out = conjugate of q (inverse for unit quaternions), without mutating q. Safe when out === q.
|
|
366
|
+
static conjugateInto(q: Quat, out: Quat): Quat {
|
|
367
|
+
out.x = -q.x
|
|
368
|
+
out.y = -q.y
|
|
369
|
+
out.z = -q.z
|
|
370
|
+
out.w = q.w
|
|
371
|
+
return out
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// out = normalized lerp from a to b, taking the shorter path (negates b when dot < 0).
|
|
375
|
+
// Cheaper than slerp; non-constant angular velocity. Safe when out === a or out === b.
|
|
376
|
+
static nlerpInto(a: Quat, b: Quat, t: number, out: Quat): Quat {
|
|
377
|
+
const d = a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w
|
|
378
|
+
const s = d < 0 ? -1 : 1
|
|
379
|
+
const x = a.x + (b.x * s - a.x) * t
|
|
380
|
+
const y = a.y + (b.y * s - a.y) * t
|
|
381
|
+
const z = a.z + (b.z * s - a.z) * t
|
|
382
|
+
const w = a.w + (b.w * s - a.w) * t
|
|
383
|
+
const invLen = 1 / Math.hypot(x, y, z, w)
|
|
384
|
+
out.x = x * invLen
|
|
385
|
+
out.y = y * invLen
|
|
386
|
+
out.z = z * invLen
|
|
387
|
+
out.w = w * invLen
|
|
388
|
+
return out
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
static nlerp(a: Quat, b: Quat, t: number): Quat {
|
|
392
|
+
return Quat.nlerpInto(a, b, t, new Quat(0, 0, 0, 1))
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// out = v rotated by unit quaternion q (no matrix, no allocation). Safe when out === v.
|
|
396
|
+
static rotateVecInto(q: Quat, v: Vec3, out: Vec3): Vec3 {
|
|
397
|
+
// v' = v + 2*qw*(qv × v) + 2*(qv × (qv × v))
|
|
398
|
+
const qx = q.x, qy = q.y, qz = q.z, qw = q.w
|
|
399
|
+
const vx = v.x, vy = v.y, vz = v.z
|
|
400
|
+
// t = 2 * (qv × v)
|
|
401
|
+
const tx = 2 * (qy * vz - qz * vy)
|
|
402
|
+
const ty = 2 * (qz * vx - qx * vz)
|
|
403
|
+
const tz = 2 * (qx * vy - qy * vx)
|
|
404
|
+
out.x = vx + qw * tx + qy * tz - qz * ty
|
|
405
|
+
out.y = vy + qw * ty + qz * tx - qx * tz
|
|
406
|
+
out.z = vz + qw * tz + qx * ty - qy * tx
|
|
407
|
+
return out
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
static rotateVec(q: Quat, v: Vec3): Vec3 {
|
|
411
|
+
return Quat.rotateVecInto(q, v, new Vec3(0, 0, 0))
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// out = v rotated by the inverse (conjugate) of unit quaternion q. Safe when out === v.
|
|
415
|
+
static rotateVecInvInto(q: Quat, v: Vec3, out: Vec3): Vec3 {
|
|
416
|
+
const qx = -q.x, qy = -q.y, qz = -q.z, qw = q.w
|
|
417
|
+
const vx = v.x, vy = v.y, vz = v.z
|
|
418
|
+
const tx = 2 * (qy * vz - qz * vy)
|
|
419
|
+
const ty = 2 * (qz * vx - qx * vz)
|
|
420
|
+
const tz = 2 * (qx * vy - qy * vx)
|
|
421
|
+
out.x = vx + qw * tx + qy * tz - qz * ty
|
|
422
|
+
out.y = vy + qw * ty + qz * tx - qx * tz
|
|
423
|
+
out.z = vz + qw * tz + qx * ty - qy * tx
|
|
424
|
+
return out
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
static rotateVecInv(q: Quat, v: Vec3): Vec3 {
|
|
428
|
+
return Quat.rotateVecInvInto(q, v, new Vec3(0, 0, 0))
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// out = shortest-arc rotation taking unit vector `from` to unit vector `to`.
|
|
432
|
+
// Matches Babylon's FromUnitVectorsToRef exactly, including the near-antiparallel
|
|
433
|
+
// branch (w = 1 + dot < 0.001 → 180° about a perpendicular picked the same way).
|
|
434
|
+
static fromUnitVectorsInto(from: Vec3, to: Vec3, out: Quat): Quat {
|
|
435
|
+
const r = from.x * to.x + from.y * to.y + from.z * to.z + 1
|
|
436
|
+
if (r < 0.001) {
|
|
437
|
+
if (Math.abs(from.x) > Math.abs(from.z)) {
|
|
438
|
+
out.setXYZW(-from.y, from.x, 0, 0)
|
|
439
|
+
} else {
|
|
440
|
+
out.setXYZW(0, -from.z, from.y, 0)
|
|
441
|
+
}
|
|
442
|
+
} else {
|
|
443
|
+
// q = (from × to, 1 + from·to)
|
|
444
|
+
out.setXYZW(
|
|
445
|
+
from.y * to.z - from.z * to.y,
|
|
446
|
+
from.z * to.x - from.x * to.z,
|
|
447
|
+
from.x * to.y - from.y * to.x,
|
|
448
|
+
r
|
|
449
|
+
)
|
|
450
|
+
}
|
|
451
|
+
const invLen = 1 / Math.sqrt(out.x * out.x + out.y * out.y + out.z * out.z + out.w * out.w)
|
|
452
|
+
out.setXYZW(out.x * invLen, out.y * invLen, out.z * invLen, out.w * invLen)
|
|
453
|
+
return out
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
static fromUnitVectors(from: Vec3, to: Vec3): Quat {
|
|
457
|
+
return Quat.fromUnitVectorsInto(from, to, new Quat(0, 0, 0, 1))
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// out = rotation taking the standard basis onto the orthonormal axes x, y, z
|
|
461
|
+
// (the columns of the column-major rotation matrix): rotateVec(out, (1,0,0)) = x, etc.
|
|
462
|
+
static fromBasisInto(x: Vec3, y: Vec3, z: Vec3, out: Quat): Quat {
|
|
463
|
+
// Shepperd's method on the 3x3 with columns x, y, z.
|
|
464
|
+
const m00 = x.x, m01 = x.y, m02 = x.z
|
|
465
|
+
const m10 = y.x, m11 = y.y, m12 = y.z
|
|
466
|
+
const m20 = z.x, m21 = z.y, m22 = z.z
|
|
467
|
+
const trace = m00 + m11 + m22
|
|
468
|
+
if (trace > 0) {
|
|
469
|
+
const s = 0.5 / Math.sqrt(trace + 1)
|
|
470
|
+
out.setXYZW((m12 - m21) * s, (m20 - m02) * s, (m01 - m10) * s, 0.25 / s)
|
|
471
|
+
} else if (m00 > m11 && m00 > m22) {
|
|
472
|
+
const s = 2 * Math.sqrt(1 + m00 - m11 - m22)
|
|
473
|
+
out.setXYZW(0.25 * s, (m10 + m01) / s, (m20 + m02) / s, (m12 - m21) / s)
|
|
474
|
+
} else if (m11 > m22) {
|
|
475
|
+
const s = 2 * Math.sqrt(1 + m11 - m00 - m22)
|
|
476
|
+
out.setXYZW((m10 + m01) / s, 0.25 * s, (m21 + m12) / s, (m20 - m02) / s)
|
|
477
|
+
} else {
|
|
478
|
+
const s = 2 * Math.sqrt(1 + m22 - m00 - m11)
|
|
479
|
+
out.setXYZW((m20 + m02) / s, (m21 + m12) / s, 0.25 * s, (m01 - m10) / s)
|
|
480
|
+
}
|
|
481
|
+
return out
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
static fromBasis(x: Vec3, y: Vec3, z: Vec3): Quat {
|
|
485
|
+
return Quat.fromBasisInto(x, y, z, new Quat(0, 0, 0, 1))
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// out = twist component of q around unit axis `a`, so that q = swing · twist
|
|
489
|
+
// (swing = Quat.multiply(q, conjugate(twist))). Singular when q is ~180° about an
|
|
490
|
+
// axis perpendicular to `a` — returns identity there.
|
|
491
|
+
static twistAroundAxisInto(q: Quat, a: Vec3, out: Quat): Quat {
|
|
492
|
+
const d = q.x * a.x + q.y * a.y + q.z * a.z
|
|
493
|
+
const px = a.x * d
|
|
494
|
+
const py = a.y * d
|
|
495
|
+
const pz = a.z * d
|
|
496
|
+
const len = Math.sqrt(px * px + py * py + pz * pz + q.w * q.w)
|
|
497
|
+
if (len < 1e-8) {
|
|
498
|
+
out.setIdentity()
|
|
499
|
+
return out
|
|
500
|
+
}
|
|
501
|
+
out.setXYZW(px / len, py / len, pz / len, q.w / len)
|
|
502
|
+
return out
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
static twistAroundAxis(q: Quat, a: Vec3): Quat {
|
|
506
|
+
return Quat.twistAroundAxisInto(q, a, new Quat(0, 0, 0, 1))
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// out = orientation looking along `forward` (mapped to local +Z, the engine's LH forward)
|
|
510
|
+
// with local +Y toward `up`. Falls back to a reference up when forward ∥ up.
|
|
511
|
+
static lookRotationInto(forward: Vec3, up: Vec3, out: Quat): Quat {
|
|
512
|
+
let zx = forward.x, zy = forward.y, zz = forward.z
|
|
513
|
+
const zl = Math.sqrt(zx * zx + zy * zy + zz * zz)
|
|
514
|
+
if (zl === 0) return out.setIdentity()
|
|
515
|
+
const zi = 1 / zl
|
|
516
|
+
zx *= zi; zy *= zi; zz *= zi
|
|
517
|
+
// x = up × z
|
|
518
|
+
let xx = up.y * zz - up.z * zy
|
|
519
|
+
let xy = up.z * zx - up.x * zz
|
|
520
|
+
let xz = up.x * zy - up.y * zx
|
|
521
|
+
let xl = Math.sqrt(xx * xx + xy * xy + xz * xz)
|
|
522
|
+
if (xl < 1e-8) {
|
|
523
|
+
// forward ∥ up: substitute a reference up not parallel to forward
|
|
524
|
+
const uy = Math.abs(zy) < 0.99 ? 1 : 0
|
|
525
|
+
const ux = 1 - uy
|
|
526
|
+
xx = uy * zz
|
|
527
|
+
xy = -ux * zz
|
|
528
|
+
xz = ux * zy - uy * zx
|
|
529
|
+
xl = Math.sqrt(xx * xx + xy * xy + xz * xz)
|
|
530
|
+
}
|
|
531
|
+
const xi = 1 / xl
|
|
532
|
+
xx *= xi; xy *= xi; xz *= xi
|
|
533
|
+
// y = z × x (unit: z ⊥ x)
|
|
534
|
+
const yx = zy * xz - zz * xy
|
|
535
|
+
const yy = zz * xx - zx * xz
|
|
536
|
+
const yz = zx * xy - zy * xx
|
|
537
|
+
_lookX.setXYZ(xx, xy, xz)
|
|
538
|
+
_lookY.setXYZ(yx, yy, yz)
|
|
539
|
+
_lookZ.setXYZ(zx, zy, zz)
|
|
540
|
+
return Quat.fromBasisInto(_lookX, _lookY, _lookZ, out)
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
static lookRotation(forward: Vec3, up: Vec3): Quat {
|
|
544
|
+
return Quat.lookRotationInto(forward, up, new Quat(0, 0, 0, 1))
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// out = q with the Z axis mirrored: right-handed Y-up ↔ left-handed Y-up
|
|
548
|
+
// (conjugation by the Z-mirror; involutive). Pairs with Vec3.mirrorZInto. Safe when out === q.
|
|
549
|
+
static mirrorZInto(q: Quat, out: Quat): Quat {
|
|
550
|
+
out.x = -q.x
|
|
551
|
+
out.y = -q.y
|
|
552
|
+
out.z = q.z
|
|
553
|
+
out.w = q.w
|
|
554
|
+
return out
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
static mirrorZ(q: Quat): Quat {
|
|
558
|
+
return new Quat(-q.x, -q.y, q.z, q.w)
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// out = quaternion from euler angles (radians) applied in the given intrinsic order.
|
|
562
|
+
// fromEulerOrderInto(x, y, z, "YXZ", out) matches Quat.fromEuler (the MMD/PMX convention).
|
|
563
|
+
static fromEulerOrderInto(x: number, y: number, z: number, order: EulerOrder, out: Quat): Quat {
|
|
564
|
+
out.setIdentity()
|
|
565
|
+
for (let i = 0; i < 3; i++) {
|
|
566
|
+
const axis = order.charCodeAt(i) - 88 // "X" → 0, "Y" → 1, "Z" → 2
|
|
567
|
+
const angle = axis === 0 ? x : axis === 1 ? y : z
|
|
568
|
+
const half = angle * 0.5
|
|
569
|
+
const s = Math.sin(half)
|
|
570
|
+
const c = Math.cos(half)
|
|
571
|
+
const ax = out.x, ay = out.y, az = out.z, aw = out.w
|
|
572
|
+
if (axis === 0) {
|
|
573
|
+
out.x = aw * s + ax * c
|
|
574
|
+
out.y = ay * c + az * s
|
|
575
|
+
out.z = az * c - ay * s
|
|
576
|
+
out.w = aw * c - ax * s
|
|
577
|
+
} else if (axis === 1) {
|
|
578
|
+
out.x = ax * c - az * s
|
|
579
|
+
out.y = aw * s + ay * c
|
|
580
|
+
out.z = az * c + ax * s
|
|
581
|
+
out.w = aw * c - ay * s
|
|
582
|
+
} else {
|
|
583
|
+
out.x = ax * c + ay * s
|
|
584
|
+
out.y = ay * c - ax * s
|
|
585
|
+
out.z = aw * s + az * c
|
|
586
|
+
out.w = aw * c - az * s
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
return out
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
static fromEulerOrder(x: number, y: number, z: number, order: EulerOrder): Quat {
|
|
593
|
+
return Quat.fromEulerOrderInto(x, y, z, order, new Quat(0, 0, 0, 1))
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// Extract euler angles (radians) in the given intrinsic order into out (out.x/y/z = rotation
|
|
597
|
+
// about X/Y/Z). At gimbal lock (middle angle ±90°) the split between first and third angle is
|
|
598
|
+
// ambiguous; the third is set to 0.
|
|
599
|
+
static toEulerOrderInto(q: Quat, order: EulerOrder, out: Vec3): Vec3 {
|
|
600
|
+
Mat4.fromQuatInto(q.x, q.y, q.z, q.w, _eulerMat, 0)
|
|
601
|
+
const m = _eulerMat
|
|
602
|
+
const m11 = m[0], m12 = m[4], m13 = m[8]
|
|
603
|
+
const m21 = m[1], m22 = m[5], m23 = m[9]
|
|
604
|
+
const m31 = m[2], m32 = m[6], m33 = m[10]
|
|
605
|
+
const clamp = (v: number) => (v < -1 ? -1 : v > 1 ? 1 : v)
|
|
606
|
+
switch (order) {
|
|
607
|
+
case "XYZ":
|
|
608
|
+
out.y = Math.asin(clamp(m13))
|
|
609
|
+
if (Math.abs(m13) < 0.9999999) {
|
|
610
|
+
out.x = Math.atan2(-m23, m33)
|
|
611
|
+
out.z = Math.atan2(-m12, m11)
|
|
612
|
+
} else {
|
|
613
|
+
out.x = Math.atan2(m32, m22)
|
|
614
|
+
out.z = 0
|
|
615
|
+
}
|
|
616
|
+
break
|
|
617
|
+
case "YXZ":
|
|
618
|
+
out.x = Math.asin(-clamp(m23))
|
|
619
|
+
if (Math.abs(m23) < 0.9999999) {
|
|
620
|
+
out.y = Math.atan2(m13, m33)
|
|
621
|
+
out.z = Math.atan2(m21, m22)
|
|
622
|
+
} else {
|
|
623
|
+
out.y = Math.atan2(-m31, m11)
|
|
624
|
+
out.z = 0
|
|
625
|
+
}
|
|
626
|
+
break
|
|
627
|
+
case "ZXY":
|
|
628
|
+
out.x = Math.asin(clamp(m32))
|
|
629
|
+
if (Math.abs(m32) < 0.9999999) {
|
|
630
|
+
out.y = Math.atan2(-m31, m33)
|
|
631
|
+
out.z = Math.atan2(-m12, m22)
|
|
632
|
+
} else {
|
|
633
|
+
out.y = 0
|
|
634
|
+
out.z = Math.atan2(m21, m11)
|
|
635
|
+
}
|
|
636
|
+
break
|
|
637
|
+
case "ZYX":
|
|
638
|
+
out.y = Math.asin(-clamp(m31))
|
|
639
|
+
if (Math.abs(m31) < 0.9999999) {
|
|
640
|
+
out.x = Math.atan2(m32, m33)
|
|
641
|
+
out.z = Math.atan2(m21, m11)
|
|
642
|
+
} else {
|
|
643
|
+
out.x = 0
|
|
644
|
+
out.z = Math.atan2(-m12, m22)
|
|
645
|
+
}
|
|
646
|
+
break
|
|
647
|
+
case "YZX":
|
|
648
|
+
out.z = Math.asin(clamp(m21))
|
|
649
|
+
if (Math.abs(m21) < 0.9999999) {
|
|
650
|
+
out.x = Math.atan2(-m23, m22)
|
|
651
|
+
out.y = Math.atan2(-m31, m11)
|
|
652
|
+
} else {
|
|
653
|
+
out.x = 0
|
|
654
|
+
out.y = Math.atan2(m13, m33)
|
|
655
|
+
}
|
|
656
|
+
break
|
|
657
|
+
case "XZY":
|
|
658
|
+
out.z = Math.asin(-clamp(m12))
|
|
659
|
+
if (Math.abs(m12) < 0.9999999) {
|
|
660
|
+
out.x = Math.atan2(m32, m22)
|
|
661
|
+
out.y = Math.atan2(m13, m11)
|
|
662
|
+
} else {
|
|
663
|
+
out.x = Math.atan2(-m23, m33)
|
|
664
|
+
out.y = 0
|
|
665
|
+
}
|
|
666
|
+
break
|
|
667
|
+
}
|
|
668
|
+
return out
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
static toEulerOrder(q: Quat, order: EulerOrder): Vec3 {
|
|
672
|
+
return Quat.toEulerOrderInto(q, order, new Vec3(0, 0, 0))
|
|
673
|
+
}
|
|
336
674
|
}
|
|
337
675
|
|
|
338
676
|
export class Mat4 {
|
|
@@ -837,6 +1175,13 @@ export class Mat4 {
|
|
|
837
1175
|
}
|
|
838
1176
|
}
|
|
839
1177
|
|
|
1178
|
+
// Module-private scratch for Quat.toEulerOrderInto / lookRotationInto (never handed out,
|
|
1179
|
+
// so no cross-call stomping with the public pools below).
|
|
1180
|
+
const _eulerMat = new Float32Array(16)
|
|
1181
|
+
const _lookX = new Vec3(0, 0, 0)
|
|
1182
|
+
const _lookY = new Vec3(0, 0, 0)
|
|
1183
|
+
const _lookZ = new Vec3(0, 0, 0)
|
|
1184
|
+
|
|
840
1185
|
// Preallocated scratch instances for hot paths. Each subsystem should use its own
|
|
841
1186
|
// slot to avoid cross-call stomping. Bump the count if more call sites need scratch.
|
|
842
1187
|
export const scratchMat4Values: Float32Array[] = [
|
|
@@ -1,17 +1,40 @@
|
|
|
1
|
-
// Shadow map depth
|
|
1
|
+
// Shadow map depth pass. Skinned VS + alpha-test FS (depth-only attachment, no
|
|
2
|
+
// color targets): texels where diffuse-texture alpha × material alpha fall below
|
|
3
|
+
// the cutoff are discarded, so lace casts lace-shaped shadows and a true veil
|
|
4
|
+
// casts nothing — per texel, with no per-material sheerness classification.
|
|
5
|
+
// Group 1 is the main pass's per-material bind group reused as-is; only
|
|
6
|
+
// bindings 0/1 are declared here (a layout may carry bindings a shader ignores).
|
|
2
7
|
|
|
3
8
|
export const SHADOW_DEPTH_SHADER_WGSL = /* wgsl */ `
|
|
4
9
|
struct LightVP { viewProj: mat4x4f, };
|
|
5
10
|
@group(0) @binding(0) var<uniform> lp: LightVP;
|
|
6
11
|
@group(0) @binding(1) var<storage, read> skinMats: array<mat4x4f>;
|
|
12
|
+
@group(0) @binding(2) var texSampler: sampler;
|
|
13
|
+
@group(1) @binding(0) var diffuseTexture: texture_2d<f32>;
|
|
14
|
+
struct MaterialDiffuse { diffuse: vec4f, };
|
|
15
|
+
@group(1) @binding(1) var<uniform> material: MaterialDiffuse;
|
|
16
|
+
|
|
17
|
+
struct VSOut {
|
|
18
|
+
@builtin(position) position: vec4f,
|
|
19
|
+
@location(0) uv: vec2f,
|
|
20
|
+
};
|
|
21
|
+
|
|
7
22
|
@vertex fn vs(@location(0) position: vec3f, @location(1) normal: vec3f, @location(2) uv: vec2f,
|
|
8
|
-
@location(3) joints0: vec4<u32>, @location(4) weights0: vec4<f32>) ->
|
|
23
|
+
@location(3) joints0: vec4<u32>, @location(4) weights0: vec4<f32>) -> VSOut {
|
|
9
24
|
let pos4 = vec4f(position, 1.0);
|
|
10
25
|
let ws = weights0.x + weights0.y + weights0.z + weights0.w;
|
|
11
26
|
let inv = select(1.0, 1.0 / ws, ws > 0.0001);
|
|
12
27
|
let nw = select(vec4f(1.0,0.0,0.0,0.0), weights0 * inv, ws > 0.0001);
|
|
13
28
|
var sp = vec4f(0.0);
|
|
14
29
|
for (var i = 0u; i < 4u; i++) { sp += (skinMats[joints0[i]] * pos4) * nw[i]; }
|
|
15
|
-
|
|
30
|
+
var out: VSOut;
|
|
31
|
+
out.position = lp.viewProj * vec4f(sp.xyz, 1.0);
|
|
32
|
+
out.uv = uv;
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@fragment fn fs(in: VSOut) {
|
|
37
|
+
let alpha = textureSample(diffuseTexture, texSampler, in.uv).a * material.diffuse.a;
|
|
38
|
+
if (alpha < 0.5) { discard; }
|
|
16
39
|
}
|
|
17
40
|
`
|