From e04a4fad0203cc44720a009a98943d8b9b52c211 Mon Sep 17 00:00:00 2001 From: jglrxavpok Date: Tue, 11 Aug 2020 23:23:14 +0200 Subject: [PATCH 1/6] Fast render with shader color lookup (wrong colors for the moment) --- build.gradle | 2 + .../minestom/demo/largeframebuffers/Demo.java | 33 +-- .../largeframebuffers/OpenGLRendering.java | 218 ++++++++++++++++++ .../largeframebuffers/PaletteGenerator.java | 41 ++++ .../map/framebuffers/GLFWCapableBuffer.java | 45 +++- src/lwjgl/resources/shaders/fragment.glsl | 33 +++ src/lwjgl/resources/shaders/vertex.glsl | 16 ++ src/lwjgl/resources/textures/box.png | Bin 0 -> 274 bytes .../net/minestom/server/map/MapColors.java | 2 +- 9 files changed, 356 insertions(+), 34 deletions(-) create mode 100644 src/lwjgl/java/net/minestom/demo/largeframebuffers/OpenGLRendering.java create mode 100644 src/lwjgl/java/net/minestom/demo/largeframebuffers/PaletteGenerator.java create mode 100644 src/lwjgl/resources/shaders/fragment.glsl create mode 100644 src/lwjgl/resources/shaders/vertex.glsl create mode 100644 src/lwjgl/resources/textures/box.png diff --git a/build.gradle b/build.gradle index fcce68d31..a054c35f3 100644 --- a/build.gradle +++ b/build.gradle @@ -125,6 +125,8 @@ dependencies { lwjglApi "org.lwjgl:lwjgl-opengl" lwjglApi "org.lwjgl:lwjgl-opengles" lwjglApi "org.lwjgl:lwjgl-glfw" + lwjglApi "org.lwjgl:lwjgl-glfw" + lwjglApi 'org.joml:joml:1.9.25' lwjglRuntimeOnly "org.lwjgl:lwjgl::$lwjglNatives" lwjglRuntimeOnly "org.lwjgl:lwjgl-opengl::$lwjglNatives" lwjglRuntimeOnly "org.lwjgl:lwjgl-opengles::$lwjglNatives" diff --git a/src/lwjgl/java/net/minestom/demo/largeframebuffers/Demo.java b/src/lwjgl/java/net/minestom/demo/largeframebuffers/Demo.java index 68a0c7aba..4607b36bb 100644 --- a/src/lwjgl/java/net/minestom/demo/largeframebuffers/Demo.java +++ b/src/lwjgl/java/net/minestom/demo/largeframebuffers/Demo.java @@ -24,8 +24,6 @@ import java.awt.*; import java.util.Arrays; import java.util.function.Consumer; -import static org.lwjgl.opengl.GL11.*; - public class Demo { public static void main(String[] args) { @@ -45,11 +43,17 @@ public class Demo { LargeGraphics2DFramebuffer graphics2DFramebuffer = new LargeGraphics2DFramebuffer(512, 512); LargeGLFWFramebuffer glfwFramebuffer = new LargeGLFWFramebuffer(512, 512); - renderingLoop(0, directFramebuffer, Demo::directRendering); - renderingLoop(101, graphics2DFramebuffer, Demo::graphics2DRendering); + glfwFramebuffer.useMapColors(); + + glfwFramebuffer.changeRenderingThreadToCurrent(); + OpenGLRendering.init(); + glfwFramebuffer.unbindContextFromThread(); + + // renderingLoop(0, directFramebuffer, Demo::directRendering); + // renderingLoop(101, graphics2DFramebuffer, Demo::graphics2DRendering); renderingLoop(201, glfwFramebuffer, f -> {}); - glfwFramebuffer.setupRenderLoop(30, TimeUnit.MILLISECOND, Demo::openGLRendering); + glfwFramebuffer.setupRenderLoop(15, TimeUnit.MILLISECOND, () -> OpenGLRendering.render(glfwFramebuffer)); for (int x = -2; x <= 2; x++) { for (int z = -2; z <= 2; z++) { @@ -97,7 +101,7 @@ public class Demo { f.preparePacket(packet); sendPacket(packet); } - }).repeat(30, TimeUnit.MILLISECOND).schedule(); + }).repeat(15, TimeUnit.MILLISECOND).schedule(); } private static void sendPacket(MapDataPacket packet) { @@ -117,21 +121,4 @@ public class Demo { renderer.drawString("Here's a very very long string that needs multiple maps to fit", 0, 100); } - private static void openGLRendering() { - glClearColor(0f, 0f, 0f, 1f); - glClear(GL_COLOR_BUFFER_BIT); - - glBegin(GL_TRIANGLES); - - glVertex2f(0, -0.75f); - glColor3f(1f, 0f, 0f); - - glVertex2f(0.75f, 0.75f); - glColor3f(0f, 1f, 0f); - - glVertex2f(-0.75f, 0.75f); - glColor3f(0f, 0f, 1f); - - glEnd(); - } } diff --git a/src/lwjgl/java/net/minestom/demo/largeframebuffers/OpenGLRendering.java b/src/lwjgl/java/net/minestom/demo/largeframebuffers/OpenGLRendering.java new file mode 100644 index 000000000..cd57de559 --- /dev/null +++ b/src/lwjgl/java/net/minestom/demo/largeframebuffers/OpenGLRendering.java @@ -0,0 +1,218 @@ +package net.minestom.demo.largeframebuffers; + +import net.minestom.server.map.MapColors; +import net.minestom.server.map.framebuffers.LargeGLFWFramebuffer; +import org.joml.Matrix4f; +import org.lwjgl.BufferUtils; +import org.lwjgl.opengl.GLUtil; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.*; +import java.nio.ByteBuffer; +import java.util.stream.Collectors; + +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL20.*; + +public final class OpenGLRendering { + + private static int vbo; + private static int indexBuffer; + private static final int VERTEX_SIZE = 5*4; // position + tex + + // array of vertices (order: X,Y,Z, Tex U, Tex V) + private static float[] vertices = { + // front face + -1f, -1f, -1f, 0, 0, + 1f, -1f, -1f, 1, 0, + 1f, 1f, -1f, 1, 1, + -1f, 1f, -1f, 0, 1, + + // back face + -1f, -1f, 1f, 0, 1, + 1f, -1f, 1f, 1, 1, + 1f, 1f, 1f, 1, 0, + -1f, 1f, 1f, 0, 0, + }; + + private static int[] indices = { + // south face + 0,1,2, + 2,3,0, + + // north face + 4,5,6, + 6,7,4, + + // west face + 0,4,7, + 7,3,0, + + // east face + 1,5,6, + 6,2,1, + + // top face + 3, 2, 6, + 6, 7, 3 + }; + private static int renderShader; + private static Matrix4f projectionMatrix; + private static Matrix4f viewMatrix; + private static Matrix4f modelMatrix; + private static int projectionUniform; + private static int viewUniform; + private static int modelUniform; + private static int paletteTexture; + private static int boxTexture; + + static void init() { + // GLUtil.setupDebugMessageCallback(); + + paletteTexture = loadTexture("palette"); + boxTexture = loadTexture("box"); + + vbo = glGenBuffers(); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW); + + indexBuffer = glGenBuffers(); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices, GL_STATIC_DRAW); + + glBindBuffer(GL_ARRAY_BUFFER, vbo); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, false, VERTEX_SIZE, 0); // position + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, false, VERTEX_SIZE, 3*4); // color + + glBindBuffer(GL_ARRAY_BUFFER, 0); + + // prepare matrices and shader + renderShader = glCreateProgram(); + projectionMatrix = new Matrix4f().setPerspective((float) (Math.PI/4f), 1f, 0.001f, 100f); + viewMatrix = new Matrix4f().setLookAt(5f, 5f, 5f, 0, 0, 0, 0, -1, 0); + modelMatrix = new Matrix4f().identity(); + int vertexShader = createShader("/shaders/vertex.glsl", GL_VERTEX_SHADER); + int fragmentShader = createShader("/shaders/fragment.glsl", GL_FRAGMENT_SHADER); + glAttachShader(renderShader, vertexShader); + glAttachShader(renderShader, fragmentShader); + glLinkProgram(renderShader); + if(glGetProgrami(renderShader, GL_LINK_STATUS) == 0) { + System.err.println("Link error: "+glGetProgramInfoLog(renderShader)); + } + + projectionUniform = glGetUniformLocation(renderShader, "projection"); + viewUniform = glGetUniformLocation(renderShader, "view"); + modelUniform = glGetUniformLocation(renderShader, "model"); + int paletteSizeUniform = glGetUniformLocation(renderShader, "paletteSize"); + int paletteUniform = glGetUniformLocation(renderShader, "palette"); + int boxUniform = glGetUniformLocation(renderShader, "box"); + + glUseProgram(renderShader); { + uploadMatrix(projectionUniform, projectionMatrix); + uploadMatrix(viewUniform, viewMatrix); + + glUniform1i(boxUniform, 0); // texture unit 0 + glUniform1i(paletteUniform, 1); // texture unit 1 + glUniform1f(paletteSizeUniform, 236); + } + } + + private static int loadTexture(String filename) { + int tex = glGenTextures(); + glBindTexture(GL_TEXTURE_2D, tex); + BufferedImage image; + try { + image = ImageIO.read(OpenGLRendering.class.getResourceAsStream("/textures/"+filename+".png")); + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException("Missing image "+filename, e); + } + ByteBuffer pixels = BufferUtils.createByteBuffer(image.getWidth()*image.getHeight()*4); + for (int y = 0; y < image.getHeight(); y++) { + for (int x = 0; x < image.getWidth(); x++) { + int rgb = image.getRGB(x, y); + int alpha = (rgb >> 24) & 0xFF; + int red = (rgb >> 16) & 0xFF; + int green = (rgb >> 8) & 0xFF; + int blue = rgb & 0xFF; + pixels.put((byte) red); + pixels.put((byte) green); + pixels.put((byte) blue); + pixels.put((byte) alpha); + } + } + pixels.flip(); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.getWidth(), image.getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + // closest neighbor required here, as pixels can have very different rgb values, and interpolation will break palette lookup + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + return tex; + } + + private static void uploadMatrix(int uniform, Matrix4f matrix) { + float[] values = new float[4*4]; + matrix.get(values); + glUniformMatrix4fv(uniform, false, values); + } + + private static int createShader(String filename, int type) { + int shader = glCreateShader(type); + try(BufferedReader reader = new BufferedReader(new InputStreamReader(OpenGLRendering.class.getResourceAsStream(filename)))) { + StringBuffer buffer = new StringBuffer(); + String source = reader.lines().collect(Collectors.joining("\n")); + glShaderSource(shader, source); + glCompileShader(shader); + } catch (IOException e) { + e.printStackTrace(); + } + return shader; + } + + private static long lastTime = System.currentTimeMillis(); + + private static int frame = 0; + + static void render(LargeGLFWFramebuffer framebuffer) { + if(frame % 100 == 0) { + long time = System.currentTimeMillis(); + long dt = time-lastTime; + System.out.println(">> Render time for 100 frames: "+dt); + System.out.println(">> Average time per frame: "+(dt/100.0)); + System.out.println(">> Average FPS: "+(1000.0/(dt/100.0))); + lastTime = time; + } + frame++; + + + glClearColor(MapColors.COLOR_BLACK.multiply53()/255.0f, 0f, 0f, 1f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glEnable(GL_DEPTH_TEST); + + modelMatrix.rotateY((float) (Math.PI/60f)); + + // TODO: render texture + glUseProgram(renderShader); { + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, paletteTexture); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, boxTexture); + + uploadMatrix(modelUniform, modelMatrix); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); + glDrawElements(GL_TRIANGLES, indices.length, GL_UNSIGNED_INT, 0); + } + glUseProgram(0); + } + +} diff --git a/src/lwjgl/java/net/minestom/demo/largeframebuffers/PaletteGenerator.java b/src/lwjgl/java/net/minestom/demo/largeframebuffers/PaletteGenerator.java new file mode 100644 index 000000000..dad366baf --- /dev/null +++ b/src/lwjgl/java/net/minestom/demo/largeframebuffers/PaletteGenerator.java @@ -0,0 +1,41 @@ +package net.minestom.demo.largeframebuffers; + +import net.minestom.server.map.MapColors; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class PaletteGenerator { + + public static void main(String[] args) { + Map colors = new HashMap<>(); + int highestIndex = 0; + for(MapColors c : MapColors.values()) { + for(MapColors.Multiplier m : MapColors.Multiplier.values()) { + byte index = m.apply(c); + if(((int)index & 0xFF) > highestIndex) { + highestIndex = ((int)index) & 0xFF; + } + int rgb = MapColors.PreciseMapColor.toRGB(c, m); + colors.put(index, rgb); + } + } + + BufferedImage paletteTexture = new BufferedImage(highestIndex+1, 1, BufferedImage.TYPE_INT_ARGB); + for (int i = 0; i <= highestIndex; i++) { + int rgb = colors.getOrDefault((byte)i, 0); + int argb = (0xFF << 24) | rgb; + paletteTexture.setRGB(i, 0, argb); + } + + try { + ImageIO.write(paletteTexture, "png", new File("src/lwjgl/resources/textures/palette.png")); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/lwjgl/java/net/minestom/server/map/framebuffers/GLFWCapableBuffer.java b/src/lwjgl/java/net/minestom/server/map/framebuffers/GLFWCapableBuffer.java index b75d8e7ad..26f236ef7 100644 --- a/src/lwjgl/java/net/minestom/server/map/framebuffers/GLFWCapableBuffer.java +++ b/src/lwjgl/java/net/minestom/server/map/framebuffers/GLFWCapableBuffer.java @@ -24,6 +24,8 @@ abstract class GLFWCapableBuffer { private final long glfwWindow; private final int width; private final int height; + private final ByteBuffer colorsBuffer; + private boolean onlyMapColors; protected GLFWCapableBuffer(int width, int height) { this(width, height, GLFW_NATIVE_CONTEXT_API, GLFW_OPENGL_API); @@ -36,6 +38,7 @@ abstract class GLFWCapableBuffer { this.width = width; this.height = height; this.colors = new byte[width*height]; + colorsBuffer = BufferUtils.createByteBuffer(width*height); this.pixels = BufferUtils.createByteBuffer(width*height*4); if(!glfwInit()) { throw new RuntimeException("Failed to init GLFW"); @@ -99,16 +102,22 @@ abstract class GLFWCapableBuffer { * Only call if you do not use {@link #render(Runnable)} nor {@link #setupRenderLoop(long, TimeUnit, Runnable)} */ public void prepareMapColors() { - glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - int i = Framebuffer.index(x, y, width)*4; - int red = pixels.get(i) & 0xFF; - int green = pixels.get(i+1) & 0xFF; - int blue = pixels.get(i+2) & 0xFF; - int alpha = pixels.get(i+3) & 0xFF; - int argb = (alpha << 24) | (red << 16) | (green << 8) | blue; - colors[Framebuffer.index(x, y, width)] = MapColors.closestColor(argb).getIndex(); + if(onlyMapColors) { + colorsBuffer.rewind(); + glReadPixels(0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE, colorsBuffer); + colorsBuffer.get(colors); + } else { + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int i = Framebuffer.index(x, y, width)*4; + int red = pixels.get(i) & 0xFF; + int green = pixels.get(i+1) & 0xFF; + int blue = pixels.get(i+2) & 0xFF; + int alpha = pixels.get(i+3) & 0xFF; + int argb = (alpha << 24) | (red << 16) | (green << 8) | blue; + colors[Framebuffer.index(x, y, width)] = MapColors.closestColor(argb).getIndex(); + } } } } @@ -128,4 +137,20 @@ abstract class GLFWCapableBuffer { public int height() { return height; } + + /** + * Tells this buffer that the **RED** channel contains the index of the map color to use. + * + * This allows for optimizations and fast rendering (because there is no need for a conversion) + */ + public void useMapColors() { + onlyMapColors = true; + } + + /** + * Opposite to {@link #useMapColors()} + */ + public void useRGB() { + onlyMapColors = false; + } } diff --git a/src/lwjgl/resources/shaders/fragment.glsl b/src/lwjgl/resources/shaders/fragment.glsl new file mode 100644 index 000000000..8ec323d44 --- /dev/null +++ b/src/lwjgl/resources/shaders/fragment.glsl @@ -0,0 +1,33 @@ +#version 330 + +in vec2 uv; + +out vec4 fragColor; + +uniform sampler2D box; +uniform sampler2D palette; +uniform float paletteSize; + +void main() { + vec3 vertexColor = texture(box, uv).rgb; + + + // render in map colors + int closest = 0; + float distance = 1.0f/0.0f; + for(int i = 1; i < paletteSize; i++) { + vec3 mapColor = texture(palette, vec2(i/paletteSize, 0.0)).rgb; + float dr = mapColor.r - vertexColor.r; + float dg = mapColor.g - vertexColor.g; + float db = mapColor.b - vertexColor.b; + + float d = dr*dr+dg*dg+db*db; + if(d < distance) { + distance = d; + closest = i; + } + } + + fragColor = vec4(closest/255.0, closest/255.0, closest/255.0, 1.0); + //fragColor = vec4(vertexColor, 1.0); +} \ No newline at end of file diff --git a/src/lwjgl/resources/shaders/vertex.glsl b/src/lwjgl/resources/shaders/vertex.glsl new file mode 100644 index 000000000..a2d795bcd --- /dev/null +++ b/src/lwjgl/resources/shaders/vertex.glsl @@ -0,0 +1,16 @@ +#version 330 + +layout(location = 0) in vec3 pos; +layout(location = 1) in vec2 texCoords; + +out vec2 uv; + +uniform mat4 projection; +uniform mat4 view; +uniform mat4 model; + +void main() { + mat4 mvp = projection * view * model; + uv = texCoords; + gl_Position = mvp * vec4(pos, 1.0); +} \ No newline at end of file diff --git a/src/lwjgl/resources/textures/box.png b/src/lwjgl/resources/textures/box.png new file mode 100644 index 0000000000000000000000000000000000000000..1f467c6c22efb7ef6cc2be72c02a2c72cb66e750 GIT binary patch literal 274 zcmV+t0qy>YP)Y4>N;B~YK+wsPthEk`{XhofZ(f$F@G}_Mbl+EUsXEn~iL7o(hLh_xl;D;FiG|9i5 Yeh>?o6j;%#>;M1&07*qoM6N<$f~S#vbpQYW literal 0 HcmV?d00001 diff --git a/src/main/java/net/minestom/server/map/MapColors.java b/src/main/java/net/minestom/server/map/MapColors.java index f3450ec14..4c9083e37 100644 --- a/src/main/java/net/minestom/server/map/MapColors.java +++ b/src/main/java/net/minestom/server/map/MapColors.java @@ -307,7 +307,7 @@ public enum MapColors { } } - enum Multiplier { + public enum Multiplier { x1_00(MapColors::baseColor, 1.00), x0_53(MapColors::multiply53, 0.53), x0_71(MapColors::multiply71, 0.71), From c0a704dc5617fed893f31b2aab77c8e5825bcffa Mon Sep 17 00:00:00 2001 From: jglrxavpok Date: Tue, 11 Aug 2020 23:39:10 +0200 Subject: [PATCH 2/6] Forgot palette --- .../demo/largeframebuffers/OpenGLRendering.java | 2 +- .../demo/largeframebuffers/PaletteGenerator.java | 12 ++++++------ src/lwjgl/resources/shaders/fragment.glsl | 14 +++++++------- src/lwjgl/resources/textures/palette.png | Bin 0 -> 876 bytes 4 files changed, 14 insertions(+), 14 deletions(-) create mode 100644 src/lwjgl/resources/textures/palette.png diff --git a/src/lwjgl/java/net/minestom/demo/largeframebuffers/OpenGLRendering.java b/src/lwjgl/java/net/minestom/demo/largeframebuffers/OpenGLRendering.java index cd57de559..a4bb38c5d 100644 --- a/src/lwjgl/java/net/minestom/demo/largeframebuffers/OpenGLRendering.java +++ b/src/lwjgl/java/net/minestom/demo/largeframebuffers/OpenGLRendering.java @@ -68,7 +68,7 @@ public final class OpenGLRendering { private static int boxTexture; static void init() { - // GLUtil.setupDebugMessageCallback(); + GLUtil.setupDebugMessageCallback(); paletteTexture = loadTexture("palette"); boxTexture = loadTexture("box"); diff --git a/src/lwjgl/java/net/minestom/demo/largeframebuffers/PaletteGenerator.java b/src/lwjgl/java/net/minestom/demo/largeframebuffers/PaletteGenerator.java index dad366baf..d49e822cf 100644 --- a/src/lwjgl/java/net/minestom/demo/largeframebuffers/PaletteGenerator.java +++ b/src/lwjgl/java/net/minestom/demo/largeframebuffers/PaletteGenerator.java @@ -12,13 +12,13 @@ import java.util.Map; public class PaletteGenerator { public static void main(String[] args) { - Map colors = new HashMap<>(); + Map colors = new HashMap<>(); int highestIndex = 0; for(MapColors c : MapColors.values()) { for(MapColors.Multiplier m : MapColors.Multiplier.values()) { - byte index = m.apply(c); - if(((int)index & 0xFF) > highestIndex) { - highestIndex = ((int)index) & 0xFF; + int index = ((int)m.apply(c)) & 0xFF; + if(index > highestIndex) { + highestIndex = index; } int rgb = MapColors.PreciseMapColor.toRGB(c, m); colors.put(index, rgb); @@ -27,8 +27,8 @@ public class PaletteGenerator { BufferedImage paletteTexture = new BufferedImage(highestIndex+1, 1, BufferedImage.TYPE_INT_ARGB); for (int i = 0; i <= highestIndex; i++) { - int rgb = colors.getOrDefault((byte)i, 0); - int argb = (0xFF << 24) | rgb; + int rgb = colors.getOrDefault(i, 0); + int argb = (0xFF << 24) | (rgb & 0xFFFFFF); paletteTexture.setRGB(i, 0, argb); } diff --git a/src/lwjgl/resources/shaders/fragment.glsl b/src/lwjgl/resources/shaders/fragment.glsl index 8ec323d44..3959edcb1 100644 --- a/src/lwjgl/resources/shaders/fragment.glsl +++ b/src/lwjgl/resources/shaders/fragment.glsl @@ -14,16 +14,16 @@ void main() { // render in map colors int closest = 0; - float distance = 1.0f/0.0f; + uint closestDistance = uint(2147483647); for(int i = 1; i < paletteSize; i++) { vec3 mapColor = texture(palette, vec2(i/paletteSize, 0.0)).rgb; - float dr = mapColor.r - vertexColor.r; - float dg = mapColor.g - vertexColor.g; - float db = mapColor.b - vertexColor.b; + int dr = int((mapColor.r - vertexColor.r)*255); + int dg = int((mapColor.g - vertexColor.g)*255); + int db = int((mapColor.b - vertexColor.b)*255); - float d = dr*dr+dg*dg+db*db; - if(d < distance) { - distance = d; + uint d = uint(dr*dr)+uint(dg*dg)+uint(db*db); + if(d < closestDistance) { + closestDistance = d; closest = i; } } diff --git a/src/lwjgl/resources/textures/palette.png b/src/lwjgl/resources/textures/palette.png new file mode 100644 index 0000000000000000000000000000000000000000..596c7938b7ea8df1041dae230118c8fce092f524 GIT binary patch literal 876 zcmV-y1C#uTP)A)SH^=DQhdHLKnjmeM(KmDTcus?N|}h_T2cyQ4z-1KtYcZn z4m0!3#4&VX2{Fs6jk4%%2_AM(qY=~d4qiN%7!zai@p&(xWSYI!* zxe38wo~5NFHa0fc-rgo04kLv1JMZYcucHW*N+rtWGXG6JpJ(yh1q#RGjVc? zGh;4RPYrQ1ImWRb5x?J0JRT>NO3~NXM@L7A?(Q;!gV5NR=gpwR@=QM)=SJ`i9HLbG zobvvU@Xw#**H+OYHg=PC_OlXckA;k^p((o*)qP@OnvsM?D7VXuwvQ{2W^2pFqeph0 zJdrqm-a_QMk>q_7*~ex)2?INkvwWW{@JIG8E1nRtETbq2s;VN2B8fzTTrP*EX#@fR zSEQh`)ay<6=fc`S>ROZ@wa0{u$?D zikBlL2Ik6)u0X3Nk8rCE;b9lTue}HhR$lLKV$R#n>O>buI?VW=5{T=ho)+lS_l*zI zeDTp0et2+)xl^x^>NRtD*vjqWHiGtQX8ToEMx%TfN-*BpgI}E@9$g}pxI$l#my0no zx6@W0uGu&}T#ffc3*qSw-a0Gea=*yDtDE&951V7-1jRPQx>~xLt@N}tV>MMV>3fyM zXpsDw(;Pi4(XV$us{cqR8l}_eWI}TDw%fo`Kzb%GlMQ;auCD8>W$O|?YLbvO-`RGyrqDzQLK<`NY{M`@dWgRbRw>AiLp zt8bM4dI#PXnF)uRju#quE*rymE6sr~*LZevn12Bi&flUh6$HNk0000 Date: Tue, 11 Aug 2020 23:49:21 +0200 Subject: [PATCH 3/6] Fixed palette lookup --- .../net/minestom/demo/largeframebuffers/PaletteGenerator.java | 2 ++ src/lwjgl/resources/shaders/fragment.glsl | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lwjgl/java/net/minestom/demo/largeframebuffers/PaletteGenerator.java b/src/lwjgl/java/net/minestom/demo/largeframebuffers/PaletteGenerator.java index d49e822cf..194e0a07a 100644 --- a/src/lwjgl/java/net/minestom/demo/largeframebuffers/PaletteGenerator.java +++ b/src/lwjgl/java/net/minestom/demo/largeframebuffers/PaletteGenerator.java @@ -15,6 +15,8 @@ public class PaletteGenerator { Map colors = new HashMap<>(); int highestIndex = 0; for(MapColors c : MapColors.values()) { + if (c == MapColors.NONE) + continue; for(MapColors.Multiplier m : MapColors.Multiplier.values()) { int index = ((int)m.apply(c)) & 0xFF; if(index > highestIndex) { diff --git a/src/lwjgl/resources/shaders/fragment.glsl b/src/lwjgl/resources/shaders/fragment.glsl index 3959edcb1..c17da90c7 100644 --- a/src/lwjgl/resources/shaders/fragment.glsl +++ b/src/lwjgl/resources/shaders/fragment.glsl @@ -15,8 +15,8 @@ void main() { // render in map colors int closest = 0; uint closestDistance = uint(2147483647); - for(int i = 1; i < paletteSize; i++) { - vec3 mapColor = texture(palette, vec2(i/paletteSize, 0.0)).rgb; + for(int i = 4; i < paletteSize; i++) { + vec3 mapColor = texture(palette, vec2((i+0.5f)/paletteSize, 0.0)).rgb; int dr = int((mapColor.r - vertexColor.r)*255); int dg = int((mapColor.g - vertexColor.g)*255); int db = int((mapColor.b - vertexColor.b)*255); From a9aff3e6ef00a3ee3a64fff1109ac6410948c4d2 Mon Sep 17 00:00:00 2001 From: Felix Cravic Date: Wed, 12 Aug 2020 07:46:07 +0200 Subject: [PATCH 4/6] Remove pathfinder specific thread pool + cleanup --- .../net/minestom/server/MinecraftServer.java | 5 +-- .../server/benchmark/BenchmarkManager.java | 1 - .../server/entity/EntityCreature.java | 33 ++++++++----------- .../entity/pathfinding/PathfinderManager.java | 16 --------- .../net/minestom/server/instance/Chunk.java | 7 ++-- .../listener/PlayerDiggingListener.java | 8 ++--- 6 files changed, 22 insertions(+), 48 deletions(-) delete mode 100644 src/main/java/net/minestom/server/entity/pathfinding/PathfinderManager.java diff --git a/src/main/java/net/minestom/server/MinecraftServer.java b/src/main/java/net/minestom/server/MinecraftServer.java index 58381e0e2..6c214f604 100644 --- a/src/main/java/net/minestom/server/MinecraftServer.java +++ b/src/main/java/net/minestom/server/MinecraftServer.java @@ -64,9 +64,6 @@ public class MinecraftServer { public static final String THREAD_NAME_TICK = "Ms-Tick"; - public static final String THREAD_NAME_PATHFINDER = "Ms-PathFinding"; - public static final int THREAD_COUNT_PATHFINDER = 2; - public static final String THREAD_NAME_PACKET_WRITER = "Ms-PacketWriterPool"; public static final int THREAD_COUNT_PACKET_WRITER = 2; @@ -132,7 +129,7 @@ public class MinecraftServer { private static MinecraftSessionService sessionService = authService.createMinecraftSessionService(); public static MinecraftServer init() { - if(minecraftServer != null) // don't init twice + if (minecraftServer != null) // don't init twice return minecraftServer; // warmup/force-init registries // without this line, registry types that are not loaded explicitly will have an internal empty registry in Registries diff --git a/src/main/java/net/minestom/server/benchmark/BenchmarkManager.java b/src/main/java/net/minestom/server/benchmark/BenchmarkManager.java index a25249c17..a032ed959 100644 --- a/src/main/java/net/minestom/server/benchmark/BenchmarkManager.java +++ b/src/main/java/net/minestom/server/benchmark/BenchmarkManager.java @@ -27,7 +27,6 @@ public class BenchmarkManager { threadMXBean.setThreadCpuTimeEnabled(true); threads.add(THREAD_NAME_MAIN_UPDATE); - threads.add(THREAD_NAME_PATHFINDER); threads.add(THREAD_NAME_PACKET_WRITER); threads.add(THREAD_NAME_BLOCK_BATCH); threads.add(THREAD_NAME_SCHEDULER); diff --git a/src/main/java/net/minestom/server/entity/EntityCreature.java b/src/main/java/net/minestom/server/entity/EntityCreature.java index 3a148e0ef..6e04c2faa 100644 --- a/src/main/java/net/minestom/server/entity/EntityCreature.java +++ b/src/main/java/net/minestom/server/entity/EntityCreature.java @@ -7,7 +7,6 @@ import net.minestom.server.attribute.Attribute; import net.minestom.server.entity.ai.GoalSelector; import net.minestom.server.entity.ai.TargetSelector; import net.minestom.server.entity.pathfinding.PFPathingEntity; -import net.minestom.server.entity.pathfinding.PathfinderManager; import net.minestom.server.event.entity.EntityAttackEvent; import net.minestom.server.event.item.ArmorEquipEvent; import net.minestom.server.instance.Chunk; @@ -32,8 +31,6 @@ import java.util.function.Supplier; public abstract class EntityCreature extends LivingEntity { - private static final PathfinderManager PATHFINDER_MANAGER = new PathfinderManager(); - private PFPathingEntity pathingEntity = new PFPathingEntity(this); private HydrazinePathFinder pathFinder; private PathObject path; @@ -123,24 +120,22 @@ public abstract class EntityCreature extends LivingEntity { // Path finding { - if (pathPosition != null && !pathLock.isLocked()) { - PATHFINDER_MANAGER.getPool().execute(() -> { - this.pathLock.lock(); - this.path = pathFinder.updatePathFor(pathingEntity); + if (pathPosition != null) { + this.pathLock.lock(); + this.path = pathFinder.updatePathFor(pathingEntity); - if (path != null) { - final float speed = getAttributeValue(Attribute.MOVEMENT_SPEED); - final Position targetPosition = pathingEntity.getTargetPosition(); - moveTowards(targetPosition, speed); - } else { - if (pathPosition != null) { - this.pathPosition = null; - this.pathFinder.reset(); - } + if (path != null) { + final float speed = getAttributeValue(Attribute.MOVEMENT_SPEED); + final Position targetPosition = pathingEntity.getTargetPosition(); + moveTowards(targetPosition, speed); + } else { + if (pathPosition != null) { + this.pathPosition = null; + this.pathFinder.reset(); } + } - this.pathLock.unlock(); - }); + this.pathLock.unlock(); } } @@ -170,7 +165,7 @@ public abstract class EntityCreature extends LivingEntity { public boolean addViewer(Player player) { final boolean result = super.addViewer(player); - PlayerConnection playerConnection = player.getPlayerConnection(); + final PlayerConnection playerConnection = player.getPlayerConnection(); EntityPacket entityPacket = new EntityPacket(); entityPacket.entityId = getEntityId(); diff --git a/src/main/java/net/minestom/server/entity/pathfinding/PathfinderManager.java b/src/main/java/net/minestom/server/entity/pathfinding/PathfinderManager.java deleted file mode 100644 index c1e7d3348..000000000 --- a/src/main/java/net/minestom/server/entity/pathfinding/PathfinderManager.java +++ /dev/null @@ -1,16 +0,0 @@ -package net.minestom.server.entity.pathfinding; - -import net.minestom.server.MinecraftServer; -import net.minestom.server.utils.thread.MinestomThread; - -import java.util.concurrent.ExecutorService; - -public final class PathfinderManager { - - private ExecutorService pathfinderPool = new MinestomThread(MinecraftServer.THREAD_COUNT_PATHFINDER, MinecraftServer.THREAD_NAME_PATHFINDER); - - public ExecutorService getPool() { - return pathfinderPool; - } - -} diff --git a/src/main/java/net/minestom/server/instance/Chunk.java b/src/main/java/net/minestom/server/instance/Chunk.java index ee368b5cb..60082cf8b 100644 --- a/src/main/java/net/minestom/server/instance/Chunk.java +++ b/src/main/java/net/minestom/server/instance/Chunk.java @@ -85,19 +85,19 @@ public final class Chunk implements Viewable { } public void UNSAFE_setCustomBlock(int x, int y, int z, short blockStateId, short customBlockId, Data data) { - CustomBlock customBlock = BLOCK_MANAGER.getCustomBlock(customBlockId); + final CustomBlock customBlock = BLOCK_MANAGER.getCustomBlock(customBlockId); Check.notNull(customBlock, "The custom block " + customBlockId + " does not exist or isn't registered"); UNSAFE_setCustomBlock(x, y, z, blockStateId, customBlock, data); } protected void UNSAFE_setCustomBlock(int x, int y, int z, short blockStateId, CustomBlock customBlock, Data data) { - UpdateConsumer updateConsumer = customBlock.hasUpdate() ? customBlock::update : null; + final UpdateConsumer updateConsumer = customBlock.hasUpdate() ? customBlock::update : null; setBlock(x, y, z, blockStateId, customBlock.getCustomBlockId(), data, updateConsumer); } public void UNSAFE_removeCustomBlock(int x, int y, int z) { - int index = getBlockIndex(x, y, z); + final int index = getBlockIndex(x, y, z); this.customBlocksId[index] = 0; // Set to none this.blocksData.remove(index); @@ -404,7 +404,6 @@ public final class Chunk implements Viewable { /** * Used to verify if the chunk should still be kept in memory - * having the chunk unloaded means no data is contained in it (blocks, data, etc...) * * @return true if the chunk is loaded */ diff --git a/src/main/java/net/minestom/server/listener/PlayerDiggingListener.java b/src/main/java/net/minestom/server/listener/PlayerDiggingListener.java index 0a8a85dc3..916532af0 100644 --- a/src/main/java/net/minestom/server/listener/PlayerDiggingListener.java +++ b/src/main/java/net/minestom/server/listener/PlayerDiggingListener.java @@ -41,7 +41,7 @@ public class PlayerDiggingListener { if (instantBreak) { breakBlock(instance, player, blockPosition); } else { - CustomBlock customBlock = instance.getCustomBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ()); + final CustomBlock customBlock = instance.getCustomBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ()); if (customBlock != null) { int breakTime = customBlock.getBreakDelay(player, blockPosition); @@ -78,13 +78,13 @@ public class PlayerDiggingListener { breakBlock(instance, player, blockPosition); break; case DROP_ITEM_STACK: - ItemStack droppedItemStack = player.getInventory().getItemInMainHand().clone(); + final ItemStack droppedItemStack = player.getInventory().getItemInMainHand().clone(); dropItem(player, droppedItemStack, ItemStack.getAirItem()); break; case DROP_ITEM: ItemStack handItem = player.getInventory().getItemInMainHand().clone(); ItemStack droppedItemStack2 = handItem.clone(); - StackingRule handStackingRule = handItem.getStackingRule(); + final StackingRule handStackingRule = handItem.getStackingRule(); droppedItemStack2 = handStackingRule.apply(droppedItemStack2, 1); @@ -129,7 +129,7 @@ public class PlayerDiggingListener { } private static void dropItem(Player player, ItemStack droppedItem, ItemStack handItem) { - PlayerInventory playerInventory = player.getInventory(); + final PlayerInventory playerInventory = player.getInventory(); if (player.dropItem(droppedItem)) { playerInventory.setItemInMainHand(handItem); } else { From 91711401c0eea4d2cad60e207366da385e5b0339 Mon Sep 17 00:00:00 2001 From: Felix Cravic Date: Wed, 12 Aug 2020 13:10:57 +0200 Subject: [PATCH 5/6] Added options for the pathfinder --- .../entity/pathfinding/PFPathingEntity.java | 79 +++++++++++++++++-- .../net/minestom/server/item/ItemStack.java | 51 +++++------- 2 files changed, 93 insertions(+), 37 deletions(-) diff --git a/src/main/java/net/minestom/server/entity/pathfinding/PFPathingEntity.java b/src/main/java/net/minestom/server/entity/pathfinding/PFPathingEntity.java index 552ab78cf..7d31be767 100644 --- a/src/main/java/net/minestom/server/entity/pathfinding/PFPathingEntity.java +++ b/src/main/java/net/minestom/server/entity/pathfinding/PFPathingEntity.java @@ -13,6 +13,15 @@ public class PFPathingEntity implements IPathingEntity { private float searchRange; private Position targetPosition; + // Capacities + private boolean fireResistant; + private boolean cautious; + private boolean climber; + private boolean swimmer; + private boolean aquaphobic; + private boolean avoidsDoorways; + private boolean opensDoors; + public PFPathingEntity(EntityCreature entity) { this.entity = entity; this.searchRange = entity.getAttributeValue(Attribute.FOLLOW_RANGE); @@ -41,6 +50,62 @@ public class PFPathingEntity implements IPathingEntity { this.searchRange = searchRange; } + public boolean isFireResistant() { + return fireResistant; + } + + public void setFireResistant(boolean fireResistant) { + this.fireResistant = fireResistant; + } + + public boolean isCautious() { + return cautious; + } + + public void setCautious(boolean cautious) { + this.cautious = cautious; + } + + public boolean isClimber() { + return climber; + } + + public void setClimber(boolean climber) { + this.climber = climber; + } + + public boolean isSwimmer() { + return swimmer; + } + + public void setSwimmer(boolean swimmer) { + this.swimmer = swimmer; + } + + public boolean isAquaphobic() { + return aquaphobic; + } + + public void setAquaphobic(boolean aquaphobic) { + this.aquaphobic = aquaphobic; + } + + public boolean isAvoidsDoorways() { + return avoidsDoorways; + } + + public void setAvoidsDoorways(boolean avoidsDoorways) { + this.avoidsDoorways = avoidsDoorways; + } + + public boolean isOpensDoors() { + return opensDoors; + } + + public void setOpensDoors(boolean opensDoors) { + this.opensDoors = opensDoors; + } + @Override public Capabilities capabilities() { return new Capabilities() { @@ -51,37 +116,37 @@ public class PFPathingEntity implements IPathingEntity { @Override public boolean fireResistant() { - return false; + return fireResistant; } @Override public boolean cautious() { - return false; + return cautious; } @Override public boolean climber() { - return true; + return climber; } @Override public boolean swimmer() { - return true; + return swimmer; } @Override public boolean aquaphobic() { - return false; + return aquaphobic; } @Override public boolean avoidsDoorways() { - return false; + return avoidsDoorways; } @Override public boolean opensDoors() { - return false; + return opensDoors; } }; } diff --git a/src/main/java/net/minestom/server/item/ItemStack.java b/src/main/java/net/minestom/server/item/ItemStack.java index d79405498..9cddf7904 100644 --- a/src/main/java/net/minestom/server/item/ItemStack.java +++ b/src/main/java/net/minestom/server/item/ItemStack.java @@ -1,6 +1,5 @@ package net.minestom.server.item; -import com.google.gson.JsonObject; import net.minestom.server.chat.ColoredText; import net.minestom.server.data.Data; import net.minestom.server.data.DataContainer; @@ -65,6 +64,11 @@ public class ItemStack implements DataContainer { this(material, amount, (short) 0); } + /** + * Get a new air item + * + * @return an air item + */ public static ItemStack getAirItem() { return new ItemStack(Material.AIR, (byte) 0); } @@ -145,10 +149,24 @@ public class ItemStack implements DataContainer { } } + /** + * Get the item damage (durability) + * + * @return the item damagel + */ public int getDamage() { return damage; } + /** + * Set the item damage (durability) + * + * @param damage the item damage + */ + public void setDamage(int damage) { + this.damage = damage; + } + /** * Get the item amount *

@@ -173,10 +191,6 @@ public class ItemStack implements DataContainer { this.amount = amount; } - public void setDamage(int damage) { - this.damage = damage; - } - /** * Get the special meta object for this item *

@@ -406,7 +420,7 @@ public class ItemStack implements DataContainer { * @return true if the item has the flag {@code flag}, false otherwise */ public boolean hasItemFlag(ItemFlag flag) { - int bitModifier = getBitModifier(flag); + final int bitModifier = getBitModifier(flag); return (this.hideFlag & bitModifier) == bitModifier; } @@ -520,7 +534,7 @@ public class ItemStack implements DataContainer { * @throws NullPointerException if {@code stackingRule} is null */ public void setStackingRule(StackingRule stackingRule) { - Check.notNull(stackingRule, "StackingRule cannot be null!"); + Check.notNull(stackingRule, "The stacking rule cannot be null!"); this.stackingRule = stackingRule; } @@ -543,29 +557,6 @@ public class ItemStack implements DataContainer { return (byte) (1 << hideFlag.ordinal()); } - /** - * Convert the item into a readable Json object - *

- * Mainly used to show an item in a message hover - * - * @return a {@link JsonObject} containing the item data - */ - public synchronized JsonObject toJsonObject() { - JsonObject object = new JsonObject(); - object.addProperty("id", material.getId()); - object.addProperty("Damage", getDamage()); - object.addProperty("Count", getAmount()); - - if (hasDisplayName() || hasLore()) { - JsonObject tagObject = new JsonObject(); - if (hasDisplayName()) { - tagObject.addProperty("display", getDisplayName().toString()); - } - } - - return object; - } - /** * Find the item meta based on the material type * From 55b36624cc6b5ffcfa701c25a7a4ea51ee30b1e4 Mon Sep 17 00:00:00 2001 From: Felix Cravic Date: Wed, 12 Aug 2020 18:41:14 +0200 Subject: [PATCH 6/6] Fix local held synchronization --- .../java/net/minestom/server/inventory/EquipmentHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/minestom/server/inventory/EquipmentHandler.java b/src/main/java/net/minestom/server/inventory/EquipmentHandler.java index 05ac539a5..937256f36 100644 --- a/src/main/java/net/minestom/server/inventory/EquipmentHandler.java +++ b/src/main/java/net/minestom/server/inventory/EquipmentHandler.java @@ -208,7 +208,7 @@ public interface EquipmentHandler { entityEquipmentPacket.slots = new EntityEquipmentPacket.Slot[]{slot}; entityEquipmentPacket.itemStacks = new ItemStack[]{itemStack}; - viewable.sendPacketToViewersAndSelf(entityEquipmentPacket); + viewable.sendPacketToViewers(entityEquipmentPacket); } /**