UNPKG

@needle-tools/engine

Version:

Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.

158 lines โ€ข 7.56 kB
import { AmbientLight, Color, HemisphereLight, Object3D } from "three"; import { Behaviour, GameObject } from "../../engine-components/Component.js"; import { ContextEvent, ContextRegistry } from "../engine_context_registry.js"; import { Mathf } from "../engine_math.js"; import { AmbientMode, DefaultReflectionMode } from "../engine_scenelighting.js"; import { getParam } from "../engine_utils.js"; import { LightmapType } from "./NEEDLE_lightmaps.js"; export const EXTENSION_NAME = "NEEDLE_lighting_settings"; const debug = getParam("debugenvlight"); export class NEEDLE_lighting_settings { get name() { return EXTENSION_NAME; } parser; sourceId; context; constructor(parser, sourceId, context) { this.parser = parser; this.sourceId = sourceId; this.context = context; } afterRoot(_result) { const extensions = this.parser.json.extensions; if (extensions) { const ext = extensions[EXTENSION_NAME]; if (ext) { if (debug) console.log("Loaded \"" + this.name + "\", src: \"" + this.sourceId + "\"", ext); let settings = undefined; // If the result scene has only one child we add the LightingSettingsComponent to that child if (_result.scene.children.length === 1) { const obj = _result.scene.children[0]; // add a component to the root of the scene settings = GameObject.addComponent(obj, SceneLightSettings, {}, { callAwake: false }); } // if the scene already has multiple children we add it as a new object else { const lightSettings = new Object3D(); lightSettings.name = "LightSettings " + this.sourceId; _result.scene.add(lightSettings); settings = GameObject.addComponent(lightSettings, SceneLightSettings, {}, { callAwake: false }); } settings.sourceId = this.sourceId; settings.ambientIntensity = ext.ambientIntensity; settings.ambientLight = new Color().fromArray(ext.ambientLight); if (Array.isArray(ext.ambientTrilight)) settings.ambientTrilight = ext.ambientTrilight.map(c => new Color().fromArray(c)); settings.ambientMode = ext.ambientMode; settings.environmentReflectionSource = ext.environmentReflectionSource; } } return null; } } ContextRegistry.registerCallback(ContextEvent.ContextCreated, e => { const ctx = e.context; const lightingSettings = GameObject.findObjectOfType(SceneLightSettings, ctx); if (lightingSettings?.sourceId) lightingSettings.enabled = true; }); // exists once per gltf scene root (if it contains reflection) // when enabled it does currently automatically set the reflection // this might not be desireable export class SceneLightSettings extends Behaviour { ambientMode = AmbientMode.Skybox; ambientLight; ambientTrilight; ambientIntensity = 1; environmentReflectionSource = DefaultReflectionMode.Skybox; _hasReflection = false; _ambientLightObj; _hemisphereLightObj; awake() { if (this.sourceId) { const type = this.environmentReflectionSource === DefaultReflectionMode.Skybox ? LightmapType.Skybox : LightmapType.Reflection; const tex = this.context.lightmaps.tryGet(this.sourceId, type, 0); this._hasReflection = tex !== null && tex !== undefined; if (tex) this.context.sceneLighting.internalRegisterReflection(this.sourceId, tex); } this.enabled = false; this.context.sceneLighting.internalRegisterSceneLightSettings(this); if (debug) { window.addEventListener("keydown", evt => { if (this.destroyed) return; switch (evt.key) { case "l": this.enabled = !this.enabled; break; } }); } // make sure the component is in the end of the list // (e.g. if we have an animation on the first component from an instance and add the scenelightingcomponent the animation binding will break) const comps = this.gameObject.userData?.components; if (comps) { const index = comps.indexOf(this); comps.splice(index, 1); comps.push(this); } } onDestroy() { this.context.sceneLighting.internalUnregisterSceneLightSettings(this); } calculateIntensityFactor(col) { const intensity = Math.max(col.r, col.g, col.b); // * 0.2126 + col.g * 0.7152 + col.b * 0.0722; const factor = 2.2 * Mathf.lerp(0, 1.33, intensity); // scale based on intensity return factor; } onEnable() { if (debug) console.warn("๐Ÿ’ก๐ŸŸก >>> Enable lighting", this.sourceId, this.enabled, this); if (this.ambientMode == AmbientMode.Flat) { if (this.ambientLight && !this._ambientLightObj) { // TODO: currently ambient intensity is always exported as 1? The exported values are not correct in threejs // the following calculation is a workaround to get the correct intensity const factor = this.calculateIntensityFactor(this.ambientLight); this._ambientLightObj = new AmbientLight(this.ambientLight, this.ambientIntensity * factor); if (debug) console.log("Created ambient light", this.sourceId, this._ambientLightObj, this.ambientIntensity, factor); } if (this._ambientLightObj) { this.gameObject.add(this._ambientLightObj); } } else if (this.ambientMode === AmbientMode.Trilight) { if (this.ambientTrilight) { const ground = this.ambientTrilight[0]; const sky = this.ambientTrilight[this.ambientTrilight.length - 1]; const factor = this.calculateIntensityFactor(sky); this._hemisphereLightObj = new HemisphereLight(sky, ground, this.ambientIntensity * factor); this.gameObject.add(this._hemisphereLightObj); if (debug) console.log("Created hemisphere ambient light", this.sourceId, this._hemisphereLightObj, this.ambientIntensity, factor); } } else { if (this._ambientLightObj) this._ambientLightObj.removeFromParent(); if (this._hemisphereLightObj) this._hemisphereLightObj.removeFromParent(); } if (this.sourceId) this.context.sceneLighting.internalEnableReflection(this.sourceId); } onDisable() { if (debug) console.warn("๐Ÿ’กโšซ <<< Disable lighting:", this.sourceId, this); if (this._ambientLightObj) this._ambientLightObj.removeFromParent(); if (this._hemisphereLightObj) this._hemisphereLightObj.removeFromParent(); if (this.sourceId) this.context.sceneLighting.internalDisableReflection(this.sourceId); } } //# sourceMappingURL=NEEDLE_lighting_settings.js.map