From 0abcd9c7fd45d6b45f2a6d881dd60059c32384f1 Mon Sep 17 00:00:00 2001 From: azhan Date: Mon, 19 Jul 2021 21:03:45 +0800 Subject: [PATCH 01/72] feat:Support atlas #197 --- packages/core/src/2d/atlas/SpriteAtlas.ts | 107 +++++++++++++++++++ packages/core/src/2d/atlas/types.ts | 41 +++++++ packages/core/src/2d/enums/SpriteMeshType.ts | 7 ++ packages/core/src/2d/index.ts | 1 + packages/core/src/2d/sprite/Sprite.ts | 98 ++++++++++------- packages/core/src/asset/AssetType.ts | 4 +- packages/loader/src/SpriteAtlasLoader.ts | 45 ++++++++ packages/loader/src/index.ts | 1 + 8 files changed, 267 insertions(+), 37 deletions(-) create mode 100644 packages/core/src/2d/atlas/SpriteAtlas.ts create mode 100644 packages/core/src/2d/atlas/types.ts create mode 100644 packages/core/src/2d/enums/SpriteMeshType.ts create mode 100644 packages/loader/src/SpriteAtlasLoader.ts diff --git a/packages/core/src/2d/atlas/SpriteAtlas.ts b/packages/core/src/2d/atlas/SpriteAtlas.ts new file mode 100644 index 0000000000..aa85c167b6 --- /dev/null +++ b/packages/core/src/2d/atlas/SpriteAtlas.ts @@ -0,0 +1,107 @@ +import { Rect, Vector2 } from "@oasis-engine/math"; +import { RefObject } from "../../asset/RefObject"; +import { Engine } from "../../Engine"; +import { Texture2D } from "../../texture"; +import { AtlasConfig } from "./types"; +import { Sprite } from "../sprite/Sprite"; + +/** + * Sprite Atlas. + */ +export class SpriteAtlas extends RefObject { + // Save all sprite instances in the Atlas. + private _spritesMap: { [key: string]: Sprite } = {}; + + /** + * Pass in atlas data and pictures to parse out the included sprites. + * @param config Raw data. + * @param originalImgs Original images. + * @returns + */ + private _initialization(config: AtlasConfig, originalImgs: HTMLImageElement[]): void { + const { engine, _spritesMap: spritesMap } = this; + const atlasItems = config.AtlasItems; + const atlasItemsLen = atlasItems.length; + const texturesArr: Texture2D[] = new Array(atlasItemsLen); + for (let idx = atlasItemsLen - 1; idx >= 0; idx--) { + // Generate Texture2D according to configuration + const originalImg = originalImgs[idx]; + const { width, height } = originalImg; + const texture = new Texture2D(engine, width, height, config.format); + /** @ts-ignore */ + if (!texture._platformTexture) return; + texture.setImageSource(originalImg); + texture.generateMipmaps(); + texturesArr[idx] = texture; + + // Generate all the sprites on this texture. + const atlasItem = atlasItems[idx]; + const frames = atlasItem.frames; + const [sourceWidth, sourceHeight] = atlasItem.size; + const sourceWidthReciprocal = 1.0 / sourceWidth; + const sourceHeightReciprocal = 1.0 / sourceHeight; + for (var key in frames) { + const frame = frames[key]; + const region = frame.region; + const pivot = frame.pivot; + const offset = frame.offset; + const atlasRegion = frame.atlasRegion; + const sprite = new Sprite( + engine, + texture, + new Rect(region.x, region.y, region.w, region.h), + new Vector2(pivot.x, pivot.y), + frame.pixelsPerUnit + ); + sprite.atlasRegion = new Rect( + atlasRegion.x * sourceWidthReciprocal, + atlasRegion.y * sourceHeightReciprocal, + atlasRegion.w * sourceWidthReciprocal, + atlasRegion.h * sourceHeightReciprocal + ); + sprite.offset = new Vector2(offset.x, offset.y); + spritesMap[key] = sprite; + } + } + } + + constructor(engine: Engine, atlasData: AtlasConfig, imgs: HTMLImageElement[]) { + super(engine); + this._initialization(atlasData, imgs); + } + + /** + * Get the sprite named 'name' from the atlas. + * @param name + * @returns + */ + public getSprite(name: string): Sprite { + const sprite = this._spritesMap[name]; + if (!sprite) { + console.warn("There is no sprite named " + name + " in the atlas."); + } + return sprite; + } + + /** + * Get all the sprites in the atlas. + * @returns + */ + public getSprites(): Sprite[] { + const sprites: Sprite[] = []; + const spritesMap = this._spritesMap; + for (let key in spritesMap) { + sprites.push(spritesMap[key]); + } + return sprites; + } + + /** + * @override + */ + _onDestroy(): void { + if (this._spritesMap) { + this._spritesMap = null; + } + } +} diff --git a/packages/core/src/2d/atlas/types.ts b/packages/core/src/2d/atlas/types.ts new file mode 100644 index 0000000000..233dba05f1 --- /dev/null +++ b/packages/core/src/2d/atlas/types.ts @@ -0,0 +1,41 @@ +import { TextureFormat } from "../../texture"; +import { SpriteMeshType } from "../enums/SpriteMeshType"; + +/** + * The original data type of the atlas. + */ +export interface AtlasConfig { + // The big picture array, each big picture contains multiple sprites. + AtlasItems: { + // The url of the big picture. + img: string; + // The size of the big picture. + size: number[]; + // Sprites contained in the big picture. + frames: { [key: string]: AtlasFrame }; + }[]; + // Version of Atlas Packaging Tool. + version: number; + // Texture format. + format: TextureFormat; +} + +/** + * The original data type of each sprite. + */ +export interface AtlasFrame { + // Sprite's mesh type. + meshType: SpriteMeshType; + // The original size of the sprite. + sourceSize: { w: number; h: number }; + // The range of the sprites on the big picture + atlasRegion: { x: number; y: number; w: number; h: number }; + // If there is trimming, the offset of the sprite relative to the original sprite. + offset: { x: number; y: number }; + region: { x: number; y: number; w: number; h: number }; + pivot: { x: number; y: number }; + pixelsPerUnit: number; + vertices: number[][]; + uv: number[][]; + triangles: number[]; +} diff --git a/packages/core/src/2d/enums/SpriteMeshType.ts b/packages/core/src/2d/enums/SpriteMeshType.ts new file mode 100644 index 0000000000..1fb8a07a2c --- /dev/null +++ b/packages/core/src/2d/enums/SpriteMeshType.ts @@ -0,0 +1,7 @@ +/** + * The shape of the sprite when it is rendered. + */ +export enum SpriteMeshType { + Rect, + Polygon +} diff --git a/packages/core/src/2d/index.ts b/packages/core/src/2d/index.ts index 4a704157ab..31ba3b30bd 100644 --- a/packages/core/src/2d/index.ts +++ b/packages/core/src/2d/index.ts @@ -1,3 +1,4 @@ export { SpriteMaskInteraction } from "./enums/SpriteMaskInteraction"; export { SpriteMaskLayer } from "./enums/SpriteMaskLayer"; +export { SpriteAtlas } from "./atlas/SpriteAtlas"; export * from "./sprite/index"; diff --git a/packages/core/src/2d/sprite/Sprite.ts b/packages/core/src/2d/sprite/Sprite.ts index 82d94536fb..85dda90a48 100644 --- a/packages/core/src/2d/sprite/Sprite.ts +++ b/packages/core/src/2d/sprite/Sprite.ts @@ -22,6 +22,8 @@ export class Sprite extends RefObject { private _pivot: Vector2 = new Vector2(0.5, 0.5); private _pixelsPerUnit: number; private _dirtyFlag: number = DirtyFlag.all; + // If and only if the type is Rect and trimmed. + private _offset: Vector2 = new Vector2(0, 0); /** * The reference to the used texture. @@ -33,7 +35,7 @@ export class Sprite extends RefObject { set texture(value: Texture2D) { if (this._texture !== value) { this._texture = value; - this._setDirtyFlagTrue(DirtyFlag.positions); + this._setDirtyFlagTrue(DirtyFlag.vertices); } } @@ -42,9 +44,9 @@ export class Sprite extends RefObject { * @remarks The returned bounds should be considered deep-read-only. */ get bounds(): Readonly { - if (this._isContainDirtyFlag(DirtyFlag.positions)) { + if (this._isContainDirtyFlag(DirtyFlag.vertices)) { this._updatePositionsAndBounds(); - this._setDirtyFlagTrue(DirtyFlag.positions); + this._setDirtyFlagTrue(DirtyFlag.vertices); } return this._bounds; } @@ -62,6 +64,7 @@ export class Sprite extends RefObject { atlasRegion.y = MathUtil.clamp(value.y, 0, 1); atlasRegion.width = MathUtil.clamp(value.width, 0, 1.0 - atlasRegion.x); atlasRegion.height = MathUtil.clamp(value.height, 0, 1.0 - atlasRegion.y); + this._setDirtyFlagTrue(DirtyFlag.vertices | DirtyFlag.uv); } /** @@ -75,7 +78,7 @@ export class Sprite extends RefObject { const pivot = this._pivot; pivot.x = MathUtil.clamp(value.x, 0, 1); pivot.y = MathUtil.clamp(value.y, 0, 1); - this._setDirtyFlagTrue(DirtyFlag.positions); + this._setDirtyFlagTrue(DirtyFlag.vertices); } /** @@ -91,7 +94,7 @@ export class Sprite extends RefObject { region.y = MathUtil.clamp(value.y, 0, 1); region.width = MathUtil.clamp(value.width, 0, 1.0 - region.x); region.height = MathUtil.clamp(value.height, 0, 1.0 - region.y); - this._setDirtyFlagTrue(DirtyFlag.positions | DirtyFlag.uv); + this._setDirtyFlagTrue(DirtyFlag.vertices | DirtyFlag.uv); } /** @@ -104,10 +107,23 @@ export class Sprite extends RefObject { set pixelsPerUnit(value: number) { if (this._pixelsPerUnit !== value) { this._pixelsPerUnit = value; - this._setDirtyFlagTrue(DirtyFlag.positions); + this._setDirtyFlagTrue(DirtyFlag.vertices); } } + /** + * Only used in the atlas!!! + * The number of pixels in the sprite that correspond to one unit in world space. + */ + get offset(): Vector2 { + return this._offset; + } + + set offset(value: Vector2) { + this._offset = value; + this._setDirtyFlagTrue(DirtyFlag.vertices); + } + /** * Constructor a sprite. * @param engine - Engine to which the sprite belongs @@ -131,7 +147,6 @@ export class Sprite extends RefObject { if (region) { this.region = region; - this.atlasRegion = region; } if (pivot) { @@ -155,28 +170,29 @@ export class Sprite extends RefObject { */ private _updatePositionsAndBounds(): void { const { texture } = this; - let lx = 0; - let ty = 0; - let rx = 0; - let by = 0; - - if (texture) { - const { width, height } = texture; - const { width: rWidth, height: rHeight } = this.region; - const pixelsPerUnitReciprocal = 1.0 / this._pixelsPerUnit; - - // Get the width and height in 3D space. - const unitWidth = rWidth * width * pixelsPerUnitReciprocal; - const unitHeight = rHeight * height * pixelsPerUnitReciprocal; - - // Get the distance between the anchor point and the four sides. - const { x: px, y: py } = this.pivot; - lx = -px * unitWidth; - ty = -py * unitHeight; - rx = (1 - px) * unitWidth; - by = (1 - py) * unitHeight; + if (!texture) { + return; } + const { _region, _atlasRegion, _pixelsPerUnit, _offset, _pivot } = this; + const { width, height } = texture; + const { width: regionWidth, height: regionHeight } = _region; + const { width: atlasRegionWidth, height: atlasRegionHeight } = _atlasRegion; + const pixelsPerUnitReciprocal = 1.0 / _pixelsPerUnit; + + // Get the width and height in 3D space. + const unitWidth = atlasRegionWidth * regionWidth * width * pixelsPerUnitReciprocal; + const unitHeight = atlasRegionHeight * regionHeight * height * pixelsPerUnitReciprocal; + + // Get the distance between the anchor point and the four sides. + const { x: px, y: py } = _pivot; + const offsetX = _offset.x * pixelsPerUnitReciprocal; + const offsetY = _offset.y * pixelsPerUnitReciprocal; + const lx = -px * unitWidth + offsetX; + const ty = -py * unitHeight + offsetY; + const rx = (1 - px) * unitWidth + offsetX; + const by = (1 - py) * unitHeight + offsetY; + // Assign values ​​to _positions const positions = this._positions; // Top-left. @@ -198,24 +214,34 @@ export class Sprite extends RefObject { * Update mesh. */ private _updateMesh(): void { - if (this._isContainDirtyFlag(DirtyFlag.positions)) { + if (this._isContainDirtyFlag(DirtyFlag.vertices)) { this._updatePositionsAndBounds(); } if (this._isContainDirtyFlag(DirtyFlag.uv)) { const uv = this._uv; - const { x, y, width, height } = this.region; - const rightX = x + width; - const bottomY = y + height; + const { + x: atlasRegionX, + y: atlasRegionY, + width: atlasRegionWidth, + height: atlasRegionHeight + } = this._atlasRegion; + const { x: regionX, y: regionY, width: regionWidth, height: regionHeight } = this._region; + const realWidth = atlasRegionWidth * regionWidth; + const realheight = atlasRegionHeight * regionHeight; + const left = atlasRegionX + realWidth * regionX; + const right = left + realWidth; + const top = atlasRegionY + realheight * regionY; + const bottom = top + realheight; // Top-left. - uv[0].setValue(x, y); + uv[0].setValue(left, top); // Top-right. - uv[1].setValue(rightX, y); + uv[1].setValue(right, top); // Bottom-right. - uv[2].setValue(rightX, bottomY); + uv[2].setValue(right, bottom); // Bottom-left. - uv[3].setValue(x, bottomY); + uv[3].setValue(left, bottom); } if (this._isContainDirtyFlag(DirtyFlag.triangles)) { @@ -257,7 +283,7 @@ export class Sprite extends RefObject { } enum DirtyFlag { - positions = 0x1, + vertices = 0x1, uv = 0x2, triangles = 0x4, all = 0x7 diff --git a/packages/core/src/asset/AssetType.ts b/packages/core/src/asset/AssetType.ts index a3b8438a1f..815b2e791d 100644 --- a/packages/core/src/asset/AssetType.ts +++ b/packages/core/src/asset/AssetType.ts @@ -32,5 +32,7 @@ export enum AssetType { /** Compress Texture. */ KTX = "ktx", /** Cube Compress Texture. */ - KTXCube = "ktx-cube" + KTXCube = "ktx-cube", + /** Sprite Atlas. */ + SpriteAtlas = "sprite-atlas" } diff --git a/packages/loader/src/SpriteAtlasLoader.ts b/packages/loader/src/SpriteAtlasLoader.ts new file mode 100644 index 0000000000..c6041a4163 --- /dev/null +++ b/packages/loader/src/SpriteAtlasLoader.ts @@ -0,0 +1,45 @@ +import { + resourceLoader, + Loader, + AssetPromise, + AssetType, + LoadItem, + SpriteAtlas, + ResourceManager +} from "@oasis-engine/core"; +import { AtlasConfig } from "@oasis-engine/core/types/2d/atlas/types"; + +@resourceLoader(AssetType.SpriteAtlas, ["atlas"], false) +class SpriteAtlasLoader extends Loader { + load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { + return new AssetPromise((resolve, reject) => { + this.request(item.url, { + ...item, + type: "json" + }) + .then((atlasData) => { + const atlasItems = atlasData.AtlasItems; + const atlasItemsLen = atlasItems.length; + const picsArr: string[] = new Array(atlasItemsLen); + // Load the texture used in the atlas. + for (let idx = atlasItemsLen - 1; idx >= 0; idx--) { + picsArr[idx] = atlasItems[idx].img; + } + Promise.all( + picsArr.map((url) => + this.request(url, { + ...item, + type: "image" + }) + ) + ).then((imgs) => { + // Return a SpriteAtlas instance. + resolve(new SpriteAtlas(resourceManager.engine, atlasData, imgs)); + }); + }) + .catch((e) => { + reject(e); + }); + }); + } +} diff --git a/packages/loader/src/index.ts b/packages/loader/src/index.ts index 9a84d8a49d..33c3c5e107 100644 --- a/packages/loader/src/index.ts +++ b/packages/loader/src/index.ts @@ -5,6 +5,7 @@ import "./KTXCubeLoader"; import "./KTXLoader"; import "./Texture2DLoader"; import "./TextureCubeLoader"; +import "./SpriteAtlasLoader"; import "./gltf/extensions/index"; export { GLTFResource } from "./gltf/GLTFResource"; From 464262bd5a3e8ea94cfe2870946da769f195a561 Mon Sep 17 00:00:00 2001 From: azhan Date: Mon, 19 Jul 2021 21:13:19 +0800 Subject: [PATCH 02/72] feat:Support atlas #197 --- packages/core/src/2d/atlas/SpriteAtlas.ts | 58 +++++++++++++---------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/packages/core/src/2d/atlas/SpriteAtlas.ts b/packages/core/src/2d/atlas/SpriteAtlas.ts index aa85c167b6..601418ac60 100644 --- a/packages/core/src/2d/atlas/SpriteAtlas.ts +++ b/packages/core/src/2d/atlas/SpriteAtlas.ts @@ -12,6 +12,32 @@ export class SpriteAtlas extends RefObject { // Save all sprite instances in the Atlas. private _spritesMap: { [key: string]: Sprite } = {}; + /** + * Get the sprite named 'name' from the atlas. + * @param name + * @returns + */ + public getSprite(name: string): Sprite { + const sprite = this._spritesMap[name]; + if (!sprite) { + console.warn("There is no sprite named " + name + " in the atlas."); + } + return sprite; + } + + /** + * Get all the sprites in the atlas. + * @returns + */ + public getSprites(): Sprite[] { + const sprites: Sprite[] = []; + const spritesMap = this._spritesMap; + for (let key in spritesMap) { + sprites.push(spritesMap[key]); + } + return sprites; + } + /** * Pass in atlas data and pictures to parse out the included sprites. * @param config Raw data. @@ -65,37 +91,17 @@ export class SpriteAtlas extends RefObject { } } + /** + * Constructor a sprite. + * @param engine + * @param atlasData Raw data. + * @param imgs Original images. + */ constructor(engine: Engine, atlasData: AtlasConfig, imgs: HTMLImageElement[]) { super(engine); this._initialization(atlasData, imgs); } - /** - * Get the sprite named 'name' from the atlas. - * @param name - * @returns - */ - public getSprite(name: string): Sprite { - const sprite = this._spritesMap[name]; - if (!sprite) { - console.warn("There is no sprite named " + name + " in the atlas."); - } - return sprite; - } - - /** - * Get all the sprites in the atlas. - * @returns - */ - public getSprites(): Sprite[] { - const sprites: Sprite[] = []; - const spritesMap = this._spritesMap; - for (let key in spritesMap) { - sprites.push(spritesMap[key]); - } - return sprites; - } - /** * @override */ From 907b2f1bcbf32fcab77bddba713a65ccb475752c Mon Sep 17 00:00:00 2001 From: azhan Date: Wed, 21 Jul 2021 10:33:36 +0800 Subject: [PATCH 03/72] feat:Support atlas --- packages/core/src/2d/atlas/SpriteAtlas.ts | 78 ++--------------------- packages/loader/src/SpriteAtlasLoader.ts | 56 +++++++++++++++- 2 files changed, 60 insertions(+), 74 deletions(-) diff --git a/packages/core/src/2d/atlas/SpriteAtlas.ts b/packages/core/src/2d/atlas/SpriteAtlas.ts index 601418ac60..c7288e47bd 100644 --- a/packages/core/src/2d/atlas/SpriteAtlas.ts +++ b/packages/core/src/2d/atlas/SpriteAtlas.ts @@ -1,21 +1,18 @@ -import { Rect, Vector2 } from "@oasis-engine/math"; import { RefObject } from "../../asset/RefObject"; import { Engine } from "../../Engine"; -import { Texture2D } from "../../texture"; -import { AtlasConfig } from "./types"; import { Sprite } from "../sprite/Sprite"; /** * Sprite Atlas. */ export class SpriteAtlas extends RefObject { - // Save all sprite instances in the Atlas. - private _spritesMap: { [key: string]: Sprite } = {}; + /** @internal */ + _spritesMap: Record = {}; /** * Get the sprite named 'name' from the atlas. - * @param name - * @returns + * @param name - The name of the sprite you want to find + * @returns The sprite you want to find */ public getSprite(name: string): Sprite { const sprite = this._spritesMap[name]; @@ -27,10 +24,9 @@ export class SpriteAtlas extends RefObject { /** * Get all the sprites in the atlas. - * @returns + * @returns all the sprites in the atlas */ - public getSprites(): Sprite[] { - const sprites: Sprite[] = []; + public getSprites(sprites: Sprite[]): Sprite[] { const spritesMap = this._spritesMap; for (let key in spritesMap) { sprites.push(spritesMap[key]); @@ -38,68 +34,8 @@ export class SpriteAtlas extends RefObject { return sprites; } - /** - * Pass in atlas data and pictures to parse out the included sprites. - * @param config Raw data. - * @param originalImgs Original images. - * @returns - */ - private _initialization(config: AtlasConfig, originalImgs: HTMLImageElement[]): void { - const { engine, _spritesMap: spritesMap } = this; - const atlasItems = config.AtlasItems; - const atlasItemsLen = atlasItems.length; - const texturesArr: Texture2D[] = new Array(atlasItemsLen); - for (let idx = atlasItemsLen - 1; idx >= 0; idx--) { - // Generate Texture2D according to configuration - const originalImg = originalImgs[idx]; - const { width, height } = originalImg; - const texture = new Texture2D(engine, width, height, config.format); - /** @ts-ignore */ - if (!texture._platformTexture) return; - texture.setImageSource(originalImg); - texture.generateMipmaps(); - texturesArr[idx] = texture; - - // Generate all the sprites on this texture. - const atlasItem = atlasItems[idx]; - const frames = atlasItem.frames; - const [sourceWidth, sourceHeight] = atlasItem.size; - const sourceWidthReciprocal = 1.0 / sourceWidth; - const sourceHeightReciprocal = 1.0 / sourceHeight; - for (var key in frames) { - const frame = frames[key]; - const region = frame.region; - const pivot = frame.pivot; - const offset = frame.offset; - const atlasRegion = frame.atlasRegion; - const sprite = new Sprite( - engine, - texture, - new Rect(region.x, region.y, region.w, region.h), - new Vector2(pivot.x, pivot.y), - frame.pixelsPerUnit - ); - sprite.atlasRegion = new Rect( - atlasRegion.x * sourceWidthReciprocal, - atlasRegion.y * sourceHeightReciprocal, - atlasRegion.w * sourceWidthReciprocal, - atlasRegion.h * sourceHeightReciprocal - ); - sprite.offset = new Vector2(offset.x, offset.y); - spritesMap[key] = sprite; - } - } - } - - /** - * Constructor a sprite. - * @param engine - * @param atlasData Raw data. - * @param imgs Original images. - */ - constructor(engine: Engine, atlasData: AtlasConfig, imgs: HTMLImageElement[]) { + constructor(engine: Engine) { super(engine); - this._initialization(atlasData, imgs); } /** diff --git a/packages/loader/src/SpriteAtlasLoader.ts b/packages/loader/src/SpriteAtlasLoader.ts index c6041a4163..80a08b1f27 100644 --- a/packages/loader/src/SpriteAtlasLoader.ts +++ b/packages/loader/src/SpriteAtlasLoader.ts @@ -4,10 +4,13 @@ import { AssetPromise, AssetType, LoadItem, - SpriteAtlas, - ResourceManager + ResourceManager, + Texture2D, + Sprite, + SpriteAtlas } from "@oasis-engine/core"; import { AtlasConfig } from "@oasis-engine/core/types/2d/atlas/types"; +import { Rect, Vector2 } from "@oasis-engine/math"; @resourceLoader(AssetType.SpriteAtlas, ["atlas"], false) class SpriteAtlasLoader extends Loader { @@ -33,8 +36,55 @@ class SpriteAtlasLoader extends Loader { }) ) ).then((imgs) => { + const engine = resourceManager.engine; + // + const spriteAtlas = new SpriteAtlas(engine); + /** @ts-ignore */ + const spriteMap = spriteAtlas._spritesMap; + const format = atlasData.format; + const texturesArr: Texture2D[] = new Array(atlasItemsLen); + for (let idx = atlasItemsLen - 1; idx >= 0; idx--) { + // Generate Texture2D according to configuration + const originalImg = imgs[idx]; + const { width, height } = originalImg; + const texture = new Texture2D(engine, width, height, format); + /** @ts-ignore */ + if (!texture._platformTexture) return; + + texture.setImageSource(originalImg); + texture.generateMipmaps(); + texturesArr[idx] = texture; + // Generate all the sprites on this texture. + const atlasItem = atlasItems[idx]; + const frames = atlasItem.frames; + const [sourceWidth, sourceHeight] = atlasItem.size; + const sourceWidthReciprocal = 1.0 / sourceWidth; + const sourceHeightReciprocal = 1.0 / sourceHeight; + for (var key in frames) { + const frame = frames[key]; + const region = frame.region; + const pivot = frame.pivot; + const offset = frame.offset; + const atlasRegion = frame.atlasRegion; + const sprite = new Sprite( + engine, + texture, + new Rect(region.x, region.y, region.w, region.h), + new Vector2(pivot.x, pivot.y), + frame.pixelsPerUnit + ); + sprite.atlasRegion = new Rect( + atlasRegion.x * sourceWidthReciprocal, + atlasRegion.y * sourceHeightReciprocal, + atlasRegion.w * sourceWidthReciprocal, + atlasRegion.h * sourceHeightReciprocal + ); + sprite.offset = new Vector2(offset.x, offset.y); + spriteMap[key] = sprite; + } + } // Return a SpriteAtlas instance. - resolve(new SpriteAtlas(resourceManager.engine, atlasData, imgs)); + resolve(spriteAtlas); }); }) .catch((e) => { From 8b9c8d6560f8d90db23eb0d4768124de2559c71c Mon Sep 17 00:00:00 2001 From: azhan Date: Wed, 21 Jul 2021 10:41:57 +0800 Subject: [PATCH 04/72] feat:Support atlas --- packages/core/src/2d/atlas/types.ts | 2 +- packages/core/src/2d/sprite/Sprite.ts | 20 ++++++++++---------- packages/loader/src/SpriteAtlasLoader.ts | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/core/src/2d/atlas/types.ts b/packages/core/src/2d/atlas/types.ts index 233dba05f1..ce1ba5bf67 100644 --- a/packages/core/src/2d/atlas/types.ts +++ b/packages/core/src/2d/atlas/types.ts @@ -12,7 +12,7 @@ export interface AtlasConfig { // The size of the big picture. size: number[]; // Sprites contained in the big picture. - frames: { [key: string]: AtlasFrame }; + frames: Record; }[]; // Version of Atlas Packaging Tool. version: number; diff --git a/packages/core/src/2d/sprite/Sprite.ts b/packages/core/src/2d/sprite/Sprite.ts index 85dda90a48..a27899a7b8 100644 --- a/packages/core/src/2d/sprite/Sprite.ts +++ b/packages/core/src/2d/sprite/Sprite.ts @@ -35,7 +35,7 @@ export class Sprite extends RefObject { set texture(value: Texture2D) { if (this._texture !== value) { this._texture = value; - this._setDirtyFlagTrue(DirtyFlag.vertices); + this._setDirtyFlagTrue(DirtyFlag.positions); } } @@ -44,9 +44,9 @@ export class Sprite extends RefObject { * @remarks The returned bounds should be considered deep-read-only. */ get bounds(): Readonly { - if (this._isContainDirtyFlag(DirtyFlag.vertices)) { + if (this._isContainDirtyFlag(DirtyFlag.positions)) { this._updatePositionsAndBounds(); - this._setDirtyFlagTrue(DirtyFlag.vertices); + this._setDirtyFlagTrue(DirtyFlag.positions); } return this._bounds; } @@ -64,7 +64,7 @@ export class Sprite extends RefObject { atlasRegion.y = MathUtil.clamp(value.y, 0, 1); atlasRegion.width = MathUtil.clamp(value.width, 0, 1.0 - atlasRegion.x); atlasRegion.height = MathUtil.clamp(value.height, 0, 1.0 - atlasRegion.y); - this._setDirtyFlagTrue(DirtyFlag.vertices | DirtyFlag.uv); + this._setDirtyFlagTrue(DirtyFlag.positions | DirtyFlag.uv); } /** @@ -78,7 +78,7 @@ export class Sprite extends RefObject { const pivot = this._pivot; pivot.x = MathUtil.clamp(value.x, 0, 1); pivot.y = MathUtil.clamp(value.y, 0, 1); - this._setDirtyFlagTrue(DirtyFlag.vertices); + this._setDirtyFlagTrue(DirtyFlag.positions); } /** @@ -94,7 +94,7 @@ export class Sprite extends RefObject { region.y = MathUtil.clamp(value.y, 0, 1); region.width = MathUtil.clamp(value.width, 0, 1.0 - region.x); region.height = MathUtil.clamp(value.height, 0, 1.0 - region.y); - this._setDirtyFlagTrue(DirtyFlag.vertices | DirtyFlag.uv); + this._setDirtyFlagTrue(DirtyFlag.positions | DirtyFlag.uv); } /** @@ -107,7 +107,7 @@ export class Sprite extends RefObject { set pixelsPerUnit(value: number) { if (this._pixelsPerUnit !== value) { this._pixelsPerUnit = value; - this._setDirtyFlagTrue(DirtyFlag.vertices); + this._setDirtyFlagTrue(DirtyFlag.positions); } } @@ -121,7 +121,7 @@ export class Sprite extends RefObject { set offset(value: Vector2) { this._offset = value; - this._setDirtyFlagTrue(DirtyFlag.vertices); + this._setDirtyFlagTrue(DirtyFlag.positions); } /** @@ -214,7 +214,7 @@ export class Sprite extends RefObject { * Update mesh. */ private _updateMesh(): void { - if (this._isContainDirtyFlag(DirtyFlag.vertices)) { + if (this._isContainDirtyFlag(DirtyFlag.positions)) { this._updatePositionsAndBounds(); } @@ -283,7 +283,7 @@ export class Sprite extends RefObject { } enum DirtyFlag { - vertices = 0x1, + positions = 0x1, uv = 0x2, triangles = 0x4, all = 0x7 diff --git a/packages/loader/src/SpriteAtlasLoader.ts b/packages/loader/src/SpriteAtlasLoader.ts index 80a08b1f27..eaafc9b303 100644 --- a/packages/loader/src/SpriteAtlasLoader.ts +++ b/packages/loader/src/SpriteAtlasLoader.ts @@ -37,7 +37,7 @@ class SpriteAtlasLoader extends Loader { ) ).then((imgs) => { const engine = resourceManager.engine; - // + // Generate a SpriteAtlas object const spriteAtlas = new SpriteAtlas(engine); /** @ts-ignore */ const spriteMap = spriteAtlas._spritesMap; From 390d11af3eae4d44bbfe4474c6501cb71c00fcfc Mon Sep 17 00:00:00 2001 From: azhan Date: Thu, 22 Jul 2021 16:06:39 +0800 Subject: [PATCH 05/72] feat:Support atlas --- packages/core/src/2d/atlas/SpriteAtlas.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/core/src/2d/atlas/SpriteAtlas.ts b/packages/core/src/2d/atlas/SpriteAtlas.ts index c7288e47bd..66cfa6fd3b 100644 --- a/packages/core/src/2d/atlas/SpriteAtlas.ts +++ b/packages/core/src/2d/atlas/SpriteAtlas.ts @@ -34,6 +34,10 @@ export class SpriteAtlas extends RefObject { return sprites; } + /** + * Constructor a sprite. + * @param engine - Engine to which the SpriteAtlas belongs + */ constructor(engine: Engine) { super(engine); } From 733d1190abcafe2a807ac1ee68bcdef4f594121d Mon Sep 17 00:00:00 2001 From: azhan Date: Thu, 22 Jul 2021 16:08:51 +0800 Subject: [PATCH 06/72] feat:Support atlas --- packages/core/src/2d/atlas/SpriteAtlas.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/2d/atlas/SpriteAtlas.ts b/packages/core/src/2d/atlas/SpriteAtlas.ts index 66cfa6fd3b..b2d6378a2b 100644 --- a/packages/core/src/2d/atlas/SpriteAtlas.ts +++ b/packages/core/src/2d/atlas/SpriteAtlas.ts @@ -35,7 +35,7 @@ export class SpriteAtlas extends RefObject { } /** - * Constructor a sprite. + * Constructor a SpriteAtlas. * @param engine - Engine to which the SpriteAtlas belongs */ constructor(engine: Engine) { From c068a35d3f5319f64bad6cf78c1fed341a4a537f Mon Sep 17 00:00:00 2001 From: azhan Date: Thu, 22 Jul 2021 17:04:38 +0800 Subject: [PATCH 07/72] feat:Support atlas --- packages/core/src/2d/sprite/Sprite.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/2d/sprite/Sprite.ts b/packages/core/src/2d/sprite/Sprite.ts index a27899a7b8..52629da34b 100644 --- a/packages/core/src/2d/sprite/Sprite.ts +++ b/packages/core/src/2d/sprite/Sprite.ts @@ -22,7 +22,7 @@ export class Sprite extends RefObject { private _pivot: Vector2 = new Vector2(0.5, 0.5); private _pixelsPerUnit: number; private _dirtyFlag: number = DirtyFlag.all; - // If and only if the type is Rect and trimmed. + // If and only if the type(SpriteMeshType) is Rect and trimmed. private _offset: Vector2 = new Vector2(0, 0); /** @@ -113,7 +113,7 @@ export class Sprite extends RefObject { /** * Only used in the atlas!!! - * The number of pixels in the sprite that correspond to one unit in world space. + * Offset of Spirte in the atlas from the original picture. */ get offset(): Vector2 { return this._offset; From 6c50d6cbb7f66a75c3db9acb70b1a247105d1685 Mon Sep 17 00:00:00 2001 From: azhan Date: Fri, 30 Jul 2021 11:47:55 +0800 Subject: [PATCH 08/72] feat:Support atlas --- packages/core/src/2d/atlas/types.ts | 31 +++++----- packages/core/src/2d/enums/SpriteMeshType.ts | 7 --- packages/core/src/2d/sprite/Sprite.ts | 62 +++++++++----------- packages/loader/src/SpriteAtlasLoader.ts | 38 ++++-------- 4 files changed, 56 insertions(+), 82 deletions(-) delete mode 100644 packages/core/src/2d/enums/SpriteMeshType.ts diff --git a/packages/core/src/2d/atlas/types.ts b/packages/core/src/2d/atlas/types.ts index ce1ba5bf67..f647f2e170 100644 --- a/packages/core/src/2d/atlas/types.ts +++ b/packages/core/src/2d/atlas/types.ts @@ -1,37 +1,36 @@ import { TextureFormat } from "../../texture"; -import { SpriteMeshType } from "../enums/SpriteMeshType"; /** * The original data type of the atlas. */ export interface AtlasConfig { - // The big picture array, each big picture contains multiple sprites. - AtlasItems: { - // The url of the big picture. + /** The big picture array, each big picture contains multiple sprites. */ + atlasItems: { + /** The url of the big picture. */ img: string; - // The size of the big picture. + /** The size of the big picture. */ size: number[]; - // Sprites contained in the big picture. - frames: Record; + /** Sprites contained in the big picture. */ + sprites: AtlasSprite[]; }[]; - // Version of Atlas Packaging Tool. + /** Version of Atlas */ version: number; - // Texture format. + /** Texture format. */ format: TextureFormat; } /** * The original data type of each sprite. */ -export interface AtlasFrame { - // Sprite's mesh type. - meshType: SpriteMeshType; - // The original size of the sprite. +export interface AtlasSprite { + /** The name the sprite. */ + name: string; + /** The original size of the sprite. */ sourceSize: { w: number; h: number }; - // The range of the sprites on the big picture + /** The range of the sprites on the big picture */ atlasRegion: { x: number; y: number; w: number; h: number }; - // If there is trimming, the offset of the sprite relative to the original sprite. - offset: { x: number; y: number }; + /** If there is trimming, the offset of the sprite relative to the original sprite. */ + atlasRegionOffset: { x: number; y: number }; region: { x: number; y: number; w: number; h: number }; pivot: { x: number; y: number }; pixelsPerUnit: number; diff --git a/packages/core/src/2d/enums/SpriteMeshType.ts b/packages/core/src/2d/enums/SpriteMeshType.ts deleted file mode 100644 index 1fb8a07a2c..0000000000 --- a/packages/core/src/2d/enums/SpriteMeshType.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * The shape of the sprite when it is rendered. - */ -export enum SpriteMeshType { - Rect, - Polygon -} diff --git a/packages/core/src/2d/sprite/Sprite.ts b/packages/core/src/2d/sprite/Sprite.ts index 52629da34b..12d1717497 100644 --- a/packages/core/src/2d/sprite/Sprite.ts +++ b/packages/core/src/2d/sprite/Sprite.ts @@ -8,19 +8,21 @@ import { Texture2D } from "../../texture/Texture2D"; */ export class Sprite extends RefObject { /** @internal */ - _triangles: number[] = []; + private static rectangleTriangles = [0, 2, 1, 2, 0, 3]; /** @internal */ _uv: Vector2[] = [new Vector2(), new Vector2(), new Vector2(), new Vector2()]; /** @internal */ _positions: Vector2[] = [new Vector2(), new Vector2(), new Vector2(), new Vector2()]; /** @internal */ _bounds: BoundingBox = new BoundingBox(); + /** @internal */ + _triangles: number[]; + private _region: Rect; + private _pivot: Vector2; + private _pixelsPerUnit: number; private _texture: Texture2D = null; private _atlasRegion: Rect = new Rect(0, 0, 1, 1); - private _region: Rect = new Rect(0, 0, 1, 1); - private _pivot: Vector2 = new Vector2(0.5, 0.5); - private _pixelsPerUnit: number; private _dirtyFlag: number = DirtyFlag.all; // If and only if the type(SpriteMeshType) is Rect and trimmed. private _offset: Vector2 = new Vector2(0, 0); @@ -145,13 +147,9 @@ export class Sprite extends RefObject { this.texture = texture; } - if (region) { - this.region = region; - } + this.region = region || new Rect(0, 0, 1, 1); - if (pivot) { - this.pivot = pivot; - } + this.pivot = pivot || new Vector2(0.5, 0.5); this.pixelsPerUnit = pixelsPerUnit; } @@ -174,18 +172,18 @@ export class Sprite extends RefObject { return; } - const { _region, _atlasRegion, _pixelsPerUnit, _offset, _pivot } = this; + const { _offset } = this; const { width, height } = texture; - const { width: regionWidth, height: regionHeight } = _region; - const { width: atlasRegionWidth, height: atlasRegionHeight } = _atlasRegion; - const pixelsPerUnitReciprocal = 1.0 / _pixelsPerUnit; + const { width: regionWidth, height: regionHeight } = this._region; + const { width: atlasRegionWidth, height: atlasRegionHeight } = this._atlasRegion; + const pixelsPerUnitReciprocal = 1.0 / this._pixelsPerUnit; // Get the width and height in 3D space. const unitWidth = atlasRegionWidth * regionWidth * width * pixelsPerUnitReciprocal; const unitHeight = atlasRegionHeight * regionHeight * height * pixelsPerUnitReciprocal; // Get the distance between the anchor point and the four sides. - const { x: px, y: py } = _pivot; + const { x: px, y: py } = this._pivot; const offsetX = _offset.x * pixelsPerUnitReciprocal; const offsetY = _offset.y * pixelsPerUnitReciprocal; const lx = -px * unitWidth + offsetX; @@ -219,19 +217,12 @@ export class Sprite extends RefObject { } if (this._isContainDirtyFlag(DirtyFlag.uv)) { - const uv = this._uv; - const { - x: atlasRegionX, - y: atlasRegionY, - width: atlasRegionWidth, - height: atlasRegionHeight - } = this._atlasRegion; - const { x: regionX, y: regionY, width: regionWidth, height: regionHeight } = this._region; - const realWidth = atlasRegionWidth * regionWidth; - const realheight = atlasRegionHeight * regionHeight; - const left = atlasRegionX + realWidth * regionX; + const { _region: region, _atlasRegion: atlasRegion, _uv: uv } = this; + const realWidth = atlasRegion.width * region.width; + const realheight = atlasRegion.height * region.height; + const left = atlasRegion.x + realWidth * region.x; const right = left + realWidth; - const top = atlasRegionY + realheight * regionY; + const top = atlasRegion.y + realheight * region.y; const bottom = top + realheight; // Top-left. @@ -242,16 +233,19 @@ export class Sprite extends RefObject { uv[2].setValue(right, bottom); // Bottom-left. uv[3].setValue(left, bottom); + + // Top-left. + uv[0].setValue(left, top); + // Top-right. + uv[1].setValue(right, top); + // Bottom-right. + uv[2].setValue(right, bottom); + // Bottom-left. + uv[3].setValue(left, bottom); } if (this._isContainDirtyFlag(DirtyFlag.triangles)) { - const triangles = this._triangles; - triangles[0] = 0; - triangles[1] = 2; - triangles[2] = 1; - triangles[3] = 2; - triangles[4] = 0; - triangles[5] = 3; + this._triangles = Sprite.rectangleTriangles; } } diff --git a/packages/loader/src/SpriteAtlasLoader.ts b/packages/loader/src/SpriteAtlasLoader.ts index eaafc9b303..1d4905e140 100644 --- a/packages/loader/src/SpriteAtlasLoader.ts +++ b/packages/loader/src/SpriteAtlasLoader.ts @@ -21,66 +21,54 @@ class SpriteAtlasLoader extends Loader { type: "json" }) .then((atlasData) => { - const atlasItems = atlasData.AtlasItems; + const { atlasItems, format } = atlasData; const atlasItemsLen = atlasItems.length; - const picsArr: string[] = new Array(atlasItemsLen); - // Load the texture used in the atlas. - for (let idx = atlasItemsLen - 1; idx >= 0; idx--) { - picsArr[idx] = atlasItems[idx].img; - } Promise.all( - picsArr.map((url) => - this.request(url, { + atlasItems.map(({ img }) => + this.request(img, { ...item, type: "image" }) ) ).then((imgs) => { - const engine = resourceManager.engine; + const { engine } = resourceManager; // Generate a SpriteAtlas object const spriteAtlas = new SpriteAtlas(engine); /** @ts-ignore */ const spriteMap = spriteAtlas._spritesMap; - const format = atlasData.format; - const texturesArr: Texture2D[] = new Array(atlasItemsLen); + const texturesArr = new Array(atlasItemsLen); for (let idx = atlasItemsLen - 1; idx >= 0; idx--) { // Generate Texture2D according to configuration const originalImg = imgs[idx]; const { width, height } = originalImg; const texture = new Texture2D(engine, width, height, format); - /** @ts-ignore */ - if (!texture._platformTexture) return; - texture.setImageSource(originalImg); texture.generateMipmaps(); texturesArr[idx] = texture; // Generate all the sprites on this texture. const atlasItem = atlasItems[idx]; - const frames = atlasItem.frames; + const sprites = atlasItem.sprites; const [sourceWidth, sourceHeight] = atlasItem.size; const sourceWidthReciprocal = 1.0 / sourceWidth; const sourceHeightReciprocal = 1.0 / sourceHeight; - for (var key in frames) { - const frame = frames[key]; - const region = frame.region; - const pivot = frame.pivot; - const offset = frame.offset; - const atlasRegion = frame.atlasRegion; + for (let spriteIdx = sprites.length - 1; spriteIdx >= 0; spriteIdx--) { + const atlasSprite = sprites[spriteIdx]; + const { region, pivot, atlasRegionOffset, atlasRegion } = atlasSprite; const sprite = new Sprite( engine, texture, new Rect(region.x, region.y, region.w, region.h), new Vector2(pivot.x, pivot.y), - frame.pixelsPerUnit + atlasSprite.pixelsPerUnit ); - sprite.atlasRegion = new Rect( + sprite.atlasRegion.setValue( atlasRegion.x * sourceWidthReciprocal, atlasRegion.y * sourceHeightReciprocal, atlasRegion.w * sourceWidthReciprocal, atlasRegion.h * sourceHeightReciprocal ); - sprite.offset = new Vector2(offset.x, offset.y); - spriteMap[key] = sprite; + sprite.offset.setValue(atlasRegionOffset.x, atlasRegionOffset.y); + spriteMap[atlasSprite.name] = sprite; } } // Return a SpriteAtlas instance. From de5b981d55c65a63bed12a863199844d7341528f Mon Sep 17 00:00:00 2001 From: azhan Date: Mon, 2 Aug 2021 11:21:31 +0800 Subject: [PATCH 09/72] feat:Support atlas --- packages/core/src/2d/atlas/SpriteAtlas.ts | 22 +++++++++++++++------- packages/loader/src/SpriteAtlasLoader.ts | 5 ++--- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/packages/core/src/2d/atlas/SpriteAtlas.ts b/packages/core/src/2d/atlas/SpriteAtlas.ts index b2d6378a2b..6484ae284f 100644 --- a/packages/core/src/2d/atlas/SpriteAtlas.ts +++ b/packages/core/src/2d/atlas/SpriteAtlas.ts @@ -7,7 +7,14 @@ import { Sprite } from "../sprite/Sprite"; */ export class SpriteAtlas extends RefObject { /** @internal */ - _spritesMap: Record = {}; + _sprites = []; + /** @internal */ + _spriteNamesToIndex: Record = {}; + + /** @internal */ + private _registerSprite(name: string, sprite: Sprite) { + this._spriteNamesToIndex[name] = this._sprites.push(sprite) - 1; + } /** * Get the sprite named 'name' from the atlas. @@ -15,7 +22,7 @@ export class SpriteAtlas extends RefObject { * @returns The sprite you want to find */ public getSprite(name: string): Sprite { - const sprite = this._spritesMap[name]; + const sprite = this._sprites[this._spriteNamesToIndex[name]]; if (!sprite) { console.warn("There is no sprite named " + name + " in the atlas."); } @@ -27,9 +34,9 @@ export class SpriteAtlas extends RefObject { * @returns all the sprites in the atlas */ public getSprites(sprites: Sprite[]): Sprite[] { - const spritesMap = this._spritesMap; - for (let key in spritesMap) { - sprites.push(spritesMap[key]); + const len = this._sprites.length; + for (let i = 0; i < len; i++) { + sprites.push(this._sprites[i]); } return sprites; } @@ -46,8 +53,9 @@ export class SpriteAtlas extends RefObject { * @override */ _onDestroy(): void { - if (this._spritesMap) { - this._spritesMap = null; + if (this._sprites) { + this._sprites = null; + this._spriteNamesToIndex = null; } } } diff --git a/packages/loader/src/SpriteAtlasLoader.ts b/packages/loader/src/SpriteAtlasLoader.ts index 8b3eb53964..45019e188c 100644 --- a/packages/loader/src/SpriteAtlasLoader.ts +++ b/packages/loader/src/SpriteAtlasLoader.ts @@ -34,8 +34,6 @@ class SpriteAtlasLoader extends Loader { const { engine } = resourceManager; // Generate a SpriteAtlas object const spriteAtlas = new SpriteAtlas(engine); - /** @ts-ignore */ - const spriteMap = spriteAtlas._spritesMap; const texturesArr = new Array(atlasItemsLen); for (let idx = atlasItemsLen - 1; idx >= 0; idx--) { // Generate Texture2D according to configuration @@ -68,7 +66,8 @@ class SpriteAtlasLoader extends Loader { atlasRegion.h * sourceHeightReciprocal ); sprite.atlasRegionOffset.setValue(atlasRegionOffset.x, atlasRegionOffset.y); - spriteMap[atlasSprite.name] = sprite; + /** @ts-ignore */ + spriteAtlas._registerSprite(atlasSprite.name, sprite); } } // Return a SpriteAtlas instance. From a0745070b14f0d15b5adcdf1521a394c08e1c674 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Mon, 2 Aug 2021 15:19:23 +0800 Subject: [PATCH 10/72] feat:Support atlas --- packages/core/src/2d/atlas/SpriteAtlas.ts | 38 ++++++--- packages/core/src/2d/sprite/Sprite.ts | 95 +++++++++++------------ packages/loader/src/SpriteAtlasLoader.ts | 7 +- 3 files changed, 77 insertions(+), 63 deletions(-) diff --git a/packages/core/src/2d/atlas/SpriteAtlas.ts b/packages/core/src/2d/atlas/SpriteAtlas.ts index 6484ae284f..6e1545d5d4 100644 --- a/packages/core/src/2d/atlas/SpriteAtlas.ts +++ b/packages/core/src/2d/atlas/SpriteAtlas.ts @@ -7,13 +7,15 @@ import { Sprite } from "../sprite/Sprite"; */ export class SpriteAtlas extends RefObject { /** @internal */ - _sprites = []; + _sprites = new Array(); /** @internal */ _spriteNamesToIndex: Record = {}; - /** @internal */ - private _registerSprite(name: string, sprite: Sprite) { - this._spriteNamesToIndex[name] = this._sprites.push(sprite) - 1; + /** + * All the sprites in the atlas. + */ + public get sprites(): Readonly { + return this._sprites; } /** @@ -30,15 +32,22 @@ export class SpriteAtlas extends RefObject { } /** - * Get all the sprites in the atlas. - * @returns all the sprites in the atlas + * + * @param name - The name of the sprite you want to find + * @param outSprites + * @returns */ - public getSprites(sprites: Sprite[]): Sprite[] { - const len = this._sprites.length; - for (let i = 0; i < len; i++) { - sprites.push(this._sprites[i]); + public getSprites(name: string, outSprites: Sprite[]): Sprite[] { + if (name != null) { + const { _sprites } = this; + for (let index = this._spriteNamesToIndex[name]; index >= 0; index--) { + const sprite = _sprites[index]; + sprite.name == name && outSprites.push(sprite); + } + } else { + console.warn("The name of the sprite you want to find is null."); } - return sprites; + return outSprites; } /** @@ -49,6 +58,13 @@ export class SpriteAtlas extends RefObject { super(engine); } + /** + * @internal + */ + _registerSprite(sprite: Sprite) { + this._spriteNamesToIndex[sprite.name] = this._sprites.push(sprite) - 1; + } + /** * @override */ diff --git a/packages/core/src/2d/sprite/Sprite.ts b/packages/core/src/2d/sprite/Sprite.ts index 18f11ccf8e..312d8bad4e 100644 --- a/packages/core/src/2d/sprite/Sprite.ts +++ b/packages/core/src/2d/sprite/Sprite.ts @@ -7,6 +7,9 @@ import { Texture2D } from "../../texture/Texture2D"; * 2D sprite. */ export class Sprite extends RefObject { + /** The name of sprite. */ + name: string; + /** @internal */ private static rectangleTriangles = [0, 2, 1, 2, 0, 3]; /** @internal */ @@ -77,7 +80,7 @@ export class Sprite extends RefObject { } set atlasRegionOffset(value: Vector2) { - this._atlasRegionOffset.setValue(value.x, value.y); + this._atlasRegionOffset.setValue(MathUtil.clamp(value.x, 0, 1), MathUtil.clamp(value.y, 0, 1)); this._setDirtyFlagTrue(DirtyFlag.positions); } @@ -138,7 +141,8 @@ export class Sprite extends RefObject { texture: Texture2D = null, region: Rect = null, pivot: Vector2 = null, - pixelsPerUnit: number = 128 + pixelsPerUnit: number = 128, + name: string = null ) { super(engine); @@ -151,6 +155,8 @@ export class Sprite extends RefObject { this.pivot = pivot || new Vector2(0.5, 0.5); this.pixelsPerUnit = pixelsPerUnit; + + this.name = name; } /** @@ -167,44 +173,44 @@ export class Sprite extends RefObject { */ private _updatePositionsAndBounds(): void { const { texture } = this; - if (!texture) { - return; - } - - const { _atlasRegionOffset } = this; - const { width, height } = texture; - const { width: regionWidth, height: regionHeight } = this._region; - const { width: atlasRegionWidth, height: atlasRegionHeight } = this._atlasRegion; - const pixelsPerUnitReciprocal = 1.0 / this._pixelsPerUnit; - - // Get the width and height in 3D space. - const unitWidth = atlasRegionWidth * regionWidth * width * pixelsPerUnitReciprocal; - const unitHeight = atlasRegionHeight * regionHeight * height * pixelsPerUnitReciprocal; - - // Get the distance between the anchor point and the four sides. - const { x: px, y: py } = this._pivot; - const offsetX = _atlasRegionOffset.x * pixelsPerUnitReciprocal; - const offsetY = _atlasRegionOffset.y * pixelsPerUnitReciprocal; - const lx = -px * unitWidth + offsetX; - const ty = -py * unitHeight + offsetY; - const rx = (1 - px) * unitWidth + offsetX; - const by = (1 - py) * unitHeight + offsetY; - - // Assign values ​​to _positions - const positions = this._positions; - // Top-left. - positions[0].setValue(lx, by); - // Top-right. - positions[1].setValue(rx, by); - // Bottom-right. - positions[2].setValue(rx, ty); - // Bottom-left. - positions[3].setValue(lx, ty); - - // Update bounds. const { min, max } = this._bounds; - min.setValue(lx, ty, 0); - max.setValue(rx, by, 0); + if (texture) { + const { _atlasRegionOffset } = this; + const { width, height } = texture; + const { width: regionWidth, height: regionHeight } = this._region; + const { width: atlasRegionWidth, height: atlasRegionHeight } = this._atlasRegion; + const pixelsPerUnitReciprocal = 1.0 / this._pixelsPerUnit; + + // Get the width and height in 3D space. + const unitWidth = atlasRegionWidth * regionWidth * width * pixelsPerUnitReciprocal; + const unitHeight = atlasRegionHeight * regionHeight * height * pixelsPerUnitReciprocal; + + // Get the distance between the anchor point and the four sides. + const { x: px, y: py } = this._pivot; + const lx = (-px + _atlasRegionOffset.x) * unitWidth; + const ty = (-py + _atlasRegionOffset.y) * unitHeight; + const rx = unitWidth + lx; + const by = unitHeight + ty; + + // Assign values ​​to _positions + const positions = this._positions; + // Top-left. + positions[0].setValue(lx, by); + // Top-right. + positions[1].setValue(rx, by); + // Bottom-right. + positions[2].setValue(rx, ty); + // Bottom-left. + positions[3].setValue(lx, ty); + + // Update bounds. + min.setValue(lx, ty, 0); + max.setValue(rx, by, 0); + } else { + // Update bounds. + min.setValue(0, 0, 0); + max.setValue(0, 0, 0); + } } /** @@ -220,8 +226,8 @@ export class Sprite extends RefObject { const realWidth = atlasRegion.width * region.width; const realheight = atlasRegion.height * region.height; const left = atlasRegion.x + realWidth * region.x; - const right = left + realWidth; const top = atlasRegion.y + realheight * region.y; + const right = left + realWidth; const bottom = top + realheight; // Top-left. @@ -232,15 +238,6 @@ export class Sprite extends RefObject { uv[2].setValue(right, bottom); // Bottom-left. uv[3].setValue(left, bottom); - - // Top-left. - uv[0].setValue(left, top); - // Top-right. - uv[1].setValue(right, top); - // Bottom-right. - uv[2].setValue(right, bottom); - // Bottom-left. - uv[3].setValue(left, bottom); } if (this._isContainDirtyFlag(DirtyFlag.triangles)) { diff --git a/packages/loader/src/SpriteAtlasLoader.ts b/packages/loader/src/SpriteAtlasLoader.ts index 45019e188c..77ae7d498c 100644 --- a/packages/loader/src/SpriteAtlasLoader.ts +++ b/packages/loader/src/SpriteAtlasLoader.ts @@ -35,7 +35,7 @@ class SpriteAtlasLoader extends Loader { // Generate a SpriteAtlas object const spriteAtlas = new SpriteAtlas(engine); const texturesArr = new Array(atlasItemsLen); - for (let idx = atlasItemsLen - 1; idx >= 0; idx--) { + for (let idx = 0; idx < atlasItemsLen; idx++) { // Generate Texture2D according to configuration const originalImg = imgs[idx]; const { width, height } = originalImg; @@ -57,7 +57,8 @@ class SpriteAtlasLoader extends Loader { texture, new Rect(region.x, region.y, region.w, region.h), new Vector2(pivot.x, pivot.y), - atlasSprite.pixelsPerUnit + atlasSprite.pixelsPerUnit, + atlasSprite.name ); sprite.atlasRegion.setValue( atlasRegion.x * sourceWidthReciprocal, @@ -67,7 +68,7 @@ class SpriteAtlasLoader extends Loader { ); sprite.atlasRegionOffset.setValue(atlasRegionOffset.x, atlasRegionOffset.y); /** @ts-ignore */ - spriteAtlas._registerSprite(atlasSprite.name, sprite); + spriteAtlas._registerSprite(sprite); } } // Return a SpriteAtlas instance. From 1d0a217a358fd94530bddcfc1bf9daec4189ed41 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Mon, 2 Aug 2021 15:25:56 +0800 Subject: [PATCH 11/72] feat:Support atlas --- packages/core/src/2d/atlas/SpriteAtlas.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/src/2d/atlas/SpriteAtlas.ts b/packages/core/src/2d/atlas/SpriteAtlas.ts index 6e1545d5d4..b6b8751ef9 100644 --- a/packages/core/src/2d/atlas/SpriteAtlas.ts +++ b/packages/core/src/2d/atlas/SpriteAtlas.ts @@ -19,7 +19,7 @@ export class SpriteAtlas extends RefObject { } /** - * Get the sprite named 'name' from the atlas. + * Get the last sprite named 'name' from the atlas. * @param name - The name of the sprite you want to find * @returns The sprite you want to find */ @@ -32,10 +32,10 @@ export class SpriteAtlas extends RefObject { } /** - * - * @param name - The name of the sprite you want to find - * @param outSprites - * @returns + * Get all the sprite named 'name' from the atlas. + * @param name - The name of the sprites you want to find + * @param outSprites - This array holds the sprites found + * @returns The sprites you want to find */ public getSprites(name: string, outSprites: Sprite[]): Sprite[] { if (name != null) { From c9c6d7b8a37e4f1aee2e88707dcc5483fb8bd69c Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Mon, 2 Aug 2021 15:28:45 +0800 Subject: [PATCH 12/72] feat:Support atlas --- packages/core/src/2d/sprite/Sprite.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/2d/sprite/Sprite.ts b/packages/core/src/2d/sprite/Sprite.ts index 312d8bad4e..1f24c1b7c0 100644 --- a/packages/core/src/2d/sprite/Sprite.ts +++ b/packages/core/src/2d/sprite/Sprite.ts @@ -73,7 +73,7 @@ export class Sprite extends RefObject { } /** - * The rectangle region offset of the original texture on its atlas texture. + * The rectangle region offset of the original texture on its atlas texture, specified in normalized. */ get atlasRegionOffset(): Vector2 { return this._atlasRegionOffset; From 2978b445bbab39e5eb8fdd9847e538432271adbe Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Mon, 2 Aug 2021 17:19:09 +0800 Subject: [PATCH 13/72] feat:Support atlas --- packages/core/src/2d/atlas/SpriteAtlas.ts | 8 ++++---- packages/core/src/2d/sprite/Sprite.ts | 18 +++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/core/src/2d/atlas/SpriteAtlas.ts b/packages/core/src/2d/atlas/SpriteAtlas.ts index b6b8751ef9..ca86dda8db 100644 --- a/packages/core/src/2d/atlas/SpriteAtlas.ts +++ b/packages/core/src/2d/atlas/SpriteAtlas.ts @@ -7,14 +7,14 @@ import { Sprite } from "../sprite/Sprite"; */ export class SpriteAtlas extends RefObject { /** @internal */ - _sprites = new Array(); + _sprites: Sprite[] = new Array(); /** @internal */ _spriteNamesToIndex: Record = {}; /** * All the sprites in the atlas. */ - public get sprites(): Readonly { + get sprites(): Readonly { return this._sprites; } @@ -23,7 +23,7 @@ export class SpriteAtlas extends RefObject { * @param name - The name of the sprite you want to find * @returns The sprite you want to find */ - public getSprite(name: string): Sprite { + getSprite(name: string): Sprite { const sprite = this._sprites[this._spriteNamesToIndex[name]]; if (!sprite) { console.warn("There is no sprite named " + name + " in the atlas."); @@ -37,7 +37,7 @@ export class SpriteAtlas extends RefObject { * @param outSprites - This array holds the sprites found * @returns The sprites you want to find */ - public getSprites(name: string, outSprites: Sprite[]): Sprite[] { + getSprites(name: string, outSprites: Sprite[]): Sprite[] { if (name != null) { const { _sprites } = this; for (let index = this._spriteNamesToIndex[name]; index >= 0; index--) { diff --git a/packages/core/src/2d/sprite/Sprite.ts b/packages/core/src/2d/sprite/Sprite.ts index 1f24c1b7c0..ce78b005c6 100644 --- a/packages/core/src/2d/sprite/Sprite.ts +++ b/packages/core/src/2d/sprite/Sprite.ts @@ -7,11 +7,11 @@ import { Texture2D } from "../../texture/Texture2D"; * 2D sprite. */ export class Sprite extends RefObject { + private static _rectangleTriangles: number[] = [0, 2, 1, 2, 0, 3]; + /** The name of sprite. */ name: string; - /** @internal */ - private static rectangleTriangles = [0, 2, 1, 2, 0, 3]; /** @internal */ _uv: Vector2[] = [new Vector2(), new Vector2(), new Vector2(), new Vector2()]; /** @internal */ @@ -21,13 +21,13 @@ export class Sprite extends RefObject { /** @internal */ _triangles: number[]; - private _region: Rect; - private _pivot: Vector2; private _pixelsPerUnit: number; private _texture: Texture2D = null; + private _region: Rect = new Rect(0, 0, 1, 1); + private _pivot: Vector2 = new Vector2(0.5, 0.5); private _atlasRegion: Rect = new Rect(0, 0, 1, 1); private _atlasRegionOffset: Vector2 = new Vector2(0, 0); - private _dirtyFlag: number = DirtyFlag.all; + private _dirtyFlag: DirtyFlag = DirtyFlag.all; /** * The reference to the used texture. @@ -73,7 +73,7 @@ export class Sprite extends RefObject { } /** - * The rectangle region offset of the original texture on its atlas texture, specified in normalized. + * The rectangle region offset of the original texture on its atlas texture, specified in normalized. */ get atlasRegionOffset(): Vector2 { return this._atlasRegionOffset; @@ -150,9 +150,9 @@ export class Sprite extends RefObject { this.texture = texture; } - this.region = region || new Rect(0, 0, 1, 1); + region && region.cloneTo(this._region); - this.pivot = pivot || new Vector2(0.5, 0.5); + pivot && pivot.cloneTo(this._pivot); this.pixelsPerUnit = pixelsPerUnit; @@ -241,7 +241,7 @@ export class Sprite extends RefObject { } if (this._isContainDirtyFlag(DirtyFlag.triangles)) { - this._triangles = Sprite.rectangleTriangles; + this._triangles = Sprite._rectangleTriangles; } } From 485e55020e96bbe8f58ec7db1a6784839817b393 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Mon, 2 Aug 2021 17:33:48 +0800 Subject: [PATCH 14/72] feat:Support atlas --- packages/core/src/2d/atlas/SpriteAtlas.ts | 8 ++++---- packages/loader/src/SpriteAtlasLoader.ts | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/core/src/2d/atlas/SpriteAtlas.ts b/packages/core/src/2d/atlas/SpriteAtlas.ts index ca86dda8db..e33aaeee3c 100644 --- a/packages/core/src/2d/atlas/SpriteAtlas.ts +++ b/packages/core/src/2d/atlas/SpriteAtlas.ts @@ -40,9 +40,9 @@ export class SpriteAtlas extends RefObject { getSprites(name: string, outSprites: Sprite[]): Sprite[] { if (name != null) { const { _sprites } = this; - for (let index = this._spriteNamesToIndex[name]; index >= 0; index--) { - const sprite = _sprites[index]; - sprite.name == name && outSprites.push(sprite); + for (let i = this._spriteNamesToIndex[name]; i >= 0; i--) { + const sprite = _sprites[i]; + sprite.name === name && outSprites.push(sprite); } } else { console.warn("The name of the sprite you want to find is null."); @@ -61,7 +61,7 @@ export class SpriteAtlas extends RefObject { /** * @internal */ - _registerSprite(sprite: Sprite) { + _registerSprite(sprite: Sprite): void { this._spriteNamesToIndex[sprite.name] = this._sprites.push(sprite) - 1; } diff --git a/packages/loader/src/SpriteAtlasLoader.ts b/packages/loader/src/SpriteAtlasLoader.ts index 77ae7d498c..640f183ed6 100644 --- a/packages/loader/src/SpriteAtlasLoader.ts +++ b/packages/loader/src/SpriteAtlasLoader.ts @@ -35,22 +35,22 @@ class SpriteAtlasLoader extends Loader { // Generate a SpriteAtlas object const spriteAtlas = new SpriteAtlas(engine); const texturesArr = new Array(atlasItemsLen); - for (let idx = 0; idx < atlasItemsLen; idx++) { + for (let i = 0; i < atlasItemsLen; i++) { // Generate Texture2D according to configuration - const originalImg = imgs[idx]; + const originalImg = imgs[i]; const { width, height } = originalImg; const texture = new Texture2D(engine, width, height, format); texture.setImageSource(originalImg); texture.generateMipmaps(); - texturesArr[idx] = texture; + texturesArr[i] = texture; // Generate all the sprites on this texture. - const atlasItem = atlasItems[idx]; + const atlasItem = atlasItems[i]; const sprites = atlasItem.sprites; const [sourceWidth, sourceHeight] = atlasItem.size; const sourceWidthReciprocal = 1.0 / sourceWidth; const sourceHeightReciprocal = 1.0 / sourceHeight; - for (let spriteIdx = sprites.length - 1; spriteIdx >= 0; spriteIdx--) { - const atlasSprite = sprites[spriteIdx]; + for (let j = sprites.length - 1; j >= 0; j--) { + const atlasSprite = sprites[j]; const { region, pivot, atlasRegionOffset, atlasRegion } = atlasSprite; const sprite = new Sprite( engine, From e79604e66106a7f9a009a176699ae62d47e8d8d7 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Mon, 2 Aug 2021 17:38:25 +0800 Subject: [PATCH 15/72] feat:Support atlas --- packages/core/src/2d/atlas/SpriteAtlas.ts | 2 +- packages/loader/src/SpriteAtlasLoader.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/2d/atlas/SpriteAtlas.ts b/packages/core/src/2d/atlas/SpriteAtlas.ts index e33aaeee3c..017e6cbf01 100644 --- a/packages/core/src/2d/atlas/SpriteAtlas.ts +++ b/packages/core/src/2d/atlas/SpriteAtlas.ts @@ -61,7 +61,7 @@ export class SpriteAtlas extends RefObject { /** * @internal */ - _registerSprite(sprite: Sprite): void { + _addSprite(sprite: Sprite): void { this._spriteNamesToIndex[sprite.name] = this._sprites.push(sprite) - 1; } diff --git a/packages/loader/src/SpriteAtlasLoader.ts b/packages/loader/src/SpriteAtlasLoader.ts index 640f183ed6..a221f408f8 100644 --- a/packages/loader/src/SpriteAtlasLoader.ts +++ b/packages/loader/src/SpriteAtlasLoader.ts @@ -68,7 +68,7 @@ class SpriteAtlasLoader extends Loader { ); sprite.atlasRegionOffset.setValue(atlasRegionOffset.x, atlasRegionOffset.y); /** @ts-ignore */ - spriteAtlas._registerSprite(sprite); + spriteAtlas._addSprite(sprite); } } // Return a SpriteAtlas instance. From f1bfb2c15f326a9f454af414277763968fd0f0d5 Mon Sep 17 00:00:00 2001 From: GuoLei1990 Date: Mon, 2 Aug 2021 21:01:37 +0800 Subject: [PATCH 16/72] refactor: opt code --- packages/core/src/2d/atlas/SpriteAtlas.ts | 20 ++++---- packages/core/src/2d/atlas/types.ts | 23 +++------ packages/core/src/2d/sprite/Sprite.ts | 59 +++++++++-------------- packages/loader/src/SpriteAtlasLoader.ts | 18 ++++--- 4 files changed, 51 insertions(+), 69 deletions(-) diff --git a/packages/core/src/2d/atlas/SpriteAtlas.ts b/packages/core/src/2d/atlas/SpriteAtlas.ts index 017e6cbf01..6410da4126 100644 --- a/packages/core/src/2d/atlas/SpriteAtlas.ts +++ b/packages/core/src/2d/atlas/SpriteAtlas.ts @@ -6,10 +6,8 @@ import { Sprite } from "../sprite/Sprite"; * Sprite Atlas. */ export class SpriteAtlas extends RefObject { - /** @internal */ - _sprites: Sprite[] = new Array(); - /** @internal */ - _spriteNamesToIndex: Record = {}; + private _sprites: Sprite[] = new Array(); + private _spriteNamesToIndex: Record = {}; /** * All the sprites in the atlas. @@ -38,14 +36,16 @@ export class SpriteAtlas extends RefObject { * @returns The sprites you want to find */ getSprites(name: string, outSprites: Sprite[]): Sprite[] { - if (name != null) { + outSprites.length = 0; + let i = this._spriteNamesToIndex[name]; + if (i !== undefined) { const { _sprites } = this; - for (let i = this._spriteNamesToIndex[name]; i >= 0; i--) { + for (; i >= 0; i--) { const sprite = _sprites[i]; sprite.name === name && outSprites.push(sprite); } } else { - console.warn("The name of the sprite you want to find is null."); + console.warn("The name of the sprite you want to find is not exit in SpriteAtlas."); } return outSprites; } @@ -69,9 +69,7 @@ export class SpriteAtlas extends RefObject { * @override */ _onDestroy(): void { - if (this._sprites) { - this._sprites = null; - this._spriteNamesToIndex = null; - } + this._sprites = null; + this._spriteNamesToIndex = null; } } diff --git a/packages/core/src/2d/atlas/types.ts b/packages/core/src/2d/atlas/types.ts index f647f2e170..8bc8693cc1 100644 --- a/packages/core/src/2d/atlas/types.ts +++ b/packages/core/src/2d/atlas/types.ts @@ -4,19 +4,17 @@ import { TextureFormat } from "../../texture"; * The original data type of the atlas. */ export interface AtlasConfig { - /** The big picture array, each big picture contains multiple sprites. */ + /** Version of Atlas. */ + version: number; + /** Texture format. */ + format: TextureFormat; + /** The sub atlas array, each sub atlas contains multiple sprites. */ atlasItems: { - /** The url of the big picture. */ + /** The url of the sub atlas. */ img: string; - /** The size of the big picture. */ - size: number[]; - /** Sprites contained in the big picture. */ + /** Sprites contained in the sub atlas. */ sprites: AtlasSprite[]; }[]; - /** Version of Atlas */ - version: number; - /** Texture format. */ - format: TextureFormat; } /** @@ -25,16 +23,11 @@ export interface AtlasConfig { export interface AtlasSprite { /** The name the sprite. */ name: string; - /** The original size of the sprite. */ - sourceSize: { w: number; h: number }; - /** The range of the sprites on the big picture */ + /** The range of the sprites on the big picture. */ atlasRegion: { x: number; y: number; w: number; h: number }; /** If there is trimming, the offset of the sprite relative to the original sprite. */ atlasRegionOffset: { x: number; y: number }; region: { x: number; y: number; w: number; h: number }; pivot: { x: number; y: number }; pixelsPerUnit: number; - vertices: number[][]; - uv: number[][]; - triangles: number[]; } diff --git a/packages/core/src/2d/sprite/Sprite.ts b/packages/core/src/2d/sprite/Sprite.ts index ce78b005c6..4e93c581da 100644 --- a/packages/core/src/2d/sprite/Sprite.ts +++ b/packages/core/src/2d/sprite/Sprite.ts @@ -70,6 +70,7 @@ export class Sprite extends RefObject { MathUtil.clamp(value.width, 0, 1 - atlasRegion.x), MathUtil.clamp(value.height, 0, 1 - atlasRegion.y) ); + this._setDirtyFlagTrue(DirtyFlag.positions); } /** @@ -129,12 +130,13 @@ export class Sprite extends RefObject { } /** - * Constructor a sprite. + * Constructor a Sprite. * @param engine - Engine to which the sprite belongs - * @param texture - Texture from which to obtain the sprite - * @param region - Rectangle region of the texture to use for the sprite, specified in normalized + * @param texture - Texture from which to obtain the Sprite + * @param region - Rectangle region of the texture to use for the Sprite, specified in normalized * @param pivot - Sprite's pivot point relative to its graphic rectangle, specified in normalized - * @param pixelsPerUnit - The number of pixels in the sprite that correspond to one unit in world space + * @param pixelsPerUnit - The number of pixels in the Sprite that correspond to one unit in world space + * @param name - The name of Sprite */ constructor( engine: Engine, @@ -146,17 +148,14 @@ export class Sprite extends RefObject { ) { super(engine); - if (texture) { - this.texture = texture; - } + this.name = name; + this._texture = texture; + this._pixelsPerUnit = pixelsPerUnit; region && region.cloneTo(this._region); - pivot && pivot.cloneTo(this._pivot); - this.pixelsPerUnit = pixelsPerUnit; - - this.name = name; + this._triangles = Sprite._rectangleTriangles; } /** @@ -172,23 +171,18 @@ export class Sprite extends RefObject { * Update positions and bounds. */ private _updatePositionsAndBounds(): void { - const { texture } = this; - const { min, max } = this._bounds; + const { texture, _bounds: bounds } = this; if (texture) { - const { _atlasRegionOffset } = this; - const { width, height } = texture; - const { width: regionWidth, height: regionHeight } = this._region; - const { width: atlasRegionWidth, height: atlasRegionHeight } = this._atlasRegion; + const { _atlasRegionOffset: atlasRegionOffset, _atlasRegion: atlasRegion, _region: region, _pivot: pivot } = this; const pixelsPerUnitReciprocal = 1.0 / this._pixelsPerUnit; // Get the width and height in 3D space. - const unitWidth = atlasRegionWidth * regionWidth * width * pixelsPerUnitReciprocal; - const unitHeight = atlasRegionHeight * regionHeight * height * pixelsPerUnitReciprocal; + const unitWidth = atlasRegion.width * region.width * texture.width * pixelsPerUnitReciprocal; + const unitHeight = atlasRegion.height * region.height * texture.height * pixelsPerUnitReciprocal; // Get the distance between the anchor point and the four sides. - const { x: px, y: py } = this._pivot; - const lx = (-px + _atlasRegionOffset.x) * unitWidth; - const ty = (-py + _atlasRegionOffset.y) * unitHeight; + const lx = (-pivot.x + atlasRegionOffset.x) * unitWidth; + const ty = (-pivot.y + atlasRegionOffset.y) * unitHeight; const rx = unitWidth + lx; const by = unitHeight + ty; @@ -204,12 +198,12 @@ export class Sprite extends RefObject { positions[3].setValue(lx, ty); // Update bounds. - min.setValue(lx, ty, 0); - max.setValue(rx, by, 0); + bounds.min.setValue(lx, ty, 0); + bounds.max.setValue(rx, by, 0); } else { // Update bounds. - min.setValue(0, 0, 0); - max.setValue(0, 0, 0); + bounds.min.setValue(0, 0, 0); + bounds.max.setValue(0, 0, 0); } } @@ -224,11 +218,11 @@ export class Sprite extends RefObject { if (this._isContainDirtyFlag(DirtyFlag.uv)) { const { _region: region, _atlasRegion: atlasRegion, _uv: uv } = this; const realWidth = atlasRegion.width * region.width; - const realheight = atlasRegion.height * region.height; + const realHeight = atlasRegion.height * region.height; const left = atlasRegion.x + realWidth * region.x; - const top = atlasRegion.y + realheight * region.y; + const top = atlasRegion.y + realHeight * region.y; const right = left + realWidth; - const bottom = top + realheight; + const bottom = top + realHeight; // Top-left. uv[0].setValue(left, top); @@ -239,10 +233,6 @@ export class Sprite extends RefObject { // Bottom-left. uv[3].setValue(left, bottom); } - - if (this._isContainDirtyFlag(DirtyFlag.triangles)) { - this._triangles = Sprite._rectangleTriangles; - } } /** @@ -275,6 +265,5 @@ export class Sprite extends RefObject { enum DirtyFlag { positions = 0x1, uv = 0x2, - triangles = 0x4, - all = 0x7 + all = 0x3 } diff --git a/packages/loader/src/SpriteAtlasLoader.ts b/packages/loader/src/SpriteAtlasLoader.ts index a221f408f8..4581e530b5 100644 --- a/packages/loader/src/SpriteAtlasLoader.ts +++ b/packages/loader/src/SpriteAtlasLoader.ts @@ -33,8 +33,9 @@ class SpriteAtlasLoader extends Loader { ).then((imgs) => { const { engine } = resourceManager; // Generate a SpriteAtlas object + const tempRect = new Rect(); + const tempPivot = new Vector2(); const spriteAtlas = new SpriteAtlas(engine); - const texturesArr = new Array(atlasItemsLen); for (let i = 0; i < atlasItemsLen; i++) { // Generate Texture2D according to configuration const originalImg = imgs[i]; @@ -42,21 +43,23 @@ class SpriteAtlasLoader extends Loader { const texture = new Texture2D(engine, width, height, format); texture.setImageSource(originalImg); texture.generateMipmaps(); - texturesArr[i] = texture; // Generate all the sprites on this texture. const atlasItem = atlasItems[i]; const sprites = atlasItem.sprites; - const [sourceWidth, sourceHeight] = atlasItem.size; - const sourceWidthReciprocal = 1.0 / sourceWidth; - const sourceHeightReciprocal = 1.0 / sourceHeight; + const sourceWidthReciprocal = 1.0 / width; + const sourceHeightReciprocal = 1.0 / height; for (let j = sprites.length - 1; j >= 0; j--) { const atlasSprite = sprites[j]; const { region, pivot, atlasRegionOffset, atlasRegion } = atlasSprite; + + tempRect.setValue(region.x, region.y, region.w, region.h); + tempPivot.setValue(pivot.x, pivot.y); + const sprite = new Sprite( engine, texture, - new Rect(region.x, region.y, region.w, region.h), - new Vector2(pivot.x, pivot.y), + tempRect, + tempPivot, atlasSprite.pixelsPerUnit, atlasSprite.name ); @@ -71,7 +74,6 @@ class SpriteAtlasLoader extends Loader { spriteAtlas._addSprite(sprite); } } - // Return a SpriteAtlas instance. resolve(spriteAtlas); }); }) From 155933cd766159c3a03c557aba7012e9b5ec8811 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Fri, 6 Aug 2021 16:04:44 +0800 Subject: [PATCH 17/72] fix:opt atlas code --- packages/core/src/2d/sprite/Sprite.ts | 28 ++++++++++++------------ packages/loader/src/SpriteAtlasLoader.ts | 14 +++++------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/packages/core/src/2d/sprite/Sprite.ts b/packages/core/src/2d/sprite/Sprite.ts index 4e93c581da..cfa914f3db 100644 --- a/packages/core/src/2d/sprite/Sprite.ts +++ b/packages/core/src/2d/sprite/Sprite.ts @@ -67,8 +67,8 @@ export class Sprite extends RefObject { atlasRegion.setValue( MathUtil.clamp(value.x, 0, 1), MathUtil.clamp(value.y, 0, 1), - MathUtil.clamp(value.width, 0, 1 - atlasRegion.x), - MathUtil.clamp(value.height, 0, 1 - atlasRegion.y) + MathUtil.clamp(value.width, 0, 1 - value.x), + MathUtil.clamp(value.height, 0, 1 - value.y) ); this._setDirtyFlagTrue(DirtyFlag.positions); } @@ -109,8 +109,8 @@ export class Sprite extends RefObject { region.setValue( MathUtil.clamp(value.x, 0, 1), MathUtil.clamp(value.y, 0, 1), - MathUtil.clamp(value.width, 0, 1 - region.x), - MathUtil.clamp(value.height, 0, 1 - region.y) + MathUtil.clamp(value.width, 0, 1 - value.x), + MathUtil.clamp(value.height, 0, 1 - value.y) ); this._setDirtyFlagTrue(DirtyFlag.positions | DirtyFlag.uv); } @@ -182,24 +182,24 @@ export class Sprite extends RefObject { // Get the distance between the anchor point and the four sides. const lx = (-pivot.x + atlasRegionOffset.x) * unitWidth; - const ty = (-pivot.y + atlasRegionOffset.y) * unitHeight; + const by = (-pivot.y + atlasRegionOffset.y) * unitHeight; const rx = unitWidth + lx; - const by = unitHeight + ty; + const ty = unitHeight + by; // Assign values ​​to _positions const positions = this._positions; // Top-left. - positions[0].setValue(lx, by); + positions[0].setValue(lx, ty); // Top-right. - positions[1].setValue(rx, by); + positions[1].setValue(rx, ty); // Bottom-right. - positions[2].setValue(rx, ty); + positions[2].setValue(rx, by); // Bottom-left. - positions[3].setValue(lx, ty); + positions[3].setValue(lx, by); // Update bounds. - bounds.min.setValue(lx, ty, 0); - bounds.max.setValue(rx, by, 0); + bounds.min.setValue(lx, by, 0); + bounds.max.setValue(rx, ty, 0); } else { // Update bounds. bounds.min.setValue(0, 0, 0); @@ -219,8 +219,8 @@ export class Sprite extends RefObject { const { _region: region, _atlasRegion: atlasRegion, _uv: uv } = this; const realWidth = atlasRegion.width * region.width; const realHeight = atlasRegion.height * region.height; - const left = atlasRegion.x + realWidth * region.x; - const top = atlasRegion.y + realHeight * region.y; + const left = atlasRegion.x + atlasRegion.width * region.x; + const top = atlasRegion.y + atlasRegion.height * region.y; const right = left + realWidth; const bottom = top + realHeight; diff --git a/packages/loader/src/SpriteAtlasLoader.ts b/packages/loader/src/SpriteAtlasLoader.ts index 4581e530b5..9216ee3c36 100644 --- a/packages/loader/src/SpriteAtlasLoader.ts +++ b/packages/loader/src/SpriteAtlasLoader.ts @@ -51,16 +51,12 @@ class SpriteAtlasLoader extends Loader { for (let j = sprites.length - 1; j >= 0; j--) { const atlasSprite = sprites[j]; const { region, pivot, atlasRegionOffset, atlasRegion } = atlasSprite; - - tempRect.setValue(region.x, region.y, region.w, region.h); - tempPivot.setValue(pivot.x, pivot.y); - const sprite = new Sprite( engine, texture, - tempRect, - tempPivot, - atlasSprite.pixelsPerUnit, + region ? tempRect.setValue(region.x, region.y, region.w, region.h) : null, + pivot ? tempPivot.setValue(pivot.x, pivot.y) : null, + atlasSprite.pixelsPerUnit || 128, atlasSprite.name ); sprite.atlasRegion.setValue( @@ -69,7 +65,9 @@ class SpriteAtlasLoader extends Loader { atlasRegion.w * sourceWidthReciprocal, atlasRegion.h * sourceHeightReciprocal ); - sprite.atlasRegionOffset.setValue(atlasRegionOffset.x, atlasRegionOffset.y); + if (atlasRegionOffset) { + sprite.atlasRegionOffset.setValue(atlasRegionOffset.x, atlasRegionOffset.y); + } /** @ts-ignore */ spriteAtlas._addSprite(sprite); } From c10c61cd6447513b22c95b5ea0bbfb100427063d Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Fri, 6 Aug 2021 17:43:01 +0800 Subject: [PATCH 18/72] fix:opt atlas code --- packages/core/src/2d/sprite/Sprite.ts | 11 +++++------ packages/loader/src/SpriteAtlasLoader.ts | 10 ++++------ 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/packages/core/src/2d/sprite/Sprite.ts b/packages/core/src/2d/sprite/Sprite.ts index cfa914f3db..429b48cd2d 100644 --- a/packages/core/src/2d/sprite/Sprite.ts +++ b/packages/core/src/2d/sprite/Sprite.ts @@ -217,12 +217,11 @@ export class Sprite extends RefObject { if (this._isContainDirtyFlag(DirtyFlag.uv)) { const { _region: region, _atlasRegion: atlasRegion, _uv: uv } = this; - const realWidth = atlasRegion.width * region.width; - const realHeight = atlasRegion.height * region.height; - const left = atlasRegion.x + atlasRegion.width * region.x; - const top = atlasRegion.y + atlasRegion.height * region.y; - const right = left + realWidth; - const bottom = top + realHeight; + const { width: atlasRegionWidth, height: atlasRegionHeight } = atlasRegion; + const left = atlasRegion.x + atlasRegionWidth * region.x; + const top = atlasRegion.y + atlasRegionHeight * region.y; + const right = left + atlasRegionWidth * region.width; + const bottom = top + atlasRegionHeight * region.height; // Top-left. uv[0].setValue(left, top); diff --git a/packages/loader/src/SpriteAtlasLoader.ts b/packages/loader/src/SpriteAtlasLoader.ts index 9216ee3c36..646c3bd8cf 100644 --- a/packages/loader/src/SpriteAtlasLoader.ts +++ b/packages/loader/src/SpriteAtlasLoader.ts @@ -54,9 +54,9 @@ class SpriteAtlasLoader extends Loader { const sprite = new Sprite( engine, texture, - region ? tempRect.setValue(region.x, region.y, region.w, region.h) : null, - pivot ? tempPivot.setValue(pivot.x, pivot.y) : null, - atlasSprite.pixelsPerUnit || 128, + region ? tempRect.setValue(region.x, region.y, region.w, region.h) : undefined, + pivot ? tempPivot.setValue(pivot.x, pivot.y) : undefined, + atlasSprite.pixelsPerUnit || undefined, atlasSprite.name ); sprite.atlasRegion.setValue( @@ -65,9 +65,7 @@ class SpriteAtlasLoader extends Loader { atlasRegion.w * sourceWidthReciprocal, atlasRegion.h * sourceHeightReciprocal ); - if (atlasRegionOffset) { - sprite.atlasRegionOffset.setValue(atlasRegionOffset.x, atlasRegionOffset.y); - } + atlasRegionOffset && sprite.atlasRegionOffset.setValue(atlasRegionOffset.x, atlasRegionOffset.y); /** @ts-ignore */ spriteAtlas._addSprite(sprite); } From 5e65294ad51b4d8b8bcfc8fff235e669540d10a2 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Fri, 6 Aug 2021 17:53:37 +0800 Subject: [PATCH 19/72] fix:opt atlas code --- packages/core/src/2d/sprite/Sprite.ts | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/packages/core/src/2d/sprite/Sprite.ts b/packages/core/src/2d/sprite/Sprite.ts index 429b48cd2d..c770214fb6 100644 --- a/packages/core/src/2d/sprite/Sprite.ts +++ b/packages/core/src/2d/sprite/Sprite.ts @@ -64,12 +64,9 @@ export class Sprite extends RefObject { set atlasRegion(value: Rect) { const atlasRegion = this._atlasRegion; - atlasRegion.setValue( - MathUtil.clamp(value.x, 0, 1), - MathUtil.clamp(value.y, 0, 1), - MathUtil.clamp(value.width, 0, 1 - value.x), - MathUtil.clamp(value.height, 0, 1 - value.y) - ); + const x = MathUtil.clamp(value.x, 0, 1); + const y = MathUtil.clamp(value.y, 0, 1); + atlasRegion.setValue(x, y, MathUtil.clamp(value.width, 0, 1 - x), MathUtil.clamp(value.height, 0, 1 - y)); this._setDirtyFlagTrue(DirtyFlag.positions); } @@ -106,12 +103,9 @@ export class Sprite extends RefObject { set region(value: Rect) { const region = this._region; - region.setValue( - MathUtil.clamp(value.x, 0, 1), - MathUtil.clamp(value.y, 0, 1), - MathUtil.clamp(value.width, 0, 1 - value.x), - MathUtil.clamp(value.height, 0, 1 - value.y) - ); + const x = MathUtil.clamp(value.x, 0, 1); + const y = MathUtil.clamp(value.y, 0, 1); + region.setValue(x, y, MathUtil.clamp(value.width, 0, 1 - x), MathUtil.clamp(value.height, 0, 1 - y)); this._setDirtyFlagTrue(DirtyFlag.positions | DirtyFlag.uv); } From 83ff1b5dacdf9208033666715e065f5f13ab6562 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Tue, 10 Aug 2021 15:02:58 +0800 Subject: [PATCH 20/72] fix:atlas support rotation --- packages/core/src/2d/atlas/types.ts | 2 + packages/core/src/2d/sprite/Sprite.ts | 55 +++++++++++++++++++----- packages/loader/src/SpriteAtlasLoader.ts | 1 + 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/packages/core/src/2d/atlas/types.ts b/packages/core/src/2d/atlas/types.ts index 8bc8693cc1..1dcde30738 100644 --- a/packages/core/src/2d/atlas/types.ts +++ b/packages/core/src/2d/atlas/types.ts @@ -23,6 +23,8 @@ export interface AtlasConfig { export interface AtlasSprite { /** The name the sprite. */ name: string; + /** Whether to rotate 90 degrees clockwise. */ + rotated: boolean; /** The range of the sprites on the big picture. */ atlasRegion: { x: number; y: number; w: number; h: number }; /** If there is trimming, the offset of the sprite relative to the original sprite. */ diff --git a/packages/core/src/2d/sprite/Sprite.ts b/packages/core/src/2d/sprite/Sprite.ts index c770214fb6..9bcb6c037f 100644 --- a/packages/core/src/2d/sprite/Sprite.ts +++ b/packages/core/src/2d/sprite/Sprite.ts @@ -23,6 +23,7 @@ export class Sprite extends RefObject { private _pixelsPerUnit: number; private _texture: Texture2D = null; + private _rotated: boolean = false; private _region: Rect = new Rect(0, 0, 1, 1); private _pivot: Vector2 = new Vector2(0.5, 0.5); private _atlasRegion: Rect = new Rect(0, 0, 1, 1); @@ -50,11 +51,23 @@ export class Sprite extends RefObject { get bounds(): Readonly { if (this._isContainDirtyFlag(DirtyFlag.positions)) { this._updatePositionsAndBounds(); - this._setDirtyFlagTrue(DirtyFlag.positions); + this._setDirtyFlagFalse(DirtyFlag.positions); } return this._bounds; } + /** + * Is it rotated 90 degrees clockwise when packing. + */ + get rotated(): boolean { + return this._rotated; + } + + set rotated(value: boolean) { + this._rotated = value; + this._setDirtyFlagTrue(DirtyFlag.positions | DirtyFlag.uv); + } + /** * The rectangle region of the original texture on its atlas texture, specified in normalized. */ @@ -171,8 +184,16 @@ export class Sprite extends RefObject { const pixelsPerUnitReciprocal = 1.0 / this._pixelsPerUnit; // Get the width and height in 3D space. - const unitWidth = atlasRegion.width * region.width * texture.width * pixelsPerUnitReciprocal; - const unitHeight = atlasRegion.height * region.height * texture.height * pixelsPerUnitReciprocal; + let unitWidth: number; + let unitHeight: number; + if (this._rotated) { + // If it is rotated, we need to swap the height and width. + unitWidth = atlasRegion.height * region.height * texture.height * pixelsPerUnitReciprocal; + unitHeight = atlasRegion.width * region.width * texture.width * pixelsPerUnitReciprocal; + } else { + unitWidth = atlasRegion.width * region.width * texture.width * pixelsPerUnitReciprocal; + unitHeight = atlasRegion.height * region.height * texture.height * pixelsPerUnitReciprocal; + } // Get the distance between the anchor point and the four sides. const lx = (-pivot.x + atlasRegionOffset.x) * unitWidth; @@ -217,14 +238,26 @@ export class Sprite extends RefObject { const right = left + atlasRegionWidth * region.width; const bottom = top + atlasRegionHeight * region.height; - // Top-left. - uv[0].setValue(left, top); - // Top-right. - uv[1].setValue(right, top); - // Bottom-right. - uv[2].setValue(right, bottom); - // Bottom-left. - uv[3].setValue(left, bottom); + if (this._rotated) { + // If it is rotated, we need to rotate the UV 90 degrees counterclockwise to correct it. + // Top-left. + uv[0].setValue(right, top); + // Top-right. + uv[1].setValue(right, bottom); + // Bottom-right. + uv[2].setValue(left, bottom); + // Bottom-left. + uv[3].setValue(left, top); + } else { + // Top-left. + uv[0].setValue(left, top); + // Top-right. + uv[1].setValue(right, top); + // Bottom-right. + uv[2].setValue(right, bottom); + // Bottom-left. + uv[3].setValue(left, bottom); + } } } diff --git a/packages/loader/src/SpriteAtlasLoader.ts b/packages/loader/src/SpriteAtlasLoader.ts index 646c3bd8cf..b56e0384b9 100644 --- a/packages/loader/src/SpriteAtlasLoader.ts +++ b/packages/loader/src/SpriteAtlasLoader.ts @@ -59,6 +59,7 @@ class SpriteAtlasLoader extends Loader { atlasSprite.pixelsPerUnit || undefined, atlasSprite.name ); + atlasSprite.rotated && (sprite.rotated = true); sprite.atlasRegion.setValue( atlasRegion.x * sourceWidthReciprocal, atlasRegion.y * sourceHeightReciprocal, From a91d5e995202bc484cd68b91d50accd91521423f Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Tue, 10 Aug 2021 15:13:33 +0800 Subject: [PATCH 21/72] fix:atlas support rotation --- packages/core/src/2d/sprite/Sprite.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/core/src/2d/sprite/Sprite.ts b/packages/core/src/2d/sprite/Sprite.ts index 9bcb6c037f..6e6d4a8af2 100644 --- a/packages/core/src/2d/sprite/Sprite.ts +++ b/packages/core/src/2d/sprite/Sprite.ts @@ -64,8 +64,10 @@ export class Sprite extends RefObject { } set rotated(value: boolean) { - this._rotated = value; - this._setDirtyFlagTrue(DirtyFlag.positions | DirtyFlag.uv); + if (this._rotated != value) { + this._rotated = value; + this._setDirtyFlagTrue(DirtyFlag.positions | DirtyFlag.uv); + } } /** From 0f60caca94ae667d6cdb1161afea58451730e6e9 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Tue, 10 Aug 2021 16:56:47 +0800 Subject: [PATCH 22/72] fix:atlas support rotation --- packages/core/src/2d/atlas/types.ts | 2 +- packages/core/src/2d/sprite/Sprite.ts | 16 ++++++++-------- packages/loader/src/SpriteAtlasLoader.ts | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/core/src/2d/atlas/types.ts b/packages/core/src/2d/atlas/types.ts index 1dcde30738..b592d5b10b 100644 --- a/packages/core/src/2d/atlas/types.ts +++ b/packages/core/src/2d/atlas/types.ts @@ -24,7 +24,7 @@ export interface AtlasSprite { /** The name the sprite. */ name: string; /** Whether to rotate 90 degrees clockwise. */ - rotated: boolean; + atlasRotated: boolean; /** The range of the sprites on the big picture. */ atlasRegion: { x: number; y: number; w: number; h: number }; /** If there is trimming, the offset of the sprite relative to the original sprite. */ diff --git a/packages/core/src/2d/sprite/Sprite.ts b/packages/core/src/2d/sprite/Sprite.ts index 6e6d4a8af2..9eec7855b0 100644 --- a/packages/core/src/2d/sprite/Sprite.ts +++ b/packages/core/src/2d/sprite/Sprite.ts @@ -23,7 +23,7 @@ export class Sprite extends RefObject { private _pixelsPerUnit: number; private _texture: Texture2D = null; - private _rotated: boolean = false; + private _atlasRotated: boolean = false; private _region: Rect = new Rect(0, 0, 1, 1); private _pivot: Vector2 = new Vector2(0.5, 0.5); private _atlasRegion: Rect = new Rect(0, 0, 1, 1); @@ -59,13 +59,13 @@ export class Sprite extends RefObject { /** * Is it rotated 90 degrees clockwise when packing. */ - get rotated(): boolean { - return this._rotated; + get atlasRotated(): boolean { + return this._atlasRotated; } - set rotated(value: boolean) { - if (this._rotated != value) { - this._rotated = value; + set atlasRotated(value: boolean) { + if (this._atlasRotated != value) { + this._atlasRotated = value; this._setDirtyFlagTrue(DirtyFlag.positions | DirtyFlag.uv); } } @@ -188,7 +188,7 @@ export class Sprite extends RefObject { // Get the width and height in 3D space. let unitWidth: number; let unitHeight: number; - if (this._rotated) { + if (this._atlasRotated) { // If it is rotated, we need to swap the height and width. unitWidth = atlasRegion.height * region.height * texture.height * pixelsPerUnitReciprocal; unitHeight = atlasRegion.width * region.width * texture.width * pixelsPerUnitReciprocal; @@ -240,7 +240,7 @@ export class Sprite extends RefObject { const right = left + atlasRegionWidth * region.width; const bottom = top + atlasRegionHeight * region.height; - if (this._rotated) { + if (this._atlasRotated) { // If it is rotated, we need to rotate the UV 90 degrees counterclockwise to correct it. // Top-left. uv[0].setValue(right, top); diff --git a/packages/loader/src/SpriteAtlasLoader.ts b/packages/loader/src/SpriteAtlasLoader.ts index b56e0384b9..05f2c8aa09 100644 --- a/packages/loader/src/SpriteAtlasLoader.ts +++ b/packages/loader/src/SpriteAtlasLoader.ts @@ -59,7 +59,7 @@ class SpriteAtlasLoader extends Loader { atlasSprite.pixelsPerUnit || undefined, atlasSprite.name ); - atlasSprite.rotated && (sprite.rotated = true); + atlasSprite.atlasRotated && (sprite.atlasRotated = true); sprite.atlasRegion.setValue( atlasRegion.x * sourceWidthReciprocal, atlasRegion.y * sourceHeightReciprocal, From 066caf9c3d0120bc064037dd4513512c3327f510 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Sun, 15 Aug 2021 21:17:49 +0800 Subject: [PATCH 23/72] =?UTF-8?q?fix:Fixed=20sprite=E2=80=99s=20rect=20dis?= =?UTF-8?q?play=20error=20when=20packaged=20into=20an=20atlas=20and=20trim?= =?UTF-8?q?med?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/src/2d/atlas/types.ts | 2 + packages/core/src/2d/sprite/Sprite.ts | 129 +++++++++++++++++------ packages/loader/src/SpriteAtlasLoader.ts | 14 ++- 3 files changed, 109 insertions(+), 36 deletions(-) diff --git a/packages/core/src/2d/atlas/types.ts b/packages/core/src/2d/atlas/types.ts index b592d5b10b..23f003c5a2 100644 --- a/packages/core/src/2d/atlas/types.ts +++ b/packages/core/src/2d/atlas/types.ts @@ -23,6 +23,8 @@ export interface AtlasConfig { export interface AtlasSprite { /** The name the sprite. */ name: string; + /** The original size of the sprite. */ + originalSize: { w: number; h: number }; /** Whether to rotate 90 degrees clockwise. */ atlasRotated: boolean; /** The range of the sprites on the big picture. */ diff --git a/packages/core/src/2d/sprite/Sprite.ts b/packages/core/src/2d/sprite/Sprite.ts index 9eec7855b0..dbbdd6ad79 100644 --- a/packages/core/src/2d/sprite/Sprite.ts +++ b/packages/core/src/2d/sprite/Sprite.ts @@ -23,6 +23,7 @@ export class Sprite extends RefObject { private _pixelsPerUnit: number; private _texture: Texture2D = null; + private _originalSize: Vector2 = null; private _atlasRotated: boolean = false; private _region: Rect = new Rect(0, 0, 1, 1); private _pivot: Vector2 = new Vector2(0.5, 0.5); @@ -44,6 +45,26 @@ export class Sprite extends RefObject { } } + /** + * The original size of the sprite. + */ + get originalSize(): Vector2 { + const { _originalSize, _texture } = this; + if (!_originalSize && _texture) { + this._originalSize = new Vector2(_texture.width, _texture.height); + } + return _originalSize; + } + + set originalSize(value: Vector2) { + if (!this._originalSize) { + this._originalSize = value.clone(); + } else { + value.cloneTo(this._originalSize); + } + this._setDirtyFlagTrue(DirtyFlag.positions); + } + /** * Bounding volume of the sprite. * @remarks The returned bounds should be considered deep-read-only. @@ -51,7 +72,7 @@ export class Sprite extends RefObject { get bounds(): Readonly { if (this._isContainDirtyFlag(DirtyFlag.positions)) { this._updatePositionsAndBounds(); - this._setDirtyFlagFalse(DirtyFlag.positions); + // this._setDirtyFlagFalse(DirtyFlag.positions); } return this._bounds; } @@ -66,7 +87,7 @@ export class Sprite extends RefObject { set atlasRotated(value: boolean) { if (this._atlasRotated != value) { this._atlasRotated = value; - this._setDirtyFlagTrue(DirtyFlag.positions | DirtyFlag.uv); + this._setDirtyFlagTrue(DirtyFlag.uv); } } @@ -82,7 +103,7 @@ export class Sprite extends RefObject { const x = MathUtil.clamp(value.x, 0, 1); const y = MathUtil.clamp(value.y, 0, 1); atlasRegion.setValue(x, y, MathUtil.clamp(value.width, 0, 1 - x), MathUtil.clamp(value.height, 0, 1 - y)); - this._setDirtyFlagTrue(DirtyFlag.positions); + this._setDirtyFlagTrue(DirtyFlag.uv); } /** @@ -180,28 +201,46 @@ export class Sprite extends RefObject { * Update positions and bounds. */ private _updatePositionsAndBounds(): void { - const { texture, _bounds: bounds } = this; - if (texture) { - const { _atlasRegionOffset: atlasRegionOffset, _atlasRegion: atlasRegion, _region: region, _pivot: pivot } = this; + const { originalSize, _bounds: bounds } = this; + if (originalSize) { + const { _atlasRegion, _texture, _atlasRegionOffset, _pivot } = this; + const { x: regionX, y: regionY, width: regionWidth, height: regionHeight } = this._region; const pixelsPerUnitReciprocal = 1.0 / this._pixelsPerUnit; - // Get the width and height in 3D space. - let unitWidth: number; - let unitHeight: number; - if (this._atlasRotated) { - // If it is rotated, we need to swap the height and width. - unitWidth = atlasRegion.height * region.height * texture.height * pixelsPerUnitReciprocal; - unitHeight = atlasRegion.width * region.width * texture.width * pixelsPerUnitReciprocal; + // The coordinates of the trimmed up, down, left, and right. + const leftBlankSpace = _atlasRegionOffset.x; + const topBlankSpace = _atlasRegionOffset.y; + const rightBlankSpace = (_texture.width * _atlasRegion.width) / originalSize.x + leftBlankSpace; + const downBlankSpace = (_texture.height * _atlasRegion.height) / originalSize.y + topBlankSpace; + + // The size of the real rendering. + let realRenderWidth: number; + let realRenderHeight: number; + if ( + regionX + regionWidth <= leftBlankSpace || + regionY + regionHeight <= topBlankSpace || + regionX >= rightBlankSpace || + regionY >= downBlankSpace + ) { + realRenderWidth = realRenderHeight = 0; } else { - unitWidth = atlasRegion.width * region.width * texture.width * pixelsPerUnitReciprocal; - unitHeight = atlasRegion.height * region.height * texture.height * pixelsPerUnitReciprocal; + realRenderWidth = Math.min(rightBlankSpace, regionX + regionWidth) - Math.max(leftBlankSpace, regionX); + realRenderHeight = Math.min(downBlankSpace, regionY + regionHeight) - Math.max(topBlankSpace, regionY); + realRenderWidth = realRenderWidth * originalSize.x * pixelsPerUnitReciprocal; + realRenderHeight = realRenderHeight * originalSize.y * pixelsPerUnitReciprocal; } // Get the distance between the anchor point and the four sides. - const lx = (-pivot.x + atlasRegionOffset.x) * unitWidth; - const by = (-pivot.y + atlasRegionOffset.y) * unitHeight; - const rx = unitWidth + lx; - const ty = unitHeight + by; + const lx = + (-_pivot.x * regionWidth + (leftBlankSpace > regionX ? leftBlankSpace - regionX : 0)) * + originalSize.x * + pixelsPerUnitReciprocal; + const ty = + (_pivot.y * regionHeight - (topBlankSpace > regionY ? topBlankSpace - regionY : 0)) * + originalSize.y * + pixelsPerUnitReciprocal; + const rx = lx + realRenderWidth; + const by = ty - realRenderHeight; // Assign values ​​to _positions const positions = this._positions; @@ -233,32 +272,58 @@ export class Sprite extends RefObject { } if (this._isContainDirtyFlag(DirtyFlag.uv)) { - const { _region: region, _atlasRegion: atlasRegion, _uv: uv } = this; + const { _region: region, _atlasRegion: atlasRegion, _uv, originalSize, _atlasRegionOffset, _texture } = this; const { width: atlasRegionWidth, height: atlasRegionHeight } = atlasRegion; - const left = atlasRegion.x + atlasRegionWidth * region.x; - const top = atlasRegion.y + atlasRegionHeight * region.y; - const right = left + atlasRegionWidth * region.width; - const bottom = top + atlasRegionHeight * region.height; + // 上下左右的间隔 + const leftSub = _atlasRegionOffset.x; + const topSub = _atlasRegionOffset.y; + const rightSub = (_texture.width * atlasRegion.width) / originalSize.x + leftSub; + const downSub = (_texture.height * atlasRegion.height) / originalSize.y + topSub; + + let left: number = 0; + let top: number = 0; + let rw: number; + let rh: number; + if ( + region.x + region.width <= leftSub || + region.y + region.height <= topSub || + region.x >= rightSub || + region.y >= downSub + ) { + rw = rh = 0; + } else { + rw = Math.min(rightSub, region.x + region.width) - Math.max(leftSub, region.x); + rh = Math.min(downSub, region.y + region.height) - Math.max(topSub, region.y); + rw = (rw * originalSize.x) / this._texture.width; + rh = (rh * originalSize.y) / this._texture.height; + left = Math.max(leftSub, region.x) - leftSub; + top = Math.max(topSub, region.y) - topSub; + } + + left = (left * originalSize.x) / this._texture.width + atlasRegion.x; + top = (top * originalSize.y) / this._texture.height + atlasRegion.y; + const right = left + rw; + const bottom = top + rh; if (this._atlasRotated) { // If it is rotated, we need to rotate the UV 90 degrees counterclockwise to correct it. // Top-left. - uv[0].setValue(right, top); + _uv[0].setValue(right, top); // Top-right. - uv[1].setValue(right, bottom); + _uv[1].setValue(right, bottom); // Bottom-right. - uv[2].setValue(left, bottom); + _uv[2].setValue(left, bottom); // Bottom-left. - uv[3].setValue(left, top); + _uv[3].setValue(left, top); } else { // Top-left. - uv[0].setValue(left, top); + _uv[0].setValue(left, top); // Top-right. - uv[1].setValue(right, top); + _uv[1].setValue(right, top); // Bottom-right. - uv[2].setValue(right, bottom); + _uv[2].setValue(right, bottom); // Bottom-left. - uv[3].setValue(left, bottom); + _uv[3].setValue(left, bottom); } } } diff --git a/packages/loader/src/SpriteAtlasLoader.ts b/packages/loader/src/SpriteAtlasLoader.ts index 05f2c8aa09..8c4ed01ccd 100644 --- a/packages/loader/src/SpriteAtlasLoader.ts +++ b/packages/loader/src/SpriteAtlasLoader.ts @@ -34,7 +34,7 @@ class SpriteAtlasLoader extends Loader { const { engine } = resourceManager; // Generate a SpriteAtlas object const tempRect = new Rect(); - const tempPivot = new Vector2(); + const tempVect2 = new Vector2(); const spriteAtlas = new SpriteAtlas(engine); for (let i = 0; i < atlasItemsLen; i++) { // Generate Texture2D according to configuration @@ -50,12 +50,12 @@ class SpriteAtlasLoader extends Loader { const sourceHeightReciprocal = 1.0 / height; for (let j = sprites.length - 1; j >= 0; j--) { const atlasSprite = sprites[j]; - const { region, pivot, atlasRegionOffset, atlasRegion } = atlasSprite; + const { region, pivot, atlasRegionOffset, atlasRegion, originalSize } = atlasSprite; const sprite = new Sprite( engine, texture, region ? tempRect.setValue(region.x, region.y, region.w, region.h) : undefined, - pivot ? tempPivot.setValue(pivot.x, pivot.y) : undefined, + pivot ? tempVect2.setValue(pivot.x, pivot.y) : undefined, atlasSprite.pixelsPerUnit || undefined, atlasSprite.name ); @@ -66,7 +66,13 @@ class SpriteAtlasLoader extends Loader { atlasRegion.w * sourceWidthReciprocal, atlasRegion.h * sourceHeightReciprocal ); - atlasRegionOffset && sprite.atlasRegionOffset.setValue(atlasRegionOffset.x, atlasRegionOffset.y); + // The original size is a necessary parameter. + sprite.originalSize = tempVect2.setValue(originalSize.w, originalSize.h); + atlasRegionOffset && + sprite.atlasRegionOffset.setValue( + atlasRegionOffset.x / originalSize.w, + atlasRegionOffset.y / originalSize.h + ); /** @ts-ignore */ spriteAtlas._addSprite(sprite); } From 4c4222da13e6c415b193d82dd4e2dcdec6ddc019 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Mon, 16 Aug 2021 11:13:06 +0800 Subject: [PATCH 24/72] =?UTF-8?q?fix:Fixed=20sprite=E2=80=99s=20rect=20dis?= =?UTF-8?q?play=20error=20when=20packaged=20into=20an=20atlas=20and=20trim?= =?UTF-8?q?med?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/src/2d/sprite/Sprite.ts | 206 +++++++++++++---------- packages/loader/src/SpriteAtlasLoader.ts | 8 +- 2 files changed, 127 insertions(+), 87 deletions(-) diff --git a/packages/core/src/2d/sprite/Sprite.ts b/packages/core/src/2d/sprite/Sprite.ts index dbbdd6ad79..dc2f72764b 100644 --- a/packages/core/src/2d/sprite/Sprite.ts +++ b/packages/core/src/2d/sprite/Sprite.ts @@ -23,8 +23,10 @@ export class Sprite extends RefObject { private _pixelsPerUnit: number; private _texture: Texture2D = null; - private _originalSize: Vector2 = null; + private _packed: boolean = false; + private _trimmed: boolean = false; private _atlasRotated: boolean = false; + private _originalSize: Vector2 = new Vector2(0, 0); private _region: Rect = new Rect(0, 0, 1, 1); private _pivot: Vector2 = new Vector2(0.5, 0.5); private _atlasRegion: Rect = new Rect(0, 0, 1, 1); @@ -50,19 +52,15 @@ export class Sprite extends RefObject { */ get originalSize(): Vector2 { const { _originalSize, _texture } = this; - if (!_originalSize && _texture) { - this._originalSize = new Vector2(_texture.width, _texture.height); + if (!this._packed && _texture) { + _originalSize.setValue(_texture.width, _texture.height); } return _originalSize; } set originalSize(value: Vector2) { - if (!this._originalSize) { - this._originalSize = value.clone(); - } else { - value.cloneTo(this._originalSize); - } - this._setDirtyFlagTrue(DirtyFlag.positions); + value.cloneTo(this._originalSize); + this._setDirtyFlagTrue(DirtyFlag.positions | DirtyFlag.uv); } /** @@ -70,9 +68,9 @@ export class Sprite extends RefObject { * @remarks The returned bounds should be considered deep-read-only. */ get bounds(): Readonly { - if (this._isContainDirtyFlag(DirtyFlag.positions)) { + if (this._isContainDirtyFlag(DirtyFlag.positions) && this._texture) { this._updatePositionsAndBounds(); - // this._setDirtyFlagFalse(DirtyFlag.positions); + this._setDirtyFlagFalse(DirtyFlag.positions); } return this._bounds; } @@ -87,7 +85,7 @@ export class Sprite extends RefObject { set atlasRotated(value: boolean) { if (this._atlasRotated != value) { this._atlasRotated = value; - this._setDirtyFlagTrue(DirtyFlag.uv); + this._setDirtyFlagTrue(DirtyFlag.positions | DirtyFlag.uv); } } @@ -159,6 +157,22 @@ export class Sprite extends RefObject { } } + /** + * Set whether to be trimmed. + * @param value Whether to be trimmed + */ + setTrimmed(value: boolean): void { + this._trimmed = value; + } + + /** + * Set whether to be packaged. + * @param value whether to be packaged + */ + setPacked(value: boolean): void { + this._packed = value; + } + /** * Constructor a Sprite. * @param engine - Engine to which the sprite belongs @@ -201,47 +215,62 @@ export class Sprite extends RefObject { * Update positions and bounds. */ private _updatePositionsAndBounds(): void { - const { originalSize, _bounds: bounds } = this; - if (originalSize) { - const { _atlasRegion, _texture, _atlasRegionOffset, _pivot } = this; + const { _texture: texture, _bounds: bounds } = this; + if (texture) { + const { _atlasRegion: atlasRegion, _pivot: pivot, originalSize } = this; const { x: regionX, y: regionY, width: regionWidth, height: regionHeight } = this._region; const pixelsPerUnitReciprocal = 1.0 / this._pixelsPerUnit; - - // The coordinates of the trimmed up, down, left, and right. - const leftBlankSpace = _atlasRegionOffset.x; - const topBlankSpace = _atlasRegionOffset.y; - const rightBlankSpace = (_texture.width * _atlasRegion.width) / originalSize.x + leftBlankSpace; - const downBlankSpace = (_texture.height * _atlasRegion.height) / originalSize.y + topBlankSpace; - - // The size of the real rendering. - let realRenderWidth: number; - let realRenderHeight: number; - if ( - regionX + regionWidth <= leftBlankSpace || - regionY + regionHeight <= topBlankSpace || - regionX >= rightBlankSpace || - regionY >= downBlankSpace - ) { - realRenderWidth = realRenderHeight = 0; + const oriWidth = originalSize.x * pixelsPerUnitReciprocal; + const oriHeight = originalSize.y * pixelsPerUnitReciprocal; + // Coordinates of the four boundaries. + let lx: number, ty: number, rx: number, by: number; + // Real rendering size. + let realRenderWidth: number, realRenderHeight: number; + if (this._trimmed) { + const { _atlasRegionOffset: atlasRegionOffset } = this; + // The coordinates of the trimmed up, down, left, and right. + let rightBlankSpace: number, downBlankSpace: number; + const leftBlankSpace = atlasRegionOffset.x; + const topBlankSpace = atlasRegionOffset.y; + if (this._atlasRotated) { + rightBlankSpace = (texture.width * atlasRegion.height) / originalSize.x + leftBlankSpace; + downBlankSpace = (texture.height * atlasRegion.width) / originalSize.y + topBlankSpace; + } else { + rightBlankSpace = (texture.width * atlasRegion.width) / originalSize.x + leftBlankSpace; + downBlankSpace = (texture.height * atlasRegion.height) / originalSize.y + topBlankSpace; + } + if ( + regionX + regionWidth <= leftBlankSpace || + regionY + regionHeight <= topBlankSpace || + regionX >= rightBlankSpace || + regionY >= downBlankSpace + ) { + lx = ty = rx = by = 0; + } else { + // The size of the real rendering. + realRenderWidth = + (Math.min(rightBlankSpace, regionX + regionWidth) - Math.max(leftBlankSpace, regionX)) * oriWidth; + realRenderHeight = + (Math.min(downBlankSpace, regionY + regionHeight) - Math.max(topBlankSpace, regionY)) * oriHeight; + lx = (-pivot.x * regionWidth + (leftBlankSpace > regionX ? leftBlankSpace - regionX : 0)) * oriWidth; + ty = (pivot.y * regionHeight - (topBlankSpace > regionY ? topBlankSpace - regionY : 0)) * oriHeight; + rx = lx + realRenderWidth; + by = ty - realRenderHeight; + } } else { - realRenderWidth = Math.min(rightBlankSpace, regionX + regionWidth) - Math.max(leftBlankSpace, regionX); - realRenderHeight = Math.min(downBlankSpace, regionY + regionHeight) - Math.max(topBlankSpace, regionY); - realRenderWidth = realRenderWidth * originalSize.x * pixelsPerUnitReciprocal; - realRenderHeight = realRenderHeight * originalSize.y * pixelsPerUnitReciprocal; + if (this._atlasRotated) { + realRenderWidth = atlasRegion.height * regionHeight * oriHeight; + realRenderHeight = atlasRegion.width * regionWidth * oriWidth; + } else { + realRenderWidth = atlasRegion.width * regionWidth * oriWidth; + realRenderHeight = atlasRegion.height * regionHeight * oriHeight; + } + lx = -pivot.x * realRenderWidth; + by = -pivot.y * realRenderHeight; + rx = realRenderWidth + lx; + ty = realRenderHeight + by; } - // Get the distance between the anchor point and the four sides. - const lx = - (-_pivot.x * regionWidth + (leftBlankSpace > regionX ? leftBlankSpace - regionX : 0)) * - originalSize.x * - pixelsPerUnitReciprocal; - const ty = - (_pivot.y * regionHeight - (topBlankSpace > regionY ? topBlankSpace - regionY : 0)) * - originalSize.y * - pixelsPerUnitReciprocal; - const rx = lx + realRenderWidth; - const by = ty - realRenderHeight; - // Assign values ​​to _positions const positions = this._positions; // Top-left. @@ -272,58 +301,63 @@ export class Sprite extends RefObject { } if (this._isContainDirtyFlag(DirtyFlag.uv)) { - const { _region: region, _atlasRegion: atlasRegion, _uv, originalSize, _atlasRegionOffset, _texture } = this; - const { width: atlasRegionWidth, height: atlasRegionHeight } = atlasRegion; - // 上下左右的间隔 - const leftSub = _atlasRegionOffset.x; - const topSub = _atlasRegionOffset.y; - const rightSub = (_texture.width * atlasRegion.width) / originalSize.x + leftSub; - const downSub = (_texture.height * atlasRegion.height) / originalSize.y + topSub; - - let left: number = 0; - let top: number = 0; - let rw: number; - let rh: number; - if ( - region.x + region.width <= leftSub || - region.y + region.height <= topSub || - region.x >= rightSub || - region.y >= downSub - ) { - rw = rh = 0; + const { _atlasRegion: atlasRegion, _uv: uv, _atlasRegionOffset: atlasRegionOffset } = this; + const { x: regionX, y: regionY, width: regionWidth, height: regionHeight } = this._region; + let left: number, top: number, right: number, bottom: number; + if (this._trimmed) { + const { originalSize, _texture: texture } = this; + // The coordinates of the trimmed up, down, left, and right. + const leftBlankSpace = atlasRegionOffset.x; + const topBlankSpace = atlasRegionOffset.y; + const rightBlankSpace = (texture.width * atlasRegion.width) / originalSize.x + leftBlankSpace; + const downBlankSpace = (texture.height * atlasRegion.height) / originalSize.y + topBlankSpace; + if ( + regionX + regionWidth <= leftBlankSpace || + regionY + regionHeight <= topBlankSpace || + regionX >= rightBlankSpace || + regionY >= downBlankSpace + ) { + left = top = right = bottom = 0; + } else { + // The size of the real rendering. + const realRenderWidth = + ((Math.min(rightBlankSpace, regionX + regionWidth) - Math.max(leftBlankSpace, regionX)) * originalSize.x) / + texture.width; + const realRenderHeight = + ((Math.min(downBlankSpace, regionY + regionHeight) - Math.max(topBlankSpace, regionY)) * originalSize.y) / + texture.height; + left = + ((Math.max(leftBlankSpace, regionX) - leftBlankSpace) * originalSize.x) / texture.width + atlasRegion.x; + top = ((Math.max(topBlankSpace, regionY) - topBlankSpace) * originalSize.y) / texture.height + atlasRegion.y; + right = left + realRenderWidth; + bottom = top + realRenderHeight; + } } else { - rw = Math.min(rightSub, region.x + region.width) - Math.max(leftSub, region.x); - rh = Math.min(downSub, region.y + region.height) - Math.max(topSub, region.y); - rw = (rw * originalSize.x) / this._texture.width; - rh = (rh * originalSize.y) / this._texture.height; - left = Math.max(leftSub, region.x) - leftSub; - top = Math.max(topSub, region.y) - topSub; + left = atlasRegion.x + atlasRegion.width * regionX; + top = atlasRegion.y + atlasRegion.height * regionY; + right = left + atlasRegion.width * regionWidth; + bottom = top + atlasRegion.height * regionHeight; } - left = (left * originalSize.x) / this._texture.width + atlasRegion.x; - top = (top * originalSize.y) / this._texture.height + atlasRegion.y; - const right = left + rw; - const bottom = top + rh; - if (this._atlasRotated) { // If it is rotated, we need to rotate the UV 90 degrees counterclockwise to correct it. // Top-left. - _uv[0].setValue(right, top); + uv[0].setValue(right, top); // Top-right. - _uv[1].setValue(right, bottom); + uv[1].setValue(right, bottom); // Bottom-right. - _uv[2].setValue(left, bottom); + uv[2].setValue(left, bottom); // Bottom-left. - _uv[3].setValue(left, top); + uv[3].setValue(left, top); } else { // Top-left. - _uv[0].setValue(left, top); + uv[0].setValue(left, top); // Top-right. - _uv[1].setValue(right, top); + uv[1].setValue(right, top); // Bottom-right. - _uv[2].setValue(right, bottom); + uv[2].setValue(right, bottom); // Bottom-left. - _uv[3].setValue(left, bottom); + uv[3].setValue(left, bottom); } } } diff --git a/packages/loader/src/SpriteAtlasLoader.ts b/packages/loader/src/SpriteAtlasLoader.ts index 8c4ed01ccd..3f3ee22579 100644 --- a/packages/loader/src/SpriteAtlasLoader.ts +++ b/packages/loader/src/SpriteAtlasLoader.ts @@ -59,7 +59,7 @@ class SpriteAtlasLoader extends Loader { atlasSprite.pixelsPerUnit || undefined, atlasSprite.name ); - atlasSprite.atlasRotated && (sprite.atlasRotated = true); + sprite.setPacked(true); sprite.atlasRegion.setValue( atlasRegion.x * sourceWidthReciprocal, atlasRegion.y * sourceHeightReciprocal, @@ -68,6 +68,12 @@ class SpriteAtlasLoader extends Loader { ); // The original size is a necessary parameter. sprite.originalSize = tempVect2.setValue(originalSize.w, originalSize.h); + if (atlasSprite.atlasRotated) { + sprite.atlasRotated = true; + sprite.setTrimmed(originalSize.w != atlasRegion.w || originalSize.h != atlasRegion.h); + } else { + sprite.setTrimmed(originalSize.h != atlasRegion.w || originalSize.w != atlasRegion.h); + } atlasRegionOffset && sprite.atlasRegionOffset.setValue( atlasRegionOffset.x / originalSize.w, From 1630680a116327b4dd311b22bdb67ee520704092 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Tue, 17 Aug 2021 17:24:18 +0800 Subject: [PATCH 25/72] =?UTF-8?q?fix:Fixed=20sprite=E2=80=99s=20rect=20dis?= =?UTF-8?q?play=20error=20when=20packaged=20into=20an=20atlas=20and=20trim?= =?UTF-8?q?med?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/src/2d/sprite/Sprite.ts | 198 +++++++++-------------- packages/loader/src/SpriteAtlasLoader.ts | 43 +++-- 2 files changed, 105 insertions(+), 136 deletions(-) diff --git a/packages/core/src/2d/sprite/Sprite.ts b/packages/core/src/2d/sprite/Sprite.ts index dc2f72764b..01bc3bc623 100644 --- a/packages/core/src/2d/sprite/Sprite.ts +++ b/packages/core/src/2d/sprite/Sprite.ts @@ -1,4 +1,4 @@ -import { BoundingBox, MathUtil, Rect, Vector2 } from "@oasis-engine/math"; +import { BoundingBox, MathUtil, Rect, Vector2, Vector4 } from "@oasis-engine/math"; import { RefObject } from "../../asset/RefObject"; import { Engine } from "../../Engine"; import { Texture2D } from "../../texture/Texture2D"; @@ -23,14 +23,12 @@ export class Sprite extends RefObject { private _pixelsPerUnit: number; private _texture: Texture2D = null; - private _packed: boolean = false; private _trimmed: boolean = false; private _atlasRotated: boolean = false; - private _originalSize: Vector2 = new Vector2(0, 0); private _region: Rect = new Rect(0, 0, 1, 1); private _pivot: Vector2 = new Vector2(0.5, 0.5); private _atlasRegion: Rect = new Rect(0, 0, 1, 1); - private _atlasRegionOffset: Vector2 = new Vector2(0, 0); + private _atlasRegionOffset: Vector4 = new Vector4(0, 0, 0, 0); private _dirtyFlag: DirtyFlag = DirtyFlag.all; /** @@ -47,22 +45,6 @@ export class Sprite extends RefObject { } } - /** - * The original size of the sprite. - */ - get originalSize(): Vector2 { - const { _originalSize, _texture } = this; - if (!this._packed && _texture) { - _originalSize.setValue(_texture.width, _texture.height); - } - return _originalSize; - } - - set originalSize(value: Vector2) { - value.cloneTo(this._originalSize); - this._setDirtyFlagTrue(DirtyFlag.positions | DirtyFlag.uv); - } - /** * Bounding volume of the sprite. * @remarks The returned bounds should be considered deep-read-only. @@ -90,34 +72,35 @@ export class Sprite extends RefObject { } /** - * The rectangle region of the original texture on its atlas texture, specified in normalized. + * The rectangle region of the original sprite on its atlas texture, specified in normalized. */ get atlasRegion(): Rect { return this._atlasRegion; } set atlasRegion(value: Rect) { - const atlasRegion = this._atlasRegion; const x = MathUtil.clamp(value.x, 0, 1); const y = MathUtil.clamp(value.y, 0, 1); - atlasRegion.setValue(x, y, MathUtil.clamp(value.width, 0, 1 - x), MathUtil.clamp(value.height, 0, 1 - y)); + this._atlasRegion.setValue(x, y, MathUtil.clamp(value.width, 0, 1 - x), MathUtil.clamp(value.height, 0, 1 - y)); this._setDirtyFlagTrue(DirtyFlag.uv); } /** - * The rectangle region offset of the original texture on its atlas texture, specified in normalized. + * The rectangle region offset of the original sprite on its atlas texture, specified in normalized. */ - get atlasRegionOffset(): Vector2 { + get atlasRegionOffset(): Vector4 { return this._atlasRegionOffset; } - set atlasRegionOffset(value: Vector2) { - this._atlasRegionOffset.setValue(MathUtil.clamp(value.x, 0, 1), MathUtil.clamp(value.y, 0, 1)); - this._setDirtyFlagTrue(DirtyFlag.positions); + set atlasRegionOffset(value: Vector4) { + const x = MathUtil.clamp(value.x, 0, 1); + const y = MathUtil.clamp(value.y, 0, 1); + this._atlasRegionOffset.setValue(x, y, MathUtil.clamp(value.z, 0, 1 - x), MathUtil.clamp(value.w, 0, 1 - y)); + this._setDirtyFlagTrue(DirtyFlag.positions | DirtyFlag.uv); } /** - * Location of the sprite's center point in the rectangle region on the original texture, specified in normalized. + * Location of the sprite's center point in the rectangle region on the original sprite, specified in normalized. */ get pivot(): Vector2 { return this._pivot; @@ -129,7 +112,7 @@ export class Sprite extends RefObject { } /** - * The rectangle region of the sprite on the original texture, specified in normalized. + * The rectangle region of the sprite, specified in normalized. */ get region(): Rect { return this._region; @@ -165,14 +148,6 @@ export class Sprite extends RefObject { this._trimmed = value; } - /** - * Set whether to be packaged. - * @param value whether to be packaged - */ - setPacked(value: boolean): void { - this._packed = value; - } - /** * Constructor a Sprite. * @param engine - Engine to which the sprite belongs @@ -217,58 +192,37 @@ export class Sprite extends RefObject { private _updatePositionsAndBounds(): void { const { _texture: texture, _bounds: bounds } = this; if (texture) { - const { _atlasRegion: atlasRegion, _pivot: pivot, originalSize } = this; - const { x: regionX, y: regionY, width: regionWidth, height: regionHeight } = this._region; - const pixelsPerUnitReciprocal = 1.0 / this._pixelsPerUnit; - const oriWidth = originalSize.x * pixelsPerUnitReciprocal; - const oriHeight = originalSize.y * pixelsPerUnitReciprocal; + const { _atlasRegion: atlasRegion, _pivot: pivot } = this; + const { x: regionX, y: regionY, width: regionW, height: regionH } = this._region; + const pPUReciprocal = 1.0 / this._pixelsPerUnit; // Coordinates of the four boundaries. let lx: number, ty: number, rx: number, by: number; - // Real rendering size. - let realRenderWidth: number, realRenderHeight: number; + // TextureSize + let textureW: number, textureH: number; + if (this._atlasRotated) { + textureW = texture.height * atlasRegion.height * pPUReciprocal; + textureH = texture.width * atlasRegion.width * pPUReciprocal; + } else { + textureW = texture.width * atlasRegion.width * pPUReciprocal; + textureH = texture.height * atlasRegion.height * pPUReciprocal; + } if (this._trimmed) { - const { _atlasRegionOffset: atlasRegionOffset } = this; - // The coordinates of the trimmed up, down, left, and right. - let rightBlankSpace: number, downBlankSpace: number; - const leftBlankSpace = atlasRegionOffset.x; - const topBlankSpace = atlasRegionOffset.y; - if (this._atlasRotated) { - rightBlankSpace = (texture.width * atlasRegion.height) / originalSize.x + leftBlankSpace; - downBlankSpace = (texture.height * atlasRegion.width) / originalSize.y + topBlankSpace; - } else { - rightBlankSpace = (texture.width * atlasRegion.width) / originalSize.x + leftBlankSpace; - downBlankSpace = (texture.height * atlasRegion.height) / originalSize.y + topBlankSpace; - } - if ( - regionX + regionWidth <= leftBlankSpace || - regionY + regionHeight <= topBlankSpace || - regionX >= rightBlankSpace || - regionY >= downBlankSpace - ) { - lx = ty = rx = by = 0; - } else { - // The size of the real rendering. - realRenderWidth = - (Math.min(rightBlankSpace, regionX + regionWidth) - Math.max(leftBlankSpace, regionX)) * oriWidth; - realRenderHeight = - (Math.min(downBlankSpace, regionY + regionHeight) - Math.max(topBlankSpace, regionY)) * oriHeight; - lx = (-pivot.x * regionWidth + (leftBlankSpace > regionX ? leftBlankSpace - regionX : 0)) * oriWidth; - ty = (pivot.y * regionHeight - (topBlankSpace > regionY ? topBlankSpace - regionY : 0)) * oriHeight; - rx = lx + realRenderWidth; - by = ty - realRenderHeight; - } + const { x: blankLeft, y: blankTop, z: blankRight, w: blankBottom } = this._atlasRegionOffset; + const oriWidth = textureW / (1 - blankRight - blankLeft); + const oriHeight = textureH / (1 - blankBottom - blankTop); + // The size of the real rendering. + lx = (-pivot.x * regionW + Math.max(blankLeft, regionX) - regionX) * oriWidth; + ty = (pivot.y * regionH - Math.max(blankTop, regionY) + regionY) * oriHeight; + rx = (-pivot.x * regionW + Math.min(1 - blankRight, regionX + regionW) - regionX) * oriWidth; + by = (pivot.y * regionH - Math.min(1 - blankBottom, regionY + regionH) + regionY) * oriHeight; } else { - if (this._atlasRotated) { - realRenderWidth = atlasRegion.height * regionHeight * oriHeight; - realRenderHeight = atlasRegion.width * regionWidth * oriWidth; - } else { - realRenderWidth = atlasRegion.width * regionWidth * oriWidth; - realRenderHeight = atlasRegion.height * regionHeight * oriHeight; - } - lx = -pivot.x * realRenderWidth; - by = -pivot.y * realRenderHeight; - rx = realRenderWidth + lx; - ty = realRenderHeight + by; + // Real rendering size. + const realRenderW = textureW * regionW; + const realRenderH = textureH * regionH; + lx = -pivot.x * realRenderW; + by = -pivot.y * realRenderH; + rx = realRenderW + lx; + ty = realRenderH + by; } // Assign values ​​to _positions @@ -301,53 +255,53 @@ export class Sprite extends RefObject { } if (this._isContainDirtyFlag(DirtyFlag.uv)) { - const { _atlasRegion: atlasRegion, _uv: uv, _atlasRegionOffset: atlasRegionOffset } = this; - const { x: regionX, y: regionY, width: regionWidth, height: regionHeight } = this._region; + const { _atlasRegion: atlasRegion, _uv: uv, _region: region, _atlasRotated: atlasRotated } = this; let left: number, top: number, right: number, bottom: number; if (this._trimmed) { - const { originalSize, _texture: texture } = this; - // The coordinates of the trimmed up, down, left, and right. - const leftBlankSpace = atlasRegionOffset.x; - const topBlankSpace = atlasRegionOffset.y; - const rightBlankSpace = (texture.width * atlasRegion.width) / originalSize.x + leftBlankSpace; - const downBlankSpace = (texture.height * atlasRegion.height) / originalSize.y + topBlankSpace; - if ( - regionX + regionWidth <= leftBlankSpace || - regionY + regionHeight <= topBlankSpace || - regionX >= rightBlankSpace || - regionY >= downBlankSpace - ) { - left = top = right = bottom = 0; + const { x: regionX, y: regionY } = region; + const { x: atlasRegionX, y: atlasRegionY } = atlasRegion; + const { x: blankLeft, y: blankTop, z: blankRight, w: blankBottom } = this._atlasRegionOffset; + // Proportion of the original sprite size in the atlas. + let textureW: number, textureH: number; + if (atlasRotated) { + textureW = atlasRegion.width / (1 - blankBottom - blankTop); + textureH = atlasRegion.height / (1 - blankRight - blankLeft); + left = (Math.max(blankBottom, 1 - regionY - region.height) - blankBottom) * textureW + atlasRegionX; + top = (Math.max(blankLeft, regionX) - blankLeft) * textureH + atlasRegionY; + right = (Math.min(1 - blankTop, 1 - regionY) - blankBottom) * textureW + atlasRegionX; + bottom = (Math.min(1 - blankRight, regionX + region.width) - blankLeft) * textureH + atlasRegionY; } else { - // The size of the real rendering. - const realRenderWidth = - ((Math.min(rightBlankSpace, regionX + regionWidth) - Math.max(leftBlankSpace, regionX)) * originalSize.x) / - texture.width; - const realRenderHeight = - ((Math.min(downBlankSpace, regionY + regionHeight) - Math.max(topBlankSpace, regionY)) * originalSize.y) / - texture.height; - left = - ((Math.max(leftBlankSpace, regionX) - leftBlankSpace) * originalSize.x) / texture.width + atlasRegion.x; - top = ((Math.max(topBlankSpace, regionY) - topBlankSpace) * originalSize.y) / texture.height + atlasRegion.y; - right = left + realRenderWidth; - bottom = top + realRenderHeight; + textureW = atlasRegion.width / (1 - blankRight - blankLeft); + textureH = atlasRegion.height / (1 - blankBottom - blankTop); + left = (Math.max(blankLeft, regionX) - blankLeft) * textureW + atlasRegionX; + top = (Math.max(blankTop, regionY) - blankTop) * textureH + atlasRegionY; + right = (Math.min(1 - blankRight, regionX + region.width) - blankLeft) * textureW + atlasRegionX; + bottom = (Math.min(1 - blankBottom, regionY + region.height) - blankTop) * textureH + atlasRegionY; } } else { - left = atlasRegion.x + atlasRegion.width * regionX; - top = atlasRegion.y + atlasRegion.height * regionY; - right = left + atlasRegion.width * regionWidth; - bottom = top + atlasRegion.height * regionHeight; + const { width: atlasRegionW, height: atlasRegionH } = atlasRegion; + if (atlasRotated) { + left = atlasRegionW * (1 - region.y - region.height) + atlasRegion.x; + top = atlasRegionH * region.x + atlasRegion.y; + right = atlasRegionW * region.height + left; + bottom = atlasRegionH * region.width + top; + } else { + left = atlasRegionW * region.x + atlasRegion.x; + top = atlasRegionH * region.y + atlasRegion.y; + right = atlasRegionW * region.width + left; + bottom = atlasRegionH * region.height + top; + } } - if (this._atlasRotated) { + if (atlasRotated) { // If it is rotated, we need to rotate the UV 90 degrees counterclockwise to correct it. - // Top-left. - uv[0].setValue(right, top); // Top-right. - uv[1].setValue(right, bottom); + uv[0].setValue(right, top); // Bottom-right. - uv[2].setValue(left, bottom); + uv[1].setValue(right, bottom); // Bottom-left. + uv[2].setValue(left, bottom); + // Top-left. uv[3].setValue(left, top); } else { // Top-left. diff --git a/packages/loader/src/SpriteAtlasLoader.ts b/packages/loader/src/SpriteAtlasLoader.ts index 3f3ee22579..734cb298f4 100644 --- a/packages/loader/src/SpriteAtlasLoader.ts +++ b/packages/loader/src/SpriteAtlasLoader.ts @@ -50,7 +50,9 @@ class SpriteAtlasLoader extends Loader { const sourceHeightReciprocal = 1.0 / height; for (let j = sprites.length - 1; j >= 0; j--) { const atlasSprite = sprites[j]; - const { region, pivot, atlasRegionOffset, atlasRegion, originalSize } = atlasSprite; + const { region, pivot } = atlasSprite; + const { w: originalW, h: originalH } = atlasSprite.originalSize; + const { x: atlasRegionX, y: atlasRegionY, w: atlasRegionW, h: atlasRegionH } = atlasSprite.atlasRegion; const sprite = new Sprite( engine, texture, @@ -59,26 +61,39 @@ class SpriteAtlasLoader extends Loader { atlasSprite.pixelsPerUnit || undefined, atlasSprite.name ); - sprite.setPacked(true); sprite.atlasRegion.setValue( - atlasRegion.x * sourceWidthReciprocal, - atlasRegion.y * sourceHeightReciprocal, - atlasRegion.w * sourceWidthReciprocal, - atlasRegion.h * sourceHeightReciprocal + atlasRegionX * sourceWidthReciprocal, + atlasRegionY * sourceHeightReciprocal, + atlasRegionW * sourceWidthReciprocal, + atlasRegionH * sourceHeightReciprocal ); + let isTrimmed: boolean; // The original size is a necessary parameter. - sprite.originalSize = tempVect2.setValue(originalSize.w, originalSize.h); if (atlasSprite.atlasRotated) { sprite.atlasRotated = true; - sprite.setTrimmed(originalSize.w != atlasRegion.w || originalSize.h != atlasRegion.h); + isTrimmed = originalW != atlasRegionH || originalH != atlasRegionW; } else { - sprite.setTrimmed(originalSize.h != atlasRegion.w || originalSize.w != atlasRegion.h); + isTrimmed = originalW != atlasRegionW || originalH != atlasRegionH; + } + if (isTrimmed) { + sprite.setTrimmed(true); + const { x: atlasRegionOffsetX, y: atlasRegionOffsetY } = atlasSprite.atlasRegionOffset; + if (atlasSprite.atlasRotated) { + sprite.atlasRegionOffset.setValue( + atlasRegionOffsetX / originalW, + atlasRegionOffsetY / originalH, + 1 - (atlasRegionH + atlasRegionOffsetX) / originalW, + 1 - (atlasRegionW + atlasRegionOffsetY) / originalH + ); + } else { + sprite.atlasRegionOffset.setValue( + atlasRegionOffsetX / originalW, + atlasRegionOffsetY / originalH, + 1 - (atlasRegionW + atlasRegionOffsetX) / originalW, + 1 - (atlasRegionH + atlasRegionOffsetY) / originalH + ); + } } - atlasRegionOffset && - sprite.atlasRegionOffset.setValue( - atlasRegionOffset.x / originalSize.w, - atlasRegionOffset.y / originalSize.h - ); /** @ts-ignore */ spriteAtlas._addSprite(sprite); } From fb91468f0e005f97c52b39cbe2ebf08fda0beafd Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Wed, 18 Aug 2021 11:31:28 +0800 Subject: [PATCH 26/72] =?UTF-8?q?fix:Fixed=20sprite=E2=80=99s=20rect=20dis?= =?UTF-8?q?play=20error=20when=20packaged=20into=20an=20atlas=20and=20trim?= =?UTF-8?q?med?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/src/2d/atlas/types.ts | 4 +- packages/loader/src/SpriteAtlasLoader.ts | 48 ++++++++---------------- 2 files changed, 18 insertions(+), 34 deletions(-) diff --git a/packages/core/src/2d/atlas/types.ts b/packages/core/src/2d/atlas/types.ts index 23f003c5a2..0435683bf0 100644 --- a/packages/core/src/2d/atlas/types.ts +++ b/packages/core/src/2d/atlas/types.ts @@ -29,8 +29,8 @@ export interface AtlasSprite { atlasRotated: boolean; /** The range of the sprites on the big picture. */ atlasRegion: { x: number; y: number; w: number; h: number }; - /** If there is trimming, the offset of the sprite relative to the original sprite. */ - atlasRegionOffset: { x: number; y: number }; + /** If there is trimming, the size of the blank area on the four sides. */ + atlasRegionOffset: { x: number; y: number; z: number; w: number }; region: { x: number; y: number; w: number; h: number }; pivot: { x: number; y: number }; pixelsPerUnit: number; diff --git a/packages/loader/src/SpriteAtlasLoader.ts b/packages/loader/src/SpriteAtlasLoader.ts index 734cb298f4..2f28b38184 100644 --- a/packages/loader/src/SpriteAtlasLoader.ts +++ b/packages/loader/src/SpriteAtlasLoader.ts @@ -50,9 +50,7 @@ class SpriteAtlasLoader extends Loader { const sourceHeightReciprocal = 1.0 / height; for (let j = sprites.length - 1; j >= 0; j--) { const atlasSprite = sprites[j]; - const { region, pivot } = atlasSprite; - const { w: originalW, h: originalH } = atlasSprite.originalSize; - const { x: atlasRegionX, y: atlasRegionY, w: atlasRegionW, h: atlasRegionH } = atlasSprite.atlasRegion; + const { region, pivot, atlasRegionOffset, atlasRegion } = atlasSprite; const sprite = new Sprite( engine, texture, @@ -62,37 +60,23 @@ class SpriteAtlasLoader extends Loader { atlasSprite.name ); sprite.atlasRegion.setValue( - atlasRegionX * sourceWidthReciprocal, - atlasRegionY * sourceHeightReciprocal, - atlasRegionW * sourceWidthReciprocal, - atlasRegionH * sourceHeightReciprocal + atlasRegion.x * sourceWidthReciprocal, + atlasRegion.y * sourceHeightReciprocal, + atlasRegion.w * sourceWidthReciprocal, + atlasRegion.h * sourceHeightReciprocal ); - let isTrimmed: boolean; - // The original size is a necessary parameter. - if (atlasSprite.atlasRotated) { - sprite.atlasRotated = true; - isTrimmed = originalW != atlasRegionH || originalH != atlasRegionW; - } else { - isTrimmed = originalW != atlasRegionW || originalH != atlasRegionH; - } - if (isTrimmed) { + atlasSprite.atlasRotated && (sprite.atlasRotated = true); + if (atlasRegionOffset) { sprite.setTrimmed(true); - const { x: atlasRegionOffsetX, y: atlasRegionOffsetY } = atlasSprite.atlasRegionOffset; - if (atlasSprite.atlasRotated) { - sprite.atlasRegionOffset.setValue( - atlasRegionOffsetX / originalW, - atlasRegionOffsetY / originalH, - 1 - (atlasRegionH + atlasRegionOffsetX) / originalW, - 1 - (atlasRegionW + atlasRegionOffsetY) / originalH - ); - } else { - sprite.atlasRegionOffset.setValue( - atlasRegionOffsetX / originalW, - atlasRegionOffsetY / originalH, - 1 - (atlasRegionW + atlasRegionOffsetX) / originalW, - 1 - (atlasRegionH + atlasRegionOffsetY) / originalH - ); - } + const { originalSize } = atlasSprite; + const originalWReciprocal = 1 / originalSize.w; + const originalHReciprocal = 1 / originalSize.h; + sprite.atlasRegionOffset.setValue( + atlasRegionOffset.x * originalWReciprocal, + atlasRegionOffset.y * originalHReciprocal, + atlasRegionOffset.z * originalWReciprocal, + atlasRegionOffset.w * originalHReciprocal + ); } /** @ts-ignore */ spriteAtlas._addSprite(sprite); From ae707579851178cd7d9047e73deea995ea2ed7a8 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Wed, 18 Aug 2021 14:42:18 +0800 Subject: [PATCH 27/72] =?UTF-8?q?fix:Fixed=20sprite=E2=80=99s=20rect=20dis?= =?UTF-8?q?play=20error=20when=20packaged=20into=20an=20atlas=20and=20trim?= =?UTF-8?q?med?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/src/2d/sprite/Sprite.ts | 93 ++++++++++++------------ packages/loader/src/SpriteAtlasLoader.ts | 1 - 2 files changed, 48 insertions(+), 46 deletions(-) diff --git a/packages/core/src/2d/sprite/Sprite.ts b/packages/core/src/2d/sprite/Sprite.ts index 01bc3bc623..f66c05525b 100644 --- a/packages/core/src/2d/sprite/Sprite.ts +++ b/packages/core/src/2d/sprite/Sprite.ts @@ -23,7 +23,6 @@ export class Sprite extends RefObject { private _pixelsPerUnit: number; private _texture: Texture2D = null; - private _trimmed: boolean = false; private _atlasRotated: boolean = false; private _region: Rect = new Rect(0, 0, 1, 1); private _pivot: Vector2 = new Vector2(0.5, 0.5); @@ -72,7 +71,7 @@ export class Sprite extends RefObject { } /** - * The rectangle region of the original sprite on its atlas texture, specified in normalized. + * The rectangle region of the original texture on its atlas texture, specified in normalized. */ get atlasRegion(): Rect { return this._atlasRegion; @@ -86,7 +85,7 @@ export class Sprite extends RefObject { } /** - * The rectangle region offset of the original sprite on its atlas texture, specified in normalized. + * The rectangle region offset of the original texture on its atlas texture, specified in normalized. */ get atlasRegionOffset(): Vector4 { return this._atlasRegionOffset; @@ -140,14 +139,6 @@ export class Sprite extends RefObject { } } - /** - * Set whether to be trimmed. - * @param value Whether to be trimmed - */ - setTrimmed(value: boolean): void { - this._trimmed = value; - } - /** * Constructor a Sprite. * @param engine - Engine to which the sprite belongs @@ -192,7 +183,7 @@ export class Sprite extends RefObject { private _updatePositionsAndBounds(): void { const { _texture: texture, _bounds: bounds } = this; if (texture) { - const { _atlasRegion: atlasRegion, _pivot: pivot } = this; + const { _atlasRegion: atlasRegion, _pivot: pivot, _atlasRegionOffset: atlasRegionOffset } = this; const { x: regionX, y: regionY, width: regionW, height: regionH } = this._region; const pPUReciprocal = 1.0 / this._pixelsPerUnit; // Coordinates of the four boundaries. @@ -206,16 +197,13 @@ export class Sprite extends RefObject { textureW = texture.width * atlasRegion.width * pPUReciprocal; textureH = texture.height * atlasRegion.height * pPUReciprocal; } - if (this._trimmed) { - const { x: blankLeft, y: blankTop, z: blankRight, w: blankBottom } = this._atlasRegionOffset; - const oriWidth = textureW / (1 - blankRight - blankLeft); - const oriHeight = textureH / (1 - blankBottom - blankTop); - // The size of the real rendering. - lx = (-pivot.x * regionW + Math.max(blankLeft, regionX) - regionX) * oriWidth; - ty = (pivot.y * regionH - Math.max(blankTop, regionY) + regionY) * oriHeight; - rx = (-pivot.x * regionW + Math.min(1 - blankRight, regionX + regionW) - regionX) * oriWidth; - by = (pivot.y * regionH - Math.min(1 - blankBottom, regionY + regionH) + regionY) * oriHeight; - } else { + // Determine whether it has been trimmed. + if ( + atlasRegionOffset.x == 0 && + atlasRegionOffset.y == 0 && + atlasRegionOffset.z == 0 && + atlasRegionOffset.w == 0 + ) { // Real rendering size. const realRenderW = textureW * regionW; const realRenderH = textureH * regionH; @@ -223,6 +211,15 @@ export class Sprite extends RefObject { by = -pivot.y * realRenderH; rx = realRenderW + lx; ty = realRenderH + by; + } else { + const { x: blankLeft, y: blankTop, z: blankRight, w: blankBottom } = atlasRegionOffset; + const oriWidth = textureW / (1 - blankRight - blankLeft); + const oriHeight = textureH / (1 - blankBottom - blankTop); + // The size of the real rendering. + lx = (-pivot.x * regionW + Math.max(blankLeft, regionX) - regionX) * oriWidth; + ty = (pivot.y * regionH - Math.max(blankTop, regionY) + regionY) * oriHeight; + rx = (-pivot.x * regionW + Math.min(1 - blankRight, regionX + regionW) - regionX) * oriWidth; + by = (pivot.y * regionH - Math.min(1 - blankBottom, regionY + regionH) + regionY) * oriHeight; } // Assign values ​​to _positions @@ -255,45 +252,51 @@ export class Sprite extends RefObject { } if (this._isContainDirtyFlag(DirtyFlag.uv)) { - const { _atlasRegion: atlasRegion, _uv: uv, _region: region, _atlasRotated: atlasRotated } = this; + const { _atlasRegion, _uv: uv, _region: region, _atlasRotated, _atlasRegionOffset: atlasRegionOffset } = this; let left: number, top: number, right: number, bottom: number; - if (this._trimmed) { + // Determine whether it has been trimmed. + if ( + atlasRegionOffset.x == 0 && + atlasRegionOffset.y == 0 && + atlasRegionOffset.z == 0 && + atlasRegionOffset.w == 0 + ) { + const { width: atlasRegionW, height: atlasRegionH } = _atlasRegion; + if (_atlasRotated) { + left = atlasRegionW * (1 - region.y - region.height) + _atlasRegion.x; + top = atlasRegionH * region.x + _atlasRegion.y; + right = atlasRegionW * region.height + left; + bottom = atlasRegionH * region.width + top; + } else { + left = atlasRegionW * region.x + _atlasRegion.x; + top = atlasRegionH * region.y + _atlasRegion.y; + right = atlasRegionW * region.width + left; + bottom = atlasRegionH * region.height + top; + } + } else { const { x: regionX, y: regionY } = region; - const { x: atlasRegionX, y: atlasRegionY } = atlasRegion; - const { x: blankLeft, y: blankTop, z: blankRight, w: blankBottom } = this._atlasRegionOffset; + const { x: atlasRegionX, y: atlasRegionY } = _atlasRegion; + const { x: blankLeft, y: blankTop, z: blankRight, w: blankBottom } = atlasRegionOffset; // Proportion of the original sprite size in the atlas. let textureW: number, textureH: number; - if (atlasRotated) { - textureW = atlasRegion.width / (1 - blankBottom - blankTop); - textureH = atlasRegion.height / (1 - blankRight - blankLeft); + if (_atlasRotated) { + textureW = _atlasRegion.width / (1 - blankBottom - blankTop); + textureH = _atlasRegion.height / (1 - blankRight - blankLeft); left = (Math.max(blankBottom, 1 - regionY - region.height) - blankBottom) * textureW + atlasRegionX; top = (Math.max(blankLeft, regionX) - blankLeft) * textureH + atlasRegionY; right = (Math.min(1 - blankTop, 1 - regionY) - blankBottom) * textureW + atlasRegionX; bottom = (Math.min(1 - blankRight, regionX + region.width) - blankLeft) * textureH + atlasRegionY; } else { - textureW = atlasRegion.width / (1 - blankRight - blankLeft); - textureH = atlasRegion.height / (1 - blankBottom - blankTop); + textureW = _atlasRegion.width / (1 - blankRight - blankLeft); + textureH = _atlasRegion.height / (1 - blankBottom - blankTop); left = (Math.max(blankLeft, regionX) - blankLeft) * textureW + atlasRegionX; top = (Math.max(blankTop, regionY) - blankTop) * textureH + atlasRegionY; right = (Math.min(1 - blankRight, regionX + region.width) - blankLeft) * textureW + atlasRegionX; bottom = (Math.min(1 - blankBottom, regionY + region.height) - blankTop) * textureH + atlasRegionY; } - } else { - const { width: atlasRegionW, height: atlasRegionH } = atlasRegion; - if (atlasRotated) { - left = atlasRegionW * (1 - region.y - region.height) + atlasRegion.x; - top = atlasRegionH * region.x + atlasRegion.y; - right = atlasRegionW * region.height + left; - bottom = atlasRegionH * region.width + top; - } else { - left = atlasRegionW * region.x + atlasRegion.x; - top = atlasRegionH * region.y + atlasRegion.y; - right = atlasRegionW * region.width + left; - bottom = atlasRegionH * region.height + top; - } } - if (atlasRotated) { + if (_atlasRotated) { // If it is rotated, we need to rotate the UV 90 degrees counterclockwise to correct it. // Top-right. uv[0].setValue(right, top); diff --git a/packages/loader/src/SpriteAtlasLoader.ts b/packages/loader/src/SpriteAtlasLoader.ts index 2f28b38184..dc664ffb74 100644 --- a/packages/loader/src/SpriteAtlasLoader.ts +++ b/packages/loader/src/SpriteAtlasLoader.ts @@ -67,7 +67,6 @@ class SpriteAtlasLoader extends Loader { ); atlasSprite.atlasRotated && (sprite.atlasRotated = true); if (atlasRegionOffset) { - sprite.setTrimmed(true); const { originalSize } = atlasSprite; const originalWReciprocal = 1 / originalSize.w; const originalHReciprocal = 1 / originalSize.h; From b59a9fedb1e834c5f56c001e5e8f1d65091eb4c4 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Wed, 18 Aug 2021 14:57:23 +0800 Subject: [PATCH 28/72] =?UTF-8?q?fix:Fixed=20sprite=E2=80=99s=20rect=20dis?= =?UTF-8?q?play=20error=20when=20packaged=20into=20an=20atlas=20and=20trim?= =?UTF-8?q?med?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/src/2d/sprite/Sprite.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/2d/sprite/Sprite.ts b/packages/core/src/2d/sprite/Sprite.ts index f66c05525b..e4dbbeeb0f 100644 --- a/packages/core/src/2d/sprite/Sprite.ts +++ b/packages/core/src/2d/sprite/Sprite.ts @@ -81,7 +81,7 @@ export class Sprite extends RefObject { const x = MathUtil.clamp(value.x, 0, 1); const y = MathUtil.clamp(value.y, 0, 1); this._atlasRegion.setValue(x, y, MathUtil.clamp(value.width, 0, 1 - x), MathUtil.clamp(value.height, 0, 1 - y)); - this._setDirtyFlagTrue(DirtyFlag.uv); + this._setDirtyFlagTrue(DirtyFlag.positions | DirtyFlag.uv); } /** From 65478e74c81bbf6206438f871b48c3d785a0461d Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Wed, 18 Aug 2021 15:00:35 +0800 Subject: [PATCH 29/72] =?UTF-8?q?fix:Fixed=20sprite=E2=80=99s=20rect=20dis?= =?UTF-8?q?play=20error=20when=20packaged=20into=20an=20atlas=20and=20trim?= =?UTF-8?q?med?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/src/2d/sprite/Sprite.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/2d/sprite/Sprite.ts b/packages/core/src/2d/sprite/Sprite.ts index e4dbbeeb0f..bfed0b147b 100644 --- a/packages/core/src/2d/sprite/Sprite.ts +++ b/packages/core/src/2d/sprite/Sprite.ts @@ -99,7 +99,7 @@ export class Sprite extends RefObject { } /** - * Location of the sprite's center point in the rectangle region on the original sprite, specified in normalized. + * Location of the sprite's center point in the rectangle region, specified in normalized. */ get pivot(): Vector2 { return this._pivot; From 9e1631a94664fe7f99c2357fefa7024999516ed9 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Wed, 18 Aug 2021 15:19:54 +0800 Subject: [PATCH 30/72] =?UTF-8?q?fix:Fixed=20sprite=E2=80=99s=20rect=20dis?= =?UTF-8?q?play=20error=20when=20packaged=20into=20an=20atlas=20and=20trim?= =?UTF-8?q?med?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/src/2d/atlas/types.ts | 2 -- packages/loader/src/SpriteAtlasLoader.ts | 20 +++++++++++++------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/packages/core/src/2d/atlas/types.ts b/packages/core/src/2d/atlas/types.ts index 0435683bf0..dfa73ed238 100644 --- a/packages/core/src/2d/atlas/types.ts +++ b/packages/core/src/2d/atlas/types.ts @@ -23,8 +23,6 @@ export interface AtlasConfig { export interface AtlasSprite { /** The name the sprite. */ name: string; - /** The original size of the sprite. */ - originalSize: { w: number; h: number }; /** Whether to rotate 90 degrees clockwise. */ atlasRotated: boolean; /** The range of the sprites on the big picture. */ diff --git a/packages/loader/src/SpriteAtlasLoader.ts b/packages/loader/src/SpriteAtlasLoader.ts index dc664ffb74..68971ae123 100644 --- a/packages/loader/src/SpriteAtlasLoader.ts +++ b/packages/loader/src/SpriteAtlasLoader.ts @@ -67,14 +67,20 @@ class SpriteAtlasLoader extends Loader { ); atlasSprite.atlasRotated && (sprite.atlasRotated = true); if (atlasRegionOffset) { - const { originalSize } = atlasSprite; - const originalWReciprocal = 1 / originalSize.w; - const originalHReciprocal = 1 / originalSize.h; + const { x: offsetLeft, y: offsetTop, z: offsetRight, w: offsetBottom } = atlasRegionOffset; + let originalWReciprocal: number, originalHReciprocal: number; + if (atlasSprite.atlasRotated) { + originalWReciprocal = 1 / (offsetLeft + atlasRegion.h + offsetRight); + originalHReciprocal = 1 / (offsetTop + atlasRegion.w + offsetBottom); + } else { + originalWReciprocal = 1 / (offsetLeft + atlasRegion.w + offsetRight); + originalHReciprocal = 1 / (offsetTop + atlasRegion.h + offsetBottom); + } sprite.atlasRegionOffset.setValue( - atlasRegionOffset.x * originalWReciprocal, - atlasRegionOffset.y * originalHReciprocal, - atlasRegionOffset.z * originalWReciprocal, - atlasRegionOffset.w * originalHReciprocal + offsetLeft * originalWReciprocal, + offsetTop * originalHReciprocal, + offsetRight * originalWReciprocal, + offsetBottom * originalHReciprocal ); } /** @ts-ignore */ From 503523333798d5ccf8248daa3f79650fae50f7fa Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Wed, 18 Aug 2021 15:25:42 +0800 Subject: [PATCH 31/72] =?UTF-8?q?fix:Fixed=20sprite=E2=80=99s=20rect=20dis?= =?UTF-8?q?play=20error=20when=20packaged=20into=20an=20atlas=20and=20trim?= =?UTF-8?q?med?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/src/2d/sprite/Sprite.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/core/src/2d/sprite/Sprite.ts b/packages/core/src/2d/sprite/Sprite.ts index bfed0b147b..2274657d96 100644 --- a/packages/core/src/2d/sprite/Sprite.ts +++ b/packages/core/src/2d/sprite/Sprite.ts @@ -278,17 +278,16 @@ export class Sprite extends RefObject { const { x: atlasRegionX, y: atlasRegionY } = _atlasRegion; const { x: blankLeft, y: blankTop, z: blankRight, w: blankBottom } = atlasRegionOffset; // Proportion of the original sprite size in the atlas. - let textureW: number, textureH: number; if (_atlasRotated) { - textureW = _atlasRegion.width / (1 - blankBottom - blankTop); - textureH = _atlasRegion.height / (1 - blankRight - blankLeft); + const textureW = _atlasRegion.width / (1 - blankBottom - blankTop); + const textureH = _atlasRegion.height / (1 - blankRight - blankLeft); left = (Math.max(blankBottom, 1 - regionY - region.height) - blankBottom) * textureW + atlasRegionX; top = (Math.max(blankLeft, regionX) - blankLeft) * textureH + atlasRegionY; right = (Math.min(1 - blankTop, 1 - regionY) - blankBottom) * textureW + atlasRegionX; bottom = (Math.min(1 - blankRight, regionX + region.width) - blankLeft) * textureH + atlasRegionY; } else { - textureW = _atlasRegion.width / (1 - blankRight - blankLeft); - textureH = _atlasRegion.height / (1 - blankBottom - blankTop); + const textureW = _atlasRegion.width / (1 - blankRight - blankLeft); + const textureH = _atlasRegion.height / (1 - blankBottom - blankTop); left = (Math.max(blankLeft, regionX) - blankLeft) * textureW + atlasRegionX; top = (Math.max(blankTop, regionY) - blankTop) * textureH + atlasRegionY; right = (Math.min(1 - blankRight, regionX + region.width) - blankLeft) * textureW + atlasRegionX; From c20f99468675de7a501ff93fc6c4fc052caec789 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Wed, 18 Aug 2021 16:35:37 +0800 Subject: [PATCH 32/72] feat:Add the function of relative path loading --- packages/loader/src/SpriteAtlasLoader.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/loader/src/SpriteAtlasLoader.ts b/packages/loader/src/SpriteAtlasLoader.ts index 68971ae123..e6cc0d395c 100644 --- a/packages/loader/src/SpriteAtlasLoader.ts +++ b/packages/loader/src/SpriteAtlasLoader.ts @@ -11,6 +11,7 @@ import { } from "@oasis-engine/core"; import { AtlasConfig } from "@oasis-engine/core/types/2d/atlas/types"; import { Rect, Vector2 } from "@oasis-engine/math"; +import { GLTFUtil } from "./gltf/GLTFUtil"; @resourceLoader(AssetType.SpriteAtlas, ["atlas"], false) class SpriteAtlasLoader extends Loader { @@ -25,7 +26,7 @@ class SpriteAtlasLoader extends Loader { const atlasItemsLen = atlasItems.length; Promise.all( atlasItems.map(({ img }) => - this.request(img, { + this.request(GLTFUtil.parseRelativeUrl(item.url, img), { ...item, type: "image" }) From 648263245cf77a4b7d1327a4d0d573132e98e2cf Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Mon, 30 Aug 2021 13:38:12 +0800 Subject: [PATCH 33/72] feat:SpriteAtlas Adaptation Editor Workflow --- packages/core/src/2d/atlas/types.ts | 2 + packages/core/src/2d/sprite/Sprite.ts | 2 + packages/loader/src/SpriteAtlasLoader.ts | 12 ++-- .../src/scene-loader/ResourceManager.ts | 4 +- .../resources/SpriteAtlasResource.ts | 61 +++++++++++++++++++ .../scene-loader/resources/SpriteResource.ts | 27 ++++---- 6 files changed, 90 insertions(+), 18 deletions(-) create mode 100644 packages/loader/src/scene-loader/resources/SpriteAtlasResource.ts diff --git a/packages/core/src/2d/atlas/types.ts b/packages/core/src/2d/atlas/types.ts index dfa73ed238..0e3c585c75 100644 --- a/packages/core/src/2d/atlas/types.ts +++ b/packages/core/src/2d/atlas/types.ts @@ -21,6 +21,8 @@ export interface AtlasConfig { * The original data type of each sprite. */ export interface AtlasSprite { + /** Asset id in the editor. */ + id: number; /** The name the sprite. */ name: string; /** Whether to rotate 90 degrees clockwise. */ diff --git a/packages/core/src/2d/sprite/Sprite.ts b/packages/core/src/2d/sprite/Sprite.ts index 2274657d96..caf8f87a9d 100644 --- a/packages/core/src/2d/sprite/Sprite.ts +++ b/packages/core/src/2d/sprite/Sprite.ts @@ -20,6 +20,8 @@ export class Sprite extends RefObject { _bounds: BoundingBox = new BoundingBox(); /** @internal */ _triangles: number[]; + /** @internal temp solution. */ + _assetID: number; private _pixelsPerUnit: number; private _texture: Texture2D = null; diff --git a/packages/loader/src/SpriteAtlasLoader.ts b/packages/loader/src/SpriteAtlasLoader.ts index e6cc0d395c..b42812d415 100644 --- a/packages/loader/src/SpriteAtlasLoader.ts +++ b/packages/loader/src/SpriteAtlasLoader.ts @@ -33,12 +33,12 @@ class SpriteAtlasLoader extends Loader { ) ).then((imgs) => { const { engine } = resourceManager; - // Generate a SpriteAtlas object + // Generate a SpriteAtlas object. const tempRect = new Rect(); const tempVect2 = new Vector2(); const spriteAtlas = new SpriteAtlas(engine); for (let i = 0; i < atlasItemsLen; i++) { - // Generate Texture2D according to configuration + // Generate Texture2D according to configuration. const originalImg = imgs[i]; const { width, height } = originalImg; const texture = new Texture2D(engine, width, height, format); @@ -51,7 +51,7 @@ class SpriteAtlasLoader extends Loader { const sourceHeightReciprocal = 1.0 / height; for (let j = sprites.length - 1; j >= 0; j--) { const atlasSprite = sprites[j]; - const { region, pivot, atlasRegionOffset, atlasRegion } = atlasSprite; + const { region, pivot, atlasRegionOffset, atlasRegion, id } = atlasSprite; const sprite = new Sprite( engine, texture, @@ -84,7 +84,11 @@ class SpriteAtlasLoader extends Loader { offsetBottom * originalHReciprocal ); } - /** @ts-ignore */ + if (id !== undefined) { + // @ts-ignore + sprite._assetID = id; + } + // @ts-ignore spriteAtlas._addSprite(sprite); } } diff --git a/packages/loader/src/scene-loader/ResourceManager.ts b/packages/loader/src/scene-loader/ResourceManager.ts index e1afd3726d..315100d85e 100644 --- a/packages/loader/src/scene-loader/ResourceManager.ts +++ b/packages/loader/src/scene-loader/ResourceManager.ts @@ -14,6 +14,7 @@ import { TextureResource, UnlitMaterialResource } from "./resources"; +import { SpriteAtlasResource } from "./resources/SpriteAtlasResource"; import { AssetConfig } from "./types"; export const RESOURCE_CLASS = { @@ -28,7 +29,8 @@ export const RESOURCE_CLASS = { BlinnPhongMaterial: BlinnPhongMaterialResource, // Animation: Animation, base: BaseResource, - sprite: SpriteResource + sprite: SpriteResource, + SpriteAtlas: SpriteAtlasResource }; const RESOURCE_TYPE: Map = new Map(); diff --git a/packages/loader/src/scene-loader/resources/SpriteAtlasResource.ts b/packages/loader/src/scene-loader/resources/SpriteAtlasResource.ts new file mode 100644 index 0000000000..0e4404c5ab --- /dev/null +++ b/packages/loader/src/scene-loader/resources/SpriteAtlasResource.ts @@ -0,0 +1,61 @@ +import { AssetType, ResourceManager, SpriteAtlas } from "@oasis-engine/core"; +import { AssetConfig } from "../types"; +import { getAllGetters } from "../utils"; +import { SchemaResource } from "./SchemaResource"; +import { SpriteResource } from "./SpriteResource"; + +export class SpriteAtlasResource extends SchemaResource { + static defaultAtlas: SpriteAtlas; + + load(resourceManager: ResourceManager, assetConfig: AssetConfig): Promise { + return new Promise((resolve) => { + this.setMeta(); + if (assetConfig.props.url) { + resourceManager + .load({ + url: assetConfig.props.url, + type: AssetType.SpriteAtlas + }) + .then((spriteAtlas) => { + this._resource = spriteAtlas; + const { sprites } = spriteAtlas; + const schemaResourceManager = this.resourceManager; + for (let index = sprites.length - 1; index >= 0; index--) { + const sprite = sprites[index]; + const spriteResource = new SpriteResource(schemaResourceManager, sprite); + // @ts-ignore + const assetID = sprite._assetID; + // @ts-ignore + schemaResourceManager.maxId = Math.max(assetID, schemaResourceManager.maxId); + // @ts-ignore + schemaResourceManager.resourceMap[assetID] = spriteResource; + // @ts-ignore + schemaResourceManager.resourceIdMap.set(spriteResource, "" + assetID); + } + resolve(this); + }); + } else { + if (!SpriteAtlasResource.defaultAtlas) { + SpriteAtlasResource.defaultAtlas = new SpriteAtlas(resourceManager.engine); + } + this._resource = SpriteAtlasResource.defaultAtlas; + resolve(this); + } + }); + } + + setMeta() { + if (this.resource) { + this.meta.name = this.resource.name; + } + } + + getProps() { + const result = {}; + const props = getAllGetters(this.resource); + props.forEach((prop) => (result[prop] = this.resource[prop])); + return result; + } + + update() {} +} diff --git a/packages/loader/src/scene-loader/resources/SpriteResource.ts b/packages/loader/src/scene-loader/resources/SpriteResource.ts index eb0a3596e9..915f5216a9 100644 --- a/packages/loader/src/scene-loader/resources/SpriteResource.ts +++ b/packages/loader/src/scene-loader/resources/SpriteResource.ts @@ -127,21 +127,22 @@ export class SpriteResource extends SchemaResource { bind() { const resource = this._resource; - Object.keys(this.configProps).forEach((attr) => { - const value = this.configProps[attr]; - if (isAsset(value)) { - const textureResource = this.resourceManager.get(value.id); - if (textureResource && textureResource instanceof TextureResource) { - resource[attr] = textureResource.resource; - this._attachedResources.push(textureResource); + this.configProps && + Object.keys(this.configProps).forEach((attr) => { + const value = this.configProps[attr]; + if (isAsset(value)) { + const textureResource = this.resourceManager.get(value.id); + if (textureResource && textureResource instanceof TextureResource) { + resource[attr] = textureResource.resource; + this._attachedResources.push(textureResource); + } else { + resource[attr] = null; + Logger.warn(`SpriteResource: ${this.meta.name} can't find asset "${attr}", which id is: ${value.id}`); + } } else { - resource[attr] = null; - Logger.warn(`SpriteResource: ${this.meta.name} can't find asset "${attr}", which id is: ${value.id}`); + resource[attr] = value; } - } else { - resource[attr] = value; - } - }); + }); } } From 509125eb5be74198116f4bf93b54c25ebf03b3d1 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Mon, 30 Aug 2021 21:41:59 +0800 Subject: [PATCH 34/72] feat:SpriteAtlas Adaptation Editor Workflow --- .../loader/src/scene-loader/resources/SpriteAtlasResource.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/loader/src/scene-loader/resources/SpriteAtlasResource.ts b/packages/loader/src/scene-loader/resources/SpriteAtlasResource.ts index 0e4404c5ab..10e04b4079 100644 --- a/packages/loader/src/scene-loader/resources/SpriteAtlasResource.ts +++ b/packages/loader/src/scene-loader/resources/SpriteAtlasResource.ts @@ -10,10 +10,10 @@ export class SpriteAtlasResource extends SchemaResource { load(resourceManager: ResourceManager, assetConfig: AssetConfig): Promise { return new Promise((resolve) => { this.setMeta(); - if (assetConfig.props.url) { + if (assetConfig.source) { resourceManager .load({ - url: assetConfig.props.url, + url: assetConfig.source, type: AssetType.SpriteAtlas }) .then((spriteAtlas) => { From 7f029a384cab16535e58875d6c49a3e9e4675b3f Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Tue, 31 Aug 2021 17:15:27 +0800 Subject: [PATCH 35/72] feat:SpriteAtlas Adaptation Editor Workflow --- packages/core/src/2d/atlas/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/2d/atlas/types.ts b/packages/core/src/2d/atlas/types.ts index 0e3c585c75..39c511622c 100644 --- a/packages/core/src/2d/atlas/types.ts +++ b/packages/core/src/2d/atlas/types.ts @@ -21,7 +21,7 @@ export interface AtlasConfig { * The original data type of each sprite. */ export interface AtlasSprite { - /** Asset id in the editor. */ + /** @internal temp solution. */ id: number; /** The name the sprite. */ name: string; From fe07a297d44e39a57d14d0f1842debffd0144903 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Wed, 1 Sep 2021 15:26:44 +0800 Subject: [PATCH 36/72] fix:code err --- packages/core/src/2d/atlas/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/2d/atlas/types.ts b/packages/core/src/2d/atlas/types.ts index 39c511622c..7256efe2e9 100644 --- a/packages/core/src/2d/atlas/types.ts +++ b/packages/core/src/2d/atlas/types.ts @@ -21,7 +21,7 @@ export interface AtlasConfig { * The original data type of each sprite. */ export interface AtlasSprite { - /** @internal temp solution. */ + /** Temp solution. */ id: number; /** The name the sprite. */ name: string; From 5a8ad2a0ebc8adf8e6febef0108eb0b008a32ed9 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Wed, 8 Sep 2021 16:12:05 +0800 Subject: [PATCH 37/72] feat: Add InputManager. --- packages/core/src/Engine.ts | 14 +- packages/core/src/Script.ts | 28 +- packages/core/src/input/Input.ts | 15 + packages/core/src/input/InputManager.ts | 362 ++++++++++++++++++++++++ packages/core/src/input/Pointer.ts | 27 ++ 5 files changed, 441 insertions(+), 5 deletions(-) create mode 100644 packages/core/src/input/Input.ts create mode 100644 packages/core/src/input/InputManager.ts create mode 100644 packages/core/src/input/Pointer.ts diff --git a/packages/core/src/Engine.ts b/packages/core/src/Engine.ts index 7b1eba4cfb..88e20d995e 100644 --- a/packages/core/src/Engine.ts +++ b/packages/core/src/Engine.ts @@ -28,6 +28,7 @@ import { Texture2D, TextureCubeFace, TextureCubeMap, TextureFormat } from "./tex import { PhysicsManager } from "./PhysicsManager"; import { ModelMesh, PrimitiveMesh } from "./mesh"; import { CompareFunction } from "./shader"; +import { InputManager } from "./input/InputManager"; /** TODO: delete */ const engineFeatureManager = new FeatureManager(); @@ -41,6 +42,7 @@ export class Engine extends EventDispatcher { readonly physicsManager: PhysicsManager = new PhysicsManager(this); _componentsManager: ComponentsManager = new ComponentsManager(); + _inputManager: InputManager; _hardwareRenderer: IHardwareRenderer; _lastRenderState: RenderState = new RenderState(); _renderElementPool: ClassPool = new ClassPool(RenderElement); @@ -170,6 +172,8 @@ export class Engine extends EventDispatcher { this._spriteMaskManager = new SpriteMaskManager(this); this._spriteDefaultMaterial = this._createSpriteMaterial(); this._spriteMaskDefaultMaterial = this._createSpriteMaskMaterial(); + /** Init InputManager. */ + this._inputManager = new InputManager(this); const whitePixel = new Uint8Array([255, 255, 255, 255]); @@ -242,11 +246,18 @@ export class Engine extends EventDispatcher { const scene = this._sceneManager._activeScene; const componentsManager = this._componentsManager; if (scene) { + const cameras = scene._activeCameras; + if (cameras.length > 0) { + /** Sort on priority. */ + cameras.sort((camera1, camera2) => camera1.priority - camera2.priority); + } + componentsManager.callScriptOnStart(); componentsManager.callScriptOnUpdate(deltaTime); componentsManager.callAnimationUpdate(deltaTime); componentsManager.callScriptOnLateUpdate(deltaTime); + this._inputManager._update(); this._render(scene); } @@ -328,9 +339,6 @@ export class Engine extends EventDispatcher { scene._updateShaderData(); if (cameras.length > 0) { - // Sort on priority - //@ts-ignore - cameras.sort((camera1, camera2) => camera1.priority - camera2.priority); for (let i = 0, l = cameras.length; i < l; i++) { const camera = cameras[i]; const cameraEntity = camera.entity; diff --git a/packages/core/src/Script.ts b/packages/core/src/Script.ts index aa6201a3a1..6607011331 100644 --- a/packages/core/src/Script.ts +++ b/packages/core/src/Script.ts @@ -23,6 +23,9 @@ export class Script extends Component { _onPreRenderIndex: number = -1; /** @internal */ @ignoreClone + _listenInput: boolean = false; + /** @internal */ + @ignoreClone _onPostRenderIndex: number = -1; @ignoreClone _entityCacheIndex: number = -1; @@ -141,7 +144,8 @@ export class Script extends Component { * @override */ _onEnable(): void { - const componentsManager = this.engine._componentsManager; + const { engine } = this; + const { _componentsManager: componentsManager } = engine; const prototype = Script.prototype; if (!this._started) { componentsManager.addOnStartScript(this); @@ -152,6 +156,21 @@ export class Script extends Component { if (this.onLateUpdate !== prototype.onLateUpdate) { componentsManager.addOnLateUpdateScript(this); } + // Input callback. + if ( + this.onPointerDown !== prototype.onPointerDown || + this.onPointerUp !== prototype.onPointerUp || + this.onPointerClick !== prototype.onPointerClick || + this.onPointerDrag !== prototype.onPointerDrag || + this.onPointerEnter !== prototype.onPointerEnter || + this.onPointerExit !== prototype.onPointerExit + ) { + if (!this._listenInput) { + engine._inputManager.on(); + this._listenInput = true; + } + } + this._entity._addScript(this); this.onEnable(); } @@ -162,7 +181,8 @@ export class Script extends Component { * @override */ _onDisable(): void { - const componentsManager = this.engine._componentsManager; + const { engine } = this; + const { _componentsManager: componentsManager } = engine; // Use "xxIndex" is more safe. // When call onDisable it maybe it still not in script queue,for example write "entity.isActive = false" in onWake(). if (this._onStartIndex !== -1) { @@ -174,6 +194,10 @@ export class Script extends Component { if (this._onLateUpdateIndex !== -1) { componentsManager.removeOnLateUpdateScript(this); } + if (this._listenInput) { + engine._inputManager.off(); + this._listenInput = false; + } if (this._entityCacheIndex !== -1) { this._entity._removeScript(this); } diff --git a/packages/core/src/input/Input.ts b/packages/core/src/input/Input.ts new file mode 100644 index 0000000000..13098baa9c --- /dev/null +++ b/packages/core/src/input/Input.ts @@ -0,0 +1,15 @@ +import { Entity } from "../Entity"; + +/** + * Input. + */ +export class Input { + /** Current pageX. */ + pageX: number = 0; + /** Current pageY. */ + pageY: number = 0; + /** Currently pressed entity. */ + pressedEntity: Entity = null; + /** Currently entered entity. */ + enteredEntity: Entity = null; +} diff --git a/packages/core/src/input/InputManager.ts b/packages/core/src/input/InputManager.ts new file mode 100644 index 0000000000..b5febb6005 --- /dev/null +++ b/packages/core/src/input/InputManager.ts @@ -0,0 +1,362 @@ +import { Ray, Vector2 } from "@oasis-engine/math"; +import { Engine } from "../Engine"; +import { Entity } from "../Entity"; +import { HitResult } from "../HitResult"; +import { PhysicsManager } from "../PhysicsManager"; +import { SceneManager } from "../SceneManager"; +import { Script } from "../Script"; +import { Input } from "./Input"; +import { Pointer } from "./Pointer"; + +export enum EnumPointerEventType { + PointDown = 0, + PointUp = 1, + PointMove = 2 +} + +export enum EnumPointerChangeType { + Update = 0, + Remove = 1 +} + +/** + * Input Manager. + */ +export class InputManager { + /** Canvas to listen for input. */ + private _canvas: HTMLCanvasElement | OffscreenCanvas; + /** SceneManager. */ + private _sceneMgr: SceneManager; + /** PhysicsManager. */ + private _physicsMgr: PhysicsManager; + + /** Current simulated input. */ + private _input: Input = new Input(); + /** The number of events received in the current frame monitor. */ + private _eventLen: number = 0; + /** Event list. */ + private _eventList: EnumPointerEventType[] = []; + /** The number of pointers currently active. */ + private _actPointerCount: number = 0; + /** Pointer list. */ + private _pointerList: Pointer[] = []; + /** 'PointerId' to 'index' mapping. */ + private _pointerIdToIndex: Record = {}; + + /** Temporary variables. */ + private _tempRay: Ray = new Ray(); + private _tempPoint: Vector2 = new Vector2(); + private _tempHitResult: HitResult = new HitResult(); + + /** Whether to support multi-touch. */ + private _multiTouchEnabled: boolean = false; + /** Number of scripts registered to listen. */ + private _regScriptCount: number = 0; + /** Is it currently listening. */ + private _hadListener: boolean = false; + + /** + * Whether to support multi-touch. + */ + get multiTouchEnabled(): boolean { + return this._multiTouchEnabled; + } + + set multiTouchEnabled(enabled: boolean) { + this._multiTouchEnabled = enabled; + } + + /** + * Add the script that listens the input. + */ + on() { + if (this._regScriptCount++ == 0) { + this._updateListener(); + } + } + + /** + * Remove the script that listens for input. + */ + off() { + if (this._regScriptCount-- == 0) { + this._updateListener(); + } + } + + /** + * Get touch pointer. + * @param idx - Index of touch pointer + * @return Touch pointer + * @remarks The returned Pointer should be considered deep-read-only. + */ + getTouch(idx: number): Pointer { + if (idx < this._actPointerCount) { + return this._pointerList[idx]; + } else { + return null; + } + } + + /** + * Get input. + * @return Input + * @remarks The returned Input should be considered deep-read-only. + */ + getInput(): Input { + return this._input; + } + + /** + * Update pointer event, will be executed every frame. + * @internal + */ + _update() { + const { _eventLen } = this; + if (_eventLen > 0 || this._actPointerCount > 0) { + const { _eventList, _input } = this; + let prePressedEntity = _input.pressedEntity; + if (prePressedEntity) { + /** Pointer Drag. */ + const preScripts = prePressedEntity._scripts._elements; + for (let i = preScripts.length - 1; i >= 0; i--) { + preScripts[i].onPointerDrag(); + } + } + /** Get the entity hit by the ray. */ + /** @ts-ignore */ + const { offsetLeft = 0, offsetTop = 0, clientWidth, clientHeight } = this._canvas; + const curEntity = this._pointerRayCast( + (_input.pageX - offsetLeft) / clientWidth, + (_input.pageY - offsetTop) / clientHeight + ); + const preEnteredEntity = _input.enteredEntity; + /** 80% of operations are on curEntity, so we cache his scripts. */ + let curEntityScripts: Script[]; + let curEntityScriptsLen = 0; + if (curEntity) { + curEntityScripts = curEntity._scripts._elements; + curEntityScriptsLen = curEntityScripts.length; + } + // Cache curEntity's Scripts. + if (preEnteredEntity != curEntity) { + if (curEntity) { + for (let i = 0; i < curEntityScriptsLen; i++) { + curEntityScripts[i].onPointerEnter(); + } + } + if (preEnteredEntity) { + const scripts = preEnteredEntity._scripts; + const scriptsArr = scripts._elements; + for (let i = scripts.length - 1; i >= 0; i--) { + scriptsArr[i].onPointerExit(); + } + } + _input.enteredEntity = curEntity; + } + for (let i = 0; i < _eventLen; i++) { + switch (_eventList[i]) { + case EnumPointerEventType.PointDown: + if (curEntity) { + for (let j = 0; j < curEntityScriptsLen; j++) { + curEntityScripts[j].onPointerDown(); + } + } + prePressedEntity = curEntity; + break; + case EnumPointerEventType.PointUp: + if (prePressedEntity) { + if (prePressedEntity == curEntity) { + for (let j = 0; j < curEntityScriptsLen; j++) { + const script = curEntityScripts[j]; + script.onPointerUp(); + script.onPointerClick(); + } + } else { + const preScripts = prePressedEntity._scripts._elements; + for (let j = preScripts.length - 1; j >= 0; j--) { + preScripts[j].onPointerUp(); + } + } + } + prePressedEntity = null; + break; + default: + break; + } + } + _input.pressedEntity = prePressedEntity; + this._eventLen = 0; + } + } + + /** + * Constructor an InputManager. + * @param engine - The current engine instance + */ + constructor(engine: Engine) { + // @ts-ignore + this._canvas = engine.canvas._webCanvas; + this._sceneMgr = engine.sceneManager; + this._physicsMgr = engine.physicsManager; + } + + /** + * Get the Entity to which the ray is cast. + * @param posX - The X coordinate of the pointer on the screen, specified in normalized + * @param posY - The Y coordinate of the pointer on the screen, specified in normalized + * @returns The Entity to which the ray is cast + */ + private _pointerRayCast(posX: number, posY: number): Entity { + const cameras = this._sceneMgr.activeScene._activeCameras; + for (let i = cameras.length - 1; i >= 0; i--) { + const camera = cameras[i]; + if (!camera.enabled) { + continue; + } + const { x: vpX, y: vpY, z: vpW, w: vpH } = camera.viewport; + if (posX >= vpX && posY >= vpY && posX - vpX <= vpW && posY - vpY <= vpH) { + const { _tempHitResult, _tempPoint } = this; + _tempPoint.setValue((posX - vpX) / vpW, (posY - vpY) / vpH); + // TODO: Only check which colliders have listened to the input. + if (this._physicsMgr.raycast(camera.viewportPointToRay(_tempPoint, this._tempRay), _tempHitResult)) { + return _tempHitResult.collider.entity; + } + } + } + return null; + } + + /** + * Update pointers info. + * @param type - Pointer change type + * @param pointerId - Pointer ID + * @param pageX - The pageX of Pointer + * @param pageY - The pageY of Pointer + */ + private _changePointer(type: EnumPointerChangeType, pointerId: number, pageX: number, pageY: number) { + const { _pointerIdToIndex, _pointerList, _actPointerCount: lastCount, _input } = this; + let idx = _pointerIdToIndex[pointerId]; + switch (type) { + case EnumPointerChangeType.Remove: + if (idx === undefined) { + return; + } else { + const nowCount = (this._actPointerCount = lastCount - 1); + if (nowCount != 0) { + const removedTouch = _pointerList[idx]; + if (idx !== lastCount) { + _pointerList[idx] = _pointerList[lastCount]; + _pointerList[lastCount] = removedTouch; + } + _input.pageX = (_input.pageX * lastCount - removedTouch.pageX) / nowCount; + _input.pageY = (_input.pageY * lastCount - removedTouch.pageY) / nowCount; + } + delete _pointerIdToIndex[pointerId]; + } + break; + case EnumPointerChangeType.Update: + if (idx === undefined) { + if (lastCount > 0 && !this._multiTouchEnabled) { + return; + } + let touch: Pointer; + if (!_pointerList[lastCount]) { + touch = _pointerList[lastCount] = new Pointer(pointerId, pageX, pageY, lastCount); + } else { + touch = _pointerList[lastCount]; + touch.pageX = pageX; + touch.pageY = pageY; + touch.indexInList = lastCount; + } + _pointerIdToIndex[pointerId] = lastCount; + const nowCount = (this._actPointerCount = lastCount + 1); + _input.pageX = (_input.pageX * lastCount + pageX) / nowCount; + _input.pageY = (_input.pageY * lastCount + pageY) / nowCount; + } else { + const touch = _pointerList[idx]; + _input.pageX += (pageX - touch.pageX) / lastCount; + _input.pageY += (pageY - touch.pageY) / lastCount; + touch.pageX = pageX; + touch.pageY = pageY; + } + break; + default: + break; + } + } + + /** + * Push event types into an ordered queue. + * @param type - Pointer event type + */ + private _pushEventList(type: EnumPointerEventType) { + const { _eventList } = this; + if (_eventList.length == this._eventLen) { + _eventList.push(type); + } else { + _eventList[this._eventLen] = type; + } + ++this._eventLen; + } + + /** + * On pointer down. + * @param evt - Pointer Event + */ + private _onPointerDown = (evt: PointerEvent) => { + this._changePointer(EnumPointerChangeType.Update, evt.pointerId, evt.pageX, evt.pageY); + if (this._actPointerCount == 1) { + this._pushEventList(EnumPointerEventType.PointDown); + } + }; + + /** + * On pointer up. + */ + private _onPointerUp = () => { + if (this._actPointerCount == 1) { + this._pushEventList(EnumPointerEventType.PointUp); + } + }; + + /** + * On pointer move. + * @param evt - Pointer Event + */ + private _onPointerMove = (evt: PointerEvent) => { + this._changePointer(EnumPointerChangeType.Update, evt.pointerId, evt.pageX, evt.pageY); + }; + + /** + * On pointer cancel or out. + * @param evt - Pointer Event + */ + private _onPointerCancelOrOut = (evt: PointerEvent) => { + this._changePointer(EnumPointerChangeType.Remove, evt.pointerId, evt.pageX, evt.pageY); + }; + + /** + * Update the current listening status. + */ + private _updateListener() { + const { _regScriptCount, _hadListener, _canvas: canvas } = this; + if (_regScriptCount > 0 && !_hadListener) { + canvas.addEventListener("pointerdown", this._onPointerDown); + canvas.addEventListener("pointerup", this._onPointerUp); + canvas.addEventListener("pointermove", this._onPointerMove); + canvas.addEventListener("pointercancel", this._onPointerCancelOrOut); + canvas.addEventListener("pointerout", this._onPointerCancelOrOut); + this._hadListener = true; + } else if (_regScriptCount <= 0 && _hadListener) { + canvas.removeEventListener("pointerdown", this._onPointerDown); + canvas.removeEventListener("pointerup", this._onPointerUp); + canvas.removeEventListener("pointermove", this._onPointerMove); + canvas.removeEventListener("pointercancel", this._onPointerCancelOrOut); + canvas.removeEventListener("pointerout", this._onPointerCancelOrOut); + this._eventLen = 0; + this._actPointerCount = 0; + this._hadListener = false; + } + } +} diff --git a/packages/core/src/input/Pointer.ts b/packages/core/src/input/Pointer.ts new file mode 100644 index 0000000000..485971ffe3 --- /dev/null +++ b/packages/core/src/input/Pointer.ts @@ -0,0 +1,27 @@ +/** + * Pointer. + */ +export class Pointer { + /** The pageX of the pointer. */ + pageX: number; + /** The pageY of the pointer. */ + pageY: number; + /** The pointerId of the pointer. */ + pointerId: number = -1; + /** The index of the pointer in pointerList. */ + indexInList: number = -1; + + /** + * Constructor a Pointer. + * @param pointerId - The pageX of the pointer + * @param pageX - The pageY of the pointer + * @param pageY - The pointerId of the pointer + * @param indexInList - The index of the pointer in pointerList + */ + constructor(pointerId: number, pageX: number, pageY: number, indexInList: number) { + this.pointerId = pointerId; + this.pageX = pageX; + this.pageY = pageY; + this.indexInList = indexInList; + } +} From 8965e6f7a060d3c7c42f0b71c3fec9629a52d599 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Wed, 8 Sep 2021 19:42:50 +0800 Subject: [PATCH 38/72] feat: Opt code. --- packages/core/src/input/InputManager.ts | 40 ++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/core/src/input/InputManager.ts b/packages/core/src/input/InputManager.ts index b5febb6005..01d41861cb 100644 --- a/packages/core/src/input/InputManager.ts +++ b/packages/core/src/input/InputManager.ts @@ -66,31 +66,13 @@ export class InputManager { this._multiTouchEnabled = enabled; } - /** - * Add the script that listens the input. - */ - on() { - if (this._regScriptCount++ == 0) { - this._updateListener(); - } - } - - /** - * Remove the script that listens for input. - */ - off() { - if (this._regScriptCount-- == 0) { - this._updateListener(); - } - } - /** * Get touch pointer. * @param idx - Index of touch pointer * @return Touch pointer * @remarks The returned Pointer should be considered deep-read-only. */ - getTouch(idx: number): Pointer { + getTouch(idx: number): Readonly { if (idx < this._actPointerCount) { return this._pointerList[idx]; } else { @@ -103,10 +85,28 @@ export class InputManager { * @return Input * @remarks The returned Input should be considered deep-read-only. */ - getInput(): Input { + getInput(): Readonly { return this._input; } + /** + * Add the script that listen to input. + */ + on() { + if (this._regScriptCount++ == 0) { + this._updateListener(); + } + } + + /** + * Remove the script that listen to input. + */ + off() { + if (this._regScriptCount-- == 0) { + this._updateListener(); + } + } + /** * Update pointer event, will be executed every frame. * @internal From 91398267c54d4ef592f531b6bd5e5aa82ff1fcac Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Sun, 12 Sep 2021 20:44:22 +0800 Subject: [PATCH 39/72] feat: Opt code. --- packages/core/src/input/InputManager.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/input/InputManager.ts b/packages/core/src/input/InputManager.ts index 01d41861cb..3f63a2b60d 100644 --- a/packages/core/src/input/InputManager.ts +++ b/packages/core/src/input/InputManager.ts @@ -234,7 +234,7 @@ export class InputManager { * @param pageX - The pageX of Pointer * @param pageY - The pageY of Pointer */ - private _changePointer(type: EnumPointerChangeType, pointerId: number, pageX: number, pageY: number) { + private _changePointer(type: EnumPointerChangeType, pointerId: number, pageX?: number, pageY?: number) { const { _pointerIdToIndex, _pointerList, _actPointerCount: lastCount, _input } = this; let idx = _pointerIdToIndex[pointerId]; switch (type) { @@ -333,7 +333,7 @@ export class InputManager { * @param evt - Pointer Event */ private _onPointerCancelOrOut = (evt: PointerEvent) => { - this._changePointer(EnumPointerChangeType.Remove, evt.pointerId, evt.pageX, evt.pageY); + this._changePointer(EnumPointerChangeType.Remove, evt.pointerId); }; /** From 9995fa4fc53a6f71c2292bff15dda70d4d7f2843 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Mon, 13 Sep 2021 14:01:40 +0800 Subject: [PATCH 40/72] feat: Opt code. --- packages/core/src/input/InputManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/input/InputManager.ts b/packages/core/src/input/InputManager.ts index 3f63a2b60d..a2372f7c8d 100644 --- a/packages/core/src/input/InputManager.ts +++ b/packages/core/src/input/InputManager.ts @@ -211,7 +211,7 @@ export class InputManager { const cameras = this._sceneMgr.activeScene._activeCameras; for (let i = cameras.length - 1; i >= 0; i--) { const camera = cameras[i]; - if (!camera.enabled) { + if (!camera.enabled || camera.renderTarget) { continue; } const { x: vpX, y: vpY, z: vpW, w: vpH } = camera.viewport; From ab2b88952b901e125926bada01aae51f0813661a Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Mon, 13 Sep 2021 16:16:05 +0800 Subject: [PATCH 41/72] feat: Opt code. --- packages/core/src/Engine.ts | 9 ++++----- packages/core/src/input/InputManager.ts | 12 ++++-------- packages/core/src/input/Pointer.ts | 11 +---------- 3 files changed, 9 insertions(+), 23 deletions(-) diff --git a/packages/core/src/Engine.ts b/packages/core/src/Engine.ts index 88e20d995e..5f79a1886c 100644 --- a/packages/core/src/Engine.ts +++ b/packages/core/src/Engine.ts @@ -42,7 +42,6 @@ export class Engine extends EventDispatcher { readonly physicsManager: PhysicsManager = new PhysicsManager(this); _componentsManager: ComponentsManager = new ComponentsManager(); - _inputManager: InputManager; _hardwareRenderer: IHardwareRenderer; _lastRenderState: RenderState = new RenderState(); _renderElementPool: ClassPool = new ClassPool(RenderElement); @@ -66,6 +65,8 @@ export class Engine extends EventDispatcher { _shaderProgramPools: ShaderProgramPool[] = []; /** @internal */ _spriteMaskManager: SpriteMaskManager; + /** @internal */ + _inputManager: InputManager; protected _canvas: Canvas; private _resourceManager: ResourceManager = new ResourceManager(this); @@ -247,10 +248,8 @@ export class Engine extends EventDispatcher { const componentsManager = this._componentsManager; if (scene) { const cameras = scene._activeCameras; - if (cameras.length > 0) { - /** Sort on priority. */ - cameras.sort((camera1, camera2) => camera1.priority - camera2.priority); - } + /** Sort on priority. */ + cameras.length > 0 && cameras.sort((camera1, camera2) => camera1.priority - camera2.priority); componentsManager.callScriptOnStart(); componentsManager.callScriptOnUpdate(deltaTime); diff --git a/packages/core/src/input/InputManager.ts b/packages/core/src/input/InputManager.ts index a2372f7c8d..252fb66ce7 100644 --- a/packages/core/src/input/InputManager.ts +++ b/packages/core/src/input/InputManager.ts @@ -73,11 +73,7 @@ export class InputManager { * @remarks The returned Pointer should be considered deep-read-only. */ getTouch(idx: number): Readonly { - if (idx < this._actPointerCount) { - return this._pointerList[idx]; - } else { - return null; - } + return idx < this._actPointerCount ? this._pointerList[idx] : null; } /** @@ -102,7 +98,7 @@ export class InputManager { * Remove the script that listen to input. */ off() { - if (this._regScriptCount-- == 0) { + if (--this._regScriptCount == 0) { this._updateListener(); } } @@ -126,7 +122,7 @@ export class InputManager { /** Get the entity hit by the ray. */ /** @ts-ignore */ const { offsetLeft = 0, offsetTop = 0, clientWidth, clientHeight } = this._canvas; - const curEntity = this._pointerRayCast( + const curEntity = this._pointerRaycast( (_input.pageX - offsetLeft) / clientWidth, (_input.pageY - offsetTop) / clientHeight ); @@ -207,7 +203,7 @@ export class InputManager { * @param posY - The Y coordinate of the pointer on the screen, specified in normalized * @returns The Entity to which the ray is cast */ - private _pointerRayCast(posX: number, posY: number): Entity { + private _pointerRaycast(posX: number, posY: number): Entity { const cameras = this._sceneMgr.activeScene._activeCameras; for (let i = cameras.length - 1; i >= 0; i--) { const camera = cameras[i]; diff --git a/packages/core/src/input/Pointer.ts b/packages/core/src/input/Pointer.ts index 485971ffe3..ea405b969b 100644 --- a/packages/core/src/input/Pointer.ts +++ b/packages/core/src/input/Pointer.ts @@ -2,15 +2,6 @@ * Pointer. */ export class Pointer { - /** The pageX of the pointer. */ - pageX: number; - /** The pageY of the pointer. */ - pageY: number; - /** The pointerId of the pointer. */ - pointerId: number = -1; - /** The index of the pointer in pointerList. */ - indexInList: number = -1; - /** * Constructor a Pointer. * @param pointerId - The pageX of the pointer @@ -18,7 +9,7 @@ export class Pointer { * @param pageY - The pointerId of the pointer * @param indexInList - The index of the pointer in pointerList */ - constructor(pointerId: number, pageX: number, pageY: number, indexInList: number) { + constructor(public pointerId: number, public pageX: number, public pageY: number, public indexInList: number) { this.pointerId = pointerId; this.pageX = pageX; this.pageY = pageY; From 33af8128eee1b0454b2590bb74bfe3f77b5425f2 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Mon, 13 Sep 2021 16:18:19 +0800 Subject: [PATCH 42/72] feat: Opt code. --- packages/core/src/input/InputManager.ts | 28 ++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/core/src/input/InputManager.ts b/packages/core/src/input/InputManager.ts index 252fb66ce7..f5445a3f07 100644 --- a/packages/core/src/input/InputManager.ts +++ b/packages/core/src/input/InputManager.ts @@ -8,13 +8,13 @@ import { Script } from "../Script"; import { Input } from "./Input"; import { Pointer } from "./Pointer"; -export enum EnumPointerEventType { +export enum PointerEventType { PointDown = 0, PointUp = 1, PointMove = 2 } -export enum EnumPointerChangeType { +export enum PointerChangeType { Update = 0, Remove = 1 } @@ -35,7 +35,7 @@ export class InputManager { /** The number of events received in the current frame monitor. */ private _eventLen: number = 0; /** Event list. */ - private _eventList: EnumPointerEventType[] = []; + private _eventList: PointerEventType[] = []; /** The number of pointers currently active. */ private _actPointerCount: number = 0; /** Pointer list. */ @@ -152,7 +152,7 @@ export class InputManager { } for (let i = 0; i < _eventLen; i++) { switch (_eventList[i]) { - case EnumPointerEventType.PointDown: + case PointerEventType.PointDown: if (curEntity) { for (let j = 0; j < curEntityScriptsLen; j++) { curEntityScripts[j].onPointerDown(); @@ -160,7 +160,7 @@ export class InputManager { } prePressedEntity = curEntity; break; - case EnumPointerEventType.PointUp: + case PointerEventType.PointUp: if (prePressedEntity) { if (prePressedEntity == curEntity) { for (let j = 0; j < curEntityScriptsLen; j++) { @@ -230,11 +230,11 @@ export class InputManager { * @param pageX - The pageX of Pointer * @param pageY - The pageY of Pointer */ - private _changePointer(type: EnumPointerChangeType, pointerId: number, pageX?: number, pageY?: number) { + private _changePointer(type: PointerChangeType, pointerId: number, pageX?: number, pageY?: number) { const { _pointerIdToIndex, _pointerList, _actPointerCount: lastCount, _input } = this; let idx = _pointerIdToIndex[pointerId]; switch (type) { - case EnumPointerChangeType.Remove: + case PointerChangeType.Remove: if (idx === undefined) { return; } else { @@ -251,7 +251,7 @@ export class InputManager { delete _pointerIdToIndex[pointerId]; } break; - case EnumPointerChangeType.Update: + case PointerChangeType.Update: if (idx === undefined) { if (lastCount > 0 && !this._multiTouchEnabled) { return; @@ -286,7 +286,7 @@ export class InputManager { * Push event types into an ordered queue. * @param type - Pointer event type */ - private _pushEventList(type: EnumPointerEventType) { + private _pushEventList(type: PointerEventType) { const { _eventList } = this; if (_eventList.length == this._eventLen) { _eventList.push(type); @@ -301,9 +301,9 @@ export class InputManager { * @param evt - Pointer Event */ private _onPointerDown = (evt: PointerEvent) => { - this._changePointer(EnumPointerChangeType.Update, evt.pointerId, evt.pageX, evt.pageY); + this._changePointer(PointerChangeType.Update, evt.pointerId, evt.pageX, evt.pageY); if (this._actPointerCount == 1) { - this._pushEventList(EnumPointerEventType.PointDown); + this._pushEventList(PointerEventType.PointDown); } }; @@ -312,7 +312,7 @@ export class InputManager { */ private _onPointerUp = () => { if (this._actPointerCount == 1) { - this._pushEventList(EnumPointerEventType.PointUp); + this._pushEventList(PointerEventType.PointUp); } }; @@ -321,7 +321,7 @@ export class InputManager { * @param evt - Pointer Event */ private _onPointerMove = (evt: PointerEvent) => { - this._changePointer(EnumPointerChangeType.Update, evt.pointerId, evt.pageX, evt.pageY); + this._changePointer(PointerChangeType.Update, evt.pointerId, evt.pageX, evt.pageY); }; /** @@ -329,7 +329,7 @@ export class InputManager { * @param evt - Pointer Event */ private _onPointerCancelOrOut = (evt: PointerEvent) => { - this._changePointer(EnumPointerChangeType.Remove, evt.pointerId); + this._changePointer(PointerChangeType.Remove, evt.pointerId); }; /** From a6aefa926d49d30a676e168a6659bc084d39efc5 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Mon, 13 Sep 2021 16:40:20 +0800 Subject: [PATCH 43/72] feat: Opt code. --- packages/core/src/input/InputManager.ts | 36 ++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/packages/core/src/input/InputManager.ts b/packages/core/src/input/InputManager.ts index f5445a3f07..4782ab2638 100644 --- a/packages/core/src/input/InputManager.ts +++ b/packages/core/src/input/InputManager.ts @@ -44,9 +44,9 @@ export class InputManager { private _pointerIdToIndex: Record = {}; /** Temporary variables. */ - private _tempRay: Ray = new Ray(); - private _tempPoint: Vector2 = new Vector2(); - private _tempHitResult: HitResult = new HitResult(); + private static _tempRay: Ray = new Ray(); + private static _tempPoint: Vector2 = new Vector2(); + private static _tempHitResult: HitResult = new HitResult(); /** Whether to support multi-touch. */ private _multiTouchEnabled: boolean = false; @@ -66,6 +66,17 @@ export class InputManager { this._multiTouchEnabled = enabled; } + /** + * Constructor an InputManager. + * @param engine - The current engine instance + */ + constructor(engine: Engine) { + // @ts-ignore + this._canvas = engine.canvas._webCanvas; + this._sceneMgr = engine.sceneManager; + this._physicsMgr = engine.physicsManager; + } + /** * Get touch pointer. * @param idx - Index of touch pointer @@ -186,17 +197,6 @@ export class InputManager { } } - /** - * Constructor an InputManager. - * @param engine - The current engine instance - */ - constructor(engine: Engine) { - // @ts-ignore - this._canvas = engine.canvas._webCanvas; - this._sceneMgr = engine.sceneManager; - this._physicsMgr = engine.physicsManager; - } - /** * Get the Entity to which the ray is cast. * @param posX - The X coordinate of the pointer on the screen, specified in normalized @@ -212,12 +212,12 @@ export class InputManager { } const { x: vpX, y: vpY, z: vpW, w: vpH } = camera.viewport; if (posX >= vpX && posY >= vpY && posX - vpX <= vpW && posY - vpY <= vpH) { - const { _tempHitResult, _tempPoint } = this; + const { _tempHitResult, _tempPoint } = InputManager; _tempPoint.setValue((posX - vpX) / vpW, (posY - vpY) / vpH); // TODO: Only check which colliders have listened to the input. - if (this._physicsMgr.raycast(camera.viewportPointToRay(_tempPoint, this._tempRay), _tempHitResult)) { - return _tempHitResult.collider.entity; - } + return this._physicsMgr.raycast(camera.viewportPointToRay(_tempPoint, InputManager._tempRay), _tempHitResult) + ? _tempHitResult.collider.entity + : null; } } return null; From 5f1298e25a850136ce1facaad013e2a553b233af Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Tue, 14 Sep 2021 11:17:38 +0800 Subject: [PATCH 44/72] feat: Opt code. --- packages/core/src/Engine.ts | 10 +++---- packages/core/src/Script.ts | 12 ++++----- packages/core/src/input/InputManager.ts | 35 ++++++++++++++++++------- 3 files changed, 35 insertions(+), 22 deletions(-) diff --git a/packages/core/src/Engine.ts b/packages/core/src/Engine.ts index 5f79a1886c..6e2188c6f7 100644 --- a/packages/core/src/Engine.ts +++ b/packages/core/src/Engine.ts @@ -173,7 +173,7 @@ export class Engine extends EventDispatcher { this._spriteMaskManager = new SpriteMaskManager(this); this._spriteDefaultMaterial = this._createSpriteMaterial(); this._spriteMaskDefaultMaterial = this._createSpriteMaskMaterial(); - /** Init InputManager. */ + this._inputManager = new InputManager(this); const whitePixel = new Uint8Array([255, 255, 255, 255]); @@ -247,16 +247,14 @@ export class Engine extends EventDispatcher { const scene = this._sceneManager._activeScene; const componentsManager = this._componentsManager; if (scene) { - const cameras = scene._activeCameras; - /** Sort on priority. */ - cameras.length > 0 && cameras.sort((camera1, camera2) => camera1.priority - camera2.priority); + scene._activeCameras.sort((camera1, camera2) => camera1.priority - camera2.priority); componentsManager.callScriptOnStart(); + this._inputManager._update(); componentsManager.callScriptOnUpdate(deltaTime); componentsManager.callAnimationUpdate(deltaTime); componentsManager.callScriptOnLateUpdate(deltaTime); - this._inputManager._update(); this._render(scene); } @@ -282,7 +280,7 @@ export class Engine extends EventDispatcher { if (this._sceneManager) { this._whiteTexture2D.destroy(true); this._whiteTextureCube.destroy(true); - + this._inputManager.destroy(); this.trigger(new Event("shutdown", this)); engineFeatureManager.callFeatureMethod(this, "shutdown", [this]); diff --git a/packages/core/src/Script.ts b/packages/core/src/Script.ts index 6607011331..9c371436d3 100644 --- a/packages/core/src/Script.ts +++ b/packages/core/src/Script.ts @@ -23,13 +23,13 @@ export class Script extends Component { _onPreRenderIndex: number = -1; /** @internal */ @ignoreClone - _listenInput: boolean = false; - /** @internal */ - @ignoreClone _onPostRenderIndex: number = -1; @ignoreClone _entityCacheIndex: number = -1; + /** Whether the script is listening to input. */ + private _listenInput: boolean = false; + /** * Called when be enabled first time, only once. */ @@ -165,10 +165,8 @@ export class Script extends Component { this.onPointerEnter !== prototype.onPointerEnter || this.onPointerExit !== prototype.onPointerExit ) { - if (!this._listenInput) { - engine._inputManager.on(); - this._listenInput = true; - } + engine._inputManager.on(); + this._listenInput = true; } this._entity._addScript(this); diff --git a/packages/core/src/input/InputManager.ts b/packages/core/src/input/InputManager.ts index 4782ab2638..7ac77d3b33 100644 --- a/packages/core/src/input/InputManager.ts +++ b/packages/core/src/input/InputManager.ts @@ -8,13 +8,15 @@ import { Script } from "../Script"; import { Input } from "./Input"; import { Pointer } from "./Pointer"; -export enum PointerEventType { +/** @internal */ +enum PointerEventType { PointDown = 0, PointUp = 1, PointMove = 2 } -export enum PointerChangeType { +/** @internal */ +enum PointerChangeType { Update = 0, Remove = 1 } @@ -52,8 +54,6 @@ export class InputManager { private _multiTouchEnabled: boolean = false; /** Number of scripts registered to listen. */ private _regScriptCount: number = 0; - /** Is it currently listening. */ - private _hadListener: boolean = false; /** * Whether to support multi-touch. @@ -114,6 +114,25 @@ export class InputManager { } } + /** + * Called when the engine is destroyed. + */ + destroy() { + const { _canvas: canvas } = this; + canvas.removeEventListener("pointerdown", this._onPointerDown); + canvas.removeEventListener("pointerup", this._onPointerUp); + canvas.removeEventListener("pointermove", this._onPointerMove); + canvas.removeEventListener("pointercancel", this._onPointerCancelOrOut); + canvas.removeEventListener("pointerout", this._onPointerCancelOrOut); + this._eventList.length = 0; + this._pointerList.length = 0; + this._input = null; + this._pointerIdToIndex = null; + this._sceneMgr = null; + this._physicsMgr = null; + this._canvas = null; + } + /** * Update pointer event, will be executed every frame. * @internal @@ -336,15 +355,14 @@ export class InputManager { * Update the current listening status. */ private _updateListener() { - const { _regScriptCount, _hadListener, _canvas: canvas } = this; - if (_regScriptCount > 0 && !_hadListener) { + const { _regScriptCount, _canvas: canvas } = this; + if (_regScriptCount > 0) { canvas.addEventListener("pointerdown", this._onPointerDown); canvas.addEventListener("pointerup", this._onPointerUp); canvas.addEventListener("pointermove", this._onPointerMove); canvas.addEventListener("pointercancel", this._onPointerCancelOrOut); canvas.addEventListener("pointerout", this._onPointerCancelOrOut); - this._hadListener = true; - } else if (_regScriptCount <= 0 && _hadListener) { + } else { canvas.removeEventListener("pointerdown", this._onPointerDown); canvas.removeEventListener("pointerup", this._onPointerUp); canvas.removeEventListener("pointermove", this._onPointerMove); @@ -352,7 +370,6 @@ export class InputManager { canvas.removeEventListener("pointerout", this._onPointerCancelOrOut); this._eventLen = 0; this._actPointerCount = 0; - this._hadListener = false; } } } From 82b2f24c90eeb41f0a2603b1f4232d32d208a127 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Tue, 14 Sep 2021 15:09:47 +0800 Subject: [PATCH 45/72] feat: Opt code. --- packages/core/src/Script.ts | 20 ------- packages/core/src/input/InputManager.ts | 71 ++++++++----------------- 2 files changed, 22 insertions(+), 69 deletions(-) diff --git a/packages/core/src/Script.ts b/packages/core/src/Script.ts index 9c371436d3..168489fec3 100644 --- a/packages/core/src/Script.ts +++ b/packages/core/src/Script.ts @@ -27,9 +27,6 @@ export class Script extends Component { @ignoreClone _entityCacheIndex: number = -1; - /** Whether the script is listening to input. */ - private _listenInput: boolean = false; - /** * Called when be enabled first time, only once. */ @@ -156,19 +153,6 @@ export class Script extends Component { if (this.onLateUpdate !== prototype.onLateUpdate) { componentsManager.addOnLateUpdateScript(this); } - // Input callback. - if ( - this.onPointerDown !== prototype.onPointerDown || - this.onPointerUp !== prototype.onPointerUp || - this.onPointerClick !== prototype.onPointerClick || - this.onPointerDrag !== prototype.onPointerDrag || - this.onPointerEnter !== prototype.onPointerEnter || - this.onPointerExit !== prototype.onPointerExit - ) { - engine._inputManager.on(); - this._listenInput = true; - } - this._entity._addScript(this); this.onEnable(); } @@ -192,10 +176,6 @@ export class Script extends Component { if (this._onLateUpdateIndex !== -1) { componentsManager.removeOnLateUpdateScript(this); } - if (this._listenInput) { - engine._inputManager.off(); - this._listenInput = false; - } if (this._entityCacheIndex !== -1) { this._entity._removeScript(this); } diff --git a/packages/core/src/input/InputManager.ts b/packages/core/src/input/InputManager.ts index 7ac77d3b33..467a27943e 100644 --- a/packages/core/src/input/InputManager.ts +++ b/packages/core/src/input/InputManager.ts @@ -44,6 +44,8 @@ export class InputManager { private _pointerList: Pointer[] = []; /** 'PointerId' to 'index' mapping. */ private _pointerIdToIndex: Record = {}; + /** Output Pointer list. */ + private _outputPointerList: Pointer[] = []; /** Temporary variables. */ private static _tempRay: Ray = new Ray(); @@ -52,8 +54,6 @@ export class InputManager { /** Whether to support multi-touch. */ private _multiTouchEnabled: boolean = false; - /** Number of scripts registered to listen. */ - private _regScriptCount: number = 0; /** * Whether to support multi-touch. @@ -71,10 +71,16 @@ export class InputManager { * @param engine - The current engine instance */ constructor(engine: Engine) { - // @ts-ignore - this._canvas = engine.canvas._webCanvas; this._sceneMgr = engine.sceneManager; this._physicsMgr = engine.physicsManager; + + // @ts-ignore + const canvas = (this._canvas = engine.canvas._webCanvas); + canvas.addEventListener("pointerdown", this._onPointerDown); + canvas.addEventListener("pointerup", this._onPointerUp); + canvas.addEventListener("pointermove", this._onPointerMove); + canvas.addEventListener("pointercancel", this._onPointerCancelOrOut); + canvas.addEventListener("pointerout", this._onPointerCancelOrOut); } /** @@ -83,8 +89,8 @@ export class InputManager { * @return Touch pointer * @remarks The returned Pointer should be considered deep-read-only. */ - getTouch(idx: number): Readonly { - return idx < this._actPointerCount ? this._pointerList[idx] : null; + getPointers(): Readonly { + return this._outputPointerList; } /** @@ -96,24 +102,6 @@ export class InputManager { return this._input; } - /** - * Add the script that listen to input. - */ - on() { - if (this._regScriptCount++ == 0) { - this._updateListener(); - } - } - - /** - * Remove the script that listen to input. - */ - off() { - if (--this._regScriptCount == 0) { - this._updateListener(); - } - } - /** * Called when the engine is destroyed. */ @@ -138,9 +126,14 @@ export class InputManager { * @internal */ _update() { - const { _eventLen } = this; - if (_eventLen > 0 || this._actPointerCount > 0) { - const { _eventList, _input } = this; + const { _eventLen, _actPointerCount } = this; + if (_eventLen > 0 || _actPointerCount > 0) { + /** Sync _outPointerList and _pointerList. */ + const { _outputPointerList: _outPointerList, _pointerList, _eventList, _input } = this; + _outPointerList.length = _actPointerCount; + for (let i = 0; i < _actPointerCount; i++) { + _outPointerList[i] = _pointerList[i]; + } let prePressedEntity = _input.pressedEntity; if (prePressedEntity) { /** Pointer Drag. */ @@ -213,6 +206,8 @@ export class InputManager { } _input.pressedEntity = prePressedEntity; this._eventLen = 0; + } else { + this._outputPointerList.length = 0; } } @@ -350,26 +345,4 @@ export class InputManager { private _onPointerCancelOrOut = (evt: PointerEvent) => { this._changePointer(PointerChangeType.Remove, evt.pointerId); }; - - /** - * Update the current listening status. - */ - private _updateListener() { - const { _regScriptCount, _canvas: canvas } = this; - if (_regScriptCount > 0) { - canvas.addEventListener("pointerdown", this._onPointerDown); - canvas.addEventListener("pointerup", this._onPointerUp); - canvas.addEventListener("pointermove", this._onPointerMove); - canvas.addEventListener("pointercancel", this._onPointerCancelOrOut); - canvas.addEventListener("pointerout", this._onPointerCancelOrOut); - } else { - canvas.removeEventListener("pointerdown", this._onPointerDown); - canvas.removeEventListener("pointerup", this._onPointerUp); - canvas.removeEventListener("pointermove", this._onPointerMove); - canvas.removeEventListener("pointercancel", this._onPointerCancelOrOut); - canvas.removeEventListener("pointerout", this._onPointerCancelOrOut); - this._eventLen = 0; - this._actPointerCount = 0; - } - } } From a4868b24b6f86940613319823119fa2fd91db458 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Tue, 14 Sep 2021 15:11:07 +0800 Subject: [PATCH 46/72] feat: Opt code. --- packages/core/src/input/InputManager.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/src/input/InputManager.ts b/packages/core/src/input/InputManager.ts index 467a27943e..3c9e4e1a4d 100644 --- a/packages/core/src/input/InputManager.ts +++ b/packages/core/src/input/InputManager.ts @@ -10,15 +10,15 @@ import { Pointer } from "./Pointer"; /** @internal */ enum PointerEventType { - PointDown = 0, - PointUp = 1, - PointMove = 2 + PointDown, + PointUp, + PointMove } /** @internal */ enum PointerChangeType { - Update = 0, - Remove = 1 + Update, + Remove } /** From 0545e14799e028203abe222856770d18dabec1e1 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Tue, 14 Sep 2021 15:12:55 +0800 Subject: [PATCH 47/72] feat: Opt code. --- packages/core/src/input/InputManager.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/core/src/input/InputManager.ts b/packages/core/src/input/InputManager.ts index 3c9e4e1a4d..ec22ee4f50 100644 --- a/packages/core/src/input/InputManager.ts +++ b/packages/core/src/input/InputManager.ts @@ -26,7 +26,7 @@ enum PointerChangeType { */ export class InputManager { /** Canvas to listen for input. */ - private _canvas: HTMLCanvasElement | OffscreenCanvas; + private _canvas: HTMLCanvasElement; /** SceneManager. */ private _sceneMgr: SceneManager; /** PhysicsManager. */ @@ -143,7 +143,6 @@ export class InputManager { } } /** Get the entity hit by the ray. */ - /** @ts-ignore */ const { offsetLeft = 0, offsetTop = 0, clientWidth, clientHeight } = this._canvas; const curEntity = this._pointerRaycast( (_input.pageX - offsetLeft) / clientWidth, From 39691a93c7b3f913653cef6424215ae8304b5e48 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Tue, 14 Sep 2021 15:14:35 +0800 Subject: [PATCH 48/72] feat: Opt code. --- packages/core/src/input/InputManager.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/core/src/input/InputManager.ts b/packages/core/src/input/InputManager.ts index ec22ee4f50..4d0ffe64d9 100644 --- a/packages/core/src/input/InputManager.ts +++ b/packages/core/src/input/InputManager.ts @@ -128,11 +128,11 @@ export class InputManager { _update() { const { _eventLen, _actPointerCount } = this; if (_eventLen > 0 || _actPointerCount > 0) { - /** Sync _outPointerList and _pointerList. */ - const { _outputPointerList: _outPointerList, _pointerList, _eventList, _input } = this; - _outPointerList.length = _actPointerCount; + /** Sync _outputPointerList and _pointerList. */ + const { _outputPointerList, _pointerList, _eventList, _input } = this; + _outputPointerList.length = _actPointerCount; for (let i = 0; i < _actPointerCount; i++) { - _outPointerList[i] = _pointerList[i]; + _outputPointerList[i] = _pointerList[i]; } let prePressedEntity = _input.pressedEntity; if (prePressedEntity) { From 4a20e9c6769069e82ebebb4a3827567d7e755392 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Tue, 14 Sep 2021 15:16:32 +0800 Subject: [PATCH 49/72] feat: Opt code. --- packages/core/src/input/InputManager.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/core/src/input/InputManager.ts b/packages/core/src/input/InputManager.ts index 4d0ffe64d9..bfa1d75460 100644 --- a/packages/core/src/input/InputManager.ts +++ b/packages/core/src/input/InputManager.ts @@ -84,10 +84,9 @@ export class InputManager { } /** - * Get touch pointer. - * @param idx - Index of touch pointer - * @return Touch pointer - * @remarks The returned Pointer should be considered deep-read-only. + * Get List of touch pointers. + * @return List of touch pointers + * @remarks The returned list should be considered deep-read-only. */ getPointers(): Readonly { return this._outputPointerList; From 1b2003451cf6d1a8ab105a5bbe5a53148595d5c3 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Wed, 15 Sep 2021 13:40:00 +0800 Subject: [PATCH 50/72] feat: Opt code. --- packages/core/src/input/Input.ts | 6 +- packages/core/src/input/InputManager.ts | 416 +++++++++++------------- packages/core/src/input/Pointer.ts | 21 +- 3 files changed, 210 insertions(+), 233 deletions(-) diff --git a/packages/core/src/input/Input.ts b/packages/core/src/input/Input.ts index 13098baa9c..76b33c5229 100644 --- a/packages/core/src/input/Input.ts +++ b/packages/core/src/input/Input.ts @@ -4,10 +4,8 @@ import { Entity } from "../Entity"; * Input. */ export class Input { - /** Current pageX. */ - pageX: number = 0; - /** Current pageY. */ - pageY: number = 0; + x: number = 0; + y: number = 0; /** Currently pressed entity. */ pressedEntity: Entity = null; /** Currently entered entity. */ diff --git a/packages/core/src/input/InputManager.ts b/packages/core/src/input/InputManager.ts index bfa1d75460..a672d24889 100644 --- a/packages/core/src/input/InputManager.ts +++ b/packages/core/src/input/InputManager.ts @@ -2,61 +2,50 @@ import { Ray, Vector2 } from "@oasis-engine/math"; import { Engine } from "../Engine"; import { Entity } from "../Entity"; import { HitResult } from "../HitResult"; -import { PhysicsManager } from "../PhysicsManager"; -import { SceneManager } from "../SceneManager"; import { Script } from "../Script"; import { Input } from "./Input"; import { Pointer } from "./Pointer"; -/** @internal */ -enum PointerEventType { - PointDown, - PointUp, - PointMove -} - -/** @internal */ -enum PointerChangeType { - Update, - Remove -} - /** * Input Manager. */ export class InputManager { - /** Canvas to listen for input. */ + private static _tempRay: Ray = new Ray(); + private static _tempPoint: Vector2 = new Vector2(); + private static _tempHitResult: HitResult = new HitResult(); + + private _engine: Engine; private _canvas: HTMLCanvasElement; - /** SceneManager. */ - private _sceneMgr: SceneManager; - /** PhysicsManager. */ - private _physicsMgr: PhysicsManager; + private _multiTouchEnabled: boolean = false; /** Current simulated input. */ private _input: Input = new Input(); - /** The number of events received in the current frame monitor. */ - private _eventLen: number = 0; - /** Event list. */ - private _eventList: PointerEventType[] = []; - /** The number of pointers currently active. */ - private _actPointerCount: number = 0; - /** Pointer list. */ + private _eventList: PointerEvent[] = []; + private _activePointerCount: number = 0; private _pointerList: Pointer[] = []; - /** 'PointerId' to 'index' mapping. */ - private _pointerIdToIndex: Record = {}; - /** Output Pointer list. */ - private _outputPointerList: Pointer[] = []; + private _pointers: Pointer[] = []; + private _pointerIdToIndexMap: Record = {}; - /** Temporary variables. */ - private static _tempRay: Ray = new Ray(); - private static _tempPoint: Vector2 = new Vector2(); - private static _tempHitResult: HitResult = new HitResult(); + /** + * Get pointers. + * @remarks The returned list should be considered deep-read-only. + * @return Pointers + */ + get pointers(): Readonly { + return this._pointers; + } - /** Whether to support multi-touch. */ - private _multiTouchEnabled: boolean = false; + /** + * Get input. + * @remarks The returned list should be considered deep-read-only. + * @return Input + */ + get input(): Readonly { + return this._input; + } /** - * Whether to support multi-touch. + * Whether to handle multi-touch. */ get multiTouchEnabled(): boolean { return this._multiTouchEnabled; @@ -71,163 +60,175 @@ export class InputManager { * @param engine - The current engine instance */ constructor(engine: Engine) { - this._sceneMgr = engine.sceneManager; - this._physicsMgr = engine.physicsManager; - + this._engine = engine; // @ts-ignore const canvas = (this._canvas = engine.canvas._webCanvas); - canvas.addEventListener("pointerdown", this._onPointerDown); - canvas.addEventListener("pointerup", this._onPointerUp); - canvas.addEventListener("pointermove", this._onPointerMove); - canvas.addEventListener("pointercancel", this._onPointerCancelOrOut); - canvas.addEventListener("pointerout", this._onPointerCancelOrOut); - } - - /** - * Get List of touch pointers. - * @return List of touch pointers - * @remarks The returned list should be considered deep-read-only. - */ - getPointers(): Readonly { - return this._outputPointerList; - } - - /** - * Get input. - * @return Input - * @remarks The returned Input should be considered deep-read-only. - */ - getInput(): Readonly { - return this._input; + canvas.addEventListener("pointerdown", this._pushEventList); + canvas.addEventListener("pointerup", this._pushEventList); + canvas.addEventListener("pointermove", this._pushEventList); + canvas.addEventListener("pointercancel", this._pushEventList); + canvas.addEventListener("pointerout", this._pushEventList); } /** * Called when the engine is destroyed. */ - destroy() { + destroy(): void { const { _canvas: canvas } = this; - canvas.removeEventListener("pointerdown", this._onPointerDown); - canvas.removeEventListener("pointerup", this._onPointerUp); - canvas.removeEventListener("pointermove", this._onPointerMove); - canvas.removeEventListener("pointercancel", this._onPointerCancelOrOut); - canvas.removeEventListener("pointerout", this._onPointerCancelOrOut); + canvas.removeEventListener("pointerdown", this._pushEventList); + canvas.removeEventListener("pointerup", this._pushEventList); + canvas.removeEventListener("pointermove", this._pushEventList); + canvas.removeEventListener("pointercancel", this._pushEventList); + canvas.removeEventListener("pointerout", this._pushEventList); this._eventList.length = 0; this._pointerList.length = 0; + this._pointers.length = 0; this._input = null; - this._pointerIdToIndex = null; - this._sceneMgr = null; - this._physicsMgr = null; - this._canvas = null; + this._pointerIdToIndexMap = null; } /** * Update pointer event, will be executed every frame. * @internal */ - _update() { - const { _eventLen, _actPointerCount } = this; - if (_eventLen > 0 || _actPointerCount > 0) { - /** Sync _outputPointerList and _pointerList. */ - const { _outputPointerList, _pointerList, _eventList, _input } = this; - _outputPointerList.length = _actPointerCount; - for (let i = 0; i < _actPointerCount; i++) { - _outputPointerList[i] = _pointerList[i]; - } - let prePressedEntity = _input.pressedEntity; - if (prePressedEntity) { - /** Pointer Drag. */ - const preScripts = prePressedEntity._scripts._elements; - for (let i = preScripts.length - 1; i >= 0; i--) { - preScripts[i].onPointerDrag(); + _update(): void { + /** Simulate a combined pointer. */ + const { _eventList } = this; + const eventLen = _eventList.length; + /** Expressed in binary. */ + let evtTypeList = 1; + if (eventLen > 0) { + for (let l = 0; l < eventLen; l++) { + const evt = _eventList[l]; + switch (evt.type) { + case "pointerdown": + this._changePointer(PointerOperationType.Update, evt.pointerId, evt.pageX, evt.pageY); + /** this._activePointerCount === 1 && (evtTypeList = (evtTypeList << 1) | PointerEventType.Down); */ + this._activePointerCount === 1 && (evtTypeList <<= 1); + break; + case "pointerup": + this._activePointerCount === 1 && (evtTypeList = (evtTypeList << 1) | PointerEventType.Up); + break; + case "pointermove": + this._changePointer(PointerOperationType.Update, evt.pointerId, evt.pageX, evt.pageY); + break; + case "pointercancel": + case "pointerout": + this._changePointer(PointerOperationType.Remove, evt.pointerId); + break; + default: + break; } } - /** Get the entity hit by the ray. */ - const { offsetLeft = 0, offsetTop = 0, clientWidth, clientHeight } = this._canvas; - const curEntity = this._pointerRaycast( - (_input.pageX - offsetLeft) / clientWidth, - (_input.pageY - offsetTop) / clientHeight - ); - const preEnteredEntity = _input.enteredEntity; - /** 80% of operations are on curEntity, so we cache his scripts. */ - let curEntityScripts: Script[]; - let curEntityScriptsLen = 0; - if (curEntity) { - curEntityScripts = curEntity._scripts._elements; - curEntityScriptsLen = curEntityScripts.length; + /** Reset event list. */ + _eventList.length = 0; + } + + /** Sync _pointers and _pointerList. */ + const { _pointers: pointers, _pointerList: pointerList, _input: input, _activePointerCount } = this; + pointers.length = _activePointerCount; + if (evtTypeList <= 1 && _activePointerCount === 0) { + return; + } else { + for (let i = 0; i < _activePointerCount; i++) { + pointers[i] = pointerList[i]; } - // Cache curEntity's Scripts. - if (preEnteredEntity != curEntity) { - if (curEntity) { - for (let i = 0; i < curEntityScriptsLen; i++) { - curEntityScripts[i].onPointerEnter(); - } + } + + /** Check whether pressed events are triggered. */ + let prePressedEntity = input.pressedEntity; + if (prePressedEntity) { + const preScripts = prePressedEntity._scripts._elements; + for (let i = preScripts.length - 1; i >= 0; i--) { + preScripts[i].onPointerDrag(); + } + } + /** Get the entity hit by the ray. */ + const curEntity = this._pointerRaycast(input.x, input.y); + const preEnteredEntity = input.enteredEntity; + /** 80% of operations are on curEntity, so we cache his scripts. */ + let curEntityScripts: Script[]; + let curEntityScriptsLen = 0; + if (curEntity) { + curEntityScripts = curEntity._scripts._elements; + curEntityScriptsLen = curEntityScripts.length; + } + /** Check whether enter and exit events are triggered. */ + if (preEnteredEntity != curEntity) { + if (curEntity) { + for (let i = 0; i < curEntityScriptsLen; i++) { + curEntityScripts[i].onPointerEnter(); } - if (preEnteredEntity) { - const scripts = preEnteredEntity._scripts; - const scriptsArr = scripts._elements; - for (let i = scripts.length - 1; i >= 0; i--) { - scriptsArr[i].onPointerExit(); - } + } + if (preEnteredEntity) { + const scripts = preEnteredEntity._scripts; + const scriptsArr = scripts._elements; + for (let i = scripts.length - 1; i >= 0; i--) { + scriptsArr[i].onPointerExit(); } - _input.enteredEntity = curEntity; } - for (let i = 0; i < _eventLen; i++) { - switch (_eventList[i]) { - case PointerEventType.PointDown: - if (curEntity) { + input.enteredEntity = curEntity; + } + /** Check whether down up and click events are triggered. */ + while (evtTypeList > 1) { + switch (evtTypeList % 2) { + case PointerEventType.Down: + if (curEntity) { + for (let j = 0; j < curEntityScriptsLen; j++) { + curEntityScripts[j].onPointerDown(); + } + } + prePressedEntity = curEntity; + break; + case PointerEventType.Up: + if (prePressedEntity) { + if (prePressedEntity == curEntity) { for (let j = 0; j < curEntityScriptsLen; j++) { - curEntityScripts[j].onPointerDown(); + const script = curEntityScripts[j]; + script.onPointerUp(); + script.onPointerClick(); } - } - prePressedEntity = curEntity; - break; - case PointerEventType.PointUp: - if (prePressedEntity) { - if (prePressedEntity == curEntity) { - for (let j = 0; j < curEntityScriptsLen; j++) { - const script = curEntityScripts[j]; - script.onPointerUp(); - script.onPointerClick(); - } - } else { - const preScripts = prePressedEntity._scripts._elements; - for (let j = preScripts.length - 1; j >= 0; j--) { - preScripts[j].onPointerUp(); - } + } else { + const preScripts = prePressedEntity._scripts._elements; + for (let j = preScripts.length - 1; j >= 0; j--) { + preScripts[j].onPointerUp(); } } - prePressedEntity = null; - break; - default: - break; - } + } + prePressedEntity = null; + break; } - _input.pressedEntity = prePressedEntity; - this._eventLen = 0; - } else { - this._outputPointerList.length = 0; + evtTypeList >>= 1; } + input.pressedEntity = prePressedEntity; } /** * Get the Entity to which the ray is cast. - * @param posX - The X coordinate of the pointer on the screen, specified in normalized - * @param posY - The Y coordinate of the pointer on the screen, specified in normalized + * @param x - The X coordinate of the pointer on the screen, specified in normalized + * @param y - The Y coordinate of the pointer on the screen, specified in normalized * @returns The Entity to which the ray is cast */ - private _pointerRaycast(posX: number, posY: number): Entity { - const cameras = this._sceneMgr.activeScene._activeCameras; + private _pointerRaycast(x: number, y: number): Entity { + const { _engine: engine, _canvas: canvas } = this; + /** Convert screen coordinates to viewport coordinates. */ + x = (x - canvas.offsetLeft) / canvas.clientWidth; + y = (y - canvas.offsetTop) / canvas.clientHeight; + const cameras = engine.sceneManager.activeScene._activeCameras; for (let i = cameras.length - 1; i >= 0; i--) { const camera = cameras[i]; if (!camera.enabled || camera.renderTarget) { continue; } const { x: vpX, y: vpY, z: vpW, w: vpH } = camera.viewport; - if (posX >= vpX && posY >= vpY && posX - vpX <= vpW && posY - vpY <= vpH) { + if (x >= vpX && y >= vpY && x - vpX <= vpW && y - vpY <= vpH) { const { _tempHitResult, _tempPoint } = InputManager; - _tempPoint.setValue((posX - vpX) / vpW, (posY - vpY) / vpH); + _tempPoint.setValue((x - vpX) / vpW, (y - vpY) / vpH); // TODO: Only check which colliders have listened to the input. - return this._physicsMgr.raycast(camera.viewportPointToRay(_tempPoint, InputManager._tempRay), _tempHitResult) + return engine.physicsManager.raycast( + camera.viewportPointToRay(_tempPoint, InputManager._tempRay), + _tempHitResult + ) ? _tempHitResult.collider.entity : null; } @@ -239,54 +240,57 @@ export class InputManager { * Update pointers info. * @param type - Pointer change type * @param pointerId - Pointer ID - * @param pageX - The pageX of Pointer - * @param pageY - The pageY of Pointer + * @param x - The x coordinate of Pointer + * @param y - The y coordinate of Pointer */ - private _changePointer(type: PointerChangeType, pointerId: number, pageX?: number, pageY?: number) { - const { _pointerIdToIndex, _pointerList, _actPointerCount: lastCount, _input } = this; + private _changePointer(type: PointerOperationType, pointerId: number, x?: number, y?: number): void { + const { + _pointerIdToIndexMap: _pointerIdToIndex, + _pointerList: pointerList, + _activePointerCount: lastCount, + _input: input + } = this; let idx = _pointerIdToIndex[pointerId]; switch (type) { - case PointerChangeType.Remove: + case PointerOperationType.Remove: if (idx === undefined) { return; } else { - const nowCount = (this._actPointerCount = lastCount - 1); + const nowCount = (this._activePointerCount = lastCount - 1); if (nowCount != 0) { - const removedTouch = _pointerList[idx]; + const removedTouch = pointerList[idx]; if (idx !== lastCount) { - _pointerList[idx] = _pointerList[lastCount]; - _pointerList[lastCount] = removedTouch; + pointerList[idx] = pointerList[lastCount]; + pointerList[lastCount] = removedTouch; } - _input.pageX = (_input.pageX * lastCount - removedTouch.pageX) / nowCount; - _input.pageY = (_input.pageY * lastCount - removedTouch.pageY) / nowCount; + input.x = (input.x * lastCount - removedTouch.position.x) / nowCount; + input.y = (input.y * lastCount - removedTouch.position.y) / nowCount; } delete _pointerIdToIndex[pointerId]; } break; - case PointerChangeType.Update: + case PointerOperationType.Update: if (idx === undefined) { if (lastCount > 0 && !this._multiTouchEnabled) { return; } let touch: Pointer; - if (!_pointerList[lastCount]) { - touch = _pointerList[lastCount] = new Pointer(pointerId, pageX, pageY, lastCount); + if (!pointerList[lastCount]) { + touch = pointerList[lastCount] = new Pointer(pointerId, x, y, lastCount); } else { - touch = _pointerList[lastCount]; - touch.pageX = pageX; - touch.pageY = pageY; - touch.indexInList = lastCount; + touch = pointerList[lastCount]; + touch.position.setValue(x, y); + touch._indexInList = lastCount; } _pointerIdToIndex[pointerId] = lastCount; - const nowCount = (this._actPointerCount = lastCount + 1); - _input.pageX = (_input.pageX * lastCount + pageX) / nowCount; - _input.pageY = (_input.pageY * lastCount + pageY) / nowCount; + const nowCount = (this._activePointerCount = lastCount + 1); + input.x = (input.x * lastCount + x) / nowCount; + input.y = (input.y * lastCount + y) / nowCount; } else { - const touch = _pointerList[idx]; - _input.pageX += (pageX - touch.pageX) / lastCount; - _input.pageY += (pageY - touch.pageY) / lastCount; - touch.pageX = pageX; - touch.pageY = pageY; + const { position } = pointerList[idx]; + input.x += (x - position.x) / lastCount; + input.y += (y - position.y) / lastCount; + position.setValue(x, y); } break; default: @@ -295,52 +299,22 @@ export class InputManager { } /** - * Push event types into an ordered queue. + * Push pointerEvent into an ordered queue. * @param type - Pointer event type */ - private _pushEventList(type: PointerEventType) { - const { _eventList } = this; - if (_eventList.length == this._eventLen) { - _eventList.push(type); - } else { - _eventList[this._eventLen] = type; - } - ++this._eventLen; - } - - /** - * On pointer down. - * @param evt - Pointer Event - */ - private _onPointerDown = (evt: PointerEvent) => { - this._changePointer(PointerChangeType.Update, evt.pointerId, evt.pageX, evt.pageY); - if (this._actPointerCount == 1) { - this._pushEventList(PointerEventType.PointDown); - } - }; - - /** - * On pointer up. - */ - private _onPointerUp = () => { - if (this._actPointerCount == 1) { - this._pushEventList(PointerEventType.PointUp); - } + private _pushEventList = (event: PointerEvent): void => { + this._eventList.push(event); }; +} - /** - * On pointer move. - * @param evt - Pointer Event - */ - private _onPointerMove = (evt: PointerEvent) => { - this._changePointer(PointerChangeType.Update, evt.pointerId, evt.pageX, evt.pageY); - }; +/** @internal */ +enum PointerEventType { + Down, + Up +} - /** - * On pointer cancel or out. - * @param evt - Pointer Event - */ - private _onPointerCancelOrOut = (evt: PointerEvent) => { - this._changePointer(PointerChangeType.Remove, evt.pointerId); - }; +/** @internal */ +enum PointerOperationType { + Update, + Remove } diff --git a/packages/core/src/input/Pointer.ts b/packages/core/src/input/Pointer.ts index ea405b969b..3ae23076ce 100644 --- a/packages/core/src/input/Pointer.ts +++ b/packages/core/src/input/Pointer.ts @@ -1,18 +1,23 @@ +import { Vector2 } from "@oasis-engine/math"; + /** * Pointer. */ export class Pointer { + /** The position of the pointer in screen space pixel coordinates. */ + position: Vector2 = new Vector2(); + /** @internal */ + _indexInList: number; + /** * Constructor a Pointer. - * @param pointerId - The pageX of the pointer - * @param pageX - The pageY of the pointer - * @param pageY - The pointerId of the pointer + * @param id - The unique identifier for the pointer + * @param x - The x coordinate of the pointer + * @param y - The y coordinate of the pointer * @param indexInList - The index of the pointer in pointerList */ - constructor(public pointerId: number, public pageX: number, public pageY: number, public indexInList: number) { - this.pointerId = pointerId; - this.pageX = pageX; - this.pageY = pageY; - this.indexInList = indexInList; + constructor(public id: number, x: number, y: number, indexInList: number) { + this.position.setValue(x, y); + this._indexInList = indexInList; } } From b16910a43317f0352f11f87a35dc451cd5d5cb7b Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Wed, 15 Sep 2021 13:42:10 +0800 Subject: [PATCH 51/72] feat: Opt code. --- packages/core/src/Script.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/core/src/Script.ts b/packages/core/src/Script.ts index 168489fec3..aa6201a3a1 100644 --- a/packages/core/src/Script.ts +++ b/packages/core/src/Script.ts @@ -141,8 +141,7 @@ export class Script extends Component { * @override */ _onEnable(): void { - const { engine } = this; - const { _componentsManager: componentsManager } = engine; + const componentsManager = this.engine._componentsManager; const prototype = Script.prototype; if (!this._started) { componentsManager.addOnStartScript(this); @@ -163,8 +162,7 @@ export class Script extends Component { * @override */ _onDisable(): void { - const { engine } = this; - const { _componentsManager: componentsManager } = engine; + const componentsManager = this.engine._componentsManager; // Use "xxIndex" is more safe. // When call onDisable it maybe it still not in script queue,for example write "entity.isActive = false" in onWake(). if (this._onStartIndex !== -1) { From 805d4d06b12a8a96461f210170575dfec3de1862 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Thu, 23 Sep 2021 01:06:17 +0800 Subject: [PATCH 52/72] feat: Opt code. --- packages/core/src/Engine.ts | 2 +- packages/core/src/input/Input.ts | 13 - packages/core/src/input/InputManager.ts | 298 +------------ packages/core/src/input/Pointer.ts | 23 - packages/core/src/input/pointer/Pointer.ts | 30 ++ .../core/src/input/pointer/PointerManager.ts | 405 ++++++++++++++++++ packages/rhi-webgl/src/WebCanvas.ts | 12 +- 7 files changed, 460 insertions(+), 323 deletions(-) delete mode 100644 packages/core/src/input/Input.ts delete mode 100644 packages/core/src/input/Pointer.ts create mode 100644 packages/core/src/input/pointer/Pointer.ts create mode 100644 packages/core/src/input/pointer/PointerManager.ts diff --git a/packages/core/src/Engine.ts b/packages/core/src/Engine.ts index 6e2188c6f7..d4837cf6e4 100644 --- a/packages/core/src/Engine.ts +++ b/packages/core/src/Engine.ts @@ -280,7 +280,7 @@ export class Engine extends EventDispatcher { if (this._sceneManager) { this._whiteTexture2D.destroy(true); this._whiteTextureCube.destroy(true); - this._inputManager.destroy(); + this._inputManager._destroy(); this.trigger(new Event("shutdown", this)); engineFeatureManager.callFeatureMethod(this, "shutdown", [this]); diff --git a/packages/core/src/input/Input.ts b/packages/core/src/input/Input.ts deleted file mode 100644 index 76b33c5229..0000000000 --- a/packages/core/src/input/Input.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Entity } from "../Entity"; - -/** - * Input. - */ -export class Input { - x: number = 0; - y: number = 0; - /** Currently pressed entity. */ - pressedEntity: Entity = null; - /** Currently entered entity. */ - enteredEntity: Entity = null; -} diff --git a/packages/core/src/input/InputManager.ts b/packages/core/src/input/InputManager.ts index a672d24889..1d42d4fd06 100644 --- a/packages/core/src/input/InputManager.ts +++ b/packages/core/src/input/InputManager.ts @@ -1,58 +1,30 @@ -import { Ray, Vector2 } from "@oasis-engine/math"; import { Engine } from "../Engine"; -import { Entity } from "../Entity"; -import { HitResult } from "../HitResult"; -import { Script } from "../Script"; -import { Input } from "./Input"; -import { Pointer } from "./Pointer"; +import { Pointer } from "./pointer/Pointer"; +import { PointerManager } from "./pointer/PointerManager"; /** - * Input Manager. + * InputManager manages device input such as mouse, touch, keyboard, etc. */ export class InputManager { - private static _tempRay: Ray = new Ray(); - private static _tempPoint: Vector2 = new Vector2(); - private static _tempHitResult: HitResult = new HitResult(); - - private _engine: Engine; - private _canvas: HTMLCanvasElement; - - private _multiTouchEnabled: boolean = false; - /** Current simulated input. */ - private _input: Input = new Input(); - private _eventList: PointerEvent[] = []; - private _activePointerCount: number = 0; - private _pointerList: Pointer[] = []; - private _pointers: Pointer[] = []; - private _pointerIdToIndexMap: Record = {}; + private _pointerManager: PointerManager; /** * Get pointers. * @remarks The returned list should be considered deep-read-only. - * @return Pointers */ get pointers(): Readonly { - return this._pointers; - } - - /** - * Get input. - * @remarks The returned list should be considered deep-read-only. - * @return Input - */ - get input(): Readonly { - return this._input; + return this._pointerManager._pointers; } /** - * Whether to handle multi-touch. + * Whether to handle multi-pointer. */ - get multiTouchEnabled(): boolean { - return this._multiTouchEnabled; + get multiPointerEnabled(): boolean { + return this._pointerManager._multiPointerEnabled; } - set multiTouchEnabled(enabled: boolean) { - this._multiTouchEnabled = enabled; + set multiPointerEnabled(enabled: boolean) { + this._pointerManager._multiPointerEnabled = enabled; } /** @@ -60,31 +32,8 @@ export class InputManager { * @param engine - The current engine instance */ constructor(engine: Engine) { - this._engine = engine; // @ts-ignore - const canvas = (this._canvas = engine.canvas._webCanvas); - canvas.addEventListener("pointerdown", this._pushEventList); - canvas.addEventListener("pointerup", this._pushEventList); - canvas.addEventListener("pointermove", this._pushEventList); - canvas.addEventListener("pointercancel", this._pushEventList); - canvas.addEventListener("pointerout", this._pushEventList); - } - - /** - * Called when the engine is destroyed. - */ - destroy(): void { - const { _canvas: canvas } = this; - canvas.removeEventListener("pointerdown", this._pushEventList); - canvas.removeEventListener("pointerup", this._pushEventList); - canvas.removeEventListener("pointermove", this._pushEventList); - canvas.removeEventListener("pointercancel", this._pushEventList); - canvas.removeEventListener("pointerout", this._pushEventList); - this._eventList.length = 0; - this._pointerList.length = 0; - this._pointers.length = 0; - this._input = null; - this._pointerIdToIndexMap = null; + this._pointerManager = new PointerManager(engine, engine.canvas._webCanvas); } /** @@ -92,229 +41,14 @@ export class InputManager { * @internal */ _update(): void { - /** Simulate a combined pointer. */ - const { _eventList } = this; - const eventLen = _eventList.length; - /** Expressed in binary. */ - let evtTypeList = 1; - if (eventLen > 0) { - for (let l = 0; l < eventLen; l++) { - const evt = _eventList[l]; - switch (evt.type) { - case "pointerdown": - this._changePointer(PointerOperationType.Update, evt.pointerId, evt.pageX, evt.pageY); - /** this._activePointerCount === 1 && (evtTypeList = (evtTypeList << 1) | PointerEventType.Down); */ - this._activePointerCount === 1 && (evtTypeList <<= 1); - break; - case "pointerup": - this._activePointerCount === 1 && (evtTypeList = (evtTypeList << 1) | PointerEventType.Up); - break; - case "pointermove": - this._changePointer(PointerOperationType.Update, evt.pointerId, evt.pageX, evt.pageY); - break; - case "pointercancel": - case "pointerout": - this._changePointer(PointerOperationType.Remove, evt.pointerId); - break; - default: - break; - } - } - /** Reset event list. */ - _eventList.length = 0; - } - - /** Sync _pointers and _pointerList. */ - const { _pointers: pointers, _pointerList: pointerList, _input: input, _activePointerCount } = this; - pointers.length = _activePointerCount; - if (evtTypeList <= 1 && _activePointerCount === 0) { - return; - } else { - for (let i = 0; i < _activePointerCount; i++) { - pointers[i] = pointerList[i]; - } - } - - /** Check whether pressed events are triggered. */ - let prePressedEntity = input.pressedEntity; - if (prePressedEntity) { - const preScripts = prePressedEntity._scripts._elements; - for (let i = preScripts.length - 1; i >= 0; i--) { - preScripts[i].onPointerDrag(); - } - } - /** Get the entity hit by the ray. */ - const curEntity = this._pointerRaycast(input.x, input.y); - const preEnteredEntity = input.enteredEntity; - /** 80% of operations are on curEntity, so we cache his scripts. */ - let curEntityScripts: Script[]; - let curEntityScriptsLen = 0; - if (curEntity) { - curEntityScripts = curEntity._scripts._elements; - curEntityScriptsLen = curEntityScripts.length; - } - /** Check whether enter and exit events are triggered. */ - if (preEnteredEntity != curEntity) { - if (curEntity) { - for (let i = 0; i < curEntityScriptsLen; i++) { - curEntityScripts[i].onPointerEnter(); - } - } - if (preEnteredEntity) { - const scripts = preEnteredEntity._scripts; - const scriptsArr = scripts._elements; - for (let i = scripts.length - 1; i >= 0; i--) { - scriptsArr[i].onPointerExit(); - } - } - input.enteredEntity = curEntity; - } - /** Check whether down up and click events are triggered. */ - while (evtTypeList > 1) { - switch (evtTypeList % 2) { - case PointerEventType.Down: - if (curEntity) { - for (let j = 0; j < curEntityScriptsLen; j++) { - curEntityScripts[j].onPointerDown(); - } - } - prePressedEntity = curEntity; - break; - case PointerEventType.Up: - if (prePressedEntity) { - if (prePressedEntity == curEntity) { - for (let j = 0; j < curEntityScriptsLen; j++) { - const script = curEntityScripts[j]; - script.onPointerUp(); - script.onPointerClick(); - } - } else { - const preScripts = prePressedEntity._scripts._elements; - for (let j = preScripts.length - 1; j >= 0; j--) { - preScripts[j].onPointerUp(); - } - } - } - prePressedEntity = null; - break; - } - evtTypeList >>= 1; - } - input.pressedEntity = prePressedEntity; + this._pointerManager._update(); } /** - * Get the Entity to which the ray is cast. - * @param x - The X coordinate of the pointer on the screen, specified in normalized - * @param y - The Y coordinate of the pointer on the screen, specified in normalized - * @returns The Entity to which the ray is cast - */ - private _pointerRaycast(x: number, y: number): Entity { - const { _engine: engine, _canvas: canvas } = this; - /** Convert screen coordinates to viewport coordinates. */ - x = (x - canvas.offsetLeft) / canvas.clientWidth; - y = (y - canvas.offsetTop) / canvas.clientHeight; - const cameras = engine.sceneManager.activeScene._activeCameras; - for (let i = cameras.length - 1; i >= 0; i--) { - const camera = cameras[i]; - if (!camera.enabled || camera.renderTarget) { - continue; - } - const { x: vpX, y: vpY, z: vpW, w: vpH } = camera.viewport; - if (x >= vpX && y >= vpY && x - vpX <= vpW && y - vpY <= vpH) { - const { _tempHitResult, _tempPoint } = InputManager; - _tempPoint.setValue((x - vpX) / vpW, (y - vpY) / vpH); - // TODO: Only check which colliders have listened to the input. - return engine.physicsManager.raycast( - camera.viewportPointToRay(_tempPoint, InputManager._tempRay), - _tempHitResult - ) - ? _tempHitResult.collider.entity - : null; - } - } - return null; - } - - /** - * Update pointers info. - * @param type - Pointer change type - * @param pointerId - Pointer ID - * @param x - The x coordinate of Pointer - * @param y - The y coordinate of Pointer + * Called when the engine is destroyed. + * @internal */ - private _changePointer(type: PointerOperationType, pointerId: number, x?: number, y?: number): void { - const { - _pointerIdToIndexMap: _pointerIdToIndex, - _pointerList: pointerList, - _activePointerCount: lastCount, - _input: input - } = this; - let idx = _pointerIdToIndex[pointerId]; - switch (type) { - case PointerOperationType.Remove: - if (idx === undefined) { - return; - } else { - const nowCount = (this._activePointerCount = lastCount - 1); - if (nowCount != 0) { - const removedTouch = pointerList[idx]; - if (idx !== lastCount) { - pointerList[idx] = pointerList[lastCount]; - pointerList[lastCount] = removedTouch; - } - input.x = (input.x * lastCount - removedTouch.position.x) / nowCount; - input.y = (input.y * lastCount - removedTouch.position.y) / nowCount; - } - delete _pointerIdToIndex[pointerId]; - } - break; - case PointerOperationType.Update: - if (idx === undefined) { - if (lastCount > 0 && !this._multiTouchEnabled) { - return; - } - let touch: Pointer; - if (!pointerList[lastCount]) { - touch = pointerList[lastCount] = new Pointer(pointerId, x, y, lastCount); - } else { - touch = pointerList[lastCount]; - touch.position.setValue(x, y); - touch._indexInList = lastCount; - } - _pointerIdToIndex[pointerId] = lastCount; - const nowCount = (this._activePointerCount = lastCount + 1); - input.x = (input.x * lastCount + x) / nowCount; - input.y = (input.y * lastCount + y) / nowCount; - } else { - const { position } = pointerList[idx]; - input.x += (x - position.x) / lastCount; - input.y += (y - position.y) / lastCount; - position.setValue(x, y); - } - break; - default: - break; - } + _destroy(): void { + this._pointerManager._destroy(); } - - /** - * Push pointerEvent into an ordered queue. - * @param type - Pointer event type - */ - private _pushEventList = (event: PointerEvent): void => { - this._eventList.push(event); - }; -} - -/** @internal */ -enum PointerEventType { - Down, - Up -} - -/** @internal */ -enum PointerOperationType { - Update, - Remove } diff --git a/packages/core/src/input/Pointer.ts b/packages/core/src/input/Pointer.ts deleted file mode 100644 index 3ae23076ce..0000000000 --- a/packages/core/src/input/Pointer.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Vector2 } from "@oasis-engine/math"; - -/** - * Pointer. - */ -export class Pointer { - /** The position of the pointer in screen space pixel coordinates. */ - position: Vector2 = new Vector2(); - /** @internal */ - _indexInList: number; - - /** - * Constructor a Pointer. - * @param id - The unique identifier for the pointer - * @param x - The x coordinate of the pointer - * @param y - The y coordinate of the pointer - * @param indexInList - The index of the pointer in pointerList - */ - constructor(public id: number, x: number, y: number, indexInList: number) { - this.position.setValue(x, y); - this._indexInList = indexInList; - } -} diff --git a/packages/core/src/input/pointer/Pointer.ts b/packages/core/src/input/pointer/Pointer.ts new file mode 100644 index 0000000000..5b1a2b67df --- /dev/null +++ b/packages/core/src/input/pointer/Pointer.ts @@ -0,0 +1,30 @@ +import { Vector2 } from "@oasis-engine/math"; + +/** + * Pointer. + */ +export class Pointer { + pointerId: number; + type: string; + timeStamp: number; + phase: PointerPhase = PointerPhase.Leave; + /** The position of the pointer in screen space pixel coordinates. */ + position: Vector2 = new Vector2(); + + /** + * Constructor a Pointer. + * @param id - The id for the pointer + */ + constructor(public id: number) {} +} + +export enum PointerPhase { + /** A Pointer pressed on the screen. */ + Down, + /** A pointer moved on the screen. */ + Move, + /** A pointer was lifted from the screen. */ + Up, + /** The system cancelled tracking for the pointer. */ + Leave +} diff --git a/packages/core/src/input/pointer/PointerManager.ts b/packages/core/src/input/pointer/PointerManager.ts new file mode 100644 index 0000000000..cce4e139e6 --- /dev/null +++ b/packages/core/src/input/pointer/PointerManager.ts @@ -0,0 +1,405 @@ +import { Ray, Vector2 } from "@oasis-engine/math"; +import { Canvas } from "../../Canvas"; +import { Engine } from "../../Engine"; +import { Entity } from "../../Entity"; +import { HitResult } from "../../HitResult"; +import { Pointer, PointerPhase } from "./Pointer"; + +/** + * Pointer Manager. + */ +export class PointerManager { + private static _tempRay: Ray = new Ray(); + private static _tempPoint: Vector2 = new Vector2(); + private static _tempHitResult: HitResult = new HitResult(); + + /** @internal */ + _pointers: Pointer[] = []; + /** @internal */ + _multiPointerEnabled: boolean = true; + + private _engine: Engine; + private _canvas: Canvas; + + /** When pressing and there is only one pointer, + * correct the pointerId to the minimum threshold, + * so as to prevent _lastMoveHash from becoming too large. */ + private _minPointerThreshold: number = 0; + private _lastMoveHash: number[] = []; + + private _eventList: PointerEvent[] = []; + private _pointerPool: Pointer[]; + private _effectiveEventList: number[] = []; + private _effectiveEventCount = 0; + private _curFrameHasCancel: boolean = false; + private _curFramePosition: Vector2 = new Vector2(); + private _curFramePressedEntity: Entity; + private _curFrameEnteredEntity: Entity; + + /** + * Create a PointerManager. + * @param engine - The current engine instance + */ + constructor(engine: Engine) { + this._engine = engine; + this._canvas = engine.canvas; + // @ts-ignore + const htmlCanvas = this._canvas._webCanvas as HTMLCanvasElement; + htmlCanvas.style.touchAction = "none"; + /** Simultaneous touch contact points are supported by the current device. */ + this._pointerPool = new Array(navigator.maxTouchPoints + 1); + // prettier-ignore + htmlCanvas.onpointerdown = htmlCanvas.onpointerup = htmlCanvas.onpointerout = (evt:PointerEvent)=>{ + this._eventList.push(evt); + }; + htmlCanvas.onpointermove = (evt: PointerEvent) => { + this._lastMoveHash[evt.pointerId - this._minPointerThreshold] = this._eventList.push(evt) - 1; + }; + } + + /** + * Update pointer event, will be executed every frame. + * @internal + */ + _update(): void { + this._curFrameHasCancel && this._adjustPointers(); + /** Check whether Drag events are triggered. */ + this._exeDrag(); + if (this._eventList.length > 0) { + this._handlePointerEvent(this._eventList); + /** Get the entity hit by the ray. */ + const curEnteredEntity = this._pointerRaycast(); + /** Check whether Enter and Exit events are triggered. */ + this._exeEnterAndExit(curEnteredEntity); + /** Check whether down, up and click events are triggered. */ + const { _effectiveEventList, _effectiveEventCount } = this; + for (let i = 0; i < _effectiveEventCount; i++) { + switch (_effectiveEventList[i]) { + case PointerEventType.Down: + this._exeDown(curEnteredEntity); + break; + case PointerEventType.Up: + this._exeUpAndClick(curEnteredEntity); + break; + case PointerEventType.Leave: + this._exeEnterAndExit(null); + this._curFramePressedEntity = null; + break; + } + } + this._effectiveEventCount = 0; + } else { + const { _pointers: pointers } = this; + for (let i = pointers.length - 1; i >= 0; i--) { + const pointer = pointers[i]; + if (pointer.phase !== PointerPhase.Leave) { + this._exeEnterAndExit(this._pointerRaycast()); + } + } + } + } + + /** + * Called when the engine is destroyed. + * @internal + */ + _destroy(): void { + // @ts-ignore + const htmlCanvas = this._canvas._webCanvas as HTMLCanvasElement; + htmlCanvas.onpointerdown = htmlCanvas.onpointerup = htmlCanvas.onpointerout = htmlCanvas.onpointermove = null; + this._eventList.length = 0; + this._pointerPool.length = 0; + this._pointers.length = 0; + this._curFramePosition = null; + this._curFrameEnteredEntity = null; + this._curFramePressedEntity = null; + this._engine = null; + this._canvas = null; + } + + /** + * Remove those pointers whose status is "End". + */ + private _adjustPointers(): void { + const { _pointers: _pointers } = this; + for (let i = _pointers.length - 1; i >= 0; i--) { + if (_pointers[i].phase === PointerPhase.Leave) { + _pointers.splice(i, 1); + } + } + this._curFrameHasCancel = false; + } + + /** + * Get the index of the pointer in the pointers. + * @param pointerId - PointerId of PointerEvent + * @returns Index of pointer in pointers + */ + private _getIndexByPointerID(pointerId: number): number { + const { _pointers: _pointers } = this; + for (let i = _pointers.length - 1; i >= 0; i--) { + if (_pointers[i].pointerId === pointerId) { + return i; + } + } + return -1; + } + + /** + * Add pointer. + * @param pointerId The pointerId of the pointer + * @param pointerType The pointerType of the pointer + * @param x - OffsetX in PointerEvent + * @param y - OffsetY in PointerEvent + * @param phase - The phase of the pointer + * @param timeStamp - Timestamp when changing phase + */ + public _addPointer( + pointerId: number, + pointerType: string, + x: number, + y: number, + phase: PointerPhase, + timeStamp: number + ): void { + const lastCount = this._pointers.length; + if (lastCount <= 0 || this._multiPointerEnabled) { + // @ts-ignore + const pixelRatio = this._canvas.pixelRatio; + x *= pixelRatio; + y *= pixelRatio; + const { _pointers: pointers, _pointerPool: pointerPool, _curFramePosition: curFramePosition } = this; + /** Get Pointer smallest index. */ + let i = 0; + for (; i < lastCount; i++) { + if (pointers[i].id > i) { + break; + } + } + if (!pointerPool[i]) { + pointerPool[i] = new Pointer(i); + } + /** Update and add pointer. */ + const touch = pointerPool[i]; + touch.pointerId = pointerId; + touch.type = pointerType; + touch.position.setValue(x, y); + touch.phase = phase; + touch.timeStamp = timeStamp; + pointers.splice(i, 0, touch); + const nowCount = lastCount + 1; + curFramePosition.x = (curFramePosition.x * lastCount + x) / nowCount; + curFramePosition.y = (curFramePosition.y * lastCount + y) / nowCount; + } + } + + /** + * Remove pointer. + * @param pointerIndex - Index of pointer in pointers + * @param timeStamp - Timestamp when changing phase + */ + private _removePointer(pointerIndex: number, timeStamp: number): void { + const { _pointers: pointers, _curFramePosition: curFramePosition } = this; + const lastCount = pointers.length; + const removedPointer = pointers[pointerIndex]; + if (lastCount > 1) { + const nowCount = lastCount - 1; + const { position } = removedPointer; + curFramePosition.x = (curFramePosition.x * lastCount - position.x) / nowCount; + curFramePosition.y = (curFramePosition.y * lastCount - position.y) / nowCount; + } + removedPointer.phase = PointerPhase.Leave; + removedPointer.timeStamp = timeStamp; + } + + /** + * Update pointer. + * @param pointerIndex - Index of pointer in pointers + * @param x - OffsetX in PointerEvent + * @param y - OffsetY in PointerEvent + * @param phase - The phase of the pointer + * @param timeStamp - Timestamp when changing phase + */ + private _updatePointer(pointerIndex: number, x: number, y: number, phase: PointerPhase, timeStamp: number): void { + const { _pointers: pointers } = this; + const updatedPointer = pointers[pointerIndex]; + const { position } = updatedPointer; + // @ts-ignore + const pixelRatio = this._canvas.pixelRatio; + x *= pixelRatio; + y *= pixelRatio; + this._curFramePosition.x += (x - position.x) / pointers.length; + this._curFramePosition.y += (y - position.y) / pointers.length; + position.setValue(x, y); + updatedPointer.phase = phase; + updatedPointer.timeStamp = timeStamp; + } + + /** + * Update pointer data and filter out valid event information. + * @param eventList - PointerEvent List waiting to be processed + * @returns Effective event information + */ + private _handlePointerEvent(eventList: PointerEvent[]): void { + const { _pointers, _effectiveEventList, _lastMoveHash, _minPointerThreshold } = this; + const timeStamp = this._engine.time.nowTime; + let activePointerCount = _pointers.length; + for (let i = 0, n = eventList.length; i < n; i++) { + const evt = eventList[i]; + let pointerIndex = this._getIndexByPointerID(evt.pointerId); + switch (evt.type) { + case "pointerdown": + if (pointerIndex < 0) { + this._addPointer(evt.pointerId, evt.type, evt.offsetX, evt.offsetY, PointerPhase.Down, timeStamp); + ++activePointerCount; + } else { + this._updatePointer(pointerIndex, evt.offsetX, evt.offsetY, PointerPhase.Down, timeStamp); + } + if (activePointerCount === 1) { + _effectiveEventList[this._effectiveEventCount++] = PointerEventType.Down; + /** Update pointer's threshold. */ + this._minPointerThreshold = evt.pointerId; + } + break; + case "pointerup": + if (pointerIndex >= 0) { + this._updatePointer(pointerIndex, evt.offsetX, evt.offsetY, PointerPhase.Up, timeStamp); + activePointerCount === 1 && (_effectiveEventList[this._effectiveEventCount++] = PointerEventType.Up); + } + break; + case "pointermove": + if (_lastMoveHash[evt.pointerId - _minPointerThreshold] == i) { + if (pointerIndex < 0) { + this._addPointer(evt.pointerId, evt.type, evt.offsetX, evt.offsetY, PointerPhase.Move, timeStamp); + ++activePointerCount; + } else { + this._updatePointer(pointerIndex, evt.offsetX, evt.offsetY, PointerPhase.Move, timeStamp); + } + _effectiveEventList[this._effectiveEventCount++] = PointerEventType.Move; + } + break; + case "pointerout": + if (pointerIndex >= 0) { + this._removePointer(pointerIndex, timeStamp); + --activePointerCount === 0 && (_effectiveEventList[this._effectiveEventCount++] = PointerEventType.Leave); + this._curFrameHasCancel = true; + } + break; + default: + break; + } + } + /** Reset event list. */ + eventList.length = 0; + } + + /** + * Get the Entity to which the ray is cast. + * @param x - The X coordinate of the pointer on the screen, specified in normalized + * @param y - The Y coordinate of the pointer on the screen, specified in normalized + * @returns The Entity to which the ray is cast + */ + private _pointerRaycast(): Entity { + let { x, y } = this._curFramePosition; + /** Convert screen coordinates to viewport coordinates. */ + x /= this._canvas.width; + y /= this._canvas.height; + const cameras = this._engine.sceneManager.activeScene._activeCameras; + for (let i = cameras.length - 1; i >= 0; i--) { + const camera = cameras[i]; + if (!camera.enabled || camera.renderTarget) { + continue; + } + const { x: vpX, y: vpY, z: vpW, w: vpH } = camera.viewport; + if (x >= vpX && y >= vpY && x - vpX <= vpW && y - vpY <= vpH) { + PointerManager._tempPoint.setValue((x - vpX) / vpW, (y - vpY) / vpH); + // TODO: Only check which colliders have listened to the input. + return this._engine.physicsManager.raycast( + camera.viewportPointToRay(PointerManager._tempPoint, PointerManager._tempRay), + PointerManager._tempHitResult + ) + ? PointerManager._tempHitResult.collider.entity + : null; + } + } + return null; + } + + /** + * Execute drag event. + */ + private _exeDrag() { + /** Check whether pressed events are triggered. */ + if (this._curFramePressedEntity) { + const preScripts = this._curFramePressedEntity._scripts._elements; + for (let i = preScripts.length - 1; i >= 0; i--) { + preScripts[i].onPointerDrag(); + } + } + } + + /** + * Execute enter and exit events. + * @param curEnteredEntity - Which entity the pointer is currently on + */ + private _exeEnterAndExit(curEnteredEntity: Entity) { + /** Check whether enter and exit events are triggered. */ + if (this._curFrameEnteredEntity !== curEnteredEntity) { + if (curEnteredEntity) { + const curEntityScripts = curEnteredEntity._scripts._elements; + for (let i = curEntityScripts.length - 1; i >= 0; i--) { + curEntityScripts[i].onPointerEnter(); + } + } + if (this._curFrameEnteredEntity) { + const scripts = this._curFrameEnteredEntity._scripts; + const scriptsArr = scripts._elements; + for (let i = scripts.length - 1; i >= 0; i--) { + scriptsArr[i].onPointerExit(); + } + } + this._curFrameEnteredEntity = curEnteredEntity; + } + } + + /** + * Execute down events. + * @param curEnteredEntity - Which entity the pointer is currently on + */ + private _exeDown(curEnteredEntity: Entity) { + if (curEnteredEntity) { + const scripts = curEnteredEntity._scripts; + const scriptsArr = scripts._elements; + for (let i = scripts.length - 1; i >= 0; i--) { + scriptsArr[i].onPointerDown(); + } + } + this._curFramePressedEntity = curEnteredEntity; + } + + /** + * Execute up and click events. + * @param curEnteredEntity - Which entity the pointer is currently on + */ + private _exeUpAndClick(curEnteredEntity: Entity) { + if (curEnteredEntity) { + const operateOneTarget = this._curFramePressedEntity === curEnteredEntity; + const scripts = curEnteredEntity._scripts; + const scriptsArr = scripts._elements; + for (let i = scripts.length - 1; i >= 0; i--) { + const script = scriptsArr[i]; + operateOneTarget && script.onPointerClick(); + script.onPointerUp(); + } + } + this._curFramePressedEntity = null; + } +} + +/** @internal */ +enum PointerEventType { + Down, + Up, + Move, + Leave +} diff --git a/packages/rhi-webgl/src/WebCanvas.ts b/packages/rhi-webgl/src/WebCanvas.ts index 8ae74ceb6d..e9f0473e09 100644 --- a/packages/rhi-webgl/src/WebCanvas.ts +++ b/packages/rhi-webgl/src/WebCanvas.ts @@ -9,6 +9,7 @@ export class WebCanvas implements Canvas { private _width: number; private _height: number; + private _pixelRatio: number; private _scale: Vector2 = new Vector2(); /** @@ -39,6 +40,10 @@ export class WebCanvas implements Canvas { } } + get pixelRatio(): number { + return this._pixelRatio; + } + /** * The scale of canvas, the value is visible width/height divide the render width/height. * @remarks Need to re-assign after modification to ensure that the modification takes effect. @@ -69,10 +74,9 @@ export class WebCanvas implements Canvas { resizeByClientSize(pixelRatio: number = window.devicePixelRatio): void { const webCanvas = this._webCanvas; if (webCanvas instanceof HTMLCanvasElement) { - const width = webCanvas.clientWidth; - const height = webCanvas.clientHeight; - this.width = width * pixelRatio; - this.height = height * pixelRatio; + this._pixelRatio = pixelRatio; + this.width = webCanvas.clientWidth * pixelRatio; + this.height = webCanvas.clientHeight * pixelRatio; } } From b4d099c2eaad8d0ee20b737a29b8418986c4080d Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Thu, 23 Sep 2021 02:39:39 +0800 Subject: [PATCH 53/72] feat: Opt code. --- .../core/src/input/pointer/PointerManager.ts | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/packages/core/src/input/pointer/PointerManager.ts b/packages/core/src/input/pointer/PointerManager.ts index cce4e139e6..848ea24139 100644 --- a/packages/core/src/input/pointer/PointerManager.ts +++ b/packages/core/src/input/pointer/PointerManager.ts @@ -331,9 +331,9 @@ export class PointerManager { private _exeDrag() { /** Check whether pressed events are triggered. */ if (this._curFramePressedEntity) { - const preScripts = this._curFramePressedEntity._scripts._elements; - for (let i = preScripts.length - 1; i >= 0; i--) { - preScripts[i].onPointerDrag(); + const scripts = this._curFramePressedEntity._scripts; + for (let i = scripts.length - 1; i >= 0; i--) { + scripts.get(i).onPointerDrag(); } } } @@ -346,16 +346,15 @@ export class PointerManager { /** Check whether enter and exit events are triggered. */ if (this._curFrameEnteredEntity !== curEnteredEntity) { if (curEnteredEntity) { - const curEntityScripts = curEnteredEntity._scripts._elements; - for (let i = curEntityScripts.length - 1; i >= 0; i--) { - curEntityScripts[i].onPointerEnter(); + const scripts = curEnteredEntity._scripts; + for (let i = scripts.length - 1; i >= 0; i--) { + scripts.get(i).onPointerEnter(); } } if (this._curFrameEnteredEntity) { const scripts = this._curFrameEnteredEntity._scripts; - const scriptsArr = scripts._elements; for (let i = scripts.length - 1; i >= 0; i--) { - scriptsArr[i].onPointerExit(); + scripts.get(i).onPointerExit(); } } this._curFrameEnteredEntity = curEnteredEntity; @@ -369,9 +368,8 @@ export class PointerManager { private _exeDown(curEnteredEntity: Entity) { if (curEnteredEntity) { const scripts = curEnteredEntity._scripts; - const scriptsArr = scripts._elements; for (let i = scripts.length - 1; i >= 0; i--) { - scriptsArr[i].onPointerDown(); + scripts.get(i).onPointerDown(); } } this._curFramePressedEntity = curEnteredEntity; @@ -385,9 +383,8 @@ export class PointerManager { if (curEnteredEntity) { const operateOneTarget = this._curFramePressedEntity === curEnteredEntity; const scripts = curEnteredEntity._scripts; - const scriptsArr = scripts._elements; for (let i = scripts.length - 1; i >= 0; i--) { - const script = scriptsArr[i]; + const script = scripts.get(i); operateOneTarget && script.onPointerClick(); script.onPointerUp(); } From 2190bb31bf0102f8db7d44a31277f9efe903697e Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Thu, 23 Sep 2021 10:55:12 +0800 Subject: [PATCH 54/72] feat: Opt code. --- packages/core/src/input/pointer/Pointer.ts | 22 +++++++++---------- .../core/src/input/pointer/PointerManager.ts | 8 +++---- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/core/src/input/pointer/Pointer.ts b/packages/core/src/input/pointer/Pointer.ts index 5b1a2b67df..1c8c7fab10 100644 --- a/packages/core/src/input/pointer/Pointer.ts +++ b/packages/core/src/input/pointer/Pointer.ts @@ -1,5 +1,16 @@ import { Vector2 } from "@oasis-engine/math"; +export enum PointerPhase { + /** A Pointer pressed on the screen. */ + Down, + /** A pointer moved on the screen. */ + Move, + /** A pointer was lifted from the screen. */ + Up, + /** The system cancelled tracking for the pointer. */ + Leave +} + /** * Pointer. */ @@ -17,14 +28,3 @@ export class Pointer { */ constructor(public id: number) {} } - -export enum PointerPhase { - /** A Pointer pressed on the screen. */ - Down, - /** A pointer moved on the screen. */ - Move, - /** A pointer was lifted from the screen. */ - Up, - /** The system cancelled tracking for the pointer. */ - Leave -} diff --git a/packages/core/src/input/pointer/PointerManager.ts b/packages/core/src/input/pointer/PointerManager.ts index 848ea24139..71bf47d6a5 100644 --- a/packages/core/src/input/pointer/PointerManager.ts +++ b/packages/core/src/input/pointer/PointerManager.ts @@ -328,7 +328,7 @@ export class PointerManager { /** * Execute drag event. */ - private _exeDrag() { + private _exeDrag(): void { /** Check whether pressed events are triggered. */ if (this._curFramePressedEntity) { const scripts = this._curFramePressedEntity._scripts; @@ -342,7 +342,7 @@ export class PointerManager { * Execute enter and exit events. * @param curEnteredEntity - Which entity the pointer is currently on */ - private _exeEnterAndExit(curEnteredEntity: Entity) { + private _exeEnterAndExit(curEnteredEntity: Entity): void { /** Check whether enter and exit events are triggered. */ if (this._curFrameEnteredEntity !== curEnteredEntity) { if (curEnteredEntity) { @@ -365,7 +365,7 @@ export class PointerManager { * Execute down events. * @param curEnteredEntity - Which entity the pointer is currently on */ - private _exeDown(curEnteredEntity: Entity) { + private _exeDown(curEnteredEntity: Entity): void { if (curEnteredEntity) { const scripts = curEnteredEntity._scripts; for (let i = scripts.length - 1; i >= 0; i--) { @@ -379,7 +379,7 @@ export class PointerManager { * Execute up and click events. * @param curEnteredEntity - Which entity the pointer is currently on */ - private _exeUpAndClick(curEnteredEntity: Entity) { + private _exeUpAndClick(curEnteredEntity: Entity): void { if (curEnteredEntity) { const operateOneTarget = this._curFramePressedEntity === curEnteredEntity; const scripts = curEnteredEntity._scripts; From b06a809fc1d8e45918ac4c6e5a01cbf195439f26 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Thu, 23 Sep 2021 11:55:25 +0800 Subject: [PATCH 55/72] feat: Opt code. --- packages/core/src/input/pointer/Pointer.ts | 7 +++++-- packages/core/src/input/pointer/PointerManager.ts | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/core/src/input/pointer/Pointer.ts b/packages/core/src/input/pointer/Pointer.ts index 1c8c7fab10..493b391e69 100644 --- a/packages/core/src/input/pointer/Pointer.ts +++ b/packages/core/src/input/pointer/Pointer.ts @@ -15,16 +15,19 @@ export enum PointerPhase { * Pointer. */ export class Pointer { + /** PointerId of PointerEvent. */ pointerId: number; - type: string; + pointerType: string; + /** Timestamp of the most recent phase change. */ timeStamp: number; + /** Recent phase. */ phase: PointerPhase = PointerPhase.Leave; /** The position of the pointer in screen space pixel coordinates. */ position: Vector2 = new Vector2(); /** * Constructor a Pointer. - * @param id - The id for the pointer + * @param id - The id for the pointer, start from 0 and automatically fill in. */ constructor(public id: number) {} } diff --git a/packages/core/src/input/pointer/PointerManager.ts b/packages/core/src/input/pointer/PointerManager.ts index 71bf47d6a5..ede8b247a9 100644 --- a/packages/core/src/input/pointer/PointerManager.ts +++ b/packages/core/src/input/pointer/PointerManager.ts @@ -182,7 +182,7 @@ export class PointerManager { /** Update and add pointer. */ const touch = pointerPool[i]; touch.pointerId = pointerId; - touch.type = pointerType; + touch.pointerType = pointerType; touch.position.setValue(x, y); touch.phase = phase; touch.timeStamp = timeStamp; From 18132ed6a78ae09369d39a703fdd99c541968b0a Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Sun, 26 Sep 2021 15:14:08 +0800 Subject: [PATCH 56/72] feat: Opt code. --- packages/core/src/input/pointer/PointerManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/input/pointer/PointerManager.ts b/packages/core/src/input/pointer/PointerManager.ts index ede8b247a9..76e2bc642f 100644 --- a/packages/core/src/input/pointer/PointerManager.ts +++ b/packages/core/src/input/pointer/PointerManager.ts @@ -268,7 +268,7 @@ export class PointerManager { } break; case "pointermove": - if (_lastMoveHash[evt.pointerId - _minPointerThreshold] == i) { + if (_lastMoveHash[evt.pointerId - _minPointerThreshold] === i) { if (pointerIndex < 0) { this._addPointer(evt.pointerId, evt.type, evt.offsetX, evt.offsetY, PointerPhase.Move, timeStamp); ++activePointerCount; From 757d91da94adb340ecf7f232dd3b11bf39a86a6b Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Sun, 26 Sep 2021 18:28:32 +0800 Subject: [PATCH 57/72] feat: Opt code. --- packages/core/src/input/enums/PointerPhase.ts | 13 +++++++++++++ packages/core/src/input/enums/PointerType.ts | 11 +++++++++++ packages/core/src/input/pointer/Pointer.ts | 19 +++++-------------- .../core/src/input/pointer/PointerManager.ts | 11 +++++++---- 4 files changed, 36 insertions(+), 18 deletions(-) create mode 100644 packages/core/src/input/enums/PointerPhase.ts create mode 100644 packages/core/src/input/enums/PointerType.ts diff --git a/packages/core/src/input/enums/PointerPhase.ts b/packages/core/src/input/enums/PointerPhase.ts new file mode 100644 index 0000000000..90228a6d72 --- /dev/null +++ b/packages/core/src/input/enums/PointerPhase.ts @@ -0,0 +1,13 @@ +/** + * The current stage of the pointer. + */ +export enum PointerPhase { + /** A Pointer pressed on the screen. */ + Down, + /** A pointer moved on the screen. */ + Move, + /** A pointer was lifted from the screen. */ + Up, + /** The system cancelled tracking for the pointer. */ + Leave +} diff --git a/packages/core/src/input/enums/PointerType.ts b/packages/core/src/input/enums/PointerType.ts new file mode 100644 index 0000000000..9f7e7d8cfc --- /dev/null +++ b/packages/core/src/input/enums/PointerType.ts @@ -0,0 +1,11 @@ +/** + * The type of the pointer. + */ +export enum PointerType { + /** The event was generated by a mouse device. */ + mouse, + /** The event was generated by a pen or stylus device. */ + pen, + /** The event was generated by a touch, such as a finger. */ + touch +} diff --git a/packages/core/src/input/pointer/Pointer.ts b/packages/core/src/input/pointer/Pointer.ts index 493b391e69..aaf283b4fe 100644 --- a/packages/core/src/input/pointer/Pointer.ts +++ b/packages/core/src/input/pointer/Pointer.ts @@ -1,23 +1,14 @@ import { Vector2 } from "@oasis-engine/math"; - -export enum PointerPhase { - /** A Pointer pressed on the screen. */ - Down, - /** A pointer moved on the screen. */ - Move, - /** A pointer was lifted from the screen. */ - Up, - /** The system cancelled tracking for the pointer. */ - Leave -} +import { PointerPhase } from "../enums/PointerPhase"; +import { PointerType } from "../enums/PointerType"; /** * Pointer. */ export class Pointer { - /** PointerId of PointerEvent. */ - pointerId: number; - pointerType: string; + /** UniqueID of PointerEvent. */ + uniqueID: number; + pointerType: PointerType; /** Timestamp of the most recent phase change. */ timeStamp: number; /** Recent phase. */ diff --git a/packages/core/src/input/pointer/PointerManager.ts b/packages/core/src/input/pointer/PointerManager.ts index b0f659e073..c33c9f32c5 100644 --- a/packages/core/src/input/pointer/PointerManager.ts +++ b/packages/core/src/input/pointer/PointerManager.ts @@ -3,10 +3,13 @@ import { Canvas } from "../../Canvas"; import { Engine } from "../../Engine"; import { Entity } from "../../Entity"; import { HitResult } from "../../physics"; -import { Pointer, PointerPhase } from "./Pointer"; +import { PointerPhase } from "../enums/PointerPhase"; +import { PointerType } from "../enums/PointerType"; +import { Pointer } from "./Pointer"; /** * Pointer Manager. + * @internal */ export class PointerManager { private static _tempRay: Ray = new Ray(); @@ -138,7 +141,7 @@ export class PointerManager { private _getIndexByPointerID(pointerId: number): number { const { _pointers: _pointers } = this; for (let i = _pointers.length - 1; i >= 0; i--) { - if (_pointers[i].pointerId === pointerId) { + if (_pointers[i].uniqueID === pointerId) { return i; } } @@ -181,8 +184,8 @@ export class PointerManager { } /** Update and add pointer. */ const touch = pointerPool[i]; - touch.pointerId = pointerId; - touch.pointerType = pointerType; + touch.uniqueID = pointerId; + touch.pointerType = PointerType[pointerType]; touch.position.setValue(x, y); touch.phase = phase; touch.timeStamp = timeStamp; From 66882b0407aef29a651483bcbfb4fa35116bcc59 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Tue, 28 Sep 2021 16:12:08 +0800 Subject: [PATCH 58/72] feat: Opt code. --- packages/core/src/input/InputManager.ts | 20 +- packages/core/src/input/enums/PointerPhase.ts | 2 +- packages/core/src/input/enums/PointerType.ts | 11 - packages/core/src/input/pointer/Pointer.ts | 25 +- .../core/src/input/pointer/PointerManager.ts | 290 +++++++----------- 5 files changed, 128 insertions(+), 220 deletions(-) delete mode 100644 packages/core/src/input/enums/PointerType.ts diff --git a/packages/core/src/input/InputManager.ts b/packages/core/src/input/InputManager.ts index 1d42d4fd06..277b1c4eb4 100644 --- a/packages/core/src/input/InputManager.ts +++ b/packages/core/src/input/InputManager.ts @@ -8,10 +8,7 @@ import { PointerManager } from "./pointer/PointerManager"; export class InputManager { private _pointerManager: PointerManager; - /** - * Get pointers. - * @remarks The returned list should be considered deep-read-only. - */ + /** Pointer List. */ get pointers(): Readonly { return this._pointerManager._pointers; } @@ -27,27 +24,18 @@ export class InputManager { this._pointerManager._multiPointerEnabled = enabled; } - /** - * Constructor an InputManager. - * @param engine - The current engine instance - */ + /** @internal */ constructor(engine: Engine) { // @ts-ignore this._pointerManager = new PointerManager(engine, engine.canvas._webCanvas); } - /** - * Update pointer event, will be executed every frame. - * @internal - */ + /** @internal */ _update(): void { this._pointerManager._update(); } - /** - * Called when the engine is destroyed. - * @internal - */ + /** @internal */ _destroy(): void { this._pointerManager._destroy(); } diff --git a/packages/core/src/input/enums/PointerPhase.ts b/packages/core/src/input/enums/PointerPhase.ts index 90228a6d72..8638b44a14 100644 --- a/packages/core/src/input/enums/PointerPhase.ts +++ b/packages/core/src/input/enums/PointerPhase.ts @@ -1,5 +1,5 @@ /** - * The current stage of the pointer. + * The current phase of the pointer. */ export enum PointerPhase { /** A Pointer pressed on the screen. */ diff --git a/packages/core/src/input/enums/PointerType.ts b/packages/core/src/input/enums/PointerType.ts deleted file mode 100644 index 9f7e7d8cfc..0000000000 --- a/packages/core/src/input/enums/PointerType.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * The type of the pointer. - */ -export enum PointerType { - /** The event was generated by a mouse device. */ - mouse, - /** The event was generated by a pen or stylus device. */ - pen, - /** The event was generated by a touch, such as a finger. */ - touch -} diff --git a/packages/core/src/input/pointer/Pointer.ts b/packages/core/src/input/pointer/Pointer.ts index aaf283b4fe..3539654b80 100644 --- a/packages/core/src/input/pointer/Pointer.ts +++ b/packages/core/src/input/pointer/Pointer.ts @@ -1,24 +1,29 @@ import { Vector2 } from "@oasis-engine/math"; import { PointerPhase } from "../enums/PointerPhase"; -import { PointerType } from "../enums/PointerType"; /** * Pointer. */ export class Pointer { - /** UniqueID of PointerEvent. */ - uniqueID: number; - pointerType: PointerType; - /** Timestamp of the most recent phase change. */ - timeStamp: number; - /** Recent phase. */ + /** + * Unique id. + * @remark Start from 0. + */ + readonly id: number; + /** The phase of pointer. */ phase: PointerPhase = PointerPhase.Leave; /** The position of the pointer in screen space pixel coordinates. */ position: Vector2 = new Vector2(); + /** @internal */ + _uniqueID: number; + /** @internal */ + _needUpdate: boolean = true; + /** - * Constructor a Pointer. - * @param id - The id for the pointer, start from 0 and automatically fill in. + * @internal */ - constructor(public id: number) {} + constructor(id: number) { + this.id = id; + } } diff --git a/packages/core/src/input/pointer/PointerManager.ts b/packages/core/src/input/pointer/PointerManager.ts index c33c9f32c5..1d11861e1a 100644 --- a/packages/core/src/input/pointer/PointerManager.ts +++ b/packages/core/src/input/pointer/PointerManager.ts @@ -4,7 +4,6 @@ import { Engine } from "../../Engine"; import { Entity } from "../../Entity"; import { HitResult } from "../../physics"; import { PointerPhase } from "../enums/PointerPhase"; -import { PointerType } from "../enums/PointerType"; import { Pointer } from "./Pointer"; /** @@ -24,20 +23,14 @@ export class PointerManager { private _engine: Engine; private _canvas: Canvas; - /** When pressing and there is only one pointer, - * correct the pointerId to the minimum threshold, - * so as to prevent _lastMoveHash from becoming too large. */ - private _minPointerThreshold: number = 0; - private _lastMoveHash: number[] = []; - - private _eventList: PointerEvent[] = []; + private _events: PointerEvent[] = []; private _pointerPool: Pointer[]; - private _effectiveEventList: number[] = []; - private _effectiveEventCount = 0; - private _curFrameHasCancel: boolean = false; - private _curFramePosition: Vector2 = new Vector2(); - private _curFramePressedEntity: Entity; - private _curFrameEnteredEntity: Entity; + private _keyEventList: number[] = []; + private _keyEventCount = 0; + private _needOverallPointers: boolean = false; + private _currentPosition: Vector2 = new Vector2(); + private _currentPressedEntity: Entity; + private _currentEnteredEntity: Entity; /** * Create a PointerManager. @@ -49,54 +42,52 @@ export class PointerManager { // @ts-ignore const htmlCanvas = this._canvas._webCanvas as HTMLCanvasElement; htmlCanvas.style.touchAction = "none"; - /** Simultaneous touch contact points are supported by the current device. */ - this._pointerPool = new Array(navigator.maxTouchPoints + 1); // prettier-ignore - htmlCanvas.onpointerdown = htmlCanvas.onpointerup = htmlCanvas.onpointerout = (evt:PointerEvent)=>{ - this._eventList.push(evt); - }; - htmlCanvas.onpointermove = (evt: PointerEvent) => { - this._lastMoveHash[evt.pointerId - this._minPointerThreshold] = this._eventList.push(evt) - 1; + htmlCanvas.onpointerdown = htmlCanvas.onpointerup = htmlCanvas.onpointerout = htmlCanvas.onpointermove = (evt:PointerEvent)=>{ + this._events.push(evt); }; + this._pointerPool = new Array(navigator.maxTouchPoints + 1); } /** - * Update pointer event, will be executed every frame. * @internal */ _update(): void { - this._curFrameHasCancel && this._adjustPointers(); - /** Check whether Drag events are triggered. */ - this._executeDrag(); - if (this._eventList.length > 0) { - this._handlePointerEvent(this._eventList); + this._needOverallPointers && this._overallPointers(); + if (this._events.length > 0) { + this._handlePointerEvent(this._events); /** Get the entity hit by the ray. */ - const curEnteredEntity = this._pointerRaycast(); + const rayCastEntity = this._pointerRayCast(); /** Check whether Enter and Exit events are triggered. */ - this._executeEnterAndExit(curEnteredEntity); + this._firePointerEnterAndExit(rayCastEntity); /** Check whether down, up and click events are triggered. */ - const { _effectiveEventList, _effectiveEventCount } = this; - for (let i = 0; i < _effectiveEventCount; i++) { - switch (_effectiveEventList[i]) { - case PointerEventType.Down: - this._executeDown(curEnteredEntity); - break; - case PointerEventType.Up: - this._executeUpAndClick(curEnteredEntity); - break; - case PointerEventType.Leave: - this._executeEnterAndExit(null); - this._curFramePressedEntity = null; - break; + const { _keyEventList: keyEventList, _keyEventCount: keyEventCount } = this; + if (keyEventCount > 0) { + for (let i = 0; i < keyEventCount; i++) { + switch (keyEventList[i]) { + case PointerEventType.Down: + this._firePointerDown(rayCastEntity); + break; + case PointerEventType.Up: + this._firePointerUpAndClick(rayCastEntity); + break; + case PointerEventType.Leave: + this._firePointerEnterAndExit(null); + this._currentPressedEntity = null; + break; + } } + this._keyEventCount = 0; + } else { + this._firePointerDrag(); } - this._effectiveEventCount = 0; } else { + this._firePointerDrag(); const { _pointers: pointers } = this; for (let i = pointers.length - 1; i >= 0; i--) { const pointer = pointers[i]; if (pointer.phase !== PointerPhase.Leave) { - this._executeEnterAndExit(this._pointerRaycast()); + this._firePointerEnterAndExit(this._pointerRayCast()); } } } @@ -110,12 +101,12 @@ export class PointerManager { // @ts-ignore const htmlCanvas = this._canvas._webCanvas as HTMLCanvasElement; htmlCanvas.onpointerdown = htmlCanvas.onpointerup = htmlCanvas.onpointerout = htmlCanvas.onpointermove = null; - this._eventList.length = 0; + this._events.length = 0; this._pointerPool.length = 0; this._pointers.length = 0; - this._curFramePosition = null; - this._curFrameEnteredEntity = null; - this._curFramePressedEntity = null; + this._currentPosition = null; + this._currentEnteredEntity = null; + this._currentPressedEntity = null; this._engine = null; this._canvas = null; } @@ -123,14 +114,14 @@ export class PointerManager { /** * Remove those pointers whose status is "End". */ - private _adjustPointers(): void { + private _overallPointers(): void { const { _pointers: _pointers } = this; for (let i = _pointers.length - 1; i >= 0; i--) { if (_pointers[i].phase === PointerPhase.Leave) { _pointers.splice(i, 1); } } - this._curFrameHasCancel = false; + this._needOverallPointers = false; } /** @@ -141,7 +132,7 @@ export class PointerManager { private _getIndexByPointerID(pointerId: number): number { const { _pointers: _pointers } = this; for (let i = _pointers.length - 1; i >= 0; i--) { - if (_pointers[i].uniqueID === pointerId) { + if (_pointers[i]._uniqueID === pointerId) { return i; } } @@ -157,21 +148,10 @@ export class PointerManager { * @param phase - The phase of the pointer * @param timeStamp - Timestamp when changing phase */ - public _addPointer( - pointerId: number, - pointerType: string, - x: number, - y: number, - phase: PointerPhase, - timeStamp: number - ): void { + public _addPointer(pointerId: number, x: number, y: number, phase: PointerPhase): void { const lastCount = this._pointers.length; if (lastCount <= 0 || this._multiPointerEnabled) { - const { _pointers: pointers, _pointerPool: pointerPool, _curFramePosition: curFramePosition, _canvas } = this; - // @ts-ignore - x *= _canvas.width / (_canvas._webCanvas as HTMLCanvasElement).clientWidth; - // @ts-ignore - y *= _canvas.height / (_canvas._webCanvas as HTMLCanvasElement).clientHeight; + const { _pointers: pointers, _pointerPool: pointerPool } = this; /** Get Pointer smallest index. */ let i = 0; for (; i < lastCount; i++) { @@ -183,109 +163,61 @@ export class PointerManager { pointerPool[i] = new Pointer(i); } /** Update and add pointer. */ - const touch = pointerPool[i]; - touch.uniqueID = pointerId; - touch.pointerType = PointerType[pointerType]; - touch.position.setValue(x, y); - touch.phase = phase; - touch.timeStamp = timeStamp; - pointers.splice(i, 0, touch); - const nowCount = lastCount + 1; - curFramePosition.x = (curFramePosition.x * lastCount + x) / nowCount; - curFramePosition.y = (curFramePosition.y * lastCount + y) / nowCount; + const pointer = pointerPool[i]; + pointer._uniqueID = pointerId; + pointer.position.setValue(x, y); + pointer._needUpdate = true; + pointer.phase = phase; + pointers.splice(i, 0, pointer); } } - /** - * Remove pointer. - * @param pointerIndex - Index of pointer in pointers - * @param timeStamp - Timestamp when changing phase - */ - private _removePointer(pointerIndex: number, timeStamp: number): void { - const { _pointers: pointers, _curFramePosition: curFramePosition } = this; - const lastCount = pointers.length; - const removedPointer = pointers[pointerIndex]; - if (lastCount > 1) { - const nowCount = lastCount - 1; - const { position } = removedPointer; - curFramePosition.x = (curFramePosition.x * lastCount - position.x) / nowCount; - curFramePosition.y = (curFramePosition.y * lastCount - position.y) / nowCount; - } - removedPointer.phase = PointerPhase.Leave; - removedPointer.timeStamp = timeStamp; + private _removePointer(pointerIndex: number): void { + this._pointers[pointerIndex].phase = PointerPhase.Leave; } - /** - * Update pointer. - * @param pointerIndex - Index of pointer in pointers - * @param x - OffsetX in PointerEvent - * @param y - OffsetY in PointerEvent - * @param phase - The phase of the pointer - * @param timeStamp - Timestamp when changing phase - */ - private _updatePointer(pointerIndex: number, x: number, y: number, phase: PointerPhase, timeStamp: number): void { - const { _pointers: pointers, _canvas: canvas } = this; - const updatedPointer = pointers[pointerIndex]; - const { position } = updatedPointer; - // @ts-ignore - x *= canvas.width / (canvas._webCanvas as HTMLCanvasElement).clientWidth; - // @ts-ignore - y *= canvas.height / (canvas._webCanvas as HTMLCanvasElement).clientHeight; - this._curFramePosition.x += (x - position.x) / pointers.length; - this._curFramePosition.y += (y - position.y) / pointers.length; - position.setValue(x, y); + private _updatePointer(pointerIndex: number, x: number, y: number, phase: PointerPhase): void { + const updatedPointer = this._pointers[pointerIndex]; + updatedPointer.position.setValue(x, y); + updatedPointer._needUpdate = true; updatedPointer.phase = phase; - updatedPointer.timeStamp = timeStamp; } - /** - * Update pointer data and filter out valid event information. - * @param eventList - PointerEvent List waiting to be processed - * @returns Effective event information - */ - private _handlePointerEvent(eventList: PointerEvent[]): void { - const { _pointers, _effectiveEventList, _lastMoveHash, _minPointerThreshold } = this; - const timeStamp = this._engine.time.nowTime; - let activePointerCount = _pointers.length; - for (let i = 0, n = eventList.length; i < n; i++) { - const evt = eventList[i]; + private _handlePointerEvent(events: PointerEvent[]): void { + const { _pointers: pointers, _keyEventList: _effectiveEventList } = this; + let activePointerCount = pointers.length; + for (let i = 0, n = events.length; i < n; i++) { + const evt = events[i]; let pointerIndex = this._getIndexByPointerID(evt.pointerId); switch (evt.type) { case "pointerdown": - if (pointerIndex < 0) { - this._addPointer(evt.pointerId, evt.type, evt.offsetX, evt.offsetY, PointerPhase.Down, timeStamp); + if (pointerIndex === -1) { + this._addPointer(evt.pointerId, evt.offsetX, evt.offsetY, PointerPhase.Down); ++activePointerCount; } else { - this._updatePointer(pointerIndex, evt.offsetX, evt.offsetY, PointerPhase.Down, timeStamp); - } - if (activePointerCount === 1) { - _effectiveEventList[this._effectiveEventCount++] = PointerEventType.Down; - /** Update pointer's threshold. */ - this._minPointerThreshold = evt.pointerId; + this._updatePointer(pointerIndex, evt.offsetX, evt.offsetY, PointerPhase.Down); } + activePointerCount === 1 && (_effectiveEventList[this._keyEventCount++] = PointerEventType.Down); break; case "pointerup": if (pointerIndex >= 0) { - this._updatePointer(pointerIndex, evt.offsetX, evt.offsetY, PointerPhase.Up, timeStamp); - activePointerCount === 1 && (_effectiveEventList[this._effectiveEventCount++] = PointerEventType.Up); + this._updatePointer(pointerIndex, evt.offsetX, evt.offsetY, PointerPhase.Up); + activePointerCount === 1 && (_effectiveEventList[this._keyEventCount++] = PointerEventType.Up); } break; case "pointermove": - if (_lastMoveHash[evt.pointerId - _minPointerThreshold] === i) { - if (pointerIndex < 0) { - this._addPointer(evt.pointerId, evt.type, evt.offsetX, evt.offsetY, PointerPhase.Move, timeStamp); - ++activePointerCount; - } else { - this._updatePointer(pointerIndex, evt.offsetX, evt.offsetY, PointerPhase.Move, timeStamp); - } - _effectiveEventList[this._effectiveEventCount++] = PointerEventType.Move; + if (pointerIndex === -1) { + this._addPointer(evt.pointerId, evt.offsetX, evt.offsetY, PointerPhase.Move); + ++activePointerCount; + } else { + this._updatePointer(pointerIndex, evt.offsetX, evt.offsetY, PointerPhase.Move); } break; case "pointerout": if (pointerIndex >= 0) { - this._removePointer(pointerIndex, timeStamp); - --activePointerCount === 0 && (_effectiveEventList[this._effectiveEventCount++] = PointerEventType.Leave); - this._curFrameHasCancel = true; + this._removePointer(pointerIndex); + --activePointerCount === 0 && (_effectiveEventList[this._keyEventCount++] = PointerEventType.Leave); + this._needOverallPointers = true; } break; default: @@ -293,20 +225,29 @@ export class PointerManager { } } /** Reset event list. */ - eventList.length = 0; + events.length = 0; + const { _canvas: canvas, _currentPosition: currentPosition } = this; + const pointerCount = pointers.length; + currentPosition.setValue(0, 0); + if (pointerCount > 0) { + // @ts-ignore + const pixelRatio = canvas.width / (canvas._webCanvas as HTMLCanvasElement).clientWidth; + for (let i = 0; i < pointerCount; i++) { + const pointer = pointers[i]; + const { position } = pointer; + if (pointer._needUpdate) { + position.scale(pixelRatio); + pointer._needUpdate = false; + } + currentPosition.add(position); + } + } + currentPosition.scale(1 / pointerCount); } - /** - * Get the Entity to which the ray is cast. - * @param x - The X coordinate of the pointer on the screen, specified in normalized - * @param y - The Y coordinate of the pointer on the screen, specified in normalized - * @returns The Entity to which the ray is cast - */ - private _pointerRaycast(): Entity { - let { x, y } = this._curFramePosition; + private _pointerRayCast(): Entity { + let { x, y } = this._currentPosition; /** Convert screen coordinates to viewport coordinates. */ - x /= this._canvas.width; - y /= this._canvas.height; const cameras = this._engine.sceneManager.activeScene._activeCameras; for (let i = cameras.length - 1; i >= 0; i--) { const camera = cameras[i]; @@ -328,63 +269,48 @@ export class PointerManager { return null; } - /** - * Execute drag event. - */ - private _executeDrag(): void { + private _firePointerDrag(): void { /** Check whether pressed events are triggered. */ - if (this._curFramePressedEntity) { - const scripts = this._curFramePressedEntity._scripts; + if (this._currentPressedEntity) { + const scripts = this._currentPressedEntity._scripts; for (let i = scripts.length - 1; i >= 0; i--) { scripts.get(i).onPointerDrag(); } } } - /** - * Execute enter and exit events. - * @param curEnteredEntity - Which entity the pointer is currently on - */ - private _executeEnterAndExit(curEnteredEntity: Entity): void { + private _firePointerEnterAndExit(curEnteredEntity: Entity): void { /** Check whether enter and exit events are triggered. */ - if (this._curFrameEnteredEntity !== curEnteredEntity) { + if (this._currentEnteredEntity !== curEnteredEntity) { if (curEnteredEntity) { const scripts = curEnteredEntity._scripts; for (let i = scripts.length - 1; i >= 0; i--) { scripts.get(i).onPointerEnter(); } } - if (this._curFrameEnteredEntity) { - const scripts = this._curFrameEnteredEntity._scripts; + if (this._currentEnteredEntity) { + const scripts = this._currentEnteredEntity._scripts; for (let i = scripts.length - 1; i >= 0; i--) { scripts.get(i).onPointerExit(); } } - this._curFrameEnteredEntity = curEnteredEntity; + this._currentEnteredEntity = curEnteredEntity; } } - /** - * Execute down events. - * @param curEnteredEntity - Which entity the pointer is currently on - */ - private _executeDown(curEnteredEntity: Entity): void { + private _firePointerDown(curEnteredEntity: Entity): void { if (curEnteredEntity) { const scripts = curEnteredEntity._scripts; for (let i = scripts.length - 1; i >= 0; i--) { scripts.get(i).onPointerDown(); } } - this._curFramePressedEntity = curEnteredEntity; + this._currentPressedEntity = curEnteredEntity; } - /** - * Execute up and click events. - * @param curEnteredEntity - Which entity the pointer is currently on - */ - private _executeUpAndClick(curEnteredEntity: Entity): void { + private _firePointerUpAndClick(curEnteredEntity: Entity): void { if (curEnteredEntity) { - const operateOneTarget = this._curFramePressedEntity === curEnteredEntity; + const operateOneTarget = this._currentPressedEntity === curEnteredEntity; const scripts = curEnteredEntity._scripts; for (let i = scripts.length - 1; i >= 0; i--) { const script = scripts.get(i); @@ -392,7 +318,7 @@ export class PointerManager { script.onPointerUp(); } } - this._curFramePressedEntity = null; + this._currentPressedEntity = null; } } From 7ebef936d8412bc7b57461a87e27acf86c4bd88b Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Tue, 28 Sep 2021 16:33:51 +0800 Subject: [PATCH 59/72] feat: Opt code. --- packages/core/src/input/pointer/PointerManager.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/input/pointer/PointerManager.ts b/packages/core/src/input/pointer/PointerManager.ts index 1d11861e1a..0f153372f8 100644 --- a/packages/core/src/input/pointer/PointerManager.ts +++ b/packages/core/src/input/pointer/PointerManager.ts @@ -193,7 +193,7 @@ export class PointerManager { case "pointerdown": if (pointerIndex === -1) { this._addPointer(evt.pointerId, evt.offsetX, evt.offsetY, PointerPhase.Down); - ++activePointerCount; + activePointerCount++; } else { this._updatePointer(pointerIndex, evt.offsetX, evt.offsetY, PointerPhase.Down); } @@ -208,7 +208,7 @@ export class PointerManager { case "pointermove": if (pointerIndex === -1) { this._addPointer(evt.pointerId, evt.offsetX, evt.offsetY, PointerPhase.Move); - ++activePointerCount; + activePointerCount++; } else { this._updatePointer(pointerIndex, evt.offsetX, evt.offsetY, PointerPhase.Move); } From 1efbd1184bda081dfa32d48deb759d2106a5bda2 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Wed, 29 Sep 2021 11:43:34 +0800 Subject: [PATCH 60/72] feat: Opt code. --- packages/core/src/input/InputManager.ts | 16 +- .../core/src/input/pointer/PointerManager.ts | 201 ++++++++---------- 2 files changed, 103 insertions(+), 114 deletions(-) diff --git a/packages/core/src/input/InputManager.ts b/packages/core/src/input/InputManager.ts index 277b1c4eb4..58a75d337b 100644 --- a/packages/core/src/input/InputManager.ts +++ b/packages/core/src/input/InputManager.ts @@ -8,7 +8,9 @@ import { PointerManager } from "./pointer/PointerManager"; export class InputManager { private _pointerManager: PointerManager; - /** Pointer List. */ + /** + * Pointer List. + */ get pointers(): Readonly { return this._pointerManager._pointers; } @@ -24,18 +26,24 @@ export class InputManager { this._pointerManager._multiPointerEnabled = enabled; } - /** @internal */ + /** + * @internal + */ constructor(engine: Engine) { // @ts-ignore this._pointerManager = new PointerManager(engine, engine.canvas._webCanvas); } - /** @internal */ + /** + * @internal + */ _update(): void { this._pointerManager._update(); } - /** @internal */ + /** + * @internal + */ _destroy(): void { this._pointerManager._destroy(); } diff --git a/packages/core/src/input/pointer/PointerManager.ts b/packages/core/src/input/pointer/PointerManager.ts index 0f153372f8..e07f3b7ad9 100644 --- a/packages/core/src/input/pointer/PointerManager.ts +++ b/packages/core/src/input/pointer/PointerManager.ts @@ -2,6 +2,7 @@ import { Ray, Vector2 } from "@oasis-engine/math"; import { Canvas } from "../../Canvas"; import { Engine } from "../../Engine"; import { Entity } from "../../Entity"; +import { CameraClearFlags } from "../../enums/CameraClearFlags"; import { HitResult } from "../../physics"; import { PointerPhase } from "../enums/PointerPhase"; import { Pointer } from "./Pointer"; @@ -22,11 +23,10 @@ export class PointerManager { private _engine: Engine; private _canvas: Canvas; - - private _events: PointerEvent[] = []; + private _nativeEvents: PointerEvent[] = []; private _pointerPool: Pointer[]; private _keyEventList: number[] = []; - private _keyEventCount = 0; + private _keyEventCount: number = 0; private _needOverallPointers: boolean = false; private _currentPosition: Vector2 = new Vector2(); private _currentPressedEntity: Entity; @@ -44,8 +44,9 @@ export class PointerManager { htmlCanvas.style.touchAction = "none"; // prettier-ignore htmlCanvas.onpointerdown = htmlCanvas.onpointerup = htmlCanvas.onpointerout = htmlCanvas.onpointermove = (evt:PointerEvent)=>{ - this._events.push(evt); + this._nativeEvents.push(evt); }; + // MaxTouchCount + MouseCount(1) this._pointerPool = new Array(navigator.maxTouchPoints + 1); } @@ -54,15 +55,12 @@ export class PointerManager { */ _update(): void { this._needOverallPointers && this._overallPointers(); - if (this._events.length > 0) { - this._handlePointerEvent(this._events); - /** Get the entity hit by the ray. */ + if (this._nativeEvents.length > 0) { + this._handlePointerEvent(this._nativeEvents); const rayCastEntity = this._pointerRayCast(); - /** Check whether Enter and Exit events are triggered. */ - this._firePointerEnterAndExit(rayCastEntity); - /** Check whether down, up and click events are triggered. */ - const { _keyEventList: keyEventList, _keyEventCount: keyEventCount } = this; + const { _keyEventCount: keyEventCount, _keyEventList: keyEventList } = this; if (keyEventCount > 0) { + this._firePointerExitAndEnter(rayCastEntity); for (let i = 0; i < keyEventCount; i++) { switch (keyEventList[i]) { case PointerEventType.Down: @@ -71,37 +69,31 @@ export class PointerManager { case PointerEventType.Up: this._firePointerUpAndClick(rayCastEntity); break; - case PointerEventType.Leave: - this._firePointerEnterAndExit(null); - this._currentPressedEntity = null; - break; } } + if (keyEventList[keyEventCount - 1] === PointerEventType.Leave) { + this._firePointerExitAndEnter(null); + this._currentPressedEntity = null; + } this._keyEventCount = 0; } else { this._firePointerDrag(); + this._firePointerExitAndEnter(rayCastEntity); } } else { this._firePointerDrag(); - const { _pointers: pointers } = this; - for (let i = pointers.length - 1; i >= 0; i--) { - const pointer = pointers[i]; - if (pointer.phase !== PointerPhase.Leave) { - this._firePointerEnterAndExit(this._pointerRayCast()); - } - } + this._firePointerExitAndEnter(this._pointerRayCast()); } } /** - * Called when the engine is destroyed. * @internal */ _destroy(): void { // @ts-ignore const htmlCanvas = this._canvas._webCanvas as HTMLCanvasElement; htmlCanvas.onpointerdown = htmlCanvas.onpointerup = htmlCanvas.onpointerout = htmlCanvas.onpointermove = null; - this._events.length = 0; + this._nativeEvents.length = 0; this._pointerPool.length = 0; this._pointers.length = 0; this._currentPosition = null; @@ -111,59 +103,49 @@ export class PointerManager { this._canvas = null; } - /** - * Remove those pointers whose status is "End". - */ private _overallPointers(): void { - const { _pointers: _pointers } = this; - for (let i = _pointers.length - 1; i >= 0; i--) { - if (_pointers[i].phase === PointerPhase.Leave) { - _pointers.splice(i, 1); + const { _pointers: pointers } = this; + let deleteCount = 0; + const totalCount = pointers.length; + for (let i = 0; i < totalCount; i++) { + if (pointers[i].phase === PointerPhase.Leave) { + deleteCount++; + } else { + if (deleteCount > 0) { + pointers[i - deleteCount] = pointers[i]; + } } } + pointers.length = totalCount - deleteCount; this._needOverallPointers = false; } - /** - * Get the index of the pointer in the pointers. - * @param pointerId - PointerId of PointerEvent - * @returns Index of pointer in pointers - */ private _getIndexByPointerID(pointerId: number): number { - const { _pointers: _pointers } = this; - for (let i = _pointers.length - 1; i >= 0; i--) { - if (_pointers[i]._uniqueID === pointerId) { + const { _pointers: pointers } = this; + for (let i = pointers.length - 1; i >= 0; i--) { + if (pointers[i]._uniqueID === pointerId) { return i; } } return -1; } - /** - * Add pointer. - * @param pointerId The pointerId of the pointer - * @param pointerType The pointerType of the pointer - * @param x - OffsetX in PointerEvent - * @param y - OffsetY in PointerEvent - * @param phase - The phase of the pointer - * @param timeStamp - Timestamp when changing phase - */ public _addPointer(pointerId: number, x: number, y: number, phase: PointerPhase): void { - const lastCount = this._pointers.length; - if (lastCount <= 0 || this._multiPointerEnabled) { - const { _pointers: pointers, _pointerPool: pointerPool } = this; - /** Get Pointer smallest index. */ + const { _pointers: pointers } = this; + const lastCount = pointers.length; + if (lastCount === 0 || this._multiPointerEnabled) { + const { _pointerPool: pointerPool } = this; + // Get Pointer smallest index. let i = 0; for (; i < lastCount; i++) { if (pointers[i].id > i) { break; } } - if (!pointerPool[i]) { - pointerPool[i] = new Pointer(i); + let pointer = pointerPool[i]; + if (pointer) { + pointer = pointerPool[i] = new Pointer(i); } - /** Update and add pointer. */ - const pointer = pointerPool[i]; pointer._uniqueID = pointerId; pointer.position.setValue(x, y); pointer._needUpdate = true; @@ -183,11 +165,11 @@ export class PointerManager { updatedPointer.phase = phase; } - private _handlePointerEvent(events: PointerEvent[]): void { - const { _pointers: pointers, _keyEventList: _effectiveEventList } = this; + private _handlePointerEvent(nativeEvents: PointerEvent[]): void { + const { _pointers: pointers, _keyEventList: keyEventList } = this; let activePointerCount = pointers.length; - for (let i = 0, n = events.length; i < n; i++) { - const evt = events[i]; + for (let i = 0, n = nativeEvents.length; i < n; i++) { + const evt = nativeEvents[i]; let pointerIndex = this._getIndexByPointerID(evt.pointerId); switch (evt.type) { case "pointerdown": @@ -197,12 +179,12 @@ export class PointerManager { } else { this._updatePointer(pointerIndex, evt.offsetX, evt.offsetY, PointerPhase.Down); } - activePointerCount === 1 && (_effectiveEventList[this._keyEventCount++] = PointerEventType.Down); + activePointerCount === 1 && (keyEventList[this._keyEventCount++] = PointerEventType.Down); break; case "pointerup": if (pointerIndex >= 0) { this._updatePointer(pointerIndex, evt.offsetX, evt.offsetY, PointerPhase.Up); - activePointerCount === 1 && (_effectiveEventList[this._keyEventCount++] = PointerEventType.Up); + activePointerCount === 1 && (keyEventList[this._keyEventCount++] = PointerEventType.Up); } break; case "pointermove": @@ -216,22 +198,19 @@ export class PointerManager { case "pointerout": if (pointerIndex >= 0) { this._removePointer(pointerIndex); - --activePointerCount === 0 && (_effectiveEventList[this._keyEventCount++] = PointerEventType.Leave); + --activePointerCount === 0 && (keyEventList[this._keyEventCount++] = PointerEventType.Leave); this._needOverallPointers = true; } break; - default: - break; } } - /** Reset event list. */ - events.length = 0; + nativeEvents.length = 0; const { _canvas: canvas, _currentPosition: currentPosition } = this; const pointerCount = pointers.length; - currentPosition.setValue(0, 0); if (pointerCount > 0) { // @ts-ignore const pixelRatio = canvas.width / (canvas._webCanvas as HTMLCanvasElement).clientWidth; + currentPosition.setValue(0, 0); for (let i = 0; i < pointerCount; i++) { const pointer = pointers[i]; const { position } = pointer; @@ -241,36 +220,39 @@ export class PointerManager { } currentPosition.add(position); } + currentPosition.scale(1 / pointerCount); } - currentPosition.scale(1 / pointerCount); } private _pointerRayCast(): Entity { - let { x, y } = this._currentPosition; - /** Convert screen coordinates to viewport coordinates. */ - const cameras = this._engine.sceneManager.activeScene._activeCameras; - for (let i = cameras.length - 1; i >= 0; i--) { - const camera = cameras[i]; - if (!camera.enabled || camera.renderTarget) { - continue; - } - const { x: vpX, y: vpY, z: vpW, w: vpH } = camera.viewport; - if (x >= vpX && y >= vpY && x - vpX <= vpW && y - vpY <= vpH) { - PointerManager._tempPoint.setValue((x - vpX) / vpW, (y - vpY) / vpH); - // TODO: Only check which colliders have listened to the input. - return this._engine.physicsManager.raycast( - camera.viewportPointToRay(PointerManager._tempPoint, PointerManager._tempRay), - PointerManager._tempHitResult - ) - ? PointerManager._tempHitResult.entity - : null; + if (this._pointers.length > 0) { + let x = this._currentPosition.x / this._canvas.width; + let y = this._currentPosition.y / this._canvas.height; + const cameras = this._engine.sceneManager.activeScene._activeCameras; + const { _tempPoint, _tempRay, _tempHitResult } = PointerManager; + for (let i = cameras.length - 1; i >= 0; i--) { + const camera = cameras[i]; + if (!camera.enabled || camera.renderTarget) { + continue; + } + const { x: vpX, y: vpY, z: vpW, w: vpH } = camera.viewport; + if (x >= vpX && y >= vpY && x - vpX <= vpW && y - vpY <= vpH) { + PointerManager._tempPoint.setValue((x - vpX) / vpW, (y - vpY) / vpH); + // TODO: Only check which colliders have listened to the input. + if (this._engine.physicsManager.raycast(camera.viewportPointToRay(_tempPoint, _tempRay), _tempHitResult)) { + return PointerManager._tempHitResult.entity; + } else { + if (camera.clearFlags === CameraClearFlags.DepthColor) { + return null; + } + } + } } } return null; } private _firePointerDrag(): void { - /** Check whether pressed events are triggered. */ if (this._currentPressedEntity) { const scripts = this._currentPressedEntity._scripts; for (let i = scripts.length - 1; i >= 0; i--) { @@ -279,46 +261,45 @@ export class PointerManager { } } - private _firePointerEnterAndExit(curEnteredEntity: Entity): void { - /** Check whether enter and exit events are triggered. */ - if (this._currentEnteredEntity !== curEnteredEntity) { - if (curEnteredEntity) { - const scripts = curEnteredEntity._scripts; - for (let i = scripts.length - 1; i >= 0; i--) { - scripts.get(i).onPointerEnter(); - } + private _firePointerExitAndEnter(rayCastEntity: Entity): void { + if (this._currentEnteredEntity) { + const scripts = this._currentEnteredEntity._scripts; + for (let i = scripts.length - 1; i >= 0; i--) { + scripts.get(i).onPointerExit(); } - if (this._currentEnteredEntity) { - const scripts = this._currentEnteredEntity._scripts; + } + if (this._currentEnteredEntity !== rayCastEntity) { + if (rayCastEntity) { + const scripts = rayCastEntity._scripts; for (let i = scripts.length - 1; i >= 0; i--) { - scripts.get(i).onPointerExit(); + scripts.get(i).onPointerEnter(); } } - this._currentEnteredEntity = curEnteredEntity; + this._currentEnteredEntity = rayCastEntity; } } - private _firePointerDown(curEnteredEntity: Entity): void { - if (curEnteredEntity) { - const scripts = curEnteredEntity._scripts; + private _firePointerDown(rayCastEntity: Entity): void { + if (rayCastEntity) { + const scripts = rayCastEntity._scripts; for (let i = scripts.length - 1; i >= 0; i--) { scripts.get(i).onPointerDown(); } } - this._currentPressedEntity = curEnteredEntity; + this._currentPressedEntity = rayCastEntity; } - private _firePointerUpAndClick(curEnteredEntity: Entity): void { - if (curEnteredEntity) { - const operateOneTarget = this._currentPressedEntity === curEnteredEntity; - const scripts = curEnteredEntity._scripts; + private _firePointerUpAndClick(rayCastEntity: Entity): void { + if (this._currentPressedEntity) { + const sameTarget = this._currentPressedEntity === rayCastEntity; + const scripts = rayCastEntity._scripts; for (let i = scripts.length - 1; i >= 0; i--) { const script = scripts.get(i); - operateOneTarget && script.onPointerClick(); + sameTarget && script.onPointerClick(); script.onPointerUp(); } + this._currentPressedEntity = null; } - this._currentPressedEntity = null; } } From 702579e39f0c292e62e43dd3386c777d012f5a49 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Wed, 29 Sep 2021 17:48:58 +0800 Subject: [PATCH 61/72] feat: Opt code. --- .../core/src/input/pointer/PointerManager.ts | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/packages/core/src/input/pointer/PointerManager.ts b/packages/core/src/input/pointer/PointerManager.ts index e07f3b7ad9..dbbd320380 100644 --- a/packages/core/src/input/pointer/PointerManager.ts +++ b/packages/core/src/input/pointer/PointerManager.ts @@ -63,15 +63,15 @@ export class PointerManager { this._firePointerExitAndEnter(rayCastEntity); for (let i = 0; i < keyEventCount; i++) { switch (keyEventList[i]) { - case PointerEventType.Down: + case PointerKeyEvent.Down: this._firePointerDown(rayCastEntity); break; - case PointerEventType.Up: + case PointerKeyEvent.Up: this._firePointerUpAndClick(rayCastEntity); break; } } - if (keyEventList[keyEventCount - 1] === PointerEventType.Leave) { + if (keyEventList[keyEventCount - 1] === PointerKeyEvent.Leave) { this._firePointerExitAndEnter(null); this._currentPressedEntity = null; } @@ -179,12 +179,12 @@ export class PointerManager { } else { this._updatePointer(pointerIndex, evt.offsetX, evt.offsetY, PointerPhase.Down); } - activePointerCount === 1 && (keyEventList[this._keyEventCount++] = PointerEventType.Down); + activePointerCount === 1 && (keyEventList[this._keyEventCount++] = PointerKeyEvent.Down); break; case "pointerup": if (pointerIndex >= 0) { this._updatePointer(pointerIndex, evt.offsetX, evt.offsetY, PointerPhase.Up); - activePointerCount === 1 && (keyEventList[this._keyEventCount++] = PointerEventType.Up); + activePointerCount === 1 && (keyEventList[this._keyEventCount++] = PointerKeyEvent.Up); } break; case "pointermove": @@ -198,24 +198,26 @@ export class PointerManager { case "pointerout": if (pointerIndex >= 0) { this._removePointer(pointerIndex); - --activePointerCount === 0 && (keyEventList[this._keyEventCount++] = PointerEventType.Leave); + --activePointerCount === 0 && (keyEventList[this._keyEventCount++] = PointerKeyEvent.Leave); this._needOverallPointers = true; } break; } } nativeEvents.length = 0; - const { _canvas: canvas, _currentPosition: currentPosition } = this; const pointerCount = pointers.length; if (pointerCount > 0) { + const { _canvas: canvas, _currentPosition: currentPosition } = this; // @ts-ignore - const pixelRatio = canvas.width / (canvas._webCanvas as HTMLCanvasElement).clientWidth; + const pixelRatioWidth = canvas.width / (canvas._webCanvas as HTMLCanvasElement).clientWidth; + // @ts-ignore + const pixelRatioHeight = canvas.height / (canvas._webCanvas as HTMLCanvasElement).clientWidth; currentPosition.setValue(0, 0); for (let i = 0; i < pointerCount; i++) { const pointer = pointers[i]; const { position } = pointer; if (pointer._needUpdate) { - position.scale(pixelRatio); + position.setValue(position.x * pixelRatioWidth, position.y * pixelRatioHeight); pointer._needUpdate = false; } currentPosition.add(position); @@ -241,10 +243,8 @@ export class PointerManager { // TODO: Only check which colliders have listened to the input. if (this._engine.physicsManager.raycast(camera.viewportPointToRay(_tempPoint, _tempRay), _tempHitResult)) { return PointerManager._tempHitResult.entity; - } else { - if (camera.clearFlags === CameraClearFlags.DepthColor) { - return null; - } + } else if (camera.clearFlags === CameraClearFlags.DepthColor) { + return null; } } } @@ -262,13 +262,13 @@ export class PointerManager { } private _firePointerExitAndEnter(rayCastEntity: Entity): void { - if (this._currentEnteredEntity) { - const scripts = this._currentEnteredEntity._scripts; - for (let i = scripts.length - 1; i >= 0; i--) { - scripts.get(i).onPointerExit(); - } - } if (this._currentEnteredEntity !== rayCastEntity) { + if (this._currentEnteredEntity) { + const scripts = this._currentEnteredEntity._scripts; + for (let i = scripts.length - 1; i >= 0; i--) { + scripts.get(i).onPointerExit(); + } + } if (rayCastEntity) { const scripts = rayCastEntity._scripts; for (let i = scripts.length - 1; i >= 0; i--) { @@ -303,10 +303,11 @@ export class PointerManager { } } -/** @internal */ -enum PointerEventType { +/** + * @internal + */ +enum PointerKeyEvent { Down, Up, - Move, Leave } From b23e00ae886d997eab2ce3b7273cdfd8db48f752 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Thu, 30 Sep 2021 17:07:36 +0800 Subject: [PATCH 62/72] feat: Opt code. --- packages/core/src/Engine.ts | 2 +- .../core/src/input/pointer/PointerManager.ts | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/core/src/Engine.ts b/packages/core/src/Engine.ts index 09e36df7f6..f16715bc26 100644 --- a/packages/core/src/Engine.ts +++ b/packages/core/src/Engine.ts @@ -256,12 +256,12 @@ export class Engine extends EventDispatcher { scene._activeCameras.sort((camera1, camera2) => camera1.priority - camera2.priority); componentsManager.callScriptOnStart(); - this._inputManager._update(); if (this.physicsManager) { componentsManager.callColliderOnUpdate(); this.physicsManager._update(deltaTime); componentsManager.callColliderOnLateUpdate(); } + this._inputManager._update(); componentsManager.callScriptOnUpdate(deltaTime); componentsManager.callAnimationUpdate(deltaTime); componentsManager.callScriptOnLateUpdate(deltaTime); diff --git a/packages/core/src/input/pointer/PointerManager.ts b/packages/core/src/input/pointer/PointerManager.ts index dbbd320380..96f0133f18 100644 --- a/packages/core/src/input/pointer/PointerManager.ts +++ b/packages/core/src/input/pointer/PointerManager.ts @@ -20,6 +20,7 @@ export class PointerManager { _pointers: Pointer[] = []; /** @internal */ _multiPointerEnabled: boolean = true; + _enablePhysics: boolean = false; private _engine: Engine; private _canvas: Canvas; @@ -48,6 +49,7 @@ export class PointerManager { }; // MaxTouchCount + MouseCount(1) this._pointerPool = new Array(navigator.maxTouchPoints + 1); + this._enablePhysics = engine.physicsManager ? true : false; } /** @@ -55,11 +57,12 @@ export class PointerManager { */ _update(): void { this._needOverallPointers && this._overallPointers(); - if (this._nativeEvents.length > 0) { - this._handlePointerEvent(this._nativeEvents); + this._nativeEvents.length > 0 && this._handlePointerEvent(this._nativeEvents); + if (this._enablePhysics) { const rayCastEntity = this._pointerRayCast(); - const { _keyEventCount: keyEventCount, _keyEventList: keyEventList } = this; + const { _keyEventCount: keyEventCount } = this; if (keyEventCount > 0) { + const { _keyEventList: keyEventList } = this; this._firePointerExitAndEnter(rayCastEntity); for (let i = 0; i < keyEventCount; i++) { switch (keyEventList[i]) { @@ -80,9 +83,6 @@ export class PointerManager { this._firePointerDrag(); this._firePointerExitAndEnter(rayCastEntity); } - } else { - this._firePointerDrag(); - this._firePointerExitAndEnter(this._pointerRayCast()); } } @@ -143,12 +143,12 @@ export class PointerManager { } } let pointer = pointerPool[i]; - if (pointer) { + if (!pointer) { pointer = pointerPool[i] = new Pointer(i); } pointer._uniqueID = pointerId; - pointer.position.setValue(x, y); pointer._needUpdate = true; + pointer.position.setValue(x, y); pointer.phase = phase; pointers.splice(i, 0, pointer); } From 23da93b7379115682aced524ef8b3218a3f16274 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Wed, 6 Oct 2021 13:30:06 +0800 Subject: [PATCH 63/72] fix:InputManager modify timing --- .../core/src/input/pointer/PointerManager.ts | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/packages/core/src/input/pointer/PointerManager.ts b/packages/core/src/input/pointer/PointerManager.ts index 96f0133f18..22c02b70ae 100644 --- a/packages/core/src/input/pointer/PointerManager.ts +++ b/packages/core/src/input/pointer/PointerManager.ts @@ -63,7 +63,6 @@ export class PointerManager { const { _keyEventCount: keyEventCount } = this; if (keyEventCount > 0) { const { _keyEventList: keyEventList } = this; - this._firePointerExitAndEnter(rayCastEntity); for (let i = 0; i < keyEventCount; i++) { switch (keyEventList[i]) { case PointerKeyEvent.Down: @@ -74,10 +73,8 @@ export class PointerManager { break; } } - if (keyEventList[keyEventCount - 1] === PointerKeyEvent.Leave) { - this._firePointerExitAndEnter(null); - this._currentPressedEntity = null; - } + this._firePointerExitAndEnter(rayCastEntity); + keyEventList[keyEventCount - 1] === PointerKeyEvent.Leave && (this._currentPressedEntity = null); this._keyEventCount = 0; } else { this._firePointerDrag(); @@ -168,7 +165,8 @@ export class PointerManager { private _handlePointerEvent(nativeEvents: PointerEvent[]): void { const { _pointers: pointers, _keyEventList: keyEventList } = this; let activePointerCount = pointers.length; - for (let i = 0, n = nativeEvents.length; i < n; i++) { + const nativeEventsLen = nativeEvents.length; + for (let i = 0; i < nativeEventsLen; i++) { const evt = nativeEvents[i]; let pointerIndex = this._getIndexByPointerID(evt.pointerId); switch (evt.type) { @@ -212,17 +210,22 @@ export class PointerManager { const pixelRatioWidth = canvas.width / (canvas._webCanvas as HTMLCanvasElement).clientWidth; // @ts-ignore const pixelRatioHeight = canvas.height / (canvas._webCanvas as HTMLCanvasElement).clientWidth; - currentPosition.setValue(0, 0); - for (let i = 0; i < pointerCount; i++) { - const pointer = pointers[i]; - const { position } = pointer; - if (pointer._needUpdate) { - position.setValue(position.x * pixelRatioWidth, position.y * pixelRatioHeight); - pointer._needUpdate = false; + if (activePointerCount === 0) { + const lastNativeEvent = nativeEvents[nativeEventsLen - 1]; + currentPosition.setValue(lastNativeEvent.offsetX * pixelRatioWidth, lastNativeEvent.offsetY * pixelRatioHeight); + } else { + currentPosition.setValue(0, 0); + for (let i = 0; i < pointerCount; i++) { + const pointer = pointers[i]; + const { position } = pointer; + if (pointer._needUpdate) { + position.setValue(position.x * pixelRatioWidth, position.y * pixelRatioHeight); + pointer._needUpdate = false; + } + currentPosition.add(position); } - currentPosition.add(position); + currentPosition.scale(1 / pointerCount); } - currentPosition.scale(1 / pointerCount); } } From 6d1d7b6657710ffaff048a07605d1715c55a6bc5 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Fri, 8 Oct 2021 15:50:09 +0800 Subject: [PATCH 64/72] fix:InputManager modify timing --- packages/core/src/input/pointer/PointerManager.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/core/src/input/pointer/PointerManager.ts b/packages/core/src/input/pointer/PointerManager.ts index 22c02b70ae..15f6acf0cb 100644 --- a/packages/core/src/input/pointer/PointerManager.ts +++ b/packages/core/src/input/pointer/PointerManager.ts @@ -202,14 +202,13 @@ export class PointerManager { break; } } - nativeEvents.length = 0; const pointerCount = pointers.length; if (pointerCount > 0) { const { _canvas: canvas, _currentPosition: currentPosition } = this; // @ts-ignore const pixelRatioWidth = canvas.width / (canvas._webCanvas as HTMLCanvasElement).clientWidth; // @ts-ignore - const pixelRatioHeight = canvas.height / (canvas._webCanvas as HTMLCanvasElement).clientWidth; + const pixelRatioHeight = canvas.height / (canvas._webCanvas as HTMLCanvasElement).clientHeight; if (activePointerCount === 0) { const lastNativeEvent = nativeEvents[nativeEventsLen - 1]; currentPosition.setValue(lastNativeEvent.offsetX * pixelRatioWidth, lastNativeEvent.offsetY * pixelRatioHeight); @@ -227,6 +226,7 @@ export class PointerManager { currentPosition.scale(1 / pointerCount); } } + nativeEvents.length = 0; } private _pointerRayCast(): Entity { @@ -293,9 +293,10 @@ export class PointerManager { } private _firePointerUpAndClick(rayCastEntity: Entity): void { - if (this._currentPressedEntity) { - const sameTarget = this._currentPressedEntity === rayCastEntity; - const scripts = rayCastEntity._scripts; + const { _currentPressedEntity } = this; + if (_currentPressedEntity) { + const sameTarget = _currentPressedEntity === rayCastEntity; + const scripts = _currentPressedEntity._scripts; for (let i = scripts.length - 1; i >= 0; i--) { const script = scripts.get(i); sameTarget && script.onPointerClick(); From 550676c6cedc29010725fdb3c0ced5a0c2917c64 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Fri, 8 Oct 2021 17:53:08 +0800 Subject: [PATCH 65/72] fix:InputManager modify timing --- packages/core/src/input/pointer/PointerManager.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/core/src/input/pointer/PointerManager.ts b/packages/core/src/input/pointer/PointerManager.ts index 15f6acf0cb..dc9529f2a2 100644 --- a/packages/core/src/input/pointer/PointerManager.ts +++ b/packages/core/src/input/pointer/PointerManager.ts @@ -20,6 +20,7 @@ export class PointerManager { _pointers: Pointer[] = []; /** @internal */ _multiPointerEnabled: boolean = true; + /** @internal */ _enablePhysics: boolean = false; private _engine: Engine; @@ -293,10 +294,10 @@ export class PointerManager { } private _firePointerUpAndClick(rayCastEntity: Entity): void { - const { _currentPressedEntity } = this; - if (_currentPressedEntity) { - const sameTarget = _currentPressedEntity === rayCastEntity; - const scripts = _currentPressedEntity._scripts; + const { _currentPressedEntity: pressedEntity } = this; + if (pressedEntity) { + const sameTarget = pressedEntity === rayCastEntity; + const scripts = pressedEntity._scripts; for (let i = scripts.length - 1; i >= 0; i--) { const script = scripts.get(i); sameTarget && script.onPointerClick(); From 0f37299f2127c27b91288ac44db2274b8a7bbe2f Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Fri, 8 Oct 2021 18:25:13 +0800 Subject: [PATCH 66/72] fix:InputManager modify timing --- packages/core/src/input/pointer/PointerManager.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/core/src/input/pointer/PointerManager.ts b/packages/core/src/input/pointer/PointerManager.ts index dc9529f2a2..0fc418e558 100644 --- a/packages/core/src/input/pointer/PointerManager.ts +++ b/packages/core/src/input/pointer/PointerManager.ts @@ -211,6 +211,7 @@ export class PointerManager { // @ts-ignore const pixelRatioHeight = canvas.height / (canvas._webCanvas as HTMLCanvasElement).clientHeight; if (activePointerCount === 0) { + // Get the pointer coordinates when leaving, and use it to correctly dispatch the click event. const lastNativeEvent = nativeEvents[nativeEventsLen - 1]; currentPosition.setValue(lastNativeEvent.offsetX * pixelRatioWidth, lastNativeEvent.offsetY * pixelRatioHeight); } else { @@ -232,10 +233,10 @@ export class PointerManager { private _pointerRayCast(): Entity { if (this._pointers.length > 0) { - let x = this._currentPosition.x / this._canvas.width; - let y = this._currentPosition.y / this._canvas.height; - const cameras = this._engine.sceneManager.activeScene._activeCameras; - const { _tempPoint, _tempRay, _tempHitResult } = PointerManager; + const { _tempPoint: point, _tempRay: ray, _tempHitResult: hitResult } = PointerManager; + const { _activeCameras: cameras } = this._engine.sceneManager.activeScene; + const x = this._currentPosition.x / this._canvas.width; + const y = this._currentPosition.y / this._canvas.height; for (let i = cameras.length - 1; i >= 0; i--) { const camera = cameras[i]; if (!camera.enabled || camera.renderTarget) { @@ -243,10 +244,10 @@ export class PointerManager { } const { x: vpX, y: vpY, z: vpW, w: vpH } = camera.viewport; if (x >= vpX && y >= vpY && x - vpX <= vpW && y - vpY <= vpH) { - PointerManager._tempPoint.setValue((x - vpX) / vpW, (y - vpY) / vpH); + point.setValue((x - vpX) / vpW, (y - vpY) / vpH); // TODO: Only check which colliders have listened to the input. - if (this._engine.physicsManager.raycast(camera.viewportPointToRay(_tempPoint, _tempRay), _tempHitResult)) { - return PointerManager._tempHitResult.entity; + if (this._engine.physicsManager.raycast(camera.viewportPointToRay(point, ray), hitResult)) { + return hitResult.entity; } else if (camera.clearFlags === CameraClearFlags.DepthColor) { return null; } From 3d4a8b7200c14c62e5edf86014e83c918a01ca0a Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Sun, 10 Oct 2021 19:42:30 +0800 Subject: [PATCH 67/72] fix:Sprite add clone function --- packages/core/src/2d/atlas/SpriteAtlas.ts | 8 +++---- packages/core/src/2d/sprite/Sprite.ts | 29 +++++++++++++++++++++-- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/packages/core/src/2d/atlas/SpriteAtlas.ts b/packages/core/src/2d/atlas/SpriteAtlas.ts index 6410da4126..a23aad0292 100644 --- a/packages/core/src/2d/atlas/SpriteAtlas.ts +++ b/packages/core/src/2d/atlas/SpriteAtlas.ts @@ -19,21 +19,21 @@ export class SpriteAtlas extends RefObject { /** * Get the last sprite named 'name' from the atlas. * @param name - The name of the sprite you want to find - * @returns The sprite you want to find + * @returns The sprite you want to find (clone) */ getSprite(name: string): Sprite { const sprite = this._sprites[this._spriteNamesToIndex[name]]; if (!sprite) { console.warn("There is no sprite named " + name + " in the atlas."); } - return sprite; + return sprite.clone(); } /** * Get all the sprite named 'name' from the atlas. * @param name - The name of the sprites you want to find * @param outSprites - This array holds the sprites found - * @returns The sprites you want to find + * @returns The sprites you want to find (clone) */ getSprites(name: string, outSprites: Sprite[]): Sprite[] { outSprites.length = 0; @@ -42,7 +42,7 @@ export class SpriteAtlas extends RefObject { const { _sprites } = this; for (; i >= 0; i--) { const sprite = _sprites[i]; - sprite.name === name && outSprites.push(sprite); + sprite.name === name && outSprites.push(sprite.clone()); } } else { console.warn("The name of the sprite you want to find is not exit in SpriteAtlas."); diff --git a/packages/core/src/2d/sprite/Sprite.ts b/packages/core/src/2d/sprite/Sprite.ts index caf8f87a9d..d6052b7207 100644 --- a/packages/core/src/2d/sprite/Sprite.ts +++ b/packages/core/src/2d/sprite/Sprite.ts @@ -108,8 +108,13 @@ export class Sprite extends RefObject { } set pivot(value: Vector2) { - this._pivot.setValue(MathUtil.clamp(value.x, 0, 1), MathUtil.clamp(value.y, 0, 1)); - this._setDirtyFlagTrue(DirtyFlag.positions); + const pivot = this._pivot; + const x = MathUtil.clamp(value.x, 0, 1); + const y = MathUtil.clamp(value.y, 0, 1); + if (pivot === value || pivot.x !== x || pivot.y !== y) { + pivot.setValue(x, y); + this._setDirtyFlagTrue(DirtyFlag.positions); + } } /** @@ -141,6 +146,26 @@ export class Sprite extends RefObject { } } + /** + * Clone. + * @returns Cloned sprite. + */ + clone(): Sprite { + const cloneSprite = new Sprite( + this._engine, + this._texture, + this._region, + this._pivot, + this._pixelsPerUnit, + this.name + ); + cloneSprite._assetID = this._assetID; + cloneSprite._atlasRotated = this._atlasRotated; + this._atlasRegion.cloneTo(cloneSprite._atlasRegion); + this._atlasRegionOffset.cloneTo(cloneSprite._atlasRegionOffset); + return cloneSprite; + } + /** * Constructor a Sprite. * @param engine - Engine to which the sprite belongs From 8332a54396263652dadef99758a552e8660254c7 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Sun, 10 Oct 2021 20:01:25 +0800 Subject: [PATCH 68/72] fix:Sprite add clone function --- packages/core/src/2d/sprite/Sprite.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/2d/sprite/Sprite.ts b/packages/core/src/2d/sprite/Sprite.ts index d6052b7207..1454680535 100644 --- a/packages/core/src/2d/sprite/Sprite.ts +++ b/packages/core/src/2d/sprite/Sprite.ts @@ -148,7 +148,7 @@ export class Sprite extends RefObject { /** * Clone. - * @returns Cloned sprite. + * @returns Cloned sprite */ clone(): Sprite { const cloneSprite = new Sprite( From 466beab24fc37cfe043eff9dc4d9c82502074727 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Mon, 25 Oct 2021 12:36:11 +0800 Subject: [PATCH 69/72] fix:Sprite add clone function --- packages/core/src/2d/atlas/SpriteAtlas.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/core/src/2d/atlas/SpriteAtlas.ts b/packages/core/src/2d/atlas/SpriteAtlas.ts index a23aad0292..6410da4126 100644 --- a/packages/core/src/2d/atlas/SpriteAtlas.ts +++ b/packages/core/src/2d/atlas/SpriteAtlas.ts @@ -19,21 +19,21 @@ export class SpriteAtlas extends RefObject { /** * Get the last sprite named 'name' from the atlas. * @param name - The name of the sprite you want to find - * @returns The sprite you want to find (clone) + * @returns The sprite you want to find */ getSprite(name: string): Sprite { const sprite = this._sprites[this._spriteNamesToIndex[name]]; if (!sprite) { console.warn("There is no sprite named " + name + " in the atlas."); } - return sprite.clone(); + return sprite; } /** * Get all the sprite named 'name' from the atlas. * @param name - The name of the sprites you want to find * @param outSprites - This array holds the sprites found - * @returns The sprites you want to find (clone) + * @returns The sprites you want to find */ getSprites(name: string, outSprites: Sprite[]): Sprite[] { outSprites.length = 0; @@ -42,7 +42,7 @@ export class SpriteAtlas extends RefObject { const { _sprites } = this; for (; i >= 0; i--) { const sprite = _sprites[i]; - sprite.name === name && outSprites.push(sprite.clone()); + sprite.name === name && outSprites.push(sprite); } } else { console.warn("The name of the sprite you want to find is not exit in SpriteAtlas."); From d6371f2fac29109933bdcc7b9272e91ff34c3bd3 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Wed, 10 Nov 2021 16:23:30 +0800 Subject: [PATCH 70/72] fix: the compatibility problem of IOS lower version --- packages/core/src/input/pointer/PointerManager.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core/src/input/pointer/PointerManager.ts b/packages/core/src/input/pointer/PointerManager.ts index 0fc418e558..47beea24e2 100644 --- a/packages/core/src/input/pointer/PointerManager.ts +++ b/packages/core/src/input/pointer/PointerManager.ts @@ -49,7 +49,8 @@ export class PointerManager { this._nativeEvents.push(evt); }; // MaxTouchCount + MouseCount(1) - this._pointerPool = new Array(navigator.maxTouchPoints + 1); + // this._pointerPool = new Array(navigator.maxTouchPoints + 1); + this._pointerPool = new Array(11); this._enablePhysics = engine.physicsManager ? true : false; } From 95bbd51a9abe4d915429949c881330f9564fa911 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Wed, 10 Nov 2021 16:28:33 +0800 Subject: [PATCH 71/72] fix: the compatibility problem of IOS lower version --- packages/core/src/input/pointer/PointerManager.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/core/src/input/pointer/PointerManager.ts b/packages/core/src/input/pointer/PointerManager.ts index 47beea24e2..0def097e52 100644 --- a/packages/core/src/input/pointer/PointerManager.ts +++ b/packages/core/src/input/pointer/PointerManager.ts @@ -48,8 +48,7 @@ export class PointerManager { htmlCanvas.onpointerdown = htmlCanvas.onpointerup = htmlCanvas.onpointerout = htmlCanvas.onpointermove = (evt:PointerEvent)=>{ this._nativeEvents.push(evt); }; - // MaxTouchCount + MouseCount(1) - // this._pointerPool = new Array(navigator.maxTouchPoints + 1); + // If there are no compatibility issues, navigator.maxTouchPoints should be used here. this._pointerPool = new Array(11); this._enablePhysics = engine.physicsManager ? true : false; } From 054aa103c2edcf46ba368a32d860b2e94d8edf56 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Tue, 16 Nov 2021 18:03:13 +0800 Subject: [PATCH 72/72] fix: the bug that inputmanager cannot be obtained --- packages/core/src/Engine.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/core/src/Engine.ts b/packages/core/src/Engine.ts index f16715bc26..4477224407 100644 --- a/packages/core/src/Engine.ts +++ b/packages/core/src/Engine.ts @@ -41,6 +41,7 @@ ShaderPool.init(); export class Engine extends EventDispatcher { /** Physics manager of Engine. */ readonly physicsManager: PhysicsManager; + readonly inputManager: InputManager; _componentsManager: ComponentsManager = new ComponentsManager(); _hardwareRenderer: IHardwareRenderer; @@ -66,8 +67,6 @@ export class Engine extends EventDispatcher { _shaderProgramPools: ShaderProgramPool[] = []; /** @internal */ _spriteMaskManager: SpriteMaskManager; - /** @internal */ - _inputManager: InputManager; protected _canvas: Canvas; private _resourceManager: ResourceManager = new ResourceManager(this); @@ -180,7 +179,7 @@ export class Engine extends EventDispatcher { this._spriteDefaultMaterial = this._createSpriteMaterial(); this._spriteMaskDefaultMaterial = this._createSpriteMaskMaterial(); - this._inputManager = new InputManager(this); + this.inputManager = new InputManager(this); const whitePixel = new Uint8Array([255, 255, 255, 255]); @@ -261,7 +260,7 @@ export class Engine extends EventDispatcher { this.physicsManager._update(deltaTime); componentsManager.callColliderOnLateUpdate(); } - this._inputManager._update(); + this.inputManager._update(); componentsManager.callScriptOnUpdate(deltaTime); componentsManager.callAnimationUpdate(deltaTime); componentsManager.callScriptOnLateUpdate(deltaTime); @@ -291,7 +290,7 @@ export class Engine extends EventDispatcher { if (this._sceneManager) { this._whiteTexture2D.destroy(true); this._whiteTextureCube.destroy(true); - this._inputManager._destroy(); + this.inputManager._destroy(); this.trigger(new Event("shutdown", this)); engineFeatureManager.callFeatureMethod(this, "shutdown", [this]);