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

feat: Implement Sprite Tint #2326

Merged
merged 6 commits into from
Jun 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ This project adheres to [Semantic Versioning](http://semver.org/).
-

### Added

- Allow tinting of `ex.Sprite`'s by setting a new `tint` property, renderers must support the tint property in order to function.
```typescript
const imageSource = new ex.ImageSource('./path/to/image.png');
await imageSource.load();
const sprite = imageSource.toSprite();
sprite.tint = ex.Color.Red;
```
- Added `ex.Sound.getPlaybackPosition()` which returns the current playback position in seconds of the currently playing sound.
- Added `ex.Sound.playbackRate` which allows developers to get/set the current rate of playback. 1.0 is the default playback rate, 2.0 is twice the speed, and 0.5 is half speed.
- Added missing `ex.EaseBy` action type, uses `ex.EasingFunctions` to move relative from the current entity position.
Expand Down
1 change: 1 addition & 0 deletions sandbox/src/game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,7 @@ var blockSprite = new ex.Sprite({
height: 49
}
});
blockSprite.tint = ex.Color.Blue;
otherPointer.get(ex.TransformComponent).z = 100;
otherPointer.graphics.use(blockSprite);
// Create spritesheet
Expand Down
35 changes: 34 additions & 1 deletion sandbox/tests/parallel/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,40 @@ var actor = new ex.Actor({
height: 50
});
actor.graphics.use(image.toSprite());
game.add(actor);
// game.add(actor);


class MyActor extends ex.Actor {
constructor() {
super({
x: 50,
y: 50,
width: 100,
height: 100,
color: ex.Color.Green
});
}

async takeDamage(): Promise<void> {
const knockBackSequence = new ex.ActionSequence(this, ctx => {
ctx.moveBy(ex.vec(100, 0), 500)
ctx.moveBy(ex.vec(-100, 0), 500)
});

const fadeSequence = new ex.ActionSequence(this, ctx => {
ctx.delay(100);
ctx.fade(0, 1000);
});
const parallel = new ex.ParallelActions([knockBackSequence, fadeSequence]);
// oops runAction() doesn't return the ActionContext will fix soon
this.actions.runAction(parallel);
await this.actions.toPromise();
}
}

var myActor = new MyActor();
game.add(myActor);
myActor.takeDamage();


var sequence1 = new ex.ActionSequence(actor, ctx => {
Expand Down
6 changes: 6 additions & 0 deletions src/engine/Graphics/Context/ExcaliburGraphicsContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface ExcaliburGraphicsContextOptions {
export interface ExcaliburGraphicsContextState {
opacity: number;
z: number;
tint: Color;
}
export interface LineGraphicsOptions {
color: Color;
Expand Down Expand Up @@ -106,6 +107,11 @@ export interface ExcaliburGraphicsContext {
*/
opacity: number;

/**
* Sets the tint color to be multiplied by any images drawn, default is black 0xFFFFFFFF
*/
tint: Color;

/**
* Resets the current transform to the identity matrix
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,14 @@ export class ExcaliburGraphicsContext2DCanvas implements ExcaliburGraphicsContex
this._state.current.opacity = value;
}

public get tint(): Color {
return this._state.current.tint;
}

public set tint(color: Color) {
this._state.current.tint = color;
}

public snapToPixel: boolean = true;

public get smoothing(): boolean {
Expand Down
9 changes: 9 additions & 0 deletions src/engine/Graphics/Context/ExcaliburGraphicsContextWebGL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,14 @@ export class ExcaliburGraphicsContextWebGL implements ExcaliburGraphicsContext {
this._state.current.opacity = value;
}

public get tint(): Color {
return this._state.current.tint;
}

public set tint(color: Color) {
this._state.current.tint = color;
}

public get width() {
return this.__gl.canvas.width;
}
Expand Down Expand Up @@ -281,6 +289,7 @@ export class ExcaliburGraphicsContextWebGL implements ExcaliburGraphicsContext {
this.getTransform().clone(drawCall.transform);
drawCall.state.z = this._state.current.z;
drawCall.state.opacity = this._state.current.opacity;
drawCall.state.tint = this._state.current.tint;
drawCall.args = args;
this._drawCalls.push(drawCall);
} else {
Expand Down
4 changes: 3 additions & 1 deletion src/engine/Graphics/Context/draw-call.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Color } from '../../Color';
import { Matrix } from '../../Math/matrix';
import { ExcaliburGraphicsContextState } from './ExcaliburGraphicsContext';

Expand All @@ -8,7 +9,8 @@ export class DrawCall {
public transform: Matrix = Matrix.identity();
public state: ExcaliburGraphicsContextState = {
z: 0,
opacity: 1
opacity: 1,
tint: Color.White
};
public args: any[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ uniform sampler2D u_textures[%%count%%];
// Opacity
in float v_opacity;

in vec4 v_tint;

out vec4 fragColor;

void main() {
Expand All @@ -27,5 +29,5 @@ void main() {

color.rgb = color.rgb * v_opacity;
color.a = color.a * v_opacity;
fragColor = color;
fragColor = color * v_tint;
}
25 changes: 22 additions & 3 deletions src/engine/Graphics/Context/image-renderer/image-renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export class ImageRenderer implements RendererPlugin {

// Setup memory layout
this._buffer = new VertexBuffer({
size: 6 * 4 * this._maxImages, // 6 components * 4 verts
size: 10 * 4 * this._maxImages, // 10 components * 4 verts
type: 'dynamic'
});
this._layout = new VertexLayout({
Expand All @@ -66,7 +66,8 @@ export class ImageRenderer implements RendererPlugin {
['a_position', 2],
['a_opacity', 1],
['a_texcoord', 2],
['a_textureIndex', 1]
['a_textureIndex', 1],
['a_tint', 4]
]
});

Expand Down Expand Up @@ -187,6 +188,8 @@ export class ImageRenderer implements RendererPlugin {
bottomRight.y = ~~bottomRight.y;
}

const tint = this._context.tint;

const textureId = this._getTextureIdForImage(image);
const potWidth = ensurePowerOfTwo(image.width || width);
const potHeight = ensurePowerOfTwo(image.height || height);
Expand All @@ -206,6 +209,10 @@ export class ImageRenderer implements RendererPlugin {
vertexBuffer[this._vertexIndex++] = uvx0;
vertexBuffer[this._vertexIndex++] = uvy0;
vertexBuffer[this._vertexIndex++] = textureId;
vertexBuffer[this._vertexIndex++] = tint.r / 255;
vertexBuffer[this._vertexIndex++] = tint.g / 255;
vertexBuffer[this._vertexIndex++] = tint.b / 255;
vertexBuffer[this._vertexIndex++] = tint.a;

// (0, 1) - 1
vertexBuffer[this._vertexIndex++] = bottomLeft.x;
Expand All @@ -214,6 +221,10 @@ export class ImageRenderer implements RendererPlugin {
vertexBuffer[this._vertexIndex++] = uvx0;
vertexBuffer[this._vertexIndex++] = uvy1;
vertexBuffer[this._vertexIndex++] = textureId;
vertexBuffer[this._vertexIndex++] = tint.r / 255;
vertexBuffer[this._vertexIndex++] = tint.g / 255;
vertexBuffer[this._vertexIndex++] = tint.b / 255;
vertexBuffer[this._vertexIndex++] = tint.a;

// (1, 0) - 2
vertexBuffer[this._vertexIndex++] = topRight.x;
Expand All @@ -222,6 +233,10 @@ export class ImageRenderer implements RendererPlugin {
vertexBuffer[this._vertexIndex++] = uvx1;
vertexBuffer[this._vertexIndex++] = uvy0;
vertexBuffer[this._vertexIndex++] = textureId;
vertexBuffer[this._vertexIndex++] = tint.r / 255;
vertexBuffer[this._vertexIndex++] = tint.g / 255;
vertexBuffer[this._vertexIndex++] = tint.b / 255;
vertexBuffer[this._vertexIndex++] = tint.a;

// (1, 1) - 3
vertexBuffer[this._vertexIndex++] = bottomRight.x;
Expand All @@ -230,6 +245,10 @@ export class ImageRenderer implements RendererPlugin {
vertexBuffer[this._vertexIndex++] = uvx1;
vertexBuffer[this._vertexIndex++] = uvy1;
vertexBuffer[this._vertexIndex++] = textureId;
vertexBuffer[this._vertexIndex++] = tint.r / 255;
vertexBuffer[this._vertexIndex++] = tint.g / 255;
vertexBuffer[this._vertexIndex++] = tint.b / 255;
vertexBuffer[this._vertexIndex++] = tint.a;
}

hasPendingDraws(): boolean {
Expand All @@ -247,7 +266,7 @@ export class ImageRenderer implements RendererPlugin {
this._shader.use();

// Bind the memory layout and upload data
this._layout.use(true, 4 * 6 * this._imageCount);
this._layout.use(true, 4 * 10 * this._imageCount);

// Update ortho matrix uniform
this._shader.setUniformMatrix('u_matrix', this._context.ortho);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ out vec2 v_texcoord;
in lowp float a_textureIndex;
out lowp float v_textureIndex;

in vec4 a_tint;
out vec4 v_tint;

uniform mat4 u_matrix;

void main() {
Expand All @@ -25,4 +28,6 @@ void main() {
v_texcoord = a_texcoord;
// Pass through the texture number to the fragment shader
v_textureIndex = a_textureIndex;
// Pass through the tint
v_tint = a_tint;
}
7 changes: 5 additions & 2 deletions src/engine/Graphics/Context/state-stack.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Color } from '../../Color';
import { ExcaliburGraphicsContextState } from './ExcaliburGraphicsContext';

export class StateStack {
Expand All @@ -7,14 +8,16 @@ export class StateStack {
private _getDefaultState() {
return {
opacity: 1,
z: 0
z: 0,
tint: Color.White
};
}

private _cloneState() {
return {
opacity: this._currentState.opacity,
z: this._currentState.z
z: this._currentState.z,
tint: this._currentState.tint.clone()
};
}

Expand Down
13 changes: 11 additions & 2 deletions src/engine/Graphics/Graphic.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Vector, vec } from '../Math/vector';
import { ExcaliburGraphicsContext } from './Context/ExcaliburGraphicsContext';
import { BoundingBox } from '../Collision/BoundingBox';
import { Matrix } from '..';
import { Color, Matrix } from '..';
import { watch } from '../Util/Watch';

export interface GraphicOptions {
Expand All @@ -14,7 +14,7 @@ export interface GraphicOptions {
*/
height?: number;
/**
* SHould the graphic be flipped horizontally
* Should the graphic be flipped horizontally
*/
flipHorizontal?: boolean;
/**
Expand All @@ -33,6 +33,10 @@ export interface GraphicOptions {
* The opacity of the graphic
*/
opacity?: number;
/**
* The tint of the graphic, this color will be multiplied by the original pixel colors
*/
tint?: Color;
/**
* The origin of the drawing in pixels to use when applying transforms, by default it will be the center of the image
*/
Expand All @@ -51,6 +55,8 @@ export abstract class Graphic {
private static _ID: number = 0;
readonly id = Graphic._ID++;

public tint: Color = null;

public transform: Matrix = Matrix.identity();
private _transformStale = true;
public isStale() {
Expand Down Expand Up @@ -235,6 +241,9 @@ export abstract class Graphic {
ex.multiply(this.transform);
// it is important to multiply alphas so graphics respect the current context
ex.opacity = ex.opacity * this.opacity;
if (this.tint) {
ex.tint = this.tint;
}
}

protected _rotate(ex: ExcaliburGraphicsContext | Matrix) {
Expand Down
1 change: 1 addition & 0 deletions src/engine/Graphics/Text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ export class Text extends Graphic {
this.font.origin = this.origin;
this.font.opacity = this.opacity;
}
this.font.tint = this.tint;

const { width, height } = this.font.measureText(this._text);
this._textWidth = width;
Expand Down
Loading