Help ordering vertex for mesh of strip

Hello all,



I’m working on generating a strip to show a path. Each pair of points connects with the next pair with quads.

I am having problems ordering the vertex so that all Quads are correctly generated and ordered to appear when you look from above.



But I’m finding this very hard. It works for some situations and not for others.

I made a MWE to ask you to take a look:



[java]

import com.jme3.app.SimpleApplication;

import com.jme3.material.Material;

import com.jme3.math.ColorRGBA;

import com.jme3.math.FastMath;

import com.jme3.math.Vector3f;

import com.jme3.scene.Geometry;

import com.jme3.scene.Mesh;

import com.jme3.scene.VertexBuffer;

import com.jme3.scene.VertexBuffer.Format;

import com.jme3.scene.VertexBuffer.Type;

import com.jme3.scene.VertexBuffer.Usage;

import com.jme3.scene.control.LodControl;

import com.jme3.scene.debug.Arrow;

import com.jme3.util.BufferUtils;

import java.util.ArrayList;



/** Sample 1 - how to get started with the most simple JME 3 application.

  • Display a blue 3D cube and view from all sides by
  • moving the mouse and pressing the WASD keys. */

    public class LOD extends SimpleApplication {



    public static void main(String[] args) {

    LOD app = new LOD();

    app.start(); // start the game

    }



    @Override

    public void simpleInitApp() {

    createStripNewDataStruct();

    attachCoordinateAxes(new Vector3f(0, 0, 0));

    }



    private boolean createStripNewDataStruct() {

    final int vertNumber = 10;

    Vector3f[] finalVertices = new Vector3f[vertNumber];

    finalVertices[0] = new Vector3f(0, 0, 0);

    finalVertices[1] = new Vector3f(1, 0, 0);

    finalVertices[2] = new Vector3f(2, 0, 0);

    finalVertices[3] = new Vector3f(3, 0, 0);

    finalVertices[4] = new Vector3f(4, 0, 0);



    finalVertices[5] = new Vector3f(0, 0, 1);

    finalVertices[6] = new Vector3f(1, 0, 1);

    finalVertices[7] = new Vector3f(2, 0, 1);

    finalVertices[8] = new Vector3f(3, 0, 1);

    finalVertices[9] = new Vector3f(4, 0, 1);



    final int triNumberAllVertex = 8;

    int orderAllVertex[] = new int[triNumberAllVertex * 3];

    Vector3f pointA[] = new Vector3f[2];

    Vector3f pointB[] = new Vector3f[2];

    ArrayList<int[]> vertexIndexUpDown;

    // all the vertex

    for (int v = 0; v < 4; v++) {

    pointA[0] = finalVertices[v];

    pointA[1] = finalVertices[v + 5];

    pointB[0] = finalVertices[v + 1];

    pointB[1] = finalVertices[v + 6];

    vertexIndexUpDown = calcVertexOrder(pointA, pointB);

    for (int i = 0; i < 6; i++) {

    switch (vertexIndexUpDown.get(0)) {

    case 0:

    orderAllVertex = v;

    break;

    case 1:

    orderAllVertex = v + 5;

    break;

    case 2:

    orderAllVertex = v + 1;

    break;

    case 3:

    orderAllVertex = v + 6;

    break;

    }

    }

    }

    createMesh(finalVertices, orderAllVertex, “upside”, ColorRGBA.Yellow, null);



    return true;

    }



    protected Geometry createMesh(Vector3f[] vertices, int[] vertexIndex, String meshName, ColorRGBA color,

    VertexBuffer[] lod) {

    Material mat;

    Geometry geom;

    Mesh mesh = new Mesh();

    // For downside

    mesh.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(vertices));

    mesh.setBuffer(Type.Index, 3, BufferUtils.createIntBuffer(vertexIndex));

    mesh.updateBound();

    geom = new Geometry(meshName, mesh);

    if (lod != null) {

    mesh.setLodLevels(lod);

    LodControl lodControl = new LodControl();

    lodControl.setDistTolerance(20f);

    geom.addControl(lodControl);

    }

    mat = new Material(assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);

    mat.getAdditionalRenderState().setWireframe(true);

    mat.setColor(“Color”, ColorRGBA.Red);

    geom.setMaterial(mat);

    rootNode.attachChild(geom);

    return geom;

    }



    private ArrayList<int[]> calcVertexOrder(Vector3f wingsA[], Vector3f wingsB[]) {

    Vector3f[] currVertices = new Vector3f[4];

    // Set the current array with the wing points of this and the past point

    currVertices[0] = wingsA[0];

    currVertices[1] = wingsA[1];

    currVertices[2] = wingsB[0];

    currVertices[3] = wingsB[1];;

    // Calculate how to put them to create one mesh clockwise and another counter clockwise

    return orderVertexIndexes(currVertices);

    }



    private ArrayList<int[]> orderVertexIndexes(Vector3f[] vertices) {

    boolean shortest12 = is12ShortestThan03(vertices);

    int common2ndWingPDown, common1stWingPDown, opposite2ndWingPDown, opposite1stWingPDown;

    opposite1stWingPDown = opposite2ndWingPDown = common1stWingPDown = common2ndWingPDown = -1;

    if (shortest12){

    common2ndWingPDown = 2;

    opposite2ndWingPDown = 3;

    common1stWingPDown = 1;

    opposite1stWingPDown = 0;

    }else{ // shortest

    common2ndWingPDown = 3;

    opposite2ndWingPDown = 2;

    common1stWingPDown = 0;

    opposite1stWingPDown = 1;



    }



    int[] indexesDown = new int[6];

    int[] indexesUp = new int[6];

    // First triangle

    boolean isCCW_Down = isCounterClock(vertices[opposite1stWingPDown], vertices[common1stWingPDown], vertices[common2ndWingPDown]);

    indexesDown[0] = opposite1stWingPDown;

    indexesUp[0] = opposite1stWingPDown;

    if (isCCW_Down) {

    indexesDown[1]=common1stWingPDown;

    indexesDown[2]=common2ndWingPDown;

    // Second triangle

    indexesDown[3] = common2ndWingPDown;

    indexesDown[4] = common1stWingPDown;

    indexesDown[5] = opposite2ndWingPDown;

    // - Up

    indexesUp[1] = common2ndWingPDown;

    indexesUp[2] = common1stWingPDown;

    // Second triangle

    indexesUp[3] = common1stWingPDown;

    indexesUp[4] = common2ndWingPDown;

    indexesUp[5] = opposite2ndWingPDown;

    }

    else {

    indexesDown[1]=common2ndWingPDown;

    indexesDown[2]=common1stWingPDown;

    // Second triangle

    indexesDown[3] = common1stWingPDown;

    indexesDown[4] = common2ndWingPDown;

    indexesDown[5] = opposite2ndWingPDown;

    // - Up

    indexesUp[1] = common1stWingPDown;

    indexesUp[2] = common2ndWingPDown;

    // Second triangle

    indexesUp[3] = common2ndWingPDown;

    indexesUp[4] = common1stWingPDown;

    indexesUp[5] = opposite2ndWingPDown;

    }



    ArrayList<int[]> vertexIndexUpDown = new ArrayList<int[]>(2);

    vertexIndexUpDown.add(0, indexesUp);

    vertexIndexUpDown.add(1, indexesDown);

    return vertexIndexUpDown;

    }



    private boolean isCounterClock(Vector3f p1, Vector3f p2, Vector3f p3) {

    Vector3f normal = FastMath.computeNormal(p1, p2, p3);

    // the normal vector for the downwards face must always have negative y

    if (normal.y <= 0f) {

    return true;

    } else {

    return false;

    }

    }



    private boolean is12ShortestThan03(Vector3f[] vertices){

    float distances[] = new float[2];

    distances[0] = vertices[1].distance(vertices[2]); // distBC

    distances[1] = vertices[0].distance(vertices[3]); // distAD

    if(distances[0] > distances [1]){

    return false;

    }

    else{

    return true;

    }

    }

    protected void attachCoordinateAxes(Vector3f pos) {

    Arrow arrow = new Arrow(Vector3f.UNIT_X.multLocal(10));

    arrow.setLineWidth(4); // make arrow thicker

    putShape(arrow, ColorRGBA.Red).setLocalTranslation(pos);



    arrow = new Arrow(Vector3f.UNIT_Y.multLocal(10));

    arrow.setLineWidth(4); // make arrow thicker

    putShape(arrow, ColorRGBA.Green).setLocalTranslation(pos);



    arrow = new Arrow(Vector3f.UNIT_Z.multLocal(10));

    arrow.setLineWidth(4); // make arrow thicker

    putShape(arrow, ColorRGBA.Blue).setLocalTranslation(pos);

    }

    protected Geometry putShape(Mesh shape, ColorRGBA color) {

    Geometry g = new Geometry(“coordinate axis”, shape);

    Material mat = new Material(assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);

    mat.getAdditionalRenderState().setWireframe(true);

    mat.setColor(“Color”, color);

    g.setMaterial(mat);

    rootNode.attachChild(g);

    return g;

    }

    }[/java]



    This is what I get:

    Photobucket

The winding order matters. If the first triangle is clockwise, then then next triangle is counter-clockwise. And this repeats.

If you need to swap the winding order, you can just add another index at the same spot; this will create a degenerate polygon that the video card will quickly remove.

Vertex 1 and 5 are duplicated in your finalVertex array, only indices should be duplicated.


0---1---2
| / | / |
3---4---5

index: 0,3,1,4,2,5

It will automatically look back 2 indexes when building the triangle starting with the new index. So the triangles will be:
031; 314; 142; 425

The vertex were badly defined, I’ve fixed it now.



About the winding order, you had to switch the 2 adjacent vertex to avoid problems with normals. I have looked again for the sources for where I took this from but can’t find it. The idea was that to avoid some triangles ending up in a limit situation where they would not be rendered you should invert the order of the adjacent vertex while keeping it in CCW. This was what I thought I was doing.

Is this true?

Is there a way to invert the adjacent vertex order and then use the remaining vertex to keep it in CCW?



So after the corrections I was able to make (the code above is updated), I get this:

Photobucket



Now I’m confused. I haven’t changed the switching of the order, why is this ok?

You don’t have to add vertices in to change the order, you just have to duplicate one index to flip the winding order (thus the normal) for that triangle. The point of triangle strips is to reduce the vertex count, and index size.

Triangle strips aren’t going to drastically improve performance, and for their confusion I would recommend just using triangles. I only used triangle strips for the terrain because it can be very large and I wanted to reduce the memory footprint.

Generally, same number of vertexes but a smaller index buffer… nearly 2/3rds the size as the buffer gets larger. So, yeah, for most things it doesn’t save much since the index buffer is already pretty small.



Triangle strips were a lot more useful in the non-index case.

I’m using strips because I want to make an actual strip to represent the path of a vehicle that would pass in the middle.

Thought that if I just made triangles between points like 062 in my indexes it would look strange for a path.

You agree?


@Sploreg You don’t have to add vertices in to change the order, you just have to duplicate one index to flip the winding order (thus the normal) for that triangle.

I'm struggling with this, I have seen it in several places but I can't really get it.
Why is it important to flip the normal? Wouldn't that make the triangle disappear for the same viewpoint? I know it doesn't because that's what I'm doing and I can see all the triangles but don't know why.

That being can you give an example of adding unnecessary vertex? I don't understand what you mean.
@kotoko said:
I'm using strips because I want to make an actual strip to represent the path of a vehicle that would pass in the middle.
Thought that if I just made triangles between points like 062 in my indexes it would look strange for a path.
You agree?


No. The ONLY difference between a regular triangle mesh and a triangle strip is the index buffer. The results can look identical if you've set them up properly... the only difference is that in the case where a triangle strip can work, the index buffer is roughly 1/3rd smaller.

Oh, I see.



But the thing about fliping the winding order, is this just for triangle strips?

@pspeed said:
No. The ONLY difference between a regular triangle mesh and a triangle strip is the index buffer. The results can look identical if you've set them up properly... the only difference is that in the case where a triangle strip can work, the index buffer is roughly 1/3rd smaller.


There is an additional difference, since you have only one texCoord / vert. This can cause problems. On a box for example you can't set the texture alignment for each side.. (Front and back are flipped for example).. Of course for 'flat' terrain this is not a problem
@kotoko said:
But the thing about fliping the winding order, is this just for triangle strips?

I'm not sure about triangles, but you do have to switch winding order occasionally with strips.
@Sploreg said:
I'm not sure about triangles, but you do have to switch winding order occasionally with strips.


"Winding order" is kind of a misnomer in this case since it's intrinsic in the structure. Each new triangle is defined by the next point in the index buffer... this technically means that winding is switching back and forth every other triangle but you don't need to worry about because you aren't specifying the three vertexes for a triangle... just the one new one.

Winding order matters when you are supplying three vertexes since the order of those vertexes matters.

I thought sploreg's post earlier (http://hub.jmonkeyengine.org/groups/graphics/forum/topic/help-ordering-vertex-for-mesh-of-strip/#post-188992) made this pretty clear... so hopefully all confusion has been erased.

The only difference between a triangle strip and a triangle mesh arranged that happens to be arranged in a triangle strip (and is sharing vertexes) is the index buffer.

Thanks for all your help!