From 201eacbd69cc1a15874241b37d6b4c0556700c15 Mon Sep 17 00:00:00 2001 From: Edouard DUPIN Date: Sun, 19 Apr 2020 01:06:31 +0200 Subject: [PATCH] [DEV] tutorial 8 'camera and projection' implementation --- src/engineTester/MainGameLoop.java | 123 ++++++++++++++++++++++--- src/entities/Camera.java | 46 ++++++++++ src/entities/Entity.java | 58 ++++++++++++ src/renderEngine/DisplayManager.java | 67 +++++++++++++- src/renderEngine/Renderer.java | 131 ++++++++++++++++++--------- src/shaders/ShaderProgram.java | 2 +- src/shaders/StaticShader.java | 15 ++- src/shaders/vertexShader.txt | 4 +- src/toolbox/Maths.java | 41 ++++++++- 9 files changed, 419 insertions(+), 68 deletions(-) create mode 100644 src/entities/Camera.java create mode 100644 src/entities/Entity.java diff --git a/src/engineTester/MainGameLoop.java b/src/engineTester/MainGameLoop.java index 770aa38..a1ae760 100644 --- a/src/engineTester/MainGameLoop.java +++ b/src/engineTester/MainGameLoop.java @@ -1,6 +1,10 @@ package engineTester; +import javax.vecmath.Vector3f; + +import entities.Camera; +import entities.Entity; import models.RawModel; import models.TexturedModel; import renderEngine.DisplayManager; @@ -29,38 +33,127 @@ public class MainGameLoop { DisplayManager manager = new DisplayManager(); Loader loader = new Loader(); - Renderer renderer = new Renderer(); manager.init(); StaticShader shader = new StaticShader(); + Renderer renderer = new Renderer(shader); - float[] vertices = { - -0.5f, 0.5f, 0f,//v0 - -0.5f, -0.5f, 0f,//v1 - 0.5f, -0.5f, 0f,//v2 - 0.5f, 0.5f, 0f,//v3 +// float[] vertices = { +// -0.5f, 0.5f, 0f,//v0 +// -0.5f, -0.5f, 0f,//v1 +// 0.5f, -0.5f, 0f,//v2 +// 0.5f, 0.5f, 0f,//v3 +// }; +// float[] textureCoords = { +// 0.0f, 0.0f,//v0 +// 0.0f, 1.0f,//v1 +// 1.0f, 1.0f,//v2 +// 1.0f, 0.0f,//v3 +// }; +// +// int[] indices = { +// 0,1,3,//top left triangle (v0, v1, v3) +// 3,1,2//bottom right triangle (v3, v1, v2) +// }; +// + float[] vertices = { + -0.5f,0.5f,-0.5f, + -0.5f,-0.5f,-0.5f, + 0.5f,-0.5f,-0.5f, + 0.5f,0.5f,-0.5f, + + -0.5f,0.5f,0.5f, + -0.5f,-0.5f,0.5f, + 0.5f,-0.5f,0.5f, + 0.5f,0.5f,0.5f, + + 0.5f,0.5f,-0.5f, + 0.5f,-0.5f,-0.5f, + 0.5f,-0.5f,0.5f, + 0.5f,0.5f,0.5f, + + -0.5f,0.5f,-0.5f, + -0.5f,-0.5f,-0.5f, + -0.5f,-0.5f,0.5f, + -0.5f,0.5f,0.5f, + + -0.5f,0.5f,0.5f, + -0.5f,0.5f,-0.5f, + 0.5f,0.5f,-0.5f, + 0.5f,0.5f,0.5f, + + -0.5f,-0.5f,0.5f, + -0.5f,-0.5f,-0.5f, + 0.5f,-0.5f,-0.5f, + 0.5f,-0.5f,0.5f + }; + float[] textureCoords = { - 0.0f, 0.0f,//v0 - 0.0f, 1.0f,//v1 - 1.0f, 1.0f,//v2 - 1.0f, 0.0f,//v3 + + 0,0, + 0,1, + 1,1, + 1,0, + 0,0, + 0,1, + 1,1, + 1,0, + 0,0, + 0,1, + 1,1, + 1,0, + 0,0, + 0,1, + 1,1, + 1,0, + 0,0, + 0,1, + 1,1, + 1,0, + 0,0, + 0,1, + 1,1, + 1,0 + + }; int[] indices = { - 0,1,3,//top left triangle (v0, v1, v3) - 3,1,2//bottom right triangle (v3, v1, v2) + 0,1,3, + 3,1,2, + 4,5,7, + 7,5,6, + 8,9,11, + 11,9,10, + 12,13,15, + 15,13,14, + 16,17,19, + 19,17,18, + 20,21,23, + 23,21,22 + }; + RawModel model = loader.loadToVAO(vertices, textureCoords, indices); ModelTexture texture = new ModelTexture(loader.loadTexture("tree_sample")); - TexturedModel texturedModel = new TexturedModel(model, texture); - + TexturedModel staticModel = new TexturedModel(model, texture); + + Entity entity = new Entity(staticModel, new Vector3f(0,0,-2), new Vector3f(0,0,0), 1); + + Camera camera = new Camera(); + manager.setDrawer(new DisplayManagerDraw() { @Override public void draw() { + //entity.increasePosition(0.0f, 0, -0.01f); + //entity.increaseRotation(0, 0, 0.01f); + entity.increaseRotation(0.01f, 0.01f, 0.0f); + camera.move(); renderer.prepare(); shader.start(); - renderer.render(texturedModel); + shader.loadViewMatrix(camera); + renderer.render(entity, shader); shader.stop(); } }); diff --git a/src/entities/Camera.java b/src/entities/Camera.java new file mode 100644 index 0000000..7d64c63 --- /dev/null +++ b/src/entities/Camera.java @@ -0,0 +1,46 @@ +package entities; + +import javax.vecmath.Vector3f; + +import renderEngine.DisplayManager; + +public class Camera { + private Vector3f position = new Vector3f(0,0,0); + private float pitch; + private float yaw; + private float roll; + + public Camera() { + + } + public void move() { + if (DisplayManager.isKeyDown('z')) { + position.x -= 0.02f; + } + if (DisplayManager.isKeyDown('s')) { + position.x += 0.02f; + } + if (DisplayManager.isKeyDown('d')) { + position.y -= 0.02f; + } + if (DisplayManager.isKeyDown('q')) { + position.y += 0.02f; + } + } + + public Vector3f getPosition() { + return position; + } + + public float getPitch() { + return pitch; + } + + public float getYaw() { + return yaw; + } + + public float getRoll() { + return roll; + } +} diff --git a/src/entities/Entity.java b/src/entities/Entity.java new file mode 100644 index 0000000..0fb2674 --- /dev/null +++ b/src/entities/Entity.java @@ -0,0 +1,58 @@ +package entities; + +import javax.vecmath.Vector3f; + +import models.TexturedModel; + +public class Entity { + private TexturedModel model; + private Vector3f position; + private Vector3f rotation; + private float scale; + public Entity(TexturedModel model, Vector3f position, Vector3f rotation, float scale) { + this.model = model; + this.position = position; + this.rotation = rotation; + this.scale = scale; + } + + public void increasePosition(float dx, float dy, float dz) { + this.position = new Vector3f(position.x + dx, position.y + dy, position.z + dz); + } + public void increasePosition(Vector3f delta) { + this.position = new Vector3f(position.x + delta.x, position.y + delta.y, position.z + delta.z); + } + public void increaseRotation(float dx, float dy, float dz) { + this.rotation = new Vector3f(rotation.x + dx, rotation.y + dy, rotation.z + dz); + } + public void increaseRotation(Vector3f delta) { + this.rotation = new Vector3f(rotation.x + delta.x, rotation.y + delta.y, rotation.z + delta.z); + } + + public TexturedModel getModel() { + return model; + } + public void setModel(TexturedModel model) { + this.model = model; + } + public Vector3f getPosition() { + return position; + } + public void setPosition(Vector3f position) { + this.position = position; + } + public Vector3f getRotation() { + return rotation; + } + public void setRotation(Vector3f rotation) { + this.rotation = rotation; + } + public float getScale() { + return scale; + } + public void setScale(float scale) { + this.scale = scale; + } + + +} diff --git a/src/renderEngine/DisplayManager.java b/src/renderEngine/DisplayManager.java index 6df9ac9..b0c3205 100644 --- a/src/renderEngine/DisplayManager.java +++ b/src/renderEngine/DisplayManager.java @@ -7,6 +7,8 @@ import org.lwjgl.system.*; import java.nio.*; +import javax.vecmath.Vector2f; + import static org.lwjgl.glfw.Callbacks.*; import static org.lwjgl.glfw.GLFW.*; import static org.lwjgl.opengl.GL11.*; @@ -67,8 +69,13 @@ public class DisplayManager { // } // The window handle - private long window; + private static long window; + public static Vector2f getSize() { + return new Vector2f(WIDTH, HEIGHT); + } + + public void init() { System.out.println("Hello LWJGL " + Version.getVersion() + "!"); @@ -93,6 +100,25 @@ public class DisplayManager { glfwTerminate(); glfwSetErrorCallback(null).free(); } + private static boolean valueS = false; + private static boolean valueZ = false; + private static boolean valueQ = false; + private static boolean valueD = false; + public static boolean isKeyDown(char value) { + if (value == 's') { + return valueS; + } + if (value == 'z') { + return valueZ; + } + if (value == 'q') { + return valueQ; + } + if (value == 'd') { + return valueD; + } + return false; + } private void initWindows() { // Setup an error callback. The default implementation @@ -115,8 +141,45 @@ public class DisplayManager { // Setup a key callback. It will be called every time a key is pressed, repeated or released. glfwSetKeyCallback(window, (window, key, scancode, action, mods) -> { - if ( key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE ) + if ( key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE ) { glfwSetWindowShouldClose(window, true); // We will detect this in the rendering loop + } + if ( key == GLFW_KEY_Z ) { + if (action == GLFW_PRESS ) { + System.out.println("Key Z is pressed"); + valueZ = true; + } else if (action == GLFW_RELEASE ) { + System.out.println("Key Z is release"); + valueZ = false; + } + } + if ( key == GLFW_KEY_S ) { + if (action == GLFW_PRESS ) { + System.out.println("Key S is pressed"); + valueS = true; + } else if (action == GLFW_RELEASE ) { + System.out.println("Key S is release"); + valueS = false; + } + } + if ( key == GLFW_KEY_Q ) { + if (action == GLFW_PRESS ) { + System.out.println("Key Q is pressed"); + valueQ = true; + } else if (action == GLFW_RELEASE ) { + System.out.println("Key Q is release"); + valueQ = false; + } + } + if ( key == GLFW_KEY_D ) { + if (action == GLFW_PRESS ) { + System.out.println("Key D is pressed"); + valueD = true; + } else if (action == GLFW_RELEASE ) { + System.out.println("Key D is release"); + valueD = false; + } + } }); // Get the thread stack and push a new frame diff --git a/src/renderEngine/Renderer.java b/src/renderEngine/Renderer.java index 1b08d0e..8b5bbc7 100644 --- a/src/renderEngine/Renderer.java +++ b/src/renderEngine/Renderer.java @@ -1,12 +1,19 @@ package renderEngine; +import javax.vecmath.Matrix4f; +import javax.vecmath.Vector2f; + import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL13; import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL30; +import org.lwjgl.system.windows.DISPLAY_DEVICE; +import entities.Entity; import models.RawModel; import models.TexturedModel; +import shaders.StaticShader; +import toolbox.Maths; /** * Handles the rendering of a model to the screen. @@ -16,59 +23,97 @@ import models.TexturedModel; */ public class Renderer { - /** - * This method must be called each frame, before any rendering is carried - * out. It basically clears the screen of everything that was rendered last - * frame (using the glClear() method). The glClearColor() method determines - * the colour that it uses to clear the screen. In this example it makes the - * entire screen red at the start of each frame. - */ + private static final float FOV = 70; + private static final float NEAR_PLANE = 0.1f; + private static final float FAR_PLANE = 10000; + + private Matrix4f projectionMatrix; + public Renderer(StaticShader shader) { + createProjectionMatrix(); + shader.start(); + shader.loadProjectionMatrix(projectionMatrix); + shader.stop(); + } + public void prepare() { + GL11.glEnable(GL11.GL_DEPTH_TEST); GL11.glClearColor(1, 0, 0, 1); - GL11.glClear(GL11.GL_COLOR_BUFFER_BIT); + GL11.glClear(GL11.GL_COLOR_BUFFER_BIT|GL11.GL_DEPTH_BUFFER_BIT); } - - /** - * Renders a model to the screen. - * - * Before we can render a VAO it needs to be made active, and we can do this - * by binding it. We also need to enable the relevant attributes of the VAO, - * which in this case is just attribute 0 where we stored the position data. - * - * The VAO can then be rendered to the screen using glDrawElements(). Using - * this draw method tells OpenGL that we want to use the index buffer to - * determine how the vertices should be connected instead of just connecting - * the vertices together in the order that they are stored in the VAO. - * - * We tell it what type of shapes to render and the number of vertices that - * it needs to render. We also tell it was format the index data is in (we - * used ints) and finally we indicate where in the index buffer it should - * start rendering. We want it to start right at the beginning and render - * everything, so we put 0. - * - * After rendering we unbind the VAO and disable the attribute. - * - * @param model - * - The model to be rendered. - */ - public void render(RawModel model) { - GL30.glBindVertexArray(model.getVaoID()); - GL20.glEnableVertexAttribArray(0); - GL11.glDrawElements(GL11.GL_TRIANGLES, model.getVertexCount(), GL11.GL_UNSIGNED_INT, 0); - GL20.glDisableVertexAttribArray(0); - GL30.glBindVertexArray(0); - } - public void render(TexturedModel texturedModel) { - RawModel model = texturedModel.getRawModel(); - GL30.glBindVertexArray(model.getVaoID()); + + public void render(Entity entity, StaticShader shader) { + TexturedModel texturedModel = entity.getModel(); + RawModel rawModel = texturedModel.getRawModel(); + GL30.glBindVertexArray(rawModel.getVaoID()); GL20.glEnableVertexAttribArray(0); GL20.glEnableVertexAttribArray(1); + Matrix4f transformationMatrix = Maths.createTransformationMatrix(entity.getPosition(), entity.getRotation(), entity.getScale()); + shader.loadTransformationMatrix(transformationMatrix); GL13.glActiveTexture(GL13.GL_TEXTURE0); GL11.glBindTexture(GL11.GL_TEXTURE_2D, texturedModel.getTexture().getTexturedID()); - GL11.glDrawElements(GL11.GL_TRIANGLES, model.getVertexCount(), GL11.GL_UNSIGNED_INT, 0); + GL11.glDrawElements(GL11.GL_TRIANGLES, rawModel.getVertexCount(), GL11.GL_UNSIGNED_INT, 0); GL20.glDisableVertexAttribArray(0); GL20.glDisableVertexAttribArray(1); GL30.glBindVertexArray(0); } + private void createProjectionMatrix() { + //float aspectRatio = (float) Display.getWidth() / (float) Display.getHeight(); + Vector2f windowsSize = DisplayManager.getSize(); + float aspectRatio = windowsSize.x / windowsSize.y; +// float y_scale = (float) ((1f/Math.atan(Math.toRadians(FOV/2f))) * aspectRatio); +// float x_scale = y_scale / aspectRatio; +// float frustrum_length = FAR_PLANE - NEAR_PLANE; +// +// projectionMatrix = new Matrix4f(); +// projectionMatrix.m00 = x_scale; +// projectionMatrix.m11 = y_scale; +// projectionMatrix.m22 = -((FAR_PLANE + NEAR_PLANE) / frustrum_length); +// projectionMatrix.m23 = -1;; +// projectionMatrix.m32 = -((2*FAR_PLANE * NEAR_PLANE) / frustrum_length); +// projectionMatrix.m33 = 0; + projectionMatrix = matPerspective(FOV, aspectRatio, NEAR_PLANE, FAR_PLANE); + } + + + Matrix4f matFrustum(float xmin, float xmax, float ymin, float ymax, float zNear, float zFar) { + Matrix4f tmp = new Matrix4f(); + // 0 1 2 3 + // 4 5 6 7 + // 8 9 10 11 + // 12 13 14 15 + tmp.m00 = (2.0f * zNear) / (xmax - xmin); + tmp.m11 = (2.0f * zNear) / (ymax - ymin); + tmp.m22 = -(zFar + zNear) / (zFar - zNear); + tmp.m02 = (xmax + xmin) / (xmax - xmin); + tmp.m12 = (ymax + ymin) / (ymax - ymin); + tmp.m32 = -1.0f; + tmp.m23 = -(2.0f * zFar * zNear) / (zFar - zNear); + return tmp; + } + + Matrix4f matPerspective(float fovx, float aspect, float zNear, float zFar) { + //TK_DEBUG("drax perspective: fovx=" << fovx << "->" << aspect << " " << zNear << "->" << zFar); + float xmax = zNear * (float)Math.atan(fovx/2.0); + float xmin = -xmax; + + float ymin = xmin / aspect; + float ymax = xmax / aspect; + //TK_DEBUG("drax perspective: " << xmin << "->" << xmax << " & " << ymin << "->" << ymax << " " << zNear << "->" << zFar); + return matFrustum(xmin, xmax, ymin, ymax, zNear, zFar); + } + + Matrix4f matOrtho(float left, float right, float bottom, float top, float nearVal, float farVal) { + Matrix4f tmp = new Matrix4f(); + tmp.m00 = 2.0f / (right - left); + tmp.m11 = 2.0f / (top - bottom); + tmp.m22 = -2.0f / (farVal - nearVal); + tmp.m03 = -1*(right + left) / (right - left); + tmp.m13 = -1*(top + bottom) / (top - bottom); + tmp.m23 = -1*(farVal + nearVal) / (farVal - nearVal); + tmp.m33 = 1; + return tmp; + } + + } diff --git a/src/shaders/ShaderProgram.java b/src/shaders/ShaderProgram.java index 4f8b783..8cdc6e5 100644 --- a/src/shaders/ShaderProgram.java +++ b/src/shaders/ShaderProgram.java @@ -75,7 +75,7 @@ public abstract class ShaderProgram { value.m20, value.m21, value.m22, value.m23, value.m30, value.m31, value.m32, value.m33 }; - GL20.glUniformMatrix4fv(location, false, values); + GL20.glUniformMatrix4fv(location, true, values); } private static int loadShader(String file, int type) { diff --git a/src/shaders/StaticShader.java b/src/shaders/StaticShader.java index 30d7c3b..8346fa9 100644 --- a/src/shaders/StaticShader.java +++ b/src/shaders/StaticShader.java @@ -2,11 +2,16 @@ package shaders; import javax.vecmath.Matrix4f; +import entities.Camera; +import toolbox.Maths; + public class StaticShader extends ShaderProgram { private static final String VERTEX_FILE = "src/shaders/vertexShader.txt"; private static final String FRAGMENT_FILE = "src/shaders/fragmentShader.txt"; private int location_transformationMatrix; + private int location_projectionMatrix; + private int location_viewMatrix; public StaticShader() { super(VERTEX_FILE, FRAGMENT_FILE); @@ -21,9 +26,17 @@ public class StaticShader extends ShaderProgram { @Override protected void getAllUniformLocations() { location_transformationMatrix = super.getUniformLocation("transformationMatrix"); + location_projectionMatrix = super.getUniformLocation("projectionMatrix"); + location_viewMatrix = super.getUniformLocation("viewMatrix"); } - + public void loadTransformationMatrix(Matrix4f value) { super.loadMatrix(location_transformationMatrix, value); } + public void loadProjectionMatrix(Matrix4f value) { + super.loadMatrix(location_projectionMatrix, value); + } + public void loadViewMatrix(Camera value) { + super.loadMatrix(location_viewMatrix, Maths.createViewMatrix(value)); + } } diff --git a/src/shaders/vertexShader.txt b/src/shaders/vertexShader.txt index 43600fc..5ca1546 100644 --- a/src/shaders/vertexShader.txt +++ b/src/shaders/vertexShader.txt @@ -6,10 +6,12 @@ in vec2 textureCoords; out vec2 pass_textureCoords; uniform mat4 transformationMatrix; +uniform mat4 projectionMatrix; +uniform mat4 viewMatrix; void main(void) { - gl_Position = transformationMatrix * vec4(position, 1.0); + gl_Position = projectionMatrix * viewMatrix * transformationMatrix * vec4(position, 1.0); pass_textureCoords = textureCoords; } diff --git a/src/toolbox/Maths.java b/src/toolbox/Maths.java index 351f6a1..19c2361 100644 --- a/src/toolbox/Maths.java +++ b/src/toolbox/Maths.java @@ -1,18 +1,49 @@ package toolbox; +import javax.vecmath.Matrix3f; import javax.vecmath.Matrix4f; import javax.vecmath.Vector3f; +import entities.Camera; + public class Maths { public static Matrix4f createTransformationMatrix(Vector3f translation, Vector3f rotation, float scale) { + // Need to rework all of this this is really not optimum ... Matrix4f matrix = new Matrix4f(); matrix.setIdentity(); - matrix.setTranslation(translation); - matrix.rotX(rotation.x); - matrix.rotY(rotation.y); - matrix.rotZ(rotation.z); - matrix.setScale(scale); + Matrix3f rotationMatX = new Matrix3f(); + rotationMatX.rotX(rotation.x); + Matrix3f rotationMatY = new Matrix3f(); + rotationMatY.rotY(rotation.y); + Matrix3f rotationMatZ = new Matrix3f(); + rotationMatZ.rotZ(rotation.z); + rotationMatX.mul(rotationMatY); + rotationMatX.mul(rotationMatZ); + matrix.set(rotationMatX, translation, scale); +// matrix.rotX(rotation.x); +// matrix.rotY(rotation.y); +// matrix.rotZ(rotation.z); +// System.out.println("elementPosition :" + translation + " " + matrix) ; +// matrix.setTranslation(translation); +// matrix.setScale(scale); + //System.out.println("elementPosition :" + translation + " " + matrix) ; + return matrix; + } + public static Matrix4f createViewMatrix(Camera camera) { + // Need to rework all of this this is really not optimum ... + Matrix4f matrix = new Matrix4f(); + matrix.setIdentity(); + Matrix3f rotationMatX = new Matrix3f(); + rotationMatX.rotX(camera.getPitch()); + Matrix3f rotationMatY = new Matrix3f(); + rotationMatY.rotY(camera.getYaw()); + Matrix3f rotationMatZ = new Matrix3f(); + rotationMatZ.rotZ(camera.getRoll()); + rotationMatX.mul(rotationMatY); + rotationMatX.mul(rotationMatZ); + Vector3f camarePos = camera.getPosition(); + matrix.set(rotationMatX, new Vector3f(-camarePos.x,-camarePos.y,-camarePos.z), 1); return matrix; } }