[libgdx] Added software clipping to SkeletonRenderer. Requires a polygon sprite batch and convex clipping attachment.

This commit is contained in:
badlogic 2017-03-27 18:50:44 +02:00
parent c73971eb4c
commit af3b5655fc
4 changed files with 234 additions and 80 deletions

View File

@ -32,17 +32,18 @@ package com.esotericsoftware.spine;
import com.badlogic.gdx.ApplicationAdapter; import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication; import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration; import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureAtlas; import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.esotericsoftware.spine.attachments.ClippingAttachment; import com.esotericsoftware.spine.attachments.ClippingAttachment;
import com.esotericsoftware.spine.utils.TwoColorPolygonBatch;
public class ClippingTest extends ApplicationAdapter { public class ClippingTest extends ApplicationAdapter {
OrthographicCamera camera; OrthographicCamera camera;
SpriteBatch batch; TwoColorPolygonBatch batch;
SkeletonRenderer renderer; SkeletonRenderer renderer;
SkeletonRendererDebug debugRenderer; SkeletonRendererDebug debugRenderer;
@ -52,9 +53,10 @@ public class ClippingTest extends ApplicationAdapter {
public void create () { public void create () {
camera = new OrthographicCamera(); camera = new OrthographicCamera();
batch = new SpriteBatch(); batch = new TwoColorPolygonBatch(2048);
renderer = new SkeletonRenderer(); renderer = new SkeletonRenderer();
renderer.setPremultipliedAlpha(true); renderer.setPremultipliedAlpha(true);
renderer.setSoftwareClipping(true);
debugRenderer = new SkeletonRendererDebug(); debugRenderer = new SkeletonRendererDebug();
debugRenderer.setBoundingBoxes(false); debugRenderer.setBoundingBoxes(false);
debugRenderer.setRegionAttachments(false); debugRenderer.setRegionAttachments(false);
@ -81,19 +83,19 @@ public class ClippingTest extends ApplicationAdapter {
// Create a clipping attachment, slot data, and slot. // Create a clipping attachment, slot data, and slot.
ClippingAttachment clip = new ClippingAttachment("clip"); ClippingAttachment clip = new ClippingAttachment("clip");
// Rectangle: // Rectangle:
// clip.setVertices(new float[] { //
// -140, -50, //
// 120, -50, //
// 120, 50, //
// -140, 50, //
// });
// Self intersection:
clip.setVertices(new float[] { // clip.setVertices(new float[] { //
-140, -50, // -140, -50, //
120, 50, //
120, -50, // 120, -50, //
120, 50, //
-140, 50, // -140, 50, //
}); });
// Self intersection:
// clip.setVertices(new float[] { //
// -140, -50, //
// 120, 50, //
// 120, -50, //
// -140, 50, //
// });
clip.setWorldVerticesLength(8); clip.setWorldVerticesLength(8);
clip.setEnd(skeleton.findSlot("muzzle")); clip.setEnd(skeleton.findSlot("muzzle"));
@ -109,6 +111,7 @@ public class ClippingTest extends ApplicationAdapter {
public void render () { public void render () {
state.update(Gdx.graphics.getDeltaTime() * 0.3f); state.update(Gdx.graphics.getDeltaTime() * 0.3f);
Gdx.gl.glClearColor(0.3f, 0.3f, 0.3f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
state.apply(skeleton); state.apply(skeleton);
@ -123,6 +126,11 @@ public class ClippingTest extends ApplicationAdapter {
batch.end(); batch.end();
debugRenderer.draw(skeleton); debugRenderer.draw(skeleton);
if (Gdx.input.isKeyJustPressed(Keys.S)) {
renderer.setSoftwareClipping(!renderer.getSoftwareClipping());
System.out.println("Software clipping: " + renderer.getSoftwareClipping());
}
} }
public void resize (int width, int height) { public void resize (int width, int height) {

View File

@ -1,8 +1,6 @@
package com.esotericsoftware.spine; package com.esotericsoftware.spine;
import javax.management.BadBinaryOpValueExpException;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import com.badlogic.gdx.ApplicationAdapter; import com.badlogic.gdx.ApplicationAdapter;

View File

@ -42,24 +42,34 @@ import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.FloatArray; import com.badlogic.gdx.utils.FloatArray;
import com.badlogic.gdx.utils.NumberUtils; import com.badlogic.gdx.utils.NumberUtils;
import com.badlogic.gdx.utils.ShortArray;
import com.esotericsoftware.spine.attachments.Attachment; import com.esotericsoftware.spine.attachments.Attachment;
import com.esotericsoftware.spine.attachments.ClippingAttachment; import com.esotericsoftware.spine.attachments.ClippingAttachment;
import com.esotericsoftware.spine.attachments.MeshAttachment; import com.esotericsoftware.spine.attachments.MeshAttachment;
import com.esotericsoftware.spine.attachments.RegionAttachment; import com.esotericsoftware.spine.attachments.RegionAttachment;
import com.esotericsoftware.spine.attachments.SkeletonAttachment; import com.esotericsoftware.spine.attachments.SkeletonAttachment;
import com.esotericsoftware.spine.utils.SutherlandHodgmanClipper;
import com.esotericsoftware.spine.utils.TwoColorPolygonBatch; import com.esotericsoftware.spine.utils.TwoColorPolygonBatch;
public class SkeletonRenderer { public class SkeletonRenderer {
static private final short[] quadTriangles = {0, 1, 2, 2, 3, 0}; static private final short[] quadTriangles = { 0, 1, 2, 2, 3, 0 };
private boolean softwareClipping;
private boolean premultipliedAlpha; private boolean premultipliedAlpha;
private final FloatArray vertices = new FloatArray(32); private final FloatArray vertices = new FloatArray(32);
private SutherlandHodgmanClipper clipper = new SutherlandHodgmanClipper();
private ClippingAttachment clipAttachment;
private Slot clipEnd; private Slot clipEnd;
private FloatArray clippingArea = new FloatArray();
private float[] clipInput = new float[6];
private FloatArray clipOutput = new FloatArray(400);
private FloatArray clippedVertices = new FloatArray(400);
private ShortArray clippedTriangles = new ShortArray(400);
private final Matrix4 combinedMatrix = new Matrix4(); private final Matrix4 combinedMatrix = new Matrix4();
private ImmediateModeRenderer renderer; // BOZO! - Dispose. private ImmediateModeRenderer renderer; // BOZO! - Dispose.
public void draw (Batch batch, Skeleton skeleton) { public void draw(Batch batch, Skeleton skeleton) {
boolean premultipliedAlpha = this.premultipliedAlpha; boolean premultipliedAlpha = this.premultipliedAlpha;
float[] vertices = this.vertices.items; float[] vertices = this.vertices.items;
Color skeletonColor = skeleton.color; Color skeletonColor = skeleton.color;
@ -69,14 +79,14 @@ public class SkeletonRenderer {
Slot slot = drawOrder.get(i); Slot slot = drawOrder.get(i);
Attachment attachment = slot.attachment; Attachment attachment = slot.attachment;
if (attachment instanceof RegionAttachment) { if (attachment instanceof RegionAttachment) {
RegionAttachment region = (RegionAttachment)attachment; RegionAttachment region = (RegionAttachment) attachment;
region.computeWorldVertices(slot.getBone(), vertices, 0, 5); region.computeWorldVertices(slot.getBone(), vertices, 0, 5);
Color color = region.getColor(), slotColor = slot.getColor(); Color color = region.getColor(), slotColor = slot.getColor();
float alpha = a * slotColor.a * color.a * 255; float alpha = a * slotColor.a * color.a * 255;
float c = NumberUtils.intToFloatColor(((int)alpha << 24) // float c = NumberUtils.intToFloatColor(((int) alpha << 24) //
| ((int)(b * slotColor.b * color.b * alpha) << 16) // | ((int) (b * slotColor.b * color.b * alpha) << 16) //
| ((int)(g * slotColor.g * color.g * alpha) << 8) // | ((int) (g * slotColor.g * color.g * alpha) << 8) //
| (int)(r * slotColor.r * color.r * alpha)); | (int) (r * slotColor.r * color.r * alpha));
float[] uvs = region.getUVs(); float[] uvs = region.getUVs();
for (int u = 0, v = 2; u < 8; u += 2, v += 5) { for (int u = 0, v = 2; u < 8; u += 2, v += 5) {
vertices[v] = c; vertices[v] = c;
@ -89,7 +99,7 @@ public class SkeletonRenderer {
batch.draw(region.getRegion().getTexture(), vertices, 0, 20); batch.draw(region.getRegion().getTexture(), vertices, 0, 20);
} else if (attachment instanceof ClippingAttachment) { } else if (attachment instanceof ClippingAttachment) {
ClippingAttachment clip = (ClippingAttachment)attachment; ClippingAttachment clip = (ClippingAttachment) attachment;
batch.end(); batch.end();
clipStart(batch.getProjectionMatrix(), batch.getTransformMatrix(), slot, clip); clipStart(batch.getProjectionMatrix(), batch.getTransformMatrix(), slot, clip);
batch.begin(); batch.begin();
@ -99,7 +109,7 @@ public class SkeletonRenderer {
throw new RuntimeException("SkeletonMeshRenderer is required to render meshes."); throw new RuntimeException("SkeletonMeshRenderer is required to render meshes.");
} else if (attachment instanceof SkeletonAttachment) { } else if (attachment instanceof SkeletonAttachment) {
Skeleton attachmentSkeleton = ((SkeletonAttachment)attachment).getSkeleton(); Skeleton attachmentSkeleton = ((SkeletonAttachment) attachment).getSkeleton();
if (attachmentSkeleton != null) { if (attachmentSkeleton != null) {
Bone bone = slot.getBone(); Bone bone = slot.getBone();
Bone rootBone = attachmentSkeleton.getRootBone(); Bone rootBone = attachmentSkeleton.getRootBone();
@ -107,8 +117,10 @@ public class SkeletonRenderer {
float oldScaleY = rootBone.getScaleY(); float oldScaleY = rootBone.getScaleY();
float oldRotation = rootBone.getRotation(); float oldRotation = rootBone.getRotation();
attachmentSkeleton.setPosition(bone.getWorldX(), bone.getWorldY()); attachmentSkeleton.setPosition(bone.getWorldX(), bone.getWorldY());
// rootBone.setScaleX(1 + bone.getWorldScaleX() - oldScaleX); // rootBone.setScaleX(1 + bone.getWorldScaleX() -
// rootBone.setScaleY(1 + bone.getWorldScaleY() - oldScaleY); // oldScaleX);
// rootBone.setScaleY(1 + bone.getWorldScaleY() -
// oldScaleY);
// Set shear. // Set shear.
rootBone.setRotation(oldRotation + bone.getWorldRotationX()); rootBone.setRotation(oldRotation + bone.getWorldRotationX());
attachmentSkeleton.updateWorldTransform(); attachmentSkeleton.updateWorldTransform();
@ -131,7 +143,7 @@ public class SkeletonRenderer {
} }
@SuppressWarnings("null") @SuppressWarnings("null")
public void draw (PolygonSpriteBatch batch, Skeleton skeleton) { public void draw(PolygonSpriteBatch batch, Skeleton skeleton) {
boolean premultipliedAlpha = this.premultipliedAlpha; boolean premultipliedAlpha = this.premultipliedAlpha;
BlendMode blendMode = null; BlendMode blendMode = null;
int verticesLength = 0; int verticesLength = 0;
@ -145,7 +157,7 @@ public class SkeletonRenderer {
Slot slot = drawOrder.get(i); Slot slot = drawOrder.get(i);
Attachment attachment = slot.attachment; Attachment attachment = slot.attachment;
if (attachment instanceof RegionAttachment) { if (attachment instanceof RegionAttachment) {
RegionAttachment region = (RegionAttachment)attachment; RegionAttachment region = (RegionAttachment) attachment;
verticesLength = 20; verticesLength = 20;
vertices = this.vertices.items; vertices = this.vertices.items;
region.computeWorldVertices(slot.getBone(), vertices, 0, 5); region.computeWorldVertices(slot.getBone(), vertices, 0, 5);
@ -155,7 +167,7 @@ public class SkeletonRenderer {
color = region.getColor(); color = region.getColor();
} else if (attachment instanceof MeshAttachment) { } else if (attachment instanceof MeshAttachment) {
MeshAttachment mesh = (MeshAttachment)attachment; MeshAttachment mesh = (MeshAttachment) attachment;
int count = mesh.getWorldVerticesLength(); int count = mesh.getWorldVerticesLength();
verticesLength = (count >> 1) * 5; verticesLength = (count >> 1) * 5;
vertices = this.vertices.setSize(verticesLength); vertices = this.vertices.setSize(verticesLength);
@ -166,14 +178,14 @@ public class SkeletonRenderer {
color = mesh.getColor(); color = mesh.getColor();
} else if (attachment instanceof ClippingAttachment) { } else if (attachment instanceof ClippingAttachment) {
ClippingAttachment clip = (ClippingAttachment)attachment; ClippingAttachment clip = (ClippingAttachment) attachment;
batch.end(); batch.end();
clipStart(batch.getProjectionMatrix(), batch.getTransformMatrix(), slot, clip); clipStart(batch.getProjectionMatrix(), batch.getTransformMatrix(), slot, clip);
batch.begin(); batch.begin();
continue; continue;
} else if (attachment instanceof SkeletonAttachment) { } else if (attachment instanceof SkeletonAttachment) {
Skeleton attachmentSkeleton = ((SkeletonAttachment)attachment).getSkeleton(); Skeleton attachmentSkeleton = ((SkeletonAttachment) attachment).getSkeleton();
if (attachmentSkeleton != null) { if (attachmentSkeleton != null) {
Bone bone = slot.getBone(); Bone bone = slot.getBone();
Bone rootBone = attachmentSkeleton.getRootBone(); Bone rootBone = attachmentSkeleton.getRootBone();
@ -181,8 +193,10 @@ public class SkeletonRenderer {
float oldScaleY = rootBone.getScaleY(); float oldScaleY = rootBone.getScaleY();
float oldRotation = rootBone.getRotation(); float oldRotation = rootBone.getRotation();
attachmentSkeleton.setPosition(bone.getWorldX(), bone.getWorldY()); attachmentSkeleton.setPosition(bone.getWorldX(), bone.getWorldY());
// rootBone.setScaleX(1 + bone.getWorldScaleX() - oldScaleX); // rootBone.setScaleX(1 + bone.getWorldScaleX() -
// rootBone.setScaleY(1 + bone.getWorldScaleY() - oldScaleY); // oldScaleX);
// rootBone.setScaleY(1 + bone.getWorldScaleY() -
// oldScaleY);
// Also set shear. // Also set shear.
rootBone.setRotation(oldRotation + bone.getWorldRotationX()); rootBone.setRotation(oldRotation + bone.getWorldRotationX());
attachmentSkeleton.updateWorldTransform(); attachmentSkeleton.updateWorldTransform();
@ -199,10 +213,10 @@ public class SkeletonRenderer {
if (texture != null) { if (texture != null) {
Color slotColor = slot.getColor(); Color slotColor = slot.getColor();
float alpha = a * slotColor.a * color.a * 255; float alpha = a * slotColor.a * color.a * 255;
float c = NumberUtils.intToFloatColor(((int)alpha << 24) // float c = NumberUtils.intToFloatColor(((int) alpha << 24) //
| ((int)(b * slotColor.b * color.b * alpha) << 16) // | ((int) (b * slotColor.b * color.b * alpha) << 16) //
| ((int)(g * slotColor.g * color.g * alpha) << 8) // | ((int) (g * slotColor.g * color.g * alpha) << 8) //
| (int)(r * slotColor.r * color.r * alpha)); | (int) (r * slotColor.r * color.r * alpha));
for (int v = 2, u = 0; v < verticesLength; v += 5, u += 2) { for (int v = 2, u = 0; v < verticesLength; v += 5, u += 2) {
vertices[v] = c; vertices[v] = c;
vertices[v + 1] = uvs[u]; vertices[v + 1] = uvs[u];
@ -214,18 +228,27 @@ public class SkeletonRenderer {
blendMode = slotBlendMode; blendMode = slotBlendMode;
batch.setBlendFunction(blendMode.getSource(premultipliedAlpha), blendMode.getDest()); batch.setBlendFunction(blendMode.getSource(premultipliedAlpha), blendMode.getDest());
} }
batch.draw(texture, vertices, 0, verticesLength, triangles, 0, triangles.length); if (softwareClipping) {
if (clipAttachment != null) {
clipSoftware(vertices, 0, verticesLength, triangles, 0, triangles.length, clippedVertices, clippedTriangles, 0, c, false);
batch.draw(texture, clippedVertices.items, 0, clippedVertices.size, clippedTriangles.items, 0, clippedTriangles.size);
} else {
batch.draw(texture, vertices, 0, verticesLength, triangles, 0, triangles.length);
}
} else {
batch.draw(texture, vertices, 0, verticesLength, triangles, 0, triangles.length);
}
} }
if (slot == clipEnd) { if (slot == clipEnd) {
batch.flush(); if (!softwareClipping) batch.flush();
clipEnd(); clipEnd();
} }
} }
} }
@SuppressWarnings("null") @SuppressWarnings("null")
public void draw (TwoColorPolygonBatch batch, Skeleton skeleton) { public void draw(TwoColorPolygonBatch batch, Skeleton skeleton) {
boolean premultipliedAlpha = this.premultipliedAlpha; boolean premultipliedAlpha = this.premultipliedAlpha;
BlendMode blendMode = null; BlendMode blendMode = null;
int verticesLength = 0; int verticesLength = 0;
@ -239,7 +262,7 @@ public class SkeletonRenderer {
Slot slot = drawOrder.get(i); Slot slot = drawOrder.get(i);
Attachment attachment = slot.attachment; Attachment attachment = slot.attachment;
if (attachment instanceof RegionAttachment) { if (attachment instanceof RegionAttachment) {
RegionAttachment region = (RegionAttachment)attachment; RegionAttachment region = (RegionAttachment) attachment;
verticesLength = 24; verticesLength = 24;
vertices = this.vertices.items; vertices = this.vertices.items;
region.computeWorldVertices(slot.getBone(), vertices, 0, 6); region.computeWorldVertices(slot.getBone(), vertices, 0, 6);
@ -249,7 +272,7 @@ public class SkeletonRenderer {
color = region.getColor(); color = region.getColor();
} else if (attachment instanceof MeshAttachment) { } else if (attachment instanceof MeshAttachment) {
MeshAttachment mesh = (MeshAttachment)attachment; MeshAttachment mesh = (MeshAttachment) attachment;
int count = mesh.getWorldVerticesLength(); int count = mesh.getWorldVerticesLength();
verticesLength = count * 3; verticesLength = count * 3;
vertices = this.vertices.setSize(verticesLength); vertices = this.vertices.setSize(verticesLength);
@ -260,14 +283,14 @@ public class SkeletonRenderer {
color = mesh.getColor(); color = mesh.getColor();
} else if (attachment instanceof ClippingAttachment) { } else if (attachment instanceof ClippingAttachment) {
ClippingAttachment clip = (ClippingAttachment)attachment; ClippingAttachment clip = (ClippingAttachment) attachment;
batch.end(); batch.end();
clipStart(batch.getProjectionMatrix(), batch.getTransformMatrix(), slot, clip); clipStart(batch.getProjectionMatrix(), batch.getTransformMatrix(), slot, clip);
batch.begin(); batch.begin();
continue; continue;
} else if (attachment instanceof SkeletonAttachment) { } else if (attachment instanceof SkeletonAttachment) {
Skeleton attachmentSkeleton = ((SkeletonAttachment)attachment).getSkeleton(); Skeleton attachmentSkeleton = ((SkeletonAttachment) attachment).getSkeleton();
if (attachmentSkeleton != null) { if (attachmentSkeleton != null) {
Bone bone = slot.getBone(); Bone bone = slot.getBone();
Bone rootBone = attachmentSkeleton.getRootBone(); Bone rootBone = attachmentSkeleton.getRootBone();
@ -275,8 +298,10 @@ public class SkeletonRenderer {
float oldScaleY = rootBone.getScaleY(); float oldScaleY = rootBone.getScaleY();
float oldRotation = rootBone.getRotation(); float oldRotation = rootBone.getRotation();
attachmentSkeleton.setPosition(bone.getWorldX(), bone.getWorldY()); attachmentSkeleton.setPosition(bone.getWorldX(), bone.getWorldY());
// rootBone.setScaleX(1 + bone.getWorldScaleX() - oldScaleX); // rootBone.setScaleX(1 + bone.getWorldScaleX() -
// rootBone.setScaleY(1 + bone.getWorldScaleY() - oldScaleY); // oldScaleX);
// rootBone.setScaleY(1 + bone.getWorldScaleY() -
// oldScaleY);
// Also set shear. // Also set shear.
rootBone.setRotation(oldRotation + bone.getWorldRotationX()); rootBone.setRotation(oldRotation + bone.getWorldRotationX());
attachmentSkeleton.updateWorldTransform(); attachmentSkeleton.updateWorldTransform();
@ -293,16 +318,17 @@ public class SkeletonRenderer {
if (texture != null) { if (texture != null) {
Color lightColor = slot.getColor(); Color lightColor = slot.getColor();
float alpha = a * lightColor.a * color.a * 255; float alpha = a * lightColor.a * color.a * 255;
float light = NumberUtils.intToFloatColor(((int)alpha << 24) // float light = NumberUtils.intToFloatColor(((int) alpha << 24) //
| ((int)(b * lightColor.b * color.b * alpha) << 16) // | ((int) (b * lightColor.b * color.b * alpha) << 16) //
| ((int)(g * lightColor.g * color.g * alpha) << 8) // | ((int) (g * lightColor.g * color.g * alpha) << 8) //
| (int)(r * lightColor.r * color.r * alpha)); | (int) (r * lightColor.r * color.r * alpha));
Color darkColor = slot.getDarkColor(); Color darkColor = slot.getDarkColor();
if (darkColor == null) darkColor = Color.BLACK; if (darkColor == null)
darkColor = Color.BLACK;
float dark = NumberUtils.intToFloatColor( // float dark = NumberUtils.intToFloatColor( //
((int)(b * darkColor.b * color.b * 255) << 16) // ((int) (b * darkColor.b * color.b * 255) << 16) //
| ((int)(g * darkColor.g * color.g * 255) << 8) // | ((int) (g * darkColor.g * color.g * 255) << 8) //
| (int)(r * darkColor.r * color.r * 255)); | (int) (r * darkColor.r * color.r * 255));
for (int v = 2, u = 0; v < verticesLength; v += 6, u += 2) { for (int v = 2, u = 0; v < verticesLength; v += 6, u += 2) {
vertices[v] = light; vertices[v] = light;
vertices[v + 1] = dark; vertices[v + 1] = dark;
@ -315,50 +341,172 @@ public class SkeletonRenderer {
blendMode = slotBlendMode; blendMode = slotBlendMode;
batch.setBlendFunction(blendMode.getSource(premultipliedAlpha), blendMode.getDest()); batch.setBlendFunction(blendMode.getSource(premultipliedAlpha), blendMode.getDest());
} }
batch.draw(texture, vertices, 0, verticesLength, triangles, 0, triangles.length);
if (softwareClipping) {
if (clipAttachment != null) {
clipSoftware(vertices, 0, verticesLength, triangles, 0, triangles.length, clippedVertices, clippedTriangles, dark, light, true);
batch.draw(texture, clippedVertices.items, 0, clippedVertices.size, clippedTriangles.items, 0, clippedTriangles.size);
} else {
batch.draw(texture, vertices, 0, verticesLength, triangles, 0, triangles.length);
}
} else {
batch.draw(texture, vertices, 0, verticesLength, triangles, 0, triangles.length);
}
} }
if (slot == clipEnd) { if (slot == clipEnd) {
batch.flush(); if (!softwareClipping) batch.flush();
clipEnd(); clipEnd();
} }
} }
} }
private void clipStart (Matrix4 transformMatrix, Matrix4 projectionMatrix, Slot slot, ClippingAttachment clip) { private void clipStart(Matrix4 transformMatrix, Matrix4 projectionMatrix, Slot slot, ClippingAttachment clip) {
if (clipEnd != null) return; if (clipEnd != null) return;
clipAttachment = clip;
clipEnd = clip.getEnd(); clipEnd = clip.getEnd();
int n = clip.getWorldVerticesLength(); if (!softwareClipping) {
float[] vertices = this.vertices.setSize(n); int n = clip.getWorldVerticesLength();
clip.computeWorldVertices(slot, 0, n, vertices, 0, 2); float[] vertices = this.vertices.setSize(n);
clip.computeWorldVertices(slot, 0, n, vertices, 0, 2);
Gdx.gl.glClearStencil(0); Gdx.gl.glClearStencil(0);
Gdx.gl.glClear(GL20.GL_STENCIL_BUFFER_BIT); Gdx.gl.glClear(GL20.GL_STENCIL_BUFFER_BIT);
Gdx.gl.glEnable(GL20.GL_STENCIL_TEST); Gdx.gl.glEnable(GL20.GL_STENCIL_TEST);
Gdx.gl.glStencilFunc(GL20.GL_NEVER, 0, 1); Gdx.gl.glStencilFunc(GL20.GL_NEVER, 0, 1);
Gdx.gl.glStencilOp(GL20.GL_INVERT, GL20.GL_INVERT, GL20.GL_INVERT); Gdx.gl.glStencilOp(GL20.GL_INVERT, GL20.GL_INVERT, GL20.GL_INVERT);
Gdx.gl.glColorMask(false, false, false, false); Gdx.gl.glColorMask(false, false, false, false);
if (renderer == null || renderer.getMaxVertices() < n) if (renderer == null || renderer.getMaxVertices() < n)
renderer = new ImmediateModeRenderer20(Math.max(100, n), false, false, 0); renderer = new ImmediateModeRenderer20(Math.max(100, n), false, false, 0);
renderer.begin(combinedMatrix.set(projectionMatrix).mul(transformMatrix), GL20.GL_TRIANGLE_FAN); renderer.begin(combinedMatrix.set(projectionMatrix).mul(transformMatrix), GL20.GL_TRIANGLE_FAN);
renderer.vertex(vertices[0], vertices[1], 0); renderer.vertex(vertices[0], vertices[1], 0);
for (int i = 2; i < n; i += 2) for (int i = 2; i < n; i += 2)
renderer.vertex(vertices[i], vertices[i + 1], 0); renderer.vertex(vertices[i], vertices[i + 1], 0);
renderer.end(); renderer.end();
Gdx.gl.glColorMask(true, true, true, true); Gdx.gl.glColorMask(true, true, true, true);
Gdx.gl.glStencilFunc(clip.getInvert() ? GL20.GL_NOTEQUAL : GL20.GL_EQUAL, 1, 1); Gdx.gl.glStencilFunc(clip.getInvert() ? GL20.GL_NOTEQUAL : GL20.GL_EQUAL, 1, 1);
Gdx.gl.glStencilOp(GL20.GL_KEEP, GL20.GL_KEEP, GL20.GL_KEEP); Gdx.gl.glStencilOp(GL20.GL_KEEP, GL20.GL_KEEP, GL20.GL_KEEP);
} else {
int n = clip.getWorldVerticesLength();
float[] vertices = this.clippingArea.setSize(n);
clip.computeWorldVertices(slot, 0, n, vertices, 0, 2);
}
} }
private void clipEnd () { private void clipEnd() {
clipAttachment = null;
clipEnd = null; clipEnd = null;
Gdx.gl.glDisable(GL20.GL_STENCIL_TEST); if (!softwareClipping) Gdx.gl.glDisable(GL20.GL_STENCIL_TEST);
} }
public void setPremultipliedAlpha (boolean premultipliedAlpha) { private void clipSoftware(float[] vertices, int offset, int verticesLength, short[] triangles, int triangleOffset, int trianglesLength, FloatArray clippedVertices, ShortArray clippedTriangles, float dark, float light, boolean twoColor) {
int idx = 0;
clippedVertices.clear();
clippedTriangles.clear();
final int vertexSize = twoColor ? 6 : 5;
final int uvOffset = twoColor ? 4 : 3;
for (int i = 0; i < trianglesLength; i += 3) {
int triOffset = triangles[i] * vertexSize;
clipInput[0] = vertices[triOffset];
clipInput[1] = vertices[triOffset + 1];
float u1 = vertices[triOffset + uvOffset];
float v1 = vertices[triOffset + uvOffset + 1];
triOffset = triangles[i + 1] * vertexSize;
clipInput[2] = vertices[triOffset];
clipInput[3] = vertices[triOffset + 1];
float u2 = vertices[triOffset + uvOffset];
float v2 = vertices[triOffset + uvOffset + 1];
triOffset = triangles[i + 2] * vertexSize;
clipInput[4] = vertices[triOffset];
clipInput[5] = vertices[triOffset + 1];
float u3 = vertices[triOffset + uvOffset];
float v3 = vertices[triOffset + uvOffset + 1];
boolean clipped = clipper.clip(clipInput, 0, 6, 2, clippingArea, clipOutput);
if (clipped) {
float x1 = clipInput[0];
float y1 = clipInput[1];
float x2 = clipInput[2];
float y2 = clipInput[3];
float x3 = clipInput[4];
float y3 = clipInput[5];
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);
for (int j = 0; j < clipOutput.size; j += 2) {
float x = clipOutput.get(j);
float y = clipOutput.get(j + 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 = u1 * a + u2 * b + u3 * c;
float v = v1 * a + v2 * b + v3 * c;
clippedVertices.add(x);
clippedVertices.add(y);
clippedVertices.add(light);
if (twoColor) clippedVertices.add(dark);
clippedVertices.add(u);
clippedVertices.add(v);
}
for (int j = 1; j < (clipOutput.size >> 1) - 1; j++) {
clippedTriangles.add(idx);
clippedTriangles.add(idx + j);
clippedTriangles.add(idx + j + 1);
}
idx += clipOutput.size >> 1;
} else {
clippedVertices.add(clipInput[0]);
clippedVertices.add(clipInput[1]);
clippedVertices.add(light);
if (twoColor) clippedVertices.add(dark);
clippedVertices.add(u1);
clippedVertices.add(v1);
clippedVertices.add(clipInput[2]);
clippedVertices.add(clipInput[3]);
clippedVertices.add(light);
if (twoColor) clippedVertices.add(dark);
clippedVertices.add(u2);
clippedVertices.add(v2);
clippedVertices.add(clipInput[4]);
clippedVertices.add(clipInput[5]);
clippedVertices.add(light);
if (twoColor) clippedVertices.add(dark);
clippedVertices.add(u3);
clippedVertices.add(v3);
clippedTriangles.add(idx++);
clippedTriangles.add(idx++);
clippedTriangles.add(idx++);
}
}
}
public void setPremultipliedAlpha(boolean premultipliedAlpha) {
this.premultipliedAlpha = premultipliedAlpha; this.premultipliedAlpha = premultipliedAlpha;
} }
public boolean getSoftwareClipping () {
return softwareClipping;
}
public void setSoftwareClipping(boolean softwareClipping) {
this.softwareClipping = softwareClipping;
}
} }

View File

@ -43,7 +43,7 @@ public class SutherlandHodgmanClipper {
edgeY2 = tmp; edgeY2 = tmp;
} }
System.out.println("-- Edge " + i / 2 + ": (" + edgeX + ", " + edgeY + ")-(" + edgeX2 + ", " + edgeY2 + ")"); // System.out.println("-- Edge " + i / 2 + ": (" + edgeX + ", " + edgeY + ")-(" + edgeX2 + ", " + edgeY2 + ")");
for (int j = 0; j < input.size; j += 2) { for (int j = 0; j < input.size; j += 2) {
float inputX = input.items[j % input.size]; float inputX = input.items[j % input.size];
@ -51,11 +51,11 @@ public class SutherlandHodgmanClipper {
float inputX2 = input.items[(j + 2) % input.size]; float inputX2 = input.items[(j + 2) % input.size];
float inputY2 = input.items[(j + 3) % input.size]; float inputY2 = input.items[(j + 3) % input.size];
System.out.println("\tinput " + j / 2 + ": (" + inputX + ", " + inputY + ")-(" + inputX2 + ", " + inputY2 + ")"); // System.out.println("\tinput " + j / 2 + ": (" + inputX + ", " + inputY + ")-(" + inputX2 + ", " + inputY2 + ")");
int side = pointLineSide(edgeX2, edgeY2, edgeX, edgeY, inputX, inputY); int side = pointLineSide(edgeX2, edgeY2, edgeX, edgeY, inputX, inputY);
int side2 = pointLineSide(edgeX2, edgeY2, edgeX, edgeY, inputX2, inputY2); int side2 = pointLineSide(edgeX2, edgeY2, edgeX, edgeY, inputX2, inputY2);
System.out.println("\tv1: " + (side < 0 ? "outside" : "inside" ) + ", v2: " + (side2 < 0 ? "outside" : "inside")); // System.out.println("\tv1: " + (side < 0 ? "outside" : "inside" ) + ", v2: " + (side2 < 0 ? "outside" : "inside"));
// v1 inside, v2 inside // v1 inside, v2 inside
if (side >= 0 && side2 >= 0) { if (side >= 0 && side2 >= 0) {