Object3D transform과 editor scene graph 매핑
Three.js의 모든 scene object는 Object3D를 기반으로 합니다. position, rotation, scale, matrix, matrixWorld는 editor scene graph의 transform inheritance와 직접 연결됩니다.
editor node를 Object3D로 투영한다
object.position.set(node.x, node.y, 0);
object.rotation.z = node.rotation;
object.scale.set(node.scaleX, node.scaleY, 1);
이 값은 renderer projection입니다. 문서의 원본 값은 editor state에 남아 있어야 합니다.
CSS transform 순서와 Object3D compose를 맞춘다
editor node가 x/y/rotation/scale을 갖는다면 Three.js에서는 position, rotation.z, scale을 쓰거나 Matrix4.compose로 직접 만들 수 있습니다.
function composeObjectMatrix(node) {
const position = new THREE.Vector3(node.x, -node.y, 0);
const rotation = new THREE.Quaternion()
.setFromEuler(new THREE.Euler(0, 0, -node.rotation));
const scale = new THREE.Vector3(node.scaleX, node.scaleY, 1);
return new THREE.Matrix4().compose(position, rotation, scale);
}
function applyNodeTransform(object, node) {
object.matrix.copy(composeObjectMatrix(node));
object.matrixAutoUpdate = false;
}
-node.y, -node.rotation은 editor world를 y-down으로 유지하는 경우의 보정입니다. y-up으로 모델을 설계했다면 이 보정은 필요하지 않습니다.
matrix를 직접 넣을 수도 있다
object.matrix.fromArray(node.localMatrix);
object.matrixAutoUpdate = false;
object.updateMatrixWorld(true);
많은 node를 다룰 때는 언제 matrix를 갱신할지 renderer가 명시적으로 제어하는 편이 좋습니다.
parent/child world matrix를 읽는다
Three.js의 matrixWorld는 Part 4에서 만든 worldMatrix와 같은 역할입니다.
function syncWorldBoundsFromObject(object, localBounds) {
object.updateMatrixWorld(true);
const corners = [
new THREE.Vector3(localBounds.x, localBounds.y, 0),
new THREE.Vector3(localBounds.x + localBounds.width, localBounds.y, 0),
new THREE.Vector3(localBounds.x + localBounds.width, localBounds.y + localBounds.height, 0),
new THREE.Vector3(localBounds.x, localBounds.y + localBounds.height, 0)
].map((point) => point.applyMatrix4(object.matrixWorld));
return aabbFromPoints(corners);
}
selection bounds, hit test, culling은 결국 이 world bounds를 기준으로 돌아갑니다.
오늘의 핵심
Object3D는 editor node의 렌더링 표현입니다. document model을 Three.js object에 맡기지 않는 것이 핵심입니다.