234 lines
5.0 KiB
JavaScript
234 lines
5.0 KiB
JavaScript
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 }
|