UNPKG

playcanvas

Version:

PlayCanvas WebGL game engine

347 lines (344 loc) 9.8 kB
import { LAYERID_WORLD } from '../../../scene/constants.js'; import { GSplatInstance } from '../../../scene/gsplat/gsplat-instance.js'; import { Asset } from '../../asset/asset.js'; import { AssetReference } from '../../asset/asset-reference.js'; import { Component } from '../component.js'; import { GSplatPlacement } from '../../../scene/gsplat-unified/gsplat-placement.js'; class GSplatComponent extends Component { constructor(system, entity){ super(system, entity), this._layers = [ LAYERID_WORLD ], this._instance = null, this._placement = null, this._materialTmp = null, this._highQualitySH = true, this._lodDistances = [ 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60 ], this._splatBudget = 0, this._customAabb = null, this._evtLayersChanged = null, this._evtLayerAdded = null, this._evtLayerRemoved = null, this._castShadows = false, this._unified = false; this._assetReference = new AssetReference('asset', this, system.app.assets, { add: this._onGSplatAssetAdded, load: this._onGSplatAssetLoad, remove: this._onGSplatAssetRemove, unload: this._onGSplatAssetUnload }, this); entity.on('remove', this.onRemoveChild, this); entity.on('removehierarchy', this.onRemoveChild, this); entity.on('insert', this.onInsertChild, this); entity.on('inserthierarchy', this.onInsertChild, this); } set customAabb(value) { this._customAabb = value; this._instance?.meshInstance?.setCustomAabb(this._customAabb); if (this._placement && this._customAabb) { this._placement.aabb = this._customAabb; } } get customAabb() { return this._customAabb; } set instance(value) { this.destroyInstance(); this._instance = value; if (this._instance) { const mi = this._instance.meshInstance; if (!mi.node) { mi.node = this.entity; } mi.castShadow = this._castShadows; mi.setCustomAabb(this._customAabb); if (this.enabled && this.entity.enabled) { this.addToLayers(); } } } get instance() { return this._instance; } set material(value) { if (this.unified) { return; } if (this._instance) { this._instance.material = value; } else { this._materialTmp = value; } } get material() { if (this.unified) { return null; } return this._instance?.material ?? this._materialTmp ?? null; } set highQualitySH(value) { if (value !== this._highQualitySH) { this._highQualitySH = value; this._instance?.setHighQualitySH(value); } } get highQualitySH() { return this._highQualitySH; } set castShadows(value) { if (this._castShadows !== value) { const mi = this.instance?.meshInstance; if (mi) { const layers = this.layers; const scene = this.system.app.scene; if (this._castShadows && !value) { for(let i = 0; i < layers.length; i++){ const layer = scene.layers.getLayerById(this.layers[i]); if (layer) { layer.removeShadowCasters([ mi ]); } } } mi.castShadow = value; if (!this._castShadows && value) { for(let i = 0; i < layers.length; i++){ const layer = scene.layers.getLayerById(layers[i]); if (layer) { layer.addShadowCasters([ mi ]); } } } } this._castShadows = value; } } get castShadows() { return this._castShadows; } set lodDistances(value) { this._lodDistances = Array.isArray(value) ? value.slice() : null; if (this._placement) { this._placement.lodDistances = this._lodDistances; } } get lodDistances() { return this._lodDistances ? this._lodDistances.slice() : null; } set splatBudget(value) { this._splatBudget = value; if (this._placement) { this._placement.splatBudget = this._splatBudget; } } get splatBudget() { return this._splatBudget; } set unified(value) { if (this.enabled && this.entity.enabled) { return; } this._unified = value; this._onGSplatAssetAdded(); } get unified() { return this._unified; } set layers(value) { this.removeFromLayers(); this._layers.length = 0; for(let i = 0; i < value.length; i++){ this._layers[i] = value[i]; } if (!this.enabled || !this.entity.enabled) { return; } this.addToLayers(); } get layers() { return this._layers; } set asset(value) { const id = value instanceof Asset ? value.id : value; if (this._assetReference.id === id) return; if (this._assetReference.asset && this._assetReference.asset.resource) { this._onGSplatAssetRemove(); } this._assetReference.id = id; if (this._assetReference.asset) { this._onGSplatAssetAdded(); } } get asset() { return this._assetReference.id; } destroyInstance() { if (this._placement) { this.removeFromLayers(); this._placement = null; } if (this._instance) { this.removeFromLayers(); this._instance?.destroy(); this._instance = null; } } addToLayers() { if (this._placement) { const layers = this.system.app.scene.layers; for(let i = 0; i < this._layers.length; i++){ layers.getLayerById(this._layers[i])?.addGSplatPlacement(this._placement); } return; } const meshInstance = this.instance?.meshInstance; if (meshInstance) { const layers = this.system.app.scene.layers; for(let i = 0; i < this._layers.length; i++){ layers.getLayerById(this._layers[i])?.addMeshInstances([ meshInstance ]); } } } removeFromLayers() { if (this._placement) { const layers = this.system.app.scene.layers; for(let i = 0; i < this._layers.length; i++){ layers.getLayerById(this._layers[i])?.removeGSplatPlacement(this._placement); } return; } const meshInstance = this.instance?.meshInstance; if (meshInstance) { const layers = this.system.app.scene.layers; for(let i = 0; i < this._layers.length; i++){ layers.getLayerById(this._layers[i])?.removeMeshInstances([ meshInstance ]); } } } onRemoveChild() { this.removeFromLayers(); } onInsertChild() { if (this.enabled && this.entity.enabled) { if (this._instance || this._placement) { this.addToLayers(); } } } onRemove() { this.destroyInstance(); this.asset = null; this._assetReference.id = null; this.entity.off('remove', this.onRemoveChild, this); this.entity.off('insert', this.onInsertChild, this); } onLayersChanged(oldComp, newComp) { this.addToLayers(); oldComp.off('add', this.onLayerAdded, this); oldComp.off('remove', this.onLayerRemoved, this); newComp.on('add', this.onLayerAdded, this); newComp.on('remove', this.onLayerRemoved, this); } onLayerAdded(layer) { const index = this.layers.indexOf(layer.id); if (index < 0) return; if (this._instance) { layer.addMeshInstances(this._instance.meshInstance); } } onLayerRemoved(layer) { const index = this.layers.indexOf(layer.id); if (index < 0) return; if (this._instance) { layer.removeMeshInstances(this._instance.meshInstance); } } onEnable() { const scene = this.system.app.scene; const layers = scene.layers; this._evtLayersChanged = scene.on('set:layers', this.onLayersChanged, this); if (layers) { this._evtLayerAdded = layers.on('add', this.onLayerAdded, this); this._evtLayerRemoved = layers.on('remove', this.onLayerRemoved, this); } if (this._instance || this._placement) { this.addToLayers(); } else if (this.asset) { this._onGSplatAssetAdded(); } } onDisable() { const scene = this.system.app.scene; const layers = scene.layers; this._evtLayersChanged?.off(); this._evtLayersChanged = null; if (layers) { this._evtLayerAdded?.off(); this._evtLayerAdded = null; this._evtLayerRemoved?.off(); this._evtLayerRemoved = null; } this.removeFromLayers(); } hide() { if (this._instance) { this._instance.meshInstance.visible = false; } } show() { if (this._instance) { this._instance.meshInstance.visible = true; } } _onGSplatAssetAdded() { if (!this._assetReference.asset) { return; } if (this._assetReference.asset.resource) { this._onGSplatAssetLoad(); } else if (this.enabled && this.entity.enabled) { this.system.app.assets.load(this._assetReference.asset); } } _onGSplatAssetLoad() { this.destroyInstance(); const asset = this._assetReference.asset; if (this.unified) { this._placement = null; if (asset) { this._placement = new GSplatPlacement(asset.resource, this.entity); this._placement.lodDistances = this._lodDistances; this._placement.splatBudget = this._splatBudget; if (this.enabled && this.entity.enabled) { this.addToLayers(); } } } else { if (asset) { this.instance = new GSplatInstance(asset.resource, { material: this._materialTmp, highQualitySH: this._highQualitySH }); this._materialTmp = null; } } if (asset) { this.customAabb = asset.resource.aabb.clone(); } } _onGSplatAssetUnload() { this.destroyInstance(); } _onGSplatAssetRemove() { this._onGSplatAssetUnload(); } } export { GSplatComponent };