Game mostly drawn in Shapes

Anything libgdx related goes here!

Game mostly drawn in Shapes

Postby LosTChG » Thu Nov 01, 2012 2:26 pm

Hi!
I began Java game programming as a hobby a year ago or so, and I managed to get comfortable with active rendering game loops and Java2D in JPanels, but I wanted to implement a physics engine to the games, so I searched on google and found this library, which also has all I want :).

However, my games were made with shapes, mainly quad curves with one control point which the user can modify in real-time. As far as I find, libgdx seems to dislike using shapes, and ShapeRenderer lacks of some functions, such as modifying the line stroke or filling options.

Should I keep trying to focus and understand this library and OpenGL given that I focus on 2D shapes? If so, how can I adapt my rendering to libgdx? Is efficient?

Thanks for all!
LosTChG
 
Posts: 7
Joined: Thu Nov 01, 2012 1:57 pm

Re: Game mostly drawn in Shapes

Postby dimecoin » Thu Nov 01, 2012 7:44 pm

For shapes I read that it's best to use pixmap. I've never tried it, but the post said this was a more efficient way to do it.
dimecoin
 
Posts: 334
Joined: Wed Oct 31, 2012 5:33 am

Re: Game mostly drawn in Shapes

Postby LosTChG » Thu Nov 01, 2012 8:34 pm

Uhmmm, there is no current support for quads in Pixmap, and I can't imagine how can I modify the line thickness...
Uff, I'm so lost I give up
LosTChG
 
Posts: 7
Joined: Thu Nov 01, 2012 1:57 pm

Re: Game mostly drawn in Shapes

Postby BurningHand » Thu Nov 01, 2012 9:04 pm

It sounds like you might be better off using Canvas (Android's version of Java's Graphics [for the most part]) at this point. I suppose you could probably accomplish what you wish using shaders (via OpenGL ES 2.0) but if you don't already know how to do shaders, it might be pretty overwhelming. I haven't much of a clue about shaders so I can't offer much help, unfortunately.

Edit: You could also build out everything using meshes, but again, it's probably overwhelming to do such a thing.
IRC: nexsoftware / mobidevelop; GitHub: MobiDevelop;
BurningHand
 
Posts: 2812
Joined: Mon Oct 25, 2010 4:35 am

Re: Game mostly drawn in Shapes

Postby davedes » Fri Nov 02, 2012 2:17 am

If you can tap into Android "Canvas" that would be ideal. On desktop, you can easily use Java2D as your rasterizer and upload the pixel data to a GL texture.

Also be sure to search the web for 2D OpenGL/Android/LibGDX frameworks as somebody may have already done all of this for you.

Rendering dynamic, complex and reliably smoothed 2D lines/shapes in OpenGL is not a trivial task, especially if you want to include glows, outlines, dotted lines, etc. Here's a writeup of various ideas and tips...

Filled Polygons: LibGDX includes some utilities for rendering polygons. PolygonSpriteBatch is a good starting point. This is mainly useful for drawing filled and unsmoothed polygons. For reliable anti-aliasing across platforms, FXAA may be a simple and suitable choice.

Adding curves: Look into Bezier curves to generate points which you then let LibGDX triangulate and render for you.

Outlining Curves + Polygons: If you just need to draw lines/outlines (e.g. a curved 2D line), the simplest solution is to render a Mesh with GL_LINES. You can adjust line thickness with glLineWidth.

Note: line thickness and anti-aliasing may be slightly different or unpredictable across platforms. Again, FXAA may solve the anti-aliasing, but you may still be limited in terms of line thickness. A more complex solution is to generate a mesh that represents the line itself, which lets you control the line thickness: see here. This post describes how it may be done in a geometry shader (for desktop), but you could theoretically do the same thing on the CPU.

It's also possible to render a bezier curve on the GPU, which would allow for resolution independence, smooth anti-aliasing, modifiable line thickness, etc: see here. (Slimmer examples here and here.)

And of course, you can always create your own software rasterizer with Pixmap. To save yourself some time, you can do the anti-aliasing after the fact in a single full-screen pass (e.g. FXAA).

--

Various other tips/techniques for primitive shape rendering:

Straight lines and rectangles: Use a 1x1 white sprite that is resized and coloured as desired.

Angled lines with varying width and decent anti-aliasing: (A) Use a technique like this or (B) use the above 1x1 sprite technique, but render it to a FBO texture with GL_LINEAR filtering; then draw the FBO texture to screen, rotated.

Dynamic circles, arcs, hearts, etc: You can use GL_LINES, GL_TRIANGLES or GL_TRIANGLE_FAN, but smoothing, line thickness, etc will be unpredictable, and effects will be difficult to add. For these things you may be better off using shaders, especially if they need to be dynamic and crisply anti-aliased (or if you need special effects, outlines, etc). You simply push a quad to the GPU and the shader creates the shape. You could use SpriteBatch or create your own more optimized batcher. See here for a simple circle, and here for an outline effect. (You can edit the values to see changes in real-time; I'd suggest using 1x resolution from the drop box in the top left.)
davedes
 
Posts: 434
Joined: Thu Oct 11, 2012 7:51 pm

Re: Game mostly drawn in Shapes

Postby LosTChG » Fri Nov 02, 2012 11:25 am

WOW, thanks for all the info!
The Codeproject website has amazing articles, and the rest of the info is really fine!
However, I might be going too deeper in programming to be just an amateur... too much info... is overwhelming, hehe.
So I might keep using Java2D for what I want.
Anyway, and again, thanks for all! The community using and developing Libgdx is sure kind and active :)
LosTChG
 
Posts: 7
Joined: Thu Nov 01, 2012 1:57 pm

Re: Game mostly drawn in Shapes

Postby davedes » Fri Nov 02, 2012 4:02 pm

If you're considering Java2D (i.e. desktop only), perhaps you should just stick with LibGDX (better performance, lots of goodies, good gamedev community, etc) and use Java2D as your 2D rasterizer. Below is an example of how to render Java2D shapes within LibGDX; of course this only works on desktop.

Image


For better performance you would minimize the number of times you upload data to GL; e.g. accumulating it into the same glTexSubImage2D call. Also, you could modify the below code to upload to a texture atlas, rather than having a single large texture per shape.

Code: Select all
package lighting;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.List;

import org.lwjgl.opengl.GL12;

import com.badlogic.gdx.Application.ApplicationType;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.GL11;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.utils.BufferUtils;
import com.badlogic.gdx.utils.GdxRuntimeException;

public class Lighting implements ApplicationListener {
   
   public static void main(String[] args) {
      LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration();
      cfg.title = "lighting_tutorial";
      cfg.useGL20 = false;
      cfg.width = 480;
      cfg.height = 320;
      
      new LwjglApplication(new Lighting(), cfg);
   }
   
   private OrthographicCamera camera;
   private SpriteBatch batch;
   private Java2DTexture j2dTex;
   private TextureRegion sprite;
   

   private List<java.awt.Shape> shapes = new ArrayList<java.awt.Shape>();
   private int pointer = 0;
   
   @Override
   public void create() {
      if (Gdx.app.getType()!=ApplicationType.Desktop)
         throw new GdxRuntimeException("this demo only works on desktop with Java2D");
      
      float w = Gdx.graphics.getWidth();
      float h = Gdx.graphics.getHeight();
      
      camera = new OrthographicCamera(w, h);
      camera.setToOrtho(false);
      batch = new SpriteBatch();
      
      // 1. -- create our Java2D buffer; must be big enough to hold our shape!
      j2dTex = new Java2DTexture(1024, 1024);
      
      // 2. -- setup our texture region for drawing part of our buffer
      sprite = new TextureRegion(j2dTex);
      
      // 3. --- Setup a few shapes for example
      GeneralPath path = new GeneralPath();
      path.moveTo(50, 120);
      path.lineTo(70, 180);
      path.lineTo(20, 140);
      path.lineTo(80, 140);
      path.lineTo(30, 180);
      path.closePath();
      shapes.add(path);

      path = new GeneralPath();
      path.moveTo(120, 180);
      path.quadTo(150, 120, 180, 180);
      path.closePath();
      shapes.add(path);

      path = new GeneralPath();
      path.moveTo(220, 150);
      path.curveTo(240, 130, 280, 160, 300, 140);
      path.lineTo(300, 180);
      path.quadTo(260, 160, 220, 180);
      path.closePath();
      shapes.add(path);

      path = new GeneralPath();
      path.moveTo(360, 100);
      path.lineTo(360, 200);
      path.lineTo(400, 140);
      path.lineTo(320, 120);
      path.lineTo(400, 180);
      path.lineTo(320, 180);
      path.closePath();
      shapes.add(path);
      
      // 4. show a shape..
      show(shapes.get(pointer));
   }

   void show(java.awt.Shape shape) {
      Graphics2D g2d = j2dTex.begin();
      
      //do whatever want here, e.g. solid fill, strokes, gradient fill
      g2d.setColor(Color.lightGray);
      g2d.fill(shape);
      
      //draw a dashed stroke...
      g2d.setColor(Color.red);
      g2d.setStroke(new BasicStroke(2, BasicStroke.CAP_ROUND,
                              BasicStroke.JOIN_ROUND,
                              10, new float[] { 4, 4 }, 0));
      g2d.draw(shape);
      
      //upload data to GL
      j2dTex.end();
      
      //don't forget to set our texture region up..
      //we use + 1 since Java2D draws outlines on the OUTSIDE
      Rectangle bounds = shape.getBounds();
      sprite.setRegion(bounds.x, bounds.y, bounds.width+1, bounds.height+1);
   }
   
   @Override
   public void dispose() {
      batch.dispose();
      j2dTex.dispose();
   }
   
   
   @Override
   public void render() {      
      Gdx.gl.glClearColor(0, 0, 0, 0);
      Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
      
      //show next shape
      if (Gdx.input.isTouched()) {
         pointer++;
         if (pointer>=shapes.size())
            pointer = 0;
         show(shapes.get(pointer));
      }
      
      batch.setProjectionMatrix(camera.combined);
      batch.begin();
      batch.draw(sprite, 10, 10);
      batch.end();
   }

   @Override
   public void resize(int width, int height) {
      camera.setToOrtho(false, width, height);
   }

   @Override
   public void pause() {
   }

   @Override
   public void resume() {
   }
   

   
   public static class Java2DTexture extends Texture {
      
      protected BufferedImage bufferImg;
      protected IntBuffer buffer;
      private final Color BACKGROUND = new Color(0, 0, 0, 0);
      
      public Java2DTexture(int width, int height, Format format) {
         super(width, height, format);
         bufferImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
         buffer = BufferUtils.newIntBuffer(width * height);
      }
      
      public Java2DTexture(int width, int height) {
         this(width, height, Format.RGBA8888);
      }
      
      public Java2DTexture() {
         this(1024, 1024);
      }

      public BufferedImage getBufferedImage() {
         return bufferImg;
      }
      
      public Graphics2D begin() {
         //you could probably cache this instead of requesting it every time
         Graphics2D g2d = (Graphics2D) bufferImg.getGraphics();
         g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
               RenderingHints.VALUE_ANTIALIAS_ON);
         g2d.setBackground(BACKGROUND);
         g2d.clearRect(0, 0, bufferImg.getWidth(), bufferImg.getHeight());
         g2d.setColor(java.awt.Color.white);
         return g2d;
      }
      
      public void end() {
         // now we pass the BufferedImage pixel data to the LibGDX texture...
         int width = bufferImg.getWidth();
         int height = bufferImg.getHeight();
         //you could probably cache this rather than requesting it every upload
         int[] pixels = ((DataBufferInt)bufferImg.getRaster().getDataBuffer())
               .getData();
         this.bind();
         buffer.rewind();
         buffer.put(pixels);
         buffer.flip();
         Gdx.gl.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, width, height,
               GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
      }
      
   }
}
davedes
 
Posts: 434
Joined: Thu Oct 11, 2012 7:51 pm

Re: Game mostly drawn in Shapes

Postby LosTChG » Thu Nov 22, 2012 12:33 am

Neat, I think Java2d as the rasterizer is the solution.
However, I've read in other of your posts in slick2d forums about this issue that modifying the shape every frame could lower the performance significantly... That means I should only render static shapes? Because if its that so I could instead use a plain image
Nevermind, I will try to stress-test this later and tell you what resolves

Thank you very much for the idea :D
LosTChG
 
Posts: 7
Joined: Thu Nov 01, 2012 1:57 pm

Re: Game mostly drawn in Shapes

Postby NateS » Thu Nov 22, 2012 1:20 am

See ShapeRenderer for circles, rects, thin lines and bezier curves, etc. Making a bezier curve thick is easy if your line is opaque. See the alternate line renderer in Slick. If translucent the triangles that make up the line can't overlap, and this is very hard, especially for thick curves that bend sharply.
NateS
 
Posts: 1980
Joined: Fri Nov 12, 2010 11:08 am

Re: Game mostly drawn in Shapes

Postby davedes » Thu Nov 22, 2012 6:39 am

LosTChG wrote:Neat, I think Java2d as the rasterizer is the solution.
However, I've read in other of your posts in slick2d forums about this issue that modifying the shape every frame could lower the performance significantly... That means I should only render static shapes? Because if its that so I could instead use a plain image
Nevermind, I will try to stress-test this later and tell you what resolves

Thank you very much for the idea :D

Uploading small textures to the GPU should be very fast on desktop, probably nothing to worry about as long as you aren't going crazy with it. On OpenGL ES (Android, WebGL, iOS) I have no idea how well it will perform...
davedes
 
Posts: 434
Joined: Thu Oct 11, 2012 7:51 pm

Next

Return to Libgdx

Who is online

Users browsing this forum: No registered users and 1 guest