import { mat4, quat, vec3 } from 'gl-matrix' /** * 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 (pitch, yaw, roll) in degrees 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() let rot = quat.create() // Set rotation quat.fromEuler(rot, this.rotation[0], this.rotation[1], this.rotation[2]) mat4.fromRotationTranslationScale(matrix, rot, this.pos, this.scale) // Add local transform to the global transform, if present // Will be present in loaded models if (this.worldTransform) { mat4.mul(matrix, matrix, this.worldTransform) } // Add parent's transform to this if (this.parent) { mat4.mul(matrix, this.parent.transform, matrix) } // 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() } } fromLocalTransform (transform) { let quaternion = quat.create() let translation = vec3.create() let rotation = vec3.create() let scale = vec3.create() mat4.transpose(transform, transform) 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) { // Nothing to draw here, so just draw children for (let i in this.children) { let child = this.children[i] if (!(child instanceof Node)) continue child.draw(gl, shader) } } update (dt) { // Update children for (let i in this.children) { let child = this.children[i] if (!(child instanceof Node)) continue child.update(dt) } } addChild (ch) { // Recursive add in case of table if (ch && typeof ch === 'object' && ch.length) { for (let i in ch) { this.addChild(ch[i]) } } if (!(ch instanceof Node)) return ch.setParent(this) this.children.push(ch) this.updateTransform() } setParent (p) { if (!(p instanceof Node)) return this.parent = p this.updateTransform() } } // A node defining a single mesh class MeshInstance extends Node { constructor (mesh, pos, scale, rot) { super(pos, scale, rot) this.mesh = mesh } draw (gl, shader) { if (!this.mesh) return super.draw(gl, shader) // Set model transform matrix uniform const transformLocation = shader.getUniformLocation(gl, 'uModelMatrix') gl.uniformMatrix4fv(transformLocation, false, this.transform) // Draw the mesh this.mesh.prepare(gl, shader) this.mesh.draw(gl, shader) // Draw children super.draw(gl, shader) } } export { Node, MeshInstance }