fix(B05): 절취 측벽에서 빗금을 뺀다
벽은 방향성을 표현하는 면이 아니다(2026-08-27 사용자). 성토면 빗금은 그대로 두고 벽만 단색으로 그린다. - Mesh: 벽 재질에서 `map`(빗금 텍스처)과 uv 속성을 뺀다. - Cut/Build: 벽이 UV를 안 쓰므로 `CorridorCutWall.uvs`와 수집 코드를 지운다. - Corridor: 저장본에서 `uvsBase64` 제거, ENVELOPE_VERSION 4 → 5. 검증: 재빌드 후 벽 서피스 우 360·좌 356 삼각형 그대로, `uvs` 필드 없음. ISO 화면에서 벽면이 단색, 주변 성토면 빗금은 유지. npm run typecheck 통과, prettier 적용. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -36,8 +36,9 @@ export interface CorridorStructureHook {
|
||||
*
|
||||
* 3 = 리본에 셀 마스크·축 고정 커브 조각 삼각형 추가 — 2026-08-26.
|
||||
* 4 = 절취 측벽 서피스(`cutWalls`) 추가 — 2026-08-27.
|
||||
* 5 = 측벽에서 빗금(UV)을 뺀다 — 벽은 방향성을 표현하지 않는다(2026-08-27 사용자).
|
||||
*/
|
||||
const ENVELOPE_VERSION = 4;
|
||||
const ENVELOPE_VERSION = 5;
|
||||
|
||||
interface CorridorEnvelope {
|
||||
version: number;
|
||||
@@ -68,7 +69,6 @@ interface CorridorEnvelope {
|
||||
cutWalls?: Array<{
|
||||
side: "left" | "right";
|
||||
positionsBase64: string;
|
||||
uvsBase64: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
@@ -200,7 +200,6 @@ function serialize(build: CorridorBuildResult, hash: string): CorridorEnvelope {
|
||||
cutWalls: (build.cutWalls ?? []).map((wall) => ({
|
||||
side: wall.side,
|
||||
positionsBase64: base64FromFloats(wall.positions),
|
||||
uvsBase64: base64FromBytes(new Uint8Array(wall.uvs.buffer.slice(0))),
|
||||
})),
|
||||
};
|
||||
}
|
||||
@@ -228,7 +227,6 @@ function deserialize(envelope: CorridorEnvelope): CorridorBuildResult {
|
||||
cutWalls: (envelope.cutWalls ?? []).map((wall) => ({
|
||||
side: wall.side,
|
||||
positions: floatsFromBase64(wall.positionsBase64),
|
||||
uvs: new Float32Array(bytesFromBase64(wall.uvsBase64).buffer),
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -92,12 +92,11 @@ export interface CorridorRibbon {
|
||||
|
||||
/**
|
||||
* 잘린 자리 **측벽 서피스**(2026-08-27 사용자) — 리본 메시에 섞지 않고 구조물처럼
|
||||
* 따로 그린다. 삼각형 수프(정점당 3, 삼각형당 9)와 짝 UV(정점당 2).
|
||||
* 따로 그린다. 삼각형 수프(정점당 3, 삼각형당 9). 빗금이 없어 UV는 담지 않는다.
|
||||
*/
|
||||
export interface CorridorCutWall {
|
||||
side: "left" | "right";
|
||||
positions: Float64Array;
|
||||
uvs: Float32Array;
|
||||
}
|
||||
|
||||
/** 시·종점 마구리(캡) — 설계선과 지반선 사이를 세로로 봉인하는 스트립(모델 좌표). */
|
||||
|
||||
@@ -293,37 +293,26 @@ function pushSkirt(
|
||||
loop: ReadonlyArray<[number, number]>,
|
||||
ribbon: CorridorRibbon,
|
||||
tris: number[],
|
||||
uvs: number[],
|
||||
): void {
|
||||
const ground = ribbon.groundZ;
|
||||
if (!ground) return;
|
||||
const cols = ribbon.colCount;
|
||||
const startChainage = ribbon.chainages[0];
|
||||
/** 격자점 → [x, y, 서피스 z, 지반 z, u]. */
|
||||
const at = (row: number, col: number): [number, number, number, number, number] => {
|
||||
/** 격자점 → [x, y, 서피스 z, 지반 z − 여유]. */
|
||||
const at = (row: number, col: number): [number, number, number, number] => {
|
||||
const i = (row * cols + col) * 3;
|
||||
return [
|
||||
ribbon.positions[i],
|
||||
ribbon.positions[i + 1],
|
||||
ribbon.positions[i + 2],
|
||||
ground[row * cols + col] - SKIRT_MARGIN_M,
|
||||
(ribbon.chainages[row] - startChainage) / 2,
|
||||
];
|
||||
};
|
||||
const push = (x: number, y: number, z: number, u: number): void => {
|
||||
tris.push(x, y, z);
|
||||
uvs.push(u, z / 2); // 빗금은 2m마다 한 타일 — 세로도 실척으로 이어진다.
|
||||
};
|
||||
for (let i = 0; i < loop.length - 1; i += 1) {
|
||||
const [x1, y1, top1, bottom1, u1] = at(loop[i][0], loop[i][1]);
|
||||
const [x2, y2, top2, bottom2, u2] = at(loop[i + 1][0], loop[i + 1][1]);
|
||||
const [x1, y1, top1, bottom1] = at(loop[i][0], loop[i][1]);
|
||||
const [x2, y2, top2, bottom2] = at(loop[i + 1][0], loop[i + 1][1]);
|
||||
if (top1 - bottom1 < 1e-6 && top2 - bottom2 < 1e-6) continue; // 높이 0이면 벽이 없다.
|
||||
push(x1, y1, top1, u1);
|
||||
push(x2, y2, top2, u2);
|
||||
push(x2, y2, bottom2, u2);
|
||||
push(x1, y1, top1, u1);
|
||||
push(x2, y2, bottom2, u2);
|
||||
push(x1, y1, bottom1, u1);
|
||||
tris.push(x1, y1, top1, x2, y2, top2, x2, y2, bottom2);
|
||||
tris.push(x1, y1, top1, x2, y2, bottom2, x1, y1, bottom1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,7 +357,6 @@ export function maskFillByPlanCurves(
|
||||
const uvs: number[] = [];
|
||||
// 측벽은 리본 메시가 아니라 **별도 서피스**로 나간다(2026-08-27 사용자).
|
||||
const wallTris: number[] = [];
|
||||
const wallUvs: number[] = [];
|
||||
const startChainage = ribbon.chainages[0];
|
||||
const point = (row: number, col: number): [number, number, number] => {
|
||||
const i = (row * cols + col) * 3;
|
||||
@@ -427,7 +415,7 @@ export function maskFillByPlanCurves(
|
||||
traceMaskLoops(rows, cols, mask).forEach((raw) => {
|
||||
const loop = dropCollinear(raw);
|
||||
if (loop.length < 4 || !planes.length) return;
|
||||
pushSkirt(loop, ribbon, wallTris, wallUvs);
|
||||
pushSkirt(loop, ribbon, wallTris);
|
||||
const meanRow = loop.reduce((sum, [row]) => sum + row, 0) / loop.length;
|
||||
const chainage = ribbon.chainages[Math.min(rows - 1, Math.round(meanRow))];
|
||||
const plane = planes.reduce((best, item) =>
|
||||
@@ -448,13 +436,7 @@ export function maskFillByPlanCurves(
|
||||
],
|
||||
});
|
||||
});
|
||||
if (wallTris.length) {
|
||||
walls.push({
|
||||
side,
|
||||
positions: new Float64Array(wallTris),
|
||||
uvs: new Float32Array(wallUvs),
|
||||
});
|
||||
}
|
||||
if (wallTris.length) walls.push({ side, positions: new Float64Array(wallTris) });
|
||||
return {
|
||||
...ribbon,
|
||||
cellMask: mask,
|
||||
|
||||
@@ -429,13 +429,12 @@ export function createCorridorGroup(build: CorridorBuildResult, bounds: ModelBou
|
||||
}
|
||||
const geometry = new THREE.BufferGeometry();
|
||||
geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
|
||||
geometry.setAttribute("uv", new THREE.BufferAttribute(wall.uvs.slice(0), 2));
|
||||
geometry.computeVertexNormals();
|
||||
const mesh = new THREE.Mesh(
|
||||
geometry,
|
||||
new THREE.MeshLambertMaterial({
|
||||
// 빗금 없음 — 벽은 방향성을 표현할 면이 아니다(2026-08-27 사용자).
|
||||
color: KIND_COLORS.fill,
|
||||
map: hatchTexture(HATCH_DIRECTION.fill ?? -1),
|
||||
side: THREE.DoubleSide,
|
||||
polygonOffset: true,
|
||||
polygonOffsetFactor: POLYGON_OFFSET_FACTOR,
|
||||
|
||||
Reference in New Issue
Block a user