parent
7b774b1fc8
commit
f3d95b99c6
|
@ -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); }
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
package com.halotroop.litecraft.logic;
|
||||
|
||||
public enum Gamemode
|
||||
{
|
||||
GRAVITY, FREECAM,
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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(); }
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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<>();
|
||||
}
|
|
@ -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--;
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
|
@ -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); }
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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); }
|
||||
}
|
||||
}
|
|
@ -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<>();
|
||||
}
|
|
@ -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());
|
||||
}
|
|
@ -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); }
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue