Initial commit

master
itycodes 2 months ago
commit b647cc32d8

@ -0,0 +1,45 @@
plugins {
id 'application'
id 'com.github.johnrengelman.shadow' version '7.1.2'
}
version = "0.0.1"
sourceCompatibility = 17
targetCompatibility = 17
repositories {
mavenCentral()
}
//applicationDefaultJvmArgs = ["-javaagent:lwjglx-debug-1.0.0.jar"]
//project.ext.lwjglNatives = "natives-linux-arm64"
project.ext.lwjglNatives = "natives-linux"
dependencies {
implementation platform("org.lwjgl:lwjgl-bom:3.3.1")
implementation "org.lwjgl:lwjgl"
implementation "org.lwjgl:lwjgl-glfw"
implementation "org.lwjgl:lwjgl-opengl"
implementation "org.lwjgl:lwjgl-stb"
runtimeOnly "org.lwjgl:lwjgl::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-glfw::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-opengl::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives"
implementation "org.joml:joml:1.10.4"
implementation 'org.reflections:reflections:0.10.2'
implementation 'org.slf4j:slf4j-nop:1.7.32'
}
application {
mainClass = 'ity.opencraft.Main'
}
jar {
manifest {
attributes 'Main-Class': 'ity.opencraft.Main'
}
}

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

234
gradlew vendored

@ -0,0 +1,234 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

89
gradlew.bat vendored

@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

@ -0,0 +1,11 @@
/*
* This file was generated by the Gradle 'init' task.
*
* The settings file is used to specify which projects to include in your build.
*
* Detailed information about configuring a multi-project build in Gradle can be found
* in the user manual at https://docs.gradle.org/7.2/userguide/multi_project_builds.html
*/
rootProject.name = 'OpenCraft'
include('app')

@ -0,0 +1,41 @@
package ity.opencraft;
import ity.opencraft.io.Screen;
import ity.opencraft.io.Window;
import ity.opencraft.physics.PhysicsManager;
import ity.opencraft.render.Renderer;
import ity.opencraft.util.TextureLoader;
import ity.opencraft.backends.Backend;
import ity.opencraft.backends.BackendScanner;
import ity.opencraft.backends.CurrentBackend;
import ity.opencraft.io.Windows;
import org.lwjgl.glfw.GLFW;
public class Main {
public static void main(String[] args) {
BackendScanner.scan();
CurrentBackend.backend = Backend.GL;
Screen.init();
Window win = new Window();
win.init(1000, 1000, "OpenCraft v0.0.1");
win.select();
win.setCursor(GLFW.GLFW_CURSOR_DISABLED);
Screen.put(Windows.MAIN.toString(), win);
TextureLoader.init();
Renderer.init();
long mOld = System.currentTimeMillis();
while(!win.shouldClose()) {
long delta = System.currentTimeMillis() - mOld;
mOld = System.currentTimeMillis();
PhysicsManager.update(delta);
Renderer.render(delta);
win.swap();
GLFW.glfwPollEvents();
}
for(Thread worker : Renderer.worldGenWorkers) {
worker.stop();
}
}
}

@ -0,0 +1,6 @@
package ity.opencraft.backends;
public enum Backend {
GL,
GLES
}

@ -0,0 +1,8 @@
package ity.opencraft.backends;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
public @interface BackendImpl {
Backend value();
}

@ -0,0 +1,52 @@
package ity.opencraft.backends;
import org.reflections.Reflections;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
public class BackendScanner {
public static Map<Class<? extends Backended>, Map<Backend, Supplier<Backended>>> backends = new HashMap<>();
public static void scan() {
ArrayList<Class<? extends Backended>> subT = new ArrayList<>();
for(Package p : Package.getPackages()) {
Set<Class<? extends Backended>> subs = new Reflections(p.getName()).getSubTypesOf(Backended.class);
for(Class<? extends Backended> sub : subs) {
if(!subT.contains(sub)) subT.add(sub);
}
}
for(Class<? extends Backended> cls : subT) {
if(cls.getSuperclass() == Backended.class) {
for(Class<? extends Backended> clsB : subT) {
if(clsB.getSuperclass() == cls) {
if(clsB.isAnnotationPresent(BackendImpl.class)) {
Map<Backend, Supplier<Backended>> bck = null;
Backend backend = clsB.getAnnotation(BackendImpl.class).value();
Supplier<Backended> backendImpl = (() -> {
try {
return clsB.getConstructor().newInstance();
} catch (InstantiationException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
});
if(!backends.containsKey(cls)) {
bck = new HashMap<>();
bck.put(backend, backendImpl);
backends.put(cls, bck);
}
else {
bck = backends.get(cls);
bck.put(backend, backendImpl);
}
}
}
}
}
}
}
}

@ -0,0 +1,14 @@
package ity.opencraft.backends;
public class Backended {
protected Backended backend;
public Backended() {
if(this.getClass().getSuperclass() == Backended.class) {
this.backend = BackendScanner.backends.get(this.getClass()).get(CurrentBackend.backend).get();
if(this.backend == null) {
throw new IllegalStateException("Backend not implemented: "+CurrentBackend.backend);
}
}
}
}

@ -0,0 +1,5 @@
package ity.opencraft.backends;
public class CurrentBackend {
public static Backend backend;
}

@ -0,0 +1,30 @@
package ity.opencraft.client;
import ity.opencraft.util.MatrixUtils;
import org.joml.Matrix3f;
import org.joml.Matrix4f;
import org.joml.Vector3f;
public class ViewportCamera {
public static Vector3f pos = new Vector3f(0,0,0);
// This is an angle...
public static Vector3f rot = new Vector3f(0,0,180);
public static Matrix4f getViewportMatrix() {
return MatrixUtils.viewMatrix(ViewportCamera.pos, ViewportCamera.rot);
}
public static Vector3f transformVector(Vector3f vec) {
Vector3f rotation = new Vector3f(rot).mul((float) (Math.PI / 180.0f), new Vector3f());
return new Vector3f(vec).mul(new Matrix3f().rotateXYZ(rotation).invert());
}
public static Vector3f forwardVector() {
return transformVector(new Vector3f(0, 0,-1));
}
public static Vector3f rightVector() {
return transformVector(new Vector3f(1, 0,0));
}
}

@ -0,0 +1,42 @@
package ity.opencraft.data;
import ity.opencraft.backends.Backended;
import org.joml.Vector2f;
import org.joml.Vector3f;
import java.util.List;
public class BlockMesh extends Backended {
public void init() {
((BlockMesh)this.backend).init();
}
public void upload(float[] pos, float[] norm, float[] texIds, float[] light, int[] inds) {
((BlockMesh)this.backend).upload(pos, norm, texIds, light, inds);
}
public void upload(List<Vector3f> pos, List<Vector3f> norm, List<Vector2f> texIds, List<Float> light, List<Integer> inds) {
((BlockMesh)this.backend).upload(pos, norm, texIds, light, inds);
}
public void bind() {
((BlockMesh)this.backend).bind();
}
public void render() {
((BlockMesh)this.backend).render();
}
public void unbind() {
((BlockMesh)this.backend).unbind();
}
public void destroy(){
((BlockMesh)this.backend).destroy();
}
// Used for debug purposes.
public int getId() {
return ((BlockMesh)this.backend).getId();
}
}

@ -0,0 +1,39 @@
package ity.opencraft.data;
import org.joml.Vector2f;
import org.joml.Vector3f;
import java.util.ArrayList;
import java.util.List;
public class BlockMeshData {
public ArrayList<Vector3f> pos;
public ArrayList<Vector3f> norms;
public ArrayList<Vector2f> texIds;
public ArrayList<Float> light;
public ArrayList<Integer> inds;
public BlockMeshData(ArrayList<Vector3f> pos, ArrayList<Vector3f> norms, ArrayList<Vector2f> texIds, ArrayList<Float> light, ArrayList<Integer> inds) {
this.pos = pos;
this.norms = norms;
this.texIds = texIds;
this.light = light;
this.inds = inds;
}
public void free() {
if(pos == null) return;
pos.clear();
pos = null;
norms.clear();
norms = null;
texIds.clear();
texIds = null;
light.clear();
light = null;
inds.clear();
inds = null;
}
}

@ -0,0 +1,161 @@
package ity.opencraft.data;
import ity.opencraft.backends.Backend;
import ity.opencraft.backends.BackendImpl;
import org.joml.Vector2f;
import org.joml.Vector3f;
import java.util.List;
import static org.lwjgl.opengl.GL30.*;
@BackendImpl(Backend.GL)
public class BlockMeshGL extends BlockMesh {
private int vaoID;
private int pboID;
private int nboID;
private int tboID;
private int lboID;
private int iboID;
private int length;
@Override
public void init() {
vaoID = glGenVertexArrays();
glBindVertexArray(vaoID);
pboID = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, pboID);
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
nboID = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, nboID);
glVertexAttribPointer(1, 3, GL_FLOAT, false, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
tboID = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, tboID);
glVertexAttribPointer(2, 2, GL_FLOAT, false, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
lboID = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, lboID);
glVertexAttribPointer(3, 1, GL_FLOAT, false, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
iboID = glGenBuffers();
glBindVertexArray(0);
}
@Override
public void upload(float[] pos, float[] norm, float[] texIds, float[] light, int[] inds) {
this.length = inds.length;
glBindVertexArray(vaoID);
glBindBuffer(GL_ARRAY_BUFFER, pboID);
glBufferData(GL_ARRAY_BUFFER, pos, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, nboID);
glBufferData(GL_ARRAY_BUFFER, norm, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, tboID);
glBufferData(GL_ARRAY_BUFFER, texIds, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, lboID);
glBufferData(GL_ARRAY_BUFFER, light, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, inds, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
@Override
public void upload(List<Vector3f> pos, List<Vector3f> norm, List<Vector2f> texIds, List<Float> light, List<Integer> inds) {
float[] pBuf = new float[pos.size() * 3];
for(int i = 0; i < pos.size(); i++) {
pBuf[i*3] = pos.get(i).x;
pBuf[i*3+1] = pos.get(i).y;
pBuf[i*3+2] = pos.get(i).z;
}
float[] nBuf = new float[norm.size() * 3];
for(int i = 0; i < norm.size(); i++) {
nBuf[i*3] = norm.get(i).x;
nBuf[i*3+1] = norm.get(i).y;
nBuf[i*3+2] = norm.get(i).z;
}
float[] tBuf = new float[texIds.size() * 2];
for(int i = 0; i < texIds.size(); i++) {
tBuf[i*2] = texIds.get(i).x;
tBuf[i*2+1] = texIds.get(i).y;
}
float[] lBuf = new float[light.size()];
for(int i = 0; i < light.size(); i++) {
lBuf[i] = light.get(i);
}
int[] iBuf = new int[inds.size()];
for(int i = 0; i < inds.size(); i++) {
iBuf[i] = inds.get(i);
}
upload(pBuf, nBuf, tBuf, lBuf, iBuf);
}
@Override
public void bind() {
glBindVertexArray(vaoID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboID);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glEnableVertexAttribArray(3);
}
@Override
public void render() {
// TODO this is a bit hacky, idk...
if(this.length > 0)
glDrawElements(GL_TRIANGLES, this.length, GL_UNSIGNED_INT, 0);
}
@Override
public void unbind() {
glBindVertexArray(0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
glDisableVertexAttribArray(3);
}
@Override
public void destroy() {
glDeleteVertexArrays(vaoID);
glDeleteBuffers(pboID);
glDeleteBuffers(nboID);
glDeleteBuffers(tboID);
glDeleteBuffers(lboID);
glDeleteBuffers(iboID);
}
@Override
public int getId() {
return vaoID;
}
}

@ -0,0 +1,28 @@
package ity.opencraft.data;
public class BlockMeshHandle {
public float timer;
public BlockMesh mesh;
private BlockMeshPool pool;
public BlockMeshData data;
public BlockMeshHandle(BlockMeshPool pool, BlockMeshData data) {
this.pool = pool;
this.data = data;
}
public void init() {
mesh = new BlockMesh();
mesh.init();
}
public void upload() {
mesh.upload(data.pos, data.norms, data.texIds, data.light, data.inds);
}
public void free() {
pool.free.add(this);
data.free();
data = null;
}
}

@ -0,0 +1,26 @@
package ity.opencraft.data;
import java.util.ArrayList;
public class BlockMeshPool {
public final ArrayList<BlockMeshHandle> free = new ArrayList<>();
public BlockMeshHandle make(BlockMeshData data) {
if(free.size() > 0) {
BlockMeshHandle handle = free.remove(free.size()-1);
handle.data = data;
handle.upload();
return handle;
} else {
BlockMeshHandle handle = new BlockMeshHandle(this, data);
handle.init();
handle.upload();
return handle;
}
}
// TODO implement pool GC
public void garbageCollectUpdate(float delta) {
}
}

@ -0,0 +1,32 @@
package ity.opencraft.data;
import ity.opencraft.backends.Backended;
import org.joml.Vector3f;
import java.util.List;
public class IndexedMesh extends Backended {
public void init() {
((IndexedMesh)this.backend).init();
}
public void upload(float[] pos, int[] inds) {
((IndexedMesh)this.backend).upload(pos, inds);
}
public void upload(List<Vector3f> pos, List<Integer> inds) {
((IndexedMesh)this.backend).upload(pos, inds);
}
public void bind() {
((IndexedMesh)this.backend).bind();
}
public void render() {
((IndexedMesh)this.backend).render();
}
public void unbind() {
((IndexedMesh)this.backend).unbind();
}
}

@ -0,0 +1,82 @@
package ity.opencraft.data;
import ity.opencraft.backends.Backend;
import ity.opencraft.backends.BackendImpl;
import org.joml.Vector3f;
import static org.lwjgl.opengl.GL30.*;
import java.util.List;
@BackendImpl(Backend.GL)
public class IndexedMeshGL extends IndexedMesh {
private int vaoID;
private int pboID;
private int iboID;
private int length;
public void init() {
vaoID = glGenVertexArrays();
glBindVertexArray(vaoID);
pboID = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, pboID);
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
iboID = glGenBuffers();
glBindVertexArray(0);
}
public void upload(float[] pos, int[] inds) {
this.length = inds.length;
glBindVertexArray(vaoID);
glBindBuffer(GL_ARRAY_BUFFER, pboID);
glBufferData(GL_ARRAY_BUFFER, pos, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, inds, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
public void upload(List<Vector3f> pos, List<Integer> inds) {
float[] pBuf = new float[pos.size() * 3];
for(int i = 0; i < pos.size(); i++) {
pBuf[i*3] = pos.get(i).x;
pBuf[i*3+1] = pos.get(i).y;
pBuf[i*3+2] = pos.get(i).z;
}
int[] iBuf = new int[inds.size()];
for(int i = 0; i < inds.size(); i++) {
iBuf[i] = inds.get(i);
}
upload(pBuf, iBuf);
}
public void bind() {
glBindVertexArray(vaoID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboID);
glEnableVertexAttribArray(0);
}
public void render() {
glDrawElements(GL_TRIANGLES, this.length, GL_UNSIGNED_INT, 0);
}
public void unbind() {
glBindVertexArray(0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(0);
}
}

@ -0,0 +1,19 @@
package ity.opencraft.data;
import ity.opencraft.backends.Backended;
public class Texture extends Backended {
public int id;
public int w;
public int h;
public int c;
public void bind() {
((Texture)this.backend).id = this.id;
((Texture)this.backend).bind();
}
public void unbind() {
((Texture)this.backend).unbind();
}
}

@ -0,0 +1,18 @@
package ity.opencraft.data;
import ity.opencraft.backends.Backend;
import ity.opencraft.backends.BackendImpl;
import org.lwjgl.opengl.GL11;
@BackendImpl(Backend.GL)
public class TextureGL extends Texture {
@Override
public void bind() {
GL11.glBindTexture(GL11.GL_TEXTURE_2D, id);
}
@Override
public void unbind() {
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
}
}

@ -0,0 +1,9 @@
package ity.opencraft.io;
import ity.opencraft.backends.Backended;
public class BackendCapabilities extends Backended {
void initCaps() {
((BackendCapabilities)this.backend).initCaps();
}
}

@ -0,0 +1,13 @@
package ity.opencraft.io;
import ity.opencraft.backends.Backend;
import ity.opencraft.backends.BackendImpl;
import org.lwjgl.opengl.GL;
@BackendImpl(Backend.GL)
public class BackendCapabilitiesGL extends BackendCapabilities {
@Override
void initCaps() {
GL.createCapabilities();
}
}

@ -0,0 +1,18 @@
package ity.opencraft.io;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWKeyCallback;
public class Keyboard {
private final boolean[] keys = new boolean[GLFW.GLFW_KEY_LAST];
public void init(long wID) {
GLFW.glfwSetKeyCallback(wID, new GLFWKeyCallback() {
public void invoke(long window, int key, int scancode, int action, int mods) {
keys[key] = (action != GLFW.GLFW_RELEASE);
}
});
}
public boolean isKeyDown(int key) {
return keys[key];
}
}

@ -0,0 +1,32 @@
package ity.opencraft.io;
import org.joml.Vector2f;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWCursorPosCallback;
import org.lwjgl.glfw.GLFWKeyCallback;
import org.lwjgl.glfw.GLFWMouseButtonCallback;
public class Mouse {
private final Vector2f cursorPos = new Vector2f();
private final boolean[] buttons = new boolean[GLFW.GLFW_MOUSE_BUTTON_LAST];
public void init(long wID) {
GLFW.glfwSetCursorPosCallback(wID, new GLFWCursorPosCallback() {
public void invoke(long window, double xPos, double yPos) {
cursorPos.set(xPos, yPos);
}
});
GLFW.glfwSetMouseButtonCallback(wID, new GLFWMouseButtonCallback() {
public void invoke(long window, int button, int action, int mods) {
buttons[button] = (action != GLFW.GLFW_RELEASE);
}
});
}
public Vector2f getCursorPos() {
return new Vector2f(cursorPos);
}
public boolean isButtonPressed(int button) {
return buttons[button];
}
}

@ -0,0 +1,19 @@
package ity.opencraft.io;
import org.lwjgl.glfw.GLFW;
import java.util.HashMap;
public class Screen {
public static HashMap<String, Window> windows = new HashMap<>();
public static void init() {
GLFW.glfwInit();
}
public static void put(String name, Window value) {
windows.put(name, value);
}
public static Window get(String name) {
return windows.get(name);
}
}

@ -0,0 +1,73 @@
package ity.opencraft.io;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWWindowSizeCallback;
import static org.lwjgl.opengl.GL11.glViewport;
import static org.lwjgl.system.MemoryUtil.NULL;
public class Window {
private long wID;
public boolean maximized;
private Keyboard keyboard;
private Mouse mouse;
public int width, height;
public void init(int sizeX, int sizeY, String title) {
this.width = sizeX; // Ah java...
this.height = sizeY;
wID = GLFW.glfwCreateWindow(width, height, title, NULL, NULL);
GLFW.glfwSetWindowSizeCallback(wID, new GLFWWindowSizeCallback() {
@Override
public void invoke(long window, int w, int h) {
width = w;
height = h;
glViewport(0, 0, w, h);
}
});
this.keyboard = new Keyboard();
keyboard.init(wID);
this.mouse = new Mouse();
mouse.init(wID);
}
public void select() {
GLFW.glfwMakeContextCurrent(wID);
GLFW.glfwSwapInterval(1);
new BackendCapabilities().initCaps();
}
public void swap() {
glViewport(0, 0, width, height);
GLFW.glfwSwapBuffers(wID);
}
public void fullscreen() {
maximized = true;
GLFW.glfwMaximizeWindow(wID);
}
public void restore() {
maximized = false;
GLFW.glfwRestoreWindow(wID);
}
public Keyboard getKeyboard() {
return this.keyboard;
}
public Mouse getMouse() {
return this.mouse;
}
public void setCursor(int cursor) {
GLFW.glfwSetInputMode(wID, GLFW.GLFW_CURSOR, cursor);
}
/**
* @return whether the close button or similar was pressed
*/
public boolean shouldClose() {
return GLFW.glfwWindowShouldClose(wID);
}
}

@ -0,0 +1,10 @@
package ity.opencraft.io;
public enum Windows {
MAIN;
@Override
public String toString() {
return super.toString().toLowerCase();
}
}

@ -0,0 +1,8 @@
package ity.opencraft.mesh;
public enum BlockModelType {
CUBE,
CROSS,
WATER
}

@ -0,0 +1,124 @@
package ity.opencraft.mesh;
import ity.opencraft.world.Chunk;
import ity.opencraft.world.Block;
import ity.opencraft.world.Blocks;
import ity.opencraft.world.World;
import org.joml.Vector2f;
import org.joml.Vector3f;
import org.joml.Vector3i;
import java.util.ArrayList;
import java.util.HashMap;
public class ChunkMesher {
private static Block top(int x, int y, int z) {
if(y == World.sizeY* Chunk.size) return Blocks.AIR;
return World.getBlockAtLazy(x, y+1, z);
}
private static Block bottom(int x, int y, int z) {
if(y == 0) return Blocks.AIR;
return World.getBlockAtLazy(x, y-1, z);
}
private static Block left(int x, int y, int z) {
if(x == 0) return Blocks.AIR;
return World.getBlockAtLazy(x-1, y, z);
}
private static Block right(int x, int y, int z) {
if(x == World.sizeXZ*Chunk.size) return Blocks.AIR;
return World.getBlockAtLazy(x+1, y, z);
}
private static Block back(int x, int y, int z) {
if(z == 0) return Blocks.AIR;
return World.getBlockAtLazy(x, y, z-1);
}
private static Block front(int x, int y, int z) {
if(z == World.sizeXZ*Chunk.size) return Blocks.AIR;
return World.getBlockAtLazy(x, y, z+1);
}
/**
* Fill the given parameters with the mesh of the chunk at the given position
* @param posA The ArrayList to fill with positions
* @param normA The ArrayList tof ill with normals
* @param inxA The ArrayList to fill with indices
* @param texA The ArrayList to fill with texture ids
* @param loc The location of the chunk, in chunk coordinates.
*/
public static void meshChunkWithSolid(ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Integer> inxA, ArrayList<Float> light, Vector3i loc) {
for(int i = 0; i < Chunk.size; i++) {
for(int j = 0; j < Chunk.size; j++) {
for(int k = 0; k < Chunk.size; k++) {
int wPosX = i + loc.x*Chunk.size, wPosY = j + loc.y*Chunk.size, wPosZ = k + loc.z*Chunk.size;
Block block = World.getBlockAtLazy(wPosX, wPosY, wPosZ);
if(block.visible() && !block.isBlend()) {
switch(block.modelType()) {
case CUBE:
CubeMesher.meshCubeWith(posA, normA, texA, inxA, block, new Vector3f(wPosX, wPosY, wPosZ), new CubeSides(
top(wPosX, wPosY, wPosZ).isTransparent(block),
bottom(wPosX, wPosY, wPosZ).isTransparent(block),
left(wPosX, wPosY, wPosZ).isTransparent(block),
right(wPosX, wPosY, wPosZ).isTransparent(block),
back(wPosX, wPosY, wPosZ).isTransparent(block),
front(wPosX, wPosY, wPosZ).isTransparent(block)
), light, new Vector3i(wPosX, wPosY, wPosZ));
break;
case CROSS:
CrossMesher.meshCrossWith(posA, normA, texA, inxA, block, new Vector3f(wPosX, wPosY, wPosZ), light, new Vector3i(wPosX, wPosY, wPosZ));
}
}
}
}
}
}
public static void meshChunkWithBlend(ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Integer> inxA, ArrayList<Float> light, Vector3i loc) {
for(int i = 0; i < Chunk.size; i++) {
for(int j = 0; j < Chunk.size; j++) {
for(int k = 0; k < Chunk.size; k++) {
int wPosX = i + loc.x*Chunk.size, wPosY = j + loc.y*Chunk.size, wPosZ = k + loc.z*Chunk.size;
Block block3;
if(wPosY-1 < 0) {
block3 = Blocks.AIR;
} else {
block3 = World.getBlockAtLazy(wPosX, wPosY-1, wPosZ);
}
Block block = World.getBlockAtLazy(wPosX, wPosY, wPosZ);
Block block2 = World.getBlockAtLazy(wPosX, wPosY+1, wPosZ);
Block block21 = World.getBlockAtLazy(wPosX+1, wPosY, wPosZ);
Block block22 = World.getBlockAtLazy(wPosX, wPosY, wPosZ+1);
Block block23 = World.getBlockAtLazy(wPosX-1, wPosY, wPosZ-1);
Block block24 = World.getBlockAtLazy(wPosX, wPosY, wPosZ);
if(block.visible() && block.isBlend()) {
switch(block.modelType()) {
case WATER:
WaterMesher.meshWaterWith(block2 == Blocks.AIR, block3 == Blocks.WATER, posA, normA, texA, inxA, block, new Vector3f(wPosX, wPosY, wPosZ), new CubeSides(
top(wPosX, wPosY, wPosZ).isTransparent(block),
bottom(wPosX, wPosY, wPosZ).isTransparent(block),
left(wPosX, wPosY, wPosZ).isTransparent(block),
right(wPosX, wPosY, wPosZ).isTransparent(block),
back(wPosX, wPosY, wPosZ).isTransparent(block),
front(wPosX, wPosY, wPosZ).isTransparent(block)
), light, new Vector3i(wPosX, wPosY, wPosZ));
break;
case CUBE:
CubeMesher.meshCubeWith(posA, normA, texA, inxA, block, new Vector3f(wPosX, wPosY, wPosZ), new CubeSides(
top(wPosX, wPosY, wPosZ).isTransparent(block),
bottom(wPosX, wPosY, wPosZ).isTransparent(block),
left(wPosX, wPosY, wPosZ).isTransparent(block),
right(wPosX, wPosY, wPosZ).isTransparent(block),
back(wPosX, wPosY, wPosZ).isTransparent(block),
front(wPosX, wPosY, wPosZ).isTransparent(block)
), light, new Vector3i(wPosX, wPosY, wPosZ));
break;
case CROSS:
CrossMesher.meshCrossWith(posA, normA, texA, inxA, block, new Vector3f(wPosX, wPosY, wPosZ), light, new Vector3i(wPosX, wPosY, wPosZ));
}
}
}
}
}
}
}

@ -0,0 +1,94 @@
package ity.opencraft.mesh;
import ity.opencraft.world.BlockSide;
import ity.opencraft.world.Block;
import ity.opencraft.world.World;
import org.joml.Vector2f;
import org.joml.Vector3f;
import org.joml.Vector3i;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.stream.Collectors;
public class CrossMesher {
public static int atlasSize = 4;
private static void meshFace(ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Integer> inxA, Vector3f loc, Vector2f texOff, ArrayList<Vector3f> facePosA, ArrayList<Vector2f> texId, Vector3f faceNorm, ArrayList<Integer> faceInxA, ArrayList<Float> lightA, Vector3i wPos) {
float light = World.getLightAt(wPos.x, wPos.y, wPos.z);
lightA.addAll(Arrays.asList(light, light, light, light));
int bInx = posA.size();
ArrayList<Vector3f> pos = new ArrayList<>(facePosA.stream().map(p -> p.add(loc)).collect(Collectors.toList()));
ArrayList<Integer> inds = new ArrayList<>(faceInxA.stream().map(i -> i+bInx).collect(Collectors.toList()));
ArrayList<Vector2f> texs = new ArrayList<>(texId.stream().map(v -> v.add(texOff).div(atlasSize,atlasSize)).collect(Collectors.toList()));
posA.addAll(pos);
inxA.addAll(inds);
texA.addAll(texs);
for(Vector3f p : pos) {
normA.add(faceNorm);
}
}
/**
* Fill the given parameters with the mesh of a partial cube, translated.
* @param posA The ArrayList to fill with positions
* @param normA The ArrayList tof ill with normals
* @param inxA The ArrayList to fill with indices
* @param loc The offset by which to offset the generated mesh
*/
public static void meshCrossWith(ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Integer> inxA, Block block, Vector3f loc, ArrayList<Float> lightA, Vector3i wPos) {
meshFace1(posA, normA, texA, inxA, loc, new Vector2f(block.textureIndex(BlockSide.CROSS_1, new Vector3i((int)loc.x, (int)loc.y, (int)loc.z))%atlasSize, block.textureIndex(BlockSide.LEFT, new Vector3i((int)loc.x, (int)loc.y, (int)loc.z))/atlasSize), lightA, wPos);
meshFace2(posA, normA, texA, inxA, loc, new Vector2f(block.textureIndex(BlockSide.CROSS_2, new Vector3i((int)loc.x, (int)loc.y, (int)loc.z))%atlasSize, block.textureIndex(BlockSide.RIGHT, new Vector3i((int)loc.x, (int)loc.y, (int)loc.z))/atlasSize), lightA, wPos);
meshFace3(posA, normA, texA, inxA, loc, new Vector2f(block.textureIndex(BlockSide.CROSS_2, new Vector3i((int)loc.x, (int)loc.y, (int)loc.z))%atlasSize, block.textureIndex(BlockSide.FRONT, new Vector3i((int)loc.x, (int)loc.y, (int)loc.z))/atlasSize), lightA, wPos);
meshFace4(posA, normA, texA, inxA, loc, new Vector2f(block.textureIndex(BlockSide.CROSS_2, new Vector3i((int)loc.x, (int)loc.y, (int)loc.z))%atlasSize, block.textureIndex(BlockSide.BACK, new Vector3i((int)loc.x, (int)loc.y, (int)loc.z))/atlasSize), lightA, wPos);
}
private static void meshFace1(ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Integer> inxA, Vector3f loc, Vector2f texOff, ArrayList<Float> lightA, Vector3i wPos) {
meshFace(posA, normA, texA, inxA, loc, texOff, new ArrayList<>(Arrays.asList(
new Vector3f(-0.5f, 0.5f, 0.5f),
new Vector3f(-0.5f, -0.5f, 0.5f),
new Vector3f( 0.5f, -0.5f, -0.5f),
new Vector3f( 0.5f, 0.5f, -0.5f))), new ArrayList<>(Arrays.asList(
new Vector2f(0f,0f),
new Vector2f(0f,1f),
new Vector2f(1f,1f),
new Vector2f(1f,0f))), new Vector3f(0,0,1), new ArrayList<>(Arrays.asList(0, 1, 2, 2, 3, 0)), lightA, wPos);
}
private static void meshFace2(ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Integer> inxA, Vector3f loc, Vector2f texOff, ArrayList<Float> lightA, Vector3i wPos) {
meshFace(posA, normA, texA, inxA, loc, texOff, new ArrayList<>(Arrays.asList(
new Vector3f(-0.5f, 0.5f, -0.5f),
new Vector3f(-0.5f, -0.5f, -0.5f),
new Vector3f( 0.5f, -0.5f, 0.5f),
new Vector3f( 0.5f, 0.5f, 0.5f))), new ArrayList<>(Arrays.asList(
new Vector2f(0f,0f),
new Vector2f(0f,1f),
new Vector2f(1f,1f),
new Vector2f(1f,0f))), new Vector3f(0,0,-1), new ArrayList<>(Arrays.asList(0, 1, 2, 2, 3, 0)), lightA, wPos);
}
private static void meshFace3(ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Integer> inxA, Vector3f loc, Vector2f texOff, ArrayList<Float> lightA, Vector3i wPos) {
meshFace(posA, normA, texA, inxA, loc, texOff, new ArrayList<>(Arrays.asList(
new Vector3f(-0.5f, 0.5f, 0.5f),
new Vector3f(-0.5f, -0.5f, 0.5f),
new Vector3f( 0.5f, -0.5f, -0.5f),
new Vector3f( 0.5f, 0.5f, -0.5f))), new ArrayList<>(Arrays.asList(
new Vector2f(0f,0f),
new Vector2f(0f,1f),
new Vector2f(1f,1f),
new Vector2f(1f,0f))), new Vector3f(0,0,1), new ArrayList<>(Arrays.asList(2, 1, 0, 0, 3, 2)), lightA, wPos);
}
private static void meshFace4(ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Integer> inxA, Vector3f loc, Vector2f texOff, ArrayList<Float> lightA, Vector3i wPos) {
meshFace(posA, normA, texA, inxA, loc, texOff, new ArrayList<>(Arrays.asList(
new Vector3f(-0.5f, 0.5f, -0.5f),
new Vector3f(-0.5f, -0.5f, -0.5f),
new Vector3f( 0.5f, -0.5f, 0.5f),
new Vector3f( 0.5f, 0.5f, 0.5f))), new ArrayList<>(Arrays.asList(
new Vector2f(0f,0f),
new Vector2f(0f,1f),
new Vector2f(1f,1f),
new Vector2f(1f,0f))), new Vector3f(0,0,-1), new ArrayList<>(Arrays.asList(2, 1, 0, 0, 3, 2)), lightA, wPos);
}
}

@ -0,0 +1,157 @@
package ity.opencraft.mesh;
import ity.opencraft.world.BlockSide;
import ity.opencraft.world.Block;
import ity.opencraft.world.World;
import org.joml.Vector2f;
import org.joml.Vector3f;
import org.joml.Vector3i;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
import java.util.stream.Collectors;
public class CubeMesher {
public static int atlasSize = 4;
private static ArrayList<Vector2f> rotateLeftOnce(ArrayList<Vector2f> inp) {
ArrayList<Vector2f> out = new ArrayList<>();
for(int i = 1; i < inp.size(); i++) {
out.add(inp.get(i));
}
out.add(inp.get(0));
return out;
}
private static ArrayList<Vector2f> rotateLeft(ArrayList<Vector2f> inp, int n) {
for(int i = 0; i < n; i++) {
inp = rotateLeftOnce(inp);
}
return inp;
}
private static void meshFace(boolean randRotate, ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Integer> inxA, Vector3f loc, Vector2f texOff, ArrayList<Vector3f> facePosA, ArrayList<Vector2f> texId, Vector3f faceNorm, ArrayList<Integer> faceInxA) {
Random rand = new Random(loc.hashCode());
int bInx = posA.size();
ArrayList<Vector3f> pos = new ArrayList<>(facePosA.stream().map(p -> p.add(loc)).collect(Collectors.toList()));
ArrayList<Integer> inds = new ArrayList<>(faceInxA.stream().map(i -> i+bInx).collect(Collectors.toList()));
ArrayList<Vector2f> texs = new ArrayList<>(texId.stream().map(v -> v.add(texOff).div(atlasSize,atlasSize)).collect(Collectors.toList()));
posA.addAll(pos);
inxA.addAll(inds);
if(randRotate) {
texA.addAll(rotateLeft(texs, rand.nextInt(4)));
} else {
texA.addAll(texs);
}
for(Vector3f p : pos) {
normA.add(faceNorm);
}
}
private static void meshFaceFront(boolean randFace, ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Float> lightA, ArrayList<Integer> inxA, Vector3f loc, Vector2f texOff, Vector3i wPos) {
float light = World.getLightAt(wPos.x, wPos.y, wPos.z+1);
lightA.addAll(Arrays.asList(light, light, light, light));
meshFace(randFace, posA, normA, texA, inxA, loc, texOff, new ArrayList<>(Arrays.asList(
new Vector3f(-0.5f, 0.5f, 0.5f),
new Vector3f(-0.5f, -0.5f, 0.5f),
new Vector3f( 0.5f, -0.5f, 0.5f),
new Vector3f( 0.5f, 0.5f, 0.5f))), new ArrayList<>(Arrays.asList(
new Vector2f(0f,0f),
new Vector2f(0f,1f),
new Vector2f(1f,1f),
new Vector2f(1f,0f))), new Vector3f(0,0,1), new ArrayList<>(Arrays.asList(0, 1, 2, 2, 3, 0)));
}
private static void meshFaceBack(boolean randFace, ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Float> lightA, ArrayList<Integer> inxA, Vector3f loc, Vector2f texOff, Vector3i wPos) {
float light = World.getLightAt(wPos.x, wPos.y, wPos.z-1);
lightA.addAll(Arrays.asList(light, light, light, light));
meshFace(randFace, posA, normA, texA, inxA, loc, texOff, new ArrayList<>(Arrays.asList(
new Vector3f(-0.5f, 0.5f, -0.5f),
new Vector3f(-0.5f, -0.5f, -0.5f),
new Vector3f( 0.5f, -0.5f, -0.5f),
new Vector3f( 0.5f, 0.5f, -0.5f))), new ArrayList<>(Arrays.asList(
new Vector2f(0f,0f),
new Vector2f(0f,1f),
new Vector2f(1f,1f),
new Vector2f(1f,0f))), new Vector3f(0,0,-1), new ArrayList<>(Arrays.asList(2, 1, 0, 0, 3, 2)));
}
private static void meshFaceLeft(boolean randFace, ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Float> lightA, ArrayList<Integer> inxA, Vector3f loc, Vector2f texOff, Vector3i wPos) {
float light = World.getLightAt(wPos.x-1, wPos.y, wPos.z);
lightA.addAll(Arrays.asList(light, light, light, light));
meshFace(randFace, posA, normA, texA, inxA, loc, texOff, new ArrayList<>(Arrays.asList(
new Vector3f(-0.5f, 0.5f, -0.5f),
new Vector3f(-0.5f, -0.5f, -0.5f),
new Vector3f(-0.5f, -0.5f, 0.5f),
new Vector3f(-0.5f, 0.5f, 0.5f))), new ArrayList<>(Arrays.asList(
new Vector2f(0f,0f),
new Vector2f(0f,1f),
new Vector2f(1f,1f),
new Vector2f(1f,0f))), new Vector3f(-1,0,0), new ArrayList<>(Arrays.asList(0, 1, 2, 2, 3, 0)));
}
private static void meshFaceRight(boolean randFace, ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Float> lightA, ArrayList<Integer> inxA, Vector3f loc, Vector2f texOff, Vector3i wPos) {
float light = World.getLightAt(wPos.x+1, wPos.y, wPos.z);
lightA.addAll(Arrays.asList(light, light, light, light));
meshFace(randFace, posA, normA, texA, inxA, loc, texOff, new ArrayList<>(Arrays.asList(
new Vector3f( 0.5f, 0.5f, -0.5f),
new Vector3f( 0.5f, -0.5f, -0.5f),
new Vector3f( 0.5f, -0.5f, 0.5f),
new Vector3f( 0.5f, 0.5f, 0.5f))), new ArrayList<>(Arrays.asList(
new Vector2f(0f,0f),
new Vector2f(0f,1f),
new Vector2f(1f,1f),
new Vector2f(1f,0f))), new Vector3f(1,0,0), new ArrayList<>(Arrays.asList(2, 1, 0, 0, 3, 2)));
}
private static void meshFaceTop(boolean randFace, ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Float> lightA, ArrayList<Integer> inxA, Vector3f loc, Vector2f texOff, Vector3i wPos) {
float light = World.getLightAt(wPos.x, wPos.y+1, wPos.z);
lightA.addAll(Arrays.asList(light, light, light, light));
meshFace(randFace, posA, normA, texA, inxA, loc, texOff, new ArrayList<>(Arrays.asList(
new Vector3f( 0.5f, 0.5f, -0.5f),
new Vector3f(-0.5f, 0.5f, -0.5f),
new Vector3f(-0.5f, 0.5f, 0.5f),
new Vector3f( 0.5f, 0.5f, 0.5f))), new ArrayList<>(Arrays.asList(
new Vector2f(1f,0f),
new Vector2f(0f,0f),
new Vector2f(0f,1f),
new Vector2f(1f,1f))), new Vector3f(0,1,0), new ArrayList<>(Arrays.asList(0, 1, 2, 2, 3, 0)));
}
private static void meshFaceBottom(boolean randFace, ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Float> lightA, ArrayList<Integer> inxA, Vector3f loc, Vector2f texOff, Vector3i wPos) {
float light = World.getLightAt(wPos.x, wPos.y-1, wPos.z);
lightA.addAll(Arrays.asList(light, light, light, light));
meshFace(randFace, posA, normA, texA, inxA, loc, texOff, new ArrayList<>(Arrays.asList(
new Vector3f( 0.5f, -0.5f, -0.5f),
new Vector3f(-0.5f, -0.5f, -0.5f),
new Vector3f(-0.5f, -0.5f, 0.5f),
new Vector3f( 0.5f, -0.5f, 0.5f))), new ArrayList<>(Arrays.asList(
new Vector2f(1f,0f),
new Vector2f(0f,0f),
new Vector2f(0f,1f),
new Vector2f(1f,1f))), new Vector3f(0,-1,0), new ArrayList<>(Arrays.asList(2, 1, 0, 0, 3, 2)));
}
/**
* Fill the given parameters with the mesh of a partial cube, translated.
* @param posA The ArrayList to fill with positions
* @param normA The ArrayList tof ill with normals
* @param inxA The ArrayList to fill with indices
* @param loc The offset by which to offset the generated mesh
* @param sides The sides which to show. True means render, false means do not render.
*/
public static void meshCubeWith(ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Integer> inxA, Block block, Vector3f loc, CubeSides sides, ArrayList<Float> light, Vector3i wPos) {
if(sides.back)
meshFaceBack(block.textureRotate(BlockSide.BACK), posA, normA, texA, light, inxA, loc, new Vector2f(block.textureIndex(BlockSide.BACK, new Vector3i((int)loc.x, (int)loc.y, (int)loc.z))%atlasSize, block.textureIndex(BlockSide.BACK, new Vector3i((int)loc.x, (int)loc.y, (int)loc.z))/atlasSize), wPos);
if(sides.front)
meshFaceFront(block.textureRotate(BlockSide.FRONT), posA, normA, texA, light, inxA, loc, new Vector2f(block.textureIndex(BlockSide.FRONT, new Vector3i((int)loc.x, (int)loc.y, (int)loc.z))%atlasSize, block.textureIndex(BlockSide.FRONT, new Vector3i((int)loc.x, (int)loc.y, (int)loc.z))/atlasSize), wPos);
if(sides.top)
meshFaceTop(block.textureRotate(BlockSide.TOP), posA, normA, texA, light, inxA, loc, new Vector2f(block.textureIndex(BlockSide.TOP, new Vector3i((int)loc.x, (int)loc.y, (int)loc.z))%atlasSize, block.textureIndex(BlockSide.TOP, new Vector3i((int)loc.x, (int)loc.y, (int)loc.z))/atlasSize), wPos);
if(sides.bottom)
meshFaceBottom(block.textureRotate(BlockSide.BOTTOM), posA, normA, texA, light, inxA, loc, new Vector2f(block.textureIndex(BlockSide.BOTTOM, new Vector3i((int)loc.x, (int)loc.y, (int)loc.z))%atlasSize, block.textureIndex(BlockSide.BOTTOM, new Vector3i((int)loc.x, (int)loc.y, (int)loc.z))/atlasSize), wPos);
if(sides.left)
meshFaceLeft(block.textureRotate(BlockSide.LEFT), posA, normA, texA, light, inxA, loc, new Vector2f(block.textureIndex(BlockSide.LEFT, new Vector3i((int)loc.x, (int)loc.y, (int)loc.z))%atlasSize, block.textureIndex(BlockSide.LEFT, new Vector3i((int)loc.x, (int)loc.y, (int)loc.z))/atlasSize), wPos);
if(sides.right)
meshFaceRight(block.textureRotate(BlockSide.RIGHT), posA, normA, texA, light, inxA, loc, new Vector2f(block.textureIndex(BlockSide.RIGHT, new Vector3i((int)loc.x, (int)loc.y, (int)loc.z))%atlasSize, block.textureIndex(BlockSide.RIGHT, new Vector3i((int)loc.x, (int)loc.y, (int)loc.z))/atlasSize), wPos);
}
}

@ -0,0 +1,19 @@
package ity.opencraft.mesh;
public class CubeSides {
public boolean top;
public boolean bottom;
public boolean left;
public boolean right;
public boolean back;
public boolean front;
public CubeSides(boolean top, boolean bottom, boolean left, boolean right, boolean back, boolean front) {
this.top = top;
this.bottom = bottom;
this.left = left;
this.right = right;
this.back = back;
this.front = front;
}
}

@ -0,0 +1,265 @@
package ity.opencraft.mesh;
import ity.opencraft.world.Block;
import ity.opencraft.world.BlockSide;
import ity.opencraft.world.World;
import org.joml.Vector2f;
import org.joml.Vector3f;
import org.joml.Vector3i;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
import java.util.stream.Collectors;
public class WaterMesher {
public static int atlasSize = 4;
private static ArrayList<Vector2f> rotateLeftOnce(ArrayList<Vector2f> inp) {
ArrayList<Vector2f> out = new ArrayList<>();
for(int i = 1; i < inp.size(); i++) {
out.add(inp.get(i));
}
out.add(inp.get(0));
return out;
}
private static ArrayList<Vector2f> rotateLeft(ArrayList<Vector2f> inp, int n) {
for(int i = 0; i < n; i++) {
inp = rotateLeftOnce(inp);
}
return inp;
}
private static void meshFace(boolean randRotate, ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Integer> inxA, Vector3f loc, Vector2f texOff, ArrayList<Vector3f> facePosA, ArrayList<Vector2f> texId, Vector3f faceNorm, ArrayList<Integer> faceInxA) {
Random rand = new Random(loc.hashCode());
int bInx = posA.size();
ArrayList<Vector3f> pos = new ArrayList<>(facePosA.stream().map(p -> p.add(loc)).collect(Collectors.toList()));
ArrayList<Integer> inds = new ArrayList<>(faceInxA.stream().map(i -> i+bInx).collect(Collectors.toList()));
ArrayList<Vector2f> texs = new ArrayList<>(texId.stream().map(v -> v.add(texOff).div(atlasSize,atlasSize)).collect(Collectors.toList()));
posA.addAll(pos);
inxA.addAll(inds);
if(randRotate) {
texA.addAll(rotateLeft(texs, rand.nextInt(4)));
} else {
texA.addAll(texs);
}
for(Vector3f p : pos) {
normA.add(faceNorm);
}
}
private static void meshFaceFront(boolean randFace, ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Float> lightA, ArrayList<Integer> inxA, Vector3f loc, Vector2f texOff, Vector3i wPos) {
float light = World.getLightAt(wPos.x, wPos.y, wPos.z+1);
lightA.addAll(Arrays.asList(light, light, light, light));
meshFace(randFace, posA, normA, texA, inxA, loc, texOff, new ArrayList<>(Arrays.asList(
new Vector3f(-0.5f, 0.45f, 0.5f),
new Vector3f(-0.5f, -0.5f, 0.5f),
new Vector3f( 0.5f, -0.5f, 0.5f),
new Vector3f( 0.5f, 0.45f, 0.5f))), new ArrayList<>(Arrays.asList(
new Vector2f(0f,0f),
new Vector2f(0f,1f),
new Vector2f(1f,1f),
new Vector2f(1f,0f))), new Vector3f(0,0,1), new ArrayList<>(Arrays.asList(0, 1, 2, 2, 3, 0)));
}
private static void meshFaceBack(boolean randFace, ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Float> lightA, ArrayList<Integer> inxA, Vector3f loc, Vector2f texOff, Vector3i wPos) {
float light = World.getLightAt(wPos.x, wPos.y, wPos.z-1);
lightA.addAll(Arrays.asList(light, light, light, light));
meshFace(randFace, posA, normA, texA, inxA, loc, texOff, new ArrayList<>(Arrays.asList(
new Vector3f(-0.5f, 0.45f, -0.5f),
new Vector3f(-0.5f, -0.5f, -0.5f),
new Vector3f( 0.5f, -0.5f, -0.5f),
new Vector3f( 0.5f, 0.45f, -0.5f))), new ArrayList<>(Arrays.asList(
new Vector2f(0f,0f),
new Vector2f(0f,1f),
new Vector2f(1f,1f),
new Vector2f(1f,0f))), new Vector3f(0,0,-1), new ArrayList<>(Arrays.asList(2, 1, 0, 0, 3, 2)));
}
private static void meshFaceLeft(boolean randFace, ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Float> lightA, ArrayList<Integer> inxA, Vector3f loc, Vector2f texOff, Vector3i wPos) {
float light = World.getLightAt(wPos.x-1, wPos.y, wPos.z);
lightA.addAll(Arrays.asList(light, light, light, light));
meshFace(randFace, posA, normA, texA, inxA, loc, texOff, new ArrayList<>(Arrays.asList(
new Vector3f(-0.5f, 0.45f, -0.5f),
new Vector3f(-0.5f, -0.5f, -0.5f),
new Vector3f(-0.5f, -0.5f, 0.5f),
new Vector3f(-0.5f, 0.45f, 0.5f))), new ArrayList<>(Arrays.asList(
new Vector2f(0f,0f),
new Vector2f(0f,1f),
new Vector2f(1f,1f),
new Vector2f(1f,0f))), new Vector3f(-1,0,0), new ArrayList<>(Arrays.asList(0, 1, 2, 2, 3, 0)));
}
private static void meshFaceRight(boolean randFace, ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Float> lightA, ArrayList<Integer> inxA, Vector3f loc, Vector2f texOff, Vector3i wPos) {
float light = World.getLightAt(wPos.x+1, wPos.y, wPos.z);
lightA.addAll(Arrays.asList(light, light, light, light));
meshFace(randFace, posA, normA, texA, inxA, loc, texOff, new ArrayList<>(Arrays.asList(
new Vector3f( 0.5f, 0.45f, -0.5f),
new Vector3f( 0.5f, -0.5f, -0.5f),
new Vector3f( 0.5f, -0.5f, 0.5f),
new Vector3f( 0.5f, 0.45f, 0.5f))), new ArrayList<>(Arrays.asList(
new Vector2f(0f,0f),
new Vector2f(0f,1f),
new Vector2f(1f,1f),
new Vector2f(1f,0f))), new Vector3f(1,0,0), new ArrayList<>(Arrays.asList(2, 1, 0, 0, 3, 2)));
}
private static void meshFaceTop2(boolean randFace, ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Float> lightA, ArrayList<Integer> inxA, Vector3f loc, Vector2f texOff, Vector3i wPos) {
float light = World.getLightAt(wPos.x, wPos.y+1, wPos.z);
lightA.addAll(Arrays.asList(light, light, light, light));
meshFace(randFace, posA, normA, texA, inxA, loc, texOff, new ArrayList<>(Arrays.asList(
new Vector3f( 0.5f, 0.5f, -0.5f),
new Vector3f(-0.5f, 0.5f, -0.5f),
new Vector3f(-0.5f, 0.5f, 0.5f),
new Vector3f( 0.5f, 0.5f, 0.5f))), new ArrayList<>(Arrays.asList(
new Vector2f(1f,0f),
new Vector2f(0f,0f),
new Vector2f(0f,1f),
new Vector2f(1f,1f))), new Vector3f(0,1,0), new ArrayList<>(Arrays.asList(0, 1, 2, 2, 3, 0)));
}
private static void meshFaceTop(boolean randFace, ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Float> lightA, ArrayList<Integer> inxA, Vector3f loc, Vector2f texOff, Vector3i wPos) {
float light = World.getLightAt(wPos.x, wPos.y+1, wPos.z);
lightA.addAll(Arrays.asList(light, light, light, light));
meshFace(randFace, posA, normA, texA, inxA, loc, texOff, new ArrayList<>(Arrays.asList(
new Vector3f( 0.5f, 0.45f, -0.5f),
new Vector3f(-0.5f, 0.45f, -0.5f),
new Vector3f(-0.5f, 0.45f, 0.5f),
new Vector3f( 0.5f, 0.45f, 0.5f))), new ArrayList<>(Arrays.asList(
new Vector2f(1f,0f),
new Vector2f(0f,0f),
new Vector2f(0f,1f),
new Vector2f(1f,1f))), new Vector3f(0,1,0), new ArrayList<>(Arrays.asList(0, 1, 2, 2, 3, 0)));
}
private static void meshFaceBottom(boolean randFace, ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Float> lightA, ArrayList<Integer> inxA, Vector3f loc, Vector2f texOff, Vector3i wPos) {
float light = World.getLightAt(wPos.x, wPos.y-1, wPos.z);
lightA.addAll(Arrays.asList(light, light, light, light));
meshFace(randFace, posA, normA, texA, inxA, loc, texOff, new ArrayList<>(Arrays.asList(
new Vector3f( 0.5f, -0.5f, -0.5f),
new Vector3f(-0.5f, -0.5f, -0.5f),
new Vector3f(-0.5f, -0.5f, 0.5f),
new Vector3f( 0.5f, -0.5f, 0.5f))), new ArrayList<>(Arrays.asList(
new Vector2f(1f,0f),
new Vector2f(0f,0f),
new Vector2f(0f,1f),
new Vector2f(1f,1f))), new Vector3f(0,-1,0), new ArrayList<>(Arrays.asList(2, 1, 0, 0, 3, 2)));
}
private static void meshFaceBottom3(boolean randFace, ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Float> lightA, ArrayList<Integer> inxA, Vector3f loc, Vector2f texOff, Vector3i wPos) {
float light = World.getLightAt(wPos.x, wPos.y-1, wPos.z);
lightA.addAll(Arrays.asList(light, light, light, light));
meshFace(randFace, posA, normA, texA, inxA, loc, texOff, new ArrayList<>(Arrays.asList(
new Vector3f( 0.5f, -0.5f-0.0625f, -0.5f),
new Vector3f(-0.5f, -0.5f-0.0625f, -0.5f),
new Vector3f(-0.5f, -0.5f-0.0625f, 0.5f),
new Vector3f( 0.5f, -0.5f-0.0625f, 0.5f))), new ArrayList<>(Arrays.asList(
new Vector2f(1f,0f),
new Vector2f(0f,0f),
new Vector2f(0f,1f),
new Vector2f(1f,1f))), new Vector3f(0,-1,0), new ArrayList<>(Arrays.asList(2, 1, 0, 0, 3, 2)));
}
private static void meshFaceFront2(boolean randFace, ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Float> lightA, ArrayList<Integer> inxA, Vector3f loc, Vector2f texOff, Vector3i wPos) {
float light = World.getLightAt(wPos.x, wPos.y, wPos.z+1);
lightA.addAll(Arrays.asList(light, light, light, light));
meshFace(randFace, posA, normA, texA, inxA, loc, texOff, new ArrayList<>(Arrays.asList(
new Vector3f(-0.5f, 0.5f, 0.5f),
new Vector3f(-0.5f, -0.5f, 0.5f),
new Vector3f( 0.5f, -0.5f, 0.5f),
new Vector3f( 0.5f, 0.5f, 0.5f))), new ArrayList<>(Arrays.asList(
new Vector2f(0f,0f),
new Vector2f(0f,1f),
new Vector2f(1f,1f),
new Vector2f(1f,0f))), new Vector3f(0,0,1), new ArrayList<>(Arrays.asList(0, 1, 2, 2, 3, 0)));
}
private static void meshFaceBack2(boolean randFace, ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Float> lightA, ArrayList<Integer> inxA, Vector3f loc, Vector2f texOff, Vector3i wPos) {
float light = World.getLightAt(wPos.x, wPos.y, wPos.z-1);
lightA.addAll(Arrays.asList(light, light, light, light));
meshFace(randFace, posA, normA, texA, inxA, loc, texOff, new ArrayList<>(Arrays.asList(
new Vector3f(-0.5f, 0.5f, -0.5f),
new Vector3f(-0.5f, -0.5f, -0.5f),
new Vector3f( 0.5f, -0.5f, -0.5f),
new Vector3f( 0.5f, 0.5f, -0.5f))), new ArrayList<>(Arrays.asList(
new Vector2f(0f,0f),
new Vector2f(0f,1f),
new Vector2f(1f,1f),
new Vector2f(1f,0f))), new Vector3f(0,0,-1), new ArrayList<>(Arrays.asList(2, 1, 0, 0, 3, 2)));
}
private static void meshFaceLeft2(boolean randFace, ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Float> lightA, ArrayList<Integer> inxA, Vector3f loc, Vector2f texOff, Vector3i wPos) {
float light = World.getLightAt(wPos.x-1, wPos.y, wPos.z);
lightA.addAll(Arrays.asList(light, light, light, light));
meshFace(randFace, posA, normA, texA, inxA, loc, texOff, new ArrayList<>(Arrays.asList(
new Vector3f(-0.5f, 0.5f, -0.5f),
new Vector3f(-0.5f, -0.5f, -0.5f),
new Vector3f(-0.5f, -0.5f, 0.5f),
new Vector3f(-0.5f, 0.5f, 0.5f))), new ArrayList<>(Arrays.asList(
new Vector2f(0f,0f),
new Vector2f(0f,1f),
new Vector2f(1f,1f),
new Vector2f(1f,0f))), new Vector3f(-1,0,0), new ArrayList<>(Arrays.asList(0, 1, 2, 2, 3, 0)));
}
private static void meshFaceRight2(boolean randFace, ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Float> lightA, ArrayList<Integer> inxA, Vector3f loc, Vector2f texOff, Vector3i wPos) {
float light = World.getLightAt(wPos.x+1, wPos.y, wPos.z);
lightA.addAll(Arrays.asList(light, light, light, light));
meshFace(randFace, posA, normA, texA, inxA, loc, texOff, new ArrayList<>(Arrays.asList(
new Vector3f( 0.5f, 0.5f, -0.5f),
new Vector3f( 0.5f, -0.5f, -0.5f),
new Vector3f( 0.5f, -0.5f, 0.5f),
new Vector3f( 0.5f, 0.5f, 0.5f))), new ArrayList<>(Arrays.asList(
new Vector2f(0f,0f),
new Vector2f(0f,1f),
new Vector2f(1f,1f),
new Vector2f(1f,0f))), new Vector3f(1,0,0), new ArrayList<>(Arrays.asList(2, 1, 0, 0, 3, 2)));
}
/**
* Fill the given parameters with the mesh of a partial cube, translated.
* @param posA The ArrayList to fill with positions
* @param normA The ArrayList tof ill with normals
* @param inxA The ArrayList to fill with indices
* @param loc The offset by which to offset the generated mesh
* @param sides The sides which to show. True means render, false means do not render.
*/
public static void meshWaterWith(boolean isUp, boolean isBot, ArrayList<Vector3f> posA, ArrayList<Vector3f> normA, ArrayList<Vector2f> texA, ArrayList<Integer> inxA, Block block, Vector3f loc, CubeSides sides, ArrayList<Float> light, Vector3i wPos) {
if(sides.back) {
if(isUp) {
meshFaceBack(block.textureRotate(BlockSide.BACK), posA, normA, texA, light, inxA, loc, new Vector2f(block.textureIndex(BlockSide.BACK, new Vector3i((int) loc.x, (int) loc.y, (int) loc.z)) % atlasSize, block.textureIndex(BlockSide.BACK, new Vector3i((int) loc.x, (int) loc.y, (int) loc.z)) / atlasSize), wPos);
} else {
meshFaceBack2(block.textureRotate(BlockSide.BACK), posA, normA, texA, light, inxA, loc, new Vector2f(block.textureIndex(BlockSide.BACK, new Vector3i((int) loc.x, (int) loc.y, (int) loc.z)) % atlasSize, block.textureIndex(BlockSide.BACK, new Vector3i((int) loc.x, (int) loc.y, (int) loc.z)) / atlasSize), wPos);
}
} if(sides.front) {
if(isUp) {
meshFaceFront(block.textureRotate(BlockSide.FRONT), posA, normA, texA, light, inxA, loc, new Vector2f(block.textureIndex(BlockSide.FRONT, new Vector3i((int) loc.x, (int) loc.y, (int) loc.z)) % atlasSize, block.textureIndex(BlockSide.FRONT, new Vector3i((int) loc.x, (int) loc.y, (int) loc.z)) / atlasSize), wPos);
} else {
meshFaceFront2(block.textureRotate(BlockSide.FRONT), posA, normA, texA, light, inxA, loc, new Vector2f(block.textureIndex(BlockSide.FRONT, new Vector3i((int) loc.x, (int) loc.y, (int) loc.z)) % atlasSize, block.textureIndex(BlockSide.FRONT, new Vector3i((int) loc.x, (int) loc.y, (int) loc.z)) / atlasSize), wPos);
}
} if(sides.top) {
if(isUp) {
meshFaceTop(block.textureRotate(BlockSide.TOP), posA, normA, texA, light, inxA, loc, new Vector2f(block.textureIndex(BlockSide.TOP, new Vector3i((int) loc.x, (int) loc.y, (int) loc.z)) % atlasSize, block.textureIndex(BlockSide.TOP, new Vector3i((int) loc.x, (int) loc.y, (int) loc.z)) / atlasSize), wPos);
} else {
meshFaceTop2(block.textureRotate(BlockSide.TOP), posA, normA, texA, light, inxA, loc, new Vector2f(block.textureIndex(BlockSide.TOP, new Vector3i((int) loc.x, (int) loc.y, (int) loc.z)) % atlasSize, block.textureIndex(BlockSide.TOP, new Vector3i((int) loc.x, (int) loc.y, (int) loc.z)) / atlasSize), wPos);
}
}
if(sides.bottom) {
meshFaceBottom(block.textureRotate(BlockSide.BOTTOM), posA, normA, texA, light, inxA, loc, new Vector2f(block.textureIndex(BlockSide.BOTTOM, new Vector3i((int) loc.x, (int) loc.y, (int) loc.z)) % atlasSize, block.textureIndex(BlockSide.BOTTOM, new Vector3i((int) loc.x, (int) loc.y, (int) loc.z)) / atlasSize), wPos);
} if(sides.left) {
if(isUp) {
meshFaceLeft(block.textureRotate(BlockSide.LEFT), posA, normA, texA, light, inxA, loc, new Vector2f(block.textureIndex(BlockSide.LEFT, new Vector3i((int) loc.x, (int) loc.y, (int) loc.z)) % atlasSize, block.textureIndex(BlockSide.LEFT, new Vector3i((int) loc.x, (int) loc.y, (int) loc.z)) / atlasSize), wPos);
} else {
meshFaceLeft2(block.textureRotate(BlockSide.LEFT), posA, normA, texA, light, inxA, loc, new Vector2f(block.textureIndex(BlockSide.LEFT, new Vector3i((int) loc.x, (int) loc.y, (int) loc.z)) % atlasSize, block.textureIndex(BlockSide.LEFT, new Vector3i((int) loc.x, (int) loc.y, (int) loc.z)) / atlasSize), wPos);
}
} if(sides.right) {
if(isUp) {
meshFaceRight(block.textureRotate(BlockSide.RIGHT), posA, normA, texA, light, inxA, loc, new Vector2f(block.textureIndex(BlockSide.RIGHT, new Vector3i((int) loc.x, (int) loc.y, (int) loc.z)) % atlasSize, block.textureIndex(BlockSide.RIGHT, new Vector3i((int) loc.x, (int) loc.y, (int) loc.z)) / atlasSize), wPos);
} else {
meshFaceRight2(block.textureRotate(BlockSide.RIGHT), posA, normA, texA, light, inxA, loc, new Vector2f(block.textureIndex(BlockSide.RIGHT, new Vector3i((int) loc.x, (int) loc.y, (int) loc.z)) % atlasSize, block.textureIndex(BlockSide.RIGHT, new Vector3i((int) loc.x, (int) loc.y, (int) loc.z)) / atlasSize), wPos);
}
}
}
}

@ -0,0 +1,17 @@
package ity.opencraft.physics;
import org.joml.Vector3f;
public class Gravity {
public static float GRAVITY = 9.8f;
public static float inAir = 0f;
public static void update(float delta) {
if(PlayerMovement.isOnGround()) {
PlayerVelocity.vel.y = 0.0f;
inAir = 0.0f;
} else {
inAir += delta/1000.0f*GRAVITY;
PlayerVelocity.vel.sub(new Vector3f(0f, inAir*1.5f, 0f));
}
}
}

@ -0,0 +1,14 @@
package ity.opencraft.physics;
public class PhysicsManager {
/**
* Advance all physics calculations
* @param delta The delta time, in seconds.
*/
public static void update(float delta) {
//Gravity.update(delta);
PlayerMovement.update(delta);
PlayerVelocity.update(delta);
}
}

@ -0,0 +1,166 @@
package ity.opencraft.physics;
import ity.opencraft.client.ViewportCamera;
import ity.opencraft.io.Screen;
import ity.opencraft.io.Window;
import ity.opencraft.render.Renderer;
import ity.opencraft.world.Blocks;
import ity.opencraft.world.Chunk;
import ity.opencraft.io.Windows;
import ity.opencraft.world.World;
import org.joml.*;
import org.lwjgl.glfw.GLFW;
import java.lang.Math;
// TODO clean up PlayerMovement, it handles too much
public class PlayerMovement {
private static final float movSpeed = 0.8f;
private static final float jumpSpeed = 2.5f;
private static final float rotSpeed = 0.1f;
private static Vector2f lastMouse;
private static Vector3i playerChunk = new Vector3i();
private static boolean keyLeftIsDown = false;
private static boolean keyRightIsDown = false;
public static boolean moveTo(Vector3f dest) {
if(true || !World.getBlockAt((int)(dest.x+0.5), (int)(dest.y-1.0), (int)(dest.z+0.5)).isSolid() && !World.getBlockAt((int)(dest.x+0.5), (int)(dest.y), (int)(dest.z+0.5)).isSolid()) {
ViewportCamera.pos.set(dest);
return true;
} else return false;
}
public static boolean isOnGround() {
return World.getBlockAt((int)(ViewportCamera.pos.x+0.5), (int)(ViewportCamera.pos.y-1.5), (int)(ViewportCamera.pos.z+0.5)).isSolid();
}
static class WTFException extends RuntimeException {
public WTFException() {
super("WTF just happened??? Did the universe break???");
}
}
private static Vector3i axisAlign(Vector3d in) {
if(Math.abs(in.x) > Math.abs(in.y) && Math.abs(in.x) > Math.abs(in.z)) {
return new Vector3i((int)Math.signum(in.x), 0, 0);
} else if(Math.abs(in.y) > Math.abs(in.x) && Math.abs(in.y) > Math.abs(in.z)) {
return new Vector3i(0, (int)Math.signum(in.y), 0);
} else if(Math.abs(in.z) > Math.abs(in.x) && Math.abs(in.z) > Math.abs(in.y)) {
return new Vector3i(0, 0, (int)Math.signum(in.z));
} else return new Vector3i(0, 0, 0);
}
// TODO: Fix the NaN problem
// Sometimes all three values in pos turn to NaN. rot untested.
/**
* Advance the player movement physics
* @param delta The delta time, in seconds.
*/
public static void update(float delta) {
Vector3i newPlayerChunk = new Vector3i((int)(ViewportCamera.pos.x+(0))/ Chunk.size, (int)(ViewportCamera.pos.y+(0))/Chunk.size, (int)(ViewportCamera.pos.z+(0))/Chunk.size);
if(!newPlayerChunk.equals(playerChunk)) {
Renderer.doChunk();
}
playerChunk = newPlayerChunk;
Window main = Screen.get(Windows.MAIN.toString());
if(main.getKeyboard().isKeyDown(GLFW.GLFW_KEY_F11)) {
if(main.maximized)
main.restore();
else
main.fullscreen();
}
if(lastMouse == null) {
lastMouse = main.getMouse().getCursorPos();
} else {
Vector2f diff = main.getMouse().getCursorPos().sub(lastMouse);
lastMouse = main.getMouse().getCursorPos();
float toX = diff.y*rotSpeed;
ViewportCamera.rot.x += diff.y*rotSpeed;
ViewportCamera.rot.y -= diff.x*rotSpeed;
// Damn bratty rotation! Clamping correction needed!!!
if(ViewportCamera.rot.x < 90.01f) {
ViewportCamera.rot.x = 90.01f;
} else if (ViewportCamera.rot.x > 269.01f) {
ViewportCamera.rot.x = 269.01f;
}
if(ViewportCamera.rot.y > 360) {
ViewportCamera.rot.y = 0;
} else if(ViewportCamera.rot.y < 0) {
ViewportCamera.rot.y = 360;
}
}
Vector3f rightVector = ViewportCamera.rightVector();
Vector3f forwardVector = ViewportCamera.forwardVector();
Vector3f posCopy = new Vector3f();
float mult = 1f;
if(main.getKeyboard().isKeyDown(GLFW.GLFW_KEY_LEFT_SHIFT)) {
mult = 10f;
}
if(main.getKeyboard().isKeyDown(GLFW.GLFW_KEY_S)) {
posCopy.add(new Vector3f(forwardVector.x, 0, forwardVector.z).normalize().mul(-delta*movSpeed*mult));
} else if(main.getKeyboard().isKeyDown(GLFW.GLFW_KEY_W)) {
posCopy.add(new Vector3f(forwardVector.x, forwardVector.y, forwardVector.z).normalize().mul(delta*movSpeed*mult));
} if(main.getKeyboard().isKeyDown(GLFW.GLFW_KEY_A)) {
posCopy.add(new Vector3f(rightVector.x, 0, rightVector.z).normalize().mul(-delta*movSpeed*mult));
} else if(main.getKeyboard().isKeyDown(GLFW.GLFW_KEY_D)) {
posCopy.add(new Vector3f(rightVector.x, 0, rightVector.z).normalize().mul(delta*movSpeed*mult));
}
// if(main.getKeyboard().isKeyDown(GLFW.GLFW_KEY_LEFT_SHIFT)) {
// posCopy.add(new Vector3f(0, 1, 0).mul(-delta*jumpSpeed));
// }
if(main.getMouse().isButtonPressed(GLFW.GLFW_MOUSE_BUTTON_1)) {
if(!keyLeftIsDown) {
keyLeftIsDown = true;
Vector3d tmpPos = new Vector3d(ViewportCamera.pos);
for (int i = 0; i < 1000; i++) {
Vector3i bPos = new Vector3i((int) (tmpPos.x+0.5), (int) (tmpPos.y+0.5), (int) (tmpPos.z+0.5));
if (World.getBlockAt(bPos.x, bPos.y, bPos.z).isSolid()) {
World.setBlockAt(bPos.x, bPos.y, bPos.z, Blocks.AIR);
break;
}
tmpPos.add(new Vector3f(ViewportCamera.forwardVector()).div(100.0f));
}
}
} else {
keyLeftIsDown = false;
}
if(main.getMouse().isButtonPressed(GLFW.GLFW_MOUSE_BUTTON_2)) {
if(!keyRightIsDown) {
keyRightIsDown = true;
Vector3d tmpPos = new Vector3d(ViewportCamera.pos);
for (int i = 0; i < 1000; i++) {
Vector3d cent = new Vector3d(tmpPos.x+0.5, tmpPos.y+0.5, tmpPos.z+0.5);
Vector3i bPos = new Vector3i((int)cent.x, (int)cent.y, (int)cent.z);
if (World.getBlockAt(bPos.x, bPos.y, bPos.z).isSolid()) {
Vector3d dir = new Vector3d(tmpPos).sub(new Vector3d(bPos).add(new Vector3d(0.0)));
Vector3i dirI = axisAlign(dir);
Vector3i bPosO = new Vector3i(bPos).add(dirI);
if(World.getBlockAt(bPosO.x, bPosO.y, bPosO.z).isSolid()) {
System.out.println(dir);
System.out.println(dirI);
} else {
World.setBlockAt(bPosO.x, bPosO.y, bPosO.z, Blocks.STONE);
}
break;
}
tmpPos.add(new Vector3f(ViewportCamera.forwardVector()).div(100.0f));
}
}
} else {
keyRightIsDown = false;
}
if(main.getKeyboard().isKeyDown(GLFW.GLFW_KEY_SPACE)) {
//if(isOnGround()) {
posCopy.add(new Vector3f(0, 1, 0).mul(delta*jumpSpeed));
//}
}
PlayerVelocity.vel.add(posCopy);
}
}

@ -0,0 +1,21 @@
package ity.opencraft.physics;
import ity.opencraft.client.ViewportCamera;
import org.joml.Vector3f;
public class PlayerVelocity {
public static Vector3f vel = new Vector3f();
public static float DRAG = 15f;
public static void update(float delta) {
Vector3f offsetVec = new Vector3f(vel).div(1000.0f).mul(delta);
Vector3f cameraPosVec = new Vector3f(ViewportCamera.pos);
Vector3f moveToVec = new Vector3f(cameraPosVec).add(offsetVec);
boolean res = PlayerMovement.moveTo(moveToVec);
if(!res) {
PlayerMovement.moveTo(new Vector3f(cameraPosVec).add(new Vector3f(0.0f, offsetVec.y, 0.0f)));
}
// https://answers.unity.com/questions/652010/how-drag-is-calculated-by-unity-engine.html
vel.mul(1-(delta/1000.0f) * DRAG);
}
}

@ -0,0 +1,426 @@
package ity.opencraft.render;
import ity.opencraft.data.*;
import ity.opencraft.io.Screen;
import ity.opencraft.io.Window;
import ity.opencraft.mesh.ChunkMesher;
import ity.opencraft.world.Blocks;
import ity.opencraft.world.Chunk;
import ity.opencraft.world.ChunkReference;
import ity.opencraft.backends.Backended;
import ity.opencraft.client.ViewportCamera;
import ity.opencraft.io.Windows;
import ity.opencraft.shader.Shader;
import ity.opencraft.util.MatrixUtils;
import ity.opencraft.util.TextureLoader;
import ity.opencraft.world.World;
import org.joml.Vector2f;
import org.joml.Vector3f;
import org.joml.Vector3i;
import org.lwjgl.opengl.GL11;
import java.lang.reflect.Array;
import java.util.*;
// TODO clean up Renderer, it handles too much
public class Renderer extends Backended {
private static IndexedMesh crosshair;
private static HashMap<Vector3i, ChunkReference> chunks = new HashMap<>();
private static ArrayList<Vector3i> toGen = new ArrayList<>();
private static ArrayList<ChunkReference> toMesh = new ArrayList<>();
private static ArrayList<ChunkReference> toUpload = new ArrayList<>();
private static ArrayList<ChunkReference> toRender = new ArrayList<>();
private static ArrayList<ChunkReference> toFree = new ArrayList<>();
private static HashMap<Vector3i, ChunkReference> toDelete = new HashMap<>();
private static ArrayList<Vector3i> toDeletePos = new ArrayList<>();
private static Shader colorShader;
private static Shader guiShader;
private static Renderer instance;
private static Texture atlas;
private static final BlockMeshPool pool = new BlockMeshPool();
public static ArrayList<Thread> worldGenWorkers = new ArrayList<>();
private static HashMap<String, Long> perfTimes = new HashMap<>();
private static long frameTime;
// TODO: Renderer should not be backend-dependant just for clearing the screen.
public void clearScreen() {
((Renderer)this.backend).clearScreen();
}
public void depthTest() {
((Renderer)this.backend).depthTest();
}
private static final int renderDistance = 4;
private static final int renderDistanceY = 4;
public static void remeshChunk(int x, int y, int z) {
Vector3i vPos = new Vector3i(x, y, z);
ChunkReference chunk = chunks.get(vPos);
if(chunk == null || chunk.getMeshHandle() == null) return;
chunk.getMeshHandle().free();
ArrayList<Vector3f> pos = new ArrayList<>();
ArrayList<Vector3f> norms = new ArrayList<>();
ArrayList<Vector2f> texs = new ArrayList<>();
ArrayList<Float> light = new ArrayList<>();
ArrayList<Integer> inds = new ArrayList<>();
ChunkMesher.meshChunkWithSolid(pos, norms, texs, inds, light, vPos);
BlockMeshHandle mesh = pool.make(new BlockMeshData(pos, norms, texs, light, inds));
chunk.setMeshHandle(mesh);
chunk.setMeshData(mesh.data);
ArrayList<Vector3f> pos2 = new ArrayList<>();
ArrayList<Vector3f> norms2 = new ArrayList<>();
ArrayList<Vector2f> texs2 = new ArrayList<>();
ArrayList<Float> light2 = new ArrayList<>();
ArrayList<Integer> inds2 = new ArrayList<>();
ChunkMesher.meshChunkWithBlend(pos2, norms2, texs2, inds2, light2, vPos);
BlockMeshHandle mesh2 = pool.make(new BlockMeshData(pos2, norms2, texs2, light2, inds2));
chunk.setMeshHandle2(mesh2);
chunk.setMeshData2(mesh2.data);
toDeletePos.remove(vPos);
toDelete.remove(vPos);
for(ChunkReference chk : toRender) {
if(chk.getPos().equals(vPos))
return;
}
toRender.add(chunk);
}
// TODO clean up
public static void doChunk() {
ArrayList<Vector3i> toGenNew = new ArrayList<>();
// TODO do in a circle around the player starting from the center, instead of in strips.
for(int i = 0; i <= renderDistance*2; i++) {
for(int j = renderDistanceY*2; j >= 0 ; j--) {
for (int k = 0; k <= renderDistance*2; k++) {
Vector3i vPos = new Vector3i(((int)(ViewportCamera.pos.x+(0))/Chunk.size)+(i*((i%2)*2-1)/2),
((int)(ViewportCamera.pos.y+(0))/Chunk.size)+(j*((j%2)*2-1)/2),
((int)(ViewportCamera.pos.z+(0))/Chunk.size)+(k*((k%2)*2-1)/2));
if(vPos.x < 0 || vPos.y < 0|| vPos.z < 0) continue;
if(new Vector3f(vPos).sub(new Vector3f(ViewportCamera.pos).div(Chunk.size)).length() > renderDistance) continue;
toGenNew.add(vPos);
}
}
}
for(Vector3i vec : toGenNew) {
if(!toGen.contains(vec)) {
toDeletePos.remove(vec);
toDelete.remove(vec);
if(!chunks.containsKey(vec)) toGen.add(vec);
}
}
// for(Vector3i vec : toGen) {
// if(!toGenNew.contains(vec)) {
// toDeletePos.add(vec);
// }
// }
for(Map.Entry<Vector3i, Chunk> chunkEntry : World.chunks.entrySet()) {
if(!toGenNew.contains(chunkEntry.getKey()) && !toDeletePos.contains(chunkEntry.getKey())) {
Chunk chunk = chunkEntry.getValue();
chunks.put(chunk.pos, World.getChunkAt(chunk.pos));
toDeletePos.add(chunk.pos);
}
}
}
/**
* Initialize the Renderer. Requires everything else to be ready.
*/
public static void init() {
instance = new Renderer();
instance.depthTest();
for(int i = 0; i < 1; i++) {
Thread worker = new Thread(() -> {
while (true) {
beginPerf("worldgen");
worldGenUpdate();
long taken = endPerf("worldgen") / 1000000;
long max = 8;
long toSleep = Math.max(max - taken, 0);
try {
Thread.sleep(toSleep);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}, "WorldGen worker "+i);
worldGenWorkers.add(worker);
}
crosshair = new IndexedMesh();
crosshair.init();
Window win = Screen.get(Windows.MAIN.toString());
float asp = (float)win.height/(float)win.width;
crosshair.upload(new float[] {
0.05f/asp, 0.05f, 0.0f, // top right
0.05f/asp, -0.05f, 0.0f, // bottom right
-0.05f/asp, -0.05f, 0.0f, // bottom left
-0.05f/asp, 0.05f, 0.0f // top left
}, new int[] {
3, 1, 0, // first triangle
3, 2, 1 // second triangle
});
atlas = TextureLoader.load("atlas.png");
Vector3i startPlayerPos = new Vector3i(World.sizeXZ/2,60/Chunk.size,World.sizeXZ/2);
for(int i = startPlayerPos.x; i <= startPlayerPos.x+(renderDistance*2); i++) {
for(int j = startPlayerPos.y; j <= startPlayerPos.y+(renderDistanceY*2); j++) {
for (int k = startPlayerPos.z; k <= startPlayerPos.z+(renderDistance*2); k++) {
if(j < 0) continue;
Vector3i vPos = new Vector3i((i/2*((i%2)*2-1)), (j/2*((j%2)*2-1)), (k/2*((k%2)*2-1)));
if(new Vector3f(startPlayerPos).sub(new Vector3f(ViewportCamera.pos).div(Chunk.size)).length() > renderDistance) continue;
toGen.add(vPos);
}
}
}
colorShader = new Shader();
colorShader.init("color.vs", "color.fs");
guiShader = new Shader();
guiShader.init("gui.vs", "gui.fs");
ViewportCamera.pos.set(World.sizeXZ*Chunk.size/2+Chunk.size/2,80+128-70,World.sizeXZ*Chunk.size/2+Chunk.size/2);
for(Thread worker : worldGenWorkers) {
worker.start();
}
}
private static synchronized void worldGenUpdate() {
if(toGen.isEmpty()) {
System.out.println("Possible deadlock");
return;
}
Vector3i toGenPos = toGen.remove(0);
if(toDelete.remove(toGenPos) != null) return;
if(toDeletePos.remove(toGenPos)) return;
ChunkReference chunk = World.getChunkAt(toGenPos.x, toGenPos.y, toGenPos.z);
if(toRender.contains(chunk)) {
// TODO fix
return;
//throw new IllegalStateException("Trying to generate a chunk that's being rendered");
}
toMesh.add(chunk);
chunks.put(toGenPos, chunk);
}
private static void meshUpdate() {
beginPerf("premesh");
beginPerf("premesh.1");
if(toMesh.isEmpty()) return;
endPerf("premesh.1");
beginPerf("premesh.2");
ChunkReference toMeshChunk = toMesh.remove(0);
if(!toMeshChunk.isChunkGenerated())
System.out.println("Generating a chunk that should've been generated already.");
endPerf("premesh.2");
beginPerf("premesh.3");
if(toRender.contains(toMeshChunk)) {
throw new IllegalStateException("Trying to mesh a chunk that's being rendered");
}
endPerf("premesh.3");
//if(toDelete.containsKey(toMeshChunk.pos)) return;
beginPerf("premesh.4");
BlockMeshData meshData = toMeshChunk.getMeshData();
endPerf("premesh.4");
beginPerf("premesh.5");
if(meshData != null) {
beginPerf("premesh.5.1");
toUpload.add(toMeshChunk);
endPerf("premesh.5.1");
return;
}
endPerf("premesh.5");
endPerf("premesh");
beginPerf("solidmesh");
ArrayList<Vector3f> pos = new ArrayList<>();
ArrayList<Vector3f> norms = new ArrayList<>();
ArrayList<Vector2f> texs = new ArrayList<>();
ArrayList<Float> light = new ArrayList<>();
ArrayList<Integer> inds = new ArrayList<>();
ChunkMesher.meshChunkWithSolid(pos, norms, texs, inds, light, toMeshChunk.getPos());
toMeshChunk.setMeshData(new BlockMeshData(pos, norms, texs, light, inds));
endPerf("solidmesh");
beginPerf("blendmesh");
ArrayList<Vector3f> pos2 = new ArrayList<>();
ArrayList<Vector3f> norms2 = new ArrayList<>();
ArrayList<Vector2f> texs2 = new ArrayList<>();
ArrayList<Float> light2 = new ArrayList<>();
ArrayList<Integer> inds2 = new ArrayList<>();
ChunkMesher.meshChunkWithBlend(pos2, norms2, texs2, inds2, light2, toMeshChunk.getPos());
toMeshChunk.setMeshData2(new BlockMeshData(pos2, norms2, texs2, light2, inds2));
toUpload.add(toMeshChunk);
endPerf("blendmesh");
}
private static void uploadUpdate() {
if(toUpload.isEmpty()) return;
ChunkReference toUploadChunk = toUpload.remove(0);
if(toRender.contains(toUploadChunk)) {
throw new IllegalStateException("Trying to upload a chunk mesh of a chunk that's being rendered");
}
//if(toDelete.containsKey(toUploadChunk.pos)) return;
if(toUploadChunk.getMeshHandle() != null) {
toRender.add(toUploadChunk);
return;
}
toUploadChunk.setMeshHandle(pool.make(toUploadChunk.getMeshData()));
toUploadChunk.getMeshData().free();
toUploadChunk.setMeshData(null);
toUploadChunk.setMeshHandle2(pool.make(toUploadChunk.getMeshData2()));
toUploadChunk.getMeshData2().free();
toUploadChunk.setMeshData2(null);
// toUploadChunk.meshData = null;
// toUploadChunk.meshHandle.data = null;
toRender.add(toUploadChunk);
}
private static void deleteUpdate() {
for(Map.Entry<Vector3i, ChunkReference> pair : toDelete.entrySet()) {
if(!toDeletePos.contains(pair.getKey()))
toDeletePos.add(pair.getKey());
}
for(Vector3i vec : toDeletePos) {
ChunkReference chunk = chunks.remove(vec);
if(chunk != null) {
toUpload.remove(chunk);
if(toUpload.remove(chunk)) throw new IllegalStateException();
toRender.remove(chunk);
if(toRender.remove(chunk)) throw new IllegalStateException();
toMesh.remove(chunk);
if(toMesh.remove(chunk)) throw new IllegalStateException();
toGen.remove(vec);
if(toGen.remove(vec)) throw new IllegalStateException();
toFree.add(chunk);
}
}
}
private static void freeUpdate() {
if(toDelete.isEmpty())
return;
ChunkReference ref = toDelete.remove(0);
Vector3i vec = ref.getPos();
//World.freeChunkAt(vec.x, vec.y, vec.z);
}
private static void beginPerf(String name) {
perfTimes.put(name, System.nanoTime());
}
private static long endPerf(String name) {
long start = perfTimes.get(name);
long end = System.nanoTime();
long timeElapsed = end - start;
System.out.print(name + ": ");
System.out.println(timeElapsed);
return timeElapsed;
}
// TODO: Do not hardcode Windows.MAIN
/**
* Render a frame
* @param delta Delta Time, the time in miliseconds between the last and this frame.
*/
public static void render(long delta) {
beginPerf("frame");
if(delta < 200) delta = 200;
for(int j = 0; j <= 1; j++) {
beginPerf("free");
freeUpdate();
endPerf("free");
beginPerf("delete");
deleteUpdate();
endPerf("delete");
beginPerf("mesh");
meshUpdate();
endPerf("mesh");
beginPerf("upload");
uploadUpdate();
endPerf("upload");
toDelete.clear();
}
// for(int i = 0; i <= (200-delta)/10; i++) {
// new Thread(() -> {
//
// }).run();
// }
beginPerf("framerender");
beginPerf("prepass");
instance.clearScreen();
colorShader.bind();
atlas.bind();
colorShader.setUniform("iRes", new Vector2f(Screen.get(Windows.MAIN.toString()).width, Screen.get(Windows.MAIN.toString()).height));
colorShader.setUniform("view", ViewportCamera.getViewportMatrix());
colorShader.setUniform("proj", MatrixUtils.projectionMatrix(70f, ((float) Screen.get(Windows.MAIN.toString()).width) / ((float) Screen.get(Windows.MAIN.toString()).height), 0.1f, 1024));
colorShader.setUniform("camPos", ViewportCamera.pos);
if(World.getBlockAt((int) ViewportCamera.pos.x, (int) ViewportCamera.pos.y, (int) ViewportCamera.pos.z) == Blocks.WATER) {
colorShader.setUniform("isWater", new Vector2f(1.0f, 2.5f));
colorShader.setUniform("fogColor", new Vector3f(0.1f, 0.1f, 1.0f));
GL11.glClearColor(0.1f, 0.1f, 1.0f, 1.0f);
} else {
colorShader.setUniform("isWater", new Vector2f(5.0f, 1.0f));
colorShader.setUniform("fogColor", new Vector3f(0.5f, 0.85f, 1.0f));
GL11.glClearColor(0.5f, 0.85f, 1.0f, 1.0f);
}
// TODO fix vertical fog
colorShader.setUniform("renderDistance", (float)renderDistance);
endPerf("prepass");
beginPerf("render");
for(int i = 0; i < 2; i++) {
for (ChunkReference chunk : toRender) {
if(i == 0) {
//beginPerf("pass1");
chunk.getMeshHandle().mesh.bind();
chunk.getMeshHandle().mesh.render();
chunk.getMeshHandle().mesh.unbind();
//endPerf("pass1");
} else {
//beginPerf("pass2");
chunk.getMeshHandle2().mesh.bind();
chunk.getMeshHandle2().mesh.render();
chunk.getMeshHandle2().mesh.unbind();
//endPerf("pass2");
}
}
}
endPerf("render");
beginPerf("ui");
colorShader.unbind();
atlas.unbind();
guiShader.bind();
guiShader.setUniform("iRes", new Vector2f(Screen.get(Windows.MAIN.toString()).width, Screen.get(Windows.MAIN.toString()).height));
if(World.getBlockAt((int) ViewportCamera.pos.x, (int) ViewportCamera.pos.y, (int) ViewportCamera.pos.z) == Blocks.WATER) {
guiShader.setUniform("blueAlpha", 0.6f);
GL11.glDisable(GL11.GL_CULL_FACE);
}
else {
guiShader.setUniform("blueAlpha", 0.0f);
GL11.glEnable(GL11.GL_CULL_FACE);
}
Window win = Screen.get(Windows.MAIN.toString());
float asp = (float)win.width/(float)win.height;
crosshair.upload(new float[] {
5f/asp, 5f, 0.0f, // top right
5f/asp, -5f, 0.0f, // bottom right
-5f/asp, -5f, 0.0f, // bottom left
-5f/asp, 5f, 0.0f // top left
}, new int[] {
3, 1, 0, // first triangle
3, 2, 1 // second triangle
});
crosshair.bind();
crosshair.render();
crosshair.unbind();
guiShader.unbind();
endPerf("ui");
//System.out.println(ViewportCamera.pos.x+", "+ViewportCamera.pos.y+", "+ViewportCamera.pos.z);
endPerf("framerender");
long thisFrameTime = endPerf("frame");
if(frameTime == 0)
frameTime = thisFrameTime;
else {
frameTime = (frameTime + thisFrameTime) / 2;
}
System.out.println("frametime: "+frameTime/1000000f);
System.out.println("----------------------");
}
}

@ -0,0 +1,22 @@
package ity.opencraft.render;
import ity.opencraft.backends.Backend;
import ity.opencraft.backends.BackendImpl;
import org.lwjgl.opengl.GL11;
@BackendImpl(Backend.GL)
public class RendererGL extends Renderer {
@Override
public void clearScreen() {
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
}
@Override
public void depthTest() {
GL11.glEnable(GL11.GL_DEPTH_TEST);
GL11.glEnable(GL11.GL_CULL_FACE);
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glClearColor(0.5f, 0.85f, 1.0f, 1.0f);
}
}

@ -0,0 +1,86 @@
package ity.opencraft.shader;
import ity.opencraft.backends.Backended;
import org.joml.Matrix4f;
import org.joml.Vector2f;
import org.joml.Vector3f;
import org.joml.Vector3i;
public class Shader extends Backended {
/**
* Initialize a shader
* @param v The path to the vertex shader
* @param f The path to the fragment shader
* @return self
*/
public Shader init(String v, String f) {
return ((Shader)this.backend).init(v, f);
}
/**
* Set a Matrix4f uniform
* @param name Name of the uniform
* @param matrix The value
*/
public void setUniform(String name, Matrix4f matrix) {
((Shader)this.backend).setUniform(name, matrix);
}
/**
* Set a Vector2f uniform
* @param name Name of the uniform
* @param vector The value
*/
public void setUniform(String name, Vector2f vector) {
((Shader)this.backend).setUniform(name, vector);
}
/**
* Set an int uniform
* @param name Name of the uniform
* @param value The value
*/
public void setUniform(String name, int value) {
((Shader)this.backend).setUniform(name, value);
}
/**
* Set a float uniform
* @param name Name of the uniform
* @param value The value
*/
public void setUniform(String name, float value) {
((Shader)this.backend).setUniform(name, value);}
/**
* Set a Vector3i uniform
* @param name Name of the uniform
* @param vector The value
*/
public void setUniform(String name, Vector3i vector) {
((Shader)this.backend).setUniform(name, vector);
}
/**
* Set a Vector3f uniform
* @param name Name of the uniform
* @param value The value
*/
public void setUniform(String name, Vector3f value) {
((Shader)this.backend).setUniform(name, value);
}
/**
* Bind/Use the shader
*/
public void bind() {
((Shader)this.backend).bind();
}
/**
* Unbind/Unuse the shader
*/
public void unbind() {
((Shader)this.backend).unbind();
}
}

@ -0,0 +1,92 @@
package ity.opencraft.shader;
import ity.opencraft.backends.Backend;
import ity.opencraft.backends.BackendImpl;
import ity.opencraft.util.StringLoader;
import org.joml.Matrix4f;
import org.joml.Vector2f;
import org.joml.Vector3f;
import org.joml.Vector3i;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import org.lwjgl.system.MemoryUtil;
import java.nio.FloatBuffer;
import static org.lwjgl.opengl.GL20.*;
import static org.lwjgl.opengl.GL20.glGetUniformLocation;
@BackendImpl(Backend.GL)
public class ShaderGL extends Shader {
private int pID;
public Shader init(String v, String f) {
pID = GL20.glCreateProgram();
int VID = GL20.glCreateShader(GL20.GL_VERTEX_SHADER);
GL20.glShaderSource(VID, StringLoader.loadResourceAsString("shaders/" + v));
GL20.glCompileShader(VID);
if (GL20.glGetShaderi(VID, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
System.err.println("Vertex Shader: " + GL20.glGetShaderInfoLog(VID));
}
int FID = GL20.glCreateShader(GL20.GL_FRAGMENT_SHADER);
GL20.glShaderSource(FID, StringLoader.loadResourceAsString("shaders/" + f));
GL20.glCompileShader(FID);
if (GL20.glGetShaderi(FID, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
System.err.println("Fragment Shader: " + GL20.glGetShaderInfoLog(FID));
}
GL20.glAttachShader(pID, VID);
GL20.glAttachShader(pID, FID);
GL20.glLinkProgram(pID);
if (GL20.glGetProgrami(pID, GL20.GL_LINK_STATUS) == GL11.GL_FALSE) {
System.err.println("Program Linking: " + GL20.glGetProgramInfoLog(pID));
}
GL20.glValidateProgram(pID);
if (GL20.glGetProgrami(pID, GL20.GL_VALIDATE_STATUS) == GL11.GL_FALSE) {
System.err.println("Program Validation: " + GL20.glGetProgramInfoLog(pID));
}
return this;
}
public void setUniform(String name, Matrix4f matrix) {
FloatBuffer matrixB = MemoryUtil.memAllocFloat(16);
matrix.get(matrixB);
glUniformMatrix4fv(glGetUniformLocation(pID, name), false, matrixB);
MemoryUtil.memFree(matrixB);
}
public void setUniform(String name, Vector2f vector) {
glUniform2fv(glGetUniformLocation(pID, name), new float[]{vector.x, vector.y});
}
public void setUniform(String name, int value) {
glUniform1iv(glGetUniformLocation(pID, name), new int[]{value});
}
public void setUniform(String name, float value) {
glUniform1fv(glGetUniformLocation(pID, name), new float[]{value});
}
public void setUniform(String name, Vector3i vector) {
glUniform3iv(glGetUniformLocation(pID, name), new int[]{vector.x, vector.y, vector.z});
}
public void setUniform(String name, Vector3f vector) {
glUniform3fv(glGetUniformLocation(pID, name), new float[]{vector.x, vector.y, vector.z});
}
public void bind() {
glUseProgram(pID);
}
public void unbind() {
glUseProgram(0);
}
}

@ -0,0 +1,43 @@
package ity.opencraft.util;
import org.joml.Matrix4f;
import org.joml.Vector3f;
public class MatrixUtils {
/**
* make a model matrix
* @param translation the position
* @param rotation the rotation
* @param scale the scale
* @return the matrix
*/
public static Matrix4f transformationMatrix(Vector3f translation, Vector3f rotation, Vector3f scale) {
rotation = rotation.mul((float) (Math.PI / 180.0f), new Vector3f());
Matrix4f matrix = new Matrix4f();
return matrix.translate(translation).rotateXYZ(rotation).scale(scale);
}
/**
* make a projection matrix
* @param fov the fov
* @param aspectRatio the aspect ratio (w/h)
* @param zNear the near plane
* @param zFar the far plane
* @return the matrix
*/
public static Matrix4f projectionMatrix(float fov, float aspectRatio, float zNear, float zFar) {
return new Matrix4f().perspective((float) Math.toRadians(fov), aspectRatio, zNear, zFar);
}
/**
* make a view matrix
* @param position the position
* @param rotation the rotation
* @return the matrix
*/
public static Matrix4f viewMatrix(Vector3f position, Vector3f rotation) {
rotation = rotation.mul((float) (Math.PI / 180.0f), new Vector3f());
return new Matrix4f().rotateXYZ(rotation).translate(position.negate(new Vector3f()));
}
}

@ -0,0 +1,14 @@
package ity.opencraft.util;
public class OctavedNoise {
public static double octavedNoise(double x, double y, double z, int octaves, double strength, double size) {
double result = 0;
double maxVal = 0;
for(int i = 0; i < octaves; i++) {
result += SimplexNoise.noise(x*size, y*size, z*size) * strength;
maxVal += strength;
size*=2;
}
return result/maxVal;
}
}

@ -0,0 +1,358 @@
package ity.opencraft.util;
/*
* A speed-improved simplex noise algorithm for 2D, 3D and 4D in Java.
*
* Based on example code by Stefan Gustavson (stegu@itn.liu.se).
* Optimisations by Peter Eastman (peastman@drizzle.stanford.edu).
* Better rank ordering method by Stefan Gustavson in 2012.
*
* This could be speeded up even further, but it's useful as it is.
*
* Version 2012-03-09
*
* This code was placed in the public domain by its original author,
* Stefan Gustavson. You may use it as you see fit, but
* attribution is appreciated.
*
*/
public class SimplexNoise { // Simplex noise in 2D, 3D and 4D
private static Grad grad3[] = {new Grad(1,1,0),new Grad(-1,1,0),new Grad(1,-1,0),new Grad(-1,-1,0),
new Grad(1,0,1),new Grad(-1,0,1),new Grad(1,0,-1),new Grad(-1,0,-1),
new Grad(0,1,1),new Grad(0,-1,1),new Grad(0,1,-1),new Grad(0,-1,-1)};
private static Grad grad4[]= {new Grad(0,1,1,1),new Grad(0,1,1,-1),new Grad(0,1,-1,1),new Grad(0,1,-1,-1),
new Grad(0,-1,1,1),new Grad(0,-1,1,-1),new Grad(0,-1,-1,1),new Grad(0,-1,-1,-1),
new Grad(1,0,1,1),new Grad(1,0,1,-1),new Grad(1,0,-1,1),new Grad(1,0,-1,-1),
new Grad(-1,0,1,1),new Grad(-1,0,1,-1),new Grad(-1,0,-1,1),new Grad(-1,0,-1,-1),
new Grad(1,1,0,1),new Grad(1,1,0,-1),new Grad(1,-1,0,1),new Grad(1,-1,0,-1),
new Grad(-1,1,0,1),new Grad(-1,1,0,-1),new Grad(-1,-1,0,1),new Grad(-1,-1,0,-1),
new Grad(1,1,1,0),new Grad(1,1,-1,0),new Grad(1,-1,1,0),new Grad(1,-1,-1,0),
new Grad(-1,1,1,0),new Grad(-1,1,-1,0),new Grad(-1,-1,1,0),new Grad(-1,-1,-1,0)};
private static short p[] = {151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180};
// To remove the need for index wrapping, double the permutation table length
private static short perm[] = new short[512];
private static short permMod12[] = new short[512];
static {
for(int i=0; i<512; i++)
{
perm[i]=p[i & 255];
permMod12[i] = (short)(perm[i] % 12);
}
}
// Skewing and unskewing factors for 2, 3, and 4 dimensions
private static final double F2 = 0.5*(Math.sqrt(3.0)-1.0);
private static final double G2 = (3.0-Math.sqrt(3.0))/6.0;
private static final double F3 = 1.0/3.0;
private static final double G3 = 1.0/6.0;
private static final double F4 = (Math.sqrt(5.0)-1.0)/4.0;
private static final double G4 = (5.0-Math.sqrt(5.0))/20.0;
// This method is a *lot* faster than using (int)Math.floor(x)
private static int fastfloor(double x) {
int xi = (int)x;
return x<xi ? xi-1 : xi;
}
private static double dot(Grad g, double x, double y) {
return g.x*x + g.y*y; }
private static double dot(Grad g, double x, double y, double z) {
return g.x*x + g.y*y + g.z*z; }
private static double dot(Grad g, double x, double y, double z, double w) {
return g.x*x + g.y*y + g.z*z + g.w*w; }
// 2D simplex noise
public static double noise(double xin, double yin) {
double n0, n1, n2; // Noise contributions from the three corners
// Skew the input space to determine which simplex cell we're in
double s = (xin+yin)*F2; // Hairy factor for 2D
int i = fastfloor(xin+s);
int j = fastfloor(yin+s);
double t = (i+j)*G2;
double X0 = i-t; // Unskew the cell origin back to (x,y) space
double Y0 = j-t;
double x0 = xin-X0; // The x,y distances from the cell origin
double y0 = yin-Y0;
// For the 2D case, the simplex shape is an equilateral triangle.
// Determine which simplex we are in.
int i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords
if(x0>y0) {i1=1; j1=0;} // lower triangle, XY order: (0,0)->(1,0)->(1,1)
else {i1=0; j1=1;} // upper triangle, YX order: (0,0)->(0,1)->(1,1)
// A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
// a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
// c = (3-sqrt(3))/6
double x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
double y1 = y0 - j1 + G2;
double x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords
double y2 = y0 - 1.0 + 2.0 * G2;
// Work out the hashed gradient indices of the three simplex corners
int ii = i & 255;
int jj = j & 255;
int gi0 = permMod12[ii+perm[jj]];
int gi1 = permMod12[ii+i1+perm[jj+j1]];
int gi2 = permMod12[ii+1+perm[jj+1]];
// Calculate the contribution from the three corners
double t0 = 0.5 - x0*x0-y0*y0;
if(t0<0) n0 = 0.0;
else {
t0 *= t0;
n0 = t0 * t0 * dot(grad3[gi0], x0, y0); // (x,y) of grad3 used for 2D gradient
}
double t1 = 0.5 - x1*x1-y1*y1;
if(t1<0) n1 = 0.0;
else {
t1 *= t1;
n1 = t1 * t1 * dot(grad3[gi1], x1, y1);
}
double t2 = 0.5 - x2*x2-y2*y2;
if(t2<0) n2 = 0.0;
else {
t2 *= t2;
n2 = t2 * t2 * dot(grad3[gi2], x2, y2);
}
// Add contributions from each corner to get the final noise value.
// The result is scaled to return values in the interval [-1,1].
return 70.0 * (n0 + n1 + n2);
}
// 3D simplex noise
public static double noise(double xin, double yin, double zin) {
double n0, n1, n2, n3; // Noise contributions from the four corners
// Skew the input space to determine which simplex cell we're in
double s = (xin+yin+zin)*F3; // Very nice and simple skew factor for 3D
int i = fastfloor(xin+s);
int j = fastfloor(yin+s);
int k = fastfloor(zin+s);
double t = (i+j+k)*G3;
double X0 = i-t; // Unskew the cell origin back to (x,y,z) space
double Y0 = j-t;
double Z0 = k-t;
double x0 = xin-X0; // The x,y,z distances from the cell origin
double y0 = yin-Y0;
double z0 = zin-Z0;
// For the 3D case, the simplex shape is a slightly irregular tetrahedron.
// Determine which simplex we are in.
int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords
int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords
if(x0>=y0) {
if(y0>=z0)
{ i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } // X Y Z order
else if(x0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } // X Z Y order
else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } // Z X Y order
}
else { // x0<y0
if(y0<z0) { i1=0; j1=0; k1=1; i2=0; j2=1; k2=1; } // Z Y X order
else if(x0<z0) { i1=0; j1=1; k1=0; i2=0; j2=1; k2=1; } // Y Z X order
else { i1=0; j1=1; k1=0; i2=1; j2=1; k2=0; } // Y X Z order
}
// A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
// a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and
// a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where
// c = 1/6.
double x1 = x0 - i1 + G3; // Offsets for second corner in (x,y,z) coords
double y1 = y0 - j1 + G3;
double z1 = z0 - k1 + G3;
double x2 = x0 - i2 + 2.0*G3; // Offsets for third corner in (x,y,z) coords
double y2 = y0 - j2 + 2.0*G3;
double z2 = z0 - k2 + 2.0*G3;
double x3 = x0 - 1.0 + 3.0*G3; // Offsets for last corner in (x,y,z) coords
double y3 = y0 - 1.0 + 3.0*G3;
double z3 = z0 - 1.0 + 3.0*G3;
// Work out the hashed gradient indices of the four simplex corners
int ii = i & 255;
int jj = j & 255;
int kk = k & 255;
int gi0 = permMod12[ii+perm[jj+perm[kk]]];
int gi1 = permMod12[ii+i1+perm[jj+j1+perm[kk+k1]]];
int gi2 = permMod12[ii+i2+perm[jj+j2+perm[kk+k2]]];
int gi3 = permMod12[ii+1+perm[jj+1+perm[kk+1]]];
// Calculate the contribution from the four corners
double t0 = 0.6 - x0*x0 - y0*y0 - z0*z0;
if(t0<0) n0 = 0.0;
else {
t0 *= t0;
n0 = t0 * t0 * dot(grad3[gi0], x0, y0, z0);
}
double t1 = 0.6 - x1*x1 - y1*y1 - z1*z1;
if(t1<0) n1 = 0.0;
else {
t1 *= t1;
n1 = t1 * t1 * dot(grad3[gi1], x1, y1, z1);
}
double t2 = 0.6 - x2*x2 - y2*y2 - z2*z2;
if(t2<0) n2 = 0.0;
else {
t2 *= t2;
n2 = t2 * t2 * dot(grad3[gi2], x2, y2, z2);
}
double t3 = 0.6 - x3*x3 - y3*y3 - z3*z3;
if(t3<0) n3 = 0.0;
else {
t3 *= t3;
n3 = t3 * t3 * dot(grad3[gi3], x3, y3, z3);
}
// Add contributions from each corner to get the final noise value.
// The result is scaled to stay just inside [-1,1]
return 32.0*(n0 + n1 + n2 + n3);
}
// 4D simplex noise, better simplex rank ordering method 2012-03-09
public static double noise(double x, double y, double z, double w) {
double n0, n1, n2, n3, n4; // Noise contributions from the five corners
// Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in
double s = (x + y + z + w) * F4; // Factor for 4D skewing
int i = fastfloor(x + s);
int j = fastfloor(y + s);
int k = fastfloor(z + s);
int l = fastfloor(w + s);
double t = (i + j + k + l) * G4; // Factor for 4D unskewing
double X0 = i - t; // Unskew the cell origin back to (x,y,z,w) space
double Y0 = j - t;
double Z0 = k - t;
double W0 = l - t;
double x0 = x - X0; // The x,y,z,w distances from the cell origin
double y0 = y - Y0;
double z0 = z - Z0;
double w0 = w - W0;
// For the 4D case, the simplex is a 4D shape I won't even try to describe.
// To find out which of the 24 possible simplices we're in, we need to
// determine the magnitude ordering of x0, y0, z0 and w0.
// Six pair-wise comparisons are performed between each possible pair
// of the four coordinates, and the results are used to rank the numbers.
int rankx = 0;
int ranky = 0;
int rankz = 0;
int rankw = 0;
if(x0 > y0) rankx++; else ranky++;
if(x0 > z0) rankx++; else rankz++;
if(x0 > w0) rankx++; else rankw++;
if(y0 > z0) ranky++; else rankz++;
if(y0 > w0) ranky++; else rankw++;
if(z0 > w0) rankz++; else rankw++;
int i1, j1, k1, l1; // The integer offsets for the second simplex corner
int i2, j2, k2, l2; // The integer offsets for the third simplex corner
int i3, j3, k3, l3; // The integer offsets for the fourth simplex corner
// simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order.
// Many values of c will never occur, since e.g. x>y>z>w makes x<z, y<w and x<w
// impossible. Only the 24 indices which have non-zero entries make any sense.
// We use a thresholding to set the coordinates in turn from the largest magnitude.
// Rank 3 denotes the largest coordinate.
i1 = rankx >= 3 ? 1 : 0;
j1 = ranky >= 3 ? 1 : 0;
k1 = rankz >= 3 ? 1 : 0;
l1 = rankw >= 3 ? 1 : 0;
// Rank 2 denotes the second largest coordinate.
i2 = rankx >= 2 ? 1 : 0;
j2 = ranky >= 2 ? 1 : 0;
k2 = rankz >= 2 ? 1 : 0;
l2 = rankw >= 2 ? 1 : 0;
// Rank 1 denotes the second smallest coordinate.
i3 = rankx >= 1 ? 1 : 0;
j3 = ranky >= 1 ? 1 : 0;
k3 = rankz >= 1 ? 1 : 0;
l3 = rankw >= 1 ? 1 : 0;
// The fifth corner has all coordinate offsets = 1, so no need to compute that.
double x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords
double y1 = y0 - j1 + G4;
double z1 = z0 - k1 + G4;
double w1 = w0 - l1 + G4;
double x2 = x0 - i2 + 2.0*G4; // Offsets for third corner in (x,y,z,w) coords
double y2 = y0 - j2 + 2.0*G4;
double z2 = z0 - k2 + 2.0*G4;
double w2 = w0 - l2 + 2.0*G4;
double x3 = x0 - i3 + 3.0*G4; // Offsets for fourth corner in (x,y,z,w) coords
double y3 = y0 - j3 + 3.0*G4;
double z3 = z0 - k3 + 3.0*G4;
double w3 = w0 - l3 + 3.0*G4;
double x4 = x0 - 1.0 + 4.0*G4; // Offsets for last corner in (x,y,z,w) coords
double y4 = y0 - 1.0 + 4.0*G4;
double z4 = z0 - 1.0 + 4.0*G4;
double w4 = w0 - 1.0 + 4.0*G4;
// Work out the hashed gradient indices of the five simplex corners
int ii = i & 255;
int jj = j & 255;
int kk = k & 255;
int ll = l & 255;
int gi0 = perm[ii+perm[jj+perm[kk+perm[ll]]]] % 32;
int gi1 = perm[ii+i1+perm[jj+j1+perm[kk+k1+perm[ll+l1]]]] % 32;
int gi2 = perm[ii+i2+perm[jj+j2+perm[kk+k2+perm[ll+l2]]]] % 32;
int gi3 = perm[ii+i3+perm[jj+j3+perm[kk+k3+perm[ll+l3]]]] % 32;
int gi4 = perm[ii+1+perm[jj+1+perm[kk+1+perm[ll+1]]]] % 32;
// Calculate the contribution from the five corners
double t0 = 0.6 - x0*x0 - y0*y0 - z0*z0 - w0*w0;
if(t0<0) n0 = 0.0;
else {
t0 *= t0;
n0 = t0 * t0 * dot(grad4[gi0], x0, y0, z0, w0);
}
double t1 = 0.6 - x1*x1 - y1*y1 - z1*z1 - w1*w1;
if(t1<0) n1 = 0.0;
else {
t1 *= t1;
n1 = t1 * t1 * dot(grad4[gi1], x1, y1, z1, w1);
}
double t2 = 0.6 - x2*x2 - y2*y2 - z2*z2 - w2*w2;
if(t2<0) n2 = 0.0;
else {
t2 *= t2;
n2 = t2 * t2 * dot(grad4[gi2], x2, y2, z2, w2);
}
double t3 = 0.6 - x3*x3 - y3*y3 - z3*z3 - w3*w3;
if(t3<0) n3 = 0.0;
else {
t3 *= t3;
n3 = t3 * t3 * dot(grad4[gi3], x3, y3, z3, w3);
}
double t4 = 0.6 - x4*x4 - y4*y4 - z4*z4 - w4*w4;
if(t4<0) n4 = 0.0;
else {
t4 *= t4;
n4 = t4 * t4 * dot(grad4[gi4], x4, y4, z4, w4);
}
// Sum up and scale the result to cover the range [-1,1]
return 27.0 * (n0 + n1 + n2 + n3 + n4);
}
// Inner class to speed upp gradient computations
// (array access is a lot slower than member access)
private static class Grad
{
double x, y, z, w;
Grad(double x, double y, double z)
{
this.x = x;
this.y = y;
this.z = z;
}
Grad(double x, double y, double z, double w)
{
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
}
}

@ -0,0 +1,27 @@
package ity.opencraft.util;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.stream.Collectors;
public class StringLoader {
/**
* make a stream out of a path
* @param path the path to the resource
* @return the stream
*/
public static InputStream loadResourceAsStream(String path) {
return StringLoader.class.getClassLoader().getResourceAsStream(path);
}
/**
* read a text file from a path
* @param path path to the text file
* @return the contents of the text file as a string
*/
public static String loadResourceAsString(String path) {
return new BufferedReader(new InputStreamReader(loadResourceAsStream(path))).lines().collect(Collectors.joining("\n"));
}
}

@ -0,0 +1,47 @@
package ity.opencraft.util;
import ity.opencraft.backends.Backended;
import ity.opencraft.data.Texture;
import java.io.IOException;
import java.nio.ByteBuffer;
import static org.lwjgl.BufferUtils.createByteBuffer;
public class TextureLoader extends Backended {
private static TextureLoader instance;
/**
* Initialize. Has to be called before load can be called.
*/
public static void init() {
instance = new TextureLoader();
}
/**
* Load a texture
* @param name The path to the texture, relative to the textures/ directory in resources.
* @return The loaded texture
*/
public static Texture load(String name) {
return instance.loadTexture(name);
}
protected Texture loadTexture(String name) {
return ((TextureLoader)backend).loadTexture(name);
}
/**
* Load a resource into a buffer
* @param resource The path to the resource
* @param bufferSize The size of the buffer
* @return The buffer
*/
public static ByteBuffer ioResourceToByteBuffer(String resource, int bufferSize) throws IOException {
ByteBuffer buffer = createByteBuffer(bufferSize);
buffer.put(StringLoader.loadResourceAsStream(resource).readAllBytes());
buffer.flip();
return buffer;
}
}

@ -0,0 +1,83 @@
package ity.opencraft.util;
import ity.opencraft.data.Texture;
import ity.opencraft.backends.Backend;
import ity.opencraft.backends.BackendImpl;
import org.lwjgl.system.MemoryStack;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
import static org.lwjgl.stb.STBImage.*;
import static org.lwjgl.system.MemoryStack.stackPush;
@BackendImpl(Backend.GL)
public class TextureLoaderGL extends TextureLoader {
@Override
public Texture loadTexture(String name) {
ByteBuffer image;
ByteBuffer imageBuffer;
try {
imageBuffer = ioResourceToByteBuffer("textures/" + name, 4*(16*4)*(16*4)*(16*4));
} catch (IOException e) {
throw new RuntimeException(e);
}
try (MemoryStack stack = stackPush()) {
IntBuffer w = stack.mallocInt(1);
IntBuffer h = stack.mallocInt(1);
IntBuffer comp = stack.mallocInt(1);
if (!stbi_info_from_memory(imageBuffer, w, h, comp)) {
throw new RuntimeException("Failed to read image information: " + stbi_failure_reason());
} else {
//System.out.println("OK with reason: " + stbi_failure_reason());
}
// System.out.println("Image width: " + w.get(0));
// System.out.println("Image height: " + h.get(0));
// System.out.println("Image components: " + comp.get(0));
// System.out.println("Image HDR: " + stbi_is_hdr_from_memory(imageBuffer));
// Decode the image
image = stbi_load_from_memory(imageBuffer, w, h, comp, 0);
if (image == null) {
throw new RuntimeException("Failed to load image: " + stbi_failure_reason());
}
Texture tex = new Texture();
tex.w = w.get(0);
tex.h = h.get(0);
tex.c = comp.get(0);
int texID = glGenTextures();
glBindTexture(GL_TEXTURE_2D, texID);
int format = 0;
if(comp.get(0) == 3)
format = GL_RGB;
else if(comp.get(0) == 4)
format = GL_RGBA;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, format, w.get(0), h.get(0), 0, format, GL_UNSIGNED_BYTE, image);
glBindTexture(GL_TEXTURE_2D, 0);
stbi_image_free(image);
tex.id = texID;
return tex;
}
}
}

@ -0,0 +1,42 @@
package ity.opencraft.world;
import ity.opencraft.mesh.BlockModelType;
import org.joml.Vector3i;
public abstract class Block {
/**
* Check if the block is transparent. Used to determine whether other block faces are visible through this block or not.
* Examples include Air, Glass, Water...
* @return Whether this block is transparent
*/
public abstract boolean isTransparent(Block sideBlock);
/**
* Get the texture index of the block. Texture indices are counted from left to right and then from top to bottom.
* @return The texture index of the block
*/
public abstract int textureIndex(BlockSide side, Vector3i pos);
/**
* Check if the block is visible. An invisible block does not have any mesh or model, and is thus not rendered at all.
* If you override this to return false, override isTransparent to return true.
* @return Whether this block is transparent
*/
public boolean visible() {
return true;
}
public boolean textureRotate(BlockSide side) {
return false;
};
public BlockModelType modelType() {
return BlockModelType.CUBE;
}
public abstract boolean isSolid();
public boolean isBlend() {
return false;
}
}

@ -0,0 +1,6 @@
package ity.opencraft.world;
public enum BlockSide {
TOP, BOTTOM, LEFT, RIGHT, FRONT, BACK,
CROSS_1, CROSS_2
}

@ -0,0 +1,19 @@
package ity.opencraft.world;
import ity.opencraft.world.blocks.*;
public class Blocks {
public static final BlockAir AIR = new BlockAir();
public static final BlockStone STONE = new BlockStone();
public static final BlockDirt DIRT = new BlockDirt();
public static final BlockGrass GRASS = new BlockGrass();
public static final BlockCoalOre COAL_ORE = new BlockCoalOre();
public static final BlockTallGrass TALL_GRASS = new BlockTallGrass();
public static final BlockDandelion DANDELION = new BlockDandelion();
public static final BlockWoodLog WOOD_LOG = new BlockWoodLog();
public static final BlockLeaves LEAVES = new BlockLeaves();
public static final BlockWater WATER = new BlockWater();
}

@ -0,0 +1,56 @@
package ity.opencraft.world;
import ity.opencraft.data.BlockMeshData;
import ity.opencraft.data.BlockMeshHandle;
import org.joml.Vector3i;
public class Chunk {
public static final int size = 32;
private boolean freed = false;
public Vector3i pos;
public BlockMeshHandle meshHandle;
public BlockMeshData meshData;
public BlockMeshHandle meshHandle2;
public BlockMeshData meshData2;
private Block[] blocks = new Block[size*size*size];
//private float[] lights = new float[size*size*size];
public Chunk(Vector3i pos) {
this.pos = pos;
}
public Block getBlockAt(int x, int y, int z) {
return blocks[x*size*size+y*size+z];
}
public void setBlockAt(int x, int y, int z, Block block) {
blocks[x*size*size+y*size+z] = block;
}
public float getLightAt(int x, int y, int z) {
//return lights[x*size*size+y*size+z];
return 0;
}
public void setLightAt(int x, int y, int z, float level) {
//lights[x*size*size+y*size+z] = level;
}
public void free() {
if(!freed) {
blocks = null;
if(meshHandle != null)
meshHandle.free();
meshHandle = null;
if(meshHandle2 != null)
meshHandle2.free();
meshHandle2 = null;
freed = true;
} else {
throw new IllegalStateException("Double free");
}
}
}

@ -0,0 +1,51 @@
package ity.opencraft.world;
import ity.opencraft.data.BlockMeshData;
import ity.opencraft.data.BlockMeshHandle;
import org.joml.Vector3i;
public class ChunkReference {
private Vector3i pos;
public boolean isChunkPresent = true;
// TODO refactor, package-private is ugly
ChunkReference(int x, int y, int z) {
this.pos = new Vector3i(x, y, z);
}
public BlockMeshHandle getMeshHandle() {
return World.getChunkAtRaw(pos).meshHandle;
}
public BlockMeshHandle getMeshHandle2() {
return World.getChunkAtRaw(pos).meshHandle2;
}
public void setMeshHandle(BlockMeshHandle handle) {
World.getChunkAtRaw(pos).meshHandle = handle;
}
public BlockMeshData getMeshData() {
return World.getChunkAtRaw(pos).meshData;
}
public void setMeshData(BlockMeshData handle) {
World.getChunkAtRaw(pos).meshData = handle;
}
public BlockMeshData getMeshData2() {
return World.getChunkAtRaw(pos).meshData2;
}
public void setMeshData2(BlockMeshData handle) {
World.getChunkAtRaw(pos).meshData2 = handle;
}
public void setMeshHandle2(BlockMeshHandle handle) {
World.getChunkAtRaw(pos).meshHandle2 = handle;
}
public boolean isChunkGenerated() {
return World.isChunkAt(pos);
}
public Vector3i getPos() {
return this.pos;
}
}

@ -0,0 +1,340 @@
package ity.opencraft.world;
import ity.opencraft.render.Renderer;
import ity.opencraft.util.OctavedNoise;
import ity.opencraft.world.blocks.BlockLeaves;
import org.joml.Vector3i;
import java.util.HashMap;
import java.util.Random;
// TODO negative coordinates
// As Y grows, it goes downwards instead of upwards.
public class World {
/**
* The size of the world in the X and Z direction; The horizontal size.
*/
public static final int sizeXZ = 1024;
public static final int sizeY = 64;
private static HashMap<String, Long> perfTimes = new HashMap<>();
public static HashMap<Vector3i, Chunk> chunks = new HashMap<>();
private static HashMap<Vector3i, ChunkReference> chunkRefs = new HashMap<>();
private static Random rand = new Random();
private static final int mult = 128;
private static final double multScale = 64.0;
private static final int height = -128;
private static int random(int x, int y) {
Random pRand = new Random(((x * 435747689L) & 438) ^ y);
return pRand.nextInt(10000);
}
private static void beginPerf(String name) {
perfTimes.put(name, System.nanoTime());
}
private static void endPerf(String name) {
long start = perfTimes.get(name);
long end = System.nanoTime();
long timeElapsed = end - start;
System.out.print(name + ": ");
System.out.println(timeElapsed);
}
private static boolean isSkyVisible(int x, int y, int z) {
while(isChunkAt(x/Chunk.size,y/Chunk.size,z/Chunk.size)) {
if(!World.getBlockAt(x, y, z).isTransparent(Blocks.AIR)) return false;
y += 1;
}
return true;
}
private static Block makeBlockAt(int x, int y, int z) {
double mult1 = (OctavedNoise.octavedNoise((x+63156) / 4096f, 0, (z+6533) / 4096f, 1, 32, 0.4f) + 1) * 0.5f;
double mult = (OctavedNoise.octavedNoise((x+9276) / 256f, 0, (z+4317) / 256f, 1, 32, 0.4f) + 1) * 32f;
int oY = y;
y += height*mult1;
//System.out.println(mult);
double noise = OctavedNoise.octavedNoise(x / multScale, y / multScale, z / multScale, 3, 32, 0.4f) * mult;
double noiseBelow = OctavedNoise.octavedNoise(x / multScale, (y - 1) / multScale, z / multScale, 3, 32, 0.4f) * mult;
/*
double treeNoise = random(x, z);
if(treeNoise > 9908) {
if (!(noiseBelow < (y - 1) - mult) && noise < (y - 1) - (mult - 1)) return Blocks.WOOD_LOG;
double noiseBelow2 = OctavedNoise.octavedNoise(x / multScale, (y - 2) / multScale, z / multScale, 3, 32, 0.4f) * mult;
if (!(noiseBelow2 < (y - 2) - mult) && noiseBelow < (y - 2) - (mult - 2)) return Blocks.WOOD_LOG;
double noiseBelow3 = OctavedNoise.octavedNoise(x / multScale, (y - 3) / multScale, z / multScale, 3, 32, 0.4f) * mult;
if (!(noiseBelow3 < (y - 3) - mult) && noiseBelow2 < (y - 3) - (mult - 3)) return Blocks.LEAVES;
}
double treeNoiseXP = random(x+1, z);
if(treeNoiseXP > 9908) {
double noiseBelow2XP = OctavedNoise.octavedNoise((x + 1) / multScale, (y - 2) / multScale, z / multScale, 3, 32, 0.4f) * mult;
double noiseBelowXP = OctavedNoise.octavedNoise((x + 1) / multScale, (y - 1) / multScale, z / multScale, 3, 32, 0.4f) * mult;
if (!(noiseBelow2XP < (y - 2) - mult) && noiseBelowXP < (y - 2) - (mult - 2)) return Blocks.LEAVES;
}
double treeNoiseXN = random(x-1, z);
if(treeNoiseXN > 9908) {
double noiseBelow2XN = OctavedNoise.octavedNoise((x - 1) / multScale, (y - 2) / multScale, z / multScale, 3, 32, 0.4f) * mult;
double noiseBelowXN = OctavedNoise.octavedNoise((x - 1) / multScale, (y - 1) / multScale, z / multScale, 3, 32, 0.4f) * mult;
if (!(noiseBelow2XN < (y - 2) - mult) && noiseBelowXN < (y - 2) - (mult - 2)) return Blocks.LEAVES;
}
double treeNoiseZP = random(x, z+1);
if(treeNoiseZP > 9908) {
double noiseBelow2ZP = OctavedNoise.octavedNoise(x / multScale, (y - 2) / multScale, (z + 1) / multScale, 3, 32, 0.4f) * mult;
double noiseBelowZP = OctavedNoise.octavedNoise(x / multScale, (y - 1) / multScale, (z + 1) / multScale, 3, 32, 0.4f) * mult;
if (!(noiseBelow2ZP < (y - 2) - mult) && noiseBelowZP < (y - 2) - (mult - 2)) return Blocks.LEAVES;
}
double treeNoiseZN = random(x, z-1);
if(treeNoiseZN > 9908) {
double noiseBelow2ZN = OctavedNoise.octavedNoise(x / multScale, (y - 2) / multScale, (z - 1) / multScale, 3, 32, 0.4f) * mult;
double noiseBelowZN = OctavedNoise.octavedNoise(x / multScale, (y - 1) / multScale, (z - 1) / multScale, 3, 32, 0.4f) * mult;
if (!(noiseBelow2ZN < (y - 2) - mult) && noiseBelowZN < (y - 2) - (mult - 2)) return Blocks.LEAVES;
}
*/
double noiseAbove = OctavedNoise.octavedNoise(x / multScale, (y + 1) / multScale, z / multScale, 3, 32, 0.4f) * mult;
int waterLevel = 104;
if(!(noiseBelow<(y-1)-mult) && noise<(y-1)-(mult-1) && rand.nextInt(5) == 0) {
if(oY <= waterLevel-1)
return Blocks.WATER;
else
return Blocks.TALL_GRASS;
}
if(!(noiseBelow<(y-1)-mult) && noise<(y-1)-(mult-1) && rand.nextInt(20) == 0) {
if(oY <= waterLevel-1)
return Blocks.WATER;
else
return Blocks.DANDELION;
}
if(noise<y-mult) {
if(oY < waterLevel)
return Blocks.WATER;
else
return Blocks.AIR;
} else if(noiseAbove<y-(mult-1)) {
if(oY < waterLevel-1)
return Blocks.DIRT;
else
return Blocks.GRASS;
} else if(noiseAbove<y-(mult-2)) {
return Blocks.DIRT;
} else if(rand.nextInt(100) == 0) {
return Blocks.COAL_ORE;
} else {
return Blocks.STONE;
}
}
private static Chunk makeChunkAt(int x, int y, int z) {
beginPerf("chunkgen");
System.out.println("Generating chunk x:"+x+", y:"+y+", z:"+z);
Chunk chunk = new Chunk(new Vector3i(x, y, z));
for(int i = 0; i < Chunk.size; i++) {
for(int j = 0; j < Chunk.size; j++) {
for(int k = 0; k < Chunk.size; k++) {
chunk.setBlockAt(i,j,k, makeBlockAt(x*Chunk.size+i, y*Chunk.size+j, z*Chunk.size+k));
Vector3i pos = new Vector3i(x, y, z);
//chunk.setLightAt(i, j, k, isSkyVisible(x, y, z) ? 1.0f : 0.3f);
}
}
}
endPerf("chunkgen");
return chunk;
}
private static void makeLightAt(Chunk chunk, int x, int y, int z) {
for(int i = 0; i < Chunk.size; i++) {
for(int j = 0; j < Chunk.size; j++) {
for(int k = 0; k < Chunk.size; k++) {
chunk.setLightAt(i, j, k, isSkyVisible(x*Chunk.size+i, y*Chunk.size+j, z*Chunk.size+k) ? 1.0f : 0.1f);
}
}
}
}
/**
* Get a block in the world at a given position. Takes the world position of the block.
* @param x The x position
* @param y The y position
* @param z The z position
* @return The block at the given position
*/
public static Block getBlockAt(int x, int y, int z) {
Chunk chunk = getChunkAtBlockPosRaw(x,y,z);
Block block = chunk.getBlockAt(x%Chunk.size, y%Chunk.size, z%Chunk.size);
// TODO better handling
//if(block == null) throw new RuntimeException("Block at "+x+","+y+","+z+" was NULL.");
return block;
}
public static Block getBlockAtLazy(int x, int y, int z) {
if(isChunkAtBlockPos(x, y, z)) {
Chunk chunk = getChunkAtBlockPosRaw(x, y, z);
if(chunk == null)
System.out.println(isChunkAtBlockPos(x, y, z));
Block block = chunk.getBlockAt(x % Chunk.size, y % Chunk.size, z % Chunk.size);
return block;
} else {
Block block = makeBlockAt(x, y, z);
return block;
}
}
public static float getLightAt(int x, int y, int z) {
//Chunk chunk = getChunkAtBlockPosRaw(x,y,z);
//return 1.0f;
//return chunk.getLightAt(x%Chunk.size, y%Chunk.size, z%Chunk.size);
// TODO better handling
//if(block == null) throw new RuntimeException("Block at "+x+","+y+","+z+" was NULL.");
return 1.0f;
}
public static void setLightAt(int x, int y, int z, float level) {
Chunk chunk = getChunkAtBlockPosRaw(x,y,z);
chunk.setLightAt(x%Chunk.size, y%Chunk.size, z%Chunk.size, level);
// TODO better handling
//if(block == null) throw new RuntimeException("Block at "+x+","+y+","+z+" was NULL.");
}
/**
* Set a block in the world at a given position. Takes the world position of the block.
* @param x The x position
* @param y The y position
* @param z The z position
* @param block The block to put
*/
public static void setBlockAt(int x, int y, int z, Block block) {
Chunk chunk = getChunkAtBlockPosRaw(x,y,z);
Vector3i blockInChunk = new Vector3i(x%Chunk.size, y%Chunk.size, z%Chunk.size);
Vector3i chunkinWorld = new Vector3i(x/Chunk.size, y/Chunk.size, z/Chunk.size);
chunk.setBlockAt(blockInChunk.x, blockInChunk.y, blockInChunk.z, block);
Renderer.remeshChunk(chunkinWorld.x, chunkinWorld.y, chunkinWorld.z);
// TODO will fail on world border
if(blockInChunk.x == 0)
Renderer.remeshChunk(chunkinWorld.x-1, chunkinWorld.y, chunkinWorld.z);
if(blockInChunk.x == Chunk.size-1)
Renderer.remeshChunk(chunkinWorld.x+1, chunkinWorld.y, chunkinWorld.z);
if(blockInChunk.y == 0)
Renderer.remeshChunk(chunkinWorld.x, chunkinWorld.y-1, chunkinWorld.z);
if(blockInChunk.y == Chunk.size-1)
Renderer.remeshChunk(chunkinWorld.x, chunkinWorld.y+1, chunkinWorld.z);
if(blockInChunk.z == 0)
Renderer.remeshChunk(chunkinWorld.x, chunkinWorld.y, chunkinWorld.z-1);
if(blockInChunk.z == Chunk.size-1)
Renderer.remeshChunk(chunkinWorld.x, chunkinWorld.y, chunkinWorld.z+1);
}
/**
* Get a chunk at a given chunk position. Takes a location in *chunks*.
* @param x The x chunk position
* @param y The y chunk position
* @param z The z chunk position
* @return The chunk at the given position
*/
public static ChunkReference getChunkAt(int x, int y, int z) {
Vector3i key = new Vector3i(x,y,z);
ChunkReference ref = chunkRefs.get(key);
if(ref == null) {
ref = new ChunkReference(x, y, z);
chunkRefs.put(key, ref);
}
if(!isChunkAt(key)) {
Chunk chunk = makeChunkAt(x, y, z);
if(chunk == null)
throw new IllegalStateException("Failed to create chunk");
setChunkAt(x, y, z, chunk);
}
return ref;
}
public static ChunkReference getChunkAt(Vector3i pos) {
return getChunkAt(pos.x, pos.y, pos.z);
}
public static boolean isChunkAt(int x, int y, int z) {
return !(chunks.get(new Vector3i(x,y,z)) == null);
}
public static boolean isChunkAt(Vector3i pos) {
return isChunkAt(pos.x, pos.y, pos.z);
}
private static Chunk getChunkAtRaw(int x, int y, int z) {
if(isChunkAt(x, y, z)) {
return chunks.get(new Vector3i(x,y,z));
} else {
Chunk chunk = makeChunkAt(x, y, z);
setChunkAt(x, y, z, chunk);
//makeLightAt(chunk, x, y, z);
if(chunk == null)
throw new IllegalStateException("Failed to generate chunk");
return chunk;
}
}
// TODO refactor, package private is ugly.
static Chunk getChunkAtRaw(Vector3i pos) {
return getChunkAtRaw(pos.x, pos.y, pos.z);
}
/**
* Get a chunk at a given block position. Takes a block position and returns the chunk the given block position is in.
* @param x The x block position
* @param y The y block position
* @param z The z block position
* @return The chunk containing the given block position
*/
public static ChunkReference getChunkAtBlockPos(int x, int y, int z) {
return getChunkAt(x/Chunk.size,y/Chunk.size,z/Chunk.size);
}
private static Chunk getChunkAtBlockPosRaw(int x, int y, int z) {
return getChunkAtRaw(x/Chunk.size,y/Chunk.size,z/Chunk.size);
}
private static boolean isChunkAtBlockPos(int x, int y, int z) {
return isChunkAt(x/Chunk.size,y/Chunk.size,z/Chunk.size);
}
/**
* Replace a chunk at a given chunk location.
* @param x The x chunk position
* @param y The y chunk position
* @param z The z chunk position
* @param chunk The chunk to replace with
*/
private static void setChunkAt(int x, int y, int z, Chunk chunk) {
if(chunk == null) {
throw new IllegalStateException("Trying to insert null as a chunk");
}
chunks.put(new Vector3i(x,y,z), chunk);
}
public static void freeChunkAt(int x, int y, int z) {
Chunk chunk = chunks.remove(new Vector3i(x,y,z));
if(chunk != null) {
chunk.free();
chunkRefs.remove(new Vector3i(x, y, z)).isChunkPresent = false;
}
}
public static int allocatedChunks() {
return chunks.size();
}
}

@ -0,0 +1,27 @@
package ity.opencraft.world.blocks;
import ity.opencraft.world.Block;
import ity.opencraft.world.BlockSide;
import org.joml.Vector3i;
public class BlockAir extends Block {
@Override
public boolean isTransparent(Block blockSide) {
return true;
}
@Override
public int textureIndex(BlockSide side, Vector3i pos) {
return 0;
}
@Override
public boolean visible() {
return false;
}
@Override
public boolean isSolid() {
return false;
}
}

@ -0,0 +1,22 @@
package ity.opencraft.world.blocks;
import ity.opencraft.world.Block;
import ity.opencraft.world.BlockSide;
import org.joml.Vector3i;
public class BlockCoalOre extends Block {
@Override
public boolean isTransparent(Block blockSide) {
return false;
}
@Override
public int textureIndex(BlockSide side, Vector3i pos) {
return 4;
}
@Override
public boolean isSolid() {
return true;
}
}

@ -0,0 +1,29 @@
package ity.opencraft.world.blocks;
import ity.opencraft.world.Block;
import ity.opencraft.world.BlockSide;
import ity.opencraft.mesh.BlockModelType;
import ity.opencraft.world.Blocks;
import org.joml.Vector3i;
public class BlockDandelion extends Block {
@Override
public boolean isTransparent(Block blockSide) {
return blockSide != Blocks.WATER;
}
@Override
public int textureIndex(BlockSide side, Vector3i pos) {
return 5;
}
@Override
public boolean isSolid() {
return true;
}
@Override
public BlockModelType modelType() {
return BlockModelType.CROSS;
}
}

@ -0,0 +1,27 @@
package ity.opencraft.world.blocks;
import ity.opencraft.world.Block;
import ity.opencraft.world.BlockSide;
import org.joml.Vector3i;
public class BlockDirt extends Block {
@Override
public boolean isTransparent(Block blockSide) {
return false;
}
@Override
public int textureIndex(BlockSide side, Vector3i pos) {
return 3;
}
@Override
public boolean isSolid() {
return true;
}
@Override
public boolean textureRotate(BlockSide side) {
return true;
}
}

@ -0,0 +1,46 @@
package ity.opencraft.world.blocks;
import ity.opencraft.world.BlockSide;
import ity.opencraft.world.Block;
import ity.opencraft.world.World;
import org.joml.Vector3i;
public class BlockGrass extends Block {
@Override
public boolean isTransparent(Block blockSide) {
return false;
}
@Override
public int textureIndex(BlockSide side, Vector3i pos) {
switch(side) {
case TOP:
return 2;
case LEFT:
if(World.getBlockAt(pos.x-1, pos.y-1, pos.z).textureIndex(BlockSide.TOP, new Vector3i(pos.x-1, pos.y-1, pos.z)) == 2) return 2;
else return 0;
case RIGHT:
if(World.getBlockAt(pos.x+1, pos.y-1, pos.z).textureIndex(BlockSide.TOP, new Vector3i(pos.x+1, pos.y-1, pos.z)) == 2) return 2;
else return 0;
case FRONT:
if(World.getBlockAt(pos.x, pos.y-1, pos.z+1).textureIndex(BlockSide.TOP, new Vector3i(pos.x, pos.y-1, pos.z+1)) == 2) return 2;
else return 0;
case BACK:
if(World.getBlockAt(pos.x, pos.y-1, pos.z-1).textureIndex(BlockSide.TOP, new Vector3i(pos.x, pos.y-1, pos.z-1)) == 2) return 2;
else return 0;
case BOTTOM:
return 3;
}
return -1;
}
@Override
public boolean textureRotate(BlockSide side) {
return side.equals(BlockSide.BOTTOM);
}
@Override
public boolean isSolid() {
return true;
}
}

@ -0,0 +1,24 @@
package ity.opencraft.world.blocks;
import ity.opencraft.world.Block;
import ity.opencraft.world.BlockSide;
import ity.opencraft.world.Blocks;
import org.joml.Vector3i;
public class BlockLeaves extends Block {
@Override
public boolean isTransparent(Block blockSide) {
return blockSide != Blocks.LEAVES;
}
@Override
public int textureIndex(BlockSide side, Vector3i pos) {
return 9;
}
@Override
public boolean isSolid() {
return true;
}
}

@ -0,0 +1,27 @@
package ity.opencraft.world.blocks;
import ity.opencraft.world.BlockSide;
import ity.opencraft.world.Block;
import org.joml.Vector3i;
public class BlockStone extends Block {
@Override
public boolean isTransparent(Block blockSide) {
return false;
}
@Override
public int textureIndex(BlockSide side, Vector3i pos) {
return 1;
}
@Override
public boolean isSolid() {
return true;
}
@Override
public boolean textureRotate(BlockSide side) {
return true;
}
}

@ -0,0 +1,29 @@
package ity.opencraft.world.blocks;
import ity.opencraft.mesh.BlockModelType;
import ity.opencraft.world.Block;
import ity.opencraft.world.BlockSide;
import ity.opencraft.world.Blocks;
import org.joml.Vector3i;
public class BlockTallGrass extends Block {
@Override
public boolean isTransparent(Block blockSide) {
return blockSide != Blocks.WATER;
}
@Override
public int textureIndex(BlockSide side, Vector3i pos) {
return 6;
}
@Override
public boolean isSolid() {
return true;
}
@Override
public BlockModelType modelType() {
return BlockModelType.CROSS;
}
}

@ -0,0 +1,34 @@
package ity.opencraft.world.blocks;
import ity.opencraft.mesh.BlockModelType;
import ity.opencraft.world.Block;
import ity.opencraft.world.BlockSide;
import ity.opencraft.world.Blocks;
import org.joml.Vector3i;
public class BlockWater extends Block {
@Override
public boolean isTransparent(Block blockSide) {
return blockSide != Blocks.WATER;
}
@Override
public int textureIndex(BlockSide side, Vector3i pos) {
return 10;
}
@Override
public boolean isSolid() {
return true;
}
@Override
public BlockModelType modelType() {
return BlockModelType.WATER;
}
@Override
public boolean isBlend() {
return true;
}
}

@ -0,0 +1,26 @@
package ity.opencraft.world.blocks;
import ity.opencraft.world.Block;
import ity.opencraft.world.BlockSide;
import org.joml.Vector3i;
public class BlockWoodLog extends Block {
@Override
public boolean isTransparent(Block blockSide) {
return false;
}
@Override
public int textureIndex(BlockSide side, Vector3i pos) {
return switch (side) {
case TOP, BOTTOM -> 8;
case LEFT, RIGHT, FRONT, BACK -> 7;
default -> -1;
};
}
@Override
public boolean isSolid() {
return true;
}
}

@ -0,0 +1,45 @@
#version 460
in vec3 passNorm;
in vec2 passTexUv;
in vec3 fragPos;
in float passLight;
out vec4 color;
uniform sampler2D tex;
uniform vec2 iRes;
uniform vec3 camPos;
uniform float renderDistance;
uniform vec2 isWater;
uniform vec3 fogColor;
const float eps = 0.001;
bool eqFlt(float a, float b) {
return abs(a - b) < eps;
}
bool eqVec(vec3 a, vec3 b) {
return eqFlt(a.x,b.x) && eqFlt(a.y,b.y) && eqFlt(a.z,b.z);
}
const float fogThick = 1.0;
const float chunkSize = 32;
void main() {
vec4 smp = texture(tex, passTexUv);
vec3 col = texture(tex, passTexUv).rgb * ((passLight/2.0)+0.5);
if(eqVec(passNorm, vec3(0,-1,0)))
col *= 0.5;
else if(eqVec(passNorm, vec3(1,0,0)) || eqVec(passNorm, vec3(-1,0,0)))
col *= 0.7;
else if(eqVec(passNorm, vec3(0,0,1)) || eqVec(passNorm, vec3(0,0,-1)))
col *= 0.9;
vec3 toObj = fragPos - camPos;
float dist = length(toObj)/((renderDistance/isWater.y-fogThick)*chunkSize);
//col = mix(col, vec3(0.5f, 0.85f, 1.0f), min(1.0, pow(dist, 5)));
col = mix(col, fogColor, min(1.0, pow(dist, isWater.x)));
color = vec4(col, smp.a);
if(smp.a < 0.1f) discard;
}

@ -0,0 +1,22 @@
#version 460
in vec3 pos;
in vec3 norm;
in vec2 texUv;
in float light;
out vec3 passNorm;
out vec2 passTexUv;
out float passLight;
out vec3 fragPos;
uniform mat4 view;
uniform mat4 proj;
void main() {
gl_Position = proj * view * vec4(pos, 1);
passNorm = norm;
fragPos = pos;
passTexUv = texUv;
passLight = light;
}

@ -0,0 +1,26 @@
#version 460
in vec3 fragPos;
uniform vec2 iRes;
uniform float blueAlpha;
out vec4 color;
const float innerRadius = 0.0040;
const float outerRadius = 0.0050;
void main() {
vec2 uv = gl_FragCoord.xy/iRes*vec2(2.0)-vec2(1.0);
uv.x *= iRes.x / iRes.y;
float dist = length(vec2(0,0)-uv);
color = vec4(vec3(1.), 1.);
if(dist > innerRadius) {
if(dist > outerRadius) {
color = vec4(0.1, 0.1, 1.0, blueAlpha);
}
else {
color = vec4(vec3(1.), 1-(dist-innerRadius)*(1/(outerRadius-innerRadius)));
}
}
}

@ -0,0 +1,9 @@
#version 460
in vec3 pos;
out vec3 fragPos;
void main() {
gl_Position = vec4(pos, 1);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Loading…
Cancel
Save