From 4e88d1d1a4eabe4d057db8ced48283ea748142b4 Mon Sep 17 00:00:00 2001 From: Antoine Date: Thu, 13 May 2021 16:30:18 +0200 Subject: [PATCH] Stock --- res/bg.jpeg | Bin 0 -> 24744 bytes res/bird.png | Bin 0 -> 65913 bytes res/pipe.png | Bin 0 -> 69674 bytes shaders/bg.frag | 14 ++++++++ shaders/bg.vert | 16 +++++++++ src/BufferUtils.java | 32 +++++++++++++++++ src/FileUtils.java | 27 +++++++++++++++ src/HelloWorld.java | 2 +- src/Input.java | 13 +++++++ src/Level.java | 40 ++++++++++++++++++++++ src/Matrix4f.java | 80 +++++++++++++++++++++++++++++++++++++++++++ src/Shader.java | 73 +++++++++++++++++++++++++++++++++++++++ src/ShaderUtils.java | 57 ++++++++++++++++++++++++++++++ src/Texture.java | 57 ++++++++++++++++++++++++++++++ src/Vector3f.java | 17 +++++++++ src/VertexArray.java | 72 ++++++++++++++++++++++++++++++++++++++ 16 files changed, 499 insertions(+), 1 deletion(-) create mode 100644 res/bg.jpeg create mode 100644 res/bird.png create mode 100644 res/pipe.png create mode 100644 shaders/bg.frag create mode 100644 shaders/bg.vert create mode 100644 src/BufferUtils.java create mode 100644 src/FileUtils.java create mode 100644 src/Input.java create mode 100644 src/Level.java create mode 100644 src/Matrix4f.java create mode 100644 src/Shader.java create mode 100644 src/ShaderUtils.java create mode 100644 src/Texture.java create mode 100644 src/Vector3f.java create mode 100644 src/VertexArray.java diff --git a/res/bg.jpeg b/res/bg.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..af5a79d2cfa54d871f634ab8244899d0827ffbbb GIT binary patch literal 24744 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
e?;4nC)^^?(IfZVws#> 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 lg~zh1l}+RTN{dF3|H75Cs%f}#^32Oa}Fms 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 literal 0 HcmV?d00001 diff --git a/res/bird.png b/res/bird.png new file mode 100644 index 0000000000000000000000000000000000000000..d80cc86b3dced295a4607caf9e875dfc35c1fd37 GIT binary patch literal 65913 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 literal 0 HcmV?d00001 diff --git a/res/pipe.png b/res/pipe.png new file mode 100644 index 0000000000000000000000000000000000000000..04cd9e774828174bc45d7c0fd4f1b81bccdb7036 GIT binary patch literal 69674 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 literal 0 HcmV?d00001 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(); + } + +}