mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-06 23:34:53 +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.attachments.AttachmentLoader;
|
||||
import com.esotericsoftware.spine.attachments.BoundingBoxAttachment;
|
||||
import com.esotericsoftware.spine.attachments.ClippingAttachment;
|
||||
import com.esotericsoftware.spine.attachments.MeshAttachment;
|
||||
import com.esotericsoftware.spine.attachments.PathAttachment;
|
||||
import com.esotericsoftware.spine.attachments.PointAttachment;
|
||||
@ -60,6 +61,10 @@ public class AnimationStateTests {
|
||||
return null;
|
||||
}
|
||||
|
||||
public ClippingAttachment newClippingAttachment (Skin skin, String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public PathAttachment newPathAttachment (Skin skin, String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -33,6 +33,7 @@ package com.esotericsoftware.spine;
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import com.esotericsoftware.spine.attachments.AttachmentLoader;
|
||||
import com.esotericsoftware.spine.attachments.BoundingBoxAttachment;
|
||||
import com.esotericsoftware.spine.attachments.ClippingAttachment;
|
||||
import com.esotericsoftware.spine.attachments.RegionAttachment;
|
||||
import com.esotericsoftware.spine.attachments.MeshAttachment;
|
||||
import com.esotericsoftware.spine.attachments.PathAttachment;
|
||||
@ -54,6 +55,10 @@ public class BonePlotting {
|
||||
return null;
|
||||
}
|
||||
|
||||
public ClippingAttachment newClippingAttachment (Skin skin, String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public PathAttachment newPathAttachment (Skin skin, String name) {
|
||||
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();
|
||||
return region;
|
||||
}
|
||||
// BOZO! - Binary clip export.
|
||||
case boundingbox: {
|
||||
int vertexCount = input.readInt(true);
|
||||
Vertices vertices = readVertices(input, vertexCount);
|
||||
|
||||
@ -337,6 +337,7 @@ public class SkeletonJson {
|
||||
region.updateOffset();
|
||||
return region;
|
||||
}
|
||||
// BOZO! - JSON clip export.
|
||||
case boundingbox: {
|
||||
BoundingBoxAttachment box = attachmentLoader.newBoundingBoxAttachment(skin, name);
|
||||
if (box == null) return null;
|
||||
|
||||
@ -30,14 +30,20 @@
|
||||
|
||||
package com.esotericsoftware.spine;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.g2d.Batch;
|
||||
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.FloatArray;
|
||||
import com.badlogic.gdx.utils.NumberUtils;
|
||||
import com.esotericsoftware.spine.attachments.Attachment;
|
||||
import com.esotericsoftware.spine.attachments.ClippingAttachment;
|
||||
import com.esotericsoftware.spine.attachments.MeshAttachment;
|
||||
import com.esotericsoftware.spine.attachments.RegionAttachment;
|
||||
import com.esotericsoftware.spine.attachments.SkeletonAttachment;
|
||||
@ -49,6 +55,10 @@ public class SkeletonRenderer {
|
||||
private boolean premultipliedAlpha;
|
||||
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) {
|
||||
boolean premultipliedAlpha = this.premultipliedAlpha;
|
||||
float[] vertices = this.vertices.items;
|
||||
@ -78,31 +88,44 @@ public class SkeletonRenderer {
|
||||
batch.setBlendFunction(blendMode.getSource(premultipliedAlpha), blendMode.getDest());
|
||||
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) {
|
||||
throw new RuntimeException("SkeletonMeshRenderer is required to render meshes.");
|
||||
|
||||
} else if (attachment instanceof SkeletonAttachment) {
|
||||
Skeleton attachmentSkeleton = ((SkeletonAttachment)attachment).getSkeleton();
|
||||
if (attachmentSkeleton == null) continue;
|
||||
Bone bone = slot.getBone();
|
||||
Bone rootBone = attachmentSkeleton.getRootBone();
|
||||
float oldScaleX = rootBone.getScaleX();
|
||||
float oldScaleY = rootBone.getScaleY();
|
||||
float oldRotation = rootBone.getRotation();
|
||||
attachmentSkeleton.setPosition(bone.getWorldX(), bone.getWorldY());
|
||||
// rootBone.setScaleX(1 + bone.getWorldScaleX() - oldScaleX);
|
||||
// rootBone.setScaleY(1 + bone.getWorldScaleY() - oldScaleY);
|
||||
// Set shear.
|
||||
rootBone.setRotation(oldRotation + bone.getWorldRotationX());
|
||||
attachmentSkeleton.updateWorldTransform();
|
||||
if (attachmentSkeleton != null) {
|
||||
Bone bone = slot.getBone();
|
||||
Bone rootBone = attachmentSkeleton.getRootBone();
|
||||
float oldScaleX = rootBone.getScaleX();
|
||||
float oldScaleY = rootBone.getScaleY();
|
||||
float oldRotation = rootBone.getRotation();
|
||||
attachmentSkeleton.setPosition(bone.getWorldX(), bone.getWorldY());
|
||||
// rootBone.setScaleX(1 + bone.getWorldScaleX() - oldScaleX);
|
||||
// rootBone.setScaleY(1 + bone.getWorldScaleY() - oldScaleY);
|
||||
// Set shear.
|
||||
rootBone.setRotation(oldRotation + bone.getWorldRotationX());
|
||||
attachmentSkeleton.updateWorldTransform();
|
||||
|
||||
draw(batch, attachmentSkeleton);
|
||||
draw(batch, attachmentSkeleton);
|
||||
|
||||
attachmentSkeleton.setX(0);
|
||||
attachmentSkeleton.setY(0);
|
||||
rootBone.setScaleX(oldScaleX);
|
||||
rootBone.setScaleY(oldScaleY);
|
||||
rootBone.setRotation(oldRotation);
|
||||
attachmentSkeleton.setX(0);
|
||||
attachmentSkeleton.setY(0);
|
||||
rootBone.setScaleX(oldScaleX);
|
||||
rootBone.setScaleY(oldScaleY);
|
||||
rootBone.setRotation(oldRotation);
|
||||
}
|
||||
}
|
||||
|
||||
if (slot == clipEnd) {
|
||||
batch.flush();
|
||||
clipEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -142,28 +165,35 @@ public class SkeletonRenderer {
|
||||
uvs = mesh.getUVs();
|
||||
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) {
|
||||
Skeleton attachmentSkeleton = ((SkeletonAttachment)attachment).getSkeleton();
|
||||
if (attachmentSkeleton == null) continue;
|
||||
Bone bone = slot.getBone();
|
||||
Bone rootBone = attachmentSkeleton.getRootBone();
|
||||
float oldScaleX = rootBone.getScaleX();
|
||||
float oldScaleY = rootBone.getScaleY();
|
||||
float oldRotation = rootBone.getRotation();
|
||||
attachmentSkeleton.setPosition(bone.getWorldX(), bone.getWorldY());
|
||||
// rootBone.setScaleX(1 + bone.getWorldScaleX() - oldScaleX);
|
||||
// rootBone.setScaleY(1 + bone.getWorldScaleY() - oldScaleY);
|
||||
// Also set shear.
|
||||
rootBone.setRotation(oldRotation + bone.getWorldRotationX());
|
||||
attachmentSkeleton.updateWorldTransform();
|
||||
if (attachmentSkeleton != null) {
|
||||
Bone bone = slot.getBone();
|
||||
Bone rootBone = attachmentSkeleton.getRootBone();
|
||||
float oldScaleX = rootBone.getScaleX();
|
||||
float oldScaleY = rootBone.getScaleY();
|
||||
float oldRotation = rootBone.getRotation();
|
||||
attachmentSkeleton.setPosition(bone.getWorldX(), bone.getWorldY());
|
||||
// rootBone.setScaleX(1 + bone.getWorldScaleX() - oldScaleX);
|
||||
// rootBone.setScaleY(1 + bone.getWorldScaleY() - oldScaleY);
|
||||
// Also set shear.
|
||||
rootBone.setRotation(oldRotation + bone.getWorldRotationX());
|
||||
attachmentSkeleton.updateWorldTransform();
|
||||
|
||||
draw(batch, attachmentSkeleton);
|
||||
draw(batch, attachmentSkeleton);
|
||||
|
||||
attachmentSkeleton.setPosition(0, 0);
|
||||
rootBone.setScaleX(oldScaleX);
|
||||
rootBone.setScaleY(oldScaleY);
|
||||
rootBone.setRotation(oldRotation);
|
||||
continue;
|
||||
attachmentSkeleton.setPosition(0, 0);
|
||||
rootBone.setScaleX(oldScaleX);
|
||||
rootBone.setScaleY(oldScaleY);
|
||||
rootBone.setRotation(oldRotation);
|
||||
}
|
||||
}
|
||||
|
||||
if (texture != null) {
|
||||
@ -186,6 +216,11 @@ public class SkeletonRenderer {
|
||||
}
|
||||
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();
|
||||
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) {
|
||||
Skeleton attachmentSkeleton = ((SkeletonAttachment)attachment).getSkeleton();
|
||||
if (attachmentSkeleton == null) continue;
|
||||
Bone bone = slot.getBone();
|
||||
Bone rootBone = attachmentSkeleton.getRootBone();
|
||||
float oldScaleX = rootBone.getScaleX();
|
||||
float oldScaleY = rootBone.getScaleY();
|
||||
float oldRotation = rootBone.getRotation();
|
||||
attachmentSkeleton.setPosition(bone.getWorldX(), bone.getWorldY());
|
||||
// rootBone.setScaleX(1 + bone.getWorldScaleX() - oldScaleX);
|
||||
// rootBone.setScaleY(1 + bone.getWorldScaleY() - oldScaleY);
|
||||
// Also set shear.
|
||||
rootBone.setRotation(oldRotation + bone.getWorldRotationX());
|
||||
attachmentSkeleton.updateWorldTransform();
|
||||
if (attachmentSkeleton != null) {
|
||||
Bone bone = slot.getBone();
|
||||
Bone rootBone = attachmentSkeleton.getRootBone();
|
||||
float oldScaleX = rootBone.getScaleX();
|
||||
float oldScaleY = rootBone.getScaleY();
|
||||
float oldRotation = rootBone.getRotation();
|
||||
attachmentSkeleton.setPosition(bone.getWorldX(), bone.getWorldY());
|
||||
// rootBone.setScaleX(1 + bone.getWorldScaleX() - oldScaleX);
|
||||
// rootBone.setScaleY(1 + bone.getWorldScaleY() - oldScaleY);
|
||||
// Also set shear.
|
||||
rootBone.setRotation(oldRotation + bone.getWorldRotationX());
|
||||
attachmentSkeleton.updateWorldTransform();
|
||||
|
||||
draw(batch, attachmentSkeleton);
|
||||
draw(batch, attachmentSkeleton);
|
||||
|
||||
attachmentSkeleton.setPosition(0, 0);
|
||||
rootBone.setScaleX(oldScaleX);
|
||||
rootBone.setScaleY(oldScaleY);
|
||||
rootBone.setRotation(oldRotation);
|
||||
continue;
|
||||
attachmentSkeleton.setPosition(0, 0);
|
||||
rootBone.setScaleX(oldScaleX);
|
||||
rootBone.setScaleY(oldScaleY);
|
||||
rootBone.setRotation(oldRotation);
|
||||
}
|
||||
}
|
||||
|
||||
if (texture != null) {
|
||||
@ -275,9 +317,47 @@ public class SkeletonRenderer {
|
||||
}
|
||||
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) {
|
||||
this.premultipliedAlpha = premultipliedAlpha;
|
||||
}
|
||||
|
||||
@ -40,6 +40,7 @@ import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.FloatArray;
|
||||
import com.esotericsoftware.spine.attachments.Attachment;
|
||||
import com.esotericsoftware.spine.attachments.BoundingBoxAttachment;
|
||||
import com.esotericsoftware.spine.attachments.ClippingAttachment;
|
||||
import com.esotericsoftware.spine.attachments.MeshAttachment;
|
||||
import com.esotericsoftware.spine.attachments.PathAttachment;
|
||||
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 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 clippingLineColor = Color.MAGENTA;
|
||||
|
||||
private final ShapeRenderer shapes;
|
||||
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 FloatArray vertices = new FloatArray(32);
|
||||
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) {
|
||||
for (int i = 0, n = slots.size; i < n; i++) {
|
||||
Slot slot = slots.get(i);
|
||||
|
||||
@ -68,6 +68,10 @@ public class AtlasAttachmentLoader implements AttachmentLoader {
|
||||
return new BoundingBoxAttachment(name);
|
||||
}
|
||||
|
||||
public ClippingAttachment newClippingAttachment (Skin skin, String name) {
|
||||
return new ClippingAttachment(name);
|
||||
}
|
||||
|
||||
public PathAttachment newPathAttachment (Skin skin, String name) {
|
||||
return new PathAttachment(name);
|
||||
}
|
||||
|
||||
@ -45,6 +45,9 @@ public interface AttachmentLoader {
|
||||
|
||||
/** @return May be null to not load the attachment. */
|
||||
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. */
|
||||
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