[solved]Terrain adjustHeight/raycast pick odd issue


#1

Hello. i need some help solving this issue, maybe you will know the reason of this.

using JME 3.3

i wanted to make editor to paint/adjustHeight/object placement/area paint/etc beginning from plain terrain. (i know sdk have it, but i need more than just adjust height and paint - also using pbr material for terrain that SDK dont support)

The problem appear when terrain is just a plain(no hills), when i use adjust height even once, the raycast stop working as it should.

funny is, when there is pre-modified height like hill nearby, then there is no problem.

Videos below contain console to show when raycast work
in videos I make circles with camera when i adjust height to show when it stop work.

videos will demonstrate(contained code changes to videos):

  • without using “setHeightAtPoint” in load
  • using:

    for (int i = 120; i < 160; i++) {
    for (int j = 120; j < 160; j++) {
    setHeightAtPoint((float) 3f, j, i);
    }
    }

  • using:

      for (int i = 0; i < size; i++) {
          for (int j = 0; j < size; j++) {
                setHeightAtPoint((float) 0f, j, i);
       }
         }
       
       for (int i = 0; i < size; i++) {
           for (int j = 0; j < size; j++) {
              setHeightAtPoint((float) Math.random() * 0.5f, j, i);
           }
       }
    

i made some tests like:

    final int terrainSize = size;
    
    AbstractHeightMap heightmap = new AbstractHeightMap() {
        @Override
        public boolean load() {
            size = terrainSize;
            // clean up data if needed.
            if (null != heightData) {
                unloadHeightMap();
            }
            heightData = new float[size * size];
            // transfer temporary buffer to final heightmap
            for (int i = 0; i < size; i++) {
                for (int j = 0; j < size; j++) {
                    setHeightAtPoint((float) 0.5f, j, i);
                }
            }
            for (int i = 120; i < 160; i++) {
                for (int j = 120; j < 160; j++) {
                    setHeightAtPoint((float) 3f, j, i);
                }
            }
            normalizeTerrain(NORMALIZE_RANGE);
            return true;
        }
    };
    heightmap.load();

where adjust height do not break raycast near this hill, when

            for (int i = 120; i < 160; i++) {
                for (int j = 120; j < 160; j++) {
                    setHeightAtPoint((float) 3f, j, i);
                }
            }

this is commented, then raycast break everywhere after single use of adjustHeight.

but i also tried make random heights, but it also had issues… (third video)

for raycast i use:

public Vector3f getCameraContactPoint() {
    System.out.println(System.currentTimeMillis() + " getCameraContactPoint");
    if (terrain != null) {
        Vector3f origin = cam.getLocation();
        Vector3f direction = cam.getDirection();
        Ray mouseRay = new Ray(origin, direction.normalize());
        CollisionResults results = new CollisionResults();
        terrain.collideWith(mouseRay, results);
        CollisionResult closestCollision = results.getClosestCollision();
        if (closestCollision != null) {
            System.out.println(System.currentTimeMillis() + " " + closestCollision.getContactPoint());
            return closestCollision.getContactPoint();
        }
    }
    return null;
}

dont even need normalize but added it to be sure.

for adjust height i use:

public void adjustHeight(Vector3f loc, float height) {
    // offset it by radius because in the loop we iterate through 2 radii
    int radiusStepsX = (int) (editHeightRadius / terrain.getLocalScale().x);
    int radiusStepsZ = (int) (editHeightRadius / terrain.getLocalScale().z);
    float xStepAmount = terrain.getLocalScale().x;
    float zStepAmount = terrain.getLocalScale().z;
    List<Vector2f> locs = new ArrayList<Vector2f>();
    List<Float> heights = new ArrayList<Float>();
    for (int z = -radiusStepsZ; z < radiusStepsZ; z++) {
        for (int x = -radiusStepsX; x < radiusStepsX; x++) {
            float locX = loc.x + (x * xStepAmount);
            float locZ = loc.z + (z * zStepAmount);
            if (isInRadius(locX - loc.x, locZ - loc.z, editHeightRadius)) {
                // see if it is in the radius of the tool
                float h = calculateHeight(editHeightRadius, height, locX - loc.x, locZ - loc.z);
                locs.add(new Vector2f(locX, locZ));
                heights.add(h);
            }
        }
    }
    terrain.adjustHeight(locs, heights);
    //System.out.println("Modified "+locs.size()+" points, took: " + (System.currentTimeMillis() - start)+" ms");
}

Overall problem started exist for me when i used simple height map generation like:

        heightmap = new HillHeightMap(size, 2, 2, 100);
        heightmap.smooth(1f, 24);

it was odd for me it was working only nearby hills. Thats why i started investigate with custom Heightmap generator.


#2

ok, solved.

i looked at SDK terrain editor code again.

and i noticed that in adjustHeight method i dont had:

((Node)terrain).updateModelBound();

like SDK had.

for some reason it solve this issue. i thought Bound is not related here, because its general bound, not precise. But looks like it affect this.

still not sure why it was possible near hills, but anyway im glad it work.

at least there is topic for people to know for future what is missing.


#3

For ray picking, the bound is checked first because it’s not required to check each individual triangle if the faster bound check already fails.


#4

thanks,

that i knew, but i thought that if raycast Hit terrain bounding, it will also check triangles that are out of this bound anyway(because it would already know to check this grid triangles).

anyway if i guess properly its BoundingBox each Grid on terrain, thats why near mountain i was able to raise terrain and on others not.