fix: [rd] slat roll을 단일 실린더로 변경 (접시 모양 제거)
- 디스크/링 제거, 샤프트 전체에 감기는 하나의 실린더로 표현 - 최소 두께 8mm 보장 (적은 감김에서도 보이도록) - 나선형 표면 라인으로 감긴 질감 표현
This commit is contained in:
@@ -1294,101 +1294,46 @@ function fs3dBuild() {
|
||||
meshes.slats.add(lineGroup);
|
||||
}
|
||||
|
||||
// === SLAT ROLL (샤프트에 감긴 슬랫 — 어느 각도에서도 잘 보이도록) ===
|
||||
// === SLAT ROLL (샤프트에 감긴 슬랫 — 샤프트 전체에 두껍게 감기는 실린더) ===
|
||||
const rolledH = H - shutterH;
|
||||
if (rolledH > 0) {
|
||||
const wrapThick = S.productType === 'steel' ? 10 : 1;
|
||||
const shaftR = b.shaftDia / 2;
|
||||
const rollThick = Math.sqrt(rolledH * wrapThick / Math.PI);
|
||||
const rollThick = Math.max(Math.sqrt(rolledH * wrapThick / Math.PI), 8);
|
||||
const rollOuterR = shaftR + rollThick;
|
||||
const rollLen = W - 40; // 샤프트보다 약간 짧게 (양쪽 20mm씩)
|
||||
const rollLen = W - 30;
|
||||
|
||||
meshes.slatRoll = new THREE.Group();
|
||||
|
||||
// 메인 롤 실린더
|
||||
const rollGeo = new THREE.CylinderGeometry(rollOuterR, rollOuterR, rollLen, 48);
|
||||
// 하나의 Mesh로 표현 (접시 모양 방지)
|
||||
const rollGeo = new THREE.CylinderGeometry(rollOuterR, rollOuterR, rollLen, 48, 1, false);
|
||||
rollGeo.rotateZ(Math.PI / 2);
|
||||
const rollColor = S.productType === 'steel' ? 0xC9B89A : 0xc084fc;
|
||||
const rollMat = new THREE.MeshStandardMaterial({
|
||||
color: rollColor,
|
||||
metalness: S.productType === 'steel' ? 0.25 : 0,
|
||||
roughness: S.productType === 'steel' ? 0.55 : 0.8,
|
||||
color: S.productType === 'steel' ? 0xC9B89A : 0xc084fc,
|
||||
metalness: S.productType === 'steel' ? 0.2 : 0,
|
||||
roughness: S.productType === 'steel' ? 0.5 : 0.8,
|
||||
opacity: 0.93,
|
||||
transparent: true,
|
||||
opacity: 0.92,
|
||||
});
|
||||
const rollMesh = new THREE.Mesh(rollGeo, rollMat);
|
||||
meshes.slatRoll.add(rollMesh);
|
||||
|
||||
// 양쪽 끝 디스크 (측면에서 롤 두께 확인 가능)
|
||||
const discMat = new THREE.MeshStandardMaterial({
|
||||
color: S.productType === 'steel' ? 0xA89070 : 0x9b6dff,
|
||||
metalness: 0.3, roughness: 0.5, side: THREE.DoubleSide,
|
||||
});
|
||||
const discShape = new THREE.Shape();
|
||||
discShape.absarc(0, 0, rollOuterR, 0, Math.PI * 2, false);
|
||||
const innerHole = new THREE.Path();
|
||||
innerHole.absarc(0, 0, shaftR + 1, 0, Math.PI * 2, true);
|
||||
discShape.holes.push(innerHole);
|
||||
const discGeo = new THREE.ShapeGeometry(discShape, 32);
|
||||
// 좌측 디스크
|
||||
const discL = new THREE.Mesh(discGeo, discMat);
|
||||
discL.rotation.y = Math.PI / 2;
|
||||
discL.position.set(-rollLen / 2, 0, 0);
|
||||
meshes.slatRoll.add(discL);
|
||||
// 우측 디스크
|
||||
const discR = new THREE.Mesh(discGeo.clone(), discMat);
|
||||
discR.rotation.y = Math.PI / 2;
|
||||
discR.position.set(rollLen / 2, 0, 0);
|
||||
meshes.slatRoll.add(discR);
|
||||
|
||||
// 감긴 슬랫 골 표현 (나선형 라인 — 여러 위치에서 반복)
|
||||
if (rollThick > 3) {
|
||||
const ringGroup = new THREE.Group();
|
||||
const ringCount = Math.min(Math.ceil(rollThick / (wrapThick * 0.12)), 15);
|
||||
const ringPositions = [-rollLen * 0.4, -rollLen * 0.15, rollLen * 0.15, rollLen * 0.4];
|
||||
const ringLineMat = new THREE.LineBasicMaterial({
|
||||
color: S.productType === 'steel' ? 0x8B7355 : 0x7c4dff,
|
||||
opacity: 0.6, transparent: true
|
||||
});
|
||||
for (const xPos of ringPositions) {
|
||||
for (let i = 1; i <= ringCount; i++) {
|
||||
const r = shaftR + (rollThick * i / (ringCount + 1));
|
||||
const pts = [];
|
||||
for (let a = 0; a <= 64; a++) {
|
||||
const angle = (a / 64) * Math.PI * 2;
|
||||
pts.push(new THREE.Vector3(0, Math.sin(angle) * r, Math.cos(angle) * r));
|
||||
}
|
||||
const ringGeo = new THREE.BufferGeometry().setFromPoints(pts);
|
||||
const ring = new THREE.Line(ringGeo, ringLineMat);
|
||||
ring.position.x = xPos;
|
||||
ringGroup.add(ring);
|
||||
}
|
||||
}
|
||||
meshes.slatRoll.add(ringGroup);
|
||||
}
|
||||
|
||||
// 나선형 표면 라인 (롤 표면에 감긴 느낌)
|
||||
if (rollThick > 5) {
|
||||
const spiralMat = new THREE.LineBasicMaterial({ color: S.productType === 'steel' ? 0x7A6040 : 0x6a3ddd, opacity: 0.4, transparent: true });
|
||||
const spiralCount = 3;
|
||||
for (let s = 0; s < spiralCount; s++) {
|
||||
const spiralPts = [];
|
||||
const turns = Math.max(2, Math.min(rollLen / 80, 20));
|
||||
const startAngle = (s / spiralCount) * Math.PI * 2;
|
||||
for (let i = 0; i <= turns * 32; i++) {
|
||||
const t = i / (turns * 32);
|
||||
const angle = startAngle + t * turns * Math.PI * 2;
|
||||
const x = -rollLen / 2 + t * rollLen;
|
||||
const surfR = rollOuterR + 0.5;
|
||||
spiralPts.push(new THREE.Vector3(x, Math.sin(angle) * surfR, Math.cos(angle) * surfR));
|
||||
}
|
||||
const spiralGeo = new THREE.BufferGeometry().setFromPoints(spiralPts);
|
||||
meshes.slatRoll.add(new THREE.Line(spiralGeo, spiralMat));
|
||||
}
|
||||
}
|
||||
|
||||
meshes.slatRoll = new THREE.Mesh(rollGeo, rollMat);
|
||||
meshes.slatRoll.position.set(0, shaftY, 0);
|
||||
scene.add(meshes.slatRoll);
|
||||
|
||||
// 표면 나선 라인 (감긴 슬랫 질감)
|
||||
if (rollThick > 5) {
|
||||
const spiralMat = new THREE.LineBasicMaterial({ color: S.productType === 'steel' ? 0x8B7355 : 0x7c4dff, opacity: 0.5, transparent: true });
|
||||
for (let s = 0; s < 4; s++) {
|
||||
const pts = [];
|
||||
const turns = Math.max(3, Math.min(rollLen / 60, 25));
|
||||
const startA = (s / 4) * Math.PI * 2;
|
||||
for (let i = 0; i <= turns * 32; i++) {
|
||||
const t = i / (turns * 32);
|
||||
const angle = startA + t * turns * Math.PI * 2;
|
||||
const x = -rollLen / 2 + t * rollLen;
|
||||
pts.push(new THREE.Vector3(x, Math.sin(angle) * (rollOuterR + 0.5), Math.cos(angle) * (rollOuterR + 0.5)));
|
||||
}
|
||||
const geo = new THREE.BufferGeometry().setFromPoints(pts);
|
||||
meshes.slatRoll.add(new THREE.Line(geo, spiralMat));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// === BOTTOM BAR ===
|
||||
|
||||
Reference in New Issue
Block a user