Skip to content

Commit

Permalink
feat:Support atlas #197 (#402)
Browse files Browse the repository at this point in the history
* feat:Support atlas #197

Co-authored-by: azhan <zhanyingwei.zyw@antgroup.com>
Co-authored-by: GuoLei1990 <gl3336563@163.com>
  • Loading branch information
3 people authored Aug 2, 2021
1 parent 3490059 commit d9f1b9a
Show file tree
Hide file tree
Showing 9 changed files with 266 additions and 79 deletions.
75 changes: 75 additions & 0 deletions packages/core/src/2d/atlas/SpriteAtlas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { RefObject } from "../../asset/RefObject";
import { Engine } from "../../Engine";
import { Sprite } from "../sprite/Sprite";

/**
* Sprite Atlas.
*/
export class SpriteAtlas extends RefObject {
private _sprites: Sprite[] = new Array<Sprite>();
private _spriteNamesToIndex: Record<string, number> = {};

/**
* All the sprites in the atlas.
*/
get sprites(): Readonly<Sprite[]> {
return this._sprites;
}

/**
* 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
*/
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;
}

/**
* 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
*/
getSprites(name: string, outSprites: Sprite[]): Sprite[] {
outSprites.length = 0;
let i = this._spriteNamesToIndex[name];
if (i !== undefined) {
const { _sprites } = this;
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 not exit in SpriteAtlas.");
}
return outSprites;
}

/**
* Constructor a SpriteAtlas.
* @param engine - Engine to which the SpriteAtlas belongs
*/
constructor(engine: Engine) {
super(engine);
}

/**
* @internal
*/
_addSprite(sprite: Sprite): void {
this._spriteNamesToIndex[sprite.name] = this._sprites.push(sprite) - 1;
}

/**
* @override
*/
_onDestroy(): void {
this._sprites = null;
this._spriteNamesToIndex = null;
}
}
33 changes: 33 additions & 0 deletions packages/core/src/2d/atlas/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { TextureFormat } from "../../texture";

/**
* The original data type of the atlas.
*/
export interface AtlasConfig {
/** Version of Atlas. */
version: number;
/** Texture format. */
format: TextureFormat;
/** The sub atlas array, each sub atlas contains multiple sprites. */
atlasItems: {
/** The url of the sub atlas. */
img: string;
/** Sprites contained in the sub atlas. */
sprites: AtlasSprite[];
}[];
}

/**
* The original data type of each sprite.
*/
export interface AtlasSprite {
/** The name the sprite. */
name: string;
/** 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;
}
1 change: 1 addition & 0 deletions packages/core/src/2d/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { SpriteMaskInteraction } from "./enums/SpriteMaskInteraction";
export { SpriteMaskLayer } from "./enums/SpriteMaskLayer";
export { SpriteAtlas } from "./atlas/SpriteAtlas";
export * from "./sprite/index";
142 changes: 66 additions & 76 deletions packages/core/src/2d/sprite/Sprite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,27 @@ import { Texture2D } from "../../texture/Texture2D";
* 2D sprite.
*/
export class Sprite extends RefObject {
/** @internal */
_positions: Vector2[] = [new Vector2(), new Vector2(), new Vector2(), new Vector2()];
private static _rectangleTriangles: number[] = [0, 2, 1, 2, 0, 3];

/** The name of sprite. */
name: string;

/** @internal */
_uv: Vector2[] = [new Vector2(), new Vector2(), new Vector2(), new Vector2()];
/** @internal */
_triangles: number[] = [];

_positions: Vector2[] = [new Vector2(), new Vector2(), new Vector2(), new Vector2()];
/** @internal */
_bounds: BoundingBox = new BoundingBox();
/** @internal */
_triangles: number[];

private _pixelsPerUnit: number;
private _texture: Texture2D = null;
private _atlasRegion: Rect = new Rect(0, 0, 1, 1);
private _atlasRegionOffset: Vector2 = new Vector2(0, 0);
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;
private _atlasRegion: Rect = new Rect(0, 0, 1, 1);
private _atlasRegionOffset: Vector2 = new Vector2(0, 0);
private _dirtyFlag: DirtyFlag = DirtyFlag.all;

/**
* The reference to the used texture.
Expand Down Expand Up @@ -66,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);
}

/**
Expand All @@ -77,6 +82,7 @@ export class Sprite extends RefObject {

set atlasRegionOffset(value: Vector2) {
this._atlasRegionOffset.setValue(MathUtil.clamp(value.x, 0, 1), MathUtil.clamp(value.y, 0, 1));
this._setDirtyFlagTrue(DirtyFlag.positions);
}

/**
Expand Down Expand Up @@ -124,36 +130,32 @@ 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,
texture: Texture2D = null,
region: Rect = null,
pivot: Vector2 = null,
pixelsPerUnit: number = 128
pixelsPerUnit: number = 128,
name: string = null
) {
super(engine);

if (texture) {
this.texture = texture;
}
this.name = name;
this._texture = texture;
this._pixelsPerUnit = pixelsPerUnit;

if (region) {
this.region = region;
this.atlasRegion = region;
}
region && region.cloneTo(this._region);
pivot && pivot.cloneTo(this._pivot);

if (pivot) {
this.pivot = pivot;
}

this.pixelsPerUnit = pixelsPerUnit;
this._triangles = Sprite._rectangleTriangles;
}

/**
Expand All @@ -169,44 +171,40 @@ export class Sprite extends RefObject {
* Update positions and bounds.
*/
private _updatePositionsAndBounds(): void {
const { texture } = this;
let lx = 0;
let ty = 0;
let rx = 0;
let by = 0;

const { texture, _bounds: bounds } = this;
if (texture) {
const { width, height } = texture;
const { width: rWidth, height: rHeight } = this.region;
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 = rWidth * width * pixelsPerUnitReciprocal;
const unitHeight = rHeight * 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;
lx = -px * unitWidth;
ty = -py * unitHeight;
rx = (1 - px) * unitWidth;
by = (1 - py) * unitHeight;
}
const lx = (-pivot.x + atlasRegionOffset.x) * unitWidth;
const ty = (-pivot.y + 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.
const { min, max } = this._bounds;
min.setValue(lx, ty, 0);
max.setValue(rx, by, 0);
// 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.
bounds.min.setValue(lx, ty, 0);
bounds.max.setValue(rx, by, 0);
} else {
// Update bounds.
bounds.min.setValue(0, 0, 0);
bounds.max.setValue(0, 0, 0);
}
}

/**
Expand All @@ -218,29 +216,22 @@ export class Sprite extends RefObject {
}

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 { _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 right = left + realWidth;
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);
}

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;
uv[3].setValue(left, bottom);
}
}

Expand Down Expand Up @@ -274,6 +265,5 @@ export class Sprite extends RefObject {
enum DirtyFlag {
positions = 0x1,
uv = 0x2,
triangles = 0x4,
all = 0x7
all = 0x3
}
4 changes: 3 additions & 1 deletion packages/core/src/asset/AssetType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
2 changes: 1 addition & 1 deletion packages/loader/src/GLTFLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { AssetPromise, AssetType, Loader, LoadItem, resourceLoader, ResourceMana
import { GLTFParser } from "./gltf/GLTFParser";
import { GLTFResource } from "./gltf/GLTFResource";

@resourceLoader(AssetType.Perfab, ["gltf", "glb"])
@resourceLoader(AssetType.Prefab, ["gltf", "glb"])
export class GLTFLoader extends Loader<GLTFResource> {
load(item: LoadItem, resourceManager: ResourceManager): AssetPromise<GLTFResource> {
const url = item.url;
Expand Down
Loading

0 comments on commit d9f1b9a

Please sign in to comment.