import Resource from '../resource' import { mat4, quat, vec3 } from 'gl-matrix' import { Mesh } from '../mesh' import { Material } from '../mesh/material' let meshCache = {} /** * Returns an euler angle representation of a quaternion * @param {vec3} out Euler angles, pitch-yaw-roll * @param {quat} mat Quaternion * @return {vec3} out */ quat.getEuler = function (out, quat) { let x = quat[0] let y = quat[1] let z = quat[2] let w = quat[3] let x2 = x * x let y2 = y * y let z2 = z * z let w2 = w * w let unit = x2 + y2 + z2 + w2 let test = x * w - y * z if (test > 0.499995 * unit) { // singularity at the north pole out[0] = Math.PI / 2 out[1] = 2 * Math.atan2(y, x) out[2] = 0 } else if (test < -0.499995 * unit) { // singularity at the south pole out[0] = -Math.PI / 2 out[1] = 2 * Math.atan2(y, x) out[2] = 0 } else { out[0] = Math.asin(2 * (x * z - w * y)) out[1] = Math.atan2(2 * (x * w + y * z), 1 - 2 * (z2 + w2)) out[2] = Math.atan2(2 * (x * y + z * w), 1 - 2 * (y2 + z2)) } return out } class Node { constructor (pos, scale, rotation) { // Translation this.pos = pos || [0.0, 0.0, 0.0] // Scaling this.scale = scale || [1.0, 1.0, 1.0] // Rotation in Euler angles (yaw, pitch, roll) in radians this.rotation = rotation || [0.0, 0.0, 0.0] this.transform = mat4.create() this.updateTransform() this.parent = null this.children = [] } updateTransform () { let matrix = mat4.create() // Add parent node's transform if (this.parent) { mat4.mul(matrix, matrix, this.parent.transform) } // Set translation mat4.translate(matrix, matrix, this.pos) // Set rotation if (this.rotation[0] !== 0) { mat4.rotateX(matrix, matrix, this.rotation[0]) } if (this.rotation[1] !== 0) { mat4.rotateY(matrix, matrix, this.rotation[1]) } if (this.rotation[2] !== 0) { mat4.rotateZ(matrix, matrix, this.rotation[2]) } // Set scale mat4.scale(matrix, matrix, this.scale) // Set the matrix this.transform = matrix // Update children's transforms for (let i in this.children) { let child = this.children[i] if (!(child instanceof Node)) continue child.updateTransform() } } setTransformation (transform) { let quaternion = quat.create() let translation = vec3.create() let rotation = vec3.create() let scale = vec3.create() mat4.getScaling(scale, transform) mat4.getRotation(quaternion, transform) mat4.getTranslation(translation, transform) quat.getEuler(rotation, quaternion) this.rotation = rotation this.pos = translation this.scale = scale this.updateTransform() } // Setters setPosition (newPos) { this.pos = newPos this.updateTransform() } setTranslation (newTrans) { this.setPosition(newTrans) } setScale (newScale) { this.scale = newScale this.updateTransform() } setRotation (newRotation) { this.rotation = newRotation this.updateTransform() } // Transforms translate (pos) { mat4.translate(this.transform, this.transform, pos) } scale (scale) { mat4.scale(this.transform, this.transform, scale) } rotate (rot) { if (rot[0] !== 0) { mat4.rotateX(this.transform, this.transform, rot[0]) } if (rot[1] !== 0) { mat4.rotateY(this.transform, this.transform, rot[1]) } if (rot[2] !== 0) { mat4.rotateZ(this.transform, this.transform, rot[2]) } } // Getters get position () { return this.pos } get translation () { return this.pos } // Draw base draw (gl, shader) { // Set model transform matrix uniform const transformLocation = shader.getUniformLocation(gl, 'uModelMatrix') gl.uniformMatrix4fv(transformLocation, false, this.transform) } addChild (ch) { if (!(ch instanceof Node)) return this.children.push(ch) this.updateTransform() } setParent (p) { if (!(p instanceof Node)) return this.parent = p this.updateTransform() } } class MeshInstance extends Node { constructor (mesh, pos, scale, rot) { super(pos, scale, rot) this.mesh = mesh } draw (gl, shader) { // Set model transform uniform super.draw(gl, shader) // Draw the mesh this.mesh.prepare(gl, shader) this.mesh.draw(gl, shader) // Invoke children's draw methods for (let i in this.children) { let child = this.children[i] if (!(child instanceof MeshInstance)) continue child.draw(gl, shader) } } } class MultiMeshInstance extends Node { constructor (meshes, pos) { super(pos) for (let i in meshes) { meshes[i].parent = this this.children.push(meshes[i]) } } draw (gl, shader) { // Invoke children's draw methods for (let i in this.children) { let child = this.children[i] if (!(child instanceof MeshInstance)) continue child.draw(gl, shader) } } } export { Node, MeshInstance, MultiMeshInstance }