oops
This commit is contained in:
parent
3d13ebef47
commit
ff51eb8abc
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
*
|
||||
!src
|
||||
!src/**
|
||||
!.gitignore
|
||||
!build.gradle
|
||||
!gradle.properties
|
||||
!gradlew
|
||||
!gradlew.bat
|
||||
!LICENSE
|
||||
!readme.md
|
||||
!settings.gradle
|
22
LICENSE
Normal file
22
LICENSE
Normal file
@ -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.
|
100
build.gradle
Normal file
100
build.gradle
Normal file
@ -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<String> 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")
|
||||
}
|
20
gradle.properties
Normal file
20
gradle.properties
Normal file
@ -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
|
185
gradlew
vendored
Normal file
185
gradlew
vendored
Normal file
@ -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" "$@"
|
89
gradlew.bat
vendored
Normal file
89
gradlew.bat
vendored
Normal file
@ -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
|
27
readme.md
Normal file
27
readme.md
Normal file
@ -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.
|
||||
|
10
settings.gradle
Normal file
10
settings.gradle
Normal file
@ -0,0 +1,10 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
jcenter()
|
||||
maven {
|
||||
name = 'Fabric'
|
||||
url = 'https://maven.fabricmc.net/'
|
||||
}
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
149
src/main/java/me/topchetoeu/smoothchunks/Descriptor.java
Normal file
149
src/main/java/me/topchetoeu/smoothchunks/Descriptor.java
Normal file
@ -0,0 +1,149 @@
|
||||
package me.topchetoeu.smoothchunks;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
public final class Descriptor<T> 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<T> 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<T> 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<T> 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<T> displayName(StringModifier modifier) {
|
||||
Validate.notNull(modifier, "modifier may not be null.");
|
||||
this.displayName = modifier.modify(this.displayName);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Descriptor<T> clone() {
|
||||
return new Descriptor<T>(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<T> 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<T> 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;
|
||||
|
||||
}
|
||||
}
|
82
src/main/java/me/topchetoeu/smoothchunks/Manager.java
Normal file
82
src/main/java/me/topchetoeu/smoothchunks/Manager.java
Normal file
@ -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<T> {
|
||||
public static interface RegisterEvent<T> {
|
||||
public void register(Manager<T> manager);
|
||||
}
|
||||
public static final <T> Event<RegisterEvent<T>> createEvent() {
|
||||
return EventFactory.createArrayBacked(RegisterEvent.class, (listeners) -> (manager) -> {
|
||||
for (RegisterEvent<T> listener: listeners) {
|
||||
listener.register(manager);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String currName = null;
|
||||
private Map<String, Descriptor<T>> 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<? extends T> 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<T>(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<T> get(String name) {
|
||||
return objects.get(name);
|
||||
}
|
||||
/**
|
||||
* Gets the currently used object's descriptor (never null)
|
||||
*/
|
||||
public Descriptor<T> 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<Descriptor<T>> getAll() {
|
||||
return Collections.unmodifiableCollection(objects.values());
|
||||
}
|
||||
|
||||
public Manager(T _default) {
|
||||
register(new Descriptor<>(_default, "default").displayName("Default").author("TopchetoEU"));
|
||||
set("default");
|
||||
}
|
||||
}
|
145
src/main/java/me/topchetoeu/smoothchunks/SmoothChunks.java
Normal file
145
src/main/java/me/topchetoeu/smoothchunks/SmoothChunks.java
Normal file
@ -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<RegisterEvent<Ease>> EASES_REGISTERING = Manager.createEvent();
|
||||
/**
|
||||
* An event, fired once, when animations are being registered
|
||||
*/
|
||||
public final Event<RegisterEvent<Animation>> ANIMATIONS_REGISTERING = Manager.createEvent();
|
||||
|
||||
private ProgressManager progress;
|
||||
private Manager<Ease> ease;
|
||||
private Manager<Animation> animation;
|
||||
|
||||
/**
|
||||
* Gets the chunk progress manager
|
||||
*/
|
||||
public ProgressManager getProgressManager() {
|
||||
return progress;
|
||||
}
|
||||
/**
|
||||
* Gets the animation manager
|
||||
*/
|
||||
public Manager<Animation> getAnimationManager() {
|
||||
return animation;
|
||||
}
|
||||
/**
|
||||
* Gets the ease manager
|
||||
*/
|
||||
public Manager<Ease> getEaseManager() {
|
||||
return ease;
|
||||
}
|
||||
|
||||
private static void registerEases(Manager<Ease> 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<Animation> 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<Screen>() {
|
||||
@Override
|
||||
public Screen create(Screen parent) {
|
||||
return new SmoothChunksScreen(parent);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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<ChunkLoc, Float> chunkToStage = new Hashtable<>();
|
||||
private HashSet<ChunkLoc> 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));
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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() {
|
||||
|
||||
}
|
||||
}
|
34
src/main/java/me/topchetoeu/smoothchunks/easing/Ease.java
Normal file
34
src/main/java/me/topchetoeu/smoothchunks/easing/Ease.java
Normal file
@ -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;
|
||||
};
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package me.topchetoeu.smoothchunks.easing;
|
||||
|
||||
public class LinearEase implements Ease {
|
||||
public float ease(float x) {
|
||||
return x;
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package me.topchetoeu.smoothchunks.easing;
|
||||
|
||||
public class QuadraticEase implements Ease {
|
||||
@Override
|
||||
public float ease(float x) {
|
||||
return x * x;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
29
src/main/java/me/topchetoeu/smoothchunks/gui/Boundbox.java
Normal file
29
src/main/java/me/topchetoeu/smoothchunks/gui/Boundbox.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package me.topchetoeu.smoothchunks.gui;
|
||||
|
||||
public interface BoundboxProvider {
|
||||
float getX();
|
||||
float getY();
|
||||
float getWidth();
|
||||
float getHeight();
|
||||
}
|
164
src/main/java/me/topchetoeu/smoothchunks/gui/Button.java
Normal file
164
src/main/java/me/topchetoeu/smoothchunks/gui/Button.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
338
src/main/java/me/topchetoeu/smoothchunks/gui/ChunkPreview.java
Normal file
338
src/main/java/me/topchetoeu/smoothchunks/gui/ChunkPreview.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
@ -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<Element, Float> widths = new Hashtable<>();
|
||||
public List<Element> 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<Line> getLines() {
|
||||
Line currLine = new Line();
|
||||
List<Line> 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;
|
||||
}
|
||||
}
|
||||
}
|
77
src/main/java/me/topchetoeu/smoothchunks/gui/Label.java
Normal file
77
src/main/java/me/topchetoeu/smoothchunks/gui/Label.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
242
src/main/java/me/topchetoeu/smoothchunks/gui/Section.java
Normal file
242
src/main/java/me/topchetoeu/smoothchunks/gui/Section.java
Normal file
@ -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<Element, Offset> offsets = new Hashtable<>();
|
||||
protected final Map<Element, BoundboxProvider> boundboxes = new Hashtable<>();
|
||||
protected final List<Element> children = new ArrayList<>();
|
||||
protected final List<Selectable> selectables = new ArrayList<>();
|
||||
protected final List<Drawable> 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 extends Element & Drawable & Selectable & BoundboxProvider> T addSelectableChild(T element) {
|
||||
return addSelectableChild(element, element);
|
||||
}
|
||||
public <T extends Element & Drawable & BoundboxProvider> T addChild(T element) {
|
||||
return addChild(element, element);
|
||||
}
|
||||
|
||||
public <T extends Element & Drawable & Selectable> 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 extends Element & Drawable> 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<? extends Element> 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<? extends Element> 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<Element> 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;
|
||||
}
|
||||
}
|
@ -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<T> extends Screen {
|
||||
public static interface SelectAction<T> {
|
||||
void onSelect(Descriptor<T> desc);
|
||||
}
|
||||
|
||||
public final Screen parent;
|
||||
public final Manager<T> manager;
|
||||
public SelectAction<T> selectAction;
|
||||
private Descriptor<T> selectedElement;
|
||||
private Descriptor<T> hoveredElement = null;
|
||||
|
||||
public Descriptor<T> 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<T> 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<Descriptor<T>, Integer> heights = new LinkedHashMap<>();
|
||||
|
||||
matrices.push();
|
||||
var currDesc = new Descriptor<T>(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<T> 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<T> manager, SelectAction<T> selectAction) {
|
||||
super(Text.of("Selection"));
|
||||
|
||||
this.manager = manager;
|
||||
this.parent = parent;
|
||||
this.selectAction = selectAction;
|
||||
}
|
||||
}
|
@ -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 <T> Section selectionSection(Manager<T> 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;
|
||||
}
|
||||
}
|
@ -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<Element, Float> heights = new Hashtable<>();
|
||||
public List<Element> 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<Line> getLines() {
|
||||
Line currLine = new Line();
|
||||
List<Line> 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
BIN
src/main/resources/assets/smooth-chunks/icon.png
Normal file
BIN
src/main/resources/assets/smooth-chunks/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
29
src/main/resources/fabric.mod.json
Normal file
29
src/main/resources/fabric.mod.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "smooth-chunks",
|
||||
"version": "0.1.0",
|
||||
"name": "Smooth Chunks",
|
||||
"description": "Smooth chunk load animations.",
|
||||
"authors": [ "TopchetoEU" ],
|
||||
"license": "MIT",
|
||||
"icon": "assets/smooth-chunks/icon.png",
|
||||
"environment": "client",
|
||||
"accessWidener": "smooth-chunks.accesswidener",
|
||||
"entrypoints": {
|
||||
"client": [
|
||||
"me.topchetoeu.smoothchunks.SmoothChunks"
|
||||
],
|
||||
"modmenu": [
|
||||
"me.topchetoeu.smoothchunks.SmoothChunks"
|
||||
]
|
||||
},
|
||||
"mixins": [
|
||||
"smooth-chunks.mixins.json"
|
||||
],
|
||||
"depends": {
|
||||
"fabricloader": ">=0.9.3+build.207",
|
||||
"fabric": "*",
|
||||
"minecraft": ">=1.18.2",
|
||||
"modmenu": ">=3.2.1"
|
||||
}
|
||||
}
|
4
src/main/resources/smooth-chunks.accesswidener
Normal file
4
src/main/resources/smooth-chunks.accesswidener
Normal file
@ -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
|
14
src/main/resources/smooth-chunks.mixins.json
Normal file
14
src/main/resources/smooth-chunks.mixins.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"package": "me.topchetoeu.smoothchunks.mixin",
|
||||
"compatibilityLevel": "JAVA_17",
|
||||
"mixins": [],
|
||||
"client": [
|
||||
"WorldRendererMixin",
|
||||
"BuiltChunkMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user