commit e1cb8cf71fedf9836372e26da75ff65ec3d390bc Author: Speiger Date: Fri Aug 27 06:07:15 2021 +0200 Initial Commit diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..8b3d57e --- /dev/null +++ b/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..00a51af --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +# +# https://help.github.com/articles/dealing-with-line-endings/ +# +# These are explicitly windows files and should use crlf +*.bat text eol=crlf + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a510247 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# Ignore Gradle project-specific cache directory +.gradle + +# Ignore Gradle build output directory +build +/bin/ \ No newline at end of file diff --git a/.project b/.project new file mode 100644 index 0000000..0be527d --- /dev/null +++ b/.project @@ -0,0 +1,23 @@ + + + SimpleJavaEngine + Project SimpleJavaEngine created by Buildship. + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.buildship.core.gradleprojectbuilder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.buildship.core.gradleprojectnature + + diff --git a/.settings/org.eclipse.buildship.core.prefs b/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 0000000..e889521 --- /dev/null +++ b/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,2 @@ +connection.project.dir= +eclipse.preferences.version=1 diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..c86fcc6 --- /dev/null +++ b/build.gradle @@ -0,0 +1,41 @@ +apply plugin: 'java' +apply plugin: 'eclipse' + +eclipse { + classpath { + downloadJavadoc = true + downloadSources = true + } +} +sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8' +repositories { + mavenCentral() + maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } + maven { + url = "https://maven.speiger.com/repository/main" + } +} + +dependencies { + //LWJGL 3 + compile platform("org.lwjgl:lwjgl-bom:$lwjglVersion") + + compile "org.lwjgl:lwjgl" + compile "org.lwjgl:lwjgl-glfw" + compile "org.lwjgl:lwjgl-jemalloc" + compile "org.lwjgl:lwjgl-openal" + compile "org.lwjgl:lwjgl-opengl" + compile "org.lwjgl:lwjgl-stb" + compile "org.lwjgl:lwjgl::$lwjglNatives" + compile "org.lwjgl:lwjgl-glfw::$lwjglNatives" + compile "org.lwjgl:lwjgl-jemalloc::$lwjglNatives" + compile "org.lwjgl:lwjgl-openal::$lwjglNatives" + compile "org.lwjgl:lwjgl-opengl::$lwjglNatives" + compile "org.lwjgl:lwjgl-stb::$lwjglNatives" + + //Gson + compile 'com.google.code.gson:gson:2.8.6' + + //Primitive Collections + compile 'de.speiger:Primitive-Collections:0.3.4' +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..096849f --- /dev/null +++ b/gradle.properties @@ -0,0 +1,4 @@ +org.gradle.jvmargs=-Xmx2G + +lwjglVersion = 3.3.0-SNAPSHOT +lwjglNatives = natives-windows \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..490fda8 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..a4b4429 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..2fe81a7 --- /dev/null +++ b/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or 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 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='"-Xmx64m" "-Xms64m"' + +# 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 or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; 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=`expr $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" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..62bd9b9 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,103 @@ +@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 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 diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..6e22cf0 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,10 @@ +/* + * 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/6.3/userguide/multi_project_builds.html + */ + +rootProject.name = 'SimpleJavaEngine' diff --git a/src/main/java/speiger/src/coreengine/assets/AssetLocation.java b/src/main/java/speiger/src/coreengine/assets/AssetLocation.java new file mode 100644 index 0000000..07d3d6d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/assets/AssetLocation.java @@ -0,0 +1,92 @@ +package speiger.src.coreengine.assets; + +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; + +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectOpenHashMap; +import speiger.src.collections.objects.utils.maps.Object2ObjectMaps; + +public final class AssetLocation +{ + static final Map LOCATION = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap()); + static final Function BUILDER = AssetLocation::compute; + final String domain; + final String location; + + private AssetLocation() {throw new UnsupportedOperationException("use AssetLocation.of instead");} + + private AssetLocation(String domain, String location) + { + this.domain = domain; + this.location = location; + } + + public String getDomain() + { + return domain; + } + + public String getLocation() + { + return location; + } + + public String getActualLocation() + { + return "assets/"+domain+"/"+location; + } + + public AssetLocation subAsset(String subPath) + { + return of(domain+":"+location + "/" + subPath); + } + + @Override + public String toString() + { + return domain+":"+location; + } + + @Override + public int hashCode() + { + return Objects.hash(domain, location); + } + + @Override + public boolean equals(Object obj) + { + if(obj == this) + { + return true; + } + if(obj instanceof AssetLocation) + { + AssetLocation location = (AssetLocation)obj; + return location.domain.equals(domain) && location.location.equals(this.location); + } + return false; + } + + public boolean matches(AssetLocation location) + { + return location.domain.equals(domain) && location.location.equals(this.location); + } + + public static final AssetLocation of(String domain, String location) + { + return of((domain == null || domain.isEmpty() ? "base" : domain)+":"+location); + } + + public static final AssetLocation of(String location) + { + return LOCATION.computeIfAbsent(location.indexOf(":") == -1 ? "base:"+location : location, BUILDER); + } + + private static final AssetLocation compute(String s) + { + int index = s.indexOf(":"); + return new AssetLocation(s.substring(0, index), s.substring(index+1, s.length())); + } +} diff --git a/src/main/java/speiger/src/coreengine/assets/AssetManager.java b/src/main/java/speiger/src/coreengine/assets/AssetManager.java new file mode 100644 index 0000000..0aaf702 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/assets/AssetManager.java @@ -0,0 +1,186 @@ +package speiger.src.coreengine.assets; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.zip.ZipFile; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap; +import speiger.src.coreengine.assets.impl.FolderAssetPackage; +import speiger.src.coreengine.assets.impl.ZipAssetPackage; +import speiger.src.coreengine.assets.reloader.IReloadableResource; + +public class AssetManager implements IReloadableResource +{ + Map domains = new Object2ObjectLinkedOpenHashMap(); + File file; + + public AssetManager(File file) + { + this.file = file; + } + + public File getMainAsset() + { + return file; + } + + @Override + public void reload() + { + domains.clear(); + loadAssetPackage(file); + } + + public void loadAssetPackage(File file) + { + if(file.isDirectory()) + { + loadAssets(new FolderAssetPackage(file)); + } + else if(isZipFile(file)) + { + loadAssets(new ZipAssetPackage(file)); + } + } + + public void loadAssets(IAssetPackage packages) + { + for(String s : packages.getDomains()) + { + DomainAssets entry = domains.get(s); + if(entry == null) + { + entry = new DomainAssets(); + domains.put(s, entry); + } + entry.addAssets(packages); + } + } + + public Set getDomains() + { + return domains.keySet(); + } + + public IAsset getAsset(AssetLocation location) throws IOException + { + DomainAssets asset = domains.get(location.getDomain()); + if(asset == null) + { + throw new FileNotFoundException("File["+location.toString()+"] not found"); + } + return asset.getAsset(location); + } + + public MultiAsset getAssets(AssetLocation...locations) + { + IAsset[] asset = new IAsset[locations.length]; + for(int i = 0,m=locations.length;i packages = new ObjectArrayList(); + + public void addAssets(IAssetPackage assets) + { + packages.add(assets); + } + + public IAsset getAsset(AssetLocation location) throws IOException + { + for(int i = packages.size() - 1;i>=0;i--) + { + IAsset asset = packages.get(i).getAsset(location); + if(asset != null) + { + return asset; + } + } + throw new FileNotFoundException("File["+location.toString()+"] not found"); + } + + public MultiAsset getMultiAsset(AssetLocation location, AssetLocation[] subLocations) + { + for(int i = packages.size() - 1;i>=0;i--) + { + IAssetPackage entry = packages.get(i); + IAsset asset = entry.getAsset(location); + if(asset != null) + { + IAsset[] result = new IAsset[1 + subLocations.length]; + result[0] = asset; + for(int x = 0;x assets = new ObjectArrayList(); + for(IAssetPackage entry : packages) + { + IAsset asset = entry.getAsset(location); + if(asset != null) + { + assets.add(asset); + } + } + if(assets.isEmpty()) + { + throw new FileNotFoundException("File["+location.toString()+"] not found"); + } + return new MultiAsset(assets.toArray(new IAsset[assets.size()])); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/assets/IAsset.java b/src/main/java/speiger/src/coreengine/assets/IAsset.java new file mode 100644 index 0000000..aa0ea29 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/assets/IAsset.java @@ -0,0 +1,22 @@ +package speiger.src.coreengine.assets; + +import java.awt.image.BufferedImage; +import java.io.BufferedReader; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; + +import com.google.gson.JsonObject; + +public interface IAsset extends Closeable +{ + public AssetLocation getLocation(); + + public InputStream getStream() throws IOException; + + public BufferedImage getTexture() throws Exception; + + public BufferedReader getStringReader() throws IOException; + + public JsonObject getJsonObject() throws IOException; +} diff --git a/src/main/java/speiger/src/coreengine/assets/IAssetPackage.java b/src/main/java/speiger/src/coreengine/assets/IAssetPackage.java new file mode 100644 index 0000000..5d46f61 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/assets/IAssetPackage.java @@ -0,0 +1,10 @@ +package speiger.src.coreengine.assets; + +import java.util.List; + +public interface IAssetPackage +{ + public List getDomains(); + + public IAsset getAsset(AssetLocation location); +} diff --git a/src/main/java/speiger/src/coreengine/assets/MultiAsset.java b/src/main/java/speiger/src/coreengine/assets/MultiAsset.java new file mode 100644 index 0000000..a89d7b2 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/assets/MultiAsset.java @@ -0,0 +1,41 @@ +package speiger.src.coreengine.assets; + +import java.io.Closeable; +import java.io.IOException; + +public class MultiAsset implements Closeable +{ + IAsset[] assets; + + public MultiAsset(IAsset[] assets) + { + this.assets = assets; + } + + @Override + public void close() throws IOException + { + if(assets != null) + { + for(int i = 0,m=assets.length;i toClose = new ArrayList(); + + public FileAsset(File file, AssetLocation location) + { + this.file = file; + this.location = location; + } + + @Override + public void close() throws IOException + { + for(Closeable close : toClose) + { + try + { + close.close(); + } + catch(Exception e) + { + e.printStackTrace(); + } + } + toClose.clear(); + } + + @Override + public AssetLocation getLocation() + { + return location; + } + + @Override + public InputStream getStream() throws IOException + { + return new BufferedInputStream(markClosed(new FileInputStream(file))); + } + + @Override + public BufferedReader getStringReader() throws IOException + { + return new BufferedReader(markClosed(new FileReader(file))); + } + + @Override + public BufferedImage getTexture() throws Exception + { + return ImageIO.read(file); + } + + @Override + public JsonObject getJsonObject() throws IOException + { + return JsonUtil.loadFile(file); + } + + protected T markClosed(T value) + { + toClose.add(value); + return value; + } +} diff --git a/src/main/java/speiger/src/coreengine/assets/impl/FolderAssetPackage.java b/src/main/java/speiger/src/coreengine/assets/impl/FolderAssetPackage.java new file mode 100644 index 0000000..f7efdf4 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/assets/impl/FolderAssetPackage.java @@ -0,0 +1,47 @@ +package speiger.src.coreengine.assets.impl; + +import java.io.File; +import java.io.FileFilter; +import java.util.ArrayList; +import java.util.List; + +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.assets.IAsset; +import speiger.src.coreengine.assets.IAssetPackage; + +public class FolderAssetPackage implements IAssetPackage +{ + static final FileFilter FOLDER_FILTER = new FileFilter(){ + @Override + public boolean accept(File f) + { + return f.isDirectory(); + } + }; + File file; + + public FolderAssetPackage(File file) + { + this.file = file; + } + + @Override + public List getDomains() + { + List result = new ArrayList(); + File asset = new File(file, "assets/"); + for(File file : asset.listFiles(FOLDER_FILTER)) + { + result.add(file.getAbsolutePath().substring(asset.getAbsolutePath().length() + 1).replace("\\", "/").replace("\"", "/")); + } + return result; + } + + @Override + public IAsset getAsset(AssetLocation location) + { + File asset = new File(file, location.getActualLocation()); + return asset.isFile() ? new FileAsset(asset, location) : null; + } + +} diff --git a/src/main/java/speiger/src/coreengine/assets/impl/ZipAsset.java b/src/main/java/speiger/src/coreengine/assets/impl/ZipAsset.java new file mode 100644 index 0000000..c0d7e32 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/assets/impl/ZipAsset.java @@ -0,0 +1,75 @@ +package speiger.src.coreengine.assets.impl; + +import java.awt.image.BufferedImage; +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import javax.imageio.ImageIO; + +import com.google.gson.JsonObject; + +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.assets.IAsset; +import speiger.src.coreengine.utils.helpers.JsonUtil; + +public class ZipAsset implements IAsset +{ + ZipFile file; + ZipEntry entry; + AssetLocation location; + Runnable runnable; + + public ZipAsset(ZipFile file, ZipEntry entry, AssetLocation location, Runnable runnable) + { + this.file = file; + this.entry = entry; + this.location = location; + this.runnable = runnable; + } + + @Override + public void close() throws IOException + { + if(runnable != null) + { + runnable.run(); + runnable = null; + } + } + + @Override + public AssetLocation getLocation() + { + return location; + } + + @Override + public InputStream getStream() throws IOException + { + return new BufferedInputStream(file.getInputStream(entry)); + } + + @Override + public BufferedImage getTexture() throws Exception + { + return ImageIO.read(file.getInputStream(entry)); + } + + @Override + public BufferedReader getStringReader() throws IOException + { + return new BufferedReader(new InputStreamReader(file.getInputStream(entry))); + } + + @Override + public JsonObject getJsonObject() throws IOException + { + return JsonUtil.loadFile(file.getInputStream(entry)); + } + +} diff --git a/src/main/java/speiger/src/coreengine/assets/impl/ZipAssetPackage.java b/src/main/java/speiger/src/coreengine/assets/impl/ZipAssetPackage.java new file mode 100644 index 0000000..98e6b8a --- /dev/null +++ b/src/main/java/speiger/src/coreengine/assets/impl/ZipAssetPackage.java @@ -0,0 +1,111 @@ +package speiger.src.coreengine.assets.impl; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.assets.IAsset; +import speiger.src.coreengine.assets.IAssetPackage; +import speiger.src.coreengine.utils.collections.iterators.IterableWrapper; + +public class ZipAssetPackage implements IAssetPackage +{ + public static final int ASSET_OFFSET = "assets/".length(); + File file; + ZipFile cache = null; + AtomicInteger usedReferences = new AtomicInteger(); + + public ZipAssetPackage(File file) + { + this.file = file; + } + + protected ZipFile getReference() throws IOException + { + usedReferences.getAndIncrement(); + if(cache == null) cache = new ZipFile(file); + return cache; + } + + protected void onReferenceClosed() + { + int left = usedReferences.decrementAndGet(); + if(left == 0 && cache != null) + { + try + { + cache.close(); + } + catch(Exception e) {e.printStackTrace();} + cache = null; + } + else if(left < 0) + { + usedReferences.set(0); + } + } + + @Override + public List getDomains() + { + try + { + List result = new ArrayList(); + ZipFile zip = new ZipFile(file); + for(ZipEntry entry : IterableWrapper.wrap(zip.entries())) + { + String name = entry.getName(); + if(isPathValid(name)) + { + result.add(name.substring(ASSET_OFFSET, name.length() - 1)); + } + } + zip.close(); + return result; + } + catch(IOException e) + { + e.printStackTrace(); + return Collections.emptyList(); + } + } + + private boolean isPathValid(String input) + { + if(input.startsWith("assets/")) + { + int index = input.indexOf("/", ASSET_OFFSET); + if(index != -1 && input.length() - index <= 1) + { + return true; + } + } + return false; + } + + @Override + public IAsset getAsset(AssetLocation location) + { + try + { + ZipFile zip = getReference(); + ZipEntry entry = zip.getEntry(location.getActualLocation()); + if(entry != null) + { + return new ZipAsset(zip, entry, location, this::onReferenceClosed); + } + } + catch(Exception e) + { + e.printStackTrace(); + } + onReferenceClosed(); + return null; + } +} diff --git a/src/main/java/speiger/src/coreengine/assets/language/I18n.java b/src/main/java/speiger/src/coreengine/assets/language/I18n.java new file mode 100644 index 0000000..8431b66 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/assets/language/I18n.java @@ -0,0 +1,16 @@ +package speiger.src.coreengine.assets.language; + +public class I18n +{ + static Language CURRENT_LANGUAGE; + + public static String translate(String key) + { + return CURRENT_LANGUAGE.translate(key); + } + + public String translate(String key, Object...args) + { + return CURRENT_LANGUAGE.translate(key, args); + } +} diff --git a/src/main/java/speiger/src/coreengine/assets/language/Language.java b/src/main/java/speiger/src/coreengine/assets/language/Language.java new file mode 100644 index 0000000..6fd7b20 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/assets/language/Language.java @@ -0,0 +1,47 @@ +package speiger.src.coreengine.assets.language; + +import java.util.Map; + +public class Language +{ + String language; + String code; + Map translations; + + public Language(String code, String language) + { + this.code = code; + this.language = language; + } + + public void load(Map data) + { + translations = data; + } + + public void clear() + { + translations = null; + } + + public String translate(String key) + { + String result = translations.get(key); + return result == null ? key : result; + } + + public String translate(String key, Object...args) + { + return String.format(translate(key), args); + } + + public String getCode() + { + return code; + } + + public String getLanguage() + { + return language; + } +} diff --git a/src/main/java/speiger/src/coreengine/assets/language/LanguageManager.java b/src/main/java/speiger/src/coreengine/assets/language/LanguageManager.java new file mode 100644 index 0000000..e4fcf67 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/assets/language/LanguageManager.java @@ -0,0 +1,151 @@ +package speiger.src.coreengine.assets.language; + +import java.util.Map; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectOpenHashMap; +import speiger.src.collections.objects.utils.maps.Object2ObjectMaps; +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.assets.AssetManager; +import speiger.src.coreengine.assets.MultiAsset; +import speiger.src.coreengine.assets.reloader.IReloadableResource; +import speiger.src.coreengine.utils.collections.iterators.IterableWrapper; + +public class LanguageManager implements IReloadableResource +{ + final AssetLocation location = AssetLocation.of("lang"); + Map languages = new Object2ObjectOpenHashMap(); + AssetManager assets; + String currentLanguage; + + @Override + public void reload() + { + languages.clear(); + try(MultiAsset langs = assets.getAllAsset(location.subAsset("langs.json"))) + { + for(int i = 0,m=langs.size();i map = new Object2ObjectOpenHashMap(); + loadLanguage(loadingLang, map); + if(loadingLang != "en_US") + { + loadLanguage("en_US", map); + } + lang.load(map); + return true; + } + + protected void loadLanguage(String lang, Map data) + { + try(MultiAsset language = assets.getAllAsset(location.subAsset(lang+".lang"))) + { + for(int i = 0,m=language.size();i 16) + { + continue; + } + Language lang = languages.get(key); + if(lang == null) + { + languages.put(key, new Language(key, value)); + } + } + } + catch(Exception e) + { + e.printStackTrace(); + } + } + +} diff --git a/src/main/java/speiger/src/coreengine/assets/reloader/IReloadableResource.java b/src/main/java/speiger/src/coreengine/assets/reloader/IReloadableResource.java new file mode 100644 index 0000000..a5d47b4 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/assets/reloader/IReloadableResource.java @@ -0,0 +1,8 @@ +package speiger.src.coreengine.assets.reloader; + +public interface IReloadableResource +{ + public void reload(); + + public default void destroy() {}; +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/assets/reloader/ResourceReloader.java b/src/main/java/speiger/src/coreengine/assets/reloader/ResourceReloader.java new file mode 100644 index 0000000..1725b45 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/assets/reloader/ResourceReloader.java @@ -0,0 +1,62 @@ +package speiger.src.coreengine.assets.reloader; + +import java.util.Set; + +import speiger.src.collections.objects.sets.ObjectLinkedOpenHashSet; + +public class ResourceReloader +{ +// public static ResourceReloader INSTANCE = new ResourceReloader(); + Set resources = new ObjectLinkedOpenHashSet(); + boolean globalRemoval = false; + boolean reloading = false; + + public T addReloadableResource(T resource) + { + return addReloadableResource(resource, false); + } + + public T addReloadableResource(T resource, boolean init) + { + if(resources.add(resource) && init) + { + resource.reload(); + } + return resource; + } + + public void removeReloadableResource(IReloadableResource resource) + { + if(globalRemoval || reloading) + { + return; + } + resources.remove(resource); + } + + public boolean isReloading() + { + return reloading; + } + + public void reloadResources() + { + reloading = true; + for(IReloadableResource resource : resources) + { + resource.reload(); + } + reloading = false; + } + + public void deleteResources() + { + globalRemoval = true; + for(IReloadableResource resource : resources) + { + resource.destroy(); + } + resources.clear(); + globalRemoval = false; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/ArrayUtil.java b/src/main/java/speiger/src/coreengine/math/ArrayUtil.java new file mode 100644 index 0000000..e77fc76 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/ArrayUtil.java @@ -0,0 +1,76 @@ +package speiger.src.coreengine.math; + +import java.util.Arrays; +import java.util.List; + +import speiger.src.collections.ints.lists.IntList; +import speiger.src.collections.objects.lists.ObjectArrayList; + +public class ArrayUtil +{ + public static int[] fromTo(int from, int to) + { + int[] result = new int[to - from]; + for(int i = 0,m=result.length;i List makeList(IntList indexes, List entries) + { + List result = new ObjectArrayList(indexes.size()); + for(int i = 0,m=indexes.size();i> 32) & 0xFFFFFFFFL); + } + + public static int toSecondInt(long value) + { + return (int)(value & 0xFFFFFFFFL); + } + + public static long addToLong(long value, long x, long z) + { + return ((x + toFirstInt(value)) & 0xFFFFFFFFL) << 32 | (((z + toSecondInt(value)) & 0xFFFFFFFFL)); + } + + public static int toInt(int firstShort, int secondShort) + { + return (firstShort & 0xFFFF) << 16 | (secondShort & 0xFFFF); + } + + public static int toFirstShort(int value) + { + return (short)(value >> 16 & 0xFFFF); + } + + public static int toSecondShort(int value) + { + return (short)(value & 0xFFFF); + } + + public static int addToInt(int value, int x, int z) + { + return ((x + toFirstShort(value)) & 0xFFFF) << 16 | ((z + toSecondShort(value)) & 0xFFFF); + } + + public static int addToInt(int value, Vec2i offset) + { + return ((offset.getX() + toFirstShort(value)) & 0xFFFF) << 16 | ((offset.getY() + toSecondShort(value)) & 0xFFFF); + } + + public static int pack_2_10_10_10_int_rev(float x, float y, float z, float w) + { + return (Math.round(w * 3F) & 3) << 30 | (Math.round(((z * 0.5F) + 0.5F) * 1023F) & 1023) << 20 | (Math.round(((y * 0.5F) + 0.5F) * 1023F) & 1023) << 10 | (Math.round(((x * 0.5F) + 0.5F) * 1023F) & 1023); + } + + public static int pack_10_10_10_2_int(float x, float y, float z, float w) + { + return (Math.round(((x * 0.5F) + 0.5F) * 1023F) & 1023) << 22 | (Math.round(((y * 0.5F) + 0.5F) * 1023F) & 1023) << 12 | (Math.round(((z * 0.5F) + 0.5F) * 1023F) & 1023) << 2 | (Math.round(w * 3F) & 3); + } +} diff --git a/src/main/java/speiger/src/coreengine/math/MathUtils.java b/src/main/java/speiger/src/coreengine/math/MathUtils.java new file mode 100644 index 0000000..9751414 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/MathUtils.java @@ -0,0 +1,171 @@ +package speiger.src.coreengine.math; + +public class MathUtils +{ + private static final float[] SIN_TABLE; + private static final float[] COS_TABLE; + private static final float TABLE_MASK; + private static final int SIN_MASK; + public static final double PI_INVERSION = 180D / Math.PI; + + public static float sin(float a) + { + return SIN_TABLE[(int)(a * TABLE_MASK) & SIN_MASK]; + } + + public static float sin(double a) + { + return SIN_TABLE[(int)(a * TABLE_MASK) & SIN_MASK]; + } + + public static float cos(float a) + { + return COS_TABLE[(int)(a * TABLE_MASK) & SIN_MASK]; + } + + public static float cos(double a) + { + return COS_TABLE[(int)(a * TABLE_MASK) & SIN_MASK]; + } + + public static byte clamp(byte min, byte max, byte current) + { + return current < min ? min : (current > max ? max : current); + } + + public static short clamp(short min, short max, short current) + { + return current < min ? min : (current > max ? max : current); + } + + public static int clamp(int min, int max, int current) + { + return current < min ? min : (current > max ? max : current); + } + + public static float clamp(float min, float max, float current) + { + return current < min ? min : (current > max ? max : current); + } + + public static double clamp(double min, double max, double current) + { + return current < min ? min : (current > max ? max : current); + } + + public static long clamp(long min, long max, long current) + { + return current < min ? min : (current > max ? max : current); + } + + public static byte sign(byte value) + { + return value > 0 ? 1 : (value < 0 ? -1 : (byte)0); + } + + public static short sign(short value) + { + return value > 0 ? 1 : (value < 0 ? -1 : (short)0); + } + + public static int sign(int value) + { + return value > 0 ? 1 : (value < 0 ? -1 : 0); + } + + public static long sign(long value) + { + return value > 0 ? 1 : (value < 0 ? -1 : 0); + } + + public static float sign(float value) + { + return value > 0 ? 1F : (value < 0 ? -1F : 0F); + } + + public static double sign(double value) + { + return value > 0 ? 1D : (value < 0 ? -1D : 0D); + } + + public static int ceil(double value) + { + int i = (int)value; + return value > i ? i + 1 : i; + } + + public static int ceil(float value) + { + int i = (int)value; + return value > i ? i + 1 : i; + } + + public static int floor(float value) + { + int i = (int)value; + return value < i ? i - 1 : i; + } + + public static int floor(double value) + { + int i = (int)value; + return value < i ? i - 1 : i; + } + + public static int pow(int base, int exp) + { + return (int)Math.pow(base, exp); + } + + public static float lerp(float start, float end, float progress) + { + return start + ((end - start) * progress); + } + + public static double lerp(double start, double end, float progress) + { + return start + ((end - start) * progress); + } + + public static float smoothLerp(float start, float end, float progress) + { + float cosProgress = (1F - MathUtils.cos(progress * Math.PI)) * 0.5F; + return start * (1F - cosProgress) + end * cosProgress; + } + + public static float quadraticCurve(float start, float median, float end, float progress) + { + return lerp(lerp(start, median, progress), lerp(median, end, progress), progress); + } + + public static float smoothQuadraticCurve(float start, float median, float end, float progress) + { + return smoothLerp(smoothLerp(start, median, progress), smoothLerp(median, end, progress), progress); + } + + static + { + SIN_MASK = ~(-1 << 12); + int SIN_COUNT = SIN_MASK + 1; + + float radFull = (float)(Math.PI * 2D); + float degFull = (float)(360.0); + float radToIndex = SIN_COUNT / radFull; + float degToIndex = SIN_COUNT / degFull; + + TABLE_MASK = radToIndex; + SIN_TABLE = new float[SIN_COUNT]; + COS_TABLE = new float[SIN_COUNT]; + + for(int i = 0;i < SIN_COUNT;i++) + { + SIN_TABLE[i] = (float)Math.sin((i + 0.5f) / SIN_COUNT * radFull); + COS_TABLE[i] = (float)Math.cos((i + 0.5f) / SIN_COUNT * radFull); + } + for(int i = 0;i < 360;i += 90) + { + SIN_TABLE[(int)(i * degToIndex) & SIN_MASK] = (float)Math.sin(i * Math.PI / 180D); + COS_TABLE[(int)(i * degToIndex) & SIN_MASK] = (float)Math.cos(i * Math.PI / 180D); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/math/ShapeUtil.java b/src/main/java/speiger/src/coreengine/math/ShapeUtil.java new file mode 100644 index 0000000..f956b4f --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/ShapeUtil.java @@ -0,0 +1,23 @@ +package speiger.src.coreengine.math; + +import speiger.src.coreengine.math.vector.ints.Vec2i; + +public class ShapeUtil +{ + public static boolean isInCircle(int position, int radius, int testX, int testZ) + { + return isInCircle(BitUtil.toFirstShort(position), BitUtil.toSecondShort(position), radius, testX, testZ); + } + + public static boolean isInCircle(Vec2i position, int radius, int testX, int testZ) + { + return isInCircle(position.getX(), position.getY(), radius, testX, testZ); + } + + public static boolean isInCircle(int posX, int posZ, int radius, int testX, int testZ) + { + posX -= testX; + posZ -= testZ; + return (posX * posX) + (posZ * posZ) < radius * radius; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/collision2d/Circle.java b/src/main/java/speiger/src/coreengine/math/collision2d/Circle.java new file mode 100644 index 0000000..cb911fb --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/collision2d/Circle.java @@ -0,0 +1,91 @@ +package speiger.src.coreengine.math.collision2d; + +import java.util.Iterator; + +import speiger.src.coreengine.math.vector.ints.Vec2i; +import speiger.src.coreengine.math.vector.ints.Vec3i; + +public class Circle implements I2DCollision +{ + final Vec2i center; + final int radius; + + public Circle(int x, int y, int radius) + { + center = Vec2i.newVec(x, y); + this.radius = radius; + } + + public Circle(Vec2i center, int radius) + { + this.center = center.asImmutable(); + this.radius = radius; + } + + public Circle(Vec3i data) + { + this(data.getX(), data.getY(), data.getZ()); + } + + public Vec2i getCenter() + { + return center; + } + + public int getRadius() + { + return radius; + } + + @Override + public boolean isColliding(int x, int y) + { + int xDiff = center.getX() - x; + int yDiff = center.getY() - y; + return ((xDiff * xDiff) + (yDiff * yDiff)) < radius * radius; + } + + @Override + public Iterator iterator() + { + return new Iterator(){ + Vec2i iter = step(Vec2i.newMutable(center).sub(radius)); + Vec2i result = Vec2i.newMutable(); + + @Override + public boolean hasNext() + { + return iter != null; + } + + @Override + public Vec2i next() + { + result.set(iter); + iter = step(iter); + return result; + } + + Vec2i step(Vec2i iter) + { + while(iter != null) + { + iter.add(1, 0); + if(iter.getX() >= center.getX() + radius) + { + iter.sub((radius * 2), 0).add(0, 1); + if(iter.getY() >= center.getY() + radius) + { + iter = null; + } + } + if(iter != null && isColliding(iter)) + { + break; + } + } + return iter; + } + }; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/collision2d/I2DCollision.java b/src/main/java/speiger/src/coreengine/math/collision2d/I2DCollision.java new file mode 100644 index 0000000..081ad6d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/collision2d/I2DCollision.java @@ -0,0 +1,12 @@ +package speiger.src.coreengine.math.collision2d; + +import speiger.src.coreengine.math.BitUtil; +import speiger.src.coreengine.math.vector.ints.Vec2i; + +public interface I2DCollision extends Iterable +{ + public default boolean isMixedCollision() {return false;} + public default boolean isColliding(int position){return isColliding(BitUtil.toFirstShort(position), BitUtil.toSecondShort(position));} + public default boolean isColliding(Vec2i pos){return isColliding(pos.getX(), pos.getY());} + public boolean isColliding(int x, int y); +} diff --git a/src/main/java/speiger/src/coreengine/math/collision2d/Mixed2DCollision.java b/src/main/java/speiger/src/coreengine/math/collision2d/Mixed2DCollision.java new file mode 100644 index 0000000..d9fb168 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/collision2d/Mixed2DCollision.java @@ -0,0 +1,72 @@ +package speiger.src.coreengine.math.collision2d; + +import java.util.Iterator; + +import speiger.src.coreengine.math.vector.ints.Vec2i; + +public class Mixed2DCollision implements I2DCollision +{ + I2DCollision mainBox; + I2DCollision[] subBoxes; + + public Mixed2DCollision(I2DCollision mainBox, I2DCollision...subBoxes) + { + this.mainBox = mainBox; + this.subBoxes = subBoxes; + } + + @Override + public boolean isColliding(int x, int y) + { + return mainBox.isColliding(x, y) && isCollidingInSubBox(x, y); + } + + public boolean isCollidingInSubBox(int x, int y) + { + for(int i = 0,m=subBoxes.length;i iterator() + { + return new Iterator(){ + Iterator mainIter = mainBox.iterator(); + Vec2i cache = findNext(Vec2i.newMutable()); + Vec2i result = Vec2i.newMutable(); + @Override + public boolean hasNext() + { + return cache != null; + } + + @Override + public Vec2i next() + { + result.set(cache); + cache = findNext(cache); + return result; + } + + Vec2i findNext(Vec2i input) + { + while(mainIter.hasNext()) + { + Vec2i next = mainIter.next(); + if(isCollidingInSubBox(next.getX(), next.getY())) + { + return input.set(next); + } + } + return null; + } + }; + } + +} diff --git a/src/main/java/speiger/src/coreengine/math/collision2d/Plane.java b/src/main/java/speiger/src/coreengine/math/collision2d/Plane.java new file mode 100644 index 0000000..593ac79 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/collision2d/Plane.java @@ -0,0 +1,104 @@ +package speiger.src.coreengine.math.collision2d; + +import java.util.Iterator; + +import speiger.src.coreengine.math.vector.ints.Vec2i; + +public class Plane implements I2DCollision +{ + Vec2i min; + Vec2i max; + + public Plane() + { + this(0, 0, 0, 0); + } + + public Plane(int minX, int minY, int maxX, int maxY) + { + this(Vec2i.newMutable(minX, minY), Vec2i.newMutable(maxX, maxY)); + } + + public Plane(Vec2i center, int radius) + { + this(center.sub(radius), center.add(radius)); + } + + public Plane(Vec2i min, Vec2i max) + { + this.min = min; + this.max = max; + } + + public Plane set(Plane other) + { + min.set(other.getMin()); + max.set(other.getMax()); + return this; + } + + public Plane set(int minX, int minY, int maxX, int maxY) + { + min.set(minX, minY); + max.set(maxX, maxY); + return this; + } + + @Override + public boolean isColliding(int x, int y) + { + return x >= min.getX() && y >= min.getY() && x < max.getX() && y < max.getY(); + } + + public boolean isIntersecting(Plane plane) + { + return isIntersecting(plane.getMinX(), plane.getMinY(), plane.getMaxX(), plane.getMaxY()); + } + + public boolean isIntersecting(int minX, int minY, int maxX, int maxY) + { + return min.getX() <= maxX && max.getX() >= minX && min.getY() <= maxY && max.getY() >= minY; + } + + public Vec2i getMin(){return min;} + public Vec2i getMax(){return max;} + public int getWidth(){return max.getX() - min.getX();} + public int getHeight(){return max.getY() - min.getY();} + public int getMinX(){return min.getX();} + public int getMinY(){return min.getY();} + public int getMaxX(){return max.getX();} + public int getMaxY(){return max.getY();} + + @Override + public Iterator iterator() + { + return new Iterator(){ + Vec2i iter = Vec2i.newMutable(min); + Vec2i result = Vec2i.newMutable(); + + @Override + public boolean hasNext() + { + return iter != null; + } + + @Override + public Vec2i next() + { + result.set(iter); + iter.add(1, 0); + if(iter.getX() == max.getX()) + { + iter.set(min.getX(), iter.getY() + 1); + if(iter.getY() == max.getY()) + { + iter = null; + } + } + return result; + } + + }; + } + +} diff --git a/src/main/java/speiger/src/coreengine/math/misc/ColorObject.java b/src/main/java/speiger/src/coreengine/math/misc/ColorObject.java new file mode 100644 index 0000000..d09cad8 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/misc/ColorObject.java @@ -0,0 +1,381 @@ +package speiger.src.coreengine.math.misc; + +import java.awt.Color; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; + +import speiger.src.collections.floats.lists.FloatList; + +public class ColorObject +{ + static final float DEVIDER = 1F / 255F; + public static final int R = 0xFF << 16; + public static final int G = 0xFF << 8; + public static final int B = 0xFF; + public static final int A = 0xFF << 24; + public static final long SIGN = 0x00000000FFFFFFFFL; + static final int ALL = 0xFFFFFFFF; + + public static final ColorObject WHITE = new ColorObject(255, 255, 255); + public static final ColorObject LIGHT_GRAY = new ColorObject(192, 192, 192); + public static final ColorObject GRAY = new ColorObject(128, 128, 128); + public static final ColorObject DARK_GRAY = new ColorObject(64, 64, 64); + public static final ColorObject BLACK = new ColorObject(0, 0, 0); + public static final ColorObject RED = new ColorObject(255, 0, 0); + public static final ColorObject PINK = new ColorObject(255, 175, 175); + public static final ColorObject PURPLE = new ColorObject(106, 13, 173); + public static final ColorObject ORANGE = new ColorObject(255, 200, 0); + public static final ColorObject YELLOW = new ColorObject(255, 255, 0); + public static final ColorObject GREEN = new ColorObject(0, 255, 0); + public static final ColorObject DARK_GREEN = new ColorObject(7, 161, 0); + public static final ColorObject MAGENTA = new ColorObject(255, 0, 255); + public static final ColorObject CYAN = new ColorObject(0, 255, 255); + public static final ColorObject BLUE = new ColorObject(0, 0, 255); + public static final ColorObject LIGHT_BLUE = new ColorObject(0, 150, 255); + + //Specialized Components that get reused + public static final ColorObject INVISIBLE = new ColorObject(0, 0, 0, 0); + public static final ColorObject TEXT_DEFAULT_BACKGROUND = new ColorObject(80, 80, 80, 144); + public static final ColorObject WINDOW_DEFAULT_BACKGROUND = new ColorObject(64, 64, 64, 128); + public static final ColorObject POPUP_DEFAULT_BACKGROUND = new ColorObject(85, 85, 85); + public static final ColorObject DESTRUCTION = new ColorObject(255, 0, 0, 128); + + int rgba; + + public ColorObject(int rgba) + { + this.rgba = rgba; + } + + public ColorObject(int r, int g, int b) + { + rgba = A | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF); + } + + public ColorObject(int r, int g, int b, int a) + { + rgba = ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF); + } + + public static ColorObject rgb(int rgb) + { + return new ColorObject(rgb | (255 << 24)); + } + + public ColorObject copy() + { + return new ColorObject(rgba); + } + + public ColorObject setRGB(int rgba) + { + this.rgba = rgba; + return this; + } + + public ColorObject setRed(int r) + { + rgba = rgba & ~R | ((r & 0xFF) << 16); + return this; + } + + public ColorObject setGreen(int g) + { + rgba = rgba & ~G | ((g & 0xFF) << 8); + return this; + } + + public ColorObject setBlue(int b) + { + rgba = rgba & ~B | (b & 0xFF); + return this; + } + + public ColorObject setAlpha(int a) + { + rgba = rgba & ~A | ((a & 0xFF) << 24); + return this; + } + + public ColorObject setRed(float r) + { + return setRed((int)(r * 255F + 0.5F)); + } + + public ColorObject setGreen(float g) + { + return setGreen((int)(g * 255F + 0.5F)); + } + + public ColorObject setBlue(float b) + { + return setBlue((int)(b * 255F + 0.5F)); + } + + public ColorObject setAlpha(float a) + { + return setAlpha((int)(a * 255F + 0.5F)); + } + + public ColorObject setRGB(float hue, float brightness, float saturation, int alpha) + { + return setRGB(Color.HSBtoRGB(hue, saturation, brightness) | alpha << 24); + } + + public int getRGB() + { + return rgba; + } + + public int getRed() + { + return (rgba >> 16) & 0xFF; + } + + public int getGreen() + { + return (rgba >> 8) & 0xFF; + } + + public int getBlue() + { + return rgba & 0xFF; + } + + public int getAlpha() + { + return (rgba >> 24) & 0xFF; + } + + public float getRedFloat() + { + return ((rgba >> 16) & 0xFF) * DEVIDER; + } + + public float getGreenFloat() + { + return ((rgba >> 8) & 0xFF) * DEVIDER; + } + + public float getBlueFloat() + { + return (rgba & 0xFF) * DEVIDER; + } + + public float getAlphaFloat() + { + return ((rgba >> 24) & 0xFF) * DEVIDER; + } + + public String getHexCode(boolean alpha) + { + return "0x"+(alpha ? Long.toHexString(1 << 32 | rgba & SIGN) : Integer.toHexString((1 << 24) | (rgba & ~A))).substring(1); + } + + public String getHTMLCode(boolean alpha) + { + return "#"+(alpha ? Long.toHexString(1 << 32 | rgba & SIGN) : Integer.toHexString((1 << 24) | (rgba & ~A))).substring(1); + } + + public boolean needsDarkColor() + { + return getBrightness() >= 130; + } + + public int getBrightness() + { + return getBrightness(rgba); + } + + public Color toColor() + { + return new Color(rgba); + } + + public float[] toHue() + { + return Color.RGBtoHSB(getRed(), getGreen(), getBlue(), new float[3]); + } + + public ColorObject brighter() + { + return brighter(0.7F); + } + + public ColorObject brighter(float factor) + { + rgba = brighter(rgba, factor); + return this; + } + + public ColorObject darker() + { + return darker(0.7F); + } + + public ColorObject darker(float factor) + { + rgba = darker(rgba, factor); + return this; + } + + public ColorObject mix(ColorObject to, float factor) + { + rgba = mix(rgba, to.rgba, factor); + return this; + } + + public ColorObject mix(ColorObject from, ColorObject to, float factor) + { + rgba = mix(from.rgba, to.rgba, factor); + return this; + } + + public ColorObject write(FloatBuffer buffer, boolean alpha) + { + buffer.put(getRedFloat()).put(getGreenFloat()).put(getBlueFloat()); + if(alpha) + { + buffer.put(getAlphaFloat()); + } + return this; + } + + public ColorObject read(FloatBuffer buffer, boolean alpha) + { + setRed(buffer.get()).setGreen(buffer.get()).setBlue(buffer.get()); + if(alpha) + { + setAlpha(buffer.get()); + } + return this; + } + + public ColorObject write(ByteBuffer buffer, boolean alpha) + { + pack(rgba, alpha, buffer); + return this; + } + + public ColorObject read(ByteBuffer buffer, boolean alpha) + { + setRed(buffer.get()).setGreen(buffer.get()).setBlue(buffer.get()); + if(alpha) + { + setAlpha(buffer.get()); + } + return this; + } + + public ColorObject write(FloatList buffer, boolean alpha) + { + buffer.add(getRedFloat()); + buffer.add(getGreenFloat()); + buffer.add(getBlueFloat()); + if(alpha) + { + buffer.add(getAlphaFloat()); + } + return this; + } + + public ColorObject writeFloat(ByteBuffer buffer, boolean alpha) + { + buffer.putFloat(getRedFloat()).putFloat(getGreenFloat()).putFloat(getBlueFloat()); + if(alpha) + { + buffer.putFloat(getAlphaFloat()); + } + return this; + } + + @Override + public int hashCode() + { + return rgba; + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof ColorObject) + { + return ((ColorObject)obj).rgba == rgba; + } + return false; + } + + @Override + public String toString() + { + return "Color[r=" + getRed() + ", g=" + getGreen() + ", b=" + getBlue() + ", a=" + getAlpha() + "]"; + } + + public String getFloatColor() + { + return "Color[r=" + getRedFloat() + ", g=" + getGreenFloat() + ", b=" + getBlueFloat() + ", a=" + getAlphaFloat() + "]"; + } + + public static void pack(int color, boolean alpha, ByteBuffer buffer) + { + buffer.put((byte)((color >> 16) & 0xFF)).put((byte)((color >> 8) & 0xFF)).put((byte)(color & 0xFF)); + if(alpha) + { + buffer.put((byte)((color >> 24) & 0xFF)); + } + } + + public static void packFloat(int color, boolean alpha, FloatBuffer buffer) + { + buffer.put(((color >> 16) & 0xFF) * DEVIDER).put(((color >> 8) & 0xFF) * DEVIDER).put((color & 0xFF) * DEVIDER); + if(alpha) + { + buffer.put(((color >> 24) & 0xFF) * DEVIDER); + } + } + + public static int getBrightness(int rgba) + { + return getBrightness((rgba >> 16) & 0xFF, (rgba >> 8) & 0xFF, rgba & 0xFF); + } + + public static int getBrightness(int r, int g, int b) + { + return (int)Math.sqrt((r * r * 0.241F) + (g * g * 0.691F) + (b * b * 0.068F)); + } + + public static int mix(int from, int to, float factor) + { + float weight0 = (1F - factor); + float weight1 = factor; + int r = (int)((((from >> 16) & 0xFF) * weight0) + (((to >> 16) & 0xFF) * weight1)); + int g = (int)((((from >> 8) & 0xFF) * weight0) + (((to >> 8) & 0xFF) * weight1)); + int b = (int)(((from & 0xFF) * weight0) + ((to & 0xFF) * weight1)); + int a = (int)((((from >> 24) & 0xFF) * weight0) + (((to >> 24) & 0xFF) * weight1)); + return ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | b & 0xFF; + } + + public static int darker(int color, float factor) + { + int r = Math.max(0, (int)(((color >> 16) & 0xFF) * factor)); + int g = Math.max(0, (int)(((color >> 8) & 0xFF) * factor)); + int b = Math.max(0, (int)((color & 0xFF) * factor)); + return (color & A) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF); + } + + public static int brighter(int color, float factor) + { + int r = (color >> 16) & 0xFF; + int g = (color >> 8) & 0xFF; + int b = color & 0xFF; + int i = (int)(1.0 / (1.0 - factor)); + if(r == 0 && g == 0 && b == 0) + { + return (color & A) | ((i & 0xFF) << 16) | ((i & 0xFF) << 8) | (i & 0xFF); + } + if(r > 0 && r < i) r = i; + if(g > 0 && g < i) g = i; + if(b > 0 && b < i) b = i; + return (color & A) | Math.min(255, (int)(r / factor)) << 16 | Math.min(255, (int)(g / factor)) << 8 | Math.min(255, (int)(b / factor)); + } +} diff --git a/src/main/java/speiger/src/coreengine/math/misc/Facing.java b/src/main/java/speiger/src/coreengine/math/misc/Facing.java new file mode 100644 index 0000000..332b74f --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/misc/Facing.java @@ -0,0 +1,194 @@ +package speiger.src.coreengine.math.misc; + +import java.util.function.Predicate; + +import speiger.src.coreengine.math.vector.ints.Vec2i; + +public enum Facing +{ + NORTH(0, 2, "North", Axis.VERTICAL, Vec2i.newVec(0, 1)), + EAST(1, 3, "East", Axis.HORIZONTAL, Vec2i.newVec(1, 0)), + SOUTH(2, 0, "South", Axis.VERTICAL, Vec2i.newVec(0, -1)), + WEST(3, 1, "West", Axis.HORIZONTAL, Vec2i.newVec(-1, 0)); + + private static final Facing[] VALUES; + private static final Facing[] ROTATIONS; + + final int index; + final int rotationIndex; + final String name; + final Axis axis; + final Vec2i offset; + final boolean positive; + + private Facing(int direction, int rotation, String display, Axis axis, Vec2i offset) + { + index = direction; + rotationIndex = rotation; + name = display; + this.axis = axis; + this.offset = offset; + positive = index < 2; + } + + public int getIndex() + { + return index; + } + + public boolean isXAxis() + { + return axis == Axis.HORIZONTAL; + } + + public boolean isZAxis() + { + return axis == Axis.VERTICAL; + } + + public Axis getAxis() + { + return axis; + } + + public boolean isPositive() + { + return positive; + } + + public Vec2i getOffset() + { + return offset; + } + + public float getMultiplier() + { + return positive ? 1F : -1F; + } + + public String getName() + { + return name; + } + + public int getRotationIndex() + { + return rotationIndex; + } + + public int getRotation() + { + return rotationIndex * 90; + } + + public int getRotation(Facing other) + { + if(other == backwards()) return getRotation() - 45; + else if(other == forward()) return getRotation() + 45; + return getRotation(); + } + + public Facing rotate(int amount) + { + return byIndex(index + amount); + } + + public Facing forward() + { + return byIndex(index + 1); + } + + public Facing backwards() + { + return byIndex(index - 1); + } + + public Facing opposite() + { + return byIndex(index + 2); + } + + @Override + public String toString() + { + return getName()+": "+offset; + } + + public static Facing byIndex(int index) + { + return VALUES[index & 3]; + } + + public static Facing byRotationIndex(int index) + { + return ROTATIONS[index & 3]; + } + + public static Facing byYaw(float value) + { + return byRotationIndex((int)(value / 90) & 3); + } + + static + { + Facing[] values = values(); + VALUES = new Facing[values.length]; + ROTATIONS = new Facing[values.length]; + for(Facing entry : values) + { + VALUES[entry.getIndex()] = entry; + ROTATIONS[entry.getRotationIndex()] = entry; + } + } + + public static enum Rotation + { + NONE(Facing.NORTH), + CLOCKWISE(Facing.EAST), + OPPOSITE(Facing.SOUTH), + COUNTER_CLOCKWISE(Facing.WEST); + + static final Rotation[] ROTATION = Rotation.values(); + Facing facing; + + private Rotation(Facing facing) + { + this.facing = facing; + } + + public static Rotation fromFacing(Facing facing) + { + return ROTATION[facing.getIndex()]; + } + + public Facing toFacing() + { + return facing; + } + } + + public static enum Axis implements Predicate + { + HORIZONTAL(5), + VERTICAL(10); + + int code; + + private Axis(int code) + { + this.code = code; + } + + public int getCode() + { + return code; + } + + @Override + public boolean test(Facing t) + { + return t.getAxis() == this; + } + } + +} diff --git a/src/main/java/speiger/src/coreengine/math/misc/FacingList.java b/src/main/java/speiger/src/coreengine/math/misc/FacingList.java new file mode 100644 index 0000000..b163d12 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/misc/FacingList.java @@ -0,0 +1,312 @@ +package speiger.src.coreengine.math.misc; + +import java.util.EnumSet; +import java.util.Iterator; +import java.util.Set; +import java.util.StringJoiner; +import java.util.function.Predicate; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.lists.ObjectList; +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.misc.Facing.Axis; +import speiger.src.coreengine.math.vector.ints.Vec2i; + +public final class FacingList implements Iterable, Predicate +{ + static final FacingList[] FACINGS = createArray(); + public static final FacingList EMPTY = ofNumber(0); + public static final FacingList NORTH = ofFacings(Facing.NORTH); + public static final FacingList EAST = ofFacings(Facing.EAST); + public static final FacingList SOUTH = ofFacings(Facing.SOUTH); + public static final FacingList WEST = ofFacings(Facing.WEST); + + public static final FacingList NORTH_EAST = ofFacings(Facing.NORTH, Facing.EAST); + public static final FacingList SOUTH_EAST = ofFacings(Facing.EAST, Facing.SOUTH); + public static final FacingList SOUTH_WEST = ofFacings(Facing.SOUTH, Facing.WEST); + public static final FacingList NORTH_WEST = ofFacings(Facing.WEST, Facing.NORTH); + + public static final FacingList VERTICAL = ofFacings(Facing.NORTH, Facing.SOUTH); + public static final FacingList HORIZONTAL = ofFacings(Facing.EAST, Facing.WEST); + public static final FacingList ALL = ofFacings(Facing.NORTH, Facing.SOUTH, Facing.EAST, Facing.WEST); + + final byte code; + final byte next; + final byte opposite; + final byte prev; + final byte count; + final Vec2i offset; + final Facing[] array; + + private FacingList(int initCode) + { + code = (byte)MathUtils.clamp(0, 15, initCode); + Vec2i pos = Vec2i.newMutable(); + ObjectList facings = new ObjectArrayList(); + for(int i = 0;i<4;i++) + { + if((code & 1 << i) != 0) + { + pos.add(Facing.byIndex(i).getOffset()); + facings.add(Facing.byIndex(i)); + } + } + int[] data = new int[3]; + for(int i = 0,m=facings.size();i toFacings() + { + return isEmpty() ? EnumSet.noneOf(Facing.class) : EnumSet.copyOf(ObjectArrayList.wrap(array)); + } + + public boolean[] toFlags() + { + return toFlags(array); + } + + public int getRotation() + { + switch(count) + { + case 1: return array[0].getRotation(); + case 2: return array[0].getRotation(array[1]); + default: return 0; + } + } + + public FacingList rotate(int amount) + { + switch(amount & 3) + { + case 1: return FACINGS[next]; + case 2: return FACINGS[opposite]; + case 3: return FACINGS[prev]; + default: return this; + } + } + + public FacingList invert() + { + return FACINGS[15 - code]; + } + + public FacingList opposite() + { + return FACINGS[opposite]; + } + + public FacingList add(Facing facing) + { + return FACINGS[code | (1 << facing.getIndex())]; + } + + public FacingList add(FacingList facings) + { + return FACINGS[code | facings.code]; + } + + public FacingList remove(Facing facing) + { + return FACINGS[code & ~(1 << facing.getIndex())]; + } + + public FacingList remove(FacingList facings) + { + return FACINGS[code & ~facings.code]; + } + + public boolean contains(Facing direction) + { + return (code & 1 << direction.getIndex()) != 0; + } + + public boolean contains(FacingList facings) + { + return (code & facings.code) == facings.code; + } + + public boolean containsAny(FacingList facings) + { + return (code & facings.code) != 0; + } + + public boolean notContains(Facing direction) + { + return (code & 1 << direction.getIndex()) == 0; + } + + public boolean notContains(FacingList facings) + { + return (code & facings.code) != facings.code; + } + + public FacingList flipFacing(Facing facing) + { + return contains(facing) ? remove(facing).add(facing.opposite()) : this; + } + + public FacingList flipAxis(Axis axis) + { + FacingList result = this; + for(int i = 0,m=array.length;i iterator() + { + return new Iterator(){ + int index = 0; + @Override + public boolean hasNext() + { + return index < size(); + } + + @Override + public Facing next() + { + return array[index++]; + } + }; + } + + public static int toNumber(Facing...facings) + { + int value = 0; + for(int i = 0,m=facings.length;i= start + duration) + { + onFinished(); + } + return value; + } + + @Override + public float get() + { + return value; + } + + @Override + public boolean isDone() + { + return done > 0; + } + + protected abstract float calculateProgress(float time); + + protected void onFinished() + { + progress = start; + done++; + } + + protected void onInitialValueSet() + { + flags |= 1; + } + + protected void finishProgress() + { + flags |= 2; + done++; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/Vec.java b/src/main/java/speiger/src/coreengine/math/vector/Vec.java new file mode 100644 index 0000000..e3683f6 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/Vec.java @@ -0,0 +1,26 @@ +package speiger.src.coreengine.math.vector; + +import java.nio.ByteBuffer; + +public interface Vec +{ + public static final int X = 1; + public static final int Y = 2; + public static final int Z = 4; + public static final int W = 8; + public static final int ALL = X | Y | Z | W; + + public Vec copy(); + public Vec abs(); + public Vec negate(); + public Vec invert(); + + public Vec store(ByteBuffer buffer); + public Vec load(ByteBuffer buffer); + + public boolean isMutable(); + public Vec asMutable(); + public Vec copyAsMutable(); + public Vec asImmutable(); + public Vec copyAsImmutable(); +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/VectorUtil.java b/src/main/java/speiger/src/coreengine/math/vector/VectorUtil.java new file mode 100644 index 0000000..6ecd435 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/VectorUtil.java @@ -0,0 +1,21 @@ +package speiger.src.coreengine.math.vector; + +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.math.vector.floats.Vec3f; + +public class VectorUtil +{ + public static float barryCentric(Vec3f p1, Vec3f p2, Vec3f p3, Vec2f pos) + { + float det = (p2.getZ() - p3.getZ()) * (p1.getX() - p3.getX()) + (p3.getX() - p2.getX()) * (p1.getZ() - p3.getZ()); + float l1 = ((p2.getZ() - p3.getZ()) * (pos.getX() - p3.getX()) + (p3.getX() - p2.getX()) * (pos.getY() - p3.getZ())) / det; + float l2 = ((p3.getZ() - p1.getZ()) * (pos.getX() - p3.getX()) + (p1.getX() - p3.getX()) * (pos.getY() - p3.getZ())) / det; + float l3 = 1.0f - l1 - l2; + return l1 * p1.getY() + l2 * p2.getY() + l3 * p3.getY(); + } + + public static float fma(float x, float y, float z) + { + return x * y + z; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec2b.java b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec2b.java new file mode 100644 index 0000000..dc33ecb --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec2b.java @@ -0,0 +1,149 @@ +package speiger.src.coreengine.math.vector.bytes; + +import java.nio.ByteBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.doubles.Vec2d; +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.math.vector.ints.Vec2i; +import speiger.src.coreengine.math.vector.longs.Vec2l; +import speiger.src.coreengine.math.vector.shorts.Vec2s; + + +public interface Vec2b extends Vecb +{ + public static final Vec2b ZERO = newVec(); + public static final Vec2b MINUS_ONE = newVec((byte)-1); + public static final Vec2b ONE = newVec((byte)1); + + public static Vec2b newMutable(){return new Vec2bMutable();} + public static Vec2b newMutable(byte value){return new Vec2bMutable(value);} + public static Vec2b newMutable(byte x, byte y){return new Vec2bMutable(x, y);} + public static Vec2b newMutable(Vec2b value){return newMutable(value.getX(), value.getY());} + + public static Vec2b newVec(){return new Vec2bImmutable();} + public static Vec2b newVec(byte value){return new Vec2bImmutable(value);} + public static Vec2b newVec(byte x, byte y){return new Vec2bImmutable(x, y);} + public static Vec2b newVec(Vec2b value){return newVec(value.getX(), value.getY());} + + public byte getX(); + public byte getY(); + public Vec2b setX(byte x); + public Vec2b setY(byte y); + @Override + public default byte[] asArray(){return new byte[]{getX(), getY()};} + @Override + public Vec2b copy(); + @Override + public default Vec2b abs(){return set((byte)Math.abs(getX()), (byte)Math.abs(getY()));} + @Override + public default Vec2b negate(){return set((byte)0, (byte)0);} + @Override + public default Vec2b invert(){return set((byte)(-getX()), (byte)(-getY()));}; + + @Override + public default Vec2b add(byte value) {return add(value, value);} + public default Vec2b add(Vec2b value) {return add(value.getX(), value.getY());} + public default Vec2b add(byte x, byte y) {return set((byte)(x + getX()), (byte)(y + getY()));} + + @Override + public default Vec2b sub(byte value){return sub(value, value);} + public default Vec2b sub(Vec2b value){return sub(value.getX(), value.getY());} + public default Vec2b sub(byte x, byte y) {return set((byte)(getX() - x), (byte)(getY() - y));} + + @Override + public default Vec2b multiply(byte value){return multiply(value, value);} + public default Vec2b multiply(Vec2b value){return multiply(value.getX(), value.getY());} + public default Vec2b multiply(byte x, byte y) {return set((byte)(x * getX()), (byte)(y * getY()));} + + @Override + public default Vec2b devide(byte value){return devide(value, value);} + public default Vec2b devide(Vec2b value){return devide(value.getX(), value.getY());} + public default Vec2b devide(byte x, byte y){return set((byte)(getX() / x), (byte)(getY() / y));} + + @Override + public default Vec2b set(byte value){return set(value, value);}; + public default Vec2b set(Vec2b value){return set(value.getX(), value.getY());} + public Vec2b set(byte x, byte y); + + public default double distanceTo(Vec2b value){return distanceTo(value.getX(), value.getY());} + public default double distanceTo(byte x, byte y){return Math.sqrt(distanceToSquared(x, y));} + + public default long distanceToSquared(Vec2b value){return distanceToSquared(value.getX(), value.getY());} + public default long distanceToSquared(byte x, byte y) + { + long xPos = getX() - x; + long yPos = getY() - y; + return (xPos * xPos) + (yPos * yPos); + } + @Override + public default long lengthSquared() {return (getX() * getX()) + (getY() * getY());} + + public default long dotProduct(Vec2b value){return dotProduct(value.getX(), value.getY());} + public default long dotProduct(byte x, byte y){return (getX() * x) + (getY() * y);} + + public default Vec2b rotate(byte angle, Vec2b center){return rotate(angle, center.getX(), center.getY());} + public default Vec2b rotate(byte angle, byte x, byte y) + { + byte xPos = (byte)(getX() - x); + byte yPos = (byte)(getY() - y); + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + return set((byte)((xPos * cos) + (yPos * sin) + x), (byte)(-(xPos * sin) + (yPos * cos) + y)); + } + + public default Vec2b min(Vec2b other) {return min(other, this);} + public default Vec2b min(Vec2b other, Vec2b result){return min(other.getX(), other.getY(), result);} + public default Vec2b min(byte x, byte y) {return min(x, y, this);} + public default Vec2b min(byte x, byte y, Vec2b result){return result.set((byte)Math.min(getX(), x), (byte)Math.min(getY(), y));} + + public default Vec2b max(Vec2b other) {return max(other, this);} + public default Vec2b max(Vec2b other, Vec2b result){return max(other.getX(), other.getY(), result);} + public default Vec2b max(byte x, byte y) {return max(x, y, this);} + public default Vec2b max(byte x, byte y, Vec2b result){return result.set((byte)Math.max(getX(), x), (byte)Math.max(getY(), y));} + + public default Vec2b difference(Vec2b other) {return difference(other, this);} + public default Vec2b difference(Vec2b other, Vec2b result){return difference(other.getX(), other.getY(), result);} + public default Vec2b difference(byte x, byte y) {return difference(x, y, this);} + public default Vec2b difference(byte x, byte y, Vec2b result){return result.set((byte)(getX() - x), (byte)(getY() - y));} + + @Override + public default Vec2b clamp(byte min, byte max){return clamp(min, max, ALL);} + public default Vec2b clamp(byte min, byte max, Vec2b result){return clamp(min, max, result, ALL);} + @Override + public default Vec2b clamp(byte min, byte max, int filter){return clamp(min, max, this, filter);} + public default Vec2b clamp(byte min, byte max, Vec2b result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()));} + + @Override + public default Vec2b store(ByteBuffer buffer) + { + buffer.put(getX()).put(getY()); + return this; + } + + @Override + public default Vec2b load(ByteBuffer buffer) + { + return set(buffer.get(), buffer.get()); + } + + @Override + public default Vec2s asShort(){return isMutable() ? Vec2s.newMutable(getX(), getY()) : Vec2s.newVec(getX(), getY());} + @Override + public default Vec2i asInt(){return isMutable() ? Vec2i.newMutable(getX(), getY()) : Vec2i.newVec(getX(), getY());} + @Override + public default Vec2l asLong(){return isMutable() ? Vec2l.newMutable(getX(), getY()) : Vec2l.newVec(getX(), getY());} + @Override + public default Vec2f asFloat() {return isMutable() ? Vec2f.newMutable(getX(), getY()) : Vec2f.newVec(getX(), getY());} + @Override + public default Vec2d asDouble(){return isMutable() ? Vec2d.newMutable(getX(), getY()) : Vec2d.newVec(getX(), getY());} + + @Override + public default Vec2b asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec2b asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec2b copyAsMutable(){return newMutable(this);} + @Override + public default Vec2b copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec2bImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec2bImmutable.java new file mode 100644 index 0000000..2c2878b --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec2bImmutable.java @@ -0,0 +1,92 @@ +package speiger.src.coreengine.math.vector.bytes; + +import java.util.Objects; + +public class Vec2bImmutable implements Vec2b +{ + final byte x; + final byte y; + + public Vec2bImmutable() + { + x = 0; + y = 0; + } + + public Vec2bImmutable(byte value) + { + x = value; + y = value; + } + + public Vec2bImmutable(byte x, byte y) + { + this.x = x; + this.y = y; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public byte getX() + { + return x; + } + + @Override + public byte getY() + { + return y; + } + + @Override + public Vec2b setX(byte x) + { + return this.x == x ? this : Vec2b.newVec(x, y); + } + + @Override + public Vec2b setY(byte y) + { + return this.y == y ? this : Vec2b.newVec(x, y); + } + + @Override + public Vec2b copy() + { + return Vec2b.newVec(this); + } + + @Override + public Vec2b set(byte x, byte y) + { + return this.x == x && this.y == y ? this : Vec2b.newVec(x, y); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec2b) + { + Vec2b vec = (Vec2b)obj; + return vec.getX() == x && vec.getY() == y; + } + return false; + } + + @Override + public String toString() + { + return "Vec2b[x="+x+", y="+y+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec2bMutable.java b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec2bMutable.java new file mode 100644 index 0000000..ecdde7c --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec2bMutable.java @@ -0,0 +1,94 @@ +package speiger.src.coreengine.math.vector.bytes; + +import java.util.Objects; + +public class Vec2bMutable implements Vec2b +{ + byte x; + byte y; + + public Vec2bMutable() + { + } + + public Vec2bMutable(byte value) + { + x = value; + y = value; + } + + public Vec2bMutable(byte x, byte y) + { + this.x = x; + this.y = y; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public byte getX() + { + return x; + } + + @Override + public byte getY() + { + return y; + } + + @Override + public Vec2b setX(byte x) + { + this.x = x; + return this; + } + + @Override + public Vec2b setY(byte y) + { + this.y = y; + return this; + } + + @Override + public Vec2b copy() + { + return Vec2b.newMutable(this); + } + + @Override + public Vec2b set(byte x, byte y) + { + this.x = x; + this.y = y; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec2b) + { + Vec2b vec = (Vec2b)obj; + return vec.getX() == x && vec.getY() == y; + } + return false; + } + + @Override + public String toString() + { + return "Vec2b[x="+x+", y="+y+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec3b.java b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec3b.java new file mode 100644 index 0000000..cc35c14 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec3b.java @@ -0,0 +1,141 @@ +package speiger.src.coreengine.math.vector.bytes; + +import java.nio.ByteBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.doubles.Vec3d; +import speiger.src.coreengine.math.vector.floats.Vec3f; +import speiger.src.coreengine.math.vector.ints.Vec3i; +import speiger.src.coreengine.math.vector.longs.Vec3l; +import speiger.src.coreengine.math.vector.shorts.Vec3s; + +public interface Vec3b extends Vecb +{ + public static final Vec3b ZERO = newVec(); + public static final Vec3b MINUS_ONE = newVec((byte)-1); + public static final Vec3b ONE = newVec((byte)1); + + public static Vec3b newMutable(){return new Vec3bMutable();} + public static Vec3b newMutable(byte value){return new Vec3bMutable(value);} + public static Vec3b newMutable(byte x, byte y, byte z){return new Vec3bMutable(x, y, z);} + public static Vec3b newMutable(Vec3b vec){return newMutable(vec.getX(), vec.getY(), vec.getZ());} + + public static Vec3b newVec(){return new Vec3bImmutable();} + public static Vec3b newVec(byte value){return new Vec3bImmutable(value);} + public static Vec3b newVec(byte x, byte y, byte z){return new Vec3bImmutable(x, y, z);} + public static Vec3b newVec(Vec3b vec){return newVec(vec.getX(), vec.getY(), vec.getZ());} + + public byte getX(); + public byte getY(); + public byte getZ(); + public Vec3b setX(byte x); + public Vec3b setY(byte y); + public Vec3b setZ(byte z); + @Override + public default byte[] asArray(){return new byte[]{getX(), getY(), getZ()};} + + @Override + public Vec3b copy(); + @Override + public default Vec3b abs(){return set((byte)Math.abs(getX()), (byte)Math.abs(getY()), (byte)Math.abs(getZ()));} + @Override + public default Vec3b negate(){return set((byte)0, (byte)0, (byte)0);} + @Override + public default Vec3b invert(){return set((byte)-getX(), (byte)-getY(), (byte)-getZ());} + @Override + public default Vec3b add(byte value){return add(value, value, value);} + public default Vec3b add(Vec3b value){return add(value.getX(), value.getY(), value.getZ());} + public default Vec3b add(byte x, byte y, byte z){return set((byte)(getX() + x), (byte)(getY() + y), (byte)(getZ() + z));} + + @Override + public default Vec3b sub(byte value){return sub(value, value, value);} + public default Vec3b sub(Vec3b value){return sub(value.getX(), value.getY(), value.getZ());} + public default Vec3b sub(byte x, byte y, byte z){return set((byte)(getX() - x), (byte)(getY() - y), (byte)(getZ() - z));} + + @Override + public default Vec3b multiply(byte value){return multiply(value, value, value);} + public default Vec3b multiply(Vec3b value){return multiply(value.getX(), value.getY(), value.getZ());} + public default Vec3b multiply(byte x, byte y, byte z){return set((byte)(getX() * x), (byte)(getY() * y), (byte)(getZ() * z));} + + @Override + public default Vec3b devide(byte value){return devide(value, value, value);} + public default Vec3b devide(Vec3b value){return devide(value.getX(), value.getY(), value.getZ());} + public default Vec3b devide(byte x, byte y, byte z){return set((byte)(getX() / x), (byte)(getY() / y), (byte)(getZ() / z));} + + @Override + public default Vec3b set(byte value){return set(value, value, value);} + public default Vec3b set(Vec3b value){return set(value.getX(), value.getY(), value.getZ());} + public Vec3b set(byte x, byte y, byte z); + + public default double distanceTo(Vec3b value){return distanceTo(value.getX(), value.getY(), value.getZ());} + public default double distanceTo(byte x, byte y, byte z){return Math.sqrt(distanceToSquared(x, y, z));} + + public default long distanceToSquared(Vec3b value){return distanceToSquared(value.getX(), value.getY(), value.getZ());} + public default long distanceToSquared(byte x, byte y, byte z) + { + long xPos = getX() - x; + long yPos = getY() - y; + long zPos = getZ() - z; + return (xPos * xPos) + (yPos * yPos) + (zPos * zPos); + } + @Override + public default long lengthSquared() {return (getX() * getX()) + (getY() * getY()) + (getZ() * getZ());} + + public default long dotProduct(Vec3b value){return dotProduct(value.getX(), value.getY(), value.getZ());} + public default long dotProduct(byte x, byte y, byte z){return (getX() * x) + (getY() * y) + (getZ() * z);} + + public default Vec3b min(Vec3b other) {return min(other, this);} + public default Vec3b min(Vec3b other, Vec3b result){return min(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3b min(byte x, byte y, byte z) {return min(x, y, z, this);} + public default Vec3b min(byte x, byte y, byte z, Vec3b result){return result.set((byte)Math.min(getX(), x), (byte)Math.min(getY(), y), (byte)Math.min(getZ(), z));} + + public default Vec3b max(Vec3b other) {return max(other, this);} + public default Vec3b max(Vec3b other, Vec3b result){return max(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3b max(byte x, byte y, byte z) {return max(x, y, z, this);} + public default Vec3b max(byte x, byte y, byte z, Vec3b result){return result.set((byte)Math.max(getX(), x), (byte)Math.max(getY(), y), (byte)Math.max(getZ(), z));} + + public default Vec3b difference(Vec3b other) {return difference(other, this);} + public default Vec3b difference(Vec3b other, Vec3b result){return difference(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3b difference(byte x, byte y, byte z) {return difference(x, y, z, this);} + public default Vec3b difference(byte x, byte y, byte z, Vec3b result){return result.set((byte)(getX() - x), (byte)(getY() - y), (byte)(getZ() - z));} + + @Override + public default Vec3b clamp(byte min, byte max){return clamp(min, max, ALL);} + public default Vec3b clamp(byte min, byte max, Vec3b result){return clamp(min, max, result, ALL);} + @Override + public default Vec3b clamp(byte min, byte max, int filter){return clamp(min, max, this, filter);} + public default Vec3b clamp(byte min, byte max, Vec3b result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()), (filter & Z) == 0 ? getZ() : MathUtils.clamp(min, max, getZ()));} + + @Override + public default Vec3b store(ByteBuffer buffer) + { + buffer.put(getX()).put(getY()).put(getZ()); + return this; + } + + @Override + public default Vec3b load(ByteBuffer buffer) + { + return set(buffer.get(), buffer.get(), buffer.get()); + } + + @Override + public default Vec3s asShort(){return isMutable() ? Vec3s.newMutable(getX(), getY(), getZ()) : Vec3s.newVec(getX(), getY(), getZ());} + @Override + public default Vec3i asInt(){return isMutable() ? Vec3i.newMutable(getX(), getY(), getZ()) : Vec3i.newVec(getX(), getY(), getZ());} + @Override + public default Vec3l asLong(){return isMutable() ? Vec3l.newMutable(getX(), getY(), getZ()) : Vec3l.newVec(getX(), getY(), getZ());} + @Override + public default Vec3f asFloat() {return isMutable() ? Vec3f.newMutable(getX(), getY(), getZ()) : Vec3f.newVec(getX(), getY(), getZ());} + @Override + public default Vec3d asDouble(){return isMutable() ? Vec3d.newMutable(getX(), getY(), getZ()) : Vec3d.newVec(getX(), getY(), getZ());} + + @Override + public default Vec3b asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec3b asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec3b copyAsMutable(){return newMutable(this);} + @Override + public default Vec3b copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec3bImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec3bImmutable.java new file mode 100644 index 0000000..5e33e4d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec3bImmutable.java @@ -0,0 +1,108 @@ +package speiger.src.coreengine.math.vector.bytes; + +import java.util.Objects; + +public class Vec3bImmutable implements Vec3b +{ + final byte x; + final byte y; + final byte z; + + public Vec3bImmutable() + { + x = 0; + y = 0; + z = 0; + } + + public Vec3bImmutable(byte value) + { + x = value; + y = value; + z = value; + } + + public Vec3bImmutable(byte x, byte y, byte z) + { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public byte getX() + { + return x; + } + + @Override + public byte getY() + { + return y; + } + + @Override + public byte getZ() + { + return z; + } + + @Override + public Vec3b setX(byte x) + { + return this.x == x ? this : Vec3b.newVec(x, y, z); + } + + @Override + public Vec3b setY(byte y) + { + return this.y == y ? this : Vec3b.newVec(x, y, z); + } + + @Override + public Vec3b setZ(byte z) + { + return this.z == z ? this : Vec3b.newVec(x, y, z); + } + + @Override + public Vec3b copy() + { + return Vec3b.newVec(this); + } + + @Override + public Vec3b set(byte x, byte y, byte z) + { + return this.x == x && this.y == y && this.z == z ? this : Vec3b.newVec(x, y, z); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec3b) + { + Vec3b vec = (Vec3b)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z; + } + return false; + } + + @Override + public String toString() + { + return "Vec3b[x="+x+", y="+y+", z="+z+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec3bMutable.java b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec3bMutable.java new file mode 100644 index 0000000..2c59e64 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec3bMutable.java @@ -0,0 +1,111 @@ +package speiger.src.coreengine.math.vector.bytes; + +import java.util.Objects; + +public class Vec3bMutable implements Vec3b +{ + byte x; + byte y; + byte z; + + public Vec3bMutable() + { + } + + public Vec3bMutable(byte value) + { + x = value; + y = value; + z = value; + } + + public Vec3bMutable(byte x, byte y, byte z) + { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public byte getX() + { + return x; + } + + @Override + public byte getY() + { + return y; + } + + @Override + public byte getZ() + { + return z; + } + + @Override + public Vec3b setX(byte x) + { + this.x = x; + return this; + } + + @Override + public Vec3b setY(byte y) + { + this.y = y; + return this; + } + + @Override + public Vec3b setZ(byte z) + { + this.z = z; + return this; + } + + @Override + public Vec3b copy() + { + return Vec3b.newMutable(this); + } + + @Override + public Vec3b set(byte x, byte y, byte z) + { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec3b) + { + Vec3b vec = (Vec3b)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z; + } + return false; + } + + @Override + public String toString() + { + return "Vec3b[x="+x+", y="+y+", z="+z+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec4b.java b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec4b.java new file mode 100644 index 0000000..0355c70 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec4b.java @@ -0,0 +1,145 @@ +package speiger.src.coreengine.math.vector.bytes; + +import java.nio.ByteBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.doubles.Vec4d; +import speiger.src.coreengine.math.vector.floats.Vec4f; +import speiger.src.coreengine.math.vector.ints.Vec4i; +import speiger.src.coreengine.math.vector.longs.Vec4l; +import speiger.src.coreengine.math.vector.shorts.Vec4s; + +public interface Vec4b extends Vecb +{ + public static final Vec4b ZERO = newVec(); + public static final Vec4b MINUS_ONE = newVec((byte)-1); + public static final Vec4b ONE = newVec((byte)1); + + public static Vec4b newMutable(){return new Vec4bMutable();} + public static Vec4b newMutable(byte value){return new Vec4bMutable(value);} + public static Vec4b newMutable(byte x, byte y, byte z, byte w){return new Vec4bMutable(x, y, z, w);} + public static Vec4b newMutable(Vec4b vec){return newMutable(vec.getX(), vec.getY(), vec.getZ(), vec.getW());} + + public static Vec4b newVec(){return new Vec4bImmutable();} + public static Vec4b newVec(byte value){return new Vec4bImmutable(value);} + public static Vec4b newVec(byte x, byte y, byte z, byte w){return new Vec4bImmutable(x, y, z, w);} + public static Vec4b newVec(Vec4b vec){return newVec(vec.getX(), vec.getY(), vec.getZ(), vec.getW());} + + public byte getX(); + public byte getY(); + public byte getZ(); + public byte getW(); + public Vec4b setX(byte x); + public Vec4b setY(byte y); + public Vec4b setZ(byte z); + public Vec4b setW(byte w); + @Override + public default byte[] asArray(){return new byte[]{getX(), getY(), getZ(), getW()};} + @Override + public Vec4b copy(); + @Override + public default Vec4b abs(){return set((byte)Math.abs(getX()), (byte)Math.abs(getY()), (byte)Math.abs(getZ()), (byte)Math.abs(getW()));} + @Override + public default Vec4b negate(){return set((byte)0, (byte)0, (byte)0, (byte)0);} + @Override + public default Vec4b invert(){return set((byte)-getX(), (byte)-getY(), (byte)-getZ(), (byte)-getW());} + + @Override + public default Vec4b add(byte value){return add(value, value, value, value);} + public default Vec4b add(Vec4b value){return add(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4b add(byte x, byte y, byte z, byte w){return set((byte)(getX() + x), (byte)(getY() + y), (byte)(getZ() + z), (byte)(getW() + w));} + + @Override + public default Vec4b sub(byte value){return sub(value, value, value, value);} + public default Vec4b sub(Vec4b value){return sub(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4b sub(byte x, byte y, byte z, byte w){return set((byte)(getX() - x), (byte)(getY() - y), (byte)(getZ() - z), (byte)(getW() - w));} + + @Override + public default Vec4b multiply(byte value){return multiply(value, value, value, value);} + public default Vec4b multiply(Vec4b value){return multiply(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4b multiply(byte x, byte y, byte z, byte w){return set((byte)(getX() * x), (byte)(getY() * y), (byte)(getZ() * z), (byte)(getW() * w));} + + @Override + public default Vec4b devide(byte value){return devide(value, value, value, value);} + public default Vec4b devide(Vec4b value){return devide(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4b devide(byte x, byte y, byte z, byte w){return set((byte)(getX() / x), (byte)(getY() / y), (byte)(getZ() / z), (byte)(getW() / w));} + + @Override + public default Vec4b set(byte value){return set(value, value, value, value);} + public default Vec4b set(Vec4b value){return set(value.getX(), value.getY(), value.getZ(), value.getW());} + public Vec4b set(byte x, byte y, byte z, byte w); + + public default double distanceTo(Vec4b value){return distanceTo(value.getX(), value.getY(), value.getZ(), value.getW());} + public default double distanceTo(byte x, byte y, byte z, byte w){return Math.sqrt(distanceTo(x, y, z, w));} + + public default long distanceToSquared(Vec4b value){return distanceToSquared(value.getX(), value.getY(), value.getZ(), value.getW());} + public default long distanceToSquared(byte x, byte y, byte z, byte w) + { + long xPos = getX() - x; + long yPos = getY() - y; + long zPos = getZ() - z; + long wPos = getW() - w; + return (xPos * xPos) + (yPos * yPos) + (zPos * zPos) + (wPos * wPos); + } + @Override + public default long lengthSquared() {return (getX() * getX()) + (getY() * getY()) + (getZ() * getZ()) + (getW() * getW());} + + public default long dotProduct(Vec4b value){return dotProduct(value.getX(), value.getY(), value.getZ(), value.getW());} + public default long dotProduct(byte x, byte y, byte z, byte w){return (getX() * x) + (getY() * y) + (getZ() * z) + (getW() * w);}; + + public default Vec4b min(Vec4b other) {return min(other, this);} + public default Vec4b min(Vec4b other, Vec4b result){return min(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4b min(byte x, byte y, byte z, byte w) {return min(x, y, z, w, this);} + public default Vec4b min(byte x, byte y, byte z, byte w, Vec4b result){return result.set((byte)Math.min(getX(), x), (byte)Math.min(getY(), y), (byte)Math.min(getZ(), z), (byte)Math.min(getW(), w));} + + public default Vec4b max(Vec4b other) {return max(other, this);} + public default Vec4b max(Vec4b other, Vec4b result){return max(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4b max(byte x, byte y, byte z, byte w) {return max(x, y, z, w, this);} + public default Vec4b max(byte x, byte y, byte z, byte w, Vec4b result){return result.set((byte)Math.max(getX(), x), (byte)Math.max(getY(), y), (byte)Math.max(getZ(), z), (byte)Math.max(getZ(), z));} + + public default Vec4b difference(Vec4b other) {return difference(other, this);} + public default Vec4b difference(Vec4b other, Vec4b result){return difference(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4b difference(byte x, byte y, byte z, byte w) {return difference(x, y, z, w, this);} + public default Vec4b difference(byte x, byte y, byte z, byte w, Vec4b result){return result.set((byte)(getX() - x), (byte)(getY() - y), (byte)(getZ() - z), (byte)(getW() - w));} + + @Override + public default Vec4b clamp(byte min, byte max){return clamp(min, max, ALL);} + public default Vec4b clamp(byte min, byte max, Vec4b result){return clamp(min, max, result, ALL);} + @Override + public default Vec4b clamp(byte min, byte max, int filter){return clamp(min, max, this, filter);} + public default Vec4b clamp(byte min, byte max, Vec4b result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()), (filter & Z) == 0 ? getZ() : MathUtils.clamp(min, max, getZ()), (filter & W) == 0 ? getW() : MathUtils.clamp(min, max, getW()));} + + @Override + public default Vec4b store(ByteBuffer buffer) + { + buffer.put(getX()).put(getY()).put(getZ()).put(getW()); + return this; + } + + @Override + public default Vec4b load(ByteBuffer buffer) + { + return set(buffer.get(), buffer.get(), buffer.get(), buffer.get()); + } + + @Override + public default Vec4s asShort(){return isMutable() ? Vec4s.newMutable(getX(), getY(), getZ(), getW()) : Vec4s.newVec(getX(), getY(), getZ(), getW());} + @Override + public default Vec4i asInt(){return isMutable() ? Vec4i.newMutable(getX(), getY(), getZ(), getW()) : Vec4i.newVec(getX(), getY(), getZ(), getW());} + @Override + public default Vec4l asLong(){return isMutable() ? Vec4l.newMutable(getX(), getY(), getZ(), getW()) : Vec4l.newVec(getX(), getY(), getZ(), getW());} + @Override + public default Vec4f asFloat() {return isMutable() ? Vec4f.newMutable(getX(), getY(), getZ(), getW()) : Vec4f.newVec(getX(), getY(), getZ(), getW());} + @Override + public default Vec4d asDouble(){return isMutable() ? Vec4d.newMutable(getX(), getY(), getZ(), getW()) : Vec4d.newVec(getX(), getY(), getZ(), getW());} + + + @Override + public default Vec4b asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec4b asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec4b copyAsMutable(){return newMutable(this);} + @Override + public default Vec4b copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec4bImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec4bImmutable.java new file mode 100644 index 0000000..040bde5 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec4bImmutable.java @@ -0,0 +1,124 @@ +package speiger.src.coreengine.math.vector.bytes; + +import java.util.Objects; + +public class Vec4bImmutable implements Vec4b +{ + final byte x; + final byte y; + final byte z; + final byte w; + + public Vec4bImmutable() + { + x = 0; + y = 0; + z = 0; + w = 0; + } + + public Vec4bImmutable(byte value) + { + x = value; + y = value; + z = value; + w = value; + } + + public Vec4bImmutable(byte x, byte y, byte z, byte w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public byte getX() + { + return x; + } + + @Override + public byte getY() + { + return y; + } + + @Override + public byte getZ() + { + return z; + } + + @Override + public byte getW() + { + return w; + } + + @Override + public Vec4b setX(byte x) + { + return this.x == x ? this : Vec4b.newVec(x, y, z, w); + } + + @Override + public Vec4b setY(byte y) + { + return this.y == y ? this : Vec4b.newVec(x, y, z, w); + } + + @Override + public Vec4b setZ(byte z) + { + return this.z == z ? this : Vec4b.newVec(x, y, z, w); + } + + @Override + public Vec4b setW(byte w) + { + return this.w == w ? this : Vec4b.newVec(x, y, z, w); + } + + @Override + public Vec4b copy() + { + return Vec4b.newVec(this); + } + + @Override + public Vec4b set(byte x, byte y, byte z, byte w) + { + return this.x == x && this.y == y && this.z == z && this.w == w ? this : Vec4b.newVec(x, y, z, w); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z, w); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec4b) + { + Vec4b vec = (Vec4b)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z && vec.getW() == w; + } + return false; + } + + @Override + public String toString() + { + return "Vec4b[x="+x+", y="+y+", z="+z+", w="+w+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec4bMutable.java b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec4bMutable.java new file mode 100644 index 0000000..ca5c2fd --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vec4bMutable.java @@ -0,0 +1,129 @@ +package speiger.src.coreengine.math.vector.bytes; + +import java.util.Objects; + +public class Vec4bMutable implements Vec4b +{ + byte x; + byte y; + byte z; + byte w; + + public Vec4bMutable() + { + } + + public Vec4bMutable(byte value) + { + x = value; + y = value; + z = value; + w = value; + } + + public Vec4bMutable(byte x, byte y, byte z, byte w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public byte getX() + { + return x; + } + + @Override + public byte getY() + { + return y; + } + + @Override + public byte getZ() + { + return z; + } + + @Override + public byte getW() + { + return w; + } + + @Override + public Vec4b setX(byte x) + { + this.x = x; + return this; + } + + @Override + public Vec4b setY(byte y) + { + this.y = y; + return this; + } + + @Override + public Vec4b setZ(byte z) + { + this.z = z; + return this; + } + + @Override + public Vec4b setW(byte w) + { + this.w = w; + return this; + } + + @Override + public Vec4b copy() + { + return Vec4b.newMutable(this); + } + + @Override + public Vec4b set(byte x, byte y, byte z, byte w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z, w); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec4b) + { + Vec4b vec = (Vec4b)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z && vec.getW() == w; + } + return false; + } + + @Override + public String toString() + { + return "Vec4b[x="+x+", y="+y+", z="+z+", w="+w+"]"; + } + +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/bytes/Vecb.java b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vecb.java new file mode 100644 index 0000000..b4eb060 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/bytes/Vecb.java @@ -0,0 +1,29 @@ +package speiger.src.coreengine.math.vector.bytes; + +import speiger.src.coreengine.math.vector.Vec; +import speiger.src.coreengine.math.vector.doubles.Vecd; +import speiger.src.coreengine.math.vector.floats.Vecf; +import speiger.src.coreengine.math.vector.ints.Veci; +import speiger.src.coreengine.math.vector.longs.Vecl; +import speiger.src.coreengine.math.vector.shorts.Vecs; + +public interface Vecb extends Vec +{ + public Vecb set(byte value); + public Vecb add(byte value); + public Vecb sub(byte value); + public Vecb multiply(byte value); + public Vecb devide(byte value); + public Vecb clamp(byte min, byte max); + public Vecb clamp(byte min, byte max, int filter); + + public long lengthSquared(); + public default double length(){return Math.sqrt(lengthSquared());} + + public byte[] asArray(); + public Vecs asShort(); + public Veci asInt(); + public Vecl asLong(); + public Vecf asFloat(); + public Vecd asDouble(); +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec2d.java b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec2d.java new file mode 100644 index 0000000..9d2d5d2 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec2d.java @@ -0,0 +1,187 @@ +package speiger.src.coreengine.math.vector.doubles; + +import java.nio.ByteBuffer; +import java.nio.DoubleBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec2b; +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.math.vector.ints.Vec2i; +import speiger.src.coreengine.math.vector.longs.Vec2l; +import speiger.src.coreengine.math.vector.shorts.Vec2s; + +public interface Vec2d extends Vecd +{ + public static final Vec2d ZERO = newVec(); + public static final Vec2d MINUS_ONE = newVec(-1D); + public static final Vec2d ONE = newVec(1D); + + public static Vec2d newMutable(){return new Vec2dMutable();} + public static Vec2d newMutable(double value){return new Vec2dMutable(value);} + public static Vec2d newMutable(double x, double y){return new Vec2dMutable(x, y);} + public static Vec2d newMutable(Vec2d value){return newMutable(value.getX(), value.getY());} + + public static Vec2d newVec(){return new Vec2dImmutable();} + public static Vec2d newVec(double value){return new Vec2dImmutable(value);} + public static Vec2d newVec(double x, double y){return new Vec2dImmutable(x, y);} + public static Vec2d newVec(Vec2d value){return newVec(value.getX(), value.getY());} + + public double getX(); + public double getY(); + public Vec2d setX(double x); + public Vec2d setY(double y); + @Override + public default double[] asArray(){return new double[]{getX(), getY()};} + @Override + public Vec2d copy(); + @Override + public default Vec2d abs(){return set(Math.abs(getX()), Math.abs(getY()));} + @Override + public default Vec2d negate(){return set(0D, 0D);} + @Override + public default Vec2d invert(){return set(-getX(), -getY());}; + public default Vec2d normalize() + { + double l = length(); + return l == 0D ? this : multiply(1D / l); + } + + @Override + public default Vec2d add(double value) {return add(value, value);} + public default Vec2d add(Vec2d value) {return add(value.getX(), value.getY());} + public default Vec2d add(double x, double y) {return set(x + getX(), y + getY());} + + @Override + public default Vec2d sub(double value){return sub(value, value);} + public default Vec2d sub(Vec2d value){return sub(value.getX(), value.getY());} + public default Vec2d sub(double x, double y) {return set(getX() - x, getY() - y);} + + @Override + public default Vec2d multiply(double value){return multiply(value, value);} + public default Vec2d multiply(Vec2d value){return multiply(value.getX(), value.getY());} + public default Vec2d multiply(double x, double y) {return set(x * getX(), y * getY());} + + @Override + public default Vec2d devide(double value){return devide(value, value);} + public default Vec2d devide(Vec2d value){return devide(value.getX(), value.getY());} + public default Vec2d devide(double x, double y){return set(getX() / x, getY() / y);} + + @Override + public default Vec2d set(double value){return set(value, value);}; + public default Vec2d set(Vec2d value){return set(value.getX(), value.getY());} + public Vec2d set(double x, double y); + + public default double distanceTo(Vec2d value){return distanceTo(value.getX(), value.getY());} + public default double distanceTo(double x, double y){return Math.sqrt(distanceToSquared(x, y));} + + public default double distanceToSquared(Vec2d value){return distanceToSquared(value.getX(), value.getY());} + public default double distanceToSquared(double x, double y) + { + double xPos = getX() - x; + double yPos = getY() - y; + return (xPos * xPos) + (yPos * yPos); + } + @Override + public default double lengthSquared() {return (getX() * getX()) + (getY() * getY());} + + public default double dotProduct(Vec2d value){return dotProduct(value.getX(), value.getY());} + public default double dotProduct(double x, double y){return (getX() * x) + (getY() * y);} + + public default Vec2d lerp(Vec2d value, float progress, Vec2d result){return lerp(value.getX(), value.getY(), progress, result);} + public default Vec2d lerp(double x, double y, float progress, Vec2d result){return result.set(MathUtils.lerp(getX(), x, progress), MathUtils.lerp(getY(), y, progress));}; + + public default double angle(Vec2d value){return angle(value.getX(), value.getY());} + public default double angle(double x, double y){return (float)Math.atan2((getX() * y) - (getY() * x), (getX() * x) + (getY() * y));} + + public default double directionAngle(Vec2d value){return directionAngle(value.getX(), value.getY());} + public default double directionAngle(double x, double y){return (float)(-Math.toDegrees(Math.atan2(getX() - x, getY() - y)) % 360D);} + + public default Vec2d reflect(Vec2d value){return reflect(value.getX(), value.getY(), this);} + public default Vec2d reflect(double x, double y){return reflect(x, y, this);}; + public default Vec2d reflect(Vec2d value, Vec2d result){return reflect(value.getX(), value.getY(), result);} + public default Vec2d reflect(double x, double y, Vec2d result) + { + double dot = dotProduct(x, y); + double x2 = (float)((dot + dot) * x); + double y2 = (float)((dot + dot) * y); + return result.set(getX() - x2, getY() - y2); + } + + public default Vec2d rotate(double angle, Vec2d center){return rotate(angle, center.getX(), center.getY());} + public default Vec2d rotate(double angle, double x, double y) + { + double xPos = getX() - x; + double yPos = getY() - y; + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + return set((float)((xPos * cos) + (yPos * sin) + x), (float)(-(xPos * sin) + (yPos * cos) + y)); + } + + public default Vec2d min(Vec2d other) {return min(other, this);} + public default Vec2d min(Vec2d other, Vec2d result){return min(other.getX(), other.getY(), result);} + public default Vec2d min(double x, double y) {return min(x, y, this);} + public default Vec2d min(double x, double y, Vec2d result){return result.set(Math.min(getX(), x), Math.min(getY(), y));} + + public default Vec2d max(Vec2d other) {return max(other, this);} + public default Vec2d max(Vec2d other, Vec2d result){return max(other.getX(), other.getY(), result);} + public default Vec2d max(double x, double y) {return max(x, y, this);} + public default Vec2d max(double x, double y, Vec2d result){return result.set(Math.max(getX(), x), Math.max(getY(), y));} + + public default Vec2d difference(Vec2d other) {return difference(other, this);} + public default Vec2d difference(Vec2d other, Vec2d result){return difference(other.getX(), other.getY(), result);} + public default Vec2d difference(double x, double y) {return difference(x, y, this);} + public default Vec2d difference(double x, double y, Vec2d result){return result.set(getX() - x, getY() - y);} + + @Override + public default Vec2d clamp(double min, double max){return clamp(min, max, ALL);} + public default Vec2d clamp(double min, double max, Vec2d result){return clamp(min, max, result, ALL);} + @Override + public default Vec2d clamp(double min, double max, int filter){return clamp(min, max, this, filter);} + public default Vec2d clamp(double min, double max, Vec2d result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()));} + + @Override + public default Vec2d store(ByteBuffer buffer) + { + buffer.putDouble(getX()).putDouble(getY()); + return this; + } + + @Override + public default Vec2d load(ByteBuffer buffer) + { + return set(buffer.getDouble(), buffer.getDouble()); + } + + @Override + public default Vec2d store(DoubleBuffer buffer) + { + buffer.put(getX()).put(getY()); + return this; + } + + @Override + public default Vec2d load(DoubleBuffer buffer) + { + return set(buffer.get(), buffer.get()); + } + @Override + public default Vec2b asByte(){return isMutable() ? Vec2b.newMutable((byte)MathUtils.floor(getX()), (byte)MathUtils.floor(getY())) : Vec2b.newVec((byte)MathUtils.floor(getX()), (byte)MathUtils.floor(getY()));} + @Override + public default Vec2s asShort(){return isMutable() ? Vec2s.newMutable((short)MathUtils.floor(getX()), (short)MathUtils.floor(getY())) : Vec2s.newVec((short)MathUtils.floor(getX()), (short)MathUtils.floor(getY()));} + @Override + public default Vec2i asInt(){return isMutable() ? Vec2i.newMutable(MathUtils.floor(getX()), MathUtils.floor(getY())) : Vec2i.newVec(MathUtils.floor(getX()), MathUtils.floor(getY()));} + @Override + public default Vec2l asLong() {return isMutable() ? Vec2l.newMutable(MathUtils.floor(getX()), MathUtils.floor(getY())) : Vec2l.newVec(MathUtils.floor(getX()), MathUtils.floor(getY()));} + @Override + public default Vec2f asFloat(){return isMutable() ? Vec2f.newMutable((float)getX(), (float)getY()) : Vec2f.newVec((float)getX(), (float)getY());} + + + @Override + public default Vec2d asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec2d asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec2d copyAsMutable(){return newMutable(this);} + @Override + public default Vec2d copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec2dImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec2dImmutable.java new file mode 100644 index 0000000..210e961 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec2dImmutable.java @@ -0,0 +1,92 @@ +package speiger.src.coreengine.math.vector.doubles; + +import java.util.Objects; + +public class Vec2dImmutable implements Vec2d +{ + final double x; + final double y; + + public Vec2dImmutable() + { + x = 0D; + y = 0D; + } + + public Vec2dImmutable(double value) + { + x = value; + y = value; + } + + public Vec2dImmutable(double x, double y) + { + this.x = x; + this.y = y; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public double getX() + { + return x; + } + + @Override + public double getY() + { + return y; + } + + @Override + public Vec2d setX(double x) + { + return this.x == x ? this : Vec2d.newVec(x, y); + } + + @Override + public Vec2d setY(double y) + { + return this.y == y ? this : Vec2d.newVec(x, y); + } + + @Override + public Vec2d copy() + { + return Vec2d.newVec(this); + } + + @Override + public Vec2d set(double x, double y) + { + return this.x == x && this.y == y ? this : Vec2d.newVec(x, y); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec2d) + { + Vec2d vec = (Vec2d)obj; + return vec.getX() == x && vec.getY() == y; + } + return false; + } + + @Override + public String toString() + { + return "Vec2d[x="+x+", y="+y+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec2dMutable.java b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec2dMutable.java new file mode 100644 index 0000000..0e2c107 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec2dMutable.java @@ -0,0 +1,96 @@ +package speiger.src.coreengine.math.vector.doubles; + +import java.util.Objects; + +public class Vec2dMutable implements Vec2d +{ + double x; + double y; + + public Vec2dMutable() + { + x = 0D; + y = 0D; + } + + public Vec2dMutable(double value) + { + x = value; + y = value; + } + + public Vec2dMutable(double x, double y) + { + this.x = x; + this.y = y; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public double getX() + { + return x; + } + + @Override + public double getY() + { + return y; + } + + @Override + public Vec2d setX(double x) + { + this.x = x; + return this; + } + + @Override + public Vec2d setY(double y) + { + this.y = y; + return this; + } + + @Override + public Vec2d copy() + { + return Vec2d.newMutable(this); + } + + @Override + public Vec2d set(double x, double y) + { + this.x = x; + this.y = y; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec2d) + { + Vec2d vec = (Vec2d)obj; + return vec.getX() == x && vec.getY() == y; + } + return false; + } + + @Override + public String toString() + { + return "Vec2d[x="+x+", y="+y+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec3d.java b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec3d.java new file mode 100644 index 0000000..95c9652 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec3d.java @@ -0,0 +1,241 @@ +package speiger.src.coreengine.math.vector.doubles; + +import java.nio.ByteBuffer; +import java.nio.DoubleBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec3b; +import speiger.src.coreengine.math.vector.floats.Vec3f; +import speiger.src.coreengine.math.vector.ints.Vec3i; +import speiger.src.coreengine.math.vector.longs.Vec3l; +import speiger.src.coreengine.math.vector.shorts.Vec3s; + +public interface Vec3d extends Vecd +{ + public static final Vec3d ZERO = newVec(); + public static final Vec3d MINUS_ONE = newVec(-1D); + public static final Vec3d ONE = newVec(1D); + + public static Vec3d newMutable(){return new Vec3dMutable();} + public static Vec3d newMutable(double value){return new Vec3dMutable(value);} + public static Vec3d newMutable(double x, double y, double z){return new Vec3dMutable(x, y, z);} + public static Vec3d newMutable(Vec3d vec){return newMutable(vec.getX(), vec.getY(), vec.getZ());} + + public static Vec3d newVec(){return new Vec3dImmutable();} + public static Vec3d newVec(double value){return new Vec3dImmutable(value);} + public static Vec3d newVec(double x, double y, double z){return new Vec3dImmutable(x, y, z);} + public static Vec3d newVec(Vec3d vec){return newVec(vec.getX(), vec.getY(), vec.getZ());} + + public double getX(); + public double getY(); + public double getZ(); + public Vec3d setX(double x); + public Vec3d setY(double y); + public Vec3d setZ(double z); + @Override + public default double[] asArray(){return new double[]{getX(), getY(), getZ()};} + + @Override + public Vec3d copy(); + @Override + public default Vec3d abs(){return set(Math.abs(getX()), Math.abs(getY()), Math.abs(getZ()));} + @Override + public default Vec3d negate(){return set(0D, 0D, 0D);} + @Override + public default Vec3d invert(){return set(-getX(), -getY(), -getZ());} + public default Vec3d normalize() + { + double l = length(); + return l == 0D ? this : multiply(1.0D / l); + } + @Override + public default Vec3d add(double value){return add(value, value, value);} + public default Vec3d add(Vec3d value){return add(value.getX(), value.getY(), value.getZ());} + public default Vec3d add(double x, double y, double z){return set(getX() + x, getY() + y, getZ() + z);} + + @Override + public default Vec3d sub(double value){return sub(value, value, value);} + public default Vec3d sub(Vec3d value){return sub(value.getX(), value.getY(), value.getZ());} + public default Vec3d sub(double x, double y, double z){return set(getX() - x, getY() - y, getZ() - z);} + + @Override + public default Vec3d multiply(double value){return multiply(value, value, value);} + public default Vec3d multiply(Vec3d value){return multiply(value.getX(), value.getY(), value.getZ());} + public default Vec3d multiply(double x, double y, double z){return set(getX() * x, getY() * y, getZ() * z);} + + @Override + public default Vec3d devide(double value){return devide(value, value, value);} + public default Vec3d devide(Vec3d value){return devide(value.getX(), value.getY(), value.getZ());} + public default Vec3d devide(double x, double y, double z){return set(getX() / x, getY() / y, getZ() / z);} + + @Override + public default Vec3d set(double value){return set(value, value, value);} + public default Vec3d set(Vec3d value){return set(value.getX(), value.getY(), value.getZ());} + public Vec3d set(double x, double y, double z); + + public default double distanceTo(Vec3d value){return distanceTo(value.getX(), value.getY(), value.getZ());} + public default double distanceTo(double x, double y, double z){return Math.sqrt(distanceToSquared(x, y, z));} + + public default double distanceToSquared(Vec3d value){return distanceToSquared(value.getX(), value.getY(), value.getZ());} + public default double distanceToSquared(double x, double y, double z) + { + double xPos = getX() - x; + double yPos = getY() - y; + double zPos = getZ() - z; + return (xPos * xPos) + (yPos * yPos) + (zPos * zPos); + } + @Override + public default double lengthSquared() {return (getX() * getX()) + (getY() * getY()) + (getZ() * getZ());} + + public default double dotProduct(Vec3d value){return dotProduct(value.getX(), value.getY(), value.getZ());} + public default double dotProduct(double x, double y, double z){return (getX() * x) + (getY() * y) + (getZ() * z);} + + public default Vec3d lerp(Vec3d value, float progress, Vec3d result){return lerp(value.getX(), value.getY(), value.getZ(), progress, result);} + public default Vec3d lerp(double x, double y, double z, float progress, Vec3d result){return result.set(MathUtils.lerp(getX(), x, progress), MathUtils.lerp(getY(), y, progress), MathUtils.lerp(getZ(), z, progress));} + + public default double angle(Vec3d value){return angle(value.getX(), value.getY(), value.getZ());} + public default double angle(double x, double y, double z){return Math.acos(MathUtils.clamp(-1, 1, angleCos(x, y, z)));} + + public default double angleCos(Vec3d value){return angleCos(value.getX(), value.getY(), value.getZ());} + public default double angleCos(double x, double y, double z) + { + double myLength = (getX() * getX()) + (getY() * getY()) + (getZ() * getZ()); + double otherLength = (x * x) + (y * y) + (z * z); + double dot = (getX() * x) + (getY() * y) + (getZ() * z); + return dot / (Math.sqrt(myLength * otherLength)); + } + + public default Vec3d crossProduct(Vec3d value){return crossProduct(value.getX(), value.getY(), value.getZ(), this);} + public default Vec3d crossProduct(Vec3d value, Vec3d result){return crossProduct(value.getX(), value.getY(), value.getZ(), result);} + public default Vec3d crossProduct(double x, double y, double z){return crossProduct(x, y, z, this);} + public default Vec3d crossProduct(double x, double y, double z, Vec3d result){return result.set((getY() * z) - (getZ() * y), (getZ() * x) - (getX() * z), (getX() * y) - (getY() * x));} + + public default Vec3d reflect(Vec3d value){return reflect(value.getX(), value.getY(), value.getZ(), this);} + public default Vec3d reflect(Vec3d value, Vec3d result){return reflect(value.getX(), value.getY(), value.getZ(), result);} + public default Vec3d reflect(double x, double y, double z){return reflect(x, y, z, this);} + public default Vec3d reflect(double x, double y, double z, Vec3d result) + { + double dot = dotProduct(x, y, z) * 2D; + return result.set(getX() - dot * x, getY() - dot * y, getZ() - dot * z); + } + + public default Vec3d rotate(double angle, Vec3d axis){return rotate(angle, axis.getX(), axis.getY(), axis.getZ());} + public default Vec3d rotate(double angle, Vec3d axis, Vec3d result){return rotate(angle, axis.getX(), axis.getY(), axis.getZ(), result);} + public default Vec3d rotate(double angle, double x, double y, double z){return rotate(angle, x, y, z, this);} + public default Vec3d rotate(double angle, double x, double y, double z, Vec3d result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double dot = dotProduct(x, y, z); + double xPos = x * dot * (1D - cos) + (getX() * cos) + (-z * getY() + y * getZ()) * sin; + double yPos = y * dot * (1D - cos) + (getY() * cos) + (z * getX() - x * getZ()) * sin; + double zPos = z * dot * (1D - cos) + (getZ() * cos) + (-y * getX() + x * getY()) * sin; + return result.set(xPos, yPos, zPos); + } + public default Vec3d rotateX(double angle){return rotateX(angle, this);} + public default Vec3d rotateX(double angle, Vec3d result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double y = getY() * cos + getZ() * sin; + double z = getZ() * cos - getY() * sin; + return result.set(getX(), y, z); + } + public default Vec3d rotateY(double angle){return rotateY(angle, this);} + public default Vec3d rotateY(double angle, Vec3d result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double x = getX() * cos + getZ() * sin; + double z = getZ() * cos - getX() * sin; + return result.set(x, getY(), z); + } + public default Vec3d rotateZ(double angle){return rotateZ(angle, this);} + public default Vec3d rotateZ(double angle, Vec3d result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double x = cos * getX() - sin * getY(); + double y = sin * getX() + cos * getY(); + return result.set(x, y, getZ()); + } + + public default Vec3d smoothStep(Vec3d value, float progress, Vec3d result){return smoothStep(value.getX(), value.getY(), value.getZ(), progress, result);} + public default Vec3d smoothStep(double x, double y, double z, float progress, Vec3d result) + { + double t2 = progress * progress; + double t3 = t2 * progress; + double xPos = ((getX() + getX() - x - x) * t3 + (3.0D * x - 3.0D * getX()) * t2 + getX() * progress + getX()); + double yPos = ((getY() + getY() - y - y) * t3 + (3.0D * y - 3.0D * getY()) * t2 + getY() * progress + getY()); + double zPos = ((getZ() + getZ() - z - z) * t3 + (3.0D * z - 3.0D * getZ()) * t2 + getZ() * progress + getZ()); + return result.set(xPos, yPos, zPos); + } + + public default Vec3d min(Vec3d other) {return min(other, this);} + public default Vec3d min(Vec3d other, Vec3d result){return min(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3d min(double x, double y, double z) {return min(x, y, z, this);} + public default Vec3d min(double x, double y, double z, Vec3d result){return result.set(Math.min(getX(), x), Math.min(getY(), y), Math.min(getZ(), z));} + + public default Vec3d max(Vec3d other) {return max(other, this);} + public default Vec3d max(Vec3d other, Vec3d result){return max(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3d max(double x, double y, double z) {return max(x, y, z, this);} + public default Vec3d max(double x, double y, double z, Vec3d result){return result.set(Math.max(getX(), x), Math.max(getY(), y), Math.max(getZ(), z));} + + public default Vec3d difference(Vec3d other) {return difference(other, this);} + public default Vec3d difference(Vec3d other, Vec3d result){return difference(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3d difference(double x, double y, double z) {return difference(x, y, z, this);} + public default Vec3d difference(double x, double y, double z, Vec3d result){return result.set(getX() - x, getY() - y, getZ() - z);} + + @Override + public default Vec3d clamp(double min, double max){return clamp(min, max, ALL);} + public default Vec3d clamp(double min, double max, Vec3d result){return clamp(min, max, result, ALL);} + @Override + public default Vec3d clamp(double min, double max, int filter){return clamp(min, max, this, filter);} + public default Vec3d clamp(double min, double max, Vec3d result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()), (filter & Z) == 0 ? getZ() : MathUtils.clamp(min, max, getZ()));} + + @Override + public default Vec3d store(ByteBuffer buffer) + { + buffer.putDouble(getX()).putDouble(getY()).putDouble(getZ()); + return this; + } + + @Override + public default Vec3d load(ByteBuffer buffer) + { + return set(buffer.getDouble(), buffer.getDouble(), buffer.getDouble()); + } + + @Override + public default Vec3d store(DoubleBuffer buffer) + { + buffer.put(getX()).put(getY()).put(getZ()); + return this; + } + + @Override + public default Vec3d load(DoubleBuffer buffer) + { + return set(buffer.get(), buffer.get(), buffer.get()); + } + + @Override + public default Vec3b asByte(){return isMutable() ? Vec3b.newMutable((byte)MathUtils.floor(getX()), (byte)MathUtils.floor(getY()), (byte)MathUtils.floor(getZ())) : Vec3b.newVec((byte)MathUtils.floor(getX()), (byte)MathUtils.floor(getY()), (byte)MathUtils.floor(getZ()));} + @Override + public default Vec3s asShort(){return isMutable() ? Vec3s.newMutable((short)MathUtils.floor(getX()), (short)MathUtils.floor(getY()), (short)MathUtils.floor(getZ())) : Vec3s.newVec((short)MathUtils.floor(getX()), (short)MathUtils.floor(getY()), (short)MathUtils.floor(getZ()));} + @Override + public default Vec3i asInt(){return isMutable() ? Vec3i.newMutable(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ())) : Vec3i.newVec(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ()));} + @Override + public default Vec3l asLong() {return isMutable() ? Vec3l.newMutable(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ())) : Vec3l.newVec(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ()));} + @Override + public default Vec3f asFloat(){return isMutable() ? Vec3f.newMutable((float)getX(), (float)getY(), (float)getZ()) : Vec3f.newVec((float)getX(), (float)getY(), (float)getZ());} + + @Override + public default Vec3d asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec3d asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec3d copyAsMutable(){return newMutable(this);} + @Override + public default Vec3d copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec3dImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec3dImmutable.java new file mode 100644 index 0000000..c99b341 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec3dImmutable.java @@ -0,0 +1,108 @@ +package speiger.src.coreengine.math.vector.doubles; + +import java.util.Objects; + +public class Vec3dImmutable implements Vec3d +{ + final double x; + final double y; + final double z; + + public Vec3dImmutable() + { + x = 0; + y = 0; + z = 0; + } + + public Vec3dImmutable(double value) + { + x = value; + y = value; + z = value; + } + + public Vec3dImmutable(double x, double y, double z) + { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public double getX() + { + return x; + } + + @Override + public double getY() + { + return y; + } + + @Override + public double getZ() + { + return z; + } + + @Override + public Vec3d setX(double x) + { + return this.x == x ? this : Vec3d.newVec(x, y, z); + } + + @Override + public Vec3d setY(double y) + { + return this.y == y ? this : Vec3d.newVec(x, y, z); + } + + @Override + public Vec3d setZ(double z) + { + return this.z == z ? this : Vec3d.newVec(x, y, z); + } + + @Override + public Vec3d copy() + { + return Vec3d.newVec(this); + } + + @Override + public Vec3d set(double x, double y, double z) + { + return this.x == x && this.y == y && this.z == z ? this : Vec3d.newVec(x, y, z); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec3d) + { + Vec3d vec = (Vec3d)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z; + } + return false; + } + + @Override + public String toString() + { + return "Vec3d[x="+x+", y="+y+", z="+z+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec3dMutable.java b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec3dMutable.java new file mode 100644 index 0000000..34a929b --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec3dMutable.java @@ -0,0 +1,114 @@ +package speiger.src.coreengine.math.vector.doubles; + +import java.util.Objects; + +public class Vec3dMutable implements Vec3d +{ + double x; + double y; + double z; + + public Vec3dMutable() + { + x = 0; + y = 0; + z = 0; + } + + public Vec3dMutable(double value) + { + x = value; + y = value; + z = value; + } + + public Vec3dMutable(double x, double y, double z) + { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public double getX() + { + return x; + } + + @Override + public double getY() + { + return y; + } + + @Override + public double getZ() + { + return z; + } + + @Override + public Vec3d setX(double x) + { + this.x = x; + return this; + } + + @Override + public Vec3d setY(double y) + { + this.y = y; + return this; + } + + @Override + public Vec3d setZ(double z) + { + this.z = z; + return this; + } + + @Override + public Vec3d copy() + { + return Vec3d.newMutable(this); + } + + @Override + public Vec3d set(double x, double y, double z) + { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec3d) + { + Vec3d vec = (Vec3d)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z; + } + return false; + } + + @Override + public String toString() + { + return "Vec3d[x="+x+", y="+y+", z="+z+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec4d.java b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec4d.java new file mode 100644 index 0000000..6025d44 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec4d.java @@ -0,0 +1,231 @@ +package speiger.src.coreengine.math.vector.doubles; + +import java.nio.ByteBuffer; +import java.nio.DoubleBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec4b; +import speiger.src.coreengine.math.vector.floats.Vec4f; +import speiger.src.coreengine.math.vector.ints.Vec4i; +import speiger.src.coreengine.math.vector.longs.Vec4l; +import speiger.src.coreengine.math.vector.shorts.Vec4s; + +public interface Vec4d extends Vecd +{ + public static final Vec4d ZERO = newVec(); + public static final Vec4d MINUS_ONE = newVec(-1D); + public static final Vec4d ONE = newVec(1D); + + public static Vec4d newMutable(){return new Vec4dMutable();} + public static Vec4d newMutable(double value){return new Vec4dMutable(value);} + public static Vec4d newMutable(double x, double y, double z, double w){return new Vec4dMutable(x, y, z, w);} + public static Vec4d newMutable(Vec4d vec){return newMutable(vec.getX(), vec.getY(), vec.getZ(), vec.getW());} + + public static Vec4d newVec(){return new Vec4dImmutable();} + public static Vec4d newVec(double value){return new Vec4dImmutable(value);} + public static Vec4d newVec(double x, double y, double z, double w){return new Vec4dImmutable(x, y, z, w);} + public static Vec4d newVec(Vec4d vec){return newVec(vec.getX(), vec.getY(), vec.getZ(), vec.getW());} + + public double getX(); + public double getY(); + public double getZ(); + public double getW(); + public Vec4d setX(double x); + public Vec4d setY(double y); + public Vec4d setZ(double z); + public Vec4d setW(double w); + @Override + public default double[] asArray(){return new double[]{getX(), getY(), getZ(), getW()};} + @Override + public Vec4d copy(); + @Override + public default Vec4d abs(){return set(Math.abs(getX()), Math.abs(getY()), Math.abs(getZ()), Math.abs(getW()));} + @Override + public default Vec4d negate(){return set(0D, 0D, 0D, 0D);} + @Override + public default Vec4d invert(){return set(-getX(), -getY(), -getZ(), -getW());} + public default Vec4d normalize() + { + double l = length(); + return l == 0D ? this : multiply(1.0D / l); + } + + public default Vec4d normalize3D() + { + double value = (getX() * getX()) + (getY() * getY()) + (getZ() * getZ()); + return value == 0D ? this : multiply(1D / value); + } + + @Override + public default Vec4d add(double value){return add(value, value, value, value);} + public default Vec4d add(Vec4d value){return add(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4d add(double x, double y, double z, double w){return set(getX() + x, getY() + y, getZ() + z, getW() + w);} + + @Override + public default Vec4d sub(double value){return sub(value, value, value, value);} + public default Vec4d sub(Vec4d value){return sub(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4d sub(double x, double y, double z, double w){return set(getX() - x, getY() - y, getZ() - z, getW() - w);} + + @Override + public default Vec4d multiply(double value){return multiply(value, value, value, value);} + public default Vec4d multiply(Vec4d value){return multiply(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4d multiply(double x, double y, double z, double w){return set(getX() * x, getY() * y, getZ() * z, getW() * w);} + + @Override + public default Vec4d devide(double value){return devide(value, value, value, value);} + public default Vec4d devide(Vec4d value){return devide(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4d devide(double x, double y, double z, double w){return set(getX() / x, getY() / y, getZ() / z, getW() / w);} + + @Override + public default Vec4d set(double value){return set(value, value, value, value);} + public default Vec4d set(Vec4d value){return set(value.getX(), value.getY(), value.getZ(), value.getW());} + public Vec4d set(double x, double y, double z, double w); + + public default double distanceTo(Vec4d value){return distanceTo(value.getX(), value.getY(), value.getZ(), value.getW());} + public default double distanceTo(double x, double y, double z, double w){return Math.sqrt(distanceTo(x, y, z, w));} + + public default double distanceToSquared(Vec4d value){return distanceToSquared(value.getX(), value.getY(), value.getZ(), value.getW());} + public default double distanceToSquared(double x, double y, double z, double w) + { + double xPos = getX() - x; + double yPos = getY() - y; + double zPos = getZ() - z; + double wPos = getW() - w; + return (xPos * xPos) + (yPos * yPos) + (zPos * zPos) + (wPos * wPos); + } + @Override + public default double lengthSquared() {return (getX() * getX()) + (getY() * getY()) + (getZ() * getZ()) + (getW() * getW());} + + public default double dotProduct(Vec4d value){return dotProduct(value.getX(), value.getY(), value.getZ(), value.getW());} + public default double dotProduct(double x, double y, double z, double w){return (getX() * x) + (getY() * y) + (getZ() * z) + (getW() * w);}; + + public default Vec4d lerp(Vec4d value, float progress, Vec4d result){return lerp(value.getX(), value.getY(), value.getZ(), value.getW(), progress, result);} + public default Vec4d lerp(double x, double y, double z, double w, float progress, Vec4d result){return result.set(MathUtils.lerp(getX(), x, progress), MathUtils.lerp(getY(), y, progress), MathUtils.lerp(getZ(), z, progress), MathUtils.lerp(getW(), w, progress));} + + public default double angle(Vec4d value){return angle(value.getX(), value.getY(), value.getZ(), value.getW());} + public default double angle(double x, double y, double z, double w){return Math.acos(MathUtils.clamp(-1D, 1D, angleCos(x, y, z, w)));} + + public default double angleCos(Vec4d value){return angleCos(value.getX(), value.getY(), value.getZ(), value.getW());} + public default double angleCos(double x, double y, double z, double w){return dotProduct(x, y, z, w) / Math.sqrt(lengthSquared() * (x * x) + (y * y) + (z * z) + (w * w));} + + public default Vec4d rotate(double angle, Vec4d axis){return rotate(angle, axis.getX(), axis.getY(), axis.getZ(), axis.getW());} + public default Vec4d rotate(double angle, Vec4d axis, Vec4d result){return rotate(angle, axis.getX(), axis.getY(), axis.getZ(), axis.getW(), result);} + public default Vec4d rotate(double angle, double x, double y, double z, double w){return rotate(angle, x, y, z, w, this);} + public default Vec4d rotate(double angle, double x, double y, double z, double w, Vec4d result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double dot = dotProduct(x, y, z, w); + double xPos = x * dot * (1D - cos) + (getX() * cos) + (-z * getY() + y * getZ()) * sin; + double yPos = y * dot * (1D - cos) + (getY() * cos) + (z * getX() - x * getZ()) * sin; + double zPos = z * dot * (1D - cos) + (getZ() * cos) + (-y * getX() + x * getY()) * sin; + return result.set(xPos, yPos, zPos, getW()); + } + public default Vec4d rotateX(double angle){return rotateX(angle, this);} + public default Vec4d rotateX(double angle, Vec4d result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double y = getY() * cos + getZ() * sin; + double z = getZ() * cos - getY() * sin; + return result.set(getX(), y, z, getW()); + } + public default Vec4d rotateY(double angle){return rotateY(angle, this);} + public default Vec4d rotateY(double angle, Vec4d result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double x = getX() * cos + getZ() * sin; + double z = getZ() * cos - getX() * sin; + return result.set(x, getY(), z, getW()); + } + public default Vec4d rotateZ(double angle){return rotateZ(angle, this);} + public default Vec4d rotateZ(double angle, Vec4d result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double x = cos * getX() - sin * getY(); + double y = sin * getX() + cos * getY(); + return result.set(x, y, getZ(), getW()); + } + + public default Vec4d smoothStep(Vec4d value, float progress, Vec4d result){return smoothStep(value.getX(), value.getY(), value.getZ(), value.getW(), progress, result);} + public default Vec4d smoothStep(double x, double y, double z, double w, float progress, Vec4d result) + { + double t2 = progress * progress; + double t3 = t2 * progress; + double xPos = ((getX() + getX() - x - x) * t3 + (3.0D * x - 3.0D * getX()) * t2 + getX() * progress + getX()); + double yPos = ((getY() + getY() - y - y) * t3 + (3.0D * y - 3.0D * getY()) * t2 + getY() * progress + getY()); + double zPos = ((getZ() + getZ() - z - z) * t3 + (3.0D * z - 3.0D * getZ()) * t2 + getZ() * progress + getZ()); + double wPos = ((getW() + getW() - w - w) * t3 + (3.0D * w - 3.0D * getW()) * t2 + getW() * progress + getW()); + return result.set(xPos, yPos, zPos, wPos); + } + + public default Vec4d min(Vec4d other) {return min(other, this);} + public default Vec4d min(Vec4d other, Vec4d result){return min(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4d min(double x, double y, double z, double w) {return min(x, y, z, w, this);} + public default Vec4d min(double x, double y, double z, double w, Vec4d result){return result.set(Math.min(getX(), x), Math.min(getY(), y), Math.min(getZ(), z), Math.min(getW(), w));} + + public default Vec4d max(Vec4d other) {return max(other, this);} + public default Vec4d max(Vec4d other, Vec4d result){return max(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4d max(double x, double y, double z, double w) {return max(x, y, z, w, this);} + public default Vec4d max(double x, double y, double z, double w, Vec4d result){return result.set(Math.max(getX(), x), Math.max(getY(), y), Math.max(getZ(), z), Math.max(getZ(), z));} + + public default Vec4d difference(Vec4d other) {return difference(other, this);} + public default Vec4d difference(Vec4d other, Vec4d result){return difference(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4d difference(double x, double y, double z, double w) {return difference(x, y, z, w, this);} + public default Vec4d difference(double x, double y, double z, double w, Vec4d result){return result.set(getX() - x, getY() - y, getZ() - z, getW() - w);} + + @Override + public default Vec4d clamp(double min, double max){return clamp(min, max, ALL);} + public default Vec4d clamp(double min, double max, Vec4d result){return clamp(min, max, result, ALL);} + @Override + public default Vec4d clamp(double min, double max, int filter){return clamp(min, max, this, filter);} + public default Vec4d clamp(double min, double max, Vec4d result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()), (filter & Z) == 0 ? getZ() : MathUtils.clamp(min, max, getZ()), (filter & W) == 0 ? getW() : MathUtils.clamp(min, max, getW()));} + + @Override + public default Vec4d store(ByteBuffer buffer) + { + buffer.putDouble(getX()).putDouble(getY()).putDouble(getZ()).putDouble(getW()); + return this; + } + + @Override + public default Vec4d load(ByteBuffer buffer) + { + return set(buffer.getDouble(), buffer.getDouble(), buffer.getDouble(), buffer.getDouble()); + } + + @Override + public default Vec4d store(DoubleBuffer buffer) + { + buffer.put(getX()).put(getY()).put(getZ()).put(getW()); + return this; + } + + @Override + public default Vec4d load(DoubleBuffer buffer) + { + return set(buffer.get(), buffer.get(), buffer.get(), buffer.get()); + } + + @Override + public default Vec4b asByte(){return isMutable() ? Vec4b.newMutable((byte)MathUtils.floor(getX()), (byte)MathUtils.floor(getY()), (byte)MathUtils.floor(getZ()), (byte)MathUtils.floor(getW())) : Vec4b.newVec((byte)MathUtils.floor(getX()), (byte)MathUtils.floor(getY()), (byte)MathUtils.floor(getZ()), (byte)MathUtils.floor(getW()));} + @Override + public default Vec4s asShort(){return isMutable() ? Vec4s.newMutable((short)MathUtils.floor(getX()), (short)MathUtils.floor(getY()), (short)MathUtils.floor(getZ()), (short)MathUtils.floor(getW())) : Vec4s.newVec((short)MathUtils.floor(getX()), (short)MathUtils.floor(getY()), (short)MathUtils.floor(getZ()), (short)MathUtils.floor(getW()));} + @Override + public default Vec4i asInt(){return isMutable() ? Vec4i.newMutable(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ()), MathUtils.floor(getW())) : Vec4i.newVec(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ()), MathUtils.floor(getW()));} + @Override + public default Vec4l asLong() {return isMutable() ? Vec4l.newMutable(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ()), MathUtils.floor(getW())) : Vec4l.newVec(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ()), MathUtils.floor(getW()));} + @Override + public default Vec4f asFloat(){return isMutable() ? Vec4f.newMutable((float)getX(), (float)getY(), (float)getZ(), (float)getW()) : Vec4f.newVec((float)getX(), (float)getY(), (float)getZ(), (float)getW());} + + @Override + public default Vec4d asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec4d asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec4d copyAsMutable(){return newMutable(this);} + @Override + public default Vec4d copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec4dImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec4dImmutable.java new file mode 100644 index 0000000..cb6c1ac --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec4dImmutable.java @@ -0,0 +1,124 @@ +package speiger.src.coreengine.math.vector.doubles; + +import java.util.Objects; + +public class Vec4dImmutable implements Vec4d +{ + final double x; + final double y; + final double z; + final double w; + + public Vec4dImmutable() + { + x = 0; + y = 0; + z = 0; + w = 0; + } + + public Vec4dImmutable(double value) + { + x = value; + y = value; + z = value; + w = value; + } + + public Vec4dImmutable(double x, double y, double z, double w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public double getX() + { + return x; + } + + @Override + public double getY() + { + return y; + } + + @Override + public double getZ() + { + return z; + } + + @Override + public double getW() + { + return w; + } + + @Override + public Vec4d setX(double x) + { + return this.x == x ? this : Vec4d.newVec(x, y, z, w); + } + + @Override + public Vec4d setY(double y) + { + return this.y == y ? this : Vec4d.newVec(x, y, z, w); + } + + @Override + public Vec4d setZ(double z) + { + return this.z == z ? this : Vec4d.newVec(x, y, z, w); + } + + @Override + public Vec4d setW(double w) + { + return this.w == w ? this : Vec4d.newVec(x, y, z, w); + } + + @Override + public Vec4d copy() + { + return Vec4d.newVec(this); + } + + @Override + public Vec4d set(double x, double y, double z, double w) + { + return this.x == x && this.y == y && this.z == z && this.w == w ? this : Vec4d.newVec(x, y, z, w); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z, w); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec4d) + { + Vec4d vec = (Vec4d)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z && vec.getW() == w; + } + return false; + } + + @Override + public String toString() + { + return "Vec4d[x="+x+", y="+y+", z="+z+", w="+w+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec4dMutable.java b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec4dMutable.java new file mode 100644 index 0000000..c38cfd2 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vec4dMutable.java @@ -0,0 +1,128 @@ +package speiger.src.coreengine.math.vector.doubles; + +import java.util.Objects; + +public class Vec4dMutable implements Vec4d +{ + double x; + double y; + double z; + double w; + + public Vec4dMutable() + { + } + + public Vec4dMutable(double value) + { + x = value; + y = value; + z = value; + w = value; + } + + public Vec4dMutable(double x, double y, double z, double w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public double getX() + { + return x; + } + + @Override + public double getY() + { + return y; + } + + @Override + public double getZ() + { + return z; + } + + @Override + public double getW() + { + return w; + } + + @Override + public Vec4d setX(double x) + { + this.x = x; + return this; + } + + @Override + public Vec4d setY(double y) + { + this.y = y; + return this; + } + + @Override + public Vec4d setZ(double z) + { + this.z = z; + return this; + } + + @Override + public Vec4d setW(double w) + { + this.w = w; + return this; + } + + @Override + public Vec4d copy() + { + return Vec4d.newMutable(this); + } + + @Override + public Vec4d set(double x, double y, double z, double w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z, w); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec4d) + { + Vec4d vec = (Vec4d)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z && vec.getW() == w; + } + return false; + } + + @Override + public String toString() + { + return "Vec4d[x="+x+", y="+y+", z="+z+", w="+w+"]"; + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/math/vector/doubles/Vecd.java b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vecd.java new file mode 100644 index 0000000..25d9b86 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/doubles/Vecd.java @@ -0,0 +1,33 @@ +package speiger.src.coreengine.math.vector.doubles; + +import java.nio.DoubleBuffer; + +import speiger.src.coreengine.math.vector.Vec; +import speiger.src.coreengine.math.vector.bytes.Vecb; +import speiger.src.coreengine.math.vector.floats.Vecf; +import speiger.src.coreengine.math.vector.ints.Veci; +import speiger.src.coreengine.math.vector.longs.Vecl; +import speiger.src.coreengine.math.vector.shorts.Vecs; + +public interface Vecd extends Vec +{ + public Vecd set(double value); + public Vecd add(double value); + public Vecd sub(double value); + public Vecd multiply(double value); + public Vecd devide(double value); + public Vecd clamp(double min, double max); + public Vecd clamp(double min, double max, int filter); + public double lengthSquared(); + public default double length(){return Math.sqrt(lengthSquared());} + + public Vecd store(DoubleBuffer buffer); + public Vecd load(DoubleBuffer buffer); + public double[] asArray(); + + public Vecb asByte(); + public Vecs asShort(); + public Veci asInt(); + public Vecl asLong(); + public Vecf asFloat(); +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/floats/Vec2f.java b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec2f.java new file mode 100644 index 0000000..1d7e1b3 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec2f.java @@ -0,0 +1,187 @@ +package speiger.src.coreengine.math.vector.floats; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec2b; +import speiger.src.coreengine.math.vector.doubles.Vec2d; +import speiger.src.coreengine.math.vector.ints.Vec2i; +import speiger.src.coreengine.math.vector.longs.Vec2l; +import speiger.src.coreengine.math.vector.shorts.Vec2s; + +public interface Vec2f extends Vecf +{ + public static final Vec2f ZERO = newVec(); + public static final Vec2f MINUS_ONE = newVec(-1F); + public static final Vec2f ONE = newVec(1F); + + public static Vec2f newMutable(){return new Vec2fMutable();} + public static Vec2f newMutable(float value){return new Vec2fMutable(value);} + public static Vec2f newMutable(float x, float y){return new Vec2fMutable(x, y);} + public static Vec2f newMutable(Vec2f value){return newMutable(value.getX(), value.getY());} + + public static Vec2f newVec(){return new Vec2fImmutable();} + public static Vec2f newVec(float value){return new Vec2fImmutable(value);} + public static Vec2f newVec(float x, float y){return new Vec2fImmutable(x, y);} + public static Vec2f newVec(Vec2f value){return newVec(value.getX(), value.getY());} + + public float getX(); + public float getY(); + public Vec2f setX(float x); + public Vec2f setY(float y); + @Override + public default float[] asArray(){return new float[]{getX(), getY()};} + @Override + public Vec2f copy(); + @Override + public default Vec2f abs(){return set(Math.abs(getX()), Math.abs(getY()));} + @Override + public default Vec2f negate(){return set(0F, 0F);} + @Override + public default Vec2f invert(){return set(-getX(), -getY());}; + public default Vec2f normalize() + { + float l = length(); + return l == 0F ? this : multiply(1F / l); + } + + @Override + public default Vec2f add(float value) {return add(value, value);} + public default Vec2f add(Vec2f value) {return add(value.getX(), value.getY());} + public default Vec2f add(float x, float y) {return set(x + getX(), y + getY());} + + @Override + public default Vec2f sub(float value){return sub(value, value);} + public default Vec2f sub(Vec2f value){return sub(value.getX(), value.getY());} + public default Vec2f sub(float x, float y) {return set(getX() - x, getY() - y);} + + @Override + public default Vec2f multiply(float value){return multiply(value, value);} + public default Vec2f multiply(Vec2f value){return multiply(value.getX(), value.getY());} + public default Vec2f multiply(float x, float y) {return set(x * getX(), y * getY());} + + @Override + public default Vec2f devide(float value){return devide(value, value);} + public default Vec2f devide(Vec2f value){return devide(value.getX(), value.getY());} + public default Vec2f devide(float x, float y){return set(getX() / x, getY() / y);} + + @Override + public default Vec2f set(float value){return set(value, value);}; + public default Vec2f set(Vec2f value){return set(value.getX(), value.getY());} + public Vec2f set(float x, float y); + + public default double distanceTo(Vec2f value){return distanceTo(value.getX(), value.getY());} + public default double distanceTo(float x, float y){return Math.sqrt(distanceToSquared(x, y));} + + public default double distanceToSquared(Vec2f value){return distanceToSquared(value.getX(), value.getY());} + public default double distanceToSquared(float x, float y) + { + double xPos = getX() - x; + double yPos = getY() - y; + return (xPos * xPos) + (yPos * yPos); + } + @Override + public default float lengthSquared() {return (getX() * getX()) + (getY() * getY());} + + public default double dotProduct(Vec2f value){return dotProduct(value.getX(), value.getY());} + public default double dotProduct(float x, float y){return (getX() * x) + (getY() * y);} + + public default Vec2f lerp(Vec2f value, float progress, Vec2f result){return lerp(value.getX(), value.getY(), progress, result);} + public default Vec2f lerp(float x, float y, float progress, Vec2f result){return result.set(MathUtils.lerp(getX(), x, progress), MathUtils.lerp(getY(), y, progress));}; + + public default float angle(Vec2f value){return angle(value.getX(), value.getY());} + public default float angle(float x, float y){return (float)Math.atan2((getX() * y) - (getY() * x), (getX() * x) + (getY() * y));} + + public default float directionAngle(Vec2f value){return directionAngle(value.getX(), value.getY());} + public default float directionAngle(float x, float y){return (float)(-Math.toDegrees(Math.atan2(getX() - x, getY() - y)) % 360D);} + + public default Vec2f reflect(Vec2f value){return reflect(value.getX(), value.getY(), this);} + public default Vec2f reflect(float x, float y){return reflect(x, y, this);}; + public default Vec2f reflect(Vec2f value, Vec2f result){return reflect(value.getX(), value.getY(), result);} + public default Vec2f reflect(float x, float y, Vec2f result) + { + double dot = dotProduct(x, y); + float x2 = (float)((dot + dot) * x); + float y2 = (float)((dot + dot) * y); + return result.set(getX() - x2, getY() - y2); + } + + public default Vec2f rotate(float angle, Vec2f center){return rotate(angle, center.getX(), center.getY());} + public default Vec2f rotate(float angle, float x, float y) + { + float xPos = getX() - x; + float yPos = getY() - y; + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + return set((float)((xPos * cos) + (yPos * sin) + x), (float)(-(xPos * sin) + (yPos * cos) + y)); + } + + public default Vec2f min(Vec2f other) {return min(other, this);} + public default Vec2f min(Vec2f other, Vec2f result){return min(other.getX(), other.getY(), result);} + public default Vec2f min(float x, float y) {return min(x, y, this);} + public default Vec2f min(float x, float y, Vec2f result){return result.set(Math.min(getX(), x), Math.min(getY(), y));} + + public default Vec2f max(Vec2f other) {return max(other, this);} + public default Vec2f max(Vec2f other, Vec2f result){return max(other.getX(), other.getY(), result);} + public default Vec2f max(float x, float y) {return max(x, y, this);} + public default Vec2f max(float x, float y, Vec2f result){return result.set(Math.max(getX(), x), Math.max(getY(), y));} + + public default Vec2f difference(Vec2f other) {return difference(other, this);} + public default Vec2f difference(Vec2f other, Vec2f result){return difference(other.getX(), other.getY(), result);} + public default Vec2f difference(float x, float y) {return difference(x, y, this);} + public default Vec2f difference(float x, float y, Vec2f result){return result.set(getX() - x, getY() - y);} + + @Override + public default Vec2f clamp(float min, float max){return clamp(min, max, ALL);} + public default Vec2f clamp(float min, float max, Vec2f result){return clamp(min, max, result, ALL);} + @Override + public default Vec2f clamp(float min, float max, int filter){return clamp(min, max, this, filter);} + public default Vec2f clamp(float min, float max, Vec2f result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()));} + + @Override + public default Vec2f store(ByteBuffer buffer) + { + buffer.putFloat(getX()).putFloat(getY()); + return this; + } + + @Override + public default Vec2f load(ByteBuffer buffer) + { + return set(buffer.getFloat(), buffer.getFloat()); + } + + @Override + public default Vec2f store(FloatBuffer buffer) + { + buffer.put(getX()).put(getY()); + return this; + } + + @Override + public default Vec2f load(FloatBuffer buffer) + { + return set(buffer.get(), buffer.get()); + } + @Override + public default Vec2b asByte(){return isMutable() ? Vec2b.newMutable((byte)MathUtils.floor(getX()), (byte)MathUtils.floor(getY())) : Vec2b.newVec((byte)MathUtils.floor(getX()), (byte)MathUtils.floor(getY()));} + @Override + public default Vec2s asShort(){return isMutable() ? Vec2s.newMutable((short)MathUtils.floor(getX()), (short)MathUtils.floor(getY())) : Vec2s.newVec((short)MathUtils.floor(getX()), (short)MathUtils.floor(getY()));} + @Override + public default Vec2i asInt(){return isMutable() ? Vec2i.newMutable(MathUtils.floor(getX()), MathUtils.floor(getY())) : Vec2i.newVec(MathUtils.floor(getX()), MathUtils.floor(getY()));} + @Override + public default Vec2l asLong() {return isMutable() ? Vec2l.newMutable(MathUtils.floor(getX()), MathUtils.floor(getY())) : Vec2l.newVec(MathUtils.floor(getX()), MathUtils.floor(getY()));} + @Override + public default Vec2d asDouble(){return isMutable() ? Vec2d.newMutable(getX(), getY()) : Vec2d.newVec(getX(), getY());} + + + @Override + public default Vec2f asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec2f asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec2f copyAsMutable(){return newMutable(this);} + @Override + public default Vec2f copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/floats/Vec2fImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec2fImmutable.java new file mode 100644 index 0000000..92dd3e9 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec2fImmutable.java @@ -0,0 +1,92 @@ +package speiger.src.coreengine.math.vector.floats; + +import java.util.Objects; + +public class Vec2fImmutable implements Vec2f +{ + final float x; + final float y; + + public Vec2fImmutable() + { + x = 0F; + y = 0F; + } + + public Vec2fImmutable(float value) + { + x = value; + y = value; + } + + public Vec2fImmutable(float x, float y) + { + this.x = x; + this.y = y; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public float getX() + { + return x; + } + + @Override + public float getY() + { + return y; + } + + @Override + public Vec2f setX(float x) + { + return this.x == x ? this : Vec2f.newVec(x, y); + } + + @Override + public Vec2f setY(float y) + { + return this.y == y ? this : Vec2f.newVec(x, y); + } + + @Override + public Vec2f copy() + { + return Vec2f.newVec(x, y); + } + + @Override + public Vec2f set(float x, float y) + { + return this.x == x && this.y == y ? this : Vec2f.newVec(x, y); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec2f) + { + Vec2f vec = (Vec2f)obj; + return vec.getX() == x && vec.getY() == y; + } + return false; + } + + @Override + public String toString() + { + return "Vec2f[x="+x+", y="+y+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/floats/Vec2fMutable.java b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec2fMutable.java new file mode 100644 index 0000000..554a2a2 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec2fMutable.java @@ -0,0 +1,94 @@ +package speiger.src.coreengine.math.vector.floats; + +import java.util.Objects; + +public class Vec2fMutable implements Vec2f +{ + float x; + float y; + + public Vec2fMutable() + { + } + + public Vec2fMutable(float value) + { + x = value; + y = value; + } + + public Vec2fMutable(float x, float y) + { + this.x = x; + this.y = y; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public float getX() + { + return x; + } + + @Override + public float getY() + { + return y; + } + + @Override + public Vec2f setX(float x) + { + this.x = x; + return this; + } + + @Override + public Vec2f setY(float y) + { + this.y = y; + return this; + } + + @Override + public Vec2f copy() + { + return Vec2f.newMutable(this); + } + + @Override + public Vec2f set(float x, float y) + { + this.x = x; + this.y = y; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec2f) + { + Vec2f vec = (Vec2f)obj; + return vec.getX() == x && vec.getY() == y; + } + return false; + } + + @Override + public String toString() + { + return "Vec2f[x="+x+", y="+y+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/floats/Vec3f.java b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec3f.java new file mode 100644 index 0000000..19e96ae --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec3f.java @@ -0,0 +1,242 @@ +package speiger.src.coreengine.math.vector.floats; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec3b; +import speiger.src.coreengine.math.vector.doubles.Vec3d; +import speiger.src.coreengine.math.vector.ints.Vec3i; +import speiger.src.coreengine.math.vector.longs.Vec3l; +import speiger.src.coreengine.math.vector.shorts.Vec3s; + +public interface Vec3f extends Vecf +{ + public static final Vec3f ZERO = newVec(); + public static final Vec3f MINUS_ONE = newVec(-1F); + public static final Vec3f ONE = newVec(1F); + + public static Vec3f newMutable(){return new Vec3fMutable();} + public static Vec3f newMutable(float value){return new Vec3fMutable(value);} + public static Vec3f newMutable(float x, float y, float z){return new Vec3fMutable(x, y, z);} + public static Vec3f newMutable(Vec3f vec){return newMutable(vec.getX(), vec.getY(), vec.getZ());} + + public static Vec3f newVec(){return new Vec3fImmutable();} + public static Vec3f newVec(float value){return new Vec3fImmutable(value);} + public static Vec3f newVec(float x, float y, float z){return new Vec3fImmutable(x, y, z);} + public static Vec3f newVec(Vec3f vec){return newVec(vec.getX(), vec.getY(), vec.getZ());} + + public float getX(); + public float getY(); + public float getZ(); + public Vec3f setX(float x); + public Vec3f setY(float y); + public Vec3f setZ(float z); + @Override + public default float[] asArray(){return new float[]{getX(), getY(), getZ()};} + + @Override + public Vec3f copy(); + @Override + public default Vec3f abs(){return set(Math.abs(getX()), Math.abs(getY()), Math.abs(getZ()));} + @Override + public default Vec3f negate(){return set(0F, 0F, 0F);} + @Override + public default Vec3f invert(){return set(-getX(), -getY(), -getZ());} + public default Vec3f normalize() + { + float l = length(); + return l == 0F ? this : multiply(1.0F / l); + } + @Override + public default Vec3f add(float value){return add(value, value, value);} + public default Vec3f add(Vec3f value){return add(value.getX(), value.getY(), value.getZ());} + public default Vec3f add(float x, float y, float z){return set(getX() + x, getY() + y, getZ() + z);} + + @Override + public default Vec3f sub(float value){return sub(value, value, value);} + public default Vec3f sub(Vec3f value){return sub(value.getX(), value.getY(), value.getZ());} + public default Vec3f sub(float x, float y, float z){return set(getX() - x, getY() - y, getZ() - z);} + + @Override + public default Vec3f multiply(float value){return multiply(value, value, value);} + public default Vec3f multiply(Vec3f value){return multiply(value.getX(), value.getY(), value.getZ());} + public default Vec3f multiply(float x, float y, float z){return set(getX() * x, getY() * y, getZ() * z);} + + @Override + public default Vec3f devide(float value){return devide(value, value, value);} + public default Vec3f devide(Vec3f value){return devide(value.getX(), value.getY(), value.getZ());} + public default Vec3f devide(float x, float y, float z){return set(getX() / x, getY() / y, getZ() / z);} + + @Override + public default Vec3f set(float value){return set(value, value, value);} + public default Vec3f set(Vec3f value){return set(value.getX(), value.getY(), value.getZ());} + public Vec3f set(float x, float y, float z); + + public default double distanceTo(Vec3f value){return distanceTo(value.getX(), value.getY(), value.getZ());} + public default double distanceTo(float x, float y, float z){return Math.sqrt(distanceToSquared(x, y, z));} + + public default double distanceToSquared(Vec3f value){return distanceToSquared(value.getX(), value.getY(), value.getZ());} + public default double distanceToSquared(float x, float y, float z) + { + double xPos = getX() - x; + double yPos = getY() - y; + double zPos = getZ() - z; + return (xPos * xPos) + (yPos * yPos) + (zPos * zPos); + } + @Override + public default float lengthSquared() {return (getX() * getX()) + (getY() * getY()) + (getZ() * getZ());} + + public default double dotProduct(Vec3f value){return dotProduct(value.getX(), value.getY(), value.getZ());} + public default double dotProduct(float x, float y, float z){return (getX() * x) + (getY() * y) + (getZ() * z);} + + public default Vec3f lerp(Vec3f value, float progress, Vec3f result){return lerp(value.getX(), value.getY(), value.getZ(), progress, result);} + public default Vec3f lerp(float x, float y, float z, float progress, Vec3f result){return result.set(MathUtils.lerp(getX(), x, progress), MathUtils.lerp(getY(), y, progress), MathUtils.lerp(getZ(), z, progress));} + + public default float angle(Vec3f value){return angle(value.getX(), value.getY(), value.getZ());} + public default float angle(float x, float y, float z){return (float)Math.acos(MathUtils.clamp(-1, 1, angleCos(x, y, z)));} + + public default float angleCos(Vec3f value){return angleCos(value.getX(), value.getY(), value.getZ());} + public default float angleCos(float x, float y, float z) + { + double myLength = (getX() * getX()) + (getY() * getY()) + (getZ() * getZ()); + double otherLength = (x * x) + (y * y) + (z * z); + float dot = (getX() * x) + (getY() * y) + (getZ() * z); + return dot / (float)(Math.sqrt(myLength * otherLength)); + } + + public default Vec3f crossProduct(Vec3f value){return crossProduct(value.getX(), value.getY(), value.getZ(), this);} + public default Vec3f crossProduct(Vec3f value, Vec3f result){return crossProduct(value.getX(), value.getY(), value.getZ(), result);} + public default Vec3f crossProduct(float x, float y, float z){return crossProduct(x, y, z, this);} + public default Vec3f crossProduct(float x, float y, float z, Vec3f result){return result.set((getY() * z) - (getZ() * y), (getZ() * x) - (getX() * z), (getX() * y) - (getY() * x));} + + public default Vec3f reflect(Vec3f value){return reflect(value.getX(), value.getY(), value.getZ(), this);} + public default Vec3f reflect(Vec3f value, Vec3f result){return reflect(value.getX(), value.getY(), value.getZ(), result);} + public default Vec3f reflect(float x, float y, float z){return reflect(x, y, z, this);} + public default Vec3f reflect(float x, float y, float z, Vec3f result) + { + double dot = dotProduct(x, y, z) * 2D; + return result.set(getX() - (float)(dot * x), getY() - (float)(dot * y), getZ() - (float)(dot * z)); + } + + public default Vec3f rotate(float angle, Vec3f axis){return rotate(angle, axis.getX(), axis.getY(), axis.getZ());} + public default Vec3f rotate(float angle, Vec3f axis, Vec3f result){return rotate(angle, axis.getX(), axis.getY(), axis.getZ(), result);} + public default Vec3f rotate(float angle, float x, float y, float z){return rotate(angle, x, y, z, this);} + public default Vec3f rotate(float angle, float x, float y, float z, Vec3f result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double dot = dotProduct(x, y, z); + double xPos = x * dot * (1D - cos) + (getX() * cos) + (-z * getY() + y * getZ()) * sin; + double yPos = y * dot * (1D - cos) + (getY() * cos) + (z * getX() - x * getZ()) * sin; + double zPos = z * dot * (1D - cos) + (getZ() * cos) + (-y * getX() + x * getY()) * sin; + return result.set((float)xPos, (float)yPos, (float)zPos); + } + public default Vec3f rotateX(float angle){return rotateX(angle, this);} + public default Vec3f rotateX(float angle, Vec3f result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double y = getY() * cos + getZ() * sin; + double z = getZ() * cos - getY() * sin; + return result.set(getX(), (float)y, (float)z); + } + public default Vec3f rotateY(float angle){return rotateY(angle, this);} + public default Vec3f rotateY(float angle, Vec3f result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double x = getX() * cos + getZ() * sin; + double z = getZ() * cos - getX() * sin; + return result.set((float)x, getY(), (float)z); + } + public default Vec3f rotateZ(float angle){return rotateZ(angle, this);} + public default Vec3f rotateZ(float angle, Vec3f result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double x = cos * getX() - sin * getY(); + double y = sin * getX() + cos * getY(); + return result.set((float)x, (float)y, getZ()); + } + + public default Vec3f smoothStep(Vec3f value, float progress, Vec3f result){return smoothStep(value.getX(), value.getY(), value.getZ(), progress, result);} + public default Vec3f smoothStep(float x, float y, float z, float progress, Vec3f result) + { + float t2 = progress * progress; + float t3 = t2 * progress; + float xPos = ((getX() + getX() - x - x) * t3 + (3.0F * x - 3.0F * getX()) * t2 + getX() * progress + getX()); + float yPos = ((getY() + getY() - y - y) * t3 + (3.0F * y - 3.0F * getY()) * t2 + getY() * progress + getY()); + float zPos = ((getZ() + getZ() - z - z) * t3 + (3.0F * z - 3.0F * getZ()) * t2 + getZ() * progress + getZ()); + return result.set(xPos, yPos, zPos); + } + + public default Vec3f min(Vec3f other) {return min(other, this);} + public default Vec3f min(Vec3f other, Vec3f result){return min(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3f min(float x, float y, float z) {return min(x, y, z, this);} + public default Vec3f min(float x, float y, float z, Vec3f result){return result.set(Math.min(getX(), x), Math.min(getY(), y), Math.min(getZ(), z));} + + public default Vec3f max(Vec3f other) {return max(other, this);} + public default Vec3f max(Vec3f other, Vec3f result){return max(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3f max(float x, float y, float z) {return max(x, y, z, this);} + public default Vec3f max(float x, float y, float z, Vec3f result){return result.set(Math.max(getX(), x), Math.max(getY(), y), Math.max(getZ(), z));} + + public default Vec3f difference(Vec3f other) {return difference(other, this);} + public default Vec3f difference(Vec3f other, Vec3f result){return difference(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3f difference(float x, float y, float z) {return difference(x, y, z, this);} + public default Vec3f difference(float x, float y, float z, Vec3f result){return result.set(getX() - x, getY() - y, getZ() - z);} + + @Override + public default Vec3f clamp(float min, float max){return clamp(min, max, ALL);} + public default Vec3f clamp(float min, float max, Vec3f result){return clamp(min, max, result, ALL);} + @Override + public default Vec3f clamp(float min, float max, int filter){return clamp(min, max, this, filter);} + public default Vec3f clamp(float min, float max, Vec3f result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()), (filter & Z) == 0 ? getZ() : MathUtils.clamp(min, max, getZ()));} + + @Override + public default Vec3f store(ByteBuffer buffer) + { + buffer.putFloat(getX()).putFloat(getY()).putFloat(getZ()); + return this; + } + + @Override + public default Vec3f load(ByteBuffer buffer) + { + return set(buffer.getFloat(), buffer.getFloat(), buffer.getFloat()); + } + + @Override + public default Vec3f store(FloatBuffer buffer) + { + buffer.put(getX()).put(getY()).put(getZ()); + return this; + } + + @Override + public default Vec3f load(FloatBuffer buffer) + { + return set(buffer.get(), buffer.get(), buffer.get()); + } + + @Override + public default Vec3b asByte(){return isMutable() ? Vec3b.newMutable((byte)MathUtils.floor(getX()), (byte)MathUtils.floor(getY()), (byte)MathUtils.floor(getZ())) : Vec3b.newVec((byte)MathUtils.floor(getX()), (byte)MathUtils.floor(getY()), (byte)MathUtils.floor(getZ()));} + @Override + public default Vec3s asShort(){return isMutable() ? Vec3s.newMutable((short)MathUtils.floor(getX()), (short)MathUtils.floor(getY()), (short)MathUtils.floor(getZ())) : Vec3s.newVec((short)MathUtils.floor(getX()), (short)MathUtils.floor(getY()), (short)MathUtils.floor(getZ()));} + @Override + public default Vec3i asInt(){return isMutable() ? Vec3i.newMutable(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ())) : Vec3i.newVec(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ()));} + @Override + public default Vec3l asLong() {return isMutable() ? Vec3l.newMutable(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ())) : Vec3l.newVec(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ()));} + @Override + public default Vec3d asDouble(){return isMutable() ? Vec3d.newMutable(getX(), getY(), getZ()) : Vec3d.newVec(getX(), getY(), getZ());} + + + @Override + public default Vec3f asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec3f asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec3f copyAsMutable(){return newMutable(this);} + @Override + public default Vec3f copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/floats/Vec3fImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec3fImmutable.java new file mode 100644 index 0000000..e0886ad --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec3fImmutable.java @@ -0,0 +1,108 @@ +package speiger.src.coreengine.math.vector.floats; + +import java.util.Objects; + +public class Vec3fImmutable implements Vec3f +{ + final float x; + final float y; + final float z; + + public Vec3fImmutable() + { + x = 0F; + y = 0F; + z = 0F; + } + + public Vec3fImmutable(float value) + { + x = value; + y = value; + z = value; + } + + public Vec3fImmutable(float x, float y, float z) + { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public float getX() + { + return x; + } + + @Override + public float getY() + { + return y; + } + + @Override + public float getZ() + { + return z; + } + + @Override + public Vec3f setX(float x) + { + return this.x == x ? this : Vec3f.newVec(x, y, z); + } + + @Override + public Vec3f setY(float y) + { + return this.y == y ? this : Vec3f.newVec(x, y, z); + } + + @Override + public Vec3f setZ(float z) + { + return this.z == z ? this : Vec3f.newVec(x, y, z); + } + + @Override + public Vec3f copy() + { + return Vec3f.newVec(this); + } + + @Override + public Vec3f set(float x, float y, float z) + { + return this.x == x && this.y == y && this.z == z ? this : Vec3f.newVec(x, y, z); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec3f) + { + Vec3f vec = (Vec3f)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z; + } + return false; + } + + @Override + public String toString() + { + return "Vec3f[x="+x+", y="+y+", z="+z+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/floats/Vec3fMutable.java b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec3fMutable.java new file mode 100644 index 0000000..522a2da --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec3fMutable.java @@ -0,0 +1,111 @@ +package speiger.src.coreengine.math.vector.floats; + +import java.util.Objects; + +public class Vec3fMutable implements Vec3f +{ + float x; + float y; + float z; + + public Vec3fMutable() + { + } + + public Vec3fMutable(float value) + { + x = value; + y = value; + z = value; + } + + public Vec3fMutable(float x, float y, float z) + { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public float getX() + { + return x; + } + + @Override + public float getY() + { + return y; + } + + @Override + public float getZ() + { + return z; + } + + @Override + public Vec3f setX(float x) + { + this.x = x; + return this; + } + + @Override + public Vec3f setY(float y) + { + this.y = y; + return this; + } + + @Override + public Vec3f setZ(float z) + { + this.z = z; + return this; + } + + @Override + public Vec3f copy() + { + return Vec3f.newMutable(this); + } + + @Override + public Vec3f set(float x, float y, float z) + { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec3f) + { + Vec3f vec = (Vec3f)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z; + } + return false; + } + + @Override + public String toString() + { + return "Vec3f[x="+x+", y="+y+", z="+z+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/floats/Vec4f.java b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec4f.java new file mode 100644 index 0000000..4086764 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec4f.java @@ -0,0 +1,232 @@ +package speiger.src.coreengine.math.vector.floats; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec4b; +import speiger.src.coreengine.math.vector.doubles.Vec4d; +import speiger.src.coreengine.math.vector.ints.Vec4i; +import speiger.src.coreengine.math.vector.longs.Vec4l; +import speiger.src.coreengine.math.vector.shorts.Vec4s; + +public interface Vec4f extends Vecf +{ + public static final Vec4f ZERO = newVec(); + public static final Vec4f MINUS_ONE = newVec(-1F); + public static final Vec4f ONE = newVec(1F); + + public static Vec4f newMutable(){return new Vec4fMutable();} + public static Vec4f newMutable(float value){return new Vec4fMutable(value);} + public static Vec4f newMutable(float x, float y, float z, float w){return new Vec4fMutable(x, y, z, w);} + public static Vec4f newMutable(Vec4f vec){return newMutable(vec.getX(), vec.getY(), vec.getZ(), vec.getW());} + + public static Vec4f newVec(){return new Vec4fImmutable();} + public static Vec4f newVec(float value){return new Vec4fImmutable(value);} + public static Vec4f newVec(float x, float y, float z, float w){return new Vec4fImmutable(x, y, z, w);} + public static Vec4f newVec(Vec4f vec){return newVec(vec.getX(), vec.getY(), vec.getZ(), vec.getW());} + + public float getX(); + public float getY(); + public float getZ(); + public float getW(); + public Vec4f setX(float x); + public Vec4f setY(float y); + public Vec4f setZ(float z); + public Vec4f setW(float w); + @Override + public default float[] asArray(){return new float[]{getX(), getY(), getZ(), getW()};} + @Override + public Vec4f copy(); + @Override + public default Vec4f abs(){return set(Math.abs(getX()), Math.abs(getY()), Math.abs(getZ()), Math.abs(getW()));} + @Override + public default Vec4f negate(){return set(0F, 0F, 0F, 0F);} + @Override + public default Vec4f invert(){return set(-getX(), -getY(), -getZ(), -getW());} + public default Vec4f normalize() + { + float l = length(); + return l == 0F ? this : multiply(1.0F / l); + } + + public default Vec4f normalize3D() + { + float value = (getX() * getX()) + (getY() * getY()) + (getZ() * getZ()); + return value == 0F ? this : multiply(1F / value); + } + + @Override + public default Vec4f add(float value){return add(value, value, value, value);} + public default Vec4f add(Vec4f value){return add(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4f add(float x, float y, float z, float w){return set(getX() + x, getY() + y, getZ() + z, getW() + w);} + + @Override + public default Vec4f sub(float value){return sub(value, value, value, value);} + public default Vec4f sub(Vec4f value){return sub(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4f sub(float x, float y, float z, float w){return set(getX() - x, getY() - y, getZ() - z, getW() - w);} + + @Override + public default Vec4f multiply(float value){return multiply(value, value, value, value);} + public default Vec4f multiply(Vec4f value){return multiply(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4f multiply(float x, float y, float z, float w){return set(getX() * x, getY() * y, getZ() * z, getW() * w);} + + @Override + public default Vec4f devide(float value){return devide(value, value, value, value);} + public default Vec4f devide(Vec4f value){return devide(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4f devide(float x, float y, float z, float w){return set(getX() / x, getY() / y, getZ() / z, getW() / w);} + + @Override + public default Vec4f set(float value){return set(value, value, value, value);} + public default Vec4f set(Vec4f value){return set(value.getX(), value.getY(), value.getZ(), value.getW());} + public Vec4f set(float x, float y, float z, float w); + + public default double distanceTo(Vec4f value){return distanceTo(value.getX(), value.getY(), value.getZ(), value.getW());} + public default double distanceTo(float x, float y, float z, float w){return Math.sqrt(distanceTo(x, y, z, w));} + + public default double distanceToSquared(Vec4f value){return distanceToSquared(value.getX(), value.getY(), value.getZ(), value.getW());} + public default double distanceToSquared(float x, float y, float z, float w) + { + double xPos = getX() - x; + double yPos = getY() - y; + double zPos = getZ() - z; + double wPos = getW() - w; + return (xPos * xPos) + (yPos * yPos) + (zPos * zPos) + (wPos * wPos); + } + @Override + public default float lengthSquared() {return (getX() * getX()) + (getY() * getY()) + (getZ() * getZ()) + (getW() * getW());} + + public default double dotProduct(Vec4f value){return dotProduct(value.getX(), value.getY(), value.getZ(), value.getW());} + public default double dotProduct(float x, float y, float z, float w){return (getX() * x) + (getY() * y) + (getZ() * z) + (getW() * w);}; + + public default Vec4f lerp(Vec4f value, float progress, Vec4f result){return lerp(value.getX(), value.getY(), value.getZ(), value.getW(), progress, result);} + public default Vec4f lerp(float x, float y, float z, float w, float progress, Vec4f result){return result.set(MathUtils.lerp(getX(), x, progress), MathUtils.lerp(getY(), y, progress), MathUtils.lerp(getZ(), z, progress), MathUtils.lerp(getW(), w, progress));} + + public default float angle(Vec4f value){return angle(value.getX(), value.getY(), value.getZ(), value.getW());} + public default float angle(float x, float y, float z, float w){return (float)Math.acos(MathUtils.clamp(-1F, 1F, angleCos(x, y, z, w)));} + + public default float angleCos(Vec4f value){return angleCos(value.getX(), value.getY(), value.getZ(), value.getW());} + public default float angleCos(float x, float y, float z, float w){return (float)(dotProduct(x, y, z, w) / Math.sqrt(lengthSquared() * (x * x) + (y * y) + (z * z) + (w * w)));} + + public default Vec4f rotate(float angle, Vec4f axis){return rotate(angle, axis.getX(), axis.getY(), axis.getZ(), axis.getW());} + public default Vec4f rotate(float angle, Vec4f axis, Vec4f result){return rotate(angle, axis.getX(), axis.getY(), axis.getZ(), axis.getW(), result);} + public default Vec4f rotate(float angle, float x, float y, float z, float w){return rotate(angle, x, y, z, w, this);} + public default Vec4f rotate(float angle, float x, float y, float z, float w, Vec4f result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double dot = dotProduct(x, y, z, w); + double xPos = x * dot * (1D - cos) + (getX() * cos) + (-z * getY() + y * getZ()) * sin; + double yPos = y * dot * (1D - cos) + (getY() * cos) + (z * getX() - x * getZ()) * sin; + double zPos = z * dot * (1D - cos) + (getZ() * cos) + (-y * getX() + x * getY()) * sin; + return result.set((float)xPos, (float)yPos, (float)zPos, getW()); + } + public default Vec4f rotateX(float angle){return rotateX(angle, this);} + public default Vec4f rotateX(float angle, Vec4f result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double y = getY() * cos + getZ() * sin; + double z = getZ() * cos - getY() * sin; + return result.set(getX(), (float)y, (float)z, getW()); + } + public default Vec4f rotateY(float angle){return rotateY(angle, this);} + public default Vec4f rotateY(float angle, Vec4f result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double x = getX() * cos + getZ() * sin; + double z = getZ() * cos - getX() * sin; + return result.set((float)x, getY(), (float)z, getW()); + } + public default Vec4f rotateZ(float angle){return rotateZ(angle, this);} + public default Vec4f rotateZ(float angle, Vec4f result) + { + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + double x = cos * getX() - sin * getY(); + double y = sin * getX() + cos * getY(); + return result.set((float)x, (float)y, getZ(), getW()); + } + + public default Vec4f smoothStep(Vec4f value, float progress, Vec4f result){return smoothStep(value.getX(), value.getY(), value.getZ(), value.getW(), progress, result);} + public default Vec4f smoothStep(float x, float y, float z, float w, float progress, Vec4f result) + { + float t2 = progress * progress; + float t3 = t2 * progress; + float xPos = ((getX() + getX() - x - x) * t3 + (3.0F * x - 3.0F * getX()) * t2 + getX() * progress + getX()); + float yPos = ((getY() + getY() - y - y) * t3 + (3.0F * y - 3.0F * getY()) * t2 + getY() * progress + getY()); + float zPos = ((getZ() + getZ() - z - z) * t3 + (3.0F * z - 3.0F * getZ()) * t2 + getZ() * progress + getZ()); + float wPos = ((getW() + getW() - w - w) * t3 + (3.0F * w - 3.0F * getW()) * t2 + getW() * progress + getW()); + return result.set(xPos, yPos, zPos, wPos); + } + + public default Vec4f min(Vec4f other) {return min(other, this);} + public default Vec4f min(Vec4f other, Vec4f result){return min(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4f min(float x, float y, float z, float w) {return min(x, y, z, w, this);} + public default Vec4f min(float x, float y, float z, float w, Vec4f result){return result.set(Math.min(getX(), x), Math.min(getY(), y), Math.min(getZ(), z), Math.min(getW(), w));} + + public default Vec4f max(Vec4f other) {return max(other, this);} + public default Vec4f max(Vec4f other, Vec4f result){return max(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4f max(float x, float y, float z, float w) {return max(x, y, z, w, this);} + public default Vec4f max(float x, float y, float z, float w, Vec4f result){return result.set(Math.max(getX(), x), Math.max(getY(), y), Math.max(getZ(), z), Math.max(getZ(), z));} + + public default Vec4f difference(Vec4f other) {return difference(other, this);} + public default Vec4f difference(Vec4f other, Vec4f result){return difference(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4f difference(float x, float y, float z, float w) {return difference(x, y, z, w, this);} + public default Vec4f difference(float x, float y, float z, float w, Vec4f result){return result.set(getX() - x, getY() - y, getZ() - z, getW() - w);} + + @Override + public default Vec4f clamp(float min, float max){return clamp(min, max, ALL);} + public default Vec4f clamp(float min, float max, Vec4f result){return clamp(min, max, result, ALL);} + @Override + public default Vec4f clamp(float min, float max, int filter){return clamp(min, max, this, filter);} + public default Vec4f clamp(float min, float max, Vec4f result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()), (filter & Z) == 0 ? getZ() : MathUtils.clamp(min, max, getZ()), (filter & W) == 0 ? getW() : MathUtils.clamp(min, max, getW()));} + + @Override + public default Vec4f store(ByteBuffer buffer) + { + buffer.putFloat(getX()).putFloat(getY()).putFloat(getZ()).putFloat(getW()); + return this; + } + + @Override + public default Vec4f load(ByteBuffer buffer) + { + return set(buffer.getFloat(), buffer.getFloat(), buffer.getFloat(), buffer.getFloat()); + } + + @Override + public default Vec4f store(FloatBuffer buffer) + { + buffer.put(getX()).put(getY()).put(getZ()).put(getW()); + return this; + } + + @Override + public default Vec4f load(FloatBuffer buffer) + { + return set(buffer.get(), buffer.get(), buffer.get(), buffer.get()); + } + + @Override + public default Vec4b asByte(){return isMutable() ? Vec4b.newMutable((byte)MathUtils.floor(getX()), (byte)MathUtils.floor(getY()), (byte)MathUtils.floor(getZ()), (byte)MathUtils.floor(getW())) : Vec4b.newVec((byte)MathUtils.floor(getX()), (byte)MathUtils.floor(getY()), (byte)MathUtils.floor(getZ()), (byte)MathUtils.floor(getW()));} + @Override + public default Vec4s asShort(){return isMutable() ? Vec4s.newMutable((short)MathUtils.floor(getX()), (short)MathUtils.floor(getY()), (short)MathUtils.floor(getZ()), (short)MathUtils.floor(getW())) : Vec4s.newVec((short)MathUtils.floor(getX()), (short)MathUtils.floor(getY()), (short)MathUtils.floor(getZ()), (short)MathUtils.floor(getW()));} + @Override + public default Vec4i asInt(){return isMutable() ? Vec4i.newMutable(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ()), MathUtils.floor(getW())) : Vec4i.newVec(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ()), MathUtils.floor(getW()));} + @Override + public default Vec4l asLong() {return isMutable() ? Vec4l.newMutable(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ()), MathUtils.floor(getW())) : Vec4l.newVec(MathUtils.floor(getX()), MathUtils.floor(getY()), MathUtils.floor(getZ()), MathUtils.floor(getW()));} + @Override + public default Vec4d asDouble(){return isMutable() ? Vec4d.newMutable(getX(), getY(), getZ(), getW()) : Vec4d.newVec(getX(), getY(), getZ(), getW());} + + + @Override + public default Vec4f asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec4f asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec4f copyAsMutable(){return newMutable(this);} + @Override + public default Vec4f copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/floats/Vec4fImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec4fImmutable.java new file mode 100644 index 0000000..e272831 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec4fImmutable.java @@ -0,0 +1,124 @@ +package speiger.src.coreengine.math.vector.floats; + +import java.util.Objects; + +public class Vec4fImmutable implements Vec4f +{ + final float x; + final float y; + final float z; + final float w; + + public Vec4fImmutable() + { + x = 0; + y = 0; + z = 0; + w = 0; + } + + public Vec4fImmutable(float value) + { + x = value; + y = value; + z = value; + w = value; + } + + public Vec4fImmutable(float x, float y, float z, float w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public float getX() + { + return x; + } + + @Override + public float getY() + { + return y; + } + + @Override + public float getZ() + { + return z; + } + + @Override + public float getW() + { + return w; + } + + @Override + public Vec4f setX(float x) + { + return this.x == x ? this : Vec4f.newVec(x, y, z, w); + } + + @Override + public Vec4f setY(float y) + { + return this.y == y ? this : Vec4f.newVec(x, y, z, w); + } + + @Override + public Vec4f setZ(float z) + { + return this.z == z ? this : Vec4f.newVec(x, y, z, w); + } + + @Override + public Vec4f setW(float w) + { + return this.w == w ? this : Vec4f.newVec(x, y, z, w); + } + + @Override + public Vec4f copy() + { + return Vec4f.newVec(this); + } + + @Override + public Vec4f set(float x, float y, float z, float w) + { + return this.x == x && this.y == y && this.z == z && this.w == w ? this : Vec4f.newVec(x, y, z, w); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z, w); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec4f) + { + Vec4f vec = (Vec4f)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z && vec.getW() == w; + } + return false; + } + + @Override + public String toString() + { + return "Vec4f[x="+x+", y="+y+", z="+z+", w="+w+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/floats/Vec4fMutable.java b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec4fMutable.java new file mode 100644 index 0000000..e1b5215 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/floats/Vec4fMutable.java @@ -0,0 +1,132 @@ +package speiger.src.coreengine.math.vector.floats; + +import java.util.Objects; + +public class Vec4fMutable implements Vec4f +{ + float x; + float y; + float z; + float w; + + public Vec4fMutable() + { + x = 0; + y = 0; + z = 0; + w = 0; + } + + public Vec4fMutable(float value) + { + x = value; + y = value; + z = value; + w = value; + } + + public Vec4fMutable(float x, float y, float z, float w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public float getX() + { + return x; + } + + @Override + public float getY() + { + return y; + } + + @Override + public float getZ() + { + return z; + } + + @Override + public float getW() + { + return w; + } + + @Override + public Vec4f setX(float x) + { + this.x = x; + return this; + } + + @Override + public Vec4f setY(float y) + { + this.y = y; + return this; + } + + @Override + public Vec4f setZ(float z) + { + this.z = z; + return this; + } + + @Override + public Vec4f setW(float w) + { + this.w = w; + return this; + } + + @Override + public Vec4f copy() + { + return Vec4f.newMutable(this); + } + + @Override + public Vec4f set(float x, float y, float z, float w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z, w); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec4f) + { + Vec4f vec = (Vec4f)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z && vec.getW() == w; + } + return false; + } + + @Override + public String toString() + { + return "Vec4f[x="+x+", y="+y+", z="+z+", w="+w+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/floats/Vecf.java b/src/main/java/speiger/src/coreengine/math/vector/floats/Vecf.java new file mode 100644 index 0000000..3c9c329 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/floats/Vecf.java @@ -0,0 +1,34 @@ +package speiger.src.coreengine.math.vector.floats; + +import java.nio.FloatBuffer; + +import speiger.src.coreengine.math.vector.Vec; +import speiger.src.coreengine.math.vector.bytes.Vecb; +import speiger.src.coreengine.math.vector.doubles.Vecd; +import speiger.src.coreengine.math.vector.ints.Veci; +import speiger.src.coreengine.math.vector.longs.Vecl; +import speiger.src.coreengine.math.vector.shorts.Vecs; + +public interface Vecf extends Vec +{ + public Vecf set(float value); + public Vecf add(float value); + public Vecf sub(float value); + public Vecf multiply(float value); + public Vecf devide(float value); + public Vecf clamp(float min, float max); + public Vecf clamp(float min, float max, int filter); + + public float lengthSquared(); + public default float length(){return (float)Math.sqrt(lengthSquared());} + + public Vecf store(FloatBuffer buffer); + public Vecf load(FloatBuffer buffer); + public float[] asArray(); + + public Vecb asByte(); + public Vecs asShort(); + public Veci asInt(); + public Vecl asLong(); + public Vecd asDouble(); +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/ints/Vec2i.java b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec2i.java new file mode 100644 index 0000000..491bcb0 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec2i.java @@ -0,0 +1,163 @@ +package speiger.src.coreengine.math.vector.ints; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec2b; +import speiger.src.coreengine.math.vector.doubles.Vec2d; +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.math.vector.longs.Vec2l; +import speiger.src.coreengine.math.vector.shorts.Vec2s; + +public interface Vec2i extends Veci +{ + public static final Vec2i ZERO = newVec(); + public static final Vec2i MINUS_ONE = newVec(-1); + public static final Vec2i ONE = newVec(1); + + public static Vec2i newMutable(){return new Vec2iMutable();} + public static Vec2i newMutable(int value){return new Vec2iMutable(value);} + public static Vec2i newMutable(int x, int y){return new Vec2iMutable(x, y);} + public static Vec2i newMutable(Vec2i value){return newMutable(value.getX(), value.getY());} + + public static Vec2i newVec(){return new Vec2iImmutable();} + public static Vec2i newVec(int value){return new Vec2iImmutable(value);} + public static Vec2i newVec(int x, int y){return new Vec2iImmutable(x, y);} + public static Vec2i newVec(Vec2i value){return newVec(value.getX(), value.getY());} + + public int getX(); + public int getY(); + public Vec2i setX(int x); + public Vec2i setY(int y); + @Override + public default int[] asArray(){return new int[]{getX(), getY()};} + @Override + public Vec2i copy(); + @Override + public default Vec2i abs(){return set(Math.abs(getX()), Math.abs(getY()));} + @Override + public default Vec2i negate(){return set(0, 0);} + @Override + public default Vec2i invert(){return set(-getX(), -getY());}; + + @Override + public default Vec2i add(int value) {return add(value, value);} + public default Vec2i add(Vec2i value) {return add(value.getX(), value.getY());} + public default Vec2i add(int x, int y) {return set(x + getX(), y + getY());} + + @Override + public default Vec2i sub(int value){return sub(value, value);} + public default Vec2i sub(Vec2i value){return sub(value.getX(), value.getY());} + public default Vec2i sub(int x, int y) {return set(getX() - x, getY() - y);} + + @Override + public default Vec2i multiply(int value){return multiply(value, value);} + public default Vec2i multiply(Vec2i value){return multiply(value.getX(), value.getY());} + public default Vec2i multiply(int x, int y) {return set(x * getX(), y * getY());} + + @Override + public default Vec2i devide(int value){return devide(value, value);} + public default Vec2i devide(Vec2i value){return devide(value.getX(), value.getY());} + public default Vec2i devide(int x, int y){return set(getX() / x, getY() / y);} + + @Override + public default Vec2i set(int value){return set(value, value);}; + public default Vec2i set(Vec2i value){return set(value.getX(), value.getY());} + public Vec2i set(int x, int y); + + public default double distanceTo(Vec2i value){return distanceTo(value.getX(), value.getY());} + public default double distanceTo(int x, int y){return Math.sqrt(distanceToSquared(x, y));} + + public default long distanceToSquared(Vec2i value){return distanceToSquared(value.getX(), value.getY());} + public default long distanceToSquared(int x, int y) + { + long xPos = getX() - x; + long yPos = getY() - y; + return (xPos * xPos) + (yPos * yPos); + } + @Override + public default long lengthSquared() {return (getX() * getX()) + (getY() * getY());} + + public default long dotProduct(Vec2i value){return dotProduct(value.getX(), value.getY());} + public default long dotProduct(int x, int y){return (getX() * x) + (getY() * y);} + + public default Vec2i rotate(int angle, Vec2i center){return rotate(angle, center.getX(), center.getY());} + public default Vec2i rotate(int angle, int x, int y) + { + int xPos = getX() - x; + int yPos = getY() - y; + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + return set((int)((xPos * cos) + (yPos * sin) + x), (int)(-(xPos * sin) + (yPos * cos) + y)); + } + + public default Vec2i min(Vec2i other) {return min(other, this);} + public default Vec2i min(Vec2i other, Vec2i result){return min(other.getX(), other.getY(), result);} + public default Vec2i min(int x, int y) {return min(x, y, this);} + public default Vec2i min(int x, int y, Vec2i result){return result.set(Math.min(getX(), x), Math.min(getY(), y));} + + public default Vec2i max(Vec2i other) {return max(other, this);} + public default Vec2i max(Vec2i other, Vec2i result){return max(other.getX(), other.getY(), result);} + public default Vec2i max(int x, int y) {return max(x, y, this);} + public default Vec2i max(int x, int y, Vec2i result){return result.set(Math.max(getX(), x), Math.max(getY(), y));} + + public default Vec2i difference(Vec2i other) {return difference(other, this);} + public default Vec2i difference(Vec2i other, Vec2i result){return difference(other.getX(), other.getY(), result);} + public default Vec2i difference(int x, int y) {return difference(x, y, this);} + public default Vec2i difference(int x, int y, Vec2i result){return result.set(getX() - x, getY() - y);} + + @Override + public default Vec2i clamp(int min, int max){return clamp(min, max, ALL);} + public default Vec2i clamp(int min, int max, Vec2i result){return clamp(min, max, result, ALL);} + @Override + public default Vec2i clamp(int min, int max, int filter){return clamp(min, max, this, filter);} + public default Vec2i clamp(int min, int max, Vec2i result, int filter){return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()));} + + @Override + public default Vec2i store(ByteBuffer buffer) + { + buffer.putInt(getX()).putInt(getY()); + return this; + } + + @Override + public default Vec2i load(ByteBuffer buffer) + { + return set(buffer.getInt(), buffer.getInt()); + } + + @Override + public default Vec2i store(IntBuffer buffer) + { + buffer.put(getX()).put(getY()); + return this; + } + + @Override + public default Vec2i load(IntBuffer buffer) + { + return set(buffer.get(), buffer.get()); + } + + @Override + public default Vec2b asByte(){return isMutable() ? Vec2b.newMutable((byte)getX(), (byte)getY()) : Vec2b.newVec((byte)getX(), (byte)getY());} + @Override + public default Vec2s asShort(){return isMutable() ? Vec2s.newMutable((short)getX(), (short)getY()) : Vec2s.newVec((short)getX(), (short)getY());} + @Override + public default Vec2l asLong(){return isMutable() ? Vec2l.newMutable(getX(), getY()) : Vec2l.newVec(getX(), getY());} + @Override + public default Vec2f asFloat() {return isMutable() ? Vec2f.newMutable(getX(), getY()) : Vec2f.newVec(getX(), getY());} + @Override + public default Vec2d asDouble(){return isMutable() ? Vec2d.newMutable(getX(), getY()) : Vec2d.newVec(getX(), getY());} + + + @Override + public default Vec2i asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec2i asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec2i copyAsMutable(){return newMutable(this);} + @Override + public default Vec2i copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/ints/Vec2iImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec2iImmutable.java new file mode 100644 index 0000000..1435978 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec2iImmutable.java @@ -0,0 +1,92 @@ +package speiger.src.coreengine.math.vector.ints; + +import java.util.Objects; + +public class Vec2iImmutable implements Vec2i +{ + final int x; + final int y; + + public Vec2iImmutable() + { + x = 0; + y = 0; + } + + public Vec2iImmutable(int value) + { + x = value; + y = value; + } + + public Vec2iImmutable(int x, int y) + { + this.x = x; + this.y = y; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public int getX() + { + return x; + } + + @Override + public int getY() + { + return y; + } + + @Override + public Vec2i setX(int x) + { + return this.x == x ? this : Vec2i.newVec(x, y); + } + + @Override + public Vec2i setY(int y) + { + return this.y == y ? this : Vec2i.newVec(x, y); + } + + @Override + public Vec2i copy() + { + return Vec2i.newVec(this); + } + + @Override + public Vec2i set(int x, int y) + { + return this.x == x && this.y == y ? this : Vec2i.newVec(x, y); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec2i) + { + Vec2i vec = (Vec2i)obj; + return vec.getX() == x && vec.getY() == y; + } + return false; + } + + @Override + public String toString() + { + return "Vec2i[x="+x+", y="+y+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/ints/Vec2iMutable.java b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec2iMutable.java new file mode 100644 index 0000000..d21e5f7 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec2iMutable.java @@ -0,0 +1,94 @@ +package speiger.src.coreengine.math.vector.ints; + +import java.util.Objects; + +public class Vec2iMutable implements Vec2i +{ + int x; + int y; + + public Vec2iMutable() + { + } + + public Vec2iMutable(int value) + { + x = value; + y = value; + } + + public Vec2iMutable(int x, int y) + { + this.x = x; + this.y = y; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public int getX() + { + return x; + } + + @Override + public int getY() + { + return y; + } + + @Override + public Vec2i setX(int x) + { + this.x = x; + return this; + } + + @Override + public Vec2i setY(int y) + { + this.y = y; + return this; + } + + @Override + public Vec2i copy() + { + return Vec2i.newMutable(this); + } + + @Override + public Vec2i set(int x, int y) + { + this.x = x; + this.y = y; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec2i) + { + Vec2i vec = (Vec2i)obj; + return vec.getX() == x && vec.getY() == y; + } + return false; + } + + @Override + public String toString() + { + return "Vec2i[x="+x+", y="+y+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/ints/Vec3i.java b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec3i.java new file mode 100644 index 0000000..4b8a3cf --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec3i.java @@ -0,0 +1,157 @@ +package speiger.src.coreengine.math.vector.ints; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec3b; +import speiger.src.coreengine.math.vector.doubles.Vec3d; +import speiger.src.coreengine.math.vector.floats.Vec3f; +import speiger.src.coreengine.math.vector.longs.Vec3l; +import speiger.src.coreengine.math.vector.shorts.Vec3s; + + +public interface Vec3i extends Veci +{ + public static final Vec3i ZERO = newVec(); + public static final Vec3i MINUS_ONE = newVec(-1); + public static final Vec3i ONE = newVec(1); + + public static Vec3i newMutable(){return new Vec3iMutable();} + public static Vec3i newMutable(int value){return new Vec3iMutable(value);} + public static Vec3i newMutable(int x, int y, int z){return new Vec3iMutable(x, y, z);} + public static Vec3i newMutable(Vec3i vec){return newMutable(vec.getX(), vec.getY(), vec.getZ());} + + public static Vec3i newVec(){return new Vec3iImmutable();} + public static Vec3i newVec(int value){return new Vec3iImmutable(value);} + public static Vec3i newVec(int x, int y, int z){return new Vec3iImmutable(x, y, z);} + public static Vec3i newVec(Vec3i vec){return newVec(vec.getX(), vec.getY(), vec.getZ());} + + public int getX(); + public int getY(); + public int getZ(); + public Vec3i setX(int x); + public Vec3i setY(int y); + public Vec3i setZ(int z); + @Override + public default int[] asArray(){return new int[]{getX(), getY(), getZ()};} + + @Override + public Vec3i copy(); + @Override + public default Vec3i abs(){return set(Math.abs(getX()), Math.abs(getY()), Math.abs(getZ()));} + @Override + public default Vec3i negate(){return set(0, 0, 0);} + @Override + public default Vec3i invert(){return set(-getX(), -getY(), -getZ());} + @Override + public default Vec3i add(int value){return add(value, value, value);} + public default Vec3i add(Vec3i value){return add(value.getX(), value.getY(), value.getZ());} + public default Vec3i add(int x, int y, int z){return set(getX() + x, getY() + y, getZ() + z);} + + @Override + public default Vec3i sub(int value){return sub(value, value, value);} + public default Vec3i sub(Vec3i value){return sub(value.getX(), value.getY(), value.getZ());} + public default Vec3i sub(int x, int y, int z){return set(getX() - x, getY() - y, getZ() - z);} + + @Override + public default Vec3i multiply(int value){return multiply(value, value, value);} + public default Vec3i multiply(Vec3i value){return multiply(value.getX(), value.getY(), value.getZ());} + public default Vec3i multiply(int x, int y, int z){return set(getX() * x, getY() * y, getZ() * z);} + + @Override + public default Vec3i devide(int value){return devide(value, value, value);} + public default Vec3i devide(Vec3i value){return devide(value.getX(), value.getY(), value.getZ());} + public default Vec3i devide(int x, int y, int z){return set(getX() / x, getY() / y, getZ() / z);} + + @Override + public default Vec3i set(int value){return set(value, value, value);} + public default Vec3i set(Vec3i value){return set(value.getX(), value.getY(), value.getZ());} + public Vec3i set(int x, int y, int z); + + public default double distanceTo(Vec3i value){return distanceTo(value.getX(), value.getY(), value.getZ());} + public default double distanceTo(int x, int y, int z){return Math.sqrt(distanceToSquared(x, y, z));} + + public default long distanceToSquared(Vec3i value){return distanceToSquared(value.getX(), value.getY(), value.getZ());} + public default long distanceToSquared(int x, int y, int z) + { + long xPos = getX() - x; + long yPos = getY() - y; + long zPos = getZ() - z; + return (xPos * xPos) + (yPos * yPos) + (zPos * zPos); + } + @Override + public default long lengthSquared() {return (getX() * getX()) + (getY() * getY()) + (getZ() * getZ());} + + public default long dotProduct(Vec3i value){return dotProduct(value.getX(), value.getY(), value.getZ());} + public default long dotProduct(int x, int y, int z){return (getX() * x) + (getY() * y) + (getZ() * z);} + + public default Vec3i min(Vec3i other) {return min(other, this);} + public default Vec3i min(Vec3i other, Vec3i result){return min(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3i min(int x, int y, int z) {return min(x, y, z, this);} + public default Vec3i min(int x, int y, int z, Vec3i result){return result.set(Math.min(getX(), x), Math.min(getY(), y), Math.min(getZ(), z));} + + public default Vec3i max(Vec3i other) {return max(other, this);} + public default Vec3i max(Vec3i other, Vec3i result){return max(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3i max(int x, int y, int z) {return max(x, y, z, this);} + public default Vec3i max(int x, int y, int z, Vec3i result){return result.set(Math.max(getX(), x), Math.max(getY(), y), Math.max(getZ(), z));} + + public default Vec3i difference(Vec3i other) {return difference(other, this);} + public default Vec3i difference(Vec3i other, Vec3i result){return difference(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3i difference(int x, int y, int z) {return difference(x, y, z, this);} + public default Vec3i difference(int x, int y, int z, Vec3i result){return result.set(getX() - x, getY() - y, getZ() - z);} + + @Override + public default Vec3i clamp(int min, int max){return clamp(min, max, ALL);} + public default Vec3i clamp(int min, int max, Vec3i result){return clamp(min, max, result, ALL);} + @Override + public default Vec3i clamp(int min, int max, int filter){return clamp(min, max, this, filter);} + public default Vec3i clamp(int min, int max, Vec3i result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()), (filter & Z) == 0 ? getZ() : MathUtils.clamp(min, max, getZ()));} + + @Override + public default Vec3i store(ByteBuffer buffer) + { + buffer.putInt(getX()).putInt(getY()).putInt(getZ()); + return this; + } + + @Override + public default Vec3i load(ByteBuffer buffer) + { + return set(buffer.getInt(), buffer.getInt(), buffer.getInt()); + } + + @Override + public default Vec3i store(IntBuffer buffer) + { + buffer.put(getX()).put(getY()).put(getZ()); + return this; + } + + @Override + public default Vec3i load(IntBuffer buffer) + { + return set(buffer.get(), buffer.get(), buffer.get()); + } + + @Override + public default Vec3b asByte(){return isMutable() ? Vec3b.newMutable((byte)getX(), (byte)getY(), (byte)getZ()) : Vec3b.newVec((byte)getX(), (byte)getY(), (byte)getZ());} + @Override + public default Vec3s asShort(){return isMutable() ? Vec3s.newMutable((short)getX(), (short)getY(), (short)getZ()) : Vec3s.newVec((short)getX(), (short)getY(), (short)getZ());} + @Override + public default Vec3l asLong(){return isMutable() ? Vec3l.newMutable(getX(), getY(), getZ()) : Vec3l.newVec(getX(), getY(), getZ());} + @Override + public default Vec3f asFloat() {return isMutable() ? Vec3f.newMutable(getX(), getY(), getZ()) : Vec3f.newVec(getX(), getY(), getZ());} + @Override + public default Vec3d asDouble(){return isMutable() ? Vec3d.newMutable(getX(), getY(), getZ()) : Vec3d.newVec(getX(), getY(), getZ());} + + + @Override + public default Vec3i asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec3i asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec3i copyAsMutable(){return newMutable(this);} + @Override + public default Vec3i copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/ints/Vec3iImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec3iImmutable.java new file mode 100644 index 0000000..09299ff --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec3iImmutable.java @@ -0,0 +1,108 @@ +package speiger.src.coreengine.math.vector.ints; + +import java.util.Objects; + +public class Vec3iImmutable implements Vec3i +{ + final int x; + final int y; + final int z; + + public Vec3iImmutable() + { + x = 0; + y = 0; + z = 0; + } + + public Vec3iImmutable(int value) + { + x = value; + y = value; + z = value; + } + + public Vec3iImmutable(int x, int y, int z) + { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public int getX() + { + return x; + } + + @Override + public int getY() + { + return y; + } + + @Override + public int getZ() + { + return z; + } + + @Override + public Vec3i setX(int x) + { + return this.x == x ? this : Vec3i.newVec(x, y, z); + } + + @Override + public Vec3i setY(int y) + { + return this.y == y ? this : Vec3i.newVec(x, y, z); + } + + @Override + public Vec3i setZ(int z) + { + return this.z == z ? this : Vec3i.newVec(x, y, z); + } + + @Override + public Vec3i copy() + { + return Vec3i.newVec(this); + } + + @Override + public Vec3i set(int x, int y, int z) + { + return this.x == x && this.y == y && this.z == z ? this : Vec3i.newVec(x, y, z); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec3i) + { + Vec3i vec = (Vec3i)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z; + } + return false; + } + + @Override + public String toString() + { + return "Vec3i[x="+x+", y="+y+", z="+z+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/ints/Vec3iMutable.java b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec3iMutable.java new file mode 100644 index 0000000..1aebf3d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec3iMutable.java @@ -0,0 +1,111 @@ +package speiger.src.coreengine.math.vector.ints; + +import java.util.Objects; + +public class Vec3iMutable implements Vec3i +{ + int x; + int y; + int z; + + public Vec3iMutable() + { + } + + public Vec3iMutable(int value) + { + x = value; + y = value; + z = value; + } + + public Vec3iMutable(int x, int y, int z) + { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public int getX() + { + return x; + } + + @Override + public int getY() + { + return y; + } + + @Override + public int getZ() + { + return z; + } + + @Override + public Vec3i setX(int x) + { + this.x = x; + return this; + } + + @Override + public Vec3i setY(int y) + { + this.y = y; + return this; + } + + @Override + public Vec3i setZ(int z) + { + this.z = z; + return this; + } + + @Override + public Vec3i copy() + { + return Vec3i.newMutable(this); + } + + @Override + public Vec3i set(int x, int y, int z) + { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec3i) + { + Vec3i vec = (Vec3i)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z; + } + return false; + } + + @Override + public String toString() + { + return "Vec3i[x="+x+", y="+y+", z="+z+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/ints/Vec4i.java b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec4i.java new file mode 100644 index 0000000..cd9f345 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec4i.java @@ -0,0 +1,158 @@ +package speiger.src.coreengine.math.vector.ints; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec4b; +import speiger.src.coreengine.math.vector.doubles.Vec4d; +import speiger.src.coreengine.math.vector.floats.Vec4f; +import speiger.src.coreengine.math.vector.longs.Vec4l; +import speiger.src.coreengine.math.vector.shorts.Vec4s; + +public interface Vec4i extends Veci +{ + public static final Vec4i ZERO = newVec(); + public static final Vec4i MINUS_ONE = newVec(-1); + public static final Vec4i ONE = newVec(1); + + public static Vec4i newMutable(){return new Vec4iMutable();} + public static Vec4i newMutable(int value){return new Vec4iMutable(value);} + public static Vec4i newMutable(int x, int y, int z, int w){return new Vec4iMutable(x, y, z, w);} + public static Vec4i newMutable(Vec4i vec){return newMutable(vec.getX(), vec.getY(), vec.getZ(), vec.getW());} + + public static Vec4i newVec(){return new Vec4iImmutable();} + public static Vec4i newVec(int value){return new Vec4iImmutable(value);} + public static Vec4i newVec(int x, int y, int z, int w){return new Vec4iImmutable(x, y, z, w);} + public static Vec4i newVec(Vec4i vec){return newVec(vec.getX(), vec.getY(), vec.getZ(), vec.getW());} + + public int getX(); + public int getY(); + public int getZ(); + public int getW(); + public Vec4i setX(int x); + public Vec4i setY(int y); + public Vec4i setZ(int z); + public Vec4i setW(int w); + @Override + public default int[] asArray(){return new int[]{getX(), getY(), getZ(), getW()};} + @Override + public Vec4i copy(); + @Override + public default Vec4i abs(){return set(Math.abs(getX()), Math.abs(getY()), Math.abs(getZ()), Math.abs(getW()));} + @Override + public default Vec4i negate(){return set(0, 0, 0, 0);} + @Override + public default Vec4i invert(){return set(-getX(), -getY(), -getZ(), -getW());} + + @Override + public default Vec4i add(int value){return add(value, value, value, value);} + public default Vec4i add(Vec4i value){return add(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4i add(int x, int y, int z, int w){return set(getX() + x, getY() + y, getZ() + z, getW() + w);} + + @Override + public default Vec4i sub(int value){return sub(value, value, value, value);} + public default Vec4i sub(Vec4i value){return sub(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4i sub(int x, int y, int z, int w){return set(getX() - x, getY() - y, getZ() - z, getW() - w);} + + @Override + public default Vec4i multiply(int value){return multiply(value, value, value, value);} + public default Vec4i multiply(Vec4i value){return multiply(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4i multiply(int x, int y, int z, int w){return set(getX() * x, getY() * y, getZ() * z, getW() * w);} + + @Override + public default Vec4i devide(int value){return devide(value, value, value, value);} + public default Vec4i devide(Vec4i value){return devide(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4i devide(int x, int y, int z, int w){return set(getX() / x, getY() / y, getZ() / z, getW() / w);} + + @Override + public default Vec4i set(int value){return set(value, value, value, value);} + public default Vec4i set(Vec4i value){return set(value.getX(), value.getY(), value.getZ(), value.getW());} + public Vec4i set(int x, int y, int z, int w); + + public default double distanceTo(Vec4i value){return distanceTo(value.getX(), value.getY(), value.getZ(), value.getW());} + public default double distanceTo(int x, int y, int z, int w){return Math.sqrt(distanceTo(x, y, z, w));} + + public default long distanceToSquared(Vec4i value){return distanceToSquared(value.getX(), value.getY(), value.getZ(), value.getW());} + public default long distanceToSquared(int x, int y, int z, int w) + { + long xPos = getX() - x; + long yPos = getY() - y; + long zPos = getZ() - z; + long wPos = getW() - w; + return (xPos * xPos) + (yPos * yPos) + (zPos * zPos) + (wPos * wPos); + } + @Override + public default long lengthSquared() {return (getX() * getX()) + (getY() * getY()) + (getZ() * getZ()) + (getW() * getW());} + + public default long dotProduct(Vec4i value){return dotProduct(value.getX(), value.getY(), value.getZ(), value.getW());} + public default long dotProduct(int x, int y, int z, int w){return (getX() * x) + (getY() * y) + (getZ() * z) + (getW() * w);}; + + public default Vec4i min(Vec4i other) {return min(other, this);} + public default Vec4i min(Vec4i other, Vec4i result){return min(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4i min(int x, int y, int z, int w) {return min(x, y, z, w, this);} + public default Vec4i min(int x, int y, int z, int w, Vec4i result){return result.set(Math.min(getX(), x), Math.min(getY(), y), Math.min(getZ(), z), Math.min(getW(), w));} + + public default Vec4i max(Vec4i other) {return max(other, this);} + public default Vec4i max(Vec4i other, Vec4i result){return max(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4i max(int x, int y, int z, int w) {return max(x, y, z, w, this);} + public default Vec4i max(int x, int y, int z, int w, Vec4i result){return result.set(Math.max(getX(), x), Math.max(getY(), y), Math.max(getZ(), z), Math.max(getZ(), z));} + + public default Vec4i difference(Vec4i other) {return difference(other, this);} + public default Vec4i difference(Vec4i other, Vec4i result){return difference(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4i difference(int x, int y, int z, int w) {return difference(x, y, z, w, this);} + public default Vec4i difference(int x, int y, int z, int w, Vec4i result){return result.set(getX() - x, getY() - y, getZ() - z, getW() - w);} + + @Override + public default Vec4i clamp(int min, int max){return clamp(min, max, ALL);} + public default Vec4i clamp(int min, int max, Vec4i result){return clamp(min, max, result, ALL);} + @Override + public default Vec4i clamp(int min, int max, int filter){return clamp(min, max, this, filter);} + public default Vec4i clamp(int min, int max, Vec4i result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()), (filter & Z) == 0 ? getZ() : MathUtils.clamp(min, max, getZ()), (filter & W) == 0 ? getW() : MathUtils.clamp(min, max, getW()));} + + @Override + public default Vec4i store(ByteBuffer buffer) + { + buffer.putInt(getX()).putInt(getY()).putInt(getZ()).putInt(getW()); + return this; + } + + @Override + public default Vec4i load(ByteBuffer buffer) + { + return set(buffer.getInt(), buffer.getInt(), buffer.getInt(), buffer.getInt()); + } + + @Override + public default Vec4i store(IntBuffer buffer) + { + buffer.put(getX()).put(getY()).put(getZ()).put(getW()); + return this; + } + + @Override + public default Vec4i load(IntBuffer buffer) + { + return set(buffer.get(), buffer.get(), buffer.get(), buffer.get()); + } + + @Override + public default Vec4b asByte(){return isMutable() ? Vec4b.newMutable((byte)getX(), (byte)getY(), (byte)getZ(), (byte)getW()) : Vec4b.newVec((byte)getX(), (byte)getY(), (byte)getZ(), (byte)getW());} + @Override + public default Vec4s asShort(){return isMutable() ? Vec4s.newMutable((short)getX(), (short)getY(), (short)getZ(), (short)getW()) : Vec4s.newVec((short)getX(), (short)getY(), (short)getZ(), (short)getW());} + @Override + public default Vec4l asLong(){return isMutable() ? Vec4l.newMutable(getX(), getY(), getZ(), getW()) : Vec4l.newVec(getX(), getY(), getZ(), getW());} + @Override + public default Vec4f asFloat() {return isMutable() ? Vec4f.newMutable(getX(), getY(), getZ(), getW()) : Vec4f.newVec(getX(), getY(), getZ(), getW());} + @Override + public default Vec4d asDouble(){return isMutable() ? Vec4d.newMutable(getX(), getY(), getZ(), getW()) : Vec4d.newVec(getX(), getY(), getZ(), getW());} + + @Override + public default Vec4i asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec4i asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec4i copyAsMutable(){return newMutable(this);} + @Override + public default Vec4i copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/ints/Vec4iImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec4iImmutable.java new file mode 100644 index 0000000..1d03c74 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec4iImmutable.java @@ -0,0 +1,124 @@ +package speiger.src.coreengine.math.vector.ints; + +import java.util.Objects; + +public class Vec4iImmutable implements Vec4i +{ + final int x; + final int y; + final int z; + final int w; + + public Vec4iImmutable() + { + x = 0; + y = 0; + z = 0; + w = 0; + } + + public Vec4iImmutable(int value) + { + x = value; + y = value; + z = value; + w = value; + } + + public Vec4iImmutable(int x, int y, int z, int w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public int getX() + { + return x; + } + + @Override + public int getY() + { + return y; + } + + @Override + public int getZ() + { + return z; + } + + @Override + public int getW() + { + return w; + } + + @Override + public Vec4i setX(int x) + { + return this.x == x ? this : Vec4i.newVec(x, y, z, w); + } + + @Override + public Vec4i setY(int y) + { + return this.y == y ? this : Vec4i.newVec(x, y, z, w); + } + + @Override + public Vec4i setZ(int z) + { + return this.z == z ? this : Vec4i.newVec(x, y, z, w); + } + + @Override + public Vec4i setW(int w) + { + return this.w == w ? this : Vec4i.newVec(x, y, z, w); + } + + @Override + public Vec4i copy() + { + return Vec4i.newVec(this); + } + + @Override + public Vec4i set(int x, int y, int z, int w) + { + return this.x == x && this.y == y && this.z == z && this.w == w ? this : Vec4i.newVec(x, y, z, w); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z, w); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec4i) + { + Vec4i vec = (Vec4i)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z && vec.getW() == w; + } + return false; + } + + @Override + public String toString() + { + return "Vec4i[x="+x+", y="+y+", z="+z+", w="+w+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/ints/Vec4iMutable.java b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec4iMutable.java new file mode 100644 index 0000000..2a8bbd3 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/ints/Vec4iMutable.java @@ -0,0 +1,128 @@ +package speiger.src.coreengine.math.vector.ints; + +import java.util.Objects; + +public class Vec4iMutable implements Vec4i +{ + int x; + int y; + int z; + int w; + + public Vec4iMutable() + { + } + + public Vec4iMutable(int value) + { + x = value; + y = value; + z = value; + w = value; + } + + public Vec4iMutable(int x, int y, int z, int w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public int getX() + { + return x; + } + + @Override + public int getY() + { + return y; + } + + @Override + public int getZ() + { + return z; + } + + @Override + public int getW() + { + return w; + } + + @Override + public Vec4i setX(int x) + { + this.x = x; + return this; + } + + @Override + public Vec4i setY(int y) + { + this.y = y; + return this; + } + + @Override + public Vec4i setZ(int z) + { + this.z = z; + return this; + } + + @Override + public Vec4i setW(int w) + { + this.w = w; + return this; + } + + @Override + public Vec4i copy() + { + return Vec4i.newMutable(this); + } + + @Override + public Vec4i set(int x, int y, int z, int w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z, w); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec4i) + { + Vec4i vec = (Vec4i)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z && vec.getW() == w; + } + return false; + } + + @Override + public String toString() + { + return "Vec4i[x="+x+", y="+y+", z="+z+", w="+w+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/ints/Veci.java b/src/main/java/speiger/src/coreengine/math/vector/ints/Veci.java new file mode 100644 index 0000000..91ce212 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/ints/Veci.java @@ -0,0 +1,34 @@ +package speiger.src.coreengine.math.vector.ints; + +import java.nio.IntBuffer; + +import speiger.src.coreengine.math.vector.Vec; +import speiger.src.coreengine.math.vector.bytes.Vecb; +import speiger.src.coreengine.math.vector.doubles.Vecd; +import speiger.src.coreengine.math.vector.floats.Vecf; +import speiger.src.coreengine.math.vector.longs.Vecl; +import speiger.src.coreengine.math.vector.shorts.Vecs; + +public interface Veci extends Vec +{ + public Veci set(int value); + public Veci add(int value); + public Veci sub(int value); + public Veci multiply(int value); + public Veci devide(int value); + public Veci clamp(int min, int max); + public Veci clamp(int min, int max, int filter); + + public long lengthSquared(); + public default double length(){return Math.sqrt(lengthSquared());} + + public Veci store(IntBuffer buffer); + public Veci load(IntBuffer buffer); + public int[] asArray(); + + public Vecb asByte(); + public Vecs asShort(); + public Vecl asLong(); + public Vecf asFloat(); + public Vecd asDouble(); +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/longs/Vec2l.java b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec2l.java new file mode 100644 index 0000000..6e0bc6d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec2l.java @@ -0,0 +1,163 @@ +package speiger.src.coreengine.math.vector.longs; + +import java.nio.ByteBuffer; +import java.nio.LongBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec2b; +import speiger.src.coreengine.math.vector.doubles.Vec2d; +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.math.vector.ints.Vec2i; +import speiger.src.coreengine.math.vector.shorts.Vec2s; + +public interface Vec2l extends Vecl +{ + public static final Vec2l ZERO = newVec(); + public static final Vec2l MINUS_ONE = newVec(-1L); + public static final Vec2l ONE = newVec(1L); + + public static Vec2l newMutable(){return new Vec2lMutable();} + public static Vec2l newMutable(long value){return new Vec2lMutable(value);} + public static Vec2l newMutable(long x, long y){return new Vec2lMutable(x, y);} + public static Vec2l newMutable(Vec2l value){return newMutable(value.getX(), value.getY());} + + public static Vec2l newVec(){return new Vec2lImmutable();} + public static Vec2l newVec(long value){return new Vec2lImmutable(value);} + public static Vec2l newVec(long x, long y){return new Vec2lImmutable(x, y);} + public static Vec2l newVec(Vec2l value){return newVec(value.getX(), value.getY());} + + public long getX(); + public long getY(); + public Vec2l setX(long x); + public Vec2l setY(long y); + @Override + public default long[] asArray(){return new long[]{getX(), getY()};} + @Override + public Vec2l copy(); + @Override + public default Vec2l abs(){return set(Math.abs(getX()), Math.abs(getY()));} + @Override + public default Vec2l negate(){return set(0L, 0L);} + @Override + public default Vec2l invert(){return set(-getX(), -getY());}; + + @Override + public default Vec2l add(long value) {return add(value, value);} + public default Vec2l add(Vec2l value) {return add(value.getX(), value.getY());} + public default Vec2l add(long x, long y) {return set(x + getX(), y + getY());} + + @Override + public default Vec2l sub(long value){return sub(value, value);} + public default Vec2l sub(Vec2l value){return sub(value.getX(), value.getY());} + public default Vec2l sub(long x, long y) {return set(getX() - x, getY() - y);} + + @Override + public default Vec2l multiply(long value){return multiply(value, value);} + public default Vec2l multiply(Vec2l value){return multiply(value.getX(), value.getY());} + public default Vec2l multiply(long x, long y) {return set(x * getX(), y * getY());} + + @Override + public default Vec2l devide(long value){return devide(value, value);} + public default Vec2l devide(Vec2l value){return devide(value.getX(), value.getY());} + public default Vec2l devide(long x, long y){return set(getX() / x, getY() / y);} + + @Override + public default Vec2l set(long value){return set(value, value);}; + public default Vec2l set(Vec2l value){return set(value.getX(), value.getY());} + public Vec2l set(long x, long y); + + public default double distanceTo(Vec2l value){return distanceTo(value.getX(), value.getY());} + public default double distanceTo(long x, long y){return Math.sqrt(distanceToSquared(x, y));} + + public default long distanceToSquared(Vec2l value){return distanceToSquared(value.getX(), value.getY());} + public default long distanceToSquared(long x, long y) + { + long xPos = getX() - x; + long yPos = getY() - y; + return (xPos * xPos) + (yPos * yPos); + } + @Override + public default long lengthSquared() {return (getX() * getX()) + (getY() * getY());} + + public default long dotProduct(Vec2l value){return dotProduct(value.getX(), value.getY());} + public default long dotProduct(long x, long y){return (getX() * x) + (getY() * y);} + + public default Vec2l rotate(long angle, Vec2l center){return rotate(angle, center.getX(), center.getY());} + public default Vec2l rotate(long angle, long x, long y) + { + long xPos = getX() - x; + long yPos = getY() - y; + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + return set((long)((xPos * cos) + (yPos * sin) + x), (long)(-(xPos * sin) + (yPos * cos) + y)); + } + + public default Vec2l min(Vec2l other) {return min(other, this);} + public default Vec2l min(Vec2l other, Vec2l result){return min(other.getX(), other.getY(), result);} + public default Vec2l min(long x, long y) {return min(x, y, this);} + public default Vec2l min(long x, long y, Vec2l result){return result.set(Math.min(getX(), x), Math.min(getY(), y));} + + public default Vec2l max(Vec2l other) {return max(other, this);} + public default Vec2l max(Vec2l other, Vec2l result){return max(other.getX(), other.getY(), result);} + public default Vec2l max(long x, long y) {return max(x, y, this);} + public default Vec2l max(long x, long y, Vec2l result){return result.set(Math.max(getX(), x), Math.max(getY(), y));} + + public default Vec2l difference(Vec2l other) {return difference(other, this);} + public default Vec2l difference(Vec2l other, Vec2l result){return difference(other.getX(), other.getY(), result);} + public default Vec2l difference(long x, long y) {return difference(x, y, this);} + public default Vec2l difference(long x, long y, Vec2l result){return result.set(getX() - x, getY() - y);} + + @Override + public default Vec2l clamp(long min, long max){return clamp(min, max, ALL);} + public default Vec2l clamp(long min, long max, Vec2l result){return clamp(min, max, result, ALL);} + @Override + public default Vec2l clamp(long min, long max, int filter){return clamp(min, max, this, filter);} + public default Vec2l clamp(long min, long max, Vec2l result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()));} + + @Override + public default Vec2l store(ByteBuffer buffer) + { + buffer.putLong(getX()).putLong(getY()); + return this; + } + + @Override + public default Vec2l load(ByteBuffer buffer) + { + return set(buffer.getLong(), buffer.getLong()); + } + + @Override + public default Vec2l store(LongBuffer buffer) + { + buffer.put(getX()).put(getY()); + return this; + } + + @Override + public default Vec2l load(LongBuffer buffer) + { + return set(buffer.get(), buffer.get()); + } + + @Override + public default Vec2b asByte(){return isMutable() ? Vec2b.newMutable((byte)getX(), (byte)getY()) : Vec2b.newVec((byte)getX(), (byte)getY());} + @Override + public default Vec2s asShort(){return isMutable() ? Vec2s.newMutable((short)getX(), (short)getY()) : Vec2s.newVec((short)getX(), (short)getY());} + @Override + public default Vec2i asInt(){return isMutable() ? Vec2i.newMutable((int)getX(), (int)getY()) : Vec2i.newVec((int)getX(), (int)getY());} + @Override + public default Vec2f asFloat() {return isMutable() ? Vec2f.newMutable(getX(), getY()) : Vec2f.newVec(getX(), getY());} + @Override + public default Vec2d asDouble(){return isMutable() ? Vec2d.newMutable(getX(), getY()) : Vec2d.newVec(getX(), getY());} + + + @Override + public default Vec2l asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec2l asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec2l copyAsMutable(){return newMutable(this);} + @Override + public default Vec2l copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/longs/Vec2lImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec2lImmutable.java new file mode 100644 index 0000000..9e66de8 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec2lImmutable.java @@ -0,0 +1,92 @@ +package speiger.src.coreengine.math.vector.longs; + +import java.util.Objects; + +public class Vec2lImmutable implements Vec2l +{ + final long x; + final long y; + + public Vec2lImmutable() + { + x = 0; + y = 0; + } + + public Vec2lImmutable(long value) + { + x = value; + y = value; + } + + public Vec2lImmutable(long x, long y) + { + this.x = x; + this.y = y; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public long getX() + { + return x; + } + + @Override + public long getY() + { + return y; + } + + @Override + public Vec2l setX(long x) + { + return this.x == x ? this : Vec2l.newVec(x, y); + } + + @Override + public Vec2l setY(long y) + { + return this.y == y ? this : Vec2l.newVec(x, y); + } + + @Override + public Vec2l copy() + { + return Vec2l.newVec(this); + } + + @Override + public Vec2l set(long x, long y) + { + return this.x == x && this.y == y ? this : Vec2l.newVec(x, y); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec2l) + { + Vec2l vec = (Vec2l)obj; + return vec.getX() == x && vec.getY() == y; + } + return false; + } + + @Override + public String toString() + { + return "Vec2l[x="+x+", y="+y+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/longs/Vec2lMutable.java b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec2lMutable.java new file mode 100644 index 0000000..6c5f7c6 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec2lMutable.java @@ -0,0 +1,94 @@ +package speiger.src.coreengine.math.vector.longs; + +import java.util.Objects; + +public class Vec2lMutable implements Vec2l +{ + long x; + long y; + + public Vec2lMutable() + { + } + + public Vec2lMutable(long value) + { + x = value; + y = value; + } + + public Vec2lMutable(long x, long y) + { + this.x = x; + this.y = y; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public long getX() + { + return x; + } + + @Override + public long getY() + { + return y; + } + + @Override + public Vec2l setX(long x) + { + this.x = x; + return this; + } + + @Override + public Vec2l setY(long y) + { + this.y = y; + return this; + } + + @Override + public Vec2l copy() + { + return Vec2l.newMutable(this); + } + + @Override + public Vec2l set(long x, long y) + { + this.x = x; + this.y = y; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec2l) + { + Vec2l vec = (Vec2l)obj; + return vec.getX() == x && vec.getY() == y; + } + return false; + } + + @Override + public String toString() + { + return "Vec2l[x="+x+", y="+y+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/longs/Vec3l.java b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec3l.java new file mode 100644 index 0000000..d7c1da7 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec3l.java @@ -0,0 +1,156 @@ +package speiger.src.coreengine.math.vector.longs; + +import java.nio.ByteBuffer; +import java.nio.LongBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec3b; +import speiger.src.coreengine.math.vector.doubles.Vec3d; +import speiger.src.coreengine.math.vector.floats.Vec3f; +import speiger.src.coreengine.math.vector.ints.Vec3i; +import speiger.src.coreengine.math.vector.shorts.Vec3s; + + +public interface Vec3l extends Vecl +{ + public static final Vec3l ZERO = newVec(); + public static final Vec3l MINUS_ONE = newVec(-1L); + public static final Vec3l ONE = newVec(1L); + + public static Vec3l newMutable(){return new Vec3lMutable();} + public static Vec3l newMutable(long value){return new Vec3lMutable(value);} + public static Vec3l newMutable(long x, long y, long z){return new Vec3lMutable(x, y, z);} + public static Vec3l newMutable(Vec3l vec){return newMutable(vec.getX(), vec.getY(), vec.getZ());} + + public static Vec3l newVec(){return new Vec3lImmutable();} + public static Vec3l newVec(long value){return new Vec3lImmutable(value);} + public static Vec3l newVec(long x, long y, long z){return new Vec3lImmutable(x, y, z);} + public static Vec3l newVec(Vec3l vec){return newVec(vec.getX(), vec.getY(), vec.getZ());} + + public long getX(); + public long getY(); + public long getZ(); + public Vec3l setX(long x); + public Vec3l setY(long y); + public Vec3l setZ(long z); + @Override + public default long[] asArray(){return new long[]{getX(), getY(), getZ()};} + + @Override + public Vec3l copy(); + @Override + public default Vec3l abs(){return set(Math.abs(getX()), Math.abs(getY()), Math.abs(getZ()));} + @Override + public default Vec3l negate(){return set(0L, 0L, 0L);} + @Override + public default Vec3l invert(){return set(-getX(), -getY(), -getZ());} + @Override + public default Vec3l add(long value){return add(value, value, value);} + public default Vec3l add(Vec3l value){return add(value.getX(), value.getY(), value.getZ());} + public default Vec3l add(long x, long y, long z){return set(getX() + x, getY() + y, getZ() + z);} + + @Override + public default Vec3l sub(long value){return sub(value, value, value);} + public default Vec3l sub(Vec3l value){return sub(value.getX(), value.getY(), value.getZ());} + public default Vec3l sub(long x, long y, long z){return set(getX() - x, getY() - y, getZ() - z);} + + @Override + public default Vec3l multiply(long value){return multiply(value, value, value);} + public default Vec3l multiply(Vec3l value){return multiply(value.getX(), value.getY(), value.getZ());} + public default Vec3l multiply(long x, long y, long z){return set(getX() * x, getY() * y, getZ() * z);} + + @Override + public default Vec3l devide(long value){return devide(value, value, value);} + public default Vec3l devide(Vec3l value){return devide(value.getX(), value.getY(), value.getZ());} + public default Vec3l devide(long x, long y, long z){return set(getX() / x, getY() / y, getZ() / z);} + + @Override + public default Vec3l set(long value){return set(value, value, value);} + public default Vec3l set(Vec3l value){return set(value.getX(), value.getY(), value.getZ());} + public Vec3l set(long x, long y, long z); + + public default double distanceTo(Vec3l value){return distanceTo(value.getX(), value.getY(), value.getZ());} + public default double distanceTo(long x, long y, long z){return Math.sqrt(distanceToSquared(x, y, z));} + + public default long distanceToSquared(Vec3l value){return distanceToSquared(value.getX(), value.getY(), value.getZ());} + public default long distanceToSquared(long x, long y, long z) + { + long xPos = getX() - x; + long yPos = getY() - y; + long zPos = getZ() - z; + return (xPos * xPos) + (yPos * yPos) + (zPos * zPos); + } + @Override + public default long lengthSquared() {return (getX() * getX()) + (getY() * getY()) + (getZ() * getZ());} + + public default long dotProduct(Vec3l value){return dotProduct(value.getX(), value.getY(), value.getZ());} + public default long dotProduct(long x, long y, long z){return (getX() * x) + (getY() * y) + (getZ() * z);} + + public default Vec3l min(Vec3l other) {return min(other, this);} + public default Vec3l min(Vec3l other, Vec3l result){return min(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3l min(long x, long y, long z) {return min(x, y, z, this);} + public default Vec3l min(long x, long y, long z, Vec3l result){return result.set(Math.min(getX(), x), Math.min(getY(), y), Math.min(getZ(), z));} + + public default Vec3l max(Vec3l other) {return max(other, this);} + public default Vec3l max(Vec3l other, Vec3l result){return max(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3l max(long x, long y, long z) {return max(x, y, z, this);} + public default Vec3l max(long x, long y, long z, Vec3l result){return result.set(Math.max(getX(), x), Math.max(getY(), y), Math.max(getZ(), z));} + + public default Vec3l difference(Vec3l other) {return difference(other, this);} + public default Vec3l difference(Vec3l other, Vec3l result){return difference(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3l difference(long x, long y, long z) {return difference(x, y, z, this);} + public default Vec3l difference(long x, long y, long z, Vec3l result){return result.set(getX() - x, getY() - y, getZ() - z);} + + @Override + public default Vec3l clamp(long min, long max){return clamp(min, max, ALL);} + public default Vec3l clamp(long min, long max, Vec3l result){return clamp(min, max, result, ALL);} + @Override + public default Vec3l clamp(long min, long max, int filter){return clamp(min, max, this, filter);} + public default Vec3l clamp(long min, long max, Vec3l result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()), (filter & Z) == 0 ? getZ() : MathUtils.clamp(min, max, getZ()));} + + @Override + public default Vec3l store(ByteBuffer buffer) + { + buffer.putLong(getX()).putLong(getY()).putLong(getZ()); + return this; + } + + @Override + public default Vec3l load(ByteBuffer buffer) + { + return set(buffer.getLong(), buffer.getLong(), buffer.getLong()); + } + + @Override + public default Vec3l store(LongBuffer buffer) + { + buffer.put(getX()).put(getY()).put(getZ()); + return this; + } + + @Override + public default Vec3l load(LongBuffer buffer) + { + return set(buffer.get(), buffer.get(), buffer.get()); + } + + @Override + public default Vec3b asByte(){return isMutable() ? Vec3b.newMutable((byte)getX(), (byte)getY(), (byte)getZ()) : Vec3b.newVec((byte)getX(), (byte)getY(), (byte)getZ());} + @Override + public default Vec3s asShort(){return isMutable() ? Vec3s.newMutable((short)getX(), (short)getY(), (short)getZ()) : Vec3s.newVec((short)getX(), (short)getY(), (short)getZ());} + @Override + public default Vec3i asInt(){return isMutable() ? Vec3i.newMutable((int)getX(), (int)getY(), (int)getZ()) : Vec3i.newVec((int)getX(), (int)getY(), (int)getZ());} + @Override + public default Vec3f asFloat() {return isMutable() ? Vec3f.newMutable(getX(), getY(), getZ()) : Vec3f.newVec(getX(), getY(), getZ());} + @Override + public default Vec3d asDouble(){return isMutable() ? Vec3d.newMutable(getX(), getY(), getZ()) : Vec3d.newVec(getX(), getY(), getZ());} + + @Override + public default Vec3l asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec3l asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec3l copyAsMutable(){return newMutable(this);} + @Override + public default Vec3l copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/longs/Vec3lImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec3lImmutable.java new file mode 100644 index 0000000..f66bb26 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec3lImmutable.java @@ -0,0 +1,108 @@ +package speiger.src.coreengine.math.vector.longs; + +import java.util.Objects; + +public class Vec3lImmutable implements Vec3l +{ + final long x; + final long y; + final long z; + + public Vec3lImmutable() + { + x = 0; + y = 0; + z = 0; + } + + public Vec3lImmutable(long value) + { + x = value; + y = value; + z = value; + } + + public Vec3lImmutable(long x, long y, long z) + { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public long getX() + { + return x; + } + + @Override + public long getY() + { + return y; + } + + @Override + public long getZ() + { + return z; + } + + @Override + public Vec3l setX(long x) + { + return this.x == x ? this : Vec3l.newVec(x, y, z); + } + + @Override + public Vec3l setY(long y) + { + return this.y == y ? this : Vec3l.newVec(x, y, z); + } + + @Override + public Vec3l setZ(long z) + { + return this.z == z ? this : Vec3l.newVec(x, y, z); + } + + @Override + public Vec3l copy() + { + return Vec3l.newVec(this); + } + + @Override + public Vec3l set(long x, long y, long z) + { + return this.x == x && this.y == y && this.z == z ? this : Vec3l.newVec(x, y, z); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec3l) + { + Vec3l vec = (Vec3l)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z; + } + return false; + } + + @Override + public String toString() + { + return "Vec3l[x="+x+", y="+y+", z="+z+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/longs/Vec3lMutable.java b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec3lMutable.java new file mode 100644 index 0000000..2cbf789 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec3lMutable.java @@ -0,0 +1,111 @@ +package speiger.src.coreengine.math.vector.longs; + +import java.util.Objects; + +public class Vec3lMutable implements Vec3l +{ + long x; + long y; + long z; + + public Vec3lMutable() + { + } + + public Vec3lMutable(long value) + { + x = value; + y = value; + z = value; + } + + public Vec3lMutable(long x, long y, long z) + { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public long getX() + { + return x; + } + + @Override + public long getY() + { + return y; + } + + @Override + public long getZ() + { + return z; + } + + @Override + public Vec3l setX(long x) + { + this.x = x; + return this; + } + + @Override + public Vec3l setY(long y) + { + this.y = y; + return this; + } + + @Override + public Vec3l setZ(long z) + { + this.z = z; + return this; + } + + @Override + public Vec3l copy() + { + return Vec3l.newMutable(this); + } + + @Override + public Vec3l set(long x, long y, long z) + { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec3l) + { + Vec3l vec = (Vec3l)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z; + } + return false; + } + + @Override + public String toString() + { + return "Vec3l[x="+x+", y="+y+", z="+z+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/longs/Vec4l.java b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec4l.java new file mode 100644 index 0000000..6c070f5 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec4l.java @@ -0,0 +1,158 @@ +package speiger.src.coreengine.math.vector.longs; + +import java.nio.ByteBuffer; +import java.nio.LongBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec4b; +import speiger.src.coreengine.math.vector.doubles.Vec4d; +import speiger.src.coreengine.math.vector.floats.Vec4f; +import speiger.src.coreengine.math.vector.ints.Vec4i; +import speiger.src.coreengine.math.vector.shorts.Vec4s; + +public interface Vec4l extends Vecl +{ + public static final Vec4l ZERO = newVec(); + public static final Vec4l MINUS_ONE = newVec(-1L); + public static final Vec4l ONE = newVec(1L); + + public static Vec4l newMutable(){return new Vec4lMutable();} + public static Vec4l newMutable(long value){return new Vec4lMutable(value);} + public static Vec4l newMutable(long x, long y, long z, long w){return new Vec4lMutable(x, y, z, w);} + public static Vec4l newMutable(Vec4l vec){return newMutable(vec.getX(), vec.getY(), vec.getZ(), vec.getW());} + + public static Vec4l newVec(){return new Vec4lImmutable();} + public static Vec4l newVec(long value){return new Vec4lImmutable(value);} + public static Vec4l newVec(long x, long y, long z, long w){return new Vec4lImmutable(x, y, z, w);} + public static Vec4l newVec(Vec4l vec){return newVec(vec.getX(), vec.getY(), vec.getZ(), vec.getW());} + + public long getX(); + public long getY(); + public long getZ(); + public long getW(); + public Vec4l setX(long x); + public Vec4l setY(long y); + public Vec4l setZ(long z); + public Vec4l setW(long w); + @Override + public default long[] asArray(){return new long[]{getX(), getY(), getZ(), getW()};} + @Override + public Vec4l copy(); + @Override + public default Vec4l abs(){return set(Math.abs(getX()), Math.abs(getY()), Math.abs(getZ()), Math.abs(getW()));} + @Override + public default Vec4l negate(){return set(0L, 0L, 0L, 0L);} + @Override + public default Vec4l invert(){return set(-getX(), -getY(), -getZ(), -getW());} + + @Override + public default Vec4l add(long value){return add(value, value, value, value);} + public default Vec4l add(Vec4l value){return add(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4l add(long x, long y, long z, long w){return set(getX() + x, getY() + y, getZ() + z, getW() + w);} + + @Override + public default Vec4l sub(long value){return sub(value, value, value, value);} + public default Vec4l sub(Vec4l value){return sub(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4l sub(long x, long y, long z, long w){return set(getX() - x, getY() - y, getZ() - z, getW() - w);} + + @Override + public default Vec4l multiply(long value){return multiply(value, value, value, value);} + public default Vec4l multiply(Vec4l value){return multiply(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4l multiply(long x, long y, long z, long w){return set(getX() * x, getY() * y, getZ() * z, getW() * w);} + + @Override + public default Vec4l devide(long value){return devide(value, value, value, value);} + public default Vec4l devide(Vec4l value){return devide(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4l devide(long x, long y, long z, long w){return set(getX() / x, getY() / y, getZ() / z, getW() / w);} + + @Override + public default Vec4l set(long value){return set(value, value, value, value);} + public default Vec4l set(Vec4l value){return set(value.getX(), value.getY(), value.getZ(), value.getW());} + public Vec4l set(long x, long y, long z, long w); + + public default double distanceTo(Vec4l value){return distanceTo(value.getX(), value.getY(), value.getZ(), value.getW());} + public default double distanceTo(long x, long y, long z, long w){return Math.sqrt(distanceTo(x, y, z, w));} + + public default long distanceToSquared(Vec4l value){return distanceToSquared(value.getX(), value.getY(), value.getZ(), value.getW());} + public default long distanceToSquared(long x, long y, long z, long w) + { + long xPos = getX() - x; + long yPos = getY() - y; + long zPos = getZ() - z; + long wPos = getW() - w; + return (xPos * xPos) + (yPos * yPos) + (zPos * zPos) + (wPos * wPos); + } + @Override + public default long lengthSquared() {return (getX() * getX()) + (getY() * getY()) + (getZ() * getZ()) + (getW() * getW());} + + public default long dotProduct(Vec4l value){return dotProduct(value.getX(), value.getY(), value.getZ(), value.getW());} + public default long dotProduct(long x, long y, long z, long w){return (getX() * x) + (getY() * y) + (getZ() * z) + (getW() * w);}; + + public default Vec4l min(Vec4l other) {return min(other, this);} + public default Vec4l min(Vec4l other, Vec4l result){return min(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4l min(long x, long y, long z, long w) {return min(x, y, z, w, this);} + public default Vec4l min(long x, long y, long z, long w, Vec4l result){return result.set(Math.min(getX(), x), Math.min(getY(), y), Math.min(getZ(), z), Math.min(getW(), w));} + + public default Vec4l max(Vec4l other) {return max(other, this);} + public default Vec4l max(Vec4l other, Vec4l result){return max(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4l max(long x, long y, long z, long w) {return max(x, y, z, w, this);} + public default Vec4l max(long x, long y, long z, long w, Vec4l result){return result.set(Math.max(getX(), x), Math.max(getY(), y), Math.max(getZ(), z), Math.max(getZ(), z));} + + public default Vec4l difference(Vec4l other) {return difference(other, this);} + public default Vec4l difference(Vec4l other, Vec4l result){return difference(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4l difference(long x, long y, long z, long w) {return difference(x, y, z, w, this);} + public default Vec4l difference(long x, long y, long z, long w, Vec4l result){return result.set(getX() - x, getY() - y, getZ() - z, getW() - w);} + + @Override + public default Vec4l clamp(long min, long max){return clamp(min, max, ALL);} + public default Vec4l clamp(long min, long max, Vec4l result){return clamp(min, max, result, ALL);} + @Override + public default Vec4l clamp(long min, long max, int filter){return clamp(min, max, this, filter);} + public default Vec4l clamp(long min, long max, Vec4l result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()), (filter & Z) == 0 ? getZ() : MathUtils.clamp(min, max, getZ()), (filter & W) == 0 ? getW() : MathUtils.clamp(min, max, getW()));} + + @Override + public default Vec4l store(ByteBuffer buffer) + { + buffer.putLong(getX()).putLong(getY()).putLong(getZ()).putLong(getW()); + return this; + } + + @Override + public default Vec4l load(ByteBuffer buffer) + { + return set(buffer.getLong(), buffer.getLong(), buffer.getLong(), buffer.getLong()); + } + + @Override + public default Vec4l store(LongBuffer buffer) + { + buffer.put(getX()).put(getY()).put(getZ()).put(getW()); + return this; + } + + @Override + public default Vec4l load(LongBuffer buffer) + { + return set(buffer.get(), buffer.get(), buffer.get(), buffer.get()); + } + + @Override + public default Vec4b asByte(){return isMutable() ? Vec4b.newMutable((byte)getX(), (byte)getY(), (byte)getZ(), (byte)getW()) : Vec4b.newVec((byte)getX(), (byte)getY(), (byte)getZ(), (byte)getW());} + @Override + public default Vec4s asShort(){return isMutable() ? Vec4s.newMutable((short)getX(), (short)getY(), (short)getZ(), (short)getW()) : Vec4s.newVec((short)getX(), (short)getY(), (short)getZ(), (short)getW());} + @Override + public default Vec4i asInt(){return isMutable() ? Vec4i.newMutable((int)getX(), (int)getY(), (int)getZ(), (int)getW()) : Vec4i.newVec((int)getX(), (int)getY(), (int)getZ(), (int)getW());} + @Override + public default Vec4f asFloat() {return isMutable() ? Vec4f.newMutable(getX(), getY(), getZ(), getW()) : Vec4f.newVec(getX(), getY(), getZ(), getW());} + @Override + public default Vec4d asDouble(){return isMutable() ? Vec4d.newMutable(getX(), getY(), getZ(), getW()) : Vec4d.newVec(getX(), getY(), getZ(), getW());} + + @Override + public default Vec4l asMutable(){return isMutable() ? this : newMutable(this);} + @Override + public default Vec4l asImmutable(){return isMutable() ? newVec(this) : this;} + @Override + public default Vec4l copyAsMutable(){return newMutable(this);} + @Override + public default Vec4l copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/longs/Vec4lImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec4lImmutable.java new file mode 100644 index 0000000..81b5726 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec4lImmutable.java @@ -0,0 +1,124 @@ +package speiger.src.coreengine.math.vector.longs; + +import java.util.Objects; + +public class Vec4lImmutable implements Vec4l +{ + final long x; + final long y; + final long z; + final long w; + + public Vec4lImmutable() + { + x = 0; + y = 0; + z = 0; + w = 0; + } + + public Vec4lImmutable(long value) + { + x = value; + y = value; + z = value; + w = value; + } + + public Vec4lImmutable(long x, long y, long z, long w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public long getX() + { + return x; + } + + @Override + public long getY() + { + return y; + } + + @Override + public long getZ() + { + return z; + } + + @Override + public long getW() + { + return w; + } + + @Override + public Vec4l setX(long x) + { + return this.x == x ? this : Vec4l.newVec(x, y, z, w); + } + + @Override + public Vec4l setY(long y) + { + return this.y == y ? this : Vec4l.newVec(x, y, z, w); + } + + @Override + public Vec4l setZ(long z) + { + return this.z == z ? this : Vec4l.newVec(x, y, z, w); + } + + @Override + public Vec4l setW(long w) + { + return this.w == w ? this : Vec4l.newVec(x, y, z, w); + } + + @Override + public Vec4l copy() + { + return Vec4l.newVec(this); + } + + @Override + public Vec4l set(long x, long y, long z, long w) + { + return this.x == x && this.y == y && this.z == z && this.w == w ? this : Vec4l.newVec(x, y, z, w); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z, w); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec4l) + { + Vec4l vec = (Vec4l)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z && vec.getW() == w; + } + return false; + } + + @Override + public String toString() + { + return "Vec4l[x="+x+", y="+y+", z="+z+", w="+w+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/longs/Vec4lMutable.java b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec4lMutable.java new file mode 100644 index 0000000..5e37dfd --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/longs/Vec4lMutable.java @@ -0,0 +1,128 @@ +package speiger.src.coreengine.math.vector.longs; + +import java.util.Objects; + +public class Vec4lMutable implements Vec4l +{ + long x; + long y; + long z; + long w; + + public Vec4lMutable() + { + } + + public Vec4lMutable(long value) + { + x = value; + y = value; + z = value; + w = value; + } + + public Vec4lMutable(long x, long y, long z, long w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public long getX() + { + return x; + } + + @Override + public long getY() + { + return y; + } + + @Override + public long getZ() + { + return z; + } + + @Override + public long getW() + { + return w; + } + + @Override + public Vec4l setX(long x) + { + this.x = x; + return this; + } + + @Override + public Vec4l setY(long y) + { + this.y = y; + return this; + } + + @Override + public Vec4l setZ(long z) + { + this.z = z; + return this; + } + + @Override + public Vec4l setW(long w) + { + this.w = w; + return this; + } + + @Override + public Vec4l copy() + { + return Vec4l.newMutable(this); + } + + @Override + public Vec4l set(long x, long y, long z, long w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z, w); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec4l) + { + Vec4l vec = (Vec4l)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z && vec.getW() == w; + } + return false; + } + + @Override + public String toString() + { + return "Vec4l[x="+x+", y="+y+", z="+z+", w="+w+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/longs/Vecl.java b/src/main/java/speiger/src/coreengine/math/vector/longs/Vecl.java new file mode 100644 index 0000000..01fd402 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/longs/Vecl.java @@ -0,0 +1,34 @@ +package speiger.src.coreengine.math.vector.longs; + +import java.nio.LongBuffer; + +import speiger.src.coreengine.math.vector.Vec; +import speiger.src.coreengine.math.vector.bytes.Vecb; +import speiger.src.coreengine.math.vector.doubles.Vecd; +import speiger.src.coreengine.math.vector.floats.Vecf; +import speiger.src.coreengine.math.vector.ints.Veci; +import speiger.src.coreengine.math.vector.shorts.Vecs; + +public interface Vecl extends Vec +{ + public Vecl set(long value); + public Vecl add(long value); + public Vecl sub(long value); + public Vecl multiply(long value); + public Vecl devide(long value); + public Vecl clamp(long min, long max); + public Vecl clamp(long min, long max, int filter); + + public long lengthSquared(); + public default double length(){return Math.sqrt(lengthSquared());} + + public Vecl store(LongBuffer buffer); + public Vecl load(LongBuffer buffer); + public long[] asArray(); + + public Vecb asByte(); + public Vecs asShort(); + public Veci asInt(); + public Vecf asFloat(); + public Vecd asDouble(); +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/matrix/Matrix4f.java b/src/main/java/speiger/src/coreengine/math/vector/matrix/Matrix4f.java new file mode 100644 index 0000000..47f053a --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/matrix/Matrix4f.java @@ -0,0 +1,898 @@ +package speiger.src.coreengine.math.vector.matrix; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.util.Arrays; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.VectorUtil; +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.math.vector.floats.Vec3f; +import speiger.src.coreengine.math.vector.floats.Vec4f; +import speiger.src.coreengine.math.vector.quaternion.Quaternion; + +public class Matrix4f +{ + static final Vec3f X_ROTATION = Vec3f.newVec(1F, 0F, 0F); + static final Vec3f Y_ROTATION = Vec3f.newVec(0F, 1F, 0F); + static final Vec3f Z_ROTATION = Vec3f.newVec(0F, 0F, 1F); + + float[] data = new float[16]; + + public Matrix4f() + { + setIdentity(); + } + + public Matrix4f(float[] data) + { + System.arraycopy(data, 0, this.data, 0, 16); + } + + public Matrix4f(Matrix4f other) + { + this(other.data); + } + + public Matrix4f store(FloatBuffer buffer) + { + buffer.put(data); + return this; + } + + public Matrix4f store(ByteBuffer buffer) + { + for(int i = 0;i < 16;i++) + { + buffer.putFloat(data[i]); + } + return this; + } + + public Matrix4f store(Matrix4f other) + { + System.arraycopy(data, 0, other.data, 0, 16); + return this; + } + + public Matrix4f load(FloatBuffer buffer) + { + buffer.get(data); + return this; + } + + public Matrix4f load(ByteBuffer buffer) + { + for(int i = 0;i < 16;i++) + { + data[i] = buffer.getFloat(); + } + return this; + } + + public Matrix4f load(Matrix4f other) + { + System.arraycopy(other.data, 0, data, 0, 16); + return this; + } + + @Override + public String toString() + { + StringBuilder buf = new StringBuilder().append("\n"); + buf.append("x0=").append(data[0]).append(' ').append("y0=").append(data[4]).append(' ').append("z0=").append(data[8]).append(' ').append("w0=").append(data[12]).append('\n'); + buf.append("x1=").append(data[1]).append(' ').append("y1=").append(data[5]).append(' ').append("z1=").append(data[9]).append(' ').append("w1=").append(data[13]).append('\n'); + buf.append("x2=").append(data[2]).append(' ').append("y2=").append(data[6]).append(' ').append("z2=").append(data[10]).append(' ').append("w2=").append(data[14]).append('\n'); + buf.append("x3=").append(data[3]).append(' ').append("y3=").append(data[7]).append(' ').append("z3=").append(data[11]).append(' ').append("w3=").append(data[15]).append('\n'); + return buf.toString(); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Matrix4f) + { + return Arrays.equals(data, ((Matrix4f)obj).data); + } + return false; + } + + public float[] getData() + { + return data; + } + + public float get(int index) + { + return data[index]; + } + + public float get(int column, int row) + { + return data[(column * 4) + row]; + } + + public Matrix4f set(int column, int row, float value) + { + data[(column * 4) + row] = value; + return this; + } + + public Matrix4f set(int index, float value) + { + data[index] = value; + return this; + } + + public Matrix4f setIdentity() + { + Arrays.fill(data, 0F); + data[0] = 1F; + data[5] = 1F; + data[10] = 1F; + data[15] = 1F; + return this; + } + + public Matrix4f invert() + { + float determinant = determinant(); + if(determinant != 0F) + { + float determinant_inv = 1F / determinant; + float t00 = determinant3x3(data[5], data[6], data[7], data[9], data[10], data[11], data[13], data[14], data[15]); + float t01 = -determinant3x3(data[4], data[6], data[7], data[8], data[10], data[11], data[12], data[14], data[15]); + float t02 = determinant3x3(data[4], data[5], data[7], data[8], data[9], data[11], data[12], data[13], data[15]); + float t03 = -determinant3x3(data[4], data[5], data[6], data[8], data[9], data[10], data[12], data[13], data[14]); + float t10 = -determinant3x3(data[1], data[2], data[3], data[9], data[10], data[11], data[13], data[14], data[15]); + float t11 = determinant3x3(data[0], data[2], data[3], data[8], data[10], data[11], data[12], data[14], data[15]); + float t12 = -determinant3x3(data[0], data[1], data[3], data[8], data[9], data[11], data[12], data[13], data[15]); + float t13 = determinant3x3(data[0], data[1], data[2], data[8], data[9], data[10], data[12], data[13], data[14]); + float t20 = determinant3x3(data[1], data[2], data[3], data[5], data[6], data[7], data[13], data[14], data[15]); + float t21 = -determinant3x3(data[0], data[2], data[3], data[4], data[6], data[7], data[12], data[14], data[15]); + float t22 = determinant3x3(data[0], data[1], data[3], data[4], data[5], data[7], data[12], data[13], data[15]); + float t23 = -determinant3x3(data[0], data[1], data[2], data[4], data[5], data[6], data[12], data[13], data[14]); + float t30 = -determinant3x3(data[1], data[2], data[3], data[5], data[6], data[7], data[9], data[10], data[11]); + float t31 = determinant3x3(data[0], data[2], data[3], data[4], data[6], data[7], data[8], data[10], data[11]); + float t32 = -determinant3x3(data[0], data[1], data[3], data[4], data[5], data[7], data[8], data[9], data[11]); + float t33 = determinant3x3(data[0], data[1], data[2], data[4], data[5], data[6], data[8], data[9], data[10]); + + data[0] = t00 * determinant_inv; + data[5] = t11 * determinant_inv; + data[10] = t22 * determinant_inv; + data[15] = t33 * determinant_inv; + data[1] = t10 * determinant_inv; + data[4] = t01 * determinant_inv; + data[8] = t02 * determinant_inv; + data[2] = t20 * determinant_inv; + data[6] = t21 * determinant_inv; + data[9] = t12 * determinant_inv; + data[3] = t30 * determinant_inv; + data[12] = t03 * determinant_inv; + data[7] = t31 * determinant_inv; + data[13] = t13 * determinant_inv; + data[14] = t23 * determinant_inv; + data[11] = t32 * determinant_inv; + } + return this; + } + + public Matrix4f flip() + { + data[0] = -data[0]; + data[1] = -data[1]; + data[2] = -data[2]; + data[3] = -data[3]; + data[4] = -data[4]; + data[5] = -data[5]; + data[6] = -data[6]; + data[7] = -data[7]; + data[8] = -data[8]; + data[9] = -data[9]; + data[10] = -data[10]; + data[11] = -data[11]; + data[12] = -data[12]; + data[13] = -data[13]; + data[14] = -data[14]; + data[15] = -data[15]; + return this; + } + + public Matrix4f negate() + { + Arrays.fill(data, 0F); + return this; + } + + public Matrix4f add(Matrix4f other) + { + data[0] += other.data[0]; + data[1] += other.data[1]; + data[2] += other.data[2]; + data[3] += other.data[3]; + data[4] += other.data[4]; + data[5] += other.data[5]; + data[6] += other.data[6]; + data[7] += other.data[7]; + data[8] += other.data[8]; + data[9] += other.data[9]; + data[10] += other.data[10]; + data[11] += other.data[11]; + data[12] += other.data[12]; + data[13] += other.data[13]; + data[14] += other.data[14]; + data[15] += other.data[15]; + return this; + } + + public Matrix4f sub(Matrix4f other) + { + data[0] -= other.data[0]; + data[1] -= other.data[1]; + data[2] -= other.data[2]; + data[3] -= other.data[3]; + data[4] -= other.data[4]; + data[5] -= other.data[5]; + data[6] -= other.data[6]; + data[7] -= other.data[7]; + data[8] -= other.data[8]; + data[9] -= other.data[9]; + data[10] -= other.data[10]; + data[11] -= other.data[11]; + data[12] -= other.data[12]; + data[13] -= other.data[13]; + data[14] -= other.data[14]; + data[15] -= other.data[15]; + return this; + } + + public Matrix4f multiply(Matrix4f other) + { + float data0 = data[0] * other.data[0] + data[4] * other.data[1] + data[8] * other.data[2] + data[12] * other.data[3]; + float data1 = data[1] * other.data[0] + data[5] * other.data[1] + data[9] * other.data[2] + data[13] * other.data[3]; + float data2 = data[2] * other.data[0] + data[6] * other.data[1] + data[10] * other.data[2] + data[14] * other.data[3]; + float data3 = data[3] * other.data[0] + data[7] * other.data[1] + data[11] * other.data[2] + data[15] * other.data[3]; + float data4 = data[0] * other.data[4] + data[4] * other.data[5] + data[8] * other.data[6] + data[12] * other.data[7]; + float data5 = data[1] * other.data[4] + data[5] * other.data[5] + data[9] * other.data[6] + data[13] * other.data[7]; + float data6 = data[2] * other.data[4] + data[6] * other.data[5] + data[10] * other.data[6] + data[14] * other.data[7]; + float data7 = data[3] * other.data[4] + data[7] * other.data[5] + data[11] * other.data[6] + data[15] * other.data[7]; + float data8 = data[0] * other.data[8] + data[4] * other.data[9] + data[8] * other.data[10] + data[12] * other.data[11]; + float data9 = data[1] * other.data[8] + data[5] * other.data[9] + data[9] * other.data[10] + data[13] * other.data[11]; + float data10 = data[2] * other.data[8] + data[6] * other.data[9] + data[10] * other.data[10] + data[14] * other.data[11]; + float data11 = data[3] * other.data[8] + data[7] * other.data[9] + data[11] * other.data[10] + data[15] * other.data[11]; + float data12 = data[0] * other.data[12] + data[4] * other.data[13] + data[8] * other.data[14] + data[12] * other.data[15]; + float data13 = data[1] * other.data[12] + data[5] * other.data[13] + data[9] * other.data[14] + data[13] * other.data[15]; + float data14 = data[2] * other.data[12] + data[6] * other.data[13] + data[10] * other.data[14] + data[14] * other.data[15]; + float data15 = data[3] * other.data[12] + data[7] * other.data[13] + data[11] * other.data[14] + data[15] * other.data[15]; + data[0] = data0; + data[1] = data1; + data[2] = data2; + data[3] = data3; + data[4] = data4; + data[5] = data5; + data[6] = data6; + data[7] = data7; + data[8] = data8; + data[9] = data9; + data[10] = data10; + data[11] = data11; + data[12] = data12; + data[13] = data13; + data[14] = data14; + data[15] = data15; + return this; + } + + public Matrix4f transpose() + { + float data0 = data[0]; + float data1 = data[4]; + float data2 = data[8]; + float data3 = data[12]; + float data4 = data[1]; + float data5 = data[5]; + float data6 = data[9]; + float data7 = data[13]; + float data8 = data[2]; + float data9 = data[6]; + float data10 = data[10]; + float data11 = data[14]; + float data12 = data[3]; + float data13 = data[7]; + float data14 = data[11]; + float data15 = data[15]; + + data[0] = data0; + data[1] = data1; + data[2] = data2; + data[3] = data3; + data[4] = data4; + data[5] = data5; + data[6] = data6; + data[7] = data7; + data[8] = data8; + data[9] = data9; + data[10] = data10; + data[11] = data11; + data[12] = data12; + data[13] = data13; + data[14] = data14; + data[15] = data15; + return this; + } + + public Matrix4f transpose3x3() + { + float nm00 = data[0]; + float nm01 = data[4]; + float nm02 = data[8]; + float nm10 = data[1]; + float nm11 = data[5]; + float nm12 = data[9]; + float nm20 = data[2]; + float nm21 = data[6]; + float nm22 = data[10]; + data[0] = nm00; + data[1] = nm01; + data[2] = nm02; + data[4] = nm10; + data[5] = nm11; + data[6] = nm12; + data[8] = nm20; + data[9] = nm21; + data[10] = nm22; + return this; + } + + public Matrix4f decompose(Vec3f position, Quaternion rotation, Vec3f scale) + { + position.set(get(3, 0), get(3, 1), get(3, 2)); + rotation.set(this); + scale.set(get(0, 0), get(1, 1), get(2, 2)); + return this; + } + + public Matrix4f setTranslation(Vec3f vec) + { + data[12] = vec.getX(); + data[13] = vec.getY(); + data[14] = vec.getZ(); + return this; + } + + public Matrix4f translate(Vec2f vec) + { + return translate(vec.getX(), vec.getY()); + } + + public Matrix4f translate(float x, float y) + { + data[12] += data[0] * x + data[4] * y; + data[13] += data[1] * x + data[5] * y; + data[14] += data[2] * x + data[6] * y; + data[15] += data[3] * x + data[7] * y; + return this; + } + + public Matrix4f translate(Vec3f vec) + { + data[12] += data[0] * vec.getX() + data[4] * vec.getY() + data[8] * vec.getZ(); + data[13] += data[1] * vec.getX() + data[5] * vec.getY() + data[9] * vec.getZ(); + data[14] += data[2] * vec.getX() + data[6] * vec.getY() + data[10] * vec.getZ(); + data[15] += data[3] * vec.getX() + data[7] * vec.getY() + data[11] * vec.getZ(); + return this; + } + + public Matrix4f translate(float x, float y, float z) + { + data[12] += data[0] * x + data[4] * y + data[8] * z; + data[13] += data[1] * x + data[5] * y + data[9] * z; + data[14] += data[2] * x + data[6] * y + data[10] * z; + data[15] += data[3] * x + data[7] * y + data[11] * z; + return this; + } + + public Matrix4f rotateX(float angle) + { + return rotate((float)Math.toRadians(angle), X_ROTATION); + } + + public Matrix4f rotateY(float angle) + { + return rotate((float)Math.toRadians(angle), Y_ROTATION); + } + + public Matrix4f rotateZ(float angle) + { + return rotate((float)Math.toRadians(angle), Z_ROTATION); + } + + public Matrix4f rotateRadX(float angle) + { + return rotate(angle, X_ROTATION); + } + + public Matrix4f rotateRadY(float angle) + { + return rotate(angle, Y_ROTATION); + } + + public Matrix4f rotateRadZ(float angle) + { + return rotate(angle, Z_ROTATION); + } + + public Matrix4f rotate(float angle, Vec3f axis) + { + float c = MathUtils.cos(angle); + float s = MathUtils.sin(angle); + float oneminusc = 1.0f - c; + float xy = axis.getX() * axis.getY(); + float yz = axis.getY() * axis.getZ(); + float xz = axis.getX() * axis.getZ(); + float xs = axis.getX() * s; + float ys = axis.getY() * s; + float zs = axis.getZ() * s; + + float f00 = axis.getX() * axis.getX() * oneminusc + c; + float f01 = xy * oneminusc + zs; + float f02 = xz * oneminusc - ys; + + float f10 = xy * oneminusc - zs; + float f11 = axis.getY() * axis.getY() * oneminusc + c; + float f12 = yz * oneminusc + xs; + + float f20 = xz * oneminusc + ys; + float f21 = yz * oneminusc - xs; + float f22 = axis.getZ() * axis.getZ() * oneminusc + c; + + float t00 = data[0] * f00 + data[4] * f01 + data[8] * f02; + float t01 = data[1] * f00 + data[5] * f01 + data[9] * f02; + float t02 = data[2] * f00 + data[6] * f01 + data[10] * f02; + float t03 = data[3] * f00 + data[7] * f01 + data[11] * f02; + float t10 = data[0] * f10 + data[4] * f11 + data[8] * f12; + float t11 = data[1] * f10 + data[5] * f11 + data[9] * f12; + float t12 = data[2] * f10 + data[6] * f11 + data[10] * f12; + float t13 = data[3] * f10 + data[7] * f11 + data[11] * f12; + data[8] = data[0] * f20 + data[4] * f21 + data[8] * f22; + data[9] = data[1] * f20 + data[5] * f21 + data[9] * f22; + data[10] = data[2] * f20 + data[6] * f21 + data[10] * f22; + data[11] = data[3] * f20 + data[7] * f21 + data[11] * f22; + data[0] = t00; + data[1] = t01; + data[2] = t02; + data[3] = t03; + data[4] = t10; + data[5] = t11; + data[6] = t12; + data[7] = t13; + return this; + } + + public Matrix4f rotate(Quaternion rotation) + { + float w2 = rotation.getW() * rotation.getW(); + float x2 = rotation.getX() * rotation.getX(); + float y2 = rotation.getY() * rotation.getY(); + float z2 = rotation.getZ() * rotation.getZ(); + float zw = rotation.getZ() * rotation.getW(); + float dzw = zw + zw; + float xy = rotation.getX() * rotation.getY(); + float dxy = xy + xy; + float xz = rotation.getX() * rotation.getZ(); + float dxz = xz + xz; + float yw = rotation.getY() * rotation.getW(); + float dyw = yw + yw; + float yz = rotation.getY() * rotation.getZ(); + float dyz = yz + yz; + float xw = rotation.getX() * rotation.getW(); + float dxw = xw + xw; + float rm00 = w2 + x2 - z2 - y2; + float rm01 = dxy + dzw; + float rm02 = dxz - dyw; + float rm10 = -dzw + dxy; + float rm11 = y2 - z2 + w2 - x2; + float rm12 = dyz + dxw; + float rm20 = dyw + dxz; + float rm21 = dyz - dxw; + float rm22 = z2 - y2 - x2 + w2; + float nm00 = data[0] * rm00 + data[4] * rm01 + data[8] * rm02; + float nm01 = data[1] * rm00 + data[5] * rm01 + data[9] * rm02; + float nm02 = data[2] * rm00 + data[6] * rm01 + data[10] * rm02; + float nm03 = data[3] * rm00 + data[7] * rm01 + data[11] * rm02; + float nm10 = data[0] * rm10 + data[4] * rm11 + data[8] * rm12; + float nm11 = data[1] * rm10 + data[5] * rm11 + data[9] * rm12; + float nm12 = data[2] * rm10 + data[6] * rm11 + data[10] * rm12; + float nm13 = data[3] * rm10 + data[7] * rm11 + data[11] * rm12; + data[8] = data[0] * rm20 + data[4] * rm21 + data[8] * rm22; + data[9] = data[1] * rm20 + data[5] * rm21 + data[9] * rm22; + data[10] = data[2] * rm20 + data[6] * rm21 + data[10] * rm22; + data[11] = data[3] * rm20 + data[7] * rm21 + data[11] * rm22; + data[0] = nm00; + data[1] = nm01; + data[2] = nm02; + data[3] = nm03; + data[4] = nm10; + data[5] = nm11; + data[6] = nm12; + data[7] = nm13; + return this; + } + + public Matrix4f setRotation(Matrix4f source) + { + data[0] = source.data[0]; + data[1] = source.data[1]; + data[2] = source.data[2]; + data[4] = source.data[4]; + data[5] = source.data[5]; + data[6] = source.data[6]; + data[8] = source.data[8]; + data[9] = source.data[9]; + data[10] = source.data[10]; + return this; + } + + public Matrix4f setBillboard(Matrix4f source) + { + data[0] = source.data[0]; + data[1] = source.data[4]; + data[2] = source.data[8]; + data[4] = source.data[1]; + data[5] = source.data[5]; + data[6] = source.data[9]; + data[8] = source.data[2]; + data[9] = source.data[6]; + data[10] = source.data[10]; + return this; + } + + public Matrix4f scale(Vec3f vec) + { + return scale(vec.getX(), vec.getY(), vec.getZ()); + } + + public Matrix4f scale(float value) + { + return scale(value, value, value); + } + + public Matrix4f scale(float x, float y, float z) + { + data[0] *= x; + data[1] *= x; + data[2] *= x; + data[3] *= x; + data[4] *= y; + data[5] *= y; + data[6] *= y; + data[7] *= y; + data[8] *= z; + data[9] *= z; + data[10] *= z; + data[11] *= z; + return this; + } + + public Matrix4f unscale(Vec3f vec) + { + return unscale(vec.getX(), vec.getY(), vec.getZ()); + } + + public Matrix4f unscale(float scale) + { + return unscale(scale, scale, scale); + } + + public Matrix4f unscale(float x, float y, float z) + { + data[0] /= x; + data[1] /= x; + data[2] /= x; + data[3] /= x; + data[4] /= y; + data[5] /= y; + data[6] /= y; + data[7] /= y; + data[8] /= z; + data[9] /= z; + data[10] /= z; + data[11] /= z; + return this; + } + + public Matrix4f setPerspective(float fovy, float aspect, float zNear, float zFar) + { + float h = (float)Math.tan(fovy * 0.5f); + set(0, 0, 1F / (h * aspect)).set(1, 1, 1F / h); + boolean farInf = zFar > 0 && Float.isInfinite(zFar); + boolean nearInf = zNear > 0 && Float.isInfinite(zNear); + if(farInf) + { + float e = 1E-6f; + set(2, 2, e - 1F).set(3, 2, (e - 2F) * zNear); + } + else if(nearInf) + { + float e = 1E-6f; + set(2, 2, 1F - e).set(3, 2, (2F - e) * zFar); + } + else + { + set(2, 2, (zFar + zNear) / (zNear - zFar)).set(3, 2, (zFar + zFar) * zNear / (zNear - zFar)); + } + return set(2, 3, -1.0f); + } + + public Matrix4f getTranslation(Vec3f vec) + { + vec.set(data[12], data[13], data[14]); + return this; + } + + public Matrix4f getScale(Vec3f vec) + { + vec.setX((float)Math.sqrt(data[0] * data[0] + data[1] * data[1] + data[2] * data[2])); + vec.setY((float)Math.sqrt(data[4] * data[4] + data[5] * data[5] + data[6] * data[6])); + vec.setZ((float)Math.sqrt(data[8] * data[8] + data[9] * data[9] + data[10] * data[10])); + return this; + } + + public Vec4f transform(Vec4f input) + { + return transform(input, input); + } + + public Vec4f transform(Vec4f input, Vec4f output) + { + float x = data[0] * input.getX() + data[4] * input.getY() + data[8] * input.getZ() + data[12] * input.getW(); + float y = data[1] * input.getX() + data[5] * input.getY() + data[9] * input.getZ() + data[13] * input.getW(); + float z = data[2] * input.getX() + data[6] * input.getY() + data[10] * input.getZ() + data[14] * input.getW(); + float w = data[3] * input.getX() + data[7] * input.getY() + data[11] * input.getZ() + data[15] * input.getW(); + return output.set(x, y, z, w); + } + + public void transform(Vec4f input, FloatBuffer buffer) + { + buffer.put(data[0] * input.getX() + data[4] * input.getY() + data[8] * input.getZ() + data[12] * input.getW()); + buffer.put(data[1] * input.getX() + data[5] * input.getY() + data[9] * input.getZ() + data[13] * input.getW()); + buffer.put(data[2] * input.getX() + data[6] * input.getY() + data[10] * input.getZ() + data[14] * input.getW()); + buffer.put(data[3] * input.getX() + data[7] * input.getY() + data[11] * input.getZ() + data[15] * input.getW()); + } + + public Vec3f transform(Vec3f input, boolean position) + { + return transform(input, input, position); + } + + public Vec3f transform(Vec3f input, Vec3f output, boolean position) + { + float pos = position ? 1F : 0F; + return output.set(data[0] * input.getX() + data[4] * input.getY() + data[8] * input.getZ() + data[12] * pos, data[1] * input.getX() + data[5] * input.getY() + data[9] * input.getZ() + data[13] * pos, data[2] * input.getX() + data[6] * input.getY() + data[10] * input.getZ() + data[14] * pos); + } + + public void transform(Vec3f input, FloatBuffer buffer, boolean position) + { + float pos = position ? 1F : 0F; + buffer.put(data[0] * input.getX() + data[4] * input.getY() + data[8] * input.getZ() + data[12] * pos); + buffer.put(data[1] * input.getX() + data[5] * input.getY() + data[9] * input.getZ() + data[13] * pos); + buffer.put(data[2] * input.getX() + data[6] * input.getY() + data[10] * input.getZ() + data[14] * pos); + } + + public float determinant() + { + float f = data[0] * ((data[5] * data[10] * data[15] + data[6] * data[11] * data[13] + data[7] * data[9] * data[14]) - data[7] * data[10] * data[13] - data[5] * data[11] * data[14] - data[6] * data[9] * data[15]); + f -= data[1] * ((data[4] * data[10] * data[15] + data[6] * data[11] * data[12] + data[7] * data[8] * data[14]) - data[7] * data[10] * data[12] - data[4] * data[11] * data[14] - data[6] * data[8] * data[15]); + f += data[2] * ((data[4] * data[9] * data[15] + data[5] * data[11] * data[12] + data[7] * data[8] * data[13]) - data[7] * data[9] * data[12] - data[4] * data[11] * data[13] - data[5] * data[8] * data[15]); + f -= data[3] * ((data[4] * data[9] * data[14] + data[5] * data[10] * data[12] + data[6] * data[8] * data[13]) - data[6] * data[9] * data[12] - data[4] * data[10] * data[13] - data[5] * data[8] * data[14]); + return f; + } + + public float determinant3x3(float t00, float t01, float t02, float t10, float t11, float t12, float t20, float t21, float t22) + { + return t00 * (t11 * t22 - t12 * t21) + t01 * (t12 * t20 - t10 * t22) + t02 * (t10 * t21 - t11 * t20); + } + + public Matrix4f setTransform(Vec3f position, Vec3f rotation, float scale) + { + setIdentity(); + translate(position); + rotate((float)Math.toRadians(rotation.getX()), X_ROTATION); + rotate((float)Math.toRadians(rotation.getY()), Y_ROTATION); + rotate((float)Math.toRadians(rotation.getZ()), Z_ROTATION); + scale(scale, scale, scale); + return this; + } + + public Matrix4f setTransform(Vec3f position, Vec3f rotation, Vec3f scale) + { + setIdentity(); + translate(position); + rotate((float)Math.toRadians(rotation.getX()), X_ROTATION); + rotate((float)Math.toRadians(rotation.getY()), Y_ROTATION); + rotate((float)Math.toRadians(rotation.getZ()), Z_ROTATION); + scale(scale); + return this; + } + + public Matrix4f setTransform(Vec3f position, Vec3f offset, Vec3f rotation, float scale) + { + setIdentity(); + translate(position); + translate(offset); + rotate((float)Math.toRadians(rotation.getX()), X_ROTATION); + rotate((float)Math.toRadians(rotation.getY()), Y_ROTATION); + rotate((float)Math.toRadians(rotation.getZ()), Z_ROTATION); + scale(scale, scale, scale); + return this; + } + + public Matrix4f setTransform(Vec3f position, Vec3f offset, Vec3f rotation, Vec3f scale) + { + setIdentity(); + translate(position); + translate(offset); + rotate((float)Math.toRadians(rotation.getX()), X_ROTATION); + rotate((float)Math.toRadians(rotation.getY()), Y_ROTATION); + rotate((float)Math.toRadians(rotation.getZ()), Z_ROTATION); + scale(scale); + return this; + } + + public Matrix4f setTransform(Vec3f position, Quaternion rotation, float scale) + { + setIdentity(); + translate(position); + rotate(rotation); + scale(scale, scale, scale); + return this; + } + + public Matrix4f setTransform(Vec3f position, Quaternion rotation, Vec3f scale) + { + setIdentity(); + translate(position); + rotate(rotation); + scale(scale); + return this; + } + + public Matrix4f setTransform(Vec3f position, Vec3f offset, Quaternion rotation, float scale) + { + setIdentity(); + translate(position); + translate(offset); + rotate(rotation); + scale(scale, scale, scale); + return this; + } + + public Matrix4f setTransform(Vec3f position, Vec3f offset, Quaternion rotation, Vec3f scale) + { + setIdentity(); + translate(position); + translate(offset); + rotate(rotation); + scale(scale); + return this; + } + + public Matrix4f setTransform(Vec3f position, Matrix4f billRotation, float scale) + { + setIdentity(); + translate(position); + setBillboard(billRotation); + scale(scale, scale, scale); + return this; + } + + public Matrix4f setTransform(Vec3f position, Matrix4f billRotation, Vec3f scale) + { + setIdentity(); + translate(position); + setBillboard(billRotation); + transpose3x3(); + scale(scale); + return this; + } + + public Vec3f project(float x, float y, float z, int[] viewport, Vec3f winCoordsDest) + { + float invW = 1F / VectorUtil.fma(data[3], x, VectorUtil.fma(data[7], y, VectorUtil.fma(data[11], z, data[15]))); + float nx = VectorUtil.fma(data[0], x, VectorUtil.fma(data[4], y, VectorUtil.fma(data[8], z, data[12]))) * invW; + float ny = VectorUtil.fma(data[1], x, VectorUtil.fma(data[5], y, VectorUtil.fma(data[9], z, data[13]))) * invW; + float nz = VectorUtil.fma(data[2], x, VectorUtil.fma(data[6], y, VectorUtil.fma(data[10], z, data[14]))) * invW; + return winCoordsDest.set(VectorUtil.fma(VectorUtil.fma(nx, 0.5F, 0.5F), viewport[2], viewport[0]), VectorUtil.fma(VectorUtil.fma(ny, 0.5F, 0.5F), viewport[3], viewport[1]), VectorUtil.fma(0.5F, nz, 0.5F)); + } + + public Vec3f unproject(float winX, float winY, float winZ, int[] viewport, Vec3f dest) { + float a = data[0] * data[5] - data[1] * data[4]; + float b = data[0] * data[6] - data[2] * data[4]; + float c = data[0] * data[7] - data[3] * data[4]; + float d = data[1] * data[6] - data[2] * data[5]; + float e = data[1] * data[7] - data[3] * data[5]; + float f = data[2] * data[7] - data[3] * data[6]; + float g = data[8] * data[13] - data[9] * data[12]; + float h = data[8] * data[14] - data[10] * data[12]; + float i = data[8] * data[15] - data[11] * data[12]; + float j = data[9] * data[14] - data[10] * data[13]; + float k = data[9] * data[15] - data[11] * data[13]; + float l = data[10] * data[15] - data[11] * data[14]; + float det = a * l - b * k + c * j + d * i - e * h + f * g; + det = 1.0f / det; + float im00 = ( data[5] * l - data[6] * k + data[7] * j) * det; + float im01 = (-data[1] * l + data[2] * k - data[3] * j) * det; + float im02 = ( data[13] * f - data[14] * e + data[15] * d) * det; + float im03 = (-data[9] * f + data[10] * e - data[11] * d) * det; + float im10 = (-data[4] * l + data[6] * i - data[7] * h) * det; + float im11 = ( data[0] * l - data[2] * i + data[3] * h) * det; + float im12 = (-data[12] * f + data[14] * c - data[15] * b) * det; + float im13 = ( data[8] * f - data[10] * c + data[11] * b) * det; + float im20 = ( data[4] * k - data[5] * i + data[7] * g) * det; + float im21 = (-data[0] * k + data[1] * i - data[3] * g) * det; + float im22 = ( data[12] * e - data[13] * c + data[15] * a) * det; + float im23 = (-data[8] * e + data[9] * c - data[11] * a) * det; + float im30 = (-data[4] * j + data[5] * h - data[6] * g) * det; + float im31 = ( data[0] * j - data[1] * h + data[2] * g) * det; + float im32 = (-data[12] * d + data[13] * b - data[14] * a) * det; + float im33 = ( data[8] * d - data[9] * b + data[10] * a) * det; + float ndcX = (winX-viewport[0])/viewport[2]*2.0f-1.0f; + float ndcY = (winY-viewport[1])/viewport[3]*2.0f-1.0f; + float ndcZ = winZ+winZ-1.0f; + float invW = 1.0f / (im03 * ndcX + im13 * ndcY + im23 * ndcZ + im33); + return dest.set((im00 * ndcX + im10 * ndcY + im20 * ndcZ + im30) * invW, (im01 * ndcX + im11 * ndcY + im21 * ndcZ + im31) * invW, (im02 * ndcX + im12 * ndcY + im22 * ndcZ + im32) * invW); + } + + public Matrix4f ortho(float x, float y, float width, float height, float zNear, float zFar) + { + return ortho(x, x+width, y+height, y, zNear, zFar, false); + } + + public Matrix4f ortho(float left, float right, float bottom, float top, float zNear, float zFar, boolean zZeroToOne) + { + float rm00 = 2F / (right - left); + float rm11 = 2F / (top - bottom); + float rm22 = (zZeroToOne ? 1F : 2F) / (zFar - zNear); + float rm30 = (left + right) / (left - right); + float rm31 = (top + bottom) / (bottom - top); + float rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar); + + data[12] = data[0] * rm30 + data[4] * rm31 + data[8] * rm32 + data[12]; + data[13] = data[1] * rm30 + data[5] * rm31 + data[9] * rm32 + data[13]; + data[14] = data[2] * rm30 + data[6] * rm31 + data[10] * rm32 + data[14]; + data[15] = data[3] * rm30 + data[7] * rm31 + data[11] * rm32 + data[15]; + data[0] = data[0] * rm00; + data[1] = data[1] * rm00; + data[2] = data[2] * rm00; + data[3] = data[3] * rm00; + data[4] = data[4] * rm11; + data[5] = data[5] * rm11; + data[6] = data[6] * rm11; + data[7] = data[7] * rm11; + data[8] = data[8] * rm22; + data[9] = data[9] * rm22; + data[10] = data[10] * rm22; + data[11] = data[11] * rm22; + return this; + } + + public Vec4f storeFrustrumPlane(int plane, Vec4f toStore) + { + switch(plane) + { + case 0: + return toStore.set(data[3] + data[0], data[7] + data[4], data[11] + data[8], data[15] + data[12]).normalize3D(); + case 1: + return toStore.set(data[3] - data[0], data[7] - data[4], data[11] - data[8], data[15] - data[12]).normalize3D(); + case 2: + return toStore.set(data[3] + data[1], data[7] + data[5], data[11] + data[9], data[15] + data[13]).normalize3D(); + case 3: + return toStore.set(data[3] - data[1], data[7] - data[5], data[11] - data[9], data[15] - data[13]).normalize3D(); + case 4: + return toStore.set(data[3] + data[2], data[7] + data[6], data[11] + data[10], data[15] + data[14]).normalize3D(); + case 5: + return toStore.set(data[3] - data[2], data[7] - data[6], data[11] - data[10], data[15] - data[14]).normalize3D(); + } + return toStore; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/quaternion/Quaternion.java b/src/main/java/speiger/src/coreengine/math/vector/quaternion/Quaternion.java new file mode 100644 index 0000000..158d5bc --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/quaternion/Quaternion.java @@ -0,0 +1,215 @@ +package speiger.src.coreengine.math.vector.quaternion; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.floats.Vec4f; +import speiger.src.coreengine.math.vector.matrix.Matrix4f; + +public interface Quaternion +{ + public static final Quaternion ZERO = newQuat(0F, 0F, 0F, 0F); + public static final Quaternion IDENTITY = newQuat(); + + public static Quaternion newQuat(){return new QuaternionImmutable();} + public static Quaternion newQuat(Quaternion source){return newQuat(source.getX(), source.getY(), source.getZ(), source.getW());} + public static Quaternion newQuat(float x, float y, float z, float w){return new QuaternionImmutable(x, y, z, w);} + public static Quaternion newAxisRadQuat(float x, float y, float z, float angle){return ZERO.setAxisRad(x, y, z, angle);} + public static Quaternion newAxisDegQuat(float x, float y, float z, float angle){return ZERO.setAxisDeg(x, y, z, angle);} + + public static Quaternion newMutable(){return new QuaternionMutable();} + public static Quaternion newMutable(Quaternion source){return newMutable(source.getX(), source.getY(), source.getZ(), source.getW());} + public static Quaternion newMutable(float x, float y, float z, float w){return new QuaternionMutable(x, y, z, w);} + public static Quaternion newMutableRadAxis(float x, float y, float z, float angle){return newMutable().setAxisRad(x, y, z, angle);} + public static Quaternion newMutableDegAxis(float x, float y, float z, float angle){return newMutable().setAxisDeg(x, y, z, angle);} + + public static double acos(double v){return v < -1D ? 3.141592653589793D : (v > 1D ? 0D : Math.acos(v));} + + public Quaternion setX(float x); + public Quaternion setY(float y); + public Quaternion setZ(float z); + public Quaternion setW(float w); + public float getX(); + public float getY(); + public float getZ(); + public float getW(); + + public default Quaternion negate(){return set(0F, 0F, 0F, 0F);} + public default Quaternion invert(){return set(-getX(), -getY(), -getZ(), -getW());} + public default Quaternion normalize(){return scale(1.0F / length());} + public default Quaternion conjugate(){return set(-getX(), -getY(), -getZ(), getW());} + public default Quaternion setIdentity(){return set(0F, 0F, 0F, 1F);} + + public default Quaternion multiply(Quaternion other){return set(getX() * other.getW() + getW() * other.getX() + getY() * other.getZ() - getZ() * other.getY(), getY() * other.getW() + getW() * other.getY() + getZ() * other.getX() - getX() * other.getZ(), getZ() * other.getW() + getW() * other.getZ() + getX() * other.getY() - getY() * other.getX(), getW() * other.getW() - getX() * other.getX() - getY() * other.getY() - getZ() * other.getZ());} + public default Quaternion scale(float scale){return set(getX() * scale, getY() * scale, getZ() * scale, getW() * scale);} + public default Quaternion rotateX(float angle) + { + angle = (float)Math.toRadians(angle); + float sin = MathUtils.sin(angle * 0.5D); + float cos = MathUtils.cos(angle * 0.5D); + return set(getW() * sin + getX() * cos, getY() * cos + getZ() * sin, getZ() * cos - getY() * sin, getW() * cos - getX() * sin); + } + public default Quaternion rotateY(float angle) + { + angle = (float)Math.toRadians(angle); + float sin = MathUtils.sin(angle * 0.5D); + float cos = MathUtils.cos(angle * 0.5D); + return set(getX() * cos - getZ() * sin, getW() * sin + getY() * cos, getX() * sin + getZ() * cos, getW() * cos - getY() * sin); + } + public default Quaternion rotateZ(float angle) + { + angle = (float)Math.toRadians(angle); + float sin = MathUtils.sin(angle * 0.5D); + float cos = MathUtils.cos(angle * 0.5D); + return set(getX() * cos + getY() * sin, getY() * cos - getX() * sin, getW() * sin + getZ() * cos, getW() * cos - getZ() * sin); + } + + public default Quaternion set(Vec4f value){return set(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Quaternion set(Quaternion value){return set(value.getX(), value.getY(), value.getZ(), value.getW());} + + public default Quaternion set(Matrix4f matrix) + { + float tr = matrix.get(0, 0) + matrix.get(1, 1) + matrix.get(2, 2); + if(tr >= 0.0D) + { + float s = (float)Math.sqrt(tr + 1.0D); + float w = s * 0.5F; + s = 0.5F / s; + float x = (matrix.get(2, 1) - matrix.get(1, 2)) * s; + float y = (matrix.get(0, 2) - matrix.get(2, 0)) * s; + float z = (matrix.get(1, 0) - matrix.get(0, 1)) * s; + return set(x, y, z, w); + } + else + { + float max = Math.max(Math.max(matrix.get(0, 0), matrix.get(1, 1)), matrix.get(2, 2)); + if(max == matrix.get(0, 0)) + { + float s = (float)Math.sqrt(matrix.get(0, 0) - (matrix.get(1, 1) + matrix.get(2, 2)) + 1.0D); + float x = s * 0.5F; + s = 0.5F / s; + float y = (matrix.get(0, 1) + matrix.get(1, 0)) * s; + float z = (matrix.get(2, 0) + matrix.get(0, 2)) * s; + float w = (matrix.get(2, 1) - matrix.get(1, 2)) * s; + return set(x, y, z, w); + } + else if(max == matrix.get(1, 1)) + { + float s = (float)Math.sqrt(matrix.get(1, 1) - (matrix.get(2, 2) + matrix.get(0, 0)) + 1.0D); + float y = s * 0.5F; + s = 0.5F / s; + float z = (matrix.get(1, 2) + matrix.get(2, 1)) * s; + float x = (matrix.get(0, 1) + matrix.get(1, 0)) * s; + float w = (matrix.get(0, 2) - matrix.get(2, 0)) * s; + return set(x, y, z, w); + } + else + { + float s = (float)Math.sqrt(matrix.get(2, 2) - (matrix.get(0, 0) + matrix.get(1, 1)) + 1.0D); + float z = s * 0.5F; + s = 0.5F / s; + float x = (matrix.get(2, 0) + matrix.get(0, 2)) * s; + float y = (matrix.get(1, 2) + matrix.get(2, 1)) * s; + float w = (matrix.get(1, 0) - matrix.get(0, 1)) * s; + return set(x, y, z, w); + } + } + } + + public default Quaternion setAxisRad(float x, float y, float z, float angle) + { + float sin = MathUtils.sin(angle * 0.5D); + float cos = MathUtils.cos(angle * 0.5D); + return set(x * sin, y * sin, z * sin, cos); + } + public default Quaternion setAxisDeg(float x, float y, float z, float angle) + { + angle = (float)Math.toRadians(angle); + float sin = MathUtils.sin(angle * 0.5D); + float cos = MathUtils.cos(angle * 0.5D); + return set(x * sin, y * sin, z * sin, cos); + } + public Quaternion set(float x, float y, float z, float w); + + public default float dot(Quaternion other){return getX() * other.getX() + getY() * other.getY() + getZ() * other.getZ() + getW() * other.getW();} + public default Quaternion difference(Quaternion other){return difference(other, this);} + public default Quaternion difference(Quaternion other, Quaternion result) + { + float invNorm = 1.0F / (getX() * getX() + getY() * getY() + getZ() * getZ() + getW() * getW()); + float x = -getX() * invNorm; + float y = -getY() * invNorm; + float z = -getZ() * invNorm; + float w = getW() * invNorm; + return set(w * other.getX() + x * other.getW() + y * other.getZ() - z * other.getY(), w * other.getY() - x * other.getZ() + y * other.getW() + z * other.getX(), w * other.getZ() + x * other.getY() - y * other.getX() + z * other.getW(), w * other.getW() - x * other.getX() - y * other.getY() - z * other.getZ()); + } + public default Quaternion lerp(Quaternion other, float progress){return lerp(other, progress, this);} + public default Quaternion lerp(Quaternion other, float progress, Quaternion result) + { + float cosom = getX() * other.getX() + getY() * other.getY() + getZ() * other.getZ() + getW() * other.getW(); + float absCosom = Math.abs(cosom); + float scale1; + float scale0; + if(1.0F - absCosom > 1.0E-006F) + { + float sinSqr = 1.0F - absCosom * absCosom; + float sinom = (float)(1.0D / Math.sqrt(sinSqr)); + float omega = (float)Math.atan2(sinSqr * sinom, absCosom); + scale0 = MathUtils.sin((1.0D - progress) * omega) * sinom; + scale1 = MathUtils.sin(progress * omega) * sinom; + } + else + { + scale0 = 1.0F - progress; + scale1 = progress; + } + scale1 = cosom >= 0.0F ? scale1 : -scale1; + return result.set(scale0 * getX() + scale1 * other.getX(), scale0 * getY() + scale1 * other.getY(), scale0 * getZ() + scale1 * other.getZ(), scale0 * getW() + scale1 * other.getW()); + } + + public default float length(){return (float)Math.sqrt(lengthSquared());} + public default double lengthSquared(){return (getX() * getX()) + (getY() * getY()) + (getZ() * getZ()) + (getW() * getW());} + + public default Matrix4f asRotationMatrix(){return new Matrix4f().rotate(this);} + public default Vec4f toAxisDegreeRotation() {return toAxisDegreeRotation(Vec4f.newMutable());} + public default Vec4f toAxisDegreeRotation(Vec4f input) + { + double invSqrt = 1.0D / Math.sqrt(1.0D - getW() * getW()); + return input.set((float)(getX() * invSqrt), (float)(getY() * invSqrt), (float)(getZ() * invSqrt), (float)Math.toDegrees(acos(getW()) * 2F)); + } + public default Vec4f toAxisRotation() {return toAxisRotation(Vec4f.newMutable());} + public default Vec4f toAxisRotation(Vec4f input) + { + double invSqrt = 1.0D / Math.sqrt(1.0D - getW() * getW()); + return input.set((float)(getX() * invSqrt), (float)(getY() * invSqrt), (float)(getZ() * invSqrt), (float)acos(getW()) * 2F); + } + + public default Quaternion store(ByteBuffer buffer) + { + buffer.putFloat(getX()).putFloat(getY()).putFloat(getZ()).putFloat(getW()); + return this; + } + + public default Quaternion load(ByteBuffer buffer) + { + return set(buffer.getFloat(), buffer.getFloat(), buffer.getFloat(), buffer.getFloat()); + } + + public default Quaternion store(FloatBuffer buffer) + { + buffer.put(getX()).put(getY()).put(getZ()).put(getW()); + return this; + } + + public default Quaternion load(FloatBuffer buffer) + { + return set(buffer.get(), buffer.get(), buffer.get(), buffer.get()); + } + + public Quaternion copy(); + public boolean isMutable(); + public default Quaternion asMutable(){return isMutable() ? this : newQuat(this);} + public default Quaternion asImmutable(){return isMutable() ? newMutable(this) : this;} + public default Quaternion copyAsMutable(){return newMutable(this);} + public default Quaternion copyAsImmutable(){return newQuat(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/quaternion/QuaternionImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/quaternion/QuaternionImmutable.java new file mode 100644 index 0000000..f5892ef --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/quaternion/QuaternionImmutable.java @@ -0,0 +1,116 @@ +package speiger.src.coreengine.math.vector.quaternion; + +import java.util.Arrays; + +public class QuaternionImmutable implements Quaternion +{ + final float x; + final float y; + final float z; + final float w; + + public QuaternionImmutable() + { + x = 0F; + y = 0F; + z = 0F; + w = 1F; + } + + public QuaternionImmutable(float x, float y, float z, float w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + @Override + public Quaternion setX(float x) + { + return this.x == x ? this : set(x, y, z, w); + } + + @Override + public Quaternion setY(float y) + { + return this.y == y ? this : set(x, y, z, w); + } + + @Override + public Quaternion setZ(float z) + { + return this.z == z ? this : set(x, y, z, w); + } + + @Override + public Quaternion setW(float w) + { + return this.w == w ? this : set(x, y, z, w); + } + + @Override + public float getX() + { + return x; + } + + @Override + public float getY() + { + return y; + } + + @Override + public float getZ() + { + return z; + } + + @Override + public float getW() + { + return w; + } + + @Override + public Quaternion set(float x, float y, float z, float w) + { + return this.x == x && this.y == y && this.z == z && this.w == w ? this : Quaternion.newQuat(x, y, z, w); + } + + @Override + public Quaternion copy() + { + return Quaternion.newQuat(this); + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public int hashCode() + { + return Arrays.hashCode(new float[]{x,y,z,w}); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Quaternion) + { + Quaternion other = (Quaternion)obj; + return other.getX() == x && other.getY() == y && other.getZ() == z && other.getW() == w; + } + return false; + } + + @Override + public String toString() + { + return "Quaternion[x="+x+", y="+y+", z="+z+", w="+w+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/quaternion/QuaternionMutable.java b/src/main/java/speiger/src/coreengine/math/vector/quaternion/QuaternionMutable.java new file mode 100644 index 0000000..61b8d46 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/quaternion/QuaternionMutable.java @@ -0,0 +1,124 @@ +package speiger.src.coreengine.math.vector.quaternion; + +import java.util.Arrays; + +public class QuaternionMutable implements Quaternion +{ + float x; + float y; + float z; + float w; + + public QuaternionMutable() + { + x = 0F; + y = 0F; + z = 0F; + w = 1F; + } + + public QuaternionMutable(float x, float y, float z, float w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + @Override + public Quaternion setX(float x) + { + this.x = x; + return this; + } + + @Override + public Quaternion setY(float y) + { + this.y = y; + return this; + } + + @Override + public Quaternion setZ(float z) + { + this.z = z; + return this; + } + + @Override + public Quaternion setW(float w) + { + this.w = w; + return this; + } + + @Override + public float getX() + { + return x; + } + + @Override + public float getY() + { + return y; + } + + @Override + public float getZ() + { + return z; + } + + @Override + public float getW() + { + return w; + } + + @Override + public Quaternion set(float x, float y, float z, float w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + } + + @Override + public Quaternion copy() + { + return Quaternion.newMutable(this); + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public int hashCode() + { + return Arrays.hashCode(new float[]{x,y,z,w}); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Quaternion) + { + Quaternion other = (Quaternion)obj; + return other.getX() == x && other.getY() == y && other.getZ() == z && other.getW() == w; + } + return false; + } + + @Override + public String toString() + { + return "Quaternion[x="+x+", y="+y+", z="+z+", w="+w+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec2s.java b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec2s.java new file mode 100644 index 0000000..d32573f --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec2s.java @@ -0,0 +1,162 @@ +package speiger.src.coreengine.math.vector.shorts; + +import java.nio.ByteBuffer; +import java.nio.ShortBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec2b; +import speiger.src.coreengine.math.vector.doubles.Vec2d; +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.math.vector.ints.Vec2i; +import speiger.src.coreengine.math.vector.longs.Vec2l; + +public interface Vec2s extends Vecs +{ + public static final Vec2s ZERO = newVec(); + public static final Vec2s MINUS_ONE = newVec((short)-1); + public static final Vec2s ONE = newVec((short)1); + + public static Vec2s newMutable(){return new Vec2sMutable();} + public static Vec2s newMutable(short value){return new Vec2sMutable(value);} + public static Vec2s newMutable(short x, short y){return new Vec2sMutable(x, y);} + public static Vec2s newMutable(Vec2s value){return newMutable(value.getX(), value.getY());} + + public static Vec2s newVec(){return new Vec2sImmutable();} + public static Vec2s newVec(short value){return new Vec2sImmutable(value);} + public static Vec2s newVec(short x, short y){return new Vec2sImmutable(x, y);} + public static Vec2s newVec(Vec2s value){return newVec(value.getX(), value.getY());} + + public short getX(); + public short getY(); + public Vec2s setX(short x); + public Vec2s setY(short y); + @Override + public default short[] asArray(){return new short[]{getX(), getY()};} + @Override + public Vec2s copy(); + @Override + public default Vec2s abs(){return set((short)Math.abs(getX()), (short)Math.abs(getY()));} + @Override + public default Vec2s negate(){return set((short)0, (short)0);} + @Override + public default Vec2s invert(){return set((short)(-getX()), (short)(-getY()));}; + + @Override + public default Vec2s add(short value) {return add(value, value);} + public default Vec2s add(Vec2s value) {return add(value.getX(), value.getY());} + public default Vec2s add(short x, short y) {return set((short)(x + getX()), (short)(y + getY()));} + + @Override + public default Vec2s sub(short value){return sub(value, value);} + public default Vec2s sub(Vec2s value){return sub(value.getX(), value.getY());} + public default Vec2s sub(short x, short y) {return set((short)(getX() - x), (short)(getY() - y));} + + @Override + public default Vec2s multiply(short value){return multiply(value, value);} + public default Vec2s multiply(Vec2s value){return multiply(value.getX(), value.getY());} + public default Vec2s multiply(short x, short y) {return set((short)(x * getX()), (short)(y * getY()));} + + @Override + public default Vec2s devide(short value){return devide(value, value);} + public default Vec2s devide(Vec2s value){return devide(value.getX(), value.getY());} + public default Vec2s devide(short x, short y){return set((short)(getX() / x), (short)(getY() / y));} + + @Override + public default Vec2s set(short value){return set(value, value);}; + public default Vec2s set(Vec2s value){return set(value.getX(), value.getY());} + public Vec2s set(short x, short y); + + public default double distanceTo(Vec2s value){return distanceTo(value.getX(), value.getY());} + public default double distanceTo(short x, short y){return Math.sqrt(distanceToSquared(x, y));} + + public default long distanceToSquared(Vec2s value){return distanceToSquared(value.getX(), value.getY());} + public default long distanceToSquared(short x, short y) + { + long xPos = getX() - x; + long yPos = getY() - y; + return (xPos * xPos) + (yPos * yPos); + } + @Override + public default long lengthSquared() {return (getX() * getX()) + (getY() * getY());} + + public default long dotProduct(Vec2s value){return dotProduct(value.getX(), value.getY());} + public default long dotProduct(short x, short y){return (getX() * x) + (getY() * y);} + + public default Vec2s rotate(short angle, Vec2s center){return rotate(angle, center.getX(), center.getY());} + public default Vec2s rotate(short angle, short x, short y) + { + short xPos = (short)(getX() - x); + short yPos = (short)(getY() - y); + double cos = MathUtils.cos(angle); + double sin = MathUtils.sin(angle); + return set((short)((xPos * cos) + (yPos * sin) + x), (short)(-(xPos * sin) + (yPos * cos) + y)); + } + + public default Vec2s min(Vec2s other) {return min(other, this);} + public default Vec2s min(Vec2s other, Vec2s result){return min(other.getX(), other.getY(), result);} + public default Vec2s min(short x, short y) {return min(x, y, this);} + public default Vec2s min(short x, short y, Vec2s result){return result.set((short)Math.min(getX(), x), (short)Math.min(getY(), y));} + + public default Vec2s max(Vec2s other) {return max(other, this);} + public default Vec2s max(Vec2s other, Vec2s result){return max(other.getX(), other.getY(), result);} + public default Vec2s max(short x, short y) {return max(x, y, this);} + public default Vec2s max(short x, short y, Vec2s result){return result.set((short)Math.max(getX(), x), (short)Math.max(getY(), y));} + + public default Vec2s difference(Vec2s other) {return difference(other, this);} + public default Vec2s difference(Vec2s other, Vec2s result){return difference(other.getX(), other.getY(), result);} + public default Vec2s difference(short x, short y) {return difference(x, y, this);} + public default Vec2s difference(short x, short y, Vec2s result){return result.set((short)(getX() - x), (short)(getY() - y));} + + @Override + public default Vec2s clamp(short min, short max){return clamp(min, max, ALL);} + public default Vec2s clamp(short min, short max, Vec2s result){return clamp(min, max, result, ALL);} + @Override + public default Vec2s clamp(short min, short max, int filter){return clamp(min, max, this, filter);} + public default Vec2s clamp(short min, short max, Vec2s result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()));} + + @Override + public default Vec2s store(ByteBuffer buffer) + { + buffer.putShort(getX()).putShort(getY()); + return this; + } + + @Override + public default Vec2s load(ByteBuffer buffer) + { + return set(buffer.getShort(), buffer.getShort()); + } + + @Override + public default Vec2s store(ShortBuffer buffer) + { + buffer.put(getX()).put(getY()); + return this; + } + + @Override + public default Vec2s load(ShortBuffer buffer) + { + return set(buffer.get(), buffer.get()); + } + + @Override + public default Vec2b asByte(){return isMutable() ? Vec2b.newMutable((byte)getX(), (byte)getY()) : Vec2b.newVec((byte)getX(), (byte)getY());} + @Override + public default Vec2i asInt(){return isMutable() ? Vec2i.newMutable(getX(), getY()) : Vec2i.newVec(getX(), getY());} + @Override + public default Vec2l asLong(){return isMutable() ? Vec2l.newMutable(getX(), getY()) : Vec2l.newVec(getX(), getY());} + @Override + public default Vec2f asFloat() {return isMutable() ? Vec2f.newMutable(getX(), getY()) : Vec2f.newVec(getX(), getY());} + @Override + public default Vec2d asDouble(){return isMutable() ? Vec2d.newMutable(getX(), getY()) : Vec2d.newVec(getX(), getY());} + + @Override + public default Vec2s asMutable(){return isMutable() ? this : newVec(this);} + @Override + public default Vec2s asImmutable(){return isMutable() ? newMutable(this) : this;} + @Override + public default Vec2s copyAsMutable(){return newMutable(this);} + @Override + public default Vec2s copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec2sImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec2sImmutable.java new file mode 100644 index 0000000..121ade3 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec2sImmutable.java @@ -0,0 +1,92 @@ +package speiger.src.coreengine.math.vector.shorts; + +import java.util.Objects; + +public class Vec2sImmutable implements Vec2s +{ + final short x; + final short y; + + public Vec2sImmutable() + { + x = 0; + y = 0; + } + + public Vec2sImmutable(short value) + { + x = value; + y = value; + } + + public Vec2sImmutable(short x, short y) + { + this.x = x; + this.y = y; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public short getX() + { + return x; + } + + @Override + public short getY() + { + return y; + } + + @Override + public Vec2s setX(short x) + { + return this.x == x ? this : Vec2s.newVec(x, y); + } + + @Override + public Vec2s setY(short y) + { + return this.y == y ? this : Vec2s.newVec(x, y); + } + + @Override + public Vec2s copy() + { + return Vec2s.newVec(this); + } + + @Override + public Vec2s set(short x, short y) + { + return this.x == x && this.y == y ? this : Vec2s.newVec(x, y); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec2s) + { + Vec2s vec = (Vec2s)obj; + return vec.getX() == x && vec.getY() == y; + } + return false; + } + + @Override + public String toString() + { + return "Vec2s[x="+x+", y="+y+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec2sMutable.java b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec2sMutable.java new file mode 100644 index 0000000..ada2c0e --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec2sMutable.java @@ -0,0 +1,94 @@ +package speiger.src.coreengine.math.vector.shorts; + +import java.util.Objects; + +public class Vec2sMutable implements Vec2s +{ + short x; + short y; + + public Vec2sMutable() + { + } + + public Vec2sMutable(short value) + { + x = value; + y = value; + } + + public Vec2sMutable(short x, short y) + { + this.x = x; + this.y = y; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public short getX() + { + return x; + } + + @Override + public short getY() + { + return y; + } + + @Override + public Vec2s setX(short x) + { + this.x = x; + return this; + } + + @Override + public Vec2s setY(short y) + { + this.y = y; + return this; + } + + @Override + public Vec2s copy() + { + return Vec2s.newMutable(this); + } + + @Override + public Vec2s set(short x, short y) + { + this.x = x; + this.y = y; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec2s) + { + Vec2s vec = (Vec2s)obj; + return vec.getX() == x && vec.getY() == y; + } + return false; + } + + @Override + public String toString() + { + return "Vec2s[x="+x+", y="+y+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec3s.java b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec3s.java new file mode 100644 index 0000000..b6584cb --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec3s.java @@ -0,0 +1,156 @@ +package speiger.src.coreengine.math.vector.shorts; + +import java.nio.ByteBuffer; +import java.nio.ShortBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec3b; +import speiger.src.coreengine.math.vector.doubles.Vec3d; +import speiger.src.coreengine.math.vector.floats.Vec3f; +import speiger.src.coreengine.math.vector.ints.Vec3i; +import speiger.src.coreengine.math.vector.longs.Vec3l; + +public interface Vec3s extends Vecs +{ + public static final Vec3s ZERO = newVec(); + public static final Vec3s MINUS_ONE = newVec((short)-1); + public static final Vec3s ONE = newVec((short)1); + + public static Vec3s newMutable(){return new Vec3sMutable();} + public static Vec3s newMutable(short value){return new Vec3sMutable(value);} + public static Vec3s newMutable(short x, short y, short z){return new Vec3sMutable(x, y, z);} + public static Vec3s newMutable(Vec3s vec){return newMutable(vec.getX(), vec.getY(), vec.getZ());} + + public static Vec3s newVec(){return new Vec3sImmutable();} + public static Vec3s newVec(short value){return new Vec3sImmutable(value);} + public static Vec3s newVec(short x, short y, short z){return new Vec3sImmutable(x, y, z);} + public static Vec3s newVec(Vec3s vec){return newVec(vec.getX(), vec.getY(), vec.getZ());} + + public short getX(); + public short getY(); + public short getZ(); + public Vec3s setX(short x); + public Vec3s setY(short y); + public Vec3s setZ(short z); + @Override + public default short[] asArray(){return new short[]{getX(), getY(), getZ()};} + + @Override + public Vec3s copy(); + @Override + public default Vec3s abs(){return set((short)Math.abs(getX()), (short)Math.abs(getY()), (short)Math.abs(getZ()));} + @Override + public default Vec3s negate(){return set((short)0, (short)0, (short)0);} + @Override + public default Vec3s invert(){return set((short)-getX(), (short)-getY(), (short)-getZ());} + @Override + public default Vec3s add(short value){return add(value, value, value);} + public default Vec3s add(Vec3s value){return add(value.getX(), value.getY(), value.getZ());} + public default Vec3s add(short x, short y, short z){return set((short)(getX() + x), (short)(getY() + y), (short)(getZ() + z));} + + @Override + public default Vec3s sub(short value){return sub(value, value, value);} + public default Vec3s sub(Vec3s value){return sub(value.getX(), value.getY(), value.getZ());} + public default Vec3s sub(short x, short y, short z){return set((short)(getX() - x), (short)(getY() - y), (short)(getZ() - z));} + + @Override + public default Vec3s multiply(short value){return multiply(value, value, value);} + public default Vec3s multiply(Vec3s value){return multiply(value.getX(), value.getY(), value.getZ());} + public default Vec3s multiply(short x, short y, short z){return set((short)(getX() * x), (short)(getY() * y), (short)(getZ() * z));} + + @Override + public default Vec3s devide(short value){return devide(value, value, value);} + public default Vec3s devide(Vec3s value){return devide(value.getX(), value.getY(), value.getZ());} + public default Vec3s devide(short x, short y, short z){return set((short)(getX() / x), (short)(getY() / y), (short)(getZ() / z));} + + @Override + public default Vec3s set(short value){return set(value, value, value);} + public default Vec3s set(Vec3s value){return set(value.getX(), value.getY(), value.getZ());} + public Vec3s set(short x, short y, short z); + + public default double distanceTo(Vec3s value){return distanceTo(value.getX(), value.getY(), value.getZ());} + public default double distanceTo(short x, short y, short z){return Math.sqrt(distanceToSquared(x, y, z));} + + public default long distanceToSquared(Vec3s value){return distanceToSquared(value.getX(), value.getY(), value.getZ());} + public default long distanceToSquared(short x, short y, short z) + { + long xPos = getX() - x; + long yPos = getY() - y; + long zPos = getZ() - z; + return (xPos * xPos) + (yPos * yPos) + (zPos * zPos); + } + @Override + public default long lengthSquared() {return (getX() * getX()) + (getY() * getY()) + (getZ() * getZ());} + + public default long dotProduct(Vec3s value){return dotProduct(value.getX(), value.getY(), value.getZ());} + public default long dotProduct(short x, short y, short z){return (getX() * x) + (getY() * y) + (getZ() * z);} + + public default Vec3s min(Vec3s other) {return min(other, this);} + public default Vec3s min(Vec3s other, Vec3s result){return min(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3s min(short x, short y, short z) {return min(x, y, z, this);} + public default Vec3s min(short x, short y, short z, Vec3s result){return result.set((short)Math.min(getX(), x), (short)Math.min(getY(), y), (short)Math.min(getZ(), z));} + + public default Vec3s max(Vec3s other) {return max(other, this);} + public default Vec3s max(Vec3s other, Vec3s result){return max(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3s max(short x, short y, short z) {return max(x, y, z, this);} + public default Vec3s max(short x, short y, short z, Vec3s result){return result.set((short)Math.max(getX(), x), (short)Math.max(getY(), y), (short)Math.max(getZ(), z));} + + public default Vec3s difference(Vec3s other) {return difference(other, this);} + public default Vec3s difference(Vec3s other, Vec3s result){return difference(other.getX(), other.getY(), other.getZ(), result);} + public default Vec3s difference(short x, short y, short z) {return difference(x, y, z, this);} + public default Vec3s difference(short x, short y, short z, Vec3s result){return result.set((short)(getX() - x), (short)(getY() - y), (short)(getZ() - z));} + + @Override + public default Vec3s clamp(short min, short max){return clamp(min, max, ALL);} + public default Vec3s clamp(short min, short max, Vec3s result){return clamp(min, max, result, ALL);} + @Override + public default Vec3s clamp(short min, short max, int filter){return clamp(min, max, this, filter);} + public default Vec3s clamp(short min, short max, Vec3s result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()), (filter & Z) == 0 ? getZ() : MathUtils.clamp(min, max, getZ()));} + + @Override + public default Vec3s store(ByteBuffer buffer) + { + buffer.putShort(getX()).putShort(getY()).putShort(getZ()); + return this; + } + + @Override + public default Vec3s load(ByteBuffer buffer) + { + return set(buffer.getShort(), buffer.getShort(), buffer.getShort()); + } + + @Override + public default Vec3s store(ShortBuffer buffer) + { + buffer.put(getX()).put(getY()).put(getZ()); + return this; + } + + @Override + public default Vec3s load(ShortBuffer buffer) + { + return set(buffer.get(), buffer.get(), buffer.get()); + } + + @Override + public default Vec3b asByte(){return isMutable() ? Vec3b.newMutable((byte)getX(), (byte)getY(), (byte)getZ()) : Vec3b.newVec((byte)getX(), (byte)getY(), (byte)getZ());} + @Override + public default Vec3i asInt(){return isMutable() ? Vec3i.newMutable(getX(), getY(), getZ()) : Vec3i.newVec(getX(), getY(), getZ());} + @Override + public default Vec3l asLong(){return isMutable() ? Vec3l.newMutable(getX(), getY(), getZ()) : Vec3l.newVec(getX(), getY(), getZ());} + @Override + public default Vec3f asFloat() {return isMutable() ? Vec3f.newMutable(getX(), getY(), getZ()) : Vec3f.newVec(getX(), getY(), getZ());} + @Override + public default Vec3d asDouble(){return isMutable() ? Vec3d.newMutable(getX(), getY(), getZ()) : Vec3d.newVec(getX(), getY(), getZ());} + + + @Override + public default Vec3s asMutable(){return isMutable() ? this : newVec(this);} + @Override + public default Vec3s asImmutable(){return isMutable() ? newMutable(this) : this;} + @Override + public default Vec3s copyAsMutable(){return newMutable(this);} + @Override + public default Vec3s copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec3sImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec3sImmutable.java new file mode 100644 index 0000000..416eec8 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec3sImmutable.java @@ -0,0 +1,108 @@ +package speiger.src.coreengine.math.vector.shorts; + +import java.util.Objects; + +public class Vec3sImmutable implements Vec3s +{ + final short x; + final short y; + final short z; + + public Vec3sImmutable() + { + x = 0; + y = 0; + z = 0; + } + + public Vec3sImmutable(short value) + { + x = value; + y = value; + z = value; + } + + public Vec3sImmutable(short x, short y, short z) + { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public short getX() + { + return x; + } + + @Override + public short getY() + { + return y; + } + + @Override + public short getZ() + { + return z; + } + + @Override + public Vec3s setX(short x) + { + return this.x == x ? this : Vec3s.newVec(x, y, z); + } + + @Override + public Vec3s setY(short y) + { + return this.y == y ? this : Vec3s.newVec(x, y, z); + } + + @Override + public Vec3s setZ(short z) + { + return this.z == z ? this : Vec3s.newVec(x, y, z); + } + + @Override + public Vec3s copy() + { + return Vec3s.newVec(this); + } + + @Override + public Vec3s set(short x, short y, short z) + { + return this.x == x && this.y == y && this.z == z ? this : Vec3s.newVec(x, y, z); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec3s) + { + Vec3s vec = (Vec3s)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z; + } + return false; + } + + @Override + public String toString() + { + return "Vec3s[x="+x+", y="+y+", z="+z+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec3sMutable.java b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec3sMutable.java new file mode 100644 index 0000000..5c4c243 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec3sMutable.java @@ -0,0 +1,111 @@ +package speiger.src.coreengine.math.vector.shorts; + +import java.util.Objects; + +public class Vec3sMutable implements Vec3s +{ + short x; + short y; + short z; + + public Vec3sMutable() + { + } + + public Vec3sMutable(short value) + { + x = value; + y = value; + z = value; + } + + public Vec3sMutable(short x, short y, short z) + { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public short getX() + { + return x; + } + + @Override + public short getY() + { + return y; + } + + @Override + public short getZ() + { + return z; + } + + @Override + public Vec3s setX(short x) + { + this.x = x; + return this; + } + + @Override + public Vec3s setY(short y) + { + this.y = y; + return this; + } + + @Override + public Vec3s setZ(short z) + { + this.z = z; + return this; + } + + @Override + public Vec3s copy() + { + return Vec3s.newMutable(this); + } + + @Override + public Vec3s set(short x, short y, short z) + { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec3s) + { + Vec3s vec = (Vec3s)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z; + } + return false; + } + + @Override + public String toString() + { + return "Vec3s[x="+x+", y="+y+", z="+z+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec4s.java b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec4s.java new file mode 100644 index 0000000..9aa7ebd --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec4s.java @@ -0,0 +1,159 @@ +package speiger.src.coreengine.math.vector.shorts; + +import java.nio.ByteBuffer; +import java.nio.ShortBuffer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.bytes.Vec4b; +import speiger.src.coreengine.math.vector.doubles.Vec4d; +import speiger.src.coreengine.math.vector.floats.Vec4f; +import speiger.src.coreengine.math.vector.ints.Vec4i; +import speiger.src.coreengine.math.vector.longs.Vec4l; + +public interface Vec4s extends Vecs +{ + public static final Vec4s ZERO = newVec(); + public static final Vec4s MINUS_ONE = newVec((short)-1); + public static final Vec4s ONE = newVec((short)1); + + public static Vec4s newMutable(){return new Vec4sMutable();} + public static Vec4s newMutable(short value){return new Vec4sMutable(value);} + public static Vec4s newMutable(short x, short y, short z, short w){return new Vec4sMutable(x, y, z, w);} + public static Vec4s newMutable(Vec4s vec){return newMutable(vec.getX(), vec.getY(), vec.getZ(), vec.getW());} + + public static Vec4s newVec(){return new Vec4sImmutable();} + public static Vec4s newVec(short value){return new Vec4sImmutable(value);} + public static Vec4s newVec(short x, short y, short z, short w){return new Vec4sImmutable(x, y, z, w);} + public static Vec4s newVec(Vec4s vec){return newVec(vec.getX(), vec.getY(), vec.getZ(), vec.getW());} + + public short getX(); + public short getY(); + public short getZ(); + public short getW(); + public Vec4s setX(short x); + public Vec4s setY(short y); + public Vec4s setZ(short z); + public Vec4s setW(short w); + @Override + public default short[] asArray(){return new short[]{getX(), getY(), getZ(), getW()};} + @Override + public Vec4s copy(); + @Override + public default Vec4s abs(){return set((short)Math.abs(getX()), (short)Math.abs(getY()), (short)Math.abs(getZ()), (short)Math.abs(getW()));} + @Override + public default Vec4s negate(){return set((short)0, (short)0, (short)0, (short)0);} + @Override + public default Vec4s invert(){return set((short)-getX(), (short)-getY(), (short)-getZ(), (short)-getW());} + + @Override + public default Vec4s add(short value){return add(value, value, value, value);} + public default Vec4s add(Vec4s value){return add(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4s add(short x, short y, short z, short w){return set((short)(getX() + x), (short)(getY() + y), (short)(getZ() + z), (short)(getW() + w));} + + @Override + public default Vec4s sub(short value){return sub(value, value, value, value);} + public default Vec4s sub(Vec4s value){return sub(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4s sub(short x, short y, short z, short w){return set((short)(getX() - x), (short)(getY() - y), (short)(getZ() - z), (short)(getW() - w));} + + @Override + public default Vec4s multiply(short value){return multiply(value, value, value, value);} + public default Vec4s multiply(Vec4s value){return multiply(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4s multiply(short x, short y, short z, short w){return set((short)(getX() * x), (short)(getY() * y), (short)(getZ() * z), (short)(getW() * w));} + + @Override + public default Vec4s devide(short value){return devide(value, value, value, value);} + public default Vec4s devide(Vec4s value){return devide(value.getX(), value.getY(), value.getZ(), value.getW());} + public default Vec4s devide(short x, short y, short z, short w){return set((short)(getX() / x), (short)(getY() / y), (short)(getZ() / z), (short)(getW() / w));} + + @Override + public default Vec4s set(short value){return set(value, value, value, value);} + public default Vec4s set(Vec4s value){return set(value.getX(), value.getY(), value.getZ(), value.getW());} + public Vec4s set(short x, short y, short z, short w); + + public default double distanceTo(Vec4s value){return distanceTo(value.getX(), value.getY(), value.getZ(), value.getW());} + public default double distanceTo(short x, short y, short z, short w){return Math.sqrt(distanceTo(x, y, z, w));} + + public default long distanceToSquared(Vec4s value){return distanceToSquared(value.getX(), value.getY(), value.getZ(), value.getW());} + public default long distanceToSquared(short x, short y, short z, short w) + { + long xPos = getX() - x; + long yPos = getY() - y; + long zPos = getZ() - z; + long wPos = getW() - w; + return (xPos * xPos) + (yPos * yPos) + (zPos * zPos) + (wPos * wPos); + } + @Override + public default long lengthSquared() {return (getX() * getX()) + (getY() * getY()) + (getZ() * getZ()) + (getW() * getW());} + + public default long dotProduct(Vec4s value){return dotProduct(value.getX(), value.getY(), value.getZ(), value.getW());} + public default long dotProduct(short x, short y, short z, short w){return (getX() * x) + (getY() * y) + (getZ() * z) + (getW() * w);}; + + public default Vec4s min(Vec4s other) {return min(other, this);} + public default Vec4s min(Vec4s other, Vec4s result){return min(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4s min(short x, short y, short z, short w) {return min(x, y, z, w, this);} + public default Vec4s min(short x, short y, short z, short w, Vec4s result){return result.set((short)Math.min(getX(), x), (short)Math.min(getY(), y), (short)Math.min(getZ(), z), (short)Math.min(getW(), w));} + + public default Vec4s max(Vec4s other) {return max(other, this);} + public default Vec4s max(Vec4s other, Vec4s result){return max(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4s max(short x, short y, short z, short w) {return max(x, y, z, w, this);} + public default Vec4s max(short x, short y, short z, short w, Vec4s result){return result.set((short)Math.max(getX(), x), (short)Math.max(getY(), y), (short)Math.max(getZ(), z), (short)Math.max(getZ(), z));} + + public default Vec4s difference(Vec4s other) {return difference(other, this);} + public default Vec4s difference(Vec4s other, Vec4s result){return difference(other.getX(), other.getY(), other.getZ(), other.getW(), result);} + public default Vec4s difference(short x, short y, short z, short w) {return difference(x, y, z, w, this);} + public default Vec4s difference(short x, short y, short z, short w, Vec4s result){return result.set((short)(getX() - x), (short)(getY() - y), (short)(getZ() - z), (short)(getW() - w));} + + @Override + public default Vec4s clamp(short min, short max){return clamp(min, max, ALL);} + public default Vec4s clamp(short min, short max, Vec4s result){return clamp(min, max, result, ALL);} + @Override + public default Vec4s clamp(short min, short max, int filter){return clamp(min, max, this, filter);} + public default Vec4s clamp(short min, short max, Vec4s result, int filter){ return result.set((filter & X) == 0 ? getX() : MathUtils.clamp(min, max, getX()), (filter & Y) == 0 ? getY() : MathUtils.clamp(min, max, getY()), (filter & Z) == 0 ? getZ() : MathUtils.clamp(min, max, getZ()), (filter & W) == 0 ? getW() : MathUtils.clamp(min, max, getW()));} + + @Override + public default Vec4s store(ByteBuffer buffer) + { + buffer.putShort(getX()).putShort(getY()).putShort(getZ()).putShort(getW()); + return this; + } + + @Override + public default Vec4s load(ByteBuffer buffer) + { + return set(buffer.getShort(), buffer.getShort(), buffer.getShort(), buffer.getShort()); + } + + @Override + public default Vec4s store(ShortBuffer buffer) + { + buffer.put(getX()).put(getY()).put(getZ()).put(getW()); + return this; + } + + @Override + public default Vec4s load(ShortBuffer buffer) + { + return set(buffer.get(), buffer.get(), buffer.get(), buffer.get()); + } + + @Override + public default Vec4b asByte(){return isMutable() ? Vec4b.newMutable((byte)getX(), (byte)getY(), (byte)getZ(), (byte)getW()) : Vec4b.newVec((byte)getX(), (byte)getY(), (byte)getZ(), (byte)getW());} + @Override + public default Vec4i asInt(){return isMutable() ? Vec4i.newMutable(getX(), getY(), getZ(), getW()) : Vec4i.newVec(getX(), getY(), getZ(), getW());} + @Override + public default Vec4l asLong(){return isMutable() ? Vec4l.newMutable(getX(), getY(), getZ(), getW()) : Vec4l.newVec(getX(), getY(), getZ(), getW());} + @Override + public default Vec4f asFloat() {return isMutable() ? Vec4f.newMutable(getX(), getY(), getZ(), getW()) : Vec4f.newVec(getX(), getY(), getZ(), getW());} + @Override + public default Vec4d asDouble(){return isMutable() ? Vec4d.newMutable(getX(), getY(), getZ(), getW()) : Vec4d.newVec(getX(), getY(), getZ(), getW());} + + + @Override + public default Vec4s asMutable(){return isMutable() ? this : newVec(this);} + @Override + public default Vec4s asImmutable(){return isMutable() ? newMutable(this) : this;} + @Override + public default Vec4s copyAsMutable(){return newMutable(this);} + @Override + public default Vec4s copyAsImmutable(){return newVec(this);} +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec4sImmutable.java b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec4sImmutable.java new file mode 100644 index 0000000..0c7763a --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec4sImmutable.java @@ -0,0 +1,124 @@ +package speiger.src.coreengine.math.vector.shorts; + +import java.util.Objects; + +public class Vec4sImmutable implements Vec4s +{ + final short x; + final short y; + final short z; + final short w; + + public Vec4sImmutable() + { + x = 0; + y = 0; + z = 0; + w = 0; + } + + public Vec4sImmutable(short value) + { + x = value; + y = value; + z = value; + w = value; + } + + public Vec4sImmutable(short x, short y, short z, short w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public short getX() + { + return x; + } + + @Override + public short getY() + { + return y; + } + + @Override + public short getZ() + { + return z; + } + + @Override + public short getW() + { + return w; + } + + @Override + public Vec4s setX(short x) + { + return this.x == x ? this : Vec4s.newVec(x, y, z, w); + } + + @Override + public Vec4s setY(short y) + { + return this.y == y ? this : Vec4s.newVec(x, y, z, w); + } + + @Override + public Vec4s setZ(short z) + { + return this.z == z ? this : Vec4s.newVec(x, y, z, w); + } + + @Override + public Vec4s setW(short w) + { + return this.w == w ? this : Vec4s.newVec(x, y, z, w); + } + + @Override + public Vec4s copy() + { + return Vec4s.newVec(this); + } + + @Override + public Vec4s set(short x, short y, short z, short w) + { + return this.x == x && this.y == y && this.z == z && this.w == w ? this : Vec4s.newVec(x, y, z, w); + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z, w); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec4s) + { + Vec4s vec = (Vec4s)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z && vec.getW() == w; + } + return false; + } + + @Override + public String toString() + { + return "Vec4s[x="+x+", y="+y+", z="+z+", w="+w+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec4sMutable.java b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec4sMutable.java new file mode 100644 index 0000000..5046117 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vec4sMutable.java @@ -0,0 +1,128 @@ +package speiger.src.coreengine.math.vector.shorts; + +import java.util.Objects; + +public class Vec4sMutable implements Vec4s +{ + short x; + short y; + short z; + short w; + + public Vec4sMutable() + { + } + + public Vec4sMutable(short value) + { + x = value; + y = value; + z = value; + w = value; + } + + public Vec4sMutable(short x, short y, short z, short w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + @Override + public boolean isMutable() + { + return true; + } + + @Override + public short getX() + { + return x; + } + + @Override + public short getY() + { + return y; + } + + @Override + public short getZ() + { + return z; + } + + @Override + public short getW() + { + return w; + } + + @Override + public Vec4s setX(short x) + { + this.x = x; + return this; + } + + @Override + public Vec4s setY(short y) + { + this.y = y; + return this; + } + + @Override + public Vec4s setZ(short z) + { + this.z = z; + return this; + } + + @Override + public Vec4s setW(short w) + { + this.w = w; + return this; + } + + @Override + public Vec4s copy() + { + return Vec4s.newMutable(this); + } + + @Override + public Vec4s set(short x, short y, short z, short w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y, z, w); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof Vec4s) + { + Vec4s vec = (Vec4s)obj; + return vec.getX() == x && vec.getY() == y && vec.getZ() == z && vec.getW() == w; + } + return false; + } + + @Override + public String toString() + { + return "Vec4s[x="+x+", y="+y+", z="+z+", w="+w+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/math/vector/shorts/Vecs.java b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vecs.java new file mode 100644 index 0000000..2c71c86 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/math/vector/shorts/Vecs.java @@ -0,0 +1,34 @@ +package speiger.src.coreengine.math.vector.shorts; + +import java.nio.ShortBuffer; + +import speiger.src.coreengine.math.vector.Vec; +import speiger.src.coreengine.math.vector.bytes.Vecb; +import speiger.src.coreengine.math.vector.doubles.Vecd; +import speiger.src.coreengine.math.vector.floats.Vecf; +import speiger.src.coreengine.math.vector.ints.Veci; +import speiger.src.coreengine.math.vector.longs.Vecl; + +public interface Vecs extends Vec +{ + public Vecs set(short value); + public Vecs add(short value); + public Vecs sub(short value); + public Vecs multiply(short value); + public Vecs devide(short value); + public Vecs clamp(short min, short max); + public Vecs clamp(short min, short max, int filter); + + public long lengthSquared(); + public default double length(){return Math.sqrt(lengthSquared());} + + public Vecs store(ShortBuffer buffer); + public Vecs load(ShortBuffer buffer); + public short[] asArray(); + + public Vecb asByte(); + public Veci asInt(); + public Vecl asLong(); + public Vecf asFloat(); + public Vecd asDouble(); +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/FontLoader.java b/src/main/java/speiger/src/coreengine/rendering/gui/FontLoader.java new file mode 100644 index 0000000..52a4198 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/FontLoader.java @@ -0,0 +1,118 @@ +package speiger.src.coreengine.rendering.gui; + +import java.io.BufferedReader; +import java.util.HashMap; +import java.util.Map; + +import speiger.src.collections.chars.maps.impl.hash.Char2ObjectOpenHashMap; +import speiger.src.collections.chars.maps.interfaces.Char2ObjectMap; +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.assets.IAsset; +import speiger.src.coreengine.rendering.gui.renderer.FontRenderer; +import speiger.src.coreengine.rendering.gui.renderer.IFontRenderer.CharInstance; +import speiger.src.coreengine.rendering.textures.SimpleTexture; +import speiger.src.coreengine.rendering.textures.TextureManager; + +public class FontLoader +{ + public static FontRenderer createFont(AssetLocation location, String name) + { + return createFont(location, name, 0.35F); + } + + public static FontRenderer createFont(AssetLocation location, String name, float scale) + { + try(IAsset asset = TextureManager.INSTANCE.getManager().getAsset(location.subAsset(name+".fnt"))) + { + Char2ObjectMap[] maps = new Char2ObjectMap[]{new Char2ObjectOpenHashMap(), new Char2ObjectOpenHashMap()}; + BufferedReader reader = asset.getStringReader(); + FontInfo info = new FontInfo(convert(reader.readLine().split(", "))); + String value = null; + while((value = getNextValidLine(reader)) != null) + { + Map dataMap = convert(value.split(", ")); + CharInstance instance = createChar(dataMap, info); + instance.scale(scale); + maps[instance.isBold() ? 1 : 0].putIfAbsent(instance.getCharacter(), instance); + } + info.scale(scale); + return new FontRenderer(maps, new SimpleTexture(location.subAsset(name+"-Texture.png")), info.fontHeight, info.lineHeight); + } + catch(Exception e) + { + e.printStackTrace(); + } + return null; + } + + static String getNextValidLine(BufferedReader reader) throws Exception + { + String line = reader.readLine(); + if(line == null) + { + return null; + } + else if(line.isEmpty() || line.startsWith("//")) + { + return getNextValidLine(reader); + } + return line; + } + + static CharInstance createChar(Map data, FontInfo info) + { + char character = (char)data.get("letter").intValue(); + int minX = data.get("minX"); + int minY = data.get("minY"); + int maxX = data.get("maxX"); + int maxY = data.get("maxY"); + return new CharInstance(character, maxX - minX, maxY - minY, info.getTextureU(minX), info.getTextureV(minY), info.getTextureU(maxX), info.getTextureV(maxY), maxX - minX, data.getOrDefault("bold", 0).intValue() == 1); + } + + static Map convert(String[] data) + { + Map map = new HashMap(); + for(String s : data) + { + int index = s.indexOf("="); + if(index != -1) + { + String[] split = s.split("="); + map.put(split[0], Integer.parseInt(split[1])); + } + } + return map; + } + + public static class FontInfo + { + int height; + int width; + float fontHeight; + float lineHeight; + + public FontInfo(Map data) + { + width = data.get("textureWidth"); + height = data.get("textureHeight"); + fontHeight = data.get("fontHeight"); + lineHeight = data.get("base"); + } + + public void scale(float scale) + { + fontHeight *= scale; + lineHeight *= scale; + } + + public float getTextureU(float value) + { + return value / width; + } + + public float getTextureV(float value) + { + return value / height; + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/GuiBase.java b/src/main/java/speiger/src/coreengine/rendering/gui/GuiBase.java new file mode 100644 index 0000000..b31667b --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/GuiBase.java @@ -0,0 +1,375 @@ +package speiger.src.coreengine.rendering.gui; + +import java.util.function.IntToLongFunction; + +import speiger.src.collections.ints.maps.impl.hash.Int2LongOpenHashMap; +import speiger.src.collections.ints.maps.interfaces.Int2LongMap; +import speiger.src.collections.ints.sets.IntSet; +import speiger.src.coreengine.rendering.gui.base.DebugOverlay; +import speiger.src.coreengine.rendering.gui.base.IButtonComponent; +import speiger.src.coreengine.rendering.gui.base.IKeyComponent; +import speiger.src.coreengine.rendering.gui.helper.Align; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.renderer.FontRenderer; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; +import speiger.src.coreengine.rendering.input.events.MouseEvent; +import speiger.src.coreengine.rendering.input.events.MouseEvent.MouseClickEvent; +import speiger.src.coreengine.rendering.input.events.MouseEvent.MouseMoveEvent; +import speiger.src.coreengine.rendering.input.events.MouseEvent.MouseScrollEvent; +import speiger.src.coreengine.rendering.input.window.ScaledResolution; +import speiger.src.coreengine.rendering.input.window.Window; +import speiger.src.coreengine.utils.profiler.IProfiler; + +public abstract class GuiBase +{ + ButtonMap pressedButtons = new ButtonMap(); + public GuiManager manager; + public int width; + public int height; + boolean focus = false; + public int layers = 0; + int baseLayer = 0; + + GuiBase prevGui; + + public final void setGuiBounds(GuiManager newManager, int screenWidth, int screenHeight) + { + manager = newManager; + width = screenWidth; + height = screenHeight; + } + + public void onInit() + { + + } + + public void onScreenChanged() + { + + } + + public void onFocusLost() + { + + } + + public void onClosed() + { + + } + + public T addComponent(T comp) + { + return addComponent(comp, null); + } + + public T addComponent(int id, T comp) + { + return addComponent(id, comp, null); + } + + public abstract T addComponent(T comp, ComponentConstrains contrains); + + public abstract T addComponent(int id, T comp, ComponentConstrains contrains); + + public T centerComponent(T comp) + { + IGuiBox box = comp.getBox(); + comp.setComponentPosition(Align.CENTER.align(width, box.getWidth()), Align.CENTER.align(height, box.getHeight())); + return comp; + } + + public abstract void addButtonListener(IButtonComponent listener); + + public abstract void addKeyListener(IKeyComponent listener); + + public abstract GuiComponent getComponent(int id); + + public T getCastedComponent(int id) { + GuiComponent component = getComponent(id); + return component == null ? null : component.cast(); + } + + public T getCastedComponent(int id, Class clz) { + GuiComponent component = getComponent(id); + return component == null ? null : component.cast(clz); + } + + public abstract void removeComponent(GuiComponent comp); + + public abstract void removeButtonListener(IButtonComponent listener); + + public abstract void removeKeyListener(IKeyComponent listener); + + protected void addConstrains(GuiComponent comp, ComponentConstrains contrains) + { + comp.constraints = contrains; + if(contrains != null) + { + contrains.setOwner(comp, null); + } + } + + public final void draw(int mouseX, int mouseY, float particalTicks) + { + IProfiler prov = manager.getGPUProfiler(); + prov.start(this instanceof DebugOverlay ? "Debug Overlay" : "Current UI"); + prov.start("Update"); + update(mouseX, mouseY, particalTicks); + prov.next("Render"); + render(mouseX, mouseY, particalTicks); + prov.stop(); + prov.stop(); + } + + public void onFixedUpdate() + { + + } + + protected void update(int mouseX, int mouseY, float particalTicks) + { + + } + + protected void render(int mouseX, int mouseY, float particalTicks) + { + + } + + public boolean isAllowingMovement() + { + return true; + } + + public boolean onKeyTyped(char letter, int keyCode) + { + return false; + } + + public boolean onKeyPressed(int key) + { + return false; + } + + public final void onMouseEvent(MouseEvent evt) + { + if(evt instanceof MouseClickEvent) + { + MouseClickEvent click = (MouseClickEvent)evt; + if(click.press) + { + pressedButtons.add(click.button); + if(onMousePressed(click.button, evt.mouseX, evt.mouseY)) + { + evt.cancel(); + } + } + else + { + pressedButtons.remove(click.button); + if(onMouseReleased(click.button, evt.mouseX, evt.mouseY)) + { + evt.cancel(); + } + } + } + else if(evt instanceof MouseMoveEvent && pressedButtons.size() > 0) + { + if(onMouseDragged(pressedButtons.getButtons(), evt.mouseX, evt.mouseY, pressedButtons)) + { + evt.cancel(); + } + } + else if(evt instanceof MouseScrollEvent) + { + MouseScrollEvent scroll = (MouseScrollEvent)evt; + if(onMouseScroll(evt.mouseX, evt.mouseY, scroll.scrollY)) + { + evt.cancel(); + } + } + } + + public boolean onMousePressed(int button, int mouseX, int mouseY) + { + return false; + } + + public boolean onMouseReleased(int button, int mouseX, int mouseY) + { + return false; + } + + public boolean onMouseDragged(IntSet activeButtons, int mouseX, int mouseY, IntToLongFunction timeMap) + { + return false; + } + + public boolean onMouseScroll(int mouseX, int mouseY, int scrollX) + { + return false; + } + + public boolean isButtonPressed(int mouseButton) + { + return pressedButtons.getButtons().contains(mouseButton); + } + + public long getPressedTime(int mouseButton) + { + return pressedButtons.applyAsLong(mouseButton); + } + + public boolean hasComponentPressed(int mouseButton) + { + return false; + } + + public boolean hasFocus() + { + return focus; + } + + public void requestComponentFocus(GuiComponent comp) + { + } + + public boolean hasComponentInTheWay(GuiComponent comp, int mouseX, int mouseY) + { + return false; + } + + public boolean isComponentInWay(GuiComponent comp, int mouseX, int mouseY) + { + return false; + } + + public boolean isComponentFocused(GuiComponent comp) + { + return false; + } + + public boolean isComponentInFront(GuiComponent comp) + { + return false; + } + + public Window getWindow() + { + return manager.window; + } + + public FontRenderer getFont() + { + return getUIManager().font; + } + + public final long getGlobalClock() + { + return manager.globalClock; + } + + public void closeGui() + { + manager.closeGui(); + } + + public void openGui(GuiBase gui) + { + manager.showGui(gui, false); + } + + public void openGuiWithPrev(GuiBase base) + { + manager.showGui(base, true); + } + + public boolean hasPrevGui() + { + return prevGui != null; + } + + public void setLastGui(GuiBase newLast) + { + prevGui = newLast; + } + + public void openPrevGui() + { + openGui(prevGui); + } + + public void openPrevGui(boolean keepPrev) + { + manager.showGui(prevGui, keepPrev); + } + + public GuiManager getUIManager() + { + return manager; + } + + public UIRenderer getRenderer() + { + return manager.renderer; + } + + public DebugOverlay getOverlay() + { + return manager.getDebug(); + } + + public ScaledResolution getRes() + { + return manager.getRes(); + } + + public final int getBaseLayer() + { + return baseLayer; + } + + public T cast() + { + return (T)this; + } + + public T cast(Class clz) + { + return (T)this; + } + + public static class ButtonMap implements IntToLongFunction + { + Int2LongMap timeMap = new Int2LongOpenHashMap(); + + public void add(int button) + { + timeMap.put(button, System.nanoTime()); + } + + public void remove(int button) + { + timeMap.remove(button); + } + + public int size() + { + return timeMap.size(); + } + + public IntSet getButtons() + { + return timeMap.keySet(); + } + + @Override + public long applyAsLong(int value) + { + return System.nanoTime() - timeMap.get(value); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/GuiComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/GuiComponent.java new file mode 100644 index 0000000..5896268 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/GuiComponent.java @@ -0,0 +1,1115 @@ +package speiger.src.coreengine.rendering.gui; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.function.Consumer; + +import org.lwjgl.glfw.GLFW; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.sets.ObjectLinkedOpenHashSet; +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.math.collision2d.Plane; +import speiger.src.coreengine.math.vector.doubles.Vec2d; +import speiger.src.coreengine.rendering.gui.base.GuiScreenBase; +import speiger.src.coreengine.rendering.gui.base.IButtonComponent; +import speiger.src.coreengine.rendering.gui.base.IKeyComponent; +import speiger.src.coreengine.rendering.gui.components.TextComponent; +import speiger.src.coreengine.rendering.gui.helper.Align; +import speiger.src.coreengine.rendering.gui.helper.animations.Animator; +import speiger.src.coreengine.rendering.gui.helper.box.GuiBox; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.Constrain; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; +import speiger.src.coreengine.rendering.input.Keyboard; +import speiger.src.coreengine.rendering.input.bindings.utils.BindingType; +import speiger.src.coreengine.rendering.input.bindings.utils.ModType; +import speiger.src.coreengine.rendering.input.window.Window; +import speiger.src.coreengine.rendering.utils.Cursor; +import speiger.src.coreengine.rendering.utils.GLUtils; +import speiger.src.coreengine.utils.collections.CollectionUtils; +import speiger.src.coreengine.utils.collections.FlagHolder; + +public abstract class GuiComponent extends FlagHolder +{ + public static final int FLAG_VISIBLE = 1; + public static final int FLAG_ENABLED = 2; + public static final int FLAG_IGNORE_PARENT_BOUNDS = 4; + public static final int FLAG_ALWAYS_CLICKABLE = 8; + public static final int FLAG_RENDER_ORDER = 16; + public static final int FLAG_MANUAL_RENDER = 32; + public static final int FLAG_MASS_CHANGE = 64; + public static final int FLAG_SUPPORT_BINDING = 128; + public static final int FLAG_TEST_SCISSORS = 256; + static final int FLAG_CLOSING = 512; + + public static final int LAST_FLAG = 1 << 19;// This is the last flag and then + // anything behind this is custom + // flags for components + + public static final int LISTENER_USER_ACTION = 0; + public static final int LISTENER_ON_CHANGE = 1; + public static final int LISTENER_CLOSED = 2; + + final IGuiBox box; + GuiBase owner; + GuiComponent parent; + ComponentConstrains constraints = null; + Animator animation = null; + KeyBindAction binding = null; + Set>[] listeners = CollectionUtils.createSets(3, true); + Set children = new ObjectLinkedOpenHashSet<>(); + Set popupChildren = new ObjectLinkedOpenHashSet<>(); + Tooltips tooltips = new Tooltips(); + UUID tooltipId; + float zLevel = 0F; + float totalZ = 0F; + float visiblity = 1F; + float totalVisibility = 1F; + float brightness = 1F; + boolean changed = false; + boolean massRepaint = false; + + public GuiComponent(float x, float y, float width, float height) + { + this(new GuiBox(x, y, width, height)); + } + + public GuiComponent(IGuiBox box) + { + super(FLAG_VISIBLE | FLAG_ENABLED); + this.box = box; + } + + public IGuiBox getBox() + { + return box; + } + + public GuiBase getGui() + { + return owner; + } + + public void calculateActualBounds(float[] area, boolean start) + { + if(start) + { + area[0] = Float.MAX_VALUE; + area[1] = Float.MAX_VALUE; + area[2] = Float.MIN_VALUE; + area[3] = Float.MIN_VALUE; + } + area[0] = Math.min(area[0], box.getMinX()); + area[1] = Math.min(area[1], box.getMinY()); + area[2] = Math.max(area[2], box.getMaxX()); + area[3] = Math.max(area[3], box.getMaxY()); + for(GuiComponent comp : children) + { + if(comp.isVisible()) comp.calculateActualBounds(area, false); + } + } + + public boolean isMouseOver(int mouseX, int mouseY) + { + return (parent == null || isFlagSet(FLAG_IGNORE_PARENT_BOUNDS) || parent.isMouseOver(mouseX, mouseY)) && isOverBox(mouseX, mouseY); + } + + protected boolean isOverBox(int mouseX, int mouseY) + { + return box.isColiding(mouseX, mouseY); + } + + public boolean isHovered(int mouseX, int mouseY) + { + return isParentVisible() && isAnyFlagSet(FLAG_ALWAYS_CLICKABLE | FLAG_ENABLED) && isMouseOver(mouseX, mouseY); + } + + public boolean isTopHovered(int mouseX, int mouseY) + { + if(!isHovered(mouseX, mouseY)) + { + return false; + } + GuiComponent top = getTopComponent(); + return !getGui().hasComponentInTheWay(top, mouseX, mouseY) && top.isChildAtTop(this, mouseX, mouseY); + } + + public boolean isChildAtTop(GuiComponent component, int mouseX, int mouseY) + { + float sourceHeight = component.calculateZLevel(); + if(children.isEmpty()) + { + return true; + } + for(GuiComponent comp : children) + { + if(comp != component && comp.isOverChild(mouseX, mouseY) && (comp.calculateZLevel() > sourceHeight || !comp.isChildAtTop(component, mouseX, mouseY))) + { + return false; + } + } + return true; + } + + public boolean isOverChild(int mouseX, int mouseY) + { + if(children.isEmpty()) + { + return false; + } + for(GuiComponent comp : children) + { + if(comp.isOverChild(mouseX, mouseY) || (comp instanceof IButtonComponent && ((IButtonComponent)comp).isComponentColliding(mouseX, mouseY))) + { + return true; + } + } + return false; + } + + public boolean hasFocus() + { + return getGui().isComponentFocused(getTopComponent()); + } + + public final boolean hasPopups() + { + return popupChildren.size() > 0; + } + + public final void setOwner(GuiBase gui) + { + owner = gui; + for(GuiComponent comp : children) + { + comp.setOwner(gui); + if(comp instanceof IButtonComponent) + { + getGui().addButtonListener((IButtonComponent)comp); + } + if(comp instanceof IKeyComponent) + { + getGui().addKeyListener((IKeyComponent)comp); + } + } + init(); + onComponentChanged(true); + if(binding != null) + { + gui.addKeyListener(binding); + } + } + + public abstract void init(); + + public void onClosed() + { + if(!setFlag(FLAG_CLOSING, true)) + { + return; + } + for(GuiComponent comp : children) + { + comp.onClosed(); + if(comp instanceof IButtonComponent) + { + getGui().removeButtonListener((IButtonComponent)comp); + } + if(comp instanceof IKeyComponent) + { + getGui().removeKeyListener((IKeyComponent)comp); + } + } + children.clear(); + popupChildren.clear(); + box.clearChildren(); + if(binding != null) + { + owner.removeKeyListener(binding); + } + notifyListeners(LISTENER_CLOSED); + clearFlag(FLAG_CLOSING); + } + + public final float getZOffset() + { + return zLevel; + } + + public final float calculateZLevel() + { + return totalZ; + } + + public final float getVisibility() + { + return visiblity; + } + + public final float getTotalVisibility() + { + return totalVisibility; + } + + public final float getBrightness() + { + return brightness; + } + + public final boolean hasConstraints() + { + return constraints != null; + } + + public final boolean isEnabled() + { + return isFlagSet(FLAG_ENABLED); + } + + public final boolean isVisible() + { + return isFlagSet(FLAG_VISIBLE); + } + + public final boolean usesRenderOrder() + { + return isFlagSet(FLAG_RENDER_ORDER); + } + + public final boolean isManualRender() + { + return isFlagSet(FLAG_MANUAL_RENDER); + } + + public boolean isTestingScissors() + { + return isFlagSet(FLAG_TEST_SCISSORS); + } + + public final boolean isParentVisible() + { + return isVisible() && (parent == null || parent.isVisible()); + } + + public final void setMassChanging() + { + setFlag(FLAG_MASS_CHANGE); + } + + public final T setMassChanging(Class clz) + { + setFlag(FLAG_MASS_CHANGE); + return (T)this; + } + + public final GuiComponent finishMassChanging() + { + return finishMassChanging(false); + } + + public final GuiComponent finishMassChanging(boolean quiet) + { + if(isFlagSet(FLAG_MASS_CHANGE)) + { + clearFlag(FLAG_MASS_CHANGE); + if(changed && !quiet) + { + onComponentChanged(massRepaint); + } + } + return this; + } + + public final GuiComponent setEnabled(boolean value) + { + if(!setFlag(FLAG_ENABLED, value)) return this; + for(GuiComponent comp : children) + { + comp.setEnabled(value); + } + return this; + } + + public final GuiComponent setVisible(boolean value) + { + if(!setFlag(FLAG_VISIBLE, value)) return this; + for(GuiComponent comp : children) + { + comp.setVisible(value); + } + return this; + } + + public final GuiComponent setManualRenderer(boolean value) + { + setFlag(FLAG_MANUAL_RENDER, value); + return this; + } + + public GuiComponent setIgnoreBounds(boolean value) + { + setFlag(FLAG_IGNORE_PARENT_BOUNDS, value); + return this; + } + + public GuiComponent setScissorsTest(boolean value) + { + setFlag(FLAG_TEST_SCISSORS, value); + return this; + } + + public final GuiComponent setScale(float value) + { + if(getBox().getBaseScale() != value) + { + getBox().setScale(value); + onComponentChanged(true); + } + return this; + } + + public final GuiComponent setZOffset(float value) + { + zLevel = value; + return this; + } + + public final GuiComponent setVisibilty(float value) + { + if(visiblity == value) return this; + visiblity = value; + updateVisibility(); + return this; + } + + public final GuiComponent setBrightness(float value) + { + brightness = value; + return this; + } + + public final GuiComponent changeVisibility(float value) + { + return value == 1F ? this : setVisibilty(visiblity * value); + } + + protected void updateVisibility() + { + totalVisibility = (parent != null ? parent.totalVisibility : 1F) * visiblity; + for(GuiComponent comp : children) + { + comp.updateVisibility(); + } + } + + public GuiComponent setUserKey(int keyBind){return setUserKey(keyBind, ModType.IGNORE, false);} + public GuiComponent setUserKey(int keyBind, boolean block){return setUserKey(keyBind, ModType.IGNORE, block);} + public GuiComponent setUserKey(int keyBind, int mod){return setUserKey(keyBind, mod, false);} + public GuiComponent setUserKey(int keyBind, int mod, boolean block) + { + if(isFlagNotSet(FLAG_SUPPORT_BINDING)) return this; + if(owner != null) + { + if(binding != null) + { + owner.removeKeyListener(binding); + tooltips.removeTooltip(binding.getTooltip()); + } + binding = new KeyBindAction(keyBind, mod, block); + owner.addKeyListener(binding); + addBindingTooltip(); + return this; + } + binding = new KeyBindAction(keyBind, mod, block); + addBindingTooltip(); + return this; + } + + private void addBindingTooltip() + { + tooltips.addComponent(binding.getTooltip(), new TextComponent(0F, 0F, 200F, 0F, "Key: "+ModType.getMods(binding.mod)+BindingType.KEYBOARD.getName(binding.key)).setLimitedHeight(false).setAlignment(Align.LEFT_TOP, Align.LEFT_TOP).setScale(0.5F)); + } + + protected boolean onUserKey() + { + return false; + } + + public T setRelativeTo(T component) + { + return setRelativeTo(component, Align.CENTER, Align.CENTER); + } + + public T setRelativeTo(T component, Align horizontal, Align vertical) + { + return component.setComponentPosition(box.getMinX() + horizontal.align(box.getWidth(), component.getBox().getWidth()), box.getMinY() + vertical.align(box.getHeight(), component.getBox().getHeight())).cast(); + } + + public T centerComponent(T component) + { + return getGui().centerComponent(component); + } + + public T addChild(T comp) + { + return addChild(comp, null); + } + + public T addChild(T comp, Constrain xPos, Constrain yPos, Constrain width, Constrain height) + { + return addChild(comp, new ComponentConstrains(xPos, yPos, width, height)); + } + + public T addChild(T comp, ComponentConstrains constrains) + { + comp.constraints = constrains; + comp.parent = this; + children.add(comp); + box.addChild(comp.getBox()); + if(constrains != null) + { + constrains.setOwner(comp, this); + constrains.onComponentChanged(); + } + if(owner != null) + { + comp.setOwner(owner); + if(comp instanceof IButtonComponent) + { + owner.addButtonListener((IButtonComponent)comp); + } + if(comp instanceof IKeyComponent) + { + owner.addKeyListener((IKeyComponent)comp); + } + } + return comp; + } + + public T addPopup(T popup) + { + popupChildren.add(popup.addCloseListener(popupChildren::remove)); + getGui().addComponent(popup); + return popup; + } + + protected void addConstrains(GuiComponent comp, ComponentConstrains constrains) + { + comp.constraints = constrains; + if(constrains != null) + { + constrains.setOwner(comp, this); + constrains.onComponentChanged(); + } + } + + public final UUID getTooltipId() + { + return tooltipId; + } + + public Tooltips getTooltips() + { + return tooltips; + } + + public GuiComponent addTooltip(String s, float width) + { + return addTooltip(s, width, 0F); + } + + public GuiComponent addTooltip(String s, float width, float height) + { + return addTooltip(s, width, height, 0.5F); + } + + public GuiComponent addTooltip(String s, float width, float height, float scale) + { + tooltips.addComponent(new TextComponent(0F, 0F, width, height, s).setLimitedHeight(height != 0F).setAlignment(Align.LEFT_TOP, Align.LEFT_TOP).setScale(scale)); + return this; + } + + public GuiComponent addTooltip(GuiComponent comp) + { + tooltips.addComponent(comp); + return this; + } + + public GuiComponent addTooltip(UUID id, GuiComponent comp) + { + tooltips.addComponent(id, comp); + return this; + } + + public boolean containsTooltip(UUID id) + { + return tooltips.contains(id); + } + + public boolean isTooltip() + { + return tooltipId != null; + } + + public GuiComponent removeTooltip(GuiComponent comp) + { + return removeTooltip(comp.getTooltipId()); + } + + public GuiComponent removeTooltip(UUID id) + { + return tooltips.removeTooltip(id); + } + + public List getChildren() + { + return new ObjectArrayList(children); + } + + public GuiComponent getParent() + { + return parent; + } + + public GuiComponent removeChild(GuiComponent comp) + { + comp.onClosed(); + children.remove(comp); + box.removeChild(comp.getBox()); + if(comp instanceof IButtonComponent) + { + owner.removeButtonListener((IButtonComponent)comp); + } + if(comp instanceof IKeyComponent) + { + owner.removeKeyListener((IKeyComponent)comp); + } + return this; + } + + public GuiComponent removeChildren() + { + for(GuiComponent comp : children) + { + comp.onClosed(); + if(comp instanceof IButtonComponent) + { + owner.removeButtonListener((IButtonComponent)comp); + } + if(comp instanceof IKeyComponent) + { + owner.removeKeyListener((IKeyComponent)comp); + } + } + children.clear(); + box.clearChildren(); + return this; + } + + public IGuiBox addBox(IGuiBox box) + { + this.box.addChild(box); + return box; + } + + public GuiComponent removeBox(IGuiBox box) + { + this.box.removeChild(box); + return this; + } + + public final GuiComponent addUserActionListener(Consumer listener) + { + return addListener(listener, GuiComponent.LISTENER_USER_ACTION); + } + + public final GuiComponent addUserActionListener(Runnable listener) + { + return addListener(listener, GuiComponent.LISTENER_USER_ACTION); + } + + public final GuiComponent addChangeListener(Consumer listener) + { + return addListener(listener, GuiComponent.LISTENER_ON_CHANGE); + } + + public final GuiComponent addChangeListener(Runnable listener) + { + return addListener(listener, GuiComponent.LISTENER_ON_CHANGE); + } + + public final GuiComponent addCloseListener(Consumer listener) + { + return addListener(listener, GuiComponent.LISTENER_CLOSED); + } + + public final GuiComponent addCloseListener(Runnable listener) + { + return addListener(listener, GuiComponent.LISTENER_CLOSED); + } + + public final GuiComponent addListener(Runnable runnable, int index) + { + listeners[index].add(T -> runnable.run()); + return this; + } + + public final GuiComponent addListener(Consumer listener, int index) + { + listeners[index].add(listener); + return this; + } + + protected final void notifyListeners(int index) + { + if(listeners[index].size() > 0) + { + for(Consumer comp : listeners[index]) + { + comp.accept(this); + } + } + } + + public final GuiComponent removeUserActionListener(Consumer listener) + { + return removeListener(listener, GuiComponent.LISTENER_USER_ACTION); + } + + public final GuiComponent removeChangeListener(Consumer listener) + { + return removeListener(listener, GuiComponent.LISTENER_ON_CHANGE); + } + + public final GuiComponent removeCloseListener(Consumer listener) + { + return removeListener(listener, GuiComponent.LISTENER_CLOSED); + } + + public final GuiComponent removeListener(Consumer listener, int index) + { + listeners[index].remove(listener); + return this; + } + + public GuiComponent moveComponent(float x, float y) + { + if(x == 0F && y == 0F || constraints != null) return this; + box.move(x, y); + onComponentChanged(false); + return this; + } + + public GuiComponent setComponentPosition(float x, float y) + { + if(box.getBaseX() == x && box.getBaseY() == y || constraints != null) return this; + box.setXY(x, y); + onComponentChanged(false); + return this; + } + + public GuiComponent resizeComponent(float moveX, float moveY) + { + if(moveX == 0F && moveY == 0F || constraints != null) return this; + box.grow(moveX, moveY); + onComponentChanged(true); + return this; + } + + public GuiComponent setComponentBounds(float width, float height) + { + if(box.getBaseWidth() == width && box.getBaseHeight() == height || constraints != null) return this; + box.setBounds(width, height); + onComponentChanged(true); + return this; + } + + public final void onComponentChanged(boolean repaint) + { + if(owner == null) return; + if(isFlagSet(FLAG_MASS_CHANGE)) + { + changed = true; + massRepaint |= repaint; + return; + } + massRepaint = false; + changed = false; + if(constraints != null) + { + constraints.onComponentChanged(); + if(animation != null) animation.applyValues(false); + } + box.onChanged(); + totalZ = 0F; + GuiComponent zComp = this; + while(zComp != null) + { + totalZ += 0.01F + zComp.getZOffset(); + zComp = zComp.parent; + } + notifyListeners(LISTENER_ON_CHANGE); + updateState(); + if(repaint) repaint(); + if(children.isEmpty()) return; + for(GuiComponent comp : children) + { + comp.onComponentChanged(repaint); + } + } + + protected void updateState() + { + + } + + protected void repaint() + { + + } + + public final void onFixedUpdate() + { + if(fixedUpdateSelf()) fixedUpdateChildren(); + } + + public final void onUpdate(int mouseX, int mouseY, float particalTicks) + { + if(animation != null) animation.update(particalTicks); + if(updateSelf(mouseX, mouseY, particalTicks)) updateChildren(mouseX, mouseY, particalTicks); + } + + protected void onPreRender() + { + + } + + public final void onRender(int mouseX, int mouseY, float particalTicks) + { + onPreRender(); + getRenderer().setVisibility(totalVisibility).setBrightness(brightness); + if(renderSelf(mouseX, mouseY, particalTicks)) renderChildren(mouseX, mouseY, particalTicks); + onPostRender(); + getRenderer().resetEffects(); + if(getGui() instanceof GuiScreenBase) + { + ((GuiScreenBase)getGui()).drawBox(this); + } + } + + protected void onPostRender() + { + + } + + protected boolean fixedUpdateSelf() + { + return true; + } + + protected boolean updateSelf(int mouseX, int mouseY, float particalTicks) + { + return true; + } + + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + return true; + } + + public void collectTooltips(int mouseX, int mouseY, float particalTicks, Map collector) + { + if(isParentVisible()) + { + if(isHovered(mouseX, mouseY)) + { + tooltips.merge(collector); + } + if(children.size() > 0) + { + for(GuiComponent entry : children) + { + entry.collectTooltips(mouseX, mouseY, particalTicks, collector); + } + } + } + } + + public void fixedUpdateChildren() + { + for(GuiComponent entry : children) + { + if(entry.isVisible()) + { + entry.onFixedUpdate(); + } + } + } + + public void updateChildren(int mouseX, int mouseY, float particalTicks) + { + for(GuiComponent entry : children) + { + if(entry.isVisible()) + { + entry.onUpdate(mouseX, mouseY, particalTicks); + } + } + } + + public void renderChildren(int mouseX, int mouseY, float particalTicks) + { + for(GuiComponent entry : children) + { + if(!entry.isManualRender() && entry.isVisible() && (!isTestingScissors() || isInScissors(entry.getBox()))) + { + float zOffset = entry.getZOffset() + 0.01F; + getRenderer().push(); + getRenderer().translate(0F, 0F, zOffset); + entry.onPreRender(); + entry.onRender(mouseX, mouseY, particalTicks); + entry.onPostRender(); + getRenderer().translate(0F, 0F, -zOffset); + getRenderer().pop(); + } + } + } + + public Animator getAnimator() + { + if(animation == null) + { + animation = new Animator(this); + } + return animation; + } + + public T cast() + { + return (T)this; + } + + public T cast(Class clz) + { + return (T)this; + } + + public T tryCast(Class clz) + { + return clz.isInstance(this) ? (T)this : null; + } + + public GuiComponent getTopComponent() + { + GuiComponent top = this; + while(top.parent != null) + { + top = top.parent; + } + return top; + } + + protected void requestFocus() + { + getGui().requestComponentFocus(this); + } + + protected boolean isFocused() + { + return getGui().isComponentFocused(this); + } + + protected boolean isFocusedOrChilds() + { + return isFocused() || isChildFocused(); + } + + protected boolean isChildFocused() + { + for(GuiComponent comp : children) + { + if(comp.isFocusedOrChilds()) + { + return true; + } + } + return false; + } + + protected UIRenderer getRenderer() + { + return owner.getRenderer(); + } + + protected Window getWindow() + { + return owner.getWindow(); + } + + protected boolean isSelect(int keyCode) + { + return keyCode == GLFW.GLFW_KEY_A && Keyboard.isCtrlDown() && !Keyboard.isShiftDown() && !Keyboard.isAltDown(); + } + + protected boolean isCopy(int keyCode) + { + return keyCode == GLFW.GLFW_KEY_C && Keyboard.isCtrlDown() && !Keyboard.isShiftDown() && !Keyboard.isAltDown(); + } + + protected boolean isPaste(int keyCode) + { + return keyCode == GLFW.GLFW_KEY_V && Keyboard.isCtrlDown() && !Keyboard.isShiftDown() && !Keyboard.isAltDown(); + } + + protected boolean isCut(int keyCode) + { + return keyCode == GLFW.GLFW_KEY_X && Keyboard.isCtrlDown() && !Keyboard.isShiftDown() && !Keyboard.isAltDown(); + } + + protected final float getBrightness(int mouseX, int mouseY) + { + return isEnabled() ? (isHovered(mouseX, mouseY) ? 0.7F : 1F) : 0.5F; + } + + public final float getActiveBrightness() + { + return isEnabled() ? 1F : 0.5F; + } + + public long getGlobalClock() + { + return getGui().getGlobalClock(); + } + + protected final void bindCursor(AssetLocation location) + { + Cursor.INSTANCE.bindCursor(location, getWindow()); + } + + protected final void clearCursor() + { + Cursor.INSTANCE.clearCursor(getWindow()); + } + + protected final void enableScissors(Plane box) + { + enableScissors(box.getMinX(), box.getMinY(), box.getWidth(), box.getHeight()); + } + + protected final void enableScissors(IGuiBox box) + { + enableScissors((int)box.getMinX(), (int)box.getMinY(), (int)box.getWidth(), (int)box.getHeight()); + } + + protected final void enableScissorsBox(float minX, float minY, float maxX, float maxY) + { + enableScissors((int)minX, (int)minY, (int)(maxX - minX), (int)(maxY - minY)); + } + + protected final void enableScissors(float x, float y, float width, float height) + { + enableScissors((int)x, (int)y, (int)width, (int)height); + } + + protected final void enableScissors(int x, int y, int width, int height) + { + getRenderer().flush(); + int bottom = y + height; + Window window = owner.getWindow(); + Vec2d vec = owner.getUIManager().res.getScaleVec(); + GLUtils.TESTER.enableScissors((int)(x * vec.getX()), (int)(window.getHeight() - bottom * vec.getY()), (int)(width * vec.getX()), (int)(height * vec.getY())); + } + + protected final boolean isInScissors(Plane box) + { + return isInScissors(box.getMinX(), box.getMinY(), box.getWidth(), box.getHeight()); + } + + protected final boolean isInScissors(IGuiBox box) + { + return isInScissors((int)box.getMinX(), (int)box.getMinY(), (int)box.getWidth(), (int)box.getHeight()); + } + + protected final boolean isInScissors(float minX, float minY, float maxX, float maxY) + { + return isInScissors((int)minX, (int)minY, (int)(maxX - minX), (int)(maxY - minY)); + } + + protected final boolean isInScissors(int x, int y, int width, int height) + { + int bottom = y + height; + Window window = owner.getWindow(); + Vec2d vec = owner.getUIManager().res.getScaleVec(); + return GLUtils.TESTER.isInScissors((int)(x * vec.getX()), (int)(window.getHeight() - bottom * vec.getY()), (int)(width * vec.getX()), (int)(height * vec.getY())); + } + + public final void disableScissors() + { + getRenderer().flush(); + GLUtils.TESTER.disableScissors(); + } + + class KeyBindAction implements IKeyComponent + { + int key; + int mod; + boolean block; + UUID tooltip = UUID.randomUUID(); + + public KeyBindAction(int key, int mod, boolean block) + { + this.key = key; + this.mod = mod; + this.block = block; + } + + public UUID getTooltip() + { + return tooltip; + } + + @Override + public boolean isAcceptingInput() + { + return isAnyFlagSet(FLAG_ALWAYS_CLICKABLE | FLAG_ENABLED); + } + + @Override + public boolean isBlockingMovement() + { + return block; + } + + @Override + public boolean isPopup() + { + return isPopupButton(GuiComponent.this) || isPopupButton(getTopComponent()); + } + + @Override + public boolean hasChildPopups() + { + return hasPopups(); + } + + private boolean isPopupButton(GuiComponent comp) + { + return comp instanceof IButtonComponent ? ((IButtonComponent)comp).isPopup() : false; + } + + @Override + public boolean onKeyPressed(int key) + { + if(key == this.key && ModType.isActive(mod)) + { + return onUserKey(); + } + return false; + } + + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/GuiManager.java b/src/main/java/speiger/src/coreengine/rendering/gui/GuiManager.java new file mode 100644 index 0000000..c07bd06 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/GuiManager.java @@ -0,0 +1,275 @@ +package speiger.src.coreengine.rendering.gui; + +import org.lwjgl.opengl.GL11; + +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.math.vector.ints.Vec2i; +import speiger.src.coreengine.rendering.gui.base.DebugOverlay; +import speiger.src.coreengine.rendering.gui.renderer.FontRenderer; +import speiger.src.coreengine.rendering.gui.renderer.GuiShader; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; +import speiger.src.coreengine.rendering.input.events.KeyEvent.CharTypeEvent; +import speiger.src.coreengine.rendering.input.events.KeyEvent.KeyPressEvent; +import speiger.src.coreengine.rendering.input.events.MouseEvent; +import speiger.src.coreengine.rendering.input.window.IWindowListener; +import speiger.src.coreengine.rendering.input.window.ScaledResolution; +import speiger.src.coreengine.rendering.input.window.Window; +import speiger.src.coreengine.rendering.models.UniformBuffer; +import speiger.src.coreengine.rendering.shader.ShaderTracker; +import speiger.src.coreengine.rendering.utils.Cursor; +import speiger.src.coreengine.rendering.utils.GLUtils; +import speiger.src.coreengine.utils.eventbus.EventBus; +import speiger.src.coreengine.utils.profiler.IProfiler; + +public abstract class GuiManager implements IWindowListener +{ + protected GuiBase[] activeGuis = new GuiBase[2]; + protected GuiBase gui; + protected DebugOverlay debug; + protected UIRenderer renderer = new UIRenderer(this); + protected Window window; + protected ScaledResolution res; + protected long globalClock = 0L; + protected boolean isReloading = false; + protected FontRenderer font = FontLoader.createFont(AssetLocation.of("font"), "Roboto-Font"); + protected GuiShader shader = ShaderTracker.INSTANCE.register(GuiShader::create, T -> shader = T); + + public GuiManager(Window window, EventBus bus) + { + this.window = window; + bus.register(MouseEvent.class, this::onMouseEvent); + bus.register(KeyPressEvent.class, (T) -> T.setCanceled(onKeyPressed(T.key))); + bus.register(CharTypeEvent.class, (T) -> T.setCanceled(onCharTyped(T.character, T.codePoint))); + window.addListener(this, false); + debug = createOverlay(); + activeGuis[1] = debug; + res = window.getUIFrame(); + debug.setGuiBounds(this, res.getScaledWidth(), res.getScaledHeight()); + debug.onInit(); + } + + public abstract IProfiler getGPUProfiler(); + public abstract IProfiler getCPUProfiler(); + public abstract IProfiler getServerProfiler(); + public abstract UniformBuffer getOrthoMatrixBuffer(); + public abstract DebugOverlay createOverlay(); + + public void showGui(GuiBase base, boolean allowBack) + { + if(allowBack) + { + base.setLastGui(gui); + } + closeGui(); + gui = base; + if(gui != null) + { + gui.setGuiBounds(this, res.getScaledWidth(), res.getScaledHeight()); + gui.onInit(); + if(activeGuis[0] != null) + { + activeGuis[1] = activeGuis[0]; + } + activeGuis[0] = base; + } + if(activeGuis[0] != null) + { + activeGuis[0].focus = true; + } + if(activeGuis[1] != null) + { + activeGuis[1].focus = false; + } + } + + public void closeGui() + { + if(gui != null) + { + if(gui == activeGuis[0]) + { + activeGuis[0] = null; + } + if(gui == activeGuis[1]) + { + activeGuis[1] = null; + } + gui.onClosed(); + gui = null; + } + } + + @Override + public void onWindowChanged(Window window) + { + if(gui != null) + { + gui.setGuiBounds(this, res.getScaledWidth(), res.getScaledHeight()); + gui.onScreenChanged(); + } + debug.setGuiBounds(this, res.getScaledWidth(), res.getScaledHeight()); + debug.onScreenChanged(); + } + + public void onFixedUpdate() + { + globalClock++; + for(int i = 0;i < 2;i++) + { + if(activeGuis[i] != null) + { + activeGuis[i].onFixedUpdate(); + } + } + } + + public void render(float particalTicks) + { + Vec2i offsetMouse = start(); + if(debug.isUpdating() || isReloading) + { + isReloading = false; + debug.setGuiBounds(this, res.getScaledWidth(), res.getScaledHeight()); + debug.onScreenChanged(); + if(gui != null) + { + gui.setGuiBounds(this, res.getScaledWidth(), res.getScaledHeight()); + gui.onScreenChanged(); + } + } + if(activeGuis[0] != null) + { + activeGuis[0].baseLayer = activeGuis[1] != null ? activeGuis[1].layers : 0; + } + for(int i = 1;i >= 0;i--) + { + if(activeGuis[i] != null && (activeGuis[i] instanceof DebugOverlay || debug.isRendering())) + { + activeGuis[i].draw(offsetMouse.getX(), offsetMouse.getY(), particalTicks); + } + } + stop(); + Cursor.INSTANCE.clearCursor(window); + } + + protected Vec2i start() + { + GLUtils.DEBTH_TEST.push(false); + GLUtils.CULL_FACE.push(false); + GLUtils.BLEND.setFunction(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA).push(true); + renderer.beginFrame(); + return res.getScaledMouse(); + } + + protected void stop() + { + GLUtils.DEBTH_TEST.pop(); + GLUtils.DEBTH_TEST.push(true); + renderer.endFrame(); + GLUtils.BLEND.pop(); + GLUtils.CULL_FACE.pop(); + ShaderTracker.INSTANCE.stopShader(); + } + + public boolean onCharTyped(char character, int codePoint) + { + for(int i = 0;i < 2;i++) + { + if(activeGuis[i] != null && debug.shouldAcceptInputs(activeGuis[i]) && activeGuis[i].onKeyTyped(character, codePoint)) + { + return true; + } + } + return false; + } + + public boolean onKeyPressed(int key) + { + for(int i = 0;i < 2;i++) + { + if(activeGuis[i] != null && debug.shouldAcceptInputs(activeGuis[i]) && activeGuis[i].onKeyPressed(key)) + { + return true; + } + } + return false; + } + + public void onMouseEvent(MouseEvent event) + { + if(!onMouseEventInternal(event)) + { + return; + } + if(activeGuis[0] != null && !event.isCanceled() && debug.shouldAcceptInputs(activeGuis[0])) + { + activeGuis[0].onMouseEvent(event); + } + if(activeGuis[1] != null && !event.isCanceled() && debug.shouldAcceptInputs(activeGuis[1])) + { + activeGuis[1].onMouseEvent(event); + if(event.isCanceled()) + { + GuiBase base = activeGuis[0]; + activeGuis[0] = activeGuis[1]; + activeGuis[1] = base; + if(base != null) + { + base.onFocusLost(); + base.baseLayer = 0; + base.layers = 0; + } + if(activeGuis[0] != null) + { + activeGuis[0].focus = true; + } + if(activeGuis[1] != null) + { + activeGuis[1].focus = false; + } + } + } + event.convertToOrigin(); + } + + protected boolean onMouseEventInternal(MouseEvent event) + { + event.convertToScreenCoords(res); + return true; + } + + public boolean isAllowingMovement() + { + return gui == null || gui.isAllowingMovement(); + } + + public DebugOverlay getDebug() + { + return debug; + } + + public boolean isRenderUIBoxes() + { + return false; + } + + public GuiShader getShader() + { + return shader; + } + + public UIRenderer getRenderer() + { + return renderer; + } + + public FontRenderer getFont() + { + return font; + } + + public ScaledResolution getRes() + { + return res; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/Tooltips.java b/src/main/java/speiger/src/coreengine/rendering/gui/Tooltips.java new file mode 100644 index 0000000..8808a15 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/Tooltips.java @@ -0,0 +1,49 @@ +package speiger.src.coreengine.rendering.gui; + +import java.util.Map; +import java.util.UUID; + +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap; + +public class Tooltips +{ + Map components = new Object2ObjectLinkedOpenHashMap<>(); + + public T addComponent(T comp) + { + return addComponent(UUID.randomUUID(), comp); + } + + public T addComponent(UUID id, T comp) + { + if(comp.tooltipId != null) throw new IllegalStateException("A Tooltip can not be added to another container"); + components.put(id, comp); + comp.tooltipId = id; + return comp; + } + + public boolean contains(UUID id) + { + return components.containsKey(id); + } + + public GuiComponent removeTooltip(UUID id) + { + GuiComponent comp = components.remove(id); + if(comp != null) comp.tooltipId = null; + return comp; + } + + public void merge(Map collector) + { + collector.putAll(components); + } + + public void clear() + { + for(GuiComponent comp : components.values()) + { + comp.tooltipId = null; + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/UITextures.java b/src/main/java/speiger/src/coreengine/rendering/gui/UITextures.java new file mode 100644 index 0000000..b73b9af --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/UITextures.java @@ -0,0 +1,19 @@ +package speiger.src.coreengine.rendering.gui; + +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.rendering.textures.ITexture; +import speiger.src.coreengine.rendering.textures.SimpleTexture; + +public class UITextures +{ + public static final AssetLocation TEXTURE_LOCATION = AssetLocation.of("textures"); + +// public static final ITexture OK_SYMBOL = new SimpleTexture(sub("okSymbol.png")); +// public static final ITexture CANCLE_SYMBOL = new SimpleTexture(sub("cancelSymbol.png")); + public static final ITexture COLOR_WHEEL = new SimpleTexture(sub("colorWheel.png")); + + public static AssetLocation sub(String name) + { + return TEXTURE_LOCATION.subAsset(name); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/base/DebugOverlay.java b/src/main/java/speiger/src/coreengine/rendering/gui/base/DebugOverlay.java new file mode 100644 index 0000000..1a410c7 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/base/DebugOverlay.java @@ -0,0 +1,19 @@ +package speiger.src.coreengine.rendering.gui.base; + +import speiger.src.coreengine.rendering.gui.GuiBase; + +public abstract class DebugOverlay extends GuiScreenBase +{ + public abstract boolean toggleFPS(); + public abstract void toggleUI(); + public abstract void toggleUpdate(); + public abstract void toggleDebug(); + public abstract boolean isUpdating(); + public abstract boolean isRendering(); + + public boolean shouldAcceptInputs(GuiBase base) + { + return base instanceof DebugOverlay || isRendering(); + } + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/base/GuiScreenBase.java b/src/main/java/speiger/src/coreengine/rendering/gui/base/GuiScreenBase.java new file mode 100644 index 0000000..20928b5 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/base/GuiScreenBase.java @@ -0,0 +1,511 @@ +package speiger.src.coreengine.rendering.gui.base; + +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.function.IntToLongFunction; + +import speiger.src.collections.ints.maps.impl.hash.Int2ObjectLinkedOpenHashMap; +import speiger.src.collections.ints.maps.impl.hash.Int2ObjectOpenHashMap; +import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap; +import speiger.src.collections.ints.maps.interfaces.Int2ObjectSortedMap; +import speiger.src.collections.ints.sets.IntSet; +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.maps.impl.hash.Object2BooleanLinkedOpenHashMap; +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap; +import speiger.src.collections.objects.maps.interfaces.Object2BooleanSortedMap; +import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap; +import speiger.src.collections.objects.sets.ObjectLinkedOpenHashSet; +import speiger.src.collections.objects.sets.ObjectSortedSet; +import speiger.src.collections.objects.utils.maps.Object2ObjectMaps; +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiBase; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.components.TooltipPanel; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; +import speiger.src.coreengine.utils.collections.iterators.IterableWrapper; + +public class GuiScreenBase extends GuiBase +{ + Int2ObjectMap getters = new Int2ObjectOpenHashMap<>(); + Set components = new ObjectLinkedOpenHashSet<>(); + Set keyOrder = new ObjectLinkedOpenHashSet<>(); + ObjectSortedSet renderOrder = new ObjectLinkedOpenHashSet<>(); + Object2BooleanSortedMap buttonOrder = new Object2BooleanLinkedOpenHashMap<>(); + Int2ObjectSortedMap selectedButtons = new Int2ObjectLinkedOpenHashMap<>(); + Set draggingButtons = new ObjectLinkedOpenHashSet<>(); + TooltipPanel tooltips = new TooltipPanel(); + int lastMouseX = -1; + int lastMouseY = -1; + long lastTooltipCheck = 0; + boolean drawsTooltip = false; + long delay = 200L; + + @Override + public void onInit() + { + super.onInit(); + addComponent(tooltips); + } + + public void setDelay(long delay, TimeUnit unit) + { + this.delay = Math.max(0, unit.toMillis(delay)); + } + + @Override + public T addComponent(T comp, ComponentConstrains contrains) + { + components.add(comp); + renderOrder.addAndMoveToLast(comp); + addConstrains(comp, contrains); + comp.setOwner(this); + if(comp instanceof IButtonComponent) + { + buttonOrder.put((IButtonComponent)comp, true); + addButtonListener((IButtonComponent)comp); + } + if(comp instanceof IKeyComponent) + { + addKeyListener((IKeyComponent)comp); + } + return comp; + } + + @Override + public T addComponent(int id, T comp, ComponentConstrains contrains) + { + getters.put(id, comp); + return addComponent(comp, contrains); + } + + @Override + public void addButtonListener(IButtonComponent listener) + { + buttonOrder.putAndMoveToFirst(listener, false); + } + + @Override + public void addKeyListener(IKeyComponent listener) + { + keyOrder.add(listener); + } + + @Override + public GuiComponent getComponent(int id) + { + return getters.get(id); + } + + @Override + public void removeComponent(GuiComponent comp) + { + if(comp != null) + { + comp.onClosed(); + } + components.remove(comp); + renderOrder.remove(comp); + getters.values().remove(comp); + if(comp instanceof IButtonComponent) + { + removeButtonListener((IButtonComponent)comp); + } + if(comp instanceof IKeyComponent) + { + removeKeyListener((IKeyComponent)comp); + } + } + + @Override + public void removeButtonListener(IButtonComponent listener) + { + buttonOrder.rem(listener); + } + + @Override + public void removeKeyListener(IKeyComponent listener) + { + keyOrder.remove(listener); + } + + @Override + public void onClosed() + { + super.onClosed(); + for(GuiComponent entry : components) + { + entry.onClosed(); + } + getters.clear(); + renderOrder.clear(); + buttonOrder.clear(); + } + + @Override + public void onScreenChanged() + { + super.onScreenChanged(); + for(GuiComponent entry : components) + { + entry.onComponentChanged(true); + } + } + + @Override + public void onFixedUpdate() + { + for(GuiComponent entry : renderOrder) + { + if(entry.isVisible()) + { + entry.onFixedUpdate(); + } + } + } + + @Override + protected void update(int mouseX, int mouseY, float particalTicks) + { + for(GuiComponent entry : renderOrder) + { + if(entry.isVisible()) + { + entry.onUpdate(mouseX, mouseY, particalTicks); + } + } + } + + @Override + protected void render(int mouseX, int mouseY, float particalTicks) + { + layers = (1 + getBaseLayer()); + UIRenderer render = getRenderer(); + float biggestZ = 0.0F; + float extra = 1.0F; + Object2ObjectMap components = hasFocus() ? new Object2ObjectLinkedOpenHashMap<>() : Object2ObjectMaps.empty(); + for(GuiComponent base : renderOrder) + { + if(base.isVisible() && !base.isManualRender()) + { + float z = base.getZOffset(); + boolean layer = base.usesRenderOrder(); + render.translate(0.0F, 0.0F, layers + z + (layer ? extra : 0.0F)); + base.onRender(mouseX, mouseY, particalTicks); + render.resetTransform(); + biggestZ = Math.max(biggestZ, z); + if(layer) + { + extra += 1.0F; + } + } + if(hasFocus()) + { + base.collectTooltips(mouseX, mouseY, particalTicks, components); + } + } + layers += MathUtils.floor(biggestZ + extra); + if(hasFocus()) + { + if(!drawsTooltip && (lastMouseX != mouseX || lastMouseY != mouseY) || components.isEmpty()) + { + lastTooltipCheck = System.currentTimeMillis(); + lastMouseX = mouseX; + lastMouseY = mouseY; + if(components.isEmpty()) + { + tooltips.updateTooltips(components); + drawsTooltip = false; + } + } + else if(System.currentTimeMillis() - lastTooltipCheck >= delay) + { + drawsTooltip = true; + tooltips.updateTooltips(components); + tooltips.setComponentPosition(mouseX+tooltips.isOutsideScreen(mouseX, width), mouseY); + render.translate(0.0F, 0.0F, layers + 50F); + tooltips.onRender(mouseX, mouseY, particalTicks); + render.resetTransform(); + } + } + render.resetEffects(); + } + + public void drawBox(GuiComponent comp) + { + if(!getUIManager().isRenderUIBoxes()) return; + UIRenderer render = getRenderer(); + render.translate(0F, 0F, 100F); + render.drawFrame(comp.getBox(), ColorObject.RED); + render.translate(0F, 0F, -100F); + } + + @Override + public boolean isAllowingMovement() + { + for(IKeyComponent comp : findKeyPopups()) + { + if(comp.isAcceptingInput() && comp.isBlockingMovement()) + { + return false; + } + } + return true; + } + + @Override + public boolean onKeyTyped(char letter, int keyCode) + { + for(IKeyComponent comp : findKeyPopups()) + { + if(comp.isAcceptingInput() && comp.onKeyTyped(letter, keyCode)) + { + return true; + } + } + return false; + } + + @Override + public boolean onKeyPressed(int key) + { + for(IKeyComponent comp : findKeyPopups()) + { + if(comp.isAcceptingInput() && comp.onKeyPressed(key)) + { + return true; + } + } + return false; + } + + @Override + public boolean onMousePressed(int button, int mouseX, int mouseY) + { + if(buttonOrder.isEmpty()) return false; + List components = new ObjectArrayList<>(); + for(Iterator iter = findPopups();iter.hasNext();) + { + IButtonComponent base = iter.next(); + if(!base.isValidButton(button)) continue; + if(!base.isComponentColliding(mouseX, mouseY)) + { + components.add(base); + continue; + } + if(!base.onClick(button, mouseX, mouseY)) continue; + buttonOrder.getAndMoveToFirst(base); + selectedButtons.put(button, base); + draggingButtons.add(base); + GuiComponent top = getTopComponent(base); + if(base.canMoveIntoForground()) + { + if(buttonOrder.getAndMoveToFirst(base) && base instanceof GuiComponent) + { + requestComponentFocus(((GuiComponent)base).getTopComponent()); + } + else + { + requestComponentFocus(top); + } + } + else if(top instanceof IButtonComponent && ((IButtonComponent)top).canMoveIntoForground()) + { + requestComponentFocus(top); + } + for(int i = 0,m=components.size();i findPopups() + { + List popups = new ObjectArrayList(); + for(IButtonComponent button : buttonOrder.keySet()) + { + if(button.isPopup() && !button.hasChildPopups()) + { + popups.add(button); + continue; + } + GuiComponent comp = getTopComponent(button); + if(comp instanceof IButtonComponent && ((IButtonComponent)comp).isPopup() && !((IButtonComponent)comp).hasChildPopups()) + { + popups.add(button); + } + } + return sortByHeight(popups.isEmpty() ? new ObjectArrayList(buttonOrder.keySet()) : popups); + } + + protected Iterator sortByHeight(List comp) + { + comp.sort(ButtonSorter.SORTER); + return comp.iterator(); + } + + protected Set findKeyPopups() + { + Set components = new ObjectLinkedOpenHashSet(); + for(IKeyComponent key : keyOrder) + { + if(key.isPopup() && !key.hasChildPopups()) + { + components.add(key); + continue; + } + GuiComponent comp = getTopComponent(key); + if((comp instanceof IKeyComponent && ((IKeyComponent)comp).isPopup() && !((IKeyComponent)comp).hasChildPopups()) || (comp instanceof IButtonComponent && ((IButtonComponent)comp).isPopup()) && !((IButtonComponent)comp).hasChildPopups()) + { + components.add(key); + } + } + return components.isEmpty() ? keyOrder : components; + } + + protected GuiComponent getTopComponent(IKeyComponent button) + { + return button instanceof GuiComponent ? ((GuiComponent)button).getTopComponent() : null; + } + + protected GuiComponent getTopComponent(IButtonComponent button) + { + return button instanceof GuiComponent ? ((GuiComponent)button).getTopComponent() : null; + } + + @Override + public void requestComponentFocus(GuiComponent comp) + { + if(!comp.hasPopups()) + { + renderOrder.moveToLast(comp); + } + } + + @Override + public boolean hasComponentInTheWay(GuiComponent comp, int mouseX, int mouseY) + { + for(GuiComponent other : renderOrder) + { + if(other == comp) break; + if(other.usesRenderOrder() && other.isHovered(mouseX, mouseY)) return true; + } + return false; + } + + @Override + public boolean isComponentInWay(GuiComponent comp, int mouseX, int mouseY) + { + for(IButtonComponent entry : buttonOrder.keySet()) + { + if(entry != comp && entry instanceof GuiComponent && ((GuiComponent)entry).isHovered(mouseX, mouseY)) + { + return true; + } + } + return false; + } + + @Override + public boolean isComponentFocused(GuiComponent comp) + { + return hasFocus() && buttonOrder.size() > 0 && ((buttonOrder.size() == 1 && buttonOrder.containsKey(comp)) || buttonOrder.firstKey() == comp); + } + + @Override + public boolean isComponentInFront(GuiComponent comp) + { + if(!hasFocus() || renderOrder.last() == comp || (renderOrder.last() instanceof IButtonComponent && ((IButtonComponent)renderOrder.last()).isPopup())) + { + return true; + } + return false; + } + + @Override + public boolean hasComponentPressed(int mouseButton) + { + return selectedButtons.containsKey(mouseButton); + } + + static final class ButtonSorter implements Comparator + { + public static final ButtonSorter SORTER = new ButtonSorter(); + @Override + public int compare(IButtonComponent o1, IButtonComponent o2) + { + return Float.compare(o2.getComponentZ(), o1.getComponentZ()); + } + + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/base/IButtonComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/base/IButtonComponent.java new file mode 100644 index 0000000..aae28f0 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/base/IButtonComponent.java @@ -0,0 +1,52 @@ +package speiger.src.coreengine.rendering.gui.base; + +import speiger.src.coreengine.rendering.gui.GuiComponent; + +public interface IButtonComponent extends IInputComponent +{ + + public default boolean isComponentColliding(int mouseX, int mouseY) + { + return ((GuiComponent)this).isHovered(mouseX, mouseY); + } + + public default float getComponentZ() + { + return this instanceof GuiComponent ? ((GuiComponent)this).calculateZLevel() : 0F; + } + + public default boolean isValidButton(int button) + { + return button == 0; + } + + public default boolean onClick(int button, int mouseX, int mouseY) + { + return true; + } + + public default boolean onDrag(int mouseX, int mouseY) + { + return false; + } + + public default void onRelease(int button, int mouseX, int mouseY) + { + + } + + public default boolean onScroll(int scroll, int mouseX, int mouseY) + { + return false; + } + + public default boolean canMoveIntoForground() + { + return false; + } + + public default void onFocusLost() + { + + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/base/IInputComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/base/IInputComponent.java new file mode 100644 index 0000000..d25081f --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/base/IInputComponent.java @@ -0,0 +1,16 @@ +package speiger.src.coreengine.rendering.gui.base; + +import speiger.src.coreengine.rendering.gui.GuiComponent; + +public interface IInputComponent +{ + public default boolean isPopup() + { + return false; + } + + public default boolean hasChildPopups() + { + return this instanceof GuiComponent && ((GuiComponent)this).hasPopups(); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/base/IKeyComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/base/IKeyComponent.java new file mode 100644 index 0000000..a583cf5 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/base/IKeyComponent.java @@ -0,0 +1,12 @@ +package speiger.src.coreengine.rendering.gui.base; + +public interface IKeyComponent extends IInputComponent +{ + public boolean isAcceptingInput(); + + public default boolean isBlockingMovement() {return false;} + + public boolean onKeyPressed(int key); + + public default boolean onKeyTyped(char letter, int keyCode) {return false;} +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/ButtonComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/ButtonComponent.java new file mode 100644 index 0000000..985c4cd --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/ButtonComponent.java @@ -0,0 +1,64 @@ +package speiger.src.coreengine.rendering.gui.components; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.base.IButtonComponent; +import speiger.src.coreengine.rendering.gui.helper.constrains.Constraints; + +public class ButtonComponent extends GuiComponent implements IButtonComponent +{ + TextComponent text = new TextComponent(); + ColorObject color; + + public ButtonComponent(String text, ColorObject color) + { + this(0F, 0F, 0F, 0F, text, color); + } + + public ButtonComponent(float x, float y, float width, float height, String text, ColorObject color) + { + super(x, y, width, height); + this.text.setText(text); + this.color = color; + setFlag(FLAG_SUPPORT_BINDING); + } + + @Override + public void init() + { + addChild(text, Constraints.getParentConstrains()); + } + + public TextComponent getText() + { + return text; + } + + public ButtonComponent setColor(ColorObject color) + { + this.color = color; + return this; + } + + @Override + public boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float brigthness = getBrightness(mouseX, mouseY); + getRenderer().setBrightness(brigthness).drawQuad(getBox(), color); + text.setBrightness(brigthness); + return true; + } + + @Override + public void onRelease(int button, int mouseX, int mouseY) + { + notifyListeners(LISTENER_USER_ACTION); + } + + @Override + protected boolean onUserKey() + { + notifyListeners(LISTENER_USER_ACTION); + return true; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/CheckBoxComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/CheckBoxComponent.java new file mode 100644 index 0000000..2f58c7b --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/CheckBoxComponent.java @@ -0,0 +1,123 @@ +package speiger.src.coreengine.rendering.gui.components; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.base.IButtonComponent; +import speiger.src.coreengine.rendering.gui.components.misc.ICheckBox; +import speiger.src.coreengine.rendering.gui.helper.UIShapes; +import speiger.src.coreengine.rendering.gui.renderer.buffer.RenderBuffer; + +public class CheckBoxComponent extends GuiComponent implements IButtonComponent, ICheckBox +{ + boolean isChecked = false; + ColorObject color; + RenderBuffer buffer; + + public CheckBoxComponent(ColorObject color) + { + super(0F, 0F, 0F, 0F); + setFlag(FLAG_SUPPORT_BINDING); + this.color = color; + } + + public CheckBoxComponent(ColorObject color, boolean checked) + { + super(0F, 0F, 0F, 0F); + setFlag(FLAG_SUPPORT_BINDING); + this.color = color; + } + + public CheckBoxComponent(float x, float y, float width, float height, ColorObject color) + { + super(x, y, width, height); + setFlag(FLAG_SUPPORT_BINDING); + this.color = color; + } + + public CheckBoxComponent(float x, float y, float width, float height, ColorObject color, boolean checked) + { + super(x, y, width, height); + setFlag(FLAG_SUPPORT_BINDING); + this.color = color; + isChecked = checked; + } + + @Override + public void init() + { + addCloseListener(buffer = getRenderer().createBuffer()); + } + + @Override + public final CheckBoxComponent setChecked(boolean isChecked) + { + this.isChecked = isChecked; + return this; + } + + public final CheckBoxComponent setColor(ColorObject color) + { + if(this.color != color) + { + this.color = color; + onComponentChanged(true); + } + return this; + } + + @Override + public boolean isChecked() + { + return isChecked; + } + + @Override + protected void repaint() + { + float width = getBox().getWidth(); + float height = getBox().getHeight(); + buffer.clear(); + UIShapes.createCross(buffer, width, height, (width / 3.3F), (height / 5F), color); + UIShapes.createCross(buffer, width, height, (width / 5F), (height / 10F), color); + } + + @Override + public boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float brightness = getActiveBrightness(); + float centerX = getBox().getCenterX(); + float centerY = getBox().getCenterY(); + getRenderer().setBrightness(brightness).drawQuad(getBox(), color).setBrightness(brightness * 0.8F).drawFrame(getBox(), color).translate(centerX, centerY); + if(isChecked()) + { + getRenderer().setBrightness(brightness * 0.7F).translate(0, 0, 0.001F).drawBuffers(buffer.selectionIterator(1), getBox().getWidth(), getBox().getHeight()).translate(0, 0, -0.001F); + } + if(isTopHovered(mouseX, mouseY)) + { + getRenderer().setBrightness(brightness * 1.3F).translate(0, 0, 0.002F).drawBuffers(buffer.selectionIterator(0), getBox().getWidth(), getBox().getHeight()).translate(0, 0, -0.002F); + } + getRenderer().setBrightness(1F).translate(-centerX, -centerY); + return true; + } + + @Override + public boolean onClick(int button, int mouseX, int mouseY) + { + return true; + } + + @Override + public void onRelease(int button, int mouseX, int mouseY) + { + isChecked = !isChecked; + notifyListeners(LISTENER_USER_ACTION); + } + + @Override + protected boolean onUserKey() + { + isChecked = !isChecked; + notifyListeners(LISTENER_USER_ACTION); + return true; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/EmptyComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/EmptyComponent.java new file mode 100644 index 0000000..defc200 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/EmptyComponent.java @@ -0,0 +1,23 @@ +package speiger.src.coreengine.rendering.gui.components; + +import speiger.src.coreengine.rendering.gui.GuiComponent; + +public class EmptyComponent extends GuiComponent +{ + public EmptyComponent() + { + super(0F, 0F, 0F, 0F); + setFlag(FLAG_SUPPORT_BINDING); + clearFlag(FLAG_ALWAYS_CLICKABLE); + } + + @Override + public void init() {} + + @Override + protected boolean onUserKey() + { + notifyListeners(LISTENER_USER_ACTION); + return true; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/GradientSliderComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/GradientSliderComponent.java new file mode 100644 index 0000000..7a806e1 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/GradientSliderComponent.java @@ -0,0 +1,58 @@ +package speiger.src.coreengine.rendering.gui.components; + +import java.util.function.IntFunction; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.math.misc.Facing; + +public class GradientSliderComponent extends SliderComponent +{ + ColorObject fromColor; + ColorObject toColor; + Facing direction; + + public GradientSliderComponent(float x, float y, float width, float height, int min, int max, int value, ColorObject color, ColorObject fromColor, ColorObject toColor, Facing direction, IntFunction textBuilder) + { + super(x, y, width, height, min, max, value, color, textBuilder); + this.direction = direction; + this.fromColor = fromColor; + this.toColor = toColor; + } + + public GradientSliderComponent(int min, int max, int value, ColorObject color, ColorObject fromColor, ColorObject toColor, Facing direction, IntFunction textBuilder) + { + super(min, max, value, color, textBuilder); + this.direction = direction; + this.fromColor = fromColor; + this.toColor = toColor; + } + + @Override + public boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float brightness = getActiveBrightness(); + float minX = getBox().getMinX(5F); + float minY = getBox().getMinY(2F); + float maxX = getBox().getMaxX(-5F); + float maxY = getBox().getMaxY(-2F); + float scale = 0.6F * getBox().getScale(); + if(vertical) + { + float extra = (((float)(value - min) / (float)(max - min)) * (getBox().getMaxY(-3F) - getBox().getMinY(3F))) + getBox().getMinY(3F); + float left = getBox().getMinX(2F); + getRenderer().setBrightness(brightness).drawGradientQuad(minX, minY, maxX, maxY, fromColor, toColor, direction).setBrightness(brightness * 0.7F).drawFrame(minX, minY, maxX, maxY, 0.001F, color); + getRenderer().setBrightness(brightness * 0.5F).translate(left, extra, 0.002F).scale(scale).drawBuffers(buffer, maxX - minX, maxX - minX).setBrightness(brightness).unscale(scale).translate(-left, -extra, -0.002F); + } + else + { + float extra = (((float)(value - min) / (float)(max - min)) * (getBox().getMaxX(-6F) - getBox().getMinX(6F))) + getBox().getMinX(6F); + float top = getBox().getMinY(); + getRenderer().setBrightness(brightness).drawGradientQuad(minX, minY, maxX, maxY, fromColor, toColor, direction).setBrightness(brightness * 0.7F).drawFrame(minX, minY, maxX, maxY, 0.001F, color); + getRenderer().setBrightness(brightness * 0.5F).translate(extra, top, 0.002F).scale(scale).drawBuffers(buffer, maxX - minX, maxX - minX).setBrightness(brightness).unscale(scale).translate(-extra, -top, -0.002F); + } + getRenderer().setBrightness(getBrightness(mouseX, mouseY)); + renderChildren(mouseX, mouseY, particalTicks); + getRenderer().setBrightness(1F); + return false; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/IconButtonComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/IconButtonComponent.java new file mode 100644 index 0000000..8c9a2f0 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/IconButtonComponent.java @@ -0,0 +1,66 @@ +package speiger.src.coreengine.rendering.gui.components; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.base.IButtonComponent; +import speiger.src.coreengine.rendering.gui.components.icon.IIcon; + +public class IconButtonComponent extends GuiComponent implements IButtonComponent +{ + IIcon icon; + ColorObject hoverColor; + + public IconButtonComponent(ColorObject hoverColor, IIcon icon) + { + super(0F, 0F, 0F, 0F); + this.icon = icon; + this.hoverColor = hoverColor; + setFlag(FLAG_SUPPORT_BINDING); + } + + public IconButtonComponent(float x, float y, float width, float height, ColorObject hoverColor, IIcon icon) + { + super(x, y, width, height); + this.icon = icon; + this.hoverColor = hoverColor; + setFlag(FLAG_SUPPORT_BINDING); + } + + @Override + public void init() + { + + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + if(isTopHovered(mouseX, mouseY) && hoverColor.getAlpha() > 0) + { + getRenderer().drawQuad(getBox(), hoverColor); + } + getRenderer().translate(0F, 0F, 0.001F); + icon.render(getRenderer(), getBox()); + getRenderer().translate(0F, 0F, -0.001F); + return true; + } + + @Override + public boolean onClick(int button, int mouseX, int mouseY) + { + return true; + } + + @Override + public void onRelease(int button, int mouseX, int mouseY) + { + notifyListeners(LISTENER_USER_ACTION); + } + + @Override + protected boolean onUserKey() + { + notifyListeners(LISTENER_USER_ACTION); + return true; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/IconComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/IconComponent.java new file mode 100644 index 0000000..7e6e143 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/IconComponent.java @@ -0,0 +1,61 @@ +package speiger.src.coreengine.rendering.gui.components; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.textures.ITexture; + +public class IconComponent extends GuiComponent +{ + ITexture texture; + ColorObject override = ColorObject.WHITE; + + public IconComponent(ITexture texture) + { + super(0F, 0F, 0F, 0F); + this.texture = texture; + } + + public IconComponent(ITexture texture, ColorObject color) + { + super(0F, 0F, 0F, 0F); + this.texture = texture; + override = color; + } + + public IconComponent(float x, float y, float width, float height, ITexture texture) + { + super(x, y, width, height); + this.texture = texture; + } + + public IconComponent(float x, float y, float width, float height, ITexture texture, ColorObject color) + { + super(x, y, width, height); + this.texture = texture; + override = color; + } + + public IconComponent setTexture(ITexture texture) + { + this.texture = texture; + return this; + } + + public IconComponent setColor(ColorObject color) + { + override = color; + return this; + } + + @Override + public void init() + { + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + getRenderer().setActiveTexture(texture).drawTexturedQuad(getBox(), override, texture.getUMin(), texture.getVMin(), texture.getUMax(), texture.getVMax()); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/LabelComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/LabelComponent.java new file mode 100644 index 0000000..9125ae9 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/LabelComponent.java @@ -0,0 +1,52 @@ +package speiger.src.coreengine.rendering.gui.components; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.helper.constrains.Constraints; + +public class LabelComponent extends GuiComponent +{ + TextComponent text = new TextComponent(); + ColorObject color; + + public LabelComponent(String text, ColorObject color) + { + this(0F, 0F, 0F, 0F, text, color); + } + + public LabelComponent(float x, float y, float width, float height, String text, ColorObject color) + { + super(x, y, width, height); + this.text.setText(text); + this.color = color; + } + + public TextComponent getText() + { + return text; + } + + public LabelComponent setColor(ColorObject color) + { + this.color = color; + return this; + } + + public ColorObject getColor() + { + return color; + } + + @Override + public void init() + { + addChild(text, Constraints.getParentConstrains()); + } + + @Override + public boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + getRenderer().drawQuad(getBox(), color); + return true; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/ListComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/ListComponent.java new file mode 100644 index 0000000..46c01f9 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/ListComponent.java @@ -0,0 +1,680 @@ +package speiger.src.coreengine.rendering.gui.components; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.function.Function; +import java.util.function.Predicate; + +import speiger.src.collections.ints.collections.IntCollection; +import speiger.src.collections.ints.lists.IntArrayList; +import speiger.src.collections.ints.lists.IntList; +import speiger.src.collections.ints.sets.IntLinkedOpenHashSet; +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.lists.ObjectList; +import speiger.src.collections.objects.utils.ObjectIterators; +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.math.vector.ints.Vec2i; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.base.IButtonComponent; +import speiger.src.coreengine.rendering.gui.components.list.IListEntry; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; +import speiger.src.coreengine.rendering.gui.helper.constrains.Constraints; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; +import speiger.src.coreengine.rendering.input.Keyboard; + +public class ListComponent extends GuiComponent + implements Iterable, IButtonComponent +{ + public static final int SELECTION_MODE_DISABLE = 0; + public static final int SELECTION_MODE_SINGLE = 1; + public static final int SELECTION_MODE_MULTI = 2; + public static final int SELECTION_MODE_INTERACT = 3; + + public static final int UPDATE_MODE_DISABLED = 0; + public static final int UPDATE_MODE_VISIBLE = 1; + public static final int UPDATE_MODE_ALL = 2; + + public static final int FLAG_DISABLE_BACKGROUND = 1024; + public static final int FLAG_START_AT_BOTTOM = 2048; + + protected ColorObject color; + protected ColorObject hoverColor = ColorObject.LIGHT_GRAY.copy(); + protected int hoverIndex = -1; + protected int dragIndex = -1; + protected int movement = 0; + protected IButtonComponent customButton; + protected IntLinkedOpenHashSet selectedIndexes = new IntLinkedOpenHashSet(); + protected ObjectList entries = new ObjectArrayList(); + protected int selectionMode = 1; + protected int updateMode = 1; + protected float entryHeight; + protected ScrollBarComponent verticalBar = new ScrollBarComponent(ColorObject.LIGHT_GRAY); + protected ScrollBarComponent horizontalBar = new ScrollBarComponent(ColorObject.LIGHT_GRAY).setHorizontal(true); + protected Vec2i lastMouse = Vec2i.newMutable(); + + public ListComponent() + { + super(0F, 0F, 0F, 0F); + } + + public ListComponent(ColorObject color, float entryHeight) + { + super(0F, 0F, 0F, 0F); + this.color = color; + this.entryHeight = entryHeight; + } + + public ListComponent(float x, float y, float width, float height, ColorObject color, float entryHeight) + { + super(x, y, width, height); + this.color = color; + this.entryHeight = entryHeight; + } + + @Override + public void init() + { + addChild(horizontalBar, Constraints.getScrollbarConstraints(verticalBar::isInUse, true, 5F)); + addChild(verticalBar, Constraints.getScrollbarConstraints(horizontalBar::isInUse, false, 5F)); + for(int i = 0, m = entries.size();i < m;i++) + { + entries.get(i).init(this, getGui()); + } + updateScrollBar(); + } + + @Override + public void onClosed() + { + for(int i = 0,m=entries.size();i disableBackground() + { + setFlag(FLAG_DISABLE_BACKGROUND); + return this; + } + + public ListComponent setStartAtBottom(boolean value) + { + setFlag(FLAG_START_AT_BOTTOM, value); + return this; + } + + public boolean isStartAtBottom() + { + return isFlagSet(FLAG_START_AT_BOTTOM); + } + + public ListComponent setColor(ColorObject color) + { + this.color = color; + return this; + } + + public ListComponent setHoverColor(ColorObject color) + { + hoverColor = color; + return this; + } + + public ListComponent setEntryHeight(float entryHeight) + { + if(this.entryHeight != entryHeight) + { + this.entryHeight = entryHeight; + onComponentChanged(true); + } + return this; + } + + public ListComponent setSelectionMode(int mode) + { + if(mode < 0 || mode > 3) + { + throw new IllegalStateException("Unknown Mode"); + } + this.selectionMode = mode; + selectedIndexes.clear(); + return this; + } + + public ListComponent setUpdateMode(int mode) + { + if(mode < 0 || mode > 2) + { + throw new IllegalStateException("Unknown Mode"); + } + updateMode = mode; + return this; + } + + public ListComponent setSelectedIndex(int index) + { + switch(selectionMode) + { + case 0: + throw new IllegalStateException("Selection is not allowed"); + case 1: + selectedIndexes.clear(); + case 2: + if(index >= 0 && index < entries.size()) + { + if(selectionMode == SELECTION_MODE_MULTI && Keyboard.isShiftDown()) + { + for(int i = selectedIndexes.lastInt();iindex;i--) + { + selectedIndexes.add(i); + } + } + selectedIndexes.add(index); + } + break; + } + return this; + } + + public boolean isSelected(int index) + { + return selectedIndexes.contains(index); + } + + public T removeSelectedIndex(int index) + { + return selectedIndexes.remove(index) ? entries.get(index) : null; + } + + public boolean hasSelected() + { + return !selectedIndexes.isEmpty(); + } + + public int getSelectedIndex() + { + return selectedIndexes.isEmpty() ? -1 : selectedIndexes.iterator().nextInt(); + } + + public T getSelectedValue() + { + return selectedIndexes.isEmpty() ? null : entries.get(selectedIndexes.iterator().nextInt()); + } + + public void clearSelection() + { + selectedIndexes.clear(); + } + + public IntList getSelection() + { + return new IntArrayList(selectedIndexes); + } + + public List getSelectedValues() + { + List entries = new ObjectArrayList(); + for(int index : selectedIndexes) + { + entries.add(this.entries.get(index)); + } + return entries; + } + + public ListComponent addEntry(T entry) + { + entries.add(entry); + if(getGui() != null) + { + entry.init(this, getGui()); + updateScrollBar(); + } + return this; + } + + public ListComponent addEntries(Collection entries) + { + this.entries.addAll(entries); + if(getGui() != null) + { + for(T entry : entries) + { + entry.init(this, getGui()); + } + updateScrollBar(); + } + return this; + } + + public int size() + { + return entries.size(); + } + + public T get(int index) + { + return entries.get(index); + } + + public boolean removeIf(Predicate filter) + { + if(entries.isEmpty()) return false; + selectedIndexes.clear(); + return entries.removeIf(K -> { + if(filter.test(K)) { + K.onClosed(); + return true; + } + return false; + }); + } + + public List map(Function mapper) + { + List list = new ObjectArrayList<>(entries.size()); + for(int i = 0,m=entries.size();i 0) + { + entries.add(index - 1, entries.remove(index)); + if(isSelected(index)) + { + removeSelectedIndex(index); + setSelectedIndex(index-1); + } + } + } + + public void moveDown(int index) + { + if(index + 1 < entries.size()) + { + entries.add(index + 1, entries.remove(index)); + if(isSelected(index)) + { + removeSelectedIndex(index); + setSelectedIndex(index+1); + } + } + } + + public void move(int fromIndex, int toIndex) + { + entries.add(toIndex, entries.remove(fromIndex)); + } + + public int indexOf(T value) + { + return entries.indexOf(value); + } + + public T remove(int index) + { + if(index < 0 || index >= entries.size()) + { + return null; + } + T value = entries.remove(index); + if(value != null) + { + value.onClosed(); + selectedIndexes.remove(index); + } + return value; + } + + public boolean removeAll(IntCollection values) + { + List result = new ObjectArrayList(); + for(int index : values) + { + if(index < 0 || index >= entries.size()) + { + continue; + } + selectedIndexes.remove(index); + T entry = entries.get(index); + entry.onClosed(); + result.add(entry); + } + if(result.size() > 0) + { + entries.removeAll(result); + return true; + } + return false; + } + + public boolean remove(T value) + { + return remove(indexOf(value)) != null; + } + + public void clear() + { + for(int i = 0,m=entries.size();i iterator() + { + return ObjectIterators.unmodifiable(entries.iterator()); + } + + protected Iterator rangeIterator(int start, int end) + { + return new Iterator() + { + int index = start; + + @Override + public boolean hasNext() + { + return index < end && entries.size() > index; + } + + @Override + public T next() + { + return entries.get(index++); + } + }; + } + + public Iterator visibleIterator() + { + int start = getStartIndex(); + return rangeIterator(start, MathUtils.clamp(0, entries.size() - 1, start + getIndexWidth())); + } + + @Override + protected void repaint() + { + float scale = getBox().getScale(); + for(int i = 0,m=entries.size();i= entries.size() ? -1 : index; + return true; + } + + @Override + public boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float brightness = getActiveBrightness(); + UIRenderer render = getRenderer(); + int start = getStartIndex(); + int end = MathUtils.clamp(0, entries.size(), start + getIndexWidth()); + IGuiBox box = getBox(); + float scale = box.getScale(); + float pixelSize = scale * entryHeight; + render.setBrightness(brightness); + if(isFlagNotSet(FLAG_DISABLE_BACKGROUND)) + { + render.drawQuad(box, color); + } + render.push(); + render.translate(box.getMinX(-horizontalBar.getScroll()), box.getMinY(-verticalBar.getScroll())); + float minX = horizontalBar.getScroll() * scale; + float maxX = box.getWidth() - verticalBar.getRequiredSpace() + (horizontalBar.getScroll() * scale); + enableScissorsBox(box.getMinX(), box.getMinY(), box.getMaxX()-verticalBar.getRequiredSpace(), box.getMaxY()-horizontalBar.getRequiredSpace()); + boolean bottom = !verticalBar.isInUse() && isStartAtBottom(); + if(hoverIndex != -1 && selectionMode != SELECTION_MODE_INTERACT && hoverIndex >= start && hoverIndex < end) + { + float offset = bottom ? box.getHeight()-(end-hoverIndex) * pixelSize : hoverIndex * pixelSize; + render.drawQuad(minX, offset, maxX, offset + pixelSize, 0.01F, hoverColor); + } + if(selectedIndexes.size() > 0) + { + render.setBrightness(0.75F * brightness); + for(int index : selectedIndexes) + { + if(index >= start && index < end) + { + float offset = bottom ? box.getHeight()-(end-index) * pixelSize : index * pixelSize; + render.drawQuad(minX, offset, maxX, offset + pixelSize, 0.011F, color); + } + } + } + mouseX -= box.getMinX(); + mouseY -= box.getMinY(); + for(int i = start;i collector) + { + super.collectTooltips(mouseX, mouseY, particalTicks, collector); + int start = getStartIndex(); + int end = MathUtils.clamp(0, entries.size(), start + getIndexWidth()); + IGuiBox box = getBox(); + float scale = box.getScale(); + float pixelSize = scale * entryHeight; + mouseX -= box.getMinX(); + mouseY -= box.getMinY(); + boolean bottom = !verticalBar.isInUse() && isStartAtBottom(); + for(int i = start;i> parts; + int maxSteps; + + public PieComponent(int maxSteps, Supplier> parts) + { + super(0F, 0F, 0F, 0F); + this.maxSteps = maxSteps; + this.parts = parts; + } + + public PieComponent(float x, float y, float width, float height, int maxSteps, Supplier> parts) + { + super(x, y, width, height); + this.maxSteps = maxSteps; + this.parts = parts; + } + + @Override + public void init() + { + addCloseListener(buffer = getRenderer().createBuffer()); + } + + public final PieComponent setAutoUpdate(boolean value) + { + setFlag(FLAG_AUTO_UPDATE, value); + return this; + } + + public final boolean isAutoUpdating() + { + return isFlagSet(FLAG_AUTO_UPDATE); + } + + public int getMaxSteps() + { + return maxSteps; + } + + @Override + protected boolean fixedUpdateSelf() + { + if(isAutoUpdating()) + { + createPie(); + } + return true; + } + + @Override + protected void repaint() + { + createPie(); + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float centerX = getBox().getCenterX(); + float centerY = getBox().getCenterY(); + getRenderer().setBrightness(getActiveBrightness()).translate(centerX, centerY).drawBuffers(buffer, 0F, 0F).translate(-centerX, -centerY).setBrightness(1F); + return true; + } + + protected void createPie() + { + buffer.clear(); + if(parts == null) + { + return; + } + float xSize = getBox().getWidth() / 2F; + float ySize = getBox().getHeight() / 2F; + float extra = getBox().getHeight() / 10F; + float value = -3.17F; + float spaceScale = 0.0495F * (127.0F / maxSteps); + List indexes = parts.get(); + int stepsDone = 0; + Tesselator tes = buffer.start(GL11.GL_TRIANGLES, VertexType.UI); + for(int j = indexes.size()-1;j>=0;j--) + { + IPieIndex pieIndex = indexes.get(j); + int steps = j == 0 ? maxSteps - stepsDone : pieIndex.getSteps(); + ColorObject color = pieIndex.getColor(); + ColorObject darker = color.copy().darker(); + tes.offset(0F, 0F, 0.01F); + for(int i = 0;i name; + IntSupplier valueGenerator; + + public ProgressBarComponent(ColorObject color, int value, int max) + { + this(color, value, max, null); + } + + public ProgressBarComponent(ColorObject color, int value, int max, IntFunction name) + { + this(0F, 0F, 0F, 0F, color, value, max, name); + } + + public ProgressBarComponent(float x, float y, float width, float height, ColorObject color, int value, int max) + { + this(x, y, width, height, color, value, max, null); + } + + public ProgressBarComponent(float x, float y, float width, float height, ColorObject color, int value, int max, IntFunction name) + { + super(x, y, width, height); + this.name = name; + setColor(color); + setMax(max); + setValue(value); + } + + @Override + public void init() + { + addBox(inner); + text.setZOffset(0.1F); + text.setText(stringify()); + addChild(text, Constraints.getParentConstrains()); + } + + public TextComponent getText() + { + return text; + } + + public ProgressBarComponent setColor(ColorObject color) + { + this.color = color; + return this; + } + + public ProgressBarComponent setValueGenerator(IntSupplier valueGenerator) + { + this.valueGenerator = valueGenerator; + return this; + } + + public ProgressBarComponent setValue(int newValue) + { + if(value != newValue) + { + value = newValue; + text.setText(stringify()); + } + return this; + } + + public ProgressBarComponent setMax(int newMax) + { + if(max != newMax) + { + max = newMax; + if(value > max) setValue(max); + } + return this; + } + + protected String stringify() + { + if(name != null) return name.apply(value); + else if(max == 0) return "0%"; + return Integer.toString((int)(((float)value / (float)max) * 100F))+"%"; + } + + protected float getProgress() + { + return max == 0 ? 0F : (float)value / (float)max; + } + + @Override + protected boolean fixedUpdateSelf() + { + if(valueGenerator != null) setValue(valueGenerator.getAsInt()); + return true; + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float extra = (inner.getMaxX() - inner.getMinX()) * getProgress(); + UIRenderer render = getRenderer(); + render.setBrightness(getActiveBrightness() * 0.75F).drawQuad(getBox(), color); + render.setBrightness(getActiveBrightness()).drawQuad(inner, 0.01F, color); + render.setBrightness(getActiveBrightness() * 1.25F).drawQuad(inner.getMinX(), inner.getMinY(), inner.getMinX() + extra, inner.getMaxY(), 0.02F, color); + text.setBrightness(getActiveBrightness()); + return true; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/ScrollBarComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/ScrollBarComponent.java new file mode 100644 index 0000000..3650585 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/ScrollBarComponent.java @@ -0,0 +1,244 @@ +package speiger.src.coreengine.rendering.gui.components; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.base.IButtonComponent; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; + +public class ScrollBarComponent extends GuiComponent implements IButtonComponent +{ + public static final int FLAG_HORIZONTAL = 1024; + public static final int FLAG_INVERTED = 2048; + ColorObject color; + float lastMouse = -1; + int current; + int max; + + public ScrollBarComponent(ColorObject color) + { + super(0F, 0F, 0F, 0F); + this.color = color; + } + + public ScrollBarComponent(ColorObject color, int max) + { + super(0F, 0F, 0F, 0F); + this.color = color; + this.max = max; + } + + public ScrollBarComponent(ColorObject color, int max, int current) + { + super(0F, 0F, 0F, 0F); + this.color = color; + this.max = max; + this.current = current; + } + + public ScrollBarComponent(float x, float y, float width, float height, ColorObject color) + { + super(x, y, width, height); + this.color = color; + } + + public ScrollBarComponent(float x, float y, float width, float height, ColorObject color, int max) + { + super(x, y, width, height); + this.color = color; + this.max = max; + } + + public ScrollBarComponent(float x, float y, float width, float height, ColorObject color, int max, int current) + { + super(x, y, width, height); + this.color = color; + this.max = max; + this.current = current; + } + + @Override + public void init() + { + + } + + public ScrollBarComponent setColor(ColorObject color) + { + this.color = color; + return this; + } + + public ScrollBarComponent setScrollMax(int max) + { + this.max = max; + return addScroll(0); + } + + public ScrollBarComponent addScroll(int add) + { + return setScrollCurrent(current + add); + } + + public ScrollBarComponent setScrollEnd() + { + return setScrollCurrent(max); + } + + public ScrollBarComponent setScrollStart() + { + return setScrollCurrent(0); + } + + public ScrollBarComponent setScrollCurrent(int current) + { + this.current = (int)MathUtils.clamp(0F, Math.max(0, max - getBaseRange()), current); + return this; + } + + protected int getCurrent() + { + return current; + } + + protected void setCurrent(int value) + { + current = value; + } + + public ScrollBarComponent setHorizontal(boolean value) + { + setFlag(FLAG_HORIZONTAL, value); + return this; + } + + public ScrollBarComponent setInverted(boolean value) + { + setFlag(FLAG_INVERTED, value); + return this; + } + + public boolean isHorizontal() + { + return isFlagSet(FLAG_HORIZONTAL); + } + + public int getScroll() + { + return current; + } + + public boolean isAtEnd() + { + return (max - getBaseRange()) <= current; + } + + public boolean isInUse() + { + return (max - getBaseRange()) > 0F; + } + + public float getRequiredSpace() + { + return (max - getBaseRange()) > 0F ? (isHorizontal() ? getBox().getHeight() : getBox().getWidth()) : 0F; + } + + @Override + public boolean isComponentColliding(int mouseX, int mouseY) + { + return isInUse() && isHovered(mouseX, mouseY); + } + + @Override + public boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float baseRange = getBaseRange(); + float extra = max - baseRange; + if(extra <= 0) + { + return true; + } + IGuiBox box = getBox(); + float range = getRange(); + float min = getCurrent() * (baseRange - range) / extra; + float brightness = getActiveBrightness(); + getRenderer().setBrightness(brightness * 0.5F).drawQuad(box, color); + if(isHorizontal()) getRenderer().setBrightness(brightness * 1F).drawQuad(box.getMinX(min), box.getMinY(), box.getMinX(min + range), box.getMaxY(), 0.01F, color); + else getRenderer().setBrightness(brightness * 1F).drawQuad(box.getMinX(), box.getMinY(min), box.getMaxX(), box.getMinY(min + range), 0.01F, color); + getRenderer().setBrightness(1F); + return true; + } + + @Override + public boolean onClick(int button, int mouseX, int mouseY) + { + float baseRange = getBaseRange(); + float extra = max - baseRange; + if(extra <= 0F) + { + return false; + } + float range = getRange(); + float pos = (baseRange - range) / extra; + float start = Math.max(0F, getCurrent() * pos); + IGuiBox box = getBox(); + float currentPos = (isHorizontal() ? box.getMinX(start) : box.getMinY(start)) + ((range * box.getScale()) / 2F); + float mouse = isHorizontal() ? mouseX : mouseY; + if(mouse < currentPos) + { + setCurrent((int)Math.max(0F, getCurrent() - (((currentPos - mouse) / pos) / box.getScale()))); + } + else if(mouse > currentPos) + { + setCurrent((int)Math.min(extra, getCurrent() + (((mouse - currentPos) / pos) / box.getScale()))); + } + lastMouse = mouse; + return true; + } + + @Override + public boolean onDrag(int mouseX, int mouseY) + { + if(lastMouse != -1F) + { + float range = getRange(); + IGuiBox box = getBox(); + float mouse = (isHorizontal() ? mouseX - box.getMinX() : mouseY - box.getMinY()) - ((range * box.getScale()) / 2F); + float baseRange = getBaseRange(); + float extra = max - baseRange; + setCurrent((int)MathUtils.clamp(0F, Math.max(0F, extra), (mouse / ((baseRange - range) / extra)) / box.getScale())); + return true; + } + return false; + } + + @Override + public void onRelease(int button, int mouseX, int mouseY) + { + lastMouse = -1F; + } + + @Override + public boolean onScroll(int scroll, int mouseX, int mouseY) + { + float extra = max - getBaseRange(); + if(extra > 0F) + { + setCurrent((int)MathUtils.clamp(0, extra, current - (scroll * (extra / 10F)))); + return true; + } + return false; + } + + protected float getBaseRange() + { + return isHorizontal() ? getBox().getBaseWidth() : getBox().getBaseHeight(); + } + + protected float getRange() + { + float value = getBaseRange(); + return MathUtils.clamp(10F, value, ((value * value) / max)); + } + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/ScrollPanelComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/ScrollPanelComponent.java new file mode 100644 index 0000000..6df3016 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/ScrollPanelComponent.java @@ -0,0 +1,96 @@ +package speiger.src.coreengine.rendering.gui.components; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.Constraints; + +public class ScrollPanelComponent extends GuiComponent +{ + public static final int FLAG_CORNER = 1 << 20; + + ScrollBarComponent horizontalBar = new ScrollBarComponent(ColorObject.LIGHT_GRAY).setHorizontal(true); + ScrollBarComponent verticalBar = new ScrollBarComponent(ColorObject.LIGHT_GRAY); + protected PanelComponent container = new PanelComponent().setManualRenderer(true).setScissorsTest(true).cast(); + PanelComponent viewPort = new PanelComponent().setManualRenderer(true).cast(); + + public ScrollPanelComponent() + { + super(0F, 0F, 0F, 0F); + } + + public ScrollPanelComponent(float x, float y, float width, float height) + { + super(x, y, width, height); + } + + @Override + public void init() + { + addChild(horizontalBar, Constraints.getScrollbarConstraints(isFlagSet(FLAG_CORNER) ? () -> true : verticalBar::isInUse, true, 5F)); + addChild(verticalBar, Constraints.getScrollbarConstraints(isFlagSet(FLAG_CORNER) ? () -> true : horizontalBar::isInUse, false, 5F)); + viewPort.addChild(container); + container.addChangeListener(this::recalculateSize); + addChild(viewPort, new ComponentConstrains(null, null, Constraints.createConditionalParent(verticalBar::isInUse, 0F, 5F), Constraints.createConditionalParent(horizontalBar::isInUse, 0F, 5F))); + } + + public ScrollPanelComponent setForcedCorner(boolean value) + { + if(setFlag(FLAG_CORNER, value) && getGui() != null) + { + addConstrains(horizontalBar, Constraints.getScrollbarConstraints(isFlagSet(FLAG_CORNER) ? () -> true : verticalBar::isInUse, true, 5F)); + addConstrains(verticalBar, Constraints.getScrollbarConstraints(isFlagSet(FLAG_CORNER) ? () -> true : horizontalBar::isInUse, false, 5F)); + onComponentChanged(false); + } + return this; + } + + public PanelComponent getContainer() + { + return container; + } + + public PanelComponent getViewPort() + { + return viewPort; + } + + protected void recalculateSize(GuiComponent owner) + { + float maxX = 0F; + float maxY = 0F; + for(GuiComponent component : container.getChildren()) + { + IGuiBox box = component.getBox(); + maxX = Math.max(maxX, box.getBaseX() + box.getBaseWidth()); + maxY = Math.max(maxY, box.getBaseY() + box.getBaseHeight()); + } + horizontalBar.setScrollMax((int)maxX); + verticalBar.setScrollMax((int)maxY); + owner.setComponentBounds(maxX, maxY).setComponentPosition(-horizontalBar.getScroll(), -verticalBar.getScroll()); + } + + @Override + protected boolean updateSelf(int mouseX, int mouseY, float particalTicks) + { + super.updateSelf(mouseX, mouseY, particalTicks); + container.setComponentPosition(-horizontalBar.getScroll(), -verticalBar.getScroll()); + return true; + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + renderChildren(mouseX, mouseY, particalTicks); + if(container.isVisible()) + { + enableScissors(viewPort.getBox()); + getRenderer().translate(0F, 0F, 0.1F); + container.onRender(mouseX, mouseY, particalTicks); + getRenderer().translate(0F, 0F, -0.1F); + disableScissors(); + } + return false; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/ScrollWindowComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/ScrollWindowComponent.java new file mode 100644 index 0000000..0a61e5d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/ScrollWindowComponent.java @@ -0,0 +1,109 @@ +package speiger.src.coreengine.rendering.gui.components; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.Constraints; + +public class ScrollWindowComponent extends WindowComponent +{ + public static final int FLAG_CORNER = 1 << 26; + ScrollBarComponent horizontalBar = new ScrollBarComponent(ColorObject.LIGHT_GRAY).setHorizontal(true); + ScrollBarComponent verticalBar = new ScrollBarComponent(ColorObject.LIGHT_GRAY); + protected PanelComponent container = new PanelComponent().setManualRenderer(true).cast(); + PanelComponent viewPort = new PanelComponent().setManualRenderer(true).cast(); + + public ScrollWindowComponent(int flags, String name) + { + super(0F, 0F, 0F, 0F, flags, name); + } + + public ScrollWindowComponent(float x, float y, float width, float height, int flags, String name) + { + super(x, y, width, height, flags, name); + } + + @Override + public void init() + { + super.init(); + addChild(horizontalBar.addChangeListener(minimizedListener), Constraints.getScrollbarConstraints(isFlagSet(FLAG_CORNER) ? () -> true : verticalBar::isInUse, true, 5F)); + addChild(verticalBar.addChangeListener(minimizedListener), Constraints.getVerticalScrollbar(isFlagSet(FLAG_CORNER) ? () -> true : horizontalBar::isInUse, 7.5F, 5F)); + viewPort.addChild(container); + container.addChangeListener(this::recalculateSize).addChangeListener(minimizedListener); + addChild(viewPort.setComponentPosition(0F, 7.5F), new ComponentConstrains(null, null, Constraints.createConditionalParent(verticalBar::isInUse, 0F, 5F), Constraints.createConditionalParent(horizontalBar::isInUse, 7.5F, 12.5F))); + } + + public T addComponent(T comp) + { + return container.addChild(comp); + } + + public T addComponent(T comp, ComponentConstrains constraints) + { + return container.addChild(comp, constraints); + } + + public PanelComponent getContainer() + { + return container; + } + + public PanelComponent getViewPort() + { + return viewPort; + } + + public ScrollWindowComponent setForcedCorner(boolean value) + { + if(setFlag(FLAG_CORNER, value) && getGui() != null) + { + addConstrains(horizontalBar, Constraints.getScrollbarConstraints(isFlagSet(FLAG_CORNER) ? () -> true : verticalBar::isInUse, true, 5F)); + addConstrains(verticalBar, Constraints.getVerticalScrollbar(isFlagSet(FLAG_CORNER) ? () -> true : horizontalBar::isInUse, 7.5F, 5F)); + onComponentChanged(false); + } + return this; + } + + protected void recalculateSize(GuiComponent owner) + { + float maxX = 0F; + float maxY = 0F; + for(GuiComponent component : container.getChildren()) + { + IGuiBox box = component.getBox(); + maxX = Math.max(maxX, box.getBaseX() + box.getBaseWidth()); + maxY = Math.max(maxY, box.getBaseY() + box.getBaseHeight()); + } + horizontalBar.setScrollMax((int)maxX); + verticalBar.setScrollMax((int)maxY); + horizontalBar.onComponentChanged(true); + verticalBar.onComponentChanged(true); + owner.setComponentBounds(maxX, maxY).setComponentPosition(-horizontalBar.getScroll(), -verticalBar.getScroll()); + } + + @Override + protected boolean updateSelf(int mouseX, int mouseY, float particalTicks) + { + super.updateSelf(mouseX, mouseY, particalTicks); + container.setComponentPosition(-horizontalBar.getScroll(), -verticalBar.getScroll()); + return true; + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + super.renderSelf(mouseX, mouseY, particalTicks); + renderChildren(mouseX, mouseY, particalTicks); + if(container.isVisible()) + { + enableScissors(viewPort.getBox()); + getRenderer().translate(0F, 0F, 0.1F); + container.onRender(mouseX, mouseY, particalTicks); + getRenderer().translate(0F, 0F, -0.1F); + disableScissors(); + } + return false; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/SelectionComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/SelectionComponent.java new file mode 100644 index 0000000..0c2469f --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/SelectionComponent.java @@ -0,0 +1,365 @@ +package speiger.src.coreengine.rendering.gui.components; + +import java.util.Collection; +import java.util.function.Consumer; + +import org.lwjgl.opengl.GL11; + +import speiger.src.collections.ints.collections.IntCollection; +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.math.value.IValue; +import speiger.src.coreengine.math.value.LiniarValue; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.base.IButtonComponent; +import speiger.src.coreengine.rendering.gui.components.list.SelectionEntry; +import speiger.src.coreengine.rendering.gui.helper.Align; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.Constraints; +import speiger.src.coreengine.rendering.gui.helper.constrains.ParentConstrain; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; +import speiger.src.coreengine.rendering.gui.renderer.buffer.RenderBuffer; +import speiger.src.coreengine.rendering.tesselation.Tesselator; +import speiger.src.coreengine.rendering.tesselation.VertexType; + +public class SelectionComponent extends GuiComponent implements IButtonComponent, Consumer +{ + public static final int FLAG_ANIMATE = 1 << 20; + ListComponent list = new ListComponent().setComponentBounds(0F, 120F).setManualRenderer(true).setIgnoreBounds(true).cast(); + TextComponent text = new TextComponent().setAlignment(Align.LEFT_TOP, Align.CENTER).setTextScale(0.85F).setManualRenderer(true).cast(); + RenderBuffer buffer; + ColorObject color; + boolean isOpen = false; + int selectedIndex = -1; + int defaultIndex = -1; + IValue animation = null; + + public SelectionComponent(ColorObject color) + { + super(0F, 0F, 0F, 0F); + this.color = color; + text.setText("Select Index"); + list.setColor(color); + } + + public SelectionComponent(ColorObject color, Collection collection) + { + super(0F, 0F, 0F, 0F); + this.color = color; + text.setText("Select Index"); + list.setColor(color); + addEntries(collection); + } + + public SelectionComponent(float x, float y, float width, float height, ColorObject color) + { + super(x, y, width, height); + this.color = color; + text.setText("Select Index"); + list.setColor(color); + } + + public SelectionComponent(float x, float y, float width, float height, ColorObject color, Collection collection) + { + super(x, y, width, height); + this.color = color; + text.setText("Select Index"); + list.setColor(color); + addEntries(collection); + } + + @Override + public void init() + { + list.setEntryHeight(getGui().getFont().getFontHeight()).addUserActionListener(this); + addChild(text, Constraints.getParentConstrains(21F, 0F, 10.5F, 0F)); + addChild(list, new ComponentConstrains(new ParentConstrain(), new ParentConstrain().invert(), new ParentConstrain(), null)); + addCloseListener(buffer = getRenderer().createBuffer()); + createArrow(); + } + + public final SelectionComponent setAnimating(boolean value) + { + setFlag(FLAG_ANIMATE, value); + return this; + } + + public final boolean isAnimating() + { + return isFlagSet(FLAG_ANIMATE); + } + + @Override + protected void updateState() + { + list.setVisible(isOpen); + } + + public TextComponent getText() + { + return text; + } + + public ListComponent getList() + { + return list; + } + + public SelectionComponent addEntry(String s) + { + list.addEntry(new SelectionEntry(s)); + return this; + } + + public SelectionComponent addEntries(Collection collection) + { + for(String s : collection) + { + list.addEntry(new SelectionEntry(s)); + } + return this; + } + + public SelectionComponent updateEntry(int index, String newName) + { + list.get(index).setText(newName); + if(index == selectedIndex) + { + updateSelection(); + } + return this; + } + + public SelectionComponent removeEntry(String s) + { + for(int i = 0,m=list.size();i textBuilder; + protected RenderBuffer buffer; + + public SliderComponent(int min, int max, int value, ColorObject color, IntFunction textBuilder) + { + this(0F, 0F, 0F, 0F, min, max, value, color, textBuilder); + } + + public SliderComponent(float x, float y, float width, float height, int min, int max, int value, ColorObject color, IntFunction textBuilder) + { + super(x, y, width, height); + this.min = min; + this.max = max; + this.value = MathUtils.clamp(min, max, value); + this.color = color; + this.textBuilder = textBuilder; + updateText(false); + } + + @Override + public void init() + { + text.setTextScale(0.35F); + addChild(text, Constraints.getParentConstrains()); + createArrow(); + } + + protected void createArrow() + { + if(getGui() == null) + { + return; + } + if(buffer == null) + { + addCloseListener(buffer = getRenderer().createBuffer()); + } + buffer.clear(); + UIShapes.createArrow(buffer, 12F, 12F, color, vertical ? Facing.EAST : Facing.SOUTH); + } + + public TextComponent getText() + { + return text; + } + + public SliderComponent updateText(boolean notifyListeners) + { + if(textBuilder != null) + { + text.setText(textBuilder.apply(value)); + } + if(notifyListeners) + { + notifyListeners(LISTENER_USER_ACTION); + } + return this; + } + + public SliderComponent setColor(ColorObject color) + { + if(this.color != color) + { + this.color = color; + createArrow(); + } + return this; + } + + public SliderComponent setValue(int value) + { + int lastValue = this.value; + this.value = MathUtils.clamp(min, max, value); + if(lastValue != value) + { + updateText(true); + } + return this; + } + + public SliderComponent addValue(int value) + { + return setValue(this.value + value); + } + + public SliderComponent setScrollEffect(int scrollEffect) + { + this.scrollEffect = scrollEffect; + return this; + } + + public SliderComponent setVertical(boolean vertical) + { + if(this.vertical != vertical) + { + this.vertical = vertical; + createArrow(); + } + return this; + } + + public int getValue() + { + return value; + } + + public float getPercentalValue() + { + return ((float)(value - min) / (float)(max - min)); + } + + @Override + public boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float brightness = getActiveBrightness(); + float minX = getBox().getMinX(5F); + float minY = getBox().getMinY(2F); + float maxX = getBox().getMaxX(-5F); + float maxY = getBox().getMaxY(-2F); + float scale = 0.6F * getBox().getScale(); + if(vertical) + { + float extra = (((float)(value - min) / (float)(max - min)) * (getBox().getMaxY(-3F) - getBox().getMinY(3F))) + getBox().getMinY(3F); + float left = getBox().getMinX(2F); + getRenderer().setBrightness(brightness).drawQuad(minX, minY, maxX, maxY, color).setBrightness(brightness * 0.7F).drawFrame(minX, minY, maxX, maxY, 0.001F, color); + getRenderer().setBrightness(brightness * 0.5F).translate(left, extra, 0.002F).scale(scale).drawBuffers(buffer, maxX - minX, maxX - minX).setBrightness(brightness).unscale(scale).translate(-left, -extra, -0.002F); + } + else + { + float extra = (((float)(value - min) / (float)(max - min)) * (getBox().getMaxX(-6F) - getBox().getMinX(6F))) + getBox().getMinX(6F); + float top = getBox().getMinY(); + getRenderer().setBrightness(brightness).drawQuad(minX, minY, maxX, maxY, color).setBrightness(brightness * 0.7F).drawFrame(minX, minY, maxX, maxY, 0.001F, color); + getRenderer().setBrightness(brightness * 0.5F).translate(extra, top, 0.002F).scale(scale).drawBuffers(buffer, maxX - minX, maxX - minX).setBrightness(brightness).unscale(scale).translate(-extra, -top, -0.002F); + } + getRenderer().setBrightness(getBrightness(mouseX, mouseY)); + renderChildren(mouseX, mouseY, particalTicks); + getRenderer().setBrightness(1F); + return false; + } + + @Override + public boolean onClick(int button, int mouseX, int mouseY) + { + updateScroll(mouseX, mouseY); + return true; + } + + @Override + public boolean onDrag(int mouseX, int mouseY) + { + updateScroll(mouseX, mouseY); + return true; + } + + @Override + public boolean onScroll(int scroll, int mouseX, int mouseY) + { + if(scrollEffect != 0) + { + int newValue = MathUtils.clamp(min, max, value + (scroll * scrollEffect)); + if(newValue != value) + { + value = newValue; + updateText(true); + } + } + return scrollEffect != 0; + } + + public void updateScroll(int mouseX, int mouseY) + { + float pos = vertical ? (mouseY - getBox().getMinY(2F)) / getBox().getHeight(-5F) : (mouseX - getBox().getMinX(5F)) / getBox().getWidth(-11F); + int newValue = min + (int)(MathUtils.clamp(0F, 1F, pos) * (max - min)); + if(newValue != value) + { + value = newValue; + updateText(true); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/TextCheckBoxComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/TextCheckBoxComponent.java new file mode 100644 index 0000000..ad0042c --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/TextCheckBoxComponent.java @@ -0,0 +1,56 @@ +package speiger.src.coreengine.rendering.gui.components; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.ParentConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.PixelConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.TextConstrain; + +public class TextCheckBoxComponent extends CheckBoxComponent +{ + TextComponent text = new TextComponent().setForcedSingleLine(true).setLimitedBounds(false).setTextScale(0.35F); + + public TextCheckBoxComponent(ColorObject color, String text) + { + super(color); + this.text.setText(text); + } + + public TextCheckBoxComponent(ColorObject color, String text, boolean checked) + { + super(color, checked); + this.text.setText(text); + } + + public TextCheckBoxComponent(float x, float y, float width, float height, ColorObject color, String text) + { + super(x, y, width, height, color); + this.text.setText(text); + } + + public TextCheckBoxComponent(float x, float y, float width, float height, ColorObject color, String text, boolean checked) + { + super(x, y, width, height, color, checked); + this.text.setText(text); + } + + @Override + public void init() + { + super.init(); + addChild(text, new ComponentConstrains(new PixelConstrain(getBox().getWidth() + 1F), new ParentConstrain(), TextConstrain.width(text), new ParentConstrain())); + } + + public TextComponent getText() + { + return text; + } + + @Override + public boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + super.renderSelf(mouseX, mouseY, particalTicks); + text.setBrightness(getBrightness(mouseX, mouseY)); + return true; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/TextComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/TextComponent.java new file mode 100644 index 0000000..66c53d4 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/TextComponent.java @@ -0,0 +1,447 @@ +package speiger.src.coreengine.rendering.gui.components; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.helper.Align; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; +import speiger.src.coreengine.rendering.gui.renderer.FontRenderer; +import speiger.src.coreengine.rendering.gui.renderer.buffer.RenderBuffer; +import speiger.src.coreengine.rendering.gui.renderer.lexer.TextMetadata; + +public class TextComponent extends GuiComponent +{ + public static final int FLAG_STRIKE_THROUGH = 1 << 20; + public static final int FLAG_UNDERLINE = 1 << 21; + public static final int FLAG_BOLD = 1 << 22; + public static final int FLAG_FORMATTING = 1 << 23; + public static final int FLAG_FLIPPED = 1 << 24; + public static final int FLAG_LIMITED_WIDTH = 1 << 25; + public static final int FLAG_LIMITED_HEIGHT = 1 << 26; + public static final int FLAG_AUTO_SCALE = 1 << 27; + public static final int FLAG_SINGLE_LINE = 1 << 28; + + FontRenderer font; + String text = ""; + Align vertical = Align.CENTER; + Align horizontal = Align.CENTER; + ColorObject textColor = ColorObject.WHITE.copy(); + ColorObject backGroundColor = null; + float italic = 0F; + float textScale = 1F; + RenderBuffer buffer; + float initialSize; + TextMetadata metadata = new TextMetadata(this); + + public TextComponent() + { + super(0F, 0F, 0F, 0F); + setFlag(FLAG_FORMATTING | FLAG_LIMITED_WIDTH | FLAG_LIMITED_HEIGHT); + } + + public TextComponent(String text) + { + this(0F, 0F, 0F, 0F, text); + } + + public TextComponent(float x, float y, float width, float height) + { + super(x, y, width, height); + setFlag(FLAG_FORMATTING | FLAG_LIMITED_WIDTH | FLAG_LIMITED_HEIGHT); + } + + public TextComponent(float x, float y, float width, float height, String text) + { + super(x, y, width, height); + setFlag(FLAG_FORMATTING | FLAG_LIMITED_WIDTH | FLAG_LIMITED_HEIGHT); + setText(text); + } + + @Override + public void init() + { + addCloseListener(buffer = getRenderer().createBuffer()); + if(font == null) + { + setFont(getGui().getFont()); + } + initialSize = getBox().getWidth(); + } + + @Override + public boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + if(text.isEmpty()) + { + return true; + } + float minX = getBox().getMinX(); + float minY = getBox().getMinY(); + getRenderer().translate(minX, minY).setActiveTexture(font.getTexture()).drawBuffers(buffer, getBox().getWidth(), getBox().getHeight()).translate(-minX, -minY); + return true; + } + + @Override + public void calculateActualBounds(float[] area, boolean start) + { + if(start) + { + area[0] = Float.MAX_VALUE; + area[1] = Float.MAX_VALUE; + area[2] = Float.MIN_VALUE; + area[3] = Float.MIN_VALUE; + } + IGuiBox box = getBox(); + area[0] = Math.min(area[0], box.getMinX() - metadata.getStartX()); + area[1] = Math.min(area[1], box.getMinY() - metadata.getStartY()); + area[2] = Math.max(area[2], (box.getMinX() - metadata.getStartX()) + metadata.getMaxWidth()); + area[3] = Math.max(area[3], (box.getMinY() - metadata.getStartY()) + metadata.getMaxHeight()); + for(GuiComponent comp : getChildren()) + { + if(comp.isVisible()) comp.calculateActualBounds(area, false); + } + } + + public RenderBuffer getBuffer() + { + return buffer; + } + + @Override + protected void repaint() + { + metadata.clear(); + if(font == null) + { + return; + } + font.updateText(this); + } + + public TextComponent setTextScale(float textScale) + { + if(this.textScale != textScale) + { + this.textScale = textScale; + onComponentChanged(true); + } + return this; + } + + public TextMetadata getMetadata() + { + return metadata; + } + + public final float getTextScale() + { + return textScale * getBox().getScale() * (isAutoScaling() ? getBox().getWidth() / initialSize : 1F); + } + + public final float getRawTextScale() + { + return textScale * (isAutoScaling() ? getBox().getWidth() / initialSize : 1F); + } + + public final float getWidth() + { + return getBox().getWidth() / getTextScale(); + } + + public final float getItalic() + { + return italic; + } + + public final boolean isBold() + { + return isFlagSet(FLAG_BOLD); + } + + public final boolean isStrikeThrough() + { + return isFlagSet(FLAG_STRIKE_THROUGH); + } + + public final boolean isUnderlined() + { + return isFlagSet(FLAG_UNDERLINE); + } + + public final boolean isFlipped() + { + return isFlagSet(FLAG_FLIPPED); + } + + public final boolean isRenderingSpecial() + { + return isFlagSet(FLAG_FORMATTING); + } + + public final boolean isWidthLimited() + { + return isFlagSet(FLAG_LIMITED_WIDTH); + } + + public final boolean isHeightLimited() + { + return isFlagSet(FLAG_LIMITED_HEIGHT); + } + + public final boolean isLimitedText() + { + return isFlagSet(FLAG_LIMITED_WIDTH | FLAG_LIMITED_HEIGHT); + } + + public final boolean isAutoScaling() + { + return isFlagSet(FLAG_AUTO_SCALE); + } + + public final boolean isForcedSingleLine() + { + return isFlagSet(FLAG_SINGLE_LINE); + } + + public final ColorObject getTextColor() + { + return textColor; + } + + public final ColorObject getBackgroundColor() + { + return backGroundColor; + } + + public FontRenderer getFont() + { + return font; + } + + public String getText() + { + return text; + } + + public String getText(int min) + { + return text.substring(min); + } + + public String getText(int min, int max) + { + return text.substring(min, max); + } + + public int length() + { + return text.length(); + } + + public Align getVertical() + { + return vertical; + } + + public Align getHorizontal() + { + return horizontal; + } + + public final TextComponent setBold(boolean value) + { + if(setFlag(FLAG_BOLD, value)) + { + return this; + } + onComponentChanged(true); + return this; + } + + public final TextComponent setStrikeThrough(boolean value) + { + if(!setFlag(FLAG_STRIKE_THROUGH, value)) + { + return this; + } + onComponentChanged(true); + return this; + } + + public final TextComponent setUnderlined(boolean value) + { + if(!setFlag(FLAG_UNDERLINE, value)) + { + return this; + } + onComponentChanged(true); + return this; + } + + public final TextComponent setFlipped(boolean value) + { + if(!setFlag(FLAG_FLIPPED, value)) + { + return this; + } + onComponentChanged(true); + return this; + } + + public final TextComponent setSpecialRendering(boolean value) + { + if(!setFlag(FLAG_FORMATTING, value)) + { + return this; + } + onComponentChanged(true); + return this; + } + + public final TextComponent setLimitedBounds(boolean value) + { + if(!setFlag(FLAG_LIMITED_HEIGHT | FLAG_LIMITED_WIDTH, value)) + { + return this; + } + onComponentChanged(true); + return this; + } + + public final TextComponent setLimitedWidth(boolean value) + { + if(!setFlag(FLAG_LIMITED_WIDTH, value)) + { + return this; + } + onComponentChanged(true); + return this; + } + + public final TextComponent setLimitedHeight(boolean value) + { + if(!setFlag(FLAG_LIMITED_HEIGHT, value)) + { + return this; + } + onComponentChanged(true); + return this; + } + + public final TextComponent setAutoScaling(boolean value) + { + if(!setFlag(FLAG_AUTO_SCALE, value)) + { + return this; + } + onComponentChanged(true); + return this; + } + + public final TextComponent setForcedSingleLine(boolean value) + { + if(!setFlag(FLAG_SINGLE_LINE, value)) + { + return this; + } + onComponentChanged(true); + return this; + } + + public final TextComponent setItalic(boolean value) + { + return setItalic(value ? 3F : 0F); + } + + public final TextComponent setItalic(float value) + { + if(italic == value) + { + return this; + } + italic = value; + onComponentChanged(true); + return this; + } + + public TextComponent setAlignment(Align horizontal, Align vertical) + { + if(this.horizontal == horizontal && this.vertical == vertical) + { + return this; + } + this.horizontal = horizontal; + this.vertical = vertical; + onComponentChanged(true); + return this; + } + + public TextComponent setHorizontalAlignment(Align horizontal) + { + if(this.horizontal == horizontal) + { + return this; + } + this.horizontal = horizontal; + onComponentChanged(true); + return this; + } + + public TextComponent setVerticalAlignment(Align vertical) + { + if(this.vertical == vertical) + { + return this; + } + this.vertical = vertical; + onComponentChanged(true); + return this; + } + + public TextComponent setText(String text) + { + if(text == null) + { + text = "null"; + } + if(this.text.equals(text)) + { + return this; + } + this.text = text; + onComponentChanged(true); + return this; + } + + public TextComponent setTextColor(ColorObject color) + { + if(textColor == color) + { + return this; + } + textColor = color; + onComponentChanged(true); + return this; + } + + public TextComponent setBackgroundColor(ColorObject color) + { + if(backGroundColor == color) + { + return this; + } + backGroundColor = color; + onComponentChanged(true); + return this; + } + + public TextComponent setFont(FontRenderer font) + { + if(this.font == font) + { + return this; + } + this.font = font; + onComponentChanged(true); + return this; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/TextFieldComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/TextFieldComponent.java new file mode 100644 index 0000000..9d48d2e --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/TextFieldComponent.java @@ -0,0 +1,723 @@ +package speiger.src.coreengine.rendering.gui.components; + +import java.util.function.Predicate; + +import org.lwjgl.glfw.GLFW; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.base.IButtonComponent; +import speiger.src.coreengine.rendering.gui.base.IKeyComponent; +import speiger.src.coreengine.rendering.gui.helper.Align; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; +import speiger.src.coreengine.rendering.gui.helper.box.ParentBox; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.Constraints; +import speiger.src.coreengine.rendering.gui.helper.constrains.DynamicConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.ParentConstrain; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; +import speiger.src.coreengine.rendering.gui.renderer.lexer.TextMetadata; +import speiger.src.coreengine.rendering.gui.renderer.lexer.Word; +import speiger.src.coreengine.rendering.gui.renderer.lexer.WordType; +import speiger.src.coreengine.rendering.input.Keyboard; +import speiger.src.coreengine.rendering.utils.Cursor; +import speiger.src.coreengine.utils.functions.Functions; + +public class TextFieldComponent extends GuiComponent + implements IButtonComponent, IKeyComponent +{ + public static final int FLAG_FOCUS = 1 << 20; + public static final int FLAG_CAN_LOSE_FOCUS = 1 << 21; + public static final int FLAG_DOUBLE_CLICK = 1 << 22; + public static final int FLAG_INFINITE_WIDTH = 1 << 23; + public static final int FLAG_AUTO_VALIDATE = 1 << 24; + + TextComponent text = new TextComponent().setHorizontalAlignment(Align.LEFT_TOP).setForcedSingleLine(true).setSpecialRendering(false).cast(); + ColorObject color; + int curserPosition = 0; + int selectionPosition = 0; + long lastClickTime = 0; + int maxTextLength = 32; + Predicate validator = Functions.getAlwaysTrue(); + int lineOffset = 0; + boolean direction = false; + int largestPos = 0; + IGuiBox viewPort = new ParentBox(1F); + + public TextFieldComponent(ColorObject color) + { + super(0F, 0F, 0F, 0F); + setFlag(FLAG_CAN_LOSE_FOCUS); + this.color = color; + } + + public TextFieldComponent(float x, float y, float width, float height, ColorObject color) + { + super(x, y, width, height); + setFlag(FLAG_CAN_LOSE_FOCUS); + this.color = color; + } + + @Override + public void init() + { + addBox(viewPort); + addChild(text, createConstraints()); + if(text.getText().length() > 0 && isFlagNotSet(FLAG_INFINITE_WIDTH)) + { + String s = text.getText(); + float scale = text.getTextScale(); + float width = text.getBox().getWidth(); + while(text.getFont().getTextLength(s) * scale > width && s.length() > 0) + { + s = s.substring(0, s.length() - 1); + } + text.setText(s); + } + } + + private ComponentConstrains createConstraints() + { + return isFlagNotSet(FLAG_INFINITE_WIDTH) ? Constraints.getParentConstrains(1F) : new ComponentConstrains(new DynamicConstrain(this::getOffset), new ParentConstrain(1F), new ParentConstrain(1F), new ParentConstrain(1F)); + } + + public TextFieldComponent setValidator(Predicate validator) + { + this.validator = validator; + return this; + } + + public TextFieldComponent setMaxTextLength(int charLimit) + { + maxTextLength = charLimit; + return this; + } + + public TextFieldComponent setCanLoseFocus(boolean value) + { + setFlag(FLAG_CAN_LOSE_FOCUS, value); + return this; + } + + public TextFieldComponent setFocused(boolean value) + { + setFlag(FLAG_FOCUS, value); + return this; + } + + public TextFieldComponent setAutoUpdating(boolean value) + { + setFlag(FLAG_AUTO_VALIDATE, value); + return this; + } + + public TextFieldComponent setColor(ColorObject color) + { + this.color = color; + return this; + } + + public final TextFieldComponent setInfiniteText(boolean value) + { + if(setFlag(FLAG_INFINITE_WIDTH, value)) + { + text.setLimitedBounds(!value); + if(getGui() != null) + { + addConstrains(text, createConstraints()); + } + } + return this; + } + + public TextComponent getRawText() + { + return text; + } + + public String getText() + { + return text.getText(); + } + + public String getSelectedText() + { + return text.getText(Math.min(getCurserPosition(), getSelectionPosition()), Math.max(getCurserPosition(), getSelectionPosition())); + } + + public TextFieldComponent setText(String s) + { + if(s == null) + { + return this; + } + if(validator.test(s)) + { + if(s.length() > maxTextLength) + { + s = s.substring(0, maxTextLength); + } + if(isFlagNotSet(FLAG_INFINITE_WIDTH) && getGui() != null) + { + float scale = text.getTextScale(); + float width = text.getBox().getWidth(); + while(text.getFont().getTextLength(s) * scale > width) + { + s = s.substring(0, s.length() - 1); + } + } + text.setText(s); + } + return this; + } + + @Override + protected boolean updateSelf(int mouseX, int mouseY, float particalTicks) + { + if(text.isTopHovered(mouseX, mouseY)) + { + bindCursor(Cursor.CURSOR_IBEAM); + } + return true; + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float brightness = getActiveBrightness(); + UIRenderer render = getRenderer(); + render.setBrightness(brightness * 0.7F).drawQuad(getBox(), color).setBrightness(brightness); + IGuiBox box = text.getBox(); + render.drawQuad(viewPort, 0.001F, color); + if(isFlagSet(FLAG_INFINITE_WIDTH)) + { + enableScissorsBox(getBox().getMinX(1F), getBox().getMinY(1F), getBox().getMaxX(-1F), getBox().getMaxY(-1F)); + } + if(isFlagSet(FLAG_FOCUS) && (getGlobalClock() / 15) % 2L == 0) + { + TextMetadata data = text.getMetadata(); + if(hasSelectedText()) + { + float extra = text.getHorizontal().align(box.getWidth(), data.getMaxWidth()); + render.drawQuad(box.getMinX() + extra + data.getWidth(Math.min(getCurserPosition(), getSelectionPosition())), box.getMinY(), box.getMinX() + extra + data.getWidth(Math.max(getCurserPosition(), getSelectionPosition())), box.getMaxY(), 0.02F, text.getTextColor()); + } + else + { + float width = data.getWidth(curserPosition) + text.getHorizontal().align(box.getWidth(), data.getMaxWidth()); + render.drawQuad(box.getMinX() + width, box.getMinY(), box.getMinX() + width + text.getTextScale(), box.getMaxY(), 0.02F, text.getTextColor()); + } + } + if(isFlagSet(FLAG_INFINITE_WIDTH)) + { + renderChildren(mouseX, mouseY, particalTicks); + disableScissors(); + return false; + } + return true; + } + + @Override + public boolean onClick(int button, int mouseX, int mouseY) + { + setFocused(true); + if(hasSelectedText() && isFlagNotSet(FLAG_DOUBLE_CLICK)) + { + setSelectionPosition(-1); + setCurserPosition(getMousePosition(mouseX)); + return true; + } + int pos = getMousePosition(mouseX); + if(pos == getCurserPosition() || isFlagSet(FLAG_DOUBLE_CLICK)) + { + if(System.currentTimeMillis() - lastClickTime < 500L) + { + if(isFlagSet(FLAG_DOUBLE_CLICK)) + { + clearFlag(FLAG_DOUBLE_CLICK); + setSelectionPosition(0); + setCurserPosition(text.length()); + } + else + { + setFlag(FLAG_DOUBLE_CLICK); + handleDoubleClick(pos); + } + } + else + { + clearFlag(FLAG_DOUBLE_CLICK); + } + lastClickTime = System.currentTimeMillis(); + return true; + } + clearFlag(FLAG_DOUBLE_CLICK); + setCurserPosition(pos); + lastClickTime = System.currentTimeMillis(); + return true; + } + + @Override + public boolean onDrag(int mouseX, int mouseY) + { + if(!hasSelectedText()) + { + setSelectionPosition(getCurserPosition()); + } + setCurserPosition(getMousePosition(mouseX)); + return true; + } + + @Override + public boolean isAcceptingInput() + { + return isFlagSet(FLAG_FOCUS); + } + + @Override + public boolean isBlockingMovement() + { + return true; + } + + @Override + public boolean onKeyPressed(int key) + { + if(isFlagNotSet(FLAG_FOCUS)) + { + return false; + } + if(key == GLFW.GLFW_KEY_ENTER) + { + notifyListeners(LISTENER_USER_ACTION); + if(isFlagSet(FLAG_CAN_LOSE_FOCUS)) + { + setFocused(false); + return true; + } + } + if(key == GLFW.GLFW_KEY_BACKSPACE) + { + if(Keyboard.isCtrlDown()) + { + Word word = text.getMetadata().getWord(getCurserPosition()); + if(word == null) + { + return true; + } + if(getCurserPosition() == word.getStartIndex()) + { + if(word.getPrev() != null) + { + deleteAtCurser(word.getPrev().getStartIndex() - word.getStartIndex()); + } + } + else + { + setCurserPosition(word.getStartIndex()); + deleteAtCurser(word.getEndIndex() - word.getStartIndex()); + } + return true; + } + else if(deleteAtCurser(-1)) + { + return true; + } + } + else if(key == GLFW.GLFW_KEY_DELETE) + { + if(Keyboard.isCtrlDown()) + { + Word word = text.getMetadata().getWord(getCurserPosition()); + if(word == null) + { + return true; + } + if(getCurserPosition() == word.getEndIndex()) + { + if(word.getNext() != null) + { + deleteAtCurser(word.getNext().getEndIndex() - word.getEndIndex()); + } + } + else + { + setCurserPosition(word.getStartIndex()); + deleteAtCurser(word.getEndIndex() - word.getStartIndex()); + } + return true; + } + else if(deleteAtCurser(1)) + { + return true; + } + } + else if(key == GLFW.GLFW_KEY_LEFT) + { + if(getCurserPosition() >= 0) + { + if(Keyboard.isShiftDown() && getSelectionPosition() == -1) + { + setSelectionPosition(getCurserPosition()); + } + else if(!Keyboard.isShiftDown() && getSelectionPosition() != -1) + { + setSelectionPosition(-1); + } + if(Keyboard.isCtrlDown()) + { + Word word = text.getMetadata().getWord(getCurserPosition()); + if(word.getStartIndex() == getCurserPosition()) + { + if(word.getPrev() != null) + { + setCurserPosition(word.getPrev().getStartIndex()); + } + } + else + { + setCurserPosition(word.getStartIndex()); + } + } + else + { + setCurserPosition(getCurserPosition() - 1); + } + return true; + } + } + else if(key == GLFW.GLFW_KEY_RIGHT) + { + if(getCurserPosition() < text.length()) + { + if(Keyboard.isShiftDown() && getSelectionPosition() == -1) + { + setSelectionPosition(getCurserPosition()); + } + else if(!Keyboard.isShiftDown() && getSelectionPosition() != -1) + { + setSelectionPosition(-1); + } + if(Keyboard.isCtrlDown()) + { + Word word = text.getMetadata().getWord(getCurserPosition()); + if(word.getEndIndex() == getCurserPosition()) + { + if(word.getNext() != null) + { + setCurserPosition(word.getNext().getEndIndex()); + } + } + else + { + setCurserPosition(word.getEndIndex()); + } + } + else + { + setCurserPosition(getCurserPosition() + 1); + } + return true; + } + else + { + if(!Keyboard.isShiftDown() && hasSelectedText()) + { + setSelectionPosition(-1); + } + return true; + } + } + else if(key == GLFW.GLFW_KEY_END) + { + if(Keyboard.isShiftDown()) + { + if(!hasSelectedText()) + { + setSelectionPosition(getCurserPosition()); + } + } + else + { + setSelectionPosition(-1); + } + setCurserToEnd(); + return true; + } + else if(key == GLFW.GLFW_KEY_HOME) + { + if(Keyboard.isShiftDown()) + { + if(getSelectionPosition() == -1) + { + setSelectionPosition(getCurserPosition()); + } + } + else + { + setSelectionPosition(-1); + } + setCurserPosition(0); + return true; + } + else if(key == GLFW.GLFW_KEY_TAB) + { + writeText(Character.toString('\t')); + return true; + } + else if(isSelect(key)) + { + setSelectionPosition(0); + setCurserToEnd(); + return true; + } + else if(isCopy(key)) + { + if(hasSelectedText()) + { + getWindow().setClipboardString(getSelectedText()); + } + return true; + } + else if(isPaste(key)) + { + String text = getWindow().getClipboardString(); + if(text != null) + { + writeText(text); + } + return true; + } + else if(isCut(key)) + { + if(hasSelectedText()) + { + getWindow().setClipboardString(getSelectedText()); + writeText(""); + } + return true; + } + else if(Keyboard.isPrintableKey(key)) + { + return true; + } + return false; + } + + @Override + public boolean onKeyTyped(char letter, int keyCode) + { + return isFlagSet(FLAG_FOCUS) && text.getFont().isCharValid(letter) && writeText(Character.toString(letter)); + } + + public boolean deleteAtCurser(int amount) + { + if(text.length() > 0) + { + if(hasSelectedText()) + { + int startPos = Math.min(getCurserPosition(), getSelectionPosition()); + writeText(""); + setCurserPosition(startPos); + } + else + { + int startPos = Math.min(getCurserPosition(), getCurserPosition() + amount); + int endPos = Math.max(getCurserPosition(), getCurserPosition() + amount); + StringBuilder builder = new StringBuilder(); + if(startPos >= 0) + { + builder.append(text.getText(0, startPos)); + } + if(endPos < text.length()) + { + builder.append(text.getText(endPos)); + } + String s = builder.toString(); + if(validator.test(s)) + { + setText(s); + setCurserPosition(Math.min(startPos, text.length())); + if(isFlagSet(FLAG_AUTO_VALIDATE)) notifyListeners(LISTENER_USER_ACTION); + } + } + return true; + } + return false; + } + + public boolean writeText(String toWrite) + { + toWrite = text.getFont().clearInvalidLetters(toWrite); + StringBuilder builder = new StringBuilder(); + int startPos = hasSelectedText() ? Math.min(getSelectionPosition(), getCurserPosition()) : getCurserPosition(); + int endPos = hasSelectedText() ? Math.max(getSelectionPosition(), getCurserPosition()) : getCurserPosition(); + int room = maxTextLength - text.length() - (startPos - endPos); + if(text.length() > 0) builder.append(text.getText(0, startPos)); + int moved = 0; + if(room < toWrite.length()) + { + builder.append(toWrite.substring(0, room)); + moved = room; + } + else + { + builder.append(toWrite); + moved = toWrite.length(); + } + if(text.length() > 0 && endPos < text.length()) builder.append(text.getText(endPos)); + String s = builder.toString(); + if(validator.test(s)) + { + setText(s); + setCurserPosition(Math.min(endPos + moved, text.length())); + setSelectionPosition(-1); + if(isFlagSet(FLAG_AUTO_VALIDATE)) notifyListeners(LISTENER_USER_ACTION); + return true; + } + return false; + } + + @Override + public void onFocusLost() + { + if(isFlagSet(FLAG_FOCUS)) + { + notifyListeners(LISTENER_USER_ACTION); + } + setFocused(false); + } + + protected void handleDoubleClick(int position) + { + Word word = text.getMetadata().getWord(position); + if(word == null) + { + return; + } + if(!word.isSpecial()) + { + setSelectionPosition(word.getStartIndex()); + setCurserPosition(word.getEndIndex()); + return; + } + WordType type = word.getType(); + if(type.isStartWord()) + { + Word other = type.findEndWord(word); + if(other != null) + { + setSelectionPosition(word.getStartIndex()); + setCurserPosition(other.getEndIndex()); + } + } + else if(type.isEndWord()) + { + Word other = type.findStartWord(word); + if(other != null) + { + setSelectionPosition(other.getStartIndex()); + setCurserPosition(word.getEndIndex()); + } + } + else if(type.isDualWord()) + { + if(word.getStartIndex() == position && word.getPrev() != null) + { + Word other = type.findStartWord(word); + if(other != null) + { + setSelectionPosition(other.getStartIndex()); + setCurserPosition(word.getEndIndex()); + } + } + else + { + Word other = type.findEndWord(word); + if(other != null) + { + setSelectionPosition(word.getStartIndex()); + setCurserPosition(other.getEndIndex()); + } + } + } + else + { + setSelectionPosition(word.getStartIndex()); + setCurserPosition(word.getEndIndex()); + } + } + + protected int getMousePosition(int mouseX) + { + return text.getMetadata().getIndex(mouseX - (text.getBox().getMinX()+text.getHorizontal().align(text.getBox().getWidth(), text.getMetadata().getMaxWidth()))); + } + + public void setCurserToEnd() + { + setCurserPosition(text.length()); + } + + public void setCurserPosition(int curserPosition) + { + this.curserPosition = Math.max(0, curserPosition); + if(isFlagSet(FLAG_INFINITE_WIDTH) && text.getFont() != null) + { + int lastLine = lineOffset; + if(lineOffset > text.length()) + { + lineOffset = text.length(); + } + + String s = text.getFont().trimStringToWidth(text.getText(lineOffset), text.getWidth()); + int k = s.length() + lineOffset; + if(curserPosition > k) + { + lineOffset += curserPosition - k; + direction = true; + largestPos = curserPosition; + } + else if(curserPosition < largestPos) + { + int diff = largestPos - curserPosition; + lineOffset -= diff; + largestPos -= diff; + if(lineOffset <= 0) direction = false; + } + lineOffset = MathUtils.clamp(0, text.length(), lineOffset); + if(lastLine != lineOffset) + { + text.onComponentChanged(false); + } + } + } + + public int getCurserPosition() + { + return curserPosition; + } + + public void setSelectionPosition(int selectionPosition) + { + this.selectionPosition = selectionPosition; + } + + public int getSelectionPosition() + { + return selectionPosition; + } + + public boolean hasSelectedText() + { + return selectionPosition != -1; + } + + protected float getOffset() + { + if(direction && text.getFont() != null) + { + return -(text.getMetadata().getWidth(largestPos)) + text.getBox().getWidth() - 1F; + } + return 1F - (text.getMetadata().getWidth(lineOffset)); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/TextPanelComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/TextPanelComponent.java new file mode 100644 index 0000000..95e8a47 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/TextPanelComponent.java @@ -0,0 +1,688 @@ +package speiger.src.coreengine.rendering.gui.components; + +import java.util.function.Predicate; + +import org.lwjgl.glfw.GLFW; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.math.vector.ints.Vec2i; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.base.IButtonComponent; +import speiger.src.coreengine.rendering.gui.base.IKeyComponent; +import speiger.src.coreengine.rendering.gui.helper.Align; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; +import speiger.src.coreengine.rendering.gui.helper.constrains.Constraints; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; +import speiger.src.coreengine.rendering.gui.renderer.lexer.Line; +import speiger.src.coreengine.rendering.gui.renderer.lexer.TextMetadata; +import speiger.src.coreengine.rendering.gui.renderer.lexer.Word; +import speiger.src.coreengine.rendering.gui.renderer.lexer.WordType; +import speiger.src.coreengine.rendering.input.Keyboard; +import speiger.src.coreengine.rendering.utils.Cursor; +import speiger.src.coreengine.utils.functions.Functions; + +public class TextPanelComponent extends GuiComponent implements IButtonComponent, IKeyComponent +{ + public static final int FLAG_FOCUS = 1024; + public static final int FLAG_CAN_LOSE_FOCUS = 2048; + + TextComponent text = new TextComponent().setAlignment(Align.LEFT_TOP, Align.LEFT_TOP).setForcedSingleLine(true).setSpecialRendering(false).cast(); + ColorObject color; + int curserPosition = 0; + Vec2i curserPos = Vec2i.newMutable(); + int selectionPosition = 0; + Vec2i selectionPos = Vec2i.newMutable(); + long lastClickTime = 0; + int maxTextLength = 64; + int clickCount = 0; + Predicate validator = Functions.getAlwaysTrue(); + + public TextPanelComponent(ColorObject color) + { + super(0F, 0F, 0F, 0F); + this.color = color; + } + + public TextPanelComponent(float x, float y, float width, float height, ColorObject color) + { + super(x, y, width, height); + this.color = color; + } + + @Override + public void init() + { + addChild(text, Constraints.getParentConstrains(1F)); + } + + public TextPanelComponent setValidator(Predicate validator) + { + this.validator = validator; + return this; + } + + public TextPanelComponent setMaxTextLength(int charLimit) + { + maxTextLength = charLimit; + return this; + } + + public TextPanelComponent setColor(ColorObject color) + { + this.color = color; + return this; + } + + public TextComponent getRawText() + { + return text; + } + + public String getText() + { + return text.getText(); + } + + public String getSelectedText() + { + return text.getText(Math.min(getCurserPosition(), getSelectionPosition()), Math.max(getCurserPosition(), getSelectionPosition())); + } + + public TextPanelComponent setText(String s) + { + if(s == null) + { + return this; + } + if(validator.test(s)) + { + if(s.length() > maxTextLength) + { + s = s.substring(0, maxTextLength); + } + float height = text.getBox().getHeight(); + float scale = text.getTextScale(); + while(s.length() > 0 && text.getFont().getTextHeight(s) * scale > height) + { + s = s.substring(0, s.length() - 1); + } + text.setText(s); + } + return this; + } + + @Override + protected boolean updateSelf(int mouseX, int mouseY, float particalTicks) + { + if(text.isMouseOver(mouseX, mouseY) && !text.isOverChild(mouseX, mouseY)) + { + bindCursor(Cursor.CURSOR_IBEAM); + } + return true; + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float brightness = getActiveBrightness(); + UIRenderer render = getRenderer(); + render.setBrightness(brightness * 0.7F).drawQuad(getBox(), color).setBrightness(brightness); + IGuiBox box = text.getBox(); + render.drawQuad(box, 0.001F, color); + if(isFlagSet(FLAG_FOCUS) && (getGlobalClock() / 15) % 2L == 0) + { + TextMetadata data = text.getMetadata(); + float height = text.getFont().getFontHeight() * text.getTextScale(); + if(hasSelectedText()) + { + if(selectionPos.getY() == curserPos.getY()) + { + render.drawQuad(box.getMinX() + data.getWidth(Math.min(curserPos.getX(), selectionPos.getX()), curserPos.getY()), box.getMinY() + (height * curserPos.getY()), box.getMinX() + data.getWidth(Math.max(curserPos.getX(), selectionPos.getX()), curserPos.getY()), box.getMinY() + (height * (curserPos.getY()+1)), 0.02F, text.getTextColor()); + } + else + { + Vec2i min = selectionPos.getY() < curserPos.getY() ? selectionPos : curserPos; + Vec2i max = selectionPos.getY() > curserPos.getY() ? selectionPos : curserPos; + for(int i = min.getY(),m=max.getY();i<=m;i++) + { + if(i == min.getY()) + { + render.drawQuad(box.getMinX() + data.getWidth(min.getX()), box.getMinY() + (height * i), box.getMinX() + data.getLineWidth(i), box.getMinY() + (height * (i+1)), 0.02F, text.getTextColor()); + } + else if(i == max.getY()) + { + render.drawQuad(box.getMinX(), box.getMinY() + (height * i), box.getMinX() + data.getWidth(max.getX()), box.getMinY() + (height * (i+1)), 0.02F, text.getTextColor()); + } + else + { + render.drawQuad(box.getMinX(), box.getMinY() + (height * i), box.getMinX() + data.getLineWidth(i), box.getMinY() + (height * (i+1)), 0.02F, text.getTextColor()); + } + } + } + return true; + } + float width = data.getWidth(curserPos.getX(), curserPos.getY()); + render.drawQuad(box.getMinX()+width, box.getMinY() + (height * curserPos.getY()), box.getMinX()+width+text.getTextScale(), box.getMinY() + (height * (curserPos.getY()+1)), 0.02F, text.getTextColor()); + } + return true; + } + + @Override + public boolean onClick(int button, int mouseX, int mouseY) + { + setFlag(FLAG_FOCUS); + if(hasSelectedText() && clickCount <= 0) + { + setSelectionPosition(-1); + setCurserPosition(getMousePosition(mouseX, mouseY)); + return true; + } + int pos = getMousePosition(mouseX, mouseY); + if(pos == getCurserPosition()) + { + if(System.currentTimeMillis() - lastClickTime < 500L) + { + if(clickCount == 1) + { + Line line = text.getMetadata().getLine(curserPos.getY()); + setSelectionPosition(line.getStart()); + setCurserPosition(line.getEnd()); + clickCount = 2; + } + else if(clickCount == 2) + { + clickCount = 3; + setSelectionPosition(0); + setCurserToEnd(); + } + else + { + clickCount = 1; + handleDoubleClick(pos); + } + } + else + { + clickCount = 0; + } + lastClickTime = System.currentTimeMillis(); + return true; + } + lastClickTime = System.currentTimeMillis(); + setCurserPosition(pos); + setSelectionPosition(-1); + clickCount = 0; + return false; + } + + @Override + public boolean onDrag(int mouseX, int mouseY) + { + if(!hasSelectedText()) + { + setSelectionPosition(getCurserPosition()); + } + setCurserPosition(getMousePosition(mouseX, mouseY)); + return true; + } + + @Override + public void onFocusLost() + { + clearFlag(FLAG_FOCUS); + } + + @Override + public boolean isAcceptingInput() + { + return isFlagSet(FLAG_FOCUS); + } + + @Override + public boolean isBlockingMovement() + { + return true; + } + + @Override + public boolean onKeyPressed(int key) + { + if(isFlagNotSet(FLAG_FOCUS)) + { + return false; + } + if(key == GLFW.GLFW_KEY_BACKSPACE) + { + if(Keyboard.isCtrlDown()) + { + Word word = text.getMetadata().getWord(getCurserPosition()); + if(word == null) + { + return true; + } + if(getCurserPosition() == word.getStartIndex()) + { + if(word.getPrev() != null) + { + deleteAtCurser(word.getPrev().getStartIndex() - word.getStartIndex()); + } + } + else + { + setCurserPosition(word.getStartIndex()); + deleteAtCurser(word.getEndIndex() - word.getStartIndex()); + } + return true; + } + else if(deleteAtCurser(-1)) + { + return true; + } + } + else if(key == GLFW.GLFW_KEY_DELETE) + { + if(Keyboard.isCtrlDown()) + { + Word word = text.getMetadata().getWord(getCurserPosition()); + if(word == null) + { + return true; + } + if(getCurserPosition() == word.getEndIndex()) + { + if(word.getNext() != null) + { + deleteAtCurser(word.getNext().getEndIndex() - word.getEndIndex()); + } + } + else + { + setCurserPosition(word.getStartIndex()); + deleteAtCurser(word.getEndIndex() - word.getStartIndex()); + } + return true; + } + else if(deleteAtCurser(1)) + { + return true; + } + } + else if(key == GLFW.GLFW_KEY_LEFT) + { + if(getCurserPosition() >= 0) + { + if(Keyboard.isShiftDown() && getSelectionPosition() == -1) + { + setSelectionPosition(getCurserPosition()); + } + else if(!Keyboard.isShiftDown() && getSelectionPosition() != -1) + { + setSelectionPosition(-1); + } + if(Keyboard.isCtrlDown()) + { + Word word = text.getMetadata().getWord(getCurserPosition()); + if(word == null) + { + return true; + } + if(word.getStartIndex() == getCurserPosition()) + { + if(word.getPrev() != null) + { + setCurserPosition(word.getPrev().getStartIndex()); + } + } + else + { + setCurserPosition(word.getStartIndex()); + } + } + else + { + setCurserPosition(getCurserPosition()-1); + } + return true; + } + } + else if(key == GLFW.GLFW_KEY_RIGHT) + { + if(getCurserPosition() < text.length()) + { + if(Keyboard.isShiftDown() && getSelectionPosition() == -1) + { + setSelectionPosition(getCurserPosition()); + } + else if(!Keyboard.isShiftDown() && getSelectionPosition() != -1) + { + setSelectionPosition(-1); + } + if(Keyboard.isCtrlDown()) + { + Word word = text.getMetadata().getWord(getCurserPosition()); + if(word == null) + { + return true; + } + if(word.getEndIndex() == getCurserPosition()) + { + if(word.getNext() != null) + { + setCurserPosition(word.getNext().getEndIndex()); + } + } + else + { + setCurserPosition(word.getEndIndex()); + } + } + else + { + setCurserPosition(getCurserPosition()+1); + } + return true; + } + else + { + if(!Keyboard.isShiftDown() && hasSelectedText()) + { + setSelectionPosition(-1); + } + return true; + } + } + else if(key == GLFW.GLFW_KEY_UP) + { + if(Keyboard.isShiftDown() && !hasSelectedText()) + { + setSelectionPosition(getCurserPosition()); + } + else if(!Keyboard.isShiftDown() && hasSelectedText()) + { + setSelectionPosition(-1); + } + setCurserPosition(text.getMetadata().moveDown(curserPos)); + } + else if(key == GLFW.GLFW_KEY_DOWN) + { + if(Keyboard.isShiftDown() && !hasSelectedText()) + { + setSelectionPosition(getCurserPosition()); + } + else if(!Keyboard.isShiftDown() && hasSelectedText()) + { + setSelectionPosition(-1); + } + setCurserPosition(text.getMetadata().moveUp(curserPos)); + } + else if(key == GLFW.GLFW_KEY_END) + { + if(Keyboard.isShiftDown()) + { + if(!hasSelectedText()) + { + setSelectionPosition(getCurserPosition()); + } + } + else + { + setSelectionPosition(-1); + } + setCurserToEnd(); + return true; + } + else if(key == GLFW.GLFW_KEY_HOME) + { + if(Keyboard.isShiftDown()) + { + if(getSelectionPosition() == -1) + { + setSelectionPosition(getCurserPosition()); + } + } + else + { + setSelectionPosition(-1); + } + setCurserPosition(0); + return true; + } + else if(key == GLFW.GLFW_KEY_TAB) + { + writeText(Character.toString('\t')); + return true; + } + else if(key == GLFW.GLFW_KEY_ENTER) + { + writeText(Character.toString('\n')); + return true; + } + else if(isSelect(key)) + { + setSelectionPosition(0); + setCurserToEnd(); + return true; + } + else if(isCopy(key)) + { + if(hasSelectedText()) + { + getWindow().setClipboardString(getSelectedText()); + } + return true; + } + else if(isPaste(key)) + { + String text = getWindow().getClipboardString(); + if(text != null) + { + writeText(text); + } + return true; + } + else if(isCut(key)) + { + if(hasSelectedText()) + { + getWindow().setClipboardString(getSelectedText()); + writeText(""); + } + return true; + } + else if(Keyboard.isPrintableKey(key)) + { + return true; + } + return false; + } + + @Override + public boolean onKeyTyped(char letter, int keyCode) + { + return isFlagSet(FLAG_FOCUS) && text.getFont().isCharValid(letter) && writeText(Character.toString(letter)); + } + + public boolean deleteAtCurser(int amount) + { + if(text.length() > 0) + { + if(hasSelectedText()) + { + int startPos = Math.min(getCurserPosition(), getSelectionPosition()); + writeText(""); + setCurserPosition(startPos); + } + else + { + int startPos = Math.min(getCurserPosition(), getCurserPosition() + amount); + int endPos = Math.max(getCurserPosition(), getCurserPosition() + amount); + StringBuilder builder = new StringBuilder(); + if(startPos >= 0) + { + builder.append(text.getText(0, startPos)); + } + if(endPos < text.length()) + { + builder.append(text.getText(endPos)); + } + String s = builder.toString(); + if (validator.test(s)) + { + setText(s); + setCurserPosition(Math.min(startPos, text.length())); + notifyListeners(LISTENER_USER_ACTION); + } + } + return true; + } + return false; + } + + public boolean writeText(String toWrite) + { + toWrite = text.getFont().clearInvalidLetters(toWrite); + StringBuilder builder = new StringBuilder(); + int startPos = hasSelectedText() ? Math.min(getSelectionPosition(), getCurserPosition()) : getCurserPosition(); + int endPos = hasSelectedText() ? Math.max(getSelectionPosition(), getCurserPosition()) : getCurserPosition(); + int room = maxTextLength - text.length() - (startPos - endPos); + if(text.length() > 0) + { + builder.append(text.getText(0, startPos)); + } + int moved = 0; + if(room < toWrite.length()) + { + builder.append(toWrite.substring(0, room)); + moved = room; + } + else + { + builder.append(toWrite); + moved = toWrite.length(); + } + if(text.length() > 0 && endPos < text.length()) + { + builder.append(text.getText(endPos)); + } + String s = builder.toString(); + if(validator.test(s)) + { + setText(s); + setCurserPosition(Math.min(endPos + moved, text.length())); + setSelectionPosition(-1); + notifyListeners(LISTENER_USER_ACTION); + return true; + } + return false; + } + + protected void handleDoubleClick(int position) + { + Word word = text.getMetadata().getWord(position); + if(word == null) + { + return; + } + if(!word.isSpecial()) + { + setSelectionPosition(word.getStartIndex()); + setCurserPosition(word.getEndIndex()); + return; + } + WordType type = word.getType(); + if(type.isStartWord()) + { + Word other = type.findEndWord(word); + if(other != null) + { + setSelectionPosition(word.getStartIndex()); + setCurserPosition(other.getEndIndex()); + } + } + else if(type.isEndWord()) + { + Word other = type.findStartWord(word); + if(other != null) + { + setSelectionPosition(other.getStartIndex()); + setCurserPosition(word.getEndIndex()); + } + } + else if(type.isDualWord()) + { + if(word.getStartIndex() == position && word.getPrev() != null) + { + Word other = type.findStartWord(word); + if(other != null) + { + setSelectionPosition(other.getStartIndex()); + setCurserPosition(word.getEndIndex()); + } + } + else + { + Word other = type.findEndWord(word); + if(other != null) + { + setSelectionPosition(word.getStartIndex()); + setCurserPosition(other.getEndIndex()); + } + } + } + else + { + setSelectionPosition(word.getStartIndex()); + setCurserPosition(word.getEndIndex()); + } + } + + public int getMousePosition(int mouseX, int mouseY) + { + return text.getMetadata().getIndex(mouseX - text.getBox().getMinX(), mouseY - text.getBox().getMinY()); + } + + public void setCurserToEnd() + { + setCurserPosition(curserPosition); + } + + public void setCurserPosition(int curserPosition) + { + if(this.curserPosition == curserPosition) + { + return; + } + this.curserPosition = curserPosition; + text.getMetadata().convert(curserPosition, curserPos); + } + + public int getCurserPosition() + { + return curserPosition; + } + + public void setSelectionPosition(int selectionPosition) + { + if(this.selectionPosition == selectionPosition) + { + return; + } + this.selectionPosition = selectionPosition; + if(selectionPosition == -1) + { + selectionPos.set(Vec2i.MINUS_ONE); + return; + } + text.getMetadata().convert(selectionPosition, selectionPos); + } + + public int getSelectionPosition() + { + return selectionPosition; + } + + public boolean hasSelectedText() + { + return selectionPosition != -1; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/TooltipPanel.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/TooltipPanel.java new file mode 100644 index 0000000..fc4b8d8 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/TooltipPanel.java @@ -0,0 +1,79 @@ +package speiger.src.coreengine.rendering.gui.components; + +import java.util.List; +import java.util.UUID; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap; +import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap; +import speiger.src.collections.objects.utils.maps.Object2ObjectMaps; +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; +import speiger.src.coreengine.rendering.gui.helper.box.ParentBox; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.MenuConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.ParentConstrain; + +public class TooltipPanel extends PanelComponent +{ + List helperList = new ObjectArrayList<>(); + List indexedComponents = new ObjectArrayList<>(); + Object2ObjectMap renderedTooltips = new Object2ObjectLinkedOpenHashMap<>(); + ColorObject color = ColorObject.BLACK; + ColorObject borderColor = ColorObject.DARK_GRAY; + IGuiBox box = new ParentBox(1.2F); + + @Override + public void init() + { + super.init(); + addBox(box); + } + + public void updateTooltips(Object2ObjectMap newTooltips) + { + helperList.addAll(renderedTooltips.keySet()); + helperList.removeAll(newTooltips.keySet()); + if(helperList.size() > 0) + { + for(int i = 0;i entry : Object2ObjectMaps.fastIterable(newTooltips)) + { + renderedTooltips.put(entry.getKey(), entry.getValue()); + indexedComponents.add(entry.getValue()); + addChild(entry.getValue(), new ComponentConstrains(new ParentConstrain(3.2F), new MenuConstrain<>(indexedComponents, () -> 1F), null, null)); + } + float[] data = new float[]{ + Float.MAX_VALUE, + Float.MAX_VALUE, + Float.MIN_VALUE, + Float.MIN_VALUE + }; + for(GuiComponent child : indexedComponents) + { + if(child.isVisible()) child.calculateActualBounds(data, false); + } + setComponentBounds(data[2]-data[0] + 6.4F, data[3]-data[1] + 2.5F); + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + getRenderer().drawQuad(getBox(), -0.01F, color).drawQuad(box, 0F, borderColor); + return true; + } + + public float isOutsideScreen(int mouseX, int width) + { + return mouseX + getBox().getWidth() > width ? -(getBox().getWidth() + 5F) : 10F; + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/TreeComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/TreeComponent.java new file mode 100644 index 0000000..530d9f3 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/TreeComponent.java @@ -0,0 +1,676 @@ +package speiger.src.coreengine.rendering.gui.components; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.sets.ObjectOpenHashSet; +import speiger.src.collections.objects.sets.ObjectSet; +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.math.misc.Facing; +import speiger.src.coreengine.math.vector.ints.Vec2i; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.base.IButtonComponent; +import speiger.src.coreengine.rendering.gui.components.tree.ITreeEntry; +import speiger.src.coreengine.rendering.gui.helper.UIShapes; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; +import speiger.src.coreengine.rendering.gui.helper.constrains.Constraints; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; +import speiger.src.coreengine.rendering.gui.renderer.buffer.RenderBuffer; +import speiger.src.coreengine.rendering.input.Keyboard; + +public class TreeComponent extends GuiComponent implements IButtonComponent +{ + public static final int SELECTION_MODE_DISABLE = 0; + public static final int SELECTION_MODE_SINGLE = 1; + public static final int SELECTION_MODE_MULTI = 2; + public static final int SELECTION_MODE_INTERACT = 3; + + public static final int UPDATE_MODE_DISABLED = 0; + public static final int UPDATE_MODE_VISIBLE = 1; + public static final int UPDATE_MODE_OPEN = 2; + public static final int UPDATE_MODE_ALL = 3; + + public static final int FLAG_NO_BACKGROUND = 1 << 20; + + ScrollBarComponent verticalBar = new ScrollBarComponent(ColorObject.LIGHT_GRAY); + ScrollBarComponent horizontalBar = new ScrollBarComponent(ColorObject.LIGHT_GRAY).setHorizontal(true); + + ColorObject color; + ColorObject selectedColor; + ColorObject hoverColor = ColorObject.LIGHT_GRAY; + int hoverIndex = -1; + int dragIndex = -1; + int movement; + Vec2i lastMouse = Vec2i.newMutable(); + IButtonComponent customButton; + protected ObjectSet openNodes = new ObjectOpenHashSet(); + protected ObjectSet selectedNodes = new ObjectOpenHashSet(); + protected int selectionMode = 1; + protected int updateMode = 1; + T node; + float entryHeight; + + boolean listChange = true; + List visibleNodes = new ObjectArrayList(); + RenderBuffer buffer; + + public TreeComponent(ColorObject color, float entryHeight) + { + super(0F, 0F, 0F, 0F); + this.entryHeight = entryHeight; + this.color = color; + this.selectedColor = color; + } + + public TreeComponent(ColorObject color, float entryHeight, T entry) + { + super(0F, 0F, 0F, 0F); + this.entryHeight = entryHeight; + getNodes(entry, openNodes, false); + this.color = color; + this.selectedColor = color; + node = entry; + if(entry != null) + { + entry.calculateDebth(); + } + } + + public TreeComponent(float x, float y, float width, float height, ColorObject color, float entryHeight, T entry) + { + super(x, y, width, height); + this.entryHeight = entryHeight; + getNodes(entry, openNodes, false); + this.color = color; + node = entry; + if(entry != null) + { + entry.calculateDebth(); + } + } + + @Override + public void init() + { + addCloseListener(buffer = getRenderer().createBuffer()); + addChild(horizontalBar, Constraints.getScrollbarConstraints(verticalBar::isInUse, true, 5F)); + addChild(verticalBar, Constraints.getScrollbarConstraints(horizontalBar::isInUse, false, 5F)); + List entries = new ObjectArrayList(); + getNodes(node, entries, false); + for(int i = 0, m = entries.size();i < m;i++) + { + entries.get(i).init(this, getGui()); + } + updateScrollBar(); + createArrow(); + } + + public TreeComponent setColor(ColorObject color) + { + if(this.color != color) + { + this.color = color; + onComponentChanged(true); + } + return this; + } + + public TreeComponent setHoverColor(ColorObject color) + { + hoverColor = color; + return this; + } + + public TreeComponent setSelectionColor(ColorObject color) + { + selectedColor = color; + return this; + } + + public TreeComponent setEntryHeight(float entryHeight) + { + if(this.entryHeight != entryHeight) + { + this.entryHeight = entryHeight; + onComponentChanged(true); + } + return this; + } + + public TreeComponent setSelectionMode(int mode) + { + if(mode < 0 || mode > 3) + { + throw new IllegalStateException("Unknown Mode"); + } + this.selectionMode = mode; + selectedNodes.clear(); + return this; + } + + public TreeComponent setUpdateMode(int mode) + { + if(mode < 0 || mode > 3) + { + throw new IllegalStateException("Unknown Mode"); + } + updateMode = mode; + return this; + } + + public TreeComponent disableBackground(boolean value) + { + setFlag(FLAG_NO_BACKGROUND, value); + return this; + } + + @Override + protected void repaint() + { + float scale = getBox().getScale(); + List entries = new ObjectArrayList(); + getNodes(node, entries, false); + for(int i = 0,m=entries.size();i setTree(T entry) + { + if(node == entry) + { + return this; + } + node = entry; + if(entry != null) + { + entry.calculateDebth(); + if(getGui() != null) + { + List entries = new ObjectArrayList(); + getNodes(entry, entries, false); + for(int i = 0,m=entries.size();i entries = new ObjectArrayList(); + getNodes(node, entries, false); + for(int i = 0,m=entries.size();i 0; + } + + public T getSelectedNode() + { + return selectedNodes.isEmpty() ? null : selectedNodes.iterator().next(); + } + + public List getSelectedNodes() + { + return new ObjectArrayList(this.selectedNodes); + } + + public List getOpenNodes() + { + return new ObjectArrayList(this.visibleNodes); + } + + public List getAllNodes() + { + List list = new ObjectArrayList(); + getNodes(node, list, false); + return list; + } + + public void openAll() + { + if(node != null) + { + openNode(node, true); + } + } + + public void closeAll() + { + if(node != null) + { + closeNode(node, true); + } + } + + public void openNode(T entry, boolean childrenIncluded) + { + openNodes.add(entry); + if(childrenIncluded) + { + getNodes(entry, openNodes, false); + } + listChange = true; + updateScrollBar(); + } + + public void toggleNode(T entry, boolean childrenIncluded) + { + if(isOpen(entry)) + { + closeNode(entry, childrenIncluded); + return; + } + openNode(entry, childrenIncluded); + } + + public boolean isOpen(T entry) + { + return openNodes.contains(entry); + } + + public void closeNode(T entry, boolean childrenIncluded) + { + openNodes.remove(entry); + if(childrenIncluded) + { + Set entries = new ObjectOpenHashSet(); + getNodes(entry, entries, false); + openNodes.removeAll(entries); + } + listChange = true; + updateScrollBar(); + } + + protected void createArrow() + { + float pixelSize = (entryHeight * 0.5F); + UIShapes.createArrow(buffer, pixelSize, pixelSize, color, Facing.EAST); + UIShapes.createArrow(buffer, pixelSize, pixelSize, color, Facing.SOUTH); + } + + protected void updateScrollBar() + { + if(listChange) + { + listChange = false; + visibleNodes.clear(); + getNodes(node, visibleNodes, true); + customButton = null; + } + float width = 0F; + float pixelSize = getBox().getScale() * entryHeight; + for(int i = 0,m=visibleNodes.size();i entries = new ObjectArrayList(); + getNodes(node, entries, false); + for(int i = 0,m=entries.size();i= visibleNodes.size() ? -1 : index; + return true; + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float brightness = getActiveBrightness(); + UIRenderer render = getRenderer(); + int start = getStartIndex(); + int end = MathUtils.clamp(0, visibleNodes.size(), start + getIndexWidth()); + IGuiBox box = getBox(); + float scale = box.getScale(); + float minX = horizontalBar.getScroll() * scale; + float minY = verticalBar.getScroll() * scale; + float maxX = box.getWidth() - verticalBar.getRequiredSpace() + (horizontalBar.getScroll() * scale); + float pixelSize = scale * entryHeight; + float offsetSize = pixelSize * 0.8F; + if(isFlagNotSet(FLAG_NO_BACKGROUND)) + { + render.setBrightness(brightness).drawQuad(box, color); + } + render.push(); + render.translate(box.getMinX(-horizontalBar.getScroll()), box.getMinY(-verticalBar.getScroll())); + enableScissorsBox(box.getMinX(), box.getMinY(), box.getMaxX()-verticalBar.getRequiredSpace(), box.getMaxY()-horizontalBar.getRequiredSpace()); + boolean skip = false; + if(hoverIndex != -1 && hoverIndex >= start && hoverIndex < end) + { + T node = visibleNodes.get(hoverIndex); + float xOffset = (offsetSize + (node.getDebth() * offsetSize * 0.6F)) * 0.8F; + if(!node.isLeaf() && mouseX - box.getMinX() >= xOffset - (pixelSize * 0.6F) && mouseX - box.getMinX() <= xOffset) + { + skip = true; + } + if(!skip) + { + int extraX = (int)(minX - getBox().getMinX() - xOffset); + int extraY = (int)(minY - hoverIndex * pixelSize - getBox().getMinY()); + if(node instanceof IButtonComponent && ((IButtonComponent)node).isComponentColliding(mouseX + extraX, mouseY + extraY)) + { + skip = true; + } + } + float otherMax = node.getHighlightWidth() >= 0F ? xOffset + node.getHighlightWidth() : maxX; + if(mouseX - box.getMinX() >= otherMax) skip = true; + if(!skip && selectionMode != SELECTION_MODE_INTERACT) + { + float offset = pixelSize * hoverIndex; + render.drawQuad(minX, offset, Math.min(otherMax, maxX), offset + pixelSize, 0.01F, hoverColor); + } + } + mouseX -= box.getMinX(); + mouseY -= box.getMinY(); + if(selectedNodes.size() > 0) + { + render.setBrightness(0.75F * brightness); + for(int i = start;i collector) + { + super.collectTooltips(mouseX, mouseY, particalTicks, collector); + int start = getStartIndex(); + int end = MathUtils.clamp(0, visibleNodes.size(), start + getIndexWidth()); + mouseX -= getBox().getMinX(); + mouseY -= getBox().getMinY(); + float scale = getBox().getScale(); + float minX = horizontalBar.getScroll() * scale; + float minY = verticalBar.getScroll() * scale; + float pixelSize = scale * entryHeight; + float offsetSize = pixelSize * 0.8F; + for(int i = start;i= offsetX - (entryHeight * scale * 0.6F) && mouseX - getBox().getMinX() <= offsetX) + { + toggleNode(visibleNodes.get(hoverIndex), Keyboard.isShiftDown()); + return true; + } + if(visibleNodes.get(hoverIndex) instanceof IButtonComponent) + { + IButtonComponent comp = (IButtonComponent)visibleNodes.get(hoverIndex); + int extraX = (int)((horizontalBar.getScroll() * scale - getBox().getMinX()) - offsetX); + int extraY = (int)(verticalBar.getScroll() * scale - (hoverIndex * scale * entryHeight) - getBox().getMinY()); + if(comp.isComponentColliding(mouseX + extraX, mouseY + extraY) && comp.onClick(button, mouseX + extraX, mouseY + extraY)) + { + customButton = comp; + return true; + } + } + } + dragIndex = hoverIndex; + if(selectionMode == SELECTION_MODE_INTERACT) + { + dragIndex = -1; + } + lastMouse.set(mouseX, mouseY); + return true; + } + + @Override + public boolean onDrag(int mouseX, int mouseY) + { + if(horizontalBar.onDrag(mouseX, mouseY) || verticalBar.onDrag(mouseX, mouseY)) + { + return true; + } + if(customButton != null) + { + float scale = getBox().getScale(); + float offsetSize = entryHeight * scale * 0.8F; + int extraX = (int)((horizontalBar.getScroll() * scale - getBox().getMinX()) - ((offsetSize + (visibleNodes.get(hoverIndex).getDebth() * offsetSize * 0.6F)) * 0.8F)); + int extraY = (int)(verticalBar.getScroll() * scale - (hoverIndex * scale * entryHeight) - getBox().getMinY()); + return customButton.onDrag(mouseX + extraX, mouseY + extraY); + } + horizontalBar.addScroll(lastMouse.getX() - mouseX); + verticalBar.addScroll(lastMouse.getY() - mouseY); + movement += Math.abs(lastMouse.getX() - mouseX) + Math.abs(lastMouse.getY() - mouseY); + lastMouse.set(mouseX, mouseY); + return true; + } + + @Override + public void onRelease(int button, int mouseX, int mouseY) + { + horizontalBar.onRelease(button, mouseX, mouseY); + verticalBar.onRelease(button, mouseX, mouseY); + if(customButton != null && (hoverIndex >= 0 && hoverIndex < visibleNodes.size() && customButton == visibleNodes.get(hoverIndex))) + { + float scale = getBox().getScale(); + float offsetSize = entryHeight * scale * 0.8F; + int extraX = (int)((horizontalBar.getScroll() * scale - getBox().getMinX()) - ((offsetSize + (visibleNodes.get(hoverIndex).getDebth() * offsetSize * 0.6F)) * 0.8F)); + int extraY = (int)(verticalBar.getScroll() * scale - (hoverIndex * scale * entryHeight) - getBox().getMinY()); + customButton.onRelease(button, mouseX + extraX, mouseY + extraY); + customButton = null; + } + else if(dragIndex != -1 && dragIndex == hoverIndex && movement < 2) + { + if(isNodeSelected(visibleNodes.get(hoverIndex))) + { + removeSelectedNode(visibleNodes.get(hoverIndex)); + } + else + { + addSelectedNode(visibleNodes.get(hoverIndex)); + } + notifyListeners(LISTENER_USER_ACTION); + dragIndex = -1; + } + movement = 0; + } + + @Override + public boolean onScroll(int scroll, int mouseX, int mouseY) + { + if((horizontalBar.isComponentColliding(mouseX, mouseY) && horizontalBar.onScroll(scroll, mouseX, mouseY)) || (verticalBar.isComponentColliding(mouseX, mouseY) && verticalBar.onScroll(scroll, mouseX, mouseY))) + { + movement = 100; + return true; + } + if(hoverIndex != -1 && visibleNodes.get(hoverIndex) instanceof IButtonComponent) + { + IButtonComponent comp = (IButtonComponent)visibleNodes.get(hoverIndex); + float scale = getBox().getScale(); + float offsetSize = entryHeight * scale * 0.8F; + int extraX = (int)((horizontalBar.getScroll() * scale - getBox().getMinX()) - ((offsetSize + (visibleNodes.get(hoverIndex).getDebth() * offsetSize * 0.6F)) * 0.8F)); + int extraY = (int)(verticalBar.getScroll() - (hoverIndex * getBox().getScale() * entryHeight) - getBox().getMinY()); + if(comp.isComponentColliding(mouseX + extraX, mouseY + extraY) && comp.onScroll(scroll, mouseX + extraX, mouseY + extraY)) + { + return true; + } + } + if(verticalBar.isInUse()) + { + verticalBar.addScroll(-(int)(scroll * 5F * getBox().getScale())); + return true; + } + return false; + } + + public int getStartIndex() + { + return MathUtils.clamp(0, visibleNodes.size(), MathUtils.floor(verticalBar.getScroll() / entryHeight)); + } + + public int getIndexWidth() + { + return MathUtils.clamp(0, visibleNodes.size(), MathUtils.ceil((getBox().getBaseHeight() - (horizontalBar.getRequiredSpace() / getBox().getScale())) / entryHeight) + 1); + } + + protected void getNodes(T entry, Collection collection, boolean openOnly) + { + if(entry != null) + { + collection.add(entry); + if(!entry.isLeaf() && (!openOnly || openNodes.contains(entry))) + { + for(int i = 0,m=entry.getChildCount();i +{ + public static final Vec2f DEFAULT_MINIMUM_BOUNDS = Vec2f.newVec(75F, 7.5F); + public static final int FLAG_MINIMIZED = 1 << 20; + public static final int FLAG_RESIZEABLE = 1 << 21; + public static final int FLAG_MOVEABLE = 1 << 22; + public static final int FLAG_RESIZEABLE_HORIZONTAL = 1 << 23; + public static final int FLAG_RESIZEABLE_VERTICAL = 1 << 24; + public static final int FLAG_RESIZE_INVERT = 1 << 25; + + public static final int WINDOW_FLAG_CLOSEABLE = 1; + public static final int WINDOW_FLAG_MINIMIZEABLE = 2; + + public static final int WINDOW_FLAGS = WINDOW_FLAG_CLOSEABLE | WINDOW_FLAG_MINIMIZEABLE; + + public static final int DEFAULT_FLAGS = WINDOW_FLAGS | FLAG_RESIZEABLE | FLAG_MOVEABLE; + public static final int FIXED_SIZE_WINDOW = WINDOW_FLAGS | FLAG_MOVEABLE; + public static final int FIXED_SIZE_POPUP = WINDOW_FLAG_CLOSEABLE | FLAG_MOVEABLE; + public static final int UNCLOSEABLE_WINDOW = WINDOW_FLAG_MINIMIZEABLE | FLAG_RESIZEABLE | FLAG_MOVEABLE; + public static final int SUB_WINDOW = WINDOW_FLAG_MINIMIZEABLE | FLAG_RESIZEABLE | FLAG_RESIZE_INVERT; + + final int flags; + FacingList facing = null; + String name; + ColorObject color = ColorObject.WINDOW_DEFAULT_BACKGROUND; + Vec2f lastSize = Vec2f.newMutable(); + Vec2i lastClick = Vec2i.newMutable(); + IValue animation = null; + protected final Consumer closeListener = new ConsumerConverter(0, this); + protected final Consumer minimizedListener = new ConsumerConverter(2, this); + + public WindowComponent(float x, float y, float width, float height, int flags, String name) + { + super(x, y, width, height); + this.name = name; + this.flags = flags & WINDOW_FLAGS; + setFlag(flags &= ~(WINDOW_FLAGS)); + setFlag(FLAG_RESIZEABLE_HORIZONTAL | FLAG_RESIZEABLE_VERTICAL); + lastSize.set(getBox().getBaseWidth(), getBox().getBaseHeight()); + } + + @Override + public void init() + { + super.init(); + LabelComponent label = new LabelComponent(name, ColorObject.DARK_GRAY); + label.getText().setTextScale(0.4F).setHorizontalAlignment(Align.LEFT_TOP).setForcedSingleLine(true); + addChild(label, new ComponentConstrains(null, null, new ParentConstrain(), new PixelConstrain(7.5F))); + float offset = 9F; + if((flags & WINDOW_FLAG_CLOSEABLE) != 0) + { + addChild(new IconButtonComponent(0F, 0F, 7.5F, 7.5F, ColorObject.RED, new CrossIcon(ColorObject.WHITE).setPadding(2.5F, 2F)).addUserActionListener(new ConsumerConverter(0, this)).setZOffset(0.001F), new ComponentConstrains(new PixelConstrain(offset).setInverted(), null, null, null)); + offset += 7.5F; + } + if((flags & WINDOW_FLAG_MINIMIZEABLE) != 0) + { + addChild(new IconButtonComponent(0F, 0F, 7.5F, 7.5F, ColorObject.GRAY, new LineIcon(ColorObject.WHITE, 0.7F, 0.25F)).addUserActionListener(new ConsumerConverter(1, this)).setZOffset(0.001F), new ComponentConstrains(new PixelConstrain(offset).setInverted(), null, null, null)); + } + if(canMoveIntoForground()) + { + setFlag(FLAG_RENDER_ORDER); + } + } + + protected void updateMinizedState(boolean value) + { + if(setFlag(FLAG_MINIMIZED, value)) + { + onComponentChanged(false); + } + } + + public final WindowComponent setMinimized(boolean value) + { + if((flags & WINDOW_FLAG_MINIMIZEABLE) != 0 && setFlag(FLAG_MINIMIZED, value)) + { + if(value) + { + Vec2f last = InternalThreadPools.VEC2F.get().set(lastSize); + setComponentBounds(last.getX(), isFlagSet(FLAG_MINIMIZED) ? getMinimizedY() : last.getY()); + lastSize.set(last); + InternalThreadPools.VEC2F.accept(last.negate()); + } + else setComponentBounds(lastSize.getX(), lastSize.getY()); + onComponentChanged(false); + } + return this; + } + + public final boolean isMinimized() + { + return isFlagSet(FLAG_MINIMIZED); + } + + public Vec2f getMinimumBounds() + { + return DEFAULT_MINIMUM_BOUNDS; + } + + public float getMinimizedY() + { + return 7.5F; + } + + public WindowComponent setColor(ColorObject color) + { + this.color = color; + return this; + } + + @Override + public void calculateActualBounds(float[] area, boolean start) + { + if(animation != null) + { + float scale = getBox().getScale(); + area[0] = Math.min(area[0], getBox().getMinX()); + area[1] = Math.min(area[1], getBox().getMinY()); + area[2] = Math.max(area[2], lastSize.getX() * scale); + area[3] = Math.max(area[3], animation.get(getMinimizedY(), lastSize.getY()) * scale); + return; + } + super.calculateActualBounds(area, start); + } + + @Override + protected void updateState() + { + if(animation == null) + { + if(isFlagSet(FLAG_MINIMIZED)) + { + lastSize.setX(getBox().getBaseWidth()); + return; + } + lastSize.set(getBox().getBaseWidth(), getBox().getBaseHeight()); + } + } + + @Override + protected boolean updateSelf(int mouseX, int mouseY, float particalTicks) + { + if(animation != null) + { + animation.update(particalTicks); + if(animation.isDone()) + { + if(animation.get() < 0.1F) + { + updateMinizedState(true); + } + Vec2f last = InternalThreadPools.VEC2F.get().set(lastSize); + setComponentBounds(last.getX(), isFlagSet(FLAG_MINIMIZED) ? getMinimizedY() : last.getY()); + lastSize.set(last); + InternalThreadPools.VEC2F.accept(last.negate()); + animation = null; + } + notifyListeners(LISTENER_ON_CHANGE); + } + else if(isFlagSet(FLAG_RESIZEABLE) && !isOverChild(mouseX, mouseY) && !getGui().hasComponentInTheWay(getTopComponent(), mouseX, mouseY)) + { + FacingList list = getBox().isColiding(mouseX, mouseY) ? getBox().getColidingBorder(mouseX, mouseY, 2F) : null; + if(list != null) + { + if(isFlagNotSet(FLAG_RESIZEABLE_HORIZONTAL)) list = list.remove(FacingList.HORIZONTAL); + if(isFlagNotSet(FLAG_RESIZEABLE_VERTICAL)) list = list.remove(FacingList.VERTICAL); + bindCursor(list.containsAny(FacingList.VERTICAL) && isFlagNotSet(FLAG_MINIMIZED) ? Cursor.CURSOR_VRESIZE : (list.containsAny(FacingList.HORIZONTAL) ? Cursor.CURSOR_HRESIZE : null)); + } + } + if(isPopup() && !hasChildPopups() && !getGui().isComponentInFront(this)) + { + requestFocus(); + } + return true; + } + + @Override + protected void onPreRender() + { + if(animation != null) + { + float scale = getBox().getScale(); + enableScissors(getBox().getMinX(), getBox().getMinY(), lastSize.getX() * scale, animation.get(getMinimizedY(), lastSize.getY()) * scale); + } + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + getRenderer().drawQuad(getBox(), color); + return true; + } + + @Override + protected void onPostRender() + { + if(animation != null) + { + disableScissors(); + } + } + + @Override + public void accept(GuiComponent value, int index) + { + switch(index) + { + case 0: + getGui().removeComponent(this); + break; + case 1: + if(animation != null || (flags & WINDOW_FLAG_MINIMIZEABLE) == 0) + { + break; + } + animation = (isMinimized() ? new LiniarValue(1F, 0F, 1F) : new LiniarValue(1F, 1F, 0F)).setSmooth(); + if(isMinimized()) + { + setComponentBounds(lastSize.getX(), lastSize.getY()); + } + updateMinizedState(false); + break; + case 2: + value.setVisible(!isMinimized()); + break; + } + } + + @Override + public boolean onClick(int button, int mouseX, int mouseY) + { + if(isOverChild(mouseX, mouseY) || getGui().hasComponentInTheWay(getTopComponent(), mouseX, mouseY)) + { + return false; + } + facing = getBox().getColidingBorder(mouseX, mouseY, 2F); + lastClick.set(mouseX, mouseY); + if(facing != null) + { + if(isFlagNotSet(FLAG_RESIZEABLE_HORIZONTAL)) facing = facing.remove(FacingList.HORIZONTAL); + if(isFlagNotSet(FLAG_RESIZEABLE_VERTICAL)) facing = facing.remove(FacingList.VERTICAL); + } + return true; + } + + @Override + public boolean onDrag(int mouseX, int mouseY) + { + if(facing != null) + { + if(facing.isEmpty() && isFlagSet(FLAG_MOVEABLE)) + { + moveComponent(mouseX - lastClick.getX(), mouseY - lastClick.getY()); + } + else if(!facing.isEmpty() && isFlagSet(FLAG_RESIZEABLE)) + { + float scale = getBox().getScale(); + float xChange = (mouseX - lastClick.getX()) * (facing.containsAny(FacingList.HORIZONTAL) ? 1F : 0F); + float yChange = (mouseY - lastClick.getY()) * (facing.containsAny(FacingList.VERTICAL) ? 1F : 0F); + if(isFlagSet(FLAG_RESIZE_INVERT)) + { + xChange *= -1F; + yChange *= -1F; + } + setMassChanging(); + if(facing.contains(Facing.NORTH) && isFlagNotSet(FLAG_MINIMIZED)) + { + resizeComponent(0F, -(yChange / scale)); + moveComponent(0F, yChange); + } + else if(facing.contains(Facing.SOUTH) && isFlagNotSet(FLAG_MINIMIZED)) + { + resizeComponent(0F, yChange / scale); + } + if(facing.contains(Facing.WEST)) + { + resizeComponent(-(xChange / scale) - 1F, 0F); + moveComponent(xChange, 0F); + } + else if(facing.contains(Facing.EAST)) + { + resizeComponent(xChange / scale, 0F); + } + ensureMinimumBounds(); + finishMassChanging(); + if(xChange > 0F || yChange > 0F) + { + onComponentChanged(true); + } + } + lastClick.set(mouseX, mouseY); + return true; + } + return false; + } + + @Override + public void onRelease(int button, int mouseX, int mouseY) + { + facing = null; + } + + @Override + public boolean canMoveIntoForground() + { + return true; + } + + @Override + public void onFocusLost() + { + facing = null; + } + + protected void ensureMinimumBounds() + { + IGuiBox box = getBox(); + Vec2f bounds = getMinimumBounds(); + if(box.getBaseWidth() < bounds.getX()) + { + box.setWidth(bounds.getX()); + onComponentChanged(true); + } + if(box.getBaseHeight() < bounds.getY()) + { + box.setHeight(bounds.getY()); + onComponentChanged(true); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/ArrowIcon.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/ArrowIcon.java new file mode 100644 index 0000000..0edcb5e --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/ArrowIcon.java @@ -0,0 +1,40 @@ +package speiger.src.coreengine.rendering.gui.components.icon; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.math.misc.Facing; +import speiger.src.coreengine.rendering.gui.helper.UIShapes; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; + +public class ArrowIcon implements IIcon +{ + ColorObject color; + Facing direction; + float scale = 1F; + + public ArrowIcon(ColorObject color, Facing direction) + { + this.color = color; + this.direction = direction; + } + + public ArrowIcon setDirection(Facing direction) + { + this.direction = direction; + return this; + } + + public ArrowIcon setScale(float scale) + { + this.scale = scale; + return this; + } + + @Override + public void render(UIRenderer render, float minX, float minY, float maxX, float maxY, float scale) + { + render.push().translate(minX + ((maxX - minX) * 0.5F), minY + ((maxY - minY) * 0.5F)); + UIShapes.createArrow(render, (maxX - minX) * this.scale, (maxY - minY) * this.scale, color, direction); + render.pop(); + } + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/CrossIcon.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/CrossIcon.java new file mode 100644 index 0000000..adae352 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/CrossIcon.java @@ -0,0 +1,33 @@ +package speiger.src.coreengine.rendering.gui.components.icon; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.helper.UIShapes; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; + +public class CrossIcon implements IIcon +{ + ColorObject color; + float paddingX = 4F; + float paddingY = 2F; + + public CrossIcon(ColorObject color) + { + this.color = color; + } + + public CrossIcon setPadding(float x, float y) + { + paddingX = x; + paddingY = y; + return this; + } + + @Override + public void render(UIRenderer render, float minX, float minY, float maxX, float maxY, float scale) + { + render.push().translate(minX + ((maxX - minX) / 2F), minY + ((maxY - minY) / 2F)); + UIShapes.createCross(render, maxX - minX, maxY - minY, paddingX * scale, paddingY * scale, color); + render.pop(); + } + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/IIcon.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/IIcon.java new file mode 100644 index 0000000..8b8f577 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/IIcon.java @@ -0,0 +1,10 @@ +package speiger.src.coreengine.rendering.gui.components.icon; + +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; + +public interface IIcon +{ + public default void render(UIRenderer render, IGuiBox box) {render(render, box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), box.getScale());}; + public void render(UIRenderer render, float minX, float minY, float maxX, float maxY, float scale); +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/LineIcon.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/LineIcon.java new file mode 100644 index 0000000..511f4c5 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/LineIcon.java @@ -0,0 +1,28 @@ +package speiger.src.coreengine.rendering.gui.components.icon; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; + +public class LineIcon implements IIcon +{ + ColorObject color; + float height; + float padding; + float thickness = 1F; + + public LineIcon(ColorObject color, float height, float padding) + { + this.color = color; + this.height = height; + this.padding = padding; + } + + @Override + public void render(UIRenderer render, float minX, float minY, float maxX, float maxY, float scale) + { + float y = minY + ((maxY - minY) * height); + float x = (maxX - minX) * padding; + render.drawQuad(minX + x, y - (0.5F * scale), maxX - x, y, color); + } + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/TexturedIcon.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/TexturedIcon.java new file mode 100644 index 0000000..15b4830 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/icon/TexturedIcon.java @@ -0,0 +1,53 @@ +package speiger.src.coreengine.rendering.gui.components.icon; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; +import speiger.src.coreengine.rendering.textures.ITexture; + +public class TexturedIcon implements IIcon +{ + ColorObject color = ColorObject.WHITE; + ITexture texture; + boolean forceBounds = false; + + public TexturedIcon(ITexture texture) + { + this.texture = texture; + } + + public TexturedIcon(ColorObject color, ITexture texture) + { + this.color = color; + this.texture = texture; + } + + public void setTexture(ITexture newTexture) + { + if(texture != null) + { + texture.deleteTexture(); + texture = null; + } + texture = newTexture; + } + + public TexturedIcon forceBounds(boolean value) + { + forceBounds = value; + return this; + } + + @Override + public void render(UIRenderer render, float minX, float minY, float maxX, float maxY, float scale) + { + if(forceBounds) + { + float width = texture.getWidth(); + float height = texture.getHeight(); + float ratio = height / width; + maxY = Math.min(minY + ((maxY - minY) * ratio), maxY); + } + render.setActiveTexture(texture).drawTexturedQuad(minX, minY, maxX, maxY, color, texture.getUMin(), texture.getVMin(), texture.getUMax(), texture.getVMax()); + } + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/layouts/FlowLayout.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/layouts/FlowLayout.java new file mode 100644 index 0000000..dd6e2a4 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/layouts/FlowLayout.java @@ -0,0 +1,55 @@ +package speiger.src.coreengine.rendering.gui.components.layouts; + +import java.util.List; +import java.util.function.Consumer; + +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; + +public class FlowLayout implements Consumer +{ + IGuiBox box; + List components; + Vec2f padding; + + public FlowLayout(IGuiBox box, Vec2f padding, List components) + { + this.padding = padding; + this.box = box; + this.components = components; + } + + @Override + public void accept(GuiComponent t) + { + float minX = box.getRelativeX(); + float minY = box.getRelativeY(); + float maxWidth = box.getWidth(); + int xInserted = 0; + int yInserted = 0; + float widthUsed = 0F; + float heightOffset = 0F; + float maxHeight = 0F; + for(int i = 0,m=components.size();i= maxWidth && xInserted > 0) + { + xInserted = 0; + maxHeight += heightOffset; + heightOffset = 0F; + widthUsed = 0F; + yInserted++; + } + component.setComponentPosition(minX + widthUsed + padding.getX(), minY + maxHeight + (padding.getY() * yInserted)); + heightOffset = Math.max(heightOffset, bounds[3] - bounds[1]); + widthUsed += width + padding.getX(); + xInserted++; + } + } + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/layouts/VerticalLayout.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/layouts/VerticalLayout.java new file mode 100644 index 0000000..adeab1a --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/layouts/VerticalLayout.java @@ -0,0 +1,84 @@ +package speiger.src.coreengine.rendering.gui.components.layouts; + +import java.util.List; +import java.util.function.Consumer; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; + +public class VerticalLayout implements Consumer +{ + GuiComponent parent; + List components = new ObjectArrayList<>(); + IGuiBox box; + float padding; + boolean changing = false; + + public VerticalLayout(List components, IGuiBox box, float padding) + { + this.components.addAll(components); + for(int i = 0;i T addComponent(T gui) + { + components.add(gui); + gui.addChangeListener(this); + gui.addCloseListener(this::removeComponent); + accept(null); + return gui; + } + + public void removeComponent(GuiComponent gui) + { + if(components.remove(gui)) + { + gui.removeChangeListener(this); + gui.removeCloseListener(this::removeComponent); + accept(null); + } + } + + @Override + public void accept(GuiComponent t) + { + if(changing) return; + changing = true; + float minX = box.getBaseX(); + float minY = box.getBaseY(); + float currentY = 0F; + for(int i = 0;i components = new ObjectArrayList<>(); + protected float maxWidth = -1F; + protected float lastScale = 1F; + protected T addComponent(T comp) + { + components.add(comp); + maxWidth = -1F; + return comp; + } + + @Override + public float getWidth() + { + if(maxWidth < 0F) + { + float[] value = new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MIN_VALUE, Float.MIN_VALUE}; + for(int i = 0,m=components.size();i collector) + { + for(int i = 0;i collector) + { + + } + + @Override + public boolean isComponentColliding(int mouseX, int mouseY) + { + return checkBox.isComponentColliding(mouseX, mouseY); + } + + @Override + public boolean onClick(int button, int mouseX, int mouseY) + { + return checkBox.onClick(button, mouseX, mouseY); + } + + @Override + public boolean onDrag(int mouseX, int mouseY) + { + return checkBox.onDrag(mouseX, mouseY); + } + + @Override + public void onRelease(int button, int mouseX, int mouseY) + { + checkBox.onRelease(button, mouseX, mouseY); + } + + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/list/FileEntry.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/list/FileEntry.java new file mode 100644 index 0000000..fc1b563 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/list/FileEntry.java @@ -0,0 +1,20 @@ + +package speiger.src.coreengine.rendering.gui.components.list; + +import java.io.File; + +public class FileEntry extends SelectionEntry +{ + File file; + + public FileEntry(File file) + { + this.file = file; + setText(file.getName()); + } + + public File getFile() + { + return file; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/list/IListEntry.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/list/IListEntry.java new file mode 100644 index 0000000..49f452d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/list/IListEntry.java @@ -0,0 +1,26 @@ +package speiger.src.coreengine.rendering.gui.components.list; + +import java.util.Map; +import java.util.UUID; + +import speiger.src.coreengine.rendering.gui.GuiBase; +import speiger.src.coreengine.rendering.gui.GuiComponent; + +public interface IListEntry +{ + public float getWidth(); + + public default float getHighlightWidth() { return -1F; } + + public void init(GuiComponent comp, GuiBase owner); + + public void updateState(GuiComponent comp, float scale); + + public void onClosed(); + + public void onFixedUpdate(); + + public void onRender(GuiComponent comp, boolean enabled, int mouseX, int mouseY, float particalTicks); + + public void collectTooltips(GuiComponent comp, int mouseX, int mouseY, float particalTicks, Map collector); +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/list/SelectionEntry.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/list/SelectionEntry.java new file mode 100644 index 0000000..316d38a --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/list/SelectionEntry.java @@ -0,0 +1,81 @@ +package speiger.src.coreengine.rendering.gui.components.list; + +import java.util.Map; +import java.util.UUID; + +import speiger.src.coreengine.rendering.gui.GuiBase; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.components.TextComponent; +import speiger.src.coreengine.rendering.gui.helper.Align; + +public class SelectionEntry implements IListEntry +{ + TextComponent text = new TextComponent().setLimitedBounds(false).setAlignment(Align.LEFT_TOP, Align.LEFT_TOP); + + public SelectionEntry(){} + + public SelectionEntry(String s) + { + setText(s); + } + + public SelectionEntry setText(String s) + { + text.setText(s); + return this; + } + + public SelectionEntry setTextScale(float scale) + { + text.setTextScale(scale); + return this; + } + + @Override + public float getWidth() + { + return text.getMetadata().getMaxWidth(); + } + + @Override + public void init(GuiComponent comp, GuiBase owner) + { + text.setOwner(owner); + text.setScale(comp.getBox().getScale()); + } + + @Override + public void updateState(GuiComponent comp, float scale) + { + text.setScale(scale); + } + + @Override + public void onClosed() + { + text.onClosed(); + } + + @Override + public void onFixedUpdate() + { + + } + + @Override + public void onRender(GuiComponent comp, boolean enabled, int mouseX, int mouseY, float particalTicks) + { + text.onRender(mouseX, mouseY, particalTicks); + } + + @Override + public void collectTooltips(GuiComponent comp, int mouseX, int mouseY, float particalTicks, Map collector) + { + text.collectTooltips(mouseX, mouseY, particalTicks, collector); + } + + public String getText() + { + return text.getText(); + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/list/TextEntry.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/list/TextEntry.java new file mode 100644 index 0000000..b5c861c --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/list/TextEntry.java @@ -0,0 +1,25 @@ +package speiger.src.coreengine.rendering.gui.components.list; + +import speiger.src.coreengine.rendering.gui.components.TextComponent; +import speiger.src.coreengine.rendering.gui.helper.Align; + +public class TextEntry extends BaseListEntry +{ + TextComponent text = addComponent(new TextComponent().setLimitedBounds(false).setAlignment(Align.LEFT_TOP, Align.LEFT_TOP).setSpecialRendering(true).setComponentPosition(0F, 0F).cast()); + + public TextEntry(String s) + { + text.setText(s); + } + + public TextEntry setScale(float scale) + { + text.setTextScale(scale); + return this; + } + + public String getText() + { + return text.getText(); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/menu/MenuBarComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/menu/MenuBarComponent.java new file mode 100644 index 0000000..e0772b9 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/menu/MenuBarComponent.java @@ -0,0 +1,103 @@ +package speiger.src.coreengine.rendering.gui.components.menu; + +import java.util.List; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.MenuConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.ParentConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.PixelConstrain; + +public class MenuBarComponent extends GuiComponent +{ + ColorObject color; + List menuItems = new ObjectArrayList(); + float scale = 1F; + + public MenuBarComponent(ColorObject color) + { + super(0F, 0F, 0F, 0F); + this.color = color; + } + + public MenuBarComponent(float x, float y, float width, float height, ColorObject color) + { + super(x, y, width, height); + this.color = color; + } + + @Override + public void init() + { + } + + public MenuBarComponent setTextScale(float scale) + { + if(this.scale != scale) + { + this.scale = scale; + for(int i = 0,m=menuItems.size();i(menuItems), new PixelConstrain(), item.createWidthConstriain(), new ParentConstrain())); + item.setZOffset(0.3F); + item.setTextScale(scale); + item.onComponentChanged(false); + return item; + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + getRenderer().drawQuad(getBox(), color); + return true; + } + + protected void closeMenus(GuiComponent owner) + { + for(int i = 0,m=menuItems.size();i +{ + public static final int FLAG_CHECKED = 1 << 21; + RenderBuffer buffer; + + public MenuCheckBoxComponent(String name) + { + super(name); + setFlag(FLAG_KEEP_MENU_OPEN | FLAG_SUPPORT_BINDING); + } + + public MenuCheckBoxComponent(String name, boolean checked) + { + super(name); + setFlag(FLAG_CHECKED, checked); + setFlag(FLAG_KEEP_MENU_OPEN | FLAG_SUPPORT_BINDING); + } + + public MenuCheckBoxComponent(float x, float y, float width, float height, String name) + { + super(x, y, width, height, name); + setFlag(FLAG_KEEP_MENU_OPEN | FLAG_SUPPORT_BINDING); + } + + public MenuCheckBoxComponent(float x, float y, float width, float height, String name, boolean checked) + { + super(x, y, width, height, name); + setFlag(FLAG_CHECKED, checked); + setFlag(FLAG_KEEP_MENU_OPEN | FLAG_SUPPORT_BINDING); + } + + @Override + public void init() + { + addChild(text, new ComponentConstrains(new PixelConstrain(getBox().getHeight() + 1F), new ParentConstrain(), new DynamicConstrain(() -> text.getMetadata().getMaxWidth() + 0.5F), new ParentConstrain())); + addCloseListener(buffer = getRenderer().createBuffer()); + } + + @Override + protected void repaint() + { + float width = getBox().getHeight(); + float height = getBox().getHeight(); + buffer.clear(); + UIShapes.createCross(buffer, width, height, (width / 3.3F), (height / 5F), color); + UIShapes.createCross(buffer, width, height, (width / 5F), (height / 10F), color); + } + + @Override + protected Constrain createWidthConstriain() + { + return new DynamicConstrain(() -> text.getMetadata().getMaxWidth() + getBox().getHeight() + 3F); + } + + @Override + public final boolean isChecked() + { + return isFlagSet(FLAG_CHECKED); + } + + @Override + public final MenuCheckBoxComponent setChecked(boolean value) + { + setFlag(FLAG_CHECKED, value); + return this; + } + + @Override + public boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float brightness = getActiveBrightness(); + float height = getBox().getHeight(); + float centerX = getBox().getMinX(height * 0.5F); + float centerY = getBox().getMinY(height * 0.5F); + getRenderer().setBrightness(brightness * 0.85F).drawQuad(getBox().getMinX(0.2F), getBox().getMinY(), getBox().getMinX(height + 0.2F), getBox().getMinY(height), color).setBrightness(brightness * 0.75F).drawFrame(getBox().getMinX(0.2F), getBox().getMinY(), getBox().getMinX(height + 0.2F), getBox().getMinY(height), color).translate(centerX, centerY); + if(isChecked()) + { + getRenderer().setBrightness(brightness * 0.7F).translate(0, 0, 0.001F).drawBuffers(buffer.selectionIterator(1), getBox().getWidth(), getBox().getHeight()).translate(0, 0, -0.001F); + } + if(isHovered(mouseX, mouseY)) + { + getRenderer().setBrightness(brightness * 1.3F).translate(0, 0, 0.002F).drawBuffers(buffer.selectionIterator(0), getBox().getWidth(), getBox().getHeight()).translate(0, 0, -0.002F); + } + float maxX = Math.min(boxWidth, getBox().getMaxX()); + getRenderer().setBrightness(1F).translate(-centerX, -centerY).drawQuad(getBox().getMinX(height), getBox().getMinY(), getBox().getMinX(maxX), getBox().getMaxY(), color).setBrightness(brightness * 0.75F).drawFrame(getBox().getMinX(height), getBox().getMinY(), getBox().getMinX(maxX), getBox().getMaxY(), color); + return true; + } + + protected boolean isCheckBoxHovered(int mouseX, int mouseY) + { + float height = getBox().getHeight(); + return getBox().getMinX() <= mouseX && getBox().getMinX(height) >= mouseX && getBox().getMinY() <= mouseY && getBox().getMinY(height) >= mouseY && getGui().hasComponentInTheWay(this, mouseX, mouseY); + } + + @Override + public boolean onClick(int button, int mouseX, int mouseY) + { + return true; + } + + @Override + public void onRelease(int button, int mouseX, int mouseY) + { + setFlag(FLAG_CHECKED, !isFlagSet(FLAG_CHECKED)); + notifyListeners(LISTENER_USER_ACTION); + } + + @Override + protected boolean onUserKey() + { + setFlag(FLAG_CHECKED, !isFlagSet(FLAG_CHECKED)); + notifyListeners(LISTENER_USER_ACTION); + return true; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/menu/MenuComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/menu/MenuComponent.java new file mode 100644 index 0000000..74f07e0 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/menu/MenuComponent.java @@ -0,0 +1,319 @@ +package speiger.src.coreengine.rendering.gui.components.menu; + +import java.util.List; +import java.util.function.Consumer; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.Constrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.DynamicConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.MenuConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.PixelConstrain; + +public class MenuComponent extends MenuItemComponent implements Consumer +{ + static final int FLAG_OPEN = 1 << 21; + static final int FLAG_SUB_MENU = 1 << 22; + List components = new ObjectArrayList(); + float defaultScale = 1F; + float width = 0F; + float entryHeight = 10F; + float textScale = 1F; + long timeNotHovered = -1; + MenuComponent ownerMenu; + + public MenuComponent(String name) + { + super(name); + clearFlag(FLAG_SUPPORT_BINDING); + } + + public MenuComponent(float x, float y, float width, float height, String name) + { + super(x, y, width, height, name); + clearFlag(FLAG_SUPPORT_BINDING); + } + + @Override + public boolean canMoveIntoForground() + { + return true; + } + + public MenuComponent setHeight(float height) + { + entryHeight = height; + return this; + } + + protected MenuComponent setParent(MenuComponent owner) + { + ownerMenu = owner; + return this; + } + + @Override + protected void setMenuColor(ColorObject color) + { + super.setMenuColor(color); + for(int i = 0,m=components.size();i").setIsSubMenu(true).setParent(this)).cast(); + } + + public MenuItemComponent addText(String name) + { + return addMenuItem(new MenuTextComponent(name)).cast(); + } + + public MenuItemComponent addMenuItem(String name) + { + return addMenuItem(new MenuItemComponent(name)); + } + + public MenuCheckBoxComponent addCheckBox(String name) + { + return addMenuItem(new MenuCheckBoxComponent(name)).cast(); + } + + public MenuCheckBoxComponent addCheckBox(String name, boolean value) + { + return addMenuItem(new MenuCheckBoxComponent(name, value)).cast(); + } + + public MenuItemComponent addMenuItem(MenuItemComponent comp) + { + components.add(comp); + comp.setScale(defaultScale); + comp.setMenuColor(color); + addChild(comp.setIgnoreBounds(true).addUserActionListener(this), createConstrains(comp)).onComponentChanged(false); + comp.setVisible(isFlagSet(FLAG_OPEN)); + comp.setTextScale(textScale); + return comp; + } + + protected ComponentConstrains createConstrains(MenuItemComponent comp) + { + Constrain constraint = isFlagSet(FLAG_SUB_MENU) ? new DynamicConstrain(() -> Math.max(getBox().getBaseWidth(), boxWidth / getBox().getScale())) : new PixelConstrain(); + return new ComponentConstrains(constraint, new MenuConstrain(components, isFlagSet(FLAG_SUB_MENU) ? MenuConstrain.DEFAULT : () -> getBox().getBaseHeight() + 0.1F).setPadding(0.01F), comp.createWidthConstriain(), new PixelConstrain(entryHeight)); + } + + @Override + public GuiComponent removeChild(GuiComponent comp) + { + super.removeChild(comp); + if(components.remove(comp)) + { + onComponentChanged(false); + } + return this; + } + + @Override + public GuiComponent removeChildren() + { + components.clear(); + return super.removeChildren(); + } + + public void removeElements() + { + for(MenuItemComponent comp : components) + { + super.removeChild(comp); + } + components.clear(); + } + + @Override + protected boolean updateSelf(int mouseX, int mouseY, float particalTicks) + { + if(isSubMenu() && isVisible()) + { + if(isFlagSet(FLAG_OPEN)) + { + if(isMouseOver(mouseX, mouseY) || isOverChild(mouseX, mouseY)) + { + timeNotHovered = -1; + return true; + } + if(timeNotHovered == -1) + { + timeNotHovered = getGlobalClock(); + } + if(getGlobalClock() - timeNotHovered > 10) + { + setOpen(false); + } + return true; + } + timeNotHovered = -1; + if(isMouseOver(mouseX, mouseY) && !ownerMenu.hasOtherMenuOpen(this)) + { + setOpen(true); + } + } + return true; + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float brigthness = isFlagSet(FLAG_OPEN) ? 0.7F : getBrightness(mouseX, mouseY); + float minX = getBox().getMinX(0.2F); + float minY = getBox().getMinY(0.2F); + float maxX = Math.max(getBox().getMaxX(), minX + boxWidth); + float maxY = getBox().getMaxY(-0.2F); + getRenderer().setBrightness(brigthness).drawQuad(minX, minY, maxX, maxY, color).setBrightness(brigthness * 0.5F).drawFrame(minX, minY, maxX, maxY, color); + text.setBrightness(brigthness); + return true; + } + + @Override + public boolean onClick(int button, int mouseX, int mouseY) + { + if(!isSubMenu()) + { + setOpen(!isFlagSet(FLAG_OPEN)).notifyListeners(LISTENER_USER_ACTION); + } + return true; + } + + protected MenuComponent setIsSubMenu(boolean value) + { + if(setFlag(FLAG_SUB_MENU, value)) + { + setFlag(FLAG_KEEP_MENU_OPEN, value); + for(int i = 0,m=components.size();i= mouseX && getBox().getMinY() <= mouseY && getBox().getMaxY() >= mouseY; + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float brigthness = getBrightness(mouseX, mouseY); + float minX = getBox().getMinX(0.2F); + float minY = getBox().getMinY(0.2F); + float maxX = Math.max(getBox().getMaxX(), minX + boxWidth); + float maxY = getBox().getMaxY(-0.2F); + getRenderer().setBrightness(brigthness).drawQuad(minX, minY, maxX, maxY, color).setBrightness(brigthness * 0.5F).drawFrame(minX, minY, maxX, maxY, color); + text.setBrightness(brigthness); + return true; + } + + @Override + public boolean onClick(int button, int mouseX, int mouseY) + { + return true; + } + + @Override + public void onRelease(int button, int mouseX, int mouseY) + { + notifyListeners(LISTENER_USER_ACTION); + } + + @Override + protected boolean onUserKey() + { + notifyListeners(LISTENER_USER_ACTION); + return true; + } + + @Override + public String toString() + { + return "Menu Item: "+getText().getText(); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/menu/MenuTextComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/menu/MenuTextComponent.java new file mode 100644 index 0000000..7acdb07 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/menu/MenuTextComponent.java @@ -0,0 +1,39 @@ +package speiger.src.coreengine.rendering.gui.components.menu; + +public class MenuTextComponent extends MenuItemComponent +{ + + public MenuTextComponent(String name) + { + super(name); + clearFlag(FLAG_SUPPORT_BINDING); + } + + public MenuTextComponent(float x, float y, float width, float height, String name) + { + super(x, y, width, height, name); + } + + @Override + public boolean isValidButton(int button) + { + return false; + } + + @Override + public boolean isComponentColliding(int mouseX, int mouseY) + { + return false; + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + float minX = getBox().getMinX(0.2F); + float minY = getBox().getMinY(0.2F); + float maxX = Math.max(getBox().getMaxX(), minX + boxWidth); + float maxY = getBox().getMaxY(-0.2F); + getRenderer().drawQuad(minX, minY, maxX, maxY, color).setBrightness(0.5F).drawFrame(minX, minY, maxX, maxY, color); + return true; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/misc/CheckBoxGroup.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/misc/CheckBoxGroup.java new file mode 100644 index 0000000..2bf1323 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/misc/CheckBoxGroup.java @@ -0,0 +1,118 @@ +package speiger.src.coreengine.rendering.gui.components.misc; + +import java.util.List; +import java.util.function.Consumer; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.lists.ObjectList; +import speiger.src.collections.objects.utils.ObjectLists; +import speiger.src.coreengine.rendering.gui.GuiComponent; + +public class CheckBoxGroup> implements Consumer +{ + ObjectList checkboxes = new ObjectArrayList(); + int selectedIndex = -1; + Runnable listener; + + public CheckBoxGroup setListener(Runnable listener) + { + this.listener = listener; + return this; + } + + public T add(T box) + { + checkboxes.add(box); + if(box.isChecked()) + { + if(selectedIndex == -1) + { + selectedIndex = checkboxes.indexOf(box); + } + else + { + box.setChecked(false); + } + } + box.addUserActionListener(this); + return box; + } + + public boolean remove(T box) + { + boolean result = checkboxes.remove(box); + if(result) + { + if(box.isChecked()) + { + selectedIndex = -1; + } + box.removeChangeListener(this); + } + return result; + } + + public T getSelected() + { + return selectedIndex == -1 ? null : checkboxes.get(selectedIndex); + } + + public boolean hasSelected() + { + return selectedIndex != -1; + } + + public int getSelectedIndex() + { + return selectedIndex; + } + + public List getCheckBoxes() + { + return ObjectLists.unmodifiable(checkboxes); + } + + public void setChecked(int selected) + { + if(selectedIndex != -1) + { + checkboxes.get(selectedIndex).setChecked(false); + } + selectedIndex = selected; + if(selectedIndex != -1) + { + checkboxes.get(selectedIndex).setChecked(true); + } + } + + @Override + public void accept(GuiComponent t) + { + ICheckBox box = t.tryCast(ICheckBox.class); + if(box == null) + { + return; + } + int index = checkboxes.indexOf(t); + if(index == -1) + { + return; + } + if(box.isChecked()) + { + if(selectedIndex != -1) + { + checkboxes.get(selectedIndex).setChecked(false); + } + selectedIndex = index; + } + else + { + selectedIndex = -1; + } + if(listener != null) + { + listener.run(); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/misc/ICheckBox.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/misc/ICheckBox.java new file mode 100644 index 0000000..4b8cf84 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/misc/ICheckBox.java @@ -0,0 +1,10 @@ +package speiger.src.coreengine.rendering.gui.components.misc; + +import speiger.src.coreengine.rendering.gui.GuiComponent; + +public interface ICheckBox +{ + public boolean isChecked(); + + public T setChecked(boolean value); +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/special/ConsoleComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/special/ConsoleComponent.java new file mode 100644 index 0000000..0421182 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/special/ConsoleComponent.java @@ -0,0 +1,223 @@ +package speiger.src.coreengine.rendering.gui.components.special; + +import java.util.List; +import java.util.function.Consumer; + +import org.lwjgl.glfw.GLFW; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.base.IKeyComponent; +import speiger.src.coreengine.rendering.gui.components.ListComponent; +import speiger.src.coreengine.rendering.gui.components.ScrollBarComponent; +import speiger.src.coreengine.rendering.gui.components.TextFieldComponent; +import speiger.src.coreengine.rendering.gui.components.list.TextEntry; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.ParentConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.PixelConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.RelativeConstrain; +import speiger.src.coreengine.rendering.gui.renderer.IFontRenderer; + +public class ConsoleComponent extends GuiComponent implements IKeyComponent +{ + TextFieldComponent chat = new TextFieldComponent(ColorObject.DARK_GRAY).setCanLoseFocus(false).setInfiniteText(true).setMaxTextLength(Integer.MAX_VALUE).setFocused(true); + ListComponent list = new ListComponent<>(ColorObject.DARK_GRAY.copy().setAlpha(120), 8F); + float lastWidth = 0F; + List messages = new ObjectArrayList<>(); + Consumer listener; + int messageIterator = 0; + boolean started = false; + + public ConsoleComponent() + { + this(0F, 0F, 0F, 0F); + } + + public ConsoleComponent(float x, float y, float width, float height) + { + super(x, y, width, height); + } + + public ConsoleComponent setListener(Consumer listener) + { + this.listener = listener; + return this; + } + + @Override + public void init() + { + chat.getRawText().setTextScale(0.45F); + chat.addUserActionListener(this::onEnter); + list.setSelectionMode(ListComponent.SELECTION_MODE_DISABLE); + list.setStartAtBottom(true); + addChild(chat, new ComponentConstrains(new ParentConstrain(), new ParentConstrain(12F).invert(), new ParentConstrain(), new PixelConstrain(12F))); + addChild(list, new ComponentConstrains(new ParentConstrain(), new ParentConstrain(100).invert(), new RelativeConstrain(0.8F), new PixelConstrain(88))); + } + + @Override + protected void updateState() + { + float width = list.getBox().getWidth(); + if(lastWidth != width) + { + lastWidth = width; + updateMessages(); + } + } + + protected void updateMessages() + { + list.clear(); + for(int i = 0,m=messages.size();i 0) + { + messageIterator--; + chat.setText(messages.get((messages.size()-1 - messageIterator)).getMessage()).setCurserToEnd(); + } + else if(started) + { + chat.setText("").setCurserToEnd(); + started = false; + } + return true; + } + return false; + } + + public void addMessage(String text) + { + addMessage(text, 0); + } + + public void addMessage(String text, int messageId) + { + addMessage(text, messageId, true); + } + + private void addMessage(String text, int messageId, boolean add) + { + if(listener != null && text.startsWith("/")) + { + listener.accept(text.substring(1)); + return; + } + ScrollBarComponent scroll = list.getVerticalBar(); + boolean atEnd = scroll.isAtEnd(); + float width = (list.getBox().getWidth() -5F) / 0.45F; + for(String s : getGui().getFont().splitLines(text, width, IFontRenderer.SPECIAL)) + { + list.addEntry(new MessageEntry(s, messageId)); + } + if(atEnd) scroll.setScrollEnd(); + if(add) + { + messages.add(new ConsoleMessage(messageId, text)); + messageIterator = 0; + } + } + + public void clearMessages() + { + messages.clear(); + list.clear(); + messageIterator = 0; + } + + public void removeMessage(int messageId) + { + messages.removeIf(T -> T.getMessageId() == messageId); + list.removeIf(T -> T.getMessageId() == messageId); + } + + private static class ConsoleMessage + { + int messageId; + String message; + + public ConsoleMessage(int messageId, String message) + { + this.messageId = messageId; + this.message = message; + } + + public String getMessage() + { + return message; + } + + public int getMessageId() + { + return messageId; + } + } + + private static class MessageEntry extends TextEntry + { + int messageId; + + public MessageEntry(String s, int messageId) + { + super(s); + this.messageId = messageId; + setScale(0.45F); + } + + public int getMessageId() + { + return messageId; + } + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/tree/BaseTreeEntry.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/tree/BaseTreeEntry.java new file mode 100644 index 0000000..df155f7 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/tree/BaseTreeEntry.java @@ -0,0 +1,142 @@ +package speiger.src.coreengine.rendering.gui.components.tree; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.rendering.gui.GuiBase; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.components.list.BaseListEntry; + +public abstract class BaseTreeEntry extends BaseListEntry implements ITreeEntry +{ + protected List children = null; + protected ITreeEntry parent = null; + protected int debth; + protected boolean init = false; + + @Override + public Iterator iterator() + { + return isLeaf() ? Collections.emptyIterator() : children.iterator(); + } + + @Override + public boolean isInit() + { + return init; + } + + @Override + public void init(GuiComponent comp, GuiBase owner) + { + super.init(comp, owner); + init = true; + } + + @Override + public boolean isLeaf() + { + return children == null; + } + + @Override + public void addChild(ITreeEntry child) + { + if(isLeaf()) + { + children = new ObjectArrayList(); + } + children.add(child); + child.setParent(this); + calculateDebth(); + onChange(); + } + + @Override + public void removeChild(ITreeEntry child) + { + if(isLeaf()) + { + return; + } + if(children.remove(child)) + { + child.setParent(null); + if(children.isEmpty()) + { + children = null; + } + onChange(); + } + } + + protected void onChange() + { + + } + + @Override + public void onClosed() + { + super.onClosed(); + if(!isLeaf()) + { + for(int i = 0;i +{ + public boolean isInit(); + + public boolean isLeaf(); + + public void addChild(ITreeEntry child); + + public void removeChild(ITreeEntry child); + + public int getChildCount(); + + public ITreeEntry getChild(int index); + + public int indexOf(ITreeEntry child); + + public ITreeEntry getParent(); + + public void setParent(ITreeEntry parent); + + public int getDebth(); + + public void calculateDebth(); +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/tree/ProfilerTreeEntry.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/tree/ProfilerTreeEntry.java new file mode 100644 index 0000000..ebb9fe9 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/tree/ProfilerTreeEntry.java @@ -0,0 +1,129 @@ +package speiger.src.coreengine.rendering.gui.components.tree; + +import java.util.Map; +import java.util.UUID; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.GuiBase; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.components.TextComponent; +import speiger.src.coreengine.rendering.gui.components.window.debug.PieProfilerWindowComponent; +import speiger.src.coreengine.rendering.gui.helper.Align; +import speiger.src.coreengine.rendering.input.Keyboard; +import speiger.src.coreengine.utils.profiler.IProfiler.IProfilerEntry; + +public class ProfilerTreeEntry extends BaseTreeEntry +{ + TextComponent text = new TextComponent().setLimitedBounds(false).setAlignment(Align.LEFT_TOP, Align.LEFT_TOP).setSpecialRendering(true).setTextScale(0.4F).setComponentPosition(0F, 1F).cast(); + IProfilerEntry entry; + + public ProfilerTreeEntry(IProfilerEntry entry) + { + this.entry = entry; + } + + @Override + public float getWidth() + { + return text.getMetadata().getMaxWidth(); + } + + @Override + public void init(GuiComponent comp, GuiBase owner) + { + super.init(comp, owner); + text.setOwner(owner); + } + + @Override + public void updateState(GuiComponent comp, float scale) + { + text.setScale(scale); + } + + @Override + public void onClosed() + { + text.onClosed(); + } + + @Override + public void onFixedUpdate() + { + if(text.length() > 0 && (Keyboard.isAltDown() || text.getGui().getGlobalClock() % 2 != 0)) + { + return; + } + StringBuilder builder = new StringBuilder(); + builder.append(entry.getName()).append(": "); + builder.append(getColor(entry.getMinTime())).append(getTime(entry.getMinTime())).append("§").append(" / "); + builder.append(getColor(entry.getNanoTime())).append(getTime(entry.getNanoTime())).append("§").append(" / "); + builder.append(getColor(entry.getMaxTime())).append(getTime(entry.getMaxTime())).append("§"); + text.setText(builder.toString()); + } + + @Override + public void onRender(GuiComponent comp, boolean enabled, int mouseX, int mouseY, float particalTicks) + { + text.setEnabled(enabled).onRender(mouseX, mouseY, particalTicks); + } + + @Override + public void collectTooltips(GuiComponent comp, int mouseX, int mouseY, float particalTicks, Map collector) + { + + } + + @Override + public int hashCode() + { + return entry.getPathName().hashCode(); + } + + @Override + public boolean equals(Object obj) + { + if(obj instanceof ProfilerTreeEntry) + { + return ((ProfilerTreeEntry)obj).entry == entry; + } + return false; + } + + public String getTime(long time) + { + if(time >= 2000000L) + { + return PieProfilerWindowComponent.PERCENT_FORMAT.format(time / 1000000.0D) + "ms"; + } + if(time >= 2000L) + { + return PieProfilerWindowComponent.PERCENT_FORMAT.format(time / 1000.0D) + "µs"; + } + return PieProfilerWindowComponent.PERCENT_FORMAT.format(time) + "ns"; + } + + public String getColor(long time) + { + time /= 1000000L; + if(time < 1) + { + return "§"; + } + if(time >= 12) + { + return "§"; + } + if(time >= 5) + { + return "§"; + } + return "§"; + } + + @Override + public String toString() + { + return entry.getPathName(); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/window/color/ColorPickerWindowComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/window/color/ColorPickerWindowComponent.java new file mode 100644 index 0000000..bec8c76 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/window/color/ColorPickerWindowComponent.java @@ -0,0 +1,154 @@ +package speiger.src.coreengine.rendering.gui.components.window.color; + +import java.awt.Color; +import java.util.function.Consumer; + +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.math.misc.Facing; +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.rendering.gui.UITextures; +import speiger.src.coreengine.rendering.gui.components.ButtonComponent; +import speiger.src.coreengine.rendering.gui.components.GradientSliderComponent; +import speiger.src.coreengine.rendering.gui.components.SliderComponent; +import speiger.src.coreengine.rendering.gui.components.TextFieldComponent; +import speiger.src.coreengine.rendering.gui.components.WindowComponent; +import speiger.src.coreengine.rendering.gui.components.icon.IIcon; +import speiger.src.coreengine.rendering.gui.components.icon.TexturedIcon; +import speiger.src.coreengine.rendering.gui.helper.box.GuiBox; +import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.ParentConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.RelativeConstrain; +import speiger.src.coreengine.utils.functions.Functions; +import speiger.src.coreengine.utils.helpers.InternalThreadPools; + +public class ColorPickerWindowComponent extends WindowComponent +{ + Consumer listener; + IIcon wheelIcon = new TexturedIcon(UITextures.COLOR_WHEEL); + IGuiBox wheelBox = new GuiBox(15, 10, 70, 70); + IGuiBox selectedBox = new GuiBox(5F, 94F, 50F, 10F); + ColorObject[] colors = new ColorObject[]{ColorObject.rgb(0), ColorObject.rgb(0), ColorObject.rgb(0), ColorObject.rgb(0), ColorObject.rgb(0)}; + SliderComponent brightness = new GradientSliderComponent(95, 13, 17, 76, 0, 100, 0, ColorObject.GRAY, colors[2], colors[3], Facing.NORTH, null).setScrollEffect(-1).setVertical(true); + SliderComponent saturation = new GradientSliderComponent(110, 13, 17, 76, 0, 100, 0, ColorObject.GRAY, colors[0], colors[1], Facing.NORTH, null).setScrollEffect(-1).setVertical(true); + TextFieldComponent code = new TextFieldComponent(60, 94, 104, 20, ColorObject.DARK_GRAY).setValidator(Functions.NUMBERS_ONLY).setMaxTextLength(8); + float[] hsv = new float[3]; + IGuiBox[] colorBoxes; + ColorPool pool; + + public ColorPickerWindowComponent(float x, float y, ColorPool pool, ColorObject defaultColor, Consumer listener, String name) + { + super(x, y, 125, 140, FIXED_SIZE_POPUP, name); + this.pool = pool; + this.listener = listener; + hsv = defaultColor.toHue(); + colorBoxes = new IGuiBox[Math.min(18, pool.size())]; + for(int i = 0,m=colorBoxes.length;i= 9 ? 12 : 0), 10, 10)); + } + } + + @Override + public void init() + { + super.init(); + addBox(wheelBox); + addBox(selectedBox); + addChild(brightness.addChangeListener(minimizedListener).addUserActionListener(T -> setColor(hsv[0], hsv[1], T.cast(SliderComponent.class).getValue() * 0.01F))); + addChild(saturation.addChangeListener(minimizedListener).addUserActionListener(T -> setColor(hsv[0], T.cast(SliderComponent.class).getValue() * 0.01F, hsv[2]))); + addChild(code.setScale(0.5F).addChangeListener(minimizedListener).addUserActionListener(T -> onTyped())); + addChild(new ButtonComponent(0F, 0F, 0F, 20F, "Select", ColorObject.GREEN).setScale(0.4F).addUserActionListener(T -> apply()), new ComponentConstrains(null, new ParentConstrain(8F).invert(), new RelativeConstrain(0.5F / 0.4F), null)); + addChild(new ButtonComponent(0F, 0F, 0F, 20F, "Cancel", ColorObject.RED).setScale(0.4F).addUserActionListener(T -> T.getGui().removeComponent(this)), new ComponentConstrains(new RelativeConstrain(0.5F), new ParentConstrain(8F).invert(), new RelativeConstrain(0.5F / 0.4F), null)); + setColor(hsv[0], hsv[1], hsv[2]); + } + + @Override + public boolean isPopup() + { + return true; + } + + @Override + protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) + { + super.renderSelf(mouseX, mouseY, particalTicks); + wheelIcon.render(getRenderer().translate(0F, 0F, 0.01F).setBrightness(hsv[1]), wheelBox); + getRenderer().setBrightness(1F); + ColorObject color = new ColorObject(0); + for(int i = 0,m=colorBoxes.length;i> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF, new float[3]); + setColor(hsv[0], hsv[1], hsv[2]); + } + catch(Exception e){} + } + + public void apply() + { + pool.addColor(colors[4].getRGB()); + listener.accept(colors[4]); + getGui().removeComponent(this); + } + + @Override + public boolean onClick(int button, int mouseX, int mouseY) + { + return wheelBox.isColiding(mouseX, mouseY) ? onChanged(mouseX, mouseY) : super.onClick(button, mouseX, mouseY); + } + + @Override + public boolean onDrag(int mouseX, int mouseY) + { + return super.onDrag(mouseX, mouseY) || (wheelBox.isColiding(mouseX, mouseY) ? onChanged(mouseX, mouseY) : false); + } + + private boolean onChanged(int mouseX, int mouseY) + { + Vec2f pos = InternalThreadPools.VEC2F.get().set(mouseX, mouseY); + float radius = (float)pos.distanceTo(wheelBox.getCenterX(), wheelBox.getCenterY()); + if(radius > 38) + { + InternalThreadPools.VEC2F.accept(pos); + return false; + } + float hue = (pos.directionAngle(wheelBox.getCenterX(), wheelBox.getCenterY()) + 180F) % 360F; + setColor(hue / 360F, hsv[1], radius / 33F); + InternalThreadPools.VEC2F.accept(pos); + return true; + } + + protected void setColor(float hue, float saturation, float brightness) + { + hsv[0] = MathUtils.clamp(0F, 1F, hue); + hsv[1] = MathUtils.clamp(0F, 1F, saturation); + hsv[2] = MathUtils.clamp(0F, 1F, brightness); + colors[0].setRGB(hsv[0], 0F, 1F, 255); + colors[1].setRGB(hsv[0], 1F, 1F, 255); + colors[2].setRGB(hsv[0], 1F, 0F, 255); + colors[3].setRGB(hsv[0], 1F, 1F, 255); + colors[4].setRGB(hsv[0], hsv[1], hsv[2], 255); + code.setText(colors[4].getHTMLCode(false)); + this.brightness.setValue((int)(hsv[2] * 100F)); + this.saturation.setValue((int)(hsv[1] * 100F)); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/window/color/ColorPool.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/window/color/ColorPool.java new file mode 100644 index 0000000..d71ac38 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/window/color/ColorPool.java @@ -0,0 +1,102 @@ +package speiger.src.coreengine.rendering.gui.components.window.color; + +import java.util.BitSet; + +import speiger.src.coreengine.math.ArrayUtil; +import speiger.src.coreengine.math.misc.ColorObject; + +public class ColorPool +{ + int[] colors; + BitSet locked; + + public ColorPool(int size) + { + colors = ArrayUtil.fill(new int[size], ColorObject.WHITE.getRGB()); + locked = new BitSet(size); + } + + public void addColor(int color) + { + int index = colors.length; + boolean lock = false; + for(int i = 0,m=colors.length;i0;i--) + { + if(isLocked(i) && (index == colors.length || i != (index - 1))) + { + continue; + } + colors[i] = colors[i-1]; + locked.set(i, locked.get(i-1)); + locked.clear(i-1); + } + colors[0] = color; + locked.set(0, lock); + } + + public void removeColor(int index) + { + colors[index] = ColorObject.WHITE.getRGB(); + } + + public int getColor(int index) + { + return colors[index]; + } + + public void toggleLock(int index) + { + locked.set(index, !locked.get(index)); + } + + public void lockIndex(int index) + { + locked.set(index); + } + + public void unlockIndex(int index) + { + locked.clear(index); + } + + public boolean isLocked(int index) + { + return locked.get(index); + } + + public int[] getColors() + { + return colors.clone(); + } + + public long[] getLocked() + { + return locked.toLongArray(); + } + + public void setData(int[] array, long[] locked) + { + System.arraycopy(array, 0, colors, 0, Math.min(array.length, colors.length)); + this.locked = BitSet.valueOf(locked); + } + + public void clear() + { + ArrayUtil.fill(colors, ColorObject.WHITE.getRGB()); + locked.clear(); + } + + public int size() + { + return colors.length; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/window/debug/PieProfilerWindowComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/window/debug/PieProfilerWindowComponent.java new file mode 100644 index 0000000..bd6f0e0 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/window/debug/PieProfilerWindowComponent.java @@ -0,0 +1,306 @@ +package speiger.src.coreengine.rendering.gui.components.window.debug; + +import java.text.DecimalFormat; +import java.util.List; +import java.util.function.Supplier; + +import org.lwjgl.glfw.GLFW; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.utils.ObjectLists; +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.rendering.gui.GuiManager; +import speiger.src.coreengine.rendering.gui.base.IKeyComponent; +import speiger.src.coreengine.rendering.gui.components.ButtonComponent; +import speiger.src.coreengine.rendering.gui.components.PieComponent; +import speiger.src.coreengine.rendering.gui.components.PieComponent.IPieIndex; +import speiger.src.coreengine.rendering.gui.components.PieComponent.PieIndex; +import speiger.src.coreengine.rendering.gui.components.TextComponent; +import speiger.src.coreengine.rendering.gui.components.WindowComponent; +import speiger.src.coreengine.rendering.gui.helper.Align; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.Constrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.DynamicConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.ParentConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.PixelConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.RelativeConstrain; +import speiger.src.coreengine.rendering.input.Keyboard; +import speiger.src.coreengine.utils.profiler.IProfiler; +import speiger.src.coreengine.utils.profiler.IProfiler.IProfilerEntry; +import speiger.src.coreengine.utils.profiler.IProfiler.ProfilerData; + +public class PieProfilerWindowComponent extends WindowComponent + implements Supplier>, IKeyComponent +{ + public static final DecimalFormat PERCENT_FORMAT = new DecimalFormat("##0.00"); + + PieComponent pie = new PieComponent(127, this); + ButtonComponent[] buttons = new ButtonComponent[3]; + TextComponent[] extraFeatures = new TextComponent[3]; + List entries = new ObjectArrayList(); + int textInUse = 0; + IProfiler profiler; + IProfilerEntry currentEntry; + String entryName; + List lastValues = null; + + public PieProfilerWindowComponent(float x, float y, float width, float height, String name) + { + super(x, y, width, height, DEFAULT_FLAGS, name); + } + + @Override + public boolean canMoveIntoForground() + { + return true; + } + + @Override + public void init() + { + super.init(); + addChild(pie.setAutoUpdate(true).setComponentPosition(0F, 5F).addChangeListener(minimizedListener), new ComponentConstrains(null, null, new ParentConstrain(), new DynamicConstrain(this::calculatePieHeight))); + buttons[0] = createButton(0, "Client"); + buttons[1] = createButton(1, "GPU"); + buttons[2] = createButton(2, "Server"); + extraFeatures[0] = new TextComponent(0F, 0F, 18F, 5.5F).setTextScale(0.3F).setText("[0] Back").setAlignment(Align.LEFT_TOP, Align.CENTER); + extraFeatures[0].addChangeListener(T -> T.setVisible(!isMinimized() && currentEntry != null && currentEntry.getParent() != null)); + addChild(extraFeatures[0], new ComponentConstrains(null, new DynamicConstrain(() -> pie.getBox().getBaseHeight() + (-5.5F)), new PixelConstrain(38).setInverted(), null)); + + extraFeatures[1] = new TextComponent(0F, 0F, 0F, 7F).setTextScale(0.4F).setText("Client Thread"); + addChild(extraFeatures[1].addChangeListener(minimizedListener), new ComponentConstrains(null, new PixelConstrain(8F), new ParentConstrain(), null)); + extraFeatures[2] = new TextComponent(0F, 0F, 0F, 6F).setTextScale(0.33F).setText("Client"); + addChild(extraFeatures[2].addChangeListener(minimizedListener), new ComponentConstrains(null, new PixelConstrain(15F), new ParentConstrain(), null)); + + } + + @Override + public void onClosed() + { + super.onClosed(); + if(profiler != null) + { + profiler.disable(); + profiler = null; + } + } + + public PieProfilerWindowComponent setProfiler(IProfiler profiler, String root) + { + if(this.profiler != null) + { + buttons[getProfilerIndex(this.profiler)].setEnabled(true); + this.profiler.disable(); + } + this.profiler = profiler; + if(profiler != null) + { + profiler.enable(); + setCurrentEntry(root); + buttons[getProfilerIndex(this.profiler)].setEnabled(false); + extraFeatures[1].setText(profiler.getName()); + } + return this; + } + + public PieProfilerWindowComponent setCurrentEntry(String name) + { + entryName = name; + currentEntry = profiler.getEntry(name); + lastValues = null; + extraFeatures[2].setText(currentEntry == null ? "Unknown" : currentEntry.getName()); + return this; + } + + @Override + protected boolean updateSelf(int mouseX, int mouseY, float particalTicks) + { + for(int i = 0,m=entries.size();i= GLFW.GLFW_KEY_1 && key <= GLFW.GLFW_KEY_9) + { + key -= GLFW.GLFW_KEY_1; + if(key < textInUse) + { + String s = entries.get(key)[0].getText(); + s = s.substring(s.indexOf("] ")+2); + if(!s.equalsIgnoreCase("Nameless")) + { + setCurrentEntry(currentEntry.getPathName() + "/" + s); + return true; + } + } + return false; + } + return false; + } + + @Override + public Vec2f getMinimumBounds() + { + return Vec2f.newVec(80F, 80F + (textInUse * 5.5F)); + } + + @Override + public List get() + { + if(lastValues != null) + { + List entries = new ObjectArrayList(); + for(int i = 0, m = lastValues.size();i < m;i++) + { + entries.add(new PieIndex(MathUtils.floor(lastValues.get(i).getEffect() * 1.28D), lastValues.get(i).getColor())); + } + return entries; + } + return ObjectLists.singleton(new PieIndex(pie.getMaxSteps(), ColorObject.LIGHT_BLUE)); + } + + protected float calculatePieHeight() + { + return getBox().getBaseHeight() - ((textInUse * 5.5F) + 9F); + } + + public IProfiler getProfiler(int index) + { + GuiManager manager = getGui().getUIManager(); + switch(index) + { + case 1: + return manager.getGPUProfiler(); + case 2: + return manager.getServerProfiler(); + default: + return manager.getCPUProfiler(); + } + } + + public int getProfilerIndex(IProfiler prof) + { + GuiManager manager = getGui().getUIManager(); + return manager.getGPUProfiler() == prof ? 1 : manager.getServerProfiler() == prof ? 2 : 0; + } + + public static String getRoot(int index) + { + return index < 2 ? "Client" : "Server"; + } + + protected TextComponent createComponent(int column) + { + final int index = entries.size(); + String text = column == 0 ? "[" + (entries.size() + 1) + "] Unknown" : PERCENT_FORMAT.format(0D) + "%"; + TextComponent comp = new TextComponent(0F, 0F, 18F, 5.5F).setTextScale(0.3F).setText(text).setAlignment(column == 0 ? Align.LEFT_TOP : Align.RIGHT_BOTTOM, Align.CENTER); + comp.addChangeListener(T -> T.setVisible(!isMinimized() && index < textInUse)); + Constrain xPos = column == 0 ? null : (column == 1 ? new PixelConstrain(38F).setInverted() : new PixelConstrain(19F).setInverted()); + addChild(comp, new ComponentConstrains(xPos, new DynamicConstrain(() -> pie.getBox().getBaseHeight() + (index * 5.5F)), column == 0 ? new PixelConstrain(38).setInverted() : null, null)); + return comp; + } + + protected ButtonComponent createButton(int index, String name) + { + ButtonComponent button = new ButtonComponent(name, ColorObject.GRAY); + button.getText().setTextScale(0.3F); + button.addChangeListener(minimizedListener).addUserActionListener(T -> setProfiler(getProfiler(index), getRoot(index))); + addChild(button, new ComponentConstrains(new RelativeConstrain(index * 0.3333F), new PixelConstrain(8F).setInverted(), new RelativeConstrain(0.3333F), new PixelConstrain(7F))); + return button; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/window/debug/TreeProfilerWindowComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/window/debug/TreeProfilerWindowComponent.java new file mode 100644 index 0000000..4c4c450 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/window/debug/TreeProfilerWindowComponent.java @@ -0,0 +1,184 @@ +package speiger.src.coreengine.rendering.gui.components.window.debug; + +import java.util.function.ObjIntConsumer; + +import speiger.src.collections.ints.queues.IntArrayFIFOQueue; +import speiger.src.collections.ints.queues.IntPriorityQueue; +import speiger.src.collections.ints.utils.IntPriorityQueues; +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.rendering.gui.GuiManager; +import speiger.src.coreengine.rendering.gui.components.ButtonComponent; +import speiger.src.coreengine.rendering.gui.components.TreeComponent; +import speiger.src.coreengine.rendering.gui.components.WindowComponent; +import speiger.src.coreengine.rendering.gui.components.tree.ProfilerTreeEntry; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.ParentConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.PixelConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.RelativeConstrain; +import speiger.src.coreengine.utils.profiler.IProfiler; +import speiger.src.coreengine.utils.profiler.IProfiler.IProfilerEntry; + +public class TreeProfilerWindowComponent extends WindowComponent +{ + IntPriorityQueue todoList = IntPriorityQueues.synchronize(new IntArrayFIFOQueue()); + TreeComponent tree = new TreeComponent(ColorObject.GRAY, 9F).disableBackground(true).setSelectionMode(TreeComponent.SELECTION_MODE_INTERACT); + ButtonComponent[] buttons = new ButtonComponent[3]; + ObjIntConsumer listener = (T, V) -> todoList.enqueue(V); + IProfiler profiler; + String root; + + public TreeProfilerWindowComponent(float x, float y, float width, float height, String name) + { + super(x, y, width, height, DEFAULT_FLAGS, name); + } + + @Override + public boolean canMoveIntoForground() + { + return true; + } + + @Override + public void init() + { + super.init(); + addChild(tree.addChangeListener(minimizedListener), new ComponentConstrains(new PixelConstrain(2F), new PixelConstrain(getMinimizedY()), new ParentConstrain(1.5F), new PixelConstrain(getMinimizedY() + 8.5F).setInverted())); + buttons[0] = createButton(0, "Client"); + buttons[1] = createButton(1, "GPU"); + buttons[2] = createButton(2, "Server"); + } + + @Override + protected boolean fixedUpdateSelf() + { + while(!todoList.isEmpty()) + { + int index = todoList.dequeue(); + switch(index) + { + case 0: + addEntries(profiler.getEntry(root)); + break; + case 1: + tree.setTree(null); + break; + case 2: + if(tree.getTree() == null) + { + addEntries(profiler.getEntry(root)); + break; + } + updateChildren(profiler.getEntry(root), tree.getTree()); + tree.onTreeChanged(); + break; + } + } + return true; + } + + public TreeProfilerWindowComponent setProfiler(IProfiler profiler, String root) + { + if(this.profiler != null) + { + buttons[getProfilerIndex(this.profiler)].setEnabled(true); + this.profiler.removeListener(listener); + this.profiler.disable(); + tree.setTree(null); + } + this.profiler = profiler; + this.root = root; + if(this.profiler != null) + { + buttons[getProfilerIndex(this.profiler)].setEnabled(false); + this.profiler.enable(); + this.profiler.addListener(listener); + addEntries(this.profiler.getEntry(this.root)); + } + return this; + } + + protected void addEntries(IProfilerEntry entry) + { + if(entry == null) + { + return; + } + ProfilerTreeEntry child = new ProfilerTreeEntry(entry); + for(int i = 0,m=entry.getChildCount();i setProfiler(getProfiler(index), PieProfilerWindowComponent.getRoot(index))); + addChild(button, new ComponentConstrains(new RelativeConstrain(index * 0.3333F), new PixelConstrain(8F).setInverted(), new RelativeConstrain(0.3333F), new PixelConstrain(7F))); + return button; + } + + @Override + public Vec2f getMinimumBounds() + { + return Vec2f.newVec(100F, 50F); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/window/misc/ChoiceComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/window/misc/ChoiceComponent.java new file mode 100644 index 0000000..71781b3 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/window/misc/ChoiceComponent.java @@ -0,0 +1,60 @@ +package speiger.src.coreengine.rendering.gui.components.window.misc; + +import speiger.src.collections.booleans.functions.BooleanConsumer; +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.components.ButtonComponent; +import speiger.src.coreengine.rendering.gui.components.TextComponent; +import speiger.src.coreengine.rendering.gui.components.WindowComponent; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.ParentConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.PixelConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.RelativeConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.TextConstrain; + +public class ChoiceComponent extends WindowComponent +{ + TextComponent message = new TextComponent().setLimitedHeight(false).setTextScale(0.5F); + ButtonComponent yesButton = new ButtonComponent("Yes", ColorObject.GRAY); + ButtonComponent noButton = new ButtonComponent("No", ColorObject.GRAY); + BooleanConsumer listener; + + public ChoiceComponent(float width, String windowTitle, String message, BooleanConsumer listener) + { + this(0F, 0F, width, windowTitle, message, listener); + } + + public ChoiceComponent(float x, float y, float width, String windowTitle, String message, BooleanConsumer listener) + { + super(x, y, width, 25F, FIXED_SIZE_POPUP, windowTitle); + this.message.setText(message); + this.listener = listener; + } + + public static ChoiceComponent createChoice(float width, String windowTitle, String message, BooleanConsumer listener) + { + return new ChoiceComponent(width, windowTitle, message, listener); + } + + public static ChoiceComponent createChoice(String windowTitle, String message, BooleanConsumer listener) + { + return new ChoiceComponent(150, windowTitle, message, listener); + } + + @Override + public void init() + { + super.init(); + yesButton.getText().setTextScale(0.5F); + noButton.getText().setTextScale(0.5F); + addChild(yesButton.addChangeListener(minimizedListener).addUserActionListener(closeListener).addUserActionListener(T -> listener.accept(true)), new ComponentConstrains(new RelativeConstrain(0F), new ParentConstrain(10F).invert(), new RelativeConstrain(0.5F), new PixelConstrain(10F))); + addChild(noButton.addChangeListener(minimizedListener).addUserActionListener(closeListener).addUserActionListener(T -> listener.accept(false)), new ComponentConstrains(new RelativeConstrain(0.5F), new ParentConstrain(10F).invert(), new RelativeConstrain(0.5F), new PixelConstrain(10F))); + addChild(message, new ComponentConstrains(new PixelConstrain(10F), new PixelConstrain(11F), new ParentConstrain(10F), TextConstrain.height(message))); + getBox().setHeight(25F + message.getMetadata().getMaxHeight()); + } + + @Override + public boolean isPopup() + { + return true; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/window/misc/MessageComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/window/misc/MessageComponent.java new file mode 100644 index 0000000..ab7794d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/window/misc/MessageComponent.java @@ -0,0 +1,74 @@ +package speiger.src.coreengine.rendering.gui.components.window.misc; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.components.ButtonComponent; +import speiger.src.coreengine.rendering.gui.components.TextComponent; +import speiger.src.coreengine.rendering.gui.components.WindowComponent; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.ParentConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.PixelConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.TextConstrain; + +public class MessageComponent extends WindowComponent +{ + TextComponent message = new TextComponent().setLimitedHeight(false).setTextScale(0.5F); + ButtonComponent resultButton = new ButtonComponent("", ColorObject.GRAY); + + public MessageComponent(float width, String windowTitle, String confirmButton, String message) + { + this(0F, 0F, width, windowTitle, confirmButton, message); + } + + public MessageComponent(float x, float y, float width, String windowTitle, String confirmButton, String message) + { + super(x, y, width, 25F, FIXED_SIZE_POPUP, windowTitle); + resultButton.getText().setText(confirmButton); + this.message.setText(message); + } + + public static MessageComponent createInfo(String message) + { + return new MessageComponent(150, "Info", "Ok", message); + } + + public static MessageComponent createInfo(float width, String message) + { + return new MessageComponent(width, "Info", "Ok", message); + } + + public static MessageComponent createWarn(String message) + { + return new MessageComponent(150, "Warn", "Ok", message); + } + + public static MessageComponent createWarn(float width, String message) + { + return new MessageComponent(width, "Warn", "Ok", message); + } + + public static MessageComponent createError(String message) + { + return new MessageComponent(150, "Error!", "Ok", message); + } + + public static MessageComponent createError(float width, String message) + { + return new MessageComponent(width, "Error!", "Ok", message); + } + + @Override + public void init() + { + super.init(); + resultButton.getText().setTextScale(0.5F); + addChild(resultButton.addChangeListener(minimizedListener).addUserActionListener(closeListener), new ComponentConstrains(new ParentConstrain(), new ParentConstrain(10F).invert(), new ParentConstrain(), new PixelConstrain(10F))); + addChild(message, new ComponentConstrains(new PixelConstrain(10F), new PixelConstrain(11F), new ParentConstrain(10F), TextConstrain.height(message))); + getBox().setHeight(25F + message.getMetadata().getMaxHeight()); + } + + @Override + public boolean isPopup() + { + return true; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/components/window/misc/TextInputComponent.java b/src/main/java/speiger/src/coreengine/rendering/gui/components/window/misc/TextInputComponent.java new file mode 100644 index 0000000..9f53d54 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/components/window/misc/TextInputComponent.java @@ -0,0 +1,61 @@ +package speiger.src.coreengine.rendering.gui.components.window.misc; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.rendering.gui.components.ButtonComponent; +import speiger.src.coreengine.rendering.gui.components.TextComponent; +import speiger.src.coreengine.rendering.gui.components.TextFieldComponent; +import speiger.src.coreengine.rendering.gui.components.WindowComponent; +import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains; +import speiger.src.coreengine.rendering.gui.helper.constrains.ParentConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.PixelConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.RelativeConstrain; +import speiger.src.coreengine.rendering.gui.helper.constrains.TextConstrain; + +public class TextInputComponent extends WindowComponent +{ + TextComponent message = new TextComponent().setLimitedHeight(false).setTextScale(0.5F); + TextFieldComponent input = new TextFieldComponent(ColorObject.GRAY); + ButtonComponent confirm = new ButtonComponent("Confirm", ColorObject.DARK_GREEN); + ButtonComponent cancel = new ButtonComponent("Cancel", ColorObject.RED); + + public TextInputComponent(float width, String name, String message) + { + this(0F, 0F, width, name, message); + } + + public TextInputComponent(float x, float y, float width, String name, String message) + { + super(x, y, width, 35F, FIXED_SIZE_POPUP, name); + this.message.setText(message); + } + + public TextFieldComponent getInput() + { + return input; + } + + public String getText() + { + return input.getText(); + } + + @Override + public void init() + { + super.init(); + confirm.getText().setTextScale(0.5F); + cancel.getText().setTextScale(0.5F); + input.getRawText().setTextScale(0.5F); + addChild(confirm.addChangeListener(minimizedListener).addUserActionListener((T) -> notifyListeners(LISTENER_USER_ACTION)).addUserActionListener(closeListener), new ComponentConstrains(new RelativeConstrain(0F), new ParentConstrain(10F).invert(), new RelativeConstrain(0.5F), new PixelConstrain(10F))); + addChild(cancel.addChangeListener(minimizedListener).addUserActionListener(closeListener), new ComponentConstrains(new RelativeConstrain(0.5F), new ParentConstrain(10F).invert(), new RelativeConstrain(0.5F), new PixelConstrain(10F))); + addChild(message, new ComponentConstrains(new PixelConstrain(10F), new PixelConstrain(11F), new ParentConstrain(10F), TextConstrain.height(message))); + addChild(input, new ComponentConstrains(new PixelConstrain(10F), TextConstrain.height(message).setPadding(15F), new ParentConstrain(10F), new PixelConstrain(12F))); + getBox().setHeight(45F + message.getMetadata().getMaxHeight()); + } + + @Override + public boolean isPopup() + { + return true; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/helper/Align.java b/src/main/java/speiger/src/coreengine/rendering/gui/helper/Align.java new file mode 100644 index 0000000..aa6865b --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/helper/Align.java @@ -0,0 +1,19 @@ +package speiger.src.coreengine.rendering.gui.helper; + +public enum Align +{ + LEFT_TOP, + CENTER, + RIGHT_BOTTOM; + + public float align(float containerSize, float objectSize) + { + switch(this) + { + case LEFT_TOP: return 0F; + case CENTER: return (containerSize / 2F) - (objectSize / 2F); + case RIGHT_BOTTOM: return containerSize - objectSize; + } + return 0F; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/helper/TextFilter.java b/src/main/java/speiger/src/coreengine/rendering/gui/helper/TextFilter.java new file mode 100644 index 0000000..3d59c8d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/helper/TextFilter.java @@ -0,0 +1,24 @@ +package speiger.src.coreengine.rendering.gui.helper; + +import java.util.function.Predicate; + +public class TextFilter +{ + public static final Predicate INTEGER_ONLY = T -> { + try { + if(T == null || T.isEmpty()); + else Integer.parseInt(T); + return true; + } + catch(Exception e) { return false; } + }; + + public static final Predicate FLOAT_ONLY = T -> { + try { + if(T == null || T.isEmpty()); + else Float.parseFloat(T); + return true; + } + catch(Exception e) { return false; } + }; +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/helper/UIShapes.java b/src/main/java/speiger/src/coreengine/rendering/gui/helper/UIShapes.java new file mode 100644 index 0000000..3ac05a8 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/helper/UIShapes.java @@ -0,0 +1,175 @@ +package speiger.src.coreengine.rendering.gui.helper; + +import org.lwjgl.opengl.GL11; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.math.misc.Facing; +import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; +import speiger.src.coreengine.rendering.gui.renderer.buffer.RenderBuffer; +import speiger.src.coreengine.rendering.tesselation.Tesselator; +import speiger.src.coreengine.rendering.tesselation.VertexType; + +public class UIShapes +{ + public static void createCross(RenderBuffer buffer, float width, float height, float paddingA, float paddingB, ColorObject color) + { + createCross(buffer.start(GL11.GL_TRIANGLES, VertexType.UI), width, height, paddingA, paddingB, color); + buffer.finishShape(0); + } + + public static void createCross(Tesselator tes, float width, float height, float paddingA, float paddingB, ColorObject color) + { + tes.setOffset(-(width * 0.5F), -(height * 0.5F), 0F); + tes.pos(paddingB, paddingA, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(paddingA, paddingB, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width - paddingB, height - paddingA, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width - paddingA, height - paddingB, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(paddingB, paddingA, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width - paddingB, height - paddingA, 0F).tex(0F, 0F).color4f(color).endVertex(); + + tes.pos(width - paddingB, paddingA, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width - paddingA, paddingB, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(paddingB, height - paddingA, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(paddingA, height - paddingB, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width - paddingB, paddingA, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(paddingB, height - paddingA, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.setOffset(0F, 0F, 0F); + } + + public static void createCross(UIRenderer renderer, float width, float height, float paddingA, float paddingB, ColorObject color) + { + renderer.translate(-(width * 0.5F), -(height * 0.5F)); + renderer.startCustomShape(GL11.GL_TRIANGLES, false); + renderer.pos(paddingB, paddingA, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(paddingA, paddingB, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width - paddingB, height - paddingA, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width - paddingA, height - paddingB, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(paddingB, paddingA, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width - paddingB, height - paddingA, 0F).tex(0F, 0F).color(color).endVertex(); + + renderer.pos(width - paddingB, paddingA, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width - paddingA, paddingB, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(paddingB, height - paddingA, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(paddingA, height - paddingB, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width - paddingB, paddingA, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(paddingB, height - paddingA, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.translate((width * 0.5F), (height * 0.5F)); + } + + public static void createArrow(RenderBuffer buffer, float width, float height, ColorObject color, Facing dir) + { + createArrow(buffer.start(GL11.GL_TRIANGLES, VertexType.UI), width, height, color, dir); + buffer.finishShape(0); + } + + public static void createArrow(Tesselator tes, float width, float height, ColorObject color, Facing dir) + { + float halfWidth = width * 0.5F; + float halfHeight = height * 0.5F; + tes.setOffset(-halfWidth, -halfHeight, 0F); + switch(dir) + { + case NORTH: + tes.pos(0F, halfHeight, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width, halfHeight, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(halfWidth, 0F, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(0F, halfHeight, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(0F, height, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width, halfHeight, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width, halfHeight, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(0F, height, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width, height, 0F).tex(0F, 0F).color4f(color).endVertex(); + break; + case SOUTH: + tes.pos(0F, 0F, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(0F, halfHeight, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width, 0F, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width, 0F, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(0F, halfHeight, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width, halfHeight, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(0F, halfHeight, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width, halfHeight, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(halfWidth, height, 0F).tex(0F, 0F).color4f(color).endVertex(); + break; + case EAST: + tes.pos(0F, 0F, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(halfWidth, 0F, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(0F, height, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(0F, height, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(halfWidth, 0F, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(halfWidth, height, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(halfWidth, 0F, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(halfWidth, height, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width, halfHeight, 0F).tex(0F, 0F).color4f(color).endVertex(); + break; + case WEST: + tes.pos(halfWidth, 0F, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width, 0F, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(halfWidth, height, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(halfWidth, height, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width, 0F, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(width, height, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(halfWidth, 0F, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(halfWidth, height, 0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(0F, halfHeight, 0F).tex(0F, 0F).color4f(color).endVertex(); + break; + } + tes.setOffset(0F, 0F, 0F); + } + + public static void createArrow(UIRenderer renderer, float width, float height, ColorObject color, Facing dir) + { + float halfWidth = width * 0.5F; + float halfHeight = height * 0.5F; + renderer.translate(-halfWidth, -halfHeight, 0F); + renderer.startCustomShape(GL11.GL_TRIANGLES, false); + switch(dir) + { + case NORTH: + renderer.pos(0F, halfHeight, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width, halfHeight, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(halfWidth, 0F, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(0F, halfHeight, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(0F, height, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width, halfHeight, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width, halfHeight, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(0F, height, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width, height, 0F).tex(0F, 0F).color(color).endVertex(); + break; + case SOUTH: + renderer.pos(0F, 0F, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(0F, halfHeight, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width, 0F, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width, 0F, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(0F, halfHeight, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width, halfHeight, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(0F, halfHeight, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width, halfHeight, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(halfWidth, height, 0F).tex(0F, 0F).color(color).endVertex(); + break; + case EAST: + renderer.pos(0F, 0F, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(halfWidth, 0F, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(0F, height, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(0F, height, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(halfWidth, 0F, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(halfWidth, height, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(halfWidth, 0F, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(halfWidth, height, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width, halfHeight, 0F).tex(0F, 0F).color(color).endVertex(); + break; + case WEST: + renderer.pos(halfWidth, 0F, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width, 0F, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(halfWidth, height, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(halfWidth, height, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width, 0F, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(width, height, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(halfWidth, 0F, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(halfWidth, height, 0F).tex(0F, 0F).color(color).endVertex(); + renderer.pos(0F, halfHeight, 0F).tex(0F, 0F).color(color).endVertex(); + break; + } + renderer.translate(halfWidth, halfHeight, 0F); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/Animation.java b/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/Animation.java new file mode 100644 index 0000000..5198d4a --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/Animation.java @@ -0,0 +1,46 @@ +package speiger.src.coreengine.rendering.gui.helper.animations; + +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap; +import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap; +import speiger.src.collections.objects.utils.maps.Object2ObjectMaps; +import speiger.src.coreengine.math.value.IValue; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.helper.animations.transitions.ITransition; + +public class Animation +{ + Object2ObjectMap transitions = new Object2ObjectLinkedOpenHashMap(); + AnimationListener listener; + float duration = 0F; + + public Animation add(AnimationTarget target, ITransition transition) + { + transitions.put(target, transition); + duration += transition.getDuration(); + return this; + } + + public Animation addListener(AnimationListener listener) + { + this.listener = listener; + return this; + } + + public AnimationInstance createInstance(GuiComponent comp, AnimationInstance old, float delay, boolean reverse) + { + Object2ObjectMap values = new Object2ObjectLinkedOpenHashMap(); + for(Object2ObjectMap.Entry entry : Object2ObjectMaps.fastIterable(transitions)) + { + AnimationTarget target = entry.getKey(); + float base = target.getBase(comp); + values.put(target, entry.getValue().initValue(base, old != null ? old.getCurrentProgress(target) : (reverse ? entry.getValue().getHiddenValue() : base), reverse, delay, duration)); + } + return new AnimationInstance(values, duration + delay, reverse, listener); + } + + @FunctionalInterface + public static interface AnimationListener + { + public void onAnimationFinished(Animation instance, GuiComponent owner, boolean reverse); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/AnimationInstance.java b/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/AnimationInstance.java new file mode 100644 index 0000000..cc4cec2 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/AnimationInstance.java @@ -0,0 +1,53 @@ +package speiger.src.coreengine.rendering.gui.helper.animations; + +import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap; +import speiger.src.collections.objects.utils.maps.Object2ObjectMaps; +import speiger.src.coreengine.math.value.ConstantValue; +import speiger.src.coreengine.math.value.IValue; +import speiger.src.coreengine.rendering.gui.helper.animations.Animation.AnimationListener; + +public class AnimationInstance +{ + Object2ObjectMap values; + float totalProgress; + boolean reverse; + AnimationListener listener; + float progress = 0F; + + public AnimationInstance(Object2ObjectMap values, float totalProgress, boolean reverse, AnimationListener listener) + { + this.values = values; + this.totalProgress = totalProgress; + this.reverse = reverse; + this.listener = listener; + } + + public void update(Animator animator, float particalTime) + { + progress += particalTime; + for(Object2ObjectMap.Entry entry : Object2ObjectMaps.fastIterable(values)) + { + entry.getKey().apply(animator, entry.getValue().update(particalTime)); + } + } + + public float getCurrentProgress(AnimationTarget target) + { + return values.getOrDefault(target, ConstantValue.ZERO).get(); + } + + public boolean isDone() + { + return reverse && progress >= totalProgress; + } + + public AnimationListener getListener() + { + return listener; + } + + public boolean isReverse() + { + return reverse; + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/AnimationTarget.java b/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/AnimationTarget.java new file mode 100644 index 0000000..554d627 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/AnimationTarget.java @@ -0,0 +1,52 @@ +package speiger.src.coreengine.rendering.gui.helper.animations; + +import speiger.src.coreengine.rendering.gui.GuiComponent; + +public enum AnimationTarget +{ + XPOS, + YPOS, + WIDTH, + HEIGHT, + SCALE, + VISIBILITY; + + public void apply(Animator animator, float amount) + { + switch(this) + { + case XPOS: + animator.modifyX(amount); + break; + case YPOS: + animator.modifyY(amount); + break; + case HEIGHT: + animator.modifyHeight(amount); + break; + case WIDTH: + animator.modifyWidth(amount); + break; + case SCALE: + animator.modifyScale(amount); + break; + case VISIBILITY: + animator.modifyVisibility(amount); + break; + } + } + + public float getBase(GuiComponent comp) + { + switch(this) + { + case XPOS: return 0; + case YPOS: return 0; + case WIDTH: return 0; + case HEIGHT: return 0; + case SCALE: return 1; + case VISIBILITY: return 1; + } + return 0F; + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/Animator.java b/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/Animator.java new file mode 100644 index 0000000..2eabce8 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/Animator.java @@ -0,0 +1,178 @@ +package speiger.src.coreengine.rendering.gui.helper.animations; + +import java.util.Iterator; +import java.util.List; +import java.util.function.BiConsumer; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap; +import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap; +import speiger.src.collections.objects.utils.maps.Object2ObjectMaps; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.gui.helper.animations.Animation.AnimationListener; + +public class Animator +{ + GuiComponent owner; + List> listeners = new ObjectArrayList>(); + float xMod = 0F; + float yMod = 0F; + float widthMod = 0F; + float heightMod = 0F; + float scaleMod = 1F; + float visibilityMod = 1F; + + + boolean changed = false; + boolean redraw = false; + + Object2ObjectMap animations = new Object2ObjectLinkedOpenHashMap(); + + public Animator(GuiComponent owner) + { + this.owner = owner; + } + + public void addAnimation(Animation animation, boolean reverse, float delay) + { + AnimationInstance old = animations.remove(animation); + animations.put(animation, animation.createInstance(owner, old, delay, reverse)); + } + + public boolean isPlayingAnimation(Animation animation) + { + return animations.containsKey(animation); + } + + public void update(float particalTicks) + { + if(listeners.size() > 0) + { + for(int i = 0,m=listeners.size();i> iter = Object2ObjectMaps.fastIterator(animations);iter.hasNext();) + { + Object2ObjectMap.Entry animation = iter.next(); + AnimationInstance instance = animation.getValue(); + instance.update(this, particalTicks); + if(instance.isDone()) + { + AnimationListener listener = instance.getListener(); + if(listener != null) + { + listener.onAnimationFinished(animation.getKey(), owner, instance.isReverse()); + } + iter.remove(); + } + } + applyValues(true); + if(changed) + { + owner.onComponentChanged(redraw); + } + owner.finishMassChanging(); + } + + private void resetValues() + { + if(!changed) + { + return; + } + if(!owner.hasConstraints()) + { + owner.changeVisibility(1F / visibilityMod).getBox().move(-xMod, -yMod).grow(-widthMod, -heightMod).scale(1F / scaleMod); + } + else + { + owner.changeVisibility(1F / visibilityMod).getBox().scale(1F / scaleMod); + } + xMod = 0F; + yMod = 0F; + widthMod = 0F; + heightMod = 0F; + scaleMod = 1F; + visibilityMod = 1F; + changed = false; + redraw = false; + } + + public void applyValues(boolean internal) + { + if(!changed || (internal && owner.hasConstraints())) + { + return; + } + owner.changeVisibility(visibilityMod).getBox().scale(scaleMod).move(xMod, yMod).grow(widthMod, heightMod); + } + + + public void modifyX(float value) + { + if(value == 0F) + { + return; + } + xMod += value; + changed = true; + } + + public void modifyY(float value) + { + if(value == 0F) + { + return; + } + yMod += value; + changed = true; + } + + public void modifyWidth(float value) + { + if(value == 0F) + { + return; + } + widthMod += value; + changed = true; + redraw = true; + } + + public void modifyHeight(float value) + { + if(value == 0F) + { + return; + } + heightMod += value; + changed = true; + redraw = true; + } + + public void modifyScale(float value) + { + if(value == 1F) + { + return; + } + scaleMod *= value; + changed = true; + redraw = true; + } + + public void modifyVisibility(float value) + { + visibilityMod *= value; + changed = true; + } + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/transitions/ITransition.java b/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/transitions/ITransition.java new file mode 100644 index 0000000..e8edc48 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/transitions/ITransition.java @@ -0,0 +1,10 @@ +package speiger.src.coreengine.rendering.gui.helper.animations.transitions; + +import speiger.src.coreengine.math.value.IValue; + +public interface ITransition +{ + public IValue initValue(float baseValue, float currentValue, boolean reverse, float delay, float totalDuration); + public float getDuration(); + public float getHiddenValue(); +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/transitions/LiniarTransition.java b/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/transitions/LiniarTransition.java new file mode 100644 index 0000000..214bab6 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/helper/animations/transitions/LiniarTransition.java @@ -0,0 +1,45 @@ +package speiger.src.coreengine.rendering.gui.helper.animations.transitions; + +import speiger.src.coreengine.math.value.IValue; +import speiger.src.coreengine.math.value.LiniarValue; + +public class LiniarTransition implements ITransition +{ + float offsetValue; + float duration; + float selfDelay; + + public LiniarTransition(float offset, float duration) + { + offsetValue = offset; + this.duration = duration; + selfDelay = 0; + } + + public LiniarTransition(float offset, float duration, float selfDelay) { + offsetValue = offset; + this.duration = duration; + this.selfDelay = selfDelay; + } + + @Override + public IValue initValue(float baseValue, float currentValue, boolean reverse, float delay, float totalDuration) + { + float target = reverse ? baseValue : offsetValue; + float selfDelay = reverse ? totalDuration - (this.selfDelay + duration) : this.selfDelay; + return new LiniarValue(delay + selfDelay, duration, currentValue, target); + } + + @Override + public float getHiddenValue() + { + return offsetValue; + } + + @Override + public float getDuration() + { + return duration + selfDelay; + } + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/helper/box/GuiBox.java b/src/main/java/speiger/src/coreengine/rendering/gui/helper/box/GuiBox.java new file mode 100644 index 0000000..db50bca --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/helper/box/GuiBox.java @@ -0,0 +1,316 @@ +package speiger.src.coreengine.rendering.gui.helper.box; + +import java.util.List; + +import speiger.src.collections.objects.lists.ObjectArrayList; + +public class GuiBox implements IGuiBox +{ + List children = new ObjectArrayList(); + IGuiBox parent; + + float minX; + float minY; + float maxX; + float maxY; + + float width; + float height; + float scale = 1F; + + float baseX; + float baseY; + float baseWidth; + float baseHeight; + float baseScale = 1F; + + + protected GuiBox() + { + } + + public GuiBox(float x, float y, float width, float height) + { + set(x, y, width, height); + onChanged(); + } + + public static IGuiBox createSimpleBox(float x, float y, float width, float height) + { + return new GuiBox(x, y, width, height); + } + + public static IGuiBox createParentBox(IGuiBox box) + { + return new GuiBox(box.getMinX(), box.getMinY(), box.getWidth(), box.getHeight()); + } + + public static IGuiBox createParentBox(IGuiBox box, float padding) + { + return new GuiBox(box.getMinX() + padding, box.getMinY() + padding, box.getWidth() - (padding * 2F), box.getHeight() - (padding * 2F)); + } + + @Override + public IGuiBox addChild(IGuiBox box) + { + children.add(box); + box.setParent(this); + return this; + } + + @Override + public IGuiBox removeChild(IGuiBox box) + { + if(children.remove(box)) + { + box.setParent(null); + } + return this; + } + + @Override + public IGuiBox clearChildren() + { + for(int i = 0,m=children.size();i= x && getMinY() <= y && getMaxY() >= y; + } + + public default FacingList getColidingBorder(float x, float y, float margin) + { + margin *= getScale(); + float minX = getMinX(); + float maxX = getMaxX(); + float minY = getMinY(); + float maxY = getMaxY(); + FacingList list = FacingList.EMPTY; + if(y <= minY + margin && y >= minY) list = list.add(Facing.NORTH); + if(x >= maxX - margin && x <= maxX) list = list.add(Facing.EAST); + if(y >= maxY - margin && y <= maxY) list = list.add(Facing.SOUTH); + if(x <= minX + margin && x >= minX) list = list.add(Facing.WEST); + return list; + } + + public default boolean isIntersecting(IGuiBox box){return isIntersecting(box.getMinX(), box.getMaxX(), box.getMinY(), box.getMaxY());} + public default boolean isIntersecting(float minX, float maxX, float minY, float maxY) + { + float xMin = getMinX(); + float yMin = getMinY(); + float xMax = getMaxX(); + float yMax = getMaxY(); + return ((minX >= xMin && minX <= xMax) || (maxX >= xMin && maxX <= xMax)) && ((minY >= yMin && minY <= yMax) || (maxY >= yMin && maxY <= yMax)); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/helper/box/IScreenBox.java b/src/main/java/speiger/src/coreengine/rendering/gui/helper/box/IScreenBox.java new file mode 100644 index 0000000..c8ad501 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/helper/box/IScreenBox.java @@ -0,0 +1,22 @@ +package speiger.src.coreengine.rendering.gui.helper.box; + +public interface IScreenBox +{ + public float getBaseScale(); + + public float getBaseWidth(); + + public default float getSquaredBaseWidth() + { + float value = getBaseWidth(); + return value * value; + } + + public float getBaseHeight(); + + public default float getSquaredBaseHeight() + { + float value = getBaseWidth(); + return value * value; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/helper/box/ParentBox.java b/src/main/java/speiger/src/coreengine/rendering/gui/helper/box/ParentBox.java new file mode 100644 index 0000000..d19a2d7 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/helper/box/ParentBox.java @@ -0,0 +1,276 @@ +package speiger.src.coreengine.rendering.gui.helper.box; + +import java.util.List; + +import speiger.src.collections.objects.lists.ObjectArrayList; + +public class ParentBox implements IGuiBox +{ + List children = new ObjectArrayList(); + IGuiBox parent; + + float minX; + float minY; + float maxX; + float maxY; + + public ParentBox(float value) + { + minX = value; + minY = value; + maxX = -value; + maxY = -value; + } + + public ParentBox(float paddingTop, float paddingLeft, float paddingBottom, float paddingRight) + { + minX = paddingLeft; + minY = paddingTop; + maxX = paddingRight; + maxY = paddingBottom; + } + + @Override + public IGuiBox addChild(IGuiBox box) + { + children.add(box); + box.setParent(this); + return this; + } + + @Override + public IGuiBox removeChild(IGuiBox box) + { + if(children.remove(box)) + { + box.setParent(null); + } + return this; + } + + @Override + public IGuiBox clearChildren() + { + for(int i = 0,m=children.size();i extends Constrain +{ + public static final FloatSupplier DEFAULT = () -> 0F; + List components; + FloatSupplier offset = DEFAULT; + float padding = 0F; + + public MenuConstrain(List components) + { + this.components = components; + } + + public MenuConstrain(List components, FloatSupplier offset) + { + this.components = components; + this.offset = offset; + } + + public MenuConstrain setPadding(float padding) + { + this.padding = padding; + return this; + } + + @Override + public void apply() + { + boolean vertical = target == ConstrainTarget.Y_POS; + if(!target.isPosition()) + { + float max = 0F; + for(int i = 0;i[] chars; + final ITexture texture; + final float height; + final float baseLine; + final float space; + + public FontRenderer(Char2ObjectMap[] chars, ITexture texture, float height, float baseLine) + { + this.chars = chars; + this.texture = texture; + this.height = height; + this.baseLine = baseLine; + space = chars[0].get(' ').getXAdvance(); + } + + @Override + public CharInstance getInstance(char letter, boolean isBold) + { + return chars[isBold ? 1 : 0].get(letter); + } + + @Override + public float getFontHeight() + { + return height; + } + + @Override + public float getBaseLine() + { + return baseLine; + } + + @Override + public ITexture getTexture() + { + return texture; + } + + public List renderText(String text, float x, float y, float z) + { + if(text.isEmpty()) return ObjectLists.empty(); + List drawCalls = new ObjectArrayList<>(); + TextContext context = new TextContext(1F, 0); + List lines = lexer.evaluateLines(text, context, Float.MAX_VALUE); + if(lines.isEmpty()) return ObjectLists.empty(); + WordContext effects = context.getEffect(); + ColorObject textColor = new ColorObject(effects.color); + float yOffset = 0F; + IVertexBuilder builder = new TranslatedVertexBuilder(bufferBuilder, x, y, z, 1F); + bufferBuilder.begin(GL11.GL_TRIANGLES, VertexType.IN_WORLD_UI); + for(int i = 0,m=lines.size();i 0) + { + drawCalls.add(bufferBuilder.getDrawCall(texture.getTextureID())); + } + bufferBuilder.setOffset(0F, 0F, 0F); + return drawCalls; + } + + @Override + public void updateText(TextComponent component) + { + RenderBuffer buffer = component.getBuffer(); + buffer.clear(); + if(component.getText().isEmpty()) + { + return; + } + TextContext context = new TextContext(component); + float boxWidth = component.getBox().getWidth(); + float boxHeight = component.getBox().getHeight(); + List lines = lexer.evaluateLines(component.getText(), context, component.isWidthLimited() ? boxWidth : Float.MAX_VALUE); + if(lines.isEmpty()) + { + return; + } + bufferBuilder.begin(GL11.GL_TRIANGLES, VertexType.UI); + int maxLanes = component.isHeightLimited() ? Math.min((int)(boxHeight / (height * context.getScale())), lines.size()) : lines.size(); + float maxHeight = maxLanes * height * context.getScale(); + float maxWidth = 0F; + float yOffset = component.getVertical().align(boxHeight, maxHeight); + float startX = component.getHorizontal().align(boxWidth, lines.get(0).getWidth()); + component.getMetadata().setStart(startX, yOffset); + WordContext effects = context.getEffect(); + while(effects.isNext(0)) + { + effects = effects.next(); + } + ColorObject textColor = new ColorObject(effects.color); + Float strikeThrough = effects.strike_through ? startX : null; + Float underline = effects.underline ? startX : -1F; + if(component.getBackgroundColor() != null) + { + Tesselator tes = buffer.start(GL11.GL_TRIANGLES, VertexType.UI).setOffset(0F, 0F, -0.001F); + addBackground(tes, lines, maxLanes, yOffset, component.getHorizontal(), boxWidth, component.getBackgroundColor(), false); + tes.setOffset(0F, 0F, 0F); + buffer.finishShape(0); + } + for(int i = 0, index = 0;i < maxLanes;i++) + { + float xOffset = component.getHorizontal().align(boxWidth, lines.get(i).getWidth()); + maxWidth = Math.max(maxWidth, lines.get(i).getWidth()); + strikeThrough = effects.strike_through ? xOffset : null; + underline = effects.underline ? xOffset : null; + for(CharInstance letter : lines.get(i).letterIterator()) + { + xOffset += renderChar(letter, xOffset, yOffset, context.getScale(), effects.italic, effects.flipped, textColor, bufferBuilder, false); + if(effects.isNext(++index)) + { + WordContext next = effects.next(); + Float newStrike = getValue(effects.strike_through, next.strike_through, xOffset, strikeThrough); + if(newStrike == null && strikeThrough != null) + { + addStrikeThrough(strikeThrough, xOffset - strikeThrough, yOffset, textColor, lineBuffer); + } + strikeThrough = newStrike; + Float newLine = getValue(effects.underline, next.underline, xOffset, strikeThrough); + if(newLine == null && underline != null) + { + addUnderline(underline, xOffset - underline, yOffset, textColor, lineBuffer, false); + } + textColor.setRGB(next.color); + underline = newLine; + effects = next; + } + } + if(strikeThrough != null) + { + addStrikeThrough(strikeThrough, xOffset - strikeThrough, yOffset, textColor, lineBuffer); + } + if(underline != null) + { + addUnderline(underline, xOffset - underline, yOffset, textColor, lineBuffer, false); + } + yOffset += height * context.getScale(); + component.getMetadata().addLine(lines.get(i)); + } + maxWidth /= 2; + buffer.finishShape(texture.getTextureID(), bufferBuilder); + if(lineBuffer.hasData()) + { + Tesselator tes = buffer.start(GL11.GL_TRIANGLES, VertexType.UI).offset(0F, 0F, 0.001F); + lineBuffer.merge(tes); + tes.setOffset(0F, 0F, 0F); + buffer.finishShape(0); + } + } + + protected float renderChar(CharInstance instance, float x, float y, float scale, float italic, boolean flipped, ColorObject color, IVertexBuilder buffer, boolean flipPos) + { + switch(instance.getCharacter()) + { + case TAB: + return space * 4 * scale; + case SPACE: + return space * scale; + } + if(instance.getXAdvance() <= 0F) + { + return 0F; + } + if(flipPos) flipped = !flipped; + float minX = x; + float minY = y; + float maxX = x + (instance.getWidth() * scale); + float maxY = flipPos ? y - (instance.getHeight() * scale) : y + (instance.getHeight() * scale); + float minV = flipped ? instance.getMaxV() : instance.getMinV(); + float maxV = flipped ? instance.getMinV() : instance.getMaxV(); + buffer.pos(minX - italic, maxY, 0F).tex(instance.getMinU(), maxV).color4f(color).endVertex(); + buffer.pos(minX + italic, minY, 0F).tex(instance.getMinU(), minV).color4f(color).endVertex(); + buffer.pos(maxX - italic, maxY, 0F).tex(instance.getMaxU(), maxV).color4f(color).endVertex(); + buffer.pos(maxX - italic, maxY, 0F).tex(instance.getMaxU(), maxV).color4f(color).endVertex(); + buffer.pos(minX + italic, minY, 0F).tex(instance.getMinU(), minV).color4f(color).endVertex(); + buffer.pos(maxX + italic, minY, 0F).tex(instance.getMaxU(), minV).color4f(color).endVertex(); + return instance.getXAdvance() * scale; + } + + protected void addBackground(IVertexBuilder tes, List lines, int maxLines, float yPos, Align align, float width, ColorObject color, boolean flipPos) + { + for(int i = 0;i < maxLines;i++) + { + float lineWidth = lines.get(i).getWidth(); + float xOffset = align.align(width, lineWidth); + float maxY = flipPos ? yPos - height : yPos + height; + tes.pos(xOffset, maxY, 0.0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(xOffset, yPos, 0.0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(xOffset + lineWidth, maxY, 0.0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(xOffset + lineWidth, maxY, 0.0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(xOffset, yPos, 0.0F).tex(0F, 0F).color4f(color).endVertex(); + tes.pos(xOffset + lineWidth, yPos, 0.0F).tex(0F, 0F).color4f(color).endVertex(); + yPos = maxY; + } + } + + protected void addUnderline(float xStart, float width, float yStart, ColorObject color, IVertexBuilder buffer, boolean flipPos) + { + float minY = yStart + baseLine + 0.5F; + float maxY = yStart + baseLine + 1.5F; + if(flipPos) + { + minY = yStart - baseLine - 0.5F; + maxY = yStart - baseLine - 1.5F; + } + buffer.pos(xStart, maxY, 0F).tex(0F, 0F).color4f(color).endVertex(); + buffer.pos(xStart, minY, 0F).tex(0F, 0F).color4f(color).endVertex(); + buffer.pos(xStart + width, maxY, 0F).tex(0F, 0F).color4f(color).endVertex(); + buffer.pos(xStart + width, maxY, 0F).tex(0F, 0F).color4f(color).endVertex(); + buffer.pos(xStart, minY, 0F).tex(0F, 0F).color4f(color).endVertex(); + buffer.pos(xStart + width, minY, 0F).tex(0F, 0F).color4f(color).endVertex(); + } + + protected void addStrikeThrough(float xStart, float width, float yStart, ColorObject color, IVertexBuilder buffer) + { + float minY = yStart + height / 2.0F; + float maxY = yStart + height / 2.0F + 1.4F; + buffer.pos(xStart, maxY, 0.0F).tex(0F, 0F).color4f(color).endVertex(); + buffer.pos(xStart, minY, 0.0F).tex(0F, 0F).color4f(color).endVertex(); + buffer.pos(xStart + width, maxY, 0.0F).tex(0F, 0F).color4f(color).endVertex(); + buffer.pos(xStart + width, maxY, 0.0F).tex(0F, 0F).color4f(color).endVertex(); + buffer.pos(xStart, minY, 0.0F).tex(0F, 0F).color4f(color).endVertex(); + buffer.pos(xStart + width, minY, 0.0F).tex(0F, 0F).color4f(color).endVertex(); + } + + protected Float getValue(boolean oldValue, boolean newValue, Float position, Float oldPosition) + { + if((position == null && oldPosition == null) || (position != null && oldPosition != null)) return oldPosition; + return newValue ? position : null; + } + + @Override + public String trimStringToWidth(String text, float limit, boolean reverse) + { + StringBuilder builder = new StringBuilder(); + float width = 0F; + int start = reverse ? text.length() - 1 : 0; + int direction = reverse ? -1 : 1; + for(int i = start;i >= 0 && i < text.length() && width < limit;i += direction) + { + char letter = text.charAt(i); + width += getCharLength(letter); + if(width > limit) break; + if(reverse) builder.insert(0, letter); + else builder.append(letter); + } + return builder.toString(); + } + + @Override + public float getCharLength(char letter, boolean bold) + { + switch(letter) + { + case SPACE: + return space; + case TAB: + return space * 4; + default: + CharInstance instance = getInstance(letter, bold); + return instance == null ? 0F : instance.getXAdvance(); + } + } + + @Override + public float getTextLength(String text, int flags) + { + float result = 0.0F; + float current = 0.0F; + boolean bold = (flags & BOLD) != 0; + for(int i = 0;i < text.length();i++) + { + char character = text.charAt(i); + if(LINE_SEPERATOR == character) + { + result = Math.max(result, current += space); + current = 0.0F; + continue; + } + if(character == '§' && (flags & SPECIAL) != 0 && i + 1 < text.length() && text.charAt(i + 1) == '<') + { + String search = TextUtil.searchUntil(text, i + 2, '>', INVALID_SEARCH); + if(search.length() > 0) + { + for(String entry : search.toLowerCase(Locale.ROOT).split(",")) + { + bold = TextUtil.findFlag(entry, "bold", bold); + } + i += search.length() + 2; + continue; + } + } + current += getCharLength(character, bold); + } + return Math.max(result, current); + } + + @Override + public float[] getTextLengths(String text, int flags) + { + FloatList results = new FloatArrayList(); + results.add(0F); + float current = 0F; + int chars = 0; + boolean bold = (flags & BOLD) != 0; + float max = 0F; + for(int i = 0;i < text.length();i++) + { + char character = text.charAt(i); + if(LINE_SEPERATOR == character) + { + results.add(current); + max = Math.max(max, current); + current = 0F; + chars = 0; + continue; + } + chars++; + if(character == '§' && (flags & SPECIAL) != 0 && i + 1 < text.length() && text.charAt(i + 1) == '<') + { + String result = TextUtil.searchUntil(text, i + 2, '>', INVALID_SEARCH); + if(result.length() > 0) + { + for(String entry : result.toLowerCase(Locale.ROOT).split(",")) + { + bold = TextUtil.findFlag(entry, "bold", bold); + } + i += result.length() + 2; + continue; + } + } + current += getCharLength(character, bold); + } + if(chars++ > 0) + { + results.add(current); + max = Math.max(max, current); + } + results.set(0, max); + return results.toFloatArray(); + } + + @Override + public float getTextHeight(String text, int flags) + { + return getTextLengths(text, flags).length - 1 * height; + } + + @Override + public boolean isCharValid(char letter) + { + return chars[0].containsKey(letter); + } + + @Override + public String[] splitLines(String text, float maxWidth, int flags) + { + TextContext context = new TextContext(1F, ((flags & IFontRenderer.SPECIAL) != 0 ? 16 : 0) | ((flags & IFontRenderer.BOLD) != 0 ? 1 : 0)); + List lines = lexer.evaluateLines(text, context, maxWidth); + String[] array = new String[lines.size()]; + if(context.allowsSpecial()) + { + int index = 0; + StringBuilder builder = new StringBuilder(); + WordContext wordContext = context.getEffect(); + while(wordContext.isNext(0)) + { + builder.append(wordContext.getString(false)); + wordContext = wordContext.next(); + } + for(int i = 0,m=lines.size();i recylcePool = new SimplePool<>(100, Matrix4f::new); + Stack transformStack = new ObjectArrayList<>(); + Matrix4f transform = new Matrix4f(); + boolean isFastTransform = true; + SimplePool fastPool = new SimplePool<>(100, () -> Vec4f.newMutable(0F, 0F, 0F, 1F)); + Stack fastStack = new ObjectArrayList<>(); + Vec4f fastTransform = Vec4f.newMutable(0F, 0F, 0F, 1F); + + SimplePool callPool = new SimplePool<>(100, GLCall::new); + ObjectPriorityDequeue activeCalls = new ObjectArrayFIFOQueue<>(); + + Tesselator bufferHelper = new Tesselator(655340); + FloatBuffer renderBuffer = MemoryUtil.memAllocFloat(655340); + Tesselator renderer = new Tesselator(renderBuffer); + ITexture currentTexture; + boolean drawing = false; + boolean useTexture = false; + Vec4f currentFrame = Vec4f.newMutable(); + float roundedNess = 0F; + + GuiModel texturedModel = GuiModel.createTextureModel(Short.MAX_VALUE * 2); + + Align horizontal = Align.CENTER; + Align vertical = Align.CENTER; + + Vec3f helper = Vec3f.newMutable(); + Vec4f transformHelper = Vec4f.newMutable(); + Vec4f alignHelper = Vec4f.newMutable(); + Vec3f alignTranslation = Vec3f.newMutable(0F); + Vec3f alignScale = Vec3f.newMutable(1F); + + GuiManager manager; + + public UIRenderer(GuiManager manager) + { + this.manager = manager; + TextureManager.INSTANCE.getReloader().addReloadableResource(this); + } + + public RenderBuffer createBuffer() + { + return new RenderBuffer(bufferHelper); + } + + public UIRenderer push() + { + if(isFastTransform) + { + fastStack.push(fastPool.get().set(fastTransform)); + return this; + } + transformStack.push(recylcePool.get().load(transform)); + return this; + } + + public UIRenderer pop() + { + if(isFastTransform) + { + Vec4f pop = fastStack.pop(); + fastTransform.set(pop); + fastPool.accept(pop); + return this; + } + Matrix4f pop = transformStack.pop(); + transform.load(pop); + pop.getScale(alignScale); + recylcePool.accept(pop); + return this; + } + + public UIRenderer setVisibility(float visibility) + { + sanityCheck("Visibilty Change"); + renderer.setVisibilty(visibility); + return this; + } + + public UIRenderer setBrightness(float brightness) + { + sanityCheck("Brightness change"); + renderer.setBrightness(brightness); + return this; + } + + public UIRenderer resetEffects() + { + renderer.setBrightness(1F).setVisibilty(1F); + return this; + } + + public UIRenderer setActiveTexture(ITexture texture) + { + if(getTextureId(currentTexture) != getTextureId(texture)) + { + addDrawCall(); + currentTexture = texture; + } + return this; + } + + public UIRenderer setRoundness(IGuiBox box, float roundedNess) + { + if(roundedNess <= 0) + { + if(this.roundedNess > 0F) addDrawCall(); + this.roundedNess = roundedNess; + return this; + } + alignHelper.set(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY()); + if(!currentFrame.equals(alignHelper) || this.roundedNess != roundedNess) + { + addDrawCall(); + currentFrame.set(alignHelper); + this.roundedNess = roundedNess; + } + return this; + } + + protected void applyEffects(UniformVec2f vec) + { + vec.storeData(renderer.getBrightness(), renderer.getVisibility()); + } + + public UIRenderer translate(float x, float y) + { + return translate(x, y, 0F); + } + + public UIRenderer translate(Vec2f offset) + { + return translate(offset.getX(), offset.getY(), 0F); + } + + public UIRenderer translate(float x, float y, float z) + { + if(isFastTransform) + { + fastTransform.add(x, y, z, 0F); + return this; + } + transform.translate(helper.set(x, y, z)); + return this; + } + + public UIRenderer translate(Vec3f offset) + { + if(isFastTransform) + { + fastTransform.add(offset.getX(), offset.getY(), offset.getZ(), 0F); + return this; + } + transform.translate(offset); + return this; + } + + public UIRenderer rotate(Quaternion quad) + { + if(isFastTransform) return this; + transform.rotate(quad); + return this; + } + + public UIRenderer rotateX(float angle) + { + if(isFastTransform) return this; + transform.rotateX((float)Math.toRadians(angle)); + return this; + } + + public UIRenderer rotateY(float angle) + { + if(isFastTransform) return this; + transform.rotateY((float)Math.toRadians(angle)); + return this; + } + + public UIRenderer rotateZ(float angle) + { + if(isFastTransform) return this; + transform.rotateZ((float)Math.toRadians(angle)); + return this; + } + + public UIRenderer scale(float x, float y, float z) + { + if(isFastTransform) return this; + transform.scale(helper.set(x, y, z)); + alignScale.multiply(x, y, z); + return this; + } + + public UIRenderer scale(float scale) + { + if(isFastTransform) + { + fastTransform.multiply(1F, 1F, 1F, scale); + return this; + } + transform.scale(helper.set(scale, scale, scale)); + alignScale.multiply(scale); + return this; + } + + public UIRenderer unscale(float x, float y, float z) + { + if(isFastTransform) return this; + transform.scale(helper.set(1.0F / x, 1.0F / y, 1.0F / z)); + alignScale.multiply(helper); + return this; + } + + public UIRenderer unscale(float scale) + { + scale = 1.0F / scale; + if(isFastTransform) + { + fastTransform.multiply(1F, 1F, 1F, scale); + return this; + } + transform.scale(helper.set(scale, scale, scale)).getScale(alignScale); + alignScale.multiply(scale); + return this; + } + + public UIRenderer resetTransform() + { + if(isFastTransform) + { + fastTransform.set(0F, 0F, 0F, 1F); + return this; + } + transform.setIdentity(); + alignScale.set(Vec3f.ONE); + return this; + } + + public float getCurrentZ() + { + return isFastTransform ? fastTransform.getZ() : transform.get(3, 2); + } + + public ITexture getActiveTexture() + { + return currentTexture; + } + + protected Matrix4f getCurrentMatrix() + { + return transform; + } + + public Matrix4f createCurrentMatrix(Matrix4f matrix) + { + return isFastTransform ? matrix.setIdentity().translate(fastTransform.getX(), fastTransform.getY(), fastTransform.getZ()) : matrix.load(transform); + } + + public UIRenderer setFastTransform(boolean fast) + { + isFastTransform = fast; + return this; + } + + public UIRenderer beginFrame() + { + if(drawing) + { + throw new RuntimeException("Already Drawing!"); + } + drawing = true; + fastTransform.set(0F, 0F, 0F, 1F); + roundedNess = 0F; + currentFrame.negate(); + transform.setIdentity(); + alignScale.set(Vec3f.ONE); + transformStack.clear(); + fastStack.clear(); + return this; + } + + public UIRenderer flush() + { + if(!renderer.isDrawing()) + { + return this; + } + renderer.finishData(); + int count = renderer.getVertexCount(); + if(count <= 0) + { + return this; + } + if(count > (activeCalls.isEmpty() ? 0 : activeCalls.last().getCount())) + { + addDrawCall(); + } + texturedModel.storeData(renderBuffer); + GuiShader shader = manager.getShader(); + shader.bind(manager.getOrthoMatrixBuffer()); + texturedModel.bindArray(); + int last = 0; + while(!activeCalls.isEmpty()) + { + GLCall call = activeCalls.dequeue(); + shader.texture.storeTexture(call.getTexture()); + shader.useTexture.storeData(call.getTexture() > 0 ? 1F : 0F); + shader.roundeness.storeData(call.getRoundedNess()); + if(call.getRoundedNess() > 0F) shader.bounds.storeData(call.getFrame()); + GLUtils.drawArrays(call.getType(), last, call.getCount() - last); + last = call.getCount(); + callPool.accept(call); + } + texturedModel.unbindArray(); + shader.stopShader(); + renderer.resetVertexes(); + return this; + } + + public UIRenderer endFrame() + { + if(!drawing) + { + throw new RuntimeException("Not Drawing!"); + } + drawing = false; + isFastTransform = true; + flush(); + fastTransform.set(0F, 0F, 0F, 1F); + transform.setIdentity(); + renderer.resetEffects(); + useTexture = false; + while(!transformStack.isEmpty()) + { + recylcePool.accept(transformStack.pop()); + } + while(!fastStack.isEmpty()) + { + fastPool.accept(fastStack.pop()); + } + return this; + } + + @Override + public void reload() + { + texturedModel.remove(); + MemoryUtil.memFree(renderBuffer); + texturedModel = GuiModel.createTextureModel(Short.MAX_VALUE * 2); + renderBuffer = MemoryUtil.memAllocFloat(655340); + renderer = new Tesselator(renderBuffer); + } + + @Override + public void destroy() + { + texturedModel.remove(); + MemoryUtil.memFree(renderBuffer); + TextureManager.INSTANCE.getReloader().removeReloadableResource(this); + } + + public UIRenderer drawBuffers(Iterable drawCalls, float width, float height) + { + sanityCheck("Buffer Drawing"); + applyAlignment(0, 0, width, height, 0, alignHelper); + for(DrawCall call : drawCalls) + { + ensureDrawing(call.getGLType(), call.getTextureId() > 0); + FloatBuffer buffer = ByteBuffer.wrap(call.getData()).order(ByteOrder.nativeOrder()).asFloatBuffer(); + for(int i = 0,m=buffer.remaining();i+9<=m;i+=9) + { + pos(transformHelper.set(buffer.get(i)+alignHelper.getX(), buffer.get(i+1)+alignHelper.getY(), buffer.get(i+2), 1F)) + .tex(buffer.get(i+3), buffer.get(i+4)) + .color4f(buffer.get(i+5), buffer.get(i+6), buffer.get(i+7), buffer.get(i+8)).endVertex(); + } + } + alignTranslation.set(0F, 0F, 0F); + return this; + } + + public UIRenderer drawQuad(IGuiBox box, ColorObject color) + { + return drawQuad(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), color); + } + + public UIRenderer drawQuad(IGuiBox box, float zLevel, ColorObject color) + { + return drawQuad(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), zLevel, color); + } + + public UIRenderer drawQuad(float minX, float minY, float maxX, float maxY, ColorObject color) + { + return drawQuad(minX, minY, maxX, maxY, 0.0F, color); + } + + public UIRenderer drawQuad(float minX, float minY, float maxX, float maxY, float zLevel, ColorObject color) + { + ensureDrawing(GL11.GL_TRIANGLES, false); + applyAlignment(minX, minY, maxX, maxY, zLevel, alignHelper); + pos(transformHelper.set(alignHelper.getX(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getZ(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + alignTranslation.set(0F, 0F, 0F); + return this; + } + + public UIRenderer drawTexturedQuad(IGuiBox box, ColorObject color, float minU, float minV, float maxU, float maxV) + { + return drawTexturedQuad(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), color, minU, minV, maxU, maxV); + } + + public UIRenderer drawTexturedQuad(IGuiBox box, float zLevel, ColorObject color, float minU, float minV, float maxU, float maxV) + { + return drawTexturedQuad(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), zLevel, color, minU, minV, maxU, maxV); + } + + public UIRenderer drawTexturedQuad(float minX, float minY, float maxX, float maxY, ColorObject color, float minU, float minV, float maxU, float maxV) + { + return drawTexturedQuad(minX, minY, maxX, maxY, 0F, color, minU, minV, maxU, maxV); + } + + public UIRenderer drawTexturedQuad(float minX, float minY, float maxX, float maxY, float zLevel, ColorObject color, float minU, float minV, float maxU, float maxV) + { + ensureDrawing(GL11.GL_TRIANGLES, true); + applyAlignment(minX, minY, maxX, maxY, zLevel, alignHelper); + pos(transformHelper.set(alignHelper.getX(), alignHelper.getW(), 0F, 1F)).tex(minU, maxV).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(minU, minV).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(maxU, maxV).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(maxU, maxV).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(minU, minV).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getZ(), alignHelper.getY(), 0F, 1F)).tex(maxU, minV).color4f(color).endVertex(); + alignTranslation.set(0F, 0F, 0F); + return this; + } + + public UIRenderer drawGradientQuad(IGuiBox box, ColorObject from, ColorObject to, Facing direction) + { + return drawGradientQuad(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), from, to, direction); + } + + public UIRenderer drawGradientQuad(float minX, float minY, float maxX, float maxY, ColorObject from, ColorObject to, Facing direction) + { + return drawGradientQuad(minX, minY, maxX, maxY, 0F, from, to, direction); + } + + public UIRenderer drawGradientQuad(float minX, float minY, float maxX, float maxY, float zLevel, ColorObject start, ColorObject end, Facing direction) + { + if(!direction.isPositive()) + { + ColorObject wrapper = start; + start = end; + end = wrapper; + } + ensureDrawing(GL11.GL_TRIANGLES, false); + applyAlignment(minX, minY, maxX, maxY, zLevel, alignHelper); + pos(transformHelper.set(alignHelper.getX(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(direction.isXAxis() ? start : end).endVertex(); + pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(direction.isXAxis() ? start : start).endVertex(); + pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(direction.isXAxis() ? end : end).endVertex(); + pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(direction.isXAxis() ? end : end).endVertex(); + pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(direction.isXAxis() ? start : start).endVertex(); + pos(transformHelper.set(alignHelper.getZ(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(direction.isXAxis() ? end : start).endVertex(); + alignTranslation.set(0F, 0F, 0F); + return this; + } + + public UIRenderer drawFrame(IGuiBox box, ColorObject color) + { + return drawFrame(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), color); + } + + public UIRenderer drawFrame(IGuiBox box, float zLevel, ColorObject color) + { + return drawFrame(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), zLevel, color); + } + + public UIRenderer drawFrame(float minX, float minY, float maxX, float maxY, ColorObject color) + { + return drawFrame(minX, minY, maxX, maxY, 0F, color); + } + + public UIRenderer drawFrame(float minX, float minY, float maxX, float maxY, float zLevel, ColorObject color) + { + ensureDrawing(GL11.GL_LINES, false); + applyAlignment(minX, minY, maxX, maxY, zLevel, alignHelper); + pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getZ(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getZ(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getX(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getX(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + alignTranslation.set(0F, 0F, 0F); + return this; + } + + public UIRenderer drawLine(float minX, float minY, float maxX, float maxY, ColorObject color) + { + return drawLine(minX, minY, maxX, maxY, 0F, color); + } + + public UIRenderer drawLine(float minX, float minY, float maxX, float maxY, float zLevel, ColorObject color) + { + ensureDrawing(GL11.GL_LINES, false); + applyAlignment(minX, minY, maxX, maxY, zLevel, alignHelper); + pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); + alignTranslation.set(0F, 0F, 0F); + return this; + } + + public UIRenderer startCustomShape(int shapeType, boolean needsTexture) + { + sanityCheck("Custom Shape"); + ensureDrawing(shapeType, needsTexture); + return this; + } + + public UIRenderer pos(float x, float y, float z) + { + pos(transformHelper.set(x, y, z, 1F)); + return this; + } + + public UIRenderer pos(Vec3f pos) + { + return pos(pos.getX(), pos.getY(), pos.getZ()); + } + + public UIRenderer color(float r, float g, float b) + { + renderer.color4f(r, g, b); + return this; + } + + public UIRenderer color(float r, float g, float b, float a) + { + renderer.color4f(r, g, b, a); + return this; + } + + public UIRenderer color(ColorObject color) + { + renderer.color4f(color); + return this; + } + + public UIRenderer tex(float u, float v) + { + renderer.tex(u, v); + return this; + } + + public UIRenderer tex(Vec2f tex) + { + return tex(tex.getX(), tex.getY()); + } + + public UIRenderer endVertex() + { + renderer.endVertex(); + return this; + } + + protected void sanityCheck(String task) + { + if(drawing) + { + return; + } + throw new RuntimeException(task); + } + + protected Tesselator pos(Vec4f pos) + { + if(isFastTransform) + { + return renderer.pos(fastTransform(pos.getX(), fastTransform.getX(), alignTranslation.getX()), fastTransform(pos.getY(), fastTransform.getY(), alignTranslation.getY()), fastTransform(pos.getZ(), fastTransform.getZ(), alignTranslation.getZ())); + } + float x = (transform.get(0) * pos.getX() + transform.get(4) * pos.getY() + transform.get(8) * pos.getZ() + transform.get(12)) + (alignTranslation.getX() * alignScale.getX()); + float y = (transform.get(1) * pos.getX() + transform.get(5) * pos.getY() + transform.get(9) * pos.getZ() + transform.get(13)) + (alignTranslation.getY() * alignScale.getY()); + float z = (transform.get(2) * pos.getX() + transform.get(6) * pos.getY() + transform.get(10) * pos.getZ() + transform.get(14)) + (alignTranslation.getZ() * alignScale.getZ()); + return renderer.pos(x, y, z); + } + + private float fastTransform(float pos, float offset, float align) + { + return ((pos + align) * fastTransform.getW()) + offset; + } + + protected void applyAlignment(float minX, float minY, float maxX, float maxY, float zLevel, Vec4f result) + { + applyAlignment(minX, minY, maxX, maxY, zLevel, vertical, horizontal, renderer, result); + } + + protected void applyAlignment(float minX, float minY, float maxX, float maxY, float zLevel, Align vertical, Align horizontal, Tesselator tes, Vec4f result) + { + float width = maxX - minX; + float height = maxY - minY; + float xOffset = 0F; + float yOffset = 0F; + switch(vertical) + { + case LEFT_TOP: + yOffset = 0; + result.setY(0F).setW(height); + break; + case CENTER: + yOffset = height / 2F; + result.setY(-yOffset).setW(yOffset); + break; + case RIGHT_BOTTOM: + yOffset = maxY; + result.setY(-height).setW(0F); + break; + } + switch(horizontal) + { + case LEFT_TOP: + xOffset = 0; + result.setX(0F).setZ(width); + break; + case CENTER: + xOffset = width / 2F; + result.setX(-xOffset).setZ(xOffset); + break; + case RIGHT_BOTTOM: + xOffset = maxX; + result.setX(-width).setZ(0F); + break; + } + alignTranslation.set(minX + xOffset, minY + yOffset, zLevel); + } + + protected void ensureDrawing(int glState, boolean textureNeeded) + { + if(!renderer.isDrawing()) + { + renderer.begin(glState, VertexType.UI); + } + if(glState != renderer.getGLDrawType() || useTexture != textureNeeded) + { + addDrawCall(); + renderer.changeGLType(glState); + useTexture = textureNeeded; + } + } + + protected void addDrawCall() + { + if(getDrawCall() >= renderer.getVertexCount()) + { + return; + } + GLCall call = callPool.get(); + call.setType(renderer.getGLDrawType()); + call.setCount(renderer.getVertexCount()); + call.setTexture(getTextureId(useTexture ? currentTexture : null)); + call.setFrame(currentFrame); + call.setRounded(roundedNess); + activeCalls.enqueue(call); + } + + private int getDrawCall() + { + return activeCalls.isEmpty() ? 0 : activeCalls.last().getCount(); + } + + private int getTextureId(ITexture texture) + { + return texture == null ? 0 : texture.getTextureID(); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/renderer/buffer/DelayedRenderBuffer.java b/src/main/java/speiger/src/coreengine/rendering/gui/renderer/buffer/DelayedRenderBuffer.java new file mode 100644 index 0000000..494f15c --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/renderer/buffer/DelayedRenderBuffer.java @@ -0,0 +1,140 @@ +package speiger.src.coreengine.rendering.gui.renderer.buffer; + +import java.util.function.BiConsumer; + +import speiger.src.collections.floats.lists.FloatArrayList; +import speiger.src.collections.floats.lists.FloatList; +import speiger.src.coreengine.rendering.tesselation.IVertexBuilder; +import speiger.src.coreengine.rendering.tesselation.Tesselator; + +public class DelayedRenderBuffer implements IVertexBuilder +{ + protected FloatList buffer = new FloatArrayList(); + + @Override + public DelayedRenderBuffer pos(float x, float y) + { + buffer.add(x); + buffer.add(y); + return this; + } + + @Override + public DelayedRenderBuffer pos(float x, float y, float z) + { + buffer.add(x); + buffer.add(y); + buffer.add(z); + return this; + } + + @Override + public DelayedRenderBuffer color4f(float r, float g, float b, float a) + { + buffer.add(r); + buffer.add(g); + buffer.add(b); + buffer.add(a); + return this; + } + + @Override + public DelayedRenderBuffer tex(float u, float v) + { + buffer.add(u); + buffer.add(v); + return this; + } + + @Override + public IVertexBuilder color3f(float r, float g, float b) + { + buffer.add(r); + buffer.add(g); + buffer.add(b); + return this; + } + + @Override + public IVertexBuilder normal(float x, float y, float z) + { + buffer.add(x); + buffer.add(y); + buffer.add(z); + return this; + } + + @Override + public IVertexBuilder custom(float x) + { + buffer.add(x); + return this; + } + + @Override + public IVertexBuilder custom(float x, float y) + { + buffer.add(x); + buffer.add(y); + return this; + } + + @Override + public IVertexBuilder custom(float x, float y, float z) + { + buffer.add(x); + buffer.add(y); + buffer.add(z); + return this; + } + + @Override + public IVertexBuilder custom(float x, float y, float z, float w) + { + buffer.add(x); + buffer.add(y); + buffer.add(z); + buffer.add(w); + return this; + } + + @Override + public IVertexBuilder endVertex() + { + return this; + } + + public void merge(Tesselator builder, BiConsumer merge) + { + merge.accept(buffer, builder); + buffer.clear(); + } + + protected void defaultMerge(FloatList buffer, Tesselator builder) + { + if(builder.hasTexture()) + { + for(int i = 0;i 0; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/renderer/buffer/RenderBuffer.java b/src/main/java/speiger/src/coreengine/rendering/gui/renderer/buffer/RenderBuffer.java new file mode 100644 index 0000000..2e8757d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/renderer/buffer/RenderBuffer.java @@ -0,0 +1,80 @@ +package speiger.src.coreengine.rendering.gui.renderer.buffer; + +import java.util.Iterator; +import java.util.List; +import java.util.function.Consumer; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.rendering.gui.GuiComponent; +import speiger.src.coreengine.rendering.models.DrawCall; +import speiger.src.coreengine.rendering.tesselation.Tesselator; +import speiger.src.coreengine.rendering.tesselation.VertexList; +import speiger.src.coreengine.utils.collections.iterators.IndexIterator; +import speiger.src.coreengine.utils.collections.iterators.IterableWrapper; + +public class RenderBuffer implements Consumer, Iterable +{ + List drawCalls = new ObjectArrayList(); + Tesselator tesselator; + + public RenderBuffer(Tesselator tesselator) + { + this.tesselator = tesselator; + } + + @Override + public void accept(GuiComponent t) + { + clear(); + } + + public T get(int index, Class clz) + { + return (T)drawCalls.get(index); + } + + public void addDrawCall(DrawCall call) + { + drawCalls.add(call); + } + + public void clear() + { + drawCalls.clear(); + } + + public Tesselator start(int shape, VertexList format) + { + return tesselator.begin(shape, format); + } + + public void finishShape(int texture, Tesselator tesselator) + { + if(!tesselator.isDrawing()) + { + throw new IllegalStateException("Tesselator wasnt filled"); + } + tesselator.finishData(); + if(tesselator.getVertexCount() <= 0) + { + return; + } + drawCalls.add(tesselator.getDrawCall(texture)); + } + + public void finishShape(int texture) + { + finishShape(texture, tesselator); + } + + @Override + public Iterator iterator() + { + return drawCalls.iterator(); + } + + public Iterable selectionIterator(int...indexes) + { + return IterableWrapper.wrap(new IndexIterator(drawCalls, indexes)); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/renderer/buffer/TranslatedVertexBuilder.java b/src/main/java/speiger/src/coreengine/rendering/gui/renderer/buffer/TranslatedVertexBuilder.java new file mode 100644 index 0000000..9933a38 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/renderer/buffer/TranslatedVertexBuilder.java @@ -0,0 +1,98 @@ +package speiger.src.coreengine.rendering.gui.renderer.buffer; + +import speiger.src.coreengine.rendering.tesselation.IVertexBuilder; + +public class TranslatedVertexBuilder implements IVertexBuilder +{ + IVertexBuilder builder; + float xOff; + float yOff; + float zOff; + float wOff; + + public TranslatedVertexBuilder(IVertexBuilder builder, float xOff, float yOff, float zOff, float wOff) + { + this.builder = builder; + this.xOff = xOff; + this.yOff = yOff; + this.zOff = zOff; + this.wOff = wOff; + } + + @Override + public IVertexBuilder pos(float x, float y) + { + builder.pos(x, y); + return this; + } + + @Override + public IVertexBuilder pos(float x, float y, float z) + { + builder.pos(x, y, z); + return this; + } + + @Override + public IVertexBuilder color3f(float r, float g, float b) + { + builder.color3f(r, g, b); + return this; + } + + @Override + public IVertexBuilder color4f(float r, float g, float b, float a) + { + builder.color4f(r, g, b, a); + return this; + } + + @Override + public IVertexBuilder tex(float u, float v) + { + builder.tex(u, v); + return this; + } + + @Override + public IVertexBuilder normal(float x, float y, float z) + { + builder.normal(x, y, z); + return this; + } + + @Override + public IVertexBuilder custom(float x) + { + builder.custom(x); + return this; + } + + @Override + public IVertexBuilder custom(float x, float y) + { + builder.custom(x, y); + return this; + } + + @Override + public IVertexBuilder custom(float x, float y, float z) + { + builder.custom(x, y, z); + return this; + } + + @Override + public IVertexBuilder custom(float x, float y, float z, float w) + { + builder.custom(x, y, z, w); + return this; + } + + @Override + public IVertexBuilder endVertex() + { + builder.custom(xOff, yOff, zOff, wOff).endVertex(); + return this; + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/renderer/lexer/Line.java b/src/main/java/speiger/src/coreengine/rendering/gui/renderer/lexer/Line.java new file mode 100644 index 0000000..63df5c3 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/renderer/lexer/Line.java @@ -0,0 +1,142 @@ +package speiger.src.coreengine.rendering.gui.renderer.lexer; + +import java.util.List; + +import speiger.src.collections.objects.collections.ObjectIterator; +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.utils.ObjectIterators; +import speiger.src.coreengine.rendering.gui.renderer.IFontRenderer.CharInstance; +import speiger.src.coreengine.utils.collections.iterators.IterableWrapper; + +public class Line +{ + float totalWidth; + float width; + int totalLetters = 0; + String result = null; + List words = new ObjectArrayList(); + + public Line(float totalWidth) + { + this.totalWidth = totalWidth; + } + + public boolean attemptAddingWord(Word word) + { + if(word.getWidth() + width > totalWidth) + { + return false; + } + width += word.getWidth(); + words.add(word); + totalLetters += word.size(); + return true; + } + + public void addWord(Word word) + { + width += word.getWidth(); + words.add(word); + totalLetters += word.size(); + } + + protected void finishLine() + { + StringBuilder builder = new StringBuilder(); + for(Word word : words) + { + builder.append(word.getLetters()); + } + result = builder.toString(); + } + + public int getIndex(float width, boolean total) + { + if(width <= 0F) return (total && !words.isEmpty()) ? words.get(0).getStartIndex() : 0; + if(width >= this.width) return ((total && !words.isEmpty()) ? words.get(0).getStartIndex() : 0) + totalLetters; + int letters = (total && !words.isEmpty()) ? words.get(0).getStartIndex() : 0; + for(int i = 0,m=words.size();i width) + { + return letters + word.getIndex(width); + } + width -= word.getWidth(); + letters += word.size(); + } + return letters; + } + + public String getText() + { + return result; + } + + public boolean isEmpty() + { + return words.isEmpty(); + } + + public int size() + { + return totalLetters; + } + + public int getStart() + { + return words.isEmpty() ? 0 : words.get(0).getStartIndex(); + } + + public int getEnd() + { + return words.isEmpty() ? 0 : words.get(words.size() - 1).getEndIndex(); + } + + public Word getWord(int letterIndex) + { + for(int i = 0;i= letterIndex) + { + return word; + } + letterIndex -= word.size(); + } + return null; + } + + public float getWidth() + { + return width; + } + + public float getWidth(int index) + { + if(index <= 0) return 0F; + else if(index >= totalLetters) return width; + float width = 0F; + for(int i = 0,m=words.size();i index) + { + return width + word.getWidth(index); + } + width += word.getWidth(); + index -= word.size(); + } + return 0; + } + + public Iterable letterIterator() + { + ObjectIterator[] arrays = new ObjectIterator[words.size()]; + for(int i = 0,m=words.size();i changes = new ObjectArrayList(); + + public TextContext(float scale) + { + this(scale, 0); + } + + public TextContext(float scale, int flags) + { + this.scale = scale; + originalFlags = this.flags = flags; + originalColor = Color.WHITE; + originalItalic = 0F; + changes.add(new WordContext(0, flags, italic, color.hashCode())); + } + + public TextContext(TextComponent component) + { + scale = component.getTextScale(); + originalFlags = createFlags(component); + originalColor = component.getTextColor().toColor(); + originalItalic = component.getItalic(); + flags = originalFlags; + color.setRGB(originalColor.getRGB()); + italic = originalItalic; + changes.add(new WordContext(0, flags, italic, color.hashCode())); + } + + public void reset() + { + flags = originalFlags; + color.setRGB(originalColor.getRGB()); + italic = originalItalic; + } + + public boolean allowsSpecial() + { + return (originalFlags & SPECIAL) != 0; + } + + public boolean isBold() + { + return (flags & BOLD) != 0; + } + + public boolean isSingle() + { + return (originalFlags & SINGLE) != 0; + } + + public float getScale() + { + return scale; + } + + public void apply(String[] flagString, int index) + { + for(int i = 0,m=flagString.length;i 0.0F ? 0.0F : 3.0F); + } + WordContext context = new WordContext(index, flags, italic, color.hashCode()); + changes.get(changes.size() - 1).setNext(context); + changes.add(context); + } + + void setFlag(int flag, boolean value) + { + flags = (value ? flags | flag : flags & ~(flag)); + } + + static int createFlags(TextComponent component) + { + int flags = 0; + flags |= component.isBold() ? BOLD : 0; + flags |= component.isFlipped() ? FLIPPED : 0; + flags |= component.isStrikeThrough() ? STRIKE_THROUGH : 0; + flags |= component.isUnderlined() ? UNDERLINE : 0; + flags |= component.isRenderingSpecial() ? SPECIAL : 0; + flags |= component.isForcedSingleLine() ? SINGLE : 0; + return flags; + } + + public static class WordContext + { + static final DecimalFormat ITALIC_FORMAT = new DecimalFormat("0.##"); + WordContext prev; + WordContext next; + public final int charIndex; + public final boolean bold; + public final boolean flipped; + public final boolean strike_through; + public final boolean underline; + public final float italic; + public final int color; + + public WordContext(int index, int flags, float italic, int color) + { + charIndex = index; + bold = (flags & BOLD) != 0; + flipped = (flags & FLIPPED) != 0; + strike_through = (flags & STRIKE_THROUGH) != 0; + underline = (flags & UNDERLINE) != 0; + this.italic = italic; + this.color = color; + } + + void setNext(WordContext next) + { + this.next = next; + this.next.prev = this; + } + + public boolean isNext(int index) + { + return next != null && next.charIndex <= index; + } + + public WordContext next() + { + return next; + } + + public String getString(boolean restart) + { + if(prev == null || restart) + { + if(anyEnabled()) + { + StringJoiner joiner = new StringJoiner(",", "§<", ">"); + if(bold) joiner.add("bold=true"); + if(flipped) joiner.add("flipped=true"); + if(underline) joiner.add("underline=true"); + if(strike_through) joiner.add("strike=true"); + if(color != -1) joiner.add("color="+ColorObject.rgb(color).getHexCode(true)); + if(italic > 0F) joiner.add("italic="+ITALIC_FORMAT.format(italic)); + return joiner.toString(); + } + return ""; + } + if(bold != prev.bold || flipped != prev.flipped || underline != prev.underline || strike_through != prev.strike_through || color != prev.color || Float.compare(italic, prev.italic) != 0) + { + StringJoiner joiner = new StringJoiner(",", "§<", ">"); + if(bold != prev.bold) joiner.add("bold="+bold); + if(flipped != prev.flipped) joiner.add("flipped="+flipped); + if(underline != prev.underline) joiner.add("underline="+underline); + if(strike_through != prev.strike_through) joiner.add("strike="+strike_through); + if(color != prev.color) joiner.add("color="+ColorObject.rgb(color).getHexCode(true)); + if(Float.compare(italic, prev.italic) != 0) joiner.add("italic="+ITALIC_FORMAT.format(italic)); + return joiner.toString(); + } + return ""; + } + + protected boolean anyEnabled() + { + return bold || flipped || underline || strike_through || color != -1 || italic > 0F; + } + } + + public WordContext getEffect() + { + return changes.get(0); + } + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/renderer/lexer/TextLexer.java b/src/main/java/speiger/src/coreengine/rendering/gui/renderer/lexer/TextLexer.java new file mode 100644 index 0000000..0eafe3f --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/renderer/lexer/TextLexer.java @@ -0,0 +1,128 @@ +package speiger.src.coreengine.rendering.gui.renderer.lexer; + +import java.util.List; +import java.util.Locale; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.utils.ObjectLists; +import speiger.src.coreengine.rendering.gui.renderer.FontRenderer; +import speiger.src.coreengine.rendering.gui.renderer.IFontRenderer.CharInstance; +import speiger.src.coreengine.utils.helpers.TextUtil; + +public class TextLexer +{ + FontRenderer render; + List helper = new ObjectArrayList(); + + public TextLexer(FontRenderer render) + { + this.render = render; + } + + public void getWords(String text, TextContext context, List words) + { + text = render.clearInvalidLetters(text); + Word current = new Word(context.getScale()); + for(int i = 0,j=0,m=text.length();i', FontRenderer.INVALID_SEARCH); + if(search.length() > 0) + { + context.apply(search.toLowerCase(Locale.ROOT).split(","), j--); + i += search.length()+2; + continue; + } + } + WordType type = WordType.getByLetter(letter); + if(current.getType() != type || type != null && type.isNewLine()) + { + if(!current.isEmpty()) + { + words.add(current); + current = new Word(current); + } + current.setSpecail(type); + } + current.addLetter(render.getInstance(letter, context.isBold())); + } + if(!current.isEmpty()) + { + words.add(current); + } + } + + public List evaluateLines(String text, TextContext context, float maxWidth) + { + if(text == null || text.isEmpty() || maxWidth < 10F * context.getScale()) + { + return ObjectLists.empty(); + } + List lines = new ObjectArrayList(); + getWords(text, context, helper); + Line line = new Line(maxWidth); + for(int i = 0;i maxLength) + { + helper.add(index++, newWord); + newWord = new Word(newWord); + } + newWord.addLetter(letter); + } + if(newWord.size() > 0) + { + helper.add(index++, newWord); + if(word.getNext() != null) + { + word.getNext().setPrev(newWord); + } + } + else if(word.getNext() != null) + { + word.getNext().setPrev(newWord.getPrev()); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/gui/renderer/lexer/TextMetadata.java b/src/main/java/speiger/src/coreengine/rendering/gui/renderer/lexer/TextMetadata.java new file mode 100644 index 0000000..71d1cbd --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/gui/renderer/lexer/TextMetadata.java @@ -0,0 +1,158 @@ +package speiger.src.coreengine.rendering.gui.renderer.lexer; + +import java.util.List; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.ints.Vec2i; +import speiger.src.coreengine.rendering.gui.components.TextComponent; + +public class TextMetadata +{ + TextComponent owner; + List lines = new ObjectArrayList(); + float startX; + float startY; + float maxHeight; + float maxWidth = 0F; + float scale; + + public TextMetadata(TextComponent owner) + { + this.owner = owner; + } + + public void clear() + { + scale = owner.getTextScale(); + lines.clear(); + maxWidth = 0F; + maxHeight = 0F; + startX = 0F; + startY = 0F; + } + + public void addLine(Line line) + { + lines.add(line); + maxWidth = Math.max(maxWidth, line.getWidth()); + maxHeight = lines.size() * owner.getFont().getFontHeight() * scale; + } + + public void setStart(float x, float y) + { + startX = x; + startY = y; + } + + public float getStartX() + { + return startX; + } + + public float getStartY() + { + return startY; + } + + public float getMaxHeight() + { + return maxHeight; + } + + public float getMaxWidth() + { + return maxWidth; + } + + public float getWidth(int letterIndex) + { + return lines.isEmpty() ? 0F : lines.get(0).getWidth(letterIndex); + } + + public float getWidth(int posX, int posY) + { + return lines.size() > posY ? lines.get(posY).getWidth(posX) : 0F; + } + + public float getLineWidth(int line) + { + return lines.size() > line ? lines.get(line).getWidth() : 0F; + } + + public Word getWord(int letterIndex) + { + return lines.isEmpty() ? null : lines.get(0).getWord(letterIndex); + } + + public Line getLine(int line) + { + return lines.size() > line ? lines.get(line) : null; + } + + public int moveUp(Vec2i pos) + { + if(pos.getY() + 1 < lines.size()) + { + float width = lines.get(pos.getY()).getWidth(pos.getX()); + return lines.get(pos.getY() + 1).getIndex(width, true); + } + return lines.isEmpty() ? 0 : lines.get(lines.size() - 1).getEnd(); + } + + public int moveDown(Vec2i pos) + { + if(pos.getY() > 0 && pos.getY() - 1 < lines.size()) + { + float width = lines.get(pos.getY()).getWidth(pos.getX()); + return lines.get(pos.getY() - 1).getIndex(width, true); + } + return lines.isEmpty() ? 0 : lines.get(0).getStart(); + } + + public int getIndex(float width) + { + return lines.size() > 0 ? lines.get(0).getIndex(width, false) : 0; + } + + public int getIndex(float width, float height) + { + return lines.isEmpty() ? 0 : lines.get(MathUtils.clamp(0, lines.size() - 1, (int)(height / (owner.getFont().getFontHeight() * scale)))).getIndex(width, true); + } + + public void getIndex(float width, float height, Vec2i result) + { + if(lines.isEmpty()) + { + result.negate(); + return; + } + int index = MathUtils.clamp(0, lines.size() - 1, (int)(height / (owner.getFont().getFontHeight() * scale))); + result.set(lines.get(index).getIndex(width, false), index); + } + + public void convert(int index, Vec2i result) + { + result.set(Vec2i.ZERO); + for(int i = 0,m=lines.size();i= index) + { + result.add(index, i); + return; + } + index -= line.size(); + } + } + + public int convert(Vec2i input) + { + int index = input.getX(); + for(int i = 0,m=input.getY();i letters = new ObjectArrayList(); + int startIndex; + Word prev; + Word next; + WordType special = null; + float width; + WordContext context; + + public Word(float scale) + { + this.scale = scale; + } + + public Word(Word parent) + { + prev = parent; + if(parent == null) + { + scale = 1F; + return; + } + parent.next = this; + startIndex = parent.getEndIndex(); + scale = parent.scale; + } + + void setPrev(Word parent) + { + prev = parent; + parent.next = this; + startIndex = parent.getEndIndex(); + } + + public void addLetter(CharInstance letter) + { + if(letter == null) + { + return; + } + letters.add(letter); + width += letter.getXAdvance() * scale; + } + + public Word getNext() + { + return next; + } + + public Word getPrev() + { + return prev; + } + + public int getStartIndex() + { + return startIndex; + } + + public int getEndIndex() + { + return startIndex + letters.size(); + } + + public boolean isEmpty() + { + return letters.isEmpty(); + } + + public int size() + { + return letters.size(); + } + + public String getLetters() + { + StringBuilder builder = new StringBuilder(); + for(int i = 0,m=letters.size();i= letters.size()) return width; + float result = 0F; + for(int i = 0;i instanceIterator() + { + return IterableWrapper.wrap(letters.iterator()); + } + + ObjectIterator objectInstanceIterator() + { + return letters.iterator(); + } + + public int getIndex(float width) + { + for(int i = 0,m=letters.size();i', 1), + MORE_THEN('>', '<', 2), + RIGHT_BRACKET('[', ']', 1), + LEFT_BRACKET(']', '[', 2), + SPACE(' ', 3), + LINE_SEPERATOR('\n', 3); + + static final int START_WORD = 1; + static final int END_WORD = 2; + static final int DUAL_WORD = 3; + static final Char2ObjectMap MAPPER; + + char letter; + Character opposite; + int flag; + + private WordType(char letter, char opposite, int flag) + { + this.letter = letter; + this.opposite = Character.valueOf(opposite); + this.flag = flag; + } + + private WordType(char letter, int flag) + { + this.letter = letter; + this.flag = flag; + } + + public char getLetter() + { + return letter; + } + + public boolean isStartWord() + { + return flag == START_WORD; + } + + public boolean isEndWord() + { + return flag == END_WORD; + } + + public boolean isDualWord() + { + return flag == DUAL_WORD; + } + + public boolean isSpace() + { + return this == SPACE; + } + + public boolean isNewLine() + { + return this == LINE_SEPERATOR; + } + + public boolean isOpposite(WordType letter) + { + return (opposite == null && letter == this) || (opposite != null && opposite.charValue() == letter.letter); + } + + public Word findStartWord(Word word) + { + Word currentWord = word; + while(currentWord.getPrev() != null) + { + currentWord = currentWord.getPrev(); + if(isOpposite(currentWord.getType())) + { + return currentWord; + } + } + return null; + } + + public Word findEndWord(Word word) + { + Word currentWord = word; + while(currentWord.getNext() != null) + { + currentWord = currentWord.getNext(); + if(isOpposite(currentWord.getType())) + { + return currentWord; + } + } + return null; + } + + public static WordType getByLetter(char letter) + { + return MAPPER.get(letter); + } + + static + { + MAPPER = new Char2ObjectOpenHashMap(); + for(WordType type : WordType.values()) + { + MAPPER.put(type.letter, type); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/DropListener.java b/src/main/java/speiger/src/coreengine/rendering/input/DropListener.java new file mode 100644 index 0000000..6b1dc0b --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/DropListener.java @@ -0,0 +1,38 @@ +package speiger.src.coreengine.rendering.input; + +import java.io.File; + +import org.lwjgl.glfw.GLFW; +import org.lwjgl.glfw.GLFWDropCallback; + +import speiger.src.coreengine.rendering.input.events.FileDropEvent; +import speiger.src.coreengine.rendering.input.window.ScaledResolution; +import speiger.src.coreengine.rendering.input.window.Window; +import speiger.src.coreengine.utils.eventbus.EventBus; + +public class DropListener +{ + EventBus bus; + ScaledResolution resolution; + + public DropListener(Window window, EventBus bus) + { + window.addCallback(T -> GLFW.glfwSetDropCallback(T, this::onFileDropped)); + resolution = window.getUIFrame(); + this.bus = bus; + } + + public void onFileDropped(long windowId, int count, long names) + { + int x = Mouse.getX(); + int y = Mouse.getY(); + for(int i = 0;i filter; + + public void init(EventBus bus, Window window) + { + this.bus = bus; + window.addCallback(T -> GLFW.glfwSetKeyCallback(T, this::onKeyTyped)); + window.addCallback(T -> GLFW.glfwSetCharCallback(T, this::onCharTyped)); + } + + public static void setFilter(BiPredicate filter) + { + INSTANCE.filter = filter; + } + + public static void clearFilter() + { + setFilter(null); + } + + protected void onKeyTyped(long window, int key, int scancode, int action, int mods) + { + if(key > 350 || key < 0) + { + return; + } + if(action >= 1) + { + pressedKeys[key] = true; + onKeyPressed(key); + } + else + { + pressedKeys[key] = false; + InputBinding.BINDINGS.updatePressing(BindingType.KEYBOARD, key, false); + } + } + + public void onKeyPressed(int key) + { + if(filter == null || filter.test(key, this)) + { + KeyPressEvent event = new KeyPressEvent(key); + bus.post(event); + if(event.isCanceled()) + { + return; + } + InputBinding.BINDINGS.updatePressing(BindingType.KEYBOARD, key, true); + } + } + + public void onCharTyped(long windowId, int codepoint) + { + bus.post(new CharTypeEvent((char)codepoint, codepoint)); + } + + public static boolean isCtrlDown() + { + return isKeyPressed(GLFW.GLFW_KEY_LEFT_CONTROL) || isKeyPressed(GLFW.GLFW_KEY_RIGHT_CONTROL); + } + + public static boolean isShiftDown() + { + return isKeyPressed(GLFW.GLFW_KEY_LEFT_SHIFT) || isKeyPressed(GLFW.GLFW_KEY_RIGHT_SHIFT); + } + + public static boolean isAltDown() + { + return isKeyPressed(GLFW.GLFW_KEY_LEFT_ALT) || isKeyPressed(GLFW.GLFW_KEY_RIGHT_ALT); + } + + public static boolean isPrintableKey(int key) + { + return key <= 162; + } + + public static boolean isKeyPressed(int key) + { + return INSTANCE.isPressed(key); + } + + public boolean isPressed(int key) + { + return pressedKeys[key]; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/Mouse.java b/src/main/java/speiger/src/coreengine/rendering/input/Mouse.java new file mode 100644 index 0000000..576c61e --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/Mouse.java @@ -0,0 +1,127 @@ +package speiger.src.coreengine.rendering.input; + +import org.lwjgl.glfw.GLFW; + +import speiger.src.collections.ints.sets.IntLinkedOpenHashSet; +import speiger.src.collections.ints.sets.IntSet; +import speiger.src.coreengine.math.vector.ints.Vec2i; +import speiger.src.coreengine.rendering.input.bindings.InputBinding; +import speiger.src.coreengine.rendering.input.bindings.utils.BindingType; +import speiger.src.coreengine.rendering.input.camera.Camera; +import speiger.src.coreengine.rendering.input.events.MouseEvent; +import speiger.src.coreengine.rendering.input.events.MouseEvent.MouseClickEvent; +import speiger.src.coreengine.rendering.input.events.MouseEvent.MouseMoveEvent; +import speiger.src.coreengine.rendering.input.events.MouseEvent.MouseScrollEvent; +import speiger.src.coreengine.rendering.input.window.Window; +import speiger.src.coreengine.utils.eventbus.EventBus; + +public class Mouse +{ + public static final Mouse INSTANCE = new Mouse(); + + MouseRay ray; + EventBus bus; + IntSet pressedButton = new IntLinkedOpenHashSet(); + Vec2i position = Vec2i.newMutable(); + Vec2i movement = Vec2i.newMutable(); + Vec2i scroll = Vec2i.newMutable(); + boolean active = true; + + public void init(EventBus bus, Window window, Camera cam) + { + this.bus = bus; + ray = new MouseRay(cam, window); + window.addCallback(T -> GLFW.glfwSetCursorPosCallback(T, Mouse.this::onDrag)); + window.addCallback(T -> GLFW.glfwSetMouseButtonCallback(T, Mouse.this::onClick)); + window.addCallback(T -> GLFW.glfwSetScrollCallback(T, Mouse.this::onScroll)); + window.addCallback(T -> GLFW.glfwSetCursorEnterCallback(T, (K, V) -> active = V)); + } + + protected void onDrag(long window, double xpos, double ypos) + { + int xChange = (int)xpos - position.getX(); + int yChange = (int)ypos - position.getY(); + position.set((int)xpos, (int)ypos); + pushEvent(new MouseMoveEvent(position.getX(), position.getY(), xChange, yChange)); + } + + protected void onScroll(long window, double xScroll, double yScroll) + { + pushEvent(new MouseScrollEvent(position.getX(), position.getY(), (int)xScroll, (int)yScroll)); + } + + protected void onClick(long window, int button, int action, int mods) + { + if(action == 1) pressedButton.add(button); + else pressedButton.remove(button); + pushEvent(new MouseClickEvent(position.getX(), position.getY(), button, action == 1)); + } + + public static void update() + { + INSTANCE.scroll.negate(); + INSTANCE.movement.negate(); + INSTANCE.ray.update(); + } + + public static boolean isButtonPressed(int button) + { + return INSTANCE.pressedButton.contains(button); + } + + public static int getX() + { + return INSTANCE.position.getX(); + } + + public static int getY() + { + return INSTANCE.position.getY(); + } + + public static Vec2i getMovement() + { + return INSTANCE.movement; + } + + public static boolean isActive() + { + return INSTANCE.active; + } + + public static MouseRay getRayCast() + { + return INSTANCE.ray; + } + + public Vec2i getScroll() + { + return scroll; + } + + public void pushEvent(MouseEvent event) + { + if(active) + { + bus.post(event); + if(!event.isCanceled() || event.isForced()) + { + if(event instanceof MouseMoveEvent) + { + MouseMoveEvent move = (MouseMoveEvent)event; + movement.add(move.xMove, move.yMove); + } + else if(event instanceof MouseClickEvent) + { + MouseClickEvent click = (MouseClickEvent)event; + InputBinding.BINDINGS.updatePressing(BindingType.MOUSE, click.button, click.press); + } + else if(event instanceof MouseScrollEvent) + { + MouseScrollEvent scroll = (MouseScrollEvent)event; + this.scroll.add(scroll.scrollX, scroll.scrollY); + } + } + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/MouseRay.java b/src/main/java/speiger/src/coreengine/rendering/input/MouseRay.java new file mode 100644 index 0000000..b854f81 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/MouseRay.java @@ -0,0 +1,76 @@ +package speiger.src.coreengine.rendering.input; + +import speiger.src.coreengine.math.vector.floats.Vec3f; +import speiger.src.coreengine.math.vector.floats.Vec4f; +import speiger.src.coreengine.math.vector.ints.Vec2i; +import speiger.src.coreengine.math.vector.matrix.Matrix4f; +import speiger.src.coreengine.rendering.input.camera.Camera; +import speiger.src.coreengine.rendering.input.raycast.Ray; +import speiger.src.coreengine.rendering.input.window.IWindowListener; +import speiger.src.coreengine.rendering.input.window.Window; + +public class MouseRay implements IWindowListener +{ + final static Vec2i INVALID = Vec2i.MINUS_ONE; + final static Vec3f INVALID_3D = Vec3f.MINUS_ONE; + Camera cam; + Matrix4f windowMatrix; + Vec2i windowBounds = Vec2i.newMutable(); + Vec3f direction = Vec3f.newVec(); + Vec4f helper = Vec4f.newMutable(); + + public MouseRay(Camera cam, Window window) + { + this.cam = cam; + window.addListener(this, true); + } + + @Override + public void onWindowChanged(Window window) + { + windowMatrix = window.getInvertedProjectionMatrix(); + windowBounds.set(window.getWidth(), window.getHeight()); + } + + public void update() + { + if(Mouse.isActive()) + { + direction = calculateMouseRay(); + } + else + { + direction = Vec3f.ZERO; + } + } + + public Vec3f getDirection() + { + return direction; + } + + public Ray getRayCast(float maxReach) + { + return new Ray(cam.getPosition(), cam.getPosition().asImmutable().add(direction.getX() * maxReach, direction.getY() * maxReach, direction.getZ() * maxReach)); + } + +// private int[] createViewPort() +// { +// return GLUtils.VIEW_PORT.getCurrent().asArray(); +// } +// +// private Vec3f calculateWindowRay() +// { +// int[] viewPort = createViewPort(); +// Matrix4f mat = GameManager.INSTANCE.getProjectionBuffer().getProViewMatrix(); +// return mat.unproject(Mouse.getX(), viewPort[3] - Mouse.getY(), 1F, viewPort, Vec3f.newMutable()).sub(mat.unproject(Mouse.getX(), viewPort[3] - Mouse.getY(), 0F, viewPort, Vec3f.newMutable())).normalize(); +// } + + private Vec3f calculateMouseRay() + { + helper.set(2F * Mouse.getX() / windowBounds.getX() - 1.0f, 1F - 2F * Mouse.getY() / windowBounds.getY(), -1F, 1F); + windowMatrix.transform(helper).setZ(-1F).setW(0F); + cam.getInvertedViewMatrix().transform(helper); + return Vec3f.newVec(helper.getX(), helper.getY(), helper.getZ()).normalize(); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/bindings/Axis.java b/src/main/java/speiger/src/coreengine/rendering/input/bindings/Axis.java new file mode 100644 index 0000000..9486cf5 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/bindings/Axis.java @@ -0,0 +1,173 @@ +package speiger.src.coreengine.rendering.input.bindings; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.lwjgl.glfw.GLFW; + +import speiger.src.coreengine.rendering.input.Mouse; +import speiger.src.coreengine.rendering.input.bindings.utils.BindingType; + +public class Axis +{ + static final Map AXIS_MAP = new LinkedHashMap(); + public static final IAxisSource MAIN_SCROLL_WEEL = new ScrollAxis(false); + public static final IAxisSource SECOND_SCROLL_WEEL = new ScrollAxis(true); + public static final Axis XMOVEMENT = new Axis("XMovement", BindingType.KEYBOARD, GLFW.GLFW_KEY_S, GLFW.GLFW_KEY_W); + public static final Axis STRAVE = new Axis("Strave", BindingType.KEYBOARD, GLFW.GLFW_KEY_D, GLFW.GLFW_KEY_A); + public static final Axis YAW = new Axis("Yaw", BindingType.KEYBOARD, GLFW.GLFW_KEY_E, GLFW.GLFW_KEY_Q); + public static final Axis PITCH = new Axis("Pitch", BindingType.KEYBOARD, GLFW.GLFW_KEY_X, GLFW.GLFW_KEY_C); + public static final Axis ZOOM = new Axis("Zoom", MAIN_SCROLL_WEEL); + + + String name; + IAxisSource source; + + public Axis(String name) + { + this(name, null); + } + + public Axis(String name, BindingType type, int pos, int neg) + { + this(name, new KeySource(type, neg, pos)); + } + + public Axis(String id, IAxisSource provider) + { + name = id; + source = provider; + AXIS_MAP.put(id, this); + } + + public void setSource(BindingType type, int pos, int neg) + { + setSource(new KeySource(type, neg, pos)); + } + + public void setSource(IAxisSource provider) + { + source = provider; + } + + public static Axis getAxis(String name) + { + return AXIS_MAP.get(name); + } + + public IAxisSource getSource() + { + return source; + } + + public float getValue() + { + if(source != null) + { + return source.getValue(); + } + return 0F; + } + + public String getName() + { + return name; + } + + public static interface IAxisSource + { + public float getValue(); + + public boolean isButton(); + + public String getButtonName(boolean first); + + public String getAxisName(); + + public boolean isAlive(); + } + + public static class ScrollAxis implements IAxisSource + { + boolean first; + + public ScrollAxis(boolean data) + { + first = data; + } + + @Override + public float getValue() + { + return first ? Mouse.INSTANCE.getScroll().getX() : Mouse.INSTANCE.getScroll().getY(); + } + + @Override + public boolean isAlive() + { + return true; + } + + @Override + public boolean isButton() + { + return false; + } + + @Override + public String getButtonName(boolean first) + { + return "Not Present"; + } + + @Override + public String getAxisName() + { + return "ScrollWeel "+(first ? 0 : 1); + } + } + + public static class KeySource implements IAxisSource + { + BindingType type; + int neg; + int pos; + + public KeySource(BindingType type, int neg, int pos) + { + this.type = type; + this.neg = neg; + this.pos = pos; + } + + @Override + public float getValue() + { + return (type.isPressed(pos) ? 1F : 0F) - (type.isPressed(neg) ? 1F : 0F); + } + + @Override + public boolean isButton() + { + return true; + } + + @Override + public boolean isAlive() + { + return true; + } + + @Override + public String getButtonName(boolean first) + { + return type.getName(first ? pos : neg); + } + + @Override + public String getAxisName() + { + return "Not Present"; + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/bindings/InputBinding.java b/src/main/java/speiger/src/coreengine/rendering/input/bindings/InputBinding.java new file mode 100644 index 0000000..a9aab3b --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/bindings/InputBinding.java @@ -0,0 +1,170 @@ +package speiger.src.coreengine.rendering.input.bindings; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.function.Consumer; + +import speiger.src.coreengine.rendering.input.bindings.utils.BindingRegistry; +import speiger.src.coreengine.rendering.input.bindings.utils.BindingType; +import speiger.src.coreengine.rendering.input.bindings.utils.ModType; + +public class InputBinding +{ + public static final BindingRegistry BINDINGS = new BindingRegistry(); +// public static final InputBinding SCREEN_SHOT = new InputBinding("misc", "Screenshot", GLFW.GLFW_KEY_F12, ModType.NONE); +// public static final InputBinding GIF_RECORDING = new InputBinding("misc", "GifRecording", GLFW.GLFW_KEY_F12, ModType.SHIFT); +// public static final InputBinding RELOAD_RENDERING = new InputBinding("misc", "Reload Rendering", GLFW.GLFW_KEY_F8); +// public static final InputBinding TOGGLE_UI = new InputBinding("UI", "ToggleUI", GLFW.GLFW_KEY_F11); +// public static final InputBinding TOGGLE_DEBUG = new InputBinding("UI", "ToggleDebug", GLFW.GLFW_KEY_F10, ModType.NONE); +// public static final InputBinding TOGGLE_RELOAD = new InputBinding("UI", "ToggleUIReload", GLFW.GLFW_KEY_F10, ModType.SHIFT); +// public static final InputBinding LAST_UI = new InputBinding("UI", "OpenLastUI", GLFW.GLFW_KEY_F9); + public static final InputBinding MOUSE_ROTATION = new InputBinding("Camera", "Mouse Rotation", 2, BindingType.MOUSE, ModType.IGNORE); + public static final InputBinding MOUSE_MOVEMENT = new InputBinding("Camera", "Mouse Movement", 1, BindingType.MOUSE, ModType.IGNORE); +// public static final InputBinding TOGGLE_GRID = new InputBinding("misc", "Toggle Terrain Grid", GLFW.GLFW_KEY_F2, ModType.NONE); +// public static final InputBinding TOGGLE_LOG_RAYCAST = new InputBinding("misc", "Toggle Log Raycast", GLFW.GLFW_KEY_SCROLL_LOCK); +// public static final InputBinding TOGGLE_FRUSTUM = new InputBinding("debug", "Toggle Frustum", GLFW.GLFW_KEY_PAUSE); + + String category; + String bindingName; + BindingType binding; + int mod; + int currentKey; + + BindingType defaultBinding; + int defaultMod; + int defaultKey; + + boolean pressed; + int timePressed; + List> listeners = new ArrayList>(); + + public InputBinding(String category, String name, int key) + { + this(category, name, key, BindingType.KEYBOARD, ModType.IGNORE); + } + + public InputBinding(String category, String name, int key, BindingType type) + { + this(category, name, key, type, ModType.IGNORE); + } + + public InputBinding(String category, String name, int key, int mod) + { + this(category, name, key, BindingType.KEYBOARD, mod); + } + + public InputBinding(String theCategory, String name, int key, BindingType type, int modType) + { + category = theCategory; + bindingName = name; + defaultKey = key; + currentKey = key; + defaultBinding = type; + binding = type; + defaultMod = modType; + mod = modType; + BINDINGS.registerBinding(this); + } + + public String getBindingName() + { + return bindingName; + } + + public int getKey() + { + return currentKey; + } + + public BindingType getInputType() + { + return binding; + } + + public int getMod() + { + return mod; + } + + public void setKey(int id) + { + BINDINGS.removeBinding(this); + currentKey = id; + BINDINGS.registerBinding(this); + } + + public void setMod(int type) + { + mod = type; + } + + public void setBinding(BindingType type) + { + BINDINGS.removeBinding(this); + binding = type; + BINDINGS.registerBinding(this); + } + + public void addListener(Consumer listener) + { + listeners.add(listener); + } + + public void reset() + { + BINDINGS.removeBinding(this); + currentKey = defaultKey; + mod = defaultMod; + binding = defaultBinding; + BINDINGS.registerBinding(this); + } + + public void setPressed(boolean newPressed) + { + pressed = newPressed && ModType.isActive(mod); + timePressed = pressed ? timePressed + 1 : 0; + if(pressed && listeners.size() > 0) + { + for(int i = 0,m=listeners.size();i>> handlers = new EnumMap>>(BindingType.class); + + public void registerBinding(InputBinding binding) + { + Int2ObjectMap> bindings = handlers.get(binding.getInputType()); + if(bindings == null) + { + bindings = new Int2ObjectLinkedOpenHashMap>(); + handlers.put(binding.getInputType(), bindings); + } + Set entries = bindings.get(binding.getKey()); + if(entries == null) + { + entries = new ObjectLinkedOpenHashSet(); + bindings.put(binding.getKey(), entries); + } + entries.add(binding); + } + + public void removeBinding(InputBinding binding) + { + Int2ObjectMap> bindings = handlers.get(binding.getInputType()); + if(bindings == null) + { + return; + } + Set entries = bindings.get(binding.getKey()); + if(entries.remove(binding) && entries.isEmpty()) + { + bindings.remove(binding.getKey()); + if(bindings.isEmpty()) + { + handlers.remove(binding.getInputType()); + } + } + } + + public void updatePressing(BindingType type, int key, boolean pressed) + { + Int2ObjectMap> bindings = handlers.get(type); + if(bindings == null) + { + return; + } + for(InputBinding binding : bindings.getOrDefault(key, Collections.emptySet())) + { + binding.setPressed(pressed); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/bindings/utils/BindingType.java b/src/main/java/speiger/src/coreengine/rendering/input/bindings/utils/BindingType.java new file mode 100644 index 0000000..7a9a0b7 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/bindings/utils/BindingType.java @@ -0,0 +1,59 @@ +package speiger.src.coreengine.rendering.input.bindings.utils; + +import org.lwjgl.glfw.GLFW; + +import speiger.src.coreengine.rendering.input.Keyboard; +import speiger.src.coreengine.rendering.input.Mouse; + +public enum BindingType +{ + KEYBOARD + { + @Override + public String getName(int key) + { + if(key >= GLFW.GLFW_KEY_F1 && key <= GLFW.GLFW_KEY_F25) return "F"+(key - GLFW.GLFW_KEY_F1+1); + else if(key == GLFW.GLFW_KEY_DELETE) return "Delete"; + String s = GLFW.glfwGetKeyName(key, GLFW.glfwGetKeyScancode(key)); + return s == null ? "Unkonwn" : s.toUpperCase(); + } + + @Override + public boolean isPressed(int key) + { + return Keyboard.isKeyPressed(key); + } + }, + MOUSE + { + @Override + public String getName(int key) + { + return "Button: "+key; + } + + @Override + public boolean isPressed(int key) + { + return Mouse.isButtonPressed(key); + } + }, + NONE + { + @Override + public String getName(int key) + { + return "None"; + } + + @Override + public boolean isPressed(int key) + { + return false; + } + }; + + public abstract String getName(int key); + + public abstract boolean isPressed(int key); +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/bindings/utils/ModType.java b/src/main/java/speiger/src/coreengine/rendering/input/bindings/utils/ModType.java new file mode 100644 index 0000000..a5fc46f --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/bindings/utils/ModType.java @@ -0,0 +1,36 @@ +package speiger.src.coreengine.rendering.input.bindings.utils; + +import java.util.StringJoiner; + +import speiger.src.coreengine.rendering.input.Keyboard; + +public final class ModType +{ + public static final int NONE = 0; + public static final int CTRL = 1; + public static final int SHIFT = 2; + public static final int ALT = 4; + public static final int IGNORE = 8; + + public static boolean isActive(int flags) + { + boolean ignore = (flags & IGNORE) != 0; + return valid(Keyboard.isCtrlDown(), (flags & CTRL) != 0, ignore) && valid(Keyboard.isShiftDown(), (flags & SHIFT) != 0, ignore) && valid(Keyboard.isAltDown(), (flags & ALT) != 0, ignore); + } + + public static String getMods(int flags) + { + flags &= ~IGNORE; + if(flags == 0) return ""; + StringJoiner builder = new StringJoiner(" + ", "", " + "); + if((flags & CTRL) != 0) builder.add("Ctrl"); + if((flags & SHIFT) != 0) builder.add("Shift"); + if((flags & ALT) != 0) builder.add("Alt"); + return builder.toString(); + } + + static boolean valid(boolean binding, boolean flags, boolean ignore) + { + return (flags == binding) || (!flags && ignore); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/camera/Camera.java b/src/main/java/speiger/src/coreengine/rendering/input/camera/Camera.java new file mode 100644 index 0000000..7716655 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/camera/Camera.java @@ -0,0 +1,206 @@ +package speiger.src.coreengine.rendering.input.camera; + +import java.text.DecimalFormat; +import java.util.List; +import java.util.function.Consumer; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.math.MathUtils; +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.math.vector.floats.Vec3f; +import speiger.src.coreengine.math.vector.ints.Vec2i; +import speiger.src.coreengine.math.vector.ints.Vec3i; +import speiger.src.coreengine.math.vector.matrix.Matrix4f; +import speiger.src.coreengine.rendering.input.window.Window; + +public class Camera +{ + static final Vec3f X_ROTATION = Vec3f.newVec(1, 0, 0); + static final Vec3f Y_ROTATION = Vec3f.newVec(0, 1, 0); + + Vec3f position = Vec3f.newMutable(0, 0, 0); + Vec3f lastPosition = Vec3f.newMutable(); + Vec2f rotation = Vec2f.newMutable(0); + Vec2f lastRotation = Vec2f.newMutable(); + boolean moved = false; + boolean rotated = false; + + Matrix4f viewMatrix = createViewMatrix(); + Matrix4f invertedViewMatrix = new Matrix4f(viewMatrix).invert(); + + ICameraController controller; + Frustrum frustrum; + boolean changed = false; + List> listeners = new ObjectArrayList>(); + + public Camera(Window window) + { + frustrum = new Frustrum(this); + window.addListener(frustrum, false); + setController(controller); + } + + public void setController(ICameraController cont) + { + cont = cont == null ? DefaultCameraController.INSTANCE : cont; + if(controller != null) + { + controller.onRemoved(this); + } + controller = cont; + cont.onAdded(this); + } + + public void addListener(Consumer listner) + { + listeners.add(listner); + } + + public void onInput() + { + controller.onInputUpdate(this); + } + + public void update(float particalTicks) + { + controller.updateCamera(this, particalTicks); + moved = !lastPosition.equals(position); + rotated = !rotation.equals(lastRotation); + if(rotated || moved) + { + viewMatrix = createViewMatrix(); + invertedViewMatrix.load(viewMatrix).invert(); + lastPosition.set(position); + lastRotation.set(rotation); + frustrum.update(); + changed = true; + for(Consumer listener : listeners) + { + listener.accept(this); + } + } + else + { + changed = false; + } + } + + public Matrix4f createViewMatrix() + { + Matrix4f viewMatrix = new Matrix4f(); + viewMatrix.rotate((float)Math.toRadians(rotation.getX()), X_ROTATION); + viewMatrix.rotate((float)Math.toRadians(rotation.getY()), Y_ROTATION); + viewMatrix.translate(position.copy().invert()); + return viewMatrix; + } + + public boolean isChanged() + { + return changed; + } + + public boolean wasMoved() + { + return moved; + } + + public boolean wasRotated() + { + return rotated; + } + + public Matrix4f getViewMatrix() + { + return viewMatrix; + } + + public Matrix4f getInvertedViewMatrix() + { + return invertedViewMatrix; + } + + public Frustrum getFrustrum() + { + return frustrum; + } + + public ICameraController getController() + { + return controller; + } + + public T getController(Class clz) + { + return (T)controller; + } + + public Vec3f getPosition() + { + return position; + } + + public Vec3f getLookDirection() + { + float f = MathUtils.cos(-rotation.getY() * 0.017453292F - (float)Math.PI); + float f1 = MathUtils.sin(-rotation.getY() * 0.017453292F - (float)Math.PI); + float f2 = -MathUtils.cos(-rotation.getX() * 0.017453292F); + float f3 = MathUtils.sin(-rotation.getX() * 0.017453292F); + return Vec3f.newVec(f1 * f2, f3, f * f2); + } + + public Camera setCameraChanged() + { + changed = true; + return this; + } + + public void setPosition(Vec3f vec, boolean update) + { + position.set(vec); + if(update) + { + controller.onPositionUpdate(this); + } + } + + public Vec2i getActualCenter(){return controller.getCenter(this);} + public Vec2i getSolidPosition(){return Vec2i.newVec((int)position.getX(), (int)position.getZ());} + + public void rotate(Vec2i vec){rotate(vec.getY(), vec.getX());} + public void rotate(Vec2f vec){rotate(vec.getY(), vec.getX());} + public void rotate(float x, float y){rotation.add(x, y);} + + public void move(Vec3f vec){move(vec.getX(), vec.getY(), vec.getZ());} + public void move(Vec3i vec){move(vec.getX(), vec.getY(), vec.getZ());} + public void move(float x, float y, float z){position.add(x, y, z);} + + public void moveRotated(float x, float y, float z) + { + if(z != 0) + { + position.add(MathUtils.sin(Math.toRadians(getYaw())) * -1.0f * z, 0, MathUtils.cos(Math.toRadians(getYaw())) * z); + } + if(x != 0) + { + position.add(MathUtils.sin(Math.toRadians(getYaw() - 90)) * -1.0f * x, 0, MathUtils.cos(Math.toRadians(getYaw() - 90)) * x); + } + if(y != 0) + { + position.add(0F, y, 0F); + } + } + + public Vec2f getRotation(){return rotation;} + public float getYaw(){return rotation.getY();} + public void setYaw(float value){rotation.setY(value);} + + public float pitch(){return rotation.getX();} + public void setPitch(float value){rotation.setX(value);} + + @Override + public String toString() + { + DecimalFormat format = new DecimalFormat("###,###"); + return "Camera[x="+format.format(position.getX())+", y="+format.format(position.getY())+", z="+format.format(position.getZ())+", Yaw="+format.format(getYaw())+", Pitch="+format.format(pitch())+"]"; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/camera/DefaultCameraController.java b/src/main/java/speiger/src/coreengine/rendering/input/camera/DefaultCameraController.java new file mode 100644 index 0000000..8f8049c --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/camera/DefaultCameraController.java @@ -0,0 +1,18 @@ +package speiger.src.coreengine.rendering.input.camera; + +import speiger.src.coreengine.math.vector.ints.Vec2i; + +public enum DefaultCameraController implements ICameraController +{ + INSTANCE; + + @Override + public Vec2i getCenter(Camera cam){return cam.getSolidPosition();} + @Override + public void onInputUpdate(Camera camera){} + @Override + public void updateCamera(Camera camera, float particalTicks){} + @Override + public void onPositionUpdate(Camera camera){} + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/camera/Frustrum.java b/src/main/java/speiger/src/coreengine/rendering/input/camera/Frustrum.java new file mode 100644 index 0000000..79a4012 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/camera/Frustrum.java @@ -0,0 +1,127 @@ +package speiger.src.coreengine.rendering.input.camera; + +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.math.vector.floats.Vec3f; +import speiger.src.coreengine.math.vector.floats.Vec4f; +import speiger.src.coreengine.math.vector.matrix.Matrix4f; +import speiger.src.coreengine.rendering.input.window.IWindowListener; +import speiger.src.coreengine.rendering.input.window.Window; +import speiger.src.coreengine.utils.helpers.InternalThreadPools; + +public class Frustrum implements IWindowListener +{ + public static boolean FREEZE_FRUSTUM = false; + Camera cam; + Matrix4f projectMatrix = new Matrix4f(); + Vec4f[] frustrumPlanes = new Vec4f[6]; + + public Frustrum(Camera camera) + { + cam = camera; + projectMatrix = new Matrix4f(); + for(int i = 0;i<6;i++) + { + frustrumPlanes[i] = Vec4f.newMutable(); + } + } + + @Override + public void onWindowChanged(Window window) + { + projectMatrix.load(window.getProjectionMatrix()); + update(); + } + + public void update() + { + if(FREEZE_FRUSTUM) + { + return; + } + Matrix4f checkMatrix = InternalThreadPools.MATRIX.get().load(projectMatrix).multiply(cam.getViewMatrix()); + for(int i = 0;i<6;i++) + { + checkMatrix.storeFrustrumPlane(i, frustrumPlanes[i]); + } + InternalThreadPools.MATRIX.accept(checkMatrix); + } + + public boolean isPointInFrustrum(Vec3f vec) + { + return isPointInFrustrum(vec.getX(), vec.getY(), vec.getZ()); + } + + public boolean isPointInFrustrum(float x, float y, float z) + { + return isSphereInFrustrum(x, y, z, 0); + } + + public boolean isSphereInFrustrum(Vec3f vec, float radius) + { + return isSphereInFrustrum(vec.getX(), vec.getY(), vec.getZ(), radius); + } + + public boolean isSphereInFrustrum(float x, float y, float z, float radius) + { + for(int i = 0;i<6;i++) + { + if(getDistance(i, x, y, z) < -radius) + { + return false; + } + } + return true; + } + + public boolean isCubeInFrustrum(Vec3f min, Vec3f max) + { + return isCubeInFrustrum(min.getX(), min.getY(), min.getZ(), max.getX(), max.getY(), max.getZ()); + } + + public boolean isCubeInFrustrum(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) + { + for(int i = 0;i<6;i++) + { + if(isOutsideOfPlane(i, minX, minY, minZ, maxX, maxY, maxZ)) + { + return false; + } + } + return true; + } + + public boolean isPlaneInFrustrum(Vec2f min, Vec2f max) + { + return isPlaneInFrustrum(min.getX(), min.getY(), max.getX(), min.getY()); + } + + public boolean isPlaneInFrustrum(float minX, float minZ, float maxX, float maxZ) + { + for(int i = 0;i<6;i++) + { + if(isOutsideOfPlane(i, minX, minZ, maxX, maxZ)) + { + return false; + } + } + return true; + } + + private float getDistance(int index, float x, float y, float z) + { + Vec4f vec = frustrumPlanes[index]; + return vec.getX() * x + vec.getY() * y + vec.getZ() * z + vec.getW(); + } + + private boolean isOutsideOfPlane(int index, float minX, float minZ, float maxX, float maxZ) + { + Vec4f vec = frustrumPlanes[index]; + return vec.getX() * (vec.getX() < 0 ? minX : maxX) + vec.getZ() * (vec.getZ() < 0 ? minZ : maxZ) >= -vec.getW(); + } + + private boolean isOutsideOfPlane(int index, float minX, float minY, float minZ, float maxX, float maxY, float maxZ) + { + Vec4f vec = frustrumPlanes[index]; + return vec.getX() * (vec.getX() < 0 ? minX : maxX) + vec.getY() * (vec.getY() < 0 ? minY : maxY) + vec.getZ() * (vec.getZ() < 0 ? minZ : maxZ) < -vec.getW(); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/camera/ICameraController.java b/src/main/java/speiger/src/coreengine/rendering/input/camera/ICameraController.java new file mode 100644 index 0000000..d17d7df --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/camera/ICameraController.java @@ -0,0 +1,14 @@ +package speiger.src.coreengine.rendering.input.camera; + +import speiger.src.coreengine.math.vector.ints.Vec2i; + +public interface ICameraController +{ + public Vec2i getCenter(Camera cam); + public void onInputUpdate(Camera camera); + public void updateCamera(Camera camera, float particalTicks); + public void onPositionUpdate(Camera camera); + public default void onAdded(Camera cam){}; + public default void onRemoved(Camera cam){}; + public default float getZoom(){return 0F;} +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/rendering/input/events/FileDropEvent.java b/src/main/java/speiger/src/coreengine/rendering/input/events/FileDropEvent.java new file mode 100644 index 0000000..3b5f028 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/events/FileDropEvent.java @@ -0,0 +1,42 @@ +package speiger.src.coreengine.rendering.input.events; + +import java.io.File; + +import speiger.src.coreengine.rendering.input.window.ScaledResolution; + +public class FileDropEvent extends MouseEvent +{ + final File file; + final String fileName; + final String fileExtension; + + public FileDropEvent(File file, int x, int y, ScaledResolution res) + { + super(x, y); + this.file = file; + fileName = file.getName(); + fileExtension = fileName.substring(fileName.lastIndexOf(".")+1); + convertToScreenCoords(res); + } + + public String getFileExtension() + { + return fileExtension; + } + + public String getFileName() + { + return fileName; + } + + public File getFile() + { + return file; + } + + @Override + public boolean isCancelable() + { + return true; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/events/KeyEvent.java b/src/main/java/speiger/src/coreengine/rendering/input/events/KeyEvent.java new file mode 100644 index 0000000..8434429 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/events/KeyEvent.java @@ -0,0 +1,34 @@ +package speiger.src.coreengine.rendering.input.events; + +import speiger.src.coreengine.utils.eventbus.Event; + +public abstract class KeyEvent extends Event +{ + @Override + public boolean isCancelable() + { + return true; + } + + public static class KeyPressEvent extends KeyEvent + { + public final int key; + + public KeyPressEvent(int key) + { + this.key = key; + } + } + + public static class CharTypeEvent extends KeyEvent + { + public final char character; + public final int codePoint; + + public CharTypeEvent(char character, int codePoint) + { + this.character = character; + this.codePoint = codePoint; + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/events/MouseEvent.java b/src/main/java/speiger/src/coreengine/rendering/input/events/MouseEvent.java new file mode 100644 index 0000000..c519297 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/events/MouseEvent.java @@ -0,0 +1,88 @@ +package speiger.src.coreengine.rendering.input.events; + +import speiger.src.coreengine.rendering.input.window.ScaledResolution; +import speiger.src.coreengine.utils.eventbus.Event; + +public class MouseEvent extends Event +{ + public int mouseX; + public int mouseY; + public final int originX; + public final int originY; + + public MouseEvent(int x, int y) + { + mouseX = x; + mouseY = y; + originX = x; + originY = y; + } + + public void convertToOrigin() + { + mouseX = originX; + mouseY = originY; + } + + public void convertToScreenCoords(ScaledResolution res) + { + res.getScaledMouseEvent(this); + } + + @Override + public boolean isCancelable() + { + return true; + } + + public boolean isForced() + { + return false; + } + + public static class MouseClickEvent extends MouseEvent + { + public final int button; + public final boolean press; + + public MouseClickEvent(int x, int y, int button, boolean press) + { + super(x, y); + this.button = button; + this.press = press; + } + + //Forced so Buttons are always released. Maybe gets disabled. Not sure yet + @Override + public boolean isForced() + { + return !press; + } + } + + public static class MouseMoveEvent extends MouseEvent + { + public final int xMove; + public final int yMove; + + public MouseMoveEvent(int x, int y, int xMove, int yMove) + { + super(x, y); + this.xMove = xMove; + this.yMove = yMove; + } + } + + public static class MouseScrollEvent extends MouseEvent + { + public final int scrollX; + public final int scrollY; + + public MouseScrollEvent(int x, int y, int scrollX, int scrollY) + { + super(x, y); + this.scrollX = scrollX; + this.scrollY = scrollY; + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/raycast/Ray.java b/src/main/java/speiger/src/coreengine/rendering/input/raycast/Ray.java new file mode 100644 index 0000000..d520b66 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/raycast/Ray.java @@ -0,0 +1,35 @@ +package speiger.src.coreengine.rendering.input.raycast; + +import speiger.src.coreengine.math.vector.floats.Vec3f; + +public class Ray +{ + protected Vec3f start; + protected Vec3f end; + + public Ray(Vec3f start, Vec3f end) + { + this.start = start.asImmutable(); + this.end = end.asImmutable(); + } + + public Vec3f getStart() + { + return start; + } + + public Vec3f getEnd() + { + return end; + } + + public Vec3f interpolate(float progress) + { + return start.lerp(end, progress, Vec3f.ZERO); + } + + public Vec3f distance() + { + return end.asMutable().sub(start); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/raycast/RayCollisions.java b/src/main/java/speiger/src/coreengine/rendering/input/raycast/RayCollisions.java new file mode 100644 index 0000000..15bb8d4 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/raycast/RayCollisions.java @@ -0,0 +1,44 @@ +package speiger.src.coreengine.rendering.input.raycast; + +import speiger.src.coreengine.math.vector.floats.Vec3f; + +public class RayCollisions +{ + public static RayResult intersectQuad(Ray ray, Vec3f first, Vec3f second, Vec3f third, Vec3f fourth) + { + RayResult result = intersectTriangle(ray, first, second, third); + return result != null ? result : intersectTriangle(ray, first, fourth, third); + } + + public static RayResult intersectTriangle(Ray ray, Vec3f first, Vec3f second, Vec3f third) + { + first = first.asImmutable(); + second = second.asImmutable(); + third = third.asImmutable(); + RayResult result = intersectPlane(ray, first, second, third); + if(result == null) return null; + Vec3f hit = result.getHit(); + Vec3f cross = second.sub(first).crossProduct(third.sub(first), Vec3f.ZERO); + if(cross.dotProduct(second.sub(first).crossProduct(hit.sub(first))) > 0) + { + if(cross.dotProduct(third.sub(second).crossProduct(hit.sub(second))) > 0) + { + if(cross.dotProduct(first.sub(third).crossProduct(hit.sub(third))) > 0) + { + return result; + } + } + } + return null; + } + + static RayResult intersectPlane(Ray ray, Vec3f first, Vec3f second, Vec3f third) + { + Vec3f cross = second.sub(first).crossProduct(third.sub(first), Vec3f.ZERO); + Vec3f distance = ray.distance(); + double hitDot = distance.dotProduct(cross); + if(Math.abs(hitDot) < 0.0000000001D) return null; + double step = first.sub(ray.getStart()).dotProduct(cross) / hitDot; + return new RayResult(ray, ray.interpolate((float)step), step); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/raycast/RayResult.java b/src/main/java/speiger/src/coreengine/rendering/input/raycast/RayResult.java new file mode 100644 index 0000000..fad7d23 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/raycast/RayResult.java @@ -0,0 +1,38 @@ +package speiger.src.coreengine.rendering.input.raycast; + +import speiger.src.coreengine.math.vector.floats.Vec3f; + +public class RayResult +{ + Ray ray; + Vec3f hit; + double distance; + public Object subData; + + public RayResult(Ray ray, Vec3f hit, double distance) + { + this.ray = ray; + this.hit = hit; + this.distance = distance; + } + + public double getDistance() + { + return distance; + } + + public Vec3f getHit() + { + return hit; + } + + public Ray getRay() + { + return ray; + } + + public T getSubData(Class clz) + { + return subData != null && clz.isInstance(subData) ? clz.cast(subData) : null; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/window/IWindowListener.java b/src/main/java/speiger/src/coreengine/rendering/input/window/IWindowListener.java new file mode 100644 index 0000000..26f6913 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/window/IWindowListener.java @@ -0,0 +1,7 @@ +package speiger.src.coreengine.rendering.input.window; + + +public interface IWindowListener +{ + public void onWindowChanged(Window window); +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/window/Monitor.java b/src/main/java/speiger/src/coreengine/rendering/input/window/Monitor.java new file mode 100644 index 0000000..87cb36e --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/window/Monitor.java @@ -0,0 +1,110 @@ +package speiger.src.coreengine.rendering.input.window; + +import java.util.List; + +import org.lwjgl.PointerBuffer; +import org.lwjgl.glfw.GLFW; +import org.lwjgl.glfw.GLFWVidMode; + +import speiger.src.collections.longs.maps.impl.hash.Long2ObjectOpenHashMap; +import speiger.src.collections.longs.maps.interfaces.Long2ObjectMap; +import speiger.src.collections.objects.lists.ObjectArrayList; + +public class Monitor +{ + final long monitor; + List modes = new ObjectArrayList(); + VideoMode defaultMode; + int xOffset; + int yOffset; + + public Monitor(long monitor) + { + this.monitor = monitor; + GLFWVidMode.Buffer buffer = GLFW.glfwGetVideoModes(monitor); + for(int i = buffer.limit() - 1;i >= 0;--i) + { + buffer.position(i); + VideoMode videomode = new VideoMode(this, buffer); + if(videomode.getRedBits() >= 8 && videomode.getGreenBits() >= 8 && videomode.getBlueBits() >= 8) + { + modes.add(videomode); + } + } + defaultMode = new VideoMode(this, GLFW.glfwGetVideoMode(monitor)); + int[] xPos = new int[1]; + int[] yPos = new int[1]; + GLFW.glfwGetMonitorPos(monitor, xPos, yPos); + xOffset = xPos[0]; + yOffset = yPos[0]; + } + + public String getMonitorName() + { + return GLFW.glfwGetMonitorName(monitor); + } + + public long getMonitorId() + { + return monitor; + } + + public int size() + { + return modes.size(); + } + + public VideoMode getMode(int index) + { + return modes.get(index); + } + + public boolean contains(VideoMode mode) + { + return modes.indexOf(mode) != -1; + } + + public List getVideoMods() + { + return new ObjectArrayList(modes); + } + + public VideoMode getDefaultMode() + { + return defaultMode; + } + + public int getXOffset() + { + return xOffset; + } + + public int getYOffset() + { + return yOffset; + } + + public long getInBounds(Window window) + { + if(xOffset <= window.getXPos() + window.getActualWidth() && xOffset + defaultMode.getWidth() >= window.getXPos() && yOffset <= window.getYPos() + window.getActualHeight() && yOffset + defaultMode.getHeight() >= window.getYPos()) + { + int xArea = Math.abs(window.getActualWidth() * defaultMode.getWidth()); + int yArea = Math.abs(window.getActualHeight() * defaultMode.getHeight()); + int areaI = (Math.min(window.getXPos() + window.getActualWidth(), xOffset + defaultMode.getWidth()) - Math.max(window.getXPos(), xOffset)) * (Math.min(window.getYPos() + window.getActualHeight(), yOffset + defaultMode.getHeight()) - Math.max(window.getYPos(), yOffset)); + return (xArea + yArea) - areaI; + } + return 0L; + } + + public static Long2ObjectMap createMonitors() + { + Long2ObjectMap monitors = new Long2ObjectOpenHashMap(); + PointerBuffer buffer = GLFW.glfwGetMonitors(); + for(int i = 0,m=buffer.limit();i callbacks = new ObjectArrayList(); + List listeners = new ObjectArrayList(); + GLCapabilities capabilities; + + public Window() + { + GLFW.glfwDefaultWindowHints(); + GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, 0); // 0 = False, 1 = true + GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, 1); + GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, 4); + GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 0); + GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE);//GLFW.GLFW_OPENGL_COMPAT_PROFILE); + GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_FORWARD_COMPAT, 1); + } + + protected void initWindowListeners() + { + addCallback(T -> GLFW.glfwSetFramebufferSizeCallback(T, this::onResize)); + addCallback(T -> GLFW.glfwSetWindowMaximizeCallback(T, (K, V) -> flags.setFlag(FLAG_WINDOW_CHANGE))); + addCallback(T -> GLFW.glfwSetWindowPosCallback(T, (W, X, Y) -> setPosition(X, Y))); + addCallback(T -> GLFW.glfwSetWindowFocusCallback(T, (K, V) -> flags.setFlag(FLAG_FOCUS, V))); + } + + Window createWindow(WindowStats stat) + { + owner = stat.provider; + title = stat.name; + width = stat.width; + height = stat.height; + aaLevel = stat.antiAlis; + videoMode = stat.videoMode; + flags.setFlag(FLAG_BORDERLESS, stat.borderless); + flags.setFlag(FLAG_FULL_SCREEN, stat.fullScreen); + flags.setFlag(FLAG_FLOATING, stat.floating); + flags.setFlag(FLAG_VSYNC, stat.vsync); + flags.setFlag(FLAG_CPU_FPS_CAP, stat.fpsCap); + GLFW.glfwWindowHint(GLFW.GLFW_SAMPLES, aaLevel); + GLFW.glfwWindowHint(GLFW.GLFW_DECORATED, flags.isFlagNotSet(FLAG_FULL_SCREEN) && flags.isFlagSet(FLAG_BORDERLESS) ? 0 : 1); + GLFW.glfwWindowHint(GLFW.GLFW_FLOATING, flags.isFlagNotSet(FLAG_FULL_SCREEN) && flags.isFlagSet(FLAG_FLOATING) ? 1 : 0); + if(videoMode == null || videoMode.getMonitor() == null) + { + throw new IllegalStateException("Monitor or Video Mode is missing: "+videoMode); + } + Monitor monitor = videoMode.getMonitor(); + windowId = GLFW.glfwCreateWindow(flags.isFlagSet(FLAG_FULL_SCREEN) ? videoMode.getWidth() : width, flags.isFlagSet(FLAG_FULL_SCREEN) ? videoMode.getHeight() : height, title, flags.isFlagSet(FLAG_FULL_SCREEN) ? monitor.getMonitorId() : 0L, stat.parent == null ? 0 : stat.parent.windowId); + if(windowId == 0) + { + throw new IllegalStateException("Window Couldn't be Created"); + } + initWindowListeners(); + GLFW.glfwMakeContextCurrent(windowId); + if(capabilities == null) + { + capabilities = GL.createCapabilities(); + } + if(flags.isFlagNotSet(FLAG_FULL_SCREEN)) + { + xPos = stat.center ? (monitor.getXOffset() + (videoMode.getWidth() / 2) - (width / 2)) : monitor.getXOffset() + xPos; + yPos = stat.center ? (monitor.getYOffset() + (videoMode.getHeight() / 2) - (width / 2)) : monitor.getYOffset() + yPos; + GLFW.glfwSetWindowPos(windowId, xPos, yPos); + } + else + { + xPos = monitor.getXOffset() + (videoMode.getWidth() / 2) - (width / 2); + yPos = monitor.getYOffset() + (videoMode.getHeight() / 2) - (width / 2); + tempWidth = width; + tempHeight = height; + } + GLFW.glfwSwapInterval(flags.isFlagSet(FLAG_VSYNC) ? 1 : 0); + fetchWindowBounds(); + updateViewPort(); + return this; + } + + protected void onResize(long window, int newWidth, int newHeight) + { + if(flags.isFlagNotSet(FLAG_FULL_SCREEN | FLAG_MAXIMIZED) && window == windowId && (width != newWidth || height != newHeight)) + { + width = newWidth; + height = newHeight; + flags.setFlag(FLAG_WINDOW_CHANGE); + } + } + + protected void updateCallbacks() + { + for(int i = 0,m=callbacks.size();i provider) + { + if(provider == null) + { + return null; + } + WindowCallback callback = new WindowCallback(provider); + callbacks.add(callback); + if(windowId != 0) + { + callback.reload(windowId); + } + return callback; + } + + public void removeCallback(WindowCallback call) + { + if(callbacks.remove(call)) + { + call.destroy(); + } + } + + private void createProjectionMatrix() + { +// float aspectRatio = (float)width / (float)height; +// float y_scale = (float)((1f / Math.tan(Math.toRadians(fieldOfView / 2f))) * aspectRatio); +// float x_scale = y_scale / aspectRatio; +// float frustum_length = far_plane - near_plane; +// projection.setIdentity(); +// projection.set(0, x_scale); +// projection.set(5, y_scale); +// projection.set(10, -((far_plane + near_plane) / frustum_length)); +// projection.set(11, -1); +// projection.set(14, -((2 * near_plane * far_plane) / frustum_length)); +// projection.set(15, 0); +// invertedProjection.load(projection).invert(); +// + projection.setIdentity().setPerspective((float)Math.toRadians(fieldOfView), (float)width / (float)height, near_plane, far_plane); + invertedProjection.load(projection).invert(); + } + + protected void fetchWindowBounds() + { + int[] width = new int[1]; + int[] height = new int[1]; + GLFW.glfwGetWindowSize(windowId, width, height); + this.width = width[0]; + this.height = height[0]; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/window/WindowCallback.java b/src/main/java/speiger/src/coreengine/rendering/input/window/WindowCallback.java new file mode 100644 index 0000000..8365d8c --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/window/WindowCallback.java @@ -0,0 +1,30 @@ +package speiger.src.coreengine.rendering.input.window; + +import java.util.function.LongFunction; + +import org.lwjgl.system.Callback; + +public class WindowCallback +{ + Callback callback; + LongFunction provider; + + public WindowCallback(LongFunction provider) + { + this.provider = provider; + } + + public void reload(long windowID) + { + callback = provider.apply(windowID); + } + + public void destroy() + { + if(callback != null) + { + callback.free(); + callback = null; + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/input/window/WindowProvider.java b/src/main/java/speiger/src/coreengine/rendering/input/window/WindowProvider.java new file mode 100644 index 0000000..ce72431 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/input/window/WindowProvider.java @@ -0,0 +1,228 @@ +package speiger.src.coreengine.rendering.input.window; + +import java.util.List; + +import org.lwjgl.glfw.GLFW; +import org.lwjgl.system.Callback; + +import speiger.src.collections.longs.maps.interfaces.Long2ObjectMap; +import speiger.src.collections.objects.lists.ObjectArrayList; + +public class WindowProvider +{ + Long2ObjectMap monitors; + List callbacks = new ObjectArrayList<>(); + List activeWindows = new ObjectArrayList<>(); + + public void init() + { + monitors = Monitor.createMonitors(); + callbacks.add(GLFW.glfwSetMonitorCallback(this::onMonitorChange)); + } + + protected void onMonitorChange(long id, int state) + { + switch(state) + { + case GLFW.GLFW_CONNECTED: + monitors.put(id, new Monitor(id)); + break; + case GLFW.GLFW_DISCONNECTED: + monitors.remove(state); + break; + } + } + + public WindowStats createBuilder() + { + return new WindowStats(this); + } + + public Monitor getMonitors(long id) + { + return monitors.get(id); + } + + public List getActiveWindows() + { + return activeWindows; + } + + public Monitor getMonitorFromWindow(Window window) + { + Monitor monitor = null; + long overlap = Long.MIN_VALUE; + for(Monitor entry : monitors.values()) + { + long thisOverlap = entry.getInBounds(window); + if(thisOverlap > overlap) + { + monitor = entry; + overlap = thisOverlap; + } + } + return monitor == null ? monitors.get(GLFW.glfwGetPrimaryMonitor()) : monitor; + } + + public List getMonitors() + { + return new ObjectArrayList(monitors.values()); + } + + public void destroy() + { + for(int i = 0,m=callbacks.size();i 0) + { + this.width = width; + } + return this; + } + + public WindowStats setHeight(int height) + { + if(height > 0) + { + this.height = height; + } + return this; + } + + public WindowStats setAntiAlis(int antiAlis) + { + if(antiAlis >= 0) + { + this.antiAlis = antiAlis; + } + return this; + } + + public WindowStats setSubWindow(Window parent) + { + this.parent = parent; + return this; + } + + public WindowStats setFullScreen(boolean fullScreen) + { + this.fullScreen = fullScreen; + if(fullScreen) + { + borderless = false; + floating = false; + } + return this; + } + + public WindowStats setBorderless(boolean borderless) + { + this.borderless = borderless; + if(borderless) + { + fullScreen = false; + } + return this; + } + + public WindowStats setFloating(boolean floating) + { + this.floating = floating; + if(floating) + { + fullScreen = false; + } + return this; + } + + public WindowStats setCentered(boolean center) + { + this.center = center; + return this; + } + + public WindowStats setMonitor(VideoMode videoMode) + { + if(videoMode != null && videoMode.getMonitor() != null) + { + this.videoMode = videoMode; + } + return this; + } + + public WindowStats setVsync(boolean value) + { + vsync = value; + return this; + } + + public WindowStats setFPSCap(boolean cap) + { + fpsCap = cap; + return this; + } + + public Window build() + { + Window window = new Window().createWindow(this); + provider.activeWindows.add(window); + return window; + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/models/BufferAttribute.java b/src/main/java/speiger/src/coreengine/rendering/models/BufferAttribute.java new file mode 100644 index 0000000..33e2d7d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/models/BufferAttribute.java @@ -0,0 +1,86 @@ +package speiger.src.coreengine.rendering.models; + +import org.lwjgl.opengl.GL20; +import org.lwjgl.opengl.GL33; + +public class BufferAttribute +{ + public final int index; + public final int size; + public final GLDataType type; + public final boolean normal; + public final int instancedAmount; + + public BufferAttribute(int index, int size) + { + this(index, size, GLDataType.FLOAT, false, 0); + } + + public BufferAttribute(int index, int size, GLDataType type) + { + this(index, size, type, false, 0); + } + + public BufferAttribute(int index, int size, GLDataType type, boolean normal) + { + this(index, size, type, normal, 0); + } + + public BufferAttribute(int index, int size, GLDataType type, int instancedAmount) + { + this(index, size, type, false, instancedAmount); + } + + public BufferAttribute(int index, int size, GLDataType type, boolean normal, int instancedAmount) + { + this.index = index; + this.size = size; + this.type = type; + this.normal = normal; + this.instancedAmount = instancedAmount; + } + + public static BufferAttribute[] createArrayAttribute(int startIndex, int width, int size, GLDataType type) + { + return createArrayAttribute(startIndex, width, size, type, false, 0); + } + + public static BufferAttribute[] createArrayAttribute(int startIndex, int width, int size, GLDataType type, boolean normal) + { + return createArrayAttribute(startIndex, width, size, type, normal, 0); + } + + public static BufferAttribute[] createArrayAttribute(int startIndex, int width, int size, GLDataType type, int instanceAmount) + { + return createArrayAttribute(startIndex, width, size, type, false, instanceAmount); + } + + public static BufferAttribute[] createArrayAttribute(int startIndex, int width, int size, GLDataType type, boolean normal, int instancedAmount) + { + BufferAttribute[] attributes = new BufferAttribute[width]; + for(int i = 0;i 0) + { + GL33.glVertexAttribDivisor(index, instancedAmount); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/models/DrawCall.java b/src/main/java/speiger/src/coreengine/rendering/models/DrawCall.java new file mode 100644 index 0000000..9ce94bd --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/models/DrawCall.java @@ -0,0 +1,88 @@ +package speiger.src.coreengine.rendering.models; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.List; + +public class DrawCall +{ + final int glType; + final int textureId; + final byte[] data; + final int vertexCount; + + public DrawCall(int glType, int textureId, float[] data, int vertexCount) + { + this.glType = glType; + this.textureId = textureId; + this.vertexCount = vertexCount; + this.data = new byte[data.length*4]; + ByteBuffer.wrap(this.data).order(ByteOrder.nativeOrder()).asFloatBuffer().put(data); + } + + public DrawCall(int glType, int textureId, byte[] data, int vertexCount) + { + this.glType = glType; + this.textureId = textureId; + this.data = data; + this.vertexCount = vertexCount; + } + + public byte[] getData() + { + return data; + } + + public int getGLType() + { + return glType; + } + + public int getTextureId() + { + return textureId; + } + + public int getVertexCount() + { + return vertexCount; + } + + public static byte[] merge(List drawCalls) + { + int total = 0; + for(int i = 0,m=drawCalls.size();i currentCapacity) + { + if(currentCapacity == limit) + { + throw new RuntimeException("THIS SHOULD NEVER HAPPEN!: "+currentCapacity+":"+limit); + } + int old = currentCapacity; + currentCapacity = Math.min(Math.max(currentCapacity + (currentCapacity / 2), capacity), limit); + buffer.bind().grow(old * bytesPerIndex, currentCapacity * bytesPerIndex, "Growable Vertex Buffer").unbind(); + } + else if(capacity < currentCapacity / 2 && currentCapacity != 10) + { + int old = currentCapacity; + currentCapacity = Math.max(10, capacity); + buffer.bind().shrink(old * bytesPerIndex, currentCapacity * bytesPerIndex, "Growable Vertex Buffer").unbind(); + } + return buffer; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/models/MultiVertexBuffer.java b/src/main/java/speiger/src/coreengine/rendering/models/MultiVertexBuffer.java new file mode 100644 index 0000000..6bbcda1 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/models/MultiVertexBuffer.java @@ -0,0 +1,99 @@ +package speiger.src.coreengine.rendering.models; + +public class MultiVertexBuffer +{ + VertexBuffer buffer; + final int bytesPerIndex; + final int limit; + final int startSize; + final float growRate; + final int padding; + final int[][] offsets; + int currentCapacity; + + public MultiVertexBuffer(VertexBuffer buffer, int bytesPerIndex, int limit, int startSize, int padding, float growRate, int[][] offsets) + { + super(); + this.buffer = buffer; + this.bytesPerIndex = bytesPerIndex; + this.limit = limit; + this.startSize = startSize; + this.padding = padding; + this.growRate = growRate; + this.offsets = offsets; + currentCapacity = startSize; + buffer.bind().allocateMemory(startSize * bytesPerIndex).unbind(); + } + + public boolean ensureSize(int index, int capacity) + { + int[] offset = offsets[index]; + int room = offset[2] - offset[0]; + if(capacity >= room) + { + int newCap = Math.max((int)(room * growRate), capacity+1); + offset[2] = offset[0] + newCap; + grow(index+1, newCap - room); + return true; + } +// else if(capacity < room * growRate && capacity != padding) +// { +// int newSize = Math.max((int)(room * growRate), padding); +// offset[2] = offset[0] + newSize; +// shrink(index+1, room - newSize); +// return true; +// } + return false; + } + + protected void grow(int index, int extra) + { + if(index == offsets.length) + { + int newCap = offsets[offsets.length-1][2]+extra; + if(newCap > currentCapacity) + { + if(currentCapacity == limit) + { + throw new RuntimeException("THIS SHOULD NEVER HAPPEN!: "+currentCapacity+":"+limit); + } + int old = currentCapacity; + currentCapacity = Math.min(Math.max(currentCapacity + (currentCapacity / 2), newCap), limit); + buffer.bind().grow(old * bytesPerIndex, currentCapacity * bytesPerIndex, "Multi Vertex Buffer").unbind(); + } + return; + } + int startBytes = offsets[index][0]*bytesPerIndex; + int endBytes = offsets[offsets.length-1][2]*bytesPerIndex; + for(int i = index;i attributes = new ArrayList(); + VertexBuffer indeciesBuffer; + List buffers = new ArrayList(); + + public VertexArray() + { + arrayID = GL30.glGenVertexArrays(); + } + + public abstract int getVertexCount(); + + protected void bind() + { + GL30.glBindVertexArray(arrayID); + } + + public void bindArray() + { + bind(); + for(BufferAttribute attr : attributes) + { + attr.bind(); + } + } + + protected BufferAttribute getAttribute(int index) + { + return attributes.get(index); + } + + protected void unbind() + { + GL30.glBindVertexArray(0); + } + + public void unbindArray() + { + for(BufferAttribute attr : attributes) + { + attr.unbind(); + } + unbind(); + } + + public int getArrayID() + { + return arrayID; + } + + public boolean isValid() + { + return arrayID != -1; + } + + public boolean isInvalid() + { + return arrayID == -1; + } + + public void remove() + { + if(arrayID == -1) + { + return; + } + GL30.glDeleteVertexArrays(arrayID); + if(indeciesBuffer != null) + { + indeciesBuffer.delete(); + } + for(VertexBuffer buffer : buffers) + { + buffer.delete(); + } + arrayID = -1; + } + + public VertexBuffer getIndeciesBuffer() + { + return indeciesBuffer; + } + + public VertexBuffer getBuffer(int index) + { + return buffers.get(index); + } + + public void createIndenciesBuffer(int[] indencies) + { + indeciesBuffer = new VertexBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER); + indeciesBuffer.bind(); + indeciesBuffer.setBufferData(indencies); + } + + public void updateIndenciesBuffer(int[] indencies) + { + indeciesBuffer.bind(); + indeciesBuffer.setBufferData(indencies); + } + + public VertexBuffer createSimpleBuffer(int index, int size, GLDataType type) + { + VertexBuffer buffer = new VertexBuffer(GL15.GL_ARRAY_BUFFER); + buffers.add(buffer); + buffer.bind(); + BufferAttribute attribute = new BufferAttribute(index, size, type); + attributes.add(attribute); + finishAttributes(attribute); + return buffer; + } + + public VertexBuffer createBuffer(BufferAttribute...array) + { + VertexBuffer buffer = new VertexBuffer(GL15.GL_ARRAY_BUFFER); + buffers.add(buffer); + attributes.addAll(Arrays.asList(array)); + buffer.bind(); + finishAttributes(array); + return buffer; + } + + public void removeBuffer(int attr) + { + for(int i = 0;i supplier) + { + FloatBuffer buffer = MemoryUtil.memAllocFloat(neededFloats); + supplier.accept(buffer); + buffer.flip(); + AllocationTracker.INSTANCE.addGPUBytes(buffer.remaining() * 4L); + GL15.glBufferData(bufferType, buffer, bufferState); + MemoryUtil.memFree(buffer); + return this; + } + + public VertexBuffer setBufferDataInt(int neededInts, Consumer supplier) + { + IntBuffer buffer = MemoryUtil.memAllocInt(neededInts); + supplier.accept(buffer); + buffer.flip(); + AllocationTracker.INSTANCE.addGPUBytes(buffer.remaining() * 4L); + GL15.glBufferData(bufferType, buffer, bufferState); + MemoryUtil.memFree(buffer); + return this; + } + + public VertexBuffer setBufferDataByte(int neededBytes, Consumer supplier) + { + ByteBuffer buffer = MemoryUtil.memAlloc(neededBytes); + supplier.accept(buffer); + buffer.flip(); + AllocationTracker.INSTANCE.addGPUBytes(buffer.remaining()); + GL15.glBufferData(bufferType, buffer, bufferState); + MemoryUtil.memFree(buffer); + return this; + } + + public VertexBuffer fillExternalBuffer(FloatBuffer buffer, int floatOffset) + { + GL15.glBufferSubData(bufferType, floatOffset, buffer); + AllocationTracker.INSTANCE.addGPUBytes(buffer.remaining() * 4); + return this; + } + + public VertexBuffer fillExternalBuffer(IntBuffer buffer, int intOffset) + { + GL15.glBufferSubData(bufferType, intOffset, buffer); + AllocationTracker.INSTANCE.addGPUBytes(buffer.remaining() * 4); + return this; + } + + public VertexBuffer fillExternalBuffer(ByteBuffer buffer, int byteOffset) + { + GL15.glBufferSubData(bufferType, byteOffset, buffer); + AllocationTracker.INSTANCE.addGPUBytes(buffer.remaining()); + return this; + } + + public VertexBuffer fillBuffer(int byteOffset, float[] data) + { + ByteBuffer buffer = GL15.glMapBuffer(bufferType, GL15.GL_WRITE_ONLY); + buffer.position(byteOffset); + buffer.asFloatBuffer().put(data); + GL15.glUnmapBuffer(bufferType); + AllocationTracker.INSTANCE.addGPUBytes(data.length * 4L); + return this; + } + + public VertexBuffer fillBuffer(int byteOffset, int[] data) + { + ByteBuffer buffer = GL15.glMapBuffer(bufferType, GL15.GL_WRITE_ONLY); + buffer.position(byteOffset); + buffer.asIntBuffer().put(data); + GL15.glUnmapBuffer(bufferType); + AllocationTracker.INSTANCE.addGPUBytes(data.length * 4L); + return this; + } + + public VertexBuffer fillBuffer(int byteOffset, byte[] data) + { + ByteBuffer buffer = GL15.glMapBuffer(bufferType, GL15.GL_WRITE_ONLY); + buffer.position(byteOffset); + buffer.put(data); + GL15.glUnmapBuffer(bufferType); + AllocationTracker.INSTANCE.addGPUBytes(data.length); + return this; + } + + public VertexBuffer fillBuffer(List> data) + { + return fillBuffer(0, data); + } + + public VertexBuffer fillBuffer(int byteOffset, List> data) + { + if(data.isEmpty()) + { + return this; + } + long totalData = 0; + ByteBuffer buffer = GL15.glMapBuffer(bufferType, GL15.GL_WRITE_ONLY); + for(int i = 0,m=data.size();i entry = data.get(i); + buffer.position(entry.getIntKey()+byteOffset); + buffer.put(entry.getValue()); + totalData += entry.getValue().length; + } + buffer.flip(); + if(!GL15.glUnmapBuffer(bufferType)) + { + GameLog.info("Memory Corruption?"); + } + AllocationTracker.INSTANCE.addGPUBytes(totalData); + return this; + } + + public VertexBuffer fillBufferFloat(int byteOffset, int neededFloats, Consumer supplier) + { + FloatBuffer buffer = MemoryUtil.memAllocFloat(neededFloats); + supplier.accept(buffer); + buffer.flip(); + AllocationTracker.INSTANCE.addGPUBytes(buffer.remaining() * 4L); + GL15.glBufferSubData(bufferType, byteOffset, buffer); + MemoryUtil.memFree(buffer); + return this; + } + + public VertexBuffer fillBufferInt(int byteOffset, int neededInts, Consumer supplier) + { + IntBuffer buffer = MemoryUtil.memAllocInt(neededInts); + supplier.accept(buffer); + buffer.flip(); + AllocationTracker.INSTANCE.addGPUBytes(buffer.remaining() * 4L); + GL15.glBufferSubData(bufferType, byteOffset, buffer); + MemoryUtil.memFree(buffer); + return this; + } + + public VertexBuffer fillBufferByte(int byteOffset, int neededBytes, Consumer supplier) + { + ByteBuffer buffer = MemoryUtil.memAlloc(neededBytes); + supplier.accept(buffer); + buffer.flip(); + AllocationTracker.INSTANCE.addGPUBytes(buffer.remaining()); + GL15.glBufferSubData(bufferType, byteOffset, buffer); + MemoryUtil.memFree(buffer); + return this; + } + + public VertexBuffer readBufferData(FloatBuffer buffer, int floatsToRead) + { + GL15.nglGetBufferSubData(bufferType, 0, floatsToRead << 2, MemoryUtil.memAddress(buffer)); + buffer.flip(); + AllocationTracker.INSTANCE.addGPUBytes(floatsToRead * 4L); + return this; + } + + public VertexBuffer readBufferData(IntBuffer buffer, int intsToRead) + { + GL15.nglGetBufferSubData(bufferType, 0, intsToRead << 2, MemoryUtil.memAddress(buffer)); + buffer.flip(); + AllocationTracker.INSTANCE.addGPUBytes(intsToRead * 4L); + return this; + } + + public VertexBuffer readBufferData(ByteBuffer buffer, int bytesToRead) + { + GL15.nglGetBufferSubData(bufferType, 0, bytesToRead, MemoryUtil.memAddress(buffer)); + buffer.flip(); + AllocationTracker.INSTANCE.addGPUBytes(bytesToRead); + return this; + } + + public VertexBuffer grow(int oldByteSize, int newByteSize, String source) + { + ByteBuffer buffer = GL15.glMapBuffer(bufferType, GL15.GL_READ_ONLY); + if(oldByteSize != buffer.remaining()) + { + GameLog.info("Grow: [Expected="+oldByteSize+", Present="+buffer.remaining()+", source="+source+"]"); + } + ByteBuffer newCap = MemoryUtil.memAlloc(newByteSize); + MemoryUtil.memCopy(MemoryUtil.memAddress(buffer), MemoryUtil.memAddress(newCap), oldByteSize); + GL15.glUnmapBuffer(bufferType); + newCap.position(newByteSize); + newCap.flip(); + GL15.glBufferData(bufferType, newCap, bufferState); + MemoryUtil.memFree(newCap); + AllocationTracker.INSTANCE.addGPUBytes(oldByteSize + newByteSize); + return this; + } + + public VertexBuffer shrink(int oldByteSize, int newByteSize, String source) + { + ByteBuffer buffer = GL15.glMapBuffer(bufferType, GL15.GL_READ_WRITE); + if(oldByteSize != buffer.remaining()) + { + GameLog.info("Shrink: [Expected="+oldByteSize+", Present="+buffer.remaining()+", source="+source+"]"); + } + ByteBuffer newBuffer = MemoryUtil.memAlloc(newByteSize); + MemoryUtil.memCopy(MemoryUtil.memAddress(buffer), MemoryUtil.memAddress(newBuffer), newByteSize); + newBuffer.position(newByteSize); + newBuffer.flip(); + GL15.glUnmapBuffer(bufferType); + GL15.glBufferData(bufferType, newBuffer, bufferState); + MemoryUtil.memFree(newBuffer); + AllocationTracker.INSTANCE.addGPUBytes(oldByteSize + newByteSize); + return this; + } + + public VertexBuffer shiftData(int startBytes, int bytesLength, int byteShift, String source) + { + ByteBuffer buffer = GL15.glMapBuffer(bufferType, GL15.GL_READ_WRITE); + int min = Math.min(startBytes, startBytes + byteShift); + int max = Math.max(startBytes + bytesLength, startBytes + bytesLength + byteShift); + if(min < 0 || max > buffer.remaining()) + { + GameLog.info("Shift Aborted due to out of range [Expected Min="+min+", Max="+max+", Source="+source+"]"); + return this; + } + MemoryUtil.memCopy(MemoryUtil.memAddress(buffer, startBytes), MemoryUtil.memAddress(buffer, startBytes+byteShift), bytesLength); + GL15.glUnmapBuffer(bufferType); + AllocationTracker.INSTANCE.addGPUBytes(bytesLength + byteShift); + return this; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/models/frameBuffer/BufferAttachment.java b/src/main/java/speiger/src/coreengine/rendering/models/frameBuffer/BufferAttachment.java new file mode 100644 index 0000000..d0b32e5 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/models/frameBuffer/BufferAttachment.java @@ -0,0 +1,82 @@ +package speiger.src.coreengine.rendering.models.frameBuffer; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL30; + +public class BufferAttachment implements IFrameAttachment +{ + int id; + int format; + int type; + int samples; + + public BufferAttachment(int format) + { + this.format = format; + } + + public static BufferAttachment createRGBA() + { + return new BufferAttachment(GL11.GL_RGBA); + } + + public static BufferAttachment createRGB() + { + return new BufferAttachment(GL11.GL_RGB); + } + + public static BufferAttachment createDepth() + { + return new BufferAttachment(GL11.GL_DEPTH_COMPONENT); + } + + public static BufferAttachment createDepthStencil() + { + return new BufferAttachment(GL30.GL_DEPTH24_STENCIL8); + } + + @Override + public int getId() + { + return id; + } + + public void setBufferId(int id) + { + if(this.id != id) + { + this.id = id; + if(type != 0 && id != 0) + { + GL30.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, type, GL30.GL_RENDERBUFFER, id); + GL30.glBindRenderbuffer(GL30.GL_RENDERBUFFER, 0); + } + } + } + + @Override + public void init(int type, int width, int height, int samples) + { + + this.type = type; + this.samples = samples; + if(id == 0) + { + id = GL30.glGenRenderbuffers(); + } + GL30.glBindRenderbuffer(GL30.GL_RENDERBUFFER, id); + GL30.glRenderbufferStorageMultisample(GL30.GL_RENDERBUFFER, samples, format, width, height); + GL30.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, type, GL30.GL_RENDERBUFFER, id); + GL30.glBindRenderbuffer(GL30.GL_RENDERBUFFER, 0); + } + + @Override + public void delete() + { + if(id != 0) + { + GL30.glDeleteRenderbuffers(id); + } + } + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/models/frameBuffer/FrameBuffer.java b/src/main/java/speiger/src/coreengine/rendering/models/frameBuffer/FrameBuffer.java new file mode 100644 index 0000000..a65bf20 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/models/frameBuffer/FrameBuffer.java @@ -0,0 +1,221 @@ +package speiger.src.coreengine.rendering.models.frameBuffer; + +import java.util.List; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL30; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.rendering.utils.GLUtils; + +public class FrameBuffer +{ + int id; + int width; + int height; + int samples; + boolean finished = false; + List colors = new ObjectArrayList(); + IFrameAttachment depth; + IFrameAttachment stencil; + IFrameAttachment stencil_depth; + + public FrameBuffer(int width, int height, int samples) + { + this.width = width; + this.height = height; + this.samples = samples; + } + + public FrameBuffer addColorAttachment(IFrameAttachment attachment) + { + if(colors.size() > 32) + { + throw new IllegalStateException("Color Attachments are full"); + } + colors.add(attachment); + return this; + } + + public FrameBuffer setColorAttachment(int index, IFrameAttachment attachment) + { + if(colors.size() <= index) + { + throw new IllegalStateException("No Attachment Found!"); + } + colors.set(index, attachment); + return this; + } + + public FrameBuffer setDepthAttachment(IFrameAttachment attachment) + { + depth = attachment; + return this; + } + + public FrameBuffer finished() + { + if(finished) + { + return this; + } + finished = true; + id = GL30.glGenFramebuffers(); + GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, id); + for(int i = 0,m=colors.size();i +{ + Camera camera; + Window window; + UniformBuffer projectionMatrixBuffer; + UniformBuffer proViewMatrixBuffer; + UniformBuffer orthoViewMatrixBuffer; + final Matrix4f proViewMatrix = new Matrix4f(); + final Matrix4f orthoViewMatrix = new Matrix4f(); + + public ProjectionBuffer(Camera camera, Window window) + { + this.camera = camera; + this.window = window; + projectionMatrixBuffer = new UniformBuffer(true); + proViewMatrixBuffer = new UniformBuffer(true); + orthoViewMatrixBuffer = new UniformBuffer(true); + window.addListener(this, true); + camera.addListener(this); + } + + @Override + public void accept(Camera t) + { + proViewMatrix.load(window.getProjectionMatrix()).multiply(camera.getViewMatrix()); + proViewMatrixBuffer.bind().setBufferData(proViewMatrix.getData()).unbind(); + } + + @Override + public void onWindowChanged(Window window) + { + ScaledResolution res = window.getUIFrame(); + orthoViewMatrix.setIdentity().ortho(0, 0, res.getScaledWidth(), res.getScaledHeight(), 1000, -1000); + projectionMatrixBuffer.bind().setBufferData(window.getProjectionMatrix().getData()).unbind(); + orthoViewMatrixBuffer.bind().setBufferData(orthoViewMatrix.getData()).unbind(); + accept(camera); + } + + public UniformBuffer getProjectionMatrixBuffer() + { + return projectionMatrixBuffer; + } + + public UniformBuffer getProViewMatrixBuffer() + { + return proViewMatrixBuffer; + } + + public UniformBuffer getOrthoViewMatrixBuffer() + { + return orthoViewMatrixBuffer; + } + + public Matrix4f getProViewMatrix() + { + return proViewMatrix; + } + + public Matrix4f getOrthoViewMatrix() + { + return orthoViewMatrix; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/ShaderEntry.java b/src/main/java/speiger/src/coreengine/rendering/shader/ShaderEntry.java new file mode 100644 index 0000000..443adc2 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/ShaderEntry.java @@ -0,0 +1,23 @@ +package speiger.src.coreengine.rendering.shader; + +import java.util.List; +import java.util.function.Consumer; + +public class ShaderEntry implements Consumer +{ + List shaders; + int index; + + public ShaderEntry(List shaders, int index) + { + this.shaders = shaders; + this.index = index; + } + + @Override + public void accept(T t) + { + shaders.set(index, t); + } + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/ShaderProgram.java b/src/main/java/speiger/src/coreengine/rendering/shader/ShaderProgram.java new file mode 100644 index 0000000..2e30627 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/ShaderProgram.java @@ -0,0 +1,129 @@ +package speiger.src.coreengine.rendering.shader; + +import java.util.List; + +import org.lwjgl.opengl.GL20; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.assets.IAsset; +import speiger.src.coreengine.rendering.shader.ShaderTracker.ReloadReference; +import speiger.src.coreengine.rendering.shader.uniforms.Uniform; +import speiger.src.coreengine.utils.collections.iterators.IterableWrapper; +import speiger.src.coreengine.utils.io.GameLog; + +public abstract class ShaderProgram +{ + protected List localUniforms = new ObjectArrayList(); + ReloadReference reference; + private int shaderID; + + protected ShaderProgram(AssetLocation vertex, AssetLocation fragment, String[] attributes) + { + int vertexID = loadShader(vertex, GL20.GL_VERTEX_SHADER); + int fragmentID = loadShader(fragment, GL20.GL_FRAGMENT_SHADER); + if(vertexID == -1 || fragmentID == -1) + { + shaderID = 0; + return; + } + shaderID = GL20.glCreateProgram(); + GL20.glAttachShader(shaderID, vertexID); + GL20.glAttachShader(shaderID, fragmentID); + for(int i = 0;i < attributes.length;i++) + { + GL20.glBindAttribLocation(shaderID, i, attributes[i]); + } + GL20.glLinkProgram(shaderID); + GL20.glDetachShader(shaderID, vertexID); + GL20.glDetachShader(shaderID, fragmentID); + GL20.glDeleteShader(vertexID); + GL20.glDeleteShader(fragmentID); + } + + public void addUniforms(Uniform... uniforms) + { + for(Uniform uni : uniforms) + { + uni.loadUniform(this); + localUniforms.add(uni); + } + GL20.glValidateProgram(shaderID); + } + + void setReference(ReloadReference ref) + { + reference = ref; + } + + public abstract ShaderProgram init(); + + public boolean isShaderValid() + { + return shaderID != 0; + } + + public int getShaderID() + { + return shaderID; + } + + public void startShader() + { + ShaderTracker.INSTANCE.startNextShader(this); + } + + public void stopShader() + { + ShaderTracker.INSTANCE.stopShader(this); + } + + public boolean isShaderRunning() + { + return ShaderTracker.INSTANCE.activeShader == getShaderID(); + } + + public void removeShader() + { + if(shaderID == 0) + { + return; + } + stopShader(); + GL20.glDeleteProgram(shaderID); + shaderID = 0; + reference.removeReference(); + reference = null; + } + + private int loadShader(AssetLocation location, int type) + { + StringBuilder shaderSource = new StringBuilder(); + try(IAsset asset = ShaderTracker.INSTANCE.getAssets().getAsset(location)) + { + for(String s : IterableWrapper.wrap(asset.getStringReader())) + { + if(s.startsWith("//")) + { + continue; + } + shaderSource.append(s).append("//\n"); + } + } + catch(Exception e) + { + e.printStackTrace(); + return -1; + } + int shaderID = GL20.glCreateShader(type); + GL20.glShaderSource(shaderID, shaderSource); + GL20.glCompileShader(shaderID); + if(GL20.glGetShaderi(shaderID, GL20.GL_COMPILE_STATUS) == 0) + { + GameLog.warn("Could not compile shader " + location); + GameLog.error(GL20.glGetShaderInfoLog(shaderID, 500)); + return -1; + } + return shaderID; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/ShaderTracker.java b/src/main/java/speiger/src/coreengine/rendering/shader/ShaderTracker.java new file mode 100644 index 0000000..eeb8d6d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/ShaderTracker.java @@ -0,0 +1,142 @@ +package speiger.src.coreengine.rendering.shader; + +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import org.lwjgl.opengl.GL20; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.assets.AssetManager; +import speiger.src.coreengine.assets.reloader.IReloadableResource; + +public class ShaderTracker implements IReloadableResource +{ + public static final ShaderTracker INSTANCE = new ShaderTracker(); + List> programs = new ObjectArrayList>(); + int activeShader = 0; + boolean reloading = false; + AssetManager assets; + + public void init(AssetManager assets) + { + this.assets = assets; + } + + public T register(Supplier creator, Consumer reloader) + { + T result = creator.get(); + result.init(); + ReloadReference reference = new ReloadReference<>(result, creator, reloader); + programs.add(reference); + return result; + } + + protected AssetManager getAssets() + { + return assets; + } + + void startNextShader(ShaderProgram shader) + { + if(shader.getShaderID() == activeShader) + { + return; + } + activeShader = shader.getShaderID(); + GL20.glUseProgram(activeShader); + } + + void stopShader(ShaderProgram shader) + { + if(activeShader == 0) + { + return; + } + activeShader = 0; + GL20.glUseProgram(activeShader); + } + + public void stopShader() + { + if(activeShader == 0) + { + return; + } + activeShader = 0; + GL20.glUseProgram(activeShader); + } + + public boolean isShaderActive() + { + return activeShader > 0; + } + + @Override + public void reload() + { + for(int i = 0,m = programs.size();i + { + T program; + Supplier creator; + Consumer listener; + + public ReloadReference(T value, Supplier creator, Consumer listener) + { + this.program = value; + this.creator = creator; + this.listener = listener; + program.setReference(this); + } + + private void destroy() + { + if(program == null || reloading) + { + return; + } + program.removeShader(); + program = null; + listener.accept(null); + } + + public void removeReference() + { + destroy(); + if(reloading) + { + return; + } + programs.remove(this); + } + + public void reload() + { + reloading = true; + program.removeShader(); + program = creator.get(); + program.init(); + program.setReference(this); + listener.accept(program); + reloading = false; + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/Uniform.java b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/Uniform.java new file mode 100644 index 0000000..24721c7 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/Uniform.java @@ -0,0 +1,38 @@ +package speiger.src.coreengine.rendering.shader.uniforms; + +import org.lwjgl.opengl.GL20; + +import speiger.src.coreengine.rendering.shader.ShaderProgram; +import speiger.src.coreengine.utils.io.GameLog; + +public abstract class Uniform +{ + protected String name; + protected int position; + protected boolean optional = false; + + public Uniform(String id) + { + name = id; + } + + public int getPosition() + { + return position; + } + + public T setOptional() + { + optional = true; + return (T)this; + } + + public void loadUniform(ShaderProgram prog) + { + position = GL20.glGetUniformLocation(prog.getShaderID(), name); + if(position == -1 && !optional) + { + GameLog.error("Couldn't find Shader Uniform: "+name+", Shader: "+"("+prog.getClass().getSimpleName()+".java:0) ("+prog.getShaderID()+")"); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformArrayBase.java b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformArrayBase.java new file mode 100644 index 0000000..d54f6e4 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformArrayBase.java @@ -0,0 +1,61 @@ +package speiger.src.coreengine.rendering.shader.uniforms; + +import java.util.Objects; + +import org.lwjgl.opengl.GL20; + +import speiger.src.coreengine.rendering.shader.ShaderProgram; + +public class UniformArrayBase extends Uniform +{ + protected int[] positions; + protected boolean[] first; + protected Object[] lastValues; + protected int limit; + + public UniformArrayBase(String id, int length) + { + super(id); + limit = length; + positions = new int[length]; + first = new boolean[length]; + lastValues = new Object[length]; + } + + public int getPosition(int index) + { + return positions[index]; + } + + protected boolean hasChanged(int index, T value) + { + if(!first[index] || !Objects.equals(lastValues[index], value)) + { + first[index] = true; + lastValues[index] = setValue((T)lastValues[index], value); + return true; + } + return false; + } + + protected T setValue(T oldValue, T newValue) + { + return newValue; + } + + @Override + public void loadUniform(ShaderProgram prog) + { + for(int i = 0;i extends Uniform +{ + T lastValue; + boolean first = true; + + public UniformBase(String id) + { + super(id); + } + + protected boolean hasChanged(T value) + { + if(first || !Objects.equals(lastValue, value)) + { + first = false; + lastValue = setValue(lastValue, value); + return true; + } + return false; + } + + protected T setValue(T oldValue, T newValue) + { + return newValue; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformBoolean.java b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformBoolean.java new file mode 100644 index 0000000..3111fe0 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformBoolean.java @@ -0,0 +1,26 @@ +package speiger.src.coreengine.rendering.shader.uniforms; + +import org.lwjgl.opengl.GL20; + +import speiger.src.coreengine.rendering.utils.AllocationTracker; + +public class UniformBoolean extends Uniform +{ + boolean last = false; + boolean first = true; + public UniformBoolean(String id) + { + super(id); + } + + public void storeData(boolean value) + { + if(last != value || first) + { + first = false; + last = value; + GL20.glUniform1f(getPosition(), value ? 1F : 0F); + AllocationTracker.INSTANCE.addGPUBytes(1L); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformBufferBlock.java b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformBufferBlock.java new file mode 100644 index 0000000..24cb4d6 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformBufferBlock.java @@ -0,0 +1,37 @@ +package speiger.src.coreengine.rendering.shader.uniforms; + +import org.lwjgl.opengl.GL30; +import org.lwjgl.opengl.GL31; + +import speiger.src.coreengine.rendering.models.UniformBuffer; +import speiger.src.coreengine.rendering.shader.ShaderProgram; +import speiger.src.coreengine.utils.io.GameLog; + +public class UniformBufferBlock extends Uniform +{ + ShaderProgram prog; + int slot; + + public UniformBufferBlock(String id, int slot) + { + super(id); + this.slot = slot; + } + + @Override + public void loadUniform(ShaderProgram prog) + { + this.prog = prog; + position = GL31.glGetUniformBlockIndex(prog.getShaderID(), name); + if(position == -1) + { + GameLog.error("Couldn't find Shader Uniform Block: "+name+", Shader ID: "+prog.getShaderID()); + } + } + + public void bindBuffer(UniformBuffer buffer) + { + GL30.glBindBufferBase(GL31.GL_UNIFORM_BUFFER, slot, buffer.getBufferID()); + GL31.glUniformBlockBinding(prog.getShaderID(), position, slot); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformFloat.java b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformFloat.java new file mode 100644 index 0000000..7d1442e --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformFloat.java @@ -0,0 +1,27 @@ +package speiger.src.coreengine.rendering.shader.uniforms; + +import org.lwjgl.opengl.GL20; + +import speiger.src.coreengine.rendering.utils.AllocationTracker; + +public class UniformFloat extends Uniform +{ + float lastValue = 0F; + boolean first = true; + + public UniformFloat(String id) + { + super(id); + } + + public void storeData(float value) + { + if(Float.floatToIntBits(lastValue) != Float.floatToIntBits(value) || first) + { + lastValue = value; + first = false; + GL20.glUniform1f(getPosition(), value); + AllocationTracker.INSTANCE.addGPUBytes(4L); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformInt.java b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformInt.java new file mode 100644 index 0000000..29b5058 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformInt.java @@ -0,0 +1,22 @@ +package speiger.src.coreengine.rendering.shader.uniforms; + +import org.lwjgl.opengl.GL20; + +import speiger.src.coreengine.rendering.utils.AllocationTracker; + +public class UniformInt extends UniformBase +{ + public UniformInt(String id) + { + super(id); + } + + public void storeData(Integer data) + { + if(hasChanged(data)) + { + GL20.glUniform1i(getPosition(), data); + AllocationTracker.INSTANCE.addGPUBytes(4L); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformMatrix.java b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformMatrix.java new file mode 100644 index 0000000..5161654 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformMatrix.java @@ -0,0 +1,33 @@ +package speiger.src.coreengine.rendering.shader.uniforms; + +import org.lwjgl.opengl.GL20; + +import speiger.src.coreengine.math.vector.matrix.Matrix4f; +import speiger.src.coreengine.rendering.utils.AllocationTracker; + +public class UniformMatrix extends UniformBase +{ + public UniformMatrix(String id) + { + super(id); + } + + public void storeData(Matrix4f data) + { + if(hasChanged(data)) + { + GL20.glUniformMatrix4fv(getPosition(), false, data.getData()); + AllocationTracker.INSTANCE.addGPUBytes(64L); + } + } + + @Override + protected Matrix4f setValue(Matrix4f oldValue, Matrix4f newValue) + { + if(oldValue != null) + { + return oldValue.load(newValue); + } + return new Matrix4f(newValue); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformTexture.java b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformTexture.java new file mode 100644 index 0000000..1e82a50 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformTexture.java @@ -0,0 +1,44 @@ +package speiger.src.coreengine.rendering.shader.uniforms; + +import speiger.src.coreengine.rendering.textures.ITexture; +import speiger.src.coreengine.rendering.textures.TextureManager; + +public class UniformTexture extends UniformInt +{ + int bank; + + public UniformTexture(String id, int bank) + { + super(id); + this.bank = bank; + } + + public void storeTexture(int id) + { + if(id == 0) + { + disable(); + return; + } + TextureManager.bindTexture(id, bank); + if(hasChanged(id)) + { + storeData(id); + } + } + + public void storeTexture(ITexture textureID) + { + if(textureID == null) + { + disable(); + return; + } + storeTexture(textureID.getTextureID()); + } + + public void disable() + { + TextureManager.unbindTexture(); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformVec2f.java b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformVec2f.java new file mode 100644 index 0000000..e074b41 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformVec2f.java @@ -0,0 +1,41 @@ +package speiger.src.coreengine.rendering.shader.uniforms; + +import org.lwjgl.opengl.GL20; + +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.rendering.utils.AllocationTracker; + +public class UniformVec2f extends UniformBase +{ + static final Vec2f STORED = Vec2f.newMutable(); + public UniformVec2f(String id) + { + super(id); + } + + @Override + protected Vec2f setValue(Vec2f oldValue, Vec2f newValue) + { + if(oldValue != null) + { + oldValue.set(newValue.getX(), newValue.getY()); + return oldValue; + } + return Vec2f.newMutable(newValue); + } + + public void storeData(Vec2f data) + { + if(hasChanged(data)) + { + GL20.glUniform2f(getPosition(), data.getX(), data.getY()); + AllocationTracker.INSTANCE.addGPUBytes(8L); + } + } + + public void storeData(float x, float y) + { + STORED.set(x, y); + storeData(STORED); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformVec3f.java b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformVec3f.java new file mode 100644 index 0000000..05e070f --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformVec3f.java @@ -0,0 +1,46 @@ +package speiger.src.coreengine.rendering.shader.uniforms; + +import org.lwjgl.opengl.GL20; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.math.vector.floats.Vec3f; +import speiger.src.coreengine.rendering.utils.AllocationTracker; + +public class UniformVec3f extends UniformBase +{ + static final Vec3f STORAGE = Vec3f.newMutable(); + + public UniformVec3f(String id) + { + super(id); + } + + public void storeData(Vec3f data) + { + if(hasChanged(data)) + { + GL20.glUniform3f(getPosition(), data.getX(), data.getY(), data.getZ()); + AllocationTracker.INSTANCE.addGPUBytes(12L); + } + } + + public void storeData(ColorObject color) + { + storeData(STORAGE.set(color.getRedFloat(), color.getGreenFloat(), color.getBlueFloat())); + } + + public void storeData(float x, float y, float z) + { + storeData(STORAGE.set(x, y, z)); + } + + @Override + protected Vec3f setValue(Vec3f oldValue, Vec3f newValue) + { + if(oldValue != null) + { + return oldValue.set(newValue); + } + return Vec3f.newMutable(newValue); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformVec3fArray.java b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformVec3fArray.java new file mode 100644 index 0000000..e35e83f --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformVec3fArray.java @@ -0,0 +1,54 @@ +package speiger.src.coreengine.rendering.shader.uniforms; + +import java.util.Collection; +import java.util.Iterator; + +import org.lwjgl.opengl.GL20; + +import speiger.src.coreengine.math.vector.floats.Vec3f; +import speiger.src.coreengine.rendering.utils.AllocationTracker; + +public class UniformVec3fArray extends UniformArrayBase +{ + static final Vec3f STORAGE = Vec3f.newMutable(); + + public UniformVec3fArray(String id, int length) + { + super(id, length); + } + + public void store(int index, Vec3f data) + { + if(hasChanged(index, data)) + { + GL20.glUniform3f(getPosition(index), data.getX(), data.getY(), data.getZ()); + AllocationTracker.INSTANCE.addGPUBytes(12L); + } + } + + public void store(int index, float x, float y, float z) + { + STORAGE.set(x, y, z); + store(index, STORAGE); + } + + public void store(Collection store) + { + Iterator values = store.iterator(); + for(int i = 0;i +{ + static final Vec4f STORAGE = Vec4f.newMutable(); + + public UniformVec4f(String id) + { + super(id); + } + + public void storeData(Vec4f data) + { + if(hasChanged(data)) + { + GL20.glUniform4f(getPosition(), data.getX(), data.getY(), data.getZ(), data.getW()); + AllocationTracker.INSTANCE.addGPUBytes(16L); + } + } + + public void storeData(ColorObject color) + { + STORAGE.set(color.getRedFloat(), color.getGreenFloat(), color.getBlueFloat(), color.getAlphaFloat()); + storeData(STORAGE); + } + + public void storeData(float x, float y, float z, float w) + { + STORAGE.set(x, y, z, w); + storeData(STORAGE); + } + + @Override + protected Vec4f setValue(Vec4f oldValue, Vec4f newValue) + { + if(oldValue != null) + { + oldValue.set(newValue); + return oldValue; + } + return Vec4f.newMutable(newValue); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformVec4fArray.java b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformVec4fArray.java new file mode 100644 index 0000000..ff02380 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/shader/uniforms/UniformVec4fArray.java @@ -0,0 +1,54 @@ +package speiger.src.coreengine.rendering.shader.uniforms; + +import java.util.Collection; +import java.util.Iterator; + +import org.lwjgl.opengl.GL20; + +import speiger.src.coreengine.math.vector.floats.Vec4f; +import speiger.src.coreengine.rendering.utils.AllocationTracker; + +public class UniformVec4fArray extends UniformArrayBase +{ + static final Vec4f STORAGE = Vec4f.newMutable(); + + public UniformVec4fArray(String id, int length) + { + super(id, length); + } + + public void store(int index, Vec4f data) + { + if(hasChanged(index, data)) + { + GL20.glUniform4f(getPosition(index), data.getX(), data.getY(), data.getZ(), data.getW()); + AllocationTracker.INSTANCE.addGPUBytes(16L); + } + } + + public void store(int index, float x, float y, float z, float w) + { + STORAGE.set(x, y, z, w); + store(index, STORAGE); + } + + public void store(Collection store) + { + Iterator values = store.iterator(); + for(int i = 0;i +{ + List elements = new ArrayList(); + Set types = EnumSet.noneOf(ElementUsage.class); + IntList offsets = new IntArrayList(); + int totalOffset; + + public VertexList() + { + } + + public VertexList(VertexList vertexList) + { + elements.addAll(vertexList.elements); + offsets.addAll(vertexList.offsets); + totalOffset = vertexList.totalOffset; + } + + public void addElement(VertexElement element) + { + elements.add(element); + offsets.add(totalOffset); + totalOffset += element.getSize(); + types.add(element.getUsage()); + } + + public int getOffsets() + { + return totalOffset; + } + + public int getElementCount() + { + return elements.size(); + } + + public VertexElement getElement(int index) + { + return elements.get(index); + } + + public int getOffset(int index) + { + return offsets.getInt(index); + } + + @Override + public Iterator iterator() + { + return new ReadOnlyIterator(elements.iterator()); + } + + public boolean hasType(ElementUsage type) + { + return types.contains(type); + } + +} diff --git a/src/main/java/speiger/src/coreengine/rendering/tesselation/VertexType.java b/src/main/java/speiger/src/coreengine/rendering/tesselation/VertexType.java new file mode 100644 index 0000000..e2c2cb8 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/tesselation/VertexType.java @@ -0,0 +1,47 @@ +package speiger.src.coreengine.rendering.tesselation; + +import speiger.src.coreengine.rendering.tesselation.VertexElement.ElementUsage; + +public class VertexType +{ + public static final VertexElement POSITION_2F = new VertexElement(2, ElementUsage.POS); + public static final VertexElement POSITION_3F = new VertexElement(3, ElementUsage.POS); + public static final VertexElement TEXTURE_2F = new VertexElement(2, ElementUsage.UV); + public static final VertexElement COLOR_3F = new VertexElement(3, ElementUsage.COLOR); + public static final VertexElement COLOR_4F = new VertexElement(4, ElementUsage.COLOR); + public static final VertexElement NORMAL_3F = new VertexElement(3, ElementUsage.NORMAL); + public static final VertexElement CUSTOM_4F = new VertexElement(4, ElementUsage.CUSTOM); + + public static final VertexList POS_COLOR_3F = new VertexList(); + public static final VertexList POS_COLOR_4F = new VertexList(); + public static final VertexList POS_COLOR_2F = new VertexList(); + public static final VertexList POS_TEX = new VertexList(); + public static final VertexList TERRAIN = new VertexList(); + + public static final VertexList UI = new VertexList(); + public static final VertexList IN_WORLD_UI = new VertexList(); + + static + { + UI.addElement(POSITION_3F); + UI.addElement(TEXTURE_2F); + UI.addElement(COLOR_4F); + + IN_WORLD_UI.addElement(POSITION_3F); + IN_WORLD_UI.addElement(TEXTURE_2F); + IN_WORLD_UI.addElement(COLOR_4F); + IN_WORLD_UI.addElement(CUSTOM_4F); + + TERRAIN.addElement(POSITION_3F); + TERRAIN.addElement(COLOR_3F); + TERRAIN.addElement(NORMAL_3F); + POS_COLOR_2F.addElement(POSITION_2F); + POS_COLOR_2F.addElement(COLOR_4F); + POS_COLOR_3F.addElement(POSITION_3F); + POS_COLOR_3F.addElement(COLOR_3F); + POS_COLOR_4F.addElement(POSITION_3F); + POS_COLOR_4F.addElement(COLOR_4F); + POS_TEX.addElement(POSITION_3F); + POS_TEX.addElement(TEXTURE_2F); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/textures/AbstractTexture.java b/src/main/java/speiger/src/coreengine/rendering/textures/AbstractTexture.java new file mode 100644 index 0000000..4b3a4ff --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/textures/AbstractTexture.java @@ -0,0 +1,69 @@ +package speiger.src.coreengine.rendering.textures; + +public abstract class AbstractTexture implements ITexture +{ + int textureID; + + public AbstractTexture() + { + TextureManager.INSTANCE.addTexture(this); + } + + public void setTextureID(int textureID) + { + this.textureID = textureID; + } + + @Override + public int getTextureID() + { + return textureID; + } + + @Override + public void bindTexture() + { + TextureManager.bindTexture(textureID); + } + + @Override + public void destroy() + { + deleteTexture(); + } + + @Override + public void deleteTexture() + { + if(textureID == -1) + { + return; + } + TextureManager.INSTANCE.removeTexture(this); + textureID = -1; + } + + @Override + public float getUMin() + { + return 0F; + } + + @Override + public float getVMin() + { + return 0F; + } + + @Override + public float getUMax() + { + return 1F; + } + + @Override + public float getVMax() + { + return 1F; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/textures/DirectTexture.java b/src/main/java/speiger/src/coreengine/rendering/textures/DirectTexture.java new file mode 100644 index 0000000..055a377 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/textures/DirectTexture.java @@ -0,0 +1,99 @@ +package speiger.src.coreengine.rendering.textures; + +import java.awt.image.BufferedImage; +import java.nio.ByteBuffer; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL13; +import org.lwjgl.opengl.GL30; +import org.lwjgl.system.MemoryUtil; + +public class DirectTexture extends AbstractTexture +{ + BufferedImage image; + + public DirectTexture(BufferedImage image) + { + this.image = image; + loadTexture(); + } + + public void loadTexture() + { + int width = image.getWidth(); + int height = image.getHeight(); + int[] pixelData = new int[width * height]; + image.getRGB(0, 0, width, height, pixelData, 0, width); + ByteBuffer buffer = MemoryUtil.memAlloc(pixelData.length * 4); + for(int y = 0;y> 16) & 0xFF)); + buffer.put((byte)((pixel >> 8) & 0xFF)); + buffer.put((byte)(pixel & 0xFF)); + buffer.put((byte)((pixel >> 24) & 0xFF)); + } + } + + buffer.flip(); + setTextureID(GL11.glGenTextures()); + bindTexture(); + GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); + GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL13.GL_CLAMP_TO_BORDER); + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL13.GL_CLAMP_TO_BORDER); + GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, width, height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer); + GL30.glGenerateMipmap(GL11.GL_TEXTURE_2D); + MemoryUtil.memFree(buffer); + } + + public void updateTexture() + { + int width = image.getWidth(); + int height = image.getHeight(); + int[] pixelData = new int[width * height]; + image.getRGB(0, 0, width, height, pixelData, 0, width); + ByteBuffer buffer = MemoryUtil.memAlloc(pixelData.length * 4); + for(int y = 0;y> 16) & 0xFF)); + buffer.put((byte)((pixel >> 8) & 0xFF)); + buffer.put((byte)(pixel & 0xFF)); + buffer.put((byte)((pixel >> 24) & 0xFF)); + } + } + buffer.flip(); + GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, width, height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer); + GL30.glGenerateMipmap(GL11.GL_TEXTURE_2D); + MemoryUtil.memFree(buffer); + } + + public BufferedImage getImage() + { + return image; + } + + @Override + public int getWidth() + { + return image.getWidth(); + } + + @Override + public int getHeight() + { + return image.getHeight(); + } + + @Override + public void reload() + { + TextureManager.INSTANCE.removeTexture(textureID); + loadTexture(); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/textures/DynamicTexture.java b/src/main/java/speiger/src/coreengine/rendering/textures/DynamicTexture.java new file mode 100644 index 0000000..6e9ab86 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/textures/DynamicTexture.java @@ -0,0 +1,153 @@ +package speiger.src.coreengine.rendering.textures; + +import java.nio.ByteBuffer; +import java.util.Iterator; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.MemoryUtil; + +import speiger.src.collections.ints.collections.IntIterator; +import speiger.src.collections.ints.maps.impl.hash.Int2ObjectOpenHashMap; +import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap; +import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap.Entry; +import speiger.src.collections.ints.sets.IntOpenHashSet; +import speiger.src.collections.ints.sets.IntSet; +import speiger.src.collections.ints.utils.maps.Int2ObjectMaps; +import speiger.src.collections.utils.ITrimmable; +import speiger.src.coreengine.math.BitUtil; +import speiger.src.coreengine.rendering.utils.AllocationTracker; + +public class DynamicTexture extends AbstractTexture +{ + Int2ObjectMap dirtyChunks = new Int2ObjectOpenHashMap(); + int[] data; + int width; + int height; + boolean first = true; + + public DynamicTexture(int width, int height) + { + this.width = width; + this.height = height; + setTextureID(GL11.glGenTextures()); + data = new int[width * height]; + } + + public int[] getTextureData() + { + return data; + } + + public void markDirty(int x, int z) + { + int chunkIndex = BitUtil.toInt(x >> 4, z >> 4); + IntSet set = dirtyChunks.get(chunkIndex); + if(set == null) + { + set = new IntOpenHashSet(); + dirtyChunks.put(chunkIndex, set); + } + set.add(BitUtil.toInt(x, z)); + } + + public void setData(int x, int z, int value) + { + data[(z * width) + x] = value; + markDirty(x, z); + } + + public boolean hasTasks() + { + return dirtyChunks.size() > 0 || first; + } + + public void updateData() + { + if(first) + { + ByteBuffer buffer = MemoryUtil.memAlloc(data.length * 4); + for(int i = 0;i> 16) & 0xFF)); + buffer.put((byte)((pixel >> 8) & 0xFF)); + buffer.put((byte)(pixel & 0xFF)); + buffer.put((byte)((pixel >> 24) & 0xFF)); + } + buffer.flip(); + bindTexture(); + GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST); + GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST); + AllocationTracker.INSTANCE.addGPUBytes(buffer.remaining()); + GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, width, height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer); + first = false; + MemoryUtil.memFree(buffer); + dirtyChunks.clear(); + } + else + { + bindTexture(); + Thread thread = Thread.currentThread(); + for(Iterator> iter = Int2ObjectMaps.fastIterator(dirtyChunks);iter.hasNext() && !thread.isInterrupted();iter.remove()) + { + Entry values = iter.next(); + int key = values.getIntKey(); + int chunkX = BitUtil.toFirstShort(key) * 16; + int chunkZ = BitUtil.toSecondShort(key) * 16; + int width = 0; + int height = 0; + for(IntIterator subIt = values.getValue().iterator();subIt.hasNext();) + { + int index = subIt.nextInt(); + width = Math.max(width, BitUtil.toFirstShort(index) - chunkX + 1); + height = Math.max(height, BitUtil.toSecondShort(index) - chunkZ + 1); + } + try(MemoryStack stack = MemoryStack.stackPush()) + { + ByteBuffer buffer = stack.malloc(width * height * 4); + for(int y = 0;y> 16) & 0xFF)); + buffer.put((byte)((pixel >> 8) & 0xFF)); + buffer.put((byte)(pixel & 0xFF)); + buffer.put((byte)((pixel >> 24) & 0xFF)); + } + } + buffer.flip(); + AllocationTracker.INSTANCE.addGPUBytes(buffer.remaining()); + GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, chunkX, chunkZ, width, height, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer); + } + } + } + if(dirtyChunks.isEmpty()) + { + ((ITrimmable)dirtyChunks).trim(); + } + } + + @Override + public void reload() + { + TextureManager.INSTANCE.removeTexture(getTextureID()); + setTextureID(GL11.glGenTextures()); + dirtyChunks.clear(); + first = true; + updateData(); + } + + @Override + public int getWidth() + { + return width; + } + + @Override + public int getHeight() + { + return height; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/textures/ITexture.java b/src/main/java/speiger/src/coreengine/rendering/textures/ITexture.java new file mode 100644 index 0000000..1ce92f2 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/textures/ITexture.java @@ -0,0 +1,22 @@ +package speiger.src.coreengine.rendering.textures; + +import speiger.src.coreengine.assets.reloader.IReloadableResource; + +public interface ITexture extends IReloadableResource +{ + public int getTextureID(); + + public void bindTexture(); + public void deleteTexture(); + + public int getWidth(); + public int getHeight(); + + public float getUMin(); + public float getVMin(); + public float getUMax(); + public float getVMax(); + + public default float getInterpolatedU(float u){return getUMin() + ((getUMax() - getUMin()) * u);} + public default float getINterpolatedV(float v){return getUMin() + ((getUMax() - getUMin()) * v);} +} diff --git a/src/main/java/speiger/src/coreengine/rendering/textures/SimpleTexture.java b/src/main/java/speiger/src/coreengine/rendering/textures/SimpleTexture.java new file mode 100644 index 0000000..a06247e --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/textures/SimpleTexture.java @@ -0,0 +1,84 @@ +package speiger.src.coreengine.rendering.textures; + +import java.awt.image.BufferedImage; +import java.nio.ByteBuffer; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL13; +import org.lwjgl.opengl.GL30; +import org.lwjgl.system.MemoryUtil; + +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.assets.IAsset; + +public class SimpleTexture extends AbstractTexture +{ + AssetLocation location; + int width; + int height; + + public SimpleTexture(AssetLocation location) + { + super(); + this.location = location; + loadTexture(); + } + + public void loadTexture() + { + try(IAsset asset = TextureManager.INSTANCE.getManager().getAsset(location)) + { + BufferedImage image = asset.getTexture(); + width = image.getWidth(); + height = image.getHeight(); + int[] pixelData = new int[width * height]; + image.getRGB(0, 0, width, height, pixelData, 0, width); + ByteBuffer buffer = MemoryUtil.memAlloc(pixelData.length * 4); + for(int y = 0;y> 16) & 0xFF)); + buffer.put((byte)((pixel >> 8) & 0xFF)); + buffer.put((byte)(pixel & 0xFF)); + buffer.put((byte)((pixel >> 24) & 0xFF)); + } + } + + buffer.flip(); + setTextureID(GL11.glGenTextures()); + bindTexture(); + GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); + GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL13.GL_CLAMP_TO_BORDER); + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL13.GL_CLAMP_TO_BORDER); + GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, width, height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer); + GL30.glGenerateMipmap(GL11.GL_TEXTURE_2D); + MemoryUtil.memFree(buffer); + } + catch(Exception e) + { + e.printStackTrace(); + } + } + + @Override + public void reload() + { + TextureManager.INSTANCE.removeTexture(textureID); + loadTexture(); + } + + @Override + public int getWidth() + { + return width; + } + + @Override + public int getHeight() + { + return height; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/textures/TextureManager.java b/src/main/java/speiger/src/coreengine/rendering/textures/TextureManager.java new file mode 100644 index 0000000..b101bd2 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/textures/TextureManager.java @@ -0,0 +1,144 @@ +package speiger.src.coreengine.rendering.textures; + +import java.awt.image.BufferedImage; +import java.nio.ByteBuffer; + +import org.lwjgl.glfw.GLFWImage; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL13; +import org.lwjgl.system.MemoryUtil; + +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.assets.AssetManager; +import speiger.src.coreengine.assets.IAsset; +import speiger.src.coreengine.assets.reloader.IReloadableResource; +import speiger.src.coreengine.assets.reloader.ResourceReloader; +import speiger.src.coreengine.rendering.utils.GLUtils; +import speiger.src.coreengine.rendering.utils.states.GLState; +import speiger.src.coreengine.utils.io.GameLog; +import speiger.src.coreengine.utils.io.GameLog.LogLevel; + +public class TextureManager implements IReloadableResource +{ + public static final TextureManager INSTANCE = new TextureManager(); + public static final GLState TEXTURE = GLUtils.addState(new GLState(GL11.GL_TEXTURE_2D, false)); + AssetManager manager; + ResourceReloader reloader = new ResourceReloader(); + int[] textureBanks = new int[GL13.GL_TEXTURE31 - GL13.GL_TEXTURE0]; + int activeBank = 0; + + public void init(AssetManager manager) + { + this.manager = manager; + } + + public ResourceReloader getReloader() + { + return reloader; + } + + public void addTexture(ITexture texture) + { + if(texture != null) + { + reloader.addReloadableResource(texture); + } + } + + public void removeTexture(ITexture texture) + { + if(texture != null) + { + GL11.glDeleteTextures(texture.getTextureID()); + reloader.removeReloadableResource(texture); + } + } + + public void removeTexture(int id) + { + GL11.glDeleteTextures(id); + } + + public static void bindTexture(int texture) + { + INSTANCE.setActiveTexture(texture); + } + + public static void bindTexture(int texture, int bank) + { + INSTANCE.setActiveTexture(texture, bank); + } + + public static void unbindTexture() + { + INSTANCE.setActiveTexture(0); + } + + public void setActiveTexture(int texture) + { + if(textureBanks[activeBank] != texture) + { + textureBanks[activeBank] = texture; + GL11.glBindTexture(GL11.GL_TEXTURE_2D, texture); + } + } + + public void setActiveTexture(int texture, int bank) + { + if(activeBank != bank) + { + activeBank = bank; + GL13.glActiveTexture(GL13.GL_TEXTURE0 + activeBank); + } + bindTexture(texture); + } + + @Override + public void reload() + { + reloader.reloadResources(); + } + + @Override + public void destroy() + { + reloader.deleteResources(); + } + + public AssetManager getManager() + { + return manager; + } + + public static GLFWImage create(AssetLocation location) + { + try(IAsset asset = INSTANCE.manager.getAsset(location)) + { + BufferedImage image = asset.getTexture(); + int width = image.getWidth(); + int height = image.getHeight(); + int[] pixelData = new int[width * height]; + image.getRGB(0, 0, width, height, pixelData, 0, width); + ByteBuffer buffer = MemoryUtil.memAlloc(pixelData.length * 4); + for(int y = 0;y> 16) & 0xFF)); + buffer.put((byte)((pixel >> 8) & 0xFF)); + buffer.put((byte)(pixel & 0xFF)); + buffer.put((byte)((pixel >> 24) & 0xFF)); + } + } + GLFWImage glfw = GLFWImage.malloc(); + glfw.set(width, height, buffer); + return glfw; + } + catch(Exception e) + { + GameLog.error("Couldn't Load Texture ["+location.toString()+"]", e, LogLevel.ERROR); + return null; + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/textures/WrappedTexture.java b/src/main/java/speiger/src/coreengine/rendering/textures/WrappedTexture.java new file mode 100644 index 0000000..fa61518 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/textures/WrappedTexture.java @@ -0,0 +1,69 @@ +package speiger.src.coreengine.rendering.textures; + +public class WrappedTexture implements ITexture +{ + int textureId; + + public WrappedTexture(int textureId) + { + this.textureId = textureId; + } + + @Override + public void reload() + { + } + + @Override + public int getTextureID() + { + return textureId; + } + + @Override + public void bindTexture() + { + TextureManager.bindTexture(textureId); + } + + @Override + public void deleteTexture() + { + } + + @Override + public int getWidth() + { + return 0; + } + + @Override + public int getHeight() + { + return 0; + } + + @Override + public float getUMin() + { + return 0F; + } + + @Override + public float getVMin() + { + return 0F; + } + + @Override + public float getUMax() + { + return 1F; + } + + @Override + public float getVMax() + { + return 1F; + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/AllocationTracker.java b/src/main/java/speiger/src/coreengine/rendering/utils/AllocationTracker.java new file mode 100644 index 0000000..0ed3b84 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/AllocationTracker.java @@ -0,0 +1,62 @@ +package speiger.src.coreengine.rendering.utils; + +import speiger.src.coreengine.utils.counters.averager.Averager; + +public class AllocationTracker +{ + public static final AllocationTracker INSTANCE = new AllocationTracker(); + + long lastTime = 0; + long lastMemory = 0L; + long addedCPUMemory = 0L; + long addedGPUMemory = 0L; + Averager cpuAllocation = new Averager(20); + Averager gpuAllocation = new Averager(20); + + public void update() + { + long newTime = System.currentTimeMillis(); + if(newTime - lastTime < 1000) + { + updateAllocation(); + return; + } + lastTime = newTime; + updateAllocation(); + cpuAllocation.addEntry(addedCPUMemory); + addedCPUMemory = 0L; + gpuAllocation.addEntry(addedGPUMemory); + addedGPUMemory = 0L; + } + + public void addGPUBytes(long amount) + { + addedGPUMemory += amount; + } + + private void updateAllocation() + { + long memory = getMemory(); + if(memory > lastMemory) + { + addedCPUMemory += (memory - lastMemory); + } + lastMemory = memory; + } + + private long getMemory() + { + Runtime runtime = Runtime.getRuntime(); + return runtime.totalMemory() - runtime.freeMemory(); + } + + public long getCPUAllocatedBytes() + { + return cpuAllocation.getAverage(); + } + + public long getGPUAllocationBytes() + { + return gpuAllocation.getAverage(); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/Cursor.java b/src/main/java/speiger/src/coreengine/rendering/utils/Cursor.java new file mode 100644 index 0000000..959a636 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/Cursor.java @@ -0,0 +1,113 @@ +package speiger.src.coreengine.rendering.utils; + +import org.lwjgl.glfw.GLFW; +import org.lwjgl.glfw.GLFWImage; + +import speiger.src.collections.objects.maps.impl.hash.Object2LongOpenHashMap; +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap; +import speiger.src.collections.objects.maps.interfaces.Object2LongMap; +import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap; +import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap.Entry; +import speiger.src.collections.objects.utils.maps.Object2ObjectMaps; +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.assets.reloader.IReloadableResource; +import speiger.src.coreengine.rendering.input.window.Window; +import speiger.src.coreengine.rendering.textures.TextureManager; + +public final class Cursor implements IReloadableResource +{ + public static final Cursor INSTANCE = new Cursor(); + public static final AssetLocation CURSOR_ARROW = AssetLocation.of("Arrow"); + public static final AssetLocation CURSOR_IBEAM = AssetLocation.of("I-Beam"); + public static final AssetLocation CURSOR_CROSSHAIR = AssetLocation.of("Crosshair"); + public static final AssetLocation CURSOR_HAND = AssetLocation.of("Hand"); + public static final AssetLocation CURSOR_HRESIZE = AssetLocation.of("H-Resize"); + public static final AssetLocation CURSOR_VRESIZE = AssetLocation.of("V-Resize"); + static final AssetLocation[] LOCATIONS = new AssetLocation[]{CURSOR_ARROW, CURSOR_IBEAM, CURSOR_CROSSHAIR, CURSOR_HAND, CURSOR_HRESIZE, CURSOR_VRESIZE}; + + long currentCursor = 0; + boolean ignore = false; + Object2ObjectMap customCursors = new Object2ObjectLinkedOpenHashMap(); + Object2LongMap cursors = new Object2LongOpenHashMap(); + + Cursor() + { + } + + public void createCursor(AssetLocation id, AssetLocation texture) + { + GLFWImage image = TextureManager.create(texture); + if(image != null) + { + customCursors.put(id, texture); + cursors.put(id, GLFW.glfwCreateCursor(image, 0, 0)); + image.close(); + } + } + + public void bindCursor(AssetLocation id, Window window) + { + long cursor = cursors.getOrDefault(id, 0L); + if(cursor != currentCursor) + { + currentCursor = cursor; + GLFW.glfwSetCursor(window.getId(), cursor); + } + if(cursor != 0) + { + ignore = true; + } + } + + public void clearCursor(Window window) + { + if(ignore) + { + ignore = false; + return; + } + if(currentCursor != 0) + { + currentCursor = 0; + GLFW.glfwSetCursor(window.getId(), 0); + } + } + + @Override + public void reload() + { + for(long value : cursors.values()) + { + GLFW.glfwDestroyCursor(value); + } + cursors.clear(); + loadCursors(); + } + + @Override + public void destroy() + { + for(long value : cursors.values()) + { + GLFW.glfwDestroyCursor(value); + } + cursors.clear(); + } + + private void loadCursors() + { + for(int i = 0,m = LOCATIONS.length;i entry : Object2ObjectMaps.fastIterable(customCursors)) + { + GLFWImage image = TextureManager.create(entry.getValue()); + if(image != null) + { + cursors.put(entry.getKey(), GLFW.glfwCreateCursor(image, 0, 0)); + image.close(); + } + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/GLStamper.java b/src/main/java/speiger/src/coreengine/rendering/utils/GLStamper.java new file mode 100644 index 0000000..5a89a32 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/GLStamper.java @@ -0,0 +1,151 @@ +package speiger.src.coreengine.rendering.utils; + +import java.util.ArrayList; +import java.util.Map; +import java.util.Set; + +import org.lwjgl.opengl.GL15; +import org.lwjgl.opengl.GL33; + +import speiger.src.collections.ints.collections.IntStack; +import speiger.src.collections.ints.lists.IntArrayList; +import speiger.src.collections.ints.lists.IntList; +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectOpenHashMap; +import speiger.src.collections.objects.sets.ObjectOpenHashSet; +import speiger.src.collections.utils.Stack; + +public class GLStamper +{ + public static GLStamper INSTANCE = new GLStamper(); + + Map> stamps = new Object2ObjectOpenHashMap>(); + Stack unusedStamps = new ObjectArrayList(); + IntStack queryIDs = new IntArrayList(); + + + public GLStamp createStamp(String ownerID) + { + Set list = stamps.get(ownerID); + if(list == null) + { + list = new ObjectOpenHashSet(); + stamps.put(ownerID, list); + } + GLStamp stamp = unusedStamps.isEmpty() ? new GLStamp(this) : unusedStamps.pop(); + stamp.setOwner(ownerID); + list.add(stamp); + return stamp; + } + + public void removeOwner(String ownerID) + { + Set set = stamps.remove(ownerID); + if(set != null) + { + for(GLStamp stamp : set) + { + stamp.release(); + } + } + } + + public void cleanup() + { + if(!stamps.isEmpty()) + { + for(String s : new ArrayList(stamps.keySet())) + { + removeOwner(s); + } + } + if(queryIDs.isEmpty()) return; + GL15.glDeleteQueries(((IntList)queryIDs).toIntArray()); + ((IntList)queryIDs).clear(); + } + + protected void release(GLStamp stamp) + { + Set set = stamps.get(stamp.getOwner()); + if(set != null) + { + set.remove(set); + } + unusedStamps.push(stamp); + } + + protected int getQueryID() + { + int id = queryIDs.isEmpty() ? GL15.glGenQueries() : queryIDs.pop(); + GL33.glQueryCounter(id, GL33.GL_TIMESTAMP); + return id; + } + + protected void releaseQueryID(int id) + { + if(id != -1) + { + queryIDs.push(id); + } + } + + public static class GLStamp + { + GLStamper stamp; + String owner; + int startID = -1; + int endID = -1; + + private GLStamp(GLStamper owner) + { + stamp = owner; + } + + void setOwner(String owner) + { + this.owner = owner; + } + + String getOwner() + { + return owner; + } + + public GLStamp start() + { + if(startID == -1) + { + startID = stamp.getQueryID(); + } + return this; + } + + public GLStamp stop() + { + if(endID == -1) + { + endID = stamp.getQueryID(); + } + return this; + } + + public void release() + { + stamp.releaseQueryID(startID); + stamp.releaseQueryID(endID); + stamp.release(this); + startID = -1; + endID = -1; + } + + public boolean isFinished() + { + return GL15.glGetQueryObjectui(endID, GL15.GL_QUERY_RESULT_AVAILABLE) == 1; + } + + public long getResult() + { + return GL33.glGetQueryObjectui64(endID, GL15.GL_QUERY_RESULT) - GL33.glGetQueryObjectui64(startID, GL15.GL_QUERY_RESULT); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/GLUtils.java b/src/main/java/speiger/src/coreengine/rendering/utils/GLUtils.java new file mode 100644 index 0000000..2f1a280 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/GLUtils.java @@ -0,0 +1,131 @@ +package speiger.src.coreengine.rendering.utils; + +import java.nio.IntBuffer; +import java.util.List; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL13; +import org.lwjgl.opengl.GL31; +import org.lwjgl.opengl.GL44; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.rendering.utils.states.BlendState; +import speiger.src.coreengine.rendering.utils.states.CullState; +import speiger.src.coreengine.rendering.utils.states.GLProvoking; +import speiger.src.coreengine.rendering.utils.states.GLState; +import speiger.src.coreengine.rendering.utils.states.GLWireFrame; +import speiger.src.coreengine.rendering.utils.states.IGLState; +import speiger.src.coreengine.rendering.utils.states.LineSize; +import speiger.src.coreengine.rendering.utils.states.PointSize; +import speiger.src.coreengine.utils.counters.averager.Counter; + +public class GLUtils +{ + static final List ALL_STATES = new ObjectArrayList<>(); + public static final GLState MULTI_SAMPLING = addState(new GLState(GL13.GL_MULTISAMPLE, false)); + public static final GLState WIRE_FRAME = addState(new GLWireFrame()); + public static final GLState PROVOKING_VERTEX = addState(new GLProvoking()); + public static final GLState DEBTH_TEST = addState(new GLState(GL11.GL_DEPTH_TEST, false)); + public static final CullState CULL_FACE = addState(new CullState(GL11.GL_BACK)); + public static final BlendState BLEND = addState(new BlendState()); + public static final GLState CLIP_PLANE0 = addState(new GLState(GL11.GL_CLIP_PLANE0, false)); + public static final PointSize POINT_SIZE = addState(new PointSize()); + public static final LineSize LINE_SIZE = addState(new LineSize()); + public static final ScissorsManager TESTER = new ScissorsManager(100); + public static final Counter[] COUNTERS = Counter.createCounters(4); + public static final ViewPortStack VIEW_PORT = new ViewPortStack(); + + public static T addState(T state) + { + ALL_STATES.add(state); + return state; + } + + public static void onFrameEnded() + { + for(int i = 0,m=COUNTERS.length;i= instances.length) + { + GameLog.warn("Scissors test would go out of Bounds!"); + return; + } + inUse++; + if(inUse == 0) + { + instances[inUse].set(x, y, x + width, y + height); + GL11.glEnable(GL11.GL_SCISSOR_TEST); + GL11.glScissor(x, y, width, height); + return; + } + Plane oldBox = instances[inUse - 1]; + int maxX = x + width; + int maxY = y + height; + if (x < oldBox.getMinX()) x = oldBox.getMinX(); + if (maxX > oldBox.getMaxX()) maxX = oldBox.getMaxX(); + if (y < oldBox.getMinY()) y = oldBox.getMinY(); + if (maxY > oldBox.getMaxY()) maxY = oldBox.getMaxY(); + Plane box = instances[inUse]; + box.set(x, y, maxX, maxY); + GL11.glScissor(box.getMinX(), box.getMinY(), box.getWidth(), box.getHeight()); + } + + public boolean isInScissors(int x, int y, int width, int height) + { + if(inUse == -1) + { + return true; + } + return instances[inUse].isIntersecting(x, y, x + width, y + height); + } + + public void disableScissors() + { + if(inUse < 0) + { + GameLog.warn("Scissors test would go into negative"); + return; + } + inUse--; + if(inUse >= 0) + { + Plane box = instances[inUse]; + GL11.glScissor(box.getMinX(), box.getMinY(), box.getWidth(), box.getHeight()); + } + else + { + GL11.glDisable(GL11.GL_SCISSOR_TEST); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/ScreenshotHandler.java b/src/main/java/speiger/src/coreengine/rendering/utils/ScreenshotHandler.java new file mode 100644 index 0000000..321447d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/ScreenshotHandler.java @@ -0,0 +1,248 @@ +package speiger.src.coreengine.rendering.utils; + +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferInt; +import java.io.File; +import java.nio.ByteBuffer; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; + +import javax.imageio.ImageIO; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.system.MemoryUtil; + +import speiger.src.coreengine.rendering.input.window.IWindowListener; +import speiger.src.coreengine.rendering.input.window.Window; +import speiger.src.coreengine.utils.helpers.FileUtils; +import speiger.src.coreengine.utils.io.GifWriter; + +public class ScreenshotHandler implements IWindowListener +{ + public static final DateTimeFormatter FILE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss"); + public static final DateTimeFormatter FOLDER_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM"); + static final ThreadPoolExecutor FACTORY = (ThreadPoolExecutor)Executors.newFixedThreadPool(1); + + File folder; + int width; + int height; + ByteBuffer buffer; + GifRecorder recorder; + Supplier imageProvider = this::getData; + + public ScreenshotHandler(File helper) + { + FACTORY.setKeepAliveTime(1, TimeUnit.SECONDS); + FACTORY.allowCoreThreadTimeOut(true); + folder = new File(helper, "Screenshots"); + } + + @Override + public void onWindowChanged(Window window) + { + width = window.getWidth(); + height = window.getHeight(); + if(buffer != null) + { + MemoryUtil.memFree(buffer); + buffer = null; + } + buffer = MemoryUtil.memAlloc(width * height * 4); + } + + public void screenShot() + { + ZonedDateTime time = ZonedDateTime.now(); + File subFolder = new File(folder, time.format(FOLDER_FORMAT)); + FileUtils.ensureFolder(subFolder); + try + { + ImageIO.write(getScreenShot(), "png", new File(subFolder, "Screenshot_"+time.format(FILE_FORMAT)+".png")); + } + catch(Exception e) + { + e.printStackTrace(); + } + } + + public boolean isRecording() + { + return recorder != null && !recorder.isFinished(); + } + + public void toggleRecording() + { + if(isRecording()) + { + stopRecording(); + } + else + { + startRecording(); + } + } + + //Undefined time recording + public void startRecording() + { + record("Capture_"+ZonedDateTime.now().format(FILE_FORMAT)+".gif", 15, Integer.MAX_VALUE); + } + + public void record(String fileName, int fps, int time) + { + if(recorder == null) + { + File subFolder = new File(folder, ZonedDateTime.now().format(FOLDER_FORMAT)); + FileUtils.ensureFolder(subFolder); + recorder = new GifRecorder(new File(subFolder, fileName), fps, time); + } + } + + public void stopRecording() + { + if(recorder != null) + { + recorder.finishPic(); + recorder = null; + } + } + + public void update() + { + if(recorder != null) + { + recorder.tick(imageProvider); + if(recorder.isFinished()) + { + recorder.finishPic(); + recorder = null; + } + } + } + + private BufferedImage getData() + { + buffer.clear(); + GL11.glReadBuffer(GL11.GL_FRONT); + GL11.glReadPixels(0, 0, width, height, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer); + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + int[] data = ((DataBufferInt)image.getRaster().getDataBuffer()).getData(); + for(int x = 0;x < width;x++) + { + for(int y = 0;y < height;y++) + { + int i = (x + (width * y)) * 4; + int r = buffer.get(i) & 0xFF; + int g = buffer.get(i + 1) & 0xFF; + int b = buffer.get(i + 2) & 0xFF; + data[x + (width * (height - (y + 1)))] = (0xFF << 24) | (r << 16) | (g << 8) | b; + } + } + return image; + } + + public BufferedImage getScreenShot() + { + buffer.clear(); + GL11.glReadBuffer(GL11.GL_FRONT); + GL11.glReadPixels(0, 0, width, height, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer); + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + int[] data = ((DataBufferInt)image.getRaster().getDataBuffer()).getData(); + for(int x = 0;x < width;x++) + { + for(int y = 0;y < height;y++) + { + int i = (x + (width * y)) * 4; + int r = buffer.get(i) & 0xFF; + int g = buffer.get(i + 1) & 0xFF; + int b = buffer.get(i + 2) & 0xFF; + data[x + (width * (height - (y + 1)))] = (0xFF << 24) | (r << 16) | (g << 8) | b; + } + } + return image; + } + + public void cleanup() + { + if(buffer != null) + { + MemoryUtil.memFree(buffer); + buffer = null; + } + } + + public static class GifRecorder + { + GifWriter writer; + long timeBetweenFrames; + long lastTime = -1L; + long freeTime; + int totalPics; + + public GifRecorder(File file, int fps, int time) + { + int ms = 1000 / fps; + writer = GifWriter.create(file, BufferedImage.TYPE_INT_ARGB, ms); + timeBetweenFrames = ms * 1000000L; + freeTime = ms * 1000000L; + totalPics = fps * time; + } + + public void tick(Supplier supply) + { + if(lastTime == -1L) + { + totalPics--; + insert(supply.get()); + lastTime = System.nanoTime(); + return; + } + long time = System.nanoTime(); + long happend = time - lastTime; + lastTime = time; + freeTime -= happend; + if(freeTime <= 0) + { + freeTime += timeBetweenFrames; + insert(supply.get()); + totalPics--; + } + } + + private void insert(final BufferedImage newBuffer) + { + FACTORY.execute(new Runnable(){ + BufferedImage buffer = newBuffer; + @Override + public void run() + { + if(writer != null) + { + writer.insertPicture(buffer); + } + } + }); + } + + public boolean isFinished() + { + return totalPics <= 0; + } + + public void finishPic() + { + FACTORY.execute(new Runnable(){ + @Override + public void run() + { + writer.close(); + writer = null; + } + }); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/ViewPortStack.java b/src/main/java/speiger/src/coreengine/rendering/utils/ViewPortStack.java new file mode 100644 index 0000000..2c48611 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/ViewPortStack.java @@ -0,0 +1,51 @@ +package speiger.src.coreengine.rendering.utils; + +import org.lwjgl.opengl.GL11; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.utils.Stack; +import speiger.src.coreengine.math.vector.ints.Vec4i; +import speiger.src.coreengine.utils.collections.pools.SimplePool; + +public class ViewPortStack +{ + SimplePool pool = new SimplePool<>(100, Vec4i::newMutable); + Stack viewPort = new ObjectArrayList(); + Vec4i defaultPort = Vec4i.newMutable(); + int inUse = 0; + + public void setDefault(int x, int y, int w, int h) + { + if(defaultPort.getX() != x || defaultPort.getY() != y || defaultPort.getZ() != w || defaultPort.getW() != h) + { + defaultPort.set(x, y, w, h); + if(viewPort.isEmpty()) + { + GL11.glViewport(x, y, w, h); + } + } + } + + public void push(int x, int y, int w, int h) + { + Vec4i current = getCurrent(); + if(current.getX() != x || current.getY() != y || current.getZ() != w || current.getW() != h) + { + viewPort.push(pool.get().set(x, y, w, h)); + GL11.glViewport(x, y, w, h); + } + } + + public void pop() + { + if(viewPort.isEmpty()) return; + pool.accept(viewPort.pop()); + Vec4i current = getCurrent(); + GL11.glViewport(current.getX(), current.getY(), current.getZ(), current.getW()); + } + + public Vec4i getCurrent() + { + return viewPort.isEmpty() ? defaultPort : viewPort.top(); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/states/BlendState.java b/src/main/java/speiger/src/coreengine/rendering/utils/states/BlendState.java new file mode 100644 index 0000000..98e82b5 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/states/BlendState.java @@ -0,0 +1,37 @@ +package speiger.src.coreengine.rendering.utils.states; + +import org.lwjgl.opengl.GL11; + +public class BlendState extends GLState +{ + int src; + int func; + + public BlendState() + { + super(GL11.GL_BLEND, false); + } + + @Override + public BlendState push(boolean newValue) + { + super.push(newValue); + return this; + } + + public BlendState setFunction(int src, int func) + { + if(this.src != src && this.func != func) + { + this.src = src; + this.func = func; + GL11.glBlendFunc(src, func); + } + return this; + } + + public void setDefault() + { + setFunction(GL11.GL_ONE, GL11.GL_ONE); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/states/CullState.java b/src/main/java/speiger/src/coreengine/rendering/utils/states/CullState.java new file mode 100644 index 0000000..4736126 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/states/CullState.java @@ -0,0 +1,33 @@ +package speiger.src.coreengine.rendering.utils.states; + +import org.lwjgl.opengl.GL11; + +public class CullState extends GLState +{ + int type; + + public CullState(int defaultState) + { + super(GL11.GL_CULL_FACE, false); + type = defaultState; + } + + @Override + public CullState push(boolean newValue) + { + super.push(newValue); + return this; + } + + public void setCullType(int type) + { + if(this.type != type) + { + this.type = type; + if(lastState) + { + GL11.glCullFace(type); + } + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/states/FloatState.java b/src/main/java/speiger/src/coreengine/rendering/utils/states/FloatState.java new file mode 100644 index 0000000..8c20b71 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/states/FloatState.java @@ -0,0 +1,58 @@ +package speiger.src.coreengine.rendering.utils.states; + +import speiger.src.collections.floats.lists.FloatArrayList; +import speiger.src.collections.floats.lists.FloatList; + +public abstract class FloatState implements IGLState +{ + protected final float defaultValue; + protected FloatList states = new FloatArrayList(); + + public FloatState(float defaultValue) + { + this.defaultValue = defaultValue; + } + + public void push(float newValue) + { + if((states.isEmpty() && equalsNot(newValue, defaultValue)) || (states.size() > 0 && equalsNot(states.getFloat(states.size()-1), newValue))) + { + setValue(newValue); + } + states.add(newValue); + } + + public void pop() + { + if(states.isEmpty()) throw new IllegalStateException("State is already reset"); + if(states.size() == 1 && equalsNot(defaultValue, states.getFloat(0))) + { + setValue(defaultValue); + } + else if(states.size() > 1 && equalsNot(states.getFloat(states.size()-2), states.getFloat(states.size()-1))) + { + setValue(states.getFloat(states.size()-2)); + } + states.removeFloat(states.size()-1); + } + + protected abstract void setValue(float value); + + protected abstract String getName(); + + protected boolean equalsNot(float key, float value) + { + return Float.floatToIntBits(key) != Float.floatToIntBits(value); + } + + @Override + public void cleanup() + { + if(states.size() > 1) + { + float value = states.getFloat(states.size()-1); + states.clear(); + states.add(value); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/states/GLProvoking.java b/src/main/java/speiger/src/coreengine/rendering/utils/states/GLProvoking.java new file mode 100644 index 0000000..445da01 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/states/GLProvoking.java @@ -0,0 +1,17 @@ +package speiger.src.coreengine.rendering.utils.states; + +import org.lwjgl.opengl.GL32; + +public class GLProvoking extends GLState +{ + public GLProvoking() + { + super(-1, false); + } + + @Override + protected void setValue(boolean value) + { + GL32.glProvokingVertex(value ? GL32.GL_FIRST_VERTEX_CONVENTION : GL32.GL_LAST_VERTEX_CONVENTION); + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/states/GLState.java b/src/main/java/speiger/src/coreengine/rendering/utils/states/GLState.java new file mode 100644 index 0000000..28f4f7c --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/states/GLState.java @@ -0,0 +1,65 @@ +package speiger.src.coreengine.rendering.utils.states; + +import java.util.BitSet; + +import org.lwjgl.opengl.GL11; + +public class GLState implements IGLState +{ + final int id; + protected final boolean defaultValue; + protected final BitSet memory = new BitSet(); + protected int index = -1; + protected boolean lastState; + + public GLState(int id, boolean defaultValue) + { + this.id = id; + this.defaultValue = defaultValue; + } + + public GLState push(boolean newValue) + { + if((index < 0 && newValue != defaultValue) || (index >= 0 && memory.get(index) != newValue)) + { + setValue(newValue); + } + memory.set(++index, newValue); + return this; + } + + public void pop() + { + if(index < 0) throw new IllegalStateException("State is already reset"); + if(index == 0 && defaultValue != memory.get(0)) + { + setValue(defaultValue); + } + else if(index > 0 && memory.get(index-1) != memory.get(index)) + { + setValue(memory.get(index-1)); + } + index--; + } + + public boolean isEnabled() + { + return index < 0 ? defaultValue : memory.get(index); + } + + protected void setValue(boolean value) + { + if(value) GL11.glEnable(id); + else GL11.glDisable(id); + } + + @Override + public void cleanup() + { + if(index > 0) + { + memory.set(0, memory.get(index)); + index = 0; + } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/states/GLWireFrame.java b/src/main/java/speiger/src/coreengine/rendering/utils/states/GLWireFrame.java new file mode 100644 index 0000000..59b42ed --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/states/GLWireFrame.java @@ -0,0 +1,17 @@ +package speiger.src.coreengine.rendering.utils.states; + +import org.lwjgl.opengl.GL11; + +public class GLWireFrame extends GLState +{ + public GLWireFrame() + { + super(-1, false); + } + + @Override + protected void setValue(boolean value) + { + GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, value ? GL11.GL_LINE : GL11.GL_FILL); + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/states/IGLState.java b/src/main/java/speiger/src/coreengine/rendering/utils/states/IGLState.java new file mode 100644 index 0000000..be57ac4 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/states/IGLState.java @@ -0,0 +1,6 @@ +package speiger.src.coreengine.rendering.utils.states; + +public interface IGLState +{ + public void cleanup(); +} diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/states/LineSize.java b/src/main/java/speiger/src/coreengine/rendering/utils/states/LineSize.java new file mode 100644 index 0000000..bb36f6a --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/states/LineSize.java @@ -0,0 +1,25 @@ +package speiger.src.coreengine.rendering.utils.states; + +import org.lwjgl.opengl.GL11; + +public class LineSize extends FloatState +{ + float value = 1F; + + public LineSize() + { + super(1F); + } + + @Override + protected String getName() + { + return "GL_Line_Width"; + } + + @Override + protected void setValue(float value) + { + GL11.glLineWidth(value); + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/states/PointSize.java b/src/main/java/speiger/src/coreengine/rendering/utils/states/PointSize.java new file mode 100644 index 0000000..bf1a1b6 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/rendering/utils/states/PointSize.java @@ -0,0 +1,23 @@ +package speiger.src.coreengine.rendering.utils.states; + +import org.lwjgl.opengl.GL11; + +public class PointSize extends FloatState +{ + public PointSize() + { + super(1F); + } + + @Override + protected String getName() + { + return "GL_Point_Size"; + } + + @Override + protected void setValue(float value) + { + GL11.glPointSize(value); + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/CollectionUtils.java b/src/main/java/speiger/src/coreengine/utils/collections/CollectionUtils.java new file mode 100644 index 0000000..c5b6a1c --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/CollectionUtils.java @@ -0,0 +1,54 @@ +package speiger.src.coreengine.utils.collections; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import speiger.src.collections.ints.maps.impl.hash.Int2ObjectLinkedOpenHashMap; +import speiger.src.collections.ints.maps.impl.hash.Int2ObjectOpenHashMap; +import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap; +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.sets.ObjectLinkedOpenHashSet; +import speiger.src.collections.objects.sets.ObjectOpenHashSet; + +public class CollectionUtils +{ + public static > K createSubCollection(List list, int[] indecies, K result) + { + for(int i = 0;i Set[] createSets(int size, boolean linked) + { + Set[] sets = new Set[size]; + for(int i = 0;i() : new ObjectOpenHashSet(); + } + return sets; + } + + public static List[] createList(int size) + { + List[] list = new List[size]; + for(int i = 0;i(); + } + return list; + } + + public static Int2ObjectMap[] createInt2ObjectMap(int size, boolean linked) + { + Int2ObjectMap[] maps = new Int2ObjectMap[size]; + for(int i = 0;i() : new Int2ObjectOpenHashMap(); + } + return maps; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/FlagHolder.java b/src/main/java/speiger/src/coreengine/utils/collections/FlagHolder.java new file mode 100644 index 0000000..0e323ff --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/FlagHolder.java @@ -0,0 +1,60 @@ +package speiger.src.coreengine.utils.collections; + +public class FlagHolder +{ + int flags; + + public FlagHolder() + { + } + + public FlagHolder(int initFlags) + { + flags = initFlags; + } + + public void setFlag(int flag) + { + flags |= flag; + } + + public boolean setFlag(int flag, boolean value) + { + if(isFlagSet(flag) == value) + { + return false; + } + flags = (value ? flags | flag : flags & ~(flag)); + return true; + } + + public int getFlags() + { + return flags; + } + + public void flipFlag(int flag) + { + flags ^= flag; + } + + public void clearFlag(int flag) + { + flags &= ~flag; + } + + public boolean isFlagSet(int flag) + { + return (flags & flag) == flag; + } + + public boolean isAnyFlagSet(int flag) + { + return (flags & flag) != 0; + } + + public boolean isFlagNotSet(int flag) + { + return (flags & flag) == 0; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/collection/BitArray.java b/src/main/java/speiger/src/coreengine/utils/collections/collection/BitArray.java new file mode 100644 index 0000000..1c69365 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/collection/BitArray.java @@ -0,0 +1,111 @@ +package speiger.src.coreengine.utils.collections.collection; + +public class BitArray +{ + long[] data; + int size; + int bits; + long maxValue; + + public BitArray(int size, int bits) + { + this.size = size; + this.bits = bits; + maxValue = (1L << bits) - 1L; + data = new long[arraySize(size * bits)]; + } + + public BitArray(int size, int bits, long[] data) + { + this.size = size; + this.bits = bits; + maxValue = (1L << bits) - 1L; + int arraySize = arraySize(size * bits); + if(data == null || data.length != arraySize) + { + this.data = new long[arraySize]; + if(data.length < arraySize) + { + System.arraycopy(data, 0, this.data, 0, Math.min(data.length, arraySize)); + } + } + else + { + this.data = data; + } + } + + private int arraySize(int entries) + { + int left = entries % 64; + return left == 0 ? entries / 64 : (entries / 64) + 1; + } + + public void set(int index, long value) + { + if(index < 0 || index >= size) throw new IllegalStateException("Out ouf Bounds"); + + int arrayIndex = (index * bits) / 64; + int bitIndex = (index * bits) % 64; + data[arrayIndex] = data[arrayIndex] & ~(maxValue << bitIndex) | (value & maxValue) << bitIndex; + int left = 64 - bitIndex; + if(left < bits) + { + int leftover = bits - left; + data[arrayIndex+1] = data[arrayIndex+1] >>> leftover << leftover | (value & maxValue) >> left; + } + } + + public void clear(int index) + { + set(index, 0L); + } + + public long get(int index) + { + if(index < 0 || index >= size) throw new IllegalStateException("Out ouf Bounds"); + int arrayIndex = (index * bits) / 64; + int bitIndex = (index * bits) % 64; + return (64 - bitIndex < bits ? (data[arrayIndex] >>> bitIndex | data[arrayIndex+1] << (64 - bitIndex)) : (data[arrayIndex] >>> bitIndex)) & maxValue; + } + + public int getInt(int index) + { + return (int)get(index); + } + + public int swapInt(int index, int newValue) + { + int value = getInt(index); + if(newValue != value) + { + set(index, newValue); + } + return value; + } + + public long swap(int index, long newValue) + { + long value = get(index); + if(newValue != value) + { + set(index, newValue); + } + return value; + } + + public int size() + { + return size; + } + + public int bits() + { + return bits; + } + + public long[] getData() + { + return data; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/collection/FixedBitSet.java b/src/main/java/speiger/src/coreengine/utils/collections/collection/FixedBitSet.java new file mode 100644 index 0000000..3d05fb9 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/collection/FixedBitSet.java @@ -0,0 +1,290 @@ +package speiger.src.coreengine.utils.collections.collection; + +import speiger.src.collections.ints.collections.IntCollection; +import speiger.src.coreengine.math.MathUtils; + +public class FixedBitSet +{ + final long[] data; + final int capacity; + int storedBits = -1; + int wordsInUse = 0; + + public FixedBitSet(int maxBits) + { + capacity = maxBits; + data = new long[MathUtils.ceil(maxBits / 8D)]; + } + + public FixedBitSet(long[] data) + { + this(data.length * 8, data); + } + + public FixedBitSet(int maxBits, long[] data) + { + this.data = data; + capacity = maxBits; + wordsInUse = data.length - 1; + validateWords(); + } + + public void set(boolean value) + { + if(value) + { + set(); + return; + } + clear(); + } + + public void set(int index, boolean value) + { + if(value) + { + set(index); + return; + } + clear(index); + } + + public void set(int fromIndex, int toIndex, boolean value) + { + if(value) + { + set(fromIndex, toIndex); + return; + } + clear(fromIndex, toIndex); + } + + public boolean ifSet(int index, boolean value) + { + return value ? ifSet(index) : ifClear(index); + } + + public void clear() + { + for(int i = 0;i < wordsInUse;i++) + { + data[wordsInUse] = 0; + } + wordsInUse = 0; + storedBits = 0; + } + + public void clear(int index) + { + if(index >= 0 && index < capacity) + { + data[index >> 6] &= ~(1L << index); + validateWords(); + } + } + + public void clear(int fromIndex, int toIndex) + { + if(fromIndex < 0 || toIndex < 0 || fromIndex >= capacity || toIndex >= capacity || fromIndex >= toIndex) + { + return; + } + int startWord = fromIndex >> 6; + if(startWord >= wordsInUse) + { + return; + } + int endWord = (toIndex - 1) >> 6; + if(endWord >= wordsInUse) + { + toIndex = length(); + endWord = wordsInUse - 1; + } + long startIndex = -1 << fromIndex; + long endIndex = -1 >>> -toIndex; + if(startWord == endWord) + { + data[startWord] &= ~(startIndex & endIndex); + } + else + { + data[startWord] &= ~startIndex; + for(int i = startWord + 1;i < endWord;i++) + { + data[i] = 0; + } + data[endWord] &= ~endIndex; + } + validateWords(); + } + + public boolean ifClear(int index) + { + if(index >= 0 && index < capacity) + { + long bit = 1L << index; + int wordIndex = index >> 6; + if((data[wordIndex] & bit) != 0) + { + data[wordIndex] &= ~bit; + validateWords(); + return true; + } + } + return false; + } + + public void set() + { + for(int i = 0;i < data.length;i++) + { + data[i] = -1; + } + wordsInUse = data.length - 1; + storedBits = capacity; + } + + public void set(int index) + { + if(index >= 0 && index < capacity) + { + data[index >> 6] |= 1L << index; + updateWords(index >> 6); + } + } + + public void set(int fromIndex, int toIndex) + { + if(fromIndex < 0 || toIndex < 0 || fromIndex >= capacity || toIndex >= capacity || fromIndex >= toIndex) + { + return; + } + int startWord = fromIndex >> 6; + int endWord = (toIndex - 1) >> 6; + long startIndex = -1 << fromIndex; + long endIndex = -1 >>> -toIndex; + if(startWord == endWord) + { + data[startWord] |= (startIndex & endIndex); + } + else + { + data[startWord] |= startIndex; + for(int i = startWord + 1;i < endWord;i++) + { + data[i] = -1; + } + data[endWord] |= endIndex; + } + updateWords(endWord); + } + + public boolean ifSet(int index) + { + if(index >= 0 && index < capacity) + { + long bit = 1L << index; + int wordIndex = index >> 6; + if((data[wordIndex] & bit) == 0) + { + data[wordIndex] |= bit; + updateWords(wordIndex); + return true; + } + } + return false; + } + + public boolean get(int index) + { + return (data[index >> 6] & (1L << index)) != 0; + } + + protected void validateWords() + { + for(;wordsInUse >= 0;wordsInUse--) + { + if(data[wordsInUse] == 0) + { + wordsInUse--; + } + } + wordsInUse++; + storedBits = -1; + } + + protected void updateWords(int check) + { + if(wordsInUse < check) + { + wordsInUse = check; + } + storedBits = -1; + } + + public boolean isEmpty() + { + return wordsInUse == 0; + } + + public boolean isFull() + { + return cardinality() == capacity; + } + + public int cardinality() + { + if(storedBits != -1) + { + return storedBits; + } + int sum = 0; + for(int i = 0;i < wordsInUse;i++) + { + sum += Long.bitCount(data[i]); + } + storedBits = sum; + return sum; + } + + public int length() + { + return wordsInUse == 0 ? 0 : 64 * (wordsInUse - 1) + (64 - Long.numberOfLeadingZeros(data[wordsInUse - 1])); + } + + public long[] getData() + { + return data.clone(); + } + + public FixedBitSet trim() + { + if(wordsInUse == 0) + { + return new FixedBitSet(1); + } + FixedBitSet set = new FixedBitSet(length()); + System.arraycopy(data, 0, set.data, 0, set.data.length); + set.wordsInUse = wordsInUse; + return set; + } + + public void pourIndexes(IntCollection target) + { + if(wordsInUse == 0) + { + return; + } + for(int i = 0;i < wordsInUse;i++) + { + long word = data[i]; + int wordBits = i << 6; + while(word != 0) + { + long t = word & -word; + target.add(wordBits + Long.bitCount(t - 1)); + word ^= t; + } + } + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/collection/Lazy.java b/src/main/java/speiger/src/coreengine/utils/collections/collection/Lazy.java new file mode 100644 index 0000000..7644b2b --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/collection/Lazy.java @@ -0,0 +1,40 @@ +package speiger.src.coreengine.utils.collections.collection; + +import java.util.function.Supplier; + +public class Lazy +{ + Supplier provider; + T value; + + public Lazy(Supplier provider) + { + this.provider = provider; + } + + public T get() + { + if(value == null) + { + value = provider.get(); + } + return value; + } + + public void clearValue() + { + value = null; + } + + public boolean hasValue() + { + return value != null; + } + + public T reload() + { + T oldValue = value; + value = provider.get(); + return oldValue; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/collection/SaveState.java b/src/main/java/speiger/src/coreengine/utils/collections/collection/SaveState.java new file mode 100644 index 0000000..7503bcd --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/collection/SaveState.java @@ -0,0 +1,77 @@ +package speiger.src.coreengine.utils.collections.collection; + +import java.util.Collection; +import java.util.List; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.utils.ObjectLists; + +public class SaveState +{ + ObjectArrayList entries = new ObjectArrayList(); + ObjectArrayList reverted = new ObjectArrayList(); + boolean lock = false; + + public void push(T entry) + { + if(lock) return; + entries.push(entry); + reverted.clear(); + } + + @SuppressWarnings("deprecation") + public void load(Collection toLoad, boolean applied) + { + (applied ? entries : reverted).addAll(toLoad); + } + + public T undo() + { + if(lock) throw new IllegalStateException("Save State Locked"); + if(entries.isEmpty()) + { + throw new IllegalStateException("No State Left"); + } + T value = entries.pop(); + reverted.push(value); + return value; + } + + public T redo() + { + if(lock) throw new IllegalStateException("Save State Locked"); + if(reverted.isEmpty()) + { + throw new IllegalStateException("Nothing to Undo"); + } + T value = reverted.pop(); + entries.push(value); + return value; + } + + public int index() + { + return entries.size(); + } + + public int max() + { + return entries.size() + reverted.size(); + } + + public List getEntries() + { + return ObjectLists.unmodifiable(entries); + } + + public List getReverted() + { + return ObjectLists.unmodifiable(reverted); + } + + public void clear() + { + reverted.clear(); + entries.clear(); + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/utils/collections/iterators/EnumIterator.java b/src/main/java/speiger/src/coreengine/utils/collections/iterators/EnumIterator.java new file mode 100644 index 0000000..6f1fa41 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/iterators/EnumIterator.java @@ -0,0 +1,27 @@ +package speiger.src.coreengine.utils.collections.iterators; + +import java.util.Enumeration; +import java.util.Iterator; + +public class EnumIterator implements Iterator +{ + Enumeration value; + + public EnumIterator(Enumeration value) + { + this.value = value; + } + + @Override + public boolean hasNext() + { + return value.hasMoreElements(); + } + + @Override + public T next() + { + return value.nextElement(); + } + +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/iterators/IndexIterator.java b/src/main/java/speiger/src/coreengine/utils/collections/iterators/IndexIterator.java new file mode 100644 index 0000000..279b23d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/iterators/IndexIterator.java @@ -0,0 +1,31 @@ +package speiger.src.coreengine.utils.collections.iterators; + +import java.util.Iterator; +import java.util.List; + +public class IndexIterator implements Iterator +{ + List list; + int[] indexes; + int index = 0; + + public IndexIterator(List list, int...indexes) + { + this.list = list; + this.indexes = indexes; + } + + @Override + public boolean hasNext() + { + return index < indexes.length; + } + + @Override + public T next() + { + return list.get(indexes[index++]); + } + + +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/iterators/IterableWrapper.java b/src/main/java/speiger/src/coreengine/utils/collections/iterators/IterableWrapper.java new file mode 100644 index 0000000..f83fb63 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/iterators/IterableWrapper.java @@ -0,0 +1,55 @@ +package speiger.src.coreengine.utils.collections.iterators; + +import java.io.BufferedReader; +import java.io.Reader; +import java.util.Enumeration; +import java.util.Iterator; + +import speiger.src.collections.objects.collections.ObjectBidirectionalIterator; + +public class IterableWrapper implements Iterable +{ + Iterator iter; + + protected IterableWrapper(Iterator value) + { + iter = value; + } + + public IterableWrapper asReadOnly() + { + iter = new ReadOnlyIterator(iter); + return this; + } + + @Override + public Iterator iterator() + { + return iter; + } + + public static IterableWrapper reverseWrap(ObjectBidirectionalIterator value) + { + return new IterableWrapper(new ReverseIterator(value)); + } + + public static IterableWrapper wrap(Iterator value) + { + return new IterableWrapper(value); + } + + public static IterableWrapper wrap(Enumeration value) + { + return new IterableWrapper(new EnumIterator(value)); + } + + public static IterableWrapper wrap(Reader reader) + { + return new IterableWrapper(new ReaderIterator(reader)); + } + + public static IterableWrapper wrap(BufferedReader reader) + { + return new IterableWrapper(new ReaderIterator(reader)); + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/iterators/ReadOnlyIterator.java b/src/main/java/speiger/src/coreengine/utils/collections/iterators/ReadOnlyIterator.java new file mode 100644 index 0000000..79485fd --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/iterators/ReadOnlyIterator.java @@ -0,0 +1,26 @@ +package speiger.src.coreengine.utils.collections.iterators; + +import java.util.Iterator; + +public class ReadOnlyIterator implements Iterator +{ + Iterator data; + + public ReadOnlyIterator(Iterator iter) + { + data = iter; + } + + @Override + public boolean hasNext() + { + return data.hasNext(); + } + + @Override + public T next() + { + return data.next(); + } + +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/iterators/ReaderIterator.java b/src/main/java/speiger/src/coreengine/utils/collections/iterators/ReaderIterator.java new file mode 100644 index 0000000..20f4f09 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/iterators/ReaderIterator.java @@ -0,0 +1,57 @@ +package speiger.src.coreengine.utils.collections.iterators; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.Iterator; + +public class ReaderIterator implements Iterator +{ + BufferedReader reader; + String value = null; + + public ReaderIterator(Reader reader) + { + this(new BufferedReader(reader)); + } + + public ReaderIterator(InputStream stream) + { + this(new InputStreamReader(stream)); + } + + public ReaderIterator(BufferedReader reader) + { + this.reader = reader; + } + + @Override + public boolean hasNext() + { + if(value != null) + { + return true; + } + try + { + value = reader.readLine(); + } + catch(IOException e) + { + e.printStackTrace(); + return false; + } + return value != null; + } + + @Override + public String next() + { + String result = value; + value = null; + return result; + } + +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/iterators/ReverseIterator.java b/src/main/java/speiger/src/coreengine/utils/collections/iterators/ReverseIterator.java new file mode 100644 index 0000000..0d78bfe --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/iterators/ReverseIterator.java @@ -0,0 +1,27 @@ +package speiger.src.coreengine.utils.collections.iterators; + +import java.util.Iterator; + +import speiger.src.collections.objects.collections.ObjectBidirectionalIterator; + +public class ReverseIterator implements Iterator +{ + ObjectBidirectionalIterator iter; + + public ReverseIterator(ObjectBidirectionalIterator iter) + { + this.iter = iter; + } + + @Override + public boolean hasNext() + { + return iter.hasPrevious(); + } + + @Override + public T next() + { + return iter.previous(); + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/managers/dynamic/DataSlot.java b/src/main/java/speiger/src/coreengine/utils/collections/managers/dynamic/DataSlot.java new file mode 100644 index 0000000..e43ff7e --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/managers/dynamic/DataSlot.java @@ -0,0 +1,64 @@ +package speiger.src.coreengine.utils.collections.managers.dynamic; + +public class DataSlot implements Comparable +{ + DataSlot previous; + DataSlot next; + int dataIndex = -1; + int byteOffset; + byte[] value; + + public DataSlot set(DataSlot previous, int dataIndex, byte[] value) + { + this.previous = previous; + byteOffset = previous == null ? 0 : previous.getNextByteOffset(); + this.dataIndex = dataIndex; + this.value = value; + if(this.previous != null) this.previous.next = this; + return this; + } + + public DataSlot setData(int dataIndex, byte[] value) + { + this.dataIndex = dataIndex; + this.value = value; + return this; + } + + @Override + public int compareTo(DataSlot o) + { + return Integer.compare(byteOffset, o.byteOffset); + } + + public int getNextByteOffset() + { + return byteOffset + (value == null ? 0 : value.length); + } + + public int getUsedBytes() + { + return next == null ? (value == null ? 0 : value.length) : next.byteOffset - byteOffset; + } + + public int getFreeBytes() + { + return next == null ? Integer.MAX_VALUE : next.byteOffset - byteOffset; + } + + public void clear() + { + dataIndex = -1; + value = null; + if(previous != null) + { + previous.next = null; + previous = null; + } + if(next != null) + { + next.previous = null; + next = null; + } + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/managers/dynamic/DynamicDataManager.java b/src/main/java/speiger/src/coreengine/utils/collections/managers/dynamic/DynamicDataManager.java new file mode 100644 index 0000000..195f178 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/managers/dynamic/DynamicDataManager.java @@ -0,0 +1,216 @@ +package speiger.src.coreengine.utils.collections.managers.dynamic; + +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import speiger.src.collections.ints.collections.IntIterator; +import speiger.src.collections.ints.maps.abstracts.AbstractInt2IntMap.BasicEntry; +import speiger.src.collections.ints.maps.abstracts.AbstractInt2ObjectMap; +import speiger.src.collections.ints.maps.interfaces.Int2IntMap; +import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap; +import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap.Entry; +import speiger.src.collections.ints.sets.IntAVLTreeSet; +import speiger.src.collections.ints.sets.IntSet; +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.sets.ObjectAVLTreeSet; +import speiger.src.collections.objects.utils.ObjectLists; +import speiger.src.coreengine.utils.collections.pools.ThreadPool; + +public class DynamicDataManager +{ + static final ThreadPool SLOTS = new ThreadPool<>(1000, DataSlot::new, DataSlot::clear); + final IDynamicDataHandler manager; + Int2ObjectMap slots = Int2ObjectMap.createLinkedMap(); + Set emptySlots = new ObjectAVLTreeSet<>(); + IntSet changedSlots = new IntAVLTreeSet(); + DataSlot lastSlot = null; + + public DynamicDataManager(IDynamicDataHandler manager) + { + this.manager = manager; + } + + public boolean add(int dataIndex, T value) + { + if(slots.containsKey(dataIndex)) return false; + return add(dataIndex, manager.toBytes(value)); + } + + protected boolean add(int dataIndex, byte[] data) + { + if(!emptySlots.isEmpty()) + { + for(Iterator iter = emptySlots.iterator();iter.hasNext();) + { + DataSlot slot = iter.next(); + if(slot.getFreeBytes() >= data.length) + { + slot.setData(dataIndex, data); + slots.put(dataIndex, slot); + changedSlots.add(dataIndex); + return true; + } + } + } + DataSlot next = SLOTS.get(); + next.set(lastSlot, dataIndex, data); + slots.put(dataIndex, next); + changedSlots.add(dataIndex); + lastSlot = next; + return true; + } + + public boolean contains(int dataIndex) + { + return slots.containsKey(dataIndex); + } + + public boolean update(int dataIndex, T value) + { + DataSlot slot = slots.get(dataIndex); + if(slot != null) + { + byte[] data = manager.toBytes(value); + if(slot.getFreeBytes() >= data.length) + { + slot.setData(dataIndex, data); + changedSlots.add(dataIndex); + return true; + } + else + { + slot.setData(-1, null); + slots.remove(dataIndex); + add(dataIndex, data); + emptySlots.add(slot); + return true; + } + } + return false; + } + + public Iterator iterator(int byteOffset) + { + return new Iterator() { + BasicEntry entry = new BasicEntry(); + DataSlot current = findNextSlot(lastSlot); + @Override + public boolean hasNext() + { + return current != null; + } + + DataSlot findNextSlot(DataSlot input) + { + for(input = input.previous; input != null && input.value == null;input = input.previous); + return input; + } + + @Override + public Int2IntMap.Entry next() + { + if(!hasNext()) throw new IllegalStateException(); + entry.set(current.dataIndex, current.byteOffset / byteOffset); + current = findNextSlot(current); + return entry; + } + }; + } + + public boolean remove(int dataIndex) + { + DataSlot slot = slots.remove(dataIndex); + if(slot != null) + { + slot.setData(-1, null); + emptySlots.add(slot); + return true; + } + return false; + } + + protected void clearEmptySlots() + { + while(lastSlot != null && lastSlot.value == null) + { + DataSlot prev = lastSlot; + emptySlots.remove(prev); + lastSlot = prev.previous; + SLOTS.accept(prev); + } + } + + public void clear() + { + slots.clear(); + emptySlots.clear(); + changedSlots.clear(); + while(lastSlot != null) + { + DataSlot prev = lastSlot; + lastSlot = prev.previous; + SLOTS.accept(prev); + } + } + + public void update() + { + int last = getEndSize(); + clearEmptySlots(); + if(changedSlots.isEmpty()) + { + if(last != getEndSize()) + { + manager.uploadBytes(ObjectLists.empty(), getEndSize()); + } + return; + } + List> list = new ObjectArrayList<>(); + while(!changedSlots.isEmpty()) + { + DataSlot start = null; + DataSlot current = null; + int bytesAllocated = 0; + for(IntIterator iter = changedSlots.iterator();iter.hasNext();) + { + DataSlot slot = slots.get(iter.nextInt()); + iter.remove(); + if(slot == null) break; + if(current == null || current.next == slot) + { + current = slot; + if(start == null) start = slot; + bytesAllocated += slot.getUsedBytes(); + continue; + } + break; + } + if(bytesAllocated <= 0) continue; + byte[] data = new byte[bytesAllocated]; + int byteOffset = start.byteOffset; + bytesAllocated = 0; + while(start.byteOffset <= current.byteOffset); + { + System.arraycopy(start.value, 0, data, bytesAllocated, start.value.length); + bytesAllocated += start.getUsedBytes(); + start = start.next; + } + list.add(new AbstractInt2ObjectMap.BasicEntry<>(byteOffset, data)); + } + if(!list.isEmpty()) + { + manager.uploadBytes(list, getEndSize()); + } + } + + private int getEndSize() + { + return lastSlot == null ? 0 : lastSlot.getNextByteOffset(); + } + + public int size() + { + return slots.size(); + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/managers/dynamic/IDynamicDataHandler.java b/src/main/java/speiger/src/coreengine/utils/collections/managers/dynamic/IDynamicDataHandler.java new file mode 100644 index 0000000..fa34aeb --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/managers/dynamic/IDynamicDataHandler.java @@ -0,0 +1,11 @@ +package speiger.src.coreengine.utils.collections.managers.dynamic; + +import java.util.List; + +import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap.Entry; + +public interface IDynamicDataHandler +{ + public byte[] toBytes(T entry); + public void uploadBytes(List> list, int newArraySize); +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/managers/fixed/FixedDataManager.java b/src/main/java/speiger/src/coreengine/utils/collections/managers/fixed/FixedDataManager.java new file mode 100644 index 0000000..5f69bea --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/managers/fixed/FixedDataManager.java @@ -0,0 +1,358 @@ +package speiger.src.coreengine.utils.collections.managers.fixed; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.List; +import java.util.concurrent.Callable; + +import speiger.src.collections.ints.collections.IntCollection; +import speiger.src.collections.ints.collections.IntIterator; +import speiger.src.collections.ints.functions.IntComparator; +import speiger.src.collections.ints.lists.IntArrayList; +import speiger.src.collections.ints.lists.IntList; +import speiger.src.collections.ints.maps.abstracts.AbstractInt2ObjectMap.BasicEntry; +import speiger.src.collections.ints.maps.impl.hash.Int2IntOpenHashMap; +import speiger.src.collections.ints.maps.interfaces.Int2IntMap; +import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap.Entry; +import speiger.src.collections.ints.sets.IntAVLTreeSet; +import speiger.src.collections.ints.sets.IntLinkedOpenHashSet; +import speiger.src.collections.ints.sets.IntSet; +import speiger.src.collections.ints.sets.IntSortedSet; +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.utils.ObjectArrays; +import speiger.src.collections.objects.utils.ObjectLists; +import speiger.src.coreengine.utils.collections.pools.ThreadPool; + +public class FixedDataManager +{ + static final ThreadPool SLOTS = new ThreadPool<>(1000, FixedSlot::new); + + final int bytesPerEntry; + final IFixedDataHandler manager; + Int2IntMap slots = new Int2IntOpenHashMap(); + IntArrayList slotData = new IntArrayList(); + IntSortedSet freeSlots = new IntAVLTreeSet(); + IntSet changes = new IntLinkedOpenHashSet(); + + public FixedDataManager(int bytesPerEntry, IFixedDataHandler manager) + { + this.bytesPerEntry = bytesPerEntry; + this.manager = manager; + slots.setDefaultReturnValue(-1); + } + + public boolean add(int dataIndex) + { + if(dataIndex == -1) + { + throw new IllegalStateException("-1 isnt allowed to be a dataIndex"); + } + if(slots.containsKey(dataIndex)) + { + return false; + } + if(freeSlots.isEmpty()) + { + slots.put(dataIndex, slotData.size()); + slotData.add(dataIndex); + changes.add(dataIndex); + return true; + } + int slot = freeSlots.pollFirstInt(); + slots.put(dataIndex, slot); + slotData.set(slot, dataIndex); + changes.add(dataIndex); + return true; + } + + public void addAll(IntCollection indexes) + { + for(int index : indexes) + { + add(index); + } + } + + public boolean contains(int dataIndex) + { + return slots.containsKey(dataIndex); + } + + public boolean update(int dataIndex) + { + if(slots.containsKey(dataIndex)) + { + changes.add(dataIndex); + return true; + } + return false; + } + + public void updateAll(IntCollection indexes) + { + for(int index : indexes) + { + if(slots.containsKey(index)) + { + changes.add(index); + } + } + } + + public boolean remove(int dataIndex) + { + int slot = slots.remove(dataIndex); + if(slot >= 0 && slot < slotData.size()) + { + slotData.set(slot, -1); + freeSlots.add(slot); + changes.remove(dataIndex); + return true; + } + return false; + } + + public void removeAll(IntCollection indexes) + { + for(int index : indexes) + { + remove(index); + } + } + + public void clear() + { + changes.clear(); + slots.clear(); + freeSlots.clear(); + slotData.clear(); + } + + public void reload(boolean now) + { + if(slots.isEmpty()) + { + return; + } + changes.addAll(slots.keySet()); + if(now) + { + updateNow(); + } + } + + public int size() + { + return slots.size(); + } + + public IntIterator valueIterator() + { + return slotData.iterator(); + } + + public void copy(FixedDataManager manager) + { + IntList list = manager.slotData; + for(int i = 0,m=list.size();i call = update(); + if(call != null) + { + call.call().run(); + } + } + catch(Exception e){} + } + + public Callable update() + { + int lastSize = slotData.size(); + clearEmptySlots(); + if(changes.isEmpty()) + { + if(slotData.size() != lastSize) + { + return new ShrinkTask(new FinishingTask(manager, ObjectLists.empty(), size())); + } + return null; + } + FixedSlot[] slots = SLOTS.get(new FixedSlot[changes.size()]); + int index = 0; + for(int dataIndex : changes) + { + slots[index++].set(dataIndex, this.slots.get(dataIndex)); + } + changes.clear(); + return new ProcessTask(slots, size(), bytesPerEntry, manager); + } + + protected void clearEmptySlots() + { + if(freeSlots.isEmpty()) + { + return; + } + if(slots.isEmpty()) + { + freeSlots.clear(); + slotData.clear(); + changes.clear(); + return; + } + int size = freeSlots.size(); + while(size > 0) + { + size -= ensureData(); + if(slotData.isEmpty() || freeSlots.isEmpty()) + { + break; + } + int index = freeSlots.pollFirstInt(); + int dataIndex = slotData.pop(); + slotData.set(index, dataIndex); + slots.put(dataIndex, index); + changes.add(dataIndex); + } + } + + protected int ensureData() + { + int cleared = 0; + while(slotData.top() == -1) + { + freeSlots.remove(slotData.size() - 1); + slotData.pop(); + cleared++; + } + return cleared; + } + + static class ProcessTask implements Callable + { + FixedSlot[] slots; + int size; + int bytes; + IFixedDataHandler handler; + + public ProcessTask(FixedSlot[] slots, int size, int bytes, IFixedDataHandler handler) + { + this.slots = slots; + this.size = size; + this.bytes = bytes; + this.handler = handler; + } + + @Override + public Runnable call() + { + ObjectArrays.quickSort(slots); + List fixed = new ObjectArrayList(); + List> data = new ObjectArrayList>(); + FixedSlot first = null; + FixedSlot last = null; + for(int i = 0,m=slots.length;i(first.index * bytes, buffer.array())); + i--; + first = last = null; + } + if(first != null) + { + ByteBuffer buffer = ByteBuffer.allocate(fixed.size() * bytes).order(ByteOrder.nativeOrder()); + handler.createData(buffer, fixed.size(), fixed); + fixed.clear(); + data.add(new BasicEntry(first.index * bytes, buffer.array())); + } + SLOTS.accept(slots); + return new FinishingTask(handler, data, size); + } + } + + static class ShrinkTask implements Callable + { + Runnable run; + + public ShrinkTask(Runnable run) + { + this.run = run; + } + + @Override + public Runnable call() throws Exception + { + return run; + } + } + + static class FinishingTask implements Runnable + { + IFixedDataHandler handler; + List> data; + int size; + + public FinishingTask(IFixedDataHandler handler, List> data, int size) + { + this.handler = handler; + this.data = data; + this.size = size; + } + + @Override + public void run() + { + handler.updateData(data, size); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/managers/fixed/FixedSlot.java b/src/main/java/speiger/src/coreengine/utils/collections/managers/fixed/FixedSlot.java new file mode 100644 index 0000000..1f80994 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/managers/fixed/FixedSlot.java @@ -0,0 +1,40 @@ +package speiger.src.coreengine.utils.collections.managers.fixed; + +public class FixedSlot implements Comparable +{ + int index; + int data; + + public void set(int data, int index) + { + this.data = data; + this.index = index; + } + + public boolean isNext(FixedSlot next) + { + return next.index - index == 1; + } + + @Override + public String toString() + { + return "DataSlot[Index="+index+", Data="+data+"]"; + } + + @Override + public int compareTo(FixedSlot o) + { + return Integer.compareUnsigned(index, o.index); + } + + public int getData() + { + return data; + } + + public int getIndex() + { + return index; + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/utils/collections/managers/fixed/IFixedDataHandler.java b/src/main/java/speiger/src/coreengine/utils/collections/managers/fixed/IFixedDataHandler.java new file mode 100644 index 0000000..110abf0 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/managers/fixed/IFixedDataHandler.java @@ -0,0 +1,13 @@ +package speiger.src.coreengine.utils.collections.managers.fixed; + +import java.nio.ByteBuffer; +import java.util.List; + +import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap.Entry; + +public interface IFixedDataHandler +{ + public void createData(ByteBuffer data, int size, List slots); + + public void updateData(List> list, int newSize); +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/utils/collections/pools/FixedPool.java b/src/main/java/speiger/src/coreengine/utils/collections/pools/FixedPool.java new file mode 100644 index 0000000..4924f65 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/pools/FixedPool.java @@ -0,0 +1,96 @@ +package speiger.src.coreengine.utils.collections.pools; + +import java.util.Collection; +import java.util.Iterator; +import java.util.function.Consumer; + +import speiger.src.coreengine.utils.functions.Functions; + +public class FixedPool implements IPool +{ + T[] stack; + int stored; + Consumer acceptor; + + public FixedPool(T[] stack) + { + this(stack, 0); + } + + public FixedPool(T[] stack, int stored) + { + this(stack, stored, Functions.getVoidConsumer()); + } + + public FixedPool(T[] stack, Consumer acceptor) + { + this(stack, 0, acceptor); + } + + public FixedPool(T[] stack, int stored, Consumer acceptor) + { + this.stack = stack; + this.stored = stored; + this.acceptor = acceptor; + } + + @Override + public T get() + { + if(stored <= 0) + { + throw new IllegalStateException("Pool is empty"); + } + T result = stack[stored]; + stack[stored--] = null; + return result; + } + + @Override + public T[] get(T[] input) + { + if(stored < input.length) + { + throw new IllegalStateException("Pool is to Small"); + } + for(int i = 0,m=input.length;i= stack.length) + { + throw new IllegalStateException("Pool is Full"); + } + acceptor.accept(t); + stack[stored++] = t; + } + + @Override + public void accept(T[] values) + { + for(int i = 0,m=Math.min(values.length, stack.length - stored);i values) + { + Iterator iter = values.iterator(); + for(int i = 0,m=Math.min(values.size(), stack.length - stored);i extends Consumer, Supplier +{ + public T[] get(T[] input); + + public void accept(T[] values); + + public void accept(Collection values); +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/pools/SimplePool.java b/src/main/java/speiger/src/coreengine/utils/collections/pools/SimplePool.java new file mode 100644 index 0000000..3199419 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/pools/SimplePool.java @@ -0,0 +1,79 @@ +package speiger.src.coreengine.utils.collections.pools; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.utils.functions.Functions; + +public class SimplePool implements IPool +{ + int cap; + List stack; + Supplier creator; + Consumer acceptor; + + public SimplePool(int size, Supplier creator) + { + this(size, creator, Functions.getVoidConsumer()); + } + + public SimplePool(int size, Supplier creator, Consumer acceptor) + { + cap = size; + stack = new ObjectArrayList(size); + this.creator = creator; + this.acceptor = acceptor; + } + + @Override + public void accept(T t) + { + if(stack.size() < cap) + { + stack.add(t); + acceptor.accept(t); + } + } + + @Override + public T get() + { + return stack.isEmpty() ? creator.get() : stack.remove(stack.size() - 1); + } + + @Override + public T[] get(T[] input) + { + for(int i = 0,m=input.length;i values) + { + Iterator iter = values.iterator(); + for(int i = 0,m=Math.min(values.size(), cap - stack.size());i implements IPool +{ + ThreadLocal> stack = ThreadLocal.withInitial(ObjectArrayList::new); + int cap; + Supplier creator; + Consumer acceptor; + + public ThreadLocalPool(int size, Supplier creator) + { + this(size, creator, Functions.getVoidConsumer()); + } + + public ThreadLocalPool(int size, Supplier creator, Consumer acceptor) + { + cap = size; + this.creator = creator; + this.acceptor = acceptor; + } + + @Override + public void accept(T t) + { + List list = stack.get(); + if(list.size() >= cap) return; + acceptor.accept(t); + list.add(t); + } + + @Override + public T get() + { + List list = stack.get(); + return list.isEmpty() ? creator.get() : list.remove(list.size() - 1); + } + + @Override + public T[] get(T[] input) + { + List list = stack.get(); + for(int i = 0,m=input.length;i list = stack.get(); + for(int i = 0,m=Math.min(array.length, cap - list.size());i values) + { + List list = stack.get(); + Iterator iter = values.iterator(); + for(int i = 0,m=Math.min(values.size(), cap - list.size());i implements IPool +{ + int cap; + List stack; + Supplier creator; + Consumer acceptor; + + public ThreadPool(int size, Supplier creator) + { + this(size, creator, Functions.getVoidConsumer()); + } + + public ThreadPool(int size, Supplier creator, Consumer acceptor) + { + cap = size; + stack = new ObjectArrayList(size); + this.creator = creator; + this.acceptor = acceptor; + } + + @Override + public synchronized void accept(T t) + { + if(stack.size() < cap) + { + stack.add(t); + acceptor.accept(t); + } + } + + @Override + public synchronized void accept(T[] array) + { + for(int i = 0,m=Math.min(array.length, cap - stack.size());i array) + { + Iterator iter = array.iterator(); + for(int i = 0,m=Math.min(array.size(), cap - stack.size());i void resolveDependencies(Object2ObjectMap map, Set values) + { + Map> results = new Object2ObjectLinkedOpenHashMap>(); + for(Object2ObjectMap.Entry entry : Object2ObjectMaps.fastIterable(map)) + { + results.put(entry.getKey(), new Entry(entry.getKey(), entry.getValue())); + } + for(Entry entry : results.values()) + { + Entry newer = results.get(entry.value); + if(newer != null) + { + newer.addChild(entry); + } + } + for(Entry entry : results.values()) + { + if(entry.isLoop()) + { + entry.clearLoop(values); + } + } + if(!results.entrySet().removeIf(T -> T.getValue().parent != null)) + { + return; + } + map.clear(); + for(Entry root : results.values()) + { + root.addToMap(map); + } + } + + static class Entry + { + T key; + T value; + Entry parent = null; + List> children = new ObjectArrayList>(); + + public Entry(T key, T value) + { + this.key = key; + this.value = value; + } + + public void addChild(Entry child) + { + if(child != null) + { + children.add(child); + child.parent = this; + } + } + + public void addToMap(Map map) + { + addToMap(value, map); + } + + protected void addToMap(T newValue, Map map) + { + map.put(key, newValue); + for(int i = 0,m=children.size();i current = parent; + while(current.parent != null) + { + current = current.parent; + if(current == parent) + { + return true; + } + } + return false; + } + + protected void clearLoop(Set keys) + { + if(keys.contains(value)) + { + parent = null; + return; + } + parent.clearLoop(keys); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/registries/flat/FlatRegistry.java b/src/main/java/speiger/src/coreengine/utils/collections/registries/flat/FlatRegistry.java new file mode 100644 index 0000000..ddd768b --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/registries/flat/FlatRegistry.java @@ -0,0 +1,60 @@ +package speiger.src.coreengine.utils.collections.registries.flat; + +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.utils.collections.registries.simple.SimpleRegistryWithDefault; + +public class FlatRegistry> extends SimpleRegistryWithDefault +{ + int usedCapacity = 0; + IFlatRegistryEntry[] flatIds; + + public FlatRegistry(int capacity, AssetLocation defaultValue) + { + super(capacity, defaultValue); + } + + @Override + public T register(T value) + { + int meta = value.getMetadata() + 1; + if(meta >= capacity - usedCapacity) + { + throw new RuntimeException("Registry Is Overflowed"); + } + T result = super.register(value); + usedCapacity += meta; + return result; + } + + public void bake() + { + freeze(); + flatIds = new IFlatRegistryEntry[usedCapacity]; + int index = 0; + for(T entry : this) + { + entry.setFlatId(index); + int meta = entry.getMetadata(); + for(int i = 0;i<=meta;i++) + { + flatIds[index+i] = entry; + } + index+=meta + 1; + } + } + + public T getFromFlatId(int id) + { + return (T)flatIds[id]; + } + + public int getMetadata(int id) + { + return id - flatIds[id].getFlatId(); + } + + public int flatSize() + { + return flatIds.length; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/registries/flat/FlatRegistryEntry.java b/src/main/java/speiger/src/coreengine/utils/collections/registries/flat/FlatRegistryEntry.java new file mode 100644 index 0000000..19155c0 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/registries/flat/FlatRegistryEntry.java @@ -0,0 +1,20 @@ +package speiger.src.coreengine.utils.collections.registries.flat; + +import speiger.src.coreengine.utils.collections.registries.simple.RegistryEntry; + +public abstract class FlatRegistryEntry> extends RegistryEntry implements IFlatRegistryEntry +{ + int flatId; + + @Override + public void setFlatId(int id) + { + this.flatId = id; + } + + @Override + public int getFlatId() + { + return flatId; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/registries/flat/IFlatRegistryEntry.java b/src/main/java/speiger/src/coreengine/utils/collections/registries/flat/IFlatRegistryEntry.java new file mode 100644 index 0000000..49a625a --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/registries/flat/IFlatRegistryEntry.java @@ -0,0 +1,11 @@ +package speiger.src.coreengine.utils.collections.registries.flat; + +import speiger.src.coreengine.utils.collections.registries.simple.IRegistryEntry; + +public interface IFlatRegistryEntry> extends IRegistryEntry +{ + public int getMetadata(); + + public void setFlatId(int id); + public int getFlatId(); +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/registries/mutable/IMutableRegistry.java b/src/main/java/speiger/src/coreengine/utils/collections/registries/mutable/IMutableRegistry.java new file mode 100644 index 0000000..7130dfb --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/registries/mutable/IMutableRegistry.java @@ -0,0 +1,12 @@ +package speiger.src.coreengine.utils.collections.registries.mutable; + +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.utils.collections.registries.simple.IRegistry; +import speiger.src.coreengine.utils.collections.registries.simple.IRegistryEntry; + +public interface IMutableRegistry> extends IRegistry +{ + public default boolean remove(T entry){return remove(getId(entry));} + public default boolean remove(AssetLocation name){return remove(getId(name));} + public boolean remove(int id); +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/registries/mutable/MutableRegistry.java b/src/main/java/speiger/src/coreengine/utils/collections/registries/mutable/MutableRegistry.java new file mode 100644 index 0000000..9961bf3 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/registries/mutable/MutableRegistry.java @@ -0,0 +1,127 @@ +package speiger.src.coreengine.utils.collections.registries.mutable; + +import java.util.BitSet; +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.lists.ObjectList; +import speiger.src.collections.objects.maps.impl.hash.Object2IntLinkedOpenHashMap; +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap; +import speiger.src.collections.objects.maps.interfaces.Object2IntMap; +import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap; +import speiger.src.collections.objects.utils.ObjectCollections; +import speiger.src.collections.objects.utils.ObjectSets; +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.utils.collections.iterators.ReadOnlyIterator; +import speiger.src.coreengine.utils.collections.registries.simple.IRegistryEntry; + +public class MutableRegistry> implements IMutableRegistry +{ + protected Object2IntMap nameToId = new Object2IntLinkedOpenHashMap(); + protected Object2ObjectMap nameToValue = new Object2ObjectLinkedOpenHashMap(); + protected ObjectList idsToName = new ObjectArrayList(); + protected BitSet freeIds; + protected int capacity; + protected int stored; + + public MutableRegistry(int capacity) + { + this.capacity = capacity; + freeIds = new BitSet(capacity + 1); + nameToId.setDefaultReturnValue(-1); + } + + @Override + public T register(T value) + { + if(value.getName() == null) + { + throw new IllegalStateException(value+", is Missing a name"); + } + AssetLocation name = value.getName(); + if(nameToValue.get(name) != null) + { + throw new IllegalStateException("Name is already registered [Name="+name+", Tile="+nameToValue.get(name)+"]"); + } + int freeBit = freeIds.nextClearBit(0); + if(capacity > 0 && freeBit >= capacity) + { + throw new IllegalStateException("Registry is full"); + } + while(idsToName.size() <= freeBit) + { + idsToName.add(null); + } + freeIds.set(freeBit); + idsToName.set(freeBit, name); + nameToValue.put(name, value); + nameToId.put(name, freeBit); + value.setId(this, freeBit); + stored++; + return value; + } + + @Override + public boolean remove(int id) + { + if(freeIds.get(id)) + { + freeIds.clear(id); + AssetLocation name = idsToName.get(id); + idsToName.set(id, null); + nameToId.rem(name); + nameToValue.remove(name).setId(null, -1); + stored--; + return true; + } + return false; + } + + @Override + public int getId(AssetLocation name) + { + return nameToId.getInt(name); + } + + @Override + public AssetLocation getName(int id) + { + return idsToName.get(id); + } + + @Override + public T getValue(AssetLocation name) + { + return nameToValue.get(name); + } + + public boolean isEmpty() + { + return stored <= 0; + } + + public int size() + { + return stored; + } + + @Override + public Set keySet() + { + return ObjectSets.unmodifiable(nameToId.keySet()); + } + + @Override + public Collection values() + { + return ObjectCollections.unmodifiable(nameToValue.values()); + } + + @Override + public Iterator iterator() + { + return new ReadOnlyIterator(nameToValue.values().iterator()); + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/IRegistry.java b/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/IRegistry.java new file mode 100644 index 0000000..39ae2fe --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/IRegistry.java @@ -0,0 +1,23 @@ +package speiger.src.coreengine.utils.collections.registries.simple; + +import java.util.Collection; +import java.util.Set; + +import speiger.src.coreengine.assets.AssetLocation; + +public interface IRegistry> extends Iterable +{ + public T register(T value); + + public default int getId(T value){return getId(value.getName());} + public int getId(AssetLocation name); + public AssetLocation getName(int id); + public T getValue(AssetLocation name); + public default T getValue(int id){return getValue(getName(id));} + + public default boolean contains(AssetLocation name){return getValue(name) != null;} + public default boolean contains(T value){return contains(value.getName());} + + public Set keySet(); + public Collection values(); +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/IRegistryEntry.java b/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/IRegistryEntry.java new file mode 100644 index 0000000..40a47b4 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/IRegistryEntry.java @@ -0,0 +1,13 @@ +package speiger.src.coreengine.utils.collections.registries.simple; + +import speiger.src.coreengine.assets.AssetLocation; + +public interface IRegistryEntry> +{ + public AssetLocation getName(); + + public default T setName(String domain, String name) { return setName(AssetLocation.of(domain, name));} + public T setName(AssetLocation name); + + public void setId(IRegistry registry, int registryId); +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/RegistryEntry.java b/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/RegistryEntry.java new file mode 100644 index 0000000..2ffaeed --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/RegistryEntry.java @@ -0,0 +1,33 @@ +package speiger.src.coreengine.utils.collections.registries.simple; + +import speiger.src.coreengine.assets.AssetLocation; + +public abstract class RegistryEntry> implements IRegistryEntry +{ + AssetLocation location; + int id; + + @Override + public AssetLocation getName() + { + return location; + } + + @Override + public T setName(AssetLocation name) + { + location = name; + return (T)this; + } + + @Override + public void setId(IRegistry registry, int registryId) + { + this.id = registryId; + } + + public int getRegistryId() + { + return id; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/SimpleRegistry.java b/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/SimpleRegistry.java new file mode 100644 index 0000000..ef4d99f --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/SimpleRegistry.java @@ -0,0 +1,133 @@ +package speiger.src.coreengine.utils.collections.registries.simple; + +import java.util.BitSet; +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.lists.ObjectList; +import speiger.src.collections.objects.maps.impl.hash.Object2IntLinkedOpenHashMap; +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap; +import speiger.src.collections.objects.maps.interfaces.Object2IntMap; +import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap; +import speiger.src.collections.objects.utils.ObjectCollections; +import speiger.src.collections.objects.utils.ObjectSets; +import speiger.src.coreengine.assets.AssetLocation; +import speiger.src.coreengine.utils.collections.iterators.ReadOnlyIterator; + +public class SimpleRegistry> implements IRegistry +{ + protected Object2IntMap nameToId = new Object2IntLinkedOpenHashMap(); + protected Object2ObjectMap nameToValue = new Object2ObjectLinkedOpenHashMap(); + protected ObjectList idsToName = new ObjectArrayList(); + protected BitSet freeIds; + protected int capacity; + protected int stored; + + boolean frozen = false; + Object2IntMap missing = new Object2IntLinkedOpenHashMap(); + Object2ObjectMap remaps = new Object2ObjectLinkedOpenHashMap(); + + public SimpleRegistry(int capacity) + { + this.capacity = capacity; + freeIds = new BitSet(capacity + 1); + nameToId.setDefaultReturnValue(-1); + } + + @Override + public T register(T value) + { + if(frozen) + { + throw new RuntimeException("Registry is Frozen"); + } + if(value.getName() == null) + { + throw new IllegalStateException(value+", is Missing a name"); + } + AssetLocation name = value.getName(); + if(nameToValue.get(name) != null) + { + throw new IllegalStateException("Name is already registered [Name="+name+", Tile="+nameToValue.get(name)+"]"); + } + int freeBit = freeIds.nextClearBit(0); + if(capacity >= 0 && freeBit >= capacity) + { + throw new IllegalStateException("Registry is full"); + } + while(idsToName.size() <= freeBit) + { + idsToName.add(null); + } + freeIds.set(freeBit); + idsToName.set(freeBit, name); + nameToValue.put(name, value); + nameToId.put(name, freeBit); + value.setId(this, freeBit); + if(missing.size() > 0) + { + missing.remove(name); + } + stored++; + return value; + } + + @Override + public int getId(AssetLocation name) + { + return nameToId.getInt(remap(name)); + } + + @Override + public AssetLocation getName(int id) + { + return idsToName.get(id); + } + + @Override + public T getValue(AssetLocation name) + { + return nameToValue.get(remap(name)); + } + + public boolean isEmpty() + { + return stored <= 0; + } + + public int size() + { + return stored; + } + + @Override + public Set keySet() + { + return ObjectSets.unmodifiable(nameToId.keySet()); + } + + @Override + public Collection values() + { + return ObjectCollections.unmodifiable(nameToValue.values()); + } + + @Override + public Iterator iterator() + { + return new ReadOnlyIterator(nameToValue.values().iterator()); + } + + protected AssetLocation remap(AssetLocation location) + { + AssetLocation mapped = remaps.get(location); + return mapped == null ? location : mapped; + } + + public final void freeze() + { + frozen = true; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/SimpleRegistryWithDefault.java b/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/SimpleRegistryWithDefault.java new file mode 100644 index 0000000..2c425c4 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/collections/registries/simple/SimpleRegistryWithDefault.java @@ -0,0 +1,32 @@ +package speiger.src.coreengine.utils.collections.registries.simple; + +import speiger.src.coreengine.assets.AssetLocation; + +public class SimpleRegistryWithDefault> extends SimpleRegistry +{ + AssetLocation defaultKey; + T defaultValue; + public SimpleRegistryWithDefault(int capacity, AssetLocation defaultValue) + { + super(capacity); + this.defaultKey = defaultValue; + } + + @Override + public T register(T value) + { + T result = super.register(value); + if(defaultKey != null && result.getName().matches(defaultKey)) + { + defaultValue = result; + } + return result; + } + + @Override + public T getValue(AssetLocation name) + { + T value = super.getValue(name); + return value == null ? defaultValue : value; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/counters/averager/Averager.java b/src/main/java/speiger/src/coreengine/utils/counters/averager/Averager.java new file mode 100644 index 0000000..5f81cdf --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/counters/averager/Averager.java @@ -0,0 +1,59 @@ +package speiger.src.coreengine.utils.counters.averager; + +import speiger.src.collections.longs.queues.LongArrayFIFOQueue; +import speiger.src.collections.longs.queues.LongPriorityQueue; + +public class Averager +{ + LongPriorityQueue values; + long totalValue; + int totalCount; + + public Averager() + { + this(200); + } + + public Averager(int totalCount) + { + this.totalCount = totalCount; + values = new LongArrayFIFOQueue(totalCount + 1); + } + + public void addEntry(long time) + { + totalValue += time; + if(values.size() >= totalCount) + { + totalValue -= values.dequeue(); + } + values.enqueue(time); + } + + public void clear() + { + values.clear(); + totalValue = 0L; + } + + public long getAverage() + { + return (values.isEmpty() ? 0 : totalValue / values.size()); + } + + public boolean isEmpty() + { + return values.isEmpty(); + } + + public long getTotal() + { + return totalValue; + } + + @Override + public String toString() + { + return "Stamper[total="+totalValue+", average="+getAverage()+", count="+values.size()+", max="+totalCount+"]"; + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/utils/counters/averager/Counter.java b/src/main/java/speiger/src/coreengine/utils/counters/averager/Counter.java new file mode 100644 index 0000000..ed7282c --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/counters/averager/Counter.java @@ -0,0 +1,33 @@ +package speiger.src.coreengine.utils.counters.averager; + +public class Counter +{ + int value; + int lastValue; + + public void add(int value) + { + this.value += value; + } + + public void onFinished() + { + lastValue = value; + value = 0; + } + + public int get() + { + return lastValue; + } + + public static Counter[] createCounters(int count) + { + Counter[] counters = new Counter[count]; + for(int i = 0;i 10000000L) + { + for(int i = 0;i < slots.length;i++) + { + slots[i] *= 0.9F; + } + total *= 0.9F; + } + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/utils/counters/averager/TimeAverager.java b/src/main/java/speiger/src/coreengine/utils/counters/averager/TimeAverager.java new file mode 100644 index 0000000..d79bf61 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/counters/averager/TimeAverager.java @@ -0,0 +1,63 @@ +package speiger.src.coreengine.utils.counters.averager; + +import java.text.DecimalFormat; + +public class TimeAverager extends Averager +{ + private static final TimeAverager GLOBAL_TIME_STAMP = new TimeAverager(); + private static final DecimalFormat FORMAT = new DecimalFormat("###,###"); + + long start = 0L; + + public TimeAverager() + { + } + + public TimeAverager(int count) + { + super(count); + } + + public void stop() + { + addEntry(System.nanoTime() - start); + } + + public void start() + { + start = System.nanoTime(); + } + + @Override + public String toString() + { + long time = getAverage(); + return "Time Stamp: "+FORMAT.format(time / 1000000L)+"ms, ("+FORMAT.format(time / 1000L)+"qs, "+FORMAT.format(time)+"ns)"; + } + + public static void startStamp() + { + GLOBAL_TIME_STAMP.start(); + } + + public static void stopStamp() + { + GLOBAL_TIME_STAMP.stop(); + } + + public static long getAverageTime() + { + return GLOBAL_TIME_STAMP.getAverage(); + } + + public static String print() + { + return GLOBAL_TIME_STAMP.toString(); + } + + public static void reset() + { + GLOBAL_TIME_STAMP.values.clear(); + GLOBAL_TIME_STAMP.totalValue = 0L; + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/utils/counters/timers/CountdownSync.java b/src/main/java/speiger/src/coreengine/utils/counters/timers/CountdownSync.java new file mode 100644 index 0000000..10a1663 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/counters/timers/CountdownSync.java @@ -0,0 +1,37 @@ +package speiger.src.coreengine.utils.counters.timers; + +import speiger.src.coreengine.utils.counters.averager.RunningAvg; + +public class CountdownSync +{ + boolean initialised = false; + RunningAvg sleepDurations = new RunningAvg(10); + RunningAvg yieldDurations = new RunningAvg(10); + + public void sync(long expectedNanoTime) + { + if(!initialised) + { + initialised = true; + sleepDurations.init(1000000); + yieldDurations.init((int)(-(System.nanoTime() - System.nanoTime()) * 1.333)); + } + long nextFrame = System.nanoTime() + expectedNanoTime; + try + { + for(long t0 = System.nanoTime(), t1;(nextFrame - t0) > sleepDurations.avg();t0 = t1) + { + Thread.sleep(1); + sleepDurations.add((t1 = System.nanoTime()) - t0); + } + sleepDurations.dampenForLowResTicker(); + + for(long t0 = System.nanoTime(), t1;(nextFrame - t0) > yieldDurations.avg();t0 = t1) + { + Thread.yield(); + yieldDurations.add((t1 = System.nanoTime()) - t0); + } + } + catch(InterruptedException e) {} + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/counters/timers/FPSTimer.java b/src/main/java/speiger/src/coreengine/utils/counters/timers/FPSTimer.java new file mode 100644 index 0000000..074c0f5 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/counters/timers/FPSTimer.java @@ -0,0 +1,89 @@ +package speiger.src.coreengine.utils.counters.timers; + +import speiger.src.coreengine.utils.counters.averager.TimeAverager; + +public class FPSTimer +{ + long lastTime; + long lastDelta; + long delta_storage; + long time_storage; + float particalTicks; + + TimerTarget fps = new TimerTarget(60); + TimerTarget ups = new TimerTarget(20); + + TimeAverager mainUsage = new TimeAverager(); + + public void init() + { + lastTime = System.nanoTime(); + } + + public long getDelta() + { + mainUsage.start(); + long time = System.nanoTime(); + lastDelta = time; + return getDelta(time); + } + + protected long getDelta(long time) + { + long delta = time - lastTime; + lastTime = time; + delta_storage += delta; + time_storage += delta; + float value = (delta_storage / 1000000000F); + particalTicks = value - ((int)value); + return delta_storage; + } + + public long consumeDelta() + { + long time = System.nanoTime(); + delta_storage -= Math.max(ups.getTargetNS(), time - lastDelta); + float value = (delta_storage / 1000000000F); + particalTicks = value - ((int)value); + lastDelta = time; + return delta_storage; + } + + public boolean update() + { + mainUsage.stop(); + if(time_storage >= 1000000000L) + { + fps.update(); + ups.update(); + time_storage -= 1000000000L; + return true; + } + return false; + } + + public TimerTarget getFPS() + { + return fps; + } + + public TimerTarget getUPS() + { + return ups; + } + + public long getLastTime() + { + return lastTime; + } + + public float getParticalTime() + { + return particalTicks; + } + + public long getUsage() + { + return mainUsage.getAverage(); + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/counters/timers/FrameSyncer.java b/src/main/java/speiger/src/coreengine/utils/counters/timers/FrameSyncer.java new file mode 100644 index 0000000..9e9589c --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/counters/timers/FrameSyncer.java @@ -0,0 +1,51 @@ +package speiger.src.coreengine.utils.counters.timers; + +import speiger.src.coreengine.utils.counters.averager.RunningAvg; + +/** + * Found this code on the Internet here: http://forum.lwjgl.org/index.php?topic=6582.0 + * + * @author Riven + * @author kappaOne + * + * @author Speiger (Adjusted original code to work with my system) + */ +public class FrameSyncer +{ + long nextFrame = 0; + boolean initialised = false; + RunningAvg sleepDurations = new RunningAvg(10); + RunningAvg yieldDurations = new RunningAvg(10); + + public void sync(int fps) + { + if(fps <= 0) + { + return; + } + if(!initialised) + { + initialised = true; + sleepDurations.init(1000000); + yieldDurations.init((int)(-(System.nanoTime() - System.nanoTime()) * 1.333)); + nextFrame = System.nanoTime(); + } + try + { + for(long t0 = System.nanoTime(), t1;(nextFrame - t0) > sleepDurations.avg();t0 = t1) + { + Thread.sleep(1);// sleep time + sleepDurations.add((t1 = System.nanoTime()) - t0); // update average + } + sleepDurations.dampenForLowResTicker(); + + for(long t0 = System.nanoTime(), t1;(nextFrame - t0) > yieldDurations.avg();t0 = t1) + { + Thread.yield();// yield time + yieldDurations.add((t1 = System.nanoTime()) - t0); // update average + } + } + catch(InterruptedException e) {} + nextFrame = Math.max(nextFrame + 1000000000L / fps, System.nanoTime()); + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/utils/counters/timers/Stopwatch.java b/src/main/java/speiger/src/coreengine/utils/counters/timers/Stopwatch.java new file mode 100644 index 0000000..6add107 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/counters/timers/Stopwatch.java @@ -0,0 +1,59 @@ +package speiger.src.coreengine.utils.counters.timers; + +import java.util.concurrent.TimeUnit; + +public class Stopwatch +{ + long start; + long elapsedTime; + boolean running; + + public Stopwatch start() + { + if(running) + { + throw new IllegalStateException("Stopwatch is started"); + } + start = System.nanoTime(); + running = true; + return this; + } + + public Stopwatch stop() + { + if(!running) + { + throw new IllegalStateException("Stopwatch is stopped"); + } + running = false; + elapsedTime += System.nanoTime() - start; + return this; + } + + public Stopwatch reset() + { + running = false; + elapsedTime = 0L; + return this; + } + + public long getTime() + { + return running ? (System.nanoTime() - start) + elapsedTime : elapsedTime; + } + + public long getTime(TimeUnit unit) + { + return unit.convert(getTime(), TimeUnit.NANOSECONDS); + } + + public boolean isTimeLeft(TimeUnit unit, long time) + { + return getTime(unit) < time; + } + + public boolean isTimeLeft(long time) + { + return getTime() < time; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/counters/timers/Timer.java b/src/main/java/speiger/src/coreengine/utils/counters/timers/Timer.java new file mode 100644 index 0000000..cd620af --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/counters/timers/Timer.java @@ -0,0 +1,58 @@ +package speiger.src.coreengine.utils.counters.timers; + +import speiger.src.coreengine.utils.counters.averager.TimeAverager; + +public class Timer +{ + long lastTime; + long trackerTime; + long deltaLeft; + TimeAverager timer = new TimeAverager(); + TimerTarget target = new TimerTarget(20); + + public void init() + { + lastTime = System.nanoTime(); + trackerTime = lastTime; + } + + public long getDelta() + { + long time = System.nanoTime(); + deltaLeft += (time - lastTime); + lastTime = time; + timer.start(); + return deltaLeft; + } + + public long consumeDelta() + { + long time = System.nanoTime(); + deltaLeft -= Math.max(target.getTargetNS(), time - lastTime); + lastTime = time; + return deltaLeft; + } + + public boolean update() + { + timer.stop(); + long newTime = System.nanoTime(); + if(newTime - trackerTime >= 1000000000) + { + trackerTime = newTime; + target.update(); + return true; + } + return false; + } + + public TimerTarget getTarget() + { + return target; + } + + public long getUsage() + { + return timer.getAverage(); + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/counters/timers/TimerTarget.java b/src/main/java/speiger/src/coreengine/utils/counters/timers/TimerTarget.java new file mode 100644 index 0000000..354349b --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/counters/timers/TimerTarget.java @@ -0,0 +1,46 @@ +package speiger.src.coreengine.utils.counters.timers; + +import speiger.src.coreengine.utils.counters.averager.Counter; + +public class TimerTarget +{ + int targetTicks; + long targetNS; + Counter counter = new Counter(); + + public TimerTarget(int defaultValue) + { + setTarget(defaultValue); + } + + public void setTarget(int targetTicks) + { + this.targetTicks = targetTicks; + targetNS = 1000000000L / targetTicks; + } + + public int getTargetTicks() + { + return targetTicks; + } + + public long getTargetNS() + { + return targetNS; + } + + public void tick() + { + counter.add(1); + } + + public void update() + { + counter.onFinished(); + } + + public int getTicks() + { + return counter.get(); + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/eventbus/Event.java b/src/main/java/speiger/src/coreengine/utils/eventbus/Event.java new file mode 100644 index 0000000..b1ccf81 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/eventbus/Event.java @@ -0,0 +1,29 @@ +package speiger.src.coreengine.utils.eventbus; + +public class Event +{ + boolean canceled = false; + + public void setCanceled(boolean canceled) + { + if(isCancelable()) + { + this.canceled = canceled; + } + } + + public final void cancel() + { + setCanceled(true); + } + + public final boolean isCanceled() + { + return canceled; + } + + public boolean isCancelable() + { + return false; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/eventbus/EventBus.java b/src/main/java/speiger/src/coreengine/utils/eventbus/EventBus.java new file mode 100644 index 0000000..2dcc831 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/eventbus/EventBus.java @@ -0,0 +1,231 @@ +package speiger.src.coreengine.utils.eventbus; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectOpenHashMap; +import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap; +import speiger.src.collections.objects.utils.ObjectLists; +import speiger.src.collections.objects.utils.maps.Object2ObjectMaps; + +public class EventBus +{ + private static final Lookup LOOKUP = MethodHandles.lookup(); + Object2ObjectMap, Listeners> listeners = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap, Listeners>()); + Map> instances = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap>()); + + public void register(Class event, Consumer listener) + { + register(event, EventPriority.MEDIUM, listener); + } + + public void register(Class event, EventPriority priority, Consumer listener) + { + if(instances.containsKey(listener)) + { + return; + } + getListeners(event).addListener(priority, (Consumer)listener); + instances.put(listener, ObjectLists.singleton(new EventListener(event, (Consumer)listener))); + } + + public void register(Object obj) + { + register(obj, false); + } + + public void register(Object obj, boolean superClasses) + { + if(instances.containsKey(obj)) + { + return; + } + final List list = new ObjectArrayList(); + try + { + register(obj.getClass().getDeclaredAnnotation(SubscribeEvent.class), obj, list); + findMethods(obj.getClass(), superClasses, new Consumer() { + @Override + public void accept(Method t) + { + try + { + t.setAccessible(true); + SubscribeEvent data = t.getAnnotation(SubscribeEvent.class); + if(data == null) + { + return; + } + Consumer listener = new MethodListener(LOOKUP.unreflect(t).bindTo(obj)); + getListeners(data.event()).addListener(data.priority(), listener); + list.add(new EventListener(data.event(), listener)); + } + catch(Exception e) + { + e.printStackTrace(); + } + } + }); + findFields(obj.getClass(), superClasses, new Consumer(){ + @Override + public void accept(Field t) + { + try + { + t.setAccessible(true); + register(t.getAnnotation(SubscribeEvent.class), t.get(obj), list); + } + catch(Exception e) + { + e.printStackTrace(); + } + } + }); + } + catch(Exception e) + { + e.printStackTrace(); + } + if(list.isEmpty()) + { + return; + } + instances.put(obj, list); + } + + private void register(SubscribeEvent data, Object obj, List listeners) + { + if(data == null || !(obj instanceof Consumer)) + { + return; + } + getListeners(data.event()).addListener(data.priority(), (Consumer)obj); + listeners.add(new EventListener(data.event(), (Consumer)obj)); + } + + private void findFields(Class clz, boolean superClasses, Consumer fields) + { + do + { + for(Field field : clz.getDeclaredFields()) + { + fields.accept(field); + } + clz = clz.getSuperclass(); + } + while(clz != Object.class && superClasses); + } + + private void findMethods(Class clz, boolean superClasses, Consumer methods) + { + do + { + for(Method field : clz.getDeclaredMethods()) + { + methods.accept(field); + } + clz = clz.getSuperclass(); + } + while(clz != Object.class && superClasses); + } + + public void unregister(Object obj) + { + List instance = instances.remove(obj); + if(instance == null) + { + return; + } + for(EventListener entry : instance) + { + getListeners(entry.getEvent()).removeListeners(entry.getListener()); + } + } + + public void post(Event event) + { + Consumer[] listeners = getListeners(event.getClass()).getListeners(); + if(listeners.length <= 0) + { + return; + } + int index = 0; + try + { + for(;index event) + { + Listeners entry = listeners.get(event); + if(entry == null) + { + if(event == Event.class) entry = new Listeners(); + else entry = new Listeners(getListeners((Class)event.getSuperclass())); + listeners.put(event, entry); + } + return entry; + } + + + public static class EventListener + { + Class event; + Consumer listener; + + public EventListener(Class event, Consumer listener) + { + this.event = event; + this.listener = listener; + } + + public Class getEvent() + { + return event; + } + + public Consumer getListener() + { + return listener; + } + } + + public static class MethodListener implements Consumer + { + MethodHandle handle; + + public MethodListener(MethodHandle handle) + { + this.handle = handle; + } + + @Override + public void accept(Event t) + { + try + { + handle.invoke(t); + } + catch(Throwable e) + { + e.printStackTrace(); + } + } + + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/eventbus/EventPriority.java b/src/main/java/speiger/src/coreengine/utils/eventbus/EventPriority.java new file mode 100644 index 0000000..5000e21 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/eventbus/EventPriority.java @@ -0,0 +1,27 @@ +package speiger.src.coreengine.utils.eventbus; + +public enum EventPriority +{ + HIGH(0), + MEDIUM(1), + LOW(2); + + int priority; + + static final EventPriority[] PRIORITIES = new EventPriority[]{HIGH, MEDIUM, LOW}; + + private EventPriority(int id) + { + priority = id; + } + + public int getPriority() + { + return priority; + } + + public static EventPriority[] getPriorities() + { + return PRIORITIES; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/eventbus/Listeners.java b/src/main/java/speiger/src/coreengine/utils/eventbus/Listeners.java new file mode 100644 index 0000000..defda3e --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/eventbus/Listeners.java @@ -0,0 +1,103 @@ +package speiger.src.coreengine.utils.eventbus; + +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.sets.ObjectLinkedOpenHashSet; + +public class Listeners +{ + Set>[] unsortedListeners = new Set[EventPriority.values().length]; + Consumer[] listeners = new Consumer[0]; + boolean rebuild = true; + Listeners parent; + List childs = null; + + public Listeners() + { + for(int i = 0;i>(); + } + } + + public Listeners(Listeners parent) + { + this(); + this.parent = parent; + this.parent.addChild(this); + } + + public void addChild(Listeners listener) + { + if(childs == null) + { + childs = new ObjectArrayList(); + } + childs.add(listener); + } + + public void addListener(EventPriority priority, Consumer listener) + { + if(unsortedListeners[priority.getPriority()].add(listener)) + { + markDirty(); + } + } + + public void removeListeners(Consumer listener) + { + boolean found = false; + for(int i = 0,m=unsortedListeners.length;i> events) + { + events.addAll(unsortedListeners[entry.getPriority()]); + if(parent != null) + { + parent.getListeners(entry, events); + } + } + + public Consumer[] getListeners() + { + if(rebuild) rebuildListeners(); + return listeners; + } + + private void markDirty() + { + rebuild = true; + if(childs != null) + { + for(int i = 0,m=childs.size();i> result = new ObjectArrayList>(); + for(EventPriority entry : EventPriority.getPriorities()) + { + getListeners(entry, result); + } + listeners = result.toArray(new Consumer[result.size()]); + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/eventbus/SubscribeEvent.java b/src/main/java/speiger/src/coreengine/utils/eventbus/SubscribeEvent.java new file mode 100644 index 0000000..6c2d58f --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/eventbus/SubscribeEvent.java @@ -0,0 +1,18 @@ +package speiger.src.coreengine.utils.eventbus; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(RUNTIME) +@Target({TYPE, FIELD, METHOD}) +public @interface SubscribeEvent +{ + public EventPriority priority() default EventPriority.MEDIUM; + + public Class event(); +} diff --git a/src/main/java/speiger/src/coreengine/utils/functions/BiIntConsumer.java b/src/main/java/speiger/src/coreengine/utils/functions/BiIntConsumer.java new file mode 100644 index 0000000..41687ef --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/functions/BiIntConsumer.java @@ -0,0 +1,7 @@ +package speiger.src.coreengine.utils.functions; + +@FunctionalInterface +public interface BiIntConsumer +{ + public void accpet(int k, int v); +} diff --git a/src/main/java/speiger/src/coreengine/utils/functions/ConsumerConverter.java b/src/main/java/speiger/src/coreengine/utils/functions/ConsumerConverter.java new file mode 100644 index 0000000..44036bd --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/functions/ConsumerConverter.java @@ -0,0 +1,23 @@ +package speiger.src.coreengine.utils.functions; + +import java.util.function.Consumer; +import java.util.function.ObjIntConsumer; + +public class ConsumerConverter implements Consumer +{ + int value; + ObjIntConsumer receiver; + + public ConsumerConverter(int value, ObjIntConsumer listener) + { + this.value = value; + receiver = listener; + } + + @Override + public void accept(T t) + { + receiver.accept(t, value); + } + +} diff --git a/src/main/java/speiger/src/coreengine/utils/functions/FloatSupplier.java b/src/main/java/speiger/src/coreengine/utils/functions/FloatSupplier.java new file mode 100644 index 0000000..4d404f8 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/functions/FloatSupplier.java @@ -0,0 +1,7 @@ +package speiger.src.coreengine.utils.functions; + +@FunctionalInterface +public interface FloatSupplier +{ + public float getAsFloat(); +} diff --git a/src/main/java/speiger/src/coreengine/utils/functions/Functions.java b/src/main/java/speiger/src/coreengine/utils/functions/Functions.java new file mode 100644 index 0000000..9b57eb7 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/functions/Functions.java @@ -0,0 +1,32 @@ +package speiger.src.coreengine.utils.functions; + +import java.util.function.Consumer; +import java.util.function.Predicate; + +public class Functions +{ + static final Predicate ALWAYS_TRUE = (T) -> true; + static final Consumer EMPTY_CONSUMER = (T) -> {}; + public static final Predicate NUMBERS_ONLY = T -> { + if(T.isEmpty() || T.equals("#") || T.equals("0x")) + { + return true; + } + try + { + Integer.decode(T); + return true; + } + catch(Exception e){return false;} + }; + + public static Predicate getAlwaysTrue() + { + return (Predicate)ALWAYS_TRUE; + } + + public static Consumer getVoidConsumer() + { + return (Consumer)EMPTY_CONSUMER; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/functions/ObjFloatFunction.java b/src/main/java/speiger/src/coreengine/utils/functions/ObjFloatFunction.java new file mode 100644 index 0000000..0bb6a3d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/functions/ObjFloatFunction.java @@ -0,0 +1,7 @@ +package speiger.src.coreengine.utils.functions; + +@FunctionalInterface +public interface ObjFloatFunction +{ + public float apply(T v); +} diff --git a/src/main/java/speiger/src/coreengine/utils/functions/TriConsumer.java b/src/main/java/speiger/src/coreengine/utils/functions/TriConsumer.java new file mode 100644 index 0000000..1cd3118 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/functions/TriConsumer.java @@ -0,0 +1,6 @@ +package speiger.src.coreengine.utils.functions; + +public interface TriConsumer +{ + public void accept(A a, B b, C c); +} diff --git a/src/main/java/speiger/src/coreengine/utils/helpers/FileUtils.java b/src/main/java/speiger/src/coreengine/utils/helpers/FileUtils.java new file mode 100644 index 0000000..edba7b0 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/helpers/FileUtils.java @@ -0,0 +1,48 @@ +package speiger.src.coreengine.utils.helpers; + +import java.io.File; + +public class FileUtils +{ + public static void ensureFolder(File file) + { + if(file.exists()) + { + if(!file.isDirectory()) + { + file.delete(); + file.mkdirs(); + } + return; + } + file.mkdirs(); + } + + public static String convertBytes(long bytes) + { + return (bytes >> 30) > 5 ? (bytes >> 30)+"GB" : ((bytes >> 20) > 5 ? (bytes >> 20)+"MB" : ((bytes >> 10) >= 5 ? (bytes >> 10)+"KB" : bytes+"B")); + } + + public static File getBase() + { + try + { + File file = new File(FileUtils.class.getProtectionDomain().getCodeSource().getLocation().toURI()); + if(file.getName().endsWith(".jar")) return file; + } + catch(Exception e) + { + } + return new File("."); + } + + public static String buildVersion(String fileName, String prefix, boolean forced_DEV, String devString) + { + if(forced_DEV) + { + return devString; + } + int index = fileName.indexOf(prefix); + return index == -1 || fileName.length() - 4 == prefix.length() ? "Unknown Version" : fileName.substring(index + prefix.length(), fileName.length() - 4); + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/helpers/InternalThreadPools.java b/src/main/java/speiger/src/coreengine/utils/helpers/InternalThreadPools.java new file mode 100644 index 0000000..f5b8504 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/helpers/InternalThreadPools.java @@ -0,0 +1,11 @@ +package speiger.src.coreengine.utils.helpers; + +import speiger.src.coreengine.math.vector.floats.Vec2f; +import speiger.src.coreengine.math.vector.matrix.Matrix4f; +import speiger.src.coreengine.utils.collections.pools.ThreadPool; + +public class InternalThreadPools +{ + public static final ThreadPool MATRIX = new ThreadPool(100, Matrix4f::new); + public static final ThreadPool VEC2F = new ThreadPool(100, Vec2f::newMutable); +} diff --git a/src/main/java/speiger/src/coreengine/utils/helpers/JsonUtil.java b/src/main/java/speiger/src/coreengine/utils/helpers/JsonUtil.java new file mode 100644 index 0000000..39c33e1 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/helpers/JsonUtil.java @@ -0,0 +1,125 @@ +package speiger.src.coreengine.utils.helpers; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.file.Files; +import java.nio.file.Path; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +public class JsonUtil +{ + public static JsonObject loadFile(Path path) + { + try(BufferedReader reader = Files.newBufferedReader(path)) + { + return JsonParser.parseReader(reader).getAsJsonObject(); + } + catch(Exception e) + { + e.printStackTrace(); + return new JsonObject(); + } + } + + public static JsonObject loadFile(File file) + { + try(Reader reader = new BufferedReader(new FileReader(file))) + { + return JsonParser.parseReader(reader).getAsJsonObject(); + } + catch(Exception e) + { + e.printStackTrace(); + return new JsonObject(); + } + } + + public static JsonObject loadFile(InputStream stream) + { + try(Reader reader = new BufferedReader(new InputStreamReader(stream))) + { + return JsonParser.parseReader(reader).getAsJsonObject(); + } + catch(Exception e) + { + e.printStackTrace(); + return new JsonObject(); + } + } + + public static boolean getOrDefault(JsonObject obj, String name, boolean defaultValue) + { + JsonElement el = obj.get(name); + return el == null ? defaultValue : el.getAsBoolean(); + } + + public static byte getOrDefault(JsonObject obj, String name, byte defaultValue) + { + JsonElement el = obj.get(name); + return el == null ? defaultValue : el.getAsByte(); + } + + public static short getOrDefault(JsonObject obj, String name, short defaultValue) + { + JsonElement el = obj.get(name); + return el == null ? defaultValue : el.getAsShort(); + } + + public static int getOrDefault(JsonObject obj, String name, int defaultValue) + { + JsonElement el = obj.get(name); + return el == null ? defaultValue : el.getAsInt(); + } + + public static long getOrDefault(JsonObject obj, String name, long defaultValue) + { + JsonElement el = obj.get(name); + return el == null ? defaultValue : el.getAsLong(); + } + + public static float getOrDefault(JsonObject obj, String name, float defaultValue) + { + JsonElement el = obj.get(name); + return el == null ? defaultValue : el.getAsFloat(); + } + + public static double getOrDefault(JsonObject obj, String name, double defaultValue) + { + JsonElement el = obj.get(name); + return el == null ? defaultValue : el.getAsDouble(); + } + + public static String getOrDefault(JsonObject obj, String name, String defaultValue) + { + JsonElement el = obj.get(name); + return el == null ? defaultValue : el.getAsString(); + } + + public static JsonArray toArray(byte[] values) + { + JsonArray array = new JsonArray(); + for(int i = 0,m=values.length;i", ""); + } + + public static String makeColor(Integer color) + { + return "§"; + } + + public static ColorObject getColorFromText(String s) + { + s = s == null ? "" : s; + return ColorObject.rgb((s.hashCode() & 11184810) + 4473924); + } + + public static String searchUntil(String s, int startIndex, char endChar, String invalidChars) + { + boolean found = false; + invalidChars = invalidChars.toLowerCase(Locale.ROOT); + StringBuilder builder = new StringBuilder(); + while(startIndex < s.length()) + { + char character = s.charAt(startIndex); + if(character == endChar) + { + found = true; + break; + } + if(invalidChars.contains(Character.toString(character).toLowerCase(Locale.ROOT))) + { + break; + } + builder.append(character); + startIndex++; + } + if(!found) + { + return ""; + } + return builder.toString(); + } + + public static boolean findFlag(String value, String search, boolean original) + { + if(value.startsWith(search)) + { + int equals = value.indexOf("="); + return equals == -1 ? !original : (Boolean.parseBoolean(value.substring(equals + 1))); + } + return original; + } + + public static ColorObject findColor(String value, String search, ColorObject current, ColorObject original) + { + if(value.startsWith(search)) + { + try + { + int equals = value.indexOf("="); + return equals == -1 ? original : ColorObject.rgb(Integer.decode(value.substring(equals + 1)).intValue()); + } + catch(Exception e) + { + GameLog.error("Color Parsing failed", e, LogLevel.WARNING); + } + } + return current; + } + + public static float findFloat(String value, String search, float current, float toggleValue) + { + if(value.startsWith(search)) + { + int equals = value.indexOf("="); + return equals == -1 ? toggleValue : (Float.parseFloat(value.substring(equals + 1))); + } + return current; + } + + public static String convertTime(long time, String key, DecimalFormat format) + { + return key + (time >= 1000000 ? format.format(time /= 1000000)+"ms" : (time >= 1000 ? format.format(time /= 1000)+"qs" : format.format(time)+"ns")); + } + + public static String repeate(String s, int amount) + { + StringBuilder builder = new StringBuilder(); + for(int i = 0;i BUFFER_PROVIDER = ThreadLocal.withInitial(() -> new StringBuilder(320)); + public static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm:ss"); + public static boolean STACK_TRACE = true; + public static boolean FULL_STACK_TRACE = false; + + public static void trace(String name) + { + StringBuilder builder = BUFFER_PROVIDER.get(); + new Exception(name).printStackTrace(GAME_LOG_STREAM); + String result = builder.toString(); + builder.setLength(0); + log(LogLevel.INFO, result); + } + + public static void test(Object...values) + { + StringBuilder builder = new StringBuilder(); + builder.append("Test: "); + for(Object obj : values) + { + builder.append(obj).append(" : "); + } + log(LogLevel.INFO, builder.substring(0, builder.length() - 3)); + } + + public static void info(String name, Object...values) + { + StringBuilder builder = new StringBuilder(); + builder.append(name).append(": "); + for(Object obj : values) + { + builder.append(obj).append(" : "); + } + log(LogLevel.INFO, builder.substring(0, builder.length() - 3)); + } + + public static void info(Object obj) + { + log(LogLevel.INFO, obj == null ? "Null" : obj.toString()); + } + + public static void info(String text) + { + log(LogLevel.INFO, text); + } + + public static void warn(Object obj) + { + log(LogLevel.WARNING, obj == null ? "Null" : obj.toString()); + } + + public static void warn(String text) + { + log(LogLevel.WARNING, text); + } + + public static void error(Object obj) + { + log(LogLevel.ERROR, obj == null ? "Null" : obj.toString()); + } + + public static void error(String text) + { + log(LogLevel.ERROR, text); + } + + public static void error(String message, Throwable error, LogLevel level) + { + StringBuilder buffer = BUFFER_PROVIDER.get(); + buffer.append("[").append(ZonedDateTime.now().format(TIME_FORMAT)).append("]").append(level.getLevelName()); + if(STACK_TRACE) + { + buffer.append("[ ").append(getTrace(4)).append("]"); + } + buffer.append(" ").append(message); + System.out.println(buffer.toString()); + buffer.setLength(0); + error.printStackTrace(GAME_LOG_STREAM); + buffer.setLength(buffer.length() - 2); + System.err.println(buffer.toString()); + buffer.setLength(0); + } + + public static void log(LogLevel level, String info) + { + StringBuilder buffer = BUFFER_PROVIDER.get(); + buffer.append("[").append(ZonedDateTime.now().format(TIME_FORMAT)).append("]").append(getThreadName()).append(level.getLevelName()); + if(STACK_TRACE) + { + buffer.append("[ ").append(getTrace(4)).append("]"); + } + buffer.append(" ").append(info); + (level == LogLevel.ERROR ? System.err : System.out).println(buffer.toString()); + buffer.setLength(0); + if(FULL_STACK_TRACE) + { + printStackTrace(buffer); + System.out.flush(); + System.err.flush(); + } + } + + private static String getThreadName() + { + return "["+Thread.currentThread().getName()+"]"; + } + + private static void printStackTrace(StringBuilder buffer) + { + new Exception("Stack trace").printStackTrace(GAME_LOG_STREAM); + System.err.println(buffer.toString()); + buffer.setLength(0); + } + + private static String getTrace(int index) + { + return createStackTrace(Thread.currentThread().getStackTrace()[index]); + } + + private static String createStackTrace(StackTraceElement trace) + { + if(trace.isNativeMethod()) + { + return "(Native Methode)"; + } + else if(trace.getFileName() != null) + { + if(trace.getLineNumber() >= 0) + { + return "(" + trace.getFileName() + ":" + trace.getLineNumber() + ")"; + } + return "("+trace.getFileName()+":0)"; + } + return "(Unknown Source)"; + } + + public static enum LogLevel + { + INFO("[Info]"), + WARNING("[Warning]"), + ERROR("[Error]"); + + String levelName; + + private LogLevel(String level) + { + levelName = level; + } + + public String getLevelName() + { + return levelName; + } + } + + public static class GameLogWriter extends PrintStream + { + public GameLogWriter(PrintStream parent) + { + super(parent); + } + + @Override + public void println(String x) + { + GameLog.BUFFER_PROVIDER.get().append(x).append(LINE_SEPERATOR); + } + + @Override + public void println(Object x) + { + GameLog.BUFFER_PROVIDER.get().append(String.valueOf(x)).append(LINE_SEPERATOR); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/GifWriter.java b/src/main/java/speiger/src/coreengine/utils/io/GifWriter.java new file mode 100644 index 0000000..ce3811c --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/GifWriter.java @@ -0,0 +1,126 @@ +package speiger.src.coreengine.utils.io; + +import java.awt.image.RenderedImage; +import java.io.File; +import java.util.function.BiConsumer; + +import javax.imageio.IIOImage; +import javax.imageio.ImageIO; +import javax.imageio.ImageTypeSpecifier; +import javax.imageio.ImageWriteParam; +import javax.imageio.ImageWriter; +import javax.imageio.metadata.IIOMetadata; +import javax.imageio.metadata.IIOMetadataNode; +import javax.imageio.stream.FileImageOutputStream; +import javax.imageio.stream.ImageOutputStream; + +import org.w3c.dom.NodeList; + +public class GifWriter +{ + ImageOutputStream stream; + ImageWriter writer; + BiConsumer inserter; + + public static GifWriter create(File file, int image, int delay) + { + try + { + return new GifWriter(file, image, delay); + } + catch(Exception e) + { + e.printStackTrace(); + return null; + } + } + + public static GifWriter create(ImageOutputStream stream, int image, int delay) + { + try + { + return new GifWriter(stream, image, delay); + } + catch(Exception e) + { + e.printStackTrace(); + return null; + } + } + + protected GifWriter(File file, int imageType, int delay) throws Exception + { + this(new FileImageOutputStream(file), imageType, delay); + } + + protected GifWriter(ImageOutputStream out, int imageType, int delay) throws Exception + { + stream = out; + writer = ImageIO.getImageWritersBySuffix("gif").next(); + final ImageWriteParam parameter = writer.getDefaultWriteParam(); + final IIOMetadata metadata = writer.getDefaultImageMetadata(ImageTypeSpecifier.createFromBufferedImageType(imageType), parameter); + IIOMetadataNode root = (IIOMetadataNode)metadata.getAsTree(metadata.getNativeMetadataFormatName()); + IIOMetadataNode graphics = getOrCreateNode(root, "GraphicControlExtension"); + graphics.setAttribute("delayTime", ""+Float.valueOf(((delay / 1000F) * 15F)).intValue()); + graphics.setAttribute("transparentColorFlag", "false"); + graphics.setAttribute("transparentColorIndex", "0"); + graphics.setAttribute("disposalMethod", "none"); + graphics.setAttribute("userInputFlag", "false"); + + IIOMetadataNode application = new IIOMetadataNode("ApplicationExtension"); + application.setAttribute("applicationID", "NETSCAPE"); + application.setAttribute("authenticationCode", "2.0"); + application.setUserObject(new byte[] {1, 0, 0}); + getOrCreateNode(root, "ApplicationExtensions").appendChild(application); + metadata.setFromTree(metadata.getNativeMetadataFormatName(), root); + writer.setOutput(out); + writer.prepareWriteSequence(null); + inserter = new BiConsumer(){ + ImageWriteParam param = parameter; + IIOMetadata meta = metadata; + @Override + public void accept(ImageWriter writer, RenderedImage image) + { + try + { + writer.writeToSequence(new IIOImage(image, null, meta), param); + } + catch(Exception e) + { + e.printStackTrace(); + } + } + }; + } + + public void insertPicture(RenderedImage image) + { + inserter.accept(writer, image); + } + + public void close() + { + try + { + writer.endWriteSequence(); + stream.close(); + inserter = null; + } + catch(Exception e) + { + e.printStackTrace(); + } + } + + private IIOMetadataNode getOrCreateNode(IIOMetadataNode parent, String name) + { + NodeList list = parent.getElementsByTagName(name); + if(list.getLength() > 0) + { + return (IIOMetadataNode)list.item(0); + } + IIOMetadataNode node = new IIOMetadataNode(name); + parent.appendChild(node); + return node; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/DataTag.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/DataTag.java new file mode 100644 index 0000000..2fe0665 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/DataTag.java @@ -0,0 +1,91 @@ +package speiger.src.coreengine.utils.io.dataTag; + +import java.io.IOException; +import java.util.function.Consumer; + +import speiger.src.coreengine.utils.io.dataTag.array.ByteArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.DoubleArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.FloatArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.IntArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.LongArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.MediumArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.ShortArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.StringArrayTag; +import speiger.src.coreengine.utils.io.dataTag.compression.DataTagLimiter; +import speiger.src.coreengine.utils.io.dataTag.compression.TagCompressor; +import speiger.src.coreengine.utils.io.dataTag.compression.TagDecompressor; +import speiger.src.coreengine.utils.io.dataTag.simple.ByteTag; +import speiger.src.coreengine.utils.io.dataTag.simple.DoubleTag; +import speiger.src.coreengine.utils.io.dataTag.simple.FloatTag; +import speiger.src.coreengine.utils.io.dataTag.simple.IntTag; +import speiger.src.coreengine.utils.io.dataTag.simple.LongTag; +import speiger.src.coreengine.utils.io.dataTag.simple.MediumTag; +import speiger.src.coreengine.utils.io.dataTag.simple.ShortTag; +import speiger.src.coreengine.utils.io.dataTag.simple.StringTag; +import speiger.src.coreengine.utils.io.dataTag.special.ListTag; +import speiger.src.coreengine.utils.io.dataTag.special.MapTag; +import speiger.src.coreengine.utils.io.streams.ExtendedDataInput; +import speiger.src.coreengine.utils.io.streams.ExtendedDataOutput; + +public interface DataTag +{ + String KEY_HIGHLIGHT = "§"; + String STRING_HIGHLIGHT = "§"; + String NUMBER_HIGHLIGHT = "§"; + String TYPE_HIGHLIGHT = "§"; + String RESET_HIGHLIGHT = "§"; + + public int getId(); + public String getTypeName(); + + public long countBytes(long input); + public default void addKeys(Consumer keys){}; + + public void write(ExtendedDataOutput output, TagCompressor compressor) throws IOException; + public void read(ExtendedDataInput input, TagDecompressor decompressor, int depthLimit, DataTagLimiter limiter) throws IOException; + + public DataTag copy(); + + public default T cast(){return (T)this;} + public default T cast(Class clz){return (T)this;} + + public default String prettyPrint(){return prettyPrint(" ", 0);} + public String prettyPrint(String indent, int depth); + + + public static DataTag create(byte id) + { + switch(id) + { + case 1: return new ByteTag(); + case 2: return new ShortTag(); + case 3: return new MediumTag(); + case 4: return new IntTag(); + case 5: return new LongTag(); + case 6: return new FloatTag(); + case 7: return new DoubleTag(); + case 8: return new StringTag(); + case 9: return new ByteArrayTag(); + case 10: return new ShortArrayTag(); + case 11: return new MediumArrayTag(); + case 12: return new IntArrayTag(); + case 13: return new LongArrayTag(); + case 14: return new FloatArrayTag(); + case 15: return new DoubleArrayTag(); + case 16: return new StringArrayTag(); + case 17: return new MapTag(); + case 18: return new ListTag(); + } + return null; + } + + static String repeate(String s, int amount) + { + StringBuilder builder = new StringBuilder(); + for(int i = 0;i {}; + + public static DataTagLimiter limit(int byteLimit) + { + return new Instance(byteLimit); + } + + public void countBytes(int amount) throws IOException; + + public static class Instance implements DataTagLimiter + { + final int max; + int read = 0; + + public Instance(int max) + { + this.max = max; + } + + @Override + public void countBytes(int amount) throws IOException + { + read += amount; + if(read > amount) + { + throw new IllegalStateException("To many Bytes Read"); + } + } + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/compression/TagCompressor.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/compression/TagCompressor.java new file mode 100644 index 0000000..50a4c29 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/compression/TagCompressor.java @@ -0,0 +1,122 @@ +package speiger.src.coreengine.utils.io.dataTag.compression; + +import java.io.Flushable; +import java.io.IOException; +import java.io.OutputStream; +import java.util.function.Consumer; +import java.util.zip.GZIPOutputStream; + +import speiger.src.collections.objects.maps.impl.hash.Object2IntLinkedOpenHashMap; +import speiger.src.collections.objects.maps.interfaces.Object2IntMap; +import speiger.src.coreengine.utils.io.dataTag.DataTag; +import speiger.src.coreengine.utils.io.streams.ExtendedDataOutput; + +public class TagCompressor implements Flushable, AutoCloseable +{ + public static final int VERSION = 1; + + protected ExtendedDataOutput output; + + public TagCompressor(ExtendedDataOutput output) + { + this.output = output; + } + + public static TagCompressor create(ExtendedDataOutput output, boolean lexed) + { + return lexed ? new LexedTagCompressor(output) : new TagCompressor(output); + } + + public static TagCompressor create(OutputStream output, boolean lexed) throws IOException + { + return create(ExtendedDataOutput.createBuffered(new GZIPOutputStream(output)), lexed); + } + + public static TagCompressor createAndSign(ExtendedDataOutput output, boolean lexed) throws IOException + { + TagCompressor compressor = create(output, lexed); + compressor.writeIdentifier(); + return compressor; + } + + public static TagCompressor createDefaultSign(OutputStream output, boolean lexed) throws IOException + { + TagCompressor compressor = create(ExtendedDataOutput.createBuffered(new GZIPOutputStream(output)), lexed); + compressor.writeIdentifier(); + return compressor; + } + + public void writeIdentifier() throws IOException + { + output.writeInt(VERSION << 1 | 0); + } + + public void write(DataTag tag) throws IOException + { + output.writeByte(tag.getId()); + tag.write(output, this); + } + + public void writeKey(String name) throws IOException + { + output.writeString(name); + } + + @Override + public void flush() throws IOException + { + output.flush(); + } + + @Override + public void close() throws Exception + { + if(output != null) + { + output.flush(); + output.close(); + output = null; + } + } + + public static class LexedTagCompressor extends TagCompressor implements Consumer + { + Object2IntMap indexes = new Object2IntLinkedOpenHashMap(); + + public LexedTagCompressor(ExtendedDataOutput output) + { + super(output); + } + + @Override + public void writeIdentifier() throws IOException + { + output.writeInt(VERSION << 1 | 1); + } + + @Override + public void write(DataTag tag) throws IOException + { + indexes.clear(); + tag.addKeys(this); + output.writeVarInt(indexes.size()); + for(String s : indexes.keySet()) + { + output.writeString(s); + } + super.write(tag); + } + + @Override + public void writeKey(String name) throws IOException + { + output.writeVarInt(indexes.getInt(name)); + } + + @Override + public void accept(String t) + { + indexes.putIfAbsent(t, indexes.size()); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/compression/TagDecompressor.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/compression/TagDecompressor.java new file mode 100644 index 0000000..725ba72 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/compression/TagDecompressor.java @@ -0,0 +1,107 @@ +package speiger.src.coreengine.utils.io.dataTag.compression; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.zip.GZIPInputStream; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.utils.io.dataTag.DataTag; +import speiger.src.coreengine.utils.io.streams.ExtendedDataInput; + +public class TagDecompressor implements AutoCloseable +{ + public static final int VERSION = 1; + public static boolean LOG = false; + + protected ExtendedDataInput input; + protected DataTagLimiter limit; + + public TagDecompressor(ExtendedDataInput input, DataTagLimiter limit) + { + this.input = input; + this.limit = limit; + } + + public static TagDecompressor create(ExtendedDataInput input, DataTagLimiter limit, boolean lexed) + { + return lexed ? new LexedTagDeCompressor(input, limit) : new TagDecompressor(input, limit); + } + + public static TagDecompressor createDefault(InputStream input, DataTagLimiter limit, boolean lexed) throws IOException + { + return create(ExtendedDataInput.createBuffered(new GZIPInputStream(input)), limit, lexed); + } + + public static TagDecompressor createFromSign(ExtendedDataInput input, DataTagLimiter limit) throws IOException + { + int sign = input.readInt(); + int version = sign >> 1; + if(version > VERSION) throw new IllegalStateException("Version ["+version+"] is to new"); + else if(version < VERSION && LOG) System.out.println("Loading Outdated Version ["+version+"] this might cause problems"); + return (sign & 1) != 0 ? new LexedTagDeCompressor(input, limit) : new TagDecompressor(input, limit); + } + + public static TagDecompressor createDefaultSign(InputStream input, DataTagLimiter limit) throws IOException + { + return createFromSign(ExtendedDataInput.createBuffered(new GZIPInputStream(input)), limit); + } + + @Override + public void close() throws Exception + { + input.close(); + } + + public DataTag read() throws IOException + { + return readSubTag(input.readByte(), "Init", 0); + } + + public String readKey() throws IOException + { + return input.readString(); + } + + public DataTag readSubTag(byte type, String key, int depth) throws IOException + { + try + { + DataTag tag = DataTag.create(type); + tag.read(input, this, depth, limit); + return tag; + } + catch(IOException e) + { + throw new IllegalStateException("Crash during Reading a Tag. [Key="+key+", Type="+type+"]", e); + } + } + + public static class LexedTagDeCompressor extends TagDecompressor + { + List keys = new ObjectArrayList<>(); + + public LexedTagDeCompressor(ExtendedDataInput input, DataTagLimiter limit) + { + super(input, limit); + } + + @Override + public DataTag read() throws IOException + { + keys.clear(); + int size = input.readVarInt(); + for(int i = 0;i, IOEntry> entries; + SerializationContext context = new SerializationContext(); + + DataTagIO(Map, IOEntry> entries) + { + this.entries = entries; + } + + public T fromData(DataTag tag, Class type) throws IOException + { + IOEntry entry = entries.get(type); + return entry == null || entry.deserializer == null ? null : (T)entry.deserializer.deserialize(tag, context); + } + + public List fromData(DataTag tag, Class type, List list) throws IOException + { + IOEntry entry = entries.get(type); + if(entry != null && entry.deserializer != null) + { + if(tag instanceof ListTag) + { + for(DataTag subTag : tag.cast(ListTag.class)) + { + list.add((T)entry.deserializer.deserialize(subTag, context)); + } + } + else + { + list.add((T)entry.deserializer.deserialize(tag, context)); + } + } + return list; + } + + public DataTag toData(Object obj) + { + return toData(obj, obj.getClass()); + } + + public DataTag toData(Object obj, Class type) + { + IOEntry entry = (IOEntry)entries.get(type); + return entry == null || entry.serializer == null ? null : entry.serializer.serialize(obj, context); + } + + public DataTag toData(List list, Class type) + { + IOEntry entry = (IOEntry)entries.get(type); + if(entry == null || entry.serializer == null) + { + return null; + } + ListTag tags = new ListTag(); + for(int i = 0;i + { + IDataSerializer serializer; + IDataDeserializer deserializer; + + public IOEntry(IDataSerializer serializer, IDataDeserializer deserializer) + { + this.serializer = serializer; + this.deserializer = deserializer; + } + + } + + class SerializationContext implements IDataDeserializationContext, IDataSerializationContext + { + @Override + public DataTag serialize(Object obj) + { + return toData(obj); + } + + @Override + public DataTag serialize(Object obj, Class clz) + { + return toData(obj, clz); + } + + @Override + public T deserialize(DataTag tag, Class type) throws IOException + { + return fromData(tag, type); + } + + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/DataTagIOBuilder.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/DataTagIOBuilder.java new file mode 100644 index 0000000..996b2b6 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/DataTagIOBuilder.java @@ -0,0 +1,37 @@ +package speiger.src.coreengine.utils.io.dataTag.parsing; + +import java.util.Map; + +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectOpenHashMap; +import speiger.src.coreengine.utils.io.dataTag.parsing.DataTagIO.IOEntry; +import speiger.src.coreengine.utils.io.dataTag.parsing.deserialize.IDataDeserializer; +import speiger.src.coreengine.utils.io.dataTag.parsing.serialize.IDataSerializer; + +public class DataTagIOBuilder +{ + Map, IOEntry> map = new Object2ObjectOpenHashMap<>(); + + public DataTagIO build() + { + return new DataTagIO(map); + } + + public static DataTagIOBuilder copyOf(DataTagIO io) + { + DataTagIOBuilder builder = new DataTagIOBuilder(); + builder.map.putAll(io.entries); + return builder; + } + + public DataTagIOBuilder add(Class type, Object obj) + { + IDataSerializer serializer = obj instanceof IDataSerializer ? (IDataSerializer)obj : null; + IDataDeserializer deserializer = obj instanceof IDataDeserializer ? (IDataDeserializer)obj : null; + if(serializer == null || deserializer == null) + { + return this; + } + map.put(type, new IOEntry<>(serializer, deserializer)); + return this; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/DataTagParser.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/DataTagParser.java new file mode 100644 index 0000000..3d67d35 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/DataTagParser.java @@ -0,0 +1,330 @@ +package speiger.src.coreengine.utils.io.dataTag.parsing; + +import java.io.IOException; +import java.util.function.Consumer; +import java.util.regex.Pattern; + +import speiger.src.collections.bytes.lists.ByteArrayList; +import speiger.src.collections.bytes.lists.ByteList; +import speiger.src.collections.doubles.lists.DoubleArrayList; +import speiger.src.collections.doubles.lists.DoubleList; +import speiger.src.collections.floats.lists.FloatArrayList; +import speiger.src.collections.floats.lists.FloatList; +import speiger.src.collections.ints.lists.IntArrayList; +import speiger.src.collections.ints.lists.IntList; +import speiger.src.collections.longs.lists.LongArrayList; +import speiger.src.collections.longs.lists.LongList; +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.lists.ObjectList; +import speiger.src.collections.shorts.lists.ShortArrayList; +import speiger.src.collections.shorts.lists.ShortList; +import speiger.src.coreengine.utils.io.dataTag.DataTag; +import speiger.src.coreengine.utils.io.dataTag.array.ByteArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.DoubleArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.FloatArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.IntArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.LongArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.MediumArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.ShortArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.StringArrayTag; +import speiger.src.coreengine.utils.io.dataTag.simple.ByteTag; +import speiger.src.coreengine.utils.io.dataTag.simple.DoubleTag; +import speiger.src.coreengine.utils.io.dataTag.simple.FloatTag; +import speiger.src.coreengine.utils.io.dataTag.simple.IntTag; +import speiger.src.coreengine.utils.io.dataTag.simple.LongTag; +import speiger.src.coreengine.utils.io.dataTag.simple.MediumTag; +import speiger.src.coreengine.utils.io.dataTag.simple.PrimitiveTag; +import speiger.src.coreengine.utils.io.dataTag.simple.ShortTag; +import speiger.src.coreengine.utils.io.dataTag.simple.StringTag; +import speiger.src.coreengine.utils.io.dataTag.special.ListTag; +import speiger.src.coreengine.utils.io.dataTag.special.MapTag; +import speiger.src.coreengine.utils.io.streams.StringReader; + +public class DataTagParser +{ + private static final Pattern BYTE_TYPE = Pattern.compile("[-+]?(?:[0-9]+)b", 2); + private static final Pattern SHORT_TYPE = Pattern.compile("[-+]?(?:[0-9]+)s", 2); + private static final Pattern MEDIUM_TYPE = Pattern.compile("[-+]?(?:[0-9]+)m", 2); + private static final Pattern INT_TYPE = Pattern.compile("[-+]?(?:[0-9]+)i", 2); + private static final Pattern INT_TYPE_DEFAULT = Pattern.compile("[-+]?(?:[0-9]+)"); + private static final Pattern LONG_TYPE = Pattern.compile("[-+]?(?:[0-9]+)l", 2); + private static final Pattern FLOAT_TYPE = Pattern.compile("[-+]?(?:[0-9]+[.]?|[0-9]*[.][0-9]+)(?:e[-+]?[0-9]+)?f", 2); + private static final Pattern DOUBLE_TYPE = Pattern.compile("[-+]?(?:[0-9]+[.]?|[0-9]*[.][0-9]+)(?:e[-+]?[0-9]+)?d", 2); + private static final Pattern DOUBLE_TYPE_DEFAULT = Pattern.compile("[-+]?(?:[0-9]+[.]|[0-9]*[.][0-9]+)(?:e[-+]?[0-9]+)?", 2); + StringReader reader; + + public DataTagParser(String s) + { + reader = new StringReader(s.replaceAll("\\§\\<(.*?)\\>", "")); + } + + public DataTag readTagSave() + { + try + { + return readTag(); + } + catch(Exception e) + { + return null; + } + } + + public DataTag readTag() throws IOException + { + reader.skipEmpty(); + if(!reader.canRead()) + { + throw new IOException("Nothing to parse found"); + } + else + { + char c0 = reader.peek(); + if(c0 == '{') + { + return readMap(); + } + else + { + return c0 == '[' ? readList() : readTagType(); + } + } + } + + public MapTag readMap() throws IOException + { + requirePresent('{'); + reader.skipEmpty(); + MapTag map = new MapTag(); + while(reader.canRead() && reader.peek() != '}') + { + reader.skipEmpty(); + int last = reader.getIndex(); + String s = reader.readString(); + if(s.isEmpty()) + { + throw new IOException("Expected Key at " + last); + } + requirePresent(':'); + map.setTag(s, readTag()); + reader.skipEmpty(); + if(!reader.isPresent(',')) + { + break; + } + reader.skipEmpty(); + } + requirePresent('}'); + return map; + } + + protected DataTag readList() throws IOException + { + return reader.canRead(3) && !StringReader.isQuoteCharacter(reader.peek(1)) && reader.peek(2) == ';' ? readArrayTag() : readListTag(); + } + + protected DataTag readTagType() throws IOException + { + reader.skipEmpty(); + int last = reader.getIndex(); + if(StringReader.isQuoteCharacter(reader.peek())) + { + return new StringTag(reader.readQuotedText()); + } + else + { + String s = reader.readString(); + if(s.isEmpty()) + { + throw new IOException("Expected Value at " + last); + } + else + { + return tag(s); + } + } + } + + public ListTag readListTag() throws IOException + { + requirePresent('['); + reader.skipEmpty(); + if(!reader.canRead()) + { + throw new IOException("Expected List Data"); + } + else + { + ListTag list = new ListTag(); + while(reader.peek() != ']') + { + list.add(readTag()); + if(!reader.isPresent(',')) + { + break; + } + reader.skipEmpty(); + if(!reader.canRead()) + { + throw new IOException("Expected Another Element"); + } + } + + requirePresent(']'); + return list; + } + } + + protected DataTag readArrayTag() throws IOException + { + requirePresent('['); + char type = reader.pull(); + reader.skip(); + reader.skipEmpty(); + if(!reader.canRead()) + { + throw new IOException("Out of Data!"); + } + else if(type == 'B') + { + ByteList list = new ByteArrayList(); + readArray(T -> list.add(T.asByte()), 1, PrimitiveTag.class); + return new ByteArrayTag(list.toByteArray()); + } + else if(type == 'S') + { + ShortList list = new ShortArrayList(); + readArray(T -> list.add(T.asShort()), 2, PrimitiveTag.class); + return new ShortArrayTag(list.toShortArray()); + } + else if(type == 'M') + { + IntList list = new IntArrayList(); + readArray(T -> list.add(T.asInt()), 3, PrimitiveTag.class); + return new MediumArrayTag(list.toIntArray()); + } + else if(type == 'I') + { + IntList list = new IntArrayList(); + readArray(T -> list.add(T.asInt()), 4, PrimitiveTag.class); + return new IntArrayTag(list.toIntArray()); + } + else if(type == 'L') + { + LongList list = new LongArrayList(); + readArray(T -> list.add(T.asLong()), 5, PrimitiveTag.class); + return new LongArrayTag(list.toLongArray()); + } + else if(type == 'F') + { + FloatList list = new FloatArrayList(); + readArray(T -> list.add(T.asFloat()), 6, PrimitiveTag.class); + return new FloatArrayTag(list.toFloatArray()); + } + else if(type == 'D') + { + DoubleList list = new DoubleArrayList(); + readArray(T -> list.add(T.asDouble()), 7, PrimitiveTag.class); + return new DoubleArrayTag(list.toDoubleArray()); + } + else if(type == 'A') + { + ObjectList list = new ObjectArrayList(); + readArray(T -> list.add(T.get()), 8, StringTag.class); + return new StringArrayTag(list.toArray(new String[list.size()])); + } + else + { + throw new IOException("Invalid Data Type: [Received=" + type + ", Valid=[B, S, M, I, L, F, D, A]"); + } + } + + protected void readArray(Consumer result, int type, Class clz) throws IOException + { + while(true) + { + if(reader.peek() != ']') + { + DataTag tag = readTag(); + if(tag.getId() != type) + { + throw new IOException("Mixing of ArrayTypes [Expected=" + DataTag.create((byte)type).getTypeName() + ", Present=" + tag.getTypeName() + "]"); + } + result.accept(tag.cast()); + reader.skipEmpty(); + if(reader.isPresent(',')) + { + reader.skipEmpty(); + if(!reader.canRead()) + { + throw new IOException("Expected Another Element"); + } + continue; + } + } + requirePresent(']'); + return; + } + } + + protected DataTag tag(String s) + { + try + { + if(FLOAT_TYPE.matcher(s).matches()) + { + return new FloatTag(Float.parseFloat(s.substring(0, s.length() - 1))); + } + if(BYTE_TYPE.matcher(s).matches()) + { + return new ByteTag(Byte.parseByte(s.substring(0, s.length() - 1))); + } + if(LONG_TYPE.matcher(s).matches()) + { + return new LongTag(Long.parseLong(s.substring(0, s.length() - 1))); + } + if(SHORT_TYPE.matcher(s).matches()) + { + return new ShortTag(Short.parseShort(s.substring(0, s.length() - 1))); + } + if(MEDIUM_TYPE.matcher(s).matches()) + { + return new MediumTag(Integer.parseInt(s.substring(0, s.length() - 1))); + } + if(INT_TYPE.matcher(s).matches()) + { + return new IntTag(Integer.parseInt(s.substring(0, s.length() - 1))); + } + if(DOUBLE_TYPE.matcher(s).matches()) + { + return new DoubleTag(Double.parseDouble(s.substring(0, s.length() - 1))); + } + if(DOUBLE_TYPE_DEFAULT.matcher(s).matches()) + { + return new DoubleTag(Double.parseDouble(s)); + } + if(INT_TYPE_DEFAULT.matcher(s).matches()) + { + return new IntTag(Integer.parseInt(s)); + } + if("true".equalsIgnoreCase(s)) + { + return new ByteTag((byte)1); + } + if("false".equalsIgnoreCase(s)) + { + return new ByteTag((byte)0); + } + } + catch(NumberFormatException e) + { + } + return new StringTag(s); + } + + protected void requirePresent(char value) + { + reader.skipEmpty(); + reader.requirePresent(value); + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/deserialize/IDataDeserializationContext.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/deserialize/IDataDeserializationContext.java new file mode 100644 index 0000000..df2befc --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/deserialize/IDataDeserializationContext.java @@ -0,0 +1,10 @@ +package speiger.src.coreengine.utils.io.dataTag.parsing.deserialize; + +import java.io.IOException; + +import speiger.src.coreengine.utils.io.dataTag.DataTag; + +public interface IDataDeserializationContext +{ + public T deserialize(DataTag tag, Class type) throws IOException; +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/deserialize/IDataDeserializer.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/deserialize/IDataDeserializer.java new file mode 100644 index 0000000..458b0c9 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/deserialize/IDataDeserializer.java @@ -0,0 +1,10 @@ +package speiger.src.coreengine.utils.io.dataTag.parsing.deserialize; + +import java.io.IOException; + +import speiger.src.coreengine.utils.io.dataTag.DataTag; + +public interface IDataDeserializer +{ + public T deserialize(DataTag tag, IDataDeserializationContext context) throws IOException; +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/serialize/IDataSerializationContext.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/serialize/IDataSerializationContext.java new file mode 100644 index 0000000..13b8cda --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/serialize/IDataSerializationContext.java @@ -0,0 +1,10 @@ +package speiger.src.coreengine.utils.io.dataTag.parsing.serialize; + +import speiger.src.coreengine.utils.io.dataTag.DataTag; + +public interface IDataSerializationContext +{ + public DataTag serialize(Object obj); + + public DataTag serialize(Object obj, Class clz); +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/serialize/IDataSerializer.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/serialize/IDataSerializer.java new file mode 100644 index 0000000..0ea1bcb --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/parsing/serialize/IDataSerializer.java @@ -0,0 +1,8 @@ +package speiger.src.coreengine.utils.io.dataTag.parsing.serialize; + +import speiger.src.coreengine.utils.io.dataTag.DataTag; + +public interface IDataSerializer +{ + public DataTag serialize(T entry, IDataSerializationContext context); +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/ByteTag.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/ByteTag.java new file mode 100644 index 0000000..e8dfe3b --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/ByteTag.java @@ -0,0 +1,120 @@ +package speiger.src.coreengine.utils.io.dataTag.simple; + +import java.io.IOException; + +import speiger.src.coreengine.utils.io.dataTag.compression.DataTagLimiter; +import speiger.src.coreengine.utils.io.dataTag.compression.TagCompressor; +import speiger.src.coreengine.utils.io.dataTag.compression.TagDecompressor; +import speiger.src.coreengine.utils.io.streams.ExtendedDataInput; +import speiger.src.coreengine.utils.io.streams.ExtendedDataOutput; + +public class ByteTag implements PrimitiveTag +{ + byte value; + + public ByteTag() + { + } + + public ByteTag(byte value) + { + this.value = value; + } + + @Override + public int getId() + { + return 1; + } + + @Override + public String getTypeName() + { + return "Byte"; + } + + @Override + public long countBytes(long input) + { + return input + 1L; + } + + @Override + public void write(ExtendedDataOutput output, TagCompressor compressor) throws IOException + { + output.writeByte(value); + } + + @Override + public void read(ExtendedDataInput input, TagDecompressor decompressor, int depthLimit, DataTagLimiter limiter) throws IOException + { + value = input.readByte(); + limiter.countBytes(1); + } + + @Override + public ByteTag copy() + { + return new ByteTag(value); + } + + @Override + public boolean equals(Object obj) + { + return obj instanceof ByteTag && ((ByteTag)obj).value == value; + } + + @Override + public int hashCode() + { + return value; + } + + @Override + public String toString() + { + return value+"b"; + } + + @Override + public String prettyPrint(String indent, int depth) + { + return new StringBuilder().append(NUMBER_HIGHLIGHT).append(value).append(TYPE_HIGHLIGHT).append("b").append(RESET_HIGHLIGHT).toString(); + } + + @Override + public byte asByte() + { + return value; + } + + @Override + public short asShort() + { + return value; + } + + @Override + public int asInt() + { + return value; + } + + @Override + public long asLong() + { + return value; + } + + @Override + public float asFloat() + { + return value; + } + + @Override + public double asDouble() + { + return value; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/DoubleTag.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/DoubleTag.java new file mode 100644 index 0000000..8b2b797 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/DoubleTag.java @@ -0,0 +1,121 @@ +package speiger.src.coreengine.utils.io.dataTag.simple; + +import java.io.IOException; + +import speiger.src.coreengine.utils.io.dataTag.DataTag; +import speiger.src.coreengine.utils.io.dataTag.compression.DataTagLimiter; +import speiger.src.coreengine.utils.io.dataTag.compression.TagCompressor; +import speiger.src.coreengine.utils.io.dataTag.compression.TagDecompressor; +import speiger.src.coreengine.utils.io.streams.ExtendedDataInput; +import speiger.src.coreengine.utils.io.streams.ExtendedDataOutput; + +public class DoubleTag implements PrimitiveTag +{ + double value; + + public DoubleTag() + { + } + + public DoubleTag(double value) + { + this.value = value; + } + + @Override + public int getId() + { + return 7; + } + + @Override + public String getTypeName() + { + return "Double"; + } + + @Override + public long countBytes(long input) + { + return input + 8L; + } + + @Override + public void write(ExtendedDataOutput output, TagCompressor compressor) throws IOException + { + output.writeDouble(value); + } + + @Override + public void read(ExtendedDataInput input, TagDecompressor decompressor, int depthLimit, DataTagLimiter limiter) throws IOException + { + value = input.readDouble(); + limiter.countBytes(8); + } + + @Override + public DataTag copy() + { + return new DoubleTag(value); + } + + @Override + public int hashCode() + { + return Double.hashCode(value); + } + + @Override + public boolean equals(Object obj) + { + return obj instanceof DoubleTag && ((DoubleTag)obj).value == value; + } + + @Override + public String toString() + { + return value+"d"; + } + + @Override + public String prettyPrint(String indent, int depth) + { + return new StringBuilder().append(NUMBER_HIGHLIGHT).append(value).append(TYPE_HIGHLIGHT).append("d").append(RESET_HIGHLIGHT).toString(); + } + + @Override + public byte asByte() + { + return (byte)value; + } + + @Override + public short asShort() + { + return (short)value; + } + + @Override + public int asInt() + { + return (int)value; + } + + @Override + public long asLong() + { + return (long)value; + } + + @Override + public float asFloat() + { + return (float)value; + } + + @Override + public double asDouble() + { + return value; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/FloatTag.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/FloatTag.java new file mode 100644 index 0000000..18c5ab5 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/FloatTag.java @@ -0,0 +1,121 @@ +package speiger.src.coreengine.utils.io.dataTag.simple; + +import java.io.IOException; + +import speiger.src.coreengine.utils.io.dataTag.DataTag; +import speiger.src.coreengine.utils.io.dataTag.compression.DataTagLimiter; +import speiger.src.coreengine.utils.io.dataTag.compression.TagCompressor; +import speiger.src.coreengine.utils.io.dataTag.compression.TagDecompressor; +import speiger.src.coreengine.utils.io.streams.ExtendedDataInput; +import speiger.src.coreengine.utils.io.streams.ExtendedDataOutput; + +public class FloatTag implements PrimitiveTag +{ + float value; + + public FloatTag() + { + } + + public FloatTag(float value) + { + this.value = value; + } + + @Override + public int getId() + { + return 6; + } + + @Override + public String getTypeName() + { + return "Float"; + } + + @Override + public long countBytes(long input) + { + return input + 4L; + } + + @Override + public void write(ExtendedDataOutput output, TagCompressor compressor) throws IOException + { + output.writeFloat(value); + } + + @Override + public void read(ExtendedDataInput input, TagDecompressor decompressor, int depthLimit, DataTagLimiter limiter) throws IOException + { + value = input.readFloat(); + limiter.countBytes(4); + } + + @Override + public DataTag copy() + { + return new FloatTag(value); + } + + @Override + public int hashCode() + { + return Float.hashCode(value); + } + + @Override + public boolean equals(Object obj) + { + return obj instanceof FloatTag && ((FloatTag)obj).value == value; + } + + @Override + public String toString() + { + return value+"f"; + } + + @Override + public String prettyPrint(String indent, int depth) + { + return new StringBuilder().append(NUMBER_HIGHLIGHT).append(value).append(TYPE_HIGHLIGHT).append("f").append(RESET_HIGHLIGHT).toString(); + } + + @Override + public byte asByte() + { + return (byte)value; + } + + @Override + public short asShort() + { + return (short)value; + } + + @Override + public int asInt() + { + return (int)value; + } + + @Override + public long asLong() + { + return (long)value; + } + + @Override + public float asFloat() + { + return value; + } + + @Override + public double asDouble() + { + return value; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/IntTag.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/IntTag.java new file mode 100644 index 0000000..59dacd0 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/IntTag.java @@ -0,0 +1,121 @@ +package speiger.src.coreengine.utils.io.dataTag.simple; + +import java.io.IOException; + +import speiger.src.coreengine.utils.io.dataTag.DataTag; +import speiger.src.coreengine.utils.io.dataTag.compression.DataTagLimiter; +import speiger.src.coreengine.utils.io.dataTag.compression.TagCompressor; +import speiger.src.coreengine.utils.io.dataTag.compression.TagDecompressor; +import speiger.src.coreengine.utils.io.streams.ExtendedDataInput; +import speiger.src.coreengine.utils.io.streams.ExtendedDataOutput; + +public class IntTag implements PrimitiveTag +{ + int value; + + public IntTag() + { + } + + public IntTag(int value) + { + this.value = value; + } + + @Override + public int getId() + { + return 4; + } + + @Override + public String getTypeName() + { + return "Integer"; + } + + @Override + public long countBytes(long input) + { + return input + 4L; + } + + @Override + public void write(ExtendedDataOutput output, TagCompressor compressor) throws IOException + { + output.writeInt(value); + } + + @Override + public void read(ExtendedDataInput input, TagDecompressor decompressor, int depthLimit, DataTagLimiter limiter) throws IOException + { + value = input.readInt(); + limiter.countBytes(4); + } + + @Override + public DataTag copy() + { + return new IntTag(value); + } + + @Override + public int hashCode() + { + return value; + } + + @Override + public boolean equals(Object obj) + { + return obj instanceof IntTag && ((IntTag)obj).value == value; + } + + @Override + public String toString() + { + return value+""; + } + + @Override + public String prettyPrint(String indent, int depth) + { + return new StringBuilder().append(NUMBER_HIGHLIGHT).append(value).append(RESET_HIGHLIGHT).toString(); + } + + @Override + public byte asByte() + { + return (byte)value; + } + + @Override + public short asShort() + { + return (short)value; + } + + @Override + public int asInt() + { + return value; + } + + @Override + public long asLong() + { + return value; + } + + @Override + public float asFloat() + { + return value; + } + + @Override + public double asDouble() + { + return value; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/LongTag.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/LongTag.java new file mode 100644 index 0000000..0b59bbf --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/LongTag.java @@ -0,0 +1,121 @@ +package speiger.src.coreengine.utils.io.dataTag.simple; + +import java.io.IOException; + +import speiger.src.coreengine.utils.io.dataTag.DataTag; +import speiger.src.coreengine.utils.io.dataTag.compression.DataTagLimiter; +import speiger.src.coreengine.utils.io.dataTag.compression.TagCompressor; +import speiger.src.coreengine.utils.io.dataTag.compression.TagDecompressor; +import speiger.src.coreengine.utils.io.streams.ExtendedDataInput; +import speiger.src.coreengine.utils.io.streams.ExtendedDataOutput; + +public class LongTag implements PrimitiveTag +{ + long value; + + public LongTag() + { + } + + public LongTag(long value) + { + this.value = value; + } + + @Override + public int getId() + { + return 5; + } + + @Override + public String getTypeName() + { + return "Long"; + } + + @Override + public long countBytes(long input) + { + return input + 8L; + } + + @Override + public void write(ExtendedDataOutput output, TagCompressor compressor) throws IOException + { + output.writeLong(value); + } + + @Override + public void read(ExtendedDataInput input, TagDecompressor decompressor, int depthLimit, DataTagLimiter limiter) throws IOException + { + value = input.readLong(); + limiter.countBytes(8); + } + + @Override + public DataTag copy() + { + return new LongTag(value); + } + + @Override + public int hashCode() + { + return Long.hashCode(value); + } + + @Override + public boolean equals(Object obj) + { + return obj instanceof LongTag && ((LongTag)obj).value == value; + } + + @Override + public String toString() + { + return value+"l"; + } + + @Override + public String prettyPrint(String indent, int depth) + { + return new StringBuilder().append(NUMBER_HIGHLIGHT).append(value).append(TYPE_HIGHLIGHT).append("l").append(RESET_HIGHLIGHT).toString(); + } + + @Override + public byte asByte() + { + return (byte)value; + } + + @Override + public short asShort() + { + return (short)value; + } + + @Override + public int asInt() + { + return (int)value; + } + + @Override + public long asLong() + { + return value; + } + + @Override + public float asFloat() + { + return value; + } + + @Override + public double asDouble() + { + return value; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/MediumTag.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/MediumTag.java new file mode 100644 index 0000000..9817831 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/MediumTag.java @@ -0,0 +1,121 @@ +package speiger.src.coreengine.utils.io.dataTag.simple; + +import java.io.IOException; + +import speiger.src.coreengine.utils.io.dataTag.DataTag; +import speiger.src.coreengine.utils.io.dataTag.compression.DataTagLimiter; +import speiger.src.coreengine.utils.io.dataTag.compression.TagCompressor; +import speiger.src.coreengine.utils.io.dataTag.compression.TagDecompressor; +import speiger.src.coreengine.utils.io.streams.ExtendedDataInput; +import speiger.src.coreengine.utils.io.streams.ExtendedDataOutput; + +public class MediumTag implements PrimitiveTag +{ + int value; + + public MediumTag() + { + } + + public MediumTag(int value) + { + this.value = value; + } + + @Override + public int getId() + { + return 3; + } + + @Override + public String getTypeName() + { + return "Medium"; + } + + @Override + public long countBytes(long input) + { + return input + 3L; + } + + @Override + public void write(ExtendedDataOutput output, TagCompressor compressor) throws IOException + { + output.writeMedium(value); + } + + @Override + public void read(ExtendedDataInput input, TagDecompressor decompressor, int depthLimit, DataTagLimiter limiter) throws IOException + { + value = input.readMedium(); + limiter.countBytes(3); + } + + @Override + public DataTag copy() + { + return new MediumTag(value); + } + + @Override + public int hashCode() + { + return value; + } + + @Override + public boolean equals(Object obj) + { + return obj instanceof MediumTag && ((MediumTag)obj).value == value; + } + + @Override + public String toString() + { + return value+"m"; + } + + @Override + public String prettyPrint(String indent, int depth) + { + return new StringBuilder().append(NUMBER_HIGHLIGHT).append(value).append(TYPE_HIGHLIGHT).append("m").append(RESET_HIGHLIGHT).toString(); + } + + @Override + public byte asByte() + { + return (byte)value; + } + + @Override + public short asShort() + { + return (short)value; + } + + @Override + public int asInt() + { + return value; + } + + @Override + public long asLong() + { + return value; + } + + @Override + public float asFloat() + { + return value; + } + + @Override + public double asDouble() + { + return value; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/PrimitiveTag.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/PrimitiveTag.java new file mode 100644 index 0000000..0d3d58f --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/PrimitiveTag.java @@ -0,0 +1,18 @@ +package speiger.src.coreengine.utils.io.dataTag.simple; + +import speiger.src.coreengine.utils.io.dataTag.DataTag; + +public interface PrimitiveTag extends DataTag +{ + public byte asByte(); + + public short asShort(); + + public int asInt(); + + public long asLong(); + + public float asFloat(); + + public double asDouble(); +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/ShortTag.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/ShortTag.java new file mode 100644 index 0000000..205077c --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/ShortTag.java @@ -0,0 +1,121 @@ +package speiger.src.coreengine.utils.io.dataTag.simple; + +import java.io.IOException; + +import speiger.src.coreengine.utils.io.dataTag.compression.DataTagLimiter; +import speiger.src.coreengine.utils.io.dataTag.compression.TagCompressor; +import speiger.src.coreengine.utils.io.dataTag.compression.TagDecompressor; +import speiger.src.coreengine.utils.io.streams.ExtendedDataInput; +import speiger.src.coreengine.utils.io.streams.ExtendedDataOutput; + +public class ShortTag implements PrimitiveTag +{ + short value; + + public ShortTag() + { + } + + public ShortTag(short value) + { + this.value = value; + } + + @Override + public int getId() + { + return 2; + } + + @Override + public String getTypeName() + { + return "Short"; + } + + @Override + public long countBytes(long input) + { + return input + 2L; + } + + @Override + public void write(ExtendedDataOutput output, TagCompressor compressor) throws IOException + { + output.writeShort(value); + } + + @Override + public void read(ExtendedDataInput input, TagDecompressor decompressor, int depthLimit, DataTagLimiter limiter) throws IOException + { + value = input.readShort(); + limiter.countBytes(2); + } + + @Override + public ShortTag copy() + { + return new ShortTag(value); + } + + @Override + public int hashCode() + { + return value; + } + + @Override + public boolean equals(Object obj) + { + return obj instanceof ShortTag && ((ShortTag)obj).value == value; + } + + @Override + public String toString() + { + return value+"s"; + } + + @Override + public String prettyPrint(String indent, int depth) + { + return new StringBuilder().append(NUMBER_HIGHLIGHT).append(value).append(TYPE_HIGHLIGHT).append("s").append(RESET_HIGHLIGHT).toString(); + } + + @Override + public byte asByte() + { + return (byte)value; + } + + @Override + public short asShort() + { + return value; + } + + @Override + public int asInt() + { + return value; + } + + @Override + public long asLong() + { + return value; + } + + @Override + public float asFloat() + { + return value; + } + + @Override + public double asDouble() + { + return value; + } + +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/StringTag.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/StringTag.java new file mode 100644 index 0000000..520291d --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/simple/StringTag.java @@ -0,0 +1,125 @@ +package speiger.src.coreengine.utils.io.dataTag.simple; + +import java.io.IOException; + +import speiger.src.coreengine.utils.io.dataTag.DataTag; +import speiger.src.coreengine.utils.io.dataTag.compression.DataTagLimiter; +import speiger.src.coreengine.utils.io.dataTag.compression.TagCompressor; +import speiger.src.coreengine.utils.io.dataTag.compression.TagDecompressor; +import speiger.src.coreengine.utils.io.streams.ExtendedDataInput; +import speiger.src.coreengine.utils.io.streams.ExtendedDataOutput; + +public class StringTag implements DataTag +{ + String value; + + public StringTag() + { + this(""); + } + + public StringTag(String value) + { + this.value = value == null ? "" : value; + } + + @Override + public int getId() + { + return 8; + } + + @Override + public String getTypeName() + { + return "String"; + } + + @Override + public long countBytes(long input) + { + return input + 4L + (value.length() * 2L); + } + + @Override + public void write(ExtendedDataOutput output, TagCompressor compressor) throws IOException + { + output.writeString(value); + } + + @Override + public void read(ExtendedDataInput input, TagDecompressor decompressor, int depthLimit, DataTagLimiter limiter) throws IOException + { + value = input.readString(); + limiter.countBytes((value.length() * 2) + 4); + } + + @Override + public StringTag copy() + { + return new StringTag(value); + } + + public String get() + { + return value; + } + + @Override + public int hashCode() + { + return value.hashCode(); + } + + @Override + public boolean equals(Object obj) + { + return obj instanceof StringTag && ((StringTag)obj).value.equals(value); + } + + @Override + public String toString() + { + return quoteAndEscape(value); + } + + @Override + public String prettyPrint(String indent, int depth) + { + StringBuilder builder = quoteAndEscapeBuilder(value).insert(1, STRING_HIGHLIGHT); + return builder.insert(builder.length() - 1, RESET_HIGHLIGHT).toString(); + } + + public static String quoteAndEscape(String value) + { + return quoteAndEscapeBuilder(value).toString(); + } + + public static StringBuilder quoteAndEscapeBuilder(String value) + { + StringBuilder builder = new StringBuilder(" "); + char c0 = 0; + for(int i = 0,m=value.length();i < m;++i) + { + char c1 = value.charAt(i); + if(c1 == '\\') + { + builder.append('\\'); + } + else if(c1 == '"' || c1 == '\'') + { + if(c0 == 0) + { + c0 = (char)(c1 == '"' ? 39 : 34); + } + if(c0 == c1) + { + builder.append('\\'); + } + } + builder.append(c1); + } + builder.setCharAt(0, c0 == 0 ? '"' : c0); + return builder.append(c0 == 0 ? '"' : c0); + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/special/IListTag.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/special/IListTag.java new file mode 100644 index 0000000..1deb524 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/special/IListTag.java @@ -0,0 +1,67 @@ +package speiger.src.coreengine.utils.io.dataTag.special; + +import speiger.src.coreengine.utils.io.dataTag.DataTag; +import speiger.src.coreengine.utils.io.dataTag.array.ByteArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.DoubleArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.FloatArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.IntArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.LongArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.MediumArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.ShortArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.StringArrayTag; +import speiger.src.coreengine.utils.io.dataTag.simple.ByteTag; +import speiger.src.coreengine.utils.io.dataTag.simple.DoubleTag; +import speiger.src.coreengine.utils.io.dataTag.simple.FloatTag; +import speiger.src.coreengine.utils.io.dataTag.simple.IntTag; +import speiger.src.coreengine.utils.io.dataTag.simple.LongTag; +import speiger.src.coreengine.utils.io.dataTag.simple.MediumTag; +import speiger.src.coreengine.utils.io.dataTag.simple.PrimitiveTag; +import speiger.src.coreengine.utils.io.dataTag.simple.ShortTag; +import speiger.src.coreengine.utils.io.dataTag.simple.StringTag; + +public interface IListTag extends DataTag +{ + public void add(DataTag tag); + public default void addList(ListTag list) { if(!list.isEmpty()) add(list); } + public default void addMap(MapTag map) { if(!map.isEmpty()) add(map); } + public DataTag get(int index); + + public default void addByte(byte value) {add(new ByteTag(value));} + public default void addShort(short value) {add(new ShortTag(value));} + public default void addMedium(int value) {add(new MediumTag(value));} + public default void addInt(int value) {add(new IntTag(value));} + public default void addLong(long value) {add(new LongTag(value));} + public default void addFloat(float value) {add(new FloatTag(value));} + public default void addDouble(double value) {add(new DoubleTag(value));} + public default void addString(String value) {add(new StringTag(value));} + + public default void addByteArray(byte[] value) {add(new ByteArrayTag(value));} + public default void addShortArray(short[] value) {add(new ShortArrayTag(value));} + public default void addMediumArray(int[] value) {add(new MediumArrayTag(value));} + public default void addIntArray(int[] value) {add(new IntArrayTag(value));} + public default void addLongArray(long[] value) {add(new LongArrayTag(value));} + public default void addFloatArray(float[] value) {add(new FloatArrayTag(value));} + public default void addDoubleArray(double[] value) {add(new DoubleArrayTag(value));} + public default void addStringArray(String[] value) {add(new StringArrayTag(value));} + + public default byte getByte(int index) {return ((PrimitiveTag)get(index)).asByte();} + public default short getShort(int index) {return ((PrimitiveTag)get(index)).asShort();} + public default int getMedium(int index) {return ((PrimitiveTag)get(index)).asInt();} + public default int getInt(int index) {return ((PrimitiveTag)get(index)).asInt();} + public default long getLong(int index) {return ((PrimitiveTag)get(index)).asLong();} + public default float getFloat(int index) {return ((PrimitiveTag)get(index)).asFloat();} + public default double getDouble(int index) {return ((PrimitiveTag)get(index)).asDouble();} + public default String getString(int index) {return ((StringTag)get(index)).get();} + + public default byte[] getByteArray(int index) {return ((ByteArrayTag)get(index)).get();} + public default short[] getShortArray(int index) {return ((ShortArrayTag)get(index)).get();} + public default int[] getMediumArray(int index) {return ((MediumArrayTag)get(index)).get();} + public default int[] getIntArray(int index) {return ((IntArrayTag)get(index)).get();} + public default long[] getLongArray(int index) {return ((LongArrayTag)get(index)).get();} + public default float[] getFloatArray(int index) {return ((FloatArrayTag)get(index)).get();} + public default double[] getDoubleArray(int index) {return ((DoubleArrayTag)get(index)).get();} + public default String[] getStringArray(int index) {return ((StringArrayTag)get(index)).get();} + + public default ListTag getList(int index){return (ListTag)get(index);} + public default MapTag getMap(int index){return (MapTag)get(index);} +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/special/IMapTag.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/special/IMapTag.java new file mode 100644 index 0000000..d99536f --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/special/IMapTag.java @@ -0,0 +1,249 @@ +package speiger.src.coreengine.utils.io.dataTag.special; + +import java.util.UUID; + +import speiger.src.coreengine.utils.io.dataTag.DataTag; +import speiger.src.coreengine.utils.io.dataTag.array.ByteArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.DoubleArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.FloatArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.IntArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.LongArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.MediumArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.ShortArrayTag; +import speiger.src.coreengine.utils.io.dataTag.array.StringArrayTag; +import speiger.src.coreengine.utils.io.dataTag.simple.ByteTag; +import speiger.src.coreengine.utils.io.dataTag.simple.DoubleTag; +import speiger.src.coreengine.utils.io.dataTag.simple.FloatTag; +import speiger.src.coreengine.utils.io.dataTag.simple.IntTag; +import speiger.src.coreengine.utils.io.dataTag.simple.LongTag; +import speiger.src.coreengine.utils.io.dataTag.simple.MediumTag; +import speiger.src.coreengine.utils.io.dataTag.simple.PrimitiveTag; +import speiger.src.coreengine.utils.io.dataTag.simple.ShortTag; +import speiger.src.coreengine.utils.io.dataTag.simple.StringTag; + +public interface IMapTag extends DataTag +{ + public void setTag(String id, DataTag tag); + public default void setList(String id, ListTag tag) { if(!tag.isEmpty()) setTag(id, tag); } + public default void setMap(String id, MapTag tag) { if(!tag.isEmpty()) setTag(id, tag); } + + public default void setBoolean(String id, boolean value) {setByte(id, (byte)(value ? 1 : 0), (byte)0);} + public default void setByte(String id, byte value) {setByte(id, value, (byte)0);} + public default void setByte(String id, byte value, byte defaultValue) + { + if(value != defaultValue) + { + setTag(id, new ByteTag(value)); + } + } + + public default void setShort(String id, short value) {setShort(id, value, (short)0);} + public default void setShort(String id, short value, short defaultValue) + { + if(value != defaultValue) + { + setTag(id, new ShortTag(value)); + } + } + + public default void setMedium(String id, int value) {setMedium(id, value, 0);} + public default void setMedium(String id, int value, int defaultValue) + { + if(value != defaultValue) + { + setTag(id, new MediumTag(value)); + } + } + + public default void setInt(String id, int value) {setInt(id, value, 0);} + public default void setInt(String id, int value, int defaultValue) + { + if(value != defaultValue) + { + setTag(id, new IntTag(value)); + } + } + + public default void setLong(String id, long value) {setLong(id, value, 0L);} + public default void setLong(String id, long value, long defaultValue) + { + if(value != defaultValue) + { + setTag(id, new LongTag(value)); + } + } + + public default void setFloat(String id, float value) {setFloat(id, value, 0F);} + public default void setFloat(String id, float value, float defaultValue) + { + if(value != defaultValue) + { + setTag(id, new FloatTag(value)); + } + } + + public default void setDouble(String id, double value) {setDouble(id, value, 0D);} + public default void setDouble(String id, double value, double defaultValue) + { + if(value != defaultValue) + { + setTag(id, new DoubleTag(value)); + } + } + + public default void setString(String id, String value) {setString(id, value, "");} + public default void setString(String id, String value, String defaultValue) + { + if(!value.equals(defaultValue)) + { + setTag(id, new StringTag(value)); + } + } + + public default void setByteArray(String id, byte[] data) {if(data.length > 0){setTag(id, new ByteArrayTag(data));}} + public default void setShortArray(String id, short[] data) {if(data.length > 0){setTag(id, new ShortArrayTag(data));}} + public default void setMediumArray(String id, int[] data) {if(data.length > 0){setTag(id, new MediumArrayTag(data));}} + public default void setIntArray(String id, int[] data) {if(data.length > 0){setTag(id, new IntArrayTag(data));}} + public default void setLongArray(String id, long[] data) {if(data.length > 0){setTag(id, new LongArrayTag(data));}} + public default void setFloatArray(String id, float[] data) {if(data.length > 0){setTag(id, new FloatArrayTag(data));}} + public default void setDoubleArray(String id, double[] data) {if(data.length > 0){setTag(id, new DoubleArrayTag(data));}} + public default void setStringArray(String id, String[] data) {if(data.length > 0){setTag(id, new StringArrayTag(data));}} + + public default void setUUID(String id, UUID data) { if(data != null) setLongArray(id, new long[]{data.getMostSignificantBits(), data.getLeastSignificantBits()});} + + public DataTag get(String id); + + public default boolean getBoolean(String id) { return getByte(id, (byte)0) != 0;} + public default byte getByte(String id) {return getByte(id, (byte)0);} + public default byte getByte(String id, byte defaultValue) + { + DataTag tag = get(id); + return isPrimitve(tag) ? ((PrimitiveTag)tag).asByte() : defaultValue; + } + + public default short getShort(String id) {return getShort(id, (short)0);} + public default short getShort(String id, short defaultValue) + { + DataTag tag = get(id); + return isPrimitve(tag) ? ((PrimitiveTag)tag).asShort() : defaultValue; + } + + public default int getMedium(String id) {return getMedium(id, 0);} + public default int getMedium(String id, int defaultValue) + { + DataTag tag = get(id); + return isPrimitve(tag) ? ((PrimitiveTag)tag).asInt() : defaultValue; + } + + public default int getInt(String id) {return getInt(id, 0);} + public default int getInt(String id, int defaultValue) + { + DataTag tag = get(id); + return isPrimitve(tag) ? ((PrimitiveTag)tag).asInt() : defaultValue; + } + + public default long getLong(String id) {return getLong(id, 0L);} + public default long getLong(String id, long defaultValue) + { + DataTag tag = get(id); + return isPrimitve(tag) ? ((PrimitiveTag)tag).asLong() : defaultValue; + } + + public default float getFloat(String id) {return getFloat(id, 0F);} + public default float getFloat(String id, float defaultValue) + { + DataTag tag = get(id); + return isPrimitve(tag) ? ((PrimitiveTag)tag).asFloat() : defaultValue; + } + + public default double getDouble(String id) {return getDouble(id, 0D);} + public default double getDouble(String id, double defaultValue) + { + DataTag tag = get(id); + return isPrimitve(tag) ? ((PrimitiveTag)tag).asByte() : defaultValue; + } + + public default String getString(String id) {return getString(id, "");} + public default String getString(String id, String defaultValue) + { + DataTag tag = get(id); + return isType(tag, 8) ? ((StringTag)tag).get() : defaultValue; + } + + public default byte[] getByteArray(String id) + { + DataTag tag = get(id); + return isType(tag, 9) ? ((ByteArrayTag)tag).get() : new byte[0]; + } + + public default short[] getShortArray(String id) + { + DataTag tag = get(id); + return isType(tag, 10) ? ((ShortArrayTag)tag).get() : new short[0]; + } + + public default int[] getMediumArray(String id) + { + DataTag tag = get(id); + return isType(tag, 11) ? ((MediumArrayTag)tag).get() : new int[0]; + } + + public default int[] getIntArray(String id) + { + DataTag tag = get(id); + return isType(tag, 12) ? ((IntArrayTag)tag).get() : new int[0]; + } + + public default long[] getLongArray(String id) + { + DataTag tag = get(id); + return isType(tag, 13) ? ((LongArrayTag)tag).get() : new long[0]; + } + + public default float[] getFloatArray(String id) + { + DataTag tag = get(id); + return isType(tag, 14) ? ((FloatArrayTag)tag).get() : new float[0]; + } + + public default double[] getDoubleArray(String id) + { + DataTag tag = get(id); + return isType(tag, 15) ? ((DoubleArrayTag)tag).get() : new double[0]; + } + + public default String[] getStringArray(String id) + { + DataTag tag = get(id); + return isType(tag, 16) ? ((StringArrayTag)tag).get() : new String[0]; + } + + public default UUID getUUID(String id) + { + long[] data = getLongArray(id); + return data == null || data.length != 2 ? null : new UUID(data[0], data[1]); + } + + public default MapTag getMap(String id) + { + DataTag tag = get(id); + return isType(tag, 17) ? (MapTag)tag : new MapTag(); + } + + public default ListTag getList(String id) + { + DataTag tag = get(id); + return isType(tag, 18) ? (ListTag)tag : new ListTag(); + } + + public default boolean isPrimitve(DataTag tag) + { + int id = tag == null ? 0 : tag.getId(); + return id >= 1 && id <= 7; + } + + public default boolean isType(DataTag tag, int type) + { + return (tag == null ? 0 : tag.getId()) == type; + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/special/ListTag.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/special/ListTag.java new file mode 100644 index 0000000..7e356d4 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/special/ListTag.java @@ -0,0 +1,257 @@ +package speiger.src.coreengine.utils.io.dataTag.special; + +import java.io.IOException; +import java.util.Iterator; +import java.util.List; +import java.util.StringJoiner; +import java.util.function.Consumer; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.coreengine.utils.io.dataTag.DataTag; +import speiger.src.coreengine.utils.io.dataTag.compression.DataTagLimiter; +import speiger.src.coreengine.utils.io.dataTag.compression.TagCompressor; +import speiger.src.coreengine.utils.io.dataTag.compression.TagDecompressor; +import speiger.src.coreengine.utils.io.streams.ExtendedDataInput; +import speiger.src.coreengine.utils.io.streams.ExtendedDataOutput; + +public class ListTag implements IListTag, Iterable +{ + List list = new ObjectArrayList(); + + @Override + public int getId() + { + return 18; + } + + @Override + public String getTypeName() + { + return "List"; + } + + @Override + public void addKeys(Consumer keys) + { + for(int i = 0,m=list.size();i= 512) + { + throw new IllegalStateException("Depth Limit Exceeded"); + } + limiter.countBytes(6); + int size = input.readVarInt(); + if(size <= 0) + { + return; + } + byte type = input.readByte(); + for(int i = 0;i= 1 && list.get(0).getId() < 8) + { + StringJoiner joiner = new StringJoiner(",", "[", "]"); + for(int i = 0,m=list.size();i= list.size()) + { + throw new ArrayIndexOutOfBoundsException(index); + } + else if(list.get(0).getId() != tag.getId() || tag.getId() == 0) + { + throw new IllegalStateException("Tag Type didn't match. [Inserted="+tag.getTypeName()+", Expected="+list.get(0).getTypeName()+"]"); + } + return list.set(index, tag); + } + + public DataTag remove(int index) + { + return list.remove(index); + } + + @Override + public DataTag get(int index) + { + return list.get(index); + } + + public boolean isEmpty() + { + return list.isEmpty(); + } + + public int size() + { + return list.size(); + } + + public ListTag subList(int from, int to) + { + ListTag list = new ListTag(); + for(int i = 0,m=to-from;i iterator() + { + return new Iterator(){ + int index = 0; + @Override + public boolean hasNext() + { + return index < list.size(); + } + @Override + public DataTag next() + { + return list.get(index++); + } + }; + } + + public Iterable typeIterator(Class clz) + { + return () -> new Iterator(){ + int index = 0; + @Override + public boolean hasNext() + { + return index < list.size(); + } + @Override + public S next() + { + return list.get(index++).cast(); + } + }; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/dataTag/special/MapTag.java b/src/main/java/speiger/src/coreengine/utils/io/dataTag/special/MapTag.java new file mode 100644 index 0000000..b626b0b --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/dataTag/special/MapTag.java @@ -0,0 +1,210 @@ +package speiger.src.coreengine.utils.io.dataTag.special; + +import java.io.IOException; +import java.util.List; +import java.util.StringJoiner; +import java.util.function.Consumer; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap; +import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap; +import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap.Entry; +import speiger.src.collections.objects.sets.ObjectSet; +import speiger.src.collections.objects.utils.ObjectSets; +import speiger.src.collections.objects.utils.maps.Object2ObjectMaps; +import speiger.src.coreengine.utils.io.dataTag.DataTag; +import speiger.src.coreengine.utils.io.dataTag.compression.DataTagLimiter; +import speiger.src.coreengine.utils.io.dataTag.compression.TagCompressor; +import speiger.src.coreengine.utils.io.dataTag.compression.TagDecompressor; +import speiger.src.coreengine.utils.io.dataTag.simple.PrimitiveTag; +import speiger.src.coreengine.utils.io.dataTag.simple.StringTag; +import speiger.src.coreengine.utils.io.streams.ExtendedDataInput; +import speiger.src.coreengine.utils.io.streams.ExtendedDataOutput; + +public class MapTag implements IMapTag +{ + Object2ObjectMap tags = new Object2ObjectLinkedOpenHashMap(); + + public MapTag() + { + } + + @Override + public int getId() + { + return 17; + } + + @Override + public String getTypeName() + { + return "Map"; + } + + @Override + public long countBytes(long input) + { + input += 4; + for(Entry entry : Object2ObjectMaps.fastIterable(tags)) + { + input += 1; + input = entry.getValue().countBytes(input); + } + return input; + } + + @Override + public void addKeys(Consumer keys) + { + for(Entry entry : Object2ObjectMaps.fastIterable(tags)) + { + keys.accept(entry.getKey()); + entry.getValue().addKeys(keys); + } + } + + @Override + public void write(ExtendedDataOutput output, TagCompressor compressor) throws IOException + { + output.writeVarInt(tags.size()); + for(Entry entry : Object2ObjectMaps.fastIterable(tags)) + { + output.writeByte(entry.getValue().getId()); + compressor.writeKey(entry.getKey()); + entry.getValue().write(output, compressor); + } + } + + @Override + public void read(ExtendedDataInput input, TagDecompressor decompressor, int depthLimit, DataTagLimiter limiter) throws IOException + { + if(depthLimit >= 512) + { + throw new IllegalStateException("Depth Limit Exceeded"); + } + int size = input.readVarInt(); + limiter.countBytes(4 + size); + for(int i = 0;i < size;i++) + { + byte type = input.readByte(); + String id = decompressor.readKey(); + limiter.countBytes(4 + (id.length() * 2)); + tags.put(id, decompressor.readSubTag(type, id, depthLimit + 1)); + } + } + + @Override + public MapTag copy() + { + MapTag tag = new MapTag(); + for(Entry entry : Object2ObjectMaps.fastIterable(tags)) + { + tag.tags.put(entry.getKey(), entry.getValue().copy()); + } + return tag; + } + + @Override + public void setTag(String id, DataTag tag) + { + tags.put(id, tag); + } + + public boolean contains(String id) + { + return tags.containsKey(id); + } + + public boolean contains(String id, int tagType) + { + DataTag tag = tags.get(id); + return tag != null && ((tagType == 99 && tag instanceof PrimitiveTag) || tag.getId() == tagType); + } + + @Override + public DataTag get(String id) + { + return tags.get(id); + } + + public DataTag remove(String id) + { + return tags.remove(id); + } + + public boolean isEmpty() + { + return tags.isEmpty(); + } + + public int size() + { + return tags.size(); + } + + public ObjectSet keySet() + { + return ObjectSets.unmodifiable(tags.keySet()); + } + + public ObjectSet> entrySet() + { + return ObjectSets.unmodifiable(tags.object2ObjectEntrySet()); + } + + @Override + public int hashCode() + { + return tags.hashCode(); + } + + @Override + public boolean equals(Object obj) + { + return obj instanceof MapTag && ((MapTag)obj).tags.equals(tags); + } + + @Override + public String toString() + { + StringJoiner joiner = new StringJoiner(",", "{", "}"); + List keys = new ObjectArrayList<>(tags.keySet()); + keys.sort(null); + for(int i = 0, m = keys.size();i < m;i++) + { + joiner.add(StringTag.quoteAndEscapeBuilder(keys.get(i)).append(":").append(tags.get(keys.get(i))).toString()); + } + return joiner.toString(); + } + + @Override + public String prettyPrint(String indent, int depth) + { + if(tags.isEmpty()) + { + return "{}"; + } + List keys = new ObjectArrayList<>(tags.keySet()); + keys.sort(null); + StringBuilder builder = new StringBuilder().append("{"); + String s = DataTag.repeate(indent, depth+1); + if(!indent.isEmpty()) + { + builder.append('\n'); + } + for(int i = 0,m=keys.size();i> implements ISetting +{ + protected final String id; + List> listeners = new ObjectArrayList>(); + + public BaseSetting(String id) + { + this.id = id; + } + + @Override + public T addListener(Consumer listener) + { + listeners.add(listener); + return (T)this; + } + + protected void notifyListeners() + { + if(listeners.isEmpty()) + { + return; + } + T value = (T)this; + for(int i = 0,m=listeners.size();i +{ + final boolean defaultValue; + boolean value; + + public BooleanSetting(String id, boolean defaultValue) + { + super(id); + this.defaultValue = defaultValue; + value = defaultValue; + } + + @Override + public boolean isDefault() + { + return value == defaultValue; + } + + @Override + public void setDefault() + { + if(value != defaultValue) + { + value = defaultValue; + notifyListeners(); + } + } + + public BooleanSetting setValue(boolean value) + { + if(this.value != value) + { + this.value = value; + notifyListeners(); + } + return this; + } + + public boolean getDefaultValue() + { + return defaultValue; + } + + public boolean getValue() + { + return value; + } + + @Override + public void write(JsonObject object) + { + object.addProperty(id, value); + } + + @Override + public void read(JsonObject object) + { + value = JsonUtil.getOrDefault(object, id, defaultValue); + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/settings/ISetting.java b/src/main/java/speiger/src/coreengine/utils/io/settings/ISetting.java new file mode 100644 index 0000000..c50b313 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/settings/ISetting.java @@ -0,0 +1,16 @@ +package speiger.src.coreengine.utils.io.settings; + +import java.util.function.Consumer; + +import com.google.gson.JsonObject; + +public interface ISetting> +{ + public T addListener(Consumer listener); + + public boolean isDefault(); + public void setDefault(); + + public void write(JsonObject object); + public void read(JsonObject object); +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/settings/IntSetting.java b/src/main/java/speiger/src/coreengine/utils/io/settings/IntSetting.java new file mode 100644 index 0000000..a7259b5 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/settings/IntSetting.java @@ -0,0 +1,66 @@ +package speiger.src.coreengine.utils.io.settings; + +import com.google.gson.JsonObject; + +import speiger.src.coreengine.utils.helpers.JsonUtil; + +public class IntSetting extends BaseSetting +{ + final int defaultValue; + int value; + + public IntSetting(String id, int defaultValue) + { + super(id); + this.defaultValue = defaultValue; + value = defaultValue; + } + + @Override + public boolean isDefault() + { + return value == defaultValue; + } + + @Override + public void setDefault() + { + if(value != defaultValue) + { + value = defaultValue; + notifyListeners(); + } + } + + public int getValue() + { + return value; + } + + public int getDefaultValue() + { + return defaultValue; + } + + public IntSetting setValue(int value) + { + if(this.value != value) + { + this.value = value; + notifyListeners(); + } + return this; + } + + @Override + public void write(JsonObject object) + { + object.addProperty(id, value); + } + + @Override + public void read(JsonObject object) + { + value = JsonUtil.getOrDefault(object, id, defaultValue); + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/settings/Settings.java b/src/main/java/speiger/src/coreengine/utils/io/settings/Settings.java new file mode 100644 index 0000000..90b44b0 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/settings/Settings.java @@ -0,0 +1,50 @@ +package speiger.src.coreengine.utils.io.settings; + +import java.io.File; +import java.io.FileWriter; + +import com.google.gson.JsonObject; +import com.google.gson.internal.Streams; +import com.google.gson.stream.JsonWriter; + +import speiger.src.coreengine.utils.helpers.JsonUtil; + +public class Settings +{ + File file; + ISetting[] settings; + + public Settings(File path, ISetting[] settings) + { + file = path; + this.settings = settings; + } + + public void load() + { + JsonObject obj = JsonUtil.loadFile(file); + for(int i = 0,m=settings.length;i 5) + { + throw new RuntimeException("VarInt too big"); + } + } + while((temp & 128) == 128); + return data; + } + + public default long readVarLong() throws IOException + { + long data = 0L; + int index = 0; + byte temp; + do + { + temp = readByte(); + data |= (long)(temp & 127) << index++ * 7; + if(index > 10) + { + throw new RuntimeException("VarLong too big"); + } + } + while((temp & 128) == 128); + return data; + } + + public default byte[] readBytes() throws IOException + { + byte[] data = new byte[readVarInt()]; + readFully(data); + return data; + } + + public default UUID readUUID() throws IOException + { + return new UUID(readLong(), readLong()); + } + + public default > T readEnumValue(Class enumType) throws IOException + { + return enumType.getEnumConstants()[readVarInt()]; + } + + public default String readString() throws IOException + { + return new String(readBytes(), StandardCharsets.UTF_8); + } + + public static class ExtendedDataInputStream extends DataInputStream + implements ExtendedDataInput + { + public ExtendedDataInputStream(InputStream in) + { + super(in); + } + } + + public static class WrappedExtendedDataInput implements ExtendedDataInput + { + DataInput input; + + public WrappedExtendedDataInput(DataInput input) + { + this.input = input; + } + + @Override + public void close() throws Exception + { + if(input instanceof AutoCloseable) + { + ((AutoCloseable)input).close(); + } + } + + @Override + public void readFully(byte[] b) throws IOException + { + input.readFully(b); + } + + @Override + public void readFully(byte[] b, int off, int len) throws IOException + { + input.readFully(b, off, len); + } + + @Override + public int skipBytes(int n) throws IOException + { + return input.skipBytes(n); + } + + @Override + public boolean readBoolean() throws IOException + { + return input.readBoolean(); + } + + @Override + public byte readByte() throws IOException + { + return input.readByte(); + } + + @Override + public int readUnsignedByte() throws IOException + { + return input.readUnsignedByte(); + } + + @Override + public short readShort() throws IOException + { + return input.readShort(); + } + + @Override + public int readUnsignedShort() throws IOException + { + return input.readUnsignedShort(); + } + + @Override + public char readChar() throws IOException + { + return input.readChar(); + } + + @Override + public int readInt() throws IOException + { + return input.readInt(); + } + + @Override + public long readLong() throws IOException + { + return input.readLong(); + } + + @Override + public float readFloat() throws IOException + { + return input.readFloat(); + } + + @Override + public double readDouble() throws IOException + { + return input.readDouble(); + } + + @Override + public String readLine() throws IOException + { + return input.readLine(); + } + + @Override + public String readUTF() throws IOException + { + return input.readUTF(); + } + } +} \ No newline at end of file diff --git a/src/main/java/speiger/src/coreengine/utils/io/streams/ExtendedDataOutput.java b/src/main/java/speiger/src/coreengine/utils/io/streams/ExtendedDataOutput.java new file mode 100644 index 0000000..27b6caf --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/streams/ExtendedDataOutput.java @@ -0,0 +1,100 @@ +package speiger.src.coreengine.utils.io.streams; + +import java.io.BufferedOutputStream; +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.Flushable; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.UUID; + +public interface ExtendedDataOutput extends DataOutput, Flushable, AutoCloseable +{ + public static ExtendedDataOutput createBuffered(OutputStream stream) + { + return new ExtendedDataOutputStream(new BufferedOutputStream(stream)); + } + + public static ExtendedDataOutput createBuffered(OutputStream stream, int size) + { + return new ExtendedDataOutputStream(new BufferedOutputStream(stream, size)); + } + + public static ExtendedDataOutput create(OutputStream stream) + { + return new ExtendedDataOutputStream(stream); + } + + public default void writeMedium(int v) throws IOException + { + write((v >>> 16) & 0xFF); + write((v >>> 8) & 0xFF); + write((v >>> 0) & 0xFF); + } + + public default void writeVarInt(int v) throws IOException + { + while((v & -128) != 0) + { + writeByte(v & 127 | 128); + v >>>= 7; + } + writeByte(v); + } + + public default void writeVarLong(long v) throws IOException + { + while((v & -128L) != 0L) + { + writeByte((int)(v & 127L) | 128); + v >>>= 7; + } + writeByte((int)v); + } + + public default void writeBytes(byte[] v) throws IOException + { + writeBytes(v, 0, v.length); + } + + public default void writeBytes(byte[] v, int offset, int length) throws IOException + { + writeVarInt(length); + write(v, offset, length); + } + + public default void writeUUID(UUID id) throws IOException + { + writeLong(id.getMostSignificantBits()); + writeLong(id.getLeastSignificantBits()); + } + + public default void writeEnum(Enum v) throws IOException + { + writeVarInt(v.ordinal()); + } + + public default void writeString(String s) throws IOException + { + writeString(s, 65535); + } + + public default void writeString(String s, int maxBytes) throws IOException + { + byte[] data = s.getBytes(StandardCharsets.UTF_8); + if(data.length > maxBytes) + { + throw new IOException("Encoded String was to big. ("+data.length+", Limit: "+maxBytes+", Diff: "+(data.length - maxBytes)+")"); + } + writeBytes(data); + } + + public static class ExtendedDataOutputStream extends DataOutputStream implements ExtendedDataOutput + { + public ExtendedDataOutputStream(OutputStream out) + { + super(out); + } + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/io/streams/StringReader.java b/src/main/java/speiger/src/coreengine/utils/io/streams/StringReader.java new file mode 100644 index 0000000..8430869 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/io/streams/StringReader.java @@ -0,0 +1,172 @@ +package speiger.src.coreengine.utils.io.streams; + +public class StringReader +{ + String text; + int index; + + public StringReader(String text) + { + this.text = text; + if(text == null) + { + throw new IllegalStateException("String is null"); + } + } + + public int getIndex() + { + return index; + } + + public void setIndex(int index) + { + this.index = index; + } + + public boolean canRead() + { + return canRead(1); + } + + public boolean canRead(int amount) + { + return index + amount <= text.length(); + } + + public char peek() + { + return text.charAt(index); + } + + public char peek(int offset) + { + return text.charAt(index + offset); + } + + public char pull() + { + return text.charAt(index++); + } + + public void skip() + { + index++; + } + + public void skip(int amount) + { + index += amount; + } + + public void skipEmpty() + { + for(;canRead() && Character.isWhitespace(peek());skip()) + ; + } + + public void requirePresent(char value) + { + if(!canRead() || peek() != value) + { + throw new IllegalStateException("Char not present"); + } + skip(); + } + + public boolean isPresent(char value) + { + if(!canRead() || peek() != value) + { + return false; + } + skip(); + return true; + } + + public static boolean isQuoteCharacter(char c) + { + return c == '"' || c == '\''; + } + + public static boolean isValidTextCharacter(char c) + { + return c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c == '_' || c == '-' || c == '.' || c == '+'; + } + + public String readText() + { + int start = index; + while(canRead() && isValidTextCharacter(peek())) + { + skip(); + } + return text.substring(start, index); + } + + public String readQuotedText() + { + if(!canRead()) + { + return ""; + } + final char next = peek(); + if(isQuoteCharacter(next)) + { + skip(); + return readUntil(next); + } + throw new IllegalStateException("No Quote found"); + } + + public String readString() + { + if(!canRead()) + { + return ""; + } + final char next = peek(); + if(isQuoteCharacter(next)) + { + skip(); + return readUntil(next); + } + return readText(); + } + + public String readUntil(char end) + { + StringBuilder builder = new StringBuilder(); + boolean skip = false; + while(canRead()) + { + char c = pull(); + if(skip) + { + if(c == end || c == '\\') + { + builder.append(c); + skip = false; + } + else + { + setIndex(getIndex() - 1); + throw new IllegalStateException("Skipping non Skippable Letter"); + } + } + else if(c == '\\') + { + skip = true; + } + else if(c == end) + { + return builder.toString(); + } + else + { + builder.append(c); + } + } + throw new ArrayIndexOutOfBoundsException("Ran out of characters"); + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/profiler/EmptyProfiler.java b/src/main/java/speiger/src/coreengine/utils/profiler/EmptyProfiler.java new file mode 100644 index 0000000..a1110d8 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/profiler/EmptyProfiler.java @@ -0,0 +1,36 @@ +package speiger.src.coreengine.utils.profiler; + +import java.util.function.ObjIntConsumer; + +public final class EmptyProfiler implements IProfiler +{ + public static final IProfiler INSTANCE = new EmptyProfiler(); + + @Override + public String getName(){return "Empty";} + @Override + public IProfilerEntry getEntry(String name){return null;} + @Override + public long getTicksRan(){return 0;} + @Override + public void enable(){} + @Override + public void disable(){} + @Override + public boolean isEnabled(){return false;} + @Override + public void addListener(ObjIntConsumer listener){} + @Override + public void removeListener(ObjIntConsumer listener){} + @Override + public IProfiler start(String name){return this;} + @Override + public IProfiler start(Class clz){return this;} + @Override + public IProfiler stop(){return this;} + @Override + public IProfiler next(String name){return this;} + @Override + public void onFrameEnded(boolean count){} + +} diff --git a/src/main/java/speiger/src/coreengine/utils/profiler/GPUProfiler.java b/src/main/java/speiger/src/coreengine/utils/profiler/GPUProfiler.java new file mode 100644 index 0000000..9a3fcaf --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/profiler/GPUProfiler.java @@ -0,0 +1,173 @@ +package speiger.src.coreengine.utils.profiler; + +import java.util.Map; +import java.util.Set; +import java.util.function.ObjIntConsumer; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap; +import speiger.src.collections.objects.sets.ObjectLinkedOpenHashSet; +import speiger.src.collections.utils.Stack; +import speiger.src.coreengine.rendering.utils.GLStamper; + +public class GPUProfiler implements IProfiler +{ + IProfiler cpuSide; + String name; + Map cache = new Object2ObjectLinkedOpenHashMap<>(); + Stack activeProfilers = new ObjectArrayList<>(); + Set> listeners = new ObjectLinkedOpenHashSet<>(); + long ticksRan = 0; + int enableRequest = 0; + + public GPUProfiler(String name, IProfiler cpu) + { + this.name = name; + cpuSide = cpu; + } + + @Override + public String getName() + { + return name; + } + + @Override + public IProfilerEntry getEntry(String name) + { + return cache.get(name); + } + + @Override + public long getTicksRan() + { + return ticksRan; + } + + @Override + public void enable() + { + enableRequest++; + if(enableRequest == 1) + { + sendState(0); + } + } + + @Override + public void disable() + { + enableRequest = Math.max(0, enableRequest-1); + if(enableRequest > 0) + { + return; + } + GLStamper.INSTANCE.removeOwner(name); + cache.clear(); + while(!activeProfilers.isEmpty()) + { + activeProfilers.pop(); + } + sendState(1); + } + + @Override + public boolean isEnabled() + { + return enableRequest > 0; + } + + @Override + public void addListener(ObjIntConsumer listener) + { + listeners.add(listener); + if(enableRequest > 0) + { + listener.accept(this, 0); + } + } + + @Override + public void removeListener(ObjIntConsumer listener) + { + listeners.remove(listener); + } + + @Override + public IProfiler start(String name) + { + cpuSide.start(name); + if(enableRequest == 0) + { + return this; + } + String entry = (activeProfilers.isEmpty() ? "" : activeProfilers.top().getPathName()+"/") + name; + GPUProfilerEntry active = cache.get(entry); + if(active == null) + { + active = new GPUProfilerEntry(name, this); + if(!activeProfilers.isEmpty()) + { + activeProfilers.top().addChild(active); + } + cache.put(entry, active); + sendState(2); + } + activeProfilers.push(active); + active.start(); + return this; + } + + @Override + public IProfiler start(Class clz) + { + cpuSide.start(clz); + return enableRequest == 0 ? this : start(clz.getSimpleName()); + } + + @Override + public IProfiler stop() + { + cpuSide.stop(); + if(enableRequest == 0 || activeProfilers.isEmpty()) + { + return this; + } + activeProfilers.pop().stop(); + return this; + } + + @Override + public IProfiler next(String name) + { + return stop().start(name); + } + + private void sendState(int state) + { + if(listeners.isEmpty()) + { + return; + } + for(ObjIntConsumer entry : listeners) + { + entry.accept(this, state); + } + } + + @Override + public void onFrameEnded(boolean end) + { + cpuSide.onFrameEnded(end); + if(enableRequest == 0) + { + return; + } + ticksRan++; + for(GPUProfilerEntry entry : cache.values()) + { + entry.onFrameFinished(end); + } + } + +} diff --git a/src/main/java/speiger/src/coreengine/utils/profiler/GPUProfilerEntry.java b/src/main/java/speiger/src/coreengine/utils/profiler/GPUProfilerEntry.java new file mode 100644 index 0000000..4aa513b --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/profiler/GPUProfilerEntry.java @@ -0,0 +1,151 @@ +package speiger.src.coreengine.utils.profiler; + +import java.util.ArrayList; +import java.util.List; + +import speiger.src.collections.longs.queues.LongArrayFIFOQueue; +import speiger.src.collections.longs.queues.LongPriorityQueue; +import speiger.src.collections.objects.queues.ObjectArrayFIFOQueue; +import speiger.src.collections.objects.queues.ObjectPriorityDequeue; +import speiger.src.coreengine.rendering.utils.GLStamper; +import speiger.src.coreengine.rendering.utils.GLStamper.GLStamp; +import speiger.src.coreengine.utils.profiler.IProfiler.IProfilerEntry; + +public class GPUProfilerEntry implements IProfilerEntry +{ + String name; + String pathName; + IProfiler owner; + IProfilerEntry parent; + List children = new ArrayList(); + int debth = 0; + int totalTicks = 0; + + long maxTime = Long.MIN_VALUE; + long minTime = Long.MAX_VALUE; + long average = 0L; + LongPriorityQueue entries = new LongArrayFIFOQueue(); + long startTime = 0L; + long currentTime = 0L; + + ObjectPriorityDequeue stamps = new ObjectArrayFIFOQueue(); + + public GPUProfilerEntry(String name, IProfiler owner) + { + this.name = name; + pathName = name; + this.owner = owner; + } + + public void addChild(GPUProfilerEntry child) + { + if(child != null) + { + children.add(child); + child.setParent(this); + } + } + + private void setParent(GPUProfilerEntry parent) + { + this.parent = parent; + debth = parent.debth + 1; + pathName = parent.pathName+"/"+name; + } + + public void start() + { + stamps.enqueue(GLStamper.INSTANCE.createStamp(owner.getName()).start()); + } + + public void stop() + { + stamps.last().stop(); + } + + public void onFrameFinished(boolean end) + { + while(!stamps.isEmpty() && stamps.first().isFinished()) + { + GLStamp stamp = stamps.dequeue(); + currentTime += stamp.getResult(); + stamp.release(); + } + average += currentTime; + entries.enqueue(currentTime); + if(entries.size() > 20) + { + average -= entries.dequeue(); + } + currentTime = average / entries.size(); + minTime = Math.min(minTime, currentTime); + maxTime = Math.max(maxTime, currentTime); + currentTime = 0; + if(end && totalTicks++ >= 10) + { + minTime = Long.MAX_VALUE; + maxTime = Long.MIN_VALUE; + totalTicks = 0; + } + } + + @Override + public String getName() + { + return name; + } + + @Override + public String getPathName() + { + return pathName; + } + + @Override + public int getDebth() + { + return debth; + } + + @Override + public IProfiler getOwner() + { + return owner; + } + + @Override + public IProfilerEntry getParent() + { + return parent; + } + + @Override + public int getChildCount() + { + return children.size(); + } + + @Override + public IProfilerEntry getChild(int index) + { + return children.get(index); + } + + @Override + public long getMinTime() + { + return minTime; + } + + @Override + public long getNanoTime() + { + return entries.isEmpty() ? 0L : average / entries.size(); + } + + @Override + public long getMaxTime() + { + return maxTime; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/profiler/IProfiler.java b/src/main/java/speiger/src/coreengine/utils/profiler/IProfiler.java new file mode 100644 index 0000000..0d1142a --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/profiler/IProfiler.java @@ -0,0 +1,164 @@ +package speiger.src.coreengine.utils.profiler; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.ObjIntConsumer; + +import speiger.src.coreengine.math.misc.ColorObject; +import speiger.src.coreengine.utils.helpers.TextUtil; + +public interface IProfiler +{ + + public String getName(); + + public IProfilerEntry getEntry(String name); + + public long getTicksRan(); + + public void enable(); + + public void disable(); + + public default void setState(boolean enabled) + { + if(enabled) + { + enable(); + return; + } + disable(); + } + + public boolean isEnabled(); + + public void addListener(ObjIntConsumer listener); + + public void removeListener(ObjIntConsumer listener); + + public IProfiler start(String name); + + public IProfiler start(Class clz); + + public IProfiler stop(); + + public IProfiler next(String name); + + public void onFrameEnded(boolean count); + + public static interface IProfilerEntry + { + public String getName(); + + public default String getPathName() + { + return getName(); + } + + public long getMinTime(); + + public long getNanoTime(); + + public long getMaxTime(); + + public int getDebth(); + + public int getChildCount(); + + public IProfilerEntry getChild(int index); + + public IProfilerEntry getParent(); + + public IProfiler getOwner(); + + default long getTotalTime() + { + IProfilerEntry entry = this; + while(entry.getParent() != null) + { + entry = entry.getParent(); + } + return entry.getNanoTime(); + } + + public default List getData() + { + List list = new ArrayList(); + long time = getNanoTime(); + long totalTime = getTotalTime(); + long used = 0L; + for(int i = 0;i + { + String name; + ColorObject color; + long nanoTime; + double effect; + double totalEffect; + + public ProfilerData(String name, long time, double effect, double totalEffect) + { + this.name = name; + color = TextUtil.getColorFromText(name); + nanoTime = time; + this.effect = effect; + this.totalEffect = totalEffect; + } + + @Override + public int compareTo(ProfilerData o) + { + if(o.nanoTime > nanoTime) + { + return 1; + } + return o.nanoTime < nanoTime ? -1 : name.compareTo(o.name); + } + + public String getName() + { + return name; + } + + public ColorObject getColor() + { + return color; + } + + public long getNanoTime() + { + return nanoTime; + } + + public double getEffect() + { + return effect; + } + + public double getTotalEffect() + { + return totalEffect; + } + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/profiler/Profiler.java b/src/main/java/speiger/src/coreengine/utils/profiler/Profiler.java new file mode 100644 index 0000000..c39cf6e --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/profiler/Profiler.java @@ -0,0 +1,165 @@ +package speiger.src.coreengine.utils.profiler; + +import java.util.Map; +import java.util.Set; +import java.util.function.ObjIntConsumer; + +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap; +import speiger.src.collections.objects.sets.ObjectLinkedOpenHashSet; +import speiger.src.collections.utils.Stack; + +public class Profiler implements IProfiler +{ + String name; + Map cache = new Object2ObjectLinkedOpenHashMap(); + Stack activeProfilers = new ObjectArrayList(); + Set> listeners = new ObjectLinkedOpenHashSet<>(); + long ticksRan = 0; + int enableRequest = 0; + + public Profiler(String profilerName) + { + name = profilerName; + } + + @Override + public String getName() + { + return name; + } + + @Override + public IProfilerEntry getEntry(String name) + { + return cache.get(name); + } + + @Override + public long getTicksRan() + { + return ticksRan; + } + + @Override + public void enable() + { + enableRequest++; + if(enableRequest == 1) + { + sendState(0); + } + } + + @Override + public void disable() + { + enableRequest = Math.max(0, enableRequest-1); + if(enableRequest > 0) + { + return; + } + cache.clear(); + while(!activeProfilers.isEmpty()) + { + activeProfilers.pop(); + } + sendState(1); + } + + @Override + public boolean isEnabled() + { + return enableRequest > 0; + } + + @Override + public void addListener(ObjIntConsumer listener) + { + listeners.add(listener); + if(enableRequest > 0) + { + listener.accept(this, 0); + } + } + + @Override + public void removeListener(ObjIntConsumer listener) + { + listeners.remove(listener); + } + + @Override + public IProfiler start(String name) + { + if(enableRequest == 0) + { + return this; + } + String entry = (activeProfilers.isEmpty() ? "" : activeProfilers.top().getPathName()+"/") + name; + ProfilerEntry active = cache.get(entry); + if(active == null) + { + active = new ProfilerEntry(name, this); + if(!activeProfilers.isEmpty()) + { + activeProfilers.top().addChild(active); + } + cache.put(entry, active); + sendState(2); + } + activeProfilers.push(active); + active.start(); + return this; + } + + @Override + public IProfiler start(Class clz) + { + return enableRequest == 0 ? this : start(clz.getSimpleName()); + } + + @Override + public IProfiler stop() + { + if(enableRequest == 0 || activeProfilers.isEmpty()) + { + return this; + } + activeProfilers.pop().stop(); + return this; + } + + @Override + public IProfiler next(String name) + { + return stop().start(name); + } + + private void sendState(int state) + { + if(listeners.isEmpty()) + { + return; + } + for(ObjIntConsumer entry : listeners) + { + entry.accept(this, state); + } + } + + @Override + public void onFrameEnded(boolean end) + { + if(enableRequest == 0) + { + return; + } + ticksRan++; + for(ProfilerEntry entry : cache.values()) + { + entry.onFrameFinished(end); + } + } + +} diff --git a/src/main/java/speiger/src/coreengine/utils/profiler/ProfilerEntry.java b/src/main/java/speiger/src/coreengine/utils/profiler/ProfilerEntry.java new file mode 100644 index 0000000..1ff5e32 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/profiler/ProfilerEntry.java @@ -0,0 +1,152 @@ +package speiger.src.coreengine.utils.profiler; + +import java.util.ArrayList; +import java.util.List; + +import speiger.src.collections.longs.queues.LongArrayFIFOQueue; +import speiger.src.collections.longs.queues.LongPriorityQueue; +import speiger.src.coreengine.utils.profiler.IProfiler.IProfilerEntry; + +public class ProfilerEntry implements IProfilerEntry +{ + String name; + String pathName; + IProfiler owner; + IProfilerEntry parent; + List children = new ArrayList(); + int debth = 0; + int totalTicks = 0; + + long maxTime = Long.MIN_VALUE; + long minTime = Long.MAX_VALUE; + long average = 0L; + LongPriorityQueue entries = new LongArrayFIFOQueue(); + long startTime = 0L; + long currentTime = 0L; + boolean didTick = false; + + public ProfilerEntry(String name, IProfiler owner) + { + this.name = name; + pathName = name; + this.owner = owner; + } + + public void addChild(ProfilerEntry child) + { + if(child != null) + { + children.add(child); + child.setParent(this); + } + } + + private void setParent(ProfilerEntry parent) + { + this.parent = parent; + debth = parent.debth + 1; + pathName = parent.pathName+"/"+name; + } + + public void start() + { + startTime = System.nanoTime(); + } + + public void stop() + { + currentTime += (System.nanoTime() - startTime); + didTick = true; + } + + public void onFrameFinished(boolean end) + { + if(!didTick) + { + if(end && totalTicks++ >= 10) + { + minTime = 0; + maxTime = 0; + totalTicks = 0; + } + return; + } + didTick = false; + average += currentTime; + entries.enqueue(currentTime); + if(entries.size() > 20) + { + average -= entries.dequeue(); + } + currentTime = average / entries.size(); + minTime = Math.min(minTime, currentTime); + maxTime = Math.max(maxTime, currentTime); + currentTime = 0; + if(end && totalTicks++ >= 10) + { + minTime = Long.MAX_VALUE; + maxTime = Long.MIN_VALUE; + totalTicks = 0; + } + } + + @Override + public String getName() + { + return name; + } + + @Override + public String getPathName() + { + return pathName; + } + + @Override + public long getMinTime() + { + return minTime; + } + + @Override + public long getNanoTime() + { + return entries.isEmpty() ? 0L : average / entries.size(); + } + + @Override + public long getMaxTime() + { + return maxTime; + } + + @Override + public int getDebth() + { + return debth; + } + + @Override + public int getChildCount() + { + return children.size(); + } + + @Override + public IProfilerEntry getChild(int index) + { + return children.get(index); + } + + @Override + public IProfilerEntry getParent() + { + return parent; + } + + @Override + public IProfiler getOwner() + { + return owner; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/tasks/ArrayRunCall.java b/src/main/java/speiger/src/coreengine/utils/tasks/ArrayRunCall.java new file mode 100644 index 0000000..0a478fa --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/tasks/ArrayRunCall.java @@ -0,0 +1,24 @@ +package speiger.src.coreengine.utils.tasks; + +import java.util.concurrent.Callable; + +public class ArrayRunCall implements Callable +{ + Callable[] calls; + + public ArrayRunCall(Callable... calls) + { + this.calls = calls; + } + + @Override + public Runnable call() throws Exception + { + Runnable[] array = new Runnable[calls.length]; + for(int i = 0,m=array.length;i implements Runnable +{ + Callable call; + Consumer listener; + boolean canceled = false; + + public ListenableTask(Callable call, Consumer listener) + { + this.call = call; + this.listener = listener; + } + + @Override + public void run() + { + if(!canceled) + { + try + { + T value = call.call(); + if(value == null) + { + return; + } + listener.accept(value); + } + catch(Exception e){} + } + } + + public void cancel() + { + canceled = true; + } +} diff --git a/src/main/java/speiger/src/coreengine/utils/tasks/MainThreadTaskProcessor.java b/src/main/java/speiger/src/coreengine/utils/tasks/MainThreadTaskProcessor.java new file mode 100644 index 0000000..61a269a --- /dev/null +++ b/src/main/java/speiger/src/coreengine/utils/tasks/MainThreadTaskProcessor.java @@ -0,0 +1,146 @@ +package speiger.src.coreengine.utils.tasks; + +import speiger.src.collections.objects.queues.ObjectArrayFIFOQueue; +import speiger.src.collections.objects.queues.ObjectPriorityDequeue; +import speiger.src.collections.objects.utils.ObjectPriorityQueues; +import speiger.src.coreengine.utils.counters.timers.CountdownSync; + +public class MainThreadTaskProcessor +{ + ObjectPriorityDequeue tasks = ObjectPriorityQueues.synchronize(new ObjectArrayFIFOQueue()); + Watchdog watch; + Thread watchThread; + boolean running = false; + long timeout; + + public MainThreadTaskProcessor(long timeout, String name) + { + this.timeout = timeout; + watch = new Watchdog(Thread.currentThread()); + watchThread = new Thread(watch, name+"-Thread-Watchdog"); + watchThread.start(); + } + + public void setTimeout(long timeout) + { + this.timeout = timeout; + } + + public void addTask(ITask task) + { + tasks.enqueue(task); + } + + public void finishAllTasks() + { + if(tasks.isEmpty()) + { + return; + } + running = true; + while(!tasks.isEmpty()) + { + try + { + tasks.dequeue().execute(); + } + catch(InterruptedException e) + { + } + catch(Exception e) + { + e.printStackTrace(); + } + } + running = false; + } + + public void update() + { + if(tasks.isEmpty()) + { + return; + } + watch.unlock(); + running = true; + boolean interrupted = false; + while(!tasks.isEmpty() && !(interrupted |= Thread.interrupted())) + { + ITask task = tasks.dequeue(); + try + { + task.execute(); + } + catch(InterruptedException e) + { + interrupted = true; + } + catch(Exception e) + { + e.printStackTrace(); + } + if(!task.isFinished()) + { + tasks.enqueue(task); + } + } + running = false; + if(!interrupted) + { + watchThread.interrupt(); + } + } + + public void kill() + { + if(watchThread != null) + { + return; + } + watch.alive = false; + watchThread.interrupt(); + watchThread = null; + } + + class Watchdog implements Runnable + { + Thread owner; + boolean alive = true; + CountdownSync timer = new CountdownSync(); + Object lock = new Object(); + + public Watchdog(Thread thread) + { + owner = thread; + } + + @Override + public void run() + { + while(alive) + { + try + { + synchronized(lock) + { + lock.wait(); + } + timer.sync(timeout); + if(running) + { + owner.interrupt(); + } + } + catch(InterruptedException e) {} + } + } + + public void unlock() + { + synchronized(lock) + { + lock.notify(); + } + } + } +}