- _Split: 사본 색을 지형 원래 색(ORIGINAL_COLORS)으로 · far 색 버퍼 clone - 뷰어 applyGrayscaleTo: 원래 색 기억을 메시 userData → 색 버퍼 열쇠(WeakMap) · ③ 사면 keepColor 그대로 - 새 시험 test_135_2_grayscale(+ 도우미) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QND7Co43B24zK2P937rdxt
97 lines
3.9 KiB
JavaScript
97 lines
3.9 KiB
JavaScript
/* PLAN 135-2 — 흑백 지형 단추: 분할본 색 = 지형 원래 색 · far 색 버퍼 따로 · 원래 색 기억 = 색 버퍼 열쇠.
|
|
* TS 를 트랜스파일해 돌리고 결과(JSON)를 낸다(helper_115_8 과 같은 방식). */
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const ROOT = path.resolve(__dirname, "..", "..");
|
|
const NM = path.join(ROOT, "config", "node_modules");
|
|
const ts = require(path.join(NM, "typescript"));
|
|
const THREE = require(path.join(NM, "three"));
|
|
|
|
function loadTs(file) {
|
|
const js = ts.transpileModule(fs.readFileSync(file, "utf8"), {
|
|
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
|
|
}).outputText;
|
|
const box = { exports: {} };
|
|
new Function("exports", "module", "require", js)(box.exports, box, (r) =>
|
|
r === "three" ? THREE : require(r),
|
|
);
|
|
return box.exports;
|
|
}
|
|
const B05 = (name) => path.join(ROOT, "B05_Profile", `${name}.ts`);
|
|
const { TerrainBandSplit, ORIGINAL_COLORS } = loadTs(B05("B05_Profile_UI_Corridor_Split"));
|
|
|
|
// 뷰어 applyGrayscaleTo 와 같은 규칙(색 버퍼 열쇠로 원본 한 번만 기억).
|
|
function gray(object, on) {
|
|
object.traverse((child) => {
|
|
const color = child.geometry?.getAttribute("color");
|
|
if (!color) return;
|
|
const target = color.array;
|
|
let original = ORIGINAL_COLORS.get(color);
|
|
if (!original) {
|
|
original = target.slice();
|
|
ORIGINAL_COLORS.set(color, original);
|
|
}
|
|
if (on) {
|
|
for (let i = 0; i + 2 < original.length; i += 3) {
|
|
const g = 0.299 * original[i] + 0.587 * original[i + 1] + 0.114 * original[i + 2];
|
|
target[i] = target[i + 1] = target[i + 2] = g;
|
|
}
|
|
} else target.set(original);
|
|
});
|
|
}
|
|
|
|
// 지형 — 40 × 40 m 격자 · 색 = 빨강 → 초록(회색과 다름).
|
|
const N = 21;
|
|
const pos = [];
|
|
const col = [];
|
|
const idx = [];
|
|
for (let j = 0; j < N; j += 1)
|
|
for (let i = 0; i < N; i += 1) {
|
|
pos.push(i * 2 - 20, 0, j * 2 - 20);
|
|
col.push(1, i / (N - 1), 0);
|
|
}
|
|
for (let j = 0; j + 1 < N; j += 1)
|
|
for (let i = 0; i + 1 < N; i += 1) {
|
|
const a = j * N + i;
|
|
idx.push(a, a + N, a + 1, a + 1, a + N, a + N + 1);
|
|
}
|
|
const geometry = new THREE.BufferGeometry();
|
|
geometry.setAttribute("position", new THREE.Float32BufferAttribute(pos, 3));
|
|
geometry.setAttribute("color", new THREE.Float32BufferAttribute(col, 3));
|
|
geometry.setIndex(idx);
|
|
const terrain = new THREE.Group();
|
|
terrain.add(new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({ vertexColors: true })));
|
|
const original = Float32Array.from(col);
|
|
|
|
gray(terrain, true); // 기본 흑백 — 지형 적재 직후 회색 먼저.
|
|
const split = new TerrainBandSplit(terrain, { minX: -5, maxX: 5, minZ: -5, maxZ: 5 });
|
|
const near = split.near[0].geometry.getAttribute("color");
|
|
const far = split.far[0].geometry.getAttribute("color");
|
|
const same = (a, b) => a.length === b.length && a.every((v, k) => Math.abs(v - b[k]) < 1e-6);
|
|
const r = {};
|
|
r.nearOriginal = same(near.array, original);
|
|
r.farOriginal = same(far.array, original);
|
|
r.farOwnBuffer = near !== far && near.array !== far.array;
|
|
|
|
// far 를 감싼 메시 두 번(③ 다시 짬) — 흑백 → 끔이 원래 색으로.
|
|
const wrap = () => {
|
|
const root = new THREE.Group();
|
|
root.add(new THREE.Mesh(split.far[0].geometry, split.far[0].material));
|
|
return root;
|
|
};
|
|
const first = wrap();
|
|
gray(first, true);
|
|
r.nearUntouchedByFarGray = same(near.array, original);
|
|
const second = wrap();
|
|
gray(second, true); // 새 메시 — 회색을 원본으로 잡으면 안 됨.
|
|
gray(second, false);
|
|
r.farBackToOriginal = same(far.array, original);
|
|
gray(terrain, false);
|
|
r.terrainBackToOriginal = same(geometry.getAttribute("color").array, original);
|
|
|
|
// 뷰어가 같은 열쇠를 쓰는가.
|
|
const viewer = fs.readFileSync(B05("B05_Profile_UI_Viewer"), "utf8");
|
|
r.viewerUsesKey =
|
|
viewer.includes("ORIGINAL_COLORS.get(color)") && !viewer.includes("userData.originalColors");
|
|
process.stdout.write(JSON.stringify(r));
|