Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SpriteRenderer drawMode property(support simple and sliced) #828

Merged
merged 25 commits into from
Jul 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions packages/core/src/2d/assembler/IAssembler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Renderer } from "../../Renderer";

export interface IAssembler {
resetData(renderer: Renderer): void;
updateData(renderer: Renderer): void;
updatePositions(renderer: Renderer): void;
updateUVs(renderer: Renderer): void;
}
77 changes: 77 additions & 0 deletions packages/core/src/2d/assembler/SimpleSpriteAssembler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { BoundingBox, Matrix, Vector2, Vector3 } from "@oasis-engine/math";
import { SpriteMask } from "../sprite";
import { SpriteRenderer } from "../sprite/SpriteRenderer";
import { IAssembler } from "./IAssembler";
import { StaticInterfaceImplement } from "./StaticInterfaceImplement";

@StaticInterfaceImplement<IAssembler>()
/**
* @internal
*/
export class SimpleSpriteAssembler {
static _rectangleTriangles: number[] = [0, 1, 2, 2, 1, 3];
static _worldMatrix: Matrix = new Matrix();

static resetData(renderer: SpriteRenderer | SpriteMask): void {
const { _renderData: renderData } = renderer;
const vertexCount = (renderData.vertexCount = 4);
const { positions, uvs } = renderData;
if (positions.length < vertexCount) {
for (let i = positions.length; i < vertexCount; i++) {
positions.push(new Vector3());
uvs.push(new Vector2());
}
}
renderData.triangles = SimpleSpriteAssembler._rectangleTriangles;
}

static updateData(renderer: SpriteRenderer | SpriteMask): void {}

static updatePositions(renderer: SpriteRenderer | SpriteMask): void {
const { width, height } = renderer;
if (width === 0 || height === 0) {
return;
}
const { sprite } = renderer;
const { x: pivotX, y: pivotY } = sprite.pivot;
// Renderer's worldMatrix;
const { _worldMatrix: worldMatrix } = SimpleSpriteAssembler;
const { elements: wE } = worldMatrix;
// Parent's worldMatrix.
const { elements: pWE } = renderer.entity.transform.worldMatrix;
const sx = renderer.flipX ? -width : width;
const sy = renderer.flipY ? -height : height;
(wE[0] = pWE[0] * sx), (wE[1] = pWE[1] * sx), (wE[2] = pWE[2] * sx);
(wE[4] = pWE[4] * sy), (wE[5] = pWE[5] * sy), (wE[6] = pWE[6] * sy);
(wE[8] = pWE[8]), (wE[9] = pWE[9]), (wE[10] = pWE[10]);
wE[12] = pWE[12] - pivotX * wE[0] - pivotY * wE[4];
wE[13] = pWE[13] - pivotX * wE[1] - pivotY * wE[5];
wE[14] = pWE[14];

// ---------------
// 2 - 3
// | |
// 0 - 1
// ---------------
// Update positions.
const spritePositions = sprite._getPositions();
const { positions } = renderer._renderData;
for (let i = 0; i < 4; i++) {
const { x, y } = spritePositions[i];
positions[i].set(wE[0] * x + wE[4] * y + wE[12], wE[1] * x + wE[5] * y + wE[13], wE[2] * x + wE[6] * y + wE[14]);
}

BoundingBox.transform(sprite._getBounds(), worldMatrix, renderer._bounds);
}

static updateUVs(renderer: SpriteRenderer | SpriteMask): void {
const spriteUVs = renderer.sprite._getUVs();
const renderUVs = renderer._renderData.uvs;
const { x: left, y: bottom } = spriteUVs[0];
const { x: right, y: top } = spriteUVs[3];
renderUVs[0].set(left, bottom);
renderUVs[1].set(right, bottom);
renderUVs[2].set(left, top);
renderUVs[3].set(right, top);
}
}
151 changes: 151 additions & 0 deletions packages/core/src/2d/assembler/SlicedSpriteAssembler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { Matrix, Vector2, Vector3 } from "@oasis-engine/math";
import { SpriteMask } from "../sprite";
import { SpriteRenderer } from "../sprite/SpriteRenderer";
import { IAssembler } from "./IAssembler";
import { StaticInterfaceImplement } from "./StaticInterfaceImplement";

@StaticInterfaceImplement<IAssembler>()
/**
* @internal
*/
export class SlicedSpriteAssembler {
static _worldMatrix: Matrix = new Matrix();
static resetData(renderer: SpriteRenderer | SpriteMask): void {
const { _renderData: renderData } = renderer;
const { positions, uvs } = renderData;
if (positions.length < 16) {
for (let i = positions.length; i < 16; i++) {
positions.push(new Vector3());
uvs.push(new Vector2());
}
}
renderData.triangles = [];
}

static updateData(renderer: SpriteRenderer | SpriteMask): void {}

static updatePositions(renderer: SpriteRenderer | SpriteMask): void {
const { width, height } = renderer;
if (width === 0 || height === 0) {
return;
}
const { sprite } = renderer;
const { positions, uvs, triangles } = renderer._renderData;
const { border } = sprite;
const spriteUVs = sprite._getUVs();
// Update local positions.
const spritePositions = sprite._getPositions();
const { x: left, y: bottom } = spritePositions[0];
const { x: right, y: top } = spritePositions[3];
const { width: expectWidth, height: expectHeight } = sprite;
const fixedLeft = expectWidth * border.x;
const fixedBottom = expectHeight * border.y;
const fixedRight = expectHeight * border.z;
const fixedTop = expectWidth * border.w;

// ------------------------
// [3]
// |
// [2]
// |
// [1]
// |
// row [0] - [1] - [2] - [3]
// column
// ------------------------
// Calculate row and column.
let row: number[], column: number[];
if (fixedLeft + fixedRight > width) {
const widthScale = width / (fixedLeft + fixedRight);
row = [
expectWidth * left * widthScale,
fixedLeft * widthScale,
fixedLeft * widthScale,
width - expectWidth * (1 - right) * widthScale
];
} else {
row = [expectWidth * left, fixedLeft, width - fixedRight, width - expectWidth * (1 - right)];
}

if (fixedTop + fixedBottom > height) {
const heightScale = height / (fixedTop + fixedBottom);
column = [
expectHeight * bottom * heightScale,
fixedBottom * heightScale,
fixedBottom * heightScale,
height - expectHeight * (1 - top) * heightScale
];
} else {
column = [expectHeight * bottom, fixedBottom, height - fixedTop, height - expectHeight * (1 - top)];
}

// Update renderer's worldMatrix.
const { x: pivotX, y: pivotY } = renderer.sprite.pivot;
const localTransX = renderer.width * pivotX;
const localTransY = renderer.height * pivotY;
// Renderer's worldMatrix.
const { _worldMatrix: worldMatrix } = SlicedSpriteAssembler;
const { elements: wE } = worldMatrix;
// Parent's worldMatrix.
const { elements: pWE } = renderer.entity.transform.worldMatrix;
const sx = renderer.flipX ? -1 : 1;
const sy = renderer.flipY ? -1 : 1;
(wE[0] = pWE[0] * sx), (wE[1] = pWE[1] * sx), (wE[2] = pWE[2] * sx);
(wE[4] = pWE[4] * sy), (wE[5] = pWE[5] * sy), (wE[6] = pWE[6] * sy);
(wE[8] = pWE[8]), (wE[9] = pWE[9]), (wE[10] = pWE[10]);
wE[12] = pWE[12] - localTransX * wE[0] - localTransY * wE[4];
wE[13] = pWE[13] - localTransX * wE[1] - localTransY * wE[5];
wE[14] = pWE[14];

// ------------------------
// 3 - 7 - 11 - 15
// | | | |
// 2 - 6 - 10 - 14
// | | | |
// 1 - 5 - 9 - 13
// | | | |
// 0 - 4 - 8 - 12
// ------------------------
// Assemble position and uv.
let vertexCount = 0;
let realICount = 0;
for (let i = 0; i < 4; i++) {
const rowValue = row[i];
const rowU = spriteUVs[i].x;
for (let j = 0; j < 4; j++) {
const columnValue = column[j];
positions[vertexCount].set(
wE[0] * rowValue + wE[4] * columnValue + wE[12],
wE[1] * rowValue + wE[5] * columnValue + wE[13],
wE[2] * rowValue + wE[6] * columnValue + wE[14]
);
uvs[vertexCount].set(rowU, spriteUVs[j].y);
++vertexCount;
}
++realICount;
}

const realJCount = vertexCount / realICount;
let indexOffset = 0;
for (let i = 0; i < realICount - 1; ++i) {
for (let j = 0; j < realJCount - 1; ++j) {
const start = i * realJCount + j;
triangles[indexOffset++] = start;
triangles[indexOffset++] = start + 1;
triangles[indexOffset++] = start + realJCount;
triangles[indexOffset++] = start + 1;
triangles[indexOffset++] = start + realJCount + 1;
triangles[indexOffset++] = start + realJCount;
}
}
renderer._renderData.vertexCount = realICount * realJCount;
triangles.length = (realICount - 1) * (realJCount - 1) * 6;

const { min, max } = renderer._bounds;
min.set(row[0], column[0], 0);
max.set(row[3], column[3], 0);
renderer._bounds.transform(worldMatrix);
}

static updateUVs(renderer: SpriteRenderer | SpriteMask): void {}
}
10 changes: 10 additions & 0 deletions packages/core/src/2d/assembler/StaticInterfaceImplement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* Static interface implement decorator.
* https://stackoverflow.com/questions/13955157/how-to-define-static-property-in-typescript-interface
*/
export function StaticInterfaceImplement<T>() {
return <U extends T>(constructor: U) => {
constructor;
};
}

14 changes: 14 additions & 0 deletions packages/core/src/2d/data/RenderData2D.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Color, Vector2, Vector3 } from "@oasis-engine/math";

/**
* @internal
*/
export class RenderData2D {
constructor(
public vertexCount: number,
public positions: Vector3[],
public uvs: Vector2[],
public triangles: number[] = null,
public color: Color = null
) {}
}
13 changes: 13 additions & 0 deletions packages/core/src/2d/enums/SpriteDirtyFlag.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* Sprite Property Dirty Flag.
*/
export enum SpritePropertyDirtyFlag {
texture = 0x1,
size = 0x2,
atlasRotate = 0x4,
atlasRegion = 0x8,
atlasRegionOffset = 0x10,
region = 0x20,
pivot = 0x40,
border = 0x80
}
9 changes: 9 additions & 0 deletions packages/core/src/2d/enums/SpriteDrawMode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Sprite's drawing mode enumeration.
*/
export enum SpriteDrawMode {
/** Overall scaling when modifying size. */
Simple,
/** When modifying the size, it is transformed according to the 9-slice settings (border). */
Sliced
}
1 change: 1 addition & 0 deletions packages/core/src/2d/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ export { TextHorizontalAlignment, TextVerticalAlignment } from "./enums/TextAlig
export { OverflowMode } from "./enums/TextOverflow";
export { FontStyle } from "./enums/FontStyle";
export { SpriteAtlas } from "./atlas/SpriteAtlas";
export { SpriteDrawMode } from "./enums/SpriteDrawMode";
export * from "./sprite/index";
export * from "./text/index";
Loading