├── .gitignore ├── LICENSE ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ └── gradle-wrapper.properties ├── icon.png ├── settings.gradle └── src └── main ├── java └── com │ └── moepus │ └── flerovium │ ├── Config.java │ ├── ConfigParser.java │ ├── Flerovium.java │ ├── MixinPlugin.java │ ├── functions │ ├── Chunk │ │ ├── FastSimpleFrustum.java │ │ └── Occlusion.java │ ├── DummyModel.java │ ├── FastEntityRenderer.java │ ├── FastSimpleBakedModelRenderer.java │ ├── IntFlatMap.java │ ├── MathUtil.java │ └── MatrixStuff.java │ └── mixins │ ├── Chunk │ ├── FrustumMixin.java │ └── OcclusionCullerMixin.java │ ├── Entity │ ├── ClientLevelMixin.java │ └── ModelPartMixin.java │ ├── Item │ ├── ItemEntityRenderMixin.java │ ├── ItemRendererMixin.java │ ├── ItemTransformMixin.java │ └── SimpleBakedModelMixin.java │ └── Particle │ ├── ParticleEngineMixin.java │ ├── ParticleMixin.java │ └── SingleQuadParticleMixin.java └── resources ├── META-INF ├── accesstransformer.cfg └── mods.toml ├── flerovium.mixins.json └── pack.mcmeta /.gitignore: -------------------------------------------------------------------------------- 1 | # eclipse 2 | bin 3 | *.launch 4 | .settings 5 | .metadata 6 | .classpath 7 | .project 8 | 9 | # idea 10 | out 11 | *.ipr 12 | *.iws 13 | *.iml 14 | .idea 15 | 16 | # gradle 17 | build 18 | .gradle 19 | 20 | # other 21 | eclipse 22 | run 23 | runs 24 | 25 | # Files from Forge MDK 26 | forge*changelog.txt 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | // These repositories are only for Gradle plugins, put any other repositories in the repository block further below 4 | maven { url = 'https://repo.spongepowered.org/repository/maven-public/' } 5 | mavenCentral() 6 | } 7 | dependencies { 8 | classpath 'org.spongepowered:mixingradle:0.7-SNAPSHOT' 9 | } 10 | } 11 | 12 | plugins { 13 | id 'eclipse' 14 | id 'idea' 15 | id 'net.minecraftforge.gradle' version '[6.0,6.2)' 16 | } 17 | 18 | apply plugin: 'org.spongepowered.mixin' 19 | 20 | group = mod_group_id 21 | version = mod_version 22 | 23 | base { 24 | archivesName = mod_id + "-forge-" + minecraft_version 25 | } 26 | 27 | java { 28 | toolchain.languageVersion = JavaLanguageVersion.of(17) 29 | } 30 | 31 | minecraft { 32 | // The mappings can be changed at any time and must be in the following format. 33 | // Channel: Version: 34 | // official MCVersion Official field/method names from Mojang mapping files 35 | // parchment YYYY.MM.DD-MCVersion Open community-sourced parameter names and javadocs layered on top of official 36 | // 37 | // You must be aware of the Mojang license when using the 'official' or 'parchment' mappings. 38 | // See more information here: https://github.com/MinecraftForge/MCPConfig/blob/master/Mojang.md 39 | // 40 | // Parchment is an unofficial project maintained by ParchmentMC, separate from MinecraftForge 41 | // Additional setup is needed to use their mappings: https://parchmentmc.org/docs/getting-started 42 | // 43 | // Use non-default mappings at your own risk. They may not always work. 44 | // Simply re-run your setup task after changing the mappings to update your workspace. 45 | mappings channel: mapping_channel, version: mapping_version 46 | 47 | // When true, this property will have all Eclipse/IntelliJ IDEA run configurations run the "prepareX" task for the given run configuration before launching the game. 48 | // enableEclipsePrepareRuns = true 49 | // enableIdeaPrepareRuns = true 50 | 51 | // This property allows configuring Gradle's ProcessResources task(s) to run on IDE output locations before launching the game. 52 | // It is REQUIRED to be set to true for this template to function. 53 | // See https://docs.gradle.org/current/dsl/org.gradle.language.jvm.tasks.ProcessResources.html 54 | copyIdeResources = true 55 | 56 | // When true, this property will add the folder name of all declared run configurations to generated IDE run configurations. 57 | // The folder name can be set on a run configuration using the "folderName" property. 58 | // By default, the folder name of a run configuration is the name of the Gradle project containing it. 59 | // generateRunFolders = true 60 | 61 | // This property enables access transformers for use in development. 62 | // They will be applied to the Minecraft artifact. 63 | // The access transformer file can be anywhere in the project. 64 | // However, it must be at "META-INF/accesstransformer.cfg" in the final mod jar to be loaded by Forge. 65 | // This default location is a best practice to automatically put the file in the right place in the final jar. 66 | // See https://docs.minecraftforge.net/en/latest/advanced/accesstransformers/ for more information. 67 | accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg') 68 | 69 | // Default run configurations. 70 | // These can be tweaked, removed, or duplicated as needed. 71 | runs { 72 | // applies to all the run configs below 73 | configureEach { 74 | workingDirectory project.file('run') 75 | 76 | // Recommended logging data for a userdev environment 77 | // The markers can be added/remove as needed separated by commas. 78 | // "SCAN": For mods scan. 79 | // "REGISTRIES": For firing of registry events. 80 | // "REGISTRYDUMP": For getting the contents of all registries. 81 | property 'forge.logging.markers', 'REGISTRIES' 82 | property 'mixin.env.remapRefMap', 'true' 83 | property 'mixin.env.refMapRemappingFile', "${projectDir}/build/createSrgToMcp/output.srg" 84 | 85 | // Recommended logging level for the console 86 | // You can set various levels here. 87 | // Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels 88 | property 'forge.logging.console.level', 'debug' 89 | 90 | mods { 91 | "${mod_id}" { 92 | source sourceSets.main 93 | } 94 | } 95 | } 96 | 97 | client { 98 | // Comma-separated list of namespaces to load gametests from. Empty = all namespaces. 99 | property 'forge.enabledGameTestNamespaces', mod_id 100 | property 'mixin.env.remapRefMap', 'true' 101 | property 'mixin.env.refMapRemappingFile', "${projectDir}/build/createSrgToMcp/output.srg" 102 | } 103 | 104 | server { 105 | property 'forge.enabledGameTestNamespaces', mod_id 106 | args '--nogui' 107 | } 108 | 109 | // This run config launches GameTestServer and runs all registered gametests, then exits. 110 | // By default, the server will crash when no gametests are provided. 111 | // The gametest system is also enabled by default for other run configs under the /test command. 112 | gameTestServer { 113 | property 'forge.enabledGameTestNamespaces', mod_id 114 | } 115 | 116 | data { 117 | // example of overriding the workingDirectory set in configureEach above 118 | workingDirectory project.file('run-data') 119 | 120 | // Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources. 121 | args '--mod', mod_id, '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/') 122 | } 123 | } 124 | } 125 | 126 | mixin { 127 | add sourceSets.main, "${mod_id}.refmap.json" 128 | 129 | config "${mod_id}.mixins.json" 130 | 131 | debug.verbose = true 132 | debug.export = true 133 | } 134 | 135 | // Include resources generated by data generators. 136 | sourceSets.main.resources { srcDir 'src/generated/resources' } 137 | 138 | repositories { 139 | // Put repositories for dependencies here 140 | // ForgeGradle automatically adds the Forge maven and Maven Central for you 141 | 142 | // If you have mod jar dependencies in ./libs, you can declare them as a repository like so. 143 | // See https://docs.gradle.org/current/userguide/declaring_repositories.html#sub:flat_dir_resolver 144 | // flatDir { 145 | // dir 'libs' 146 | // } 147 | maven { 148 | url = "https://maven.blamejared.com" 149 | } 150 | maven { 151 | url = 'https://www.cursemaven.com' 152 | content { 153 | includeGroup "curse.maven" 154 | } 155 | } 156 | maven { 157 | name = 'tterrag maven' 158 | url = 'https://maven.tterrag.com/' 159 | } 160 | } 161 | 162 | dependencies { 163 | minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}" 164 | 165 | annotationProcessor 'org.spongepowered:mixin:0.8.5:processor' 166 | compileOnly(annotationProcessor("io.github.llamalad7:mixinextras-common:0.4.1")) 167 | implementation(jarJar("io.github.llamalad7:mixinextras-forge:0.4.1")) { 168 | jarJar.ranged(it, "[0.4.1,)") 169 | } 170 | implementation fg.deobf("org.embeddedt:embeddium-${minecraft_version}:0.3.31-beta.53+mc${minecraft_version}") 171 | runtimeOnly fg.deobf("curse.maven:oculus-581495:6020952") 172 | // runtimeOnly fg.deobf("curse.maven:moonlight-499980:5938277") 173 | // runtimeOnly fg.deobf("curse.maven:supplementaries-412082:5928556") 174 | // runtimeOnly fg.deobf("curse.maven:ftbteams-404468:5267190") 175 | // runtimeOnly fg.deobf("curse.maven:ftblib-404465:5925398") 176 | // runtimeOnly fg.deobf("curse.maven:ftbquests-289412:5816794") 177 | // runtimeOnly fg.deobf("curse.maven:architectury-419699:5137938") 178 | // runtimeOnly fg.deobf("curse.maven:simplyswords-659887:5639538") 179 | // runtimeOnly fg.deobf("curse.maven:clothconfig-348521:5729105") 180 | // runtimeOnly fg.deobf("curse.maven:create-328085:5838779") 181 | // runtimeOnly fg.deobf("curse.maven:mek-268560:5919382") 182 | // runtimeOnly fg.deobf("curse.maven:functional-storage-556861:5650296") 183 | // runtimeOnly fg.deobf("curse.maven:titanium-287342:5468426") 184 | } 185 | 186 | // This block of code expands all declared replace properties in the specified resource targets. 187 | // A missing property will result in an error. Properties are expanded using ${} Groovy notation. 188 | // When "copyIdeResources" is enabled, this will also run before the game launches in IDE environments. 189 | // See https://docs.gradle.org/current/dsl/org.gradle.language.jvm.tasks.ProcessResources.html 190 | tasks.named('processResources', ProcessResources).configure { 191 | var replaceProperties = [ 192 | minecraft_version: minecraft_version, minecraft_version_range: minecraft_version_range, 193 | forge_version: forge_version, forge_version_range: forge_version_range, 194 | loader_version_range: loader_version_range, 195 | mod_id: mod_id, mod_name: mod_name, mod_license: mod_license, mod_version: mod_version, 196 | mod_authors: mod_authors, mod_description: mod_description, 197 | ] 198 | 199 | inputs.properties replaceProperties 200 | 201 | filesMatching(['META-INF/mods.toml', 'pack.mcmeta']) { 202 | expand replaceProperties + [project: project] 203 | }} 204 | 205 | // Example for how to get properties into the manifest for reading at runtime. 206 | tasks.named('jar', Jar).configure { 207 | manifest { 208 | attributes([ 209 | "Specification-Title": mod_id, 210 | "Specification-Vendor": mod_authors, 211 | "Specification-Version": "1", // We are version 1 of ourselves 212 | "Implementation-Title": project.name, 213 | "Implementation-Version": project.jar.archiveVersion, 214 | "Implementation-Vendor": mod_authors, 215 | "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ") 216 | ]) 217 | } 218 | 219 | // This is the preferred method to reobfuscate your jar file 220 | finalizedBy 'reobfJar' 221 | } 222 | 223 | tasks.withType(JavaCompile).configureEach { 224 | options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation 225 | } 226 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx3G 2 | org.gradle.daemon=false 3 | 4 | 5 | # The Minecraft version must agree with the Forge version to get a valid artifact 6 | minecraft_version=1.20.1 7 | # The Minecraft version range can use any release version of Minecraft as bounds. 8 | # Snapshots, pre-releases, and release candidates are not guaranteed to sort properly 9 | # as they do not follow standard versioning conventions. 10 | minecraft_version_range=[1.20.1,1.21) 11 | # The Forge version must agree with the Minecraft version to get a valid artifact 12 | forge_version=47.3.7 13 | # The Forge version range can use any version of Forge as bounds or match the loader version range 14 | forge_version_range=[47,) 15 | # The loader version range can only use the major version of Forge/FML as bounds 16 | loader_version_range=[47,) 17 | 18 | # The mapping channel to use for mappings. 19 | # The default set of supported mapping channels are ["official", "snapshot", "snapshot_nodoc", "stable", "stable_nodoc"]. 20 | # Additional mapping channels can be registered through the "channelProviders" extension in a Gradle plugin. 21 | # 22 | # | Channel | Version | | 23 | # |-----------|----------------------|--------------------------------------------------------------------------------| 24 | # | official | MCVersion | Official field/method names from Mojang mapping files | 25 | # | parchment | YYYY.MM.DD-MCVersion | Open community-sourced parameter names and javadocs layered on top of official | 26 | # 27 | # You must be aware of the Mojang license when using the 'official' or 'parchment' mappings. 28 | # See more information here: https://github.com/MinecraftForge/MCPConfig/blob/master/Mojang.md 29 | # 30 | # Parchment is an unofficial project maintained by ParchmentMC, separate from Minecraft Forge. 31 | # Additional setup is needed to use their mappings, see https://parchmentmc.org/docs/getting-started 32 | mapping_channel=official 33 | # The mapping version to query from the mapping channel. 34 | # This must match the format required by the mapping channel. 35 | mapping_version=1.20.1 36 | 37 | 38 | # The unique mod identifier for the mod. Must be lowercase in English locale. Must fit the regex [a-z][a-z0-9_]{1,63} 39 | # Must match the String constant located in the main mod class annotated with @Mod. 40 | mod_id=flerovium 41 | # The human-readable display name for the mod. 42 | mod_name=Flerovium 43 | # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. 44 | mod_license=LGPL3.0 45 | # The mod version. See https://semver.org/ 46 | mod_version=1.2.14 47 | # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. 48 | # This should match the base package used for the mod sources. 49 | # See https://maven.apache.org/guides/mini/guide-naming-conventions.html 50 | mod_group_id=com.moepus 51 | # The authors of the mod. This is a simple text string that is used for display purposes in the mod list. 52 | mod_authors= 53 | # The description of the mod. This is a simple multiline text string that is used for display purposes in the mod list. 54 | mod_description= 55 | 56 | create_minecraft_version = 1.20.1 57 | flywheel_minecraft_version = 1.20.1 58 | create_version = 0.5.1.j-55 59 | flywheel_version = 0.6.11-13 60 | registrate_version = MC1.20-1.3.3 -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip 2 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoePus/Flerovium/45e2ee56e6541c218b2ee7ed0c3c26091dc754b0/icon.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | maven { 5 | name = 'MinecraftForge' 6 | url = 'https://maven.minecraftforge.net/' 7 | } 8 | } 9 | } 10 | 11 | plugins { 12 | id 'org.gradle.toolchains.foojay-resolver-convention' version '0.5.0' 13 | } 14 | 15 | rootProject.name = 'Flerovium' 16 | -------------------------------------------------------------------------------- /src/main/java/com/moepus/flerovium/Config.java: -------------------------------------------------------------------------------- 1 | package com.moepus.flerovium; 2 | 3 | public class Config { 4 | public boolean entityBackFaceCulling = true; 5 | public boolean itemBackFaceCulling = true; 6 | } -------------------------------------------------------------------------------- /src/main/java/com/moepus/flerovium/ConfigParser.java: -------------------------------------------------------------------------------- 1 | package com.moepus.flerovium; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import com.google.gson.JsonIOException; 6 | import com.google.gson.JsonSyntaxException; 7 | import net.minecraftforge.fml.loading.FMLPaths; 8 | 9 | import java.io.File; 10 | import java.io.FileReader; 11 | import java.io.FileWriter; 12 | import java.io.IOException; 13 | import java.nio.file.Paths; 14 | import java.util.function.Supplier; 15 | 16 | public class ConfigParser { 17 | private static Config config; 18 | static Supplier config_path = ()->String.valueOf(Paths.get(String.valueOf(FMLPaths.CONFIGDIR.get()), 19 | "flerovium.json")); 20 | 21 | public static void loadConfig() { 22 | Gson gson = new Gson(); 23 | File configFile = new File(config_path.get()); 24 | 25 | if (!configFile.exists()) { 26 | config = new Config(); 27 | saveConfig(); 28 | } else { 29 | // Load the existing config 30 | try (FileReader reader = new FileReader(configFile)) { 31 | config = gson.fromJson(reader, Config.class); 32 | saveConfig(); 33 | } catch (JsonIOException | JsonSyntaxException | IOException e) { 34 | e.printStackTrace(); 35 | } 36 | } 37 | } 38 | 39 | public static void saveConfig() { 40 | Gson gson = new GsonBuilder().setPrettyPrinting().create(); 41 | try (FileWriter writer = new FileWriter(config_path.get())) { 42 | gson.toJson(config, writer); 43 | } catch (IOException e) { 44 | Flerovium.LOGGER.error(e.getMessage());; 45 | } 46 | } 47 | 48 | public static Config getConfig() { 49 | if (config == null) { 50 | loadConfig(); 51 | } 52 | return config; 53 | } 54 | } -------------------------------------------------------------------------------- /src/main/java/com/moepus/flerovium/Flerovium.java: -------------------------------------------------------------------------------- 1 | package com.moepus.flerovium; 2 | 3 | import com.mojang.logging.LogUtils; 4 | import net.minecraftforge.api.distmarker.Dist; 5 | import net.minecraftforge.common.MinecraftForge; 6 | import net.minecraftforge.eventbus.api.IEventBus; 7 | import net.minecraftforge.eventbus.api.SubscribeEvent; 8 | import net.minecraftforge.fml.common.Mod; 9 | import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; 10 | import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; 11 | import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; 12 | import org.slf4j.Logger; 13 | 14 | // The value here should match an entry in the META-INF/mods.toml file 15 | @Mod(Flerovium.MODID) 16 | public class Flerovium { 17 | // Define mod id in a common place for everything to reference 18 | public static final String MODID = "flerovium"; 19 | // Directly reference a slf4j logger 20 | public static final Logger LOGGER = LogUtils.getLogger(); 21 | // Create a Deferred Register to hold Blocks which will all be registered under the "flerovium" namespace 22 | public static final Config config = ConfigParser.getConfig(); 23 | 24 | public Flerovium() { 25 | IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); 26 | 27 | // Register the commonSetup method for modloading 28 | modEventBus.addListener(this::commonSetup); 29 | 30 | // Register ourselves for server and other game events we are interested in 31 | MinecraftForge.EVENT_BUS.register(this); 32 | } 33 | 34 | private void commonSetup(final FMLCommonSetupEvent event) { 35 | } 36 | 37 | // You can use EventBusSubscriber to automatically register all static methods in the class annotated with @SubscribeEvent 38 | @Mod.EventBusSubscriber(modid = MODID, bus = Mod.EventBusSubscriber.Bus.MOD, value = Dist.CLIENT) 39 | public static class ClientModEvents { 40 | 41 | @SubscribeEvent 42 | public static void onClientSetup(FMLClientSetupEvent event) 43 | { 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/moepus/flerovium/MixinPlugin.java: -------------------------------------------------------------------------------- 1 | package com.moepus.flerovium; 2 | 3 | import net.minecraftforge.api.distmarker.Dist; 4 | import net.minecraftforge.fml.loading.FMLLoader; 5 | import net.minecraftforge.fml.loading.LoadingModList; 6 | import org.objectweb.asm.tree.ClassNode; 7 | import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; 8 | import org.spongepowered.asm.mixin.extensibility.IMixinInfo; 9 | 10 | import java.util.List; 11 | import java.util.Set; 12 | 13 | public class MixinPlugin implements IMixinConfigPlugin { 14 | @Override 15 | public void onLoad(String mixinPackage) { 16 | } 17 | 18 | @Override 19 | public String getRefMapperConfig() { 20 | return null; 21 | } 22 | 23 | private static boolean isModLoaded(String modId) { 24 | return LoadingModList.get().getModFileById(modId) != null; 25 | } 26 | 27 | @Override 28 | public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { 29 | return switch (mixinClassName) { 30 | case "com.moepus.flerovium.mixins.Entity.ModelPartMixin" -> !isModLoaded("bendylib") && !isModLoaded("physicsmod"); 31 | case "com.moepus.flerovium.mixins.Chunk.FrustumMixin" -> !isModLoaded("acedium") && !isModLoaded("nvidium"); 32 | default -> true; 33 | }; 34 | } 35 | 36 | @Override 37 | public void acceptTargets(Set myTargets, Set otherTargets) { 38 | 39 | } 40 | 41 | @Override 42 | public List getMixins() { 43 | return null; 44 | } 45 | 46 | @Override 47 | public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { 48 | } 49 | 50 | @Override 51 | public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { 52 | 53 | } 54 | } -------------------------------------------------------------------------------- /src/main/java/com/moepus/flerovium/functions/Chunk/FastSimpleFrustum.java: -------------------------------------------------------------------------------- 1 | package com.moepus.flerovium.functions.Chunk; 2 | 3 | import me.jellysquid.mods.sodium.client.render.viewport.frustum.Frustum; 4 | import org.joml.FrustumIntersection; 5 | import org.joml.Vector4f; 6 | 7 | import java.lang.reflect.Field; 8 | 9 | public class FastSimpleFrustum implements Frustum { 10 | // The bounding box of a chunk section must be large enough to contain all possible geometry within it. Block models 11 | // can extend outside a block volume by +/- 1.0 blocks on all axis. Additionally, we make use of a small epsilon 12 | // to deal with floating point imprecision during a frustum check (see GH#2132). 13 | public static final float CHUNK_SECTION_RADIUS = 8.0f /* chunk bounds */; 14 | public static final float CHUNK_SECTION_SIZE = CHUNK_SECTION_RADIUS + 1.0f /* maximum model extent */ + 0.125f /* epsilon */; 15 | 16 | // all the w components are double negated 17 | private float nxX, nxY, nxZ, negNxW; 18 | private float pxX, pxY, pxZ, negPxW; 19 | private float nyX, nyY, nyZ, negNyW; 20 | private float pyX, pyY, pyZ, negPyW; 21 | private float nzX, nzY, nzZ, negNzW; 22 | 23 | public FastSimpleFrustum(FrustumIntersection frustumIntersection) { 24 | Vector4f[] planes = getFrustumPlanes(frustumIntersection); 25 | 26 | nxX = planes[0].x; 27 | nxY = planes[0].y; 28 | nxZ = planes[0].z; 29 | pxX = planes[1].x; 30 | pxY = planes[1].y; 31 | pxZ = planes[1].z; 32 | nyX = planes[2].x; 33 | nyY = planes[2].y; 34 | nyZ = planes[2].z; 35 | pyX = planes[3].x; 36 | pyY = planes[3].y; 37 | pyZ = planes[3].z; 38 | nzX = planes[4].x; 39 | nzY = planes[4].y; 40 | nzZ = planes[4].z; 41 | 42 | final float size = CHUNK_SECTION_SIZE; 43 | negNxW = 2 * (-(planes[0].w + nxX * (nxX < 0 ? -size : size) + 44 | nxY * (nxY < 0 ? -size : size) + 45 | nxZ * (nxZ < 0 ? -size : size))); 46 | negPxW = 2 * (-(planes[1].w + pxX * (pxX < 0 ? -size : size) + 47 | pxY * (pxY < 0 ? -size : size) + 48 | pxZ * (pxZ < 0 ? -size : size))); 49 | negNyW = 2 * (-(planes[2].w + nyX * (nyX < 0 ? -size : size) + 50 | nyY * (nyY < 0 ? -size : size) + 51 | nyZ * (nyZ < 0 ? -size : size))); 52 | negPyW = 2 * (-(planes[3].w + pyX * (pyX < 0 ? -size : size) + 53 | pyY * (pyY < 0 ? -size : size) + 54 | pyZ * (pyZ < 0 ? -size : size))); 55 | negNzW = 2 * (-(planes[4].w + nzX * (nzX < 0 ? -size : size) + 56 | nzY * (nzY < 0 ? -size : size) + 57 | nzZ * (nzZ < 0 ? -size : size))); 58 | } 59 | 60 | private static Vector4f[] getFrustumPlanes(Object frustumIntersection) { 61 | Vector4f[] planes; 62 | try { 63 | Field planesField = FrustumIntersection.class.getDeclaredField("planes"); 64 | planesField.setAccessible(true); 65 | planes = (Vector4f[]) planesField.get(frustumIntersection); 66 | } catch (NoSuchFieldException | IllegalAccessException e) { 67 | throw new RuntimeException("Failed to access planes field in FrustumIntersection", e); 68 | } 69 | return planes; 70 | } 71 | 72 | public boolean testCubeQuick(float wx, float wy, float wz) { 73 | // Skip far plane checks because it has been ensured by searchDistance and isWithinRenderDistance check in OcclusionCuller 74 | return nxX * wx + nxY * wy + nxZ * wz >= negNxW && 75 | pxX * wx + pxY * wy + pxZ * wz >= negPxW && 76 | nyX * wx + nyY * wy + nyZ * wz >= negNyW && 77 | pyX * wx + pyY * wy + pyZ * wz >= negPyW && 78 | nzX * wx + nzY * wy + nzZ * wz >= negNzW; 79 | } 80 | 81 | @Override 82 | public boolean testAab(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { 83 | return testCubeQuick(minX + maxX, minY + maxY, minZ + maxZ); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/com/moepus/flerovium/functions/Chunk/Occlusion.java: -------------------------------------------------------------------------------- 1 | package com.moepus.flerovium.functions.Chunk; 2 | 3 | import me.jellysquid.mods.sodium.client.render.chunk.occlusion.GraphDirection; 4 | import org.spongepowered.asm.mixin.Unique; 5 | 6 | public class Occlusion { 7 | public static long ThroughUpDown = between(GraphDirection.UP, GraphDirection.DOWN); 8 | 9 | public static long ThroughNorthSouth = between(GraphDirection.NORTH, GraphDirection.SOUTH); 10 | 11 | public static long ThroughEastWest = between(GraphDirection.EAST, GraphDirection.WEST); 12 | 13 | public static int bit(int from, int to) { 14 | return (from * 8) + to; 15 | } 16 | 17 | public static long dir(int from, int to) { 18 | return (1L << bit(from, to)); 19 | } 20 | 21 | public static long between(int from, int to) { 22 | return dir(from, to) | dir(to, from); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/moepus/flerovium/functions/DummyModel.java: -------------------------------------------------------------------------------- 1 | package com.moepus.flerovium.functions; 2 | 3 | 4 | import net.minecraft.client.renderer.block.model.BakedQuad; 5 | import net.minecraft.client.renderer.block.model.ItemOverrides; 6 | import net.minecraft.client.renderer.texture.TextureAtlasSprite; 7 | import net.minecraft.client.resources.model.BakedModel; 8 | import net.minecraft.core.Direction; 9 | import net.minecraft.util.RandomSource; 10 | import net.minecraft.world.level.block.state.BlockState; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | import java.util.List; 14 | 15 | public class DummyModel implements BakedModel { 16 | static final List EMPTY_QUADS = List.of(); 17 | 18 | @Override 19 | public List getQuads(@Nullable BlockState blockState, @Nullable Direction direction, RandomSource randomSource) { 20 | return EMPTY_QUADS; 21 | } 22 | 23 | @Override 24 | public boolean useAmbientOcclusion() { 25 | return false; 26 | } 27 | 28 | @Override 29 | public boolean isGui3d() { 30 | return false; 31 | } 32 | 33 | @Override 34 | public boolean usesBlockLight() { 35 | return false; 36 | } 37 | 38 | @Override 39 | public boolean isCustomRenderer() { 40 | return false; 41 | } 42 | 43 | @Override 44 | public TextureAtlasSprite getParticleIcon() { 45 | return null; 46 | } 47 | 48 | @Override 49 | public ItemOverrides getOverrides() { 50 | return null; 51 | } 52 | } -------------------------------------------------------------------------------- /src/main/java/com/moepus/flerovium/functions/FastEntityRenderer.java: -------------------------------------------------------------------------------- 1 | package com.moepus.flerovium.functions; 2 | 3 | import com.moepus.flerovium.Flerovium; 4 | import com.mojang.blaze3d.systems.RenderSystem; 5 | import com.mojang.blaze3d.vertex.PoseStack; 6 | import me.jellysquid.mods.sodium.client.render.immediate.model.ModelCuboid; 7 | import me.jellysquid.mods.sodium.client.render.immediate.model.ModelPartData; 8 | import net.caffeinemc.mods.sodium.api.math.MatrixHelper; 9 | import net.caffeinemc.mods.sodium.api.vertex.attributes.common.NormalAttribute; 10 | import net.caffeinemc.mods.sodium.api.vertex.buffer.VertexBufferWriter; 11 | import net.caffeinemc.mods.sodium.api.vertex.format.common.ModelVertex; 12 | import net.minecraft.client.model.geom.ModelPart; 13 | import org.apache.commons.lang3.ArrayUtils; 14 | import org.embeddedt.embeddium.render.matrix_stack.CachingPoseStack; 15 | import org.joml.*; 16 | import org.lwjgl.system.MemoryStack; 17 | import org.lwjgl.system.MemoryUtil; 18 | 19 | import static com.moepus.flerovium.functions.MathUtil.*; 20 | 21 | public class FastEntityRenderer { 22 | 23 | private static final int NUM_CUBE_VERTICES = 8; 24 | private static final int NUM_CUBE_FACES = 6; 25 | private static final int NUM_FACE_VERTICES = 4; 26 | 27 | private static final byte 28 | FACE_NEG_Y = 0, // DOWN 29 | FACE_POS_Y = 1, // UP 30 | FACE_NEG_Z = 2, // NORTH 31 | FACE_POS_Z = 3, // SOUTH 32 | FACE_NEG_X = 4, // WEST 33 | FACE_POS_X = 5; // EAST 34 | 35 | private static final byte 36 | VERTEX_X1_Y1_Z1 = 0, 37 | VERTEX_X2_Y1_Z1 = 1, 38 | VERTEX_X2_Y2_Z1 = 2, 39 | VERTEX_X1_Y2_Z1 = 3, 40 | VERTEX_X1_Y1_Z2 = 4, 41 | VERTEX_X2_Y1_Z2 = 5, 42 | VERTEX_X2_Y2_Z2 = 6, 43 | VERTEX_X1_Y2_Z2 = 7; 44 | 45 | 46 | private static final long SCRATCH_BUFFER = MemoryUtil.nmemAlignedAlloc(64, NUM_CUBE_FACES * NUM_FACE_VERTICES * ModelVertex.STRIDE); 47 | private static final MemoryStack STACK = MemoryStack.create(); 48 | 49 | static class Vertex { 50 | public long xy; 51 | public long zw; 52 | 53 | public void set(float x, float y, float z, int color) { 54 | this.xy = compose(Float.floatToRawIntBits(x), Float.floatToRawIntBits(y)); 55 | this.zw = compose(Float.floatToRawIntBits(z), color); 56 | } 57 | } 58 | 59 | private static final Vertex[] CUBE_CORNERS = new Vertex[NUM_CUBE_VERTICES]; 60 | private static final byte[][] CUBE_VERTICES = new byte[][]{ 61 | {VERTEX_X2_Y1_Z2, VERTEX_X1_Y1_Z2, VERTEX_X1_Y1_Z1, VERTEX_X2_Y1_Z1}, 62 | {VERTEX_X2_Y2_Z1, VERTEX_X1_Y2_Z1, VERTEX_X1_Y2_Z2, VERTEX_X2_Y2_Z2}, 63 | {VERTEX_X2_Y1_Z1, VERTEX_X1_Y1_Z1, VERTEX_X1_Y2_Z1, VERTEX_X2_Y2_Z1}, 64 | {VERTEX_X1_Y1_Z2, VERTEX_X2_Y1_Z2, VERTEX_X2_Y2_Z2, VERTEX_X1_Y2_Z2}, 65 | {VERTEX_X1_Y1_Z1, VERTEX_X1_Y1_Z2, VERTEX_X1_Y2_Z2, VERTEX_X1_Y2_Z1}, 66 | {VERTEX_X2_Y1_Z2, VERTEX_X2_Y1_Z1, VERTEX_X2_Y2_Z1, VERTEX_X2_Y2_Z2}, 67 | }; 68 | 69 | private static final Vertex[][] VERTEX_POSITIONS = new Vertex[NUM_CUBE_FACES][NUM_FACE_VERTICES]; 70 | private static final long[][] VERTEX_TEXTURES = new long[NUM_CUBE_FACES][NUM_FACE_VERTICES]; 71 | 72 | private static final int[] CUBE_NORMALS = new int[NUM_CUBE_FACES]; 73 | 74 | private static int FACE; 75 | 76 | static { 77 | for (int cornerIndex = 0; cornerIndex < NUM_CUBE_VERTICES; cornerIndex++) { 78 | CUBE_CORNERS[cornerIndex] = new Vertex(); 79 | } 80 | 81 | for (int quadIndex = 0; quadIndex < NUM_CUBE_FACES; quadIndex++) { 82 | for (int vertexIndex = 0; vertexIndex < NUM_FACE_VERTICES; vertexIndex++) { 83 | VERTEX_POSITIONS[quadIndex][vertexIndex] = CUBE_CORNERS[CUBE_VERTICES[quadIndex][vertexIndex]]; 84 | } 85 | } 86 | } 87 | 88 | public static void render(PoseStack matrixStack, VertexBufferWriter writer, ModelPart part, int light, int overlay, int color) { 89 | ModelPartData accessor = ModelPartData.from(part); 90 | 91 | if (!accessor.isVisible()) { 92 | return; 93 | } 94 | 95 | var cuboids = accessor.getCuboids(); 96 | var children = accessor.getChildren(); 97 | 98 | if (ArrayUtils.isEmpty(cuboids) && ArrayUtils.isEmpty(children)) { 99 | return; 100 | } 101 | 102 | ((CachingPoseStack) matrixStack).embeddium$setCachingEnabled(true); 103 | 104 | matrixStack.pushPose(); 105 | 106 | part.translateAndRotate(matrixStack); 107 | 108 | if (!accessor.isHidden()) { 109 | renderCuboids(matrixStack.last(), writer, cuboids, light, overlay, color); 110 | } 111 | 112 | renderChildren(matrixStack, writer, light, overlay, color, children); 113 | 114 | matrixStack.popPose(); 115 | 116 | ((CachingPoseStack) matrixStack).embeddium$setCachingEnabled(false); 117 | } 118 | 119 | private static void renderCuboids(PoseStack.Pose matrices, VertexBufferWriter writer, ModelCuboid[] cuboids, int light, int overlay, int color) { 120 | prepareNormals(matrices); 121 | 122 | for (ModelCuboid cuboid : cuboids) { 123 | prepareVertices(matrices, cuboid, color); 124 | 125 | var vertexCount = emitQuads(cuboid, overlay, light); 126 | 127 | try (MemoryStack stack = MemoryStack.stackPush()) { 128 | writer.push(stack, SCRATCH_BUFFER, vertexCount, ModelVertex.FORMAT); 129 | } 130 | } 131 | } 132 | 133 | private static void renderChildren(PoseStack matrices, VertexBufferWriter writer, int light, int overlay, int color, ModelPart[] children) { 134 | for (ModelPart part : children) { 135 | render(matrices, writer, part, light, overlay, color); 136 | } 137 | } 138 | 139 | public static void renderCuboidFast(PoseStack.Pose matrices, VertexBufferWriter writer, ModelCuboid cuboid, int light, int overlay, int color) { 140 | prepareVertices(matrices, cuboid, color); 141 | 142 | var vertexCount = emitQuads(cuboid, overlay, light); 143 | 144 | STACK.push(); 145 | writer.push(STACK, SCRATCH_BUFFER, vertexCount, ModelVertex.FORMAT); 146 | STACK.pop(); 147 | } 148 | 149 | private static int emitQuads(ModelCuboid cuboid, int overlay, int light) { 150 | final long packedOverlayLight = compose(overlay, light); 151 | var vertexCount = 0; 152 | long ptr = SCRATCH_BUFFER; 153 | 154 | if (!cuboid.mirror) { 155 | for (int quadIndex = 0; quadIndex < NUM_CUBE_FACES; quadIndex++) { 156 | if (!cuboid.shouldDrawFace(quadIndex) || (FACE & (1 << quadIndex)) == 0) { 157 | continue; 158 | } 159 | int normal = CUBE_NORMALS[quadIndex]; 160 | 161 | emitVertex(ptr, VERTEX_POSITIONS[quadIndex][0], VERTEX_TEXTURES[quadIndex][0], packedOverlayLight, normal); 162 | ptr += ModelVertex.STRIDE; 163 | 164 | emitVertex(ptr, VERTEX_POSITIONS[quadIndex][1], VERTEX_TEXTURES[quadIndex][1], packedOverlayLight, normal); 165 | ptr += ModelVertex.STRIDE; 166 | 167 | emitVertex(ptr, VERTEX_POSITIONS[quadIndex][2], VERTEX_TEXTURES[quadIndex][2], packedOverlayLight, normal); 168 | ptr += ModelVertex.STRIDE; 169 | 170 | emitVertex(ptr, VERTEX_POSITIONS[quadIndex][3], VERTEX_TEXTURES[quadIndex][3], packedOverlayLight, normal); 171 | ptr += ModelVertex.STRIDE; 172 | 173 | vertexCount += 4; 174 | } 175 | } else { 176 | for (int quadIndex = 0; quadIndex < NUM_CUBE_FACES; quadIndex++) { 177 | if (!cuboid.shouldDrawFace(quadIndex) || (FACE & (1 << quadIndex)) == 0) { 178 | continue; 179 | } 180 | int normal = CUBE_NORMALS[quadIndex]; 181 | 182 | emitVertex(ptr, VERTEX_POSITIONS[quadIndex][3], VERTEX_TEXTURES[quadIndex][3], packedOverlayLight, normal); 183 | ptr += ModelVertex.STRIDE; 184 | 185 | emitVertex(ptr, VERTEX_POSITIONS[quadIndex][2], VERTEX_TEXTURES[quadIndex][2], packedOverlayLight, normal); 186 | ptr += ModelVertex.STRIDE; 187 | 188 | emitVertex(ptr, VERTEX_POSITIONS[quadIndex][1], VERTEX_TEXTURES[quadIndex][1], packedOverlayLight, normal); 189 | ptr += ModelVertex.STRIDE; 190 | 191 | emitVertex(ptr, VERTEX_POSITIONS[quadIndex][0], VERTEX_TEXTURES[quadIndex][0], packedOverlayLight, normal); 192 | ptr += ModelVertex.STRIDE; 193 | 194 | vertexCount += 4; 195 | } 196 | } 197 | 198 | return vertexCount; 199 | } 200 | 201 | private static void emitVertex(long ptr, Vertex vertex, long uv, long packedOverlayLight, int normal) { 202 | MemoryUtil.memPutLong(ptr + 0L, vertex.xy); 203 | MemoryUtil.memPutLong(ptr + 8L, vertex.zw); // overlaps with color attribute 204 | MemoryUtil.memPutLong(ptr + 16L, uv); 205 | MemoryUtil.memPutLong(ptr + 24L, packedOverlayLight); 206 | NormalAttribute.set(ptr + 32L, normal); 207 | } 208 | 209 | private static void prepareVertices(PoseStack.Pose matrices, ModelCuboid cuboid, int color) { 210 | Matrix4f pose = matrices.pose(); 211 | 212 | /** 213 | * Build a Cube from a Vertex and 3 Vectors 214 | * 215 | * Using one vertex (P1) and three vectors (X, Y, Z): 216 | * 217 | * P8 +----------------+ P7 218 | * /| /| 219 | * / | / | 220 | * P4 +----------------+ P3| 221 | * | | | | 222 | * | | | | 223 | * | P5 +-----------|--+ P6 224 | * | / | / 225 | * |/ |/ 226 | * P1 +----------------+ P2 227 | * 228 | * Vertices: 229 | * P2 = P1 + X 230 | * P3 = P2 + Y 231 | * P4 = P1 + Y 232 | * P5 = P1 + Z 233 | * P6 = P2 + Z 234 | * P7 = P6 + Y 235 | * P8 = P5 + Y 236 | */ 237 | float p1x = MatrixHelper.transformPositionX(pose, cuboid.x1, cuboid.y1, cuboid.z1); 238 | float p1y = MatrixHelper.transformPositionY(pose, cuboid.x1, cuboid.y1, cuboid.z1); 239 | float p1z = MatrixHelper.transformPositionZ(pose, cuboid.x1, cuboid.y1, cuboid.z1); 240 | CUBE_CORNERS[VERTEX_X1_Y1_Z1].set(p1x, p1y, p1z, color); 241 | 242 | float lx = cuboid.x2 - cuboid.x1, ly = cuboid.y2 - cuboid.y1, lz = cuboid.z2 - cuboid.z1; 243 | float vxx = pose.m00() * lx, vxy = pose.m01() * lx, vxz = pose.m02() * lx; 244 | float vyx = pose.m10() * ly, vyy = pose.m11() * ly, vyz = pose.m12() * ly; 245 | float vzx = pose.m20() * lz, vzy = pose.m21() * lz, vzz = pose.m22() * lz; 246 | 247 | float p2x = p1x + vxx; 248 | float p2y = p1y + vxy; 249 | float p2z = p1z + vxz; 250 | CUBE_CORNERS[VERTEX_X2_Y1_Z1].set(p2x, p2y, p2z, color); 251 | 252 | float p3x = p2x + vyx; 253 | float p3y = p2y + vyy; 254 | float p3z = p2z + vyz; 255 | CUBE_CORNERS[VERTEX_X2_Y2_Z1].set(p3x, p3y, p3z, color); 256 | 257 | float p4x = p1x + vyx; 258 | float p4y = p1y + vyy; 259 | float p4z = p1z + vyz; 260 | CUBE_CORNERS[VERTEX_X1_Y2_Z1].set(p4x, p4y, p4z, color); 261 | 262 | float p5x = p1x + vzx; 263 | float p5y = p1y + vzy; 264 | float p5z = p1z + vzz; 265 | CUBE_CORNERS[VERTEX_X1_Y1_Z2].set(p5x, p5y, p5z, color); 266 | 267 | float p6x = p2x + vzx; 268 | float p6y = p2y + vzy; 269 | float p6z = p2z + vzz; 270 | CUBE_CORNERS[VERTEX_X2_Y1_Z2].set(p6x, p6y, p6z, color); 271 | 272 | float p7x = p3x + vzx; 273 | float p7y = p3y + vzy; 274 | float p7z = p3z + vzz; 275 | CUBE_CORNERS[VERTEX_X2_Y2_Z2].set(p7x, p7y, p7z, color); 276 | 277 | float p8x = p4x + vzx; 278 | float p8y = p4y + vzy; 279 | float p8z = p4z + vzz; 280 | CUBE_CORNERS[VERTEX_X1_Y2_Z2].set(p8x, p8y, p8z, color); 281 | 282 | buildVertexTexCoord(VERTEX_TEXTURES[FACE_NEG_Y], cuboid.u1, cuboid.v0, cuboid.u2, cuboid.v1); 283 | buildVertexTexCoord(VERTEX_TEXTURES[FACE_POS_Y], cuboid.u2, cuboid.v1, cuboid.u3, cuboid.v0); 284 | buildVertexTexCoord(VERTEX_TEXTURES[FACE_NEG_Z], cuboid.u1, cuboid.v1, cuboid.u2, cuboid.v2); 285 | buildVertexTexCoord(VERTEX_TEXTURES[FACE_POS_Z], cuboid.u4, cuboid.v1, cuboid.u5, cuboid.v2); 286 | buildVertexTexCoord(VERTEX_TEXTURES[FACE_NEG_X], cuboid.u0, cuboid.v1, cuboid.u1, cuboid.v2); 287 | buildVertexTexCoord(VERTEX_TEXTURES[FACE_POS_X], cuboid.u2, cuboid.v1, cuboid.u4, cuboid.v2); 288 | 289 | FACE = ~0; 290 | if (!Flerovium.config.entityBackFaceCulling) 291 | return; 292 | 293 | if (matrices.pose().m32() <= -16.0F && RenderSystem.modelViewMatrix.m32() == 0 && ly != 0) { 294 | Matrix3f normal = matrices.normal(); 295 | 296 | float posX = p1x + p8x; 297 | float posY = p1y + p8y; 298 | float posZ = p1z + p8z; 299 | if (posX * normal.m00 + posY * normal.m01 + posZ * normal.m02 < 0) 300 | FACE &= ~(1 << (lx > 0 ? FACE_NEG_X : FACE_POS_X)); 301 | 302 | posX = p2x + p7x; 303 | posY = p2y + p7y; 304 | posZ = p2z + p7z; 305 | if (posX * normal.m00 + posY * normal.m01 + posZ * normal.m02 > 0) 306 | FACE &= ~(1 << (lx < 0 ? FACE_NEG_X : FACE_POS_X)); 307 | 308 | posX = p1x + p3x; 309 | posY = p1y + p3y; 310 | posZ = p1z + p3z; 311 | if (posX * normal.m20 + posY * normal.m21 + posZ * normal.m22 < 0) FACE &= ~(1 << FACE_NEG_Z); 312 | 313 | posX = p5x + p7x; 314 | posY = p5y + p7y; 315 | posZ = p5z + p7z; 316 | if (posX * normal.m20 + posY * normal.m21 + posZ * normal.m22 > 0) FACE &= ~(1 << FACE_POS_Z); 317 | 318 | posX = p1x + p6x; 319 | posY = p1y + p6y; 320 | posZ = p1z + p6z; 321 | if (posX * normal.m10 + posY * normal.m11 + posZ * normal.m12 < 0) FACE &= ~(1 << FACE_NEG_Y); 322 | 323 | posX = p4x + p7x; 324 | posY = p4y + p7y; 325 | posZ = p4z + p7z; 326 | if (posX * normal.m10 + posY * normal.m11 + posZ * normal.m12 > 0) FACE &= ~(1 << FACE_POS_Y); 327 | } 328 | } 329 | 330 | public static void prepareNormals(PoseStack.Pose matrices) { 331 | Matrix3f normal = matrices.normal(); 332 | CUBE_NORMALS[FACE_NEG_Y] = normal2IntClamp(-normal.m10, -normal.m11, -normal.m12); 333 | CUBE_NORMALS[FACE_POS_Y] = normal2IntClamp(normal.m10, normal.m11, normal.m12); 334 | CUBE_NORMALS[FACE_NEG_Z] = normal2IntClamp(-normal.m20, -normal.m21, -normal.m22); 335 | CUBE_NORMALS[FACE_POS_Z] = normal2IntClamp(normal.m20, normal.m21, normal.m22); 336 | CUBE_NORMALS[FACE_NEG_X] = normal2IntClamp(-normal.m00, -normal.m01, -normal.m02); 337 | CUBE_NORMALS[FACE_POS_X] = normal2IntClamp(normal.m00, normal.m01, normal.m02); 338 | } 339 | 340 | private static void buildVertexTexCoord(long[] uvs, float u1, float v1, float u2, float v2) { 341 | uvs[0] = compose(Float.floatToRawIntBits(u2), Float.floatToRawIntBits(v1)); 342 | uvs[1] = compose(Float.floatToRawIntBits(u1), Float.floatToRawIntBits(v1)); 343 | uvs[2] = compose(Float.floatToRawIntBits(u1), Float.floatToRawIntBits(v2)); 344 | uvs[3] = compose(Float.floatToRawIntBits(u2), Float.floatToRawIntBits(v2)); 345 | } 346 | } 347 | -------------------------------------------------------------------------------- /src/main/java/com/moepus/flerovium/functions/FastSimpleBakedModelRenderer.java: -------------------------------------------------------------------------------- 1 | package com.moepus.flerovium.functions; 2 | 3 | import com.mojang.blaze3d.vertex.PoseStack; 4 | import me.jellysquid.mods.sodium.client.model.color.interop.ItemColorsExtended; 5 | import me.jellysquid.mods.sodium.client.model.quad.BakedQuadView; 6 | import me.jellysquid.mods.sodium.client.model.quad.properties.ModelQuadFacing; 7 | import me.jellysquid.mods.sodium.client.render.texture.SpriteUtil; 8 | import net.caffeinemc.mods.sodium.api.math.MatrixHelper; 9 | import net.caffeinemc.mods.sodium.api.util.ColorARGB; 10 | import net.caffeinemc.mods.sodium.api.util.NormI8; 11 | import net.caffeinemc.mods.sodium.api.vertex.buffer.VertexBufferWriter; 12 | import net.caffeinemc.mods.sodium.api.vertex.format.common.ModelVertex; 13 | import net.minecraft.client.color.item.ItemColor; 14 | import net.minecraft.client.color.item.ItemColors; 15 | import net.minecraft.client.renderer.block.model.BakedQuad; 16 | import net.minecraft.client.resources.model.SimpleBakedModel; 17 | import net.minecraft.core.Direction; 18 | import net.minecraft.world.item.ItemStack; 19 | import org.joml.Math; 20 | import org.joml.Matrix3f; 21 | import org.joml.Matrix4f; 22 | import org.joml.Vector3f; 23 | import org.lwjgl.system.MemoryStack; 24 | import org.lwjgl.system.MemoryUtil; 25 | 26 | import java.util.List; 27 | 28 | import static com.moepus.flerovium.functions.MathUtil.*; 29 | 30 | public class FastSimpleBakedModelRenderer { 31 | private static final MemoryStack STACK = MemoryStack.create(); 32 | private static final int VERTEX_COUNT = 4; 33 | private static final int BUFFER_VERTEX_COUNT = 48; 34 | private static final int STRIDE = 8; 35 | private static final long SCRATCH_BUFFER = MemoryUtil.nmemAlignedAlloc(64, BUFFER_VERTEX_COUNT * ModelVertex.STRIDE); 36 | private static long BUFFER_PTR = SCRATCH_BUFFER; 37 | private static int BUFFED_VERTEX = 0; 38 | private static int LAST_TINT_INDEX = -1; 39 | private static int LAST_TINT = -1; 40 | 41 | public static int multiplyIntBytes(int a, int b) { 42 | int first = ((a & 0xff) * (b & 0xff) + 127) / 255; 43 | int second = (((a >>> 8) & 0xff) * ((b >>> 8) & 0xff) + 127) / 255; 44 | int third = (((a >>> 16) & 0xff) * ((b >>> 16) & 0xff) + 127) / 255; 45 | return first | second << 8 | third << 16 | (b & 0xff000000); 46 | } 47 | 48 | private static void flush(VertexBufferWriter writer) { 49 | if (BUFFED_VERTEX == 0) return; 50 | STACK.push(); 51 | writer.push(STACK, SCRATCH_BUFFER, BUFFED_VERTEX, ModelVertex.FORMAT); 52 | STACK.pop(); 53 | BUFFER_PTR = SCRATCH_BUFFER; 54 | BUFFED_VERTEX = 0; 55 | } 56 | 57 | private static boolean isBufferMax() { 58 | return BUFFED_VERTEX >= BUFFER_VERTEX_COUNT; 59 | } 60 | 61 | // Check for functional storage, they are doing rotation by mul the pose only but not normal. 62 | // Sign for X axis should be the same. 63 | private static boolean checkNormalRotateEqual(PoseStack.Pose pose) { 64 | return ((Float.floatToRawIntBits(pose.pose().m00()) ^ Float.floatToRawIntBits(pose.normal().m00())) >> 31) + 65 | ((Float.floatToRawIntBits(pose.pose().m10()) ^ Float.floatToRawIntBits(pose.normal().m10())) >> 31) + 66 | ((Float.floatToRawIntBits(pose.pose().m20()) ^ Float.floatToRawIntBits(pose.normal().m20())) >> 31) 67 | == 0; 68 | } 69 | 70 | private static void putBulkData(VertexBufferWriter writer, PoseStack.Pose pose, BakedQuad bakedQuad, 71 | int light, int overlay, int color, int faces) { 72 | int[] vertices = bakedQuad.getVertices(); 73 | if (vertices.length != VERTEX_COUNT * STRIDE) return; 74 | Matrix4f pose_matrix = pose.pose(); 75 | int baked_normal = vertices[7]; 76 | float unpackedX = NormI8.unpackX(baked_normal); 77 | float unpackedY = NormI8.unpackY(baked_normal); 78 | float unpackedZ = NormI8.unpackZ(baked_normal); 79 | float nx = MatrixHelper.transformNormalX(pose.normal(), unpackedX, unpackedY, unpackedZ); 80 | float ny = MatrixHelper.transformNormalY(pose.normal(), unpackedX, unpackedY, unpackedZ); 81 | float nz = MatrixHelper.transformNormalZ(pose.normal(), unpackedX, unpackedY, unpackedZ); 82 | 83 | float x = Float.intBitsToFloat(vertices[0]), y = Float.intBitsToFloat(vertices[1]), z = Float.intBitsToFloat(vertices[2]); 84 | float pos0_x = MatrixHelper.transformPositionX(pose_matrix, x, y, z); 85 | float pos0_y = MatrixHelper.transformPositionY(pose_matrix, x, y, z); 86 | float pos0_z = MatrixHelper.transformPositionZ(pose_matrix, x, y, z); 87 | x = Float.intBitsToFloat(vertices[STRIDE * 2]); 88 | y = Float.intBitsToFloat(vertices[STRIDE * 2 + 1]); 89 | z = Float.intBitsToFloat(vertices[STRIDE * 2 + 2]); 90 | 91 | float pos2_x = MatrixHelper.transformPositionX(pose_matrix, x, y, z); 92 | float pos2_y = MatrixHelper.transformPositionY(pose_matrix, x, y, z); 93 | float pos2_z = MatrixHelper.transformPositionZ(pose_matrix, x, y, z); 94 | 95 | if ((faces & 0b1000000) != 0) { // Backface culling 96 | if (checkNormalRotateEqual(pose)) 97 | if ((pos0_x + pos2_x) * nx + (pos0_y + pos2_y) * ny + (pos0_z + pos2_z) * nz > 0) 98 | if (((BakedQuadView) bakedQuad).getNormalFace() != ModelQuadFacing.UNASSIGNED) 99 | return; 100 | } 101 | int n = packSafe(nx, ny, nz); 102 | 103 | x = Float.intBitsToFloat(vertices[STRIDE]); 104 | y = Float.intBitsToFloat(vertices[STRIDE + 1]); 105 | z = Float.intBitsToFloat(vertices[STRIDE + 2]); 106 | float pos1_x = MatrixHelper.transformPositionX(pose_matrix, x, y, z); 107 | float pos1_y = MatrixHelper.transformPositionY(pose_matrix, x, y, z); 108 | float pos1_z = MatrixHelper.transformPositionZ(pose_matrix, x, y, z); 109 | 110 | x = Float.intBitsToFloat(vertices[STRIDE * 3]); 111 | y = Float.intBitsToFloat(vertices[STRIDE * 3 + 1]); 112 | z = Float.intBitsToFloat(vertices[STRIDE * 3 + 2]); 113 | float pos3_x = MatrixHelper.transformPositionX(pose_matrix, x, y, z); 114 | float pos3_y = MatrixHelper.transformPositionY(pose_matrix, x, y, z); 115 | float pos3_z = MatrixHelper.transformPositionZ(pose_matrix, x, y, z); 116 | 117 | final int c = color != -1 ? multiplyIntBytes(color, vertices[3]):vertices[3]; 118 | final int baked = vertices[6]; 119 | final int l = Math.max(((baked & 0xffff) << 16) | (baked >> 16), light); 120 | ModelVertex.write(BUFFER_PTR, pos0_x, pos0_y, pos0_z, c, Float.intBitsToFloat(vertices[4]), 121 | Float.intBitsToFloat(vertices[5]), overlay, l, n); 122 | BUFFER_PTR += ModelVertex.STRIDE; 123 | ModelVertex.write(BUFFER_PTR, pos1_x, pos1_y, pos1_z, c, Float.intBitsToFloat(vertices[STRIDE + 4]), 124 | Float.intBitsToFloat(vertices[STRIDE + 5]), overlay, l, n); 125 | BUFFER_PTR += ModelVertex.STRIDE; 126 | ModelVertex.write(BUFFER_PTR, pos2_x, pos2_y, pos2_z, c, Float.intBitsToFloat(vertices[STRIDE * 2 + 4]), 127 | Float.intBitsToFloat(vertices[STRIDE * 2 + 5]), overlay, l, n); 128 | BUFFER_PTR += ModelVertex.STRIDE; 129 | ModelVertex.write(BUFFER_PTR, pos3_x, pos3_y, pos3_z, c, Float.intBitsToFloat(vertices[STRIDE * 3 + 4]), 130 | Float.intBitsToFloat(vertices[STRIDE * 3 + 5]), overlay, l, n); 131 | BUFFER_PTR += ModelVertex.STRIDE; 132 | 133 | BUFFED_VERTEX += VERTEX_COUNT; 134 | if (isBufferMax()) flush(writer); 135 | } 136 | 137 | public static int GetItemTint(int tintIndex, ItemStack itemStack, ItemColor colorProvider) { 138 | if (tintIndex == LAST_TINT_INDEX) return LAST_TINT; 139 | int tint = colorProvider.getColor(itemStack, tintIndex); 140 | LAST_TINT = ColorARGB.toABGR(tint, 255); 141 | LAST_TINT_INDEX = tintIndex; 142 | return LAST_TINT; 143 | } 144 | 145 | private static void renderQuadList(PoseStack.Pose pose, VertexBufferWriter writer, int faces, List bakedQuads, 146 | int light, int overlay, ItemStack itemStack, ItemColor colorProvider) { 147 | for (BakedQuad bakedQuad : bakedQuads) { 148 | if ((faces & (1 << bakedQuad.getDirection().ordinal())) == 0) continue; 149 | int color = colorProvider != null && bakedQuad.getTintIndex() != -1 ? GetItemTint(bakedQuad.getTintIndex(), itemStack, colorProvider):-1; 150 | putBulkData(writer, pose, bakedQuad, light, overlay, color, faces); 151 | } 152 | if (pose.pose().m32() > -8.0F) { // Do animation for item in GUI or nearby in world 153 | for (BakedQuad bakedQuad : bakedQuads) { 154 | SpriteUtil.markSpriteActive(bakedQuad.getSprite()); 155 | } 156 | } 157 | } 158 | 159 | public static void render(SimpleBakedModel model, int faces, ItemStack itemStack, int packedLight, int packedOverlay, 160 | PoseStack poseStack, VertexBufferWriter writer, ItemColors itemColors) { 161 | PoseStack.Pose pose = poseStack.last(); 162 | ItemColor colorProvider = !itemStack.isEmpty() ? ((ItemColorsExtended) itemColors).sodium$getColorProvider(itemStack):null; 163 | 164 | LAST_TINT_INDEX = LAST_TINT = -1; 165 | for (Direction direction : Direction.values()) { 166 | renderQuadList(pose, writer, faces, model.getQuads(null, direction, null), packedLight, packedOverlay, itemStack, colorProvider); 167 | } 168 | renderQuadList(pose, writer, faces, model.getQuads(null, null, null), packedLight, packedOverlay, itemStack, colorProvider); 169 | 170 | flush(writer); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/main/java/com/moepus/flerovium/functions/IntFlatMap.java: -------------------------------------------------------------------------------- 1 | package com.moepus.flerovium.functions; 2 | 3 | import java.util.Arrays; 4 | 5 | public class IntFlatMap { 6 | private int[] keys; 7 | private V[] values; 8 | private int size = 0; 9 | 10 | @SuppressWarnings("unchecked") 11 | public IntFlatMap(int capacity) { 12 | this.keys = new int[capacity]; 13 | this.values = (V[]) new Object[capacity]; 14 | } 15 | 16 | public int size() { 17 | return size; 18 | } 19 | 20 | public boolean isEmpty() { 21 | return size == 0; 22 | } 23 | 24 | public V get(int key) { 25 | int index = findIndex(key); 26 | return index >= 0 ? values[index] : null; 27 | } 28 | 29 | public void put(int key, V value) { 30 | if (size >= keys.length) { 31 | throw new ArrayIndexOutOfBoundsException(); 32 | } 33 | keys[size] = key; 34 | values[size] = value; 35 | size++; 36 | } 37 | 38 | public V remove(int key) { 39 | int index = findIndex(key); 40 | if (index < 0) { 41 | return null; 42 | } 43 | V oldValue = values[index]; 44 | shift(index); 45 | return oldValue; 46 | } 47 | 48 | private int findIndex(int key) { 49 | return Arrays.binarySearch(keys, key); 50 | } 51 | 52 | private void shift(int index) { 53 | System.arraycopy(keys, index + 1, keys, index, size - index - 1); 54 | System.arraycopy(values, index + 1, values, index, size - index - 1); 55 | values[size - 1] = null; 56 | size--; 57 | } 58 | 59 | public void resize(int newCapacity) { 60 | keys = Arrays.copyOf(keys, newCapacity); 61 | values = Arrays.copyOf(values, newCapacity); 62 | } 63 | 64 | public void sort() { 65 | if (size != keys.length) { 66 | resize(size); 67 | } 68 | quickSort(0, size - 1); 69 | } 70 | 71 | private void quickSort(int low, int high) { 72 | if (low < high) { 73 | int pi = partition(low, high); 74 | quickSort(low, pi - 1); 75 | quickSort(pi + 1, high); 76 | } 77 | } 78 | 79 | private int partition(int low, int high) { 80 | int pivot = keys[high]; 81 | int i = (low - 1); 82 | for (int j = low; j < high; j++) { 83 | if (keys[j] < pivot) { 84 | i++; 85 | swap(i, j); 86 | } 87 | } 88 | swap(i + 1, high); 89 | return i + 1; 90 | } 91 | 92 | private void swap(int i, int j) { 93 | int tempintey = keys[i]; 94 | keys[i] = keys[j]; 95 | keys[j] = tempintey; 96 | 97 | V tempValue = values[i]; 98 | values[i] = values[j]; 99 | values[j] = tempValue; 100 | } 101 | 102 | public void clear() { 103 | Arrays.fill(keys, 0, size, 0); 104 | Arrays.fill(values, 0, size, null); 105 | size = 0; 106 | } 107 | } -------------------------------------------------------------------------------- /src/main/java/com/moepus/flerovium/functions/MathUtil.java: -------------------------------------------------------------------------------- 1 | package com.moepus.flerovium.functions; 2 | 3 | import net.minecraft.util.Mth; 4 | import org.joml.Math; 5 | 6 | public class MathUtil { 7 | 8 | static final public float[] PACK_FACTOR = {127.0f, -127.0f}; 9 | public static int packSafe(float x, float y, float z, float factor) { 10 | float scalar = Math.invsqrt(Math.fma(x, x, Math.fma(y, y, z * z))); 11 | 12 | x *= scalar; 13 | y *= scalar; 14 | z *= scalar; 15 | 16 | int normX = (int) (x * factor) & 255; 17 | int normY = (int) (y * factor) & 255; 18 | int normZ = (int) (z * factor) & 255; 19 | 20 | return (normZ << 16) | (normY << 8) | normX; 21 | } 22 | 23 | public static int packSafe(float x, float y, float z) { 24 | float scalar = Math.invsqrt(Math.fma(x, x, Math.fma(y, y, z * z))); 25 | 26 | x *= scalar; 27 | y *= scalar; 28 | z *= scalar; 29 | 30 | int normX = (int) (x * 127.0f) & 255; 31 | int normY = (int) (y * 127.0f) & 255; 32 | int normZ = (int) (z * 127.0f) & 255; 33 | 34 | return (normZ << 16) | (normY << 8) | normX; 35 | } 36 | 37 | public static int packUnsafe(float x, float y, float z, float factor) { 38 | int normX = (int) (x * factor) & 255; 39 | int normY = (int) (y * factor) & 255; 40 | int normZ = (int) (z * factor) & 255; 41 | 42 | return (normZ << 16) | (normY << 8) | normX; 43 | } 44 | public static int packUnsafe(float x, float y, float z) { 45 | int normX = (int) (x * 127.0f) & 255; 46 | int normY = (int) (y * 127.0f) & 255; 47 | int normZ = (int) (z * 127.0f) & 255; 48 | 49 | return (normZ << 16) | (normY << 8) | normX; 50 | } 51 | 52 | public static int normal2Int(float x, float y, float z) { 53 | float scalar = Math.invsqrt(Math.fma(x, x, Math.fma(y, y, z * z))); 54 | return packUnsafe(x * scalar, y * scalar, z * scalar); 55 | } 56 | 57 | public static int normal2IntClamp(float x, float y, float z) { 58 | return packUnsafe(Mth.clamp(x, -1.0F, 1.0F), Mth.clamp(y, -1.0F, 1.0F), Mth.clamp(z, -1.0F, 1.0F)); 59 | } 60 | 61 | public static boolean cullBackFace(byte viewX, byte viewY, byte viewZ, int normal) { 62 | byte normalX = (byte) normal; 63 | byte normalY = (byte) (normal >> 8); 64 | byte normalZ = (byte) (normal >> 16); 65 | return (int) viewX * (int) normalX + (int) viewY * (int) normalY + (int) viewZ * (int) normalZ > 768; 66 | } 67 | 68 | public static long compose(int lo, int hi) { 69 | return ((long) hi << 32) | (lo & 0xFFFFFFFFL); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/moepus/flerovium/functions/MatrixStuff.java: -------------------------------------------------------------------------------- 1 | package com.moepus.flerovium.functions; 2 | 3 | import org.joml.Math; 4 | import org.joml.Matrix3f; 5 | import org.joml.Matrix4f; 6 | 7 | public class MatrixStuff { 8 | public static void rotateXY(Matrix4f dest, float sinX, float cosX, float sinY, float cosY) { 9 | final float lm00 = dest.m00(), lm01 = dest.m01(), lm02 = dest.m02(), lm03 = dest.m03(), 10 | lm10 = dest.m10(), lm11 = dest.m11(), lm12 = dest.m12(), lm13 = dest.m13(), 11 | lm20 = dest.m20(), lm21 = dest.m21(), lm22 = dest.m22(), lm23 = dest.m23(); 12 | 13 | final float m_sinX = -sinX; 14 | final float m_sinY = -sinY; 15 | 16 | final float xm20 = Math.fma(lm10, m_sinX, lm20 * cosX); 17 | final float xm21 = Math.fma(lm11, m_sinX, lm21 * cosX); 18 | final float xm22 = Math.fma(lm12, m_sinX, lm22 * cosX); 19 | final float xm23 = Math.fma(lm13, m_sinX, lm23 * cosX); 20 | final float xm10 = Math.fma(lm10, cosX, lm20 * sinX); 21 | final float xm11 = Math.fma(lm11, cosX, lm21 * sinX); 22 | final float xm12 = Math.fma(lm12, cosX, lm22 * sinX); 23 | final float xm13 = Math.fma(lm13, cosX, lm23 * sinX); 24 | final float nm00 = Math.fma(lm00, cosY, xm20 * m_sinY); 25 | final float nm01 = Math.fma(lm01, cosY, xm21 * m_sinY); 26 | final float nm02 = Math.fma(lm02, cosY, xm22 * m_sinY); 27 | final float nm03 = Math.fma(lm03, cosY, xm23 * m_sinY); 28 | final float ym20 = Math.fma(lm00, sinY, xm20 * cosY); 29 | final float ym21 = Math.fma(lm01, sinY, xm21 * cosY); 30 | final float ym22 = Math.fma(lm02, sinY, xm22 * cosY); 31 | final float ym23 = Math.fma(lm03, sinY, xm23 * cosY); 32 | dest.set(nm00, nm01, nm02, nm03, 33 | xm10, xm11, xm12, xm13, 34 | ym20, ym21, ym22, ym23, 35 | dest.m30(), dest.m31(), dest.m32(), dest.m33()); 36 | } 37 | 38 | public static void rotateXY(Matrix3f dest, float sinX, float cosX, float sinY, float cosY) { 39 | final float m_sinX = -sinX; 40 | final float m_sinY = -sinY; 41 | 42 | final float nm10 = Math.fma(dest.m10, cosX, dest.m20 * sinX); 43 | final float nm11 = Math.fma(dest.m11, cosX, dest.m21 * sinX); 44 | final float nm12 = Math.fma(dest.m12, cosX, dest.m22 * sinX); 45 | final float nm20 = Math.fma(dest.m10, m_sinX, dest.m20 * cosX); 46 | final float nm21 = Math.fma(dest.m11, m_sinX, dest.m21 * cosX); 47 | final float nm22 = Math.fma(dest.m12, m_sinX, dest.m22 * cosX); 48 | final float nm00 = Math.fma(dest.m00, cosY, nm20 * m_sinY); 49 | final float nm01 = Math.fma(dest.m01, cosY, nm21 * m_sinY); 50 | final float nm02 = Math.fma(dest.m02, cosY, nm22 * m_sinY); 51 | dest.m20 = Math.fma(dest.m00, sinY, nm20 * cosY); 52 | dest.m21 = Math.fma(dest.m01, sinY, nm21 * cosY); 53 | dest.m22 = Math.fma(dest.m02, sinY, nm22 * cosY); 54 | dest.m00 = nm00; 55 | dest.m01 = nm01; 56 | dest.m02 = nm02; 57 | dest.m10 = nm10; 58 | dest.m11 = nm11; 59 | dest.m12 = nm12; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/moepus/flerovium/mixins/Chunk/FrustumMixin.java: -------------------------------------------------------------------------------- 1 | package com.moepus.flerovium.mixins.Chunk; 2 | 3 | import com.moepus.flerovium.functions.Chunk.FastSimpleFrustum; 4 | import me.jellysquid.mods.sodium.client.render.viewport.Viewport; 5 | import me.jellysquid.mods.sodium.client.render.viewport.ViewportProvider; 6 | import net.minecraft.client.renderer.culling.Frustum; 7 | import org.joml.FrustumIntersection; 8 | import org.joml.Vector3d; 9 | import org.spongepowered.asm.mixin.Final; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.Shadow; 12 | 13 | @Mixin(Frustum.class) 14 | public class FrustumMixin implements ViewportProvider { 15 | @Shadow 16 | public double camX; 17 | 18 | @Shadow 19 | public double camY; 20 | 21 | @Shadow 22 | public double camZ; 23 | 24 | @Shadow 25 | @Final 26 | public FrustumIntersection intersection; 27 | 28 | @Override 29 | public Viewport sodium$createViewport() { 30 | return new Viewport(new FastSimpleFrustum(this.intersection), new Vector3d(this.camX, this.camY, this.camZ)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/moepus/flerovium/mixins/Chunk/OcclusionCullerMixin.java: -------------------------------------------------------------------------------- 1 | package com.moepus.flerovium.mixins.Chunk; 2 | 3 | import com.moepus.flerovium.functions.Chunk.Occlusion; 4 | import me.jellysquid.mods.sodium.client.render.chunk.RenderSection; 5 | import me.jellysquid.mods.sodium.client.render.chunk.occlusion.GraphDirectionSet; 6 | import me.jellysquid.mods.sodium.client.render.chunk.occlusion.OcclusionCuller; 7 | import me.jellysquid.mods.sodium.client.render.chunk.occlusion.VisibilityEncoding; 8 | import me.jellysquid.mods.sodium.client.render.viewport.Viewport; 9 | import me.jellysquid.mods.sodium.client.util.collections.ReadQueue; 10 | import me.jellysquid.mods.sodium.client.util.collections.WriteQueue; 11 | import net.minecraft.core.SectionPos; 12 | import org.spongepowered.asm.mixin.*; 13 | 14 | @Mixin(value = OcclusionCuller.class, remap = false) 15 | public abstract class OcclusionCullerMixin { 16 | @Shadow 17 | private static boolean isSectionVisible(RenderSection section, Viewport viewport, float searchDistance) { 18 | return false; 19 | } 20 | 21 | @Shadow 22 | private static int getOutwardDirections(SectionPos origin, RenderSection section) { 23 | return 0; 24 | } 25 | 26 | @Shadow 27 | private static void visitNeighbors(final WriteQueue queue, RenderSection section, int outgoing, int frame) { 28 | } 29 | 30 | /** 31 | * @author MoePus 32 | * @reason Cull More Chunks 33 | */ 34 | @Overwrite 35 | private static void processQueue(OcclusionCuller.Visitor visitor, 36 | Viewport viewport, 37 | float searchDistance, 38 | boolean useOcclusionCulling, 39 | int frame, 40 | ReadQueue readQueue, 41 | WriteQueue writeQueue) { 42 | RenderSection section; 43 | 44 | while ((section = readQueue.dequeue()) != null) { 45 | if (!isSectionVisible(section, viewport, searchDistance)) { 46 | continue; 47 | } 48 | visitor.visit(section, true); 49 | 50 | int connections; 51 | { 52 | if (useOcclusionCulling) { 53 | long visibilityData = flerovium$processVisibilityByAngleOcculusion(viewport, section, section.getVisibilityData()); 54 | 55 | // When using occlusion culling, we can only traverse into neighbors for which there is a path of 56 | // visibility through this chunk. This is determined by taking all the incoming paths to this chunk and 57 | // creating a union of the outgoing paths from those. 58 | connections = VisibilityEncoding.getConnections(visibilityData, section.getIncomingDirections()); 59 | } else { 60 | // Not using any occlusion culling, so traversing in any direction is legal. 61 | connections = GraphDirectionSet.ALL; 62 | } 63 | 64 | // We can only traverse *outwards* from the center of the graph search, so mask off any invalid 65 | // directions. 66 | connections &= getOutwardDirections(viewport.getChunkCoord(), section); 67 | } 68 | 69 | visitNeighbors(writeQueue, section, connections, frame); 70 | } 71 | } 72 | 73 | // Picked from https://github.com/CaffeineMC/sodium/pull/2811 74 | @Unique 75 | private static long flerovium$processVisibilityByAngleOcculusion(Viewport viewport, RenderSection section, long visibilityData) { 76 | double dx = Math.abs(viewport.getTransform().x - section.getCenterX()); 77 | double dy = Math.abs(viewport.getTransform().y - section.getCenterY()); 78 | double dz = Math.abs(viewport.getTransform().z - section.getCenterZ()); 79 | 80 | long mask = 0L; 81 | 82 | if (dx < dy || dx < dz) { 83 | mask |= Occlusion.ThroughEastWest; 84 | } 85 | if (dy < dx || dy < dz) { 86 | mask |= Occlusion.ThroughUpDown; 87 | } 88 | if (dz < dx || dz < dy) { 89 | mask |= Occlusion.ThroughNorthSouth; 90 | } 91 | 92 | return visibilityData & ~mask; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/com/moepus/flerovium/mixins/Entity/ClientLevelMixin.java: -------------------------------------------------------------------------------- 1 | package com.moepus.flerovium.mixins.Entity; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import net.minecraft.client.multiplayer.ClientLevel; 5 | import net.minecraft.client.player.LocalPlayer; 6 | import net.minecraft.world.entity.Entity; 7 | import net.minecraft.world.entity.EntitySelector; 8 | import net.minecraft.world.level.CommonLevelAccessor; 9 | import net.minecraft.world.phys.AABB; 10 | import net.minecraft.world.phys.shapes.Shapes; 11 | import net.minecraft.world.phys.shapes.VoxelShape; 12 | import org.jetbrains.annotations.NotNull; 13 | import org.spongepowered.asm.mixin.Mixin; 14 | import org.spongepowered.asm.mixin.Unique; 15 | 16 | import javax.annotation.Nullable; 17 | import java.util.List; 18 | import java.util.function.Predicate; 19 | 20 | @Mixin(ClientLevel.class) 21 | public abstract class ClientLevelMixin implements CommonLevelAccessor { 22 | @Unique 23 | List flerovium$getPlayerCollisions(Entity entity, AABB aabb) { 24 | if (aabb.getSize() < 1.0E-7D) { 25 | return List.of(); 26 | } 27 | List list = this.getEntities(entity, aabb.inflate(1.0E-7D), EntitySelector.NO_SPECTATORS.and(entity::canCollideWith)); 28 | if (list.isEmpty()) { 29 | return List.of(); 30 | } 31 | ImmutableList.Builder builder = ImmutableList.builderWithExpectedSize(list.size()); 32 | 33 | for (Entity e : list) { 34 | builder.add(Shapes.create(e.getBoundingBox())); 35 | } 36 | 37 | return builder.build(); 38 | } 39 | 40 | @Override 41 | public @NotNull List getEntityCollisions(@Nullable Entity entity, AABB aabb) { 42 | if (entity instanceof LocalPlayer) { 43 | return flerovium$getPlayerCollisions(entity, aabb); 44 | } 45 | return List.of(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/moepus/flerovium/mixins/Entity/ModelPartMixin.java: -------------------------------------------------------------------------------- 1 | package com.moepus.flerovium.mixins.Entity; 2 | 3 | import com.moepus.flerovium.functions.FastEntityRenderer; 4 | import me.jellysquid.mods.sodium.client.render.vertex.VertexConsumerUtils; 5 | import net.caffeinemc.mods.sodium.api.util.ColorABGR; 6 | import net.caffeinemc.mods.sodium.api.vertex.buffer.VertexBufferWriter; 7 | import net.minecraft.client.model.geom.ModelPart; 8 | import org.spongepowered.asm.mixin.*; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Inject; 11 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 12 | import com.mojang.blaze3d.vertex.PoseStack; 13 | import com.mojang.blaze3d.vertex.VertexConsumer; 14 | 15 | @Mixin(value = ModelPart.class, priority = 900) 16 | public class ModelPartMixin { 17 | @Inject(method = "render(Lcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;IIFFFF)V", at = @At("HEAD"), cancellable = true) 18 | private void onRender(PoseStack matrices, VertexConsumer vertices, int light, int overlay, float red, float green, float blue, float alpha, CallbackInfo ci) { 19 | if ((Object) this.getClass() != ModelPart.class) { 20 | return; 21 | } 22 | 23 | VertexBufferWriter writer = VertexConsumerUtils.convertOrLog(vertices); 24 | 25 | if (writer == null) { 26 | return; 27 | } 28 | 29 | ci.cancel(); 30 | 31 | FastEntityRenderer.render(matrices, writer, (ModelPart) (Object) this, light, overlay, ColorABGR.pack(red, green, blue, alpha)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/moepus/flerovium/mixins/Item/ItemEntityRenderMixin.java: -------------------------------------------------------------------------------- 1 | package com.moepus.flerovium.mixins.Item; 2 | 3 | import com.mojang.blaze3d.vertex.PoseStack; 4 | import net.minecraft.client.Minecraft; 5 | import net.minecraft.client.player.LocalPlayer; 6 | import net.minecraft.client.renderer.MultiBufferSource; 7 | import net.minecraft.client.renderer.entity.EntityRenderer; 8 | import net.minecraft.client.renderer.entity.EntityRendererProvider; 9 | import net.minecraft.client.renderer.entity.ItemEntityRenderer; 10 | import net.minecraft.world.entity.Entity; 11 | import net.minecraft.world.entity.item.ItemEntity; 12 | import net.minecraft.world.item.ItemStack; 13 | import net.minecraft.world.phys.Vec3; 14 | import net.minecraftforge.client.event.RenderNameTagEvent; 15 | import net.minecraftforge.common.MinecraftForge; 16 | import org.spongepowered.asm.mixin.Mixin; 17 | import org.spongepowered.asm.mixin.injection.At; 18 | import org.spongepowered.asm.mixin.injection.Redirect; 19 | 20 | @Mixin(ItemEntityRenderer.class) 21 | public abstract class ItemEntityRenderMixin extends EntityRenderer { 22 | protected ItemEntityRenderMixin(EntityRendererProvider.Context p_174008_) { 23 | super(p_174008_); 24 | } 25 | 26 | @Redirect( 27 | method = {"render(Lnet/minecraft/world/entity/item/ItemEntity;FFLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;I)V"}, 28 | at = @At( 29 | value = "INVOKE", 30 | target = "Lnet/minecraft/client/renderer/entity/ItemEntityRenderer;getRenderAmount(Lnet/minecraft/world/item/ItemStack;)I" 31 | ) 32 | ) 33 | private int redirectGetRenderAmount(ItemEntityRenderer instance, ItemStack itemstack, ItemEntity itemEntity) { 34 | int count = itemstack.getCount(); 35 | LocalPlayer player = Minecraft.getInstance().player; 36 | Vec3 eye = new Vec3(player.getX(), player.getY(), player.getZ()); 37 | int maxRender = (int) Math.ceil(360.0F / eye.distanceToSqr(itemEntity.getX(), itemEntity.getY(), itemEntity.getZ())); 38 | return Math.min(Math.min(count, (count - 1) / 16 + 2), Math.min(maxRender, 5)); 39 | } 40 | 41 | @Redirect( 42 | method = "render(Lnet/minecraft/world/entity/item/ItemEntity;FFLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;I)V", 43 | at = @At( 44 | value = "INVOKE", 45 | target = "Lnet/minecraft/client/renderer/entity/EntityRenderer;render(Lnet/minecraft/world/entity/Entity;FFLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;I)V" 46 | ) 47 | ) 48 | private void skipSuperRender(EntityRenderer instance, Entity entity, float p_115037_, float p_115038_, PoseStack p_115039_, MultiBufferSource p_115040_, int p_115041_) { 49 | RenderNameTagEvent renderNameTagEvent = new RenderNameTagEvent(entity, entity.getDisplayName(), this, p_115039_, p_115040_, p_115041_, p_115038_); 50 | MinecraftForge.EVENT_BUS.post(renderNameTagEvent); 51 | // skip render 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/moepus/flerovium/mixins/Item/ItemRendererMixin.java: -------------------------------------------------------------------------------- 1 | package com.moepus.flerovium.mixins.Item; 2 | 3 | import com.moepus.flerovium.Flerovium; 4 | import com.moepus.flerovium.functions.DummyModel; 5 | import com.moepus.flerovium.functions.FastSimpleBakedModelRenderer; 6 | import com.mojang.blaze3d.systems.RenderSystem; 7 | import com.mojang.blaze3d.vertex.PoseStack; 8 | import com.mojang.blaze3d.vertex.VertexConsumer; 9 | import net.caffeinemc.mods.sodium.api.vertex.buffer.VertexBufferWriter; 10 | import net.minecraft.client.color.item.ItemColors; 11 | import net.minecraft.client.renderer.block.model.ItemTransform; 12 | import net.minecraft.client.renderer.block.model.ItemTransforms; 13 | import net.minecraft.client.renderer.entity.ItemRenderer; 14 | import net.minecraft.client.resources.model.BakedModel; 15 | import net.minecraft.client.resources.model.SimpleBakedModel; 16 | import net.minecraft.core.Direction; 17 | import net.minecraft.world.item.ItemDisplayContext; 18 | import net.minecraft.world.item.ItemStack; 19 | import org.spongepowered.asm.mixin.Final; 20 | import org.spongepowered.asm.mixin.Mixin; 21 | import org.spongepowered.asm.mixin.Shadow; 22 | import org.spongepowered.asm.mixin.Unique; 23 | import org.spongepowered.asm.mixin.injection.At; 24 | import org.spongepowered.asm.mixin.injection.Inject; 25 | import org.spongepowered.asm.mixin.injection.ModifyArg; 26 | import com.llamalad7.mixinextras.sugar.Local; 27 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 28 | 29 | @Mixin(value = ItemRenderer.class, priority = 100) 30 | public abstract class ItemRendererMixin { 31 | @Final 32 | @Shadow 33 | private ItemColors itemColors; 34 | 35 | @Unique 36 | private static final DummyModel flerovium$dummy = new DummyModel(); 37 | 38 | @Inject(method = "renderModelLists(Lnet/minecraft/client/resources/model/BakedModel;Lnet/minecraft/world/item/ItemStack;IILcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;)V", at = @At("HEAD"), cancellable = true) 39 | public void renderModelLists(BakedModel model, ItemStack itemStack, int packedLight, int packedOverlay, PoseStack poseStack, VertexConsumer vertexConsumer, CallbackInfo ci) { 40 | if (model == flerovium$dummy) ci.cancel(); 41 | } 42 | 43 | @ModifyArg(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/entity/ItemRenderer;renderModelLists(Lnet/minecraft/client/resources/model/BakedModel;Lnet/minecraft/world/item/ItemStack;IILcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;)V")) 44 | public BakedModel onRrenderModelLists(BakedModel model, ItemStack stack, int light, int overlay, PoseStack poseStack, VertexConsumer vertexConsumer, @Local(ordinal = 0) BakedModel originalModel, @Local ItemDisplayContext itemDisplayContext) { 45 | if (originalModel.getClass() != SimpleBakedModel.class) 46 | return model; 47 | if (model.getClass() != SimpleBakedModel.class) 48 | return model; 49 | 50 | VertexBufferWriter writer = VertexBufferWriter.tryOf(vertexConsumer); 51 | if (writer == null) return model; 52 | 53 | int faces = flerovium$decideCull(((SimpleBakedModel) originalModel).getTransforms(), itemDisplayContext, poseStack.last()); 54 | FastSimpleBakedModelRenderer.render((SimpleBakedModel) model, faces, stack, light, overlay, poseStack, writer, itemColors); 55 | 56 | return flerovium$dummy; 57 | } 58 | 59 | @Unique 60 | private int flerovium$decideCull(ItemTransforms transforms, ItemDisplayContext itemDisplayContext, PoseStack.Pose pose) { 61 | final int extraCull = 0b1000000; 62 | if (RenderSystem.modelViewMatrix.m32() != 0) { // In GUI 63 | if (itemDisplayContext != ItemDisplayContext.GUI) { 64 | return 0b111111; 65 | } 66 | if (transforms.gui == ItemTransform.NO_TRANSFORM) { // Item 67 | if (pose.pose().m20() == 0 && pose.pose().m21() == 0) { // Not per-transformed 68 | return 1 << Direction.SOUTH.ordinal(); 69 | } 70 | } else if (transforms.gui.rotation.equals(30.0F, 225.0F, 0.0F)) { // Block 71 | return (1 << Direction.UP.ordinal()) | (1 << Direction.NORTH.ordinal()) | 72 | (1 << Direction.EAST.ordinal()); 73 | } else if (transforms.gui.rotation.equals(30.0F, 135.0F, 0.0F)) { // Block 74 | return (1 << Direction.UP.ordinal()) | (1 << Direction.NORTH.ordinal()) | 75 | (1 << Direction.WEST.ordinal()); 76 | } 77 | return 0b111111; 78 | } 79 | // In World 80 | int faces = 0b111111; 81 | if (transforms.gui == ItemTransform.NO_TRANSFORM && pose.pose().m32() < -10.0F) { // Item Far away 82 | faces &= ((1 << Direction.NORTH.ordinal()) | (1 << Direction.SOUTH.ordinal())); 83 | } 84 | if (Flerovium.config.itemBackFaceCulling && pose.pose().m32() < -3.0F) { 85 | faces |= extraCull; 86 | } 87 | return faces; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/com/moepus/flerovium/mixins/Item/ItemTransformMixin.java: -------------------------------------------------------------------------------- 1 | package com.moepus.flerovium.mixins.Item; 2 | 3 | import com.mojang.blaze3d.vertex.PoseStack; 4 | import net.minecraft.client.renderer.block.model.ItemTransform; 5 | import org.joml.Vector3f; 6 | import org.joml.Math; 7 | import org.spongepowered.asm.mixin.Final; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.Shadow; 10 | import org.spongepowered.asm.mixin.Unique; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.Inject; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 14 | 15 | import static net.minecraftforge.common.util.TransformationHelper.quatFromXYZ; 16 | import com.moepus.flerovium.functions.MatrixStuff; 17 | 18 | @Mixin(ItemTransform.class) 19 | public abstract class ItemTransformMixin { 20 | @Final 21 | @Shadow 22 | public Vector3f rotation; 23 | @Final 24 | @Shadow 25 | public Vector3f translation; 26 | @Final 27 | @Shadow 28 | public Vector3f scale; 29 | @Final 30 | @Shadow(remap = false) 31 | public Vector3f rightRotation; 32 | 33 | @Unique 34 | boolean flerovium$noRot = false; 35 | @Unique 36 | boolean flerovium$noTrans = false; 37 | @Unique 38 | boolean flerovium$scaleSameAndPositive = false; 39 | @Unique 40 | boolean flerovium$noRightRot = false; 41 | @Unique 42 | float flerovium$sinX = 0f; 43 | @Unique 44 | float flerovium$cosX = 0f; 45 | @Unique 46 | float flerovium$sinY = 0f; 47 | @Unique 48 | float flerovium$cosY = 0f; 49 | 50 | @Inject(method = "(Lorg/joml/Vector3f;Lorg/joml/Vector3f;Lorg/joml/Vector3f;Lorg/joml/Vector3f;)V", at = @At("TAIL"), remap = false) 51 | public void init(Vector3f p_254427_, Vector3f p_254496_, Vector3f p_254022_, Vector3f rightRotation, CallbackInfo ci) { 52 | if (rotation.equals(0, 0, 0)) { 53 | flerovium$noRot = true; 54 | } else if (rotation.z == 0) { 55 | float radX = Math.toRadians(rotation.x); 56 | float radY = Math.toRadians(rotation.y); 57 | flerovium$sinX = Math.sin(radX); 58 | flerovium$sinY = Math.sin(radY); 59 | flerovium$cosX = Math.cosFromSin(flerovium$sinX, radX); 60 | flerovium$cosY = Math.cosFromSin(flerovium$sinY, radY); 61 | } 62 | if (translation.equals(0, 0, 0)) { 63 | flerovium$noTrans = true; 64 | } 65 | if (scale.x() == scale.y() && scale.y() == scale.z() && scale.x() > 0) { 66 | flerovium$scaleSameAndPositive = true; 67 | } else if (scale.z() == 0) { 68 | scale.z = 1E-5F; // Work Around for some MCR mods 69 | } 70 | if (rightRotation.equals(0, 0, 0)) { 71 | flerovium$noRightRot = true; 72 | } 73 | } 74 | 75 | @Inject(method = "apply(ZLcom/mojang/blaze3d/vertex/PoseStack;)V", at = @At("HEAD"), cancellable = true) 76 | public void apply(boolean doFlip, PoseStack pose, CallbackInfo ci) { 77 | if ((Object) this != ItemTransform.NO_TRANSFORM) { 78 | final float flip = doFlip ? -1 : 1; 79 | if (!flerovium$noTrans) { 80 | pose.translate(flip * translation.x(), translation.y(), translation.z()); 81 | } 82 | if (!flerovium$noRot) { 83 | if (rotation.z() == 0) { 84 | PoseStack.Pose last = pose.last(); 85 | float flipY = flip * flerovium$sinY; 86 | MatrixStuff.rotateXY(last.pose(), flerovium$sinX, flerovium$cosX, flipY, flerovium$cosY); 87 | MatrixStuff.rotateXY(last.normal(), flerovium$sinX, flerovium$cosX, flipY, flerovium$cosY); 88 | } else { 89 | pose.mulPose(quatFromXYZ(rotation.x(), rotation.y() * flip, rotation.z() * flip, true)); 90 | } 91 | } 92 | if (flerovium$scaleSameAndPositive) { 93 | pose.last().pose().scale(scale.x(), scale.x(), scale.x()); 94 | } else { 95 | pose.scale(scale.x(), scale.y(), scale.z()); 96 | } 97 | if (!flerovium$noRightRot) { 98 | pose.mulPose(quatFromXYZ(rightRotation.x(), rightRotation.y() * flip, rightRotation.z() * flip, true)); 99 | } 100 | } 101 | ci.cancel(); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/com/moepus/flerovium/mixins/Item/SimpleBakedModelMixin.java: -------------------------------------------------------------------------------- 1 | package com.moepus.flerovium.mixins.Item; 2 | 3 | import net.minecraft.client.renderer.RenderType; 4 | import net.minecraft.client.resources.model.SimpleBakedModel; 5 | import net.minecraft.world.item.ItemStack; 6 | import net.minecraftforge.client.extensions.IForgeBakedModel; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.spongepowered.asm.mixin.*; 9 | 10 | import java.util.List; 11 | 12 | @Mixin(SimpleBakedModel.class) 13 | public abstract class SimpleBakedModelMixin implements IForgeBakedModel { 14 | @Shadow(remap = false) 15 | @Final 16 | @Mutable 17 | protected List itemRenderTypes; 18 | @Shadow(remap = false) 19 | @Final 20 | @Mutable 21 | protected List fabulousItemRenderTypes; 22 | /** 23 | * @author MoePus 24 | * @reason Cache Render Types 25 | */ 26 | @Overwrite(remap = false) 27 | public @NotNull List getRenderTypes(@NotNull ItemStack itemStack, boolean fabulous) { 28 | if (!fabulous) { 29 | if (itemRenderTypes == null) { 30 | itemRenderTypes = IForgeBakedModel.super.getRenderTypes(itemStack, fabulous); 31 | } 32 | return itemRenderTypes; 33 | } 34 | if (fabulousItemRenderTypes == null) { 35 | fabulousItemRenderTypes = IForgeBakedModel.super.getRenderTypes(itemStack, fabulous); 36 | } 37 | return fabulousItemRenderTypes; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/moepus/flerovium/mixins/Particle/ParticleEngineMixin.java: -------------------------------------------------------------------------------- 1 | package com.moepus.flerovium.mixins.Particle; 2 | 3 | import com.llamalad7.mixinextras.sugar.Local; 4 | import net.minecraft.client.particle.Particle; 5 | import net.minecraft.client.particle.ParticleEngine; 6 | import net.minecraft.client.renderer.culling.Frustum; 7 | import net.minecraft.world.phys.AABB; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Redirect; 11 | 12 | @Mixin(value = ParticleEngine.class, priority = 500) 13 | public abstract class ParticleEngineMixin { 14 | @Redirect(method = "render(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;Lnet/minecraft/client/renderer/LightTexture;Lnet/minecraft/client/Camera;FLnet/minecraft/client/renderer/culling/Frustum;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/culling/Frustum;isVisible(Lnet/minecraft/world/phys/AABB;)Z")) 15 | boolean FastFrustumCheck(Frustum instance, AABB aabb, @Local Particle particle) { 16 | if (aabb.minX == Double.NEGATIVE_INFINITY) return true; 17 | 18 | float x = (float) (particle.x - instance.camX); 19 | float y = (float) (particle.y - instance.camY); 20 | float z = (float) (particle.z - instance.camZ); 21 | 22 | float width = particle.bbWidth; 23 | float height = particle.bbHeight; 24 | return instance.intersection.testSphere(x, y, z, Math.max(width, height) * 0.5F); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/moepus/flerovium/mixins/Particle/ParticleMixin.java: -------------------------------------------------------------------------------- 1 | package com.moepus.flerovium.mixins.Particle; 2 | import net.minecraft.client.particle.Particle; 3 | import net.minecraft.util.RandomSource; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.injection.At; 6 | import org.spongepowered.asm.mixin.injection.Redirect; 7 | 8 | @Mixin(Particle.class) 9 | public abstract class ParticleMixin { 10 | @Redirect(method = "(Lnet/minecraft/client/multiplayer/ClientLevel;DDD)V", at = @At(value = "INVOKE", target ="Lnet/minecraft/util/RandomSource;create()Lnet/minecraft/util/RandomSource;")) 11 | private RandomSource onInit() { 12 | return RandomSource.createNewThreadLocalInstance(); 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/java/com/moepus/flerovium/mixins/Particle/SingleQuadParticleMixin.java: -------------------------------------------------------------------------------- 1 | package com.moepus.flerovium.mixins.Particle; 2 | 3 | import com.mojang.blaze3d.vertex.VertexConsumer; 4 | import net.caffeinemc.mods.sodium.api.util.ColorABGR; 5 | import net.caffeinemc.mods.sodium.api.vertex.buffer.VertexBufferWriter; 6 | import net.caffeinemc.mods.sodium.api.vertex.format.common.ParticleVertex; 7 | import net.minecraft.client.Camera; 8 | import net.minecraft.client.multiplayer.ClientLevel; 9 | import net.minecraft.client.particle.Particle; 10 | import net.minecraft.client.particle.SingleQuadParticle; 11 | import net.minecraft.util.Mth; 12 | import net.minecraft.world.phys.Vec3; 13 | import org.joml.Math; 14 | import org.spongepowered.asm.mixin.Mixin; 15 | import org.spongepowered.asm.mixin.Shadow; 16 | import org.spongepowered.asm.mixin.Unique; 17 | import org.spongepowered.asm.mixin.injection.At; 18 | import org.spongepowered.asm.mixin.injection.Inject; 19 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 20 | import org.lwjgl.system.MemoryStack; 21 | 22 | 23 | @Mixin(value = SingleQuadParticle.class, priority = 100) 24 | public abstract class SingleQuadParticleMixin extends Particle { 25 | @Shadow 26 | public abstract float getQuadSize(float pt); 27 | 28 | @Shadow 29 | protected abstract float getU0(); 30 | 31 | @Shadow 32 | protected abstract float getU1(); 33 | 34 | @Shadow 35 | protected abstract float getV0(); 36 | 37 | @Shadow 38 | protected abstract float getV1(); 39 | 40 | protected SingleQuadParticleMixin(ClientLevel p_107234_, double p_107235_, double p_107236_, double p_107237_) { 41 | super(p_107234_, p_107235_, p_107236_, p_107237_); 42 | } 43 | 44 | @Unique 45 | int flerovium$lastTick = 0; 46 | 47 | @Unique 48 | int flerovium$cachedLight = 0; 49 | 50 | @Unique 51 | private int flerovium$getLightColorCached(float pt, Camera camera) { 52 | if (camera.getEntity() == null) 53 | return getLightColor(pt); 54 | int tickCount = camera.getEntity().tickCount; 55 | if (tickCount == 0) 56 | return getLightColor(pt); 57 | if (tickCount == flerovium$lastTick) { 58 | return flerovium$cachedLight; 59 | } 60 | flerovium$lastTick = tickCount; 61 | flerovium$cachedLight = getLightColor(pt); 62 | return flerovium$cachedLight; 63 | } 64 | 65 | @Inject(method = "render", at = @At("HEAD"), cancellable = true) 66 | public void renderFast(VertexConsumer vertexConsumer, Camera camera, float pt, CallbackInfo ci) { 67 | VertexBufferWriter writer = VertexBufferWriter.tryOf(vertexConsumer); 68 | if (writer == null) 69 | return; 70 | ci.cancel(); 71 | 72 | Vec3 camPos = camera.getPosition(); 73 | float x = (float) (Mth.lerp(pt, this.xo, this.x) - camPos.x()); 74 | float y = (float) (Mth.lerp(pt, this.yo, this.y) - camPos.y()); 75 | float z = (float) (Mth.lerp(pt, this.zo, this.z) - camPos.z()); 76 | 77 | float minU = this.getU0(); 78 | float maxU = this.getU1(); 79 | float minV = this.getV0(); 80 | float maxV = this.getV1(); 81 | int light = flerovium$getLightColorCached(pt, camera); 82 | float size = this.getQuadSize(pt); 83 | int color = ColorABGR.pack(this.rCol, this.gCol, this.bCol, this.alpha); 84 | 85 | float lx = camera.getLeftVector().x, ly = camera.getLeftVector().y, lz = camera.getLeftVector().z; 86 | float ux = camera.getUpVector().x, uy = camera.getUpVector().y, uz = camera.getUpVector().z; 87 | if (roll != 0) { 88 | // Manually rotate the left and up vectors by the roll angle 89 | float nroll = Mth.lerp(pt, this.oRoll, this.roll); 90 | float sinRoll = Math.sin(nroll); 91 | float cosRoll = Math.cosFromSin(sinRoll, nroll); 92 | 93 | float rv1x = Math.fma(cosRoll, lx, sinRoll * ux), 94 | rv1y = Math.fma(cosRoll, ly, sinRoll * uy), 95 | rv1z = Math.fma(cosRoll, lz, sinRoll * uz); 96 | float rv2x = Math.fma(-sinRoll, lx, cosRoll * ux), 97 | rv2y = Math.fma(-sinRoll, ly, cosRoll * uy), 98 | rv2z = Math.fma(-sinRoll, lz, cosRoll * uz); 99 | lx = rv1x; 100 | ly = rv1y; 101 | lz = rv1z; 102 | ux = rv2x; 103 | uy = rv2y; 104 | uz = rv2z; 105 | } 106 | 107 | /** 108 | * Constructs a sprite's four vertices using the camera's leftVector and upVector. 109 | * 110 | * +---------------------+ 111 | * | | 112 | * (-left, +up) | +up | (+left, +up) 113 | * | | 114 | * Vertex 2| ^ | Vertex 3 115 | * | | | 116 | * | <------+------> | 117 | * | left | 118 | * | | 119 | * (-left, -up) | -up | (+left, -up) 120 | * | | 121 | * Vertex 1| | Vertex 4 122 | * +---------------------+ 123 | * 124 | * The sprite lies in a plane defined by the camera's orientation. Using the 125 | * size of the sprite (S), leftVector (L), and upVector (U), we calculate: 126 | * 127 | * Vertex 1: (L + U) * -S 128 | * Vertex 2: (-L + U) * S 129 | * Vertex 3: (L + U) * S 130 | * Vertex 4: (-L + U) * -S 131 | * 132 | * Each vertex is then transformed to the world position of the sprite 133 | * by adding the sprite's position (P). 134 | * 135 | * This approach avoids expensive quaternion operations and directly 136 | * leverages the camera's orientation to efficiently calculate vertices. 137 | */ 138 | try (MemoryStack stack = MemoryStack.stackPush()) { 139 | long buffer = stack.nmalloc(4 * ParticleVertex.STRIDE); 140 | long ptr = buffer; 141 | 142 | ParticleVertex.put(ptr, Math.fma(lx + ux, -size, x), Math.fma(ly + uy, -size, y), Math.fma(lz + uz, -size, z), maxU, maxV, color, light); 143 | ptr += ParticleVertex.STRIDE; 144 | 145 | ParticleVertex.put(ptr, Math.fma(-lx + ux, size, x), Math.fma(-ly + uy, size, y), Math.fma(-lz + uz, size, z), maxU, minV, color, light); 146 | ptr += ParticleVertex.STRIDE; 147 | 148 | ParticleVertex.put(ptr, Math.fma(lx + ux, size, x), Math.fma(ly + uy, size, y), Math.fma(lz + uz, size, z), minU, minV, color, light); 149 | ptr += ParticleVertex.STRIDE; 150 | 151 | ParticleVertex.put(ptr, Math.fma(-lx + ux, -size, x), Math.fma(-ly + uy, -size, y), Math.fma(-lz + uz, -size, z), minU, maxV, color, light); 152 | 153 | writer.push(stack, buffer, 4, ParticleVertex.FORMAT); 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/accesstransformer.cfg: -------------------------------------------------------------------------------- 1 | public net.minecraft.client.renderer.culling.Frustum f_112996_ # camX 2 | public net.minecraft.client.renderer.culling.Frustum f_112997_ # camY 3 | public net.minecraft.client.renderer.culling.Frustum f_112998_ # camZ 4 | public net.minecraft.client.renderer.culling.Frustum f_252531_ # intersection 5 | public net.minecraft.client.particle.Particle f_107212_ # x 6 | public net.minecraft.client.particle.Particle f_107213_ # y 7 | public net.minecraft.client.particle.Particle f_107214_ # z 8 | public net.minecraft.client.particle.Particle f_107221_ # bbWidth 9 | public net.minecraft.client.particle.Particle f_107222_ # bbHeight 10 | public com.mojang.blaze3d.systems.RenderSystem modelViewMatrix # modelViewMatrix -------------------------------------------------------------------------------- /src/main/resources/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | # This is an example mods.toml file. It contains the data relating to the loading mods. 2 | # There are several mandatory fields (#mandatory), and many more that are optional (#optional). 3 | # The overall format is standard TOML format, v0.5.0. 4 | # Note that there are a couple of TOML lists in this file. 5 | # Find more information on toml format here: https://github.com/toml-lang/toml 6 | # The name of the mod loader type to load - for regular FML @Mod mods it should be javafml 7 | modLoader="javafml" #mandatory 8 | # A version range to match for said mod loader - for regular FML @Mod it will be the forge version 9 | loaderVersion="${loader_version_range}" #mandatory This is typically bumped every Minecraft version by Forge. See our download page for lists of versions. 10 | # The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties. 11 | # Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here. 12 | license="${mod_license}" 13 | # A URL to refer people to when problems occur with this mod 14 | #issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional 15 | # A list of mods - how many allowed here is determined by the individual mod loader 16 | [[mods]] #mandatory 17 | # The modid of the mod 18 | modId="${mod_id}" #mandatory 19 | # The version number of the mod 20 | version="${mod_version}" #mandatory 21 | # A display name for the mod 22 | displayName="${mod_name}" #mandatory 23 | # A URL to query for updates for this mod. See the JSON update specification https://docs.minecraftforge.net/en/latest/misc/updatechecker/ 24 | #updateJSONURL="https://change.me.example.invalid/updates.json" #optional 25 | # A URL for the "homepage" for this mod, displayed in the mod UI 26 | #displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional 27 | # A file name (in the root of the mod JAR) containing a logo for display 28 | #logoFile="flerovium.png" #optional 29 | # A text field displayed in the mod UI 30 | #credits="Thanks for this example mod goes to Java" #optional 31 | # A text field displayed in the mod UI 32 | authors="${mod_authors}" #optional 33 | # Display Test controls the display for your mod in the server connection screen 34 | # MATCH_VERSION means that your mod will cause a red X if the versions on client and server differ. This is the default behaviour and should be what you choose if you have server and client elements to your mod. 35 | # IGNORE_SERVER_VERSION means that your mod will not cause a red X if it's present on the server but not on the client. This is what you should use if you're a server only mod. 36 | # IGNORE_ALL_VERSION means that your mod will not cause a red X if it's present on the client or the server. This is a special case and should only be used if your mod has no server component. 37 | # NONE means that no display test is set on your mod. You need to do this yourself, see IExtensionPoint.DisplayTest for more information. You can define any scheme you wish with this value. 38 | # IMPORTANT NOTE: this is NOT an instruction as to which environments (CLIENT or DEDICATED SERVER) your mod loads on. Your mod should load (and maybe do nothing!) whereever it finds itself. 39 | #displayTest="MATCH_VERSION" # MATCH_VERSION is the default if nothing is specified (#optional) 40 | 41 | # The description text for the mod (multi line!) (#mandatory) 42 | description='''${mod_description}''' 43 | # A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional. 44 | [[dependencies.${mod_id}]] #optional 45 | # the modid of the dependency 46 | modId="forge" #mandatory 47 | # Does this dependency have to exist - if not, ordering below must be specified 48 | mandatory=true #mandatory 49 | # The version range of the dependency 50 | versionRange="${forge_version_range}" #mandatory 51 | # An ordering relationship for the dependency - BEFORE or AFTER required if the dependency is not mandatory 52 | # BEFORE - This mod is loaded BEFORE the dependency 53 | # AFTER - This mod is loaded AFTER the dependency 54 | ordering="NONE" 55 | # Side this dependency is applied on - BOTH, CLIENT, or SERVER 56 | side="CLIENT"# Here's another dependency 57 | [[dependencies.${mod_id}]] 58 | modId="minecraft" 59 | mandatory=true 60 | # This version range declares a minimum of the current minecraft version up to but not including the next major version 61 | versionRange="${minecraft_version_range}" 62 | ordering="NONE" 63 | side="CLIENT" 64 | [[dependencies.${mod_id}]] 65 | modId="embeddium" 66 | mandatory=true 67 | versionRange="[0.3.25,)" 68 | ordering="NONE" 69 | side="CLIENT" -------------------------------------------------------------------------------- /src/main/resources/flerovium.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "com.moepus.flerovium.mixins", 5 | "compatibilityLevel": "JAVA_17", 6 | "refmap": "flerovium.refmap.json", 7 | "plugin": "com.moepus.flerovium.MixinPlugin", 8 | "mixins": [ 9 | "Chunk.OcclusionCullerMixin" 10 | ], 11 | "client": [ 12 | "Chunk.FrustumMixin", 13 | "Entity.ClientLevelMixin", 14 | "Entity.ModelPartMixin", 15 | "Item.ItemEntityRenderMixin", 16 | "Item.ItemRendererMixin", 17 | "Item.ItemTransformMixin", 18 | "Item.SimpleBakedModelMixin", 19 | "Particle.ParticleEngineMixin", 20 | "Particle.ParticleMixin", 21 | "Particle.SingleQuadParticleMixin" 22 | ], 23 | "injectors": { 24 | "defaultRequire": 1 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "flerovium resources", 4 | "pack_format": 15, 5 | "forge:server_data_pack_format": 12 6 | } 7 | } 8 | --------------------------------------------------------------------------------