UNPKG

pex-renderer

Version:

Physically Based Renderer for Pex

204 lines (187 loc) 5.53 kB
const Signal = require('signals') const vec3 = require('pex-math').vec3 function LightHelper(opts) { this.type = 'LightHelper' this.entity = null this.changed = new Signal() this.dirty = false this.enabled = true if (opts) this.set(opts) } // this function gets called when the component is added // to an enity LightHelper.prototype.init = function(entity) { this.entity = entity } LightHelper.prototype.set = function(opts) { Object.assign(this, opts) this.dirty = true Object.keys(opts).forEach((prop) => this.changed.dispatch(prop)) } LightHelper.prototype.update = function() { if (!this.dirty) return this.dirty = false } LightHelper.prototype.getCirclePositions = function(opts) { const points = [] for (let i = 0; i < opts.steps; i++) { const t = (i / opts.steps) * 2 * Math.PI const x = Math.cos(t) const y = Math.sin(t) const pos = [0, 0, 0] pos[opts.axis ? opts.axis[0] : 0] = x pos[opts.axis ? opts.axis[1] : 1] = y vec3.scale(pos, opts.radius || 1) vec3.add(pos, opts.center || [0, 0, 0]) points.push(pos) } const lines = points.reduce((lines, p, i) => { lines.push(p) lines.push(points[(i + 1) % points.length]) return lines }, []) return lines } LightHelper.prototype.getPrismPositions = function(opts) { const r = opts.radius const position = opts.position || [0, 0, 0] // prettier-ignore const points = [ [0, r, 0], [r, 0, 0], [0, -r, 0], [r, 0, 0], [0, r, 0], [-r, 0, 0], [0, -r, 0], [-r, 0, 0], [0, r, 0], [0, 0, r], [0, -r, 0], [0, 0, r], [0, r, 0], [0, 0, -r], [0, -r, 0], [0, 0, -r], [-r, 0, 0], [0, 0, -r], [r, 0, 0], [0, 0, -r], [r, 0, 0], [0, 0, r], [-r, 0, 0], [0, 0, r] ] points.forEach((p) => vec3.add(p, position)) return points } LightHelper.prototype.getQuadPositions = function(opts) { const w = opts.width const h = opts.height const position = opts.position || [0, 0, 0] // prettier-ignore const points = [ [-1, -1, 0], [1, -1, 0], [1, -1, 0], [1, 1, 0], [1, 1, 0], [-1, 1, 0], [-1, 1, 0], [-1, -1, 0], [-1, -1, 0], [1, 1, 0], [-1, 1, 0], [1, -1, 0], [-1, -1, 0], [-1, -1, 1 / 2], [1, -1, 0], [1, -1, 1 / 2], [1, 1, 0], [1, 1, 1 / 2], [-1, 1, 0], [-1, 1, 1 / 2], [0, 0, 0], [0, 0, 1 / 2] ] points.forEach((p) => { p[0] *= w / 2 p[1] *= h / 2 vec3.add(p, position) }) return points } LightHelper.prototype.draw = function(geomBuilder) { let lType lType = this.entity.getComponent('DirectionalLight') if (lType) { let dirLightTransform = this.entity.getComponent('Transform') const directionalLightGizmoPositions = this.getPrismPositions({ radius: 0.3 }).concat( /* prettier-ignore */ [ [0, 0, 0.3], [0, 0, 1], [0.3, 0, 0], [0.3, 0, 1], [-0.3, 0, 0], [-0.3, 0, 1], [0, 0.3, 0], [0, 0.3, 1], [0, -0.3, 0], [0, -0.3, 1] ] ) directionalLightGizmoPositions.forEach((pos) => { vec3.multMat4(pos, dirLightTransform.modelMatrix) geomBuilder.addPosition(pos) geomBuilder.addColor(lType.color) }) } lType = this.entity.getComponent('AreaLight') if (lType) { //area light let areaLightTransform = this.entity.getComponent('Transform') const areaLightHelperPositions = this.getQuadPositions({ width: 1, height: 1 }) areaLightHelperPositions.forEach((pos) => { vec3.multMat4(pos, areaLightTransform.modelMatrix) geomBuilder.addPosition(pos) geomBuilder.addColor(lType.color) }) } lType = this.entity.getComponent('PointLight') if (lType) { //pointlight let pointLightTransform = this.entity.getComponent('Transform') const pointLightHelperPositions = this.getPrismPositions({ radius: 0.2 }).concat( /* prettier-ignore */ [ [0, 0.0, 0], [0, 0.6, 0], [0, -0.0, 0], [0, -0.6, 0], [0.0, 0, 0], [0.6, 0, 0], [-0.0, 0, 0], [-0.6, 0, 0], [0, 0, 0.0], [0, 0, 0.6], [0, 0, -0.0], [0, 0, -0.6] ] ) pointLightHelperPositions.forEach((pos) => { vec3.multMat4(pos, pointLightTransform.modelMatrix) geomBuilder.addPosition(pos) geomBuilder.addColor(lType.color) }) } lType = this.entity.getComponent('SpotLight') if (lType) { //spotlight let spotlightTransform = this.entity.getComponent('Transform') //the range seemed way too large ? const spotLightDistance = lType.range const spotLightRadius = spotLightDistance * Math.tan(lType.angle) const spotLightHelperPositions = this.getPrismPositions({ radius: 0.2 }) .concat([ [0, 0, 0], [spotLightRadius, 0, spotLightDistance], [0, 0, 0], [-spotLightRadius, 0, spotLightDistance], [0, 0, 0], [0, spotLightRadius, spotLightDistance], [0, 0, 0], [0, -spotLightRadius, spotLightDistance] ]) .concat( this.getCirclePositions({ radius: spotLightRadius, center: [0, 0, spotLightDistance], steps: 64, axis: [0, 1] }) ) spotLightHelperPositions.forEach((pos) => { geomBuilder.addPosition( vec3.multMat4(vec3.copy(pos), spotlightTransform.modelMatrix) ) geomBuilder.addColor(lType.color) }) } } // by pex-renderer convention we export factory function // instead of the class type module.exports = function createLightHelper(opts) { return new LightHelper(opts) }