mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-14 11:01:36 +08:00
Merge branch '4.1' into 4.2-beta
# Conflicts: # spine-ts/package-lock.json # spine-ts/package.json # spine-ts/spine-canvas/package.json # spine-ts/spine-core/package.json # spine-ts/spine-phaser/package.json # spine-ts/spine-phaser/src/SpinePlugin.ts # spine-ts/spine-pixi/package.json # spine-ts/spine-player/package.json # spine-ts/spine-threejs/package.json # spine-ts/spine-webgl/package.json
This commit is contained in:
commit
42959e0e72
@ -1,79 +1,161 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>spine-ts Examples</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
|
||||
<title>spine-ts Examples</title>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>spine-ts Examples</h1>
|
||||
<ul>
|
||||
<li>Canvas</li>
|
||||
<ul>
|
||||
<li><a href="/spine-canvas/example">Example</a></li>
|
||||
<li><a href="/spine-canvas/example/mouse-click.html">Mouse click</a></li>
|
||||
</ul>
|
||||
<li>Pixi</li>
|
||||
<ul>
|
||||
<li><a href="/spine-pixi/example/index.html">Basic example</a></li>
|
||||
<li><a href="/spine-pixi/example/events-example.html">Events example</a></li>
|
||||
</ul>
|
||||
<li>Phaser</li>
|
||||
<ul>
|
||||
<li><a href="/spine-phaser/example/basic-example.html">Basic example</a></li>
|
||||
<li><a href="/spine-phaser/example/events-example.html">Events example</a></li>
|
||||
<li><a href="/spine-phaser/example/mix-and-match-example.html">Mix and match example</a></li>
|
||||
<li><a href="/spine-phaser/example/arcade-physics-example.html">Arcade physics example</a></li>
|
||||
<li><a href="/spine-phaser/example/control-bones-example.html">Control bones example</a></li>
|
||||
<li><a href="/spine-phaser/example/batching-test.html">Batching test</a></li>
|
||||
<li><a href="/spine-phaser/example/multi-scene-test.html">Multi-scene test</a></li>
|
||||
<li><a href="/spine-phaser/example/bounds-test.html">Bounds test</a></li>
|
||||
<li><a href="/spine-phaser/example/visibility-test.html">Visibility test</a></li>
|
||||
<li><a href="/spine-phaser/example/blend-test.html">Blend test</a></li>
|
||||
<li><a href="/spine-phaser/example/camera-pipeline-test.html">Camera pipeline test</a></li>
|
||||
<li><a href="/spine-phaser/example/extended-class-test.html">Extended class</a></li>
|
||||
<li><a href="/spine-phaser/example/canvas-test.html">Canvas test</a></li>
|
||||
<li><a href="/spine-phaser/example/depth-test.html">Depth test</a></li>
|
||||
<li><a href="/spine-phaser/example/render-to-texture-test.html">Render to texture test</a></li>
|
||||
</ul>
|
||||
<li>Player</li>
|
||||
<ul>
|
||||
<li><a href="/spine-player/example/example.html">Example</a></li>
|
||||
<li><a href="/spine-player/example/embedding-binary-example.html">Embedding binary</a></li>
|
||||
<li><a href="/spine-player/example/embedding-json-example.html">Embedding JSON</a></li>
|
||||
<li><a href="/spine-player/example/dispose.html">Disposing a player</a></li>
|
||||
</ul>
|
||||
<li>WebGL</li>
|
||||
<ul>
|
||||
<li><a href="/spine-webgl/example">Example</a></li>
|
||||
<li><a href="/spine-webgl/example/barebones.html">Barebones</a></li>
|
||||
<li><a href="/spine-webgl/example/mix-and-match.html">Mix & match</a></li>
|
||||
<li><a href="/spine-webgl/example/custom-attachment.html">Custom attachment</a></li>
|
||||
<li><a href="/spine-webgl/example/drag-and-drop.html">Drag & drop</a></li>
|
||||
<li><a href="/spine-webgl/example/dress-up.html">Dress-up</a></li>
|
||||
<li><a href="/spine-webgl/example/bone-dragging.html">Bone dragging</a></li>
|
||||
<li><a href="/spine-webgl/demos/additiveblending.html">Additive blending</a></li>
|
||||
<li><a href="/spine-webgl/demos/clipping.html">Clipping</a></li>
|
||||
<li><a href="/spine-webgl/demos/hoverboard.html">Hoverboard</a></li>
|
||||
<li><a href="/spine-webgl/demos/imagechanges.html">Image changes</a></li>
|
||||
<li><a href="/spine-webgl/demos/meshes.html">Meshes</a></li>
|
||||
<li><a href="/spine-webgl/demos/skins.html">Skins</a></li>
|
||||
<li><a href="/spine-webgl/demos/spritesheets.html">Spritesheets</a></li>
|
||||
<li><a href="/spine-webgl/demos/stretchyman.html">Stretchyman</a></li>
|
||||
<li><a href="/spine-webgl/demos/tank.html">Tank</a></li>
|
||||
<li><a href="/spine-webgl/demos/transforms.html">Transforms</a></li>
|
||||
<li><a href="/spine-webgl/demos/transitions.html">Transitions</a></li>
|
||||
<li><a href="/spine-webgl/demos/vine.html">Vine</a></li>
|
||||
</ul>
|
||||
<li>THREE.JS</li>
|
||||
<ul>
|
||||
<li><a href="/spine-threejs/example/index.html">Example</a></li>
|
||||
<li><a href="/spine-threejs/example/coordinate-transform.html">Coordinate transform</a></li>
|
||||
</ul>
|
||||
</ul>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
<body>
|
||||
<h1>spine-ts Examples</h1>
|
||||
<ul>
|
||||
<li>Canvas</li>
|
||||
<ul>
|
||||
<li><a href="/spine-canvas/example">Example</a></li>
|
||||
<li>
|
||||
<a href="/spine-canvas/example/mouse-click.html">Mouse click</a>
|
||||
</li>
|
||||
</ul>
|
||||
<li>Pixi</li>
|
||||
<ul>
|
||||
<li><a href="/spine-pixi/example/index.html">Basic example</a></li>
|
||||
<li>
|
||||
<a href="/spine-pixi/example/events-example.html">Events example</a>
|
||||
</li>
|
||||
</ul>
|
||||
<li>Phaser</li>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/spine-phaser/example/basic-example.html">Basic example</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/spine-phaser/example/events-example.html">Events example</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/spine-phaser/example/mix-and-match-example.html"
|
||||
>Mix and match example</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/spine-phaser/example/arcade-physics-example.html"
|
||||
>Arcade physics example</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/spine-phaser/example/control-bones-example.html"
|
||||
>Control bones example</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/spine-phaser/example/batching-test.html">Batching test</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/spine-phaser/example/multi-scene-test.html"
|
||||
>Multi-scene test</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/spine-phaser/example/bounds-test.html">Bounds test</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/spine-phaser/example/visibility-test.html"
|
||||
>Visibility test</a
|
||||
>
|
||||
</li>
|
||||
<li><a href="/spine-phaser/example/blend-test.html">Blend test</a></li>
|
||||
<li>
|
||||
<a href="/spine-phaser/example/camera-pipeline-test.html"
|
||||
>Camera pipeline test</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/spine-phaser/example/extended-class-test.html"
|
||||
>Extended class</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/spine-phaser/example/canvas-test.html">Canvas test</a>
|
||||
</li>
|
||||
<li><a href="/spine-phaser/example/depth-test.html">Depth test</a></li>
|
||||
<li>
|
||||
<a href="/spine-phaser/example/render-to-texture-test.html"
|
||||
>Render to texture test</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/spine-phaser/example/custom-spine-object-type.html">Custom object factory name</a>
|
||||
</li>
|
||||
</ul>
|
||||
<li>Player</li>
|
||||
<ul>
|
||||
<li><a href="/spine-player/example/example.html">Example</a></li>
|
||||
<li>
|
||||
<a href="/spine-player/example/embedding-binary-example.html"
|
||||
>Embedding binary</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/spine-player/example/embedding-json-example.html"
|
||||
>Embedding JSON</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/spine-player/example/dispose.html">Disposing a player</a>
|
||||
</li>
|
||||
</ul>
|
||||
<li>WebGL</li>
|
||||
<ul>
|
||||
<li><a href="/spine-webgl/example">Example</a></li>
|
||||
<li><a href="/spine-webgl/example/barebones.html">Barebones</a></li>
|
||||
<li>
|
||||
<a href="/spine-webgl/example/mix-and-match.html">Mix & match</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/spine-webgl/example/custom-attachment.html"
|
||||
>Custom attachment</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/spine-webgl/example/drag-and-drop.html">Drag & drop</a>
|
||||
</li>
|
||||
<li><a href="/spine-webgl/example/dress-up.html">Dress-up</a></li>
|
||||
<li>
|
||||
<a href="/spine-webgl/example/bone-dragging.html">Bone dragging</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/spine-webgl/demos/additiveblending.html"
|
||||
>Additive blending</a
|
||||
>
|
||||
</li>
|
||||
<li><a href="/spine-webgl/demos/clipping.html">Clipping</a></li>
|
||||
<li><a href="/spine-webgl/demos/hoverboard.html">Hoverboard</a></li>
|
||||
<li>
|
||||
<a href="/spine-webgl/demos/imagechanges.html">Image changes</a>
|
||||
</li>
|
||||
<li><a href="/spine-webgl/demos/meshes.html">Meshes</a></li>
|
||||
<li><a href="/spine-webgl/demos/skins.html">Skins</a></li>
|
||||
<li><a href="/spine-webgl/demos/spritesheets.html">Spritesheets</a></li>
|
||||
<li><a href="/spine-webgl/demos/stretchyman.html">Stretchyman</a></li>
|
||||
<li><a href="/spine-webgl/demos/tank.html">Tank</a></li>
|
||||
<li><a href="/spine-webgl/demos/transforms.html">Transforms</a></li>
|
||||
<li><a href="/spine-webgl/demos/transitions.html">Transitions</a></li>
|
||||
<li><a href="/spine-webgl/demos/vine.html">Vine</a></li>
|
||||
</ul>
|
||||
<li>THREE.JS</li>
|
||||
<ul>
|
||||
<li><a href="/spine-threejs/example/index.html">Example</a></li>
|
||||
<li>
|
||||
<a href="/spine-threejs/example/coordinate-transform.html"
|
||||
>Coordinate transform</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/spine-threejs/example/logarithmic-depth-buffer.html"
|
||||
>Logarithmic depth buffer</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
49
spine-ts/spine-phaser/example/custom-spine-object-type.html
Normal file
49
spine-ts/spine-phaser/example/custom-spine-object-type.html
Normal file
@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<script src="//cdn.jsdelivr.net/npm/phaser@3.55.2/dist/phaser.js"></script>
|
||||
<script src="../dist/iife/spine-phaser.js"></script>
|
||||
<title>Spine Phaser Example</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Custom object factor name</h1>
|
||||
</body>
|
||||
<script>
|
||||
window.SPINE_GAME_OBJECT_TYPE = "spineOfficial";
|
||||
|
||||
class BasicExample extends Phaser.Scene {
|
||||
preload() {
|
||||
this.load.spineBinary("spineboy-data", "assets/spineboy-pro.skel");
|
||||
this.load.spineAtlas("spineboy-atlas", "assets/spineboy-pma.atlas");
|
||||
}
|
||||
|
||||
create() {
|
||||
const spineboy = this.add.spineOfficial(400, 500, 'spineboy-data', "spineboy-atlas");
|
||||
spineboy.setInteractive();
|
||||
spineboy.displayWidth = 200;
|
||||
spineboy.displayHeight = spineboy.height / spineboy.width * 200;
|
||||
this.input.enableDebug(spineboy, 0xff00ff);
|
||||
spineboy.animationState.setAnimation(0, "walk", true);
|
||||
}
|
||||
}
|
||||
|
||||
new Phaser.Game({
|
||||
type: Phaser.AUTO,
|
||||
width: 800,
|
||||
height: 600,
|
||||
type: Phaser.WEBGL,
|
||||
scene: [BasicExample],
|
||||
plugins: {
|
||||
scene: [
|
||||
{ key: "spine.SpinePlugin", plugin: spine.SpinePlugin, mapping: "spine" }
|
||||
]
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</html>
|
||||
@ -29,90 +29,138 @@
|
||||
|
||||
import { SPINE_GAME_OBJECT_TYPE } from "./keys";
|
||||
import { SpinePlugin } from "./SpinePlugin";
|
||||
import { ComputedSizeMixin, DepthMixin, FlipMixin, ScrollFactorMixin, TransformMixin, VisibleMixin, AlphaMixin, OriginMixin } from "./mixins";
|
||||
import { AnimationState, AnimationStateData, Bone, MathUtils, Skeleton, Skin, Vector2 } from "@esotericsoftware/spine-core";
|
||||
import {
|
||||
ComputedSizeMixin,
|
||||
DepthMixin,
|
||||
FlipMixin,
|
||||
ScrollFactorMixin,
|
||||
TransformMixin,
|
||||
VisibleMixin,
|
||||
AlphaMixin,
|
||||
OriginMixin,
|
||||
} from "./mixins";
|
||||
import {
|
||||
AnimationState,
|
||||
AnimationStateData,
|
||||
Bone,
|
||||
MathUtils,
|
||||
Skeleton,
|
||||
Skin,
|
||||
Vector2,
|
||||
} from "@esotericsoftware/spine-core";
|
||||
|
||||
class BaseSpineGameObject extends Phaser.GameObjects.GameObject {
|
||||
constructor (scene: Phaser.Scene, type: string) {
|
||||
super(scene, type);
|
||||
}
|
||||
constructor(scene: Phaser.Scene, type: string) {
|
||||
super(scene, type);
|
||||
}
|
||||
}
|
||||
|
||||
/** A bounds provider calculates the bounding box for a skeleton, which is then assigned as the size of the SpineGameObject. */
|
||||
export interface SpineGameObjectBoundsProvider {
|
||||
// Returns the bounding box for the skeleton, in skeleton space.
|
||||
calculateBounds (gameObject: SpineGameObject): { x: number, y: number, width: number, height: number };
|
||||
// Returns the bounding box for the skeleton, in skeleton space.
|
||||
calculateBounds(gameObject: SpineGameObject): {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
}
|
||||
|
||||
/** A bounds provider that calculates the bounding box from the setup pose. */
|
||||
export class SetupPoseBoundsProvider implements SpineGameObjectBoundsProvider {
|
||||
calculateBounds (gameObject: SpineGameObject) {
|
||||
if (!gameObject.skeleton) return { x: 0, y: 0, width: 0, height: 0 };
|
||||
// Make a copy of animation state and skeleton as this might be called while
|
||||
// the skeleton in the GameObject has already been heavily modified. We can not
|
||||
// reconstruct that state.
|
||||
const skeleton = new Skeleton(gameObject.skeleton.data);
|
||||
skeleton.setToSetupPose();
|
||||
skeleton.updateWorldTransform();
|
||||
const bounds = skeleton.getBoundsRect();
|
||||
return bounds.width == Number.NEGATIVE_INFINITY ? { x: 0, y: 0, width: 0, height: 0 } : bounds;
|
||||
}
|
||||
calculateBounds(gameObject: SpineGameObject) {
|
||||
if (!gameObject.skeleton) return { x: 0, y: 0, width: 0, height: 0 };
|
||||
// Make a copy of animation state and skeleton as this might be called while
|
||||
// the skeleton in the GameObject has already been heavily modified. We can not
|
||||
// reconstruct that state.
|
||||
const skeleton = new Skeleton(gameObject.skeleton.data);
|
||||
skeleton.setToSetupPose();
|
||||
skeleton.updateWorldTransform();
|
||||
const bounds = skeleton.getBoundsRect();
|
||||
return bounds.width == Number.NEGATIVE_INFINITY
|
||||
? { x: 0, y: 0, width: 0, height: 0 }
|
||||
: bounds;
|
||||
}
|
||||
}
|
||||
|
||||
/** A bounds provider that calculates the bounding box by taking the maximumg bounding box for a combination of skins and specific animation. */
|
||||
export class SkinsAndAnimationBoundsProvider implements SpineGameObjectBoundsProvider {
|
||||
/**
|
||||
* @param animation The animation to use for calculating the bounds. If null, the setup pose is used.
|
||||
* @param skins The skins to use for calculating the bounds. If empty, the default skin is used.
|
||||
* @param timeStep The time step to use for calculating the bounds. A smaller time step means more precision, but slower calculation.
|
||||
*/
|
||||
constructor (private animation: string | null, private skins: string[] = [], private timeStep: number = 0.05) {
|
||||
}
|
||||
export class SkinsAndAnimationBoundsProvider
|
||||
implements SpineGameObjectBoundsProvider
|
||||
{
|
||||
/**
|
||||
* @param animation The animation to use for calculating the bounds. If null, the setup pose is used.
|
||||
* @param skins The skins to use for calculating the bounds. If empty, the default skin is used.
|
||||
* @param timeStep The time step to use for calculating the bounds. A smaller time step means more precision, but slower calculation.
|
||||
*/
|
||||
constructor(
|
||||
private animation: string | null,
|
||||
private skins: string[] = [],
|
||||
private timeStep: number = 0.05
|
||||
) {}
|
||||
|
||||
calculateBounds (gameObject: SpineGameObject): { x: number; y: number; width: number; height: number; } {
|
||||
if (!gameObject.skeleton || !gameObject.animationState) return { x: 0, y: 0, width: 0, height: 0 };
|
||||
// Make a copy of animation state and skeleton as this might be called while
|
||||
// the skeleton in the GameObject has already been heavily modified. We can not
|
||||
// reconstruct that state.
|
||||
const animationState = new AnimationState(gameObject.animationState.data);
|
||||
const skeleton = new Skeleton(gameObject.skeleton.data);
|
||||
const data = skeleton.data;
|
||||
if (this.skins.length > 0) {
|
||||
let customSkin = new Skin("custom-skin");
|
||||
for (const skinName of this.skins) {
|
||||
const skin = data.findSkin(skinName);
|
||||
if (skin == null) continue;
|
||||
customSkin.addSkin(skin);
|
||||
}
|
||||
skeleton.setSkin(customSkin);
|
||||
}
|
||||
skeleton.setToSetupPose();
|
||||
calculateBounds(gameObject: SpineGameObject): {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
} {
|
||||
if (!gameObject.skeleton || !gameObject.animationState)
|
||||
return { x: 0, y: 0, width: 0, height: 0 };
|
||||
// Make a copy of animation state and skeleton as this might be called while
|
||||
// the skeleton in the GameObject has already been heavily modified. We can not
|
||||
// reconstruct that state.
|
||||
const animationState = new AnimationState(gameObject.animationState.data);
|
||||
const skeleton = new Skeleton(gameObject.skeleton.data);
|
||||
const data = skeleton.data;
|
||||
if (this.skins.length > 0) {
|
||||
let customSkin = new Skin("custom-skin");
|
||||
for (const skinName of this.skins) {
|
||||
const skin = data.findSkin(skinName);
|
||||
if (skin == null) continue;
|
||||
customSkin.addSkin(skin);
|
||||
}
|
||||
skeleton.setSkin(customSkin);
|
||||
}
|
||||
skeleton.setToSetupPose();
|
||||
|
||||
const animation = this.animation != null ? data.findAnimation(this.animation!) : null;
|
||||
if (animation == null) {
|
||||
skeleton.updateWorldTransform();
|
||||
const bounds = skeleton.getBoundsRect();
|
||||
return bounds.width == Number.NEGATIVE_INFINITY ? { x: 0, y: 0, width: 0, height: 0 } : bounds;
|
||||
} else {
|
||||
let minX = Number.POSITIVE_INFINITY, minY = Number.POSITIVE_INFINITY, maxX = Number.NEGATIVE_INFINITY, maxY = Number.NEGATIVE_INFINITY;
|
||||
animationState.clearTracks();
|
||||
animationState.setAnimationWith(0, animation, false);
|
||||
const steps = Math.max(animation.duration / this.timeStep, 1.0);
|
||||
for (let i = 0; i < steps; i++) {
|
||||
animationState.update(i > 0 ? this.timeStep : 0);
|
||||
animationState.apply(skeleton);
|
||||
skeleton.updateWorldTransform();
|
||||
const animation =
|
||||
this.animation != null ? data.findAnimation(this.animation!) : null;
|
||||
if (animation == null) {
|
||||
skeleton.updateWorldTransform();
|
||||
const bounds = skeleton.getBoundsRect();
|
||||
return bounds.width == Number.NEGATIVE_INFINITY
|
||||
? { x: 0, y: 0, width: 0, height: 0 }
|
||||
: bounds;
|
||||
} else {
|
||||
let minX = Number.POSITIVE_INFINITY,
|
||||
minY = Number.POSITIVE_INFINITY,
|
||||
maxX = Number.NEGATIVE_INFINITY,
|
||||
maxY = Number.NEGATIVE_INFINITY;
|
||||
animationState.clearTracks();
|
||||
animationState.setAnimationWith(0, animation, false);
|
||||
const steps = Math.max(animation.duration / this.timeStep, 1.0);
|
||||
for (let i = 0; i < steps; i++) {
|
||||
animationState.update(i > 0 ? this.timeStep : 0);
|
||||
animationState.apply(skeleton);
|
||||
skeleton.updateWorldTransform();
|
||||
|
||||
const bounds = skeleton.getBoundsRect();
|
||||
minX = Math.min(minX, bounds.x);
|
||||
minY = Math.min(minY, bounds.y);
|
||||
maxX = Math.max(maxX, minX + bounds.width);
|
||||
maxY = Math.max(maxY, minY + bounds.height);
|
||||
}
|
||||
const bounds = { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
||||
return bounds.width == Number.NEGATIVE_INFINITY ? { x: 0, y: 0, width: 0, height: 0 } : bounds;
|
||||
}
|
||||
}
|
||||
const bounds = skeleton.getBoundsRect();
|
||||
minX = Math.min(minX, bounds.x);
|
||||
minY = Math.min(minY, bounds.y);
|
||||
maxX = Math.max(maxX, minX + bounds.width);
|
||||
maxY = Math.max(maxY, minY + bounds.height);
|
||||
}
|
||||
const bounds = {
|
||||
x: minX,
|
||||
y: minY,
|
||||
width: maxX - minX,
|
||||
height: maxY - minY,
|
||||
};
|
||||
return bounds.width == Number.NEGATIVE_INFINITY
|
||||
? { x: 0, y: 0, width: 0, height: 0 }
|
||||
: bounds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -136,147 +184,215 @@ export class SkinsAndAnimationBoundsProvider implements SpineGameObjectBoundsPro
|
||||
*
|
||||
* See {@link skeletonToPhaserWorldCoordinates}, {@link phaserWorldCoordinatesToSkeleton}, and {@link phaserWorldCoordinatesToBoneLocal.}
|
||||
*/
|
||||
export class SpineGameObject extends DepthMixin(OriginMixin(ComputedSizeMixin(FlipMixin(ScrollFactorMixin(TransformMixin(VisibleMixin(AlphaMixin(BaseSpineGameObject)))))))) {
|
||||
blendMode = -1;
|
||||
skeleton: Skeleton;
|
||||
animationStateData: AnimationStateData;
|
||||
animationState: AnimationState;
|
||||
beforeUpdateWorldTransforms: (object: SpineGameObject) => void = () => { };
|
||||
afterUpdateWorldTransforms: (object: SpineGameObject) => void = () => { };
|
||||
private premultipliedAlpha = false;
|
||||
export class SpineGameObject extends DepthMixin(
|
||||
OriginMixin(
|
||||
ComputedSizeMixin(
|
||||
FlipMixin(
|
||||
ScrollFactorMixin(
|
||||
TransformMixin(VisibleMixin(AlphaMixin(BaseSpineGameObject)))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
) {
|
||||
blendMode = -1;
|
||||
skeleton: Skeleton;
|
||||
animationStateData: AnimationStateData;
|
||||
animationState: AnimationState;
|
||||
beforeUpdateWorldTransforms: (object: SpineGameObject) => void = () => {};
|
||||
afterUpdateWorldTransforms: (object: SpineGameObject) => void = () => {};
|
||||
private premultipliedAlpha = false;
|
||||
|
||||
constructor (scene: Phaser.Scene, private plugin: SpinePlugin, x: number, y: number, dataKey: string, atlasKey: string, public boundsProvider: SpineGameObjectBoundsProvider = new SetupPoseBoundsProvider()) {
|
||||
super(scene, SPINE_GAME_OBJECT_TYPE);
|
||||
this.setPosition(x, y);
|
||||
constructor(
|
||||
scene: Phaser.Scene,
|
||||
private plugin: SpinePlugin,
|
||||
x: number,
|
||||
y: number,
|
||||
dataKey: string,
|
||||
atlasKey: string,
|
||||
public boundsProvider: SpineGameObjectBoundsProvider = new SetupPoseBoundsProvider()
|
||||
) {
|
||||
super(scene, (window as any).SPINE_GAME_OBJECT_TYPE ? (window as any).SPINE_GAME_OBJECT_TYPE : SPINE_GAME_OBJECT_TYPE);
|
||||
this.setPosition(x, y);
|
||||
|
||||
this.premultipliedAlpha = this.plugin.isAtlasPremultiplied(atlasKey);
|
||||
this.skeleton = this.plugin.createSkeleton(dataKey, atlasKey);
|
||||
this.animationStateData = new AnimationStateData(this.skeleton.data);
|
||||
this.animationState = new AnimationState(this.animationStateData);
|
||||
this.skeleton.updateWorldTransform();
|
||||
this.updateSize();
|
||||
}
|
||||
this.premultipliedAlpha = this.plugin.isAtlasPremultiplied(atlasKey);
|
||||
this.skeleton = this.plugin.createSkeleton(dataKey, atlasKey);
|
||||
this.animationStateData = new AnimationStateData(this.skeleton.data);
|
||||
this.animationState = new AnimationState(this.animationStateData);
|
||||
this.skeleton.updateWorldTransform();
|
||||
this.updateSize();
|
||||
}
|
||||
|
||||
updateSize () {
|
||||
if (!this.skeleton) return;
|
||||
let bounds = this.boundsProvider.calculateBounds(this);
|
||||
// For some reason the TS compiler and the ComputedSize mixin don't work well together and we have
|
||||
// to cast to any.
|
||||
let self = this as any;
|
||||
self.width = bounds.width;
|
||||
self.height = bounds.height;
|
||||
this.displayOriginX = -bounds.x;
|
||||
this.displayOriginY = -bounds.y;
|
||||
}
|
||||
updateSize() {
|
||||
if (!this.skeleton) return;
|
||||
let bounds = this.boundsProvider.calculateBounds(this);
|
||||
// For some reason the TS compiler and the ComputedSize mixin don't work well together and we have
|
||||
// to cast to any.
|
||||
let self = this as any;
|
||||
self.width = bounds.width;
|
||||
self.height = bounds.height;
|
||||
this.displayOriginX = -bounds.x;
|
||||
this.displayOriginY = -bounds.y;
|
||||
}
|
||||
|
||||
/** Converts a point from the skeleton coordinate system to the Phaser world coordinate system. */
|
||||
skeletonToPhaserWorldCoordinates (point: { x: number, y: number }) {
|
||||
let transform = this.getWorldTransformMatrix();
|
||||
let a = transform.a, b = transform.b, c = transform.c, d = transform.d, tx = transform.tx, ty = transform.ty;
|
||||
let x = point.x
|
||||
let y = point.y
|
||||
point.x = x * a + y * c + tx;
|
||||
point.y = x * b + y * d + ty;
|
||||
}
|
||||
/** Converts a point from the skeleton coordinate system to the Phaser world coordinate system. */
|
||||
skeletonToPhaserWorldCoordinates(point: { x: number; y: number }) {
|
||||
let transform = this.getWorldTransformMatrix();
|
||||
let a = transform.a,
|
||||
b = transform.b,
|
||||
c = transform.c,
|
||||
d = transform.d,
|
||||
tx = transform.tx,
|
||||
ty = transform.ty;
|
||||
let x = point.x;
|
||||
let y = point.y;
|
||||
point.x = x * a + y * c + tx;
|
||||
point.y = x * b + y * d + ty;
|
||||
}
|
||||
|
||||
/** Converts a point from the Phaser world coordinate system to the skeleton coordinate system. */
|
||||
phaserWorldCoordinatesToSkeleton (point: { x: number, y: number }) {
|
||||
let transform = this.getWorldTransformMatrix();
|
||||
transform = transform.invert();
|
||||
let a = transform.a, b = transform.b, c = transform.c, d = transform.d, tx = transform.tx, ty = transform.ty;
|
||||
let x = point.x
|
||||
let y = point.y
|
||||
point.x = x * a + y * c + tx;
|
||||
point.y = x * b + y * d + ty;
|
||||
}
|
||||
/** Converts a point from the Phaser world coordinate system to the skeleton coordinate system. */
|
||||
phaserWorldCoordinatesToSkeleton(point: { x: number; y: number }) {
|
||||
let transform = this.getWorldTransformMatrix();
|
||||
transform = transform.invert();
|
||||
let a = transform.a,
|
||||
b = transform.b,
|
||||
c = transform.c,
|
||||
d = transform.d,
|
||||
tx = transform.tx,
|
||||
ty = transform.ty;
|
||||
let x = point.x;
|
||||
let y = point.y;
|
||||
point.x = x * a + y * c + tx;
|
||||
point.y = x * b + y * d + ty;
|
||||
}
|
||||
|
||||
/** Converts a point from the Phaser world coordinate system to the bone's local coordinate system. */
|
||||
phaserWorldCoordinatesToBone (point: { x: number, y: number }, bone: Bone) {
|
||||
this.phaserWorldCoordinatesToSkeleton(point);
|
||||
if (bone.parent) {
|
||||
bone.parent.worldToLocal(point as Vector2);
|
||||
} else {
|
||||
bone.worldToLocal(point as Vector2);
|
||||
}
|
||||
}
|
||||
/** Converts a point from the Phaser world coordinate system to the bone's local coordinate system. */
|
||||
phaserWorldCoordinatesToBone(point: { x: number; y: number }, bone: Bone) {
|
||||
this.phaserWorldCoordinatesToSkeleton(point);
|
||||
if (bone.parent) {
|
||||
bone.parent.worldToLocal(point as Vector2);
|
||||
} else {
|
||||
bone.worldToLocal(point as Vector2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the {@link AnimationState}, applies it to the {@link Skeleton}, then updates the world transforms of all bones.
|
||||
* @param delta The time delta in milliseconds
|
||||
*/
|
||||
updatePose (delta: number) {
|
||||
this.animationState.update(delta / 1000);
|
||||
this.animationState.apply(this.skeleton);
|
||||
this.beforeUpdateWorldTransforms(this);
|
||||
this.skeleton.updateWorldTransform();
|
||||
this.afterUpdateWorldTransforms(this);
|
||||
}
|
||||
/**
|
||||
* Updates the {@link AnimationState}, applies it to the {@link Skeleton}, then updates the world transforms of all bones.
|
||||
* @param delta The time delta in milliseconds
|
||||
*/
|
||||
updatePose(delta: number) {
|
||||
this.animationState.update(delta / 1000);
|
||||
this.animationState.apply(this.skeleton);
|
||||
this.beforeUpdateWorldTransforms(this);
|
||||
this.skeleton.updateWorldTransform();
|
||||
this.afterUpdateWorldTransforms(this);
|
||||
}
|
||||
|
||||
preUpdate (time: number, delta: number) {
|
||||
if (!this.skeleton || !this.animationState) return;
|
||||
this.updatePose(delta);
|
||||
}
|
||||
preUpdate(time: number, delta: number) {
|
||||
if (!this.skeleton || !this.animationState) return;
|
||||
this.updatePose(delta);
|
||||
}
|
||||
|
||||
preDestroy () {
|
||||
// FIXME tear down any event emitters
|
||||
}
|
||||
preDestroy() {
|
||||
// FIXME tear down any event emitters
|
||||
}
|
||||
|
||||
willRender (camera: Phaser.Cameras.Scene2D.Camera) {
|
||||
if (!this.visible) return false;
|
||||
willRender(camera: Phaser.Cameras.Scene2D.Camera) {
|
||||
if (!this.visible) return false;
|
||||
|
||||
var GameObjectRenderMask = 0xf;
|
||||
var result = (!this.skeleton || !(GameObjectRenderMask !== this.renderFlags || (this.cameraFilter !== 0 && (this.cameraFilter & camera.id))));
|
||||
var GameObjectRenderMask = 0xf;
|
||||
var result = !this.skeleton || !(GameObjectRenderMask !== this.renderFlags || (this.cameraFilter !== 0 && this.cameraFilter & camera.id));
|
||||
|
||||
return result;
|
||||
}
|
||||
if (!result && this.parentContainer && this.plugin.webGLRenderer) {
|
||||
var sceneRenderer = this.plugin.webGLRenderer;
|
||||
|
||||
renderWebGL (renderer: Phaser.Renderer.WebGL.WebGLRenderer, src: SpineGameObject, camera: Phaser.Cameras.Scene2D.Camera, parentMatrix: Phaser.GameObjects.Components.TransformMatrix) {
|
||||
if (!this.skeleton || !this.animationState || !this.plugin.webGLRenderer) return;
|
||||
if (this.plugin.gl && this.plugin.phaserRenderer instanceof Phaser.Renderer.WebGL.WebGLRenderer && sceneRenderer.batcher.isDrawing) {
|
||||
sceneRenderer.end();
|
||||
this.plugin.phaserRenderer.pipelines.rebind();
|
||||
}
|
||||
}
|
||||
|
||||
let sceneRenderer = this.plugin.webGLRenderer;
|
||||
if (renderer.newType) {
|
||||
renderer.pipelines.clear();
|
||||
sceneRenderer.begin();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
camera.addToRenderList(src);
|
||||
let transform = Phaser.GameObjects.GetCalcMatrix(src, camera, parentMatrix).calc;
|
||||
let a = transform.a, b = transform.b, c = transform.c, d = transform.d, tx = transform.tx, ty = transform.ty;
|
||||
sceneRenderer.drawSkeleton(this.skeleton, this.premultipliedAlpha, -1, -1, (vertices, numVertices, stride) => {
|
||||
for (let i = 0; i < numVertices; i += stride) {
|
||||
let vx = vertices[i];
|
||||
let vy = vertices[i + 1];
|
||||
vertices[i] = vx * a + vy * c + tx;
|
||||
vertices[i + 1] = vx * b + vy * d + ty;
|
||||
}
|
||||
});
|
||||
renderWebGL(
|
||||
renderer: Phaser.Renderer.WebGL.WebGLRenderer,
|
||||
src: SpineGameObject,
|
||||
camera: Phaser.Cameras.Scene2D.Camera,
|
||||
parentMatrix: Phaser.GameObjects.Components.TransformMatrix
|
||||
) {
|
||||
if (!this.skeleton || !this.animationState || !this.plugin.webGLRenderer)
|
||||
return;
|
||||
|
||||
if (!renderer.nextTypeMatch) {
|
||||
sceneRenderer.end();
|
||||
renderer.pipelines.rebind();
|
||||
}
|
||||
}
|
||||
let sceneRenderer = this.plugin.webGLRenderer;
|
||||
if (renderer.newType) {
|
||||
renderer.pipelines.clear();
|
||||
sceneRenderer.begin();
|
||||
}
|
||||
|
||||
renderCanvas (renderer: Phaser.Renderer.Canvas.CanvasRenderer, src: SpineGameObject, camera: Phaser.Cameras.Scene2D.Camera, parentMatrix: Phaser.GameObjects.Components.TransformMatrix) {
|
||||
if (!this.skeleton || !this.animationState || !this.plugin.canvasRenderer) return;
|
||||
camera.addToRenderList(src);
|
||||
let transform = Phaser.GameObjects.GetCalcMatrix(
|
||||
src,
|
||||
camera,
|
||||
parentMatrix
|
||||
).calc;
|
||||
let a = transform.a,
|
||||
b = transform.b,
|
||||
c = transform.c,
|
||||
d = transform.d,
|
||||
tx = transform.tx,
|
||||
ty = transform.ty;
|
||||
sceneRenderer.drawSkeleton(
|
||||
this.skeleton,
|
||||
this.premultipliedAlpha,
|
||||
-1,
|
||||
-1,
|
||||
(vertices, numVertices, stride) => {
|
||||
for (let i = 0; i < numVertices; i += stride) {
|
||||
let vx = vertices[i];
|
||||
let vy = vertices[i + 1];
|
||||
vertices[i] = vx * a + vy * c + tx;
|
||||
vertices[i + 1] = vx * b + vy * d + ty;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
let context = renderer.currentContext;
|
||||
let skeletonRenderer = this.plugin.canvasRenderer;
|
||||
(skeletonRenderer as any).ctx = context;
|
||||
if (!renderer.nextTypeMatch) {
|
||||
sceneRenderer.end();
|
||||
renderer.pipelines.rebind();
|
||||
}
|
||||
}
|
||||
|
||||
camera.addToRenderList(src);
|
||||
let transform = Phaser.GameObjects.GetCalcMatrix(src, camera, parentMatrix).calc;
|
||||
let skeleton = this.skeleton;
|
||||
skeleton.x = transform.tx;
|
||||
skeleton.y = transform.ty;
|
||||
skeleton.scaleX = transform.scaleX;
|
||||
skeleton.scaleY = transform.scaleY;
|
||||
let root = skeleton.getRootBone()!;
|
||||
root.rotation = -MathUtils.radiansToDegrees * transform.rotationNormalized;
|
||||
this.skeleton.updateWorldTransform();
|
||||
renderCanvas(
|
||||
renderer: Phaser.Renderer.Canvas.CanvasRenderer,
|
||||
src: SpineGameObject,
|
||||
camera: Phaser.Cameras.Scene2D.Camera,
|
||||
parentMatrix: Phaser.GameObjects.Components.TransformMatrix
|
||||
) {
|
||||
if (!this.skeleton || !this.animationState || !this.plugin.canvasRenderer)
|
||||
return;
|
||||
|
||||
context.save();
|
||||
skeletonRenderer.draw(skeleton);
|
||||
context.restore();
|
||||
}
|
||||
let context = renderer.currentContext;
|
||||
let skeletonRenderer = this.plugin.canvasRenderer;
|
||||
(skeletonRenderer as any).ctx = context;
|
||||
|
||||
camera.addToRenderList(src);
|
||||
let transform = Phaser.GameObjects.GetCalcMatrix(
|
||||
src,
|
||||
camera,
|
||||
parentMatrix
|
||||
).calc;
|
||||
let skeleton = this.skeleton;
|
||||
skeleton.x = transform.tx;
|
||||
skeleton.y = transform.ty;
|
||||
skeleton.scaleX = transform.scaleX;
|
||||
skeleton.scaleY = transform.scaleY;
|
||||
let root = skeleton.getRootBone()!;
|
||||
root.rotation = -MathUtils.radiansToDegrees * transform.rotationNormalized;
|
||||
this.skeleton.updateWorldTransform();
|
||||
|
||||
context.save();
|
||||
skeletonRenderer.draw(skeleton);
|
||||
context.restore();
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
*****************************************************************************/
|
||||
|
||||
import Phaser from "phaser";
|
||||
import { SPINE_ATLAS_CACHE_KEY, SPINE_CONTAINER_TYPE, SPINE_GAME_OBJECT_TYPE, SPINE_SKELETON_DATA_FILE_TYPE, SPINE_ATLAS_FILE_TYPE, SPINE_SKELETON_FILE_CACHE_KEY as SPINE_SKELETON_DATA_CACHE_KEY } from "./keys";
|
||||
import { SPINE_ATLAS_CACHE_KEY, SPINE_GAME_OBJECT_TYPE, SPINE_SKELETON_DATA_FILE_TYPE, SPINE_ATLAS_FILE_TYPE, SPINE_SKELETON_FILE_CACHE_KEY as SPINE_SKELETON_DATA_CACHE_KEY } from "./keys";
|
||||
import { AtlasAttachmentLoader, GLTexture, SceneRenderer, Skeleton, SkeletonBinary, SkeletonData, SkeletonJson, TextureAtlas } from "@esotericsoftware/spine-webgl"
|
||||
import { SpineGameObject, SpineGameObjectBoundsProvider } from "./SpineGameObject";
|
||||
import { CanvasTexture, SkeletonRenderer } from "@esotericsoftware/spine-canvas";
|
||||
@ -74,9 +74,10 @@ export interface SpineGameObjectConfig extends Phaser.Types.GameObjects.GameObje
|
||||
export class SpinePlugin extends Phaser.Plugins.ScenePlugin {
|
||||
game: Phaser.Game;
|
||||
private isWebGL: boolean;
|
||||
private gl: WebGLRenderingContext | null;
|
||||
gl: WebGLRenderingContext | null;
|
||||
webGLRenderer: SceneRenderer | null;
|
||||
canvasRenderer: SkeletonRenderer | null;
|
||||
phaserRenderer: Phaser.Renderer.Canvas.CanvasRenderer | Phaser.Renderer.WebGL.WebGLRenderer;
|
||||
private skeletonDataCache: Phaser.Cache.BaseCache;
|
||||
private atlasCache: Phaser.Cache.BaseCache;
|
||||
|
||||
@ -85,6 +86,7 @@ export class SpinePlugin extends Phaser.Plugins.ScenePlugin {
|
||||
this.game = pluginManager.game;
|
||||
this.isWebGL = this.game.config.renderType === 2;
|
||||
this.gl = this.isWebGL ? (this.game.renderer as Phaser.Renderer.WebGL.WebGLRenderer).gl : null;
|
||||
this.phaserRenderer = this.game.renderer;
|
||||
this.webGLRenderer = null;
|
||||
this.canvasRenderer = null;
|
||||
this.skeletonDataCache = this.game.cache.addCustom(SPINE_SKELETON_DATA_CACHE_KEY);
|
||||
@ -136,7 +138,7 @@ export class SpinePlugin extends Phaser.Plugins.ScenePlugin {
|
||||
}
|
||||
return Phaser.GameObjects.BuildGameObject(this.scene, gameObject, config);
|
||||
}
|
||||
pluginManager.registerGameObject(SPINE_GAME_OBJECT_TYPE, addSpineGameObject, makeSpineGameObject);
|
||||
pluginManager.registerGameObject((window as any).SPINE_GAME_OBJECT_TYPE ? (window as any).SPINE_GAME_OBJECT_TYPE : SPINE_GAME_OBJECT_TYPE, addSpineGameObject, makeSpineGameObject);
|
||||
}
|
||||
|
||||
boot () {
|
||||
@ -186,8 +188,7 @@ export class SpinePlugin extends Phaser.Plugins.ScenePlugin {
|
||||
}
|
||||
|
||||
gameDestroy () {
|
||||
this.pluginManager.removeGameObject(SPINE_GAME_OBJECT_TYPE, true, true);
|
||||
this.pluginManager.removeGameObject(SPINE_CONTAINER_TYPE, true, true);
|
||||
this.pluginManager.removeGameObject((window as any).SPINE_GAME_OBJECT_TYPE ? (window as any).SPINE_GAME_OBJECT_TYPE : SPINE_GAME_OBJECT_TYPE, true, true);
|
||||
if (this.webGLRenderer) this.webGLRenderer.dispose();
|
||||
}
|
||||
|
||||
@ -264,15 +265,13 @@ interface SpineSkeletonDataFileConfig {
|
||||
|
||||
class SpineSkeletonDataFile extends Phaser.Loader.MultiFile {
|
||||
constructor (loader: Phaser.Loader.LoaderPlugin, key: string | SpineSkeletonDataFileConfig, url?: string, public fileType?: SpineSkeletonDataFileType, xhrSettings?: Phaser.Types.Loader.XHRSettingsObject) {
|
||||
|
||||
if (typeof key !== "string") {
|
||||
const config = key;
|
||||
key = config.key;
|
||||
url = config.url;
|
||||
const config = key;
|
||||
key = config.key;
|
||||
url = config.url;
|
||||
fileType = config.type === "spineJson" ? SpineSkeletonDataFileType.json : SpineSkeletonDataFileType.binary;
|
||||
xhrSettings = config.xhrSettings;
|
||||
}
|
||||
|
||||
xhrSettings = config.xhrSettings;
|
||||
}
|
||||
let file = null;
|
||||
let isJson = fileType == SpineSkeletonDataFileType.json;
|
||||
if (isJson) {
|
||||
@ -311,14 +310,13 @@ interface SpineAtlasFileConfig {
|
||||
|
||||
class SpineAtlasFile extends Phaser.Loader.MultiFile {
|
||||
constructor (loader: Phaser.Loader.LoaderPlugin, key: string | SpineAtlasFileConfig, url?: string, public premultipliedAlpha: boolean = true, xhrSettings?: Phaser.Types.Loader.XHRSettingsObject) {
|
||||
|
||||
if (typeof key !== "string") {
|
||||
const config = key;
|
||||
key = config.key;
|
||||
url = config.url;
|
||||
const config = key;
|
||||
key = config.key;
|
||||
url = config.url;
|
||||
premultipliedAlpha = config.premultipliedAlpha ?? true;
|
||||
xhrSettings = config.xhrSettings;
|
||||
}
|
||||
xhrSettings = config.xhrSettings;
|
||||
}
|
||||
|
||||
super(loader, SPINE_ATLAS_FILE_TYPE, key, [
|
||||
new Phaser.Loader.FileTypes.TextFile(loader, {
|
||||
|
||||
@ -33,4 +33,3 @@ export const SPINE_LOADER_TYPE = "spine";
|
||||
export const SPINE_SKELETON_DATA_FILE_TYPE = "spineSkeletonData";
|
||||
export const SPINE_ATLAS_FILE_TYPE = "spineAtlasData";
|
||||
export const SPINE_GAME_OBJECT_TYPE = "spine";
|
||||
export const SPINE_CONTAINER_TYPE = "spineContainer";
|
||||
|
||||
@ -1,141 +1,150 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>spine-threejs</title>
|
||||
<script src="https://unpkg.com/three@0.141.0/build/three.js"></script>
|
||||
<script src="../dist/iife/spine-threejs.js"></script>
|
||||
<script src="./OrbitalControls.js"></script>
|
||||
</head>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>spine-threejs</title>
|
||||
<script src="https://unpkg.com/three@0.141.0/build/three.js"></script>
|
||||
<script src="../dist/iife/spine-threejs.js"></script>
|
||||
<script src="./OrbitalControls.js"></script>
|
||||
</head>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
body,
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body,
|
||||
html {
|
||||
height: 100%
|
||||
}
|
||||
canvas {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
canvas {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<script>
|
||||
(function () {
|
||||
let scene, camera, renderer;
|
||||
let geometry, material, mesh, skeletonMesh;
|
||||
let assetManager;
|
||||
let canvas;
|
||||
let controls;
|
||||
let lastFrameTime = Date.now() / 1000;
|
||||
|
||||
<body>
|
||||
<script>
|
||||
(function () {
|
||||
let scene, camera, renderer;
|
||||
let geometry, material, mesh, skeletonMesh;
|
||||
let assetManager;
|
||||
let canvas;
|
||||
let controls;
|
||||
let lastFrameTime = Date.now() / 1000;
|
||||
let baseUrl = "assets/";
|
||||
let skeletonFile = "raptor-pro.json";
|
||||
let atlasFile = skeletonFile
|
||||
.replace("-pro", "")
|
||||
.replace("-ess", "")
|
||||
.replace(".json", ".atlas");
|
||||
let animation = "walk";
|
||||
|
||||
let baseUrl = "assets/";
|
||||
let skeletonFile = "raptor-pro.json";
|
||||
let atlasFile = skeletonFile.replace("-pro", "").replace("-ess", "").replace(".json", ".atlas");
|
||||
let animation = "walk";
|
||||
function init() {
|
||||
// create the THREE.JS camera, scene and renderer (WebGL)
|
||||
let width = window.innerWidth,
|
||||
height = window.innerHeight;
|
||||
camera = new THREE.PerspectiveCamera(75, width / height, 1, 3000);
|
||||
camera.position.y = 100;
|
||||
camera.position.z = 400;
|
||||
scene = new THREE.Scene();
|
||||
renderer = new THREE.WebGLRenderer();
|
||||
renderer.setSize(width, height);
|
||||
document.body.appendChild(renderer.domElement);
|
||||
canvas = renderer.domElement;
|
||||
controls = new OrbitControls(camera, renderer.domElement);
|
||||
|
||||
function init() {
|
||||
// create the THREE.JS camera, scene and renderer (WebGL)
|
||||
let width = window.innerWidth, height = window.innerHeight;
|
||||
camera = new THREE.PerspectiveCamera(75, width / height, 1, 3000);
|
||||
camera.position.y = 100;
|
||||
camera.position.z = 400;
|
||||
scene = new THREE.Scene();
|
||||
renderer = new THREE.WebGLRenderer();
|
||||
renderer.setSize(width, height);
|
||||
document.body.appendChild(renderer.domElement);
|
||||
canvas = renderer.domElement;
|
||||
controls = new OrbitControls(camera, renderer.domElement);
|
||||
// load the assets required to display the Raptor model
|
||||
assetManager = new spine.AssetManager(baseUrl);
|
||||
assetManager.loadText(skeletonFile);
|
||||
assetManager.loadTextureAtlas(atlasFile);
|
||||
|
||||
// load the assets required to display the Raptor model
|
||||
assetManager = new spine.AssetManager(baseUrl);
|
||||
assetManager.loadText(skeletonFile);
|
||||
assetManager.loadTextureAtlas(atlasFile);
|
||||
requestAnimationFrame(load);
|
||||
}
|
||||
|
||||
requestAnimationFrame(load);
|
||||
}
|
||||
function load(name, scale) {
|
||||
if (assetManager.isLoadingComplete()) {
|
||||
// Add a box to the scene to which we attach the skeleton mesh
|
||||
geometry = new THREE.BoxGeometry(200, 200, 200);
|
||||
material = new THREE.MeshBasicMaterial({
|
||||
color: 0xff0000,
|
||||
wireframe: true,
|
||||
});
|
||||
mesh = new THREE.Mesh(geometry, material);
|
||||
scene.add(mesh);
|
||||
|
||||
function load(name, scale) {
|
||||
if (assetManager.isLoadingComplete()) {
|
||||
// Add a box to the scene to which we attach the skeleton mesh
|
||||
geometry = new THREE.BoxGeometry(200, 200, 200);
|
||||
material = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true });
|
||||
mesh = new THREE.Mesh(geometry, material);
|
||||
scene.add(mesh);
|
||||
// Load the texture atlas using name.atlas and name.png from the AssetManager.
|
||||
// The function passed to TextureAtlas is used to resolve relative paths.
|
||||
atlas = assetManager.require(atlasFile);
|
||||
|
||||
// Load the texture atlas using name.atlas and name.png from the AssetManager.
|
||||
// The function passed to TextureAtlas is used to resolve relative paths.
|
||||
atlas = assetManager.require(atlasFile);
|
||||
// Create a AtlasAttachmentLoader that resolves region, mesh, boundingbox and path attachments
|
||||
atlasLoader = new spine.AtlasAttachmentLoader(atlas);
|
||||
|
||||
// Create a AtlasAttachmentLoader that resolves region, mesh, boundingbox and path attachments
|
||||
atlasLoader = new spine.AtlasAttachmentLoader(atlas);
|
||||
// Create a SkeletonJson instance for parsing the .json file.
|
||||
let skeletonJson = new spine.SkeletonJson(atlasLoader);
|
||||
|
||||
// Create a SkeletonJson instance for parsing the .json file.
|
||||
let skeletonJson = new spine.SkeletonJson(atlasLoader);
|
||||
// Set the scale to apply during parsing, parse the file, and create a new skeleton.
|
||||
skeletonJson.scale = 0.4;
|
||||
let skeletonData = skeletonJson.readSkeletonData(
|
||||
assetManager.require(skeletonFile)
|
||||
);
|
||||
|
||||
// Set the scale to apply during parsing, parse the file, and create a new skeleton.
|
||||
skeletonJson.scale = 0.4;
|
||||
let skeletonData = skeletonJson.readSkeletonData(assetManager.require(skeletonFile));
|
||||
// Create a SkeletonMesh from the data and attach it to the scene
|
||||
skeletonMesh = new spine.SkeletonMesh(
|
||||
skeletonData,
|
||||
(parameters) => {
|
||||
parameters.depthTest = true;
|
||||
parameters.depthWrite = true;
|
||||
parameters.alphaTest = 0.001;
|
||||
}
|
||||
);
|
||||
skeletonMesh.state.setAnimation(0, animation, true);
|
||||
mesh.add(skeletonMesh);
|
||||
|
||||
// Create a SkeletonMesh from the data and attach it to the scene
|
||||
skeletonMesh = new spine.SkeletonMesh(skeletonData, (parameters) => {
|
||||
parameters.depthTest = true;
|
||||
parameters.depthWrite = true;
|
||||
parameters.alphaTest = 0.001;
|
||||
});
|
||||
skeletonMesh.state.setAnimation(0, animation, true);
|
||||
mesh.add(skeletonMesh);
|
||||
requestAnimationFrame(render);
|
||||
} else requestAnimationFrame(load);
|
||||
}
|
||||
|
||||
let lastTime = Date.now();
|
||||
function render() {
|
||||
// calculate delta time for animation purposes
|
||||
let now = Date.now() / 1000;
|
||||
let delta = now - lastFrameTime;
|
||||
lastFrameTime = now;
|
||||
|
||||
requestAnimationFrame(render);
|
||||
} else requestAnimationFrame(load);
|
||||
}
|
||||
// resize canvas to use full page, adjust camera/renderer
|
||||
resize();
|
||||
|
||||
let lastTime = Date.now();
|
||||
function render() {
|
||||
// calculate delta time for animation purposes
|
||||
let now = Date.now() / 1000;
|
||||
let delta = now - lastFrameTime;
|
||||
lastFrameTime = now;
|
||||
// Update orbital controls
|
||||
controls.update();
|
||||
|
||||
// resize canvas to use full page, adjust camera/renderer
|
||||
resize();
|
||||
// update the animation
|
||||
skeletonMesh.update(delta);
|
||||
|
||||
// Update orbital controls
|
||||
controls.update();
|
||||
// render the scene
|
||||
renderer.render(scene, camera);
|
||||
|
||||
// update the animation
|
||||
skeletonMesh.update(delta);
|
||||
requestAnimationFrame(render);
|
||||
}
|
||||
|
||||
// render the scene
|
||||
renderer.render(scene, camera);
|
||||
function resize() {
|
||||
let w = window.innerWidth;
|
||||
let h = window.innerHeight;
|
||||
if (canvas.width != w || canvas.height != h) {
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
}
|
||||
|
||||
requestAnimationFrame(render);
|
||||
}
|
||||
camera.aspect = w / h;
|
||||
camera.updateProjectionMatrix();
|
||||
|
||||
function resize() {
|
||||
let w = window.innerWidth;
|
||||
let h = window.innerHeight;
|
||||
if (canvas.width != w || canvas.height != h) {
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
}
|
||||
renderer.setSize(w, h);
|
||||
}
|
||||
|
||||
camera.aspect = w / h;
|
||||
camera.updateProjectionMatrix();
|
||||
|
||||
renderer.setSize(w, h);
|
||||
}
|
||||
|
||||
init();
|
||||
}());
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
init();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
186
spine-ts/spine-threejs/example/logarithmic-depth-buffer.html
Normal file
186
spine-ts/spine-threejs/example/logarithmic-depth-buffer.html
Normal file
@ -0,0 +1,186 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>spine-threejs</title>
|
||||
<script src="https://unpkg.com/three@0.141.0/build/three.js"></script>
|
||||
<script src="../dist/iife/spine-threejs.js"></script>
|
||||
<script src="./OrbitalControls.js"></script>
|
||||
</head>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body,
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
canvas {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body>
|
||||
<script>
|
||||
(function () {
|
||||
let scene, camera, renderer;
|
||||
let geometry, material, mesh, skeletonMesh;
|
||||
let assetManager;
|
||||
let canvas;
|
||||
let controls;
|
||||
let lastFrameTime = Date.now() / 1000;
|
||||
|
||||
let baseUrl = "assets/";
|
||||
let skeletonFile = "raptor-pro.json";
|
||||
let atlasFile = skeletonFile
|
||||
.replace("-pro", "")
|
||||
.replace("-ess", "")
|
||||
.replace(".json", ".atlas");
|
||||
let animation = "walk";
|
||||
|
||||
function init() {
|
||||
// create the THREE.JS camera, scene and renderer (WebGL)
|
||||
let width = window.innerWidth,
|
||||
height = window.innerHeight;
|
||||
camera = new THREE.PerspectiveCamera(75, width / height, 1, 3000);
|
||||
camera.position.y = 100;
|
||||
camera.position.z = 400;
|
||||
scene = new THREE.Scene();
|
||||
renderer = new THREE.WebGLRenderer({ logarithmicDepthBuffer: true });
|
||||
renderer.setSize(width, height);
|
||||
document.body.appendChild(renderer.domElement);
|
||||
canvas = renderer.domElement;
|
||||
controls = new OrbitControls(camera, renderer.domElement);
|
||||
|
||||
// load the assets required to display the Raptor model
|
||||
assetManager = new spine.AssetManager(baseUrl);
|
||||
assetManager.loadText(skeletonFile);
|
||||
assetManager.loadTextureAtlas(atlasFile);
|
||||
|
||||
requestAnimationFrame(load);
|
||||
}
|
||||
|
||||
function load(name, scale) {
|
||||
if (assetManager.isLoadingComplete()) {
|
||||
// Add a box to the scene to which we attach the skeleton mesh
|
||||
geometry = new THREE.BoxGeometry(200, 200, 200);
|
||||
material = new THREE.MeshBasicMaterial({
|
||||
color: 0xff0000,
|
||||
wireframe: true,
|
||||
});
|
||||
mesh = new THREE.Mesh(geometry, material);
|
||||
scene.add(mesh);
|
||||
|
||||
// Load the texture atlas using name.atlas and name.png from the AssetManager.
|
||||
// The function passed to TextureAtlas is used to resolve relative paths.
|
||||
atlas = assetManager.require(atlasFile);
|
||||
|
||||
// Create a AtlasAttachmentLoader that resolves region, mesh, boundingbox and path attachments
|
||||
atlasLoader = new spine.AtlasAttachmentLoader(atlas);
|
||||
|
||||
// Create a SkeletonJson instance for parsing the .json file.
|
||||
let skeletonJson = new spine.SkeletonJson(atlasLoader);
|
||||
|
||||
// Set the scale to apply during parsing, parse the file, and create a new skeleton.
|
||||
skeletonJson.scale = 0.4;
|
||||
let skeletonData = skeletonJson.readSkeletonData(
|
||||
assetManager.require(skeletonFile)
|
||||
);
|
||||
|
||||
// Create a SkeletonMesh from the data and attach it to the scene
|
||||
// Provide a custom vertex/fragment shader pair that supports
|
||||
// the logarithmic depth buffer.
|
||||
// See https://discourse.threejs.org/t/shadermaterial-render-order-with-logarithmicdepthbuffer-is-wrong/49221/3
|
||||
skeletonMesh = new spine.SkeletonMesh(
|
||||
skeletonData,
|
||||
(materialParameters) => {
|
||||
materialParameters.vertexShader = `
|
||||
attribute vec4 color;
|
||||
varying vec2 vUv;
|
||||
varying vec4 vColor;
|
||||
|
||||
#include <common>
|
||||
#include <logdepthbuf_pars_vertex>
|
||||
|
||||
void main() {
|
||||
vUv = uv;
|
||||
vColor = color;
|
||||
gl_Position = projectionMatrix*modelViewMatrix*vec4(position,1.0);
|
||||
#include <logdepthbuf_vertex>
|
||||
}
|
||||
`;
|
||||
|
||||
materialParameters.fragmentShader = `
|
||||
uniform sampler2D map;
|
||||
#ifdef USE_SPINE_ALPHATEST
|
||||
uniform float alphaTest;
|
||||
#endif
|
||||
varying vec2 vUv;
|
||||
varying vec4 vColor;
|
||||
|
||||
#include <common>
|
||||
#include <logdepthbuf_pars_fragment>
|
||||
|
||||
void main(void) {
|
||||
#include <logdepthbuf_fragment>
|
||||
|
||||
gl_FragColor = texture2D(map, vUv)*vColor;
|
||||
#ifdef USE_SPINE_ALPHATEST
|
||||
if (gl_FragColor.a < alphaTest) discard;
|
||||
#endif
|
||||
}
|
||||
`;
|
||||
}
|
||||
);
|
||||
skeletonMesh.state.setAnimation(0, animation, true);
|
||||
mesh.add(skeletonMesh);
|
||||
|
||||
requestAnimationFrame(render);
|
||||
} else requestAnimationFrame(load);
|
||||
}
|
||||
|
||||
let lastTime = Date.now();
|
||||
function render() {
|
||||
// calculate delta time for animation purposes
|
||||
let now = Date.now() / 1000;
|
||||
let delta = now - lastFrameTime;
|
||||
lastFrameTime = now;
|
||||
|
||||
// resize canvas to use full page, adjust camera/renderer
|
||||
resize();
|
||||
|
||||
// Update orbital controls
|
||||
controls.update();
|
||||
|
||||
// update the animation
|
||||
skeletonMesh.update(delta);
|
||||
|
||||
// render the scene
|
||||
renderer.render(scene, camera);
|
||||
|
||||
requestAnimationFrame(render);
|
||||
}
|
||||
|
||||
function resize() {
|
||||
let w = window.innerWidth;
|
||||
let h = window.innerHeight;
|
||||
if (canvas.width != w || canvas.height != h) {
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
}
|
||||
|
||||
camera.aspect = w / h;
|
||||
camera.updateProjectionMatrix();
|
||||
|
||||
renderer.setSize(w, h);
|
||||
}
|
||||
|
||||
init();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -27,16 +27,33 @@
|
||||
* SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { AnimationState, AnimationStateData, BlendMode, ClippingAttachment, Color, MeshAttachment, NumberArrayLike, RegionAttachment, Skeleton, SkeletonClipping, SkeletonData, TextureAtlasRegion, Utils, Vector2 } from "@esotericsoftware/spine-core";
|
||||
import {
|
||||
AnimationState,
|
||||
AnimationStateData,
|
||||
BlendMode,
|
||||
ClippingAttachment,
|
||||
Color,
|
||||
MeshAttachment,
|
||||
NumberArrayLike,
|
||||
RegionAttachment,
|
||||
Skeleton,
|
||||
SkeletonClipping,
|
||||
SkeletonData,
|
||||
TextureAtlasRegion,
|
||||
Utils,
|
||||
Vector2,
|
||||
} from "@esotericsoftware/spine-core";
|
||||
import { MeshBatcher } from "./MeshBatcher";
|
||||
import * as THREE from "three";
|
||||
import { ThreeJsTexture } from "./ThreeJsTexture";
|
||||
|
||||
export type SkeletonMeshMaterialParametersCustomizer = (materialParameters: THREE.ShaderMaterialParameters) => void;
|
||||
export type SkeletonMeshMaterialParametersCustomizer = (
|
||||
materialParameters: THREE.ShaderMaterialParameters
|
||||
) => void;
|
||||
|
||||
export class SkeletonMeshMaterial extends THREE.ShaderMaterial {
|
||||
constructor (customizer: SkeletonMeshMaterialParametersCustomizer) {
|
||||
let vertexShader = `
|
||||
constructor(customizer: SkeletonMeshMaterialParametersCustomizer) {
|
||||
let vertexShader = `
|
||||
attribute vec4 color;
|
||||
varying vec2 vUv;
|
||||
varying vec4 vColor;
|
||||
@ -46,7 +63,7 @@ export class SkeletonMeshMaterial extends THREE.ShaderMaterial {
|
||||
gl_Position = projectionMatrix*modelViewMatrix*vec4(position,1.0);
|
||||
}
|
||||
`;
|
||||
let fragmentShader = `
|
||||
let fragmentShader = `
|
||||
uniform sampler2D map;
|
||||
#ifdef USE_SPINE_ALPHATEST
|
||||
uniform float alphaTest;
|
||||
@ -61,212 +78,253 @@ export class SkeletonMeshMaterial extends THREE.ShaderMaterial {
|
||||
}
|
||||
`;
|
||||
|
||||
let parameters: THREE.ShaderMaterialParameters = {
|
||||
uniforms: {
|
||||
map: { value: null },
|
||||
},
|
||||
vertexShader: vertexShader,
|
||||
fragmentShader: fragmentShader,
|
||||
side: THREE.DoubleSide,
|
||||
transparent: true,
|
||||
depthWrite: false,
|
||||
alphaTest: 0.0
|
||||
};
|
||||
customizer(parameters);
|
||||
if (parameters.alphaTest && parameters.alphaTest > 0) {
|
||||
parameters.defines = { "USE_SPINE_ALPHATEST": 1 };
|
||||
if (!parameters.uniforms) parameters.uniforms = {};
|
||||
parameters.uniforms["alphaTest"] = { value: parameters.alphaTest };
|
||||
}
|
||||
super(parameters);
|
||||
};
|
||||
let parameters: THREE.ShaderMaterialParameters = {
|
||||
uniforms: {
|
||||
map: { value: null },
|
||||
},
|
||||
vertexShader: vertexShader,
|
||||
fragmentShader: fragmentShader,
|
||||
side: THREE.DoubleSide,
|
||||
transparent: true,
|
||||
depthWrite: true,
|
||||
alphaTest: 0.0,
|
||||
};
|
||||
customizer(parameters);
|
||||
if (parameters.alphaTest && parameters.alphaTest > 0) {
|
||||
parameters.defines = { USE_SPINE_ALPHATEST: 1 };
|
||||
if (!parameters.uniforms) parameters.uniforms = {};
|
||||
parameters.uniforms["alphaTest"] = { value: parameters.alphaTest };
|
||||
}
|
||||
super(parameters);
|
||||
}
|
||||
}
|
||||
|
||||
export class SkeletonMesh extends THREE.Object3D {
|
||||
tempPos: Vector2 = new Vector2();
|
||||
tempUv: Vector2 = new Vector2();
|
||||
tempLight = new Color();
|
||||
tempDark = new Color();
|
||||
skeleton: Skeleton;
|
||||
state: AnimationState;
|
||||
zOffset: number = 0.1;
|
||||
tempPos: Vector2 = new Vector2();
|
||||
tempUv: Vector2 = new Vector2();
|
||||
tempLight = new Color();
|
||||
tempDark = new Color();
|
||||
skeleton: Skeleton;
|
||||
state: AnimationState;
|
||||
zOffset: number = 0.1;
|
||||
|
||||
private batches = new Array<MeshBatcher>();
|
||||
private nextBatchIndex = 0;
|
||||
private clipper: SkeletonClipping = new SkeletonClipping();
|
||||
private batches = new Array<MeshBatcher>();
|
||||
private nextBatchIndex = 0;
|
||||
private clipper: SkeletonClipping = new SkeletonClipping();
|
||||
|
||||
static QUAD_TRIANGLES = [0, 1, 2, 2, 3, 0];
|
||||
static VERTEX_SIZE = 2 + 2 + 4;
|
||||
static QUAD_TRIANGLES = [0, 1, 2, 2, 3, 0];
|
||||
static VERTEX_SIZE = 2 + 2 + 4;
|
||||
|
||||
private vertices = Utils.newFloatArray(1024);
|
||||
private tempColor = new Color();
|
||||
private vertices = Utils.newFloatArray(1024);
|
||||
private tempColor = new Color();
|
||||
|
||||
constructor (skeletonData: SkeletonData, private materialCustomerizer: SkeletonMeshMaterialParametersCustomizer = (material) => { }) {
|
||||
super();
|
||||
constructor(
|
||||
skeletonData: SkeletonData,
|
||||
private materialCustomerizer: SkeletonMeshMaterialParametersCustomizer = (
|
||||
material
|
||||
) => {}
|
||||
) {
|
||||
super();
|
||||
|
||||
this.skeleton = new Skeleton(skeletonData);
|
||||
let animData = new AnimationStateData(skeletonData);
|
||||
this.state = new AnimationState(animData);
|
||||
}
|
||||
this.skeleton = new Skeleton(skeletonData);
|
||||
let animData = new AnimationStateData(skeletonData);
|
||||
this.state = new AnimationState(animData);
|
||||
}
|
||||
|
||||
update (deltaTime: number) {
|
||||
let state = this.state;
|
||||
let skeleton = this.skeleton;
|
||||
update(deltaTime: number) {
|
||||
let state = this.state;
|
||||
let skeleton = this.skeleton;
|
||||
|
||||
state.update(deltaTime);
|
||||
state.apply(skeleton);
|
||||
skeleton.updateWorldTransform();
|
||||
state.update(deltaTime);
|
||||
state.apply(skeleton);
|
||||
skeleton.updateWorldTransform();
|
||||
|
||||
this.updateGeometry();
|
||||
}
|
||||
this.updateGeometry();
|
||||
}
|
||||
|
||||
dispose () {
|
||||
for (var i = 0; i < this.batches.length; i++) {
|
||||
this.batches[i].dispose();
|
||||
}
|
||||
}
|
||||
dispose() {
|
||||
for (var i = 0; i < this.batches.length; i++) {
|
||||
this.batches[i].dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private clearBatches () {
|
||||
for (var i = 0; i < this.batches.length; i++) {
|
||||
this.batches[i].clear();
|
||||
this.batches[i].visible = false;
|
||||
}
|
||||
this.nextBatchIndex = 0;
|
||||
}
|
||||
private clearBatches() {
|
||||
for (var i = 0; i < this.batches.length; i++) {
|
||||
this.batches[i].clear();
|
||||
this.batches[i].visible = false;
|
||||
}
|
||||
this.nextBatchIndex = 0;
|
||||
}
|
||||
|
||||
private nextBatch () {
|
||||
if (this.batches.length == this.nextBatchIndex) {
|
||||
let batch = new MeshBatcher(10920, this.materialCustomerizer);
|
||||
this.add(batch);
|
||||
this.batches.push(batch);
|
||||
}
|
||||
let batch = this.batches[this.nextBatchIndex++];
|
||||
batch.visible = true;
|
||||
return batch;
|
||||
}
|
||||
private nextBatch() {
|
||||
if (this.batches.length == this.nextBatchIndex) {
|
||||
let batch = new MeshBatcher(10920, this.materialCustomerizer);
|
||||
this.add(batch);
|
||||
this.batches.push(batch);
|
||||
}
|
||||
let batch = this.batches[this.nextBatchIndex++];
|
||||
batch.visible = true;
|
||||
return batch;
|
||||
}
|
||||
|
||||
private updateGeometry () {
|
||||
this.clearBatches();
|
||||
private updateGeometry() {
|
||||
this.clearBatches();
|
||||
|
||||
let tempPos = this.tempPos;
|
||||
let tempUv = this.tempUv;
|
||||
let tempLight = this.tempLight;
|
||||
let tempDark = this.tempDark;
|
||||
let clipper = this.clipper;
|
||||
let tempPos = this.tempPos;
|
||||
let tempUv = this.tempUv;
|
||||
let tempLight = this.tempLight;
|
||||
let tempDark = this.tempDark;
|
||||
let clipper = this.clipper;
|
||||
|
||||
let vertices: NumberArrayLike = this.vertices;
|
||||
let triangles: Array<number> | null = null;
|
||||
let uvs: NumberArrayLike | null = null;
|
||||
let drawOrder = this.skeleton.drawOrder;
|
||||
let batch = this.nextBatch();
|
||||
batch.begin();
|
||||
let z = 0;
|
||||
let zOffset = this.zOffset;
|
||||
for (let i = 0, n = drawOrder.length; i < n; i++) {
|
||||
let vertexSize = clipper.isClipping() ? 2 : SkeletonMesh.VERTEX_SIZE;
|
||||
let slot = drawOrder[i];
|
||||
if (!slot.bone.active) {
|
||||
clipper.clipEndWithSlot(slot);
|
||||
continue;
|
||||
}
|
||||
let attachment = slot.getAttachment();
|
||||
let attachmentColor: Color | null;
|
||||
let texture: ThreeJsTexture | null;
|
||||
let numFloats = 0;
|
||||
if (attachment instanceof RegionAttachment) {
|
||||
let region = <RegionAttachment>attachment;
|
||||
attachmentColor = region.color;
|
||||
vertices = this.vertices;
|
||||
numFloats = vertexSize * 4;
|
||||
region.computeWorldVertices(slot, vertices, 0, vertexSize);
|
||||
triangles = SkeletonMesh.QUAD_TRIANGLES;
|
||||
uvs = region.uvs;
|
||||
texture = <ThreeJsTexture>region.region!.texture;
|
||||
} else if (attachment instanceof MeshAttachment) {
|
||||
let mesh = <MeshAttachment>attachment;
|
||||
attachmentColor = mesh.color;
|
||||
vertices = this.vertices;
|
||||
numFloats = (mesh.worldVerticesLength >> 1) * vertexSize;
|
||||
if (numFloats > vertices.length) {
|
||||
vertices = this.vertices = Utils.newFloatArray(numFloats);
|
||||
}
|
||||
mesh.computeWorldVertices(slot, 0, mesh.worldVerticesLength, vertices, 0, vertexSize);
|
||||
triangles = mesh.triangles;
|
||||
uvs = mesh.uvs;
|
||||
texture = <ThreeJsTexture>mesh.region!.texture;
|
||||
} else if (attachment instanceof ClippingAttachment) {
|
||||
let clip = <ClippingAttachment>(attachment);
|
||||
clipper.clipStart(slot, clip);
|
||||
continue;
|
||||
} else {
|
||||
clipper.clipEndWithSlot(slot);
|
||||
continue;
|
||||
}
|
||||
let vertices: NumberArrayLike = this.vertices;
|
||||
let triangles: Array<number> | null = null;
|
||||
let uvs: NumberArrayLike | null = null;
|
||||
let drawOrder = this.skeleton.drawOrder;
|
||||
let batch = this.nextBatch();
|
||||
batch.begin();
|
||||
let z = 0;
|
||||
let zOffset = this.zOffset;
|
||||
for (let i = 0, n = drawOrder.length; i < n; i++) {
|
||||
let vertexSize = clipper.isClipping() ? 2 : SkeletonMesh.VERTEX_SIZE;
|
||||
let slot = drawOrder[i];
|
||||
if (!slot.bone.active) {
|
||||
clipper.clipEndWithSlot(slot);
|
||||
continue;
|
||||
}
|
||||
let attachment = slot.getAttachment();
|
||||
let attachmentColor: Color | null;
|
||||
let texture: ThreeJsTexture | null;
|
||||
let numFloats = 0;
|
||||
if (attachment instanceof RegionAttachment) {
|
||||
let region = <RegionAttachment>attachment;
|
||||
attachmentColor = region.color;
|
||||
vertices = this.vertices;
|
||||
numFloats = vertexSize * 4;
|
||||
region.computeWorldVertices(slot, vertices, 0, vertexSize);
|
||||
triangles = SkeletonMesh.QUAD_TRIANGLES;
|
||||
uvs = region.uvs;
|
||||
texture = <ThreeJsTexture>region.region!.texture;
|
||||
} else if (attachment instanceof MeshAttachment) {
|
||||
let mesh = <MeshAttachment>attachment;
|
||||
attachmentColor = mesh.color;
|
||||
vertices = this.vertices;
|
||||
numFloats = (mesh.worldVerticesLength >> 1) * vertexSize;
|
||||
if (numFloats > vertices.length) {
|
||||
vertices = this.vertices = Utils.newFloatArray(numFloats);
|
||||
}
|
||||
mesh.computeWorldVertices(
|
||||
slot,
|
||||
0,
|
||||
mesh.worldVerticesLength,
|
||||
vertices,
|
||||
0,
|
||||
vertexSize
|
||||
);
|
||||
triangles = mesh.triangles;
|
||||
uvs = mesh.uvs;
|
||||
texture = <ThreeJsTexture>mesh.region!.texture;
|
||||
} else if (attachment instanceof ClippingAttachment) {
|
||||
let clip = <ClippingAttachment>attachment;
|
||||
clipper.clipStart(slot, clip);
|
||||
continue;
|
||||
} else {
|
||||
clipper.clipEndWithSlot(slot);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (texture != null) {
|
||||
let skeleton = slot.bone.skeleton;
|
||||
let skeletonColor = skeleton.color;
|
||||
let slotColor = slot.color;
|
||||
let alpha = skeletonColor.a * slotColor.a * attachmentColor.a;
|
||||
let color = this.tempColor;
|
||||
color.set(skeletonColor.r * slotColor.r * attachmentColor.r,
|
||||
skeletonColor.g * slotColor.g * attachmentColor.g,
|
||||
skeletonColor.b * slotColor.b * attachmentColor.b,
|
||||
alpha);
|
||||
if (texture != null) {
|
||||
let skeleton = slot.bone.skeleton;
|
||||
let skeletonColor = skeleton.color;
|
||||
let slotColor = slot.color;
|
||||
let alpha = skeletonColor.a * slotColor.a * attachmentColor.a;
|
||||
let color = this.tempColor;
|
||||
color.set(
|
||||
skeletonColor.r * slotColor.r * attachmentColor.r,
|
||||
skeletonColor.g * slotColor.g * attachmentColor.g,
|
||||
skeletonColor.b * slotColor.b * attachmentColor.b,
|
||||
alpha
|
||||
);
|
||||
|
||||
let finalVertices: NumberArrayLike;
|
||||
let finalVerticesLength: number;
|
||||
let finalIndices: NumberArrayLike;
|
||||
let finalIndicesLength: number;
|
||||
let finalVertices: NumberArrayLike;
|
||||
let finalVerticesLength: number;
|
||||
let finalIndices: NumberArrayLike;
|
||||
let finalIndicesLength: number;
|
||||
|
||||
if (clipper.isClipping()) {
|
||||
clipper.clipTriangles(vertices, numFloats, triangles, triangles.length, uvs, color, tempLight, false);
|
||||
let clippedVertices = clipper.clippedVertices;
|
||||
let clippedTriangles = clipper.clippedTriangles;
|
||||
finalVertices = clippedVertices;
|
||||
finalVerticesLength = clippedVertices.length;
|
||||
finalIndices = clippedTriangles;
|
||||
finalIndicesLength = clippedTriangles.length;
|
||||
} else {
|
||||
let verts = vertices;
|
||||
for (let v = 2, u = 0, n = numFloats; v < n; v += vertexSize, u += 2) {
|
||||
verts[v] = color.r;
|
||||
verts[v + 1] = color.g;
|
||||
verts[v + 2] = color.b;
|
||||
verts[v + 3] = color.a;
|
||||
verts[v + 4] = uvs[u];
|
||||
verts[v + 5] = uvs[u + 1];
|
||||
}
|
||||
finalVertices = vertices;
|
||||
finalVerticesLength = numFloats;
|
||||
finalIndices = triangles;
|
||||
finalIndicesLength = triangles.length;
|
||||
}
|
||||
if (clipper.isClipping()) {
|
||||
clipper.clipTriangles(
|
||||
vertices,
|
||||
numFloats,
|
||||
triangles,
|
||||
triangles.length,
|
||||
uvs,
|
||||
color,
|
||||
tempLight,
|
||||
false
|
||||
);
|
||||
let clippedVertices = clipper.clippedVertices;
|
||||
let clippedTriangles = clipper.clippedTriangles;
|
||||
finalVertices = clippedVertices;
|
||||
finalVerticesLength = clippedVertices.length;
|
||||
finalIndices = clippedTriangles;
|
||||
finalIndicesLength = clippedTriangles.length;
|
||||
} else {
|
||||
let verts = vertices;
|
||||
for (
|
||||
let v = 2, u = 0, n = numFloats;
|
||||
v < n;
|
||||
v += vertexSize, u += 2
|
||||
) {
|
||||
verts[v] = color.r;
|
||||
verts[v + 1] = color.g;
|
||||
verts[v + 2] = color.b;
|
||||
verts[v + 3] = color.a;
|
||||
verts[v + 4] = uvs[u];
|
||||
verts[v + 5] = uvs[u + 1];
|
||||
}
|
||||
finalVertices = vertices;
|
||||
finalVerticesLength = numFloats;
|
||||
finalIndices = triangles;
|
||||
finalIndicesLength = triangles.length;
|
||||
}
|
||||
|
||||
if (finalVerticesLength == 0 || finalIndicesLength == 0) {
|
||||
clipper.clipEndWithSlot(slot);
|
||||
continue;
|
||||
}
|
||||
if (finalVerticesLength == 0 || finalIndicesLength == 0) {
|
||||
clipper.clipEndWithSlot(slot);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Start new batch if this one can't hold vertices/indices
|
||||
if (!batch.canBatch(finalVerticesLength / SkeletonMesh.VERTEX_SIZE, finalIndicesLength)) {
|
||||
batch.end();
|
||||
batch = this.nextBatch();
|
||||
batch.begin();
|
||||
}
|
||||
// Start new batch if this one can't hold vertices/indices
|
||||
if (
|
||||
!batch.canBatch(
|
||||
finalVerticesLength / SkeletonMesh.VERTEX_SIZE,
|
||||
finalIndicesLength
|
||||
)
|
||||
) {
|
||||
batch.end();
|
||||
batch = this.nextBatch();
|
||||
batch.begin();
|
||||
}
|
||||
|
||||
const slotBlendMode = slot.data.blendMode;
|
||||
const slotTexture = texture.texture;
|
||||
const materialGroup = batch.findMaterialGroup(slotTexture, slotBlendMode);
|
||||
const slotBlendMode = slot.data.blendMode;
|
||||
const slotTexture = texture.texture;
|
||||
const materialGroup = batch.findMaterialGroup(
|
||||
slotTexture,
|
||||
slotBlendMode
|
||||
);
|
||||
|
||||
batch.addMaterialGroup(finalIndicesLength, materialGroup);
|
||||
batch.batch(finalVertices, finalVerticesLength, finalIndices, finalIndicesLength, z);
|
||||
z += zOffset;
|
||||
}
|
||||
batch.addMaterialGroup(finalIndicesLength, materialGroup);
|
||||
batch.batch(
|
||||
finalVertices,
|
||||
finalVerticesLength,
|
||||
finalIndices,
|
||||
finalIndicesLength,
|
||||
z
|
||||
);
|
||||
z += zOffset;
|
||||
}
|
||||
|
||||
clipper.clipEndWithSlot(slot);
|
||||
}
|
||||
clipper.clipEnd();
|
||||
batch.end();
|
||||
}
|
||||
clipper.clipEndWithSlot(slot);
|
||||
}
|
||||
clipper.clipEnd();
|
||||
batch.end();
|
||||
}
|
||||
}
|
||||
|
||||
@ -88,7 +88,7 @@ export class LoadingScreen implements Disposable {
|
||||
|
||||
renderer.resize(ResizeMode.Expand);
|
||||
renderer.camera.position.set(canvas.width / 2, canvas.height / 2, 0);
|
||||
renderer.batcher.setBlendMode(gl.ONE, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
||||
renderer.batcher.setBlendMode(gl.ONE, gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
if (complete) {
|
||||
this.fadeOut += this.timeKeeper.delta * (this.timeKeeper.totalTime < 1 ? 2 : 1);
|
||||
|
||||
@ -39,7 +39,7 @@ export class PolygonBatcher implements Disposable {
|
||||
private context: ManagedWebGLRenderingContext;
|
||||
private drawCalls = 0;
|
||||
private static globalDrawCalls = 0;
|
||||
private isDrawing = false;
|
||||
isDrawing = false;
|
||||
private mesh: Mesh;
|
||||
private shader: Shader | null = null;
|
||||
private lastTexture: GLTexture | null = null;
|
||||
@ -47,7 +47,8 @@ export class PolygonBatcher implements Disposable {
|
||||
private indicesLength = 0;
|
||||
private srcColorBlend: number;
|
||||
private srcAlphaBlend: number;
|
||||
private dstBlend: number;
|
||||
private dstColorBlend: number;
|
||||
private dstAlphaBlend: number;
|
||||
private cullWasEnabled = false;
|
||||
|
||||
constructor (context: ManagedWebGLRenderingContext | WebGLRenderingContext, twoColorTint: boolean = true, maxVertices: number = 10920) {
|
||||
@ -60,7 +61,8 @@ export class PolygonBatcher implements Disposable {
|
||||
let gl = this.context.gl;
|
||||
this.srcColorBlend = gl.SRC_ALPHA;
|
||||
this.srcAlphaBlend = gl.ONE;
|
||||
this.dstBlend = gl.ONE_MINUS_SRC_ALPHA;
|
||||
this.dstColorBlend = gl.ONE_MINUS_SRC_ALPHA;
|
||||
this.dstAlphaBlend = gl.ONE_MINUS_SRC_ALPHA;
|
||||
}
|
||||
|
||||
begin (shader: Shader) {
|
||||
@ -72,7 +74,7 @@ export class PolygonBatcher implements Disposable {
|
||||
|
||||
let gl = this.context.gl;
|
||||
gl.enable(gl.BLEND);
|
||||
gl.blendFuncSeparate(this.srcColorBlend, this.dstBlend, this.srcAlphaBlend, this.dstBlend);
|
||||
gl.blendFuncSeparate(this.srcColorBlend, this.dstColorBlend, this.srcAlphaBlend, this.dstAlphaBlend);
|
||||
|
||||
if (PolygonBatcher.disableCulling) {
|
||||
this.cullWasEnabled = gl.isEnabled(gl.CULL_FACE);
|
||||
@ -80,15 +82,16 @@ export class PolygonBatcher implements Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
setBlendMode (srcColorBlend: number, srcAlphaBlend: number, dstBlend: number) {
|
||||
if (this.srcColorBlend == srcColorBlend && this.srcAlphaBlend == srcAlphaBlend && this.dstBlend == dstBlend) return;
|
||||
setBlendMode (srcColorBlend: number, srcAlphaBlend: number, dstColorBlend: number, dstAlphaBlend: number) {
|
||||
if (this.srcColorBlend == srcColorBlend && this.srcAlphaBlend == srcAlphaBlend && this.dstColorBlend == dstColorBlend && this.dstAlphaBlend == dstAlphaBlend) return;
|
||||
this.srcColorBlend = srcColorBlend;
|
||||
this.srcAlphaBlend = srcAlphaBlend;
|
||||
this.dstBlend = dstBlend;
|
||||
this.dstColorBlend = dstColorBlend;
|
||||
this.dstAlphaBlend = dstAlphaBlend;
|
||||
if (this.isDrawing) {
|
||||
this.flush();
|
||||
let gl = this.context.gl;
|
||||
gl.blendFuncSeparate(srcColorBlend, dstBlend, srcAlphaBlend, dstBlend);
|
||||
gl.blendFuncSeparate(srcColorBlend, dstColorBlend, srcAlphaBlend, dstAlphaBlend);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -43,7 +43,8 @@ export class ShapeRenderer implements Disposable {
|
||||
private tmp = new Vector2();
|
||||
private srcColorBlend: number;
|
||||
private srcAlphaBlend: number;
|
||||
private dstBlend: number;
|
||||
private dstColorBlend: number;
|
||||
private dstAlphaBlend: number;
|
||||
|
||||
constructor (context: ManagedWebGLRenderingContext | WebGLRenderingContext, maxVertices: number = 10920) {
|
||||
if (maxVertices > 10920) throw new Error("Can't have more than 10920 triangles per batch: " + maxVertices);
|
||||
@ -52,7 +53,8 @@ export class ShapeRenderer implements Disposable {
|
||||
let gl = this.context.gl;
|
||||
this.srcColorBlend = gl.SRC_ALPHA;
|
||||
this.srcAlphaBlend = gl.ONE;
|
||||
this.dstBlend = gl.ONE_MINUS_SRC_ALPHA;
|
||||
this.dstColorBlend = gl.ONE_MINUS_SRC_ALPHA;
|
||||
this.dstAlphaBlend = gl.ONE_MINUS_SRC_ALPHA;
|
||||
}
|
||||
|
||||
begin (shader: Shader) {
|
||||
@ -63,17 +65,18 @@ export class ShapeRenderer implements Disposable {
|
||||
|
||||
let gl = this.context.gl;
|
||||
gl.enable(gl.BLEND);
|
||||
gl.blendFuncSeparate(this.srcColorBlend, this.dstBlend, this.srcAlphaBlend, this.dstBlend);
|
||||
gl.blendFuncSeparate(this.srcColorBlend, this.dstColorBlend, this.srcAlphaBlend, this.dstAlphaBlend);
|
||||
}
|
||||
|
||||
setBlendMode (srcColorBlend: number, srcAlphaBlend: number, dstBlend: number) {
|
||||
setBlendMode (srcColorBlend: number, srcAlphaBlend: number, dstColorBlend: number, dstAlphaBlend: number) {
|
||||
this.srcColorBlend = srcColorBlend;
|
||||
this.srcAlphaBlend = srcAlphaBlend;
|
||||
this.dstBlend = dstBlend;
|
||||
this.dstColorBlend = dstColorBlend;
|
||||
this.dstAlphaBlend = dstAlphaBlend;
|
||||
if (this.isDrawing) {
|
||||
this.flush();
|
||||
let gl = this.context.gl;
|
||||
gl.blendFuncSeparate(srcColorBlend, dstBlend, srcAlphaBlend, dstBlend);
|
||||
gl.blendFuncSeparate(srcColorBlend, dstColorBlend, srcAlphaBlend, dstAlphaBlend);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -67,7 +67,7 @@ export class SkeletonDebugRenderer implements Disposable {
|
||||
let skeletonY = skeleton.y;
|
||||
let gl = this.context.gl;
|
||||
let srcFunc = this.premultipliedAlpha ? gl.ONE : gl.SRC_ALPHA;
|
||||
shapes.setBlendMode(srcFunc, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
||||
shapes.setBlendMode(srcFunc, gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
let bones = skeleton.bones;
|
||||
if (this.drawBones) {
|
||||
|
||||
@ -168,8 +168,9 @@ export class SkeletonRenderer {
|
||||
blendMode = slotBlendMode;
|
||||
batcher.setBlendMode(
|
||||
WebGLBlendModeConverter.getSourceColorGLBlendMode(blendMode, premultipliedAlpha),
|
||||
WebGLBlendModeConverter.getSourceAlphaGLBlendMode(blendMode),
|
||||
WebGLBlendModeConverter.getDestGLBlendMode(blendMode));
|
||||
WebGLBlendModeConverter.getSourceAlphaGLBlendMode(blendMode, premultipliedAlpha),
|
||||
WebGLBlendModeConverter.getDestColorGLBlendMode(blendMode),
|
||||
WebGLBlendModeConverter.getDestAlphaGLBlendMode(blendMode, premultipliedAlpha) );
|
||||
}
|
||||
|
||||
if (clipper.isClipping()) {
|
||||
|
||||
@ -71,6 +71,7 @@ const ONE_MINUS_DST_ALPHA = 0x0305;
|
||||
const DST_COLOR = 0x0306;
|
||||
|
||||
export class WebGLBlendModeConverter {
|
||||
|
||||
static getDestGLBlendMode (blendMode: BlendMode) {
|
||||
switch (blendMode) {
|
||||
case BlendMode.Normal: return ONE_MINUS_SRC_ALPHA;
|
||||
@ -81,22 +82,42 @@ export class WebGLBlendModeConverter {
|
||||
}
|
||||
}
|
||||
|
||||
static getDestColorGLBlendMode (blendMode: BlendMode) {
|
||||
switch (blendMode) {
|
||||
case BlendMode.Normal: return ONE_MINUS_SRC_ALPHA;
|
||||
case BlendMode.Additive: return ONE;
|
||||
case BlendMode.Multiply: return ONE_MINUS_SRC_ALPHA;
|
||||
case BlendMode.Screen: return ONE_MINUS_SRC_COLOR;
|
||||
default: throw new Error("Unknown blend mode: " + blendMode);
|
||||
}
|
||||
}
|
||||
|
||||
static getDestAlphaGLBlendMode (blendMode: BlendMode, premultipliedAlpha: boolean = false) {
|
||||
switch (blendMode) {
|
||||
case BlendMode.Normal: return ONE_MINUS_SRC_ALPHA;
|
||||
case BlendMode.Additive: return premultipliedAlpha ? ONE_MINUS_SRC_ALPHA : ONE;
|
||||
case BlendMode.Multiply: return ONE_MINUS_SRC_ALPHA;
|
||||
case BlendMode.Screen: return ONE_MINUS_SRC_ALPHA;
|
||||
default: throw new Error("Unknown blend mode: " + blendMode);
|
||||
}
|
||||
}
|
||||
|
||||
static getSourceColorGLBlendMode (blendMode: BlendMode, premultipliedAlpha: boolean = false) {
|
||||
switch (blendMode) {
|
||||
case BlendMode.Normal: return premultipliedAlpha ? ONE : SRC_ALPHA;
|
||||
case BlendMode.Additive: return premultipliedAlpha ? ONE : SRC_ALPHA;
|
||||
case BlendMode.Multiply: return DST_COLOR;
|
||||
case BlendMode.Screen: return ONE;
|
||||
case BlendMode.Screen: return premultipliedAlpha ? ONE : SRC_ALPHA;
|
||||
default: throw new Error("Unknown blend mode: " + blendMode);
|
||||
}
|
||||
}
|
||||
|
||||
static getSourceAlphaGLBlendMode (blendMode: BlendMode) {
|
||||
static getSourceAlphaGLBlendMode (blendMode: BlendMode, premultipliedAlpha: boolean = false) {
|
||||
switch (blendMode) {
|
||||
case BlendMode.Normal: return ONE;
|
||||
case BlendMode.Additive: return ONE;
|
||||
case BlendMode.Multiply: return ONE_MINUS_SRC_ALPHA;
|
||||
case BlendMode.Screen: return ONE_MINUS_SRC_COLOR;
|
||||
case BlendMode.Normal: return premultipliedAlpha ? SRC_ALPHA : ONE;
|
||||
case BlendMode.Additive: return premultipliedAlpha ? SRC_ALPHA : ONE;
|
||||
case BlendMode.Multiply: return ONE;
|
||||
case BlendMode.Screen: return ONE;
|
||||
default: throw new Error("Unknown blend mode: " + blendMode);
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,15 +14,15 @@ ASpineboyCppPawn::ASpineboyCppPawn() {
|
||||
// Called when the game starts or when spawned
|
||||
void ASpineboyCppPawn::BeginPlay() {
|
||||
Super::BeginPlay();
|
||||
USpineSkeletonAnimationComponent *animation = FindComponentByClass<USpineSkeletonAnimationComponent>();
|
||||
animation->SetAnimation(0, FString("walk"), true);
|
||||
USpineSkeletonAnimationComponent *animationComponent = FindComponentByClass<USpineSkeletonAnimationComponent>();
|
||||
animationComponent->SetAnimation(0, FString("walk"), true);
|
||||
}
|
||||
|
||||
// Called every frame
|
||||
void ASpineboyCppPawn::Tick(float DeltaTime) {
|
||||
Super::Tick(DeltaTime);
|
||||
USpineSkeletonAnimationComponent *animation = FindComponentByClass<USpineSkeletonAnimationComponent>();
|
||||
spine::AnimationState *state = animation->GetAnimationState();
|
||||
USpineSkeletonAnimationComponent *animationComponent = FindComponentByClass<USpineSkeletonAnimationComponent>();
|
||||
spine::AnimationState *state = animationComponent->GetAnimationState();
|
||||
spine::TrackEntry *entry = state->getCurrent(0);
|
||||
if (entry) {
|
||||
GEngine->AddOnScreenDebugMessage(-1, 0.5f, FColor::Yellow, FString(entry->getAnimation()->getName().buffer()));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user