240 lines
6.8 KiB
Java

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.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.FloatArray;
import com.badlogic.gdx.utils.ShortArray;
import com.esotericsoftware.spine.utils.SutherlandHodgmanClipper;
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();
SutherlandHodgmanClipper clipper;
@Override
public void create () {
sceneCamera = new OrthographicCamera();
shapes = new ShapeRenderer();
polyBatcher = new PolygonSpriteBatch();
clipper = new SutherlandHodgmanClipper();
image = new Texture("skin/skin.png");
}
@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(tmp.x);
clippingPolygon.add(tmp.y);
if (Gdx.input.isButtonPressed(Buttons.RIGHT)) {
isCreatingClippingArea = false;
clip();
}
}
}
private void renderScene () {
sceneCamera.update();
shapes.setProjectionMatrix(sceneCamera.combined);
polyBatcher.setProjectionMatrix(sceneCamera.combined);
polyBatcher.begin();
polyBatcher.disableBlending();
if (clippedPolygon.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);
// clipped polygons
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 = SutherlandHodgmanClipper.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];
// must duplicate first vertex at end of polygon
// so we can avoid module/branch in clipping code
SutherlandHodgmanClipper.makeClockwise(clippingPolygon);
clippingPolygon.add(clippingPolygon.get(0));
clippingPolygon.add(clippingPolygon.get(1));
boolean clipped = clipper.clip(x1, y1, x2, y2, x3, y3, clippingPolygon, clippedPolygon);
System.out.println("Clipped: " + clipped);
if (clipped) {
clippedPolygonVertices.clear();
clippedPolygonIndices.clear();
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
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(0);
clippedPolygonIndices.add(i);
clippedPolygonIndices.add(i + 1);
}
} else {
clippedPolygon.clear();
}
clippingPolygon.setSize(clippingPolygon.size - 2);
}
public static void main (String[] args) {
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
new LwjglApplication(new SoftwareClippingTest(), config);
}
}