diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..763ac98 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/.idea/ +/.gitignore +/jeu-de-combat.iml +/target/ +/bin/ diff --git a/.idea/jeu-de-combat.iml b/.idea/jeu-de-combat.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/jeu-de-combat.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..90e6b95 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/docs/Architecture_Moteur_V1.pdf b/docs/Architecture_Moteur_V1.pdf new file mode 100644 index 0000000..32d5633 Binary files /dev/null and b/docs/Architecture_Moteur_V1.pdf differ diff --git a/shaders/ObjectGl/frag.glsl b/shaders/ObjectGl/frag.glsl new file mode 100644 index 0000000..f76853f --- /dev/null +++ b/shaders/ObjectGl/frag.glsl @@ -0,0 +1,10 @@ +#version 330 core + +out vec4 FragColor; + +uniform float time; + +void main() +{ + FragColor = vec4(1.0f, 1.0f, 1.0f, 1.0f); +} \ No newline at end of file diff --git a/shaders/ObjectGl/vert.glsl b/shaders/ObjectGl/vert.glsl new file mode 100644 index 0000000..58be87a --- /dev/null +++ b/shaders/ObjectGl/vert.glsl @@ -0,0 +1,12 @@ +#version 330 core + +layout (location = 0) in vec3 aPos; + +uniform mat4 projection; +uniform mat4 view; +uniform mat4 transform; + +void main() +{ + gl_Position = projection * view * transform * vec4(aPos, 1.0); +} \ No newline at end of file diff --git a/shaders/ObjectGlColor/frag.glsl b/shaders/ObjectGlColor/frag.glsl new file mode 100644 index 0000000..3a1ca99 --- /dev/null +++ b/shaders/ObjectGlColor/frag.glsl @@ -0,0 +1,12 @@ +#version 330 core + +in vec3 color; + +out vec4 FragColor; + +uniform float time; + +void main() +{ + FragColor = vec4(color, 1.0f); +} \ No newline at end of file diff --git a/shaders/ObjectGlColor/vert.glsl b/shaders/ObjectGlColor/vert.glsl new file mode 100644 index 0000000..2a867f6 --- /dev/null +++ b/shaders/ObjectGlColor/vert.glsl @@ -0,0 +1,16 @@ +#version 330 core + +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aColor; + +out vec3 color; + +uniform mat4 projection; +uniform mat4 view; +uniform mat4 transform; + +void main() +{ + gl_Position = projection * view * transform * vec4(aPos, 1.0); + color = aColor; +} \ No newline at end of file diff --git a/shaders/ObjectGlTex/frag.glsl b/shaders/ObjectGlTex/frag.glsl new file mode 100644 index 0000000..a13c996 --- /dev/null +++ b/shaders/ObjectGlTex/frag.glsl @@ -0,0 +1,13 @@ +#version 410 core + +out vec4 FragColor; + +in vec2 texCoord; + +uniform sampler2D texture1; +uniform float time; + +void main() +{ + FragColor = texture(texture1, texCoord); +} \ No newline at end of file diff --git a/shaders/ObjectGlTex/vert.glsl b/shaders/ObjectGlTex/vert.glsl new file mode 100644 index 0000000..64f4b92 --- /dev/null +++ b/shaders/ObjectGlTex/vert.glsl @@ -0,0 +1,16 @@ +#version 410 core + +layout (location = 0) in vec3 aPos; +layout (location = 2) in vec2 aTexCoord; + +out vec2 texCoord; + +uniform mat4 projection; +uniform mat4 view; +uniform mat4 transform; + +void main() +{ + gl_Position = projection * view * transform * vec4(aPos, 1.0); + texCoord = aTexCoord; +} \ No newline at end of file diff --git a/shaders/ObjectGlTexColor/frag.glsl b/shaders/ObjectGlTexColor/frag.glsl new file mode 100644 index 0000000..2f6e845 --- /dev/null +++ b/shaders/ObjectGlTexColor/frag.glsl @@ -0,0 +1,19 @@ +#version 410 core + +out vec4 FragColor; + +in vec4 color; +in vec2 texCoord; + +uniform sampler2D texture1; +uniform float time; + +void main() +{ + vec4 tex = texture(texture1, texCoord); + if (tex.a == 0.0){ + FragColor = tex; + } else{ + FragColor = mix(tex, color, 0.5); + } +} \ No newline at end of file diff --git a/shaders/ObjectGlTexColor/vert.glsl b/shaders/ObjectGlTexColor/vert.glsl new file mode 100644 index 0000000..d1e7488 --- /dev/null +++ b/shaders/ObjectGlTexColor/vert.glsl @@ -0,0 +1,19 @@ +#version 410 core + +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aColor; +layout (location = 2) in vec2 aTexCoord; + +out vec2 texCoord; +out vec4 color; + +uniform mat4 projection; +uniform mat4 view; +uniform mat4 transform; + +void main() +{ + gl_Position = projection * view * transform * vec4(aPos, 1.0); + color = vec4(aColor, 1.0f); + texCoord = aTexCoord; +} \ No newline at end of file diff --git a/shaders/StylishShaders/BasicVert.glsl b/shaders/StylishShaders/BasicVert.glsl new file mode 100644 index 0000000..625b793 --- /dev/null +++ b/shaders/StylishShaders/BasicVert.glsl @@ -0,0 +1,21 @@ +#version 410 core + +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aColor; +layout (location = 2) in vec2 aTexCoord; + +out vec2 fragCoord; +out vec2 texCoord; +out vec4 color; + +uniform mat4 projection; +uniform mat4 view; +uniform mat4 transform; + +void main() +{ + gl_Position = projection * view * transform * vec4(aPos, 1.0); + color = vec4(aColor, 1.0f); + texCoord = aTexCoord; + fragCoord = aPos.xy; +} \ No newline at end of file diff --git a/shaders/StylishShaders/EnergyWave.glsl b/shaders/StylishShaders/EnergyWave.glsl new file mode 100644 index 0000000..a550c6b --- /dev/null +++ b/shaders/StylishShaders/EnergyWave.glsl @@ -0,0 +1,21 @@ +#version 410 + +out vec4 FragColor; + +in vec4 color; +in vec2 fragCoord; +in vec2 texCoord; + +uniform sampler2D texture1; +uniform float time; + +void main() +{ // Pas fini c'est moche + vec4 colorPoweredUp = abs(vec4(color.xyz * sin(fragCoord.y + time*20), 1.0)); + vec4 tex = texture(texture1, texCoord); + if (tex.a == 0.0){ + FragColor = tex; + } else{ + FragColor = mix(tex, colorPoweredUp, 0.5); + } +} diff --git a/src/Main.java b/src/Main.java index 7c9120e..601def5 100644 --- a/src/Main.java +++ b/src/Main.java @@ -7,8 +7,8 @@ * */ -import launcher.Launcher; import engine.Engine; +import launcher.Launcher; import javafx.application.Application; public class Main { diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 2195c4e..505ac5a 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -1,14 +1,305 @@ -/** - * CLASS ENGINE - * - * Classe principale du moteur de jeu - * - * @author François Autin - * - */ - package engine; +import engine.math.*; +import engine.object.*; + +import org.lwjgl.glfw.GLFW; +import org.lwjgl.glfw.GLFWFramebufferSizeCallback; +import org.lwjgl.glfw.GLFWVidMode; +import org.lwjgl.opengl.GL; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.util.ArrayList; +import java.util.List; + +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.system.MemoryUtil.NULL; + public class Engine { + private static long window; + + private final List objectsGl; + + private static boolean present = glfwJoystickPresent(GLFW_JOYSTICK_1); + + private boolean running; + + /** + * Create the engine and initial attributes use .init() to start the engine + */ + public Engine() { + this.running = false; + this.objectsGl = new ArrayList<>(); + float width = 1280.0f; + ObjectGl.projection = Matrix4f.orthographic(-width, width, -width * 9.0f / 16.0f, width * 9.0f / 16.0f, 0.1f, 100.0f); + ObjectGl.view = Matrix4f.translate(new Vector3f(0.0f,0.0f,1.0f)); + } + + /** + * Start the engine + * Create the window + * Set the color of the background + */ + public void init() { + glfwInit(); + + this.running = true; + + glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); //On utilise la version 3.3 d'openGL + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); //Compatible MAC + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //Le core profile est l'interface 'avancé' d'openGL + + int width = 1280; + int height = 720; + this.setWindow(glfwCreateWindow(width, height, "Boulevard Combattant", NULL, NULL)); + assert getWindow() != NULL; + + + System.out.println(present); + + // On récupère les informations du moniteur principal + GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + assert vidmode != null; + + // On met la fenêtre au centre de l'écran principale + glfwSetWindowPos(getWindow(), (vidmode.width() - width)/2, (vidmode.height() - height)/2); + + glfwSetKeyCallback(getWindow(), new Input()); + glfwSetInputMode(getWindow(), GLFW_STICKY_KEYS, GLFW_TRUE); + + // Contexte = zone cible des rendus + glfwMakeContextCurrent(getWindow()); + glfwShowWindow(getWindow()); + GL.createCapabilities(); + + glfwSetFramebufferSizeCallback(getWindow(), resizeWindow); + + glEnable(GL_DEPTH_TEST); // Z-Buffer plus z est grand plus l'objet est proche de la camera limite à 100.0f au dela l'objet disparait + + glEnable(GL_BLEND); // Transparence + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glClearColor(0.2f, 0.3f, 0.3f, 1.0f); + System.out.println("OpenGL: " + glGetString(GL_VERSION)); + } + + /** + * + */ + public void update(){ + glfwPollEvents(); + } + + /** + * + */ + public void render(){ + + glClearColor(0.2f, 0.3f, 0.3f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + objectsGl.sort(new SortZ()); + for (ObjectGl objectGl : objectsGl) { + objectGl.render(); + } + + int error = glGetError(); + if (error != GL_NO_ERROR) System.out.println(error); + glfwSwapBuffers(getWindow()); //Envoie le buffer vers le moniteur + } + + /** + * Add obj to the render queue + * @param obj + */ + public void add_objectGl(ObjectGl obj){ + objectsGl.add(obj); + } + + public void remove_objectGl(ObjectGl obj){ + objectsGl.remove(obj); + } + + public boolean isRunning(){ + return running; + } + + public void setRunning(boolean b){ + running = b; + } + + public boolean shouldClose(){ + return glfwWindowShouldClose(getWindow()); + } + + public static long getWindow() { + return window; + } + + public void setWindow(long window) { + Engine.window = window; + } + + /** + * Est appelé àa chaque modification de la taille de la fenêtre, et modifie la taille de la zone de rendu + * pour quelle corresponde à la taille de la fenêtre + */ + private static final GLFWFramebufferSizeCallback resizeWindow = new GLFWFramebufferSizeCallback(){ + @Override + public void invoke(long window, int width, int height){ + glViewport(0,0,width,height); + } + }; + + public static void main(String[] args) { + Engine engine = new Engine(); + int speed = 10 ; //vitesse déplacement Object + engine.init(); + + // Add objects to render + String path = "textures/zangief_sprite.png"; + + String path2 = "textures/awesomeface.png"; + + ObjectGl zangief = new ObjectGl(0f,60f,80f,10f, path, null); + zangief.setTextureWrap(58,0,62,84, ObjectGl.STICK_TOP); + engine.add_objectGl(zangief); + zangief.translate(new Vector3f(-5000.0f,500.0f,10.0f)); + zangief.setColor(new Vector3f(0.0f, 0.0f, 1.0f)); + zangief.useTime = true; + zangief.setShader("shaders/StylishShaders/BasicVert.glsl","shaders/StylishShaders/EnergyWave.glsl"); + +// ObjectGl smiley2 = new ObjectGl(0.0f,500.0f,500.0f, 1f, path2, null); +// engine.add_objectGl(smiley2); +// smiley2.translate(new Vector3f(0.0f,0.0f,5.0f)); + + long timer = System.currentTimeMillis(); + long lastFrame; + int frame = 0; + boolean nextFrame = false; + + while(engine.isRunning()){ + lastFrame = System.currentTimeMillis(); + // Game logic should fit here + + if (present) { + gamepadInput(zangief, speed); + } + + input(zangief, speed); +// input(smiley2, speed); + + /* + ******************** + * essential part v * + ******************** + */ + engine.update(); + engine.render(); + + frame++; + + if (System.currentTimeMillis() - timer > 1000) { + timer += 1000; + System.out.println("FPS: " + frame); + frame = 0; + } + + while(!nextFrame){ + nextFrame = System.currentTimeMillis() - lastFrame >= 16.66f; + } + + nextFrame = false; + if(engine.shouldClose()) engine.setRunning(false); + } + } + + private static void gamepadInput(ObjectGl token, int speed) { + ByteBuffer gamepadButton = glfwGetJoystickButtons(GLFW_JOYSTICK_1); + FloatBuffer gamepadAxes = glfwGetJoystickAxes(GLFW_JOYSTICK_1); + + assert gamepadAxes != null; + assert gamepadButton != null; + + String name = GLFW.glfwGetJoystickName(GLFW_JOYSTICK_1); + System.out.println("GamePad Name :" + name); +// for (int i =0 ; i < gamepadAxes.capacity(); i++) { +// System.out.println(i + " :" + gamepadAxes.get(i)); +// } + + + if (gamepadButton.get(0) ==1 ) { // appuie sur croix(PlayStation) A (Xbox) + token.translate(new Vector3f ( 0.0f, speed * 5.0f, 0.0f)); + } + if ( (gamepadAxes.get(2) < -0.1 || gamepadAxes.get(2) > 0.1) ) { // de droite à gauche //joystick gauche + token.translate(new Vector3f (5*speed * gamepadAxes.get(2), 0.0f, 0.0f)); + if ( gamepadAxes.get(2) < -0.1 ){ + token.setTextureWrap(121,0,57,80, ObjectGl.DEFAULT); + }else if (gamepadAxes.get(2) > 0.1) { + token.setTextureWrap(178,0,62,82, ObjectGl.DEFAULT); + } + } + + if ( (gamepadAxes.get(3) < -0.1 || gamepadAxes.get(3) > 0.1) ) { // de haut en bas //joystick gauche + token.translate(new Vector3f (0.0f, -5* speed * gamepadAxes.get(3), 0.0f)); + + } + + /* Buttons + 0 : Croix / A + 1: rond /B + 2: carré / X + 3: triangle / Y + 4: L1 / LB + 5: R1 / RB + 6:select + 7:start + 8:L3 + 9:R3 + 10: haut + 11: droite + 12: bas + 13: gauche + */ + + /* Axes + 0 : left X axe ( right : 1 left -1) + 1: left Y axe ( down : 1 , Up -1) + 2: right X axe ( right : 1 left -1) + 3: right Y axe ( down : 1 , Up -1) + 4:L2 / LT : 1 active, -1 unactive + 5: R2 /RT : 1 active, -1 unactive + */ + + } + + public static void input(ObjectGl token, int speed) { + boolean keyPressed = false; + if (Input.isKeyDown(GLFW.GLFW_KEY_S)) { + token.setTextureWrap(161,260,56,59, ObjectGl.STICK_BOTTOM); + keyPressed = true; + } else if (Input.isKeyDown(GLFW.GLFW_KEY_W)) { +// token.translate(new Vector3f (0.0f, speed * 5.0f, 0.0f)); + keyPressed = true; + } + if (Input.isKeyDown(GLFW.GLFW_KEY_A)) { + token.translate(new Vector3f (speed *-5.0f, 0.0f, 0.0f)); +// token.setTextureWrap(121,0,57,80, ObjectGl.STICK_TOP); + token.setTextureWrap(121,0,57,82, ObjectGl.STICK_TOP); + keyPressed = true; + } + else if (Input.isKeyDown(GLFW.GLFW_KEY_D)) { + token.translate(new Vector3f (speed * 5.0f, 0.0f, 0.0f)); +// token.setTextureWrap(178,0,62,82, ObjectGl.STICK_TOP); + token.setTextureWrap(178,0,62,82, ObjectGl.STICK_TOP); + keyPressed = true; + } +// if (!keyPressed) token.setTextureWrap(58,0,62,84, ObjectGl.STICK_TOP); + if (!keyPressed) token.setTextureWrap(58,0,62,82, ObjectGl.STICK_TOP); + } } diff --git a/src/engine/Input.java b/src/engine/Input.java new file mode 100644 index 0000000..194335c --- /dev/null +++ b/src/engine/Input.java @@ -0,0 +1,26 @@ +package engine; + +import org.lwjgl.glfw.GLFW; +import org.lwjgl.glfw.GLFWKeyCallback; + +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.opengl.GL11.*; + +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; + if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) + glfwSetWindowShouldClose(window, true); + else if(key == GLFW_KEY_SPACE && action == GLFW_PRESS) //Switch to wireframe + if (glGetInteger(GL_POLYGON_MODE) == GL_FILL) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + else glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + } + public static boolean isKeyDown(int keyCode) { + return glfwGetKey(Engine.getWindow(), keyCode) == 1; + } + +} diff --git a/src/engine/graphics/Shader.java b/src/engine/graphics/Shader.java new file mode 100644 index 0000000..19ae290 --- /dev/null +++ b/src/engine/graphics/Shader.java @@ -0,0 +1,75 @@ +package engine.graphics; + +import engine.math.Matrix4f; +import engine.utils.ShaderUtils; +import engine.math.Vector3f; + +import java.util.HashMap; +import java.util.Map; + +import static org.lwjgl.opengl.GL20.*; + +public class Shader { + + 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 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 setUniform4f(String name, float x, float y, float z, float w) { + if (!enabled) enable(); + glUniform4f(getUniform(name), x, y, z, w); + } + + 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/engine/graphics/Texture.java b/src/engine/graphics/Texture.java new file mode 100644 index 0000000..4f06db9 --- /dev/null +++ b/src/engine/graphics/Texture.java @@ -0,0 +1,75 @@ +package engine.graphics; + +import engine.utils.BufferUtils; +import org.lwjgl.opengl.GL11; + +import java.awt.image.BufferedImage; +import java.io.FileInputStream; +import java.io.IOException; + +import javax.imageio.ImageIO; + +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL30.*; + +public class Texture { + + private int width, height; + private int texture; + private int index; + + public Texture(String path, int index) { + this.index = index; + 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); + GL11.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, BufferUtils.createIntBuffer(data)); + glGenerateMipmap(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + return result; + } + + public void bind() { + glActiveTexture(GL_TEXTURE0 + this.index); + glBindTexture(GL_TEXTURE_2D, texture); + } + + public void unbind() { + glBindTexture(GL_TEXTURE_2D, 0); + } + + public int getWidth(){ + return width; + } + + public int getHeight(){ + return height; + } + +} diff --git a/src/engine/graphics/VertexArray.java b/src/engine/graphics/VertexArray.java new file mode 100644 index 0000000..a8dc154 --- /dev/null +++ b/src/engine/graphics/VertexArray.java @@ -0,0 +1,89 @@ +package engine.graphics; + +import engine.utils.BufferUtils; + +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, EBO, CBO, TBO; + private int count; + + public VertexArray(float[] vertices, byte[] indices, float[] color, float[] texture) { + count = indices.length; + // VERTEX ARRAY OBJECT + VAO = glGenVertexArrays(); + glBindVertexArray(VAO); + + glEnableVertexAttribArray(0); + + // VERTEX BUFFER OBJECT + createVertexBufferObject(vertices); + // COLOR BUFFER OBJECT + if (color != null) createColorBufferObject(color); + // TEXTURE BUFFER OBJECT + if (texture != null) createTextureBufferObject(texture); + + EBO = glGenBuffers(); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, BufferUtils.createByteBuffer(indices), GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + } + + private void createVertexBufferObject(float[] vertices){ + VBO = glGenBuffers(); + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(vertices), GL_STATIC_DRAW); + glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0); + glEnableVertexAttribArray(0); + } + + private void createColorBufferObject(float[] color){ + CBO = glGenBuffers(); + glBindBuffer(GL_ARRAY_BUFFER, CBO); + glBufferData(GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(color), GL_STATIC_DRAW); + glVertexAttribPointer(1, 3, GL_FLOAT, false, 0, 0); + glEnableVertexAttribArray(1); + } + + private void createTextureBufferObject(float[] texture){ + TBO = glGenBuffers(); + glBindBuffer(GL_ARRAY_BUFFER, TBO); + glBufferData(GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(texture), GL_STATIC_DRAW); + glVertexAttribPointer(2, 2, GL_FLOAT, false, 0, 0); + glEnableVertexAttribArray(2); + } + + public void swapVertexBufferObject(float[] vertices){ + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(vertices), GL_STATIC_DRAW); + } + + public void swapTextureBufferObject(float [] texture){ + glBindBuffer(GL_ARRAY_BUFFER, TBO); + glBufferData(GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(texture), GL_STATIC_DRAW); + } + + public void bind(){ + glBindVertexArray(this.VAO); + } + + public void unbind(){ + glBindVertexArray(0); + } + + public void draw(){ + glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_BYTE, 0); + } + + public void render(){ + bind(); + draw(); + unbind(); + } + +} diff --git a/src/engine/math/Matrix4f.java b/src/engine/math/Matrix4f.java new file mode 100644 index 0000000..6291eb9 --- /dev/null +++ b/src/engine/math/Matrix4f.java @@ -0,0 +1,123 @@ +package engine.math; + +import engine.utils.BufferUtils; + +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 + 0 * 4] = 1.0f; + result.elements[1 + 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 + 0 * 4] = 2.0f / (right - left); + result.elements[1 + 1 * 4] = 2.0f / (top - bottom); + result.elements[2 + 2 * 4] = 2.0f / (near - far); + + result.elements[0 + 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 scale(Vector3f vector){ + Matrix4f result= identity(); + result.elements[0 + 0*4] = vector.x; + result.elements[1 + 1*4] = vector.y; + result.elements[2 + 2*4] = vector.z; + + return result; + } + + public static Matrix4f rotateX(float angle){ + Matrix4f result = identity(); + float r = (float) Math.toRadians(angle); + float cos = (float) Math.cos(r); + float sin = (float) Math.sin(r); + + result.elements[1 + 1 * 4] = cos; + result.elements[2 + 1 * 4] = -sin; + + result.elements[1 + 2 * 4] = sin; + result.elements[2 + 2 * 4] = cos; + + return result; + } + + public static Matrix4f rotateY(float angle){ + Matrix4f result = identity(); + float r = (float) Math.toRadians(angle); + float cos = (float) Math.cos(r); + float sin = (float) Math.sin(r); + + result.elements[0 + 0 * 4] = cos; + result.elements[2 + 0 * 4] = sin; + + result.elements[0 + 2 * 4] = -sin; + result.elements[2 + 2 * 4] = cos; + + return result; + } + + public static Matrix4f rotateZ(float angle){ + Matrix4f result = identity(); + float r = (float) Math.toRadians(angle); + float cos = (float) Math.cos(r); + float sin = (float) Math.sin(r); + + 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/engine/math/Primitive.java b/src/engine/math/Primitive.java new file mode 100644 index 0000000..56f2f95 --- /dev/null +++ b/src/engine/math/Primitive.java @@ -0,0 +1,38 @@ +package engine.math; + +import engine.math.Vector3f; + +public class Primitive { + + public static float[] createRectangle(float z, float w, float h){ + return new float[] { + 0 , 0 , z, // Haut gauche + 0 + w, 0 , z, // Haut droit + 0 + w, 0 - h, z, // Bas droit + 0 , 0 - h, z // Bas gauche + }; + } + + /** + * Chaque point correspond à un vertex de la primite le reste est interpolé + */ + public static float[] stdTexWrap = new float[] { + 0.0f, 0.0f, + 1.0f, 0.0f, + 1.0f, 1.0f, + 0.0f, 1.0f + }; + + public static float[] upperHalfTexWrap = new float[] { + 0.0f, 0.0f, + 1.0f, 0.0f, + 1.0f, 0.5f, + 0.0f, 0.5f + }; + + public static byte[] rectangle_indices = new byte[] { + 0, 1, 3, + 1, 2, 3 + }; + +} diff --git a/src/engine/math/Vector3f.java b/src/engine/math/Vector3f.java new file mode 100644 index 0000000..4f7c98b --- /dev/null +++ b/src/engine/math/Vector3f.java @@ -0,0 +1,24 @@ +package engine.math; + +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; + } + + public void divXY(float div){ + this.x /= div; + this.y /= div; + } + +} diff --git a/src/engine/object/ObjectGl.java b/src/engine/object/ObjectGl.java new file mode 100644 index 0000000..723cc2d --- /dev/null +++ b/src/engine/object/ObjectGl.java @@ -0,0 +1,237 @@ +package engine.object; + +import engine.graphics.*; +import engine.math.*; + +import static org.lwjgl.glfw.GLFW.glfwGetTime; + +/** + * + */ +public class ObjectGl { + + /** + * STATE CONST DECLARATION + */ + public static final int DEFAULT = 0, STICK_BOTTOM = 1, STICK_TOP = 2; + + private int stick_state; + + protected VertexArray vertexArray; + protected Shader shader; + protected Matrix4f transform; + + /** + * Projection and view matrix are set by the engine do not modify + */ + public static Matrix4f projection; + public static Matrix4f view; + + private float zPos; + private float width; // To be used in setTextureWrap + private float height; + private float scalingFactor; + + public boolean useTime; + + private Texture texture; + + /** + * Create a rectangle shape, use setTextureWrap to correctly align the texture with the model + * @param z depth of your model the larger it is the more it will be "close" to the camera + * @param w height of the rectangle + * @param h width of the rectangle + * @param size scaling factor of the rectangle, the model could not show up because this value is too small or too large, a good compromise is between 2 and 15 + * @param tex set to null if you don't want a tex on your model + * @param color set to null if you don't want a Color on your model + */ + public ObjectGl(float z, float w, float h, float size, String tex, Vector3f color){ + float[] colorBuffer = null; + // Check des options + if (color != null){ + colorBuffer = new float[] { + color.x, color.y, color.z, + color.x, color.y, color.z, + color.x, color.y, color.z, + color.x, color.y, color.z + }; + } + + if (tex != null){ + this.texture = new Texture(tex, 0); + } + + this.zPos = z; + this.height = h; + this.width = w; + + this.vertexArray = new VertexArray(Primitive.createRectangle(this.zPos, this.width, this.height), Primitive.rectangle_indices, colorBuffer, Primitive.stdTexWrap); + + this.scalingFactor = size; + this.transform = Matrix4f.identity(); + this.scale(new Vector3f(size, size,1f)); + this.stick_state = DEFAULT; + this.useTime = false; + + // use different shader for each set of option + if (tex == null && color == null){ + this.shader = new Shader("shaders/ObjectGl/vert.glsl","shaders/ObjectGl/frag.glsl"); + } else if (tex == null){ + this.shader = new Shader("shaders/ObjectGlColor/vert.glsl","shaders/ObjectGlColor/frag.glsl"); + } else if (color == null){ + this.shader = new Shader("shaders/ObjectGlTex/vert.glsl","shaders/ObjectGlTex/frag.glsl"); + } else { + this.shader = new Shader("shaders/ObjectGlTexColor/vert.glsl","shaders/ObjectGlTexColor/frag.glsl"); + } + } + + /** + * Reset the transform matrix, the model will appear at the 0.0.0 coordinate, his scaleFactor will be set to zero + * Because the model is at position 0 on the z axis he will not show up on screen + */ + public void resetTransform(){ + this.transform = Matrix4f.identity(); + this.scalingFactor = 1; + } + + /** + * Move the object according to vec, direction can change if rotation method have been used + * @param vec Vector3f + */ + public void translate(Vector3f vec){ + vec.divXY(this.scalingFactor); + this.transform = this.transform.multiply(Matrix4f.translate(vec)); + this.zPos += vec.z; + } + + /** + * Scale the model with the vec vector, the x component is used to mitigate size modification in the behavior of other transformation method + * @param vec Vector3f + */ + public void scale(Vector3f vec){ + this.scalingFactor *= vec.x; + this.transform = this.transform.multiply(Matrix4f.scale(vec)); + } + + /** + * rotate the model by angle degree on the local x axis, beware this will change the behavior of the translate method + * @param angle in degree + */ + public void rotateX(float angle){ + this.transform = this.transform.multiply(Matrix4f.rotateX(angle)); + } + + /** + * rotate the model by angle degree on the local y axis, beware this will change the behavior of the translate method + * @param angle in degree + */ + public void rotateY(float angle){ + this.transform = this.transform.multiply(Matrix4f.rotateY(angle)); + } + + /** + * rotate the model by angle degree on the local z axis, beware this will change the behavior of the translate method + * @param angle in degree + */ + public void rotateZ(float angle){ + this.transform = this.transform.multiply(Matrix4f.rotateZ(angle)); + } + + /** + * Set a new texture to be used on the model. You may need to use setTextureWrap tp get the correct wrap + * @param texPath path to the new texture + */ + public void setTexture(String texPath){ + this.texture = new Texture(texPath, 0); + } + + /** + * Change the wrapping coordinate + * @param x starting wrapping on the horizontal axis + * @param y starting wrapping on the vertical axis + * @param w the length of the wrapping on the horizontal axis + * @param h the length of the wrapping on the vertical axis + */ + public void setTextureWrap(float x, float y, float w, float h, int sticky){ + // TODO set sticky property + precision issue + if (this.stick_state != sticky){ // Check if we're using a new dimension + if (sticky == STICK_BOTTOM){ + this.stick_state = STICK_BOTTOM; + this.translate(new Vector3f(0.0f, (h - this.height)*this.scalingFactor, 0.0f)); + } else if (sticky == STICK_TOP){ + this.stick_state = STICK_TOP; + this.translate(new Vector3f(0.0f, (h - this.height)*this.scalingFactor, 0.0f)); + } else { + this.stick_state = DEFAULT; + } + } + this.height = h; + this.width = w; + this.vertexArray.swapVertexBufferObject(Primitive.createRectangle(this.zPos, w, h)); + int texWidth = this.texture.getWidth(); + int texHeight = this.texture.getHeight(); + x /= texWidth; + w /= texWidth; + y /= texHeight; + h /= texHeight; + float[] result = { + x , y , + x + w , y , + x + w , y + h , + x , y + h , + }; + this.setTextureWrap(result); + } + + /** + * Set a new shader to be used by this object + * @param vert path to glsl Vertex Shader + * @param frag path to glsl Fragment Shader + */ + public void setShader(String vert, String frag){ + this.shader = new Shader(vert, frag); + } + + /** + * Set a new Color for the object if the shader do not use the color attrib this will make no change. + * @param color Vector3f r,g,b format + */ + public void setColor(Vector3f color){ + float[] colorBuffer = new float[] { + color.x, color.y, color.z, + color.x, color.y, color.z, + color.x, color.y, color.z, + color.x, color.y, color.z + }; + this.vertexArray = new VertexArray(Primitive.createRectangle(this.zPos, this.width, this.height), Primitive.rectangle_indices, colorBuffer, Primitive.stdTexWrap); + } + + private void setTextureWrap(float[] texture){ + this.vertexArray.swapTextureBufferObject(texture); + } + + public float getZPos(){ + return zPos; + } + + /** + * Do shader binding, texture binding and vertexArray drawing + */ + public void render(){ + + this.shader.enable(); + if (this.texture != null) this.texture.bind(); + + if (this.useTime) this.shader.setUniform1f("time", (float) glfwGetTime()); + + this.shader.setUniformMat4f("projection", projection); + this.shader.setUniformMat4f("view", view); + this.shader.setUniformMat4f("transform", this.transform); + + this.vertexArray.render(); + + if (this.texture != null) this.texture.unbind(); + this.shader.disable(); + } + +} diff --git a/src/engine/object/SortZ.java b/src/engine/object/SortZ.java new file mode 100644 index 0000000..5b9cd33 --- /dev/null +++ b/src/engine/object/SortZ.java @@ -0,0 +1,11 @@ +package engine.object; + +import java.util.Comparator; + +public class SortZ implements Comparator +{ + public int compare(ObjectGl a, ObjectGl b) + { + return (int) (a.getZPos() - b.getZPos()); + } +} \ No newline at end of file diff --git a/src/engine/utils/BufferUtils.java b/src/engine/utils/BufferUtils.java new file mode 100644 index 0000000..d420b8e --- /dev/null +++ b/src/engine/utils/BufferUtils.java @@ -0,0 +1,32 @@ +package engine.utils; + +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/engine/utils/FileUtils.java b/src/engine/utils/FileUtils.java new file mode 100644 index 0000000..4f82877 --- /dev/null +++ b/src/engine/utils/FileUtils.java @@ -0,0 +1,29 @@ +package engine.utils; + +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/engine/utils/ShaderUtils.java b/src/engine/utils/ShaderUtils.java new file mode 100644 index 0000000..69879ba --- /dev/null +++ b/src/engine/utils/ShaderUtils.java @@ -0,0 +1,60 @@ +package engine.utils; + +import engine.utils.FileUtils; + +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/textures/awesomeface.png b/textures/awesomeface.png new file mode 100644 index 0000000..c7bfec6 Binary files /dev/null and b/textures/awesomeface.png differ diff --git a/textures/container.jpg b/textures/container.jpg new file mode 100644 index 0000000..d07bee4 Binary files /dev/null and b/textures/container.jpg differ diff --git a/textures/zangief_sprite.png b/textures/zangief_sprite.png new file mode 100644 index 0000000..862b9fb Binary files /dev/null and b/textures/zangief_sprite.png differ