From 7dba6ec9524dbce96665be7d602fd8ff81fb4045 Mon Sep 17 00:00:00 2001 From: Gegy Date: Sat, 2 May 2026 15:50:40 +0200 Subject: [PATCH 1/2] Fix: clear breaking overlay state after rendering Once the breaking overlay has been rendered once, any chunk rebuild on the main thread will be considered to be part of a breaking overlay. --- src/main/java/com/supermartijn642/fusion/FusionClient.java | 3 ++- .../fusion/mixin/BlockRenderDispatcherMixin.java | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/supermartijn642/fusion/FusionClient.java b/src/main/java/com/supermartijn642/fusion/FusionClient.java index fceaf24b..e7c5cf24 100644 --- a/src/main/java/com/supermartijn642/fusion/FusionClient.java +++ b/src/main/java/com/supermartijn642/fusion/FusionClient.java @@ -13,6 +13,7 @@ import com.supermartijn642.fusion.util.IdentifierUtil; import net.minecraft.client.renderer.chunk.ChunkSectionLayer; import net.minecraft.resources.Identifier; +import net.minecraft.util.Unit; import net.neoforged.fml.ModList; import net.neoforged.fml.ModLoadingContext; import net.neoforged.neoforge.client.event.ModelEvent; @@ -29,7 +30,7 @@ public class FusionClient { public static final Logger LOGGER = LoggerFactory.getLogger("fusion"); - public static final ThreadLocal IS_RENDERING_BREAKING_OVERLAY = new ThreadLocal<>(); + public static final ThreadLocal IS_RENDERING_BREAKING_OVERLAY = new ThreadLocal<>(); public static void init(){ // Register default texture types diff --git a/src/main/java/com/supermartijn642/fusion/mixin/BlockRenderDispatcherMixin.java b/src/main/java/com/supermartijn642/fusion/mixin/BlockRenderDispatcherMixin.java index 79c7c7c7..9129dae4 100644 --- a/src/main/java/com/supermartijn642/fusion/mixin/BlockRenderDispatcherMixin.java +++ b/src/main/java/com/supermartijn642/fusion/mixin/BlockRenderDispatcherMixin.java @@ -2,6 +2,7 @@ import com.supermartijn642.fusion.FusionClient; import net.minecraft.client.renderer.block.BlockRenderDispatcher; +import net.minecraft.util.Unit; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -18,7 +19,7 @@ public class BlockRenderDispatcherMixin { at = @At("HEAD") ) private void renderBreakingTextureHead(CallbackInfo ci){ - FusionClient.IS_RENDERING_BREAKING_OVERLAY.set(true); + FusionClient.IS_RENDERING_BREAKING_OVERLAY.set(Unit.INSTANCE); } @Inject( @@ -26,6 +27,6 @@ private void renderBreakingTextureHead(CallbackInfo ci){ at = @At("TAIL") ) private void renderBreakingTextureTail(CallbackInfo ci){ - FusionClient.IS_RENDERING_BREAKING_OVERLAY.set(false); + FusionClient.IS_RENDERING_BREAKING_OVERLAY.remove(); } } From c021b74337605fcbce1a9ed82700b6bcd3f87996 Mon Sep 17 00:00:00 2001 From: Gegy Date: Sat, 2 May 2026 16:24:37 +0200 Subject: [PATCH 2/2] Update to Minecraft 26.1.2 --- build.gradle | 10 +- gradle.properties | 20 +- gradle/wrapper/gradle-wrapper.jar | Bin 59536 -> 43764 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 286 +++++++++++------- gradlew.bat | 41 +-- .../supermartijn642/fusion/FusionClient.java | 21 -- .../api/model/BlockModelBakingContext.java | 33 +- .../fusion/api/model/DefaultModelTypes.java | 6 +- .../fusion/api/model/ModelInstance.java | 6 +- .../fusion/api/model/ModelType.java | 5 +- .../fusion/api/model/SpriteIdentifier.java | 34 --- .../fusion/api/model/data/BaseModelData.java | 4 +- .../model/data/VanillaModelDataBuilder.java | 14 +- .../api/texture/data/BaseTextureData.java | 20 -- .../texture/data/ConnectingTextureData.java | 23 -- .../fusion/entity/EntityRenderTypeHelper.java | 10 +- ...tension.java => CuboidModelExtension.java} | 2 +- .../fusion/mixin/AtlasConfigMixin.java | 17 +- .../mixin/BlockRenderDispatcherMixin.java | 8 +- ....java => CuboidItemModelWrapperMixin.java} | 13 +- ...kModelMixin.java => CuboidModelMixin.java} | 8 +- .../fusion/mixin/FaceBakeryMixin.java | 22 ++ .../fusion/mixin/FusionMixinPlugin.java | 1 - .../mixin/ItemFeatureRendererMixin.java | 32 ++ .../fusion/mixin/ItemRendererMixin.java | 42 --- .../fusion/mixin/ModelBakeryMixin.java | 12 +- .../fusion/mixin/ModelBlockRendererMixin.java | 60 +--- .../fusion/mixin/ModelManagerMixin.java | 4 +- .../fusion/mixin/PackEntryMixin.java | 14 +- .../fusion/mixin/PathPackResourcesMixin.java | 10 +- .../fusion/mixin/SingleVariantMixin.java | 6 +- .../mixin/sodium/ItemRendererMixinSodium.java | 105 ------- .../model/BlockModelBakingContextImpl.java | 23 +- .../fusion/model/FusionBlockModel.java | 32 +- .../model/ItemModelBakingContextImpl.java | 10 +- .../fusion/model/ModelInstanceImpl.java | 7 +- .../fusion/model/MutableQuad.java | 52 ++-- .../fusion/model/SpriteIdentifierImpl.java | 65 ---- .../fusion/model/WrappedBakedModel.java | 31 +- .../block/BlockModelModifierBakedModel.java | 31 +- .../BlockModelModifierReloadListener.java | 6 +- .../block/PaneCullingBakedModel.java | 41 ++- .../item/ItemModelModifierReloadListener.java | 5 +- .../fusion/model/types/UnknownModelType.java | 27 +- .../model/types/base/BaseBakedModel.java | 117 ++++--- .../model/types/base/BaseItemModel.java | 80 ++--- .../types/base/BaseModelDataBuilderImpl.java | 33 +- .../model/types/base/BaseModelDataImpl.java | 61 ++-- .../model/types/base/BaseModelElement.java | 12 +- .../model/types/base/BaseModelQuad.java | 26 +- .../model/types/base/BaseModelType.java | 43 ++- .../connecting/ConnectingBakedModel.java | 271 ++++++++--------- .../types/connecting/ConnectingItemModel.java | 79 ++--- .../ConnectingModelDataBuilderImpl.java | 5 +- .../connecting/ConnectingModelDataImpl.java | 42 +-- .../connecting/ConnectingModelElement.java | 6 +- .../types/connecting/ConnectingModelQuad.java | 4 +- .../types/connecting/ConnectingModelType.java | 36 +-- .../connecting/SurroundingBlockCache.java | 2 +- .../vanilla/VanillaModelDataBuilderImpl.java | 37 +-- .../types/vanilla/VanillaModelSerializer.java | 17 +- .../model/types/vanilla/VanillaModelType.java | 10 +- .../MinimumVersionWarningScreen.java | 26 +- .../ResourcePackListTipRenderer.java | 6 +- .../fusion/texture/QuadTintingHelper.java | 33 +- .../base/BaseTextureDataBuilderImpl.java | 9 +- .../types/base/BaseTextureDataImpl.java | 9 +- .../texture/types/base/BaseTextureSprite.java | 29 ++ .../texture/types/base/BaseTextureType.java | 15 - .../ConnectingTextureDataBuilderImpl.java | 24 +- .../connecting/ConnectingTextureDataImpl.java | 5 +- .../connecting/ConnectingTextureSprite.java | 8 + .../connecting/ConnectingTextureType.java | 1 - .../ContinuousTextureDataBuilderImpl.java | 9 +- .../continuous/ContinuousTextureDataImpl.java | 4 +- .../continuous/ContinuousTextureSprite.java | 7 + .../continuous/ContinuousTextureType.java | 1 - .../random/RandomTextureDataBuilderImpl.java | 9 +- .../types/random/RandomTextureDataImpl.java | 4 +- .../types/random/RandomTextureSprite.java | 7 + .../types/random/RandomTextureType.java | 1 - .../ScrollingTextureDataBuilderImpl.java | 9 +- .../scrolling/ScrollingTextureDataImpl.java | 4 +- .../types/scrolling/ScrollingTextureType.java | 29 +- .../resources/META-INF/accesstransformer.cfg | 32 +- src/main/resources/modid.mixins.json | 7 +- 87 files changed, 1058 insertions(+), 1332 deletions(-) mode change 100644 => 100755 gradlew delete mode 100644 src/main/java/com/supermartijn642/fusion/api/model/SpriteIdentifier.java rename src/main/java/com/supermartijn642/fusion/extensions/{BlockModelExtension.java => CuboidModelExtension.java} (86%) rename src/main/java/com/supermartijn642/fusion/mixin/{BlockModelWrapperMixin.java => CuboidItemModelWrapperMixin.java} (76%) rename src/main/java/com/supermartijn642/fusion/mixin/{BlockModelMixin.java => CuboidModelMixin.java} (67%) create mode 100644 src/main/java/com/supermartijn642/fusion/mixin/FaceBakeryMixin.java create mode 100644 src/main/java/com/supermartijn642/fusion/mixin/ItemFeatureRendererMixin.java delete mode 100644 src/main/java/com/supermartijn642/fusion/mixin/ItemRendererMixin.java delete mode 100644 src/main/java/com/supermartijn642/fusion/mixin/sodium/ItemRendererMixinSodium.java delete mode 100644 src/main/java/com/supermartijn642/fusion/model/SpriteIdentifierImpl.java diff --git a/build.gradle b/build.gradle index cd4419d7..d9f91d49 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id "eclipse" id "idea" id "java-library" - id "net.neoforged.gradle.userdev" version "7.0.192" + id "net.neoforged.gradle.userdev" version "7.1.25" id "me.modmuss50.mod-publish-plugin" version "0.5.2" } @@ -35,12 +35,12 @@ dependencies { implementation "net.neoforged:neoforge:${neoforge_version}" // ModernFix -// runtimeOnly "curse.maven:modernfix-790626:6171686" +// runtimeOnly "curse.maven:modernfix-790626:7969600" // Sodium - compileOnly "net.caffeinemc:sodium-neoforge-mod:0.8.1+mc1.21.11" -// runtimeOnly "net.caffeinemc:sodium-neoforge:0.8.1+mc1.21.11" + compileOnly "net.caffeinemc:sodium-neoforge-mod:0.8.10+mc26.1.2" +// runtimeOnly "net.caffeinemc:sodium-neoforge:0.8.10+mc26.1.2" // Iris -// runtimeOnly "maven.modrinth:iris:1.10.3+1.21.11-neoforge" +// runtimeOnly "maven.modrinth:iris:1.10.9+26.1-neoforge" // Just Enough Items // runtimeOnly "curse.maven:jei-238222:${just_enough_items_file}" diff --git a/gradle.properties b/gradle.properties index 07e8b124..6c9f142f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,16 +3,16 @@ org.gradle.jvmargs=-Xmx3G org.gradle.daemon=false # Minecraft -minecraft_version=1.21.11 -minecraft_dependency=[1.21.11,1.22) -minecraft_suffix=mc1.21.11 -java_target=21 -java_dependency=>=21 -resource_pack_format=75 +minecraft_version=26.1.2 +minecraft_dependency=[26.1,26.2) +minecraft_suffix=mc26.1 +java_target=25 +java_dependency=>=25 +resource_pack_format=84 # NeoForge -neoforge_version=21.11.12-beta -neoforge_dependency=[21.11.12-beta,) +neoforge_version=26.1.2.30-beta +neoforge_dependency=[26.1.2.30-beta,) javafml_dependency=[1,) # Mixin @@ -36,7 +36,7 @@ maven_group=com.supermartijn642 # Publishing publishing_release_type=stable -publishing_game_versions=1.21.11 +publishing_game_versions=26.1 26.1.1 26.1.2 curseforge_project_id=854949 curseforge_required_dependency_ids= curseforge_optional_dependency_ids= @@ -45,4 +45,4 @@ modrinth_required_dependency_ids= modrinth_optional_dependency_ids= # Runtime mods -just_enough_items_file=5630564 +just_enough_items_file=8014757 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180f2ae8848c63b8b4dea2cb829da983f2fa..1b33c55baabb587c669f562ae36f953de2481846 100644 GIT binary patch literal 43764 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 QuEJmfE|#`Dqx|px0L@K;Y5)KL diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ff23a68d..1a704683 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 index 4f906e0c..23d15a93 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,81 +15,115 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # 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 +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac 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"' +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # 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 - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar +CLASSPATH="\\\"\\\"" # 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" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,88 +132,120 @@ 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. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + 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 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 +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac 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 +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # 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 +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # 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\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg 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" +# 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"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 107acd32..db3a6ac2 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,8 +13,10 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +27,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 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. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -56,32 +59,34 @@ 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. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar +set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 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 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/src/main/java/com/supermartijn642/fusion/FusionClient.java b/src/main/java/com/supermartijn642/fusion/FusionClient.java index e7c5cf24..96ddadab 100644 --- a/src/main/java/com/supermartijn642/fusion/FusionClient.java +++ b/src/main/java/com/supermartijn642/fusion/FusionClient.java @@ -86,27 +86,6 @@ public static void init(){ // ClientLifecycleEvents.CLIENT_STARTED.register(client -> PredicateRegistryImpl.finalizeRegistration()); } - public static Optional getChunkLayer(BaseTextureData.RenderType renderType){ - if(renderType == null) - return Optional.empty(); - ChunkSectionLayer material; - //noinspection EnhancedSwitchMigration - switch(renderType){ - case OPAQUE: - material = ChunkSectionLayer.SOLID; - break; - case CUTOUT: - material = ChunkSectionLayer.CUTOUT; - break; - case TRANSLUCENT: - material = ChunkSectionLayer.TRANSLUCENT; - break; - default: - throw new AssertionError(); - } - return Optional.of(material); - } - private static String fusionVersion; public static String getFusionVersion(){ diff --git a/src/main/java/com/supermartijn642/fusion/api/model/BlockModelBakingContext.java b/src/main/java/com/supermartijn642/fusion/api/model/BlockModelBakingContext.java index dda1234a..addb4604 100644 --- a/src/main/java/com/supermartijn642/fusion/api/model/BlockModelBakingContext.java +++ b/src/main/java/com/supermartijn642/fusion/api/model/BlockModelBakingContext.java @@ -1,12 +1,10 @@ package com.supermartijn642.fusion.api.model; -import com.supermartijn642.fusion.util.TextureAtlases; -import net.minecraft.client.renderer.block.model.ItemTransforms; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.resources.model.Material; +import net.minecraft.client.renderer.block.dispatch.ModelState; import net.minecraft.client.resources.model.ModelBaker; -import net.minecraft.client.resources.model.ModelState; -import net.minecraft.client.resources.model.UnbakedGeometry; +import net.minecraft.client.resources.model.cuboid.ItemTransforms; +import net.minecraft.client.resources.model.geometry.UnbakedGeometry; +import net.minecraft.client.resources.model.sprite.Material; import net.minecraft.resources.Identifier; import net.minecraft.util.context.ContextMap; import org.jetbrains.annotations.Nullable; @@ -24,27 +22,12 @@ public interface BlockModelBakingContext { ModelBaker getModelBaker(); /** - * Gets the sprite for the given material. - * @param identifier identifier for the sprite + * Bakes the given material. + * @param material material to bake */ - TextureAtlasSprite getTexture(SpriteIdentifier identifier); + Material.Baked bakeMaterial(Material material); - /** - * Gets the sprite for the given atlas and texture. - * @param atlas atlas which the texture is stitched to - * @param texture texture identifier - */ - default TextureAtlasSprite getTexture(Identifier atlas, Identifier texture){ - return this.getTexture(SpriteIdentifier.of(atlas, texture)); - } - - /** - * Gets the sprite for the given texture on the block atlas. - * @param texture texture identifier - */ - default TextureAtlasSprite getBlockTexture(Identifier texture){ - return this.getTexture(TextureAtlases.getBlocks(), texture); - } + Material missingMaterial(); /** * @return the transformations which should be applied to the model diff --git a/src/main/java/com/supermartijn642/fusion/api/model/DefaultModelTypes.java b/src/main/java/com/supermartijn642/fusion/api/model/DefaultModelTypes.java index 892a3d92..0e3b458e 100644 --- a/src/main/java/com/supermartijn642/fusion/api/model/DefaultModelTypes.java +++ b/src/main/java/com/supermartijn642/fusion/api/model/DefaultModelTypes.java @@ -8,8 +8,8 @@ import com.supermartijn642.fusion.model.types.base.BaseModelType; import com.supermartijn642.fusion.model.types.connecting.ConnectingModelType; import com.supermartijn642.fusion.model.types.vanilla.VanillaModelType; -import net.minecraft.client.renderer.block.model.BlockModel; import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.client.resources.model.cuboid.CuboidModel; /** * Contains references to the default model types provided by Fusion. @@ -19,9 +19,9 @@ public class DefaultModelTypes { /** - * Model type used for vanilla {@link BlockModel} instances. + * Model type used for vanilla {@link CuboidModel} instances. */ - public static final ModelType VANILLA = new VanillaModelType(); + public static final ModelType VANILLA = new VanillaModelType(); /** * Model type used for any unknown models added by other mods. */ diff --git a/src/main/java/com/supermartijn642/fusion/api/model/ModelInstance.java b/src/main/java/com/supermartijn642/fusion/api/model/ModelInstance.java index f4847661..93b6bf8a 100644 --- a/src/main/java/com/supermartijn642/fusion/api/model/ModelInstance.java +++ b/src/main/java/com/supermartijn642/fusion/api/model/ModelInstance.java @@ -1,12 +1,13 @@ package com.supermartijn642.fusion.api.model; import com.supermartijn642.fusion.model.ModelInstanceImpl; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.client.renderer.block.model.BlockStateModel; import net.minecraft.client.renderer.item.ItemModel; import net.minecraft.client.resources.model.UnbakedModel; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Nullable; +import org.joml.Matrix4fc; import java.util.Collection; import java.util.List; @@ -40,10 +41,11 @@ static ModelInstance of(ModelType modelType, T modelData){ /** * Converts the model data into a baked item model. * @param context context for baking the model + * @param transformation inherited transformation for this model * @return a baked model * @see ItemModelBakingContext */ - ItemModel bakeItemModel(ItemModelBakingContext context); + ItemModel bakeItemModel(ItemModelBakingContext context, Matrix4fc transformation); /** * Represents the model as a vanilla {@link BlockModel} instance. May be used gather info from other models, such as with the vanilla 'parent' property. diff --git a/src/main/java/com/supermartijn642/fusion/api/model/ModelType.java b/src/main/java/com/supermartijn642/fusion/api/model/ModelType.java index 629533a7..aa428591 100644 --- a/src/main/java/com/supermartijn642/fusion/api/model/ModelType.java +++ b/src/main/java/com/supermartijn642/fusion/api/model/ModelType.java @@ -1,11 +1,12 @@ package com.supermartijn642.fusion.api.model; import com.supermartijn642.fusion.api.util.Serializer; -import net.minecraft.client.renderer.block.model.BlockStateModel; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; import net.minecraft.client.renderer.item.ItemModel; import net.minecraft.client.resources.model.UnbakedModel; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Nullable; +import org.joml.Matrix4fc; import java.util.Collection; import java.util.List; @@ -30,7 +31,7 @@ public interface ModelType extends Serializer { */ BlockStateModel bakeBlockModel(BlockModelBakingContext context, T data); - ItemModel bakeItemModel(ItemModelBakingContext context, T data); + ItemModel bakeItemModel(ItemModelBakingContext context, T data, Matrix4fc transformation); /** * Represents the model as a vanilla {@link UnbakedModel} instance. May be used gather info from other models, such as with the vanilla 'parent' property. diff --git a/src/main/java/com/supermartijn642/fusion/api/model/SpriteIdentifier.java b/src/main/java/com/supermartijn642/fusion/api/model/SpriteIdentifier.java deleted file mode 100644 index b866be9a..00000000 --- a/src/main/java/com/supermartijn642/fusion/api/model/SpriteIdentifier.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.supermartijn642.fusion.api.model; - -import com.supermartijn642.fusion.model.SpriteIdentifierImpl; -import net.minecraft.client.resources.model.Material; -import net.minecraft.resources.Identifier; - -/** - * Created 29/04/2023 by SuperMartijn642 - */ -public interface SpriteIdentifier { - - static SpriteIdentifier of(Identifier atlas, Identifier texture){ - return SpriteIdentifierImpl.of(atlas, texture); - } - - static SpriteIdentifier of(Material material){ - return SpriteIdentifierImpl.of(material); - } - - /** - * @return the identifier for the missing texture sprite in the block atlas - */ - static SpriteIdentifier missing(){ - return SpriteIdentifierImpl.MISSING; - } - - Identifier getAtlas(); - - Identifier getTexture(); - - default Material toMaterial(){ - return new Material(this.getAtlas(), this.getTexture()); - } -} diff --git a/src/main/java/com/supermartijn642/fusion/api/model/data/BaseModelData.java b/src/main/java/com/supermartijn642/fusion/api/model/data/BaseModelData.java index e3a7eab3..cc17a728 100644 --- a/src/main/java/com/supermartijn642/fusion/api/model/data/BaseModelData.java +++ b/src/main/java/com/supermartijn642/fusion/api/model/data/BaseModelData.java @@ -1,6 +1,6 @@ package com.supermartijn642.fusion.api.model.data; -import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.cuboid.CuboidModel; import net.minecraft.resources.Identifier; import java.util.List; @@ -14,7 +14,7 @@ static BaseModelDataBuilder builder(){ return BaseModelDataBuilder.builder(); } - BlockModel getVanillaModel(); + CuboidModel getVanillaModel(); List getParents(); } diff --git a/src/main/java/com/supermartijn642/fusion/api/model/data/VanillaModelDataBuilder.java b/src/main/java/com/supermartijn642/fusion/api/model/data/VanillaModelDataBuilder.java index 98971047..a3035a78 100644 --- a/src/main/java/com/supermartijn642/fusion/api/model/data/VanillaModelDataBuilder.java +++ b/src/main/java/com/supermartijn642/fusion/api/model/data/VanillaModelDataBuilder.java @@ -1,7 +1,8 @@ package com.supermartijn642.fusion.api.model.data; import com.supermartijn642.fusion.model.types.vanilla.VanillaModelDataBuilderImpl; -import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.cuboid.CuboidModel; +import net.minecraft.client.resources.model.sprite.Material; import net.minecraft.resources.Identifier; /** @@ -9,7 +10,7 @@ */ public interface VanillaModelDataBuilder, S> { - static VanillaModelDataBuilder builder(){ + static VanillaModelDataBuilder builder(){ return new VanillaModelDataBuilderImpl(); } @@ -26,7 +27,14 @@ static VanillaModelDataBuilder builder(){ /** * Puts the given texture under the given key. These keys may be used when on faces for elements of this model or its parent's. */ - T texture(String key, Identifier texture); + T texture(String key, Material material); + + /** + * Puts the given texture under the given key. These keys may be used when on faces for elements of this model or its parent's. + */ + default T texture(String key, Identifier texture){ + return texture(key, new Material(texture)); + } S build(); } diff --git a/src/main/java/com/supermartijn642/fusion/api/texture/data/BaseTextureData.java b/src/main/java/com/supermartijn642/fusion/api/texture/data/BaseTextureData.java index 17eabe4c..b3b1ef3c 100644 --- a/src/main/java/com/supermartijn642/fusion/api/texture/data/BaseTextureData.java +++ b/src/main/java/com/supermartijn642/fusion/api/texture/data/BaseTextureData.java @@ -12,28 +12,10 @@ static Builder builder(){ return new BaseTextureDataBuilderImpl(); } - @Nullable - RenderType getRenderType(); - boolean isEmissive(); QuadTinting getTinting(); - enum RenderType { - /** - * Pixels in the texture will be rendered fully opaque. - */ - OPAQUE, - /** - * Every pixel in the texture will be either fully transparent or fully opaque. - */ - CUTOUT, - /** - * Pixels will be rendered with the transparency in the texture. - */ - TRANSLUCENT - } - enum QuadTinting { /** * Provides a grass tint based on the biome the model is in. @@ -50,8 +32,6 @@ enum QuadTinting { } interface Builder, S> { - T renderType(@Nullable RenderType type); - T emissive(boolean emissive); T tinting(@Nullable QuadTinting tinting); diff --git a/src/main/java/com/supermartijn642/fusion/api/texture/data/ConnectingTextureData.java b/src/main/java/com/supermartijn642/fusion/api/texture/data/ConnectingTextureData.java index 9c9c76fb..2a53cd04 100644 --- a/src/main/java/com/supermartijn642/fusion/api/texture/data/ConnectingTextureData.java +++ b/src/main/java/com/supermartijn642/fusion/api/texture/data/ConnectingTextureData.java @@ -1,7 +1,6 @@ package com.supermartijn642.fusion.api.texture.data; import com.supermartijn642.fusion.texture.types.connecting.ConnectingTextureDataBuilderImpl; -import org.jetbrains.annotations.Nullable; /** * Created 23/10/2023 by SuperMartijn642 @@ -23,27 +22,5 @@ interface Builder extends BaseTextureData.Builder * @see ConnectingTextureLayout */ Builder layout(ConnectingTextureLayout layout); - - /** - * @deprecated use {@link #renderType(BaseTextureData.RenderType)} - */ - @Deprecated - Builder renderType(@Nullable RenderType type); - } - - @Deprecated - enum RenderType { - /** - * Pixels in the texture will be rendered fully opaque. - */ - OPAQUE, - /** - * Every pixel in the texture will be either fully transparent or fully opaque. - */ - CUTOUT, - /** - * Pixels will be rendered with the transparency in the texture. - */ - TRANSLUCENT } } diff --git a/src/main/java/com/supermartijn642/fusion/entity/EntityRenderTypeHelper.java b/src/main/java/com/supermartijn642/fusion/entity/EntityRenderTypeHelper.java index 89554af7..704451c8 100644 --- a/src/main/java/com/supermartijn642/fusion/entity/EntityRenderTypeHelper.java +++ b/src/main/java/com/supermartijn642/fusion/entity/EntityRenderTypeHelper.java @@ -25,17 +25,13 @@ public class EntityRenderTypeHelper { renderTypes.put("entity_solid", RenderTypes::entitySolid); renderTypes.put("entity_solid_z_offset_forward", RenderTypes::entitySolidZOffsetForward); renderTypes.put("entity_cutout", RenderTypes::entityCutout); - renderTypes.put("item_entity_translucent_cull", RenderTypes::itemEntityTranslucentCull); - renderTypes.put("entity_smooth_cutout", RenderTypes::entitySmoothCutout); - renderTypes.put("entity_decal", RenderTypes::entityDecal); - renderTypes.put("entity_no_outline", RenderTypes::entityNoOutline); + renderTypes.put("item_entity_translucent_cull", RenderTypes::itemTranslucent); renderTypes.put("entity_shadow", RenderTypes::entityShadow); - renderTypes.put("entity_alpha", RenderTypes::dragonExplosionAlpha); renderTypes.put("eyes", RenderTypes::eyes); RENDER_TYPES_BY_NAME = renderTypes.build(); ImmutableMap.Builder> outlineRenderTypes = ImmutableMap.builder(); - outlineRenderTypes.put("entity_cutout_no_cull", RenderTypes::entityCutoutNoCull); - outlineRenderTypes.put("entity_cutout_no_cull_z_offset", RenderTypes::entityCutoutNoCullZOffset); + outlineRenderTypes.put("entity_cutout_no_cull", RenderTypes::entityCutout); + outlineRenderTypes.put("entity_cutout_no_cull_z_offset", RenderTypes::entityCutoutZOffset); outlineRenderTypes.put("entity_translucent", RenderTypes::entityTranslucent); outlineRenderTypes.put("entity_translucent_emissive", RenderTypes::entityTranslucentEmissive); VAR_OUTLINE_RENDER_TYPES_BY_NAME = outlineRenderTypes.build(); diff --git a/src/main/java/com/supermartijn642/fusion/extensions/BlockModelExtension.java b/src/main/java/com/supermartijn642/fusion/extensions/CuboidModelExtension.java similarity index 86% rename from src/main/java/com/supermartijn642/fusion/extensions/BlockModelExtension.java rename to src/main/java/com/supermartijn642/fusion/extensions/CuboidModelExtension.java index 9fd3cbc0..668ee707 100644 --- a/src/main/java/com/supermartijn642/fusion/extensions/BlockModelExtension.java +++ b/src/main/java/com/supermartijn642/fusion/extensions/CuboidModelExtension.java @@ -5,7 +5,7 @@ /** * Created 30/04/2023 by SuperMartijn642 */ -public interface BlockModelExtension { +public interface CuboidModelExtension { ModelInstance getFusionModel(); diff --git a/src/main/java/com/supermartijn642/fusion/mixin/AtlasConfigMixin.java b/src/main/java/com/supermartijn642/fusion/mixin/AtlasConfigMixin.java index 7e986393..9bcfc2ee 100644 --- a/src/main/java/com/supermartijn642/fusion/mixin/AtlasConfigMixin.java +++ b/src/main/java/com/supermartijn642/fusion/mixin/AtlasConfigMixin.java @@ -1,7 +1,7 @@ package com.supermartijn642.fusion.mixin; import com.supermartijn642.fusion.texture.FusionTextureMetadataSection; -import net.minecraft.client.resources.model.AtlasManager; +import net.minecraft.client.resources.model.sprite.AtlasManager; import net.minecraft.server.packs.metadata.MetadataSectionType; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -17,15 +17,16 @@ public class AtlasConfigMixin { @ModifyVariable( - method = "(Lnet/minecraft/resources/Identifier;Lnet/minecraft/resources/Identifier;ZLjava/util/Set;)V", - at = @At("HEAD"), - ordinal = 0 + method = "(Lnet/minecraft/resources/Identifier;Lnet/minecraft/resources/Identifier;ZLjava/util/Set;)V", + at = @At("HEAD"), + argsOnly = true, + name = "additionalMetadata" ) - private static Set> init(Set> set){ - if(set.isEmpty()) + private static Set> init(Set> additionalMetadata){ + if(additionalMetadata.isEmpty()) return Set.of(FusionTextureMetadataSection.TYPE); - ArrayList> copy = new ArrayList<>(set.size() + 1); - copy.addAll(set); + ArrayList> copy = new ArrayList<>(additionalMetadata.size() + 1); + copy.addAll(additionalMetadata); copy.add(FusionTextureMetadataSection.TYPE); return Set.copyOf(copy); } diff --git a/src/main/java/com/supermartijn642/fusion/mixin/BlockRenderDispatcherMixin.java b/src/main/java/com/supermartijn642/fusion/mixin/BlockRenderDispatcherMixin.java index 9129dae4..e2eba4c4 100644 --- a/src/main/java/com/supermartijn642/fusion/mixin/BlockRenderDispatcherMixin.java +++ b/src/main/java/com/supermartijn642/fusion/mixin/BlockRenderDispatcherMixin.java @@ -1,7 +1,7 @@ package com.supermartijn642.fusion.mixin; import com.supermartijn642.fusion.FusionClient; -import net.minecraft.client.renderer.block.BlockRenderDispatcher; +import net.minecraft.client.renderer.feature.BlockFeatureRenderer; import net.minecraft.util.Unit; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -11,11 +11,11 @@ /** * Created 22/08/2025 by SuperMartijn642 */ -@Mixin(BlockRenderDispatcher.class) +@Mixin(BlockFeatureRenderer.class) public class BlockRenderDispatcherMixin { @Inject( - method = "renderBreakingTexture", + method = "renderBreakingBlockModelSubmits", at = @At("HEAD") ) private void renderBreakingTextureHead(CallbackInfo ci){ @@ -23,7 +23,7 @@ private void renderBreakingTextureHead(CallbackInfo ci){ } @Inject( - method = "renderBreakingTexture", + method = "renderBreakingBlockModelSubmits", at = @At("TAIL") ) private void renderBreakingTextureTail(CallbackInfo ci){ diff --git a/src/main/java/com/supermartijn642/fusion/mixin/BlockModelWrapperMixin.java b/src/main/java/com/supermartijn642/fusion/mixin/CuboidItemModelWrapperMixin.java similarity index 76% rename from src/main/java/com/supermartijn642/fusion/mixin/BlockModelWrapperMixin.java rename to src/main/java/com/supermartijn642/fusion/mixin/CuboidItemModelWrapperMixin.java index e2b80c85..0ebaeca2 100644 --- a/src/main/java/com/supermartijn642/fusion/mixin/BlockModelWrapperMixin.java +++ b/src/main/java/com/supermartijn642/fusion/mixin/CuboidItemModelWrapperMixin.java @@ -2,11 +2,12 @@ import com.supermartijn642.fusion.model.FusionBlockModel; import net.minecraft.client.color.item.ItemTintSource; -import net.minecraft.client.renderer.item.BlockModelWrapper; +import net.minecraft.client.renderer.item.CuboidItemModelWrapper; import net.minecraft.client.renderer.item.ItemModel; import net.minecraft.client.resources.model.ResolvedModel; import net.minecraft.client.resources.model.UnbakedModel; import net.minecraft.resources.Identifier; +import org.joml.Matrix4fc; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -17,24 +18,24 @@ /** * Created 09/04/2025 by SuperMartijn642 */ -@Mixin(BlockModelWrapper.Unbaked.class) -public class BlockModelWrapperMixin { +@Mixin(CuboidItemModelWrapper.Unbaked.class) +public class CuboidItemModelWrapperMixin { @Inject( method = "bake", at = @At("HEAD"), cancellable = true ) - private void bake(ItemModel.BakingContext context, CallbackInfoReturnable ci){ + private void bake(ItemModel.BakingContext context, Matrix4fc transformation, CallbackInfoReturnable ci){ //noinspection DataFlowIssue - BlockModelWrapper.Unbaked unbaked = (BlockModelWrapper.Unbaked)(Object)this; + CuboidItemModelWrapper.Unbaked unbaked = (CuboidItemModelWrapper.Unbaked)(Object)this; Identifier location = unbaked.model(); ResolvedModel wrapper = context.blockModelBaker().getModel(location); if(wrapper != null){ UnbakedModel wrapped = wrapper.wrapped(); if(wrapped instanceof FusionBlockModel fusionBlockModel){ List tintSources = unbaked.tints(); - ItemModel model = fusionBlockModel.bakeItemModel(wrapper, context.blockModelBaker(), tintSources, context.entityModelSet()); + ItemModel model = fusionBlockModel.bakeItemModel(wrapper, context.blockModelBaker(), tintSources, context.entityModelSet(), transformation); ci.setReturnValue(model); } } diff --git a/src/main/java/com/supermartijn642/fusion/mixin/BlockModelMixin.java b/src/main/java/com/supermartijn642/fusion/mixin/CuboidModelMixin.java similarity index 67% rename from src/main/java/com/supermartijn642/fusion/mixin/BlockModelMixin.java rename to src/main/java/com/supermartijn642/fusion/mixin/CuboidModelMixin.java index 45e2e6d0..62ef1fc8 100644 --- a/src/main/java/com/supermartijn642/fusion/mixin/BlockModelMixin.java +++ b/src/main/java/com/supermartijn642/fusion/mixin/CuboidModelMixin.java @@ -1,16 +1,16 @@ package com.supermartijn642.fusion.mixin; import com.supermartijn642.fusion.api.model.ModelInstance; -import com.supermartijn642.fusion.extensions.BlockModelExtension; -import net.minecraft.client.renderer.block.model.BlockModel; +import com.supermartijn642.fusion.extensions.CuboidModelExtension; +import net.minecraft.client.resources.model.cuboid.CuboidModel; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; /** * Created 30/04/2023 by SuperMartijn642 */ -@Mixin(value = BlockModel.class, priority = 900) -public class BlockModelMixin implements BlockModelExtension { +@Mixin(value = CuboidModel.class, priority = 900) +public class CuboidModelMixin implements CuboidModelExtension { @Unique private ModelInstance fusionModel; diff --git a/src/main/java/com/supermartijn642/fusion/mixin/FaceBakeryMixin.java b/src/main/java/com/supermartijn642/fusion/mixin/FaceBakeryMixin.java new file mode 100644 index 00000000..979cafb5 --- /dev/null +++ b/src/main/java/com/supermartijn642/fusion/mixin/FaceBakeryMixin.java @@ -0,0 +1,22 @@ +package com.supermartijn642.fusion.mixin; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.mojang.blaze3d.platform.Transparency; +import com.supermartijn642.fusion.texture.types.base.BaseTextureSprite; +import net.minecraft.client.renderer.texture.SpriteContents; +import net.minecraft.client.resources.model.cuboid.FaceBakery; +import net.minecraft.client.resources.model.sprite.Material; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(FaceBakery.class) +public class FaceBakeryMixin { + @WrapOperation(method = "computeMaterialTransparency", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/texture/SpriteContents;computeTransparency(FFFF)Lcom/mojang/blaze3d/platform/Transparency;")) + private static Transparency computeMaterialTransparency(SpriteContents instance, float u0, float v0, float u1, float v1, Operation original, Material.Baked material){ + if(material.sprite() instanceof BaseTextureSprite sprite){ + return sprite.computeTransparency(u0, v0, u1, v1); + } + return original.call(instance, u0, v0, u1, v1); + } +} diff --git a/src/main/java/com/supermartijn642/fusion/mixin/FusionMixinPlugin.java b/src/main/java/com/supermartijn642/fusion/mixin/FusionMixinPlugin.java index 833f8209..3e4b5350 100644 --- a/src/main/java/com/supermartijn642/fusion/mixin/FusionMixinPlugin.java +++ b/src/main/java/com/supermartijn642/fusion/mixin/FusionMixinPlugin.java @@ -49,7 +49,6 @@ public List getMixins(){ List mixins = new ArrayList<>(); if(this.isSodiumLoaded){ mixins.add("sodium.BlockRendererMixinSodium"); - mixins.add("sodium.ItemRendererMixinSodium"); } return mixins; } diff --git a/src/main/java/com/supermartijn642/fusion/mixin/ItemFeatureRendererMixin.java b/src/main/java/com/supermartijn642/fusion/mixin/ItemFeatureRendererMixin.java new file mode 100644 index 00000000..2064dfbb --- /dev/null +++ b/src/main/java/com/supermartijn642/fusion/mixin/ItemFeatureRendererMixin.java @@ -0,0 +1,32 @@ +package com.supermartijn642.fusion.mixin; + +import com.supermartijn642.fusion.api.texture.data.BaseTextureData; +import com.supermartijn642.fusion.texture.QuadTintingHelper; +import com.supermartijn642.fusion.texture.types.base.BaseTextureSprite; +import net.minecraft.client.renderer.feature.ItemFeatureRenderer; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.geometry.BakedQuad; +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.CallbackInfoReturnable; + +/** + * Created 16/09/2024 by SuperMartijn642 + */ +@Mixin(ItemFeatureRenderer.class) +public class ItemFeatureRendererMixin { + + @Inject(method = "getLayerColorSafe([ILnet/minecraft/client/resources/model/geometry/BakedQuad$MaterialInfo;)I", at = @At("HEAD"), cancellable = true) + private static void getLayerColor(int[] tintLayers, BakedQuad.MaterialInfo material, CallbackInfoReturnable ci){ + // In case texture has a custom tinting set, replace the original tinting + if(material.tintIndex() == 39216){ + TextureAtlasSprite sprite = material.sprite(); + if(sprite instanceof BaseTextureSprite){ + BaseTextureData.QuadTinting tinting = ((BaseTextureSprite)sprite).data().getTinting(); + if(tinting != null) + ci.setReturnValue(QuadTintingHelper.getColor(tinting, null, null, null)); + } + } + } +} diff --git a/src/main/java/com/supermartijn642/fusion/mixin/ItemRendererMixin.java b/src/main/java/com/supermartijn642/fusion/mixin/ItemRendererMixin.java deleted file mode 100644 index 0bb9031b..00000000 --- a/src/main/java/com/supermartijn642/fusion/mixin/ItemRendererMixin.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.supermartijn642.fusion.mixin; - -import com.llamalad7.mixinextras.sugar.Local; -import com.llamalad7.mixinextras.sugar.ref.LocalIntRef; -import com.llamalad7.mixinextras.sugar.ref.LocalRef; -import com.supermartijn642.fusion.api.texture.data.BaseTextureData; -import com.supermartijn642.fusion.texture.QuadTintingHelper; -import com.supermartijn642.fusion.texture.types.base.BaseTextureSprite; -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.renderer.entity.ItemRenderer; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -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; - -/** - * Created 16/09/2024 by SuperMartijn642 - */ -@Mixin(ItemRenderer.class) -public class ItemRendererMixin { - - @Inject( - method = "renderQuadList", - at = @At( - value = "INVOKE_ASSIGN", - target = "Lnet/minecraft/client/renderer/entity/ItemRenderer;getLayerColorSafe([II)I", - shift = At.Shift.AFTER - ) - ) - private static void renderQuadList(CallbackInfo ci, @Local LocalRef quad, @Local(ordinal = 2) LocalIntRef color){ - // In case texture has a custom tinting set, replace the original tinting - if(quad.get().tintIndex() == 39216){ - TextureAtlasSprite sprite = quad.get().sprite(); - if(sprite instanceof BaseTextureSprite){ - BaseTextureData.QuadTinting tinting = ((BaseTextureSprite)sprite).data().getTinting(); - if(tinting != null) - color.set(QuadTintingHelper.getColor(tinting, null, null, null)); - } - } - } -} diff --git a/src/main/java/com/supermartijn642/fusion/mixin/ModelBakeryMixin.java b/src/main/java/com/supermartijn642/fusion/mixin/ModelBakeryMixin.java index 6faeb58a..09639286 100644 --- a/src/main/java/com/supermartijn642/fusion/mixin/ModelBakeryMixin.java +++ b/src/main/java/com/supermartijn642/fusion/mixin/ModelBakeryMixin.java @@ -6,9 +6,9 @@ import net.minecraft.client.model.geom.EntityModelSet; import net.minecraft.client.renderer.PlayerSkinRenderCache; import net.minecraft.client.renderer.item.ItemModel; -import net.minecraft.client.resources.model.MaterialSet; import net.minecraft.client.resources.model.ModelBakery; -import net.minecraft.client.resources.model.SpriteGetter; +import net.minecraft.client.resources.model.sprite.MaterialBaker; +import net.minecraft.client.resources.model.sprite.SpriteGetter; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -29,7 +29,7 @@ public class ModelBakeryMixin { private EntityModelSet entityModelSet; @Final @Shadow - private MaterialSet materials; + private SpriteGetter sprites; @Final @Shadow private PlayerSkinRenderCache playerSkinRenderCache; @@ -38,7 +38,7 @@ public class ModelBakeryMixin { method = "bakeModels", at = @At("RETURN") ) - private CompletableFuture applyBlockModelOverlays(CompletableFuture future, SpriteGetter textureGetter, Executor executor){ + private CompletableFuture applyBlockModelOverlays(CompletableFuture future, MaterialBaker materials, Executor taskExecutor){ // Ignore non-vanilla model bakeries //noinspection ConstantValue,EqualsBetweenInconvertibleTypes if(!this.getClass().equals(ModelBakery.class)) @@ -59,12 +59,12 @@ private CompletableFuture applyBlockModelOverlays(Comp } // Apply Fusion model modifiers - ModelBakery.ModelBakerImpl resolver = ((ModelBakery)(Object)this).new ModelBakerImpl(textureGetter, new ModelBakery.PartCacheImpl(), results.missingModels()); + ModelBakery.ModelBakerImpl resolver = ((ModelBakery)(Object)this).new ModelBakerImpl(materials, new ModelBakery.InternerImpl(), results.missingModels()); BlockModelModifierReloadListener.INSTANCE.applyOverlays(results, resolver); ItemModelModifierReloadListener.INSTANCE.applyPredicateModels(results, new ItemModel.BakingContext( resolver, this.entityModelSet, - this.materials, + this.sprites, this.playerSkinRenderCache, results.missingModels().item(), null diff --git a/src/main/java/com/supermartijn642/fusion/mixin/ModelBlockRendererMixin.java b/src/main/java/com/supermartijn642/fusion/mixin/ModelBlockRendererMixin.java index d6ff20b9..6b7e99c2 100644 --- a/src/main/java/com/supermartijn642/fusion/mixin/ModelBlockRendererMixin.java +++ b/src/main/java/com/supermartijn642/fusion/mixin/ModelBlockRendererMixin.java @@ -1,19 +1,18 @@ package com.supermartijn642.fusion.mixin; -import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.blaze3d.vertex.VertexConsumer; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; import com.supermartijn642.fusion.api.texture.data.BaseTextureData; import com.supermartijn642.fusion.texture.QuadTintingHelper; import com.supermartijn642.fusion.texture.types.base.BaseTextureSprite; +import net.minecraft.client.renderer.block.BlockAndTintGetter; import net.minecraft.client.renderer.block.ModelBlockRenderer; -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.geometry.BakedQuad; import net.minecraft.core.BlockPos; -import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.block.state.BlockState; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.ModifyVariable; /** * Created 07/09/2024 by SuperMartijn642 @@ -21,47 +20,14 @@ @Mixin(ModelBlockRenderer.class) public class ModelBlockRendererMixin { - @ModifyVariable( - method = "putQuadData", - at = @At( - value = "FIELD", - target = "Lnet/minecraft/client/renderer/block/ModelBlockRenderer$CommonRenderStorage;tintCacheValue:I", - shift = At.Shift.AFTER - ), - ordinal = 2 - ) - private int tintQuadCached(int blockColor, BlockAndTintGetter level, BlockState state, BlockPos pos, VertexConsumer vertexConsumer, PoseStack.Pose pose, BakedQuad quad){ - // In case texture has a custom tinting set, replace the original tinting - if(quad.tintIndex() == 39216){ - TextureAtlasSprite sprite = quad.sprite(); - if(sprite instanceof BaseTextureSprite){ - BaseTextureData.QuadTinting tinting = ((BaseTextureSprite)sprite).data().getTinting(); - if(tinting != null) - return QuadTintingHelper.getColor(tinting, state, level, pos); - } + // In case texture has a custom tinting set, replace the original tinting + @WrapOperation(method = "putQuadWithTint", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/block/ModelBlockRenderer;getTintColor(Lnet/minecraft/client/renderer/block/BlockAndTintGetter;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/BlockPos;I)I")) + private int getTintColor(ModelBlockRenderer instance, BlockAndTintGetter level, BlockState state, BlockPos pos, int tintIndex, Operation original, @Local(argsOnly = true, name = "quad") BakedQuad quad){ + if (tintIndex == 39216 && quad.materialInfo().sprite() instanceof BaseTextureSprite sprite){ + BaseTextureData.QuadTinting tinting = sprite.data().getTinting(); + if (tinting != null) + return QuadTintingHelper.getColor(tinting, state, level, pos); } - return blockColor; - } - - @ModifyVariable( - method = "putQuadData", - at = @At( - value = "INVOKE_ASSIGN", - target = "Lnet/minecraft/client/color/block/BlockColors;getColor(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/BlockAndTintGetter;Lnet/minecraft/core/BlockPos;I)I", - shift = At.Shift.AFTER - ), - ordinal = 2 - ) - private int tintQuad(int blockColor, BlockAndTintGetter level, BlockState state, BlockPos pos, VertexConsumer vertexConsumer, PoseStack.Pose pose, BakedQuad quad){ - // In case texture has a custom tinting set, replace the original tinting - if(quad.tintIndex() == 39216){ - TextureAtlasSprite sprite = quad.sprite(); - if(sprite instanceof BaseTextureSprite){ - BaseTextureData.QuadTinting tinting = ((BaseTextureSprite)sprite).data().getTinting(); - if(tinting != null) - return QuadTintingHelper.getColor(tinting, state, level, pos); - } - } - return blockColor; + return original.call(instance, level, state, pos, tintIndex); } } diff --git a/src/main/java/com/supermartijn642/fusion/mixin/ModelManagerMixin.java b/src/main/java/com/supermartijn642/fusion/mixin/ModelManagerMixin.java index 856cd9a1..ef33c008 100644 --- a/src/main/java/com/supermartijn642/fusion/mixin/ModelManagerMixin.java +++ b/src/main/java/com/supermartijn642/fusion/mixin/ModelManagerMixin.java @@ -23,7 +23,7 @@ public class ModelManagerMixin { @Inject( - method = "lambda$loadBlockModels$5(Lnet/minecraft/server/packs/resources/ResourceManager;)Ljava/util/Map;", + method = "lambda$loadBlockModels$0", at = @At("HEAD") ) private static void reloadModelModifiers(ResourceManager resourceManager, CallbackInfoReturnable> ci){ @@ -47,7 +47,7 @@ private static void registerBlockModelOverlays(CallbackInfoReturnable getNamespaces(Set namespaces, PackType type){ @@ -116,7 +116,7 @@ private Set getNamespaces(Set namespaces, PackType type){ } @ModifyVariable( - method = "listResources", + method = "listResources(Lnet/minecraft/server/packs/PackType;Ljava/lang/String;Ljava/lang/String;Lnet/minecraft/server/packs/PackResources$ResourceOutput;)V", at = @At("HEAD"), ordinal = 0 ) diff --git a/src/main/java/com/supermartijn642/fusion/mixin/SingleVariantMixin.java b/src/main/java/com/supermartijn642/fusion/mixin/SingleVariantMixin.java index 1b4a7281..6e6e210a 100644 --- a/src/main/java/com/supermartijn642/fusion/mixin/SingleVariantMixin.java +++ b/src/main/java/com/supermartijn642/fusion/mixin/SingleVariantMixin.java @@ -1,9 +1,9 @@ package com.supermartijn642.fusion.mixin; import com.supermartijn642.fusion.model.FusionBlockModel; -import net.minecraft.client.renderer.block.model.BlockStateModel; -import net.minecraft.client.renderer.block.model.SingleVariant; -import net.minecraft.client.renderer.block.model.Variant; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; +import net.minecraft.client.renderer.block.dispatch.SingleVariant; +import net.minecraft.client.renderer.block.dispatch.Variant; import net.minecraft.client.resources.model.ModelBaker; import net.minecraft.client.resources.model.ResolvedModel; import net.minecraft.client.resources.model.UnbakedModel; diff --git a/src/main/java/com/supermartijn642/fusion/mixin/sodium/ItemRendererMixinSodium.java b/src/main/java/com/supermartijn642/fusion/mixin/sodium/ItemRendererMixinSodium.java deleted file mode 100644 index 2e6a7c86..00000000 --- a/src/main/java/com/supermartijn642/fusion/mixin/sodium/ItemRendererMixinSodium.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.supermartijn642.fusion.mixin.sodium; - -import com.mojang.blaze3d.vertex.PoseStack; -import com.supermartijn642.fusion.api.texture.data.BaseTextureData; -import com.supermartijn642.fusion.texture.QuadTintingHelper; -import com.supermartijn642.fusion.texture.types.base.BaseTextureSprite; -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.renderer.entity.ItemRenderer; -import net.minecraft.client.renderer.item.ItemStackRenderState; -import net.minecraft.client.renderer.rendertype.RenderType; -import net.minecraft.world.item.ItemDisplayContext; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; -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.CallbackInfoReturnable; - -import java.util.Iterator; -import java.util.List; - -/** - * Created 20/12/2025 by SuperMartijn642 - */ -@Mixin(ItemRenderer.class) -public class ItemRendererMixinSodium { - - /* - The goal is to overwrite the quad tinting that normally occurs in ItemRenderer#renderQuadList. - Sodium intercepts #renderQuadList in ItemRenderer#renderItem with a mixin and applies the quad - tinting themselves in their mixin. - - Fusion needs the sprite and hence the quad as context for the tinting. - - Sodium calls #getLayerColorSafe once per quad, so we capture an iterator for the list of quads - in #renderItem and then iterate through them whenever #getLayerColorSafe is called. - Then from #getLayerColorSafe we return Fusion's tinting. - */ - - @Unique - private static final ThreadLocal> lastSubmittedQuads = new ThreadLocal<>(); - - @Inject( - method = "renderItem", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/client/renderer/entity/ItemRenderer;renderQuadList(Lcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;Ljava/util/List;[III)V", - shift = At.Shift.BEFORE - ) - ) - private static void captureQuads(ItemDisplayContext displayContext, PoseStack poseStack, MultiBufferSource bufferSource, int combinedLight, int combinedOverlay, int[] colors, List quads, RenderType renderType, ItemStackRenderState.FoilType foilType, CallbackInfo ci){ - for(BakedQuad quad : quads){ - //noinspection resource - if(quad.tintIndex() == 39216 && quad.sprite() instanceof BaseTextureSprite){ - lastSubmittedQuads.set(quads.iterator()); - break; - } - } - } - - @Inject( - method = "getLayerColorSafe", - at = @At("HEAD"), - cancellable = true - ) - private static void overwriteTinting(int[] colors, int tintIndex, CallbackInfoReturnable ci){ - // In case texture has a custom tinting set, replace the original tinting - if(tintIndex != 39216) - return; - - // Find the relevant quad - Iterator quads = lastSubmittedQuads.get(); - if(quads == null) - return; - BaseTextureSprite sprite = null; - while(quads.hasNext()){ - BakedQuad quad = quads.next(); - //noinspection resource - if(quad.tintIndex() == 39216 && quad.sprite() instanceof BaseTextureSprite s){ - sprite = s; - break; - } - } - if(sprite == null) - return; - - // Calculate tinting - BaseTextureData.QuadTinting tinting = sprite.data().getTinting(); - if(tinting != null) - ci.setReturnValue(QuadTintingHelper.getColor(tinting, null, null, null)); - } - - @Inject( - method = "renderItem", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/client/renderer/entity/ItemRenderer;renderQuadList(Lcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;Ljava/util/List;[III)V", - shift = At.Shift.AFTER - ) - ) - private static void clearQuads(CallbackInfo ci){ - lastSubmittedQuads.remove(); - } -} diff --git a/src/main/java/com/supermartijn642/fusion/model/BlockModelBakingContextImpl.java b/src/main/java/com/supermartijn642/fusion/model/BlockModelBakingContextImpl.java index 3480da77..5fe01aee 100644 --- a/src/main/java/com/supermartijn642/fusion/model/BlockModelBakingContextImpl.java +++ b/src/main/java/com/supermartijn642/fusion/model/BlockModelBakingContextImpl.java @@ -2,10 +2,12 @@ import com.supermartijn642.fusion.api.model.BlockModelBakingContext; import com.supermartijn642.fusion.api.model.ModelInstance; -import com.supermartijn642.fusion.api.model.SpriteIdentifier; -import net.minecraft.client.renderer.block.model.ItemTransforms; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.block.dispatch.ModelState; +import net.minecraft.client.renderer.texture.MissingTextureAtlasSprite; import net.minecraft.client.resources.model.*; +import net.minecraft.client.resources.model.cuboid.ItemTransforms; +import net.minecraft.client.resources.model.geometry.UnbakedGeometry; +import net.minecraft.client.resources.model.sprite.Material; import net.minecraft.resources.Identifier; import net.minecraft.util.context.ContextMap; @@ -16,9 +18,10 @@ * Created 27/04/2023 by SuperMartijn642 */ public class BlockModelBakingContextImpl implements BlockModelBakingContext { + private static final Material MISSING_MATERIAL = new Material(MissingTextureAtlasSprite.getLocation()); private final ModelBaker modelBaker; - private final Function spriteGetter; + private final Function materialBaker; private final ModelState modelState; private final Identifier modelIdentifier; private final Map dependencies; @@ -29,9 +32,9 @@ public class BlockModelBakingContextImpl implements BlockModelBakingContext { private final UnbakedGeometry topLevelGeometry; private final ContextMap neoforgeAdditionalProperties; - public BlockModelBakingContextImpl(ModelBaker modelBaker, Function spriteGetter, ModelState modelState, Identifier modelIdentifier, Map dependencies, Map topLevelTextureReferences, boolean topLevelAmbientOcclusion, boolean topLevelUseBlockLighting, ItemTransforms topLevelItemTransforms, UnbakedGeometry topLevelGeometry, ContextMap neoforgeAdditionalProperties){ + public BlockModelBakingContextImpl(ModelBaker modelBaker, Function materialBaker, ModelState modelState, Identifier modelIdentifier, Map dependencies, Map topLevelTextureReferences, boolean topLevelAmbientOcclusion, boolean topLevelUseBlockLighting, ItemTransforms topLevelItemTransforms, UnbakedGeometry topLevelGeometry, ContextMap neoforgeAdditionalProperties){ this.modelBaker = modelBaker; - this.spriteGetter = spriteGetter; + this.materialBaker = materialBaker; this.modelState = modelState; this.modelIdentifier = modelIdentifier; this.dependencies = dependencies; @@ -49,13 +52,13 @@ public ModelBaker getModelBaker(){ } @Override - public TextureAtlasSprite getTexture(SpriteIdentifier identifier){ - return this.spriteGetter.apply(identifier.toMaterial()); + public Material.Baked bakeMaterial(Material material){ + return this.materialBaker.apply(material); } @Override - public TextureAtlasSprite getTexture(Identifier atlas, Identifier texture){ - return this.spriteGetter.apply(new Material(atlas, texture)); + public Material missingMaterial(){ + return MISSING_MATERIAL; } @Override diff --git a/src/main/java/com/supermartijn642/fusion/model/FusionBlockModel.java b/src/main/java/com/supermartijn642/fusion/model/FusionBlockModel.java index dad2864a..3f0949d5 100644 --- a/src/main/java/com/supermartijn642/fusion/model/FusionBlockModel.java +++ b/src/main/java/com/supermartijn642/fusion/model/FusionBlockModel.java @@ -4,19 +4,23 @@ import com.supermartijn642.fusion.api.model.DefaultModelTypes; import com.supermartijn642.fusion.api.model.ItemModelBakingContext; import com.supermartijn642.fusion.api.model.ModelInstance; -import com.supermartijn642.fusion.extensions.BlockModelExtension; +import com.supermartijn642.fusion.extensions.CuboidModelExtension; import com.supermartijn642.fusion.util.IdentifierUtil; import net.minecraft.client.color.item.ItemTintSource; import net.minecraft.client.model.geom.EntityModelSet; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.client.renderer.block.model.BlockStateModel; -import net.minecraft.client.renderer.block.model.ItemTransforms; -import net.minecraft.client.renderer.block.model.TextureSlots; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; +import net.minecraft.client.renderer.block.dispatch.ModelState; import net.minecraft.client.renderer.item.ItemModel; import net.minecraft.client.resources.model.*; +import net.minecraft.client.resources.model.cuboid.CuboidModel; +import net.minecraft.client.resources.model.cuboid.ItemTransforms; +import net.minecraft.client.resources.model.cuboid.MissingCuboidModel; +import net.minecraft.client.resources.model.geometry.UnbakedGeometry; +import net.minecraft.client.resources.model.sprite.TextureSlots; import net.minecraft.resources.Identifier; import net.minecraft.util.context.ContextMap; import org.jetbrains.annotations.Nullable; +import org.joml.Matrix4fc; import java.util.*; @@ -45,7 +49,7 @@ public BlockStateModel bakeBlockModel(ResolvedModel wrapper, ModelBaker modelBak // Create baking context BlockModelBakingContext context = new BlockModelBakingContextImpl( modelBakery, - material -> modelBakery.sprites().get(material, wrapper), + material -> modelBakery.materials().get(material, wrapper), modelState, this.name, this.resolvedDependencies, @@ -64,12 +68,12 @@ public BlockStateModel bakeBlockModel(ResolvedModel wrapper, ModelBaker modelBak } } - public ItemModel bakeItemModel(ResolvedModel wrapper, ModelBaker modelBakery, List tintSources, EntityModelSet entityModelSet){ + public ItemModel bakeItemModel(ResolvedModel wrapper, ModelBaker modelBakery, List tintSources, EntityModelSet entityModelSet, Matrix4fc transformation){ this.resolveDependencies(modelBakery); // Create baking context ItemModelBakingContext context = new ItemModelBakingContextImpl( modelBakery, - material -> modelBakery.sprites().get(material, wrapper), + material -> modelBakery.materials().get(material, wrapper), this.name, this.resolvedDependencies, wrapper.getTopTextureSlots().resolvedValues, @@ -83,7 +87,7 @@ public ItemModel bakeItemModel(ResolvedModel wrapper, ModelBaker modelBakery, Li ); // Let the custom model handle the actual baking try{ - return this.model.bakeItemModel(context); + return this.model.bakeItemModel(context, transformation); }catch(Exception e){ throw new RuntimeException("Encountered an exception while baking item model of type '" + ModelTypeRegistryImpl.getIdentifier(this.model.getModelType()) + "' for '" + this.name + "'!", e); } @@ -121,7 +125,7 @@ private Map resolveDependencies(ModelBaker modelBaker){ } } // Always add missing model - this.resolvedDependencies.put(MissingBlockModel.LOCATION, modelBaker.getModel(MissingBlockModel.LOCATION).wrapped()); + this.resolvedDependencies.put(MissingCuboidModel.LOCATION, modelBaker.getModel(MissingCuboidModel.LOCATION).wrapped()); return this.resolvedDependencies; } @@ -172,11 +176,11 @@ public void fillAdditionalProperties(ContextMap.Builder builder){ public static ModelInstance getModelInstance(UnbakedModel model){ if(model instanceof FusionBlockModel) return ((FusionBlockModel)model).model; - if(model instanceof BlockModel){ - ModelInstance modelInstance = ((BlockModelExtension)model).getFusionModel(); + if(model instanceof CuboidModel){ + ModelInstance modelInstance = ((CuboidModelExtension)model).getFusionModel(); if(modelInstance == null){ - modelInstance = new ModelInstanceImpl<>(DefaultModelTypes.VANILLA, (BlockModel)model); - ((BlockModelExtension)model).setFusionModel(modelInstance); + modelInstance = new ModelInstanceImpl<>(DefaultModelTypes.VANILLA, (CuboidModel)model); + ((CuboidModelExtension)model).setFusionModel(modelInstance); } return modelInstance; } diff --git a/src/main/java/com/supermartijn642/fusion/model/ItemModelBakingContextImpl.java b/src/main/java/com/supermartijn642/fusion/model/ItemModelBakingContextImpl.java index a5085231..f82b37a1 100644 --- a/src/main/java/com/supermartijn642/fusion/model/ItemModelBakingContextImpl.java +++ b/src/main/java/com/supermartijn642/fusion/model/ItemModelBakingContextImpl.java @@ -3,9 +3,11 @@ import com.supermartijn642.fusion.api.model.ItemModelBakingContext; import net.minecraft.client.color.item.ItemTintSource; import net.minecraft.client.model.geom.EntityModelSet; -import net.minecraft.client.renderer.block.model.ItemTransforms; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.block.dispatch.BlockModelRotation; import net.minecraft.client.resources.model.*; +import net.minecraft.client.resources.model.cuboid.ItemTransforms; +import net.minecraft.client.resources.model.geometry.UnbakedGeometry; +import net.minecraft.client.resources.model.sprite.Material; import net.minecraft.resources.Identifier; import net.minecraft.util.context.ContextMap; @@ -21,8 +23,8 @@ public class ItemModelBakingContextImpl extends BlockModelBakingContextImpl impl private final List tintSources; private final EntityModelSet entityModelSet; - public ItemModelBakingContextImpl(ModelBaker modelBaker, Function spriteGetter, Identifier modelIdentifier, Map dependencies, Map topLevelTextureReferences, boolean topLevelAmbientOcclusion, boolean topLevelUseBlockLighting, ItemTransforms topLevelItemTransforms, UnbakedGeometry topLevelGeometry, ContextMap neoforgeAdditionalProperties, List tintSources, EntityModelSet entityModelSet){ - super(modelBaker, spriteGetter, BlockModelRotation.IDENTITY, modelIdentifier, dependencies, topLevelTextureReferences, topLevelAmbientOcclusion, topLevelUseBlockLighting, topLevelItemTransforms, topLevelGeometry, neoforgeAdditionalProperties); + public ItemModelBakingContextImpl(ModelBaker modelBaker, Function materialBaker, Identifier modelIdentifier, Map dependencies, Map topLevelTextureReferences, boolean topLevelAmbientOcclusion, boolean topLevelUseBlockLighting, ItemTransforms topLevelItemTransforms, UnbakedGeometry topLevelGeometry, ContextMap neoforgeAdditionalProperties, List tintSources, EntityModelSet entityModelSet){ + super(modelBaker, materialBaker, BlockModelRotation.IDENTITY, modelIdentifier, dependencies, topLevelTextureReferences, topLevelAmbientOcclusion, topLevelUseBlockLighting, topLevelItemTransforms, topLevelGeometry, neoforgeAdditionalProperties); this.tintSources = tintSources; this.entityModelSet = entityModelSet; } diff --git a/src/main/java/com/supermartijn642/fusion/model/ModelInstanceImpl.java b/src/main/java/com/supermartijn642/fusion/model/ModelInstanceImpl.java index dcbbe6c7..b1567700 100644 --- a/src/main/java/com/supermartijn642/fusion/model/ModelInstanceImpl.java +++ b/src/main/java/com/supermartijn642/fusion/model/ModelInstanceImpl.java @@ -4,11 +4,12 @@ import com.supermartijn642.fusion.api.model.ItemModelBakingContext; import com.supermartijn642.fusion.api.model.ModelInstance; import com.supermartijn642.fusion.api.model.ModelType; -import net.minecraft.client.renderer.block.model.BlockStateModel; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; import net.minecraft.client.renderer.item.ItemModel; import net.minecraft.client.resources.model.UnbakedModel; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Nullable; +import org.joml.Matrix4fc; import java.util.Collection; import java.util.List; @@ -52,8 +53,8 @@ public BlockStateModel bakeBlockModel(BlockModelBakingContext context){ } @Override - public ItemModel bakeItemModel(ItemModelBakingContext context){ - return this.modelType.bakeItemModel(context, this.modelData); + public ItemModel bakeItemModel(ItemModelBakingContext context, Matrix4fc transformation){ + return this.modelType.bakeItemModel(context, this.modelData, transformation); } @Override diff --git a/src/main/java/com/supermartijn642/fusion/model/MutableQuad.java b/src/main/java/com/supermartijn642/fusion/model/MutableQuad.java index 5ff5d862..e8e2f578 100644 --- a/src/main/java/com/supermartijn642/fusion/model/MutableQuad.java +++ b/src/main/java/com/supermartijn642/fusion/model/MutableQuad.java @@ -1,12 +1,13 @@ package com.supermartijn642.fusion.model; import net.minecraft.client.model.geom.builders.UVPair; -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.ModelBaker; +import net.minecraft.client.resources.model.geometry.BakedQuad; import net.minecraft.core.Direction; import net.neoforged.neoforge.client.model.quad.BakedColors; import net.neoforged.neoforge.client.model.quad.BakedNormals; import org.joml.Vector3f; +import org.jspecify.annotations.Nullable; /** * Created 11/09/2024 by SuperMartijn642 @@ -15,15 +16,14 @@ public class MutableQuad { private final Vector3f[] positions = new Vector3f[4]; private final long[] uvs = new long[4]; - private int tintIndex; private Direction lightFace; - private TextureAtlasSprite sprite; - private boolean shade; - private int lightEmission; + private BakedQuad.MaterialInfo oldMaterialInfo; private boolean hasAmbientOcclusion; private BakedNormals bakedNormals = BakedNormals.UNSPECIFIED; private BakedColors bakedColors = BakedColors.DEFAULT; private boolean emissive = false; + private int tintIndex; + private boolean changedMaterialInfo; public MutableQuad(){ for(int i = 0; i < 4; i++) @@ -35,23 +35,28 @@ public void fillFromBakedQuad(BakedQuad quad){ this.positions[i].set(quad.position(i)); this.uvs[i] = quad.packedUV(i); } - this.tintIndex = quad.tintIndex(); this.lightFace = quad.direction(); - this.sprite = quad.sprite(); - this.shade = quad.shade(); - this.lightEmission = quad.lightEmission(); - this.hasAmbientOcclusion = quad.hasAmbientOcclusion(); + this.oldMaterialInfo = quad.materialInfo(); + this.hasAmbientOcclusion = quad.materialInfo().ambientOcclusion(); this.bakedNormals = quad.bakedNormals(); this.bakedColors = quad.bakedColors(); this.emissive = false; + this.tintIndex = quad.materialInfo().tintIndex(); } public void emissive(boolean emissive){ this.emissive = emissive; + this.changedMaterialInfo = true; } public void ambientOcclusion(boolean ambientOcclusion){ this.hasAmbientOcclusion = ambientOcclusion; + this.changedMaterialInfo = true; + } + + public void tintIndex(int tintIndex){ + this.tintIndex = tintIndex; + this.changedMaterialInfo = true; } public void uv(int vertexIndex, float u, float v){ @@ -82,18 +87,29 @@ public float z(int vertexIndex){ return this.positions[vertexIndex].z(); } - public BakedQuad toBakedQuad(){ + public BakedQuad toBakedQuad(ModelBaker.@Nullable Interner interner){ + BakedQuad.MaterialInfo materialInfo; + if(this.changedMaterialInfo){ + materialInfo = new BakedQuad.MaterialInfo( + this.oldMaterialInfo.sprite(), + this.oldMaterialInfo.layer(), + this.oldMaterialInfo.itemRenderType(), + this.tintIndex, + this.oldMaterialInfo.shade(), + this.emissive ? 15 : this.oldMaterialInfo.lightEmission(), + this.hasAmbientOcclusion + ); + materialInfo = interner != null ? interner.materialInfo(materialInfo) : materialInfo; + }else{ + materialInfo = this.oldMaterialInfo; + } return new BakedQuad( new Vector3f(this.positions[0]), new Vector3f(this.positions[1]), new Vector3f(this.positions[2]), new Vector3f(this.positions[3]), this.uvs[0], this.uvs[1], this.uvs[2], this.uvs[3], - this.tintIndex, this.lightFace, - this.sprite, - this.shade, - this.emissive ? 15 : this.lightEmission, + materialInfo, this.bakedNormals, - this.bakedColors, - this.hasAmbientOcclusion + this.bakedColors ); } } diff --git a/src/main/java/com/supermartijn642/fusion/model/SpriteIdentifierImpl.java b/src/main/java/com/supermartijn642/fusion/model/SpriteIdentifierImpl.java deleted file mode 100644 index dac15bb4..00000000 --- a/src/main/java/com/supermartijn642/fusion/model/SpriteIdentifierImpl.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.supermartijn642.fusion.model; - -import com.supermartijn642.fusion.api.model.SpriteIdentifier; -import com.supermartijn642.fusion.util.TextureAtlases; -import net.minecraft.client.renderer.texture.MissingTextureAtlasSprite; -import net.minecraft.client.resources.model.Material; -import net.minecraft.resources.Identifier; - -/** - * Created 30/04/2023 by SuperMartijn642 - */ -public class SpriteIdentifierImpl implements SpriteIdentifier { - - public static final SpriteIdentifier MISSING = of(TextureAtlases.getBlocks(), MissingTextureAtlasSprite.getLocation()); - - public static SpriteIdentifier of(Identifier atlas, Identifier texture){ - return new SpriteIdentifierImpl(atlas, texture); - } - - public static SpriteIdentifier of(Material material){ - return new SpriteIdentifierImpl(material); - } - - private final Identifier atlas, texture; - private Material material; - - private SpriteIdentifierImpl(Identifier atlas, Identifier texture){ - this.atlas = atlas; - this.texture = texture; - } - - private SpriteIdentifierImpl(Material material){ - this(material.atlasLocation(), material.texture()); - this.material = material; - } - - @Override - public Identifier getAtlas(){ - return this.atlas; - } - - @Override - public Identifier getTexture(){ - return this.texture; - } - - @Override - public Material toMaterial(){ - return this.material == null ? (this.material = SpriteIdentifier.super.toMaterial()) : this.material; - } - - @Override - public final boolean equals(Object o){ - if(!(o instanceof SpriteIdentifierImpl that)) return false; - - return this.atlas.equals(that.atlas) && this.texture.equals(that.texture); - } - - @Override - public int hashCode(){ - int result = this.atlas.hashCode(); - result = 31 * result + this.texture.hashCode(); - return result; - } -} diff --git a/src/main/java/com/supermartijn642/fusion/model/WrappedBakedModel.java b/src/main/java/com/supermartijn642/fusion/model/WrappedBakedModel.java index 20dcbc76..fdf1529f 100644 --- a/src/main/java/com/supermartijn642/fusion/model/WrappedBakedModel.java +++ b/src/main/java/com/supermartijn642/fusion/model/WrappedBakedModel.java @@ -1,11 +1,12 @@ package com.supermartijn642.fusion.model; -import net.minecraft.client.renderer.block.model.BlockModelPart; -import net.minecraft.client.renderer.block.model.BlockStateModel; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.block.BlockAndTintGetter; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; +import net.minecraft.client.renderer.block.dispatch.BlockStateModelPart; +import net.minecraft.client.resources.model.geometry.BakedQuad; +import net.minecraft.client.resources.model.sprite.Material; import net.minecraft.core.BlockPos; import net.minecraft.util.RandomSource; -import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.block.state.BlockState; import org.jetbrains.annotations.Nullable; @@ -23,18 +24,18 @@ public WrappedBakedModel(BlockStateModel original){ } @Override - public List collectParts(RandomSource random){ - return this.original.collectParts(random); + public void collectParts(RandomSource random, List parts){ + this.original.collectParts(random, parts); } @Override - public void collectParts(RandomSource random, List parts){ - this.original.collectParts(random, parts); + public Material.Baked particleMaterial(){ + return this.original.particleMaterial(); } @Override - public TextureAtlasSprite particleIcon(){ - return this.original.particleIcon(); + public @BakedQuad.MaterialFlags int materialFlags(){ + return this.original.materialFlags(); } @Override @@ -43,17 +44,17 @@ public TextureAtlasSprite particleIcon(){ } @Override - public void collectParts(BlockAndTintGetter level, BlockPos pos, BlockState state, RandomSource random, List parts){ + public void collectParts(BlockAndTintGetter level, BlockPos pos, BlockState state, RandomSource random, List parts){ this.original.collectParts(level, pos, state, random, parts); } @Override - public List collectParts(BlockAndTintGetter level, BlockPos pos, BlockState state, RandomSource random){ - return this.original.collectParts(level, pos, state, random); + public Material.Baked particleMaterial(BlockAndTintGetter level, BlockPos pos, BlockState state){ + return this.original.particleMaterial(level, pos, state); } @Override - public TextureAtlasSprite particleIcon(BlockAndTintGetter level, BlockPos pos, BlockState state){ - return this.original.particleIcon(level, pos, state); + public @BakedQuad.MaterialFlags int materialFlags(BlockAndTintGetter level, BlockPos pos, BlockState state){ + return this.original.materialFlags(level, pos, state); } } diff --git a/src/main/java/com/supermartijn642/fusion/model/modifiers/block/BlockModelModifierBakedModel.java b/src/main/java/com/supermartijn642/fusion/model/modifiers/block/BlockModelModifierBakedModel.java index 6b41f2d3..d2d7eb58 100644 --- a/src/main/java/com/supermartijn642/fusion/model/modifiers/block/BlockModelModifierBakedModel.java +++ b/src/main/java/com/supermartijn642/fusion/model/modifiers/block/BlockModelModifierBakedModel.java @@ -1,12 +1,13 @@ package com.supermartijn642.fusion.model.modifiers.block; import com.supermartijn642.fusion.FusionClient; -import net.minecraft.client.renderer.block.model.BlockModelPart; -import net.minecraft.client.renderer.block.model.BlockStateModel; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.block.BlockAndTintGetter; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; +import net.minecraft.client.renderer.block.dispatch.BlockStateModelPart; +import net.minecraft.client.resources.model.geometry.BakedQuad; +import net.minecraft.client.resources.model.sprite.Material; import net.minecraft.core.BlockPos; import net.minecraft.util.RandomSource; -import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.block.state.BlockState; import org.jetbrains.annotations.Nullable; @@ -31,7 +32,7 @@ public BlockModelModifierBakedModel(BlockStateModel original, List parts){ + public void collectParts(BlockAndTintGetter level, BlockPos pos, BlockState state, RandomSource random, List parts){ if(!this.showBreakingOverlay && FusionClient.IS_RENDERING_BREAKING_OVERLAY.get() != null){ this.original.collectParts(level, pos, state, random, parts); return; @@ -50,7 +51,7 @@ public void collectParts(BlockAndTintGetter level, BlockPos pos, BlockState stat } @Override - public void collectParts(RandomSource random, List parts){ + public void collectParts(RandomSource random, List parts){ if(!this.showBreakingOverlay && FusionClient.IS_RENDERING_BREAKING_OVERLAY.get() != null){ //noinspection deprecation this.original.collectParts(random, parts); @@ -61,12 +62,22 @@ public void collectParts(RandomSource random, List parts){ } @Override - public TextureAtlasSprite particleIcon(){ - return this.original.particleIcon(); + public Material.Baked particleMaterial(){ + return this.original.particleMaterial(); } @Override - public TextureAtlasSprite particleIcon(BlockAndTintGetter level, BlockPos pos, BlockState state){ - return this.original.particleIcon(level, pos, state); + public Material.Baked particleMaterial(BlockAndTintGetter level, BlockPos pos, BlockState state){ + return this.original.particleMaterial(level, pos, state); + } + + @Override + public @BakedQuad.MaterialFlags int materialFlags(){ + return this.original.materialFlags(); + } + + @Override + public @BakedQuad.MaterialFlags int materialFlags(BlockAndTintGetter level, BlockPos pos, BlockState state){ + return this.original.materialFlags(level, pos, state); } } diff --git a/src/main/java/com/supermartijn642/fusion/model/modifiers/block/BlockModelModifierReloadListener.java b/src/main/java/com/supermartijn642/fusion/model/modifiers/block/BlockModelModifierReloadListener.java index edde6536..43336d90 100644 --- a/src/main/java/com/supermartijn642/fusion/model/modifiers/block/BlockModelModifierReloadListener.java +++ b/src/main/java/com/supermartijn642/fusion/model/modifiers/block/BlockModelModifierReloadListener.java @@ -12,9 +12,9 @@ import com.mojang.serialization.JsonOps; import com.supermartijn642.fusion.FusionClient; import com.supermartijn642.fusion.util.IdentifierUtil; -import net.minecraft.client.renderer.block.model.BlockStateModel; -import net.minecraft.client.renderer.block.model.SingleVariant; -import net.minecraft.client.renderer.block.model.Variant; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; +import net.minecraft.client.renderer.block.dispatch.SingleVariant; +import net.minecraft.client.renderer.block.dispatch.Variant; import net.minecraft.client.resources.model.ModelBakery; import net.minecraft.client.resources.model.ResolvableModel; import net.minecraft.core.registries.BuiltInRegistries; diff --git a/src/main/java/com/supermartijn642/fusion/model/modifiers/block/PaneCullingBakedModel.java b/src/main/java/com/supermartijn642/fusion/model/modifiers/block/PaneCullingBakedModel.java index 1013eb18..a3484787 100644 --- a/src/main/java/com/supermartijn642/fusion/model/modifiers/block/PaneCullingBakedModel.java +++ b/src/main/java/com/supermartijn642/fusion/model/modifiers/block/PaneCullingBakedModel.java @@ -3,16 +3,15 @@ import com.supermartijn642.fusion.api.util.Pair; import com.supermartijn642.fusion.model.MutableQuad; import com.supermartijn642.fusion.model.WrappedBakedModel; -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.renderer.block.model.BlockModelPart; -import net.minecraft.client.renderer.block.model.BlockStateModel; -import net.minecraft.client.renderer.chunk.ChunkSectionLayer; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.block.BlockAndTintGetter; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; +import net.minecraft.client.renderer.block.dispatch.BlockStateModelPart; +import net.minecraft.client.resources.model.geometry.BakedQuad; +import net.minecraft.client.resources.model.sprite.Material; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.util.RandomSource; import net.minecraft.util.TriState; -import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.BooleanProperty; @@ -42,14 +41,14 @@ public PaneCullingBakedModel(BlockStateModel original){ } @Override - public void collectParts(BlockAndTintGetter level, BlockPos pos, BlockState state, RandomSource random, List parts){ + public void collectParts(BlockAndTintGetter level, BlockPos pos, BlockState state, RandomSource random, List parts){ if(state == null){ - parts.addAll(super.collectParts(level, pos, state, random)); + super.collectParts(level, pos, state, random, parts); return; } // If state has no side properties, then there's nothing to be culled if(!state.hasProperty(BlockStateProperties.NORTH) && !state.hasProperty(BlockStateProperties.SOUTH) && !state.hasProperty(BlockStateProperties.WEST) && !state.hasProperty(BlockStateProperties.EAST)){ - parts.addAll(super.collectParts(level, pos, state, random)); + super.collectParts(level, pos, state, random, parts); return; } @@ -61,17 +60,17 @@ public void collectParts(BlockAndTintGetter level, BlockPos pos, BlockState stat if(below.getBlock() != state.getBlock()) below = null; if(above == null && below == null){ - parts.addAll(super.collectParts(level, pos, state, random)); + super.collectParts(level, pos, state, random, parts); return; } - Pair neighbors = Pair.of(above, below); + super.collectParts(level, pos, state, random, parts); // Wrap the parts with a quad filter - for(BlockModelPart part : super.collectParts(level, pos, state, random)) - parts.add(new FilteringModelPart(part, neighbors)); + Pair neighbors = Pair.of(above, below); + parts.replaceAll(part -> new FilteringModelPart(part, neighbors)); } - private List getQuads(BlockModelPart part, Pair neighbors, @Nullable Direction cullDirection){ + private List getQuads(BlockStateModelPart part, Pair neighbors, @Nullable Direction cullDirection){ // Filter out certain quads List quads = part.getQuads(cullDirection); List culledQuads = new ArrayList<>(quads.size()); @@ -114,12 +113,12 @@ private boolean filterQuad(BakedQuad bakedQuad, BlockState stateAbove, BlockStat ); } - private class FilteringModelPart implements BlockModelPart { + private class FilteringModelPart implements BlockStateModelPart { - private final BlockModelPart original; + private final BlockStateModelPart original; private final Pair neighbors; - private FilteringModelPart(BlockModelPart original, Pair neighbors){ + private FilteringModelPart(BlockStateModelPart original, Pair neighbors){ this.original = original; this.neighbors = neighbors; } @@ -135,13 +134,13 @@ public boolean useAmbientOcclusion(){ } @Override - public TextureAtlasSprite particleIcon(){ - return this.original.particleIcon(); + public Material.Baked particleMaterial(){ + return this.original.particleMaterial(); } @Override - public ChunkSectionLayer getRenderType(BlockState state){ - return this.original.getRenderType(state); + public @BakedQuad.MaterialFlags int materialFlags() { + return this.original.materialFlags(); } @Override diff --git a/src/main/java/com/supermartijn642/fusion/model/modifiers/item/ItemModelModifierReloadListener.java b/src/main/java/com/supermartijn642/fusion/model/modifiers/item/ItemModelModifierReloadListener.java index effeb431..8e5b4f51 100644 --- a/src/main/java/com/supermartijn642/fusion/model/modifiers/item/ItemModelModifierReloadListener.java +++ b/src/main/java/com/supermartijn642/fusion/model/modifiers/item/ItemModelModifierReloadListener.java @@ -14,7 +14,7 @@ import com.supermartijn642.fusion.model.modifiers.item.predicates.AndItemPredicate; import com.supermartijn642.fusion.model.modifiers.item.predicates.ItemPredicateRegistry; import com.supermartijn642.fusion.util.IdentifierUtil; -import net.minecraft.client.renderer.item.BlockModelWrapper; +import net.minecraft.client.renderer.item.CuboidItemModelWrapper; import net.minecraft.client.renderer.item.ItemModel; import net.minecraft.client.resources.model.ModelBakery; import net.minecraft.client.resources.model.ResolvableModel; @@ -24,6 +24,7 @@ import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener; import net.minecraft.world.item.Item; +import org.joml.Matrix4f; import java.util.*; @@ -64,7 +65,7 @@ public void applyPredicateModels(ModelBakery.BakingResult results, ItemModel.Bak } private ItemModel getOrBakeModel(Identifier location, Map bakedModels, ItemModel.BakingContext bakingContext){ - return bakedModels.computeIfAbsent(location, l -> new BlockModelWrapper.Unbaked(l, List.of()).bake(bakingContext)); + return bakedModels.computeIfAbsent(location, l -> new CuboidItemModelWrapper.Unbaked(l, Optional.empty(), List.of()).bake(bakingContext, new Matrix4f())); } public void reload(ResourceManager resourceManager){ diff --git a/src/main/java/com/supermartijn642/fusion/model/types/UnknownModelType.java b/src/main/java/com/supermartijn642/fusion/model/types/UnknownModelType.java index 3b9aa788..6f780493 100644 --- a/src/main/java/com/supermartijn642/fusion/model/types/UnknownModelType.java +++ b/src/main/java/com/supermartijn642/fusion/model/types/UnknownModelType.java @@ -5,19 +5,20 @@ import com.supermartijn642.fusion.api.model.BlockModelBakingContext; import com.supermartijn642.fusion.api.model.ItemModelBakingContext; import com.supermartijn642.fusion.api.model.ModelType; -import net.minecraft.client.renderer.block.model.BlockStateModel; -import net.minecraft.client.renderer.block.model.SimpleModelWrapper; -import net.minecraft.client.renderer.block.model.SingleVariant; -import net.minecraft.client.renderer.block.model.TextureSlots; -import net.minecraft.client.renderer.item.BlockModelWrapper; +import net.minecraft.client.renderer.block.dispatch.BlockModelRotation; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; +import net.minecraft.client.renderer.block.dispatch.SingleVariant; +import net.minecraft.client.renderer.item.CuboidItemModelWrapper; import net.minecraft.client.renderer.item.ItemModel; import net.minecraft.client.renderer.item.ModelRenderProperties; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.resources.model.BlockModelRotation; -import net.minecraft.client.resources.model.QuadCollection; +import net.minecraft.client.resources.model.SimpleModelWrapper; import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.client.resources.model.geometry.QuadCollection; +import net.minecraft.client.resources.model.sprite.Material; +import net.minecraft.client.resources.model.sprite.TextureSlots; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Nullable; +import org.joml.Matrix4fc; import java.util.Collection; import java.util.List; @@ -47,21 +48,21 @@ public Collection getModelDependencies(T data){ public BlockStateModel bakeBlockModel(BlockModelBakingContext context, T data){ TextureSlots textures = new TextureSlots(context.getTopLevelTextureReferences()); boolean ambientOcclusion = context.getTopLevelAmbientOcclusion(); - TextureAtlasSprite particle = context.getModelBaker().sprites().resolveSlot(textures, "particle", () -> context.getModelIdentifier().toString()); + Material.Baked particle = context.getModelBaker().materials().resolveSlot(textures, "particle", () -> context.getModelIdentifier().toString()); QuadCollection quads = context.getTopLevelGeometry().bake(textures, context.getModelBaker(), context.getTransformation(), () -> context.getModelIdentifier().toString(), context.getNeoForgeAdditionalProperties()); return new SingleVariant(new SimpleModelWrapper(quads, ambientOcclusion, particle)); } @Override - public ItemModel bakeItemModel(ItemModelBakingContext context, T data){ + public ItemModel bakeItemModel(ItemModelBakingContext context, T data, Matrix4fc transformation){ TextureSlots textures = new TextureSlots(context.getTopLevelTextureReferences()); - TextureAtlasSprite particle = context.getModelBaker().sprites().resolveSlot(textures, "particle", () -> context.getModelIdentifier().toString()); + Material.Baked particle = context.getModelBaker().materials().resolveSlot(textures, "particle", () -> context.getModelIdentifier().toString()); QuadCollection quads = context.getTopLevelGeometry().bake(textures, context.getModelBaker(), BlockModelRotation.IDENTITY, () -> context.getModelIdentifier().toString()); - return new BlockModelWrapper(context.getTintSources(), quads.getAll(), new ModelRenderProperties( + return new CuboidItemModelWrapper(context.getTintSources(), quads, new ModelRenderProperties( context.getTopLevelUseBlockLighting(), particle, context.getTopLevelItemTransforms() - ), BlockModelWrapper.detectRenderType(quads.getAll())); + ), transformation); } @Override diff --git a/src/main/java/com/supermartijn642/fusion/model/types/base/BaseBakedModel.java b/src/main/java/com/supermartijn642/fusion/model/types/base/BaseBakedModel.java index 66458261..4c687523 100644 --- a/src/main/java/com/supermartijn642/fusion/model/types/base/BaseBakedModel.java +++ b/src/main/java/com/supermartijn642/fusion/model/types/base/BaseBakedModel.java @@ -1,6 +1,5 @@ package com.supermartijn642.fusion.model.types.base; -import com.supermartijn642.fusion.FusionClient; import com.supermartijn642.fusion.api.texture.DefaultTextureTypes; import com.supermartijn642.fusion.api.texture.TextureType; import com.supermartijn642.fusion.api.util.Pair; @@ -9,16 +8,16 @@ import com.supermartijn642.fusion.texture.types.continuous.ContinuousTextureType; import com.supermartijn642.fusion.texture.types.random.RandomTextureSprite; import com.supermartijn642.fusion.texture.types.random.RandomTextureType; -import net.minecraft.client.renderer.ItemBlockRenderTypes; -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.renderer.block.model.BlockModelPart; -import net.minecraft.client.renderer.block.model.BlockStateModel; -import net.minecraft.client.renderer.chunk.ChunkSectionLayer; +import net.minecraft.client.renderer.block.BlockAndTintGetter; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; +import net.minecraft.client.renderer.block.dispatch.BlockStateModelPart; import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.ModelBaker; +import net.minecraft.client.resources.model.geometry.BakedQuad; +import net.minecraft.client.resources.model.sprite.Material; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.util.RandomSource; -import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.block.state.BlockState; import org.jetbrains.annotations.Nullable; @@ -29,90 +28,81 @@ */ public class BaseBakedModel implements BlockStateModel { - private final Map,List[]> blockMesh; - private final List> blockRenderTypes; + private final List[] blockMesh; private final List sprites; private final boolean hasSpecialQuads; private final boolean hasAmbientOcclusion; - private final TextureAtlasSprite particleIcon; + private final Material.Baked particleMaterial; + private final @BakedQuad.MaterialFlags int materialFlags; - public BaseBakedModel(List quads, boolean hasAmbientOcclusion, TextureAtlasSprite particleIcon, ChunkSectionLayer neoforgeRenderType){ + public BaseBakedModel(List quads, boolean hasAmbientOcclusion, Material.Baked particleMaterial, ModelBaker.Interner interner){ this.hasAmbientOcclusion = hasAmbientOcclusion; - this.particleIcon = particleIcon; + this.particleMaterial = particleMaterial; // Create block and item meshes from the quads - Map,List[]> blockMesh = new HashMap<>(); - Set> blockRenderTypes = new HashSet<>(); + //noinspection unchecked + List[] blockMesh = new List[7]; HashMap sprites = new HashMap<>(); boolean hasSpecialQuads = false; + @BakedQuad.MaterialFlags int materialFlags = 0; MutableQuad mutableQuad = new MutableQuad(); for(BaseModelQuad quad : quads){ - mutableQuad.fillFromBakedQuad(quad.bakedQuad()); + quad.fill(mutableQuad); mutableQuad.ambientOcclusion(!quad.emissive() && hasAmbientOcclusion); - mutableQuad.emissive(quad.emissive()); // Tag quads which need additional processing int spriteIndex = -1; if(quad.textureType() == DefaultTextureTypes.RANDOM || quad.textureType() == DefaultTextureTypes.CONTINUOUS){ // Give each sprite a unique index - spriteIndex = sprites.computeIfAbsent(quad.bakedQuad().sprite(), o -> sprites.size()); + spriteIndex = sprites.computeIfAbsent(quad.bakedQuad().materialInfo().sprite(), o -> sprites.size()); hasSpecialQuads = true; } - TaggedBakedQuad finishedQuad = new TaggedBakedQuad(mutableQuad.toBakedQuad(), quad.textureType(), spriteIndex); + TaggedBakedQuad finishedQuad = new TaggedBakedQuad(mutableQuad.toBakedQuad(interner), quad.textureType(), spriteIndex); // Add the block quads - Optional layer = FusionClient.getChunkLayer(quad.renderType()); - if(layer.isEmpty() && neoforgeRenderType != null) - layer = Optional.of(neoforgeRenderType); - blockRenderTypes.add(layer); int cullIndex = cullIndex(quad.cullDirection()); - //noinspection unchecked - List[] mesh = blockMesh.computeIfAbsent(layer, r -> new List[7]); - if(mesh[cullIndex] == null) - mesh[cullIndex] = new ArrayList<>(); - mesh[cullIndex].add(finishedQuad); + if(blockMesh[cullIndex] == null) + blockMesh[cullIndex] = new ArrayList<>(); + blockMesh[cullIndex].add(finishedQuad); + materialFlags |= finishedQuad.bakedQuad.materialInfo().flags(); } - this.blockMesh = Map.copyOf(blockMesh); - this.blockRenderTypes = List.copyOf(blockRenderTypes); + this.blockMesh = blockMesh; this.sprites = sprites.entrySet().stream().sorted(Map.Entry.comparingByValue()).map(Map.Entry::getKey).toList(); this.hasSpecialQuads = hasSpecialQuads; + this.materialFlags = materialFlags; } @Override - public void collectParts(BlockAndTintGetter level, BlockPos pos, BlockState state, RandomSource random, List parts){ - for(Optional layer : this.blockRenderTypes){ - parts.add(new BlockModelPart() { - @Override - public List getQuads(@Nullable Direction cullDirection){ - return BaseBakedModel.this.getQuads(level, pos, state, cullDirection, random, layer); - } - - @Override - public boolean useAmbientOcclusion(){ - return BaseBakedModel.this.hasAmbientOcclusion; - } - - @Override - public TextureAtlasSprite particleIcon(){ - return BaseBakedModel.this.particleIcon; - } - - @Override - public ChunkSectionLayer getRenderType(BlockState state){ - //noinspection deprecation - return layer.orElseGet(() -> ItemBlockRenderTypes.getChunkRenderType(state)); - } - }); - } + public void collectParts(BlockAndTintGetter level, BlockPos pos, BlockState state, RandomSource random, List parts){ + parts.add(new BlockStateModelPart() { + @Override + public List getQuads(@Nullable Direction cullDirection){ + return BaseBakedModel.this.getQuads(level, pos, state, cullDirection, random); + } + + @Override + public boolean useAmbientOcclusion(){ + return BaseBakedModel.this.hasAmbientOcclusion; + } + + @Override + public Material.Baked particleMaterial(){ + return BaseBakedModel.this.particleMaterial; + } + + @Override + public @BakedQuad.MaterialFlags int materialFlags(){ + return BaseBakedModel.this.materialFlags; + } + }); } @Override - public void collectParts(RandomSource random, List parts){ + public void collectParts(RandomSource random, List parts){ this.collectParts(null, null, null, random, parts); } - @SuppressWarnings("OptionalUsedAsFieldOrParameterType") - private List getQuads(@Nullable BlockAndTintGetter blockView, @Nullable BlockPos pos, @Nullable BlockState state, @Nullable Direction cullDirection, RandomSource random, Optional layer){ - List quads = this.blockMesh.get(layer)[cullIndex(cullDirection)]; + private List getQuads(@Nullable BlockAndTintGetter blockView, @Nullable BlockPos pos, @Nullable BlockState state, @Nullable Direction cullDirection, RandomSource random){ + List quads = this.blockMesh[cullIndex(cullDirection)]; if(quads == null) return Collections.emptyList(); @@ -148,7 +138,7 @@ private List getQuads(@Nullable BlockAndTintGetter blockView, @Nullab else // Handle continuous texture type ContinuousTextureType.processQuad(mutableQuad, pos, quad.bakedQuad.direction(), (ContinuousTextureSprite)sprite); - bakedQuads.add(mutableQuad.toBakedQuad()); + bakedQuads.add(mutableQuad.toBakedQuad(null)); }else bakedQuads.add(quad.bakedQuad); } @@ -161,8 +151,13 @@ private List getQuads(@Nullable BlockAndTintGetter blockView, @Nullab } @Override - public TextureAtlasSprite particleIcon(){ - return this.particleIcon; + public Material.Baked particleMaterial(){ + return this.particleMaterial; + } + + @Override + public @BakedQuad.MaterialFlags int materialFlags(){ + return this.materialFlags; } private static int cullIndex(Direction cullDirection){ diff --git a/src/main/java/com/supermartijn642/fusion/model/types/base/BaseItemModel.java b/src/main/java/com/supermartijn642/fusion/model/types/base/BaseItemModel.java index aaa3e7b6..f6c09f36 100644 --- a/src/main/java/com/supermartijn642/fusion/model/types/base/BaseItemModel.java +++ b/src/main/java/com/supermartijn642/fusion/model/types/base/BaseItemModel.java @@ -1,25 +1,23 @@ package com.supermartijn642.fusion.model.types.base; import com.google.common.base.Suppliers; -import com.supermartijn642.fusion.api.util.Pair; import com.supermartijn642.fusion.model.MutableQuad; +import it.unimi.dsi.fastutil.ints.IntList; import net.minecraft.client.color.item.ItemTintSource; import net.minecraft.client.multiplayer.ClientLevel; -import net.minecraft.client.renderer.Sheets; -import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.item.CuboidItemModelWrapper; import net.minecraft.client.renderer.item.*; -import net.minecraft.client.renderer.rendertype.RenderType; -import net.minecraft.client.renderer.texture.TextureAtlas; +import net.minecraft.client.resources.model.ModelBaker; +import net.minecraft.client.resources.model.geometry.BakedQuad; import net.minecraft.world.entity.ItemOwner; import net.minecraft.world.item.ItemDisplayContext; import net.minecraft.world.item.ItemStack; import org.jetbrains.annotations.Nullable; +import org.joml.Matrix4fc; import org.joml.Vector3fc; import java.util.ArrayList; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import java.util.function.Supplier; /** @@ -29,43 +27,29 @@ public class BaseItemModel implements ItemModel { private final List tints; private final ModelRenderProperties properties; - private final List>> mesh; + private final List mesh = new ArrayList<>(); private final Supplier extents; private final boolean animated; + private final Matrix4fc transformation; - public BaseItemModel(List tints, List quads, ModelRenderProperties properties, RenderType neoforgeItemRenderType, RenderType neoforgeBlockRenderType){ + public BaseItemModel(List tints, List quads, ModelRenderProperties properties, Matrix4fc transformation, ModelBaker.Interner interner){ this.tints = tints; this.properties = properties; - this.extents = Suppliers.memoize(() -> BlockModelWrapper.computeExtents(quads.stream().map(BaseModelQuad::bakedQuad).toList())); + this.extents = Suppliers.memoize(() -> CuboidItemModelWrapper.computeExtents(quads.stream().map(BaseModelQuad::bakedQuad).toList())); // Create the item mesh - Map> mesh = new LinkedHashMap<>(); + @BakedQuad.MaterialFlags int flags = 0; MutableQuad mutableQuad = new MutableQuad(); for(BaseModelQuad quad : quads){ - mutableQuad.fillFromBakedQuad(quad.bakedQuad()); - mutableQuad.emissive(quad.emissive()); - - // Add the item quads - RenderType renderType = quad.bakedQuad().sprite().atlasLocation().equals(TextureAtlas.LOCATION_BLOCKS) ? - neoforgeBlockRenderType == null || quad.renderType() != null ? Sheets.translucentBlockItemSheet() : neoforgeBlockRenderType : - neoforgeItemRenderType == null || quad.renderType() != null ? Sheets.translucentItemSheet() : neoforgeItemRenderType; - List itemQuads = mesh.computeIfAbsent(renderType, k -> new ArrayList<>()); - itemQuads.add(mutableQuad.toBakedQuad()); + quad.fill(mutableQuad); + this.mesh.add(mutableQuad.toBakedQuad(interner)); + flags |= quad.bakedQuad().materialInfo().flags(); } - this.mesh = mesh.entrySet().stream() - .map(entry -> Pair.of(entry.getKey(), entry.getValue())) - .toList(); // Check whether the quads contain animated textures - boolean animated = false; - for(BaseModelQuad quad : quads){ - //noinspection resource - if(quad.bakedQuad().sprite().contents().isAnimated()){ - animated = true; - break; - } - } - this.animated = animated; + this.animated = (flags & BakedQuad.FLAG_ANIMATED) != 0; + + this.transformation = transformation; } @Override @@ -73,27 +57,25 @@ public void update(ItemStackRenderState renderState, ItemStack stack, ItemModelR renderState.appendModelIdentityElement(this); if(this.animated) renderState.setAnimated(); - ItemStackRenderState.FoilType foilType = stack.hasFoil() ? BlockModelWrapper.hasSpecialAnimatedTexture(stack) ? ItemStackRenderState.FoilType.SPECIAL : ItemStackRenderState.FoilType.STANDARD : null; + ItemStackRenderState.FoilType foilType = stack.hasFoil() ? CuboidItemModelWrapper.hasSpecialAnimatedTexture(stack) ? ItemStackRenderState.FoilType.SPECIAL : ItemStackRenderState.FoilType.STANDARD : null; if(foilType != null){ renderState.setAnimated(); renderState.appendModelIdentityElement(foilType); } - int tints = this.tints.size(); - int[] tintValues = new int[tints]; - for(int j = 0; j < tints; j++){ - int tint = this.tints.get(j).calculate(stack, level, owner == null ? null : owner.asLivingEntity()); - tintValues[j] = tint; - renderState.appendModelIdentityElement(tint); - } - for(Pair> pair : this.mesh){ - ItemStackRenderState.LayerRenderState layer = renderState.newLayer(); - if(foilType != null) - layer.setFoilType(foilType); - System.arraycopy(tintValues, 0, layer.prepareTintLayers(tints), 0, tintValues.length); - layer.setExtents(this.extents); - this.properties.applyToLayer(layer, displayContext); - layer.prepareQuadList().addAll(pair.right()); - layer.setRenderType(pair.left()); + ItemStackRenderState.LayerRenderState layer = renderState.newLayer(); + if(foilType != null) + layer.setFoilType(foilType); + if(!this.tints.isEmpty()){ + IntList tintLayers = layer.tintLayers(); + for(ItemTintSource tintSource : this.tints){ + int tint = tintSource.calculate(stack, level, owner == null ? null : owner.asLivingEntity()); + tintLayers.add(tint); + renderState.appendModelIdentityElement(tint); + } } + layer.setExtents(this.extents); + layer.setLocalTransform(this.transformation); + this.properties.applyToLayer(layer, displayContext); + layer.prepareQuadList().addAll(this.mesh); } } diff --git a/src/main/java/com/supermartijn642/fusion/model/types/base/BaseModelDataBuilderImpl.java b/src/main/java/com/supermartijn642/fusion/model/types/base/BaseModelDataBuilderImpl.java index 7931c63d..7bbb96cf 100644 --- a/src/main/java/com/supermartijn642/fusion/model/types/base/BaseModelDataBuilderImpl.java +++ b/src/main/java/com/supermartijn642/fusion/model/types/base/BaseModelDataBuilderImpl.java @@ -2,10 +2,9 @@ import com.supermartijn642.fusion.api.model.data.BaseModelData; import com.supermartijn642.fusion.api.model.data.BaseModelDataBuilder; -import com.supermartijn642.fusion.util.TextureAtlases; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.client.renderer.block.model.TextureSlots; -import net.minecraft.client.resources.model.Material; +import net.minecraft.client.resources.model.cuboid.CuboidModel; +import net.minecraft.client.resources.model.sprite.Material; +import net.minecraft.client.resources.model.sprite.TextureSlots; import net.minecraft.resources.Identifier; import java.util.*; @@ -16,7 +15,7 @@ public class BaseModelDataBuilderImpl implements BaseModelDataBuilder { private final Set parents = new LinkedHashSet<>(); // This should maintain insertion order - private final Map textures = new HashMap<>(); + private final Map textures = new HashMap<>(); @Override public BaseModelDataBuilderImpl parent(Identifier parent){ @@ -34,24 +33,24 @@ public BaseModelDataBuilderImpl texture(String key, String reference){ if(!key.matches("[a-zA-Z_]*")) throw new IllegalArgumentException("Texture reference must only contain characters [a-zA-Z_]!"); - // Prepend '#' character - if(reference.charAt(0) != '#') - reference = '#' + reference; + // Strip '#' character + if(reference.charAt(0) == '#') + reference = reference.substring(1); if(this.textures.containsKey(key)) throw new RuntimeException("Duplicate texture entry for key '" + key + "': '" + this.textures.get(key) + "' and '" + reference + "'!"); - this.textures.put(key, reference); + this.textures.put(key, new TextureSlots.Reference(reference)); return this; } @Override - public BaseModelDataBuilderImpl texture(String key, Identifier texture){ + public BaseModelDataBuilderImpl texture(String key, Material material){ if(!key.matches("[a-zA-Z_]*")) throw new IllegalArgumentException("Texture reference must only contain characters [a-zA-Z_]!"); if(this.textures.containsKey(key)) - throw new RuntimeException("Duplicate texture entry for key '" + key + "': '" + this.textures.get(key) + "' and '" + texture + "'!"); + throw new RuntimeException("Duplicate texture entry for key '" + key + "': '" + this.textures.get(key) + "' and '" + material + "'!"); - this.textures.put(key, texture.toString()); + this.textures.put(key, new TextureSlots.Value(material)); return this; } @@ -60,14 +59,8 @@ public BaseModelData build(){ List parents = new ArrayList<>(this.parents); // Create a vanilla model representation of the properties Identifier parent = parents.isEmpty() ? null : parents.get(0); - TextureSlots.Data.Builder textures = new TextureSlots.Data.Builder(); - this.textures.forEach((key, value) -> { - if(value.charAt(0) == '#') - textures.addReference(key, value.substring(1)); - else - textures.addTexture(key, new Material(TextureAtlases.getBlocks(), Identifier.parse(value))); - }); - BlockModel vanillaModel = new BlockModel(null, null, null, null, textures.build(), parent); + TextureSlots.Data textures = new TextureSlots.Data(Map.copyOf(this.textures)); + CuboidModel vanillaModel = new CuboidModel(null, null, null, null, textures, parent); return new BaseModelDataImpl(vanillaModel, parents, Collections.emptyList()); } } diff --git a/src/main/java/com/supermartijn642/fusion/model/types/base/BaseModelDataImpl.java b/src/main/java/com/supermartijn642/fusion/model/types/base/BaseModelDataImpl.java index 21316ad1..3e426ac0 100644 --- a/src/main/java/com/supermartijn642/fusion/model/types/base/BaseModelDataImpl.java +++ b/src/main/java/com/supermartijn642/fusion/model/types/base/BaseModelDataImpl.java @@ -5,17 +5,18 @@ import com.supermartijn642.fusion.api.model.BlockModelBakingContext; import com.supermartijn642.fusion.api.model.DefaultModelTypes; import com.supermartijn642.fusion.api.model.ModelInstance; -import com.supermartijn642.fusion.api.model.SpriteIdentifier; import com.supermartijn642.fusion.api.model.data.BaseModelData; -import net.minecraft.client.renderer.block.model.*; -import net.minecraft.client.renderer.texture.SpriteContents; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.resources.model.QuadCollection; -import net.minecraft.client.resources.model.UnbakedGeometry; import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.client.resources.model.cuboid.*; +import net.minecraft.client.resources.model.geometry.BakedQuad; +import net.minecraft.client.resources.model.geometry.QuadCollection; +import net.minecraft.client.resources.model.geometry.UnbakedGeometry; +import net.minecraft.client.resources.model.sprite.Material; +import net.minecraft.client.resources.model.sprite.TextureSlots; import net.minecraft.core.Direction; import net.minecraft.resources.Identifier; import net.minecraft.world.item.ItemDisplayContext; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Deque; @@ -30,18 +31,18 @@ */ public class BaseModelDataImpl implements BaseModelData { - protected final BlockModel model; + protected final CuboidModel model; protected final List parents; protected final List elements; - public BaseModelDataImpl(BlockModel model, List parents, List elements){ + public BaseModelDataImpl(CuboidModel model, List parents, List elements){ this.model = model; this.parents = ImmutableList.copyOf(parents); this.elements = ImmutableList.copyOf(elements); } @Override - public BlockModel getVanillaModel(){ + public CuboidModel getVanillaModel(){ return this.model; } @@ -111,7 +112,7 @@ public ItemTransform findItemTransform(BlockModelBakingContext context, ItemDisp ); } - public SpriteIdentifier findParticleSprite(BlockModelBakingContext context){ + public Material findParticleMaterial(BlockModelBakingContext context){ ModelInstance model = ModelInstance.of(DefaultModelTypes.BASE, this); // Repeatedly resolve key references until we get to a texture @@ -123,17 +124,17 @@ public SpriteIdentifier findParticleSprite(BlockModelBakingContext context){ TextureSlots.SlotContents contents = this.findProperty(context, model, m -> m.textureSlots().values().get(finalCurrentKey)); // If a key could not be found, return the missing texture if(contents == null) - return SpriteIdentifier.missing(); + return context.missingMaterial(); // If a texture is found return it - if(contents instanceof TextureSlots.Value) - return SpriteIdentifier.of(((TextureSlots.Value)contents).material()); + if(contents instanceof TextureSlots.Value(Material material)) + return material; // Check if a key has already been encountered currentKey = ((TextureSlots.Reference)contents).target(); if(encounteredKeys.contains(currentKey)){ FusionClient.LOGGER.warn("Unable to resolve texture due to circular references {}->'{}' in '{}'!", encounteredKeys.stream().map(o -> "'" + o + "'").collect(Collectors.joining("->")), currentKey, context.getModelIdentifier()); - return SpriteIdentifier.missing(); + return context.missingMaterial(); } encounteredKeys.add(currentKey); } @@ -155,9 +156,10 @@ private void bakeQuads(BlockModelBakingContext context, ModelInstance model, // Bake the faces of each element for(BaseModelElement element : elements){ for(Direction direction : element.original.faces().keySet()){ - BlockElementFace face = element.original.faces().get(direction); - TextureAtlasSprite sprite = context.getTexture(this.resolveMaterial(context, modelStack, face.texture())); - BakedQuad quad = FaceBakery.bakeQuad(context.getModelBaker().parts(), element.original.from(), element.original.to(), face, sprite, direction, context.getTransformation(), element.original.rotation(), element.original.shade(), element.original.lightEmission()); + CuboidFace face = element.original.faces().get(direction); + Material unbakedMaterial = this.resolveMaterial(context, modelStack, face.texture()); + Material.Baked material = unbakedMaterial != null ? context.bakeMaterial(unbakedMaterial) : context.bakeMaterial(context.missingMaterial()); + BakedQuad quad = FaceBakery.bakeQuad(context.getModelBaker(), element.original.from(), element.original.to(), face, material, direction, context.getTransformation(), element.original.rotation(), element.original.shade(), element.original.lightEmission()); Direction cullDirection = face.cullForDirection() != null ? Direction.rotate(context.getTransformation().transformation().getMatrix(), face.cullForDirection()) : null; output.accept(new BaseModelQuad(quad, cullDirection)); } @@ -192,7 +194,7 @@ private void bakeQuads(BlockModelBakingContext context, ModelInstance model, modelStack.removeLast(); } - protected SpriteIdentifier resolveMaterial(BlockModelBakingContext context, Deque> modelStack, String key){ + protected @Nullable Material resolveMaterial(BlockModelBakingContext context, Deque> modelStack, String key){ if(key.charAt(0) == '#') key = key.substring(1); @@ -218,33 +220,32 @@ protected SpriteIdentifier resolveMaterial(BlockModelBakingContext context, Dequ } // If a key could not be found, return the missing texture if(contents == null) - return SpriteIdentifier.missing(); + return null; // If a texture is found return it - if(contents instanceof TextureSlots.Value) - return SpriteIdentifier.of(((TextureSlots.Value)contents).material()); + if(contents instanceof TextureSlots.Value(Material material)) + return material; // Check if a key has already been encountered currentKey = ((TextureSlots.Reference)contents).target(); if(encounteredKeys.contains(currentKey)){ FusionClient.LOGGER.warn("Unable to resolve texture due to circular references {}->'{}' in '{}'!", encounteredKeys.stream().map(o -> "'" + o + "'").collect(Collectors.joining("->")), currentKey, context.getModelIdentifier()); - return SpriteIdentifier.missing(); + return null; } encounteredKeys.add(currentKey); } } - protected List generateItemModel(BlockModelBakingContext context, Deque> modelStack){ - List elements = new ArrayList<>(); + protected QuadCollection bakeItemModel(BlockModelBakingContext context, Deque> modelStack){ + QuadCollection.Builder quads = new QuadCollection.Builder(); for(int layer = 0; layer < ItemModelGenerator.LAYERS.size(); layer++){ String layerName = ItemModelGenerator.LAYERS.get(layer); - SpriteIdentifier sprite = this.resolveMaterial(context, modelStack, layerName); - if(SpriteIdentifier.missing().equals(sprite)) + Material unbakedMaterial = this.resolveMaterial(context, modelStack, layerName); + if(unbakedMaterial == null) break; - - SpriteContents spriteContents = context.getTexture(sprite).contents(); - elements.addAll(ItemModelGenerator.processFrames(layer, layerName, spriteContents)); + Material.Baked material = context.bakeMaterial(unbakedMaterial); + quads.addAll(context.getModelBaker().compute(new ItemModelGenerator.ItemLayerKey(material, context.getTransformation(), layer))); } - return elements; + return quads.build(); } } diff --git a/src/main/java/com/supermartijn642/fusion/model/types/base/BaseModelElement.java b/src/main/java/com/supermartijn642/fusion/model/types/base/BaseModelElement.java index c391a535..f0ee5548 100644 --- a/src/main/java/com/supermartijn642/fusion/model/types/base/BaseModelElement.java +++ b/src/main/java/com/supermartijn642/fusion/model/types/base/BaseModelElement.java @@ -1,8 +1,8 @@ package com.supermartijn642.fusion.model.types.base; -import net.minecraft.client.renderer.block.model.BlockElement; -import net.minecraft.client.renderer.block.model.BlockElementFace; -import net.minecraft.client.renderer.block.model.BlockElementRotation; +import net.minecraft.client.resources.model.cuboid.CuboidFace; +import net.minecraft.client.resources.model.cuboid.CuboidModelElement; +import net.minecraft.client.resources.model.cuboid.CuboidRotation; import net.minecraft.core.Direction; import org.jetbrains.annotations.Nullable; import org.joml.Vector3fc; @@ -14,9 +14,9 @@ */ public class BaseModelElement { - public final BlockElement original; + public final CuboidModelElement original; - public BaseModelElement(Vector3fc from, Vector3fc to, Map faces, @Nullable BlockElementRotation rotation, boolean shade, int lightEmission){ - this.original = new BlockElement(from, to, faces, rotation, shade, lightEmission); + public BaseModelElement(Vector3fc from, Vector3fc to, Map faces, @Nullable CuboidRotation rotation, boolean shade, int lightEmission){ + this.original = new CuboidModelElement(from, to, faces, rotation, shade, lightEmission); } } diff --git a/src/main/java/com/supermartijn642/fusion/model/types/base/BaseModelQuad.java b/src/main/java/com/supermartijn642/fusion/model/types/base/BaseModelQuad.java index 54096d48..c3c4f669 100644 --- a/src/main/java/com/supermartijn642/fusion/model/types/base/BaseModelQuad.java +++ b/src/main/java/com/supermartijn642/fusion/model/types/base/BaseModelQuad.java @@ -3,9 +3,10 @@ import com.supermartijn642.fusion.api.texture.SpriteHelper; import com.supermartijn642.fusion.api.texture.TextureType; import com.supermartijn642.fusion.api.texture.data.BaseTextureData; +import com.supermartijn642.fusion.model.MutableQuad; import com.supermartijn642.fusion.texture.types.base.BaseTextureSprite; -import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.geometry.BakedQuad; import net.minecraft.core.Direction; /** @@ -16,22 +17,21 @@ public class BaseModelQuad { private final BakedQuad bakedQuad; private final TextureType textureType; private final Direction cullDirection; - private final BaseTextureData.RenderType renderType; private final boolean emissive; + private final int tintIndex; public BaseModelQuad(BakedQuad bakedQuad, Direction cullDirection){ - this.textureType = SpriteHelper.getTextureType(bakedQuad.sprite()); + BakedQuad.MaterialInfo materialInfo = bakedQuad.materialInfo(); + TextureAtlasSprite sprite = materialInfo.sprite(); + this.textureType = SpriteHelper.getTextureType(sprite); this.cullDirection = cullDirection; - TextureAtlasSprite sprite = bakedQuad.sprite(); if(sprite instanceof BaseTextureSprite && ((BaseTextureSprite)sprite).data() != null){ BaseTextureData data = ((BaseTextureSprite)sprite).data(); - this.renderType = data.getRenderType(); this.emissive = data.isEmissive(); - if(data.getTinting() != null) // Create an identical quad, but with tint index '39216' - bakedQuad = new BakedQuad(bakedQuad.position0(), bakedQuad.position1(), bakedQuad.position2(), bakedQuad.position3(), bakedQuad.packedUV0(), bakedQuad.packedUV1(), bakedQuad.packedUV2(), bakedQuad.packedUV3(), 39216, bakedQuad.direction(), bakedQuad.sprite(), bakedQuad.shade(), bakedQuad.lightEmission(), bakedQuad.bakedNormals(), bakedQuad.bakedColors(), bakedQuad.hasAmbientOcclusion()); + this.tintIndex = data.getTinting() != null ? 39216 : bakedQuad.materialInfo().tintIndex(); }else{ - this.renderType = null; this.emissive = false; + this.tintIndex = bakedQuad.materialInfo().tintIndex(); } this.bakedQuad = bakedQuad; } @@ -48,11 +48,13 @@ public Direction cullDirection(){ return this.cullDirection; } - public BaseTextureData.RenderType renderType(){ - return this.renderType; - } - public boolean emissive(){ return this.emissive; } + + public void fill(MutableQuad mutableQuad){ + mutableQuad.fillFromBakedQuad(this.bakedQuad); + mutableQuad.emissive(this.emissive); + mutableQuad.tintIndex(this.tintIndex); + } } diff --git a/src/main/java/com/supermartijn642/fusion/model/types/base/BaseModelType.java b/src/main/java/com/supermartijn642/fusion/model/types/base/BaseModelType.java index d222caf1..fad97560 100644 --- a/src/main/java/com/supermartijn642/fusion/model/types/base/BaseModelType.java +++ b/src/main/java/com/supermartijn642/fusion/model/types/base/BaseModelType.java @@ -10,18 +10,16 @@ import com.supermartijn642.fusion.api.model.ModelType; import com.supermartijn642.fusion.api.model.data.BaseModelData; import com.supermartijn642.fusion.util.IdentifierUtil; -import net.minecraft.client.renderer.block.model.*; -import net.minecraft.client.renderer.chunk.ChunkSectionLayer; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; import net.minecraft.client.renderer.item.ItemModel; import net.minecraft.client.renderer.item.ModelRenderProperties; -import net.minecraft.client.renderer.rendertype.RenderType; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.client.resources.model.cuboid.*; +import net.minecraft.client.resources.model.sprite.Material; import net.minecraft.resources.Identifier; import net.minecraft.world.item.ItemDisplayContext; -import net.neoforged.neoforge.client.RenderTypeGroup; -import net.neoforged.neoforge.client.model.NeoForgeModelProperties; import org.jetbrains.annotations.Nullable; +import org.joml.Matrix4fc; import java.util.ArrayList; import java.util.Collection; @@ -56,27 +54,25 @@ public BlockStateModel bakeBlockModel(BlockModelBakingContext context, BaseModel List quads = ((BaseModelDataImpl)data).bakeQuads(context); // Gather remaining model properties boolean ambientOcclusion = ((BaseModelDataImpl)data).findProperty(context, UnbakedModel::ambientOcclusion, true); - TextureAtlasSprite particleSprite = context.getTexture(((BaseModelDataImpl)data).findParticleSprite(context)); - RenderTypeGroup neoforgeRenderTypeGroup = context.getNeoForgeAdditionalProperties().getOptional(NeoForgeModelProperties.RENDER_TYPE); - ChunkSectionLayer neoforgeRenderType = neoforgeRenderTypeGroup == null ? null : neoforgeRenderTypeGroup.block(); + Material.Baked particleMaterial = context.bakeMaterial(((BaseModelDataImpl)data).findParticleMaterial(context)); // Finally, create the model return new BaseBakedModel( quads, ambientOcclusion, - particleSprite, - neoforgeRenderType + particleMaterial, + context.getModelBaker().interner() ); } @Override - public ItemModel bakeItemModel(ItemModelBakingContext context, BaseModelData data){ + public ItemModel bakeItemModel(ItemModelBakingContext context, BaseModelData data, Matrix4fc transformation){ // Check for circular dependencies ((BaseModelDataImpl)data).validateParents(context); // Bake the quads List quads = ((BaseModelDataImpl)data).bakeQuads(context); // Gather remaining model properties - boolean usesBlockLight = ((BaseModelDataImpl)data).findProperty(context, UnbakedModel::guiLight, BlockModel.GuiLight.SIDE).lightLikeBlock(); - TextureAtlasSprite particleSprite = context.getTexture(((BaseModelDataImpl)data).findParticleSprite(context)); + boolean usesBlockLight = ((BaseModelDataImpl)data).findProperty(context, UnbakedModel::guiLight, CuboidModel.GuiLight.SIDE).lightLikeBlock(); + Material.Baked particleMaterial = context.bakeMaterial(((BaseModelDataImpl)data).findParticleMaterial(context)); //noinspection deprecation ItemTransforms transforms = new ItemTransforms( ((BaseModelDataImpl)data).findItemTransform(context, ItemDisplayContext.THIRD_PERSON_LEFT_HAND), @@ -89,23 +85,20 @@ public ItemModel bakeItemModel(ItemModelBakingContext context, BaseModelData dat ((BaseModelDataImpl)data).findItemTransform(context, ItemDisplayContext.FIXED), ((BaseModelDataImpl)data).findItemTransform(context, ItemDisplayContext.ON_SHELF) ); - RenderTypeGroup neoforgeRenderTypeGroup = context.getNeoForgeAdditionalProperties().getOptional(NeoForgeModelProperties.RENDER_TYPE); - RenderType neoforgeItemRenderType = neoforgeRenderTypeGroup == null ? null : neoforgeRenderTypeGroup.entityItem(); - RenderType neoforgeBlockRenderType = neoforgeRenderTypeGroup == null ? null : neoforgeRenderTypeGroup.entityBlock(); // Finally, create the model return new BaseItemModel( context.getTintSources(), quads, - new ModelRenderProperties(usesBlockLight, particleSprite, transforms), - neoforgeItemRenderType, - neoforgeBlockRenderType + new ModelRenderProperties(usesBlockLight, particleMaterial, transforms), + transformation, + context.getModelBaker().interner() ); } @Override public BaseModelData deserialize(JsonObject json) throws JsonParseException{ // Deserialize the vanilla model attributes - BlockModel model = DefaultModelTypes.VANILLA.deserialize(json); + CuboidModel model = DefaultModelTypes.VANILLA.deserialize(json); // Read parents if(json.has("parent") && json.has("parents")) throw new JsonParseException("Model can only have either 'parent' or 'parents', not both!"); @@ -131,14 +124,14 @@ public BaseModelData deserialize(JsonObject json) throws JsonParseException{ parents.add(Identifier.parse(parent)); } if(!parents.isEmpty()) - model = new BlockModel(model.geometry(), model.guiLight(), model.ambientOcclusion(), model.transforms(), model.textureSlots(), parents.get(0), model.rootTransform(), model.renderTypeGroup(), model.partVisibility()); + model = new CuboidModel(model.geometry(), model.guiLight(), model.ambientOcclusion(), model.transforms(), model.textureSlots(), parents.get(0), model.rootTransform(), model.partVisibility()); } List elements = List.of(); - if(model.geometry() instanceof SimpleUnbakedGeometry( - List vanillaElements + if(model.geometry() instanceof UnbakedCuboidGeometry( + List vanillaElements )){ elements = new ArrayList<>(vanillaElements.size()); - for(BlockElement element : vanillaElements) + for(CuboidModelElement element : vanillaElements) elements.add(new BaseModelElement(element.from(), element.to(), element.faces(), element.rotation(), element.shade(), element.lightEmission())); } return new BaseModelDataImpl(model, parents, elements); diff --git a/src/main/java/com/supermartijn642/fusion/model/types/connecting/ConnectingBakedModel.java b/src/main/java/com/supermartijn642/fusion/model/types/connecting/ConnectingBakedModel.java index 11a3ab4d..22db29fa 100644 --- a/src/main/java/com/supermartijn642/fusion/model/types/connecting/ConnectingBakedModel.java +++ b/src/main/java/com/supermartijn642/fusion/model/types/connecting/ConnectingBakedModel.java @@ -2,7 +2,6 @@ import com.mojang.blaze3d.vertex.DefaultVertexFormat; import com.mojang.blaze3d.vertex.VertexFormatElement; -import com.supermartijn642.fusion.FusionClient; import com.supermartijn642.fusion.api.predicate.ConnectionDirection; import com.supermartijn642.fusion.api.predicate.ConnectionPredicate; import com.supermartijn642.fusion.api.texture.DefaultTextureTypes; @@ -17,16 +16,16 @@ import com.supermartijn642.fusion.texture.types.random.RandomTextureSprite; import com.supermartijn642.fusion.texture.types.random.RandomTextureType; import net.minecraft.client.model.geom.builders.UVPair; -import net.minecraft.client.renderer.ItemBlockRenderTypes; -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.renderer.block.model.BlockModelPart; -import net.minecraft.client.renderer.block.model.BlockStateModel; -import net.minecraft.client.renderer.chunk.ChunkSectionLayer; +import net.minecraft.client.renderer.block.BlockAndTintGetter; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; +import net.minecraft.client.renderer.block.dispatch.BlockStateModelPart; import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.ModelBaker; +import net.minecraft.client.resources.model.geometry.BakedQuad; +import net.minecraft.client.resources.model.sprite.Material; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.util.RandomSource; -import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.block.state.BlockState; import org.jetbrains.annotations.Nullable; import org.joml.Vector3fc; @@ -88,25 +87,26 @@ private static float[] getPosition(BakedQuad quad, int vertexIndex){ return new float[]{position.x(), position.y(), position.z()}; } - private final List completeBlockMesh; - private final Map,List[]> blockMesh; - private final List> blockRenderTypes; + private final BlockStateModelPart completeBlockMesh; + private final List[] blockMesh; private final List predicates; private final List sprites; private final boolean hasSpecialQuads; private final boolean hasAmbientOcclusion; - private final TextureAtlasSprite particleIcon; + private final Material.Baked particleMaterial; + private final @BakedQuad.MaterialFlags int materialFlags; - public ConnectingBakedModel(List quads, boolean hasAmbientOcclusion, TextureAtlasSprite particleIcon, ChunkSectionLayer neoforgeRenderType){ + public ConnectingBakedModel(List quads, boolean hasAmbientOcclusion, Material.Baked particleMaterial, ModelBaker.Interner interner){ this.hasAmbientOcclusion = hasAmbientOcclusion; - this.particleIcon = particleIcon; + this.particleMaterial = particleMaterial; // Create block and item meshes from the quads - Map,List[]> blockMesh = new HashMap<>(); - Set> blockRenderTypes = new HashSet<>(); + //noinspection unchecked + List[] blockMesh = new List[7]; HashMap predicates = new HashMap<>(); HashMap sprites = new HashMap<>(); boolean hasSpecialQuads = false; + @BakedQuad.MaterialFlags int materialFlags = 0; OrientedMutableQuad mutableQuad = new OrientedMutableQuad(); for(ConnectingModelQuad quad : quads){ TextureType textureType = quad.textureType(); @@ -123,72 +123,60 @@ public ConnectingBakedModel(List quads, boolean hasAmbientO // Give each combination of direction, orientation, and predicate a unique index predicateIndex = predicates.computeIfAbsent(new QuadPredicates(direction, orientation, predicate), o -> predicates.size()); // Give each sprite a unique index - spriteIndex = sprites.computeIfAbsent(quad.bakedQuad().sprite(), o -> sprites.size()); + spriteIndex = sprites.computeIfAbsent(quad.bakedQuad().materialInfo().sprite(), o -> sprites.size()); } // Tag quads which need additional processing if(quad.textureType() == DefaultTextureTypes.RANDOM || quad.textureType() == DefaultTextureTypes.CONTINUOUS){ // Give each sprite a unique index - spriteIndex = sprites.computeIfAbsent(quad.bakedQuad().sprite(), o -> sprites.size()); + spriteIndex = sprites.computeIfAbsent(quad.bakedQuad().materialInfo().sprite(), o -> sprites.size()); hasSpecialQuads = true; } // Submit the quads for(int quadIndex = 0; quadIndex < auxiliaryQuadCount + 1; quadIndex++){ - mutableQuad.fillFromBakedQuad(quad.bakedQuad()); + quad.fill(mutableQuad); mutableQuad.ambientOcclusion(!quad.emissive() && hasAmbientOcclusion); - mutableQuad.emissive(quad.emissive()); // Add the block quad - TaggedBakedQuad finishedQuad = new TaggedBakedQuad(mutableQuad.toBakedQuad(), textureType, spriteIndex, predicateIndex, quadIndex); - Optional layer = FusionClient.getChunkLayer(quad.renderType()); - if(layer.isEmpty() && neoforgeRenderType != null) - layer = Optional.of(neoforgeRenderType); - blockRenderTypes.add(layer); + TaggedBakedQuad finishedQuad = new TaggedBakedQuad(mutableQuad.toBakedQuad(interner), textureType, spriteIndex, predicateIndex, quadIndex); int cullIndex = cullIndex(quad.cullDirection()); - //noinspection unchecked - List[] mesh = blockMesh.computeIfAbsent(layer, r -> new List[7]); - if(mesh[cullIndex] == null) - mesh[cullIndex] = new ArrayList<>(); - mesh[cullIndex].add(finishedQuad); + if(blockMesh[cullIndex] == null) + blockMesh[cullIndex] = new ArrayList<>(); + blockMesh[cullIndex].add(finishedQuad); + materialFlags |= finishedQuad.bakedQuad.materialInfo().flags(); } } - this.blockMesh = Map.copyOf(blockMesh); - this.blockRenderTypes = List.copyOf(blockRenderTypes); + this.blockMesh = blockMesh; this.predicates = predicates.entrySet().stream().sorted(Map.Entry.comparingByValue()).map(Map.Entry::getKey).toList(); this.sprites = sprites.entrySet().stream().sorted(Map.Entry.comparingByValue()).map(Map.Entry::getKey).toList(); this.hasSpecialQuads = hasSpecialQuads; + this.materialFlags = materialFlags; // Create a mesh of unprocessed quads - List parts = new ArrayList<>(); - for(Map.Entry,List[]> entry : blockMesh.entrySet()){ - Optional layer = entry.getKey(); - //noinspection unchecked - List[] mesh = Arrays.stream(entry.getValue()) - .map(l -> l == null ? List.of() : l.stream().map(q -> q.bakedQuad).toList()) - .toArray(List[]::new); - parts.add(new BlockModelPart() { - @Override - public List getQuads(@Nullable Direction cullDirection){ - return mesh[cullIndex(cullDirection)]; - } + //noinspection unchecked + List[] mesh = Arrays.stream(blockMesh) + .map(l -> l == null ? List.of() : l.stream().map(q -> q.bakedQuad).toList()) + .toArray(List[]::new); + this.completeBlockMesh = new BlockStateModelPart() { + @Override + public List getQuads(@Nullable Direction cullDirection){ + return mesh[cullIndex(cullDirection)]; + } - @Override - public boolean useAmbientOcclusion(){ - return hasAmbientOcclusion; - } + @Override + public boolean useAmbientOcclusion(){ + return hasAmbientOcclusion; + } - @Override - public TextureAtlasSprite particleIcon(){ - return particleIcon; - } + @Override + public Material.Baked particleMaterial(){ + return particleMaterial; + } - @Override - public ChunkSectionLayer getRenderType(BlockState state){ - //noinspection deprecation - return layer.orElseGet(() -> ItemBlockRenderTypes.getChunkRenderType(state)); - } - }); - } - this.completeBlockMesh = List.copyOf(parts); + @Override + public @BakedQuad.MaterialFlags int materialFlags(){ + return ConnectingBakedModel.this.materialFlags; + } + }; } private static TextureOrientation findOrientation(BakedQuad quad){ @@ -252,11 +240,11 @@ private static TextureOrientation findOrientation(BakedQuad quad){ } @Override - public void collectParts(BlockAndTintGetter blockView, BlockPos pos, BlockState state, RandomSource random, List parts){ + public void collectParts(BlockAndTintGetter blockView, BlockPos pos, BlockState state, RandomSource random, List parts){ // If either level or position is not given, the connected textures cannot be updated, so don't process them boolean processConnectingTextures = blockView != null && pos != null && !this.predicates.isEmpty(); if(!processConnectingTextures && !this.hasSpecialQuads){ - parts.addAll(this.completeBlockMesh); + parts.add(this.completeBlockMesh); return; } @@ -274,95 +262,91 @@ public void collectParts(BlockAndTintGetter blockView, BlockPos pos, BlockState // Create one model part for each render type OrientedMutableQuad mutableQuad = new OrientedMutableQuad(); - for(Optional layer : this.blockRenderTypes){ - List[] mesh = this.blockMesh.get(layer); - //noinspection unchecked - List[] processedMesh = new List[mesh.length]; - for(Direction cullDirection : CULL_DIRECTIONS){ - List quads = this.blockMesh.get(layer)[cullIndex(cullDirection)]; - if(quads == null){ - processedMesh[cullIndex(cullDirection)] = List.of(); - continue; + //noinspection unchecked + List[] processedMesh = new List[this.blockMesh.length]; + for(Direction cullDirection : CULL_DIRECTIONS){ + List quads = this.blockMesh[cullIndex(cullDirection)]; + if(quads == null){ + processedMesh[cullIndex(cullDirection)] = List.of(); + continue; + } + ArrayList bakedQuads = new ArrayList<>(quads.size()); + + for(TaggedBakedQuad quad : quads){ + // Process special texture type quads + if(pos != null && (quad.textureType == DefaultTextureTypes.RANDOM || quad.textureType == DefaultTextureTypes.CONTINUOUS)){ + // Get the sprite + TextureAtlasSprite sprite = this.sprites.get(quad.spriteIndex); + + mutableQuad.fillFromBakedQuad(quad.bakedQuad); + mutableQuad.resetPermutation(); + if(quad.textureType == DefaultTextureTypes.RANDOM) + // Handle random texture type + RandomTextureType.processQuad(mutableQuad, pos, quad.bakedQuad.direction(), random, (RandomTextureSprite)sprite); + else + // Handle continuous texture type + ContinuousTextureType.processQuad(mutableQuad, pos, quad.bakedQuad.direction(), (ContinuousTextureSprite)sprite); + bakedQuads.add(mutableQuad.toBakedQuad(null)); } - ArrayList bakedQuads = new ArrayList<>(quads.size()); - - for(TaggedBakedQuad quad : quads){ - // Process special texture type quads - if(pos != null && (quad.textureType == DefaultTextureTypes.RANDOM || quad.textureType == DefaultTextureTypes.CONTINUOUS)){ - // Get the sprite - TextureAtlasSprite sprite = this.sprites.get(quad.spriteIndex); - - mutableQuad.fillFromBakedQuad(quad.bakedQuad); - mutableQuad.resetPermutation(); - if(quad.textureType == DefaultTextureTypes.RANDOM) - // Handle random texture type - RandomTextureType.processQuad(mutableQuad, pos, quad.bakedQuad.direction(), random, (RandomTextureSprite)sprite); - else - // Handle continuous texture type - ContinuousTextureType.processQuad(mutableQuad, pos, quad.bakedQuad.direction(), (ContinuousTextureSprite)sprite); - bakedQuads.add(mutableQuad.toBakedQuad()); + // Process connecting textures + else if(blockCache != null && quad.textureType == DefaultTextureTypes.CONNECTING){ + // Get the quad index, predicate index, and sprite index + int quadIndex = quad.quadIndex; + int predicateIndex = quad.predicateIndex; + int spriteIndex = quad.spriteIndex; + + // Get the connection predicate + QuadPredicates predicate = this.predicates.get(predicateIndex); + // Check if the connections have already been computed, otherwise compute them + TextureConnections connections = connectionsCache[predicateIndex]; + if(connections == null){ + // Compute the connections + connections = connectionsCache[predicateIndex] = computeConnections(predicate, blockCache); } - // Process connecting textures - else if(blockCache != null && quad.textureType == DefaultTextureTypes.CONNECTING){ - // Get the quad index, predicate index, and sprite index - int quadIndex = quad.quadIndex; - int predicateIndex = quad.predicateIndex; - int spriteIndex = quad.spriteIndex; - - // Get the connection predicate - QuadPredicates predicate = this.predicates.get(predicateIndex); - // Check if the connections have already been computed, otherwise compute them - TextureConnections connections = connectionsCache[predicateIndex]; - if(connections == null){ - // Compute the connections - connections = connectionsCache[predicateIndex] = computeConnections(predicate, blockCache); - } - - // Get the sprite and the texture layout - TextureAtlasSprite sprite = this.sprites.get(spriteIndex); - ConnectingTextureLayout layout = ((ConnectingTextureSprite)sprite).data().getLayout(); - - // Remap the quad's uv - mutableQuad.fillFromBakedQuad(quad.bakedQuad); - mutableQuad.set(predicate.orientation.vertexIndexPermutation); - boolean keepQuad = ConnectingTextureLayoutHandler.get(layout).processBlockQuad(quadIndex, mutableQuad, (ConnectingTextureSprite)sprite, connections); - if(keepQuad) - bakedQuads.add(mutableQuad.toBakedQuad()); - }else - bakedQuads.add(quad.bakedQuad); - } - processedMesh[cullIndex(cullDirection)] = bakedQuads; + // Get the sprite and the texture layout + TextureAtlasSprite sprite = this.sprites.get(spriteIndex); + ConnectingTextureLayout layout = ((ConnectingTextureSprite)sprite).data().getLayout(); + + // Remap the quad's uv + mutableQuad.fillFromBakedQuad(quad.bakedQuad); + mutableQuad.set(predicate.orientation.vertexIndexPermutation); + boolean keepQuad = ConnectingTextureLayoutHandler.get(layout).processBlockQuad(quadIndex, mutableQuad, (ConnectingTextureSprite)sprite, connections); + if(keepQuad) + bakedQuads.add(mutableQuad.toBakedQuad(null)); + }else + bakedQuads.add(quad.bakedQuad); } - // Creat the model part for the mesh - parts.add(new BlockModelPart() { - @Override - public List getQuads(@Nullable Direction cullDirection){ - return processedMesh[cullIndex(cullDirection)]; - } + processedMesh[cullIndex(cullDirection)] = bakedQuads; + } - @Override - public boolean useAmbientOcclusion(){ - return ConnectingBakedModel.this.hasAmbientOcclusion; - } + // Creat the model part for the mesh + parts.add(new BlockStateModelPart() { + @Override + public List getQuads(@Nullable Direction cullDirection){ + return processedMesh[cullIndex(cullDirection)]; + } - @Override - public TextureAtlasSprite particleIcon(){ - return ConnectingBakedModel.this.particleIcon; - } + @Override + public boolean useAmbientOcclusion(){ + return ConnectingBakedModel.this.hasAmbientOcclusion; + } - @Override - public ChunkSectionLayer getRenderType(BlockState state){ - //noinspection deprecation - return layer.orElseGet(() -> ItemBlockRenderTypes.getChunkRenderType(state)); - } - }); - } + @Override + public Material.Baked particleMaterial(){ + return ConnectingBakedModel.this.particleMaterial; + } + + @Override + public @BakedQuad.MaterialFlags int materialFlags(){ + return ConnectingBakedModel.this.materialFlags; + } + }); } @Override - public void collectParts(RandomSource random, List parts){ + public void collectParts(RandomSource random, List parts){ this.collectParts(null, null, null, random, parts); } @@ -419,8 +403,13 @@ private static boolean shouldConnect(ConnectionPredicate predicate, SurroundingB } @Override - public TextureAtlasSprite particleIcon(){ - return this.particleIcon; + public Material.Baked particleMaterial(){ + return this.particleMaterial; + } + + @Override + public @BakedQuad.MaterialFlags int materialFlags(){ + return this.materialFlags; } private static int cullIndex(Direction cullDirection){ diff --git a/src/main/java/com/supermartijn642/fusion/model/types/connecting/ConnectingItemModel.java b/src/main/java/com/supermartijn642/fusion/model/types/connecting/ConnectingItemModel.java index a30a864d..8e1c9bb4 100644 --- a/src/main/java/com/supermartijn642/fusion/model/types/connecting/ConnectingItemModel.java +++ b/src/main/java/com/supermartijn642/fusion/model/types/connecting/ConnectingItemModel.java @@ -1,27 +1,25 @@ package com.supermartijn642.fusion.model.types.connecting; import com.google.common.base.Suppliers; -import com.supermartijn642.fusion.api.util.Pair; import com.supermartijn642.fusion.model.types.base.BaseModelQuad; import com.supermartijn642.fusion.texture.types.connecting.ConnectingTextureSprite; import com.supermartijn642.fusion.texture.types.connecting.layouts.ConnectingTextureLayoutHandler; +import it.unimi.dsi.fastutil.ints.IntList; import net.minecraft.client.color.item.ItemTintSource; import net.minecraft.client.multiplayer.ClientLevel; -import net.minecraft.client.renderer.Sheets; -import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.item.CuboidItemModelWrapper; import net.minecraft.client.renderer.item.*; -import net.minecraft.client.renderer.rendertype.RenderType; -import net.minecraft.client.renderer.texture.TextureAtlas; +import net.minecraft.client.resources.model.ModelBaker; +import net.minecraft.client.resources.model.geometry.BakedQuad; import net.minecraft.world.entity.ItemOwner; import net.minecraft.world.item.ItemDisplayContext; import net.minecraft.world.item.ItemStack; import org.jetbrains.annotations.Nullable; +import org.joml.Matrix4fc; import org.joml.Vector3fc; import java.util.ArrayList; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import java.util.function.Supplier; /** @@ -31,57 +29,44 @@ public class ConnectingItemModel implements ItemModel { private final List tints; private final ModelRenderProperties properties; - private final List>> mesh; + private final List mesh = new ArrayList<>(); private final Supplier extents; private final boolean animated; + private final Matrix4fc transformation; - public ConnectingItemModel(List tints, List quads, ModelRenderProperties properties, RenderType neoforgeItemRenderType, RenderType neoforgeBlockRenderType){ + public ConnectingItemModel(List tints, List quads, ModelRenderProperties properties, Matrix4fc transformation, ModelBaker.Interner interner){ this.tints = tints; this.properties = properties; - this.extents = Suppliers.memoize(() -> BlockModelWrapper.computeExtents(quads.stream().map(BaseModelQuad::bakedQuad).toList())); + this.extents = Suppliers.memoize(() -> CuboidItemModelWrapper.computeExtents(quads.stream().map(BaseModelQuad::bakedQuad).toList())); // Create the item mesh - Map> mesh = new LinkedHashMap<>(); OrientedMutableQuad mutableQuad = new OrientedMutableQuad(); + @BakedQuad.MaterialFlags int flags = 0; for(ConnectingModelQuad quad : quads){ // Some layouts need auxiliary quads, hence simply repeat the quad that many times int auxiliaryQuadCount = quad.hasConnectingTexture() ? ConnectingTextureLayoutHandler.get(quad.getLayout()).getAuxiliaryQuadCount() : 0; // Submit the quads for(int quadIndex = 0; quadIndex < auxiliaryQuadCount + 1; quadIndex++){ - mutableQuad.fillFromBakedQuad(quad.bakedQuad()); - mutableQuad.emissive(quad.emissive()); + quad.fill(mutableQuad); // Add the item quad - RenderType renderType = quad.bakedQuad().sprite().atlasLocation().equals(TextureAtlas.LOCATION_BLOCKS) ? - neoforgeBlockRenderType == null || quad.renderType() != null ? Sheets.translucentBlockItemSheet() : neoforgeBlockRenderType : - neoforgeItemRenderType == null || quad.renderType() != null ? Sheets.translucentItemSheet() : neoforgeItemRenderType; - List itemQuads = mesh.computeIfAbsent(renderType, k -> new ArrayList<>()); // Process the quad if it has a connecting texture // As item mesh does not depend on state, we can run the connecting texture processing immediately if(quad.hasConnectingTexture()){ mutableQuad.set(ConnectingBakedModel.TextureOrientation.NORMAL_0.vertexIndexPermutation); - boolean keepQuad = ConnectingTextureLayoutHandler.get(quad.getLayout()).processItemQuad(quadIndex, mutableQuad, (ConnectingTextureSprite)quad.bakedQuad().sprite()); + boolean keepQuad = ConnectingTextureLayoutHandler.get(quad.getLayout()).processItemQuad(quadIndex, mutableQuad, (ConnectingTextureSprite)quad.bakedQuad().materialInfo().sprite()); mutableQuad.resetPermutation(); if(!keepQuad) continue; } - itemQuads.add(mutableQuad.toBakedQuad()); + this.mesh.add(mutableQuad.toBakedQuad(interner)); + flags |= quad.bakedQuad().materialInfo().flags(); } } - this.mesh = mesh.entrySet().stream() - .map(entry -> Pair.of(entry.getKey(), entry.getValue())) - .toList(); // Check whether the quads contain animated textures - boolean animated = false; - for(BaseModelQuad quad : quads){ - //noinspection resource - if(quad.bakedQuad().sprite().contents().isAnimated()){ - animated = true; - break; - } - } - this.animated = animated; + this.animated = (flags & BakedQuad.FLAG_ANIMATED) != 0; + this.transformation = transformation; } @Override @@ -89,27 +74,25 @@ public void update(ItemStackRenderState renderState, ItemStack stack, ItemModelR renderState.appendModelIdentityElement(this); if(this.animated) renderState.setAnimated(); - ItemStackRenderState.FoilType foilType = stack.hasFoil() ? BlockModelWrapper.hasSpecialAnimatedTexture(stack) ? ItemStackRenderState.FoilType.SPECIAL : ItemStackRenderState.FoilType.STANDARD : null; + ItemStackRenderState.FoilType foilType = stack.hasFoil() ? CuboidItemModelWrapper.hasSpecialAnimatedTexture(stack) ? ItemStackRenderState.FoilType.SPECIAL : ItemStackRenderState.FoilType.STANDARD : null; if(foilType != null){ renderState.setAnimated(); renderState.appendModelIdentityElement(foilType); } - int tints = this.tints.size(); - int[] tintValues = new int[tints]; - for(int j = 0; j < tints; j++){ - int tint = this.tints.get(j).calculate(stack, level, owner == null ? null : owner.asLivingEntity()); - tintValues[j] = tint; - renderState.appendModelIdentityElement(tint); - } - for(Pair> pair : this.mesh){ - ItemStackRenderState.LayerRenderState layer = renderState.newLayer(); - if(foilType != null) - layer.setFoilType(foilType); - System.arraycopy(tintValues, 0, layer.prepareTintLayers(tints), 0, tintValues.length); - layer.setExtents(this.extents); - this.properties.applyToLayer(layer, displayContext); - layer.prepareQuadList().addAll(pair.right()); - layer.setRenderType(pair.left()); + ItemStackRenderState.LayerRenderState layer = renderState.newLayer(); + if(foilType != null) + layer.setFoilType(foilType); + if(!this.tints.isEmpty()){ + IntList tintLayers = layer.tintLayers(); + for(ItemTintSource tintSource : this.tints){ + int tint = tintSource.calculate(stack, level, owner == null ? null : owner.asLivingEntity()); + tintLayers.add(tint); + renderState.appendModelIdentityElement(tint); + } } + layer.setExtents(this.extents); + layer.setLocalTransform(this.transformation); + this.properties.applyToLayer(layer, displayContext); + layer.prepareQuadList().addAll(mesh); } } diff --git a/src/main/java/com/supermartijn642/fusion/model/types/connecting/ConnectingModelDataBuilderImpl.java b/src/main/java/com/supermartijn642/fusion/model/types/connecting/ConnectingModelDataBuilderImpl.java index ba70ff7a..032805da 100644 --- a/src/main/java/com/supermartijn642/fusion/model/types/connecting/ConnectingModelDataBuilderImpl.java +++ b/src/main/java/com/supermartijn642/fusion/model/types/connecting/ConnectingModelDataBuilderImpl.java @@ -7,6 +7,7 @@ import com.supermartijn642.fusion.api.predicate.ConnectionPredicate; import com.supermartijn642.fusion.api.util.Either; import com.supermartijn642.fusion.model.types.base.BaseModelDataImpl; +import net.minecraft.client.resources.model.sprite.Material; import net.minecraft.resources.Identifier; import java.util.LinkedHashMap; @@ -40,8 +41,8 @@ public ConnectingModelDataBuilder texture(String key, String reference){ } @Override - public ConnectingModelDataBuilder texture(String key, Identifier texture){ - this.baseModel.texture(key, texture); + public ConnectingModelDataBuilder texture(String key, Material material){ + this.baseModel.texture(key, material); return this; } diff --git a/src/main/java/com/supermartijn642/fusion/model/types/connecting/ConnectingModelDataImpl.java b/src/main/java/com/supermartijn642/fusion/model/types/connecting/ConnectingModelDataImpl.java index 0404df07..9cca0121 100644 --- a/src/main/java/com/supermartijn642/fusion/model/types/connecting/ConnectingModelDataImpl.java +++ b/src/main/java/com/supermartijn642/fusion/model/types/connecting/ConnectingModelDataImpl.java @@ -10,9 +10,12 @@ import com.supermartijn642.fusion.model.types.base.BaseModelDataImpl; import com.supermartijn642.fusion.model.types.base.BaseModelElement; import com.supermartijn642.fusion.model.types.base.BaseModelQuad; -import net.minecraft.client.renderer.block.model.*; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.client.resources.model.cuboid.*; +import net.minecraft.client.resources.model.geometry.BakedQuad; +import net.minecraft.client.resources.model.geometry.QuadCollection; +import net.minecraft.client.resources.model.sprite.Material; +import net.minecraft.client.resources.model.sprite.TextureSlots; import net.minecraft.core.Direction; import net.minecraft.resources.Identifier; @@ -27,7 +30,7 @@ public class ConnectingModelDataImpl extends BaseModelDataImpl implements Connec private final Map> connections; - public ConnectingModelDataImpl(BlockModel model, List parents, List elements, Map> connections){ + public ConnectingModelDataImpl(CuboidModel model, List parents, List elements, Map> connections){ //noinspection rawtypes,unchecked super(model, parents, (List)elements); this.connections = Map.copyOf(connections); @@ -71,9 +74,9 @@ private void bakeQuads(BlockModelBakingContext context, ModelInstance model, // Bake the faces of each element for(BaseModelElement element : elements){ for(Direction direction : element.original.faces().keySet()){ - BlockElementFace face = element.original.faces().get(direction); - TextureAtlasSprite sprite = context.getTexture(this.resolveMaterial(context, modelStack, face.texture())); - BakedQuad quad = FaceBakery.bakeQuad(context.getModelBaker().parts(), element.original.from(), element.original.to(), face, sprite, direction, context.getTransformation(), element.original.rotation(), element.original.shade(), element.original.lightEmission()); + CuboidFace face = element.original.faces().get(direction); + Material.Baked material = context.bakeMaterial(this.resolveMaterial(context, modelStack, face.texture())); + BakedQuad quad = FaceBakery.bakeQuad(context.getModelBaker(), element.original.from(), element.original.to(), face, material, direction, context.getTransformation(), element.original.rotation(), element.original.shade(), element.original.lightEmission()); Direction cullDirection = face.cullForDirection() != null ? Direction.rotate(context.getTransformation().transformation().getMatrix(), face.cullForDirection()) : null; String connectionsKey = element instanceof ConnectingModelElement && ((ConnectingModelElement)element).faceConnectionKeys.containsKey(direction) ? ((ConnectingModelElement)element).faceConnectionKeys.get(direction) : face.texture(); ConnectionPredicate predicate = this.resolveConnectionKey(context, modelStack, connectionsKey); @@ -86,19 +89,22 @@ private void bakeQuads(BlockModelBakingContext context, ModelInstance model, } } - List elements = null; UnbakedModel vanillaModel = model.getAsVanillaModel(); - if(vanillaModel instanceof ItemModelGenerator) - elements = this.generateItemModel(context, modelStack); - else if(vanillaModel != null && vanillaModel.geometry() instanceof SimpleUnbakedGeometry geometry) - elements = geometry.elements(); - if(elements != null && !elements.isEmpty()){ + if(vanillaModel instanceof ItemModelGenerator){ + QuadCollection quads = this.bakeItemModel(context, modelStack); + for(BakedQuad quad : quads.getAll()){ + output.accept(new ConnectingModelQuad(quad, null, null)); + } + // If the model had elements, ignore parent models' elements + modelStack.pop(); + return; + }else if(vanillaModel != null && vanillaModel.geometry() instanceof UnbakedCuboidGeometry geometry && !geometry.elements().isEmpty()){ // Bake the faces of each element - for(BlockElement element : elements){ + for(CuboidModelElement element : geometry.elements()){ for(Direction direction : element.faces().keySet()){ - BlockElementFace face = element.faces().get(direction); - TextureAtlasSprite sprite = context.getTexture(this.resolveMaterial(context, modelStack, face.texture())); - BakedQuad quad = FaceBakery.bakeQuad(context.getModelBaker().parts(), element.from(), element.to(), face, sprite, direction, context.getTransformation(), element.rotation(), element.shade(), element.lightEmission()); + CuboidFace face = element.faces().get(direction); + Material.Baked material = context.bakeMaterial(this.resolveMaterial(context, modelStack, face.texture())); + BakedQuad quad = FaceBakery.bakeQuad(context.getModelBaker(), element.from(), element.to(), face, material, direction, context.getTransformation(), element.rotation(), element.shade(), element.lightEmission()); Direction cullDirection = face.cullForDirection() != null ? Direction.rotate(context.getTransformation().transformation().getMatrix(), face.cullForDirection()) : null; String connectionsKey = face.texture(); ConnectionPredicate predicate = this.resolveConnectionKey(context, modelStack, connectionsKey); @@ -152,8 +158,8 @@ private ConnectionPredicate resolveConnectionKey(BlockModelBakingContext context continue; TextureSlots.SlotContents material = vanillaModel.textureSlots().values().get(currentKey); if(material != null){ - newKey = material instanceof TextureSlots.Value ? - ((TextureSlots.Value)material).material().texture().toString() : + newKey = material instanceof TextureSlots.Value(Material value) ? + value.sprite().toString() : ((TextureSlots.Reference)material).target(); break; } diff --git a/src/main/java/com/supermartijn642/fusion/model/types/connecting/ConnectingModelElement.java b/src/main/java/com/supermartijn642/fusion/model/types/connecting/ConnectingModelElement.java index 93d25983..e9a47c98 100644 --- a/src/main/java/com/supermartijn642/fusion/model/types/connecting/ConnectingModelElement.java +++ b/src/main/java/com/supermartijn642/fusion/model/types/connecting/ConnectingModelElement.java @@ -1,8 +1,8 @@ package com.supermartijn642.fusion.model.types.connecting; import com.supermartijn642.fusion.model.types.base.BaseModelElement; -import net.minecraft.client.renderer.block.model.BlockElementFace; -import net.minecraft.client.renderer.block.model.BlockElementRotation; +import net.minecraft.client.resources.model.cuboid.CuboidFace; +import net.minecraft.client.resources.model.cuboid.CuboidRotation; import net.minecraft.core.Direction; import org.jetbrains.annotations.Nullable; import org.joml.Vector3fc; @@ -17,7 +17,7 @@ public class ConnectingModelElement extends BaseModelElement { public final Map faceConnectionKeys; - public ConnectingModelElement(Vector3fc from, Vector3fc to, Map faces, @Nullable BlockElementRotation rotation, boolean shade, int lightEmission, Map faceConnectionKeys){ + public ConnectingModelElement(Vector3fc from, Vector3fc to, Map faces, @Nullable CuboidRotation rotation, boolean shade, int lightEmission, Map faceConnectionKeys){ super(from, to, faces, rotation, shade, lightEmission); this.faceConnectionKeys = faceConnectionKeys == null ? Collections.emptyMap() : Collections.unmodifiableMap(faceConnectionKeys); } diff --git a/src/main/java/com/supermartijn642/fusion/model/types/connecting/ConnectingModelQuad.java b/src/main/java/com/supermartijn642/fusion/model/types/connecting/ConnectingModelQuad.java index 5172df55..10fd24ee 100644 --- a/src/main/java/com/supermartijn642/fusion/model/types/connecting/ConnectingModelQuad.java +++ b/src/main/java/com/supermartijn642/fusion/model/types/connecting/ConnectingModelQuad.java @@ -5,8 +5,8 @@ import com.supermartijn642.fusion.api.texture.data.ConnectingTextureLayout; import com.supermartijn642.fusion.model.types.base.BaseModelQuad; import com.supermartijn642.fusion.texture.types.connecting.ConnectingTextureSprite; -import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.geometry.BakedQuad; import net.minecraft.core.Direction; /** @@ -19,7 +19,7 @@ public class ConnectingModelQuad extends BaseModelQuad { public ConnectingModelQuad(BakedQuad bakedQuad, Direction cullDirection, ConnectionPredicate connectionPredicate){ super(bakedQuad, cullDirection); - TextureAtlasSprite sprite = bakedQuad.sprite(); + TextureAtlasSprite sprite = bakedQuad.materialInfo().sprite(); if(sprite instanceof ConnectingTextureSprite && ((ConnectingTextureSprite)sprite).data() != null){ ConnectingTextureData data = ((ConnectingTextureSprite)sprite).data(); this.predicate = connectionPredicate; diff --git a/src/main/java/com/supermartijn642/fusion/model/types/connecting/ConnectingModelType.java b/src/main/java/com/supermartijn642/fusion/model/types/connecting/ConnectingModelType.java index 45e6a4d1..95ace22d 100644 --- a/src/main/java/com/supermartijn642/fusion/model/types/connecting/ConnectingModelType.java +++ b/src/main/java/com/supermartijn642/fusion/model/types/connecting/ConnectingModelType.java @@ -16,21 +16,18 @@ import com.supermartijn642.fusion.api.util.Either; import com.supermartijn642.fusion.model.types.base.BaseModelDataImpl; import com.supermartijn642.fusion.model.types.base.BaseModelElement; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.client.renderer.block.model.BlockStateModel; -import net.minecraft.client.renderer.block.model.ItemTransforms; -import net.minecraft.client.renderer.chunk.ChunkSectionLayer; +import net.minecraft.client.renderer.block.dispatch.BlockStateModel; import net.minecraft.client.renderer.item.ItemModel; import net.minecraft.client.renderer.item.ModelRenderProperties; -import net.minecraft.client.renderer.rendertype.RenderType; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.client.resources.model.cuboid.CuboidModel; +import net.minecraft.client.resources.model.cuboid.ItemTransforms; +import net.minecraft.client.resources.model.sprite.Material; import net.minecraft.core.Direction; import net.minecraft.resources.Identifier; import net.minecraft.world.item.ItemDisplayContext; -import net.neoforged.neoforge.client.RenderTypeGroup; -import net.neoforged.neoforge.client.model.NeoForgeModelProperties; import org.jetbrains.annotations.Nullable; +import org.joml.Matrix4fc; import java.util.*; @@ -66,28 +63,26 @@ public BlockStateModel bakeBlockModel(BlockModelBakingContext context, Connectin List quads = (List)((ConnectingModelDataImpl)data).bakeQuads(context); // Gather remaining model properties boolean ambientOcclusion = ((BaseModelDataImpl)data).findProperty(context, UnbakedModel::ambientOcclusion, true); - TextureAtlasSprite particleSprite = context.getTexture(((BaseModelDataImpl)data).findParticleSprite(context)); - RenderTypeGroup neoforgeRenderTypeGroup = context.getNeoForgeAdditionalProperties().getOptional(NeoForgeModelProperties.RENDER_TYPE); - ChunkSectionLayer neoforgeRenderType = neoforgeRenderTypeGroup == null ? null : neoforgeRenderTypeGroup.block(); + Material.Baked particleMaterial = context.bakeMaterial(((BaseModelDataImpl)data).findParticleMaterial(context)); // Finally, create the model return new ConnectingBakedModel( quads, ambientOcclusion, - particleSprite, - neoforgeRenderType + particleMaterial, + context.getModelBaker().interner() ); } @Override - public ItemModel bakeItemModel(ItemModelBakingContext context, ConnectingModelData data){ + public ItemModel bakeItemModel(ItemModelBakingContext context, ConnectingModelData data, Matrix4fc transformation){ // Check for circular dependencies ((ConnectingModelDataImpl)data).validateParents(context); // Bake the quads //noinspection unchecked,rawtypes List quads = (List)((ConnectingModelDataImpl)data).bakeQuads(context); // Gather remaining model properties - boolean usesBlockLight = ((BaseModelDataImpl)data).findProperty(context, UnbakedModel::guiLight, BlockModel.GuiLight.SIDE).lightLikeBlock(); - TextureAtlasSprite particleSprite = context.getTexture(((BaseModelDataImpl)data).findParticleSprite(context)); + boolean usesBlockLight = ((BaseModelDataImpl)data).findProperty(context, UnbakedModel::guiLight, CuboidModel.GuiLight.SIDE).lightLikeBlock(); + Material.Baked particleMaterial = context.bakeMaterial(((BaseModelDataImpl)data).findParticleMaterial(context)); //noinspection deprecation ItemTransforms transforms = new ItemTransforms( ((BaseModelDataImpl)data).findItemTransform(context, ItemDisplayContext.THIRD_PERSON_LEFT_HAND), @@ -100,16 +95,13 @@ public ItemModel bakeItemModel(ItemModelBakingContext context, ConnectingModelDa ((BaseModelDataImpl)data).findItemTransform(context, ItemDisplayContext.FIXED), ((BaseModelDataImpl)data).findItemTransform(context, ItemDisplayContext.ON_SHELF) ); - RenderTypeGroup neoforgeRenderTypeGroup = context.getNeoForgeAdditionalProperties().getOptional(NeoForgeModelProperties.RENDER_TYPE); - RenderType neoforgeItemRenderType = neoforgeRenderTypeGroup == null ? null : neoforgeRenderTypeGroup.entityItem(); - RenderType neoforgeBlockRenderType = neoforgeRenderTypeGroup == null ? null : neoforgeRenderTypeGroup.entityBlock(); // Finally, create the model return new ConnectingItemModel( context.getTintSources(), quads, - new ModelRenderProperties(usesBlockLight, particleSprite, transforms), - neoforgeItemRenderType, - neoforgeBlockRenderType + new ModelRenderProperties(usesBlockLight, particleMaterial, transforms), + transformation, + context.getModelBaker().interner() ); } diff --git a/src/main/java/com/supermartijn642/fusion/model/types/connecting/SurroundingBlockCache.java b/src/main/java/com/supermartijn642/fusion/model/types/connecting/SurroundingBlockCache.java index 4ca6afd7..b28372a1 100644 --- a/src/main/java/com/supermartijn642/fusion/model/types/connecting/SurroundingBlockCache.java +++ b/src/main/java/com/supermartijn642/fusion/model/types/connecting/SurroundingBlockCache.java @@ -1,7 +1,7 @@ package com.supermartijn642.fusion.model.types.connecting; +import net.minecraft.client.renderer.block.BlockAndTintGetter; import net.minecraft.core.BlockPos; -import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.block.state.BlockState; /** diff --git a/src/main/java/com/supermartijn642/fusion/model/types/vanilla/VanillaModelDataBuilderImpl.java b/src/main/java/com/supermartijn642/fusion/model/types/vanilla/VanillaModelDataBuilderImpl.java index bee16459..f0afe9ad 100644 --- a/src/main/java/com/supermartijn642/fusion/model/types/vanilla/VanillaModelDataBuilderImpl.java +++ b/src/main/java/com/supermartijn642/fusion/model/types/vanilla/VanillaModelDataBuilderImpl.java @@ -1,10 +1,9 @@ package com.supermartijn642.fusion.model.types.vanilla; import com.supermartijn642.fusion.api.model.data.VanillaModelDataBuilder; -import com.supermartijn642.fusion.util.TextureAtlases; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.client.renderer.block.model.TextureSlots; -import net.minecraft.client.resources.model.Material; +import net.minecraft.client.resources.model.cuboid.CuboidModel; +import net.minecraft.client.resources.model.sprite.Material; +import net.minecraft.client.resources.model.sprite.TextureSlots; import net.minecraft.resources.Identifier; import java.util.HashMap; @@ -13,9 +12,9 @@ /** * Created 01/05/2023 by SuperMartijn642 */ -public class VanillaModelDataBuilderImpl implements VanillaModelDataBuilder { +public class VanillaModelDataBuilderImpl implements VanillaModelDataBuilder { - private final Map textures = new HashMap<>(); + private final Map textures = new HashMap<>(); private Identifier parent; @Override @@ -29,36 +28,30 @@ public VanillaModelDataBuilderImpl texture(String key, String reference){ if(!key.matches("[a-zA-Z_]*")) throw new IllegalArgumentException("Texture reference must only contain characters [a-zA-Z_]!"); - // Prepend '#' character - if(reference.charAt(0) != '#') - reference = '#' + reference; + // Strip '#' character + if(reference.charAt(0) == '#') + reference = reference.substring(1); if(this.textures.containsKey(key)) throw new RuntimeException("Duplicate texture entry for key '" + key + "': '" + this.textures.get(key) + "' and '" + reference + "'!"); - this.textures.put(key, reference); + this.textures.put(key, new TextureSlots.Reference(reference)); return this; } @Override - public VanillaModelDataBuilderImpl texture(String key, Identifier texture){ + public VanillaModelDataBuilderImpl texture(String key, Material material){ if(!key.matches("[a-zA-Z_]*")) throw new IllegalArgumentException("Texture reference must only contain characters [a-zA-Z_]!"); if(this.textures.containsKey(key)) - throw new RuntimeException("Duplicate texture entry for key '" + key + "': '" + this.textures.get(key) + "' and '" + texture + "'!"); + throw new RuntimeException("Duplicate texture entry for key '" + key + "': '" + this.textures.get(key) + "' and '" + material + "'!"); - this.textures.put(key, texture.toString()); + this.textures.put(key, new TextureSlots.Value(material)); return this; } @Override - public BlockModel build(){ - TextureSlots.Data.Builder textures = new TextureSlots.Data.Builder(); - this.textures.forEach((key, value) -> { - if(value.charAt(0) == '#') - textures.addReference(key, value); - else - textures.addTexture(key, new Material(TextureAtlases.getBlocks(), Identifier.parse(value))); - }); - return new BlockModel(null, null, null, null, textures.build(), this.parent); + public CuboidModel build(){ + TextureSlots.Data textures = new TextureSlots.Data(Map.copyOf(this.textures)); + return new CuboidModel(null, null, null, null, textures, this.parent); } } diff --git a/src/main/java/com/supermartijn642/fusion/model/types/vanilla/VanillaModelSerializer.java b/src/main/java/com/supermartijn642/fusion/model/types/vanilla/VanillaModelSerializer.java index 2e5c8aa8..8395efdf 100644 --- a/src/main/java/com/supermartijn642/fusion/model/types/vanilla/VanillaModelSerializer.java +++ b/src/main/java/com/supermartijn642/fusion/model/types/vanilla/VanillaModelSerializer.java @@ -1,9 +1,11 @@ package com.supermartijn642.fusion.model.types.vanilla; import com.google.gson.*; -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.client.renderer.block.model.TextureSlots; +import com.mojang.serialization.JsonOps; import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.client.resources.model.cuboid.CuboidModel; +import net.minecraft.client.resources.model.sprite.Material; +import net.minecraft.client.resources.model.sprite.TextureSlots; import net.minecraft.resources.Identifier; import java.lang.reflect.Type; @@ -12,22 +14,25 @@ /** * Created 02/05/2023 by SuperMartijn642 */ -public class VanillaModelSerializer implements JsonSerializer { +public class VanillaModelSerializer implements JsonSerializer { - public static final Gson GSON = new GsonBuilder().registerTypeAdapter(BlockModel.class, new VanillaModelSerializer()).disableHtmlEscaping().setPrettyPrinting().create(); + public static final Gson GSON = new GsonBuilder().registerTypeAdapter(CuboidModel.class, new VanillaModelSerializer()).disableHtmlEscaping().setPrettyPrinting().create(); private VanillaModelSerializer(){ } @Override - public JsonElement serialize(BlockModel src, Type typeOfSrc, JsonSerializationContext context){ + public JsonElement serialize(CuboidModel src, Type typeOfSrc, JsonSerializationContext context){ JsonObject json = new JsonObject(); Identifier parent = src.parent(); if(parent != null) json.addProperty("parent", parent.toString()); if(!src.textureSlots().values().isEmpty()){ JsonObject textures = new JsonObject(); - src.textureSlots().values().forEach((key, texture) -> textures.addProperty(key, texture instanceof TextureSlots.Value ? ((TextureSlots.Value)texture).material().texture().toString() : '#' + ((TextureSlots.Reference)texture).target())); + src.textureSlots().values().forEach((key, texture) -> textures.add(key, switch (texture){ + case TextureSlots.Reference(String target) -> new JsonPrimitive("#" + target); + case TextureSlots.Value(Material material) -> Material.CODEC.encodeStart(JsonOps.INSTANCE, material).getOrThrow(); + })); json.add("textures", textures); } if(src.ambientOcclusion() != null) diff --git a/src/main/java/com/supermartijn642/fusion/model/types/vanilla/VanillaModelType.java b/src/main/java/com/supermartijn642/fusion/model/types/vanilla/VanillaModelType.java index 8d3544d7..23cebe56 100644 --- a/src/main/java/com/supermartijn642/fusion/model/types/vanilla/VanillaModelType.java +++ b/src/main/java/com/supermartijn642/fusion/model/types/vanilla/VanillaModelType.java @@ -3,20 +3,20 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.supermartijn642.fusion.model.types.UnknownModelType; -import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.cuboid.CuboidModel; /** * Created 29/04/2023 by SuperMartijn642 */ -public class VanillaModelType extends UnknownModelType { +public class VanillaModelType extends UnknownModelType { @Override - public BlockModel deserialize(JsonObject json) throws JsonParseException{ - return BlockModel.GSON.fromJson(json, BlockModel.class); + public CuboidModel deserialize(JsonObject json) throws JsonParseException{ + return CuboidModel.GSON.fromJson(json, CuboidModel.class); } @Override - public JsonObject serialize(BlockModel value){ + public JsonObject serialize(CuboidModel value){ return (JsonObject)VanillaModelSerializer.GSON.toJsonTree(value); } } diff --git a/src/main/java/com/supermartijn642/fusion/resources/MinimumVersionWarningScreen.java b/src/main/java/com/supermartijn642/fusion/resources/MinimumVersionWarningScreen.java index b9d78a37..f1a9ffdd 100644 --- a/src/main/java/com/supermartijn642/fusion/resources/MinimumVersionWarningScreen.java +++ b/src/main/java/com/supermartijn642/fusion/resources/MinimumVersionWarningScreen.java @@ -6,15 +6,15 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.gui.ActiveTextCollector; import net.minecraft.client.gui.Font; -import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.TextAlignment; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.MultiLineLabel; -import net.minecraft.client.gui.render.state.GuiTextRenderState; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.packs.PackSelectionModel; import net.minecraft.client.input.KeyEvent; import net.minecraft.client.renderer.RenderPipelines; +import net.minecraft.client.renderer.state.gui.GuiTextRenderState; import net.minecraft.locale.Language; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.FormattedText; @@ -95,8 +95,8 @@ protected void init(){ } @Override - public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks){ - super.render(graphics, mouseX, mouseY, partialTicks); + public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTicks){ + super.extractRenderState(graphics, mouseX, mouseY, partialTicks); Matrix3x2fStack poseStack = graphics.pose(); poseStack.pushMatrix(); poseStack.translate(this.width / 2f, this.height / 2f - 110); @@ -104,7 +104,7 @@ public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTi // Title int titleLeft = -(this.titleWidth + 17) / 2; graphics.blit(FUSION_LOGO, titleLeft, 0, 0, 0, 12, 12, 12, 12); - graphics.drawString(this.font, this.title, titleLeft + 17, 2, -1); + graphics.text(this.font, this.title, titleLeft + 17, 2, -1); // Content poseStack.popMatrix(); @@ -114,7 +114,7 @@ public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTi graphics.fill(-98, 0, 98, 36, ARGB.color(70, 255, 255, 255)); graphics.blit(RenderPipelines.GUI_TEXTURED, this.pack.getIconTexture(), -96, 2, 0, 0, 32, 32, 32, 32); - graphics.drawString(this.font, this.packName, -62, 3, ARGB.color(255, 16777215)); + graphics.text(this.font, this.packName, -62, 3, ARGB.color(255, 16777215)); ActiveTextCollector dummyTextCollector = graphics.textRenderer(); this.packDescription.visitLines(TextAlignment.LEFT, -62, 14, 10, new ActiveTextCollector() { // I want not-white text, so now I have to do this garbage @Override @@ -134,7 +134,7 @@ public void accept(TextAlignment alignment, int x, int y, Parameters parameters, Minecraft.getInstance().font, text, parameters.pose(), left, y, ARGB.color(parameters.opacity(), -8355712), 0, true, true, parameters.scissor() ); if(ARGB.as8BitChannel(parameters.opacity()) != 0) - graphics.guiRenderState.submitText(textRenderState); + graphics.guiRenderState.addText(textRenderState); ActiveTextCollector.findElementUnderCursor(textRenderState, mouseX, mouseY, s -> {}); } @@ -145,7 +145,7 @@ public void acceptScrolling(Component component, int i, int j, int k, int l, int } }); - graphics.hLine(-115, 115, 44, ARGB.color(255, 255, 255)); + graphics.horizontalLine(-115, 115, 44, ARGB.color(255, 255, 255)); int textLeft = -Math.max(this.headerMessage.getWidth(), this.confirmationMessage.getWidth()) / 2; this.headerMessage.visitLines(TextAlignment.LEFT, textLeft, 54, 10, graphics.textRenderer()); @@ -153,13 +153,13 @@ public void acceptScrolling(Component component, int i, int j, int k, int l, int this.confirmationMessage.visitLines(TextAlignment.LEFT, textLeft, 58 + textHeight, 10, graphics.textRenderer()); textHeight += this.confirmationMessage.getLineCount() * 10; - graphics.hLine(-115, 115, 66 + textHeight, ARGB.color(255, 255, 255)); + graphics.horizontalLine(-115, 115, 66 + textHeight, ARGB.color(255, 255, 255)); textLeft = -(this.versionLabelTextWidth + 5 + this.versionTextWidth) / 2; - graphics.drawString(this.font, this.currentVersionLabel, textLeft, 76 + textHeight, ARGB.color(180, 180, 180)); - graphics.drawString(this.font, this.requiredVersionLabel, textLeft, 88 + textHeight, ARGB.color(180, 180, 180)); - graphics.drawString(this.font, this.currentVersion, textLeft + this.versionLabelTextWidth + 5, 76 + textHeight, ARGB.color(255, 16777215)); - graphics.drawString(this.font, this.requiredVersion, textLeft + this.versionLabelTextWidth + 5, 88 + textHeight, ARGB.color(255, 16777215)); + graphics.text(this.font, this.currentVersionLabel, textLeft, 76 + textHeight, ARGB.color(180, 180, 180)); + graphics.text(this.font, this.requiredVersionLabel, textLeft, 88 + textHeight, ARGB.color(180, 180, 180)); + graphics.text(this.font, this.currentVersion, textLeft + this.versionLabelTextWidth + 5, 76 + textHeight, ARGB.color(255, 16777215)); + graphics.text(this.font, this.requiredVersion, textLeft + this.versionLabelTextWidth + 5, 88 + textHeight, ARGB.color(255, 16777215)); poseStack.popMatrix(); } diff --git a/src/main/java/com/supermartijn642/fusion/resources/ResourcePackListTipRenderer.java b/src/main/java/com/supermartijn642/fusion/resources/ResourcePackListTipRenderer.java index 19ae2cfa..b6298afb 100644 --- a/src/main/java/com/supermartijn642/fusion/resources/ResourcePackListTipRenderer.java +++ b/src/main/java/com/supermartijn642/fusion/resources/ResourcePackListTipRenderer.java @@ -2,7 +2,7 @@ import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.screens.packs.PackSelectionModel; import net.minecraft.client.renderer.RenderPipelines; import net.minecraft.network.chat.Component; @@ -18,12 +18,12 @@ public class ResourcePackListTipRenderer { private static final Identifier FUSION_LOGO = Identifier.fromNamespaceAndPath("fusion", "textures/resourcepacks/fusion_icon_blurred.png"); - public static void renderBackground(FusionPackMetadata metadata, boolean isVanillaCompatible, GuiGraphics graphics, int x, int y, int width, int height){ + public static void renderBackground(FusionPackMetadata metadata, boolean isVanillaCompatible, GuiGraphicsExtractor graphics, int x, int y, int width, int height){ if(isVanillaCompatible && !metadata.isMinVersionSatisfied()) graphics.fill(x - 1, y - 1, x + width + 1, y + height + 1, ARGB.color(255, 114, 83, 0)); } - public static void renderIcon(FusionPackMetadata metadata, boolean isVanillaCompatible, GuiGraphics graphics, int x, int y, int width, int height){ + public static void renderIcon(FusionPackMetadata metadata, boolean isVanillaCompatible, GuiGraphicsExtractor graphics, int x, int y, int width, int height){ graphics.blit(RenderPipelines.GUI_TEXTURED, FUSION_LOGO, x, y, 0, 0, 12, 12, 12, 12); } diff --git a/src/main/java/com/supermartijn642/fusion/texture/QuadTintingHelper.java b/src/main/java/com/supermartijn642/fusion/texture/QuadTintingHelper.java index 24cfa50d..78fa48b1 100644 --- a/src/main/java/com/supermartijn642/fusion/texture/QuadTintingHelper.java +++ b/src/main/java/com/supermartijn642/fusion/texture/QuadTintingHelper.java @@ -1,12 +1,11 @@ package com.supermartijn642.fusion.texture; import com.supermartijn642.fusion.api.texture.data.BaseTextureData; -import net.minecraft.client.color.block.BlockColor; -import net.minecraft.client.renderer.BiomeColors; +import net.minecraft.client.color.block.BlockTintSource; +import net.minecraft.client.color.block.BlockTintSources; +import net.minecraft.client.renderer.block.BlockAndTintGetter; import net.minecraft.core.BlockPos; -import net.minecraft.world.level.BlockAndTintGetter; -import net.minecraft.world.level.FoliageColor; -import net.minecraft.world.level.GrassColor; +import net.minecraft.util.ARGB; import net.minecraft.world.level.block.state.BlockState; /** @@ -17,22 +16,10 @@ public class QuadTintingHelper { /** * @see com.supermartijn642.fusion.api.texture.data.BaseTextureData.QuadTinting */ - private static final BlockColor[] TINT_FUNCTIONS = new BlockColor[]{ - (state, level, pos, tintIndex) -> { - if(level == null || pos == null) - return GrassColor.getDefaultColor(); - return BiomeColors.getAverageGrassColor(level, pos); - }, - (state, level, pos, tintIndex) -> { - if(level == null || pos == null) - return FoliageColor.FOLIAGE_DEFAULT; - return BiomeColors.getAverageFoliageColor(level, pos); - }, - (state, level, pos, tintIndex) -> { - if(level == null || pos == null) - return -1; - return BiomeColors.getAverageWaterColor(level, pos); - } + private static final BlockTintSource[] TINT_FUNCTIONS = new BlockTintSource[]{ + BlockTintSources.grass(), + BlockTintSources.foliage(), + BlockTintSources.water() }; static{ @@ -41,7 +28,7 @@ public class QuadTintingHelper { } public static int getColor(BaseTextureData.QuadTinting tinting, BlockState state, BlockAndTintGetter level, BlockPos pos){ - BlockColor tintFunction = TINT_FUNCTIONS[tinting.ordinal()]; - return tintFunction.getColor(state, level, pos, 0) | 0xff000000; + BlockTintSource tintFunction = TINT_FUNCTIONS[tinting.ordinal()]; + return ARGB.opaque(tintFunction.colorInWorld(state, level, pos)); } } diff --git a/src/main/java/com/supermartijn642/fusion/texture/types/base/BaseTextureDataBuilderImpl.java b/src/main/java/com/supermartijn642/fusion/texture/types/base/BaseTextureDataBuilderImpl.java index 66864cc7..26711fe7 100644 --- a/src/main/java/com/supermartijn642/fusion/texture/types/base/BaseTextureDataBuilderImpl.java +++ b/src/main/java/com/supermartijn642/fusion/texture/types/base/BaseTextureDataBuilderImpl.java @@ -8,16 +8,9 @@ */ public class BaseTextureDataBuilderImpl implements BaseTextureData.Builder { - private BaseTextureData.RenderType renderType; private boolean emissive = false; private BaseTextureData.QuadTinting tinting; - @Override - public BaseTextureDataBuilderImpl renderType(@Nullable BaseTextureData.RenderType renderType){ - this.renderType = renderType; - return this; - } - @Override public BaseTextureDataBuilderImpl emissive(boolean emissive){ this.emissive = emissive; @@ -32,6 +25,6 @@ public BaseTextureDataBuilderImpl tinting(BaseTextureData.@Nullable QuadTinting @Override public BaseTextureData build(){ - return new BaseTextureDataImpl(this.renderType, this.emissive, this.tinting); + return new BaseTextureDataImpl(this.emissive, this.tinting); } } diff --git a/src/main/java/com/supermartijn642/fusion/texture/types/base/BaseTextureDataImpl.java b/src/main/java/com/supermartijn642/fusion/texture/types/base/BaseTextureDataImpl.java index cb58e018..a50fbf3c 100644 --- a/src/main/java/com/supermartijn642/fusion/texture/types/base/BaseTextureDataImpl.java +++ b/src/main/java/com/supermartijn642/fusion/texture/types/base/BaseTextureDataImpl.java @@ -8,21 +8,14 @@ */ public class BaseTextureDataImpl implements BaseTextureData { - private final RenderType renderType; private final boolean emissive; private final QuadTinting tinting; - public BaseTextureDataImpl(RenderType renderType, boolean emissive, QuadTinting tinting){ - this.renderType = renderType; + public BaseTextureDataImpl(boolean emissive, QuadTinting tinting){ this.emissive = emissive; this.tinting = tinting; } - @Override - public @Nullable RenderType getRenderType(){ - return this.renderType; - } - @Override public boolean isEmissive(){ return this.emissive; diff --git a/src/main/java/com/supermartijn642/fusion/texture/types/base/BaseTextureSprite.java b/src/main/java/com/supermartijn642/fusion/texture/types/base/BaseTextureSprite.java index d0d509e1..f5d9f41c 100644 --- a/src/main/java/com/supermartijn642/fusion/texture/types/base/BaseTextureSprite.java +++ b/src/main/java/com/supermartijn642/fusion/texture/types/base/BaseTextureSprite.java @@ -1,5 +1,6 @@ package com.supermartijn642.fusion.texture.types.base; +import com.mojang.blaze3d.platform.Transparency; import com.supermartijn642.fusion.api.texture.data.BaseTextureData; import net.minecraft.client.renderer.texture.SpriteContents; import net.minecraft.client.renderer.texture.TextureAtlasSprite; @@ -20,4 +21,32 @@ public BaseTextureSprite(Identifier atlas, SpriteContents contents, int atlasWid public BaseTextureData data(){ return this.data; } + + public Transparency computeTransparency(float u0, float v0, float u1, float v1){ + return this.contents().computeTransparency(u0, v0, u1, v1); + } + + public static Transparency computeTiledTransparency(TextureAtlasSprite sprite, float u0, float v0, float u1, float v1, int columns, int rows){ + Transparency spriteTransparency = sprite.contents().transparency(); + if(spriteTransparency.isOpaque()){ + return spriteTransparency; + }else if(u0 == 0.0f && v0 == 0.0f && u1 == 1.0f && v1 == 1.0f){ + return spriteTransparency; + } + Transparency transparency = Transparency.NONE; + for(int y = 0; y < rows; y++){ + for(int x = 0; x < columns; x++){ + transparency = transparency.or(sprite.contents().computeTransparency( + (u0 + x) / columns, + (v0 + y) / rows, + (u1 + x) / columns, + (v1 + y) / rows + )); + if(transparency.hasTranslucent()){ + return transparency; + } + } + } + return transparency; + } } diff --git a/src/main/java/com/supermartijn642/fusion/texture/types/base/BaseTextureType.java b/src/main/java/com/supermartijn642/fusion/texture/types/base/BaseTextureType.java index 098cdd61..c846a568 100644 --- a/src/main/java/com/supermartijn642/fusion/texture/types/base/BaseTextureType.java +++ b/src/main/java/com/supermartijn642/fusion/texture/types/base/BaseTextureType.java @@ -45,19 +45,6 @@ public TextureAtlasSprite createSprite(SpriteCreationContext context, BaseTextur @Override public BaseTextureData deserialize(JsonObject json) throws JsonParseException{ BaseTextureData.Builder builder = BaseTextureData.builder(); - // render_type - if(json.has("render_type")){ - if(!json.get("render_type").isJsonPrimitive() || !json.getAsJsonPrimitive("render_type").isString()) - throw new JsonParseException("Property 'render_type' must be a string!"); - String renderTypeString = json.get("render_type").getAsString(); - BaseTextureData.RenderType renderType; - try{ - renderType = BaseTextureData.RenderType.valueOf(renderTypeString.toUpperCase(Locale.ROOT)); - }catch(IllegalArgumentException e){ - throw new JsonParseException("Property 'render_type' must be one of " + Arrays.toString(BaseTextureData.RenderType.values()).toLowerCase(Locale.ROOT) + ", not '" + renderTypeString + "'!"); - } - builder.renderType(renderType); - } // emissive if(json.has("emissive")){ if(!json.get("emissive").isJsonPrimitive() || !json.getAsJsonPrimitive("emissive").isBoolean()) @@ -83,8 +70,6 @@ public BaseTextureData deserialize(JsonObject json) throws JsonParseException{ @Override public JsonObject serialize(BaseTextureData value){ JsonObject json = new JsonObject(); - if(value.getRenderType() != null) - json.addProperty("render_type", value.getRenderType().name().toLowerCase()); if(value.isEmissive()) json.addProperty("emissive", true); if(value.getTinting() != null) diff --git a/src/main/java/com/supermartijn642/fusion/texture/types/connecting/ConnectingTextureDataBuilderImpl.java b/src/main/java/com/supermartijn642/fusion/texture/types/connecting/ConnectingTextureDataBuilderImpl.java index 11cd28f8..6945bd40 100644 --- a/src/main/java/com/supermartijn642/fusion/texture/types/connecting/ConnectingTextureDataBuilderImpl.java +++ b/src/main/java/com/supermartijn642/fusion/texture/types/connecting/ConnectingTextureDataBuilderImpl.java @@ -10,17 +10,10 @@ */ public class ConnectingTextureDataBuilderImpl implements ConnectingTextureData.Builder { - private BaseTextureData.RenderType renderType; private boolean emissive = false; private BaseTextureData.QuadTinting tinting; private ConnectingTextureLayout layout = ConnectingTextureLayout.FULL; - @Override - public ConnectingTextureDataBuilderImpl renderType(@Nullable BaseTextureData.RenderType renderType){ - this.renderType = renderType; - return this; - } - @Override public ConnectingTextureDataBuilderImpl emissive(boolean emissive){ this.emissive = emissive; @@ -39,23 +32,8 @@ public ConnectingTextureData.Builder layout(ConnectingTextureLayout layout){ return this; } - @Override - public ConnectingTextureData.Builder renderType(@Nullable ConnectingTextureData.RenderType type){ - if(type == null) - return this.renderType((BaseTextureData.RenderType)null); - switch(type){ - case OPAQUE: - return this.renderType(BaseTextureData.RenderType.OPAQUE); - case CUTOUT: - return this.renderType(BaseTextureData.RenderType.CUTOUT); - case TRANSLUCENT: - return this.renderType(BaseTextureData.RenderType.TRANSLUCENT); - } - return this; - } - @Override public ConnectingTextureData build(){ - return new ConnectingTextureDataImpl(this.renderType, this.emissive, this.tinting, this.layout); + return new ConnectingTextureDataImpl(this.emissive, this.tinting, this.layout); } } diff --git a/src/main/java/com/supermartijn642/fusion/texture/types/connecting/ConnectingTextureDataImpl.java b/src/main/java/com/supermartijn642/fusion/texture/types/connecting/ConnectingTextureDataImpl.java index c3ee94b3..e47062f5 100644 --- a/src/main/java/com/supermartijn642/fusion/texture/types/connecting/ConnectingTextureDataImpl.java +++ b/src/main/java/com/supermartijn642/fusion/texture/types/connecting/ConnectingTextureDataImpl.java @@ -1,6 +1,5 @@ package com.supermartijn642.fusion.texture.types.connecting; -import com.supermartijn642.fusion.api.texture.data.BaseTextureData; import com.supermartijn642.fusion.api.texture.data.ConnectingTextureData; import com.supermartijn642.fusion.api.texture.data.ConnectingTextureLayout; import com.supermartijn642.fusion.texture.types.base.BaseTextureDataImpl; @@ -12,8 +11,8 @@ public class ConnectingTextureDataImpl extends BaseTextureDataImpl implements Co private final ConnectingTextureLayout layout; - public ConnectingTextureDataImpl(BaseTextureData.RenderType renderType, boolean emissive, QuadTinting tinting, ConnectingTextureLayout layout){ - super(renderType, emissive, tinting); + public ConnectingTextureDataImpl(boolean emissive, QuadTinting tinting, ConnectingTextureLayout layout){ + super(emissive, tinting); this.layout = layout; } diff --git a/src/main/java/com/supermartijn642/fusion/texture/types/connecting/ConnectingTextureSprite.java b/src/main/java/com/supermartijn642/fusion/texture/types/connecting/ConnectingTextureSprite.java index 9baa92cf..45145e6a 100644 --- a/src/main/java/com/supermartijn642/fusion/texture/types/connecting/ConnectingTextureSprite.java +++ b/src/main/java/com/supermartijn642/fusion/texture/types/connecting/ConnectingTextureSprite.java @@ -1,7 +1,9 @@ package com.supermartijn642.fusion.texture.types.connecting; +import com.mojang.blaze3d.platform.Transparency; import com.supermartijn642.fusion.api.texture.data.ConnectingTextureData; import com.supermartijn642.fusion.texture.types.base.BaseTextureSprite; +import com.supermartijn642.fusion.texture.types.connecting.layouts.ConnectingTextureLayoutHandler; import net.minecraft.client.renderer.texture.TextureAtlasSprite; /** @@ -42,4 +44,10 @@ public float getStartV(){ public ConnectingTextureData data(){ return (ConnectingTextureData)super.data(); } + + @Override + public Transparency computeTransparency(float u0, float v0, float u1, float v1){ + ConnectingTextureLayoutHandler handler = ConnectingTextureLayoutHandler.get(this.data().getLayout()); + return computeTiledTransparency(this, u0, v0, u1, v1, handler.getWidth(), handler.getHeight()); + } } diff --git a/src/main/java/com/supermartijn642/fusion/texture/types/connecting/ConnectingTextureType.java b/src/main/java/com/supermartijn642/fusion/texture/types/connecting/ConnectingTextureType.java index d4c03764..1a54f154 100644 --- a/src/main/java/com/supermartijn642/fusion/texture/types/connecting/ConnectingTextureType.java +++ b/src/main/java/com/supermartijn642/fusion/texture/types/connecting/ConnectingTextureType.java @@ -26,7 +26,6 @@ public ConnectingTextureData deserialize(JsonObject json) throws JsonParseExcept BaseTextureData base = DefaultTextureTypes.BASE.deserialize(json); // Copy base properties ConnectingTextureData.Builder builder = ConnectingTextureData.builder(); - builder.renderType(base.getRenderType()); builder.emissive(base.isEmissive()); builder.tinting(base.getTinting()); // Deserialize 'layout' diff --git a/src/main/java/com/supermartijn642/fusion/texture/types/continuous/ContinuousTextureDataBuilderImpl.java b/src/main/java/com/supermartijn642/fusion/texture/types/continuous/ContinuousTextureDataBuilderImpl.java index 84374a56..d74ddfed 100644 --- a/src/main/java/com/supermartijn642/fusion/texture/types/continuous/ContinuousTextureDataBuilderImpl.java +++ b/src/main/java/com/supermartijn642/fusion/texture/types/continuous/ContinuousTextureDataBuilderImpl.java @@ -9,17 +9,10 @@ */ public class ContinuousTextureDataBuilderImpl implements ContinuousTextureData.Builder { - private BaseTextureData.RenderType renderType; private boolean emissive = false; private BaseTextureData.QuadTinting tinting; private int rows = 1, columns = 1; - @Override - public ContinuousTextureDataBuilderImpl renderType(@Nullable BaseTextureData.RenderType renderType){ - this.renderType = renderType; - return this; - } - @Override public ContinuousTextureDataBuilderImpl emissive(boolean emissive){ this.emissive = emissive; @@ -46,6 +39,6 @@ public ContinuousTextureData.Builder columns(int columns){ @Override public ContinuousTextureData build(){ - return new ContinuousTextureDataImpl(this.renderType, this.emissive, this.tinting, this.rows, this.columns); + return new ContinuousTextureDataImpl(this.emissive, this.tinting, this.rows, this.columns); } } diff --git a/src/main/java/com/supermartijn642/fusion/texture/types/continuous/ContinuousTextureDataImpl.java b/src/main/java/com/supermartijn642/fusion/texture/types/continuous/ContinuousTextureDataImpl.java index 63fe6b93..fa4a4019 100644 --- a/src/main/java/com/supermartijn642/fusion/texture/types/continuous/ContinuousTextureDataImpl.java +++ b/src/main/java/com/supermartijn642/fusion/texture/types/continuous/ContinuousTextureDataImpl.java @@ -10,8 +10,8 @@ public class ContinuousTextureDataImpl extends BaseTextureDataImpl implements Co private final int rows, columns; - public ContinuousTextureDataImpl(RenderType renderType, boolean emissive, QuadTinting tinting, int rows, int columns){ - super(renderType, emissive, tinting); + public ContinuousTextureDataImpl(boolean emissive, QuadTinting tinting, int rows, int columns){ + super(emissive, tinting); this.rows = rows; this.columns = columns; } diff --git a/src/main/java/com/supermartijn642/fusion/texture/types/continuous/ContinuousTextureSprite.java b/src/main/java/com/supermartijn642/fusion/texture/types/continuous/ContinuousTextureSprite.java index 29fc67f8..37991e56 100644 --- a/src/main/java/com/supermartijn642/fusion/texture/types/continuous/ContinuousTextureSprite.java +++ b/src/main/java/com/supermartijn642/fusion/texture/types/continuous/ContinuousTextureSprite.java @@ -1,5 +1,6 @@ package com.supermartijn642.fusion.texture.types.continuous; +import com.mojang.blaze3d.platform.Transparency; import com.supermartijn642.fusion.api.texture.data.ContinuousTextureData; import com.supermartijn642.fusion.texture.types.base.BaseTextureSprite; import net.minecraft.client.renderer.texture.TextureAtlasSprite; @@ -30,4 +31,10 @@ protected ContinuousTextureSprite(TextureAtlasSprite original, ContinuousTexture public ContinuousTextureData data(){ return (ContinuousTextureData)super.data(); } + + @Override + public Transparency computeTransparency(float u0, float v0, float u1, float v1){ + ContinuousTextureData data = this.data(); + return computeTiledTransparency(this, u0, v0, u1, v1, data.getColumns(), data.getRows()); + } } diff --git a/src/main/java/com/supermartijn642/fusion/texture/types/continuous/ContinuousTextureType.java b/src/main/java/com/supermartijn642/fusion/texture/types/continuous/ContinuousTextureType.java index dba2558d..98fb83c6 100644 --- a/src/main/java/com/supermartijn642/fusion/texture/types/continuous/ContinuousTextureType.java +++ b/src/main/java/com/supermartijn642/fusion/texture/types/continuous/ContinuousTextureType.java @@ -24,7 +24,6 @@ public ContinuousTextureData deserialize(JsonObject json) throws JsonParseExcept BaseTextureData base = DefaultTextureTypes.BASE.deserialize(json); // Copy base properties ContinuousTextureData.Builder builder = ContinuousTextureData.builder(); - builder.renderType(base.getRenderType()); builder.emissive(base.isEmissive()); builder.tinting(base.getTinting()); // Rows diff --git a/src/main/java/com/supermartijn642/fusion/texture/types/random/RandomTextureDataBuilderImpl.java b/src/main/java/com/supermartijn642/fusion/texture/types/random/RandomTextureDataBuilderImpl.java index 20188945..32386538 100644 --- a/src/main/java/com/supermartijn642/fusion/texture/types/random/RandomTextureDataBuilderImpl.java +++ b/src/main/java/com/supermartijn642/fusion/texture/types/random/RandomTextureDataBuilderImpl.java @@ -9,18 +9,11 @@ */ public class RandomTextureDataBuilderImpl implements RandomTextureData.Builder { - private BaseTextureData.RenderType renderType; private boolean emissive = false; private BaseTextureData.QuadTinting tinting; private int rows = 1, columns = 1, count; private Long seed; - @Override - public RandomTextureDataBuilderImpl renderType(@Nullable BaseTextureData.RenderType renderType){ - this.renderType = renderType; - return this; - } - @Override public RandomTextureDataBuilderImpl emissive(boolean emissive){ this.emissive = emissive; @@ -67,6 +60,6 @@ public RandomTextureData.Builder seed(Long seed){ public RandomTextureData build(){ if(this.count > this.rows * this.columns) throw new IllegalArgumentException("Count cannot be greater than rows * columns!"); - return new RandomTextureDataImpl(this.renderType, this.emissive, this.tinting, this.rows, this.columns, this.count, this.seed); + return new RandomTextureDataImpl(this.emissive, this.tinting, this.rows, this.columns, this.count, this.seed); } } diff --git a/src/main/java/com/supermartijn642/fusion/texture/types/random/RandomTextureDataImpl.java b/src/main/java/com/supermartijn642/fusion/texture/types/random/RandomTextureDataImpl.java index df9a2dec..0e603259 100644 --- a/src/main/java/com/supermartijn642/fusion/texture/types/random/RandomTextureDataImpl.java +++ b/src/main/java/com/supermartijn642/fusion/texture/types/random/RandomTextureDataImpl.java @@ -12,8 +12,8 @@ public class RandomTextureDataImpl extends BaseTextureDataImpl implements Random private final int rows, columns, count; private final Long seed; - public RandomTextureDataImpl(RenderType renderType, boolean emissive, QuadTinting tinting, int rows, int columns, int count, Long seed){ - super(renderType, emissive, tinting); + public RandomTextureDataImpl(boolean emissive, QuadTinting tinting, int rows, int columns, int count, Long seed){ + super(emissive, tinting); this.rows = rows; this.columns = columns; this.count = count; diff --git a/src/main/java/com/supermartijn642/fusion/texture/types/random/RandomTextureSprite.java b/src/main/java/com/supermartijn642/fusion/texture/types/random/RandomTextureSprite.java index b9ce1655..6a631cfa 100644 --- a/src/main/java/com/supermartijn642/fusion/texture/types/random/RandomTextureSprite.java +++ b/src/main/java/com/supermartijn642/fusion/texture/types/random/RandomTextureSprite.java @@ -1,5 +1,6 @@ package com.supermartijn642.fusion.texture.types.random; +import com.mojang.blaze3d.platform.Transparency; import com.supermartijn642.fusion.api.texture.data.RandomTextureData; import com.supermartijn642.fusion.texture.types.base.BaseTextureSprite; import net.minecraft.client.renderer.texture.TextureAtlasSprite; @@ -30,4 +31,10 @@ protected RandomTextureSprite(TextureAtlasSprite original, RandomTextureData dat public RandomTextureData data(){ return (RandomTextureData)super.data(); } + + @Override + public Transparency computeTransparency(float u0, float v0, float u1, float v1){ + RandomTextureData data = this.data(); + return computeTiledTransparency(this, u0, v0, u1, v1, data.getColumns(), data.getRows()); + } } diff --git a/src/main/java/com/supermartijn642/fusion/texture/types/random/RandomTextureType.java b/src/main/java/com/supermartijn642/fusion/texture/types/random/RandomTextureType.java index 3cafdb92..0ce4f61c 100644 --- a/src/main/java/com/supermartijn642/fusion/texture/types/random/RandomTextureType.java +++ b/src/main/java/com/supermartijn642/fusion/texture/types/random/RandomTextureType.java @@ -25,7 +25,6 @@ public RandomTextureData deserialize(JsonObject json) throws JsonParseException{ BaseTextureData base = DefaultTextureTypes.BASE.deserialize(json); // Copy base properties RandomTextureData.Builder builder = RandomTextureData.builder(); - builder.renderType(base.getRenderType()); builder.emissive(base.isEmissive()); builder.tinting(base.getTinting()); // Rows diff --git a/src/main/java/com/supermartijn642/fusion/texture/types/scrolling/ScrollingTextureDataBuilderImpl.java b/src/main/java/com/supermartijn642/fusion/texture/types/scrolling/ScrollingTextureDataBuilderImpl.java index 52bb2e12..b2d0b524 100644 --- a/src/main/java/com/supermartijn642/fusion/texture/types/scrolling/ScrollingTextureDataBuilderImpl.java +++ b/src/main/java/com/supermartijn642/fusion/texture/types/scrolling/ScrollingTextureDataBuilderImpl.java @@ -9,7 +9,6 @@ */ public class ScrollingTextureDataBuilderImpl implements ScrollingTextureData.Builder { - private BaseTextureData.RenderType renderType; private boolean emissive = false; private BaseTextureData.QuadTinting tinting; private ScrollingTextureData.Position startPosition = ScrollingTextureData.Position.TOP_LEFT, endPosition = ScrollingTextureData.Position.BOTTOM_LEFT; @@ -17,12 +16,6 @@ public class ScrollingTextureDataBuilderImpl implements ScrollingTextureData.Bui private ScrollingTextureData.LoopType loopType = ScrollingTextureData.LoopType.RESET; private int loopPause = 0; - @Override - public ScrollingTextureDataBuilderImpl renderType(@Nullable BaseTextureData.RenderType renderType){ - this.renderType = renderType; - return this; - } - @Override public ScrollingTextureDataBuilderImpl emissive(boolean emissive){ this.emissive = emissive; @@ -104,7 +97,7 @@ public ScrollingTextureDataBuilderImpl loopPause(int ticks){ public ScrollingTextureData build(){ return new ScrollingTextureDataImpl( - this.renderType, this.emissive, this.tinting, + this.emissive, this.tinting, this.startPosition, this.endPosition, this.frameTime, this.frameWidth, this.frameHeight, this.loopType, this.loopPause ); } diff --git a/src/main/java/com/supermartijn642/fusion/texture/types/scrolling/ScrollingTextureDataImpl.java b/src/main/java/com/supermartijn642/fusion/texture/types/scrolling/ScrollingTextureDataImpl.java index eb454023..b3b20b92 100644 --- a/src/main/java/com/supermartijn642/fusion/texture/types/scrolling/ScrollingTextureDataImpl.java +++ b/src/main/java/com/supermartijn642/fusion/texture/types/scrolling/ScrollingTextureDataImpl.java @@ -13,8 +13,8 @@ public class ScrollingTextureDataImpl extends BaseTextureDataImpl implements Scr private final ScrollingTextureData.LoopType loopType; private final int loopPause; - public ScrollingTextureDataImpl(RenderType renderType, boolean emissive, QuadTinting tinting, Position startPosition, Position endPosition, int frameTime, int frameWidth, int frameHeight, LoopType loopType, int loopPause){ - super(renderType, emissive, tinting); + public ScrollingTextureDataImpl(boolean emissive, QuadTinting tinting, Position startPosition, Position endPosition, int frameTime, int frameWidth, int frameHeight, LoopType loopType, int loopPause){ + super(emissive, tinting); this.startPosition = startPosition; this.endPosition = endPosition; this.frameTime = frameTime; diff --git a/src/main/java/com/supermartijn642/fusion/texture/types/scrolling/ScrollingTextureType.java b/src/main/java/com/supermartijn642/fusion/texture/types/scrolling/ScrollingTextureType.java index 2ebb16b6..a7ac2a11 100644 --- a/src/main/java/com/supermartijn642/fusion/texture/types/scrolling/ScrollingTextureType.java +++ b/src/main/java/com/supermartijn642/fusion/texture/types/scrolling/ScrollingTextureType.java @@ -3,6 +3,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.mojang.blaze3d.buffers.GpuBufferSlice; +import com.mojang.blaze3d.platform.Transparency; import com.mojang.blaze3d.systems.GpuDevice; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.textures.GpuTexture; @@ -16,10 +17,12 @@ import com.supermartijn642.fusion.texture.types.base.BaseTextureSprite; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntList; import net.minecraft.client.renderer.texture.SpriteContents; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.resources.metadata.animation.FrameSize; import net.minecraft.util.ARGB; +import net.minecraft.util.Mth; import java.util.ArrayList; import java.util.Arrays; @@ -215,6 +218,30 @@ public boolean isTransparent(int frame, int x, int y){ )) == 0; } + @Override + public Transparency computeTransparency(float u0, float v0, float u1, float v1){ + Transparency baseTransparency = this.transparency(); + if(baseTransparency.isOpaque()){ + return baseTransparency; + }else if(u0 == 0.0f && v0 == 0.0f && u1 == 1.0f && v1 == 1.0f){ + return baseTransparency; + }else{ + int x0 = Mth.floor(u0 * this.width); + int y0 = Mth.floor(v0 * this.height); + int x1 = Mth.ceil(u1 * this.width); + int y1 = Mth.ceil(v1 * this.height); + IntList uniqueFrames = this.animatedTexture.getUniqueFrames(); + Transparency transparency = Transparency.NONE; + for(int i = 0; i < uniqueFrames.size(); i++){ + int frame = uniqueFrames.getInt(i); + int frameX = this.xPositions[frame] * this.width; + int frameY = this.yPositions[frame] * this.height; + transparency = transparency.or(this.originalImage.computeTransparency(frameX + x0, frameY + y0, frameX + x1, frameY + y1)); + } + return transparency; + } + } + private class ScrollingAnimatedTexture extends SpriteContents.AnimatedTexture { public ScrollingAnimatedTexture(){ @@ -227,7 +254,7 @@ public AnimationState createAnimationState(GpuBufferSlice bufferSlice, int offse Int2ObjectMap textureViews = new Int2ObjectOpenHashMap<>(); GpuBufferSlice[] slices = new GpuBufferSlice[ScrollingSpriteContents.this.byMipLevel.length]; - for(int frameIndex : this.getUniqueFrames().toArray()){ + for(int frameIndex : this.getUniqueFrames().toIntArray()){ GpuTexture gpuTexture = gpuDevice.createTexture( () -> ScrollingSpriteContents.this.name + " animation frame " + frameIndex, 5, diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 52d8b6e6..54ae9203 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -1,13 +1,5 @@ -# BakedQuad -public-f net.minecraft.client.renderer.block.model.BakedQuad tintIndex -# BlockElement -public-f net.minecraft.client.renderer.block.model.BlockElement -# BlockModel -public net.minecraft.client.renderer.block.model.BlockModel GSON -public-f net.minecraft.client.renderer.block.model.BlockModel -# BlockModelWrapper -public net.minecraft.client.renderer.item.BlockModelWrapper detectRenderType(Ljava/util/List;)Ljava/util/function/Function; -public net.minecraft.client.renderer.item.BlockModelWrapper hasSpecialAnimatedTexture(Lnet/minecraft/world/item/ItemStack;)Z +# CuboidItemModelWrapper +public net.minecraft.client.renderer.item.CuboidItemModelWrapper hasSpecialAnimatedTexture(Lnet/minecraft/world/item/ItemStack;)Z # EntityModelSet public net.minecraft.client.model.geom.EntityModelSet roots # FilePackResources$SharedZipFileAccess @@ -15,17 +7,15 @@ public net.minecraft.server.packs.FilePackResources$SharedZipFileAccess file public net.minecraft.server.packs.FilePackResources$SharedZipFileAccess getOrCreateZipFile()Ljava/util/zip/ZipFile; public net.minecraft.server.packs.FilePackResources$SharedZipFileAccess # GuiGraphics -public net.minecraft.client.gui.GuiGraphics guiRenderState -# ItemModelGenerator -public net.minecraft.client.renderer.block.model.ItemModelGenerator processFrames(ILjava/lang/String;Lnet/minecraft/client/renderer/texture/TextureAtlasSprite;)Ljava/util/List; +public net.minecraft.client.gui.GuiGraphicsExtractor guiRenderState # LayerDefinition public net.minecraft.client.model.geom.builders.LayerDefinition (Lnet/minecraft/client/model/geom/builders/MeshDefinition;Lnet/minecraft/client/model/geom/builders/MaterialDefinition;)V # ModelBakery$ModelBakerImpl -public net.minecraft.client.resources.model.ModelBakery$ModelBakerImpl (Lnet/minecraft/client/resources/model/ModelBakery;Lnet/minecraft/client/resources/model/SpriteGetter;Lnet/minecraft/client/resources/model/ModelBaker$PartCache;Lnet/minecraft/client/resources/model/ModelBakery$MissingModels;)V +public net.minecraft.client.resources.model.ModelBakery$ModelBakerImpl (Lnet/minecraft/client/resources/model/ModelBakery;Lnet/minecraft/client/resources/model/sprite/MaterialBaker;Lnet/minecraft/client/resources/model/ModelBaker$Interner;Lnet/minecraft/client/resources/model/ModelBakery$MissingModels;)V public net.minecraft.client.resources.model.ModelBakery$ModelBakerImpl -# ModelBakery$PartCacheImpl -public net.minecraft.client.resources.model.ModelBakery$PartCacheImpl ()V -public net.minecraft.client.resources.model.ModelBakery$PartCacheImpl +# ModelBakery$InternerImpl +public net.minecraft.client.resources.model.ModelBakery$InternerImpl ()V +public net.minecraft.client.resources.model.ModelBakery$InternerImpl # ModelDiscovery$ModelWrapper public net.minecraft.client.resources.model.ModelDiscovery$ModelWrapper # ModelLayers @@ -96,12 +86,12 @@ public-f net.minecraft.client.renderer.texture.TextureAtlasSprite v0 public-f net.minecraft.client.renderer.texture.TextureAtlasSprite v1 public net.minecraft.client.renderer.texture.TextureAtlasSprite padding # TextureSlots -public net.minecraft.client.renderer.block.model.TextureSlots resolvedValues -public net.minecraft.client.renderer.block.model.TextureSlots (Ljava/util/Map;)V +public net.minecraft.client.resources.model.sprite.TextureSlots resolvedValues +public net.minecraft.client.resources.model.sprite.TextureSlots (Ljava/util/Map;)V # TextureSlots$Reference -public net.minecraft.client.renderer.block.model.TextureSlots$Reference +public net.minecraft.client.resources.model.sprite.TextureSlots$Reference # TextureSlots$Value -public net.minecraft.client.renderer.block.model.TextureSlots$Value +public net.minecraft.client.resources.model.sprite.TextureSlots$Value # TransferableSelectionList public net.minecraft.client.gui.screens.packs.TransferableSelectionList screen # VertexFormat diff --git a/src/main/resources/modid.mixins.json b/src/main/resources/modid.mixins.json index 7a32a164..eabf9cd1 100644 --- a/src/main/resources/modid.mixins.json +++ b/src/main/resources/modid.mixins.json @@ -9,19 +9,20 @@ "neoforge.DatagenModLoaderMixin", "AgeableMobMixin", "AtlasConfigMixin", - "BlockModelMixin", - "BlockModelWrapperMixin", "BlockRenderDispatcherMixin", "BufferSourceMixin", "CompositePackResourceMixin", + "CuboidItemModelWrapperMixin", + "CuboidModelMixin", "EntityMixin", "EntityModelSetMixin", "EntityRendererMixin", "EntityRenderersMixin", "EntityRenderStateMixin", + "FaceBakeryMixin", "FilePackResourcesMixin", "IntermediaryFormatMixin", - "ItemRendererMixin", + "ItemFeatureRendererMixin", "JarContentsPackResourcesMixin", "ModelBakeryMixin", "ModelBlockRendererMixin",