Attach thousands of childs to a node and optimize fps

Hello everybody,

I’m using JME for a few months now, and I must say I’m impressed by the engine and its capabilities.

Currently I am assigned to make a “3D Game of Life”.

For the ones who don’t know what it means:

  • Make a cube with cells (most GoL projects contain boxes or spheres)
  • In this cube there have to be for example 8x8x8 cells
  • These cells can be dead or alive
  • If the cell is alive
    • If the cell has les than two neighbours it’ll die (loneliness)
    • if the cell has more than eight neighbours it’ll die (overcrowded)
  • If the cell is dead
    • If the cell has exactly 5 neighbours it’ll live again (reproduction)

Here is a picture of the project so far:

My problem is that i want to attach a whole lot of cells (40x40x40 = 64.000) to the cube node.

I’ve recently changed the boxes to custom shaped pyramids which decrease the amount of triangles by 3x
Also i make 1 shape and clone it instead of reproducing it.
I Also use wireframes in stead of filling the cells.
I Only change the material of the cell in stead of using two dead/alive cells.

My fps still drops enormously (6fps in stead of 100fps).

My questions are:

  • Is there a way to attach childs more rapidly?
  • How can i increase the fps?

Here’s the code i use to create the cube:
[java]

package mygame;

import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.VertexBuffer;
import com.jme3.util.BufferUtils;

/**
*

  • @author Gebruiker
    */
    public class Cube {

    private Node cube;
    private Geometry[][][] dot;
    private Materials materials;
    private int d;

    public Cube()
    {
    d = CommonData.getDimension();
    dot = new Geometry[d][d][d];
    materials = new Materials();
    cube = new Node(“cube”);

     Geometry mainDot = new Geometry("dot", singleDot());
     mainDot.setMaterial(materials.Off());
     
     for(int a = 0; a < d; a++)
     {
         for(int b = 0; b < d; b++)
         {
             for(int c = 0; c < d; c++)
             {
                 dot[a][b][c] = mainDot.clone();
                 dot[a][b][c].setLocalTranslation(a,b,c);
                 
                 cube.attachChild(dot[a][b][c]);
             }
         }
     }
     CommonData.getRootNode().attachChild(cube);
    

    }

    private Mesh singleDot()
    {
    Mesh pyramid = new Mesh();
    Vector3f [] vertices = new Vector3f[4];
    int [] indexes = new int[12];

     vertices[0] = new Vector3f(0,0,0);
     vertices[1] = new Vector3f(0,0.1f,0);
     vertices[2] = new Vector3f(0.1f,0,0);
     vertices[3] = new Vector3f(0,0,0.1f);
     
     indexes[0] = 0;
     indexes[1] = 1;
     indexes[2] = 2;
     
     indexes[3] = 1;
     indexes[4] = 2;
     indexes[5] = 3;
     
     indexes[6] = 1;
     indexes[7] = 0;
     indexes[8] = 3;
     
     indexes[9] = 0;
     indexes[10] = 2;
     indexes[11] = 3;
     
     // Setting buffers
     pyramid.setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(vertices));
     pyramid.setBuffer(VertexBuffer.Type.Index, 1, BufferUtils.createIntBuffer(indexes));
     pyramid.updateBound();
     
     return pyramid;
     
     /*Box s = new Box(0.1f,0.1f,0.1f);
     Geometry geom = new Geometry("Ball",s);
     geom.setMaterial(materials.Off());
     //geom.setQueueBucket(RenderQueue.Bucket.Translucent);
     //geom.setQueueBucket(RenderQueue.Bucket.Transparent); 
     
     return geom;*/
    

    }

    public void updateCube(int a, int b, int c, Boolean s)
    {
    if(s)
    dot[a][b][c].setMaterial(materials.On());
    else
    dot[a][b][c].setMaterial(materials.Off());
    }

    // not used
    public Geometry[][][] getAll()
    {
    return dot;
    }

    // not used (detaching cube node instead) when changing dimension in-game
    public void detachChildren()
    {
    cube.detachAllChildren();
    }
    }
    [/java]

Thanks in advance (-:

i think that attach a lot of geometry will always result in a slow rendering. I am maybe wrong, but according to me (and myself approve) you should consider using the mesh directly. You can build your own mesh and it could be a good idea to build it once and for all with a texture map and update texture coordinates to change a cell state.

Also, if you want to run your simulation and see the result after X steps, you don’t need to render all the steps, only the last one (or one times to times).

You can try to see if a custom mesh will solve your problem by using (for TEST !) the GeometryBatchFactory. What i mean is : this will create a single geometry that “gather” all the geometries in your node (if they have the same material, if i remember well) so you can give a try and see how the fps change. But you cannot use it every tick of your simulation because it will recreate an entire mesh each time, even if only the color of your cell changed.

I am tired and english is not my native language, sorry ^^

EDIT : also, i found this article some time ago (years ago) and i don’t know what it became since that.
http://www.jeuxvideo.fr/euclideon-technologie-100-000-meilleure-systemes-actuels-actu-438644.html

It’s a bit related to your work, but it basically say what you fear : with our current technology, it’s pretty hard to render a lot of vertex, and it’s why a we use LOD (level of details).

Btw, the “lod” thing could also help you, but i don’t exactly know how you could do it fast enough. I really don’t know for that.

EDIT 2 : the article in the link is in french, but the video is in english ^^

Thank you for the fast reply, I think i should have given the ‘state’-class too (look below).

In this class i have also a 3-dimensional array which holds true or false (according to the state of the cell)

The feature of this class is that I can check every state before I apply it to the cube.
This way I am able to change the cube only once per cycle as you describe.

I gave the GeometryBatchFactory a try and my frame rate is back to 100fps which is ideal.
But as you mentioned, it creates a single geometry so the entire state of the cube changes instead of the ones that need to be changed.

When i look away with the camera I get also 100fps, therefor I now know that it’s the rendering which slows it down to 6fps.

My only question about your reply is, what do you mean by " build it once and for all with a texture map and update texture coordinates to change a cell state".
Because I think Í’m not getting it or I’m already doing that :-?

state-class:
[java]package mygame;

import java.util.Random;

/**
*

  • @author Henk Reitsma
    */
    public class State {

    private Boolean[][][] stateList;
    private Random random = new Random();
    private int d;
    private int e;
    private int max;

    public State()
    {
    this.d = CommonData.getDimension();
    this.e = 39;
    this.max = CommonData.getMaxIndex();
    this.stateList = new Boolean[d][d][d];

     setDefaultState();
    

    }

    // not used
    public Boolean[][][] getAll()
    {
    return stateList;
    }

    public void setDefaultState()
    {
    int cellA;
    int cellB;
    int cellC;

     // set all cells to false
     for(int a = 0; a < d; a++) {
         for(int b = 0; b < d; b++) {
             for(int c = 0; c < d; c++) {
                 setState(a, b, c, false);
             }
         }
     }
     
     // take a random cell
     int a = random.nextInt(e);
     int b = random.nextInt(e);
     int c = random.nextInt(e);
     
     // make the selected cell (and all it's neighbours) alive as a startingpoint
     for(int i = -1; i <= 1; i++) {
         for(int j = -1; j <= 1; j++) {
             for(int k = -1; k <= 1; k++) {
                 
                 cellA = a+i < 0 ? e : a+i > e ? 0 : a+i;
                 cellB = b+j < 0 ? e : b+j > e ? 0 : b+j;
                 cellC = c+k < 0 ? e : c+k > e ? 0 : c+k;
    
                 setState(cellA, cellB, cellC, true);
             }
         }
     }
    

    }

    public int countNeighbours(int a, int b, int c)
    {
    int count = 0;

     int cellA;
     int cellB;
     int cellC;
     
     // loop through all the neighbours of the cell
     for(int i = -1; i <= 1; i++) {
         for(int j = -1; j <= 1; j++) {
             for(int k = -1; k <= 1; k++) {
    
                 // if there is no neighbour use the opposite side of the cube as neighbour
                 cellA = a+i < 0 ? e : a+i > e ? 0 : a+i;
                 cellB = b+j < 0 ? e : b+j > e ? 0 : b+j;
                 cellC = c+k < 0 ? e : c+k > e ? 0 : c+k;
                 
                 // count the neighbours
                 count += getState(cellA, cellB, cellC) ? 1 : 0;
             }
         }
     }
     
     return count;
    

    }

    public Boolean getState(int a, int b, int c)
    {
    return stateList[a][b][c];
    }

    public void setState(int a, int b, int c, Boolean n)
    {
    stateList[a][b][c] = n;

     CommonData.updateAliveCells(n ? 1 : -1);
     CommonData.updateDeadCells(n ? -1 : 1);
    

    }

    public void rewind()
    {
    // switch the whole cube a few times before resetting the defaultstate (just for fun)
    for(int a = 0; a < d; a++) {
    for(int b = 0; b < d; b++) {
    for(int c = 0; c < d; c++) {
    if(getState(a,b,c))
    setState(a,b,c,false);
    else
    setState(a,b,c,true);
    }
    }
    }
    }
    }
    [/java]

You have to use some kind of batching in your scene.
Look for BatchNode, GeometryBatchFactory, and this https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:intermediate:optimization

1 Like

Ok, no, you are not doing that. Believe me, create you own mesh can be hell, but once it’s done you are really happy.

The best thing i can do is give you the link to the tutorial about this: https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:advanced:custom_meshes

But, be carefull, it’s not something easy.
To give a short description : create your own mesh means that you have to calculate all the position of the vertex and store these positions in a … hmmm … big array. This is also true for the position of the texture, the normals and a lot of other things.

Once it’s done, you have your mesh. After that, you can edit the texture by jumping into the array in the right place and modify some data. For exemple, if you have a picture of 32x64, half green half red (32x32 green, 32x32 red) you can assign values to use the left part of the texture when the cell is dead and use the right part when the cell is alive.

But mesh generation is a bit like fractals generation : if you do a little mistake, you’ll have it everywhere, but once everything is ok you can apply it as much as you want.

but, really, look the tutorial, it’s the best thing to do ^^.

I think what bubche is getting at is you’re going to have to dive into some custom shaders to update the display of a particular cell.

Modern GPUs can render millions of polygons at 60fps, but only if they are batched in a intelligent way. Asking the GPU to handle 64K individual batches is asking to much.

One approach would to keep all your geometry in one batch (mesh) and use a custom shader to control the display of individual vertexes. These are the kind of problems shaders were designed to do.

Bubche’s suggestion is to pass a texture into a custom shader. The texture has to be large enough to have a pixel for every cell in your simulation. So if your simulation is 40x40x40… then you are talking a texture that is 40x1600. Your state code updates the texture before each frame and changes the color depending on the cell state (maybe black for dead, white for alive). The texture is then passed to the shader.

The shader takes over. It knows what vertex it’s currently working with, looks up the corresponding value in the texture and changes the color of the vertex accordingly.

Unless you actually need triangles, I’d probably construct my mesh out of single vertexes for each cell and use some form of point rendering. This keep the math simple. Again you can accomplish this by using shaders.

@bubuche

I’m sorry, but i really think I’m already making a custom Mesh when you look at the code (below) for the pyramid I create (instead of a box).
I’ve updated the code a little so it doesn’t look retarted (-:

I have 2 arrays, one for the coördinates and one for the lines between them to make the triangles.
So i don’t really see the problem here?

[java]private Mesh singleDot()
{
Mesh pyramid = new Mesh();
Vector3f [] vertices = new Vector3f[4];
int [] indexes = new int[]{0,1,2,1,2,3,1,0,3,0,2,3};

    vertices[0] = new Vector3f(0,0,0);
    vertices[1] = new Vector3f(0,0.1f,0);
    vertices[2] = new Vector3f(0.1f,0,0);
    vertices[3] = new Vector3f(0,0,0.1f);
    
    // Setting buffers
    pyramid.setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(vertices));
    pyramid.setBuffer(VertexBuffer.Type.Index, 1, BufferUtils.createIntBuffer(indexes));
    pyramid.updateBound();
    
    return pyramid;
}[/java] 

Anyways, thanks a lot for the rest of the comment, I will dive into that tomorrow and come back a.s.a.p!

@aaronperkins
I don’t really understand what you’re saying.
As far as i can get it, I think that bubuche’s suggestion which you further explained is the better one.
Could you give some example code or explain at a lower level please?

Thanks a lot in advance!

I don’t really understand what you’re saying. As far as i can get it, I think that bubuche’s suggestion which you further explained is the better one. Could you give some example code or explain at a lower level please?

In the simplest terms:

  1. Construct your entire simulation as a single custom mesh (not just each individual cell as a custom mesh).
  2. Create a texture in memory large enough so 1 pixel = 1 simulation cell.
  3. Update the individual pixels in the texture based on the cell state in your simulation.
  4. Write a custom shader that takes the texture and uses it to display or not display a vertex in your custom mesh.

That’s the basics of what you need to do.

Not going to write code for you. If you’re confused about shaders or custom meshes. Check out the tutorials.

the problem is here:

private Mesh singleDot()

You need (with my method) to create one big enourmous mesh which contains all the triangles. This is what batchgeometryfactory does. But, you need to create in only once, then come back a lot of time to edit the texture array.

I am not familiar with shaders, so i can’t give you advice on this. But it could be a good way to do that.

@aaronperkins & @hreitsma : actually, i am not talking about a texture with a shader. I think that it’s even possible with just colors (assigned to vertex), with no texture. I think that this will be even faster than shaders.

I don’t like it either when others write code for me, at the end it’s only an example, and the numeration you just gave is way more explaining, thnx!

I will come back asap with the results!

edit:
I’ll try both idea’s so I can be certain I keep the fastest one (-:

actually, i am not talking about a texture with a shader. I think that it’s even possible with just colors (assigned to vertex), with no texture. I think that this will be even faster than shaders.

Directly modifying the vertex color array would certainly be easier. No need for a custom shader.

It comes down to which is faster, modifying the color array in the mesh’s VBO every frame or modifying a texture every frame.

If there isn’t much difference in speed, then I’d go with modifying the color array in the VBO every frame.

Damn, you guys are awesome!

  1. I started over and made just one mesh.
  2. created 4 coördinates and stored them in an array
  3. created 4 RGBA-colors and stored them in an array
  4. used setBuffer to add the two arrays of coördinates and colors to the mesh.
  5. Now, instead of making a shape i used “Mesh.Mode.Points”.
    This way i don’t have to draw lines because every coördinate is a point already (a tiny box which can be scaled).
  6. Next I created a geometry using the Mesh i made.
  7. Finally I attached the geometry to the rootNode.

Now my cube was ready and working I took the array of the RGBA-colors and edited the color of the first dot.
I used setBuffer again to set the new color-buffer.
To make it complete i used updateBound() on the Mesh and the color of a single dot is changed.

When I now play the project with not 4 cells as described above but 1.000.000 cells, the project is rendered in less than 0.2 seconds :-o
It’s overwhelming what kind of power the CPU has…

I have to say this is way more then I was praying for so thnx a lot guys!
Wouldn’t be able to do it without your support!

1 Like
@hreitsma said: It's overwhelming what kind of power the CPU GPU has..

…fixed that for you. :slight_smile:

1 Like

My bad, I ment GPU :facepalm:

Btw, I would also like to add that my fps also is back at a perfect 100fps thanks to you guys, forgot to mention that 8)

1 Like