This commit is contained in:
TopchetoEU 2022-09-12 20:35:52 +03:00
parent 3d13ebef47
commit ff51eb8abc
No known key found for this signature in database
GPG Key ID: 0F2543CA49C81E3A
38 changed files with 2704 additions and 0 deletions

11
.gitignore vendored Normal file
View File

@ -0,0 +1,11 @@
*
!src
!src/**
!.gitignore
!build.gradle
!gradle.properties
!gradlew
!gradlew.bat
!LICENSE
!readme.md
!settings.gradle

22
LICENSE Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,10 @@
pluginManagement {
repositories {
jcenter()
maven {
name = 'Fabric'
url = 'https://maven.fabricmc.net/'
}
gradlePluginPortal()
}
}

View 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;
}
}

View 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");
}
}

View 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);
}
};
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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));
}
}

View File

@ -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;
}
}

View File

@ -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() {
}
}

View 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;
};
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,7 @@
package me.topchetoeu.smoothchunks.easing;
public class LinearEase implements Ease {
public float ease(float x) {
return x;
}
}

View File

@ -0,0 +1,8 @@
package me.topchetoeu.smoothchunks.easing;
public class QuadraticEase implements Ease {
@Override
public float ease(float x) {
return x * x;
}
}

View File

@ -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);
}
}

View 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;
}
}

View File

@ -0,0 +1,8 @@
package me.topchetoeu.smoothchunks.gui;
public interface BoundboxProvider {
float getX();
float getY();
float getWidth();
float getHeight();
}

View 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;
}
}

View 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;
}
}

View File

@ -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;
}
}
}

View 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;
}
}

View 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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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());
}
}

View File

@ -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);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View 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"
}
}

View 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

View 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
}
}