Custom mesh and indexes

Hello guys, i’m currently working on a custom mesh, and I was wondering if there was an efficient way of storing vertex positions and indexes,

i’m thinking about makined a LinkedHashmap that stores all my vertices and then transform it into a buffer later on, I use a hasmap in order to determine if a vertex already exists in the buffer, and si I can retrieve its index quickly without having to iterate over the whole buffer,
is this a good solution or im doing it wrong?

Sure, ofc you double the data but while you’re working the tradeoff in favor of computation speed can certainly be worth it. Probably you should only create such a map when you actually want to use it. Otherwise if you create the locations or whatever procedurally it might also be that you can just generate the locations from that same base data.

1 Like

Also note that a unique vertex includes all of the vertex attributes. So if you will have normals, texture coordinates, colors, etc. then they are all part of what makes a vertex unique and will have to be checked for sameness to join them.

1 Like
@pspeed said: Also note that a unique vertex includes all of the vertex attributes. So if you will have normals, texture coordinates, colors, etc. then they are all part of what makes a vertex unique and will have to be checked for sameness to join them.
so you are kind of suggesting to create an intermediary Vertex Object that stoires all the informations relative to a vertex : position, index, texcoord, normals, colors etc.

then I store that vertex object in memory inside a LinkedHashMap, and everytime a user changes a vertex in the linkedHashMap, I just update the current mesh buffer accordingly?

I was recently thinking about not using Vector3f objects but I think trying to save on memory like that doesn’t worth it, since it makes life harder.

i’d rather use vectorf and vector2f than float buffers is it alright to think that way?
Or I have to be memory greedy in the case of mesh generation?

@navycougar said: so you are kind of suggesting to create an intermediary Vertex Object that stoires all the informations relative to a vertex : position, index, texcoord, normals, colors etc.

then I store that vertex object in memory inside a LinkedHashMap, and everytime a user changes a vertex in the linkedHashMap, I just update the current mesh buffer accordingly?

I was recently thinking about not using Vector3f objects but I think trying to save on memory like that doesn’t worth it, since it makes life harder.

i’d rather use vectorf and vector2f than float buffers is it alright to think that way?
Or I have to be memory greedy in the case of mesh generation?

I’m saying that if you make it only about position then you’re probably going to have a bad time.

1 Like
@pspeed said: I'm saying that if you make it only about position then you're probably going to have a bad time.

thanks alot pspeed, i’ve made a verrrryy simple custom mesh can I have any critics on it?

also i’d really love if someone could share this code, or add this code if its good to the custom meshes tutorials, so people can understand how to handle multiple shapes.

For the moment you just have to call addSquare on the mesh, at certain positions, then call generateBuffers once after adding squares, its really simple to use, and it only works on x,y plane… but this is a start, and i’d like to share my code if this can help anyone understand cutom meshes i’d be really glad.
i’ll later update the generated buffers when adding a new square to the VerticesHandler.

usage :
[java] MyTerrain2 terrain = new MyTerrain2();
terrain.addSquare(new Vector3f(0,0,0));

    terrain.addSquare(new Vector3f(1,0,0));
    terrain.addSquare(new Vector3f(2,0,0));
    terrain.addSquare(new Vector3f(4,0,0));
    terrain.addSquare(new Vector3f(0,1,0));
    terrain.addSquare(new Vector3f(0,2,0));
    /*terrain.addSquare(new Vector3f(0,0,1));
    terrain.addSquare(new Vector3f(0,0,2));*/
    terrain.generateMesh();
    Geometry geo = new Geometry("OurMesh", terrain); // using our custom mesh object
    Material mat = new Material(assetManager, 
        "Common/MatDefs/Misc/Unshaded.j3md");
    mat.setColor("Color", ColorRGBA.Blue);
    mat.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);
    mat.getAdditionalRenderState().setWireframe(true);
    geo.setMaterial(mat);
    geo.setLocalTranslation(0f, 0f, 0f);
    rootNode.attachChild(geo);[/java]

[java]
public class MyTerrain2 extends Mesh{
private MyVerticeHandler verticeHandler = new MyVerticeHandler();

public void addSquare(Vector3f position){
    verticeHandler.addSquare(position);
}
public void generateMesh() {
    verticeHandler.generateBuffers();
    this.setBuffer(Type.Position, 3, verticeHandler.getPositions());
    this.setBuffer(Type.TexCoord, 2, verticeHandler.getTexCoords());
    this.setBuffer(Type.Index, 3, verticeHandler.getIndexes());
    this.updateBound();
}

}[/java]
[java]

public class MyVerticeHandler {

private int maxVertices = 256;
private Map<Integer, MyVertice> vertices = new LinkedHashMap<Integer, MyVertice>(maxVertices);
private int[] verticesIndexes = new int[maxVertices];
int verticeIndex = 0;
int nbIndex = 0;
private float[] positions;
private float[] texCoords;
private int[] indexes;

public void addSquare(Vector3f position) {
    Vector3f roundedPosition = MyMath.round(position);

    MyVertice vertice1 = null;
    MyVertice vertice2 = null;
    MyVertice vertice3 = null;
    MyVertice vertice4 = null;
    //cube position at position
    Vector3f position1 = new Vector3f(roundedPosition.x, roundedPosition.y, 0);
    Vector3f position2 = new Vector3f(1 + roundedPosition.x, roundedPosition.y, 0);
    Vector3f position3 = new Vector3f(roundedPosition.x, 1 + roundedPosition.y, 0);
    Vector3f position4 = new Vector3f(1 + roundedPosition.x, 1 + roundedPosition.y, 0);

    vertice1 = getVertice(position1);
    vertice2 = getVertice(position2);
    vertice3 = getVertice(position3);
    vertice4 = getVertice(position4);

    if (vertice1 == null) {
        vertice1 = new MyVertice();
        vertice1.setPosition(position1);
        vertice1.setTexCoord(new Vector2f(0, 0));
        vertice1.setIndex(verticeIndex);
        vertices.put(vertice1.hashCode(), vertice1);
        verticeIndex++;
    }
    if (vertice2 == null) {
        vertice2 = new MyVertice();
        vertice2.setPosition(position2);
        vertice2.setTexCoord(new Vector2f(1, 0));
        vertice2.setIndex(verticeIndex);
        vertices.put(vertice2.hashCode(), vertice2);
        verticeIndex++;
    }
    if (vertice3 == null) {
        vertice3 = new MyVertice();
        vertice3.setPosition(position3);
        vertice3.setTexCoord(new Vector2f(0, 1));
        vertice3.setIndex(verticeIndex);
        vertices.put(vertice3.hashCode(), vertice3);
        verticeIndex++;
    }
    if (vertice4 == null) {
        vertice4 = new MyVertice();
        vertice4.setPosition(position4);
        vertice4.setTexCoord(new Vector2f(1, 1));
        vertice4.setIndex(verticeIndex);
        vertices.put(vertice4.hashCode(), vertice4);
        verticeIndex++;
    }
    // 3,1,2, 2,4,3 
    verticesIndexes[nbIndex] = vertice3.getIndex();
    nbIndex++;
    verticesIndexes[nbIndex] = vertice1.getIndex();
    nbIndex++;
    verticesIndexes[nbIndex] = vertice2.getIndex();
    nbIndex++;

    verticesIndexes[nbIndex] = vertice2.getIndex();
    nbIndex++;
    verticesIndexes[nbIndex] = vertice4.getIndex();
    nbIndex++;
    verticesIndexes[nbIndex] = vertice3.getIndex();
    nbIndex++;
}

private MyVertice getVertice(Vector3f position) {
    return vertices.get(position.hashCode());
}

public Map<Integer, MyVertice> getVertices() {
    return vertices;
}

public void generateBuffers() {
    if (positions == null) {
        positions = new float[nbIndex*3];
    }
    if (texCoords == null) {
        texCoords = new float[nbIndex * 2];
    }

    if (indexes == null) {
        indexes = new int[nbIndex];
    }
    int i = 0;
    for (MyVertice vertice : vertices.values()) {
       positions[vertice.getIndex()*3] = vertice.getPosition().x;
        positions[(vertice.getIndex()*3) + 1] = vertice.getPosition().y;
        positions[(vertice.getIndex()*3) + 2] = vertice.getPosition().z;
        texCoords[vertice.getIndex()*2] = vertice.getPosition().x;
        texCoords[(vertice.getIndex()*2)+1] = vertice.getPosition().y;
        indexes[i] = verticesIndexes[vertice.getIndex()];
       i++;
    }
}

public int[] getIndexes() {
    return verticesIndexes;
}

public void setIndexes(int[] indexes) {
    this.verticesIndexes = indexes;
}

public float[] getPositions() {
    return positions;
}

public void setPositions(float[] positions) {
    this.positions = positions;
}

public float[] getTexCoords() {
    return texCoords;
}

public void setTexCoords(float[] texCoords) {
    this.texCoords = texCoords;
}

}
[/java]

i’ll improve this version and post the code as soon as i’m finished here.

@normen @pspeed

guys I am using the hashcode for identifiying a Vector3f, do you think its the best solution for finding a position index? knowing that i can get collisions

You will have to explain more. Hashcodes are not unique… so it depends on how you are using it.

1 Like
@pspeed said: You will have to explain more. Hashcodes are not unique... so it depends on how you are using it.

hey pspeed: before explaining what I do, if I add new indexes to the indexes buffer, and to the positions buffer do I have to Regenerate the whole buffer, of i can just use my reference to update the buffers?

example :

does this work??
[java]float [] position = new float[3];
this.setBuffer(Type.Position, 3, position);
position[0] = 0;
position[1] = 1;
position[2] = 0;

    this.updateBound();[/java]

as for the explanation, I posted some code earlier ^^ wanted to share so people can learn from my code with me.

I define a square (commposed of 4 vertexes) and then I calculate the new square position by rounding the position of the user square, then
in order to check if the vertexes of the new square collide with other existing vertexes, I just use the hashcode of the position, and get it from my linkedhashmap :slight_smile: then I get the index of the already existing vertex (no need to create a new one).

The hash code is not good enough. Two different Vector3fs can have the same hash code and there is no way you will detect this.

If you want to use a hash map then you will need to make proper keys.

1 Like

you mean like a String key : “x,y,z” I believe this is unique

A string works… or you can create your own class with a proper hashCode and equals() that takes the fields into account.

…are you not doing anything but positions?

1 Like

I Thought the HashCode from the Vertex3f took into accound the positions.

You cansee from my posted code (few posts earlier)
that I handle texCoords also :stuck_out_tongue: I know I should handle normals and all for using light shaders Etc.
i’ll handle those later ^^ for the moment MyVertice only handles position and texCoord ^^

it’s the first time I ever work with vertex buffers i’m learning! my first objective is to be able to put squares on a 2d plane, then put some other shapes, and later on i’ll handle texCoords with an atlas, like i’ll make a huge atlas of small 128x128 textures (sand stone etc.) then each shape withh have a type (sand stone etc associated with it, and i’ll set the correct texCoords)

@navycougar said: I Thought the HashCode from the Vertex3f took into accound the positions.

Hashcodes are not unique. That’s not how hashtables work. You cannot use some object’s hashcode as a key on its own. It just won’t work.

You could use a Vector3f as a key to a hashmap because Vector3f implements .hashCode() AND .equals(). Both are 10000% necessary. But as discussed, this limits your meshes to just positions, ie: no normals, no texture coordinates, etc… pretty lame.

Or you can create your own key that contains all of the vertex attributes and implements .hashCode() and .equals().

1 Like
@pspeed said: Hashcodes are not unique. That's not how hashtables work. You cannot use some object's hashcode as a key on its own. It just won't work.

You could use a Vector3f as a key to a hashmap because Vector3f implements .hashCode() AND .equals(). Both are 10000% necessary. But as discussed, this limits your meshes to just positions, ie: no normals, no texture coordinates, etc… pretty lame.

Or you can create your own key that contains all of the vertex attributes and implements .hashCode() and .equals().

hello pspeed, I’ve inplemented a linkedHashMap with a chained list for handling hashcode collisions, it works well :slight_smile:

collisions don’t happen too often, Now I have a new problem :slight_smile:

when I retrieve a group of 4 vertices I need to tell if those 4 vertices are part of a face or not :slight_smile: in order to fix that I created a map of vertices Triangles, and I use DeepHashCodes for checking if vertexes are part of a triangle.

I post my code in a new thread where I’ll share my findings.

http://hub.jmonkeyengine.org/forum/topic/custom-mesh-and-editable-terrain/

Hi,
I also need to have unique identifiers for vectors and while searching I found this thread.

My first approach was to use the hashcode() function as I thought it would be exactly what I need. But after some research and reinterpreting the jme javadoc for Vector3f.hashCode() and the native java Object.hashCode() I figured out that it does not really guarantee unique values the way I thought and my interpretation of “unique” was a little too harsh. I first thought it is an injective projection or even bijective but unfortunately “unique” seems to just be a surjective projection.

Anyways I need an at least injective projection from vector3 or rather 3 position values to one integer. I know the most simple way would be to use some kind of string format. But as I have to watch out for the memory usage I really would like to use integers if possible. So I wonder if that is possible or if someone did something like that or what your thoughts is about that.

This is my situation:
I actually don’t neccessarily want to use the position (just in some cases) I just need to know if I processed the position so a map<vector3, int> would be way more memory that I need. I just want to use a TreeSet<Integer> to check whether I passed a position in a 3d grid or not. Because I run through it in the common linear way with 3 loops but on the first point of interest I start processing depending on a data structure that changes over time. But still have to assure that I pass each position exactly one time.

I would use HashSet<Vector3f> for this. The memory would be no more than Integer (in fact probably less) because the Vector3fs already exist and the Integer objects would have to be created.

Edit: note: I’m presuming you create a new one with each new pass through the coordinates.

1 Like

Actually nope :smiley: it should look somethin like this:

[java]
int hash;
for (int x = 0; x < bound; x++) {
for (int y = 0; y < bound; y++) {
hash = hash(x, y, 0);

            if (!processedFaces.contains(hash)) {
                processedFaces.add(hash);
                getVoxelData(x, bound - y, 0, scale, heightMap);

                fCase = getFaceCase();
                if (fCase &gt;= 0 &amp;&amp; fCase &lt; 15) {
                    extractSurface(s, x, y, 0, fCase, heightMap, scale);
                }
            }
        }
    }

[/java]

And now that you say it again you are right HashSet probably better I mixed up that constant time ofcourse best ever but when I saw log(n) I don’t know why but I thought oh yea log(n) super cool.
:wink:

So how often do you run this code? I’d assume before one whole run that you’d need to clear or reset the set in some way.

Also, contains is redundant here since add() can be used for the same purpose (ie: it will return false if the set already contains the item but saves lookup.)

1 Like