UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

251 lines (207 loc) • 5.68 kB
import { LightType } from "../LightType.js"; import { AmbientLight, DirectionalLight, PointLight, SpotLight } from "three"; import { assert } from "../../../../../core/assert.js"; import { light2type } from "./light2type.js"; /** * * @param {Light} light */ function disableLight(light) { light.intensity = 0; if (typeof light.castShadow !== "undefined") { light.castShadow = false; } } /** * * @param {LightType} type */ function createLight(type) { switch (type) { case LightType.POINT: return new PointLight(0x000000, 0, 0, 1); case LightType.AMBIENT: return new AmbientLight(0x000000, 0); case LightType.DIRECTION: return new DirectionalLight(0x000000, 0); case LightType.SPOT: return new SpotLight(0x000000, 0, 0, 0, 0, 1); default: throw new Error('Unknown light type: ' + type); } } /** * @template T,U * @param {Map<T,U[]>} map * @param {T} key * @returns U[] */ function ensureBucket(map, key) { let elements = map.get(key); if (elements === undefined) { elements = []; map.set(key, elements); } return elements; } export class ThreeLightCache { constructor() { /** * * @type {Map<LightType, Light[]>} * @private */ this.__used = new Map(); /** * * @type {Map<LightType, Light[]>} * @private */ this.__available = new Map(); /** * * @type {null} * @private */ this.__scene = null; } /** * * @param {LightType} type * @returns {Light[]} * @private */ __getUsed(type) { return ensureBucket(this.__used, type); } /** * * @param {LightType} type * @returns {Light[]} * @private */ __getAvailable(type) { return ensureBucket(this.__available, type); } /** * * @param {LightType} type * @returns {Light} * @private */ __create(type) { const light = createLight(type); if (this.__scene !== null) { this.__scene.add(light); } return light; } /** * * @param {Scene} scene Three.js scene */ attach(scene) { if (this.__scene === scene) { // nothing to do return; } if (this.__scene !== null) { this.detach(); } this.__scene = scene; // add all lights this.__traverseAllLight(scene.add, scene); } detach() { const scene = this.__scene; if (scene === null) { // not connected return; } // disconnect all lights this.__traverseAllLight(scene.remove, scene); this.__scene = null; } /** * * @param {function(light:(Light|Object3D)):*} visitor * @param {*} [thisArg] * @private */ __traverseAllLight(visitor, thisArg) { for (const [type, lights] of this.__used) { lights.forEach(visitor, thisArg); } for (const [type, lights] of this.__available) { lights.forEach(visitor, thisArg); } } /** * * @param {LightType} type * @returns {Light} */ obtain(type) { // check for available light of the type const available_lights = this.__getAvailable(type); let light; if (available_lights.length <= 0) { // no available lights, create one light = this.__create(type); } else { // found some available lights, take one light = available_lights.pop(); } // move to "used" bucket const used = this.__getUsed(type); used.push(light); return light; } /** * * @param {Light} light */ release(light) { assert.defined(light, 'light'); assert.notNull(light, 'light'); const type = light2type(light); const used_lights = this.__getUsed(type); const index = used_lights.indexOf(light); if (index === -1) { // light is not in use return false; } // disable light and move it to available disableLight(light); // removed from "used" used_lights.splice(index, 1); const available_lights = this.__getAvailable(type); // add to "available" available_lights.push(light); return true; } /** * * @param {LightType} type * @returns {number} */ countTotalByType(type) { const available = this.__getAvailable(type); const used = this.__getUsed(type); return available.length + used.length; } /** * Pre-allocate up to specified number of lights of a given type. If number of allocated light already matches or exceeds that count, nothing will happen * @param {LightType} type * @param {number} count */ reserve(type, count) { assert.isNonNegativeInteger(count, 'count'); let i = this.countTotalByType(type); for (; i < count; i++) { const light = this.__create(type); const available = this.__getAvailable(type); available.push(light); } } }