|threejs] Add logarithmic depth buffer example.

This commit is contained in:
Mario Zechner 2023-09-04 09:54:05 +02:00
parent c9fb1c372c
commit 8e84ad990d
4 changed files with 712 additions and 380 deletions

View File

@ -1,79 +1,158 @@
<!DOCTYPE html> <!DOCTYPE html>
<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> <body>
<meta charset='utf-8'> <h1>spine-ts Examples</h1>
<meta http-equiv='X-UA-Compatible' content='IE=edge'> <ul>
<title>spine-ts Examples</title> <li>Canvas</li>
<meta name='viewport' content='width=device-width, initial-scale=1'> <ul>
</head> <li><a href="/spine-canvas/example">Example</a></li>
<li>
<body> <a href="/spine-canvas/example/mouse-click.html">Mouse click</a>
<h1>spine-ts Examples</h1> </li>
<ul> </ul>
<li>Canvas</li> <li>Pixi</li>
<ul> <ul>
<li><a href="/spine-canvas/example">Example</a></li> <li><a href="/spine-pixi/example/index.html">Basic example</a></li>
<li><a href="/spine-canvas/example/mouse-click.html">Mouse click</a></li> <li>
</ul> <a href="/spine-pixi/example/events-example.html">Events example</a>
<li>Pixi</li> </li>
<ul> </ul>
<li><a href="/spine-pixi/example/index.html">Basic example</a></li> <li>Phaser</li>
<li><a href="/spine-pixi/example/events-example.html">Events example</a></li> <ul>
</ul> <li>
<li>Phaser</li> <a href="/spine-phaser/example/basic-example.html">Basic example</a>
<ul> </li>
<li><a href="/spine-phaser/example/basic-example.html">Basic example</a></li> <li>
<li><a href="/spine-phaser/example/events-example.html">Events example</a></li> <a href="/spine-phaser/example/events-example.html">Events example</a>
<li><a href="/spine-phaser/example/mix-and-match-example.html">Mix and match example</a></li> </li>
<li><a href="/spine-phaser/example/arcade-physics-example.html">Arcade physics example</a></li> <li>
<li><a href="/spine-phaser/example/control-bones-example.html">Control bones example</a></li> <a href="/spine-phaser/example/mix-and-match-example.html"
<li><a href="/spine-phaser/example/batching-test.html">Batching test</a></li> >Mix and match example</a
<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>
<li><a href="/spine-phaser/example/visibility-test.html">Visibility test</a></li> <li>
<li><a href="/spine-phaser/example/blend-test.html">Blend test</a></li> <a href="/spine-phaser/example/arcade-physics-example.html"
<li><a href="/spine-phaser/example/camera-pipeline-test.html">Camera pipeline test</a></li> >Arcade physics example</a
<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>
<li><a href="/spine-phaser/example/depth-test.html">Depth test</a></li> <li>
<li><a href="/spine-phaser/example/render-to-texture-test.html">Render to texture test</a></li> <a href="/spine-phaser/example/control-bones-example.html"
</ul> >Control bones example</a
<li>Player</li> >
<ul> </li>
<li><a href="/spine-player/example/example.html">Example</a></li> <li>
<li><a href="/spine-player/example/embedding-binary-example.html">Embedding binary</a></li> <a href="/spine-phaser/example/batching-test.html">Batching test</a>
<li><a href="/spine-player/example/embedding-json-example.html">Embedding JSON</a></li> </li>
<li><a href="/spine-player/example/dispose.html">Disposing a player</a></li> <li>
</ul> <a href="/spine-phaser/example/multi-scene-test.html"
<li>WebGL</li> >Multi-scene test</a
<ul> >
<li><a href="/spine-webgl/example">Example</a></li> </li>
<li><a href="/spine-webgl/example/barebones.html">Barebones</a></li> <li>
<li><a href="/spine-webgl/example/mix-and-match.html">Mix &amp; match</a></li> <a href="/spine-phaser/example/bounds-test.html">Bounds test</a>
<li><a href="/spine-webgl/example/custom-attachment.html">Custom attachment</a></li> </li>
<li><a href="/spine-webgl/example/drag-and-drop.html">Drag &amp; drop</a></li> <li>
<li><a href="/spine-webgl/example/dress-up.html">Dress-up</a></li> <a href="/spine-phaser/example/visibility-test.html"
<li><a href="/spine-webgl/example/bone-dragging.html">Bone dragging</a></li> >Visibility test</a
<li><a href="/spine-webgl/demos/additiveblending.html">Additive blending</a></li> >
<li><a href="/spine-webgl/demos/clipping.html">Clipping</a></li> </li>
<li><a href="/spine-webgl/demos/hoverboard.html">Hoverboard</a></li> <li><a href="/spine-phaser/example/blend-test.html">Blend test</a></li>
<li><a href="/spine-webgl/demos/imagechanges.html">Image changes</a></li> <li>
<li><a href="/spine-webgl/demos/meshes.html">Meshes</a></li> <a href="/spine-phaser/example/camera-pipeline-test.html"
<li><a href="/spine-webgl/demos/skins.html">Skins</a></li> >Camera pipeline test</a
<li><a href="/spine-webgl/demos/spritesheets.html">Spritesheets</a></li> >
<li><a href="/spine-webgl/demos/stretchyman.html">Stretchyman</a></li> </li>
<li><a href="/spine-webgl/demos/tank.html">Tank</a></li> <li>
<li><a href="/spine-webgl/demos/transforms.html">Transforms</a></li> <a href="/spine-phaser/example/extended-class-test.html"
<li><a href="/spine-webgl/demos/transitions.html">Transitions</a></li> >Extended class</a
<li><a href="/spine-webgl/demos/vine.html">Vine</a></li> >
</ul> </li>
<li>THREE.JS</li> <li>
<ul> <a href="/spine-phaser/example/canvas-test.html">Canvas test</a>
<li><a href="/spine-threejs/example/index.html">Example</a></li> </li>
<li><a href="/spine-threejs/example/coordinate-transform.html">Coordinate transform</a></li> <li><a href="/spine-phaser/example/depth-test.html">Depth test</a></li>
</ul> <li>
</ul> <a href="/spine-phaser/example/render-to-texture-test.html"
</body> >Render to texture test</a
>
</html> </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 &amp; 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 &amp; 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>

View File

@ -1,141 +1,150 @@
<html> <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> body,
<meta charset="UTF-8"> html {
<title>spine-threejs</title> height: 100%;
<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, canvas {
html { position: absolute;
height: 100% width: 100%;
} height: 100%;
}
</style>
canvas { <body>
position: absolute; <script>
width: 100%; (function () {
height: 100%; let scene, camera, renderer;
} let geometry, material, mesh, skeletonMesh;
</style> let assetManager;
let canvas;
let controls;
let lastFrameTime = Date.now() / 1000;
<body> let baseUrl = "assets/";
<script> let skeletonFile = "raptor-pro.json";
(function () { let atlasFile = skeletonFile
let scene, camera, renderer; .replace("-pro", "")
let geometry, material, mesh, skeletonMesh; .replace("-ess", "")
let assetManager; .replace(".json", ".atlas");
let canvas; let animation = "walk";
let controls;
let lastFrameTime = Date.now() / 1000;
let baseUrl = "assets/"; function init() {
let skeletonFile = "raptor-pro.json"; // create the THREE.JS camera, scene and renderer (WebGL)
let atlasFile = skeletonFile.replace("-pro", "").replace("-ess", "").replace(".json", ".atlas"); let width = window.innerWidth,
let animation = "walk"; 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() { // load the assets required to display the Raptor model
// create the THREE.JS camera, scene and renderer (WebGL) assetManager = new spine.AssetManager(baseUrl);
let width = window.innerWidth, height = window.innerHeight; assetManager.loadText(skeletonFile);
camera = new THREE.PerspectiveCamera(75, width / height, 1, 3000); assetManager.loadTextureAtlas(atlasFile);
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 requestAnimationFrame(load);
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);
function load(name, scale) { // Load the texture atlas using name.atlas and name.png from the AssetManager.
if (assetManager.isLoadingComplete()) { // The function passed to TextureAtlas is used to resolve relative paths.
// Add a box to the scene to which we attach the skeleton mesh atlas = assetManager.require(atlasFile);
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. // Create a AtlasAttachmentLoader that resolves region, mesh, boundingbox and path attachments
// The function passed to TextureAtlas is used to resolve relative paths. atlasLoader = new spine.AtlasAttachmentLoader(atlas);
atlas = assetManager.require(atlasFile);
// Create a AtlasAttachmentLoader that resolves region, mesh, boundingbox and path attachments // Create a SkeletonJson instance for parsing the .json file.
atlasLoader = new spine.AtlasAttachmentLoader(atlas); let skeletonJson = new spine.SkeletonJson(atlasLoader);
// Create a SkeletonJson instance for parsing the .json file. // Set the scale to apply during parsing, parse the file, and create a new skeleton.
let skeletonJson = new spine.SkeletonJson(atlasLoader); 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. // Create a SkeletonMesh from the data and attach it to the scene
skeletonJson.scale = 0.4; skeletonMesh = new spine.SkeletonMesh(
let skeletonData = skeletonJson.readSkeletonData(assetManager.require(skeletonFile)); 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 requestAnimationFrame(render);
skeletonMesh = new spine.SkeletonMesh(skeletonData, (parameters) => { } else requestAnimationFrame(load);
parameters.depthTest = true; }
parameters.depthWrite = true;
parameters.alphaTest = 0.001;
});
skeletonMesh.state.setAnimation(0, animation, true);
mesh.add(skeletonMesh);
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); // resize canvas to use full page, adjust camera/renderer
} else requestAnimationFrame(load); resize();
}
let lastTime = Date.now(); // Update orbital controls
function render() { controls.update();
// 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 // update the animation
resize(); skeletonMesh.update(delta);
// Update orbital controls // render the scene
controls.update(); renderer.render(scene, camera);
// update the animation requestAnimationFrame(render);
skeletonMesh.update(delta); }
// render the scene function resize() {
renderer.render(scene, camera); 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() { renderer.setSize(w, h);
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; init();
camera.updateProjectionMatrix(); })();
</script>
renderer.setSize(w, h); </body>
} </html>
init();
}());
</script>
</body>
</html>

View 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 = vec4(1, 0, 1, 1); // 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>

View File

@ -27,16 +27,33 @@
* SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * 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 { MeshBatcher } from "./MeshBatcher";
import * as THREE from "three"; import * as THREE from "three";
import { ThreeJsTexture } from "./ThreeJsTexture"; import { ThreeJsTexture } from "./ThreeJsTexture";
export type SkeletonMeshMaterialParametersCustomizer = (materialParameters: THREE.ShaderMaterialParameters) => void; export type SkeletonMeshMaterialParametersCustomizer = (
materialParameters: THREE.ShaderMaterialParameters
) => void;
export class SkeletonMeshMaterial extends THREE.ShaderMaterial { export class SkeletonMeshMaterial extends THREE.ShaderMaterial {
constructor (customizer: SkeletonMeshMaterialParametersCustomizer) { constructor(customizer: SkeletonMeshMaterialParametersCustomizer) {
let vertexShader = ` let vertexShader = `
attribute vec4 color; attribute vec4 color;
varying vec2 vUv; varying vec2 vUv;
varying vec4 vColor; varying vec4 vColor;
@ -46,7 +63,7 @@ export class SkeletonMeshMaterial extends THREE.ShaderMaterial {
gl_Position = projectionMatrix*modelViewMatrix*vec4(position,1.0); gl_Position = projectionMatrix*modelViewMatrix*vec4(position,1.0);
} }
`; `;
let fragmentShader = ` let fragmentShader = `
uniform sampler2D map; uniform sampler2D map;
#ifdef USE_SPINE_ALPHATEST #ifdef USE_SPINE_ALPHATEST
uniform float alphaTest; uniform float alphaTest;
@ -61,212 +78,253 @@ export class SkeletonMeshMaterial extends THREE.ShaderMaterial {
} }
`; `;
let parameters: THREE.ShaderMaterialParameters = { let parameters: THREE.ShaderMaterialParameters = {
uniforms: { uniforms: {
map: { value: null }, map: { value: null },
}, },
vertexShader: vertexShader, vertexShader: vertexShader,
fragmentShader: fragmentShader, fragmentShader: fragmentShader,
side: THREE.DoubleSide, side: THREE.DoubleSide,
transparent: true, transparent: true,
depthWrite: false, depthWrite: true,
alphaTest: 0.0 alphaTest: 0.0,
}; };
customizer(parameters); customizer(parameters);
if (parameters.alphaTest && parameters.alphaTest > 0) { if (parameters.alphaTest && parameters.alphaTest > 0) {
parameters.defines = { "USE_SPINE_ALPHATEST": 1 }; parameters.defines = { USE_SPINE_ALPHATEST: 1 };
if (!parameters.uniforms) parameters.uniforms = {}; if (!parameters.uniforms) parameters.uniforms = {};
parameters.uniforms["alphaTest"] = { value: parameters.alphaTest }; parameters.uniforms["alphaTest"] = { value: parameters.alphaTest };
} }
super(parameters); super(parameters);
}; }
} }
export class SkeletonMesh extends THREE.Object3D { export class SkeletonMesh extends THREE.Object3D {
tempPos: Vector2 = new Vector2(); tempPos: Vector2 = new Vector2();
tempUv: Vector2 = new Vector2(); tempUv: Vector2 = new Vector2();
tempLight = new Color(); tempLight = new Color();
tempDark = new Color(); tempDark = new Color();
skeleton: Skeleton; skeleton: Skeleton;
state: AnimationState; state: AnimationState;
zOffset: number = 0.1; zOffset: number = 0.1;
private batches = new Array<MeshBatcher>(); private batches = new Array<MeshBatcher>();
private nextBatchIndex = 0; private nextBatchIndex = 0;
private clipper: SkeletonClipping = new SkeletonClipping(); private clipper: SkeletonClipping = new SkeletonClipping();
static QUAD_TRIANGLES = [0, 1, 2, 2, 3, 0]; static QUAD_TRIANGLES = [0, 1, 2, 2, 3, 0];
static VERTEX_SIZE = 2 + 2 + 4; static VERTEX_SIZE = 2 + 2 + 4;
private vertices = Utils.newFloatArray(1024); private vertices = Utils.newFloatArray(1024);
private tempColor = new Color(); private tempColor = new Color();
constructor (skeletonData: SkeletonData, private materialCustomerizer: SkeletonMeshMaterialParametersCustomizer = (material) => { }) { constructor(
super(); skeletonData: SkeletonData,
private materialCustomerizer: SkeletonMeshMaterialParametersCustomizer = (
material
) => {}
) {
super();
this.skeleton = new Skeleton(skeletonData); this.skeleton = new Skeleton(skeletonData);
let animData = new AnimationStateData(skeletonData); let animData = new AnimationStateData(skeletonData);
this.state = new AnimationState(animData); this.state = new AnimationState(animData);
} }
update (deltaTime: number) { update(deltaTime: number) {
let state = this.state; let state = this.state;
let skeleton = this.skeleton; let skeleton = this.skeleton;
state.update(deltaTime); state.update(deltaTime);
state.apply(skeleton); state.apply(skeleton);
skeleton.updateWorldTransform(); skeleton.updateWorldTransform();
this.updateGeometry(); this.updateGeometry();
} }
dispose () { dispose() {
for (var i = 0; i < this.batches.length; i++) { for (var i = 0; i < this.batches.length; i++) {
this.batches[i].dispose(); this.batches[i].dispose();
} }
} }
private clearBatches () { private clearBatches() {
for (var i = 0; i < this.batches.length; i++) { for (var i = 0; i < this.batches.length; i++) {
this.batches[i].clear(); this.batches[i].clear();
this.batches[i].visible = false; this.batches[i].visible = false;
} }
this.nextBatchIndex = 0; this.nextBatchIndex = 0;
} }
private nextBatch () { private nextBatch() {
if (this.batches.length == this.nextBatchIndex) { if (this.batches.length == this.nextBatchIndex) {
let batch = new MeshBatcher(10920, this.materialCustomerizer); let batch = new MeshBatcher(10920, this.materialCustomerizer);
this.add(batch); this.add(batch);
this.batches.push(batch); this.batches.push(batch);
} }
let batch = this.batches[this.nextBatchIndex++]; let batch = this.batches[this.nextBatchIndex++];
batch.visible = true; batch.visible = true;
return batch; return batch;
} }
private updateGeometry () { private updateGeometry() {
this.clearBatches(); this.clearBatches();
let tempPos = this.tempPos; let tempPos = this.tempPos;
let tempUv = this.tempUv; let tempUv = this.tempUv;
let tempLight = this.tempLight; let tempLight = this.tempLight;
let tempDark = this.tempDark; let tempDark = this.tempDark;
let clipper = this.clipper; let clipper = this.clipper;
let vertices: NumberArrayLike = this.vertices; let vertices: NumberArrayLike = this.vertices;
let triangles: Array<number> | null = null; let triangles: Array<number> | null = null;
let uvs: NumberArrayLike | null = null; let uvs: NumberArrayLike | null = null;
let drawOrder = this.skeleton.drawOrder; let drawOrder = this.skeleton.drawOrder;
let batch = this.nextBatch(); let batch = this.nextBatch();
batch.begin(); batch.begin();
let z = 0; let z = 0;
let zOffset = this.zOffset; let zOffset = this.zOffset;
for (let i = 0, n = drawOrder.length; i < n; i++) { for (let i = 0, n = drawOrder.length; i < n; i++) {
let vertexSize = clipper.isClipping() ? 2 : SkeletonMesh.VERTEX_SIZE; let vertexSize = clipper.isClipping() ? 2 : SkeletonMesh.VERTEX_SIZE;
let slot = drawOrder[i]; let slot = drawOrder[i];
if (!slot.bone.active) { if (!slot.bone.active) {
clipper.clipEndWithSlot(slot); clipper.clipEndWithSlot(slot);
continue; continue;
} }
let attachment = slot.getAttachment(); let attachment = slot.getAttachment();
let attachmentColor: Color | null; let attachmentColor: Color | null;
let texture: ThreeJsTexture | null; let texture: ThreeJsTexture | null;
let numFloats = 0; let numFloats = 0;
if (attachment instanceof RegionAttachment) { if (attachment instanceof RegionAttachment) {
let region = <RegionAttachment>attachment; let region = <RegionAttachment>attachment;
attachmentColor = region.color; attachmentColor = region.color;
vertices = this.vertices; vertices = this.vertices;
numFloats = vertexSize * 4; numFloats = vertexSize * 4;
region.computeWorldVertices(slot, vertices, 0, vertexSize); region.computeWorldVertices(slot, vertices, 0, vertexSize);
triangles = SkeletonMesh.QUAD_TRIANGLES; triangles = SkeletonMesh.QUAD_TRIANGLES;
uvs = region.uvs; uvs = region.uvs;
texture = <ThreeJsTexture>region.region!.texture; texture = <ThreeJsTexture>region.region!.texture;
} else if (attachment instanceof MeshAttachment) { } else if (attachment instanceof MeshAttachment) {
let mesh = <MeshAttachment>attachment; let mesh = <MeshAttachment>attachment;
attachmentColor = mesh.color; attachmentColor = mesh.color;
vertices = this.vertices; vertices = this.vertices;
numFloats = (mesh.worldVerticesLength >> 1) * vertexSize; numFloats = (mesh.worldVerticesLength >> 1) * vertexSize;
if (numFloats > vertices.length) { if (numFloats > vertices.length) {
vertices = this.vertices = Utils.newFloatArray(numFloats); vertices = this.vertices = Utils.newFloatArray(numFloats);
} }
mesh.computeWorldVertices(slot, 0, mesh.worldVerticesLength, vertices, 0, vertexSize); mesh.computeWorldVertices(
triangles = mesh.triangles; slot,
uvs = mesh.uvs; 0,
texture = <ThreeJsTexture>mesh.region!.texture; mesh.worldVerticesLength,
} else if (attachment instanceof ClippingAttachment) { vertices,
let clip = <ClippingAttachment>(attachment); 0,
clipper.clipStart(slot, clip); vertexSize
continue; );
} else { triangles = mesh.triangles;
clipper.clipEndWithSlot(slot); uvs = mesh.uvs;
continue; 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) { if (texture != null) {
let skeleton = slot.bone.skeleton; let skeleton = slot.bone.skeleton;
let skeletonColor = skeleton.color; let skeletonColor = skeleton.color;
let slotColor = slot.color; let slotColor = slot.color;
let alpha = skeletonColor.a * slotColor.a * attachmentColor.a; let alpha = skeletonColor.a * slotColor.a * attachmentColor.a;
let color = this.tempColor; let color = this.tempColor;
color.set(skeletonColor.r * slotColor.r * attachmentColor.r, color.set(
skeletonColor.g * slotColor.g * attachmentColor.g, skeletonColor.r * slotColor.r * attachmentColor.r,
skeletonColor.b * slotColor.b * attachmentColor.b, skeletonColor.g * slotColor.g * attachmentColor.g,
alpha); skeletonColor.b * slotColor.b * attachmentColor.b,
alpha
);
let finalVertices: NumberArrayLike; let finalVertices: NumberArrayLike;
let finalVerticesLength: number; let finalVerticesLength: number;
let finalIndices: NumberArrayLike; let finalIndices: NumberArrayLike;
let finalIndicesLength: number; let finalIndicesLength: number;
if (clipper.isClipping()) { if (clipper.isClipping()) {
clipper.clipTriangles(vertices, numFloats, triangles, triangles.length, uvs, color, tempLight, false); clipper.clipTriangles(
let clippedVertices = clipper.clippedVertices; vertices,
let clippedTriangles = clipper.clippedTriangles; numFloats,
finalVertices = clippedVertices; triangles,
finalVerticesLength = clippedVertices.length; triangles.length,
finalIndices = clippedTriangles; uvs,
finalIndicesLength = clippedTriangles.length; color,
} else { tempLight,
let verts = vertices; false
for (let v = 2, u = 0, n = numFloats; v < n; v += vertexSize, u += 2) { );
verts[v] = color.r; let clippedVertices = clipper.clippedVertices;
verts[v + 1] = color.g; let clippedTriangles = clipper.clippedTriangles;
verts[v + 2] = color.b; finalVertices = clippedVertices;
verts[v + 3] = color.a; finalVerticesLength = clippedVertices.length;
verts[v + 4] = uvs[u]; finalIndices = clippedTriangles;
verts[v + 5] = uvs[u + 1]; finalIndicesLength = clippedTriangles.length;
} } else {
finalVertices = vertices; let verts = vertices;
finalVerticesLength = numFloats; for (
finalIndices = triangles; let v = 2, u = 0, n = numFloats;
finalIndicesLength = triangles.length; 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) { if (finalVerticesLength == 0 || finalIndicesLength == 0) {
clipper.clipEndWithSlot(slot); clipper.clipEndWithSlot(slot);
continue; continue;
} }
// Start new batch if this one can't hold vertices/indices // Start new batch if this one can't hold vertices/indices
if (!batch.canBatch(finalVerticesLength / SkeletonMesh.VERTEX_SIZE, finalIndicesLength)) { if (
batch.end(); !batch.canBatch(
batch = this.nextBatch(); finalVerticesLength / SkeletonMesh.VERTEX_SIZE,
batch.begin(); finalIndicesLength
} )
) {
batch.end();
batch = this.nextBatch();
batch.begin();
}
const slotBlendMode = slot.data.blendMode; const slotBlendMode = slot.data.blendMode;
const slotTexture = texture.texture; const slotTexture = texture.texture;
const materialGroup = batch.findMaterialGroup(slotTexture, slotBlendMode); const materialGroup = batch.findMaterialGroup(
slotTexture,
slotBlendMode
);
batch.addMaterialGroup(finalIndicesLength, materialGroup); batch.addMaterialGroup(finalIndicesLength, materialGroup);
batch.batch(finalVertices, finalVerticesLength, finalIndices, finalIndicesLength, z); batch.batch(
z += zOffset; finalVertices,
} finalVerticesLength,
finalIndices,
finalIndicesLength,
z
);
z += zOffset;
}
clipper.clipEndWithSlot(slot); clipper.clipEndWithSlot(slot);
} }
clipper.clipEnd(); clipper.clipEnd();
batch.end(); batch.end();
} }
} }