From c52ab34f71155013cf1fe925c8901adb4db249d0 Mon Sep 17 00:00:00 2001 From: GeeTransit Date: Wed, 17 Jun 2020 11:23:57 -0400 Subject: [PATCH] Add instanced rendering (not working) --- res/fragment-3d.fs | 2 +- res/vertex-3d.vs | 13 ++- src/engine/InstancedMesh.java | 96 ++++++++++++++++++++ src/engine/Item.java | 9 +- src/engine/Mesh.java | 36 +++----- src/engine/ObjLoader.java | 52 +++++------ src/engine/Renderer.java | 163 +++++++++++++++++++--------------- src/engine/SceneRender.java | 16 +++- src/engine/Shader.java | 1 + src/engine/TextItem.java | 3 +- src/engine/Window.java | 10 +++ src/game/Hud.java | 12 ++- src/game/Skybox.java | 12 ++- src/game/World.java | 15 ++-- 14 files changed, 291 insertions(+), 149 deletions(-) create mode 100644 src/engine/InstancedMesh.java diff --git a/res/fragment-3d.fs b/res/fragment-3d.fs index 1ba8629..06bc248 100644 --- a/res/fragment-3d.fs +++ b/res/fragment-3d.fs @@ -9,7 +9,7 @@ uniform int useTexture; void main() { - if (useTexture == 1) + if (useTexture < 0) { fragColor = texture(texture_sampler, outCoord); } diff --git a/res/vertex-3d.vs b/res/vertex-3d.vs index 5471826..0d09c3b 100644 --- a/res/vertex-3d.vs +++ b/res/vertex-3d.vs @@ -3,14 +3,25 @@ // bind in Shader.link in vec3 position; // layout(location = 0) in vec2 coord; // layout(location = 1) +in mat4 modelViewInstancedMatrix; // layout(location = 2-5) out vec2 outCoord; uniform mat4 projectionMatrix; uniform mat4 modelViewMatrix; +uniform int useInstanced; void main() { - gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); + mat4 mvMatrix; + if ( useInstanced < 0 ) + { + mvMatrix = modelViewInstancedMatrix; + } + else + { + mvMatrix = modelViewMatrix; + } + gl_Position = projectionMatrix * mvMatrix * vec4(position, 1.0); outCoord = coord; } diff --git a/src/engine/InstancedMesh.java b/src/engine/InstancedMesh.java new file mode 100644 index 0000000..c097cde --- /dev/null +++ b/src/engine/InstancedMesh.java @@ -0,0 +1,96 @@ +/* +ahbejarano +Instanced mesh class. +*/ + +package geetransit.minecraft05.engine; + +import java.util.*; +import java.util.function.*; +import java.nio.*; +import org.joml.Matrix4f; +import org.lwjgl.system.*; +import static org.lwjgl.opengl.GL33.*; + +public class InstancedMesh extends Mesh { + private static final int FLOAT_BYTES = 4; + private static final int VECTOR4F_BYTES = 4*FLOAT_BYTES; + private static final int MATRIX4F_BYTES = 4*VECTOR4F_BYTES; + private static final int MATRIX4F_SIZE = 4*4; + + protected final int instances; + protected final int modelViewVBO; + protected FloatBuffer modelViewBuffer; + + public InstancedMesh(float[] posArray, int[] indexArray, float[] coordArray, int instances) { + super(posArray, indexArray, coordArray); + this.instances = instances; + + glBindVertexArray(this.vaoId); + + // model view matrix + this.modelViewVBO = glGenBuffers(); + this.vboIdList.add(this.modelViewVBO); + this.modelViewBuffer = MemoryUtil.memAllocFloat(this.instances*MATRIX4F_SIZE); + glBindBuffer(GL_ARRAY_BUFFER, this.modelViewVBO); + int stride = 2; + for (int pointer = 0; pointer < 4; pointer++) { + glEnableVertexAttribArray(stride); + System.out.println("glEnableVertexAttribArray:"+glGetError()); + glVertexAttribPointer(stride, 4, GL_FLOAT, false, MATRIX4F_BYTES, pointer*VECTOR4F_BYTES); + System.out.println("glVertexAttribPointer:"+glGetError()); + glVertexAttribDivisor(stride, 1); + System.out.println("glVertexAttribDivisor:"+glGetError()); + stride++; + } + + // Unbind the VBO / VAB + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + } + + @Override + public void cleanup() { + super.cleanup(); + if (this.modelViewBuffer != null) { + MemoryUtil.memFree(this.modelViewBuffer); + this.modelViewBuffer = null; + } + } + + public int getInstances() { return this.instances; } + + public void render3DList(List items, Transformation transformation, Matrix4f viewMatrix) { + int size = this.getInstances(); + int length = items.size(); + for (int start = 0; start < length; start += size) { + int end = Math.min(length, start + size); + List chunk = items.subList(start, end); + render3DChunk(chunk, transformation, viewMatrix); + } + } + + private void render3DChunk(List items, Transformation transformation, Matrix4f viewMatrix) { + this.modelViewBuffer.clear(); + + int index = 0; + for (Item item : items) { + Matrix4f modelViewMatrix = transformation.getModelViewMatrix(item, viewMatrix); + modelViewMatrix.get(index*MATRIX4F_SIZE, this.modelViewBuffer); + index++; + } + + glBindBuffer(GL_ARRAY_BUFFER, this.modelViewVBO); + // System.out.println("glBindBuffer:"+glGetError()); + glBufferData(GL_ARRAY_BUFFER, this.modelViewBuffer, GL_DYNAMIC_DRAW); + // System.out.println("glBufferData:"+glGetError()); + + glDrawElementsInstanced(GL_TRIANGLES, this.vertexCount, GL_UNSIGNED_INT, 0, items.size()); + // System.out.println("glDrawElementsInstanced:"+glGetError()); + // glDrawArraysInstanced(GL_TRIANGLES, 0, this.vertexCount, items.size()); + // System.out.println("glDrawArraysInstanced:"+glGetError()); + glBindBuffer(GL_ARRAY_BUFFER, 0); + // System.out.println("glBindBuffer(0):"+glGetError()); + // throw new RuntimeException("why are you my clarity"); + } +} diff --git a/src/engine/Item.java b/src/engine/Item.java index a646ef5..3ef6d46 100644 --- a/src/engine/Item.java +++ b/src/engine/Item.java @@ -6,12 +6,13 @@ package geetransit.minecraft05.engine; import org.joml.Vector3f; +import org.joml.Quaternionf; public class Item { protected Mesh mesh; private final Vector3f position; - private final Vector3f rotation; // Degrees, not radians. + private final Quaternionf rotation; // Degrees, not radians. private float scale; public Item(Mesh mesh) { @@ -20,7 +21,7 @@ public Item(Mesh mesh) { } public Item() { this.position = new Vector3f(); - this.rotation = new Vector3f(); + this.rotation = new Quaternionf(); this.scale = 1; } @@ -36,7 +37,7 @@ public void cleanup() { public Mesh getMesh() { return this.mesh; } public Vector3f getPosition() { return this.position; } - public Vector3f getRotation() { return this.rotation; } + public Quaternionf getRotation() { return this.rotation; } public float getScale() { return this.scale; } public Item setPosition(Vector3f position) { this.position.set(position); return this; } @@ -47,7 +48,7 @@ public Item setPosition(float x, float y, float z) { return this; } - public Item setRotation(Vector3f rotation) { this.rotation.set(rotation); return this; } + public Item setRotation(Quaternionf rotation) { this.rotation.set(rotation); return this; } public Item setRotation(float x, float y, float z) { this.rotation.x = x; this.rotation.y = y; diff --git a/src/engine/Mesh.java b/src/engine/Mesh.java index 7cb1d37..a54b332 100644 --- a/src/engine/Mesh.java +++ b/src/engine/Mesh.java @@ -6,22 +6,23 @@ package geetransit.minecraft05.engine; import java.util.*; +import java.util.function.*; import java.nio.*; import org.joml.*; import static org.lwjgl.opengl.GL30.*; import static org.lwjgl.system.MemoryUtil.*; public class Mesh { - private static final Vector3f DEFAULT_COLOUR = new Vector3f(0.0f, 0.0f, 0.0f); + public 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; + protected final int vaoId; + protected final int vertexCount; + protected final List vboIdList; - private Texture texture; - private Vector4f color; + protected Texture texture; + protected Vector4f color; - public Mesh(float[] posArray, int[] indexArray, float[] coordArray, float[] normalArray) { + public Mesh(float[] posArray, int[] indexArray, float[] coordArray) { this.vboIdList = new ArrayList<>(); this.vertexCount = indexArray.length; int vboId; @@ -29,7 +30,6 @@ public Mesh(float[] posArray, int[] indexArray, float[] coordArray, float[] norm FloatBuffer posBuffer = null; IntBuffer indexBuffer = null; FloatBuffer coordBuffer = null; - FloatBuffer normalBuffer = null; try { // Create the VAO this.vaoId = glGenVertexArrays(); @@ -64,16 +64,6 @@ public Mesh(float[] posArray, int[] indexArray, float[] coordArray, float[] norm glBufferData(GL_ARRAY_BUFFER, coordBuffer, GL_STATIC_DRAW); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 2, GL_FLOAT, false, 0, 0); - - // normals VBO - vboId = glGenBuffers(); - this.vboIdList.add(vboId); - normalBuffer = memAllocFloat(normalArray.length); - normalBuffer.put(normalArray).flip(); - glBindBuffer(GL_ARRAY_BUFFER, vboId); - glBufferData(GL_ARRAY_BUFFER, normalBuffer, GL_STATIC_DRAW); - glEnableVertexAttribArray(2); - glVertexAttribPointer(2, 3, GL_FLOAT, false, 0, 0); // Unbind the VBO / VAB glBindBuffer(GL_ARRAY_BUFFER, 0); @@ -83,7 +73,6 @@ public Mesh(float[] posArray, int[] indexArray, float[] coordArray, float[] norm if (posBuffer != null) memFree(posBuffer); if (indexBuffer != null) memFree(indexBuffer); if (coordBuffer != null) memFree(coordBuffer); - if (normalBuffer != null) memFree(normalBuffer); } } @@ -96,14 +85,14 @@ public Mesh(float[] posArray, int[] indexArray, float[] coordArray, float[] norm public Mesh setTexture(Texture texture) { this.texture = texture; return this; } public Mesh setColor(Vector3f color) { this.color = new Vector4f(color, 1f); return this; } public Mesh setColor(Vector4f color) { this.color = color; return this; } - public boolean isTexture() { return this.texture != null; } + public boolean useTexture() { return this.texture != null; } // prepare mesh public void prepare() { this.prepare(null); } public void prepare(Mesh lastMesh) { if (this == lastMesh) return; - if (this.isTexture()) { + if (this.useTexture()) { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, this.texture.getId()); } @@ -126,7 +115,8 @@ public void restore(Mesh nextMesh) { protected void deleteVbos() { // Delete the VBO glBindBuffer(GL_ARRAY_BUFFER, 0); - this.vboIdList.stream().forEach(id -> glDeleteBuffers(id)); + for (int id : this.vboIdList) + glDeleteBuffers(id); } protected void disableVao() { @@ -143,7 +133,7 @@ protected void deleteVao() { public void cleanup(boolean cleanupTexture) { this.disableVao(); this.deleteVbos(); - if (cleanupTexture && this.isTexture()) { + if (cleanupTexture && this.useTexture()) { this.texture.cleanup(); this.texture = null; } diff --git a/src/engine/ObjLoader.java b/src/engine/ObjLoader.java index af8ee53..9360a22 100644 --- a/src/engine/ObjLoader.java +++ b/src/engine/ObjLoader.java @@ -11,12 +11,27 @@ import org.joml.Vector2f; public class ObjLoader { + @FunctionalInterface + public static interface MeshCreator { + T create(float[] posArray, int[] indexArray, float[] coordArray); + } + + public static MeshCreator toInstancedMesh(int instances) { + return (pos, index, coord) -> new InstancedMesh(pos, index, coord, instances); + } + public static MeshCreator toMesh() { + return (pos, index, coord) -> new Mesh(pos, index, coord); + } + public static Mesh loadMesh(String file) throws Exception { + return loadMesh(file, toMesh()); + } + + public static T loadMesh(String file, MeshCreator meshCreator) throws Exception { Stream lines = Utils.loadLinesStream(file); List vertices = new ArrayList<>(); List textures = new ArrayList<>(); - List normals = new ArrayList<>(); List faces = new ArrayList<>(); lines.forEach(line -> { @@ -39,11 +54,6 @@ public static Mesh loadMesh(String file) throws Exception { break; case "vn": // Vertex normal - normals.add(new Vector3f( - Float.parseFloat(tokens[1]), - Float.parseFloat(tokens[2]), - Float.parseFloat(tokens[3]) - )); break; case "f": Face face = new Face(tokens[1], tokens[2], tokens[3]); @@ -54,14 +64,14 @@ public static Mesh loadMesh(String file) throws Exception { break; } }); - return reorderLists(vertices, textures, normals, faces); + return reorderLists(vertices, textures, faces, meshCreator); } - private static Mesh reorderLists( + private static T reorderLists( List vertexList, List coordList, - List normalList, - List faceList + List faceList, + MeshCreator meshCreator ) { List posList = new ArrayList<>(); // Create position array in the order it has been declared @@ -73,27 +83,21 @@ private static Mesh reorderLists( posArray[i*3 + 2] = pos.z; } float[] coordArray = new float[vertexList.size() * 2]; - float[] normalArray = new float[vertexList.size() * 3]; for (Face face : faceList) for (IndexGroup group : face.groups) - processFaceVertex( - group, coordList, normalList, - posList, coordArray, normalArray - ); + processFaceVertex(group, coordList, posList, coordArray); // int[] indexArray = new int[indices.size()]; int[] indexArray = Utils.intListToArray(posList); - return new Mesh(posArray, indexArray, coordArray, normalArray); + return meshCreator.create(posArray, indexArray, coordArray); } private static void processFaceVertex( IndexGroup group, List coordList, - List normalList, List posList, - float[] coordArray, - float[] normalArray + float[] coordArray ) { // Set pos for vertex coordinates int pos = group.pos; @@ -105,25 +109,16 @@ private static void processFaceVertex( coordArray[pos*2 + 0] = coord.x; coordArray[pos*2 + 1] = 1 - coord.y; } - if (group.normal != IndexGroup.NO_VALUE) { - // Reorder normal vectors - Vector3f normal = normalList.get(group.normal); - normalArray[pos*3 + 0] = normal.x; - normalArray[pos*3 + 1] = normal.y; - normalArray[pos*3 + 2] = normal.z; - } } protected static class IndexGroup { public static final int NO_VALUE = -1; public int pos; public int coord; - public int normal; public IndexGroup() { this.pos = NO_VALUE; this.coord = NO_VALUE; - this.normal = NO_VALUE; } } @@ -155,7 +150,6 @@ private IndexGroup parseLine(String line) { if (length <= 2) return group; - group.normal = Integer.parseInt(tokens[2]) - 1; return group; } } diff --git a/src/engine/Renderer.java b/src/engine/Renderer.java index 21e4f7b..6207b87 100644 --- a/src/engine/Renderer.java +++ b/src/engine/Renderer.java @@ -10,26 +10,23 @@ import static org.lwjgl.opengl.GL30.*; public abstract class Renderer { - protected SceneRender parent; - protected Shader shader; protected Transformation transformation; private static final float Z_NEAR = 0.01f; private static final float Z_FAR = 1000f; - public Renderer(SceneRender parent) { - this.parent = parent; + public Renderer() { this.transformation = new Transformation(); } - public SceneRender getParent() { return this.parent; } - public Renderer setParent(SceneRender parent) { this.parent = parent; return this; } + // create shaders + public abstract void init(Window window) throws Exception; - public void init(Window window) throws Exception { - this.shader = this.create(window); - } + // render scene + public abstract void render(Window window); - public abstract Shader create(Window window) throws Exception; + // cleanup shaders + public abstract void cleanup(); public Shader createShader(String vertex, String fragment) throws Exception { Shader shader = new Shader(); @@ -43,6 +40,7 @@ public Shader create3D(String vertex, String fragment) throws Exception { Shader shader = this.createShader(vertex, fragment); shader.createUniform("projectionMatrix"); shader.createUniform("modelViewMatrix"); + shader.createUniform("useInstanced"); shader.createUniform("texture_sampler"); shader.createUniform("color"); shader.createUniform("useTexture"); @@ -58,71 +56,60 @@ public Shader create2D(String vertex, String fragment) throws Exception { return shader; } - public abstract void render(Window window); - - public void render3D(Window window, Camera camera) { - this.render3D(window, camera, this.parent.getItems()); - } - public void render3D(Window window, Camera camera, List items) { - this.shader.bind(); - + public void render3D(Shader shader, Window window, Camera camera, SceneRender scene) { + shader.bind(); glEnable(GL_CULL_FACE); glCullFace(GL_BACK); // projection Matrix4f projectionMatrix = this.transformation.getProjectionMatrix(window, camera); - this.shader.setUniform("projectionMatrix", projectionMatrix); + shader.setUniform("projectionMatrix", projectionMatrix); // view Matrix4f viewMatrix = this.transformation.getViewMatrix(camera); // Draw meshes - this.shader.setUniform("texture_sampler", 0); + shader.setUniform("texture_sampler", 0); + shader.setUniform("useInstanced", 0); + List items = scene.getItems(); 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); - this.shader.setUniform("modelViewMatrix", modelViewMatrix); - this.shader.setUniform("color", mesh.getColor()); - this.shader.setUniform("useTexture", mesh.isTexture()); - mesh.prepare(this.getMeshFromItems(items, i-1, itemsSize)); - mesh.render(); - mesh.restore(this.getMeshFromItems(items, i+1, itemsSize)); + shader.setUniform("modelViewMatrix", modelViewMatrix); + shader.setUniform("color", item.getMesh().getColor()); + shader.setUniform("useTexture", item.getMesh().useTexture()); + item.render(window); } glDisable(GL_CULL_FACE); - - this.shader.unbind(); + shader.unbind(); } - public void render3DSingle(Window window, Camera camera) { - this.render3DSingle(window, camera, this.parent.getItems()); - } - public void render3DSingle(Window window, Camera camera, List items) { - this.shader.bind(); - + public void render3DList(Shader shader, Window window, Camera camera, SceneRender scene) { + shader.bind(); glEnable(GL_CULL_FACE); glCullFace(GL_BACK); // projection Matrix4f projectionMatrix = this.transformation.getProjectionMatrix(window, camera); - this.shader.setUniform("projectionMatrix", projectionMatrix); + shader.setUniform("projectionMatrix", projectionMatrix); // view Matrix4f viewMatrix = this.transformation.getViewMatrix(camera); - // Draw meshes - this.shader.setUniform("texture_sampler", 0); + shader.setUniform("texture_sampler", 0); + shader.setUniform("useInstanced", 0); + List items = scene.getItems(); if (!items.isEmpty()) { Mesh firstMesh = items.get(0).getMesh(); - this.shader.setUniform("color", firstMesh.getColor()); - this.shader.setUniform("useTexture", firstMesh.isTexture()); + shader.setUniform("color", firstMesh.getColor()); + shader.setUniform("useTexture", firstMesh.useTexture()); firstMesh.prepare(); for (Item item : items) { Matrix4f modelViewMatrix = this.transformation.getModelViewMatrix(item, viewMatrix); - this.shader.setUniform("modelViewMatrix", modelViewMatrix); + shader.setUniform("modelViewMatrix", modelViewMatrix); item.getMesh().render(); } @@ -130,15 +117,55 @@ public void render3DSingle(Window window, Camera camera, List items) { } glDisable(GL_CULL_FACE); - - this.shader.unbind(); + shader.unbind(); } - public void render2D(Window window) { - this.render2D(window, this.parent.getItems()); + // note does NOT call Item.render(Window) + public void render3DMap(Shader shader, Window window, Camera camera, SceneRender scene) { + shader.bind(); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + + // projection + Matrix4f projectionMatrix = this.transformation.getProjectionMatrix(window, camera); + shader.setUniform("projectionMatrix", projectionMatrix); + + // view + Matrix4f viewMatrix = this.transformation.getViewMatrix(camera); + + // Draw meshes + shader.setUniform("texture_sampler", 0); + for (Map.Entry> entry : scene.getMeshMap().entrySet()) { + Mesh mesh = entry.getKey(); + List items = entry.getValue(); + shader.setUniform("color", mesh.getColor()); + shader.setUniform("useTexture", mesh.useTexture()); + mesh.prepare(); + + // check if it's instanced + if (mesh instanceof InstancedMesh) { + // instanced : render all of them + shader.setUniform("useInstanced", 1); + InstancedMesh instancedMesh = (InstancedMesh) mesh; + instancedMesh.render3DList(items, this.transformation, viewMatrix); + } else { + // single : loop through items + shader.setUniform("useInstanced", 0); + for (Item item : items) { + Matrix4f modelViewMatrix = this.transformation.getModelViewMatrix(item, viewMatrix); + shader.setUniform("modelViewMatrix", modelViewMatrix); + mesh.render(); + } + } + mesh.restore(); + } + + glDisable(GL_CULL_FACE); + shader.unbind(); } - public void render2D(Window window, List items) { - this.shader.bind(); + + public void render2D(Shader shader, Window window, SceneRender scene) { + shader.bind(); // source # https://stackoverflow.com/a/5467636 glDepthMask(false); // disable writes to Z-Buffer @@ -147,30 +174,27 @@ public void render2D(Window window, List items) { Matrix4f orthoMatrix = this.transformation.getOrthoProjectionMatrix(window); // Draw meshes - this.shader.setUniform("texture_sampler", 0); - for (Item item : items) { + shader.setUniform("texture_sampler", 0); + for (Item item : scene.getItems()) { Matrix4f projModelMatrix = this.transformation.getOrthoProjModelMatrix(item, orthoMatrix); - this.shader.setUniform("projModelMatrix", projModelMatrix); - this.shader.setUniform("color", item.getMesh().getColor()); - this.shader.setUniform("useTexture", item.getMesh().isTexture()); + shader.setUniform("projModelMatrix", projModelMatrix); + shader.setUniform("color", item.getMesh().getColor()); + shader.setUniform("useTexture", item.getMesh().useTexture()); item.render(window); } glDepthMask(true); glEnable(GL_DEPTH_TEST); - this.shader.unbind(); + shader.unbind(); } - public void renderSkybox(Window window, Camera camera) { - this.renderSkybox(window, camera, this.parent.getItems()); - } - public void renderSkybox(Window window, Camera camera, List items) { - this.shader.bind(); + public void renderSkybox(Shader shader, Window window, Camera camera, SceneRender scene) { + shader.bind(); // projection Matrix4f projectionMatrix = this.transformation.getProjectionMatrix(window, camera); - this.shader.setUniform("projectionMatrix", projectionMatrix); + shader.setUniform("projectionMatrix", projectionMatrix); // view Matrix4f viewMatrix = this.transformation.getViewMatrix(camera); @@ -179,29 +203,24 @@ public void renderSkybox(Window window, Camera camera, List items) { viewMatrix.setTranslation(0, 0, 0); // Draw meshes - this.shader.setUniform("texture_sampler", 0); - for (Item item : items) { + shader.setUniform("texture_sampler", 0); + shader.setUniform("useInstanced", 0); + for (Item item : scene.getItems()) { Matrix4f modelViewMatrix = this.transformation.getModelViewMatrix(item, viewMatrix); - this.shader.setUniform("modelViewMatrix", modelViewMatrix); - this.shader.setUniform("color", item.getMesh().getColor()); - this.shader.setUniform("useTexture", item.getMesh().isTexture()); + shader.setUniform("modelViewMatrix", modelViewMatrix); + shader.setUniform("color", item.getMesh().getColor()); + shader.setUniform("useTexture", item.getMesh().useTexture()); item.render(window); } - this.shader.unbind(); + shader.unbind(); } + // this WILL return null 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 cleanup() { - if (this.shader != null) { - this.shader.cleanup(); - this.shader = null; - } - } } diff --git a/src/engine/SceneRender.java b/src/engine/SceneRender.java index 1b902bb..5c045f1 100644 --- a/src/engine/SceneRender.java +++ b/src/engine/SceneRender.java @@ -5,26 +5,34 @@ package geetransit.minecraft05.engine; -import java.util.List; -import java.util.ArrayList; +import java.util.*; public abstract class SceneRender extends SceneBase { protected Renderer renderer; protected List items; + protected 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 List getItems() { return this.items; } public SceneRender setRenderer(Renderer renderer) { this.renderer = renderer; return this; } - public SceneRender addItem(Item item) { this.items.add(item); return this; } + + public List getItems() { return this.items; } + public Map> getMeshMap() { return this.meshMap; } + public SceneRender addItem(Item item) { + this.items.add(item); + this.meshMap.putIfAbsent(item.getMesh(), new ArrayList<>()); + this.meshMap.get(item.getMesh()).add(item); + return this; + } @Override public void init(Window window) throws Exception { diff --git a/src/engine/Shader.java b/src/engine/Shader.java index b74936b..24fb9c2 100644 --- a/src/engine/Shader.java +++ b/src/engine/Shader.java @@ -95,6 +95,7 @@ public void link() throws Exception { // equivalent of `layout (location = #) ...` glBindAttribLocation(this.programId, 0, "position"); glBindAttribLocation(this.programId, 1, "coords"); + glBindAttribLocation(this.programId, 2, "modelViewInstancedMatrix"); // 2-5 glValidateProgram(this.programId); if (glGetProgrami(this.programId, GL_VALIDATE_STATUS) == 0) { System.err.println("Warning validating Shader code: " + glGetProgramInfoLog(this.programId, 1024)); diff --git a/src/engine/TextItem.java b/src/engine/TextItem.java index 8762ebf..7aa84bd 100644 --- a/src/engine/TextItem.java +++ b/src/engine/TextItem.java @@ -107,7 +107,6 @@ private Mesh buildMesh(Texture texture) { float[] posArray = Utils.floatListToArray(posList); float[] coordArray = Utils.floatListToArray(coordList); int[] indexArray = Utils.intListToArray(indexList); - float[] normalArray = new float[0]; - return new Mesh(posArray, indexArray, coordArray, normalArray).setTexture(texture); + return new Mesh(posArray, indexArray, coordArray).setTexture(texture); } } diff --git a/src/engine/Window.java b/src/engine/Window.java index b2d156d..cdd0737 100644 --- a/src/engine/Window.java +++ b/src/engine/Window.java @@ -6,6 +6,7 @@ package geetransit.minecraft05.engine; import org.lwjgl.glfw.*; +import org.lwjgl.system.*; import org.lwjgl.opengl.*; import static org.lwjgl.glfw.Callbacks.*; @@ -41,6 +42,7 @@ public class Window { private long monitor; private GLFWVidMode vidmode; + private Callback errorCallbackGL; public Window( String title, @@ -79,6 +81,7 @@ public void createWindow() { 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 + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // debug // Get the resolution of the primary monitor this.monitor = glfwGetPrimaryMonitor(); @@ -138,6 +141,10 @@ public void eventTerminate() { // Terminate GLFW and release the error function glfwTerminate(); glfwSetErrorCallback(null).free(); + if (this.errorCallbackGL != null) { + this.errorCallbackGL.free(); + this.errorCallbackGL = null; + } } public void renderThread(Scene scene) { @@ -163,6 +170,9 @@ private void renderInit(Scene scene) throws Exception { // bindings available for use. GL.createCapabilities(); + // add error output + this.errorCallbackGL = GLUtil.setupDebugMessageCallback(System.err); + // Check vSync glfwSwapInterval(this.isVSync() ? 1 : 0); diff --git a/src/game/Hud.java b/src/game/Hud.java index e13dae6..495b499 100644 --- a/src/game/Hud.java +++ b/src/game/Hud.java @@ -20,12 +20,16 @@ public class Hud extends SceneRender { public Hud(Mouse mouse, Camera camera) { super(); - this.setRenderer(new Renderer(this) { - public Shader create(Window window) throws Exception { - return this.create2D("/res/vertex-2d.vs", "/res/fragment-2d.fs"); + this.setRenderer(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) { - this.render2D(window); + render2D(shader, window, Hud.this); + } + public void cleanup() { + if (shader != null) shader.cleanup(); } }); this.mouse = mouse; diff --git a/src/game/Skybox.java b/src/game/Skybox.java index f55e4e4..2eb47cf 100644 --- a/src/game/Skybox.java +++ b/src/game/Skybox.java @@ -13,12 +13,16 @@ public class Skybox extends SceneRender { public Skybox(Camera camera) { super(); - this.setRenderer(new Renderer(this) { - public Shader create(Window window) throws Exception { - return this.create3D("/res/vertex-3d.vs", "/res/fragment-3d.fs"); + this.setRenderer(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) { - this.renderSkybox(window, Skybox.this.getCamera()); + render3DList(shader, window, Skybox.this.getCamera(), Skybox.this); + } + public void cleanup() { + if (shader != null) shader.cleanup(); } }); this.camera = camera; diff --git a/src/game/World.java b/src/game/World.java index 5e331d0..e9fcfb7 100644 --- a/src/game/World.java +++ b/src/game/World.java @@ -30,12 +30,16 @@ public class World extends SceneRender { public World(Mouse mouse, Camera camera) { super(); - this.setRenderer(new Renderer(this) { - public Shader create(Window window) throws Exception { - return this.create3D("/res/vertex-3d.vs", "/res/fragment-3d.fs"); + this.setRenderer(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) { - this.render3DSingle(window, World.this.getCamera()); + render3DMap(shader, window, World.this.getCamera(), World.this); + } + public void cleanup() { + if (shader != null) shader.cleanup(); } }); @@ -59,7 +63,8 @@ public void init(Window window) throws Exception { super.init(window); // Create the cube mesh - Mesh mesh = ObjLoader.loadMesh("/res/cube.obj"); + // Mesh mesh = ObjLoader.loadMesh("/res/cube.obj"); + InstancedMesh mesh = ObjLoader.loadMesh("/res/cube.obj", ObjLoader.toInstancedMesh(8)); mesh.setTexture(new Texture("/res/grassblock.png")); // get heightmap