fivetech commited on
Commit
545e491
·
1 Parent(s): 5cb54f6

Escote: parche clonando la malla del jersey (mask inversa sin muestreo); fondo tropical por defecto (?bg=off); giro del avatar con el raton

Browse files
Files changed (1) hide show
  1. chunk-p1yb2ca4.js +193 -161
chunk-p1yb2ca4.js CHANGED
@@ -34429,93 +34429,76 @@ function mq(c) {
34429
  M = Number(h.get("neckTop") ?? 1.5015),
34430
  F = Number(h.get("neckFront") ?? 0.014),
34431
  T = y === "u" ? 0 : 1,
34432
- b = i.geometry.attributes,
34433
- R = b.position,
34434
- _ = b.normal,
34435
- x = b.skinIndex,
34436
- P = b.skinWeight;
34437
- if (!R || !_) return;
34438
- let K = 10,
34439
- se = (u, v, w2) => {
34440
- let S = [],
34441
- px = 0,
34442
- py = 0,
34443
- pz = 0,
34444
- nx = 0,
34445
- ny = 0,
34446
- nz = 0,
34447
- ch = 0;
34448
- for (let j2 = 0; j2 < R.count; j2++) {
34449
- let m = {
34450
- d:
34451
- (R.array[j2 * 3] - u) ** 2 +
34452
- (R.array[j2 * 3 + 1] - v) ** 2 +
34453
- (R.array[j2 * 3 + 2] - w2) ** 2,
34454
- j: j2,
34455
- };
34456
- if (S.length < K) S.push(m);
34457
- else if (m.d < S[K - 1].d) ((S[K - 1] = m), S.sort((B, D) => B.d - D.d));
34458
- }
34459
- for (let m of S) {
34460
- let j2 = m.j,
34461
- q = 1 / (m.d + 1e-6);
34462
- (ch += q),
34463
- (px += R.array[j2 * 3] * q),
34464
- (py += R.array[j2 * 3 + 1] * q),
34465
- (pz += R.array[j2 * 3 + 2] * q),
34466
- (nx += _.array[j2 * 3] * q),
34467
- (ny += _.array[j2 * 3 + 1] * q),
34468
- (nz += _.array[j2 * 3 + 2] * q);
34469
- }
34470
- let acc = new Map();
34471
- if (x && P)
34472
- for (let m of S) {
34473
- let j2 = m.j,
34474
- q = 1 / (m.d + 1e-6);
34475
- for (let k4 = 0; k4 < 4; k4++) {
34476
- let bone = x.array[j2 * 4 + k4],
34477
- bw = P.array[j2 * 4 + k4] * q;
34478
- acc.set(bone, (acc.get(bone) ?? 0) + bw);
34479
- }
34480
- }
34481
- let bs = [...acc.entries()].sort((B, D) => D[1] - B[1]).slice(0, 4),
34482
- wsum = bs.reduce((B, D) => B + D[1], 0) || 1;
34483
- return {
34484
- p: [px / ch, py / ch, pz / ch],
34485
- n: [nx / ch, ny / ch, nz / ch],
34486
- idx: bs.map((B) => B[0]),
34487
- w: bs.map((B) => B[1] / wsum),
34488
- };
34489
- },
34490
- Q = (u) => (u <= 0.04045 ? u / 12.92 : ((u + 0.055) / 1.055) ** 2.4),
34491
- tone = null;
34492
- try {
34493
- let img = c.Wolf3D_Head?.material?.map?.image;
34494
- if (img?.width) {
34495
- let cv = document.createElement("canvas"),
34496
- cx = cv.getContext("2d", { willReadFrequently: !0 });
34497
- cv.width = img.width;
34498
- cv.height = img.height;
34499
- cx.drawImage(img, 0, 0);
34500
- let [l0, m0, l1, m1] = $t.lids_uv ?? [0.36, 0.28, 0.64, 0.39],
34501
- px = Math.round(((l0 + l1) / 2) * img.width),
34502
- py = Math.round(((m0 + m1) / 2) * img.height),
34503
- dtd = cx.getImageData(Math.max(0, px - 12), Math.max(0, py - 12), 24, 24).data,
34504
- rr = 0,
34505
- gg = 0,
34506
- bb = 0,
34507
- n5 = dtd.length / 4;
34508
- for (let k5 = 0; k5 < dtd.length; k5 += 4) (rr += dtd[k5]), (gg += dtd[k5 + 1]), (bb += dtd[k5 + 2]);
34509
- tone = new re(Q(rr / n5 / 255), Q(gg / n5 / 255), Q(bb / n5 / 255));
34510
- }
34511
- } catch {}
34512
- let sk = tone ?? new re(0.6, 0.34, 0.26);
34513
  {
34514
- let ubc = i.material.onBeforeCompile,
34515
- U1 = { value: new Ye(M, p, g, T) },
34516
- U2 = { value: new ve(F, 0) };
34517
- let V3 = (sh, cxA) => {
34518
- ubc?.(sh, cxA);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34519
  Object.assign(sh.uniforms, { uNeck: U1, uNeckF: U2 });
34520
  sh.vertexShader = sh.vertexShader
34521
  .replace("#include <common>", `#include <common>
@@ -34530,84 +34513,109 @@ varying vec3 vBindN; uniform vec4 uNeck; uniform vec2 uNeckF;`)
34530
  `#include <clipping_planes_fragment>
34531
  {
34532
  float rel = abs(vBindN.x) / uNeck.z;
34533
- if (vBindN.z > uNeckF.x && vBindN.y > uNeck.x - uNeck.y - 0.012 && vBindN.y < uNeck.x + 0.001) {
34534
- if (uNeck.w > 0.5) { if ((uNeck.x - vBindN.y) > uNeck.y * rel) discard; }
34535
- else { if ((uNeck.x - vBindN.y) > uNeck.y * sqrt(max(0.0, 1.0 - rel * rel))) discard; }
34536
- }
 
34537
  }`,
34538
  );
34539
  };
34540
- ((i.material.onBeforeCompile = V3),
34541
- (i.material.customProgramCacheKey = () => `ava-neck-cut-${i.material.uuid}`),
34542
- (i.material.needsUpdate = !0));
34543
- }
34544
- let GX = 40,
34545
- GY = 16,
34546
- padX = 0.02,
34547
- padY = 0.012,
34548
- yUp = M + 0.0025,
34549
- yBot = M - p - padY,
34550
- gW = g + padX,
34551
- pos = [],
34552
- nor = [],
34553
- js = [],
34554
- ws = [],
34555
- vm = [],
34556
- insideM =
34557
- y === "u"
34558
- ? (xx, yy) => yy < M + 0.0025 - 1e-4 && yy > M - p * Math.max(0, 1 - (xx / g) ** 2) ** 0.5 - padY && Math.abs(xx) < gW
34559
- : (xx, yy) => yy < M + 0.0025 - 1e-4 && yy > yBot && (M - yy) > (p * Math.abs(xx)) / g && Math.abs(xx) < gW;
34560
- for (let gy = 0; gy < GY; gy++) {
34561
- let vv = yUp - ((yUp - yBot) * gy) / (GY - 1);
34562
- for (let gx = 0; gx < GX; gx++) {
34563
- let uu = (-gW + (2 * gW * gx) / (GX - 1)),
34564
- sm = se(uu, vv, 0),
34565
- nx2 = sm.n,
34566
- nn = (nx2[0] ** 2 + nx2[1] ** 2 + nx2[2] ** 2) ** 0.5 || 1;
34567
- (pos.push(sm.p[0] - (nx2[0] / nn) * 0.0035, sm.p[1] - (nx2[1] / nn) * 0.0035, sm.p[2] - (nx2[2] / nn) * 0.0035),
34568
- nor.push(nx2[0] / nn, nx2[1] / nn, nx2[2] / nn));
34569
- for (let k6 = 0; k6 < 4; k6++) (js.push(sm.idx[k6] ?? 0), ws.push(sm.w[k6] ?? 0));
34570
- vm.push(insideM(uu, vv) ? 1 : 0);
34571
- }
34572
- }
34573
- let geo = new Et();
34574
- (geo.setAttribute("position", new at(pos, 3)),
34575
- geo.setAttribute("normal", new at(nor, 3)),
34576
- x && P && (geo.setAttribute("skinIndex", new er(js, 4)), geo.setAttribute("skinWeight", new at(ws, 4))));
34577
- let idx = [];
34578
- for (let gy = 0; gy < GY - 1; gy++)
34579
- for (let gx = 0; gx < GX - 1; gx++) {
34580
- let a1 = gy * GX + gx,
34581
- q1 = a1 + 1,
34582
- v1 = a1 + GX,
34583
- y1 = v1 + 1;
34584
- if (vm[a1] && vm[q1] && vm[v1] && vm[y1]) idx.push(a1, v1, q1, q1, v1, y1);
34585
- }
34586
- geo.setIndex(idx);
34587
- let mat = new tn({
34588
- side: Yt,
34589
- color: sk,
34590
- roughness: 0.48,
34591
- metalness: 0,
34592
- specularIntensity: 0.4,
34593
- sheen: 0.4,
34594
- sheenColor: new re(1, 0.96, 0.92),
34595
- sheenRoughness: 0.7,
34596
- clearcoat: 0.05,
34597
- clearcoatRoughness: 0.35,
34598
- }),
34599
- mk = new Bi(geo, mat);
34600
- (t1(mat),
34601
- mk.bind(i.skeleton, i.bindMatrix),
34602
- mk.position.copy(i.position),
34603
- mk.quaternion.copy(i.quaternion),
34604
- mk.scale.copy(i.scale),
34605
- mk.updateMatrix(),
34606
- (mk.castShadow = !1),
34607
- (mk.receiveShadow = !0),
34608
- (mk.frustumCulled = !1),
34609
- mk.matrixAutoUpdate && mk.updateMatrixWorld(!0),
34610
- i.parent.add(mk));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34611
  }
34612
  function vp(e, t = {}) {
34613
  let { studio: n = !0, materials: i = !0, shadows: s = !0 } = t,
@@ -34625,6 +34633,7 @@ function vp(e, t = {}) {
34625
  (r.toneMappingExposure = Number(h.get("exp") ?? 1)),
34626
  (o.environment = Y_(r)),
34627
  (o.environmentIntensity = Number(h.get("env") ?? 1.08)));
 
34628
  let u = new Qn("#fff8f7", Number(h.get("key") ?? 0.38));
34629
  if ((u.position.set(-0.75, 2.55, 1.6), u.target.position.set(0, 1.55, 0.03), o.add(u, u.target), s)) {
34630
  ((r.shadowMap.enabled = !0), (r.shadowMap.type = Fo), (u.castShadow = !0), u.shadow.mapSize.set(2048, 2048));
@@ -34815,6 +34824,29 @@ varying float vShellX; uniform vec2 lidBlink;`,
34815
  });
34816
  }
34817
  mq(c);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34818
  a.traverse((h) => {
34819
  if (!h.isMesh) return;
34820
  for (let u of [h.material.map, h.material.normalMap, h.material.roughnessMap])
 
34429
  M = Number(h.get("neckTop") ?? 1.5015),
34430
  F = Number(h.get("neckFront") ?? 0.014),
34431
  T = y === "u" ? 0 : 1,
34432
+ U1 = { value: new Ye(M, p, g, T) },
34433
+ U2 = { value: new ve(F, 0) };
34434
+ // 1) escote: tira los fragmentos del jersey dentro del hueco (espacio de bind pose)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34435
  {
34436
+ let ubc = i.material.onBeforeCompile;
34437
+ (i.material.onBeforeCompile = (sh, cxA) => {
34438
+ (ubc?.(sh, cxA),
34439
+ Object.assign(sh.uniforms, { uNeck: U1, uNeckF: U2 }),
34440
+ (sh.vertexShader = sh.vertexShader
34441
+ .replace("#include <common>", `#include <common>
34442
+ varying vec3 vBindN;`)
34443
+ .replace("#include <begin_vertex>", `#include <begin_vertex>
34444
+ vBindN = position;`)),
34445
+ (sh.fragmentShader = sh.fragmentShader
34446
+ .replace("#include <common>", `#include <common>
34447
+ varying vec3 vBindN; uniform vec4 uNeck; uniform vec2 uNeckF;`)
34448
+ .replace(
34449
+ "#include <clipping_planes_fragment>",
34450
+ `#include <clipping_planes_fragment>
34451
+ {
34452
+ float rel = abs(vBindN.x) / uNeck.z;
34453
+ if (vBindN.z > uNeckF.x && vBindN.y > uNeck.x - uNeck.y - 0.012 && vBindN.y < uNeck.x + 0.001) {
34454
+ if (uNeck.w > 0.5) { if ((uNeck.x - vBindN.y) > uNeck.y * rel) discard; }
34455
+ else { if ((uNeck.x - vBindN.y) > uNeck.y * sqrt(max(0.0, 1.0 - rel * rel))) discard; }
34456
+ }
34457
+ }`,
34458
+ )));
34459
+ }),
34460
+ (i.material.customProgramCacheKey = () => `ava-neck-cut-${i.material.uuid}`),
34461
+ (i.material.needsUpdate = !0);
34462
+ }
34463
+ // 2) piel: clon de la geometría del jersey hundido 3.5 mm, dibujado solo dentro del hueco,
34464
+ // así el skinning es el exacto del jersey y no hay muestreo runtime que falle.
34465
+ try {
34466
+ let g2 = i.geometry.clone(),
34467
+ A = g2.attributes.position.array,
34468
+ B = g2.attributes.normal.array;
34469
+ for (let v2 = 0; v2 < A.length; v2 += 3)
34470
+ ((A[v2] -= B[v2] * 0.0035), (A[v2 + 1] -= B[v2 + 1] * 0.0035), (A[v2 + 2] -= B[v2 + 2] * 0.0035));
34471
+ let sk = new re(0.6, 0.34, 0.26);
34472
+ try {
34473
+ let img = c.Wolf3D_Head?.material?.map?.image;
34474
+ if (img?.width) {
34475
+ let cv = document.createElement("canvas"),
34476
+ cx = cv.getContext("2d", { willReadFrequently: !0 });
34477
+ ((cv.width = img.width), (cv.height = img.height), cx.drawImage(img, 0, 0));
34478
+ let [l0, m0, l1, m1] = $t.lids_uv ?? [0.36, 0.28, 0.64, 0.39],
34479
+ px = Math.round(((l0 + l1) / 2) * img.width),
34480
+ py = Math.round(((m0 + m1) / 2) * img.height),
34481
+ dtd = cx.getImageData(Math.max(0, px - 12), Math.max(0, py - 12), 24, 24).data,
34482
+ rr = 0,
34483
+ gg = 0,
34484
+ bb = 0,
34485
+ n5 = dtd.length / 4;
34486
+ for (let k5 = 0; k5 < dtd.length; k5 += 4) ((rr += dtd[k5]), (gg += dtd[k5 + 1]), (bb += dtd[k5 + 2]));
34487
+ let Q = (u) => (u <= 0.04045 ? u / 12.92 : ((u + 0.055) / 1.055) ** 2.4);
34488
+ sk = new re(Q(rr / n5 / 255), Q(gg / n5 / 255), Q(bb / n5 / 255));
34489
+ }
34490
+ } catch {}
34491
+ let mat2 = new tn({
34492
+ side: Yt,
34493
+ color: sk,
34494
+ roughness: 0.48,
34495
+ metalness: 0,
34496
+ specularIntensity: 0.4,
34497
+ sheen: 0.35,
34498
+ sheenColor: new re(1, 0.95, 0.9),
34499
+ sheenRoughness: 0.7,
34500
+ });
34501
+ mat2.onBeforeCompile = (sh, cxA) => {
34502
  Object.assign(sh.uniforms, { uNeck: U1, uNeckF: U2 });
34503
  sh.vertexShader = sh.vertexShader
34504
  .replace("#include <common>", `#include <common>
 
34513
  `#include <clipping_planes_fragment>
34514
  {
34515
  float rel = abs(vBindN.x) / uNeck.z;
34516
+ if (vBindN.z <= uNeckF.x) discard;
34517
+ if (vBindN.y > uNeck.x - uNeck.y - 0.02) discard;
34518
+ if (vBindN.y > uNeck.x + 0.001) discard;
34519
+ if (uNeck.w > 0.5) { if ((uNeck.x - vBindN.y) <= uNeck.y * rel * 0.88) discard; }
34520
+ else { if ((uNeck.x - vBindN.y) <= uNeck.y * sqrt(max(0.0, 1.0 - rel * rel)) * 0.88) discard; }
34521
  }`,
34522
  );
34523
  };
34524
+ t1(mat2);
34525
+ mat2.customProgramCacheKey = () => "ava-neck-patch";
34526
+ let pc = new Bi(g2, mat2);
34527
+ pc.bind(i.skeleton, i.bindMatrix);
34528
+ ((pc.position.copy(i.position),
34529
+ pc.quaternion.copy(i.quaternion),
34530
+ pc.scale.copy(i.scale)),
34531
+ (pc.castShadow = !1),
34532
+ (pc.receiveShadow = !0),
34533
+ (pc.frustumCulled = !1),
34534
+ i.parent.add(pc));
34535
+ } catch (er2) {
34536
+ console.warn("neck patch failed:", er2);
34537
+ }
34538
+ }
34539
+ function Tq() {
34540
+ let cv = document.createElement("canvas");
34541
+ ((cv.width = 2048), (cv.height = 1024));
34542
+ let cx = cv.getContext("2d"),
34543
+ n2 = (a2, b2, c2) => Math.round(a2 + Math.random() * (b2 - a2));
34544
+ // cielo: azul profundo arriba, bruma cálida en el horizonte
34545
+ let air = cx.createLinearGradient(0, 0, 0, 512);
34546
+ (air.addColorStop(0, "#1b66b8"),
34547
+ air.addColorStop(0.5, "#57a8e0"),
34548
+ air.addColorStop(0.82, "#c8e2f0"),
34549
+ air.addColorStop(1, "#f6e2c4"),
34550
+ (cx.fillStyle = air),
34551
+ cx.fillRect(0, 0, 2048, 512));
34552
+ // sol bajo y cálido
34553
+ let sun = cx.createRadialGradient(640, 452, 6, 640, 452, 210);
34554
+ (sun.addColorStop(0, "#fff6d8"),
34555
+ sun.addColorStop(0.22, "rgba(255,229,168,0.55)"),
34556
+ sun.addColorStop(1, "rgba(255,229,168,0)"),
34557
+ (cx.fillStyle = sun),
34558
+ cx.fillRect(400, 200, 520, 312));
34559
+ // nubes suaves
34560
+ for (let q = 0; q < 30; q++) {
34561
+ let x = n2(0, 2048),
34562
+ w = n2(120, 420),
34563
+ hh = n2(14, 42),
34564
+ yy = n2(60, 470);
34565
+ cx.fillStyle = `rgba(255,255,255,${0.06 + Math.random() * 0.14})`;
34566
+ cx.beginPath(), cx.ellipse(x, yy, w / 2, hh, 0, 0, 6.283), cx.fill();
34567
+ }
34568
+ // mar
34569
+ let sea = cx.createLinearGradient(0, 510, 0, 1024);
34570
+ (sea.addColorStop(0, "#8fc4d8"),
34571
+ sea.addColorStop(0.12, "#2b8bab"),
34572
+ sea.addColorStop(0.55, "#0f6b96"),
34573
+ sea.addColorStop(1, "#0a3c60"),
34574
+ (cx.fillStyle = sea),
34575
+ cx.fillRect(0, 510, 2048, 514));
34576
+ // destellos de ola
34577
+ for (let q = 0; q < 320; q++) {
34578
+ cx.fillStyle = `rgba(255,255,255,${0.02 + Math.random() * 0.06})`;
34579
+ cx.fillRect(n2(0, 2040), 516 + n2(0, 470), n2(18, 110), n2(1, 2));
34580
+ }
34581
+ // costa: siluetas lejanas de islas (palmera simple + montes) varias longitudes
34582
+ for (let [u0, w2] of [[0.06, 300], [0.14, 170], [0.38, 240], [0.62, 190], [0.85, 330]]) {
34583
+ let bx = u0 * 2048,
34584
+ hh = w2 * 0.16;
34585
+ (cx.shadowColor = "rgba(40,90,80,0.9)"),
34586
+ (cx.shadowBlur = 10),
34587
+ (cx.fillStyle = "#3f6f5f");
34588
+ cx.beginPath(),
34589
+ cx.ellipse(bx, 508, w2 / 2, hh, 0, Math.PI, 2 * Math.PI),
34590
+ cx.fill();
34591
+ cx.beginPath(),
34592
+ cx.moveTo(bx - w2 * 0.42, 508),
34593
+ cx.lineTo(bx - w2 * 0.3, 508 - hh * 0.9),
34594
+ cx.lineTo(bx - w2 * 0.18, 508),
34595
+ cx.fill();
34596
+ cx.beginPath(),
34597
+ cx.moveTo(bx + w2 * 0.2, 508),
34598
+ cx.lineTo(bx + w2 * 0.32, 508 - hh * 0.6),
34599
+ cx.lineTo(bx + w2 * 0.46, 508),
34600
+ cx.fill();
34601
+ // palmera
34602
+ let px = bx + w2 * 0.04;
34603
+ (cx.strokeStyle = "#35594d"),
34604
+ (cx.lineWidth = 4),
34605
+ cx.beginPath(),
34606
+ cx.moveTo(px, 508),
34607
+ cx.lineTo(px - 4, 508 - hh * 1.5),
34608
+ cx.stroke();
34609
+ for (let f2 of [-1, 1]) {
34610
+ cx.beginPath(),
34611
+ cx.moveTo(px - 4, 508 - hh * 1.5),
34612
+ cx.quadraticCurveTo(px - 4 + f2 * 26, 508 - hh * 1.7, px - 4 + f2 * 40, 508 - hh * 1.4),
34613
+ cx.stroke();
34614
+ }
34615
+ }
34616
+ cx.shadowBlur = 0;
34617
+ let tex = new Rt(cv);
34618
+ return ((tex.mapping = Zs), (tex.colorSpace = lt), tex);
34619
  }
34620
  function vp(e, t = {}) {
34621
  let { studio: n = !0, materials: i = !0, shadows: s = !0 } = t,
 
34633
  (r.toneMappingExposure = Number(h.get("exp") ?? 1)),
34634
  (o.environment = Y_(r)),
34635
  (o.environmentIntensity = Number(h.get("env") ?? 1.08)));
34636
+ if (h.get("bg") !== "off") o.background = Tq();
34637
  let u = new Qn("#fff8f7", Number(h.get("key") ?? 0.38));
34638
  if ((u.position.set(-0.75, 2.55, 1.6), u.target.position.set(0, 1.55, 0.03), o.add(u, u.target), s)) {
34639
  ((r.shadowMap.enabled = !0), (r.shadowMap.type = Fo), (u.castShadow = !0), u.shadow.mapSize.set(2048, 2048));
 
34824
  });
34825
  }
34826
  mq(c);
34827
+ // ratón: arrastrar gira el avatar; doble clic lo devuelve al frente
34828
+ {
34829
+ let mg = r.domElement,
34830
+ dn = !1,
34831
+ lx = 0,
34832
+ ly = 0;
34833
+ ((mg.style.touchAction = "none"),
34834
+ mg.addEventListener("pointerdown", (ev) => {
34835
+ if (ev.button === 0) ((dn = !0), (lx = ev.clientX), (ly = ev.clientY));
34836
+ }),
34837
+ window.addEventListener("pointerup", () => (dn = !1)),
34838
+ mg.addEventListener("pointerleave", () => (dn = !1)),
34839
+ mg.addEventListener("dblclick", () => ((a.rotation.y = 0), (a.rotation.x = 0))),
34840
+ mg.addEventListener("pointermove", (ev) => {
34841
+ if (!dn) return;
34842
+ let dx = ev.clientX - lx,
34843
+ dy = ev.clientY - ly;
34844
+ ((lx = ev.clientX),
34845
+ (ly = ev.clientY),
34846
+ (a.rotation.y += dx / 220),
34847
+ (a.rotation.x = Math.max(-0.3, Math.min(0.3, a.rotation.x + dy / 220))));
34848
+ }));
34849
+ }
34850
  a.traverse((h) => {
34851
  if (!h.isMesh) return;
34852
  for (let u of [h.material.map, h.material.normalMap, h.material.roughnessMap])