diff --git a/build.gradle.kts b/build.gradle.kts index 86cdb6a..6f31129 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -179,6 +179,7 @@ gameModules.forEach { } else { links("https://download.java.net/java/early_access/$jdkEarlyAccessDoc/docs/api/") } + links("https://over-run.github.io/overrungl/") if (jdkEnablePreview.toBoolean()) { addBooleanOption("-enable-preview", true) addStringOption("source", jdkVersion) diff --git a/modules/freeworld.client/src/main/java/freeworld/client/Freeworld.java b/modules/freeworld.client/src/main/java/freeworld/client/Freeworld.java index fb6853a..f629c06 100644 --- a/modules/freeworld.client/src/main/java/freeworld/client/Freeworld.java +++ b/modules/freeworld.client/src/main/java/freeworld/client/Freeworld.java @@ -69,7 +69,7 @@ public final class Freeworld implements AutoCloseable { private Entity player; private int blockDestroyTimer = 0; private int blockPlaceTimer = 0; - private int selectBlock = 0; + private int hotBarSelection = 0; private Freeworld() { this.glfw = GLFW.INSTANCE; @@ -120,7 +120,7 @@ public void start() { BuiltinRegistries.ENTITY_TYPE.freeze(); world = new World("New world"); - player = world.createEntity(EntityTypes.PLAYER, 32.0, 16.0, 32.0); + player = world.createEntity(EntityTypes.PLAYER, 0.0, 0.0, 0.0); initGL(); run(); @@ -140,9 +140,9 @@ private void onKey(int key, int scancode, int action, int mods) { } case GLFW.PRESS -> { switch (key) { - case GLFW.KEY_1 -> selectBlock = 0; - case GLFW.KEY_2 -> selectBlock = 1; - case GLFW.KEY_3 -> selectBlock = 2; + case GLFW.KEY_1 -> hotBarSelection = 0; + case GLFW.KEY_2 -> hotBarSelection = 1; + case GLFW.KEY_3 -> hotBarSelection = 2; } } } @@ -207,7 +207,7 @@ private void tick() { if (!hitResult.missed() && glfw.getMouseButton(window, GLFW.MOUSE_BUTTON_RIGHT) == GLFW.PRESS) { final Direction face = hitResult.face(); - final BlockType type = switch (selectBlock) { + final BlockType type = switch (hotBarSelection) { case 0 -> BlockTypes.STONE; case 1 -> BlockTypes.DIRT; case 2 -> BlockTypes.GRASS_BLOCK; @@ -301,6 +301,10 @@ public Entity player() { return player; } + public int hotBarSelection() { + return hotBarSelection; + } + public static Freeworld getInstance() { return INSTANCE; } diff --git a/modules/freeworld.client/src/main/java/freeworld/client/render/GameRenderer.java b/modules/freeworld.client/src/main/java/freeworld/client/render/GameRenderer.java index 159ad19..63b3199 100644 --- a/modules/freeworld.client/src/main/java/freeworld/client/render/GameRenderer.java +++ b/modules/freeworld.client/src/main/java/freeworld/client/render/GameRenderer.java @@ -12,6 +12,7 @@ import freeworld.client.render.gl.GLDrawMode; import freeworld.client.render.gl.GLStateMgr; +import freeworld.client.render.texture.TextureRegion; import freeworld.client.render.world.HitResult; import freeworld.client.render.world.WorldRenderer; import freeworld.client.Freeworld; @@ -50,8 +51,13 @@ public final class GameRenderer implements GLResource { public static final Identifier TEX_DIRT = Identifier.ofBuiltin("texture/block/dirt.png"); public static final Identifier TEX_GRASS_BLOCK = Identifier.ofBuiltin("texture/block/grass_block.png"); public static final Identifier TEX_STONE = Identifier.ofBuiltin("texture/block/stone.png"); - private TextureAtlas texture; + private static final Identifier TEX_CROSSING = Identifier.ofBuiltin("texture/gui/crossing.png"); + private static final Identifier TEX_HOT_BAR = Identifier.ofBuiltin("texture/gui/hotbar.png"); + private static final Identifier TEX_HOT_BAR_SELECTED = Identifier.ofBuiltin("texture/gui/hotbar_selected.png"); + private final float guiScale = 2; private TextureManager textureManager; + private TextureAtlas blockAtlas; + private TextureAtlas guiAtlas; private BlockRenderer blockRenderer; private WorldRenderer worldRenderer; private Tessellator tessellator; @@ -70,9 +76,13 @@ public void init(GLStateMgr gl) { textureManager = new TextureManager(); - texture = TextureAtlas.load(gl, List.of(TEX_DIRT, TEX_GRASS_BLOCK, TEX_STONE)); - textureManager.addTexture(TextureManager.BLOCK_ATLAS, texture); - logger.info("Created {}x{}x{} {}", texture.width(), texture.height(), texture.mipmapLevel(), TextureManager.BLOCK_ATLAS); + blockAtlas = TextureAtlas.load(gl, List.of(TEX_DIRT, TEX_GRASS_BLOCK, TEX_STONE), 4); + textureManager.addTexture(TextureManager.BLOCK_ATLAS, blockAtlas); + logger.info("Created {}x{}x{} {}", blockAtlas.width(), blockAtlas.height(), blockAtlas.mipmapLevel(), TextureManager.BLOCK_ATLAS); + + guiAtlas = TextureAtlas.load(gl, List.of(TEX_CROSSING, TEX_HOT_BAR, TEX_HOT_BAR_SELECTED), 0); + textureManager.addTexture(TextureManager.GUI_ATLAS, guiAtlas); + logger.info("Created {}x{}x{} {}", guiAtlas.width(), guiAtlas.height(), guiAtlas.mipmapLevel(), TextureManager.GUI_ATLAS); blockRenderer = new BlockRenderer(this); worldRenderer = new WorldRenderer(this, client.world()); @@ -97,10 +107,11 @@ private GLProgram initBootstrapProgram(GLStateMgr gl, String path, VertexLayout public void render(GLStateMgr gl, double partialTick) { gl.clear(GL10C.COLOR_BUFFER_BIT | GL10C.DEPTH_BUFFER_BIT); + gl.disableBlend(); gl.enableCullFace(); gl.enableDepthTest(); gl.setDepthFunc(GL10C.LEQUAL); - texture.bind(gl); + blockAtlas.bind(gl); projectionViewMatrix.setPerspective( (float) Math.toRadians(70.0), (float) client.framebufferWidth() / client.framebufferHeight(), @@ -166,39 +177,71 @@ public void render(GLStateMgr gl, double partialTick) { private void renderGui(GLStateMgr gl, double partialTick) { final int width = client.framebufferWidth(); final int height = client.framebufferHeight(); + final float screenWidth = width / guiScale; + final float screenHeight = height / guiScale; + projectionViewMatrix.setOrtho(0.0f, screenWidth, 0.0f, screenHeight, -100.0f, 100.0f); + modelMatrix.translation(screenWidth * 0.5f, screenHeight * 0.5f, 0.0f); gl.clear(GL10C.DEPTH_BUFFER_BIT); + gl.enableBlend(); + gl.setBlendFuncSeparate(GL10C.ONE_MINUS_DST_COLOR, GL10C.ONE_MINUS_SRC_ALPHA, GL10C.ONE, GL10C.ZERO); gl.disableCullFace(); gl.disableDepthTest(); - gl.setTextureBinding2D(0); - positionColorProgram.use(gl); - projectionViewMatrix.setOrtho(0.0f, width, 0.0f, height, -100.0f, 100.0f); - modelMatrix.translation(width * 0.5f, height * 0.5f, 0.0f); - positionColorProgram.getUniform(GLProgram.UNIFORM_PROJECTION_VIEW_MATRIX).set(projectionViewMatrix); - positionColorProgram.getUniform(GLProgram.UNIFORM_MODEL_MATRIX).set(modelMatrix); - positionColorProgram.uploadUniforms(gl); + guiAtlas.bind(gl); + positionColorTexProgram.use(gl); + positionColorTexProgram.getUniform(GLProgram.UNIFORM_PROJECTION_VIEW_MATRIX).set(projectionViewMatrix); + positionColorTexProgram.getUniform(GLProgram.UNIFORM_MODEL_MATRIX).set(modelMatrix); + positionColorTexProgram.uploadUniforms(gl); tessellator.begin(GLDrawMode.TRIANGLES); + renderCrossing(); + tessellator.end(gl); + + gl.setBlendFunc(GL10C.SRC_ALPHA, GL10C.ONE_MINUS_SRC_ALPHA); + tessellator.begin(GLDrawMode.TRIANGLES); + renderHotBar(screenHeight); + renderHotBarSelected(screenHeight); + tessellator.end(gl); + } + + private void renderGuiSprite(Identifier identifier, float x, float y, float anchorX, float anchorY) { + final TextureRegion region = guiAtlas.getRegion(identifier); + final int width = guiAtlas.width(); + final int height = guiAtlas.height(); + final float lWidth = region.width() * anchorX; + final float rWidth = region.width() * (1.0f - anchorX); + final float bHeight = region.height() * anchorY; + final float tHeight = region.height() * (1.0f - anchorY); + final float u0 = region.u0(width); + final float u1 = region.u1(width); + final float v0 = region.v0(height); + final float v1 = region.v1(height); tessellator.color(1.0f, 1.0f, 1.0f); tessellator.indices(0, 1, 2, 2, 3, 0); - tessellator.position(-8, 1, 0).emit(); - tessellator.position(-8, -1, 0).emit(); - tessellator.position(8, -1, 0).emit(); - tessellator.position(8, 1, 0).emit(); - tessellator.indices(0, 1, 2, 2, 3, 0); - tessellator.position(1, 8, 0).emit(); - tessellator.position(1, -8, 0).emit(); - tessellator.position(-1, -8, 0).emit(); - tessellator.position(-1, 8, 0).emit(); - tessellator.end(gl); + tessellator.texCoord(u0, v0).position(x - lWidth, y + tHeight, 0).emit(); + tessellator.texCoord(u0, v1).position(x - lWidth, y - bHeight, 0).emit(); + tessellator.texCoord(u1, v1).position(x + rWidth, y - bHeight, 0).emit(); + tessellator.texCoord(u1, v0).position(x + rWidth, y + tHeight, 0).emit(); + } + + private void renderCrossing() { + renderGuiSprite(TEX_CROSSING, 0.0f, 0.0f, 0.5f, 0.5f); + } + + private void renderHotBar(float screenHeight) { + renderGuiSprite(TEX_HOT_BAR, 0.0f, -screenHeight * 0.5f, 0.5f, 0.0f); + } + + private void renderHotBarSelected(float screenHeight) { + renderGuiSprite(TEX_HOT_BAR_SELECTED, (client.hotBarSelection() - 5) * 22.0f - 2.0f, -screenHeight * 0.5f, 0.0f, 0.0f); } @Override public void close(GLStateMgr gl) { logger.info("Closing game renderer"); - if (textureManager != null) textureManager.close(gl); if (worldRenderer != null) worldRenderer.close(gl); + if (textureManager != null) textureManager.close(gl); if (positionColorProgram != null) positionColorProgram.close(gl); if (positionColorTexProgram != null) positionColorTexProgram.close(gl); diff --git a/modules/freeworld.client/src/main/java/freeworld/client/render/gl/GLStateMgr.java b/modules/freeworld.client/src/main/java/freeworld/client/render/gl/GLStateMgr.java index 7c2fa12..a161402 100644 --- a/modules/freeworld.client/src/main/java/freeworld/client/render/gl/GLStateMgr.java +++ b/modules/freeworld.client/src/main/java/freeworld/client/render/gl/GLStateMgr.java @@ -20,12 +20,17 @@ * @since 0.1.0 */ public abstract class GLStateMgr implements - GL10C, GL11C, GL15C, + GL10C, GL11C, GL14C, GL15C, GL20C, GL30C, GL41C, DirectAccess { private int arrayBufferBinding = 0; + private boolean blend = false; + private int blendSrcRGB = ONE; + private int blendSrcAlpha = ONE; + private int blendDstRGB = ZERO; + private int blendDstAlpha = ZERO; private boolean cullFace = false; private int currentProgram = 0; private int depthFunc = LESS; @@ -46,6 +51,66 @@ public int arrayBufferBinding() { return arrayBufferBinding; } + @Skip + public void enableBlend() { + if (!this.blend) { + this.blend = true; + enable(BLEND); + } + } + + @Skip + public void disableBlend() { + if (this.blend) { + this.blend = false; + disable(BLEND); + } + } + + @Skip + public boolean blend() { + return blend; + } + + @Skip + public void setBlendFuncSeparate(int srcRGB, int dstRGB, int srcAlpha, int dstAlpha) { + if (this.blendSrcRGB != srcRGB || + this.blendDstRGB != dstRGB || + this.blendSrcAlpha != srcAlpha || + this.blendDstAlpha != dstAlpha) { + this.blendSrcRGB = srcRGB; + this.blendDstRGB = dstRGB; + this.blendSrcAlpha = srcAlpha; + this.blendDstAlpha = dstAlpha; + blendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); + } + } + + @Skip + public void setBlendFunc(int sfactor, int dfactor) { + setBlendFuncSeparate(sfactor, dfactor, sfactor, dfactor); + } + + @Skip + public int blendSrcRGB() { + return blendSrcRGB; + } + + @Skip + public int blendSrcAlpha() { + return blendSrcAlpha; + } + + @Skip + public int blendDstRGB() { + return blendDstRGB; + } + + @Skip + public int blendDstAlpha() { + return blendDstAlpha; + } + @Skip public void enableCullFace() { if (!this.cullFace) { diff --git a/modules/freeworld.client/src/main/java/freeworld/client/render/texture/TextureAtlas.java b/modules/freeworld.client/src/main/java/freeworld/client/render/texture/TextureAtlas.java index 24bf3aa..426f248 100644 --- a/modules/freeworld.client/src/main/java/freeworld/client/render/texture/TextureAtlas.java +++ b/modules/freeworld.client/src/main/java/freeworld/client/render/texture/TextureAtlas.java @@ -37,7 +37,7 @@ private TextureAtlas(int id, int width, int height, int mipmapLevel, Map identifierList) { + public static TextureAtlas load(GLStateMgr gl, List identifierList, int initMipmapLevel) { final int numIds = identifierList.size(); final STBRectPack stbrp = STBRectPack.INSTANCE; try (Arena arena = Arena.ofConfined()) { @@ -47,12 +47,13 @@ public static TextureAtlas load(GLStateMgr gl, List identifierList) final STBRPContext context = STBRPContext.OF.of(arena); final STBRPNode nodes = STBRPNode.OF.of(arena, numIds); final STBRPRect rects = STBRPRect.OF.of(arena, numIds); - int mipmapLevel = 4; + int mipmapLevel = initMipmapLevel; for (int i = 0; i < numIds; i++) { final NativeImage image = imageMap.get(identifierList.get(i)); final int width = image.width(); final int height = image.height(); - if (!isPowerOfTwo(width) || !isPowerOfTwo(height)) { + if (mipmapLevel != 0 && + (!isPowerOfTwo(width) || !isPowerOfTwo(height))) { mipmapLevel = 0; } else if (mipmapLevel > 0) { mipmapLevel = Math.min(Integer.numberOfTrailingZeros(width), Integer.numberOfTrailingZeros(height)); diff --git a/modules/freeworld.client/src/main/java/freeworld/client/render/texture/TextureManager.java b/modules/freeworld.client/src/main/java/freeworld/client/render/texture/TextureManager.java index 14b25b6..5980bcc 100644 --- a/modules/freeworld.client/src/main/java/freeworld/client/render/texture/TextureManager.java +++ b/modules/freeworld.client/src/main/java/freeworld/client/render/texture/TextureManager.java @@ -23,6 +23,7 @@ */ public final class TextureManager implements GLResource { public static final Identifier BLOCK_ATLAS = Identifier.ofBuiltin("texture/atlas/block-atlas"); + public static final Identifier GUI_ATLAS = Identifier.ofBuiltin("texture/atlas/gui-atlas"); private final Map textureMap = new HashMap<>(); public void addTexture(Identifier identifier, Texture texture) { diff --git a/modules/freeworld.client/src/main/java/freeworld/client/render/world/ChunkCompiler.java b/modules/freeworld.client/src/main/java/freeworld/client/render/world/ChunkCompiler.java index 4b49b38..41bb25f 100644 --- a/modules/freeworld.client/src/main/java/freeworld/client/render/world/ChunkCompiler.java +++ b/modules/freeworld.client/src/main/java/freeworld/client/render/world/ChunkCompiler.java @@ -42,6 +42,7 @@ public ChunkVertexData call() throws Exception { final var pool = worldRenderer.vertexBuilderPool(); final DefaultVertexBuilder builder = pool.borrowObject(); try { + builder.reset(); final int cx = chunk.x(); final int cy = chunk.y(); final int cz = chunk.z(); diff --git a/modules/freeworld.client/src/main/java/freeworld/client/render/world/WorldRenderer.java b/modules/freeworld.client/src/main/java/freeworld/client/render/world/WorldRenderer.java index 80ece64..552c457 100644 --- a/modules/freeworld.client/src/main/java/freeworld/client/render/world/WorldRenderer.java +++ b/modules/freeworld.client/src/main/java/freeworld/client/render/world/WorldRenderer.java @@ -54,11 +54,6 @@ public DefaultVertexBuilder create() { return createVertexBuilder(); } - @Override - public void activateObject(PooledObject p) { - p.getObject().reset(); - } - @Override public PooledObject wrap(DefaultVertexBuilder obj) { return new DefaultPooledObject<>(obj); @@ -129,8 +124,8 @@ public void compileChunks(Entity player, List renderingChunks) { chunk.copyFrom(chunk1); } final ChunkCompileTask task = new ChunkCompileTask(new ChunkCompiler(gameRenderer, this, chunk), player, chunk.x(), chunk.y(), chunk.z()); - chunk.future = task; executor.execute(task); + chunk.future = task; chunk.dirty = false; } } diff --git a/modules/freeworld.client/src/main/resources/assets/freeworld/texture/gui/crossing.png b/modules/freeworld.client/src/main/resources/assets/freeworld/texture/gui/crossing.png new file mode 100644 index 0000000..f517c29 Binary files /dev/null and b/modules/freeworld.client/src/main/resources/assets/freeworld/texture/gui/crossing.png differ diff --git a/modules/freeworld.client/src/main/resources/assets/freeworld/texture/gui/hotbar.png b/modules/freeworld.client/src/main/resources/assets/freeworld/texture/gui/hotbar.png new file mode 100644 index 0000000..87bd087 Binary files /dev/null and b/modules/freeworld.client/src/main/resources/assets/freeworld/texture/gui/hotbar.png differ diff --git a/modules/freeworld.client/src/main/resources/assets/freeworld/texture/gui/hotbar_selected.png b/modules/freeworld.client/src/main/resources/assets/freeworld/texture/gui/hotbar_selected.png new file mode 100644 index 0000000..c42addf Binary files /dev/null and b/modules/freeworld.client/src/main/resources/assets/freeworld/texture/gui/hotbar_selected.png differ diff --git a/modules/freeworld.core/src/main/java/freeworld/world/World.java b/modules/freeworld.core/src/main/java/freeworld/world/World.java index c02f901..0471d7b 100644 --- a/modules/freeworld.core/src/main/java/freeworld/world/World.java +++ b/modules/freeworld.core/src/main/java/freeworld/world/World.java @@ -29,7 +29,7 @@ public final class World { public static final int TICKING_RADIUS = 5; public static final int TICKING_CHUNK_COUNT_CBRT = TICKING_RADIUS * 2 + 1; public static final int TICKING_CHUNK_COUNT = TICKING_CHUNK_COUNT_CBRT * TICKING_CHUNK_COUNT_CBRT * TICKING_CHUNK_COUNT_CBRT; - public final Map chunks = HashMap.newHashMap(TICKING_CHUNK_COUNT); + public final Map chunks = WeakHashMap.newWeakHashMap(TICKING_CHUNK_COUNT); private final List entities = new ArrayList<>(); private final MotionSystem motionSystem = new MotionSystem(); private final List listeners = new ArrayList<>(); diff --git a/modules/freeworld.core/src/main/java/freeworld/world/chunk/Chunk.java b/modules/freeworld.core/src/main/java/freeworld/world/chunk/Chunk.java index 1818058..6bda60a 100644 --- a/modules/freeworld.core/src/main/java/freeworld/world/chunk/Chunk.java +++ b/modules/freeworld.core/src/main/java/freeworld/world/chunk/Chunk.java @@ -62,11 +62,11 @@ public void generateTerrain() { for (int bz = 0; bz < depth; bz++) { for (int by = 0; by < height; by++) { final int absY = ChunkPos.relativeToAbsolute(y, by); - if (absY < 5) { + if (absY < -4) { setBlockType(bx, by, bz, BlockTypes.STONE); - } else if (absY < 8) { + } else if (absY < -1) { setBlockType(bx, by, bz, BlockTypes.DIRT); - } else if (absY == 8) { + } else if (absY == -1) { setBlockType(bx, by, bz, BlockTypes.GRASS_BLOCK); } }