Removed Litecraft

liteCraft v2.0.0-alpha
Caroline Bell 2020-03-08 21:09:07 -07:00
parent 7b774b1fc8
commit f3d95b99c6
29 changed files with 0 additions and 2640 deletions

View File

@ -1,217 +0,0 @@
package com.halotroop.litecraft;
import org.joml.*;
import com.github.hydos.ginger.engine.common.Constants;
import com.github.hydos.ginger.engine.common.api.*;
import com.github.hydos.ginger.engine.common.api.game.*;
import com.github.hydos.ginger.engine.common.cameras.*;
import com.github.hydos.ginger.engine.common.elements.objects.Light;
import com.github.hydos.ginger.engine.common.font.FontType;
import com.github.hydos.ginger.engine.common.info.RenderAPI;
import com.github.hydos.ginger.engine.common.io.Window;
import com.github.hydos.ginger.engine.common.obj.ModelLoader;
import com.github.hydos.ginger.engine.opengl.api.GingerGL;
import com.github.hydos.ginger.engine.opengl.postprocessing.PostProcessing;
import com.github.hydos.ginger.engine.opengl.render.*;
import com.github.hydos.ginger.engine.opengl.render.models.GLTexturedModel;
import com.github.hydos.ginger.engine.opengl.utils.*;
import com.halotroop.litecraft.render.BlockRenderer;
import com.halotroop.litecraft.save.LitecraftSave;
import com.halotroop.litecraft.screens.*;
import com.halotroop.litecraft.types.block.Blocks;
import com.halotroop.litecraft.types.entity.PlayerEntity;
import com.halotroop.litecraft.util.RelativeDirection;
import com.halotroop.litecraft.world.World;
import tk.valoeghese.gateways.client.io.*;
public class Litecraft extends Game
{
// FIXME: search for ((GingerGL)engine) and properly implement both render APIs when Vulkan is complete.
private static Litecraft INSTANCE;
private World world;
private LitecraftSave save;
private GingerEngine engine;
public int fps, ups, tps;
public Vector4i dbgStats = new Vector4i();
private long frameTimer;
private BlockRenderer blockRenderer;
public Litecraft(int windowWidth, int windowHeight, float frameLimit)
{
Litecraft.INSTANCE = this;
// set constants
this.setupConstants();
this.setupGinger(windowWidth, windowHeight, frameLimit);
// make sure blocks are initialised ??? (Currently does nothing)
Blocks.init();
this.frameTimer = System.currentTimeMillis();
// setup keybinds
setupKeybinds();
// Open the title screen if nothing is already open.
if (GingerRegister.getInstance().currentScreen == null && world == null) ((GingerGL) engine).openScreen(new TitleScreen());
// start the game loop
this.engine.startGameLoop();
}
@Override
public void exit()
{
engine.openScreen(new ExitGameScreen());
render(); // Render the exit game screen
if (this.world != null)
{
System.out.println("Saving chunks...");
long time = System.currentTimeMillis();
this.world.unloadAllChunks();
this.getSave().saveGlobalData(this.world.getSeed(), ((PlayerEntity) this.player));
System.out.println("Saved world in " + (System.currentTimeMillis() - time) + " milliseconds");
}
engine.cleanup();
System.exit(0);
}
/** Things that ARE rendering: Anything that results in something being drawn to the frame buffer
* Things that are NOT rendering: Things that happen to update between frames but do not result in things being drawn to the screen */
@Override
public void render()
{
fps += 1;
if (System.currentTimeMillis() > frameTimer + 1000) updateDebugStats();
// Render shadows
GingerRegister.getInstance().masterRenderer.renderShadowMap(data.entities, data.lights.get(0));
// If there's a world, render it!
if (this.world != null) renderWorld();
// Render any overlays (GUIs, HUDs)
this.engine.renderOverlays();
// Put what's stored in the inactive framebuffer on the screen
Window.swapBuffers();
}
// Updates the debug stats once per real-time second, regardless of how many frames have been rendered
private void updateDebugStats()
{
this.dbgStats.set(fps, ups, tps, 0);
this.fps = 0;
this.ups = 0;
this.tps = 0;
this.frameTimer += 1000;
}
public void renderWorld()
{
GameData data = GingerRegister.getInstance().game.data;
if (Window.renderAPI == RenderAPI.OpenGL)
{
GLUtils.preRenderScene(((GingerGL) engine).getRegistry().masterRenderer);
((GingerGL) engine).contrastFbo.bindFBO();
((GingerGL) engine).getRegistry().masterRenderer.renderScene(data.entities, data.normalMapEntities, data.lights, data.camera, data.clippingPlane);
((GingerGL) engine).contrastFbo.unbindFBO();
PostProcessing.doPostProcessing(((GingerGL) engine).contrastFbo.colorTexture);
}
}
@Override
public void update()
{ ups += 1; }
private void setupConstants()
{
Constants.movementSpeed = 0.5f; // movement speed
Constants.turnSpeed = 0.00006f; // turn speed
Constants.gravity = new Vector3f(0, -0.0000000005f, 0); // compute gravity as a vec3f
Constants.jumpPower = 0.00005f; // jump power
}
// set up Ginger3D engine stuff
private void setupGinger(int windowWidth, int windowHeight, float frameCap)
{
if (engine == null) // Prevents this from being run more than once on accident.
{
Window.create(windowWidth, windowHeight, "Litecraft", frameCap, RenderAPI.OpenGL); // create window
// set up the gateways keybind key tracking
KeyCallbackHandler.trackWindow(Window.getWindow());
MouseCallbackHandler.trackWindow(Window.getWindow());
// set up ginger utilities
GLUtils.init();
switch (Window.renderAPI)
{
case OpenGL:
{
this.engine = new GingerGL();
//Set the player model
GLTexturedModel playerModel = ModelLoader.loadGenericCube("block/cubes/stone/brick/stonebrick.png");
FontType font = new FontType(GLLoader.loadFontAtlas("candara.png"), "candara.fnt");
this.blockRenderer = new BlockRenderer(GingerRegister.getInstance().masterRenderer.getEntityShader(), GingerRegister.getInstance().masterRenderer.getProjectionMatrix());
this.player = new PlayerEntity(playerModel, new Vector3f(0, 0, -3), 0, 180f, 0, new Vector3f(0.2f, 0.2f, 0.2f));
this.camera = new FirstPersonCamera(player);
this.data = new GameData(this.player, this.camera, 20);
this.data.handleGuis = false;
((GingerGL) engine).setup(new GLRenderManager(this.camera), INSTANCE);
((GingerGL) engine).setGlobalFont(font);
this.data.entities.add(this.player);
break;
}
case Vulkan:
{
// TODO: Setup Vulkan
exit();
break;
}
}
Light sun = new Light(new Vector3f(0, 105, 0), new Vector3f(0.9765625f, 0.98828125f, 0.05859375f), new Vector3f(0.002f, 0.002f, 0.002f));
this.data.lights.add(sun);
}
}
private void setupKeybinds()
{
Input.addPressCallback(Keybind.EXIT, this::exit);
Input.addInitialPressCallback(Keybind.FULLSCREEN, Window::fullscreen);
Input.addInitialPressCallback(Keybind.WIREFRAME, GingerRegister.getInstance()::toggleWireframe);
Input.addPressCallback(Keybind.MOVE_FORWARD, () -> ((PlayerEntity) this.player).move(RelativeDirection.FORWARD));
Input.addPressCallback(Keybind.MOVE_BACKWARD, () -> ((PlayerEntity) this.player).move(RelativeDirection.BACKWARD));
Input.addPressCallback(Keybind.STRAFE_LEFT, () -> ((PlayerEntity) this.player).move(RelativeDirection.LEFT));
Input.addPressCallback(Keybind.STRAFE_RIGHT, () -> ((PlayerEntity) this.player).move(RelativeDirection.RIGHT));
Input.addPressCallback(Keybind.FLY_UP, () -> ((PlayerEntity) this.player).move(RelativeDirection.UP));
Input.addPressCallback(Keybind.FLY_DOWN, () -> ((PlayerEntity) this.player).move(RelativeDirection.DOWN));
}
/** Things that should be ticked: Entities when deciding an action, in-game timers (such as smelting), the in-game time
* Things that should not be ticked: Rendering, input, player movement */
@Override
public void tick()
{
tps += 1;
if (this.player instanceof PlayerEntity && camera != null)
{
Input.invokeAllListeners();
((PlayerEntity) this.player).updateMovement();
camera.updateMovement();
}
}
// @formatter=off
public static Litecraft getInstance()
{ return INSTANCE; }
public Camera getCamera()
{ return this.camera; }
public LitecraftSave getSave()
{ return save; }
public World getWorld()
{ return this.world; }
public void changeWorld(World world)
{ this.world = world; }
public void setSave(LitecraftSave save)
{ this.save = save; }
@Override
public void renderScene()
{ world.render(this.blockRenderer); }
}

View File

@ -1,17 +0,0 @@
package com.halotroop.litecraft;
import org.lwjgl.Version;
import org.lwjgl.glfw.GLFW;
public class StarterGL
{
// private static final boolean usingEclipse = false;
public static void main(String[] args)
{
System.out.println("GLFW version: " + GLFW.glfwGetVersionString());
System.out.println("LWJGL version: " + Version.getVersion());
// Put SoundSystem version here
// TODO: Put a commandline reader here to check for desired width, height, and frame limit!
new Litecraft(1280, 720, 60);
}
}

View File

@ -1,6 +0,0 @@
package com.halotroop.litecraft.logic;
public enum Gamemode
{
GRAVITY, FREECAM,
}

View File

@ -1,10 +0,0 @@
package com.halotroop.litecraft.logic;
import tk.valoeghese.sod.BinaryData;
public interface SODSerializable
{
void read(BinaryData data);
void write(BinaryData data);
}

View File

@ -1,90 +0,0 @@
package com.halotroop.litecraft.render;
import org.joml.Matrix4f;
import org.lwjgl.opengl.*;
import com.github.hydos.ginger.engine.common.api.GingerRegister;
import com.github.hydos.ginger.engine.common.elements.objects.RenderObject;
import com.github.hydos.ginger.engine.common.io.Window;
import com.github.hydos.ginger.engine.common.math.Maths;
import com.github.hydos.ginger.engine.opengl.render.Renderer;
import com.github.hydos.ginger.engine.opengl.render.models.GLTexturedModel;
import com.github.hydos.ginger.engine.opengl.render.shaders.StaticShader;
import com.halotroop.litecraft.types.block.BlockInstance;
import com.halotroop.litecraft.world.gen.WorldGenConstants;
public class BlockRenderer extends Renderer implements WorldGenConstants
{
public StaticShader shader;
public int atlasID;
public BlockRenderer(StaticShader shader, Matrix4f projectionMatrix)
{
this.shader = shader;
shader.start();
shader.loadProjectionMatrix(projectionMatrix);
shader.stop();
this.atlasID = VoxelLoader.createBlockAtlas();
}
private void prepBlockInstance(RenderObject entity)
{
Matrix4f transformationMatrix = Maths.createTransformationMatrix(entity.getPosition(), entity.getRotX(),
entity.getRotY(), entity.getRotZ(), entity.getScale());
shader.loadTransformationMatrix(transformationMatrix);
}
public void prepareModel(GLTexturedModel model)
{
GL30.glBindVertexArray(model.getRawModel().getVaoID());
GL20.glEnableVertexAttribArray(0);
GL20.glEnableVertexAttribArray(1);
GL20.glEnableVertexAttribArray(2);
}
public void unbindModel()
{
GL20.glDisableVertexAttribArray(0);
GL20.glDisableVertexAttribArray(1);
GL20.glDisableVertexAttribArray(2);
GL30.glBindVertexArray(0);
}
public void enableWireframe()
{ if (GingerRegister.getInstance().wireframe)
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE); }
public void disableWireframe()
{ if (GingerRegister.getInstance().wireframe)
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL); }
public void prepareRender()
{
// TODO: combine VBOS
shader.start();
shader.loadSkyColour(Window.getColour());
shader.loadViewMatrix(GingerRegister.getInstance().game.data.camera);
shader.loadFakeLightingVariable(true);
shader.loadShine(1, 1);
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, atlasID);
enableWireframe();
}
public void render(BlockInstance[] renderList)
{
prepareRender();
for (BlockInstance entity : renderList)
{
if (entity != null && entity.getModel() != null)
{
GLTexturedModel blockModel = entity.getModel();
GL11.glBindTexture(GL11.GL_TEXTURE_2D, blockModel.getTexture().getTextureID());
prepBlockInstance(entity);
GL11.glDrawElements(GL11.GL_TRIANGLES, blockModel.getRawModel().getVertexCount(), GL11.GL_UNSIGNED_INT, 0);
}
}
// disableWireframe();
// shader.stop();
}
}

View File

@ -1,50 +0,0 @@
package com.halotroop.litecraft.render;
import java.nio.ByteBuffer;
import org.lwjgl.opengl.*;
import com.github.hydos.ginger.engine.opengl.utils.GLLoader;
import com.halotroop.litecraft.types.block.*;
public class VoxelLoader extends GLLoader
{
public static int createBlockAtlas()
{
int width = 16;
int height = 16;
//Prepare the atlas texture and gen it
int atlasId = GL11.glGenTextures();
//Bind it to openGL
GL11.glBindTexture(GL11.GL_TEXTURE_2D, atlasId);
//Apply the settings for the texture
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
//Fill the image with blank image data
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, width * 2, height * 2, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null);
long maxX = Math.round(Math.sqrt(Blocks.blocks.size()));
int currentX = 0;
int currentY = 0;
for (Block block : Blocks.blocks)
{
//just in case
if (!block.texture.equals("DONTLOAD"))
{
System.out.println(block.texture);
block.updateBlockModelData();
if (currentX > maxX)
{
currentX = 0;
currentY--;
}
GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0,
currentX * width, currentY * height,
width, height,
GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE,
block.model.getTexture().getTexture().getImage());
currentX++;
}
}
return atlasId;
}
}

View File

@ -1,163 +0,0 @@
package com.halotroop.litecraft.save;
import java.io.*;
import java.util.Random;
import org.joml.Vector3f;
import com.github.hydos.ginger.engine.common.cameras.Camera;
import com.halotroop.litecraft.Litecraft;
import com.halotroop.litecraft.types.entity.PlayerEntity;
import com.halotroop.litecraft.world.*;
import com.halotroop.litecraft.world.dimension.Dimension;
import tk.valoeghese.sod.*;
public final class LitecraftSave
{
public LitecraftSave(String name, boolean mustCreateNew)
{
StringBuilder sb = new StringBuilder(SAVE_DIR).append(name);
File saveDir = new File(sb.toString());
if (mustCreateNew)
{
while (saveDir.exists())
{
sb.append('_'); // append "_" to the save name until we get a unique save, if we must create a new save
saveDir = new File(sb.toString());
}
}
this.file = saveDir;
this.file.mkdirs();
}
private final File file;
public boolean saveChunk(Chunk chunk)
{
StringBuilder fileLocBuilder = new StringBuilder(this.file.getPath())
.append('/').append(Dimension.getById(chunk.dimension).saveIdentifier)
.append('/').append(chunk.chunkX)
.append('/').append(chunk.chunkZ);
File chunkDir = new File(fileLocBuilder.toString());
chunkDir.mkdirs(); // create directory for file if it does not exist
// format: <save dir>/<dim>/<chunkX>/<chunkZ>/<chunkY>.sod
File chunkFile = new File(fileLocBuilder.append('/').append(chunk.chunkY).append(".sod").toString());
try
{
chunkFile.createNewFile();
BinaryData data = new BinaryData(); // create new empty binary data
chunk.write(data); // write the chunk info to the binary data
return data.write(chunkFile); // write the data to the file, return whether an io exception occurred
}
catch (IOException e)
{
e.printStackTrace();
return false; // io exception = chunk writing failed at some point
}
}
public Chunk readChunk(World world, int chunkX, int chunkY, int chunkZ, int dimension)
{
// format: <save dir>/<dim>/<chunkX>/<chunkZ>/<chunkY>.sod
File chunkFile = new File(new StringBuilder(this.file.getPath())
.append('/').append(Dimension.getById(dimension).saveIdentifier)
.append('/').append(chunkX)
.append('/').append(chunkZ)
.append('/').append(chunkY).append(".sod").toString());
if (chunkFile.isFile())
{
BinaryData data = BinaryData.read(chunkFile);
Chunk result = new Chunk(world, chunkX, chunkY, chunkZ, dimension); // create chunk
result.read(data); // load the chunk data we have just read into the chunk
return result;
}
else return null;
}
public World getWorldOrCreate(Dimension<?> dim)
{
File globalDataFile = new File(this.file.getPath() + "/global_data.sod");
if (globalDataFile.isFile()) // load world
{
BinaryData data = BinaryData.read(globalDataFile); // read data from the world file
DataSection properties = data.get("properties"); // get the properties data section from the data that we have just read
DataSection playerData = data.get("player");
long seed = 0; // default seed if we cannot read it is 0
float playerX = 0, playerY = 0, playerZ = -3; // default player x/y/z
float playerYaw = 0.0f;
try // try read the seed from the file
{
seed = properties.readLong(0); // seed is at index 0
playerX = playerData.readFloat(0); // player x/y/z is at index 0/1/2 respectively
playerY = playerData.readFloat(1);
playerZ = playerData.readFloat(2);
Camera camera = Litecraft.getInstance().getCamera(); // get camera
camera.setPitch(playerData.readFloat(3)); // read pitch, yaw, roll from 3/4/5
playerYaw = playerData.readFloat(4);
camera.setYaw(playerYaw);
camera.setRoll(playerData.readFloat(5));
}
catch (Throwable e)
{
System.out.println("Exception in reading save data! This may be benign, merely an artifact of an update to the contents of the world save data.");
}
World world = new World(seed, RENDER_SIZE, dim, this); // create new world with seed read from file or 0, if it could not be read
world.spawnPlayer(playerX, playerY, playerZ); // spawn player in world
return world;
}
else // create world
{
long seed = new Random().nextLong();
try
{
globalDataFile.createNewFile(); // create world file
this.writeGlobalData(globalDataFile, seed, new Vector3f(0, 0, -3)); // write global data with default player pos
}
catch (IOException e)
{
// If this fails the world seed will not be consistent across saves
e.printStackTrace();
}
World world = new World(seed, RENDER_SIZE, dim, this); // create new world with generated seed
world.spawnPlayer(); // spawn player in world
return world;
}
}
public void saveGlobalData(long seed, PlayerEntity playerEntity)
{
try
{
File globalDataFile = new File(this.file.getPath() + "/global_data.sod");
globalDataFile.createNewFile(); // create world file if it doesn't exist.
writeGlobalData(globalDataFile, seed, playerEntity.getPosition());
}
catch (IOException e)
{
System.err.println("A critical error occurred while trying to save world data!");
e.printStackTrace();
}
}
private void writeGlobalData(File globalDataFile, long seed, Vector3f playerPos)
{
BinaryData data = new BinaryData(); // create empty binary data
DataSection properties = new DataSection(); // create empty data section for properties
properties.writeLong(seed); // write seed at index 0
DataSection playerData = new DataSection();
playerData.writeFloat(playerPos.x); // default spawn player x/y/z
playerData.writeFloat(playerPos.y);
playerData.writeFloat(playerPos.z);
Camera camera = Litecraft.getInstance().getCamera();
playerData.writeFloat(camera.getPitch());
playerData.writeFloat(camera.getYaw());
playerData.writeFloat(camera.getRoll());
data.put("properties", properties); // add properties section to data
data.put("player", playerData); // add player section to data
data.write(globalDataFile); // write to file
}
private static final String SAVE_DIR = "./saves/";
private static final int RENDER_SIZE = 5;
}

View File

@ -1,34 +0,0 @@
package com.halotroop.litecraft.screens;
import org.joml.Vector2f;
import com.github.hydos.ginger.engine.common.api.GingerEngine;
import com.github.hydos.ginger.engine.common.font.GUIText;
import com.github.hydos.ginger.engine.common.io.Window;
import com.github.hydos.ginger.engine.common.screen.Screen;
import com.github.hydos.ginger.engine.opengl.api.GingerGL;
public class ExitGameScreen extends Screen
{
private GUIText infoText;
// TODO: Add Vulkan text renderer
private GingerEngine engine = GingerGL.getInstance();
public ExitGameScreen()
{
infoText = ((GingerGL) engine).registerText("Saving and exiting...", 3, new Vector2f(Window.getWidth() / 2, Window.getHeight() / 2), 1f, true, "info");
infoText.setBorderWidth(0.5f);
}
@Override
public void render()
{}
@Override
public void tick()
{}
@Override
public void cleanup()
{ infoText.remove(); }
}

View File

@ -1,50 +0,0 @@
package com.halotroop.litecraft.screens;
import org.joml.*;
import com.github.hydos.ginger.engine.common.api.*;
import com.github.hydos.ginger.engine.common.font.GUIText;
import com.github.hydos.ginger.engine.common.screen.Screen;
import com.github.hydos.ginger.engine.opengl.api.GingerGL;
import com.halotroop.litecraft.Litecraft;
public class IngameHUD extends Screen
{
private GUIText debugText;
private GUIText positionText;
// TODO: Add Vulkan text renderer
private GingerEngine engine = GingerGL.getInstance();
private Litecraft litecraft = Litecraft.getInstance();
public IngameHUD()
{
debugText = ((GingerGL) engine).registerText("Loading...", 2, new Vector2f(0, 0), 1f, true, "debugInfo");
debugText.setBorderWidth(0.5f);
positionText = ((GingerGL) engine).registerText("Loading...", 2, new Vector2f(0, -0.1f), 1f, true, "posInfo");
positionText.setBorderWidth(0.5f);
}
@Override
public void render()
{}
@Override
public void tick()
{
// long maxMemory = Runtime.getRuntime().maxMemory();
long totalMemory = Runtime.getRuntime().totalMemory();
long freeMemory = Runtime.getRuntime().freeMemory();
long usedMemory = (totalMemory - freeMemory) / 1024 / 1024;
Vector4i dbg = litecraft.dbgStats;
Vector3f position = GingerRegister.getInstance().game.data.playerObject.getPosition();
debugText.setText("FPS: " + dbg.x() + " UPS: " + dbg.y() + " TPS: " + dbg.z() + " TWL: " + dbg.w() + " Mem: " + usedMemory + "MB");
positionText.setText("Position: " + (int) position.x() + ", " + (int) position.y() + ", " + (int) position.z());
}
@Override
public void cleanup()
{
debugText.remove();
positionText.remove();
}
}

View File

@ -1,71 +0,0 @@
package com.halotroop.litecraft.screens;
import java.util.ArrayList;
import org.joml.*;
import com.github.hydos.ginger.engine.common.api.GingerEngine;
import com.github.hydos.ginger.engine.common.elements.GuiTexture;
import com.github.hydos.ginger.engine.common.elements.buttons.TextureButton;
import com.github.hydos.ginger.engine.common.font.GUIText;
import com.github.hydos.ginger.engine.common.io.Window;
import com.github.hydos.ginger.engine.common.screen.Screen;
import com.github.hydos.ginger.engine.opengl.api.GingerGL;
import com.halotroop.litecraft.Litecraft;
import com.halotroop.litecraft.save.LitecraftSave;
import com.halotroop.litecraft.world.dimension.Dimensions;
/** YeS */
public class TitleScreen extends Screen
{
private GUIText debugText;
// TODO: Add Vulkan text renderer
private GingerEngine engine = GingerGL.getInstance();
private TextureButton playButton;
private Litecraft litecraft = Litecraft.getInstance();
public TitleScreen()
{
elements = new ArrayList<GuiTexture>();
playButton = ((GingerGL) engine).registerButton("/textures/guis/playbutton.png", new Vector2f(0, 0), new Vector2f(0.25f, 0.1f));
playButton.show(Litecraft.getInstance().data.guis);
debugText = ((GingerGL) engine).registerText("Loading...", 2, new Vector2f(0, 0), 1f, true, "debugInfo");
debugText.setBorderWidth(0.5f);
}
@Override
public void render()
{}
@Override
public void tick()
{
Vector4i dbg = litecraft.dbgStats;
debugText.setText("FPS: " + dbg.x() + " UPS: " + dbg.y() + " TPS: " + dbg.z() + " TWL: " + dbg.w());
playButton.update();
if (playButton.isClicked())
{
Window.lockMouse();
if (Litecraft.getInstance().getWorld() == null)
{
Litecraft.getInstance().setSave(new LitecraftSave("SegregatedOrdinalData", false));
Litecraft.getInstance().changeWorld(Litecraft.getInstance().getSave().getWorldOrCreate(Dimensions.OVERWORLD));
((GingerGL) engine).setGingerPlayer(Litecraft.getInstance().getWorld().playerEntity);
}
if (Litecraft.getInstance().getWorld() != null)
{
((GingerGL) engine).openScreen(new IngameHUD());
this.cleanup();
}
//TODO: add world creation gui so it takes u to world creation place
//TODO: add a texture to be rendered behind the gui as an option
}
}
@Override
public void cleanup()
{
this.debugText.remove();
this.playButton.hide(Litecraft.getInstance().data.guis);
}
}

View File

@ -1,95 +0,0 @@
package com.halotroop.litecraft.types.block;
import java.util.*;
import com.github.hydos.ginger.engine.common.obj.ModelLoader;
import com.github.hydos.ginger.engine.opengl.render.models.GLTexturedModel;
public class Block
{
public static class Properties
{ // add properties to this builder!
private boolean visible = true;
private boolean fullCube = true;
private float caveCarveThreshold = -1f; // cannot carve
private final String identifier;
public Properties(String identifier)
{ this.identifier = identifier; }
public Properties fullCube(boolean fullCube)
{
this.fullCube = fullCube;
return this;
}
public Properties visible(boolean visible)
{
this.visible = visible;
return this;
}
public Properties caveCarveThreshold(float threshold)
{
this.caveCarveThreshold = threshold;
return this;
}
}
public GLTexturedModel model;
private final boolean visible, fullCube;
private final float caveCarveThreshold;
public final String identifier;
public String texture;
public boolean isFullCube()
{ return this.fullCube; }
public boolean isVisible()
{ return this.visible; }
public float getCaveCarveThreshold()
{ return this.caveCarveThreshold; }
protected Block(Properties properties)
{ this((GLTexturedModel) null, properties); }
protected Block(String texture, Properties properties)
{
this(ModelLoader.loadGenericCube("block/" + texture), properties);
this.texture = texture;
}
protected Block(GLTexturedModel model, Properties properties)
{
this.model = model;
this.visible = properties.visible;
this.fullCube = properties.fullCube;
this.identifier = properties.identifier;
this.caveCarveThreshold = properties.caveCarveThreshold;
if (model != null)
{
this.texture = model.getTexture().getTexture().getLocation();
}
else
{
this.texture = "DONTLOAD";
}
IDENTIFIER_TO_BLOCK.put(this.identifier, this);
Blocks.blocks.add(this);
}
public void updateBlockModelData()
{
System.out.println("Updating block with texture at block/" + texture);
this.model = ModelLoader.loadGenericCube("block/" + texture);
}
public static final Block getBlock(String identifier)
{ return IDENTIFIER_TO_BLOCK.get(identifier); }
public static final Block getBlockOrAir(String identifier)
{ return IDENTIFIER_TO_BLOCK.getOrDefault(identifier, Blocks.AIR); }
private static final Map<String, Block> IDENTIFIER_TO_BLOCK = new HashMap<>();
}

View File

@ -1,18 +0,0 @@
package com.halotroop.litecraft.types.block;
import org.joml.Vector3f;
import com.github.hydos.ginger.engine.common.elements.objects.RenderObject;
import com.halotroop.litecraft.world.Chunk;
public class BlockInstance extends RenderObject
{
public BlockInstance(Block block, Vector3f position)
{ super(block.model, position, 0, 0, 0, new Vector3f(1f, 1f, 1f)); }
public void processCulling(Chunk chunk)
{
Vector3f southNeighbourBlockLocation = this.getPosition();
southNeighbourBlockLocation.x--;
}
}

View File

@ -1,20 +0,0 @@
package com.halotroop.litecraft.types.block;
import java.util.ArrayList;
import com.halotroop.litecraft.types.block.Block.Properties;
public final class Blocks
{
public static ArrayList<Block> blocks = new ArrayList<Block>();
public static final Block AIR = new Block(new Properties("air").visible(false).fullCube(false));
public static final Block GRASS = new Block(new Properties("cubes/soil/grass/grass_top.png").caveCarveThreshold(0.04f));
public static final Block DIRT = new Block("cubes/soil/dirt.png", new Properties("dirt").caveCarveThreshold(0.04f));
public static final Block ANDESITE = new Block("cubes/stone/basic/andesite.png", new Properties("andesite").caveCarveThreshold(0.08f));
public static final Block DIORITE = new Block("cubes/stone/basic/diorite.png", new Properties("diorite").caveCarveThreshold(0.05f));
public static final Block GRANITE = new Block("cubes/stone/basic/granite.png", new Properties("granite").caveCarveThreshold(0.06f));
public static final Block GNEISS = new Block("cubes/stone/basic/gneiss.png", new Properties("gneiss").caveCarveThreshold(0.09f));
public static Block init()
{ return AIR; }
}

View File

@ -1,12 +0,0 @@
package com.halotroop.litecraft.types.entity;
import org.joml.Vector3f;
import com.github.hydos.ginger.engine.common.elements.objects.RenderObject;
import com.github.hydos.ginger.engine.opengl.render.models.GLTexturedModel;
public abstract class Entity extends RenderObject
{
public Entity(GLTexturedModel model, Vector3f position, float rotX, float rotY, float rotZ, Vector3f scale)
{ super(model, position, rotX, rotY, rotZ, scale); }
}

View File

@ -1,100 +0,0 @@
package com.halotroop.litecraft.types.entity;
import org.joml.Vector3f;
import com.github.hydos.ginger.engine.common.Constants;
import com.github.hydos.ginger.engine.common.api.GingerRegister;
import com.github.hydos.ginger.engine.common.io.Window;
import com.github.hydos.ginger.engine.opengl.render.models.GLTexturedModel;
import com.halotroop.litecraft.Litecraft;
import com.halotroop.litecraft.util.RelativeDirection;
import com.halotroop.litecraft.world.gen.WorldGenConstants;
public class PlayerEntity extends Entity implements WorldGenConstants
{
private boolean isInAir = false;
private double upwardsSpeed;
private boolean noWeight = true;
private int chunkX, chunkY, chunkZ;
public PlayerEntity(GLTexturedModel model, Vector3f position, float rotX, float rotY, float rotZ, Vector3f scale)
{
super(model, position, rotX, rotY, rotZ, scale);
this.chunkX = (int) position.x >> POS_SHIFT;
this.chunkY = (int) position.y >> POS_SHIFT;
this.chunkZ = (int) position.z >> POS_SHIFT;
}
public void move(RelativeDirection direction)
{
float ry = (float) Math.toRadians(GingerRegister.getInstance().game.data.camera.getYaw());
switch (direction)
{
default:
case FORWARD:
position.z -= Math.cos(ry) * Constants.movementSpeed;
position.x += Math.sin(ry) * Constants.movementSpeed;
break;
case BACKWARD:
position.z += Math.cos(ry) * Constants.movementSpeed;
position.x -= Math.sin(ry) * Constants.movementSpeed;
break;
case LEFT:
ry -= RIGHT_ANGLE;
position.z -= Math.cos(ry) * Constants.movementSpeed;
position.x += Math.sin(ry) * Constants.movementSpeed;
break;
case RIGHT:
ry += RIGHT_ANGLE;
position.z -= Math.cos(ry) * Constants.movementSpeed;
position.x += Math.sin(ry) * Constants.movementSpeed;
break;
case UP:
if (this.noWeight)
position.y += Constants.movementSpeed;
else this.jump();
break;
case DOWN:
position.y -= Constants.movementSpeed;
break;
}
}
private static final float RIGHT_ANGLE = (float) (Math.PI / 2f);
private void jump()
{
if (!isInAir)
{
isInAir = true;
this.upwardsSpeed = Constants.jumpPower;
}
}
public int getChunkX()
{ return this.chunkX; }
public int getChunkY()
{ return this.chunkY; }
public int getChunkZ()
{ return this.chunkZ; }
public void updateMovement()
{
super.increasePosition(0, (float) (upwardsSpeed * (Window.getTime())), 0);
upwardsSpeed += Constants.gravity.y() * Window.getTime(); // TODO: Implement 3D gravity
isInAir = false;
upwardsSpeed = 0;
int newChunkX = (int) position.x >> POS_SHIFT;
int newChunkY = (int) position.y >> POS_SHIFT;
int newChunkZ = (int) position.z >> POS_SHIFT;
if (newChunkX != this.chunkX || newChunkY != this.chunkY || newChunkZ != this.chunkZ)
{
Litecraft.getInstance().getWorld().updateLoadedChunks(newChunkX, newChunkY, newChunkZ);
this.chunkX = newChunkX;
this.chunkY = newChunkY;
this.chunkZ = newChunkZ;
}
}
}

View File

@ -1,11 +0,0 @@
package com.halotroop.litecraft.util;
public enum RelativeDirection
{
UP, // up up
DOWN, // down down
LEFT, // >left< right
RIGHT, // left >right<
FORWARD, // b
BACKWARD,// a
}

View File

@ -1,51 +0,0 @@
package com.halotroop.litecraft.util.noise;
import java.util.Random;
public final class OctaveSimplexNoise
{
protected SimplexNoise[] samplers;
private double clamp;
private double spread, amplitudeLow, amplitudeHigh;
public OctaveSimplexNoise(Random rand, int octaves)
{ this(rand, octaves, 1D, 1D, 1D); }
public OctaveSimplexNoise(Random rand, int octaves, double spread, double amplitudeHigh, double amplitudeLow)
{
this.samplers = new SimplexNoise[octaves];
this.clamp = 1D / (1D - (1D / Math.pow(2, octaves)));
for (int i = 0; i < octaves; ++i)
{ samplers[i] = new SimplexNoise(rand.nextLong()); }
this.spread = spread;
this.amplitudeLow = amplitudeLow;
this.amplitudeHigh = amplitudeHigh;
}
public double sample(double x, double y)
{
double amplSpread = 0.5D;
double result = 0;
for (SimplexNoise sampler : this.samplers)
{
result += (amplSpread * sampler.sample(x / (amplSpread * this.spread), y / (amplSpread * this.spread)));
amplSpread *= 0.5D;
}
result = result * this.clamp;
return result > 0 ? result * this.amplitudeHigh : result * this.amplitudeLow;
}
public double sample(double x, double y, double z)
{
double amplSpread = 0.5D;
double result = 0;
for (SimplexNoise sampler : this.samplers)
{
double divisor = amplSpread * this.spread;
result += (amplSpread * sampler.sample(x / divisor, y / divisor, z / divisor));
amplSpread *= 0.5D;
}
result = result * this.clamp;
return result > 0 ? result * this.amplitudeHigh : result * this.amplitudeLow;
}
}

View File

@ -1,854 +0,0 @@
package com.halotroop.litecraft.util.noise;
/** OpenSimplex Noise in Java.
* (Using implementation by Kurt Spencer)
* v1.1 (October 5, 2014)
* - Added 2D and 4D implementations.
* - Proper gradient sets for all dimensions, from a
* dimensionally-generalizable scheme with an actual
* rhyme and reason behind it.
* - Removed default permutation array in favor of
* default seed.
* - Changed seed-based constructor to be independent
* of any particular randomization library, so results
* will be the same when ported to other languages. */
public class SimplexNoise
{
private static final double STRETCH_CONSTANT_2D = -0.211324865405187; //(1/Math.sqrt(2+1)-1)/2;
private static final double SQUISH_CONSTANT_2D = 0.366025403784439; //(Math.sqrt(2+1)-1)/2;
private static final double STRETCH_CONSTANT_3D = -1.0 / 6; //(1/Math.sqrt(3+1)-1)/3;
private static final double SQUISH_CONSTANT_3D = 1.0 / 3; //(Math.sqrt(3+1)-1)/3;
private static final double NORM_CONSTANT_2D = 47;
private static final double NORM_CONSTANT_3D = 103;
private static final long DEFAULT_SEED = 0;
//Gradients for 2D. They approximate the directions to the
//vertices of an octagon from the center.
private static byte[] gradients2D = new byte[]
{
5, 2, 2, 5,
-5, 2, -2, 5,
5, -2, 2, -5,
-5, -2, -2, -5,
};
//Gradients for 3D. They approximate the directions to the
//vertices of a rhombicuboctahedron from the center, skewed so
//that the triangular and square facets can be inscribed inside
//circles of the same radius.
private static byte[] gradients3D = new byte[]
{
-11, 4, 4, -4, 11, 4, -4, 4, 11,
11, 4, 4, 4, 11, 4, 4, 4, 11,
-11, -4, 4, -4, -11, 4, -4, -4, 11,
11, -4, 4, 4, -11, 4, 4, -4, 11,
-11, 4, -4, -4, 11, -4, -4, 4, -11,
11, 4, -4, 4, 11, -4, 4, 4, -11,
-11, -4, -4, -4, -11, -4, -4, -4, -11,
11, -4, -4, 4, -11, -4, 4, -4, -11,
};
private short[] perm;
private short[] permGradIndex3D;
private long seed = -1L;
public SimplexNoise()
{ this(DEFAULT_SEED); }
//Initializes the class using a permutation array generated from a 64-bit seed.
//Generates a proper permutation (i.e. doesn't merely perform N successive pair swaps on a base array)
//Uses a simple 64-bit LCG.
public SimplexNoise(long seed)
{
this.seed = seed;
perm = new short[256];
permGradIndex3D = new short[256];
short[] source = new short[256];
for (short i = 0; i < 256; i++)
source[i] = i;
seed = seed * 6364136223846793005L + 1442695040888963407L;
seed = seed * 6364136223846793005L + 1442695040888963407L;
seed = seed * 6364136223846793005L + 1442695040888963407L;
for (int i = 255; i >= 0; i--)
{
seed = seed * 6364136223846793005L + 1442695040888963407L;
int r = (int) ((seed + 31) % (i + 1));
if (r < 0)
r += (i + 1);
perm[i] = source[r];
permGradIndex3D[i] = (short) ((perm[i] % (gradients3D.length / 3)) * 3);
source[r] = source[i];
}
}
private static int fastFloor(double x)
{
int xi = (int) x;
return x < xi ? xi - 1 : xi;
}
/** Function added to noise by Valoeghese to get seed. */
public long getSeed()
{ return this.seed; }
//2D OpenSimplex Noise.
public double sample(double x, double y)
{
//Place input coordinates onto grid.
double stretchOffset = (x + y) * STRETCH_CONSTANT_2D;
double xs = x + stretchOffset;
double ys = y + stretchOffset;
//Floor to get grid coordinates of rhombus (stretched square) super-cell origin.
int xsb = (int) Math.floor(xs);
int ysb = (int) Math.floor(ys);
//Skew out to get actual coordinates of rhombus origin. We'll need these later.
double squishOffset = (xsb + ysb) * SQUISH_CONSTANT_2D;
double xb = xsb + squishOffset;
double yb = ysb + squishOffset;
//Compute grid coordinates relative to rhombus origin.
double xins = xs - xsb;
double yins = ys - ysb;
//Sum those together to get a value that determines which region we're in.
double inSum = xins + yins;
//Positions relative to origin point.
double dx0 = x - xb;
double dy0 = y - yb;
//We'll be defining these inside the next block and using them afterwards.
double dx_ext, dy_ext;
int xsv_ext, ysv_ext;
double value = 0;
//Contribution (1,0)
double dx1 = dx0 - 1 - SQUISH_CONSTANT_2D;
double dy1 = dy0 - 0 - SQUISH_CONSTANT_2D;
double attn1 = 2 - dx1 * dx1 - dy1 * dy1;
if (attn1 > 0)
{
attn1 *= attn1;
value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, dx1, dy1);
}
//Contribution (0,1)
double dx2 = dx0 - 0 - SQUISH_CONSTANT_2D;
double dy2 = dy0 - 1 - SQUISH_CONSTANT_2D;
double attn2 = 2 - dx2 * dx2 - dy2 * dy2;
if (attn2 > 0)
{
attn2 *= attn2;
value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, dx2, dy2);
}
if (inSum <= 1)
{ //We're inside the triangle (2-Simplex) at (0,0)
double zins = 1 - inSum;
if (zins > xins || zins > yins)
{ //(0,0) is one of the closest two triangular vertices
if (xins > yins)
{
xsv_ext = xsb + 1;
ysv_ext = ysb - 1;
dx_ext = dx0 - 1;
dy_ext = dy0 + 1;
}
else
{
xsv_ext = xsb - 1;
ysv_ext = ysb + 1;
dx_ext = dx0 + 1;
dy_ext = dy0 - 1;
}
}
else
{ //(1,0) and (0,1) are the closest two vertices.
xsv_ext = xsb + 1;
ysv_ext = ysb + 1;
dx_ext = dx0 - 1 - 2 * SQUISH_CONSTANT_2D;
dy_ext = dy0 - 1 - 2 * SQUISH_CONSTANT_2D;
}
}
else
{ //We're inside the triangle (2-Simplex) at (1,1)
double zins = 2 - inSum;
if (zins < xins || zins < yins)
{ //(0,0) is one of the closest two triangular vertices
if (xins > yins)
{
xsv_ext = xsb + 2;
ysv_ext = ysb + 0;
dx_ext = dx0 - 2 - 2 * SQUISH_CONSTANT_2D;
dy_ext = dy0 + 0 - 2 * SQUISH_CONSTANT_2D;
}
else
{
xsv_ext = xsb + 0;
ysv_ext = ysb + 2;
dx_ext = dx0 + 0 - 2 * SQUISH_CONSTANT_2D;
dy_ext = dy0 - 2 - 2 * SQUISH_CONSTANT_2D;
}
}
else
{ //(1,0) and (0,1) are the closest two vertices.
dx_ext = dx0;
dy_ext = dy0;
xsv_ext = xsb;
ysv_ext = ysb;
}
xsb += 1;
ysb += 1;
dx0 = dx0 - 1 - 2 * SQUISH_CONSTANT_2D;
dy0 = dy0 - 1 - 2 * SQUISH_CONSTANT_2D;
}
//Contribution (0,0) or (1,1)
double attn0 = 2 - dx0 * dx0 - dy0 * dy0;
if (attn0 > 0)
{
attn0 *= attn0;
value += attn0 * attn0 * extrapolate(xsb, ysb, dx0, dy0);
}
//Extra Vertex
double attn_ext = 2 - dx_ext * dx_ext - dy_ext * dy_ext;
if (attn_ext > 0)
{
attn_ext *= attn_ext;
value += attn_ext * attn_ext * extrapolate(xsv_ext, ysv_ext, dx_ext, dy_ext);
}
return value / NORM_CONSTANT_2D;
}
//3D OpenSimplex Noise.
public double sample(double x, double y, double z)
{
//Place input coordinates on simplectic honeycomb.
double stretchOffset = (x + y + z) * STRETCH_CONSTANT_3D;
double xs = x + stretchOffset;
double ys = y + stretchOffset;
double zs = z + stretchOffset;
//Floor to get simplectic honeycomb coordinates of rhombohedron (stretched cube) super-cell origin.
int xsb = fastFloor(xs);
int ysb = fastFloor(ys);
int zsb = fastFloor(zs);
//Skew out to get actual coordinates of rhombohedron origin. We'll need these later.
double squishOffset = (xsb + ysb + zsb) * SQUISH_CONSTANT_3D;
double xb = xsb + squishOffset;
double yb = ysb + squishOffset;
double zb = zsb + squishOffset;
//Compute simplectic honeycomb coordinates relative to rhombohedral origin.
double xins = xs - xsb;
double yins = ys - ysb;
double zins = zs - zsb;
//Sum those together to get a value that determines which region we're in.
double inSum = xins + yins + zins;
//Positions relative to origin point.
double dx0 = x - xb;
double dy0 = y - yb;
double dz0 = z - zb;
//We'll be defining these inside the next block and using them afterwards.
double dx_ext0, dy_ext0, dz_ext0;
double dx_ext1, dy_ext1, dz_ext1;
int xsv_ext0, ysv_ext0, zsv_ext0;
int xsv_ext1, ysv_ext1, zsv_ext1;
double value = 0;
if (inSum <= 1)
{ //We're inside the tetrahedron (3-Simplex) at (0,0,0)
//Determine which two of (0,0,1), (0,1,0), (1,0,0) are closest.
byte aPoint = 0x01;
double aScore = xins;
byte bPoint = 0x02;
double bScore = yins;
if (aScore >= bScore && zins > bScore)
{
bScore = zins;
bPoint = 0x04;
}
else if (aScore < bScore && zins > aScore)
{
aScore = zins;
aPoint = 0x04;
}
//Now we determine the two lattice points not part of the tetrahedron that may contribute.
//This depends on the closest two tetrahedral vertices, including (0,0,0)
double wins = 1 - inSum;
if (wins > aScore || wins > bScore)
{ //(0,0,0) is one of the closest two tetrahedral vertices.
byte c = (bScore > aScore ? bPoint : aPoint); //Our other closest vertex is the closest out of a and b.
if ((c & 0x01) == 0)
{
xsv_ext0 = xsb - 1;
xsv_ext1 = xsb;
dx_ext0 = dx0 + 1;
dx_ext1 = dx0;
}
else
{
xsv_ext0 = xsv_ext1 = xsb + 1;
dx_ext0 = dx_ext1 = dx0 - 1;
}
if ((c & 0x02) == 0)
{
ysv_ext0 = ysv_ext1 = ysb;
dy_ext0 = dy_ext1 = dy0;
if ((c & 0x01) == 0)
{
ysv_ext1 -= 1;
dy_ext1 += 1;
}
else
{
ysv_ext0 -= 1;
dy_ext0 += 1;
}
}
else
{
ysv_ext0 = ysv_ext1 = ysb + 1;
dy_ext0 = dy_ext1 = dy0 - 1;
}
if ((c & 0x04) == 0)
{
zsv_ext0 = zsb;
zsv_ext1 = zsb - 1;
dz_ext0 = dz0;
dz_ext1 = dz0 + 1;
}
else
{
zsv_ext0 = zsv_ext1 = zsb + 1;
dz_ext0 = dz_ext1 = dz0 - 1;
}
}
else
{ //(0,0,0) is not one of the closest two tetrahedral vertices.
byte c = (byte) (aPoint | bPoint); //Our two extra vertices are determined by the closest two.
if ((c & 0x01) == 0)
{
xsv_ext0 = xsb;
xsv_ext1 = xsb - 1;
dx_ext0 = dx0 - 2 * SQUISH_CONSTANT_3D;
dx_ext1 = dx0 + 1 - SQUISH_CONSTANT_3D;
}
else
{
xsv_ext0 = xsv_ext1 = xsb + 1;
dx_ext0 = dx0 - 1 - 2 * SQUISH_CONSTANT_3D;
dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_3D;
}
if ((c & 0x02) == 0)
{
ysv_ext0 = ysb;
ysv_ext1 = ysb - 1;
dy_ext0 = dy0 - 2 * SQUISH_CONSTANT_3D;
dy_ext1 = dy0 + 1 - SQUISH_CONSTANT_3D;
}
else
{
ysv_ext0 = ysv_ext1 = ysb + 1;
dy_ext0 = dy0 - 1 - 2 * SQUISH_CONSTANT_3D;
dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_3D;
}
if ((c & 0x04) == 0)
{
zsv_ext0 = zsb;
zsv_ext1 = zsb - 1;
dz_ext0 = dz0 - 2 * SQUISH_CONSTANT_3D;
dz_ext1 = dz0 + 1 - SQUISH_CONSTANT_3D;
}
else
{
zsv_ext0 = zsv_ext1 = zsb + 1;
dz_ext0 = dz0 - 1 - 2 * SQUISH_CONSTANT_3D;
dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_3D;
}
}
//Contribution (0,0,0)
double attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0;
if (attn0 > 0)
{
attn0 *= attn0;
value += attn0 * attn0 * extrapolate(xsb + 0, ysb + 0, zsb + 0, dx0, dy0, dz0);
}
//Contribution (1,0,0)
double dx1 = dx0 - 1 - SQUISH_CONSTANT_3D;
double dy1 = dy0 - 0 - SQUISH_CONSTANT_3D;
double dz1 = dz0 - 0 - SQUISH_CONSTANT_3D;
double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1;
if (attn1 > 0)
{
attn1 *= attn1;
value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, zsb + 0, dx1, dy1, dz1);
}
//Contribution (0,1,0)
double dx2 = dx0 - 0 - SQUISH_CONSTANT_3D;
double dy2 = dy0 - 1 - SQUISH_CONSTANT_3D;
double dz2 = dz1;
double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2;
if (attn2 > 0)
{
attn2 *= attn2;
value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, zsb + 0, dx2, dy2, dz2);
}
//Contribution (0,0,1)
double dx3 = dx2;
double dy3 = dy1;
double dz3 = dz0 - 1 - SQUISH_CONSTANT_3D;
double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3;
if (attn3 > 0)
{
attn3 *= attn3;
value += attn3 * attn3 * extrapolate(xsb + 0, ysb + 0, zsb + 1, dx3, dy3, dz3);
}
}
else if (inSum >= 2)
{ //We're inside the tetrahedron (3-Simplex) at (1,1,1)
//Determine which two tetrahedral vertices are the closest, out of (1,1,0), (1,0,1), (0,1,1) but not (1,1,1).
byte aPoint = 0x06;
double aScore = xins;
byte bPoint = 0x05;
double bScore = yins;
if (aScore <= bScore && zins < bScore)
{
bScore = zins;
bPoint = 0x03;
}
else if (aScore > bScore && zins < aScore)
{
aScore = zins;
aPoint = 0x03;
}
//Now we determine the two lattice points not part of the tetrahedron that may contribute.
//This depends on the closest two tetrahedral vertices, including (1,1,1)
double wins = 3 - inSum;
if (wins < aScore || wins < bScore)
{ //(1,1,1) is one of the closest two tetrahedral vertices.
byte c = (bScore < aScore ? bPoint : aPoint); //Our other closest vertex is the closest out of a and b.
if ((c & 0x01) != 0)
{
xsv_ext0 = xsb + 2;
xsv_ext1 = xsb + 1;
dx_ext0 = dx0 - 2 - 3 * SQUISH_CONSTANT_3D;
dx_ext1 = dx0 - 1 - 3 * SQUISH_CONSTANT_3D;
}
else
{
xsv_ext0 = xsv_ext1 = xsb;
dx_ext0 = dx_ext1 = dx0 - 3 * SQUISH_CONSTANT_3D;
}
if ((c & 0x02) != 0)
{
ysv_ext0 = ysv_ext1 = ysb + 1;
dy_ext0 = dy_ext1 = dy0 - 1 - 3 * SQUISH_CONSTANT_3D;
if ((c & 0x01) != 0)
{
ysv_ext1 += 1;
dy_ext1 -= 1;
}
else
{
ysv_ext0 += 1;
dy_ext0 -= 1;
}
}
else
{
ysv_ext0 = ysv_ext1 = ysb;
dy_ext0 = dy_ext1 = dy0 - 3 * SQUISH_CONSTANT_3D;
}
if ((c & 0x04) != 0)
{
zsv_ext0 = zsb + 1;
zsv_ext1 = zsb + 2;
dz_ext0 = dz0 - 1 - 3 * SQUISH_CONSTANT_3D;
dz_ext1 = dz0 - 2 - 3 * SQUISH_CONSTANT_3D;
}
else
{
zsv_ext0 = zsv_ext1 = zsb;
dz_ext0 = dz_ext1 = dz0 - 3 * SQUISH_CONSTANT_3D;
}
}
else
{ //(1,1,1) is not one of the closest two tetrahedral vertices.
byte c = (byte) (aPoint & bPoint); //Our two extra vertices are determined by the closest two.
if ((c & 0x01) != 0)
{
xsv_ext0 = xsb + 1;
xsv_ext1 = xsb + 2;
dx_ext0 = dx0 - 1 - SQUISH_CONSTANT_3D;
dx_ext1 = dx0 - 2 - 2 * SQUISH_CONSTANT_3D;
}
else
{
xsv_ext0 = xsv_ext1 = xsb;
dx_ext0 = dx0 - SQUISH_CONSTANT_3D;
dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_3D;
}
if ((c & 0x02) != 0)
{
ysv_ext0 = ysb + 1;
ysv_ext1 = ysb + 2;
dy_ext0 = dy0 - 1 - SQUISH_CONSTANT_3D;
dy_ext1 = dy0 - 2 - 2 * SQUISH_CONSTANT_3D;
}
else
{
ysv_ext0 = ysv_ext1 = ysb;
dy_ext0 = dy0 - SQUISH_CONSTANT_3D;
dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_3D;
}
if ((c & 0x04) != 0)
{
zsv_ext0 = zsb + 1;
zsv_ext1 = zsb + 2;
dz_ext0 = dz0 - 1 - SQUISH_CONSTANT_3D;
dz_ext1 = dz0 - 2 - 2 * SQUISH_CONSTANT_3D;
}
else
{
zsv_ext0 = zsv_ext1 = zsb;
dz_ext0 = dz0 - SQUISH_CONSTANT_3D;
dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_3D;
}
}
//Contribution (1,1,0)
double dx3 = dx0 - 1 - 2 * SQUISH_CONSTANT_3D;
double dy3 = dy0 - 1 - 2 * SQUISH_CONSTANT_3D;
double dz3 = dz0 - 0 - 2 * SQUISH_CONSTANT_3D;
double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3;
if (attn3 > 0)
{
attn3 *= attn3;
value += attn3 * attn3 * extrapolate(xsb + 1, ysb + 1, zsb + 0, dx3, dy3, dz3);
}
//Contribution (1,0,1)
double dx2 = dx3;
double dy2 = dy0 - 0 - 2 * SQUISH_CONSTANT_3D;
double dz2 = dz0 - 1 - 2 * SQUISH_CONSTANT_3D;
double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2;
if (attn2 > 0)
{
attn2 *= attn2;
value += attn2 * attn2 * extrapolate(xsb + 1, ysb + 0, zsb + 1, dx2, dy2, dz2);
}
//Contribution (0,1,1)
double dx1 = dx0 - 0 - 2 * SQUISH_CONSTANT_3D;
double dy1 = dy3;
double dz1 = dz2;
double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1;
if (attn1 > 0)
{
attn1 *= attn1;
value += attn1 * attn1 * extrapolate(xsb + 0, ysb + 1, zsb + 1, dx1, dy1, dz1);
}
//Contribution (1,1,1)
dx0 = dx0 - 1 - 3 * SQUISH_CONSTANT_3D;
dy0 = dy0 - 1 - 3 * SQUISH_CONSTANT_3D;
dz0 = dz0 - 1 - 3 * SQUISH_CONSTANT_3D;
double attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0;
if (attn0 > 0)
{
attn0 *= attn0;
value += attn0 * attn0 * extrapolate(xsb + 1, ysb + 1, zsb + 1, dx0, dy0, dz0);
}
}
else
{ //We're inside the octahedron (Rectified 3-Simplex) in between.
double aScore;
byte aPoint;
boolean aIsFurtherSide;
double bScore;
byte bPoint;
boolean bIsFurtherSide;
//Decide between point (0,0,1) and (1,1,0) as closest
double p1 = xins + yins;
if (p1 > 1)
{
aScore = p1 - 1;
aPoint = 0x03;
aIsFurtherSide = true;
}
else
{
aScore = 1 - p1;
aPoint = 0x04;
aIsFurtherSide = false;
}
//Decide between point (0,1,0) and (1,0,1) as closest
double p2 = xins + zins;
if (p2 > 1)
{
bScore = p2 - 1;
bPoint = 0x05;
bIsFurtherSide = true;
}
else
{
bScore = 1 - p2;
bPoint = 0x02;
bIsFurtherSide = false;
}
//The closest out of the two (1,0,0) and (0,1,1) will replace the furthest out of the two decided above, if closer.
double p3 = yins + zins;
if (p3 > 1)
{
double score = p3 - 1;
if (aScore <= bScore && aScore < score)
{
aScore = score;
aPoint = 0x06;
aIsFurtherSide = true;
}
else if (aScore > bScore && bScore < score)
{
bScore = score;
bPoint = 0x06;
bIsFurtherSide = true;
}
}
else
{
double score = 1 - p3;
if (aScore <= bScore && aScore < score)
{
aScore = score;
aPoint = 0x01;
aIsFurtherSide = false;
}
else if (aScore > bScore && bScore < score)
{
bScore = score;
bPoint = 0x01;
bIsFurtherSide = false;
}
}
//Where each of the two closest points are determines how the extra two vertices are calculated.
if (aIsFurtherSide == bIsFurtherSide)
{
if (aIsFurtherSide)
{ //Both closest points on (1,1,1) side
//One of the two extra points is (1,1,1)
dx_ext0 = dx0 - 1 - 3 * SQUISH_CONSTANT_3D;
dy_ext0 = dy0 - 1 - 3 * SQUISH_CONSTANT_3D;
dz_ext0 = dz0 - 1 - 3 * SQUISH_CONSTANT_3D;
xsv_ext0 = xsb + 1;
ysv_ext0 = ysb + 1;
zsv_ext0 = zsb + 1;
//Other extra point is based on the shared axis.
byte c = (byte) (aPoint & bPoint);
if ((c & 0x01) != 0)
{
dx_ext1 = dx0 - 2 - 2 * SQUISH_CONSTANT_3D;
dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_3D;
dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_3D;
xsv_ext1 = xsb + 2;
ysv_ext1 = ysb;
zsv_ext1 = zsb;
}
else if ((c & 0x02) != 0)
{
dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_3D;
dy_ext1 = dy0 - 2 - 2 * SQUISH_CONSTANT_3D;
dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_3D;
xsv_ext1 = xsb;
ysv_ext1 = ysb + 2;
zsv_ext1 = zsb;
}
else
{
dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_3D;
dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_3D;
dz_ext1 = dz0 - 2 - 2 * SQUISH_CONSTANT_3D;
xsv_ext1 = xsb;
ysv_ext1 = ysb;
zsv_ext1 = zsb + 2;
}
}
else
{//Both closest points on (0,0,0) side
//One of the two extra points is (0,0,0)
dx_ext0 = dx0;
dy_ext0 = dy0;
dz_ext0 = dz0;
xsv_ext0 = xsb;
ysv_ext0 = ysb;
zsv_ext0 = zsb;
//Other extra point is based on the omitted axis.
byte c = (byte) (aPoint | bPoint);
if ((c & 0x01) == 0)
{
dx_ext1 = dx0 + 1 - SQUISH_CONSTANT_3D;
dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_3D;
dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_3D;
xsv_ext1 = xsb - 1;
ysv_ext1 = ysb + 1;
zsv_ext1 = zsb + 1;
}
else if ((c & 0x02) == 0)
{
dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_3D;
dy_ext1 = dy0 + 1 - SQUISH_CONSTANT_3D;
dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_3D;
xsv_ext1 = xsb + 1;
ysv_ext1 = ysb - 1;
zsv_ext1 = zsb + 1;
}
else
{
dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_3D;
dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_3D;
dz_ext1 = dz0 + 1 - SQUISH_CONSTANT_3D;
xsv_ext1 = xsb + 1;
ysv_ext1 = ysb + 1;
zsv_ext1 = zsb - 1;
}
}
}
else
{ //One point on (0,0,0) side, one point on (1,1,1) side
byte c1, c2;
if (aIsFurtherSide)
{
c1 = aPoint;
c2 = bPoint;
}
else
{
c1 = bPoint;
c2 = aPoint;
}
//One contribution is a permutation of (1,1,-1)
if ((c1 & 0x01) == 0)
{
dx_ext0 = dx0 + 1 - SQUISH_CONSTANT_3D;
dy_ext0 = dy0 - 1 - SQUISH_CONSTANT_3D;
dz_ext0 = dz0 - 1 - SQUISH_CONSTANT_3D;
xsv_ext0 = xsb - 1;
ysv_ext0 = ysb + 1;
zsv_ext0 = zsb + 1;
}
else if ((c1 & 0x02) == 0)
{
dx_ext0 = dx0 - 1 - SQUISH_CONSTANT_3D;
dy_ext0 = dy0 + 1 - SQUISH_CONSTANT_3D;
dz_ext0 = dz0 - 1 - SQUISH_CONSTANT_3D;
xsv_ext0 = xsb + 1;
ysv_ext0 = ysb - 1;
zsv_ext0 = zsb + 1;
}
else
{
dx_ext0 = dx0 - 1 - SQUISH_CONSTANT_3D;
dy_ext0 = dy0 - 1 - SQUISH_CONSTANT_3D;
dz_ext0 = dz0 + 1 - SQUISH_CONSTANT_3D;
xsv_ext0 = xsb + 1;
ysv_ext0 = ysb + 1;
zsv_ext0 = zsb - 1;
}
//One contribution is a permutation of (0,0,2)
dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_3D;
dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_3D;
dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_3D;
xsv_ext1 = xsb;
ysv_ext1 = ysb;
zsv_ext1 = zsb;
if ((c2 & 0x01) != 0)
{
dx_ext1 -= 2;
xsv_ext1 += 2;
}
else if ((c2 & 0x02) != 0)
{
dy_ext1 -= 2;
ysv_ext1 += 2;
}
else
{
dz_ext1 -= 2;
zsv_ext1 += 2;
}
}
//Contribution (1,0,0)
double dx1 = dx0 - 1 - SQUISH_CONSTANT_3D;
double dy1 = dy0 - 0 - SQUISH_CONSTANT_3D;
double dz1 = dz0 - 0 - SQUISH_CONSTANT_3D;
double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1;
if (attn1 > 0)
{
attn1 *= attn1;
value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, zsb + 0, dx1, dy1, dz1);
}
//Contribution (0,1,0)
double dx2 = dx0 - 0 - SQUISH_CONSTANT_3D;
double dy2 = dy0 - 1 - SQUISH_CONSTANT_3D;
double dz2 = dz1;
double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2;
if (attn2 > 0)
{
attn2 *= attn2;
value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, zsb + 0, dx2, dy2, dz2);
}
//Contribution (0,0,1)
double dx3 = dx2;
double dy3 = dy1;
double dz3 = dz0 - 1 - SQUISH_CONSTANT_3D;
double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3;
if (attn3 > 0)
{
attn3 *= attn3;
value += attn3 * attn3 * extrapolate(xsb + 0, ysb + 0, zsb + 1, dx3, dy3, dz3);
}
//Contribution (1,1,0)
double dx4 = dx0 - 1 - 2 * SQUISH_CONSTANT_3D;
double dy4 = dy0 - 1 - 2 * SQUISH_CONSTANT_3D;
double dz4 = dz0 - 0 - 2 * SQUISH_CONSTANT_3D;
double attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4;
if (attn4 > 0)
{
attn4 *= attn4;
value += attn4 * attn4 * extrapolate(xsb + 1, ysb + 1, zsb + 0, dx4, dy4, dz4);
}
//Contribution (1,0,1)
double dx5 = dx4;
double dy5 = dy0 - 0 - 2 * SQUISH_CONSTANT_3D;
double dz5 = dz0 - 1 - 2 * SQUISH_CONSTANT_3D;
double attn5 = 2 - dx5 * dx5 - dy5 * dy5 - dz5 * dz5;
if (attn5 > 0)
{
attn5 *= attn5;
value += attn5 * attn5 * extrapolate(xsb + 1, ysb + 0, zsb + 1, dx5, dy5, dz5);
}
//Contribution (0,1,1)
double dx6 = dx0 - 0 - 2 * SQUISH_CONSTANT_3D;
double dy6 = dy4;
double dz6 = dz5;
double attn6 = 2 - dx6 * dx6 - dy6 * dy6 - dz6 * dz6;
if (attn6 > 0)
{
attn6 *= attn6;
value += attn6 * attn6 * extrapolate(xsb + 0, ysb + 1, zsb + 1, dx6, dy6, dz6);
}
}
//First extra vertex
double attn_ext0 = 2 - dx_ext0 * dx_ext0 - dy_ext0 * dy_ext0 - dz_ext0 * dz_ext0;
if (attn_ext0 > 0)
{
attn_ext0 *= attn_ext0;
value += attn_ext0 * attn_ext0 * extrapolate(xsv_ext0, ysv_ext0, zsv_ext0, dx_ext0, dy_ext0, dz_ext0);
}
//Second extra vertex
double attn_ext1 = 2 - dx_ext1 * dx_ext1 - dy_ext1 * dy_ext1 - dz_ext1 * dz_ext1;
if (attn_ext1 > 0)
{
attn_ext1 *= attn_ext1;
value += attn_ext1 * attn_ext1 * extrapolate(xsv_ext1, ysv_ext1, zsv_ext1, dx_ext1, dy_ext1, dz_ext1);
}
return value / NORM_CONSTANT_3D;
}
private double extrapolate(int xsb, int ysb, double dx, double dy)
{
int index = perm[(perm[xsb & 0xFF] + ysb) & 0xFF] & 0x0E;
return gradients2D[index] * dx
+ gradients2D[index + 1] * dy;
}
private double extrapolate(int xsb, int ysb, int zsb, double dx, double dy, double dz)
{
int index = permGradIndex3D[(perm[(perm[xsb & 0xFF] + ysb) & 0xFF] + zsb) & 0xFF];
return gradients3D[index] * dx
+ gradients3D[index + 1] * dy
+ gradients3D[index + 2] * dz;
}
}

View File

@ -1,10 +0,0 @@
package com.halotroop.litecraft.world;
import com.halotroop.litecraft.types.block.Block;
public interface BlockAccess
{
Block getBlock(int x, int y, int z);
void setBlock(int x, int y, int z, Block block);
}

View File

@ -1,245 +0,0 @@
package com.halotroop.litecraft.world;
import java.util.*;
import java.util.function.ToIntFunction;
import org.joml.Vector3f;
import com.halotroop.litecraft.logic.SODSerializable;
import com.halotroop.litecraft.render.BlockRenderer;
import com.halotroop.litecraft.types.block.*;
import com.halotroop.litecraft.world.gen.WorldGenConstants;
import it.unimi.dsi.fastutil.ints.*;
import it.unimi.dsi.fastutil.objects.*;
import tk.valoeghese.sod.*;
public class Chunk implements BlockAccess, WorldGenConstants, SODSerializable
{
/** @param x in-chunk x coordinate.
* @param y in-chunk y coordinate.
* @param z in-chunk z coordinate.
* @return creates a long that represents a coordinate, for use as a key in arrays. */
public static int index(int x, int y, int z)
{ return (x & MAX_POS) | ((y & MAX_POS) << POS_SHIFT) | ((z & MAX_POS) << DOUBLE_SHIFT); }
private final Block[] blocks = new Block[CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE];
private BlockInstance[] blockInstances = new BlockInstance[CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE];
private boolean shouldRender = false;
public final int chunkX, chunkY, chunkZ;
public final int chunkStartX, chunkStartY, chunkStartZ;
private boolean fullyGenerated = false;
public final int dimension;
private boolean dirty = true;
/** A holder for the rendered blocks in this chunk. This array is *NOT* safe to use for getting BIs at a position!
* It can vary in size from 0 to 512 elements long and must only be read linearly. */
private BlockInstance[] renderedBlocks = new BlockInstance[CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE];
public Chunk(World world, int chunkX, int chunkY, int chunkZ, int dimension)
{
this.chunkX = chunkX;
this.chunkY = chunkY;
this.chunkZ = chunkZ;
this.chunkStartX = chunkX << POS_SHIFT;
this.chunkStartY = chunkY << POS_SHIFT;
this.chunkStartZ = chunkZ << POS_SHIFT;
this.dimension = dimension;
}
public boolean doRender()
{ return this.shouldRender; }
public void setFullyGenerated(boolean fullyGenerated)
{ this.fullyGenerated = fullyGenerated; }
@Override
public Block getBlock(int x, int y, int z)
{
if (x > CHUNK_SIZE || y > CHUNK_SIZE || z > CHUNK_SIZE || x < 0 || y < 0 || z < 0)
{ throw new RuntimeException("Block [" + x + ", " + y + ", " + z + "] out of chunk bounds!"); }
return blocks[index(x, y, z)];
}
public BlockInstance getBlockInstance(int x, int y, int z)
{
if (x > CHUNK_SIZE || y > CHUNK_SIZE || z > CHUNK_SIZE || x < 0 || y < 0 || z < 0)
{ throw new RuntimeException("BlockInstance [" + x + ", " + y + ", " + z + "] out of chunk bounds!"); }
return this.blockInstances[index(x, y, z)];
}
public void render(BlockRenderer blockRenderer)
{
if (shouldRender)
{
if (dirty)
{
dirty = false;
List<BlockInstance> tempList = new ArrayList<>();
Arrays.fill(renderedBlocks, null);
for (int x = 0; x < CHUNK_SIZE; x++)
for (int y = 0; y < CHUNK_SIZE; y++)
for (int z = 0; z < CHUNK_SIZE; z++)
{
BlockInstance block = getBlockInstance(x, y, z);
// Check for chunk edges to avoid errors when get the neighboring blocks, TODO fix this
if (x == 0 || x == CHUNK_SIZE - 1 || z == 0 || z == CHUNK_SIZE - 1 || y == 0 || y == CHUNK_SIZE - 1)
{
tempList.add(block);
continue;
}
// Check for air. Yes this is stupid, TODO fix this too
try
{
if (getBlockInstance(x - 1, y, z).getModel() == null || getBlockInstance(x + 1, y, z).getModel() == null ||
getBlockInstance(x, y - 1, z).getModel() == null || getBlockInstance(x, y + 1, z).getModel() == null ||
getBlockInstance(x, y, z - 1).getModel() == null || getBlockInstance(x, y, z + 1).getModel() == null)
{ tempList.add(block); }
}
catch (NullPointerException e)
{ // this seems to be a hotspot for errors
e.printStackTrace(); // so I can add a debug breakpoint on this line
throw e; // e
}
}
renderedBlocks = tempList.toArray(BlockInstance[]::new);
}
blockRenderer.prepareRender();
blockRenderer.render(renderedBlocks);
blockRenderer.shader.stop();
}
}
/** Change the block in this exact position
*
* @param x, y, z The coordinate position of block to overwrite
* @param block The block to place there */
@Override
public void setBlock(int x, int y, int z, Block block)
{
// This section makes sure the blocks don't go out of range
if (x > MAX_POS)
x = MAX_POS;
else if (x < 0) x = 0;
if (y > MAX_POS)
y = MAX_POS;
else if (y < 0) y = 0;
if (z > MAX_POS)
z = MAX_POS;
else if (z < 0) z = 0;
//
this.blocks[index(x, y, z)] = block;
if (this.shouldRender) this.blockInstances[index(x, y, z)] = new BlockInstance(block, new Vector3f(this.chunkStartX + x, this.chunkStartY + y, this.chunkStartZ + z));
dirty = true;
}
/** Set whether or not the chunk should render */
public void setRender(boolean render)
{
if (render && !this.shouldRender) // if it has been changed to true
for (int x = 0; x < CHUNK_SIZE; ++x)
for (int y = 0; y < CHUNK_SIZE; ++y)
for (int z = 0; z < CHUNK_SIZE; ++z)
{
Block block = this.blocks[index(x, y, z)];
this.blockInstances[index(x, y, z)] = new BlockInstance(block,
new Vector3f(
this.chunkStartX + x,
this.chunkStartY + y,
this.chunkStartZ + z));
}
else if (!render && this.shouldRender) // else if it has been changed to false.
// we need to check both variables because there are two cases that make
// the if statement fall to here
blockInstances = new BlockInstance[CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE];
this.shouldRender = render;
dirty = true;
}
public boolean isFullyGenerated()
{ return this.fullyGenerated; }
@Override
public void read(BinaryData data)
{
Int2ObjectMap<Block> palette = new Int2ObjectArrayMap<>();
DataSection paletteData = data.get("palette");
boolean readInt = true; // whether the thing from the palette to be read is int
int intIdCache = 0;
//
for (Object o : paletteData)
{
if (readInt)
{
intIdCache = (int) o;
readInt = false;
}
else
{
palette.put(intIdCache, Block.getBlockOrAir((String) o));
readInt = true;
}
}
//
IntArrayDataSection blockData = data.getIntArray("block");
int index = 0;
// Iterate over each block in the chunk
for (int z = 0; z < CHUNK_SIZE; ++z) // z, y, x order for data saving and loading so we can use incremental pos hashes
for (int y = 0; y < CHUNK_SIZE; ++y)
for (int x = 0; x < CHUNK_SIZE; ++x)
{
blocks[index] = palette.get(blockData.readInt(index));
++index;
}
//
DataSection properties = data.get("properties");
try
{
this.fullyGenerated = properties.readBoolean(0); // index 0 is the "fully generated" property
}
catch (Throwable e)
{
if (!readExceptionNotif)
{
System.out.println("An exception occurred reading properties for a chunk! This could be a benign error due to updates to chunk properties.");
readExceptionNotif = true;
}
}
}
private static boolean readExceptionNotif = false;
private int nextId; // for saving
@Override
public void write(BinaryData data)
{
Object2IntMap<Block> palette = new Object2IntArrayMap<>(); // block to int id
DataSection paletteData = new DataSection();
IntArrayDataSection blockData = new IntArrayDataSection();
int index = 0;
nextId = 0;
ToIntFunction<Block> nextIdProvider = b -> nextId++;
//
for (int z = 0; z < CHUNK_SIZE; ++z) // z, y, x order for data saving and loading so we can use incremental pos hashes
for (int y = 0; y < CHUNK_SIZE; ++y)
for (int x = 0; x < CHUNK_SIZE; ++x)
{
Block b = blocks[index];
blockData.writeInt(palette.computeIntIfAbsent(b, nextIdProvider));
++index;
}
//
palette.forEach((b, id) ->
{
paletteData.writeInt(id);
paletteData.writeString(b.identifier);
});
//
data.put("palette", paletteData);
data.put("block", blockData);
//
DataSection properties = new DataSection();
properties.writeBoolean(this.fullyGenerated);
data.put("properties", properties);
dirty = true;
}
}

View File

@ -1,253 +0,0 @@
package com.halotroop.litecraft.world;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.LongConsumer;
import org.joml.Vector3f;
import com.halotroop.litecraft.Litecraft;
import com.halotroop.litecraft.render.BlockRenderer;
import com.halotroop.litecraft.save.LitecraftSave;
import com.halotroop.litecraft.types.block.*;
import com.halotroop.litecraft.types.entity.PlayerEntity;
import com.halotroop.litecraft.world.dimension.Dimension;
import com.halotroop.litecraft.world.gen.*;
import com.halotroop.litecraft.world.gen.modifier.WorldModifier;
import it.unimi.dsi.fastutil.longs.*;
public class World implements BlockAccess, WorldGenConstants
{
Long2ObjectMap<Chunk> chunks;
private final WorldModifier[] worldModifiers;
private final ChunkGenerator chunkGenerator;
private final BlockAccess genBlockAccess;
private final LitecraftSave save;
private final long seed;
private final int dimension;
private final ForkJoinPool threadPool;
public PlayerEntity playerEntity;
int renderBound;
int renderBoundVertical;
// dummy block instance for retrieving the default block model
private final BlockInstance dummy;
public World(long seed, int renderSize, Dimension<?> dim, LitecraftSave save)
{
this.threadPool = new ForkJoinPool(4, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true);
this.dummy = new BlockInstance(Blocks.ANDESITE, new Vector3f(0, 0, 0));
this.dummy.setVisible(false);
this.chunks = new Long2ObjectArrayMap<>();
this.seed = seed;
this.chunkGenerator = dim.createChunkGenerator(seed);
this.worldModifiers = dim.getWorldModifierArray();
// initialize world modifiers with seed
for (WorldModifier modifier : this.worldModifiers)
{ modifier.initialize(seed); }
this.genBlockAccess = new GenerationWorld(this);
this.save = save;
this.dimension = dim.id;
this.renderBound = renderSize / 2;
this.renderBoundVertical = this.renderBound / 2;
if (this.renderBoundVertical < 2)
{ this.renderBoundVertical = 2; }
}
public int findAir(int x, int z)
{
int y = SEA_LEVEL;
int attemptsRemaining = 255;
while (attemptsRemaining-- > 0)
{
// DO NOT CHANGE TO y++
if (this.getBlock(x, ++y, z) == Blocks.AIR)
return y;
}
return -1; // if it fails, returns -1
}
public void spawnPlayer()
{
int y = this.findAir(0, 0);
if (y == -1)
y = 300; // yeet
this.spawnPlayer(0, y, -3);
}
public PlayerEntity spawnPlayer(float x, float y, float z)
{
this.playerEntity = (PlayerEntity) Litecraft.getInstance().player;
this.playerEntity.setVisible(false);
// Generate world around player
long time = System.currentTimeMillis();
System.out.println("Generating world!");
this.updateLoadedChunks(this.playerEntity.getChunkX(), this.playerEntity.getChunkY(), this.playerEntity.getChunkZ());
System.out.println("Generated world in " + (System.currentTimeMillis() - time) + " milliseconds");
// return player
return this.playerEntity;
}
public Chunk getChunk(int chunkX, int chunkY, int chunkZ)
{
Chunk chunk = this.chunks.computeIfAbsent(posHash(chunkX, chunkY, chunkZ), pos ->
{
Chunk readChunk = save.readChunk(this, chunkX, chunkY, chunkZ, this.dimension);
return readChunk == null ? this.chunkGenerator.generateChunk(this, chunkX, chunkY, chunkZ) : readChunk;
});
if (chunk.isFullyGenerated()) return chunk;
this.populateChunk(chunkX, chunkY, chunkZ, chunk.chunkStartX, chunk.chunkStartY, chunk.chunkStartZ);
chunk.setFullyGenerated(true);
return chunk;
}
public Chunk getChunkToLoad(int chunkX, int chunkY, int chunkZ)
{
long posHash = posHash(chunkX, chunkY, chunkZ);
// try get an already loaded chunk
Chunk result = this.chunks.get(posHash);
if (result != null)
return result;
// try read a chunk from memory
result = save.readChunk(this, chunkX, chunkY, chunkZ, this.dimension);
// if neither of those work, generate the chunk
result = result == null ? this.chunkGenerator.generateChunk(this, chunkX, chunkY, chunkZ) : result;
// add chunk to array
this.chunks.put(posHash, result);
return result;
}
/** @return whether the chunk was unloaded without errors. Will often, but not always, be equal to whether the chunk was already in memory. */
boolean unloadChunk(long posHash)
{
Chunk chunk = this.chunks.get(posHash);
// If the chunk is not in memory, it does not need to be unloaded
if (chunk == null) return false;
// Otherwise save the chunk
AtomicBoolean result = new AtomicBoolean(false);
CompletableFuture.runAsync(() ->
{
result.set(this.save.saveChunk(chunk));
this.chunks.remove(posHash);
}, threadPool);
return result.get();
}
void populateChunk(Chunk chunk)
{ this.populateChunk(chunk.chunkX, chunk.chunkY, chunk.chunkZ, chunk.chunkStartX, chunk.chunkStartY, chunk.chunkStartZ); }
private void populateChunk(int chunkX, int chunkY, int chunkZ, int chunkStartX, int chunkStartY, int chunkStartZ)
{
Random rand = new Random(this.seed + 5828671L * chunkX + -47245139L * chunkY + 8972357 * (long) chunkZ);
for (WorldModifier modifier : this.worldModifiers)
{ modifier.modifyWorld(this.genBlockAccess, rand, chunkStartX, chunkStartY, chunkStartZ); }
}
/** @return a chunk that has not neccesarily gone through chunk populating. Used in chunk populating to prevent infinite recursion. */
Chunk getGenChunk(int chunkX, int chunkY, int chunkZ)
{ return this.chunks.computeIfAbsent(posHash(chunkX, chunkY, chunkZ), pos -> this.chunkGenerator.generateChunk(this, chunkX, chunkY, chunkZ)); }
long posHash(int chunkX, int chunkY, int chunkZ)
{ return ((long) chunkX & 0x3FF) | (((long) chunkY & 0x3FF) << 10) | (((long) chunkZ & 0x3FF) << 20); }
@Override
public Block getBlock(int x, int y, int z)
{ return this.getChunk(x >> POS_SHIFT, y >> POS_SHIFT, z >> POS_SHIFT).getBlock(x & MAX_POS, y & MAX_POS, z & MAX_POS); }
@Override
public void setBlock(int x, int y, int z, Block block)
{ this.getChunk(x >> POS_SHIFT, y >> POS_SHIFT, z >> POS_SHIFT).setBlock(x & MAX_POS, y & MAX_POS, z & MAX_POS, block); }
public void optimiseChunks()
{ this.chunks.forEach((pos, chunk) -> optimiseChunk(chunk)); }
//used for model combining and culling
public Chunk optimiseChunk(Chunk chunk)
{ return chunk; }
public void render(BlockRenderer blockRenderer)
{
blockRenderer.prepareModel(this.dummy.getModel());
this.chunks.forEach((pos, c) ->
{
if (c != null && c.isFullyGenerated())
c.render(blockRenderer);
});
blockRenderer.unbindModel();
}
public void unloadAllChunks()
{
LongList chunkPositions = new LongArrayList();
List<CompletableFuture<Void>> futures = new ArrayList<>();
if (this.chunks != null)
{
this.chunks.forEach((pos, chunk) ->
{ // for every chunk in memory
futures.add(CompletableFuture.runAsync(() ->
{
chunkPositions.add((long) pos); // add pos to chunk positions list for removal later
this.save.saveChunk(chunk); // save chunk
}, threadPool));
});
}
futures.forEach(CompletableFuture::join);
chunkPositions.forEach((LongConsumer) (pos -> this.chunks.remove(pos))); // remove all chunks
}
public long getSeed()
{ return this.seed; }
public static final int SEA_LEVEL = 0;
public void updateLoadedChunks(int chunkX, int chunkY, int chunkZ)
{
CompletableFuture.runAsync(() ->
{
List<Chunk> toKeep = new ArrayList<>();
// loop over rendered area, adding chunks that are needed
for (int x = chunkX - this.renderBound; x < chunkX + this.renderBound; x++)
for (int z = chunkZ - this.renderBound; z < chunkZ + this.renderBound; z++)
for (int y = chunkY - this.renderBound; y < chunkY + this.renderBound; y++)
toKeep.add(this.getChunkToLoad(x, y, z));
LongList toRemove = new LongArrayList();
// check which loaded chunks are not neccesary
chunks.forEach((pos, chunk) ->
{
if (!toKeep.contains(chunk))
toRemove.add((long) pos);
});
// unload unneccesary chunks from chunk array
toRemove.forEach((LongConsumer) this::unloadChunk);
toKeep.forEach(chunk ->
{
if (!chunk.isFullyGenerated())
{
this.populateChunk(chunk);
chunk.setFullyGenerated(true);
}
boolean alreadyRendering = chunk.doRender(); // if it's already rendering then it's most likely in the map
chunk.setRender(true);
if (!alreadyRendering)
chunks.put(this.posHash(chunk.chunkX, chunk.chunkY, chunk.chunkZ), chunk);
});
}, threadPool);
}
private static final class GenerationWorld implements BlockAccess, WorldGenConstants
{
GenerationWorld(World parent)
{ this.parent = parent; }
public final World parent;
@Override
public Block getBlock(int x, int y, int z)
{ return this.parent.getGenChunk(x >> POS_SHIFT, y >> POS_SHIFT, z >> POS_SHIFT).getBlock(x & MAX_POS, y & MAX_POS, z & MAX_POS); }
@Override
public void setBlock(int x, int y, int z, Block block)
{ this.parent.getGenChunk(x >> POS_SHIFT, y >> POS_SHIFT, z >> POS_SHIFT).setBlock(x & MAX_POS, y & MAX_POS, z & MAX_POS, block); }
}
}

View File

@ -1,38 +0,0 @@
package com.halotroop.litecraft.world.dimension;
import java.util.*;
import com.halotroop.litecraft.world.gen.ChunkGenerator;
import com.halotroop.litecraft.world.gen.modifier.WorldModifier;
import it.unimi.dsi.fastutil.ints.*;
public abstract class Dimension<T extends ChunkGenerator>
{
public List<WorldModifier> worldModifiers = new ArrayList<>();
public final int id;
public final String saveIdentifier;
public Dimension(int id, String saveIdentifier)
{
this.id = id;
this.saveIdentifier = saveIdentifier;
ID_TO_DIMENSION.put(id, this);
}
public Dimension<T> addWorldModifier(WorldModifier modifier)
{
this.worldModifiers.add(modifier);
return this;
}
public WorldModifier[] getWorldModifierArray()
{ return this.worldModifiers.toArray(new WorldModifier[0]); }
public abstract T createChunkGenerator(long seed);
public static Dimension<?> getById(int id)
{ return ID_TO_DIMENSION.get(id); }
private static final Int2ObjectMap<Dimension<?>> ID_TO_DIMENSION = new Int2ObjectArrayMap<>();
}

View File

@ -1,9 +0,0 @@
package com.halotroop.litecraft.world.dimension;
import com.halotroop.litecraft.world.gen.EarthChunkGenerator;
import com.halotroop.litecraft.world.gen.modifier.CavesModifier;
public final class Dimensions
{
public static final Dimension<EarthChunkGenerator> OVERWORLD = new EarthDimension(0, "earth").addWorldModifier(new CavesModifier());
}

View File

@ -1,13 +0,0 @@
package com.halotroop.litecraft.world.dimension;
import com.halotroop.litecraft.world.gen.EarthChunkGenerator;
class EarthDimension extends Dimension<EarthChunkGenerator>
{
public EarthDimension(int id, String saveIdentifier)
{ super(id, saveIdentifier); }
@Override
public EarthChunkGenerator createChunkGenerator(long seed)
{ return new EarthChunkGenerator(seed, this.id); }
}

View File

@ -1,8 +0,0 @@
package com.halotroop.litecraft.world.gen;
import com.halotroop.litecraft.world.*;
public interface ChunkGenerator
{
Chunk generateChunk(World world, int chunkX, int chunkY, int chunkZ);
}

View File

@ -1,71 +0,0 @@
package com.halotroop.litecraft.world.gen;
import java.util.Random;
import com.halotroop.litecraft.types.block.*;
import com.halotroop.litecraft.util.noise.OctaveSimplexNoise;
import com.halotroop.litecraft.world.*;
public class EarthChunkGenerator implements ChunkGenerator, WorldGenConstants
{
public EarthChunkGenerator(long seed, int dimension)
{
Random rand = new Random(seed);
this.noise = new OctaveSimplexNoise(rand, 3, 250.0, 50.0, 18.0);
this.stoneNoise = new OctaveSimplexNoise(rand, 1);
this.dimension = dimension;
}
private final OctaveSimplexNoise noise;
private final OctaveSimplexNoise stoneNoise;
private final int dimension;
@Override
public Chunk generateChunk(World world, int chunkX, int chunkY, int chunkZ)
{
Chunk chunk = new Chunk(world, chunkX, chunkY, chunkZ, this.dimension);
for (int x = 0; x < CHUNK_SIZE; x++)
{
double totalX = x + chunk.chunkStartX;
for (int z = 0; z < CHUNK_SIZE; z++)
{
double totalZ = chunk.chunkStartZ + z;
int height = (int) this.noise.sample(totalX, totalZ);
for (int y = 0; y < CHUNK_SIZE; y++)
{
double rockNoise = this.stoneNoise.sample(totalX / 160.0, (chunk.chunkStartY + y) / 50.0, totalZ / 160.0);
int totalY = chunk.chunkStartY + y;
Block block = Blocks.AIR;
if (totalY < height - 4)
block = pickStone(rockNoise);
else if (totalY < height - 1)
block = Blocks.DIRT;
else if (totalY < height)
block = Blocks.GRASS;
chunk.setBlock(x, y, z, block);
}
}
}
return chunk;
}
private static Block pickStone(double rockNoise)
{
if (rockNoise < -0.25)
{
return Blocks.ANDESITE;
}
else if (rockNoise < 0)
{
return Blocks.DIORITE;
}
else if (rockNoise < 0.25)
{
return Blocks.GNEISS;
}
else
{
return Blocks.GRANITE;
}
}
}

View File

@ -1,9 +0,0 @@
package com.halotroop.litecraft.world.gen;
public interface WorldGenConstants
{
int POS_SHIFT = 4;
int DOUBLE_SHIFT = POS_SHIFT * 2;
int CHUNK_SIZE = (int) Math.pow(2, POS_SHIFT);
int MAX_POS = CHUNK_SIZE - 1;
}

View File

@ -1,103 +0,0 @@
package com.halotroop.litecraft.world.gen.modifier;
import java.util.Random;
import com.halotroop.litecraft.types.block.Blocks;
import com.halotroop.litecraft.util.noise.OctaveSimplexNoise;
import com.halotroop.litecraft.world.BlockAccess;
import com.halotroop.litecraft.world.gen.WorldGenConstants;
public class CavesModifier implements WorldModifier, WorldGenConstants
{
private OctaveSimplexNoise caveNoise;
@Override
public void initialize(long seed)
{
Random rand = new Random(seed);
this.caveNoise = new OctaveSimplexNoise(rand, 2, 65.0, 1.0, 1.0);
}
@Override
public void modifyWorld(BlockAccess world, Random rand, int chunkStartX, int chunkStartY, int chunkStartZ)
{
final int subChunks = CHUNK_SIZE >> 2; // in 4x4x4 blocks
for (int subChunkX = 0; subChunkX < subChunks; subChunkX++)
{
int scOffsetX = subChunkX << 2; // sub chunk offset x
int scTotalX = scOffsetX + chunkStartX;
for (int subChunkZ = 0; subChunkZ < subChunks; subChunkZ++)
{
int scOffsetZ = subChunkZ << 2; // sub chunk offset z
int scTotalZ = scOffsetZ + chunkStartZ;
for (int subChunkY = 0; subChunkY < subChunks; subChunkY++)
{
int scOffsetY = subChunkY << 2; // sub chunk offset y
int scTotalY = scOffsetY + chunkStartY;
double scSampleY = scTotalY * 1.5; // squish caves along y axis a bit
double scUpperYOffset = 4.0 * 1.5;
// calculate noise at each corner of the cube [lower|upper][south|north][west|east]
double noiseLSW = this.caveNoise.sample(scTotalX, scSampleY, scTotalZ); // base = lower south west
double noiseUSW = this.caveNoise.sample(scTotalX, scSampleY + scUpperYOffset, scTotalZ);
double noiseLNW = this.caveNoise.sample(scTotalX, scSampleY, scTotalZ + 4);
double noiseUNW = this.caveNoise.sample(scTotalX, scSampleY + scUpperYOffset, scTotalZ + 4);
double noiseLSE = this.caveNoise.sample(scTotalX + 4, scSampleY, scTotalZ);
double noiseUSE = this.caveNoise.sample(scTotalX + 4, scSampleY + scUpperYOffset, scTotalZ);
double noiseLNE = this.caveNoise.sample(scTotalX + 4, scSampleY, scTotalZ + 4);
double noiseUNE = this.caveNoise.sample(scTotalX + 4, scSampleY + scUpperYOffset, scTotalZ + 4);
// calculate y lerp progresses
// lerp = low + progress * (high - low)
double ypSW = 0.25 * (noiseUSW - noiseLSW);
double ypNW = 0.25 * (noiseUNW - noiseLNW);
double ypSE = 0.25 * (noiseUSE - noiseLSE);
double ypNE = 0.25 * (noiseUNE - noiseLNE);
// initial Y noises
double ySW = noiseLSW;
double ySE = noiseLSE;
double yNW = noiseLNW;
double yNE = noiseLNE;
// loop over y, adding the progress each time
for (int subY = 0; subY < 4; ++subY)
{
int totalY = subY + scTotalY;
// calculate z lerp progresses
double zpW = 0.25 * (yNW - ySW);
double zpE = 0.25 * (yNE - ySE);
// initial Z noises
double zW = ySW;
double zE = ySE;
// loop over z, adding the progress each time
for (int subZ = 0; subZ < 4; ++subZ)
{
int totalZ = subZ + scTotalZ;
// calculate x lerp progress
double lerpProg = 0.25 * (zE - zW);
// initial x noise
double lerpNoise = zW;
// loop over x, adding the progress each time
for (int subX = 0; subX < 4; ++subX)
{
int totalX = subX + scTotalX;
// calculate whether to replace block with air
// if the noise is within the threshold for that block for caves
float threshold = world.getBlock(totalX, totalY, totalZ).getCaveCarveThreshold();
if (-threshold < lerpNoise && lerpNoise < threshold)
{ world.setBlock(totalX, totalY, totalZ, Blocks.AIR); }
// add progress to the noise
lerpNoise += lerpProg;
}
// add z progresses
zW += zpW;
zE += zpE;
}
// add y progresses
ySW += ypSW;
ySE += ypSE;
yNW += ypNW;
yNE += ypNE;
}
}
}
}
}
}

View File

@ -1,12 +0,0 @@
package com.halotroop.litecraft.world.gen.modifier;
import java.util.Random;
import com.halotroop.litecraft.world.BlockAccess;
public interface WorldModifier
{
void modifyWorld(BlockAccess world, Random rand, int chunkStartX, int chunkStartY, int chunkStartZ);
void initialize(long seed);
}