mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-21 09:46:02 +08:00
Merge branch '4.0' into 4.1-beta
# Conflicts: # spine-ts/package-lock.json # spine-ts/spine-threejs/package.json
This commit is contained in:
commit
ecbe087227
@ -122,7 +122,7 @@
|
|||||||
* Generated normals are now correctly flipped for back faces.
|
* Generated normals are now correctly flipped for back faces.
|
||||||
* Modifying parent materials updates material instances accordingly.
|
* Modifying parent materials updates material instances accordingly.
|
||||||
* Only `.json` files that are actually encoding Spine skeletons will be loaded. Other `.json` files will be left to other importers.
|
* Only `.json` files that are actually encoding Spine skeletons will be loaded. Other `.json` files will be left to other importers.
|
||||||
* Updated example project to UE 4.25.
|
* Updated example project to UE 4.27.
|
||||||
|
|
||||||
|
|
||||||
## C# ##
|
## C# ##
|
||||||
|
|||||||
@ -5,7 +5,6 @@
|
|||||||
<title>spine-threejs</title>
|
<title>spine-threejs</title>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.js"></script>
|
||||||
<script src="../dist/iife/spine-threejs.js"></script>
|
<script src="../dist/iife/spine-threejs.js"></script>
|
||||||
<script src="index.js"></script>
|
|
||||||
</head>
|
</head>
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
|
|||||||
@ -29,6 +29,8 @@
|
|||||||
|
|
||||||
import { SkeletonMeshMaterial, SkeletonMeshMaterialParametersCustomizer } from "./SkeletonMesh";
|
import { SkeletonMeshMaterial, SkeletonMeshMaterialParametersCustomizer } from "./SkeletonMesh";
|
||||||
import * as THREE from "three"
|
import * as THREE from "three"
|
||||||
|
import { ThreeJsTexture } from "./ThreeJsTexture";
|
||||||
|
import { BlendMode } from "@esotericsoftware/spine-core";
|
||||||
|
|
||||||
export class MeshBatcher extends THREE.Mesh {
|
export class MeshBatcher extends THREE.Mesh {
|
||||||
private static VERTEX_SIZE = 9;
|
private static VERTEX_SIZE = 9;
|
||||||
@ -37,8 +39,9 @@ export class MeshBatcher extends THREE.Mesh {
|
|||||||
private verticesLength = 0;
|
private verticesLength = 0;
|
||||||
private indices: Uint16Array;
|
private indices: Uint16Array;
|
||||||
private indicesLength = 0;
|
private indicesLength = 0;
|
||||||
|
private materialGroups: [number, number, number][] = [];
|
||||||
|
|
||||||
constructor (maxVertices: number = 10920, materialCustomizer: SkeletonMeshMaterialParametersCustomizer = (parameters) => { }) {
|
constructor (maxVertices: number = 10920, private materialCustomizer: SkeletonMeshMaterialParametersCustomizer = (parameters) => { }) {
|
||||||
super();
|
super();
|
||||||
if (maxVertices > 10920) throw new Error("Can't have more than 10920 triangles per batch: " + maxVertices);
|
if (maxVertices > 10920) throw new Error("Can't have more than 10920 triangles per batch: " + maxVertices);
|
||||||
let vertices = this.vertices = new Float32Array(maxVertices * MeshBatcher.VERTEX_SIZE);
|
let vertices = this.vertices = new Float32Array(maxVertices * MeshBatcher.VERTEX_SIZE);
|
||||||
@ -54,7 +57,7 @@ export class MeshBatcher extends THREE.Mesh {
|
|||||||
geo.drawRange.start = 0;
|
geo.drawRange.start = 0;
|
||||||
geo.drawRange.count = 0;
|
geo.drawRange.count = 0;
|
||||||
this.geometry = geo;
|
this.geometry = geo;
|
||||||
this.material = new SkeletonMeshMaterial(materialCustomizer);
|
this.material = [new SkeletonMeshMaterial(materialCustomizer)];
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose () {
|
dispose () {
|
||||||
@ -74,7 +77,19 @@ export class MeshBatcher extends THREE.Mesh {
|
|||||||
let geo = (<THREE.BufferGeometry>this.geometry);
|
let geo = (<THREE.BufferGeometry>this.geometry);
|
||||||
geo.drawRange.start = 0;
|
geo.drawRange.start = 0;
|
||||||
geo.drawRange.count = 0;
|
geo.drawRange.count = 0;
|
||||||
(<SkeletonMeshMaterial>this.material).uniforms.map.value = null;
|
geo.clearGroups();
|
||||||
|
this.materialGroups = [];
|
||||||
|
if (this.material instanceof THREE.Material) {
|
||||||
|
const meshMaterial = this.material as SkeletonMeshMaterial;
|
||||||
|
meshMaterial.uniforms.map.value = null;
|
||||||
|
meshMaterial.blending = THREE.NormalBlending;
|
||||||
|
} else if (Array.isArray(this.material)) {
|
||||||
|
for (let i = 0; i < this.material.length; i++) {
|
||||||
|
const meshMaterial = this.material[i] as SkeletonMeshMaterial;
|
||||||
|
meshMaterial.uniforms.map.value = null;
|
||||||
|
meshMaterial.blending = THREE.NormalBlending;
|
||||||
|
}
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,10 +133,67 @@ export class MeshBatcher extends THREE.Mesh {
|
|||||||
this.vertexBuffer.updateRange.offset = 0;
|
this.vertexBuffer.updateRange.offset = 0;
|
||||||
this.vertexBuffer.updateRange.count = this.verticesLength;
|
this.vertexBuffer.updateRange.count = this.verticesLength;
|
||||||
let geo = (<THREE.BufferGeometry>this.geometry);
|
let geo = (<THREE.BufferGeometry>this.geometry);
|
||||||
|
this.closeMaterialGroups();
|
||||||
geo.getIndex().needsUpdate = this.indicesLength > 0;
|
geo.getIndex().needsUpdate = this.indicesLength > 0;
|
||||||
geo.getIndex().updateRange.offset = 0;
|
geo.getIndex().updateRange.offset = 0;
|
||||||
geo.getIndex().updateRange.count = this.indicesLength;
|
geo.getIndex().updateRange.count = this.indicesLength;
|
||||||
geo.drawRange.start = 0;
|
geo.drawRange.start = 0;
|
||||||
geo.drawRange.count = this.indicesLength;
|
geo.drawRange.count = this.indicesLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addMaterialGroup (indicesLength: number, materialGroup: number) {
|
||||||
|
const currentGroup = this.materialGroups[this.materialGroups.length - 1];
|
||||||
|
|
||||||
|
if (currentGroup === undefined || currentGroup[2] !== materialGroup) {
|
||||||
|
this.materialGroups.push([this.indicesLength, indicesLength, materialGroup]);
|
||||||
|
} else {
|
||||||
|
currentGroup[1] += indicesLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private closeMaterialGroups () {
|
||||||
|
const geometry = this.geometry as THREE.BufferGeometry;
|
||||||
|
for (let i = 0; i < this.materialGroups.length; i++) {
|
||||||
|
const [startIndex, count, materialGroup] = this.materialGroups[i];
|
||||||
|
|
||||||
|
geometry.addGroup(startIndex, count, materialGroup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
findMaterialGroup (slotTexture: THREE.Texture, slotBlendMode: BlendMode) {
|
||||||
|
const blending = ThreeJsTexture.toThreeJsBlending(slotBlendMode);
|
||||||
|
let group = -1;
|
||||||
|
|
||||||
|
if (Array.isArray(this.material)) {
|
||||||
|
for (let i = 0; i < this.material.length; i++) {
|
||||||
|
const meshMaterial = this.material[i] as SkeletonMeshMaterial;
|
||||||
|
|
||||||
|
if (meshMaterial.uniforms.map.value === null) {
|
||||||
|
updateMeshMaterial(meshMaterial, slotTexture, blending);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (meshMaterial.uniforms.map.value === slotTexture && meshMaterial.blending === blending) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const meshMaterial = new SkeletonMeshMaterial(this.materialCustomizer);
|
||||||
|
updateMeshMaterial(meshMaterial, slotTexture, blending);
|
||||||
|
this.material.push(meshMaterial);
|
||||||
|
group = this.material.length - 1;
|
||||||
|
} else {
|
||||||
|
throw new Error("MeshBatcher.material needs to be an array for geometry groups to work");
|
||||||
|
}
|
||||||
|
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateMeshMaterial (meshMaterial: SkeletonMeshMaterial, slotTexture: THREE.Texture, blending: THREE.Blending) {
|
||||||
|
meshMaterial.uniforms.map.value = slotTexture;
|
||||||
|
meshMaterial.blending = blending;
|
||||||
|
meshMaterial.blendDst = blending === THREE.CustomBlending ? THREE.OneMinusSrcColorFactor : THREE.OneMinusSrcAlphaFactor;
|
||||||
|
meshMaterial.blendSrc = blending === THREE.CustomBlending ? THREE.OneFactor : THREE.SrcAlphaFactor;
|
||||||
|
meshMaterial.needsUpdate = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,6 +65,7 @@ export class SkeletonMeshMaterial extends THREE.ShaderMaterial {
|
|||||||
fragmentShader: fragmentShader,
|
fragmentShader: fragmentShader,
|
||||||
side: THREE.DoubleSide,
|
side: THREE.DoubleSide,
|
||||||
transparent: true,
|
transparent: true,
|
||||||
|
depthWrite: false,
|
||||||
alphaTest: 0.5
|
alphaTest: 0.5
|
||||||
};
|
};
|
||||||
customizer(parameters);
|
customizer(parameters);
|
||||||
@ -91,11 +92,10 @@ export class SkeletonMesh extends THREE.Object3D {
|
|||||||
|
|
||||||
private vertices = Utils.newFloatArray(1024);
|
private vertices = Utils.newFloatArray(1024);
|
||||||
private tempColor = new Color();
|
private tempColor = new Color();
|
||||||
private materialCustomizer: SkeletonMeshMaterialParametersCustomizer;
|
|
||||||
|
|
||||||
constructor (skeletonData: SkeletonData, materialCustomizer: SkeletonMeshMaterialParametersCustomizer = (parameters) => { }) {
|
constructor (skeletonData: SkeletonData) {
|
||||||
super();
|
super();
|
||||||
this.materialCustomizer = materialCustomizer;
|
|
||||||
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);
|
||||||
@ -128,7 +128,7 @@ export class SkeletonMesh extends THREE.Object3D {
|
|||||||
|
|
||||||
private nextBatch () {
|
private nextBatch () {
|
||||||
if (this.batches.length == this.nextBatchIndex) {
|
if (this.batches.length == this.nextBatchIndex) {
|
||||||
let batch = new MeshBatcher(10920, this.materialCustomizer);
|
let batch = new MeshBatcher();
|
||||||
this.add(batch);
|
this.add(batch);
|
||||||
this.batches.push(batch);
|
this.batches.push(batch);
|
||||||
}
|
}
|
||||||
@ -163,10 +163,7 @@ export class SkeletonMesh extends THREE.Object3D {
|
|||||||
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) continue;
|
||||||
clipper.clipEndWithSlot(slot);
|
|
||||||
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;
|
||||||
@ -196,12 +193,9 @@ export class SkeletonMesh extends THREE.Object3D {
|
|||||||
let clip = <ClippingAttachment>(attachment);
|
let clip = <ClippingAttachment>(attachment);
|
||||||
clipper.clipStart(slot, clip);
|
clipper.clipStart(slot, clip);
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else continue;
|
||||||
clipper.clipEndWithSlot(slot);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (texture) {
|
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;
|
||||||
@ -221,7 +215,7 @@ export class SkeletonMesh extends THREE.Object3D {
|
|||||||
clipper.clipTriangles(vertices, numFloats, triangles, triangles.length, uvs, color, null, false);
|
clipper.clipTriangles(vertices, numFloats, triangles, triangles.length, uvs, color, null, false);
|
||||||
let clippedVertices = clipper.clippedVertices;
|
let clippedVertices = clipper.clippedVertices;
|
||||||
let clippedTriangles = clipper.clippedTriangles;
|
let clippedTriangles = clipper.clippedTriangles;
|
||||||
if (this.vertexEffect) {
|
if (this.vertexEffect != null) {
|
||||||
let vertexEffect = this.vertexEffect;
|
let vertexEffect = this.vertexEffect;
|
||||||
let verts = clippedVertices;
|
let verts = clippedVertices;
|
||||||
for (let v = 0, n = clippedVertices.length; v < n; v += vertexSize) {
|
for (let v = 0, n = clippedVertices.length; v < n; v += vertexSize) {
|
||||||
@ -248,7 +242,7 @@ export class SkeletonMesh extends THREE.Object3D {
|
|||||||
finalIndicesLength = clippedTriangles.length;
|
finalIndicesLength = clippedTriangles.length;
|
||||||
} else {
|
} else {
|
||||||
let verts = vertices;
|
let verts = vertices;
|
||||||
if (this.vertexEffect) {
|
if (this.vertexEffect != null) {
|
||||||
let vertexEffect = this.vertexEffect;
|
let vertexEffect = this.vertexEffect;
|
||||||
for (let v = 0, u = 0, n = numFloats; v < n; v += vertexSize, u += 2) {
|
for (let v = 0, u = 0, n = numFloats; v < n; v += vertexSize, u += 2) {
|
||||||
tempPos.x = verts[v];
|
tempPos.x = verts[v];
|
||||||
@ -283,10 +277,8 @@ export class SkeletonMesh extends THREE.Object3D {
|
|||||||
finalIndicesLength = triangles.length;
|
finalIndicesLength = triangles.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (finalVerticesLength == 0 || finalIndicesLength == 0) {
|
if (finalVerticesLength == 0 || finalIndicesLength == 0)
|
||||||
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, finalIndicesLength)) {
|
if (!batch.canBatch(finalVerticesLength, finalIndicesLength)) {
|
||||||
@ -295,24 +287,11 @@ export class SkeletonMesh extends THREE.Object3D {
|
|||||||
batch.begin();
|
batch.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME per slot blending would require multiple material support
|
const slotBlendMode = slot.data.blendMode;
|
||||||
//let slotBlendMode = slot.data.blendMode;
|
const slotTexture = texture.texture;
|
||||||
//if (slotBlendMode != blendMode) {
|
const materialGroup = batch.findMaterialGroup(slotTexture, slotBlendMode);
|
||||||
// blendMode = slotBlendMode;
|
|
||||||
// batcher.setBlendMode(getSourceGLBlendMode(this._gl, blendMode, premultipliedAlpha), getDestGLBlendMode(this._gl, blendMode));
|
|
||||||
//}
|
|
||||||
|
|
||||||
let batchMaterial = <SkeletonMeshMaterial>batch.material;
|
|
||||||
if (!batchMaterial.uniforms.map.value) batchMaterial.uniforms.map.value = texture.texture;
|
|
||||||
if (batchMaterial.uniforms.map.value != texture.texture) {
|
|
||||||
batch.end();
|
|
||||||
batch = this.nextBatch();
|
|
||||||
batch.begin();
|
|
||||||
batchMaterial = <SkeletonMeshMaterial>batch.material;
|
|
||||||
batchMaterial.uniforms.map.value = texture.texture;
|
|
||||||
}
|
|
||||||
batchMaterial.needsUpdate = true;
|
|
||||||
|
|
||||||
|
batch.addMaterialGroup(finalIndicesLength, materialGroup);
|
||||||
batch.batch(finalVertices, finalVerticesLength, finalIndices, finalIndicesLength, z);
|
batch.batch(finalVertices, finalVerticesLength, finalIndices, finalIndicesLength, z);
|
||||||
z += zOffset;
|
z += zOffset;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,34 +27,34 @@
|
|||||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
import { Texture, TextureFilter, TextureWrap } from "@esotericsoftware/spine-core";
|
import { BlendMode, Texture, TextureFilter, TextureWrap } from "@esotericsoftware/spine-core";
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
|
|
||||||
export class ThreeJsTexture extends Texture {
|
export class ThreeJsTexture extends Texture {
|
||||||
texture: THREE.Texture;
|
texture: THREE.Texture;
|
||||||
|
|
||||||
constructor (image: HTMLImageElement) {
|
constructor(image: HTMLImageElement) {
|
||||||
super(image);
|
super(image);
|
||||||
this.texture = new THREE.Texture(image);
|
this.texture = new THREE.Texture(image);
|
||||||
this.texture.flipY = false;
|
this.texture.flipY = false;
|
||||||
this.texture.needsUpdate = true;
|
this.texture.needsUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
setFilters (minFilter: TextureFilter, magFilter: TextureFilter) {
|
setFilters(minFilter: TextureFilter, magFilter: TextureFilter) {
|
||||||
this.texture.minFilter = ThreeJsTexture.toThreeJsTextureFilter(minFilter);
|
this.texture.minFilter = ThreeJsTexture.toThreeJsTextureFilter(minFilter);
|
||||||
this.texture.magFilter = ThreeJsTexture.toThreeJsTextureFilter(magFilter);
|
this.texture.magFilter = ThreeJsTexture.toThreeJsTextureFilter(magFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
setWraps (uWrap: TextureWrap, vWrap: TextureWrap) {
|
setWraps(uWrap: TextureWrap, vWrap: TextureWrap) {
|
||||||
this.texture.wrapS = ThreeJsTexture.toThreeJsTextureWrap(uWrap);
|
this.texture.wrapS = ThreeJsTexture.toThreeJsTextureWrap(uWrap);
|
||||||
this.texture.wrapT = ThreeJsTexture.toThreeJsTextureWrap(vWrap);
|
this.texture.wrapT = ThreeJsTexture.toThreeJsTextureWrap(vWrap);
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose () {
|
dispose() {
|
||||||
this.texture.dispose();
|
this.texture.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
static toThreeJsTextureFilter (filter: TextureFilter) {
|
static toThreeJsTextureFilter(filter: TextureFilter) {
|
||||||
if (filter === TextureFilter.Linear) return THREE.LinearFilter;
|
if (filter === TextureFilter.Linear) return THREE.LinearFilter;
|
||||||
else if (filter === TextureFilter.MipMap) return THREE.LinearMipMapLinearFilter; // also includes TextureFilter.MipMapLinearLinear
|
else if (filter === TextureFilter.MipMap) return THREE.LinearMipMapLinearFilter; // also includes TextureFilter.MipMapLinearLinear
|
||||||
else if (filter === TextureFilter.MipMapLinearNearest) return THREE.LinearMipMapNearestFilter;
|
else if (filter === TextureFilter.MipMapLinearNearest) return THREE.LinearMipMapNearestFilter;
|
||||||
@ -64,10 +64,18 @@ export class ThreeJsTexture extends Texture {
|
|||||||
else throw new Error("Unknown texture filter: " + filter);
|
else throw new Error("Unknown texture filter: " + filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
static toThreeJsTextureWrap (wrap: TextureWrap) {
|
static toThreeJsTextureWrap(wrap: TextureWrap) {
|
||||||
if (wrap === TextureWrap.ClampToEdge) return THREE.ClampToEdgeWrapping;
|
if (wrap === TextureWrap.ClampToEdge) return THREE.ClampToEdgeWrapping;
|
||||||
else if (wrap === TextureWrap.MirroredRepeat) return THREE.MirroredRepeatWrapping;
|
else if (wrap === TextureWrap.MirroredRepeat) return THREE.MirroredRepeatWrapping;
|
||||||
else if (wrap === TextureWrap.Repeat) return THREE.RepeatWrapping;
|
else if (wrap === TextureWrap.Repeat) return THREE.RepeatWrapping;
|
||||||
else throw new Error("Unknown texture wrap: " + wrap);
|
else throw new Error("Unknown texture wrap: " + wrap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static toThreeJsBlending(blend: BlendMode) {
|
||||||
|
if (blend === BlendMode.Normal) return THREE.NormalBlending;
|
||||||
|
else if (blend === BlendMode.Additive) return THREE.AdditiveBlending;
|
||||||
|
else if (blend === BlendMode.Multiply) return THREE.MultiplyBlending;
|
||||||
|
else if (blend === BlendMode.Screen) return THREE.CustomBlending;
|
||||||
|
else throw new Error("Unknown blendMode: " + blend);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -21,7 +21,7 @@
|
|||||||
info;
|
info;
|
||||||
|
|
||||||
loadAssets(canvas) {
|
loadAssets(canvas) {
|
||||||
this.numSkeletons = 100;
|
this.numSkeletons = 400;
|
||||||
this.skeletons = [];
|
this.skeletons = [];
|
||||||
this.states = [];
|
this.states = [];
|
||||||
this.info = document.querySelector("#info")[0];
|
this.info = document.querySelector("#info")[0];
|
||||||
@ -92,7 +92,7 @@
|
|||||||
renderer.drawSkeleton(skeleton, true);
|
renderer.drawSkeleton(skeleton, true);
|
||||||
}
|
}
|
||||||
renderer.end();
|
renderer.end();
|
||||||
info.innerText = "Draw calls: " + renderer.batcher.drawCalls;
|
info.innerText = "Draw calls: " + renderer.batcher.drawCalls + ", FPS: " + canvas.time.framesPerSecond.toFixed(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"FileVersion": 3,
|
"FileVersion": 3,
|
||||||
"EngineAssociation": "4.25",
|
"EngineAssociation": "4.27",
|
||||||
"Category": "",
|
"Category": "",
|
||||||
"Description": "",
|
"Description": "",
|
||||||
"Modules": [
|
"Modules": [
|
||||||
|
|||||||
@ -109,8 +109,13 @@ public class SpineAnimationStateDrawer : PropertyDrawer {
|
|||||||
EditorGUI.PropertyField(singleFieldRect, useBlendDurationProp);
|
EditorGUI.PropertyField(singleFieldRect, useBlendDurationProp);
|
||||||
|
|
||||||
singleFieldRect.y += lineHeightWithSpacing;
|
singleFieldRect.y += lineHeightWithSpacing;
|
||||||
|
|
||||||
|
bool greyOutMixDuration = (!useBlendDurationProp.hasMultipleDifferentValues &&
|
||||||
|
useBlendDurationProp.boolValue == true);
|
||||||
|
using (new EditorGUI.DisabledGroupScope(greyOutMixDuration)) {
|
||||||
EditorGUI.PropertyField(singleFieldRect, mixDurationProp);
|
EditorGUI.PropertyField(singleFieldRect, mixDurationProp);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
singleFieldRect.y += lineHeightWithSpacing;
|
singleFieldRect.y += lineHeightWithSpacing;
|
||||||
EditorGUI.PropertyField(singleFieldRect, holdPreviousProp);
|
EditorGUI.PropertyField(singleFieldRect, holdPreviousProp);
|
||||||
|
|||||||
@ -41,6 +41,9 @@ namespace Spine.Unity.Playables {
|
|||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class SpineAnimationStateBehaviour : PlayableBehaviour {
|
public class SpineAnimationStateBehaviour : PlayableBehaviour {
|
||||||
|
|
||||||
|
[NonSerialized] public TimelineClip timelineClip;
|
||||||
|
|
||||||
public AnimationReferenceAsset animationReference;
|
public AnimationReferenceAsset animationReference;
|
||||||
public bool loop;
|
public bool loop;
|
||||||
|
|
||||||
|
|||||||
@ -38,8 +38,11 @@ namespace Spine.Unity.Playables {
|
|||||||
public SpineAnimationStateBehaviour template = new SpineAnimationStateBehaviour();
|
public SpineAnimationStateBehaviour template = new SpineAnimationStateBehaviour();
|
||||||
|
|
||||||
public ClipCaps clipCaps { get { return ClipCaps.Blending | ClipCaps.ClipIn | ClipCaps.SpeedMultiplier | (template.loop ? ClipCaps.Looping : 0); } }
|
public ClipCaps clipCaps { get { return ClipCaps.Blending | ClipCaps.ClipIn | ClipCaps.SpeedMultiplier | (template.loop ? ClipCaps.Looping : 0); } }
|
||||||
|
[NonSerialized] public TimelineClip timelineClip;
|
||||||
|
|
||||||
public override Playable CreatePlayable (PlayableGraph graph, GameObject owner) {
|
public override Playable CreatePlayable (PlayableGraph graph, GameObject owner) {
|
||||||
|
template.timelineClip = this.timelineClip;
|
||||||
|
|
||||||
var playable = ScriptPlayable<SpineAnimationStateBehaviour>.Create(graph, template);
|
var playable = ScriptPlayable<SpineAnimationStateBehaviour>.Create(graph, template);
|
||||||
playable.GetBehaviour();
|
playable.GetBehaviour();
|
||||||
return playable;
|
return playable;
|
||||||
|
|||||||
@ -95,6 +95,8 @@ namespace Spine.Unity.Playables {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void HandleClipEnd () {
|
protected void HandleClipEnd () {
|
||||||
|
if (animationStateComponent == null) return;
|
||||||
|
|
||||||
var state = animationStateComponent.AnimationState;
|
var state = animationStateComponent.AnimationState;
|
||||||
if (endAtClipEnd &&
|
if (endAtClipEnd &&
|
||||||
timelineStartedTrackEntry != null &&
|
timelineStartedTrackEntry != null &&
|
||||||
@ -172,13 +174,14 @@ namespace Spine.Unity.Playables {
|
|||||||
endMixOutDuration = clipData.endMixOutDuration;
|
endMixOutDuration = clipData.endMixOutDuration;
|
||||||
|
|
||||||
if (clipData.animationReference == null) {
|
if (clipData.animationReference == null) {
|
||||||
float mixDuration = clipData.customDuration ? clipData.mixDuration : state.Data.DefaultMix;
|
float mixDuration = clipData.customDuration ? GetCustomMixDuration(clipData) : state.Data.DefaultMix;
|
||||||
state.SetEmptyAnimation(trackIndex, mixDuration);
|
state.SetEmptyAnimation(trackIndex, mixDuration);
|
||||||
} else {
|
} else {
|
||||||
if (clipData.animationReference.Animation != null) {
|
if (clipData.animationReference.Animation != null) {
|
||||||
TrackEntry currentEntry = state.GetCurrent(trackIndex);
|
TrackEntry currentEntry = state.GetCurrent(trackIndex);
|
||||||
Spine.TrackEntry trackEntry;
|
Spine.TrackEntry trackEntry;
|
||||||
if (currentEntry == null && (clipData.customDuration && clipData.mixDuration > 0)) {
|
float customMixDuration = clipData.customDuration ? GetCustomMixDuration(clipData) : 0.0f;
|
||||||
|
if (currentEntry == null && customMixDuration > 0) {
|
||||||
state.SetEmptyAnimation(trackIndex, 0); // ease in requires empty animation
|
state.SetEmptyAnimation(trackIndex, 0); // ease in requires empty animation
|
||||||
trackEntry = state.AddAnimation(trackIndex, clipData.animationReference.Animation, clipData.loop, 0);
|
trackEntry = state.AddAnimation(trackIndex, clipData.animationReference.Animation, clipData.loop, 0);
|
||||||
} else
|
} else
|
||||||
@ -192,7 +195,7 @@ namespace Spine.Unity.Playables {
|
|||||||
trackEntry.HoldPrevious = clipData.holdPrevious;
|
trackEntry.HoldPrevious = clipData.holdPrevious;
|
||||||
|
|
||||||
if (clipData.customDuration)
|
if (clipData.customDuration)
|
||||||
trackEntry.MixDuration = clipData.mixDuration;
|
trackEntry.MixDuration = customMixDuration;
|
||||||
|
|
||||||
timelineStartedTrackEntry = trackEntry;
|
timelineStartedTrackEntry = trackEntry;
|
||||||
}
|
}
|
||||||
@ -314,6 +317,14 @@ namespace Spine.Unity.Playables {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
float GetCustomMixDuration (SpineAnimationStateBehaviour clipData) {
|
||||||
|
if (clipData.useBlendDuration) {
|
||||||
|
TimelineClip clip = clipData.timelineClip;
|
||||||
|
return (float)Math.Max(clip.blendInDuration, clip.easeInDuration);
|
||||||
|
} else {
|
||||||
|
return clipData.mixDuration;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.Playables;
|
using UnityEngine.Playables;
|
||||||
using UnityEngine.Timeline;
|
using UnityEngine.Timeline;
|
||||||
@ -39,6 +40,13 @@ namespace Spine.Unity.Playables {
|
|||||||
public int trackIndex = 0;
|
public int trackIndex = 0;
|
||||||
|
|
||||||
public override Playable CreateTrackMixer (PlayableGraph graph, GameObject go, int inputCount) {
|
public override Playable CreateTrackMixer (PlayableGraph graph, GameObject go, int inputCount) {
|
||||||
|
IEnumerable<TimelineClip> clips = this.GetClips();
|
||||||
|
foreach (TimelineClip clip in clips) {
|
||||||
|
var animationStateClip = clip.asset as SpineAnimationStateClip;
|
||||||
|
if (animationStateClip != null)
|
||||||
|
animationStateClip.timelineClip = clip;
|
||||||
|
}
|
||||||
|
|
||||||
var scriptPlayable = ScriptPlayable<SpineAnimationStateMixerBehaviour>.Create(graph, inputCount);
|
var scriptPlayable = ScriptPlayable<SpineAnimationStateMixerBehaviour>.Create(graph, inputCount);
|
||||||
var mixerBehaviour = scriptPlayable.GetBehaviour();
|
var mixerBehaviour = scriptPlayable.GetBehaviour();
|
||||||
mixerBehaviour.trackIndex = this.trackIndex;
|
mixerBehaviour.trackIndex = this.trackIndex;
|
||||||
|
|||||||
@ -104,7 +104,7 @@ half4 CombinedShapeLightFragment(VertexOutputSpriteURP2D input) : SV_Target
|
|||||||
pixel.rgb = applyRimLighting(input.positionWS.xyz, normalWS, pixel);
|
pixel.rgb = applyRimLighting(input.positionWS.xyz, normalWS, pixel);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
APPLY_EMISSION(pixel.rgb, input.texcoord)
|
APPLY_EMISSION(pixel.rgb, input.texcoord.xy)
|
||||||
pixel = prepareLitPixelForOutput(pixel, texureColor.a, input.vertexColor.a);
|
pixel = prepareLitPixelForOutput(pixel, texureColor.a, input.vertexColor.a);
|
||||||
COLORISE(pixel)
|
COLORISE(pixel)
|
||||||
return pixel;
|
return pixel;
|
||||||
|
|||||||
@ -127,7 +127,7 @@ half4 LightweightFragmentBlinnPhongSimplified(InputData inputData, half4 texDiff
|
|||||||
#endif
|
#endif
|
||||||
half3 diffuseLighting = inputData.bakedGI;
|
half3 diffuseLighting = inputData.bakedGI;
|
||||||
|
|
||||||
half3 attenuation = mainLight.distanceAttenuation * mainLight.shadowAttenuation;
|
half3 attenuation = mainLight.distanceAttenuation* mainLight.shadowAttenuation;
|
||||||
half3 attenuatedLightColor = mainLight.color * attenuation;
|
half3 attenuatedLightColor = mainLight.color * attenuation;
|
||||||
#ifndef _DIFFUSE_RAMP
|
#ifndef _DIFFUSE_RAMP
|
||||||
diffuseLighting += LightingLambert(attenuatedLightColor, mainLight.direction, inputData.normalWS);
|
diffuseLighting += LightingLambert(attenuatedLightColor, mainLight.direction, inputData.normalWS);
|
||||||
@ -184,24 +184,15 @@ VertexOutputLWRP ForwardPassVertexSprite(VertexInput input)
|
|||||||
output.positionWS = float4(positionWS, 1);
|
output.positionWS = float4(positionWS, 1);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(PER_PIXEL_LIGHTING)
|
|
||||||
|
|
||||||
half3 normalWS = calculateSpriteWorldNormal(input, -backFaceSign);
|
half3 normalWS = calculateSpriteWorldNormal(input, -backFaceSign);
|
||||||
output.normalWorld.xyz = normalWS;
|
output.normalWorld.xyz = normalWS;
|
||||||
|
|
||||||
#if defined(_NORMALMAP)
|
#if defined(_NORMALMAP)
|
||||||
output.tangentWorld.xyz = calculateWorldTangent(input.tangent);
|
output.tangentWorld.xyz = calculateWorldTangent(input.tangent);
|
||||||
output.binormalWorld.xyz = calculateSpriteWorldBinormal(input, output.normalWorld.xyz, output.tangentWorld.xyz, backFaceSign);
|
output.binormalWorld.xyz = calculateSpriteWorldBinormal(input, output.normalWorld.xyz, output.tangentWorld.xyz, backFaceSign);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#else // !PER_PIXEL_LIGHTING
|
|
||||||
half3 fixedNormal = half3(0, 0, -1);
|
|
||||||
half3 normalWS = normalize(mul((float3x3)unity_ObjectToWorld, fixedNormal));
|
|
||||||
|
|
||||||
#endif // !PER_PIXEL_LIGHTING
|
|
||||||
output.fogFactorAndVertexLight.yzw = LightweightLightVertexSimplified(positionWS, normalWS);
|
output.fogFactorAndVertexLight.yzw = LightweightLightVertexSimplified(positionWS, normalWS);
|
||||||
|
|
||||||
|
|
||||||
#if (defined(_MAIN_LIGHT_SHADOWS) || defined(MAIN_LIGHT_CALCULATE_SHADOWS)) && !defined(_RECEIVE_SHADOWS_OFF)
|
#if (defined(_MAIN_LIGHT_SHADOWS) || defined(MAIN_LIGHT_CALCULATE_SHADOWS)) && !defined(_RECEIVE_SHADOWS_OFF)
|
||||||
VertexPositionInputs vertexInput;
|
VertexPositionInputs vertexInput;
|
||||||
vertexInput.positionWS = positionWS;
|
vertexInput.positionWS = positionWS;
|
||||||
@ -243,16 +234,11 @@ half4 ForwardPassFragmentSprite(VertexOutputLWRP input) : SV_Target
|
|||||||
inputData.viewDirectionWS = input.viewDirectionWS;
|
inputData.viewDirectionWS = input.viewDirectionWS;
|
||||||
inputData.vertexLighting = input.fogFactorAndVertexLight.yzw;
|
inputData.vertexLighting = input.fogFactorAndVertexLight.yzw;
|
||||||
|
|
||||||
#if defined(PER_PIXEL_LIGHTING)
|
#if defined(PER_PIXEL_LIGHTING) && defined(_NORMALMAP)
|
||||||
#if defined(_NORMALMAP)
|
|
||||||
half3 normalWS = calculateNormalFromBumpMap(input.texcoord.xy, input.tangentWorld.xyz, input.binormalWorld.xyz, input.normalWorld.xyz);
|
half3 normalWS = calculateNormalFromBumpMap(input.texcoord.xy, input.tangentWorld.xyz, input.binormalWorld.xyz, input.normalWorld.xyz);
|
||||||
#else
|
#else
|
||||||
half3 normalWS = input.normalWorld.xyz;
|
half3 normalWS = input.normalWorld.xyz;
|
||||||
#endif
|
#endif
|
||||||
#else // !PER_PIXEL_LIGHTING
|
|
||||||
half3 fixedNormal = half3(0, 0, -1);
|
|
||||||
half3 normalWS = normalize(mul((float3x3)unity_ObjectToWorld, fixedNormal));
|
|
||||||
#endif // !PER_PIXEL_LIGHTING
|
|
||||||
|
|
||||||
inputData.normalWS = normalWS;
|
inputData.normalWS = normalWS;
|
||||||
inputData.bakedGI = SAMPLE_GI(input.lightmapUV, input.vertexSH, inputData.normalWS);
|
inputData.bakedGI = SAMPLE_GI(input.lightmapUV, input.vertexSH, inputData.normalWS);
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
"name": "com.esotericsoftware.spine.urp-shaders",
|
"name": "com.esotericsoftware.spine.urp-shaders",
|
||||||
"displayName": "Spine Universal RP Shaders",
|
"displayName": "Spine Universal RP Shaders",
|
||||||
"description": "This plugin provides universal render pipeline (URP) shaders for the spine-unity runtime.\n\nPrerequisites:\nIt requires a working installation of the spine-unity runtime, version 4.0.\n(See http://esotericsoftware.com/git/spine-runtimes/spine-unity)",
|
"description": "This plugin provides universal render pipeline (URP) shaders for the spine-unity runtime.\n\nPrerequisites:\nIt requires a working installation of the spine-unity runtime, version 4.0.\n(See http://esotericsoftware.com/git/spine-runtimes/spine-unity)",
|
||||||
"version": "4.0.4",
|
"version": "4.0.5",
|
||||||
"unity": "2019.3",
|
"unity": "2019.3",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Esoteric Software",
|
"name": "Esoteric Software",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user