Skip to content
Snippets Groups Projects
Commit 7c7ec09c authored by Janne Mareike Koschinski's avatar Janne Mareike Koschinski
Browse files

Initial demo version

parents
Branches master
No related tags found
No related merge requests found
Showing
with 1448 additions and 0 deletions
*.mca filter=lfs diff=lfs merge=lfs -text
*.mcr filter=lfs diff=lfs merge=lfs -text
*.iml
.gradle
/local.properties
/.idea/
.DS_Store
build/
out/
import org.gradle.internal.os.OperatingSystem
plugins {
application
id("org.jetbrains.kotlin.jvm").version("1.1.61")
}
group = "de.kuschku"
version = "1.0-SNAPSHOT"
configure<ApplicationPluginConvention> {
mainClassName = "de.kuschku.atlas.MainKt"
}
repositories {
jcenter()
mavenCentral()
}
dependencies {
compile(kotlin("stdlib"))
compile("com.flowpowered", "flow-nbt", "1.0.0")
val lwjglVersion = "3.1.5"
val lwjglNatives = when {
OperatingSystem.current().isWindows -> "natives-windows"
OperatingSystem.current().isMacOsX -> "natives-macos"
OperatingSystem.current().isLinux -> "natives-linux"
else -> throw IllegalArgumentException("OS not supported")
}
compile("org.lwjgl:lwjgl:$lwjglVersion")
compile("org.lwjgl:lwjgl-glfw:$lwjglVersion")
compile("org.lwjgl:lwjgl-jemalloc:$lwjglVersion")
compile("org.lwjgl:lwjgl-opengl:$lwjglVersion")
compile("org.lwjgl:lwjgl-sse:$lwjglVersion")
runtime("org.lwjgl:lwjgl:$lwjglVersion:$lwjglNatives")
runtime("org.lwjgl:lwjgl-glfw:$lwjglVersion:$lwjglNatives")
runtime("org.lwjgl:lwjgl-jemalloc:$lwjglVersion:$lwjglNatives")
runtime("org.lwjgl:lwjgl-opengl:$lwjglVersion:$lwjglNatives")
runtime("org.lwjgl:lwjgl-sse:$lwjglVersion:$lwjglNatives")
compile("org.joml:joml:1.9.6")
testCompile("junit", "junit", "4.12")
}
configure<JavaPluginConvention> {
sourceCompatibility = JavaVersion.VERSION_1_8
}
\ No newline at end of file
data/r.0.0.mca 0 → 100644 LFS
File added
File added
#Sat Nov 25 21:38:45 CET 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https://services.gradle.org/distributions/gradle-4.3.1-all.zip
gradlew 0 → 100755
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$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=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# 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
;;
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" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save ( ) {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
@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 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=
@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 init
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 init
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
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
: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 %CMD_LINE_ARGS%
: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
rootProject.name = 'atlas'
rootProject.buildFileName = 'build.gradle.kts'
package de.kuschku.atlas;
public class BrightnessGenerator {
/**
* Returns the value of the first parameter, clamped to be within the lower and upper limits given by the second and
* third parameters
*/
private static float clamp(float num, float min, float max)
{
if (num < min)
{
return min;
}
else
{
return num > max ? max : num;
}
}
private float lightBrightnessTable(int i) {
float f1 = 1.0F - (float)i / 15.0F;
return (1.0F - f1) / (f1 * 3.0F + 1.0F) * 1.0F + 0.0F;
}
private float getCelestialAngle(float time) {
float f = time - 0.25F;
float f1 = 1.0F - (float)((Math.cos((double)f * Math.PI) + 1.0D) / 2.0D);
f = f + (f1 - f) / 3.0F;
return f;
}
private float sunBrightness(float time) {
float f = getCelestialAngle(time);
float f1 = (float) (1.0F - (Math.cos(f * ((float)Math.PI * 2F)) * 2.0F + 0.2F));
f1 = clamp(f1, 0.0F, 1.0F);
f1 = 1.0F - f1;
return f1 * 0.8F + 0.2F;
}
public float[] determineBrightness(int y, int x, int mcTime, float gammaSetting, boolean nightVision, float nightVisionBrightness) {
float sunLevel = sunBrightness(mcTime);
float scaledSunBrightness = sunLevel * 0.95F + 0.05F;
float skyLight = lightBrightnessTable(y) * scaledSunBrightness;
float blockLight = lightBrightnessTable(x) * 1.5F;
float skyLightBrightness = skyLight * (sunLevel * 0.65F + 0.35F);
float blockLightGreen = blockLight * ((blockLight * 0.6F + 0.4F) * 0.6F + 0.4F);
float blockLightBlue = (float) (blockLight * (Math.pow (blockLight, 2.0f) *0.6F + 0.4F));
float redFloat = skyLightBrightness + blockLight;
float greenFloat = skyLightBrightness + blockLightGreen;
float blueFloat = skyLight + blockLightBlue;
redFloat = redFloat * 0.96F + 0.03F;
greenFloat = greenFloat * 0.96F + 0.03F;
blueFloat = blueFloat * 0.96F + 0.03F;
redFloat = clamp(redFloat, 0.0f, 1.0f);
greenFloat = clamp(greenFloat, 0.0f, 1.0f);
blueFloat = clamp(blueFloat, 0.0f, 1.0f);
if (nightVision) {
float nightVisionFactor = 1.0F / Math.max(redFloat, Math.max (greenFloat, blueFloat));
redFloat = redFloat * (1.0F - nightVisionBrightness) + redFloat * nightVisionFactor * nightVisionBrightness;
greenFloat =
greenFloat * (1.0F - nightVisionBrightness) + greenFloat * nightVisionFactor * nightVisionBrightness;
blueFloat = blueFloat * (1.0F - nightVisionBrightness) + blueFloat * nightVisionFactor * nightVisionBrightness;
}
redFloat = clamp(redFloat, 0.0f, 1.0f);
greenFloat = clamp(greenFloat, 0.0f, 1.0f);
blueFloat = clamp(blueFloat, 0.0f, 1.0f);
float redDiff = 1.0F - redFloat;
float greenDiff = 1.0F - greenFloat;
float blueDiff = 1.0F - blueFloat;
redDiff = (float) (1.0F - Math.pow (redDiff, 4.0f));
greenDiff = (float) (1.0F - Math.pow (greenDiff, 4.0f));
blueDiff = (float) (1.0F - Math.pow (blueDiff, 4.0f));
redFloat = redFloat * (1.0F - gammaSetting) + redDiff * gammaSetting;
greenFloat = greenFloat * (1.0F - gammaSetting) + greenDiff * gammaSetting;
blueFloat = blueFloat * (1.0F - gammaSetting) + blueDiff * gammaSetting;
redFloat = redFloat * 0.96F + 0.03F;
greenFloat = greenFloat * 0.96F + 0.03F;
blueFloat = blueFloat * 0.96F + 0.03F;
redFloat = clamp(redFloat, 0.0f, 1.0f);
greenFloat = clamp(greenFloat, 0.0f, 1.0f);
blueFloat = clamp(blueFloat, 0.0f, 1.0f);
int red = (int) (redFloat * 255.0F);
int green = (int) (greenFloat * 255.0F);
int blue = (int) (blueFloat * 255.0F);
return new float[]{red, green, blue};
}
}
package de.kuschku.atlas;
/*
** 2011 January 5
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**/
/*
* 2011 February 16
*
* This source code is based on the work of Scaevolus (see notice above).
* It has been slightly modified by Mojang AB (constants instead of magic
* numbers, a chunk timestamp header, and auto-formatted according to our
* formatter template).
*
*/
// Interfaces with region files on the disk
/*
Region File Format
Concept: The minimum unit of storage on hard drives is 4KB. 90% of Minecraft
chunks are smaller than 4KB. 99% are smaller than 8KB. Write a simple
container to store chunks in single files in runs of 4KB sectors.
Each region file represents a 32x32 group of chunks. The conversion from
chunk number to region number is floor(coord / 32): a chunk at (30, -3)
would be in region (0, -1), and one at (70, -30) would be at (3, -1).
Region files are named "r.x.z.data", where x and z are the region coordinates.
A region file begins with a 4KB header that describes where chunks are stored
in the file. A 4-byte big-endian integer represents sector offsets and sector
counts. The chunk offset for a chunk (x, z) begins at byte 4*(x+z*32) in the
file. The bottom byte of the chunk offset indicates the number of sectors the
chunk takes up, and the top 3 bytes represent the sector number of the chunk.
Given a chunk offset o, the chunk data begins at byte 4096*(o/256) and takes up
at most 4096*(o%256) bytes. A chunk cannot exceed 1MB in size. If a chunk
offset is 0, the corresponding chunk is not stored in the region file.
Chunk data begins with a 4-byte big-endian integer representing the chunk data
length in bytes, not counting the length field. The length must be smaller than
4096 times the number of sectors. The next byte is a version field, to allow
backwards-compatible updates to how chunks are encoded.
A version of 1 represents a gzipped NBT file. The gzipped data is the chunk
length - 1.
A version of 2 represents a deflated (zlib compressed) NBT file. The deflated
data is the chunk length - 1.
*/
import kotlin.Pair;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.InflaterInputStream;
public class RegionFile {
public static final String ANVIL_EXTENSION = ".mca";
public static final String MCREGION_EXTENSION = ".mcr";
static final int CHUNK_HEADER_SIZE = 5;
private static final int VERSION_GZIP = 1;
private static final int VERSION_DEFLATE = 2;
private static final int SECTOR_BYTES = 4096;
private static final int SECTOR_INTS = SECTOR_BYTES / 4;
private static final byte emptySector[] = new byte[4096];
private final File fileName;
private final int offsets[];
private final int chunkTimestamps[];
private RandomAccessFile file;
private ArrayList<Boolean> sectorFree;
private int sizeDelta;
private long lastModified = 0;
public RegionFile(File path) {
offsets = new int[SECTOR_INTS];
chunkTimestamps = new int[SECTOR_INTS];
fileName = path;
debugln("REGION LOAD " + fileName);
sizeDelta = 0;
try {
if (path.exists()) {
lastModified = path.lastModified();
}
file = new RandomAccessFile(path, "rw");
if (file.length() < SECTOR_BYTES) {
/* we need to write the chunk offset table */
for (int i = 0; i < SECTOR_INTS; ++i) {
file.writeInt(0);
}
// write another sector for the timestamp info
for (int i = 0; i < SECTOR_INTS; ++i) {
file.writeInt(0);
}
sizeDelta += SECTOR_BYTES * 2;
}
if ((file.length() & 0xfff) != 0) {
/* the file size is not a multiple of 4KB, grow it */
for (int i = 0; i < (file.length() & 0xfff); ++i) {
file.write((byte) 0);
}
}
/* set up the available sector map */
int nSectors = (int) file.length() / SECTOR_BYTES;
sectorFree = new ArrayList<Boolean>(nSectors);
for (int i = 0; i < nSectors; ++i) {
sectorFree.add(true);
}
sectorFree.set(0, false); // chunk offset table
sectorFree.set(1, false); // for the last modified info
file.seek(0);
for (int i = 0; i < SECTOR_INTS; ++i) {
int offset = file.readInt();
offsets[i] = offset;
if (offset != 0 && (offset >> 8) + (offset & 0xFF) <= sectorFree.size()) {
for (int sectorNum = 0; sectorNum < (offset & 0xFF); ++sectorNum) {
sectorFree.set((offset >> 8) + sectorNum, false);
}
}
}
for (int i = 0; i < SECTOR_INTS; ++i) {
int lastModValue = file.readInt();
chunkTimestamps[i] = lastModValue;
}
} catch (IOException e) {
e.printStackTrace();
}
}
/* the modification date of the region file when it was first opened */
public long lastModified() {
return lastModified;
}
/* gets how much the region file has grown since it was last checked */
public synchronized int getSizeDelta() {
int ret = sizeDelta;
sizeDelta = 0;
return ret;
}
// various small debug printing helpers
private void debug(String in) {
// System.out.print(in);
}
private void debugln(String in) {
debug(in + "\n");
}
private void debug(String mode, int x, int z, String in) {
debug("REGION " + mode + " " + fileName.getName() + "[" + x + "," + z + "] = " + in);
}
private void debug(String mode, int x, int z, int count, String in) {
debug("REGION " + mode + " " + fileName.getName() + "[" + x + "," + z + "] " + count + "B = " + in);
}
private void debugln(String mode, int x, int z, String in) {
debug(mode, x, z, in + "\n");
}
/*
* gets an (uncompressed) stream representing the chunk data returns null if
* the chunk is not found or an error occurs
*/
public synchronized DataInputStream getChunkDataInputStream(int x, int z) {
if (outOfBounds(x, z)) {
debugln("READ", x, z, "out of bounds");
return null;
}
try {
int offset = getOffset(x, z);
if (offset == 0) {
// debugln("READ", x, z, "miss");
return null;
}
int sectorNumber = offset >> 8;
int numSectors = offset & 0xFF;
if (sectorNumber + numSectors > sectorFree.size()) {
debugln("READ", x, z, "invalid sector");
return null;
}
file.seek(sectorNumber * SECTOR_BYTES);
int length = file.readInt();
if (length > SECTOR_BYTES * numSectors) {
debugln("READ", x, z, "invalid length: " + length + " > 4096 * " + numSectors);
return null;
}
byte version = file.readByte();
if (version == VERSION_GZIP) {
byte[] data = new byte[length - 1];
file.read(data);
DataInputStream ret = new DataInputStream(new BufferedInputStream(new GZIPInputStream(new ByteArrayInputStream(data))));
// debug("READ", x, z, " = found");
return ret;
} else if (version == VERSION_DEFLATE) {
byte[] data = new byte[length - 1];
file.read(data);
DataInputStream ret = new DataInputStream(new BufferedInputStream(new InflaterInputStream(new ByteArrayInputStream(data))));
// debug("READ", x, z, " = found");
return ret;
}
debugln("READ", x, z, "unknown version " + version);
return null;
} catch (IOException e) {
debugln("READ", x, z, "exception");
return null;
}
}
public DataOutputStream getChunkDataOutputStream(int x, int z) {
if (outOfBounds(x, z)) return null;
return new DataOutputStream(new DeflaterOutputStream(new ChunkBuffer(x, z)));
}
/* write a chunk at (x,z) with length bytes of data to disk */
protected synchronized void write(int x, int z, byte[] data, int length) {
try {
int offset = getOffset(x, z);
int sectorNumber = offset >> 8;
int sectorsAllocated = offset & 0xFF;
int sectorsNeeded = (length + CHUNK_HEADER_SIZE) / SECTOR_BYTES + 1;
// maximum chunk size is 1MB
if (sectorsNeeded >= 256) {
return;
}
if (sectorNumber != 0 && sectorsAllocated == sectorsNeeded) {
/* we can simply overwrite the old sectors */
debug("SAVE", x, z, length, "rewrite");
write(sectorNumber, data, length);
} else {
/* we need to allocate new sectors */
/* mark the sectors previously used for this chunk as free */
for (int i = 0; i < sectorsAllocated; ++i) {
sectorFree.set(sectorNumber + i, true);
}
/* scan for a free space large enough to store this chunk */
int runStart = sectorFree.indexOf(true);
int runLength = 0;
if (runStart != -1) {
for (int i = runStart; i < sectorFree.size(); ++i) {
if (runLength != 0) {
if (sectorFree.get(i)) runLength++;
else runLength = 0;
} else if (sectorFree.get(i)) {
runStart = i;
runLength = 1;
}
if (runLength >= sectorsNeeded) {
break;
}
}
}
if (runLength >= sectorsNeeded) {
/* we found a free space large enough */
debug("SAVE", x, z, length, "reuse");
sectorNumber = runStart;
setOffset(x, z, (sectorNumber << 8) | sectorsNeeded);
for (int i = 0; i < sectorsNeeded; ++i) {
sectorFree.set(sectorNumber + i, false);
}
write(sectorNumber, data, length);
} else {
/*
* no free space large enough found -- we need to grow the
* file
*/
debug("SAVE", x, z, length, "grow");
file.seek(file.length());
sectorNumber = sectorFree.size();
for (int i = 0; i < sectorsNeeded; ++i) {
file.write(emptySector);
sectorFree.add(false);
}
sizeDelta += SECTOR_BYTES * sectorsNeeded;
write(sectorNumber, data, length);
setOffset(x, z, (sectorNumber << 8) | sectorsNeeded);
}
}
setTimestamp(x, z, (int) (System.currentTimeMillis() / 1000L));
} catch (IOException e) {
e.printStackTrace();
}
}
/* write a chunk data to the region file at specified sector number */
private void write(int sectorNumber, byte[] data, int length) throws IOException {
debugln(" " + sectorNumber);
file.seek(sectorNumber * SECTOR_BYTES);
file.writeInt(length + 1); // chunk length
file.writeByte(VERSION_DEFLATE); // chunk version number
file.write(data, 0, length); // chunk data
}
/* is this an invalid chunk coordinate? */
private boolean outOfBounds(int x, int z) {
return x < 0 || x >= 32 || z < 0 || z >= 32;
}
private int getOffset(int x, int z) {
return offsets[x + z * 32];
}
public boolean hasChunk(int x, int z) {
return getOffset(x, z) != 0;
}
public List<Pair<Integer, Integer>> chunks() {
ArrayList<Pair<Integer, Integer>> pairs = new ArrayList<>();
for (int z = 0; z < 32; z++) {
for (int x = 0; x < 32; x++) {
if (offsets[x + z * 32] > 0)
pairs.add(new Pair<>(x, z));
}
}
return pairs;
}
private void setOffset(int x, int z, int offset) throws IOException {
offsets[x + z * 32] = offset;
file.seek((x + z * 32) * 4);
file.writeInt(offset);
}
private void setTimestamp(int x, int z, int value) throws IOException {
chunkTimestamps[x + z * 32] = value;
file.seek(SECTOR_BYTES + (x + z * 32) * 4);
file.writeInt(value);
}
public void close() throws IOException {
file.close();
}
/*
* lets chunk writing be multithreaded by not locking the whole file as a
* chunk is serializing -- only writes when serialization is over
*/
class ChunkBuffer extends ByteArrayOutputStream {
private int x, z;
ChunkBuffer(int x, int z) {
super(8096); // initialize to 8KB
this.x = x;
this.z = z;
}
public void close() {
RegionFile.this.write(x, z, buf, count);
}
}
}
\ No newline at end of file
package de.kuschku.atlas
import de.kuschku.atlas.mesh.Mesh
import de.kuschku.atlas.mesh.buildChunkMesh
import de.kuschku.atlas.model.Chunk
import de.kuschku.atlas.util.GLFWwindow
import org.joml.Matrix4f
import org.joml.Vector3f
import org.lwjgl.glfw.GLFW.*
import org.lwjgl.glfw.GLFWCursorPosCallback
import org.lwjgl.glfw.GLFWErrorCallback
import org.lwjgl.glfw.GLFWFramebufferSizeCallback
import org.lwjgl.glfw.GLFWKeyCallback
import org.lwjgl.opengl.ARBDirectStateAccess.*
import org.lwjgl.opengl.ARBFramebufferObject.*
import org.lwjgl.opengl.GL.createCapabilities
import org.lwjgl.opengl.GL11.*
import org.lwjgl.system.MemoryUtil.NULL
class Demo {
private var windowWidth: Int = 0
private var windowHeight: Int = 0
private var msaa: Boolean = true
private var msaaFbo: Int = 0
private var msaaColorBuffer: Int = 0
private var msaaDepthBuffer: Int = 0
private var samples: Int = 0
private val view = Matrix4f()
private val projection = Matrix4f()
private var fps = 0
private var lastTime = 0.0
private var deltaTime = 0.0
private var lastFrame = 0.0
private var lastX = 0f
private var lastY = 0f
private var pitch = 0.0f
private var yaw = -90.0f
private var paused = true
private val cameraPos = Vector3f(0f, 0f, 3f)
private val cameraFront = Vector3f(0.0f, 0.0f, -1.0f)
private val cameraUp = Vector3f(0.0f, 1.0f, 0.0f)
fun demo(initialWidth: Int, initialHeight: Int, chunks: MutableList<Chunk>) {
windowWidth = initialWidth
windowHeight = initialHeight
lastX = initialWidth / 2.0f
lastY = initialHeight / 2.0f
glfwSetErrorCallback(errorCallback)
if (!glfwInit()) {
throw IllegalStateException("Unable to initialize GLFW")
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4)
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5)
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE)
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE)
val window = glfwCreateWindow(windowWidth, windowHeight, "Example", NULL, NULL)
if (window == NULL) {
glfwTerminate()
throw IllegalStateException("Unable to create GLFW window")
}
glfwMakeContextCurrent(window)
glfwSetFramebufferSizeCallback(window, framebufferSizeCallback)
glfwSetKeyCallback(window, keyCallback)
createCapabilities()
msaaFbo = glCreateFramebuffers()
msaaColorBuffer = glCreateRenderbuffers()
msaaDepthBuffer = glCreateRenderbuffers()
samples = glGetInteger(GL_MAX_SAMPLES)
framebufferSizeCallback(window, windowWidth, windowHeight)
glfwSetCursorPosCallback(window, cursorPosCallback)
val chunkShaderProgram = ShaderProgram(
this::class.java.getResource("/chunk/vertex.glsl").readText(),
this::class.java.getResource("/chunk/fragment.glsl").readText()
)
val chunkMeshes = chunks.map { buildChunkMesh(chunkShaderProgram, it) }
chunkMeshes.forEach(Mesh::create)
glEnable(GL_DEPTH_TEST)
while (!glfwWindowShouldClose(window)) {
// input
// -----
processInput(window)
if (msaa) {
glBindFramebuffer(GL_FRAMEBUFFER, msaaFbo)
glNamedFramebufferRenderbuffer(msaaFbo, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, msaaColorBuffer)
glNamedFramebufferRenderbuffer(msaaFbo, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, msaaDepthBuffer)
glNamedFramebufferRenderbuffer(msaaFbo, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, msaaDepthBuffer)
}
// render
// ------
glClearColor(0.2f, 0.3f, 0.3f, 1.0f)
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)
cameraFront.set(
(Math.cos(Math.toRadians(pitch.toDouble())) * Math.cos(Math.toRadians(yaw.toDouble()))).toFloat(),
(Math.sin(Math.toRadians(pitch.toDouble()))).toFloat(),
(Math.cos(Math.toRadians(pitch.toDouble())) * Math.sin(Math.toRadians(yaw.toDouble()))).toFloat()
).normalize()
view.setLookAt(
cameraPos,
Vector3f(cameraPos).add(cameraFront),
cameraUp
)
for (chunkMesh in chunkMeshes) {
chunkMesh.draw(view, projection)
}
if (msaa) {
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)
glBindFramebuffer(GL_READ_FRAMEBUFFER, msaaFbo)
glDrawBuffer(GL_BACK)
glBlitFramebuffer(0, 0, windowWidth, windowHeight, 0, 0, windowWidth, windowHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST)
}
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
glfwSwapBuffers(window)
glfwPollEvents()
determineFPS()
}
// optional: de-allocate all resources once they've outlived their purpose:
// ------------------------------------------------------------------------
chunkMeshes.forEach(Mesh::free)
glfwDestroyWindow(window)
framebufferSizeCallback.free()
// glfw: terminate, clearing all previously allocated GLFW resources.
// ------------------------------------------------------------------
glfwTerminate()
errorCallback.free()
}
private fun determineFPS() {
fps++
val time = glfwGetTime()
deltaTime = time - lastFrame
lastFrame = time
val elapsedSeconds = Math.floor(time) - Math.floor(lastTime)
if (elapsedSeconds > 1) {
lastTime = Math.floor(time)
println("$fps ${1000.0 / fps}ms")
fps = 0
}
}
private fun processInput(window: GLFWwindow) {
val cameraSpeed = 2.5f * deltaTime.toFloat()
val yBefore = cameraPos.y
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) {
cameraPos.add(Vector3f(cameraFront).mul(cameraSpeed))
cameraPos.y = yBefore
}
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) {
cameraPos.sub(Vector3f(cameraFront).mul(cameraSpeed))
cameraPos.y = yBefore
}
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) {
cameraPos.sub(Vector3f(cameraFront).cross(cameraUp).normalize().mul(cameraSpeed))
cameraPos.y = yBefore
}
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) {
cameraPos.add(Vector3f(cameraFront).cross(cameraUp).normalize().mul(cameraSpeed))
cameraPos.y = yBefore
}
if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS) {
cameraPos.add(Vector3f(cameraUp).mul(cameraSpeed))
}
if (glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS || glfwGetKey(window, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS) {
cameraPos.sub(Vector3f(cameraUp).mul(cameraSpeed))
}
}
private val keyCallback = GLFWKeyCallback.create { window, key, scancode, action, mods ->
if (action == GLFW_RELEASE) when (key) {
GLFW_KEY_ESCAPE -> glfwSetWindowShouldClose(window, true)
GLFW_KEY_M -> msaa = !msaa
GLFW_KEY_P -> {
val cursor_x = windowWidth / 2.0
val cursor_y = windowHeight / 2.0
glfwSetCursorPos(window, cursor_x, cursor_y)
lastX = cursor_x.toFloat()
lastY = cursor_y.toFloat()
glfwSetInputMode(window, GLFW_CURSOR, if (paused) GLFW_CURSOR_DISABLED else GLFW_CURSOR_NORMAL)
paused = !paused
}
}
}
private val framebufferSizeCallback = GLFWFramebufferSizeCallback.create { _, width, height ->
this.windowWidth = width
this.windowHeight = height
projection.identity().perspective(Math.toRadians(45.0).toFloat(), width * 1.0f / height, 0.1f, 100.0f)
glViewport(0, 0, width, height)
glNamedRenderbufferStorageMultisample(msaaColorBuffer, samples, GL_RGBA8, width, height)
glNamedRenderbufferStorageMultisample(msaaDepthBuffer, samples, GL_DEPTH24_STENCIL8, width, height)
glNamedFramebufferRenderbuffer(msaaFbo, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, msaaColorBuffer)
glNamedFramebufferRenderbuffer(msaaFbo, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, msaaDepthBuffer)
glNamedFramebufferRenderbuffer(msaaFbo, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, msaaDepthBuffer)
}
private val cursorPosCallback = GLFWCursorPosCallback.create { _, xpos, ypos ->
if (!paused) {
var xoffset: Float = xpos.toFloat() - lastX
var yoffset: Float = lastY - ypos.toFloat()
lastX = xpos.toFloat()
lastY = ypos.toFloat()
val sensitivity = 0.05f
xoffset *= sensitivity
yoffset *= sensitivity
yaw += xoffset
pitch += yoffset
if (pitch > 89.0f)
pitch = 89.0f
if (pitch < -89.0f)
pitch = -89.0f
}
}
private val errorCallback = GLFWErrorCallback.createThrow()
}
\ No newline at end of file
package de.kuschku.atlas
import de.kuschku.atlas.model.Chunk
import de.kuschku.atlas.model.parseRegionFile
fun main(args: Array<String>) {
val width = 800
val height = 600
val chunks = mutableListOf<Chunk>()
parseRegionFile("data/r.0.0.mca", chunks)
val demo = Demo()
demo.demo(width, height, chunks)
}
\ No newline at end of file
package de.kuschku.atlas
import org.lwjgl.opengl.GL20.*
class ShaderProgram private constructor() {
private var id: Int = -1
constructor(vertexShaderSource: String, fragmentShaderSource: String) : this() {
val vertexShader = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(vertexShader, vertexShaderSource)
glCompileShader(vertexShader)
if (glGetShaderi(vertexShader, GL_COMPILE_STATUS) == 0) {
println("ERROR::SHADER::VERTEX::COMPILATION_FAILED")
println(glGetShaderInfoLog(vertexShader))
}
val fragmentShader = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(fragmentShader, fragmentShaderSource)
glCompileShader(fragmentShader)
if (glGetShaderi(fragmentShader, GL_COMPILE_STATUS) == 0) {
println("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED")
println(glGetShaderInfoLog(fragmentShader))
}
id = glCreateProgram()
glAttachShader(id, vertexShader)
glAttachShader(id, fragmentShader)
glLinkProgram(id)
if (glGetProgrami(id, GL_LINK_STATUS) == 0) {
println("ERROR::SHADER::PROGRAM::LINKING_FAILED")
println(glGetProgramInfoLog(id))
}
glDeleteShader(fragmentShader)
glDeleteShader(vertexShader)
}
operator fun get(name: CharSequence) = glGetUniformLocation(id, name)
fun uniformLocation(name: CharSequence) = glGetUniformLocation(id, name)
operator fun set(name: CharSequence, value: Boolean) = glUniform1i(uniformLocation(name), if (value) 1 else 0)
operator fun set(name: CharSequence, value: Int) = glUniform1i(uniformLocation(name), value)
operator fun set(name: CharSequence, value: Float) = glUniform1f(uniformLocation(name), value)
fun use() = glUseProgram(id)
}
\ No newline at end of file
package de.kuschku.atlas.mesh
import de.kuschku.atlas.ShaderProgram
class ChunkMesh(program: ShaderProgram, vertices: FloatArray, indices: IntArray) : IndexedMesh(program, vertices, indices) {
init {
/*
// Block Light
strides.add(Stride(
sizeof<Int>(),
GL_INT,
1
))
// Sky Light
strides.add(Stride(
sizeof<Int>(),
GL_INT,
1
))
*/
}
}
\ No newline at end of file
package de.kuschku.atlas.mesh
import de.kuschku.atlas.ShaderProgram
import org.joml.Matrix4f
import org.lwjgl.opengl.ARBDirectStateAccess
import org.lwjgl.opengl.ARBVertexArrayObject
import org.lwjgl.opengl.GL11
import org.lwjgl.opengl.GL15
open class IndexedMesh(program: ShaderProgram, vertices: FloatArray, protected val indices: IntArray) : Mesh(program, vertices) {
protected var ebo: Int = -1
override fun create() {
super.create()
ebo = ARBDirectStateAccess.glCreateBuffers()
ARBDirectStateAccess.glNamedBufferData(ebo, indices, GL15.GL_STATIC_DRAW)
ARBDirectStateAccess.glVertexArrayElementBuffer(vao, ebo)
}
override fun draw(view: Matrix4f, projection: Matrix4f) {
shader.use()
updateUniforms(view, projection)
ARBVertexArrayObject.glBindVertexArray(vao)
GL11.glDrawElements(GL11.GL_TRIANGLES, indices.size, GL11.GL_UNSIGNED_INT, 0)
ARBVertexArrayObject.glBindVertexArray(0)
}
override fun free() {
if (ebo != -1) GL15.glDeleteBuffers(ebo)
}
}
\ No newline at end of file
package de.kuschku.atlas.mesh
import de.kuschku.atlas.ShaderProgram
import de.kuschku.atlas.util.memoryStack
import de.kuschku.atlas.util.sizeof
import org.joml.Matrix4f
import org.joml.Vector3f
import org.lwjgl.opengl.ARBDirectStateAccess.*
import org.lwjgl.opengl.ARBVertexArrayObject.glBindVertexArray
import org.lwjgl.opengl.ARBVertexArrayObject.glDeleteVertexArrays
import org.lwjgl.opengl.GL11.*
import org.lwjgl.opengl.GL15.GL_STATIC_DRAW
import org.lwjgl.opengl.GL15.glDeleteBuffers
import org.lwjgl.opengl.GL20.glUniformMatrix4fv
import org.lwjgl.system.MemoryUtil.NULL
open class Mesh(val shader: ShaderProgram, protected val vertices: FloatArray) {
protected var vao: Int = -1
protected var vbo: Int = -1
protected val model = Matrix4f()
var position = Vector3f(0.0f, 0.0f, 0.0f)
var scale = 1.0f
protected var strides = mutableListOf(
Stride(
typeSize = sizeof<Float>(),
glType = GL_FLOAT,
size = 3
)
)
open fun create() {
vao = glCreateVertexArrays()
vbo = glCreateBuffers()
glNamedBufferData(vbo, vertices, GL_STATIC_DRAW)
val strideWidth = strides.sumBy { it.typeSize * it.size }
var offset = 0
for ((index, stride) in strides.withIndex()) {
glEnableVertexArrayAttrib(vao, index)
glVertexArrayVertexBuffer(vao, 0, vbo, NULL + offset, strideWidth)
glVertexArrayAttribFormat(vao, index, stride.size, stride.glType, false, 0)
glVertexArrayAttribBinding(vao, index, 0)
offset += stride.typeSize + stride.size
}
}
protected fun updateUniforms(view: Matrix4f, projection: Matrix4f) {
memoryStack {
val floatBuffer = mallocFloat(16)
view.get(floatBuffer)
glUniformMatrix4fv(shader["view"], false, floatBuffer)
projection.get(floatBuffer)
glUniformMatrix4fv(shader["projection"], false, floatBuffer)
model.identity()
.translate(position)
.scale(scale)
.get(floatBuffer)
glUniformMatrix4fv(shader["model"], false, floatBuffer)
}
}
open fun draw(view: Matrix4f, projection: Matrix4f) {
shader.use()
updateUniforms(view, projection)
glBindVertexArray(vao)
glDrawArrays(GL_TRIANGLES, vertices.size, GL_FLOAT)
glBindVertexArray(0)
}
open fun free() {
if (vao != -1) glDeleteVertexArrays(vao)
if (vbo != -1) glDeleteBuffers(vbo)
}
data class Stride(
val typeSize: Int,
val glType: Int,
val size: Int
)
}
package de.kuschku.atlas.mesh
import de.kuschku.atlas.ShaderProgram
import de.kuschku.atlas.model.Chunk
import org.joml.Vector3f
val cubeVertices = arrayOf(
/* 0 */ Vector3f(+0.5f, +0.5f, -0.5f), // back top right
/* 1 */ Vector3f(+0.5f, -0.5f, -0.5f), // back bottom right
/* 2 */ Vector3f(-0.5f, -0.5f, -0.5f), // back bottom left
/* 3 */ Vector3f(-0.5f, +0.5f, -0.5f), // back top left
/* 4 */ Vector3f(+0.5f, +0.5f, +0.5f), // front top right
/* 5 */ Vector3f(+0.5f, -0.5f, +0.5f), // front bottom right
/* 6 */ Vector3f(-0.5f, -0.5f, +0.5f), // front bottom left
/* 7 */ Vector3f(-0.5f, +0.5f, +0.5f) // front top left
)
val cubeIndices = intArrayOf( // note that we start from 0!
2, 1, 0,
0, 3, 2,
6, 5, 4,
4, 7, 6,
7, 3, 2,
2, 6, 7,
4, 0, 1,
1, 5, 4,
2, 1, 5,
5, 6, 2,
3, 0, 4,
4, 7, 3
)
fun buildChunkMesh(shaderProgram: ShaderProgram, chunk: Chunk): ChunkMesh {
val zero = 0.toByte()
val vertices = ArrayList<Float>()
val indices = ArrayList<Int>()
var cubeIndex = 0
val chunkPosition = Vector3f(chunk.position.first.toFloat(), 0f, chunk.position.second.toFloat()).mul(Chunk.CHUNK_SIZE.toFloat())
for (section in chunk.sections.values) {
val sectionPosition = Vector3f(0f, section.y * Chunk.CHUNK_SIZE.toFloat(), 0f).add(chunkPosition)
for (y in 0 until Chunk.CHUNK_SIZE) {
for (z in 0 until Chunk.CHUNK_SIZE) {
for (x in 0 until Chunk.CHUNK_SIZE) {
val blockIndex = y * Chunk.CHUNK_SIZE * Chunk.CHUNK_SIZE + z * Chunk.CHUNK_SIZE + x
val position = Vector3f(x.toFloat(), y.toFloat(), z.toFloat()).add(sectionPosition)
if (section.blocks[blockIndex] != 0) {
val skyLight = chunk.skyLight(x, section.y * Chunk.CHUNK_SIZE + y + 1, z)
if (skyLight != zero) {
val tmp = Vector3f()
for (index in cubeIndices) {
indices.add(index + cubeVertices.size * cubeIndex)
}
for (vertex in cubeVertices) {
tmp.set(position).add(vertex).div(64f)
vertices.add(tmp.x)
vertices.add(tmp.y)
vertices.add(tmp.z)
/*
vertices[verticesIndex++] = blockLight.toFloat()
vertices[verticesIndex++] = skyLight.toFloat()
*/
}
cubeIndex++
}
}
}
}
}
}
return ChunkMesh(shaderProgram, vertices.toFloatArray(), indices.toIntArray())
}
\ No newline at end of file
package de.kuschku.atlas.model;
data class Chunk(val position: Pair<Int, Int>, val heightmap: IntArray, val sections: Map<Int, Section>) {
companion object {
val CHUNK_SIZE = 16
}
fun blockLight(x: Int, y: Int, z: Int): Byte {
return sections[y]?.blockLight?.get((y % CHUNK_SIZE) * CHUNK_SIZE * CHUNK_SIZE + z * CHUNK_SIZE + x) ?: 0
}
fun skyLight(x: Int, y: Int, z: Int): Byte {
return sections[y]?.skyLight?.get((y % CHUNK_SIZE) * CHUNK_SIZE * CHUNK_SIZE + z * CHUNK_SIZE + x) ?: 0
}
fun block(x: Int, y: Int, z: Int): Int {
return sections[y]?.blocks?.get((y % CHUNK_SIZE) * CHUNK_SIZE * CHUNK_SIZE + z * CHUNK_SIZE + x) ?: 0
}
fun data(x: Int, y: Int, z: Int): Byte {
return sections[y]?.data?.get((y % CHUNK_SIZE) * CHUNK_SIZE * CHUNK_SIZE + z * CHUNK_SIZE + x) ?: 0
}
}
package de.kuschku.atlas.model
import com.flowpowered.nbt.*
import com.flowpowered.nbt.stream.NBTInputStream
import de.kuschku.atlas.RegionFile
import de.kuschku.atlas.util.convertBlocks
import de.kuschku.atlas.util.convertNibbleArray
import java.io.File
fun parseSection(section: CompoundMap): Section {
val blocksTag = (section["Blocks"] as ByteArrayTag).value
val addTag = (section["Add"] as ByteArrayTag?)?.value
val blocks = convertBlocks(blocksTag, addTag)
return Section(
y = (section["Y"] as ByteTag).value,
blockLight = convertNibbleArray((section["BlockLight"] as ByteArrayTag).value),
skyLight = convertNibbleArray((section["SkyLight"] as ByteArrayTag).value),
blocks = blocks,
data = convertNibbleArray((section["Data"] as ByteArrayTag).value)
)
}
fun parseChunk(chunk: CompoundMap): Chunk {
val level = (chunk["Level"] as CompoundTag).value
val sections = (level["Sections"] as ListTag<CompoundTag>).value
val heightMap = (level["HeightMap"] as IntArrayTag).value
val xPos = (level["xPos"] as IntTag).value
val zPos = (level["zPos"] as IntTag).value
val builtSections = sections.map(CompoundTag::getValue).map(::parseSection)
val sectionMap = mutableMapOf<Int, Section>()
for (section in builtSections) {
for (i in 0 until Chunk.CHUNK_SIZE) {
sectionMap[section.y * Chunk.CHUNK_SIZE + i] = section
}
}
return Chunk(
position = Pair(xPos, zPos),
heightmap = heightMap,
sections = sectionMap
)
}
fun parseRegionFile(pathName: String, chunkStorage: MutableList<Chunk>) {
val regionFile = RegionFile(File(pathName))
for ((x, z) in regionFile.chunks()) {
val stream = NBTInputStream(regionFile.getChunkDataInputStream(x, z), false)
chunkStorage.add(parseChunk((stream.readTag() as CompoundTag).value))
}
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment