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
|
@ -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" "$@"
|
@ -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…
Reference in new issue