Bad looking lighting after optimising voxel mesh

Is this…

…the expected behavior for lighting this…

…mesh?

A first guess: do you compute the normals correctly?
Without more information (the mesh itself, code that generates it, …), I can’t say much.

I had initially thought that the normals would be to blame also, but I do not see any issue with them (unless I’m being stupid)

Ok SSCCE example here:

import com.jme3.app.SimpleApplication;
import com.jme3.app.StatsAppState;
import com.jme3.light.PointLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.VertexBuffer;
import com.jme3.system.AppSettings;
import com.jme3.util.BufferUtils;

import java.util.ArrayList;
import java.util.List;

/**
 *
 */
public class TestApplication extends SimpleApplication {

    public static void main(String[] args) {
        TestApplication game = new TestApplication();
        game.setShowSettings(false);

        AppSettings settings = new AppSettings(true);
        settings.setResizable(true);
        settings.setSamples(4);
        game.setSettings(settings);

        game.start();
    }

    private TestApplication() {
        super(new StatsAppState());
    }

    @Override
    public void simpleInitApp() {
        inputManager.setCursorVisible(true);

        TestGeometry geom = new TestGeometry("test-mesh");
        Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
        mat.setColor("Diffuse", ColorRGBA.White);
        geom.setMaterial(mat);
        rootNode.attachChild(geom);

        PointLight pl = new PointLight();
        pl.setColor(ColorRGBA.White);
        pl.setRadius(10);
        pl.getPosition().set(3f, 5f, 3f);
        rootNode.addLight(pl);

        getCamera().setLocation(new Vector3f(3f, 5f, 10f));
        getCamera().lookAt(new Vector3f(3f, 0, 3f), Vector3f.UNIT_Y);
    }

    public static class TestGeometry extends Geometry {

        ArrayList<Vector3f> vertices = new ArrayList<>();
        ArrayList<Vector3f> normals = new ArrayList<>();
        ArrayList<Integer> indexes = new ArrayList<>();

        public TestGeometry(String name) {
            super(name);
            addQuad(0, 0, 0, 2, 5);
            addQuad(2, 0, 0, 3, 3);
            addQuad(2, 0, 3, 3, 2);
            addQuad(0, 0, 5, 5, 1);
            addQuad(5, 0, 0, 1, 6);
            create();
        }

        private void addQuad(int x, int y, int z, int width, int height) {
            int count = vertices.size();

            vertices.add(new Vector3f(x,         y, z));
            vertices.add(new Vector3f(x + width, y, z));
            vertices.add(new Vector3f(x,         y, z + height));
            vertices.add(new Vector3f(x + width, y, z + height));

            normals.add(new Vector3f(0, 1, 0));
            normals.add(new Vector3f(0, 1, 0));
            normals.add(new Vector3f(0, 1, 0));
            normals.add(new Vector3f(0, 1, 0));

            indexes.add(count + 1);
            indexes.add(count);
            indexes.add(count + 2);
            indexes.add(count + 2);
            indexes.add(count + 3);
            indexes.add(count + 1);
        }

        private void create() {
            mesh = new Mesh();
            mesh.setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(convertVector3fList(vertices)));
            mesh.setBuffer(VertexBuffer.Type.Normal, 3, BufferUtils.createFloatBuffer(convertVector3fList(normals)));
            mesh.setBuffer(VertexBuffer.Type.Index, 3, BufferUtils.createIntBuffer(convertIntegerList(indexes)));
            mesh.updateBound();
        }

        public static int[] convertIntegerList(List<Integer> list) {
            int[] ret = new int[list.size()];
            for (int i = 0; i < ret.length; i++) {
                ret[i] = list.get(i).intValue();
            }
            return ret;
        }

        public static Vector3f[] convertVector3fList(List<Vector3f> list) {
            Vector3f[] ret = new Vector3f[list.size()];
            for (int i = 0; i < ret.length; i++) {
                ret[i] = list.get(i);
            }
            return ret;
        }

    }

}

UPDATE

Sorry my initial example didn’t work ^^. Fixed now.

UPDATE

For those who don’t want to run the code, it produces this:

Use TangentBinormalGenerator.

Ok so I used the TangentBinormalGenerator but it didn’t change anything. I’m not 100% sure I used it correctly.

Updated code:

import com.jme3.app.SimpleApplication;
import com.jme3.app.StatsAppState;
import com.jme3.light.PointLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.VertexBuffer;
import com.jme3.system.AppSettings;
import com.jme3.util.BufferUtils;
import com.jme3.util.TangentBinormalGenerator;

import java.util.ArrayList;
import java.util.List;

/**
 *
 */
public class TestApplication extends SimpleApplication {

    public static void main(String[] args) {
        TestApplication game = new TestApplication();
        game.setShowSettings(false);

        AppSettings settings = new AppSettings(true);
        settings.setResizable(true);
        settings.setSamples(4);
        game.setSettings(settings);

        game.start();
    }

    private TestApplication() {
        super(new StatsAppState());
    }

    @Override
    public void simpleInitApp() {
        inputManager.setCursorVisible(true);

        TestGeometry geom = new TestGeometry("test-mesh");
        Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
        mat.setColor("Diffuse", ColorRGBA.White);
        geom.setMaterial(mat);
        rootNode.attachChild(geom);

        PointLight pl = new PointLight();
        pl.setColor(ColorRGBA.White);
        pl.setRadius(10);
        pl.getPosition().set(3f, 5f, 3f);
        rootNode.addLight(pl);

        getCamera().setLocation(new Vector3f(3f, 5f, 10f));
        getCamera().lookAt(new Vector3f(3f, 0, 3f), Vector3f.UNIT_Y);
    }

    public static class TestGeometry extends Geometry {

        ArrayList<Vector3f> vertices = new ArrayList<>();
        ArrayList<Vector3f> normals = new ArrayList<>();
        ArrayList<Vector2f> texCoord = new ArrayList<>();
        ArrayList<Integer> indexes = new ArrayList<>();

        public TestGeometry(String name) {
            super(name);
            addQuad(0, 0, 0, 2, 5);
            addQuad(2, 0, 0, 3, 3);
            addQuad(2, 0, 3, 3, 2);
            addQuad(0, 0, 5, 5, 1);
            addQuad(5, 0, 0, 1, 6);
            create();
        }

        private void addQuad(int x, int y, int z, int width, int height) {
            int count = vertices.size();

            vertices.add(new Vector3f(x,         y, z));
            vertices.add(new Vector3f(x + width, y, z));
            vertices.add(new Vector3f(x,         y, z + height));
            vertices.add(new Vector3f(x + width, y, z + height));

            normals.add(new Vector3f(0, 1, 0));
            normals.add(new Vector3f(0, 1, 0));
            normals.add(new Vector3f(0, 1, 0));
            normals.add(new Vector3f(0, 1, 0));

            texCoord.add(new Vector2f(0, 0));
            texCoord.add(new Vector2f(1, 0));
            texCoord.add(new Vector2f(0, 1));
            texCoord.add(new Vector2f(1, 1));

            indexes.add(count + 1);
            indexes.add(count);
            indexes.add(count + 2);
            indexes.add(count + 2);
            indexes.add(count + 3);
            indexes.add(count + 1);
        }

        private void create() {
            mesh = new Mesh();
            mesh.setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(convertVector3fList(vertices)));
            mesh.setBuffer(VertexBuffer.Type.Normal, 3, BufferUtils.createFloatBuffer(convertVector3fList(normals)));
            mesh.setBuffer(VertexBuffer.Type.TexCoord, 2, BufferUtils.createFloatBuffer(convertVector2fList(texCoord)));
            mesh.setBuffer(VertexBuffer.Type.Index, 3, BufferUtils.createIntBuffer(convertIntegerList(indexes)));
            mesh.updateBound();
            if (mesh.getBuffer(VertexBuffer.Type.Tangent) != null) System.out.println("Has tangent buffer");
            else System.out.println("No tangent buffer");
            TangentBinormalGenerator.generate(mesh);
            if (mesh.getBuffer(VertexBuffer.Type.Tangent) != null) System.out.println("Has tangent buffer");
            else System.out.println("No tangent buffer");
        }

        public static int[] convertIntegerList(List<Integer> list) {
            int[] ret = new int[list.size()];
            for (int i = 0; i < ret.length; i++) {
                ret[i] = list.get(i).intValue();
            }
            return ret;
        }

        public static Vector2f[] convertVector2fList(List<Vector2f> list) {
            Vector2f[] ret = new Vector2f[list.size()];
            for (int i = 0; i < ret.length; i++) {
                ret[i] = list.get(i);
            }
            return ret;
        }

        public static Vector3f[] convertVector3fList(List<Vector3f> list) {
            Vector3f[] ret = new Vector3f[list.size()];
            for (int i = 0; i < ret.length; i++) {
                ret[i] = list.get(i);
            }
            return ret;
        }

    }

}

Tangents never ever come into play unless you are using normal maps or bump maps. So go ahead and ignore that.

Are you using JME 3.0 or 3.1?

It looks like you are using a point light… per-fragment lighting is approximated over the whole surface linearly from values calculated at the corners. The longer the span, the more approximate it will be in the middle. Normally this doesn’t show up… but when you have two different span sizes next to each other then it will be noticeable.

If you plan to support a “minecraft number” of lights then this is the wrong approach, anyway. You have to bake in lighting for that to work or go with deferred rendering or some other approach that can handle thousands of lights.

JME 3.1.beta-1

I had suspected it would be something along these lines.

I actually just have directional light and ambient light in my scene I I was interested only in what point lights would look like when I stumbled upon it. No real intention of adding any.

Glad To know the reason though as I will bear this in mind for the future. My main concern was checking that I hadn’t done something silly! :slight_smile: