From ff51eb8abce14419264c2f1cfb05f4703382baa7 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Mon, 12 Sep 2022 20:35:52 +0300 Subject: [PATCH] oops --- .gitignore | 11 + LICENSE | 22 ++ build.gradle | 100 ++++++ gradle.properties | 20 ++ gradlew | 185 ++++++++++ gradlew.bat | 89 +++++ readme.md | 27 ++ settings.gradle | 10 + .../topchetoeu/smoothchunks/Descriptor.java | 149 ++++++++ .../me/topchetoeu/smoothchunks/Manager.java | 82 +++++ .../topchetoeu/smoothchunks/SmoothChunks.java | 145 ++++++++ .../smoothchunks/animation/Animation.java | 18 + .../smoothchunks/animation/FallAnimation.java | 29 ++ .../animation/FlyInAnimation.java | 27 ++ .../animation/ProgressManager.java | 146 ++++++++ .../smoothchunks/animation/RiseAnimation.java | 29 ++ .../animation/ScaleAnimation.java | 50 +++ .../topchetoeu/smoothchunks/easing/Ease.java | 34 ++ .../smoothchunks/easing/ElasticEase.java | 29 ++ .../smoothchunks/easing/LinearEase.java | 7 + .../smoothchunks/easing/QuadraticEase.java | 8 + .../smoothchunks/easing/SineEase.java | 8 + .../topchetoeu/smoothchunks/gui/Boundbox.java | 29 ++ .../smoothchunks/gui/BoundboxProvider.java | 8 + .../topchetoeu/smoothchunks/gui/Button.java | 164 +++++++++ .../smoothchunks/gui/ChunkPreview.java | 338 ++++++++++++++++++ .../smoothchunks/gui/HorizontalSection.java | 106 ++++++ .../me/topchetoeu/smoothchunks/gui/Label.java | 77 ++++ .../topchetoeu/smoothchunks/gui/Section.java | 242 +++++++++++++ .../smoothchunks/gui/SelectionScreen.java | 134 +++++++ .../smoothchunks/gui/SmoothChunksScreen.java | 108 ++++++ .../smoothchunks/gui/VerticalSection.java | 106 ++++++ .../smoothchunks/mixin/BuiltChunkMixin.java | 22 ++ .../mixin/WorldRendererMixin.java | 98 +++++ .../resources/assets/smooth-chunks/icon.png | Bin 0 -> 14810 bytes src/main/resources/fabric.mod.json | 29 ++ .../resources/smooth-chunks.accesswidener | 4 + src/main/resources/smooth-chunks.mixins.json | 14 + 38 files changed, 2704 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 build.gradle create mode 100644 gradle.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 readme.md create mode 100644 settings.gradle create mode 100644 src/main/java/me/topchetoeu/smoothchunks/Descriptor.java create mode 100644 src/main/java/me/topchetoeu/smoothchunks/Manager.java create mode 100644 src/main/java/me/topchetoeu/smoothchunks/SmoothChunks.java create mode 100644 src/main/java/me/topchetoeu/smoothchunks/animation/Animation.java create mode 100644 src/main/java/me/topchetoeu/smoothchunks/animation/FallAnimation.java create mode 100644 src/main/java/me/topchetoeu/smoothchunks/animation/FlyInAnimation.java create mode 100644 src/main/java/me/topchetoeu/smoothchunks/animation/ProgressManager.java create mode 100644 src/main/java/me/topchetoeu/smoothchunks/animation/RiseAnimation.java create mode 100644 src/main/java/me/topchetoeu/smoothchunks/animation/ScaleAnimation.java create mode 100644 src/main/java/me/topchetoeu/smoothchunks/easing/Ease.java create mode 100644 src/main/java/me/topchetoeu/smoothchunks/easing/ElasticEase.java create mode 100644 src/main/java/me/topchetoeu/smoothchunks/easing/LinearEase.java create mode 100644 src/main/java/me/topchetoeu/smoothchunks/easing/QuadraticEase.java create mode 100644 src/main/java/me/topchetoeu/smoothchunks/easing/SineEase.java create mode 100644 src/main/java/me/topchetoeu/smoothchunks/gui/Boundbox.java create mode 100644 src/main/java/me/topchetoeu/smoothchunks/gui/BoundboxProvider.java create mode 100644 src/main/java/me/topchetoeu/smoothchunks/gui/Button.java create mode 100644 src/main/java/me/topchetoeu/smoothchunks/gui/ChunkPreview.java create mode 100644 src/main/java/me/topchetoeu/smoothchunks/gui/HorizontalSection.java create mode 100644 src/main/java/me/topchetoeu/smoothchunks/gui/Label.java create mode 100644 src/main/java/me/topchetoeu/smoothchunks/gui/Section.java create mode 100644 src/main/java/me/topchetoeu/smoothchunks/gui/SelectionScreen.java create mode 100644 src/main/java/me/topchetoeu/smoothchunks/gui/SmoothChunksScreen.java create mode 100644 src/main/java/me/topchetoeu/smoothchunks/gui/VerticalSection.java create mode 100644 src/main/java/me/topchetoeu/smoothchunks/mixin/BuiltChunkMixin.java create mode 100644 src/main/java/me/topchetoeu/smoothchunks/mixin/WorldRendererMixin.java create mode 100644 src/main/resources/assets/smooth-chunks/icon.png create mode 100644 src/main/resources/fabric.mod.json create mode 100644 src/main/resources/smooth-chunks.accesswidener create mode 100644 src/main/resources/smooth-chunks.mixins.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eee3e0a --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +* +!src +!src/** +!.gitignore +!build.gradle +!gradle.properties +!gradlew +!gradlew.bat +!LICENSE +!readme.md +!settings.gradle \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8175492 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2022 TopchetoEU +Copyright (c) 2020 Caden Kriese + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..3ee691b --- /dev/null +++ b/build.gradle @@ -0,0 +1,100 @@ +plugins { + id 'fabric-loom' version '0.11-SNAPSHOT' + id 'maven-publish' +} + +sourceCompatibility = JavaVersion.VERSION_17 +targetCompatibility = JavaVersion.VERSION_17 + +archivesBaseName = project.archives_base_name +version = project.mod_version as Object +group = project.maven_group as Object + +repositories { + maven { + url "https://maven.terraformersmc.com/releases" + } +} + +dependencies { + //to change the versions see the gradle.properties file + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + // Fabric API. + //TODO try this approach to fabric api in the future to avoid unneeded modules. + // for some reason it asks for like fabric-biomes and stuff right now. +// Set apiModules = [ +// "fabric-api-base", +// "fabric-registry-sync-v0" +// ] +// + // Add each module as a dependency +// apiModules.forEach { +// modImplementation(fabricApi.module(it, project.fabric_version)) +// } + + // Fabric API. + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" + + // Cloth Config / Autoconfig + // modImplementation("me.shedaniel.cloth:config-2:${project.cloth_version}") { + // exclude(group: "net.fabricmc.fabric-api") + // exclude(module: "modmenu") + // } + // modImplementation("me.sargunvohra.mcmods:autoconfig1u:${project.autoconfig_version}") { + // exclude(group: "net.fabricmc.fabric-api") + // } + // include("me.shedaniel.cloth:config-2:${project.cloth_version}") + // include("me.sargunvohra.mcmods:autoconfig1u:${project.autoconfig_version}") + + modImplementation("com.terraformersmc:modmenu:${project.modmenu_version}") +} + + +processResources { + inputs.property "version", project.version + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} + +tasks.withType(JavaCompile).configureEach { + // Minecraft 1.18 (1.18-pre2) upwards uses Java 17. + it.options.release = 17 +} + +java { + // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task + // if it is present. + // If you remove this line, sources will not be generated. + withSourcesJar() +} + +jar { + from("LICENSE") { + rename { "${it}_${project.archivesBaseName}"} + } +} + +// configure the maven publication +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + } + } + + // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. + repositories { + // Add repositories to publish to here. + // Notice: This block does NOT have the same function as the block in the top level. + // The repositories here will be used for publishing your artifact, not for + // retrieving dependencies. + } +} +loom { + accessWidenerPath = file("src/main/resources/smooth-chunks.accesswidener") +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..4e2f599 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,20 @@ +# Check for updates on https://modmuss50.me/fabric.html +# Done to increase the memory available to gradle. +org.gradle.jvmargs=-Xmx4G + +# Fabric Properties +minecraft_version=1.18.2 +yarn_mappings=1.18.2+build.1 +loader_version=0.13.3 + +# Mod Properties +mod_version=0.1.0 +maven_group=me.topchetoeu +archives_base_name=smooth-chunks + +# Dependencies +fabric_version=0.47.8+1.18.2 + +autoconfig_version=3.3.1 +cloth_version=3.2.24 +modmenu_version=3.2.1 diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..4f906e0 --- /dev/null +++ b/gradlew @@ -0,0 +1,185 @@ +#!/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..ac1b06f --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..912cde2 --- /dev/null +++ b/readme.md @@ -0,0 +1,27 @@ +# Smooth chunks + +This is a fork of [flogic](https://github.com/cadenkriese/smooth-chunks)'s mod (although code bases have nothing in common) + +## What is this? + +This is a mod that adds animations of currently loading chunks. This makes chunk loading generally seem much more pleasant than them appearing out of thin air. There are multiple built-in animations and ease types, and if this isnt't enough, there's an API which will allow you to add your own animations and eases (with ease) + +Generally, this is what you'd call an "eye-candy" mod. + +## Currently supported animations: + +- Rise +- Fall +- Scale + +## Currently supported interpolation types: + +- Linear +- Sine +- Quadratic +- Elastic + +## Future plans: + +In the future, I plan to extend the scope of this mod to not only smoothen the chunk loading, but entity spawning / despawning / dying, liquid flowing, crops growing, etc. + diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..5b60df3 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,10 @@ +pluginManagement { + repositories { + jcenter() + maven { + name = 'Fabric' + url = 'https://maven.fabricmc.net/' + } + gradlePluginPortal() + } +} diff --git a/src/main/java/me/topchetoeu/smoothchunks/Descriptor.java b/src/main/java/me/topchetoeu/smoothchunks/Descriptor.java new file mode 100644 index 0000000..8e147ea --- /dev/null +++ b/src/main/java/me/topchetoeu/smoothchunks/Descriptor.java @@ -0,0 +1,149 @@ +package me.topchetoeu.smoothchunks; + +import org.apache.commons.lang3.Validate; + +public final class Descriptor implements Cloneable { + public static interface StringModifier { + String modify(String original); + } + + private final T val; + private final String name; + private String displayName = null; + private String author = null; + private String description = null; + + /** + * Gets the value this descriptor describes + */ + public T get() { + return val; + } + /** + * Gets the author of the described object (null if not specified) + */ + public String getAuthor() { + return author; + } + /** + * Gets the display name of the described object (null if not specified) + */ + public String getDisplayName() { + return displayName; + } + /** + * Gets the name of the described object (never null) + */ + public String getDescription() { + return description; + } + /** + * Gets the name of the described object (never null) + */ + public String getName() { + return name; + } + + /** + * Gets the author, if not specified, will return a default value + */ + public String getAuthorOrDefault() { + if (author == null) return "Someone"; + return author; + } + /** + * Gets the display name, if not specified, will return a default value + */ + public String getDisplayNameOrDefault() { + if (displayName == null) return name; + return displayName; + } + /** + * Gets the author, if not specified, will return a default value + */ + public String getDescriptionOrDefault() { + if (description == null) return "Something that does stuff (probably)."; + return description; + } + + /** + * Sets the author name + * @param author The new author name (may be null) + * @return The instance upon which this method was called + */ + public Descriptor author(String author) { + this.author = author; + return this; + } + /** + * Sets the author name + * @param modifier The modifier that will be used to gain the new author name value (may not be null) + * @return The instance upon which this method was called + */ + public Descriptor author(StringModifier modifier) { + Validate.notNull(modifier, "modifier may not be null."); + this.author = modifier.modify(this.author); + return this; + } + + /** + * Sets the display name + * @param dn The new display name (may be null) + * @return The instance upon which this method was called + */ + public Descriptor displayName(String dn) { + this.displayName = dn; + return this; + } + /** + * Sets the display name + * @param modifier The modifier that will be used to gain the new display name value (may not be null) + * @return The instance upon which this method was called + */ + public Descriptor displayName(StringModifier modifier) { + Validate.notNull(modifier, "modifier may not be null."); + this.displayName = modifier.modify(this.displayName); + return this; + } + + public Descriptor clone() { + return new Descriptor(val, name) + .author(author) + .description(description) + .displayName(displayName); + } + + /** + * Sets the description + * @param desc The new description (may be null) + * @return The instance upon which this method was called + */ + public Descriptor description(String desc) { + this.description = desc; + return this; + } + /** + * Sets the description + * @param modifier The modifier that will be used to gain the new description value (may not be null) + * @return The instance upon which this method was called + */ + public Descriptor description(StringModifier modifier) { + Validate.notNull(modifier, "modifier may not be null."); + this.description = modifier.modify(this.description); + return this; + } + + @Override + public boolean equals(Object other) { + return other instanceof Descriptor && ((Descriptor)other).name == name; + } + + public Descriptor(T val, String name) { + Validate.notNull(val, "val may not be null."); + Validate.notNull(name, "name may not be null."); + + this.val = val; + this.name = name; + + } +} diff --git a/src/main/java/me/topchetoeu/smoothchunks/Manager.java b/src/main/java/me/topchetoeu/smoothchunks/Manager.java new file mode 100644 index 0000000..d6327ac --- /dev/null +++ b/src/main/java/me/topchetoeu/smoothchunks/Manager.java @@ -0,0 +1,82 @@ +package me.topchetoeu.smoothchunks; + +import java.util.Collection; +import java.util.Collections; +import java.util.Hashtable; +import java.util.Map; + +import org.apache.commons.lang3.Validate; + +import net.fabricmc.fabric.api.event.Event; +import net.fabricmc.fabric.api.event.EventFactory; + +public final class Manager { + public static interface RegisterEvent { + public void register(Manager manager); + } + public static final Event> createEvent() { + return EventFactory.createArrayBacked(RegisterEvent.class, (listeners) -> (manager) -> { + for (RegisterEvent listener: listeners) { + listener.register(manager); + } + }); + } + + private String currName = null; + private Map> objects = new Hashtable<>(); + + /** + * Registers an object (throws if an object of the same name already exists) + * @param obj The object to register (may not be null) + */ + public void register(Descriptor obj) { + Validate.notNull(obj, "obj may not be null."); + + if (objects.containsKey(obj.getName())) throw new RuntimeException("The ease %s already exists.".formatted(obj.getName())); + objects.put(obj.getName(), new Descriptor(obj.get(), obj.getName()) + .displayName(obj.getDisplayName()) + .author(obj.getAuthor()) + .description(obj.getDescription()) + ); + } + + /** + * Gets an object by its name (null if such an object isn't registered) + */ + public Descriptor get(String name) { + return objects.get(name); + } + /** + * Gets the currently used object's descriptor (never null) + */ + public Descriptor get() { + return objects.get(currName); + } + /** + * Gets the currently used object (never null) + */ + public T getValue() { + return objects.get(currName).get(); + } + /** + * Set the currently used object by its name + * @param name The name of the animation to use (throws if the name doesn't correspond to a registered animation) + */ + public void set(String name) { + Validate.notNull(name, "name may not be null."); + if (!objects.containsKey(name)) throw new RuntimeException("The ease %s doesn't exist.".formatted(name)); + this.currName = name; + } + + /** + * Gets all the currently registered objects + */ + public Collection> getAll() { + return Collections.unmodifiableCollection(objects.values()); + } + + public Manager(T _default) { + register(new Descriptor<>(_default, "default").displayName("Default").author("TopchetoEU")); + set("default"); + } +} diff --git a/src/main/java/me/topchetoeu/smoothchunks/SmoothChunks.java b/src/main/java/me/topchetoeu/smoothchunks/SmoothChunks.java new file mode 100644 index 0000000..2da51db --- /dev/null +++ b/src/main/java/me/topchetoeu/smoothchunks/SmoothChunks.java @@ -0,0 +1,145 @@ +package me.topchetoeu.smoothchunks; + +import com.terraformersmc.modmenu.api.ConfigScreenFactory; +import com.terraformersmc.modmenu.api.ModMenuApi; + +import me.topchetoeu.smoothchunks.Manager.RegisterEvent; +import me.topchetoeu.smoothchunks.animation.Animation; +import me.topchetoeu.smoothchunks.animation.FallAnimation; +import me.topchetoeu.smoothchunks.animation.FlyInAnimation; +import me.topchetoeu.smoothchunks.animation.ProgressManager; +import me.topchetoeu.smoothchunks.animation.RiseAnimation; +import me.topchetoeu.smoothchunks.animation.ScaleAnimation; +import me.topchetoeu.smoothchunks.easing.Ease; +import me.topchetoeu.smoothchunks.easing.ElasticEase; +import me.topchetoeu.smoothchunks.easing.LinearEase; +import me.topchetoeu.smoothchunks.easing.SineEase; +import me.topchetoeu.smoothchunks.gui.SmoothChunksScreen; +import me.topchetoeu.smoothchunks.easing.QuadraticEase; +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientChunkEvents; +import net.fabricmc.fabric.api.event.Event; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.util.math.BlockPos; + +public final class SmoothChunks implements ClientModInitializer, ModMenuApi { + private static SmoothChunks instance; + public static SmoothChunks getInstance() { + return instance; + } + + /** + * An event, fired once, when eases are being registered + */ + public final Event> EASES_REGISTERING = Manager.createEvent(); + /** + * An event, fired once, when animations are being registered + */ + public final Event> ANIMATIONS_REGISTERING = Manager.createEvent(); + + private ProgressManager progress; + private Manager ease; + private Manager animation; + + /** + * Gets the chunk progress manager + */ + public ProgressManager getProgressManager() { + return progress; + } + /** + * Gets the animation manager + */ + public Manager getAnimationManager() { + return animation; + } + /** + * Gets the ease manager + */ + public Manager getEaseManager() { + return ease; + } + + private static void registerEases(Manager manager) { + manager.register(new Descriptor<>(new LinearEase(), "linear") + .displayName("Linear") + .author("TopchetoEU") + .description("Animates with an even velocity") + ); + manager.register(new Descriptor<>(new SineEase(), "sine") + .displayName("Sine") + .author("TopchetoEU") + .description("Animation takes off relatively quickly, and ends smoothly. No abrupt ending is noticeable.") + ); + manager.register(new Descriptor<>(new QuadraticEase(), "quad") + .displayName("Quadratic") + .author("TopchetoEU") + .description("Animation takes off quickly, and then slows down. There is a noticeable stop in the animation.") + ); + manager.register(new Descriptor<>(new ElasticEase(), "elastic") + .displayName("Elastic") + .author("TopchetoEU") + .description("Animation takes off very quickly, overshoots, then undershoots, until it reaches the end.") + ); + + manager.set("elastic"); + } + private static void registerAnimations(Manager manager) { + manager.register(new Descriptor<>(new RiseAnimation(), "rise") + .displayName("Rise") + .author("TopchetoEU") + .description("Chunks will rise out of the ground (go from below upwards).") + ); + manager.register(new Descriptor<>(new FallAnimation(), "fall") + .displayName("Fall") + .author("TopchetoEU") + .description("Chunks will fall from the sky (go from above downwards).") + ); + manager.register(new Descriptor<>(new ScaleAnimation(), "scale") + .displayName("Scale") + .author("TopchetoEU") + .description("Chunks will scale up from 0 to normal size.") + ); + manager.register(new Descriptor<>(new FlyInAnimation(), "fly-in") + .displayName("Fly In") + .author("TopchetoEU") + .description("Chunks will slide torwards you until they get to their locations.") + ); + + manager.set("rise"); + } + + @Override public void onInitializeClient() { + instance = this; + + progress = new ProgressManager(); + ease = new Manager<>(x -> 1); + ease.get() + .description("Ends the animation as soon as it has started.") + .displayName("No animation"); + animation = new Manager<>((a, b, c, d, e, f, g, h) -> {}); + animation.get() + .description("Does nothing.") + .displayName("No animation"); + + registerEases(ease); + registerAnimations(animation); + + EASES_REGISTERING.invoker().register(ease); + ANIMATIONS_REGISTERING.invoker().register(animation); + + ClientChunkEvents.CHUNK_UNLOAD.register((world, chunk) -> { + BlockPos pos = chunk.getPos().getStartPos(); + progress.unload(pos.getX(), pos.getY(), pos.getZ()); + }); + } + @Override + public ConfigScreenFactory getModConfigScreenFactory() { + return new ConfigScreenFactory() { + @Override + public Screen create(Screen parent) { + return new SmoothChunksScreen(parent); + } + }; + } +} diff --git a/src/main/java/me/topchetoeu/smoothchunks/animation/Animation.java b/src/main/java/me/topchetoeu/smoothchunks/animation/Animation.java new file mode 100644 index 0000000..f872fca --- /dev/null +++ b/src/main/java/me/topchetoeu/smoothchunks/animation/Animation.java @@ -0,0 +1,18 @@ +package me.topchetoeu.smoothchunks.animation; + +import net.minecraft.client.util.math.MatrixStack; + +public interface Animation { + /** + * Animations using the currently set ease + * @param progress The point at which the animation currently is (a value between 0 and 1) + * @param matrices The matrix stack used for rendering + * @param chunkX The current chunk's x (in blocks) which is animated + * @param chunkY The current chunk's y (in blocks) which is animated + * @param chunkZ The current chunk's z (in blocks) which is animated + * @param playerX The player's x (in blocks) + * @param playerY The player's y (in blocks) + * @param playerZ The player's z (in blocks) + */ + void animate(float progress, MatrixStack matrices, int chunkX, int chunkY, int chunkZ, float playerX, float playerY, float playerZ); +} diff --git a/src/main/java/me/topchetoeu/smoothchunks/animation/FallAnimation.java b/src/main/java/me/topchetoeu/smoothchunks/animation/FallAnimation.java new file mode 100644 index 0000000..0214755 --- /dev/null +++ b/src/main/java/me/topchetoeu/smoothchunks/animation/FallAnimation.java @@ -0,0 +1,29 @@ +package me.topchetoeu.smoothchunks.animation; + +import net.minecraft.client.util.math.MatrixStack; + +public class FallAnimation implements Animation { + private float offset; + + public float getOffset() { + return offset; + } + public void setOffset(float offset) { + this.offset = offset; + } + + @Override + public void animate(float progress, MatrixStack matrices, int chunkX, int chunkY, int chunkZ, float playerX, float playerY, float playerZ) { + animate(progress, matrices); + } + public void animate(float progress, MatrixStack matrices) { + matrices.translate(0, offset * (1 - progress), 0); + } + + public FallAnimation(float offset) { + this.offset = offset; + } + public FallAnimation() { + offset = 50; + } +} diff --git a/src/main/java/me/topchetoeu/smoothchunks/animation/FlyInAnimation.java b/src/main/java/me/topchetoeu/smoothchunks/animation/FlyInAnimation.java new file mode 100644 index 0000000..4ffa5ff --- /dev/null +++ b/src/main/java/me/topchetoeu/smoothchunks/animation/FlyInAnimation.java @@ -0,0 +1,27 @@ +package me.topchetoeu.smoothchunks.animation; + +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.math.Vec2f; + +public class FlyInAnimation implements Animation { + private float offset; + + public float getOffset() { + return offset; + } + public void setOffset(float offset) { + this.offset = offset; + } + + + @Override + public void animate(float progress, MatrixStack matrices, int chunkX, int chunkY, int chunkZ, float playerX, float playerY, float playerZ) { + Vec2f direction = new Vec2f(playerX, playerZ).add(new Vec2f(-chunkX, -chunkZ)).normalize().multiply(-offset); + + matrices.translate(direction.x * (1 - progress), 0, direction.y * (1 - progress)); + } + + public FlyInAnimation() { + offset = 50; + } +} diff --git a/src/main/java/me/topchetoeu/smoothchunks/animation/ProgressManager.java b/src/main/java/me/topchetoeu/smoothchunks/animation/ProgressManager.java new file mode 100644 index 0000000..fd1bc2d --- /dev/null +++ b/src/main/java/me/topchetoeu/smoothchunks/animation/ProgressManager.java @@ -0,0 +1,146 @@ +package me.topchetoeu.smoothchunks.animation; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Hashtable; + +public final class ProgressManager { + public static class ChunkLoc { + public final int x; + public final int y; + public final int z; + + @Override + public boolean equals(Object obj) { + return obj instanceof ChunkLoc && ((ChunkLoc)obj).x == x && ((ChunkLoc)obj).y == y && ((ChunkLoc)obj).z == z; + } + + @Override + public int hashCode() { + return 137 * x + 149 * y + 163 * z; + } + + public ChunkLoc(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } + } + + private Hashtable chunkToStage = new Hashtable<>(); + private HashSet chunks = new HashSet<>(); + private float duration = 1; + + public ProgressManager() { + + } + + public float getDuration() { + return duration; + } + public void setDuration(float duration) { + this.duration = duration; + } + + /** + * Removes all loaded chunks (called when a world is unloaded and loaded) + */ + public void reset() { + chunkToStage.clear(); + chunks.clear(); + } + /** + * Advances the animation for all tracked chunks, according to the duration and the specified delta + * @param delta The second delta to advance the animation by + */ + public void tick(float delta) { + for (ChunkLoc loc : new ArrayList<>(chunkToStage.keySet())) { + float val = chunkToStage.get(loc); + val += delta / duration; + if (val > 1f) chunkToStage.remove(loc); + chunkToStage.put(loc, val); + } + } + + /** + * Loads a specified chunk (starts tracking its animation) + * @param x The x of the chunk + * @param y The y of the chunk + * @param y The z of the chunk + */ + public void load(int x, int y, int z) { + if (isChunkLoaded(x, y, z)) return; + ChunkLoc loc = new ChunkLoc(x, y, z); + chunks.add(loc); + chunkToStage.put(loc, 0f); + } + /** + * Unloads a specified chunk (stops tracking its animation) + * @param x The x of the chunk + * @param y The y of the chunk + * @param y The z of the chunk + */ + public void unload(int x, int y, int z) { + ChunkLoc loc = new ChunkLoc(x, y, z); + chunkToStage.remove(loc); + chunks.remove(loc); + } + /** + * Unloads all loaded chunks + */ + public void unloadAll() { + chunkToStage.clear(); + chunks.clear(); + } + /** + * Unloads all the chunks that are outside the render distance specified + * @param viewDistance The view distance (in chunks, radius) + * @param playerChunkX The x coordinate of the chunk in which the player stands + * @param playerChunkY The y coordinate of the chunk in which the player stands + * @param playerChunkZ The z coordinate of the chunk in which the player stands + */ + public void unloadAllFar(int viewDistance, int playerChunkX, int playerChunkY, int playerChunkZ) { + float circleVD = viewDistance + 1.38f; + int squareVD = viewDistance; + + for (ChunkLoc loc : new ArrayList<>(chunks)) { + int chunkX = loc.x / 16; + int chunkZ = loc.z / 16; + + int diffSquareX = playerChunkX - chunkX ; + int diffSquareZ = playerChunkZ - chunkZ; + + int diffCircleX = playerChunkX - chunkX; + int diffCircleZ = playerChunkZ - chunkZ; + + int dist = diffCircleX * diffCircleX + diffCircleZ * diffCircleZ; + + if (dist > circleVD * circleVD) unload(loc.x, loc.y, loc.z); + if (Math.abs(diffSquareX) > squareVD || Math.abs(diffSquareZ) > squareVD) unload(loc.x, loc.y, loc.z); + } + } + + /** + * Checks whether or not a chunk is loaded + * @param x The x of the chunk + * @param y The y of the chunk + * @param y The z of the chunk + * @return A value indicating whether or not the specified chunk is tracked + */ + public boolean isChunkLoaded(int x, int y, int z) { + return chunks.contains(new ChunkLoc(x, y, z)); + } + + /** + * Gets the animation progress of the specified chunk + * @param x The x of the chunk + * @param y The y of the chunk + * @param y The z of the chunk + * @return A float value from 0 to 1 (0 if the chunk is not tracked), indicating the animation progress of the specified chunk + */ + public float getChunkProgress(int x, int y, int z) { + if (!isChunkLoaded(x, y, z)) return 0f; + if (!chunkToStage.containsKey(new ChunkLoc(x, y, z))) return 1f; + return chunkToStage.get(new ChunkLoc(x, y, z)); + } +} diff --git a/src/main/java/me/topchetoeu/smoothchunks/animation/RiseAnimation.java b/src/main/java/me/topchetoeu/smoothchunks/animation/RiseAnimation.java new file mode 100644 index 0000000..15185c9 --- /dev/null +++ b/src/main/java/me/topchetoeu/smoothchunks/animation/RiseAnimation.java @@ -0,0 +1,29 @@ +package me.topchetoeu.smoothchunks.animation; + +import net.minecraft.client.util.math.MatrixStack; + +public final class RiseAnimation implements Animation { + private float offset; + + public float getOffset() { + return offset; + } + public void setOffset(float offset) { + this.offset = offset; + } + + @Override + public void animate(float progress, MatrixStack matrices, int chunkX, int chunkY, int chunkZ, float playerX, float playerY, float playerZ) { + animate(progress, matrices); + } + public void animate(float progress, MatrixStack matrices) { + matrices.translate(0, offset * (progress - 1), 0); + } + + public RiseAnimation(float offset) { + this.offset = offset; + } + public RiseAnimation() { + offset = 50; + } +} diff --git a/src/main/java/me/topchetoeu/smoothchunks/animation/ScaleAnimation.java b/src/main/java/me/topchetoeu/smoothchunks/animation/ScaleAnimation.java new file mode 100644 index 0000000..a5bd353 --- /dev/null +++ b/src/main/java/me/topchetoeu/smoothchunks/animation/ScaleAnimation.java @@ -0,0 +1,50 @@ +package me.topchetoeu.smoothchunks.animation; + +import net.minecraft.client.util.math.MatrixStack; + +public class ScaleAnimation implements Animation { + private boolean scaleY = true; + private float xOffset = 8, yOffset = 8, zOffset = 8; + + public boolean isScalingY() { + return scaleY; + } + public void setScalingY(boolean scaleY) { + this.scaleY = scaleY; + } + + public float getXOffset() { + return xOffset; + } + public void setXOffset(float xOffset) { + this.xOffset = xOffset; + } + + public float getYOffset() { + return yOffset; + } + public void setYOffset(float yOffset) { + this.yOffset = yOffset; + } + + public float getZOffset() { + return zOffset; + } + public void setZOffset(float zOffset) { + this.zOffset = zOffset; + } + + @Override + public void animate(float progress, MatrixStack matrices, int chunkX, int chunkY, int chunkZ, float playerX, float playerY, float playerZ) { + float scaleX = progress; + float scaleZ = progress; + + matrices.translate(xOffset, yOffset, zOffset); + matrices.scale(scaleX, 1, scaleZ); + matrices.translate(-xOffset, -yOffset, -zOffset); + } + + public ScaleAnimation() { + + } +} diff --git a/src/main/java/me/topchetoeu/smoothchunks/easing/Ease.java b/src/main/java/me/topchetoeu/smoothchunks/easing/Ease.java new file mode 100644 index 0000000..4948b59 --- /dev/null +++ b/src/main/java/me/topchetoeu/smoothchunks/easing/Ease.java @@ -0,0 +1,34 @@ +package me.topchetoeu.smoothchunks.easing; + +public interface Ease { + /** + * Converts the linear progress of an animation to an eased progress + * @param x The progress of the animation being eased + * @return The new, eased progress + */ + float ease(float x); + + /** + * Converts a function to an ease out version of itself. + * Mathematically, the function is being "rotated" 180 degrees + * @param func The function to convert + * @return The ease out version of the function + */ + public static Ease easeOut(Ease func) { + return x -> 1 - func.ease(1 - x); + } + /** + * Converts a function to an ease in-out version of itself. + * Mathematically, the function is being split into two, where in the interval [0; 0.5], the ease-out function is being used, and in the other interval [0.5; 1], the ease-in function is being used + * @param func The function to convert + * @return The ease in-out version of the function + */ + public static Ease easeInOut(Ease func) { + return x -> { + float x2 = 2 * x; + + if (x < 0.5f) return (1 - func.ease(1 - x2)) / 2; + else return (1 + func.ease(x2 - 1)) / 2; + }; + } +} diff --git a/src/main/java/me/topchetoeu/smoothchunks/easing/ElasticEase.java b/src/main/java/me/topchetoeu/smoothchunks/easing/ElasticEase.java new file mode 100644 index 0000000..7c0ab25 --- /dev/null +++ b/src/main/java/me/topchetoeu/smoothchunks/easing/ElasticEase.java @@ -0,0 +1,29 @@ +package me.topchetoeu.smoothchunks.easing; + +public class ElasticEase implements Ease { + private float steepness = 1; + private int periods = 3; + + public int getPeriods() { + return periods; + } + public void setPeriods(int periods) { + this.periods = periods; + } + + public float getSteepness() { + return steepness; + } + public void setSteepness(float steepness) { + this.steepness = steepness; + } + + @Override + public float ease(float x) { + float amplitude = (float)Math.pow(2, -steepness * x) * (1 - x); + float wave = (float)Math.sin(2 * Math.PI * periods * x - Math.PI / 2); + + return amplitude * wave + 1; + } + +} diff --git a/src/main/java/me/topchetoeu/smoothchunks/easing/LinearEase.java b/src/main/java/me/topchetoeu/smoothchunks/easing/LinearEase.java new file mode 100644 index 0000000..293f4c7 --- /dev/null +++ b/src/main/java/me/topchetoeu/smoothchunks/easing/LinearEase.java @@ -0,0 +1,7 @@ +package me.topchetoeu.smoothchunks.easing; + +public class LinearEase implements Ease { + public float ease(float x) { + return x; + } +} diff --git a/src/main/java/me/topchetoeu/smoothchunks/easing/QuadraticEase.java b/src/main/java/me/topchetoeu/smoothchunks/easing/QuadraticEase.java new file mode 100644 index 0000000..2fe5839 --- /dev/null +++ b/src/main/java/me/topchetoeu/smoothchunks/easing/QuadraticEase.java @@ -0,0 +1,8 @@ +package me.topchetoeu.smoothchunks.easing; + +public class QuadraticEase implements Ease { + @Override + public float ease(float x) { + return x * x; + } +} diff --git a/src/main/java/me/topchetoeu/smoothchunks/easing/SineEase.java b/src/main/java/me/topchetoeu/smoothchunks/easing/SineEase.java new file mode 100644 index 0000000..f37acbd --- /dev/null +++ b/src/main/java/me/topchetoeu/smoothchunks/easing/SineEase.java @@ -0,0 +1,8 @@ +package me.topchetoeu.smoothchunks.easing; + +public class SineEase implements Ease { + @Override + public float ease(float x) { + return (float)Math.sin(x * Math.PI / 2); + } +} diff --git a/src/main/java/me/topchetoeu/smoothchunks/gui/Boundbox.java b/src/main/java/me/topchetoeu/smoothchunks/gui/Boundbox.java new file mode 100644 index 0000000..0b8f393 --- /dev/null +++ b/src/main/java/me/topchetoeu/smoothchunks/gui/Boundbox.java @@ -0,0 +1,29 @@ +package me.topchetoeu.smoothchunks.gui; + +public class Boundbox implements BoundboxProvider { + public float x, y, width, height; + + @Override + public float getX() { + return x; + } + @Override + public float getY() { + return y; + } + @Override + public float getWidth() { + return width; + } + @Override + public float getHeight() { + return height; + } + + public Boundbox(float x, float y, float width, float height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } +} diff --git a/src/main/java/me/topchetoeu/smoothchunks/gui/BoundboxProvider.java b/src/main/java/me/topchetoeu/smoothchunks/gui/BoundboxProvider.java new file mode 100644 index 0000000..a824afe --- /dev/null +++ b/src/main/java/me/topchetoeu/smoothchunks/gui/BoundboxProvider.java @@ -0,0 +1,8 @@ +package me.topchetoeu.smoothchunks.gui; + +public interface BoundboxProvider { + float getX(); + float getY(); + float getWidth(); + float getHeight(); +} diff --git a/src/main/java/me/topchetoeu/smoothchunks/gui/Button.java b/src/main/java/me/topchetoeu/smoothchunks/gui/Button.java new file mode 100644 index 0000000..ff857a2 --- /dev/null +++ b/src/main/java/me/topchetoeu/smoothchunks/gui/Button.java @@ -0,0 +1,164 @@ +package me.topchetoeu.smoothchunks.gui; + +import org.apache.commons.lang3.Validate; +import org.lwjgl.glfw.GLFW; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.Drawable; +import net.minecraft.client.gui.DrawableHelper; +import net.minecraft.client.gui.Element; +import net.minecraft.client.gui.Selectable; +import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; +import net.minecraft.client.gui.screen.narration.NarrationPart; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.text.Text; +import net.minecraft.util.math.ColorHelper.Argb; + +public class Button extends DrawableHelper implements Drawable, Element, Selectable, BoundboxProvider { + public static interface ClickAction { + void onClick(); + } + + public ClickAction clickAction; + public final MinecraftClient client; + private boolean clicked = false; + private boolean focused = false; + private boolean hovered = false; + + + private Text text = Text.of(""); + public int paddingX = 5, paddingY = 2; + public int x, y; + + public float getX() { + return x; + } + public float getY() { + return y; + } + + public float getWidth() { + return 2 + paddingX * 2 + client.textRenderer.getWidth(text); + } + public float getHeight() { + return 1 + paddingY * 2 + client.textRenderer.fontHeight; + } + + public Text getText() { + return text; + } + public void setText(Text text) { + Validate.notNull(text, "text may not be null."); + this.text = text; + } + public void setText(String text) { + Validate.notNull(text, "text may not be null."); + this.text = Text.of(text); + } + + public void click() { + if (clickAction != null) clickAction.onClick(); + } + + @Override + public void appendNarrations(NarrationMessageBuilder msgBuilder) { + msgBuilder.put(NarrationPart.HINT, "Button " + text); + } + + @Override + public SelectionType getType() { + if (focused) return SelectionType.FOCUSED; + if (hovered) return SelectionType.HOVERED; + return SelectionType.NONE; + } + @Override + public boolean changeFocus(boolean lookForwards) { + focused = !focused; + return focused; + } + + @Override + public void render(MatrixStack matrices, int x, int y, float delta) { + int white = Argb.getArgb(255, 255, 255, 255); + matrices.push(); + matrices.translate(this.x, this.y, getZOffset()); + + hovered = isMouseOver(x, y); + + if (hovered) { + fill(matrices, 0, 0, (int)getWidth(), (int)getHeight(), Argb.getArgb(32, 255, 255, 255)); + } + if (clicked) { + fill(matrices, 0, 0, (int)getWidth(), (int)getHeight(), Argb.getArgb(127, 255, 255, 255)); + } + + drawHorizontalLine(matrices, 0, (int)getWidth() - 1, (int)getHeight() - 1, white); + + if (focused) { + drawHorizontalLine(matrices, 0, (int)getWidth() - 1, 0, white); + drawVerticalLine(matrices, 0, 0, (int)getHeight() - 1, white); + drawVerticalLine(matrices, (int)getWidth() - 1, 0, (int)getHeight() - 1, white); + } + + client.textRenderer.draw(matrices, text, paddingX + 1, paddingY + 1, white); + + matrices.pop(); + } + + @Override + public boolean isMouseOver(double x, double y) { + if (clicked) return true; + + x -= this.x; + y -= this.y; + + return x >= 0 && x < getWidth() && y >= 0 && y < getHeight(); + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + if (keyCode == GLFW.GLFW_KEY_ENTER) { + click(); + clicked = true; + hovered = true; + return true; + } + return false; + } + @Override + public boolean keyReleased(int keyCode, int scanCode, int modifiers) { + if (keyCode == GLFW.GLFW_KEY_ENTER) { + clicked = false; + return true; + } + return false; + } + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (button != 0) return false; + if (isMouseOver(mouseX, mouseY)) { + clicked = true; + hovered = true; + } + return false; + // return true; + } + @Override + public boolean mouseReleased(double mouseX, double mouseY, int button) { + if (button != 0) return false; + if (clicked) { + click(); + clicked = false; + return true; + } + return false; + } + + public Button(int x, int y, Text text, ClickAction clickAction) { + this.clickAction = clickAction; + this.client = MinecraftClient.getInstance(); + setText(text); + this.x = x; + this.y = y; + } +} diff --git a/src/main/java/me/topchetoeu/smoothchunks/gui/ChunkPreview.java b/src/main/java/me/topchetoeu/smoothchunks/gui/ChunkPreview.java new file mode 100644 index 0000000..883e96b --- /dev/null +++ b/src/main/java/me/topchetoeu/smoothchunks/gui/ChunkPreview.java @@ -0,0 +1,338 @@ +package me.topchetoeu.smoothchunks.gui; + +import org.lwjgl.glfw.GLFW; +import org.lwjgl.opengl.GL11; + +import com.mojang.blaze3d.systems.RenderSystem; + +import me.topchetoeu.smoothchunks.SmoothChunks; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.Drawable; +import net.minecraft.client.gui.DrawableHelper; +import net.minecraft.client.gui.Element; +import net.minecraft.client.gui.Selectable; +import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; +import net.minecraft.client.gui.screen.narration.NarrationPart; +import net.minecraft.client.render.BufferBuilder; +import net.minecraft.client.render.BufferRenderer; +import net.minecraft.client.render.GameRenderer; +import net.minecraft.client.render.Tessellator; +import net.minecraft.client.render.VertexFormat; +import net.minecraft.client.render.VertexFormats; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.math.Matrix4f; +import net.minecraft.util.math.Quaternion; +import net.minecraft.util.math.Vec3f; +import net.minecraft.util.math.Vector4f; +import net.minecraft.util.math.ColorHelper.Argb; + +public class ChunkPreview extends DrawableHelper implements Drawable, Element, Selectable, BoundboxProvider { + public static interface ClickAction { + void onClick(); + } + + public ClickAction clickAction; + public final MinecraftClient client; + private boolean clicked = false; + private boolean rotating = false; + private boolean focused = false; + private boolean hovered = false; + private float globalProgress = 0; + private float duration = 0; + private float mouseStartX, mouseStartY; + private float rotX, rotY, scale = 1; + + public float x, y; + public float padding = 5; + public float width = 100, height = 100; + + public float getX() { + return x; + } + public float getY() { + return y; + } + + public float getWidth() { + return width; + } + public float getHeight() { + return height; + } + + public void click() { + refresh(); + } + + @Override + public void appendNarrations(NarrationMessageBuilder msgBuilder) { + msgBuilder.put(NarrationPart.HINT, "Chunk preview"); + } + + @Override + public SelectionType getType() { + if (focused) return SelectionType.FOCUSED; + if (hovered || clicked || rotating) return SelectionType.HOVERED; + return SelectionType.NONE; + } + @Override + public boolean changeFocus(boolean lookForwards) { + focused = !focused; + return focused; + } + + private void refresh() { + globalProgress = 0; + } + + private static boolean checkZ(float z) { + return z < 3; + } + + private static void myFill(MatrixStack matrices, float x1, float y1, float x2, float y2, float a, float r, float g, float b) { + if (x1 < x2) { + float tmp = x1; + x1 = x2; + x2 = tmp; + } + if (y1 < y2) { + float tmp = y1; + y1 = y2; + y2 = tmp; + } + + Vector4f p1 = new Vector4f(x1, y1, 0, 1); + Vector4f p2 = new Vector4f(x1, y2, 0, 1); + Vector4f p3 = new Vector4f(x2, y2, 0, 1); + Vector4f p4 = new Vector4f(x2, y1, 0, 1); + + p1.transform(matrices.peek().getPositionMatrix()); + p2.transform(matrices.peek().getPositionMatrix()); + p3.transform(matrices.peek().getPositionMatrix()); + p4.transform(matrices.peek().getPositionMatrix()); + + p1.multiply(1 / p1.getW()); + p2.multiply(1 / p2.getW()); + p3.multiply(1 / p3.getW()); + p4.multiply(1 / p4.getW()); + + // System.out.println(p1.getZ()); + + if (checkZ(p1.getZ()) && checkZ(p2.getZ()) && checkZ(p3.getZ()) && checkZ(p4.getZ())) { + BufferBuilder bufferBuilder = Tessellator.getInstance().getBuffer(); + RenderSystem.enableBlend(); + RenderSystem.disableCull(); + RenderSystem.disableTexture(); + RenderSystem.defaultBlendFunc(); + RenderSystem.setShader(GameRenderer::getPositionColorShader); + bufferBuilder.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_COLOR); + bufferBuilder.vertex(p1.getX(), p1.getY(), p1.getZ()).color(r, g, b, a).next(); + bufferBuilder.vertex(p2.getX(), p2.getY(), p2.getZ()).color(r, g, b, a).next(); + bufferBuilder.vertex(p3.getX(), p3.getY(), p3.getZ()).color(r, g, b, a).next(); + bufferBuilder.vertex(p4.getX(), p4.getY(), p4.getZ()).color(r, g, b, a).next(); + bufferBuilder.end(); + BufferRenderer.draw(bufferBuilder); + RenderSystem.enableTexture(); + RenderSystem.disableBlend(); + } + + } + + private final Quaternion rotation = Quaternion.fromEulerXyzDegrees(new Vec3f(-90, 0, 0)); + + private void setupDepth(MatrixStack matrices) { + RenderSystem.enableDepthTest(); + RenderSystem.disableBlend(); + RenderSystem.clearDepth(-1); + RenderSystem.clear(GL11.GL_DEPTH_BUFFER_BIT, true); + RenderSystem.depthFunc(GL11.GL_ALWAYS); + RenderSystem.depthMask(true); + matrices.push(); + matrices.scale(.5f, .5f, 1); + matrices.translate(0, 0, .1f); + if (hovered) { + myFill(matrices, -1, -1, 1, 1, 0.1f, 1, 1, 1); + } + else { + myFill(matrices, -1, -1, 1, 1, 0.004f, 0, 0, 0); + } + matrices.pop(); + RenderSystem.depthMask(false); + RenderSystem.depthFunc(GL11.GL_LESS); + RenderSystem.enableBlend(); + } + private void renderChunk(MatrixStack matrices, int x, int y, int n, float delta) { + matrices.push(); + matrices.translate(x * 16 - 8, 0, y * 16 - 8); + + // x += n; + // y += n; + + float progress = globalProgress / duration - (float)Math.sqrt(x * x + y * y) / (float)n / 2; + if (progress < 0) progress = 0; + if (progress > 1) progress = 1; + if (progress < 0.999) { + float _progress = SmoothChunks.getInstance().getEaseManager().getValue().ease(progress); + SmoothChunks.getInstance().getAnimationManager().getValue().animate( + _progress, matrices, + x * 16, 0, y * 16, 0, 0, 0 + ); + // matrices.translate(0, 0, 16); + } + matrices.translate(0, 0, 16); + matrices.multiply(rotation); + myFill(matrices, 2, 1, 14, 2, progress, 1, 1, 1); + myFill(matrices, 2, 14, 14, 15, progress, 1, 1, 1); + myFill(matrices, 1, 1, 2, 15, progress, 1, 1, 1); + myFill(matrices, 14, 1, 15, 15, progress, 1, 1, 1); + + matrices.pop(); + } + private void renderChunks(MatrixStack matrices, float delta, int n) { + duration = SmoothChunks.getInstance().getProgressManager().getDuration(); + // globalProgress += (lastTime - (lastTime = System.nanoTime())) / -1000000000f; + globalProgress += delta * 0.05f; + matrices.push(); + + matrices.scale(width, width, 1); + matrices.translate(.5f, .5f, 0); + setupDepth(matrices); + + matrices.multiplyPositionMatrix(Matrix4f.viewboxMatrix(75, 1, .00001f, 100)); + matrices.translate(0, 0, -2); + matrices.scale(0.0625f, 0.0625f, 0.0625f); + matrices.scale(1f / (n * 2 + 1), 1f / (n * 2 + 1), 1f / (n * 2 + 1)); + // matrices.multiply(Quaternion.fromEulerXyzDegrees(new Vec3f(150, -45, 0))); + matrices.scale(scale, scale, scale); + + matrices.multiply(Quaternion.fromEulerXyzDegrees(new Vec3f(rotY + 150, 0, 0))); + matrices.multiply(Quaternion.fromEulerXyzDegrees(new Vec3f(0, rotX - 45, 0))); + + // matrices.translate(-8, 0, 8); + + for (int x = -n; x <= n; x++) { + for (int y = -n; y <= n; y++) { + renderChunk(matrices, x, y, n, delta); + } + } + + RenderSystem.depthMask(true); + RenderSystem.disableDepthTest(); + RenderSystem.clearDepth(1); + RenderSystem.clear(GL11.GL_DEPTH_BUFFER_BIT, true); + matrices.pop(); + } + + @Override + public void render(MatrixStack matrices, int x, int y, float delta) { + int white = Argb.getArgb(255, 255, 255, 255); + matrices.push(); + matrices.translate(this.x, this.y, getZOffset()); + + hovered = isMouseOver(x, y); + + renderChunks(matrices, delta, 5); + + drawHorizontalLine(matrices, 0, (int)getWidth() - 1, (int)getHeight() - 1, white); + + if (focused) { + drawHorizontalLine(matrices, 0, (int)getWidth() - 1, 0, white); + drawVerticalLine(matrices, 0, 0, (int)getHeight() - 1, white); + drawVerticalLine(matrices, (int)getWidth() - 1, 0, (int)getHeight() - 1, white); + } + + + matrices.pop(); + } + + @Override + public boolean isMouseOver(double x, double y) { + if (clicked) return true; + + x -= this.x; + y -= this.y; + + return x >= 0 && x < getWidth() && y >= 0 && y < getHeight(); + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + if (keyCode == GLFW.GLFW_KEY_ENTER) { + click(); + clicked = true; + hovered = true; + return true; + } + return false; + } + @Override + public boolean keyReleased(int keyCode, int scanCode, int modifiers) { + if (keyCode == GLFW.GLFW_KEY_ENTER) { + clicked = false; + return true; + } + return false; + } + + @Override + public boolean mouseScrolled(double mouseX, double mouseY, double amount) { + if (isMouseOver(mouseX, mouseY)) { + scale /= Math.pow(2, -amount / 3); + return true; + } + return false; + } + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (isMouseOver(mouseX, mouseY)) { + if (button == 0) { + click(); + clicked = true; + hovered = true; + return true; + } + else if (button == 2) { + rotating = true; + hovered = true; + mouseStartX = (float)mouseX; + mouseStartY = (float)mouseY; + return true; + } + } + return false; + } + @Override + public void mouseMoved(double mouseX, double mouseY) { + if (rotating) { + rotX += mouseStartX - (float)mouseX; + rotY += mouseStartY - (float)mouseY; + mouseStartX = (float)mouseX; + mouseStartY = (float)mouseY; + } + } + @Override + public boolean mouseReleased(double mouseX, double mouseY, int button) { + if (button == 0 && clicked) { + clicked = false; + return true; + } + if (button == 2 && rotating) { + rotating = false; + return true; + } + return false; + } + + public ChunkPreview(int x, int y) { + this.client = MinecraftClient.getInstance(); + this.x = x; + this.y = y; + } + public ChunkPreview(int x, int y, int w, int h) { + this.client = MinecraftClient.getInstance(); + this.x = x; + this.y = y; + this.width = w; + this.height = h; + } +} diff --git a/src/main/java/me/topchetoeu/smoothchunks/gui/HorizontalSection.java b/src/main/java/me/topchetoeu/smoothchunks/gui/HorizontalSection.java new file mode 100644 index 0000000..99d37e5 --- /dev/null +++ b/src/main/java/me/topchetoeu/smoothchunks/gui/HorizontalSection.java @@ -0,0 +1,106 @@ +package me.topchetoeu.smoothchunks.gui; + +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; + +import net.minecraft.client.gui.Element; + +public final class HorizontalSection extends Section { + private class Line { + public Map widths = new Hashtable<>(); + public List elements = new ArrayList<>(); + public float width = 0, height = 0; + } + + private float targetWidth; + + public float getTargetWidth(float width) { + return this.targetWidth; + } + public void setTargetWidth(float width) { + this.targetWidth = width; + recalculate(); + } + + private List getLines() { + Line currLine = new Line(); + List lines = new ArrayList<>(); + + for (Element el : children.get()) { + var box = children.getBoundbox(el); + var x = box.getX() + box.getWidth(); + var y = box.getY() + box.getHeight(); + + if (currLine.width + x > targetWidth) { + lines.add(currLine); + currLine = new Line(); + } + + currLine.width += x; + currLine.elements.add(el); + currLine.widths.put(el, x); + + if (currLine.height < y) currLine.height = y; + } + lines.add(currLine); + + return lines; + } + + private void recalculateLeft(Line line, float offsetY) { + float currX = 0; + for (Element el : line.elements) { + children.offsets.put(el, new Offset(currX, offsetY)); + currX += line.widths.get(el); + } + } + private void recalculateCenter(Line line, float offsetY) { + recalculateLeft(line, offsetY); + + for (Element el : line.elements) { + children.offsets.get(el).x += (targetWidth - line.width) / 2; + } + } + private void recalculateRight(Line line, float offsetY) { + recalculateLeft(line, offsetY); + + for (Element el : line.elements) { + children.offsets.get(el).x += (targetWidth - line.width); + } + } + private void recalculateJustified(Line line, float offsetY) { + recalculateLeft(line, offsetY); + int i = 0; + + if (line.elements.size() < 2) return; + + for (Element el : line.elements) { + children.offsets.get(el).x += (targetWidth - line.width) / (line.elements.size() - 1) * i; + i++; + } + } + + @Override + protected final void recalculate() { + width = targetWidth; + height = title == null ? 0 : mc.textRenderer.fontHeight; + + for (Line line : getLines()) { + if (line.width > targetWidth) { + recalculateLeft(line, height); + } + else { + switch (order) { + case Start: recalculateLeft(line, height); break; + case Middle: recalculateCenter(line, height); break; + case End: recalculateRight(line, height); break; + case Justified: recalculateJustified(line, height); break; + } + } + height += line.height; + if (width < line.width) width = line.width; + } + } +} diff --git a/src/main/java/me/topchetoeu/smoothchunks/gui/Label.java b/src/main/java/me/topchetoeu/smoothchunks/gui/Label.java new file mode 100644 index 0000000..d346390 --- /dev/null +++ b/src/main/java/me/topchetoeu/smoothchunks/gui/Label.java @@ -0,0 +1,77 @@ +package me.topchetoeu.smoothchunks.gui; + +import org.apache.commons.lang3.Validate; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.Drawable; +import net.minecraft.client.gui.DrawableHelper; +import net.minecraft.client.gui.Element; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.text.Text; +import net.minecraft.util.math.ColorHelper.Argb; + +public class Label extends DrawableHelper implements Drawable, Element, BoundboxProvider { + public final MinecraftClient client; + + private Text text = Text.of(""); + private int height = 0; + public int paddingX = 0, paddingY = 0; + public int x, y; + public int maxWidth; + + public Label setMaxWidth(int val) { + maxWidth = val; + return this; + } + + public float getX() { + return x; + } + public float getY() { + return y; + } + + public float getWidth() { + if (maxWidth <= 0) return 2 + paddingX * 2 + client.textRenderer.getWidth(text); + else return 2 + paddingX * 2 + maxWidth; + } + public float getHeight() { + return 1 + paddingY * 2 + height; + } + + public Text getText() { + return text; + } + public void setText(Text text) { + Validate.notNull(text, "text may not be null."); + this.text = text; + } + public void setText(String text) { + Validate.notNull(text, "text may not be null."); + this.text = Text.of(text); + } + + @Override + public void render(MatrixStack matrices, int x, int y, float delta) { + int white = Argb.getArgb(255, 255, 255, 255); + matrices.push(); + matrices.translate(this.x, this.y, getZOffset()); + + if (maxWidth <= 0) { + client.textRenderer.draw(matrices, text, paddingX + 1, paddingY + 1, white); + height = client.textRenderer.fontHeight; + } + else { + height = (int)SelectionScreen.drawWarpedText(client.textRenderer, matrices, text, paddingX + 1, paddingY + 1, maxWidth); + } + + matrices.pop(); + } + + public Label(int x, int y, Text text) { + this.client = MinecraftClient.getInstance(); + setText(text); + this.x = x; + this.y = y; + } +} diff --git a/src/main/java/me/topchetoeu/smoothchunks/gui/Section.java b/src/main/java/me/topchetoeu/smoothchunks/gui/Section.java new file mode 100644 index 0000000..87339a3 --- /dev/null +++ b/src/main/java/me/topchetoeu/smoothchunks/gui/Section.java @@ -0,0 +1,242 @@ +package me.topchetoeu.smoothchunks.gui; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.AbstractParentElement; +import net.minecraft.client.gui.Drawable; +import net.minecraft.client.gui.Element; +import net.minecraft.client.gui.Selectable; +import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.text.Text; +import net.minecraft.util.math.ColorHelper.Argb; + +public abstract class Section extends AbstractParentElement implements Drawable, Selectable, BoundboxProvider { + protected class Offset { + public float x, y; + + public Offset(float x, float y) { + this.x = x; + this.y = y; + } + } + + public class ChildList { + protected final Map offsets = new Hashtable<>(); + protected final Map boundboxes = new Hashtable<>(); + protected final List children = new ArrayList<>(); + protected final List selectables = new ArrayList<>(); + protected final List drawables = new ArrayList<>(); + + protected Offset getOffsetAndPos(Object element) { + Offset of = new Offset(offsets.get(element).x, offsets.get(element).y); + of.x += boundboxes.get(element).getX(); + of.y += boundboxes.get(element).getY(); + return of; + } + protected Offset getOffset(Object element) { + return offsets.get(element); + } + public BoundboxProvider getBoundbox(Object element) { + return boundboxes.get(element); + } + + public T addSelectableChild(T element) { + return addSelectableChild(element, element); + } + public T addChild(T element) { + return addChild(element, element); + } + + public T addSelectableChild(T element, BoundboxProvider boundbox) { + this.boundboxes.put(element, boundbox); + this.offsets.put(element, new Offset(0, 0)); + this.drawables.add(element); + this.selectables.add(element); + this.children.add(element); + recalculate(); + return element; + } + public T addChild(T element, BoundboxProvider boundbox) { + this.boundboxes.put(element, boundbox); + this.offsets.put(element, new Offset(0, 0)); + this.drawables.add(element); + this.children.add(element); + recalculate(); + return element; + } + + public List get() { + return Collections.unmodifiableList(children); + } + + public void clear() { + focusedIndex = -1; + offsets.clear(); + boundboxes.clear(); + children.clear(); + selectables.clear(); + drawables.clear(); + } + + private ChildList() {} + } + + public enum OrderType { + Start, + Middle, + End, + Justified, + } + + public final ChildList children = new ChildList(); + public OrderType order = OrderType.Start; + protected int focusedIndex = 0; + protected float width, height; + protected MinecraftClient mc = MinecraftClient.getInstance(); + public Text title; + public float x, y; + + public float getX() { + return x; + } + public float getY() { + return y; + } + + public float getWidth() { + return width; + } + public float getHeight() { + return height; + } + + @Override + public List children() { + return children.get(); + } + + @Override + public Element getFocused() { + if (focusedIndex < 0) return null; + else return children().get(focusedIndex); + } + + @Override + public void setFocused(Element element) { + focusedIndex = children().indexOf(element); + } + + @Override + public void appendNarrations(NarrationMessageBuilder var1) { + } + + protected abstract void recalculate(); + + @Override + public SelectionType getType() { + if (this.children.selectables.stream().anyMatch(v -> v.getType() == SelectionType.HOVERED)) { + return SelectionType.HOVERED; + } + + return SelectionType.NONE; + } + + @Override + public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { + recalculate(); + + matrices.push(); + matrices.translate(x, y, getZOffset()); + + if (title != null) { + mc.textRenderer.draw(matrices, title, 5, 0, Argb.getArgb(255, 255, 255, 255)); + drawHorizontalLine(matrices, 0, (int)width, mc.textRenderer.fontHeight, Argb.getArgb(255, 255, 255, 255)); + } + + for (Drawable d : children.drawables) { + Offset o = children.getOffset(d); + + matrices.push(); + matrices.translate(o.x, o.y, 0); + d.render(matrices, mouseX - (int)o.x - (int)x, mouseY - (int)o.y - (int)y, delta); + matrices.pop(); + } + + matrices.pop(); + } + + + @Override + public boolean isMouseOver(double mouseX, double mouseY) { + return getType() == SelectionType.HOVERED || (mouseX >= x && mouseX <= width && mouseY >= y && mouseY <= height); + } + + @Override + public Optional hoveredElement(double mouseX, double mouseY) { + for (Selectable element : this.children.selectables) { + var offset = children.getOffsetAndPos(element); + if (element.getType() != SelectionType.HOVERED || !((Element)element).isMouseOver(mouseX - offset.x, mouseY - offset.y)) continue; + return Optional.of((Element)element); + } + return Optional.empty(); + } + + @Override + public boolean mouseScrolled(double mouseX, double mouseY, double delta) { + for (Element element : this.children()) { + var offset = children.getOffsetAndPos(element); + if (!element.mouseScrolled(mouseX - offset.x, mouseY - offset.y, delta)) continue; + return true; + } + return false; + } + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + for (Element element : this.children()) { + var offset = children.getOffsetAndPos(element); + if (!element.mouseClicked(mouseX - offset.x, mouseY - offset.y, button)) continue; + this.setFocused(element); + if (button == 0) { + this.setDragging(true); + } + return true; + } + return false; + } + + @Override + public void mouseMoved(double mouseX, double mouseY) { + for (Element element : this.children()) { + var offset = children.getOffsetAndPos(element); + element.mouseMoved(mouseX - offset.x, mouseY - offset.y); + } + } + + @Override + public boolean mouseReleased(double mouseX, double mouseY, int button) { + this.setDragging(false); + + for (Element element : children.get()) { + var offset = children.getOffsetAndPos(element); + if (element.mouseReleased(mouseX - (int)offset.x, mouseY - (int)offset.y, button)) return true; + } + + return false; + } + + @Override + public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { + if (this.getFocused() != null && this.isDragging() && button == 0) { + var offset = children.getOffsetAndPos(getFocused()); + return this.getFocused().mouseDragged(mouseX - offset.x, mouseY - offset.y, button, deltaX, deltaY); + } + return false; + } +} diff --git a/src/main/java/me/topchetoeu/smoothchunks/gui/SelectionScreen.java b/src/main/java/me/topchetoeu/smoothchunks/gui/SelectionScreen.java new file mode 100644 index 0000000..d25c754 --- /dev/null +++ b/src/main/java/me/topchetoeu/smoothchunks/gui/SelectionScreen.java @@ -0,0 +1,134 @@ +package me.topchetoeu.smoothchunks.gui; + +import java.util.LinkedHashMap; +import java.util.Map; + +import me.topchetoeu.smoothchunks.Descriptor; +import me.topchetoeu.smoothchunks.Manager; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.text.LiteralText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import net.minecraft.util.math.ColorHelper.Argb; + +public class SelectionScreen extends Screen { + public static interface SelectAction { + void onSelect(Descriptor desc); + } + + public final Screen parent; + public final Manager manager; + public SelectAction selectAction; + private Descriptor selectedElement; + private Descriptor hoveredElement = null; + + public Descriptor getSelected() { + return selectedElement; + } + + public static float drawWarpedText(TextRenderer textRenderer, MatrixStack matrices, Text text, int x, int y, int width) { + var lines = textRenderer.wrapLines(text, width); + float offset = 0; + + for (var line : lines) { + textRenderer.draw(matrices, line, x, y + offset, 0xFFFFFFFF); + offset += textRenderer.fontHeight; + } + + return offset; + } + + private float renderElement(MatrixStack matrices, int mouseX, int mouseY, Descriptor element) { + float y = 0; + matrices.push(); + matrices.translate(20, 5, 0); + textRenderer.draw(matrices, new LiteralText(element.getDisplayNameOrDefault()).formatted(Formatting.BOLD), 0, y, 0xFFFFFFFF); + y += textRenderer.fontHeight + 3; + textRenderer.draw(matrices, new LiteralText("Author: " + element.getAuthorOrDefault()), 0, y, 0xFFFFFFFF); + y += textRenderer.fontHeight + 2; + y += drawWarpedText(textRenderer, matrices, new LiteralText(element.getDescriptionOrDefault()).formatted(Formatting.ITALIC), 0, (int)y, width - 40); + y += 5; + matrices.pop(); + + return y; + } + + @Override + public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { + if (parent == null) renderBackground(matrices); + else parent.render(matrices, Integer.MIN_VALUE, Integer.MIN_VALUE, delta); + + fill(matrices, 0, 0, width, height, Argb.getArgb(220, 0, 0, 0)); + + Map, Integer> heights = new LinkedHashMap<>(); + + matrices.push(); + var currDesc = new Descriptor(manager.getValue(), manager.get().getName()) + .author(manager.get().getAuthor()) + .description(manager.get().getDescription()) + .displayName(manager.get().getDisplayNameOrDefault() + " (currently selected)"); + float offset = renderElement(matrices, mouseX, mouseY, currDesc); + heights.put(currDesc, (int)offset); + matrices.translate(0, offset, 0); + for (Descriptor desc : manager.getAll()) { + offset = renderElement(matrices, mouseX, mouseY, desc); + heights.put(desc, (int)offset); + matrices.translate(0, offset, 0); + } + matrices.pop(); + + int y1 = 5; + + for (var pair : heights.entrySet()) { + int y2 = y1 + pair.getValue(); + + if (mouseY >= y1 - 3 && mouseY <= y2 - 5 && mouseX >= 15 && mouseX <= width - 15) { + hoveredElement = pair.getKey(); + drawHorizontalLine(matrices, 15, width - 15, (int)y1 - 3, 0xFFFFFFFF); + drawHorizontalLine(matrices, 15, width - 15, (int)y2 - 5, 0xFFFFFFFF); + drawVerticalLine(matrices, 15, y1 - 3, y2 - 5, 0xFFFFFFFF); + drawVerticalLine(matrices, width - 15, y1 - 3, y2 - 5, 0xFFFFFFFF); + System.out.println(hoveredElement.getName()); + break; + } + + y1 = y2; + } + + super.render(matrices, mouseX, mouseY, delta); + } + + @Override + protected void init() { + super.init(); + parent.init(client, width, height); + } + + @Override + public void close() { + MinecraftClient.getInstance().setScreen(parent); + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (button == 0 && hoveredElement != null) { + System.out.print(hoveredElement.getName()); + selectAction.onSelect(hoveredElement); + selectedElement = hoveredElement; + close(); + return true; + } + return false; + } + + public SelectionScreen(Screen parent, Manager manager, SelectAction selectAction) { + super(Text.of("Selection")); + + this.manager = manager; + this.parent = parent; + this.selectAction = selectAction; + } +} diff --git a/src/main/java/me/topchetoeu/smoothchunks/gui/SmoothChunksScreen.java b/src/main/java/me/topchetoeu/smoothchunks/gui/SmoothChunksScreen.java new file mode 100644 index 0000000..156f9f5 --- /dev/null +++ b/src/main/java/me/topchetoeu/smoothchunks/gui/SmoothChunksScreen.java @@ -0,0 +1,108 @@ +package me.topchetoeu.smoothchunks.gui; + +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.sound.PositionedSoundInstance; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.sound.SoundEvents; +import net.minecraft.text.LiteralText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import net.minecraft.util.math.ColorHelper.Argb; +import me.topchetoeu.smoothchunks.Manager; +import me.topchetoeu.smoothchunks.SmoothChunks; +import me.topchetoeu.smoothchunks.gui.Section.OrderType; +import net.minecraft.client.MinecraftClient; + +public class SmoothChunksScreen extends Screen { + public final Screen parent; + private final HorizontalSection mainSection = new HorizontalSection(); + + public static void playClick() { + MinecraftClient.getInstance().getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0f)); + } + + @Override + public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { + if (parent == null) renderBackground(matrices); + else parent.render(matrices, Integer.MIN_VALUE, Integer.MIN_VALUE, delta); + + fill(matrices, 0, 0, width, height, Argb.getArgb(191, 0, 0, 0)); + mainSection.render(matrices, mouseX, mouseY, delta); + // super.render(matrices, mouseX, mouseY, delta); + } + + @Override + protected void init() { + super.init(); + parent.init(client, width, height); + + addDrawableChild(mainSection); + mainSection.children.clear(); + mainSection.setTargetWidth(width - 15); + mainSection.order = OrderType.Justified; + mainSection.children.addSelectableChild(selectionsSection()); + mainSection.children.addSelectableChild(previewSection()); + // setZOffset(parent.getZOffset() + 1); + } + + @Override + public void close() { + MinecraftClient.getInstance().setScreen(parent); + } + @Override + public void mouseMoved(double mouseX, double mouseY) { + mainSection.mouseMoved(mouseX, mouseY); + } + + private Section previewSection() { + var res = new HorizontalSection(); + res.x = res.y = 5; + res.title = Text.of("Preview:"); + + res.children.addSelectableChild(new ChunkPreview(0, 0, 150, 150)); + + return res; + } + private Section selectionSection(Manager manager, String name) { + var res = new HorizontalSection(); + res.x = 5; + res.y = 10; + res.title = Text.of(name + ":"); + res.setTargetWidth(width / 2); + + var selectScreen = new SelectionScreen<>(this, manager, val -> manager.set(val.getName())); + var buttonSection = new HorizontalSection(); + buttonSection.setTargetWidth(width / 2); + buttonSection.order = OrderType.Justified; + buttonSection.children.addChild(new Label( + 5, 7, + new LiteralText(manager.get().getDisplayName()).formatted(Formatting.BOLD) + )); + buttonSection.children.addSelectableChild(new Button(5, 5, Text.of("Select ..."), () -> client.setScreen(selectScreen))); + res.children.addSelectableChild(buttonSection); + res.children.addChild(new Label( + 5, 3, + new LiteralText("Author: " + manager.get().getAuthorOrDefault()) + ).setMaxWidth(width / 2)); + res.children.addChild(new Label( + 5, 3, + new LiteralText(manager.get().getDescriptionOrDefault()).formatted(Formatting.ITALIC) + ).setMaxWidth(width / 2)); + + return res; + } + private Section selectionsSection() { + var res = new HorizontalSection(); + res.x = res.y = 5; + res.title = Text.of("Animation config:"); + res.children.addSelectableChild(selectionSection(SmoothChunks.getInstance().getAnimationManager(), "Animation")); + res.children.addSelectableChild(selectionSection(SmoothChunks.getInstance().getEaseManager(), "Ease")); + return res; + } + public SmoothChunksScreen(Screen parent) { + super(Text.of("Smooth Chunks Config")); + mainSection.x = mainSection.y = 5; + + this.parent = parent; + } +} diff --git a/src/main/java/me/topchetoeu/smoothchunks/gui/VerticalSection.java b/src/main/java/me/topchetoeu/smoothchunks/gui/VerticalSection.java new file mode 100644 index 0000000..6c60c93 --- /dev/null +++ b/src/main/java/me/topchetoeu/smoothchunks/gui/VerticalSection.java @@ -0,0 +1,106 @@ +package me.topchetoeu.smoothchunks.gui; + +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; + +import net.minecraft.client.gui.Element; + +public final class VerticalSection extends Section { + private class Line { + public Map heights = new Hashtable<>(); + public List elements = new ArrayList<>(); + public float width = 0, height = 0; + } + + private float targetHeight; + + public float getTargetHeight(float width) { + return this.targetHeight; + } + public void setTargetHeight(float width) { + this.targetHeight = width; + recalculate(); + } + + private List getLines() { + Line currLine = new Line(); + List lines = new ArrayList<>(); + + for (Element el : children.get()) { + var box = children.getBoundbox(el); + var x = box.getX() + box.getWidth(); + var y = box.getY() + box.getHeight(); + + if (currLine.height + y > targetHeight) { + lines.add(currLine); + currLine = new Line(); + } + + currLine.height += y; + currLine.elements.add(el); + currLine.heights.put(el, y); + + if (currLine.width < x) currLine.width = x; + } + lines.add(currLine); + + return lines; + } + + private void recalculateTop(Line line, float offsetX) { + float currY = 0; + for (Element el : line.elements) { + children.offsets.put(el, new Offset(offsetX, currY)); + currY += line.heights.get(el); + } + } + private void recalculateCenter(Line line, float offsetX) { + recalculateTop(line, offsetX); + + for (Element el : line.elements) { + children.offsets.get(el).y += (targetHeight - line.height) / 2; + } + } + private void recalculateBottom(Line line, float offsetX) { + recalculateTop(line, offsetX); + + for (Element el : line.elements) { + children.offsets.get(el).y += (targetHeight - line.height); + } + } + private void recalculateJustified(Line line, float offsetX) { + recalculateTop(line, offsetX); + int i = 0; + + if (line.elements.size() < 2) return; + + for (Element el : line.elements) { + children.offsets.get(el).y += (targetHeight - line.height) / (line.elements.size() - 1) * i; + i++; + } + } + + @Override + protected final void recalculate() { + width = 0; + height = title == null ? 0 : mc.textRenderer.fontHeight + targetHeight; + + for (Line line : getLines()) { + if (line.height > targetHeight) { + recalculateTop(line, width); + } + else { + switch (order) { + case Start: recalculateTop(line, width); break; + case Middle: recalculateCenter(line, width); break; + case End: recalculateBottom(line, width); break; + case Justified: recalculateJustified(line, width); break; + } + } + width += line.width; + if (height < line.height) height = line.height; + } + } +} diff --git a/src/main/java/me/topchetoeu/smoothchunks/mixin/BuiltChunkMixin.java b/src/main/java/me/topchetoeu/smoothchunks/mixin/BuiltChunkMixin.java new file mode 100644 index 0000000..21b8349 --- /dev/null +++ b/src/main/java/me/topchetoeu/smoothchunks/mixin/BuiltChunkMixin.java @@ -0,0 +1,22 @@ +package me.topchetoeu.smoothchunks.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import me.topchetoeu.smoothchunks.SmoothChunks; +import net.minecraft.client.render.chunk.ChunkBuilder; +import net.minecraft.util.math.BlockPos; + +// From flogic's mod +@Mixin(ChunkBuilder.BuiltChunk.class) +abstract class BuiltChunkMixin { + @Inject(method = "clear", at = @At(value = "TAIL"), cancellable = true) + public void clear(CallbackInfo ci) { + // ci.cancel(); + // return; + BlockPos origin = ((ChunkBuilder.BuiltChunk)(Object)this).getOrigin(); + SmoothChunks.getInstance().getProgressManager().unload(origin.getX(), 0, origin.getZ()); + } +} \ No newline at end of file diff --git a/src/main/java/me/topchetoeu/smoothchunks/mixin/WorldRendererMixin.java b/src/main/java/me/topchetoeu/smoothchunks/mixin/WorldRendererMixin.java new file mode 100644 index 0000000..ea50187 --- /dev/null +++ b/src/main/java/me/topchetoeu/smoothchunks/mixin/WorldRendererMixin.java @@ -0,0 +1,98 @@ +package me.topchetoeu.smoothchunks.mixin; + +import net.minecraft.client.gl.GlUniform; +import net.minecraft.client.render.BuiltChunkStorage; +import net.minecraft.client.render.Camera; +import net.minecraft.client.render.GameRenderer; +import net.minecraft.client.render.LightmapTextureManager; +import net.minecraft.client.render.RenderLayer; +import net.minecraft.client.render.Shader; +import net.minecraft.client.render.VertexFormat; +import net.minecraft.client.render.WorldRenderer; +import net.minecraft.client.render.chunk.ChunkBuilder.BuiltChunk; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.math.Matrix4f; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import it.unimi.dsi.fastutil.objects.ObjectListIterator; +import me.topchetoeu.smoothchunks.SmoothChunks; +import me.topchetoeu.smoothchunks.animation.ProgressManager; + +@Mixin(WorldRenderer.class) +abstract class WorldRendererMixin { + private long lastTime = System.nanoTime(); + + @Accessor abstract BuiltChunkStorage getChunks(); + + private ProgressManager getProgressManager() { + return SmoothChunks.getInstance().getProgressManager(); + } + + @Inject(method = "render", at = @At(value = "HEAD")) + private void renderStart(MatrixStack matrices, float tickDelta, long limitTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightmapTextureManager lightmapTextureManager, Matrix4f positionMatrix, CallbackInfo ci) { + long currTime = System.nanoTime(); + getProgressManager().tick((currTime - lastTime) / 1000000000f); + lastTime = currTime; + } + + @Inject(method = "renderLayer", at = @At(value = "HEAD")) + private void renderLayerAfter(RenderLayer renderLayer, MatrixStack matrices, double playerX, double playerY, double playerZ, Matrix4f positionMatrix, CallbackInfo ci) { + int chunkX = (int)(playerX / 16); + int chunkY = (int)(playerY / 16); + int chunkZ = (int)(playerZ / 16); + + if (playerX < 0) chunkX--; + if (playerY < 0) chunkY--; + if (playerZ < 0) chunkZ--; + + getProgressManager().unloadAllFar((int)((WorldRenderer)(Object)this).getViewDistance(), chunkX, chunkY, chunkZ); + } + @Inject(method = "renderLayer", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gl/VertexBuffer;drawVertices()V"), locals=LocalCapture.CAPTURE_FAILHARD) + private void renderChunkBefore(RenderLayer renderLayer, MatrixStack matrices, double playerX, double playerY, double playerZ, Matrix4f positionMatrix, CallbackInfo ci, + boolean _1, ObjectListIterator _2, VertexFormat _3, Shader shader, GlUniform _4, boolean _5, WorldRenderer.ChunkInfo _6, BuiltChunk chunk) { + + matrices.push(); + + int x = chunk.getOrigin().getX(); + int y = chunk.getOrigin().getY(); + int z = chunk.getOrigin().getZ(); + + shader.fogStart.set(Float.POSITIVE_INFINITY); + + if (getProgressManager().isChunkLoaded(x, 0, z)) { + float progress = getProgressManager().getChunkProgress(x, 0, z); + + if (progress < 0.999) { + progress = SmoothChunks.getInstance().getEaseManager().getValue().ease(progress); + + float centerX = (float)playerX - x; + float centerY = (float)playerY - y; + float centerZ = (float)playerZ - z; + + matrices.translate(-centerX, -centerY, -centerZ); + SmoothChunks.getInstance().getAnimationManager().getValue().animate(progress, matrices, x, y, z, (float)playerX, (float)playerY, (float)playerZ); + matrices.translate(centerX, centerY, centerZ); + } + } + else { + matrices.scale(0, 0, 0); + } + + shader.modelViewMat.set(matrices.peek().getPositionMatrix()); + matrices.pop(); + shader.bind(); + } + @Inject(method = "renderLayer", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gl/VertexBuffer;drawVertices()V"), locals=LocalCapture.CAPTURE_FAILHARD) + private void renderChunkAfter(RenderLayer renderLayer, MatrixStack matrices, double playerX, double playerY, double playerZ, Matrix4f positionMatrix, CallbackInfo ci, + boolean _1, ObjectListIterator _2, VertexFormat _3, Shader _4, GlUniform _5, boolean _6, WorldRenderer.ChunkInfo _7, BuiltChunk chunk) { + int x = chunk.getOrigin().getX(); + int z = chunk.getOrigin().getZ(); + getProgressManager().load(x, 0, z); + } +} diff --git a/src/main/resources/assets/smooth-chunks/icon.png b/src/main/resources/assets/smooth-chunks/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..fe68be42bcd2008299e71ef5e194f291cb633ec1 GIT binary patch literal 14810 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 fpR=0.9.3+build.207", + "fabric": "*", + "minecraft": ">=1.18.2", + "modmenu": ">=3.2.1" + } +} diff --git a/src/main/resources/smooth-chunks.accesswidener b/src/main/resources/smooth-chunks.accesswidener new file mode 100644 index 0000000..0e107e4 --- /dev/null +++ b/src/main/resources/smooth-chunks.accesswidener @@ -0,0 +1,4 @@ +accessWidener v1 named + +accessible class net/minecraft/client/render/chunk/ChunkBuilder$BuiltChunk$RebuildTask +accessible class net/minecraft/client/render/WorldRenderer$ChunkInfo \ No newline at end of file diff --git a/src/main/resources/smooth-chunks.mixins.json b/src/main/resources/smooth-chunks.mixins.json new file mode 100644 index 0000000..d7e0296 --- /dev/null +++ b/src/main/resources/smooth-chunks.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "me.topchetoeu.smoothchunks.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [], + "client": [ + "WorldRendererMixin", + "BuiltChunkMixin" + ], + "injectors": { + "defaultRequire": 1 + } +}