pex-renderer
Version:
Physically Based Renderer for Pex
148 lines (132 loc) • 4.02 kB
JavaScript
const Signal = require('signals')
const vec3 = require('pex-math/vec3')
const vec4 = require('pex-math/vec4')
const mat4 = require('pex-math/mat4')
const aabb = require('pex-geom/aabb')
function vec4set4(v, x, y, z, w) {
v[0] = x
v[1] = y
v[2] = z
v[3] = w
return v
}
// TODO remove, should in AABB
function emptyAABB(a) {
a[0][0] = Infinity
a[0][1] = Infinity
a[0][2] = Infinity
a[1][0] = -Infinity
a[1][1] = -Infinity
a[1][2] = -Infinity
}
// TODO remove, should in AABB
function aabbToPoints(points, box) {
vec4set4(points[0], box[0][0], box[0][1], box[0][2], 1)
vec4set4(points[1], box[1][0], box[0][1], box[0][2], 1)
vec4set4(points[2], box[1][0], box[0][1], box[1][2], 1)
vec4set4(points[3], box[0][0], box[0][1], box[1][2], 1)
vec4set4(points[4], box[0][0], box[1][1], box[0][2], 1)
vec4set4(points[5], box[1][0], box[1][1], box[0][2], 1)
vec4set4(points[6], box[1][0], box[1][1], box[1][2], 1)
vec4set4(points[7], box[0][0], box[1][1], box[1][2], 1)
return points
}
function aabbFromPoints(aabb, points) {
var min = aabb[0]
var max = aabb[1]
for (var i = 0, len = points.length; i < len; i++) {
var p = points[i]
min[0] = Math.min(min[0], p[0])
min[1] = Math.min(min[1], p[1])
min[2] = Math.min(min[2], p[2])
max[0] = Math.max(max[0], p[0])
max[1] = Math.max(max[1], p[1])
max[2] = Math.max(max[2], p[2])
}
return aabb
}
var tempMat4multQuatMat4 = mat4.create()
function mat4multQuat(m, q) {
mat4.fromQuat(tempMat4multQuatMat4, q)
mat4.mult(m, tempMat4multQuatMat4)
return m
}
function Transform(opts) {
this.type = 'Transform'
this.enabled = true
this.changed = new Signal()
this.entity = null
this.position = [0, 0, 0]
this.worldPosition = [0, 0, 0]
this.rotation = [0, 0, 0, 1]
this.scale = [1, 1, 1]
this.parent = null
this.children = []
// bounds of this node and it's children
this.bounds = aabb.create()
this._boundsPoints = new Array(8).fill(0).map(() => vec4.create())
// bounds of this node and it's children in the world space
this.worldBounds = aabb.create()
this.localModelMatrix = mat4.create()
this.modelMatrix = mat4.create()
this.geometry = null
this.set(opts)
}
Transform.prototype.init = function(entity) {
this.entity = entity
}
Transform.prototype.set = function(opts) {
if (opts.parent !== undefined) {
if (this.parent) {
this.parent.children.splice(this.parent.children.indexOf(this), 1)
}
if (opts.parent) {
opts.parent.children.push(this)
}
}
Object.assign(this, opts)
Object.keys(opts).forEach((prop) => this.changed.dispatch(prop))
}
Transform.prototype.update = function() {
mat4.identity(this.localModelMatrix)
mat4.translate(this.localModelMatrix, this.position)
mat4multQuat(this.localModelMatrix, this.rotation)
mat4.scale(this.localModelMatrix, this.scale)
if (this.matrix) mat4.mult(this.localModelMatrix, this.matrix)
mat4.identity(this.modelMatrix)
var parents = []
var parent = this
while (parent) {
parents.unshift(parent) // TODO: GC
parent = parent.parent
}
parents.forEach((p) => {
// TODO: forEach
mat4.mult(this.modelMatrix, p.localModelMatrix)
})
emptyAABB(this.bounds)
const geom = this.entity.getComponent('Geometry')
if (geom) {
aabb.set(this.bounds, geom.bounds)
}
}
Transform.prototype.afterUpdate = function() {
emptyAABB(this.worldBounds)
if (!aabb.isEmpty(this.bounds)) {
aabbToPoints(this._boundsPoints, this.bounds)
for (var i = 0; i < this._boundsPoints.length; i++) {
vec3.multMat4(this._boundsPoints[i], this.modelMatrix)
}
aabbFromPoints(this.worldBounds, this._boundsPoints)
}
this.children.forEach((child) => {
if (!aabb.isEmpty(child.worldBounds)) {
aabb.includeAABB(this.worldBounds, child.worldBounds)
}
})
vec3.scale(this.worldPosition, 0)
vec3.multMat4(this.worldPosition, this.modelMatrix)
}
module.exports = function createTransform(opts) {
return new Transform(opts)
}