mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-04 14:24:53 +08:00
186 lines
6.1 KiB
HTML
186 lines
6.1 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
</head>
|
|
<script src="../dist/iife/spine-webgl.js"></script>
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
</style>
|
|
|
|
<body>
|
|
<canvas id="canvas" style="position: absolute; width: 100%; height: 100%;"></canvas>
|
|
<div style="position: absolute; color: white;">
|
|
Red: <input id="red-input" type="range" min="0" max="100" value="100" /> <output id="red-value"></output>
|
|
<br>
|
|
Green: <input id="green-input" type="range" min="0" max="100" value="100" /> <output id="green-value"></output>
|
|
<br>
|
|
Blue: <input id="blue-input" type="range" min="0" max="100" value="100" /> <output id="blue-value"></output>
|
|
</div>
|
|
|
|
<script>
|
|
class App {
|
|
constructor() {
|
|
this.skeleton = null;
|
|
this.animationState = null;
|
|
this.shaderValues = { r: 0, g: 0, b: 0, growing: true, color: "r" };
|
|
}
|
|
|
|
loadAssets(canvas) {
|
|
// Load the skeleton file.
|
|
canvas.assetManager.loadBinary("assets/raptor-pro.skel");
|
|
// Load the atlas and its pages.
|
|
canvas.assetManager.loadTextureAtlas("assets/raptor-pma.atlas");
|
|
}
|
|
|
|
initialize(canvas) {
|
|
let assetManager = canvas.assetManager;
|
|
|
|
// Create the texture atlas.
|
|
var atlas = assetManager.require("assets/raptor-pma.atlas");
|
|
|
|
// Create a AtlasAttachmentLoader that resolves region, mesh, boundingbox and path attachments
|
|
var atlasLoader = new spine.AtlasAttachmentLoader(atlas);
|
|
|
|
// Create a SkeletonBinary instance for parsing the .skel file.
|
|
var skeletonBinary = new spine.SkeletonBinary(atlasLoader);
|
|
|
|
// Set the scale to apply during parsing, parse the file, and create a new skeleton.
|
|
skeletonBinary.scale = 0.6;
|
|
var skeletonData = skeletonBinary.readSkeletonData(assetManager.require("assets/raptor-pro.skel"));
|
|
this.skeleton = new spine.Skeleton(skeletonData);
|
|
|
|
// Create an AnimationState, and set the "cape-follow-example" animation in looping mode.
|
|
var animationStateData = new spine.AnimationStateData(skeletonData);
|
|
this.animationState = new spine.AnimationState(animationStateData);
|
|
this.animationState.setAnimation(0, "walk", true);
|
|
|
|
// Center the camera on the skeleton
|
|
const offset = new spine.Vector2();
|
|
const size = new spine.Vector2();
|
|
this.skeleton.setToSetupPose();
|
|
this.skeleton.update(0);
|
|
this.skeleton.updateWorldTransform(spine.Physics.update);
|
|
this.skeleton.getBounds(offset, size);
|
|
canvas.renderer.camera.position.x = offset.x + size.x / 2;
|
|
canvas.renderer.camera.position.y = offset.y + size.y / 2;
|
|
}
|
|
|
|
update(canvas, delta) {
|
|
// Update the animation state using the delta time.
|
|
this.animationState.update(delta);
|
|
// Apply the animation state to the skeleton.
|
|
this.animationState.apply(this.skeleton);
|
|
// Let the skeleton update the transforms of its bones and apply physics
|
|
this.skeleton.update(delta);
|
|
this.skeleton.updateWorldTransform(spine.Physics.update);
|
|
}
|
|
|
|
render(canvas) {
|
|
let renderer = canvas.renderer;
|
|
// Resize the viewport to the full canvas.
|
|
renderer.resize(spine.ResizeMode.Expand);
|
|
|
|
// Clear the canvas with a light gray color.
|
|
canvas.clear(0.2, 0.2, 0.2, 1);
|
|
|
|
// Begin rendering.
|
|
renderer.begin();
|
|
|
|
// updateShader(canvas.shader, this.shaderValues);
|
|
|
|
// Draw the skeleton
|
|
renderer.drawSkeleton(this.skeleton, true);
|
|
// Complete rendering.
|
|
renderer.end();
|
|
}
|
|
}
|
|
|
|
// Vertex and Fragment shader has been copied from src/Shader.ts#newTwoColoredTextured
|
|
// Fragment shader has been slightly changed to add uniform red_multiplier, green_multiplier, blue_multiplier uniforms
|
|
// that are used to multiply the rgb colors
|
|
let vertexShader = `
|
|
attribute vec4 ${spine.Shader.POSITION};
|
|
attribute vec4 ${spine.Shader.COLOR};
|
|
attribute vec4 ${spine.Shader.COLOR2};
|
|
attribute vec2 ${spine.Shader.TEXCOORDS};
|
|
uniform mat4 ${spine.Shader.MVP_MATRIX};
|
|
varying vec4 v_light;
|
|
varying vec4 v_dark;
|
|
varying vec2 v_texCoords;
|
|
|
|
void main () {
|
|
v_light = ${spine.Shader.COLOR};
|
|
v_dark = ${spine.Shader.COLOR2};
|
|
v_texCoords = ${spine.Shader.TEXCOORDS};
|
|
gl_Position = ${spine.Shader.MVP_MATRIX} * ${spine.Shader.POSITION};
|
|
}
|
|
`;
|
|
|
|
let fragmentShader = `
|
|
#ifdef GL_ES
|
|
#define LOWP lowp
|
|
precision mediump float;
|
|
#else
|
|
#define LOWP
|
|
#endif
|
|
varying LOWP vec4 v_light;
|
|
varying LOWP vec4 v_dark;
|
|
varying vec2 v_texCoords;
|
|
uniform sampler2D u_texture;
|
|
uniform float red_multiplier;
|
|
uniform float green_multiplier;
|
|
uniform float blue_multiplier;
|
|
|
|
void main () {
|
|
vec4 texColor = texture2D(u_texture, v_texCoords);
|
|
gl_FragColor.a = texColor.a * v_light.a;
|
|
vec3 multipliers = vec3(clamp(red_multiplier, 0.0, 1.0), clamp(green_multiplier, 0.0, 1.0), clamp(blue_multiplier, 0.0, 1.0));
|
|
gl_FragColor.rgb = ((texColor.a - 1.0) * v_dark.a + 1.0 - texColor.rgb) * v_dark.rgb + texColor.rgb * v_light.rgb * multipliers;
|
|
}
|
|
`;
|
|
|
|
const inputToValue = input => parseFloat(input) / 100;
|
|
const redInput = document.querySelector("#red-input");
|
|
const redValue = document.querySelector("#red-value");
|
|
redValue.textContent = redInput.value;
|
|
|
|
const greenInput = document.querySelector("#green-input");
|
|
const greenValue = document.querySelector("#green-value");
|
|
greenValue.textContent = greenInput.value;
|
|
|
|
const blueInput = document.querySelector("#blue-input");
|
|
const blueValue = document.querySelector("#blue-value");
|
|
blueValue.textContent = blueInput.value;
|
|
|
|
redInput.addEventListener("input", (event) => {
|
|
redValue.textContent = event.target.value;
|
|
});
|
|
greenInput.addEventListener("input", (event) => {
|
|
greenValue.textContent = event.target.value;
|
|
});
|
|
blueInput.addEventListener("input", (event) => {
|
|
blueValue.textContent = event.target.value;
|
|
});
|
|
|
|
const shader = {
|
|
vertexShader,
|
|
fragmentShader,
|
|
setUniformCallback: (shader) => {
|
|
shader.setUniformf("red_multiplier", inputToValue(redInput.value));
|
|
shader.setUniformf("green_multiplier", inputToValue(greenInput.value));
|
|
shader.setUniformf("blue_multiplier", inputToValue(blueInput.value));
|
|
}
|
|
}
|
|
const app = new spine.SpineCanvas(document.getElementById("canvas"), {
|
|
app: new App(),
|
|
shader,
|
|
})
|
|
</script>
|
|
</body>
|
|
|
|
</html> |