From 407219c4d6938fe2e9da1762ffce27b3bb1fa6fc Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Fri, 19 Jun 2020 18:10:15 -0400 Subject: [PATCH 01/52] Add more interfaces --- src/engine/Initializable.java | 11 +++++++++++ src/engine/Inputtable.java | 10 ++++++++++ src/engine/Renderable.java | 10 ++++++++++ src/engine/Updateable.java | 10 ++++++++++ 4 files changed, 41 insertions(+) create mode 100644 src/engine/Initializable.java create mode 100644 src/engine/Inputtable.java create mode 100644 src/engine/Renderable.java create mode 100644 src/engine/Updateable.java diff --git a/src/engine/Initializable.java b/src/engine/Initializable.java new file mode 100644 index 0000000..4341c34 --- /dev/null +++ b/src/engine/Initializable.java @@ -0,0 +1,11 @@ +/* +George Zhang +Initializable interface. +*/ + +package geetransit.minecraft05.engine; + +public interface Initializable { + void init(Window window) throws Exception; + void cleanup(); +} diff --git a/src/engine/Inputtable.java b/src/engine/Inputtable.java new file mode 100644 index 0000000..8470835 --- /dev/null +++ b/src/engine/Inputtable.java @@ -0,0 +1,10 @@ +/* +George Zhang +Inputtable interface +*/ + +package geetransit.minecraft05.engine; + +public interface Inputtable { + void input(Window window); +} diff --git a/src/engine/Renderable.java b/src/engine/Renderable.java new file mode 100644 index 0000000..12f530a --- /dev/null +++ b/src/engine/Renderable.java @@ -0,0 +1,10 @@ +/* +George Zhang +Renderable interface. +*/ + +package geetransit.minecraft05.engine; + +public interface Renderable { + void render(Window window); +} diff --git a/src/engine/Updateable.java b/src/engine/Updateable.java new file mode 100644 index 0000000..c696c8c --- /dev/null +++ b/src/engine/Updateable.java @@ -0,0 +1,10 @@ +/* +George Zhang +Updateable interface +*/ + +package geetransit.minecraft05.engine; + +public interface Updateable { + void update(float interval); +} From 74a545775c8957dece16f794abb405e39ddc8cec Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Sat, 20 Jun 2020 00:52:44 -0400 Subject: [PATCH 02/52] Add base classes for the interfaces --- src/engine/Init.java | 33 +++++++++++++++++++++++++++++++++ src/engine/Input.java | 27 +++++++++++++++++++++++++++ src/engine/Render.java | 27 +++++++++++++++++++++++++++ src/engine/Update.java | 27 +++++++++++++++++++++++++++ 4 files changed, 114 insertions(+) create mode 100644 src/engine/Init.java create mode 100644 src/engine/Input.java create mode 100644 src/engine/Render.java create mode 100644 src/engine/Update.java diff --git a/src/engine/Init.java b/src/engine/Init.java new file mode 100644 index 0000000..001d38b --- /dev/null +++ b/src/engine/Init.java @@ -0,0 +1,33 @@ +/* +George Zhang +Init class +*/ + +package geetransit.minecraft05.engine; + +import java.util.List; +import java.util.ArrayList; + +public class Init implements Initializable { + private List inits; + + public Init(List inits) { + this.inits = inits; + } + public Init() { this(new ArrayList<>()); } + + public List getInits() { return this.inits; } + public Init addInit(Initializable init) { this.inits.add(init); return this; } + + @Override + public void init(Window window) throws Exception { + for (Initializable init : this.getInits()) + init.init(window); + } + + @Override + public void cleanup() { + for (Initializable init : this.getInits()) + init.cleanup(); + } +} diff --git a/src/engine/Input.java b/src/engine/Input.java new file mode 100644 index 0000000..2d41e73 --- /dev/null +++ b/src/engine/Input.java @@ -0,0 +1,27 @@ +/* +George Zhang +Input class +*/ + +package geetransit.minecraft05.engine; + +import java.util.List; +import java.util.ArrayList; + +public class Input implements Inputtable { + private List inputs; + + public Input(List inputs) { + this.inputs = inputs; + } + public Input() { this(new ArrayList<>()); } + + public List getInputs() { return this.inputs; } + public Input addInput(Inputtable input) { this.inputs.add(input); return this; } + + @Override + public void input(Window window) { + for (Inputtable input : this.getInputs()) + input.input(window); + } +} diff --git a/src/engine/Render.java b/src/engine/Render.java new file mode 100644 index 0000000..97e6a54 --- /dev/null +++ b/src/engine/Render.java @@ -0,0 +1,27 @@ +/* +George Zhang +Render class +*/ + +package geetransit.minecraft05.engine; + +import java.util.List; +import java.util.ArrayList; + +public class Render implements Renderable { + private List renders; + + public Render(List renders) { + this.renders = renders; + } + public Render() { this(new ArrayList<>()); } + + public List getRenders() { return this.renders; } + public Render addRender(Renderable render) { this.renders.add(render); return this; } + + @Override + public void render(Window window) { + for (Renderable render : this.getRenders()) + render.render(window); + } +} diff --git a/src/engine/Update.java b/src/engine/Update.java new file mode 100644 index 0000000..dc224e4 --- /dev/null +++ b/src/engine/Update.java @@ -0,0 +1,27 @@ +/* +George Zhang +Update class +*/ + +package geetransit.minecraft05.engine; + +import java.util.List; +import java.util.ArrayList; + +public class Update implements Updateable { + private List updates; + + public Update(List updates) { + this.updates = updates; + } + public Update() { this(new ArrayList<>()); } + + public List getUpdates() { return this.updates; } + public Update addUpdate(Updateable update) { this.updates.add(update); return this; } + + @Override + public void update(float interval) { + for (Updateable update : this.getUpdates()) + update.update(interval); + } +} From e28a7896cb546997229978778e97fd811417ddb9 Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Mon, 22 Jun 2020 05:11:54 -0400 Subject: [PATCH 03/52] Rename Scene -> Loopable Make Initializable.cleanup optional --- src/engine/Engine.java | 10 +++++----- src/engine/Initializable.java | 2 +- src/engine/{Scene.java => Loopable.java} | 4 ++-- src/engine/SceneBase.java | 18 +++++++++--------- src/engine/Window.java | 20 ++++++++++---------- src/game/Background.java | 2 +- 6 files changed, 28 insertions(+), 28 deletions(-) rename src/engine/{Scene.java => Loopable.java} (61%) diff --git a/src/engine/Engine.java b/src/engine/Engine.java index f26e753..121e13d 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -7,7 +7,7 @@ public class Engine implements Runnable { private Window window; - private Scene scene; + private Loopable loop; private boolean updateVSync = false; private boolean updateSize = false; @@ -15,19 +15,19 @@ public class Engine implements Runnable { public Engine( Window window, - Scene scene + Loopable loop ) { this.window = window; - this.scene = scene; + this.loop = loop; } @Override public void run() { this.window.createWindow(); - new Thread(() -> this.window.renderThread(this.scene)).start(); + new Thread(() -> this.window.renderThread(this.loop)).start(); this.window.eventThread(); } public Window getWindow() { return this.window; } - public Scene getScene() { return this.scene; } + public Loopable getLoop() { return this.loop; } } diff --git a/src/engine/Initializable.java b/src/engine/Initializable.java index 4341c34..fb4ee26 100644 --- a/src/engine/Initializable.java +++ b/src/engine/Initializable.java @@ -7,5 +7,5 @@ public interface Initializable { void init(Window window) throws Exception; - void cleanup(); + default void cleanup() {} } diff --git a/src/engine/Scene.java b/src/engine/Loopable.java similarity index 61% rename from src/engine/Scene.java rename to src/engine/Loopable.java index 467d9f3..ecb03cb 100644 --- a/src/engine/Scene.java +++ b/src/engine/Loopable.java @@ -1,11 +1,11 @@ /* George Zhang -Scene interface. (used by Window) +Loopable interface class. (used by Window) */ package geetransit.minecraft05.engine; -public interface Scene { +public interface Loopable extends Initializable, Inputtable, Updateable, Renderable { void init(Window window) throws Exception; void input(Window window); void update(float interval); diff --git a/src/engine/SceneBase.java b/src/engine/SceneBase.java index d9340a4..a63384e 100644 --- a/src/engine/SceneBase.java +++ b/src/engine/SceneBase.java @@ -7,38 +7,38 @@ import java.util.*; -public abstract class SceneBase implements Scene { - private List scenes; +public abstract class SceneBase implements Loopable { + private List scenes; public SceneBase() { this.scenes = new ArrayList<>(); } - public List getScenes() { return this.scenes; } - public SceneBase addScene(Scene scene) { this.scenes.add(scene); return this; } + public List getScenes() { return this.scenes; } + public SceneBase addScene(Loopable scene) { this.scenes.add(scene); return this; } public void init(Window window) throws Exception { - for (Scene scene : this.getScenes()) + for (Loopable scene : this.getScenes()) scene.init(window); } public void input(Window window) { - for (Scene scene : this.getScenes()) + for (Loopable scene : this.getScenes()) scene.input(window); } public void update(float interval) { - for (Scene scene : this.getScenes()) + for (Loopable scene : this.getScenes()) scene.update(interval); } public void render(Window window) { - for (Scene scene : this.getScenes()) + for (Loopable scene : this.getScenes()) scene.render(window); } public void cleanup() { - for (Scene scene : this.getScenes()) + for (Loopable scene : this.getScenes()) scene.cleanup(); } } diff --git a/src/engine/Window.java b/src/engine/Window.java index aeed08b..a7e93fd 100644 --- a/src/engine/Window.java +++ b/src/engine/Window.java @@ -140,19 +140,19 @@ public void eventTerminate() { glfwSetErrorCallback(null).free(); } - public void renderThread(Scene scene) { + public void renderThread(Loopable loop) { try { - this.renderInit(scene); - this.renderLoop(scene); + this.renderInit(loop); + this.renderLoop(loop); } catch (Exception e) { e.printStackTrace(); System.exit(1); } finally { - scene.cleanup(); + loop.cleanup(); } } - private void renderInit(Scene scene) throws Exception { + private void renderInit(Loopable loop) throws Exception { // This adds the OpenGL context into this function. glfwMakeContextCurrent(this.handle); @@ -167,14 +167,14 @@ private void renderInit(Scene scene) throws Exception { glfwSwapInterval(this.isVSync() ? 1 : 0); // init - scene.init(this); + loop.init(this); // Start timer. this.timer.init(); } // Render loop. - private void renderLoop(Scene scene) { + private void renderLoop(Loopable loop) { while (!this.isDestroyed()) { this.elapsedTime = this.timer.getElapsedTime(); this.accumulatedTime += this.elapsedTime; @@ -189,17 +189,17 @@ private void renderLoop(Scene scene) { this.next.run("targetUps"); // input - scene.input(this); + loop.input(this); // update float interval = 1f / this.getTargetUps(); while (this.accumulatedTime >= interval) { - scene.update(interval); + loop.update(interval); this.accumulatedTime -= interval; } // render - scene.render(this); + loop.render(this); this.renderUpdate(); if (!this.isVSync()) this.renderSync(); diff --git a/src/game/Background.java b/src/game/Background.java index 210b45a..34725b8 100644 --- a/src/game/Background.java +++ b/src/game/Background.java @@ -9,7 +9,7 @@ import static org.lwjgl.glfw.GLFW.*; -public class Background implements Scene { +public class Background implements Loopable { private int direction; private float color; From 49278b5cf1866aaf5fe1a68cf9e8658208b221a6 Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Mon, 22 Jun 2020 05:23:09 -0400 Subject: [PATCH 04/52] Big refactor MergeSceneBase + SceneRender -> Scene Mouse and Camera now implement Inputtable Made some constants public final Remove Item.render and .cleanup (use .getMesh()) Renderer now implements Renderable Move item maps from SceneRender -> Renderer Move render distance changing from Background -> World Check for Alt+F4 instead of anything other than Shift+F4 --- src/engine/Camera.java | 47 ++++++++----- src/engine/HeightMap.java | 3 +- src/engine/Item.java | 10 --- src/engine/Loopable.java | 5 -- src/engine/Mouse.java | 16 ++--- src/engine/Renderer.java | 127 +++++++++++++++++++----------------- src/engine/Scene.java | 94 ++++++++++++++++++++++++++ src/engine/SceneBase.java | 44 ------------- src/engine/SceneRender.java | 70 -------------------- src/engine/Window.java | 4 +- src/game/Background.java | 17 +---- src/game/Game.java | 44 +++++-------- src/game/Hud.java | 22 ++++--- src/game/Skybox.java | 15 ++--- src/game/World.java | 100 ++++++++++++++-------------- 15 files changed, 294 insertions(+), 324 deletions(-) create mode 100644 src/engine/Scene.java delete mode 100644 src/engine/SceneBase.java delete mode 100644 src/engine/SceneRender.java diff --git a/src/engine/Camera.java b/src/engine/Camera.java index 130fa03..ca90008 100644 --- a/src/engine/Camera.java +++ b/src/engine/Camera.java @@ -5,13 +5,17 @@ package geetransit.minecraft05.engine; +import org.joml.Vector2f; import org.joml.Vector3f; import org.joml.Matrix4f; -public class Camera { +public class Camera implements Inputtable { public static final float FOV = 80f; public static final float NEAR = 0.01f; public static final float FAR = 50f; + public static final float SENSITIVITY = 0.75f; + + private Mouse mouse; private final Vector3f position; private final Vector3f rotation; // in degrees @@ -20,31 +24,42 @@ public class Camera { private float fov; private float near; private float far; + private float sensitivity; - private Vector3f radiansRotation; - private Vector3f negativePosition; + private final Vector3f radiansRotation; + private final Vector3f negativePosition; - public Camera(Vector3f position, Vector3f rotation, float fov, float near, float far) { - this.position = position; - this.rotation = rotation; + public Camera(Mouse mouse) { + this.mouse = mouse; + + this.position = new Vector3f(); + this.rotation = new Vector3f(); this.viewMatrix = new Matrix4f(); - this.fov = (float) Math.toRadians(fov); - this.near = near; - this.far = far; + this.setFov(FOV); + this.setNear(NEAR); + this.setFar(FAR); + this.setSensitivity(SENSITIVITY); this.radiansRotation = new Vector3f(); this.negativePosition = new Vector3f(); } - public Camera(Vector3f position, Vector3f rotation, float fov) { this(position, rotation, fov, NEAR, FAR); } - public Camera(Vector3f position, Vector3f rotation) { this(position, rotation, FOV); } - public Camera() { this(new Vector3f(), new Vector3f()); } + @Override + public void input(Window window) { + if (this.mouse.isInside()) + this.rotateMovement(this.mouse.getMovement(), this.sensitivity); + } + + public Mouse getMouse() { return this.mouse; } public Vector3f getPosition() { return this.position; } public Vector3f getRotation() { return this.rotation; } public float getFov() { return (float) Math.toDegrees(this.fov); } public float getNear() { return this.near; } public float getFar() { return this.far; } + public float getSensitivity() { return this.sensitivity; } + + public Camera setMouse(Mouse mouse) { this.mouse = mouse; return this; } public Camera setPosition(Vector3f position) { this.position.set(position); return this; } public Camera setPosition(float x, float y, float z) { this.position.set(x, y, z); return this; } public Camera setRotation(Vector3f rotation) { this.rotation.set(rotation); return this; } @@ -52,9 +67,11 @@ public Camera(Vector3f position, Vector3f rotation, float fov, float near, float public Camera setFov(float fov) { this.fov = (float) Math.toRadians(fov); return this; } public Camera setNear(float near) { this.near = near; return this; } public Camera setFar(float far) { this.far = far; return this; } + public Camera setSensitivity(float sensitivity) { this.sensitivity = sensitivity; return this; } public Camera movePosition(Vector3f position) { return this.movePosition(position.x, position.y, position.z); } public Camera movePosition(float x, float y, float z) { + // TODO optimize this (using functions inside Vector3f) if (z != 0) { this.position.x += (float) Math.sin(Math.toRadians(this.rotation.y)) * -1.0f * z; this.position.z += (float) Math.cos(Math.toRadians(this.rotation.y)) * z; @@ -73,9 +90,9 @@ public Camera moveRotation(float x, float y, float z) { return this; } - public Camera rotateUsingMouse(Mouse mouse, float sensitivity) { - float x = mouse.getMovement().x; - float y = mouse.getMovement().y; + public Camera rotateMovement(Vector2f movement, float sensitivity) { + float x = movement.x; + float y = movement.y; if (mouse.isLeft()) // dragging this.moveRotation(-y*sensitivity, -x*sensitivity, 0); if (mouse.isRight()) // panning diff --git a/src/engine/HeightMap.java b/src/engine/HeightMap.java index 95a37df..9eb8f89 100644 --- a/src/engine/HeightMap.java +++ b/src/engine/HeightMap.java @@ -8,7 +8,8 @@ Encapsulate an image (used to get pixel values). import java.nio.ByteBuffer; public class HeightMap implements AutoCloseable { - public static int CHANNELS = 4; + public static final int CHANNELS = 4; + public static final int MAX_COLOR = 255*255*255; public final ByteBuffer buffer; public final int width; diff --git a/src/engine/Item.java b/src/engine/Item.java index 1a56449..dd7e8ae 100644 --- a/src/engine/Item.java +++ b/src/engine/Item.java @@ -27,16 +27,6 @@ protected Item() { this.selected = false; } - public void render(Window window) { - this.mesh.prepare(); - this.mesh.render(); - this.mesh.restore(); - } - - public void cleanup() { - this.mesh.cleanup(); - } - // does NOT copy the mesh (shallow copy) @Override public Item clone() { diff --git a/src/engine/Loopable.java b/src/engine/Loopable.java index ecb03cb..3c7da87 100644 --- a/src/engine/Loopable.java +++ b/src/engine/Loopable.java @@ -6,9 +6,4 @@ package geetransit.minecraft05.engine; public interface Loopable extends Initializable, Inputtable, Updateable, Renderable { - void init(Window window) throws Exception; - void input(Window window); - void update(float interval); - void render(Window window); - void cleanup(); } diff --git a/src/engine/Mouse.java b/src/engine/Mouse.java index f15ef9f..9a44cfd 100644 --- a/src/engine/Mouse.java +++ b/src/engine/Mouse.java @@ -8,10 +8,10 @@ import org.joml.Vector2f; import static org.lwjgl.glfw.GLFW.*; -public class Mouse { +public class Mouse implements Initializable, Inputtable { private final Vector2f current; private final Vector2f movement; - private Vector2f previous; + private final Vector2f previous; private boolean inside = false; private boolean left = false; @@ -22,6 +22,12 @@ public Mouse() { this.movement = new Vector2f(); this.previous = new Vector2f(0, 0); } + + public Vector2f getMovement() { return this.movement; } + public Vector2f getCurrent() { return this.current; } + public boolean isInside() { return this.inside; } + public boolean isLeft() { return this.left; } + public boolean isRight() { return this.right; } public void init(Window window) { glfwSetCursorPosCallback(window.getHandle(), (handle, x, y) -> { @@ -36,12 +42,6 @@ public void init(Window window) { if (button == GLFW_MOUSE_BUTTON_2) this.right = (action == GLFW_PRESS); }); } - - public Vector2f getMovement() { return this.movement; } - public Vector2f getCurrent() { return this.current; } - public boolean isInside() { return this.inside; } - public boolean isLeft() { return this.left; } - public boolean isRight() { return this.right; } public void input(Window window) { this.current.sub(this.previous, this.movement); diff --git a/src/engine/Renderer.java b/src/engine/Renderer.java index 8b396b5..9293372 100644 --- a/src/engine/Renderer.java +++ b/src/engine/Renderer.java @@ -1,6 +1,6 @@ /* ahbejarano -Renderer class. +Renderer abstract helper class. */ package geetransit.minecraft05.engine; @@ -9,22 +9,45 @@ import org.joml.Matrix4f; import static org.lwjgl.opengl.GL30.*; -public abstract class Renderer { - protected Transformation transformation; +public abstract class Renderer implements Initializable, Renderable { + public final Map> map; + public final List items; + public final Transformation transformation; public Renderer() { + this.map = new HashMap<>(); + this.items = new ArrayList<>(); this.transformation = new Transformation(); } - // create shaders + // create shaders: shader = create?(VERTEX_SHADER, FRAGMENT_SHADER); public abstract void init(Window window) throws Exception; - // render scene + // render scene: render?(shader, window, ?); public abstract void render(Window window); - // destroy shaders + // destroy shaders: shader.cleanup(); public abstract void cleanup(); + public Renderer addItem(Item item) { + Mesh mesh = item.getMesh(); + this.items.add(item); + if (!this.map.containsKey(mesh)) + this.map.put(mesh, new ArrayList<>()); + this.map.get(mesh).add(item); + return this; + } + + public Renderer removeItem(Item item) { + Mesh mesh = item.getMesh(); + this.items.remove(item); + this.map.get(mesh).remove(item); + if (this.map.get(mesh).size() == 0) + this.map.remove(mesh); + return this; + } + + // shader creators public Shader createShader(String vertex, String fragment) throws Exception { Shader shader = new Shader(); shader.createVertexShader(Utils.loadResource(vertex)); @@ -53,42 +76,8 @@ public Shader create2D(String vertex, String fragment) throws Exception { return shader; } - public void render3D(Shader shader, Window window, Camera camera, List items) { - shader.bind(); - - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - - // projection - Matrix4f projectionMatrix = this.transformation.getProjectionMatrix(window, camera); - shader.setUniform("projectionMatrix", projectionMatrix); - - // view - Matrix4f viewMatrix = camera.getViewMatrix(); - - // Draw meshes - shader.setUniform("texture_sampler", 0); - int itemsSize = items.size(); - for (int i = 0; i < itemsSize; i++) { - Item item = items.get(i); - Mesh mesh = item.getMesh(); - Matrix4f modelViewMatrix = this.transformation.getModelViewMatrix(item, viewMatrix); - shader.setUniform("modelViewMatrix", modelViewMatrix); - shader.setUniform("color", mesh.getColor()); - shader.setUniform("isTextured", mesh.isTextured()); - shader.setUniform("isSelected", item.isSelected()); - mesh.prepare(this.getMeshFromItems(items, i-1, itemsSize)); - mesh.render(); - mesh.restore(this.getMeshFromItems(items, i+1, itemsSize)); - } - - glDisable(GL_CULL_FACE); - - shader.unbind(); - } - // note does NOT call Item.render(Window) - public void render3DMap(Shader shader, Window window, Camera camera, Map> map) { + public void render3D(Shader shader, Window window, Camera camera) { shader.bind(); glEnable(GL_CULL_FACE); glCullFace(GL_BACK); @@ -102,7 +91,7 @@ public void render3DMap(Shader shader, Window window, Camera camera, Map> entry : map.entrySet()) { + for (Map.Entry> entry : this.map.entrySet()) { Mesh mesh = entry.getKey(); List items = entry.getValue(); shader.setUniform("color", mesh.getColor()); @@ -124,7 +113,8 @@ public void render3DMap(Shader shader, Window window, Camera camera, Map items) { + // uses the List of items + public void render2DList(Shader shader, Window window) { shader.bind(); // source # https://stackoverflow.com/a/5467636 @@ -135,12 +125,17 @@ public void render2D(Shader shader, Window window, List items) { // Draw meshes shader.setUniform("texture_sampler", 0); - for (Item item : items) { + for (Item item : this.items) { + Mesh mesh = item.getMesh(); + shader.setUniform("color", mesh.getColor()); + shader.setUniform("isTextured", mesh.isTextured()); + mesh.prepare(); + Matrix4f projModelMatrix = this.transformation.getOrthoProjModelMatrix(item, orthoMatrix); shader.setUniform("projModelMatrix", projModelMatrix); - shader.setUniform("color", item.getMesh().getColor()); - shader.setUniform("isTextured", item.getMesh().isTextured()); - item.render(window); + mesh.render(); + + mesh.restore(); } glDepthMask(true); @@ -149,7 +144,8 @@ public void render2D(Shader shader, Window window, List items) { shader.unbind(); } - public void renderSkybox(Shader shader, Window window, Camera camera, List items) { + // note does NOT call Item.render(Window) + public void render3DSkybox(Shader shader, Window window, Camera camera) { shader.bind(); // projection @@ -164,21 +160,34 @@ public void renderSkybox(Shader shader, Window window, Camera camera, List // Draw meshes shader.setUniform("texture_sampler", 0); - for (Item item : items) { - Matrix4f modelViewMatrix = this.transformation.getModelViewMatrix(item, viewMatrix); - shader.setUniform("modelViewMatrix", modelViewMatrix); - shader.setUniform("color", item.getMesh().getColor()); - shader.setUniform("isTextured", item.getMesh().isTextured()); - item.render(window); + for (Map.Entry> entry : this.map.entrySet()) { + Mesh mesh = entry.getKey(); + List items = entry.getValue(); + shader.setUniform("color", mesh.getColor()); + shader.setUniform("isTextured", mesh.isTextured()); + mesh.prepare(); + + // single : loop through items + for (Item item : items) { + Matrix4f modelViewMatrix = this.transformation.getModelViewMatrix(item, viewMatrix); + shader.setUniform("modelViewMatrix", modelViewMatrix); + shader.setUniform("isSelected", item.isSelected()); + mesh.render(); + } + + mesh.restore(); } shader.unbind(); } - public Mesh getMeshFromItems(List items, int index) { return this.getMeshFromItems(items, index, items.size()); } - public Mesh getMeshFromItems(List items, int index, int size) { - if (0 <= index && index < size) - return items.get(index).getMesh(); - return null; + public void destroy(Shader shader) { + this.destroyShader(shader); + for (Mesh mesh : this.map.keySet()) + mesh.cleanup(); + } + + public void destroyShader(Shader shader) { + shader.cleanup(); } } diff --git a/src/engine/Scene.java b/src/engine/Scene.java new file mode 100644 index 0000000..a84b529 --- /dev/null +++ b/src/engine/Scene.java @@ -0,0 +1,94 @@ +/* +George Zhang +Scene class. +Encapsulates a whole scene (init, loop, cleanup) +*/ + +package geetransit.minecraft05.engine; + +import java.util.*; + +public class Scene implements Loopable { + // bit flags to add to different lists + public static final int INIT = 0b0001; + public static final int INPUT = 0b0010; + public static final int UPDATE = 0b0100; + public static final int RENDER = 0b1000; + public static final int ALL = INIT & INPUT & UPDATE & RENDER; + + private final List inits; + private final List inputs; + private final List updates; + private final List renders; + + public Scene() { + this.inits = new ArrayList<>(); + this.inputs = new ArrayList<>(); + this.updates = new ArrayList<>(); + this.renders = new ArrayList<>(); + } + + public List getInits() { return this.inits; } + public List getInputs() { return this.inputs; } + public List getUpdates() { return this.updates; } + public List getRenders() { return this.renders; } + + public Scene addInit(Initializable init) { this.inits.add(init); return this; } + public Scene addInput(Inputtable input) { this.inputs.add(input); return this; } + public Scene addUpdate(Updateable update) { this.updates.add(update); return this; } + public Scene addRender(Renderable render) { this.renders.add(render); return this; } + + public Scene addTo(int flags, Object obj) { + if ((flags & INIT) != 0) + this.addInit((Initializable) obj); + if ((flags & INPUT) != 0) + this.addInput((Inputtable) obj); + if ((flags & UPDATE) != 0) + this.addUpdate((Updateable) obj); + if ((flags & RENDER) != 0) + this.addRender((Renderable) obj); + return this; + } + + public Scene addFrom(Object obj) { + if (obj instanceof Initializable) + this.addTo(INIT, obj); + if (obj instanceof Inputtable) + this.addTo(INPUT, obj); + if (obj instanceof Updateable) + this.addTo(UPDATE, obj); + if (obj instanceof Renderable) + this.addTo(RENDER, obj); + return this; + } + + @Override + public void init(Window window) throws Exception { + for (Initializable init : this.getInits()) + init.init(window); + } + + @Override + public void input(Window window) { + for (Inputtable input : this.getInputs()) + input.input(window); + } + + @Override + public void render(Window window) { + for (Renderable render : this.getRenders()) + render.render(window); + } + + @Override + public void update(float interval) { + for (Updateable update : this.getUpdates()) + update.update(interval); + } + + @Override + public void cleanup() { + for (Initializable init : this.getInits()) + init.cleanup(); + } +} diff --git a/src/engine/SceneBase.java b/src/engine/SceneBase.java deleted file mode 100644 index a63384e..0000000 --- a/src/engine/SceneBase.java +++ /dev/null @@ -1,44 +0,0 @@ -/* -George Zhang -Base scene implementation. (no rendering) -*/ - -package geetransit.minecraft05.engine; - -import java.util.*; - -public abstract class SceneBase implements Loopable { - private List scenes; - - public SceneBase() { - this.scenes = new ArrayList<>(); - } - - public List getScenes() { return this.scenes; } - public SceneBase addScene(Loopable scene) { this.scenes.add(scene); return this; } - - public void init(Window window) throws Exception { - for (Loopable scene : this.getScenes()) - scene.init(window); - } - - public void input(Window window) { - for (Loopable scene : this.getScenes()) - scene.input(window); - } - - public void update(float interval) { - for (Loopable scene : this.getScenes()) - scene.update(interval); - } - - public void render(Window window) { - for (Loopable scene : this.getScenes()) - scene.render(window); - } - - public void cleanup() { - for (Loopable scene : this.getScenes()) - scene.cleanup(); - } -} diff --git a/src/engine/SceneRender.java b/src/engine/SceneRender.java deleted file mode 100644 index 1e9c202..0000000 --- a/src/engine/SceneRender.java +++ /dev/null @@ -1,70 +0,0 @@ -/* -George Zhang -Base rendered scene implementation. -*/ - -package geetransit.minecraft05.engine; - -import java.util.*; - -public abstract class SceneRender extends SceneBase { - private Renderer renderer; - private List items; - private Map> meshMap; - - public SceneRender(Renderer renderer) { - super(); - this.renderer = renderer; - this.items = new ArrayList<>(); - this.meshMap = new HashMap<>(); - } - public SceneRender() { - this(null); - } - - public Renderer getRenderer() { return this.renderer; } - public SceneRender setRenderer(Renderer renderer) { this.renderer = renderer; return this; } - - public List getItems() { return this.items; } - public Map> getMeshMap() { return this.meshMap; } - public SceneRender addItem(Item item) { - Mesh mesh = item.getMesh(); - this.items.add(item); - this.meshMap.putIfAbsent(mesh, new ArrayList<>()); - this.meshMap.get(mesh).add(item); - return this; - } - public SceneRender removeItem(Item item) { - Mesh mesh = item.getMesh(); - this.items.remove(item); - this.meshMap.get(mesh).remove(item); - if (this.meshMap.get(mesh).size() == 0) - this.meshMap.remove(mesh); - return this; - } - - @Override - public void init(Window window) throws Exception { - this.renderer.init(window); - super.init(window); - } - - @Override - public void input(Window window) { - super.input(window); - } - - @Override - public void render(Window window) { - this.renderer.render(window); - super.render(window); - } - - @Override - public void cleanup() { - this.renderer.cleanup(); - super.cleanup(); - for (Item item : this.getItems()) - item.cleanup(); - } -} diff --git a/src/engine/Window.java b/src/engine/Window.java index a7e93fd..0658603 100644 --- a/src/engine/Window.java +++ b/src/engine/Window.java @@ -227,8 +227,8 @@ private void renderUpdate() { } protected void updateWindowPos() { - int[] xPos = new int[1]; - int[] yPos = new int[1]; + int[] xPos = {0}; + int[] yPos = {0}; glfwGetWindowPos(this.getHandle(), xPos, yPos); this.xPos = xPos[0]; this.yPos = yPos[0]; diff --git a/src/game/Background.java b/src/game/Background.java index 34725b8..fa0f0ea 100644 --- a/src/game/Background.java +++ b/src/game/Background.java @@ -13,14 +13,9 @@ public class Background implements Loopable { private int direction; private float color; - private Camera camera; - private int render; - - public Background(Camera camera) { + public Background() { this.direction = 0; this.color = 0.5f; - this.camera = camera; - this.render = 0; } @Override @@ -36,18 +31,11 @@ public void input(Window window) { this.direction = 0; if (window.isKeyDown(GLFW_KEY_UP)) this.direction++; if (window.isKeyDown(GLFW_KEY_DOWN)) this.direction--; - - // render distance (camera) - this.render = 0; - if (window.isKeyDown(GLFW_KEY_L)) this.camera.setFar(Camera.FAR); - if (window.isKeyDown(GLFW_KEY_RIGHT_BRACKET)) this.render++; - if (window.isKeyDown(GLFW_KEY_LEFT_BRACKET)) this.render--; } @Override public void update(float interval) { this.color = Math.max(0f, Math.min(1f, this.color+0.01f*this.direction)); - this.camera.setFar(Math.max(Camera.NEAR+0.01f, this.camera.getFar() + 0.1f*this.render)); } @Override @@ -58,7 +46,4 @@ public void render(Window window) { else window.clearColor(this.color, this.color, this.color, 0.0f); } - - @Override - public void cleanup() {} } diff --git a/src/game/Game.java b/src/game/Game.java index f94c7f4..48ea0ed 100644 --- a/src/game/Game.java +++ b/src/game/Game.java @@ -14,7 +14,7 @@ import static org.lwjgl.glfw.GLFW.*; import static org.lwjgl.opengl.GL11.*; -public class Game extends SceneBase { +public class Game extends Scene { private Mouse mouse; private Camera camera; @@ -25,37 +25,31 @@ public class Game extends SceneBase { public Game() { super(); + + // inputs this.mouse = new Mouse(); - this.camera = new Camera(); + this.camera = new Camera(this.mouse); + this + .addFrom(this.mouse) + .addFrom(this.camera); - this.background = new Background(this.camera); + // child scenes + this.background = new Background(); this.skybox = new Skybox(this.camera); this.world = new World(this.mouse, this.camera); this.hud = new Hud(this.mouse, this.camera, this.world); - - // add scenes this - .addScene(this.background) - .addScene(this.skybox) - .addScene(this.world) - .addScene(this.hud); + .addFrom(this.background) + .addFrom(this.skybox) + .addFrom(this.world) + .addFrom(this.hud); } - public Mouse getMouse() { return this.mouse; } - public Camera getCamera() { return this.camera; } - - public Background getBackground() { return this.background; } - public World getWorld() { return this.world; } - public Hud getHud() { return this.hud; } - @Override public void init(Window window) throws Exception { System.out.println("LWJGL version: " + Version.getVersion()); System.out.println("OpenGL version: " + GL11.glGetString(GL11.GL_VERSION)); - // setup mouse - this.mouse.init(window); - // call child scenes' init super.init(window); @@ -68,7 +62,7 @@ public void init(Window window) throws Exception { // Setup a key callback. It will be called every time a key is pressed, repeated or released. window.setKeyCallback((handle, key, scancode, action, mods) -> { - if (key == GLFW_KEY_F4 && action == GLFW_RELEASE && ((mods & GLFW_MOD_SHIFT) != 0)) { + if (key == GLFW_KEY_F4 && action == GLFW_RELEASE && ((mods & GLFW_MOD_ALT) != 0)) { window.setShouldClose(true); // We will detect this in the rendering loop window.postEmptyEvent(); } @@ -116,18 +110,12 @@ else if ((mods & GLFW_MOD_SHIFT) != 0) window.next.add("targetFps", () -> window.setTargetFps(window.getTargetFps() + 1)); // debug if (key == GLFW_KEY_U && action == GLFW_RELEASE) { - System.out.println(this.camera); - System.out.println(this.mouse); + System.out.println("mouse="+this.mouse); + System.out.println("camera="+this.camera); } }); } - @Override - public void input(Window window) { - this.mouse.input(window); - super.input(window); - } - @Override public void render(Window window) { // clear the framebuffer diff --git a/src/game/Hud.java b/src/game/Hud.java index 5ad57a0..7feec76 100644 --- a/src/game/Hud.java +++ b/src/game/Hud.java @@ -9,30 +9,32 @@ import static org.lwjgl.glfw.GLFW.*; -public class Hud extends SceneRender { +public class Hud extends Scene { private static final int FONT_COLS = 16; private static final int FONT_ROWS = 16; private static final String FONT_FILE = "/res/font.png"; - private TextItem text; - private Item compass; - private Item crosshair; + private Renderer renderer; private Mouse mouse; private Camera camera; private World world; + private TextItem text; + private Item compass; + private Item crosshair; + public Hud(Mouse mouse, Camera camera, World world) { super(); - this.setRenderer(new Renderer() { + this.addFrom(this.renderer = new Renderer() { Shader shader; public void init(Window window) throws Exception { shader = create2D("/res/vertex-2d.vs", "/res/fragment-2d.fs"); } public void render(Window window) { - render2D(shader, window, Hud.this.getItems()); + render2DList(shader, window); } public void cleanup() { - shader.cleanup(); + destroy(shader); } }); this.mouse = mouse; @@ -53,7 +55,7 @@ public void init(Window window) throws Exception { this.crosshair = new Item(ObjLoader.loadMesh("/res/crosshair.obj")); this.crosshair.getMesh().setColor(1, 1, 1); - this + this.renderer .addItem(this.text) .addItem(this.compass) .addItem(this.crosshair); @@ -61,14 +63,14 @@ public void init(Window window) throws Exception { @Override public void render(Window window) { + this.text.setPosition(10f, window.getHeight() * 0.85f, 0f); + this.text.setScale(window.getWidth() * (1/3500f)); this.text.setText(String.format( "vsync=%s mode=%s mouse=%s\nchange=%s wait=%s\ncamera=%s\nmouse=%s", window.isVSync(), window.getMode(), window.getInputMode(GLFW_CURSOR) == GLFW_CURSOR_NORMAL, this.world.getChange(), this.world.getWait(), this.camera, this.mouse )); - this.text.setPosition(10f, window.getHeight() * 0.85f, 0f); - this.text.setScale(window.getWidth() * (1/3500f)); this.compass.setPosition(window.getWidth() * 0.95f, window.getWidth() * 0.05f, 0f); this.compass.setRotation(0f, 0f, 180f - this.camera.getRotation().y); diff --git a/src/game/Skybox.java b/src/game/Skybox.java index 618b1af..450d6fd 100644 --- a/src/game/Skybox.java +++ b/src/game/Skybox.java @@ -7,36 +7,35 @@ import geetransit.minecraft05.engine.*; -public class Skybox extends SceneRender { +public class Skybox extends Scene { + private Renderer renderer; private Camera camera; private Item skybox; public Skybox(Camera camera) { super(); - this.setRenderer(new Renderer() { + this.addFrom(this.renderer = new Renderer() { Shader shader; public void init(Window window) throws Exception { shader = create3D("/res/vertex-3d.vs", "/res/fragment-3d.fs"); } public void render(Window window) { - renderSkybox(shader, window, Skybox.this.getCamera(), Skybox.this.getItems()); + render3DSkybox(shader, window, Skybox.this.camera); } public void cleanup() { - shader.cleanup(); + destroy(shader); } }); this.camera = camera; } - public Camera getCamera() { return this.camera; } - @Override public void init(Window window) throws Exception { - super.init(window); Mesh mesh = ObjLoader.loadMesh("/res/skybox.obj"); mesh.setTexture(new Texture("/res/skybox.png")); this.skybox = new Item(mesh).setPosition(0, 0, 0); - this.addItem(this.skybox); + this.renderer.addItem(this.skybox); + super.init(window); } @Override diff --git a/src/game/World.java b/src/game/World.java index 27c8bfe..d8e6e6d 100644 --- a/src/game/World.java +++ b/src/game/World.java @@ -13,64 +13,56 @@ import static org.lwjgl.glfw.GLFW.*; -public class World extends SceneRender { - private Mouse mouse; - private float sensitivity; +public class World extends Scene { + public static final float CHANGE_DELAY = 0.2f; + public static final float STEP = 0.1f; - private Camera camera; - private float step; - private Vector3f movement; - private ClosestItem closestItem; + private final Renderer renderer; + private final Mouse mouse; + private final Camera camera; - private Map blockMap; + private final Map blockMap; + private final ClosestItem closestItem; + private final Vector3f movement; + private float step; + private int render; - private static final float CHANGE_DELAY = 0.2f; private String change; // ""=air private float wait; // time until next place / remove - - private static final int MAX_COLOR = 255*255*255; public World(Mouse mouse, Camera camera) { super(); - this.setRenderer(new Renderer() { + this.addFrom(this.renderer = new Renderer() { Shader shader; public void init(Window window) throws Exception { shader = create3D("/res/vertex-3d.vs", "/res/fragment-3d.fs"); } public void render(Window window) { - render3DMap(shader, window, World.this.getCamera(), World.this.getMeshMap()); + render3D(shader, window, World.this.camera); } public void cleanup() { - shader.cleanup(); + destroy(shader); } }); this.mouse = mouse; - this.sensitivity = 0.3f; - this.camera = camera; - this.step = 0.1f; - this.movement = new Vector3f(); + + this.blockMap = new HashMap<>(); this.closestItem = new ClosestItem(); + this.movement = new Vector3f(); + this.step = STEP; } - public Mouse getMouse() { return this.mouse; } - public float getSensitivity() { return this.sensitivity; } - - public Camera getCamera() { return this.camera; } - public float getStep() { return this.step; } - public Vector3f getMovement() { return this.movement; } - public ClosestItem getClosestItem() { return this.closestItem; } - public String getChange() { return this.change; } public float getWait() { return this.wait; } + public float getStep() { return this.step; } + public World setStep(float step) { this.step = step; return this; } + @Override public void init(Window window) throws Exception { - super.init(window); - // Create the blocks' mesh - this.blockMap = new HashMap<>(); this.blockMap.put("grassblock", this.loadBlock("/res/cube.obj", "/res/grassblock.png")); this.blockMap.put("cobbleblock", this.loadBlock("/res/cube.obj", "/res/cobbleblock.png")); @@ -79,30 +71,31 @@ public void init(Window window) throws Exception { // create terrain for (int x = 0; x < map.width; x++) { for (int z = 0; z < map.length; z++) { - int y = (int) map.compressExpand(map.heightAt(x, z), 0, MAX_COLOR, 0, 16); - this.addItem(this.newBlock("grassblock").setPosition(x, y, z)); + int y = (int) map.compressExpand(map.heightAt(x, z), 0, map.MAX_COLOR, 0, 16); + this.renderer.addItem(this.newBlock("grassblock").setPosition(x, y, z)); for (int k = y-1; k >= Math.max(y-2, 0); k--) { - this.addItem(this.newBlock("cobbleblock").setPosition(x, k, z)); + this.renderer.addItem(this.newBlock("cobbleblock").setPosition(x, k, z)); } } } } // add spawn markers (-2z is forwards) - this + this.renderer .addItem(this.newBlock("grassblock").setPosition(+1, +1, 0)) .addItem(this.newBlock("grassblock").setPosition(-1, +1, 0)) .addItem(this.newBlock("grassblock").setPosition( 0, +1, +1)) .addItem(this.newBlock("grassblock").setPosition( 0, +1, -2)); + + super.init(window); } - @Override public void input(Window window) { super.input(window); // movement this.movement.zero(); - boolean sprinting = (!window.isKeyDown(GLFW_KEY_LEFT_SHIFT) && window.isKeyDown(GLFW_KEY_LEFT_CONTROL)); + boolean SPRINTING = (!window.isKeyDown(GLFW_KEY_LEFT_SHIFT) && window.isKeyDown(GLFW_KEY_LEFT_CONTROL)); if (window.isKeyDown(GLFW_KEY_W)) this.movement.z--; if (window.isKeyDown(GLFW_KEY_S)) this.movement.z++; @@ -113,7 +106,13 @@ public void input(Window window) { if (window.isKeyDown(GLFW_KEY_SPACE)) this.movement.y++; if (this.movement.length() > 1f) this.movement.div(this.movement.length()); - if (sprinting && this.movement.z < 0) this.movement.mul(1.5f); + if (SPRINTING && this.movement.z < 0) this.movement.mul(1.5f); + + // render distance (camera) + this.render = 0; + if (window.isKeyDown(GLFW_KEY_L)) this.camera.setFar(Camera.FAR); + if (window.isKeyDown(GLFW_KEY_RIGHT_BRACKET)) this.render++; + if (window.isKeyDown(GLFW_KEY_LEFT_BRACKET)) this.render--; // placing / removing this.change = null; @@ -122,29 +121,33 @@ public void input(Window window) { if (window.isKeyDown(GLFW_KEY_2)) this.change = "cobbleblock"; } - @Override public void update(float interval) { - super.update(interval); - // movement - if (this.mouse.isInside()) - this.camera.rotateUsingMouse(this.mouse, this.sensitivity); this.camera.movePosition(this.movement.mul(this.step, new Vector3f())); + // render distance + this.camera.setFar(Math.max(Camera.NEAR+0.01f, this.camera.getFar() + 0.1f*this.render)); + // placing / removing if (this.change != null && this.wait <= 0) { - this.closestItem.update(this.getItems(), this.camera); + this.closestItem.update(this.renderer.items, this.camera); if (this.closestItem.closest != null) { if (this.change.equals("")) { - this.removeItem(this.closestItem.closest); + this.renderer.removeItem(this.closestItem.closest); } else { Vector3f position = new Vector3f(); position.set(this.closestItem.direction); // get normalized camera direction position.negate(); // move towards camera - position.mul(0.01f); + position.mul(0.01f); // add a small offset (to go to next block) position.add(this.closestItem.hit); // start from intersection point position.round(); // round to grid - this.addItem(this.newBlock(this.change).setPosition(position)); + check: { + for (Item item : this.renderer.items) + if (item.getPosition().equals(position)) + break check; + // else + this.renderer.addItem(this.newBlock(this.change).setPosition(position)); + } } } this.wait += this.CHANGE_DELAY; @@ -155,18 +158,19 @@ public void update(float interval) { this.wait -= interval; if (this.wait < 0 && this.change == null) this.wait = 0; + + super.update(interval); } - @Override public void render(Window window) { this.updateSelectedItem(); super.render(window); } private void updateSelectedItem() { - for (Item item : this.getItems()) + for (Item item : this.renderer.items) item.setSelected(false); - this.closestItem.update(this.getItems(), this.camera); + this.closestItem.update(this.renderer.items, this.camera); if (this.closestItem.closest != null) this.closestItem.closest.setSelected(true); } From 27396ca82c83442e86b9694a7b214a515f9aedfa Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Mon, 22 Jun 2020 16:43:22 -0400 Subject: [PATCH 05/52] Reformat spacing Indent uses tabs Comments are `code // comment` Remove trailing whitespace --- src/engine/Bucket.java | 4 +- src/engine/Camera.java | 42 +++++----- src/engine/ClosestItem.java | 18 ++--- src/engine/Engine.java | 8 +- src/engine/HeightMap.java | 12 +-- src/engine/Init.java | 2 +- src/engine/Input.java | 2 +- src/engine/Item.java | 12 +-- src/engine/Mesh.java | 34 ++++---- src/engine/Mouse.java | 4 +- src/engine/ObjLoader.java | 18 ++--- src/engine/Render.java | 2 +- src/engine/Renderer.java | 62 +++++++------- src/engine/Scene.java | 4 +- src/engine/Shader.java | 8 +- src/engine/TextItem.java | 56 ++++++------- src/engine/Texture.java | 8 +- src/engine/Timer.java | 34 ++++---- src/engine/Transformation.java | 8 +- src/engine/Update.java | 2 +- src/engine/Utils.java | 16 ++-- src/engine/Window.java | 142 ++++++++++++++++----------------- src/game/Background.java | 12 +-- src/game/Game.java | 22 ++--- src/game/Hud.java | 24 +++--- src/game/Skybox.java | 10 +-- src/game/World.java | 52 ++++++------ 27 files changed, 309 insertions(+), 309 deletions(-) diff --git a/src/engine/Bucket.java b/src/engine/Bucket.java index 258b6bf..77491fa 100644 --- a/src/engine/Bucket.java +++ b/src/engine/Bucket.java @@ -10,7 +10,7 @@ Bucket class (map of lambdas). public class Bucket { public final Map next; public Bucket() { this.next = new HashMap<>(); } - + public boolean run(String string) { Runnable runnable = this.remove(string); if (runnable == null) @@ -22,6 +22,6 @@ public boolean run(String string) { public Runnable remove(String string) { return this.next.remove(string); } public boolean empty() { return this.next.isEmpty(); } public Bucket add(String string, Runnable runnable) { this.next.put(string, runnable); return this; } - + public String toString() { return this.next.toString(); } } diff --git a/src/engine/Camera.java b/src/engine/Camera.java index ca90008..719c14d 100644 --- a/src/engine/Camera.java +++ b/src/engine/Camera.java @@ -16,57 +16,57 @@ public class Camera implements Inputtable { public static final float SENSITIVITY = 0.75f; private Mouse mouse; - + private final Vector3f position; private final Vector3f rotation; // in degrees private final Matrix4f viewMatrix; - + private float fov; private float near; private float far; private float sensitivity; - + private final Vector3f radiansRotation; private final Vector3f negativePosition; public Camera(Mouse mouse) { this.mouse = mouse; - + this.position = new Vector3f(); this.rotation = new Vector3f(); this.viewMatrix = new Matrix4f(); - + this.setFov(FOV); this.setNear(NEAR); this.setFar(FAR); this.setSensitivity(SENSITIVITY); - + this.radiansRotation = new Vector3f(); this.negativePosition = new Vector3f(); } - + @Override public void input(Window window) { if (this.mouse.isInside()) this.rotateMovement(this.mouse.getMovement(), this.sensitivity); } - + public Mouse getMouse() { return this.mouse; } public Vector3f getPosition() { return this.position; } public Vector3f getRotation() { return this.rotation; } - public float getFov() { return (float) Math.toDegrees(this.fov); } + public float getFov() { return (float) Math.toDegrees(this.fov); } public float getNear() { return this.near; } - public float getFar() { return this.far; } + public float getFar() { return this.far; } public float getSensitivity() { return this.sensitivity; } - + public Camera setMouse(Mouse mouse) { this.mouse = mouse; return this; } - public Camera setPosition(Vector3f position) { this.position.set(position); return this; } + public Camera setPosition(Vector3f position) { this.position.set(position); return this; } public Camera setPosition(float x, float y, float z) { this.position.set(x, y, z); return this; } - public Camera setRotation(Vector3f rotation) { this.rotation.set(rotation); return this; } + public Camera setRotation(Vector3f rotation) { this.rotation.set(rotation); return this; } public Camera setRotation(float x, float y, float z) { this.rotation.set(x, y, z); return this; } - public Camera setFov(float fov) { this.fov = (float) Math.toRadians(fov); return this; } + public Camera setFov(float fov) { this.fov = (float) Math.toRadians(fov); return this; } public Camera setNear(float near) { this.near = near; return this; } - public Camera setFar(float far) { this.far = far; return this; } + public Camera setFar(float far) { this.far = far; return this; } public Camera setSensitivity(float sensitivity) { this.sensitivity = sensitivity; return this; } public Camera movePosition(Vector3f position) { return this.movePosition(position.x, position.y, position.z); } @@ -83,13 +83,13 @@ public Camera movePosition(float x, float y, float z) { this.position.y += y; return this; } - + public Camera moveRotation(Vector3f rotation) { this.rotation.add(rotation); return this; } public Camera moveRotation(float x, float y, float z) { this.rotation.add(x, y, z); return this; } - + public Camera rotateMovement(Vector2f movement, float sensitivity) { float x = movement.x; float y = movement.y; @@ -100,7 +100,7 @@ public Camera rotateMovement(Vector2f movement, float sensitivity) { this.rotation.x = Math.max(-90f, Math.min(90f, this.rotation.x)); // don't allow neck snapping return this; } - + public Vector3f getRadiansRotation() { return this.radiansRotation.set( (float) Math.toRadians(this.rotation.x), @@ -108,18 +108,18 @@ public Vector3f getRadiansRotation() { (float) Math.toRadians(this.rotation.z) ); } - + public Vector3f getNegativePosition() { return this.position.negate(this.negativePosition); } - + public Matrix4f getViewMatrix() { return this.viewMatrix .identity() .rotateXYZ(this.getRadiansRotation()) .translate(this.getNegativePosition()); } - + public String toString() { return String.format( "<%s position=%s rotation=%s>", diff --git a/src/engine/ClosestItem.java b/src/engine/ClosestItem.java index 5aa09cd..cbe2433 100644 --- a/src/engine/ClosestItem.java +++ b/src/engine/ClosestItem.java @@ -16,15 +16,15 @@ public class ClosestItem { public Item closest; public Vector3f hit; // position of intersection public Vector3f direction; // points away from camera - + private final Vector3f max; private final Vector3f min; private final Vector2f nearFar; - + public ClosestItem() { this.hit = new Vector3f(); this.direction = new Vector3f(); - + this.min = new Vector3f(); this.max = new Vector3f(); this.nearFar = new Vector2f(); @@ -33,30 +33,30 @@ public ClosestItem(List items, Camera camera) { this(); this.update(items, camera); } - + public ClosestItem reset() { this.distance = Float.POSITIVE_INFINITY; this.closest = null; return this; } - + public ClosestItem update(List items, Camera camera) { this.reset().extend(items, camera); return this; } - + public ClosestItem extend(List items, Camera camera) { // get camera direction camera.getViewMatrix().positiveZ(this.direction); this.direction.negate().normalize(); - + // loop through all items for (Item item : items) { this.min.set(item.getPosition()); this.max.set(item.getPosition()); this.min.add(-item.getScale(), -item.getScale(), -item.getScale()); this.max.add(item.getScale(), item.getScale(), item.getScale()); - + // check if intersects and is closer if (Intersectionf.intersectRayAab( camera.getPosition(), this.direction, @@ -71,7 +71,7 @@ public ClosestItem extend(List items, Camera camera) { } } } - + // allow method chaining return this; } diff --git a/src/engine/Engine.java b/src/engine/Engine.java index 121e13d..2a46277 100644 --- a/src/engine/Engine.java +++ b/src/engine/Engine.java @@ -8,11 +8,11 @@ public class Engine implements Runnable { private Window window; private Loopable loop; - + private boolean updateVSync = false; private boolean updateSize = false; private boolean destroyed = false; - + public Engine( Window window, Loopable loop @@ -20,14 +20,14 @@ public Engine( this.window = window; this.loop = loop; } - + @Override public void run() { this.window.createWindow(); new Thread(() -> this.window.renderThread(this.loop)).start(); this.window.eventThread(); } - + public Window getWindow() { return this.window; } public Loopable getLoop() { return this.loop; } } diff --git a/src/engine/HeightMap.java b/src/engine/HeightMap.java index 9eb8f89..5ab9fe5 100644 --- a/src/engine/HeightMap.java +++ b/src/engine/HeightMap.java @@ -10,35 +10,35 @@ Encapsulate an image (used to get pixel values). public class HeightMap implements AutoCloseable { public static final int CHANNELS = 4; public static final int MAX_COLOR = 255*255*255; - + public final ByteBuffer buffer; public final int width; public final int length; - + public HeightMap(ByteBuffer buffer, int width, int length) { this.buffer = buffer; this.width = width; this.length = length; } - + public static HeightMap loadFromImage(String fileName) throws Exception { int width[] = {0}, length[] = {0}; ByteBuffer buffer = Utils.loadImage(fileName, width, length); return new HeightMap(buffer, width[0], length[0]); } - + @Override public void close() { Utils.freeImage(this.buffer); } - + // usage: compressExpand(heightAt(...), 0, MAX_COLOR, 0, 16) public static float compressExpand(int f, float cMin, float cMax, float eMin, float eMax) { return expand(compress(f, cMin, cMax), eMin, eMax); } public static float compress(int f, float min, float max) { return (f-min) / (max-min); } public static float expand(float f, float min, float max) { return min + f*(max-min); } - + public int heightAt(int x, int z) { return this.heightAt(x*CHANNELS + z*CHANNELS*this.width); } public int heightAt(int i) { byte r = this.buffer.get(i + 0); diff --git a/src/engine/Init.java b/src/engine/Init.java index 001d38b..eb1cd3a 100644 --- a/src/engine/Init.java +++ b/src/engine/Init.java @@ -10,7 +10,7 @@ public class Init implements Initializable { private List inits; - + public Init(List inits) { this.inits = inits; } diff --git a/src/engine/Input.java b/src/engine/Input.java index 2d41e73..552aba1 100644 --- a/src/engine/Input.java +++ b/src/engine/Input.java @@ -10,7 +10,7 @@ public class Input implements Inputtable { private List inputs; - + public Input(List inputs) { this.inputs = inputs; } diff --git a/src/engine/Item.java b/src/engine/Item.java index dd7e8ae..7725a95 100644 --- a/src/engine/Item.java +++ b/src/engine/Item.java @@ -26,7 +26,7 @@ protected Item() { this.scale = 1; this.selected = false; } - + // does NOT copy the mesh (shallow copy) @Override public Item clone() { @@ -36,9 +36,9 @@ public Item clone() { .setScale(this.getScale()) .setSelected(this.isSelected()); } - + public Mesh getMesh() { return this.mesh; } - + public Vector3f getPosition() { return this.position; } public Item setPosition(Vector3f position) { this.position.set(position); return this; } public Item setPosition(float x, float y, float z) { @@ -47,7 +47,7 @@ public Item setPosition(float x, float y, float z) { this.position.z = z; return this; } - + public Quaternionf getRotation() { return this.rotation; } public Item setRotation(Quaternionf rotation) { this.rotation.set(rotation); return this; } public Item setRotation(float x, float y, float z) { @@ -56,13 +56,13 @@ public Item setRotation(float x, float y, float z) { this.rotation.z = z; return this; } - + public float getScale() { return this.scale; } public Item setScale(float scale) { this.scale = scale; return this; } - + public boolean isSelected() { return this.selected; } public Item setSelected(boolean selected) { this.selected = selected; diff --git a/src/engine/Mesh.java b/src/engine/Mesh.java index 63ae02f..23d18e1 100644 --- a/src/engine/Mesh.java +++ b/src/engine/Mesh.java @@ -13,11 +13,11 @@ public class Mesh { private static final Vector3f DEFAULT_COLOUR = new Vector3f(0.0f, 0.0f, 0.0f); - + private final int vaoId; private final int vertexCount; private final List vboIdList; - + private Texture texture; private Vector4f color; @@ -26,7 +26,7 @@ public Mesh(float[] posArray, int[] indexArray, float[] coordArray) { this.vertexCount = indexArray.length; this.color = new Vector4f(); this.setColor(DEFAULT_COLOUR); - + int vboId; FloatBuffer posBuffer = null; IntBuffer indexBuffer = null; @@ -35,8 +35,8 @@ public Mesh(float[] posArray, int[] indexArray, float[] coordArray) { // Create the VAO this.vaoId = glGenVertexArrays(); glBindVertexArray(this.vaoId); - - + + // Position VBO vboId = glGenBuffers(); this.vboIdList.add(vboId); @@ -46,7 +46,7 @@ public Mesh(float[] posArray, int[] indexArray, float[] coordArray) { glBufferData(GL_ARRAY_BUFFER, posBuffer, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0); - + // Index VBO vboId = glGenBuffers(); this.vboIdList.add(vboId); @@ -54,7 +54,7 @@ public Mesh(float[] posArray, int[] indexArray, float[] coordArray) { indexBuffer.put(indexArray).flip(); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboId); glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBuffer, GL_STATIC_DRAW); - + // texture coords VBO vboId = glGenBuffers(); this.vboIdList.add(vboId); @@ -68,7 +68,7 @@ public Mesh(float[] posArray, int[] indexArray, float[] coordArray) { // Unbind the VBO / VAB glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); - + } finally { if (posBuffer != null) memFree(posBuffer); if (indexBuffer != null) memFree(indexBuffer); @@ -78,17 +78,17 @@ public Mesh(float[] posArray, int[] indexArray, float[] coordArray) { public int getVaoId() { return this.vaoId; } public int getVertexCount() { return this.vertexCount; } - + public Texture getTexture() { return this.texture; } public Mesh setTexture(Texture texture) { this.texture = texture; return this; } public boolean isTextured() { return this.texture != null; } - + public Vector4f getColor() { return this.color; } public Mesh setColor(float r, float g, float b) { this.setColor(new Vector3f(r, g, b)); return this; } public Mesh setColor(float r, float g, float b, float a) { this.setColor(new Vector4f(r, g, b, a)); return this; } public Mesh setColor(Vector3f color) { this.setColor(new Vector4f(color, 1f)); return this; } public Mesh setColor(Vector4f color) { this.color.set(color); return this; } - + // prepare mesh public void prepare() { this.prepare(null); } public void prepare(Mesh lastMesh) { @@ -98,12 +98,12 @@ public void prepare(Mesh lastMesh) { this.texture.prepare(); glBindVertexArray(this.vaoId); } - + // draw elements public void render() { glDrawElements(GL_TRIANGLES, this.vertexCount, GL_UNSIGNED_INT, 0); } - + // Restore state public void restore() { this.restore(null); } public void restore(Mesh nextMesh) { @@ -111,24 +111,24 @@ public void restore(Mesh nextMesh) { return; glBindVertexArray(0); } - + protected void deleteVbos() { // Delete the VBO glBindBuffer(GL_ARRAY_BUFFER, 0); for (int id : this.vboIdList) glDeleteBuffers(id); } - + protected void disableVao() { glDisableVertexAttribArray(0); } - + protected void deleteVao() { // Delete the VAO glBindVertexArray(0); glDeleteVertexArrays(this.vaoId); } - + public void cleanup() { this.cleanup(true); } public void cleanup(boolean cleanupTexture) { this.disableVao(); diff --git a/src/engine/Mouse.java b/src/engine/Mouse.java index 9a44cfd..a18d27e 100644 --- a/src/engine/Mouse.java +++ b/src/engine/Mouse.java @@ -22,7 +22,7 @@ public Mouse() { this.movement = new Vector2f(); this.previous = new Vector2f(0, 0); } - + public Vector2f getMovement() { return this.movement; } public Vector2f getCurrent() { return this.current; } public boolean isInside() { return this.inside; } @@ -48,7 +48,7 @@ public void input(Window window) { this.movement.div(window.getTargetUps() * window.getElapsedTime()); this.previous.set(this.current); } - + public String toString() { return String.format( "<%s movement=%s current=%s>", diff --git a/src/engine/ObjLoader.java b/src/engine/ObjLoader.java index ac80374..9c8b7f0 100644 --- a/src/engine/ObjLoader.java +++ b/src/engine/ObjLoader.java @@ -13,7 +13,7 @@ public static Mesh loadMesh(String file) throws Exception { List vertices = new ArrayList<>(); List textures = new ArrayList<>(); List faces = new ArrayList<>(); - + Utils.loadLinesStream(file).forEach(line -> { String[] tokens = line.split("\\s+"); switch (tokens[0]) { @@ -43,7 +43,7 @@ public static Mesh loadMesh(String file) throws Exception { }); return reorderLists(vertices, textures, faces); } - + private static Mesh reorderLists( List vertexList, List coordList, @@ -63,7 +63,7 @@ private static Mesh reorderLists( for (Face face : faceList) for (IndexGroup group : face.groups) processFaceVertex(group, coordList, posList, coordArray); - + // int[] indexArray = new int[indices.size()]; int[] indexArray = Utils.intListToArray(posList); return new Mesh(posArray, indexArray, coordArray); @@ -86,18 +86,18 @@ private static void processFaceVertex( coordArray[pos*2 + 1] = 1 - coord.y; } } - + protected static class IndexGroup { public static final int NO_VALUE = -1; public int pos; public int coord; - + public IndexGroup() { this.pos = NO_VALUE; this.coord = NO_VALUE; } } - + protected static class Face { // List of pos groups for a face triangle (3 vertices per face). public final IndexGroup[] groups; @@ -115,17 +115,17 @@ private IndexGroup parseLine(String line) { String[] tokens = line.split("/"); int length = tokens.length; - + group.pos = Integer.parseInt(tokens[0]) - 1; if (length <= 1) return group; - + // It can be empty if the obj does not define text coords if (tokens[1].length() != 0) group.coord = Integer.parseInt(tokens[1]) - 1; if (length <= 2) return group; - + return group; } } diff --git a/src/engine/Render.java b/src/engine/Render.java index 97e6a54..9029e5a 100644 --- a/src/engine/Render.java +++ b/src/engine/Render.java @@ -10,7 +10,7 @@ public class Render implements Renderable { private List renders; - + public Render(List renders) { this.renders = renders; } diff --git a/src/engine/Renderer.java b/src/engine/Renderer.java index 9293372..6401577 100644 --- a/src/engine/Renderer.java +++ b/src/engine/Renderer.java @@ -13,22 +13,22 @@ public abstract class Renderer implements Initializable, Renderable { public final Map> map; public final List items; public final Transformation transformation; - + public Renderer() { this.map = new HashMap<>(); this.items = new ArrayList<>(); this.transformation = new Transformation(); } - + // create shaders: shader = create?(VERTEX_SHADER, FRAGMENT_SHADER); public abstract void init(Window window) throws Exception; - + // render scene: render?(shader, window, ?); public abstract void render(Window window); - + // destroy shaders: shader.cleanup(); public abstract void cleanup(); - + public Renderer addItem(Item item) { Mesh mesh = item.getMesh(); this.items.add(item); @@ -37,7 +37,7 @@ public Renderer addItem(Item item) { this.map.get(mesh).add(item); return this; } - + public Renderer removeItem(Item item) { Mesh mesh = item.getMesh(); this.items.remove(item); @@ -46,7 +46,7 @@ public Renderer removeItem(Item item) { this.map.remove(mesh); return this; } - + // shader creators public Shader createShader(String vertex, String fragment) throws Exception { Shader shader = new Shader(); @@ -55,7 +55,7 @@ public Shader createShader(String vertex, String fragment) throws Exception { shader.link(); return shader; } - + public Shader create3D(String vertex, String fragment) throws Exception { Shader shader = this.createShader(vertex, fragment); shader.createUniform("projectionMatrix"); @@ -66,7 +66,7 @@ public Shader create3D(String vertex, String fragment) throws Exception { shader.createUniform("isSelected"); return shader; } - + public Shader create2D(String vertex, String fragment) throws Exception { Shader shader = this.createShader(vertex, fragment); shader.createUniform("projModelMatrix"); @@ -75,20 +75,20 @@ public Shader create2D(String vertex, String fragment) throws Exception { shader.createUniform("isTextured"); return shader; } - + // note does NOT call Item.render(Window) public void render3D(Shader shader, Window window, Camera camera) { shader.bind(); glEnable(GL_CULL_FACE); glCullFace(GL_BACK); - + // projection Matrix4f projectionMatrix = this.transformation.getProjectionMatrix(window, camera); shader.setUniform("projectionMatrix", projectionMatrix); - + // view Matrix4f viewMatrix = camera.getViewMatrix(); - + // Draw meshes shader.setUniform("texture_sampler", 0); for (Map.Entry> entry : this.map.entrySet()) { @@ -97,7 +97,7 @@ public void render3D(Shader shader, Window window, Camera camera) { shader.setUniform("color", mesh.getColor()); shader.setUniform("isTextured", mesh.isTextured()); mesh.prepare(); - + // single : loop through items for (Item item : items) { Matrix4f modelViewMatrix = this.transformation.getModelViewMatrix(item, viewMatrix); @@ -105,24 +105,24 @@ public void render3D(Shader shader, Window window, Camera camera) { shader.setUniform("isSelected", item.isSelected()); mesh.render(); } - + mesh.restore(); } - + glDisable(GL_CULL_FACE); shader.unbind(); } - + // uses the List of items public void render2DList(Shader shader, Window window) { shader.bind(); - + // source # https://stackoverflow.com/a/5467636 glDepthMask(false); // disable writes to Z-Buffer glDisable(GL_DEPTH_TEST); // disable depth-testing Matrix4f orthoMatrix = this.transformation.getOrthoProjectionMatrix(window); - + // Draw meshes shader.setUniform("texture_sampler", 0); for (Item item : this.items) { @@ -130,34 +130,34 @@ public void render2DList(Shader shader, Window window) { shader.setUniform("color", mesh.getColor()); shader.setUniform("isTextured", mesh.isTextured()); mesh.prepare(); - + Matrix4f projModelMatrix = this.transformation.getOrthoProjModelMatrix(item, orthoMatrix); shader.setUniform("projModelMatrix", projModelMatrix); mesh.render(); - + mesh.restore(); } - + glDepthMask(true); glEnable(GL_DEPTH_TEST); shader.unbind(); } - + // note does NOT call Item.render(Window) public void render3DSkybox(Shader shader, Window window, Camera camera) { shader.bind(); - + // projection Matrix4f projectionMatrix = this.transformation.getProjectionMatrix(window, camera); shader.setUniform("projectionMatrix", projectionMatrix); - + // view Matrix4f viewMatrix = camera.getViewMatrix(); - + // remove translation (different from render3D) viewMatrix.setTranslation(0, 0, 0); - + // Draw meshes shader.setUniform("texture_sampler", 0); for (Map.Entry> entry : this.map.entrySet()) { @@ -166,7 +166,7 @@ public void render3DSkybox(Shader shader, Window window, Camera camera) { shader.setUniform("color", mesh.getColor()); shader.setUniform("isTextured", mesh.isTextured()); mesh.prepare(); - + // single : loop through items for (Item item : items) { Matrix4f modelViewMatrix = this.transformation.getModelViewMatrix(item, viewMatrix); @@ -174,19 +174,19 @@ public void render3DSkybox(Shader shader, Window window, Camera camera) { shader.setUniform("isSelected", item.isSelected()); mesh.render(); } - + mesh.restore(); } shader.unbind(); } - + public void destroy(Shader shader) { this.destroyShader(shader); for (Mesh mesh : this.map.keySet()) mesh.cleanup(); } - + public void destroyShader(Shader shader) { shader.cleanup(); } diff --git a/src/engine/Scene.java b/src/engine/Scene.java index a84b529..e25f400 100644 --- a/src/engine/Scene.java +++ b/src/engine/Scene.java @@ -10,8 +10,8 @@ Encapsulates a whole scene (init, loop, cleanup) public class Scene implements Loopable { // bit flags to add to different lists - public static final int INIT = 0b0001; - public static final int INPUT = 0b0010; + public static final int INIT = 0b0001; + public static final int INPUT = 0b0010; public static final int UPDATE = 0b0100; public static final int RENDER = 0b1000; public static final int ALL = INIT & INPUT & UPDATE & RENDER; diff --git a/src/engine/Shader.java b/src/engine/Shader.java index b74936b..927116e 100644 --- a/src/engine/Shader.java +++ b/src/engine/Shader.java @@ -15,7 +15,7 @@ public class Shader { private int vertexShaderId; private int fragmentShaderId; - + private final Map uniforms; public Shader() throws Exception { @@ -25,7 +25,7 @@ public Shader() throws Exception { } this.uniforms = new HashMap<>(); } - + public void createUniform(String name) throws Exception { int location = glGetUniformLocation(this.programId, name); if (location < 0) { @@ -33,7 +33,7 @@ public void createUniform(String name) throws Exception { } uniforms.put(name, location); } - + public void setUniform(String name, Matrix4f value) { // Dump the matrix into a float buffer try (MemoryStack stack = MemoryStack.stackPush()) { @@ -90,7 +90,7 @@ public void link() throws Exception { if (this.fragmentShaderId != 0) { glDetachShader(this.programId, this.fragmentShaderId); } - + // definition in res/vertex.vs // equivalent of `layout (location = #) ...` glBindAttribLocation(this.programId, 0, "position"); diff --git a/src/engine/TextItem.java b/src/engine/TextItem.java index 7dca5c6..0cc9e9c 100644 --- a/src/engine/TextItem.java +++ b/src/engine/TextItem.java @@ -12,24 +12,24 @@ public class TextItem extends Item { private static final float ZPOS = 0f; private static final int VERTICES_PER_QUAD = 4; - + private String text; private final int fontCols; private final int fontRows; - + public TextItem(String text, String fontFile, int fontCols, int fontRows) throws Exception { super(); this.text = text; this.fontCols = fontCols; this.fontRows = fontRows; this.mesh = this.buildMesh(new Texture(fontFile)); - + } - + public String getText() { return this.text; } public int getFontCols() { return this.fontCols; } public int getFontRows() { return this.fontRows; } - + public Item setText(String text) { this.text = text; Vector4f color = this.mesh.getColor(); @@ -38,17 +38,17 @@ public Item setText(String text) { this.mesh.setColor(color); return this; } - + private Mesh buildMesh(Texture texture) { byte[] charArray = this.text.getBytes(Charset.forName("ISO-8859-1")); List posList = new ArrayList<>(); List coordList = new ArrayList<>(); List indexList = new ArrayList<>(); - + float width = (float) texture.getWidth() / this.fontCols; float length = (float) texture.getLength() / this.fontRows; - + int currentCol = 0; int currentRow = 0; int currentIndex = 0; @@ -59,51 +59,51 @@ private Mesh buildMesh(Texture texture) { currentCol = 0; continue; } - + // Build a character tile composed by two triangles int fontCol = currentChar % this.fontCols; int fontRow = currentChar / this.fontCols; - + // Left Top vertex - posList.add(currentCol*width); // x - posList.add(currentRow*length); // y - posList.add(ZPOS); // z + posList.add(currentCol*width); // x + posList.add(currentRow*length); // y + posList.add(ZPOS); // z coordList.add((float) fontCol / this.fontCols); coordList.add((float) fontRow / this.fontRows); indexList.add(currentIndex*VERTICES_PER_QUAD + 0); - + // Left Bottom vertex - posList.add(currentCol*width); // x - posList.add(currentRow*length + length); // y - posList.add(ZPOS); // z + posList.add(currentCol*width); // x + posList.add(currentRow*length + length); // y + posList.add(ZPOS); // z coordList.add((float) fontCol / this.fontCols); coordList.add((float) (fontRow + 1) / this.fontRows); indexList.add(currentIndex*VERTICES_PER_QUAD + 1); - + // Right Bottom vertex - posList.add(currentCol*width + width); // x - posList.add(currentRow*length + length); // y - posList.add(ZPOS); // z + posList.add(currentCol*width + width); // x + posList.add(currentRow*length + length); // y + posList.add(ZPOS); // z coordList.add((float) (fontCol + 1) / this.fontCols); coordList.add((float) (fontRow + 1) / this.fontRows); indexList.add(currentIndex*VERTICES_PER_QUAD + 2); - + // Right Top vertex - posList.add(currentCol*width + width); // x - posList.add(currentRow*length); // y - posList.add(ZPOS); // z + posList.add(currentCol*width + width); // x + posList.add(currentRow*length); // y + posList.add(ZPOS); // z coordList.add((float) (fontCol + 1) / this.fontCols); coordList.add((float) fontRow / this.fontRows); indexList.add(currentIndex*VERTICES_PER_QUAD + 3); - + // Add indices for left top and bottom right vertices indexList.add(currentIndex*VERTICES_PER_QUAD + 0); indexList.add(currentIndex*VERTICES_PER_QUAD + 2); - + currentCol++; currentIndex++; } - + float[] posArray = Utils.floatListToArray(posList); float[] coordArray = Utils.floatListToArray(coordList); int[] indexArray = Utils.intListToArray(indexList); diff --git a/src/engine/Texture.java b/src/engine/Texture.java index eccb722..6dc2c32 100644 --- a/src/engine/Texture.java +++ b/src/engine/Texture.java @@ -24,7 +24,7 @@ public Texture(int id, int width, int length) throws Exception { this.width = width; this.length = length; } - + public int getId() { return this.id; } public int getWidth() { return this.width; } public int getLength() { return this.length; } @@ -39,7 +39,7 @@ public void prepare() { public void cleanup() { glDeleteTextures(this.id); } - + private void loadTexture(String fileName) throws Exception { ByteBuffer image = Utils.loadImage(fileName, (w, l) -> { this.width = w; this.length = l; }); @@ -59,9 +59,9 @@ private void loadTexture(String fileName) throws Exception { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, this.width, this.length, 0, GL_RGBA, GL_UNSIGNED_BYTE, image); // Generate Mip Map glGenerateMipmap(GL_TEXTURE_2D); - + Utils.freeImage(image); - + this.id = textureId; } } diff --git a/src/engine/Timer.java b/src/engine/Timer.java index 8fdaebb..0b3fc5c 100644 --- a/src/engine/Timer.java +++ b/src/engine/Timer.java @@ -8,24 +8,24 @@ public class Timer { - private double lastLoopTime; - - public void init() { - this.lastLoopTime = getTime(); - } + private double lastLoopTime; - public double getTime() { - return System.nanoTime() / 1000_000_000.0; - } + public void init() { + this.lastLoopTime = getTime(); + } - public float getElapsedTime() { - double time = getTime(); - float elapsedTime = (float) (time - this.lastLoopTime); - this.lastLoopTime = time; - return elapsedTime; - } + public double getTime() { + return System.nanoTime() / 1000_000_000.0; + } - public double getLastLoopTime() { - return this.lastLoopTime; - } + public float getElapsedTime() { + double time = getTime(); + float elapsedTime = (float) (time - this.lastLoopTime); + this.lastLoopTime = time; + return elapsedTime; + } + + public double getLastLoopTime() { + return this.lastLoopTime; + } } diff --git a/src/engine/Transformation.java b/src/engine/Transformation.java index abf56fb..defd9e6 100644 --- a/src/engine/Transformation.java +++ b/src/engine/Transformation.java @@ -30,19 +30,19 @@ public Matrix4f getProjectionMatrix(Window window, Camera camera) { camera.getNear(), camera.getFar() ); } - + public Matrix4f getModelMatrix(Item item) { return this.modelMatrix.translationRotateScale(item.getPosition(), item.getRotation(), item.getScale()); } - + public Matrix4f getModelViewMatrix(Item item, Matrix4f viewMatrix) { return viewMatrix.mulAffine(this.getModelMatrix(item), this.modelViewMatrix); } - + public Matrix4f getOrthoProjectionMatrix(Window window) { return this.orthoProjectionMatrix.setOrtho2D(0, window.getWidth(), window.getHeight(), 0); } - + // these 2 are different? public Matrix4f newGetOrthoProjModelMatrix(Item item, Matrix4f orthoMatrix) { return orthoMatrix.mulOrthoAffine(this.getModelMatrix(item), this.orthoProjModelMatrix); diff --git a/src/engine/Update.java b/src/engine/Update.java index dc224e4..1b42519 100644 --- a/src/engine/Update.java +++ b/src/engine/Update.java @@ -10,7 +10,7 @@ public class Update implements Updateable { private List updates; - + public Update(List updates) { this.updates = updates; } diff --git a/src/engine/Utils.java b/src/engine/Utils.java index cbea121..b7f229c 100644 --- a/src/engine/Utils.java +++ b/src/engine/Utils.java @@ -17,14 +17,14 @@ import org.lwjgl.stb.*; public class Utils { - + public static InputStream loadInputStream(String file) throws Exception { InputStream in = Utils.class.getResourceAsStream(file); if (in == null) throw new Exception("file [" + file + "] does not exist"); return in; } - + // source # https://stackoverflow.com/a/17861016 public static byte[] loadByteArray(String file) throws Exception { try ( @@ -54,29 +54,29 @@ public static Stream loadLinesStream(String file) throws Exception { InputStreamReader isr = new InputStreamReader(in, StandardCharsets.UTF_8.name()); return new BufferedReader(isr).lines(); } - + public static ByteBuffer loadImage(String fileName, BiConsumer consumer) throws Exception { ByteBuffer imageBuffer; ByteBuffer rawBuffer; - + // Load Texture file try (MemoryStack stack = MemoryStack.stackPush()) { IntBuffer widthBuffer = stack.mallocInt(1); IntBuffer heightBuffer = stack.mallocInt(1); IntBuffer channelsBuffer = stack.mallocInt(1); - + byte[] array = loadByteArray(fileName); rawBuffer = MemoryUtil.memAlloc(array.length); rawBuffer.put(array).flip(); imageBuffer = STBImage.stbi_load_from_memory(rawBuffer, widthBuffer, heightBuffer, channelsBuffer, 4); if (imageBuffer == null) - throw new Exception("Image file [" + fileName + "] not loaded: " + STBImage.stbi_failure_reason()); + throw new Exception("Image file [" + fileName + "] not loaded: " + STBImage.stbi_failure_reason()); // Get width and height of image consumer.accept(widthBuffer.get(), heightBuffer.get()); } - + return imageBuffer; } public static ByteBuffer loadImage(String fileName, int[] widthArray, int[] heightArray) throws Exception { @@ -85,7 +85,7 @@ public static ByteBuffer loadImage(String fileName, int[] widthArray, int[] heig public static void freeImage(ByteBuffer imageBuffer) { STBImage.stbi_image_free(imageBuffer); } - + public static int[] intListToArray(List intList) { return intList.stream().mapToInt(i -> i).toArray(); } diff --git a/src/engine/Window.java b/src/engine/Window.java index 0658603..a92d5a4 100644 --- a/src/engine/Window.java +++ b/src/engine/Window.java @@ -18,30 +18,30 @@ public class Window { private Timer timer; private Object lock; public final Bucket next; - + private String title; private int xPos; private int yPos; private int width; private int height; private int mode; - + public static final int WINDOWED = 0; public static final int BORDERLESS = 1; public static final int FULLSCREEN = 2; - + private boolean vSync; private int targetFps; private int targetUps; - + private float elapsedTime; private float accumulatedTime; - + private boolean destroyed = false; - + private long monitor; private GLFWVidMode vidmode; - + public Window( String title, int width, @@ -55,16 +55,16 @@ public Window( this.width = width; this.height = height; this.mode = mode; - + this.vSync = vSync; this.targetFps = targetFps; this.targetUps = targetUps; - + this.timer = new Timer(); this.lock = new Object(); this.next = new Bucket(); } - + public void createWindow() { // Setup an error callback. The default implementation // will print the error message in System.err. @@ -75,45 +75,45 @@ public void createWindow() { throw new IllegalStateException("Unable to initialize GLFW"); // Configure our window - glfwDefaultWindowHints(); // optional, the current window hints are already the default - glfwWindowHint(GLFW_VISIBLE, GL_FALSE); // the window will stay hidden after creation - glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); // the window will be resizable - glfwWindowHint(GLFW_FOCUSED, GL_TRUE); // get focus when shown + glfwDefaultWindowHints(); // optional, the current window hints are already the default + glfwWindowHint(GLFW_VISIBLE, GL_FALSE); // the window will stay hidden after creation + glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); // the window will be resizable + glfwWindowHint(GLFW_FOCUSED, GL_TRUE); // get focus when shown // Get the resolution of the primary monitor this.monitor = glfwGetPrimaryMonitor(); this.vidmode = glfwGetVideoMode(monitor); - + // Create the window this.handle = glfwCreateWindow(this.width, this.height, this.title, NULL, NULL); if (this.handle == NULL) throw new RuntimeException("Failed to create the GLFW window"); - // Setup resize callback - glfwSetFramebufferSizeCallback(this.handle, (window, width, height) -> { + // Setup resize callback + glfwSetFramebufferSizeCallback(this.handle, (window, width, height) -> { if (width > 0 && height > 0) this.next.add("size", () -> { if (this.isWindowed()) this.setSize(width, height); }); - }); + }); // Center our window this.xPos = this.getCenteredXPos(); this.yPos = this.getCenteredYPos(); this.updateWindowMonitor(); } - + public void eventThread() { this.eventLoop(); this.eventDestroy(); this.eventTerminate(); } - + private void eventLoop() { // Make the window visible glfwShowWindow(this.handle); - + while (!glfwWindowShouldClose(this.handle)) { // Only force focus when in borderless fullscreen if (this.isBorderless()) @@ -123,7 +123,7 @@ private void eventLoop() { glfwWaitEvents(); } } - + private void eventDestroy() { synchronized (this.lock) { this.destroyed = true; @@ -133,13 +133,13 @@ private void eventDestroy() { // Release window callbacks glfwFreeCallbacks(this.handle); } - + public void eventTerminate() { // Terminate GLFW and release the error function glfwTerminate(); glfwSetErrorCallback(null).free(); } - + public void renderThread(Loopable loop) { try { this.renderInit(loop); @@ -151,34 +151,34 @@ public void renderThread(Loopable loop) { loop.cleanup(); } } - + private void renderInit(Loopable loop) throws Exception { // This adds the OpenGL context into this function. glfwMakeContextCurrent(this.handle); - + // This line is critical for LWJGL's interoperation with GLFW's // OpenGL context, or any context that is managed externally. // LWJGL detects the context that is current in the current thread, // creates the ContextCapabilities instance and makes the OpenGL // bindings available for use. GL.createCapabilities(); - + // Check vSync glfwSwapInterval(this.isVSync() ? 1 : 0); - + // init loop.init(this); - + // Start timer. this.timer.init(); } - + // Render loop. private void renderLoop(Loopable loop) { while (!this.isDestroyed()) { this.elapsedTime = this.timer.getElapsedTime(); this.accumulatedTime += this.elapsedTime; - + // bucket this.next.run("mode"); this.next.run("size"); @@ -187,17 +187,17 @@ private void renderLoop(Loopable loop) { this.next.run("attrib"); this.next.run("targetFps"); this.next.run("targetUps"); - + // input loop.input(this); - + // update float interval = 1f / this.getTargetUps(); while (this.accumulatedTime >= interval) { loop.update(interval); this.accumulatedTime -= interval; } - + // render loop.render(this); this.renderUpdate(); @@ -205,7 +205,7 @@ private void renderLoop(Loopable loop) { this.renderSync(); } } - + // Sync with target FPS. private void renderSync() { float loopSlot = 1f / this.getTargetFps(); @@ -217,15 +217,15 @@ private void renderSync() { } } } - + private void renderUpdate() { // This can fail if not sync'd. (Can only swap when window exists) synchronized (this.lock) { if (!this.destroyed) - glfwSwapBuffers(this.handle); // swap the color buffers + glfwSwapBuffers(this.handle); // swap the color buffers } } - + protected void updateWindowPos() { int[] xPos = {0}; int[] yPos = {0}; @@ -233,7 +233,7 @@ protected void updateWindowPos() { this.xPos = xPos[0]; this.yPos = yPos[0]; } - + protected void updateWindowMonitor() { this.setAttrib(GLFW_DECORATED, this.isBorderless() ? 0 : 1); glfwSetWindowMonitor( @@ -241,49 +241,49 @@ protected void updateWindowMonitor() { this.getWidth(), this.getHeight(), GLFW_DONT_CARE ); } - + protected void updateSwapInterval(boolean vSync) { glfwSwapInterval(vSync ? 1 : 0); } protected void updateSwapInterval() { this.updateSwapInterval(this.isVSync()); } protected long getCurrentMonitor() { return this.isFullscreen() ? this.monitor : NULL; } - + public void setShouldClose(boolean shouldClose) { glfwSetWindowShouldClose(this.getHandle(), true); } public void clear(int bits) { glClear(bits); } public void clearColor(float r, float g, float b, float a) { glClearColor(r, g, b, a); } - + public GLFWKeyCallback setKeyCallback(GLFWKeyCallbackI keyCallback) { return glfwSetKeyCallback(this.getHandle(), keyCallback); } - public void setAttrib(int attrib, int value) { glfwSetWindowAttrib(this.getHandle(), attrib, value); } - public long getAttrib(int attrib) { return glfwGetWindowAttrib(this.getHandle(), attrib); } + public void setAttrib(int attrib, int value) { glfwSetWindowAttrib(this.getHandle(), attrib, value); } + public long getAttrib(int attrib) { return glfwGetWindowAttrib(this.getHandle(), attrib); } public void setInputMode(int mode, int value) { glfwSetInputMode(this.getHandle(), mode, value); } - public long getInputMode(int mode) { return glfwGetInputMode(this.getHandle(), mode); } - public int getKey(int key) { return glfwGetKey(this.getHandle(), key); } - public boolean isKeyDown(int key) { return (this.getKey(key) == GLFW_PRESS); } - public void postEmptyEvent() { glfwPostEmptyEvent(); } - - public long getHandle() { return this.handle; } - public Object getLock() { return this.lock; } - public boolean isDestroyed() { return this.destroyed; } - public float getElapsedTime() { return this.elapsedTime; } + public long getInputMode(int mode) { return glfwGetInputMode(this.getHandle(), mode); } + public int getKey(int key) { return glfwGetKey(this.getHandle(), key); } + public boolean isKeyDown(int key) { return (this.getKey(key) == GLFW_PRESS); } + public void postEmptyEvent() { glfwPostEmptyEvent(); } + + public long getHandle() { return this.handle; } + public Object getLock() { return this.lock; } + public boolean isDestroyed() { return this.destroyed; } + public float getElapsedTime() { return this.elapsedTime; } public float getAccumulatedTime() { return this.accumulatedTime; } - - public int getTargetFps() { return this.targetFps; } - public int getTargetUps() { return this.targetUps; } + + public int getTargetFps() { return this.targetFps; } + public int getTargetUps() { return this.targetUps; } public void setTargetFps(int targetFps) { this.targetFps = targetFps; } public void setTargetUps(int targetFps) { this.targetUps = targetUps; } - + // width, height - public int getWidth() { return !this.isWindowed() ? this.getScreenWidth() : this.getWindowWidth(); } - public int getHeight() { return !this.isWindowed() ? this.getScreenHeight() : this.getWindowHeight(); } - public int getWindowWidth() { return this.width; } + public int getWidth() { return !this.isWindowed() ? this.getScreenWidth() : this.getWindowWidth(); } + public int getHeight() { return !this.isWindowed() ? this.getScreenHeight() : this.getWindowHeight(); } + public int getWindowWidth() { return this.width; } public int getWindowHeight() { return this.height; } - public int getScreenWidth() { return this.vidmode.width(); } + public int getScreenWidth() { return this.vidmode.width(); } public int getScreenHeight() { return this.vidmode.height(); } // xpos, ypos - public int getXPos() { return !this.isWindowed() ? this.getScreenXPos() : this.getWindowXPos(); } - public int getYPos() { return !this.isWindowed() ? this.getScreenYPos() : this.getWindowYPos(); } - public int getWindowXPos() { return this.xPos; } - public int getWindowYPos() { return this.yPos; } - public int getScreenXPos() { return 0; } - public int getScreenYPos() { return 0; } + public int getXPos() { return !this.isWindowed() ? this.getScreenXPos() : this.getWindowXPos(); } + public int getYPos() { return !this.isWindowed() ? this.getScreenYPos() : this.getWindowYPos(); } + public int getWindowXPos() { return this.xPos; } + public int getWindowYPos() { return this.yPos; } + public int getScreenXPos() { return 0; } + public int getScreenYPos() { return 0; } public int getCenteredXPos() { return !this.isWindowed() ? 0 : (this.getScreenWidth() - this.getWindowWidth()) / 2; } public int getCenteredYPos() { return !this.isWindowed() ? 0 : (this.getScreenHeight() - this.getWindowHeight()) / 2; } // setter @@ -294,9 +294,9 @@ public void setSize(int width, int height) { glViewport(0, 0, this.getWidth(), this.getHeight()); this.updateWindowMonitor(); } - - public int getMode() { return this.mode; } - public boolean isWindowed() { return this.mode == WINDOWED; } + + public int getMode() { return this.mode; } + public boolean isWindowed() { return this.mode == WINDOWED; } public boolean isBorderless() { return this.mode == BORDERLESS; } public boolean isFullscreen() { return this.mode == FULLSCREEN; } public void setMode(int mode) { @@ -309,7 +309,7 @@ public void setMode(int mode) { glViewport(0, 0, this.getWidth(), this.getHeight()); this.updateWindowMonitor(); } - + public boolean isVSync() { return this.vSync; } public void setVSync(boolean vSync) { this.vSync = vSync; diff --git a/src/game/Background.java b/src/game/Background.java index fa0f0ea..6d5879d 100644 --- a/src/game/Background.java +++ b/src/game/Background.java @@ -12,32 +12,32 @@ public class Background implements Loopable { private int direction; private float color; - + public Background() { this.direction = 0; this.color = 0.5f; } - + @Override public void init(Window window) throws Exception { // blank background for first frame window.clearColor(1f, 1f, 1f, 0f); } - + @Override public void input(Window window) { if (window.isKeyDown(GLFW_KEY_L)) this.color = 0f; - + this.direction = 0; if (window.isKeyDown(GLFW_KEY_UP)) this.direction++; if (window.isKeyDown(GLFW_KEY_DOWN)) this.direction--; } - + @Override public void update(float interval) { this.color = Math.max(0f, Math.min(1f, this.color+0.01f*this.direction)); } - + @Override public void render(Window window) { // Different color based on vSync or not (colorful = vSync on) diff --git a/src/game/Game.java b/src/game/Game.java index 48ea0ed..f68b59b 100644 --- a/src/game/Game.java +++ b/src/game/Game.java @@ -17,22 +17,22 @@ public class Game extends Scene { private Mouse mouse; private Camera camera; - + private Background background; private Skybox skybox; private World world; private Hud hud; - + public Game() { super(); - + // inputs this.mouse = new Mouse(); this.camera = new Camera(this.mouse); this .addFrom(this.mouse) .addFrom(this.camera); - + // child scenes this.background = new Background(); this.skybox = new Skybox(this.camera); @@ -44,22 +44,22 @@ public Game() { .addFrom(this.world) .addFrom(this.hud); } - + @Override public void init(Window window) throws Exception { System.out.println("LWJGL version: " + Version.getVersion()); System.out.println("OpenGL version: " + GL11.glGetString(GL11.GL_VERSION)); - + // call child scenes' init super.init(window); - + // Use correct depth checking glEnable(GL_DEPTH_TEST); - + // makes text better glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - + // Setup a key callback. It will be called every time a key is pressed, repeated or released. window.setKeyCallback((handle, key, scancode, action, mods) -> { if (key == GLFW_KEY_F4 && action == GLFW_RELEASE && ((mods & GLFW_MOD_ALT) != 0)) { @@ -115,12 +115,12 @@ else if ((mods & GLFW_MOD_SHIFT) != 0) } }); } - + @Override public void render(Window window) { // clear the framebuffer window.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - + // render scenes super.render(window); } diff --git a/src/game/Hud.java b/src/game/Hud.java index 7feec76..b9fe20f 100644 --- a/src/game/Hud.java +++ b/src/game/Hud.java @@ -13,16 +13,16 @@ public class Hud extends Scene { private static final int FONT_COLS = 16; private static final int FONT_ROWS = 16; private static final String FONT_FILE = "/res/font.png"; - + private Renderer renderer; private Mouse mouse; private Camera camera; private World world; - + private TextItem text; private Item compass; private Item crosshair; - + public Hud(Mouse mouse, Camera camera, World world) { super(); this.addFrom(this.renderer = new Renderer() { @@ -41,26 +41,26 @@ public void cleanup() { this.camera = camera; this.world = world; } - + @Override public void init(Window window) throws Exception { super.init(window); - + this.text = new TextItem("", FONT_FILE, FONT_COLS, FONT_ROWS); this.text.getMesh().setColor(1, 1, 1); - + this.compass = new Item(ObjLoader.loadMesh("/res/compass.obj")); this.compass.getMesh().setColor(1, 1, 1); - + this.crosshair = new Item(ObjLoader.loadMesh("/res/crosshair.obj")); this.crosshair.getMesh().setColor(1, 1, 1); - + this.renderer .addItem(this.text) .addItem(this.compass) .addItem(this.crosshair); } - + @Override public void render(Window window) { this.text.setPosition(10f, window.getHeight() * 0.85f, 0f); @@ -71,14 +71,14 @@ public void render(Window window) { this.world.getChange(), this.world.getWait(), this.camera, this.mouse )); - + this.compass.setPosition(window.getWidth() * 0.95f, window.getWidth() * 0.05f, 0f); this.compass.setRotation(0f, 0f, 180f - this.camera.getRotation().y); this.compass.setScale(window.getWidth() * (1/20f)); - + this.crosshair.setPosition(window.getWidth() * 0.5f, window.getHeight() * 0.5f, 0f); this.crosshair.setScale(window.getWidth() * (1/50f)); - + super.render(window); } } diff --git a/src/game/Skybox.java b/src/game/Skybox.java index 450d6fd..9dc7c61 100644 --- a/src/game/Skybox.java +++ b/src/game/Skybox.java @@ -12,8 +12,8 @@ public class Skybox extends Scene { private Camera camera; private Item skybox; - public Skybox(Camera camera) { - super(); + public Skybox(Camera camera) { + super(); this.addFrom(this.renderer = new Renderer() { Shader shader; public void init(Window window) throws Exception { @@ -27,8 +27,8 @@ public void cleanup() { } }); this.camera = camera; - } - + } + @Override public void init(Window window) throws Exception { Mesh mesh = ObjLoader.loadMesh("/res/skybox.obj"); @@ -37,7 +37,7 @@ public void init(Window window) throws Exception { this.renderer.addItem(this.skybox); super.init(window); } - + @Override public void render(Window window) { this.skybox.setScale(this.camera.getFar() * 0.5f); diff --git a/src/game/World.java b/src/game/World.java index d8e6e6d..2d37dc0 100644 --- a/src/game/World.java +++ b/src/game/World.java @@ -16,17 +16,17 @@ public class World extends Scene { public static final float CHANGE_DELAY = 0.2f; public static final float STEP = 0.1f; - + private final Renderer renderer; private final Mouse mouse; private final Camera camera; - + private final Map blockMap; private final ClosestItem closestItem; private final Vector3f movement; private float step; private int render; - + private String change; // ""=air private float wait; // time until next place / remove @@ -44,28 +44,28 @@ public void cleanup() { destroy(shader); } }); - + this.mouse = mouse; this.camera = camera; - + this.blockMap = new HashMap<>(); this.closestItem = new ClosestItem(); this.movement = new Vector3f(); this.step = STEP; } - + public String getChange() { return this.change; } public float getWait() { return this.wait; } - + public float getStep() { return this.step; } public World setStep(float step) { this.step = step; return this; } - + @Override public void init(Window window) throws Exception { // Create the blocks' mesh this.blockMap.put("grassblock", this.loadBlock("/res/cube.obj", "/res/grassblock.png")); this.blockMap.put("cobbleblock", this.loadBlock("/res/cube.obj", "/res/cobbleblock.png")); - + // get heightmap try (HeightMap map = HeightMap.loadFromImage("/res/heightmap.png")) { // create terrain @@ -79,55 +79,55 @@ public void init(Window window) throws Exception { } } } - + // add spawn markers (-2z is forwards) this.renderer .addItem(this.newBlock("grassblock").setPosition(+1, +1, 0)) .addItem(this.newBlock("grassblock").setPosition(-1, +1, 0)) .addItem(this.newBlock("grassblock").setPosition( 0, +1, +1)) .addItem(this.newBlock("grassblock").setPosition( 0, +1, -2)); - + super.init(window); } public void input(Window window) { super.input(window); - + // movement this.movement.zero(); boolean SPRINTING = (!window.isKeyDown(GLFW_KEY_LEFT_SHIFT) && window.isKeyDown(GLFW_KEY_LEFT_CONTROL)); - + if (window.isKeyDown(GLFW_KEY_W)) this.movement.z--; if (window.isKeyDown(GLFW_KEY_S)) this.movement.z++; if (window.isKeyDown(GLFW_KEY_A)) this.movement.x--; if (window.isKeyDown(GLFW_KEY_D)) this.movement.x++; - + if (window.isKeyDown(GLFW_KEY_LEFT_SHIFT)) this.movement.y--; if (window.isKeyDown(GLFW_KEY_SPACE)) this.movement.y++; - + if (this.movement.length() > 1f) this.movement.div(this.movement.length()); if (SPRINTING && this.movement.z < 0) this.movement.mul(1.5f); - + // render distance (camera) this.render = 0; if (window.isKeyDown(GLFW_KEY_L)) this.camera.setFar(Camera.FAR); if (window.isKeyDown(GLFW_KEY_RIGHT_BRACKET)) this.render++; if (window.isKeyDown(GLFW_KEY_LEFT_BRACKET)) this.render--; - + // placing / removing this.change = null; if (window.isKeyDown(GLFW_KEY_0)) this.change = ""; if (window.isKeyDown(GLFW_KEY_1)) this.change = "grassblock"; if (window.isKeyDown(GLFW_KEY_2)) this.change = "cobbleblock"; } - + public void update(float interval) { // movement this.camera.movePosition(this.movement.mul(this.step, new Vector3f())); - + // render distance this.camera.setFar(Math.max(Camera.NEAR+0.01f, this.camera.getFar() + 0.1f*this.render)); - + // placing / removing if (this.change != null && this.wait <= 0) { this.closestItem.update(this.renderer.items, this.camera); @@ -152,21 +152,21 @@ public void update(float interval) { } this.wait += this.CHANGE_DELAY; } - + // update wait time if (this.wait > 0) this.wait -= interval; if (this.wait < 0 && this.change == null) this.wait = 0; - + super.update(interval); } - + public void render(Window window) { this.updateSelectedItem(); super.render(window); } - + private void updateSelectedItem() { for (Item item : this.renderer.items) item.setSelected(false); @@ -174,7 +174,7 @@ private void updateSelectedItem() { if (this.closestItem.closest != null) this.closestItem.closest.setSelected(true); } - + private static Item loadBlock(String objFileName, String textureFileName) throws Exception { Mesh mesh = ObjLoader.loadMesh(objFileName); mesh.setTexture(new Texture(textureFileName)); @@ -182,7 +182,7 @@ private static Item loadBlock(String objFileName, String textureFileName) throws block.setScale(0.5f); return block; } - + private Item newBlock(String name) { return this.blockMap.get(name).clone(); } From c6b545a1b2b3017655ec2b88a8d22e96ee750c83 Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Mon, 22 Jun 2020 17:42:36 -0400 Subject: [PATCH 06/52] Add interval usage (allow different UPS) Remove Mouse.movement's division by UPS and elapsed time Add Camera.movePosition(Vector3f, float) to replace multiplying with a temporary vector Decrease sensitivity to original value --- src/engine/Camera.java | 3 ++- src/engine/Mouse.java | 1 - src/game/Background.java | 2 +- src/game/World.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/engine/Camera.java b/src/engine/Camera.java index 719c14d..ddf5fc7 100644 --- a/src/engine/Camera.java +++ b/src/engine/Camera.java @@ -13,7 +13,7 @@ public class Camera implements Inputtable { public static final float FOV = 80f; public static final float NEAR = 0.01f; public static final float FAR = 50f; - public static final float SENSITIVITY = 0.75f; + public static final float SENSITIVITY = 0.3f; private Mouse mouse; @@ -70,6 +70,7 @@ public void input(Window window) { public Camera setSensitivity(float sensitivity) { this.sensitivity = sensitivity; return this; } public Camera movePosition(Vector3f position) { return this.movePosition(position.x, position.y, position.z); } + public Camera movePosition(Vector3f position, float step) { return this.movePosition(position.x*step, position.y*step, position.z*step); } public Camera movePosition(float x, float y, float z) { // TODO optimize this (using functions inside Vector3f) if (z != 0) { diff --git a/src/engine/Mouse.java b/src/engine/Mouse.java index a18d27e..217fb28 100644 --- a/src/engine/Mouse.java +++ b/src/engine/Mouse.java @@ -45,7 +45,6 @@ public void init(Window window) { public void input(Window window) { this.current.sub(this.previous, this.movement); - this.movement.div(window.getTargetUps() * window.getElapsedTime()); this.previous.set(this.current); } diff --git a/src/game/Background.java b/src/game/Background.java index 6d5879d..a95bed5 100644 --- a/src/game/Background.java +++ b/src/game/Background.java @@ -35,7 +35,7 @@ public void input(Window window) { @Override public void update(float interval) { - this.color = Math.max(0f, Math.min(1f, this.color+0.01f*this.direction)); + this.color = Math.max(0f, Math.min(1f, this.color+30*interval*0.01f*this.direction)); } @Override diff --git a/src/game/World.java b/src/game/World.java index 2d37dc0..9985684 100644 --- a/src/game/World.java +++ b/src/game/World.java @@ -123,7 +123,7 @@ public void input(Window window) { public void update(float interval) { // movement - this.camera.movePosition(this.movement.mul(this.step, new Vector3f())); + this.camera.movePosition(this.movement, 30*interval * this.step); // render distance this.camera.setFar(Math.max(Camera.NEAR+0.01f, this.camera.getFar() + 0.1f*this.render)); From 3271c3f7fda99df3d7d58c9b659998e2c7e8f984 Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Thu, 25 Jun 2020 22:44:21 -0400 Subject: [PATCH 07/52] Use Loopable interface instead of Scene class --- src/Main.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Main.java b/src/Main.java index 5c88667..8bcefd4 100644 --- a/src/Main.java +++ b/src/Main.java @@ -17,8 +17,8 @@ public static void main(String[] args) { int targetFps = 10; int targetUps = 30; Window window = new Window("Hello World!", 300, 300, mode, vSync, targetFps, targetUps); - Scene scene = new Game(); - Engine engine = new Engine(window, scene); + Loopable loop = new Game(); + Engine engine = new Engine(window, loop); engine.run(); } catch (Exception e) { e.printStackTrace(); From c27162e3bd344f7e542795b150697b8d205f5623 Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Fri, 26 Jun 2020 06:00:22 -0400 Subject: [PATCH 08/52] Split TextItem's font mesh creation into own Texture subclass Add FontTexture (and use it in TextItem) --- src/engine/FontTexture.java | 103 +++++++++++++++++++++++++++++++++ src/engine/TextItem.java | 112 +++++++----------------------------- src/game/Hud.java | 2 +- 3 files changed, 124 insertions(+), 93 deletions(-) create mode 100644 src/engine/FontTexture.java diff --git a/src/engine/FontTexture.java b/src/engine/FontTexture.java new file mode 100644 index 0000000..71b2dd8 --- /dev/null +++ b/src/engine/FontTexture.java @@ -0,0 +1,103 @@ +/* +George Zhang +Font texture subclass. +*/ + +package geetransit.minecraft05.engine; + +import java.util.*; +import java.nio.charset.Charset; + +public class FontTexture extends Texture { + public static final float ZPOS = 0f; + public static final int VERTICES_PER_QUAD = 4; + + private final int cols; + private final int rows; + + public FontTexture(String fileName, int cols, int rows) throws Exception { + super(fileName); + this.cols = cols; + this.rows = rows; + } + + public int getCols() { return this.cols; } + public int getRows() { return this.rows; } + + public Mesh buildMesh(String text) { + byte[] charArray = text.getBytes(Charset.forName("ISO-8859-1")); + + List posList = new ArrayList<>(); + List coordList = new ArrayList<>(); + List indexList = new ArrayList<>(); + + int fontCols = this.getCols(); + int fontRows = this.getRows(); + float charWidth = (float) this.getWidth() / fontCols; + float charLength = (float) this.getLength() / fontRows; + + int currentCol = 0; + int currentRow = 0; + int currentIndex = 0; + for (int i = 0; i < charArray.length; i++) { + byte currentChar = charArray[i]; + if (currentChar == '\n') { + currentRow++; + currentCol = 0; + continue; + } + + // Build a character tile composed by two triangles + // 0 2 + // 1 3 + int fontCol = currentChar % fontCols; + int fontRow = currentChar / fontCols; + + // Left Top vertex + posList.add((currentCol + 0)*charWidth); // x + posList.add((currentRow + 0)*charLength); // y + posList.add(ZPOS); // z + coordList.add((float) (fontCol + 0) / fontCols); + coordList.add((float) (fontRow + 0) / fontRows); + + // Left Bottom vertex + posList.add((currentCol + 0)*charWidth); // x + posList.add((currentRow + 1)*charLength); // y + posList.add(ZPOS); // z + coordList.add((float) (fontCol + 0) / fontCols); + coordList.add((float) (fontRow + 1) / fontRows); + + // Right Top vertex + posList.add((currentCol + 1)*charWidth); // x + posList.add((currentRow + 0)*charLength); // y + posList.add(ZPOS); // z + coordList.add((float) (fontCol + 1) / fontCols); + coordList.add((float) (fontRow + 0) / fontRows); + + // Right Bottom vertex + posList.add((currentCol + 1)*charWidth); // x + posList.add((currentRow + 1)*charLength); // y + posList.add(ZPOS); // z + coordList.add((float) (fontCol + 1) / fontCols); + coordList.add((float) (fontRow + 1) / fontRows); + + // Add indices for triangles (counter-clockwise) + // 0 0 2 + // 1 3 3 + indexList.add(currentIndex*VERTICES_PER_QUAD + 0); + indexList.add(currentIndex*VERTICES_PER_QUAD + 1); + indexList.add(currentIndex*VERTICES_PER_QUAD + 3); + indexList.add(currentIndex*VERTICES_PER_QUAD + 0); + indexList.add(currentIndex*VERTICES_PER_QUAD + 3); + indexList.add(currentIndex*VERTICES_PER_QUAD + 2); + + currentCol++; + currentIndex++; + } + + float[] posArray = Utils.floatListToArray(posList); + float[] coordArray = Utils.floatListToArray(coordList); + int[] indexArray = Utils.intListToArray(indexList); + return new Mesh(posArray, indexArray, coordArray).setTexture(this); + } +} diff --git a/src/engine/TextItem.java b/src/engine/TextItem.java index 0cc9e9c..8e61c6f 100644 --- a/src/engine/TextItem.java +++ b/src/engine/TextItem.java @@ -1,112 +1,40 @@ /* ahbejarano -Game item wrapper class. +Text item class. */ package geetransit.minecraft05.engine; -import java.util.*; -import java.nio.charset.Charset; +import org.joml.Vector3f; import org.joml.Vector4f; +import org.joml.Quaternionf; public class TextItem extends Item { - private static final float ZPOS = 0f; - private static final int VERTICES_PER_QUAD = 4; - private String text; - private final int fontCols; - private final int fontRows; + private final FontTexture fontTexture; - public TextItem(String text, String fontFile, int fontCols, int fontRows) throws Exception { - super(); + public TextItem(String text, FontTexture fontTexture) throws Exception { + super(fontTexture.buildMesh(text)); this.text = text; - this.fontCols = fontCols; - this.fontRows = fontRows; - this.mesh = this.buildMesh(new Texture(fontFile)); - + this.fontTexture = fontTexture; } + protected TextItem setMesh(Mesh mesh) { super.setMesh(mesh); return this; } + public TextItem setPosition(Vector3f position) { super.setPosition(position); return this; } + public TextItem setPosition(float x, float y, float z) { super.setPosition(x, y, z); return this; } + public TextItem setRotation(Quaternionf rotation) { super.setRotation(rotation); return this; } + public TextItem setRotation(float x, float y, float z) { super.setRotation(x, y, z); return this; } + public TextItem setScale(float scale) { super.setScale(scale); return this; } + public String getText() { return this.text; } - public int getFontCols() { return this.fontCols; } - public int getFontRows() { return this.fontRows; } + public FontTexture getFontTexture() { return this.fontTexture; } - public Item setText(String text) { + public TextItem setText(String text) { this.text = text; - Vector4f color = this.mesh.getColor(); - this.mesh.cleanup(false); - this.mesh = this.buildMesh(this.mesh.getTexture()); - this.mesh.setColor(color); + Vector4f color = this.getMesh().getColor(); + this.getMesh().cleanup(false); + this.setMesh(this.fontTexture.buildMesh(this.text)); + this.getMesh().setColor(color); return this; } - - private Mesh buildMesh(Texture texture) { - byte[] charArray = this.text.getBytes(Charset.forName("ISO-8859-1")); - - List posList = new ArrayList<>(); - List coordList = new ArrayList<>(); - List indexList = new ArrayList<>(); - - float width = (float) texture.getWidth() / this.fontCols; - float length = (float) texture.getLength() / this.fontRows; - - int currentCol = 0; - int currentRow = 0; - int currentIndex = 0; - for (int i = 0; i < charArray.length; i++) { - byte currentChar = charArray[i]; - if (currentChar == '\n') { - currentRow++; - currentCol = 0; - continue; - } - - // Build a character tile composed by two triangles - int fontCol = currentChar % this.fontCols; - int fontRow = currentChar / this.fontCols; - - // Left Top vertex - posList.add(currentCol*width); // x - posList.add(currentRow*length); // y - posList.add(ZPOS); // z - coordList.add((float) fontCol / this.fontCols); - coordList.add((float) fontRow / this.fontRows); - indexList.add(currentIndex*VERTICES_PER_QUAD + 0); - - // Left Bottom vertex - posList.add(currentCol*width); // x - posList.add(currentRow*length + length); // y - posList.add(ZPOS); // z - coordList.add((float) fontCol / this.fontCols); - coordList.add((float) (fontRow + 1) / this.fontRows); - indexList.add(currentIndex*VERTICES_PER_QUAD + 1); - - // Right Bottom vertex - posList.add(currentCol*width + width); // x - posList.add(currentRow*length + length); // y - posList.add(ZPOS); // z - coordList.add((float) (fontCol + 1) / this.fontCols); - coordList.add((float) (fontRow + 1) / this.fontRows); - indexList.add(currentIndex*VERTICES_PER_QUAD + 2); - - // Right Top vertex - posList.add(currentCol*width + width); // x - posList.add(currentRow*length); // y - posList.add(ZPOS); // z - coordList.add((float) (fontCol + 1) / this.fontCols); - coordList.add((float) fontRow / this.fontRows); - indexList.add(currentIndex*VERTICES_PER_QUAD + 3); - - // Add indices for left top and bottom right vertices - indexList.add(currentIndex*VERTICES_PER_QUAD + 0); - indexList.add(currentIndex*VERTICES_PER_QUAD + 2); - - currentCol++; - currentIndex++; - } - - float[] posArray = Utils.floatListToArray(posList); - float[] coordArray = Utils.floatListToArray(coordList); - int[] indexArray = Utils.intListToArray(indexList); - return new Mesh(posArray, indexArray, coordArray).setTexture(texture); - } } diff --git a/src/game/Hud.java b/src/game/Hud.java index b9fe20f..6bd3753 100644 --- a/src/game/Hud.java +++ b/src/game/Hud.java @@ -46,7 +46,7 @@ public void cleanup() { public void init(Window window) throws Exception { super.init(window); - this.text = new TextItem("", FONT_FILE, FONT_COLS, FONT_ROWS); + this.text = new TextItem("", new FontTexture(FONT_FILE, FONT_COLS, FONT_ROWS)); this.text.getMesh().setColor(1, 1, 1); this.compass = new Item(ObjLoader.loadMesh("/res/compass.obj")); From 5b066933a12521b0a7d7f0c7cdc1b53abb50c79c Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Fri, 26 Jun 2020 16:37:04 -0400 Subject: [PATCH 09/52] Add ClosestItem generics --- src/engine/ClosestItem.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/engine/ClosestItem.java b/src/engine/ClosestItem.java index cbe2433..9b67d16 100644 --- a/src/engine/ClosestItem.java +++ b/src/engine/ClosestItem.java @@ -11,9 +11,9 @@ import org.joml.Vector3f; import org.joml.Intersectionf; -public class ClosestItem { +public class ClosestItem { public float distance; // distance from camera - public Item closest; + public T closest; public Vector3f hit; // position of intersection public Vector3f direction; // points away from camera @@ -29,29 +29,28 @@ public ClosestItem() { this.max = new Vector3f(); this.nearFar = new Vector2f(); } - public ClosestItem(List items, Camera camera) { + public ClosestItem(List items, Camera camera) { this(); this.update(items, camera); } - public ClosestItem reset() { + public ClosestItem reset() { this.distance = Float.POSITIVE_INFINITY; this.closest = null; return this; } - public ClosestItem update(List items, Camera camera) { + public void update(List items, Camera camera) { this.reset().extend(items, camera); - return this; } - public ClosestItem extend(List items, Camera camera) { + public ClosestItem extend(List items, Camera camera) { // get camera direction camera.getViewMatrix().positiveZ(this.direction); this.direction.negate().normalize(); // loop through all items - for (Item item : items) { + for (T item : items) { this.min.set(item.getPosition()); this.max.set(item.getPosition()); this.min.add(-item.getScale(), -item.getScale(), -item.getScale()); From 4a3249e47c017a91fb2aa031ce7345e981d39ff9 Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Fri, 26 Jun 2020 16:41:07 -0400 Subject: [PATCH 10/52] Remove interface implementations Remove Init, Input, Render, Update --- src/engine/Init.java | 33 --------------------------------- src/engine/Input.java | 27 --------------------------- src/engine/Render.java | 27 --------------------------- src/engine/Update.java | 27 --------------------------- 4 files changed, 114 deletions(-) delete mode 100644 src/engine/Init.java delete mode 100644 src/engine/Input.java delete mode 100644 src/engine/Render.java delete mode 100644 src/engine/Update.java diff --git a/src/engine/Init.java b/src/engine/Init.java deleted file mode 100644 index eb1cd3a..0000000 --- a/src/engine/Init.java +++ /dev/null @@ -1,33 +0,0 @@ -/* -George Zhang -Init class -*/ - -package geetransit.minecraft05.engine; - -import java.util.List; -import java.util.ArrayList; - -public class Init implements Initializable { - private List inits; - - public Init(List inits) { - this.inits = inits; - } - public Init() { this(new ArrayList<>()); } - - public List getInits() { return this.inits; } - public Init addInit(Initializable init) { this.inits.add(init); return this; } - - @Override - public void init(Window window) throws Exception { - for (Initializable init : this.getInits()) - init.init(window); - } - - @Override - public void cleanup() { - for (Initializable init : this.getInits()) - init.cleanup(); - } -} diff --git a/src/engine/Input.java b/src/engine/Input.java deleted file mode 100644 index 552aba1..0000000 --- a/src/engine/Input.java +++ /dev/null @@ -1,27 +0,0 @@ -/* -George Zhang -Input class -*/ - -package geetransit.minecraft05.engine; - -import java.util.List; -import java.util.ArrayList; - -public class Input implements Inputtable { - private List inputs; - - public Input(List inputs) { - this.inputs = inputs; - } - public Input() { this(new ArrayList<>()); } - - public List getInputs() { return this.inputs; } - public Input addInput(Inputtable input) { this.inputs.add(input); return this; } - - @Override - public void input(Window window) { - for (Inputtable input : this.getInputs()) - input.input(window); - } -} diff --git a/src/engine/Render.java b/src/engine/Render.java deleted file mode 100644 index 9029e5a..0000000 --- a/src/engine/Render.java +++ /dev/null @@ -1,27 +0,0 @@ -/* -George Zhang -Render class -*/ - -package geetransit.minecraft05.engine; - -import java.util.List; -import java.util.ArrayList; - -public class Render implements Renderable { - private List renders; - - public Render(List renders) { - this.renders = renders; - } - public Render() { this(new ArrayList<>()); } - - public List getRenders() { return this.renders; } - public Render addRender(Renderable render) { this.renders.add(render); return this; } - - @Override - public void render(Window window) { - for (Renderable render : this.getRenders()) - render.render(window); - } -} diff --git a/src/engine/Update.java b/src/engine/Update.java deleted file mode 100644 index 1b42519..0000000 --- a/src/engine/Update.java +++ /dev/null @@ -1,27 +0,0 @@ -/* -George Zhang -Update class -*/ - -package geetransit.minecraft05.engine; - -import java.util.List; -import java.util.ArrayList; - -public class Update implements Updateable { - private List updates; - - public Update(List updates) { - this.updates = updates; - } - public Update() { this(new ArrayList<>()); } - - public List getUpdates() { return this.updates; } - public Update addUpdate(Updateable update) { this.updates.add(update); return this; } - - @Override - public void update(float interval) { - for (Updateable update : this.getUpdates()) - update.update(interval); - } -} From 894edfc7eea9e79b38a6256ba5bdd821f4cddf7d Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Sat, 27 Jun 2020 00:57:49 -0400 Subject: [PATCH 11/52] Add default implementations to Loopable --- src/engine/Loopable.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/engine/Loopable.java b/src/engine/Loopable.java index 3c7da87..111da71 100644 --- a/src/engine/Loopable.java +++ b/src/engine/Loopable.java @@ -6,4 +6,15 @@ package geetransit.minecraft05.engine; public interface Loopable extends Initializable, Inputtable, Updateable, Renderable { + @Override + default void init(Window window) throws Exception {} + + @Override + default void input(Window window) {} + + @Override + default void update(float interval) {} + + @Override + default void render(Window window) {} } From b7d4fcbf8ca4a2a532fbf2a4a54dfcaa61c4854a Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Sat, 27 Jun 2020 01:01:29 -0400 Subject: [PATCH 12/52] Remove target UPS Call Loopable.update with elapsedTime Remove accumulatedTime --- src/Main.java | 3 +-- src/engine/Window.java | 20 +++----------------- 2 files changed, 4 insertions(+), 19 deletions(-) diff --git a/src/Main.java b/src/Main.java index 8bcefd4..9b2f6d8 100644 --- a/src/Main.java +++ b/src/Main.java @@ -15,8 +15,7 @@ public static void main(String[] args) { int mode = Window.WINDOWED; boolean vSync = true; int targetFps = 10; - int targetUps = 30; - Window window = new Window("Hello World!", 300, 300, mode, vSync, targetFps, targetUps); + Window window = new Window("Hello World!", 300, 300, mode, vSync, targetFps); Loopable loop = new Game(); Engine engine = new Engine(window, loop); engine.run(); diff --git a/src/engine/Window.java b/src/engine/Window.java index a92d5a4..11d97db 100644 --- a/src/engine/Window.java +++ b/src/engine/Window.java @@ -32,10 +32,7 @@ public class Window { private boolean vSync; private int targetFps; - private int targetUps; - private float elapsedTime; - private float accumulatedTime; private boolean destroyed = false; @@ -48,8 +45,7 @@ public Window( int height, int mode, boolean vSync, - int targetFps, - int targetUps + int targetFps ) { this.title = title; this.width = width; @@ -58,7 +54,6 @@ public Window( this.vSync = vSync; this.targetFps = targetFps; - this.targetUps = targetUps; this.timer = new Timer(); this.lock = new Object(); @@ -177,7 +172,6 @@ private void renderInit(Loopable loop) throws Exception { private void renderLoop(Loopable loop) { while (!this.isDestroyed()) { this.elapsedTime = this.timer.getElapsedTime(); - this.accumulatedTime += this.elapsedTime; // bucket this.next.run("mode"); @@ -186,17 +180,12 @@ private void renderLoop(Loopable loop) { this.next.run("inputMode"); this.next.run("attrib"); this.next.run("targetFps"); - this.next.run("targetUps"); // input loop.input(this); // update - float interval = 1f / this.getTargetUps(); - while (this.accumulatedTime >= interval) { - loop.update(interval); - this.accumulatedTime -= interval; - } + loop.update(this.elapsedTime); // render loop.render(this); @@ -262,13 +251,10 @@ protected void updateWindowMonitor() { public long getHandle() { return this.handle; } public Object getLock() { return this.lock; } public boolean isDestroyed() { return this.destroyed; } - public float getElapsedTime() { return this.elapsedTime; } - public float getAccumulatedTime() { return this.accumulatedTime; } + public float getElapsedTime() { return this.elapsedTime; } public int getTargetFps() { return this.targetFps; } - public int getTargetUps() { return this.targetUps; } public void setTargetFps(int targetFps) { this.targetFps = targetFps; } - public void setTargetUps(int targetFps) { this.targetUps = targetUps; } // width, height public int getWidth() { return !this.isWindowed() ? this.getScreenWidth() : this.getWindowWidth(); } From 90de8ac28baf612c1ca0850152a80b81e4d12015 Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Sat, 27 Jun 2020 01:05:45 -0400 Subject: [PATCH 13/52] Another refactor Render changes - Move render* to Mesh.renderList and renderItem - Remove Renderer - Make Hud, Skybox, and World implement Loopable (instead of extending Scene) Transformation changes - Move getProjectionMatrix and getOrthoProjectionMatrix to Window (with build/get) - Move getModelMatrix, getModelViewMatrix, and [new]getOrthoProjModelMatrix to Item (with Matrix4f argument) - Remove Transformation - Update Camera.getViewMatrix to build/get Item changes - Split Item.isSelected into own BlockItem subclass - Split fragment-3d.fs[isSelected] into fragment-3d-block.fs - Add overriden fluent methods (method chaining) Texture changes - Make raw constructor protected --- res/fragment-3d-block.fs | 20 ++++ res/fragment-3d.fs | 4 - src/Main.java | 5 +- src/engine/Camera.java | 3 +- src/engine/Item.java | 45 ++++---- src/engine/Mesh.java | 66 ++++++++--- src/engine/Renderer.java | 193 --------------------------------- src/engine/Texture.java | 60 +++++----- src/engine/Transformation.java | 59 ---------- src/engine/Window.java | 20 ++++ src/game/BlockItem.java | 29 +++++ src/game/Hud.java | 95 +++++++++++----- src/game/Skybox.java | 65 +++++++---- src/game/World.java | 158 +++++++++++++++++---------- 14 files changed, 386 insertions(+), 436 deletions(-) create mode 100644 res/fragment-3d-block.fs delete mode 100644 src/engine/Renderer.java delete mode 100644 src/engine/Transformation.java create mode 100644 src/game/BlockItem.java diff --git a/res/fragment-3d-block.fs b/res/fragment-3d-block.fs new file mode 100644 index 0000000..92d0c79 --- /dev/null +++ b/res/fragment-3d-block.fs @@ -0,0 +1,20 @@ +#version 130 + +in vec2 outCoord; +out vec4 fragColor; + +uniform sampler2D texture_sampler; +uniform vec4 color; +uniform int isTextured; +uniform int isSelected; + +void main() { + if (isTextured > 0) { + fragColor = texture(texture_sampler, outCoord); + } else { + fragColor = color; + } + if (isSelected > 0) { + fragColor = vec4(fragColor.x, fragColor.y, 1, 1); + } +} diff --git a/res/fragment-3d.fs b/res/fragment-3d.fs index 77d71d1..faf3bbc 100644 --- a/res/fragment-3d.fs +++ b/res/fragment-3d.fs @@ -6,7 +6,6 @@ out vec4 fragColor; uniform sampler2D texture_sampler; uniform vec4 color; uniform int isTextured; -uniform int isSelected; void main() { if (isTextured > 0) { @@ -14,7 +13,4 @@ void main() { } else { fragColor = color; } - if (isSelected > 0) { - fragColor = vec4(fragColor.x, fragColor.y, 11, 1); - } } diff --git a/src/Main.java b/src/Main.java index 9b2f6d8..f0ead8d 100644 --- a/src/Main.java +++ b/src/Main.java @@ -12,10 +12,7 @@ public class Main { public static void main(String[] args) { try { - int mode = Window.WINDOWED; - boolean vSync = true; - int targetFps = 10; - Window window = new Window("Hello World!", 300, 300, mode, vSync, targetFps); + Window window = new Window("Hello World!", 300, 300, Window.WINDOWED, /*vSync*/ true, /*targetFps*/ 10); Loopable loop = new Game(); Engine engine = new Engine(window, loop); engine.run(); diff --git a/src/engine/Camera.java b/src/engine/Camera.java index ddf5fc7..789dc59 100644 --- a/src/engine/Camera.java +++ b/src/engine/Camera.java @@ -114,7 +114,8 @@ public Vector3f getNegativePosition() { return this.position.negate(this.negativePosition); } - public Matrix4f getViewMatrix() { + public Matrix4f getViewMatrix() { return this.viewMatrix; } + public Matrix4f buildViewMatrix() { return this.viewMatrix .identity() .rotateXYZ(this.getRadiansRotation()) diff --git a/src/engine/Item.java b/src/engine/Item.java index 7725a95..d2e78f8 100644 --- a/src/engine/Item.java +++ b/src/engine/Item.java @@ -1,20 +1,19 @@ /* ahbejarano -Game item wrapper class. +Game item class. */ package geetransit.minecraft05.engine; import org.joml.Vector3f; import org.joml.Quaternionf; +import org.joml.Matrix4f; -public class Item implements Cloneable { - protected Mesh mesh; - +public class Item { + private Mesh mesh; private final Vector3f position; private final Quaternionf rotation; // Degrees, not radians. private float scale; - private boolean selected; public Item(Mesh mesh) { this(); @@ -24,20 +23,10 @@ protected Item() { this.position = new Vector3f(); this.rotation = new Quaternionf(); this.scale = 1; - this.selected = false; - } - - // does NOT copy the mesh (shallow copy) - @Override - public Item clone() { - return new Item(this.getMesh()) - .setPosition(this.getPosition()) - .setRotation(this.getRotation()) - .setScale(this.getScale()) - .setSelected(this.isSelected()); } public Mesh getMesh() { return this.mesh; } + protected Item setMesh(Mesh mesh) { this.mesh = mesh; return this; } public Vector3f getPosition() { return this.position; } public Item setPosition(Vector3f position) { this.position.set(position); return this; } @@ -63,9 +52,25 @@ public Item setScale(float scale) { return this; } - public boolean isSelected() { return this.selected; } - public Item setSelected(boolean selected) { - this.selected = selected; - return this; + public Matrix4f buildModelMatrix(Matrix4f result) { + return result.translationRotateScale(this.position, this.rotation, this.scale); + } + + public Matrix4f buildModelViewMatrix(Matrix4f viewMatrix, Matrix4f result) { + return viewMatrix.mulAffine(this.buildModelMatrix(result), result); + } + + // these 2 are different? + public Matrix4f newBuildOrthoProjModelMatrix(Matrix4f orthoMatrix, Matrix4f result) { + return orthoMatrix.mulOrthoAffine(this.buildModelMatrix(result), result); + } + public Matrix4f buildOrthoProjModelMatrix(Matrix4f orthoMatrix, Matrix4f result) { + return result + .set(orthoMatrix) + .translate(this.position) + .rotateX((float) Math.toRadians(-this.rotation.x)) + .rotateY((float) Math.toRadians(-this.rotation.y)) + .rotateZ((float) Math.toRadians(-this.rotation.z)) + .scale(this.scale); } } diff --git a/src/engine/Mesh.java b/src/engine/Mesh.java index 23d18e1..65680f8 100644 --- a/src/engine/Mesh.java +++ b/src/engine/Mesh.java @@ -6,6 +6,7 @@ package geetransit.minecraft05.engine; import java.util.*; +import java.util.function.*; import java.nio.*; import org.joml.*; import static org.lwjgl.opengl.GL30.*; @@ -84,14 +85,46 @@ public Mesh(float[] posArray, int[] indexArray, float[] coordArray) { public boolean isTextured() { return this.texture != null; } public Vector4f getColor() { return this.color; } - public Mesh setColor(float r, float g, float b) { this.setColor(new Vector3f(r, g, b)); return this; } - public Mesh setColor(float r, float g, float b, float a) { this.setColor(new Vector4f(r, g, b, a)); return this; } - public Mesh setColor(Vector3f color) { this.setColor(new Vector4f(color, 1f)); return this; } - public Mesh setColor(Vector4f color) { this.color.set(color); return this; } + public Mesh setColor(Vector3f color) { return this.setColor(color.x, color.y, color.z, 1f); } + public Mesh setColor(Vector4f color) { return this.setColor(color.x, color.y, color.z, color.w); } + public Mesh setColor(float r, float g, float b) { return this.setColor(r, g, b, 1f); } + public Mesh setColor(float r, float g, float b, float a) { this.color.set(r, g, b, a); return this; } + + public void renderList(List items, Shader shader, BiConsumer consumer) { + this.with(shader, () -> { + for (T item : items) { + consumer.accept(item, shader); + this.draw(); + } + }); + } + + public void renderItem(T item, Shader shader, BiConsumer consumer) { + this.with(shader, () -> { + consumer.accept(item, shader); + this.draw(); + }); + } + + protected void with(Shader shader, Runnable runnable) { + this.prepare(); + this.setup(shader); + runnable.run(); + this.restore(); + } + + public void cleanup() { this.cleanup(true); } + public void cleanup(boolean cleanupTexture) { + this.disableVao(); + this.deleteVbos(); + if (cleanupTexture && this.isTextured()) + this.texture.cleanup(); + this.deleteVao(); + } // prepare mesh - public void prepare() { this.prepare(null); } - public void prepare(Mesh lastMesh) { + protected void prepare() { this.prepare(null); } + protected void prepare(Mesh lastMesh) { if (this == lastMesh) return; if (this.isTextured()) @@ -99,14 +132,20 @@ public void prepare(Mesh lastMesh) { glBindVertexArray(this.vaoId); } + // setup uniforms + protected void setup(Shader shader) { + shader.setUniform("color", this.color); + shader.setUniform("isTextured", this.isTextured()); + } + // draw elements - public void render() { + protected void draw() { glDrawElements(GL_TRIANGLES, this.vertexCount, GL_UNSIGNED_INT, 0); } // Restore state - public void restore() { this.restore(null); } - public void restore(Mesh nextMesh) { + protected void restore() { this.restore(null); } + protected void restore(Mesh nextMesh) { if (this == nextMesh) return; glBindVertexArray(0); @@ -128,13 +167,4 @@ protected void deleteVao() { glBindVertexArray(0); glDeleteVertexArrays(this.vaoId); } - - public void cleanup() { this.cleanup(true); } - public void cleanup(boolean cleanupTexture) { - this.disableVao(); - this.deleteVbos(); - if (cleanupTexture && this.isTextured()) - this.texture.cleanup(); - this.deleteVao(); - } } diff --git a/src/engine/Renderer.java b/src/engine/Renderer.java deleted file mode 100644 index 6401577..0000000 --- a/src/engine/Renderer.java +++ /dev/null @@ -1,193 +0,0 @@ -/* -ahbejarano -Renderer abstract helper class. -*/ - -package geetransit.minecraft05.engine; - -import java.util.*; -import org.joml.Matrix4f; -import static org.lwjgl.opengl.GL30.*; - -public abstract class Renderer implements Initializable, Renderable { - public final Map> map; - public final List items; - public final Transformation transformation; - - public Renderer() { - this.map = new HashMap<>(); - this.items = new ArrayList<>(); - this.transformation = new Transformation(); - } - - // create shaders: shader = create?(VERTEX_SHADER, FRAGMENT_SHADER); - public abstract void init(Window window) throws Exception; - - // render scene: render?(shader, window, ?); - public abstract void render(Window window); - - // destroy shaders: shader.cleanup(); - public abstract void cleanup(); - - public Renderer addItem(Item item) { - Mesh mesh = item.getMesh(); - this.items.add(item); - if (!this.map.containsKey(mesh)) - this.map.put(mesh, new ArrayList<>()); - this.map.get(mesh).add(item); - return this; - } - - public Renderer removeItem(Item item) { - Mesh mesh = item.getMesh(); - this.items.remove(item); - this.map.get(mesh).remove(item); - if (this.map.get(mesh).size() == 0) - this.map.remove(mesh); - return this; - } - - // shader creators - public Shader createShader(String vertex, String fragment) throws Exception { - Shader shader = new Shader(); - shader.createVertexShader(Utils.loadResource(vertex)); - shader.createFragmentShader(Utils.loadResource(fragment)); - shader.link(); - return shader; - } - - public Shader create3D(String vertex, String fragment) throws Exception { - Shader shader = this.createShader(vertex, fragment); - shader.createUniform("projectionMatrix"); - shader.createUniform("modelViewMatrix"); - shader.createUniform("texture_sampler"); - shader.createUniform("color"); - shader.createUniform("isTextured"); - shader.createUniform("isSelected"); - return shader; - } - - public Shader create2D(String vertex, String fragment) throws Exception { - Shader shader = this.createShader(vertex, fragment); - shader.createUniform("projModelMatrix"); - shader.createUniform("texture_sampler"); - shader.createUniform("color"); - shader.createUniform("isTextured"); - return shader; - } - - // note does NOT call Item.render(Window) - public void render3D(Shader shader, Window window, Camera camera) { - shader.bind(); - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - - // projection - Matrix4f projectionMatrix = this.transformation.getProjectionMatrix(window, camera); - shader.setUniform("projectionMatrix", projectionMatrix); - - // view - Matrix4f viewMatrix = camera.getViewMatrix(); - - // Draw meshes - shader.setUniform("texture_sampler", 0); - for (Map.Entry> entry : this.map.entrySet()) { - Mesh mesh = entry.getKey(); - List items = entry.getValue(); - shader.setUniform("color", mesh.getColor()); - shader.setUniform("isTextured", mesh.isTextured()); - mesh.prepare(); - - // single : loop through items - for (Item item : items) { - Matrix4f modelViewMatrix = this.transformation.getModelViewMatrix(item, viewMatrix); - shader.setUniform("modelViewMatrix", modelViewMatrix); - shader.setUniform("isSelected", item.isSelected()); - mesh.render(); - } - - mesh.restore(); - } - - glDisable(GL_CULL_FACE); - shader.unbind(); - } - - // uses the List of items - public void render2DList(Shader shader, Window window) { - shader.bind(); - - // source # https://stackoverflow.com/a/5467636 - glDepthMask(false); // disable writes to Z-Buffer - glDisable(GL_DEPTH_TEST); // disable depth-testing - - Matrix4f orthoMatrix = this.transformation.getOrthoProjectionMatrix(window); - - // Draw meshes - shader.setUniform("texture_sampler", 0); - for (Item item : this.items) { - Mesh mesh = item.getMesh(); - shader.setUniform("color", mesh.getColor()); - shader.setUniform("isTextured", mesh.isTextured()); - mesh.prepare(); - - Matrix4f projModelMatrix = this.transformation.getOrthoProjModelMatrix(item, orthoMatrix); - shader.setUniform("projModelMatrix", projModelMatrix); - mesh.render(); - - mesh.restore(); - } - - glDepthMask(true); - glEnable(GL_DEPTH_TEST); - - shader.unbind(); - } - - // note does NOT call Item.render(Window) - public void render3DSkybox(Shader shader, Window window, Camera camera) { - shader.bind(); - - // projection - Matrix4f projectionMatrix = this.transformation.getProjectionMatrix(window, camera); - shader.setUniform("projectionMatrix", projectionMatrix); - - // view - Matrix4f viewMatrix = camera.getViewMatrix(); - - // remove translation (different from render3D) - viewMatrix.setTranslation(0, 0, 0); - - // Draw meshes - shader.setUniform("texture_sampler", 0); - for (Map.Entry> entry : this.map.entrySet()) { - Mesh mesh = entry.getKey(); - List items = entry.getValue(); - shader.setUniform("color", mesh.getColor()); - shader.setUniform("isTextured", mesh.isTextured()); - mesh.prepare(); - - // single : loop through items - for (Item item : items) { - Matrix4f modelViewMatrix = this.transformation.getModelViewMatrix(item, viewMatrix); - shader.setUniform("modelViewMatrix", modelViewMatrix); - shader.setUniform("isSelected", item.isSelected()); - mesh.render(); - } - - mesh.restore(); - } - - shader.unbind(); - } - - public void destroy(Shader shader) { - this.destroyShader(shader); - for (Mesh mesh : this.map.keySet()) - mesh.cleanup(); - } - - public void destroyShader(Shader shader) { - shader.cleanup(); - } -} diff --git a/src/engine/Texture.java b/src/engine/Texture.java index 6dc2c32..90b61f0 100644 --- a/src/engine/Texture.java +++ b/src/engine/Texture.java @@ -8,18 +8,39 @@ import java.nio.*; import static org.lwjgl.opengl.GL30.*; -import static org.lwjgl.system.MemoryUtil.*; public class Texture { - - private int id; - private int width; - private int length; + private final int id; + private final int width; + private final int length; public Texture(String fileName) throws Exception { - this.loadTexture(fileName); + int widthArray[] = {0}; + int lengthArray[] = {0}; + ByteBuffer image = Utils.loadImage(fileName, widthArray, lengthArray); + this.width = widthArray[0]; + this.length = lengthArray[0]; + + // Create a new OpenGL texture + this.id = glGenTextures(); + // Bind the texture + this.bind(); + + // Tell OpenGL how to unpack the RGBA bytes. Each component is 1 byte size + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + // make text easier to read + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + // Upload the texture data + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, this.width, this.length, 0, GL_RGBA, GL_UNSIGNED_BYTE, image); + // Generate Mip Map + glGenerateMipmap(GL_TEXTURE_2D); + + Utils.freeImage(image); } - public Texture(int id, int width, int length) throws Exception { + protected Texture(int id, int width, int length) { this.id = id; this.width = width; this.length = length; @@ -39,29 +60,4 @@ public void prepare() { public void cleanup() { glDeleteTextures(this.id); } - - private void loadTexture(String fileName) throws Exception { - ByteBuffer image = Utils.loadImage(fileName, (w, l) -> { this.width = w; this.length = l; }); - - // Create a new OpenGL texture - int textureId = glGenTextures(); - // Bind the texture - glBindTexture(GL_TEXTURE_2D, textureId); - - // Tell OpenGL how to unpack the RGBA bytes. Each component is 1 byte size - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - // make text easier to read - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - // Upload the texture data - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, this.width, this.length, 0, GL_RGBA, GL_UNSIGNED_BYTE, image); - // Generate Mip Map - glGenerateMipmap(GL_TEXTURE_2D); - - Utils.freeImage(image); - - this.id = textureId; - } } diff --git a/src/engine/Transformation.java b/src/engine/Transformation.java deleted file mode 100644 index defd9e6..0000000 --- a/src/engine/Transformation.java +++ /dev/null @@ -1,59 +0,0 @@ -/* -ahbejarano -Transformation helper class. -*/ - -package geetransit.minecraft05.engine; - -import org.joml.Matrix4f; -import org.joml.Vector3f; -import org.joml.Quaternionf; - -public class Transformation { - private final Matrix4f projectionMatrix; - private final Matrix4f modelViewMatrix; - private final Matrix4f orthoProjectionMatrix; - private final Matrix4f orthoProjModelMatrix; - private final Matrix4f modelMatrix; - - public Transformation() { - this.projectionMatrix = new Matrix4f(); - this.modelViewMatrix = new Matrix4f(); - this.orthoProjectionMatrix = new Matrix4f(); - this.orthoProjModelMatrix = new Matrix4f(); - this.modelMatrix = new Matrix4f(); - } - - public Matrix4f getProjectionMatrix(Window window, Camera camera) { - return this.projectionMatrix.setPerspective( - -camera.getFov(), (float) window.getWidth() / window.getHeight(), - camera.getNear(), camera.getFar() - ); - } - - public Matrix4f getModelMatrix(Item item) { - return this.modelMatrix.translationRotateScale(item.getPosition(), item.getRotation(), item.getScale()); - } - - public Matrix4f getModelViewMatrix(Item item, Matrix4f viewMatrix) { - return viewMatrix.mulAffine(this.getModelMatrix(item), this.modelViewMatrix); - } - - public Matrix4f getOrthoProjectionMatrix(Window window) { - return this.orthoProjectionMatrix.setOrtho2D(0, window.getWidth(), window.getHeight(), 0); - } - - // these 2 are different? - public Matrix4f newGetOrthoProjModelMatrix(Item item, Matrix4f orthoMatrix) { - return orthoMatrix.mulOrthoAffine(this.getModelMatrix(item), this.orthoProjModelMatrix); - } - public Matrix4f getOrthoProjModelMatrix(Item item, Matrix4f orthoMatrix) { - return this.orthoProjModelMatrix - .set(orthoMatrix) - .translate(item.getPosition()) - .rotateX((float) Math.toRadians(-item.getRotation().x)) - .rotateY((float) Math.toRadians(-item.getRotation().y)) - .rotateZ((float) Math.toRadians(-item.getRotation().z)) - .scale(item.getScale()); - } -} diff --git a/src/engine/Window.java b/src/engine/Window.java index 11d97db..a20f6f0 100644 --- a/src/engine/Window.java +++ b/src/engine/Window.java @@ -7,6 +7,7 @@ import org.lwjgl.glfw.*; import org.lwjgl.opengl.*; +import org.joml.Matrix4f; import static org.lwjgl.glfw.Callbacks.*; import static org.lwjgl.glfw.GLFW.*; @@ -34,6 +35,9 @@ public class Window { private int targetFps; private float elapsedTime; + private final Matrix4f projectionMatrix; + private final Matrix4f orthoProjectionMatrix; + private boolean destroyed = false; private long monitor; @@ -58,6 +62,9 @@ public Window( this.timer = new Timer(); this.lock = new Object(); this.next = new Bucket(); + + this.projectionMatrix = new Matrix4f(); + this.orthoProjectionMatrix = new Matrix4f(); } public void createWindow() { @@ -215,6 +222,19 @@ private void renderUpdate() { } } + public Matrix4f getProjectionMatrix() { return this.projectionMatrix; } + public Matrix4f buildProjectionMatrix(Camera camera) { + return this.projectionMatrix.setPerspective( + -camera.getFov(), (float) this.getWidth() / this.getHeight(), + camera.getNear(), camera.getFar() + ); + } + + public Matrix4f getOrthoProjectionMatrix() { return this.orthoProjectionMatrix; } + public Matrix4f buildOrthoProjectionMatrix() { + return this.orthoProjectionMatrix.setOrtho2D(0, this.getWidth(), this.getHeight(), 0); + } + protected void updateWindowPos() { int[] xPos = {0}; int[] yPos = {0}; diff --git a/src/game/BlockItem.java b/src/game/BlockItem.java new file mode 100644 index 0000000..a55c1cf --- /dev/null +++ b/src/game/BlockItem.java @@ -0,0 +1,29 @@ +/* +George Zhang +Block item subclass. +*/ + +package geetransit.minecraft05.game; + +import geetransit.minecraft05.engine.*; + +import org.joml.Vector3f; +import org.joml.Quaternionf; + +public class BlockItem extends Item { + private boolean selected; + + public BlockItem(Mesh mesh) { + super(mesh); + this.selected = false; + } + + public BlockItem setPosition(Vector3f position) { super.setPosition(position); return this; } + public BlockItem setPosition(float x, float y, float z) { super.setPosition(x, y, z); return this; } + public BlockItem setRotation(Quaternionf rotation) { super.setRotation(rotation); return this; } + public BlockItem setRotation(float x, float y, float z) { super.setRotation(x, y, z); return this; } + public BlockItem setScale(float scale) { super.setScale(scale); return this; } + + public boolean isSelected() { return this.selected; } + public BlockItem setSelected(boolean selected) { this.selected = selected; return this; } +} diff --git a/src/game/Hud.java b/src/game/Hud.java index 6bd3753..f571212 100644 --- a/src/game/Hud.java +++ b/src/game/Hud.java @@ -7,44 +7,48 @@ import geetransit.minecraft05.engine.*; +import java.util.*; +import org.joml.Matrix4f; + import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.opengl.GL11.*; -public class Hud extends Scene { +public class Hud implements Loopable { private static final int FONT_COLS = 16; private static final int FONT_ROWS = 16; private static final String FONT_FILE = "/res/font.png"; - private Renderer renderer; private Mouse mouse; private Camera camera; private World world; + private Window window; + + private Shader shader; + private List items; private TextItem text; private Item compass; private Item crosshair; public Hud(Mouse mouse, Camera camera, World world) { - super(); - this.addFrom(this.renderer = new Renderer() { - Shader shader; - public void init(Window window) throws Exception { - shader = create2D("/res/vertex-2d.vs", "/res/fragment-2d.fs"); - } - public void render(Window window) { - render2DList(shader, window); - } - public void cleanup() { - destroy(shader); - } - }); this.mouse = mouse; this.camera = camera; this.world = world; + + this.items = new ArrayList<>(); } @Override public void init(Window window) throws Exception { - super.init(window); + this.shader = new Shader(); + this.shader.createVertexShader(Utils.loadResource("/res/vertex-2d.vs")); + this.shader.createFragmentShader(Utils.loadResource("/res/fragment-2d.fs")); + this.shader.link(); + + this.shader.createUniform("projModelMatrix"); + this.shader.createUniform("texture_sampler"); + this.shader.createUniform("color"); + this.shader.createUniform("isTextured"); this.text = new TextItem("", new FontTexture(FONT_FILE, FONT_COLS, FONT_ROWS)); this.text.getMesh().setColor(1, 1, 1); @@ -55,30 +59,63 @@ public void init(Window window) throws Exception { this.crosshair = new Item(ObjLoader.loadMesh("/res/crosshair.obj")); this.crosshair.getMesh().setColor(1, 1, 1); - this.renderer - .addItem(this.text) - .addItem(this.compass) - .addItem(this.crosshair); + this.items.add(this.text); + this.items.add(this.compass); + this.items.add(this.crosshair); + + this.window = window; } @Override - public void render(Window window) { - this.text.setPosition(10f, window.getHeight() * 0.85f, 0f); - this.text.setScale(window.getWidth() * (1/3500f)); + public void update(float interval) { + this.text.setPosition(10f, this.window.getHeight() * 0.85f, 0f); + this.text.setScale(this.window.getWidth() * (1/3500f)); this.text.setText(String.format( "vsync=%s mode=%s mouse=%s\nchange=%s wait=%s\ncamera=%s\nmouse=%s", - window.isVSync(), window.getMode(), window.getInputMode(GLFW_CURSOR) == GLFW_CURSOR_NORMAL, + this.window.isVSync(), this.window.getMode(), this.window.getInputMode(GLFW_CURSOR) == GLFW_CURSOR_NORMAL, this.world.getChange(), this.world.getWait(), this.camera, this.mouse )); - this.compass.setPosition(window.getWidth() * 0.95f, window.getWidth() * 0.05f, 0f); + this.compass.setPosition(this.window.getWidth() * 0.95f, this.window.getWidth() * 0.05f, 0f); this.compass.setRotation(0f, 0f, 180f - this.camera.getRotation().y); - this.compass.setScale(window.getWidth() * (1/20f)); + this.compass.setScale(this.window.getWidth() * (1/20f)); - this.crosshair.setPosition(window.getWidth() * 0.5f, window.getHeight() * 0.5f, 0f); - this.crosshair.setScale(window.getWidth() * (1/50f)); + this.crosshair.setPosition(this.window.getWidth() * 0.5f, this.window.getHeight() * 0.5f, 0f); + this.crosshair.setScale(this.window.getWidth() * (1/50f)); + } - super.render(window); + @Override + public void render(Window window) { + + // rendering + this.shader.bind(); + this.shader.setUniform("texture_sampler", 0); + + // disable depth testing : source # https://stackoverflow.com/a/5467636 + glDepthMask(false); // disable writes to Z-Buffer + glDisable(GL_DEPTH_TEST); // disable depth-testing + + Matrix4f orthoMatrix = window.buildOrthoProjectionMatrix(); + + // draw items + Matrix4f temp = new Matrix4f(); + for (Item item : this.items) + // ($, $$) are ignored paramenters + item.getMesh().renderItem(item, this.shader, ($, $$) -> { + item.buildOrthoProjModelMatrix(orthoMatrix, temp); + this.shader.setUniform("projModelMatrix", temp); + }); + + glDepthMask(true); + glEnable(GL_DEPTH_TEST); + this.shader.unbind(); + } + + @Override + public void cleanup() { + this.shader.cleanup(); + for (Item item : this.items) + item.getMesh().cleanup(); } } diff --git a/src/game/Skybox.java b/src/game/Skybox.java index 9dc7c61..b249f73 100644 --- a/src/game/Skybox.java +++ b/src/game/Skybox.java @@ -7,40 +7,65 @@ import geetransit.minecraft05.engine.*; -public class Skybox extends Scene { - private Renderer renderer; +import java.util.*; +import org.joml.Matrix4f; + +public class Skybox implements Loopable { private Camera camera; + private Shader shader; private Item skybox; public Skybox(Camera camera) { - super(); - this.addFrom(this.renderer = new Renderer() { - Shader shader; - public void init(Window window) throws Exception { - shader = create3D("/res/vertex-3d.vs", "/res/fragment-3d.fs"); - } - public void render(Window window) { - render3DSkybox(shader, window, Skybox.this.camera); - } - public void cleanup() { - destroy(shader); - } - }); this.camera = camera; } @Override public void init(Window window) throws Exception { + this.shader = new Shader(); + this.shader.createVertexShader(Utils.loadResource("/res/vertex-3d.vs")); + this.shader.createFragmentShader(Utils.loadResource("/res/fragment-3d.fs")); + this.shader.link(); + + this.shader.createUniform("projectionMatrix"); + this.shader.createUniform("modelViewMatrix"); + this.shader.createUniform("texture_sampler"); + this.shader.createUniform("color"); + this.shader.createUniform("isTextured"); + Mesh mesh = ObjLoader.loadMesh("/res/skybox.obj"); mesh.setTexture(new Texture("/res/skybox.png")); - this.skybox = new Item(mesh).setPosition(0, 0, 0); - this.renderer.addItem(this.skybox); - super.init(window); + this.skybox = new Item(mesh); + this.skybox.setPosition(0, 0, 0); } @Override - public void render(Window window) { + public void update(float interval) { this.skybox.setScale(this.camera.getFar() * 0.5f); - super.render(window); + } + + @Override + public void render(Window window) { + this.shader.bind(); + this.shader.setUniform("texture_sampler", 0); + this.shader.setUniform("projectionMatrix", window.buildProjectionMatrix(this.camera)); + + // remove view translation + Matrix4f viewMatrix = this.camera.buildViewMatrix(); + viewMatrix.setTranslation(0, 0, 0); + + // draw skybox + Matrix4f temp = new Matrix4f(); + this.skybox.getMesh().renderItem(this.skybox, this.shader, (item, shader) -> { + item.buildModelViewMatrix(viewMatrix, temp); + shader.setUniform("modelViewMatrix", temp); + }); + + this.shader.unbind(); + } + + @Override + public void cleanup() { + this.shader.cleanup(); + this.skybox.getMesh().cleanup(); } } diff --git a/src/game/World.java b/src/game/World.java index 9985684..4ac3532 100644 --- a/src/game/World.java +++ b/src/game/World.java @@ -8,21 +8,25 @@ import geetransit.minecraft05.engine.*; import java.util.*; - import org.joml.Vector3f; +import org.joml.Matrix4f; import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.opengl.GL11.*; -public class World extends Scene { +public class World implements Loopable { public static final float CHANGE_DELAY = 0.2f; public static final float STEP = 0.1f; - private final Renderer renderer; private final Mouse mouse; private final Camera camera; - private final Map blockMap; - private final ClosestItem closestItem; + private Shader shader; + private final Map meshMap; + private final Map> blockMap; + private final List blockList; + + private final ClosestItem closestItem; private final Vector3f movement; private float step; private int render; @@ -31,25 +35,14 @@ public class World extends Scene { private float wait; // time until next place / remove public World(Mouse mouse, Camera camera) { - super(); - this.addFrom(this.renderer = new Renderer() { - Shader shader; - public void init(Window window) throws Exception { - shader = create3D("/res/vertex-3d.vs", "/res/fragment-3d.fs"); - } - public void render(Window window) { - render3D(shader, window, World.this.camera); - } - public void cleanup() { - destroy(shader); - } - }); - this.mouse = mouse; this.camera = camera; + this.meshMap = new HashMap<>(); this.blockMap = new HashMap<>(); - this.closestItem = new ClosestItem(); + this.blockList = new ArrayList<>(); + + this.closestItem = new ClosestItem<>(); this.movement = new Vector3f(); this.step = STEP; } @@ -62,9 +55,22 @@ public void cleanup() { @Override public void init(Window window) throws Exception { + this.shader = new Shader(); + this.shader.createVertexShader(Utils.loadResource("/res/vertex-3d.vs")); + this.shader.createFragmentShader(Utils.loadResource("/res/fragment-3d-block.fs")); + this.shader.link(); + + this.shader.createUniform("projectionMatrix"); + this.shader.createUniform("modelViewMatrix"); + this.shader.createUniform("texture_sampler"); + this.shader.createUniform("color"); + this.shader.createUniform("isTextured"); + this.shader.createUniform("isSelected"); + // Create the blocks' mesh - this.blockMap.put("grassblock", this.loadBlock("/res/cube.obj", "/res/grassblock.png")); - this.blockMap.put("cobbleblock", this.loadBlock("/res/cube.obj", "/res/cobbleblock.png")); + this + .putMesh("grassblock", this.loadMesh("/res/cube.obj", "/res/grassblock.png")) + .putMesh("cobbleblock", this.loadMesh("/res/cube.obj", "/res/cobbleblock.png")); // get heightmap try (HeightMap map = HeightMap.loadFromImage("/res/heightmap.png")) { @@ -72,27 +78,23 @@ public void init(Window window) throws Exception { for (int x = 0; x < map.width; x++) { for (int z = 0; z < map.length; z++) { int y = (int) map.compressExpand(map.heightAt(x, z), 0, map.MAX_COLOR, 0, 16); - this.renderer.addItem(this.newBlock("grassblock").setPosition(x, y, z)); - for (int k = y-1; k >= Math.max(y-2, 0); k--) { - this.renderer.addItem(this.newBlock("cobbleblock").setPosition(x, k, z)); - } + this.addBlock(this.newBlock("grassblock").setPosition(x, y, z)); + for (int k = y-1; k >= Math.max(y-2, 0); k--) + this.addBlock(this.newBlock("cobbleblock").setPosition(x, k, z)); } } } // add spawn markers (-2z is forwards) - this.renderer - .addItem(this.newBlock("grassblock").setPosition(+1, +1, 0)) - .addItem(this.newBlock("grassblock").setPosition(-1, +1, 0)) - .addItem(this.newBlock("grassblock").setPosition( 0, +1, +1)) - .addItem(this.newBlock("grassblock").setPosition( 0, +1, -2)); - - super.init(window); + this + .addBlock(this.newBlock("grassblock").setPosition(+1, +1, 0)) + .addBlock(this.newBlock("grassblock").setPosition(-1, +1, 0)) + .addBlock(this.newBlock("grassblock").setPosition( 0, +1, +1)) + .addBlock(this.newBlock("grassblock").setPosition( 0, +1, -2)); } + @Override public void input(Window window) { - super.input(window); - // movement this.movement.zero(); boolean SPRINTING = (!window.isKeyDown(GLFW_KEY_LEFT_SHIFT) && window.isKeyDown(GLFW_KEY_LEFT_CONTROL)); @@ -121,6 +123,7 @@ public void input(Window window) { if (window.isKeyDown(GLFW_KEY_2)) this.change = "cobbleblock"; } + @Override public void update(float interval) { // movement this.camera.movePosition(this.movement, 30*interval * this.step); @@ -130,10 +133,10 @@ public void update(float interval) { // placing / removing if (this.change != null && this.wait <= 0) { - this.closestItem.update(this.renderer.items, this.camera); + this.closestItem.update(this.blockList, this.camera); if (this.closestItem.closest != null) { if (this.change.equals("")) { - this.renderer.removeItem(this.closestItem.closest); + this.removeBlock(this.closestItem.closest); } else { Vector3f position = new Vector3f(); position.set(this.closestItem.direction); // get normalized camera direction @@ -142,11 +145,11 @@ public void update(float interval) { position.add(this.closestItem.hit); // start from intersection point position.round(); // round to grid check: { - for (Item item : this.renderer.items) - if (item.getPosition().equals(position)) + for (BlockItem block : this.blockList) + if (block.getPosition().equals(position)) break check; // else - this.renderer.addItem(this.newBlock(this.change).setPosition(position)); + this.addBlock(this.newBlock(this.change).setPosition(position)); } } } @@ -158,32 +161,75 @@ public void update(float interval) { this.wait -= interval; if (this.wait < 0 && this.change == null) this.wait = 0; - - super.update(interval); } + @Override public void render(Window window) { - this.updateSelectedItem(); - super.render(window); - } - - private void updateSelectedItem() { - for (Item item : this.renderer.items) - item.setSelected(false); - this.closestItem.update(this.renderer.items, this.camera); + // update selected item + for (BlockItem block : this.blockList) + block.setSelected(false); + this.closestItem.update(this.blockList, this.camera); if (this.closestItem.closest != null) this.closestItem.closest.setSelected(true); + + this.shader.bind(); + this.shader.setUniform("texture_sampler", 0); + this.shader.setUniform("projectionMatrix", window.buildProjectionMatrix(this.camera)); + + // view + Matrix4f viewMatrix = this.camera.buildViewMatrix(); + + // draw blocks + Matrix4f temp = new Matrix4f(); + for (Map.Entry> entry : this.blockMap.entrySet()) + entry.getKey().renderList(entry.getValue(), this.shader, (item, shader) -> { + item.buildModelViewMatrix(viewMatrix, temp); + shader.setUniform("modelViewMatrix", temp); + shader.setUniform("isSelected", item.isSelected()); + }); + + this.shader.unbind(); } - private static Item loadBlock(String objFileName, String textureFileName) throws Exception { + @Override + public void cleanup() { + this.shader.cleanup(); + for (Mesh mesh : this.meshMap.values()) + mesh.cleanup(); + } + + // block helpers + private static Mesh loadMesh(String objFileName, String textureFileName) throws Exception { Mesh mesh = ObjLoader.loadMesh(objFileName); mesh.setTexture(new Texture(textureFileName)); - Item block = new Item(mesh); - block.setScale(0.5f); - return block; + return mesh; + } + + private World putMesh(String name, Mesh mesh) { + this.meshMap.put(name, mesh); + return this; + } + + private BlockItem newBlock(String name) { + Mesh mesh = this.meshMap.get(name); + return new BlockItem(mesh).setScale(0.5f); + } + + private World addBlock(BlockItem block) { + Mesh mesh = block.getMesh(); + this.blockList.add(block); + if (!this.blockMap.containsKey(mesh)) + this.blockMap.put(mesh, new ArrayList<>()); + this.blockMap.get(mesh).add(block); + return this; } - private Item newBlock(String name) { - return this.blockMap.get(name).clone(); + private World removeBlock(BlockItem block) { + Mesh mesh = block.getMesh(); + this.blockList.remove(block); + this.blockMap.get(mesh).remove(block); + if (this.blockMap.get(mesh).size() == 0) + this.blockMap.remove(mesh); + return this; } } From 3d3a20f715ea42acb0fa05d6a121a4c0ce6648dc Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Sat, 27 Jun 2020 13:09:59 -0400 Subject: [PATCH 14/52] Readd backface culling Accidentally removed when Renderer was removed in 90de8ac28baf612c1ca0850152a80b81e4d12015 --- src/game/World.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/game/World.java b/src/game/World.java index 4ac3532..1e776ff 100644 --- a/src/game/World.java +++ b/src/game/World.java @@ -175,6 +175,8 @@ public void render(Window window) { this.shader.bind(); this.shader.setUniform("texture_sampler", 0); this.shader.setUniform("projectionMatrix", window.buildProjectionMatrix(this.camera)); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); // view Matrix4f viewMatrix = this.camera.buildViewMatrix(); @@ -188,6 +190,7 @@ public void render(Window window) { shader.setUniform("isSelected", item.isSelected()); }); + glDisable(GL_CULL_FACE); this.shader.unbind(); } From 050536c566a07ab7657499e1b26b8d216634455d Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Sat, 27 Jun 2020 15:37:16 -0400 Subject: [PATCH 15/52] Add frustum culling Rename Mesh.render* to just .render Add .isVisible to BlockItem Add Camera.updateFrustum and .insideFrustum Reverse Mesh.render arguments (item, shader -> shader, item) --- src/engine/Camera.java | 19 +++++++++++++++++++ src/engine/Mesh.java | 12 ++++++++---- src/game/BlockItem.java | 5 +++++ src/game/Hud.java | 2 +- src/game/Skybox.java | 6 +++--- src/game/World.java | 27 ++++++++++++++++++--------- 6 files changed, 54 insertions(+), 17 deletions(-) diff --git a/src/engine/Camera.java b/src/engine/Camera.java index 789dc59..8de2161 100644 --- a/src/engine/Camera.java +++ b/src/engine/Camera.java @@ -8,6 +8,7 @@ import org.joml.Vector2f; import org.joml.Vector3f; import org.joml.Matrix4f; +import org.joml.FrustumIntersection; public class Camera implements Inputtable { public static final float FOV = 80f; @@ -19,7 +20,10 @@ public class Camera implements Inputtable { private final Vector3f position; private final Vector3f rotation; // in degrees + private final Matrix4f viewMatrix; + private final Matrix4f projViewMatrix; + private final FrustumIntersection frustumIntersection; private float fov; private float near; @@ -34,7 +38,10 @@ public Camera(Mouse mouse) { this.position = new Vector3f(); this.rotation = new Vector3f(); + this.viewMatrix = new Matrix4f(); + this.projViewMatrix = new Matrix4f(); + this.frustumIntersection = new FrustumIntersection(); this.setFov(FOV); this.setNear(NEAR); @@ -122,6 +129,18 @@ public Matrix4f buildViewMatrix() { .translate(this.getNegativePosition()); } + public void updateFrustum(Matrix4f projMatrix) { + projMatrix.mul(this.viewMatrix, this.projViewMatrix); + this.frustumIntersection.set(this.projViewMatrix); + } + + public boolean insideFrustum(Vector3f position, float radius) { + return this.insideFrustum(position.x, position.y, position.z, radius); + } + public boolean insideFrustum(float x, float y, float z, float radius) { + return this.frustumIntersection.testSphere(x, y, z, radius); + } + public String toString() { return String.format( "<%s position=%s rotation=%s>", diff --git a/src/engine/Mesh.java b/src/engine/Mesh.java index 65680f8..89098ba 100644 --- a/src/engine/Mesh.java +++ b/src/engine/Mesh.java @@ -7,6 +7,7 @@ import java.util.*; import java.util.function.*; +import java.util.stream.*; import java.nio.*; import org.joml.*; import static org.lwjgl.opengl.GL30.*; @@ -90,18 +91,21 @@ public Mesh(float[] posArray, int[] indexArray, float[] coordArray) { public Mesh setColor(float r, float g, float b) { return this.setColor(r, g, b, 1f); } public Mesh setColor(float r, float g, float b, float a) { this.color.set(r, g, b, a); return this; } - public void renderList(List items, Shader shader, BiConsumer consumer) { + public void render(Shader shader, Stream items, BiConsumer consumer) { + this.render(shader, (Iterable) items::iterator, consumer); + } + public void render(Shader shader, Iterable items, BiConsumer consumer) { this.with(shader, () -> { for (T item : items) { - consumer.accept(item, shader); + consumer.accept(shader, item); this.draw(); } }); } - public void renderItem(T item, Shader shader, BiConsumer consumer) { + public void render(Shader shader, T item, BiConsumer consumer) { this.with(shader, () -> { - consumer.accept(item, shader); + consumer.accept(shader, item); this.draw(); }); } diff --git a/src/game/BlockItem.java b/src/game/BlockItem.java index a55c1cf..a677f16 100644 --- a/src/game/BlockItem.java +++ b/src/game/BlockItem.java @@ -12,10 +12,12 @@ public class BlockItem extends Item { private boolean selected; + private boolean visible; public BlockItem(Mesh mesh) { super(mesh); this.selected = false; + this.visible = false; } public BlockItem setPosition(Vector3f position) { super.setPosition(position); return this; } @@ -26,4 +28,7 @@ public BlockItem(Mesh mesh) { public boolean isSelected() { return this.selected; } public BlockItem setSelected(boolean selected) { this.selected = selected; return this; } + + public boolean isVisible() { return this.visible; } + public BlockItem setVisible(boolean visible) { this.visible = visible; return this; } } diff --git a/src/game/Hud.java b/src/game/Hud.java index f571212..c0a8968 100644 --- a/src/game/Hud.java +++ b/src/game/Hud.java @@ -102,7 +102,7 @@ public void render(Window window) { Matrix4f temp = new Matrix4f(); for (Item item : this.items) // ($, $$) are ignored paramenters - item.getMesh().renderItem(item, this.shader, ($, $$) -> { + item.getMesh().render(this.shader, item, ($, $$) -> { item.buildOrthoProjModelMatrix(orthoMatrix, temp); this.shader.setUniform("projModelMatrix", temp); }); diff --git a/src/game/Skybox.java b/src/game/Skybox.java index b249f73..8ce10d1 100644 --- a/src/game/Skybox.java +++ b/src/game/Skybox.java @@ -55,9 +55,9 @@ public void render(Window window) { // draw skybox Matrix4f temp = new Matrix4f(); - this.skybox.getMesh().renderItem(this.skybox, this.shader, (item, shader) -> { - item.buildModelViewMatrix(viewMatrix, temp); - shader.setUniform("modelViewMatrix", temp); + this.skybox.getMesh().render(this.shader, this.skybox, ($, $$) -> { + this.skybox.buildModelViewMatrix(viewMatrix, temp); + this.shader.setUniform("modelViewMatrix", temp); }); this.shader.unbind(); diff --git a/src/game/World.java b/src/game/World.java index 1e776ff..1aa6770 100644 --- a/src/game/World.java +++ b/src/game/World.java @@ -161,17 +161,17 @@ public void update(float interval) { this.wait -= interval; if (this.wait < 0 && this.change == null) this.wait = 0; - } - @Override - public void render(Window window) { - // update selected item + // update selected block for (BlockItem block : this.blockList) block.setSelected(false); this.closestItem.update(this.blockList, this.camera); if (this.closestItem.closest != null) this.closestItem.closest.setSelected(true); + } + @Override + public void render(Window window) { this.shader.bind(); this.shader.setUniform("texture_sampler", 0); this.shader.setUniform("projectionMatrix", window.buildProjectionMatrix(this.camera)); @@ -181,14 +181,23 @@ public void render(Window window) { // view Matrix4f viewMatrix = this.camera.buildViewMatrix(); + // update visible blocks + this.camera.updateFrustum(window.getProjectionMatrix()); + for (BlockItem block : this.blockList) + block.setVisible(this.camera.insideFrustum(block.getPosition(), 2*block.getScale())); + // draw blocks Matrix4f temp = new Matrix4f(); for (Map.Entry> entry : this.blockMap.entrySet()) - entry.getKey().renderList(entry.getValue(), this.shader, (item, shader) -> { - item.buildModelViewMatrix(viewMatrix, temp); - shader.setUniform("modelViewMatrix", temp); - shader.setUniform("isSelected", item.isSelected()); - }); + entry.getKey().render( + this.shader, + entry.getValue().stream().filter(item -> item.isVisible()), + (shader, item) -> { + item.buildModelViewMatrix(viewMatrix, temp); + shader.setUniform("modelViewMatrix", temp); + shader.setUniform("isSelected", item.isSelected()); + } + ); glDisable(GL_CULL_FACE); this.shader.unbind(); From 7428d28c9dafc557a6f50c6a49924e2c179641a7 Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Sun, 28 Jun 2020 22:47:39 -0400 Subject: [PATCH 16/52] Add Ticker / Countdown and move matrice building to main render Use get*Matrix instead of build*Matrix in subscenes Make Skybox view removing temporary (restore old translation values) More constants in World --- src/engine/Countdown.java | 45 +++++++++++++++++++++++++++++++++++++++ src/engine/Ticker.java | 45 +++++++++++++++++++++++++++++++++++++++ src/game/Game.java | 6 ++++++ src/game/Hud.java | 4 +--- src/game/Skybox.java | 34 ++++++++++++++++++++++++++--- src/game/World.java | 41 ++++++++++++++++------------------- 6 files changed, 146 insertions(+), 29 deletions(-) create mode 100644 src/engine/Countdown.java create mode 100644 src/engine/Ticker.java diff --git a/src/engine/Countdown.java b/src/engine/Countdown.java new file mode 100644 index 0000000..6c4318f --- /dev/null +++ b/src/engine/Countdown.java @@ -0,0 +1,45 @@ +/* +George Zhang +Countdown class. +*/ + +package geetransit.minecraft05.engine; + +public class Countdown { + private float interval; + private float wait; + + public Countdown(float interval) { + this.interval = interval; + this.wait = 0f; + } + + public float getInterval() { return this.interval; } + public Countdown setInterval(float interval) { this.interval = interval; return this; } + + public Countdown add(float time) { + this.wait -= time; + return this; + } + + public Countdown reset() { + this.wait = 0f; + return this; + } + + // while (countdown.next()) + public boolean next() { + if (this.wait > 0) + return false; + this.wait += this.interval; + return true; + } + + // if (countdown.nextOnce()) + public boolean nextOnce() { + if (this.wait > 0) + return false; + this.wait = this.interval; + return true; + } +} diff --git a/src/engine/Ticker.java b/src/engine/Ticker.java new file mode 100644 index 0000000..e3449f2 --- /dev/null +++ b/src/engine/Ticker.java @@ -0,0 +1,45 @@ +/* +George Zhang +Ticker class. +*/ + +package geetransit.minecraft05.engine; + +public class Ticker { + private float interval; + private float accumulated; + + public Ticker(float interval) { + this.interval = interval; + this.accumulated = 0f; + } + + public float getInterval() { return this.interval; } + public Ticker setInterval(float interval) { this.interval = interval; return this; } + + public Ticker add(float time) { + this.accumulated += time; + return this; + } + + public Ticker reset() { + this.accumulated = 0f; + return this; + } + + // while (ticker.next()) + public boolean next() { + if (this.accumulated < this.interval) + return false; + this.accumulated -= this.interval; + return true; + } + + // if (ticker.nextOnce()) + public boolean nextOnce() { + if (this.accumulated < this.interval) + return false; + this.accumulated %= this.interval; + return true; + } +} diff --git a/src/game/Game.java b/src/game/Game.java index f68b59b..d5ae120 100644 --- a/src/game/Game.java +++ b/src/game/Game.java @@ -121,6 +121,12 @@ public void render(Window window) { // clear the framebuffer window.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + // build matrices + window.buildProjectionMatrix(this.camera); + window.buildOrthoProjectionMatrix(); + this.camera.buildViewMatrix(); + this.camera.updateFrustum(window.getProjectionMatrix()); + // render scenes super.render(window); } diff --git a/src/game/Hud.java b/src/game/Hud.java index c0a8968..1b44a52 100644 --- a/src/game/Hud.java +++ b/src/game/Hud.java @@ -87,8 +87,6 @@ public void update(float interval) { @Override public void render(Window window) { - - // rendering this.shader.bind(); this.shader.setUniform("texture_sampler", 0); @@ -96,7 +94,7 @@ public void render(Window window) { glDepthMask(false); // disable writes to Z-Buffer glDisable(GL_DEPTH_TEST); // disable depth-testing - Matrix4f orthoMatrix = window.buildOrthoProjectionMatrix(); + Matrix4f orthoMatrix = window.getOrthoProjectionMatrix(); // draw items Matrix4f temp = new Matrix4f(); diff --git a/src/game/Skybox.java b/src/game/Skybox.java index 8ce10d1..23f51c3 100644 --- a/src/game/Skybox.java +++ b/src/game/Skybox.java @@ -8,15 +8,24 @@ import geetransit.minecraft05.engine.*; import java.util.*; -import org.joml.Matrix4f; +import org.joml.*; + +import static org.lwjgl.glfw.GLFW.*; public class Skybox implements Loopable { private Camera camera; + private Countdown countdown; + private Shader shader; private Item skybox; + private boolean toggle; + private boolean visible; + public Skybox(Camera camera) { this.camera = camera; + this.countdown = new Countdown(0.5f); + this.visible = true; } @Override @@ -38,19 +47,35 @@ public void init(Window window) throws Exception { this.skybox.setPosition(0, 0, 0); } + @Override + public void input(Window window) { + this.toggle = window.isKeyDown(GLFW_KEY_T); + if (!this.toggle) + this.countdown.reset(); + } + @Override public void update(float interval) { this.skybox.setScale(this.camera.getFar() * 0.5f); + + // toggle skybox + this.countdown.add(interval); + if (this.toggle && this.countdown.nextOnce()) + this.visible = !this.visible; } @Override public void render(Window window) { + if (!this.visible) + return; + this.shader.bind(); this.shader.setUniform("texture_sampler", 0); - this.shader.setUniform("projectionMatrix", window.buildProjectionMatrix(this.camera)); + this.shader.setUniform("projectionMatrix", window.getProjectionMatrix()); // remove view translation - Matrix4f viewMatrix = this.camera.buildViewMatrix(); + Matrix4f viewMatrix = this.camera.getViewMatrix(); + Vector3f oldTanslation = viewMatrix.getTranslation(new Vector3f()); viewMatrix.setTranslation(0, 0, 0); // draw skybox @@ -60,6 +85,9 @@ public void render(Window window) { this.shader.setUniform("modelViewMatrix", temp); }); + // undo view translation removal + viewMatrix.setTranslation(oldTanslation); + this.shader.unbind(); } diff --git a/src/game/World.java b/src/game/World.java index 1aa6770..244b255 100644 --- a/src/game/World.java +++ b/src/game/World.java @@ -15,11 +15,16 @@ import static org.lwjgl.opengl.GL11.*; public class World implements Loopable { - public static final float CHANGE_DELAY = 0.2f; - public static final float STEP = 0.1f; + public static final float CHANGE_DELAY = 0.2f; // time between block change (place / remove) + public static final float MOVEMENT_STEP = 3.0f; // distance moved in 1 second + public static final float RENDER_STEP = 3.0f; // render changed in 1 second + public static final float SPRINT_MULTIPLIER = 1.5f; // sprinting change + public static final float BLOCK_SCALE = 0.5f; // block scaling (mesh is 2x2x2) + public static final float BLOCK_RADIUS = 2f; // radius around block (for frustum culling) private final Mouse mouse; private final Camera camera; + private final Countdown countdown; private Shader shader; private final Map meshMap; @@ -28,7 +33,6 @@ public class World implements Loopable { private final ClosestItem closestItem; private final Vector3f movement; - private float step; private int render; private String change; // ""=air @@ -37,6 +41,7 @@ public class World implements Loopable { public World(Mouse mouse, Camera camera) { this.mouse = mouse; this.camera = camera; + this.countdown = new Countdown(CHANGE_DELAY); this.meshMap = new HashMap<>(); this.blockMap = new HashMap<>(); @@ -44,15 +49,11 @@ public World(Mouse mouse, Camera camera) { this.closestItem = new ClosestItem<>(); this.movement = new Vector3f(); - this.step = STEP; } public String getChange() { return this.change; } public float getWait() { return this.wait; } - public float getStep() { return this.step; } - public World setStep(float step) { this.step = step; return this; } - @Override public void init(Window window) throws Exception { this.shader = new Shader(); @@ -108,7 +109,7 @@ public void input(Window window) { if (window.isKeyDown(GLFW_KEY_SPACE)) this.movement.y++; if (this.movement.length() > 1f) this.movement.div(this.movement.length()); - if (SPRINTING && this.movement.z < 0) this.movement.mul(1.5f); + if (SPRINTING && this.movement.z < 0) this.movement.mul(SPRINT_MULTIPLIER); // render distance (camera) this.render = 0; @@ -121,18 +122,20 @@ public void input(Window window) { if (window.isKeyDown(GLFW_KEY_0)) this.change = ""; if (window.isKeyDown(GLFW_KEY_1)) this.change = "grassblock"; if (window.isKeyDown(GLFW_KEY_2)) this.change = "cobbleblock"; + if (this.change == null) this.countdown.reset(); } @Override public void update(float interval) { // movement - this.camera.movePosition(this.movement, 30*interval * this.step); + this.camera.movePosition(this.movement, interval*MOVEMENT_STEP); // render distance - this.camera.setFar(Math.max(Camera.NEAR+0.01f, this.camera.getFar() + 0.1f*this.render)); + this.camera.setFar(Math.max(Camera.NEAR+0.01f, this.camera.getFar() + this.render * interval*RENDER_STEP)); // placing / removing - if (this.change != null && this.wait <= 0) { + this.countdown.add(interval); + if (this.change != null && this.countdown.nextOnce()) { this.closestItem.update(this.blockList, this.camera); if (this.closestItem.closest != null) { if (this.change.equals("")) { @@ -153,15 +156,8 @@ public void update(float interval) { } } } - this.wait += this.CHANGE_DELAY; } - // update wait time - if (this.wait > 0) - this.wait -= interval; - if (this.wait < 0 && this.change == null) - this.wait = 0; - // update selected block for (BlockItem block : this.blockList) block.setSelected(false); @@ -174,17 +170,16 @@ public void update(float interval) { public void render(Window window) { this.shader.bind(); this.shader.setUniform("texture_sampler", 0); - this.shader.setUniform("projectionMatrix", window.buildProjectionMatrix(this.camera)); + this.shader.setUniform("projectionMatrix", window.getProjectionMatrix()); glEnable(GL_CULL_FACE); glCullFace(GL_BACK); // view - Matrix4f viewMatrix = this.camera.buildViewMatrix(); + Matrix4f viewMatrix = this.camera.getViewMatrix(); // update visible blocks - this.camera.updateFrustum(window.getProjectionMatrix()); for (BlockItem block : this.blockList) - block.setVisible(this.camera.insideFrustum(block.getPosition(), 2*block.getScale())); + block.setVisible(this.camera.insideFrustum(block.getPosition(), BLOCK_RADIUS*block.getScale())); // draw blocks Matrix4f temp = new Matrix4f(); @@ -224,7 +219,7 @@ private World putMesh(String name, Mesh mesh) { private BlockItem newBlock(String name) { Mesh mesh = this.meshMap.get(name); - return new BlockItem(mesh).setScale(0.5f); + return new BlockItem(mesh).setScale(BLOCK_SCALE); } private World addBlock(BlockItem block) { From 64a01cf14576d9e2bb9c7b6e620ca6ec8580e44d Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Mon, 29 Jun 2020 09:02:10 -0400 Subject: [PATCH 17/52] Cleanup Shader class Renamings: programId -> program vertexShaderId -> fragmentShaderId -> fragment arg shaderCode -> code arg shaderType -> type --- src/engine/Shader.java | 96 +++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 53 deletions(-) diff --git a/src/engine/Shader.java b/src/engine/Shader.java index 927116e..275a735 100644 --- a/src/engine/Shader.java +++ b/src/engine/Shader.java @@ -11,99 +11,90 @@ import static org.lwjgl.opengl.GL20.*; public class Shader { - private final int programId; + private final int program; - private int vertexShaderId; - private int fragmentShaderId; + private int vertex; + private int fragment; private final Map uniforms; public Shader() throws Exception { - this.programId = glCreateProgram(); - if (this.programId == 0) { + this.program = glCreateProgram(); + if (this.program == 0) throw new Exception("Could not create Shader"); - } this.uniforms = new HashMap<>(); } public void createUniform(String name) throws Exception { - int location = glGetUniformLocation(this.programId, name); - if (location < 0) { + int location = glGetUniformLocation(this.program, name); + if (location < 0) throw new Exception("Could not find uniform:" + name); - } - uniforms.put(name, location); + this.uniforms.put(name, location); } public void setUniform(String name, Matrix4f value) { // Dump the matrix into a float buffer try (MemoryStack stack = MemoryStack.stackPush()) { - glUniformMatrix4fv(uniforms.get(name), false, value.get(stack.mallocFloat(16))); + glUniformMatrix4fv(this.uniforms.get(name), false, value.get(stack.mallocFloat(16))); } } public void setUniform(String name, int value) { - glUniform1i(uniforms.get(name), value); + glUniform1i(this.uniforms.get(name), value); } public void setUniform(String name, boolean value) { - glUniform1i(uniforms.get(name), value ? 1 : 0); + this.setUniform(name, value ? 1 : 0); } public void setUniform(String name, Vector3f value) { - glUniform3f(uniforms.get(name), value.x, value.y, value.z); + glUniform3f(this.uniforms.get(name), value.x, value.y, value.z); } public void setUniform(String name, Vector4f value) { - glUniform4f(uniforms.get(name), value.x, value.y, value.z, value.w); + glUniform4f(this.uniforms.get(name), value.x, value.y, value.z, value.w); } - public void createVertexShader(String shaderCode) throws Exception { - this.vertexShaderId = createShader(shaderCode, GL_VERTEX_SHADER); + public void createVertexShader(String code) throws Exception { + this.vertex = this.createShader(code, GL_VERTEX_SHADER); } - public void createFragmentShader(String shaderCode) throws Exception { - this.fragmentShaderId = createShader(shaderCode, GL_FRAGMENT_SHADER); + public void createFragmentShader(String code) throws Exception { + this.fragment = this.createShader(code, GL_FRAGMENT_SHADER); } - protected int createShader(String shaderCode, int shaderType) throws Exception { - int shaderId = glCreateShader(shaderType); - if (shaderId == 0) { - throw new Exception("Error creating shader. Type: " + shaderType); - } + protected int createShader(String code, int type) throws Exception { + int id = glCreateShader(type); + if (id == 0) + throw new Exception("Error creating shader. Type: " + type); - glShaderSource(shaderId, shaderCode); - glCompileShader(shaderId); + glShaderSource(id, code); + glCompileShader(id); - if (glGetShaderi(shaderId, GL_COMPILE_STATUS) == 0) { - throw new Exception("Error compiling Shader code: " + glGetShaderInfoLog(shaderId, 1024)); - } - glAttachShader(programId, shaderId); + if (glGetShaderi(id, GL_COMPILE_STATUS) == 0) + throw new Exception("Error compiling Shader code: " + glGetShaderInfoLog(id, 1024)); + glAttachShader(this.program, id); - return shaderId; + return id; } public void link() throws Exception { - glLinkProgram(this.programId); - if (glGetProgrami(this.programId, GL_LINK_STATUS) == 0) { - throw new Exception("Error linking Shader code: " + glGetProgramInfoLog(this.programId, 1024)); - } + glLinkProgram(this.program); + if (glGetProgrami(this.program, GL_LINK_STATUS) == 0) + throw new Exception("Error linking Shader code: " + glGetProgramInfoLog(this.program, 1024)); - if (this.vertexShaderId != 0) { - glDetachShader(this.programId, this.vertexShaderId); - } - if (this.fragmentShaderId != 0) { - glDetachShader(this.programId, this.fragmentShaderId); - } + if (this.vertex != 0) + glDetachShader(this.program, this.vertex); + if (this.fragment != 0) + glDetachShader(this.program, this.fragment); // definition in res/vertex.vs // equivalent of `layout (location = #) ...` - glBindAttribLocation(this.programId, 0, "position"); - glBindAttribLocation(this.programId, 1, "coords"); - glValidateProgram(this.programId); - if (glGetProgrami(this.programId, GL_VALIDATE_STATUS) == 0) { - System.err.println("Warning validating Shader code: " + glGetProgramInfoLog(this.programId, 1024)); - } - + glBindAttribLocation(this.program, 0, "position"); + glBindAttribLocation(this.program, 1, "coords"); + glValidateProgram(this.program); + if (glGetProgrami(this.program, GL_VALIDATE_STATUS) == 0) + System.err.println("Warning validating Shader code: " + glGetProgramInfoLog(this.program, 1024)); } public void bind() { - glUseProgram(this.programId); + glUseProgram(this.program); } public void unbind() { @@ -112,8 +103,7 @@ public void unbind() { public void cleanup() { this.unbind(); - if (this.programId != 0) { - glDeleteProgram(this.programId); - } + if (this.program != 0) + glDeleteProgram(this.program); } -} \ No newline at end of file +} From c17502efbb097f5b2216d68bba8f8a6d60e48622 Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Mon, 29 Jun 2020 10:23:16 -0400 Subject: [PATCH 18/52] Add getter methods for time tracker classes (Ticker.accumulated / Countdown.wait) --- src/engine/Countdown.java | 1 + src/engine/Ticker.java | 1 + 2 files changed, 2 insertions(+) diff --git a/src/engine/Countdown.java b/src/engine/Countdown.java index 6c4318f..bf37c44 100644 --- a/src/engine/Countdown.java +++ b/src/engine/Countdown.java @@ -14,6 +14,7 @@ public Countdown(float interval) { this.wait = 0f; } + public float getWait() { return this.wait; } public float getInterval() { return this.interval; } public Countdown setInterval(float interval) { this.interval = interval; return this; } diff --git a/src/engine/Ticker.java b/src/engine/Ticker.java index e3449f2..1c656cd 100644 --- a/src/engine/Ticker.java +++ b/src/engine/Ticker.java @@ -14,6 +14,7 @@ public Ticker(float interval) { this.accumulated = 0f; } + public float getAccumulated() { return this.accumulated; } public float getInterval() { return this.interval; } public Ticker setInterval(float interval) { this.interval = interval; return this; } From 412e069a218b476d6a872d9b70096d68ad71f36d Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Mon, 29 Jun 2020 10:35:22 -0400 Subject: [PATCH 19/52] Use countdown's wait time in World class --- src/game/World.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/game/World.java b/src/game/World.java index 244b255..e5f5bf5 100644 --- a/src/game/World.java +++ b/src/game/World.java @@ -34,9 +34,7 @@ public class World implements Loopable { private final ClosestItem closestItem; private final Vector3f movement; private int render; - private String change; // ""=air - private float wait; // time until next place / remove public World(Mouse mouse, Camera camera) { this.mouse = mouse; @@ -52,7 +50,7 @@ public World(Mouse mouse, Camera camera) { } public String getChange() { return this.change; } - public float getWait() { return this.wait; } + public float getWait() { return this.countdown.getWait(); } @Override public void init(Window window) throws Exception { From fe1e82a06ff32ff60ac43a5be66568eec9b11542 Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Mon, 29 Jun 2020 11:00:34 -0400 Subject: [PATCH 20/52] Renaming of Shader methods createUniform -> create setUniform -> set createVertexShader -> compileVertex createFragmentShader -> compileFragment protected createShader -> compile --- src/engine/Mesh.java | 6 ++++-- src/engine/Shader.java | 27 +++++++++++++-------------- src/game/Hud.java | 16 ++++++++-------- src/game/Skybox.java | 20 ++++++++++---------- src/game/World.java | 24 ++++++++++++------------ 5 files changed, 47 insertions(+), 46 deletions(-) diff --git a/src/engine/Mesh.java b/src/engine/Mesh.java index 89098ba..0f4251d 100644 --- a/src/engine/Mesh.java +++ b/src/engine/Mesh.java @@ -46,6 +46,7 @@ public Mesh(float[] posArray, int[] indexArray, float[] coordArray) { posBuffer.put(posArray).flip(); glBindBuffer(GL_ARRAY_BUFFER, vboId); glBufferData(GL_ARRAY_BUFFER, posBuffer, GL_STATIC_DRAW); + // position uniform glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0); @@ -64,6 +65,7 @@ public Mesh(float[] posArray, int[] indexArray, float[] coordArray) { coordBuffer.put(coordArray).flip(); glBindBuffer(GL_ARRAY_BUFFER, vboId); glBufferData(GL_ARRAY_BUFFER, coordBuffer, GL_STATIC_DRAW); + // coord uniform glEnableVertexAttribArray(1); glVertexAttribPointer(1, 2, GL_FLOAT, false, 0, 0); @@ -138,8 +140,8 @@ protected void prepare(Mesh lastMesh) { // setup uniforms protected void setup(Shader shader) { - shader.setUniform("color", this.color); - shader.setUniform("isTextured", this.isTextured()); + shader.set("color", this.color); + shader.set("isTextured", this.isTextured()); } // draw elements diff --git a/src/engine/Shader.java b/src/engine/Shader.java index 275a735..4c4b19b 100644 --- a/src/engine/Shader.java +++ b/src/engine/Shader.java @@ -12,12 +12,11 @@ public class Shader { private final int program; + private final Map uniforms; private int vertex; private int fragment; - private final Map uniforms; - public Shader() throws Exception { this.program = glCreateProgram(); if (this.program == 0) @@ -25,41 +24,41 @@ public Shader() throws Exception { this.uniforms = new HashMap<>(); } - public void createUniform(String name) throws Exception { + public void create(String name) throws Exception { int location = glGetUniformLocation(this.program, name); if (location < 0) throw new Exception("Could not find uniform:" + name); this.uniforms.put(name, location); } - public void setUniform(String name, Matrix4f value) { + public void set(String name, Matrix4f value) { // Dump the matrix into a float buffer try (MemoryStack stack = MemoryStack.stackPush()) { glUniformMatrix4fv(this.uniforms.get(name), false, value.get(stack.mallocFloat(16))); } } - public void setUniform(String name, int value) { + public void set(String name, int value) { glUniform1i(this.uniforms.get(name), value); } - public void setUniform(String name, boolean value) { - this.setUniform(name, value ? 1 : 0); + public void set(String name, boolean value) { + this.set(name, value ? 1 : 0); } - public void setUniform(String name, Vector3f value) { + public void set(String name, Vector3f value) { glUniform3f(this.uniforms.get(name), value.x, value.y, value.z); } - public void setUniform(String name, Vector4f value) { + public void set(String name, Vector4f value) { glUniform4f(this.uniforms.get(name), value.x, value.y, value.z, value.w); } - public void createVertexShader(String code) throws Exception { - this.vertex = this.createShader(code, GL_VERTEX_SHADER); + public void compileVertex(String code) throws Exception { + this.vertex = this.compile(code, GL_VERTEX_SHADER); } - public void createFragmentShader(String code) throws Exception { - this.fragment = this.createShader(code, GL_FRAGMENT_SHADER); + public void compileFragment(String code) throws Exception { + this.fragment = this.compile(code, GL_FRAGMENT_SHADER); } - protected int createShader(String code, int type) throws Exception { + protected int compile(String code, int type) throws Exception { int id = glCreateShader(type); if (id == 0) throw new Exception("Error creating shader. Type: " + type); diff --git a/src/game/Hud.java b/src/game/Hud.java index 1b44a52..70d5605 100644 --- a/src/game/Hud.java +++ b/src/game/Hud.java @@ -41,14 +41,14 @@ public Hud(Mouse mouse, Camera camera, World world) { @Override public void init(Window window) throws Exception { this.shader = new Shader(); - this.shader.createVertexShader(Utils.loadResource("/res/vertex-2d.vs")); - this.shader.createFragmentShader(Utils.loadResource("/res/fragment-2d.fs")); + this.shader.compileVertex(Utils.loadResource("/res/vertex-2d.vs")); + this.shader.compileFragment(Utils.loadResource("/res/fragment-2d.fs")); this.shader.link(); - this.shader.createUniform("projModelMatrix"); - this.shader.createUniform("texture_sampler"); - this.shader.createUniform("color"); - this.shader.createUniform("isTextured"); + this.shader.create("projModelMatrix"); + this.shader.create("texture_sampler"); + this.shader.create("color"); + this.shader.create("isTextured"); this.text = new TextItem("", new FontTexture(FONT_FILE, FONT_COLS, FONT_ROWS)); this.text.getMesh().setColor(1, 1, 1); @@ -88,7 +88,7 @@ public void update(float interval) { @Override public void render(Window window) { this.shader.bind(); - this.shader.setUniform("texture_sampler", 0); + this.shader.set("texture_sampler", 0); // disable depth testing : source # https://stackoverflow.com/a/5467636 glDepthMask(false); // disable writes to Z-Buffer @@ -102,7 +102,7 @@ public void render(Window window) { // ($, $$) are ignored paramenters item.getMesh().render(this.shader, item, ($, $$) -> { item.buildOrthoProjModelMatrix(orthoMatrix, temp); - this.shader.setUniform("projModelMatrix", temp); + this.shader.set("projModelMatrix", temp); }); glDepthMask(true); diff --git a/src/game/Skybox.java b/src/game/Skybox.java index 23f51c3..1329371 100644 --- a/src/game/Skybox.java +++ b/src/game/Skybox.java @@ -31,15 +31,15 @@ public Skybox(Camera camera) { @Override public void init(Window window) throws Exception { this.shader = new Shader(); - this.shader.createVertexShader(Utils.loadResource("/res/vertex-3d.vs")); - this.shader.createFragmentShader(Utils.loadResource("/res/fragment-3d.fs")); + this.shader.compileVertex(Utils.loadResource("/res/vertex-3d.vs")); + this.shader.compileFragment(Utils.loadResource("/res/fragment-3d.fs")); this.shader.link(); - this.shader.createUniform("projectionMatrix"); - this.shader.createUniform("modelViewMatrix"); - this.shader.createUniform("texture_sampler"); - this.shader.createUniform("color"); - this.shader.createUniform("isTextured"); + this.shader.create("projectionMatrix"); + this.shader.create("modelViewMatrix"); + this.shader.create("texture_sampler"); + this.shader.create("color"); + this.shader.create("isTextured"); Mesh mesh = ObjLoader.loadMesh("/res/skybox.obj"); mesh.setTexture(new Texture("/res/skybox.png")); @@ -70,8 +70,8 @@ public void render(Window window) { return; this.shader.bind(); - this.shader.setUniform("texture_sampler", 0); - this.shader.setUniform("projectionMatrix", window.getProjectionMatrix()); + this.shader.set("texture_sampler", 0); + this.shader.set("projectionMatrix", window.getProjectionMatrix()); // remove view translation Matrix4f viewMatrix = this.camera.getViewMatrix(); @@ -82,7 +82,7 @@ public void render(Window window) { Matrix4f temp = new Matrix4f(); this.skybox.getMesh().render(this.shader, this.skybox, ($, $$) -> { this.skybox.buildModelViewMatrix(viewMatrix, temp); - this.shader.setUniform("modelViewMatrix", temp); + this.shader.set("modelViewMatrix", temp); }); // undo view translation removal diff --git a/src/game/World.java b/src/game/World.java index e5f5bf5..bc62d07 100644 --- a/src/game/World.java +++ b/src/game/World.java @@ -55,16 +55,16 @@ public World(Mouse mouse, Camera camera) { @Override public void init(Window window) throws Exception { this.shader = new Shader(); - this.shader.createVertexShader(Utils.loadResource("/res/vertex-3d.vs")); - this.shader.createFragmentShader(Utils.loadResource("/res/fragment-3d-block.fs")); + this.shader.compileVertex(Utils.loadResource("/res/vertex-3d.vs")); + this.shader.compileFragment(Utils.loadResource("/res/fragment-3d-block.fs")); this.shader.link(); - this.shader.createUniform("projectionMatrix"); - this.shader.createUniform("modelViewMatrix"); - this.shader.createUniform("texture_sampler"); - this.shader.createUniform("color"); - this.shader.createUniform("isTextured"); - this.shader.createUniform("isSelected"); + this.shader.create("projectionMatrix"); + this.shader.create("modelViewMatrix"); + this.shader.create("texture_sampler"); + this.shader.create("color"); + this.shader.create("isTextured"); + this.shader.create("isSelected"); // Create the blocks' mesh this @@ -167,8 +167,8 @@ public void update(float interval) { @Override public void render(Window window) { this.shader.bind(); - this.shader.setUniform("texture_sampler", 0); - this.shader.setUniform("projectionMatrix", window.getProjectionMatrix()); + this.shader.set("texture_sampler", 0); + this.shader.set("projectionMatrix", window.getProjectionMatrix()); glEnable(GL_CULL_FACE); glCullFace(GL_BACK); @@ -187,8 +187,8 @@ public void render(Window window) { entry.getValue().stream().filter(item -> item.isVisible()), (shader, item) -> { item.buildModelViewMatrix(viewMatrix, temp); - shader.setUniform("modelViewMatrix", temp); - shader.setUniform("isSelected", item.isSelected()); + shader.set("modelViewMatrix", temp); + shader.set("isSelected", item.isSelected()); } ); From d165dd988272e110e45d372b1033a99abfe631bf Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Mon, 29 Jun 2020 11:15:08 -0400 Subject: [PATCH 21/52] Relax exceptions (remove `throws Exception`) Remove `throws Exception` in Initializable.init(Window) Throw unchecked RuntimeExceptions in place of checked ones Display 0 in place of a negative wait time on the Hud --- src/engine/FontTexture.java | 2 +- src/engine/HeightMap.java | 2 +- src/engine/Initializable.java | 2 +- src/engine/Loopable.java | 2 +- src/engine/ObjLoader.java | 2 +- src/engine/Scene.java | 2 +- src/engine/Shader.java | 23 ++++++++++++----------- src/engine/TextItem.java | 2 +- src/engine/Texture.java | 2 +- src/engine/Utils.java | 32 ++++++++++++++++++++------------ src/game/Background.java | 2 +- src/game/Game.java | 2 +- src/game/Hud.java | 4 ++-- src/game/Skybox.java | 2 +- src/game/World.java | 4 ++-- 15 files changed, 47 insertions(+), 38 deletions(-) diff --git a/src/engine/FontTexture.java b/src/engine/FontTexture.java index 71b2dd8..79fdf5d 100644 --- a/src/engine/FontTexture.java +++ b/src/engine/FontTexture.java @@ -15,7 +15,7 @@ public class FontTexture extends Texture { private final int cols; private final int rows; - public FontTexture(String fileName, int cols, int rows) throws Exception { + public FontTexture(String fileName, int cols, int rows) { super(fileName); this.cols = cols; this.rows = rows; diff --git a/src/engine/HeightMap.java b/src/engine/HeightMap.java index 5ab9fe5..1afcfa8 100644 --- a/src/engine/HeightMap.java +++ b/src/engine/HeightMap.java @@ -21,7 +21,7 @@ public HeightMap(ByteBuffer buffer, int width, int length) { this.length = length; } - public static HeightMap loadFromImage(String fileName) throws Exception { + public static HeightMap loadFromImage(String fileName) { int width[] = {0}, length[] = {0}; ByteBuffer buffer = Utils.loadImage(fileName, width, length); return new HeightMap(buffer, width[0], length[0]); diff --git a/src/engine/Initializable.java b/src/engine/Initializable.java index fb4ee26..0078ed3 100644 --- a/src/engine/Initializable.java +++ b/src/engine/Initializable.java @@ -6,6 +6,6 @@ package geetransit.minecraft05.engine; public interface Initializable { - void init(Window window) throws Exception; + void init(Window window); default void cleanup() {} } diff --git a/src/engine/Loopable.java b/src/engine/Loopable.java index 111da71..1f0a5f7 100644 --- a/src/engine/Loopable.java +++ b/src/engine/Loopable.java @@ -7,7 +7,7 @@ public interface Loopable extends Initializable, Inputtable, Updateable, Renderable { @Override - default void init(Window window) throws Exception {} + default void init(Window window) {} @Override default void input(Window window) {} diff --git a/src/engine/ObjLoader.java b/src/engine/ObjLoader.java index 9c8b7f0..8aea324 100644 --- a/src/engine/ObjLoader.java +++ b/src/engine/ObjLoader.java @@ -9,7 +9,7 @@ import org.joml.*; public class ObjLoader { - public static Mesh loadMesh(String file) throws Exception { + public static Mesh loadMesh(String file) { List vertices = new ArrayList<>(); List textures = new ArrayList<>(); List faces = new ArrayList<>(); diff --git a/src/engine/Scene.java b/src/engine/Scene.java index e25f400..19fd3ee 100644 --- a/src/engine/Scene.java +++ b/src/engine/Scene.java @@ -63,7 +63,7 @@ public Scene addFrom(Object obj) { } @Override - public void init(Window window) throws Exception { + public void init(Window window) { for (Initializable init : this.getInits()) init.init(window); } diff --git a/src/engine/Shader.java b/src/engine/Shader.java index 4c4b19b..fbe88bc 100644 --- a/src/engine/Shader.java +++ b/src/engine/Shader.java @@ -8,6 +8,7 @@ import java.util.*; import org.joml.*; import org.lwjgl.system.*; + import static org.lwjgl.opengl.GL20.*; public class Shader { @@ -17,17 +18,17 @@ public class Shader { private int vertex; private int fragment; - public Shader() throws Exception { + public Shader() { this.program = glCreateProgram(); if (this.program == 0) - throw new Exception("Could not create Shader"); + throw new RuntimeException("Could not create Shader"); this.uniforms = new HashMap<>(); } - public void create(String name) throws Exception { + public void create(String name) { int location = glGetUniformLocation(this.program, name); if (location < 0) - throw new Exception("Could not find uniform:" + name); + throw new RuntimeException("Could not find uniform:" + name); this.uniforms.put(name, location); } @@ -50,33 +51,33 @@ public void set(String name, Vector4f value) { glUniform4f(this.uniforms.get(name), value.x, value.y, value.z, value.w); } - public void compileVertex(String code) throws Exception { + public void compileVertex(String code) { this.vertex = this.compile(code, GL_VERTEX_SHADER); } - public void compileFragment(String code) throws Exception { + public void compileFragment(String code) { this.fragment = this.compile(code, GL_FRAGMENT_SHADER); } - protected int compile(String code, int type) throws Exception { + protected int compile(String code, int type) { int id = glCreateShader(type); if (id == 0) - throw new Exception("Error creating shader. Type: " + type); + throw new RuntimeException("Error creating shader. Type: " + type); glShaderSource(id, code); glCompileShader(id); if (glGetShaderi(id, GL_COMPILE_STATUS) == 0) - throw new Exception("Error compiling Shader code: " + glGetShaderInfoLog(id, 1024)); + throw new RuntimeException("Error compiling Shader code: " + glGetShaderInfoLog(id, 1024)); glAttachShader(this.program, id); return id; } - public void link() throws Exception { + public void link() { glLinkProgram(this.program); if (glGetProgrami(this.program, GL_LINK_STATUS) == 0) - throw new Exception("Error linking Shader code: " + glGetProgramInfoLog(this.program, 1024)); + throw new RuntimeException("Error linking Shader code: " + glGetProgramInfoLog(this.program, 1024)); if (this.vertex != 0) glDetachShader(this.program, this.vertex); diff --git a/src/engine/TextItem.java b/src/engine/TextItem.java index 8e61c6f..b9e4614 100644 --- a/src/engine/TextItem.java +++ b/src/engine/TextItem.java @@ -13,7 +13,7 @@ public class TextItem extends Item { private String text; private final FontTexture fontTexture; - public TextItem(String text, FontTexture fontTexture) throws Exception { + public TextItem(String text, FontTexture fontTexture) { super(fontTexture.buildMesh(text)); this.text = text; this.fontTexture = fontTexture; diff --git a/src/engine/Texture.java b/src/engine/Texture.java index 90b61f0..17c9b31 100644 --- a/src/engine/Texture.java +++ b/src/engine/Texture.java @@ -14,7 +14,7 @@ public class Texture { private final int width; private final int length; - public Texture(String fileName) throws Exception { + public Texture(String fileName) { int widthArray[] = {0}; int lengthArray[] = {0}; ByteBuffer image = Utils.loadImage(fileName, widthArray, lengthArray); diff --git a/src/engine/Utils.java b/src/engine/Utils.java index b7f229c..c09b55a 100644 --- a/src/engine/Utils.java +++ b/src/engine/Utils.java @@ -18,15 +18,15 @@ public class Utils { - public static InputStream loadInputStream(String file) throws Exception { + public static InputStream loadInputStream(String file) { InputStream in = Utils.class.getResourceAsStream(file); if (in == null) - throw new Exception("file [" + file + "] does not exist"); + throw new RuntimeException("file [" + file + "] does not exist"); return in; } // source # https://stackoverflow.com/a/17861016 - public static byte[] loadByteArray(String file) throws Exception { + public static byte[] loadByteArray(String file) { try ( InputStream in = loadInputStream(file); ByteArrayOutputStream out = new ByteArrayOutputStream(); @@ -36,26 +36,34 @@ public static byte[] loadByteArray(String file) throws Exception { while ((len = in.read(buffer)) != -1) out.write(buffer, 0, len); return out.toByteArray(); + } catch (IOException e) { + throw new RuntimeException(e); } } - public static String loadResource(String file) throws Exception { + public static String loadResource(String file) { try ( InputStream in = loadInputStream(file); Scanner scanner = new Scanner(in, StandardCharsets.UTF_8.name()); ) { return scanner.useDelimiter("\\A").next(); + } catch (IOException e) { + throw new RuntimeException(e); } } - public static Stream loadLinesStream(String file) throws Exception { - // source # https://stackoverflow.com/a/30336423 - InputStream in = loadInputStream(file); - InputStreamReader isr = new InputStreamReader(in, StandardCharsets.UTF_8.name()); - return new BufferedReader(isr).lines(); + public static Stream loadLinesStream(String file) { + try { + // source # https://stackoverflow.com/a/30336423 + InputStream in = loadInputStream(file); + InputStreamReader isr = new InputStreamReader(in, StandardCharsets.UTF_8.name()); + return new BufferedReader(isr).lines(); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } } - public static ByteBuffer loadImage(String fileName, BiConsumer consumer) throws Exception { + public static ByteBuffer loadImage(String fileName, BiConsumer consumer) { ByteBuffer imageBuffer; ByteBuffer rawBuffer; @@ -71,7 +79,7 @@ public static ByteBuffer loadImage(String fileName, BiConsumer imageBuffer = STBImage.stbi_load_from_memory(rawBuffer, widthBuffer, heightBuffer, channelsBuffer, 4); if (imageBuffer == null) - throw new Exception("Image file [" + fileName + "] not loaded: " + STBImage.stbi_failure_reason()); + throw new RuntimeException("Image file [" + fileName + "] not loaded: " + STBImage.stbi_failure_reason()); // Get width and height of image consumer.accept(widthBuffer.get(), heightBuffer.get()); @@ -79,7 +87,7 @@ public static ByteBuffer loadImage(String fileName, BiConsumer return imageBuffer; } - public static ByteBuffer loadImage(String fileName, int[] widthArray, int[] heightArray) throws Exception { + public static ByteBuffer loadImage(String fileName, int[] widthArray, int[] heightArray) { return loadImage(fileName, (width, height) -> { widthArray[0] = width; heightArray[0] = height; }); } public static void freeImage(ByteBuffer imageBuffer) { diff --git a/src/game/Background.java b/src/game/Background.java index a95bed5..0bd283b 100644 --- a/src/game/Background.java +++ b/src/game/Background.java @@ -19,7 +19,7 @@ public Background() { } @Override - public void init(Window window) throws Exception { + public void init(Window window) { // blank background for first frame window.clearColor(1f, 1f, 1f, 0f); } diff --git a/src/game/Game.java b/src/game/Game.java index d5ae120..734fdd0 100644 --- a/src/game/Game.java +++ b/src/game/Game.java @@ -46,7 +46,7 @@ public Game() { } @Override - public void init(Window window) throws Exception { + public void init(Window window) { System.out.println("LWJGL version: " + Version.getVersion()); System.out.println("OpenGL version: " + GL11.glGetString(GL11.GL_VERSION)); diff --git a/src/game/Hud.java b/src/game/Hud.java index 70d5605..577a276 100644 --- a/src/game/Hud.java +++ b/src/game/Hud.java @@ -39,7 +39,7 @@ public Hud(Mouse mouse, Camera camera, World world) { } @Override - public void init(Window window) throws Exception { + public void init(Window window) { this.shader = new Shader(); this.shader.compileVertex(Utils.loadResource("/res/vertex-2d.vs")); this.shader.compileFragment(Utils.loadResource("/res/fragment-2d.fs")); @@ -73,7 +73,7 @@ public void update(float interval) { this.text.setText(String.format( "vsync=%s mode=%s mouse=%s\nchange=%s wait=%s\ncamera=%s\nmouse=%s", this.window.isVSync(), this.window.getMode(), this.window.getInputMode(GLFW_CURSOR) == GLFW_CURSOR_NORMAL, - this.world.getChange(), this.world.getWait(), + this.world.getChange(), Math.max(0, this.world.getWait()), this.camera, this.mouse )); diff --git a/src/game/Skybox.java b/src/game/Skybox.java index 1329371..a0cdc9d 100644 --- a/src/game/Skybox.java +++ b/src/game/Skybox.java @@ -29,7 +29,7 @@ public Skybox(Camera camera) { } @Override - public void init(Window window) throws Exception { + public void init(Window window) { this.shader = new Shader(); this.shader.compileVertex(Utils.loadResource("/res/vertex-3d.vs")); this.shader.compileFragment(Utils.loadResource("/res/fragment-3d.fs")); diff --git a/src/game/World.java b/src/game/World.java index bc62d07..4461d1f 100644 --- a/src/game/World.java +++ b/src/game/World.java @@ -53,7 +53,7 @@ public World(Mouse mouse, Camera camera) { public float getWait() { return this.countdown.getWait(); } @Override - public void init(Window window) throws Exception { + public void init(Window window) { this.shader = new Shader(); this.shader.compileVertex(Utils.loadResource("/res/vertex-3d.vs")); this.shader.compileFragment(Utils.loadResource("/res/fragment-3d-block.fs")); @@ -204,7 +204,7 @@ public void cleanup() { } // block helpers - private static Mesh loadMesh(String objFileName, String textureFileName) throws Exception { + private static Mesh loadMesh(String objFileName, String textureFileName) { Mesh mesh = ObjLoader.loadMesh(objFileName); mesh.setTexture(new Texture(textureFileName)); return mesh; From f994a7c50aaa54e2099e3eb2e6356413d6d35b19 Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Mon, 29 Jun 2020 11:31:13 -0400 Subject: [PATCH 22/52] Move render distance changing to Skybox class Add more constants in Skybox --- src/game/Skybox.java | 21 ++++++++++++++++++--- src/game/World.java | 11 ----------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/game/Skybox.java b/src/game/Skybox.java index a0cdc9d..8a6d576 100644 --- a/src/game/Skybox.java +++ b/src/game/Skybox.java @@ -8,11 +8,16 @@ import geetransit.minecraft05.engine.*; import java.util.*; -import org.joml.*; +import org.joml.Vector3f; +import org.joml.Matrix4f; import static org.lwjgl.glfw.GLFW.*; public class Skybox implements Loopable { + public static final float RENDER_STEP = 3.0f; // render changed in 1 second + public static final float RENDER_DELAY = 0.5f; // time between skybox toggling + public static final float SKYBOX_SCALE = 0.5f; // skybox scale (multiplied with camera far) + private Camera camera; private Countdown countdown; @@ -21,10 +26,11 @@ public class Skybox implements Loopable { private boolean toggle; private boolean visible; + private int render; public Skybox(Camera camera) { this.camera = camera; - this.countdown = new Countdown(0.5f); + this.countdown = new Countdown(RENDER_DELAY); this.visible = true; } @@ -49,6 +55,13 @@ public void init(Window window) { @Override public void input(Window window) { + // render distance (camera) + this.render = 0; + if (window.isKeyDown(GLFW_KEY_L)) this.camera.setFar(Camera.FAR); + if (window.isKeyDown(GLFW_KEY_RIGHT_BRACKET)) this.render++; + if (window.isKeyDown(GLFW_KEY_LEFT_BRACKET)) this.render--; + + // toggle skybox this.toggle = window.isKeyDown(GLFW_KEY_T); if (!this.toggle) this.countdown.reset(); @@ -56,7 +69,9 @@ public void input(Window window) { @Override public void update(float interval) { - this.skybox.setScale(this.camera.getFar() * 0.5f); + // render distance + this.camera.setFar(Math.max(Camera.NEAR+0.01f, this.camera.getFar() + this.render * interval*RENDER_STEP)); + this.skybox.setScale(this.camera.getFar() * SKYBOX_SCALE); // toggle skybox this.countdown.add(interval); diff --git a/src/game/World.java b/src/game/World.java index 4461d1f..7d21502 100644 --- a/src/game/World.java +++ b/src/game/World.java @@ -17,7 +17,6 @@ public class World implements Loopable { public static final float CHANGE_DELAY = 0.2f; // time between block change (place / remove) public static final float MOVEMENT_STEP = 3.0f; // distance moved in 1 second - public static final float RENDER_STEP = 3.0f; // render changed in 1 second public static final float SPRINT_MULTIPLIER = 1.5f; // sprinting change public static final float BLOCK_SCALE = 0.5f; // block scaling (mesh is 2x2x2) public static final float BLOCK_RADIUS = 2f; // radius around block (for frustum culling) @@ -33,7 +32,6 @@ public class World implements Loopable { private final ClosestItem closestItem; private final Vector3f movement; - private int render; private String change; // ""=air public World(Mouse mouse, Camera camera) { @@ -109,12 +107,6 @@ public void input(Window window) { if (this.movement.length() > 1f) this.movement.div(this.movement.length()); if (SPRINTING && this.movement.z < 0) this.movement.mul(SPRINT_MULTIPLIER); - // render distance (camera) - this.render = 0; - if (window.isKeyDown(GLFW_KEY_L)) this.camera.setFar(Camera.FAR); - if (window.isKeyDown(GLFW_KEY_RIGHT_BRACKET)) this.render++; - if (window.isKeyDown(GLFW_KEY_LEFT_BRACKET)) this.render--; - // placing / removing this.change = null; if (window.isKeyDown(GLFW_KEY_0)) this.change = ""; @@ -128,9 +120,6 @@ public void update(float interval) { // movement this.camera.movePosition(this.movement, interval*MOVEMENT_STEP); - // render distance - this.camera.setFar(Math.max(Camera.NEAR+0.01f, this.camera.getFar() + this.render * interval*RENDER_STEP)); - // placing / removing this.countdown.add(interval); if (this.change != null && this.countdown.nextOnce()) { From b7e5afb66dc60f65eda5e601221e2505d56fc20a Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Tue, 30 Jun 2020 05:06:39 -0400 Subject: [PATCH 23/52] ObjLoader refactor Print warning if texture coord redefined --- src/engine/ObjLoader.java | 121 +++++++++++++++++++++----------------- 1 file changed, 68 insertions(+), 53 deletions(-) diff --git a/src/engine/ObjLoader.java b/src/engine/ObjLoader.java index 8aea324..d1358de 100644 --- a/src/engine/ObjLoader.java +++ b/src/engine/ObjLoader.java @@ -10,16 +10,17 @@ public class ObjLoader { public static Mesh loadMesh(String file) { - List vertices = new ArrayList<>(); - List textures = new ArrayList<>(); - List faces = new ArrayList<>(); + List posList = new ArrayList<>(); + List indexList = new ArrayList<>(); + List coordList = new ArrayList<>(); + List faceList = new ArrayList<>(); Utils.loadLinesStream(file).forEach(line -> { String[] tokens = line.split("\\s+"); switch (tokens[0]) { case "v": // Geometric vertex - vertices.add(new Vector3f( + posList.add(new Vector3f( Float.parseFloat(tokens[1]), Float.parseFloat(tokens[2]), Float.parseFloat(tokens[3]) @@ -27,105 +28,119 @@ public static Mesh loadMesh(String file) { break; case "vt": // Texture coordinate - textures.add(new Vector2f( + coordList.add(new Vector2f( Float.parseFloat(tokens[1]), Float.parseFloat(tokens[2]) )); break; case "f": Face face = new Face(tokens[1], tokens[2], tokens[3]); - faces.add(face); + faceList.add(face); break; default: // Ignore other lines break; } }); - return reorderLists(vertices, textures, faces); - } - private static Mesh reorderLists( - List vertexList, - List coordList, - List faceList - ) { - List posList = new ArrayList<>(); // Create position array in the order it has been declared - float[] posArray = new float[vertexList.size() * 3]; - for (int i = 0; i < vertexList.size(); i++) { - Vector3f pos = vertexList.get(i); + float[] posArray = new float[posList.size() * 3]; + float[] coordArray = new float[posList.size() * 2]; + for (int i = 0; i < posList.size(); i++) { + Vector3f pos = posList.get(i); posArray[i*3 + 0] = pos.x; posArray[i*3 + 1] = pos.y; posArray[i*3 + 2] = pos.z; } - float[] coordArray = new float[vertexList.size() * 2]; - - for (Face face : faceList) - for (IndexGroup group : face.groups) - processFaceVertex(group, coordList, posList, coordArray); + for (int i = 0; i < coordArray.length; i++) + coordArray[i] = -1; + + for (Face face : faceList) { + for (Group group : face.groups) { + int index = group.index - 1; + + // Set pos for vertex coordinates + indexList.add(index); + + // Reorder texture coordinates + if (group.coord != Group.NO_VALUE) { + int coord = group.coord - 1; + Vector2f coordVec = coordList.get(coord); + + float cX = coordVec.x; + float cY = 1 - coordVec.y; + float aX = coordArray[index*2 + 0]; + float aY = coordArray[index*2 + 1]; + + if ((aX != -1 && aX != cX) || (aX != -1 && aY != cY)) + System.err.println( + "ObjLoader texture coord already defined ["+aX+","+aY+"]: " + +file+" f "+group+" ["+cX+","+cY+"]" + ); + coordArray[index*2 + 0] = cX; + coordArray[index*2 + 1] = cY; + } + } + } - // int[] indexArray = new int[indices.size()]; - int[] indexArray = Utils.intListToArray(posList); + int[] indexArray = Utils.intListToArray(indexList); return new Mesh(posArray, indexArray, coordArray); } - private static void processFaceVertex( - IndexGroup group, - List coordList, - List posList, - float[] coordArray - ) { - // Set pos for vertex coordinates - int pos = group.pos; - posList.add(pos); - - // Reorder texture coordinates - if (group.coord != IndexGroup.NO_VALUE) { - Vector2f coord = coordList.get(group.coord); - coordArray[pos*2 + 0] = coord.x; - coordArray[pos*2 + 1] = 1 - coord.y; - } - } - - protected static class IndexGroup { + protected static class Group { public static final int NO_VALUE = -1; - public int pos; + public int index; public int coord; + public int normal; - public IndexGroup() { - this.pos = NO_VALUE; + public Group() { + this.index = NO_VALUE; this.coord = NO_VALUE; + this.normal = NO_VALUE; + } + + public String toString() { + return ( + this.index + +"/"+(this.coord != NO_VALUE ? this.coord : "") + +"/"+(this.normal != NO_VALUE ? this.normal : "") + ); } } protected static class Face { // List of pos groups for a face triangle (3 vertices per face). - public final IndexGroup[] groups; + public final Group[] groups; public Face(String v1, String v2, String v3) { - this.groups = new IndexGroup[3]; + this.groups = new Group[3]; // Parse the lines this.groups[0] = parseLine(v1); this.groups[1] = parseLine(v2); this.groups[2] = parseLine(v3); } - private IndexGroup parseLine(String line) { - IndexGroup group = new IndexGroup(); + private static Group parseLine(String line) { + Group group = new Group(); String[] tokens = line.split("/"); int length = tokens.length; - group.pos = Integer.parseInt(tokens[0]) - 1; + group.index = Integer.parseInt(tokens[0]); if (length <= 1) return group; - // It can be empty if the obj does not define text coords + // can be empty if (tokens[1].length() != 0) - group.coord = Integer.parseInt(tokens[1]) - 1; + group.coord = Integer.parseInt(tokens[1]); if (length <= 2) return group; + if (tokens[2].length() != 0) + group.normal = Integer.parseInt(tokens[2]); + if (length <= 3) + return group; + return group; } } From 3fb456d0c8ca6a68315d1f8f09e92159d8c990de Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Tue, 30 Jun 2020 05:09:13 -0400 Subject: [PATCH 24/52] Improve OBJ files Re-formatting Rename cube -> cube-fblr,u,d (sides, top, bottom) New cube-fblrud.obj (for cobbleblock where all sides are the same) Smaller cobbleblock.png --- res/cobbleblock.png | Bin 195 -> 157 bytes res/cube-fblr,u,d.obj | 94 ++++++++++++++++++++++++++++++++++++++++++ res/cube-fblrud.obj | 86 ++++++++++++++++++++++++++++++++++++++ res/cube.obj | 55 ------------------------ res/skybox.obj | 94 +++++++++++++++++++++--------------------- src/game/World.java | 4 +- 6 files changed, 229 insertions(+), 104 deletions(-) create mode 100644 res/cube-fblr,u,d.obj create mode 100644 res/cube-fblrud.obj delete mode 100644 res/cube.obj diff --git a/res/cobbleblock.png b/res/cobbleblock.png index 7afd1fab2e40cfa6b253b2cbaeeeca1554840149..98c9359332083842a95c8042d14857a4f55f59ed 100644 GIT binary patch delta 113 zcmX@iIG0heGr-TCmrII^fq{Y7)59eQNDF{42OE%-|NK93qN1t0k*AAeh=qUhn)U1d zpLbwnkbdy!5!2I2Y~SA8JbZv9buzCTuY@TBiyLplGdGRdJO>UiFxc{QO?oa=lm#?} N!PC{xWt~$(695KUBCG%a delta 151 zcmbQsc$iVKGr-TCmrII^fq{Y7)59eQNGpIa2OE$quB!SnQPDIv!qdeuB*OjewT-+$ z8JEBkvu{lYZ}gUVJ;KI7<1GnfOe%Dfj6e7@$+{F}F#fF=OJpN)N+ t?T_Bwym#pz`4vnREnvX~vKRLBxtj5{$}ElzTCyG_=IQF^vd$@?2>|j;GvNRL diff --git a/res/cube-fblr,u,d.obj b/res/cube-fblr,u,d.obj new file mode 100644 index 0000000..86b74cb --- /dev/null +++ b/res/cube-fblr,u,d.obj @@ -0,0 +1,94 @@ +# vertices : cube +# 3---7 +# |\ \ +# 1 4-5-8 +# \| | +# 2---6 +v -1 -1 -1 +v -1 -1 1 +v -1 1 -1 +v -1 1 1 +v 1 -1 -1 +v 1 -1 1 +v 1 1 -1 +v 1 1 1 +# 9-16 (1-8) +v -1 -1 -1 +v -1 -1 1 +v -1 1 -1 +v -1 1 1 +v 1 -1 -1 +v 1 -1 1 +v 1 1 -1 +v 1 1 1 +# 17-24 (1-8) +v -1 -1 -1 +v -1 -1 1 +v -1 1 -1 +v -1 1 1 +v 1 -1 -1 +v 1 -1 1 +v 1 1 -1 +v 1 1 1 + +# texture +# side bottom +# top +# 3 6 9 +# 2 5 8 +# 1 4 7 +vt 0.0 0.0 +vt 0.0 0.5 +vt 0.0 1.0 +vt 0.5 0.0 +vt 0.5 0.5 +vt 0.5 1.0 +vt 1.0 0.0 +vt 1.0 0.5 +vt 1.0 1.0 + +# normals : -z z -y y -x x +# back front down up left right +vn 0 0 -1 +vn 0 0 1 +vn 0 -1 0 +vn 0 1 0 +vn -1 0 0 +vn 1 0 0 + +# faces : back front down up left right +# back +# 7 3 +# 5 1 +f 7/3/1 5/2/1 1/5/1 +f 7/3/1 1/5/1 3/6/1 + +# front +# 4 8 +# 2 6 +f 4/3/2 2/2/2 6/5/2 +f 4/3/2 6/5/2 8/6/2 + +# down +# 2 6 +# 1 5 +f 10/6/3 9/5/3 13/8/3 +f 10/6/3 13/8/3 14/9/3 + +# up +# 3 7 +# 4 8 +f 11/2/4 12/1/4 16/4/4 +f 11/2/4 16/4/4 15/5/4 + +# left +# 3 4 +# 1 2 +f 19/3/5 17/2/5 18/5/5 +f 19/3/5 18/5/5 20/6/5 + +# right +# 8 7 +# 6 5 +f 24/3/6 22/2/6 21/5/6 +f 24/3/6 21/5/6 23/6/6 diff --git a/res/cube-fblrud.obj b/res/cube-fblrud.obj new file mode 100644 index 0000000..1a7632d --- /dev/null +++ b/res/cube-fblrud.obj @@ -0,0 +1,86 @@ +# vertices : cube +# 3---7 +# |\ \ +# 1 4-5-8 +# \| | +# 2---6 +v -1 -1 -1 +v -1 -1 1 +v -1 1 -1 +v -1 1 1 +v 1 -1 -1 +v 1 -1 1 +v 1 1 -1 +v 1 1 1 +# 9-16 (1-8) +v -1 -1 -1 +v -1 -1 1 +v -1 1 -1 +v -1 1 1 +v 1 -1 -1 +v 1 -1 1 +v 1 1 -1 +v 1 1 1 +# 17-24 (1-8) +v -1 -1 -1 +v -1 -1 1 +v -1 1 -1 +v -1 1 1 +v 1 -1 -1 +v 1 -1 1 +v 1 1 -1 +v 1 1 1 + +# texture +# 2 4 +# 1 3 +vt 0.0 0.0 +vt 0.0 1.0 +vt 1.0 0.0 +vt 1.0 1.0 + +# normals : -z z -y y -x x +# back front down up left right +vn 0 0 -1 +vn 0 0 1 +vn 0 -1 0 +vn 0 1 0 +vn -1 0 0 +vn 1 0 0 + +# faces : back front down up left right +# back +# 7 3 +# 5 1 +f 7/2/1 5/1/1 1/3/1 +f 7/2/1 1/3/1 3/4/1 + +# front +# 4 8 +# 2 6 +f 4/2/2 2/1/2 6/3/2 +f 4/2/2 6/3/2 8/4/2 + +# down +# 2 6 +# 1 5 +f 10/2/3 9/1/3 13/3/3 +f 10/2/3 13/3/3 14/4/3 + +# up +# 3 7 +# 4 8 +f 11/2/4 12/1/4 16/3/4 +f 11/2/4 16/3/4 15/4/4 + +# left +# 3 4 +# 1 2 +f 19/2/5 17/1/5 18/3/5 +f 19/2/5 18/3/5 20/4/5 + +# right +# 8 7 +# 6 5 +f 24/2/6 22/1/6 21/3/6 +f 24/2/6 21/3/6 23/4/6 diff --git a/res/cube.obj b/res/cube.obj deleted file mode 100644 index 8a6eee0..0000000 --- a/res/cube.obj +++ /dev/null @@ -1,55 +0,0 @@ -v 1.0 -1.0 -1.0 -v 1.0 -1.0 1.0 -v -1.0 -1.0 1.0 -v -1.0 -1.0 -1.0 -v 1.0 1.0 -1.0 -v 1.0 1.0 1.0 -v -1.0 1.0 1.0 -v -1.0 1.0 -1.0 -v 1.0 -1.0 -1.0 -v 1.0 -1.0 -1.0 -v 1.0 -1.0 1.0 -v 1.0 -1.0 1.0 -v -1.0 -1.0 -1.0 -v -1.0 -1.0 -1.0 -v 1.0 1.0 -1.0 -v 1.0 1.0 -1.0 -v -1.0 -1.0 1.0 -v -1.0 -1.0 1.0 -v 1.0 1.0 1.0 -v 1.0 1.0 1.0 -v -1.0 1.0 1.0 -v -1.0 1.0 1.0 -v -1.0 1.0 -1.0 -v -1.0 1.0 -1.0 - -vt 0.5 1.0 -vt 0.5 0.5 -vt 1.0 0.5 -vt 0.0 0.5 -vt 0.0 0.0 -vt 0.5 0.0 -vt 0.0 1.0 -vt 0.0 1.0 -vt 0.5 1.0 -vt 1.0 1.0 - -vn 0.0 -1.0 0.0 -vn 0.0 1.0 0.0 -vn 1.0 0.0 0.0 -vn 0.0 0.0 1.0 -vn -1.0 0.0 0.0 -vn 0.0 0.0 -1.0 - -f 11/1/1 17/2/1 13/3/1 -f 24/4/2 22/5/2 20/6/2 -f 15/1/3 19/7/3 12/4/3 -f 6/1/4 21/8/4 18/4/4 -f 3/2/5 7/9/5 23/7/5 -f 1/4/6 4/2/6 8/1/6 -f 9/10/1 11/1/1 13/3/1 -f 16/2/2 24/4/2 20/6/2 -f 10/2/3 15/1/3 12/4/3 -f 2/2/4 6/1/4 18/4/4 -f 14/4/5 3/2/5 23/7/5 -f 5/7/6 1/4/6 8/1/6 diff --git a/res/skybox.obj b/res/skybox.obj index afc5063..1147484 100644 --- a/res/skybox.obj +++ b/res/skybox.obj @@ -1,86 +1,86 @@ # Front Face -v -1.0 1.0 1.0 -v -1.0 -1.0 1.0 -v 1.0 -1.0 1.0 -v 1.0 1.0 1.0 +v -1 1 1 +v -1 -1 1 +v 1 -1 1 +v 1 1 1 # Left Face -v -1.0 1.0 1.0 -v -1.0 -1.0 1.0 -v -1.0 -1.0 -1.0 -v -1.0 1.0 -1.0 +v -1 1 1 +v -1 -1 1 +v -1 -1 -1 +v -1 1 -1 # Right Face -v 1.0 1.0 1.0 -v 1.0 -1.0 1.0 -v 1.0 -1.0 -1.0 -v 1.0 1.0 -1.0 +v 1 1 1 +v 1 -1 1 +v 1 -1 -1 +v 1 1 -1 # Back Face -v -1.0 1.0 -1.0 -v -1.0 -1.0 -1.0 -v 1.0 -1.0 -1.0 -v 1.0 1.0 -1.0 +v -1 1 -1 +v -1 -1 -1 +v 1 -1 -1 +v 1 1 -1 # Top Face -v -1.0 1.0 -1.0 -v -1.0 1.0 1.0 -v 1.0 1.0 1.0 -v 1.0 1.0 -1.0 +v -1 1 -1 +v -1 1 1 +v 1 1 1 +v 1 1 -1 # Bottom Face -v -1.0 -1.0 -1.0 -v -1.0 -1.0 1.0 -v 1.0 -1.0 1.0 -v 1.0 -1.0 -1.0 +v -1 -1 -1 +v -1 -1 1 +v 1 -1 1 +v 1 -1 -1 # Front Face vt 0.333333 0.5 -vt 0.333333 0.0 -vt 0.0 0.0 -vt 0.0 0.5 +vt 0.333333 0 +vt 0 0 +vt 0 0.5 # Left Face -vt 0.0 1.0 -vt 0.0 0.5 +vt 0 1 +vt 0 0.5 vt 0.333333 0.5 -vt 0.333333 1.0 +vt 0.333333 1 # Right Face -vt 1.0 1.0 -vt 1.0 0.5 +vt 1 1 +vt 1 0.5 vt 0.666666 0.5 -vt 0.666666 1.0 +vt 0.666666 1 # Back Face -vt 0.333333 1.0 +vt 0.333333 1 vt 0.333333 0.5 vt 0.666666 0.5 -vt 0.666666 1.0 +vt 0.666666 1 # Top Face -vt 0.335 0.5 +vt 0.335 0.5 vt 0.666666 0.5 -vt 0.666666 0.0 -vt 0.335 0.0 +vt 0.666666 0 +vt 0.335 0 # Bottom Face vt 0.666666 0.5 -vt 0.666666 0.0 -vt 1.0 0.0 -vt 1.0 0.5 +vt 0.666666 0 +vt 1 0 +vt 1 0.5 # Front Face -f 1/1/ 2/2/ 3/3/ -f 4/4/ 1/1/ 3/3/ +f 1/1/ 2/2/ 3/3/ +f 4/4/ 1/1/ 3/3/ # Left Face -f 5/5/ 6/6/ 7/7/ -f 8/8/ 5/5/ 7/7/ +f 5/5/ 6/6/ 7/7/ +f 8/8/ 5/5/ 7/7/ # Right Face -f 9/9/ 10/10/ 11/11/ -f 12/12/ 9/9/ 11/11/ +f 9/9/ 10/10/ 11/11/ +f 12/12/ 9/9/ 11/11/ # Back Face f 13/13/ 14/14/ 15/15/ diff --git a/src/game/World.java b/src/game/World.java index 7d21502..33bf4ba 100644 --- a/src/game/World.java +++ b/src/game/World.java @@ -66,8 +66,8 @@ public void init(Window window) { // Create the blocks' mesh this - .putMesh("grassblock", this.loadMesh("/res/cube.obj", "/res/grassblock.png")) - .putMesh("cobbleblock", this.loadMesh("/res/cube.obj", "/res/cobbleblock.png")); + .putMesh("grassblock", this.loadMesh("/res/cube-fblr,u,d.obj", "/res/grassblock.png")) + .putMesh("cobbleblock", this.loadMesh("/res/cube-fblrud.obj", "/res/cobbleblock.png")) // get heightmap try (HeightMap map = HeightMap.loadFromImage("/res/heightmap.png")) { From 1251b106d72946313fe60168d917ba38b3e9b69e Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Tue, 30 Jun 2020 05:09:39 -0400 Subject: [PATCH 25/52] More constants --- src/game/Background.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/game/Background.java b/src/game/Background.java index 0bd283b..472b8b1 100644 --- a/src/game/Background.java +++ b/src/game/Background.java @@ -10,6 +10,8 @@ import static org.lwjgl.glfw.GLFW.*; public class Background implements Loopable { + public static final float COLOR_STEP = 0.3f; // how much colour changed in 1 second + private int direction; private float color; @@ -35,7 +37,7 @@ public void input(Window window) { @Override public void update(float interval) { - this.color = Math.max(0f, Math.min(1f, this.color+30*interval*0.01f*this.direction)); + this.color = Math.max(0f, Math.min(1f, this.color + this.direction * interval*COLOR_STEP)); } @Override From cb2fbbbd3a01d2225c223829d63ed39cd72ee60a Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Tue, 30 Jun 2020 23:10:20 -0400 Subject: [PATCH 26/52] Add glass block texture --- res/glassblock.png | Bin 0 -> 194 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 res/glassblock.png diff --git a/res/glassblock.png b/res/glassblock.png new file mode 100644 index 0000000000000000000000000000000000000000..f184a13caa20537e3b756a5edfcc404d861b14e5 GIT binary patch literal 194 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`ot`d^Ar}5uCm!T&P~dT8l%CUU zJLk~en4?oIb~Y!8e&%;s?8f+2;rgfe_~@Jc!OC(roDQC=&L;BMTz}6zXEsmH7QHuf zuO3uT<@uqhu&mhNcN%+nasF$2TO;)dg?x8Li Date: Tue, 30 Jun 2020 23:50:37 -0400 Subject: [PATCH 27/52] Add glass block (transparent) New "groups" of block names (addGroup) Block item maps changed (map -> map) Merge addBlock,newBlock -> addBlock Add reordering of block items using distance from camera (reorderBlock) --- src/game/World.java | 168 ++++++++++++++++++++++++++++++++------------ 1 file changed, 124 insertions(+), 44 deletions(-) diff --git a/src/game/World.java b/src/game/World.java index 33bf4ba..abb30f9 100644 --- a/src/game/World.java +++ b/src/game/World.java @@ -26,8 +26,10 @@ public class World implements Loopable { private final Countdown countdown; private Shader shader; - private final Map meshMap; - private final Map> blockMap; + private final Map meshMap; // name -> mesh + private final Map> groupMap; // group -> list + private final Map orderMap; // group -> ordered + private final Map> blockMap; // group -> list private final List blockList; private final ClosestItem closestItem; @@ -40,6 +42,8 @@ public World(Mouse mouse, Camera camera) { this.countdown = new Countdown(CHANGE_DELAY); this.meshMap = new HashMap<>(); + this.groupMap = new HashMap<>(); + this.orderMap = new HashMap<>(); this.blockMap = new HashMap<>(); this.blockList = new ArrayList<>(); @@ -64,10 +68,15 @@ public void init(Window window) { this.shader.create("isTextured"); this.shader.create("isSelected"); - // Create the blocks' mesh - this - .putMesh("grassblock", this.loadMesh("/res/cube-fblr,u,d.obj", "/res/grassblock.png")) - .putMesh("cobbleblock", this.loadMesh("/res/cube-fblrud.obj", "/res/cobbleblock.png")) + // create groups and block meshes + this.addGroup("grass", false); + this.putMesh("grass", "grassblock", this.loadMesh("/res/cube-fblr,u,d.obj", "/res/grassblock.png")); + + this.addGroup("cobble", false); + this.putMesh("cobble", "cobbleblock", this.loadMesh("/res/cube-fblrud.obj", "/res/cobbleblock.png")); + + this.addGroup("glass", true); + this.putMesh("glass", "glassblock", this.loadMesh("/res/cube-fblrud.obj", "/res/glassblock.png")); // get heightmap try (HeightMap map = HeightMap.loadFromImage("/res/heightmap.png")) { @@ -75,19 +84,18 @@ public void init(Window window) { for (int x = 0; x < map.width; x++) { for (int z = 0; z < map.length; z++) { int y = (int) map.compressExpand(map.heightAt(x, z), 0, map.MAX_COLOR, 0, 16); - this.addBlock(this.newBlock("grassblock").setPosition(x, y, z)); + this.addBlock("grassblock", x, y, z); for (int k = y-1; k >= Math.max(y-2, 0); k--) - this.addBlock(this.newBlock("cobbleblock").setPosition(x, k, z)); + this.addBlock("cobbleblock", x, k, z); } } } // add spawn markers (-2z is forwards) - this - .addBlock(this.newBlock("grassblock").setPosition(+1, +1, 0)) - .addBlock(this.newBlock("grassblock").setPosition(-1, +1, 0)) - .addBlock(this.newBlock("grassblock").setPosition( 0, +1, +1)) - .addBlock(this.newBlock("grassblock").setPosition( 0, +1, -2)); + this.addBlock("grassblock", +1, +1, 0); + this.addBlock("grassblock", -1, +1, 0); + this.addBlock("grassblock", 0, +1, +1); + this.addBlock("grassblock", 0, +1, -2); } @Override @@ -112,6 +120,7 @@ public void input(Window window) { if (window.isKeyDown(GLFW_KEY_0)) this.change = ""; if (window.isKeyDown(GLFW_KEY_1)) this.change = "grassblock"; if (window.isKeyDown(GLFW_KEY_2)) this.change = "cobbleblock"; + if (window.isKeyDown(GLFW_KEY_3)) this.change = "glassblock"; if (this.change == null) this.countdown.reset(); } @@ -119,10 +128,14 @@ public void input(Window window) { public void update(float interval) { // movement this.camera.movePosition(this.movement, interval*MOVEMENT_STEP); + if (!this.movement.equals(0, 0, 0)) + for (Map.Entry entry : this.orderMap.entrySet()) + if (entry.getValue()) + this.reorderBlock(entry.getKey()); - // placing / removing this.countdown.add(interval); if (this.change != null && this.countdown.nextOnce()) { + // placing / removing this.closestItem.update(this.blockList, this.camera); if (this.closestItem.closest != null) { if (this.change.equals("")) { @@ -139,7 +152,7 @@ public void update(float interval) { if (block.getPosition().equals(position)) break check; // else - this.addBlock(this.newBlock(this.change).setPosition(position)); + this.addBlock(this.change, position); } } } @@ -161,25 +174,27 @@ public void render(Window window) { glEnable(GL_CULL_FACE); glCullFace(GL_BACK); - // view - Matrix4f viewMatrix = this.camera.getViewMatrix(); - // update visible blocks for (BlockItem block : this.blockList) block.setVisible(this.camera.insideFrustum(block.getPosition(), BLOCK_RADIUS*block.getScale())); - // draw blocks - Matrix4f temp = new Matrix4f(); - for (Map.Entry> entry : this.blockMap.entrySet()) - entry.getKey().render( - this.shader, - entry.getValue().stream().filter(item -> item.isVisible()), - (shader, item) -> { - item.buildModelViewMatrix(viewMatrix, temp); - shader.set("modelViewMatrix", temp); - shader.set("isSelected", item.isSelected()); - } - ); + // reorder ordered groups + for (String group : this.blockMap.keySet()) + if (this.orderMap.get(group)) + this.reorderBlock(group); + + Matrix4f viewMatrix = this.camera.getViewMatrix(); // view matrix + Matrix4f temp = new Matrix4f(); // temporary matrix (stores model view matrix) + + // opaque blocks + for (String group : this.groupMap.keySet()) + if (!this.orderMap.get(group)) + this.renderBlock(group, viewMatrix, temp); + + // transparent blocks + for (String group : this.groupMap.keySet()) + if (this.orderMap.get(group)) + this.renderBlock(group, viewMatrix, temp); glDisable(GL_CULL_FACE); this.shader.unbind(); @@ -199,9 +214,27 @@ private static Mesh loadMesh(String objFileName, String textureFileName) { return mesh; } - private World putMesh(String name, Mesh mesh) { + private void putMesh(String group, String name, Mesh mesh) { + // mesh + if (this.meshMap.containsKey(name)) + throw new RuntimeException("mesh already defined: "+name); this.meshMap.put(name, mesh); - return this; + + // group + if (!this.groupMap.containsKey(group)) + throw new RuntimeException("group not defined: "+group); + if (!this.groupMap.get(group).contains(name)) + this.groupMap.get(group).add(name); + } + + private void addGroup(String group, boolean order) { this.addGroup(group, order, false); } + private void addGroup(String group, boolean order, boolean redefine) { + if (!redefine && this.groupMap.containsKey(group)) + throw new RuntimeException("group already defined: "+group); + + this.groupMap.put(group, new ArrayList<>()); + this.orderMap.put(group, order); + this.blockMap.put(group, new ArrayList<>()); } private BlockItem newBlock(String name) { @@ -209,21 +242,68 @@ private BlockItem newBlock(String name) { return new BlockItem(mesh).setScale(BLOCK_SCALE); } - private World addBlock(BlockItem block) { - Mesh mesh = block.getMesh(); + private void renderBlock(String group, Matrix4f viewMatrix, Matrix4f temp) { + List blocks = this.blockMap.get(group); + if (this.groupMap.get(group).size() == 1) { + if (blocks.size() > 0){ + blocks.get(0).getMesh().render( + this.shader, + blocks.stream().filter(block -> block.isVisible()), + (shader, block) -> { + block.buildModelViewMatrix(viewMatrix, temp); + shader.set("modelViewMatrix", temp); + shader.set("isSelected", block.isSelected()); + } + ); + } + } else { + for (BlockItem block : blocks) + block.getMesh().render(this.shader, block, (shader, block2) -> { + block.buildModelViewMatrix(viewMatrix, temp); + shader.set("modelViewMatrix", temp); + shader.set("isSelected", block2.isSelected()); + }); + } + } + + private String nameOf(BlockItem block) { + for (Map.Entry entry : this.meshMap.entrySet()) + if (entry.getValue() == block.getMesh()) + return entry.getKey(); + throw new RuntimeException("could not find parent name: "+block); + } + private String groupOf(String name) { + for (Map.Entry> entry : this.groupMap.entrySet()) + if (entry.getValue().contains(name)) + return entry.getKey(); + throw new RuntimeException("could not find parent group: "+name); + } + + private void addBlock(String name, Vector3f position) { this.addBlock(name, position.x, position.y, position.z); } + private void addBlock(String name, float x, float y, float z) { + Mesh mesh = this.meshMap.get(name); + BlockItem block = new BlockItem(mesh); + block.setScale(BLOCK_SCALE); + block.setPosition(x, y, z); + + String group = this.groupOf(name); this.blockList.add(block); - if (!this.blockMap.containsKey(mesh)) - this.blockMap.put(mesh, new ArrayList<>()); - this.blockMap.get(mesh).add(block); - return this; + this.blockMap.get(group).add(block); + if (this.orderMap.get(group)) + this.reorderBlock(group); + } + + private void reorderBlock(String group) { + Vector3f cameraPosition = this.camera.getPosition(); + this.blockMap.get(group).sort(Comparator.comparingDouble( + block -> block.getPosition().distance(cameraPosition) + )); } - private World removeBlock(BlockItem block) { - Mesh mesh = block.getMesh(); + private void removeBlock(BlockItem block) { + String name = this.nameOf(block); + String group = this.groupOf(name); this.blockList.remove(block); - this.blockMap.get(mesh).remove(block); - if (this.blockMap.get(mesh).size() == 0) - this.blockMap.remove(mesh); - return this; + this.blockMap.get(group).remove(block); } } From c04dbf5ce02d190b91aabe91456ed056b62dded5 Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Wed, 1 Jul 2020 01:12:12 -0400 Subject: [PATCH 28/52] Update README.md from master and add glass block placing --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index f28ade3..fb7bef7 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,9 @@ Book # https://ahbejarano.gitbook.io/lwjglgamedev | Space | Move up | | Shift | Move down | | 0 | Destroy the highlighted block | +| 1 | Place a grass block | +| 2 | Place a cobble block | +| 3 | Place a glass block | | Ctrl | Faster (forwards and backwards only) | | Alt+F4 | Exit game | | Esc | Exit fullscreen and display mouse | From 658b15469a3f22f41a472c0bfe38e8290956b4b0 Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Wed, 1 Jul 2020 14:14:35 -0400 Subject: [PATCH 29/52] Rename World -> View --- src/game/Game.java | 8 ++++---- src/game/Hud.java | 8 ++++---- src/game/{World.java => View.java} | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) rename src/game/{World.java => View.java} (98%) diff --git a/src/game/Game.java b/src/game/Game.java index 734fdd0..6e610d1 100644 --- a/src/game/Game.java +++ b/src/game/Game.java @@ -20,7 +20,7 @@ public class Game extends Scene { private Background background; private Skybox skybox; - private World world; + private View view; private Hud hud; public Game() { @@ -36,12 +36,12 @@ public Game() { // child scenes this.background = new Background(); this.skybox = new Skybox(this.camera); - this.world = new World(this.mouse, this.camera); - this.hud = new Hud(this.mouse, this.camera, this.world); + this.view = new View(this.mouse, this.camera); + this.hud = new Hud(this.mouse, this.camera, this.view); this .addFrom(this.background) .addFrom(this.skybox) - .addFrom(this.world) + .addFrom(this.view) .addFrom(this.hud); } diff --git a/src/game/Hud.java b/src/game/Hud.java index 577a276..372ffc2 100644 --- a/src/game/Hud.java +++ b/src/game/Hud.java @@ -20,7 +20,7 @@ public class Hud implements Loopable { private Mouse mouse; private Camera camera; - private World world; + private View view; private Window window; private Shader shader; @@ -30,10 +30,10 @@ public class Hud implements Loopable { private Item compass; private Item crosshair; - public Hud(Mouse mouse, Camera camera, World world) { + public Hud(Mouse mouse, Camera camera, View view) { this.mouse = mouse; this.camera = camera; - this.world = world; + this.view = view; this.items = new ArrayList<>(); } @@ -73,7 +73,7 @@ public void update(float interval) { this.text.setText(String.format( "vsync=%s mode=%s mouse=%s\nchange=%s wait=%s\ncamera=%s\nmouse=%s", this.window.isVSync(), this.window.getMode(), this.window.getInputMode(GLFW_CURSOR) == GLFW_CURSOR_NORMAL, - this.world.getChange(), Math.max(0, this.world.getWait()), + this.view.getChange(), Math.max(0, this.view.getWait()), this.camera, this.mouse )); diff --git a/src/game/World.java b/src/game/View.java similarity index 98% rename from src/game/World.java rename to src/game/View.java index abb30f9..264856a 100644 --- a/src/game/World.java +++ b/src/game/View.java @@ -1,6 +1,6 @@ /* George Zhang -World scene implementation. +World view class. */ package geetransit.minecraft05.game; @@ -14,7 +14,7 @@ import static org.lwjgl.glfw.GLFW.*; import static org.lwjgl.opengl.GL11.*; -public class World implements Loopable { +public class View implements Loopable { public static final float CHANGE_DELAY = 0.2f; // time between block change (place / remove) public static final float MOVEMENT_STEP = 3.0f; // distance moved in 1 second public static final float SPRINT_MULTIPLIER = 1.5f; // sprinting change @@ -36,7 +36,7 @@ public class World implements Loopable { private final Vector3f movement; private String change; // ""=air - public World(Mouse mouse, Camera camera) { + public View(Mouse mouse, Camera camera) { this.mouse = mouse; this.camera = camera; this.countdown = new Countdown(CHANGE_DELAY); From fb4b312c6a389322c969b74c5d67a35ac2b75d86 Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Wed, 1 Jul 2020 14:15:41 -0400 Subject: [PATCH 30/52] Disable depth test for Skybox --- src/game/Skybox.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/game/Skybox.java b/src/game/Skybox.java index 8a6d576..af596c6 100644 --- a/src/game/Skybox.java +++ b/src/game/Skybox.java @@ -12,6 +12,7 @@ import org.joml.Matrix4f; import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.opengl.GL11.*; public class Skybox implements Loopable { public static final float RENDER_STEP = 3.0f; // render changed in 1 second @@ -71,7 +72,6 @@ public void input(Window window) { public void update(float interval) { // render distance this.camera.setFar(Math.max(Camera.NEAR+0.01f, this.camera.getFar() + this.render * interval*RENDER_STEP)); - this.skybox.setScale(this.camera.getFar() * SKYBOX_SCALE); // toggle skybox this.countdown.add(interval); @@ -88,6 +88,10 @@ public void render(Window window) { this.shader.set("texture_sampler", 0); this.shader.set("projectionMatrix", window.getProjectionMatrix()); + // disable depth testing (very back) + glDepthMask(false); + glDisable(GL_DEPTH_TEST); + // remove view translation Matrix4f viewMatrix = this.camera.getViewMatrix(); Vector3f oldTanslation = viewMatrix.getTranslation(new Vector3f()); @@ -103,6 +107,8 @@ public void render(Window window) { // undo view translation removal viewMatrix.setTranslation(oldTanslation); + glDepthMask(true); + glEnable(GL_DEPTH_TEST); this.shader.unbind(); } From 9383ae7ae39f4768ac88a7234b3b7b69cde3476f Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Wed, 1 Jul 2020 14:20:10 -0400 Subject: [PATCH 31/52] Use lambda arguments --- src/game/Hud.java | 7 +++---- src/game/Skybox.java | 6 +++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/game/Hud.java b/src/game/Hud.java index 372ffc2..2fc073c 100644 --- a/src/game/Hud.java +++ b/src/game/Hud.java @@ -99,10 +99,9 @@ public void render(Window window) { // draw items Matrix4f temp = new Matrix4f(); for (Item item : this.items) - // ($, $$) are ignored paramenters - item.getMesh().render(this.shader, item, ($, $$) -> { - item.buildOrthoProjModelMatrix(orthoMatrix, temp); - this.shader.set("projModelMatrix", temp); + item.getMesh().render(this.shader, item, (shader, item2) -> { + item2.buildOrthoProjModelMatrix(orthoMatrix, temp); + shader.set("projModelMatrix", temp); }); glDepthMask(true); diff --git a/src/game/Skybox.java b/src/game/Skybox.java index af596c6..394bf23 100644 --- a/src/game/Skybox.java +++ b/src/game/Skybox.java @@ -99,9 +99,9 @@ public void render(Window window) { // draw skybox Matrix4f temp = new Matrix4f(); - this.skybox.getMesh().render(this.shader, this.skybox, ($, $$) -> { - this.skybox.buildModelViewMatrix(viewMatrix, temp); - this.shader.set("modelViewMatrix", temp); + this.skybox.getMesh().render(this.shader, this.skybox, (shader, item) -> { + item.buildModelViewMatrix(viewMatrix, temp); + shader.set("modelViewMatrix", temp); }); // undo view translation removal From 8dc62e083ed66eeceb4c05f15e6aa5f204d64f50 Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Wed, 1 Jul 2020 14:20:35 -0400 Subject: [PATCH 32/52] Remove View.newBlock --- src/game/View.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/game/View.java b/src/game/View.java index 264856a..50e8f42 100644 --- a/src/game/View.java +++ b/src/game/View.java @@ -237,11 +237,6 @@ private void addGroup(String group, boolean order, boolean redefine) { this.blockMap.put(group, new ArrayList<>()); } - private BlockItem newBlock(String name) { - Mesh mesh = this.meshMap.get(name); - return new BlockItem(mesh).setScale(BLOCK_SCALE); - } - private void renderBlock(String group, Matrix4f viewMatrix, Matrix4f temp) { List blocks = this.blockMap.get(group); if (this.groupMap.get(group).size() == 1) { From f4ef93fbcf3e25bbe24712d09ea8b24bd14596d6 Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Mon, 6 Jul 2020 05:14:26 -0400 Subject: [PATCH 33/52] Allow plain Iterables in ClosestItem --- src/engine/ClosestItem.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/engine/ClosestItem.java b/src/engine/ClosestItem.java index 9b67d16..c21bd5b 100644 --- a/src/engine/ClosestItem.java +++ b/src/engine/ClosestItem.java @@ -29,7 +29,7 @@ public ClosestItem() { this.max = new Vector3f(); this.nearFar = new Vector2f(); } - public ClosestItem(List items, Camera camera) { + public ClosestItem(Iterable items, Camera camera) { this(); this.update(items, camera); } @@ -40,11 +40,11 @@ public ClosestItem reset() { return this; } - public void update(List items, Camera camera) { + public void update(Iterable items, Camera camera) { this.reset().extend(items, camera); } - public ClosestItem extend(List items, Camera camera) { + public ClosestItem extend(Iterable items, Camera camera) { // get camera direction camera.getViewMatrix().positiveZ(this.direction); this.direction.negate().normalize(); From 24676960889aab52907612e74b39757b77351052 Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Mon, 6 Jul 2020 05:30:05 -0400 Subject: [PATCH 34/52] Formatting --- src/engine/FontTexture.java | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/engine/FontTexture.java b/src/engine/FontTexture.java index 79fdf5d..a70a0d4 100644 --- a/src/engine/FontTexture.java +++ b/src/engine/FontTexture.java @@ -54,30 +54,30 @@ public Mesh buildMesh(String text) { int fontRow = currentChar / fontCols; // Left Top vertex - posList.add((currentCol + 0)*charWidth); // x - posList.add((currentRow + 0)*charLength); // y + posList.add((currentCol + 0) * charWidth); // x + posList.add((currentRow + 0) * charLength); // y posList.add(ZPOS); // z coordList.add((float) (fontCol + 0) / fontCols); coordList.add((float) (fontRow + 0) / fontRows); // Left Bottom vertex - posList.add((currentCol + 0)*charWidth); // x - posList.add((currentRow + 1)*charLength); // y - posList.add(ZPOS); // z + posList.add((currentCol + 0) * charWidth); + posList.add((currentRow + 1) * charLength); + posList.add(ZPOS); coordList.add((float) (fontCol + 0) / fontCols); coordList.add((float) (fontRow + 1) / fontRows); // Right Top vertex - posList.add((currentCol + 1)*charWidth); // x - posList.add((currentRow + 0)*charLength); // y - posList.add(ZPOS); // z + posList.add((currentCol + 1) * charWidth); + posList.add((currentRow + 0) * charLength); + posList.add(ZPOS); coordList.add((float) (fontCol + 1) / fontCols); coordList.add((float) (fontRow + 0) / fontRows); // Right Bottom vertex - posList.add((currentCol + 1)*charWidth); // x - posList.add((currentRow + 1)*charLength); // y - posList.add(ZPOS); // z + posList.add((currentCol + 1) * charWidth); + posList.add((currentRow + 1) * charLength); + posList.add(ZPOS); coordList.add((float) (fontCol + 1) / fontCols); coordList.add((float) (fontRow + 1) / fontRows); @@ -98,6 +98,9 @@ public Mesh buildMesh(String text) { float[] posArray = Utils.floatListToArray(posList); float[] coordArray = Utils.floatListToArray(coordList); int[] indexArray = Utils.intListToArray(indexList); - return new Mesh(posArray, indexArray, coordArray).setTexture(this); + + Mesh mesh = new Mesh(posArray, indexArray, coordArray); + mesh.setTexture(this); + return mesh; } } From a1bcfef3099782206b95918d8eba9c00a8143750 Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Mon, 6 Jul 2020 05:31:47 -0400 Subject: [PATCH 35/52] Refactor Mesh internals prepare -> bind restore -> unbind cleanup, disableVao, deleteVbos, deleteVao -> close, cleanup --- src/engine/Mesh.java | 50 +++++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/src/engine/Mesh.java b/src/engine/Mesh.java index 0f4251d..c353da2 100644 --- a/src/engine/Mesh.java +++ b/src/engine/Mesh.java @@ -13,7 +13,7 @@ import static org.lwjgl.opengl.GL30.*; import static org.lwjgl.system.MemoryUtil.*; -public class Mesh { +public class Mesh implements AutoCloseable { private static final Vector3f DEFAULT_COLOUR = new Vector3f(0.0f, 0.0f, 0.0f); private final int vaoId; @@ -38,7 +38,6 @@ public Mesh(float[] posArray, int[] indexArray, float[] coordArray) { this.vaoId = glGenVertexArrays(); glBindVertexArray(this.vaoId); - // Position VBO vboId = glGenBuffers(); this.vboIdList.add(vboId); @@ -113,28 +112,16 @@ public void render(Shader shader, T item, BiConsumer } protected void with(Shader shader, Runnable runnable) { - this.prepare(); + this.bind(); this.setup(shader); runnable.run(); - this.restore(); - } - - public void cleanup() { this.cleanup(true); } - public void cleanup(boolean cleanupTexture) { - this.disableVao(); - this.deleteVbos(); - if (cleanupTexture && this.isTextured()) - this.texture.cleanup(); - this.deleteVao(); + this.unbind(); } // prepare mesh - protected void prepare() { this.prepare(null); } - protected void prepare(Mesh lastMesh) { - if (this == lastMesh) - return; + protected void bind() { if (this.isTextured()) - this.texture.prepare(); + this.texture.bind(); glBindVertexArray(this.vaoId); } @@ -149,27 +136,28 @@ protected void draw() { glDrawElements(GL_TRIANGLES, this.vertexCount, GL_UNSIGNED_INT, 0); } - // Restore state - protected void restore() { this.restore(null); } - protected void restore(Mesh nextMesh) { - if (this == nextMesh) - return; + // restore state + protected void unbind() { + if (this.isTextured()) + this.texture.unbind(); glBindVertexArray(0); } - protected void deleteVbos() { - // Delete the VBO + @Override + public void close() { this.cleanup(true); } + public void cleanup(boolean closeTexture) { + glDisableVertexAttribArray(0); + + // delete VBO glBindBuffer(GL_ARRAY_BUFFER, 0); for (int id : this.vboIdList) glDeleteBuffers(id); - } - protected void disableVao() { - glDisableVertexAttribArray(0); - } + // delete texture + if (closeTexture && this.texture != null) + this.texture.close(); - protected void deleteVao() { - // Delete the VAO + // delete VAO glBindVertexArray(0); glDeleteVertexArrays(this.vaoId); } From 0287af8e4353d45f5eeb1a82d9a145f073769e81 Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Mon, 6 Jul 2020 05:41:17 -0400 Subject: [PATCH 36/52] Refactor image utilities Move Utils.loadImage and freeImage to Image.new and close heightAt -> pixel, pixelAlpha MAX_COLOR -> MAX, MAX_ALPHA Rename Texture.prepare -> bind, cleanup -> close, and new unbind --- src/engine/HeightMap.java | 54 ---------------------------- src/engine/Image.java | 74 +++++++++++++++++++++++++++++++++++++++ src/engine/Texture.java | 53 ++++++++++++++-------------- src/engine/Utils.java | 33 ----------------- 4 files changed, 101 insertions(+), 113 deletions(-) delete mode 100644 src/engine/HeightMap.java create mode 100644 src/engine/Image.java diff --git a/src/engine/HeightMap.java b/src/engine/HeightMap.java deleted file mode 100644 index 1afcfa8..0000000 --- a/src/engine/HeightMap.java +++ /dev/null @@ -1,54 +0,0 @@ -/* -George Zhang -Encapsulate an image (used to get pixel values). -*/ - -package geetransit.minecraft05.engine; - -import java.nio.ByteBuffer; - -public class HeightMap implements AutoCloseable { - public static final int CHANNELS = 4; - public static final int MAX_COLOR = 255*255*255; - - public final ByteBuffer buffer; - public final int width; - public final int length; - - public HeightMap(ByteBuffer buffer, int width, int length) { - this.buffer = buffer; - this.width = width; - this.length = length; - } - - public static HeightMap loadFromImage(String fileName) { - int width[] = {0}, length[] = {0}; - ByteBuffer buffer = Utils.loadImage(fileName, width, length); - return new HeightMap(buffer, width[0], length[0]); - } - - @Override - public void close() { - Utils.freeImage(this.buffer); - } - - // usage: compressExpand(heightAt(...), 0, MAX_COLOR, 0, 16) - public static float compressExpand(int f, float cMin, float cMax, float eMin, float eMax) { - return expand(compress(f, cMin, cMax), eMin, eMax); - } - public static float compress(int f, float min, float max) { return (f-min) / (max-min); } - public static float expand(float f, float min, float max) { return min + f*(max-min); } - - public int heightAt(int x, int z) { return this.heightAt(x*CHANNELS + z*CHANNELS*this.width); } - public int heightAt(int i) { - byte r = this.buffer.get(i + 0); - byte g = this.buffer.get(i + 1); - byte b = this.buffer.get(i + 2); - byte a = this.buffer.get(i + 3); - return 0 - // | ((0xFF & a) << 24) // removed cuz it turns overflows int - | ((0xFF & r) << 16) - | ((0xFF & g) << 8) - | ((0xFF & b) << 0); - } -} diff --git a/src/engine/Image.java b/src/engine/Image.java new file mode 100644 index 0000000..5ebde62 --- /dev/null +++ b/src/engine/Image.java @@ -0,0 +1,74 @@ +/* +George Zhang +Encapsulate an image (used to get pixel values). +*/ + +package geetransit.minecraft05.engine; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; + +import org.lwjgl.system.MemoryUtil; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.stb.STBImage; + +public class Image implements AutoCloseable { + public static final int CHANNELS = 4; + public static final int MAX = 0x00FFFFFF; + public static final int MAX_ALPHA = 0xFFFFFFFF; + + public final ByteBuffer buffer; + public final int width; + public final int length; + + public Image(String file) { this(Utils.loadByteArray(file)); } + public Image(byte[] array) { this((ByteBuffer) MemoryUtil.memAlloc(array.length).put(array).flip()); } + public Image(ByteBuffer raw) { + // Load Texture file + try (MemoryStack stack = MemoryStack.stackPush()) { + IntBuffer width = stack.mallocInt(1); + IntBuffer length = stack.mallocInt(1); + IntBuffer channels = stack.mallocInt(1); + + this.buffer = STBImage.stbi_load_from_memory(raw, width, length, channels, 4); + if (this.buffer == null) + throw new RuntimeException("Buffer could not be loaded: " + STBImage.stbi_failure_reason()); + + // store width and length of image + this.width = width.get(); + this.length = length.get(); + } + } + + @Override + public void close() { + STBImage.stbi_image_free(this.buffer); + } + + // usage: compressExpand(heightAt(...), 0, MAX_COLOR, 0, 16) + public static float compressExpand(int f, float cMin, float cMax, float eMin, float eMax) { + return expand(compress(f, cMin, cMax), eMin, eMax); + } + public static float compress(int f, float min, float max) { return (f-min) / (max-min); } + public static float expand(float f, float min, float max) { return min + f*(max-min); } + + // RRGGBB + public int pixel(int x, int z) { + int i = x*CHANNELS + z*CHANNELS*this.width; + return 0 + | (this.buffer.get(i + 0) & 0xFF) << 020 + | (this.buffer.get(i + 1) & 0xFF) << 010 + | (this.buffer.get(i + 2) & 0xFF) << 000; + } + + // RRGGBBAA + // remember to use >>> when shifting down + public int pixelAlpha(int x, int z) { + int i = x*CHANNELS + z*CHANNELS*this.width; + return 0 + | (this.buffer.get(i + 0) & 0xFF) << 030 + | (this.buffer.get(i + 1) & 0xFF) << 020 + | (this.buffer.get(i + 2) & 0xFF) << 010 + | (this.buffer.get(i + 3) & 0xFF) << 000; + } +} diff --git a/src/engine/Texture.java b/src/engine/Texture.java index 17c9b31..0e966a3 100644 --- a/src/engine/Texture.java +++ b/src/engine/Texture.java @@ -9,37 +9,35 @@ import static org.lwjgl.opengl.GL30.*; -public class Texture { +public class Texture implements AutoCloseable { private final int id; private final int width; private final int length; - public Texture(String fileName) { - int widthArray[] = {0}; - int lengthArray[] = {0}; - ByteBuffer image = Utils.loadImage(fileName, widthArray, lengthArray); - this.width = widthArray[0]; - this.length = lengthArray[0]; + public Texture(String file) { + try (Image image = new Image(file)) { + this.width = image.width; + this.length = image.length; - // Create a new OpenGL texture - this.id = glGenTextures(); - // Bind the texture - this.bind(); + // Create a new OpenGL texture + this.id = glGenTextures(); + // Bind the texture + glBindTexture(GL_TEXTURE_2D, this.id); - // Tell OpenGL how to unpack the RGBA bytes. Each component is 1 byte size - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + // Tell OpenGL how to unpack the RGBA bytes. Each component is 1 byte size + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - // make text easier to read - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + // make text easier to read + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - // Upload the texture data - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, this.width, this.length, 0, GL_RGBA, GL_UNSIGNED_BYTE, image); - // Generate Mip Map - glGenerateMipmap(GL_TEXTURE_2D); - - Utils.freeImage(image); + // Upload the texture data + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, this.width, this.length, 0, GL_RGBA, GL_UNSIGNED_BYTE, image.buffer); + // Generate Mip Map + glGenerateMipmap(GL_TEXTURE_2D); + } } + protected Texture(int id, int width, int length) { this.id = id; this.width = width; @@ -51,13 +49,16 @@ protected Texture(int id, int width, int length) { public int getLength() { return this.length; } public void bind() { + glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, this.id); } - public void prepare() { - glActiveTexture(GL_TEXTURE0); - this.bind(); + + public void unbind() { + glBindTexture(GL_TEXTURE_2D, 0); } - public void cleanup() { + + @Override + public void close() { glDeleteTextures(this.id); } } diff --git a/src/engine/Utils.java b/src/engine/Utils.java index c09b55a..bc59039 100644 --- a/src/engine/Utils.java +++ b/src/engine/Utils.java @@ -8,13 +8,11 @@ import java.io.*; import java.util.*; import java.util.stream.*; -import java.util.function.*; import java.nio.*; import java.nio.charset.StandardCharsets; import org.lwjgl.system.*; -import org.lwjgl.stb.*; public class Utils { @@ -63,37 +61,6 @@ public static Stream loadLinesStream(String file) { } } - public static ByteBuffer loadImage(String fileName, BiConsumer consumer) { - ByteBuffer imageBuffer; - ByteBuffer rawBuffer; - - // Load Texture file - try (MemoryStack stack = MemoryStack.stackPush()) { - IntBuffer widthBuffer = stack.mallocInt(1); - IntBuffer heightBuffer = stack.mallocInt(1); - IntBuffer channelsBuffer = stack.mallocInt(1); - - byte[] array = loadByteArray(fileName); - rawBuffer = MemoryUtil.memAlloc(array.length); - rawBuffer.put(array).flip(); - - imageBuffer = STBImage.stbi_load_from_memory(rawBuffer, widthBuffer, heightBuffer, channelsBuffer, 4); - if (imageBuffer == null) - throw new RuntimeException("Image file [" + fileName + "] not loaded: " + STBImage.stbi_failure_reason()); - - // Get width and height of image - consumer.accept(widthBuffer.get(), heightBuffer.get()); - } - - return imageBuffer; - } - public static ByteBuffer loadImage(String fileName, int[] widthArray, int[] heightArray) { - return loadImage(fileName, (width, height) -> { widthArray[0] = width; heightArray[0] = height; }); - } - public static void freeImage(ByteBuffer imageBuffer) { - STBImage.stbi_image_free(imageBuffer); - } - public static int[] intListToArray(List intList) { return intList.stream().mapToInt(i -> i).toArray(); } From a45a99bf430c975ec37b37fa8e4ecd81bf74bbd0 Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Mon, 6 Jul 2020 05:51:44 -0400 Subject: [PATCH 37/52] Split View into World and Player World generates, stores, and renders the blocks Player takes care of camera and player interaction Move View.loadMesh to ObjLoader Use new Image class in place of HeightMap Replace groups concept with block types (transparent blocks are stored in one sorted list) Replace addGroup and putMesh with addFullType and addTransparentType Add a public setBlock method to change a block --- src/engine/ObjLoader.java | 6 + src/game/Player.java | 89 +++++++++++ src/game/View.java | 304 -------------------------------------- src/game/World.java | 222 ++++++++++++++++++++++++++++ 4 files changed, 317 insertions(+), 304 deletions(-) create mode 100644 src/game/Player.java delete mode 100644 src/game/View.java create mode 100644 src/game/World.java diff --git a/src/engine/ObjLoader.java b/src/engine/ObjLoader.java index d1358de..7bffb8b 100644 --- a/src/engine/ObjLoader.java +++ b/src/engine/ObjLoader.java @@ -9,6 +9,12 @@ import org.joml.*; public class ObjLoader { + public static Mesh loadMesh(String obj, String texture) { + Mesh mesh = loadMesh(obj); + mesh.setTexture(new Texture(texture)); + return mesh; + } + public static Mesh loadMesh(String file) { List posList = new ArrayList<>(); List indexList = new ArrayList<>(); diff --git a/src/game/Player.java b/src/game/Player.java new file mode 100644 index 0000000..3bb1b49 --- /dev/null +++ b/src/game/Player.java @@ -0,0 +1,89 @@ +/* +George Zhang +Player interaction class. +*/ + +package geetransit.minecraft05.game; + +import geetransit.minecraft05.engine.*; + +import java.util.*; +import org.joml.Vector3f; +import org.joml.Matrix4f; + +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.opengl.GL11.*; + +public class Player implements Loopable { + public static final float CHANGE_DELAY = 0.2f; // time between block change (place / remove) + public static final float MOVEMENT_STEP = 3.0f; // distance moved in 1 second + public static final float SPRINT_MULTIPLIER = 1.5f; // sprinting change + + private final Mouse mouse; + private final Camera camera; + private final World world; + + private final Countdown countdown; + private final Vector3f movement; + private String change; // ""=air + + public Player(Mouse mouse, Camera camera, World world) { + this.mouse = mouse; + this.camera = camera; + this.world = world; + + this.countdown = new Countdown(CHANGE_DELAY); + this.movement = new Vector3f(); + } + + public String getChange() { return this.change; } + public float getWait() { return this.countdown.getWait(); } + + @Override + public void input(Window window) { + // movement + this.movement.zero(); + boolean SPRINTING = !window.isKeyDown(GLFW_KEY_LEFT_SHIFT) && window.isKeyDown(GLFW_KEY_LEFT_CONTROL); + + if (window.isKeyDown(GLFW_KEY_W)) this.movement.z--; + if (window.isKeyDown(GLFW_KEY_S)) this.movement.z++; + if (window.isKeyDown(GLFW_KEY_A)) this.movement.x--; + if (window.isKeyDown(GLFW_KEY_D)) this.movement.x++; + + if (window.isKeyDown(GLFW_KEY_LEFT_SHIFT)) this.movement.y--; + if (window.isKeyDown(GLFW_KEY_SPACE)) this.movement.y++; + + if (this.movement.length() > 1f) this.movement.div(this.movement.length()); + if (SPRINTING && this.movement.z < 0) this.movement.mul(SPRINT_MULTIPLIER); + + // placing / removing + this.change = null; + if (window.isKeyDown(GLFW_KEY_0)) this.change = ""; + if (window.isKeyDown(GLFW_KEY_1)) this.change = "grassblock"; + if (window.isKeyDown(GLFW_KEY_2)) this.change = "cobbleblock"; + if (window.isKeyDown(GLFW_KEY_3)) this.change = "glassblock"; + if (this.change == null) this.countdown.reset(); + } + + @Override + public void update(float interval) { + // movement + this.camera.movePosition(this.movement, interval*MOVEMENT_STEP); + + // placing / removing + this.countdown.add(interval); + if (this.change != null && this.countdown.nextOnce()) { + ClosestItem closest = this.world.updateClosest(); + // check if block found + if (closest.closest != null) { + Vector3f position = new Vector3f(); + position.set(closest.direction); // get normalized camera direction + position.negate(); // move towards camera + position.mul(0.001f * (this.change.equals("") ? -1 : 1)); // go to block + position.add(closest.hit); // start from intersection point + position.round(); // round to grid + this.world.setBlock(this.change, position); + } + } + } +} diff --git a/src/game/View.java b/src/game/View.java deleted file mode 100644 index 50e8f42..0000000 --- a/src/game/View.java +++ /dev/null @@ -1,304 +0,0 @@ -/* -George Zhang -World view class. -*/ - -package geetransit.minecraft05.game; - -import geetransit.minecraft05.engine.*; - -import java.util.*; -import org.joml.Vector3f; -import org.joml.Matrix4f; - -import static org.lwjgl.glfw.GLFW.*; -import static org.lwjgl.opengl.GL11.*; - -public class View implements Loopable { - public static final float CHANGE_DELAY = 0.2f; // time between block change (place / remove) - public static final float MOVEMENT_STEP = 3.0f; // distance moved in 1 second - public static final float SPRINT_MULTIPLIER = 1.5f; // sprinting change - public static final float BLOCK_SCALE = 0.5f; // block scaling (mesh is 2x2x2) - public static final float BLOCK_RADIUS = 2f; // radius around block (for frustum culling) - - private final Mouse mouse; - private final Camera camera; - private final Countdown countdown; - - private Shader shader; - private final Map meshMap; // name -> mesh - private final Map> groupMap; // group -> list - private final Map orderMap; // group -> ordered - private final Map> blockMap; // group -> list - private final List blockList; - - private final ClosestItem closestItem; - private final Vector3f movement; - private String change; // ""=air - - public View(Mouse mouse, Camera camera) { - this.mouse = mouse; - this.camera = camera; - this.countdown = new Countdown(CHANGE_DELAY); - - this.meshMap = new HashMap<>(); - this.groupMap = new HashMap<>(); - this.orderMap = new HashMap<>(); - this.blockMap = new HashMap<>(); - this.blockList = new ArrayList<>(); - - this.closestItem = new ClosestItem<>(); - this.movement = new Vector3f(); - } - - public String getChange() { return this.change; } - public float getWait() { return this.countdown.getWait(); } - - @Override - public void init(Window window) { - this.shader = new Shader(); - this.shader.compileVertex(Utils.loadResource("/res/vertex-3d.vs")); - this.shader.compileFragment(Utils.loadResource("/res/fragment-3d-block.fs")); - this.shader.link(); - - this.shader.create("projectionMatrix"); - this.shader.create("modelViewMatrix"); - this.shader.create("texture_sampler"); - this.shader.create("color"); - this.shader.create("isTextured"); - this.shader.create("isSelected"); - - // create groups and block meshes - this.addGroup("grass", false); - this.putMesh("grass", "grassblock", this.loadMesh("/res/cube-fblr,u,d.obj", "/res/grassblock.png")); - - this.addGroup("cobble", false); - this.putMesh("cobble", "cobbleblock", this.loadMesh("/res/cube-fblrud.obj", "/res/cobbleblock.png")); - - this.addGroup("glass", true); - this.putMesh("glass", "glassblock", this.loadMesh("/res/cube-fblrud.obj", "/res/glassblock.png")); - - // get heightmap - try (HeightMap map = HeightMap.loadFromImage("/res/heightmap.png")) { - // create terrain - for (int x = 0; x < map.width; x++) { - for (int z = 0; z < map.length; z++) { - int y = (int) map.compressExpand(map.heightAt(x, z), 0, map.MAX_COLOR, 0, 16); - this.addBlock("grassblock", x, y, z); - for (int k = y-1; k >= Math.max(y-2, 0); k--) - this.addBlock("cobbleblock", x, k, z); - } - } - } - - // add spawn markers (-2z is forwards) - this.addBlock("grassblock", +1, +1, 0); - this.addBlock("grassblock", -1, +1, 0); - this.addBlock("grassblock", 0, +1, +1); - this.addBlock("grassblock", 0, +1, -2); - } - - @Override - public void input(Window window) { - // movement - this.movement.zero(); - boolean SPRINTING = (!window.isKeyDown(GLFW_KEY_LEFT_SHIFT) && window.isKeyDown(GLFW_KEY_LEFT_CONTROL)); - - if (window.isKeyDown(GLFW_KEY_W)) this.movement.z--; - if (window.isKeyDown(GLFW_KEY_S)) this.movement.z++; - if (window.isKeyDown(GLFW_KEY_A)) this.movement.x--; - if (window.isKeyDown(GLFW_KEY_D)) this.movement.x++; - - if (window.isKeyDown(GLFW_KEY_LEFT_SHIFT)) this.movement.y--; - if (window.isKeyDown(GLFW_KEY_SPACE)) this.movement.y++; - - if (this.movement.length() > 1f) this.movement.div(this.movement.length()); - if (SPRINTING && this.movement.z < 0) this.movement.mul(SPRINT_MULTIPLIER); - - // placing / removing - this.change = null; - if (window.isKeyDown(GLFW_KEY_0)) this.change = ""; - if (window.isKeyDown(GLFW_KEY_1)) this.change = "grassblock"; - if (window.isKeyDown(GLFW_KEY_2)) this.change = "cobbleblock"; - if (window.isKeyDown(GLFW_KEY_3)) this.change = "glassblock"; - if (this.change == null) this.countdown.reset(); - } - - @Override - public void update(float interval) { - // movement - this.camera.movePosition(this.movement, interval*MOVEMENT_STEP); - if (!this.movement.equals(0, 0, 0)) - for (Map.Entry entry : this.orderMap.entrySet()) - if (entry.getValue()) - this.reorderBlock(entry.getKey()); - - this.countdown.add(interval); - if (this.change != null && this.countdown.nextOnce()) { - // placing / removing - this.closestItem.update(this.blockList, this.camera); - if (this.closestItem.closest != null) { - if (this.change.equals("")) { - this.removeBlock(this.closestItem.closest); - } else { - Vector3f position = new Vector3f(); - position.set(this.closestItem.direction); // get normalized camera direction - position.negate(); // move towards camera - position.mul(0.01f); // add a small offset (to go to next block) - position.add(this.closestItem.hit); // start from intersection point - position.round(); // round to grid - check: { - for (BlockItem block : this.blockList) - if (block.getPosition().equals(position)) - break check; - // else - this.addBlock(this.change, position); - } - } - } - } - - // update selected block - for (BlockItem block : this.blockList) - block.setSelected(false); - this.closestItem.update(this.blockList, this.camera); - if (this.closestItem.closest != null) - this.closestItem.closest.setSelected(true); - } - - @Override - public void render(Window window) { - this.shader.bind(); - this.shader.set("texture_sampler", 0); - this.shader.set("projectionMatrix", window.getProjectionMatrix()); - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - - // update visible blocks - for (BlockItem block : this.blockList) - block.setVisible(this.camera.insideFrustum(block.getPosition(), BLOCK_RADIUS*block.getScale())); - - // reorder ordered groups - for (String group : this.blockMap.keySet()) - if (this.orderMap.get(group)) - this.reorderBlock(group); - - Matrix4f viewMatrix = this.camera.getViewMatrix(); // view matrix - Matrix4f temp = new Matrix4f(); // temporary matrix (stores model view matrix) - - // opaque blocks - for (String group : this.groupMap.keySet()) - if (!this.orderMap.get(group)) - this.renderBlock(group, viewMatrix, temp); - - // transparent blocks - for (String group : this.groupMap.keySet()) - if (this.orderMap.get(group)) - this.renderBlock(group, viewMatrix, temp); - - glDisable(GL_CULL_FACE); - this.shader.unbind(); - } - - @Override - public void cleanup() { - this.shader.cleanup(); - for (Mesh mesh : this.meshMap.values()) - mesh.cleanup(); - } - - // block helpers - private static Mesh loadMesh(String objFileName, String textureFileName) { - Mesh mesh = ObjLoader.loadMesh(objFileName); - mesh.setTexture(new Texture(textureFileName)); - return mesh; - } - - private void putMesh(String group, String name, Mesh mesh) { - // mesh - if (this.meshMap.containsKey(name)) - throw new RuntimeException("mesh already defined: "+name); - this.meshMap.put(name, mesh); - - // group - if (!this.groupMap.containsKey(group)) - throw new RuntimeException("group not defined: "+group); - if (!this.groupMap.get(group).contains(name)) - this.groupMap.get(group).add(name); - } - - private void addGroup(String group, boolean order) { this.addGroup(group, order, false); } - private void addGroup(String group, boolean order, boolean redefine) { - if (!redefine && this.groupMap.containsKey(group)) - throw new RuntimeException("group already defined: "+group); - - this.groupMap.put(group, new ArrayList<>()); - this.orderMap.put(group, order); - this.blockMap.put(group, new ArrayList<>()); - } - - private void renderBlock(String group, Matrix4f viewMatrix, Matrix4f temp) { - List blocks = this.blockMap.get(group); - if (this.groupMap.get(group).size() == 1) { - if (blocks.size() > 0){ - blocks.get(0).getMesh().render( - this.shader, - blocks.stream().filter(block -> block.isVisible()), - (shader, block) -> { - block.buildModelViewMatrix(viewMatrix, temp); - shader.set("modelViewMatrix", temp); - shader.set("isSelected", block.isSelected()); - } - ); - } - } else { - for (BlockItem block : blocks) - block.getMesh().render(this.shader, block, (shader, block2) -> { - block.buildModelViewMatrix(viewMatrix, temp); - shader.set("modelViewMatrix", temp); - shader.set("isSelected", block2.isSelected()); - }); - } - } - - private String nameOf(BlockItem block) { - for (Map.Entry entry : this.meshMap.entrySet()) - if (entry.getValue() == block.getMesh()) - return entry.getKey(); - throw new RuntimeException("could not find parent name: "+block); - } - private String groupOf(String name) { - for (Map.Entry> entry : this.groupMap.entrySet()) - if (entry.getValue().contains(name)) - return entry.getKey(); - throw new RuntimeException("could not find parent group: "+name); - } - - private void addBlock(String name, Vector3f position) { this.addBlock(name, position.x, position.y, position.z); } - private void addBlock(String name, float x, float y, float z) { - Mesh mesh = this.meshMap.get(name); - BlockItem block = new BlockItem(mesh); - block.setScale(BLOCK_SCALE); - block.setPosition(x, y, z); - - String group = this.groupOf(name); - this.blockList.add(block); - this.blockMap.get(group).add(block); - if (this.orderMap.get(group)) - this.reorderBlock(group); - } - - private void reorderBlock(String group) { - Vector3f cameraPosition = this.camera.getPosition(); - this.blockMap.get(group).sort(Comparator.comparingDouble( - block -> block.getPosition().distance(cameraPosition) - )); - } - - private void removeBlock(BlockItem block) { - String name = this.nameOf(block); - String group = this.groupOf(name); - this.blockList.remove(block); - this.blockMap.get(group).remove(block); - } -} diff --git a/src/game/World.java b/src/game/World.java new file mode 100644 index 0000000..3508e0f --- /dev/null +++ b/src/game/World.java @@ -0,0 +1,222 @@ +/* +George Zhang +World view class. +*/ + +package geetransit.minecraft05.game; + +import geetransit.minecraft05.engine.*; + +import java.util.*; +import java.util.function.*; +import java.util.stream.*; +import org.joml.Vector3f; +import org.joml.Matrix4f; + +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.opengl.GL11.*; + +public class World implements Loopable { + public static final float BLOCK_SCALE = 0.5f; // block scaling (mesh is 2x2x2) + public static final float BLOCK_RADIUS = 2f; // radius around block (for frustum culling) + + private Shader shader; + private final Camera camera; + private final ClosestItem closest; + + private final Map> blocks; // type -> list + private final Map meshes; // type -> mesh + private final Set transparentNames; // type (if exists, transparent) + private final List transparentBlocks; // list (ordered) + + public World(Camera camera) { + this.camera = camera; + this.closest = new ClosestItem<>(); + + this.blocks = new HashMap<>(); + this.meshes = new HashMap<>(); + this.transparentNames = new HashSet<>(); + this.transparentBlocks = new ArrayList<>(); + } + + @Override + public void init(Window window) { + this.shader = new Shader(); + this.shader.compileVertex(Utils.loadResource("/res/vertex-3d.vs")); + this.shader.compileFragment(Utils.loadResource("/res/fragment-3d-block.fs")); + this.shader.link(); + + this.shader.create("projectionMatrix"); + this.shader.create("modelViewMatrix"); + this.shader.create("texture_sampler"); + this.shader.create("color"); + this.shader.create("isTextured"); + this.shader.create("isSelected"); + + // create groups and block meshes + this.addFullType("grassblock", ObjLoader.loadMesh("/res/cube-fblr,u,d.obj", "/res/grassblock.png")); + this.addFullType("cobbleblock", ObjLoader.loadMesh("/res/cube-fblrud.obj", "/res/cobbleblock.png")); + this.addTransparentType("glassblock", ObjLoader.loadMesh("/res/cube-fblrud.obj", "/res/glassblock.png")); + + // get heightmap + try (Image image = new Image("/res/heightmap.png")) { + // create terrain + for (int x = 0; x < image.width; x++) { + for (int z = 0; z < image.length; z++) { + int y = (int) Image.compressExpand(image.pixel(x, z), 0, Image.MAX, 0, 16); + this.setBlock("grassblock", x, y, z); + for (int k = y-1; k >= Math.max(y-2, 0); k--) + this.setBlock("cobbleblock", x, k, z); + } + } + } + + // add spawn markers (-2z is forwards, cobble is left) + this.setBlock("grassblock", +1, +1, 0); + this.setBlock("cobbleblock", -1, +1, 0); + this.setBlock("grassblock", 0, +1, +1); + this.setBlock("grassblock", 0, +1, -2); + } + + @Override + public void update(float interval) { + // reorder transparent blocks + this.sortBlocks(this.transparentBlocks); + } + + @Override + public void render(Window window) { + this.shader.bind(); + this.shader.set("texture_sampler", 0); + this.shader.set("projectionMatrix", window.getProjectionMatrix()); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + + // update visible blocks + for (BlockItem block : this.iterBlocks()) + block.setVisible(this.camera.insideFrustum(block.getPosition(), BLOCK_RADIUS*block.getScale())); + + // update selected block + for (BlockItem block : this.iterBlocks()) + block.setSelected(false); + this.closest.update(this.iterBlocks(), this.camera); + if (this.closest.closest != null) + this.closest.closest.setSelected(true); + + Matrix4f viewMatrix = this.camera.getViewMatrix(); // view matrix + Matrix4f temp = new Matrix4f(); // temporary matrix (stores model view matrix) + BiConsumer setup = (shader, block) -> { + block.buildModelViewMatrix(viewMatrix, temp); + shader.set("modelViewMatrix", temp); + shader.set("isSelected", block.isSelected()); + }; + + // opaque blocks (loop by type) + for (Map.Entry> entry : this.blocks.entrySet()) { + Mesh mesh = this.meshes.get(entry.getKey()); + Stream blocks = entry.getValue().stream().filter(block -> block.isVisible()); + mesh.render(this.shader, blocks, setup); + } + + // transparent blocks (loop individually) + for (BlockItem block : this.transparentBlocks) { + Mesh mesh = block.getMesh(); + if (block.isVisible()) + mesh.render(this.shader, block, setup); + } + + glDisable(GL_CULL_FACE); + this.shader.unbind(); + } + + @Override + public void cleanup() { + this.shader.cleanup(); + for (Mesh mesh : this.meshes.values()) + mesh.close(); + } + + private Iterable iterBlocks() { + return this.streamBlocks()::iterator; + } + + private Stream streamBlocks() { + Stream blocks = this.blocks.values().stream().flatMap(Collection::stream); + Stream transparentBlocks = this.transparentBlocks.stream(); + return Stream.concat(blocks, transparentBlocks); + } + + public ClosestItem updateClosest() { + this.closest.update(this.iterBlocks(), this.camera); + return this.closest; + } + + public void addFullType(String name, Mesh mesh) { + if (this.meshes.containsKey(name)) + throw new RuntimeException("type already defined: "+name); + this.meshes.put(name, mesh); + this.blocks.put(name, new ArrayList<>()); + } + + public void addTransparentType(String name, Mesh mesh) { + if (this.meshes.containsKey(name)) + throw new RuntimeException("type already defined: "+name); + this.meshes.put(name, mesh); + this.transparentNames.add(name); + } + + public void setBlock(String name, Vector3f position) { this.setBlock(name, position.x, position.y, position.z); } + public void setBlock(String name, float x, float y, float z) { + this.removeBlock(x, y, z); + if (name.length() != 0) + this.addBlock(name, x, y, z); + } + + private void addBlock(String name, float x, float y, float z) { + BlockItem block = new BlockItem(this.meshes.get(name)); + block.setScale(BLOCK_SCALE); + block.setPosition(x, y, z); + this.addBlock(block); + } + + private void addBlock(BlockItem block) { + String name = this.nameOf(block.getMesh()); + if (this.transparentNames.contains(name)) { + this.transparentBlocks.add(block); + this.sortBlocks(this.transparentBlocks); + } else { + this.blocks.get(name).add(block); + } + } + + private void removeBlock(float x, float y, float z) { + for (List blocks : this.blocks.values()) + for (BlockItem block : blocks) + if (block.getPosition().equals(x, y, z)) { + this.removeBlock(block); + return; + } + } + + private void removeBlock(BlockItem block) { + String name = this.nameOf(block.getMesh()); + if (this.transparentNames.contains(name)) + this.transparentBlocks.remove(block); + else + this.blocks.get(name).remove(block); + } + + private void sortBlocks(List blocks) { + Vector3f position = this.camera.getPosition(); + blocks.sort(Comparator.comparingDouble( + block -> -block.getPosition().distance(position) + )); + } + + private String nameOf(Mesh mesh) { + for (Map.Entry entry : this.meshes.entrySet()) + if (entry.getValue() == mesh) + return entry.getKey(); + throw new RuntimeException("could not find name of mesh"); + } +} From bdf9e7f6cf6a31ad92beb7c4d43e7654b5c8847f Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Mon, 6 Jul 2020 05:53:30 -0400 Subject: [PATCH 38/52] Update code Update View with World, Player Update Mesh.cleanup -> close --- src/game/Game.java | 25 ++++++++++++++----------- src/game/Hud.java | 10 +++++----- src/game/Skybox.java | 2 +- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/game/Game.java b/src/game/Game.java index 6e610d1..700c650 100644 --- a/src/game/Game.java +++ b/src/game/Game.java @@ -20,7 +20,8 @@ public class Game extends Scene { private Background background; private Skybox skybox; - private View view; + private Player player; // player actions + private World world; // world loading and rendering private Hud hud; public Game() { @@ -29,20 +30,22 @@ public Game() { // inputs this.mouse = new Mouse(); this.camera = new Camera(this.mouse); - this - .addFrom(this.mouse) - .addFrom(this.camera); // child scenes this.background = new Background(); this.skybox = new Skybox(this.camera); - this.view = new View(this.mouse, this.camera); - this.hud = new Hud(this.mouse, this.camera, this.view); - this - .addFrom(this.background) - .addFrom(this.skybox) - .addFrom(this.view) - .addFrom(this.hud); + this.world = new World(this.camera); + this.player = new Player(this.mouse, this.camera, this.world); + this.hud = new Hud(this.mouse, this.camera, this.player); + + // scene ordering + this.addFrom(this.mouse); + this.addFrom(this.camera); + this.addFrom(this.background); + this.addFrom(this.skybox); + this.addFrom(this.player); + this.addFrom(this.world); + this.addFrom(this.hud); } @Override diff --git a/src/game/Hud.java b/src/game/Hud.java index 2fc073c..2254fcf 100644 --- a/src/game/Hud.java +++ b/src/game/Hud.java @@ -20,7 +20,7 @@ public class Hud implements Loopable { private Mouse mouse; private Camera camera; - private View view; + private Player player; private Window window; private Shader shader; @@ -30,10 +30,10 @@ public class Hud implements Loopable { private Item compass; private Item crosshair; - public Hud(Mouse mouse, Camera camera, View view) { + public Hud(Mouse mouse, Camera camera, Player player) { this.mouse = mouse; this.camera = camera; - this.view = view; + this.player = player; this.items = new ArrayList<>(); } @@ -73,7 +73,7 @@ public void update(float interval) { this.text.setText(String.format( "vsync=%s mode=%s mouse=%s\nchange=%s wait=%s\ncamera=%s\nmouse=%s", this.window.isVSync(), this.window.getMode(), this.window.getInputMode(GLFW_CURSOR) == GLFW_CURSOR_NORMAL, - this.view.getChange(), Math.max(0, this.view.getWait()), + this.player.getChange(), Math.max(0, this.player.getWait()), this.camera, this.mouse )); @@ -113,6 +113,6 @@ public void render(Window window) { public void cleanup() { this.shader.cleanup(); for (Item item : this.items) - item.getMesh().cleanup(); + item.getMesh().close(); } } diff --git a/src/game/Skybox.java b/src/game/Skybox.java index 394bf23..76b264e 100644 --- a/src/game/Skybox.java +++ b/src/game/Skybox.java @@ -115,6 +115,6 @@ public void render(Window window) { @Override public void cleanup() { this.shader.cleanup(); - this.skybox.getMesh().cleanup(); + this.skybox.getMesh().close(); } } From 044b52828b25ff84f3a881db4d80e68a94941807 Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Mon, 6 Jul 2020 05:54:03 -0400 Subject: [PATCH 39/52] Minimum render distance at 5 units --- src/game/Skybox.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/game/Skybox.java b/src/game/Skybox.java index 76b264e..c42ee91 100644 --- a/src/game/Skybox.java +++ b/src/game/Skybox.java @@ -16,6 +16,7 @@ public class Skybox implements Loopable { public static final float RENDER_STEP = 3.0f; // render changed in 1 second + public static final float RENDER_MIN = Math.min(Camera.NEAR, 5f); // render changed in 1 second public static final float RENDER_DELAY = 0.5f; // time between skybox toggling public static final float SKYBOX_SCALE = 0.5f; // skybox scale (multiplied with camera far) @@ -71,7 +72,7 @@ public void input(Window window) { @Override public void update(float interval) { // render distance - this.camera.setFar(Math.max(Camera.NEAR+0.01f, this.camera.getFar() + this.render * interval*RENDER_STEP)); + this.camera.setFar(Math.max(RENDER_MIN, this.camera.getFar() + this.render * interval*RENDER_STEP)); // toggle skybox this.countdown.add(interval); From 7f013450cb5bc78d7cdc014413b50ecfe47a802c Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Mon, 6 Jul 2020 05:54:56 -0400 Subject: [PATCH 40/52] Replace Utils.intListToArray with simpler code --- src/engine/Utils.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/engine/Utils.java b/src/engine/Utils.java index bc59039..f6bfc60 100644 --- a/src/engine/Utils.java +++ b/src/engine/Utils.java @@ -62,7 +62,11 @@ public static Stream loadLinesStream(String file) { } public static int[] intListToArray(List intList) { - return intList.stream().mapToInt(i -> i).toArray(); + int[] intArray = new int[intList.size()]; + int i = 0; + for (int j : intList) + intArray[i++] = j; + return intArray; } public static float[] floatListToArray(List floatList) { float[] floatArray = new float[floatList.size()]; From 635cc9d2a23a8e44634e0bbaa54e895fb704c10a Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Mon, 6 Jul 2020 11:45:48 -0400 Subject: [PATCH 41/52] Deprecate Shader.cleanup -> close --- src/engine/Shader.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/engine/Shader.java b/src/engine/Shader.java index fbe88bc..32cdaf8 100644 --- a/src/engine/Shader.java +++ b/src/engine/Shader.java @@ -11,7 +11,7 @@ import static org.lwjgl.opengl.GL20.*; -public class Shader { +public class Shader implements AutoCloseable { private final int program; private final Map uniforms; @@ -101,9 +101,15 @@ public void unbind() { glUseProgram(0); } - public void cleanup() { + @Override + public void close() { this.unbind(); if (this.program != 0) glDeleteProgram(this.program); } + + @Deprecated + public void cleanup() { + this.close(); + } } From 54986f919167f4129001750233e8168f118de70f Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Mon, 6 Jul 2020 11:47:18 -0400 Subject: [PATCH 42/52] Fix World.removeBlock Use iterBlocks to include transparent blocks --- src/game/World.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/game/World.java b/src/game/World.java index 3508e0f..a6c9b9f 100644 --- a/src/game/World.java +++ b/src/game/World.java @@ -190,12 +190,11 @@ private void addBlock(BlockItem block) { } private void removeBlock(float x, float y, float z) { - for (List blocks : this.blocks.values()) - for (BlockItem block : blocks) - if (block.getPosition().equals(x, y, z)) { - this.removeBlock(block); - return; - } + for (BlockItem block : this.iterBlocks()) + if (block.getPosition().equals(x, y, z)) { + this.removeBlock(block); + return; + } } private void removeBlock(BlockItem block) { From eb935eac3fb8704f2b6880c56018fee843d0258c Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Mon, 6 Jul 2020 12:21:55 -0400 Subject: [PATCH 43/52] Delete Hud and rename Hud -> Player_ --- src/game/Player.java | 89 ----------------------------- src/game/{Hud.java => Player_.java} | 0 2 files changed, 89 deletions(-) delete mode 100644 src/game/Player.java rename src/game/{Hud.java => Player_.java} (100%) diff --git a/src/game/Player.java b/src/game/Player.java deleted file mode 100644 index 3bb1b49..0000000 --- a/src/game/Player.java +++ /dev/null @@ -1,89 +0,0 @@ -/* -George Zhang -Player interaction class. -*/ - -package geetransit.minecraft05.game; - -import geetransit.minecraft05.engine.*; - -import java.util.*; -import org.joml.Vector3f; -import org.joml.Matrix4f; - -import static org.lwjgl.glfw.GLFW.*; -import static org.lwjgl.opengl.GL11.*; - -public class Player implements Loopable { - public static final float CHANGE_DELAY = 0.2f; // time between block change (place / remove) - public static final float MOVEMENT_STEP = 3.0f; // distance moved in 1 second - public static final float SPRINT_MULTIPLIER = 1.5f; // sprinting change - - private final Mouse mouse; - private final Camera camera; - private final World world; - - private final Countdown countdown; - private final Vector3f movement; - private String change; // ""=air - - public Player(Mouse mouse, Camera camera, World world) { - this.mouse = mouse; - this.camera = camera; - this.world = world; - - this.countdown = new Countdown(CHANGE_DELAY); - this.movement = new Vector3f(); - } - - public String getChange() { return this.change; } - public float getWait() { return this.countdown.getWait(); } - - @Override - public void input(Window window) { - // movement - this.movement.zero(); - boolean SPRINTING = !window.isKeyDown(GLFW_KEY_LEFT_SHIFT) && window.isKeyDown(GLFW_KEY_LEFT_CONTROL); - - if (window.isKeyDown(GLFW_KEY_W)) this.movement.z--; - if (window.isKeyDown(GLFW_KEY_S)) this.movement.z++; - if (window.isKeyDown(GLFW_KEY_A)) this.movement.x--; - if (window.isKeyDown(GLFW_KEY_D)) this.movement.x++; - - if (window.isKeyDown(GLFW_KEY_LEFT_SHIFT)) this.movement.y--; - if (window.isKeyDown(GLFW_KEY_SPACE)) this.movement.y++; - - if (this.movement.length() > 1f) this.movement.div(this.movement.length()); - if (SPRINTING && this.movement.z < 0) this.movement.mul(SPRINT_MULTIPLIER); - - // placing / removing - this.change = null; - if (window.isKeyDown(GLFW_KEY_0)) this.change = ""; - if (window.isKeyDown(GLFW_KEY_1)) this.change = "grassblock"; - if (window.isKeyDown(GLFW_KEY_2)) this.change = "cobbleblock"; - if (window.isKeyDown(GLFW_KEY_3)) this.change = "glassblock"; - if (this.change == null) this.countdown.reset(); - } - - @Override - public void update(float interval) { - // movement - this.camera.movePosition(this.movement, interval*MOVEMENT_STEP); - - // placing / removing - this.countdown.add(interval); - if (this.change != null && this.countdown.nextOnce()) { - ClosestItem closest = this.world.updateClosest(); - // check if block found - if (closest.closest != null) { - Vector3f position = new Vector3f(); - position.set(closest.direction); // get normalized camera direction - position.negate(); // move towards camera - position.mul(0.001f * (this.change.equals("") ? -1 : 1)); // go to block - position.add(closest.hit); // start from intersection point - position.round(); // round to grid - this.world.setBlock(this.change, position); - } - } - } -} diff --git a/src/game/Hud.java b/src/game/Player_.java similarity index 100% rename from src/game/Hud.java rename to src/game/Player_.java From 7e8bdab154d6d35f737e3c2bc2a6f4012e1cf9d4 Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Mon, 6 Jul 2020 12:40:23 -0400 Subject: [PATCH 44/52] Delete Hud and rename Player -> Player_ --- src/game/Hud.java | 118 ------------------------- src/game/{Player.java => Player_.java} | 0 2 files changed, 118 deletions(-) delete mode 100644 src/game/Hud.java rename src/game/{Player.java => Player_.java} (100%) diff --git a/src/game/Hud.java b/src/game/Hud.java deleted file mode 100644 index 2254fcf..0000000 --- a/src/game/Hud.java +++ /dev/null @@ -1,118 +0,0 @@ -/* -ahbejarano -Hud implementation. -*/ - -package geetransit.minecraft05.game; - -import geetransit.minecraft05.engine.*; - -import java.util.*; -import org.joml.Matrix4f; - -import static org.lwjgl.glfw.GLFW.*; -import static org.lwjgl.opengl.GL11.*; - -public class Hud implements Loopable { - private static final int FONT_COLS = 16; - private static final int FONT_ROWS = 16; - private static final String FONT_FILE = "/res/font.png"; - - private Mouse mouse; - private Camera camera; - private Player player; - private Window window; - - private Shader shader; - private List items; - - private TextItem text; - private Item compass; - private Item crosshair; - - public Hud(Mouse mouse, Camera camera, Player player) { - this.mouse = mouse; - this.camera = camera; - this.player = player; - - this.items = new ArrayList<>(); - } - - @Override - public void init(Window window) { - this.shader = new Shader(); - this.shader.compileVertex(Utils.loadResource("/res/vertex-2d.vs")); - this.shader.compileFragment(Utils.loadResource("/res/fragment-2d.fs")); - this.shader.link(); - - this.shader.create("projModelMatrix"); - this.shader.create("texture_sampler"); - this.shader.create("color"); - this.shader.create("isTextured"); - - this.text = new TextItem("", new FontTexture(FONT_FILE, FONT_COLS, FONT_ROWS)); - this.text.getMesh().setColor(1, 1, 1); - - this.compass = new Item(ObjLoader.loadMesh("/res/compass.obj")); - this.compass.getMesh().setColor(1, 1, 1); - - this.crosshair = new Item(ObjLoader.loadMesh("/res/crosshair.obj")); - this.crosshair.getMesh().setColor(1, 1, 1); - - this.items.add(this.text); - this.items.add(this.compass); - this.items.add(this.crosshair); - - this.window = window; - } - - @Override - public void update(float interval) { - this.text.setPosition(10f, this.window.getHeight() * 0.85f, 0f); - this.text.setScale(this.window.getWidth() * (1/3500f)); - this.text.setText(String.format( - "vsync=%s mode=%s mouse=%s\nchange=%s wait=%s\ncamera=%s\nmouse=%s", - this.window.isVSync(), this.window.getMode(), this.window.getInputMode(GLFW_CURSOR) == GLFW_CURSOR_NORMAL, - this.player.getChange(), Math.max(0, this.player.getWait()), - this.camera, this.mouse - )); - - this.compass.setPosition(this.window.getWidth() * 0.95f, this.window.getWidth() * 0.05f, 0f); - this.compass.setRotation(0f, 0f, 180f - this.camera.getRotation().y); - this.compass.setScale(this.window.getWidth() * (1/20f)); - - this.crosshair.setPosition(this.window.getWidth() * 0.5f, this.window.getHeight() * 0.5f, 0f); - this.crosshair.setScale(this.window.getWidth() * (1/50f)); - } - - @Override - public void render(Window window) { - this.shader.bind(); - this.shader.set("texture_sampler", 0); - - // disable depth testing : source # https://stackoverflow.com/a/5467636 - glDepthMask(false); // disable writes to Z-Buffer - glDisable(GL_DEPTH_TEST); // disable depth-testing - - Matrix4f orthoMatrix = window.getOrthoProjectionMatrix(); - - // draw items - Matrix4f temp = new Matrix4f(); - for (Item item : this.items) - item.getMesh().render(this.shader, item, (shader, item2) -> { - item2.buildOrthoProjModelMatrix(orthoMatrix, temp); - shader.set("projModelMatrix", temp); - }); - - glDepthMask(true); - glEnable(GL_DEPTH_TEST); - this.shader.unbind(); - } - - @Override - public void cleanup() { - this.shader.cleanup(); - for (Item item : this.items) - item.getMesh().close(); - } -} diff --git a/src/game/Player.java b/src/game/Player_.java similarity index 100% rename from src/game/Player.java rename to src/game/Player_.java From 5cb63997e666da364a16c056ce12e87e4a2b561e Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Mon, 6 Jul 2020 12:43:02 -0400 Subject: [PATCH 45/52] Rename Player_ -> Player and update Game --- src/game/Game.java | 7 ++----- src/game/{Player_.java => Player.java} | 0 2 files changed, 2 insertions(+), 5 deletions(-) rename src/game/{Player_.java => Player.java} (100%) diff --git a/src/game/Game.java b/src/game/Game.java index 700c650..59675a8 100644 --- a/src/game/Game.java +++ b/src/game/Game.java @@ -20,9 +20,8 @@ public class Game extends Scene { private Background background; private Skybox skybox; - private Player player; // player actions + private Player player; // player actions and hud private World world; // world loading and rendering - private Hud hud; public Game() { super(); @@ -36,16 +35,14 @@ public Game() { this.skybox = new Skybox(this.camera); this.world = new World(this.camera); this.player = new Player(this.mouse, this.camera, this.world); - this.hud = new Hud(this.mouse, this.camera, this.player); // scene ordering this.addFrom(this.mouse); this.addFrom(this.camera); this.addFrom(this.background); this.addFrom(this.skybox); - this.addFrom(this.player); this.addFrom(this.world); - this.addFrom(this.hud); + this.addFrom(this.player); } @Override diff --git a/src/game/Player_.java b/src/game/Player.java similarity index 100% rename from src/game/Player_.java rename to src/game/Player.java From 4063dff87a00b95a5bc7d65cc515dfb52dc62b29 Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Mon, 6 Jul 2020 12:56:19 -0400 Subject: [PATCH 46/52] Use single instance of setup lambda --- src/game/Player.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/game/Player.java b/src/game/Player.java index 1b124e4..b723eb7 100644 --- a/src/game/Player.java +++ b/src/game/Player.java @@ -8,6 +8,7 @@ import geetransit.minecraft05.engine.*; import java.util.*; +import java.util.function.BiConsumer; import org.joml.Vector3f; import org.joml.Matrix4f; @@ -151,14 +152,15 @@ public void render(Window window) { glDisable(GL_DEPTH_TEST); // disable depth-testing Matrix4f orthoMatrix = window.getOrthoProjectionMatrix(); + Matrix4f temp = new Matrix4f(); + BiConsumer setup = (shader, item) -> { + item.buildOrthoProjModelMatrix(orthoMatrix, temp); + shader.set("projModelMatrix", temp); + }; // draw items - Matrix4f temp = new Matrix4f(); for (Item item : this.items) - item.getMesh().render(this.shader, item, (shader, item2) -> { - item2.buildOrthoProjModelMatrix(orthoMatrix, temp); - shader.set("projModelMatrix", temp); - }); + item.getMesh().render(this.shader, item, setup); glDepthMask(true); glEnable(GL_DEPTH_TEST); From 1860e304f7c0896fa6f3ec0accbeeec1c926c694 Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Mon, 6 Jul 2020 13:35:18 -0400 Subject: [PATCH 47/52] Update Shader.cleanup -> close and remove Shader.cleanup --- src/engine/Shader.java | 5 ----- src/game/Player.java | 2 +- src/game/Skybox.java | 2 +- src/game/World.java | 2 +- 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/engine/Shader.java b/src/engine/Shader.java index 32cdaf8..0f0d9d5 100644 --- a/src/engine/Shader.java +++ b/src/engine/Shader.java @@ -107,9 +107,4 @@ public void close() { if (this.program != 0) glDeleteProgram(this.program); } - - @Deprecated - public void cleanup() { - this.close(); - } } diff --git a/src/game/Player.java b/src/game/Player.java index b723eb7..3ccdf53 100644 --- a/src/game/Player.java +++ b/src/game/Player.java @@ -169,7 +169,7 @@ public void render(Window window) { @Override public void cleanup() { - this.shader.cleanup(); + this.shader.close(); for (Item item : this.items) item.getMesh().close(); } diff --git a/src/game/Skybox.java b/src/game/Skybox.java index c42ee91..d7b0287 100644 --- a/src/game/Skybox.java +++ b/src/game/Skybox.java @@ -115,7 +115,7 @@ public void render(Window window) { @Override public void cleanup() { - this.shader.cleanup(); + this.shader.close(); this.skybox.getMesh().close(); } } diff --git a/src/game/World.java b/src/game/World.java index a6c9b9f..b9eeda7 100644 --- a/src/game/World.java +++ b/src/game/World.java @@ -131,7 +131,7 @@ public void render(Window window) { @Override public void cleanup() { - this.shader.cleanup(); + this.shader.close(); for (Mesh mesh : this.meshes.values()) mesh.close(); } From b10cb93053534913a65835399853b677ab8df2a3 Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Tue, 7 Jul 2020 17:34:32 -0400 Subject: [PATCH 48/52] Update World to prepare for removal of Scene sortBlocks -> updateTransparent init => createShader, buildSpawn, buildSimple update => updateTransparent render => updateVisible, updateSelected, renderWorld cleanup => close --- src/game/World.java | 121 ++++++++++++++++++++++++++++---------------- 1 file changed, 78 insertions(+), 43 deletions(-) diff --git a/src/game/World.java b/src/game/World.java index b9eeda7..cea9207 100644 --- a/src/game/World.java +++ b/src/game/World.java @@ -16,7 +16,7 @@ import static org.lwjgl.glfw.GLFW.*; import static org.lwjgl.opengl.GL11.*; -public class World implements Loopable { +public class World implements Loopable, AutoCloseable { public static final float BLOCK_SCALE = 0.5f; // block scaling (mesh is 2x2x2) public static final float BLOCK_RADIUS = 2f; // radius around block (for frustum culling) @@ -41,6 +41,40 @@ public World(Camera camera) { @Override public void init(Window window) { + this.createShader(); + + // add block types + this.addFullType("grassblock", ObjLoader.loadMesh("/res/cube-fblr,u,d.obj", "/res/grassblock.png")); + this.addFullType("cobbleblock", ObjLoader.loadMesh("/res/cube-fblrud.obj", "/res/cobbleblock.png")); + this.addTransparentType("glassblock", ObjLoader.loadMesh("/res/cube-fblrud.obj", "/res/glassblock.png")); + + // build world + this.buildSpawn("grassblock", "cobbleblock"); + try (Image image = new Image("/res/heightmap.png")) { + // heightmap + this.buildSimple(image, "grassblock", "cobbleblock"); + } + } + + @Override + public void update(float interval) { + // best to leave it up to others whether they want to update or not + } + + @Override + public void render(Window window) { + this.updateTransparent(); + this.updateVisible(); + this.updateSelected(); + this.renderWorld(window); + } + + @Override + public void cleanup() { + this.close(); + } + + public void createShader() { this.shader = new Shader(); this.shader.compileVertex(Utils.loadResource("/res/vertex-3d.vs")); this.shader.compileFragment(Utils.loadResource("/res/fragment-3d-block.fs")); @@ -52,56 +86,66 @@ public void init(Window window) { this.shader.create("color"); this.shader.create("isTextured"); this.shader.create("isSelected"); + } - // create groups and block meshes - this.addFullType("grassblock", ObjLoader.loadMesh("/res/cube-fblr,u,d.obj", "/res/grassblock.png")); - this.addFullType("cobbleblock", ObjLoader.loadMesh("/res/cube-fblrud.obj", "/res/cobbleblock.png")); - this.addTransparentType("glassblock", ObjLoader.loadMesh("/res/cube-fblrud.obj", "/res/glassblock.png")); - - // get heightmap - try (Image image = new Image("/res/heightmap.png")) { - // create terrain - for (int x = 0; x < image.width; x++) { - for (int z = 0; z < image.length; z++) { - int y = (int) Image.compressExpand(image.pixel(x, z), 0, Image.MAX, 0, 16); - this.setBlock("grassblock", x, y, z); - for (int k = y-1; k >= Math.max(y-2, 0); k--) - this.setBlock("cobbleblock", x, k, z); - } + public void buildSimple(Image image, String top, String bottom) { + // create terrain + // T + // T B + // T B B + // B B B + // B B + for (int x = 0; x < image.width; x++) { + for (int z = 0; z < image.length; z++) { + int y = (int) Image.compressExpand(image.pixel(x, z), 0, Image.MAX, 0, 16); + this.setBlock(top, x, y, z); + for (int k = y-1; k >= Math.max(y-2, 0); k--) + this.setBlock(bottom, x, k, z); } } + } + public void buildSpawn(String normal, String left) { // add spawn markers (-2z is forwards, cobble is left) - this.setBlock("grassblock", +1, +1, 0); - this.setBlock("cobbleblock", -1, +1, 0); - this.setBlock("grassblock", 0, +1, +1); - this.setBlock("grassblock", 0, +1, -2); + // N + // + // L N + // N + this.setBlock(normal, +1, 0, 0); + this.setBlock(left, -1, 0, 0); + this.setBlock(normal, 0, 0, +1); + this.setBlock(normal, 0, 0, -2); } - @Override - public void update(float interval) { + public void updateTransparent() { // reorder transparent blocks - this.sortBlocks(this.transparentBlocks); + Vector3f position = this.camera.getPosition(); + this.transparentBlocks.sort(Comparator.comparingDouble( + block -> -block.getPosition().distance(position) + )); } - @Override - public void render(Window window) { - this.shader.bind(); - this.shader.set("texture_sampler", 0); - this.shader.set("projectionMatrix", window.getProjectionMatrix()); - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - + public void updateVisible() { // update visible blocks for (BlockItem block : this.iterBlocks()) block.setVisible(this.camera.insideFrustum(block.getPosition(), BLOCK_RADIUS*block.getScale())); + } + public void updateSelected() { // update selected block for (BlockItem block : this.iterBlocks()) block.setSelected(false); this.closest.update(this.iterBlocks(), this.camera); if (this.closest.closest != null) this.closest.closest.setSelected(true); + } + + public void renderWorld(Window window) { + this.shader.bind(); + this.shader.set("texture_sampler", 0); + this.shader.set("projectionMatrix", window.getProjectionMatrix()); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); Matrix4f viewMatrix = this.camera.getViewMatrix(); // view matrix Matrix4f temp = new Matrix4f(); // temporary matrix (stores model view matrix) @@ -130,7 +174,7 @@ public void render(Window window) { } @Override - public void cleanup() { + public void close() { this.shader.close(); for (Mesh mesh : this.meshes.values()) mesh.close(); @@ -181,12 +225,10 @@ private void addBlock(String name, float x, float y, float z) { private void addBlock(BlockItem block) { String name = this.nameOf(block.getMesh()); - if (this.transparentNames.contains(name)) { + if (this.transparentNames.contains(name)) this.transparentBlocks.add(block); - this.sortBlocks(this.transparentBlocks); - } else { + else this.blocks.get(name).add(block); - } } private void removeBlock(float x, float y, float z) { @@ -205,13 +247,6 @@ private void removeBlock(BlockItem block) { this.blocks.get(name).remove(block); } - private void sortBlocks(List blocks) { - Vector3f position = this.camera.getPosition(); - blocks.sort(Comparator.comparingDouble( - block -> -block.getPosition().distance(position) - )); - } - private String nameOf(Mesh mesh) { for (Map.Entry entry : this.meshes.entrySet()) if (entry.getValue() == mesh) From 981e8e24ce7e53e94bb5c3c28cba15039a49c9b8 Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Tue, 7 Jul 2020 21:26:21 -0400 Subject: [PATCH 49/52] Update Skybox to prepare for removal of Scene --- src/game/Skybox.java | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/src/game/Skybox.java b/src/game/Skybox.java index d7b0287..26f4b3d 100644 --- a/src/game/Skybox.java +++ b/src/game/Skybox.java @@ -14,7 +14,7 @@ import static org.lwjgl.glfw.GLFW.*; import static org.lwjgl.opengl.GL11.*; -public class Skybox implements Loopable { +public class Skybox implements Loopable, AutoCloseable { public static final float RENDER_STEP = 3.0f; // render changed in 1 second public static final float RENDER_MIN = Math.min(Camera.NEAR, 5f); // render changed in 1 second public static final float RENDER_DELAY = 0.5f; // time between skybox toggling @@ -38,6 +38,11 @@ public Skybox(Camera camera) { @Override public void init(Window window) { + this.createShader(); + this.createSkybox("/res/skybox.obj", "/res/skybox.png"); + } + + public void createShader() { this.shader = new Shader(); this.shader.compileVertex(Utils.loadResource("/res/vertex-3d.vs")); this.shader.compileFragment(Utils.loadResource("/res/fragment-3d.fs")); @@ -48,15 +53,19 @@ public void init(Window window) { this.shader.create("texture_sampler"); this.shader.create("color"); this.shader.create("isTextured"); + } - Mesh mesh = ObjLoader.loadMesh("/res/skybox.obj"); - mesh.setTexture(new Texture("/res/skybox.png")); - this.skybox = new Item(mesh); + public void createSkybox(String obj, String texture) { + this.skybox = new Item(ObjLoader.loadMesh(obj, texture)); this.skybox.setPosition(0, 0, 0); } @Override public void input(Window window) { + this.inputSkybox(window); + } + + public void inputSkybox(Window window) { // render distance (camera) this.render = 0; if (window.isKeyDown(GLFW_KEY_L)) this.camera.setFar(Camera.FAR); @@ -71,6 +80,10 @@ public void input(Window window) { @Override public void update(float interval) { + this.updateSkybox(interval); + } + + public void updateSkybox(float interval) { // render distance this.camera.setFar(Math.max(RENDER_MIN, this.camera.getFar() + this.render * interval*RENDER_STEP)); @@ -84,6 +97,12 @@ public void update(float interval) { public void render(Window window) { if (!this.visible) return; + this.renderSkybox(window); + } + + public void renderSkybox(Window window) { + if (this.skybox == null) + return; this.shader.bind(); this.shader.set("texture_sampler", 0); @@ -115,6 +134,11 @@ public void render(Window window) { @Override public void cleanup() { + this.close(); + } + + @Override + public void close() { this.shader.close(); this.skybox.getMesh().close(); } From 3549aa68320ffd4b197fdb0f7c4d6372c1fcf8a1 Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Wed, 8 Jul 2020 00:41:53 -0400 Subject: [PATCH 50/52] Update Skybox to prepare for removal of Scene --- src/game/Player.java | 52 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/src/game/Player.java b/src/game/Player.java index 3ccdf53..b40a896 100644 --- a/src/game/Player.java +++ b/src/game/Player.java @@ -15,7 +15,7 @@ import static org.lwjgl.glfw.GLFW.*; import static org.lwjgl.opengl.GL11.*; -public class Player implements Loopable { +public class Player implements Loopable, AutoCloseable { public static final float CHANGE_DELAY = 0.2f; // time between block change (place / remove) public static final float MOVEMENT_STEP = 3.0f; // distance moved in 1 second public static final float SPRINT_MULTIPLIER = 1.5f; // sprinting change @@ -52,6 +52,12 @@ public Player(Mouse mouse, Camera camera, World world) { @Override public void init(Window window) { + this.window = window; + this.createShader(); + this.createHud(); + } + + public void createShader() { this.shader = new Shader(); this.shader.compileVertex(Utils.loadResource("/res/vertex-2d.vs")); this.shader.compileFragment(Utils.loadResource("/res/fragment-2d.fs")); @@ -61,7 +67,9 @@ public void init(Window window) { this.shader.create("texture_sampler"); this.shader.create("color"); this.shader.create("isTextured"); + } + public void createHud() { this.text = new TextItem("", new FontTexture(FONT_FILE, FONT_COLS, FONT_ROWS)); this.text.getMesh().setColor(1, 1, 1); @@ -74,12 +82,15 @@ public void init(Window window) { this.items.add(this.text); this.items.add(this.compass); this.items.add(this.crosshair); - - this.window = window; } @Override public void input(Window window) { + this.inputMovement(window); + this.inputPlace(window); + } + + public void inputMovement(Window window) { // movement this.movement.zero(); boolean SPRINTING = !window.isKeyDown(GLFW_KEY_LEFT_SHIFT) && window.isKeyDown(GLFW_KEY_LEFT_CONTROL); @@ -94,7 +105,9 @@ public void input(Window window) { if (this.movement.length() > 1f) this.movement.div(this.movement.length()); if (SPRINTING && this.movement.z < 0) this.movement.mul(SPRINT_MULTIPLIER); + } + public void inputPlace(Window window) { // placing / removing this.change = null; if (window.isKeyDown(GLFW_KEY_0)) this.change = ""; @@ -106,9 +119,17 @@ public void input(Window window) { @Override public void update(float interval) { + this.updateMovement(interval); + this.updatePlace(interval); + this.updateHud(this.window); + } + + public void updateMovement(float interval) { // movement this.camera.movePosition(this.movement, interval*MOVEMENT_STEP); + } + public void updatePlace(float interval) { // placing / removing this.countdown.add(interval); if (this.change != null && this.countdown.nextOnce()) { @@ -124,26 +145,32 @@ public void update(float interval) { this.world.setBlock(this.change, position); } } + } - this.text.setPosition(10f, this.window.getHeight() * 0.85f, 0f); - this.text.setScale(this.window.getWidth() * (1/3500f)); + public void updateHud(Window window) { + this.text.setPosition(10f, window.getHeight() * 0.85f, 0f); + this.text.setScale(window.getWidth() * (1/3500f)); this.text.setText(String.format( "vsync=%s mode=%s mouse=%s\nchange=%s wait=%s\ncamera=%s\nmouse=%s", - this.window.isVSync(), this.window.getMode(), this.window.getInputMode(GLFW_CURSOR) == GLFW_CURSOR_NORMAL, + window.isVSync(), window.getMode(), window.getInputMode(GLFW_CURSOR) == GLFW_CURSOR_NORMAL, this.change, Math.max(0, this.countdown.getWait()), this.camera, this.mouse )); - this.compass.setPosition(this.window.getWidth() * 0.95f, this.window.getWidth() * 0.05f, 0f); + this.compass.setPosition(window.getWidth() * 0.95f, window.getWidth() * 0.05f, 0f); this.compass.setRotation(0f, 0f, 180f - this.camera.getRotation().y); - this.compass.setScale(this.window.getWidth() * (1/20f)); + this.compass.setScale(window.getWidth() * (1/20f)); - this.crosshair.setPosition(this.window.getWidth() * 0.5f, this.window.getHeight() * 0.5f, 0f); - this.crosshair.setScale(this.window.getWidth() * (1/50f)); + this.crosshair.setPosition(window.getWidth() * 0.5f, window.getHeight() * 0.5f, 0f); + this.crosshair.setScale(window.getWidth() * (1/50f)); } @Override public void render(Window window) { + this.renderHud(window); + } + + public void renderHud(Window window) { this.shader.bind(); this.shader.set("texture_sampler", 0); @@ -169,6 +196,11 @@ public void render(Window window) { @Override public void cleanup() { + this.close(); + } + + @Override + public void close() { this.shader.close(); for (Item item : this.items) item.getMesh().close(); From 052b57ecce5654b128e8cf06733a6ca5725d8195 Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Wed, 8 Jul 2020 00:46:30 -0400 Subject: [PATCH 51/52] Update Background to prepare for removal of Scene --- src/game/Background.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/game/Background.java b/src/game/Background.java index 472b8b1..8638251 100644 --- a/src/game/Background.java +++ b/src/game/Background.java @@ -22,12 +22,14 @@ public Background() { @Override public void init(Window window) { - // blank background for first frame - window.clearColor(1f, 1f, 1f, 0f); } @Override public void input(Window window) { + this.inputColor(window); + } + + public void inputColor(Window window) { if (window.isKeyDown(GLFW_KEY_L)) this.color = 0f; this.direction = 0; @@ -37,11 +39,19 @@ public void input(Window window) { @Override public void update(float interval) { + this.updateColor(interval); + } + + public void updateColor(float interval) { this.color = Math.max(0f, Math.min(1f, this.color + this.direction * interval*COLOR_STEP)); } @Override public void render(Window window) { + this.updateBackground(window); + } + + public void updateBackground(Window window) { // Different color based on vSync or not (colorful = vSync on) if (window.isVSync()) window.clearColor(1-this.color, this.color/2+0.5f, this.color, 0.0f); From d292709322c4be0e05739ae29e19a827279761af Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Wed, 8 Jul 2020 04:14:23 -0400 Subject: [PATCH 52/52] Update Mouse to prepare for removal of Scene --- src/engine/Mouse.java | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/engine/Mouse.java b/src/engine/Mouse.java index 217fb28..f608978 100644 --- a/src/engine/Mouse.java +++ b/src/engine/Mouse.java @@ -29,21 +29,27 @@ public Mouse() { public boolean isLeft() { return this.left; } public boolean isRight() { return this.right; } + @Override public void init(Window window) { - glfwSetCursorPosCallback(window.getHandle(), (handle, x, y) -> { - this.current.x = (float) x; - this.current.y = (float) y; - }); - glfwSetCursorEnterCallback(window.getHandle(), (handle, entered) -> { - this.inside = entered; - }); + glfwSetCursorPosCallback(window.getHandle(), (handle, x, y) -> { this.setCurrent((float) x, (float) y); }); + glfwSetCursorEnterCallback(window.getHandle(), (handle, inside) -> { this.setInside(inside); }); glfwSetMouseButtonCallback(window.getHandle(), (handle, button, action, mode) -> { - if (button == GLFW_MOUSE_BUTTON_1) this.left = (action == GLFW_PRESS); - if (button == GLFW_MOUSE_BUTTON_2) this.right = (action == GLFW_PRESS); + if (button == GLFW_MOUSE_BUTTON_1) this.setLeft(action == GLFW_PRESS); + if (button == GLFW_MOUSE_BUTTON_2) this.setRight(action == GLFW_PRESS); }); } + public void setCurrent(float x, float y) { this.current.set(x, y); } + public void setInside(boolean inside) { this.inside = inside; } + public void setLeft(boolean left) { this.left = left; } + public void setRight(boolean right) { this.right = right; } + + @Override public void input(Window window) { + this.flush(); + } + + public void flush() { this.current.sub(this.previous, this.movement); this.previous.set(this.current); }