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.

205 lines • 9.12 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; import { EquirectangularReflectionMapping, LinearSRGBColorSpace, MeshBasicMaterial, Texture, Vector3 } from "three"; import { isDevEnvironment, showBalloonWarning } from "../engine/debug/index.js"; import { serializable } from "../engine/engine_serialization.js"; import { getParam } from "../engine/engine_utils.js"; import { BoxHelperComponent } from "./BoxHelperComponent.js"; import { Behaviour } from "./Component.js"; export const debug = getParam("debugreflectionprobe"); const disable = getParam("noreflectionprobe"); const $reflectionProbeKey = Symbol("reflectionProbeKey"); const $originalMaterial = Symbol("original material"); /** * @category Rendering * @group Components */ export class ReflectionProbe extends Behaviour { static _probes = new Map(); static isUsingReflectionProbe(material) { return !!(material[$reflectionProbeKey] || material[$originalMaterial]?.[$reflectionProbeKey]); } static get(object, context, isAnchor, anchor) { if (!object || object.isObject3D !== true) return null; if (disable) return null; const probes = ReflectionProbe._probes.get(context); if (probes) { for (const probe of probes) { if (!probe.__didAwake) probe.__internalAwake(); if (probe.activeAndEnabled) { if (anchor) { // test if anchor is reflection probe object if (probe.gameObject === anchor) { return probe; } } // TODO not supported right now, as we'd have to pass the ReflectionProbe scale through as well. else if (probe.isInBox(object)) { if (debug) console.log("Found reflection probe", object.name, probe.name); return probe; } } } } if (debug) console.debug("Did not find reflection probe", object.name, isAnchor, object); return null; } _texture; // @serializable(Texture) set texture(tex) { if (tex && !(tex instanceof Texture)) { console.error("ReflectionProbe.texture must be a Texture", tex); return; } this._texture = tex; if (tex) { tex.mapping = EquirectangularReflectionMapping; tex.colorSpace = LinearSRGBColorSpace; tex.needsUpdate = true; } } get texture() { return this._texture; } center; size; _boxHelper; isInBox(obj) { return this._boxHelper?.isInBox(obj); } constructor() { super(); if (!ReflectionProbe._probes.has(this.context)) { ReflectionProbe._probes.set(this.context, []); } ReflectionProbe._probes.get(this.context)?.push(this); } awake() { this._boxHelper = this.gameObject.addComponent(BoxHelperComponent); this._boxHelper.updateBox(true); if (debug) this._boxHelper.showHelper(0x555500, true); if (this._texture) { this._texture.mapping = EquirectangularReflectionMapping; this._texture.colorSpace = LinearSRGBColorSpace; this._texture.needsUpdate = true; } } start() { if (!this._texture && isDevEnvironment()) { console.warn(`[ReflectionProbe] Missing texture. Please assign a custom cubemap texture. To use reflection probes assign them to your renderer's "anchor" property.`); showBalloonWarning("ReflectionProbe configuration hint: See browser console for details"); } } onDestroy() { const probes = ReflectionProbe._probes.get(this.context); if (probes) { const index = probes.indexOf(this); if (index >= 0) { probes.splice(index, 1); } } } // when objects are rendered and they share material // and some need reflection probe and some don't // we need to make sure we don't override the material but use a copy static _rendererMaterialsCache = new Map(); onSet(_rend) { if (disable) return; if (!this.enabled) return; if (_rend.sharedMaterials?.length <= 0) return; if (!this.texture) return; let rendererCache = ReflectionProbe._rendererMaterialsCache.get(_rend); if (!rendererCache) { rendererCache = []; ReflectionProbe._rendererMaterialsCache.set(_rend, rendererCache); } // TODO: dont clone material for every renderer that uses reflection probes, we can do it once per material when they use the same reflection texture // need to make sure materials are not shared when using reflection probes // otherwise some renderers outside of the probe will be affected or vice versa for (let i = 0; i < _rend.sharedMaterials.length; i++) { const material = _rend.sharedMaterials[i]; if (!material) { continue; } if (material["envMap"] === undefined) { continue; } if (material instanceof MeshBasicMaterial) { continue; } let cached = rendererCache[i]; // make sure we have the currently assigned material cached (and an up to date clone of that) // TODO: this is causing problems with progressive textures sometimes (depending on the order) when the version changes and we re-create materials over and over. We might want to just set the material envmap instead of making a clone const isCachedInstance = material === cached?.copy; const hasChanged = !cached || cached.material.uuid !== material.uuid || cached.copy.version !== material.version; if (!isCachedInstance && hasChanged) { if (debug) { let reason = ""; if (!cached) reason = "not cached"; else if (cached.material !== material) reason = "reference changed; cached instance?: " + isCachedInstance; else if (cached.copy.version !== material.version) reason = "version changed"; console.warn("Cloning material", material.name, material.version, "Reason:", reason, "\n", material.uuid, "\n", cached?.copy.uuid, "\n", _rend.name); } const clone = material.clone(); clone.version = material.version; if (cached) { cached.copy = clone; cached.material = material; } else { cached = { material: material, copy: clone }; rendererCache.push(cached); } clone[$reflectionProbeKey] = this; clone[$originalMaterial] = material; if (debug) console.log("Set reflection", _rend.name, _rend.guid); } // See NE-4771 and NE-4856 if (cached && cached.copy) { cached.copy.onBeforeCompile = material.onBeforeCompile; } /** this is the material that we copied and that has the reflection probe */ const copy = cached?.copy; // make sure the reflection probe is assigned copy["envMap"] = this.texture; _rend.sharedMaterials[i] = copy; } } onUnset(_rend) { const rendererCache = ReflectionProbe._rendererMaterialsCache.get(_rend); if (rendererCache) { for (let i = 0; i < rendererCache.length; i++) { const cached = rendererCache[i]; _rend.sharedMaterials[i] = cached.material; } } } } __decorate([ serializable(Vector3) ], ReflectionProbe.prototype, "center", void 0); __decorate([ serializable(Vector3) ], ReflectionProbe.prototype, "size", void 0); //# sourceMappingURL=ReflectionProbe.js.map