mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-24 10:41:24 +08:00
Added WIP stencil-based clipping attachment.
This commit is contained in:
parent
be9b522237
commit
95b593ef1f
@ -41,6 +41,7 @@ import com.esotericsoftware.spine.AnimationState.AnimationStateListener;
|
|||||||
import com.esotericsoftware.spine.AnimationState.TrackEntry;
|
import com.esotericsoftware.spine.AnimationState.TrackEntry;
|
||||||
import com.esotericsoftware.spine.attachments.AttachmentLoader;
|
import com.esotericsoftware.spine.attachments.AttachmentLoader;
|
||||||
import com.esotericsoftware.spine.attachments.BoundingBoxAttachment;
|
import com.esotericsoftware.spine.attachments.BoundingBoxAttachment;
|
||||||
|
import com.esotericsoftware.spine.attachments.ClippingAttachment;
|
||||||
import com.esotericsoftware.spine.attachments.MeshAttachment;
|
import com.esotericsoftware.spine.attachments.MeshAttachment;
|
||||||
import com.esotericsoftware.spine.attachments.PathAttachment;
|
import com.esotericsoftware.spine.attachments.PathAttachment;
|
||||||
import com.esotericsoftware.spine.attachments.PointAttachment;
|
import com.esotericsoftware.spine.attachments.PointAttachment;
|
||||||
@ -60,6 +61,10 @@ public class AnimationStateTests {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ClippingAttachment newClippingAttachment (Skin skin, String name) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public PathAttachment newPathAttachment (Skin skin, String name) {
|
public PathAttachment newPathAttachment (Skin skin, String name) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,6 +33,7 @@ package com.esotericsoftware.spine;
|
|||||||
import com.badlogic.gdx.files.FileHandle;
|
import com.badlogic.gdx.files.FileHandle;
|
||||||
import com.esotericsoftware.spine.attachments.AttachmentLoader;
|
import com.esotericsoftware.spine.attachments.AttachmentLoader;
|
||||||
import com.esotericsoftware.spine.attachments.BoundingBoxAttachment;
|
import com.esotericsoftware.spine.attachments.BoundingBoxAttachment;
|
||||||
|
import com.esotericsoftware.spine.attachments.ClippingAttachment;
|
||||||
import com.esotericsoftware.spine.attachments.RegionAttachment;
|
import com.esotericsoftware.spine.attachments.RegionAttachment;
|
||||||
import com.esotericsoftware.spine.attachments.MeshAttachment;
|
import com.esotericsoftware.spine.attachments.MeshAttachment;
|
||||||
import com.esotericsoftware.spine.attachments.PathAttachment;
|
import com.esotericsoftware.spine.attachments.PathAttachment;
|
||||||
@ -54,6 +55,10 @@ public class BonePlotting {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ClippingAttachment newClippingAttachment (Skin skin, String name) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public PathAttachment newPathAttachment (Skin skin, String name) {
|
public PathAttachment newPathAttachment (Skin skin, String name) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,141 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Spine Runtimes Software License v2.5
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2016, Esoteric Software
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||||
|
* non-transferable license to use, install, execute, and perform the Spine
|
||||||
|
* Runtimes software and derivative works solely for personal or internal
|
||||||
|
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||||
|
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||||
|
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||||
|
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||||
|
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||||
|
* or other intellectual property or proprietary rights notices on or in the
|
||||||
|
* Software, including any copy thereof. Redistributions in binary or source
|
||||||
|
* form must include this license and terms.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||||
|
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||||
|
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||||
|
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
package com.esotericsoftware.spine;
|
||||||
|
|
||||||
|
import com.badlogic.gdx.ApplicationAdapter;
|
||||||
|
import com.badlogic.gdx.Gdx;
|
||||||
|
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
|
||||||
|
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
|
||||||
|
import com.badlogic.gdx.graphics.GL20;
|
||||||
|
import com.badlogic.gdx.graphics.OrthographicCamera;
|
||||||
|
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||||
|
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
|
||||||
|
import com.esotericsoftware.spine.attachments.ClippingAttachment;
|
||||||
|
|
||||||
|
public class ClippingTest extends ApplicationAdapter {
|
||||||
|
OrthographicCamera camera;
|
||||||
|
SpriteBatch batch;
|
||||||
|
SkeletonRenderer renderer;
|
||||||
|
SkeletonRendererDebug debugRenderer;
|
||||||
|
|
||||||
|
TextureAtlas atlas;
|
||||||
|
Skeleton skeleton;
|
||||||
|
AnimationState state;
|
||||||
|
|
||||||
|
public void create () {
|
||||||
|
camera = new OrthographicCamera();
|
||||||
|
batch = new SpriteBatch();
|
||||||
|
renderer = new SkeletonRenderer();
|
||||||
|
renderer.setPremultipliedAlpha(true);
|
||||||
|
debugRenderer = new SkeletonRendererDebug();
|
||||||
|
debugRenderer.setBoundingBoxes(false);
|
||||||
|
debugRenderer.setRegionAttachments(false);
|
||||||
|
|
||||||
|
atlas = new TextureAtlas(Gdx.files.internal("spineboy/spineboy-pma.atlas"));
|
||||||
|
SkeletonJson json = new SkeletonJson(atlas);
|
||||||
|
json.setScale(0.6f);
|
||||||
|
SkeletonData skeletonData = json.readSkeletonData(Gdx.files.internal("spineboy/spineboy.json"));
|
||||||
|
|
||||||
|
skeleton = new Skeleton(skeletonData);
|
||||||
|
skeleton.setPosition(250, 20);
|
||||||
|
|
||||||
|
AnimationStateData stateData = new AnimationStateData(skeletonData);
|
||||||
|
stateData.setMix("run", "jump", 0.2f);
|
||||||
|
stateData.setMix("jump", "run", 0.2f);
|
||||||
|
|
||||||
|
state = new AnimationState(stateData);
|
||||||
|
state.setTimeScale(0.5f);
|
||||||
|
|
||||||
|
state.setAnimation(0, "run", true);
|
||||||
|
state.addAnimation(0, "jump", false, 2);
|
||||||
|
state.addAnimation(0, "run", true, 0);
|
||||||
|
|
||||||
|
// Create a clipping attachment, slot data, and slot.
|
||||||
|
ClippingAttachment clip = new ClippingAttachment("clip");
|
||||||
|
// Rectangle:
|
||||||
|
// clip.setVertices(new float[] { //
|
||||||
|
// -140, -50, //
|
||||||
|
// 120, -50, //
|
||||||
|
// 120, 50, //
|
||||||
|
// -140, 50, //
|
||||||
|
// });
|
||||||
|
// Self intersection:
|
||||||
|
clip.setVertices(new float[] { //
|
||||||
|
-140, -50, //
|
||||||
|
120, 50, //
|
||||||
|
120, -50, //
|
||||||
|
-140, 50, //
|
||||||
|
});
|
||||||
|
clip.setWorldVerticesLength(8);
|
||||||
|
clip.setEnd(skeleton.findSlot("muzzle"));
|
||||||
|
|
||||||
|
SlotData clipSlotData = new SlotData(skeletonData.getSlots().size, "clip slot", skeletonData.getBones().first());
|
||||||
|
skeletonData.getSlots().add(clipSlotData);
|
||||||
|
|
||||||
|
Slot clipSlot = new Slot(clipSlotData, skeleton.getRootBone());
|
||||||
|
clipSlot.setAttachment(clip);
|
||||||
|
skeleton.getSlots().add(clipSlot);
|
||||||
|
skeleton.getDrawOrder().insert(skeletonData.findSlot("neck").getIndex(), clipSlot);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void render () {
|
||||||
|
state.update(Gdx.graphics.getDeltaTime() * 0.3f);
|
||||||
|
|
||||||
|
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
state.apply(skeleton);
|
||||||
|
skeleton.updateWorldTransform();
|
||||||
|
|
||||||
|
camera.update();
|
||||||
|
batch.getProjectionMatrix().set(camera.combined);
|
||||||
|
debugRenderer.getShapeRenderer().setProjectionMatrix(camera.combined);
|
||||||
|
|
||||||
|
batch.begin();
|
||||||
|
renderer.draw(batch, skeleton);
|
||||||
|
batch.end();
|
||||||
|
|
||||||
|
debugRenderer.draw(skeleton);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resize (int width, int height) {
|
||||||
|
camera.setToOrtho(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dispose () {
|
||||||
|
atlas.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main (String[] args) throws Exception {
|
||||||
|
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
|
||||||
|
config.stencil = 8;
|
||||||
|
new LwjglApplication(new ClippingTest(), config);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -377,6 +377,7 @@ public class SkeletonBinary {
|
|||||||
region.updateOffset();
|
region.updateOffset();
|
||||||
return region;
|
return region;
|
||||||
}
|
}
|
||||||
|
// BOZO! - Binary clip export.
|
||||||
case boundingbox: {
|
case boundingbox: {
|
||||||
int vertexCount = input.readInt(true);
|
int vertexCount = input.readInt(true);
|
||||||
Vertices vertices = readVertices(input, vertexCount);
|
Vertices vertices = readVertices(input, vertexCount);
|
||||||
|
|||||||
@ -337,6 +337,7 @@ public class SkeletonJson {
|
|||||||
region.updateOffset();
|
region.updateOffset();
|
||||||
return region;
|
return region;
|
||||||
}
|
}
|
||||||
|
// BOZO! - JSON clip export.
|
||||||
case boundingbox: {
|
case boundingbox: {
|
||||||
BoundingBoxAttachment box = attachmentLoader.newBoundingBoxAttachment(skin, name);
|
BoundingBoxAttachment box = attachmentLoader.newBoundingBoxAttachment(skin, name);
|
||||||
if (box == null) return null;
|
if (box == null) return null;
|
||||||
|
|||||||
@ -30,14 +30,20 @@
|
|||||||
|
|
||||||
package com.esotericsoftware.spine;
|
package com.esotericsoftware.spine;
|
||||||
|
|
||||||
|
import com.badlogic.gdx.Gdx;
|
||||||
import com.badlogic.gdx.graphics.Color;
|
import com.badlogic.gdx.graphics.Color;
|
||||||
|
import com.badlogic.gdx.graphics.GL20;
|
||||||
import com.badlogic.gdx.graphics.Texture;
|
import com.badlogic.gdx.graphics.Texture;
|
||||||
import com.badlogic.gdx.graphics.g2d.Batch;
|
import com.badlogic.gdx.graphics.g2d.Batch;
|
||||||
import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch;
|
import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch;
|
||||||
|
import com.badlogic.gdx.graphics.glutils.ImmediateModeRenderer;
|
||||||
|
import com.badlogic.gdx.graphics.glutils.ImmediateModeRenderer20;
|
||||||
|
import com.badlogic.gdx.math.Matrix4;
|
||||||
import com.badlogic.gdx.utils.Array;
|
import com.badlogic.gdx.utils.Array;
|
||||||
import com.badlogic.gdx.utils.FloatArray;
|
import com.badlogic.gdx.utils.FloatArray;
|
||||||
import com.badlogic.gdx.utils.NumberUtils;
|
import com.badlogic.gdx.utils.NumberUtils;
|
||||||
import com.esotericsoftware.spine.attachments.Attachment;
|
import com.esotericsoftware.spine.attachments.Attachment;
|
||||||
|
import com.esotericsoftware.spine.attachments.ClippingAttachment;
|
||||||
import com.esotericsoftware.spine.attachments.MeshAttachment;
|
import com.esotericsoftware.spine.attachments.MeshAttachment;
|
||||||
import com.esotericsoftware.spine.attachments.RegionAttachment;
|
import com.esotericsoftware.spine.attachments.RegionAttachment;
|
||||||
import com.esotericsoftware.spine.attachments.SkeletonAttachment;
|
import com.esotericsoftware.spine.attachments.SkeletonAttachment;
|
||||||
@ -49,6 +55,10 @@ public class SkeletonRenderer {
|
|||||||
private boolean premultipliedAlpha;
|
private boolean premultipliedAlpha;
|
||||||
private final FloatArray vertices = new FloatArray(32);
|
private final FloatArray vertices = new FloatArray(32);
|
||||||
|
|
||||||
|
private Slot clipEnd;
|
||||||
|
private final Matrix4 combinedMatrix = new Matrix4();
|
||||||
|
private ImmediateModeRenderer renderer; // BOZO! - Dispose.
|
||||||
|
|
||||||
public void draw (Batch batch, Skeleton skeleton) {
|
public void draw (Batch batch, Skeleton skeleton) {
|
||||||
boolean premultipliedAlpha = this.premultipliedAlpha;
|
boolean premultipliedAlpha = this.premultipliedAlpha;
|
||||||
float[] vertices = this.vertices.items;
|
float[] vertices = this.vertices.items;
|
||||||
@ -78,31 +88,44 @@ public class SkeletonRenderer {
|
|||||||
batch.setBlendFunction(blendMode.getSource(premultipliedAlpha), blendMode.getDest());
|
batch.setBlendFunction(blendMode.getSource(premultipliedAlpha), blendMode.getDest());
|
||||||
batch.draw(region.getRegion().getTexture(), vertices, 0, 20);
|
batch.draw(region.getRegion().getTexture(), vertices, 0, 20);
|
||||||
|
|
||||||
|
} else if (attachment instanceof ClippingAttachment) {
|
||||||
|
ClippingAttachment clip = (ClippingAttachment)attachment;
|
||||||
|
batch.end();
|
||||||
|
clipStart(batch.getProjectionMatrix(), batch.getTransformMatrix(), slot, clip);
|
||||||
|
batch.begin();
|
||||||
|
continue;
|
||||||
|
|
||||||
} else if (attachment instanceof MeshAttachment) {
|
} else if (attachment instanceof MeshAttachment) {
|
||||||
throw new RuntimeException("SkeletonMeshRenderer is required to render meshes.");
|
throw new RuntimeException("SkeletonMeshRenderer is required to render meshes.");
|
||||||
|
|
||||||
} else if (attachment instanceof SkeletonAttachment) {
|
} else if (attachment instanceof SkeletonAttachment) {
|
||||||
Skeleton attachmentSkeleton = ((SkeletonAttachment)attachment).getSkeleton();
|
Skeleton attachmentSkeleton = ((SkeletonAttachment)attachment).getSkeleton();
|
||||||
if (attachmentSkeleton == null) continue;
|
if (attachmentSkeleton != null) {
|
||||||
Bone bone = slot.getBone();
|
Bone bone = slot.getBone();
|
||||||
Bone rootBone = attachmentSkeleton.getRootBone();
|
Bone rootBone = attachmentSkeleton.getRootBone();
|
||||||
float oldScaleX = rootBone.getScaleX();
|
float oldScaleX = rootBone.getScaleX();
|
||||||
float oldScaleY = rootBone.getScaleY();
|
float oldScaleY = rootBone.getScaleY();
|
||||||
float oldRotation = rootBone.getRotation();
|
float oldRotation = rootBone.getRotation();
|
||||||
attachmentSkeleton.setPosition(bone.getWorldX(), bone.getWorldY());
|
attachmentSkeleton.setPosition(bone.getWorldX(), bone.getWorldY());
|
||||||
// rootBone.setScaleX(1 + bone.getWorldScaleX() - oldScaleX);
|
// rootBone.setScaleX(1 + bone.getWorldScaleX() - oldScaleX);
|
||||||
// rootBone.setScaleY(1 + bone.getWorldScaleY() - oldScaleY);
|
// rootBone.setScaleY(1 + bone.getWorldScaleY() - oldScaleY);
|
||||||
// Set shear.
|
// Set shear.
|
||||||
rootBone.setRotation(oldRotation + bone.getWorldRotationX());
|
rootBone.setRotation(oldRotation + bone.getWorldRotationX());
|
||||||
attachmentSkeleton.updateWorldTransform();
|
attachmentSkeleton.updateWorldTransform();
|
||||||
|
|
||||||
draw(batch, attachmentSkeleton);
|
draw(batch, attachmentSkeleton);
|
||||||
|
|
||||||
attachmentSkeleton.setX(0);
|
attachmentSkeleton.setX(0);
|
||||||
attachmentSkeleton.setY(0);
|
attachmentSkeleton.setY(0);
|
||||||
rootBone.setScaleX(oldScaleX);
|
rootBone.setScaleX(oldScaleX);
|
||||||
rootBone.setScaleY(oldScaleY);
|
rootBone.setScaleY(oldScaleY);
|
||||||
rootBone.setRotation(oldRotation);
|
rootBone.setRotation(oldRotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slot == clipEnd) {
|
||||||
|
batch.flush();
|
||||||
|
clipEnd();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -142,28 +165,35 @@ public class SkeletonRenderer {
|
|||||||
uvs = mesh.getUVs();
|
uvs = mesh.getUVs();
|
||||||
color = mesh.getColor();
|
color = mesh.getColor();
|
||||||
|
|
||||||
|
} else if (attachment instanceof ClippingAttachment) {
|
||||||
|
ClippingAttachment clip = (ClippingAttachment)attachment;
|
||||||
|
batch.end();
|
||||||
|
clipStart(batch.getProjectionMatrix(), batch.getTransformMatrix(), slot, clip);
|
||||||
|
batch.begin();
|
||||||
|
continue;
|
||||||
|
|
||||||
} else if (attachment instanceof SkeletonAttachment) {
|
} else if (attachment instanceof SkeletonAttachment) {
|
||||||
Skeleton attachmentSkeleton = ((SkeletonAttachment)attachment).getSkeleton();
|
Skeleton attachmentSkeleton = ((SkeletonAttachment)attachment).getSkeleton();
|
||||||
if (attachmentSkeleton == null) continue;
|
if (attachmentSkeleton != null) {
|
||||||
Bone bone = slot.getBone();
|
Bone bone = slot.getBone();
|
||||||
Bone rootBone = attachmentSkeleton.getRootBone();
|
Bone rootBone = attachmentSkeleton.getRootBone();
|
||||||
float oldScaleX = rootBone.getScaleX();
|
float oldScaleX = rootBone.getScaleX();
|
||||||
float oldScaleY = rootBone.getScaleY();
|
float oldScaleY = rootBone.getScaleY();
|
||||||
float oldRotation = rootBone.getRotation();
|
float oldRotation = rootBone.getRotation();
|
||||||
attachmentSkeleton.setPosition(bone.getWorldX(), bone.getWorldY());
|
attachmentSkeleton.setPosition(bone.getWorldX(), bone.getWorldY());
|
||||||
// rootBone.setScaleX(1 + bone.getWorldScaleX() - oldScaleX);
|
// rootBone.setScaleX(1 + bone.getWorldScaleX() - oldScaleX);
|
||||||
// rootBone.setScaleY(1 + bone.getWorldScaleY() - oldScaleY);
|
// rootBone.setScaleY(1 + bone.getWorldScaleY() - oldScaleY);
|
||||||
// Also set shear.
|
// Also set shear.
|
||||||
rootBone.setRotation(oldRotation + bone.getWorldRotationX());
|
rootBone.setRotation(oldRotation + bone.getWorldRotationX());
|
||||||
attachmentSkeleton.updateWorldTransform();
|
attachmentSkeleton.updateWorldTransform();
|
||||||
|
|
||||||
draw(batch, attachmentSkeleton);
|
draw(batch, attachmentSkeleton);
|
||||||
|
|
||||||
attachmentSkeleton.setPosition(0, 0);
|
attachmentSkeleton.setPosition(0, 0);
|
||||||
rootBone.setScaleX(oldScaleX);
|
rootBone.setScaleX(oldScaleX);
|
||||||
rootBone.setScaleY(oldScaleY);
|
rootBone.setScaleY(oldScaleY);
|
||||||
rootBone.setRotation(oldRotation);
|
rootBone.setRotation(oldRotation);
|
||||||
continue;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (texture != null) {
|
if (texture != null) {
|
||||||
@ -186,6 +216,11 @@ public class SkeletonRenderer {
|
|||||||
}
|
}
|
||||||
batch.draw(texture, vertices, 0, verticesLength, triangles, 0, triangles.length);
|
batch.draw(texture, vertices, 0, verticesLength, triangles, 0, triangles.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (slot == clipEnd) {
|
||||||
|
batch.flush();
|
||||||
|
clipEnd();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,28 +259,35 @@ public class SkeletonRenderer {
|
|||||||
uvs = mesh.getUVs();
|
uvs = mesh.getUVs();
|
||||||
color = mesh.getColor();
|
color = mesh.getColor();
|
||||||
|
|
||||||
|
} else if (attachment instanceof ClippingAttachment) {
|
||||||
|
ClippingAttachment clip = (ClippingAttachment)attachment;
|
||||||
|
batch.end();
|
||||||
|
clipStart(batch.getProjectionMatrix(), batch.getTransformMatrix(), slot, clip);
|
||||||
|
batch.begin();
|
||||||
|
continue;
|
||||||
|
|
||||||
} else if (attachment instanceof SkeletonAttachment) {
|
} else if (attachment instanceof SkeletonAttachment) {
|
||||||
Skeleton attachmentSkeleton = ((SkeletonAttachment)attachment).getSkeleton();
|
Skeleton attachmentSkeleton = ((SkeletonAttachment)attachment).getSkeleton();
|
||||||
if (attachmentSkeleton == null) continue;
|
if (attachmentSkeleton != null) {
|
||||||
Bone bone = slot.getBone();
|
Bone bone = slot.getBone();
|
||||||
Bone rootBone = attachmentSkeleton.getRootBone();
|
Bone rootBone = attachmentSkeleton.getRootBone();
|
||||||
float oldScaleX = rootBone.getScaleX();
|
float oldScaleX = rootBone.getScaleX();
|
||||||
float oldScaleY = rootBone.getScaleY();
|
float oldScaleY = rootBone.getScaleY();
|
||||||
float oldRotation = rootBone.getRotation();
|
float oldRotation = rootBone.getRotation();
|
||||||
attachmentSkeleton.setPosition(bone.getWorldX(), bone.getWorldY());
|
attachmentSkeleton.setPosition(bone.getWorldX(), bone.getWorldY());
|
||||||
// rootBone.setScaleX(1 + bone.getWorldScaleX() - oldScaleX);
|
// rootBone.setScaleX(1 + bone.getWorldScaleX() - oldScaleX);
|
||||||
// rootBone.setScaleY(1 + bone.getWorldScaleY() - oldScaleY);
|
// rootBone.setScaleY(1 + bone.getWorldScaleY() - oldScaleY);
|
||||||
// Also set shear.
|
// Also set shear.
|
||||||
rootBone.setRotation(oldRotation + bone.getWorldRotationX());
|
rootBone.setRotation(oldRotation + bone.getWorldRotationX());
|
||||||
attachmentSkeleton.updateWorldTransform();
|
attachmentSkeleton.updateWorldTransform();
|
||||||
|
|
||||||
draw(batch, attachmentSkeleton);
|
draw(batch, attachmentSkeleton);
|
||||||
|
|
||||||
attachmentSkeleton.setPosition(0, 0);
|
attachmentSkeleton.setPosition(0, 0);
|
||||||
rootBone.setScaleX(oldScaleX);
|
rootBone.setScaleX(oldScaleX);
|
||||||
rootBone.setScaleY(oldScaleY);
|
rootBone.setScaleY(oldScaleY);
|
||||||
rootBone.setRotation(oldRotation);
|
rootBone.setRotation(oldRotation);
|
||||||
continue;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (texture != null) {
|
if (texture != null) {
|
||||||
@ -275,9 +317,47 @@ public class SkeletonRenderer {
|
|||||||
}
|
}
|
||||||
batch.draw(texture, vertices, 0, verticesLength, triangles, 0, triangles.length);
|
batch.draw(texture, vertices, 0, verticesLength, triangles, 0, triangles.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (slot == clipEnd) {
|
||||||
|
batch.flush();
|
||||||
|
clipEnd();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void clipStart (Matrix4 transformMatrix, Matrix4 projectionMatrix, Slot slot, ClippingAttachment clip) {
|
||||||
|
if (clipEnd != null) return;
|
||||||
|
clipEnd = clip.getEnd();
|
||||||
|
|
||||||
|
int n = clip.getWorldVerticesLength();
|
||||||
|
float[] vertices = this.vertices.setSize(n);
|
||||||
|
clip.computeWorldVertices(slot, 0, n, vertices, 0, 2);
|
||||||
|
|
||||||
|
Gdx.gl.glClearStencil(0);
|
||||||
|
Gdx.gl.glClear(GL20.GL_STENCIL_BUFFER_BIT);
|
||||||
|
Gdx.gl.glEnable(GL20.GL_STENCIL_TEST);
|
||||||
|
Gdx.gl.glStencilFunc(GL20.GL_NEVER, 0, 1);
|
||||||
|
Gdx.gl.glStencilOp(GL20.GL_INVERT, GL20.GL_INVERT, GL20.GL_INVERT);
|
||||||
|
Gdx.gl.glColorMask(false, false, false, false);
|
||||||
|
|
||||||
|
if (renderer == null || renderer.getMaxVertices() < n)
|
||||||
|
renderer = new ImmediateModeRenderer20(Math.max(100, n), false, false, 0);
|
||||||
|
renderer.begin(combinedMatrix.set(projectionMatrix).mul(transformMatrix), GL20.GL_TRIANGLE_FAN);
|
||||||
|
renderer.vertex(vertices[0], vertices[1], 0);
|
||||||
|
for (int i = 2; i < n; i += 2)
|
||||||
|
renderer.vertex(vertices[i], vertices[i + 1], 0);
|
||||||
|
renderer.end();
|
||||||
|
|
||||||
|
Gdx.gl.glColorMask(true, true, true, true);
|
||||||
|
Gdx.gl.glStencilFunc(clip.getInvert() ? GL20.GL_NOTEQUAL : GL20.GL_EQUAL, 1, 1);
|
||||||
|
Gdx.gl.glStencilOp(GL20.GL_KEEP, GL20.GL_KEEP, GL20.GL_KEEP);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clipEnd () {
|
||||||
|
clipEnd = null;
|
||||||
|
Gdx.gl.glDisable(GL20.GL_STENCIL_TEST);
|
||||||
|
}
|
||||||
|
|
||||||
public void setPremultipliedAlpha (boolean premultipliedAlpha) {
|
public void setPremultipliedAlpha (boolean premultipliedAlpha) {
|
||||||
this.premultipliedAlpha = premultipliedAlpha;
|
this.premultipliedAlpha = premultipliedAlpha;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,6 +40,7 @@ import com.badlogic.gdx.utils.Array;
|
|||||||
import com.badlogic.gdx.utils.FloatArray;
|
import com.badlogic.gdx.utils.FloatArray;
|
||||||
import com.esotericsoftware.spine.attachments.Attachment;
|
import com.esotericsoftware.spine.attachments.Attachment;
|
||||||
import com.esotericsoftware.spine.attachments.BoundingBoxAttachment;
|
import com.esotericsoftware.spine.attachments.BoundingBoxAttachment;
|
||||||
|
import com.esotericsoftware.spine.attachments.ClippingAttachment;
|
||||||
import com.esotericsoftware.spine.attachments.MeshAttachment;
|
import com.esotericsoftware.spine.attachments.MeshAttachment;
|
||||||
import com.esotericsoftware.spine.attachments.PathAttachment;
|
import com.esotericsoftware.spine.attachments.PathAttachment;
|
||||||
import com.esotericsoftware.spine.attachments.PointAttachment;
|
import com.esotericsoftware.spine.attachments.PointAttachment;
|
||||||
@ -51,10 +52,11 @@ public class SkeletonRendererDebug {
|
|||||||
static private final Color attachmentLineColor = new Color(0, 0, 1, 0.5f);
|
static private final Color attachmentLineColor = new Color(0, 0, 1, 0.5f);
|
||||||
static private final Color triangleLineColor = new Color(1, 0.64f, 0, 0.5f);
|
static private final Color triangleLineColor = new Color(1, 0.64f, 0, 0.5f);
|
||||||
static private final Color aabbColor = new Color(0, 1, 0, 0.5f);
|
static private final Color aabbColor = new Color(0, 1, 0, 0.5f);
|
||||||
|
static private final Color clippingLineColor = Color.MAGENTA;
|
||||||
|
|
||||||
private final ShapeRenderer shapes;
|
private final ShapeRenderer shapes;
|
||||||
private boolean drawBones = true, drawRegionAttachments = true, drawBoundingBoxes = true, drawPoints = true;
|
private boolean drawBones = true, drawRegionAttachments = true, drawBoundingBoxes = true, drawPoints = true;
|
||||||
private boolean drawMeshHull = true, drawMeshTriangles = true, drawPaths = true;
|
private boolean drawMeshHull = true, drawMeshTriangles = true, drawPaths = true, drawClipping = true;
|
||||||
private final SkeletonBounds bounds = new SkeletonBounds();
|
private final SkeletonBounds bounds = new SkeletonBounds();
|
||||||
private final FloatArray vertices = new FloatArray(32);
|
private final FloatArray vertices = new FloatArray(32);
|
||||||
private float scale = 1;
|
private float scale = 1;
|
||||||
@ -179,6 +181,22 @@ public class SkeletonRendererDebug {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (drawClipping) {
|
||||||
|
for (int i = 0, n = slots.size; i < n; i++) {
|
||||||
|
Slot slot = slots.get(i);
|
||||||
|
Attachment attachment = slot.attachment;
|
||||||
|
if (!(attachment instanceof ClippingAttachment)) continue;
|
||||||
|
ClippingAttachment clip = (ClippingAttachment)attachment;
|
||||||
|
int nn = clip.getWorldVerticesLength();
|
||||||
|
float[] vertices = this.vertices.setSize(nn);
|
||||||
|
clip.computeWorldVertices(slot, 0, nn, vertices, 0, 2);
|
||||||
|
shapes.setColor(clippingLineColor);
|
||||||
|
for (int ii = 2; ii < nn; ii += 2)
|
||||||
|
shapes.line(vertices[ii - 2], vertices[ii - 1], vertices[ii], vertices[ii + 1]);
|
||||||
|
shapes.line(vertices[0], vertices[1], vertices[nn - 2], vertices[nn - 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (drawPaths) {
|
if (drawPaths) {
|
||||||
for (int i = 0, n = slots.size; i < n; i++) {
|
for (int i = 0, n = slots.size; i < n; i++) {
|
||||||
Slot slot = slots.get(i);
|
Slot slot = slots.get(i);
|
||||||
|
|||||||
@ -68,6 +68,10 @@ public class AtlasAttachmentLoader implements AttachmentLoader {
|
|||||||
return new BoundingBoxAttachment(name);
|
return new BoundingBoxAttachment(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ClippingAttachment newClippingAttachment (Skin skin, String name) {
|
||||||
|
return new ClippingAttachment(name);
|
||||||
|
}
|
||||||
|
|
||||||
public PathAttachment newPathAttachment (Skin skin, String name) {
|
public PathAttachment newPathAttachment (Skin skin, String name) {
|
||||||
return new PathAttachment(name);
|
return new PathAttachment(name);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,6 +45,9 @@ public interface AttachmentLoader {
|
|||||||
|
|
||||||
/** @return May be null to not load the attachment. */
|
/** @return May be null to not load the attachment. */
|
||||||
public BoundingBoxAttachment newBoundingBoxAttachment (Skin skin, String name);
|
public BoundingBoxAttachment newBoundingBoxAttachment (Skin skin, String name);
|
||||||
|
|
||||||
|
/** @return May be null to not load the attachment. */
|
||||||
|
public ClippingAttachment newClippingAttachment (Skin skin, String name);
|
||||||
|
|
||||||
/** @return May be null to not load the attachment. */
|
/** @return May be null to not load the attachment. */
|
||||||
public PathAttachment newPathAttachment (Skin skin, String name);
|
public PathAttachment newPathAttachment (Skin skin, String name);
|
||||||
|
|||||||
@ -0,0 +1,72 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Spine Runtimes Software License v2.5
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2016, Esoteric Software
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* You are granted a perpetual, non-exclusive, non-sublicensable, and
|
||||||
|
* non-transferable license to use, install, execute, and perform the Spine
|
||||||
|
* Runtimes software and derivative works solely for personal or internal
|
||||||
|
* use. Without the written permission of Esoteric Software (see Section 2 of
|
||||||
|
* the Spine Software License Agreement), you may not (a) modify, translate,
|
||||||
|
* adapt, or develop new applications using the Spine Runtimes or otherwise
|
||||||
|
* create derivative works or improvements of the Spine Runtimes or (b) remove,
|
||||||
|
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
|
||||||
|
* or other intellectual property or proprietary rights notices on or in the
|
||||||
|
* Software, including any copy thereof. Redistributions in binary or source
|
||||||
|
* form must include this license and terms.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||||
|
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
|
||||||
|
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||||
|
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
package com.esotericsoftware.spine.attachments;
|
||||||
|
|
||||||
|
import com.badlogic.gdx.graphics.Color;
|
||||||
|
import com.esotericsoftware.spine.Slot;
|
||||||
|
|
||||||
|
/** An attachment with vertices that make up a polygon used for clipping the rendering of other attachments. */
|
||||||
|
public class ClippingAttachment extends VertexAttachment {
|
||||||
|
Slot end;
|
||||||
|
boolean invert;
|
||||||
|
|
||||||
|
// Nonessential.
|
||||||
|
final Color color = new Color(0.38f, 0.94f, 0, 1);
|
||||||
|
|
||||||
|
public ClippingAttachment (String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Clipping is performed between the clipping polygon's slot and the end slot. */
|
||||||
|
public Slot getEnd () {
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnd (Slot end) {
|
||||||
|
this.end = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** If false, attachments outside the clipping polygon will be drawn. If true, attachments inside the clipping polygon will be
|
||||||
|
* drawn. */
|
||||||
|
public boolean getInvert () {
|
||||||
|
return invert;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInvert (boolean invert) {
|
||||||
|
this.invert = invert;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The color of the clipping polygon as it was in Spine. Available only when nonessential data was exported. Clipping polygons
|
||||||
|
* are not usually rendered at runtime. */
|
||||||
|
public Color getColor () {
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user