pointer 좌표를 world 좌표로 바꾸기
편집 도구는 대부분 pointer event에서 시작합니다. 그런데 pointer event가 주는 좌표는 문서 좌표가 아닙니다. 브라우저 viewport 기준 숫자입니다.
그래서 GPU 에디터의 입력 첫 단계는 항상 좌표 변환입니다.
client -> canvas screen -> world
먼저 canvas 안의 screen 좌표를 만든다
clientX, clientY에서 canvas의 viewport 위치를 뺍니다.
function clientToCanvas(event, canvas) {
const rect = canvas.getBoundingClientRect();
return {
x: event.clientX - rect.left,
y: event.clientY - rect.top
};
}
이 값은 canvas 왼쪽 위를 기준으로 한 screen 좌표입니다.
camera inverse로 world를 얻는다
camera가 screen = (world - camera) * zoom이라면 반대 변환은 이렇습니다.
function screenToWorld(screen, camera) {
return {
x: screen.x / camera.zoom + camera.x,
y: screen.y / camera.zoom + camera.y
};
}
function worldToScreen(world, camera) {
return {
x: (world.x - camera.x) * camera.zoom,
y: (world.y - camera.y) * camera.zoom
};
}
도형 선택, 드래그, snapping은 보통 이 world 좌표를 기준으로 시작합니다.
pointer action은 두 좌표를 함께 보낸다
tool은 screen 좌표와 world 좌표를 모두 쓰는 경우가 많습니다. marquee selection은 screen 좌표가 편하고, move/resize/hit test는 world 좌표가 편합니다.
function pointerEventToInput(event, canvas, camera) {
const screen = clientToCanvas(event, canvas);
const world = screenToWorld(screen, camera);
return {
pointerId: event.pointerId,
buttons: event.buttons,
shiftKey: event.shiftKey,
altKey: event.altKey,
screen,
world
};
}
이 함수가 있으면 모든 tool이 같은 좌표 변환을 공유합니다.
입력 좌표는 renderer와 공유된다
renderer도 world를 screen으로 바꿉니다. input system은 screen을 world로 되돌립니다. 두 변환은 서로 반대여야 합니다.
renderer: world -> screen
input: screen -> world
둘이 다른 공식을 쓰기 시작하면 선택 박스와 실제 도형 위치가 어긋납니다.
오늘의 핵심
GPU 에디터에서 pointer 좌표는 바로 도형 좌표가 아닙니다. camera inverse를 거쳐 world 좌표로 바꿔야 합니다.
pointer event는 입력 표면의 좌표다.
editor command는 문서 좌표를 바꾼다.