Hex Tile based games in JME

Hi,



I had a similar problem for my own game (except it was not set in space but on a terrain).

I finally settled for this solution :

  • I don't display the hex tiles on the terrain (which might not be OK for you, but you can use a tiled map on a quad as you suggested)
  • I detect hexagon picks (getting the world coordinates is well documented on this forum, to get the corresponding hex coordinates it depends on your coordinates system but it's just a little math)
  • I adapted the TerrainMarker class from MonkeyWorld project to display an hexagon over my terrain (easier for you since you "terrain" is flat)

    It works perfectly even on big terrains.



    Hope it helps :wink:

Thanks for the quick response.  I'll take a look into the MonkeyWorld code (unless you'd be so kind as to send or post the modifications you've done) and see what I can do.



Just to make sure I understand this, your solution is to tile-map a hex texture across a quad, determine hex picks from world space, and when it's required to highlight or otherwise designate a given hex to actually "overlay" a real Hexagon primitive (or several, if need be) mapped with whatever texture is required, at the appropriate location on the quad?



Did I get that right?  If so, then I can probably salvage much of the math I had for the 2D versions and reuse it here.



I do have on question, if I want to tile the quad with hex textures, and hexes don't neatly line up in nice square rows, how would I go about doing it?  Does my hex texture need to be like a "hex-and-a-half" and then I'd throw on an "end-cap" texture to fill out the edges of the quad to make it look correct?  Or am I complicating this and the proper offsetting can be handled somehow with texture coords?

Yes that's exactly how I'd do it in your case.



TerrainMarker (from MonkeyWorld3D) is a circular marker that is "pressed" onto the terrain. I modified it so that it's an hex marker, but in your case you probably just need an Hexagon primitive since your terrain is flat. If you're still interested, the source is here :

http://sanctuaire.svn.sourceforge.net/viewvc/sanctuaire/sanctuaire-client/trunk/sanctuaire-plugin/src/org/sanctuaire/rcpclient/opengl/TerrainMarker.java?revision=HEAD&view=markup



Here's what it looks like (the angle of vision is not optimal on this screenshot…) :

http://sanctuaireone.free.fr/screens/06jan-trooper.jpg



As for your alignment problem, I guess you could hide it with your overlayed Hexagon. I don't use a texture on my marker, just a bright red border with alpha set to 0.5 or something.

must say, thats a lovely bit of coding by ndebruyn

Which reminds me I should probably change some comments to reflect the small changes I made…



Edit: updated :slight_smile:

Wow that's amazing!



Any chance any of this could be release open source? This is something I may be interested in.



Thanks

What is ?

If you mean either MonkeyWorld3D or my project (Sanctuaire),  both are open source :slight_smile: Or maybe you mean chirstius's project ?

locking the meshes should give you quite a performance gain, not sure you can do it in your game (as things are locked then :))

Maybe we should reimplement Hexagon with 4 instead of 6 triangles. Would throw out 1/3 of the triangles.



Here is a (very poor) sketch:




Implementing all of the primitives with as few vertices/triangles as possible seems like an easy way to improve jME performance and efficiency across the board - so long as it doesn't mess with the actual shape.


Well, this is only true up to certain point. As long as the primitives are completely visible, you're right. If the primitive is partially out of the screen, having more triangles can speed things up, as it is easier to ignore off-screen triangles than calculating huge partially off-screen triangles (I already tried it: Having a "floor" with medium size quads was better than one giant quad). Further, the shape of the triangles matters. A few more triangles with about 60

The class has a different name, so you can better test if there is a performance win. Please let us know if it works…



package com.jme.scene.shape;

import java.io.IOException;

import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.TriMesh;
import com.jme.scene.batch.TriangleBatch;
import com.jme.util.export.InputCapsule;
import com.jme.util.export.JMEExporter;
import com.jme.util.export.JMEImporter;
import com.jme.util.export.OutputCapsule;
import com.jme.util.geom.BufferUtils;

/**
 * <code>Hexagon4</code> provides an extension of <code>TriMesh</code>. A
 * <code>Hexagon4</code> provides a regular hexagon with each triangle having
 * side length that is given in the constructor.
 *
 * This is an alternative implementation of <code>Hexagon</code>,
 * using 4 instead of 6 triangles.
 *
 * @author Joel Schuster
 * @author Daniel Gronau
 */
public class Hexagon4 extends TriMesh {
   private static final long serialVersionUID = 1L;

   private static final int NUM_POINTS = 6;

   private static final int NUM_TRIS = 4;

   private float sideLength;

   /**
    * Hexagon Constructor instantiates a new Hexagon. This element is center on
    * 0,0,0 with all normals pointing up. The user must move and rotate for
    * positioning.
    *
    * @param name
    *            the name of the scene element. This is required for
    *            identification and comparision purposes.
    * @param sideLength
    *            The length of all the sides of the tiangles
    */
   public Hexagon4(String name, float sideLength) {
      super(name);
      this.sideLength = sideLength;
        TriangleBatch batch = getBatch(0);

      // allocate vertices
      batch.setVertexCount(NUM_POINTS);
      batch.setVertexBuffer(BufferUtils.createVector3Buffer(batch.getVertexCount()));
      batch.setNormalBuffer(BufferUtils.createVector3Buffer(batch.getVertexCount()));
      batch.getTextureBuffers().set(0, BufferUtils.createVector2Buffer(batch.getVertexCount()));
      
      batch.setTriangleQuantity(NUM_TRIS);
      batch.setIndexBuffer(BufferUtils.createIntBuffer(3 * batch.getTriangleCount()));

      setVertexData();
      setIndexData();
      setTextureData();
      setNormalData();
       setDefaultColor(ColorRGBA.white);

   }

   /**
    * Vertexes are set up like this: 0__1 / / 5/__6/__2 / / /___ /
    * 4 3
    *
    * All lines on this diagram are sideLength long. Therefore, the width of
    * the hexagon is sideLength * 2, and the height is 2 * the height of one
    * equalateral triangle with all side = sideLength which is .866
    * 
    */
   private void setVertexData() {
        TriangleBatch batch = getBatch(0);
       batch.getVertexBuffer().put(-(sideLength / 2)).put(sideLength * 0.866f).put(0.0f);
       batch.getVertexBuffer().put(sideLength / 2).put(sideLength * 0.866f).put(0.0f);
       batch.getVertexBuffer().put(sideLength).put(0.0f).put(0.0f);
       batch.getVertexBuffer().put(sideLength / 2).put(-sideLength * 0.866f).put(0.0f);
       batch.getVertexBuffer().put(-(sideLength / 2)).put(-sideLength * 0.866f).put(0.0f);
       batch.getVertexBuffer().put(-sideLength).put(0.0f).put(0.0f);
   }

   /**
    * Sets up the indexes of the mesh. These go in a clockwise fassion and thus
    * only the 'up' side of the hex is visible. If you wish to have to either
    * set two sided lighting or create two hexes back-to-back
    * 
    */

   private void setIndexData() {
        TriangleBatch batch = getBatch(0);
      batch.getIndexBuffer().rewind();
      // tri 1
      batch.getIndexBuffer().put(0);
      batch.getIndexBuffer().put(1);
      batch.getIndexBuffer().put(2);
      // tri 2
      batch.getIndexBuffer().put(2);
      batch.getIndexBuffer().put(3);
      batch.getIndexBuffer().put(4);
      // tri 3
      batch.getIndexBuffer().put(4);
      batch.getIndexBuffer().put(5);
      batch.getIndexBuffer().put(0);
      // tri 4
      batch.getIndexBuffer().put(0);
      batch.getIndexBuffer().put(2);
      batch.getIndexBuffer().put(4);
   }

   private void setTextureData() {
        TriangleBatch batch = getBatch(0);
        batch.getTextureBuffers().get(0).put(0.25f).put(0);
        batch.getTextureBuffers().get(0).put(0.75f).put(0);
        batch.getTextureBuffers().get(0).put(1.0f).put(0.5f);
        batch.getTextureBuffers().get(0).put(0.75f).put(1.0f);
        batch.getTextureBuffers().get(0).put(0.25f).put(1.0f);
        batch.getTextureBuffers().get(0).put(0.0f).put(0.5f);
   }

   /**
    * Sets all the default vertex normals to 'up', +1 in the Z direction.
    * 
    */
   private void setNormalData() {
        TriangleBatch batch = getBatch(0);
       Vector3f zAxis = new Vector3f(0, 0, 1);
      for (int i = 0; i < NUM_POINTS; i++)
          BufferUtils.setInBuffer(zAxis, batch.getNormalBuffer(), i);
   }
   
    public void write(JMEExporter e) throws IOException {
        super.write(e);
        OutputCapsule capsule = e.getCapsule(this);
        capsule.write(sideLength, "sideLength", 0);
       
    }

    public void read(JMEImporter e) throws IOException {
        super.read(e);
        InputCapsule capsule = e.getCapsule(this);
        sideLength = capsule.readInt("sideLength", 0);
       
    }

}

Landei,



When you say it helps with the rendering quality does that apply even when textured?  Just curious how that works exactly.



I'm at work now, I will try the updated Hexagon class when I get home tonight.  Thanks for throwing it together!



Lastly, can I please get a yes/no answer on the SharedMesh thing?  Does my implementation appear to be correct?

  • As far as I know, a good triangulation is important especially when textured


  • For me the SharedMesh implementation looks good (but that doesn't mean so much)


  • Sorry, but my tests seem to suggest that both Hexagons have about the same speed (the origininal one was even slightly faster). This seems odd to me, maybe some of the more experienced people could have a look (or give a good explanation).



    Looks like the only reason for using my implementation could be less memory :smiley:
Landei said:

- As far as I know, a good triangulation is important *especially* when textured


Ok, that was kinda what I was afraid of.

A couple less triangles per mesh sent to the card usually means a lot less than the number of meshes sent to the card - especially when you are talking about really small (in terms of verts/tris) meshes.  Faster/slower will mostly depend on circumstances of the test at that point - things like fill rate, what else is happening on your machine in the background, and such - not the two less/more triangles you have per hexagon.

renanse said:

A couple less triangles per mesh sent to the card usually means a lot less than the number of meshes sent to the card - especially when you are talking about really small (in terms of verts/tris) meshes. Faster/slower will mostly depend on circumstances of the test at that point - things like fill rate, what else is happening on your machine in the background, and such - not the two less/more triangles you have per hexagon.


is this the same with shared meshes ( im under the illusion that the gpu is reusing the mesh

I was under the same impression theprism.



Renanse-



I have to think dual X1900XT video cards have a pretty decent fill rate.  Plenty enough to render a roughly 40x50 grid of hexes - textured or not.  Does the fact that I have them in the Transparent Queue mean anything?  Could that be slowing things down?

@Renanse:



Thanks for the hints.



What I still don't understand: How can reducing the number of triangles by 1/3 make rendering (slightly) slower? I tested different configs, and always the 4 triangle hexagon was about 0.5 FPS slower…



If I understand you right, then constructing the board as one giant mesh with the hexagons as triangle batches should be much faster? If yes, shouldn't we have some kind of Board class for hexagons and rectangles in JME, which has some support regarding access of single hexagons/rectangles (or did I overlooked it)?



BTW: I would suggest to put that things in the Wiki, as every neebie will first try things like the ones we did. No long explanations, just some tipps and remarks how to avoid the "usual" performance bottlenecks and traps.



@chirstius:



I can't tell much about the transparent queue. I had some problems with transparency, which I solved by turning off two pass transparency (I've to look in my code what the exact call was). You could also try this. If I understand it right, you loose some transparency rendering quality (but for me it was perfectly OK), and gain some speed… don't know how much

I didn't get much time to play with this last night since we're in the middle of a snowstorm in the mid-west and I was shoveling all night trying to stay ahead of it.  But I did run the test with the Hexagon4 and got similar results - no real improvment in performance.  I can say that the textures mapped totally fine though, so no concerns about that.



One strange thing - I grabbed the nightly build (previously I was using the current stable release) and imported it.  When I ran the tests under that build the performance was about half of what it was previously.  It hovered around 25-35 fps vs the 50-65fps I had been seeing.  It didn't seem to matter what I tried after that, it was always slower.



I realize at this point that using "raw" hexagons is probably NOT the way to implement this gameboard.  But from a purely academic standpoint I am still very interested in what exactly is going on here - why this performance seems so flaky.


Landei said:

If I understand you right, then constructing the board as one giant mesh with the hexagons as triangle batches should be much faster? If yes, shouldn't we have some kind of Board class for hexagons and rectangles in JME, which has some support regarding access of single hexagons/rectangles (or did I overlooked it)?

BTW: I would suggest to put that things in the Wiki, as every neebie will first try things like the ones we did. No long explanations, just some tipps and remarks how to avoid the "usual" performance bottlenecks and traps.


I think that Board class idea sounds great.  As I am probably on the road to abandoning the hexagon method of doing this (I still plan to play with it a bit more) I would be interested in helping develop/test this if possible.  It seems like several people have already done some work to implement things like this - like The Librarian for instance - so maybe the best of that work can be brought together and added as an official jME feature.  Again, I am willing to help in this effort.

Also, regarding the Wiki, if I can help there please let me know.  The jME community seems incredibly helpful and supportive so I'm happy to continue that and help in any way possible.

It looks like we're in for a real doozy of a storm today so I might not be able to muck with this much tonight, but I'll keep plugging away at it and if I discover anything interesting I will let you know.
theprism said:

renanse said:

A couple less triangles per mesh sent to the card usually means a lot less than the number of meshes sent to the card - especially when you are talking about really small (in terms of verts/tris) meshes. Faster/slower will mostly depend on circumstances of the test at that point - things like fill rate, what else is happening on your machine in the background, and such - not the two less/more triangles you have per hexagon.


is this the same with shared meshes ( im under the illusion that the gpu is reusing the mesh


That's a false impression unless you are locking the target mesh as well.