Problems setting up LoDs

Hello all,



I’m setting up LoDs for a custom mesh for the first time.

So far I have:

  1. Built separate int arrays for different distances;
  2. Initialize them as VertexBuffers arrays:

    [java]VertexBuffer lods[] = new VertexBuffer[3];

    lods[0] = new VertexBuffer(Type.Index);

    lods[0].setupData(Usage.Dynamic, 1, Format.UnsignedInt, BufferUtils.createIntBuffer(orderAllVertex));[/java]
  3. Added them to the mesh:

    [java]mesh.setLodLevels(lods);[/java]
  4. Created an LodControl and added it to the geometry:

    [java]LodControl lodControl = new LodControl();

    lodControl.setDistTolerance(20f);

    geom.addControl(lodControl);[/java]



    When I run it I always see 4 quads no matter how far I am.

    Am I missing a step?



    Here is the full code:

    [java]package lods;



    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;



    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;

    }

    }

    }

    // middle res

    final int triNumberMiddleRes = 6;

    int orderMiddleRes[] = new int[triNumberMiddleRes * 3];

    pointA[0] = finalVertices[0];

    pointA[1] = finalVertices[5];

    pointB[0] = finalVertices[2];

    pointB[1] = finalVertices[7];

    vertexIndexUpDown = calcVertexOrder(pointA, pointB);

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

    orderMiddleRes = vertexIndexUpDown.get(0);

    }

    pointA[0] = finalVertices[2];

    pointA[1] = finalVertices[7];

    pointB[0] = finalVertices[4];

    pointB[1] = finalVertices[9];

    vertexIndexUpDown = calcVertexOrder(pointA, pointB);

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

    final int verticeIndex = vertexIndexUpDown.get(0);

    switch (verticeIndex) {

    case 0:

    orderMiddleRes = 2;

    break;

    case 1:

    orderMiddleRes = 7;

    break;

    case 2:

    orderMiddleRes = 4;

    break;

    case 3:

    orderMiddleRes = 9;

    break;

    }

    }

    // low res

    final int triNumberLowRes = 4;

    int orderLowRes[] = new int[triNumberLowRes * 3];

    pointA[0] = finalVertices[0];

    pointA[1] = finalVertices[5];

    pointB[0] = finalVertices[4];

    pointB[1] = finalVertices[9];

    vertexIndexUpDown = calcVertexOrder(pointA, pointB);

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

    orderMiddleRes = vertexIndexUpDown.get(0);

    }

    VertexBuffer lods[] = new VertexBuffer[3];

    lods[0] = new VertexBuffer(Type.Index);

    lods[0].setupData(Usage.Dynamic, 1, Format.UnsignedInt, BufferUtils.createIntBuffer(orderAllVertex));

    lods[1] = new VertexBuffer(Type.Index);

    lods[1].setupData(Usage.Dynamic, 1, Format.UnsignedInt, BufferUtils.createIntBuffer(orderMiddleRes));

    lods[2] = new VertexBuffer(Type.Index);

    lods[2].setupData(Usage.Dynamic, 1, Format.UnsignedInt, BufferUtils.createIntBuffer(orderLowRes));



    // with lod

    createMesh(finalVertices, orderAllVertex, "upside", ColorRGBA.Yellow, lods);



    return true;

    }



    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;

    }



    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;

    }

    }[/java]

The LodControl has no connection to the camera? It needs it to tell how far the player is away.

1 Like

So I checked this out and found this already inside LodControl:

[java]protected void controlRender(RenderManager rm, ViewPort vp){

BoundingVolume bv = spatial.getWorldBound();



Camera cam = vp.getCamera();

float atanNH = FastMath.atan(cam.getFrustumNear() * cam.getFrustumTop());

float ratio = (FastMath.PI / (8f * atanNH));

float newDistance = bv.distanceTo(vp.getCamera().getLocation()) / ratio;[/java]



This should be enough, no?

Also, I didn’t find any methods to add the camera to LodControl.

Hm, ok, then idk.