UNPKG

playcanvas

Version:

PlayCanvas WebGL game engine

1,009 lines (1,006 loc) 34.5 kB
import { math } from '../../../core/math/math.js'; import { Color } from '../../../core/math/color.js'; import { Vec2 } from '../../../core/math/vec2.js'; import { Vec3 } from '../../../core/math/vec3.js'; import { Vec4 } from '../../../core/math/vec4.js'; import { TYPE_FLOAT32, SEMANTIC_POSITION, SEMANTIC_NORMAL, SEMANTIC_TEXCOORD0, PRIMITIVE_TRISTRIP, STENCILOP_DECREMENT, FUNC_EQUAL } from '../../../platform/graphics/constants.js'; import { VertexBuffer } from '../../../platform/graphics/vertex-buffer.js'; import { VertexFormat } from '../../../platform/graphics/vertex-format.js'; import { DeviceCache } from '../../../platform/graphics/device-cache.js'; import { SPRITE_RENDERMODE_SLICED, SPRITE_RENDERMODE_TILED, LAYER_HUD, LAYER_WORLD, SPRITE_RENDERMODE_SIMPLE } from '../../../scene/constants.js'; import { GraphNode } from '../../../scene/graph-node.js'; import { Mesh } from '../../../scene/mesh.js'; import { MeshInstance } from '../../../scene/mesh-instance.js'; import { Model } from '../../../scene/model.js'; import { StencilParameters } from '../../../platform/graphics/stencil-parameters.js'; import { FITMODE_STRETCH, FITMODE_CONTAIN, FITMODE_COVER } from './constants.js'; import { Asset } from '../../asset/asset.js'; const _tempColor = new Color(); const _vertexFormatDeviceCache = new DeviceCache(); class ImageRenderable { constructor(entity, mesh, material){ this._entity = entity; this._element = entity.element; this.model = new Model(); this.node = new GraphNode(); this.model.graph = this.node; this.mesh = mesh; this.meshInstance = new MeshInstance(this.mesh, material, this.node); this.meshInstance.name = `ImageElement: ${entity.name}`; this.meshInstance.castShadow = false; this.meshInstance.receiveShadow = false; this._meshDirty = false; this.model.meshInstances.push(this.meshInstance); this._entity.addChild(this.model.graph); this.model._entity = this._entity; this.unmaskMeshInstance = null; } destroy() { this.setMaterial(null); this._element.removeModelFromLayers(this.model); this.model.destroy(); this.model = null; this.node = null; this.mesh = null; this.meshInstance?.destroy(); this.meshInstance = null; this.unmaskMeshInstance?.destroy(); this.unmaskMeshInstance = null; this._entity = null; this._element = null; } setMesh(mesh) { if (!this.meshInstance) return; this.mesh = mesh; this.meshInstance.mesh = mesh; this.meshInstance.visible = !!mesh; if (this.unmaskMeshInstance) { this.unmaskMeshInstance.mesh = mesh; } this.forceUpdateAabb(); } setMask(mask) { if (!this.meshInstance) return; if (this._entity.enabled && this._element.enabled) { this._element.removeModelFromLayers(this.model); } if (mask) { this.unmaskMeshInstance = new MeshInstance(this.mesh, this.meshInstance.material, this.node); this.unmaskMeshInstance.name = `Unmask: ${this._entity.name}`; this.unmaskMeshInstance.castShadow = false; this.unmaskMeshInstance.receiveShadow = false; this.unmaskMeshInstance.pick = false; this.model.meshInstances.push(this.unmaskMeshInstance); for(const name in this.meshInstance.parameters){ this.unmaskMeshInstance.setParameter(name, this.meshInstance.parameters[name].data); } } else { const idx = this.model.meshInstances.indexOf(this.unmaskMeshInstance); if (idx >= 0) { this.model.meshInstances.splice(idx, 1); } } if (this._entity.enabled && this._element.enabled) { this._element.addModelToLayers(this.model); } if (!mask) { this.unmaskMeshInstance?.destroy(); this.unmaskMeshInstance = null; } } setMaterial(material) { if (!this.meshInstance) return; this.meshInstance.material = material; if (this.unmaskMeshInstance) { this.unmaskMeshInstance.material = material; } } setParameter(name, value) { if (!this.meshInstance) return; this.meshInstance.setParameter(name, value); if (this.unmaskMeshInstance) { this.unmaskMeshInstance.setParameter(name, value); } } deleteParameter(name) { if (!this.meshInstance) return; this.meshInstance.deleteParameter(name); if (this.unmaskMeshInstance) { this.unmaskMeshInstance.deleteParameter(name); } } setUnmaskDrawOrder() { if (!this.meshInstance) return; const getLastChild = function(e) { let last; const c = e.children; const l = c.length; if (l) { for(let i = 0; i < l; i++){ if (c[i].element) { last = c[i]; } } if (!last) return null; const child = getLastChild(last); if (child) { return child; } return last; } return null; }; if (this.unmaskMeshInstance) { const lastChild = getLastChild(this._entity); if (lastChild && lastChild.element) { this.unmaskMeshInstance.drawOrder = lastChild.element.drawOrder + lastChild.element.getMaskOffset(); } else { this.unmaskMeshInstance.drawOrder = this.meshInstance.drawOrder + this._element.getMaskOffset(); } } } setDrawOrder(drawOrder) { if (!this.meshInstance) { return; } this.meshInstance.drawOrder = drawOrder; } setCull(cull) { if (!this.meshInstance) return; const element = this._element; let visibleFn = null; if (cull && element._isScreenSpace()) { visibleFn = function(camera) { return element.isVisibleForCamera(camera); }; } this.meshInstance.cull = cull; this.meshInstance.isVisibleFunc = visibleFn; if (this.unmaskMeshInstance) { this.unmaskMeshInstance.cull = cull; this.unmaskMeshInstance.isVisibleFunc = visibleFn; } } setScreenSpace(screenSpace) { if (!this.meshInstance) return; this.meshInstance.screenSpace = screenSpace; if (this.unmaskMeshInstance) { this.unmaskMeshInstance.screenSpace = screenSpace; } } setLayer(layer) { if (!this.meshInstance) return; this.meshInstance.layer = layer; if (this.unmaskMeshInstance) { this.unmaskMeshInstance.layer = layer; } } forceUpdateAabb(mask) { if (!this.meshInstance) return; this.meshInstance._aabbVer = -1; if (this.unmaskMeshInstance) { this.unmaskMeshInstance._aabbVer = -1; } } setAabbFunc(fn) { if (!this.meshInstance) return; this.meshInstance._updateAabbFunc = fn; if (this.unmaskMeshInstance) { this.unmaskMeshInstance._updateAabbFunc = fn; } } } class ImageElement { constructor(element){ this._evtSetMeshes = null; this._element = element; this._entity = element.entity; this._system = element.system; this._textureAsset = null; this._texture = null; this._materialAsset = null; this._material = null; this._spriteAsset = null; this._sprite = null; this._spriteFrame = 0; this._pixelsPerUnit = null; this._targetAspectRatio = -1; this._rect = new Vec4(0, 0, 1, 1); this._mask = false; this._maskRef = 0; this._outerScale = new Vec2(); this._outerScaleUniform = new Float32Array(2); this._innerOffset = new Vec4(); this._innerOffsetUniform = new Float32Array(4); this._atlasRect = new Vec4(); this._atlasRectUniform = new Float32Array(4); this._defaultMesh = this._createMesh(); this._renderable = new ImageRenderable(this._entity, this._defaultMesh, this._material); this._color = new Color(1, 1, 1, 1); this._colorUniform = new Float32Array([ 1, 1, 1 ]); this._updateRenderableEmissive(); this._renderable.setParameter('material_opacity', 1); this._updateAabbFunc = this._updateAabb.bind(this); this._onScreenChange(this._element.screen); this._element.on('resize', this._onParentResizeOrPivotChange, this); this._element.on('set:pivot', this._onParentResizeOrPivotChange, this); this._element.on('screen:set:screenspace', this._onScreenSpaceChange, this); this._element.on('set:screen', this._onScreenChange, this); this._element.on('set:draworder', this._onDrawOrderChange, this); this._element.on('screen:set:resolution', this._onResolutionChange, this); } destroy() { this.textureAsset = null; this.spriteAsset = null; this.materialAsset = null; this._renderable.setMesh(this._defaultMesh); this._renderable.destroy(); this._defaultMesh = null; this._element.off('resize', this._onParentResizeOrPivotChange, this); this._element.off('set:pivot', this._onParentResizeOrPivotChange, this); this._element.off('screen:set:screenspace', this._onScreenSpaceChange, this); this._element.off('set:screen', this._onScreenChange, this); this._element.off('set:draworder', this._onDrawOrderChange, this); this._element.off('screen:set:resolution', this._onResolutionChange, this); } _onResolutionChange(res) {} _onParentResizeOrPivotChange() { if (this._renderable.mesh) { this._updateMesh(this._renderable.mesh); } } _onScreenSpaceChange(value) { this._updateMaterial(value); } _onScreenChange(screen, previous) { if (screen) { this._updateMaterial(screen.screen.screenSpace); } else { this._updateMaterial(false); } } _onDrawOrderChange(order) { this._renderable.setDrawOrder(order); if (this.mask && this._element.screen) { this._element.screen.screen.once('syncdraworder', function() { this._renderable.setUnmaskDrawOrder(); }, this); } } _hasUserMaterial() { return !!this._materialAsset || !!this._material && this._system.defaultImageMaterials.indexOf(this._material) === -1; } _use9Slicing() { return this.sprite && (this.sprite.renderMode === SPRITE_RENDERMODE_SLICED || this.sprite.renderMode === SPRITE_RENDERMODE_TILED); } _updateMaterial(screenSpace) { const mask = !!this._mask; const nineSliced = !!(this.sprite && this.sprite.renderMode === SPRITE_RENDERMODE_SLICED); const nineTiled = !!(this.sprite && this.sprite.renderMode === SPRITE_RENDERMODE_TILED); if (!this._hasUserMaterial()) { this._material = this._system.getImageElementMaterial(screenSpace, mask, nineSliced, nineTiled); } if (this._renderable) { this._renderable.setCull(!this._element._isScreenSpace() || this._element._isScreenCulled()); this._renderable.setMaterial(this._material); this._renderable.setScreenSpace(screenSpace); this._renderable.setLayer(screenSpace ? LAYER_HUD : LAYER_WORLD); } } _createMesh() { const element = this._element; const w = element.calculatedWidth; const h = element.calculatedHeight; const r = this._rect; const device = this._system.app.graphicsDevice; const vertexData = new Float32Array([ w, 0, 0, 0, 0, 1, r.x + r.z, 1.0 - r.y, w, h, 0, 0, 0, 1, r.x + r.z, 1.0 - (r.y + r.w), 0, 0, 0, 0, 0, 1, r.x, 1.0 - r.y, 0, h, 0, 0, 0, 1, r.x, 1.0 - (r.y + r.w) ]); const vertexFormat = _vertexFormatDeviceCache.get(device, ()=>{ return new VertexFormat(device, [ { semantic: SEMANTIC_POSITION, components: 3, type: TYPE_FLOAT32 }, { semantic: SEMANTIC_NORMAL, components: 3, type: TYPE_FLOAT32 }, { semantic: SEMANTIC_TEXCOORD0, components: 2, type: TYPE_FLOAT32 } ]); }); const vertexBuffer = new VertexBuffer(device, vertexFormat, 4, { data: vertexData.buffer }); const mesh = new Mesh(device); mesh.vertexBuffer = vertexBuffer; mesh.primitive[0].type = PRIMITIVE_TRISTRIP; mesh.primitive[0].base = 0; mesh.primitive[0].count = 4; mesh.primitive[0].indexed = false; mesh.aabb.setMinMax(Vec3.ZERO, new Vec3(w, h, 0)); this._updateMesh(mesh); return mesh; } _updateMesh(mesh) { const element = this._element; let w = element.calculatedWidth; let h = element.calculatedHeight; if (element.fitMode !== FITMODE_STRETCH && this._targetAspectRatio > 0) { const actualRatio = element.calculatedWidth / element.calculatedHeight; if (element.fitMode === FITMODE_CONTAIN && actualRatio > this._targetAspectRatio || element.fitMode === FITMODE_COVER && actualRatio < this._targetAspectRatio) { w = element.calculatedHeight * this._targetAspectRatio; } else { h = element.calculatedWidth / this._targetAspectRatio; } } const screenSpace = element._isScreenSpace(); this._updateMaterial(screenSpace); if (this._renderable) this._renderable.forceUpdateAabb(); if (this.sprite && (this.sprite.renderMode === SPRITE_RENDERMODE_SLICED || this.sprite.renderMode === SPRITE_RENDERMODE_TILED)) { const frameData = this._sprite.atlas.frames[this._sprite.frameKeys[this._spriteFrame]]; const borderWidthScale = 2 / frameData.rect.z; const borderHeightScale = 2 / frameData.rect.w; this._innerOffset.set(frameData.border.x * borderWidthScale, frameData.border.y * borderHeightScale, frameData.border.z * borderWidthScale, frameData.border.w * borderHeightScale); const tex = this.sprite.atlas.texture; this._atlasRect.set(frameData.rect.x / tex.width, frameData.rect.y / tex.height, frameData.rect.z / tex.width, frameData.rect.w / tex.height); const ppu = this._pixelsPerUnit !== null ? this._pixelsPerUnit : this.sprite.pixelsPerUnit; const scaleMulX = frameData.rect.z / ppu; const scaleMulY = frameData.rect.w / ppu; this._outerScale.set(Math.max(w, this._innerOffset.x * scaleMulX), Math.max(h, this._innerOffset.y * scaleMulY)); let scaleX = scaleMulX; let scaleY = scaleMulY; this._outerScale.x /= scaleMulX; this._outerScale.y /= scaleMulY; scaleX *= math.clamp(w / (this._innerOffset.x * scaleMulX), 0.0001, 1); scaleY *= math.clamp(h / (this._innerOffset.y * scaleMulY), 0.0001, 1); if (this._renderable) { this._innerOffsetUniform[0] = this._innerOffset.x; this._innerOffsetUniform[1] = this._innerOffset.y; this._innerOffsetUniform[2] = this._innerOffset.z; this._innerOffsetUniform[3] = this._innerOffset.w; this._renderable.setParameter('innerOffset', this._innerOffsetUniform); this._atlasRectUniform[0] = this._atlasRect.x; this._atlasRectUniform[1] = this._atlasRect.y; this._atlasRectUniform[2] = this._atlasRect.z; this._atlasRectUniform[3] = this._atlasRect.w; this._renderable.setParameter('atlasRect', this._atlasRectUniform); this._outerScaleUniform[0] = this._outerScale.x; this._outerScaleUniform[1] = this._outerScale.y; this._renderable.setParameter('outerScale', this._outerScaleUniform); this._renderable.setAabbFunc(this._updateAabbFunc); this._renderable.node.setLocalScale(scaleX, scaleY, 1); this._renderable.node.setLocalPosition((0.5 - element.pivot.x) * w, (0.5 - element.pivot.y) * h, 0); } } else { const vb = mesh.vertexBuffer; const vertexDataF32 = new Float32Array(vb.lock()); const hp = element.pivot.x; const vp = element.pivot.y; vertexDataF32[0] = w - hp * w; vertexDataF32[1] = 0 - vp * h; vertexDataF32[8] = w - hp * w; vertexDataF32[9] = h - vp * h; vertexDataF32[16] = 0 - hp * w; vertexDataF32[17] = 0 - vp * h; vertexDataF32[24] = 0 - hp * w; vertexDataF32[25] = h - vp * h; let atlasTextureWidth = 1; let atlasTextureHeight = 1; let rect = this._rect; if (this._sprite && this._sprite.frameKeys[this._spriteFrame] && this._sprite.atlas) { const frame = this._sprite.atlas.frames[this._sprite.frameKeys[this._spriteFrame]]; if (frame) { rect = frame.rect; atlasTextureWidth = this._sprite.atlas.texture.width; atlasTextureHeight = this._sprite.atlas.texture.height; } } vertexDataF32[6] = (rect.x + rect.z) / atlasTextureWidth; vertexDataF32[7] = 1.0 - rect.y / atlasTextureHeight; vertexDataF32[14] = (rect.x + rect.z) / atlasTextureWidth; vertexDataF32[15] = 1.0 - (rect.y + rect.w) / atlasTextureHeight; vertexDataF32[22] = rect.x / atlasTextureWidth; vertexDataF32[23] = 1.0 - rect.y / atlasTextureHeight; vertexDataF32[30] = rect.x / atlasTextureWidth; vertexDataF32[31] = 1.0 - (rect.y + rect.w) / atlasTextureHeight; vb.unlock(); const min = new Vec3(0 - hp * w, 0 - vp * h, 0); const max = new Vec3(w - hp * w, h - vp * h, 0); mesh.aabb.setMinMax(min, max); if (this._renderable) { this._renderable.node.setLocalScale(1, 1, 1); this._renderable.node.setLocalPosition(0, 0, 0); this._renderable.setAabbFunc(null); } } this._meshDirty = false; } _updateSprite() { let nineSlice = false; let mesh = null; this._targetAspectRatio = -1; if (this._sprite && this._sprite.atlas) { mesh = this._sprite.meshes[this.spriteFrame]; nineSlice = this._sprite.renderMode === SPRITE_RENDERMODE_SLICED || this._sprite.renderMode === SPRITE_RENDERMODE_TILED; const frameData = this._sprite.atlas.frames[this._sprite.frameKeys[this._spriteFrame]]; if (frameData?.rect.w > 0) { this._targetAspectRatio = frameData.rect.z / frameData.rect.w; } } this.mesh = nineSlice ? mesh : this._defaultMesh; this.refreshMesh(); } refreshMesh() { if (this.mesh) { if (!this._element._beingInitialized) { this._updateMesh(this.mesh); } else { this._meshDirty = true; } } } _updateAabb(aabb) { aabb.center.set(0, 0, 0); aabb.halfExtents.set(this._outerScale.x * 0.5, this._outerScale.y * 0.5, 0.001); aabb.setFromTransformedAabb(aabb, this._renderable.node.getWorldTransform()); return aabb; } _toggleMask() { this._element._dirtifyMask(); const screenSpace = this._element._isScreenSpace(); this._updateMaterial(screenSpace); this._renderable.setMask(!!this._mask); } _onMaterialLoad(asset) { this.material = asset.resource; } _onMaterialAdded(asset) { this._system.app.assets.off(`add:${asset.id}`, this._onMaterialAdded, this); if (this._materialAsset === asset.id) { this._bindMaterialAsset(asset); } } _bindMaterialAsset(asset) { if (!this._entity.enabled) return; asset.on('load', this._onMaterialLoad, this); asset.on('change', this._onMaterialChange, this); asset.on('remove', this._onMaterialRemove, this); if (asset.resource) { this._onMaterialLoad(asset); } else { this._system.app.assets.load(asset); } } _unbindMaterialAsset(asset) { asset.off('load', this._onMaterialLoad, this); asset.off('change', this._onMaterialChange, this); asset.off('remove', this._onMaterialRemove, this); } _onMaterialChange() {} _onMaterialRemove() {} _onTextureAdded(asset) { this._system.app.assets.off(`add:${asset.id}`, this._onTextureAdded, this); if (this._textureAsset === asset.id) { this._bindTextureAsset(asset); } } _bindTextureAsset(asset) { if (!this._entity.enabled) return; asset.on('load', this._onTextureLoad, this); asset.on('change', this._onTextureChange, this); asset.on('remove', this._onTextureRemove, this); if (asset.resource) { this._onTextureLoad(asset); } else { this._system.app.assets.load(asset); } } _unbindTextureAsset(asset) { asset.off('load', this._onTextureLoad, this); asset.off('change', this._onTextureChange, this); asset.off('remove', this._onTextureRemove, this); } _onTextureLoad(asset) { this.texture = asset.resource; } _onTextureChange(asset) {} _onTextureRemove(asset) {} _onSpriteAssetAdded(asset) { this._system.app.assets.off(`add:${asset.id}`, this._onSpriteAssetAdded, this); if (this._spriteAsset === asset.id) { this._bindSpriteAsset(asset); } } _bindSpriteAsset(asset) { if (!this._entity.enabled) return; asset.on('load', this._onSpriteAssetLoad, this); asset.on('change', this._onSpriteAssetChange, this); asset.on('remove', this._onSpriteAssetRemove, this); if (asset.resource) { this._onSpriteAssetLoad(asset); } else { this._system.app.assets.load(asset); } } _unbindSpriteAsset(asset) { asset.off('load', this._onSpriteAssetLoad, this); asset.off('change', this._onSpriteAssetChange, this); asset.off('remove', this._onSpriteAssetRemove, this); if (asset.data.textureAtlasAsset) { this._system.app.assets.off(`load:${asset.data.textureAtlasAsset}`, this._onTextureAtlasLoad, this); } } _onSpriteAssetLoad(asset) { if (!asset || !asset.resource) { this.sprite = null; } else { if (!asset.resource.atlas) { const atlasAssetId = asset.data.textureAtlasAsset; if (atlasAssetId) { const assets = this._system.app.assets; assets.off(`load:${atlasAssetId}`, this._onTextureAtlasLoad, this); assets.once(`load:${atlasAssetId}`, this._onTextureAtlasLoad, this); } } else { this.sprite = asset.resource; } } } _onSpriteAssetChange(asset) { this._onSpriteAssetLoad(asset); } _onSpriteAssetRemove(asset) {} _bindSprite(sprite) { this._evtSetMeshes = sprite.on('set:meshes', this._onSpriteMeshesChange, this); sprite.on('set:pixelsPerUnit', this._onSpritePpuChange, this); sprite.on('set:atlas', this._onAtlasTextureChange, this); if (sprite.atlas) { sprite.atlas.on('set:texture', this._onAtlasTextureChange, this); } } _unbindSprite(sprite) { this._evtSetMeshes?.off(); this._evtSetMeshes = null; sprite.off('set:pixelsPerUnit', this._onSpritePpuChange, this); sprite.off('set:atlas', this._onAtlasTextureChange, this); if (sprite.atlas) { sprite.atlas.off('set:texture', this._onAtlasTextureChange, this); } } _onSpriteMeshesChange() { if (this._sprite) { this._spriteFrame = math.clamp(this._spriteFrame, 0, this._sprite.frameKeys.length - 1); } this._updateSprite(); } _onSpritePpuChange() { if (this.sprite.renderMode !== SPRITE_RENDERMODE_SIMPLE && this._pixelsPerUnit === null) { this._updateSprite(); } } _onAtlasTextureChange() { if (this.sprite && this.sprite.atlas && this.sprite.atlas.texture) { this._renderable.setParameter('texture_emissiveMap', this._sprite.atlas.texture); this._renderable.setParameter('texture_opacityMap', this._sprite.atlas.texture); } else { this._renderable.deleteParameter('texture_emissiveMap'); this._renderable.deleteParameter('texture_opacityMap'); } } _onTextureAtlasLoad(atlasAsset) { const spriteAsset = this._spriteAsset; if (spriteAsset instanceof Asset) { this._onSpriteAssetLoad(spriteAsset); } else { this._onSpriteAssetLoad(this._system.app.assets.get(spriteAsset)); } } onEnable() { if (this._materialAsset) { const asset = this._system.app.assets.get(this._materialAsset); if (asset && asset.resource !== this._material) { this._bindMaterialAsset(asset); } } if (this._textureAsset) { const asset = this._system.app.assets.get(this._textureAsset); if (asset && asset.resource !== this._texture) { this._bindTextureAsset(asset); } } if (this._spriteAsset) { const asset = this._system.app.assets.get(this._spriteAsset); if (asset && asset.resource !== this._sprite) { this._bindSpriteAsset(asset); } } this._element.addModelToLayers(this._renderable.model); } onDisable() { this._element.removeModelFromLayers(this._renderable.model); } _setStencil(stencilParams) { this._renderable.meshInstance.stencilFront = stencilParams; this._renderable.meshInstance.stencilBack = stencilParams; let ref = 0; if (this._element.maskedBy) { ref = this._element.maskedBy.element._image._maskRef; } if (this._renderable.unmaskMeshInstance) { const sp = new StencilParameters({ ref: ref + 1, func: FUNC_EQUAL, zpass: STENCILOP_DECREMENT }); this._renderable.unmaskMeshInstance.stencilFront = sp; this._renderable.unmaskMeshInstance.stencilBack = sp; } } _updateRenderableEmissive() { _tempColor.linear(this._color); this._colorUniform[0] = _tempColor.r; this._colorUniform[1] = _tempColor.g; this._colorUniform[2] = _tempColor.b; this._renderable.setParameter('material_emissive', this._colorUniform); } set color(value) { const { r, g, b } = value; if (this._color.r !== r || this._color.g !== g || this._color.b !== b) { this._color.r = r; this._color.g = g; this._color.b = b; this._updateRenderableEmissive(); } if (this._element) { this._element.fire('set:color', this._color); } } get color() { return this._color; } set opacity(value) { if (value !== this._color.a) { this._color.a = value; this._renderable.setParameter('material_opacity', value); } if (this._element) { this._element.fire('set:opacity', value); } } get opacity() { return this._color.a; } set rect(value) { let x, y, z, w; if (value instanceof Vec4) { x = value.x; y = value.y; z = value.z; w = value.w; } else { x = value[0]; y = value[1]; z = value[2]; w = value[3]; } if (x === this._rect.x && y === this._rect.y && z === this._rect.z && w === this._rect.w) { return; } this._rect.set(x, y, z, w); if (this._renderable.mesh) { if (!this._element._beingInitialized) { this._updateMesh(this._renderable.mesh); } else { this._meshDirty = true; } } } get rect() { return this._rect; } _removeMaterialAssetEvents() { if (this._materialAsset) { const assets = this._system.app.assets; assets.off(`add:${this._materialAsset}`, this._onMaterialAdded, this); const asset = assets.get(this._materialAsset); if (asset) { asset.off('load', this._onMaterialLoad, this); asset.off('change', this._onMaterialChange, this); asset.off('remove', this._onMaterialRemove, this); } } } set material(value) { if (this._material === value) return; if (!value) { const screenSpace = this._element._isScreenSpace(); if (this.mask) { value = screenSpace ? this._system.defaultScreenSpaceImageMaskMaterial : this._system.defaultImageMaskMaterial; } else { value = screenSpace ? this._system.defaultScreenSpaceImageMaterial : this._system.defaultImageMaterial; } } this._material = value; if (this._materialAsset) { const asset = this._system.app.assets.get(this._materialAsset); if (!asset || asset.resource !== value) { this._removeMaterialAssetEvents(); this._materialAsset = null; } } if (value) { this._renderable.setMaterial(value); if (this._hasUserMaterial()) { this._renderable.deleteParameter('material_opacity'); this._renderable.deleteParameter('material_emissive'); } else { this._updateRenderableEmissive(); this._renderable.setParameter('material_opacity', this._color.a); } } } get material() { return this._material; } set materialAsset(value) { const assets = this._system.app.assets; let _id = value; if (value instanceof Asset) { _id = value.id; } if (this._materialAsset !== _id) { this._removeMaterialAssetEvents(); this._materialAsset = _id; if (this._materialAsset) { const asset = assets.get(this._materialAsset); if (!asset) { this._materialAsset = null; this.material = null; this._materialAsset = _id; assets.on(`add:${this._materialAsset}`, this._onMaterialAdded, this); } else { this._bindMaterialAsset(asset); } } else { this._materialAsset = null; this.material = null; this._materialAsset = _id; } } } get materialAsset() { return this._materialAsset; } set texture(value) { if (this._texture === value) return; if (this._textureAsset) { const textureAsset = this._system.app.assets.get(this._textureAsset); if (textureAsset && textureAsset.resource !== value) { this.textureAsset = null; } } this._texture = value; if (value) { if (this._spriteAsset) { this.spriteAsset = null; } this._renderable.setParameter('texture_emissiveMap', this._texture); this._renderable.setParameter('texture_opacityMap', this._texture); this._updateRenderableEmissive(); this._renderable.setParameter('material_opacity', this._color.a); const newAspectRatio = this._texture.width / this._texture.height; if (newAspectRatio !== this._targetAspectRatio) { this._targetAspectRatio = newAspectRatio; if (this._element.fitMode !== FITMODE_STRETCH) { this.refreshMesh(); } } } else { this._renderable.deleteParameter('texture_emissiveMap'); this._renderable.deleteParameter('texture_opacityMap'); this._targetAspectRatio = -1; if (this._element.fitMode !== FITMODE_STRETCH) { this.refreshMesh(); } } } get texture() { return this._texture; } set textureAsset(value) { const assets = this._system.app.assets; let _id = value; if (value instanceof Asset) { _id = value.id; } if (this._textureAsset !== _id) { if (this._textureAsset) { assets.off(`add:${this._textureAsset}`, this._onTextureAdded, this); const _prev = assets.get(this._textureAsset); if (_prev) { _prev.off('load', this._onTextureLoad, this); _prev.off('change', this._onTextureChange, this); _prev.off('remove', this._onTextureRemove, this); } } this._textureAsset = _id; if (this._textureAsset) { const asset = assets.get(this._textureAsset); if (!asset) { this.texture = null; assets.on(`add:${this._textureAsset}`, this._onTextureAdded, this); } else { this._bindTextureAsset(asset); } } else { this.texture = null; } } } get textureAsset() { return this._textureAsset; } set spriteAsset(value) { const assets = this._system.app.assets; let _id = value; if (value instanceof Asset) { _id = value.id; } if (this._spriteAsset !== _id) { if (this._spriteAsset) { assets.off(`add:${this._spriteAsset}`, this._onSpriteAssetAdded, this); const _prev = assets.get(this._spriteAsset); if (_prev) { this._unbindSpriteAsset(_prev); } } this._spriteAsset = _id; if (this._spriteAsset) { const asset = assets.get(this._spriteAsset); if (!asset) { this.sprite = null; assets.on(`add:${this._spriteAsset}`, this._onSpriteAssetAdded, this); } else { this._bindSpriteAsset(asset); } } else { this.sprite = null; } } if (this._element) { this._element.fire('set:spriteAsset', _id); } } get spriteAsset() { return this._spriteAsset; } set sprite(value) { if (this._sprite === value) return; if (this._sprite) { this._unbindSprite(this._sprite); } if (this._spriteAsset) { const spriteAsset = this._system.app.assets.get(this._spriteAsset); if (spriteAsset && spriteAsset.resource !== value) { this.spriteAsset = null; } } this._sprite = value; if (this._sprite) { this._bindSprite(this._sprite); if (this._textureAsset) { this.textureAsset = null; } } if (this._sprite && this._sprite.atlas && this._sprite.atlas.texture) { this._renderable.setParameter('texture_emissiveMap', this._sprite.atlas.texture); this._renderable.setParameter('texture_opacityMap', this._sprite.atlas.texture); } else { this._renderable.deleteParameter('texture_emissiveMap'); this._renderable.deleteParameter('texture_opacityMap'); } if (this._sprite) { this._spriteFrame = math.clamp(this._spriteFrame, 0, this._sprite.frameKeys.length - 1); } this._updateSprite(); } get sprite() { return this._sprite; } set spriteFrame(value) { const oldValue = this._spriteFrame; if (this._sprite) { this._spriteFrame = math.clamp(value, 0, this._sprite.frameKeys.length - 1); } else { this._spriteFrame = value; } if (this._spriteFrame !== oldValue) { this._updateSprite(); } if (this._element) { this._element.fire('set:spriteFrame', value); } } get spriteFrame() { return this._spriteFrame; } set mesh(value) { this._renderable.setMesh(value); if (this._defaultMesh === value) { this._renderable.setAabbFunc(null); } else { this._renderable.setAabbFunc(this._updateAabbFunc); } } get mesh() { return this._renderable.mesh; } set mask(value) { if (this._mask !== value) { this._mask = value; this._toggleMask(); } } get mask() { return this._mask; } set pixelsPerUnit(value) { if (this._pixelsPerUnit === value) return; this._pixelsPerUnit = value; if (this._sprite && (this._sprite.renderMode === SPRITE_RENDERMODE_SLICED || this._sprite.renderMode === SPRITE_RENDERMODE_TILED)) { this._updateSprite(); } } get pixelsPerUnit() { return this._pixelsPerUnit; } get aabb() { if (this._renderable.meshInstance) { return this._renderable.meshInstance.aabb; } return null; } } export { ImageElement };