LibGDX 以一定角度切割纹理
LibGDX Cut texture with an angle
假设我有一个 Texture
并且我想在以给定角度旋转的直线上将它分成两半。
结果我希望得到两个 Texture
对象,它们具有原始纹理的相应一半。如果可以用某种 transformation/masking 来完成,那就太好了,这样我就不必在运行时存储两个新纹理的副本了。
这可能吗?
我尝试解决 Gdx.gl.glSsisors()
但无法使其工作,因为它需要将屏幕坐标作为参数传递。
我也尝试使用 Pixmap
但找不到任何表明它甚至可能的东西。
这可以通过手动计算两个倾斜四边形并使用 SpriteBatch
渲染它们来实现。
沿角度 angle
切割一个精灵,我们得到一个表示切割的方向向量
public void cut(float angle) {
Vector2 d = (new Vector2(1.0f, 0.0f)).rotate(angle);
如果我们还为 Texture
的 UV Mapping 定义了四个角以及中心和一个向量 la
远离中心 d
并且lb
进行 -d
Vector2 c = new Vector2(0.5f, 0.5f);
Vector2 la = (new Vector2(d)).scl( 1.0f).add(c);
Vector2 lb = (new Vector2(d)).scl(-1.0f).add(c);
Vector2 tl = new Vector2(0, 1);
Vector2 tr = new Vector2(1, 1);
Vector2 bl = new Vector2(0, 0);
Vector2 br = new Vector2(1, 0);
然后我们可以计算切割的交点
Vector2 i1 = new Vector2();
Vector2 i2 = new Vector2();
if (Intersector.intersectSegments(c, la, tl, tr, i1) || Intersector.intersectSegments(c, lb, tl, tr, i1))
i2.set(1.0f - i1.x, 1.0f - i1.y);
else {
if (Intersector.intersectSegments(c, la, tl, bl, i1) || Intersector.intersectSegments(c, lb, tl, bl, i1))
i2.set(1.0f - i1.x, 1.0f - i1.y);
}
此时我们知道切割的一半将由顶点i1
、i2
和tl
、tr
、[=26=中的两个顶点组成] 和 br
,所以如果我们按远离切割的角度对它们进行排序,然后从 i1
中取出前 4 个,我们将得到构建倾斜四边形所需的顶点:
Vector2[] vertexList = new Vector2[] {
tl, tr, bl, br, i1, i2
};
Array<VertexAngle> vas = new Array<>();
for (Vector2 v : vertexList) {
Vector2 vd = (new Vector2(v)).sub(c);
float a = d.angle(vd);
VertexAngle va = new VertexAngle();
va.v = v;
va.a = a;
vas.add(va);
}
vas.sort(new Comparator<VertexAngle>() {
@Override
public int compare(VertexAngle a, VertexAngle b) {
return Float.compare(a.a, b.a);
}
});
Array<Vector2> nv = new Array<>();
for (VertexAngle va : vas)
nv.add(va.v);
int index = nv.indexOf(i1, true);
可以通过在 SpriteBatch
.
上调用 draw
来构造和渲染包含绘图调用顶点数据的浮点数组
例如:
上面例子的完整源代码是:
package com.bornander.sandbox;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
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.SpriteBatch;
import com.badlogic.gdx.math.Intersector;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
import java.util.Comparator;
public class SandboxGame extends ApplicationAdapter {
OrthographicCamera camera;
SpriteBatch batch;
Texture texture;
CutTexture cutTexture;
public static class CutTexture
{
public static class VertexAngle {
public Vector2 v;
public float a;
}
public static class CutHalf {
public float[] vertices = new float[4 * 5];
public void translate(float x, float y) {
for(int i = 0; i < vertices.length; i += 5) {
vertices[i + 0] += x;
vertices[i + 1] += y;
}
}
}
public Vector2 position = new Vector2();
public Vector2 driftDirection = new Vector2();
public float drift = 0.0f;
public Texture source;
public CutHalf halfA = new CutHalf();
public CutHalf halfB = new CutHalf();
public void cut(float angle) {
Vector2 d = (new Vector2(1.0f, 0.0f)).rotate(angle);
Vector2 c = new Vector2(0.5f, 0.5f);
driftDirection.set(d).rotate(90.0f);
Vector2 la = (new Vector2(d)).scl( 1.0f).add(c);
Vector2 lb = (new Vector2(d)).scl(-1.0f).add(c);
Vector2 tl = new Vector2(0, 1);
Vector2 tr = new Vector2(1, 1);
Vector2 bl = new Vector2(0, 0);
Vector2 br = new Vector2(1, 0);
Vector2 i1 = new Vector2();
Vector2 i2 = new Vector2();
if (Intersector.intersectSegments(c, la, tl, tr, i1) || Intersector.intersectSegments(c, lb, tl, tr, i1))
i2.set(1.0f - i1.x, 1.0f - i1.y);
else {
if (Intersector.intersectSegments(c, la, tl, bl, i1) || Intersector.intersectSegments(c, lb, tl, bl, i1))
i2.set(1.0f - i1.x, 1.0f - i1.y);
}
Vector2[] vertexList = new Vector2[] {
tl, tr, bl, br, i1, i2
};
Array<VertexAngle> vas = new Array<>();
for (Vector2 v : vertexList) {
Vector2 vd = (new Vector2(v)).sub(c);
float a = d.angle(vd);
VertexAngle va = new VertexAngle();
va.v = v;
va.a = a;
vas.add(va);
}
vas.sort(new Comparator<VertexAngle>() {
@Override
public int compare(VertexAngle a, VertexAngle b) {
return Float.compare(a.a, b.a);
}
});
Array<Vector2> nv = new Array<>();
for (VertexAngle va : vas)
nv.add(va.v);
int index = nv.indexOf(i1, true);
int idx = 0;
int lastIndex = 0;
for(int j = 0; j < 4; ++j) {
lastIndex = (index + j) % nv.size;
Vector2 vertex = nv.get(lastIndex);
float width = source.getWidth();
float height = source.getWidth();
float fx2 = position.x + width * vertex.x - width / 2.0f;
float fy2 = position.y + height * vertex.y - height / 2.0f;
halfA.vertices[idx++] = fx2;
halfA.vertices[idx++] = fy2;
halfA.vertices[idx++] = Color.WHITE_FLOAT_BITS;
halfA.vertices[idx++] = vertex.x;
halfA.vertices[idx++] = 1.0f - vertex.y;
}
idx = 0;
for(int j = 0; j < 4; ++j) {
Vector2 vertex = nv.get((lastIndex + j) % nv.size);
float width = source.getWidth();
float height = source.getWidth();
float fx2 = position.x + width * vertex.x - width / 2.0f;
float fy2 = position.y + height * vertex.y - height / 2.0f;
halfB.vertices[idx++] = fx2;
halfB.vertices[idx++] = fy2;
halfB.vertices[idx++] = Color.WHITE_FLOAT_BITS;
halfB.vertices[idx++] = vertex.x;
halfB.vertices[idx++] = 1.0f - vertex.y;
}
}
public void render(SpriteBatch batch) {
float dx = driftDirection.x * drift;
float dy = driftDirection.y * drift;
halfA.translate(dx, dy);
halfB.translate(-dx, -dy);
batch.draw(source, halfA.vertices, 0, 20);
batch.draw(source, halfB.vertices, 0, 20);
halfA.translate(-dx, -dy);
halfB.translate(dx, dy);
}
}
@Override
public void create () {
float aspectRatio = (float)Gdx.graphics.getHeight()/(float)Gdx.graphics.getWidth();
camera = new OrthographicCamera(800, 800 * aspectRatio);
camera.position.set(camera.viewportWidth / 2.0f, camera.viewportHeight / 2.0f, 0.0f);
batch = new SpriteBatch();
texture = new Texture(Gdx.files.internal("badlogic.jpg"));
Gdx.gl.glCullFace(0);
cutTexture = new CutTexture();
cutTexture.position.set(camera.viewportWidth / 2.0f, camera.viewportHeight / 2.0f);
cutTexture.source = texture;
cutTexture.cut(0);
}
float[] cutAngles = new float[] { 0.0f, -22.5f, -45.0f, -12.0f, -75.0f, -90.0f};
int ai = 0;
@Override
public void render () {
if (Gdx.input.isKeyJustPressed(Input.Keys.SPACE))
{
cutTexture.drift = 0.0f;
cutTexture.cut(cutAngles[(ai++) % cutAngles.length]);
}
cutTexture.drift -= 64.0f * Gdx.graphics.getDeltaTime();
Gdx.gl.glClearColor(0.6f, 0.6f, 1.0f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
Gdx.gl.glCullFace(GL20.GL_NONE);
camera.update();
batch.setProjectionMatrix(camera.combined);
batch.begin();
cutTexture.render(batch);
batch.end();
}
}
假设我有一个 Texture
并且我想在以给定角度旋转的直线上将它分成两半。
结果我希望得到两个 Texture
对象,它们具有原始纹理的相应一半。如果可以用某种 transformation/masking 来完成,那就太好了,这样我就不必在运行时存储两个新纹理的副本了。
这可能吗?
我尝试解决 Gdx.gl.glSsisors()
但无法使其工作,因为它需要将屏幕坐标作为参数传递。
我也尝试使用 Pixmap
但找不到任何表明它甚至可能的东西。
这可以通过手动计算两个倾斜四边形并使用 SpriteBatch
渲染它们来实现。
沿角度 angle
切割一个精灵,我们得到一个表示切割的方向向量
public void cut(float angle) {
Vector2 d = (new Vector2(1.0f, 0.0f)).rotate(angle);
如果我们还为 Texture
的 UV Mapping 定义了四个角以及中心和一个向量 la
远离中心 d
并且lb
进行 -d
Vector2 c = new Vector2(0.5f, 0.5f);
Vector2 la = (new Vector2(d)).scl( 1.0f).add(c);
Vector2 lb = (new Vector2(d)).scl(-1.0f).add(c);
Vector2 tl = new Vector2(0, 1);
Vector2 tr = new Vector2(1, 1);
Vector2 bl = new Vector2(0, 0);
Vector2 br = new Vector2(1, 0);
然后我们可以计算切割的交点
Vector2 i1 = new Vector2();
Vector2 i2 = new Vector2();
if (Intersector.intersectSegments(c, la, tl, tr, i1) || Intersector.intersectSegments(c, lb, tl, tr, i1))
i2.set(1.0f - i1.x, 1.0f - i1.y);
else {
if (Intersector.intersectSegments(c, la, tl, bl, i1) || Intersector.intersectSegments(c, lb, tl, bl, i1))
i2.set(1.0f - i1.x, 1.0f - i1.y);
}
此时我们知道切割的一半将由顶点i1
、i2
和tl
、tr
、[=26=中的两个顶点组成] 和 br
,所以如果我们按远离切割的角度对它们进行排序,然后从 i1
中取出前 4 个,我们将得到构建倾斜四边形所需的顶点:
Vector2[] vertexList = new Vector2[] {
tl, tr, bl, br, i1, i2
};
Array<VertexAngle> vas = new Array<>();
for (Vector2 v : vertexList) {
Vector2 vd = (new Vector2(v)).sub(c);
float a = d.angle(vd);
VertexAngle va = new VertexAngle();
va.v = v;
va.a = a;
vas.add(va);
}
vas.sort(new Comparator<VertexAngle>() {
@Override
public int compare(VertexAngle a, VertexAngle b) {
return Float.compare(a.a, b.a);
}
});
Array<Vector2> nv = new Array<>();
for (VertexAngle va : vas)
nv.add(va.v);
int index = nv.indexOf(i1, true);
可以通过在 SpriteBatch
.
draw
来构造和渲染包含绘图调用顶点数据的浮点数组
例如:
上面例子的完整源代码是:
package com.bornander.sandbox;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
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.SpriteBatch;
import com.badlogic.gdx.math.Intersector;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
import java.util.Comparator;
public class SandboxGame extends ApplicationAdapter {
OrthographicCamera camera;
SpriteBatch batch;
Texture texture;
CutTexture cutTexture;
public static class CutTexture
{
public static class VertexAngle {
public Vector2 v;
public float a;
}
public static class CutHalf {
public float[] vertices = new float[4 * 5];
public void translate(float x, float y) {
for(int i = 0; i < vertices.length; i += 5) {
vertices[i + 0] += x;
vertices[i + 1] += y;
}
}
}
public Vector2 position = new Vector2();
public Vector2 driftDirection = new Vector2();
public float drift = 0.0f;
public Texture source;
public CutHalf halfA = new CutHalf();
public CutHalf halfB = new CutHalf();
public void cut(float angle) {
Vector2 d = (new Vector2(1.0f, 0.0f)).rotate(angle);
Vector2 c = new Vector2(0.5f, 0.5f);
driftDirection.set(d).rotate(90.0f);
Vector2 la = (new Vector2(d)).scl( 1.0f).add(c);
Vector2 lb = (new Vector2(d)).scl(-1.0f).add(c);
Vector2 tl = new Vector2(0, 1);
Vector2 tr = new Vector2(1, 1);
Vector2 bl = new Vector2(0, 0);
Vector2 br = new Vector2(1, 0);
Vector2 i1 = new Vector2();
Vector2 i2 = new Vector2();
if (Intersector.intersectSegments(c, la, tl, tr, i1) || Intersector.intersectSegments(c, lb, tl, tr, i1))
i2.set(1.0f - i1.x, 1.0f - i1.y);
else {
if (Intersector.intersectSegments(c, la, tl, bl, i1) || Intersector.intersectSegments(c, lb, tl, bl, i1))
i2.set(1.0f - i1.x, 1.0f - i1.y);
}
Vector2[] vertexList = new Vector2[] {
tl, tr, bl, br, i1, i2
};
Array<VertexAngle> vas = new Array<>();
for (Vector2 v : vertexList) {
Vector2 vd = (new Vector2(v)).sub(c);
float a = d.angle(vd);
VertexAngle va = new VertexAngle();
va.v = v;
va.a = a;
vas.add(va);
}
vas.sort(new Comparator<VertexAngle>() {
@Override
public int compare(VertexAngle a, VertexAngle b) {
return Float.compare(a.a, b.a);
}
});
Array<Vector2> nv = new Array<>();
for (VertexAngle va : vas)
nv.add(va.v);
int index = nv.indexOf(i1, true);
int idx = 0;
int lastIndex = 0;
for(int j = 0; j < 4; ++j) {
lastIndex = (index + j) % nv.size;
Vector2 vertex = nv.get(lastIndex);
float width = source.getWidth();
float height = source.getWidth();
float fx2 = position.x + width * vertex.x - width / 2.0f;
float fy2 = position.y + height * vertex.y - height / 2.0f;
halfA.vertices[idx++] = fx2;
halfA.vertices[idx++] = fy2;
halfA.vertices[idx++] = Color.WHITE_FLOAT_BITS;
halfA.vertices[idx++] = vertex.x;
halfA.vertices[idx++] = 1.0f - vertex.y;
}
idx = 0;
for(int j = 0; j < 4; ++j) {
Vector2 vertex = nv.get((lastIndex + j) % nv.size);
float width = source.getWidth();
float height = source.getWidth();
float fx2 = position.x + width * vertex.x - width / 2.0f;
float fy2 = position.y + height * vertex.y - height / 2.0f;
halfB.vertices[idx++] = fx2;
halfB.vertices[idx++] = fy2;
halfB.vertices[idx++] = Color.WHITE_FLOAT_BITS;
halfB.vertices[idx++] = vertex.x;
halfB.vertices[idx++] = 1.0f - vertex.y;
}
}
public void render(SpriteBatch batch) {
float dx = driftDirection.x * drift;
float dy = driftDirection.y * drift;
halfA.translate(dx, dy);
halfB.translate(-dx, -dy);
batch.draw(source, halfA.vertices, 0, 20);
batch.draw(source, halfB.vertices, 0, 20);
halfA.translate(-dx, -dy);
halfB.translate(dx, dy);
}
}
@Override
public void create () {
float aspectRatio = (float)Gdx.graphics.getHeight()/(float)Gdx.graphics.getWidth();
camera = new OrthographicCamera(800, 800 * aspectRatio);
camera.position.set(camera.viewportWidth / 2.0f, camera.viewportHeight / 2.0f, 0.0f);
batch = new SpriteBatch();
texture = new Texture(Gdx.files.internal("badlogic.jpg"));
Gdx.gl.glCullFace(0);
cutTexture = new CutTexture();
cutTexture.position.set(camera.viewportWidth / 2.0f, camera.viewportHeight / 2.0f);
cutTexture.source = texture;
cutTexture.cut(0);
}
float[] cutAngles = new float[] { 0.0f, -22.5f, -45.0f, -12.0f, -75.0f, -90.0f};
int ai = 0;
@Override
public void render () {
if (Gdx.input.isKeyJustPressed(Input.Keys.SPACE))
{
cutTexture.drift = 0.0f;
cutTexture.cut(cutAngles[(ai++) % cutAngles.length]);
}
cutTexture.drift -= 64.0f * Gdx.graphics.getDeltaTime();
Gdx.gl.glClearColor(0.6f, 0.6f, 1.0f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
Gdx.gl.glCullFace(GL20.GL_NONE);
camera.update();
batch.setProjectionMatrix(camera.combined);
batch.begin();
cutTexture.render(batch);
batch.end();
}
}