[WIP] 1.14.4 port
This commit is contained in:
1 parent
1ea2b6b946
commit
46cbe64328
164 files changed
+1708
-2108
No files matched your search
+67
-30
@@ -1,62 +1,99 @@
|
|||||||
apply plugin: "net.minecraftforge.gradle.forge"
|
|
||||||
apply plugin: 'kotlin'
|
|
||||||
|
|
||||||
archivesBaseName = jarName
|
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
jcenter()
|
||||||
maven {
|
maven {
|
||||||
name = "forge"
|
name = "forge"
|
||||||
url = "http://files.minecraftforge.net/maven"
|
url = "http://files.minecraftforge.net/maven"
|
||||||
}
|
}
|
||||||
|
maven {
|
||||||
|
name = 'sponge'
|
||||||
|
url = 'https://repo.spongepowered.org/maven'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath "net.minecraftforge.gradle:ForgeGradle:2.3-SNAPSHOT"
|
classpath(group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '3.+', changing: true) {
|
||||||
|
exclude group: 'trove', module: 'trove'
|
||||||
|
}
|
||||||
|
classpath 'org.spongepowered:mixingradle:0.7-SNAPSHOT'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
apply plugin: "net.minecraftforge.gradle"
|
||||||
|
apply plugin: 'org.spongepowered.mixin'
|
||||||
|
apply plugin: 'kotlin'
|
||||||
|
|
||||||
|
sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
jcenter()
|
jcenter()
|
||||||
maven {
|
maven {
|
||||||
name = "shadowfacts"
|
name = "forge"
|
||||||
url = "https://maven.shadowfacts.net/"
|
url = "http://files.minecraftforge.net/maven"
|
||||||
|
}
|
||||||
|
maven {
|
||||||
|
name = 'Curse'
|
||||||
|
url = 'https://minecraft.curseforge.com/api/maven/'
|
||||||
|
}
|
||||||
|
maven {
|
||||||
|
name = 'sponge'
|
||||||
|
url = 'https://repo.spongepowered.org/maven'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile "net.shadowfacts:Forgelin:$forgelin_version"
|
minecraft "net.minecraftforge:forge:$mc_version-$forge_version"
|
||||||
|
implementation "kottle:Kottle:$kottle_version"
|
||||||
|
implementation "org.spongepowered:mixin:0.8-SNAPSHOT"
|
||||||
|
}
|
||||||
|
|
||||||
|
mixin {
|
||||||
|
add sourceSets.main, "betterfoliage.refmap.json"
|
||||||
}
|
}
|
||||||
|
|
||||||
minecraft {
|
minecraft {
|
||||||
version = mc_version + "-" + forge_version
|
mappings channel: "$mcp_mappings_channel", version: "$mcp_mappings_version"
|
||||||
mappings = mcp_mappings
|
accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg')
|
||||||
runDir = 'run'
|
|
||||||
}
|
|
||||||
processResources {
|
|
||||||
from(sourceSets.main.resources) { exclude 'mcmod.info' }
|
|
||||||
from(sourceSets.main.resources) { include 'mcmod.info' expand 'version':version, 'mcversion':minecraft.version }
|
|
||||||
|
|
||||||
into "${buildDir}/classes/main"
|
runs {
|
||||||
|
client {
|
||||||
|
workingDirectory project.file('run')
|
||||||
|
property 'forge.logging.markers', 'CORE'
|
||||||
|
property 'forge.logging.console.level', 'debug'
|
||||||
|
mods {
|
||||||
|
betterfoliage {
|
||||||
|
source sourceSets.main
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
workingDirectory project.file('run')
|
||||||
|
property 'forge.logging.markers', 'CORE'
|
||||||
|
property 'forge.logging.console.level', 'debug'
|
||||||
|
mods {
|
||||||
|
betterfoliage {
|
||||||
|
source sourceSets.main
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def manifestCfg = {
|
compileKotlin {
|
||||||
attributes "FMLCorePlugin": "mods.betterfoliage.loader.BetterFoliageLoader"
|
kotlinOptions {
|
||||||
attributes "FMLCorePluginContainsFMLMod": "mods.betterfoliage.BetterFoliageMod"
|
freeCompilerArgs += ("-Xno-param-assertions")
|
||||||
attributes "FMLAT": "BetterFoliage_at.cfg"
|
freeCompilerArgs += ("-Xno-call-assertions")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
manifest manifestCfg
|
archiveName = "BetterFoliage-${version}-Forge-${mc_version}.jar"
|
||||||
exclude "optifine"
|
|
||||||
}
|
|
||||||
|
|
||||||
task sourcesJar(type: Jar, dependsOn: classes) {
|
manifest {
|
||||||
classifier = 'sources'
|
from "src/main/resources/META-INF/MANIFEST.MF"
|
||||||
manifest manifestCfg
|
attributes "Implementation-Version": "${version}"
|
||||||
from(sourceSets.main.kotlin)
|
}
|
||||||
from(sourceSets.main.resources) { exclude 'mcmod.info' }
|
|
||||||
from(sourceSets.main.resources) { include 'mcmod.info' expand 'version':version, 'mcversion':minecraft.version }
|
|
||||||
}
|
}
|
||||||
+9
-5
@@ -1,11 +1,15 @@
|
|||||||
|
org.gradle.jvmargs=-Xmx2G
|
||||||
|
|
||||||
group = com.github.octarine-noise
|
group = com.github.octarine-noise
|
||||||
jarName = BetterFoliage-MC1.12
|
jarName = BetterFoliage-Forge
|
||||||
|
|
||||||
version = 2.3.0
|
version = 2.3.0
|
||||||
|
|
||||||
mc_version = 1.12.2
|
mc_version = 1.14.4
|
||||||
forge_version = 14.23.5.2847
|
forge_version = 28.1.109
|
||||||
mcp_mappings = stable_39
|
mcp_mappings_channel = snapshot
|
||||||
|
mcp_mappings_version = 20190719-1.14.3
|
||||||
|
|
||||||
kotlin_version = 1.3.40
|
kotlin_version = 1.3.61
|
||||||
forgelin_version = 1.8.4
|
forgelin_version = 1.8.4
|
||||||
|
kottle_version = 1.4.0
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package mods.betterfoliage;
|
||||||
|
|
||||||
|
import net.minecraftforge.fml.ModLoader;
|
||||||
|
import org.spongepowered.asm.mixin.Mixins;
|
||||||
|
import org.spongepowered.asm.mixin.connect.IMixinConnector;
|
||||||
|
|
||||||
|
public class MixinConnector implements IMixinConnector {
|
||||||
|
@Override
|
||||||
|
public void connect() {
|
||||||
|
Mixins.addConfiguration("betterfoliage.common.mixins.json");
|
||||||
|
|
||||||
|
try {
|
||||||
|
Class.forName("optifine.OptiFineTransformationService");
|
||||||
|
Mixins.addConfiguration("betterfoliage.optifine.mixins.json");
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
Mixins.addConfiguration("betterfoliage.vanilla.mixins.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
package mods.betterfoliage.loader;
|
|
||||||
|
|
||||||
import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@IFMLLoadingPlugin.TransformerExclusions({
|
|
||||||
"mods.betterfoliage.loader",
|
|
||||||
"mods.octarinecore.metaprog",
|
|
||||||
"kotlin"
|
|
||||||
})
|
|
||||||
@IFMLLoadingPlugin.MCVersion("1.12.2")
|
|
||||||
@IFMLLoadingPlugin.SortingIndex(1400)
|
|
||||||
public class BetterFoliageLoader implements IFMLLoadingPlugin {
|
|
||||||
@Override
|
|
||||||
public String[] getASMTransformerClass() {
|
|
||||||
return new String[] { "mods.betterfoliage.loader.BetterFoliageTransformer" };
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getModContainerClass() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getSetupClass() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void injectData(Map<String, Object> data) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getAccessTransformerClass() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package mods.betterfoliage.mixin;
|
||||||
|
|
||||||
|
import mods.betterfoliage.client.Hooks;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.util.Direction;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.shapes.VoxelShape;
|
||||||
|
import net.minecraft.world.IBlockReader;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mixin overriding the {@link VoxelShape} used for the neighbor block in {@link Block}.shouldSideBeRendered().
|
||||||
|
*
|
||||||
|
* This way log blocks can be made to not block rendering, without altering any {@link Block} or
|
||||||
|
* {@link BlockState} properties with potential gameplay ramifications.
|
||||||
|
*/
|
||||||
|
@Mixin(Block.class)
|
||||||
|
public class BlockMixin {
|
||||||
|
private static final String shouldSideBeRendered = "Lnet/minecraft/block/Block;shouldSideBeRendered(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/Direction;)Z";
|
||||||
|
private static final String getVoxelShape = "Lnet/minecraft/block/BlockState;func_215702_a(Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/Direction;)Lnet/minecraft/util/math/shapes/VoxelShape;";
|
||||||
|
|
||||||
|
@Redirect(method = shouldSideBeRendered, at = @At(value = "INVOKE", target = getVoxelShape, ordinal = 1))
|
||||||
|
private static VoxelShape getVoxelShapeOverride(BlockState state, IBlockReader reader, BlockPos pos, Direction dir) {
|
||||||
|
return Hooks.getVoxelShapeOverride(state, reader, pos, dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package mods.betterfoliage.mixin;
|
||||||
|
|
||||||
|
import mods.betterfoliage.client.Hooks;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.world.IBlockReader;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mixin to override the result of {@link BlockState}.getAmbientOcclusionLightValue().
|
||||||
|
*
|
||||||
|
* Needed to avoid excessive darkening of Round Logs at the corners, now that they are not full blocks.
|
||||||
|
*/
|
||||||
|
@Mixin(BlockState.class)
|
||||||
|
public class BlockStateMixin {
|
||||||
|
private static final String callFrom = "Lnet/minecraft/block/BlockState;func_215703_d(Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;)F";
|
||||||
|
private static final String callTo = "Lnet/minecraft/block/Block;func_220080_a(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;)F";
|
||||||
|
|
||||||
|
@Redirect(method = callFrom, at = @At(value = "INVOKE", target = callTo))
|
||||||
|
float getAmbientOcclusionValue(Block block, BlockState state, IBlockReader reader, BlockPos pos) {
|
||||||
|
return Hooks.getAmbientOcclusionLightValueOverride(block.func_220080_a(state, reader, pos), state);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package mods.betterfoliage.mixin;
|
||||||
|
|
||||||
|
import mods.betterfoliage.client.Hooks;
|
||||||
|
import net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.client.renderer.BlockRendererDispatcher;
|
||||||
|
import net.minecraft.client.renderer.BufferBuilder;
|
||||||
|
import net.minecraft.client.renderer.chunk.ChunkRender;
|
||||||
|
import net.minecraft.util.BlockRenderLayer;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.world.IEnviromentBlockReader;
|
||||||
|
import net.minecraftforge.client.MinecraftForgeClient;
|
||||||
|
import net.minecraftforge.client.model.data.IModelData;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
@Mixin(ChunkRender.class)
|
||||||
|
public class ChunkRenderMixin {
|
||||||
|
|
||||||
|
private static final String rebuildChunk = "Lnet/minecraft/client/renderer/chunk/ChunkRender;rebuildChunk(FFFLnet/minecraft/client/renderer/chunk/ChunkRenderTask;)V";
|
||||||
|
private static final String renderBlock = "Lnet/minecraft/client/renderer/BlockRendererDispatcher;renderBlock(Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/IEnviromentBlockReader;Lnet/minecraft/client/renderer/BufferBuilder;Ljava/util/Random;Lnet/minecraftforge/client/model/data/IModelData;)Z";
|
||||||
|
|
||||||
|
@Redirect(method = rebuildChunk, at = @At(value = "INVOKE", target = renderBlock))
|
||||||
|
public boolean renderBlock(BlockRendererDispatcher dispatcher, BlockState state, BlockPos pos, IEnviromentBlockReader reader, BufferBuilder buffer, Random random, IModelData modelData) {
|
||||||
|
return Hooks.renderWorldBlock(dispatcher, state, pos, reader, buffer, random, modelData, MinecraftForgeClient.getRenderLayer());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package mods.betterfoliage.mixin;
|
||||||
|
|
||||||
|
import mods.betterfoliage.client.Hooks;
|
||||||
|
import net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.client.renderer.chunk.ChunkRender;
|
||||||
|
import net.minecraft.util.BlockRenderLayer;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Coerce;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Slice;
|
||||||
|
|
||||||
|
@Mixin(ChunkRender.class)
|
||||||
|
public class ChunkRenderOptifineMixin {
|
||||||
|
|
||||||
|
private static final String rebuildChunk = "Lnet/minecraft/client/renderer/chunk/ChunkRender;rebuildChunk(FFFLnet/minecraft/client/renderer/chunk/ChunkRenderTask;)V";
|
||||||
|
private static final String invokeReflector = "Lnet/optifine/reflect/Reflector;callBoolean(Ljava/lang/Object;Lnet/optifine/reflect/ReflectorMethod;[Ljava/lang/Object;)Z";
|
||||||
|
private static final String forgeBlockCanRender = "Lnet/minecraft/client/renderer/chunk/ChunkRender;FORGE_BLOCK_CAN_RENDER_IN_LAYER:Z";
|
||||||
|
|
||||||
|
@Redirect(
|
||||||
|
method = rebuildChunk,
|
||||||
|
at = @At(value = "INVOKE", target = invokeReflector),
|
||||||
|
slice = @Slice(
|
||||||
|
from = @At(value = "FIELD", target = forgeBlockCanRender)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@SuppressWarnings("UnresolvedMixinReference")
|
||||||
|
boolean canRenderInLayer(Object state, @Coerce Object reflector, Object[] layer) {
|
||||||
|
return Hooks.canRenderInLayerOverride((BlockState) state, (BlockRenderLayer) layer[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package mods.betterfoliage.mixin;
|
||||||
|
|
||||||
|
import mods.betterfoliage.client.Hooks;
|
||||||
|
import net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.client.renderer.BlockRendererDispatcher;
|
||||||
|
import net.minecraft.client.renderer.BufferBuilder;
|
||||||
|
import net.minecraft.client.renderer.chunk.ChunkRender;
|
||||||
|
import net.minecraft.util.BlockRenderLayer;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.world.IEnviromentBlockReader;
|
||||||
|
import net.minecraftforge.client.MinecraftForgeClient;
|
||||||
|
import net.minecraftforge.client.model.data.IModelData;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
@Mixin(ChunkRender.class)
|
||||||
|
public class ChunkRenderVanillaMixin {
|
||||||
|
|
||||||
|
private static final String rebuildChunk = "Lnet/minecraft/client/renderer/chunk/ChunkRender;rebuildChunk(FFFLnet/minecraft/client/renderer/chunk/ChunkRenderTask;)V";
|
||||||
|
private static final String canRenderInLayer = "Lnet/minecraft/block/BlockState;canRenderInLayer(Lnet/minecraft/util/BlockRenderLayer;)Z";
|
||||||
|
|
||||||
|
@Redirect(method = rebuildChunk, at = @At(value = "INVOKE", target = canRenderInLayer))
|
||||||
|
boolean canRenderInLayer(BlockState state, BlockRenderLayer layer) {
|
||||||
|
return Hooks.canRenderInLayerOverride(state, layer);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package mods.betterfoliage.mixin;
|
||||||
|
|
||||||
|
import mods.betterfoliage.client.Hooks;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.client.renderer.WorldRenderer;
|
||||||
|
import net.minecraft.client.world.ClientWorld;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.world.IBlockReader;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
@Mixin(ClientWorld.class)
|
||||||
|
public class ClientWorldMixin {
|
||||||
|
|
||||||
|
private static final String worldAnimateTick = "Lnet/minecraft/client/world/ClientWorld;animateTick(IIIILjava/util/Random;ZLnet/minecraft/util/math/BlockPos$MutableBlockPos;)V";
|
||||||
|
private static final String blockAnimateTick = "Lnet/minecraft/block/Block;animateTick(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Ljava/util/Random;)V";
|
||||||
|
|
||||||
|
private static final String worldNotify = "Lnet/minecraft/client/world/ClientWorld;notifyBlockUpdate(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Lnet/minecraft/block/BlockState;I)V";
|
||||||
|
private static final String rendererNotify = "Lnet/minecraft/client/renderer/WorldRenderer;notifyBlockUpdate(Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Lnet/minecraft/block/BlockState;I)V";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inject a callback to call for every random display tick. Used for adding custom particle effects to blocks.
|
||||||
|
*/
|
||||||
|
@Redirect(method = worldAnimateTick, at = @At(value = "INVOKE", target = blockAnimateTick))
|
||||||
|
void onAnimateTick(Block block, BlockState state, World world, BlockPos pos, Random random) {
|
||||||
|
Hooks.onRandomDisplayTick(block, state, world, pos, random);
|
||||||
|
block.animateTick(state, world, pos, random);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inject callback to get notified of client-side blockstate changes.
|
||||||
|
* Used to invalidate caches in the {@link mods.betterfoliage.client.chunk.ChunkOverlayManager}
|
||||||
|
*/
|
||||||
|
@Redirect(method = worldNotify, at = @At(value = "INVOKE", target = rendererNotify))
|
||||||
|
void onClientBlockChanged(WorldRenderer renderer, IBlockReader world, BlockPos pos, BlockState oldState, BlockState newState, int flags) {
|
||||||
|
Hooks.onClientBlockChanged((ClientWorld) world, pos, oldState, newState, flags);
|
||||||
|
renderer.notifyBlockUpdate(world, pos, oldState, newState, flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package mods.betterfoliage.mixin;
|
||||||
|
|
||||||
|
import mods.betterfoliage.client.Hooks;
|
||||||
|
import net.minecraft.client.renderer.model.ModelBakery;
|
||||||
|
import net.minecraft.profiler.IProfiler;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
|
@Mixin(ModelBakery.class)
|
||||||
|
public class ModelBakeryMixin {
|
||||||
|
|
||||||
|
private static final String processLoading = "Lnet/minecraft/client/renderer/model/ModelBakery;processLoading(Lnet/minecraft/profiler/IProfiler;)V";
|
||||||
|
private static final String endStartSection = "Lnet/minecraft/profiler/IProfiler;endStartSection(Ljava/lang/String;)V";
|
||||||
|
|
||||||
|
@Inject(method = processLoading, at = @At(value = "INVOKE_STRING", target = endStartSection, args = "ldc=stitching"))
|
||||||
|
void preStitchTextures(IProfiler profiler, CallbackInfo ci) {
|
||||||
|
Hooks.onLoadModelDefinitions(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
package optifine;
|
|
||||||
|
|
||||||
import net.minecraft.launchwrapper.IClassTransformer;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.net.URLClassLoader;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
import java.util.zip.ZipEntry;
|
|
||||||
import java.util.zip.ZipFile;
|
|
||||||
|
|
||||||
public class OptifineTransformerDevWrapper implements IClassTransformer {
|
|
||||||
|
|
||||||
public static String OPTIFINE_CLASSNAME = "optifine/OptiFineClassTransformer.class";
|
|
||||||
private ZipFile ofZip = null;
|
|
||||||
|
|
||||||
public OptifineTransformerDevWrapper() {
|
|
||||||
Stream<URL> loaderSources = Arrays.stream(((URLClassLoader) getClass().getClassLoader()).getURLs());
|
|
||||||
Optional<URL> optifineURL = loaderSources.filter(this::isOptifineJar).findFirst();
|
|
||||||
optifineURL.ifPresent(url -> ofZip = getZip(url));
|
|
||||||
}
|
|
||||||
|
|
||||||
private ZipFile getZip(URL url) {
|
|
||||||
try {
|
|
||||||
return new ZipFile(new File(url.toURI()));
|
|
||||||
} catch (Exception e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isOptifineJar(URL url) {
|
|
||||||
ZipFile zip = getZip(url);
|
|
||||||
return zip != null && zip.getEntry(OPTIFINE_CLASSNAME) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] transform(String name, String transformedName, byte[] basicClass) {
|
|
||||||
if (ofZip == null) return basicClass;
|
|
||||||
ZipEntry replacement = ofZip.getEntry(name.replace(".", "/") + ".class");
|
|
||||||
if (replacement == null) return basicClass;
|
|
||||||
|
|
||||||
try {
|
|
||||||
return readAll(ofZip.getInputStream(replacement));
|
|
||||||
} catch (IOException e) {
|
|
||||||
return basicClass;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] readAll(InputStream is) throws IOException {
|
|
||||||
byte[] buf = new byte[4096];
|
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
||||||
int len;
|
|
||||||
do {
|
|
||||||
len = is.read(buf, 0, 4096);
|
|
||||||
if (len > 0) bos.write(buf, 0, len);
|
|
||||||
} while (len > -1);
|
|
||||||
is.close();
|
|
||||||
return bos.toByteArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package optifine;
|
|
||||||
|
|
||||||
import net.minecraft.launchwrapper.ITweaker;
|
|
||||||
import net.minecraft.launchwrapper.LaunchClassLoader;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class OptifineTweakerDevWrapper implements ITweaker {
|
|
||||||
@Override
|
|
||||||
public void acceptOptions(List<String> args, File gameDir, File assetsDir, String profile) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void injectIntoClassLoader(LaunchClassLoader classLoader) {
|
|
||||||
classLoader.registerTransformer("optifine.OptifineTransformerDevWrapper");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getLaunchTarget() {
|
|
||||||
return "net.minecraft.client.main.Main";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] getLaunchArguments() {
|
|
||||||
return new String[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package mods.betterfoliage
|
||||||
|
|
||||||
|
import mods.betterfoliage.client.Client
|
||||||
|
import mods.betterfoliage.client.config.Config
|
||||||
|
import mods.betterfoliage.client.isAfterPostInit
|
||||||
|
import mods.octarinecore.client.resource.GeneratorPack
|
||||||
|
import net.alexwells.kottle.FMLKotlinModLoadingContext
|
||||||
|
import net.minecraft.client.Minecraft
|
||||||
|
import net.minecraftforge.eventbus.api.IEventBus
|
||||||
|
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||||
|
import net.minecraftforge.fml.ModLoadingContext
|
||||||
|
import net.minecraftforge.fml.common.Mod
|
||||||
|
import net.minecraftforge.fml.config.ModConfig
|
||||||
|
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent
|
||||||
|
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent
|
||||||
|
import org.apache.logging.log4j.Level.DEBUG
|
||||||
|
import org.apache.logging.log4j.LogManager
|
||||||
|
import org.apache.logging.log4j.simple.SimpleLogger
|
||||||
|
import org.apache.logging.log4j.util.PropertiesUtil
|
||||||
|
import java.io.File
|
||||||
|
import java.io.PrintStream
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
@Mod(BetterFoliage.MOD_ID)
|
||||||
|
object BetterFoliage {
|
||||||
|
const val MOD_ID = "betterfoliage"
|
||||||
|
const val MOD_NAME = "Better Foliage"
|
||||||
|
|
||||||
|
val modBus = FMLKotlinModLoadingContext.get().modEventBus
|
||||||
|
|
||||||
|
var log = LogManager.getLogger("BetterFoliage")
|
||||||
|
var logDetail = SimpleLogger(
|
||||||
|
"BetterFoliage",
|
||||||
|
DEBUG,
|
||||||
|
false, false, true, false,
|
||||||
|
"yyyy-MM-dd HH:mm:ss",
|
||||||
|
null,
|
||||||
|
PropertiesUtil(Properties()),
|
||||||
|
PrintStream(File("logs/betterfoliage.log").apply {
|
||||||
|
parentFile.mkdirs()
|
||||||
|
if (!exists()) createNewFile()
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
val genPack = GeneratorPack(
|
||||||
|
"bf_gen",
|
||||||
|
"Better Foliage generated assets",
|
||||||
|
"bf_generated_pack.png"
|
||||||
|
)
|
||||||
|
|
||||||
|
init {
|
||||||
|
log.log(DEBUG, "Constructing mod")
|
||||||
|
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, Config.build())
|
||||||
|
Minecraft.getInstance().resourcePackList.addPackFinder(genPack.packFinder)
|
||||||
|
Client.init()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
package mods.betterfoliage
|
|
||||||
|
|
||||||
import mods.betterfoliage.client.Client
|
|
||||||
import mods.betterfoliage.client.config.Config
|
|
||||||
import mods.betterfoliage.client.isAfterPostInit
|
|
||||||
import net.minecraftforge.common.config.Configuration
|
|
||||||
import net.minecraftforge.fml.common.FMLCommonHandler
|
|
||||||
import net.minecraftforge.fml.common.Mod
|
|
||||||
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent
|
|
||||||
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent
|
|
||||||
import net.minecraftforge.fml.common.network.NetworkCheckHandler
|
|
||||||
import net.minecraftforge.fml.relauncher.Side
|
|
||||||
import org.apache.logging.log4j.Level.DEBUG
|
|
||||||
import org.apache.logging.log4j.Level.INFO
|
|
||||||
import org.apache.logging.log4j.Logger
|
|
||||||
import org.apache.logging.log4j.simple.SimpleLogger
|
|
||||||
import org.apache.logging.log4j.util.PropertiesUtil
|
|
||||||
import java.io.File
|
|
||||||
import java.io.PrintStream
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
@Mod(
|
|
||||||
modid = BetterFoliageMod.MOD_ID,
|
|
||||||
name = BetterFoliageMod.MOD_NAME,
|
|
||||||
acceptedMinecraftVersions = BetterFoliageMod.MC_VERSIONS,
|
|
||||||
guiFactory = BetterFoliageMod.GUI_FACTORY,
|
|
||||||
dependencies = "after:forgelin",
|
|
||||||
modLanguageAdapter = "net.shadowfacts.forgelin.KotlinAdapter",
|
|
||||||
clientSideOnly = true
|
|
||||||
)
|
|
||||||
object BetterFoliageMod {
|
|
||||||
|
|
||||||
const val MOD_ID = "betterfoliage"
|
|
||||||
const val MOD_NAME = "Better Foliage"
|
|
||||||
const val DOMAIN = "betterfoliage"
|
|
||||||
const val LEGACY_DOMAIN = "bettergrassandleaves"
|
|
||||||
const val MC_VERSIONS = "[1.12]"
|
|
||||||
const val GUI_FACTORY = "mods.betterfoliage.client.gui.ConfigGuiFactory"
|
|
||||||
|
|
||||||
lateinit var log: Logger
|
|
||||||
lateinit var logDetail: Logger
|
|
||||||
|
|
||||||
var config: Configuration? = null
|
|
||||||
|
|
||||||
init {
|
|
||||||
// inject pack into default list at construction time to get domains enumerated
|
|
||||||
// there's no 2nd resource reload pass anymore
|
|
||||||
Client.generatorPack.inject()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Mod.EventHandler
|
|
||||||
fun preInit(event: FMLPreInitializationEvent) {
|
|
||||||
log = event.modLog
|
|
||||||
val logDetailFile = File(event.modConfigurationDirectory.parentFile, "logs/betterfoliage.log").apply {
|
|
||||||
parentFile.mkdirs()
|
|
||||||
if (!exists()) createNewFile()
|
|
||||||
}
|
|
||||||
logDetail = SimpleLogger(
|
|
||||||
"BetterFoliage",
|
|
||||||
DEBUG,
|
|
||||||
false, false, true, false,
|
|
||||||
"yyyy-MM-dd HH:mm:ss",
|
|
||||||
null,
|
|
||||||
PropertiesUtil(Properties()),
|
|
||||||
PrintStream(logDetailFile)
|
|
||||||
)
|
|
||||||
config = Configuration(event.suggestedConfigurationFile, null, true)
|
|
||||||
|
|
||||||
Config.attach(config!!)
|
|
||||||
Client.init()
|
|
||||||
Client.log(INFO, "BetterFoliage initialized")
|
|
||||||
isAfterPostInit = true
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Mod is cosmetic only, always allow connection. */
|
|
||||||
@NetworkCheckHandler
|
|
||||||
fun checkVersion(mods: Map<String, String>, side: Side) = true
|
|
||||||
}
|
|
||||||
@@ -1,53 +1,48 @@
|
|||||||
package mods.betterfoliage.client
|
package mods.betterfoliage.client
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
import mods.betterfoliage.BetterFoliage
|
||||||
import mods.betterfoliage.client.chunk.ChunkOverlayManager
|
import mods.betterfoliage.client.chunk.ChunkOverlayManager
|
||||||
import mods.betterfoliage.client.gui.ConfigGuiFactory
|
import mods.betterfoliage.client.config.BlockConfig
|
||||||
import mods.betterfoliage.client.integration.*
|
import mods.betterfoliage.client.integration.ForestryIntegration
|
||||||
|
import mods.betterfoliage.client.integration.IC2RubberIntegration
|
||||||
|
import mods.betterfoliage.client.integration.OptifineCustomColors
|
||||||
|
import mods.betterfoliage.client.integration.TechRebornRubberIntegration
|
||||||
import mods.betterfoliage.client.render.*
|
import mods.betterfoliage.client.render.*
|
||||||
import mods.betterfoliage.client.texture.*
|
import mods.betterfoliage.client.texture.*
|
||||||
import mods.octarinecore.client.KeyHandler
|
|
||||||
import mods.octarinecore.client.gui.textComponent
|
import mods.octarinecore.client.gui.textComponent
|
||||||
import mods.octarinecore.client.render.AbstractBlockRenderingHandler
|
import mods.octarinecore.client.render.AbstractBlockRenderingHandler
|
||||||
import mods.octarinecore.client.resource.CenteringTextureGenerator
|
import mods.octarinecore.client.resource.CenteringTextureGenerator
|
||||||
import mods.octarinecore.client.resource.GeneratorPack
|
import mods.octarinecore.client.resource.GeneratorPack
|
||||||
import net.minecraft.block.Block
|
import mods.octarinecore.client.resource.IConfigChangeListener
|
||||||
import net.minecraft.block.state.IBlockState
|
import net.minecraft.block.BlockState
|
||||||
import net.minecraft.client.Minecraft
|
import net.minecraft.client.Minecraft
|
||||||
import net.minecraft.util.math.BlockPos
|
import net.minecraft.util.math.BlockPos
|
||||||
import net.minecraft.util.text.TextComponentTranslation
|
|
||||||
import net.minecraft.util.text.TextFormatting
|
import net.minecraft.util.text.TextFormatting
|
||||||
import net.minecraftforge.fml.client.FMLClientHandler
|
import net.minecraft.util.text.TranslationTextComponent
|
||||||
import net.minecraftforge.fml.relauncher.Side
|
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
import net.minecraftforge.fml.config.ModConfig
|
||||||
|
import net.minecraftforge.registries.ForgeRegistries
|
||||||
import org.apache.logging.log4j.Level
|
import org.apache.logging.log4j.Level
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Object responsible for initializing (and holding a reference to) all the infrastructure of the mod
|
* Object responsible for initializing (and holding a reference to) all the infrastructure of the mod
|
||||||
* except for the call hooks.
|
* except for the call hooks.
|
||||||
*
|
|
||||||
* This and all other singletons are annotated [SideOnly] to avoid someone accidentally partially
|
|
||||||
* initializing the mod on a server environment.
|
|
||||||
*/
|
*/
|
||||||
@SideOnly(Side.CLIENT)
|
|
||||||
object Client {
|
object Client {
|
||||||
|
var renderers= emptyList<AbstractBlockRenderingHandler>()
|
||||||
|
var configListeners = emptyList<IConfigChangeListener>()
|
||||||
|
|
||||||
lateinit var renderers: List<AbstractBlockRenderingHandler>
|
val suppressRenderErrors = mutableSetOf<BlockState>()
|
||||||
val suppressRenderErrors = mutableSetOf<IBlockState>()
|
|
||||||
|
|
||||||
// texture generation stuff
|
// texture generators
|
||||||
val genGrass = GrassGenerator("bf_gen_grass")
|
val genGrass = GrassGenerator("bf_gen_grass")
|
||||||
val genLeaves = LeafGenerator("bf_gen_leaves")
|
val genLeaves = LeafGenerator("bf_gen_leaves")
|
||||||
val genReeds = CenteringTextureGenerator("bf_gen_reeds", 1, 2)
|
val genReeds = CenteringTextureGenerator("bf_gen_reeds", 1, 2)
|
||||||
|
|
||||||
val generatorPack = GeneratorPack(
|
|
||||||
"Better Foliage generated",
|
|
||||||
genGrass,
|
|
||||||
genLeaves,
|
|
||||||
genReeds
|
|
||||||
)
|
|
||||||
|
|
||||||
fun init() {
|
fun init() {
|
||||||
|
// add resource generators to pack
|
||||||
|
listOf(genGrass, genLeaves, genReeds).forEach { BetterFoliage.genPack.generators.add(it) }
|
||||||
|
|
||||||
// init renderers
|
// init renderers
|
||||||
renderers = listOf(
|
renderers = listOf(
|
||||||
RenderGrass(),
|
RenderGrass(),
|
||||||
@@ -66,6 +61,7 @@ object Client {
|
|||||||
|
|
||||||
// init other singletons
|
// init other singletons
|
||||||
val singletons = listOf(
|
val singletons = listOf(
|
||||||
|
BlockConfig,
|
||||||
StandardCactusRegistry,
|
StandardCactusRegistry,
|
||||||
LeafParticleRegistry,
|
LeafParticleRegistry,
|
||||||
ChunkOverlayManager,
|
ChunkOverlayManager,
|
||||||
@@ -75,7 +71,7 @@ object Client {
|
|||||||
|
|
||||||
// init mod integrations
|
// init mod integrations
|
||||||
val integrations = listOf(
|
val integrations = listOf(
|
||||||
ShadersModIntegration,
|
// ShadersModIntegration,
|
||||||
OptifineCustomColors,
|
OptifineCustomColors,
|
||||||
ForestryIntegration,
|
ForestryIntegration,
|
||||||
IC2RubberIntegration,
|
IC2RubberIntegration,
|
||||||
@@ -87,30 +83,26 @@ object Client {
|
|||||||
LeafRegistry.addRegistry(StandardLeafRegistry)
|
LeafRegistry.addRegistry(StandardLeafRegistry)
|
||||||
LogRegistry.addRegistry(StandardLogRegistry)
|
LogRegistry.addRegistry(StandardLogRegistry)
|
||||||
|
|
||||||
// init config hotkey
|
configListeners = listOf(renderers, singletons, integrations).flatten().filterIsInstance<IConfigChangeListener>()
|
||||||
val configKey = KeyHandler(BetterFoliageMod.MOD_NAME, 66, "key.betterfoliage.gui") {
|
configListeners.forEach { it.onConfigChange() }
|
||||||
FMLClientHandler.instance().showGuiScreen(
|
|
||||||
ConfigGuiFactory.createBFConfigGui(Minecraft.getMinecraft().currentScreen)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun log(level: Level, msg: String) {
|
fun log(level: Level, msg: String) {
|
||||||
BetterFoliageMod.log.log(level, "[BetterFoliage] $msg")
|
BetterFoliage.log.log(level, "[BetterFoliage] $msg")
|
||||||
BetterFoliageMod.logDetail.log(level, msg)
|
BetterFoliage.logDetail.log(level, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun logDetail(msg: String) {
|
fun logDetail(msg: String) {
|
||||||
BetterFoliageMod.logDetail.log(Level.DEBUG, msg)
|
BetterFoliage.logDetail.log(Level.DEBUG, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun logRenderError(state: IBlockState, location: BlockPos) {
|
fun logRenderError(state: BlockState, location: BlockPos) {
|
||||||
if (state in suppressRenderErrors) return
|
if (state in suppressRenderErrors) return
|
||||||
suppressRenderErrors.add(state)
|
suppressRenderErrors.add(state)
|
||||||
|
|
||||||
val blockName = Block.REGISTRY.getNameForObject(state.block).toString()
|
val blockName = ForgeRegistries.BLOCKS.getKey(state.block).toString()
|
||||||
val blockLoc = "${location.x},${location.y},${location.z}"
|
val blockLoc = "${location.x},${location.y},${location.z}"
|
||||||
Minecraft.getMinecraft().ingameGUI.chatGUI.printChatMessage(TextComponentTranslation(
|
Minecraft.getInstance().ingameGUI.chatGUI.printChatMessage(TranslationTextComponent(
|
||||||
"betterfoliage.rendererror",
|
"betterfoliage.rendererror",
|
||||||
textComponent(blockName, TextFormatting.GOLD),
|
textComponent(blockName, TextFormatting.GOLD),
|
||||||
textComponent(blockLoc, TextFormatting.GOLD)
|
textComponent(blockLoc, TextFormatting.GOLD)
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
@file:JvmName("Hooks")
|
@file:JvmName("Hooks")
|
||||||
@file:SideOnly(Side.CLIENT)
|
|
||||||
package mods.betterfoliage.client
|
package mods.betterfoliage.client
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage
|
||||||
|
import mods.betterfoliage.client.chunk.ChunkOverlayManager
|
||||||
|
import mods.betterfoliage.client.config.BlockConfig
|
||||||
import mods.betterfoliage.client.config.Config
|
import mods.betterfoliage.client.config.Config
|
||||||
import mods.betterfoliage.client.render.*
|
import mods.betterfoliage.client.render.*
|
||||||
import mods.betterfoliage.loader.Refs
|
import mods.betterfoliage.loader.Refs
|
||||||
@@ -10,46 +12,43 @@ import mods.octarinecore.client.resource.LoadModelDataEvent
|
|||||||
import mods.octarinecore.common.plus
|
import mods.octarinecore.common.plus
|
||||||
import mods.octarinecore.metaprog.allAvailable
|
import mods.octarinecore.metaprog.allAvailable
|
||||||
import net.minecraft.block.Block
|
import net.minecraft.block.Block
|
||||||
import net.minecraft.block.state.IBlockState
|
import net.minecraft.block.BlockState
|
||||||
|
import net.minecraft.block.Blocks
|
||||||
import net.minecraft.client.Minecraft
|
import net.minecraft.client.Minecraft
|
||||||
import net.minecraft.client.renderer.BlockRendererDispatcher
|
import net.minecraft.client.renderer.BlockRendererDispatcher
|
||||||
import net.minecraft.client.renderer.BufferBuilder
|
import net.minecraft.client.renderer.BufferBuilder
|
||||||
import net.minecraft.init.Blocks
|
import net.minecraft.client.renderer.model.ModelBakery
|
||||||
|
import net.minecraft.client.world.ClientWorld
|
||||||
import net.minecraft.util.BlockRenderLayer
|
import net.minecraft.util.BlockRenderLayer
|
||||||
import net.minecraft.util.BlockRenderLayer.CUTOUT
|
import net.minecraft.util.BlockRenderLayer.CUTOUT
|
||||||
import net.minecraft.util.BlockRenderLayer.CUTOUT_MIPPED
|
import net.minecraft.util.BlockRenderLayer.CUTOUT_MIPPED
|
||||||
import net.minecraft.util.EnumFacing
|
import net.minecraft.util.Direction
|
||||||
import net.minecraft.util.math.BlockPos
|
import net.minecraft.util.math.BlockPos
|
||||||
import net.minecraft.world.IBlockAccess
|
import net.minecraft.util.math.shapes.VoxelShape
|
||||||
|
import net.minecraft.util.math.shapes.VoxelShapes
|
||||||
|
import net.minecraft.world.IBlockReader
|
||||||
|
import net.minecraft.world.IEnviromentBlockReader
|
||||||
import net.minecraft.world.World
|
import net.minecraft.world.World
|
||||||
import net.minecraftforge.client.model.ModelLoader
|
import net.minecraftforge.client.model.data.IModelData
|
||||||
import net.minecraftforge.common.MinecraftForge
|
import java.util.*
|
||||||
import net.minecraftforge.fml.relauncher.Side
|
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
|
||||||
|
|
||||||
var isAfterPostInit = false
|
var isAfterPostInit = false
|
||||||
val isOptifinePresent = allAvailable(Refs.OptifineClassTransformer)
|
val isOptifinePresent = allAvailable(Refs.OptifineClassTransformer)
|
||||||
|
|
||||||
fun doesSideBlockRenderingOverride(original: Boolean, blockAccess: IBlockAccess, pos: BlockPos, side: EnumFacing): Boolean {
|
fun getAmbientOcclusionLightValueOverride(original: Float, state: BlockState): Float {
|
||||||
return original && !(Config.enabled && Config.roundLogs.enabled && Config.blocks.logClasses.matchesClass(blockAccess.getBlockState(pos).block));
|
if (Config.enabled && Config.roundLogs.enabled && BlockConfig.logBlocks.matchesClass(state.block)) return Config.roundLogs.dimming.toFloat();
|
||||||
|
return original
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isOpaqueCubeOverride(original: Boolean, state: IBlockState): Boolean {
|
fun getUseNeighborBrightnessOverride(original: Boolean, state: BlockState): Boolean {
|
||||||
// caution: blocks are initialized and the method called during startup
|
return original || (Config.enabled && Config.roundLogs.enabled && BlockConfig.logBlocks.matchesClass(state.block));
|
||||||
if (!isAfterPostInit) return original
|
|
||||||
return original && !(Config.enabled && Config.roundLogs.enabled && Config.blocks.logClasses.matchesClass(state.block))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAmbientOcclusionLightValueOverride(original: Float, state: IBlockState): Float {
|
fun onClientBlockChanged(worldClient: ClientWorld, pos: BlockPos, oldState: BlockState, newState: BlockState, flags: Int) {
|
||||||
if (Config.enabled && Config.roundLogs.enabled && Config.blocks.logClasses.matchesClass(state.block)) return Config.roundLogs.dimming;
|
ChunkOverlayManager.onBlockChange(worldClient, pos)
|
||||||
return original;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getUseNeighborBrightnessOverride(original: Boolean, state: IBlockState): Boolean {
|
fun onRandomDisplayTick(block: Block, state: BlockState, world: World, pos: BlockPos, random: Random) {
|
||||||
return original || (Config.enabled && Config.roundLogs.enabled && Config.blocks.logClasses.matchesClass(state.block));
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onRandomDisplayTick(world: World, state: IBlockState, pos: BlockPos) {
|
|
||||||
if (Config.enabled &&
|
if (Config.enabled &&
|
||||||
Config.risingSoul.enabled &&
|
Config.risingSoul.enabled &&
|
||||||
state.block == Blocks.SOUL_SAND &&
|
state.block == Blocks.SOUL_SAND &&
|
||||||
@@ -60,42 +59,52 @@ fun onRandomDisplayTick(world: World, state: IBlockState, pos: BlockPos) {
|
|||||||
|
|
||||||
if (Config.enabled &&
|
if (Config.enabled &&
|
||||||
Config.fallingLeaves.enabled &&
|
Config.fallingLeaves.enabled &&
|
||||||
Config.blocks.leavesClasses.matchesClass(state.block) &&
|
BlockConfig.leafBlocks.matchesClass(state.block) &&
|
||||||
world.isAirBlock(pos + down1) &&
|
world.isAirBlock(pos + down1) &&
|
||||||
Math.random() < Config.fallingLeaves.chance) {
|
Math.random() < Config.fallingLeaves.chance) {
|
||||||
EntityFallingLeavesFX(world, pos).addIfValid()
|
EntityFallingLeavesFX(world, pos).addIfValid()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onAfterLoadModelDefinitions(loader: ModelLoader) {
|
fun onLoadModelDefinitions(bakery: Any) {
|
||||||
MinecraftForge.EVENT_BUS.post(LoadModelDataEvent(loader))
|
BetterFoliage.modBus.post(LoadModelDataEvent(bakery as ModelBakery))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getVoxelShapeOverride(state: BlockState, reader: IBlockReader, pos: BlockPos, dir: Direction): VoxelShape {
|
||||||
|
if (LogRegistry[state, reader, pos] != null) return VoxelShapes.empty()
|
||||||
|
return state.func_215702_a(reader, pos, dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun renderWorldBlock(dispatcher: BlockRendererDispatcher,
|
fun renderWorldBlock(dispatcher: BlockRendererDispatcher,
|
||||||
state: IBlockState,
|
state: BlockState,
|
||||||
pos: BlockPos,
|
pos: BlockPos,
|
||||||
blockAccess: IBlockAccess,
|
reader: IEnviromentBlockReader,
|
||||||
worldRenderer: BufferBuilder,
|
buffer: BufferBuilder,
|
||||||
|
random: Random,
|
||||||
|
modelData: IModelData,
|
||||||
layer: BlockRenderLayer
|
layer: BlockRenderLayer
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val doBaseRender = state.canRenderInLayer(layer) || (layer == targetCutoutLayer && state.canRenderInLayer(otherCutoutLayer))
|
val doBaseRender = state.canRenderInLayer(layer) || (layer == targetCutoutLayer && state.canRenderInLayer(otherCutoutLayer))
|
||||||
blockContext.let { ctx ->
|
blockContext.let { ctx ->
|
||||||
ctx.set(blockAccess, pos)
|
ctx.set(reader, pos)
|
||||||
Client.renderers.forEach { renderer ->
|
Client.renderers.forEach { renderer ->
|
||||||
if (renderer.isEligible(ctx)) {
|
if (renderer.isEligible(ctx)) {
|
||||||
// render on the block's default layer
|
// render on the block's default layer
|
||||||
// also render on the cutout layer if the renderer requires it
|
// also render on the cutout layer if the renderer requires it
|
||||||
if (doBaseRender || (renderer.addToCutout && layer == targetCutoutLayer)) {
|
if (doBaseRender || (renderer.addToCutout && layer == targetCutoutLayer)) {
|
||||||
return renderer.render(ctx, dispatcher, worldRenderer, layer)
|
return renderer.render(ctx, dispatcher, buffer, random, modelData, layer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return if (doBaseRender) dispatcher.renderBlock(state, pos, blockAccess, worldRenderer) else false
|
return if (doBaseRender) dispatcher.renderBlock(state, pos, reader, buffer, random, modelData) else false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun canRenderBlockInLayer(block: Block, state: IBlockState, layer: BlockRenderLayer) = block.canRenderInLayer(state, layer) || layer == targetCutoutLayer
|
fun canRenderInLayerOverride(state: BlockState, layer: BlockRenderLayer) = state.canRenderInLayer(layer) || layer == targetCutoutLayer
|
||||||
|
|
||||||
val targetCutoutLayer: BlockRenderLayer get() = if (Minecraft.getMinecraft().gameSettings.mipmapLevels > 0) CUTOUT_MIPPED else CUTOUT
|
fun canRenderInLayerOverrideOptifine(state: BlockState, optifineReflector: Any?, layerArray: Array<Any>) =
|
||||||
val otherCutoutLayer: BlockRenderLayer get() = if (Minecraft.getMinecraft().gameSettings.mipmapLevels > 0) CUTOUT else CUTOUT_MIPPED
|
canRenderInLayerOverride(state, layerArray[0] as BlockRenderLayer)
|
||||||
|
|
||||||
|
val targetCutoutLayer: BlockRenderLayer get() = if (Minecraft.getInstance().gameSettings.mipmapLevels > 0) CUTOUT_MIPPED else CUTOUT
|
||||||
|
val otherCutoutLayer: BlockRenderLayer get() = if (Minecraft.getInstance().gameSettings.mipmapLevels > 0) CUTOUT else CUTOUT_MIPPED
|
||||||
@@ -1,56 +1,66 @@
|
|||||||
package mods.betterfoliage.client.chunk
|
package mods.betterfoliage.client.chunk
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage
|
||||||
import mods.betterfoliage.client.Client
|
import mods.betterfoliage.client.Client
|
||||||
import net.minecraft.block.state.IBlockState
|
import mods.betterfoliage.loader.Refs
|
||||||
import net.minecraft.client.multiplayer.WorldClient
|
import net.minecraft.block.BlockState
|
||||||
import net.minecraft.entity.Entity
|
import net.minecraft.client.renderer.chunk.ChunkRenderCache
|
||||||
import net.minecraft.entity.player.EntityPlayer
|
import net.minecraft.client.world.ClientWorld
|
||||||
import net.minecraft.util.SoundCategory
|
|
||||||
import net.minecraft.util.SoundEvent
|
|
||||||
import net.minecraft.util.math.BlockPos
|
import net.minecraft.util.math.BlockPos
|
||||||
import net.minecraft.util.math.ChunkPos
|
import net.minecraft.util.math.ChunkPos
|
||||||
import net.minecraft.world.IBlockAccess
|
import net.minecraft.world.IBlockReader
|
||||||
import net.minecraft.world.IWorldEventListener
|
import net.minecraft.world.IEnviromentBlockReader
|
||||||
import net.minecraft.world.World
|
import net.minecraft.world.IWorldReader
|
||||||
import net.minecraft.world.chunk.EmptyChunk
|
import net.minecraft.world.dimension.DimensionType
|
||||||
import net.minecraftforge.common.MinecraftForge
|
import net.minecraftforge.common.MinecraftForge
|
||||||
import net.minecraftforge.event.world.ChunkEvent
|
import net.minecraftforge.event.world.ChunkEvent
|
||||||
import net.minecraftforge.event.world.WorldEvent
|
import net.minecraftforge.event.world.WorldEvent
|
||||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
|
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||||
import org.apache.logging.log4j.Level
|
import org.apache.logging.log4j.Level
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
val IEnviromentBlockReader.dimType: DimensionType get() = when {
|
||||||
|
this is IWorldReader -> dimension.type
|
||||||
|
this is ChunkRenderCache -> world.dimension.type
|
||||||
|
Refs.OptifineChunkCache.isInstance(this) -> (Refs.CCOFChunkCache.get(this) as ChunkRenderCache).world.dimension.type
|
||||||
|
else -> throw IllegalArgumentException("DimensionType of world with class ${this::class.qualifiedName} cannot be determined!")
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents some form of arbitrary non-persistent data that can be calculated and cached for each block position
|
* Represents some form of arbitrary non-persistent data that can be calculated and cached for each block position
|
||||||
*/
|
*/
|
||||||
interface ChunkOverlayLayer<T> {
|
interface ChunkOverlayLayer<T> {
|
||||||
abstract fun calculate(world: IBlockAccess, pos: BlockPos): T
|
abstract fun calculate(reader: IEnviromentBlockReader, pos: BlockPos): T
|
||||||
abstract fun onBlockUpdate(world: IBlockAccess, pos: BlockPos)
|
abstract fun onBlockUpdate(reader: IEnviromentBlockReader, pos: BlockPos)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query, lazy calculation and lifecycle management of multiple layers of chunk overlay data.
|
* Query, lazy calculation and lifecycle management of multiple layers of chunk overlay data.
|
||||||
*/
|
*/
|
||||||
object ChunkOverlayManager : IBlockUpdateListener {
|
object ChunkOverlayManager {
|
||||||
|
|
||||||
|
var tempCounter = 0
|
||||||
|
|
||||||
init {
|
init {
|
||||||
Client.log(Level.INFO, "Initializing client overlay manager")
|
Client.log(Level.INFO, "Initializing client overlay manager")
|
||||||
MinecraftForge.EVENT_BUS.register(this)
|
MinecraftForge.EVENT_BUS.register(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
val chunkData = mutableMapOf<ChunkPos, ChunkOverlayData>()
|
val chunkData = IdentityHashMap<DimensionType, MutableMap<ChunkPos, ChunkOverlayData>>()
|
||||||
val layers = mutableListOf<ChunkOverlayLayer<*>>()
|
val layers = mutableListOf<ChunkOverlayLayer<*>>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the overlay data for a given layer and position
|
* Get the overlay data for a given layer and position
|
||||||
*
|
*
|
||||||
* @param layer Overlay layer to query
|
* @param layer Overlay layer to query
|
||||||
* @param world World to use if calculation of overlay value is necessary
|
* @param reader World to use if calculation of overlay value is necessary
|
||||||
* @param pos Block position
|
* @param pos Block position
|
||||||
*/
|
*/
|
||||||
fun <T> get(layer: ChunkOverlayLayer<T>, world: IBlockAccess, pos: BlockPos): T? {
|
fun <T> get(layer: ChunkOverlayLayer<T>, reader: IEnviromentBlockReader, pos: BlockPos): T? {
|
||||||
val data = chunkData[ChunkPos(pos)] ?: return null
|
val data = chunkData[reader.dimType]?.get(ChunkPos(pos)) ?: return null
|
||||||
data.get(layer, pos).let { value ->
|
data.get(layer, pos).let { value ->
|
||||||
if (value !== ChunkOverlayData.UNCALCULATED) return value
|
if (value !== ChunkOverlayData.UNCALCULATED) return value
|
||||||
val newValue = layer.calculate(world, pos)
|
val newValue = layer.calculate(reader, pos)
|
||||||
data.set(layer, pos, newValue)
|
data.set(layer, pos, newValue)
|
||||||
return newValue
|
return newValue
|
||||||
}
|
}
|
||||||
@@ -62,32 +72,39 @@ object ChunkOverlayManager : IBlockUpdateListener {
|
|||||||
* @param layer Overlay layer to clear
|
* @param layer Overlay layer to clear
|
||||||
* @param pos Block position
|
* @param pos Block position
|
||||||
*/
|
*/
|
||||||
fun <T> clear(layer: ChunkOverlayLayer<T>, pos: BlockPos) {
|
fun <T> clear(dimension: DimensionType, layer: ChunkOverlayLayer<T>, pos: BlockPos) {
|
||||||
chunkData[ChunkPos(pos)]?.clear(layer, pos)
|
chunkData[dimension]?.get(ChunkPos(pos))?.clear(layer, pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun notifyBlockUpdate(world: World, pos: BlockPos, oldState: IBlockState, newState: IBlockState, flags: Int) {
|
fun onBlockChange(world: ClientWorld, pos: BlockPos) {
|
||||||
if (chunkData.containsKey(ChunkPos(pos))) layers.forEach { layer -> layer.onBlockUpdate(world, pos) }
|
if (chunkData[world.dimType]?.containsKey(ChunkPos(pos)) == true) {
|
||||||
}
|
layers.forEach { layer -> layer.onBlockUpdate(world, pos) }
|
||||||
|
|
||||||
@SubscribeEvent
|
|
||||||
fun handleLoadWorld(event: WorldEvent.Load) {
|
|
||||||
if (event.world is WorldClient) {
|
|
||||||
event.world.addEventListener(this)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
fun handleLoadChunk(event: ChunkEvent.Load) {
|
fun handleLoadWorld(event: WorldEvent.Load) = (event.world as? ClientWorld)?.let { world ->
|
||||||
if (event.world is WorldClient && event.chunk !is EmptyChunk) {
|
BetterFoliage.log.debug("Unloaded world: id=${world.dimType.id} name=${world.dimType.registryName}")
|
||||||
chunkData[event.chunk.pos] = ChunkOverlayData(layers)
|
chunkData[world.dimType] = mutableMapOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
fun handleUnloadWorld(event: WorldEvent.Unload) = (event.world as? ClientWorld)?.let { world ->
|
||||||
|
BetterFoliage.log.debug("Unloaded world: id=${world.dimType.id} name=${world.dimType.registryName}")
|
||||||
|
chunkData.remove(world.dimType)
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
fun handleLoadChunk(event: ChunkEvent.Load) = (event.world as? ClientWorld)?.let { world ->
|
||||||
|
chunkData[world.dimType]?.let { chunks ->
|
||||||
|
// check for existence first because Optifine fires a TON of these
|
||||||
|
if (event.chunk.pos !in chunks.keys) chunks[event.chunk.pos] = ChunkOverlayData(layers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
fun handleUnloadChunk(event: ChunkEvent.Unload) {
|
fun handleUnloadChunk(event: ChunkEvent.Unload) = (event.world as? ClientWorld)?.let { world ->
|
||||||
if (event.world is WorldClient) {
|
chunkData[world.dimType]?.remove(event.chunk.pos)
|
||||||
chunkData.remove(event.chunk.pos)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,6 +124,7 @@ class ChunkOverlayData(layers: List<ChunkOverlayLayer<*>>) {
|
|||||||
* IWorldEventListener helper subclass
|
* IWorldEventListener helper subclass
|
||||||
* No-op for everything except notifyBlockUpdate()
|
* No-op for everything except notifyBlockUpdate()
|
||||||
*/
|
*/
|
||||||
|
/*
|
||||||
interface IBlockUpdateListener : IWorldEventListener {
|
interface IBlockUpdateListener : IWorldEventListener {
|
||||||
override fun playSoundToAllNearExcept(player: EntityPlayer?, soundIn: SoundEvent, category: SoundCategory, x: Double, y: Double, z: Double, volume: Float, pitch: Float) {}
|
override fun playSoundToAllNearExcept(player: EntityPlayer?, soundIn: SoundEvent, category: SoundCategory, x: Double, y: Double, z: Double, volume: Float, pitch: Float) {}
|
||||||
override fun onEntityAdded(entityIn: Entity) {}
|
override fun onEntityAdded(entityIn: Entity) {}
|
||||||
@@ -119,4 +137,8 @@ interface IBlockUpdateListener : IWorldEventListener {
|
|||||||
override fun playRecord(soundIn: SoundEvent, pos: BlockPos) {}
|
override fun playRecord(soundIn: SoundEvent, pos: BlockPos) {}
|
||||||
override fun sendBlockBreakProgress(breakerId: Int, pos: BlockPos, progress: Int) {}
|
override fun sendBlockBreakProgress(breakerId: Int, pos: BlockPos, progress: Int) {}
|
||||||
override fun markBlockRangeForRenderUpdate(x1: Int, y1: Int, z1: Int, x2: Int, y2: Int, z2: Int) {}
|
override fun markBlockRangeForRenderUpdate(x1: Int, y1: Int, z1: Int, x2: Int, y2: Int, z2: Int) {}
|
||||||
|
override fun addParticle(p0: IParticleData, p1: Boolean, p2: Double, p3: Double, p4: Double, p5: Double, p6: Double, p7: Double) {}
|
||||||
|
override fun addParticle(p0: IParticleData, p1: Boolean, p2: Boolean, p3: Double, p4: Double, p5: Double, p6: Double, p7: Double, p8: Double) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
@@ -1,18 +1,16 @@
|
|||||||
package mods.betterfoliage.client.config
|
package mods.betterfoliage.client.config
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
import mods.betterfoliage.BetterFoliage
|
||||||
import mods.betterfoliage.client.gui.BiomeListConfigEntry
|
import mods.octarinecore.client.resource.LoadModelDataEvent
|
||||||
import mods.octarinecore.common.config.*
|
import mods.octarinecore.common.config.*
|
||||||
import net.minecraft.client.Minecraft
|
import net.minecraft.util.ResourceLocation
|
||||||
import net.minecraft.world.biome.Biome
|
import net.minecraftforge.common.MinecraftForge
|
||||||
import net.minecraftforge.fml.client.event.ConfigChangedEvent
|
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||||
import net.minecraftforge.fml.relauncher.Side
|
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
|
||||||
import org.lwjgl.opengl.GL11
|
|
||||||
|
|
||||||
// BetterFoliage-specific property delegates
|
// BetterFoliage-specific property delegates
|
||||||
private val OBSOLETE = ObsoleteConfigProperty()
|
//private val OBSOLETE = ObsoleteConfigProperty()
|
||||||
private fun featureEnable() = boolean(true).lang("enabled")
|
private fun featureEnable() = boolean(true).lang("enabled")
|
||||||
|
/*
|
||||||
fun biomeList(defaults: (Biome) -> Boolean) = intList {
|
fun biomeList(defaults: (Biome) -> Boolean) = intList {
|
||||||
Biome.REGISTRY
|
Biome.REGISTRY
|
||||||
.filter { it != null && defaults(it) }
|
.filter { it != null && defaults(it) }
|
||||||
@@ -24,14 +22,15 @@ fun biomeList(defaults: (Biome) -> Boolean) = intList {
|
|||||||
private fun Biome.filterTemp(min: Float?, max: Float?) = (min == null || min <= defaultTemperature) && (max == null || max >= defaultTemperature)
|
private fun Biome.filterTemp(min: Float?, max: Float?) = (min == null || min <= defaultTemperature) && (max == null || max >= defaultTemperature)
|
||||||
private fun Biome.filterRain(min: Float?, max: Float?) = (min == null || min <= rainfall) && (max == null || max >= rainfall)
|
private fun Biome.filterRain(min: Float?, max: Float?) = (min == null || min <= rainfall) && (max == null || max >= rainfall)
|
||||||
private fun Biome.filterClass(vararg name: String) = name.any { it in this.javaClass.name.toLowerCase() }
|
private fun Biome.filterClass(vararg name: String) = name.any { it in this.javaClass.name.toLowerCase() }
|
||||||
|
*/
|
||||||
|
|
||||||
// Config singleton
|
// Config singleton
|
||||||
@SideOnly(Side.CLIENT)
|
object Config : DelegatingConfig(BetterFoliage.MOD_ID, BetterFoliage.MOD_ID) {
|
||||||
object Config : DelegatingConfig(BetterFoliageMod.MOD_ID, BetterFoliageMod.DOMAIN) {
|
|
||||||
|
|
||||||
var enabled by boolean(true)
|
val enabled by boolean(true)
|
||||||
var nVidia by boolean(GL11.glGetString(GL11.GL_VENDOR).toLowerCase().contains("nvidia"))
|
val nVidia by boolean(false)
|
||||||
|
|
||||||
|
/*
|
||||||
object blocks {
|
object blocks {
|
||||||
val leavesClasses = ConfigurableBlockMatcher(BetterFoliageMod.DOMAIN, "leaves_blocks_default.cfg")
|
val leavesClasses = ConfigurableBlockMatcher(BetterFoliageMod.DOMAIN, "leaves_blocks_default.cfg")
|
||||||
val leavesModels = ModelTextureListConfigOption(BetterFoliageMod.DOMAIN, "leaves_models_default.cfg", 1)
|
val leavesModels = ModelTextureListConfigOption(BetterFoliageMod.DOMAIN, "leaves_models_default.cfg", 1)
|
||||||
@@ -54,8 +53,9 @@ object Config : DelegatingConfig(BetterFoliageMod.MOD_ID, BetterFoliageMod.DOMAI
|
|||||||
val logsWhitelist = OBSOLETE
|
val logsWhitelist = OBSOLETE
|
||||||
val logsBlacklist = OBSOLETE
|
val logsBlacklist = OBSOLETE
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
object leaves {
|
object leaves : ConfigCategory() {
|
||||||
val enabled by featureEnable()
|
val enabled by featureEnable()
|
||||||
val snowEnabled by boolean(true)
|
val snowEnabled by boolean(true)
|
||||||
val hOffset by double(max=0.4, default=0.2).lang("hOffset")
|
val hOffset by double(max=0.4, default=0.2).lang("hOffset")
|
||||||
@@ -65,7 +65,7 @@ object Config : DelegatingConfig(BetterFoliageMod.MOD_ID, BetterFoliageMod.DOMAI
|
|||||||
val hideInternal by boolean(true)
|
val hideInternal by boolean(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
object shortGrass {
|
object shortGrass : ConfigCategory(){
|
||||||
val grassEnabled by boolean(true)
|
val grassEnabled by boolean(true)
|
||||||
val myceliumEnabled by boolean(true)
|
val myceliumEnabled by boolean(true)
|
||||||
val snowEnabled by boolean(true)
|
val snowEnabled by boolean(true)
|
||||||
@@ -79,23 +79,16 @@ object Config : DelegatingConfig(BetterFoliageMod.MOD_ID, BetterFoliageMod.DOMAI
|
|||||||
val saturationThreshold by double(default=0.1)
|
val saturationThreshold by double(default=0.1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// object hangingGrass {
|
object connectedGrass : ConfigCategory(){
|
||||||
// var enabled by featureEnable()
|
|
||||||
// var distance by distanceLimit()
|
|
||||||
// var size by double(min=0.25, max=1.5, default=0.75).lang("size")
|
|
||||||
// var separation by double(max=0.5, default=0.25)
|
|
||||||
// }
|
|
||||||
|
|
||||||
object connectedGrass {
|
|
||||||
val enabled by boolean(true)
|
val enabled by boolean(true)
|
||||||
val snowEnabled by boolean(false)
|
val snowEnabled by boolean(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
object roundLogs {
|
object roundLogs : ConfigCategory(){
|
||||||
val enabled by featureEnable()
|
val enabled by featureEnable()
|
||||||
val radiusSmall by double(max=0.5, default=0.25)
|
val radiusSmall by double(max=0.5, default=0.25)
|
||||||
val radiusLarge by double(max=0.5, default=0.44)
|
val radiusLarge by double(max=0.5, default=0.44)
|
||||||
val dimming by float(default = 0.7)
|
val dimming by double(default = 0.7)
|
||||||
val connectSolids by boolean(false)
|
val connectSolids by boolean(false)
|
||||||
val lenientConnect by boolean(true)
|
val lenientConnect by boolean(true)
|
||||||
val connectPerpendicular by boolean(true)
|
val connectPerpendicular by boolean(true)
|
||||||
@@ -104,41 +97,43 @@ object Config : DelegatingConfig(BetterFoliageMod.MOD_ID, BetterFoliageMod.DOMAI
|
|||||||
val zProtection by double(min = 0.9, default = 0.99)
|
val zProtection by double(min = 0.9, default = 0.99)
|
||||||
}
|
}
|
||||||
|
|
||||||
object cactus {
|
object cactus : ConfigCategory(){
|
||||||
val enabled by featureEnable()
|
val enabled by featureEnable()
|
||||||
val size by double(min=0.5, max=1.5, default=0.8).lang("size")
|
val size by double(min=0.5, max=1.5, default=0.8).lang("size")
|
||||||
val sizeVariation by double(max=0.5, default=0.1)
|
val sizeVariation by double(max=0.5, default=0.1)
|
||||||
val hOffset by double(max=0.5, default=0.1).lang("hOffset")
|
val hOffset by double(max=0.5, default=0.1).lang("hOffset")
|
||||||
}
|
}
|
||||||
|
|
||||||
object lilypad {
|
object lilypad : ConfigCategory(){
|
||||||
val enabled by featureEnable()
|
val enabled by featureEnable()
|
||||||
val hOffset by double(max=0.25, default=0.1).lang("hOffset")
|
val hOffset by double(max=0.25, default=0.1).lang("hOffset")
|
||||||
val flowerChance by int(max=64, default=16, min=0)
|
val flowerChance by int(max=64, default=16, min=0)
|
||||||
}
|
}
|
||||||
|
|
||||||
object reed {
|
object reed : ConfigCategory(){
|
||||||
val enabled by featureEnable()
|
val enabled by featureEnable()
|
||||||
val hOffset by double(max=0.4, default=0.2).lang("hOffset")
|
val hOffset by double(max=0.4, default=0.2).lang("hOffset")
|
||||||
val heightMin by double(min=1.5, max=3.5, default=1.7).lang("heightMin")
|
val heightMin by double(min=1.5, max=3.5, default=1.7).lang("heightMin")
|
||||||
val heightMax by double(min=1.5, max=3.5, default=2.2).lang("heightMax")
|
val heightMax by double(min=1.5, max=3.5, default=2.2).lang("heightMax")
|
||||||
val population by int(max=64, default=32).lang("population")
|
val population by int(max=64, default=32).lang("population")
|
||||||
val biomes by biomeList { it.filterTemp(0.4f, null) && it.filterRain(0.4f, null) }
|
val minBiomeTemp by double(default=0.4)
|
||||||
|
val minBiomeRainfall by double(default=0.4)
|
||||||
|
// val biomes by biomeList { it.filterTemp(0.4f, null) && it.filterRain(0.4f, null) }
|
||||||
val shaderWind by boolean(true).lang("shaderWind")
|
val shaderWind by boolean(true).lang("shaderWind")
|
||||||
}
|
}
|
||||||
|
|
||||||
object algae {
|
object algae : ConfigCategory(){
|
||||||
val enabled by featureEnable()
|
val enabled by featureEnable()
|
||||||
val hOffset by double(max=0.25, default=0.1).lang("hOffset")
|
val hOffset by double(max=0.25, default=0.1).lang("hOffset")
|
||||||
val size by double(min=0.5, max=1.5, default=1.0).lang("size")
|
val size by double(min=0.5, max=1.5, default=1.0).lang("size")
|
||||||
val heightMin by double(min=0.1, max=1.5, default=0.5).lang("heightMin")
|
val heightMin by double(min=0.1, max=1.5, default=0.5).lang("heightMin")
|
||||||
val heightMax by double(min=0.1, max=1.5, default=1.0).lang("heightMax")
|
val heightMax by double(min=0.1, max=1.5, default=1.0).lang("heightMax")
|
||||||
val population by int(max=64, default=48).lang("population")
|
val population by int(max=64, default=48).lang("population")
|
||||||
val biomes by biomeList { it.filterClass("river", "ocean") }
|
// val biomes by biomeList { it.filterClass("river", "ocean") }
|
||||||
val shaderWind by boolean(true).lang("shaderWind")
|
val shaderWind by boolean(true).lang("shaderWind")
|
||||||
}
|
}
|
||||||
|
|
||||||
object coral {
|
object coral : ConfigCategory(){
|
||||||
val enabled by featureEnable()
|
val enabled by featureEnable()
|
||||||
val shallowWater by boolean(false)
|
val shallowWater by boolean(false)
|
||||||
val hOffset by double(max=0.4, default=0.2).lang("hOffset")
|
val hOffset by double(max=0.4, default=0.2).lang("hOffset")
|
||||||
@@ -147,10 +142,10 @@ object Config : DelegatingConfig(BetterFoliageMod.MOD_ID, BetterFoliageMod.DOMAI
|
|||||||
val crustSize by double(min=0.5, max=1.5, default=1.4)
|
val crustSize by double(min=0.5, max=1.5, default=1.4)
|
||||||
val chance by int(max=64, default=32)
|
val chance by int(max=64, default=32)
|
||||||
val population by int(max=64, default=48).lang("population")
|
val population by int(max=64, default=48).lang("population")
|
||||||
val biomes by biomeList { it.filterClass("river", "ocean", "beach") }
|
// val biomes by biomeList { it.filterClass("river", "ocean", "beach") }
|
||||||
}
|
}
|
||||||
|
|
||||||
object netherrack {
|
object netherrack : ConfigCategory(){
|
||||||
val enabled by featureEnable()
|
val enabled by featureEnable()
|
||||||
val hOffset by double(max=0.4, default=0.2).lang("hOffset")
|
val hOffset by double(max=0.4, default=0.2).lang("hOffset")
|
||||||
val heightMin by double(min=0.1, max=1.5, default=0.6).lang("heightMin")
|
val heightMin by double(min=0.1, max=1.5, default=0.6).lang("heightMin")
|
||||||
@@ -158,7 +153,7 @@ object Config : DelegatingConfig(BetterFoliageMod.MOD_ID, BetterFoliageMod.DOMAI
|
|||||||
val size by double(min=0.5, max=1.5, default=1.0).lang("size")
|
val size by double(min=0.5, max=1.5, default=1.0).lang("size")
|
||||||
}
|
}
|
||||||
|
|
||||||
object fallingLeaves {
|
object fallingLeaves : ConfigCategory(){
|
||||||
val enabled by featureEnable()
|
val enabled by featureEnable()
|
||||||
val speed by double(min=0.01, max=0.15, default=0.05)
|
val speed by double(min=0.01, max=0.15, default=0.05)
|
||||||
val windStrength by double(min=0.1, max=2.0, default=0.5)
|
val windStrength by double(min=0.1, max=2.0, default=0.5)
|
||||||
@@ -170,20 +165,20 @@ object Config : DelegatingConfig(BetterFoliageMod.MOD_ID, BetterFoliageMod.DOMAI
|
|||||||
val opacityHack by boolean(true)
|
val opacityHack by boolean(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
object risingSoul {
|
object risingSoul : ConfigCategory(){
|
||||||
val enabled by featureEnable()
|
val enabled by featureEnable()
|
||||||
val chance by double(min=0.001, max=1.0, default=0.02)
|
val chance by double(min=0.001, max=1.0, default=0.02)
|
||||||
val perturb by double(min=0.01, max=0.25, default=0.05)
|
val perturb by double(min=0.01, max=0.25, default=0.05)
|
||||||
val headSize by double(min=0.25, max=1.5, default=1.0)
|
val headSize by double(min=0.25, max=1.5, default=1.0)
|
||||||
val trailSize by double(min=0.25, max=1.5, default=0.75)
|
val trailSize by double(min=0.25, max=1.5, default=0.75)
|
||||||
val opacity by float(min=0.05, max=1.0, default=0.5)
|
val opacity by double(min=0.05, max=1.0, default=0.5)
|
||||||
val sizeDecay by double(min=0.5, max=1.0, default=0.97)
|
val sizeDecay by double(min=0.5, max=1.0, default=0.97)
|
||||||
val opacityDecay by float(min=0.5, max=1.0, default=0.97)
|
val opacityDecay by double(min=0.5, max=1.0, default=0.97)
|
||||||
val lifetime by double(min=1.0, max=15.0, default=4.0)
|
val lifetime by double(min=1.0, max=15.0, default=4.0)
|
||||||
val trailLength by int(min=2, max=128, default=48)
|
val trailLength by int(min=2, max=128, default=48)
|
||||||
val trailDensity by int(min=1, max=16, default=3)
|
val trailDensity by int(min=1, max=16, default=3)
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
val forceReloadOptions = listOf(
|
val forceReloadOptions = listOf(
|
||||||
blocks.leavesClasses,
|
blocks.leavesClasses,
|
||||||
blocks.leavesModels,
|
blocks.leavesModels,
|
||||||
@@ -194,8 +189,39 @@ object Config : DelegatingConfig(BetterFoliageMod.MOD_ID, BetterFoliageMod.DOMAI
|
|||||||
|
|
||||||
override fun onChange(event: ConfigChangedEvent.PostConfigChangedEvent) {
|
override fun onChange(event: ConfigChangedEvent.PostConfigChangedEvent) {
|
||||||
if (hasChanged(forceReloadOptions))
|
if (hasChanged(forceReloadOptions))
|
||||||
Minecraft.getMinecraft().refreshResources()
|
Minecraft.getInstance().refreshResources()
|
||||||
else
|
else
|
||||||
Minecraft.getMinecraft().renderGlobal.loadRenderers()
|
Minecraft.getInstance().renderGlobal.loadRenderers()
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
object BlockConfig {
|
||||||
|
private val list = mutableListOf<Any>()
|
||||||
|
|
||||||
|
val leafBlocks = blocks("leaves_blocks_default.cfg")
|
||||||
|
val leafModels = models("leaves_models_default.cfg")
|
||||||
|
val grassBlocks = blocks("grass_blocks_default.cfg")
|
||||||
|
val grassModels = models("grass_models_default.cfg")
|
||||||
|
val mycelium = blocks("mycelium_blocks_default.cfg")
|
||||||
|
// val dirt = blocks("dirt_default.cfg")
|
||||||
|
val crops = blocks("crop_default.cfg")
|
||||||
|
val logBlocks = blocks("log_blocks_default.cfg")
|
||||||
|
val logModels = models("log_models_default.cfg")
|
||||||
|
val sand = blocks("sand_default.cfg")
|
||||||
|
val lilypad = blocks("lilypad_default.cfg")
|
||||||
|
val cactus = blocks("cactus_default.cfg")
|
||||||
|
val netherrack = blocks("netherrack_blocks_default.cfg")
|
||||||
|
|
||||||
|
init { BetterFoliage.modBus.register(this) }
|
||||||
|
private fun blocks(cfgName: String) = ConfigurableBlockMatcher(BetterFoliage.logDetail, ResourceLocation(BetterFoliage.MOD_ID, cfgName)).apply { list.add(this) }
|
||||||
|
private fun models(cfgName: String) = ModelTextureListConfiguration(BetterFoliage.logDetail, ResourceLocation(BetterFoliage.MOD_ID, cfgName)).apply { list.add(this) }
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
fun handleLoadModelData(event: LoadModelDataEvent) {
|
||||||
|
list.forEach { when(it) {
|
||||||
|
is ConfigurableBlockMatcher -> it.readDefaults()
|
||||||
|
is ModelTextureListConfiguration -> it.readDefaults()
|
||||||
|
} }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package mods.betterfoliage.client.gui
|
|
||||||
|
|
||||||
import mods.octarinecore.client.gui.IdListConfigEntry
|
|
||||||
import net.minecraft.world.biome.Biome
|
|
||||||
import net.minecraftforge.fml.client.config.GuiConfig
|
|
||||||
import net.minecraftforge.fml.client.config.GuiConfigEntries
|
|
||||||
import net.minecraftforge.fml.client.config.IConfigElement
|
|
||||||
|
|
||||||
/** Toggleable list of all defined biomes. */
|
|
||||||
class BiomeListConfigEntry(
|
|
||||||
owningScreen: GuiConfig,
|
|
||||||
owningEntryList: GuiConfigEntries,
|
|
||||||
configElement: IConfigElement)
|
|
||||||
: IdListConfigEntry<Biome>(owningScreen, owningEntryList, configElement) {
|
|
||||||
|
|
||||||
override val baseSet: List<Biome> get() = Biome.REGISTRY.filterNotNull()
|
|
||||||
override val Biome.itemId: Int get() = Biome.REGISTRY.getIDForObject(this)
|
|
||||||
override val Biome.itemName: String get() = this.biomeName
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package mods.betterfoliage.client.gui
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
|
||||||
import mods.betterfoliage.client.config.Config
|
|
||||||
import net.minecraft.client.Minecraft
|
|
||||||
import net.minecraft.client.gui.GuiScreen
|
|
||||||
import net.minecraftforge.fml.client.IModGuiFactory
|
|
||||||
import net.minecraftforge.fml.client.config.GuiConfig
|
|
||||||
|
|
||||||
class ConfigGuiFactory : IModGuiFactory {
|
|
||||||
|
|
||||||
override fun initialize(minecraftInstance: Minecraft?) { }
|
|
||||||
override fun hasConfigGui() = true
|
|
||||||
override fun runtimeGuiCategories() = hashSetOf<IModGuiFactory.RuntimeOptionCategoryElement>()
|
|
||||||
override fun createConfigGui(parentScreen: GuiScreen?) = createBFConfigGui(parentScreen)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
fun createBFConfigGui(parentScreen: GuiScreen?) = GuiConfig(
|
|
||||||
parentScreen,
|
|
||||||
Config.rootGuiElements,
|
|
||||||
BetterFoliageMod.MOD_ID,
|
|
||||||
null,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
BetterFoliageMod.MOD_NAME
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package mods.betterfoliage.client.integration
|
package mods.betterfoliage.client.integration
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
import mods.betterfoliage.BetterFoliage
|
||||||
import mods.betterfoliage.client.Client
|
import mods.betterfoliage.client.Client
|
||||||
import mods.betterfoliage.client.config.Config
|
import mods.betterfoliage.client.config.BlockConfig
|
||||||
import mods.betterfoliage.client.render.LogRegistry
|
import mods.betterfoliage.client.render.LogRegistry
|
||||||
import mods.betterfoliage.client.render.StandardLogRegistry
|
import mods.betterfoliage.client.render.StandardLogRegistry
|
||||||
import mods.betterfoliage.client.render.column.ColumnTextureInfo
|
import mods.betterfoliage.client.render.column.ColumnTextureInfo
|
||||||
@@ -19,19 +19,19 @@ import mods.octarinecore.metaprog.ClassRef
|
|||||||
import mods.octarinecore.metaprog.FieldRef
|
import mods.octarinecore.metaprog.FieldRef
|
||||||
import mods.octarinecore.metaprog.MethodRef
|
import mods.octarinecore.metaprog.MethodRef
|
||||||
import mods.octarinecore.metaprog.allAvailable
|
import mods.octarinecore.metaprog.allAvailable
|
||||||
import net.minecraft.block.state.IBlockState
|
import net.minecraft.block.BlockState
|
||||||
import net.minecraft.client.Minecraft
|
import net.minecraft.client.Minecraft
|
||||||
import net.minecraft.client.renderer.block.model.ModelResourceLocation
|
import net.minecraft.client.renderer.model.IUnbakedModel
|
||||||
|
import net.minecraft.client.renderer.model.ModelResourceLocation
|
||||||
import net.minecraft.util.ResourceLocation
|
import net.minecraft.util.ResourceLocation
|
||||||
import net.minecraft.util.math.BlockPos
|
import net.minecraft.util.math.BlockPos
|
||||||
import net.minecraft.world.IBlockAccess
|
import net.minecraft.world.IBlockReader
|
||||||
|
import net.minecraft.world.IWorldReader
|
||||||
import net.minecraftforge.client.event.TextureStitchEvent
|
import net.minecraftforge.client.event.TextureStitchEvent
|
||||||
import net.minecraftforge.client.model.IModel
|
import net.minecraftforge.client.model.IModel
|
||||||
import net.minecraftforge.fml.common.Loader
|
import net.minecraftforge.eventbus.api.EventPriority
|
||||||
import net.minecraftforge.fml.common.eventhandler.EventPriority
|
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
|
import net.minecraftforge.fml.ModList
|
||||||
import net.minecraftforge.fml.relauncher.Side
|
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
|
||||||
import org.apache.logging.log4j.Level
|
import org.apache.logging.log4j.Level
|
||||||
import kotlin.collections.Map
|
import kotlin.collections.Map
|
||||||
import kotlin.collections.component1
|
import kotlin.collections.component1
|
||||||
@@ -45,7 +45,6 @@ import kotlin.collections.mapValues
|
|||||||
import kotlin.collections.mutableMapOf
|
import kotlin.collections.mutableMapOf
|
||||||
import kotlin.collections.set
|
import kotlin.collections.set
|
||||||
|
|
||||||
@SideOnly(Side.CLIENT)
|
|
||||||
object ForestryIntegration {
|
object ForestryIntegration {
|
||||||
|
|
||||||
val TextureLeaves = ClassRef("forestry.arboriculture.models.TextureLeaves")
|
val TextureLeaves = ClassRef("forestry.arboriculture.models.TextureLeaves")
|
||||||
@@ -71,7 +70,7 @@ object ForestryIntegration {
|
|||||||
val getSprite = MethodRef(ILeafSpriteProvider, "getSprite", Refs.ResourceLocation, ClassRef.boolean, ClassRef.boolean)
|
val getSprite = MethodRef(ILeafSpriteProvider, "getSprite", Refs.ResourceLocation, ClassRef.boolean, ClassRef.boolean)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (Loader.isModLoaded("forestry") && allAvailable(TiLgetLeaveSprite, getLeafSpriteProvider, getSprite)) {
|
if (ModList.get().isLoaded("forestry") && allAvailable(TiLgetLeaveSprite, getLeafSpriteProvider, getSprite)) {
|
||||||
Client.log(Level.INFO, "Forestry support initialized")
|
Client.log(Level.INFO, "Forestry support initialized")
|
||||||
LeafRegistry.addRegistry(ForestryLeafRegistry)
|
LeafRegistry.addRegistry(ForestryLeafRegistry)
|
||||||
LogRegistry.addRegistry(ForestryLogRegistry)
|
LogRegistry.addRegistry(ForestryLogRegistry)
|
||||||
@@ -80,13 +79,13 @@ object ForestryIntegration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
object ForestryLeafRegistry : ModelRenderRegistry<LeafInfo> {
|
object ForestryLeafRegistry : ModelRenderRegistry<LeafInfo> {
|
||||||
val logger = BetterFoliageMod.logDetail
|
val logger = BetterFoliage.logDetail
|
||||||
val textureToKey = mutableMapOf<ResourceLocation, ModelRenderKey<LeafInfo>>()
|
val textureToKey = mutableMapOf<ResourceLocation, ModelRenderKey<LeafInfo>>()
|
||||||
var textureToValue = emptyMap<ResourceLocation, LeafInfo>()
|
var textureToValue = emptyMap<ResourceLocation, LeafInfo>()
|
||||||
|
|
||||||
override fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos): LeafInfo? {
|
override fun get(state: BlockState, world: IBlockReader, pos: BlockPos): LeafInfo? {
|
||||||
// check variant property (used in decorative leaves)
|
// check variant property (used in decorative leaves)
|
||||||
state.properties.entries.find {
|
state.values.entries.find {
|
||||||
ForestryIntegration.PropertyTreeType.isInstance(it.key) && ForestryIntegration.TreeDefinition.isInstance(it.value)
|
ForestryIntegration.PropertyTreeType.isInstance(it.key) && ForestryIntegration.TreeDefinition.isInstance(it.value)
|
||||||
} ?.let {
|
} ?.let {
|
||||||
val species = ForestryIntegration.TdSpecies.get(it.value)
|
val species = ForestryIntegration.TdSpecies.get(it.value)
|
||||||
@@ -96,7 +95,7 @@ object ForestryLeafRegistry : ModelRenderRegistry<LeafInfo> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// extract leaf texture information from TileEntity
|
// extract leaf texture information from TileEntity
|
||||||
val tile = world.getTileEntitySafe(pos) ?: return null
|
val tile = world.getTileEntity(pos) ?: return null
|
||||||
if (!ForestryIntegration.TileLeaves.isInstance(tile)) return null
|
if (!ForestryIntegration.TileLeaves.isInstance(tile)) return null
|
||||||
val textureLoc = ForestryIntegration.TiLgetLeaveSprite.invoke(tile, Minecraft.isFancyGraphicsEnabled()) ?: return null
|
val textureLoc = ForestryIntegration.TiLgetLeaveSprite.invoke(tile, Minecraft.isFancyGraphicsEnabled()) ?: return null
|
||||||
return textureToValue[textureLoc]
|
return textureToValue[textureLoc]
|
||||||
@@ -115,7 +114,7 @@ object ForestryLeafRegistry : ModelRenderRegistry<LeafInfo> {
|
|||||||
ForestryIntegration.TeLpollplain.get(it.value) as ResourceLocation,
|
ForestryIntegration.TeLpollplain.get(it.value) as ResourceLocation,
|
||||||
ForestryIntegration.TeLpollfancy.get(it.value) as ResourceLocation
|
ForestryIntegration.TeLpollfancy.get(it.value) as ResourceLocation
|
||||||
).forEach { textureLocation ->
|
).forEach { textureLocation ->
|
||||||
val key = StandardLeafKey(logger, textureLocation.toString()).apply { onPreStitch(event.map) }
|
val key = StandardLeafKey(logger, textureLocation.toString()).apply { onPreStitch(event) }
|
||||||
textureToKey[textureLocation] = key
|
textureToKey[textureLocation] = key
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,14 +128,14 @@ object ForestryLeafRegistry : ModelRenderRegistry<LeafInfo> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
object ForestryLogRegistry : ModelRenderRegistryBase<ColumnTextureInfo>() {
|
object ForestryLogRegistry : ModelRenderRegistryBase<ColumnTextureInfo>() {
|
||||||
override val logger = BetterFoliageMod.logDetail
|
override val logger = BetterFoliage.logDetail
|
||||||
|
|
||||||
override fun processModel(state: IBlockState, modelLoc: ModelResourceLocation, model: IModel): ModelRenderKey<ColumnTextureInfo>? {
|
override fun processModel(state: BlockState, modelLoc: ModelResourceLocation, models: List<Pair<IUnbakedModel, ResourceLocation>>): ModelRenderKey<ColumnTextureInfo>? {
|
||||||
// respect class list to avoid triggering on fences, stairs, etc.
|
// respect class list to avoid triggering on fences, stairs, etc.
|
||||||
if (!Config.blocks.logClasses.matchesClass(state.block)) return null
|
if (!BlockConfig.logBlocks.matchesClass(state.block)) return null
|
||||||
|
|
||||||
// find wood type property
|
// find wood type property
|
||||||
val woodType = state.properties.entries.find {
|
val woodType = state.values.entries.find {
|
||||||
ForestryIntegration.PropertyWoodType.isInstance(it.key) && ForestryIntegration.IWoodType.isInstance(it.value)
|
ForestryIntegration.PropertyWoodType.isInstance(it.key) && ForestryIntegration.IWoodType.isInstance(it.value)
|
||||||
} ?: return null
|
} ?: return null
|
||||||
|
|
||||||
|
|||||||
@@ -7,22 +7,19 @@ import mods.octarinecore.client.render.BlockContext
|
|||||||
import mods.octarinecore.common.Int3
|
import mods.octarinecore.common.Int3
|
||||||
import mods.octarinecore.metaprog.allAvailable
|
import mods.octarinecore.metaprog.allAvailable
|
||||||
import mods.octarinecore.metaprog.reflectField
|
import mods.octarinecore.metaprog.reflectField
|
||||||
import net.minecraft.block.state.IBlockState
|
import net.minecraft.block.BlockState
|
||||||
import net.minecraft.client.Minecraft
|
import net.minecraft.client.Minecraft
|
||||||
import net.minecraft.client.renderer.block.model.BakedQuad
|
import net.minecraft.client.renderer.model.BakedQuad
|
||||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats
|
import net.minecraft.client.renderer.vertex.DefaultVertexFormats
|
||||||
import net.minecraft.util.EnumFacing
|
import net.minecraft.util.Direction.UP
|
||||||
import net.minecraft.util.math.BlockPos
|
import net.minecraft.util.math.BlockPos
|
||||||
import net.minecraft.world.IBlockAccess
|
import net.minecraft.world.IBlockReader
|
||||||
import net.minecraftforge.fml.relauncher.Side
|
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
|
||||||
import org.apache.logging.log4j.Level
|
import org.apache.logging.log4j.Level
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Integration for OptiFine custom block colors.
|
* Integration for OptiFine custom block colors.
|
||||||
*/
|
*/
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
@SideOnly(Side.CLIENT)
|
|
||||||
object OptifineCustomColors {
|
object OptifineCustomColors {
|
||||||
|
|
||||||
val isColorAvailable = allAvailable(
|
val isColorAvailable = allAvailable(
|
||||||
@@ -34,27 +31,26 @@ object OptifineCustomColors {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val renderEnv by ThreadLocalDelegate { OptifineRenderEnv() }
|
val renderEnv by ThreadLocalDelegate { OptifineRenderEnv() }
|
||||||
val fakeQuad = BakedQuad(IntArray(0), 1, EnumFacing.UP, null, true, DefaultVertexFormats.BLOCK)
|
val fakeQuad = BakedQuad(IntArray(0), 1, UP, null, true, DefaultVertexFormats.BLOCK)
|
||||||
|
|
||||||
fun getBlockColor(ctx: BlockContext): Int {
|
fun getBlockColor(ctx: BlockContext): Int {
|
||||||
val ofColor = if (isColorAvailable && Minecraft.getMinecraft().gameSettings.reflectField<Boolean>("ofCustomColors") == true) {
|
val ofColor = if (isColorAvailable && Minecraft.getInstance().gameSettings.reflectField<Boolean>("ofCustomColors") == true) {
|
||||||
renderEnv.reset(ctx.world!!, ctx.blockState(Int3.zero), ctx.pos)
|
renderEnv.reset(ctx.reader!!, ctx.blockState(Int3.zero), ctx.pos)
|
||||||
Refs.getColorMultiplier.invokeStatic(fakeQuad, ctx.blockState(Int3.zero), ctx.world!!, ctx.pos, renderEnv.wrapped) as? Int
|
Refs.getColorMultiplier.invokeStatic(fakeQuad, ctx.blockState(Int3.zero), ctx.reader!!, ctx.pos, renderEnv.wrapped) as? Int
|
||||||
} else null
|
} else null
|
||||||
return if (ofColor == null || ofColor == -1) ctx.blockData(Int3.zero).color else ofColor
|
return if (ofColor == null || ofColor == -1) ctx.blockData(Int3.zero).color else ofColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SideOnly(Side.CLIENT)
|
|
||||||
class OptifineRenderEnv {
|
class OptifineRenderEnv {
|
||||||
val wrapped: Any = Refs.RenderEnv.element!!.getDeclaredConstructor(
|
val wrapped: Any = Refs.RenderEnv.element!!.getDeclaredConstructor(
|
||||||
Refs.IBlockAccess.element, Refs.IBlockState.element, Refs.BlockPos.element
|
Refs.IBlockReader.element, Refs.BlockState.element, Refs.BlockPos.element
|
||||||
).let {
|
).let {
|
||||||
it.isAccessible = true
|
it.isAccessible = true
|
||||||
it.newInstance(null, null, null)
|
it.newInstance(null, null, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun reset(blockAccess: IBlockAccess, state: IBlockState, pos: BlockPos) {
|
fun reset(blockAccess: IBlockReader, state: BlockState, pos: BlockPos) {
|
||||||
Refs.RenderEnv_reset.invoke(wrapped, blockAccess, state, pos)
|
Refs.RenderEnv_reset.invoke(wrapped, blockAccess, state, pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package mods.betterfoliage.client.integration
|
package mods.betterfoliage.client.integration
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
import mods.betterfoliage.BetterFoliage
|
||||||
import mods.betterfoliage.client.Client
|
import mods.betterfoliage.client.Client
|
||||||
import mods.betterfoliage.client.render.LogRegistry
|
import mods.betterfoliage.client.render.LogRegistry
|
||||||
import mods.betterfoliage.client.render.column.ColumnTextureInfo
|
import mods.betterfoliage.client.render.column.ColumnTextureInfo
|
||||||
@@ -13,39 +13,39 @@ import mods.octarinecore.client.resource.*
|
|||||||
import mods.octarinecore.common.rotate
|
import mods.octarinecore.common.rotate
|
||||||
import mods.octarinecore.metaprog.ClassRef
|
import mods.octarinecore.metaprog.ClassRef
|
||||||
import mods.octarinecore.metaprog.allAvailable
|
import mods.octarinecore.metaprog.allAvailable
|
||||||
import net.minecraft.block.state.IBlockState
|
import net.minecraft.block.BlockState
|
||||||
import net.minecraft.client.renderer.block.model.ModelResourceLocation
|
import net.minecraft.client.renderer.model.BlockModel
|
||||||
|
import net.minecraft.client.renderer.model.IUnbakedModel
|
||||||
|
import net.minecraft.client.renderer.model.ModelResourceLocation
|
||||||
|
import net.minecraft.client.renderer.texture.AtlasTexture
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||||
import net.minecraft.client.renderer.texture.TextureMap
|
import net.minecraft.util.Direction
|
||||||
import net.minecraft.util.EnumFacing
|
import net.minecraft.util.Direction.*
|
||||||
import net.minecraft.util.ResourceLocation
|
import net.minecraft.util.ResourceLocation
|
||||||
import net.minecraftforge.client.model.IModel
|
import net.minecraftforge.client.model.IModel
|
||||||
import net.minecraftforge.fml.common.Loader
|
import net.minecraftforge.fml.ModList
|
||||||
import net.minecraftforge.fml.relauncher.Side
|
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
|
||||||
import org.apache.logging.log4j.Level
|
import org.apache.logging.log4j.Level
|
||||||
|
import org.apache.logging.log4j.Level.DEBUG
|
||||||
import org.apache.logging.log4j.Logger
|
import org.apache.logging.log4j.Logger
|
||||||
|
|
||||||
@SideOnly(Side.CLIENT)
|
|
||||||
object IC2RubberIntegration {
|
object IC2RubberIntegration {
|
||||||
|
|
||||||
val BlockRubWood = ClassRef("ic2.core.block.BlockRubWood")
|
val BlockRubWood = ClassRef("ic2.core.block.BlockRubWood")
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (Loader.isModLoaded("ic2") && allAvailable(BlockRubWood)) {
|
if (ModList.get().isLoaded("ic2") && allAvailable(BlockRubWood)) {
|
||||||
Client.log(Level.INFO, "IC2 rubber support initialized")
|
Client.log(Level.INFO, "IC2 rubber support initialized")
|
||||||
LogRegistry.addRegistry(IC2LogSupport)
|
LogRegistry.addRegistry(IC2LogSupport)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SideOnly(Side.CLIENT)
|
|
||||||
object TechRebornRubberIntegration {
|
object TechRebornRubberIntegration {
|
||||||
|
|
||||||
val BlockRubberLog = ClassRef("techreborn.blocks.BlockRubberLog")
|
val BlockRubberLog = ClassRef("techreborn.blocks.BlockRubberLog")
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (Loader.isModLoaded("techreborn") && allAvailable(BlockRubberLog)) {
|
if (ModList.get().isLoaded("techreborn") && allAvailable(BlockRubberLog)) {
|
||||||
Client.log(Level.INFO, "TechReborn rubber support initialized")
|
Client.log(Level.INFO, "TechReborn rubber support initialized")
|
||||||
LogRegistry.addRegistry(TechRebornLogSupport)
|
LogRegistry.addRegistry(TechRebornLogSupport)
|
||||||
}
|
}
|
||||||
@@ -53,8 +53,8 @@ object TechRebornRubberIntegration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class RubberLogInfo(
|
class RubberLogInfo(
|
||||||
axis: EnumFacing.Axis?,
|
axis: Axis?,
|
||||||
val spotDir: EnumFacing,
|
val spotDir: Direction,
|
||||||
topTexture: TextureAtlasSprite,
|
topTexture: TextureAtlasSprite,
|
||||||
bottomTexture: TextureAtlasSprite,
|
bottomTexture: TextureAtlasSprite,
|
||||||
val spotTexture: TextureAtlasSprite,
|
val spotTexture: TextureAtlasSprite,
|
||||||
@@ -62,85 +62,85 @@ class RubberLogInfo(
|
|||||||
) : SimpleColumnInfo(axis, topTexture, bottomTexture, sideTextures) {
|
) : SimpleColumnInfo(axis, topTexture, bottomTexture, sideTextures) {
|
||||||
|
|
||||||
override val side: QuadIconResolver = { ctx: ShadingContext, idx: Int, quad: Quad ->
|
override val side: QuadIconResolver = { ctx: ShadingContext, idx: Int, quad: Quad ->
|
||||||
val worldFace = (if ((idx and 1) == 0) EnumFacing.SOUTH else EnumFacing.EAST).rotate(ctx.rotation)
|
val worldFace = (if ((idx and 1) == 0) SOUTH else EAST).rotate(ctx.rotation)
|
||||||
if (worldFace == spotDir) spotTexture else {
|
if (worldFace == spotDir) spotTexture else {
|
||||||
val sideIdx = if (this.sideTextures.size > 1) (blockContext.random(1) + dirToIdx[worldFace.ordinal]) % this.sideTextures.size else 0
|
val sideIdx = if (this.sideTextures.size > 1) (blockContext.random(1) + dirToIdx[worldFace.ordinal]) % this.sideTextures.size else 0
|
||||||
this.sideTextures[sideIdx]
|
this.sideTextures[sideIdx]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Key(override val logger: Logger, val axis: EnumFacing.Axis?, val spotDir: EnumFacing, val textures: List<String>): ModelRenderKey<ColumnTextureInfo> {
|
class Key(override val logger: Logger, val axis: Axis?, val spotDir: Direction, val textures: List<String>): ModelRenderKey<ColumnTextureInfo> {
|
||||||
override fun resolveSprites(atlas: TextureMap) = RubberLogInfo(
|
override fun resolveSprites(atlas: AtlasTexture) = RubberLogInfo(
|
||||||
axis,
|
axis,
|
||||||
spotDir,
|
spotDir,
|
||||||
atlas[textures[0]] ?: atlas.missingSprite,
|
atlas[textures[0]] ?: missingSprite,
|
||||||
atlas[textures[1]] ?: atlas.missingSprite,
|
atlas[textures[1]] ?: missingSprite,
|
||||||
atlas[textures[2]] ?: atlas.missingSprite,
|
atlas[textures[2]] ?: missingSprite,
|
||||||
textures.drop(3).map { atlas[it] ?: atlas.missingSprite }
|
textures.drop(3).map { atlas[it] ?: missingSprite }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object IC2LogSupport : ModelRenderRegistryBase<ColumnTextureInfo>() {
|
object IC2LogSupport : ModelRenderRegistryBase<ColumnTextureInfo>() {
|
||||||
override val logger = BetterFoliageMod.logDetail
|
override val logger = BetterFoliage.logDetail
|
||||||
|
|
||||||
override fun processModel(state: IBlockState, modelLoc: ModelResourceLocation, model: IModel): ModelRenderKey<ColumnTextureInfo>? {
|
override fun processModel(state: BlockState, modelLoc: ModelResourceLocation, models: List<Pair<IUnbakedModel, ResourceLocation>>): ModelRenderKey<ColumnTextureInfo>? {
|
||||||
// check for proper block class, existence of ModelBlock, and "state" blockstate property
|
// check for proper block class, existence of ModelBlock, and "state" blockstate property
|
||||||
if (!IC2RubberIntegration.BlockRubWood.isInstance(state.block)) return null
|
if (!IC2RubberIntegration.BlockRubWood.isInstance(state.block)) return null
|
||||||
val blockLoc = model.modelBlockAndLoc.firstOrNull() ?: return null
|
val blockLoc = models.firstOrNull() as Pair<BlockModel, ResourceLocation> ?: return null
|
||||||
val type = state.properties.entries.find { it.key.getName() == "state" }?.value?.toString() ?: return null
|
val type = state.values.entries.find { it.key.getName() == "state" }?.value?.toString() ?: return null
|
||||||
|
|
||||||
// logs with no rubber spot
|
// logs with no rubber spot
|
||||||
if (blockLoc.derivesFrom(ResourceLocation("block/cube_column"))) {
|
if (blockLoc.derivesFrom(ResourceLocation("block/cube_column"))) {
|
||||||
val axis = when(type) {
|
val axis = when(type) {
|
||||||
"plain_y" -> EnumFacing.Axis.Y
|
"plain_y" -> Axis.Y
|
||||||
"plain_x" -> EnumFacing.Axis.X
|
"plain_x" -> Axis.X
|
||||||
"plain_z" -> EnumFacing.Axis.Z
|
"plain_z" -> Axis.Z
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
val textureNames = listOf("end", "end", "side").map { blockLoc.first.resolveTextureName(it) }
|
val textureNames = listOf("end", "end", "side").map { blockLoc.first.resolveTextureName(it) }
|
||||||
if (textureNames.any { it == "missingno" }) return null
|
if (textureNames.any { it == "missingno" }) return null
|
||||||
logger.log(Level.DEBUG, "IC2LogSupport: block state ${state.toString()}")
|
logger.log(DEBUG, "IC2LogSupport: block state ${state.toString()}")
|
||||||
logger.log(Level.DEBUG, "IC2LogSupport: axis=$axis, end=${textureNames[0]}, side=${textureNames[2]}")
|
logger.log(DEBUG, "IC2LogSupport: axis=$axis, end=${textureNames[0]}, side=${textureNames[2]}")
|
||||||
return SimpleColumnInfo.Key(logger, axis, textureNames)
|
return SimpleColumnInfo.Key(logger, axis, textureNames)
|
||||||
}
|
}
|
||||||
|
|
||||||
// logs with rubber spot
|
// logs with rubber spot
|
||||||
val spotDir = when(type) {
|
val spotDir = when(type) {
|
||||||
"dry_north", "wet_north" -> EnumFacing.NORTH
|
"dry_north", "wet_north" -> NORTH
|
||||||
"dry_south", "wet_south" -> EnumFacing.SOUTH
|
"dry_south", "wet_south" -> SOUTH
|
||||||
"dry_west", "wet_west" -> EnumFacing.WEST
|
"dry_west", "wet_west" -> WEST
|
||||||
"dry_east", "wet_east" -> EnumFacing.EAST
|
"dry_east", "wet_east" -> EAST
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
val textureNames = listOf("up", "down", "north", "south").map { blockLoc.first.resolveTextureName(it) }
|
val textureNames = listOf("up", "down", "north", "south").map { blockLoc.first.resolveTextureName(it) }
|
||||||
if (textureNames.any { it == "missingno" }) return null
|
if (textureNames.any { it == "missingno" }) return null
|
||||||
logger.log(Level.DEBUG, "IC2LogSupport: block state ${state.toString()}")
|
logger.log(DEBUG, "IC2LogSupport: block state ${state.toString()}")
|
||||||
logger.log(Level.DEBUG, "IC2LogSupport: spotDir=$spotDir, up=${textureNames[0]}, down=${textureNames[1]}, side=${textureNames[2]}, spot=${textureNames[3]}")
|
logger.log(DEBUG, "IC2LogSupport: spotDir=$spotDir, up=${textureNames[0]}, down=${textureNames[1]}, side=${textureNames[2]}, spot=${textureNames[3]}")
|
||||||
return if (spotDir != null) RubberLogInfo.Key(logger, EnumFacing.Axis.Y, spotDir, textureNames) else SimpleColumnInfo.Key(logger, EnumFacing.Axis.Y, textureNames)
|
return if (spotDir != null) RubberLogInfo.Key(logger, Axis.Y, spotDir, textureNames) else SimpleColumnInfo.Key(logger, Axis.Y, textureNames)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object TechRebornLogSupport : ModelRenderRegistryBase<ColumnTextureInfo>() {
|
object TechRebornLogSupport : ModelRenderRegistryBase<ColumnTextureInfo>() {
|
||||||
override val logger = BetterFoliageMod.logDetail
|
override val logger = BetterFoliage.logDetail
|
||||||
|
|
||||||
override fun processModel(state: IBlockState, modelLoc: ModelResourceLocation, model: IModel): ModelRenderKey<ColumnTextureInfo>? {
|
override fun processModel(state: BlockState, modelLoc: ModelResourceLocation, models: List<Pair<IUnbakedModel, ResourceLocation>>): ModelRenderKey<ColumnTextureInfo>? {
|
||||||
// check for proper block class, existence of ModelBlock
|
// check for proper block class, existence of ModelBlock
|
||||||
if (!TechRebornRubberIntegration.BlockRubberLog.isInstance(state.block)) return null
|
if (!TechRebornRubberIntegration.BlockRubberLog.isInstance(state.block)) return null
|
||||||
val blockLoc = model.modelBlockAndLoc.firstOrNull() ?: return null
|
val blockLoc = models.firstOrNull() as Pair<BlockModel, ResourceLocation> ?: return null
|
||||||
|
|
||||||
val hasSap = state.properties.entries.find { it.key.getName() == "hassap" }?.value as? Boolean ?: return null
|
val hasSap = state.values.entries.find { it.key.getName() == "hassap" }?.value as? Boolean ?: return null
|
||||||
val sapSide = state.properties.entries.find { it.key.getName() == "sapside" }?.value as? EnumFacing ?: return null
|
val sapSide = state.values.entries.find { it.key.getName() == "sapside" }?.value as? Direction ?: return null
|
||||||
|
|
||||||
logger.log(Level.DEBUG, "$logName: block state $state")
|
logger.log(DEBUG, "$logName: block state $state")
|
||||||
if (hasSap) {
|
if (hasSap) {
|
||||||
val textureNames = listOf("end", "end", "sapside", "side").map { blockLoc.first.resolveTextureName(it) }
|
val textureNames = listOf("end", "end", "sapside", "side").map { blockLoc.first.resolveTextureName(it) }
|
||||||
logger.log(Level.DEBUG, "$logName: spotDir=$sapSide, end=${textureNames[0]}, side=${textureNames[2]}, spot=${textureNames[3]}")
|
logger.log(DEBUG, "$logName: spotDir=$sapSide, end=${textureNames[0]}, side=${textureNames[2]}, spot=${textureNames[3]}")
|
||||||
if (textureNames.all { it != "missingno" }) return RubberLogInfo.Key(logger, EnumFacing.Axis.Y, sapSide, textureNames)
|
if (textureNames.all { it != "missingno" }) return RubberLogInfo.Key(logger, Axis.Y, sapSide, textureNames)
|
||||||
} else {
|
} else {
|
||||||
val textureNames = listOf("end", "end", "side").map { blockLoc.first.resolveTextureName(it) }
|
val textureNames = listOf("end", "end", "side").map { blockLoc.first.resolveTextureName(it) }
|
||||||
logger.log(Level.DEBUG, "$logName: end=${textureNames[0]}, side=${textureNames[2]}")
|
logger.log(DEBUG, "$logName: end=${textureNames[0]}, side=${textureNames[2]}")
|
||||||
if (textureNames.all { it != "missingno" })return SimpleColumnInfo.Key(logger, EnumFacing.Axis.Y, textureNames)
|
if (textureNames.all { it != "missingno" })return SimpleColumnInfo.Key(logger, Axis.Y, textureNames)
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,41 +1,36 @@
|
|||||||
package mods.betterfoliage.client.integration
|
package mods.betterfoliage.client.integration
|
||||||
|
|
||||||
import mods.betterfoliage.client.Client
|
import mods.betterfoliage.client.Client
|
||||||
import mods.betterfoliage.client.config.Config
|
import mods.betterfoliage.client.config.BlockConfig
|
||||||
import mods.betterfoliage.loader.Refs
|
import mods.betterfoliage.loader.Refs
|
||||||
import mods.octarinecore.metaprog.allAvailable
|
import mods.octarinecore.metaprog.allAvailable
|
||||||
import net.minecraft.block.Block
|
import net.minecraft.block.BlockState
|
||||||
import net.minecraft.block.BlockTallGrass
|
import net.minecraft.block.Blocks
|
||||||
import net.minecraft.block.state.IBlockState
|
|
||||||
import net.minecraft.client.renderer.BufferBuilder
|
import net.minecraft.client.renderer.BufferBuilder
|
||||||
import net.minecraft.init.Blocks
|
|
||||||
import net.minecraftforge.fml.relauncher.Side
|
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
|
||||||
import org.apache.logging.log4j.Level.INFO
|
import org.apache.logging.log4j.Level.INFO
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Integration for ShadersMod.
|
* Integration for ShadersMod.
|
||||||
*/
|
*/
|
||||||
@SideOnly(Side.CLIENT)
|
|
||||||
object ShadersModIntegration {
|
object ShadersModIntegration {
|
||||||
|
|
||||||
@JvmStatic var isAvailable = allAvailable(Refs.sVertexBuilder, Refs.pushEntity_state, Refs.pushEntity_num, Refs.popEntity)
|
@JvmStatic var isAvailable = allAvailable(Refs.sVertexBuilder, Refs.pushEntity_state, Refs.pushEntity_num, Refs.popEntity)
|
||||||
@JvmStatic val tallGrassEntityData = entityDataFor(Blocks.TALLGRASS.defaultState.withProperty(BlockTallGrass.TYPE, BlockTallGrass.EnumType.GRASS))
|
@JvmStatic val tallGrassEntityData = entityDataFor(Blocks.TALL_GRASS.defaultState)
|
||||||
@JvmStatic val leavesEntityData = entityDataFor(Blocks.LEAVES.defaultState)
|
@JvmStatic val leavesEntityData = entityDataFor(Blocks.OAK_LEAVES.defaultState)
|
||||||
|
|
||||||
fun entityDataFor(blockState: IBlockState) =
|
fun entityDataFor(blockState: BlockState) = 0L
|
||||||
(Block.REGISTRY.getIDForObject(blockState.block).toLong() and 65535) or
|
// (ForgeRegistries.BLOCKS.getIDForObject(blockState.block).toLong() and 65535) or
|
||||||
((blockState.renderType.ordinal.toLong() and 65535) shl 16) or
|
// ((blockState.renderType.ordinal.toLong() and 65535) shl 16) or
|
||||||
(blockState.block.getMetaFromState(blockState).toLong() shl 32)
|
// (blockState.block.getMetaFromState(blockState).toLong() shl 32)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called from transformed ShadersMod code.
|
* Called from transformed ShadersMod code.
|
||||||
* @see mods.betterfoliage.loader.BetterFoliageTransformer
|
* @see mods.betterfoliage.loader.BetterFoliageTransformer
|
||||||
*/
|
*/
|
||||||
@JvmStatic fun getBlockIdOverride(original: Long, blockState: IBlockState): Long {
|
@JvmStatic fun getBlockIdOverride(original: Long, blockState: BlockState): Long {
|
||||||
if (Config.blocks.leavesClasses.matchesClass(blockState.block)) return leavesEntityData
|
if (BlockConfig.leafBlocks.matchesClass(blockState.block)) return leavesEntityData
|
||||||
if (Config.blocks.crops.matchesClass(blockState.block)) return tallGrassEntityData
|
if (BlockConfig.crops.matchesClass(blockState.block)) return tallGrassEntityData
|
||||||
return original
|
return original
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,7 +51,7 @@ object ShadersModIntegration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Quads rendered inside this block will use the given block entity data in shader programs. */
|
/** Quads rendered inside this block will use the given block entity data in shader programs. */
|
||||||
inline fun renderAs(state: IBlockState, renderer: BufferBuilder, enabled: Boolean = true, func: ()->Unit) =
|
inline fun renderAs(state: BlockState, renderer: BufferBuilder, enabled: Boolean = true, func: ()->Unit) =
|
||||||
renderAs(entityDataFor(state), renderer, enabled, func)
|
renderAs(entityDataFor(state), renderer, enabled, func)
|
||||||
|
|
||||||
/** Quads rendered inside this block will behave as tallgrass blocks in shader programs. */
|
/** Quads rendered inside this block will behave as tallgrass blocks in shader programs. */
|
||||||
|
|||||||
@@ -15,18 +15,22 @@ import net.minecraft.util.math.BlockPos
|
|||||||
import net.minecraft.util.math.MathHelper
|
import net.minecraft.util.math.MathHelper
|
||||||
import net.minecraft.world.World
|
import net.minecraft.world.World
|
||||||
import net.minecraftforge.common.MinecraftForge
|
import net.minecraftforge.common.MinecraftForge
|
||||||
|
import net.minecraftforge.event.TickEvent
|
||||||
import net.minecraftforge.event.world.WorldEvent
|
import net.minecraftforge.event.world.WorldEvent
|
||||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
|
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||||
import net.minecraftforge.fml.common.gameevent.TickEvent
|
|
||||||
import net.minecraftforge.fml.relauncher.Side
|
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
|
||||||
import org.lwjgl.opengl.GL11
|
import org.lwjgl.opengl.GL11
|
||||||
import java.lang.Math.*
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.math.abs
|
||||||
|
import kotlin.math.cos
|
||||||
|
import kotlin.math.sin
|
||||||
|
|
||||||
@SideOnly(Side.CLIENT)
|
const val rotationFactor = PI2.toFloat() / 64.0f
|
||||||
class EntityFallingLeavesFX(world: World, pos: BlockPos) :
|
|
||||||
AbstractEntityFX(world, pos.x.toDouble() + 0.5, pos.y.toDouble(), pos.z.toDouble() + 0.5) {
|
class EntityFallingLeavesFX(
|
||||||
|
world: World, pos: BlockPos
|
||||||
|
) : AbstractEntityFX(
|
||||||
|
world, pos.x.toDouble() + 0.5, pos.y.toDouble(), pos.z.toDouble() + 0.5
|
||||||
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic val biomeBrightnessMultiplier = 0.5f
|
@JvmStatic val biomeBrightnessMultiplier = 0.5f
|
||||||
@@ -38,38 +42,40 @@ AbstractEntityFX(world, pos.x.toDouble() + 0.5, pos.y.toDouble(), pos.z.toDouble
|
|||||||
var wasCollided = false
|
var wasCollided = false
|
||||||
|
|
||||||
init {
|
init {
|
||||||
particleMaxAge = MathHelper.floor(random(0.6, 1.0) * Config.fallingLeaves.lifetime * 20.0)
|
maxAge = MathHelper.floor(random(0.6, 1.0) * Config.fallingLeaves.lifetime * 20.0)
|
||||||
motionY = -Config.fallingLeaves.speed
|
motionY = -Config.fallingLeaves.speed
|
||||||
|
|
||||||
particleScale = Config.fallingLeaves.size.toFloat() * 0.1f
|
particleScale = Config.fallingLeaves.size.toFloat() * 0.1f
|
||||||
|
|
||||||
val state = world.getBlockState(pos)
|
val state = world.getBlockState(pos)
|
||||||
val blockColor = Minecraft.getMinecraft().blockColors.colorMultiplier(state, world, pos, 0)
|
|
||||||
val leafInfo = LeafRegistry[state, world, pos]
|
val leafInfo = LeafRegistry[state, world, pos]
|
||||||
|
val blockColor = Minecraft.getInstance().blockColors.getColor(state, world, pos, 0)
|
||||||
if (leafInfo != null) {
|
if (leafInfo != null) {
|
||||||
particleTexture = leafInfo.particleTextures[rand.nextInt(1024)]
|
sprite = leafInfo.particleTextures[rand.nextInt(1024)]
|
||||||
calculateParticleColor(leafInfo.averageColor, blockColor)
|
calculateParticleColor(leafInfo.averageColor, blockColor)
|
||||||
} else {
|
} else {
|
||||||
particleTexture = LeafParticleRegistry["default"][rand.nextInt(1024)]
|
sprite = LeafParticleRegistry["default"][rand.nextInt(1024)]
|
||||||
setColor(blockColor)
|
setColor(blockColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override val isValid: Boolean get() = (particleTexture != null)
|
override val isValid: Boolean get() = (sprite != null)
|
||||||
|
|
||||||
override fun update() {
|
override fun update() {
|
||||||
if (rand.nextFloat() > 0.95f) rotPositive = !rotPositive
|
if (rand.nextFloat() > 0.95f) rotPositive = !rotPositive
|
||||||
if (particleAge > particleMaxAge - 20) particleAlpha = 0.05f * (particleMaxAge - particleAge)
|
if (age > maxAge - 20) particleAlpha = 0.05f * (maxAge - age)
|
||||||
|
|
||||||
if (onGround || wasCollided) {
|
if (onGround || wasCollided) {
|
||||||
velocity.setTo(0.0, 0.0, 0.0)
|
velocity.setTo(0.0, 0.0, 0.0)
|
||||||
if (!wasCollided) {
|
if (!wasCollided) {
|
||||||
particleAge = Math.max(particleAge, particleMaxAge - 20)
|
age = Math.max(age, maxAge - 20)
|
||||||
wasCollided = true
|
wasCollided = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
velocity.setTo(cos[particleRot], 0.0, sin[particleRot]).mul(Config.fallingLeaves.perturb)
|
velocity.setTo(cos[particleRot], 0.0, sin[particleRot]).mul(Config.fallingLeaves.perturb)
|
||||||
.add(LeafWindTracker.current).add(0.0, -1.0, 0.0).mul(Config.fallingLeaves.speed)
|
.add(LeafWindTracker.current).add(0.0, -1.0, 0.0).mul(Config.fallingLeaves.speed)
|
||||||
particleRot = (particleRot + (if (rotPositive) 1 else -1)) and 63
|
particleRot = (particleRot + (if (rotPositive) 1 else -1)) and 63
|
||||||
|
particleAngle = rotationFactor * particleRot.toFloat()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +102,6 @@ AbstractEntityFX(world, pos.x.toDouble() + 0.5, pos.y.toDouble(), pos.z.toDouble
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SideOnly(Side.CLIENT)
|
|
||||||
object LeafWindTracker {
|
object LeafWindTracker {
|
||||||
var random = Random()
|
var random = Random()
|
||||||
val target = Double3.zero
|
val target = Double3.zero
|
||||||
@@ -108,7 +113,7 @@ object LeafWindTracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun changeWind(world: World) {
|
fun changeWind(world: World) {
|
||||||
nextChange = world.worldInfo.worldTime + 120 + random.nextInt(80)
|
nextChange = world.worldInfo.gameTime + 120 + random.nextInt(80)
|
||||||
val direction = PI2 * random.nextDouble()
|
val direction = PI2 * random.nextDouble()
|
||||||
val speed = abs(random.nextGaussian()) * Config.fallingLeaves.windStrength +
|
val speed = abs(random.nextGaussian()) * Config.fallingLeaves.windStrength +
|
||||||
(if (!world.isRaining) 0.0 else abs(random.nextGaussian()) * Config.fallingLeaves.stormStrength)
|
(if (!world.isRaining) 0.0 else abs(random.nextGaussian()) * Config.fallingLeaves.stormStrength)
|
||||||
@@ -117,9 +122,9 @@ object LeafWindTracker {
|
|||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
fun handleWorldTick(event: TickEvent.ClientTickEvent) {
|
fun handleWorldTick(event: TickEvent.ClientTickEvent) {
|
||||||
if (event.phase == TickEvent.Phase.START) Minecraft.getMinecraft().world?.let { world ->
|
if (event.phase == TickEvent.Phase.START) Minecraft.getInstance().world?.let { world ->
|
||||||
// change target wind speed
|
// change target wind speed
|
||||||
if (world.worldInfo.worldTime >= nextChange) changeWind(world)
|
if (world.worldInfo.dayTime >= nextChange) changeWind(world)
|
||||||
|
|
||||||
// change current wind speed
|
// change current wind speed
|
||||||
val changeRate = if (world.isRaining) 0.015 else 0.005
|
val changeRate = if (world.isRaining) 0.015 else 0.005
|
||||||
@@ -132,5 +137,5 @@ object LeafWindTracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
fun handleWorldLoad(event: WorldEvent.Load) { if (event.world.isRemote) changeWind(event.world) }
|
fun handleWorldLoad(event: WorldEvent.Load) { if (event.world.isRemote) changeWind(event.world.world) }
|
||||||
}
|
}
|
||||||
@@ -1,22 +1,21 @@
|
|||||||
package mods.betterfoliage.client.render
|
package mods.betterfoliage.client.render
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
import mods.betterfoliage.BetterFoliage
|
||||||
import mods.betterfoliage.client.Client
|
import mods.betterfoliage.client.Client
|
||||||
import mods.betterfoliage.client.config.Config
|
import mods.betterfoliage.client.config.Config
|
||||||
import mods.octarinecore.client.render.AbstractEntityFX
|
import mods.octarinecore.client.render.AbstractEntityFX
|
||||||
|
import mods.octarinecore.client.resource.Atlas
|
||||||
import mods.octarinecore.client.resource.ResourceHandler
|
import mods.octarinecore.client.resource.ResourceHandler
|
||||||
import mods.octarinecore.common.Double3
|
import mods.octarinecore.common.Double3
|
||||||
import mods.octarinecore.forEachPairIndexed
|
import mods.octarinecore.forEachPairIndexed
|
||||||
import net.minecraft.client.renderer.BufferBuilder
|
import net.minecraft.client.renderer.BufferBuilder
|
||||||
|
import net.minecraft.util.ResourceLocation
|
||||||
import net.minecraft.util.math.BlockPos
|
import net.minecraft.util.math.BlockPos
|
||||||
import net.minecraft.util.math.MathHelper
|
import net.minecraft.util.math.MathHelper
|
||||||
import net.minecraft.world.World
|
import net.minecraft.world.World
|
||||||
import net.minecraftforge.fml.relauncher.Side
|
import org.apache.logging.log4j.Level.DEBUG
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
|
||||||
import org.apache.logging.log4j.Level.INFO
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@SideOnly(Side.CLIENT)
|
|
||||||
class EntityRisingSoulFX(world: World, pos: BlockPos) :
|
class EntityRisingSoulFX(world: World, pos: BlockPos) :
|
||||||
AbstractEntityFX(world, pos.x.toDouble() + 0.5, pos.y.toDouble() + 1.0, pos.z.toDouble() + 0.5) {
|
AbstractEntityFX(world, pos.x.toDouble() + 0.5, pos.y.toDouble() + 1.0, pos.z.toDouble() + 0.5) {
|
||||||
|
|
||||||
@@ -26,14 +25,14 @@ AbstractEntityFX(world, pos.x.toDouble() + 0.5, pos.y.toDouble() + 1.0, pos.z.to
|
|||||||
init {
|
init {
|
||||||
motionY = 0.1
|
motionY = 0.1
|
||||||
particleGravity = 0.0f
|
particleGravity = 0.0f
|
||||||
particleTexture = RisingSoulTextures.headIcons[rand.nextInt(256)]
|
sprite = RisingSoulTextures.headIcons[rand.nextInt(256)]
|
||||||
particleMaxAge = MathHelper.floor((0.6 + 0.4 * rand.nextDouble()) * Config.risingSoul.lifetime * 20.0)
|
maxAge = MathHelper.floor((0.6 + 0.4 * rand.nextDouble()) * Config.risingSoul.lifetime * 20.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val isValid: Boolean get() = true
|
override val isValid: Boolean get() = true
|
||||||
|
|
||||||
override fun update() {
|
override fun update() {
|
||||||
val phase = (initialPhase + particleAge) % 64
|
val phase = (initialPhase + age) % 64
|
||||||
velocity.setTo(cos[phase] * Config.risingSoul.perturb, 0.1, sin[phase] * Config.risingSoul.perturb)
|
velocity.setTo(cos[phase] * Config.risingSoul.perturb, 0.1, sin[phase] * Config.risingSoul.perturb)
|
||||||
|
|
||||||
particleTrail.addFirst(currentPos.copy())
|
particleTrail.addFirst(currentPos.copy())
|
||||||
@@ -43,8 +42,8 @@ AbstractEntityFX(world, pos.x.toDouble() + 0.5, pos.y.toDouble() + 1.0, pos.z.to
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun render(worldRenderer: BufferBuilder, partialTickTime: Float) {
|
override fun render(worldRenderer: BufferBuilder, partialTickTime: Float) {
|
||||||
var alpha = Config.risingSoul.opacity
|
var alpha = Config.risingSoul.opacity.toFloat()
|
||||||
if (particleAge > particleMaxAge - 40) alpha *= (particleMaxAge - particleAge) / 40.0f
|
if (age > maxAge - 40) alpha *= (maxAge - age) / 40.0f
|
||||||
|
|
||||||
renderParticleQuad(worldRenderer, partialTickTime,
|
renderParticleQuad(worldRenderer, partialTickTime,
|
||||||
size = Config.risingSoul.headSize * 0.25,
|
size = Config.risingSoul.headSize * 0.25,
|
||||||
@@ -54,7 +53,7 @@ AbstractEntityFX(world, pos.x.toDouble() + 0.5, pos.y.toDouble() + 1.0, pos.z.to
|
|||||||
var scale = Config.risingSoul.trailSize * 0.25
|
var scale = Config.risingSoul.trailSize * 0.25
|
||||||
particleTrail.forEachPairIndexed { idx, current, previous ->
|
particleTrail.forEachPairIndexed { idx, current, previous ->
|
||||||
scale *= Config.risingSoul.sizeDecay
|
scale *= Config.risingSoul.sizeDecay
|
||||||
alpha *= Config.risingSoul.opacityDecay
|
alpha *= Config.risingSoul.opacityDecay.toFloat()
|
||||||
if (idx % Config.risingSoul.trailDensity == 0) renderParticleQuad(worldRenderer, partialTickTime,
|
if (idx % Config.risingSoul.trailDensity == 0) renderParticleQuad(worldRenderer, partialTickTime,
|
||||||
currentPos = current,
|
currentPos = current,
|
||||||
prevPos = previous,
|
prevPos = previous,
|
||||||
@@ -66,12 +65,11 @@ AbstractEntityFX(world, pos.x.toDouble() + 0.5, pos.y.toDouble() + 1.0, pos.z.to
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SideOnly(Side.CLIENT)
|
object RisingSoulTextures : ResourceHandler(BetterFoliage.MOD_ID, BetterFoliage.modBus, targetAtlas = Atlas.PARTICLES) {
|
||||||
object RisingSoulTextures : ResourceHandler(BetterFoliageMod.MOD_ID) {
|
val headIcons = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "rising_soul_$idx") }
|
||||||
val headIcons = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "blocks/rising_soul_%d")
|
val trackIcon = iconStatic(BetterFoliage.MOD_ID, "soul_track")
|
||||||
val trackIcon = iconStatic(BetterFoliageMod.LEGACY_DOMAIN, "blocks/soul_track")
|
|
||||||
|
|
||||||
override fun afterPreStitch() {
|
override fun afterPreStitch() {
|
||||||
Client.log(INFO, "Registered ${headIcons.num} soul particle textures")
|
Client.log(DEBUG, "Registered ${headIcons.num} soul particle textures")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,8 @@ import mods.betterfoliage.client.config.Config
|
|||||||
import mods.octarinecore.client.render.*
|
import mods.octarinecore.client.render.*
|
||||||
import mods.octarinecore.common.Double3
|
import mods.octarinecore.common.Double3
|
||||||
import mods.octarinecore.exchange
|
import mods.octarinecore.exchange
|
||||||
import net.minecraft.util.EnumFacing.*
|
import net.minecraft.util.Direction
|
||||||
|
import net.minecraft.util.Direction.*
|
||||||
import org.lwjgl.opengl.GL11
|
import org.lwjgl.opengl.GL11
|
||||||
|
|
||||||
/** Weight of the same-side AO values on the outer edges of the 45deg chamfered column faces. */
|
/** Weight of the same-side AO values on the outer edges of the 45deg chamfered column faces. */
|
||||||
@@ -25,10 +26,10 @@ fun Model.columnSide(radius: Double, yBottom: Double, yTop: Double, transform: (
|
|||||||
verticalRectangle(x1 = 0.5 - radius, z1 = 0.5, x2 = 0.5 - halfRadius, z2 = 0.5 - halfRadius, yBottom = yBottom, yTop = yTop)
|
verticalRectangle(x1 = 0.5 - radius, z1 = 0.5, x2 = 0.5 - halfRadius, z2 = 0.5 - halfRadius, yBottom = yBottom, yTop = yTop)
|
||||||
.clampUV(minU = 0.5 - radius)
|
.clampUV(minU = 0.5 - radius)
|
||||||
.setAoShader(
|
.setAoShader(
|
||||||
faceOrientedAuto(overrideFace = SOUTH, corner = cornerInterpolate(Axis.Y, chamferAffinity, Config.roundLogs.dimming))
|
faceOrientedAuto(overrideFace = SOUTH, corner = cornerInterpolate(Axis.Y, chamferAffinity, Config.roundLogs.dimming.toFloat()))
|
||||||
)
|
)
|
||||||
.setAoShader(
|
.setAoShader(
|
||||||
faceOrientedAuto(overrideFace = SOUTH, corner = cornerInterpolate(Axis.Y, 0.5f, Config.roundLogs.dimming)),
|
faceOrientedAuto(overrideFace = SOUTH, corner = cornerInterpolate(Axis.Y, 0.5f, Config.roundLogs.dimming.toFloat())),
|
||||||
predicate = { v, vi -> vi == 1 || vi == 2}
|
predicate = { v, vi -> vi == 1 || vi == 2}
|
||||||
)
|
)
|
||||||
).forEach { transform(it.setFlatShader(FaceFlat(SOUTH))).add() }
|
).forEach { transform(it.setFlatShader(FaceFlat(SOUTH))).add() }
|
||||||
@@ -37,9 +38,9 @@ fun Model.columnSide(radius: Double, yBottom: Double, yTop: Double, transform: (
|
|||||||
verticalRectangle(x1 = 0.5 - halfRadius, z1 = 0.5 - halfRadius, x2 = 0.5, z2 = 0.5 - radius, yBottom = yBottom, yTop = yTop)
|
verticalRectangle(x1 = 0.5 - halfRadius, z1 = 0.5 - halfRadius, x2 = 0.5, z2 = 0.5 - radius, yBottom = yBottom, yTop = yTop)
|
||||||
.clampUV(maxU = radius - 0.5)
|
.clampUV(maxU = radius - 0.5)
|
||||||
.setAoShader(
|
.setAoShader(
|
||||||
faceOrientedAuto(overrideFace = EAST, corner = cornerInterpolate(Axis.Y, chamferAffinity, Config.roundLogs.dimming)))
|
faceOrientedAuto(overrideFace = EAST, corner = cornerInterpolate(Axis.Y, chamferAffinity, Config.roundLogs.dimming.toFloat())))
|
||||||
.setAoShader(
|
.setAoShader(
|
||||||
faceOrientedAuto(overrideFace = EAST, corner = cornerInterpolate(Axis.Y, 0.5f, Config.roundLogs.dimming)),
|
faceOrientedAuto(overrideFace = EAST, corner = cornerInterpolate(Axis.Y, 0.5f, Config.roundLogs.dimming.toFloat())),
|
||||||
predicate = { v, vi -> vi == 0 || vi == 3}
|
predicate = { v, vi -> vi == 0 || vi == 3}
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|||||||
@@ -1,42 +1,46 @@
|
|||||||
package mods.betterfoliage.client.render
|
package mods.betterfoliage.client.render
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
import mods.betterfoliage.BetterFoliage
|
||||||
import mods.betterfoliage.client.Client
|
import mods.betterfoliage.client.Client
|
||||||
|
import mods.betterfoliage.client.config.BlockConfig
|
||||||
import mods.betterfoliage.client.config.Config
|
import mods.betterfoliage.client.config.Config
|
||||||
import mods.betterfoliage.client.integration.ShadersModIntegration
|
import mods.betterfoliage.client.integration.ShadersModIntegration
|
||||||
import mods.octarinecore.client.render.*
|
import mods.octarinecore.client.render.*
|
||||||
import mods.octarinecore.common.Int3
|
import mods.octarinecore.common.Int3
|
||||||
import mods.octarinecore.common.Rotation
|
import mods.octarinecore.common.Rotation
|
||||||
|
import net.minecraft.block.Block
|
||||||
import net.minecraft.block.material.Material
|
import net.minecraft.block.material.Material
|
||||||
import net.minecraft.client.renderer.BlockRendererDispatcher
|
import net.minecraft.client.renderer.BlockRendererDispatcher
|
||||||
import net.minecraft.client.renderer.BufferBuilder
|
import net.minecraft.client.renderer.BufferBuilder
|
||||||
|
import net.minecraft.tags.BlockTags
|
||||||
import net.minecraft.util.BlockRenderLayer
|
import net.minecraft.util.BlockRenderLayer
|
||||||
import net.minecraftforge.fml.relauncher.Side
|
import net.minecraft.util.ResourceLocation
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
import net.minecraft.world.biome.Biome
|
||||||
import org.apache.logging.log4j.Level.INFO
|
import net.minecraftforge.client.model.data.IModelData
|
||||||
|
import org.apache.logging.log4j.Level.DEBUG
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
@SideOnly(Side.CLIENT)
|
class RenderAlgae : AbstractBlockRenderingHandler(BetterFoliage.MOD_ID, BetterFoliage.modBus) {
|
||||||
class RenderAlgae : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|
||||||
|
|
||||||
val noise = simplexNoise()
|
val noise = simplexNoise()
|
||||||
|
|
||||||
val algaeIcons = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "blocks/better_algae_%d")
|
val algaeIcons = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_algae_$idx") }
|
||||||
val algaeModels = modelSet(64, RenderGrass.grassTopQuads(Config.algae.heightMin, Config.algae.heightMax))
|
val algaeModels = modelSet(64) { idx -> RenderGrass.grassTopQuads(Config.algae.heightMin, Config.algae.heightMax)(idx) }
|
||||||
|
|
||||||
override fun afterPreStitch() {
|
override fun afterPreStitch() {
|
||||||
Client.log(INFO, "Registered ${algaeIcons.num} algae textures")
|
Client.log(DEBUG, "Registered ${algaeIcons.num} algae textures")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isEligible(ctx: BlockContext) =
|
override fun isEligible(ctx: BlockContext) =
|
||||||
Config.enabled && Config.algae.enabled &&
|
Config.enabled && Config.algae.enabled &&
|
||||||
ctx.blockState(up2).material == Material.WATER &&
|
ctx.blockState(up2).material == Material.WATER &&
|
||||||
ctx.blockState(up1).material == Material.WATER &&
|
ctx.blockState(up1).material == Material.WATER &&
|
||||||
Config.blocks.dirt.matchesClass(ctx.block) &&
|
BlockTags.DIRT_LIKE.contains(ctx.block) &&
|
||||||
ctx.biomeId in Config.algae.biomes &&
|
ctx.biome.category.let { it == Biome.Category.OCEAN || it == Biome.Category.BEACH || it == Biome.Category.RIVER } &&
|
||||||
noise[ctx.pos] < Config.algae.population
|
noise[ctx.pos] < Config.algae.population
|
||||||
|
|
||||||
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: BufferBuilder, layer: BlockRenderLayer): Boolean {
|
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: BufferBuilder, random: Random, modelData: IModelData, layer: BlockRenderLayer): Boolean {
|
||||||
val baseRender = renderWorldBlockBase(ctx, dispatcher, renderer, layer)
|
val baseRender = renderWorldBlockBase(ctx, dispatcher, renderer, random, modelData, layer)
|
||||||
if (!layer.isCutout) return baseRender
|
if (!layer.isCutout) return baseRender
|
||||||
|
|
||||||
modelRenderer.updateShading(Int3.zero, allFaces)
|
modelRenderer.updateShading(Int3.zero, allFaces)
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package mods.betterfoliage.client.render
|
package mods.betterfoliage.client.render
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
import mods.betterfoliage.BetterFoliage
|
||||||
import mods.betterfoliage.client.Client
|
import mods.betterfoliage.client.Client
|
||||||
|
import mods.betterfoliage.client.config.BlockConfig
|
||||||
import mods.betterfoliage.client.config.Config
|
import mods.betterfoliage.client.config.Config
|
||||||
import mods.betterfoliage.client.render.column.ColumnTextureInfo
|
import mods.betterfoliage.client.render.column.ColumnTextureInfo
|
||||||
import mods.betterfoliage.client.render.column.SimpleColumnInfo
|
import mods.betterfoliage.client.render.column.SimpleColumnInfo
|
||||||
@@ -11,33 +12,32 @@ import mods.octarinecore.common.Int3
|
|||||||
import mods.octarinecore.common.Rotation
|
import mods.octarinecore.common.Rotation
|
||||||
import mods.octarinecore.common.config.ModelTextureList
|
import mods.octarinecore.common.config.ModelTextureList
|
||||||
import mods.octarinecore.common.config.SimpleBlockMatcher
|
import mods.octarinecore.common.config.SimpleBlockMatcher
|
||||||
import net.minecraft.block.BlockCactus
|
import net.minecraft.block.BlockState
|
||||||
import net.minecraft.block.state.IBlockState
|
import net.minecraft.block.CactusBlock
|
||||||
import net.minecraft.client.renderer.BlockRendererDispatcher
|
import net.minecraft.client.renderer.BlockRendererDispatcher
|
||||||
import net.minecraft.client.renderer.BufferBuilder
|
import net.minecraft.client.renderer.BufferBuilder
|
||||||
import net.minecraft.util.BlockRenderLayer
|
import net.minecraft.util.BlockRenderLayer
|
||||||
import net.minecraft.util.EnumFacing.*
|
import net.minecraft.util.Direction.*
|
||||||
import net.minecraftforge.common.MinecraftForge
|
import net.minecraft.util.ResourceLocation
|
||||||
import net.minecraftforge.fml.relauncher.Side
|
import net.minecraftforge.client.model.data.IModelData
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
import org.apache.logging.log4j.Level.DEBUG
|
||||||
import org.apache.logging.log4j.Level
|
import java.util.*
|
||||||
|
|
||||||
object StandardCactusRegistry : ModelRenderRegistryConfigurable<ColumnTextureInfo>() {
|
object StandardCactusRegistry : ModelRenderRegistryConfigurable<ColumnTextureInfo>() {
|
||||||
override val logger = BetterFoliageMod.logDetail
|
override val logger = BetterFoliage.logDetail
|
||||||
override val matchClasses = SimpleBlockMatcher(BlockCactus::class.java)
|
override val matchClasses = SimpleBlockMatcher(CactusBlock::class.java)
|
||||||
override val modelTextures = listOf(ModelTextureList("block/cactus", "top", "bottom", "side"))
|
override val modelTextures = listOf(ModelTextureList("block/cactus", "top", "bottom", "side"))
|
||||||
override fun processModel(state: IBlockState, textures: List<String>) = SimpleColumnInfo.Key(logger, Axis.Y, textures)
|
override fun processModel(state: BlockState, textures: List<String>) = SimpleColumnInfo.Key(logger, Axis.Y, textures)
|
||||||
init { MinecraftForge.EVENT_BUS.register(this) }
|
init { BetterFoliage.modBus.register(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@SideOnly(Side.CLIENT)
|
class RenderCactus : AbstractBlockRenderingHandler(BetterFoliage.MOD_ID, BetterFoliage.modBus) {
|
||||||
class RenderCactus : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|
||||||
|
|
||||||
val cactusStemRadius = 0.4375
|
val cactusStemRadius = 0.4375
|
||||||
val cactusArmRotation = listOf(NORTH, SOUTH, EAST, WEST).map { Rotation.rot90[it.ordinal] }
|
val cactusArmRotation = listOf(NORTH, SOUTH, EAST, WEST).map { Rotation.rot90[it.ordinal] }
|
||||||
|
|
||||||
val iconCross = iconStatic(BetterFoliageMod.LEGACY_DOMAIN, "blocks/better_cactus")
|
val iconCross = iconStatic(ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_cactus"))
|
||||||
val iconArm = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "blocks/better_cactus_arm_%d")
|
val iconArm = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_cactus_arm_$idx") }
|
||||||
|
|
||||||
val modelStem = model {
|
val modelStem = model {
|
||||||
horizontalRectangle(x1 = -cactusStemRadius, x2 = cactusStemRadius, z1 = -cactusStemRadius, z2 = cactusStemRadius, y = 0.5)
|
horizontalRectangle(x1 = -cactusStemRadius, x2 = cactusStemRadius, z1 = -cactusStemRadius, z2 = cactusStemRadius, y = 0.5)
|
||||||
@@ -68,20 +68,20 @@ class RenderCactus : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun afterPreStitch() {
|
override fun afterPreStitch() {
|
||||||
Client.log(Level.INFO, "Registered ${iconArm.num} cactus arm textures")
|
Client.log(DEBUG, "Registered ${iconArm.num} cactus arm textures")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isEligible(ctx: BlockContext): Boolean =
|
override fun isEligible(ctx: BlockContext): Boolean =
|
||||||
Config.enabled && Config.cactus.enabled &&
|
Config.enabled && Config.cactus.enabled &&
|
||||||
Config.blocks.cactus.matchesClass(ctx.block)
|
StandardCactusRegistry[ctx] != null
|
||||||
|
|
||||||
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: BufferBuilder, layer: BlockRenderLayer): Boolean {
|
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: BufferBuilder, random: Random, modelData: IModelData, layer: BlockRenderLayer): Boolean {
|
||||||
// render the whole block on the cutout layer
|
// render the whole block on the cutout layer
|
||||||
if (!layer.isCutout) return false
|
if (!layer.isCutout) return false
|
||||||
|
|
||||||
// get AO data
|
// get AO data
|
||||||
modelRenderer.updateShading(Int3.zero, allFaces)
|
modelRenderer.updateShading(Int3.zero, allFaces)
|
||||||
val icons = StandardCactusRegistry[ctx] ?: return renderWorldBlockBase(ctx, dispatcher, renderer, null)
|
val icons = StandardCactusRegistry[ctx] ?: return renderWorldBlockBase(ctx, dispatcher, renderer, random, modelData, null)
|
||||||
|
|
||||||
modelRenderer.render(
|
modelRenderer.render(
|
||||||
renderer,
|
renderer,
|
||||||
|
|||||||
@@ -1,36 +1,36 @@
|
|||||||
package mods.betterfoliage.client.render
|
package mods.betterfoliage.client.render
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
import mods.betterfoliage.BetterFoliage
|
||||||
|
import mods.betterfoliage.client.config.BlockConfig
|
||||||
import mods.betterfoliage.client.config.Config
|
import mods.betterfoliage.client.config.Config
|
||||||
|
import mods.betterfoliage.client.texture.GrassRegistry
|
||||||
import mods.octarinecore.client.render.AbstractBlockRenderingHandler
|
import mods.octarinecore.client.render.AbstractBlockRenderingHandler
|
||||||
import mods.octarinecore.client.render.BlockContext
|
import mods.octarinecore.client.render.BlockContext
|
||||||
import mods.octarinecore.client.render.withOffset
|
import mods.octarinecore.client.render.offset
|
||||||
import mods.octarinecore.common.Int3
|
import mods.octarinecore.common.Int3
|
||||||
import mods.octarinecore.common.forgeDirsHorizontal
|
import mods.octarinecore.common.forgeDirsHorizontal
|
||||||
import mods.octarinecore.common.offset
|
import mods.octarinecore.common.offset
|
||||||
|
import net.minecraft.block.Block
|
||||||
import net.minecraft.client.renderer.BlockRendererDispatcher
|
import net.minecraft.client.renderer.BlockRendererDispatcher
|
||||||
import net.minecraft.client.renderer.BufferBuilder
|
import net.minecraft.client.renderer.BufferBuilder
|
||||||
|
import net.minecraft.tags.BlockTags
|
||||||
import net.minecraft.util.BlockRenderLayer
|
import net.minecraft.util.BlockRenderLayer
|
||||||
import net.minecraftforge.fml.relauncher.Side
|
import net.minecraftforge.client.model.data.IModelData
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
import java.util.*
|
||||||
|
|
||||||
@SideOnly(Side.CLIENT)
|
class RenderConnectedGrass : AbstractBlockRenderingHandler(BetterFoliage.MOD_ID, BetterFoliage.modBus) {
|
||||||
class RenderConnectedGrass : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|
||||||
override fun isEligible(ctx: BlockContext) =
|
override fun isEligible(ctx: BlockContext) =
|
||||||
Config.enabled && Config.connectedGrass.enabled &&
|
Config.enabled && Config.connectedGrass.enabled &&
|
||||||
Config.blocks.dirt.matchesClass(ctx.block) &&
|
BlockTags.DIRT_LIKE.contains(ctx.block) &&
|
||||||
Config.blocks.grassClasses.matchesClass(ctx.block(up1)) &&
|
GrassRegistry[ctx, up1] != null &&
|
||||||
(Config.connectedGrass.snowEnabled || !ctx.blockState(up2).isSnow)
|
(Config.connectedGrass.snowEnabled || !ctx.blockState(up2).isSnow)
|
||||||
|
|
||||||
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: BufferBuilder, layer: BlockRenderLayer): Boolean {
|
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: BufferBuilder, random: Random, modelData: IModelData, layer: BlockRenderLayer): Boolean {
|
||||||
// if the block sides are not visible anyway, render normally
|
// if the block sides are not visible anyway, render normally
|
||||||
if (forgeDirsHorizontal.all { ctx.blockState(it.offset).isOpaqueCube }) return renderWorldBlockBase(ctx, dispatcher, renderer, layer)
|
if (forgeDirsHorizontal.none { ctx.shouldSideBeRendered(it) }) return renderWorldBlockBase(ctx, dispatcher, renderer, random, modelData, layer)
|
||||||
|
|
||||||
if (ctx.isSurroundedBy { it.isOpaqueCube } ) return false
|
return ctx.offset(Int3.zero, up1).offset(up1, up2).let { offsetCtx ->
|
||||||
return ctx.withOffset(Int3.zero, up1) {
|
renderWorldBlockBase(offsetCtx, dispatcher, renderer, random, modelData, layer)
|
||||||
ctx.withOffset(up1, up2) {
|
|
||||||
renderWorldBlockBase(ctx, dispatcher, renderer, layer)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,36 +1,39 @@
|
|||||||
package mods.betterfoliage.client.render
|
package mods.betterfoliage.client.render
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
import mods.betterfoliage.BetterFoliage
|
||||||
|
import mods.betterfoliage.client.config.BlockConfig
|
||||||
import mods.betterfoliage.client.config.Config
|
import mods.betterfoliage.client.config.Config
|
||||||
|
import mods.betterfoliage.client.texture.GrassRegistry
|
||||||
import mods.octarinecore.client.render.AbstractBlockRenderingHandler
|
import mods.octarinecore.client.render.AbstractBlockRenderingHandler
|
||||||
import mods.octarinecore.client.render.BlockContext
|
import mods.octarinecore.client.render.BlockContext
|
||||||
import mods.octarinecore.client.render.withOffset
|
import mods.octarinecore.client.render.offset
|
||||||
import mods.octarinecore.common.Int3
|
import mods.octarinecore.common.Int3
|
||||||
import mods.octarinecore.common.offset
|
import mods.octarinecore.common.offset
|
||||||
|
import net.minecraft.block.Block
|
||||||
import net.minecraft.client.renderer.BlockRendererDispatcher
|
import net.minecraft.client.renderer.BlockRendererDispatcher
|
||||||
import net.minecraft.client.renderer.BufferBuilder
|
import net.minecraft.client.renderer.BufferBuilder
|
||||||
|
import net.minecraft.tags.BlockTags
|
||||||
import net.minecraft.util.BlockRenderLayer
|
import net.minecraft.util.BlockRenderLayer
|
||||||
import net.minecraft.util.EnumFacing.*
|
import net.minecraft.util.Direction.*
|
||||||
import net.minecraftforge.fml.relauncher.Side
|
import net.minecraftforge.client.model.data.IModelData
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
import java.util.*
|
||||||
|
|
||||||
@SideOnly(Side.CLIENT)
|
class RenderConnectedGrassLog : AbstractBlockRenderingHandler(BetterFoliage.MOD_ID, BetterFoliage.modBus) {
|
||||||
class RenderConnectedGrassLog : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|
||||||
|
|
||||||
val grassCheckDirs = listOf(EAST, WEST, NORTH, SOUTH)
|
val grassCheckDirs = listOf(EAST, WEST, NORTH, SOUTH)
|
||||||
|
|
||||||
override fun isEligible(ctx: BlockContext) =
|
override fun isEligible(ctx: BlockContext) =
|
||||||
Config.enabled && Config.roundLogs.enabled && Config.roundLogs.connectGrass &&
|
Config.enabled && Config.roundLogs.enabled && Config.roundLogs.connectGrass &&
|
||||||
Config.blocks.dirt.matchesClass(ctx.block) &&
|
BlockTags.DIRT_LIKE.contains(ctx.block) &&
|
||||||
Config.blocks.logClasses.matchesClass(ctx.block(up1))
|
LogRegistry[ctx, up1] != null
|
||||||
|
|
||||||
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: BufferBuilder, layer: BlockRenderLayer): Boolean {
|
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: BufferBuilder, random: Random, modelData: IModelData, layer: BlockRenderLayer): Boolean {
|
||||||
val grassDir = grassCheckDirs.find {
|
val grassDir = grassCheckDirs.find {
|
||||||
Config.blocks.grassClasses.matchesClass(ctx.block(it.offset))
|
GrassRegistry[ctx, it.offset] != null
|
||||||
} ?: return renderWorldBlockBase(ctx, dispatcher, renderer, layer)
|
} ?: return renderWorldBlockBase(ctx, dispatcher, renderer, random, modelData, layer)
|
||||||
|
|
||||||
return ctx.withOffset(Int3.zero, grassDir.offset) {
|
return ctx.offset(Int3.zero, grassDir.offset).let { offsetCtx ->
|
||||||
renderWorldBlockBase(ctx, dispatcher, renderer, layer)
|
renderWorldBlockBase(offsetCtx, dispatcher, renderer, random, modelData, layer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
package mods.betterfoliage.client.render
|
package mods.betterfoliage.client.render
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
import mods.betterfoliage.BetterFoliage
|
||||||
import mods.betterfoliage.client.Client
|
import mods.betterfoliage.client.Client
|
||||||
|
import mods.betterfoliage.client.config.BlockConfig
|
||||||
import mods.betterfoliage.client.config.Config
|
import mods.betterfoliage.client.config.Config
|
||||||
import mods.octarinecore.client.render.*
|
import mods.octarinecore.client.render.*
|
||||||
import mods.octarinecore.common.Int3
|
import mods.octarinecore.common.Int3
|
||||||
@@ -12,19 +13,22 @@ import net.minecraft.block.material.Material
|
|||||||
import net.minecraft.client.renderer.BlockRendererDispatcher
|
import net.minecraft.client.renderer.BlockRendererDispatcher
|
||||||
import net.minecraft.client.renderer.BufferBuilder
|
import net.minecraft.client.renderer.BufferBuilder
|
||||||
import net.minecraft.util.BlockRenderLayer
|
import net.minecraft.util.BlockRenderLayer
|
||||||
import net.minecraft.util.EnumFacing.Axis
|
import net.minecraft.util.Direction.Axis
|
||||||
import net.minecraft.util.EnumFacing.UP
|
import net.minecraft.util.Direction.UP
|
||||||
import net.minecraftforge.fml.relauncher.Side
|
import net.minecraft.util.ResourceLocation
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
import net.minecraft.world.biome.Biome
|
||||||
import org.apache.logging.log4j.Level.INFO
|
import net.minecraftforge.api.distmarker.Dist
|
||||||
|
import net.minecraftforge.client.model.data.IModelData
|
||||||
|
import net.minecraftforge.fml.common.Mod
|
||||||
|
import org.apache.logging.log4j.Level.DEBUG
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
@SideOnly(Side.CLIENT)
|
class RenderCoral : AbstractBlockRenderingHandler(BetterFoliage.MOD_ID, BetterFoliage.modBus) {
|
||||||
class RenderCoral : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|
||||||
|
|
||||||
val noise = simplexNoise()
|
val noise = simplexNoise()
|
||||||
|
|
||||||
val coralIcons = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "blocks/better_coral_%d")
|
val coralIcons = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_coral_$idx") }
|
||||||
val crustIcons = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "blocks/better_crust_%d")
|
val crustIcons = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_crust_$idx") }
|
||||||
val coralModels = modelSet(64) { modelIdx ->
|
val coralModels = modelSet(64) { modelIdx ->
|
||||||
verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yBottom = 0.0, yTop = 1.0)
|
verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yBottom = 0.0, yTop = 1.0)
|
||||||
.scale(Config.coral.size).move(0.5 to UP)
|
.scale(Config.coral.size).move(0.5 to UP)
|
||||||
@@ -41,26 +45,26 @@ class RenderCoral : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun afterPreStitch() {
|
override fun afterPreStitch() {
|
||||||
Client.log(INFO, "Registered ${coralIcons.num} coral textures")
|
Client.log(DEBUG, "Registered ${coralIcons.num} coral textures")
|
||||||
Client.log(INFO, "Registered ${crustIcons.num} coral crust textures")
|
Client.log(DEBUG, "Registered ${crustIcons.num} coral crust textures")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isEligible(ctx: BlockContext) =
|
override fun isEligible(ctx: BlockContext) =
|
||||||
Config.enabled && Config.coral.enabled &&
|
Config.enabled && Config.coral.enabled &&
|
||||||
(ctx.blockState(up2).material == Material.WATER || Config.coral.shallowWater) &&
|
(ctx.blockState(up2).material == Material.WATER || Config.coral.shallowWater) &&
|
||||||
ctx.blockState(up1).material == Material.WATER &&
|
ctx.blockState(up1).material == Material.WATER &&
|
||||||
Config.blocks.sand.matchesClass(ctx.block) &&
|
BlockConfig.sand.matchesClass(ctx.block) &&
|
||||||
ctx.biomeId in Config.coral.biomes &&
|
ctx.biome.category.let { it == Biome.Category.OCEAN || it == Biome.Category.BEACH } &&
|
||||||
noise[ctx.pos] < Config.coral.population
|
noise[ctx.pos] < Config.coral.population
|
||||||
|
|
||||||
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: BufferBuilder, layer: BlockRenderLayer): Boolean {
|
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: BufferBuilder, random: Random, modelData: IModelData, layer: BlockRenderLayer): Boolean {
|
||||||
val baseRender = renderWorldBlockBase(ctx, dispatcher, renderer, layer)
|
val baseRender = renderWorldBlockBase(ctx, dispatcher, renderer, random, modelData, layer)
|
||||||
if (!layer.isCutout) return baseRender
|
if (!layer.isCutout) return baseRender
|
||||||
|
|
||||||
modelRenderer.updateShading(Int3.zero, allFaces)
|
modelRenderer.updateShading(Int3.zero, allFaces)
|
||||||
|
|
||||||
forgeDirs.forEachIndexed { idx, face ->
|
forgeDirs.forEachIndexed { idx, face ->
|
||||||
if (!ctx.blockState(forgeDirOffsets[idx]).isOpaqueCube && blockContext.random(idx) < Config.coral.chance) {
|
if (!ctx.isNormalCube(forgeDirOffsets[idx]) && blockContext.random(idx) < Config.coral.chance) {
|
||||||
var variation = blockContext.random(6)
|
var variation = blockContext.random(6)
|
||||||
modelRenderer.render(
|
modelRenderer.render(
|
||||||
renderer,
|
renderer,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package mods.betterfoliage.client.render
|
package mods.betterfoliage.client.render
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
import mods.betterfoliage.BetterFoliage
|
||||||
import mods.betterfoliage.client.Client
|
import mods.betterfoliage.client.Client
|
||||||
import mods.betterfoliage.client.config.Config
|
import mods.betterfoliage.client.config.Config
|
||||||
import mods.betterfoliage.client.integration.OptifineCustomColors
|
import mods.betterfoliage.client.integration.OptifineCustomColors
|
||||||
@@ -9,17 +9,19 @@ import mods.betterfoliage.client.texture.GrassRegistry
|
|||||||
import mods.octarinecore.client.render.*
|
import mods.octarinecore.client.render.*
|
||||||
import mods.octarinecore.common.*
|
import mods.octarinecore.common.*
|
||||||
import mods.octarinecore.random
|
import mods.octarinecore.random
|
||||||
|
import net.minecraft.block.Block
|
||||||
import net.minecraft.client.renderer.BlockRendererDispatcher
|
import net.minecraft.client.renderer.BlockRendererDispatcher
|
||||||
import net.minecraft.client.renderer.BufferBuilder
|
import net.minecraft.client.renderer.BufferBuilder
|
||||||
|
import net.minecraft.tags.BlockTags
|
||||||
import net.minecraft.util.BlockRenderLayer
|
import net.minecraft.util.BlockRenderLayer
|
||||||
import net.minecraft.util.EnumFacing.Axis
|
import net.minecraft.util.Direction.Axis
|
||||||
import net.minecraft.util.EnumFacing.UP
|
import net.minecraft.util.Direction.*
|
||||||
import net.minecraftforge.fml.relauncher.Side
|
import net.minecraft.util.ResourceLocation
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
import net.minecraftforge.client.model.data.IModelData
|
||||||
import org.apache.logging.log4j.Level.INFO
|
import org.apache.logging.log4j.Level.DEBUG
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
@SideOnly(Side.CLIENT)
|
class RenderGrass : AbstractBlockRenderingHandler(BetterFoliage.MOD_ID, BetterFoliage.modBus) {
|
||||||
class RenderGrass : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic fun grassTopQuads(heightMin: Double, heightMax: Double): Model.(Int)->Unit = { modelIdx ->
|
@JvmStatic fun grassTopQuads(heightMin: Double, heightMax: Double): Model.(Int)->Unit = { modelIdx ->
|
||||||
@@ -34,16 +36,16 @@ class RenderGrass : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|||||||
|
|
||||||
val noise = simplexNoise()
|
val noise = simplexNoise()
|
||||||
|
|
||||||
val normalIcons = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "blocks/better_grass_long_%d")
|
val normalIcons = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_grass_long_$idx") }
|
||||||
val snowedIcons = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "blocks/better_grass_snowed_%d")
|
val snowedIcons = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_grass_snowed_$idx") }
|
||||||
val normalGenIcon = iconStatic(Client.genGrass.generatedResource("minecraft:blocks/tallgrass", "snowed" to false))
|
val normalGenIcon = iconStatic { Client.genGrass.register(texture = "minecraft:blocks/tallgrass", isSnowed = false) }
|
||||||
val snowedGenIcon = iconStatic(Client.genGrass.generatedResource("minecraft:blocks/tallgrass", "snowed" to true))
|
val snowedGenIcon = iconStatic { Client.genGrass.register(texture = "minecraft:blocks/tallgrass", isSnowed = true) }
|
||||||
|
|
||||||
val grassModels = modelSet(64, grassTopQuads(Config.shortGrass.heightMin, Config.shortGrass.heightMax))
|
val grassModels = modelSet(64) { idx -> grassTopQuads(Config.shortGrass.heightMin, Config.shortGrass.heightMax)(idx) }
|
||||||
|
|
||||||
override fun afterPreStitch() {
|
override fun afterPreStitch() {
|
||||||
Client.log(INFO, "Registered ${normalIcons.num} grass textures")
|
Client.log(DEBUG, "Registered ${normalIcons.num} grass textures")
|
||||||
Client.log(INFO, "Registered ${snowedIcons.num} snowed grass textures")
|
Client.log(DEBUG, "Registered ${snowedIcons.num} snowed grass textures")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isEligible(ctx: BlockContext) =
|
override fun isEligible(ctx: BlockContext) =
|
||||||
@@ -51,14 +53,11 @@ class RenderGrass : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|||||||
(Config.shortGrass.grassEnabled || Config.connectedGrass.enabled) &&
|
(Config.shortGrass.grassEnabled || Config.connectedGrass.enabled) &&
|
||||||
GrassRegistry[ctx] != null
|
GrassRegistry[ctx] != null
|
||||||
|
|
||||||
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: BufferBuilder, layer: BlockRenderLayer): Boolean {
|
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: BufferBuilder, random: Random, modelData: IModelData, layer: BlockRenderLayer): Boolean {
|
||||||
// render the whole block on the cutout layer
|
// render the whole block on the cutout layer
|
||||||
if (!layer.isCutout) return false
|
if (!layer.isCutout) return false
|
||||||
|
|
||||||
val isConnected = ctx.block(down1).let {
|
val isConnected = BlockTags.DIRT_LIKE.contains(ctx.block(down1)) || GrassRegistry[ctx, down1] != null
|
||||||
Config.blocks.dirt.matchesClass(it) ||
|
|
||||||
Config.blocks.grassClasses.matchesClass(it)
|
|
||||||
}
|
|
||||||
val isSnowed = ctx.blockState(up1).isSnow
|
val isSnowed = ctx.blockState(up1).isSnow
|
||||||
val connectedGrass = isConnected && Config.connectedGrass.enabled && (!isSnowed || Config.connectedGrass.snowEnabled)
|
val connectedGrass = isConnected && Config.connectedGrass.enabled && (!isSnowed || Config.connectedGrass.snowEnabled)
|
||||||
|
|
||||||
@@ -66,7 +65,7 @@ class RenderGrass : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|||||||
if (grass == null) {
|
if (grass == null) {
|
||||||
// shouldn't happen
|
// shouldn't happen
|
||||||
Client.logRenderError(ctx.blockState(Int3.zero), ctx.pos)
|
Client.logRenderError(ctx.blockState(Int3.zero), ctx.pos)
|
||||||
return renderWorldBlockBase(ctx, dispatcher, renderer, null)
|
return renderWorldBlockBase(ctx, dispatcher, renderer, random, modelData, null)
|
||||||
}
|
}
|
||||||
val blockColor = OptifineCustomColors.getBlockColor(ctx)
|
val blockColor = OptifineCustomColors.getBlockColor(ctx)
|
||||||
|
|
||||||
@@ -75,14 +74,14 @@ class RenderGrass : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|||||||
modelRenderer.updateShading(Int3.zero, allFaces)
|
modelRenderer.updateShading(Int3.zero, allFaces)
|
||||||
|
|
||||||
// check occlusion
|
// check occlusion
|
||||||
val isHidden = forgeDirs.map { ctx.blockState(it.offset).isOpaqueCube }
|
val isVisible = forgeDirs.map { ctx.shouldSideBeRendered(it) }
|
||||||
|
|
||||||
// render full grass block
|
// render full grass block
|
||||||
ShadersModIntegration.renderAs(ctx.blockState(Int3.zero), renderer) {
|
ShadersModIntegration.renderAs(ctx.blockState(Int3.zero), renderer) {
|
||||||
modelRenderer.render(
|
modelRenderer.render(
|
||||||
renderer,
|
renderer,
|
||||||
fullCube,
|
fullCube,
|
||||||
quadFilter = { qi, _ -> !isHidden[qi] },
|
quadFilter = { qi, _ -> isVisible[qi] },
|
||||||
icon = { _, _, _ -> grass.grassTopTexture },
|
icon = { _, _, _ -> grass.grassTopTexture },
|
||||||
postProcess = { ctx, _, _, _, _ ->
|
postProcess = { ctx, _, _, _, _ ->
|
||||||
rotateUV(2)
|
rotateUV(2)
|
||||||
@@ -93,7 +92,7 @@ class RenderGrass : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
renderWorldBlockBase(ctx, dispatcher, renderer, null)
|
renderWorldBlockBase(ctx, dispatcher, renderer, random, modelData, null)
|
||||||
|
|
||||||
// get AO data only for block top
|
// get AO data only for block top
|
||||||
modelRenderer.updateShading(Int3.zero, topOnly)
|
modelRenderer.updateShading(Int3.zero, topOnly)
|
||||||
@@ -101,7 +100,7 @@ class RenderGrass : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|||||||
|
|
||||||
if (!Config.shortGrass.grassEnabled) return true
|
if (!Config.shortGrass.grassEnabled) return true
|
||||||
if (isSnowed && !Config.shortGrass.snowEnabled) return true
|
if (isSnowed && !Config.shortGrass.snowEnabled) return true
|
||||||
if (ctx.blockState(up1).isOpaqueCube) return true
|
if (ctx.isNormalCube(up1)) return true
|
||||||
if (Config.shortGrass.population < 64 && noise[ctx.pos] >= Config.shortGrass.population) return true
|
if (Config.shortGrass.population < 64 && noise[ctx.pos] >= Config.shortGrass.population) return true
|
||||||
|
|
||||||
// render grass quads
|
// render grass quads
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package mods.betterfoliage.client.render
|
package mods.betterfoliage.client.render
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
import mods.betterfoliage.BetterFoliage
|
||||||
import mods.betterfoliage.client.Client
|
import mods.betterfoliage.client.Client
|
||||||
import mods.betterfoliage.client.config.Config
|
import mods.betterfoliage.client.config.Config
|
||||||
import mods.betterfoliage.client.integration.OptifineCustomColors
|
import mods.betterfoliage.client.integration.OptifineCustomColors
|
||||||
@@ -17,15 +17,14 @@ import net.minecraft.block.material.Material
|
|||||||
import net.minecraft.client.renderer.BlockRendererDispatcher
|
import net.minecraft.client.renderer.BlockRendererDispatcher
|
||||||
import net.minecraft.client.renderer.BufferBuilder
|
import net.minecraft.client.renderer.BufferBuilder
|
||||||
import net.minecraft.util.BlockRenderLayer
|
import net.minecraft.util.BlockRenderLayer
|
||||||
import net.minecraft.util.EnumFacing.DOWN
|
import net.minecraft.util.Direction.*
|
||||||
import net.minecraft.util.EnumFacing.UP
|
import net.minecraft.util.ResourceLocation
|
||||||
import net.minecraftforge.fml.relauncher.Side
|
import net.minecraftforge.client.model.data.IModelData
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
|
||||||
import java.lang.Math.cos
|
import java.lang.Math.cos
|
||||||
import java.lang.Math.sin
|
import java.lang.Math.sin
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
@SideOnly(Side.CLIENT)
|
class RenderLeaves : AbstractBlockRenderingHandler(BetterFoliage.MOD_ID, BetterFoliage.modBus) {
|
||||||
class RenderLeaves : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|
||||||
|
|
||||||
val leavesModel = model {
|
val leavesModel = model {
|
||||||
verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yBottom = -0.5 * 1.41, yTop = 0.5 * 1.41)
|
verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yBottom = -0.5 * 1.41, yTop = 0.5 * 1.41)
|
||||||
@@ -34,7 +33,7 @@ class RenderLeaves : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|||||||
.scale(Config.leaves.size)
|
.scale(Config.leaves.size)
|
||||||
.toCross(UP).addAll()
|
.toCross(UP).addAll()
|
||||||
}
|
}
|
||||||
val snowedIcon = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "blocks/better_leaves_snowed_%d")
|
val snowedIcon = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_leaves_snowed_$idx") }
|
||||||
|
|
||||||
val perturbs = vectorSet(64) { idx ->
|
val perturbs = vectorSet(64) { idx ->
|
||||||
val angle = PI2 * idx / 64.0
|
val angle = PI2 * idx / 64.0
|
||||||
@@ -46,21 +45,19 @@ class RenderLeaves : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|||||||
Config.enabled &&
|
Config.enabled &&
|
||||||
Config.leaves.enabled &&
|
Config.leaves.enabled &&
|
||||||
LeafRegistry[ctx] != null &&
|
LeafRegistry[ctx] != null &&
|
||||||
!(Config.leaves.hideInternal && ctx.isSurroundedBy { it.isFullCube || it.material == Material.LEAVES } )
|
!(Config.leaves.hideInternal && ctx.isSurroundedByNormal)
|
||||||
|
|
||||||
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: BufferBuilder, layer: BlockRenderLayer): Boolean {
|
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: BufferBuilder, random: Random, modelData: IModelData, layer: BlockRenderLayer): Boolean {
|
||||||
val isSnowed = ctx.blockState(up1).material.let {
|
val isSnowed = ctx.blockState(up1).isSnow
|
||||||
it == Material.SNOW || it == Material.CRAFTED_SNOW
|
|
||||||
}
|
|
||||||
val leafInfo = LeafRegistry[ctx]
|
val leafInfo = LeafRegistry[ctx]
|
||||||
if (leafInfo == null) {
|
if (leafInfo == null) {
|
||||||
// shouldn't happen
|
// shouldn't happen
|
||||||
Client.logRenderError(ctx.blockState(Int3.zero), ctx.pos)
|
Client.logRenderError(ctx.blockState(Int3.zero), ctx.pos)
|
||||||
return renderWorldBlockBase(ctx, dispatcher, renderer, layer)
|
return renderWorldBlockBase(ctx, dispatcher, renderer, random, modelData, layer)
|
||||||
}
|
}
|
||||||
val blockColor = OptifineCustomColors.getBlockColor(ctx)
|
val blockColor = OptifineCustomColors.getBlockColor(ctx)
|
||||||
|
|
||||||
renderWorldBlockBase(ctx, dispatcher, renderer, layer)
|
renderWorldBlockBase(ctx, dispatcher, renderer, random, modelData, layer)
|
||||||
if (!layer.isCutout) return true
|
if (!layer.isCutout) return true
|
||||||
|
|
||||||
modelRenderer.updateShading(Int3.zero, allFaces)
|
modelRenderer.updateShading(Int3.zero, allFaces)
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package mods.betterfoliage.client.render
|
package mods.betterfoliage.client.render
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
import mods.betterfoliage.BetterFoliage
|
||||||
import mods.betterfoliage.client.Client
|
import mods.betterfoliage.client.Client
|
||||||
|
import mods.betterfoliage.client.config.BlockConfig
|
||||||
import mods.betterfoliage.client.config.Config
|
import mods.betterfoliage.client.config.Config
|
||||||
import mods.betterfoliage.client.integration.ShadersModIntegration
|
import mods.betterfoliage.client.integration.ShadersModIntegration
|
||||||
import mods.octarinecore.client.render.*
|
import mods.octarinecore.client.render.*
|
||||||
@@ -10,14 +11,14 @@ import mods.octarinecore.common.Rotation
|
|||||||
import net.minecraft.client.renderer.BlockRendererDispatcher
|
import net.minecraft.client.renderer.BlockRendererDispatcher
|
||||||
import net.minecraft.client.renderer.BufferBuilder
|
import net.minecraft.client.renderer.BufferBuilder
|
||||||
import net.minecraft.util.BlockRenderLayer
|
import net.minecraft.util.BlockRenderLayer
|
||||||
import net.minecraft.util.EnumFacing.DOWN
|
import net.minecraft.util.Direction.DOWN
|
||||||
import net.minecraft.util.EnumFacing.UP
|
import net.minecraft.util.Direction.UP
|
||||||
import net.minecraftforge.fml.relauncher.Side
|
import net.minecraft.util.ResourceLocation
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
import net.minecraftforge.client.model.data.IModelData
|
||||||
import org.apache.logging.log4j.Level
|
import org.apache.logging.log4j.Level.DEBUG
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
@SideOnly(Side.CLIENT)
|
class RenderLilypad : AbstractBlockRenderingHandler(BetterFoliage.MOD_ID, BetterFoliage.modBus) {
|
||||||
class RenderLilypad : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|
||||||
|
|
||||||
val rootModel = model {
|
val rootModel = model {
|
||||||
verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yBottom = -1.5, yTop = -0.5)
|
verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yBottom = -1.5, yTop = -0.5)
|
||||||
@@ -30,24 +31,24 @@ class RenderLilypad : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|||||||
.setFlatShader(FlatOffsetNoColor(Int3.zero))
|
.setFlatShader(FlatOffsetNoColor(Int3.zero))
|
||||||
.toCross(UP).addAll()
|
.toCross(UP).addAll()
|
||||||
}
|
}
|
||||||
val rootIcon = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "blocks/better_lilypad_roots_%d")
|
val rootIcon = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_lilypad_roots_$idx") }
|
||||||
val flowerIcon = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "blocks/better_lilypad_flower_%d")
|
val flowerIcon = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_lilypad_flower_$idx") }
|
||||||
val perturbs = vectorSet(64) { modelIdx -> xzDisk(modelIdx) * Config.lilypad.hOffset }
|
val perturbs = vectorSet(64) { modelIdx -> xzDisk(modelIdx) * Config.lilypad.hOffset }
|
||||||
|
|
||||||
override fun afterPreStitch() {
|
override fun afterPreStitch() {
|
||||||
Client.log(Level.INFO, "Registered ${rootIcon.num} lilypad root textures")
|
Client.log(DEBUG, "Registered ${rootIcon.num} lilypad root textures")
|
||||||
Client.log(Level.INFO, "Registered ${flowerIcon.num} lilypad flower textures")
|
Client.log(DEBUG, "Registered ${flowerIcon.num} lilypad flower textures")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isEligible(ctx: BlockContext): Boolean =
|
override fun isEligible(ctx: BlockContext): Boolean =
|
||||||
Config.enabled && Config.lilypad.enabled &&
|
Config.enabled && Config.lilypad.enabled &&
|
||||||
Config.blocks.lilypad.matchesClass(ctx.block)
|
BlockConfig.lilypad.matchesClass(ctx.block)
|
||||||
|
|
||||||
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: BufferBuilder, layer: BlockRenderLayer): Boolean {
|
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: BufferBuilder, random: Random, modelData: IModelData, layer: BlockRenderLayer): Boolean {
|
||||||
// render the whole block on the cutout layer
|
// render the whole block on the cutout layer
|
||||||
if (!layer.isCutout) return false
|
if (!layer.isCutout) return false
|
||||||
|
|
||||||
renderWorldBlockBase(ctx, dispatcher, renderer, null)
|
renderWorldBlockBase(ctx, dispatcher, renderer, random, modelData, null)
|
||||||
modelRenderer.updateShading(Int3.zero, allFaces)
|
modelRenderer.updateShading(Int3.zero, allFaces)
|
||||||
|
|
||||||
val rand = ctx.semiRandomArray(5)
|
val rand = ctx.semiRandomArray(5)
|
||||||
|
|||||||
@@ -1,30 +1,32 @@
|
|||||||
package mods.betterfoliage.client.render
|
package mods.betterfoliage.client.render
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
import mods.betterfoliage.BetterFoliage
|
||||||
import mods.betterfoliage.client.chunk.ChunkOverlayManager
|
import mods.betterfoliage.client.chunk.ChunkOverlayManager
|
||||||
|
import mods.betterfoliage.client.config.BlockConfig
|
||||||
import mods.betterfoliage.client.config.Config
|
import mods.betterfoliage.client.config.Config
|
||||||
import mods.betterfoliage.client.render.column.AbstractRenderColumn
|
import mods.betterfoliage.client.render.column.AbstractRenderColumn
|
||||||
import mods.betterfoliage.client.render.column.ColumnRenderLayer
|
import mods.betterfoliage.client.render.column.ColumnRenderLayer
|
||||||
import mods.betterfoliage.client.render.column.ColumnTextureInfo
|
import mods.betterfoliage.client.render.column.ColumnTextureInfo
|
||||||
import mods.betterfoliage.client.render.column.SimpleColumnInfo
|
import mods.betterfoliage.client.render.column.SimpleColumnInfo
|
||||||
|
import mods.betterfoliage.client.texture.GrassRegistry
|
||||||
import mods.octarinecore.client.render.BlockContext
|
import mods.octarinecore.client.render.BlockContext
|
||||||
import mods.octarinecore.client.resource.*
|
import mods.octarinecore.client.resource.ModelRenderRegistry
|
||||||
|
import mods.octarinecore.client.resource.ModelRenderRegistryConfigurable
|
||||||
|
import mods.octarinecore.client.resource.ModelRenderRegistryRoot
|
||||||
import mods.octarinecore.common.config.ConfigurableBlockMatcher
|
import mods.octarinecore.common.config.ConfigurableBlockMatcher
|
||||||
import mods.octarinecore.common.config.ModelTextureList
|
import mods.octarinecore.common.config.ModelTextureList
|
||||||
import mods.octarinecore.tryDefault
|
import mods.octarinecore.tryDefault
|
||||||
import net.minecraft.block.BlockLog
|
import net.minecraft.block.BlockState
|
||||||
import net.minecraft.block.state.IBlockState
|
import net.minecraft.block.LogBlock
|
||||||
import net.minecraft.util.EnumFacing.Axis
|
import net.minecraft.util.Direction.Axis
|
||||||
import net.minecraftforge.fml.relauncher.Side
|
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
|
||||||
|
|
||||||
class RenderLog : AbstractRenderColumn(BetterFoliageMod.MOD_ID) {
|
class RenderLog : AbstractRenderColumn(BetterFoliage.MOD_ID, BetterFoliage.modBus) {
|
||||||
|
|
||||||
override val addToCutout: Boolean get() = false
|
override val addToCutout: Boolean get() = false
|
||||||
|
|
||||||
override fun isEligible(ctx: BlockContext) =
|
override fun isEligible(ctx: BlockContext) =
|
||||||
Config.enabled && Config.roundLogs.enabled &&
|
Config.enabled && Config.roundLogs.enabled &&
|
||||||
Config.blocks.logClasses.matchesClass(ctx.block)
|
LogRegistry[ctx] != null
|
||||||
|
|
||||||
override val overlayLayer = RoundLogOverlayLayer()
|
override val overlayLayer = RoundLogOverlayLayer()
|
||||||
override val connectPerpendicular: Boolean get() = Config.roundLogs.connectPerpendicular
|
override val connectPerpendicular: Boolean get() = Config.roundLogs.connectPerpendicular
|
||||||
@@ -37,26 +39,26 @@ class RenderLog : AbstractRenderColumn(BetterFoliageMod.MOD_ID) {
|
|||||||
|
|
||||||
class RoundLogOverlayLayer : ColumnRenderLayer() {
|
class RoundLogOverlayLayer : ColumnRenderLayer() {
|
||||||
override val registry: ModelRenderRegistry<ColumnTextureInfo> get() = LogRegistry
|
override val registry: ModelRenderRegistry<ColumnTextureInfo> get() = LogRegistry
|
||||||
override val blockPredicate = { state: IBlockState -> Config.blocks.logClasses.matchesClass(state.block) }
|
override val blockPredicate = { state: BlockState -> BlockConfig.logBlocks.matchesClass(state.block) }
|
||||||
override val surroundPredicate = { state: IBlockState -> state.isOpaqueCube && !Config.blocks.logClasses.matchesClass(state.block) }
|
override val surroundPredicate = { state: BlockState -> !BlockConfig.logBlocks.matchesClass(state.block) }
|
||||||
|
|
||||||
override val connectSolids: Boolean get() = Config.roundLogs.connectSolids
|
override val connectSolids: Boolean get() = Config.roundLogs.connectSolids
|
||||||
override val lenientConnect: Boolean get() = Config.roundLogs.lenientConnect
|
override val lenientConnect: Boolean get() = Config.roundLogs.lenientConnect
|
||||||
override val defaultToY: Boolean get() = Config.roundLogs.defaultY
|
override val defaultToY: Boolean get() = Config.roundLogs.defaultY
|
||||||
}
|
}
|
||||||
|
|
||||||
@SideOnly(Side.CLIENT)
|
|
||||||
object LogRegistry : ModelRenderRegistryRoot<ColumnTextureInfo>()
|
object LogRegistry : ModelRenderRegistryRoot<ColumnTextureInfo>()
|
||||||
|
|
||||||
object StandardLogRegistry : ModelRenderRegistryConfigurable<ColumnTextureInfo>() {
|
object StandardLogRegistry : ModelRenderRegistryConfigurable<ColumnTextureInfo>() {
|
||||||
override val logger = BetterFoliageMod.logDetail
|
override val logger = BetterFoliage.logDetail
|
||||||
override val matchClasses: ConfigurableBlockMatcher get() = Config.blocks.logClasses
|
override val matchClasses: ConfigurableBlockMatcher get() = BlockConfig.logBlocks
|
||||||
override val modelTextures: List<ModelTextureList> get() = Config.blocks.logModels.list
|
override val modelTextures: List<ModelTextureList> get() = BlockConfig.logModels.modelList
|
||||||
override fun processModel(state: IBlockState, textures: List<String>) = SimpleColumnInfo.Key(logger, getAxis(state), textures)
|
override fun processModel(state: BlockState, textures: List<String>) = SimpleColumnInfo.Key(logger, getAxis(state), textures)
|
||||||
|
init { BetterFoliage.modBus.register(this) }
|
||||||
|
|
||||||
fun getAxis(state: IBlockState): Axis? {
|
fun getAxis(state: BlockState): Axis? {
|
||||||
val axis = tryDefault(null) { state.getValue(BlockLog.LOG_AXIS).toString() } ?:
|
val axis = tryDefault(null) { state.get(LogBlock.AXIS).toString() } ?:
|
||||||
state.properties.entries.find { it.key.getName().toLowerCase() == "axis" }?.value?.toString()
|
state.values.entries.find { it.key.getName().toLowerCase() == "axis" }?.value?.toString()
|
||||||
return when (axis) {
|
return when (axis) {
|
||||||
"x" -> Axis.X
|
"x" -> Axis.X
|
||||||
"y" -> Axis.Y
|
"y" -> Axis.Y
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package mods.betterfoliage.client.render
|
package mods.betterfoliage.client.render
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
import mods.betterfoliage.BetterFoliage
|
||||||
import mods.betterfoliage.client.Client
|
import mods.betterfoliage.client.Client
|
||||||
|
import mods.betterfoliage.client.config.BlockConfig
|
||||||
import mods.betterfoliage.client.config.Config
|
import mods.betterfoliage.client.config.Config
|
||||||
import mods.octarinecore.client.render.AbstractBlockRenderingHandler
|
import mods.octarinecore.client.render.AbstractBlockRenderingHandler
|
||||||
import mods.octarinecore.client.render.BlockContext
|
import mods.octarinecore.client.render.BlockContext
|
||||||
@@ -12,35 +13,35 @@ import mods.octarinecore.common.Rotation
|
|||||||
import net.minecraft.client.renderer.BlockRendererDispatcher
|
import net.minecraft.client.renderer.BlockRendererDispatcher
|
||||||
import net.minecraft.client.renderer.BufferBuilder
|
import net.minecraft.client.renderer.BufferBuilder
|
||||||
import net.minecraft.util.BlockRenderLayer
|
import net.minecraft.util.BlockRenderLayer
|
||||||
import net.minecraftforge.fml.relauncher.Side
|
import net.minecraft.util.ResourceLocation
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
import net.minecraftforge.client.model.data.IModelData
|
||||||
import org.apache.logging.log4j.Level.INFO
|
import org.apache.logging.log4j.Level.DEBUG
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
@SideOnly(Side.CLIENT)
|
class RenderMycelium : AbstractBlockRenderingHandler(BetterFoliage.MOD_ID, BetterFoliage.modBus) {
|
||||||
class RenderMycelium : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|
||||||
|
|
||||||
val myceliumIcon = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "blocks/better_mycel_%d")
|
val myceliumIcon = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_mycel_$idx") }
|
||||||
val myceliumModel = modelSet(64, RenderGrass.grassTopQuads(Config.shortGrass.heightMin, Config.shortGrass.heightMax))
|
val myceliumModel = modelSet(64) { idx -> RenderGrass.grassTopQuads(Config.shortGrass.heightMin, Config.shortGrass.heightMax)(idx) }
|
||||||
|
|
||||||
override fun afterPreStitch() {
|
override fun afterPreStitch() {
|
||||||
Client.log(INFO, "Registered ${myceliumIcon.num} mycelium textures")
|
Client.log(DEBUG, "Registered ${myceliumIcon.num} mycelium textures")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isEligible(ctx: BlockContext): Boolean {
|
override fun isEligible(ctx: BlockContext): Boolean {
|
||||||
if (!Config.enabled || !Config.shortGrass.myceliumEnabled) return false
|
if (!Config.enabled || !Config.shortGrass.myceliumEnabled) return false
|
||||||
return Config.blocks.mycelium.matchesClass(ctx.block)
|
return BlockConfig.mycelium.matchesClass(ctx.block)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: BufferBuilder, layer: BlockRenderLayer): Boolean {
|
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: BufferBuilder, random: Random, modelData: IModelData, layer: BlockRenderLayer): Boolean {
|
||||||
// render the whole block on the cutout layer
|
// render the whole block on the cutout layer
|
||||||
if (!layer.isCutout) return false
|
if (!layer.isCutout) return false
|
||||||
|
|
||||||
val isSnowed = ctx.blockState(up1).isSnow
|
val isSnowed = ctx.blockState(up1).isSnow
|
||||||
|
|
||||||
renderWorldBlockBase(ctx, dispatcher, renderer, null)
|
renderWorldBlockBase(ctx, dispatcher, renderer, random, modelData, null)
|
||||||
|
|
||||||
if (isSnowed && !Config.shortGrass.snowEnabled) return true
|
if (isSnowed && !Config.shortGrass.snowEnabled) return true
|
||||||
if (ctx.blockState(up1).isOpaqueCube) return true
|
if (ctx.isNormalCube(up1)) return true
|
||||||
|
|
||||||
val rand = ctx.semiRandomArray(2)
|
val rand = ctx.semiRandomArray(2)
|
||||||
modelRenderer.render(
|
modelRenderer.render(
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package mods.betterfoliage.client.render
|
package mods.betterfoliage.client.render
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
import mods.betterfoliage.BetterFoliage
|
||||||
import mods.betterfoliage.client.Client
|
import mods.betterfoliage.client.Client
|
||||||
|
import mods.betterfoliage.client.config.BlockConfig
|
||||||
import mods.betterfoliage.client.config.Config
|
import mods.betterfoliage.client.config.Config
|
||||||
import mods.octarinecore.client.render.*
|
import mods.octarinecore.client.render.*
|
||||||
import mods.octarinecore.common.Int3
|
import mods.octarinecore.common.Int3
|
||||||
@@ -10,15 +11,16 @@ import mods.octarinecore.random
|
|||||||
import net.minecraft.client.renderer.BlockRendererDispatcher
|
import net.minecraft.client.renderer.BlockRendererDispatcher
|
||||||
import net.minecraft.client.renderer.BufferBuilder
|
import net.minecraft.client.renderer.BufferBuilder
|
||||||
import net.minecraft.util.BlockRenderLayer
|
import net.minecraft.util.BlockRenderLayer
|
||||||
import net.minecraft.util.EnumFacing.*
|
import net.minecraft.util.Direction.Axis
|
||||||
import net.minecraftforge.fml.relauncher.Side
|
import net.minecraft.util.Direction.*
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
import net.minecraft.util.ResourceLocation
|
||||||
import org.apache.logging.log4j.Level.INFO
|
import net.minecraftforge.client.model.data.IModelData
|
||||||
|
import org.apache.logging.log4j.Level.DEBUG
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
@SideOnly(Side.CLIENT)
|
class RenderNetherrack : AbstractBlockRenderingHandler(BetterFoliage.MOD_ID, BetterFoliage.modBus) {
|
||||||
class RenderNetherrack : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|
||||||
|
|
||||||
val netherrackIcon = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "blocks/better_netherrack_%d")
|
val netherrackIcon = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_netherrack_$idx") }
|
||||||
val netherrackModel = modelSet(64) { modelIdx ->
|
val netherrackModel = modelSet(64) { modelIdx ->
|
||||||
verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yTop = -0.5,
|
verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yTop = -0.5,
|
||||||
yBottom = -0.5 - random(Config.netherrack.heightMin, Config.netherrack.heightMax))
|
yBottom = -0.5 - random(Config.netherrack.heightMin, Config.netherrack.heightMax))
|
||||||
@@ -29,19 +31,19 @@ class RenderNetherrack : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID)
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun afterPreStitch() {
|
override fun afterPreStitch() {
|
||||||
Client.log(INFO, "Registered ${netherrackIcon.num} netherrack textures")
|
Client.log(DEBUG, "Registered ${netherrackIcon.num} netherrack textures")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isEligible(ctx: BlockContext): Boolean {
|
override fun isEligible(ctx: BlockContext): Boolean {
|
||||||
if (!Config.enabled || !Config.netherrack.enabled) return false
|
if (!Config.enabled || !Config.netherrack.enabled) return false
|
||||||
return Config.blocks.netherrack.matchesClass(ctx.block)
|
return BlockConfig.netherrack.matchesClass(ctx.block)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: BufferBuilder, layer: BlockRenderLayer): Boolean {
|
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: BufferBuilder, random: Random, modelData: IModelData, layer: BlockRenderLayer): Boolean {
|
||||||
val baseRender = renderWorldBlockBase(ctx, dispatcher, renderer, layer)
|
val baseRender = renderWorldBlockBase(ctx, dispatcher, renderer, random, modelData, layer)
|
||||||
if (!layer.isCutout) return baseRender
|
if (!layer.isCutout) return baseRender
|
||||||
|
|
||||||
if (ctx.blockState(down1).isOpaqueCube) return baseRender
|
if (ctx.isNormalCube(down1)) return baseRender
|
||||||
|
|
||||||
modelRenderer.updateShading(Int3.zero, allFaces)
|
modelRenderer.updateShading(Int3.zero, allFaces)
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,30 @@
|
|||||||
package mods.betterfoliage.client.render
|
package mods.betterfoliage.client.render
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
import mods.betterfoliage.BetterFoliage
|
||||||
import mods.betterfoliage.client.Client
|
import mods.betterfoliage.client.Client
|
||||||
|
import mods.betterfoliage.client.config.BlockConfig
|
||||||
import mods.betterfoliage.client.config.Config
|
import mods.betterfoliage.client.config.Config
|
||||||
import mods.betterfoliage.client.integration.ShadersModIntegration
|
import mods.betterfoliage.client.integration.ShadersModIntegration
|
||||||
import mods.octarinecore.client.render.*
|
import mods.octarinecore.client.render.*
|
||||||
import mods.octarinecore.common.Int3
|
import mods.octarinecore.common.Int3
|
||||||
import mods.octarinecore.common.Rotation
|
import mods.octarinecore.common.Rotation
|
||||||
import mods.octarinecore.random
|
import mods.octarinecore.random
|
||||||
|
import net.minecraft.block.Block
|
||||||
import net.minecraft.block.material.Material
|
import net.minecraft.block.material.Material
|
||||||
import net.minecraft.client.renderer.BlockRendererDispatcher
|
import net.minecraft.client.renderer.BlockRendererDispatcher
|
||||||
import net.minecraft.client.renderer.BufferBuilder
|
import net.minecraft.client.renderer.BufferBuilder
|
||||||
|
import net.minecraft.tags.BlockTags
|
||||||
import net.minecraft.util.BlockRenderLayer
|
import net.minecraft.util.BlockRenderLayer
|
||||||
import net.minecraft.util.EnumFacing.UP
|
import net.minecraft.util.Direction.UP
|
||||||
import net.minecraftforge.fml.relauncher.Side
|
import net.minecraft.util.ResourceLocation
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
import net.minecraftforge.client.model.data.IModelData
|
||||||
import org.apache.logging.log4j.Level
|
import org.apache.logging.log4j.Level.DEBUG
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
@SideOnly(Side.CLIENT)
|
class RenderReeds : AbstractBlockRenderingHandler(BetterFoliage.MOD_ID, BetterFoliage.modBus) {
|
||||||
class RenderReeds : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|
||||||
|
|
||||||
val noise = simplexNoise()
|
val noise = simplexNoise()
|
||||||
val reedIcons = iconSet(Client.genReeds.generatedResource("${BetterFoliageMod.LEGACY_DOMAIN}:blocks/better_reed_%d"))
|
val reedIcons = iconSet { idx -> Client.genReeds.registerResource(ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_reed_$idx")) }
|
||||||
val reedModels = modelSet(64) { modelIdx ->
|
val reedModels = modelSet(64) { modelIdx ->
|
||||||
val height = random(Config.reed.heightMin, Config.reed.heightMax)
|
val height = random(Config.reed.heightMin, Config.reed.heightMax)
|
||||||
val waterline = 0.875f
|
val waterline = 0.875f
|
||||||
@@ -41,19 +44,19 @@ class RenderReeds : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun afterPreStitch() {
|
override fun afterPreStitch() {
|
||||||
Client.log(Level.INFO, "Registered ${reedIcons.num} reed textures")
|
Client.log(DEBUG, "Registered ${reedIcons.num} reed textures")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isEligible(ctx: BlockContext) =
|
override fun isEligible(ctx: BlockContext) =
|
||||||
Config.enabled && Config.reed.enabled &&
|
Config.enabled && Config.reed.enabled &&
|
||||||
ctx.blockState(up2).material == Material.AIR &&
|
ctx.blockState(up2).material == Material.AIR &&
|
||||||
ctx.blockState(up1).material == Material.WATER &&
|
ctx.blockState(up1).material == Material.WATER &&
|
||||||
Config.blocks.dirt.matchesClass(ctx.block) &&
|
BlockTags.DIRT_LIKE.contains(ctx.block) &&
|
||||||
ctx.biomeId in Config.reed.biomes &&
|
ctx.biome.let { it.downfall > Config.reed.minBiomeRainfall && it.defaultTemperature >= Config.reed.minBiomeTemp } &&
|
||||||
noise[ctx.pos] < Config.reed.population
|
noise[ctx.pos] < Config.reed.population
|
||||||
|
|
||||||
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: BufferBuilder, layer: BlockRenderLayer): Boolean {
|
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: BufferBuilder, random: Random, modelData: IModelData, layer: BlockRenderLayer): Boolean {
|
||||||
val baseRender = renderWorldBlockBase(ctx, dispatcher, renderer, layer)
|
val baseRender = renderWorldBlockBase(ctx, dispatcher, renderer, random, modelData, layer)
|
||||||
if (!layer.isCutout) return baseRender
|
if (!layer.isCutout) return baseRender
|
||||||
|
|
||||||
modelRenderer.updateShading(Int3.zero, allFaces)
|
modelRenderer.updateShading(Int3.zero, allFaces)
|
||||||
|
|||||||
@@ -2,17 +2,20 @@
|
|||||||
package mods.betterfoliage.client.render
|
package mods.betterfoliage.client.render
|
||||||
|
|
||||||
import mods.octarinecore.PI2
|
import mods.octarinecore.PI2
|
||||||
import mods.octarinecore.client.render.*
|
import mods.octarinecore.client.render.Model
|
||||||
|
import mods.octarinecore.client.render.PostProcessLambda
|
||||||
|
import mods.octarinecore.client.render.Quad
|
||||||
import mods.octarinecore.common.Double3
|
import mods.octarinecore.common.Double3
|
||||||
import mods.octarinecore.common.Int3
|
import mods.octarinecore.common.Int3
|
||||||
import mods.octarinecore.common.Rotation
|
import mods.octarinecore.common.Rotation
|
||||||
import mods.octarinecore.common.times
|
import mods.octarinecore.common.times
|
||||||
import net.minecraft.block.Block
|
import net.minecraft.block.BlockState
|
||||||
import net.minecraft.block.material.Material
|
import net.minecraft.block.material.Material
|
||||||
import net.minecraft.block.state.IBlockState
|
|
||||||
import net.minecraft.util.BlockRenderLayer
|
import net.minecraft.util.BlockRenderLayer
|
||||||
import net.minecraft.util.EnumFacing
|
import net.minecraft.util.Direction
|
||||||
import net.minecraft.util.EnumFacing.*
|
import net.minecraft.util.Direction.*
|
||||||
|
import kotlin.math.cos
|
||||||
|
import kotlin.math.sin
|
||||||
|
|
||||||
val up1 = Int3(1 to UP)
|
val up1 = Int3(1 to UP)
|
||||||
val up2 = Int3(2 to UP)
|
val up2 = Int3(2 to UP)
|
||||||
@@ -25,15 +28,15 @@ val denseLeavesRot = arrayOf(Rotation.identity, Rotation.rot90[EAST.ordinal], Ro
|
|||||||
val whitewash: PostProcessLambda = { _, _, _, _, _ -> setGrey(1.4f) }
|
val whitewash: PostProcessLambda = { _, _, _, _, _ -> setGrey(1.4f) }
|
||||||
val greywash: PostProcessLambda = { _, _, _, _, _ -> setGrey(1.0f) }
|
val greywash: PostProcessLambda = { _, _, _, _, _ -> setGrey(1.0f) }
|
||||||
|
|
||||||
val IBlockState.isSnow: Boolean get() = material.let { it == Material.SNOW || it == Material.CRAFTED_SNOW }
|
val BlockState.isSnow: Boolean get() = material.let { it == Material.SNOW }
|
||||||
|
|
||||||
fun Quad.toCross(rotAxis: EnumFacing, trans: (Quad)->Quad) =
|
fun Quad.toCross(rotAxis: Direction, trans: (Quad)->Quad) =
|
||||||
(0..3).map { rotIdx ->
|
(0..3).map { rotIdx ->
|
||||||
trans(rotate(Rotation.rot90[rotAxis.ordinal] * rotIdx).mirrorUV(rotIdx > 1, false))
|
trans(rotate(Rotation.rot90[rotAxis.ordinal] * rotIdx).mirrorUV(rotIdx > 1, false))
|
||||||
}
|
}
|
||||||
fun Quad.toCross(rotAxis: EnumFacing) = toCross(rotAxis) { it }
|
fun Quad.toCross(rotAxis: Direction) = toCross(rotAxis) { it }
|
||||||
|
|
||||||
fun xzDisk(modelIdx: Int) = (PI2 * modelIdx / 64.0).let { Double3(Math.cos(it), 0.0, Math.sin(it)) }
|
fun xzDisk(modelIdx: Int) = (PI2 * modelIdx / 64.0).let { Double3(cos(it), 0.0, sin(it)) }
|
||||||
|
|
||||||
val rotationFromUp = arrayOf(
|
val rotationFromUp = arrayOf(
|
||||||
Rotation.rot90[EAST.ordinal] * 2,
|
Rotation.rot90[EAST.ordinal] * 2,
|
||||||
@@ -58,5 +61,5 @@ fun Model.mix(first: Model, second: Model, predicate: (Int)->Boolean) {
|
|||||||
|
|
||||||
val BlockRenderLayer.isCutout: Boolean get() = (this == BlockRenderLayer.CUTOUT) || (this == BlockRenderLayer.CUTOUT_MIPPED)
|
val BlockRenderLayer.isCutout: Boolean get() = (this == BlockRenderLayer.CUTOUT) || (this == BlockRenderLayer.CUTOUT_MIPPED)
|
||||||
|
|
||||||
fun IBlockState.canRenderInLayer(layer: BlockRenderLayer) = this.block.canRenderInLayer(this, layer)
|
fun BlockState.canRenderInLayer(layer: BlockRenderLayer) = this.block.canRenderInLayer(this, layer)
|
||||||
fun IBlockState.canRenderInCutout() = this.block.canRenderInLayer(this, BlockRenderLayer.CUTOUT) || this.block.canRenderInLayer(this, BlockRenderLayer.CUTOUT_MIPPED)
|
fun BlockState.canRenderInCutout() = this.block.canRenderInLayer(this, BlockRenderLayer.CUTOUT) || this.block.canRenderInLayer(this, BlockRenderLayer.CUTOUT_MIPPED)
|
||||||
@@ -1,31 +1,29 @@
|
|||||||
package mods.betterfoliage.client.render.column
|
package mods.betterfoliage.client.render.column
|
||||||
|
|
||||||
import mods.betterfoliage.client.Client
|
import mods.betterfoliage.client.Client
|
||||||
import mods.betterfoliage.client.chunk.ChunkOverlayLayer
|
|
||||||
import mods.betterfoliage.client.chunk.ChunkOverlayManager
|
import mods.betterfoliage.client.chunk.ChunkOverlayManager
|
||||||
import mods.betterfoliage.client.config.Config
|
|
||||||
import mods.betterfoliage.client.integration.ShadersModIntegration.renderAs
|
import mods.betterfoliage.client.integration.ShadersModIntegration.renderAs
|
||||||
import mods.betterfoliage.client.render.*
|
import mods.betterfoliage.client.render.*
|
||||||
import mods.betterfoliage.client.render.column.ColumnLayerData.SpecialRender.BlockType.*
|
import mods.betterfoliage.client.render.column.ColumnLayerData.SpecialRender.BlockType.*
|
||||||
import mods.betterfoliage.client.render.column.ColumnLayerData.SpecialRender.QuadrantType
|
import mods.betterfoliage.client.render.column.ColumnLayerData.SpecialRender.QuadrantType
|
||||||
import mods.betterfoliage.client.render.column.ColumnLayerData.SpecialRender.QuadrantType.*
|
import mods.betterfoliage.client.render.column.ColumnLayerData.SpecialRender.QuadrantType.*
|
||||||
import mods.octarinecore.client.render.*
|
import mods.octarinecore.client.render.*
|
||||||
import mods.octarinecore.client.resource.ModelRenderRegistry
|
import mods.octarinecore.common.Int3
|
||||||
import mods.octarinecore.common.*
|
import mods.octarinecore.common.Rotation
|
||||||
import net.minecraft.block.state.IBlockState
|
import mods.octarinecore.common.face
|
||||||
|
import mods.octarinecore.common.rot
|
||||||
import net.minecraft.client.renderer.BlockRendererDispatcher
|
import net.minecraft.client.renderer.BlockRendererDispatcher
|
||||||
import net.minecraft.client.renderer.BufferBuilder
|
import net.minecraft.client.renderer.BufferBuilder
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
import net.minecraft.client.renderer.chunk.ChunkRenderCache
|
||||||
|
import net.minecraft.client.world.ClientWorld
|
||||||
import net.minecraft.util.BlockRenderLayer
|
import net.minecraft.util.BlockRenderLayer
|
||||||
import net.minecraft.util.EnumFacing.*
|
import net.minecraft.util.Direction.*
|
||||||
import net.minecraft.util.math.BlockPos
|
import net.minecraftforge.client.model.data.IModelData
|
||||||
import net.minecraft.world.IBlockAccess
|
import net.minecraftforge.eventbus.api.IEventBus
|
||||||
import net.minecraftforge.fml.relauncher.Side
|
import java.util.*
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
|
||||||
|
|
||||||
@SideOnly(Side.CLIENT)
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
abstract class AbstractRenderColumn(modId: String) : AbstractBlockRenderingHandler(modId) {
|
abstract class AbstractRenderColumn(modId: String, modBus: IEventBus) : AbstractBlockRenderingHandler(modId, modBus) {
|
||||||
|
|
||||||
/** The rotations necessary to bring the models in position for the 4 quadrants */
|
/** The rotations necessary to bring the models in position for the 4 quadrants */
|
||||||
val quadrantRotations = Array(4) { Rotation.rot90[UP.ordinal] * it }
|
val quadrantRotations = Array(4) { Rotation.rot90[UP.ordinal] * it }
|
||||||
@@ -96,21 +94,21 @@ abstract class AbstractRenderColumn(modId: String) : AbstractBlockRenderingHandl
|
|||||||
q1 == q2 || ((q1 == SQUARE || q1 == INVISIBLE) && (q2 == SQUARE || q2 == INVISIBLE))
|
q1 == q2 || ((q1 == SQUARE || q1 == INVISIBLE) && (q2 == SQUARE || q2 == INVISIBLE))
|
||||||
|
|
||||||
@Suppress("NON_EXHAUSTIVE_WHEN")
|
@Suppress("NON_EXHAUSTIVE_WHEN")
|
||||||
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: BufferBuilder, layer: BlockRenderLayer): Boolean {
|
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: BufferBuilder, random: Random, modelData: IModelData, layer: BlockRenderLayer): Boolean {
|
||||||
|
|
||||||
val roundLog = ChunkOverlayManager.get(overlayLayer, ctx.world!!, ctx.pos)
|
val roundLog = ChunkOverlayManager.get(overlayLayer, ctx.reader!!, ctx.pos)
|
||||||
when(roundLog) {
|
when(roundLog) {
|
||||||
ColumnLayerData.SkipRender -> return true
|
ColumnLayerData.SkipRender -> return true
|
||||||
ColumnLayerData.NormalRender -> return renderWorldBlockBase(ctx, dispatcher, renderer, null)
|
ColumnLayerData.NormalRender -> return renderWorldBlockBase(ctx, dispatcher, renderer, random, modelData, null)
|
||||||
ColumnLayerData.ResolveError, null -> {
|
ColumnLayerData.ResolveError, null -> {
|
||||||
Client.logRenderError(ctx.blockState(Int3.zero), ctx.pos)
|
Client.logRenderError(ctx.blockState(Int3.zero), ctx.pos)
|
||||||
return renderWorldBlockBase(ctx, dispatcher, renderer, null)
|
return renderWorldBlockBase(ctx, dispatcher, renderer, random, modelData, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if log axis is not defined and "Default to vertical" config option is not set, render normally
|
// if log axis is not defined and "Default to vertical" config option is not set, render normally
|
||||||
if ((roundLog as ColumnLayerData.SpecialRender).column.axis == null && !overlayLayer.defaultToY) {
|
if ((roundLog as ColumnLayerData.SpecialRender).column.axis == null && !overlayLayer.defaultToY) {
|
||||||
return renderWorldBlockBase(ctx, dispatcher, renderer, null)
|
return renderWorldBlockBase(ctx, dispatcher, renderer, random, modelData, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
// get AO data
|
// get AO data
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package mods.betterfoliage.client.render.column
|
|||||||
|
|
||||||
import mods.betterfoliage.client.chunk.ChunkOverlayLayer
|
import mods.betterfoliage.client.chunk.ChunkOverlayLayer
|
||||||
import mods.betterfoliage.client.chunk.ChunkOverlayManager
|
import mods.betterfoliage.client.chunk.ChunkOverlayManager
|
||||||
|
import mods.betterfoliage.client.chunk.dimType
|
||||||
import mods.betterfoliage.client.config.Config
|
import mods.betterfoliage.client.config.Config
|
||||||
import mods.betterfoliage.client.render.column.ColumnLayerData.SpecialRender.BlockType.*
|
import mods.betterfoliage.client.render.column.ColumnLayerData.SpecialRender.BlockType.*
|
||||||
import mods.betterfoliage.client.render.column.ColumnLayerData.SpecialRender.QuadrantType
|
import mods.betterfoliage.client.render.column.ColumnLayerData.SpecialRender.QuadrantType
|
||||||
@@ -13,12 +14,11 @@ import mods.octarinecore.common.Int3
|
|||||||
import mods.octarinecore.common.Rotation
|
import mods.octarinecore.common.Rotation
|
||||||
import mods.octarinecore.common.face
|
import mods.octarinecore.common.face
|
||||||
import mods.octarinecore.common.plus
|
import mods.octarinecore.common.plus
|
||||||
import net.minecraft.block.state.IBlockState
|
import net.minecraft.block.BlockState
|
||||||
import net.minecraft.util.EnumFacing
|
import net.minecraft.util.Direction.Axis
|
||||||
|
import net.minecraft.util.Direction.AxisDirection
|
||||||
import net.minecraft.util.math.BlockPos
|
import net.minecraft.util.math.BlockPos
|
||||||
import net.minecraft.world.IBlockAccess
|
import net.minecraft.world.IEnviromentBlockReader
|
||||||
import net.minecraftforge.fml.relauncher.Side
|
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
|
||||||
|
|
||||||
/** Index of SOUTH-EAST quadrant. */
|
/** Index of SOUTH-EAST quadrant. */
|
||||||
const val SE = 0
|
const val SE = 0
|
||||||
@@ -32,13 +32,11 @@ const val SW = 3
|
|||||||
/**
|
/**
|
||||||
* Sealed class hierarchy for all possible render outcomes
|
* Sealed class hierarchy for all possible render outcomes
|
||||||
*/
|
*/
|
||||||
@SideOnly(Side.CLIENT)
|
|
||||||
sealed class ColumnLayerData {
|
sealed class ColumnLayerData {
|
||||||
/**
|
/**
|
||||||
* Data structure to cache texture and world neighborhood data relevant to column rendering
|
* Data structure to cache texture and world neighborhood data relevant to column rendering
|
||||||
*/
|
*/
|
||||||
@Suppress("ArrayInDataClass") // not used in comparisons anywhere
|
@Suppress("ArrayInDataClass") // not used in comparisons anywhere
|
||||||
@SideOnly(Side.CLIENT)
|
|
||||||
data class SpecialRender(
|
data class SpecialRender(
|
||||||
val column: ColumnTextureInfo,
|
val column: ColumnTextureInfo,
|
||||||
val upType: BlockType,
|
val upType: BlockType,
|
||||||
@@ -52,15 +50,12 @@ sealed class ColumnLayerData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Column block should not be rendered at all */
|
/** Column block should not be rendered at all */
|
||||||
@SideOnly(Side.CLIENT)
|
|
||||||
object SkipRender : ColumnLayerData()
|
object SkipRender : ColumnLayerData()
|
||||||
|
|
||||||
/** Column block must be rendered normally */
|
/** Column block must be rendered normally */
|
||||||
@SideOnly(Side.CLIENT)
|
|
||||||
object NormalRender : ColumnLayerData()
|
object NormalRender : ColumnLayerData()
|
||||||
|
|
||||||
/** Error while resolving render data, column block must be rendered normally */
|
/** Error while resolving render data, column block must be rendered normally */
|
||||||
@SideOnly(Side.CLIENT)
|
|
||||||
object ResolveError : ColumnLayerData()
|
object ResolveError : ColumnLayerData()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,29 +63,29 @@ sealed class ColumnLayerData {
|
|||||||
abstract class ColumnRenderLayer : ChunkOverlayLayer<ColumnLayerData> {
|
abstract class ColumnRenderLayer : ChunkOverlayLayer<ColumnLayerData> {
|
||||||
|
|
||||||
abstract val registry: ModelRenderRegistry<ColumnTextureInfo>
|
abstract val registry: ModelRenderRegistry<ColumnTextureInfo>
|
||||||
abstract val blockPredicate: (IBlockState)->Boolean
|
abstract val blockPredicate: (BlockState)->Boolean
|
||||||
abstract val surroundPredicate: (IBlockState) -> Boolean
|
abstract val surroundPredicate: (BlockState) -> Boolean
|
||||||
abstract val connectSolids: Boolean
|
abstract val connectSolids: Boolean
|
||||||
abstract val lenientConnect: Boolean
|
abstract val lenientConnect: Boolean
|
||||||
abstract val defaultToY: Boolean
|
abstract val defaultToY: Boolean
|
||||||
|
|
||||||
val allNeighborOffsets = (-1..1).flatMap { offsetX -> (-1..1).flatMap { offsetY -> (-1..1).map { offsetZ -> Int3(offsetX, offsetY, offsetZ) }}}
|
val allNeighborOffsets = (-1..1).flatMap { offsetX -> (-1..1).flatMap { offsetY -> (-1..1).map { offsetZ -> Int3(offsetX, offsetY, offsetZ) }}}
|
||||||
|
|
||||||
override fun onBlockUpdate(world: IBlockAccess, pos: BlockPos) {
|
override fun onBlockUpdate(reader: IEnviromentBlockReader, pos: BlockPos) {
|
||||||
allNeighborOffsets.forEach { offset -> ChunkOverlayManager.clear(this, pos + offset) }
|
allNeighborOffsets.forEach { offset -> ChunkOverlayManager.clear(reader.dimType, this, pos + offset) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun calculate(world: IBlockAccess, pos: BlockPos) = calculate(BlockContext(world, pos))
|
override fun calculate(reader: IEnviromentBlockReader, pos: BlockPos) = calculate(BlockContext(reader, pos))
|
||||||
|
|
||||||
fun calculate(ctx: BlockContext): ColumnLayerData {
|
fun calculate(ctx: BlockContext): ColumnLayerData {
|
||||||
if (ctx.isSurroundedBy(surroundPredicate)) return ColumnLayerData.SkipRender
|
if (ctx.isSurroundedBy(surroundPredicate) && ctx.isSurroundedByNormal) return ColumnLayerData.SkipRender
|
||||||
val columnTextures = registry[ctx] ?: return ColumnLayerData.ResolveError
|
val columnTextures = registry[ctx] ?: return ColumnLayerData.ResolveError
|
||||||
|
|
||||||
// if log axis is not defined and "Default to vertical" config option is not set, render normally
|
// if log axis is not defined and "Default to vertical" config option is not set, render normally
|
||||||
val logAxis = columnTextures.axis ?: if (defaultToY) EnumFacing.Axis.Y else return ColumnLayerData.NormalRender
|
val logAxis = columnTextures.axis ?: if (defaultToY) Axis.Y else return ColumnLayerData.NormalRender
|
||||||
|
|
||||||
// check log neighborhood
|
// check log neighborhood
|
||||||
val baseRotation = rotationFromUp[(logAxis to EnumFacing.AxisDirection.POSITIVE).face.ordinal]
|
val baseRotation = rotationFromUp[(logAxis to AxisDirection.POSITIVE).face.ordinal]
|
||||||
|
|
||||||
val upType = ctx.blockType(baseRotation, logAxis, Int3(0, 1, 0))
|
val upType = ctx.blockType(baseRotation, logAxis, Int3(0, 1, 0))
|
||||||
val downType = ctx.blockType(baseRotation, logAxis, Int3(0, -1, 0))
|
val downType = ctx.blockType(baseRotation, logAxis, Int3(0, -1, 0))
|
||||||
@@ -109,7 +104,7 @@ abstract class ColumnRenderLayer : ChunkOverlayLayer<ColumnLayerData> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Fill the array of [QuadrantType]s based on the blocks to the sides of this one. */
|
/** Fill the array of [QuadrantType]s based on the blocks to the sides of this one. */
|
||||||
fun Array<QuadrantType>.checkNeighbors(ctx: BlockContext, rotation: Rotation, logAxis: EnumFacing.Axis, yOff: Int): Array<QuadrantType> {
|
fun Array<QuadrantType>.checkNeighbors(ctx: BlockContext, rotation: Rotation, logAxis: Axis, yOff: Int): Array<QuadrantType> {
|
||||||
val blkS = ctx.blockType(rotation, logAxis, Int3(0, yOff, 1))
|
val blkS = ctx.blockType(rotation, logAxis, Int3(0, yOff, 1))
|
||||||
val blkE = ctx.blockType(rotation, logAxis, Int3(1, yOff, 0))
|
val blkE = ctx.blockType(rotation, logAxis, Int3(1, yOff, 0))
|
||||||
val blkN = ctx.blockType(rotation, logAxis, Int3(0, yOff, -1))
|
val blkN = ctx.blockType(rotation, logAxis, Int3(0, yOff, -1))
|
||||||
@@ -176,13 +171,13 @@ abstract class ColumnRenderLayer : ChunkOverlayLayer<ColumnLayerData> {
|
|||||||
/**
|
/**
|
||||||
* Get the type of the block at the given offset in a rotated reference frame.
|
* Get the type of the block at the given offset in a rotated reference frame.
|
||||||
*/
|
*/
|
||||||
fun BlockContext.blockType(rotation: Rotation, axis: EnumFacing.Axis, offset: Int3): ColumnLayerData.SpecialRender.BlockType {
|
fun BlockContext.blockType(rotation: Rotation, axis: Axis, offset: Int3): ColumnLayerData.SpecialRender.BlockType {
|
||||||
val offsetRot = offset.rotate(rotation)
|
val offsetRot = offset.rotate(rotation)
|
||||||
val state = blockState(offsetRot)
|
val state = blockState(offsetRot)
|
||||||
return if (!blockPredicate(state)) {
|
return if (!blockPredicate(state)) {
|
||||||
if (state.isOpaqueCube) SOLID else NONSOLID
|
if (isNormalCube(offsetRot)) SOLID else NONSOLID
|
||||||
} else {
|
} else {
|
||||||
(registry[state, world!!, pos + offsetRot]?.axis ?: if (Config.roundLogs.defaultY) EnumFacing.Axis.Y else null)?.let {
|
(registry[state, reader!!, pos + offsetRot]?.axis ?: if (Config.roundLogs.defaultY) Axis.Y else null)?.let {
|
||||||
if (it == axis) PARALLEL else PERPENDICULAR
|
if (it == axis) PARALLEL else PERPENDICULAR
|
||||||
} ?: SOLID
|
} ?: SOLID
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,25 +4,22 @@ import mods.octarinecore.client.render.QuadIconResolver
|
|||||||
import mods.octarinecore.client.render.blockContext
|
import mods.octarinecore.client.render.blockContext
|
||||||
import mods.octarinecore.client.resource.ModelRenderKey
|
import mods.octarinecore.client.resource.ModelRenderKey
|
||||||
import mods.octarinecore.client.resource.get
|
import mods.octarinecore.client.resource.get
|
||||||
|
import mods.octarinecore.client.resource.missingSprite
|
||||||
import mods.octarinecore.common.rotate
|
import mods.octarinecore.common.rotate
|
||||||
|
import net.minecraft.client.renderer.texture.AtlasTexture
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||||
import net.minecraft.client.renderer.texture.TextureMap
|
import net.minecraft.util.Direction.*
|
||||||
import net.minecraft.util.EnumFacing
|
|
||||||
import net.minecraftforge.fml.relauncher.Side
|
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
|
||||||
import org.apache.logging.log4j.Logger
|
import org.apache.logging.log4j.Logger
|
||||||
|
|
||||||
@SideOnly(Side.CLIENT)
|
|
||||||
interface ColumnTextureInfo {
|
interface ColumnTextureInfo {
|
||||||
val axis: EnumFacing.Axis?
|
val axis: Axis?
|
||||||
val top: QuadIconResolver
|
val top: QuadIconResolver
|
||||||
val bottom: QuadIconResolver
|
val bottom: QuadIconResolver
|
||||||
val side: QuadIconResolver
|
val side: QuadIconResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
@SideOnly(Side.CLIENT)
|
|
||||||
open class SimpleColumnInfo(
|
open class SimpleColumnInfo(
|
||||||
override val axis: EnumFacing.Axis?,
|
override val axis: Axis?,
|
||||||
val topTexture: TextureAtlasSprite,
|
val topTexture: TextureAtlasSprite,
|
||||||
val bottomTexture: TextureAtlasSprite,
|
val bottomTexture: TextureAtlasSprite,
|
||||||
val sideTextures: List<TextureAtlasSprite>
|
val sideTextures: List<TextureAtlasSprite>
|
||||||
@@ -34,17 +31,17 @@ open class SimpleColumnInfo(
|
|||||||
override val top: QuadIconResolver = { _, _, _ -> topTexture }
|
override val top: QuadIconResolver = { _, _, _ -> topTexture }
|
||||||
override val bottom: QuadIconResolver = { _, _, _ -> bottomTexture }
|
override val bottom: QuadIconResolver = { _, _, _ -> bottomTexture }
|
||||||
override val side: QuadIconResolver = { ctx, idx, _ ->
|
override val side: QuadIconResolver = { ctx, idx, _ ->
|
||||||
val worldFace = (if ((idx and 1) == 0) EnumFacing.SOUTH else EnumFacing.EAST).rotate(ctx.rotation)
|
val worldFace = (if ((idx and 1) == 0) SOUTH else EAST).rotate(ctx.rotation)
|
||||||
val sideIdx = if (sideTextures.size > 1) (blockContext.random(1) + dirToIdx[worldFace.ordinal]) % sideTextures.size else 0
|
val sideIdx = if (sideTextures.size > 1) (blockContext.random(1) + dirToIdx[worldFace.ordinal]) % sideTextures.size else 0
|
||||||
sideTextures[sideIdx]
|
sideTextures[sideIdx]
|
||||||
}
|
}
|
||||||
|
|
||||||
class Key(override val logger: Logger, val axis: EnumFacing.Axis?, val textures: List<String>) : ModelRenderKey<ColumnTextureInfo> {
|
class Key(override val logger: Logger, val axis: Axis?, val textures: List<String>) : ModelRenderKey<ColumnTextureInfo> {
|
||||||
override fun resolveSprites(atlas: TextureMap) = SimpleColumnInfo(
|
override fun resolveSprites(atlas: AtlasTexture) = SimpleColumnInfo(
|
||||||
axis,
|
axis,
|
||||||
atlas[textures[0]] ?: atlas.missingSprite,
|
atlas[textures[0]] ?: missingSprite,
|
||||||
atlas[textures[1]] ?: atlas.missingSprite,
|
atlas[textures[1]] ?: missingSprite,
|
||||||
textures.drop(2).map { atlas[it] ?: atlas.missingSprite }
|
textures.drop(2).map { atlas[it] ?: missingSprite }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,9 @@ package mods.betterfoliage.client.texture
|
|||||||
|
|
||||||
import mods.octarinecore.client.resource.*
|
import mods.octarinecore.client.resource.*
|
||||||
import net.minecraft.util.ResourceLocation
|
import net.minecraft.util.ResourceLocation
|
||||||
|
import net.minecraftforge.resource.VanillaResourceType.TEXTURES
|
||||||
import java.awt.image.BufferedImage
|
import java.awt.image.BufferedImage
|
||||||
|
import java.io.InputStream
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate Short Grass textures from [Blocks.tallgrass] block textures.
|
* Generate Short Grass textures from [Blocks.tallgrass] block textures.
|
||||||
@@ -10,13 +12,16 @@ import java.awt.image.BufferedImage
|
|||||||
*
|
*
|
||||||
* @param[domain] Resource domain of generator
|
* @param[domain] Resource domain of generator
|
||||||
*/
|
*/
|
||||||
class GrassGenerator(domain: String) : TextureGenerator(domain) {
|
class GrassGenerator(domain: String) : GeneratorBase<GrassGenerator.Key>(domain, TEXTURES) {
|
||||||
|
|
||||||
override fun generate(params: ParameterList): BufferedImage? {
|
override val locationMapper = Atlas.BLOCKS::unwrap
|
||||||
val target = targetResource(params)!!
|
|
||||||
val isSnowed = params["snowed"]?.toBoolean() ?: false
|
|
||||||
|
|
||||||
val baseTexture = resourceManager[target.second]?.loadImage() ?: return null
|
fun register(texture: String, isSnowed: Boolean) = registerResource(Key(ResourceLocation(texture), isSnowed))
|
||||||
|
|
||||||
|
override fun exists(key: Key) = resourceManager.hasResource(Atlas.BLOCKS.wrap(key.texture))
|
||||||
|
|
||||||
|
override fun get(key: Key): InputStream? {
|
||||||
|
val baseTexture = resourceManager[Atlas.BLOCKS.wrap(key.texture)]?.loadImage() ?: return null
|
||||||
|
|
||||||
val result = BufferedImage(baseTexture.width, baseTexture.height, BufferedImage.TYPE_4BYTE_ABGR)
|
val result = BufferedImage(baseTexture.width, baseTexture.height, BufferedImage.TYPE_4BYTE_ABGR)
|
||||||
val graphics = result.createGraphics()
|
val graphics = result.createGraphics()
|
||||||
@@ -39,12 +44,14 @@ class GrassGenerator(domain: String) : TextureGenerator(domain) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// blend with white if snowed
|
// blend with white if snowed
|
||||||
if (isSnowed && target.first == ResourceType.COLOR) {
|
if (key.isSnowed) {
|
||||||
for (x in 0..result.width - 1) for (y in 0..result.height - 1) {
|
for (x in 0..result.width - 1) for (y in 0..result.height - 1) {
|
||||||
result[x, y] = blendRGB(result[x, y], 16777215, 2, 3)
|
result[x, y] = blendRGB(result[x, y], 16777215, 2, 3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result.asStream
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class Key(val texture: ResourceLocation, val isSnowed: Boolean)
|
||||||
}
|
}
|
||||||
@@ -1,25 +1,15 @@
|
|||||||
package mods.betterfoliage.client.texture
|
package mods.betterfoliage.client.texture
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
import mods.betterfoliage.BetterFoliage
|
||||||
import mods.betterfoliage.client.Client
|
import mods.betterfoliage.client.config.BlockConfig
|
||||||
import mods.betterfoliage.client.config.Config
|
import mods.betterfoliage.client.config.Config
|
||||||
import mods.octarinecore.client.render.BlockContext
|
|
||||||
import mods.octarinecore.client.render.HSB
|
import mods.octarinecore.client.render.HSB
|
||||||
import mods.octarinecore.client.resource.*
|
import mods.octarinecore.client.resource.*
|
||||||
import mods.octarinecore.common.Int3
|
|
||||||
import mods.octarinecore.common.config.ConfigurableBlockMatcher
|
import mods.octarinecore.common.config.ConfigurableBlockMatcher
|
||||||
import mods.octarinecore.common.config.IBlockMatcher
|
|
||||||
import mods.octarinecore.common.config.ModelTextureList
|
import mods.octarinecore.common.config.ModelTextureList
|
||||||
import mods.octarinecore.findFirst
|
import net.minecraft.block.BlockState
|
||||||
import net.minecraft.block.state.IBlockState
|
import net.minecraft.client.renderer.texture.AtlasTexture
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||||
import net.minecraft.client.renderer.texture.TextureMap
|
|
||||||
import net.minecraft.util.EnumFacing
|
|
||||||
import net.minecraft.util.math.BlockPos
|
|
||||||
import net.minecraft.world.IBlockAccess
|
|
||||||
import net.minecraftforge.common.MinecraftForge
|
|
||||||
import net.minecraftforge.fml.relauncher.Side
|
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
|
||||||
import org.apache.logging.log4j.Level
|
import org.apache.logging.log4j.Level
|
||||||
import org.apache.logging.log4j.Logger
|
import org.apache.logging.log4j.Logger
|
||||||
import java.lang.Math.min
|
import java.lang.Math.min
|
||||||
@@ -43,16 +33,17 @@ class GrassInfo(
|
|||||||
object GrassRegistry : ModelRenderRegistryRoot<GrassInfo>()
|
object GrassRegistry : ModelRenderRegistryRoot<GrassInfo>()
|
||||||
|
|
||||||
object StandardGrassRegistry : ModelRenderRegistryConfigurable<GrassInfo>() {
|
object StandardGrassRegistry : ModelRenderRegistryConfigurable<GrassInfo>() {
|
||||||
override val logger = BetterFoliageMod.logDetail
|
override val logger = BetterFoliage.logDetail
|
||||||
override val matchClasses: ConfigurableBlockMatcher get() = Config.blocks.grassClasses
|
override val matchClasses: ConfigurableBlockMatcher get() = BlockConfig.grassBlocks
|
||||||
override val modelTextures: List<ModelTextureList> get() = Config.blocks.grassModels.list
|
override val modelTextures: List<ModelTextureList> get() = BlockConfig.grassModels.modelList
|
||||||
override fun processModel(state: IBlockState, textures: List<String>) = StandardGrassKey(logger, textures[0])
|
override fun processModel(state: BlockState, textures: List<String>) = StandardGrassKey(logger, textures[0])
|
||||||
|
init { BetterFoliage.modBus.register(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
class StandardGrassKey(override val logger: Logger, val textureName: String) : ModelRenderKey<GrassInfo> {
|
class StandardGrassKey(override val logger: Logger, val textureName: String) : ModelRenderKey<GrassInfo> {
|
||||||
override fun resolveSprites(atlas: TextureMap): GrassInfo {
|
override fun resolveSprites(atlas: AtlasTexture): GrassInfo {
|
||||||
val logName = "StandardGrassKey"
|
val logName = "StandardGrassKey"
|
||||||
val texture = atlas[textureName] ?: atlas.missingSprite
|
val texture = atlas[textureName] ?: missingSprite
|
||||||
logger.log(Level.DEBUG, "$logName: texture $textureName")
|
logger.log(Level.DEBUG, "$logName: texture $textureName")
|
||||||
val hsb = HSB.fromColor(texture.averageColor ?: defaultGrassColor)
|
val hsb = HSB.fromColor(texture.averageColor ?: defaultGrassColor)
|
||||||
val overrideColor = if (hsb.saturation >= Config.shortGrass.saturationThreshold) {
|
val overrideColor = if (hsb.saturation >= Config.shortGrass.saturationThreshold) {
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
package mods.betterfoliage.client.texture
|
package mods.betterfoliage.client.texture
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
import mods.betterfoliage.BetterFoliage
|
||||||
import mods.octarinecore.client.resource.*
|
import mods.octarinecore.client.resource.*
|
||||||
import mods.octarinecore.stripStart
|
import mods.octarinecore.stripStart
|
||||||
|
import net.minecraft.resources.IResource
|
||||||
import net.minecraft.util.ResourceLocation
|
import net.minecraft.util.ResourceLocation
|
||||||
|
import net.minecraftforge.resource.VanillaResourceType.TEXTURES
|
||||||
import java.awt.image.BufferedImage
|
import java.awt.image.BufferedImage
|
||||||
|
import java.io.InputStream
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate round leaf textures from leaf block textures.
|
* Generate round leaf textures from leaf block textures.
|
||||||
@@ -14,22 +17,24 @@ import java.awt.image.BufferedImage
|
|||||||
*
|
*
|
||||||
* @param[domain] Resource domain of generator
|
* @param[domain] Resource domain of generator
|
||||||
*/
|
*/
|
||||||
class LeafGenerator(domain: String) : TextureGenerator(domain) {
|
class LeafGenerator(domain: String) : GeneratorBase<LeafGenerator.Key>(domain, TEXTURES) {
|
||||||
|
|
||||||
override fun generate(params: ParameterList): BufferedImage? {
|
override val locationMapper = Atlas.BLOCKS::unwrap
|
||||||
val target = targetResource(params)!!
|
|
||||||
val leafType = params["type"] ?: "default"
|
|
||||||
|
|
||||||
val handDrawnLoc = target.second.stripStart("textures/").stripStart("blocks/").let {
|
fun register(texture: ResourceLocation, leafType: String) = registerResource(Key(texture, leafType))
|
||||||
ResourceLocation(BetterFoliageMod.DOMAIN, "${it.namespace}/textures/blocks/${it.path}")
|
|
||||||
}
|
|
||||||
resourceManager[handDrawnLoc]?.loadImage()?.let { return it }
|
|
||||||
|
|
||||||
val baseTexture = resourceManager[target.second]?.loadImage() ?: return null
|
override fun exists(key: Key) = resourceManager.hasResource(Atlas.BLOCKS.wrap(key.texture))
|
||||||
|
|
||||||
|
override fun get(key: Key): InputStream? {
|
||||||
|
|
||||||
|
// val handDrawnLoc = Atlas.BLOCKS.wrap(key.texture)
|
||||||
|
// resourceManager[handDrawnLoc]?.loadImage()?.let { return it.asStream }
|
||||||
|
|
||||||
|
val baseTexture = resourceManager[Atlas.BLOCKS.wrap(key.texture)]?.loadImage() ?: return null
|
||||||
val size = baseTexture.width
|
val size = baseTexture.width
|
||||||
val frames = baseTexture.height / size
|
val frames = baseTexture.height / size
|
||||||
|
|
||||||
val maskTexture = (getLeafMask(leafType, size * 2) ?: getLeafMask("default", size * 2))?.loadImage()
|
val maskTexture = (getLeafMask(key.leafType, size * 2) ?: getLeafMask("default", size * 2))?.loadImage()
|
||||||
fun scale(i: Int) = i * maskTexture!!.width / (size * 2)
|
fun scale(i: Int) = i * maskTexture!!.width / (size * 2)
|
||||||
|
|
||||||
val leafTexture = BufferedImage(size * 2, size * 2 * frames, BufferedImage.TYPE_4BYTE_ABGR)
|
val leafTexture = BufferedImage(size * 2, size * 2 * frames, BufferedImage.TYPE_4BYTE_ABGR)
|
||||||
@@ -49,7 +54,7 @@ class LeafGenerator(domain: String) : TextureGenerator(domain) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// overlay alpha mask
|
// overlay alpha mask
|
||||||
if (target.first == ResourceType.COLOR && maskTexture != null) {
|
if (maskTexture != null) {
|
||||||
for (x in 0 .. size * 2 - 1) for (y in 0 .. size * 2 - 1) {
|
for (x in 0 .. size * 2 - 1) for (y in 0 .. size * 2 - 1) {
|
||||||
val basePixel = leafFrame[x, y].toLong() and 0xFFFFFFFFL
|
val basePixel = leafFrame[x, y].toLong() and 0xFFFFFFFFL
|
||||||
val maskPixel = maskTexture[scale(x), scale(y)].toLong() and 0xFF000000L or 0xFFFFFFL
|
val maskPixel = maskTexture[scale(x), scale(y)].toLong() and 0xFF000000L or 0xFFFFFFL
|
||||||
@@ -61,7 +66,7 @@ class LeafGenerator(domain: String) : TextureGenerator(domain) {
|
|||||||
graphics.drawImage(leafFrame, 0, size * frame * 2, null)
|
graphics.drawImage(leafFrame, 0, size * frame * 2, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
return leafTexture
|
return leafTexture.asStream
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,7 +76,22 @@ class LeafGenerator(domain: String) : TextureGenerator(domain) {
|
|||||||
* @param[maxSize] Preferred mask size.
|
* @param[maxSize] Preferred mask size.
|
||||||
*/
|
*/
|
||||||
fun getLeafMask(type: String, maxSize: Int) = getMultisizeTexture(maxSize) { size ->
|
fun getLeafMask(type: String, maxSize: Int) = getMultisizeTexture(maxSize) { size ->
|
||||||
ResourceLocation(BetterFoliageMod.DOMAIN, "textures/blocks/leafmask_${size}_${type}.png")
|
ResourceLocation(BetterFoliage.MOD_ID, "textures/blocks/leafmask_${size}_${type}.png")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a texture resource when multiple sizes may exist.
|
||||||
|
*
|
||||||
|
* @param[maxSize] Maximum size to consider. This value is progressively halved when searching for smaller versions.
|
||||||
|
* @param[maskPath] Location of the texture of the given size
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
fun getMultisizeTexture(maxSize: Int, maskPath: (Int)->ResourceLocation): IResource? {
|
||||||
|
var size = maxSize
|
||||||
|
val sizes = mutableListOf<Int>()
|
||||||
|
while(size > 2) { sizes.add(size); size /= 2 }
|
||||||
|
return sizes.map { resourceManager[maskPath(it)] }.filterNotNull().firstOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Key(val texture: ResourceLocation, val leafType: String)
|
||||||
}
|
}
|
||||||
@@ -1,40 +1,42 @@
|
|||||||
package mods.betterfoliage.client.texture
|
package mods.betterfoliage.client.texture
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
import mods.betterfoliage.BetterFoliage
|
||||||
import mods.octarinecore.client.resource.*
|
import mods.octarinecore.client.resource.*
|
||||||
import mods.octarinecore.stripStart
|
import mods.octarinecore.stripStart
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
|
||||||
import net.minecraft.util.ResourceLocation
|
import net.minecraft.util.ResourceLocation
|
||||||
import net.minecraftforge.client.event.TextureStitchEvent
|
import net.minecraftforge.client.event.TextureStitchEvent
|
||||||
import net.minecraftforge.common.MinecraftForge
|
import net.minecraftforge.common.MinecraftForge
|
||||||
import net.minecraftforge.fml.common.eventhandler.EventPriority
|
import net.minecraftforge.eventbus.api.EventPriority
|
||||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
|
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||||
|
|
||||||
object LeafParticleRegistry {
|
object LeafParticleRegistry {
|
||||||
|
val targetAtlas = Atlas.PARTICLES
|
||||||
val typeMappings = TextureMatcher()
|
val typeMappings = TextureMatcher()
|
||||||
val particles = hashMapOf<String, IconSet>()
|
val particles = hashMapOf<String, IconSet>()
|
||||||
|
|
||||||
operator fun get(type: String) = particles[type] ?: particles["default"]!!
|
operator fun get(type: String) = particles[type] ?: particles["default"]!!
|
||||||
|
|
||||||
init { MinecraftForge.EVENT_BUS.register(this) }
|
init { BetterFoliage.modBus.register(this) }
|
||||||
|
|
||||||
@SubscribeEvent(priority = EventPriority.HIGH)
|
|
||||||
fun handleLoadModelData(event: LoadModelDataEvent) {
|
|
||||||
particles.clear()
|
|
||||||
typeMappings.loadMappings(ResourceLocation(BetterFoliageMod.DOMAIN, "leaf_texture_mappings.cfg"))
|
|
||||||
}
|
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
fun handlePreStitch(event: TextureStitchEvent.Pre) {
|
fun handlePreStitch(event: TextureStitchEvent.Pre) {
|
||||||
|
if (!targetAtlas.matches(event)) return
|
||||||
|
|
||||||
|
particles.clear()
|
||||||
|
typeMappings.loadMappings(ResourceLocation(BetterFoliage.MOD_ID, "leaf_texture_mappings.cfg"))
|
||||||
|
|
||||||
val allTypes = (typeMappings.mappings.map { it.type } + "default").distinct()
|
val allTypes = (typeMappings.mappings.map { it.type } + "default").distinct()
|
||||||
allTypes.forEach { leafType ->
|
allTypes.forEach { leafType ->
|
||||||
val particleSet = IconSet("betterfoliage", "blocks/falling_leaf_${leafType}_%d").apply { onPreStitch(event.map) }
|
val particleSet = IconSet(Atlas.PARTICLES) {
|
||||||
|
idx -> ResourceLocation(BetterFoliage.MOD_ID, "falling_leaf_${leafType}_$idx")
|
||||||
|
}.apply { onPreStitch(event) }
|
||||||
if (leafType == "default" || particleSet.num > 0) particles[leafType] = particleSet
|
if (leafType == "default" || particleSet.num > 0) particles[leafType] = particleSet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
fun handlePostStitch(event: TextureStitchEvent.Post) {
|
fun handlePostStitch(event: TextureStitchEvent.Post) {
|
||||||
|
if (!targetAtlas.matches(event)) return
|
||||||
particles.forEach { (_, particleSet) -> particleSet.onPostStitch(event.map) }
|
particles.forEach { (_, particleSet) -> particleSet.onPostStitch(event.map) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,16 @@
|
|||||||
package mods.betterfoliage.client.texture
|
package mods.betterfoliage.client.texture
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
import mods.betterfoliage.BetterFoliage
|
||||||
import mods.betterfoliage.client.Client
|
import mods.betterfoliage.client.Client
|
||||||
import mods.betterfoliage.client.config.Config
|
import mods.betterfoliage.client.config.BlockConfig
|
||||||
import mods.octarinecore.client.render.BlockContext
|
|
||||||
import mods.octarinecore.client.resource.*
|
import mods.octarinecore.client.resource.*
|
||||||
import mods.octarinecore.common.Int3
|
|
||||||
import mods.octarinecore.common.config.ConfigurableBlockMatcher
|
import mods.octarinecore.common.config.ConfigurableBlockMatcher
|
||||||
import mods.octarinecore.common.config.IBlockMatcher
|
|
||||||
import mods.octarinecore.common.config.ModelTextureList
|
import mods.octarinecore.common.config.ModelTextureList
|
||||||
import mods.octarinecore.findFirst
|
import net.minecraft.block.BlockState
|
||||||
import net.minecraft.block.state.IBlockState
|
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||||
import net.minecraft.client.renderer.texture.TextureMap
|
import net.minecraft.client.renderer.texture.AtlasTexture
|
||||||
import net.minecraft.util.EnumFacing
|
|
||||||
import net.minecraft.util.ResourceLocation
|
import net.minecraft.util.ResourceLocation
|
||||||
import net.minecraft.util.math.BlockPos
|
|
||||||
import net.minecraft.world.IBlockAccess
|
|
||||||
import net.minecraftforge.client.event.TextureStitchEvent
|
import net.minecraftforge.client.event.TextureStitchEvent
|
||||||
import net.minecraftforge.common.MinecraftForge
|
|
||||||
import net.minecraftforge.fml.common.eventhandler.EventPriority
|
|
||||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
|
|
||||||
import net.minecraftforge.fml.relauncher.Side
|
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
|
||||||
import org.apache.logging.log4j.Level
|
import org.apache.logging.log4j.Level
|
||||||
import org.apache.logging.log4j.Logger
|
import org.apache.logging.log4j.Logger
|
||||||
|
|
||||||
@@ -46,25 +34,26 @@ class LeafInfo(
|
|||||||
object LeafRegistry : ModelRenderRegistryRoot<LeafInfo>()
|
object LeafRegistry : ModelRenderRegistryRoot<LeafInfo>()
|
||||||
|
|
||||||
object StandardLeafRegistry : ModelRenderRegistryConfigurable<LeafInfo>() {
|
object StandardLeafRegistry : ModelRenderRegistryConfigurable<LeafInfo>() {
|
||||||
override val logger = BetterFoliageMod.logDetail
|
override val logger = BetterFoliage.logDetail
|
||||||
override val matchClasses: ConfigurableBlockMatcher get() = Config.blocks.leavesClasses
|
override val matchClasses: ConfigurableBlockMatcher get() = BlockConfig.leafBlocks
|
||||||
override val modelTextures: List<ModelTextureList> get() = Config.blocks.leavesModels.list
|
override val modelTextures: List<ModelTextureList> get() = BlockConfig.leafModels.modelList
|
||||||
override fun processModel(state: IBlockState, textures: List<String>) = StandardLeafKey(logger, textures[0])
|
override fun processModel(state: BlockState, textures: List<String>) = StandardLeafKey(logger, textures[0])
|
||||||
|
init { BetterFoliage.modBus.register(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
class StandardLeafKey(override val logger: Logger, val textureName: String) : ModelRenderKey<LeafInfo> {
|
class StandardLeafKey(override val logger: Logger, val textureName: String) : ModelRenderKey<LeafInfo> {
|
||||||
lateinit var leafType: String
|
lateinit var leafType: String
|
||||||
lateinit var generated: ResourceLocation
|
lateinit var generated: ResourceLocation
|
||||||
|
|
||||||
override fun onPreStitch(atlas: TextureMap) {
|
override fun onPreStitch(event: TextureStitchEvent.Pre) {
|
||||||
val logName = "StandardLeafKey"
|
val logName = "StandardLeafKey"
|
||||||
leafType = LeafParticleRegistry.typeMappings.getType(textureName) ?: "default"
|
leafType = LeafParticleRegistry.typeMappings.getType(textureName) ?: "default"
|
||||||
generated = Client.genLeaves.generatedResource(textureName, "type" to leafType)
|
generated = Client.genLeaves.register(ResourceLocation(textureName), leafType)
|
||||||
atlas.registerSprite(generated)
|
event.addSprite(generated)
|
||||||
|
|
||||||
logger.log(Level.DEBUG, "$logName: leaf texture $textureName")
|
logger.log(Level.DEBUG, "$logName: leaf texture $textureName")
|
||||||
logger.log(Level.DEBUG, "$logName: particle $leafType")
|
logger.log(Level.DEBUG, "$logName: particle $leafType")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun resolveSprites(atlas: TextureMap) = LeafInfo(atlas[generated] ?: atlas.missingSprite, leafType)
|
override fun resolveSprites(atlas: AtlasTexture) = LeafInfo(atlas[generated] ?: missingSprite, leafType)
|
||||||
}
|
}
|
||||||
@@ -1,31 +1,23 @@
|
|||||||
package mods.betterfoliage.loader
|
package mods.betterfoliage.loader
|
||||||
|
|
||||||
import mods.octarinecore.metaprog.Transformer
|
//import mods.octarinecore.metaprog.Transformer
|
||||||
import mods.octarinecore.metaprog.allAvailable
|
import mods.octarinecore.metaprog.allAvailable
|
||||||
import net.minecraftforge.fml.relauncher.FMLLaunchHandler
|
|
||||||
import org.objectweb.asm.ClassWriter
|
|
||||||
import org.objectweb.asm.ClassWriter.COMPUTE_FRAMES
|
import org.objectweb.asm.ClassWriter.COMPUTE_FRAMES
|
||||||
import org.objectweb.asm.ClassWriter.COMPUTE_MAXS
|
import org.objectweb.asm.ClassWriter.COMPUTE_MAXS
|
||||||
import org.objectweb.asm.Opcodes.*
|
import org.objectweb.asm.Opcodes.*
|
||||||
|
|
||||||
|
/*
|
||||||
class BetterFoliageTransformer : Transformer() {
|
class BetterFoliageTransformer : Transformer() {
|
||||||
|
|
||||||
val isOptifinePresent = allAvailable(Refs.OptifineClassTransformer)
|
val isOptifinePresent = allAvailable(Refs.OptifineClassTransformer)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (FMLLaunchHandler.side().isClient) setupClient()
|
// where: WorldClient.animateTick(), replacing invocation to Block.animateTick
|
||||||
}
|
|
||||||
|
|
||||||
fun setupClient() {
|
|
||||||
// where: WorldClient.showBarrierParticles(), right after invoking Block.randomDisplayTick
|
|
||||||
// what: invoke BF code for every random display tick
|
// what: invoke BF code for every random display tick
|
||||||
// why: allows us to catch random display ticks, without touching block code
|
// why: allows us to catch random display ticks
|
||||||
transformMethod(Refs.showBarrierParticles) {
|
transformMethod(Refs.WC_animeteTick) {
|
||||||
find(invokeRef(Refs.randomDisplayTick))?.insertAfter {
|
find(invokeRef(Refs.B_animateTick))?.replace {
|
||||||
log.info("[BetterFoliageLoader] Applying random display tick call hook")
|
log.info("[BetterFoliageLoader] Applying random display tick call hook")
|
||||||
varinsn(ALOAD, 0)
|
|
||||||
varinsn(ALOAD, 11)
|
|
||||||
varinsn(ALOAD, 7)
|
|
||||||
invokeStatic(Refs.onRandomDisplayTick)
|
invokeStatic(Refs.onRandomDisplayTick)
|
||||||
} ?: log.warn("[BetterFoliageLoader] Failed to apply random display tick call hook!")
|
} ?: log.warn("[BetterFoliageLoader] Failed to apply random display tick call hook!")
|
||||||
}
|
}
|
||||||
@@ -58,9 +50,7 @@ class BetterFoliageTransformer : Transformer() {
|
|||||||
transformMethod(Refs.doesSideBlockRendering) {
|
transformMethod(Refs.doesSideBlockRendering) {
|
||||||
find(IRETURN)?.insertBefore {
|
find(IRETURN)?.insertBefore {
|
||||||
log.info("[BetterFoliageLoader] Applying doesSideBlockRendering() override")
|
log.info("[BetterFoliageLoader] Applying doesSideBlockRendering() override")
|
||||||
varinsn(ALOAD, 1)
|
varinsn(ALOAD, 0)
|
||||||
varinsn(ALOAD, 2)
|
|
||||||
varinsn(ALOAD, 3)
|
|
||||||
invokeStatic(Refs.doesSideBlockRenderingOverride)
|
invokeStatic(Refs.doesSideBlockRenderingOverride)
|
||||||
} ?: log.warn("[BetterFoliageLoader] Failed to apply doesSideBlockRendering() override!")
|
} ?: log.warn("[BetterFoliageLoader] Failed to apply doesSideBlockRendering() override!")
|
||||||
}
|
}
|
||||||
@@ -76,11 +66,11 @@ class BetterFoliageTransformer : Transformer() {
|
|||||||
} ?: log.warn("[BetterFoliageLoader] Failed to apply isOpaqueCube() override!")
|
} ?: log.warn("[BetterFoliageLoader] Failed to apply isOpaqueCube() override!")
|
||||||
}
|
}
|
||||||
|
|
||||||
// where: ModelLoader.setupModelRegistry(), right before the textures are loaded
|
// where: ModelBakery.setupModelRegistry(), after all models are loaded
|
||||||
// what: invoke handler code with ModelLoader instance
|
// what: invoke handler code with ModelBakery instance
|
||||||
// why: allows us to iterate the unbaked models in ModelLoader in time to register textures
|
// why: allows us to iterate the unbaked models in ModelBakery in time to register textures
|
||||||
transformMethod(Refs.setupModelRegistry) {
|
transformMethod(Refs.setupModelRegistry) {
|
||||||
find(invokeName("addAll"))?.insertAfter {
|
find(invokeName("newLinkedHashSet"))?.insertBefore {
|
||||||
log.info("[BetterFoliageLoader] Applying ModelLoader lifecycle callback")
|
log.info("[BetterFoliageLoader] Applying ModelLoader lifecycle callback")
|
||||||
varinsn(ALOAD, 0)
|
varinsn(ALOAD, 0)
|
||||||
invokeStatic(Refs.onAfterLoadModelDefinitions)
|
invokeStatic(Refs.onAfterLoadModelDefinitions)
|
||||||
@@ -136,3 +126,5 @@ class BetterFoliageTransformer : Transformer() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
@@ -3,11 +3,10 @@ package mods.betterfoliage.loader
|
|||||||
import mods.octarinecore.metaprog.ClassRef
|
import mods.octarinecore.metaprog.ClassRef
|
||||||
import mods.octarinecore.metaprog.FieldRef
|
import mods.octarinecore.metaprog.FieldRef
|
||||||
import mods.octarinecore.metaprog.MethodRef
|
import mods.octarinecore.metaprog.MethodRef
|
||||||
import net.minecraftforge.fml.relauncher.FMLInjectionData
|
|
||||||
|
|
||||||
/** Singleton object holding references to foreign code elements. */
|
/** Singleton object holding references to foreign code elements. */
|
||||||
object Refs {
|
object Refs {
|
||||||
val mcVersion = FMLInjectionData.data()[4].toString()
|
// val mcVersion = FMLInjectionData.data()[4].toString()
|
||||||
|
|
||||||
// Java
|
// Java
|
||||||
val String = ClassRef("java.lang.String")
|
val String = ClassRef("java.lang.String")
|
||||||
@@ -16,102 +15,44 @@ object Refs {
|
|||||||
val Random = ClassRef("java.util.Random")
|
val Random = ClassRef("java.util.Random")
|
||||||
|
|
||||||
// Minecraft
|
// Minecraft
|
||||||
val IBlockAccess = ClassRef("net.minecraft.world.IBlockAccess")
|
val IBlockReader = ClassRef("net.minecraft.world.IBlockReader")
|
||||||
val IBlockState = ClassRef("net.minecraft.block.state.IBlockState")
|
val IEnvironmentBlockReader = ClassRef("net.minecraft.world.IEnvironmentBlockReader")
|
||||||
val BlockStateBase = ClassRef("net.minecraft.block.state.BlockStateBase")
|
val BlockState = ClassRef("net.minecraft.block.state.BlockState")
|
||||||
val BlockPos = ClassRef("net.minecraft.util.math.BlockPos")
|
val BlockPos = ClassRef("net.minecraft.util.math.BlockPos")
|
||||||
val MutableBlockPos = ClassRef("net.minecraft.util.math.BlockPos\$MutableBlockPos")
|
|
||||||
val BlockRenderLayer = ClassRef("net.minecraft.util.BlockRenderLayer")
|
val BlockRenderLayer = ClassRef("net.minecraft.util.BlockRenderLayer")
|
||||||
val EnumFacing = ClassRef("net.minecraft.util.EnumFacing")
|
|
||||||
|
|
||||||
val World = ClassRef("net.minecraft.world.World")
|
|
||||||
val WorldClient = ClassRef("net.minecraft.client.multiplayer.WorldClient")
|
|
||||||
val ChunkCache = ClassRef("net.minecraft.world.ChunkCache")
|
|
||||||
val showBarrierParticles = MethodRef(WorldClient, "showBarrierParticles", "func_184153_a", ClassRef.void, ClassRef.int, ClassRef.int, ClassRef.int, ClassRef.int, Random, ClassRef.boolean, MutableBlockPos)
|
|
||||||
|
|
||||||
val Block = ClassRef("net.minecraft.block.Block")
|
val Block = ClassRef("net.minecraft.block.Block")
|
||||||
val StateImplementation = ClassRef("net.minecraft.block.state.BlockStateContainer\$StateImplementation")
|
|
||||||
val canRenderInLayer = MethodRef(Block, "canRenderInLayer", ClassRef.boolean, IBlockState, BlockRenderLayer)
|
|
||||||
val getAmbientOcclusionLightValue = MethodRef(StateImplementation, "getAmbientOcclusionLightValue", "func_185892_j", ClassRef.float)
|
|
||||||
val useNeighborBrightness = MethodRef(StateImplementation, "useNeighborBrightness", "func_185916_f", ClassRef.boolean)
|
|
||||||
val doesSideBlockRendering = MethodRef(StateImplementation, "doesSideBlockRendering", ClassRef.boolean, IBlockAccess, BlockPos, EnumFacing)
|
|
||||||
val isOpaqueCube = MethodRef(StateImplementation, "isOpaqueCube", "func_185914_p", ClassRef.boolean)
|
|
||||||
val randomDisplayTick = MethodRef(Block, "randomDisplayTick", "func_180655_c", ClassRef.void, IBlockState, World, BlockPos, Random)
|
|
||||||
|
|
||||||
val BlockModelRenderer = ClassRef("net.minecraft.client.renderer.BlockModelRenderer")
|
|
||||||
val AmbientOcclusionFace = ClassRef("net.minecraft.client.renderer.BlockModelRenderer\$AmbientOcclusionFace")
|
|
||||||
val ChunkCompileTaskGenerator = ClassRef("net.minecraft.client.renderer.chunk.ChunkCompileTaskGenerator")
|
|
||||||
val BufferBuilder = ClassRef("net.minecraft.client.renderer.BufferBuilder")
|
val BufferBuilder = ClassRef("net.minecraft.client.renderer.BufferBuilder")
|
||||||
val AOF_constructor = MethodRef(AmbientOcclusionFace, "<init>", ClassRef.void, BlockModelRenderer)
|
|
||||||
|
|
||||||
val RenderChunk = ClassRef("net.minecraft.client.renderer.chunk.RenderChunk")
|
|
||||||
val rebuildChunk = MethodRef(RenderChunk, "rebuildChunk", "func_178581_b", ClassRef.void, ClassRef.float, ClassRef.float, ClassRef.float, ChunkCompileTaskGenerator)
|
|
||||||
|
|
||||||
val BlockRendererDispatcher = ClassRef("net.minecraft.client.renderer.BlockRendererDispatcher")
|
val BlockRendererDispatcher = ClassRef("net.minecraft.client.renderer.BlockRendererDispatcher")
|
||||||
val renderBlock = MethodRef(BlockRendererDispatcher, "renderBlock", "func_175018_a", ClassRef.boolean, IBlockState, BlockPos, IBlockAccess, BufferBuilder)
|
val ChunkCache = ClassRef("net.minecraft.client.renderer.chunk.ChunkRenderCache")
|
||||||
|
|
||||||
val TextureAtlasSprite = ClassRef("net.minecraft.client.renderer.texture.TextureAtlasSprite")
|
val TextureAtlasSprite = ClassRef("net.minecraft.client.renderer.texture.TextureAtlasSprite")
|
||||||
|
|
||||||
val IRegistry = ClassRef("net.minecraft.util.registry.IRegistry")
|
|
||||||
val ModelLoader = ClassRef("net.minecraftforge.client.model.ModelLoader")
|
|
||||||
val stateModels = FieldRef(ModelLoader, "stateModels", Map)
|
|
||||||
val setupModelRegistry = MethodRef(ModelLoader, "setupModelRegistry", "func_177570_a", IRegistry)
|
|
||||||
|
|
||||||
val IModel = ClassRef("net.minecraftforge.client.model.IModel")
|
|
||||||
val ModelBlock = ClassRef("net.minecraft.client.renderer.block.model.ModelBlock")
|
|
||||||
val ResourceLocation = ClassRef("net.minecraft.util.ResourceLocation")
|
val ResourceLocation = ClassRef("net.minecraft.util.ResourceLocation")
|
||||||
val ModelResourceLocation = ClassRef("net.minecraft.client.renderer.block.model.ModelResourceLocation")
|
|
||||||
val VanillaModelWrapper = ClassRef("net.minecraftforge.client.model.ModelLoader\$VanillaModelWrapper")
|
|
||||||
val model_VMW = FieldRef(VanillaModelWrapper, "model", ModelBlock)
|
|
||||||
val location_VMW = FieldRef(VanillaModelWrapper, "location", ModelBlock)
|
|
||||||
val WeightedRandomModel = ClassRef("net.minecraftforge.client.model.ModelLoader\$WeightedRandomModel")
|
|
||||||
val models_WRM = FieldRef(WeightedRandomModel, "models", List)
|
|
||||||
val MultiModel = ClassRef("net.minecraftforge.client.model.MultiModel")
|
|
||||||
val base_MM = FieldRef(MultiModel, "base", IModel)
|
|
||||||
val MultipartModel = ClassRef("net.minecraftforge.client.model.ModelLoader\$MultipartModel")
|
|
||||||
val partModels_MPM = FieldRef(MultipartModel, "partModels", List)
|
|
||||||
|
|
||||||
val BakedQuad = ClassRef("net.minecraft.client.renderer.block.model.BakedQuad")
|
|
||||||
|
|
||||||
val resetChangedState = MethodRef(ClassRef("net.minecraftforge.common.config.Configuration"), "resetChangedState", ClassRef.void)
|
|
||||||
|
|
||||||
|
|
||||||
// Better Foliage
|
|
||||||
val BetterFoliageHooks = ClassRef("mods.betterfoliage.client.Hooks")
|
|
||||||
val getAmbientOcclusionLightValueOverride = MethodRef(BetterFoliageHooks, "getAmbientOcclusionLightValueOverride", ClassRef.float, ClassRef.float, IBlockState)
|
|
||||||
val useNeighborBrightnessOverride = MethodRef(BetterFoliageHooks, "getUseNeighborBrightnessOverride", ClassRef.boolean, ClassRef.boolean, IBlockState)
|
|
||||||
val doesSideBlockRenderingOverride = MethodRef(BetterFoliageHooks, "doesSideBlockRenderingOverride", ClassRef.boolean, ClassRef.boolean, IBlockAccess, BlockPos, EnumFacing)
|
|
||||||
val isOpaqueCubeOverride = MethodRef(BetterFoliageHooks, "isOpaqueCubeOverride", ClassRef.boolean, ClassRef.boolean, IBlockState)
|
|
||||||
val onRandomDisplayTick = MethodRef(BetterFoliageHooks, "onRandomDisplayTick", ClassRef.void, World, IBlockState, BlockPos)
|
|
||||||
val onAfterLoadModelDefinitions = MethodRef(BetterFoliageHooks, "onAfterLoadModelDefinitions", ClassRef.void, ModelLoader)
|
|
||||||
val onAfterBakeModels = MethodRef(BetterFoliageHooks, "onAfterBakeModels", ClassRef.void, Map)
|
|
||||||
val renderWorldBlock = MethodRef(BetterFoliageHooks, "renderWorldBlock", ClassRef.boolean, BlockRendererDispatcher, IBlockState, BlockPos, IBlockAccess, BufferBuilder, BlockRenderLayer)
|
|
||||||
val canRenderBlockInLayer = MethodRef(BetterFoliageHooks, "canRenderBlockInLayer", ClassRef.boolean, Block, IBlockState, BlockRenderLayer)
|
|
||||||
|
|
||||||
// Optifine
|
// Optifine
|
||||||
val OptifineClassTransformer = ClassRef("optifine.OptiFineClassTransformer")
|
val OptifineClassTransformer = ClassRef("optifine.OptiFineClassTransformer")
|
||||||
val OptifineChunkCache = ClassRef("net.optifine.override.ChunkCacheOF")
|
val OptifineChunkCache = ClassRef("net.optifine.override.ChunkCacheOF")
|
||||||
val CCOFChunkCache = FieldRef(OptifineChunkCache, "chunkCache", ChunkCache)
|
val CCOFChunkCache = FieldRef(OptifineChunkCache, "chunkCache", ChunkCache)
|
||||||
|
|
||||||
val getBlockId = MethodRef(BlockStateBase, "getBlockId", ClassRef.int);
|
val getBlockId = MethodRef(BlockState, "getBlockId", ClassRef.int);
|
||||||
val getMetadata = MethodRef(BlockStateBase, "getMetadata", ClassRef.int);
|
val getMetadata = MethodRef(BlockState, "getMetadata", ClassRef.int);
|
||||||
|
|
||||||
// Optifine
|
// Optifine
|
||||||
val RenderEnv = ClassRef("net.optifine.render.RenderEnv")
|
val RenderEnv = ClassRef("net.optifine.render.RenderEnv")
|
||||||
val RenderEnv_reset = MethodRef(RenderEnv, "reset", ClassRef.void, IBlockAccess, IBlockState, BlockPos)
|
val RenderEnv_reset = MethodRef(RenderEnv, "reset", ClassRef.void, IBlockReader, BlockState, BlockPos)
|
||||||
val quadSprite = FieldRef(BufferBuilder, "quadSprite", TextureAtlasSprite)
|
val quadSprite = FieldRef(BufferBuilder, "quadSprite", TextureAtlasSprite)
|
||||||
|
val BlockPosM = ClassRef("net.optifine.BlockPosM")
|
||||||
|
val IColorizer = ClassRef("net.optifine.CustomColors\$IColorizer")
|
||||||
|
|
||||||
// Optifine: custom colors
|
// Optifine: custom colors
|
||||||
val CustomColors = ClassRef("net.optifine.CustomColors")
|
val CustomColors = ClassRef("net.optifine.CustomColors")
|
||||||
val getColorMultiplier = MethodRef(CustomColors, "getColorMultiplier", ClassRef.int, BakedQuad, IBlockState, IBlockAccess, BlockPos, RenderEnv)
|
val getColorMultiplier = MethodRef(CustomColors, "getSmoothColorMultiplier", ClassRef.int, BlockState, IEnvironmentBlockReader, BlockPos, IColorizer, BlockPosM)
|
||||||
|
|
||||||
// Optifine: shaders
|
// Optifine: shaders
|
||||||
val SVertexBuilder = ClassRef("net.optifine.shaders.SVertexBuilder")
|
val SVertexBuilder = ClassRef("net.optifine.shaders.SVertexBuilder")
|
||||||
val sVertexBuilder = FieldRef(BufferBuilder, "sVertexBuilder", SVertexBuilder)
|
val sVertexBuilder = FieldRef(BufferBuilder, "sVertexBuilder", SVertexBuilder)
|
||||||
val pushEntity_state = MethodRef(SVertexBuilder, "pushEntity", ClassRef.void, IBlockState, BlockPos, IBlockAccess, BufferBuilder)
|
val pushEntity_state = MethodRef(SVertexBuilder, "pushEntity", ClassRef.void, BlockState, BlockPos, IBlockReader, BufferBuilder)
|
||||||
val pushEntity_num = MethodRef(SVertexBuilder, "pushEntity", ClassRef.void, ClassRef.long)
|
val pushEntity_num = MethodRef(SVertexBuilder, "pushEntity", ClassRef.void, ClassRef.long)
|
||||||
val popEntity = MethodRef(SVertexBuilder, "popEntity", ClassRef.void)
|
val popEntity = MethodRef(SVertexBuilder, "popEntity", ClassRef.void)
|
||||||
|
|
||||||
val ShadersModIntegration = ClassRef("mods.betterfoliage.client.integration.ShadersModIntegration")
|
val ShadersModIntegration = ClassRef("mods.betterfoliage.client.integration.ShadersModIntegration")
|
||||||
val getBlockIdOverride = MethodRef(ShadersModIntegration, "getBlockIdOverride", ClassRef.long, ClassRef.long, IBlockState)
|
val getBlockIdOverride = MethodRef(ShadersModIntegration, "getBlockIdOverride", ClassRef.long, ClassRef.long, BlockState)
|
||||||
}
|
}
|
||||||
@@ -6,19 +6,19 @@ import mods.betterfoliage.loader.Refs
|
|||||||
import net.minecraft.tileentity.TileEntity
|
import net.minecraft.tileentity.TileEntity
|
||||||
import net.minecraft.util.ResourceLocation
|
import net.minecraft.util.ResourceLocation
|
||||||
import net.minecraft.util.math.BlockPos
|
import net.minecraft.util.math.BlockPos
|
||||||
import net.minecraft.world.ChunkCache
|
import net.minecraft.world.*
|
||||||
import net.minecraft.world.IBlockAccess
|
|
||||||
import net.minecraft.world.World
|
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
import java.lang.Math.*
|
import java.lang.Math.*
|
||||||
|
|
||||||
const val PI2 = 2.0 * PI
|
const val PI2 = 2.0 * PI
|
||||||
|
|
||||||
/** Strip the given prefix off the start of the string, if present */
|
/** Strip the given prefix off the start of the string, if present */
|
||||||
inline fun String.stripStart(str: String) = if (startsWith(str)) substring(str.length) else this
|
inline fun String.stripStart(str: String, ignoreCase: Boolean = true) = if (startsWith(str, ignoreCase)) substring(str.length) else this
|
||||||
|
inline fun String.stripEnd(str: String, ignoreCase: Boolean = true) = if (endsWith(str, ignoreCase)) substring(0, length - str.length) else this
|
||||||
|
|
||||||
/** Strip the given prefix off the start of the resource path, if present */
|
/** Strip the given prefix off the start of the resource path, if present */
|
||||||
inline fun ResourceLocation.stripStart(str: String) = ResourceLocation(namespace, path.stripStart(str))
|
inline fun ResourceLocation.stripStart(str: String) = ResourceLocation(namespace, path.stripStart(str))
|
||||||
|
inline fun ResourceLocation.stripEnd(str: String) = ResourceLocation(namespace, path.stripEnd(str))
|
||||||
|
|
||||||
/** Mutating version of _map_. Replace each element of the list with the result of the given transformation. */
|
/** Mutating version of _map_. Replace each element of the list with the result of the given transformation. */
|
||||||
inline fun <reified T> MutableList<T>.replace(transform: (T) -> T) = forEachIndexed { idx, value -> this[idx] = transform(value) }
|
inline fun <reified T> MutableList<T>.replace(transform: (T) -> T) = forEachIndexed { idx, value -> this[idx] = transform(value) }
|
||||||
@@ -104,17 +104,18 @@ fun nextPowerOf2(x: Int): Int {
|
|||||||
* Check if the Chunk containing the given [BlockPos] is loaded.
|
* Check if the Chunk containing the given [BlockPos] is loaded.
|
||||||
* Works for both [World] and [ChunkCache] (vanilla and OptiFine) instances.
|
* Works for both [World] and [ChunkCache] (vanilla and OptiFine) instances.
|
||||||
*/
|
*/
|
||||||
fun IBlockAccess.isBlockLoaded(pos: BlockPos) = when {
|
//fun IWorldReader.isBlockLoaded(pos: BlockPos) = when {
|
||||||
this is World -> isBlockLoaded(pos, false)
|
// this is World -> isBlockLoaded(pos, false)
|
||||||
this is ChunkCache -> world.isBlockLoaded(pos, false)
|
// this is RenderChunkCache -> isworld.isBlockLoaded(pos, false)
|
||||||
Refs.OptifineChunkCache.isInstance(this) -> (Refs.CCOFChunkCache.get(this) as ChunkCache).world.isBlockLoaded(pos, false)
|
// Refs.OptifineChunkCache.isInstance(this) -> (Refs.CCOFChunkCache.get(this) as ChunkCache).world.isBlockLoaded(pos, false)
|
||||||
else -> false
|
// else -> false
|
||||||
}
|
//}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the [TileEntity] at the given position, suppressing exceptions.
|
* Get the [TileEntity] at the given position, suppressing exceptions.
|
||||||
* Also returns null if the chunk is unloaded, which can happen because of multithreaded rendering.
|
* Also returns null if the chunk is unloaded, which can happen because of multithreaded rendering.
|
||||||
*/
|
*/
|
||||||
fun IBlockAccess.getTileEntitySafe(pos: BlockPos): TileEntity? = tryDefault(null as TileEntity?) {
|
fun IWorldReader.getTileEntitySafe(pos: BlockPos): TileEntity? = tryDefault(null as TileEntity?) {
|
||||||
if (isBlockLoaded(pos)) getTileEntity(pos) else null
|
if (isBlockLoaded(pos)) getTileEntity(pos) else null
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
package mods.octarinecore.client
|
package mods.octarinecore.client
|
||||||
|
|
||||||
import net.minecraft.client.settings.KeyBinding
|
import net.minecraft.client.settings.KeyBinding
|
||||||
|
import net.minecraftforge.client.event.InputEvent
|
||||||
|
import net.minecraftforge.common.MinecraftForge
|
||||||
|
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||||
import net.minecraftforge.fml.client.registry.ClientRegistry
|
import net.minecraftforge.fml.client.registry.ClientRegistry
|
||||||
import net.minecraftforge.fml.common.FMLCommonHandler
|
|
||||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
|
|
||||||
import net.minecraftforge.fml.common.gameevent.InputEvent
|
|
||||||
|
|
||||||
class KeyHandler(val modId: String, val defaultKey: Int, val lang: String, val action: (InputEvent.KeyInputEvent)->Unit) {
|
class KeyHandler(val modId: String, val defaultKey: Int, val lang: String, val action: (InputEvent.KeyInputEvent)->Unit) {
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ class KeyHandler(val modId: String, val defaultKey: Int, val lang: String, val a
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
ClientRegistry.registerKeyBinding(keyBinding)
|
ClientRegistry.registerKeyBinding(keyBinding)
|
||||||
FMLCommonHandler.instance().bus().register(this)
|
MinecraftForge.EVENT_BUS.register(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
package mods.octarinecore.client.gui
|
|
||||||
|
|
||||||
import net.minecraft.client.gui.GuiScreen
|
|
||||||
import net.minecraft.client.resources.I18n
|
|
||||||
import net.minecraft.util.text.TextFormatting.*
|
|
||||||
import net.minecraftforge.fml.client.config.*
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for a config GUI element.
|
|
||||||
* The GUI representation is a list of toggleable objects.
|
|
||||||
* The config representation is an integer list of the selected objects' IDs.
|
|
||||||
*/
|
|
||||||
abstract class IdListConfigEntry<T>(
|
|
||||||
owningScreen: GuiConfig,
|
|
||||||
owningEntryList: GuiConfigEntries,
|
|
||||||
configElement: IConfigElement
|
|
||||||
) : GuiConfigEntries.CategoryEntry(owningScreen, owningEntryList, configElement) {
|
|
||||||
|
|
||||||
/** Create the child GUI elements. */
|
|
||||||
fun createChildren() = baseSet.map {
|
|
||||||
ItemWrapperElement(it, it.itemId in configElement.list, it.itemId in configElement.defaults)
|
|
||||||
}
|
|
||||||
|
|
||||||
init { stripTooltipDefaultText(toolTip as MutableList<String>) }
|
|
||||||
|
|
||||||
override fun buildChildScreen(): GuiScreen {
|
|
||||||
return GuiConfig(
|
|
||||||
this.owningScreen,
|
|
||||||
createChildren(),
|
|
||||||
this.owningScreen.modID,
|
|
||||||
owningScreen.allRequireWorldRestart || this.configElement.requiresWorldRestart(),
|
|
||||||
owningScreen.allRequireMcRestart || this.configElement.requiresMcRestart(),
|
|
||||||
this.owningScreen.title,
|
|
||||||
(if (this.owningScreen.titleLine2 == null) "" else this.owningScreen.titleLine2) + " > " + this.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun saveConfigElement(): Boolean {
|
|
||||||
val requiresRestart = (childScreen as GuiConfig).entryList.saveConfigElements()
|
|
||||||
val children = (childScreen as GuiConfig).configElements as List<ItemWrapperElement>
|
|
||||||
val ids = children.filter { it.booleanValue == true }.map { it.item.itemId }
|
|
||||||
configElement.set(ids.sorted().toTypedArray())
|
|
||||||
return requiresRestart
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract val baseSet: List<T>
|
|
||||||
abstract val T.itemId: Int
|
|
||||||
abstract val T.itemName: String
|
|
||||||
|
|
||||||
/** Child config GUI element of a single toggleable object. */
|
|
||||||
inner class ItemWrapperElement(val item: T, value: Boolean, val default: Boolean) :
|
|
||||||
DummyConfigElement(item.itemName, default, ConfigGuiType.BOOLEAN, item.itemName) {
|
|
||||||
|
|
||||||
init {
|
|
||||||
this.value = value
|
|
||||||
this.defaultValue = default
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getComment() = I18n.format("${configElement.languageKey}.tooltip.element", "${GOLD}${item.itemName}${YELLOW}")
|
|
||||||
val booleanValue: Boolean get() = defaultValue as Boolean
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
package mods.octarinecore.client.gui
|
|
||||||
|
|
||||||
import net.minecraft.client.resources.I18n
|
|
||||||
import net.minecraft.util.text.TextFormatting.*
|
|
||||||
import net.minecraftforge.fml.client.config.GuiConfig
|
|
||||||
import net.minecraftforge.fml.client.config.GuiConfigEntries
|
|
||||||
import net.minecraftforge.fml.client.config.IConfigElement
|
|
||||||
|
|
||||||
class NonVerboseArrayEntry(
|
|
||||||
owningScreen: GuiConfig,
|
|
||||||
owningEntryList: GuiConfigEntries,
|
|
||||||
configElement: IConfigElement
|
|
||||||
) : GuiConfigEntries.ArrayEntry(owningScreen, owningEntryList, configElement) {
|
|
||||||
|
|
||||||
init {
|
|
||||||
stripTooltipDefaultText(toolTip as MutableList<String>)
|
|
||||||
val shortDefaults = I18n.format("${configElement.languageKey}.arrayEntry", configElement.defaults.size)
|
|
||||||
toolTip.addAll(mc.fontRenderer.listFormattedStringToWidth("$AQUA${I18n.format("fml.configgui.tooltip.default", shortDefaults)}", 300))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun updateValueButtonText() {
|
|
||||||
btnValue.displayString = I18n.format("${configElement.languageKey}.arrayEntry", currentValues.size)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
@file:JvmName("Utils")
|
@file:JvmName("Utils")
|
||||||
package mods.octarinecore.client.gui
|
package mods.octarinecore.client.gui
|
||||||
|
|
||||||
|
import net.minecraft.util.text.StringTextComponent
|
||||||
import net.minecraft.util.text.Style
|
import net.minecraft.util.text.Style
|
||||||
import net.minecraft.util.text.TextComponentString
|
|
||||||
import net.minecraft.util.text.TextFormatting
|
import net.minecraft.util.text.TextFormatting
|
||||||
import net.minecraft.util.text.TextFormatting.AQUA
|
import net.minecraft.util.text.TextFormatting.AQUA
|
||||||
import net.minecraft.util.text.TextFormatting.GRAY
|
import net.minecraft.util.text.TextFormatting.GRAY
|
||||||
@@ -16,7 +16,7 @@ fun stripTooltipDefaultText(tooltip: MutableList<String>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun textComponent(msg: String, color: TextFormatting = GRAY): TextComponentString {
|
fun textComponent(msg: String, color: TextFormatting = GRAY): StringTextComponent {
|
||||||
val style = Style().apply { this.color = color }
|
val style = Style().apply { this.color = color }
|
||||||
return TextComponentString(msg).apply { this.style = style }
|
return StringTextComponent(msg).apply { this.style = style }
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
package mods.octarinecore.client.render
|
package mods.octarinecore.client.render
|
||||||
|
|
||||||
import mods.betterfoliage.client.render.canRenderInCutout
|
import mods.betterfoliage.client.render.canRenderInCutout
|
||||||
import mods.betterfoliage.client.render.canRenderInLayer
|
|
||||||
import mods.betterfoliage.client.render.isCutout
|
import mods.betterfoliage.client.render.isCutout
|
||||||
import mods.octarinecore.ThreadLocalDelegate
|
import mods.octarinecore.ThreadLocalDelegate
|
||||||
import mods.octarinecore.client.resource.ResourceHandler
|
import mods.octarinecore.client.resource.ResourceHandler
|
||||||
@@ -12,16 +11,21 @@ import mods.octarinecore.common.forgeDirOffsets
|
|||||||
import mods.octarinecore.common.plus
|
import mods.octarinecore.common.plus
|
||||||
import mods.octarinecore.semiRandom
|
import mods.octarinecore.semiRandom
|
||||||
import net.minecraft.block.Block
|
import net.minecraft.block.Block
|
||||||
import net.minecraft.block.state.IBlockState
|
import net.minecraft.block.BlockState
|
||||||
import net.minecraft.client.Minecraft
|
import net.minecraft.client.Minecraft
|
||||||
import net.minecraft.client.renderer.BlockRendererDispatcher
|
import net.minecraft.client.renderer.BlockRendererDispatcher
|
||||||
import net.minecraft.client.renderer.BufferBuilder
|
import net.minecraft.client.renderer.BufferBuilder
|
||||||
import net.minecraft.client.renderer.color.BlockColors
|
import net.minecraft.client.renderer.color.BlockColors
|
||||||
import net.minecraft.util.BlockRenderLayer
|
import net.minecraft.util.BlockRenderLayer
|
||||||
|
import net.minecraft.util.Direction
|
||||||
import net.minecraft.util.math.BlockPos
|
import net.minecraft.util.math.BlockPos
|
||||||
import net.minecraft.util.math.MathHelper
|
import net.minecraft.util.math.MathHelper
|
||||||
import net.minecraft.world.IBlockAccess
|
import net.minecraft.world.IBlockReader
|
||||||
|
import net.minecraft.world.IEnviromentBlockReader
|
||||||
import net.minecraft.world.biome.Biome
|
import net.minecraft.world.biome.Biome
|
||||||
|
import net.minecraftforge.client.model.data.IModelData
|
||||||
|
import net.minecraftforge.eventbus.api.IEventBus
|
||||||
|
import java.util.*
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -36,7 +40,7 @@ val modelRenderer by ThreadLocalDelegate { ModelRenderer() }
|
|||||||
|
|
||||||
val blockColors = ThreadLocal<BlockColors>()
|
val blockColors = ThreadLocal<BlockColors>()
|
||||||
|
|
||||||
abstract class AbstractBlockRenderingHandler(modId: String) : ResourceHandler(modId) {
|
abstract class AbstractBlockRenderingHandler(modId: String, modBus: IEventBus) : ResourceHandler(modId, modBus) {
|
||||||
|
|
||||||
open val addToCutout: Boolean get() = true
|
open val addToCutout: Boolean get() = true
|
||||||
|
|
||||||
@@ -44,7 +48,7 @@ abstract class AbstractBlockRenderingHandler(modId: String) : ResourceHandler(mo
|
|||||||
// Custom rendering
|
// Custom rendering
|
||||||
// ============================
|
// ============================
|
||||||
abstract fun isEligible(ctx: BlockContext): Boolean
|
abstract fun isEligible(ctx: BlockContext): Boolean
|
||||||
abstract fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: BufferBuilder, layer: BlockRenderLayer): Boolean
|
abstract fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: BufferBuilder, random: Random, modelData: IModelData, layer: BlockRenderLayer): Boolean
|
||||||
|
|
||||||
// ============================
|
// ============================
|
||||||
// Vanilla rendering wrapper
|
// Vanilla rendering wrapper
|
||||||
@@ -52,12 +56,12 @@ abstract class AbstractBlockRenderingHandler(modId: String) : ResourceHandler(mo
|
|||||||
/**
|
/**
|
||||||
* Render the block in the current [BlockContext]
|
* Render the block in the current [BlockContext]
|
||||||
*/
|
*/
|
||||||
fun renderWorldBlockBase(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: BufferBuilder, layer: BlockRenderLayer?): Boolean {
|
fun renderWorldBlockBase(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: BufferBuilder, random: Random, modelData: IModelData, layer: BlockRenderLayer?): Boolean {
|
||||||
ctx.blockState(Int3.zero).let { state ->
|
ctx.blockState(Int3.zero).let { state ->
|
||||||
if (layer == null ||
|
if (layer == null ||
|
||||||
state.canRenderInLayer(layer) ||
|
state.canRenderInLayer(layer) ||
|
||||||
(state.canRenderInCutout() && layer.isCutout)) {
|
(state.canRenderInCutout() && layer.isCutout)) {
|
||||||
return dispatcher.renderBlock(state, ctx.pos, ctx.world, renderer)
|
return dispatcher.renderBlock(state, ctx.pos, ctx.reader!!, renderer, random, modelData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@@ -65,33 +69,23 @@ abstract class AbstractBlockRenderingHandler(modId: String) : ResourceHandler(mo
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data class BlockData(val state: IBlockState, val color: Int, val brightness: Int)
|
data class BlockData(val state: BlockState, val color: Int, val brightness: Int)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the block being rendered. Has properties and methods to query the neighborhood of the block in
|
* Represents the block being rendered. Has properties and methods to query the neighborhood of the block in
|
||||||
* block-relative coordinates.
|
* block-relative coordinates.
|
||||||
*/
|
*/
|
||||||
class BlockContext(
|
@Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
|
||||||
var world: IBlockAccess? = null,
|
open class BlockContext(
|
||||||
var pos: BlockPos = BlockPos.ORIGIN
|
var reader: IEnviromentBlockReader? = null,
|
||||||
) {
|
open var pos: BlockPos = BlockPos.ZERO
|
||||||
fun set(world: IBlockAccess, pos: BlockPos) { this.world = world; this.pos = pos; }
|
) {
|
||||||
|
fun set(blockReader: IEnviromentBlockReader, pos: BlockPos) { this.reader = blockReader; this.pos = pos; }
|
||||||
|
|
||||||
val block: Block get() = block(Int3.zero)
|
val block: Block get() = block(Int3.zero)
|
||||||
fun block(offset: Int3) = blockState(offset).block
|
fun block(offset: Int3) = blockState(offset).block
|
||||||
fun blockState(offset: Int3) = (pos + offset).let { world!!.getBlockState(it) }
|
fun blockState(offset: Int3) = (pos + offset).let { reader!!.getBlockState(it) }
|
||||||
fun blockData(offset: Int3) = (pos + offset).let { pos ->
|
fun isNormalCube(offset: Int3) = (pos + offset).let { reader!!.getBlockState(it).isNormalCube(reader, it) }
|
||||||
world!!.getBlockState(pos).let { state ->
|
|
||||||
BlockData(
|
|
||||||
state,
|
|
||||||
Minecraft.getMinecraft().blockColors.colorMultiplier(state, world!!, pos, 0),
|
|
||||||
state.block.getPackedLightmapCoords(state, world!!, pos)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get the biome ID at the block position. */
|
|
||||||
val biomeId: Int get() = Biome.getIdForBiome(world!!.getBiome(pos))
|
|
||||||
|
|
||||||
/** Get the centerpoint of the block being rendered. */
|
/** Get the centerpoint of the block being rendered. */
|
||||||
val blockCenter: Double3 get() = Double3(pos.x + 0.5, pos.y + 0.5, pos.z + 0.5)
|
val blockCenter: Double3 get() = Double3(pos.x + 0.5, pos.y + 0.5, pos.z + 0.5)
|
||||||
@@ -103,8 +97,24 @@ class BlockContext(
|
|||||||
return Double3(cX * 16.0, cY * 16.0, cZ * 16.0)
|
return Double3(cX * 16.0, cY * 16.0, cZ * 16.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Get the biome at the block position. */
|
||||||
|
val biome: Biome get() = reader!!.getBiome(pos)
|
||||||
|
|
||||||
|
fun blockData(offset: Int3) = (pos + offset).let { pos ->
|
||||||
|
reader!!.getBlockState(pos).let { state ->
|
||||||
|
BlockData(
|
||||||
|
state,
|
||||||
|
Minecraft.getInstance().blockColors.getColor(state, reader!!, pos, 0),
|
||||||
|
state.getPackedLightmapCoords(reader!!, pos)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun shouldSideBeRendered(dir: Direction) = Block.shouldSideBeRendered(blockState(Int3.zero), reader, pos, dir)
|
||||||
|
|
||||||
/** Is the block surrounded by other blocks that satisfy the predicate on all sides? */
|
/** Is the block surrounded by other blocks that satisfy the predicate on all sides? */
|
||||||
fun isSurroundedBy(predicate: (IBlockState)->Boolean) = forgeDirOffsets.all { predicate(blockState(it)) }
|
fun isSurroundedBy(predicate: (BlockState)->Boolean) = forgeDirOffsets.all { predicate(blockState(it)) }
|
||||||
|
val isSurroundedByNormal: Boolean get() = forgeDirOffsets.all { isNormalCube(it) }
|
||||||
|
|
||||||
/** Get a semi-random value based on the block coordinate and the given seed. */
|
/** Get a semi-random value based on the block coordinate and the given seed. */
|
||||||
fun random(seed: Int) = semiRandom(pos.x, pos.y, pos.z, seed)
|
fun random(seed: Int) = semiRandom(pos.x, pos.y, pos.z, seed)
|
||||||
@@ -114,9 +124,9 @@ class BlockContext(
|
|||||||
|
|
||||||
/** Get the distance of the block from the camera (player). */
|
/** Get the distance of the block from the camera (player). */
|
||||||
val cameraDistance: Int get() {
|
val cameraDistance: Int get() {
|
||||||
val camera = Minecraft.getMinecraft().renderViewEntity ?: return 0
|
val camera = Minecraft.getInstance().renderViewEntity ?: return 0
|
||||||
return abs(pos.x - MathHelper.floor(camera.posX)) +
|
return abs(pos.x - MathHelper.floor(camera.posX)) +
|
||||||
abs(pos.y - MathHelper.floor(camera.posY)) +
|
abs(pos.y - MathHelper.floor(camera.posY)) +
|
||||||
abs(pos.z - MathHelper.floor(camera.posZ))
|
abs(pos.z - MathHelper.floor(camera.posZ))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,13 +3,16 @@ package mods.octarinecore.client.render
|
|||||||
import mods.octarinecore.PI2
|
import mods.octarinecore.PI2
|
||||||
import mods.octarinecore.common.Double3
|
import mods.octarinecore.common.Double3
|
||||||
import net.minecraft.client.Minecraft
|
import net.minecraft.client.Minecraft
|
||||||
|
import net.minecraft.client.particle.IParticleRenderType
|
||||||
import net.minecraft.client.particle.Particle
|
import net.minecraft.client.particle.Particle
|
||||||
|
import net.minecraft.client.particle.SpriteTexturedParticle
|
||||||
|
import net.minecraft.client.renderer.ActiveRenderInfo
|
||||||
import net.minecraft.client.renderer.BufferBuilder
|
import net.minecraft.client.renderer.BufferBuilder
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||||
import net.minecraft.entity.Entity
|
import net.minecraft.entity.Entity
|
||||||
import net.minecraft.world.World
|
import net.minecraft.world.World
|
||||||
|
|
||||||
abstract class AbstractEntityFX(world: World, x: Double, y: Double, z: Double) : Particle(world, x, y, z) {
|
abstract class AbstractEntityFX(world: World, x: Double, y: Double, z: Double) : SpriteTexturedParticle(world, x, y, z) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic val sin = Array(64) { idx -> Math.sin(PI2 / 64.0 * idx) }
|
@JvmStatic val sin = Array(64) { idx -> Math.sin(PI2 / 64.0 * idx) }
|
||||||
@@ -21,8 +24,8 @@ abstract class AbstractEntityFX(world: World, x: Double, y: Double, z: Double) :
|
|||||||
val prevPos = Double3.zero
|
val prevPos = Double3.zero
|
||||||
val velocity = Double3.zero
|
val velocity = Double3.zero
|
||||||
|
|
||||||
override fun onUpdate() {
|
override fun tick() {
|
||||||
super.onUpdate()
|
super.tick()
|
||||||
currentPos.setTo(posX, posY, posZ)
|
currentPos.setTo(posX, posY, posZ)
|
||||||
prevPos.setTo(prevPosX, prevPosY, prevPosZ)
|
prevPos.setTo(prevPosX, prevPosY, prevPosZ)
|
||||||
velocity.setTo(motionX, motionY, motionZ)
|
velocity.setTo(motionX, motionY, motionZ)
|
||||||
@@ -41,12 +44,12 @@ abstract class AbstractEntityFX(world: World, x: Double, y: Double, z: Double) :
|
|||||||
abstract val isValid: Boolean
|
abstract val isValid: Boolean
|
||||||
|
|
||||||
/** Add the particle to the effect renderer if it is valid. */
|
/** Add the particle to the effect renderer if it is valid. */
|
||||||
fun addIfValid() { if (isValid) Minecraft.getMinecraft().effectRenderer.addEffect(this) }
|
fun addIfValid() { if (isValid) Minecraft.getInstance().particles.addEffect(this) }
|
||||||
|
|
||||||
override fun renderParticle(worldRenderer: BufferBuilder, entity: Entity, partialTickTime: Float, rotX: Float, rotZ: Float, rotYZ: Float, rotXY: Float, rotXZ: Float) {
|
override fun renderParticle(buffer: BufferBuilder, entity: ActiveRenderInfo, partialTicks: Float, rotX: Float, rotZ: Float, rotYZ: Float, rotXY: Float, rotXZ: Float) {
|
||||||
billboardRot.first.setTo(rotX + rotXY, rotZ, rotYZ + rotXZ)
|
billboardRot.first.setTo(rotX + rotXY, rotZ, rotYZ + rotXZ)
|
||||||
billboardRot.second.setTo(rotX - rotXY, -rotZ, rotYZ - rotXZ)
|
billboardRot.second.setTo(rotX - rotXY, -rotZ, rotYZ - rotXZ)
|
||||||
render(worldRenderer, partialTickTime)
|
render(buffer, partialTicks)
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Render a particle quad.
|
* Render a particle quad.
|
||||||
@@ -67,7 +70,7 @@ abstract class AbstractEntityFX(world: World, x: Double, y: Double, z: Double) :
|
|||||||
prevPos: Double3 = this.prevPos,
|
prevPos: Double3 = this.prevPos,
|
||||||
size: Double = particleScale.toDouble(),
|
size: Double = particleScale.toDouble(),
|
||||||
rotation: Int = 0,
|
rotation: Int = 0,
|
||||||
icon: TextureAtlasSprite = particleTexture,
|
icon: TextureAtlasSprite = sprite,
|
||||||
isMirrored: Boolean = false,
|
isMirrored: Boolean = false,
|
||||||
alpha: Float = this.particleAlpha) {
|
alpha: Float = this.particleAlpha) {
|
||||||
|
|
||||||
@@ -115,7 +118,8 @@ abstract class AbstractEntityFX(world: World, x: Double, y: Double, z: Double) :
|
|||||||
.endVertex()
|
.endVertex()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getFXLayer() = 1
|
// override fun getFXLayer() = 1
|
||||||
|
override fun getRenderType(): IParticleRenderType = IParticleRenderType.PARTICLE_SHEET_TRANSLUCENT
|
||||||
|
|
||||||
fun setColor(color: Int) {
|
fun setColor(color: Int) {
|
||||||
particleBlue = (color and 255) / 256.0f
|
particleBlue = (color and 255) / 256.0f
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package mods.octarinecore.client.render
|
|||||||
import mods.octarinecore.common.*
|
import mods.octarinecore.common.*
|
||||||
import mods.octarinecore.minmax
|
import mods.octarinecore.minmax
|
||||||
import mods.octarinecore.replace
|
import mods.octarinecore.replace
|
||||||
import net.minecraft.util.EnumFacing
|
import net.minecraft.util.Direction
|
||||||
import java.lang.Math.max
|
import java.lang.Math.max
|
||||||
import java.lang.Math.min
|
import java.lang.Math.min
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ data class Quad(val v1: Vertex, val v2: Vertex, val v3: Vertex, val v4: Vertex)
|
|||||||
val normal: Double3 get() = (v2.xyz - v1.xyz).cross(v4.xyz - v1.xyz).normalize
|
val normal: Double3 get() = (v2.xyz - v1.xyz).cross(v4.xyz - v1.xyz).normalize
|
||||||
|
|
||||||
fun move(trans: Double3) = transformV { it.copy(xyz = it.xyz + trans) }
|
fun move(trans: Double3) = transformV { it.copy(xyz = it.xyz + trans) }
|
||||||
fun move(trans: Pair<Double, EnumFacing>) = move(Double3(trans.second) * trans.first)
|
fun move(trans: Pair<Double, Direction>) = move(Double3(trans.second) * trans.first)
|
||||||
fun scale (scale: Double) = transformV { it.copy(xyz = it.xyz * scale) }
|
fun scale (scale: Double) = transformV { it.copy(xyz = it.xyz * scale) }
|
||||||
fun scale (scale: Double3) = transformV { it.copy(xyz = Double3(it.xyz.x * scale.x, it.xyz.y * scale.y, it.xyz.z * scale.z)) }
|
fun scale (scale: Double3) = transformV { it.copy(xyz = Double3(it.xyz.x * scale.x, it.xyz.y * scale.y, it.xyz.z * scale.z)) }
|
||||||
fun scaleUV (scale: Double) = transformV { it.copy(uv = UV(it.uv.u * scale, it.uv.v * scale)) }
|
fun scaleUV (scale: Double) = transformV { it.copy(uv = UV(it.uv.u * scale, it.uv.v * scale)) }
|
||||||
@@ -123,7 +123,7 @@ class Model() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun faceQuad(face: EnumFacing): Quad {
|
fun faceQuad(face: Direction): Quad {
|
||||||
val base = face.vec * 0.5
|
val base = face.vec * 0.5
|
||||||
val top = faceCorners[face.ordinal].topLeft.first.vec * 0.5
|
val top = faceCorners[face.ordinal].topLeft.first.vec * 0.5
|
||||||
val left = faceCorners[face.ordinal].topLeft.second.vec * 0.5
|
val left = faceCorners[face.ordinal].topLeft.second.vec * 0.5
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import mods.octarinecore.common.*
|
|||||||
import net.minecraft.client.Minecraft
|
import net.minecraft.client.Minecraft
|
||||||
import net.minecraft.client.renderer.BufferBuilder
|
import net.minecraft.client.renderer.BufferBuilder
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||||
import net.minecraft.util.EnumFacing
|
import net.minecraft.util.Direction
|
||||||
import net.minecraft.util.EnumFacing.*
|
import net.minecraft.util.Direction.*
|
||||||
|
|
||||||
typealias QuadIconResolver = (ShadingContext, Int, Quad) -> TextureAtlasSprite?
|
typealias QuadIconResolver = (ShadingContext, Int, Quad) -> TextureAtlasSprite?
|
||||||
typealias PostProcessLambda = RenderVertex.(ShadingContext, Int, Quad, Int, Vertex) -> Unit
|
typealias PostProcessLambda = RenderVertex.(ShadingContext, Int, Quad, Int, Vertex) -> Unit
|
||||||
@@ -43,7 +43,7 @@ class ModelRenderer : ShadingContext() {
|
|||||||
aoEnabled = Minecraft.isAmbientOcclusionEnabled()
|
aoEnabled = Minecraft.isAmbientOcclusionEnabled()
|
||||||
|
|
||||||
// make sure we have space in the buffer for our quads plus one
|
// make sure we have space in the buffer for our quads plus one
|
||||||
worldRenderer.ensureSpaceForQuads(model.quads.size + 1)
|
// worldRenderer.ensureSpaceForQuads(model.quads.size + 1)
|
||||||
|
|
||||||
model.quads.forEachIndexed { quadIdx, quad ->
|
model.quads.forEachIndexed { quadIdx, quad ->
|
||||||
if (quadFilter(quadIdx, quad)) {
|
if (quadFilter(quadIdx, quad)) {
|
||||||
@@ -81,18 +81,18 @@ open class ShadingContext {
|
|||||||
var aoEnabled = Minecraft.isAmbientOcclusionEnabled()
|
var aoEnabled = Minecraft.isAmbientOcclusionEnabled()
|
||||||
val aoFaces = Array(6) { AoFaceData(forgeDirs[it]) }
|
val aoFaces = Array(6) { AoFaceData(forgeDirs[it]) }
|
||||||
|
|
||||||
val EnumFacing.aoMultiplier: Float get() = when(this) {
|
val Direction.aoMultiplier: Float get() = when(this) {
|
||||||
UP -> 1.0f
|
UP -> 1.0f
|
||||||
DOWN -> 0.5f
|
DOWN -> 0.5f
|
||||||
NORTH, SOUTH -> 0.8f
|
NORTH, SOUTH -> 0.8f
|
||||||
EAST, WEST -> 0.6f
|
EAST, WEST -> 0.6f
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateShading(offset: Int3, predicate: (EnumFacing) -> Boolean = { true }) {
|
fun updateShading(offset: Int3, predicate: (Direction) -> Boolean = { true }) {
|
||||||
forgeDirs.forEach { if (predicate(it)) aoFaces[it.ordinal].update(offset, multiplier = it.aoMultiplier) }
|
forgeDirs.forEach { if (predicate(it)) aoFaces[it.ordinal].update(offset, multiplier = it.aoMultiplier) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun aoShading(face: EnumFacing, corner1: EnumFacing, corner2: EnumFacing) =
|
fun aoShading(face: Direction, corner1: Direction, corner2: Direction) =
|
||||||
aoFaces[face.rotate(rotation).ordinal][corner1.rotate(rotation), corner2.rotate(rotation)]
|
aoFaces[face.rotate(rotation).ordinal][corner1.rotate(rotation), corner2.rotate(rotation)]
|
||||||
|
|
||||||
fun blockData(offset: Int3) = blockContext.blockData(offset.rotate(rotation))
|
fun blockData(offset: Int3) = blockContext.blockData(offset.rotate(rotation))
|
||||||
@@ -169,13 +169,13 @@ class RenderVertex() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun BufferBuilder.ensureSpaceForQuads(num: Int) {
|
//fun BufferBuilder.ensureSpaceForQuads(num: Int) {
|
||||||
rawIntBuffer.position(bufferSize)
|
// rawIntBuffer.position(bufferSize)
|
||||||
growBuffer(num * vertexFormat.size)
|
// growBuffer(num * vertexFormat.size)
|
||||||
}
|
//}
|
||||||
|
|
||||||
val allFaces: (EnumFacing) -> Boolean = { true }
|
val allFaces: (Direction) -> Boolean = { true }
|
||||||
val topOnly: (EnumFacing) -> Boolean = { it == UP }
|
val topOnly: (Direction) -> Boolean = { it == UP }
|
||||||
|
|
||||||
/** Perform no post-processing */
|
/** Perform no post-processing */
|
||||||
val noPost: PostProcessLambda = { _, _, _, _, _ -> }
|
val noPost: PostProcessLambda = { _, _, _, _, _ -> }
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
package mods.octarinecore.client.render
|
|
||||||
|
|
||||||
import mods.octarinecore.common.Int3
|
|
||||||
import mods.octarinecore.common.plus
|
|
||||||
import net.minecraft.util.EnumFacing
|
|
||||||
import net.minecraft.util.math.BlockPos
|
|
||||||
import net.minecraft.world.IBlockAccess
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delegating [IBlockAccess] that fakes a _modified_ location to return values from a _target_ location.
|
|
||||||
* All other locations are handled normally.
|
|
||||||
*
|
|
||||||
* @param[original] the [IBlockAccess] that is delegated to
|
|
||||||
*/
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
class OffsetBlockAccess(val original: IBlockAccess, val modded: BlockPos, val target: BlockPos) : IBlockAccess {
|
|
||||||
|
|
||||||
inline fun actualPos(pos: BlockPos?) =
|
|
||||||
if (pos != null && pos.x == modded.x && pos.y == modded.y && pos.z == modded.z) target else pos
|
|
||||||
|
|
||||||
override fun getBiome(pos: BlockPos?) = original.getBiome(actualPos(pos))
|
|
||||||
override fun getBlockState(pos: BlockPos?) = original.getBlockState(actualPos(pos))
|
|
||||||
override fun getCombinedLight(pos: BlockPos?, lightValue: Int) = original.getCombinedLight(actualPos(pos), lightValue)
|
|
||||||
override fun getStrongPower(pos: BlockPos?, direction: EnumFacing?) = original.getStrongPower(actualPos(pos), direction)
|
|
||||||
override fun getTileEntity(pos: BlockPos?) = original.getTileEntity(actualPos(pos))
|
|
||||||
override fun getWorldType() = original.worldType
|
|
||||||
override fun isAirBlock(pos: BlockPos?) = original.isAirBlock(actualPos(pos))
|
|
||||||
override fun isSideSolid(pos: BlockPos?, side: EnumFacing?, _default: Boolean) = original.isSideSolid(actualPos(pos), side, _default)
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Temporarily replaces the [IBlockAccess] used by this [BlockContext] and the corresponding [ExtendedRenderBlocks]
|
|
||||||
* to use an [OffsetBlockAccess] while executing this lambda.
|
|
||||||
*
|
|
||||||
* @param[modded] the _modified_ location
|
|
||||||
* @param[target] the _target_ location
|
|
||||||
* @param[func] the lambda to execute
|
|
||||||
*/
|
|
||||||
inline fun <reified T> BlockContext.withOffset(modded: Int3, target: Int3, func: () -> T): T {
|
|
||||||
val original = world!!
|
|
||||||
world = OffsetBlockAccess(original, pos + modded, pos + target)
|
|
||||||
val result = func()
|
|
||||||
world = original
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package mods.octarinecore.client.render
|
||||||
|
|
||||||
|
import mods.octarinecore.common.Int3
|
||||||
|
import mods.octarinecore.common.plus
|
||||||
|
import net.minecraft.util.math.BlockPos
|
||||||
|
import net.minecraft.world.IBlockReader
|
||||||
|
import net.minecraft.world.IEnviromentBlockReader
|
||||||
|
import net.minecraft.world.LightType
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delegating [IBlockAccess] that fakes a _modified_ location to return values from a _target_ location.
|
||||||
|
* All other locations are handled normally.
|
||||||
|
*
|
||||||
|
* @param[original] the [IBlockAccess] that is delegated to
|
||||||
|
*/
|
||||||
|
@Suppress("NOTHING_TO_INLINE", "NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS", "HasPlatformType")
|
||||||
|
open class OffsetBlockReader(open val original: IBlockReader, val modded: BlockPos, val target: BlockPos) : IBlockReader {
|
||||||
|
inline fun actualPos(pos: BlockPos) = if (pos != null && pos.x == modded.x && pos.y == modded.y && pos.z == modded.z) target else pos
|
||||||
|
|
||||||
|
override fun getBlockState(pos: BlockPos) = original.getBlockState(actualPos(pos))
|
||||||
|
override fun getTileEntity(pos: BlockPos) = original.getTileEntity(actualPos(pos))
|
||||||
|
override fun getFluidState(pos: BlockPos) = original.getFluidState(actualPos(pos))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE", "NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS", "HasPlatformType")
|
||||||
|
class OffsetEnvBlockReader(val original: IEnviromentBlockReader, val modded: BlockPos, val target: BlockPos) : IEnviromentBlockReader by original {
|
||||||
|
inline fun actualPos(pos: BlockPos) = if (pos != null && pos.x == modded.x && pos.y == modded.y && pos.z == modded.z) target else pos
|
||||||
|
|
||||||
|
override fun getBlockState(pos: BlockPos) = original.getBlockState(actualPos(pos))
|
||||||
|
override fun getTileEntity(pos: BlockPos) = original.getTileEntity(actualPos(pos))
|
||||||
|
override fun getFluidState(pos: BlockPos) = original.getFluidState(actualPos(pos))
|
||||||
|
|
||||||
|
override fun getLightFor(type: LightType, pos: BlockPos) = original.getLightFor(type, actualPos(pos))
|
||||||
|
override fun getCombinedLight(pos: BlockPos, light: Int) = original.getCombinedLight(actualPos(pos), light)
|
||||||
|
override fun getBiome(pos: BlockPos) = original.getBiome(actualPos(pos))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Temporarily replaces the [IBlockReader] used by this [BlockContext] and the corresponding [ExtendedRenderBlocks]
|
||||||
|
* to use an [OffsetEnvBlockReader] while executing this lambda.
|
||||||
|
*
|
||||||
|
* @param[modded] the _modified_ location
|
||||||
|
* @param[target] the _target_ location
|
||||||
|
* @param[func] the lambda to execute
|
||||||
|
*/
|
||||||
|
//inline fun <reified T> BlockContext.withOffset(modded: Int3, target: Int3, func: () -> T): T {
|
||||||
|
// val original = reader!!
|
||||||
|
// reader = OffsetEnvBlockReader(original, pos + modded, pos + target)
|
||||||
|
// val result = func()
|
||||||
|
// reader = original
|
||||||
|
// return result
|
||||||
|
//}
|
||||||
|
|
||||||
|
inline fun BlockContext.offset(modded: Int3, target: Int3) = BlockContext(OffsetEnvBlockReader(reader!!, pos + modded, pos + target), pos)
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package mods.octarinecore.client.render
|
package mods.octarinecore.client.render
|
||||||
|
|
||||||
import mods.octarinecore.common.*
|
import mods.octarinecore.common.*
|
||||||
import net.minecraft.util.EnumFacing
|
import net.minecraft.util.Direction
|
||||||
|
|
||||||
|
|
||||||
const val defaultCornerDimming = 0.5f
|
const val defaultCornerDimming = 0.5f
|
||||||
@@ -10,17 +10,17 @@ const val defaultEdgeDimming = 0.8f
|
|||||||
// ================================
|
// ================================
|
||||||
// Shader instantiation lambdas
|
// Shader instantiation lambdas
|
||||||
// ================================
|
// ================================
|
||||||
fun cornerAo(fallbackAxis: EnumFacing.Axis): CornerShaderFactory = { face, dir1, dir2 ->
|
fun cornerAo(fallbackAxis: Direction.Axis): CornerShaderFactory = { face, dir1, dir2 ->
|
||||||
val fallbackDir = listOf(face, dir1, dir2).find { it.axis == fallbackAxis }!!
|
val fallbackDir = listOf(face, dir1, dir2).find { it.axis == fallbackAxis }!!
|
||||||
CornerSingleFallback(face, dir1, dir2, fallbackDir)
|
CornerSingleFallback(face, dir1, dir2, fallbackDir)
|
||||||
}
|
}
|
||||||
val cornerFlat = { face: EnumFacing, dir1: EnumFacing, dir2: EnumFacing -> FaceFlat(face) }
|
val cornerFlat = { face: Direction, dir1: Direction, dir2: Direction -> FaceFlat(face) }
|
||||||
fun cornerAoTri(func: (AoData, AoData)-> AoData) = { face: EnumFacing, dir1: EnumFacing, dir2: EnumFacing ->
|
fun cornerAoTri(func: (AoData, AoData)-> AoData) = { face: Direction, dir1: Direction, dir2: Direction ->
|
||||||
CornerTri(face, dir1, dir2, func)
|
CornerTri(face, dir1, dir2, func)
|
||||||
}
|
}
|
||||||
val cornerAoMaxGreen = cornerAoTri { s1, s2 -> if (s1.green > s2.green) s1 else s2 }
|
val cornerAoMaxGreen = cornerAoTri { s1, s2 -> if (s1.green > s2.green) s1 else s2 }
|
||||||
|
|
||||||
fun cornerInterpolate(edgeAxis: EnumFacing.Axis, weight: Float, dimming: Float): CornerShaderFactory = { dir1, dir2, dir3 ->
|
fun cornerInterpolate(edgeAxis: Direction.Axis, weight: Float, dimming: Float): CornerShaderFactory = { dir1, dir2, dir3 ->
|
||||||
val edgeDir = listOf(dir1, dir2, dir3).find { it.axis == edgeAxis }!!
|
val edgeDir = listOf(dir1, dir2, dir3).find { it.axis == edgeAxis }!!
|
||||||
val faceDirs = listOf(dir1, dir2, dir3).filter { it.axis != edgeAxis }
|
val faceDirs = listOf(dir1, dir2, dir3).filter { it.axis != edgeAxis }
|
||||||
CornerInterpolateDimming(faceDirs[0], faceDirs[1], edgeDir, weight, dimming)
|
CornerInterpolateDimming(faceDirs[0], faceDirs[1], edgeDir, weight, dimming)
|
||||||
@@ -34,7 +34,7 @@ object NoShader : Shader {
|
|||||||
override fun rotate(rot: Rotation) = this
|
override fun rotate(rot: Rotation) = this
|
||||||
}
|
}
|
||||||
|
|
||||||
class CornerSingleFallback(val face: EnumFacing, val dir1: EnumFacing, val dir2: EnumFacing, val fallbackDir: EnumFacing, val fallbackDimming: Float = defaultCornerDimming) : Shader {
|
class CornerSingleFallback(val face: Direction, val dir1: Direction, val dir2: Direction, val fallbackDir: Direction, val fallbackDimming: Float = defaultCornerDimming) : Shader {
|
||||||
val offset = Int3(fallbackDir)
|
val offset = Int3(fallbackDir)
|
||||||
override fun shade(context: ShadingContext, vertex: RenderVertex) {
|
override fun shade(context: ShadingContext, vertex: RenderVertex) {
|
||||||
val shading = context.aoShading(face, dir1, dir2)
|
val shading = context.aoShading(face, dir1, dir2)
|
||||||
@@ -56,7 +56,7 @@ inline fun accumulate(v1: AoData?, v2: AoData?, func: ((AoData, AoData)-> AoData
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
class CornerTri(val face: EnumFacing, val dir1: EnumFacing, val dir2: EnumFacing,
|
class CornerTri(val face: Direction, val dir1: Direction, val dir2: Direction,
|
||||||
val func: ((AoData, AoData)-> AoData)) : Shader {
|
val func: ((AoData, AoData)-> AoData)) : Shader {
|
||||||
override fun shade(context: ShadingContext, vertex: RenderVertex) {
|
override fun shade(context: ShadingContext, vertex: RenderVertex) {
|
||||||
var acc = accumulate(
|
var acc = accumulate(
|
||||||
@@ -72,15 +72,15 @@ class CornerTri(val face: EnumFacing, val dir1: EnumFacing, val dir2: EnumFacing
|
|||||||
override fun rotate(rot: Rotation) = CornerTri(face.rotate(rot), dir1.rotate(rot), dir2.rotate(rot), func)
|
override fun rotate(rot: Rotation) = CornerTri(face.rotate(rot), dir1.rotate(rot), dir2.rotate(rot), func)
|
||||||
}
|
}
|
||||||
|
|
||||||
class EdgeInterpolateFallback(val face: EnumFacing, val edgeDir: EnumFacing, val pos: Double, val fallbackDimming: Float = defaultEdgeDimming): Shader {
|
class EdgeInterpolateFallback(val face: Direction, val edgeDir: Direction, val pos: Double, val fallbackDimming: Float = defaultEdgeDimming): Shader {
|
||||||
val offset = Int3(edgeDir)
|
val offset = Int3(edgeDir)
|
||||||
val edgeAxis = axes.find { it != face.axis && it != edgeDir.axis }!!
|
val edgeAxis = axes.find { it != face.axis && it != edgeDir.axis }!!
|
||||||
val weightN = (0.5 - pos).toFloat()
|
val weightN = (0.5 - pos).toFloat()
|
||||||
val weightP = (0.5 + pos).toFloat()
|
val weightP = (0.5 + pos).toFloat()
|
||||||
|
|
||||||
override fun shade(context: ShadingContext, vertex: RenderVertex) {
|
override fun shade(context: ShadingContext, vertex: RenderVertex) {
|
||||||
val shadingP = context.aoShading(face, edgeDir, (edgeAxis to EnumFacing.AxisDirection.POSITIVE).face)
|
val shadingP = context.aoShading(face, edgeDir, (edgeAxis to Direction.AxisDirection.POSITIVE).face)
|
||||||
val shadingN = context.aoShading(face, edgeDir, (edgeAxis to EnumFacing.AxisDirection.NEGATIVE).face)
|
val shadingN = context.aoShading(face, edgeDir, (edgeAxis to Direction.AxisDirection.NEGATIVE).face)
|
||||||
if (!shadingP.valid && !shadingN.valid) context.blockData(offset).let {
|
if (!shadingP.valid && !shadingN.valid) context.blockData(offset).let {
|
||||||
return vertex.shade(it.brightness brMul fallbackDimming, it.color colorMul fallbackDimming)
|
return vertex.shade(it.brightness brMul fallbackDimming, it.color colorMul fallbackDimming)
|
||||||
}
|
}
|
||||||
@@ -91,7 +91,7 @@ class EdgeInterpolateFallback(val face: EnumFacing, val edgeDir: EnumFacing, val
|
|||||||
override fun rotate(rot: Rotation) = EdgeInterpolateFallback(face.rotate(rot), edgeDir.rotate(rot), pos)
|
override fun rotate(rot: Rotation) = EdgeInterpolateFallback(face.rotate(rot), edgeDir.rotate(rot), pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
class CornerInterpolateDimming(val face1: EnumFacing, val face2: EnumFacing, val edgeDir: EnumFacing,
|
class CornerInterpolateDimming(val face1: Direction, val face2: Direction, val edgeDir: Direction,
|
||||||
val weight: Float, val dimming: Float, val fallbackDimming: Float = defaultCornerDimming) : Shader {
|
val weight: Float, val dimming: Float, val fallbackDimming: Float = defaultCornerDimming) : Shader {
|
||||||
val offset = Int3(edgeDir)
|
val offset = Int3(edgeDir)
|
||||||
override fun shade(context: ShadingContext, vertex: RenderVertex) {
|
override fun shade(context: ShadingContext, vertex: RenderVertex) {
|
||||||
@@ -111,7 +111,7 @@ class CornerInterpolateDimming(val face1: EnumFacing, val face2: EnumFacing, val
|
|||||||
CornerInterpolateDimming(face1.rotate(rot), face2.rotate(rot), edgeDir.rotate(rot), weight, dimming, fallbackDimming)
|
CornerInterpolateDimming(face1.rotate(rot), face2.rotate(rot), edgeDir.rotate(rot), weight, dimming, fallbackDimming)
|
||||||
}
|
}
|
||||||
|
|
||||||
class FaceCenter(val face: EnumFacing): Shader {
|
class FaceCenter(val face: Direction): Shader {
|
||||||
override fun shade(context: ShadingContext, vertex: RenderVertex) {
|
override fun shade(context: ShadingContext, vertex: RenderVertex) {
|
||||||
vertex.red = 0.0f; vertex.green = 0.0f; vertex.blue = 0.0f;
|
vertex.red = 0.0f; vertex.green = 0.0f; vertex.blue = 0.0f;
|
||||||
val b = IntArray(4)
|
val b = IntArray(4)
|
||||||
@@ -128,7 +128,7 @@ class FaceCenter(val face: EnumFacing): Shader {
|
|||||||
override fun rotate(rot: Rotation) = FaceCenter(face.rotate(rot))
|
override fun rotate(rot: Rotation) = FaceCenter(face.rotate(rot))
|
||||||
}
|
}
|
||||||
|
|
||||||
class FaceFlat(val face: EnumFacing): Shader {
|
class FaceFlat(val face: Direction): Shader {
|
||||||
override fun shade(context: ShadingContext, vertex: RenderVertex) {
|
override fun shade(context: ShadingContext, vertex: RenderVertex) {
|
||||||
val color = context.blockData(Int3.zero).color
|
val color = context.blockData(Int3.zero).color
|
||||||
vertex.shade(context.blockData(face.offset).brightness, color)
|
vertex.shade(context.blockData(face.offset).brightness, color)
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
package mods.octarinecore.client.render
|
package mods.octarinecore.client.render
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliage
|
||||||
import mods.betterfoliage.loader.Refs
|
import mods.betterfoliage.loader.Refs
|
||||||
import mods.octarinecore.common.*
|
import mods.octarinecore.common.*
|
||||||
import mods.octarinecore.metaprog.allAvailable
|
import mods.octarinecore.metaprog.allAvailable
|
||||||
import net.minecraft.client.Minecraft
|
import net.minecraft.client.Minecraft
|
||||||
import net.minecraft.client.renderer.BlockModelRenderer
|
import net.minecraft.client.renderer.BlockModelRenderer
|
||||||
import net.minecraft.util.EnumFacing
|
import net.minecraft.client.renderer.BlockModelRenderer.AmbientOcclusionFace
|
||||||
import net.minecraft.util.EnumFacing.*
|
import net.minecraft.util.Direction
|
||||||
|
import net.minecraft.util.Direction.*
|
||||||
|
import org.apache.logging.log4j.Level
|
||||||
import java.lang.Math.min
|
import java.lang.Math.min
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
typealias EdgeShaderFactory = (EnumFacing, EnumFacing) -> Shader
|
typealias EdgeShaderFactory = (Direction, Direction) -> Shader
|
||||||
typealias CornerShaderFactory = (EnumFacing, EnumFacing, EnumFacing) -> Shader
|
typealias CornerShaderFactory = (Direction, Direction, Direction) -> Shader
|
||||||
typealias ShaderFactory = (Quad, Vertex) -> Shader
|
typealias ShaderFactory = (Quad, Vertex) -> Shader
|
||||||
|
|
||||||
/** Holds shading values for block corners as calculated by vanilla Minecraft rendering. */
|
/** Holds shading values for block corners as calculated by vanilla Minecraft rendering. */
|
||||||
@@ -46,12 +49,13 @@ class AoData() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AoFaceData(val face: EnumFacing) {
|
class AoFaceData(val face: Direction) {
|
||||||
val ao = Refs.AmbientOcclusionFace.element!!.let {
|
companion object {
|
||||||
if (allAvailable(Refs.OptifineClassTransformer)) it.getDeclaredConstructor().newInstance()
|
private val aoFactory = BlockModelRenderer.AmbientOcclusionFace::class.java.let {
|
||||||
else it.getDeclaredConstructor(Refs.BlockModelRenderer.element!!)
|
it.getDeclaredConstructor(BlockModelRenderer::class.java).apply { isAccessible = true }
|
||||||
.newInstance(BlockModelRenderer(Minecraft.getMinecraft().blockColors))
|
}.let { ctor -> { ctor.newInstance(Minecraft.getInstance().blockRendererDispatcher.blockModelRenderer) } }
|
||||||
} as BlockModelRenderer.AmbientOcclusionFace
|
}
|
||||||
|
val ao = aoFactory()
|
||||||
val top = faceCorners[face.ordinal].topLeft.first
|
val top = faceCorners[face.ordinal].topLeft.first
|
||||||
val left = faceCorners[face.ordinal].topLeft.second
|
val left = faceCorners[face.ordinal].topLeft.second
|
||||||
|
|
||||||
@@ -74,11 +78,11 @@ class AoFaceData(val face: EnumFacing) {
|
|||||||
val quadBounds: FloatArray = FloatArray(12)
|
val quadBounds: FloatArray = FloatArray(12)
|
||||||
val flags = BitSet(3).apply { set(0) }
|
val flags = BitSet(3).apply { set(0) }
|
||||||
|
|
||||||
ao.updateVertexBrightness(ctx.world, blockState, ctx.pos + offset, face, quadBounds, flags)
|
ao.updateVertexBrightness(ctx.reader!!, blockState, ctx.pos + offset, face, quadBounds, flags)
|
||||||
ordered.forEachIndexed { idx, aoData -> aoData.set(ao.vertexBrightness[idx], ao.vertexColorMultiplier[idx] * multiplier) }
|
ordered.forEachIndexed { idx, aoData -> aoData.set(ao.vertexBrightness[idx], ao.vertexColorMultiplier[idx] * multiplier) }
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun get(dir1: EnumFacing, dir2: EnumFacing): AoData {
|
operator fun get(dir1: Direction, dir2: Direction): AoData {
|
||||||
val isTop = top == dir1 || top == dir2
|
val isTop = top == dir1 || top == dir2
|
||||||
val isLeft = left == dir1 || left == dir2
|
val isLeft = left == dir1 || left == dir2
|
||||||
return if (isTop) {
|
return if (isTop) {
|
||||||
@@ -143,7 +147,7 @@ interface Shader {
|
|||||||
* @param[corner] shader instantiation lambda for corner vertices
|
* @param[corner] shader instantiation lambda for corner vertices
|
||||||
* @param[edge] shader instantiation lambda for edge midpoint vertices
|
* @param[edge] shader instantiation lambda for edge midpoint vertices
|
||||||
*/
|
*/
|
||||||
fun faceOrientedAuto(overrideFace: EnumFacing? = null,
|
fun faceOrientedAuto(overrideFace: Direction? = null,
|
||||||
corner: CornerShaderFactory? = null,
|
corner: CornerShaderFactory? = null,
|
||||||
edge: EdgeShaderFactory? = null) =
|
edge: EdgeShaderFactory? = null) =
|
||||||
fun(quad: Quad, vertex: Vertex): Shader {
|
fun(quad: Quad, vertex: Vertex): Shader {
|
||||||
@@ -170,7 +174,7 @@ fun faceOrientedAuto(overrideFace: EnumFacing? = null,
|
|||||||
* @param[overrideEdge] assume the given edge instead of going by the _quad_ normal
|
* @param[overrideEdge] assume the given edge instead of going by the _quad_ normal
|
||||||
* @param[corner] shader instantiation lambda
|
* @param[corner] shader instantiation lambda
|
||||||
*/
|
*/
|
||||||
fun edgeOrientedAuto(overrideEdge: Pair<EnumFacing, EnumFacing>? = null,
|
fun edgeOrientedAuto(overrideEdge: Pair<Direction, Direction>? = null,
|
||||||
corner: CornerShaderFactory) =
|
corner: CornerShaderFactory) =
|
||||||
fun(quad: Quad, vertex: Vertex): Shader {
|
fun(quad: Quad, vertex: Vertex): Shader {
|
||||||
val edgeDir = overrideEdge ?: nearestAngle(quad.normal, boxEdges) { it.first.vec + it.second.vec }.first
|
val edgeDir = overrideEdge ?: nearestAngle(quad.normal, boxEdges) { it.first.vec + it.second.vec }.first
|
||||||
@@ -181,11 +185,11 @@ fun edgeOrientedAuto(overrideEdge: Pair<EnumFacing, EnumFacing>? = null,
|
|||||||
return corner(nearestFace, nearestCorner.first, nearestCorner.second)
|
return corner(nearestFace, nearestCorner.first, nearestCorner.second)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun faceOrientedInterpolate(overrideFace: EnumFacing? = null) =
|
fun faceOrientedInterpolate(overrideFace: Direction? = null) =
|
||||||
fun(quad: Quad, vertex: Vertex): Shader {
|
fun(quad: Quad, vertex: Vertex): Shader {
|
||||||
val resolver = faceOrientedAuto(overrideFace, edge = { face, edgeDir ->
|
val resolver = faceOrientedAuto(overrideFace, edge = { face, edgeDir ->
|
||||||
val axis = axes.find { it != face.axis && it != edgeDir.axis }!!
|
val axis = axes.find { it != face.axis && it != edgeDir.axis }!!
|
||||||
val vec = Double3((axis to EnumFacing.AxisDirection.POSITIVE).face)
|
val vec = Double3((axis to Direction.AxisDirection.POSITIVE).face)
|
||||||
val pos = vertex.xyz.dot(vec)
|
val pos = vertex.xyz.dot(vec)
|
||||||
EdgeInterpolateFallback(face, edgeDir, pos)
|
EdgeInterpolateFallback(face, edgeDir, pos)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,13 +1,23 @@
|
|||||||
package mods.octarinecore.client.resource
|
package mods.octarinecore.client.resource
|
||||||
|
|
||||||
|
import net.minecraft.util.ResourceLocation
|
||||||
|
import net.minecraftforge.resource.VanillaResourceType
|
||||||
import java.awt.image.BufferedImage
|
import java.awt.image.BufferedImage
|
||||||
|
import java.io.InputStream
|
||||||
import java.lang.Math.*
|
import java.lang.Math.*
|
||||||
|
|
||||||
class CenteringTextureGenerator(domain: String, val aspectWidth: Int, val aspectHeight: Int) : TextureGenerator(domain) {
|
class CenteringTextureGenerator(
|
||||||
|
domain: String,
|
||||||
|
val aspectWidth: Int,
|
||||||
|
val aspectHeight: Int
|
||||||
|
) : GeneratorBase<ResourceLocation>(domain, VanillaResourceType.TEXTURES) {
|
||||||
|
|
||||||
override fun generate(params: ParameterList): BufferedImage? {
|
override val locationMapper = Atlas.BLOCKS::unwrap
|
||||||
val target = targetResource(params)!!
|
|
||||||
val baseTexture = resourceManager[target.second]?.loadImage() ?: return null
|
override fun exists(key: ResourceLocation) = resourceManager.hasResource(Atlas.BLOCKS.wrap(key))
|
||||||
|
|
||||||
|
override fun get(key: ResourceLocation): InputStream? {
|
||||||
|
val baseTexture = resourceManager[Atlas.BLOCKS.wrap(key)]?.loadImage() ?: return null
|
||||||
|
|
||||||
val frameWidth = baseTexture.width
|
val frameWidth = baseTexture.width
|
||||||
val frameHeight = baseTexture.width * aspectHeight / aspectWidth
|
val frameHeight = baseTexture.width * aspectHeight / aspectWidth
|
||||||
@@ -28,6 +38,6 @@ class CenteringTextureGenerator(domain: String, val aspectWidth: Int, val aspect
|
|||||||
graphics.drawImage(resultFrame, 0, size * frame, null)
|
graphics.drawImage(resultFrame, 0, size * frame, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
return resultTexture
|
return resultTexture.asStream
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,57 +1,51 @@
|
|||||||
package mods.octarinecore.client.resource
|
package mods.octarinecore.client.resource
|
||||||
|
|
||||||
import com.google.common.base.Joiner
|
import com.google.common.base.Joiner
|
||||||
import mods.betterfoliage.client.Client
|
|
||||||
import mods.betterfoliage.loader.Refs
|
|
||||||
import mods.octarinecore.client.render.BlockContext
|
import mods.octarinecore.client.render.BlockContext
|
||||||
import mods.octarinecore.common.Int3
|
import mods.octarinecore.common.Int3
|
||||||
import mods.octarinecore.common.config.IBlockMatcher
|
import mods.octarinecore.common.config.IBlockMatcher
|
||||||
import mods.octarinecore.common.config.ModelTextureList
|
import mods.octarinecore.common.config.ModelTextureList
|
||||||
import mods.octarinecore.filterValuesNotNull
|
import mods.octarinecore.common.plus
|
||||||
import mods.octarinecore.findFirst
|
import mods.octarinecore.findFirst
|
||||||
import net.minecraft.block.Block
|
import net.minecraft.block.BlockState
|
||||||
import net.minecraft.block.state.IBlockState
|
import net.minecraft.client.renderer.BlockModelShapes
|
||||||
import net.minecraft.client.renderer.block.model.ModelResourceLocation
|
import net.minecraft.client.renderer.model.*
|
||||||
import net.minecraft.client.renderer.block.statemap.DefaultStateMapper
|
import net.minecraft.client.renderer.texture.AtlasTexture
|
||||||
import net.minecraft.client.renderer.block.statemap.IStateMapper
|
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
|
||||||
import net.minecraft.client.renderer.texture.TextureMap
|
|
||||||
import net.minecraft.util.ResourceLocation
|
import net.minecraft.util.ResourceLocation
|
||||||
import net.minecraft.util.math.BlockPos
|
import net.minecraft.util.math.BlockPos
|
||||||
import net.minecraft.world.IBlockAccess
|
import net.minecraft.world.IBlockReader
|
||||||
import net.minecraftforge.client.event.TextureStitchEvent
|
import net.minecraftforge.client.event.TextureStitchEvent
|
||||||
import net.minecraftforge.client.model.IModel
|
|
||||||
import net.minecraftforge.client.model.ModelLoader
|
import net.minecraftforge.client.model.ModelLoader
|
||||||
import net.minecraftforge.common.MinecraftForge
|
import net.minecraftforge.eventbus.api.Event
|
||||||
import net.minecraftforge.fml.common.eventhandler.Event
|
import net.minecraftforge.eventbus.api.EventPriority
|
||||||
import net.minecraftforge.fml.common.eventhandler.EventPriority
|
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
|
import net.minecraftforge.registries.ForgeRegistries
|
||||||
import org.apache.logging.log4j.Level
|
import org.apache.logging.log4j.Level
|
||||||
import org.apache.logging.log4j.Logger
|
import org.apache.logging.log4j.Logger
|
||||||
|
|
||||||
class LoadModelDataEvent(val loader: ModelLoader) : Event()
|
class LoadModelDataEvent(val bakery: ModelBakery) : Event()
|
||||||
|
|
||||||
interface ModelRenderRegistry<T> {
|
interface ModelRenderRegistry<T> {
|
||||||
operator fun get(ctx: BlockContext) = get(ctx.blockState(Int3.zero), ctx.world!!, ctx.pos)
|
operator fun get(ctx: BlockContext) = get(ctx.blockState(Int3.zero), ctx.reader!!, ctx.pos)
|
||||||
operator fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos): T?
|
operator fun get(ctx: BlockContext, offset: Int3) = get(ctx.blockState(offset), ctx.reader!!, ctx.pos + offset)
|
||||||
|
operator fun get(state: BlockState, world: IBlockReader, pos: BlockPos): T?
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ModelRenderDataExtractor<T> {
|
interface ModelRenderDataExtractor<T> {
|
||||||
fun processModel(state: IBlockState, modelLoc: ModelResourceLocation, model: IModel): ModelRenderKey<T>?
|
fun processModel(state: BlockState, modelLoc: ModelResourceLocation, models: List<Pair<IUnbakedModel, ResourceLocation>>): ModelRenderKey<T>?
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ModelRenderKey<T> {
|
interface ModelRenderKey<T> {
|
||||||
val logger: Logger?
|
val logger: Logger?
|
||||||
fun onPreStitch(atlas: TextureMap) {}
|
fun onPreStitch(event: TextureStitchEvent.Pre) {}
|
||||||
fun resolveSprites(atlas: TextureMap): T
|
fun resolveSprites(atlas: AtlasTexture): T
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class ModelRenderRegistryRoot<T> : ModelRenderRegistry<T> {
|
abstract class ModelRenderRegistryRoot<T> : ModelRenderRegistry<T> {
|
||||||
val subRegistries = mutableListOf<ModelRenderRegistry<T>>()
|
val subRegistries = mutableListOf<ModelRenderRegistry<T>>()
|
||||||
override fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos) = subRegistries.findFirst { it[state, world, pos] }
|
override fun get(state: BlockState, world: IBlockReader, pos: BlockPos) = subRegistries.findFirst { it[state, world, pos] }
|
||||||
fun addRegistry(registry: ModelRenderRegistry<T>) {
|
fun addRegistry(registry: ModelRenderRegistry<T>) {
|
||||||
subRegistries.add(registry)
|
subRegistries.add(registry)
|
||||||
MinecraftForge.EVENT_BUS.register(registry)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,40 +53,41 @@ abstract class ModelRenderRegistryBase<T> : ModelRenderRegistry<T>, ModelRenderD
|
|||||||
open val logger: Logger? = null
|
open val logger: Logger? = null
|
||||||
open val logName: String get() = this::class.java.name
|
open val logName: String get() = this::class.java.name
|
||||||
|
|
||||||
val stateToKey = mutableMapOf<IBlockState, ModelRenderKey<T>>()
|
val stateToKey = mutableMapOf<BlockState, ModelRenderKey<T>>()
|
||||||
var stateToValue = mapOf<IBlockState, T>()
|
var stateToValue = mapOf<BlockState, T>()
|
||||||
|
|
||||||
override fun get(state: IBlockState, world: IBlockAccess, pos: BlockPos) = stateToValue[state]
|
override fun get(state: BlockState, world: IBlockReader, pos: BlockPos) = stateToValue[state]
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
fun handleLoadModelData(event: LoadModelDataEvent) {
|
fun handleLoadModelData(event: LoadModelDataEvent) {
|
||||||
stateToValue = emptyMap()
|
stateToValue = emptyMap()
|
||||||
|
|
||||||
val stateMappings = Block.REGISTRY.flatMap { block ->
|
val stateMappings = ForgeRegistries.BLOCKS.flatMap { block ->
|
||||||
val mapper = event.loader.blockModelShapes.blockStateMapper.blockStateMap[block] as? IStateMapper ?: DefaultStateMapper()
|
block.stateContainer.validStates.map { state -> state to BlockModelShapes.getModelLocation(state) }
|
||||||
(mapper.putStateModelLocations(block as Block) as Map<IBlockState, ModelResourceLocation>).entries
|
|
||||||
}
|
}
|
||||||
val stateModels = Refs.stateModels.get(event.loader) as Map<ModelResourceLocation, IModel>
|
// val unbakedModels = (Refs.unbakedModels.get(event.loader) as Map<*, *>).filter { it.value is IUnbakedModel } as Map<ModelResourceLocation, IUnbakedModel>
|
||||||
|
|
||||||
stateMappings.forEach { mapping ->
|
val missingModel = event.bakery.getUnbakedModel(ModelBakery.MODEL_MISSING)
|
||||||
if (mapping.key.block != null) stateModels[mapping.value]?.let { model ->
|
stateMappings.forEach { (state, stateModelResource) ->
|
||||||
try {
|
val allModels = event.bakery.let { it.unwrapVariants(it.getUnbakedModel(stateModelResource) to stateModelResource) }.filter { it.second != missingModel }
|
||||||
processModel(mapping.key, mapping.value, model)?.let { stateToKey[mapping.key] = it }
|
try {
|
||||||
} catch (e: Exception) {
|
processModel(state, stateModelResource, allModels)?.let { stateToKey[state] = it }
|
||||||
logger?.warn("Exception while trying to process model ${mapping.value}", e)
|
} catch (e: Exception) {
|
||||||
}
|
logger?.warn("Exception while trying to process model ${stateModelResource}", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent(priority = EventPriority.LOW)
|
@SubscribeEvent(priority = EventPriority.LOW)
|
||||||
fun handlePreStitch(event: TextureStitchEvent.Pre) {
|
fun handlePreStitch(event: TextureStitchEvent.Pre) {
|
||||||
stateToKey.forEach { (_, key) -> key.onPreStitch(event.map) }
|
if (event.map.basePath != "textures") return
|
||||||
|
stateToKey.forEach { (_, key) -> key.onPreStitch(event) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent(priority = EventPriority.LOW)
|
@SubscribeEvent(priority = EventPriority.LOW)
|
||||||
fun handlePostStitch(event: TextureStitchEvent.Post) {
|
fun handlePostStitch(event: TextureStitchEvent.Post) {
|
||||||
|
if (event.map.basePath != "textures") return
|
||||||
stateToValue = stateToKey.mapValues { (_, key) -> key.resolveSprites(event.map) }
|
stateToValue = stateToKey.mapValues { (_, key) -> key.resolveSprites(event.map) }
|
||||||
stateToKey.clear()
|
stateToKey.clear()
|
||||||
}
|
}
|
||||||
@@ -103,23 +98,23 @@ abstract class ModelRenderRegistryConfigurable<T> : ModelRenderRegistryBase<T>()
|
|||||||
abstract val matchClasses: IBlockMatcher
|
abstract val matchClasses: IBlockMatcher
|
||||||
abstract val modelTextures: List<ModelTextureList>
|
abstract val modelTextures: List<ModelTextureList>
|
||||||
|
|
||||||
override fun processModel(state: IBlockState, modelLoc: ModelResourceLocation, model: IModel): ModelRenderKey<T>? {
|
override fun processModel(state: BlockState, modelLoc: ModelResourceLocation, models: List<Pair<IUnbakedModel, ResourceLocation>>): ModelRenderKey<T>? {
|
||||||
val matchClass = matchClasses.matchingClass(state.block) ?: return null
|
val matchClass = matchClasses.matchingClass(state.block) ?: return null
|
||||||
logger?.log(Level.DEBUG, "$logName: block state ${state.toString()}")
|
logger?.log(Level.DEBUG, "$logName: block state ${state.toString()}")
|
||||||
logger?.log(Level.DEBUG, "$logName: class ${state.block.javaClass.name} matches ${matchClass.name}")
|
logger?.log(Level.DEBUG, "$logName: class ${state.block.javaClass.name} matches ${matchClass.name}")
|
||||||
|
|
||||||
val allModels = model.modelBlockAndLoc.distinctBy { it.second }
|
if (models.isEmpty()) {
|
||||||
if (allModels.isEmpty()) {
|
|
||||||
logger?.log(Level.DEBUG, "$logName: no models found")
|
logger?.log(Level.DEBUG, "$logName: no models found")
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
allModels.forEach { blockLoc ->
|
models.filter { it.first is BlockModel }.forEach { (model, location) ->
|
||||||
val modelMatch = modelTextures.firstOrNull { blockLoc.derivesFrom(it.modelLocation) }
|
model as BlockModel
|
||||||
|
val modelMatch = modelTextures.firstOrNull { (model to location).derivesFrom(it.modelLocation) }
|
||||||
if (modelMatch != null) {
|
if (modelMatch != null) {
|
||||||
logger?.log(Level.DEBUG, "$logName: model ${blockLoc.second} matches ${modelMatch.modelLocation}")
|
logger?.log(Level.DEBUG, "$logName: model ${model} matches ${modelMatch.modelLocation}")
|
||||||
|
|
||||||
val textures = modelMatch.textureNames.map { it to blockLoc.first.resolveTextureName(it) }
|
val textures = modelMatch.textureNames.map { it to model.resolveTextureName(it) }
|
||||||
val texMapString = Joiner.on(", ").join(textures.map { "${it.first}=${it.second}" })
|
val texMapString = Joiner.on(", ").join(textures.map { "${it.first}=${it.second}" })
|
||||||
logger?.log(Level.DEBUG, "$logName: textures [$texMapString]")
|
logger?.log(Level.DEBUG, "$logName: textures [$texMapString]")
|
||||||
|
|
||||||
@@ -132,5 +127,11 @@ abstract class ModelRenderRegistryConfigurable<T> : ModelRenderRegistryBase<T>()
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract fun processModel(state: IBlockState, textures: List<String>) : ModelRenderKey<T>?
|
abstract fun processModel(state: BlockState, textures: List<String>) : ModelRenderKey<T>?
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ModelBakery.unwrapVariants(modelAndLoc: Pair<IUnbakedModel, ResourceLocation>): List<Pair<IUnbakedModel, ResourceLocation>> = when(val model = modelAndLoc.first) {
|
||||||
|
is VariantList -> model.variantList.flatMap { variant -> unwrapVariants(getUnbakedModel(variant.modelLocation) to variant.modelLocation) }
|
||||||
|
is BlockModel -> listOf(modelAndLoc)
|
||||||
|
else -> emptyList()
|
||||||
}
|
}
|
||||||
@@ -1,65 +1,111 @@
|
|||||||
package mods.octarinecore.client.resource
|
package mods.octarinecore.client.resource
|
||||||
|
|
||||||
import mods.octarinecore.metaprog.reflectField
|
import net.minecraft.client.Minecraft
|
||||||
import net.minecraft.client.resources.IResourcePack
|
import net.minecraft.resources.*
|
||||||
import net.minecraft.client.resources.data.IMetadataSection
|
import net.minecraft.resources.ResourcePackType.CLIENT_RESOURCES
|
||||||
import net.minecraft.client.resources.data.IMetadataSectionSerializer
|
import net.minecraft.resources.data.IMetadataSectionSerializer
|
||||||
import net.minecraft.client.resources.data.MetadataSerializer
|
import net.minecraft.resources.data.PackMetadataSection
|
||||||
import net.minecraft.client.resources.data.PackMetadataSection
|
|
||||||
import net.minecraft.util.ResourceLocation
|
import net.minecraft.util.ResourceLocation
|
||||||
import net.minecraft.util.text.TextComponentString
|
import net.minecraft.util.text.StringTextComponent
|
||||||
import net.minecraftforge.fml.client.FMLClientHandler
|
import net.minecraftforge.resource.IResourceType
|
||||||
|
import net.minecraftforge.resource.ISelectiveResourceReloadListener
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.function.Predicate
|
||||||
|
import java.util.function.Supplier
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [IResourcePack] containing generated resources. Adds itself to the default resource pack list
|
* [IResourcePack] containing generated resources
|
||||||
* of Minecraft, so it is invisible and always active.
|
|
||||||
*
|
*
|
||||||
* @param[name] Name of the resource pack
|
* @param[name] Name of the resource pack
|
||||||
* @param[generators] List of resource generators
|
* @param[generators] List of resource generators
|
||||||
*/
|
*/
|
||||||
class GeneratorPack(val name: String, vararg val generators: GeneratorBase) : IResourcePack {
|
class GeneratorPack(val packName: String, val packDescription: String, val packImage: String) : IResourcePack {
|
||||||
|
|
||||||
fun inject() {
|
val generators = mutableListOf<GeneratorBase<*>>()
|
||||||
FMLClientHandler.instance().reflectField<MutableList<IResourcePack>>("resourcePackList")!!.add(this)
|
|
||||||
|
val packFinder = Finder(this)
|
||||||
|
override fun getName() = packName
|
||||||
|
override fun getResourceNamespaces(type: ResourcePackType) = if (type == CLIENT_RESOURCES) generators.map { it.namespace }.toSet() else emptySet()
|
||||||
|
|
||||||
|
override fun <T : Any?> getMetadata(deserializer: IMetadataSectionSerializer<T>): T? {
|
||||||
|
if (deserializer.sectionName != "pack") return null
|
||||||
|
return PackMetadataSection(StringTextComponent(packDescription), 4) as? T
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getPackName() = name
|
override fun resourceExists(type: ResourcePackType, location: ResourceLocation?) =
|
||||||
override fun getPackImage() = null
|
location != null &&
|
||||||
override fun getResourceDomains() = HashSet(generators.map { it.domain })
|
type == CLIENT_RESOURCES &&
|
||||||
override fun <T : IMetadataSection?> getPackMetadata(serializer: MetadataSerializer?, sectionName: String?) =
|
generators.find { it.namespace == location.namespace && it.resourceExists(location) } != null
|
||||||
if (sectionName == "pack") PackMetadataSection(TextComponentString("Generated resources"), 1) as? T else null
|
|
||||||
|
|
||||||
override fun resourceExists(location: ResourceLocation?): Boolean =
|
override fun getResourceStream(type: ResourcePackType, location: ResourceLocation) =
|
||||||
if (location == null) false
|
if (location != null && type == CLIENT_RESOURCES)
|
||||||
else generators.find {
|
generators.firstOrNull { it.namespace == location.namespace && it.resourceExists(location) }?.getInputStream(location)
|
||||||
it.domain == location.namespace && it.resourceExists(location)
|
else
|
||||||
} != null
|
null
|
||||||
|
|
||||||
override fun getInputStream(location: ResourceLocation?): InputStream? =
|
override fun getAllResourceLocations(type: ResourcePackType, pathIn: String, maxDepth: Int, filter: Predicate<String>) = emptyList<ResourceLocation>()
|
||||||
if (location == null) null
|
override fun getRootResourceStream(fileName: String) = fileName.let { if (it == "pack.png") packImage else it }.let { this::class.java.classLoader.getResourceAsStream(it) }
|
||||||
else generators.filter {
|
override fun close() {}
|
||||||
it.domain == location.namespace && it.resourceExists(location)
|
|
||||||
}.map { it.getInputStream(location) }
|
|
||||||
.filterNotNull().first()
|
|
||||||
|
|
||||||
// override fun <T : IMetadataSection?> getPackMetadata(p_135058_1_: IMetadataSerializer?, p_135058_2_: String?): T {
|
class Finder(val pack: GeneratorPack) : IPackFinder {
|
||||||
// return if (type == "pack") PackMetadataSection(ChatComponentText("Generated resources"), 1) else null
|
override fun <T : ResourcePackInfo> addPackInfosToMap(nameToPackMap: MutableMap<String, T>, packInfoFactory: ResourcePackInfo.IFactory<T>) {
|
||||||
// }
|
val packInfo = ResourcePackInfo.createResourcePack(
|
||||||
|
pack.packName,
|
||||||
|
true,
|
||||||
|
Supplier { pack } as Supplier<IResourcePack>,
|
||||||
|
packInfoFactory,
|
||||||
|
ResourcePackInfo.Priority.BOTTOM
|
||||||
|
)
|
||||||
|
nameToPackMap[pack.packName] = packInfo!!
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract base class for resource generators
|
* Abstract base class for resource generators
|
||||||
*
|
*
|
||||||
* @param[domain] Resource domain of generator
|
* @param[namespace] Resource namespace of generator
|
||||||
|
* @param[generatedType] IResourceType of generated resources
|
||||||
*/
|
*/
|
||||||
abstract class GeneratorBase(val domain: String) {
|
abstract class GeneratorBase<T>(val namespace: String, val generatedType: IResourceType) : ISelectiveResourceReloadListener {
|
||||||
/** @see [IResourcePack.resourceExists] */
|
val keyToId = mutableMapOf<T, String>()
|
||||||
abstract fun resourceExists(location: ResourceLocation?): Boolean
|
val idToKey = mutableMapOf<String, T>()
|
||||||
|
open val locationMapper: (ResourceLocation)->ResourceLocation = { it }
|
||||||
|
|
||||||
|
init { resourceManager.addReloadListener(this) }
|
||||||
|
|
||||||
|
abstract fun get(key: T): InputStream?
|
||||||
|
abstract fun exists(key: T): Boolean
|
||||||
|
|
||||||
|
fun registerResource(key: T): ResourceLocation {
|
||||||
|
keyToId[key]?.let { return ResourceLocation(namespace, it) }
|
||||||
|
val id = UUID.randomUUID().toString()
|
||||||
|
keyToId[key] = id
|
||||||
|
idToKey[id] = key
|
||||||
|
return ResourceLocation(namespace, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resourceExists(location: ResourceLocation?): Boolean {
|
||||||
|
val key = location?.let { locationMapper(it) }?.path?.let { idToKey[it] } ?: return false
|
||||||
|
return exists(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getInputStream(location: ResourceLocation?): InputStream? {
|
||||||
|
val key = location?.let { locationMapper(it) }?.path?.let { idToKey[it] } ?: return null
|
||||||
|
return get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun onReload(resourceManager: IResourceManager) {
|
||||||
|
keyToId.clear()
|
||||||
|
idToKey.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResourceManagerReload(resourceManager: IResourceManager, resourcePredicate: Predicate<IResourceType>) {
|
||||||
|
if (resourcePredicate.test(generatedType)) onReload(resourceManager)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** @see [IResourcePack.getInputStream] */
|
|
||||||
abstract fun getInputStream(location: ResourceLocation?): InputStream?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -70,6 +116,7 @@ abstract class GeneratorBase(val domain: String) {
|
|||||||
* @param[params] key-value pairs
|
* @param[params] key-value pairs
|
||||||
* @param[value] keyless extra value
|
* @param[value] keyless extra value
|
||||||
*/
|
*/
|
||||||
|
/*
|
||||||
class ParameterList(val params: Map<String, String>, val value: String?) {
|
class ParameterList(val params: Map<String, String>, val value: String?) {
|
||||||
override fun toString() =
|
override fun toString() =
|
||||||
params.entries
|
params.entries
|
||||||
@@ -118,3 +165,4 @@ abstract class ParameterBasedGenerator(domain: String) : GeneratorBase(domain) {
|
|||||||
override fun getInputStream(location: ResourceLocation?) =
|
override fun getInputStream(location: ResourceLocation?) =
|
||||||
getInputStream(ParameterList.fromString(location?.path ?: ""))
|
getInputStream(ParameterList.fromString(location?.path ?: ""))
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
@@ -3,29 +3,42 @@ package mods.octarinecore.client.resource
|
|||||||
import mods.octarinecore.client.render.Model
|
import mods.octarinecore.client.render.Model
|
||||||
import mods.octarinecore.common.Double3
|
import mods.octarinecore.common.Double3
|
||||||
import mods.octarinecore.common.Int3
|
import mods.octarinecore.common.Int3
|
||||||
|
import mods.octarinecore.stripEnd
|
||||||
|
import mods.octarinecore.stripStart
|
||||||
|
import net.minecraft.client.renderer.texture.AtlasTexture
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||||
import net.minecraft.client.renderer.texture.TextureMap
|
|
||||||
import net.minecraft.util.ResourceLocation
|
import net.minecraft.util.ResourceLocation
|
||||||
import net.minecraft.util.math.BlockPos
|
import net.minecraft.util.math.BlockPos
|
||||||
import net.minecraft.util.math.MathHelper
|
import net.minecraft.util.math.MathHelper
|
||||||
import net.minecraft.world.World
|
import net.minecraft.world.IWorld
|
||||||
import net.minecraft.world.gen.NoiseGeneratorSimplex
|
import net.minecraft.world.gen.SimplexNoiseGenerator
|
||||||
import net.minecraftforge.client.event.TextureStitchEvent
|
import net.minecraftforge.client.event.TextureStitchEvent
|
||||||
import net.minecraftforge.common.MinecraftForge
|
import net.minecraftforge.common.MinecraftForge
|
||||||
import net.minecraftforge.event.world.WorldEvent
|
import net.minecraftforge.event.world.WorldEvent
|
||||||
|
import net.minecraftforge.eventbus.api.IEventBus
|
||||||
|
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||||
import net.minecraftforge.fml.client.event.ConfigChangedEvent
|
import net.minecraftforge.fml.client.event.ConfigChangedEvent
|
||||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
|
import net.minecraftforge.fml.config.ModConfig
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
enum class Atlas(val basePath: String) {
|
||||||
|
BLOCKS("textures"),
|
||||||
|
PARTICLES("textures/particle");
|
||||||
|
|
||||||
|
fun wrap(resource: ResourceLocation) = ResourceLocation(resource.namespace, "$basePath/${resource.path}.png")
|
||||||
|
fun unwrap(resource: ResourceLocation) = resource.stripStart("$basePath/").stripEnd(".png")
|
||||||
|
fun matches(event: TextureStitchEvent) = event.map.basePath == basePath
|
||||||
|
}
|
||||||
|
|
||||||
// ============================
|
// ============================
|
||||||
// Resource types
|
// Resource types
|
||||||
// ============================
|
// ============================
|
||||||
interface IStitchListener {
|
interface IStitchListener {
|
||||||
fun onPreStitch(atlas: TextureMap)
|
fun onPreStitch(event: TextureStitchEvent.Pre)
|
||||||
fun onPostStitch(atlas: TextureMap)
|
fun onPostStitch(atlas: AtlasTexture)
|
||||||
}
|
}
|
||||||
interface IConfigChangeListener { fun onConfigChange() }
|
interface IConfigChangeListener { fun onConfigChange() }
|
||||||
interface IWorldLoadListener { fun onWorldLoad(world: World) }
|
interface IWorldLoadListener { fun onWorldLoad(world: IWorld) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for declarative resource handling.
|
* Base class for declarative resource handling.
|
||||||
@@ -34,7 +47,11 @@ interface IWorldLoadListener { fun onWorldLoad(world: World) }
|
|||||||
*
|
*
|
||||||
* @param[modId] mod ID associated with this handler (used to filter config change events)
|
* @param[modId] mod ID associated with this handler (used to filter config change events)
|
||||||
*/
|
*/
|
||||||
open class ResourceHandler(val modId: String) {
|
open class ResourceHandler(
|
||||||
|
val modId: String,
|
||||||
|
val modBus: IEventBus,
|
||||||
|
val targetAtlas: Atlas = Atlas.BLOCKS
|
||||||
|
) {
|
||||||
|
|
||||||
val resources = mutableListOf<Any>()
|
val resources = mutableListOf<Any>()
|
||||||
open fun afterPreStitch() {}
|
open fun afterPreStitch() {}
|
||||||
@@ -43,15 +60,15 @@ open class ResourceHandler(val modId: String) {
|
|||||||
// ============================
|
// ============================
|
||||||
// Self-registration
|
// Self-registration
|
||||||
// ============================
|
// ============================
|
||||||
init { MinecraftForge.EVENT_BUS.register(this) }
|
init { modBus.register(this) }
|
||||||
|
|
||||||
// ============================
|
// ============================
|
||||||
// Resource declarations
|
// Resource declarations
|
||||||
// ============================
|
// ============================
|
||||||
fun iconStatic(domain: String, path: String) = IconHolder(domain, path).apply { resources.add(this) }
|
fun iconStatic(location: ()->ResourceLocation) = IconHolder(location).apply { resources.add(this) }
|
||||||
fun iconStatic(location: ResourceLocation) = iconStatic(location.namespace, location.path)
|
fun iconStatic(location: ResourceLocation) = iconStatic { location }
|
||||||
fun iconSet(domain: String, pathPattern: String) = IconSet(domain, pathPattern).apply { this@ResourceHandler.resources.add(this) }
|
fun iconStatic(domain: String, path: String) = iconStatic(ResourceLocation(domain, path))
|
||||||
fun iconSet(location: ResourceLocation) = iconSet(location.namespace, location.path)
|
fun iconSet(targetAtlas: Atlas = Atlas.BLOCKS, location: (Int)->ResourceLocation) = IconSet(targetAtlas, location).apply { this@ResourceHandler.resources.add(this) }
|
||||||
fun model(init: Model.()->Unit) = ModelHolder(init).apply { resources.add(this) }
|
fun model(init: Model.()->Unit) = ModelHolder(init).apply { resources.add(this) }
|
||||||
fun modelSet(num: Int, init: Model.(Int)->Unit) = ModelSet(num, init).apply { resources.add(this) }
|
fun modelSet(num: Int, init: Model.(Int)->Unit) = ModelSet(num, init).apply { resources.add(this) }
|
||||||
fun vectorSet(num: Int, init: (Int)-> Double3) = VectorSet(num, init).apply { resources.add(this) }
|
fun vectorSet(num: Int, init: (Int)-> Double3) = VectorSet(num, init).apply { resources.add(this) }
|
||||||
@@ -62,19 +79,21 @@ open class ResourceHandler(val modId: String) {
|
|||||||
// ============================
|
// ============================
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
fun onPreStitch(event: TextureStitchEvent.Pre) {
|
fun onPreStitch(event: TextureStitchEvent.Pre) {
|
||||||
resources.forEach { (it as? IStitchListener)?.onPreStitch(event.map) }
|
if (!targetAtlas.matches(event)) return
|
||||||
|
resources.forEach { (it as? IStitchListener)?.onPreStitch(event) }
|
||||||
afterPreStitch()
|
afterPreStitch()
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
fun onPostStitch(event: TextureStitchEvent.Post) {
|
fun onPostStitch(event: TextureStitchEvent.Post) {
|
||||||
|
if (!targetAtlas.matches(event)) return
|
||||||
resources.forEach { (it as? IStitchListener)?.onPostStitch(event.map) }
|
resources.forEach { (it as? IStitchListener)?.onPostStitch(event.map) }
|
||||||
afterPostStitch()
|
afterPostStitch()
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
fun handleConfigChange(event: ConfigChangedEvent.OnConfigChangedEvent) {
|
fun handleModConfigChange(event: ModConfig.ModConfigEvent) {
|
||||||
if (event.modID == modId) resources.forEach { (it as? IConfigChangeListener)?.onConfigChange() }
|
resources.forEach { (it as? IConfigChangeListener)?.onConfigChange() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
@@ -85,33 +104,38 @@ open class ResourceHandler(val modId: String) {
|
|||||||
// ============================
|
// ============================
|
||||||
// Resource container classes
|
// Resource container classes
|
||||||
// ============================
|
// ============================
|
||||||
class IconHolder(val domain: String, val name: String) : IStitchListener {
|
class IconHolder(val location: ()->ResourceLocation) : IStitchListener {
|
||||||
val iconRes = ResourceLocation(domain, name)
|
var iconRes: ResourceLocation? = null
|
||||||
var icon: TextureAtlasSprite? = null
|
var icon: TextureAtlasSprite? = null
|
||||||
override fun onPreStitch(atlas: TextureMap) { atlas.registerSprite(iconRes) }
|
override fun onPreStitch(event: TextureStitchEvent.Pre) {
|
||||||
override fun onPostStitch(atlas: TextureMap) { icon = atlas[iconRes] }
|
iconRes = location()
|
||||||
|
event.addSprite(iconRes)
|
||||||
|
}
|
||||||
|
override fun onPostStitch(atlas: AtlasTexture) {
|
||||||
|
icon = atlas[iconRes!!]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ModelHolder(val init: Model.()->Unit): IConfigChangeListener {
|
class ModelHolder(val init: Model.()->Unit): IConfigChangeListener {
|
||||||
var model: Model = Model().apply(init)
|
var model: Model = Model()
|
||||||
override fun onConfigChange() { model = Model().apply(init) }
|
override fun onConfigChange() { model = Model().apply(init) }
|
||||||
}
|
}
|
||||||
|
|
||||||
class IconSet(val domain: String, val namePattern: String) : IStitchListener {
|
class IconSet(val targetAtlas: Atlas, val location: (Int)->ResourceLocation) : IStitchListener {
|
||||||
val resources = arrayOfNulls<ResourceLocation>(16)
|
val resources = arrayOfNulls<ResourceLocation>(16)
|
||||||
val icons = arrayOfNulls<TextureAtlasSprite>(16)
|
val icons = arrayOfNulls<TextureAtlasSprite>(16)
|
||||||
var num = 0
|
var num = 0
|
||||||
|
|
||||||
override fun onPreStitch(atlas: TextureMap) {
|
override fun onPreStitch(event: TextureStitchEvent.Pre) {
|
||||||
num = 0
|
num = 0
|
||||||
(0..15).forEach { idx ->
|
(0..15).forEach { idx ->
|
||||||
icons[idx] = null
|
icons[idx] = null
|
||||||
val locReal = ResourceLocation(domain, "textures/${namePattern.format(idx)}.png")
|
val loc = location(idx)
|
||||||
if (resourceManager[locReal] != null) resources[num++] = ResourceLocation(domain, namePattern.format(idx)).apply { atlas.registerSprite(this) }
|
if (resourceManager[targetAtlas.wrap(loc)] != null) resources[num++] = loc.apply { event.addSprite(this) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPostStitch(atlas: TextureMap) {
|
override fun onPostStitch(atlas: AtlasTexture) {
|
||||||
(0 until num).forEach { idx -> icons[idx] = atlas[resources[idx]!!] }
|
(0 until num).forEach { idx -> icons[idx] = atlas[resources[idx]!!] }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,20 +143,20 @@ class IconSet(val domain: String, val namePattern: String) : IStitchListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ModelSet(val num: Int, val init: Model.(Int)->Unit): IConfigChangeListener {
|
class ModelSet(val num: Int, val init: Model.(Int)->Unit): IConfigChangeListener {
|
||||||
val models = Array(num) { Model().apply{ init(it) } }
|
val models = Array(num) { Model() }
|
||||||
override fun onConfigChange() { (0 until num).forEach { models[it] = Model().apply{ init(it) } } }
|
override fun onConfigChange() { (0 until num).forEach { models[it] = Model().apply{ init(it) } } }
|
||||||
operator fun get(idx: Int) = models[idx % num]
|
operator fun get(idx: Int) = models[idx % num]
|
||||||
}
|
}
|
||||||
|
|
||||||
class VectorSet(val num: Int, val init: (Int)->Double3): IConfigChangeListener {
|
class VectorSet(val num: Int, val init: (Int)->Double3): IConfigChangeListener {
|
||||||
val models = Array(num) { init(it) }
|
val models = Array(num) { Double3.zero }
|
||||||
override fun onConfigChange() { (0 until num).forEach { models[it] = init(it) } }
|
override fun onConfigChange() { (0 until num).forEach { models[it] = init(it) } }
|
||||||
operator fun get(idx: Int) = models[idx % num]
|
operator fun get(idx: Int) = models[idx % num]
|
||||||
}
|
}
|
||||||
|
|
||||||
class SimplexNoise() : IWorldLoadListener {
|
class SimplexNoise : IWorldLoadListener {
|
||||||
var noise = NoiseGeneratorSimplex()
|
var noise = SimplexNoiseGenerator(Random())
|
||||||
override fun onWorldLoad(world: World) { noise = NoiseGeneratorSimplex(Random(world.worldInfo.seed))
|
override fun onWorldLoad(world: IWorld) { noise = SimplexNoiseGenerator(Random(world.worldInfo.seed))
|
||||||
}
|
}
|
||||||
operator fun get(x: Int, z: Int) = MathHelper.floor((noise.getValue(x.toDouble(), z.toDouble()) + 1.0) * 32.0)
|
operator fun get(x: Int, z: Int) = MathHelper.floor((noise.getValue(x.toDouble(), z.toDouble()) + 1.0) * 32.0)
|
||||||
operator fun get(pos: Int3) = get(pos.x, pos.z)
|
operator fun get(pos: Int3) = get(pos.x, pos.z)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package mods.octarinecore.client.resource
|
package mods.octarinecore.client.resource
|
||||||
|
|
||||||
import mods.octarinecore.client.resource.ResourceType.*
|
import mods.octarinecore.client.resource.ResourceType.*
|
||||||
import net.minecraft.client.resources.IResource
|
import net.minecraft.resources.IResource
|
||||||
import net.minecraft.util.ResourceLocation
|
import net.minecraft.util.ResourceLocation
|
||||||
import java.awt.image.BufferedImage
|
import java.awt.image.BufferedImage
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
@@ -20,6 +20,7 @@ enum class ResourceType {
|
|||||||
*
|
*
|
||||||
* @param[domain] Resource domain of generator
|
* @param[domain] Resource domain of generator
|
||||||
*/
|
*/
|
||||||
|
/*
|
||||||
abstract class TextureGenerator(domain: String) : ParameterBasedGenerator(domain) {
|
abstract class TextureGenerator(domain: String) : ParameterBasedGenerator(domain) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -29,7 +30,7 @@ abstract class TextureGenerator(domain: String) : ParameterBasedGenerator(domain
|
|||||||
* @param[extraParams] additional parameters of the generated texture
|
* @param[extraParams] additional parameters of the generated texture
|
||||||
*/
|
*/
|
||||||
fun generatedResource(iconName: String, vararg extraParams: Pair<String, Any>) = ResourceLocation(
|
fun generatedResource(iconName: String, vararg extraParams: Pair<String, Any>) = ResourceLocation(
|
||||||
domain,
|
namespace,
|
||||||
textureLocation(iconName).let {
|
textureLocation(iconName).let {
|
||||||
ParameterList(
|
ParameterList(
|
||||||
mapOf("dom" to it.namespace, "path" to it.path) +
|
mapOf("dom" to it.namespace, "path" to it.path) +
|
||||||
@@ -84,3 +85,5 @@ abstract class TextureGenerator(domain: String) : ParameterBasedGenerator(domain
|
|||||||
return sizes.map { resourceManager[maskPath(it)] }.filterNotNull().firstOrNull()
|
return sizes.map { resourceManager[maskPath(it)] }.filterNotNull().firstOrNull()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
@@ -4,16 +4,17 @@ package mods.octarinecore.client.resource
|
|||||||
import mods.betterfoliage.loader.Refs
|
import mods.betterfoliage.loader.Refs
|
||||||
import mods.octarinecore.PI2
|
import mods.octarinecore.PI2
|
||||||
import mods.octarinecore.client.render.HSB
|
import mods.octarinecore.client.render.HSB
|
||||||
import mods.octarinecore.metaprog.reflectField
|
import mods.octarinecore.stripEnd
|
||||||
import mods.octarinecore.stripStart
|
import mods.octarinecore.stripStart
|
||||||
import mods.octarinecore.tryDefault
|
import mods.octarinecore.tryDefault
|
||||||
import net.minecraft.client.Minecraft
|
import net.minecraft.client.Minecraft
|
||||||
import net.minecraft.client.renderer.block.model.ModelBlock
|
import net.minecraft.client.renderer.model.BlockModel
|
||||||
|
import net.minecraft.client.renderer.texture.AtlasTexture
|
||||||
|
import net.minecraft.client.renderer.texture.MissingTextureSprite
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||||
import net.minecraft.client.renderer.texture.TextureMap
|
import net.minecraft.resources.IResource
|
||||||
import net.minecraft.client.resources.IResource
|
import net.minecraft.resources.IResourceManager
|
||||||
import net.minecraft.client.resources.IResourceManager
|
import net.minecraft.resources.SimpleReloadableResourceManager
|
||||||
import net.minecraft.client.resources.SimpleReloadableResourceManager
|
|
||||||
import net.minecraft.util.ResourceLocation
|
import net.minecraft.util.ResourceLocation
|
||||||
import net.minecraftforge.client.model.IModel
|
import net.minecraftforge.client.model.IModel
|
||||||
import java.awt.image.BufferedImage
|
import java.awt.image.BufferedImage
|
||||||
@@ -24,8 +25,9 @@ import java.lang.Math.*
|
|||||||
import javax.imageio.ImageIO
|
import javax.imageio.ImageIO
|
||||||
|
|
||||||
/** Concise getter for the Minecraft resource manager. */
|
/** Concise getter for the Minecraft resource manager. */
|
||||||
val resourceManager: SimpleReloadableResourceManager get() =
|
val resourceManager: SimpleReloadableResourceManager
|
||||||
Minecraft.getMinecraft().resourceManager as SimpleReloadableResourceManager
|
get() =
|
||||||
|
Minecraft.getInstance().resourceManager as SimpleReloadableResourceManager
|
||||||
|
|
||||||
/** Append a string to the [ResourceLocation]'s path. */
|
/** Append a string to the [ResourceLocation]'s path. */
|
||||||
operator fun ResourceLocation.plus(str: String) = ResourceLocation(namespace, path + str)
|
operator fun ResourceLocation.plus(str: String) = ResourceLocation(namespace, path + str)
|
||||||
@@ -36,8 +38,10 @@ operator fun IResourceManager.get(domain: String, path: String): IResource? = ge
|
|||||||
operator fun IResourceManager.get(location: ResourceLocation): IResource? = tryDefault(null) { getResource(location) }
|
operator fun IResourceManager.get(location: ResourceLocation): IResource? = tryDefault(null) { getResource(location) }
|
||||||
|
|
||||||
/** Index operator to get a texture sprite. */
|
/** Index operator to get a texture sprite. */
|
||||||
operator fun TextureMap.get(name: String): TextureAtlasSprite? = getTextureExtry(ResourceLocation(name).toString())
|
operator fun AtlasTexture.get(res: ResourceLocation): TextureAtlasSprite? = getSprite(res)
|
||||||
operator fun TextureMap.get(res: ResourceLocation): TextureAtlasSprite? = getTextureExtry(res.toString())
|
operator fun AtlasTexture.get(name: String): TextureAtlasSprite? = getSprite(ResourceLocation(name))
|
||||||
|
|
||||||
|
val missingSprite: TextureAtlasSprite get() = MissingTextureSprite.func_217790_a()
|
||||||
|
|
||||||
/** Load an image resource. */
|
/** Load an image resource. */
|
||||||
fun IResource.loadImage(): BufferedImage? = ImageIO.read(this.inputStream)
|
fun IResource.loadImage(): BufferedImage? = ImageIO.read(this.inputStream)
|
||||||
@@ -65,18 +69,18 @@ val BufferedImage.asStream: InputStream get() =
|
|||||||
* and the result transformed back to the RGB color space.
|
* and the result transformed back to the RGB color space.
|
||||||
*/
|
*/
|
||||||
val TextureAtlasSprite.averageColor: Int? get() {
|
val TextureAtlasSprite.averageColor: Int? get() {
|
||||||
val locationNoDirs = ResourceLocation(iconName).stripStart("blocks/")
|
// val locationNoDirs = ResourceLocation(iconName).stripStart("blocks/")
|
||||||
val locationWithDirs = ResourceLocation(locationNoDirs.namespace, "textures/blocks/%s.png".format(locationNoDirs.path))
|
// val locationWithDirs = ResourceLocation(locationNoDirs.namespace, "textures/blocks/%s.png".format(locationNoDirs.path))
|
||||||
val image = resourceManager[locationWithDirs]?.loadImage() ?: return null
|
// val image = resourceManager[locationWithDirs]?.loadImage() ?: return null
|
||||||
|
|
||||||
var numOpaque = 0
|
var numOpaque = 0
|
||||||
var sumHueX = 0.0
|
var sumHueX = 0.0
|
||||||
var sumHueY = 0.0
|
var sumHueY = 0.0
|
||||||
var sumSaturation = 0.0f
|
var sumSaturation = 0.0f
|
||||||
var sumBrightness = 0.0f
|
var sumBrightness = 0.0f
|
||||||
for (x in 0..image.width - 1)
|
for (x in 0..width - 1)
|
||||||
for (y in 0..image.height - 1) {
|
for (y in 0..height - 1) {
|
||||||
val pixel = image[x, y]
|
val pixel = getPixelRGBA(0, x, y);
|
||||||
val alpha = (pixel shr 24) and 255
|
val alpha = (pixel shr 24) and 255
|
||||||
val hsb = HSB.fromColor(pixel)
|
val hsb = HSB.fromColor(pixel)
|
||||||
if (alpha == 255) {
|
if (alpha == 255) {
|
||||||
@@ -101,27 +105,9 @@ fun textureLocation(iconName: String) = ResourceLocation(iconName).let {
|
|||||||
else ResourceLocation(it.namespace, "textures/${it.path}")
|
else ResourceLocation(it.namespace, "textures/${it.path}")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
fun Pair<BlockModel, ResourceLocation>.derivesFrom(targetLocation: ResourceLocation): Boolean {
|
||||||
val IModel.modelBlockAndLoc: List<Pair<ModelBlock, ResourceLocation>> get() {
|
|
||||||
if (Refs.VanillaModelWrapper.isInstance(this))
|
|
||||||
return listOf(Pair(Refs.model_VMW.get(this) as ModelBlock, Refs.location_VMW.get(this) as ResourceLocation))
|
|
||||||
else if (Refs.WeightedRandomModel.isInstance(this)) Refs.models_WRM.get(this)?.let {
|
|
||||||
return (it as List<IModel>).flatMap(IModel::modelBlockAndLoc)
|
|
||||||
}
|
|
||||||
else if (Refs.MultipartModel.isInstance(this)) Refs.partModels_MPM.get(this)?.let {
|
|
||||||
return (it as Map<Any, IModel>).flatMap { it.value.modelBlockAndLoc }
|
|
||||||
} else {
|
|
||||||
this::class.java.declaredFields.find { it.type.isInstance(IModel::class.java) }?.let { modelField ->
|
|
||||||
modelField.isAccessible = true
|
|
||||||
return (modelField.get(this) as IModel).modelBlockAndLoc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return listOf()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Pair<ModelBlock, ResourceLocation>.derivesFrom(targetLocation: ResourceLocation): Boolean {
|
|
||||||
if (second.stripStart("models/") == targetLocation) return true
|
if (second.stripStart("models/") == targetLocation) return true
|
||||||
if (first.parent != null && first.parentLocation != null)
|
if (first.parent != null && first.parentLocation != null)
|
||||||
return Pair(first.parent, first.parentLocation!!).derivesFrom(targetLocation)
|
return Pair(first.parent!!, first.parentLocation!!).derivesFrom(targetLocation)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
package mods.octarinecore.common
|
package mods.octarinecore.common
|
||||||
|
|
||||||
import mods.octarinecore.cross
|
import mods.octarinecore.cross
|
||||||
import net.minecraft.util.EnumFacing
|
import net.minecraft.util.Direction
|
||||||
import net.minecraft.util.EnumFacing.*
|
import net.minecraft.util.Direction.*
|
||||||
import net.minecraft.util.EnumFacing.Axis.*
|
import net.minecraft.util.Direction.Axis.*
|
||||||
import net.minecraft.util.EnumFacing.AxisDirection.NEGATIVE
|
import net.minecraft.util.Direction.AxisDirection.NEGATIVE
|
||||||
import net.minecraft.util.EnumFacing.AxisDirection.POSITIVE
|
import net.minecraft.util.Direction.AxisDirection.POSITIVE
|
||||||
import net.minecraft.util.math.BlockPos
|
import net.minecraft.util.math.BlockPos
|
||||||
|
|
||||||
// ================================
|
// ================================
|
||||||
@@ -13,19 +13,19 @@ import net.minecraft.util.math.BlockPos
|
|||||||
// ================================
|
// ================================
|
||||||
val axes = listOf(X, Y, Z)
|
val axes = listOf(X, Y, Z)
|
||||||
val axisDirs = listOf(POSITIVE, NEGATIVE)
|
val axisDirs = listOf(POSITIVE, NEGATIVE)
|
||||||
val EnumFacing.dir: AxisDirection get() = axisDirection
|
val Direction.dir: AxisDirection get() = axisDirection
|
||||||
val AxisDirection.sign: String get() = when(this) { POSITIVE -> "+"; NEGATIVE -> "-" }
|
val AxisDirection.sign: String get() = when(this) { POSITIVE -> "+"; NEGATIVE -> "-" }
|
||||||
val forgeDirs = EnumFacing.values()
|
val forgeDirs = Direction.values()
|
||||||
val forgeDirsHorizontal = listOf(NORTH, SOUTH, EAST, WEST)
|
val forgeDirsHorizontal = listOf(NORTH, SOUTH, EAST, WEST)
|
||||||
val forgeDirOffsets = forgeDirs.map { Int3(it) }
|
val forgeDirOffsets = forgeDirs.map { Int3(it) }
|
||||||
val Pair<Axis, AxisDirection>.face: EnumFacing get() = when(this) {
|
val Pair<Axis, AxisDirection>.face: Direction get() = when(this) {
|
||||||
X to POSITIVE -> EAST; X to NEGATIVE -> WEST;
|
X to POSITIVE -> EAST; X to NEGATIVE -> WEST;
|
||||||
Y to POSITIVE -> UP; Y to NEGATIVE -> DOWN;
|
Y to POSITIVE -> UP; Y to NEGATIVE -> DOWN;
|
||||||
Z to POSITIVE -> SOUTH; else -> NORTH;
|
Z to POSITIVE -> SOUTH; else -> NORTH;
|
||||||
}
|
}
|
||||||
val EnumFacing.perpendiculars: List<EnumFacing> get() =
|
val Direction.perpendiculars: List<Direction> get() =
|
||||||
axes.filter { it != this.axis }.cross(axisDirs).map { it.face }
|
axes.filter { it != this.axis }.cross(axisDirs).map { it.face }
|
||||||
val EnumFacing.offset: Int3 get() = forgeDirOffsets[ordinal]
|
val Direction.offset: Int3 get() = forgeDirOffsets[ordinal]
|
||||||
|
|
||||||
/** Old ForgeDirection rotation matrix yanked from 1.7.10 */
|
/** Old ForgeDirection rotation matrix yanked from 1.7.10 */
|
||||||
val ROTATION_MATRIX: Array<IntArray> get() = arrayOf(
|
val ROTATION_MATRIX: Array<IntArray> get() = arrayOf(
|
||||||
@@ -40,15 +40,15 @@ val ROTATION_MATRIX: Array<IntArray> get() = arrayOf(
|
|||||||
// ================================
|
// ================================
|
||||||
// Vectors
|
// Vectors
|
||||||
// ================================
|
// ================================
|
||||||
operator fun EnumFacing.times(scale: Double) =
|
operator fun Direction.times(scale: Double) =
|
||||||
Double3(directionVec.x.toDouble() * scale, directionVec.y.toDouble() * scale, directionVec.z.toDouble() * scale)
|
Double3(directionVec.x.toDouble() * scale, directionVec.y.toDouble() * scale, directionVec.z.toDouble() * scale)
|
||||||
val EnumFacing.vec: Double3 get() = Double3(directionVec.x.toDouble(), directionVec.y.toDouble(), directionVec.z.toDouble())
|
val Direction.vec: Double3 get() = Double3(directionVec.x.toDouble(), directionVec.y.toDouble(), directionVec.z.toDouble())
|
||||||
operator fun BlockPos.plus(other: Int3) = BlockPos(x + other.x, y + other.y, z + other.z)
|
operator fun BlockPos.plus(other: Int3) = BlockPos(x + other.x, y + other.y, z + other.z)
|
||||||
|
|
||||||
/** 3D vector of [Double]s. Offers both mutable operations, and immutable operations in operator notation. */
|
/** 3D vector of [Double]s. Offers both mutable operations, and immutable operations in operator notation. */
|
||||||
data class Double3(var x: Double, var y: Double, var z: Double) {
|
data class Double3(var x: Double, var y: Double, var z: Double) {
|
||||||
constructor(x: Float, y: Float, z: Float) : this(x.toDouble(), y.toDouble(), z.toDouble())
|
constructor(x: Float, y: Float, z: Float) : this(x.toDouble(), y.toDouble(), z.toDouble())
|
||||||
constructor(dir: EnumFacing) : this(dir.directionVec.x.toDouble(), dir.directionVec.y.toDouble(), dir.directionVec.z.toDouble())
|
constructor(dir: Direction) : this(dir.directionVec.x.toDouble(), dir.directionVec.y.toDouble(), dir.directionVec.z.toDouble())
|
||||||
companion object {
|
companion object {
|
||||||
val zero: Double3 get() = Double3(0.0, 0.0, 0.0)
|
val zero: Double3 get() = Double3(0.0, 0.0, 0.0)
|
||||||
fun weight(v1: Double3, weight1: Double, v2: Double3, weight2: Double) =
|
fun weight(v1: Double3, weight1: Double, v2: Double3, weight2: Double) =
|
||||||
@@ -92,13 +92,13 @@ data class Double3(var x: Double, var y: Double, var z: Double) {
|
|||||||
infix fun cross(o: Double3) = Double3(y * o.z - z * o.y, z * o.x - x * o.z, x * o.y - y * o.x)
|
infix fun cross(o: Double3) = Double3(y * o.z - z * o.y, z * o.x - x * o.z, x * o.y - y * o.x)
|
||||||
val length: Double get() = Math.sqrt(x * x + y * y + z * z)
|
val length: Double get() = Math.sqrt(x * x + y * y + z * z)
|
||||||
val normalize: Double3 get() = (1.0 / length).let { Double3(x * it, y * it, z * it) }
|
val normalize: Double3 get() = (1.0 / length).let { Double3(x * it, y * it, z * it) }
|
||||||
val nearestCardinal: EnumFacing get() = nearestAngle(this, forgeDirs.asIterable()) { it.vec }.first
|
val nearestCardinal: Direction get() = nearestAngle(this, forgeDirs.asIterable()) { it.vec }.first
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 3D vector of [Int]s. Offers both mutable operations, and immutable operations in operator notation. */
|
/** 3D vector of [Int]s. Offers both mutable operations, and immutable operations in operator notation. */
|
||||||
data class Int3(var x: Int, var y: Int, var z: Int) {
|
data class Int3(var x: Int, var y: Int, var z: Int) {
|
||||||
constructor(dir: EnumFacing) : this(dir.directionVec.x, dir.directionVec.y, dir.directionVec.z)
|
constructor(dir: Direction) : this(dir.directionVec.x, dir.directionVec.y, dir.directionVec.z)
|
||||||
constructor(offset: Pair<Int, EnumFacing>) : this(
|
constructor(offset: Pair<Int, Direction>) : this(
|
||||||
offset.first * offset.second.directionVec.x,
|
offset.first * offset.second.directionVec.x,
|
||||||
offset.first * offset.second.directionVec.y,
|
offset.first * offset.second.directionVec.y,
|
||||||
offset.first * offset.second.directionVec.z
|
offset.first * offset.second.directionVec.z
|
||||||
@@ -109,7 +109,7 @@ data class Int3(var x: Int, var y: Int, var z: Int) {
|
|||||||
|
|
||||||
// immutable operations
|
// immutable operations
|
||||||
operator fun plus(other: Int3) = Int3(x + other.x, y + other.y, z + other.z)
|
operator fun plus(other: Int3) = Int3(x + other.x, y + other.y, z + other.z)
|
||||||
operator fun plus(other: Pair<Int, EnumFacing>) = Int3(
|
operator fun plus(other: Pair<Int, Direction>) = Int3(
|
||||||
x + other.first * other.second.directionVec.x,
|
x + other.first * other.second.directionVec.x,
|
||||||
y + other.first * other.second.directionVec.y,
|
y + other.first * other.second.directionVec.y,
|
||||||
z + other.first * other.second.directionVec.z
|
z + other.first * other.second.directionVec.z
|
||||||
@@ -145,17 +145,17 @@ data class Int3(var x: Int, var y: Int, var z: Int) {
|
|||||||
// ================================
|
// ================================
|
||||||
// Rotation
|
// Rotation
|
||||||
// ================================
|
// ================================
|
||||||
val EnumFacing.rotations: Array<EnumFacing> get() =
|
val Direction.rotations: Array<Direction> get() =
|
||||||
Array(6) { idx -> EnumFacing.values()[ROTATION_MATRIX[ordinal][idx]] }
|
Array(6) { idx -> Direction.values()[ROTATION_MATRIX[ordinal][idx]] }
|
||||||
fun EnumFacing.rotate(rot: Rotation) = rot.forward[ordinal]
|
fun Direction.rotate(rot: Rotation) = rot.forward[ordinal]
|
||||||
fun rot(axis: EnumFacing) = Rotation.rot90[axis.ordinal]
|
fun rot(axis: Direction) = Rotation.rot90[axis.ordinal]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class representing an arbitrary rotation (or combination of rotations) around cardinal axes by 90 degrees.
|
* Class representing an arbitrary rotation (or combination of rotations) around cardinal axes by 90 degrees.
|
||||||
* In effect, a permutation of [ForgeDirection]s.
|
* In effect, a permutation of [ForgeDirection]s.
|
||||||
*/
|
*/
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
class Rotation(val forward: Array<EnumFacing>, val reverse: Array<EnumFacing>) {
|
class Rotation(val forward: Array<Direction>, val reverse: Array<Direction>) {
|
||||||
operator fun plus(other: Rotation) = Rotation(
|
operator fun plus(other: Rotation) = Rotation(
|
||||||
Array(6) { idx -> forward[other.forward[idx].ordinal] },
|
Array(6) { idx -> forward[other.forward[idx].ordinal] },
|
||||||
Array(6) { idx -> other.reverse[reverse[idx].ordinal] }
|
Array(6) { idx -> other.reverse[reverse[idx].ordinal] }
|
||||||
@@ -163,9 +163,9 @@ class Rotation(val forward: Array<EnumFacing>, val reverse: Array<EnumFacing>) {
|
|||||||
operator fun unaryMinus() = Rotation(reverse, forward)
|
operator fun unaryMinus() = Rotation(reverse, forward)
|
||||||
operator fun times(num: Int) = when(num % 4) { 1 -> this; 2 -> this + this; 3 -> -this; else -> identity }
|
operator fun times(num: Int) = when(num % 4) { 1 -> this; 2 -> this + this; 3 -> -this; else -> identity }
|
||||||
|
|
||||||
inline fun rotatedComponent(dir: EnumFacing, x: Int, y: Int, z: Int) =
|
inline fun rotatedComponent(dir: Direction, x: Int, y: Int, z: Int) =
|
||||||
when(reverse[dir.ordinal]) { EAST -> x; WEST -> -x; UP -> y; DOWN -> -y; SOUTH -> z; NORTH -> -z; else -> 0 }
|
when(reverse[dir.ordinal]) { EAST -> x; WEST -> -x; UP -> y; DOWN -> -y; SOUTH -> z; NORTH -> -z; else -> 0 }
|
||||||
inline fun rotatedComponent(dir: EnumFacing, x: Double, y: Double, z: Double) =
|
inline fun rotatedComponent(dir: Direction, x: Double, y: Double, z: Double) =
|
||||||
when(reverse[dir.ordinal]) { EAST -> x; WEST -> -x; UP -> y; DOWN -> -y; SOUTH -> z; NORTH -> -z; else -> 0.0 }
|
when(reverse[dir.ordinal]) { EAST -> x; WEST -> -x; UP -> y; DOWN -> -y; SOUTH -> z; NORTH -> -z; else -> 0.0 }
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -204,11 +204,11 @@ fun <T> nearestPosition(vertex: Double3, objs: Iterable<T>, objPos: (T)-> Double
|
|||||||
fun <T> nearestAngle(vector: Double3, objs: Iterable<T>, objAngle: (T)-> Double3): Pair<T, Double> =
|
fun <T> nearestAngle(vector: Double3, objs: Iterable<T>, objAngle: (T)-> Double3): Pair<T, Double> =
|
||||||
objs.map { it to objAngle(it).dot(vector) }.maxBy { it.second }!!
|
objs.map { it to objAngle(it).dot(vector) }.maxBy { it.second }!!
|
||||||
|
|
||||||
data class FaceCorners(val topLeft: Pair<EnumFacing, EnumFacing>,
|
data class FaceCorners(val topLeft: Pair<Direction, Direction>,
|
||||||
val topRight: Pair<EnumFacing, EnumFacing>,
|
val topRight: Pair<Direction, Direction>,
|
||||||
val bottomLeft: Pair<EnumFacing, EnumFacing>,
|
val bottomLeft: Pair<Direction, Direction>,
|
||||||
val bottomRight: Pair<EnumFacing, EnumFacing>) {
|
val bottomRight: Pair<Direction, Direction>) {
|
||||||
constructor(top: EnumFacing, left: EnumFacing) :
|
constructor(top: Direction, left: Direction) :
|
||||||
this(top to left, top to left.opposite, top.opposite to left, top.opposite to left.opposite)
|
this(top to left, top to left.opposite, top.opposite to left, top.opposite to left.opposite)
|
||||||
|
|
||||||
val asArray = arrayOf(topLeft, topRight, bottomLeft, bottomRight)
|
val asArray = arrayOf(topLeft, topRight, bottomLeft, bottomRight)
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
package mods.octarinecore.common.config
|
|
||||||
|
|
||||||
import mods.octarinecore.client.gui.NonVerboseArrayEntry
|
|
||||||
import mods.octarinecore.client.resource.get
|
|
||||||
import mods.octarinecore.client.resource.getLines
|
|
||||||
import mods.octarinecore.client.resource.resourceManager
|
|
||||||
import net.minecraftforge.common.config.Configuration
|
|
||||||
import net.minecraftforge.common.config.Property
|
|
||||||
|
|
||||||
abstract class BlackWhiteListConfigOption<VALUE>(val domain: String, val path: String) : ConfigPropertyBase() {
|
|
||||||
|
|
||||||
val blackList = mutableListOf<VALUE>()
|
|
||||||
val whiteList = mutableListOf<VALUE>()
|
|
||||||
var blacklistProperty: Property? = null
|
|
||||||
var whitelistProperty: Property? = null
|
|
||||||
|
|
||||||
override val hasChanged: Boolean
|
|
||||||
get() = blacklistProperty?.hasChanged() ?: false || whitelistProperty?.hasChanged() ?: false
|
|
||||||
|
|
||||||
override val guiProperties: List<Property> get() = listOf(whitelistProperty!!, blacklistProperty!!)
|
|
||||||
|
|
||||||
override fun attach(target: Configuration, langPrefix: String, categoryName: String, propertyName: String) {
|
|
||||||
lang = null
|
|
||||||
val defaults = readDefaults(domain, path)
|
|
||||||
blacklistProperty = target.get(categoryName, "${propertyName}Blacklist", defaults.first)
|
|
||||||
whitelistProperty = target.get(categoryName, "${propertyName}Whitelist", defaults.second)
|
|
||||||
listOf(blacklistProperty!!, whitelistProperty!!).forEach {
|
|
||||||
it.configEntryClass = NonVerboseArrayEntry::class.java
|
|
||||||
it.languageKey = "$langPrefix.$categoryName.${it.name}"
|
|
||||||
}
|
|
||||||
read()
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract fun convertValue(line: String): VALUE?
|
|
||||||
|
|
||||||
override fun read() {
|
|
||||||
listOf(Pair(blackList, blacklistProperty!!), Pair(whiteList, whitelistProperty!!)).forEach {
|
|
||||||
it.first.clear()
|
|
||||||
it.second.stringList.forEach { line ->
|
|
||||||
val value = convertValue(line)
|
|
||||||
if (value != null) it.first.add(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun readDefaults(domain: String, path: String): Pair<Array<String>, Array<String>> {
|
|
||||||
val blackList = arrayListOf<String>()
|
|
||||||
val whiteList = arrayListOf<String>()
|
|
||||||
val defaults = resourceManager[domain, path]?.getLines()
|
|
||||||
defaults?.map{ it.trim() }?.filter { !it.startsWith("//") && it.isNotEmpty() }?.forEach {
|
|
||||||
if (it.startsWith("-")) { blackList.add(it.substring(1)) }
|
|
||||||
else { whiteList.add(it) }
|
|
||||||
}
|
|
||||||
return (blackList.toTypedArray() to whiteList.toTypedArray())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,256 +1,80 @@
|
|||||||
|
@file:JvmName("DelegatingConfigKt")
|
||||||
|
|
||||||
package mods.octarinecore.common.config
|
package mods.octarinecore.common.config
|
||||||
|
|
||||||
import com.google.common.collect.LinkedListMultimap
|
import mods.octarinecore.metaprog.reflectDelegates
|
||||||
import mods.betterfoliage.loader.Refs
|
|
||||||
import mods.octarinecore.metaprog.reflectField
|
|
||||||
import mods.octarinecore.metaprog.reflectFieldsOfType
|
|
||||||
import mods.octarinecore.metaprog.reflectNestedObjects
|
import mods.octarinecore.metaprog.reflectNestedObjects
|
||||||
import net.minecraftforge.common.MinecraftForge
|
import net.minecraftforge.common.ForgeConfigSpec
|
||||||
import net.minecraftforge.common.config.ConfigElement
|
import kotlin.properties.ReadOnlyProperty
|
||||||
import net.minecraftforge.common.config.Configuration
|
|
||||||
import net.minecraftforge.common.config.Property
|
|
||||||
import net.minecraftforge.fml.client.config.GuiConfigEntries
|
|
||||||
import net.minecraftforge.fml.client.config.IConfigElement
|
|
||||||
import net.minecraftforge.fml.client.event.ConfigChangedEvent
|
|
||||||
import net.minecraftforge.fml.common.FMLCommonHandler
|
|
||||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
|
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
// ============================
|
open class DelegatingConfig(val modId: String, val langPrefix: String) {
|
||||||
// Configuration object base
|
fun build() = ForgeConfigSpec.Builder().apply { ConfigBuildContext(langPrefix, emptyList(), this).addCategory(this@DelegatingConfig) }.build()
|
||||||
// ============================
|
}
|
||||||
/**
|
|
||||||
* Base class for declarative configuration handling.
|
|
||||||
*
|
|
||||||
* Subclasses should be singleton objects, containing one layer of further singleton objects representing
|
|
||||||
* config categories (nesting is not supported).
|
|
||||||
*
|
|
||||||
* Both the root object (maps to the category _global_) and category objects can contain [ConfigPropertyBase]
|
|
||||||
* instances (either directly or as a delegate), which handle the Forge [Configuration] itself.
|
|
||||||
*
|
|
||||||
* Config properties map to language keys by their field names.
|
|
||||||
*
|
|
||||||
* @param[modId] mod ID this configuration is linked to
|
|
||||||
* @param[langPrefix] prefix to use for language keys
|
|
||||||
*/
|
|
||||||
abstract class DelegatingConfig(val modId: String, val langPrefix: String) {
|
|
||||||
|
|
||||||
init { MinecraftForge.EVENT_BUS.register(this) }
|
class ConfigBuildContext(val langPrefix: String, val path: List<String>, val builder: ForgeConfigSpec.Builder) {
|
||||||
|
|
||||||
/** The [Configuration] backing this config object. */
|
fun addCategory(configObj: Any) {
|
||||||
var config: Configuration? = null
|
configObj.reflectNestedObjects.forEach { (name, category) ->
|
||||||
val rootGuiElements = mutableListOf<IConfigElement>()
|
builder.push(name)
|
||||||
|
descend(name).addCategory(category)
|
||||||
/** Attach this config object to the given [Configuration] and update all properties. */
|
builder.pop()
|
||||||
fun attach(config: Configuration) {
|
|
||||||
this.config = config
|
|
||||||
val subProperties = LinkedListMultimap.create<String, String>()
|
|
||||||
rootGuiElements.clear()
|
|
||||||
|
|
||||||
forEachProperty { category, name, property ->
|
|
||||||
property.lang = property.lang ?: "$category.$name"
|
|
||||||
property.attach(config, langPrefix, category, name)
|
|
||||||
property.guiProperties.forEach { guiProperty ->
|
|
||||||
property.guiClass?.let { guiProperty.setConfigEntryClass(it) }
|
|
||||||
if (category == "global") rootGuiElements.add(ConfigElement(guiProperty))
|
|
||||||
else subProperties.put(category, guiProperty.name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for (category in subProperties.keySet()) {
|
configObj.reflectDelegates(ConfigDelegate::class.java).forEach { (name, delegate) ->
|
||||||
val configCategory = config.getCategory(category)
|
descend(name).apply { delegate.addToBuilder(this) }
|
||||||
configCategory.setLanguageKey("$langPrefix.$category")
|
|
||||||
configCategory.setPropertyOrder(subProperties[category])
|
|
||||||
rootGuiElements.add(ConfigElement(configCategory))
|
|
||||||
}
|
|
||||||
save()
|
|
||||||
|
|
||||||
// hide all categories not in the config singleton
|
|
||||||
config.categoryNames.forEach {
|
|
||||||
config.getCategory(it).setShowInGui(it in subProperties.keySet())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
fun descend(pathName: String) = ConfigBuildContext(langPrefix, path + pathName, builder)
|
||||||
* Execute the given lambda for all config properties.
|
}
|
||||||
* Lambda params: (category name, property name, property instance)
|
|
||||||
*/
|
open class ConfigCategory(val comment: String? = null) {
|
||||||
inline fun forEachProperty(init: (String, String, ConfigPropertyBase)->Unit) {
|
}
|
||||||
reflectFieldsOfType(ConfigPropertyBase::class.java).forEach { property ->
|
|
||||||
init("global", property.first.split("$")[0], property.second as ConfigPropertyBase)
|
abstract class ConfigDelegate<T> : ReadOnlyProperty<Any, T> {
|
||||||
}
|
lateinit var configValue: ForgeConfigSpec.ConfigValue<T>
|
||||||
for (category in reflectNestedObjects) {
|
var cachedValue: T? = null
|
||||||
category.second.reflectFieldsOfType(ConfigPropertyBase::class.java).forEach { property ->
|
|
||||||
init(category.first, property.first.split("$")[0], property.second as ConfigPropertyBase)
|
override fun getValue(thisRef: Any, property: KProperty<*>): T {
|
||||||
}
|
if (cachedValue == null) cachedValue = configValue.get()
|
||||||
}
|
return cachedValue!!
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Save changes to the [Configuration]. */
|
abstract fun getConfigValue(name: String, builder: ForgeConfigSpec.Builder): ForgeConfigSpec.ConfigValue<T>
|
||||||
fun save() { if (config?.hasChanged() ?: false) config!!.save() }
|
fun addToBuilder(ctx: ConfigBuildContext) {
|
||||||
|
val langKey = ctx.langPrefix + "." + (langPrefixOverride ?: ctx.path.joinToString("."))
|
||||||
/**
|
ctx.builder.translation(langKey)
|
||||||
* Returns true if any of the given configuration elements have changed.
|
configValue = getConfigValue(ctx.path.last(), ctx.builder)
|
||||||
* Supports both categories and
|
|
||||||
*/
|
|
||||||
fun hasChanged(elements: List<ConfigPropertyBase>): Boolean {
|
|
||||||
reflectNestedObjects.forEach { category ->
|
|
||||||
if (category.second in elements && config?.getCategory(category.first)?.hasChanged() ?: false) return true
|
|
||||||
}
|
|
||||||
forEachProperty { category, name, property ->
|
|
||||||
if (property in elements && property.hasChanged) return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Called when the configuration for the mod changes. */
|
var langPrefixOverride: String? = null
|
||||||
abstract fun onChange(event: ConfigChangedEvent.PostConfigChangedEvent)
|
fun lang(prefix: String) = apply { langPrefixOverride = prefix }
|
||||||
|
|
||||||
@SubscribeEvent
|
|
||||||
fun handleConfigChange(event: ConfigChangedEvent.PostConfigChangedEvent) {
|
|
||||||
if (event.modID == modId) {
|
|
||||||
// refresh values
|
|
||||||
forEachProperty { c, n, prop -> prop.read() }
|
|
||||||
|
|
||||||
// call mod-specific handler
|
|
||||||
onChange(event)
|
|
||||||
|
|
||||||
// save to file
|
|
||||||
save()
|
|
||||||
Refs.resetChangedState.invoke(config!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Extension to get the underlying delegate of a field */
|
|
||||||
operator fun Any.get(name: String) = this.reflectField<ConfigPropertyBase>("$name\$delegate")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================
|
class DelegatingBooleanValue(val defaultValue: Boolean) : ConfigDelegate<Boolean>() {
|
||||||
// Property delegates
|
override fun getConfigValue(name: String, builder: ForgeConfigSpec.Builder) = builder.define(name, defaultValue)
|
||||||
// ============================
|
|
||||||
|
|
||||||
/** Base class for config property delegates. */
|
|
||||||
abstract class ConfigPropertyBase {
|
|
||||||
/** Language key of the property. */
|
|
||||||
var lang: String? = null
|
|
||||||
|
|
||||||
/** GUI class to use. */
|
|
||||||
var guiClass: Class<out GuiConfigEntries.IConfigEntry>? = null
|
|
||||||
|
|
||||||
/** @return true if the property has changed. */
|
|
||||||
abstract val hasChanged: Boolean
|
|
||||||
|
|
||||||
/** Attach this delegate to a Forge [Configuration]. */
|
|
||||||
abstract fun attach(target: Configuration, langPrefix: String, categoryName: String, propertyName: String)
|
|
||||||
|
|
||||||
/** List of [Property] instances backing this delegate. */
|
|
||||||
abstract val guiProperties: List<Property>
|
|
||||||
|
|
||||||
/** Re-read the property value from the [Configuration]. */
|
|
||||||
open fun read() {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ObsoleteConfigProperty : ConfigPropertyBase() {
|
class DelegatingIntValue(
|
||||||
override fun attach(target: Configuration, langPrefix: String, categoryName: String, propertyName: String) {
|
val minValue: Int = 0,
|
||||||
target.getCategory(categoryName)?.remove(propertyName)
|
val maxValue: Int = 1,
|
||||||
}
|
val defaultValue: Int = 0
|
||||||
override val guiProperties = emptyList<Property>()
|
) : ConfigDelegate<Int>() {
|
||||||
override val hasChanged: Boolean get() = false
|
override fun getConfigValue(name: String, builder: ForgeConfigSpec.Builder) = builder.defineInRange(name, defaultValue, minValue, maxValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Delegate for a property backed by a single [Property] instance. */
|
class DelegatingDoubleValue(
|
||||||
abstract class ConfigPropertyDelegate<T>() : ConfigPropertyBase() {
|
val minValue: Double = 0.0,
|
||||||
/** Cached value of the property. */
|
val maxValue: Double = 1.0,
|
||||||
var cached: T? = null
|
val defaultValue: Double = 0.0
|
||||||
/** The [Property] backing this delegate. */
|
) : ConfigDelegate<Double>() {
|
||||||
var property: Property? = null
|
override fun getConfigValue(name: String, builder: ForgeConfigSpec.Builder) = builder.defineInRange(name, defaultValue, minValue, maxValue)
|
||||||
|
|
||||||
override val guiProperties: List<Property> get() = listOf(property!!)
|
|
||||||
override val hasChanged: Boolean get() = property?.hasChanged() ?: false
|
|
||||||
|
|
||||||
/** Chained setter for the language key. */
|
|
||||||
fun lang(lang: String) = apply { this.lang = lang }
|
|
||||||
|
|
||||||
/** Read the backing [Property] instance. */
|
|
||||||
abstract fun Property.read(): T
|
|
||||||
|
|
||||||
/** Write the backing [Property] instance. */
|
|
||||||
abstract fun Property.write(value: T)
|
|
||||||
|
|
||||||
/** Get the backing [Property] instance. */
|
|
||||||
abstract fun resolve(target: Configuration, category: String, name: String): Property
|
|
||||||
|
|
||||||
/** Kotlin deleagation implementation. */
|
|
||||||
operator fun getValue(thisRef: Any, delegator: KProperty<*>): T {
|
|
||||||
if (cached != null) return cached!!
|
|
||||||
cached = property!!.read()
|
|
||||||
return cached!!
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Kotlin deleagation implementation. */
|
|
||||||
operator fun setValue(thisRef: Any, delegator: KProperty<*>, value: T) {
|
|
||||||
cached = value
|
|
||||||
property!!.write(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun read() { cached = null }
|
|
||||||
|
|
||||||
override fun attach(target: Configuration, langPrefix: String, categoryName: String, propertyName: String) {
|
|
||||||
cached = null
|
|
||||||
property = resolve(target, categoryName, propertyName)
|
|
||||||
property!!.setLanguageKey("$langPrefix.$lang")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** [Double]-typed property delegate. */
|
|
||||||
class ConfigPropertyDouble(val min: Double, val max: Double, val default: Double) :
|
|
||||||
ConfigPropertyDelegate<Double>() {
|
|
||||||
override fun resolve(target: Configuration, category: String, name: String) =
|
|
||||||
target.get(category, name, default, null).apply { setMinValue(min); setMaxValue(max) }
|
|
||||||
override fun Property.read() = property!!.double
|
|
||||||
override fun Property.write(value: Double) = property!!.set(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** [Float]-typed property delegate. */
|
|
||||||
class ConfigPropertyFloat(val min: Double, val max: Double, val default: Double) :
|
|
||||||
ConfigPropertyDelegate<Float>() {
|
|
||||||
override fun resolve(target: Configuration, category: String, name: String) =
|
|
||||||
target.get(category, name, default, null).apply { setMinValue(min); setMaxValue(max) }
|
|
||||||
override fun Property.read() = property!!.double.toFloat()
|
|
||||||
override fun Property.write(value: Float) = property!!.set(value.toDouble())
|
|
||||||
}
|
|
||||||
|
|
||||||
/** [Int]-typed property delegate. */
|
|
||||||
class ConfigPropertyInt(val min: Int, val max: Int, val default: Int) :
|
|
||||||
ConfigPropertyDelegate<Int>() {
|
|
||||||
override fun resolve(target: Configuration, category: String, name: String) =
|
|
||||||
target.get(category, name, default, null).apply { setMinValue(min); setMaxValue(max) }
|
|
||||||
override fun Property.read() = property!!.int
|
|
||||||
override fun Property.write(value: Int) = property!!.set(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** [Boolean]-typed property delegate. */
|
|
||||||
class ConfigPropertyBoolean(val default: Boolean) :
|
|
||||||
ConfigPropertyDelegate<Boolean>() {
|
|
||||||
override fun resolve(target: Configuration, category: String, name: String) =
|
|
||||||
target.get(category, name, default, null)
|
|
||||||
override fun Property.read() = property!!.boolean
|
|
||||||
override fun Property.write(value: Boolean) = property!!.set(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** [Int] array typed property delegate. */
|
|
||||||
class ConfigPropertyIntList(val defaults: ()->Array<Int>) :
|
|
||||||
ConfigPropertyDelegate<Array<Int>>() {
|
|
||||||
override fun resolve(target: Configuration, category: String, name: String) =
|
|
||||||
target.get(category, name, defaults().toIntArray(), null)
|
|
||||||
override fun Property.read() = property!!.intList.toTypedArray()
|
|
||||||
override fun Property.write(value: Array<Int>) = property!!.set(value.toIntArray())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================
|
// ============================
|
||||||
// Delegate factory methods
|
// Delegate factory methods
|
||||||
// ============================
|
// ============================
|
||||||
fun double(min: Double = 0.0, max: Double = 1.0, default: Double) = ConfigPropertyDouble(min, max, default)
|
fun double(min: Double = 0.0, max: Double = 1.0, default: Double) = DelegatingDoubleValue(min, max, default)
|
||||||
fun float(min: Double = 0.0, max: Double = 1.0, default: Double) = ConfigPropertyFloat(min, max, default)
|
fun int(min: Int = 0, max: Int, default: Int) = DelegatingIntValue(min, max, default)
|
||||||
fun int(min: Int = 0, max: Int, default: Int) = ConfigPropertyInt(min, max, default)
|
fun boolean(default: Boolean) = DelegatingBooleanValue(default)
|
||||||
fun intList(defaults: ()->Array<Int>) = ConfigPropertyIntList(defaults)
|
|
||||||
fun boolean(default: Boolean) = ConfigPropertyBoolean(default)
|
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
package mods.octarinecore.common.config
|
package mods.octarinecore.common.config
|
||||||
|
|
||||||
|
import mods.octarinecore.client.resource.getLines
|
||||||
|
import mods.octarinecore.client.resource.resourceManager
|
||||||
import mods.octarinecore.metaprog.getJavaClass
|
import mods.octarinecore.metaprog.getJavaClass
|
||||||
import net.minecraft.block.Block
|
import net.minecraft.block.Block
|
||||||
import net.minecraft.util.ResourceLocation
|
import net.minecraft.util.ResourceLocation
|
||||||
|
import org.apache.logging.log4j.Logger
|
||||||
|
|
||||||
interface IBlockMatcher {
|
interface IBlockMatcher {
|
||||||
fun matchesClass(block: Block): Boolean
|
fun matchesClass(block: Block): Boolean
|
||||||
@@ -19,8 +22,11 @@ class SimpleBlockMatcher(vararg val classes: Class<*>) : IBlockMatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConfigurableBlockMatcher(domain: String, path: String) : IBlockMatcher, BlackWhiteListConfigOption<Class<*>>(domain, path) {
|
class ConfigurableBlockMatcher(val logger: Logger, val location: ResourceLocation) : IBlockMatcher {
|
||||||
override fun convertValue(line: String) = getJavaClass(line)
|
|
||||||
|
val blackList = mutableListOf<Class<*>>()
|
||||||
|
val whiteList = mutableListOf<Class<*>>()
|
||||||
|
// override fun convertValue(line: String) = getJavaClass(line)
|
||||||
|
|
||||||
override fun matchesClass(block: Block): Boolean {
|
override fun matchesClass(block: Block): Boolean {
|
||||||
val blockClass = block.javaClass
|
val blockClass = block.javaClass
|
||||||
@@ -35,16 +41,34 @@ class ConfigurableBlockMatcher(domain: String, path: String) : IBlockMatcher, Bl
|
|||||||
whiteList.forEach { if (it.isAssignableFrom(blockClass)) return it }
|
whiteList.forEach { if (it.isAssignableFrom(blockClass)) return it }
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun readDefaults() {
|
||||||
|
blackList.clear()
|
||||||
|
whiteList.clear()
|
||||||
|
resourceManager.getAllResources(location).forEach { resource ->
|
||||||
|
logger.debug("Reading resource $location from pack ${resource.packName}")
|
||||||
|
resource.getLines().map{ it.trim() }.filter { !it.startsWith("//") && it.isNotEmpty() }.forEach { line ->
|
||||||
|
if (line.startsWith("-")) getJavaClass(line.substring(1))?.let { blackList.add(it) }
|
||||||
|
else getJavaClass(line)?.let { whiteList.add(it) }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class ModelTextureList(val modelLocation: ResourceLocation, val textureNames: List<String>) {
|
data class ModelTextureList(val modelLocation: ResourceLocation, val textureNames: List<String>) {
|
||||||
constructor(vararg args: String) : this(ResourceLocation(args[0]), listOf(*args).drop(1))
|
constructor(vararg args: String) : this(ResourceLocation(args[0]), listOf(*args).drop(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
class ModelTextureListConfigOption(domain: String, path: String, val minTextures: Int) : StringListConfigOption<ModelTextureList>(domain, path) {
|
class ModelTextureListConfiguration(val logger: Logger, val location: ResourceLocation) {
|
||||||
override fun convertValue(line: String): ModelTextureList? {
|
val modelList = mutableListOf<ModelTextureList>()
|
||||||
val elements = line.split(",")
|
fun readDefaults() {
|
||||||
if (elements.size < minTextures + 1) return null
|
resourceManager.getAllResources(location).forEach { resource ->
|
||||||
return ModelTextureList(ResourceLocation(elements.first()), elements.drop(1))
|
logger.debug("Reading resource $location from pack ${resource.packName}")
|
||||||
|
resource.getLines().map{ it.trim() }.filter { !it.startsWith("//") && it.isNotEmpty() }.forEach { line ->
|
||||||
|
val elements = line.split(",")
|
||||||
|
modelList.add(ModelTextureList(ResourceLocation(elements.first()), elements.drop(1)))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
package mods.octarinecore.common.config
|
|
||||||
|
|
||||||
import mods.octarinecore.client.gui.NonVerboseArrayEntry
|
|
||||||
import mods.octarinecore.client.resource.get
|
|
||||||
import mods.octarinecore.client.resource.getLines
|
|
||||||
import mods.octarinecore.client.resource.resourceManager
|
|
||||||
import net.minecraftforge.common.config.Configuration
|
|
||||||
import net.minecraftforge.common.config.Property
|
|
||||||
|
|
||||||
abstract class StringListConfigOption<VALUE>(val domain: String, val path: String) : ConfigPropertyBase() {
|
|
||||||
|
|
||||||
val list = mutableListOf<VALUE>()
|
|
||||||
lateinit var listProperty: Property
|
|
||||||
|
|
||||||
override val hasChanged: Boolean get() = listProperty.hasChanged() ?: false
|
|
||||||
override val guiProperties: List<Property> get() = listOf(listProperty)
|
|
||||||
|
|
||||||
override fun attach(target: Configuration, langPrefix: String, categoryName: String, propertyName: String) {
|
|
||||||
lang = null
|
|
||||||
val defaults = readDefaults(domain, path)
|
|
||||||
listProperty = target.get(categoryName, "${propertyName}", defaults)
|
|
||||||
listProperty.configEntryClass = NonVerboseArrayEntry::class.java
|
|
||||||
listProperty.languageKey = "$langPrefix.$categoryName.${listProperty.name}"
|
|
||||||
read()
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract fun convertValue(line: String): VALUE?
|
|
||||||
|
|
||||||
override fun read() {
|
|
||||||
list.clear()
|
|
||||||
listProperty.stringList.forEach { line ->
|
|
||||||
val value = convertValue(line)
|
|
||||||
if (value != null) list.add(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun readDefaults(domain: String, path: String): Array<String> {
|
|
||||||
val list = arrayListOf<String>()
|
|
||||||
val defaults = resourceManager[domain, path]?.getLines()
|
|
||||||
defaults?.map { it.trim() }?.filter { !it.startsWith("//") && it.isNotEmpty() }?.forEach { list.add(it) }
|
|
||||||
return list.toTypedArray()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -43,6 +43,14 @@ fun Any.reflectFieldsOfType(vararg types: Class<*>) = this.javaClass.declaredFie
|
|||||||
.map { field -> field.name to field.let { it.isAccessible = true; it.get(this) } }
|
.map { field -> field.name to field.let { it.isAccessible = true; it.get(this) } }
|
||||||
.filterNotNull()
|
.filterNotNull()
|
||||||
|
|
||||||
|
fun <T> Any.reflectFields(type: Class<T>) = this.javaClass.declaredFields
|
||||||
|
.filter { field -> type.isAssignableFrom(field.type) }
|
||||||
|
.map { field -> field.name to field.let { it.isAccessible = true; it.get(this) as T } }
|
||||||
|
|
||||||
|
fun <T> Any.reflectDelegates(type: Class<T>) = this.javaClass.declaredFields
|
||||||
|
.filter { field -> type.isAssignableFrom(field.type) && field.name.contains("$") }
|
||||||
|
.map { field -> field.name.split("$")[0] to field.let { it.isAccessible = true; it.get(this) } as T }
|
||||||
|
|
||||||
enum class Namespace { MCP, SRG }
|
enum class Namespace { MCP, SRG }
|
||||||
|
|
||||||
abstract class Resolvable<T> {
|
abstract class Resolvable<T> {
|
||||||
|
|||||||
@@ -1,212 +0,0 @@
|
|||||||
package mods.octarinecore.metaprog
|
|
||||||
|
|
||||||
import mods.octarinecore.metaprog.Namespace.*
|
|
||||||
import net.minecraft.launchwrapper.IClassTransformer
|
|
||||||
import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin
|
|
||||||
import org.apache.logging.log4j.LogManager
|
|
||||||
import org.objectweb.asm.ClassReader
|
|
||||||
import org.objectweb.asm.ClassWriter
|
|
||||||
import org.objectweb.asm.Opcodes
|
|
||||||
import org.objectweb.asm.tree.*
|
|
||||||
import java.io.File
|
|
||||||
import java.io.FileOutputStream
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for convenient bytecode transformers.
|
|
||||||
*/
|
|
||||||
open class Transformer : IClassTransformer {
|
|
||||||
|
|
||||||
val log = LogManager.getLogger(this)
|
|
||||||
|
|
||||||
/** The list of transformers and targets. */
|
|
||||||
var methodTransformers: MutableList<Pair<MethodRef, MethodTransformContext.()->Unit>> = arrayListOf()
|
|
||||||
|
|
||||||
/** Add a transformation to perform. Call this during instance initialization.
|
|
||||||
*
|
|
||||||
* @param[method] the target method of the transformation
|
|
||||||
* @param[trans] method transformation lambda
|
|
||||||
*/
|
|
||||||
fun transformMethod(method: MethodRef, trans: MethodTransformContext.()->Unit) = methodTransformers.add(method to trans)
|
|
||||||
|
|
||||||
override fun transform(name: String?, transformedName: String?, classData: ByteArray?): ByteArray? {
|
|
||||||
if (classData == null) return null
|
|
||||||
val classNode = ClassNode().apply { val reader = ClassReader(classData); reader.accept(this, 0) }
|
|
||||||
var workDone = false
|
|
||||||
var writerFlags = 0
|
|
||||||
|
|
||||||
synchronized(this) {
|
|
||||||
methodTransformers.forEach { (targetMethod, transform) ->
|
|
||||||
if (transformedName != targetMethod.parentClass.name) return@forEach
|
|
||||||
|
|
||||||
for (method in classNode.methods) {
|
|
||||||
val namespace = Namespace.values().find {
|
|
||||||
method.name == targetMethod.name(it) && method.desc == targetMethod.asmDescriptor(it)
|
|
||||||
} ?: continue
|
|
||||||
when (namespace) {
|
|
||||||
MCP -> log.info("Found method ${targetMethod.parentClass.name}.${targetMethod.name(MCP)} ${targetMethod.asmDescriptor(MCP)}")
|
|
||||||
SRG -> log.info("Found method ${targetMethod.parentClass.name}.${targetMethod.name(namespace)} ${targetMethod.asmDescriptor(namespace)} (matching ${targetMethod.name(MCP)})")
|
|
||||||
}
|
|
||||||
|
|
||||||
// write input bytecode for debugging - definitely not in production...
|
|
||||||
//File("BF_debug").mkdir()
|
|
||||||
//FileOutputStream(File("BF_debug/$transformedName.class")).apply {
|
|
||||||
// write(classData)
|
|
||||||
// close()
|
|
||||||
//}
|
|
||||||
|
|
||||||
// transform
|
|
||||||
writerFlags = MethodTransformContext(method, namespace, writerFlags).apply(transform).writerFlags
|
|
||||||
workDone = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return if (!workDone) classData else ClassWriter(writerFlags).apply { classNode.accept(this) }.toByteArray()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allows builder-style declarative definition of transformations. Transformation lambdas are extension
|
|
||||||
* methods on this class.
|
|
||||||
*
|
|
||||||
* @param[method] the [MethodNode] currently being transformed
|
|
||||||
* @param[environment] the type of environment we are in
|
|
||||||
*/
|
|
||||||
class MethodTransformContext(val method: MethodNode, val environment: Namespace, var writerFlags: Int) {
|
|
||||||
|
|
||||||
fun applyWriterFlags(vararg flagValue: Int) { flagValue.forEach { writerFlags = writerFlags or it } }
|
|
||||||
|
|
||||||
fun makePublic() {
|
|
||||||
method.access = (method.access or Opcodes.ACC_PUBLIC) and (Opcodes.ACC_PRIVATE or Opcodes.ACC_PROTECTED).inv()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the first instruction that matches a predicate.
|
|
||||||
*
|
|
||||||
* @param[start] the instruction node to start iterating from
|
|
||||||
* @param[predicate] the predicate to check
|
|
||||||
*/
|
|
||||||
fun find(start: AbstractInsnNode, predicate: (AbstractInsnNode) -> Boolean): AbstractInsnNode? {
|
|
||||||
var current: AbstractInsnNode? = start
|
|
||||||
while (current != null && !predicate(current)) current = current.next
|
|
||||||
return current
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Find the first instruction in the current [MethodNode] that matches a predicate. */
|
|
||||||
fun find(predicate: (AbstractInsnNode)->Boolean): AbstractInsnNode? = find(method.instructions.first, predicate)
|
|
||||||
|
|
||||||
/** Find the first instruction in the current [MethodNode] with the given opcode. */
|
|
||||||
fun find(opcode: Int) = find { it.opcode == opcode }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Insert new instructions after this one.
|
|
||||||
*
|
|
||||||
* @param[init] builder-style lambda to assemble instruction list
|
|
||||||
*/
|
|
||||||
fun AbstractInsnNode.insertAfter(init: InstructionList.()->Unit) = InstructionList(environment).apply{
|
|
||||||
this.init(); list.reversed().forEach { method.instructions.insert(this@insertAfter, it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Insert new instructions before this one.
|
|
||||||
*
|
|
||||||
* @param[init] builder-style lambda to assemble instruction list
|
|
||||||
*/
|
|
||||||
fun AbstractInsnNode.insertBefore(init: InstructionList.()->Unit) = InstructionList(environment).apply{
|
|
||||||
val insertBeforeNode = this@insertBefore //.let { if (it.previous is FrameNode) it.previous else it }
|
|
||||||
this.init(); list.forEach { method.instructions.insertBefore(insertBeforeNode, it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun AbstractInsnNode.replace(init: InstructionList.()->Unit) = InstructionList(environment).apply {
|
|
||||||
insertAfter(init)
|
|
||||||
method.instructions.remove(this@replace)
|
|
||||||
}
|
|
||||||
/** Remove all isntructiuons between the given two (inclusive). */
|
|
||||||
fun Pair<AbstractInsnNode, AbstractInsnNode>.remove() {
|
|
||||||
var current: AbstractInsnNode? = first
|
|
||||||
while (current != null && current != second) {
|
|
||||||
val next = current.next
|
|
||||||
method.instructions.remove(current)
|
|
||||||
current = next
|
|
||||||
}
|
|
||||||
if (current != null) method.instructions.remove(current)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replace all isntructiuons between the given two (inclusive) with the specified instruction list.
|
|
||||||
*
|
|
||||||
* @param[init] builder-style lambda to assemble instruction list
|
|
||||||
*/
|
|
||||||
fun Pair<AbstractInsnNode, AbstractInsnNode>.replace(init: InstructionList.()->Unit) {
|
|
||||||
val beforeInsn = first.previous
|
|
||||||
remove()
|
|
||||||
beforeInsn.insertAfter(init)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Matches variable instructions.
|
|
||||||
*
|
|
||||||
* @param[opcode] instruction opcode
|
|
||||||
* @param[idx] variable the opcode references
|
|
||||||
*/
|
|
||||||
fun varinsn(opcode: Int, idx: Int): (AbstractInsnNode)->Boolean = { insn ->
|
|
||||||
insn.opcode == opcode && insn is VarInsnNode && insn.`var` == idx
|
|
||||||
}
|
|
||||||
|
|
||||||
fun invokeName(name: String): (AbstractInsnNode)->Boolean = { insn ->
|
|
||||||
(insn as? MethodInsnNode)?.name == name
|
|
||||||
}
|
|
||||||
|
|
||||||
fun invokeRef(ref: MethodRef): (AbstractInsnNode)->Boolean = { insn ->
|
|
||||||
(insn as? MethodInsnNode)?.let {
|
|
||||||
it.name == ref.name(environment) && it.owner == ref.parentClass.name.replace(".", "/")
|
|
||||||
} ?: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allows builder-style declarative definition of instruction lists.
|
|
||||||
*
|
|
||||||
* @param[environment] the type of environment we are in
|
|
||||||
*/
|
|
||||||
class InstructionList(val environment: Namespace) {
|
|
||||||
|
|
||||||
fun insn(opcode: Int) = list.add(InsnNode(opcode))
|
|
||||||
|
|
||||||
/** The instruction list being assembled. */
|
|
||||||
val list: MutableList<AbstractInsnNode> = arrayListOf()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a variable instruction.
|
|
||||||
*
|
|
||||||
* @param[opcode] instruction opcode
|
|
||||||
* @param[idx] variable the opcode references
|
|
||||||
*/
|
|
||||||
fun varinsn(opcode: Int, idx: Int) = list.add(VarInsnNode(opcode, idx))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an INVOKESTATIC instruction.
|
|
||||||
*
|
|
||||||
* @param[target] the target method of the instruction
|
|
||||||
* @param[isInterface] true if the target method is defined by an interface
|
|
||||||
*/
|
|
||||||
fun invokeStatic(target: MethodRef, isInterface: Boolean = false) = list.add(MethodInsnNode(
|
|
||||||
Opcodes.INVOKESTATIC,
|
|
||||||
target.parentClass.name.replace(".", "/"),
|
|
||||||
target.name(environment),
|
|
||||||
target.asmDescriptor(environment),
|
|
||||||
isInterface
|
|
||||||
))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a GETFIELD instruction.
|
|
||||||
*
|
|
||||||
* @param[target] the target field of the instruction
|
|
||||||
*/
|
|
||||||
fun getField(target: FieldRef) = list.add(FieldInsnNode(
|
|
||||||
Opcodes.GETFIELD,
|
|
||||||
target.parentClass.name.replace(".", "/"),
|
|
||||||
target.name(environment),
|
|
||||||
target.asmDescriptor(environment)
|
|
||||||
))
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
public net.minecraft.client.renderer.BlockModelRenderer$AmbientOcclusionFace
|
|
||||||
public net.minecraft.client.renderer.BlockModelRenderer$AmbientOcclusionFace field_178206_b # vertexColorMultiplier
|
|
||||||
public net.minecraft.client.renderer.BlockModelRenderer$AmbientOcclusionFace field_178207_c # vertexBrightness
|
|
||||||
|
|
||||||
public net.minecraft.client.renderer.block.model.ModelBakery field_177610_k # blockModelShapes
|
|
||||||
|
|
||||||
public net.minecraft.client.renderer.block.statemap.BlockStateMapper field_178450_a # blockStateMap
|
|
||||||
|
|
||||||
public net.minecraft.client.renderer.BufferBuilder field_178999_b # rawIntBuffer
|
|
||||||
public net.minecraft.client.renderer.BufferBuilder func_181670_b(I)V # growBuffer
|
|
||||||
public net.minecraft.client.renderer.BufferBuilder func_181664_j()I # getBufferSize
|
|
||||||
|
|
||||||
public net.minecraft.client.renderer.BlockModelRenderer field_187499_a # blockColors
|
|
||||||
|
|
||||||
public net.minecraft.world.ChunkCache field_72815_e # world
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
Manifest-Version: 1.0
|
||||||
|
Specification-Title: BetterFoliage
|
||||||
|
Implementation-Title: BetterFoliage
|
||||||
|
Specification-Vendor: octarine-noise
|
||||||
|
Implementation-Vendor: octarine-noise
|
||||||
|
MixinConnector: mods.betterfoliage.MixinConnector
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
public net.minecraft.client.renderer.BlockModelRenderer$AmbientOcclusionFace
|
||||||
|
public net.minecraft.client.renderer.BlockModelRenderer$AmbientOcclusionFace <init>
|
||||||
|
public net.minecraft.client.renderer.BlockModelRenderer$AmbientOcclusionFace field_178206_b #vertexColorMultiplier
|
||||||
|
public net.minecraft.client.renderer.BlockModelRenderer$AmbientOcclusionFace field_178207_c #vertexBrightness
|
||||||
|
|
||||||
|
public net.minecraft.block.BlockState$Cache
|
||||||
|
|
||||||
|
public net.minecraft.client.renderer.chunk.ChunkRenderCache field_212408_i #world
|
||||||
|
|
||||||
|
#public net.minecraft.client.renderer.block.model.ModelBakery field_177610_k # blockModelShapes
|
||||||
|
|
||||||
|
#public net.minecraft.client.renderer.block.statemap.BlockStateMapper field_178450_a # blockStateMap
|
||||||
|
|
||||||
|
#public net.minecraft.client.renderer.BufferBuilder field_178999_b # rawIntBuffer
|
||||||
|
#public net.minecraft.client.renderer.BufferBuilder func_181670_b(I)V # growBuffer
|
||||||
|
#public net.minecraft.client.renderer.BufferBuilder func_181664_j()I # getBufferSize
|
||||||
|
|
||||||
|
#public net.minecraft.client.renderer.BlockModelRenderer field_187499_a # blockColors
|
||||||
|
|
||||||
|
#public net.minecraft.world.ChunkCache field_72815_e # world
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
modLoader="kotlinfml"
|
||||||
|
loaderVersion="[1.4,)"
|
||||||
|
issueTrackerURL="https://github.com/octarine-noise/BetterFoliage/issues"
|
||||||
|
|
||||||
|
[[mods]]
|
||||||
|
modId="betterfoliage"
|
||||||
|
version="${version}"
|
||||||
|
displayName="Better Foliage"
|
||||||
|
authors="octarine-noise"
|
||||||
|
description='''
|
||||||
|
Leafier leaves and grassier grass.
|
||||||
|
'''
|
||||||
|
displayURL="https://www.curseforge.com/minecraft/mc-mods/better-foliage"
|
||||||
|
side="CLIENT"
|
||||||
@@ -1,5 +1,2 @@
|
|||||||
// Vanilla
|
// Vanilla
|
||||||
net.minecraft.block.BlockCactus
|
net.minecraft.block.CactusBlock
|
||||||
|
|
||||||
// TerraFirmaCraft
|
|
||||||
com.bioxx.tfc.Blocks.Vanilla.BlockCustomCactus
|
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
// Vanilla
|
// Vanilla
|
||||||
net.minecraft.block.BlockTallGrass
|
net.minecraft.block.TallGrassBlock
|
||||||
net.minecraft.block.BlockCrops
|
net.minecraft.block.CropsBlock
|
||||||
-net.minecraft.block.BlockReed
|
-net.minecraft.block.ReedBlock
|
||||||
-net.minecraft.block.BlockDoublePlant
|
-net.minecraft.block.DoublePlantBlock
|
||||||
-net.minecraft.block.BlockCarrot
|
-net.minecraft.block.CarrotBlock
|
||||||
-net.minecraft.block.BlockPotato
|
-net.minecraft.block.PotatoBlock
|
||||||
|
|
||||||
// Biomes O'Plenty
|
// Biomes O'Plenty
|
||||||
biomesoplenty.common.block.BlockBOPFlower
|
biomesoplenty.common.block.BlockBOPFlower
|
||||||
@@ -13,24 +13,3 @@ biomesoplenty.common.block.BlockBOPPlant
|
|||||||
|
|
||||||
// Tinkers' Construct
|
// Tinkers' Construct
|
||||||
tconstruct.blocks.slime.SlimeTallGrass
|
tconstruct.blocks.slime.SlimeTallGrass
|
||||||
|
|
||||||
// Plant Mega Pack
|
|
||||||
plantmegapack.block.PMPBlockBerrybush
|
|
||||||
plantmegapack.block.PMPBlockCrops
|
|
||||||
plantmegapack.block.PMPBlockDesert
|
|
||||||
plantmegapack.block.PMPBlockFern
|
|
||||||
plantmegapack.block.PMPBlockFlowerMulti
|
|
||||||
plantmegapack.block.PMPBlockFlowerSingle
|
|
||||||
plantmegapack.block.PMPBlockForest
|
|
||||||
plantmegapack.block.PMPBlockGrass
|
|
||||||
plantmegapack.block.PMPBlockJungle
|
|
||||||
plantmegapack.block.PMPBlockMountain
|
|
||||||
plantmegapack.block.PMPBlockSavanna
|
|
||||||
plantmegapack.block.PMPBlockShrub
|
|
||||||
plantmegapack.block.PMPBlockWetlands
|
|
||||||
|
|
||||||
// Pam's HarvestCraft
|
|
||||||
com.pam.harvestcraft.BlockPamCrop
|
|
||||||
com.pam.harvestcraft.BlockPamDesertGarden
|
|
||||||
com.pam.harvestcraft.BlockPamNormalGarden
|
|
||||||
com.pam.harvestcraft.BlockPamWaterGarden
|
|
||||||
@@ -1,18 +1,2 @@
|
|||||||
// Vanilla
|
// Vanilla
|
||||||
net.minecraft.block.BlockDirt
|
net.minecraft.block.DirtBlock
|
||||||
|
|
||||||
// Biomes O'Plenty
|
|
||||||
biomesoplenty.common.block.BlockBOPDirt
|
|
||||||
|
|
||||||
// Enhanced Biomes
|
|
||||||
enhancedbiomes.blocks.BlockSoilEB
|
|
||||||
|
|
||||||
// TerraFirmaCraft
|
|
||||||
com.bioxx.tfc.Blocks.Terrain.BlockDirt
|
|
||||||
|
|
||||||
// Aether
|
|
||||||
net.aetherteam.aether.blocks.natural.BlockAetherDirt
|
|
||||||
com.gildedgames.aether.common.blocks.natural.BlockAetherDirt
|
|
||||||
|
|
||||||
// Tinker's Construct
|
|
||||||
slimeknights.tconstruct.world.block.BlockSlimeDirt
|
|
||||||
@@ -1,18 +1,2 @@
|
|||||||
// Vanilla
|
// Vanilla
|
||||||
net.minecraft.block.BlockGrass
|
net.minecraft.block.GrassBlock
|
||||||
|
|
||||||
// Biomes O'Plenty
|
|
||||||
biomesoplenty.common.block.BlockBOPGrass
|
|
||||||
|
|
||||||
// Tinker's Construct
|
|
||||||
tconstruct.blocks.slime.SlimeGrass
|
|
||||||
|
|
||||||
// Enhanced Biomes
|
|
||||||
enhancedbiomes.blocks.BlockGrassEB
|
|
||||||
|
|
||||||
// TerraFirmaCraft
|
|
||||||
com.bioxx.tfc.Blocks.Terrain.BlockGrass
|
|
||||||
|
|
||||||
// AbyssalCraft
|
|
||||||
com.shinoow.abyssalcraft.common.blocks.BlockDreadGrass
|
|
||||||
com.shinoow.abyssalcraft.common.blocks.BlockDarklandsgrass
|
|
||||||
@@ -1,6 +1,3 @@
|
|||||||
// Vanilla
|
// Vanilla
|
||||||
block/grass,top
|
block/grass_block,top
|
||||||
block/cube_bottom_top,top
|
block/cube_bottom_top,top
|
||||||
|
|
||||||
// Lithos Core
|
|
||||||
block/soil/grass_master,top
|
|
||||||
@@ -1,8 +1,2 @@
|
|||||||
// Vanilla
|
// Vanilla
|
||||||
net.minecraft.block.BlockLeaves
|
net.minecraft.block.LeavesBlock
|
||||||
|
|
||||||
// Biomes O' Plenty
|
|
||||||
biomesoplenty.common.block.BlockBOPLeaves
|
|
||||||
|
|
||||||
// Aether II
|
|
||||||
com.gildedgames.aether.common.blocks.natural.BlockAetherLeaves
|
|
||||||
@@ -1,35 +1,2 @@
|
|||||||
// Vanilla
|
// Vanilla
|
||||||
net.minecraft.block.BlockLog
|
net.minecraft.block.LogBlock
|
||||||
|
|
||||||
// Biomes O'Plenty
|
|
||||||
biomesoplenty.common.block.BlockBOPLog
|
|
||||||
|
|
||||||
// Natura
|
|
||||||
com.progwml6.natura.common.block.BlockEnumLog
|
|
||||||
|
|
||||||
// Thaumcraft
|
|
||||||
thaumcraft.common.blocks.world.plants.BlockLogsTC
|
|
||||||
|
|
||||||
// Forestry
|
|
||||||
forestry.arboriculture.gadgets.BlockLog
|
|
||||||
|
|
||||||
// Extra Biomes XL
|
|
||||||
-extrabiomes.blocks.BlockMiniLog
|
|
||||||
|
|
||||||
// TerraFirmaCraft
|
|
||||||
com.bioxx.tfc.Blocks.Flora.BlockLogVert
|
|
||||||
com.bioxx.tfc.Blocks.Flora.BlockLogNatural
|
|
||||||
|
|
||||||
// The Agricultural Revolution a.k.a. Cooking Plus
|
|
||||||
CookingPlus.blocks.CookingPlusPalmLog
|
|
||||||
CookingPlus.blocks.CookingPlusTangleLog
|
|
||||||
CookingPlus.blocks.CookingPlusTangleHeart
|
|
||||||
|
|
||||||
// IC2
|
|
||||||
ic2.core.block.BlockRubWood
|
|
||||||
|
|
||||||
// TechReborn
|
|
||||||
techreborn.blocks.BlockRubberLog
|
|
||||||
|
|
||||||
//Better With Mods
|
|
||||||
betterwithmods.blocks.BlockStump
|
|
||||||
@@ -1,5 +1,2 @@
|
|||||||
// Vanilla
|
// Vanilla
|
||||||
net.minecraft.block.BlockMycelium
|
net.minecraft.block.MyceliumBlock
|
||||||
|
|
||||||
// NetherEx
|
|
||||||
nex.block.BlockMycelium
|
|
||||||
@@ -1,5 +1,2 @@
|
|||||||
// Vanilla
|
// Vanilla
|
||||||
net.minecraft.block.BlockNetherrack
|
net.minecraft.block.NetherrackBlock
|
||||||
|
|
||||||
// NetherEx
|
|
||||||
nex.block.BlockNetherrack
|
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loaded 100 of 164 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user