@openhps/core
Version:
Open Hybrid Positioning System - Core component
383 lines (378 loc) • 14.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.WebGLLights = WebGLLights;
var _Color = require("../../math/Color.js");
var _Matrix = require("../../math/Matrix4.js");
var _Vector = require("../../math/Vector2.js");
var _Vector2 = require("../../math/Vector3.js");
var _UniformsLib = require("../shaders/UniformsLib.js");
function UniformsCache() {
const lights = {};
return {
get: function (light) {
if (lights[light.id] !== undefined) {
return lights[light.id];
}
let uniforms;
switch (light.type) {
case 'DirectionalLight':
uniforms = {
direction: new _Vector2.Vector3(),
color: new _Color.Color()
};
break;
case 'SpotLight':
uniforms = {
position: new _Vector2.Vector3(),
direction: new _Vector2.Vector3(),
color: new _Color.Color(),
distance: 0,
coneCos: 0,
penumbraCos: 0,
decay: 0
};
break;
case 'PointLight':
uniforms = {
position: new _Vector2.Vector3(),
color: new _Color.Color(),
distance: 0,
decay: 0
};
break;
case 'HemisphereLight':
uniforms = {
direction: new _Vector2.Vector3(),
skyColor: new _Color.Color(),
groundColor: new _Color.Color()
};
break;
case 'RectAreaLight':
uniforms = {
color: new _Color.Color(),
position: new _Vector2.Vector3(),
halfWidth: new _Vector2.Vector3(),
halfHeight: new _Vector2.Vector3()
};
break;
}
lights[light.id] = uniforms;
return uniforms;
}
};
}
function ShadowUniformsCache() {
const lights = {};
return {
get: function (light) {
if (lights[light.id] !== undefined) {
return lights[light.id];
}
let uniforms;
switch (light.type) {
case 'DirectionalLight':
uniforms = {
shadowIntensity: 1,
shadowBias: 0,
shadowNormalBias: 0,
shadowRadius: 1,
shadowMapSize: new _Vector.Vector2()
};
break;
case 'SpotLight':
uniforms = {
shadowIntensity: 1,
shadowBias: 0,
shadowNormalBias: 0,
shadowRadius: 1,
shadowMapSize: new _Vector.Vector2()
};
break;
case 'PointLight':
uniforms = {
shadowIntensity: 1,
shadowBias: 0,
shadowNormalBias: 0,
shadowRadius: 1,
shadowMapSize: new _Vector.Vector2(),
shadowCameraNear: 1,
shadowCameraFar: 1000
};
break;
// TODO (abelnation): set RectAreaLight shadow uniforms
}
lights[light.id] = uniforms;
return uniforms;
}
};
}
let nextVersion = 0;
function shadowCastingAndTexturingLightsFirst(lightA, lightB) {
return (lightB.castShadow ? 2 : 0) - (lightA.castShadow ? 2 : 0) + (lightB.map ? 1 : 0) - (lightA.map ? 1 : 0);
}
function WebGLLights(extensions) {
const cache = new UniformsCache();
const shadowCache = ShadowUniformsCache();
const state = {
version: 0,
hash: {
directionalLength: -1,
pointLength: -1,
spotLength: -1,
rectAreaLength: -1,
hemiLength: -1,
numDirectionalShadows: -1,
numPointShadows: -1,
numSpotShadows: -1,
numSpotMaps: -1,
numLightProbes: -1
},
ambient: [0, 0, 0],
probe: [],
directional: [],
directionalShadow: [],
directionalShadowMap: [],
directionalShadowMatrix: [],
spot: [],
spotLightMap: [],
spotShadow: [],
spotShadowMap: [],
spotLightMatrix: [],
rectArea: [],
rectAreaLTC1: null,
rectAreaLTC2: null,
point: [],
pointShadow: [],
pointShadowMap: [],
pointShadowMatrix: [],
hemi: [],
numSpotLightShadowsWithMaps: 0,
numLightProbes: 0
};
for (let i = 0; i < 9; i++) state.probe.push(new _Vector2.Vector3());
const vector3 = new _Vector2.Vector3();
const matrix4 = new _Matrix.Matrix4();
const matrix42 = new _Matrix.Matrix4();
function setup(lights) {
let r = 0,
g = 0,
b = 0;
for (let i = 0; i < 9; i++) state.probe[i].set(0, 0, 0);
let directionalLength = 0;
let pointLength = 0;
let spotLength = 0;
let rectAreaLength = 0;
let hemiLength = 0;
let numDirectionalShadows = 0;
let numPointShadows = 0;
let numSpotShadows = 0;
let numSpotMaps = 0;
let numSpotShadowsWithMaps = 0;
let numLightProbes = 0;
// ordering : [shadow casting + map texturing, map texturing, shadow casting, none ]
lights.sort(shadowCastingAndTexturingLightsFirst);
for (let i = 0, l = lights.length; i < l; i++) {
const light = lights[i];
const color = light.color;
const intensity = light.intensity;
const distance = light.distance;
const shadowMap = light.shadow && light.shadow.map ? light.shadow.map.texture : null;
if (light.isAmbientLight) {
r += color.r * intensity;
g += color.g * intensity;
b += color.b * intensity;
} else if (light.isLightProbe) {
for (let j = 0; j < 9; j++) {
state.probe[j].addScaledVector(light.sh.coefficients[j], intensity);
}
numLightProbes++;
} else if (light.isDirectionalLight) {
const uniforms = cache.get(light);
uniforms.color.copy(light.color).multiplyScalar(light.intensity);
if (light.castShadow) {
const shadow = light.shadow;
const shadowUniforms = shadowCache.get(light);
shadowUniforms.shadowIntensity = shadow.intensity;
shadowUniforms.shadowBias = shadow.bias;
shadowUniforms.shadowNormalBias = shadow.normalBias;
shadowUniforms.shadowRadius = shadow.radius;
shadowUniforms.shadowMapSize = shadow.mapSize;
state.directionalShadow[directionalLength] = shadowUniforms;
state.directionalShadowMap[directionalLength] = shadowMap;
state.directionalShadowMatrix[directionalLength] = light.shadow.matrix;
numDirectionalShadows++;
}
state.directional[directionalLength] = uniforms;
directionalLength++;
} else if (light.isSpotLight) {
const uniforms = cache.get(light);
uniforms.position.setFromMatrixPosition(light.matrixWorld);
uniforms.color.copy(color).multiplyScalar(intensity);
uniforms.distance = distance;
uniforms.coneCos = Math.cos(light.angle);
uniforms.penumbraCos = Math.cos(light.angle * (1 - light.penumbra));
uniforms.decay = light.decay;
state.spot[spotLength] = uniforms;
const shadow = light.shadow;
if (light.map) {
state.spotLightMap[numSpotMaps] = light.map;
numSpotMaps++;
// make sure the lightMatrix is up to date
// TODO : do it if required only
shadow.updateMatrices(light);
if (light.castShadow) numSpotShadowsWithMaps++;
}
state.spotLightMatrix[spotLength] = shadow.matrix;
if (light.castShadow) {
const shadowUniforms = shadowCache.get(light);
shadowUniforms.shadowIntensity = shadow.intensity;
shadowUniforms.shadowBias = shadow.bias;
shadowUniforms.shadowNormalBias = shadow.normalBias;
shadowUniforms.shadowRadius = shadow.radius;
shadowUniforms.shadowMapSize = shadow.mapSize;
state.spotShadow[spotLength] = shadowUniforms;
state.spotShadowMap[spotLength] = shadowMap;
numSpotShadows++;
}
spotLength++;
} else if (light.isRectAreaLight) {
const uniforms = cache.get(light);
uniforms.color.copy(color).multiplyScalar(intensity);
uniforms.halfWidth.set(light.width * 0.5, 0.0, 0.0);
uniforms.halfHeight.set(0.0, light.height * 0.5, 0.0);
state.rectArea[rectAreaLength] = uniforms;
rectAreaLength++;
} else if (light.isPointLight) {
const uniforms = cache.get(light);
uniforms.color.copy(light.color).multiplyScalar(light.intensity);
uniforms.distance = light.distance;
uniforms.decay = light.decay;
if (light.castShadow) {
const shadow = light.shadow;
const shadowUniforms = shadowCache.get(light);
shadowUniforms.shadowIntensity = shadow.intensity;
shadowUniforms.shadowBias = shadow.bias;
shadowUniforms.shadowNormalBias = shadow.normalBias;
shadowUniforms.shadowRadius = shadow.radius;
shadowUniforms.shadowMapSize = shadow.mapSize;
shadowUniforms.shadowCameraNear = shadow.camera.near;
shadowUniforms.shadowCameraFar = shadow.camera.far;
state.pointShadow[pointLength] = shadowUniforms;
state.pointShadowMap[pointLength] = shadowMap;
state.pointShadowMatrix[pointLength] = light.shadow.matrix;
numPointShadows++;
}
state.point[pointLength] = uniforms;
pointLength++;
} else if (light.isHemisphereLight) {
const uniforms = cache.get(light);
uniforms.skyColor.copy(light.color).multiplyScalar(intensity);
uniforms.groundColor.copy(light.groundColor).multiplyScalar(intensity);
state.hemi[hemiLength] = uniforms;
hemiLength++;
}
}
if (rectAreaLength > 0) {
if (extensions.has('OES_texture_float_linear') === true) {
state.rectAreaLTC1 = _UniformsLib.UniformsLib.LTC_FLOAT_1;
state.rectAreaLTC2 = _UniformsLib.UniformsLib.LTC_FLOAT_2;
} else {
state.rectAreaLTC1 = _UniformsLib.UniformsLib.LTC_HALF_1;
state.rectAreaLTC2 = _UniformsLib.UniformsLib.LTC_HALF_2;
}
}
state.ambient[0] = r;
state.ambient[1] = g;
state.ambient[2] = b;
const hash = state.hash;
if (hash.directionalLength !== directionalLength || hash.pointLength !== pointLength || hash.spotLength !== spotLength || hash.rectAreaLength !== rectAreaLength || hash.hemiLength !== hemiLength || hash.numDirectionalShadows !== numDirectionalShadows || hash.numPointShadows !== numPointShadows || hash.numSpotShadows !== numSpotShadows || hash.numSpotMaps !== numSpotMaps || hash.numLightProbes !== numLightProbes) {
state.directional.length = directionalLength;
state.spot.length = spotLength;
state.rectArea.length = rectAreaLength;
state.point.length = pointLength;
state.hemi.length = hemiLength;
state.directionalShadow.length = numDirectionalShadows;
state.directionalShadowMap.length = numDirectionalShadows;
state.pointShadow.length = numPointShadows;
state.pointShadowMap.length = numPointShadows;
state.spotShadow.length = numSpotShadows;
state.spotShadowMap.length = numSpotShadows;
state.directionalShadowMatrix.length = numDirectionalShadows;
state.pointShadowMatrix.length = numPointShadows;
state.spotLightMatrix.length = numSpotShadows + numSpotMaps - numSpotShadowsWithMaps;
state.spotLightMap.length = numSpotMaps;
state.numSpotLightShadowsWithMaps = numSpotShadowsWithMaps;
state.numLightProbes = numLightProbes;
hash.directionalLength = directionalLength;
hash.pointLength = pointLength;
hash.spotLength = spotLength;
hash.rectAreaLength = rectAreaLength;
hash.hemiLength = hemiLength;
hash.numDirectionalShadows = numDirectionalShadows;
hash.numPointShadows = numPointShadows;
hash.numSpotShadows = numSpotShadows;
hash.numSpotMaps = numSpotMaps;
hash.numLightProbes = numLightProbes;
state.version = nextVersion++;
}
}
function setupView(lights, camera) {
let directionalLength = 0;
let pointLength = 0;
let spotLength = 0;
let rectAreaLength = 0;
let hemiLength = 0;
const viewMatrix = camera.matrixWorldInverse;
for (let i = 0, l = lights.length; i < l; i++) {
const light = lights[i];
if (light.isDirectionalLight) {
const uniforms = state.directional[directionalLength];
uniforms.direction.setFromMatrixPosition(light.matrixWorld);
vector3.setFromMatrixPosition(light.target.matrixWorld);
uniforms.direction.sub(vector3);
uniforms.direction.transformDirection(viewMatrix);
directionalLength++;
} else if (light.isSpotLight) {
const uniforms = state.spot[spotLength];
uniforms.position.setFromMatrixPosition(light.matrixWorld);
uniforms.position.applyMatrix4(viewMatrix);
uniforms.direction.setFromMatrixPosition(light.matrixWorld);
vector3.setFromMatrixPosition(light.target.matrixWorld);
uniforms.direction.sub(vector3);
uniforms.direction.transformDirection(viewMatrix);
spotLength++;
} else if (light.isRectAreaLight) {
const uniforms = state.rectArea[rectAreaLength];
uniforms.position.setFromMatrixPosition(light.matrixWorld);
uniforms.position.applyMatrix4(viewMatrix);
// extract local rotation of light to derive width/height half vectors
matrix42.identity();
matrix4.copy(light.matrixWorld);
matrix4.premultiply(viewMatrix);
matrix42.extractRotation(matrix4);
uniforms.halfWidth.set(light.width * 0.5, 0.0, 0.0);
uniforms.halfHeight.set(0.0, light.height * 0.5, 0.0);
uniforms.halfWidth.applyMatrix4(matrix42);
uniforms.halfHeight.applyMatrix4(matrix42);
rectAreaLength++;
} else if (light.isPointLight) {
const uniforms = state.point[pointLength];
uniforms.position.setFromMatrixPosition(light.matrixWorld);
uniforms.position.applyMatrix4(viewMatrix);
pointLength++;
} else if (light.isHemisphereLight) {
const uniforms = state.hemi[hemiLength];
uniforms.direction.setFromMatrixPosition(light.matrixWorld);
uniforms.direction.transformDirection(viewMatrix);
hemiLength++;
}
}
}
return {
setup: setup,
setupView: setupView,
state: state
};
}