mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-06 23:34:53 +08:00
Bit of reorganization.
It's pretty now!
This commit is contained in:
parent
8d9d46ca56
commit
ff98e136a1
@ -1,178 +0,0 @@
|
||||
/******************************************************************************
|
||||
* 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.BitmapFont;
|
||||
import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
|
||||
import com.badlogic.gdx.math.WindowedMean;
|
||||
import com.esotericsoftware.spine.attachments.ClippingAttachment;
|
||||
|
||||
public class ClippingTest extends ApplicationAdapter {
|
||||
OrthographicCamera camera;
|
||||
PolygonSpriteBatch batch;
|
||||
SkeletonRenderer renderer;
|
||||
SkeletonRendererDebug debugRenderer;
|
||||
BitmapFont font;
|
||||
|
||||
TextureAtlas atlas;
|
||||
Skeleton skeleton;
|
||||
AnimationState state;
|
||||
|
||||
WindowedMean mean = new WindowedMean(30);
|
||||
|
||||
public void create () {
|
||||
camera = new OrthographicCamera();
|
||||
batch = new PolygonSpriteBatch(2048);
|
||||
renderer = new SkeletonRenderer();
|
||||
renderer.setPremultipliedAlpha(true);
|
||||
debugRenderer = new SkeletonRendererDebug();
|
||||
debugRenderer.setBoundingBoxes(false);
|
||||
debugRenderer.setRegionAttachments(false);
|
||||
font = new BitmapFont();
|
||||
|
||||
atlas = new TextureAtlas(Gdx.files.internal("raptor/raptor-pma.atlas"));
|
||||
SkeletonJson json = new SkeletonJson(atlas);
|
||||
json.setScale(0.6f);
|
||||
SkeletonData skeletonData = json.readSkeletonData(Gdx.files.internal("raptor/raptor.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, "walk", true);
|
||||
state.addAnimation(0, "Jump", false, 2);
|
||||
state.addAnimation(0, "walk", true, 0);
|
||||
|
||||
// Create a clipping attachment, slot data, and slot.
|
||||
ClippingAttachment clip = new ClippingAttachment("clip");
|
||||
|
||||
// Spiral.
|
||||
clip.setVertices(new float[] {430.90802f, 278.212f, 72.164f, 361.816f, 31.143997f, 128.804f, 191.896f, 61.0f, 291.312f,
|
||||
175.73201f, 143.956f, 207.408f, 161.4f, 145.628f, 227.456f, 160.61601f, 224.392f, 126.535995f, 188.264f, 113.144f,
|
||||
147.13199f, 108.87601f, 77.035995f, 158.212f, 86.15199f, 220.676f, 102.77199f, 240.716f, 174.74399f, 243.20801f,
|
||||
250.572f, 216.74802f, 324.772f, 200.33202f, 309.388f, 124.968f, 258.168f, 60.503998f, 199.696f, 42.872f, 116.951996f,
|
||||
6.7400017f, 11.332001f, 72.48f, -6.708008f, 143.136f, 1.0679932f, 239.92801f, 26.5f, 355.6f, -47.380005f, 377.52798f,
|
||||
-40.608f, 303.1f, -53.584015f, 77.316f, 5.4600067f, 8.728001f, 113.343994f, -56.04f, 192.42801f, -45.112f, 274.564f,
|
||||
-38.784f, 322.592f, -10.604f, 371.98f, 21.920002f, 405.16f, 60.896004f, 428.68f, 104.852005f, 406.996f, 188.976f,
|
||||
364.58398f, 220.14401f, 309.3f, 238.788f, 263.232f, 244.75201f, 219.468f, 271.58002f, 210.824f, 294.176f, 250.664f,
|
||||
295.2f, 295.972f, 276.02f, 357.46f, 269.172f, 420.008f, 242.37201f, 466.63602f, 207.648f, 437.516f, -10.579998f,
|
||||
378.05603f, -64.624f, 465.24f, -104.992f, 554.11206f, 95.43199f, 514.89197f, 259.02f});
|
||||
|
||||
// Polygon:
|
||||
// clip.setVertices(
|
||||
// new float[] { 94.0f, 84.0f, 45.0f, 165.0f, 218.0f, 292.0f, 476.0f, 227.0f, 480.0f, 125.0f, 325.0f, 191.0f, 333.0f, 77.0f,
|
||||
// 302.0f, 30.0f, 175.0f, 140.0f });
|
||||
|
||||
// Rectangle:
|
||||
// new float[] { //
|
||||
// -140, 50, //
|
||||
// 250, 50, //
|
||||
// 250, 350, //
|
||||
// -140, 350, //
|
||||
// });
|
||||
|
||||
// Self intersection:
|
||||
// clip.setVertices(new float[] { //
|
||||
// -140, -50, //
|
||||
// 120, 50, //
|
||||
// 120, -50, //
|
||||
// -140, 50, //
|
||||
// });
|
||||
|
||||
for (int j = 0; j < clip.getVertices().length; j += 2) {
|
||||
clip.getVertices()[j] = (clip.getVertices()[j] - 150f);
|
||||
clip.getVertices()[j + 1] = (clip.getVertices()[j + 1] + 100);
|
||||
}
|
||||
clip.setWorldVerticesLength(clip.getVertices().length);
|
||||
clip.setEndSlot(skeleton.findSlot("front_hand").data.index);
|
||||
|
||||
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("back_hand").getIndex(), clipSlot);
|
||||
}
|
||||
|
||||
public void render () {
|
||||
state.update(Gdx.graphics.getDeltaTime() * 0.3f);
|
||||
state.update(0);
|
||||
|
||||
Gdx.gl.glClearColor(0.3f, 0.3f, 0.3f, 1);
|
||||
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();
|
||||
long start = System.nanoTime();
|
||||
renderer.draw(batch, skeleton);
|
||||
mean.addValue((System.nanoTime() - start) / 1000000.0f);
|
||||
renderer.setPremultipliedAlpha(false);
|
||||
font.draw(batch, "Time: " + mean.getMean() + "ms", 10, Gdx.graphics.getHeight() - font.getLineHeight());
|
||||
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.width = 800;
|
||||
config.height = 600;
|
||||
new LwjglApplication(new ClippingTest(), config);
|
||||
}
|
||||
}
|
||||
@ -1,274 +0,0 @@
|
||||
|
||||
package com.esotericsoftware.spine;
|
||||
|
||||
import com.badlogic.gdx.ApplicationAdapter;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.Input.Buttons;
|
||||
import com.badlogic.gdx.Input.Keys;
|
||||
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
|
||||
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.OrthographicCamera;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.g2d.BitmapFont;
|
||||
import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch;
|
||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
|
||||
import com.badlogic.gdx.math.Intersector;
|
||||
import com.badlogic.gdx.math.MathUtils;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.FloatArray;
|
||||
import com.esotericsoftware.spine.utils.Clipper;
|
||||
import com.esotericsoftware.spine.utils.ConvexDecomposer;
|
||||
|
||||
public class ConvexDecomposerTest extends ApplicationAdapter {
|
||||
OrthographicCamera sceneCamera;
|
||||
ShapeRenderer shapes;
|
||||
PolygonSpriteBatch polyBatcher;
|
||||
Texture image;
|
||||
ConvexDecomposer decomposer = new ConvexDecomposer();
|
||||
FloatArray polygon = new FloatArray();
|
||||
Array<FloatArray> convexPolygons = new Array<FloatArray>();
|
||||
boolean isCreatingPolygon = false;
|
||||
Vector3 tmp = new Vector3();
|
||||
Array<Color> colors = new Array<Color>();
|
||||
BitmapFont font;
|
||||
|
||||
@Override
|
||||
public void create () {
|
||||
sceneCamera = new OrthographicCamera();
|
||||
shapes = new ShapeRenderer();
|
||||
polyBatcher = new PolygonSpriteBatch();
|
||||
image = new Texture("skin/skin.png");
|
||||
font = new BitmapFont();
|
||||
|
||||
float[] v = new float[] {430.90802f, 278.212f, 72.164f, 361.816f, 31.143997f, 128.804f, 191.896f, 61.0f, 291.312f,
|
||||
175.73201f, 143.956f, 207.408f, 161.4f, 145.628f, 227.456f, 160.61601f, 224.392f, 126.535995f, 188.264f, 113.144f,
|
||||
147.13199f, 108.87601f, 77.035995f, 158.212f, 86.15199f, 220.676f, 102.77199f, 240.716f, 174.74399f, 243.20801f,
|
||||
250.572f, 216.74802f, 324.772f, 200.33202f, 309.388f, 124.968f, 258.168f, 60.503998f, 199.696f, 42.872f, 116.951996f,
|
||||
6.7400017f, 11.332001f, 72.48f, -6.708008f, 143.136f, 1.0679932f, 239.92801f, 26.5f, 355.6f, -47.380005f, 377.52798f,
|
||||
-40.608f, 303.1f, -53.584015f, 77.316f, 5.4600067f, 8.728001f, 113.343994f, -56.04f, 192.42801f, -45.112f, 274.564f,
|
||||
-38.784f, 322.592f, -10.604f, 371.98f, 21.920002f, 405.16f, 60.896004f, 428.68f, 104.852005f, 406.996f, 188.976f,
|
||||
364.58398f, 220.14401f, 309.3f, 238.788f, 263.232f, 244.75201f, 219.468f, 271.58002f, 210.824f, 294.176f, 250.664f,
|
||||
295.2f, 295.972f, 276.02f, 357.46f, 269.172f, 420.008f, 242.37201f, 466.63602f, 207.648f, 437.516f, -10.579998f,
|
||||
378.05603f, -64.624f, 465.24f, -104.992f, 554.11206f, 95.43199f, 514.89197f, 259.02f};
|
||||
for (int i = 0, n = v.length; i < n; i++)
|
||||
v[i] += 200;
|
||||
|
||||
// float[] v = new float[] { 94.0f, 84.0f, 45.0f, 165.0f, 218.0f, 292.0f, 476.0f, 227.0f, 480.0f, 125.0f, 325.0f, 191.0f,
|
||||
// 333.0f, 77.0f, 302.0f, 30.0f, 175.0f, 140.0f };
|
||||
// float[] v = {87, 288, 217, 371, 456, 361, 539, 175, 304, 194, 392, 290, 193, 214, 123, 15, 14, 137};
|
||||
// float[] v = { 336, 153, 207, 184, 364, 333, 529, 326, 584, 130, 438, 224 };
|
||||
polygon.addAll(v);
|
||||
triangulate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize (int width, int height) {
|
||||
sceneCamera.setToOrtho(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render () {
|
||||
Gdx.gl.glClearColor(0.3f, 0.3f, 0.3f, 1);
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
|
||||
|
||||
processInput();
|
||||
renderScene();
|
||||
}
|
||||
|
||||
private void processInput () {
|
||||
tmp.set(Gdx.input.getX(), Gdx.input.getY(), 0);
|
||||
sceneCamera.unproject(tmp);
|
||||
|
||||
if (Gdx.input.justTouched()) {
|
||||
if (!isCreatingPolygon) {
|
||||
polygon.clear();
|
||||
convexPolygons = null;
|
||||
isCreatingPolygon = true;
|
||||
}
|
||||
|
||||
polygon.add((int)tmp.x);
|
||||
polygon.add((int)tmp.y);
|
||||
|
||||
if (Gdx.input.isButtonPressed(Buttons.RIGHT)) {
|
||||
isCreatingPolygon = false;
|
||||
System.out.print("float[] v = { ");
|
||||
for (int i = 0; i < polygon.size; i++) {
|
||||
System.out.print(polygon.get(i));
|
||||
if (i != polygon.size - 1) System.out.print(", ");
|
||||
}
|
||||
System.out.println("};");
|
||||
triangulate();
|
||||
}
|
||||
}
|
||||
|
||||
if (Gdx.input.isKeyJustPressed(Keys.R)) {
|
||||
long start = System.nanoTime();
|
||||
generateRandomPolygon();
|
||||
System.out.println("Took: " + (System.nanoTime() - start) / 1000000000.0f + " secs");
|
||||
System.out.print("float[] v = { ");
|
||||
for (int i = 0; i < polygon.size; i++) {
|
||||
System.out.print(polygon.get(i));
|
||||
if (i != polygon.size - 1) System.out.print(", ");
|
||||
}
|
||||
System.out.println("};");
|
||||
triangulate();
|
||||
}
|
||||
|
||||
if (Gdx.input.isKeyJustPressed(Keys.T)) {
|
||||
triangulate();
|
||||
}
|
||||
}
|
||||
|
||||
private void generateRandomPolygon () {
|
||||
polygon.clear();
|
||||
convexPolygons.clear();
|
||||
|
||||
int numVertices = MathUtils.random(3, 30);
|
||||
for (int i = 0; i < numVertices; i++) {
|
||||
float x = (float)(50 + Math.random() * (Gdx.graphics.getWidth() - 50));
|
||||
float y = (float)(50 + Math.random() * (Gdx.graphics.getHeight() - 50));
|
||||
|
||||
polygon.add(x);
|
||||
polygon.add(y);
|
||||
System.out.println(polygon.toString(","));
|
||||
if (selfIntersects(polygon)) {
|
||||
polygon.size -= 2;
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean selfIntersects (FloatArray polygon) {
|
||||
Vector2 tmp = new Vector2();
|
||||
if (polygon.size == 6) return false;
|
||||
for (int i = 0, n = polygon.size; i <= n; i += 2) {
|
||||
float x1 = polygon.get(i % n);
|
||||
float y1 = polygon.get((i + 1) % n);
|
||||
float x2 = polygon.get((i + 2) % n);
|
||||
float y2 = polygon.get((i + 3) % n);
|
||||
|
||||
for (int j = 0; j <= n; j += 2) {
|
||||
float x3 = polygon.get(j % n);
|
||||
float y3 = polygon.get((j + 1) % n);
|
||||
float x4 = polygon.get((j + 2) % n);
|
||||
float y4 = polygon.get((j + 3) % n);
|
||||
if (x1 == x3 && y1 == y3) continue;
|
||||
if (x1 == x4 && y1 == y4) continue;
|
||||
if (x2 == x3 && y2 == y3) continue;
|
||||
if (x2 == x4 && y2 == y4) continue;
|
||||
if (Intersector.intersectSegments(x1, y1, x2, y2, x3, y3, x4, y4, tmp)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void renderScene () {
|
||||
sceneCamera.update();
|
||||
shapes.setProjectionMatrix(sceneCamera.combined);
|
||||
polyBatcher.setProjectionMatrix(sceneCamera.combined);
|
||||
|
||||
polyBatcher.begin();
|
||||
polyBatcher.disableBlending();
|
||||
|
||||
polyBatcher.end();
|
||||
|
||||
// polygon
|
||||
shapes.setColor(Color.RED);
|
||||
shapes.begin(ShapeType.Line);
|
||||
if (isCreatingPolygon) {
|
||||
tmp.set(Gdx.input.getX(), Gdx.input.getY(), 0);
|
||||
sceneCamera.unproject(tmp);
|
||||
polygon.add(tmp.x);
|
||||
polygon.add(tmp.y);
|
||||
}
|
||||
|
||||
// polygon while drawing
|
||||
// switch (polygon.size) {
|
||||
// case 0:
|
||||
// break;
|
||||
// case 2:
|
||||
// shapes.end();
|
||||
// shapes.begin(ShapeType.Point);
|
||||
// GL11.glPointSize(4);
|
||||
// shapes.point(polygon.get(0), polygon.get(1), 0);
|
||||
// shapes.end();
|
||||
// shapes.begin(ShapeType.Line);
|
||||
// break;
|
||||
// case 4:
|
||||
// shapes.line(polygon.get(0), polygon.get(1), polygon.get(2), polygon.get(3));
|
||||
// break;
|
||||
// default:
|
||||
// shapes.polygon(polygon.items, 0, polygon.size);
|
||||
// }
|
||||
|
||||
// edge normals
|
||||
// shapes.setColor(Color.YELLOW);
|
||||
// if (polygon.size > 2) {
|
||||
// boolean clockwise = Clipper.isClockwise(polygon);
|
||||
// for (int i = 0; i < polygon.size; i += 2) {
|
||||
// float x = polygon.get(i);
|
||||
// float y = polygon.get(i + 1);
|
||||
// float x2 = polygon.get((i + 2) % polygon.size);
|
||||
// float y2 = polygon.get((i + 3) % polygon.size);
|
||||
//
|
||||
// float mx = x + (x2 - x) / 2;
|
||||
// float my = y + (y2 - y) / 2;
|
||||
// float nx = (y2 - y);
|
||||
// float ny = -(x2 - x);
|
||||
// if (!clockwise) {
|
||||
// nx = -nx;
|
||||
// ny = -ny;
|
||||
// }
|
||||
// float l = 1 / (float)Math.sqrt(nx * nx + ny * ny);
|
||||
// nx *= l * 20;
|
||||
// ny *= l * 20;
|
||||
//
|
||||
// shapes.line(mx, my, mx + nx, my + ny);
|
||||
// }
|
||||
// }
|
||||
|
||||
// decomposition
|
||||
if (convexPolygons != null) {
|
||||
for (int i = 0, n = convexPolygons.size; i < n; i++) {
|
||||
if (colors.size <= i) {
|
||||
colors.add(new Color(MathUtils.random(), MathUtils.random(), MathUtils.random(), 1));
|
||||
}
|
||||
shapes.setColor(colors.get(i));
|
||||
shapes.polygon(convexPolygons.get(i).items, 0, convexPolygons.get(i).size);
|
||||
if (i == 29) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isCreatingPolygon) {
|
||||
polygon.setSize(polygon.size - 2);
|
||||
}
|
||||
shapes.end();
|
||||
|
||||
polyBatcher.begin();
|
||||
polyBatcher.enableBlending();
|
||||
for (int i = 0; i < polygon.size; i += 2) {
|
||||
float x = polygon.get(i);
|
||||
float y = polygon.get(i + 1);
|
||||
font.draw(polyBatcher, "" + (i >> 1), x, y); // + ", " + x + ", " + y, x, y);
|
||||
}
|
||||
font.draw(polyBatcher, Gdx.input.getX() + ", " + (Gdx.graphics.getHeight() - Gdx.input.getY()), 0, 20);
|
||||
polyBatcher.end();
|
||||
}
|
||||
|
||||
private void triangulate () {
|
||||
Clipper.makeClockwise(polygon);
|
||||
convexPolygons = decomposer.decompose(polygon);
|
||||
}
|
||||
|
||||
public static void main (String[] args) {
|
||||
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
|
||||
config.width = 800;
|
||||
config.height = 600;
|
||||
new LwjglApplication(new ConvexDecomposerTest(), config);
|
||||
}
|
||||
}
|
||||
@ -1,265 +0,0 @@
|
||||
|
||||
package com.esotericsoftware.spine;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import com.badlogic.gdx.ApplicationAdapter;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.Input.Buttons;
|
||||
import com.badlogic.gdx.Input.Keys;
|
||||
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
|
||||
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.OrthographicCamera;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch;
|
||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.FloatArray;
|
||||
import com.badlogic.gdx.utils.ShortArray;
|
||||
import com.esotericsoftware.spine.utils.Clipper;
|
||||
import com.esotericsoftware.spine.utils.ConvexDecomposer;
|
||||
|
||||
public class SoftwareClippingTest extends ApplicationAdapter {
|
||||
OrthographicCamera sceneCamera;
|
||||
ShapeRenderer shapes;
|
||||
PolygonSpriteBatch polyBatcher;
|
||||
Texture image;
|
||||
|
||||
float[] triangleOutline = {100, 100, 300, 100, 200, 300};
|
||||
float[] triangle = {100, 100, Color.WHITE.toFloatBits(), 0, 1, 300, 100, Color.WHITE.toFloatBits(), 1, 1, 200, 300,
|
||||
Color.WHITE.toFloatBits(), 0.5f, 0};
|
||||
short[] triangleIndices = {0, 1, 2};
|
||||
FloatArray clippingPolygon = new FloatArray();
|
||||
FloatArray clippedPolygon = new FloatArray();
|
||||
|
||||
FloatArray clippedPolygonVertices = new FloatArray();
|
||||
ShortArray clippedPolygonIndices = new ShortArray();
|
||||
|
||||
boolean isCreatingClippingArea = false;
|
||||
Vector3 tmp = new Vector3();
|
||||
Clipper clipper;
|
||||
ConvexDecomposer decomposer;
|
||||
|
||||
@Override
|
||||
public void create () {
|
||||
sceneCamera = new OrthographicCamera();
|
||||
shapes = new ShapeRenderer();
|
||||
polyBatcher = new PolygonSpriteBatch();
|
||||
clipper = new Clipper();
|
||||
decomposer = new ConvexDecomposer();
|
||||
image = new Texture("skin/skin.png");
|
||||
|
||||
float[] v = new float[] {430.90802f, 278.212f, 72.164f, 361.816f, 31.143997f, 128.804f, 191.896f, 61.0f, 291.312f,
|
||||
175.73201f, 143.956f, 207.408f, 161.4f, 145.628f, 227.456f, 160.61601f, 224.392f, 126.535995f, 188.264f, 113.144f,
|
||||
147.13199f, 108.87601f, 77.035995f, 158.212f, 86.15199f, 220.676f, 102.77199f, 240.716f, 174.74399f, 243.20801f,
|
||||
250.572f, 216.74802f, 324.772f, 200.33202f, 309.388f, 124.968f, 258.168f, 60.503998f, 199.696f, 42.872f, 116.951996f,
|
||||
6.7400017f, 11.332001f, 72.48f, -6.708008f, 143.136f, 1.0679932f, 239.92801f, 26.5f, 355.6f, -47.380005f, 377.52798f,
|
||||
-40.608f, 303.1f, -53.584015f, 77.316f, 5.4600067f, 8.728001f, 113.343994f, -56.04f, 192.42801f, -45.112f, 274.564f,
|
||||
-38.784f, 322.592f, -10.604f, 371.98f, 21.920002f, 405.16f, 60.896004f, 428.68f, 104.852005f, 406.996f, 188.976f,
|
||||
364.58398f, 220.14401f, 309.3f, 238.788f, 263.232f, 244.75201f, 219.468f, 271.58002f, 210.824f, 294.176f, 250.664f,
|
||||
295.2f, 295.972f, 276.02f, 357.46f, 269.172f, 420.008f, 242.37201f, 466.63602f, 207.648f, 437.516f, -10.579998f,
|
||||
378.05603f, -64.624f, 465.24f, -104.992f, 554.11206f, 95.43199f, 514.89197f, 259.02f};
|
||||
for (int i = 0, n = v.length; i < n; i++)
|
||||
v[i] = v[i] * 0.5f + 70;
|
||||
clippingPolygon.addAll(v);
|
||||
clip();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize (int width, int height) {
|
||||
sceneCamera.setToOrtho(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render () {
|
||||
Gdx.gl.glClearColor(0.3f, 0.3f, 0.3f, 1);
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
|
||||
|
||||
processInput();
|
||||
renderScene();
|
||||
}
|
||||
|
||||
private void processInput () {
|
||||
tmp.set(Gdx.input.getX(), Gdx.input.getY(), 0);
|
||||
sceneCamera.unproject(tmp);
|
||||
|
||||
if (Gdx.input.justTouched()) {
|
||||
if (!isCreatingClippingArea) {
|
||||
clippingPolygon.clear();
|
||||
isCreatingClippingArea = true;
|
||||
}
|
||||
|
||||
clippingPolygon.add((int)tmp.x);
|
||||
clippingPolygon.add((int)tmp.y);
|
||||
|
||||
if (Gdx.input.isButtonPressed(Buttons.RIGHT)) {
|
||||
isCreatingClippingArea = false;
|
||||
clip();
|
||||
}
|
||||
}
|
||||
|
||||
if (Gdx.input.isKeyJustPressed(Keys.T)) {
|
||||
clip();
|
||||
}
|
||||
}
|
||||
|
||||
private void renderScene () {
|
||||
sceneCamera.update();
|
||||
shapes.setProjectionMatrix(sceneCamera.combined);
|
||||
polyBatcher.setProjectionMatrix(sceneCamera.combined);
|
||||
|
||||
polyBatcher.begin();
|
||||
polyBatcher.disableBlending();
|
||||
|
||||
// clipped polygon
|
||||
if (clippedPolygonVertices.size == 0) {
|
||||
polyBatcher.draw(image, triangle, 0, 15, triangleIndices, 0, 3);
|
||||
} else {
|
||||
polyBatcher.draw(image, clippedPolygonVertices.items, 0, clippedPolygonVertices.size, clippedPolygonIndices.items, 0,
|
||||
clippedPolygonIndices.size);
|
||||
}
|
||||
polyBatcher.end();
|
||||
|
||||
shapes.begin(ShapeType.Line);
|
||||
|
||||
// triangle
|
||||
shapes.setColor(Color.GREEN);
|
||||
shapes.polygon(triangleOutline);
|
||||
|
||||
// clipping area
|
||||
shapes.setColor(Color.RED);
|
||||
if (isCreatingClippingArea) {
|
||||
tmp.set(Gdx.input.getX(), Gdx.input.getY(), 0);
|
||||
sceneCamera.unproject(tmp);
|
||||
clippingPolygon.add(tmp.x);
|
||||
clippingPolygon.add(tmp.y);
|
||||
}
|
||||
|
||||
switch (clippingPolygon.size) {
|
||||
case 0:
|
||||
break;
|
||||
case 2:
|
||||
shapes.end();
|
||||
shapes.begin(ShapeType.Point);
|
||||
GL11.glPointSize(4);
|
||||
shapes.point(clippingPolygon.get(0), clippingPolygon.get(1), 0);
|
||||
shapes.end();
|
||||
shapes.begin(ShapeType.Line);
|
||||
break;
|
||||
case 4:
|
||||
shapes.line(clippingPolygon.get(0), clippingPolygon.get(1), clippingPolygon.get(2), clippingPolygon.get(3));
|
||||
break;
|
||||
default:
|
||||
shapes.polygon(clippingPolygon.items, 0, clippingPolygon.size);
|
||||
}
|
||||
|
||||
// // edge normals
|
||||
// shapes.setColor(Color.YELLOW);
|
||||
// if (clippingPolygon.size > 2) {
|
||||
// boolean clockwise = Clipper.isClockwise(clippingPolygon);
|
||||
// for (int i = 0; i < clippingPolygon.size; i += 2) {
|
||||
// float x = clippingPolygon.get(i);
|
||||
// float y = clippingPolygon.get(i + 1);
|
||||
// float x2 = clippingPolygon.get((i + 2) % clippingPolygon.size);
|
||||
// float y2 = clippingPolygon.get((i + 3) % clippingPolygon.size);
|
||||
//
|
||||
// float mx = x + (x2 - x) / 2;
|
||||
// float my = y + (y2 - y) / 2;
|
||||
// float nx = (y2 - y);
|
||||
// float ny = -(x2 - x);
|
||||
// if (!clockwise) {
|
||||
// nx = -nx;
|
||||
// ny = -ny;
|
||||
// }
|
||||
// float l = 1 / (float)Math.sqrt(nx * nx + ny * ny);
|
||||
// nx *= l * 20;
|
||||
// ny *= l * 20;
|
||||
//
|
||||
// shapes.line(mx, my, mx + nx, my + ny);
|
||||
// }
|
||||
// }
|
||||
|
||||
if (isCreatingClippingArea) {
|
||||
clippingPolygon.setSize(clippingPolygon.size - 2);
|
||||
}
|
||||
|
||||
// // clipped polygon
|
||||
// shapes.setColor(Color.PINK);
|
||||
// if (clippedPolygon.size > 0) {
|
||||
// shapes.polygon(clippedPolygon.items, 0, clippedPolygon.size);
|
||||
// }
|
||||
|
||||
shapes.end();
|
||||
}
|
||||
|
||||
private void clip () {
|
||||
float x1 = triangle[0];
|
||||
float y1 = triangle[1];
|
||||
float x2 = triangle[5];
|
||||
float y2 = triangle[6];
|
||||
float x3 = triangle[10];
|
||||
float y3 = triangle[11];
|
||||
|
||||
Clipper.makeClockwise(clippingPolygon);
|
||||
Array<FloatArray> clippingPolygons = decomposer.decompose(clippingPolygon);
|
||||
clippedPolygonVertices.clear();
|
||||
clippedPolygonIndices.clear();
|
||||
|
||||
for (FloatArray poly : clippingPolygons) {
|
||||
Clipper.makeClockwise(poly);
|
||||
poly.add(poly.get(0));
|
||||
poly.add(poly.get(1));
|
||||
|
||||
boolean clipped = clipper.clip(x1, y1, x2, y2, x3, y3, poly, clippedPolygon);
|
||||
System.out.println("Clipped: " + clipped);
|
||||
if (clipped) {
|
||||
float d0 = y2 - y3;
|
||||
float d1 = x3 - x2;
|
||||
float d2 = x1 - x3;
|
||||
float d3 = y1 - y3;
|
||||
float d4 = y3 - y1;
|
||||
|
||||
float denom = 1 / (d0 * d2 + d1 * d3);
|
||||
|
||||
// triangulate by creating a triangle fan, duplicate vertices
|
||||
int o = clippedPolygonVertices.size / 5;
|
||||
float color = Color.WHITE.toFloatBits();
|
||||
for (int i = 0; i < clippedPolygon.size; i += 2) {
|
||||
float x = clippedPolygon.get(i);
|
||||
float y = clippedPolygon.get(i + 1);
|
||||
|
||||
float a = (d0 * (x - x3) + d1 * (y - y3)) * denom;
|
||||
float b = (d4 * (x - x3) + d2 * (y - y3)) * denom;
|
||||
float c = 1.0f - a - b;
|
||||
|
||||
float u = triangle[3] * a + triangle[8] * b + triangle[13] * c;
|
||||
float v = triangle[4] * a + triangle[9] * b + triangle[14] * c;
|
||||
clippedPolygonVertices.add(x);
|
||||
clippedPolygonVertices.add(y);
|
||||
clippedPolygonVertices.add(color);
|
||||
clippedPolygonVertices.add(u);
|
||||
clippedPolygonVertices.add(v);
|
||||
}
|
||||
|
||||
for (int i = 1; i < (clippedPolygon.size >> 1) - 1; i++) {
|
||||
clippedPolygonIndices.add(o);
|
||||
clippedPolygonIndices.add(o + i);
|
||||
clippedPolygonIndices.add(o + i + 1);
|
||||
}
|
||||
} else {
|
||||
clippedPolygon.clear();
|
||||
}
|
||||
|
||||
poly.setSize(poly.size - 2);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main (String[] args) {
|
||||
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
|
||||
new LwjglApplication(new SoftwareClippingTest(), config);
|
||||
}
|
||||
}
|
||||
@ -1,146 +0,0 @@
|
||||
/******************************************************************************
|
||||
* 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.utils;
|
||||
|
||||
import com.badlogic.gdx.utils.FloatArray;
|
||||
|
||||
public class Clipper {
|
||||
private final FloatArray scratch = new FloatArray();
|
||||
|
||||
/** Clips the input triangle against the convex clipping area, which needs to be clockwise. If the triangle lies entirely
|
||||
* within the clipping area, false is returned. The clipping area must duplicate the first vertex at the end of the vertices
|
||||
* list. */
|
||||
public boolean clip (float x1, float y1, float x2, float y2, float x3, float y3, FloatArray clippingArea, FloatArray output) {
|
||||
FloatArray originalOutput = output;
|
||||
boolean clipped = false;
|
||||
|
||||
// Avoid copy at the end.
|
||||
FloatArray input = null;
|
||||
if (clippingArea.size % 4 >= 2) {
|
||||
input = output;
|
||||
output = scratch;
|
||||
} else
|
||||
input = scratch;
|
||||
|
||||
input.clear();
|
||||
input.add(x1);
|
||||
input.add(y1);
|
||||
input.add(x2);
|
||||
input.add(y2);
|
||||
input.add(x3);
|
||||
input.add(y3);
|
||||
input.add(x1);
|
||||
input.add(y1);
|
||||
output.clear();
|
||||
|
||||
float[] clippingVertices = clippingArea.items;
|
||||
int clippingVerticesLast = clippingArea.size - 4;
|
||||
for (int i = 0;; i += 2) {
|
||||
float edgeX = clippingVertices[i], edgeY = clippingVertices[i + 1];
|
||||
float edgeX2 = clippingVertices[i + 2], edgeY2 = clippingVertices[i + 3];
|
||||
float deltaX = edgeX - edgeX2, deltaY = edgeY - edgeY2;
|
||||
|
||||
float[] inputVertices = input.items;
|
||||
int inputVerticesLength = input.size - 2, outputStart = output.size;
|
||||
for (int ii = 0; ii < inputVerticesLength; ii += 2) {
|
||||
float inputX = inputVertices[ii], inputY = inputVertices[ii + 1];
|
||||
float inputX2 = inputVertices[ii + 2], inputY2 = inputVertices[ii + 3];
|
||||
boolean side2 = deltaX * (inputY2 - edgeY2) - deltaY * (inputX2 - edgeX2) > 0;
|
||||
if (deltaX * (inputY - edgeY2) - deltaY * (inputX - edgeX2) > 0) {
|
||||
if (side2) { // v1 inside, v2 inside
|
||||
output.add(inputX2);
|
||||
output.add(inputY2);
|
||||
continue;
|
||||
}
|
||||
// v1 inside, v2 outside
|
||||
float c0 = inputY2 - inputY, c2 = inputX2 - inputX;
|
||||
float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY));
|
||||
output.add(edgeX + (edgeX2 - edgeX) * ua);
|
||||
output.add(edgeY + (edgeY2 - edgeY) * ua);
|
||||
} else if (side2) { // v1 outside, v2 inside
|
||||
float c0 = inputY2 - inputY, c2 = inputX2 - inputX;
|
||||
float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY));
|
||||
output.add(edgeX + (edgeX2 - edgeX) * ua);
|
||||
output.add(edgeY + (edgeY2 - edgeY) * ua);
|
||||
output.add(inputX2);
|
||||
output.add(inputY2);
|
||||
}
|
||||
clipped = true;
|
||||
}
|
||||
|
||||
if (outputStart == output.size) { // All edges outside.
|
||||
originalOutput.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
output.add(output.items[0]);
|
||||
output.add(output.items[1]);
|
||||
|
||||
if (i == clippingVerticesLast) break;
|
||||
FloatArray temp = output;
|
||||
output = input;
|
||||
output.clear();
|
||||
input = temp;
|
||||
}
|
||||
|
||||
if (originalOutput != output) {
|
||||
originalOutput.clear();
|
||||
originalOutput.addAll(output.items, 0, output.size - 2);
|
||||
} else
|
||||
originalOutput.setSize(originalOutput.size - 2);
|
||||
|
||||
return clipped;
|
||||
}
|
||||
|
||||
static public void makeClockwise (FloatArray polygon) {
|
||||
float[] vertices = polygon.items;
|
||||
int verticeslength = polygon.size;
|
||||
|
||||
float area = vertices[verticeslength - 2] * vertices[1] - vertices[0] * vertices[verticeslength - 1], p1x, p1y, p2x, p2y;
|
||||
for (int i = 0, n = verticeslength - 3; i < n; i += 2) {
|
||||
p1x = vertices[i];
|
||||
p1y = vertices[i + 1];
|
||||
p2x = vertices[i + 2];
|
||||
p2y = vertices[i + 3];
|
||||
area += p1x * p2y - p2x * p1y;
|
||||
}
|
||||
if (area < 0) return;
|
||||
|
||||
for (int i = 0, lastX = verticeslength - 2, n = verticeslength >> 1; i < n; i += 2) {
|
||||
float x = vertices[i], y = vertices[i + 1];
|
||||
int other = lastX - i;
|
||||
vertices[i] = vertices[other];
|
||||
vertices[i + 1] = vertices[other + 1];
|
||||
vertices[other] = x;
|
||||
vertices[other + 1] = y;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -36,7 +36,7 @@ import com.badlogic.gdx.utils.FloatArray;
|
||||
import com.badlogic.gdx.utils.Pool;
|
||||
import com.badlogic.gdx.utils.ShortArray;
|
||||
|
||||
public class ConvexDecomposer {
|
||||
class ConvexDecomposer {
|
||||
private final Array<FloatArray> convexPolygons = new Array();
|
||||
private final Array<ShortArray> convexPolygonsIndices = new Array();
|
||||
|
||||
@ -75,40 +75,60 @@ public class ConvexDecomposer {
|
||||
triangles.clear();
|
||||
triangles.ensureCapacity(Math.max(0, vertexCount - 2) << 2);
|
||||
|
||||
// Triangulate.
|
||||
while (vertexCount > 3) {
|
||||
// Find ear tip.
|
||||
int i = 0;
|
||||
int previous = vertexCount - 1, i = 0, next = 1;
|
||||
while (true) {
|
||||
if (!isConcave[i] && isEarTip(i, vertexCount, vertices, indices)) break;
|
||||
i++;
|
||||
if (i == vertexCount) {
|
||||
outer:
|
||||
if (!isConcave[i]) {
|
||||
int p1 = indices[previous] << 1, p2 = indices[i] << 1, p3 = indices[next] << 1;
|
||||
float p1x = vertices[p1], p1y = vertices[p1 + 1];
|
||||
float p2x = vertices[p2], p2y = vertices[p2 + 1];
|
||||
float p3x = vertices[p3], p3y = vertices[p3 + 1];
|
||||
for (int ii = (next + 1) % vertexCount; ii != previous; ii = (ii + 1) % vertexCount) {
|
||||
if (!isConcave[ii]) continue;
|
||||
int v = indices[ii] << 1;
|
||||
float vx = vertices[v], vy = vertices[v + 1];
|
||||
if (positiveArea(p3x, p3y, p1x, p1y, vx, vy)) {
|
||||
if (positiveArea(p1x, p1y, p2x, p2y, vx, vy)) {
|
||||
if (positiveArea(p2x, p2y, p3x, p3y, vx, vy)) break outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (next == 0) {
|
||||
do {
|
||||
i--;
|
||||
if (!isConcave[i]) break;
|
||||
i--;
|
||||
} while (i > 0);
|
||||
break;
|
||||
}
|
||||
|
||||
previous = i;
|
||||
i = next;
|
||||
next = (next + 1) % vertexCount;
|
||||
}
|
||||
|
||||
// Cut ear tip.
|
||||
triangles.add(indices[previousIndex(i, vertexCount)]);
|
||||
triangles.add(indices[(vertexCount + i - 1) % vertexCount]);
|
||||
triangles.add(indices[i]);
|
||||
triangles.add(indices[nextIndex(i, vertexCount)]);
|
||||
triangles.add(indices[(i + 1) % vertexCount]);
|
||||
indicesArray.removeIndex(i);
|
||||
isConcaveArray.removeIndex(i);
|
||||
vertexCount--;
|
||||
|
||||
int previousIndex = previousIndex(i, vertexCount);
|
||||
int previousIndex = (vertexCount + i - 1) % vertexCount;
|
||||
int nextIndex = i == vertexCount ? 0 : i;
|
||||
isConcave[previousIndex] = isConcave(previousIndex, vertexCount, vertices, indices);
|
||||
isConcave[nextIndex] = isConcave(nextIndex, vertexCount, vertices, indices);
|
||||
}
|
||||
|
||||
if (vertexCount == 3) {
|
||||
triangles.add(indicesArray.get(2));
|
||||
triangles.add(indicesArray.get(0));
|
||||
triangles.add(indicesArray.get(1));
|
||||
triangles.add(indices[2]);
|
||||
triangles.add(indices[0]);
|
||||
triangles.add(indices[1]);
|
||||
}
|
||||
|
||||
Array<FloatArray> convexPolygons = this.convexPolygons;
|
||||
@ -127,18 +147,20 @@ public class ConvexDecomposer {
|
||||
|
||||
// Merge subsequent triangles if they form a triangle fan.
|
||||
int fanBaseIndex = -1, lastWinding = 0;
|
||||
short[] trianglesItems = triangles.items;
|
||||
for (int i = 0, n = triangles.size; i < n; i += 3) {
|
||||
int t1 = triangles.get(i) << 1, t2 = triangles.get(i + 1) << 1, t3 = triangles.get(i + 2) << 1;
|
||||
float x1 = input.get(t1), y1 = input.get(t1 + 1);
|
||||
float x2 = input.get(t2), y2 = input.get(t2 + 1);
|
||||
float x3 = input.get(t3), y3 = input.get(t3 + 1);
|
||||
int t1 = trianglesItems[i] << 1, t2 = trianglesItems[i + 1] << 1, t3 = trianglesItems[i + 2] << 1;
|
||||
float x1 = vertices[t1], y1 = vertices[t1 + 1];
|
||||
float x2 = vertices[t2], y2 = vertices[t2 + 1];
|
||||
float x3 = vertices[t3], y3 = vertices[t3 + 1];
|
||||
|
||||
// If the base of the last triangle is the same as this triangle, check if they form a convex polygon (triangle fan).
|
||||
boolean merged = false;
|
||||
if (fanBaseIndex == t1) {
|
||||
int o = polygon.size - 4;
|
||||
int winding1 = winding(polygon.get(o), polygon.get(o + 1), polygon.get(o + 2), polygon.get(o + 3), x3, y3);
|
||||
int winding2 = winding(x3, y3, polygon.get(0), polygon.get(1), polygon.get(2), polygon.get(3));
|
||||
float[] p = polygon.items;
|
||||
int winding1 = winding(p[o], p[o + 1], p[o + 2], p[o + 3], x3, y3);
|
||||
int winding2 = winding(x3, y3, p[0], p[1], p[2], p[3]);
|
||||
if (winding1 == lastWinding && winding2 == lastWinding) {
|
||||
polygon.add(x3);
|
||||
polygon.add(y3);
|
||||
@ -185,10 +207,11 @@ public class ConvexDecomposer {
|
||||
|
||||
polygon = convexPolygons.get(i);
|
||||
int o = polygon.size - 4;
|
||||
float prevPrevX = polygon.get(o), prevPrevY = polygon.get(o + 1);
|
||||
float prevX = polygon.get(o + 2), prevY = polygon.get(o + 3);
|
||||
float firstX = polygon.get(0), firstY = polygon.get(1);
|
||||
float secondX = polygon.get(2), secondY = polygon.get(3);
|
||||
float[] p = polygon.items;
|
||||
float prevPrevX = p[o], prevPrevY = p[o + 1];
|
||||
float prevX = p[o + 2], prevY = p[o + 3];
|
||||
float firstX = p[0], firstY = p[1];
|
||||
float secondX = p[2], secondY = p[3];
|
||||
int winding = winding(prevPrevX, prevPrevY, prevX, prevY, firstX, firstY);
|
||||
|
||||
for (int ii = 0; ii < n; ii++) {
|
||||
@ -200,8 +223,7 @@ public class ConvexDecomposer {
|
||||
int otherLastIndex = otherIndices.get(2);
|
||||
|
||||
FloatArray otherPoly = convexPolygons.get(ii);
|
||||
float x3 = otherPoly.get(otherPoly.size - 2);
|
||||
float y3 = otherPoly.get(otherPoly.size - 1);
|
||||
float x3 = otherPoly.get(otherPoly.size - 2), y3 = otherPoly.get(otherPoly.size - 1);
|
||||
|
||||
if (otherFirstIndex != firstIndex || otherSecondIndex != lastIndex) continue;
|
||||
int winding1 = winding(prevPrevX, prevPrevY, prevX, prevY, x3, y3);
|
||||
@ -233,47 +255,14 @@ public class ConvexDecomposer {
|
||||
return convexPolygons;
|
||||
}
|
||||
|
||||
private boolean isEarTip (int earTipIndex, int vertexCount, float[] vertices, short[] indices) {
|
||||
int previousIndex = previousIndex(earTipIndex, vertexCount);
|
||||
int nextIndex = nextIndex(earTipIndex, vertexCount);
|
||||
int p1 = indices[previousIndex] << 1;
|
||||
int p2 = indices[earTipIndex] << 1;
|
||||
int p3 = indices[nextIndex] << 1;
|
||||
float p1x = vertices[p1], p1y = vertices[p1 + 1];
|
||||
float p2x = vertices[p2], p2y = vertices[p2 + 1];
|
||||
float p3x = vertices[p3], p3y = vertices[p3 + 1];
|
||||
boolean[] isConcave = this.isConcaveArray.items;
|
||||
|
||||
for (int i = nextIndex(nextIndex, vertexCount); i != previousIndex; i = nextIndex(i, vertexCount)) {
|
||||
if (isConcave[i]) {
|
||||
int v = indices[i] << 1;
|
||||
float vx = vertices[v], vy = vertices[v + 1];
|
||||
if (positiveArea(p3x, p3y, p1x, p1y, vx, vy)) {
|
||||
if (positiveArea(p1x, p1y, p2x, p2y, vx, vy)) {
|
||||
if (positiveArea(p2x, p2y, p3x, p3y, vx, vy)) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static private boolean isConcave (int index, int vertexCount, float[] vertices, short[] indices) {
|
||||
int previous = indices[previousIndex(index, vertexCount)] << 1;
|
||||
int previous = indices[(vertexCount + index - 1) % vertexCount] << 1;
|
||||
int current = indices[index] << 1;
|
||||
int next = indices[nextIndex(index, vertexCount)] << 1;
|
||||
int next = indices[(index + 1) % vertexCount] << 1;
|
||||
return !positiveArea(vertices[previous], vertices[previous + 1], vertices[current], vertices[current + 1], vertices[next],
|
||||
vertices[next + 1]);
|
||||
}
|
||||
|
||||
static private int previousIndex (int index, int vertexCount) {
|
||||
return (index == 0 ? vertexCount : index) - 1;
|
||||
}
|
||||
|
||||
static private int nextIndex (int index, int vertexCount) {
|
||||
return (index + 1) % vertexCount;
|
||||
}
|
||||
|
||||
static private boolean positiveArea (float p1x, float p1y, float p2x, float p2y, float p3x, float p3y) {
|
||||
return p1x * (p3y - p2y) + p2x * (p1y - p3y) + p3x * (p2y - p1y) >= 0;
|
||||
}
|
||||
|
||||
@ -37,12 +37,12 @@ import com.esotericsoftware.spine.Slot;
|
||||
import com.esotericsoftware.spine.attachments.ClippingAttachment;
|
||||
|
||||
public class SkeletonClipping {
|
||||
private final Clipper clipper = new Clipper();
|
||||
private final ConvexDecomposer decomposer = new ConvexDecomposer();
|
||||
private final FloatArray clippingPolygon = new FloatArray();
|
||||
private final FloatArray clipOutput = new FloatArray(400);
|
||||
private final FloatArray clippedVertices = new FloatArray(400);
|
||||
private final ShortArray clippedTriangles = new ShortArray(400);
|
||||
private final FloatArray clipOutput = new FloatArray(128);
|
||||
private final FloatArray clippedVertices = new FloatArray(128);
|
||||
private final ShortArray clippedTriangles = new ShortArray(128);
|
||||
private final FloatArray scratch = new FloatArray();
|
||||
|
||||
private ClippingAttachment clipAttachment;
|
||||
private Array<FloatArray> clippingPolygons;
|
||||
@ -54,10 +54,10 @@ public class SkeletonClipping {
|
||||
int n = clip.getWorldVerticesLength();
|
||||
float[] vertices = clippingPolygon.setSize(n);
|
||||
clip.computeWorldVertices(slot, 0, n, vertices, 0, 2);
|
||||
Clipper.makeClockwise(clippingPolygon);
|
||||
makeClockwise(clippingPolygon);
|
||||
clippingPolygons = decomposer.decompose(clippingPolygon);
|
||||
for (FloatArray polygon : clippingPolygons) {
|
||||
Clipper.makeClockwise(polygon);
|
||||
makeClockwise(polygon);
|
||||
polygon.add(polygon.items[0]);
|
||||
polygon.add(polygon.items[1]);
|
||||
}
|
||||
@ -79,7 +79,6 @@ public class SkeletonClipping {
|
||||
public void clipTriangles (float[] vertices, int verticesLength, short[] triangles, int trianglesLength, float[] uvs,
|
||||
float light, float dark, boolean twoColor) {
|
||||
|
||||
Clipper clipper = this.clipper;
|
||||
FloatArray clipOutput = this.clipOutput, clippedVertices = this.clippedVertices;
|
||||
ShortArray clippedTriangles = this.clippedTriangles;
|
||||
Object[] polygons = clippingPolygons.items;
|
||||
@ -105,7 +104,7 @@ public class SkeletonClipping {
|
||||
|
||||
for (int p = 0; p < polygonsCount; p++) {
|
||||
int s = clippedVertices.size;
|
||||
if (clipper.clip(x1, y1, x2, y2, x3, y3, (FloatArray)polygons[p], clipOutput)) {
|
||||
if (clip(x1, y1, x2, y2, x3, y3, (FloatArray)polygons[p], clipOutput)) {
|
||||
int clipOutputLength = clipOutput.size;
|
||||
if (clipOutputLength == 0) continue;
|
||||
float d0 = y2 - y3, d1 = x3 - x2, d2 = x1 - x3, d4 = y3 - y1;
|
||||
@ -196,6 +195,90 @@ public class SkeletonClipping {
|
||||
}
|
||||
}
|
||||
|
||||
/** Clips the input triangle against the convex, clockwise clipping area. If the triangle lies entirely within the clipping
|
||||
* area, false is returned. The clipping area must duplicate the first vertex at the end of the vertices list. */
|
||||
boolean clip (float x1, float y1, float x2, float y2, float x3, float y3, FloatArray clippingArea, FloatArray output) {
|
||||
FloatArray originalOutput = output;
|
||||
boolean clipped = false;
|
||||
|
||||
// Avoid copy at the end.
|
||||
FloatArray input = null;
|
||||
if (clippingArea.size % 4 >= 2) {
|
||||
input = output;
|
||||
output = scratch;
|
||||
} else
|
||||
input = scratch;
|
||||
|
||||
input.clear();
|
||||
input.add(x1);
|
||||
input.add(y1);
|
||||
input.add(x2);
|
||||
input.add(y2);
|
||||
input.add(x3);
|
||||
input.add(y3);
|
||||
input.add(x1);
|
||||
input.add(y1);
|
||||
output.clear();
|
||||
|
||||
float[] clippingVertices = clippingArea.items;
|
||||
int clippingVerticesLast = clippingArea.size - 4;
|
||||
for (int i = 0;; i += 2) {
|
||||
float edgeX = clippingVertices[i], edgeY = clippingVertices[i + 1];
|
||||
float edgeX2 = clippingVertices[i + 2], edgeY2 = clippingVertices[i + 3];
|
||||
float deltaX = edgeX - edgeX2, deltaY = edgeY - edgeY2;
|
||||
|
||||
float[] inputVertices = input.items;
|
||||
int inputVerticesLength = input.size - 2, outputStart = output.size;
|
||||
for (int ii = 0; ii < inputVerticesLength; ii += 2) {
|
||||
float inputX = inputVertices[ii], inputY = inputVertices[ii + 1];
|
||||
float inputX2 = inputVertices[ii + 2], inputY2 = inputVertices[ii + 3];
|
||||
boolean side2 = deltaX * (inputY2 - edgeY2) - deltaY * (inputX2 - edgeX2) > 0;
|
||||
if (deltaX * (inputY - edgeY2) - deltaY * (inputX - edgeX2) > 0) {
|
||||
if (side2) { // v1 inside, v2 inside
|
||||
output.add(inputX2);
|
||||
output.add(inputY2);
|
||||
continue;
|
||||
}
|
||||
// v1 inside, v2 outside
|
||||
float c0 = inputY2 - inputY, c2 = inputX2 - inputX;
|
||||
float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY));
|
||||
output.add(edgeX + (edgeX2 - edgeX) * ua);
|
||||
output.add(edgeY + (edgeY2 - edgeY) * ua);
|
||||
} else if (side2) { // v1 outside, v2 inside
|
||||
float c0 = inputY2 - inputY, c2 = inputX2 - inputX;
|
||||
float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY));
|
||||
output.add(edgeX + (edgeX2 - edgeX) * ua);
|
||||
output.add(edgeY + (edgeY2 - edgeY) * ua);
|
||||
output.add(inputX2);
|
||||
output.add(inputY2);
|
||||
}
|
||||
clipped = true;
|
||||
}
|
||||
|
||||
if (outputStart == output.size) { // All edges outside.
|
||||
originalOutput.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
output.add(output.items[0]);
|
||||
output.add(output.items[1]);
|
||||
|
||||
if (i == clippingVerticesLast) break;
|
||||
FloatArray temp = output;
|
||||
output = input;
|
||||
output.clear();
|
||||
input = temp;
|
||||
}
|
||||
|
||||
if (originalOutput != output) {
|
||||
originalOutput.clear();
|
||||
originalOutput.addAll(output.items, 0, output.size - 2);
|
||||
} else
|
||||
originalOutput.setSize(originalOutput.size - 2);
|
||||
|
||||
return clipped;
|
||||
}
|
||||
|
||||
public FloatArray getClippedVertices () {
|
||||
return clippedVertices;
|
||||
}
|
||||
@ -203,4 +286,28 @@ public class SkeletonClipping {
|
||||
public ShortArray getClippedTriangles () {
|
||||
return clippedTriangles;
|
||||
}
|
||||
|
||||
static void makeClockwise (FloatArray polygon) {
|
||||
float[] vertices = polygon.items;
|
||||
int verticeslength = polygon.size;
|
||||
|
||||
float area = vertices[verticeslength - 2] * vertices[1] - vertices[0] * vertices[verticeslength - 1], p1x, p1y, p2x, p2y;
|
||||
for (int i = 0, n = verticeslength - 3; i < n; i += 2) {
|
||||
p1x = vertices[i];
|
||||
p1y = vertices[i + 1];
|
||||
p2x = vertices[i + 2];
|
||||
p2y = vertices[i + 3];
|
||||
area += p1x * p2y - p2x * p1y;
|
||||
}
|
||||
if (area < 0) return;
|
||||
|
||||
for (int i = 0, lastX = verticeslength - 2, n = verticeslength >> 1; i < n; i += 2) {
|
||||
float x = vertices[i], y = vertices[i + 1];
|
||||
int other = lastX - i;
|
||||
vertices[i] = vertices[other];
|
||||
vertices[i + 1] = vertices[other + 1];
|
||||
vertices[other] = x;
|
||||
vertices[other + 1] = y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user