Java OpenGL仅在四边形上渲染纹理的一个像素?

Java OpenGL仅在四边形上渲染纹理的一个像素?,java,opengl,textures,lwjgl,Java,Opengl,Textures,Lwjgl,我想用LWJGL从头开始创建一个具有纹理的四边形,但出于某种原因,只有第一个右上角的texel/像素应用于四边形 我也尝试了多种纹理,但仍然无法解决这个问题 以下是纹理: 以下是我得到的: ********************Application.java******************** package com.codebitcookie.engine; import static org.lwjgl.glfw.GLFW.*; import static org.lwjgl.

我想用LWJGL从头开始创建一个具有纹理的四边形,但出于某种原因,只有第一个右上角的texel/像素应用于四边形

我也尝试了多种纹理,但仍然无法解决这个问题

以下是纹理:

以下是我得到的:

********************Application.java********************

package com.codebitcookie.engine;

import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.system.MemoryUtil.NULL;

import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL11;

import com.codebitcookie.graphics.Color;

public class Application {
    
    private static final String TITLE = "2D Game Engine /w GUI";
    private static int width = 1280;
    private static int height = 720;
    private static long window;
    
    public static long init() {
        //check if GLFW is initialized if not we throw an exception
        if(!glfwInit()) {
            throw new IllegalStateException("GLFW failed to be initialized");
        }
        
//      GLFW.glfwDefaultWindowHints(); //equals to the two lines below
        glfwWindowHint(GLFW_VISIBLE, GL11.GL_FALSE); //makes the window visible
        glfwWindowHint(GLFW_RESIZABLE, GL11.GL_TRUE); //makes the window resizable
        
        //sets OpenGL to 3.3; major is 3.*** and minor is ***.3 = 3.3
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
        
        //for more info on these, check out: 
        //https://community.khronos.org/t/forward-compatible-vs-core-profile/65039
        //https://www.khronos.org/opengl/wiki/OpenGL_Context
        glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL11.GL_TRUE); //makes mac run nicely but restores all deprecated functionality
    
        window = glfwCreateWindow(width, height, TITLE, NULL, NULL);
        
        //if window could not be created
        if(window == NULL) {
            glfwTerminate(); // Destroys all windows and cursors, frees and restores resources. you must call glfwInit after.
            throw new RuntimeException("Unable to create window " + window);
        }
        
        GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
        glfwSetWindowPos(window, (vidmode.width() - width) / 2, (vidmode.height() - height) / 2);

        glfwMakeContextCurrent(window);
        glfwShowWindow(window); //show window which we hid in the window hints
        
        GL.createCapabilities(); 
        
        //I don't have screen tearing issues so I am commenting this out
//        glfwSwapInterval(1); 
    
        GL11.glEnable(GL11.GL_DEPTH_TEST); 
        
        return window;
    }

    public static String getTitle() {
        return TITLE;
    }

    public static int getWidth() {
        return width;
    }
package com.codebitcookie.graphics;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;

import com.codebitcookie.utils.BufferUtils;

public class Mesh {

    public final static int VERTEX_ATTRIB = 0;
    public final static int TEXCOORD_ATTRIB = 1;
    
    private int vao, vbo, tcbo; //Vertex Array Obj, Vertex Buffer Obj, Texture Coords Buffer Obj
    private float[] vertices, textureCoordinates;
    
    public Mesh(float[] vertices, float[] textureCoordinates) {
        this.vertices = vertices;
        this.textureCoordinates = textureCoordinates;
    
        //Create and bind VAO
        vao = GL30.glGenVertexArrays();
        GL30.glBindVertexArray(vao);
        
        //Create and bind VBO, creates a buffer data store and stores that in VAO
        vbo = GL15.glGenBuffers();
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
        
        GL15.glBufferData(GL15.GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(vertices), GL15.GL_STATIC_DRAW);
        GL20.glVertexAttribPointer(VERTEX_ATTRIB, 2, GL11.GL_FLOAT, false, 0, 0);
        
        //Create and bind TCBO, creates a buffer data store and stores that in VAO
        tcbo = GL15.glGenBuffers();
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, tcbo);
        
        GL15.glBufferData(GL15.GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(textureCoordinates), GL15.GL_STATIC_DRAW);
        GL20.glVertexAttribPointer(TEXCOORD_ATTRIB, 2, GL11.GL_UNSIGNED_INT, false, 0, 0);
        
        GL30.glBindVertexArray(0); //unbind latest bounded VAO
    }

    public void render() {
        GL30.glBindVertexArray(vao);
        
        GL20.glEnableVertexAttribArray(1);
        GL20.glEnableVertexAttribArray(0); //enabling the 0th attribute list index (ORDER OF ENABLING / DISABLING DOES'NT MATTER)

        GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, vertices.length);
        
        GL20.glDisableVertexAttribArray(0); //disabling the 0th attribute list index 
        GL20.glDisableVertexAttribArray(1);
        
        GL30.glBindVertexArray(0);
    }
    
    //delete the VAOs and the VBOs
    public void cleanUp() {
        GL30.glDeleteVertexArrays(vao);
        GL15.glDeleteBuffers(vbo);
        GL15.glDeleteBuffers(tcbo);
    }
    
}
package com.codebitcookie.shaders;

import java.util.HashMap;
import java.util.Map;

import javax.swing.plaf.PanelUI;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.system.CallbackI.P;

import com.codebitcookie.graphics.Color;
import com.codebitcookie.maths.Matrix4f;
import com.codebitcookie.maths.Vector3f;
import com.codebitcookie.utils.BufferUtils;
import com.codebitcookie.utils.FileUtils;

public class Shader {
    
    private Map<String, Integer> locationCache = new HashMap<>();
    
    private static int programID;
    private int vertexShaderID;
    private int fragmentShaderID;
    
    private boolean enabled = false;
    
    public Shader(String vertexShaderPath, String fragmentShaderPath) {
        
        //Create Shaders
        programID = GL20.glCreateProgram();
        vertexShaderID = GL20.glCreateShader(GL20.GL_VERTEX_SHADER);
        fragmentShaderID = GL20.glCreateShader(GL20.GL_FRAGMENT_SHADER);
        
        //After the shader is created we assign the actual shader file to the shader
        GL20.glShaderSource(vertexShaderID, FileUtils.loadAsString(vertexShaderPath));
        GL20.glShaderSource(fragmentShaderID, FileUtils.loadAsString(fragmentShaderPath));
        
        //Compile the Shaders, and check for any compilation errors
        GL20.glCompileShader(vertexShaderID);
        if(GL20.glGetShaderi(vertexShaderID, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
            System.err.println("[VERTEX] Compile Error: " + GL20.glGetShaderInfoLog(vertexShaderID, 2048));
            System.exit(-1);
        }
        
        GL20.glCompileShader(fragmentShaderID);
        if(GL20.glGetShaderi(fragmentShaderID, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
            System.err.println("[FRAGMENT] Compile Error: " + GL20.glGetShaderInfoLog(fragmentShaderID, 2048));
            System.exit(-1);
        }
        
        //Attach shaders to program
        GL20.glAttachShader(programID, vertexShaderID);
        GL20.glAttachShader(programID, fragmentShaderID);
        
        //set layout location explicitly in java
        GL20.glBindAttribLocation(programID, 0, "vertices");
        GL20.glBindAttribLocation(programID, 1, "uv");
            
        //Links together and creates executables for all shaders that were attached to the program, and check for any linking errors
        GL20.glLinkProgram(programID); 
        if(GL20.glGetProgrami(programID, GL20.GL_LINK_STATUS) == GL11.GL_FALSE) {
            System.err.println("[PROGRAM] Link Error: " + GL20.glGetProgramInfoLog(programID, 2048));
            System.exit(-1);
        }
        
        //Checks if the executables created are valid and can execute given the current OpenGL state, and check for any validation errors
        GL20.glValidateProgram(programID);
        
        if(GL20.glGetProgrami(programID, GL20.GL_VALIDATE_STATUS) == GL11.GL_FALSE) {
            System.err.println("[PROGRAM] Validation Error: " + GL20.glGetProgramInfoLog(programID, 2048));
            System.exit(-1);
        }
                
    }
    
    public void bind() {
        GL20.glUseProgram(programID);
        enabled = true;
    }
    public void unbind() {
        GL20.glUseProgram(0);
    }
    
    public void cleanUp() {
        unbind();
        locationCache.clear();
        GL20.glDetachShader(programID, vertexShaderID);
        GL20.glDetachShader(programID, fragmentShaderID);
        GL20.glDeleteShader(vertexShaderID);
        GL20.glDeleteShader(fragmentShaderID);
        GL20.glDeleteProgram(programID);
    }
    
    public int getUniformLocation(String name) {
        if(locationCache.containsKey(name)) {
            return locationCache.get(name);
        }
        
        int location = GL20.glGetUniformLocation(programID, name);
        
        if(location == -1) {
            System.err.println("[Shader] Error: Couldn't get the location uniform variable " + name);
        } else {
            locationCache.put(name, location);
        }
        
        return location;
    }
    
    public void setUniformColor(String name, Color c) {
        if(!enabled) bind();
        GL20.glUniform4f(getUniformLocation(name), c.getR(), c.getG(), c.getB(), c.getA());
    }
    public void setUniform1i(String name, int value) {
        if(!enabled) bind();
        GL20.glUniform1i(getUniformLocation(name), value);
    }
    public void setUniform1b(String name, boolean value) {
        if(!enabled) bind();
        
        int toLoad = value ? 1 : 0;
        GL20.glUniform1f(getUniformLocation(name), toLoad);
    }
    public void setUniform1f(String name, float value) {
        if(!enabled) bind();
        GL20.glUniform1f(getUniformLocation(name), value);
    }
    public void setUniform2f(String name, float x, float y) {
        if(!enabled) bind();
        GL20.glUniform2f(getUniformLocation(name), x, y);
    }
    public void setUniform3f(String name, Vector3f value) {
        if(!enabled) bind();
        GL20.glUniform3f(getUniformLocation(name), value.getX(), value.getY(), value.getZ());
    }   
    public void setUniformMat4f(String name, Matrix4f matrix) {
        if(!enabled) bind();
        GL20.glUniformMatrix4fv(getUniformLocation(name), false, BufferUtils.createFloatBuffer(matrix.getMatrix())); //false is for transpose, if we didn't setup our matrices in column major we could say true which would to this for us automatically but extra work for the memory, I think?
    }
}
//It doesn't matter what extension or name you give these Shader files as we only read the text
//Vertex files are used for positioning vertices and that's it.
//Will be run per vertex, so 3 times

//Uniform is the type of variable which can communicate with java code / not glsl 

//Uses version 3.3 with the core profile

#version 330 core

in vec2 position;
in vec2 textureCoords;

out vec4 color;
out vec2 uvCoords;

uniform vec4 matColor;
uniform mat4 projection;

void main(){

    //our triangle, or whatever is drawn is a single unit, and because of our special projection matrix is rendered as one single pixel
    //to fix this problem we create a world position (see notes) which scales the unit up (position * 100) 
    //and moves it by 100 pixels from the top and 100 pixels from the left ( + vec2(100, 100) )
    
    vec2 worldPosition = (position * 100);// + vec2(100, 100);
    gl_Position = projection * vec4(worldPosition, 0.0f, 1.0f);
    
    color = matColor;
    uvCoords = textureCoords;
}
#version 330 core

uniform sampler2D sampler;


in vec4 color;
in vec2 uvCoords;

out vec4 out_color;
void main() {
    out_color = color * texture(sampler, uvCoords);
}
package com.codebitcookie.graphics;

import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.LinkedList;
import java.util.List;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.stb.STBImage;

public class Texture {
    
    private String name = "";
    private String filepath = "";
    
    private int id;
    private int width, height;

    private static List<Texture> textureInstances = new LinkedList<>();
    private static Texture tmp = null;
    private static int i = 0;
    
    public Texture(String name, String filepath) {
        this.name = name;
        this.filepath = filepath;
        
        IntBuffer width = BufferUtils.createIntBuffer(1);
        IntBuffer height = BufferUtils.createIntBuffer(1);
        IntBuffer channels = BufferUtils.createIntBuffer(1);
        
        //get pixel data in RGBA format with STBImage, if we used BufferedImage we would get ARGB and we would have to change it to RGBA
        ByteBuffer data = STBImage.stbi_load(filepath, width, height, channels, 4);
        
        id = GL11.glGenTextures();
        this.width = width.get();
        this.height = height.get();
        
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, id);
        
        //see notes for this
        //We do GL_NEAREST because in our game we are using pixel art and it won't look sharp if we use bi linear filtering
        GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
        GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
        
        GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, this.width, this.height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, data);
        STBImage.stbi_image_free(data);
        
        textureInstances.add(this);
    }
    
    public static Texture find(String texturename) {
        for(i = 0; i < textureInstances.size(); i++) {
            tmp = getTextureInstances().get(i);
            if(tmp.name.startsWith(texturename))
                return tmp;
        }
        
        return null;
    }
    
    public void bind() {
        GL13.glActiveTexture(GL13.GL_TEXTURE0);
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, id);
    }
    
    public void unbind() {
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
    }
    
    public static void cleanUp() {
        for(i = 0; i < textureInstances.size(); i++) {
            GL13.glDeleteTextures(textureInstances.get(i).getId());
        }
    }
    
    public String getName() {
        return name;
    }
    public int getId() {
        return id;
    }
    public int getWidth() {
        return width;
    }
    public int getHeight() {
        return height;
    }
    public static List<Texture> getTextureInstances() {
        return textureInstances;
    }
    
}
********************Mesh.java********************

package com.codebitcookie.engine;

import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.system.MemoryUtil.NULL;

import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL11;

import com.codebitcookie.graphics.Color;

public class Application {
    
    private static final String TITLE = "2D Game Engine /w GUI";
    private static int width = 1280;
    private static int height = 720;
    private static long window;
    
    public static long init() {
        //check if GLFW is initialized if not we throw an exception
        if(!glfwInit()) {
            throw new IllegalStateException("GLFW failed to be initialized");
        }
        
//      GLFW.glfwDefaultWindowHints(); //equals to the two lines below
        glfwWindowHint(GLFW_VISIBLE, GL11.GL_FALSE); //makes the window visible
        glfwWindowHint(GLFW_RESIZABLE, GL11.GL_TRUE); //makes the window resizable
        
        //sets OpenGL to 3.3; major is 3.*** and minor is ***.3 = 3.3
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
        
        //for more info on these, check out: 
        //https://community.khronos.org/t/forward-compatible-vs-core-profile/65039
        //https://www.khronos.org/opengl/wiki/OpenGL_Context
        glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL11.GL_TRUE); //makes mac run nicely but restores all deprecated functionality
    
        window = glfwCreateWindow(width, height, TITLE, NULL, NULL);
        
        //if window could not be created
        if(window == NULL) {
            glfwTerminate(); // Destroys all windows and cursors, frees and restores resources. you must call glfwInit after.
            throw new RuntimeException("Unable to create window " + window);
        }
        
        GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
        glfwSetWindowPos(window, (vidmode.width() - width) / 2, (vidmode.height() - height) / 2);

        glfwMakeContextCurrent(window);
        glfwShowWindow(window); //show window which we hid in the window hints
        
        GL.createCapabilities(); 
        
        //I don't have screen tearing issues so I am commenting this out
//        glfwSwapInterval(1); 
    
        GL11.glEnable(GL11.GL_DEPTH_TEST); 
        
        return window;
    }

    public static String getTitle() {
        return TITLE;
    }

    public static int getWidth() {
        return width;
    }
package com.codebitcookie.graphics;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;

import com.codebitcookie.utils.BufferUtils;

public class Mesh {

    public final static int VERTEX_ATTRIB = 0;
    public final static int TEXCOORD_ATTRIB = 1;
    
    private int vao, vbo, tcbo; //Vertex Array Obj, Vertex Buffer Obj, Texture Coords Buffer Obj
    private float[] vertices, textureCoordinates;
    
    public Mesh(float[] vertices, float[] textureCoordinates) {
        this.vertices = vertices;
        this.textureCoordinates = textureCoordinates;
    
        //Create and bind VAO
        vao = GL30.glGenVertexArrays();
        GL30.glBindVertexArray(vao);
        
        //Create and bind VBO, creates a buffer data store and stores that in VAO
        vbo = GL15.glGenBuffers();
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
        
        GL15.glBufferData(GL15.GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(vertices), GL15.GL_STATIC_DRAW);
        GL20.glVertexAttribPointer(VERTEX_ATTRIB, 2, GL11.GL_FLOAT, false, 0, 0);
        
        //Create and bind TCBO, creates a buffer data store and stores that in VAO
        tcbo = GL15.glGenBuffers();
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, tcbo);
        
        GL15.glBufferData(GL15.GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(textureCoordinates), GL15.GL_STATIC_DRAW);
        GL20.glVertexAttribPointer(TEXCOORD_ATTRIB, 2, GL11.GL_UNSIGNED_INT, false, 0, 0);
        
        GL30.glBindVertexArray(0); //unbind latest bounded VAO
    }

    public void render() {
        GL30.glBindVertexArray(vao);
        
        GL20.glEnableVertexAttribArray(1);
        GL20.glEnableVertexAttribArray(0); //enabling the 0th attribute list index (ORDER OF ENABLING / DISABLING DOES'NT MATTER)

        GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, vertices.length);
        
        GL20.glDisableVertexAttribArray(0); //disabling the 0th attribute list index 
        GL20.glDisableVertexAttribArray(1);
        
        GL30.glBindVertexArray(0);
    }
    
    //delete the VAOs and the VBOs
    public void cleanUp() {
        GL30.glDeleteVertexArrays(vao);
        GL15.glDeleteBuffers(vbo);
        GL15.glDeleteBuffers(tcbo);
    }
    
}
package com.codebitcookie.shaders;

import java.util.HashMap;
import java.util.Map;

import javax.swing.plaf.PanelUI;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.system.CallbackI.P;

import com.codebitcookie.graphics.Color;
import com.codebitcookie.maths.Matrix4f;
import com.codebitcookie.maths.Vector3f;
import com.codebitcookie.utils.BufferUtils;
import com.codebitcookie.utils.FileUtils;

public class Shader {
    
    private Map<String, Integer> locationCache = new HashMap<>();
    
    private static int programID;
    private int vertexShaderID;
    private int fragmentShaderID;
    
    private boolean enabled = false;
    
    public Shader(String vertexShaderPath, String fragmentShaderPath) {
        
        //Create Shaders
        programID = GL20.glCreateProgram();
        vertexShaderID = GL20.glCreateShader(GL20.GL_VERTEX_SHADER);
        fragmentShaderID = GL20.glCreateShader(GL20.GL_FRAGMENT_SHADER);
        
        //After the shader is created we assign the actual shader file to the shader
        GL20.glShaderSource(vertexShaderID, FileUtils.loadAsString(vertexShaderPath));
        GL20.glShaderSource(fragmentShaderID, FileUtils.loadAsString(fragmentShaderPath));
        
        //Compile the Shaders, and check for any compilation errors
        GL20.glCompileShader(vertexShaderID);
        if(GL20.glGetShaderi(vertexShaderID, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
            System.err.println("[VERTEX] Compile Error: " + GL20.glGetShaderInfoLog(vertexShaderID, 2048));
            System.exit(-1);
        }
        
        GL20.glCompileShader(fragmentShaderID);
        if(GL20.glGetShaderi(fragmentShaderID, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
            System.err.println("[FRAGMENT] Compile Error: " + GL20.glGetShaderInfoLog(fragmentShaderID, 2048));
            System.exit(-1);
        }
        
        //Attach shaders to program
        GL20.glAttachShader(programID, vertexShaderID);
        GL20.glAttachShader(programID, fragmentShaderID);
        
        //set layout location explicitly in java
        GL20.glBindAttribLocation(programID, 0, "vertices");
        GL20.glBindAttribLocation(programID, 1, "uv");
            
        //Links together and creates executables for all shaders that were attached to the program, and check for any linking errors
        GL20.glLinkProgram(programID); 
        if(GL20.glGetProgrami(programID, GL20.GL_LINK_STATUS) == GL11.GL_FALSE) {
            System.err.println("[PROGRAM] Link Error: " + GL20.glGetProgramInfoLog(programID, 2048));
            System.exit(-1);
        }
        
        //Checks if the executables created are valid and can execute given the current OpenGL state, and check for any validation errors
        GL20.glValidateProgram(programID);
        
        if(GL20.glGetProgrami(programID, GL20.GL_VALIDATE_STATUS) == GL11.GL_FALSE) {
            System.err.println("[PROGRAM] Validation Error: " + GL20.glGetProgramInfoLog(programID, 2048));
            System.exit(-1);
        }
                
    }
    
    public void bind() {
        GL20.glUseProgram(programID);
        enabled = true;
    }
    public void unbind() {
        GL20.glUseProgram(0);
    }
    
    public void cleanUp() {
        unbind();
        locationCache.clear();
        GL20.glDetachShader(programID, vertexShaderID);
        GL20.glDetachShader(programID, fragmentShaderID);
        GL20.glDeleteShader(vertexShaderID);
        GL20.glDeleteShader(fragmentShaderID);
        GL20.glDeleteProgram(programID);
    }
    
    public int getUniformLocation(String name) {
        if(locationCache.containsKey(name)) {
            return locationCache.get(name);
        }
        
        int location = GL20.glGetUniformLocation(programID, name);
        
        if(location == -1) {
            System.err.println("[Shader] Error: Couldn't get the location uniform variable " + name);
        } else {
            locationCache.put(name, location);
        }
        
        return location;
    }
    
    public void setUniformColor(String name, Color c) {
        if(!enabled) bind();
        GL20.glUniform4f(getUniformLocation(name), c.getR(), c.getG(), c.getB(), c.getA());
    }
    public void setUniform1i(String name, int value) {
        if(!enabled) bind();
        GL20.glUniform1i(getUniformLocation(name), value);
    }
    public void setUniform1b(String name, boolean value) {
        if(!enabled) bind();
        
        int toLoad = value ? 1 : 0;
        GL20.glUniform1f(getUniformLocation(name), toLoad);
    }
    public void setUniform1f(String name, float value) {
        if(!enabled) bind();
        GL20.glUniform1f(getUniformLocation(name), value);
    }
    public void setUniform2f(String name, float x, float y) {
        if(!enabled) bind();
        GL20.glUniform2f(getUniformLocation(name), x, y);
    }
    public void setUniform3f(String name, Vector3f value) {
        if(!enabled) bind();
        GL20.glUniform3f(getUniformLocation(name), value.getX(), value.getY(), value.getZ());
    }   
    public void setUniformMat4f(String name, Matrix4f matrix) {
        if(!enabled) bind();
        GL20.glUniformMatrix4fv(getUniformLocation(name), false, BufferUtils.createFloatBuffer(matrix.getMatrix())); //false is for transpose, if we didn't setup our matrices in column major we could say true which would to this for us automatically but extra work for the memory, I think?
    }
}
//It doesn't matter what extension or name you give these Shader files as we only read the text
//Vertex files are used for positioning vertices and that's it.
//Will be run per vertex, so 3 times

//Uniform is the type of variable which can communicate with java code / not glsl 

//Uses version 3.3 with the core profile

#version 330 core

in vec2 position;
in vec2 textureCoords;

out vec4 color;
out vec2 uvCoords;

uniform vec4 matColor;
uniform mat4 projection;

void main(){

    //our triangle, or whatever is drawn is a single unit, and because of our special projection matrix is rendered as one single pixel
    //to fix this problem we create a world position (see notes) which scales the unit up (position * 100) 
    //and moves it by 100 pixels from the top and 100 pixels from the left ( + vec2(100, 100) )
    
    vec2 worldPosition = (position * 100);// + vec2(100, 100);
    gl_Position = projection * vec4(worldPosition, 0.0f, 1.0f);
    
    color = matColor;
    uvCoords = textureCoords;
}
#version 330 core

uniform sampler2D sampler;


in vec4 color;
in vec2 uvCoords;

out vec4 out_color;
void main() {
    out_color = color * texture(sampler, uvCoords);
}
package com.codebitcookie.graphics;

import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.LinkedList;
import java.util.List;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.stb.STBImage;

public class Texture {
    
    private String name = "";
    private String filepath = "";
    
    private int id;
    private int width, height;

    private static List<Texture> textureInstances = new LinkedList<>();
    private static Texture tmp = null;
    private static int i = 0;
    
    public Texture(String name, String filepath) {
        this.name = name;
        this.filepath = filepath;
        
        IntBuffer width = BufferUtils.createIntBuffer(1);
        IntBuffer height = BufferUtils.createIntBuffer(1);
        IntBuffer channels = BufferUtils.createIntBuffer(1);
        
        //get pixel data in RGBA format with STBImage, if we used BufferedImage we would get ARGB and we would have to change it to RGBA
        ByteBuffer data = STBImage.stbi_load(filepath, width, height, channels, 4);
        
        id = GL11.glGenTextures();
        this.width = width.get();
        this.height = height.get();
        
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, id);
        
        //see notes for this
        //We do GL_NEAREST because in our game we are using pixel art and it won't look sharp if we use bi linear filtering
        GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
        GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
        
        GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, this.width, this.height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, data);
        STBImage.stbi_image_free(data);
        
        textureInstances.add(this);
    }
    
    public static Texture find(String texturename) {
        for(i = 0; i < textureInstances.size(); i++) {
            tmp = getTextureInstances().get(i);
            if(tmp.name.startsWith(texturename))
                return tmp;
        }
        
        return null;
    }
    
    public void bind() {
        GL13.glActiveTexture(GL13.GL_TEXTURE0);
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, id);
    }
    
    public void unbind() {
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
    }
    
    public static void cleanUp() {
        for(i = 0; i < textureInstances.size(); i++) {
            GL13.glDeleteTextures(textureInstances.get(i).getId());
        }
    }
    
    public String getName() {
        return name;
    }
    public int getId() {
        return id;
    }
    public int getWidth() {
        return width;
    }
    public int getHeight() {
        return height;
    }
    public static List<Texture> getTextureInstances() {
        return textureInstances;
    }
    
}
********************Shader.java********************

package com.codebitcookie.engine;

import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.system.MemoryUtil.NULL;

import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL11;

import com.codebitcookie.graphics.Color;

public class Application {
    
    private static final String TITLE = "2D Game Engine /w GUI";
    private static int width = 1280;
    private static int height = 720;
    private static long window;
    
    public static long init() {
        //check if GLFW is initialized if not we throw an exception
        if(!glfwInit()) {
            throw new IllegalStateException("GLFW failed to be initialized");
        }
        
//      GLFW.glfwDefaultWindowHints(); //equals to the two lines below
        glfwWindowHint(GLFW_VISIBLE, GL11.GL_FALSE); //makes the window visible
        glfwWindowHint(GLFW_RESIZABLE, GL11.GL_TRUE); //makes the window resizable
        
        //sets OpenGL to 3.3; major is 3.*** and minor is ***.3 = 3.3
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
        
        //for more info on these, check out: 
        //https://community.khronos.org/t/forward-compatible-vs-core-profile/65039
        //https://www.khronos.org/opengl/wiki/OpenGL_Context
        glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL11.GL_TRUE); //makes mac run nicely but restores all deprecated functionality
    
        window = glfwCreateWindow(width, height, TITLE, NULL, NULL);
        
        //if window could not be created
        if(window == NULL) {
            glfwTerminate(); // Destroys all windows and cursors, frees and restores resources. you must call glfwInit after.
            throw new RuntimeException("Unable to create window " + window);
        }
        
        GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
        glfwSetWindowPos(window, (vidmode.width() - width) / 2, (vidmode.height() - height) / 2);

        glfwMakeContextCurrent(window);
        glfwShowWindow(window); //show window which we hid in the window hints
        
        GL.createCapabilities(); 
        
        //I don't have screen tearing issues so I am commenting this out
//        glfwSwapInterval(1); 
    
        GL11.glEnable(GL11.GL_DEPTH_TEST); 
        
        return window;
    }

    public static String getTitle() {
        return TITLE;
    }

    public static int getWidth() {
        return width;
    }
package com.codebitcookie.graphics;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;

import com.codebitcookie.utils.BufferUtils;

public class Mesh {

    public final static int VERTEX_ATTRIB = 0;
    public final static int TEXCOORD_ATTRIB = 1;
    
    private int vao, vbo, tcbo; //Vertex Array Obj, Vertex Buffer Obj, Texture Coords Buffer Obj
    private float[] vertices, textureCoordinates;
    
    public Mesh(float[] vertices, float[] textureCoordinates) {
        this.vertices = vertices;
        this.textureCoordinates = textureCoordinates;
    
        //Create and bind VAO
        vao = GL30.glGenVertexArrays();
        GL30.glBindVertexArray(vao);
        
        //Create and bind VBO, creates a buffer data store and stores that in VAO
        vbo = GL15.glGenBuffers();
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
        
        GL15.glBufferData(GL15.GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(vertices), GL15.GL_STATIC_DRAW);
        GL20.glVertexAttribPointer(VERTEX_ATTRIB, 2, GL11.GL_FLOAT, false, 0, 0);
        
        //Create and bind TCBO, creates a buffer data store and stores that in VAO
        tcbo = GL15.glGenBuffers();
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, tcbo);
        
        GL15.glBufferData(GL15.GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(textureCoordinates), GL15.GL_STATIC_DRAW);
        GL20.glVertexAttribPointer(TEXCOORD_ATTRIB, 2, GL11.GL_UNSIGNED_INT, false, 0, 0);
        
        GL30.glBindVertexArray(0); //unbind latest bounded VAO
    }

    public void render() {
        GL30.glBindVertexArray(vao);
        
        GL20.glEnableVertexAttribArray(1);
        GL20.glEnableVertexAttribArray(0); //enabling the 0th attribute list index (ORDER OF ENABLING / DISABLING DOES'NT MATTER)

        GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, vertices.length);
        
        GL20.glDisableVertexAttribArray(0); //disabling the 0th attribute list index 
        GL20.glDisableVertexAttribArray(1);
        
        GL30.glBindVertexArray(0);
    }
    
    //delete the VAOs and the VBOs
    public void cleanUp() {
        GL30.glDeleteVertexArrays(vao);
        GL15.glDeleteBuffers(vbo);
        GL15.glDeleteBuffers(tcbo);
    }
    
}
package com.codebitcookie.shaders;

import java.util.HashMap;
import java.util.Map;

import javax.swing.plaf.PanelUI;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.system.CallbackI.P;

import com.codebitcookie.graphics.Color;
import com.codebitcookie.maths.Matrix4f;
import com.codebitcookie.maths.Vector3f;
import com.codebitcookie.utils.BufferUtils;
import com.codebitcookie.utils.FileUtils;

public class Shader {
    
    private Map<String, Integer> locationCache = new HashMap<>();
    
    private static int programID;
    private int vertexShaderID;
    private int fragmentShaderID;
    
    private boolean enabled = false;
    
    public Shader(String vertexShaderPath, String fragmentShaderPath) {
        
        //Create Shaders
        programID = GL20.glCreateProgram();
        vertexShaderID = GL20.glCreateShader(GL20.GL_VERTEX_SHADER);
        fragmentShaderID = GL20.glCreateShader(GL20.GL_FRAGMENT_SHADER);
        
        //After the shader is created we assign the actual shader file to the shader
        GL20.glShaderSource(vertexShaderID, FileUtils.loadAsString(vertexShaderPath));
        GL20.glShaderSource(fragmentShaderID, FileUtils.loadAsString(fragmentShaderPath));
        
        //Compile the Shaders, and check for any compilation errors
        GL20.glCompileShader(vertexShaderID);
        if(GL20.glGetShaderi(vertexShaderID, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
            System.err.println("[VERTEX] Compile Error: " + GL20.glGetShaderInfoLog(vertexShaderID, 2048));
            System.exit(-1);
        }
        
        GL20.glCompileShader(fragmentShaderID);
        if(GL20.glGetShaderi(fragmentShaderID, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
            System.err.println("[FRAGMENT] Compile Error: " + GL20.glGetShaderInfoLog(fragmentShaderID, 2048));
            System.exit(-1);
        }
        
        //Attach shaders to program
        GL20.glAttachShader(programID, vertexShaderID);
        GL20.glAttachShader(programID, fragmentShaderID);
        
        //set layout location explicitly in java
        GL20.glBindAttribLocation(programID, 0, "vertices");
        GL20.glBindAttribLocation(programID, 1, "uv");
            
        //Links together and creates executables for all shaders that were attached to the program, and check for any linking errors
        GL20.glLinkProgram(programID); 
        if(GL20.glGetProgrami(programID, GL20.GL_LINK_STATUS) == GL11.GL_FALSE) {
            System.err.println("[PROGRAM] Link Error: " + GL20.glGetProgramInfoLog(programID, 2048));
            System.exit(-1);
        }
        
        //Checks if the executables created are valid and can execute given the current OpenGL state, and check for any validation errors
        GL20.glValidateProgram(programID);
        
        if(GL20.glGetProgrami(programID, GL20.GL_VALIDATE_STATUS) == GL11.GL_FALSE) {
            System.err.println("[PROGRAM] Validation Error: " + GL20.glGetProgramInfoLog(programID, 2048));
            System.exit(-1);
        }
                
    }
    
    public void bind() {
        GL20.glUseProgram(programID);
        enabled = true;
    }
    public void unbind() {
        GL20.glUseProgram(0);
    }
    
    public void cleanUp() {
        unbind();
        locationCache.clear();
        GL20.glDetachShader(programID, vertexShaderID);
        GL20.glDetachShader(programID, fragmentShaderID);
        GL20.glDeleteShader(vertexShaderID);
        GL20.glDeleteShader(fragmentShaderID);
        GL20.glDeleteProgram(programID);
    }
    
    public int getUniformLocation(String name) {
        if(locationCache.containsKey(name)) {
            return locationCache.get(name);
        }
        
        int location = GL20.glGetUniformLocation(programID, name);
        
        if(location == -1) {
            System.err.println("[Shader] Error: Couldn't get the location uniform variable " + name);
        } else {
            locationCache.put(name, location);
        }
        
        return location;
    }
    
    public void setUniformColor(String name, Color c) {
        if(!enabled) bind();
        GL20.glUniform4f(getUniformLocation(name), c.getR(), c.getG(), c.getB(), c.getA());
    }
    public void setUniform1i(String name, int value) {
        if(!enabled) bind();
        GL20.glUniform1i(getUniformLocation(name), value);
    }
    public void setUniform1b(String name, boolean value) {
        if(!enabled) bind();
        
        int toLoad = value ? 1 : 0;
        GL20.glUniform1f(getUniformLocation(name), toLoad);
    }
    public void setUniform1f(String name, float value) {
        if(!enabled) bind();
        GL20.glUniform1f(getUniformLocation(name), value);
    }
    public void setUniform2f(String name, float x, float y) {
        if(!enabled) bind();
        GL20.glUniform2f(getUniformLocation(name), x, y);
    }
    public void setUniform3f(String name, Vector3f value) {
        if(!enabled) bind();
        GL20.glUniform3f(getUniformLocation(name), value.getX(), value.getY(), value.getZ());
    }   
    public void setUniformMat4f(String name, Matrix4f matrix) {
        if(!enabled) bind();
        GL20.glUniformMatrix4fv(getUniformLocation(name), false, BufferUtils.createFloatBuffer(matrix.getMatrix())); //false is for transpose, if we didn't setup our matrices in column major we could say true which would to this for us automatically but extra work for the memory, I think?
    }
}
//It doesn't matter what extension or name you give these Shader files as we only read the text
//Vertex files are used for positioning vertices and that's it.
//Will be run per vertex, so 3 times

//Uniform is the type of variable which can communicate with java code / not glsl 

//Uses version 3.3 with the core profile

#version 330 core

in vec2 position;
in vec2 textureCoords;

out vec4 color;
out vec2 uvCoords;

uniform vec4 matColor;
uniform mat4 projection;

void main(){

    //our triangle, or whatever is drawn is a single unit, and because of our special projection matrix is rendered as one single pixel
    //to fix this problem we create a world position (see notes) which scales the unit up (position * 100) 
    //and moves it by 100 pixels from the top and 100 pixels from the left ( + vec2(100, 100) )
    
    vec2 worldPosition = (position * 100);// + vec2(100, 100);
    gl_Position = projection * vec4(worldPosition, 0.0f, 1.0f);
    
    color = matColor;
    uvCoords = textureCoords;
}
#version 330 core

uniform sampler2D sampler;


in vec4 color;
in vec2 uvCoords;

out vec4 out_color;
void main() {
    out_color = color * texture(sampler, uvCoords);
}
package com.codebitcookie.graphics;

import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.LinkedList;
import java.util.List;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.stb.STBImage;

public class Texture {
    
    private String name = "";
    private String filepath = "";
    
    private int id;
    private int width, height;

    private static List<Texture> textureInstances = new LinkedList<>();
    private static Texture tmp = null;
    private static int i = 0;
    
    public Texture(String name, String filepath) {
        this.name = name;
        this.filepath = filepath;
        
        IntBuffer width = BufferUtils.createIntBuffer(1);
        IntBuffer height = BufferUtils.createIntBuffer(1);
        IntBuffer channels = BufferUtils.createIntBuffer(1);
        
        //get pixel data in RGBA format with STBImage, if we used BufferedImage we would get ARGB and we would have to change it to RGBA
        ByteBuffer data = STBImage.stbi_load(filepath, width, height, channels, 4);
        
        id = GL11.glGenTextures();
        this.width = width.get();
        this.height = height.get();
        
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, id);
        
        //see notes for this
        //We do GL_NEAREST because in our game we are using pixel art and it won't look sharp if we use bi linear filtering
        GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
        GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
        
        GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, this.width, this.height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, data);
        STBImage.stbi_image_free(data);
        
        textureInstances.add(this);
    }
    
    public static Texture find(String texturename) {
        for(i = 0; i < textureInstances.size(); i++) {
            tmp = getTextureInstances().get(i);
            if(tmp.name.startsWith(texturename))
                return tmp;
        }
        
        return null;
    }
    
    public void bind() {
        GL13.glActiveTexture(GL13.GL_TEXTURE0);
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, id);
    }
    
    public void unbind() {
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
    }
    
    public static void cleanUp() {
        for(i = 0; i < textureInstances.size(); i++) {
            GL13.glDeleteTextures(textureInstances.get(i).getId());
        }
    }
    
    public String getName() {
        return name;
    }
    public int getId() {
        return id;
    }
    public int getWidth() {
        return width;
    }
    public int getHeight() {
        return height;
    }
    public static List<Texture> getTextureInstances() {
        return textureInstances;
    }
    
}
*******************FragmentShader.fs********************

package com.codebitcookie.engine;

import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.system.MemoryUtil.NULL;

import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL11;

import com.codebitcookie.graphics.Color;

public class Application {
    
    private static final String TITLE = "2D Game Engine /w GUI";
    private static int width = 1280;
    private static int height = 720;
    private static long window;
    
    public static long init() {
        //check if GLFW is initialized if not we throw an exception
        if(!glfwInit()) {
            throw new IllegalStateException("GLFW failed to be initialized");
        }
        
//      GLFW.glfwDefaultWindowHints(); //equals to the two lines below
        glfwWindowHint(GLFW_VISIBLE, GL11.GL_FALSE); //makes the window visible
        glfwWindowHint(GLFW_RESIZABLE, GL11.GL_TRUE); //makes the window resizable
        
        //sets OpenGL to 3.3; major is 3.*** and minor is ***.3 = 3.3
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
        
        //for more info on these, check out: 
        //https://community.khronos.org/t/forward-compatible-vs-core-profile/65039
        //https://www.khronos.org/opengl/wiki/OpenGL_Context
        glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL11.GL_TRUE); //makes mac run nicely but restores all deprecated functionality
    
        window = glfwCreateWindow(width, height, TITLE, NULL, NULL);
        
        //if window could not be created
        if(window == NULL) {
            glfwTerminate(); // Destroys all windows and cursors, frees and restores resources. you must call glfwInit after.
            throw new RuntimeException("Unable to create window " + window);
        }
        
        GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
        glfwSetWindowPos(window, (vidmode.width() - width) / 2, (vidmode.height() - height) / 2);

        glfwMakeContextCurrent(window);
        glfwShowWindow(window); //show window which we hid in the window hints
        
        GL.createCapabilities(); 
        
        //I don't have screen tearing issues so I am commenting this out
//        glfwSwapInterval(1); 
    
        GL11.glEnable(GL11.GL_DEPTH_TEST); 
        
        return window;
    }

    public static String getTitle() {
        return TITLE;
    }

    public static int getWidth() {
        return width;
    }
package com.codebitcookie.graphics;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;

import com.codebitcookie.utils.BufferUtils;

public class Mesh {

    public final static int VERTEX_ATTRIB = 0;
    public final static int TEXCOORD_ATTRIB = 1;
    
    private int vao, vbo, tcbo; //Vertex Array Obj, Vertex Buffer Obj, Texture Coords Buffer Obj
    private float[] vertices, textureCoordinates;
    
    public Mesh(float[] vertices, float[] textureCoordinates) {
        this.vertices = vertices;
        this.textureCoordinates = textureCoordinates;
    
        //Create and bind VAO
        vao = GL30.glGenVertexArrays();
        GL30.glBindVertexArray(vao);
        
        //Create and bind VBO, creates a buffer data store and stores that in VAO
        vbo = GL15.glGenBuffers();
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
        
        GL15.glBufferData(GL15.GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(vertices), GL15.GL_STATIC_DRAW);
        GL20.glVertexAttribPointer(VERTEX_ATTRIB, 2, GL11.GL_FLOAT, false, 0, 0);
        
        //Create and bind TCBO, creates a buffer data store and stores that in VAO
        tcbo = GL15.glGenBuffers();
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, tcbo);
        
        GL15.glBufferData(GL15.GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(textureCoordinates), GL15.GL_STATIC_DRAW);
        GL20.glVertexAttribPointer(TEXCOORD_ATTRIB, 2, GL11.GL_UNSIGNED_INT, false, 0, 0);
        
        GL30.glBindVertexArray(0); //unbind latest bounded VAO
    }

    public void render() {
        GL30.glBindVertexArray(vao);
        
        GL20.glEnableVertexAttribArray(1);
        GL20.glEnableVertexAttribArray(0); //enabling the 0th attribute list index (ORDER OF ENABLING / DISABLING DOES'NT MATTER)

        GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, vertices.length);
        
        GL20.glDisableVertexAttribArray(0); //disabling the 0th attribute list index 
        GL20.glDisableVertexAttribArray(1);
        
        GL30.glBindVertexArray(0);
    }
    
    //delete the VAOs and the VBOs
    public void cleanUp() {
        GL30.glDeleteVertexArrays(vao);
        GL15.glDeleteBuffers(vbo);
        GL15.glDeleteBuffers(tcbo);
    }
    
}
package com.codebitcookie.shaders;

import java.util.HashMap;
import java.util.Map;

import javax.swing.plaf.PanelUI;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.system.CallbackI.P;

import com.codebitcookie.graphics.Color;
import com.codebitcookie.maths.Matrix4f;
import com.codebitcookie.maths.Vector3f;
import com.codebitcookie.utils.BufferUtils;
import com.codebitcookie.utils.FileUtils;

public class Shader {
    
    private Map<String, Integer> locationCache = new HashMap<>();
    
    private static int programID;
    private int vertexShaderID;
    private int fragmentShaderID;
    
    private boolean enabled = false;
    
    public Shader(String vertexShaderPath, String fragmentShaderPath) {
        
        //Create Shaders
        programID = GL20.glCreateProgram();
        vertexShaderID = GL20.glCreateShader(GL20.GL_VERTEX_SHADER);
        fragmentShaderID = GL20.glCreateShader(GL20.GL_FRAGMENT_SHADER);
        
        //After the shader is created we assign the actual shader file to the shader
        GL20.glShaderSource(vertexShaderID, FileUtils.loadAsString(vertexShaderPath));
        GL20.glShaderSource(fragmentShaderID, FileUtils.loadAsString(fragmentShaderPath));
        
        //Compile the Shaders, and check for any compilation errors
        GL20.glCompileShader(vertexShaderID);
        if(GL20.glGetShaderi(vertexShaderID, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
            System.err.println("[VERTEX] Compile Error: " + GL20.glGetShaderInfoLog(vertexShaderID, 2048));
            System.exit(-1);
        }
        
        GL20.glCompileShader(fragmentShaderID);
        if(GL20.glGetShaderi(fragmentShaderID, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
            System.err.println("[FRAGMENT] Compile Error: " + GL20.glGetShaderInfoLog(fragmentShaderID, 2048));
            System.exit(-1);
        }
        
        //Attach shaders to program
        GL20.glAttachShader(programID, vertexShaderID);
        GL20.glAttachShader(programID, fragmentShaderID);
        
        //set layout location explicitly in java
        GL20.glBindAttribLocation(programID, 0, "vertices");
        GL20.glBindAttribLocation(programID, 1, "uv");
            
        //Links together and creates executables for all shaders that were attached to the program, and check for any linking errors
        GL20.glLinkProgram(programID); 
        if(GL20.glGetProgrami(programID, GL20.GL_LINK_STATUS) == GL11.GL_FALSE) {
            System.err.println("[PROGRAM] Link Error: " + GL20.glGetProgramInfoLog(programID, 2048));
            System.exit(-1);
        }
        
        //Checks if the executables created are valid and can execute given the current OpenGL state, and check for any validation errors
        GL20.glValidateProgram(programID);
        
        if(GL20.glGetProgrami(programID, GL20.GL_VALIDATE_STATUS) == GL11.GL_FALSE) {
            System.err.println("[PROGRAM] Validation Error: " + GL20.glGetProgramInfoLog(programID, 2048));
            System.exit(-1);
        }
                
    }
    
    public void bind() {
        GL20.glUseProgram(programID);
        enabled = true;
    }
    public void unbind() {
        GL20.glUseProgram(0);
    }
    
    public void cleanUp() {
        unbind();
        locationCache.clear();
        GL20.glDetachShader(programID, vertexShaderID);
        GL20.glDetachShader(programID, fragmentShaderID);
        GL20.glDeleteShader(vertexShaderID);
        GL20.glDeleteShader(fragmentShaderID);
        GL20.glDeleteProgram(programID);
    }
    
    public int getUniformLocation(String name) {
        if(locationCache.containsKey(name)) {
            return locationCache.get(name);
        }
        
        int location = GL20.glGetUniformLocation(programID, name);
        
        if(location == -1) {
            System.err.println("[Shader] Error: Couldn't get the location uniform variable " + name);
        } else {
            locationCache.put(name, location);
        }
        
        return location;
    }
    
    public void setUniformColor(String name, Color c) {
        if(!enabled) bind();
        GL20.glUniform4f(getUniformLocation(name), c.getR(), c.getG(), c.getB(), c.getA());
    }
    public void setUniform1i(String name, int value) {
        if(!enabled) bind();
        GL20.glUniform1i(getUniformLocation(name), value);
    }
    public void setUniform1b(String name, boolean value) {
        if(!enabled) bind();
        
        int toLoad = value ? 1 : 0;
        GL20.glUniform1f(getUniformLocation(name), toLoad);
    }
    public void setUniform1f(String name, float value) {
        if(!enabled) bind();
        GL20.glUniform1f(getUniformLocation(name), value);
    }
    public void setUniform2f(String name, float x, float y) {
        if(!enabled) bind();
        GL20.glUniform2f(getUniformLocation(name), x, y);
    }
    public void setUniform3f(String name, Vector3f value) {
        if(!enabled) bind();
        GL20.glUniform3f(getUniformLocation(name), value.getX(), value.getY(), value.getZ());
    }   
    public void setUniformMat4f(String name, Matrix4f matrix) {
        if(!enabled) bind();
        GL20.glUniformMatrix4fv(getUniformLocation(name), false, BufferUtils.createFloatBuffer(matrix.getMatrix())); //false is for transpose, if we didn't setup our matrices in column major we could say true which would to this for us automatically but extra work for the memory, I think?
    }
}
//It doesn't matter what extension or name you give these Shader files as we only read the text
//Vertex files are used for positioning vertices and that's it.
//Will be run per vertex, so 3 times

//Uniform is the type of variable which can communicate with java code / not glsl 

//Uses version 3.3 with the core profile

#version 330 core

in vec2 position;
in vec2 textureCoords;

out vec4 color;
out vec2 uvCoords;

uniform vec4 matColor;
uniform mat4 projection;

void main(){

    //our triangle, or whatever is drawn is a single unit, and because of our special projection matrix is rendered as one single pixel
    //to fix this problem we create a world position (see notes) which scales the unit up (position * 100) 
    //and moves it by 100 pixels from the top and 100 pixels from the left ( + vec2(100, 100) )
    
    vec2 worldPosition = (position * 100);// + vec2(100, 100);
    gl_Position = projection * vec4(worldPosition, 0.0f, 1.0f);
    
    color = matColor;
    uvCoords = textureCoords;
}
#version 330 core

uniform sampler2D sampler;


in vec4 color;
in vec2 uvCoords;

out vec4 out_color;
void main() {
    out_color = color * texture(sampler, uvCoords);
}
package com.codebitcookie.graphics;

import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.LinkedList;
import java.util.List;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.stb.STBImage;

public class Texture {
    
    private String name = "";
    private String filepath = "";
    
    private int id;
    private int width, height;

    private static List<Texture> textureInstances = new LinkedList<>();
    private static Texture tmp = null;
    private static int i = 0;
    
    public Texture(String name, String filepath) {
        this.name = name;
        this.filepath = filepath;
        
        IntBuffer width = BufferUtils.createIntBuffer(1);
        IntBuffer height = BufferUtils.createIntBuffer(1);
        IntBuffer channels = BufferUtils.createIntBuffer(1);
        
        //get pixel data in RGBA format with STBImage, if we used BufferedImage we would get ARGB and we would have to change it to RGBA
        ByteBuffer data = STBImage.stbi_load(filepath, width, height, channels, 4);
        
        id = GL11.glGenTextures();
        this.width = width.get();
        this.height = height.get();
        
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, id);
        
        //see notes for this
        //We do GL_NEAREST because in our game we are using pixel art and it won't look sharp if we use bi linear filtering
        GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
        GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
        
        GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, this.width, this.height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, data);
        STBImage.stbi_image_free(data);
        
        textureInstances.add(this);
    }
    
    public static Texture find(String texturename) {
        for(i = 0; i < textureInstances.size(); i++) {
            tmp = getTextureInstances().get(i);
            if(tmp.name.startsWith(texturename))
                return tmp;
        }
        
        return null;
    }
    
    public void bind() {
        GL13.glActiveTexture(GL13.GL_TEXTURE0);
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, id);
    }
    
    public void unbind() {
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
    }
    
    public static void cleanUp() {
        for(i = 0; i < textureInstances.size(); i++) {
            GL13.glDeleteTextures(textureInstances.get(i).getId());
        }
    }
    
    public String getName() {
        return name;
    }
    public int getId() {
        return id;
    }
    public int getWidth() {
        return width;
    }
    public int getHeight() {
        return height;
    }
    public static List<Texture> getTextureInstances() {
        return textureInstances;
    }
    
}
********************Texture.java********************

package com.codebitcookie.engine;

import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.system.MemoryUtil.NULL;

import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL11;

import com.codebitcookie.graphics.Color;

public class Application {
    
    private static final String TITLE = "2D Game Engine /w GUI";
    private static int width = 1280;
    private static int height = 720;
    private static long window;
    
    public static long init() {
        //check if GLFW is initialized if not we throw an exception
        if(!glfwInit()) {
            throw new IllegalStateException("GLFW failed to be initialized");
        }
        
//      GLFW.glfwDefaultWindowHints(); //equals to the two lines below
        glfwWindowHint(GLFW_VISIBLE, GL11.GL_FALSE); //makes the window visible
        glfwWindowHint(GLFW_RESIZABLE, GL11.GL_TRUE); //makes the window resizable
        
        //sets OpenGL to 3.3; major is 3.*** and minor is ***.3 = 3.3
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
        
        //for more info on these, check out: 
        //https://community.khronos.org/t/forward-compatible-vs-core-profile/65039
        //https://www.khronos.org/opengl/wiki/OpenGL_Context
        glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL11.GL_TRUE); //makes mac run nicely but restores all deprecated functionality
    
        window = glfwCreateWindow(width, height, TITLE, NULL, NULL);
        
        //if window could not be created
        if(window == NULL) {
            glfwTerminate(); // Destroys all windows and cursors, frees and restores resources. you must call glfwInit after.
            throw new RuntimeException("Unable to create window " + window);
        }
        
        GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
        glfwSetWindowPos(window, (vidmode.width() - width) / 2, (vidmode.height() - height) / 2);

        glfwMakeContextCurrent(window);
        glfwShowWindow(window); //show window which we hid in the window hints
        
        GL.createCapabilities(); 
        
        //I don't have screen tearing issues so I am commenting this out
//        glfwSwapInterval(1); 
    
        GL11.glEnable(GL11.GL_DEPTH_TEST); 
        
        return window;
    }

    public static String getTitle() {
        return TITLE;
    }

    public static int getWidth() {
        return width;
    }
package com.codebitcookie.graphics;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;

import com.codebitcookie.utils.BufferUtils;

public class Mesh {

    public final static int VERTEX_ATTRIB = 0;
    public final static int TEXCOORD_ATTRIB = 1;
    
    private int vao, vbo, tcbo; //Vertex Array Obj, Vertex Buffer Obj, Texture Coords Buffer Obj
    private float[] vertices, textureCoordinates;
    
    public Mesh(float[] vertices, float[] textureCoordinates) {
        this.vertices = vertices;
        this.textureCoordinates = textureCoordinates;
    
        //Create and bind VAO
        vao = GL30.glGenVertexArrays();
        GL30.glBindVertexArray(vao);
        
        //Create and bind VBO, creates a buffer data store and stores that in VAO
        vbo = GL15.glGenBuffers();
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
        
        GL15.glBufferData(GL15.GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(vertices), GL15.GL_STATIC_DRAW);
        GL20.glVertexAttribPointer(VERTEX_ATTRIB, 2, GL11.GL_FLOAT, false, 0, 0);
        
        //Create and bind TCBO, creates a buffer data store and stores that in VAO
        tcbo = GL15.glGenBuffers();
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, tcbo);
        
        GL15.glBufferData(GL15.GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(textureCoordinates), GL15.GL_STATIC_DRAW);
        GL20.glVertexAttribPointer(TEXCOORD_ATTRIB, 2, GL11.GL_UNSIGNED_INT, false, 0, 0);
        
        GL30.glBindVertexArray(0); //unbind latest bounded VAO
    }

    public void render() {
        GL30.glBindVertexArray(vao);
        
        GL20.glEnableVertexAttribArray(1);
        GL20.glEnableVertexAttribArray(0); //enabling the 0th attribute list index (ORDER OF ENABLING / DISABLING DOES'NT MATTER)

        GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, vertices.length);
        
        GL20.glDisableVertexAttribArray(0); //disabling the 0th attribute list index 
        GL20.glDisableVertexAttribArray(1);
        
        GL30.glBindVertexArray(0);
    }
    
    //delete the VAOs and the VBOs
    public void cleanUp() {
        GL30.glDeleteVertexArrays(vao);
        GL15.glDeleteBuffers(vbo);
        GL15.glDeleteBuffers(tcbo);
    }
    
}
package com.codebitcookie.shaders;

import java.util.HashMap;
import java.util.Map;

import javax.swing.plaf.PanelUI;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.system.CallbackI.P;

import com.codebitcookie.graphics.Color;
import com.codebitcookie.maths.Matrix4f;
import com.codebitcookie.maths.Vector3f;
import com.codebitcookie.utils.BufferUtils;
import com.codebitcookie.utils.FileUtils;

public class Shader {
    
    private Map<String, Integer> locationCache = new HashMap<>();
    
    private static int programID;
    private int vertexShaderID;
    private int fragmentShaderID;
    
    private boolean enabled = false;
    
    public Shader(String vertexShaderPath, String fragmentShaderPath) {
        
        //Create Shaders
        programID = GL20.glCreateProgram();
        vertexShaderID = GL20.glCreateShader(GL20.GL_VERTEX_SHADER);
        fragmentShaderID = GL20.glCreateShader(GL20.GL_FRAGMENT_SHADER);
        
        //After the shader is created we assign the actual shader file to the shader
        GL20.glShaderSource(vertexShaderID, FileUtils.loadAsString(vertexShaderPath));
        GL20.glShaderSource(fragmentShaderID, FileUtils.loadAsString(fragmentShaderPath));
        
        //Compile the Shaders, and check for any compilation errors
        GL20.glCompileShader(vertexShaderID);
        if(GL20.glGetShaderi(vertexShaderID, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
            System.err.println("[VERTEX] Compile Error: " + GL20.glGetShaderInfoLog(vertexShaderID, 2048));
            System.exit(-1);
        }
        
        GL20.glCompileShader(fragmentShaderID);
        if(GL20.glGetShaderi(fragmentShaderID, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
            System.err.println("[FRAGMENT] Compile Error: " + GL20.glGetShaderInfoLog(fragmentShaderID, 2048));
            System.exit(-1);
        }
        
        //Attach shaders to program
        GL20.glAttachShader(programID, vertexShaderID);
        GL20.glAttachShader(programID, fragmentShaderID);
        
        //set layout location explicitly in java
        GL20.glBindAttribLocation(programID, 0, "vertices");
        GL20.glBindAttribLocation(programID, 1, "uv");
            
        //Links together and creates executables for all shaders that were attached to the program, and check for any linking errors
        GL20.glLinkProgram(programID); 
        if(GL20.glGetProgrami(programID, GL20.GL_LINK_STATUS) == GL11.GL_FALSE) {
            System.err.println("[PROGRAM] Link Error: " + GL20.glGetProgramInfoLog(programID, 2048));
            System.exit(-1);
        }
        
        //Checks if the executables created are valid and can execute given the current OpenGL state, and check for any validation errors
        GL20.glValidateProgram(programID);
        
        if(GL20.glGetProgrami(programID, GL20.GL_VALIDATE_STATUS) == GL11.GL_FALSE) {
            System.err.println("[PROGRAM] Validation Error: " + GL20.glGetProgramInfoLog(programID, 2048));
            System.exit(-1);
        }
                
    }
    
    public void bind() {
        GL20.glUseProgram(programID);
        enabled = true;
    }
    public void unbind() {
        GL20.glUseProgram(0);
    }
    
    public void cleanUp() {
        unbind();
        locationCache.clear();
        GL20.glDetachShader(programID, vertexShaderID);
        GL20.glDetachShader(programID, fragmentShaderID);
        GL20.glDeleteShader(vertexShaderID);
        GL20.glDeleteShader(fragmentShaderID);
        GL20.glDeleteProgram(programID);
    }
    
    public int getUniformLocation(String name) {
        if(locationCache.containsKey(name)) {
            return locationCache.get(name);
        }
        
        int location = GL20.glGetUniformLocation(programID, name);
        
        if(location == -1) {
            System.err.println("[Shader] Error: Couldn't get the location uniform variable " + name);
        } else {
            locationCache.put(name, location);
        }
        
        return location;
    }
    
    public void setUniformColor(String name, Color c) {
        if(!enabled) bind();
        GL20.glUniform4f(getUniformLocation(name), c.getR(), c.getG(), c.getB(), c.getA());
    }
    public void setUniform1i(String name, int value) {
        if(!enabled) bind();
        GL20.glUniform1i(getUniformLocation(name), value);
    }
    public void setUniform1b(String name, boolean value) {
        if(!enabled) bind();
        
        int toLoad = value ? 1 : 0;
        GL20.glUniform1f(getUniformLocation(name), toLoad);
    }
    public void setUniform1f(String name, float value) {
        if(!enabled) bind();
        GL20.glUniform1f(getUniformLocation(name), value);
    }
    public void setUniform2f(String name, float x, float y) {
        if(!enabled) bind();
        GL20.glUniform2f(getUniformLocation(name), x, y);
    }
    public void setUniform3f(String name, Vector3f value) {
        if(!enabled) bind();
        GL20.glUniform3f(getUniformLocation(name), value.getX(), value.getY(), value.getZ());
    }   
    public void setUniformMat4f(String name, Matrix4f matrix) {
        if(!enabled) bind();
        GL20.glUniformMatrix4fv(getUniformLocation(name), false, BufferUtils.createFloatBuffer(matrix.getMatrix())); //false is for transpose, if we didn't setup our matrices in column major we could say true which would to this for us automatically but extra work for the memory, I think?
    }
}
//It doesn't matter what extension or name you give these Shader files as we only read the text
//Vertex files are used for positioning vertices and that's it.
//Will be run per vertex, so 3 times

//Uniform is the type of variable which can communicate with java code / not glsl 

//Uses version 3.3 with the core profile

#version 330 core

in vec2 position;
in vec2 textureCoords;

out vec4 color;
out vec2 uvCoords;

uniform vec4 matColor;
uniform mat4 projection;

void main(){

    //our triangle, or whatever is drawn is a single unit, and because of our special projection matrix is rendered as one single pixel
    //to fix this problem we create a world position (see notes) which scales the unit up (position * 100) 
    //and moves it by 100 pixels from the top and 100 pixels from the left ( + vec2(100, 100) )
    
    vec2 worldPosition = (position * 100);// + vec2(100, 100);
    gl_Position = projection * vec4(worldPosition, 0.0f, 1.0f);
    
    color = matColor;
    uvCoords = textureCoords;
}
#version 330 core

uniform sampler2D sampler;


in vec4 color;
in vec2 uvCoords;

out vec4 out_color;
void main() {
    out_color = color * texture(sampler, uvCoords);
}
package com.codebitcookie.graphics;

import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.LinkedList;
import java.util.List;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.stb.STBImage;

public class Texture {
    
    private String name = "";
    private String filepath = "";
    
    private int id;
    private int width, height;

    private static List<Texture> textureInstances = new LinkedList<>();
    private static Texture tmp = null;
    private static int i = 0;
    
    public Texture(String name, String filepath) {
        this.name = name;
        this.filepath = filepath;
        
        IntBuffer width = BufferUtils.createIntBuffer(1);
        IntBuffer height = BufferUtils.createIntBuffer(1);
        IntBuffer channels = BufferUtils.createIntBuffer(1);
        
        //get pixel data in RGBA format with STBImage, if we used BufferedImage we would get ARGB and we would have to change it to RGBA
        ByteBuffer data = STBImage.stbi_load(filepath, width, height, channels, 4);
        
        id = GL11.glGenTextures();
        this.width = width.get();
        this.height = height.get();
        
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, id);
        
        //see notes for this
        //We do GL_NEAREST because in our game we are using pixel art and it won't look sharp if we use bi linear filtering
        GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
        GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
        
        GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, this.width, this.height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, data);
        STBImage.stbi_image_free(data);
        
        textureInstances.add(this);
    }
    
    public static Texture find(String texturename) {
        for(i = 0; i < textureInstances.size(); i++) {
            tmp = getTextureInstances().get(i);
            if(tmp.name.startsWith(texturename))
                return tmp;
        }
        
        return null;
    }
    
    public void bind() {
        GL13.glActiveTexture(GL13.GL_TEXTURE0);
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, id);
    }
    
    public void unbind() {
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
    }
    
    public static void cleanUp() {
        for(i = 0; i < textureInstances.size(); i++) {
            GL13.glDeleteTextures(textureInstances.get(i).getId());
        }
    }
    
    public String getName() {
        return name;
    }
    public int getId() {
        return id;
    }
    public int getWidth() {
        return width;
    }
    public int getHeight() {
        return height;
    }
    public static List<Texture> getTextureInstances() {
        return textureInstances;
    }
    
}
package com.codebitcookie.graphics;
导入java.nio.ByteBuffer;
导入java.nio.IntBuffer;
导入java.util.LinkedList;
导入java.util.List;
导入org.lwjgl.BufferUtils;
导入org.lwjgl.opengl.GL11;
导入org.lwjgl.opengl.GL13;
导入org.lwjgl.stb.STBImage;
公共阶级结构{
私有字符串名称=”;
私有字符串filepath=“”;
私有int-id;
私人int宽度、高度;
私有静态列表textureInstances=new LinkedList();
私有静态纹理tmp=null;
私有静态int i=0;
公共纹理(字符串名称、字符串文件路径){
this.name=名称;
this.filepath=filepath;
IntBuffer width=BufferUtils.createIntBuffer(1);
IntBuffer height=BufferUtils.createIntBuffer(1);
IntBuffer channels=BufferUtils.createIntBuffer(1);
//使用STBImage获取RGBA格式的像素数据,如果使用BuffereImage,我们将获得ARGB,并且必须将其更改为RGBA
ByteBuffer数据=STBImage.stbi_加载(文件路径、宽度、高度、通道,4);
id=GL11.glGenTextures();
this.width=width.get();
this.height=height.get();
GL11.glBindTexture(GL11.GL_TEXTURE_2D,id);
//请参见注释
//我们使用最接近的GL_,因为在我们的游戏中,我们使用像素艺术,如果我们使用双线性过滤,它看起来不会很锐利
GL11.glTexParameterf(GL11.GL\u纹理\u 2D,GL11.GL\u纹理\u最小\u过滤器,GL11.GL\u最近);
GL11.glTexParameterf(GL11.GL_纹理_2D,GL11.GL_纹理_贴图过滤器,GL11.GL_最近);
GL11.glTexImage2D(GL11.GL_纹理_2D,0,GL11.GL_RGBA,this.width,this.height,0,GL11.GL_RGBA,GL11.GL_无符号_字节,数据);
STBImage.stbi_图像_free(数据);
textureInstances.add(此);
}
公共静态纹理查找(字符串texturename){
对于(i=0;i
最初,这家伙回答了我的帖子 但在一次评论中,我要求他把它作为一个答案发布,但他没有。不幸的是,我的帖子被删除了。这就是为什么我决定把它作为一个答案。答案是:

GL20.glvertexattributepointer(TEXCOORD\u ATTRIB,2,GL11.GL\u UNSIGNED\u INT
,,
您正在使用浮点数据,但告诉OpenGL它包含无符号整数。

我查看了您的代码,没有看到正在为图集中的一个正方形计算纹理坐标。在将图集的一个区域映射到顶点时,您还必须使用这些纹理坐标


mesh.render中使用的顶点是如何从atlas中获取纹理坐标的?

您好,谢谢您的回答,但如果您阅读了我自己的回答,我说我以前的帖子已被删除,所以我再次问了这个问题,然后回答了。您能告诉我为什么这样做吗?谢谢!)