UNPKG

playcanvas

Version:

PlayCanvas WebGL game engine

259 lines (256 loc) 6.94 kB
import { EventHandler } from '../core/event-handler.js'; import { Vec2 } from '../core/math/vec2.js'; import { SPRITE_RENDERMODE_SIMPLE, SPRITE_RENDERMODE_SLICED, SPRITE_RENDERMODE_TILED } from './constants.js'; import { Mesh } from './mesh.js'; import { Geometry } from './geometry/geometry.js'; const spriteNormals = [ 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1 ]; const spriteIndices = [ 0, 1, 3, 2, 3, 1 ]; class Sprite extends EventHandler { constructor(device, options){ super(); this._device = device; this._pixelsPerUnit = options && options.pixelsPerUnit !== undefined ? options.pixelsPerUnit : 1; this._renderMode = options && options.renderMode !== undefined ? options.renderMode : SPRITE_RENDERMODE_SIMPLE; this._atlas = options && options.atlas !== undefined ? options.atlas : null; this._frameKeys = options && options.frameKeys !== undefined ? options.frameKeys : null; this._meshes = []; this._updatingProperties = false; this._meshesDirty = false; if (this._atlas && this._frameKeys) { this._createMeshes(); } } set frameKeys(value) { this._frameKeys = value; if (this._atlas && this._frameKeys) { if (this._updatingProperties) { this._meshesDirty = true; } else { this._createMeshes(); } } this.fire('set:frameKeys', value); } get frameKeys() { return this._frameKeys; } set atlas(value) { if (value === this._atlas) return; if (this._atlas) { this._atlas.off('set:frames', this._onSetFrames, this); this._atlas.off('set:frame', this._onFrameChanged, this); this._atlas.off('remove:frame', this._onFrameRemoved, this); } this._atlas = value; if (this._atlas && this._frameKeys) { this._atlas.on('set:frames', this._onSetFrames, this); this._atlas.on('set:frame', this._onFrameChanged, this); this._atlas.on('remove:frame', this._onFrameRemoved, this); if (this._updatingProperties) { this._meshesDirty = true; } else { this._createMeshes(); } } this.fire('set:atlas', value); } get atlas() { return this._atlas; } set pixelsPerUnit(value) { if (this._pixelsPerUnit === value) return; this._pixelsPerUnit = value; this.fire('set:pixelsPerUnit', value); if (this._atlas && this._frameKeys && this.renderMode === SPRITE_RENDERMODE_SIMPLE) { if (this._updatingProperties) { this._meshesDirty = true; } else { this._createMeshes(); } } } get pixelsPerUnit() { return this._pixelsPerUnit; } set renderMode(value) { if (this._renderMode === value) { return; } const prev = this._renderMode; this._renderMode = value; this.fire('set:renderMode', value); if (prev === SPRITE_RENDERMODE_SIMPLE || value === SPRITE_RENDERMODE_SIMPLE) { if (this._atlas && this._frameKeys) { if (this._updatingProperties) { this._meshesDirty = true; } else { this._createMeshes(); } } } } get renderMode() { return this._renderMode; } get meshes() { return this._meshes; } _createMeshes() { const len = this._meshes.length; for(let i = 0; i < len; i++){ const mesh = this._meshes[i]; if (mesh) { mesh.destroy(); } } const count = this._frameKeys.length; this._meshes = new Array(count); const createMeshFunc = this.renderMode === SPRITE_RENDERMODE_SLICED || this._renderMode === SPRITE_RENDERMODE_TILED ? this._create9SliceMesh : this._createSimpleMesh; for(let i = 0; i < count; i++){ const frame = this._atlas.frames[this._frameKeys[i]]; this._meshes[i] = frame ? createMeshFunc.call(this, frame) : null; } this.fire('set:meshes'); } _createSimpleMesh(frame) { const rect = frame.rect; const texWidth = this._atlas.texture.width; const texHeight = this._atlas.texture.height; const w = rect.z / this._pixelsPerUnit; const h = rect.w / this._pixelsPerUnit; const hp = frame.pivot.x; const vp = frame.pivot.y; const positions = [ -hp * w, -vp * h, 0, (1 - hp) * w, -vp * h, 0, (1 - hp) * w, (1 - vp) * h, 0, -hp * w, (1 - vp) * h, 0 ]; const lu = rect.x / texWidth; const bv = 1.0 - rect.y / texHeight; const ru = (rect.x + rect.z) / texWidth; const tv = 1.0 - (rect.y + rect.w) / texHeight; const uvs = [ lu, bv, ru, bv, ru, tv, lu, tv ]; const geom = new Geometry(); geom.positions = positions; geom.normals = spriteNormals; geom.uvs = uvs; geom.indices = spriteIndices; return Mesh.fromGeometry(this._device, geom); } _create9SliceMesh() { const he = Vec2.ONE; const ws = 3; const ls = 3; const positions = []; const normals = []; const uvs = []; const indices = []; let vcounter = 0; for(let i = 0; i <= ws; i++){ const u = i === 0 || i === ws ? 0 : 1; for(let j = 0; j <= ls; j++){ const x = -he.x + 2.0 * he.x * (i <= 1 ? 0 : 3) / ws; const y = 0.0; const z = -(-he.y + 2.0 * he.y * (j <= 1 ? 0 : 3) / ls); const v = j === 0 || j === ls ? 0 : 1; positions.push(-x, y, z); normals.push(0.0, 1.0, 0.0); uvs.push(u, v); if (i < ws && j < ls) { indices.push(vcounter + ls + 1, vcounter + 1, vcounter); indices.push(vcounter + ls + 1, vcounter + ls + 2, vcounter + 1); } vcounter++; } } const geom = new Geometry(); geom.positions = positions; geom.normals = normals; geom.uvs = uvs; geom.indices = indices; return Mesh.fromGeometry(this._device, geom); } _onSetFrames(frames) { if (this._updatingProperties) { this._meshesDirty = true; } else { this._createMeshes(); } } _onFrameChanged(frameKey, frame) { const idx = this._frameKeys.indexOf(frameKey); if (idx < 0) return; if (frame) { if (this.renderMode === SPRITE_RENDERMODE_SIMPLE) { this._meshes[idx] = this._createSimpleMesh(frame); } } else { this._meshes[idx] = null; } this.fire('set:meshes'); } _onFrameRemoved(frameKey) { const idx = this._frameKeys.indexOf(frameKey); if (idx < 0) return; this._meshes[idx] = null; this.fire('set:meshes'); } startUpdate() { this._updatingProperties = true; this._meshesDirty = false; } endUpdate() { this._updatingProperties = false; if (this._meshesDirty && this._atlas && this._frameKeys) { this._createMeshes(); } this._meshesDirty = false; } destroy() { for (const mesh of this._meshes){ if (mesh) { mesh.destroy(); } } this._meshes.length = 0; } } export { Sprite };