import Class from '../core/Class';
import Matrix4 from '../math/Matrix4';
import Vector3 from '../math/Vector3';
import log from '../utils/log';
import {
each
} from '../utils/util';
const tempMatrix4 = new Matrix4();
const tempVector3 = new Vector3();
const tempFloat32Array = new Float32Array([0, 0, 0]);
/**
* 光管理类
* @class
*/
const LightManager = Class.create(/** @lends LightManager.prototype */{
/**
* @default true
* @type {boolean}
*/
isLightManager: true,
/**
* @default LightManager
* @type {string}
*/
className: 'LightManager',
/**
* 是否开启阴影
* @type {boolean}
* @default true
*/
shadowEnabled: true,
/**
* @constructs
* @param {Object} [params] 创建对象的属性参数。可包含此类的所有属性。
*/
constructor(params) {
/**
* @type {AmbientLight[]}
*/
this.ambientLights = [];
/**
* @type {DirectionalLight[]}
*/
this.directionalLights = [];
/**
* @type {PointLight[]}
*/
this.pointLights = [];
/**
* @type {SpotLight[]}
*/
this.spotLights = [];
/**
* @type {AreaLight[]}
*/
this.areaLights = [];
this.lightInfo = {
AMBIENT_LIGHTS: 0,
POINT_LIGHTS: 0,
DIRECTIONAL_LIGHTS: 0,
SPOT_LIGHTS: 0,
AREA_LIGHTS: 0,
uid: 0
};
Object.assign(this, params);
},
getRenderOption(option = {}) {
each(this.lightInfo, (count, name) => {
if (name === 'uid' || !count) {
return;
}
option[name] = count;
const shadowMapCount = this.getShadowMapCount(name);
if (shadowMapCount) {
option[name + '_SMC'] = shadowMapCount;
}
});
return option;
},
/**
* 增加光
* @param {Light} light 光源
* @return {LightManager} this
*/
addLight(light) {
let lights = null;
if (!light.enabled) {
return this;
}
if (light.isAmbientLight) {
lights = this.ambientLights;
} else if (light.isDirectionalLight) {
lights = this.directionalLights;
} else if (light.isPointLight) {
lights = this.pointLights;
} else if (light.isSpotLight) {
lights = this.spotLights;
} else if (light.isAreaLight) {
lights = this.areaLights;
} else {
log.warnOnce(`LightManager.addLight(${light.id})`, 'Not support this light:', light);
}
if (lights) {
if (light.shadow) {
lights.unshift(light);
} else {
lights.push(light);
}
}
return this;
},
/**
* 获取方向光信息
* @param {Camera} camera 摄像机
* @return {Object}
*/
getDirectionalInfo(camera) {
const colors = [];
const infos = [];
const shadowMap = [];
const shadowMapSize = [];
const lightSpaceMatrix = [];
const shadowBias = [];
this.directionalLights.forEach((light, index) => {
const offset = index * 3;
light.getRealColor().toRGBArray(colors, offset);
light.getViewDirection(camera).toArray(infos, offset);
if (light.shadow && light.lightShadow) {
shadowMap.push(light.lightShadow.framebuffer.texture);
shadowMapSize.push(light.lightShadow.width);
shadowMapSize.push(light.lightShadow.height);
shadowBias.push(light.lightShadow.minBias, light.lightShadow.maxBias);
tempMatrix4.copy(camera.worldMatrix);
tempMatrix4.premultiply(light.lightShadow.camera.viewProjectionMatrix);
tempMatrix4.toArray(lightSpaceMatrix, index * 16);
}
});
const result = {
colors: new Float32Array(colors),
infos: new Float32Array(infos)
};
if (shadowMap.length) {
result.shadowMap = shadowMap;
result.shadowMapSize = new Float32Array(shadowMapSize);
result.shadowBias = new Float32Array(shadowBias);
result.lightSpaceMatrix = new Float32Array(lightSpaceMatrix);
}
return result;
},
/**
* 获取聚光灯信息
* @param {Camera} camera 摄像机
* @return {Object}
*/
getSpotInfo(camera) {
const colors = [];
const infos = [];
const poses = [];
const dirs = [];
const cutoffs = [];
const shadowMap = [];
const shadowMapSize = [];
const lightSpaceMatrix = [];
const shadowBias = [];
const ranges = [];
this.spotLights.forEach((light, index) => {
const offset = index * 3;
light.getRealColor().toRGBArray(colors, offset);
light.toInfoArray(infos, offset);
light.getViewDirection(camera).toArray(dirs, offset);
ranges.push(light.range);
cutoffs.push(light._cutoffCos, light._outerCutoffCos);
camera.getModelViewMatrix(light, tempMatrix4);
tempMatrix4.getTranslation(tempVector3);
tempVector3.toArray(poses, offset);
if (light.shadow && light.lightShadow) {
shadowMap.push(light.lightShadow.framebuffer.texture);
shadowMapSize.push(light.lightShadow.width);
shadowMapSize.push(light.lightShadow.height);
shadowBias.push(light.lightShadow.minBias, light.lightShadow.maxBias);
tempMatrix4.multiply(light.lightShadow.camera.viewProjectionMatrix, camera.worldMatrix);
tempMatrix4.toArray(lightSpaceMatrix, index * 16);
}
});
const result = {
colors: new Float32Array(colors),
infos: new Float32Array(infos),
poses: new Float32Array(poses),
dirs: new Float32Array(dirs),
cutoffs: new Float32Array(cutoffs),
ranges: new Float32Array(ranges)
};
if (shadowMap.length) {
result.shadowMap = shadowMap;
result.shadowMapSize = new Float32Array(shadowMapSize);
result.shadowBias = new Float32Array(shadowBias);
result.lightSpaceMatrix = new Float32Array(lightSpaceMatrix);
}
return result;
},
/**
* 获取点光源信息
* @param {Camera} camera 摄像机
* @return {Object}
*/
getPointInfo(camera) {
const colors = [];
const infos = [];
const poses = [];
const shadowMap = [];
const lightSpaceMatrix = [];
const shadowBias = [];
const cameras = [];
const ranges = [];
this.pointLights.forEach((light, index) => {
const offset = index * 3;
light.getRealColor().toRGBArray(colors, offset);
light.toInfoArray(infos, offset);
ranges.push(light.range);
camera.getModelViewMatrix(light, tempMatrix4);
tempMatrix4.getTranslation(tempVector3);
tempVector3.toArray(poses, offset);
if (light.shadow && light.lightShadow) {
shadowMap.push(light.lightShadow.framebuffer.texture);
shadowBias.push(light.lightShadow.minBias, light.lightShadow.maxBias);
camera.worldMatrix.toArray(lightSpaceMatrix, index * 16);
cameras[index * 2] = light.lightShadow.camera.near;
cameras[index * 2 + 1] = light.lightShadow.camera.far;
}
});
const result = {
colors: new Float32Array(colors),
infos: new Float32Array(infos),
poses: new Float32Array(poses),
ranges: new Float32Array(ranges)
};
if (shadowMap.length) {
result.shadowMap = shadowMap;
result.shadowBias = new Float32Array(shadowBias);
result.lightSpaceMatrix = new Float32Array(lightSpaceMatrix);
result.cameras = new Float32Array(cameras);
}
return result;
},
/**
* 获取面光源信息
* @param {Camera} camera 摄像机
* @return {Object}
*/
getAreaInfo(camera) {
const colors = [];
const poses = [];
const width = [];
const height = [];
let ltcTexture1;
let ltcTexture2;
this.areaLights.forEach((light, index) => {
const offset = index * 3;
light.getRealColor().toRGBArray(colors, offset);
camera.getModelViewMatrix(light, tempMatrix4);
tempMatrix4.getTranslation(tempVector3);
tempVector3.toArray(poses, offset);
const quat = tempMatrix4.getRotation();
tempMatrix4.fromQuat(quat);
tempVector3.set(light.width * 0.5, 0, 0);
tempVector3.transformMat4(tempMatrix4);
tempVector3.toArray(width, offset);
tempVector3.set(0.0, light.height * 0.5, 0.0);
tempVector3.transformMat4(tempMatrix4);
tempVector3.toArray(height, offset);
ltcTexture1 = light.ltcTexture1;
ltcTexture2 = light.ltcTexture2;
});
const result = {
colors: new Float32Array(colors),
poses: new Float32Array(poses),
width: new Float32Array(width),
height: new Float32Array(height),
ltcTexture1,
ltcTexture2
};
return result;
},
/**
* 获取环境光信息
* @return {Object}
*/
getAmbientInfo() {
tempFloat32Array[0] = tempFloat32Array[1] = tempFloat32Array[2] = 0;
this.ambientLights.forEach((light) => {
const realColor = light.getRealColor();
tempFloat32Array[0] += realColor.r;
tempFloat32Array[1] += realColor.g;
tempFloat32Array[2] += realColor.b;
});
tempFloat32Array[0] = Math.min(1, tempFloat32Array[0]);
tempFloat32Array[1] = Math.min(1, tempFloat32Array[1]);
tempFloat32Array[2] = Math.min(1, tempFloat32Array[2]);
return tempFloat32Array;
},
/**
* 更新所有光源信息
* @param {Camera} camera 摄像机
*/
updateInfo(camera) {
const {
lightInfo,
ambientLights,
directionalLights,
pointLights,
spotLights,
areaLights
} = this;
lightInfo.AMBIENT_LIGHTS = ambientLights.length;
lightInfo.POINT_LIGHTS = pointLights.length;
lightInfo.DIRECTIONAL_LIGHTS = directionalLights.length;
lightInfo.SPOT_LIGHTS = spotLights.length;
lightInfo.AREA_LIGHTS = areaLights.length;
const shadowFilter = light => !!light.shadow;
lightInfo.SHADOW_POINT_LIGHTS = pointLights.filter(shadowFilter).length;
lightInfo.SHADOW_SPOT_LIGHTS = spotLights.filter(shadowFilter).length;
lightInfo.SHADOW_DIRECTIONAL_LIGHTS = directionalLights.filter(shadowFilter).length;
lightInfo.uid = [
lightInfo.AMBIENT_LIGHTS,
lightInfo.POINT_LIGHTS,
lightInfo.SHADOW_POINT_LIGHTS,
lightInfo.DIRECTIONAL_LIGHTS,
lightInfo.SHADOW_DIRECTIONAL_LIGHTS,
lightInfo.SPOT_LIGHTS,
lightInfo.SHADOW_SPOT_LIGHTS,
lightInfo.AREA_LIGHTS
].join('_');
this.directionalInfo = this.getDirectionalInfo(camera);
this.pointInfo = this.getPointInfo(camera);
this.spotInfo = this.getSpotInfo(camera);
this.areaInfo = this.getAreaInfo(camera);
this.ambientInfo = this.getAmbientInfo();
if (this.updateCustomInfo) {
this.updateCustomInfo(this, camera);
}
},
/**
* 更新自定义灯光信息
* @type updateCustomInfoCallback
* @default null
*/
updateCustomInfo: null,
/**
* 获取光源信息
* @return {Object}
*/
getInfo() {
return this.lightInfo;
},
/**
* 重置所有光源
*/
reset() {
this.ambientLights.length = 0;
this.directionalLights.length = 0;
this.pointLights.length = 0;
this.spotLights.length = 0;
this.areaLights.length = 0;
},
/**
* 获取阴影贴图数量
* @param {string} type
* @returns {number}
*/
getShadowMapCount(type) {
if (!this.shadowEnabled) {
return 0;
}
let lights = [];
if (type === 'POINT_LIGHTS') {
lights = this.pointLights;
} else if (type === 'DIRECTIONAL_LIGHTS') {
lights = this.directionalLights;
} else if (type === 'SPOT_LIGHTS') {
lights = this.spotLights;
} else if (type === 'AREA_LIGHTS') {
lights = this.spotLights;
}
let count = 0;
lights.forEach((light) => {
count += light.shadow ? 1 : 0;
});
return count;
},
/**
* 更新光源信息
* @param {WebGLRenderer} renderer
* @param {Light[]} lights
* @param {Camera} camera
*/
update(renderer, camera, lights) {
lights.forEach((light) => {
this.addLight(light);
});
this.createShadowMap(renderer, camera);
this.updateInfo(camera);
},
/**
* 生成阴影贴图
* @param {WebGLRenderer} renderer
* @param {Camera} camera
*/
createShadowMap(renderer, camera) {
if (!this.shadowEnabled) {
return;
}
this.directionalLights.forEach((light) => {
light.createShadowMap(renderer, camera);
});
this.spotLights.forEach((light) => {
light.createShadowMap(renderer, camera);
});
this.pointLights.forEach((light) => {
light.createShadowMap(renderer, camera);
});
this.areaLights.forEach((light) => {
light.createShadowMap(renderer, camera);
});
}
});
export default LightManager;
/**
* 更新自定义灯光回调
* @callback updateCustomInfoCallback
* @param { LightManager } lightManager
* @param { Camera } camera
*/
/**
* 灯光信息接口
* @interface ILightInfo
* @property {string} uid
*/
/**
* 灯光管理器接口
* @interface ILightManager
* @property {boolean} shadowEnabled
* @property {ILightInfo} lightInfo
*/
/**
* 重置所有光源信息
* @function
* @name ILightManager#reset
*/
/**
* 更新光源信息
* @function
* @param {WebGLRenderer} renderer
* @param {Camera} camera
* @param {Light[]} lights
* @name ILightManager#update
*/
/**
* 获取渲染配置
* @function
* @param {object} [option]
* @name ILightManager#getRenderOption
*/