[Solved] Unshaded plane works, but textured plane fails

Hello Monkeys,

tl;dr: If you have little time, I am happy with your idea: Is that a major problem, such as a problem in maths or general understanding, or is it just a little typing mistake that I have to find somewhere? Thanks!

I am currently experimenting with different generated planes, as a foundation for a subsequent generated terrain.

That plane I want to generate (numbers are vertex indexes):

I think I’ve made code that is well understandable, through the using of named variables and console outputs that make transparent what the code did there.

The last change however needs a comment line: As I want the plane to generate from 0 towards -z direction, I’ve added “-1 * …” to the z-Coordinates of vertex position generation, and reversed the triangle indexes (so now the triangles are specified clockwise). Just to minimize confusion :wink:

Oh, also the green numbers are the “outer” vertices and the brown ones the “inner” vertices.

public void initialize(AppStateManager stateManager, Application app) {
        this.app = (SimpleApplication) app;
        this.assetManager = this.app.getAssetManager();
        this.rootNode = this.app.getRootNode();
        this.flyCam = this.app.getFlyByCamera();
        this.cam = this.app.getCamera();
        this.inputManager = this.app.getInputManager();
        
        /** Specify variables for the mesh: */      
        
        float vertDist = 1f;
        int nX = 4;     // Quads count
        int nZ = 3;     // Quads count
        int nOuter = (nX+1) * (nZ+1);   // outer verts are always one more
        int nInner = nX * nZ;
        int nGes = nOuter + nInner;
        
        int n;
        
        /** --- Create the mesh --- */
        
        // The mesh object
        Mesh mesh = new Mesh();
        
        // Vertex Coordinates
        Vector3f[] outerVerts = new Vector3f[nOuter];
        Vector3f[] innerVerts = new Vector3f[nInner];
        Vector3f[] vertices = new Vector3f[nGes];
        
        for (int i = 0; i < nOuter; i++) {
            outerVerts[i] = new Vector3f((i%(nX+1)) * vertDist, 0f * vertDist, -1 * ((int)(i/(nX+1))) * vertDist);
        }
        for (int i = 0; i < nInner; i++) {
            innerVerts[i] = new Vector3f(((i%nX) + 0.5f) * vertDist, 0f * vertDist, -1 * (((int)(i/nX)) + 0.5f) * vertDist);
        }
        
        n = 0;
        for (Vector3f vert : outerVerts) {
            vertices[n] = vert;
            n++;
        }
        for (Vector3f vert : innerVerts) {
            vertices[n] = vert;
            n++;
        }
        
        if (logMode == LogMode.All || logMode == LogMode.Vertices) {
            for (Vector3f vert : vertices) {
                System.out.println(vert);
            }
            System.out.println(outerVerts.length);
            System.out.println(innerVerts.length);
            System.out.println(vertices.length);
        }
        System.out.println("Vertices created successfully.");
        
        // Triangles: now clockwise
        int[] triangles = new int[12*4*3];    // 12 Quads à 4 Triangles à 3 Vertices
        n = 0;
        for (int i = 0; i < triangles.length; ) {
            // Quad by Quad: 4 Triangles consisting of 5 Vertices
            if((n+1)%(nX+1) == 0)
                n++;    // bc of this outer-vert-one-more-thing n has to be one additionally more per line
            int nCenter = nOuter+n - ((int)(n/(nX+1)));     // unfortunately, we have to subtract that, to access the innerVert-index
            int nTL = n;       // top-left
            int nTR = n+1;              // top-right
            int nBL = n+(nX+1);         // bottom-left
            int nBR = nBL+1;            // bottom-right
            
            triangles[i++] = nTL;
            triangles[i++] = nTR;
            triangles[i++] = nCenter;
            
            triangles[i++] = nBL;
            triangles[i++] = nTL;
            triangles[i++] = nCenter;
            
            triangles[i++] = nBR;
            triangles[i++] = nBL;
            triangles[i++] = nCenter;
            
            triangles[i++] = nTR;
            triangles[i++] = nBR;
            triangles[i++] = nCenter;
            
            n++;
        }
        
        if (logMode == LogMode.All || logMode == LogMode.Triangles) {
            n = 1;
            for (int triangle : triangles) {
                System.out.print(triangle + " ");
                if (n%3 == 0)
                    System.out.println();
                n++;
            }
        }
        System.out.println("Triangles created successfully.");
        
        // Texture Coordinates
        Vector2f[] texCoord = new Vector2f[vertices.length];
        for (int i = 0; i < vertices.length; i++) {
//            texCoord[count] = new Vector2f(x*(1/vert_xmaxf), z*(1/vert_zmaxf));
            texCoord[i] = new Vector2f(vertices[i].x/(nX), vertices[i].z/(nZ));
        }
        
        if (logMode == LogMode.All || logMode == LogMode.TexCoords) {
            for (Vector2f texCoord1 : texCoord) {
                System.out.println(texCoord1);
            }
        }
        System.out.println("TexCoords created successfully.");
        
        // Normals
        float[] normals = new float[3*vertices.length];
        for (int i = 0; i < vertices.length; ) {
            normals[i++] = 0;
            normals[i++] = 1;
            normals[i++] = 0;
        }
        System.out.println("Normals created successfully.");
        
        // Set the Mesh Buffer
        mesh.setBuffer(VertexBuffer.Type.Normal, 3, BufferUtils.createFloatBuffer(normals));
        mesh.setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(vertices));
        mesh.setBuffer(VertexBuffer.Type.TexCoord, 2, BufferUtils.createFloatBuffer(texCoord));
        mesh.setBuffer(VertexBuffer.Type.Index,    3, BufferUtils.createIntBuffer(triangles));
        mesh.updateBound();     // pre-calculate the bounding volume
        
        
        /** --- Using the Mesh in a Scene --- */
        
        Geometry geo = new Geometry("OurMesh", mesh);   // using our custom mesh object
        mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
        mat.setTexture("DiffuseMap", assetManager.loadTexture("Textures/Anno2070_TitleScreen.jpg"));
//        mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
//        mat.setColor("Color", ColorRGBA.Blue);
        geo.setMaterial(mat);
        rootNode.attachChild(geo);

And the console output:

(0.0, 0.0, 0.0)
(1.0, 0.0, 0.0)
(2.0, 0.0, 0.0)
(3.0, 0.0, 0.0)
(4.0, 0.0, 0.0)
(0.0, 0.0, -1.0)
(1.0, 0.0, -1.0)
(2.0, 0.0, -1.0)
(3.0, 0.0, -1.0)
(4.0, 0.0, -1.0)
(0.0, 0.0, -2.0)
(1.0, 0.0, -2.0)
(2.0, 0.0, -2.0)
(3.0, 0.0, -2.0)
(4.0, 0.0, -2.0)
(0.0, 0.0, -3.0)
(1.0, 0.0, -3.0)
(2.0, 0.0, -3.0)
(3.0, 0.0, -3.0)
(4.0, 0.0, -3.0)
(0.5, 0.0, -0.5)
(1.5, 0.0, -0.5)
(2.5, 0.0, -0.5)
(3.5, 0.0, -0.5)
(0.5, 0.0, -1.5)
(1.5, 0.0, -1.5)
(2.5, 0.0, -1.5)
(3.5, 0.0, -1.5)
(0.5, 0.0, -2.5)
(1.5, 0.0, -2.5)
(2.5, 0.0, -2.5)
(3.5, 0.0, -2.5)
20
12
32
Vertices created successfully.
0 1 20 
5 0 20 
6 5 20 
1 6 20 
1 2 21 
6 1 21 
7 6 21 
2 7 21 
2 3 22 
7 2 22 
8 7 22 
3 8 22 
3 4 23 
8 3 23 
9 8 23 
4 9 23 
5 6 24 
10 5 24 
11 10 24 
6 11 24 
6 7 25 
11 6 25 
12 11 25 
7 12 25 
7 8 26 
12 7 26 
13 12 26 
8 13 26 
8 9 27 
13 8 27 
14 13 27 
9 14 27 
10 11 28 
15 10 28 
16 15 28 
11 16 28 
11 12 29 
16 11 29 
17 16 29 
12 17 29 
12 13 30 
17 12 30 
18 17 30 
13 18 30 
13 14 31 
18 13 31 
19 18 31 
14 19 31 
Triangles created successfully.
(0.0, 0.0)
(0.25, 0.0)
(0.5, 0.0)
(0.75, 0.0)
(1.0, 0.0)
(0.0, -0.33333334)
(0.25, -0.33333334)
(0.5, -0.33333334)
(0.75, -0.33333334)
(1.0, -0.33333334)
(0.0, -0.6666667)
(0.25, -0.6666667)
(0.5, -0.6666667)
(0.75, -0.6666667)
(1.0, -0.6666667)
(0.0, -1.0)
(0.25, -1.0)
(0.5, -1.0)
(0.75, -1.0)
(1.0, -1.0)
(0.125, -0.16666667)
(0.375, -0.16666667)
(0.625, -0.16666667)
(0.875, -0.16666667)
(0.125, -0.5)
(0.375, -0.5)
(0.625, -0.5)
(0.875, -0.5)
(0.125, -0.8333333)
(0.375, -0.8333333)
(0.625, -0.8333333)
(0.875, -0.8333333)
TexCoords created successfully.
Normals created successfully.
Toggle Wireframe
BUILD SUCCESSFUL (total time: 13 seconds)

As I can see, everything generated right, including the texCoords. However, because of the following problem I suppose the error has to be somewhere there.

If I use the unshaded material, the result looks like that:

However if I use the lighting.j3md, the result is that:
(Edit: To see the lines I may suggest to click and open the picture in fullscreen - the lines are thin and, when scaled down, only barely seen).

There aren’t even triangles there anymore, just lines and squares. And If I check the console output, everything should be fine. Hmmmm.

So what is your general perception? Is that a major problem, such as a problem in maths or general understanding, or is it just a little typing mistake that I have to find somewhere?

Thanks!

Let there be light. because you use lighting shader.

rootNode.addLight(new AmbientLight());

Edit:
Set material wireframe to false, that’s why only lines can be seen when have diffuse map.
Set texture.warp to WrapMode.Repeat.

    Texture tex = assetManager.loadTexture("Textures/aa.bmp");
    tex.setWrap(WrapMode.Repeat);
    mat.setTexture("DiffuseMap", tex);

I tried your code and it works fine for me.

I’ve only captured the Wireframed versions because, with the Unshaded material you see a nice blue plane, and with the Lighting material you don’t see anything except a black nothing. Such as the Wireframe screens suggest, the one having a nice triangle grid, and the other, after switching to Lighting.j3md, being a complete mess.

I have also switched to another texture (a brick texture). That did not work either for me. Tommorrow I will try the same you did - copy and paste my code into a new AppState and test if that works (sometimes such things do magic). But strangely, it does not work until now for me.

Well, one thing is that we can’t see in your code where you added the light. So we are left to assume many things about the dozen things that could be wrong other than the posted code.

Like @yan said above: add light, without light you see nothing :slight_smile:

Hint: The next post came up with the problem’s solution. But there are still a few questions. If you can answer them, it would be really nice! :slight_smile:

—Original post for purposes:—

I have a light, but I forgot to show the code for it here. So here is the whole code:

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package MapCreation;

import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.asset.AssetManager;
import com.jme3.input.FlyByCamera;
import com.jme3.input.InputManager;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.scene.CameraNode;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.VertexBuffer;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Quad;
import com.jme3.shader.VarType;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;
import com.jme3.util.BufferUtils;
import static java.lang.Math.floor;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.Random;
import org.omg.PortableInterceptor.SYSTEM_EXCEPTION;

/**
 *
 * @author IrApp
 */
public class AppState_Test_Mesh_4 extends AbstractAppState {
    
    SimpleApplication app;
    AssetManager assetManager;
    Node rootNode;
    FlyByCamera flyCam;
    Camera cam;
    InputManager inputManager;
    
    Material mat;
    boolean isWireframe = false;
    private enum LogMode {
        None,
        All,
        Vertices,
        Triangles,
        TexCoords,
        Normals
    };
    final private LogMode logMode = LogMode.All;
    
    @Override
    public void initialize(AppStateManager stateManager, Application app) {
        this.app = (SimpleApplication) app;
        this.assetManager = this.app.getAssetManager();
        this.rootNode = this.app.getRootNode();
        this.flyCam = this.app.getFlyByCamera();
        this.cam = this.app.getCamera();
        this.inputManager = this.app.getInputManager();
        
        /** Specify variables for the mesh: */      
        
        float vertDist = 1f;
        int nX = 4;     // Quads count
        int nZ = 3;     // Quads count
        int nOuter = (nX+1) * (nZ+1);   // outer verts are always one more
        int nInner = nX * nZ;
        int nGes = nOuter + nInner;
        
        int n;
        
        /** --- Create the mesh --- */
        
        // The mesh object
        Mesh mesh = new Mesh();
        
        // Vertex Coordinates
        Vector3f[] outerVerts = new Vector3f[nOuter];
        Vector3f[] innerVerts = new Vector3f[nInner];
        Vector3f[] vertices = new Vector3f[nGes];
        
        for (int i = 0; i < nOuter; i++) {
            outerVerts[i] = new Vector3f((i%(nX+1)) * vertDist, 0f * vertDist, -1 * ((int)(i/(nX+1))) * vertDist);
        }
        for (int i = 0; i < nInner; i++) {
            innerVerts[i] = new Vector3f(((i%nX) + 0.5f) * vertDist, 0f * vertDist, -1 * (((int)(i/nX)) + 0.5f) * vertDist);
        }
        
        n = 0;
        for (Vector3f vert : outerVerts) {
            vertices[n] = vert;
            n++;
        }
        for (Vector3f vert : innerVerts) {
            vertices[n] = vert;
            n++;
        }
        
        if (logMode == LogMode.All || logMode == LogMode.Vertices) {
            for (Vector3f vert : vertices) {
                System.out.println(vert);
            }
            System.out.println(outerVerts.length);
            System.out.println(innerVerts.length);
            System.out.println(vertices.length);
        }
        System.out.println("Vertices created successfully.");
        
        // Triangles: now clockwise
        int[] triangles = new int[12*4*3];    // 12 Quads à 4 Triangles à 3 Vertices
        n = 0;
        for (int i = 0; i < triangles.length; ) {
            // Quad by Quad: 4 Triangles consisting of 5 Vertices
            if((n+1)%(nX+1) == 0)
                n++;    // bc of this outer-vert-one-more-thing n has to be one additionally more per line
            int nCenter = nOuter+n - ((int)(n/(nX+1)));     // unfortunately, we have to subtract that, to access the innerVert-index
            int nTL = n;       // top-left
            int nTR = n+1;              // top-right
            int nBL = n+(nX+1);         // bottom-left
            int nBR = nBL+1;            // bottom-right
            
            triangles[i++] = nTL;
            triangles[i++] = nTR;
            triangles[i++] = nCenter;
            
            triangles[i++] = nBL;
            triangles[i++] = nTL;
            triangles[i++] = nCenter;
            
            triangles[i++] = nBR;
            triangles[i++] = nBL;
            triangles[i++] = nCenter;
            
            triangles[i++] = nTR;
            triangles[i++] = nBR;
            triangles[i++] = nCenter;
            
            n++;
        }
        
        if (logMode == LogMode.All || logMode == LogMode.Triangles) {
            n = 1;
            for (int triangle : triangles) {
                System.out.print(triangle + " ");
                if (n%3 == 0)
                    System.out.println();
                n++;
            }
        }
        System.out.println("Triangles created successfully.");
        
        // Texture Coordinates
        Vector2f[] texCoord = new Vector2f[vertices.length];
        for (int i = 0; i < vertices.length; i++) {
//            texCoord[count] = new Vector2f(x*(1/vert_xmaxf), z*(1/vert_zmaxf));
            texCoord[i] = new Vector2f(vertices[i].x/(nX), vertices[i].z/(nZ));
        }
        
        if (logMode == LogMode.All || logMode == LogMode.TexCoords) {
            for (Vector2f texCoord1 : texCoord) {
                System.out.println(texCoord1);
            }
        }
        System.out.println("TexCoords created successfully.");
        
        // Normals
        float[] normals = new float[3*vertices.length];
        for (int i = 0; i < vertices.length; ) {
            normals[i++] = 0;
            normals[i++] = 1;
            normals[i++] = 0;
        }
        System.out.println("Normals created successfully.");
        
        // Set the Mesh Buffer
        mesh.setBuffer(VertexBuffer.Type.Normal, 3, BufferUtils.createFloatBuffer(normals));
        mesh.setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(vertices));
        mesh.setBuffer(VertexBuffer.Type.TexCoord, 2, BufferUtils.createFloatBuffer(texCoord));
        mesh.setBuffer(VertexBuffer.Type.Index,    3, BufferUtils.createIntBuffer(triangles));
        mesh.updateBound();     // pre-calculate the bounding volume
        
        
        /** --- Using the Mesh in a Scene --- */
        
        Geometry geo = new Geometry("OurMesh", mesh);   // using our custom mesh object
        mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
        mat.setTexture("DiffuseMap", assetManager.loadTexture("Textures/Anno2070_TitleScreen.jpg"));
//        mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
//        mat.setColor("Color", ColorRGBA.Blue);
        geo.setMaterial(mat);
        rootNode.attachChild(geo);
        
        // MeshQ to show that lighting should normally work:
        Mesh meshQ = new Mesh();
        meshQ.setBuffer(VertexBuffer.Type.Normal, 3, BufferUtils.createFloatBuffer(
            new float[] {
                0,1,0,
                0,1,0,
                0,1,0,
                0,1,0
            }));
        meshQ.setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(
            new float[] {
                0,0,0,
                1,0,0,
                0,0,1,
                1,0,1
            }));
        meshQ.setBuffer(VertexBuffer.Type.TexCoord, 2, BufferUtils.createFloatBuffer(
            new float[] {
                0,1,
                1,1,
                0,0,
                1,0
            }));
        meshQ.setBuffer(VertexBuffer.Type.Index,    3, BufferUtils.createIntBuffer(
            new int[] {
                0,2,1,
                1,2,3
            }));
        meshQ.updateBound();     // pre-calculate the bounding volume
        
        Geometry geoQ = new Geometry("Quad", meshQ);
        Material matQ = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
        matQ.setTexture("DiffuseMap", assetManager.loadTexture("Textures/Concept_2TriQuadPlane_nmbrs.png"));
        geoQ.setMaterial(matQ);
        geoQ.move(10f,0f,0f);
        rootNode.attachChild(geoQ);
        
        DirectionalLight sun = new DirectionalLight();
        //sun.setDirection(new Vector3f(1,0,-2).normalizeLocal());
        sun.setColor(ColorRGBA.White);
        rootNode.addLight(sun);
        
        flyCam.setMoveSpeed(800f);
//        cam.setLocation(new Vector3f(0f,50f,0f));
        cam.setFrustumFar(100000);
//        cam.setFrustumNear(2);
        
        inputManager.addMapping("slowMotion", new KeyTrigger(KeyInput.KEY_LCONTROL));
        inputManager.addMapping("fastMotion", new KeyTrigger(KeyInput.KEY_LSHIFT));
        inputManager.addMapping("toggleWireframe", new KeyTrigger(KeyInput.KEY_T));
        inputManager.addListener(actionListener, "slowMotion", "fastMotion", "toggleWireframe");
    }
    
    private ActionListener actionListener = new ActionListener() {

        @Override
        public void onAction(String name, boolean isPressed, float tpf) {
            if (name.equals("slowMotion") && isPressed)
            {
                flyCam.setMoveSpeed(5f);
            }
            if (name.equals("slowMotion") && !isPressed)
            {
                flyCam.setMoveSpeed(20f);
            }
            if (name.equals("fastMotion") && isPressed)
            {
                flyCam.setMoveSpeed(200f);
            }
            if (name.equals("fastMotion") && !isPressed)
            {
                flyCam.setMoveSpeed(20f);
            }
            if (name.equals("toggleWireframe") && isPressed)
            {
                isWireframe = !isWireframe;
                mat.getAdditionalRenderState().setWireframe(isWireframe);
                System.out.println("Toggle Wireframe");
            }
        }
    };
    
}

To show that the lighting normally works, I’ve quickly set up an additional Quad mesh (“meshQ”) that works and put it on the right.

Here are the missing screenshots:

Unshaded.j3md

Lighting.j3md

The wireframe screens are in my startpost accordingly: Where you can see nothing, the mesh structure is different just because the using of the lighting.j3md material definition? Unshaded does not use the texCoords I guess, therefore my thoughts if the problem is in the generation of the texCoords (because lighting.j3md uses them and then it destroys the mesh structure, anyway).

The plane on the right works very well :smiley:

Now I hope you have the whole picture. Thanks for your effort :sunflower:

Important: Now I have added the rootNode.addLight(new AmbientLight()); and suddenly everything shows up.

Wireframe mode:

Here the non-Wireframe screenshot:

And if I delete all lights, then I see nothing - even in Wareframe render mode. Then, the right white plane also disappears.

That is the conclusion after all. Wireframe mode in games as I know usually was a kind of “xray” thing: You can always see it. I didn’t know that it also needs a light to render. But why did the directional light sun not work first? A directional light should light everything in general.

A few questions, but after all, I thank you all very much for helping me to that englighting understandment. The mess with the texture on the plane I can work out myself. I have already done this with a few planes :slight_smile:

The questions that keeps, why does the directional sun-light do not work?

The code for it is that:

        DirectionalLight sun = new DirectionalLight();
        sun.setColor(ColorRGBA.White);
        rootNode.addLight(sun);

//      rootNode.addLight(new AmbientLight());   // why is that neccessary?

Plus, the sun light did work for the other plane. But not for my primary generated one.

I think because your sun comes in very very flat as y is 0. I’m not 100% sure.
Argh you changed your post I thought I saw that direction was 1, 0, -2, so forget what I said.

1 Like

Now I have that code:

        DirectionalLight sun = new DirectionalLight();
        sun.setDirection(new Vector3f(0f, -1f, 0f));    // facing down, or not? (y=-1)
        sun.setColor(ColorRGBA.White);
        rootNode.addLight(sun);

Solution: The same (black, in wireframe mode the following edges:)

Suspicous is, that if I add the AmbientLight, then those edges that you can see here, are twice as bright. Such as if the sun only lights those edges (and forgets the other ones):

Here is the real cause: Now I have added the output of the normals. The generated normals are these:

0.0 1.0 0.0 
0.0 1.0 0.0 
0.0 1.0 0.0 
0.0 1.0 0.0 
0.0 1.0 0.0 
0.0 1.0 0.0 
0.0 1.0 0.0 
0.0 1.0 0.0 
0.0 1.0 0.0 
0.0 1.0 0.0 
0.0 1.0 0.0 
0.0 0.0 0.0 
0.0 0.0 0.0 
0.0 0.0 0.0 
0.0 0.0 0.0 
0.0 0.0 0.0 
0.0 0.0 0.0 
0.0 0.0 0.0 
0.0 0.0 0.0 
0.0 0.0 0.0 
0.0 0.0 0.0 
0.0 0.0 0.0 
0.0 0.0 0.0 
0.0 0.0 0.0 
0.0 0.0 0.0 
0.0 0.0 0.0 
0.0 0.0 0.0 
0.0 0.0 0.0 
0.0 0.0 0.0 
0.0 0.0 0.0 
0.0 0.0 0.0 
0.0 0.0 0.0 
Normals created successfully.
BUILD SUCCESSFUL (total time: 3 seconds)

You see, that only the first few vertices have the correct y=1 normal vectors. And therefore, only those edges are lighted by the directional sun light. :sunny:

So that is definitely the reason for this complete mess here: The normals. Wo would have known that? :smiley:

To make it to the end: The mistake is here:

float[] normals = new float[3*vertices.length];
        for (int i = 0; i < vertices.length; ) {
            normals[i++] = 0;
            normals[i++] = 1;
            normals[i++] = 0;
        }

The 3*vertices.length in the array definition is right, but I should also copy it into the exit condition of the loop so that it is computed for the whole array:

float[] normals = new float[3*vertices.length];
        for (int i = 0; i < 3*vertices.length; ) {
            normals[i++] = 0;
            normals[i++] = 1;
            normals[i++] = 0;
        }

So this little 3* prefix (or the missing of it) was responsible for everything. Ahhhhh!!! :smiley:

I suppose the problem is about the normals.

I read you code once again very carefully and I noticed that:
Your mesh use vertex index clockwise, while the meshQ use index counter clockwise.
In this part:

		triangles[i++] = nTL;
		triangles[i++] = nTR;
		triangles[i++] = nCenter;

		triangles[i++] = nBL;
		triangles[i++] = nTL;
		triangles[i++] = nCenter;

		triangles[i++] = nBR;
		triangles[i++] = nBL;
		triangles[i++] = nCenter;

		triangles[i++] = nTR;
		triangles[i++] = nBR;
		triangles[i++] = nCenter;

I imaged it like this.

0     1
   5
2     3

Your triangle index is: 015, 205, 325, 135. It’s clockwise.

The meshQ’s index is like this:

0    1
2    3

It’s index is: 021, 123. It’s counter clockwise.

You set the both mesh normal to UNIT_Y(0,1,0)

Here comes the most important part: In OpenGL, the positive face of a triangle is decided by the index COUNTER CLOCKWISE. All the faces of your mesh has been culled.

In Lighting material, ambient light does not care about normals, so once you add an AmbientLight to the scene, you can see the model. But DirectionalLight calculate colors depends on the correct normals.

1 Like

Yeah, that was because I just inverted the z-coordinate in the generation algorithm - later - to generate towards the -z direction. Then the triangles faced downwards. Therefore I simply ordered them clockwise to turn them around, but I realized that this is not enough. I would have to adapt the other algorithms too, so I undid the -1 * of the z-axis and the ordering of my triangles.

Vertex generation now:

        // Vertex Coordinates
        Vector3f[] outerVerts = new Vector3f[nOuter];
        Vector3f[] innerVerts = new Vector3f[nInner];
        Vector3f[] vertices = new Vector3f[nGes];
        
        for (int i = 0; i < nOuter; i++) {
            outerVerts[i] = new Vector3f((i%(nX+1)) * vertDist, 0f * vertDist, ((int)(i/(nX+1))) * vertDist);
        }
        for (int i = 0; i < nInner; i++) {
            innerVerts[i] = new Vector3f(((i%nX) + 0.5f) * vertDist, 0f * vertDist, (((int)(i/nX)) + 0.5f) * vertDist);
        }
        
        n = 0;
        for (Vector3f vert : outerVerts) {
            vertices[n] = vert;
            n++;
        }
        for (Vector3f vert : innerVerts) {
            vertices[n] = vert;
            n++;
        }

Triangle generation now:

        // Triangles: counterclockwise
        int[] triangles = new int[12*4*3];    // 12 Quads à 4 Triangles à 3 Vertices
        n = 0;
        for (int i = 0; i < triangles.length; ) {
            // Quad by Quad: 4 Triangles consisting of 5 Vertices
            if((n+1)%(nX+1) == 0)
                n++;    // bc of this outer-vert-one-more-thing n has to be one additionally more per line
            int nCenter = nOuter+n - ((int)(n/(nX+1)));     // unfortunately, we have to subtract that, to access the innerVert-index
            int nTL = n;       // top-left
            int nTR = n+1;              // top-right
            int nBL = n+(nX+1);         // bottom-left
            int nBR = nBL+1;            // bottom-right
            
            triangles[i++] = nTR;
            triangles[i++] = nTL;
            triangles[i++] = nCenter;
            
            triangles[i++] = nTL;
            triangles[i++] = nBL;
            triangles[i++] = nCenter;
            
            triangles[i++] = nBL;
            triangles[i++] = nBR;
            triangles[i++] = nCenter;
            
            triangles[i++] = nBR;
            triangles[i++] = nTR;
            triangles[i++] = nCenter;
            
            n++;
        }

TexCoord generation now:
I had to reverse the z coordinate because UVs are specified from bottom to top, but my mesh is generated from top to bottom (normal z-direction is towards the player):

        Vector2f[] texCoord = new Vector2f[vertices.length];
        for (int i = 0; i < vertices.length; i++) {
            texCoord[i] = new Vector2f(vertices[i].x/(nX), 1 - vertices[i].z/(nZ));  // reversed z coords
        }

Result now:

2 Likes

A word of advice: when filling an array try to always arrange it to test the index against the target array’s length instead of some randomly calculated value. It would have saved you in this case…

for( int i = 0; i < normals.length; ) {
    normals[i++] = 0;
    normals[i++] = 1;
    normals[i++] = 0;
}

Also, more of a style issue but it makes the code a bit more readable if the whole loop is defined in one place…

for( int i = 0; i < normals.length; i += 3) {
    normals[i] = 0;
    normals[i+1] = 1;
    normals[i+2] = 0;
}

Even if other code ends up inside the loop or comments or other stuff, it is still plainly clear to the reader what is happening no matter where they look.

2 Likes

Thanks for the advices. It all sounds very reasonable, I will take it to heart.