Normal edges from TerrainPatch

Hello guys, it’s me again :smiley:

I am working on an own try of a way to implement endless terrain. For that I use TerrainPatch objects that are dynamically loaded and saved. My first steps were promising but now I am stuck at the normal edges, showing gaps.
I tried copying the fixNormalEdges method from TerainPatch (can’t use it directly since it’s protected) but it does not work.
I am no pro in such things so it may seem noobish to try it that way, but some help would be really… helpful :wink:

Here is how it looks right now (view from the side):

Here is the code I copied from TerrainPatch (I just changed “this.” to “middle.”, rest stays the same):

protected void fixNormalEdges(TerrainPatch middle,
                            TerrainPatch right,
                            TerrainPatch bottom,
                            TerrainPatch top,
                            TerrainPatch left,
                            TerrainPatch bottomRight,
                            TerrainPatch bottomLeft,
                            TerrainPatch topRight,
                            TerrainPatch topLeft)
{
    Vector3f rootPoint = new Vector3f();
    Vector3f rightPoint = new Vector3f();
    Vector3f leftPoint = new Vector3f();
    Vector3f topPoint = new Vector3f();

    Vector3f bottomPoint = new Vector3f();

    Vector3f tangent = new Vector3f();
    Vector3f binormal = new Vector3f();
    Vector3f normal = new Vector3f();

    
    int s = middle.getSize()-1;
    
    if (right != null) { // right side,    works its way down
        for (int i=0; i<s+1; i++) {
            rootPoint.set(0, middle.getHeightmapHeight(s,i), 0);
            leftPoint.set(-1, middle.getHeightmapHeight(s-1,i), 0);
            rightPoint.set(1, right.getHeightmapHeight(1,i), 0);

            if (i == 0) { // top point
                bottomPoint.set(0, middle.getHeightmapHeight(s,i+1), 1);
                
                if (top == null) {
                    averageNormalsTangents(null, rootPoint, leftPoint, bottomPoint, rightPoint,  normal, tangent, binormal);
                    setInBuffer(middle.getMesh(), s, normal, tangent, binormal);
                    setInBuffer(right.getMesh(), 0, normal, tangent, binormal);
                } else {
                    topPoint.set(0, top.getHeightmapHeight(s,s-1), -1);
                    
                    averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint,normal, tangent, binormal);
                    setInBuffer(middle.getMesh(), s, normal, tangent, binormal);
                    setInBuffer(right.getMesh(), 0, normal, tangent, binormal);
                    setInBuffer(top.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);
                    
                    if (topRight != null) {
                //        setInBuffer(topRight.getMesh(), (s+1)*s, normal, tangent, binormal);
                    }
                }
            } else if (i == s) { // bottom point
                topPoint.set(0, middle.getHeightmapHeight(s,s-1), -1);
                
                if (bottom == null) {
                    averageNormalsTangents(topPoint, rootPoint, leftPoint, null, rightPoint, normal, tangent, binormal);
                    setInBuffer(middle.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);
                    setInBuffer(right.getMesh(), (s+1)*(s), normal, tangent, binormal);
                } else {
                    bottomPoint.set(0, bottom.getHeightmapHeight(s,1), 1);
                    averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);
                    setInBuffer(middle.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);
                    setInBuffer(right.getMesh(), (s+1)*s, normal, tangent, binormal);
                    setInBuffer(bottom.getMesh(), s, normal, tangent, binormal);
                    
                    if (bottomRight != null) {
               //         setInBuffer(bottomRight.getMesh(), 0, normal, tangent, binormal);
                    }
                }
            } else { // all in the middle
                topPoint.set(0, middle.getHeightmapHeight(s,i-1), -1);
                bottomPoint.set(0, middle.getHeightmapHeight(s,i+1), 1);
                averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);
                setInBuffer(middle.getMesh(), (s+1)*(i+1)-1, normal, tangent, binormal);
                setInBuffer(right.getMesh(), (s+1)*(i), normal, tangent, binormal);
            }
        }
    }

    if (left != null) { // left side,    works its way down
        for (int i=0; i<s+1; i++) {
            rootPoint.set(0, middle.getHeightmapHeight(0,i), 0);
            leftPoint.set(-1, left.getHeightmapHeight(s-1,i), 0);
            rightPoint.set(1, middle.getHeightmapHeight(1,i), 0);
            
            if (i == 0) { // top point
                bottomPoint.set(0, middle.getHeightmapHeight(0,i+1), 1);
                
                if (top == null) {
                    averageNormalsTangents(null, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);
                    setInBuffer(middle.getMesh(), 0, normal, tangent, binormal);
                    setInBuffer(left.getMesh(), s, normal, tangent, binormal);
                } else {
                    topPoint.set(0, top.getHeightmapHeight(0,s-1), -1);
                    
                    averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);
                    setInBuffer(middle.getMesh(), 0, normal, tangent, binormal);
                    setInBuffer(left.getMesh(), s, normal, tangent, binormal);
                    setInBuffer(top.getMesh(), (s+1)*s, normal, tangent, binormal);
                    
                    if (topLeft != null) {
                 //       setInBuffer(topLeft.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);
                    }
                }
            } else if (i == s) { // bottom point
                topPoint.set(0, middle.getHeightmapHeight(0,i-1), -1);
                
                if (bottom == null) {
                    averageNormalsTangents(topPoint, rootPoint, leftPoint, null, rightPoint, normal, tangent, binormal);
                    setInBuffer(middle.getMesh(), (s+1)*(s), normal, tangent, binormal);
                    setInBuffer(left.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);
                } else {
                    bottomPoint.set(0, bottom.getHeightmapHeight(0,1), 1);
                    
                    averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);
                    setInBuffer(middle.getMesh(), (s+1)*(s), normal, tangent, binormal);
                    setInBuffer(left.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);
                    setInBuffer(bottom.getMesh(), 0, normal, tangent, binormal);
                    
                    if (bottomLeft != null) {
                 //       setInBuffer(bottomLeft.getMesh(), s, normal, tangent, binormal);
                    }
                }
            } else { // all in the middle
                topPoint.set(0, middle.getHeightmapHeight(0,i-1), -1);
                bottomPoint.set(0, middle.getHeightmapHeight(0,i+1), 1);
                
                averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);
                setInBuffer(middle.getMesh(), (s+1)*(i), normal, tangent, binormal);
                setInBuffer(left.getMesh(), (s+1)*(i+1)-1, normal, tangent, binormal);
            }
        }
    }

    if (top != null) { // top side,    works its way right
        for (int i=0; i<s+1; i++) {
            rootPoint.set(0, middle.getHeightmapHeight(i,0), 0);
            topPoint.set(0, top.getHeightmapHeight(i,s-1), -1);
            bottomPoint.set(0, middle.getHeightmapHeight(i,1), 1);
            
            if (i == 0) { // left corner
                // handled by left side pass
                
            } else if (i == s) { // right corner
                
                // handled by this patch when it does its right side
                
            } else { // all in the middle
                leftPoint.set(-1, middle.getHeightmapHeight(i-1,0), 0);
                rightPoint.set(1, middle.getHeightmapHeight(i+1,0), 0);
                averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);
                setInBuffer(middle.getMesh(), i, normal, tangent, binormal);
                setInBuffer(top.getMesh(), (s+1)*(s)+i, normal, tangent, binormal);
            }
        }
        
    }
    
    if (bottom != null) { // bottom side,    works its way right
        for (int i=0; i<s+1; i++) {
            rootPoint.set(0, middle.getHeightmapHeight(i,s), 0);
            topPoint.set(0, middle.getHeightmapHeight(i,s-1), -1);
            bottomPoint.set(0, bottom.getHeightmapHeight(i,1), 1);

            if (i == 0) { // left
                // handled by the left side pass
                
            } else if (i == s) { // right
                
                // handled by the right side pass
                
            } else { // all in the middle
                leftPoint.set(-1, middle.getHeightmapHeight(i-1,s), 0);
                rightPoint.set(1, middle.getHeightmapHeight(i+1,s), 0);
                averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);
                setInBuffer(middle.getMesh(), (s+1)*(s)+i, normal, tangent, binormal);
                setInBuffer(bottom.getMesh(), i, normal, tangent, binormal);
            }
        }
        
    }
}

private void setInBuffer(Mesh mesh, int index, Vector3f normal, Vector3f tangent, Vector3f binormal) {
    VertexBuffer NB = mesh.getBuffer(VertexBuffer.Type.Normal);
    VertexBuffer TB = mesh.getBuffer(VertexBuffer.Type.Tangent);
    VertexBuffer BB = mesh.getBuffer(VertexBuffer.Type.Binormal);
    BufferUtils.setInBuffer(normal, (FloatBuffer)NB.getData(), index);
    BufferUtils.setInBuffer(tangent, (FloatBuffer)TB.getData(), index);
    BufferUtils.setInBuffer(binormal, (FloatBuffer)BB.getData(), index);
    NB.setUpdateNeeded();
    TB.setUpdateNeeded();
    BB.setUpdateNeeded();
}

protected void averageNormalsTangents(
        Vector3f topPoint,
        Vector3f rootPoint,
        Vector3f leftPoint, 
        Vector3f bottomPoint, 
        Vector3f rightPoint,
        Vector3f normal,
        Vector3f tangent,
        Vector3f binormal)
{
    Vector3f scale = getWorldScale();
    
    Vector3f n1 = new Vector3f(0,0,0);
    if (topPoint != null && leftPoint != null) {
        n1.set(calculateNormal(topPoint.mult(scale), rootPoint.mult(scale), leftPoint.mult(scale)));
    }
    Vector3f n2 = new Vector3f(0,0,0);
    if (leftPoint != null && bottomPoint != null) {
        n2.set(calculateNormal(leftPoint.mult(scale), rootPoint.mult(scale), bottomPoint.mult(scale)));
    }
    Vector3f n3 = new Vector3f(0,0,0);
    if (rightPoint != null && bottomPoint != null) {
        n3.set(calculateNormal(bottomPoint.mult(scale), rootPoint.mult(scale), rightPoint.mult(scale)));
    }
    Vector3f n4 = new Vector3f(0,0,0);
    if (rightPoint != null && topPoint != null) {
        n4.set(calculateNormal(rightPoint.mult(scale), rootPoint.mult(scale), topPoint.mult(scale)));
    }
    
    //if (bottomPoint != null && rightPoint != null && rootTex != null && rightTex != null && bottomTex != null)
    //    LODGeomap.calculateTangent(new Vector3f[]{rootPoint.mult(scale),rightPoint.mult(scale),bottomPoint.mult(scale)}, new Vector2f[]{rootTex,rightTex,bottomTex}, tangent, binormal);

    normal.set(n1.add(n2).add(n3).add(n4).normalize());
    
    tangent.set(normal.cross(new Vector3f(0,0,1)).normalize());
    binormal.set(new Vector3f(1,0,0).cross(normal).normalize());
}

private Vector3f calculateNormal(Vector3f firstPoint, Vector3f rootPoint, Vector3f secondPoint) {
    Vector3f normal = new Vector3f();
    normal.set(firstPoint).subtractLocal(rootPoint)
              .crossLocal(secondPoint.subtract(rootPoint)).normalizeLocal();
    return normal;
}

Here is the part where I try to connect the patches:

/**
 * Attaches a terrain patch to the node. Creates a new patch if necessary
 * @param pos the patch pos
 */
public void attachTerrain(Vector2f pos) throws Exception{
    if(!isCreated(pos))
        this.createTerrain(pos);
    
    TerrainPatch patch = (TerrainPatch) importer.load(new File(cachePath + posToName(pos)));
    this.fixNormalEdges(patch,
            this.getPatchAtPatchPos(pos.add(new Vector2f(1, 0))),
            this.getPatchAtPatchPos(pos.add(new Vector2f(0, -1))),
            this.getPatchAtPatchPos(pos.add(new Vector2f(0, 1))),
            this.getPatchAtPatchPos(pos.add(new Vector2f(-1, 0))),
            this.getPatchAtPatchPos(pos.add(new Vector2f(1, -1))),
            this.getPatchAtPatchPos(pos.add(new Vector2f(-1, -1))),
            this.getPatchAtPatchPos(pos.add(new Vector2f(1, 1))),
            this.getPatchAtPatchPos(pos.add(new Vector2f(-1, 1))));
    patch.setMaterial(matNormal);
    this.attachChild(patch);
}

Thank you to anyone who takes the time to help me :slight_smile: