|
@ -11,7 +11,7 @@
|
|||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-13">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
/bin/
|
||||
/target/
|
||||
.classpath
|
||||
.project
|
||||
.project
|
||||
/saves/
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=11
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=13
|
||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||
org.eclipse.jdt.core.compiler.compliance=11
|
||||
org.eclipse.jdt.core.compiler.compliance=13
|
||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||
|
@ -12,7 +12,7 @@ org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
|||
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
|
||||
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
|
||||
org.eclipse.jdt.core.compiler.release=enabled
|
||||
org.eclipse.jdt.core.compiler.source=11
|
||||
org.eclipse.jdt.core.compiler.source=13
|
||||
org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false
|
||||
org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647
|
||||
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
|
||||
|
|
50
pom.xml
|
@ -2,18 +2,18 @@
|
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>LiteCraft</groupId>
|
||||
<groupId>com.halotroop2288</groupId>
|
||||
<artifactId>LiteCraft</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>NIGHTLY</version>
|
||||
<name>LiteCraft</name>
|
||||
<description>Lightweight Voxel Engine</description>
|
||||
<description>Lightweight Voxel Engine utilizing the Ginger3D engine by hYdos</description>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.0</version>
|
||||
<configuration>
|
||||
<release>11</release>
|
||||
<release>13</release>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
@ -51,17 +51,21 @@
|
|||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>sonatype-snapshots</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
|
||||
<releases><enabled>false</enabled></releases>
|
||||
<snapshots><enabled>true</enabled></snapshots>
|
||||
<releases>
|
||||
<enabled>false</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
|
@ -78,30 +82,27 @@
|
|||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.github.hYdos</groupId>
|
||||
<artifactId>Ginger3D</artifactId>
|
||||
<version>NIGHTLY</version>
|
||||
<scope>system</scope>
|
||||
<systemPath>${project.basedir}/libs/Ginger3D-NIGHTLY.jar</systemPath>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.Spoutcraft</groupId>
|
||||
<artifactId>soundsystem</artifactId>
|
||||
<version>master-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.hYdos</groupId>
|
||||
<artifactId>Ginger3D</artifactId>
|
||||
<version>liteCraft-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<version>2.11.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<version>2.11.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-slf4j-impl</artifactId>
|
||||
<version>2.11.1</version>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-slf4j-impl</artifactId>
|
||||
<version>2.11.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-cli</groupId>
|
||||
|
@ -169,5 +170,10 @@
|
|||
<artifactId>joml-camera</artifactId>
|
||||
<version>master-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.unimi.dsi</groupId>
|
||||
<artifactId>fastutil</artifactId>
|
||||
<version>8.3.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -1,168 +0,0 @@
|
|||
package com.github.halotroop.litecraft;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Random;
|
||||
|
||||
import org.aeonbits.owner.ConfigFactory;
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.DefaultParser;
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.lwjgl.Version;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import org.lwjgl.glfw.GLFWErrorCallback;
|
||||
|
||||
import com.github.halotroop.litecraft.input.Input;
|
||||
import com.github.halotroop.litecraft.input.KeyCallbackHandler;
|
||||
import com.github.halotroop.litecraft.input.Keybind;
|
||||
import com.github.halotroop.litecraft.input.MouseCallbackHandler;
|
||||
import com.github.halotroop.litecraft.logic.Timer;
|
||||
import com.github.halotroop.litecraft.logic.Timer.TickListener;
|
||||
import com.github.halotroop.litecraft.options.SettingsConfig;
|
||||
import com.github.halotroop.litecraft.options.SettingsHandler;
|
||||
import com.github.halotroop.litecraft.render.RenderWrapper;
|
||||
|
||||
import io.github.hydos.ginger.engine.elements.objects.Player;
|
||||
import io.github.hydos.ginger.engine.io.Window;
|
||||
import io.github.hydos.ginger.engine.math.vectors.Vector3f;
|
||||
import io.github.hydos.ginger.engine.obj.ModelLoader;
|
||||
import io.github.hydos.ginger.engine.render.models.TexturedModel;
|
||||
|
||||
public class LiteCraftMain implements Runnable
|
||||
{
|
||||
public static Logger logger = LogManager.getLogger(Logger.class.getName());
|
||||
private static SettingsConfig config;
|
||||
public static int width = 640, height = 480, maxFPS = 60; // Don't change these values. They just initialize it in case we forget to set them later.
|
||||
public static boolean spamLog = false, debug = false, limitFPS = false;
|
||||
public static String splashText = "404";
|
||||
public static boolean fullscreen = false;
|
||||
private int fps, ups, tps;
|
||||
private long frameTimer;
|
||||
protected Timer timer;
|
||||
protected TickListener tickListener = new TickListener()
|
||||
{
|
||||
@Override
|
||||
public void onTick(float deltaTime)
|
||||
{ tps++; }
|
||||
};
|
||||
|
||||
public static void main(String[] args) throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
config = ConfigFactory.create(SettingsConfig.class);
|
||||
width = config.screenWidth();
|
||||
height = config.screenHeight();
|
||||
maxFPS = config.max_fps();
|
||||
spamLog = config.spamLog();
|
||||
debug = config.debugMode();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
System.err.println("Config failed to load.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
try
|
||||
{
|
||||
Options options = SettingsHandler.createCommandLineOptions();
|
||||
CommandLine cmd = new DefaultParser().parse(options, args);
|
||||
width = Integer.parseInt(cmd.getOptionValue("width", "640"));
|
||||
height = Integer.parseInt(cmd.getOptionValue("height", "480"));
|
||||
maxFPS = Integer.parseInt(cmd.getOptionValue("max_fps", "60"));
|
||||
debug = Boolean.parseBoolean(cmd.getOptionValue("debug", "false"));
|
||||
spamLog = Boolean.parseBoolean(cmd.getOptionValue("spam_log", "false"));
|
||||
limitFPS = Boolean.parseBoolean(cmd.getOptionValue("limit_fps", "false"));
|
||||
}
|
||||
catch (ParseException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
new LiteCraftMain().run();
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
// Leave this alone.
|
||||
GLFWErrorCallback.createPrint(System.err).set();
|
||||
if (!GLFW.glfwInit()) throw new IllegalStateException("Unable to initialize GLFW");
|
||||
timer = new Timer(20);
|
||||
timer.addTickListener(tickListener);
|
||||
try
|
||||
{
|
||||
String[] splashes = TextFileReader.readFileToStringArray("text/splashes.txt");
|
||||
splashText = splashes[new Random().nextInt(splashes.length)];
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
//because someone has not made player models and im lazy lets use the ones bundeled with the engine :)
|
||||
RenderWrapper.preInit();
|
||||
TexturedModel tModel = ModelLoader.loadModel("stall.obj", "stallTexture.png");
|
||||
tModel.getTexture().setReflectivity(1f);
|
||||
tModel.getTexture().setShineDamper(7f);
|
||||
Player renderPlayer = new Player(tModel, new Vector3f(0, 0, -3), 0, 180f, 0, new Vector3f(0.2f, 0.2f, 0.2f));
|
||||
RenderWrapper.init(splashText, renderPlayer);
|
||||
long windowId = Window.window;
|
||||
KeyCallbackHandler.trackWindow(windowId);
|
||||
MouseCallbackHandler.trackWindow(windowId);
|
||||
// window.setWindowTitle("LiteCraft - " + ((splashText == "" || splashText == null) ? "INSERT SPLASH TEXT HERE!" : splashText));
|
||||
input();
|
||||
}
|
||||
|
||||
// Sets up the key inputs for the game (currently just esc for closing the game)
|
||||
public void input()
|
||||
{ Input.addPressCallback(Keybind.EXIT, LiteCraftMain::shutDown); }
|
||||
|
||||
// Things that the game should do over and over and over again until it is closed
|
||||
private void loop()
|
||||
{
|
||||
ups++;
|
||||
// Poll for window events. The key callback above will only be invoked during this call.
|
||||
GLFW.glfwPollEvents();
|
||||
Input.invokeAllListeners();
|
||||
RenderWrapper.update();
|
||||
timer.tick();
|
||||
if (fps < maxFPS || !limitFPS) render();
|
||||
if (System.currentTimeMillis() > frameTimer + 1000) // wait for one second
|
||||
{
|
||||
fps = 0;
|
||||
ups = 0;
|
||||
tps = 0;
|
||||
frameTimer += 1000; // reset the wait time
|
||||
}
|
||||
}
|
||||
|
||||
public void render()
|
||||
{
|
||||
if (debug) System.out.println("LiteCraft | FPS: " + fps + " | TPS: " + tps + " | UPS: " + ups);
|
||||
RenderWrapper.render();
|
||||
fps++; // After a successful frame render, increase the frame counter.
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
System.out.println("Starting game..." + "\n" + "LWJGL version: " + Version.getVersion() + "\n" + "Resolution: " + width + 'x' + height);
|
||||
init();
|
||||
frameTimer = System.currentTimeMillis();
|
||||
// Run the rendering loop until the player has attempted to close the window
|
||||
while (!Window.closed())
|
||||
{
|
||||
if (Window.isUpdating())
|
||||
{ loop(); }
|
||||
}
|
||||
shutDown();
|
||||
}
|
||||
|
||||
// Shuts down the game and destroys all the things that are using RAM (so the user doesn't have to restart their computer afterward...)
|
||||
private static void shutDown()
|
||||
{
|
||||
logger.log(Level.DEBUG, "Closing game...");
|
||||
RenderWrapper.cleanup();
|
||||
System.out.println("Game closed successfully.");
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
package com.github.halotroop.litecraft;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class TextFileReader
|
||||
{
|
||||
public static String[] readFileToStringArray(String filename) throws IOException
|
||||
{
|
||||
ClassLoader.getSystemClassLoader();
|
||||
InputStream inputStream = ClassLoader.getSystemResourceAsStream(filename);
|
||||
InputStreamReader streamReader = new InputStreamReader(inputStream, "UTF-8");
|
||||
BufferedReader in = new BufferedReader(streamReader);
|
||||
String[] output = new String[] {};
|
||||
for (String line; (line = in.readLine()) != null;)
|
||||
{
|
||||
String[] bufferArray = new String[output.length + 1];
|
||||
System.arraycopy(output, 0, bufferArray, 0, output.length);
|
||||
bufferArray[output.length] = line;
|
||||
output = bufferArray;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
package com.github.halotroop.litecraft.input;
|
||||
|
||||
/*
|
||||
* Author: Valoeghese
|
||||
*/
|
||||
public final class InitialPressHandler implements KeyListener
|
||||
{
|
||||
public InitialPressHandler(KeyCallback callback)
|
||||
{ this.callback = callback; }
|
||||
|
||||
private boolean activatedPreviously = false;
|
||||
private final KeyCallback callback;
|
||||
|
||||
@Override
|
||||
public void listen(boolean active)
|
||||
{
|
||||
if (!activatedPreviously && active)
|
||||
{ callback.onCallback(); }
|
||||
activatedPreviously = active;
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package com.github.halotroop.litecraft.input;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/*
|
||||
* Author: Valoeghese
|
||||
*/
|
||||
public class Input
|
||||
{
|
||||
private static final Map<Keybind, List<KeyCallback>> CALLBACKS = new HashMap<>();
|
||||
private static final Map<Keybind, List<KeyListener>> LISTENERS = new HashMap<>();
|
||||
|
||||
public static void addPressCallback(Keybind key, KeyCallback callback)
|
||||
{ CALLBACKS.computeIfAbsent(key, listener -> new ArrayList<>()).add(callback); }
|
||||
|
||||
public static void addListener(Keybind key, KeyListener callback)
|
||||
{ LISTENERS.computeIfAbsent(key, listener -> new ArrayList<>()).add(callback); }
|
||||
|
||||
public static void invokeAllListeners()
|
||||
{
|
||||
CALLBACKS.forEach((keybind, listeners) ->
|
||||
{
|
||||
if (keybind.isActive())
|
||||
{ listeners.forEach(callback -> callback.onCallback()); }
|
||||
});
|
||||
LISTENERS.forEach((keybind, listeners) ->
|
||||
{
|
||||
listeners.forEach(listener -> listener.listen(keybind.isActive()));
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
package com.github.halotroop.litecraft.input;
|
||||
|
||||
/*
|
||||
* Author: Valoeghese
|
||||
*/
|
||||
public interface KeyCallback
|
||||
{
|
||||
public void onCallback();
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
package com.github.halotroop.litecraft.input;
|
||||
|
||||
import org.lwjgl.glfw.*;
|
||||
|
||||
/*
|
||||
* Author: Valoeghese
|
||||
*/
|
||||
public class KeyCallbackHandler extends GLFWKeyCallback
|
||||
{
|
||||
private KeyCallbackHandler()
|
||||
{}
|
||||
|
||||
private static final KeyCallbackHandler INSTANCE = new KeyCallbackHandler();
|
||||
|
||||
public static void trackWindow(long window)
|
||||
{ GLFW.glfwSetKeyCallback(window, INSTANCE); }
|
||||
|
||||
public static boolean[] keys = new boolean[GLFW.GLFW_KEY_LAST];
|
||||
|
||||
@Override
|
||||
public void invoke(long window, int key, int scancode, int action, int mods)
|
||||
{
|
||||
try
|
||||
{
|
||||
keys[key] = action != GLFW.GLFW_RELEASE;
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException e)
|
||||
{
|
||||
// Probably just changing the volume
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
package com.github.halotroop.litecraft.input;
|
||||
|
||||
/*
|
||||
* Author: Valoeghese
|
||||
*/
|
||||
public interface KeyListener
|
||||
{
|
||||
public void listen(boolean active);
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package com.github.halotroop.litecraft.input;
|
||||
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
/*
|
||||
* Author: Valoeghese
|
||||
*/
|
||||
public final class Keybind
|
||||
{
|
||||
public int value;
|
||||
public boolean mouse;
|
||||
public static final Keybind MOVE_UP = new Keybind(GLFW.GLFW_KEY_W, false);
|
||||
public static final Keybind MOVE_DOWN = new Keybind(GLFW.GLFW_KEY_S, false);
|
||||
public static final Keybind MOVE_LEFT = new Keybind(GLFW.GLFW_KEY_A, false);
|
||||
public static final Keybind MOVE_RIGHT = new Keybind(GLFW.GLFW_KEY_D, false);
|
||||
public static final Keybind USE = new Keybind(GLFW.GLFW_MOUSE_BUTTON_1, true);
|
||||
public static final Keybind SELECT_0 = new Keybind(GLFW.GLFW_KEY_1, false);
|
||||
public static final Keybind SELECT_1 = new Keybind(GLFW.GLFW_KEY_2, false);
|
||||
public static final Keybind SELECT_2 = new Keybind(GLFW.GLFW_KEY_3, false);
|
||||
public static final Keybind EXIT = new Keybind(GLFW.GLFW_KEY_ESCAPE, false);
|
||||
|
||||
public Keybind(int initValue, boolean isMouse)
|
||||
{
|
||||
this.value = initValue;
|
||||
this.mouse = isMouse;
|
||||
}
|
||||
|
||||
public boolean isActive()
|
||||
{ return mouse ? MouseCallbackHandler.buttons[value] : KeyCallbackHandler.keys[value]; }
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
package com.github.halotroop.litecraft.input;
|
||||
|
||||
import org.lwjgl.glfw.*;
|
||||
|
||||
/*
|
||||
* Author: Valoeghese
|
||||
*/
|
||||
public class MouseCallbackHandler extends GLFWMouseButtonCallback
|
||||
{
|
||||
private MouseCallbackHandler()
|
||||
{}
|
||||
|
||||
private static final MouseCallbackHandler INSTANCE = new MouseCallbackHandler();
|
||||
|
||||
public static void trackWindow(long window)
|
||||
{ GLFW.glfwSetMouseButtonCallback(window, INSTANCE); }
|
||||
|
||||
public static boolean[] buttons = new boolean[GLFW.GLFW_MOUSE_BUTTON_LAST];
|
||||
|
||||
@Override
|
||||
public void invoke(long window, int button, int action, int mods)
|
||||
{ buttons[button] = action != GLFW.GLFW_RELEASE; }
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
package com.github.halotroop.litecraft.logic;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/*
|
||||
* @author Jack Wilsdon (Stack Exchange)
|
||||
* https://codereview.stackexchange.com/questions/111855/ticker-for-game-timing
|
||||
*/
|
||||
public class Timer
|
||||
{
|
||||
private double lastTick;
|
||||
private double nextTick;
|
||||
private int tickRate;
|
||||
private Set<TickListener> tickListeners = new HashSet<>();
|
||||
|
||||
public Timer(int tickRate)
|
||||
{ this.tickRate = tickRate; }
|
||||
|
||||
public void addTickListener(TickListener listener)
|
||||
{ tickListeners.add(listener); }
|
||||
|
||||
public void removeTickListener(TickListener listener)
|
||||
{ tickListeners.remove(listener); }
|
||||
|
||||
public void setTickRate(int tickRate)
|
||||
{ this.tickRate = tickRate; }
|
||||
|
||||
public int getTickRate()
|
||||
{ return tickRate; }
|
||||
|
||||
public void reset()
|
||||
{
|
||||
lastTick = 0;
|
||||
nextTick = 0;
|
||||
}
|
||||
|
||||
public boolean tick()
|
||||
{
|
||||
long currentTime = System.currentTimeMillis();
|
||||
if (currentTime >= nextTick)
|
||||
{
|
||||
long targetTimeDelta = 1000L / tickRate;
|
||||
if (lastTick == 0 || nextTick == 0)
|
||||
{
|
||||
lastTick = currentTime - targetTimeDelta;
|
||||
nextTick = currentTime;
|
||||
}
|
||||
float deltaTime = (float) (currentTime - lastTick) / targetTimeDelta;
|
||||
for (TickListener listener : tickListeners)
|
||||
{ listener.onTick(deltaTime); }
|
||||
lastTick = currentTime;
|
||||
nextTick = currentTime + targetTimeDelta;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public interface TickListener
|
||||
{
|
||||
void onTick(float deltaTime);
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
package com.github.halotroop.litecraft.options;
|
||||
|
||||
import org.aeonbits.owner.Config;
|
||||
|
||||
@Config.Sources("file:~/Documents/LiteCraft.config")
|
||||
public interface SettingsConfig extends Config
|
||||
{
|
||||
@Key("render.screen_width")
|
||||
@DefaultValue("640")
|
||||
public int screenWidth();
|
||||
|
||||
@Key("render.screen_height")
|
||||
@DefaultValue("480")
|
||||
public int screenHeight();
|
||||
|
||||
@Key("render.max_fps")
|
||||
@DefaultValue("60")
|
||||
public int max_fps();
|
||||
|
||||
@Key("debug.debug_mode")
|
||||
@DefaultValue("false")
|
||||
public boolean debugMode();
|
||||
|
||||
@Key("debug.spam_log")
|
||||
@DefaultValue("false")
|
||||
public boolean spamLog();
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
package com.github.halotroop.litecraft.options;
|
||||
|
||||
import org.apache.commons.cli.*;
|
||||
|
||||
public class SettingsHandler
|
||||
{
|
||||
public static Options createCommandLineOptions()
|
||||
{
|
||||
Options cmdOptions = new Options();
|
||||
cmdOptions.addOption(new Option("w", "width", true, "Screen width"));
|
||||
cmdOptions.addOption(new Option("h", "height", true, "Screen height"));
|
||||
cmdOptions.addOption(new Option("debug", "debug", true, "Use debug features"));
|
||||
cmdOptions.addOption(new Option("spam_log", "spam_log", true, "Log sanity checks"));
|
||||
cmdOptions.addOption(new Option("limit_fps", "limit_fps", true, "Use the FPS limiter"));
|
||||
cmdOptions.addOption(new Option("max_fps", "max_fps", true, "The maximum amount of FPS"));
|
||||
return cmdOptions;
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package com.github.halotroop.litecraft.registries;
|
||||
|
||||
public class BlockList
|
||||
{
|
||||
// this is how you fix bugs :)
|
||||
// public static List<Block> blocks;
|
||||
// public static Block dirt;
|
||||
// public static Block stone;
|
||||
public BlockList()
|
||||
{}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
package com.github.halotroop.litecraft.registries;
|
||||
|
||||
public class EntityList
|
||||
{
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
package com.github.halotroop.litecraft.registries;
|
||||
|
||||
public class ItemList
|
||||
{
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
package com.github.halotroop.litecraft.render;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.github.halotroop.litecraft.LiteCraftMain;
|
||||
|
||||
import io.github.hydos.ginger.engine.cameras.ThirdPersonCamera;
|
||||
import io.github.hydos.ginger.engine.elements.GuiTexture;
|
||||
import io.github.hydos.ginger.engine.elements.objects.Entity;
|
||||
import io.github.hydos.ginger.engine.elements.objects.Light;
|
||||
import io.github.hydos.ginger.engine.elements.objects.Player;
|
||||
import io.github.hydos.ginger.engine.font.TextMaster;
|
||||
import io.github.hydos.ginger.engine.io.Window;
|
||||
import io.github.hydos.ginger.engine.math.vectors.Vector3f;
|
||||
import io.github.hydos.ginger.engine.math.vectors.Vector4f;
|
||||
import io.github.hydos.ginger.engine.particle.ParticleMaster;
|
||||
import io.github.hydos.ginger.engine.postprocessing.PostProcessing;
|
||||
import io.github.hydos.ginger.engine.render.MasterRenderer;
|
||||
import io.github.hydos.ginger.engine.terrain.Terrain;
|
||||
import io.github.hydos.ginger.engine.utils.Loader;
|
||||
import io.github.hydos.ginger.main.GingerMain;
|
||||
|
||||
/*
|
||||
* Render wrapper for Ginger3D, Hydos' render engine
|
||||
*/
|
||||
public class RenderWrapper
|
||||
{
|
||||
private static MasterRenderer masterRenderer;
|
||||
public static List<Entity> entities = new ArrayList<Entity>();
|
||||
public static List<GuiTexture> guis = new ArrayList<GuiTexture>();
|
||||
public static List<Light> lights = new ArrayList<Light>();
|
||||
public static ThirdPersonCamera camera;
|
||||
private static final List<Terrain> TERRAIN = new ArrayList<Terrain>();
|
||||
private static final List<Entity> NORMAL_ENTITY = new ArrayList<Entity>();
|
||||
|
||||
public static void init(String splash, Player renderPlayer)
|
||||
{
|
||||
camera = new ThirdPersonCamera(new Vector3f(0, 0.1f, 0), renderPlayer);
|
||||
Window.setBackgroundColour(96 / 256F, 26 / 256F, 108 / 25F);
|
||||
masterRenderer = new MasterRenderer(camera);
|
||||
ParticleMaster.init(masterRenderer.getProjectionMatrix());
|
||||
PostProcessing.init();
|
||||
}
|
||||
|
||||
public static void cleanup()
|
||||
{
|
||||
Window.stop();
|
||||
PostProcessing.cleanUp();
|
||||
ParticleMaster.cleanUp();
|
||||
masterRenderer.cleanUp();
|
||||
TextMaster.cleanUp();
|
||||
Loader.cleanUp();
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
public static void render()
|
||||
{
|
||||
Window.update();
|
||||
GingerMain.update();
|
||||
GingerMain.preRenderScene(masterRenderer);
|
||||
masterRenderer.renderScene(entities, NORMAL_ENTITY, TERRAIN, lights, camera, new Vector4f(0, -1, 0, 100000));
|
||||
ParticleMaster.renderParticles(camera);
|
||||
masterRenderer.renderGuis(guis);
|
||||
TextMaster.render();
|
||||
Window.swapBuffers();
|
||||
}
|
||||
|
||||
public static void preInit()
|
||||
{
|
||||
Window.create(LiteCraftMain.width, LiteCraftMain.height, "LiteCraft - " + LiteCraftMain.splashText, 60);
|
||||
GingerMain.init();
|
||||
}
|
||||
|
||||
public static void update()
|
||||
{
|
||||
Window.update();
|
||||
GingerMain.update();
|
||||
ParticleMaster.update(camera);
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
package com.github.halotroop.litecraft.types.block;
|
||||
|
||||
public class Block
|
||||
{
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
package com.github.halotroop.litecraft.types.entity;
|
||||
|
||||
public abstract class Entity
|
||||
{
|
||||
public Entity(String id, String type)
|
||||
{}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
package com.github.halotroop.litecraft.types.gui;
|
||||
|
||||
public abstract class HUD
|
||||
{
|
||||
public HUD()
|
||||
{}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
package com.github.halotroop.litecraft.types.gui;
|
||||
|
||||
public class MainMenu extends Menu
|
||||
{
|
||||
public MainMenu()
|
||||
{ super(); }
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
package com.github.halotroop.litecraft.types.gui;
|
||||
|
||||
public abstract class Menu
|
||||
{
|
||||
public Menu()
|
||||
{}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
package com.github.halotroop.litecraft.types.item;
|
||||
|
||||
public class Item
|
||||
{
|
||||
}
|
|
@ -0,0 +1,217 @@
|
|||
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.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.blockRenderer = new BlockRenderer(GingerRegister.getInstance().masterRenderer.getEntityShader(), GingerRegister.getInstance().masterRenderer.getProjectionMatrix());
|
||||
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); }
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package com.halotroop.litecraft.logic;
|
||||
|
||||
public enum Gamemode
|
||||
{
|
||||
GRAVITY, FREECAM,
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package com.halotroop.litecraft.logic;
|
||||
|
||||
import tk.valoeghese.sod.BinaryData;
|
||||
|
||||
public interface SODSerializable
|
||||
{
|
||||
void read(BinaryData data);
|
||||
|
||||
void write(BinaryData data);
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
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;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
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(); }
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
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<>();
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
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--;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
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; }
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
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); }
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package com.halotroop.litecraft.util;
|
||||
|
||||
public enum RelativeDirection
|
||||
{
|
||||
UP, // up up
|
||||
DOWN, // down down
|
||||
LEFT, // >left< right
|
||||
RIGHT, // left >right<
|
||||
FORWARD, // b
|
||||
BACKWARD,// a
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,854 @@
|
|||
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
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);
|
||||
}
|
|
@ -0,0 +1,245 @@
|
|||
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,253 @@
|
|||
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); }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
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<>();
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
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());
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
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); }
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package com.halotroop.litecraft.world.gen;
|
||||
|
||||
import com.halotroop.litecraft.world.*;
|
||||
|
||||
public interface ChunkGenerator
|
||||
{
|
||||
Chunk generateChunk(World world, int chunkX, int chunkY, int chunkZ);
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
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;
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
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);
|
||||
}
|
After Width: | Height: | Size: 51 KiB |
|
@ -0,0 +1,6 @@
|
|||
# Made in Blockbench 3.3.1
|
||||
newmtl 0
|
||||
map_Kd textures/blocks/cubes/wood/planks/planks_oak.png
|
||||
newmtl 1
|
||||
map_Kd textures/blocks/cubes/wood/logs/side/log_oak.png
|
||||
newmtl none
|
|
@ -1,26 +0,0 @@
|
|||
# Blender v2.79 (sub 0) OBJ File: 'very good grass.blend'
|
||||
# www.blender.org
|
||||
o Plane
|
||||
v 0.000000 1.963634 1.000000
|
||||
v -0.000000 -0.036366 1.000000
|
||||
v 0.000000 1.963634 -1.000000
|
||||
v -0.000000 -0.036366 -1.000000
|
||||
v -0.978509 1.963634 0.206204
|
||||
v -0.978509 -0.036366 0.206204
|
||||
v 0.978509 1.963634 -0.206204
|
||||
v 0.978509 -0.036366 -0.206204
|
||||
vt 0.000977 -0.001953
|
||||
vt 1.000000 1.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 0.997070 0.001953
|
||||
vt -0.001953 0.998047
|
||||
vt 0.998047 0.000000
|
||||
vt 0.998047 0.997070
|
||||
vt 0.003907 -0.002930
|
||||
vn 1.0000 -0.0000 0.0000
|
||||
vn 0.2062 -0.0000 0.9785
|
||||
s off
|
||||
f 2/1/1 3/2/1 1/3/1
|
||||
f 2/1/1 4/4/1 3/2/1
|
||||
f 5/5/2 8/6/2 7/7/2
|
||||
f 5/5/2 6/8/2 8/6/2
|
|
@ -24,4 +24,6 @@ void main(void){
|
|||
|
||||
// calculate brightness
|
||||
out_Colour.rgb *= brightness;
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@ uniform vec3 skyColour;
|
|||
|
||||
void main(void){
|
||||
|
||||
float brightness = 3;
|
||||
|
||||
vec3 unitNormal = normalize(surfaceNormal);
|
||||
vec3 unitVectorToCamera = normalize(toCameraVector);
|
||||
|
||||
|
@ -46,5 +48,6 @@ void main(void){
|
|||
|
||||
out_Color = vec4(totalDiffuse, 1.0) * textureColour + vec4(totalSpecular, 1.0);
|
||||
out_Color = mix(vec4(skyColour, 1.0), out_Color, visibility);
|
||||
out_Color = vec4(out_Color.r*brightness, out_Color.g*brightness, out_Color.b*brightness, out_Color.a);
|
||||
|
||||
}
|
||||
|
|
|
@ -20,8 +20,8 @@ const vec4 plane = vec4(0, -1, 0, 15);
|
|||
|
||||
uniform float useFakeLighting;
|
||||
|
||||
const float density = 0.01;
|
||||
const float gradient = 5.0;
|
||||
const float density = 0.0025;
|
||||
const float gradient = 10.0;
|
||||
|
||||
void main(void){
|
||||
|
||||
|
@ -35,9 +35,7 @@ void main(void){
|
|||
pass_textureCoords = textureCoords;
|
||||
|
||||
vec3 actualNormal = normal;
|
||||
if(useFakeLighting > 0.5){
|
||||
actualNormal = vec3(0.0, 1.0, 0.0);
|
||||
}
|
||||
actualNormal = vec3(0.0, 1.0, 0.0);
|
||||
|
||||
surfaceNormal = (transformationMatrix * vec4(actualNormal, 0.0)).xyz;
|
||||
for(int i=0;i<5;i++){
|
||||
|
|
|
@ -7,8 +7,8 @@ out vec4 out_colour;
|
|||
uniform vec3 colour;
|
||||
uniform sampler2D fontAtlas;
|
||||
|
||||
const float width = 0.5;
|
||||
const float edge = 0.1;
|
||||
const float width = 0;
|
||||
const float edge = 1.0;
|
||||
|
||||
uniform float borderWidth;
|
||||
uniform float borderEdge;
|
||||
|
|
|
@ -9,7 +9,7 @@ uniform vec2 translation;
|
|||
|
||||
void main(void){
|
||||
|
||||
gl_Position = vec4(position, 0.0, 1.0);
|
||||
gl_Position = vec4(position + translation, 0.0, 1.0);
|
||||
pass_textureCoords = textureCoords;
|
||||
|
||||
}
|
||||
|
|
|
@ -8,9 +8,9 @@ uniform sampler2D modelTexture;
|
|||
|
||||
void main(void){
|
||||
float alpha = texture(modelTexture, textureCoords).a;
|
||||
if(alpha < 0.5){
|
||||
if(alpha < 0.4){
|
||||
discard;
|
||||
}
|
||||
|
||||
out_Colour = vec4(1.0);
|
||||
out_Colour = vec4(1.0, 1.0, 1.0, 0.1);
|
||||
}
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
& Knuckles
|
||||
0% Dairy
|
||||
0% crackers
|
||||
2+2=10 in base-4!
|
||||
4bitsinabyte!
|
||||
99
|
||||
Absolutely no mixins!
|
||||
Also try 7 Days to Die!
|
||||
Also try CastleMiner Z!
|
||||
Also try Miner Dig Deep... oh wait
|
||||
Also try Terraria!
|
||||
Also try Total Miner Forge!
|
||||
Alt-F4!
|
||||
Are dimensions even real?
|
||||
Are you feeling lucky punk?
|
||||
Battle Royale!
|
||||
Better without Kinect sensor!
|
||||
CM != MC
|
||||
California thinks it causes cancer!
|
||||
Can't pirate what's free!
|
||||
Conservatives not allowed!
|
||||
Could not connect to PSN!
|
||||
Ctrl-Alt-Delete!
|
||||
Deluxe Edition
|
||||
Don't forget to vote!
|
||||
Don't punch trees!
|
||||
Don't stay in school.
|
||||
Drink! Drink! Drink!
|
||||
Easy-to-use!
|
||||
Episode 3
|
||||
Featuring Dante from the Devil May Cry Series!
|
||||
Forget the coremods!
|
||||
Fully 2D!
|
||||
Go the fuck to sleep.
|
||||
Happy birthday!
|
||||
HeartGold
|
||||
Horse Armour at no extra cost!
|
||||
Howling at the moon!
|
||||
I can't believe it's not Minecraft!
|
||||
I mean for fucks sake, it's [current year]!
|
||||
Innuendo abound!
|
||||
Jam it up your pee hole!
|
||||
Java Edition
|
||||
Keep playing, or else!
|
||||
LOL SO RANDOM!
|
||||
Many of a kind!
|
||||
Mega-dick: ACTIVATE!
|
||||
Absolutely no mods!
|
||||
Modular!
|
||||
New Funky Mode!
|
||||
Not available on Xbox!
|
||||
Not for kids!
|
||||
Not rated by PEGI
|
||||
Not rated by the ESRB
|
||||
Not safe for work!
|
||||
Now or never!
|
||||
On Linux AND PC!
|
||||
Open-source!
|
||||
Please try again!
|
||||
Pull request!
|
||||
Slap my nuts and call me grandma!
|
||||
SoulSilver
|
||||
Splashy fun!
|
||||
Starfish really loves you!
|
||||
Start-up complete!
|
||||
The first of its kind!
|
||||
They took my freaking kidney!
|
||||
This is the end, friend.
|
||||
Too many splashes?
|
||||
Try turning it off and back on again!
|
||||
U
|
||||
Use at your own risk!
|
||||
Vegan-friendly!
|
||||
WTFPL
|
||||
Watch out for dragons!
|
||||
We don't need no education.
|
||||
What was that?
|
||||
Who's been drawing dicks?
|
||||
With extra dip!
|
||||
and a large soda.
|
Before Width: | Height: | Size: 1.9 MiB |
After Width: | Height: | Size: 625 B |
After Width: | Height: | Size: 786 B |
After Width: | Height: | Size: 698 B |
After Width: | Height: | Size: 810 B |
After Width: | Height: | Size: 763 B |
After Width: | Height: | Size: 769 B |
After Width: | Height: | Size: 857 B |
After Width: | Height: | Size: 767 B |
After Width: | Height: | Size: 829 B |
After Width: | Height: | Size: 794 B |
After Width: | Height: | Size: 795 B |
After Width: | Height: | Size: 859 B |
After Width: | Height: | Size: 780 B |
After Width: | Height: | Size: 765 B |
After Width: | Height: | Size: 850 B |
After Width: | Height: | Size: 792 B |
Before Width: | Height: | Size: 329 B |
Before Width: | Height: | Size: 584 B |
Before Width: | Height: | Size: 406 B |
Before Width: | Height: | Size: 557 B |
Before Width: | Height: | Size: 400 B |
Before Width: | Height: | Size: 590 B |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 411 B |
Before Width: | Height: | Size: 631 B |
Before Width: | Height: | Size: 412 B |