Bit of reorganization.

It's pretty now!
This commit is contained in:
Nathan Sweet 2017-04-07 16:54:07 +09:00
parent 8d9d46ca56
commit ff98e136a1
6 changed files with 164 additions and 931 deletions

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}

View File

@ -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;
}
}
}