좌표계, matrix, bounds readout 설계
debug overlay가 그림이라면 readout은 숫자입니다. pointer가 어디에 있고, 선택된 node의 matrix와 bounds가 무엇인지 바로 읽을 수 있어야 합니다.
readout payload를 만드는 코드
디버그 패널은 렌더러 내부 객체를 직접 읽기보다, editor snapshot에서 필요한 숫자만 뽑아 표시하는 편이 안전합니다.
function createReadout(snapshot, pointerScreen) {
const world = screenToWorld(pointerScreen, snapshot.camera);
const selected = snapshot.selection[0]
? snapshot.scene.nodesById.get(snapshot.selection[0])
: null;
const local = selected
? transformPoint(invert3(selected.worldMatrix), world)
: null;
return {
screen: roundPoint(pointerScreen),
world: roundPoint(world),
local: local ? roundPoint(local) : null,
matrix: selected ? selected.worldMatrix.map(round3) : null,
bounds: selected ? roundRect(nodeWorldBounds(selected)) : null
};
}
function round3(value) {
return Math.round(value * 1000) / 1000;
}
같이 보여줄 값
screen: pointer relative to canvas
world: after inverse camera
local: after inverse node matrix
matrix: local/world
bounds: AABB/OBB
숫자는 많을수록 좋은 것이 아니라, 같은 사건을 여러 좌표계에서 비교할 수 있어야 좋습니다.
matrix를 보기 좋은 형태로 출력한다
function formatMatrix3(m) {
return [
[m[0], m[3], m[6]],
[m[1], m[4], m[7]],
[m[2], m[5], m[8]]
].map((row) => row.map(round3).join("\t")).join("\n");
}
function updateMatrixPanel(root, readout) {
root.querySelector("[data-screen]").textContent = JSON.stringify(readout.screen);
root.querySelector("[data-world]").textContent = JSON.stringify(readout.world);
root.querySelector("[data-local]").textContent = JSON.stringify(readout.local);
root.querySelector("[data-matrix]").textContent = readout.matrix
? formatMatrix3(readout.matrix)
: "no selection";
}
matrix는 한 줄 배열로 보면 거의 읽히지 않습니다. 디버그 패널에서는 실제 행렬 모양으로 줄바꿈해 보여주는 편이 좌표계 오류를 빨리 찾게 해줍니다.
오늘의 핵심
좌표계 버그는 말로 설명하기 어렵습니다. readout은 그 버그를 재현 가능한 숫자로 바꿉니다.