I want to draw some (filled) polygons with libGDX. It shoudn't be filled with a graphic/texture. I have only the vertices of the polygon (closed path) and tried to visualize with meshes but at some point this is not the best solution, I think.
My code for an rectangle is:
private Mesh mesh;
#Override
public void create() {
if (mesh == null) {
mesh = new Mesh(
true, 4, 0,
new VertexAttribute(Usage.Position, 3, "a_position")
);
mesh.setVertices(new float[] {
-0.5f, -0.5f, 0
0.5f, -0.5f, 0,
-0.5f, 0.5f, 0,
0.5f, 0.5f, 0
});
}
}
// ...
#Override
public void render() {
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
mesh.render(GL10.GL_TRIANGLE_STRIP, 0, 4);
}
is there a function or something to draw filled polygons in an easier way?
Since recent updates of LibGDX, #Rus answer is using deprecated functions. However, I give him/her credits for the new updated version below:
PolygonSprite poly;
PolygonSpriteBatch polyBatch = new PolygonSpriteBatch(); // To assign at the beginning
Texture textureSolid;
// Creating the color filling (but textures would work the same way)
Pixmap pix = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
pix.setColor(0xDEADBEFF); // DE is red, AD is green and BE is blue.
pix.fill();
textureSolid = new Texture(pix);
PolygonRegion polyReg = new PolygonRegion(new TextureRegion(textureSolid),
new float[] { // Four vertices
0, 0, // Vertex 0 3--2
100, 0, // Vertex 1 | /|
100, 100, // Vertex 2 |/ |
0, 100 // Vertex 3 0--1
}, new short[] {
0, 1, 2, // Two triangles using vertex indices.
0, 2, 3 // Take care of the counter-clockwise direction.
});
poly = new PolygonSprite(polyReg);
poly.setOrigin(a, b);
polyBatch = new PolygonSpriteBatch();
For good triangulating algorithms if your polygon is not convex, see the almost-linear earclipping algorithm from Toussaint (1991)
Efficient triangulation of simple polygons, Godfried Toussaint, 1991
Here is a libGDX example which draws a 2D concave polygon.
Define class members for PolygonSprite PolygonSpriteBatch
PolygonSprite poly;
PolygonSpriteBatch polyBatch;
Texture textureSolid;
Create instances, 1x1 size texture used with red pixel as workaround. An array of coordinates (x, y) is used for initialization of the polygon.
ctor() {
textureSolid = makeTextureBox(1, 0xFFFF0000, 0, 0);
float a = 100;
float b = 100;
PolygonRegion polyReg = new PolygonRegion(new TextureRegion(textureSolid),
new float[] {
a*0, b*0,
a*0, b*2,
a*3, b*2,
a*3, b*0,
a*2, b*0,
a*2, b*1,
a*1, b*1,
a*1, b*0,
});
poly = new PolygonSprite(polyReg);
poly.setOrigin(a, b);
polyBatch = new PolygonSpriteBatch();
}
Draw and rotate polygon
void draw() {
super.draw();
polyBatch.begin();
poly.draw(polyBatch);
polyBatch.end();
poly.rotate(1.1f);
}
I believe the ShapeRenderer class now has a polygon method for vertex defined polygons:
ShapeRenderer.polygon()
You can use the ShapeRenderer API to draw simple, solid-color shapes with Libgdx.
The code you've given is a reasonable way to draw solid color polygons too. Its much more flexible than ShapeRenderer, but is a good bit more complicated. You'll need to use glColor4f to set the color, or add a Usage.Color attribute to each vertex. See the SubMeshColorTest example for more details on the first approach and the MeshColorTexture example for details on the second approach.
Another option to think about is using sprite textures. If you're only interested in simple solid colors objects, you can use very simple 1x1 textures of a single color and let the system stretch that across the sprite. Much of Libgdx and the underlying hardware are really optimized for rendering textures, so you may find it easier to use even if you're not really taking advantage of the texture contents. (You can even use a 1x1 white texture, and then use a SpriteBatch with setColor and draw()
to draw different color rectangles easily.)
You can also mix and match the various approaches, too.
Use triangulation algorithm and then draw all triangles as GL_TRIANGLE_STRIP
http://www.personal.psu.edu/cxc11/AERSP560/DELAUNEY/13_Two_algorithms_Delauney.pdf
just wanted to share my related solution with you, namely for implementing and drawing a walkZone with scene2d. I basically had to put together the different suggestions of the others' posts:
1) The WalkZone:
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.PolygonRegion;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.EarClippingTriangulator;
import com.badlogic.gdx.math.Polygon;
import com.mygdx.game.MyGame;
public class WalkZone extends Polygon {
private PolygonRegion polygonRegion = null;
public WalkZone(float[] vertices) {
super(vertices);
if (MyGame.DEBUG) {
Pixmap pix = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
pix.setColor(0x00FF00AA);
pix.fill();
polygonRegion = new PolygonRegion(new TextureRegion(new Texture(pix)),
vertices, new EarClippingTriangulator().computeTriangles(vertices).toArray());
}
}
public PolygonRegion getPolygonRegion() {
return polygonRegion;
}
}
2) The Screen:
you can then add a listener in the desired Stage:
myStage.addListener(new InputListener() {
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
if (walkZone.contains(x, y)) player.walkTo(x, y);
// or even directly: player.addAction(moveTo ...
return super.touchDown(event, x, y, pointer, button);
}
});
3) The implementation:
The array passed to te WZ constructor is a set of x,y,x,y... points. If you put them counter-clockwise, it works (I didn't check the other way, nor know how it exactly works); for example this generates a 100x100 square:
yourScreen.walkZone = new WalkZone(new int[]{0, 0, 100, 0, 100, 100, 0, 100});
In my project it works like a charm, even with very intricated polygons. Hope it helps!!
Most answers suggest triangulation, which is fine, but you can also do it using the stencil buffer. It handles both convex and concave polygons. This may be a better solution if your polygon changes a lot, since otherwise you'd have to do triangulation every frame. Also, this solution properly handles self intersecting polygons, which EarClippingTriangulator does not.
FloatArray vertices = ... // The polygon x,y pairs.
Color color = ... // The color to draw the polygon.
ShapeRenderer shapes = ...
ImmediateModeRenderer renderer = shapes.getRenderer();
Gdx.gl.glClearStencil(0);
Gdx.gl.glClear(GL20.GL_STENCIL_BUFFER_BIT);
Gdx.gl.glEnable(GL20.GL_STENCIL_TEST);
Gdx.gl.glStencilFunc(GL20.GL_NEVER, 0, 1);
Gdx.gl.glStencilOp(GL20.GL_INVERT, GL20.GL_INVERT, GL20.GL_INVERT);
Gdx.gl.glColorMask(false, false, false, false);
renderer.begin(shapes.getProjectionMatrix(), GL20.GL_TRIANGLE_FAN);
renderer.vertex(vertices.get(0), vertices.get(1), 0);
for (int i = 2, n = vertices.size; i < n; i += 2)
renderer.vertex(vertices.get(i), vertices.get(i + 1), 0);
renderer.end();
Gdx.gl.glColorMask(true, true, true, true);
Gdx.gl.glStencilOp(GL20.GL_ZERO, GL20.GL_ZERO, GL20.GL_ZERO);
Gdx.gl.glStencilFunc(GL20.GL_EQUAL, 1, 1);
Gdx.gl.glEnable(GL20.GL_BLEND);
shapes.setColor(color);
shapes.begin(ShapeType.Filled);
shapes.rect(-9999999, -9999999, 9999999 * 2, 9999999 * 2);
shapes.end();
Gdx.gl.glDisable(GL20.GL_STENCIL_TEST);
To use the stencil buffer, you must specify the number of bits for the stencil buffer when your app starts. For example, here is how to do that using the LWJGL2 backend:
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
config.stencil = 8;
new LwjglApplication(new YourApp(), config);
For more information on this technique, try one of these links:
http://commaexcess.com/articles/7/concave-polygon-triangulation-shortcut
http://glprogramming.com/red/chapter14.html#name13
http://what-when-how.com/opengl-programming-guide/drawing-filled-concave-polygons-using-the-stencil-buffer-opengl-programming/
Related
I'm using LibGDX with Scene2D for my java game. I know my issue is connected to Scene2D, because I used the EXACT same class passing it normally to SpriteBatch (not through Stage instance) and it worked as expected.
I let Stage manage all of my drawables entities which are actors. It draws everything using implementor of Batch; SpriteBatch is the default. And it was working until I wanted to draw a polygon, which has to be drawn by PolygonSpriteBatch, not SpriteBatch.. So during one Stage.draw() call I need to use them both.
I made a CheckedPolygon class which is basically two PolygonSprites drawn on top of each other (one is semi-transparent). SpriteBatch passed in draw() method is temporarily ended to enable PolygonSpriteBatch for a moment, draw the polygon and disable it.
And the output is empty screen, I get nothing. Again, it worked when I wasn't using Stage class.
Here's the class, so you get a better understanding.
NOTE: I know this is bad in terms of performance, because I don't dispose of Texture and keep Batch for one object but it's for the sake of simplicity.
NOTE2: Yes, I passed it properly to stage, the code is executed, I checked through debugging.
public class CheckedPolygon extends Actor {
private final PolygonSprite mBackground, mCross;
private final PolygonSpriteBatch mPolygonSpriteBatch;
private final Camera mCamera;
public CheckedPolygon(Camera camera, float[] vertices, short[] triangles) {
super();
mCamera = camera;
mPolygonSpriteBatch = new PolygonSpriteBatch();
Texture textureBack = new Texture(Gdx.files.internal("source/back.png"));
textureBack.setWrap(Texture.TextureWrap.Repeat, Texture.TextureWrap.Repeat);
PolygonRegion regionBack = new PolygonRegion(new TextureRegion(textureBack), vertices, triangles);
mBackground = new PolygonSprite(regionBack);
Texture textureCross = new Texture(Gdx.files.internal("source/cross.png"));
textureCross.setWrap(Texture.TextureWrap.Repeat, Texture.TextureWrap.Repeat);
PolygonRegion regionCross = new PolygonRegion(new TextureRegion(textureCross), vertices, triangles);
mCross = new PolygonSprite(regionCross);
}
#Override
public void draw(Batch batch, float parentAlpha) {
batch.end();
mPolygonSpriteBatch.setProjectionMatrix(mCamera.combined);
mPolygonSpriteBatch.begin();
mBackground.draw(mPolygonSpriteBatch);
mCross.draw(mPolygonSpriteBatch);
mPolygonSpriteBatch.end();
batch.begin();
}
}
And I use it like this:
CheckedPolygon polygon = new CheckedPolygon(mStage.getCamera(), mTextureAtlas, new float[]{0, 500, 0, 0, 0, 500}, new short[]{0, 1, 2});
mStage.addActor(polygon);
I checked values from methods PolygonSprite.getVertices() and PolygonSprite.getBoundingRectangle() and got some weird outputs...
You create an invalid polygon with 0,500 being the first and last point.
So you need to chose valid vertices, for example:
new float[] {0, 0, 0, 500, 500, 500}
I created a small image to visualize it. The line on top is your "polygon", the triangle below is a polygon with the vertices from above:
The vertices you get back from the polygon sprite are otherwise ok, since they contain x, y, color, u, v.
I have been wondering how I would go by making a health bar in libgdx/java. I am looking into a Shape Renderer thinking I can use a filled shape to create a health bar, though I am not sure on how to make an approach in that direction. On the other hand I have been looking into using scene2D and NinePatches to create a healthbar for the game. I have tried to use a NinePatch/Scene2D by looking up websites on google, though it seems when I add the code I always get an issue when I try to render it to my screen. So if anybody can help me create a functional health bar in libgdx using whatever method, I would be extremely thankful. Thank you.
I cannot post pictures yet, because I have not posted 10 posts. So here is the link http://gyazo.com/6650f3d820f31c99c3f8061ebed0bdec. Sorry
I don't have any experience with Scene2D and stages but here is how i would do a simple healthbar:
Create a rectangle and fill this with a color or texture.
When a player loses HP shorten that rectangle by the same percentage as the HP lost.
Create a basic Orthographic camera with the height/width of the screen. I usually call this viewportCam.
Below all the other draw logic you change the SpriteBatch too spriteBatch(viewportCam.Combined)
Within this spritebatch you draw the healthbar.
public void Draw()
{
spriteBatch(normalCam.combined);
spriteBatch.begin();
//Normal draw logic.
spriteBatch.end();
spriteBatch(viewportCam.combined);
spriteBatch.begin();
//Draw UI elements last so the will be drawn on top of the rest.
spriteBatch.end();
}
If you draw a slightly larger rectangle behind the healthbar you have yourself a border.
You can change color depending on how much percentage health is left.
Edit
Just came across this again and currently using another method. I a ninepatch from a 1 pixel wide red gradient. health = new NinePatch(gradient, 0, 0, 0, 0) this makes sure no artifacts occur while stretching it. From here I just calculate how long it needs to be and draw it.
width = currentHealth / totalHealth * totalBarWidth;
Now you can draw it anywhere you want.
health.draw(batch, 10, 10, width, gradient.getHeight);
If you want somekind of container for it you setup a ninepatch for it and draw it before the dynamic health in the background. So let's say the container is 2 bigger at top/bottom and 5 at left/right.
container = new NinePatch(containerRegion, 5, 5, 2, 2);
//Offset it by the dynamic bar, let's say the gradient is 4 high.
container.draw(batch, 5, 8, totalBarWidth + 10, 8);
health.draw(batch, 10, 10, width, 4);
I "hardcoded" the container 5 to the left and made it 10 longer so it fits horizontally. Vertically I lowered it by 2 and made it 8 high to fit the bar perfectly.
This is how you can do it ,implement logic your self
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
public class MyGdxGame extends ApplicationAdapter {
SpriteBatch batch;
int i=0;
Texture texture,texture2;
#Override
public void create () {
batch = new SpriteBatch();
initTestObjects() ;
}
#Override
public void render () {
Gdx.gl.glClearColor(0, 0, 0, 0);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
batch.draw(texture2,100,100,300,20);
batch.draw(texture,100,100,i,20);
if(i>300)
{
i=0;
}
i++;
batch.end();
}
private void initTestObjects() {
int width =1 ;
int height = 1;
Pixmap pixmap = createProceduralPixmap(width, height,0,1,0);
Pixmap pixmap2 = createProceduralPixmap(width, height,1,0,0);
texture = new Texture(pixmap);
texture2 = new Texture(pixmap2);
}
private Pixmap createProceduralPixmap (int width, int height,int r,int g,int b) {
Pixmap pixmap = new Pixmap(width, height, Format.RGBA8888);
pixmap.setColor(r, g, b, 1);
pixmap.fill();
return pixmap;
}
}
Say, I have a sprite of a game object which is a png image with transparency.
I want to create a polygon from this image which will contain my game object.
I am pretty sure there is an existing algorithm for it, but I haven't found any.
I expect something like:
public static Polygon getPolygon(BufferedImage sprite)
{
// get coordinates of all points for polygon
return polygon;
}
See this question. It will be slow, but it depends on how accurate you want it (second answer is sloppier, but a bit faster). After you get the Area from getOutline() on the other question, try using this code (untested):
public static Polygon getPolygonOutline(BufferedImage image) {
Area a = getOutline(image, new Color(0, 0, 0, 0), false, 10); // 10 or whatever color tolerance you want
Polygon p = new Polygon();
FlatteningPathIterator fpi = new FlatteningPathIterator(a.getPathIterator(null), 0.1); // 0.1 or how sloppy you want it
double[] pts = new double[6];
while (!fpi.isDone()) {
switch (fpi.currentSegment(pts)) {
case FlatteningPathIterator.SEG_MOVETO:
case FlatteningPathIterator.SEG_LINETO:
p.addPoint((int) pts[0], (int) pts[1]);
break;
}
fpi.next();
}
return p;
}
I have created Polygon object to wrap my airplane (size of airplane's TextureRegion is 256x74, but size of this one in a game is 70x20). So:
TextureRegion[] texRegsAirplane = TextureRegion.split(textureAirplane, 256, 74);
Rectangle bounds = new Rectangle(0, 0, 70, 20);
Polygon polygon = new Polygon(new float[]{0,0,bounds.width,0,bounds.width,bounds.height,0,bounds.height,0,0});
After that in my update function I update position of it:
public void update(float delta){
Vector2 v = getPosition();
v.add(velocity);
polygon.setPosition(v.x, v.y);
}
Then I render polygon to know where it is:
public void render(SpriteBatch spriteBatch, float pixelPerUnitX, float pixelPerUnitY){
spriteBatch.draw(testTexture,polygon.getX()*pixelPerUnitX, polygon.getY()*pixelPerUnitY,
polygon.getBoundingRectangle().width*pixelPerUnitX,polygon.getBoundingRectangle().height*pixelPerUnitY);
}
At the end I create 2 airplanes and make them fly to each other and every iteration I try to detect collision like below:
public void detectCollision(){
for(Airplane airplane1 : Airplanes){
for(Airplane airplane2 : Airplanes){
if(Intersector.overlapConvexPolygons(airplane1.getPolygon(), airplane2.getPolygon())){
//COLLISION DON'T HAPPEN!!!
}
}
}
I see that 2 rectangles move to each other and intersect, but overlapConvexPolygons function doesn't work! Why?
I've solved this problem. I incorrectly specified vertices. I needed to get rectangular polygon, so I had to use following:
polygon = new Polygon(new float[]{0,0,bounds.width,0,bounds.width,bounds.height,0,bounds.height});
and do not forget set origin if you are going to rotate polygon object:
polygon.setOrigin(bounds.width/2, bounds.height/2);
Now it works perfect!
Hey all I'm trying to implement 3D picking into my program, and it works perfectly if I don't move from the origin. It is perfectly accurate. But if I move the model matrix away from the origin (the viewmatrix eye is still at 0,0,0) the picking vectors are still drawn from the original location. It should still be drawing from the view matrix eye (0,0,0) but it isn't. Here's some of my code to see if you can find out why..
Vector3d near = unProject(x, y, 0, mMVPMatrix, this.width, this.height);
Vector3d far = unProject(x, y, 1, mMVPMatrix, this.width, this.height);
Vector3d pickingRay = far.subtract(near);
//pickingRay.z *= -1;
Vector3d normal = new Vector3d(0,0,1);
if (normal.dot(pickingRay) != 0 && pickingRay.z < 0)
{
float t = (-5f-normal.dot(mCamera.eye))/(normal.dot(pickingRay));
pickingRay = mCamera.eye.add(pickingRay.scale(t));
addObject(pickingRay.x, pickingRay.y, pickingRay.z+.5f, Shape.BOX);
//a line for the picking vector for debugging
PrimProperties a = new PrimProperties(); //new prim properties for size and center
Prim result = null;
result = new Line(a, mCamera.eye, far);//new line object for seeing look at vector
result.createVertices();
objects.add(result);
}
public static Vector3d unProject(
float winx, float winy, float winz,
float[] resultantMatrix,
float width, float height)
{
winy = height-winy;
float[] m = new float[16],
in = new float[4],
out = new float[4];
Matrix.invertM(m, 0, resultantMatrix, 0);
in[0] = (winx / width) * 2 - 1;
in[1] = (winy / height) * 2 - 1;
in[2] = 2 * winz - 1;
in[3] = 1;
Matrix.multiplyMV(out, 0, m, 0, in, 0);
if (out[3]==0)
return null;
out[3] = 1/out[3];
return new Vector3d(out[0] * out[3], out[1] * out[3], out[2] * out[3]);
}
Matrix.translateM(mModelMatrix, 0, this.diffX, this.diffY, 0); //i use this to move the model matrix based on pinch zooming stuff.
Any help would be greatly appreciated! Thanks.
I wonder which algorithm you have implemented. Is it a ray casting approach to the problem?
I didn't focus much on the code itself but this looks a way too simple implementation to be a fully operational ray casting solution.
In my humble experience, i would like to suggest you, depending on the complexity of your final project (which I don't know), to adopt a color picking solution.
This solution is usually the most flexible and the easiest to be implemented.
It consist in the rendering of the objects in your scene with unique flat colors (usually you disable lighting as well in your shaders) to a backbuffer...a texture, then you acquire the coordinates of the click (touch) and you read the color of the pixel in that specific coordinates.
Having the color of the pixel and the tables of the colors of the different objects you rendered, makes possible for you to understand what the user clicked from a logical perspective.
There are other approaches to the object picking problem, this is probably universally recognized as the fastest one.
Cheers
Maurizio