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.

187 lines • 7.46 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 { LOD as ThreeLOD, Object3D } from "three"; import { serializable } from "../engine/engine_serialization_decorator.js"; import { getParam } from "../engine/engine_utils.js"; import { Behaviour } from "./Component.js"; import { Renderer } from "./Renderer.js"; const debug = getParam("debuglods"); const noLods = getParam("nolods"); /** * Defines how LOD levels transition between each other */ var LODFadeMode; (function (LODFadeMode) { /** Instant switch between LOD levels */ LODFadeMode[LODFadeMode["None"] = 0] = "None"; /** Smooth cross-fade transition between levels */ LODFadeMode[LODFadeMode["CrossFade"] = 1] = "CrossFade"; /** SpeedTree-style blending for vegetation */ LODFadeMode[LODFadeMode["SpeedTree"] = 2] = "SpeedTree"; })(LODFadeMode || (LODFadeMode = {})); /** * Defines a single LOD level with its transition distance and associated renderers. * Used by {@link LODGroup} to configure level of detail switching. */ export class LODModel { /** Screen height ratio (0-1) at which this LOD becomes active */ screenRelativeTransitionHeight; /** Distance from camera at which this LOD becomes active */ distance; /** Renderers to show at this LOD level */ renderers; } __decorate([ serializable() ], LODModel.prototype, "screenRelativeTransitionHeight", void 0); __decorate([ serializable() ], LODModel.prototype, "distance", void 0); __decorate([ serializable(Renderer) ], LODModel.prototype, "renderers", void 0); class LOD { model; get renderers() { return this.model.renderers; } constructor(model) { this.model = model; } } /** * LODGroup manages multiple levels of detail for optimized rendering. * Objects switch between different detail levels based on distance from camera. * * LOD levels are defined in {@link LODModel} objects, each specifying: * - The distance at which that level becomes active * - The {@link Renderer} components to show at that level * * This is useful for performance optimization - showing high-detail models up close * and lower-detail versions at distance where the difference isn't visible. * * **Progressive Loading:** * For automatic texture/mesh LOD streaming, see the `@needle-tools/gltf-progressive` package * which provides progressive loading capabilities independent of this component. * * **Debug options:** * - `?debuglods` - Log LOD switching information * - `?nolods` - Disable LOD system entirely * * @summary Level of Detail Group for optimizing rendering * @category Rendering * @group Components * @see {@link LODModel} for configuring individual LOD levels * @see {@link Renderer} for the renderers controlled by LOD * @see {@link LODsManager} for programmatic control of progressive LODs * @link https://npmjs.com/package/@needle-tools/gltf-progressive */ export class LODGroup extends Behaviour { /** Array of LOD level configurations */ lodModels = []; _lods = []; _settings = []; // https://threejs.org/docs/#api/en/objects/LOD _lodsHandler; start() { if (debug) console.log("LODGROUP", this.name, this.lodModels, this); if (noLods) return; if (this._lodsHandler) return; if (!this.gameObject) return; if (this.lodModels && Array.isArray(this.lodModels)) { const renderers = []; for (const model of this.lodModels) { const lod = new LOD(model); this._lods.push(lod); for (const rend of lod.renderers) { if (!renderers.includes(rend)) renderers.push(rend); } } this._lodsHandler = new Array(); for (let i = 0; i < renderers.length; i++) { const handler = new ThreeLOD(); this._lodsHandler.push(handler); this.gameObject.add(handler); } const empty = new Object3D(); empty.name = "Cull " + this.name; for (let i = 0; i < renderers.length; i++) { const rend = renderers[i]; const handler = this._lodsHandler[i]; const obj = rend.gameObject; if (debug) console.log(i, obj.name); for (const lod of this._lods) { const dist = lod.model.distance; // get object to be lodded, it can be empty let object = null; if (lod.renderers.includes(rend)) { object = obj; } else { object = empty; } if (object.type === "Group") { console.warn(`LODGroup ${this.name}: Group or MultiMaterial object's are not supported as LOD object: ${object.name}`); continue; } if (debug) console.log("LEVEL", object.name, dist); handler.autoUpdate = false; this.onAddLodLevel(handler, object, lod.model.distance); } } } } onAfterRender() { if (!this.gameObject) return; if (!this._lodsHandler) return; const cam = this.context.mainCamera; if (!cam) return; for (const h of this._lodsHandler) { h.update(cam); const levelIndex = h.getCurrentLevel(); const level = h.levels[levelIndex]; h.layers.mask = level.object.layers.mask; } } onAddLodLevel(lod, obj, dist) { if (obj === this.gameObject) { console.warn("LODGroup component must be on parent object and not mesh directly at the moment", obj.name, obj); return; } lod.addLevel(obj, dist * this._distanceFactor, .01); const setting = { lod: lod, levelIndex: lod.levels.length - 1, distance: dist }; this._settings.push(setting); } _distanceFactor = 1; /** * Adjusts all LOD transition distances by a multiplier. * Values > 1 push LOD transitions further away (higher quality at distance). * Values < 1 bring transitions closer (better performance). * @param factor Multiplier to apply to all LOD distances */ distanceFactor(factor) { if (factor === this._distanceFactor) return; this._distanceFactor = factor; for (const setting of this._settings) { const level = setting.lod.levels[setting.levelIndex]; level.distance = setting.distance * factor; } } } __decorate([ serializable(LODModel) ], LODGroup.prototype, "lodModels", void 0); //# sourceMappingURL=LODGroup.js.map