diff --git a/core/src/ru/mclord/classic/Block.java b/core/src/ru/mclord/classic/Block.java index 7b0ae18..edb8f00 100644 --- a/core/src/ru/mclord/classic/Block.java +++ b/core/src/ru/mclord/classic/Block.java @@ -1,6 +1,7 @@ package ru.mclord.classic; import com.badlogic.gdx.graphics.g3d.Model; +import com.badlogic.gdx.graphics.g3d.attributes.FloatAttribute; import com.badlogic.gdx.utils.Disposable; public class Block implements McLordRenderable, Disposable { @@ -48,7 +49,9 @@ public boolean doIHaveThisPermission() { /* package-private */ final int backTextureId; /* package-private */ final int bottomTextureId; - protected Model model; + private Model model; + private boolean alphaTestEnabled = true; + private float alphaTestValue = 0.5f; public Block( short id, @@ -109,6 +112,9 @@ public Block( this.bottomTextureId = bottomTextureId; } + public void onBlockRegister() { + } + @Override public void initGraphics() { if (model != null) return; @@ -121,7 +127,8 @@ public void initGraphics() { manager.getTexture(bottomTextureId), manager.getTexture(topTextureId), manager.getTexture(leftTextureId), - manager.getTexture(rightTextureId) + manager.getTexture(rightTextureId), + (alphaTestEnabled ? FloatAttribute.createAlphaTest(alphaTestValue) : null) ); } @@ -129,6 +136,26 @@ public final Model getModel() { return model; } + protected final void setModel(Model model) { + this.model = model; + } + + protected final boolean isAlphaTestEnabled() { + return alphaTestEnabled; + } + + protected final void setAlphaTestEnabled(boolean alphaTestEnabled) { + this.alphaTestEnabled = alphaTestEnabled; + } + + protected final float getAlphaTestValue() { + return alphaTestValue; + } + + protected final void setAlphaTestValue(float alphaTestValue) { + this.alphaTestValue = alphaTestValue; + } + public final short getId() { return id; } diff --git a/core/src/ru/mclord/classic/Chunk.java b/core/src/ru/mclord/classic/Chunk.java index ac6d27d..b1f7978 100644 --- a/core/src/ru/mclord/classic/Chunk.java +++ b/core/src/ru/mclord/classic/Chunk.java @@ -53,7 +53,8 @@ public void initGraphics() { int realX = getX(this); int realZ = getZ(this); - modelCache = new ModelCache(new ModelCache.Sorter(), new ModelCache.TightMeshPool()); + modelCache = new ModelCache( + new ModelCache.Sorter(), new ModelCache.TightMeshPool()); modelCache.begin(); for (int x = realX; x < realX + CHUNK_SIZE; x++) { for (int y = 0; y < level.sizeY; y++) { @@ -63,7 +64,7 @@ public void initGraphics() { block.initGraphics(); ModelInstance modelInstance = new ModelInstance(block.getModel(), - new Matrix4().translate(x, y, z), (String[]) null); + (new Matrix4()).translate(x, y, z), (String[]) null); modelCache.add(modelInstance); } } @@ -76,8 +77,8 @@ public void render(ModelBatch modelBatch, Environment environment) { if (modelCache == null) return; Camera camera = modelBatch.getCamera(); - float distanceSquared = - Helper.distanceSquared(this, camera.position.x, camera.position.z); + float distanceSquared = Helper + .distanceSquared(this, camera.position.x, camera.position.z); if (distanceSquared > RENDER_DISTANCE_SQUARED) return; modelBatch.render(modelCache, environment); diff --git a/core/src/ru/mclord/classic/Helper.java b/core/src/ru/mclord/classic/Helper.java index 2091cb3..dde3406 100644 --- a/core/src/ru/mclord/classic/Helper.java +++ b/core/src/ru/mclord/classic/Helper.java @@ -7,6 +7,7 @@ import com.badlogic.gdx.graphics.g3d.Material; import com.badlogic.gdx.graphics.g3d.Model; import com.badlogic.gdx.graphics.g3d.attributes.BlendingAttribute; +import com.badlogic.gdx.graphics.g3d.attributes.FloatAttribute; import com.badlogic.gdx.graphics.g3d.attributes.TextureAttribute; import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder; import com.badlogic.gdx.utils.Disposable; @@ -54,6 +55,20 @@ public static Model constructBlock( Texture top, Texture left, Texture right + ) { + return constructBlock(size, front, back, bottom, top, left, right, null); + } + + public static Model constructBlock( + float size, + Texture front, + Texture back, + Texture bottom, + Texture top, + Texture left, + Texture right, + // many thanks to https://gamedev.stackexchange.com/a/183790 + FloatAttribute alphaTest ) { TextureManager manager = TextureManager.getInstance(); ModelBuilder modelBuilder = new ModelBuilder(); @@ -138,7 +153,13 @@ public static Model constructBlock( ); Model model = modelBuilder.end(); - for (Material material : model.materials) material.set(ALPHA); + for (Material material : model.materials) { + if (alphaTest != null) { + material.set(ALPHA, alphaTest); + } else { + material.set(ALPHA); + } + } return model; } @@ -164,11 +185,11 @@ public static Texture generateCrosshairTexture(int screenWidth, int screenHeight Pixmap pixmap = new Pixmap(size, size, Pixmap.Format.RGBA8888); for (int i = 0; i < size; i++) { pixmap.drawPixel(half, i, 0xFFFFFFFF); - pixmap.drawPixel(half + 1, i, 0xFFFFFFFF); + pixmap.drawPixel(half - 1, i, 0xFFFFFFFF); } for (int i = 0; i < size; i++) { pixmap.drawPixel(i, half, 0xFFFFFFFF); - pixmap.drawPixel(i, half + 1, 0xFFFFFFFF); + pixmap.drawPixel(i, half - 1, 0xFFFFFFFF); } Texture result = new Texture(pixmap); pixmap.dispose(); diff --git a/core/src/ru/mclord/classic/InGameScreen.java b/core/src/ru/mclord/classic/InGameScreen.java index 029072f..ec4f99d 100644 --- a/core/src/ru/mclord/classic/InGameScreen.java +++ b/core/src/ru/mclord/classic/InGameScreen.java @@ -8,25 +8,29 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g3d.Environment; import com.badlogic.gdx.graphics.g3d.ModelBatch; +import com.badlogic.gdx.graphics.g3d.ModelInstance; +import com.badlogic.gdx.math.Matrix4; +import com.badlogic.gdx.math.Vector3; import ru.mclord.classic.events.CustomizeEnvironmentEvent; public class InGameScreen implements Screen { private static final InGameScreen INSTANCE = new InGameScreen(); + private final Plugin corePlugin = PluginManager.getInstance().getCorePlugin(); private BitmapFont font; private SpriteBatch spriteBatch; private Texture crosshairTexture; + private ModelInstance aimedBlockModelInstance; private ModelBatch modelBatch; private McLordFirstPersonCameraController cameraController; private Level level; - private final Skybox skybox; + private Skybox skybox; private Environment environment; private PerspectiveCamera camera; private final float fov; private InGameScreen() { fov = Float.parseFloat(McLordClassic.getProperty("fov")); - skybox = new Skybox(); } public static InGameScreen getInstance() { @@ -53,6 +57,19 @@ public void show() { spriteBatch = new SpriteBatch(); modelBatch = new ModelBatch(); + Texture aimedBlockTexture = TextureManager.getInstance().aimedBlockTexture; + aimedBlockModelInstance = new ModelInstance(Helper + .constructBlock(0.5f, + aimedBlockTexture, + aimedBlockTexture, + aimedBlockTexture, + aimedBlockTexture, + aimedBlockTexture, + aimedBlockTexture + )); + + skybox = new Skybox(); + environment = new Environment(); if (!EventManager.getInstance() .fireEvent(CustomizeEnvironmentEvent.create(environment))) { @@ -90,7 +107,16 @@ public void render(float delta) { if (skybox.isReady()) { skybox.render(modelBatch, camera); } + + corePlugin.levelRenderStart(); level.render(modelBatch, environment); + corePlugin.levelRenderFinish(); + + Vector3 aimedBlock = level.findAimedBlock(camera.position, camera.direction); + if (aimedBlock != null) { + aimedBlockModelInstance.transform.set((new Matrix4()).translate(aimedBlock)); + modelBatch.render(aimedBlockModelInstance); + } modelBatch.end(); spriteBatch.begin(); @@ -126,8 +152,14 @@ public void dispose() { Helper.dispose(font); font = null; Helper.dispose(spriteBatch); spriteBatch = null; Helper.dispose(crosshairTexture); crosshairTexture = null; + if (aimedBlockModelInstance != null) { + Helper.dispose(aimedBlockModelInstance.model); aimedBlockModelInstance = null; + } Helper.dispose(modelBatch); modelBatch = null; Helper.dispose(level); level = null; + Helper.dispose(skybox); skybox = null; cameraController = null; + environment = null; + camera = null; } } diff --git a/core/src/ru/mclord/classic/Level.java b/core/src/ru/mclord/classic/Level.java index b009f2b..62df612 100644 --- a/core/src/ru/mclord/classic/Level.java +++ b/core/src/ru/mclord/classic/Level.java @@ -2,6 +2,7 @@ import com.badlogic.gdx.graphics.g3d.Environment; import com.badlogic.gdx.graphics.g3d.ModelBatch; +import com.badlogic.gdx.math.Vector3; import java.util.HashMap; import java.util.Map; @@ -52,19 +53,40 @@ public void initGraphics() { graphicsInitialized = true; } + /* + * Thanks to devquickie. + * https://gitlab.com/devquickie/minecraft-clone + * + * src/com/devquickie/minecraftclone/Grid.java#L63 + */ + public Vector3 findAimedBlock(Vector3 startingPoint, Vector3 direction) { + // int finish = Math.max(Math.max(sizeX, sizeY), sizeZ) * 2; + for (int i = 1; i <= Player.MAX_CLICK_DISTANCE; i++) { + Vector3 tmpStartingPoint = new Vector3(startingPoint); + Vector3 tmpDirection = new Vector3(direction); + Vector3 line = tmpStartingPoint.add(tmpDirection.nor().scl(i)); + int x = Math.round(line.x); + int y = Math.round(line.y); + int z = Math.round(line.z); + + if (x >= sizeX || y >= sizeY || z >= sizeZ || x < 0 || y < 0 || z < 0) { + break; + } + Block block = getBlockDefAt(x, y, z); + if (block.shouldBeRenderedAt(x, y, z)) { + return new Vector3(x, y, z); + } + } + + return null; + } + public Chunk getChunk(int x, int z) { return getChunkByChunkCords(Chunk.getChunkX(x), Chunk.getChunkZ(z)); } public Chunk getChunkByChunkCords(int chunkX, int chunkZ) { - Chunk chunk = chunks.get(Pair.of(chunkX, chunkZ)); - if (chunk == null) { - chunk = new Chunk(this, chunkX, chunkZ); - // until initGraphics() is called against - // this object, we don't have to dispose it - } - - return chunk; + return chunks.get(Pair.of(chunkX, chunkZ)); } @ShouldBeCalledBy(thread = "main") diff --git a/core/src/ru/mclord/classic/McLordClassic.java b/core/src/ru/mclord/classic/McLordClassic.java index 34fcca7..c643922 100644 --- a/core/src/ru/mclord/classic/McLordClassic.java +++ b/core/src/ru/mclord/classic/McLordClassic.java @@ -55,8 +55,8 @@ public enum GameStage { public static final boolean DEBUG = true; public static final String APP_NAME = "McLordClassic"; - public static final String VERSION = "0.1.4"; - public static final int VERSION_CODE = 4; + public static final String VERSION = "0.1.5"; + public static final int VERSION_CODE = 5; private static final McLordClassic INSTANCE = new McLordClassic(); @@ -249,7 +249,8 @@ public void setStage(GameStage stage) { break; } case IN_GAME: { - LoadingScreen.getInstance().setStatus("Preparing level"); + LoadingScreen.getInstance() + .setStatus("Preparing level (this can take a while)"); addTask(() -> setScreen(InGameScreen.getInstance())); break; diff --git a/core/src/ru/mclord/classic/Player.java b/core/src/ru/mclord/classic/Player.java index 039cf8d..dda8a52 100644 --- a/core/src/ru/mclord/classic/Player.java +++ b/core/src/ru/mclord/classic/Player.java @@ -3,6 +3,8 @@ import com.badlogic.gdx.utils.Disposable; public class Player implements Locatable, Disposable { + public static final int MAX_CLICK_DISTANCE = 5; + /* package-private */ final byte id; /* package-private */ final String username; /* package-private */ final Location location; diff --git a/core/src/ru/mclord/classic/Plugin.java b/core/src/ru/mclord/classic/Plugin.java index 40e9798..b85fb55 100644 --- a/core/src/ru/mclord/classic/Plugin.java +++ b/core/src/ru/mclord/classic/Plugin.java @@ -16,4 +16,8 @@ default void message(Object message) throws Exception { } default void message(String description, Object message) throws Exception {} + + default void levelRenderStart() {} + + default void levelRenderFinish() {} } diff --git a/core/src/ru/mclord/classic/PluginManager.java b/core/src/ru/mclord/classic/PluginManager.java index 233c3b3..3d1e9ba 100644 --- a/core/src/ru/mclord/classic/PluginManager.java +++ b/core/src/ru/mclord/classic/PluginManager.java @@ -25,6 +25,7 @@ private Key() { .parseInt(System.getProperty("mclordMaxPluginAttempts", "100")); private final Map pluginMap = new HashMap<>(); + private Plugin corePlugin; private Integer keyHashCode; static { @@ -241,6 +242,8 @@ public static PluginManager getInstance() { keyHashCode = key.hashCode(); plugin.message(key); + + this.corePlugin = plugin; } plugin.preInit(); } catch (Throwable t) { @@ -286,6 +289,11 @@ public Plugin getPlugin(String name) { return pluginMap.get(name); } + @ShouldBeCalledBy(thread = "main") + /* package-private */ Plugin getCorePlugin() { + return corePlugin; + } + // is called by a plugin public void initPlugins(Key key) { checkKey(key); diff --git a/core/src/ru/mclord/classic/Skybox.java b/core/src/ru/mclord/classic/Skybox.java index 540222a..22e94e6 100644 --- a/core/src/ru/mclord/classic/Skybox.java +++ b/core/src/ru/mclord/classic/Skybox.java @@ -56,6 +56,8 @@ public void render(ModelBatch batch, Camera camera) { @Override public void dispose() { - Helper.dispose(modelInstance.model); modelInstance = null; + if (modelInstance != null) { + Helper.dispose(modelInstance.model); modelInstance = null; + } } } diff --git a/core/src/ru/mclord/classic/TextureManager.java b/core/src/ru/mclord/classic/TextureManager.java index 98c251e..d4fbdaf 100644 --- a/core/src/ru/mclord/classic/TextureManager.java +++ b/core/src/ru/mclord/classic/TextureManager.java @@ -34,7 +34,8 @@ default boolean shouldWalk(int i) { /* package-private */ int textureSize; private final List temporaryPixmaps = new ArrayList<>(); private final List temporaryTextures; - private Texture emptyTexture; + /* package-private */ Texture emptyTexture; + /* package-private */ Texture aimedBlockTexture; private TextureManager() { this.searchForSkybox = Boolean.parseBoolean( @@ -107,6 +108,15 @@ public void load(String path, boolean allowNet, boolean allowFileSystem) { emptyTexture = new Texture(emptyPixmap); temporaryPixmaps.add(emptyPixmap); + Pixmap aimedBlockPixmap = new Pixmap(textureSize, textureSize, Pixmap.Format.RGBA8888); + for (int i = 0; i < textureSize; i++) { + for (int j = 0; j < textureSize; j++) { + aimedBlockPixmap.drawPixel(i, j, 0xFFFFFF0F); // 15% + } + } + aimedBlockTexture = new Texture(aimedBlockPixmap); + temporaryPixmaps.add(aimedBlockPixmap); + walk(textures, temporaryPixmaps, textures.length, textureSize, textureSize, TEXTURE_COUNT_IN_A_ROW, (pixmap, xOffset, yOffset, x, y) -> { @@ -274,6 +284,10 @@ public Texture getEmptyTexture() { return emptyTexture; } + public Texture getAimedBlockTexture() { + return aimedBlockTexture; + } + public int getTextureCount() { return textures.length; } @@ -346,6 +360,7 @@ public void rotate90Pixmap(Pixmap pixmap, boolean clockwise) { @ShouldBeCalledBy(thread = "main") public void dispose() { Helper.dispose(emptyTexture); + Helper.dispose(aimedBlockTexture); if (textures != null) { for (Texture texture : textures) { Helper.dispose(texture);