diff --git a/res/bg.jpeg b/res/bg.jpeg new file mode 100644 index 0000000..af5a79d Binary files /dev/null and b/res/bg.jpeg differ diff --git a/res/bird.png b/res/bird.png new file mode 100644 index 0000000..d80cc86 Binary files /dev/null and b/res/bird.png differ diff --git a/res/pipe.png b/res/pipe.png new file mode 100644 index 0000000..04cd9e7 Binary files /dev/null and b/res/pipe.png differ diff --git a/shaders/bg.frag b/shaders/bg.frag new file mode 100644 index 0000000..57415c7 --- /dev/null +++ b/shaders/bg.frag @@ -0,0 +1,14 @@ +# version 330 core + +layout (location = 0) out vec4 color; + +in DATA { + vec2 tc; +} fs_in; + +uniform sampler2D tex; + +void main() +{ + color = texture(tex, fs_in.tc); +} \ No newline at end of file diff --git a/shaders/bg.vert b/shaders/bg.vert new file mode 100644 index 0000000..eb0ea51 --- /dev/null +++ b/shaders/bg.vert @@ -0,0 +1,16 @@ +# version 330 core + +layout (location = 0) in vec4 position; +layout (location = 2) in vec2 tc; + +uniform mat4 pr_matrix; + +out DATA { + vec2 tc; +} vs_out; + +void main() +{ + gl_Position = pr_matrix * position; + vs_out.tc = tc; +} \ No newline at end of file diff --git a/src/BufferUtils.java b/src/BufferUtils.java new file mode 100644 index 0000000..b1e8d1f --- /dev/null +++ b/src/BufferUtils.java @@ -0,0 +1,32 @@ +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + +public class BufferUtils { + + private BufferUtils() { + + } + + public static ByteBuffer createByteBuffer(byte[] array){ + ByteBuffer result = ByteBuffer.allocateDirect(array.length).order(ByteOrder.nativeOrder()); + result.put(array).flip(); + return result; + } + + public static FloatBuffer createFloatBuffer(float[] array){ + FloatBuffer result = ByteBuffer.allocateDirect(array.length << 2).order(ByteOrder.nativeOrder()).asFloatBuffer(); + result.put(array).flip(); + return result; + } + + public static IntBuffer createIntBuffer(int[] array){ + IntBuffer result = ByteBuffer.allocateDirect(array.length << 2).order(ByteOrder.nativeOrder()).asIntBuffer(); + result.put(array).flip(); + return result; + } + + + +} diff --git a/src/FileUtils.java b/src/FileUtils.java new file mode 100644 index 0000000..56c672f --- /dev/null +++ b/src/FileUtils.java @@ -0,0 +1,27 @@ +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; + +public class FileUtils { + + private FileUtils() { + + } + + public static String loadAsString(String file){ + StringBuilder result = new StringBuilder(); + try { + BufferedReader reader = new BufferedReader(new FileReader(file)); + String buffer = ""; + while ((buffer = reader.readLine()) != null) { + result.append(buffer +"\n"); + } + reader.close(); + } catch (IOException e){ + e.printStackTrace(); + } + return result.toString(); + } + +} diff --git a/src/HelloWorld.java b/src/HelloWorld.java index 284f611..046e316 100644 --- a/src/HelloWorld.java +++ b/src/HelloWorld.java @@ -93,7 +93,7 @@ public class HelloWorld { GL.createCapabilities(); // Set the clear color - glClearColor(1.0f, 0.0f, 0.0f, 0.0f); + glClearColor(1.0f, 0.0f, 1.0f, 0.0f); // Run the rendering loop until the user has attempted to close // the window or has pressed the ESCAPE key. diff --git a/src/Input.java b/src/Input.java new file mode 100644 index 0000000..26efef9 --- /dev/null +++ b/src/Input.java @@ -0,0 +1,13 @@ +import org.lwjgl.glfw.GLFW; +import org.lwjgl.glfw.GLFWKeyCallback; + +public class Input extends GLFWKeyCallback { + + public static boolean[] keys = new boolean[65536]; + + @Override + public void invoke(long window, int key, int scancode, int action, int mods) { + keys[key] = action != GLFW.GLFW_RELEASE; + + } +} diff --git a/src/Level.java b/src/Level.java new file mode 100644 index 0000000..f0a8e44 --- /dev/null +++ b/src/Level.java @@ -0,0 +1,40 @@ +import static org.lwjgl.glfw.GLFW.*; + +public class Level { + + private VertexArray background; + private Texture bgTexture; + + public Level(){ + float[] vertices = new float[] { + -10.0f, -10.0f * 9.0f / 16.0f, 0.0f, + -10.0f, 10.0f * 9.0f / 16.0f, 0.0f, + 0f, 10.0f * 9.0f / 16.0f, 0.0f, + 0f, -10.0f * 9.0f / 16.0f, 0.0f, + }; + + byte[] indices = new byte[] { + 0, 1, 2, + 2, 3, 0 + }; + + float[] tcs = new float[] { + 0, 1, + 0, 0, + 1, 0, + 1, 1 + }; + + background = new VertexArray(vertices, indices, tcs); + bgTexture = new Texture("res/bg.jpeg"); + } + + public void render() { + bgTexture.bind(); + Shader.BG.enable(); + background.render(); + Shader.BG.disable(); + bgTexture.unbind(); + } + +} diff --git a/src/Matrix4f.java b/src/Matrix4f.java new file mode 100644 index 0000000..bdc9985 --- /dev/null +++ b/src/Matrix4f.java @@ -0,0 +1,80 @@ +import java.nio.FloatBuffer; + +public class Matrix4f { + + public static final int SIZE = 4 * 4; + public float[] elements = new float[4 * 4]; + + public Matrix4f(){ + + } + + public static Matrix4f identity(){ + Matrix4f result = new Matrix4f(); + for (int i = 0; i < SIZE; i++){ + result.elements[i] = 0.0f; + } + result.elements[0] = 1.0f; + result.elements[1 + 4] = 1.0f; + result.elements[2 + 2 * 4] = 1.0f; + result.elements[3 + 3 * 4] = 1.0f; + return result; + } + + public static Matrix4f orthographic(float left, float right, float bottom, float top, float near, float far){ + Matrix4f result = identity(); + + result.elements[0] = 2.0f / (right - left); + result.elements[1 + 4] = 2.0f / (top - bottom); + result.elements[2 + 2 * 4] = 2.0f / (near - far); + + result.elements[3 * 4] = (left + right) / (left - right); + result.elements[1 + 3 * 4] = (bottom + top) / (bottom - top); + result.elements[2 + 3 * 4] = (far + near) / (far - near); + + return result; + } + + public static Matrix4f translate(Vector3f vector){ + Matrix4f result= identity(); + result.elements[0 + 3*4] = vector.x; + result.elements[1 + 3*4] = vector.y; + result.elements[2 + 3*4] = vector.z; + + return result; + } + + public static Matrix4f rotate(float angle){ + Matrix4f result = identity(); + float r = (float) Math.toRadians(angle); + float cos = (float) Math.cos(angle); + float sin = (float) Math.sin(angle); + + result.elements[0 + 0 * 4] = cos; + result.elements[1 + 0 * 4] = sin; + + result.elements[0 + 1 * 4] = -sin; + result.elements[1 + 1 * 4] = cos; + + return result; + } + + public Matrix4f multiply(Matrix4f matrix){ + Matrix4f result = new Matrix4f(); + for (int y = 0; y< 4; y++){ + for (int x = 0; x< 4; x++){ + float sum = 0.0f; + for (int e = 0; e< 4; e++){ + sum += this.elements[x + e * 4] * matrix.elements[e + y * 4]; + } + result.elements[x + y * 4] = sum; + } + } + return result; + } + + public FloatBuffer toFloatBuffer() { + return BufferUtils.createFloatBuffer(elements); + } + +} diff --git a/src/Shader.java b/src/Shader.java new file mode 100644 index 0000000..c7173f1 --- /dev/null +++ b/src/Shader.java @@ -0,0 +1,73 @@ +import java.util.HashMap; +import java.util.Map; + +import static org.lwjgl.opengl.GL20.*; + +public class Shader { + + public static final int VERTEX_ATTRIB = 0; + public static final int TCOORD_ATTRIB = 1; + + public static Shader BG; + + private boolean enabled = false; + + //Identifiant du programme resultat de la compilation des shaders + private final int ID; + private Map locationCache = new HashMap(); + + /* + Crée le fragment et le vertex shader les lie dans un programme dont il renvoie l'identifiant. + */ + public Shader(String vertex, String fragment) { + ID = ShaderUtils.load(vertex, fragment); + } + + public static void loadAll(){ + BG = new Shader("shaders/bg.vert", "shaders/bg.frag"); + } + + public int getUniform(String name){ + if (locationCache.containsKey(name)) return locationCache.get(name); + int result = glGetUniformLocation(ID, name); + if (result == -1) System.err.println("Could not find uniform variable " + name); + else locationCache.put(name, result); + return result; + } + + public void setUniform1i(String name, int value) { + if (!enabled) enable(); + glUniform1i(getUniform(name), value); + } + + public void setUniform1f(String name, float value) { + if (!enabled) enable(); + glUniform1f(getUniform(name), value); + } + + public void setUniform2f(String name, float x, float y) { + if (!enabled) enable(); + glUniform2f(getUniform(name), x, y); + } + + public void setUniform3f(String name, Vector3f vector) { + if (!enabled) enable(); + glUniform3f(getUniform(name), vector.x, vector.y, vector.z); + } + + public void setUniformMat4f(String name, Matrix4f matrix){ + if (!enabled) enable(); + glUniformMatrix4fv(getUniform(name), false, matrix.toFloatBuffer()); + } + + public void enable() { + glUseProgram(ID); + enabled = true; + } + + public void disable() { + glUseProgram(0); + enabled = false; + } + +} diff --git a/src/ShaderUtils.java b/src/ShaderUtils.java new file mode 100644 index 0000000..625c84c --- /dev/null +++ b/src/ShaderUtils.java @@ -0,0 +1,57 @@ + +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL20.*; + +public class ShaderUtils { + + private ShaderUtils(){ + + } + + public static int load(String vertPath, String fragPath){ + String vert = FileUtils.loadAsString(vertPath); + String frag = FileUtils.loadAsString(fragPath); + return create(vert, frag); + } + + public static int create(String vert, String frag){ + // On crée et compile le vertex et le fragment shader + int vertID = glCreateShader(GL_VERTEX_SHADER); + int fragID = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(vertID, vert); + glShaderSource(fragID, frag); + + glCompileShader(vertID); + if (glGetShaderi(vertID, GL_COMPILE_STATUS) == GL_FALSE){ + System.err.println("Failed to compile vertex shader"); + System.err.println(glGetShaderInfoLog(vertID)); + return -1; + } + + glCompileShader(fragID); + if (glGetShaderi(fragID, GL_COMPILE_STATUS) == GL_FALSE){ + System.err.println("Failed to compile fragment shader"); + System.err.println(glGetShaderInfoLog(fragID)); + return -1; + } + //on lie les shaders au programme + int program = glCreateProgram(); + glAttachShader(program, vertID); + glAttachShader(program, fragID); + glLinkProgram(program); + + if(glGetProgrami(program, GL_LINK_STATUS) == GL_FALSE) { + System.err.println("Failed to link vertex and fragment shader"); + System.err.println(glGetShaderInfoLog(program)); + return -1; + } + + glValidateProgram(program); + + glDeleteShader(vertID); + glDeleteShader(fragID); + + return program; + } + +} diff --git a/src/Texture.java b/src/Texture.java new file mode 100644 index 0000000..175210a --- /dev/null +++ b/src/Texture.java @@ -0,0 +1,57 @@ +import java.awt.image.BufferedImage; +import java.io.FileInputStream; +import java.io.IOException; + +import javax.imageio.ImageIO; + +import static org.lwjgl.opengl.GL11.*; + +public class Texture { + + private int width, height; + private int texture; + + public Texture(String path) { + texture = load(path); + } + + private int load(String path) { + int[] pixels = null; + try { + BufferedImage image = ImageIO.read(new FileInputStream(path)); + width = image.getWidth(); + height = image.getHeight(); + pixels = new int[width * height]; + image.getRGB(0, 0, width, height, pixels, 0, width); + } catch (IOException e) { + e.printStackTrace(); + } + + int[] data = new int[width * height]; + for (int i = 0; i < width * height; i++) { + int a = (pixels[i] & 0xff000000) >> 24; + int r = (pixels[i] & 0xff0000) >> 16; + int g = (pixels[i] & 0xff00) >> 8; + int b = (pixels[i] & 0xff); + + data[i] = a << 24 | b << 16 | g << 8 | r; + } + + int result = glGenTextures(); + glBindTexture(GL_TEXTURE_2D, result); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, BufferUtils.createIntBuffer(data)); + glBindTexture(GL_TEXTURE_2D, 0); + return result; + } + + public void bind() { + glBindTexture(GL_TEXTURE_2D, texture); + } + + public void unbind() { + glBindTexture(GL_TEXTURE_2D, 0); + } + +} \ No newline at end of file diff --git a/src/Vector3f.java b/src/Vector3f.java new file mode 100644 index 0000000..3eb454e --- /dev/null +++ b/src/Vector3f.java @@ -0,0 +1,17 @@ +public class Vector3f { + + public float x, y, z; + + public Vector3f(){ + x = 0.0f; + y = 0.0f; + z = 0.0f; + } + + public Vector3f(float x, float y, float z){ + this.x = x; + this.y = y; + this.z = z; + } + +} diff --git a/src/VertexArray.java b/src/VertexArray.java new file mode 100644 index 0000000..e3594fb --- /dev/null +++ b/src/VertexArray.java @@ -0,0 +1,72 @@ +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL15.*; +import static org.lwjgl.opengl.GL30.*; + + +public class VertexArray { + + private int vao, vbo, ibo, tbo; + private int count; + + /* + Donne la description de l'agencement des differents buffer + */ + public VertexArray(float[] vertices, byte[] indices, float[] textureCoordinates){ + count = indices.length; + + /* + VERTEX ARRAY OBJECT + */ + vao = glGenVertexArrays(); + glBindVertexArray(vao); + + /* + VERTEX BUFFER OBJECT + */ + vbo = glGenBuffers(); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(vertices), GL_STATIC_DRAW); + glVertexAttribPointer(Shader.VERTEX_ATTRIB, 3, GL_FLOAT, false, 0, 0); + glEnableVertexAttribArray(Shader.VERTEX_ATTRIB); + + /* + TEXTURE BUFFER OBJECT + */ + tbo = glGenBuffers(); + glBindBuffer(GL_ARRAY_BUFFER, tbo); + glBufferData(GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(textureCoordinates), GL_STATIC_DRAW); + glVertexAttribPointer(Shader.TCOORD_ATTRIB, 2, GL_FLOAT, false, 0, 0); + glEnableVertexAttribArray(Shader.TCOORD_ATTRIB); + + /* + INDICES BUFFER OBJECT OR ELEMENT BUFFER OBJECT + */ + ibo = glGenBuffers(); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, BufferUtils.createByteBuffer(indices), GL_STATIC_DRAW); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindVertexArray(0); + } + + public void bind(){ + glBindVertexArray(vao); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); + } + + public void unbind(){ + glBindVertexArray(0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + + public void draw(){ + glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_BYTE, 0); + } + + public void render(){ + bind(); + draw(); + } + +}