port to MC 1.8

This commit is contained in:
octarine-noise
2016-01-09 12:55:52 +01:00
parent f44043bb0b
commit 8460103030
87 changed files with 1144 additions and 940 deletions

View File

@@ -1,12 +1,12 @@
apply plugin: "forge"
apply plugin: "kotlin"
apply plugin: 'kotlin'
group = 'com.github.octarine-noise'
version = "2.0"
archivesBaseName = rootProject.name + '-MC1.7.10'
archivesBaseName = rootProject.name + '-MC1.8'
buildscript {
ext.kotlin_version = '1.0.0-beta-4583'
ext.kotlin_version = '1.0.0-beta-4584'
repositories {
mavenCentral()
maven {
@@ -29,10 +29,12 @@ configurations {
}
dependencies {
shade "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}
minecraft {
version = '1.7.10-10.13.4.1448-1.7.10'
srgExtra "PK: kotlin mods/betterfoliage/kotlin"
version = '1.8-11.14.3.1502'
mappings = 'stable_18'
srgExtra "PK: kotlin mods/octarinecore/kotlin"
}
processResources {
@@ -55,6 +57,7 @@ jar {
manifest {
attributes "FMLCorePlugin": "mods.betterfoliage.loader.BetterFoliageLoader"
attributes "FMLCorePluginContainsFMLMod": "mods.betterfoliage.BetterFoliageMod"
attributes "FMLAT": "BetterFoliage_at.cfg"
}
configurations.shade.each { dep ->
from(project.zipTree(dep)){

View File

@@ -0,0 +1,25 @@
package net.minecraft.client;
import net.minecraft.block.Block;
import net.minecraft.client.renderer.BlockModelRenderer;
import net.minecraft.util.EnumFacing;
import java.util.BitSet;
/**
* FFS why isn't this public static...
*/
public class BFBlockModelRenderer extends BlockModelRenderer {
public class BFAmbientOcclusionFace extends BlockModelRenderer.AmbientOcclusionFace {}
private static BFBlockModelRenderer INSTANCE = new BFBlockModelRenderer();
public static BFAmbientOcclusionFace getVanillaAoObject() {
return INSTANCE.new BFAmbientOcclusionFace();
}
public static void fillQuadBounds2(Block blockIn, int[] vertexData, EnumFacing facingIn, float[] quadBounds, BitSet boundsFlags) {
INSTANCE.fillQuadBounds(blockIn, vertexData, facingIn, quadBounds, boundsFlags);
}
}

View File

@@ -1,19 +1,15 @@
package mods.betterfoliage
import cpw.mods.fml.common.FMLCommonHandler
import cpw.mods.fml.common.Mod
import cpw.mods.fml.common.event.FMLPostInitializationEvent
import cpw.mods.fml.common.event.FMLPreInitializationEvent
import cpw.mods.fml.common.network.NetworkCheckHandler
import cpw.mods.fml.relauncher.Side
import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.integration.ShadersModIntegration
import mods.betterfoliage.client.integration.TFCIntegration
import mods.betterfoliage.loader.Refs
import mods.octarinecore.metaprog.ClassRef
import net.minecraftforge.common.config.Configuration
import org.apache.logging.log4j.Level.*
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.INFO
import org.apache.logging.log4j.Logger
@Mod(
@@ -28,7 +24,7 @@ object BetterFoliageMod {
const val MOD_NAME = "Better Foliage"
const val DOMAIN = "betterfoliage"
const val LEGACY_DOMAIN = "bettergrassandleaves"
const val MC_VERSIONS = "[1.7.10]"
const val MC_VERSIONS = "[1.8]"
const val GUI_FACTORY = "mods.betterfoliage.client.gui.ConfigGuiFactory"
var log: Logger? = null

View File

@@ -1,19 +1,20 @@
package mods.betterfoliage.client
import cpw.mods.fml.client.FMLClientHandler
import cpw.mods.fml.relauncher.Side
import cpw.mods.fml.relauncher.SideOnly
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.gui.ConfigGuiFactory
import mods.betterfoliage.client.integration.CLCIntegration
import mods.betterfoliage.client.integration.ShadersModIntegration
import mods.betterfoliage.client.integration.TFCIntegration
import mods.betterfoliage.client.render.*
import mods.betterfoliage.client.texture.*
import mods.betterfoliage.client.texture.GrassGenerator
import mods.betterfoliage.client.texture.GrassRegistry
import mods.betterfoliage.client.texture.LeafGenerator
import mods.betterfoliage.client.texture.LeafRegistry
import mods.octarinecore.client.KeyHandler
import mods.octarinecore.client.resource.CenteringTextureGenerator
import mods.octarinecore.client.resource.GeneratorPack
import net.minecraft.client.Minecraft
import net.minecraftforge.fml.client.FMLClientHandler
import net.minecraftforge.fml.relauncher.Side
import net.minecraftforge.fml.relauncher.SideOnly
import org.apache.logging.log4j.Level
/**
@@ -65,9 +66,7 @@ object Client {
GrassRegistry,
LeafWindTracker,
RisingSoulTextures,
TFCIntegration,
ShadersModIntegration,
CLCIntegration
ShadersModIntegration
)
fun log(level: Level, msg: String) = BetterFoliageMod.log!!.log(level, msg)

View File

@@ -2,32 +2,33 @@
@file:SideOnly(Side.CLIENT)
package mods.betterfoliage.client
import cpw.mods.fml.relauncher.Side
import cpw.mods.fml.relauncher.SideOnly
import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.render.EntityFallingLeavesFX
import mods.betterfoliage.client.render.EntityRisingSoulFX
import mods.betterfoliage.client.render.down1
import mods.betterfoliage.client.render.up1
import mods.octarinecore.client.render.blockContext
import mods.octarinecore.client.resource.LoadModelDataEvent
import mods.octarinecore.common.plus
import net.minecraft.block.Block
import net.minecraft.client.Minecraft
import net.minecraft.block.state.IBlockState
import net.minecraft.client.renderer.BlockRendererDispatcher
import net.minecraft.client.renderer.WorldRenderer
import net.minecraft.init.Blocks
import net.minecraft.util.BlockPos
import net.minecraft.util.EnumFacing
import net.minecraft.util.EnumWorldBlockLayer
import net.minecraft.util.EnumWorldBlockLayer.CUTOUT
import net.minecraft.util.EnumWorldBlockLayer.CUTOUT_MIPPED
import net.minecraft.world.IBlockAccess
import net.minecraft.world.World
import net.minecraftforge.client.model.ModelLoader
import net.minecraftforge.common.MinecraftForge
import net.minecraftforge.fml.relauncher.Side
import net.minecraftforge.fml.relauncher.SideOnly
fun getRenderTypeOverride(blockAccess: IBlockAccess, x: Int, y: Int, z: Int, block: Block, original: Int): Int {
if (!Config.enabled) return original;
// universal sign for DON'T RENDER ME!
if (original == -1) return original;
return blockContext.let { ctx ->
ctx.set(blockAccess, x, y, z)
Client.renderers.find { it.isEligible(ctx) }?.renderId ?: original
}
}
fun shouldRenderBlockSideOverride(original: Boolean, blockAccess: IBlockAccess, x: Int, y: Int, z: Int, side: Int): Boolean {
return original || (Config.enabled && Config.roundLogs.enabled && Config.blocks.logs.matchesID(blockAccess.getBlock(x, y, z)));
fun shouldRenderBlockSideOverride(original: Boolean, blockAccess: IBlockAccess, pos: BlockPos, side: EnumFacing): Boolean {
return original || (Config.enabled && Config.roundLogs.enabled && Config.blocks.logs.matchesID(blockAccess.getBlockState(pos).block));
}
fun getAmbientOcclusionLightValueOverride(original: Float, block: Block): Float {
@@ -39,20 +40,58 @@ fun getUseNeighborBrightnessOverride(original: Boolean, block: Block): Boolean {
return original || (Config.enabled && Config.roundLogs.enabled && Config.blocks.logs.matchesID(block));
}
fun onRandomDisplayTick(block: Block, world: World, x: Int, y: Int, z: Int) {
fun onRandomDisplayTick(world: World, state: IBlockState, pos: BlockPos) {
if (Config.enabled &&
Config.risingSoul.enabled &&
block == Blocks.soul_sand &&
world.isAirBlock(x, y + 1, z) &&
state.block == Blocks.soul_sand &&
world.isAirBlock(pos + up1) &&
Math.random() < Config.risingSoul.chance) {
EntityRisingSoulFX(world, x, y, z).addIfValid()
EntityRisingSoulFX(world, pos).addIfValid()
}
if (Config.enabled &&
Config.fallingLeaves.enabled &&
Config.blocks.leaves.matchesID(block) &&
world.isAirBlock(x, y - 1, z) &&
Config.blocks.leaves.matchesID(state.block) &&
world.isAirBlock(pos + down1) &&
Math.random() < Config.fallingLeaves.chance) {
EntityFallingLeavesFX(world, x, y, z).addIfValid()
EntityFallingLeavesFX(world, pos).addIfValid()
}
}
fun onAfterLoadModelDefinitions(loader: ModelLoader) {
MinecraftForge.EVENT_BUS.post(LoadModelDataEvent(loader))
}
fun renderWorldBlock(dispatcher: BlockRendererDispatcher,
state: IBlockState,
pos: BlockPos,
blockAccess: IBlockAccess,
worldRenderer: WorldRenderer,
layer: EnumWorldBlockLayer
): Boolean {
val isCutout = layer == CUTOUT_MIPPED || layer == CUTOUT
val needsCutout = state.block.canRenderInLayer(CUTOUT_MIPPED) || state.block.canRenderInLayer(CUTOUT)
val canRender = (isCutout && needsCutout) || state.block.canRenderInLayer(layer)
blockContext.let { ctx ->
ctx.set(blockAccess, pos)
Client.renderers.forEach { renderer ->
if (renderer.isEligible(ctx)) {
return if (renderer.moveToCutout) {
if (isCutout) renderer.render(ctx, dispatcher, worldRenderer, layer) else false
} else {
renderer.render(ctx, dispatcher, worldRenderer, layer)
}
}
}
}
return if (canRender) dispatcher.renderBlock(state, pos, blockAccess, worldRenderer) else false
}
fun canRenderBlockInLayer(block: Block, layer: EnumWorldBlockLayer): Boolean {
if (layer == CUTOUT_MIPPED && !block.canRenderInLayer(CUTOUT)) {
return true
}
return block.canRenderInLayer(layer)
}

View File

@@ -1,11 +1,10 @@
package mods.betterfoliage.client.config
import cpw.mods.fml.common.eventhandler.SubscribeEvent
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 mods.octarinecore.config.ConfigPropertyBase
import mods.octarinecore.common.config.ConfigPropertyBase
import mods.octarinecore.metaprog.getJavaClass
import net.minecraft.block.Block
import net.minecraft.client.multiplayer.WorldClient
@@ -13,6 +12,7 @@ import net.minecraftforge.common.MinecraftForge
import net.minecraftforge.common.config.Configuration
import net.minecraftforge.common.config.Property
import net.minecraftforge.event.world.WorldEvent
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
/**
* Match blocks based on their class names. Caches block IDs for faster lookup.

View File

@@ -1,14 +1,14 @@
package mods.betterfoliage.client.config
import cpw.mods.fml.client.event.ConfigChangedEvent
import cpw.mods.fml.relauncher.Side
import cpw.mods.fml.relauncher.SideOnly
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.gui.BiomeListConfigEntry
import mods.octarinecore.config.*
import mods.octarinecore.common.config.*
import mods.octarinecore.metaprog.reflectField
import net.minecraft.client.Minecraft
import net.minecraft.world.biome.BiomeGenBase
import net.minecraftforge.fml.client.event.ConfigChangedEvent
import net.minecraftforge.fml.relauncher.Side
import net.minecraftforge.fml.relauncher.SideOnly
// BetterFoliage-specific property delegates
private fun featureEnable() = boolean(true).lang("enabled")

View File

@@ -1,16 +1,16 @@
package mods.betterfoliage.client.gui
import cpw.mods.fml.client.config.GuiConfig
import cpw.mods.fml.client.config.GuiConfigEntries
import cpw.mods.fml.client.config.IConfigElement
import mods.octarinecore.client.gui.IdListConfigEntry
import net.minecraft.world.biome.BiomeGenBase
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<*>)
owningScreen: GuiConfig,
owningEntryList: GuiConfigEntries,
configElement: IConfigElement)
: IdListConfigEntry<BiomeGenBase>(owningScreen, owningEntryList, configElement) {
override val baseSet: List<BiomeGenBase> get() = BiomeGenBase.getBiomeGenArray().filterNotNull()

View File

@@ -1,18 +1,17 @@
package mods.betterfoliage.client.gui
import cpw.mods.fml.client.IModGuiFactory
import cpw.mods.fml.client.IModGuiFactory.RuntimeOptionCategoryElement
import cpw.mods.fml.client.config.GuiConfig
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 mainConfigGuiClass() = ConfigGuiBetterFoliage::class.java
override fun runtimeGuiCategories() = hashSetOf<RuntimeOptionCategoryElement>()
override fun getHandlerFor(element: RuntimeOptionCategoryElement?) = null
override fun runtimeGuiCategories() = hashSetOf<IModGuiFactory.RuntimeOptionCategoryElement>()
override fun getHandlerFor(element: IModGuiFactory.RuntimeOptionCategoryElement?) = null
override fun initialize(minecraftInstance: Minecraft?) { }
class ConfigGuiBetterFoliage(parentScreen: GuiScreen?) : GuiConfig(

View File

@@ -1,19 +0,0 @@
package mods.betterfoliage.client.integration
import mods.betterfoliage.client.Client
import mods.betterfoliage.loader.Refs
import mods.octarinecore.client.render.brightnessComponents
import org.apache.logging.log4j.Level.*
/**
* Integration for Colored Lights Core.
*/
object CLCIntegration {
init {
if (Refs.CLCLoadingPlugin.element != null) {
Client.log(INFO, "Colored Lights Core integration enabled")
brightnessComponents = listOf(4, 8, 12, 16, 20)
}
}
}

View File

@@ -1,14 +1,17 @@
package mods.betterfoliage.client.integration
import cpw.mods.fml.relauncher.Side
import cpw.mods.fml.relauncher.SideOnly
import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.Config
import mods.betterfoliage.loader.Refs
import mods.octarinecore.metaprog.allAvailable
import net.minecraft.block.Block
import net.minecraft.block.BlockTallGrass
import net.minecraft.block.state.IBlockState
import net.minecraft.client.renderer.WorldRenderer
import net.minecraft.init.Blocks
import org.apache.logging.log4j.Level.*
import net.minecraftforge.fml.relauncher.Side
import net.minecraftforge.fml.relauncher.SideOnly
import org.apache.logging.log4j.Level.INFO
/**
* Integration for ShadersMod.
@@ -17,37 +20,53 @@ import org.apache.logging.log4j.Level.*
object ShadersModIntegration {
@JvmStatic var isPresent = false
@JvmStatic val tallGrassEntityData = Block.blockRegistry.getIDForObject(Blocks.tallgrass) and 65535 or (Blocks.tallgrass.renderType shl 16)
@JvmStatic val leavesEntityData = Block.blockRegistry.getIDForObject(Blocks.leaves) and 65535 or (Blocks.leaves.renderType shl 16)
@JvmStatic val tallGrassEntityData = entityDataFor(Blocks.tallgrass.defaultState.withProperty(BlockTallGrass.TYPE, BlockTallGrass.EnumType.GRASS))
@JvmStatic val leavesEntityData = entityDataFor(Blocks.leaves.defaultState)
fun entityDataFor(blockState: IBlockState) =
(Block.blockRegistry.getIDForObject(blockState.block).toLong() and 65535) or
((blockState.block.renderType.toLong() and 65535) shl 16) or
(blockState.block.getMetaFromState(blockState).toLong() shl 32)
/**
* Called from transformed ShadersMod code.
* @see mods.betterfoliage.loader.BetterFoliageTransformer
*/
@JvmStatic fun getBlockIdOverride(original: Int, block: Block): Int {
if (Config.blocks.leaves.matchesID(original and 65535)) return leavesEntityData
if (Config.blocks.crops.matchesID(original and 65535)) return tallGrassEntityData
@JvmStatic fun getBlockIdOverride(original: Long, blockState: IBlockState): Long {
if (Config.blocks.leaves.matchesID(blockState.block)) return leavesEntityData
if (Config.blocks.crops.matchesID(blockState.block)) return tallGrassEntityData
return original
}
init {
if (allAvailable(Refs.pushEntity_I, Refs.popEntity)) {
if (allAvailable(Refs.sVertexBuilder, Refs.pushEntity_state, Refs.pushEntity_num, Refs.popEntity)) {
Client.log(INFO, "ShadersMod integration enabled")
isPresent = true
}
}
/** Quads rendered inside this block will behave as tallgrass blocks in shader programs. */
inline fun grass(enabled: Boolean = true, func: ()->Unit) {
if (isPresent && enabled) Refs.pushEntity_I.invokeStatic(tallGrassEntityData)
func()
if (isPresent && enabled) Refs.popEntity.invokeStatic()
inline fun grass(renderer: WorldRenderer, enabled: Boolean = true, func: ()->Unit) {
if ((isPresent && enabled)) {
val vertexBuilder = Refs.sVertexBuilder.get(renderer)!!
Refs.pushEntity_num.invoke(vertexBuilder, tallGrassEntityData)
func()
Refs.popEntity.invoke(vertexBuilder)
} else {
func()
}
}
/** Quads rendered inside this block will behave as leaf blocks in shader programs. */
inline fun leaves(enabled: Boolean = true, func: ()->Unit) {
if (isPresent && enabled) Refs.pushEntity_I.invokeStatic(leavesEntityData)
func()
if (isPresent && enabled) Refs.popEntity.invokeStatic()
inline fun leaves(renderer: WorldRenderer, enabled: Boolean = true, func: ()->Unit) {
if ((isPresent && enabled)) {
val vertexBuilder = Refs.sVertexBuilder.get(renderer)!!
Refs.pushEntity_num.invoke(vertexBuilder, leavesEntityData.toLong())
func()
Refs.popEntity.invoke(vertexBuilder)
} else {
func()
}
}
}

View File

@@ -1,37 +0,0 @@
package mods.betterfoliage.client.integration
import cpw.mods.fml.common.Loader
import cpw.mods.fml.relauncher.Side
import cpw.mods.fml.relauncher.SideOnly
import mods.betterfoliage.client.Client
import mods.octarinecore.client.render.Axis
import net.minecraft.block.Block
import org.apache.logging.log4j.Level
/**
* Integration for TerraFirmaCraft
*/
@SideOnly(Side.CLIENT)
object TFCIntegration {
@JvmStatic val vanillaLogAxis = Client.logRenderer.axisFunc
init {
if (Loader.isModLoaded("terrafirmacraft")) {
Client.log(Level.INFO, "TerraFirmaCraft found - setting up compatibility")
// patch axis detection for log blocks to support TFC logs
Client.logRenderer.axisFunc = { block: Block, meta: Int ->
block.javaClass.name.let {
if (it.startsWith("com.bioxx.tfc")) {
if (it.contains("Horiz"))
if (meta shr 3 == 0) Axis.Z else Axis.X
else
Axis.Y
} else {
vanillaLogAxis(block, meta)
}
}
}
}
}
}

View File

@@ -1,11 +1,35 @@
package mods.betterfoliage.client.render
import mods.betterfoliage.client.config.BlockMatcher
import mods.betterfoliage.client.render.AbstractRenderColumn.BlockType.*
import mods.betterfoliage.client.render.AbstractRenderColumn.QuadrantType.*
import mods.octarinecore.client.render.*
import net.minecraft.block.Block
import net.minecraft.client.renderer.RenderBlocks
import net.minecraftforge.common.util.ForgeDirection.*
import mods.octarinecore.client.resource.BlockTextureInspector
import mods.octarinecore.common.Int3
import mods.octarinecore.common.Rotation
import mods.octarinecore.common.face
import mods.octarinecore.common.rot
import net.minecraft.block.state.IBlockState
import net.minecraft.client.renderer.BlockRendererDispatcher
import net.minecraft.client.renderer.WorldRenderer
import net.minecraft.client.renderer.texture.TextureAtlasSprite
import net.minecraft.client.renderer.texture.TextureMap
import net.minecraft.util.EnumFacing
import net.minecraft.util.EnumFacing.*
import net.minecraft.util.EnumWorldBlockLayer
data class ColumnInfo(val topTexture: TextureAtlasSprite,
val bottomTexture: TextureAtlasSprite,
val sideTexture: TextureAtlasSprite)
open class ColumnTextures(val matcher: BlockMatcher) : BlockTextureInspector<ColumnInfo>() {
init {
matchClassAndModel(matcher, "block/column_side", listOf("end", "end", "side"))
matchClassAndModel(matcher, "block/cube_column", listOf("end", "end", "side"))
}
override fun processTextures(textures: List<TextureAtlasSprite>, atlas: TextureMap) =
ColumnInfo(textures[0], textures[1], textures[2])
}
/** Index of SOUTH-EAST quadrant. */
const val SE = 0
@@ -30,7 +54,7 @@ abstract class AbstractRenderColumn(modId: String) : AbstractBlockRenderingHandl
// ============================
abstract val radiusSmall: Double
abstract val radiusLarge: Double
abstract val surroundPredicate: (Block) -> Boolean
abstract val surroundPredicate: (IBlockState) -> Boolean
abstract val connectPerpendicular: Boolean
abstract val connectSolids: Boolean
abstract val lenientConnect: Boolean
@@ -89,26 +113,26 @@ abstract class AbstractRenderColumn(modId: String) : AbstractBlockRenderingHandl
val transitionTop = model { mix(sideRoundLarge.model, sideRoundSmall.model) { it > 1 } }
val transitionBottom = model { mix(sideRoundSmall.model, sideRoundLarge.model) { it > 1 } }
val sideTexture = { ctx: ShadingContext, qi: Int, q: Quad -> if ((qi and 1) == 0) ctx.icon(SOUTH) else ctx.icon(EAST) }
val upTexture = { ctx: ShadingContext, qi: Int, q: Quad -> ctx.icon(UP) }
val downTexture = { ctx: ShadingContext, qi: Int, q: Quad -> ctx.icon(DOWN) }
inline fun continous(q1: QuadrantType, q2: QuadrantType) =
q1 == q2 || ((q1 == SQUARE || q1 == INVISIBLE) && (q2 == SQUARE || q2 == INVISIBLE))
abstract val axisFunc: (Block, Int)->Axis
abstract val blockPredicate: (Block, Int)->Boolean
abstract val axisFunc: (IBlockState)->EnumFacing.Axis
abstract val blockPredicate: (IBlockState)->Boolean
abstract val sideTexture: (ShadingContext, Int, Quad)->TextureAtlasSprite?
abstract val upTexture: (ShadingContext, Int, Quad)->TextureAtlasSprite?
abstract val downTexture: (ShadingContext, Int, Quad)->TextureAtlasSprite?
@Suppress("NON_EXHAUSTIVE_WHEN")
override fun render(ctx: BlockContext, parent: RenderBlocks): Boolean {
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: WorldRenderer, layer: EnumWorldBlockLayer): Boolean {
if (ctx.isSurroundedBy(surroundPredicate) ) return false
// get AO data
if (renderWorldBlockBase(parent, face = neverRender)) return true
modelRenderer.updateShading(Int3.zero, allFaces)
// check log neighborhood
val logAxis = ctx.blockAxis
val baseRotation = rotationFromUp[(logAxis to Dir.P).face.ordinal]
val baseRotation = rotationFromUp[(logAxis to AxisDirection.POSITIVE).face.ordinal]
val upType = ctx.blockType(baseRotation, logAxis, Int3(0, 1, 0))
val downType = ctx.blockType(baseRotation, logAxis, Int3(0, -1, 0))
@@ -141,6 +165,7 @@ abstract class AbstractRenderColumn(modId: String) : AbstractBlockRenderingHandl
}
if (sideModel != null) modelRenderer.render(
renderer,
sideModel,
rotation,
blockContext.blockCenter,
@@ -192,6 +217,7 @@ abstract class AbstractRenderColumn(modId: String) : AbstractBlockRenderingHandl
}
if (upModel != null) modelRenderer.render(
renderer,
upModel,
rotation,
blockContext.blockCenter,
@@ -200,6 +226,7 @@ abstract class AbstractRenderColumn(modId: String) : AbstractBlockRenderingHandl
postProcess = noPost
)
if (downModel != null) modelRenderer.render(
renderer,
downModel,
rotation,
blockContext.blockCenter,
@@ -283,19 +310,18 @@ abstract class AbstractRenderColumn(modId: String) : AbstractBlockRenderingHandl
}
/** Get the axis of the block */
val BlockContext.blockAxis: Axis get() = axisFunc(block(Int3.zero), meta(Int3.zero))
val BlockContext.blockAxis: Axis get() = axisFunc(blockState(Int3.zero))
/**
* Get the type of the block at the given offset in a rotated reference frame.
*/
fun BlockContext.blockType(rotation: Rotation, axis: Axis, offset: Int3): BlockType {
val offsetRot = offset.rotate(rotation)
val logBlock = block(offsetRot)
val logMeta = meta(offsetRot)
return if (!blockPredicate(logBlock, logMeta)) {
if (logBlock.isOpaqueCube) SOLID else NONSOLID
val state = blockState(offsetRot)
return if (!blockPredicate(state)) {
if (state.block.isOpaqueCube) SOLID else NONSOLID
} else {
if (axisFunc(logBlock, logMeta) == axis) PARALLEL else PERPENDICULAR
if (axisFunc(state) == axis) PARALLEL else PERPENDICULAR
}
}
}

View File

@@ -1,31 +1,31 @@
package mods.betterfoliage.client.render
import cpw.mods.fml.common.FMLCommonHandler
import cpw.mods.fml.common.eventhandler.SubscribeEvent
import cpw.mods.fml.common.gameevent.TickEvent
import cpw.mods.fml.relauncher.Side
import cpw.mods.fml.relauncher.SideOnly
import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.texture.LeafRegistry
import mods.octarinecore.PI2
import mods.octarinecore.client.render.AbstractEntityFX
import mods.octarinecore.client.render.Double3
import mods.octarinecore.client.render.HSB
import mods.octarinecore.common.Double3
import mods.octarinecore.minmax
import mods.octarinecore.random
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.Tessellator
import net.minecraft.client.renderer.WorldRenderer
import net.minecraft.util.BlockPos
import net.minecraft.util.MathHelper
import net.minecraft.world.World
import net.minecraftforge.common.MinecraftForge
import net.minecraftforge.common.util.ForgeDirection.DOWN
import net.minecraftforge.event.world.WorldEvent
import net.minecraftforge.fml.common.FMLCommonHandler
import net.minecraftforge.fml.common.eventhandler.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 java.lang.Math.*
import java.util.*
class EntityFallingLeavesFX(world: World, x: Int, y: Int, z: Int) :
AbstractEntityFX(world, x.toDouble() + 0.5, y.toDouble(), 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 {
@JvmStatic val biomeBrightnessMultiplier = 0.5f
@@ -41,10 +41,10 @@ AbstractEntityFX(world, x.toDouble() + 0.5, y.toDouble(), z.toDouble() + 0.5) {
motionY = -Config.fallingLeaves.speed
particleScale = Config.fallingLeaves.size.toFloat() * 0.1f
val block = world.getBlock(x, y, z)
LeafRegistry.leaves[block.getIcon(world, x, y, z, DOWN.ordinal)]?.let {
val state = world.getBlockState(pos)
LeafRegistry[world.getBlockState(pos)]?.let {
particleIcon = it.particleTextures[rand.nextInt(1024)]
calculateParticleColor(it.averageColor, block.colorMultiplier(world, x, y, z))
calculateParticleColor(it.averageColor, state.block.colorMultiplier(world, pos))
}
}
@@ -67,9 +67,9 @@ AbstractEntityFX(world, x.toDouble() + 0.5, y.toDouble(), z.toDouble() + 0.5) {
}
}
override fun render(tessellator: Tessellator, partialTickTime: Float) {
override fun render(worldRenderer: WorldRenderer, partialTickTime: Float) {
if (Config.fallingLeaves.opacityHack) GL11.glDepthMask(true)
renderParticleQuad(tessellator, partialTickTime, rotation = particleRot, isMirrored = isMirrored)
renderParticleQuad(worldRenderer, partialTickTime, rotation = particleRot, isMirrored = isMirrored)
}
fun calculateParticleColor(textureAvgColor: Int, blockColor: Int) {

View File

@@ -1,22 +1,23 @@
package mods.betterfoliage.client.render
import cpw.mods.fml.relauncher.Side
import cpw.mods.fml.relauncher.SideOnly
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.Config
import mods.octarinecore.client.render.AbstractEntityFX
import mods.octarinecore.client.render.Double3
import mods.octarinecore.client.resource.ResourceHandler
import mods.octarinecore.common.Double3
import mods.octarinecore.forEachPairIndexed
import net.minecraft.client.renderer.Tessellator
import net.minecraft.client.renderer.WorldRenderer
import net.minecraft.util.BlockPos
import net.minecraft.util.MathHelper
import net.minecraft.world.World
import org.apache.logging.log4j.Level.*
import net.minecraftforge.fml.relauncher.Side
import net.minecraftforge.fml.relauncher.SideOnly
import org.apache.logging.log4j.Level.INFO
import java.util.*
class EntityRisingSoulFX(world: World, x: Int, y: Int, z: Int) :
AbstractEntityFX(world, x.toDouble() + 0.5, y.toDouble() + 1.0, z.toDouble() + 0.5) {
class EntityRisingSoulFX(world: World, pos: BlockPos) :
AbstractEntityFX(world, pos.x.toDouble() + 0.5, pos.y.toDouble() + 1.0, pos.z.toDouble() + 0.5) {
val particleTrail: Deque<Double3> = linkedListOf()
val initialPhase = rand.nextInt(64)
@@ -40,11 +41,11 @@ AbstractEntityFX(world, x.toDouble() + 0.5, y.toDouble() + 1.0, z.toDouble() + 0
if (!Config.enabled) setDead()
}
override fun render(tessellator: Tessellator, partialTickTime: Float) {
override fun render(worldRenderer: WorldRenderer, partialTickTime: Float) {
var alpha = Config.risingSoul.opacity
if (particleAge > particleMaxAge - 40) alpha *= (particleMaxAge - particleAge) / 40.0f
renderParticleQuad(tessellator, partialTickTime,
renderParticleQuad(worldRenderer, partialTickTime,
size = Config.risingSoul.headSize * 0.25,
alpha = alpha
)
@@ -53,7 +54,7 @@ AbstractEntityFX(world, x.toDouble() + 0.5, y.toDouble() + 1.0, z.toDouble() + 0
particleTrail.forEachPairIndexed { idx, current, previous ->
scale *= Config.risingSoul.sizeDecay
alpha *= Config.risingSoul.opacityDecay
if (idx % Config.risingSoul.trailDensity == 0) renderParticleQuad(tessellator, partialTickTime,
if (idx % Config.risingSoul.trailDensity == 0) renderParticleQuad(worldRenderer, partialTickTime,
currentPos = current,
prevPos = previous,
size = scale,
@@ -66,8 +67,8 @@ AbstractEntityFX(world, x.toDouble() + 0.5, y.toDouble() + 1.0, z.toDouble() + 0
@SideOnly(Side.CLIENT)
object RisingSoulTextures : ResourceHandler(BetterFoliageMod.MOD_ID) {
val headIcons = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "rising_soul_%d")
val trackIcon = iconStatic(BetterFoliageMod.LEGACY_DOMAIN, "soul_track")
val headIcons = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "blocks/rising_soul_%d")
val trackIcon = iconStatic(BetterFoliageMod.LEGACY_DOMAIN, "blocks/soul_track")
override fun afterStitch() {
Client.log(INFO, "Registered ${headIcons.num} soul particle textures")

View File

@@ -3,8 +3,9 @@ package mods.betterfoliage.client.render
import mods.betterfoliage.client.config.Config
import mods.octarinecore.client.render.*
import mods.octarinecore.common.Double3
import mods.octarinecore.exchange
import net.minecraftforge.common.util.ForgeDirection.*
import net.minecraft.util.EnumFacing.*
/** Weight of the same-side AO values on the outer edges of the 45deg chamfered column faces. */
const val chamferAffinity = 0.9f

View File

@@ -5,16 +5,19 @@ import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.integration.ShadersModIntegration
import mods.octarinecore.client.render.*
import mods.octarinecore.common.Int3
import mods.octarinecore.common.Rotation
import net.minecraft.block.material.Material
import net.minecraft.client.renderer.RenderBlocks
import net.minecraft.init.Blocks
import net.minecraft.client.renderer.BlockRendererDispatcher
import net.minecraft.client.renderer.WorldRenderer
import net.minecraft.util.EnumWorldBlockLayer
import org.apache.logging.log4j.Level.INFO
class RenderAlgae : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
val noise = simplexNoise()
val algaeIcons = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "better_algae_%d")
val algaeIcons = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "blocks/better_algae_%d")
val algaeModels = modelSet(64, RenderGrass.grassTopQuads)
override fun afterStitch() {
@@ -28,15 +31,17 @@ class RenderAlgae : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
ctx.block(up1).material == Material.water &&
Config.blocks.dirt.matchesID(ctx.block) &&
ctx.biomeId in Config.algae.biomes &&
noise[ctx.x, ctx.z] < Config.algae.population
noise[ctx.pos] < Config.algae.population
override fun render(ctx: BlockContext, parent: RenderBlocks): Boolean {
if (renderWorldBlockBase(parent, face = alwaysRender)) return true
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: WorldRenderer, layer: EnumWorldBlockLayer): Boolean {
renderWorldBlockBase(ctx, dispatcher, renderer, null)
modelRenderer.updateShading(Int3.zero, allFaces)
val rand = ctx.semiRandomArray(3)
ShadersModIntegration.grass(Config.algae.shaderWind) {
ShadersModIntegration.grass(renderer, Config.algae.shaderWind) {
modelRenderer.render(
renderer,
algaeModels[rand[2]],
Rotation.identity,
icon = { ctx, qi, q -> algaeIcons[rand[qi and 1]]!! },

View File

@@ -4,8 +4,13 @@ import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.Config
import mods.octarinecore.client.render.*
import net.minecraft.client.renderer.RenderBlocks
import net.minecraftforge.common.util.ForgeDirection.*
import mods.octarinecore.client.resource.BlockTextureInspector
import mods.octarinecore.common.Int3
import mods.octarinecore.common.Rotation
import net.minecraft.client.renderer.BlockRendererDispatcher
import net.minecraft.client.renderer.WorldRenderer
import net.minecraft.util.EnumFacing.*
import net.minecraft.util.EnumWorldBlockLayer
import org.apache.logging.log4j.Level
class RenderCactus : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
@@ -13,8 +18,13 @@ class RenderCactus : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
val cactusStemRadius = 0.4375
val cactusArmRotation = listOf(NORTH, SOUTH, EAST, WEST).map { Rotation.rot90[it.ordinal] }
val iconCross = iconStatic(BetterFoliageMod.LEGACY_DOMAIN, "better_cactus")
val iconArm = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "better_cactus_arm_%d")
val iconCross = iconStatic(BetterFoliageMod.LEGACY_DOMAIN, "blocks/better_cactus")
val iconArm = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "blocks/better_cactus_arm_%d")
val iconBase = object : ColumnTextures(Config.blocks.cactus) {
init {
matchClassAndModel(matcher, "block/cactus", listOf("top", "bottom", "side"))
}
}
val modelStem = model {
horizontalRectangle(x1 = -cactusStemRadius, x2 = cactusStemRadius, z1 = -cactusStemRadius, z2 = cactusStemRadius, y = 0.5)
@@ -53,18 +63,23 @@ class RenderCactus : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
ctx.cameraDistance < Config.cactus.distance &&
Config.blocks.cactus.matchesID(ctx.block)
override fun render(ctx: BlockContext, parent: RenderBlocks): Boolean {
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: WorldRenderer, layer: EnumWorldBlockLayer): Boolean {
// get AO data
if (renderWorldBlockBase(parent, face = neverRender)) return true
modelRenderer.updateShading(Int3.zero, allFaces)
val icons = iconBase[ctx.blockState(Int3.zero)] ?: return renderWorldBlockBase(ctx, dispatcher, renderer, null)
modelRenderer.render(
renderer,
modelStem.model,
Rotation.identity,
icon = { ctx, qi, q -> ctx.icon(forgeDirs[qi])},
icon = { ctx, qi, q -> when(qi) {
0 -> icons.bottomTexture; 1 -> icons.topTexture; else -> icons.sideTexture
} },
rotateUV = { 0 },
postProcess = noPost
)
modelRenderer.render(
renderer,
modelCross[ctx.random(0)],
Rotation.identity,
icon = { ctx, qi, q -> iconCross.icon!!},
@@ -72,6 +87,7 @@ class RenderCactus : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
postProcess = noPost
)
modelRenderer.render(
renderer,
modelArm[ctx.random(1)],
cactusArmRotation[ctx.random(2) % 4],
icon = { ctx2, qi, q -> iconArm[ctx.random(3)]!!},

View File

@@ -2,9 +2,13 @@ package mods.betterfoliage.client.render
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.config.Config
import mods.octarinecore.client.render.*
import net.minecraft.block.material.Material
import net.minecraft.client.renderer.RenderBlocks
import mods.octarinecore.client.render.AbstractBlockRenderingHandler
import mods.octarinecore.client.render.BlockContext
import mods.octarinecore.client.render.withOffset
import mods.octarinecore.common.Int3
import net.minecraft.client.renderer.BlockRendererDispatcher
import net.minecraft.client.renderer.WorldRenderer
import net.minecraft.util.EnumWorldBlockLayer
class RenderConnectedGrass : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
override fun isEligible(ctx: BlockContext) =
@@ -13,10 +17,10 @@ class RenderConnectedGrass : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_
Config.blocks.grass.matchesID(ctx.block(up1)) &&
(Config.connectedGrass.snowEnabled || !ctx.block(up2).isSnow)
override fun render(ctx: BlockContext, parent: RenderBlocks): Boolean {
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: WorldRenderer, layer: EnumWorldBlockLayer): Boolean {
return ctx.withOffset(Int3.zero, up1) {
ctx.withOffset(up1, up2) {
renderWorldBlockBase(parent, face = alwaysRender)
renderWorldBlockBase(ctx, dispatcher, renderer, null)
}
}
}

View File

@@ -2,9 +2,15 @@ package mods.betterfoliage.client.render
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.config.Config
import mods.octarinecore.client.render.*
import net.minecraft.client.renderer.RenderBlocks
import net.minecraftforge.common.util.ForgeDirection.*
import mods.octarinecore.client.render.AbstractBlockRenderingHandler
import mods.octarinecore.client.render.BlockContext
import mods.octarinecore.client.render.withOffset
import mods.octarinecore.common.Int3
import mods.octarinecore.common.offset
import net.minecraft.client.renderer.BlockRendererDispatcher
import net.minecraft.client.renderer.WorldRenderer
import net.minecraft.util.EnumFacing.*
import net.minecraft.util.EnumWorldBlockLayer
class RenderConnectedGrassLog : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
@@ -15,17 +21,17 @@ class RenderConnectedGrassLog : AbstractBlockRenderingHandler(BetterFoliageMod.M
Config.blocks.dirt.matchesID(ctx.block) &&
Config.blocks.logs.matchesID(ctx.block(up1))
override fun render(ctx: BlockContext, parent: RenderBlocks): Boolean {
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: WorldRenderer, layer: EnumWorldBlockLayer): Boolean {
val grassDir = grassCheckDirs.find {
Config.blocks.grass.matchesID(ctx.block(it.offset))
}
return if (grassDir != null) {
ctx.withOffset(Int3.zero, grassDir.offset) {
renderWorldBlockBase(parent, face = alwaysRender)
renderWorldBlockBase(ctx, dispatcher, renderer, null)
}
} else {
renderWorldBlockBase(parent, face = alwaysRender)
renderWorldBlockBase(ctx, dispatcher, renderer, null)
}
}
}

View File

@@ -4,18 +4,24 @@ import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.Config
import mods.octarinecore.client.render.*
import mods.octarinecore.common.Int3
import mods.octarinecore.common.forgeDirOffsets
import mods.octarinecore.common.forgeDirs
import mods.octarinecore.random
import net.minecraft.block.material.Material
import net.minecraft.client.renderer.RenderBlocks
import net.minecraftforge.common.util.ForgeDirection.UP
import net.minecraft.client.renderer.BlockRendererDispatcher
import net.minecraft.client.renderer.WorldRenderer
import net.minecraft.util.EnumFacing.Axis
import net.minecraft.util.EnumFacing.UP
import net.minecraft.util.EnumWorldBlockLayer
import org.apache.logging.log4j.Level.INFO
class RenderCoral : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
val noise = simplexNoise()
val coralIcons = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "better_coral_%d")
val crustIcons = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "better_crust_%d")
val coralIcons = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "blocks/better_coral_%d")
val crustIcons = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "blocks/better_crust_%d")
val coralModels = modelSet(64) { modelIdx ->
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)
@@ -32,7 +38,8 @@ class RenderCoral : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
}
override fun afterStitch() {
Client.log(INFO, "Registered ${coralIcons.num} algae textures")
Client.log(INFO, "Registered ${coralIcons.num} coral textures")
Client.log(INFO, "Registered ${crustIcons.num} coral crust textures")
}
override fun isEligible(ctx: BlockContext) =
@@ -42,15 +49,17 @@ class RenderCoral : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
ctx.block(up1).material == Material.water &&
Config.blocks.sand.matchesID(ctx.block) &&
ctx.biomeId in Config.coral.biomes &&
noise[ctx.x, ctx.z] < Config.coral.population
noise[ctx.pos] < Config.coral.population
override fun render(ctx: BlockContext, parent: RenderBlocks): Boolean {
if (renderWorldBlockBase(parent, face = alwaysRender)) return true
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: WorldRenderer, layer: EnumWorldBlockLayer): Boolean {
renderWorldBlockBase(ctx, dispatcher, renderer, null)
modelRenderer.updateShading(Int3.zero, allFaces)
forgeDirs.forEachIndexed { idx, face ->
if (!ctx.block(forgeDirOffsets[idx]).isOpaqueCube && blockContext.random(idx) < Config.coral.chance) {
var variation = blockContext.random(6)
modelRenderer.render(
renderer,
coralModels[variation++],
rotationFromUp[idx],
icon = { ctx, qi, q -> if (qi == 4) crustIcons[variation]!! else coralIcons[variation + (qi and 1)]!!},

View File

@@ -6,10 +6,16 @@ import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.integration.ShadersModIntegration
import mods.betterfoliage.client.texture.GrassRegistry
import mods.octarinecore.client.render.*
import mods.octarinecore.common.Double3
import mods.octarinecore.common.Int3
import mods.octarinecore.common.Rotation
import mods.octarinecore.random
import net.minecraft.block.material.Material
import net.minecraft.client.renderer.RenderBlocks
import net.minecraftforge.common.util.ForgeDirection.UP
import net.minecraft.client.renderer.BlockRendererDispatcher
import net.minecraft.client.renderer.WorldRenderer
import net.minecraft.util.EnumFacing.Axis
import net.minecraft.util.EnumFacing.UP
import net.minecraft.util.EnumWorldBlockLayer
import org.apache.logging.log4j.Level.INFO
class RenderGrass : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
@@ -25,10 +31,10 @@ class RenderGrass : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
}
}
val normalIcons = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "better_grass_long_%d")
val snowedIcons = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "better_grass_snowed_%d")
val normalGenIcon = iconStatic(Client.genGrass.generatedResource("minecraft:tallgrass", "snowed" to false))
val snowedGenIcon = iconStatic(Client.genGrass.generatedResource("minecraft:tallgrass", "snowed" to true))
val normalIcons = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "blocks/better_grass_long_%d")
val snowedIcons = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "blocks/better_grass_snowed_%d")
val normalGenIcon = iconStatic(Client.genGrass.generatedResource("minecraft:blocks/tallgrass", "snowed" to false))
val snowedGenIcon = iconStatic(Client.genGrass.generatedResource("minecraft:blocks/tallgrass", "snowed" to true))
val grassModels = modelSet(64, grassTopQuads)
@@ -43,61 +49,58 @@ class RenderGrass : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
(Config.shortGrass.grassEnabled || Config.connectedGrass.enabled) &&
Config.blocks.grass.matchesID(ctx.block)
override fun render(ctx: BlockContext, parent: RenderBlocks): Boolean {
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: WorldRenderer, layer: EnumWorldBlockLayer): Boolean {
val isConnected = ctx.block(down1).let { Config.blocks.dirt.matchesID(it) || Config.blocks.grass.matchesID(it) }
val isSnowed = ctx.block(up1).isSnow
val connectedGrass = isConnected && Config.connectedGrass.enabled && (!isSnowed || Config.connectedGrass.snowEnabled)
val grassInfo = GrassRegistry.grass[ctx.icon(UP)]
if (grassInfo == null) {
renderWorldBlockBase(parent, face = alwaysRender)
return true
}
val cubeTexture = if (isSnowed) ctx.icon(up1, UP) else null ?: grassInfo.grassTopTexture
val blockColor = ctx.blockColor(Int3.zero)
val grassInfo = GrassRegistry[ctx.blockState(Int3.zero)] ?: return renderWorldBlockBase(ctx, dispatcher, renderer, layer)
val blockColor = ctx.blockData(Int3.zero, 0).color
if (connectedGrass) {
// get AO data
if (renderWorldBlockBase(parent, face = neverRender)) return true
// get full AO data
modelRenderer.updateShading(Int3.zero, allFaces)
// render full grass block
modelRenderer.render(
renderer,
fullCube,
Rotation.identity,
ctx.blockCenter,
icon = { ctx, qi, q -> cubeTexture },
icon = { ctx, qi, q -> grassInfo.grassTopTexture },
rotateUV = { 2 },
postProcess = { ctx, qi, q, vi, v ->
if (isSnowed) { if(!ctx.aoEnabled) setGrey(1.4f) }
else if (qi != UP.ordinal && ctx.aoEnabled) multiplyColor(blockColor)
else if (ctx.aoEnabled) multiplyColor(blockColor)
}
)
} else {
// render normally
if (renderWorldBlockBase(parent, face = alwaysRender)) return true
renderWorldBlockBase(ctx, dispatcher, renderer, null)
// get AO data only for block top
modelRenderer.updateShading(Int3.zero, topOnly)
}
if (!Config.shortGrass.grassEnabled) return true
if (isSnowed && !Config.shortGrass.snowEnabled) return true
if (ctx.block(up1).isOpaqueCube) return true
if (ctx.block(up1).material != Material.air) return true
// render grass quads
val iconset = if (isSnowed) snowedIcons else normalIcons
val iconGen = if (isSnowed) snowedGenIcon else normalGenIcon
val rand = ctx.semiRandomArray(2)
ShadersModIntegration.grass(Config.shortGrass.shaderWind) {
ShadersModIntegration.grass(renderer, Config.shortGrass.shaderWind) {
modelRenderer.render(
renderer,
grassModels[rand[0]],
Rotation.identity,
ctx.blockCenter + (if (isSnowed) snowOffset else Double3.zero),
icon = if (Config.shortGrass.useGenerated)
{ ctx: ShadingContext, qi: Int, q: Quad -> iconGen.icon!! }
else
{ ctx: ShadingContext, qi: Int, q: Quad -> iconset[rand[qi and 1]]!! },
icon = { ctx: ShadingContext, qi: Int, q: Quad ->
if (Config.shortGrass.useGenerated) iconGen.icon!! else iconset[rand[qi and 1]]!!
},
rotateUV = { 0 },
postProcess = if (isSnowed) whitewash else if (grassInfo.overrideColor == null) noPost else
{ ctx, qi, q, vi, v -> multiplyColor(grassInfo.overrideColor) }
postProcess = { ctx, qi, q, vi, v -> if (isSnowed) setGrey(1.4f) else multiplyColor(grassInfo.overrideColor ?: blockColor) }
)
}

View File

@@ -6,11 +6,16 @@ import mods.betterfoliage.client.integration.ShadersModIntegration
import mods.betterfoliage.client.texture.LeafRegistry
import mods.octarinecore.PI2
import mods.octarinecore.client.render.*
import mods.octarinecore.common.Double3
import mods.octarinecore.common.Int3
import mods.octarinecore.common.Rotation
import mods.octarinecore.common.vec
import mods.octarinecore.random
import net.minecraft.block.material.Material
import net.minecraft.client.renderer.RenderBlocks
import net.minecraftforge.common.util.ForgeDirection.DOWN
import net.minecraftforge.common.util.ForgeDirection.UP
import net.minecraft.client.renderer.BlockRendererDispatcher
import net.minecraft.client.renderer.WorldRenderer
import net.minecraft.util.EnumFacing.UP
import net.minecraft.util.EnumWorldBlockLayer
import java.lang.Math.cos
import java.lang.Math.sin
@@ -23,7 +28,7 @@ class RenderLeaves : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
.scale(Config.leaves.size)
.toCross(UP).addAll()
}
val snowedIcon = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "better_leaves_snowed_%d")
val snowedIcon = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "blocks/better_leaves_snowed_%d")
val perturbs = vectorSet(64) { idx ->
val angle = PI2 * idx / 64.0
@@ -37,27 +42,30 @@ class RenderLeaves : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
ctx.cameraDistance < Config.leaves.distance &&
Config.blocks.leaves.matchesID(ctx.block)
override fun render(ctx: BlockContext, parent: RenderBlocks): Boolean {
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: WorldRenderer, layer: EnumWorldBlockLayer): Boolean {
val isSnowed = ctx.block(up1).material.let {
it == Material.snow || it == Material.craftedSnow
}
renderWorldBlockBase(ctx, dispatcher, renderer, null)
val leafInfo = LeafRegistry[ctx.blockState(Int3.zero)] ?: return true
val blockColor = ctx.blockData(Int3.zero, 0).color
if (renderWorldBlockBase(parent, face = alwaysRender)) return true
val leafInfo = LeafRegistry.leaves[ctx.icon(DOWN)]
if (leafInfo != null) ShadersModIntegration.leaves {
modelRenderer.updateShading(Int3.zero, allFaces)
ShadersModIntegration.leaves(renderer) {
val rand = ctx.semiRandomArray(2)
(if (Config.leaves.dense) denseLeavesRot else normalLeavesRot).forEach { rotation ->
modelRenderer.render(
renderer,
leavesModel.model,
rotation,
ctx.blockCenter + perturbs[rand[0]],
icon = { ctx, qi, q -> leafInfo.roundLeafTexture },
rotateUV = { q -> rand[1] },
postProcess = noPost
postProcess = { ctx, qi, q, vi, v -> multiplyColor(blockColor) }
)
}
if (isSnowed) modelRenderer.render(
renderer,
leavesModel.model,
Rotation.identity,
ctx.blockCenter + perturbs[rand[0]],

View File

@@ -3,9 +3,15 @@ package mods.betterfoliage.client.render
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.integration.ShadersModIntegration
import mods.octarinecore.client.render.*
import net.minecraft.client.renderer.RenderBlocks
import net.minecraftforge.common.util.ForgeDirection.*
import mods.octarinecore.common.Int3
import mods.octarinecore.common.Rotation
import net.minecraft.client.renderer.BlockRendererDispatcher
import net.minecraft.client.renderer.WorldRenderer
import net.minecraft.util.EnumFacing.DOWN
import net.minecraft.util.EnumFacing.UP
import net.minecraft.util.EnumWorldBlockLayer
import org.apache.logging.log4j.Level
class RenderLilypad : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
@@ -21,8 +27,8 @@ class RenderLilypad : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
.setFlatShader(FlatOffsetNoColor(Int3.zero))
.toCross(UP).addAll()
}
val rootIcon = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "better_lilypad_roots_%d")
val flowerIcon = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "better_lilypad_flower_%d")
val rootIcon = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "blocks/better_lilypad_roots_%d")
val flowerIcon = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "blocks/better_lilypad_flower_%d")
val perturbs = vectorSet(64) { modelIdx -> xzDisk(modelIdx) * Config.lilypad.hOffset }
override fun afterStitch() {
@@ -35,21 +41,27 @@ class RenderLilypad : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
ctx.cameraDistance < Config.lilypad.distance &&
Config.blocks.lilypad.matchesID(ctx.block)
override fun render(ctx: BlockContext, parent: RenderBlocks): Boolean {
if (renderWorldBlockBase(parent, face = alwaysRender)) return true
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: WorldRenderer, layer: EnumWorldBlockLayer): Boolean {
renderWorldBlockBase(ctx, dispatcher, renderer, null)
modelRenderer.updateShading(Int3.zero, allFaces)
val rand = ctx.semiRandomArray(5)
modelRenderer.render(
rootModel.model,
Rotation.identity,
ctx.blockCenter.add(perturbs[rand[2]]),
forceFlat = true,
icon = { ctx, qi, q -> rootIcon[rand[qi and 1]]!! },
rotateUV = { 0 },
postProcess = noPost
)
ShadersModIntegration.grass(renderer) {
modelRenderer.render(
renderer,
rootModel.model,
Rotation.identity,
ctx.blockCenter.add(perturbs[rand[2]]),
forceFlat = true,
icon = { ctx, qi, q -> rootIcon[rand[qi and 1]]!! },
rotateUV = { 0 },
postProcess = noPost
)
}
if (rand[3] < Config.lilypad.flowerChance) modelRenderer.render(
renderer,
flowerModel.model,
Rotation.identity,
ctx.blockCenter.add(perturbs[rand[4]]),

View File

@@ -2,9 +2,16 @@ package mods.betterfoliage.client.render
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.config.Config
import mods.octarinecore.client.render.Axis
import mods.octarinecore.client.render.BlockContext
import net.minecraft.block.Block
import mods.octarinecore.client.render.Quad
import mods.octarinecore.client.render.ShadingContext
import mods.octarinecore.client.resource.BlockTextureInspector
import mods.octarinecore.common.Int3
import net.minecraft.block.BlockLog
import net.minecraft.block.state.IBlockState
import net.minecraft.client.renderer.texture.TextureAtlasSprite
import net.minecraft.client.renderer.texture.TextureMap
import net.minecraft.util.EnumFacing.Axis
class RenderLog : AbstractRenderColumn(BetterFoliageMod.MOD_ID) {
@@ -13,18 +20,32 @@ class RenderLog : AbstractRenderColumn(BetterFoliageMod.MOD_ID) {
ctx.cameraDistance < Config.roundLogs.distance &&
Config.blocks.logs.matchesID(ctx.block)
override var axisFunc = { block: Block, meta: Int -> when ((meta shr 2) and 3) {
1 -> Axis.X
2 -> Axis.Z
else -> Axis.Y
} }
override var axisFunc = { state: IBlockState ->
when (state.getValue(BlockLog.LOG_AXIS).toString()) {
"x" -> Axis.X
"z" -> Axis.Z
else -> Axis.Y
}
}
override val blockPredicate = { block: Block, meta: Int -> Config.blocks.logs.matchesID(block) }
override val surroundPredicate = { block: Block -> block.isOpaqueCube && !Config.blocks.logs.matchesID(block) }
val columnTextures = ColumnTextures(Config.blocks.logs)
override val blockPredicate = { state: IBlockState -> Config.blocks.logs.matchesID(state.block) }
override val surroundPredicate = { state: IBlockState -> state.block.isOpaqueCube && !Config.blocks.logs.matchesID(state.block) }
override val connectPerpendicular: Boolean get() = Config.roundLogs.connectPerpendicular
override val connectSolids: Boolean get() = Config.roundLogs.connectSolids
override val lenientConnect: Boolean get() = Config.roundLogs.lenientConnect
override val radiusLarge: Double get() = Config.roundLogs.radiusLarge
override val radiusSmall: Double get() = Config.roundLogs.radiusSmall
override val downTexture = { ctx: ShadingContext, idx: Int, quad: Quad ->
columnTextures[ctx.blockData(Int3.zero).state]?.bottomTexture
}
override val sideTexture = { ctx: ShadingContext, idx: Int, quad: Quad ->
columnTextures[ctx.blockData(Int3.zero).state]?.sideTexture
}
override val upTexture = { ctx: ShadingContext, idx: Int, quad: Quad ->
columnTextures[ctx.blockData(Int3.zero).state]?.topTexture
}
}

View File

@@ -3,15 +3,22 @@ package mods.betterfoliage.client.render
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.Config
import mods.octarinecore.client.render.*
import mods.octarinecore.client.render.AbstractBlockRenderingHandler
import mods.octarinecore.client.render.BlockContext
import mods.octarinecore.client.render.modelRenderer
import mods.octarinecore.client.render.noPost
import mods.octarinecore.common.Double3
import mods.octarinecore.common.Rotation
import net.minecraft.block.material.Material
import net.minecraft.client.renderer.RenderBlocks
import net.minecraft.client.renderer.BlockRendererDispatcher
import net.minecraft.client.renderer.WorldRenderer
import net.minecraft.init.Blocks
import net.minecraft.util.EnumWorldBlockLayer
import org.apache.logging.log4j.Level.INFO
class RenderMycelium : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
val myceliumIcon = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "better_mycel_%d")
val myceliumIcon = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "blocks/better_mycel_%d")
val myceliumModel = modelSet(64, RenderGrass.grassTopQuads)
override fun afterStitch() {
@@ -24,17 +31,17 @@ class RenderMycelium : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
ctx.cameraDistance < Config.shortGrass.distance
}
override fun render(ctx: BlockContext, parent: RenderBlocks): Boolean {
val isSnowed = ctx.block(up1).material.let {
it == Material.snow || it == Material.craftedSnow
}
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: WorldRenderer, layer: EnumWorldBlockLayer): Boolean {
val isSnowed = ctx.block(up1).isSnow
renderWorldBlockBase(ctx, dispatcher, renderer, null)
if (renderWorldBlockBase(parent, face = alwaysRender)) return true
if (isSnowed && !Config.shortGrass.snowEnabled) return true
if (ctx.block(up1).isOpaqueCube) return true
val rand = ctx.semiRandomArray(2)
modelRenderer.render(
renderer,
myceliumModel[rand[0]],
Rotation.identity,
ctx.blockCenter + (if (isSnowed) snowOffset else Double3.zero),

View File

@@ -4,16 +4,19 @@ import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.Config
import mods.octarinecore.client.render.*
import mods.octarinecore.common.Int3
import mods.octarinecore.common.Rotation
import mods.octarinecore.random
import net.minecraft.client.renderer.RenderBlocks
import net.minecraft.client.renderer.BlockRendererDispatcher
import net.minecraft.client.renderer.WorldRenderer
import net.minecraft.init.Blocks
import net.minecraftforge.common.util.ForgeDirection.DOWN
import net.minecraftforge.common.util.ForgeDirection.UP
import net.minecraft.util.EnumWorldBlockLayer
import net.minecraft.util.EnumFacing.*
import org.apache.logging.log4j.Level.INFO
class RenderNetherrack : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
val netherrackIcon = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "better_netherrack_%d")
val netherrackIcon = iconSet(BetterFoliageMod.LEGACY_DOMAIN, "blocks/better_netherrack_%d")
val netherrackModel = modelSet(64) { modelIdx ->
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))
@@ -33,12 +36,14 @@ class RenderNetherrack : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID)
ctx.cameraDistance < Config.netherrack.distance
}
override fun render(ctx: BlockContext, parent: RenderBlocks): Boolean {
if (renderWorldBlockBase(parent, face = alwaysRender)) return true
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: WorldRenderer, layer: EnumWorldBlockLayer): Boolean {
renderWorldBlockBase(ctx, dispatcher, renderer, null)
if (ctx.block(down1).isOpaqueCube) return true
modelRenderer.updateShading(Int3.zero, allFaces)
val rand = ctx.semiRandomArray(2)
modelRenderer.render(
renderer,
netherrackModel[rand[0]],
Rotation.identity,
icon = { ctx, qi, q -> netherrackIcon[rand[qi and 1]]!! },

View File

@@ -5,16 +5,20 @@ import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.integration.ShadersModIntegration
import mods.octarinecore.client.render.*
import mods.octarinecore.common.Int3
import mods.octarinecore.common.Rotation
import mods.octarinecore.random
import net.minecraft.block.material.Material
import net.minecraft.client.renderer.RenderBlocks
import net.minecraftforge.common.util.ForgeDirection.UP
import net.minecraft.client.renderer.BlockRendererDispatcher
import net.minecraft.client.renderer.WorldRenderer
import net.minecraft.util.EnumFacing.UP
import net.minecraft.util.EnumWorldBlockLayer
import org.apache.logging.log4j.Level
class RenderReeds : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
val noise = simplexNoise()
val reedIcons = iconSet(Client.genReeds.generatedResource("${BetterFoliageMod.LEGACY_DOMAIN}:better_reed_%d"))
val reedIcons = iconSet(Client.genReeds.generatedResource("${BetterFoliageMod.LEGACY_DOMAIN}:blocks/better_reed_%d"))
val reedModels = modelSet(64) { modelIdx ->
val height = random(Config.reed.heightMin, Config.reed.heightMax)
val waterline = 0.875f
@@ -44,14 +48,16 @@ class RenderReeds : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
ctx.block(up1).material == Material.water &&
Config.blocks.dirt.matchesID(ctx.block) &&
ctx.biomeId in Config.reed.biomes &&
noise[ctx.x, ctx.z] < Config.reed.population
noise[ctx.pos] < Config.reed.population
override fun render(ctx: BlockContext, parent: RenderBlocks): Boolean {
if (renderWorldBlockBase(parent, face = alwaysRender)) return true
override fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: WorldRenderer, layer: EnumWorldBlockLayer): Boolean {
renderWorldBlockBase(ctx, dispatcher, renderer, null)
modelRenderer.updateShading(Int3.zero, allFaces)
val iconVar = ctx.random(1)
ShadersModIntegration.grass(Config.reed.shaderWind) {
ShadersModIntegration.grass(renderer, Config.reed.shaderWind) {
modelRenderer.render(
renderer,
reedModels[ctx.random(0)],
Rotation.identity,
forceFlat = true,

View File

@@ -3,13 +3,14 @@ package mods.betterfoliage.client.render
import mods.octarinecore.PI2
import mods.octarinecore.client.render.*
import mods.octarinecore.common.Double3
import mods.octarinecore.common.Int3
import mods.octarinecore.common.Rotation
import mods.octarinecore.common.times
import net.minecraft.block.Block
import net.minecraft.block.material.Material
import net.minecraft.tileentity.TileEntity
import net.minecraft.world.IBlockAccess
import net.minecraft.world.biome.BiomeGenBase
import net.minecraftforge.common.util.ForgeDirection
import net.minecraftforge.common.util.ForgeDirection.*
import net.minecraft.util.EnumFacing
import net.minecraft.util.EnumFacing.*
val up1 = Int3(1 to UP)
val up2 = Int3(2 to UP)
@@ -24,11 +25,11 @@ val greywash: RenderVertex.(ShadingContext, Int, Quad, Int, Vertex)->Unit = { ct
val Block.isSnow: Boolean get() = material.let { it == Material.snow || it == Material.craftedSnow }
fun Quad.toCross(rotAxis: ForgeDirection, trans: (Quad)->Quad) =
fun Quad.toCross(rotAxis: EnumFacing, trans: (Quad)->Quad) =
(0..3).map { rotIdx ->
trans(rotate(Rotation.rot90[rotAxis.ordinal] * rotIdx).mirrorUV(rotIdx > 1, false))
}
fun Quad.toCross(rotAxis: ForgeDirection) = toCross(rotAxis) { it }
fun Quad.toCross(rotAxis: EnumFacing) = toCross(rotAxis) { it }
fun xzDisk(modelIdx: Int) = (PI2 * modelIdx / 64.0).let { Double3(Math.cos(it), 0.0, Math.sin(it)) }

View File

@@ -1,19 +1,19 @@
package mods.betterfoliage.client.texture
import cpw.mods.fml.common.eventhandler.SubscribeEvent
import cpw.mods.fml.relauncher.Side
import cpw.mods.fml.relauncher.SideOnly
import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.Config
import mods.octarinecore.client.render.HSB
import mods.octarinecore.client.resource.averageColor
import net.minecraft.block.Block
import mods.octarinecore.client.resource.*
import net.minecraft.block.state.IBlockState
import net.minecraft.client.renderer.texture.TextureAtlasSprite
import net.minecraft.client.renderer.texture.TextureMap
import net.minecraft.util.IIcon
import net.minecraft.client.resources.model.ModelResourceLocation
import net.minecraftforge.client.event.TextureStitchEvent
import net.minecraftforge.client.model.IModel
import net.minecraftforge.common.MinecraftForge
import org.apache.logging.log4j.Level.DEBUG
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.INFO
const val defaultGrassColor = 0
@@ -34,36 +34,21 @@ class GrassInfo(
/** Collects and manages rendering-related information for grass blocks. */
@SideOnly(Side.CLIENT)
object GrassRegistry {
val grass: MutableMap<IIcon, GrassInfo> = hashMapOf()
object GrassRegistry : BlockTextureInspector<GrassInfo>() {
init {
MinecraftForge.EVENT_BUS.register(this)
matchClassAndModel(Config.blocks.grass, "block/grass", listOf("top"))
matchClassAndModel(Config.blocks.grass, "block/cube_bottom_top", listOf("top"))
}
@SubscribeEvent
fun handleTextureReload(event: TextureStitchEvent.Pre) {
if (event.map.textureType != 0) return
grass.clear()
override fun onAfterModelLoad() {
super.onAfterModelLoad()
Client.log(INFO, "Inspecting grass textures")
Block.blockRegistry.forEach { block ->
if (Config.blocks.grass.matchesClass(block as Block)) {
block.registerBlockIcons { location ->
val original = event.map.getTextureExtry(location)
Client.log(DEBUG, "Found grass texture: $location")
registerGrass(event.map, original)
return@registerBlockIcons original
}
}
}
}
fun registerGrass(atlas: TextureMap, icon: TextureAtlasSprite) {
val hsb = HSB.fromColor(icon.averageColor ?: defaultGrassColor)
override fun processTextures(textures: List<TextureAtlasSprite>, atlas: TextureMap): GrassInfo {
val hsb = HSB.fromColor(textures[0].averageColor ?: defaultGrassColor)
val overrideColor = if (hsb.saturation > Config.shortGrass.saturationThreshold) hsb.copy(brightness = 0.8f).asColor else null
grass.put(icon, GrassInfo(icon, overrideColor))
return GrassInfo(textures[0], overrideColor)
}
}

View File

@@ -1,21 +1,20 @@
package mods.betterfoliage.client.texture
import cpw.mods.fml.common.FMLCommonHandler
import cpw.mods.fml.common.eventhandler.SubscribeEvent
import cpw.mods.fml.relauncher.Side
import cpw.mods.fml.relauncher.SideOnly
import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.Config
import mods.octarinecore.client.resource.BlockTextureInspector
import mods.octarinecore.client.resource.IconSet
import mods.octarinecore.client.resource.averageColor
import net.minecraft.block.Block
import net.minecraft.client.renderer.texture.TextureAtlasSprite
import net.minecraft.client.renderer.texture.TextureMap
import net.minecraft.util.IIcon
import net.minecraft.util.ResourceLocation
import net.minecraftforge.client.event.TextureStitchEvent
import net.minecraftforge.common.MinecraftForge
import org.apache.logging.log4j.Level.*
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.INFO
import org.apache.logging.log4j.Level.WARN
const val defaultLeafColor = 0
@@ -36,49 +35,24 @@ class LeafInfo(
/** Collects and manages rendering-related information for leaf blocks. */
@SideOnly(Side.CLIENT)
object LeafRegistry {
object LeafRegistry : BlockTextureInspector<LeafInfo>() {
val leaves: MutableMap<IIcon, LeafInfo> = hashMapOf()
val particles: MutableMap<String, IconSet> = hashMapOf()
val typeMappings = TextureMatcher()
val typeMappings = TextureMatcher().apply { loadMappings(ResourceLocation("betterfoliage", "leafTypeMappings.cfg")) }
init {
MinecraftForge.EVENT_BUS.register(this)
matchClassAndModel(Config.blocks.leaves, "minecraft:block/leaves", listOf("all"))
}
@SubscribeEvent
fun handleTextureReload(event: TextureStitchEvent.Pre) {
if (event.map.textureType != 0) return
leaves.clear()
particles.clear()
typeMappings.loadMappings(ResourceLocation("betterfoliage", "leafTypeMappings.cfg"))
Client.log(INFO, "Generating leaf textures")
IconSet("betterfoliage", "falling_leaf_default_%d").let {
it.onStitch(event.map)
particles.put("default", it)
}
Block.blockRegistry.forEach { block ->
if (Config.blocks.leaves.matchesClass(block as Block)) {
block.registerBlockIcons { location ->
val original = event.map.getTextureExtry(location)
Client.log(DEBUG, "Found leaf texture: $location")
registerLeaf(event.map, original)
return@registerBlockIcons original
}
}
}
}
fun registerLeaf(atlas: TextureMap, icon: TextureAtlasSprite) {
var leafType = typeMappings.getType(icon) ?: "default"
val generated = atlas.registerIcon(
Client.genLeaves.generatedResource(icon.iconName, "type" to leafType).toString()
override fun processTextures(textures: List<TextureAtlasSprite>, atlas: TextureMap): LeafInfo {
val texture = textures[0]
var leafType = typeMappings.getType(texture) ?: "default"
val generated = atlas.registerSprite(
Client.genLeaves.generatedResource(texture.iconName, "type" to leafType)
)
if (leafType !in particles.keys) {
val particleSet = IconSet("betterfoliage", "falling_leaf_${leafType}_%d")
val particleSet = IconSet("betterfoliage", "blocks/falling_leaf_${leafType}_%d")
particleSet.onStitch(atlas)
if (particleSet.num == 0) {
Client.log(WARN, "Leaf particle textures not found for leaf type: $leafType")
@@ -88,7 +62,6 @@ object LeafRegistry {
}
}
val leafInfo = LeafInfo(generated as TextureAtlasSprite, leafType)
leaves.put(icon, leafInfo)
return LeafInfo(generated as TextureAtlasSprite, leafType)
}
}

View File

@@ -1,69 +1,42 @@
package mods.betterfoliage.loader
import cpw.mods.fml.relauncher.FMLLaunchHandler
import cpw.mods.fml.relauncher.IFMLLoadingPlugin
import mods.octarinecore.metaprog.*
import mods.octarinecore.metaprog.ASMPlugin
import mods.octarinecore.metaprog.Transformer
import mods.octarinecore.metaprog.allAvailable
import net.minecraftforge.fml.relauncher.FMLLaunchHandler
import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin
import org.objectweb.asm.Opcodes.*
@IFMLLoadingPlugin.TransformerExclusions(
"mods.betterfoliage.loader",
"mods.octarinecore.metaprog",
"kotlin",
"mods.betterfoliage.kotlin"
"mods.octarinecore.kotlin"
)
class BetterFoliageLoader : ASMPlugin(BetterFoliageTransformer::class.java)
class BetterFoliageTransformer : Transformer() {
val isOptifinePresent = allAvailable(Refs.OptifineClassTransformer)
init {
if (FMLLaunchHandler.side().isClient) setupClient()
}
fun setupClient() {
// where: RenderBlocks.renderBlockByRenderType()
// what: invoke BF code to overrule the return value of Block.getRenderType()
// why: allows us to use custom block renderers for any block, without touching block code
transformMethod(Refs.renderBlockByRenderType) {
find(varinsn(ISTORE, 5))?.insertAfter {
log.info("Applying block render type override")
varinsn(ALOAD, 0)
getField(Refs.blockAccess)
varinsn(ILOAD, 2)
varinsn(ILOAD, 3)
varinsn(ILOAD, 4)
varinsn(ALOAD, 1)
varinsn(ILOAD, 5)
invokeStatic(Refs.getRenderTypeOverride)
varinsn(ISTORE, 5)
} ?: log.warn("Failed to apply block render type override!")
}
// where: WorldClient.doVoidFogParticles(), right before the end of the loop
// what: invoke BF code for every random display tick
// why: allows us to catch random display ticks, without touching block code
transformMethod(Refs.doVoidFogParticles) {
find(IINC)?.insertBefore {
log.info("Applying random display tick call hook")
varinsn(ALOAD, 10)
varinsn(ALOAD, 0)
varinsn(ILOAD, 7)
varinsn(ILOAD, 8)
varinsn(ILOAD, 9)
varinsn(ALOAD, 13)
varinsn(ALOAD, if (isOptifinePresent) 8 else 12)
invokeStatic(Refs.onRandomDisplayTick)
} ?: log.warn("Failed to apply random display tick call hook!")
}
// where: shadersmodcore.client.Shaders.pushEntity()
// what: invoke BF code to overrule block data
// why: allows us to change the block ID seen by shader programs
transformMethod(Refs.pushEntity) {
find(IASTORE)?.insertBefore {
log.info("Applying Shaders.pushEntity() block id override")
varinsn(ALOAD, 1)
invokeStatic(Refs.getBlockIdOverride)
} ?: log.warn("Failed to apply Shaders.pushEntity() block id override!")
}
// where: Block.getAmbientOcclusionLightValue()
// what: invoke BF code to overrule AO transparency value
// why: allows us to have light behave properly on non-solid log blocks without
@@ -95,12 +68,67 @@ class BetterFoliageTransformer : Transformer() {
find(IRETURN)?.insertBefore {
log.info("Applying Block.shouldSideBeRendered() override")
varinsn(ALOAD, 1)
varinsn(ILOAD, 2)
varinsn(ILOAD, 3)
varinsn(ILOAD, 4)
varinsn(ILOAD, 5)
varinsn(ALOAD, 2)
varinsn(ALOAD, 3)
invokeStatic(Refs.shouldRenderBlockSideOverride)
} ?: log.warn("Failed to apply Block.shouldSideBeRendered() override!")
}
// where: ModelLoader.setupModelRegistry(), right before the textures are loaded
// what: invoke handler code with ModelLoader instance
// why: allows us to iterate the unbaked models in ModelLoader in time to register textures
transformMethod(Refs.setupModelRegistry) {
find(invokeName("addAll"))?.insertAfter {
log.info("Applying ModelLoader lifecycle callback")
varinsn(ALOAD, 0)
invokeStatic(Refs.onAfterLoadModelDefinitions)
}
}
// where: RenderChunk.rebuildChunk()
// what: replace call to BlockRendererDispatcher.renderBlock()
// why: allows us to perform additional rendering for each block
// what: invoke code to overrule result of Block.canRenderInLayer()
// why: allows us to render transparent quads for blocks which are only on the SOLID layer
transformMethod(Refs.rebuildChunk) {
find(invokeRef(Refs.renderBlock))?.replace {
log.info("Applying RenderChunk block render override")
varinsn(ALOAD, if (isOptifinePresent) 21 else 18)
invokeStatic(Refs.renderWorldBlock)
}
if (isOptifinePresent) {
find(varinsn(ISTORE, 22))?.insertBefore {
log.info("Applying RenderChunk block layer override")
insn(POP)
varinsn(ALOAD, 17)
varinsn(ALOAD, 21)
invokeStatic(Refs.canRenderBlockInLayer)
}
} else {
find(invokeRef(Refs.canRenderInLayer))?.replace {
log.info("Applying RenderChunk block layer override")
invokeStatic(Refs.canRenderBlockInLayer)
}
}
}
// where: net.minecraft.client.renderer.BlockModelRenderer$AmbientOcclusionFace
// what: make constructor public
// why: use vanilla AO calculation at will without duplicating code
transformMethod(Refs.AOF_constructor) {
log.info("Setting AmbientOcclusionFace constructor public")
makePublic()
}
// where: shadersmod.client.SVertexBuilder.pushEntity()
// what: invoke code to overrule block data
// why: allows us to change the block ID seen by shader programs
transformMethod(Refs.pushEntity_state) {
find(invokeRef(Refs.pushEntity_num))?.insertBefore {
log.info("Applying SVertexBuilder.pushEntity() block ID override")
varinsn(ALOAD, 0)
invokeStatic(Refs.getBlockIdOverride)
} ?: log.warn("Failed to apply SVertexBuilder.pushEntity() block ID override!")
}
}
}

View File

@@ -1,9 +1,9 @@
package mods.betterfoliage.loader
import cpw.mods.fml.relauncher.FMLInjectionData
import mods.octarinecore.metaprog.ClassRef
import mods.octarinecore.metaprog.FieldRef
import mods.octarinecore.metaprog.MethodRef
import net.minecraftforge.fml.relauncher.FMLInjectionData
/** Singleton object holding references to foreign code elements. */
object Refs {
@@ -14,58 +14,76 @@ object Refs {
val List = ClassRef("java.util.List")
// Minecraft
val IBlockAccess = ClassRef("net.minecraft.world.IBlockAccess", "ahl")
val IBlockAccess = ClassRef("net.minecraft.world.IBlockAccess", "ard")
val IBlockState = ClassRef("net.minecraft.block.state.IBlockState", "bec")
val BlockPos = ClassRef("net.minecraft.util.BlockPos", "dt")
val EnumWorldBlockLayer = ClassRef("net.minecraft.util.EnumWorldBlockLayer", "aql")
val EnumFacing = ClassRef("net.minecraft.util.EnumFacing", "ej")
val Block = ClassRef("net.minecraft.block.Block", "aji")
val getAmbientOcclusionLightValue = MethodRef(Block, "getAmbientOcclusionLightValue", "func_149685_I", "I", ClassRef.float)
val getUseNeighborBrightness = MethodRef(Block, "getUseNeighborBrightness", "func_149710_n", "n", ClassRef.boolean)
val shouldSideBeRendered = MethodRef(Block, "shouldSideBeRendered", "func_149646_a", "a", ClassRef.boolean, IBlockAccess, ClassRef.int, ClassRef.int, ClassRef.int, ClassRef.int)
val Block = ClassRef("net.minecraft.block.Block", "atr")
val canRenderInLayer = MethodRef(Block, "canRenderInLayer", ClassRef.boolean, EnumWorldBlockLayer)
val getAmbientOcclusionLightValue = MethodRef(Block, "getAmbientOcclusionLightValue", "func_149685_I", "f", ClassRef.float)
val getUseNeighborBrightness = MethodRef(Block, "getUseNeighborBrightness", "func_149710_n", "q", ClassRef.boolean)
val shouldSideBeRendered = MethodRef(Block, "shouldSideBeRendered", "func_149646_a", "a", ClassRef.boolean, IBlockAccess, BlockPos, EnumFacing)
val RenderBlocks = ClassRef("net.minecraft.client.renderer.RenderBlocks", "blm")
val blockAccess = FieldRef(RenderBlocks, "blockAccess", null, "a", IBlockAccess)
val renderBlockByRenderType = MethodRef(RenderBlocks, "renderBlockByRenderType", null, "b", ClassRef.boolean, Block, ClassRef.int, ClassRef.int, ClassRef.int)
val BlockModelRenderer = ClassRef("net.minecraft.client.renderer.BlockModelRenderer", "cln")
val AmbientOcclusionFace = ClassRef("net.minecraft.client.renderer.BlockModelRenderer\$AmbientOcclusionFace", "clq")
val ChunkCompileTaskGenerator = ClassRef("net.minecraft.client.renderer.chunk.ChunkCompileTaskGenerator", "coa")
val WorldRenderer = ClassRef("net.minecraft.client.renderer.WorldRenderer", "civ")
val AOF_constructor = MethodRef(AmbientOcclusionFace, "<init>", ClassRef.void, BlockModelRenderer)
val WorldClient = ClassRef("net.minecraft.client.multiplayer.WorldClient", "bjf")
val doVoidFogParticles = MethodRef(WorldClient, "doVoidFogParticles", null, "C", ClassRef.void, ClassRef.int, ClassRef.int, ClassRef.int)
val RenderChunk = ClassRef("net.minecraft.client.renderer.chunk.RenderChunk", "cop")
val rebuildChunk = MethodRef(RenderChunk, "rebuildChunk", "func_178581_b", "b", ClassRef.void, ClassRef.float, ClassRef.float, ClassRef.float, ChunkCompileTaskGenerator)
val World = ClassRef("net.minecraft.world.World", "ahb")
val BlockRendererDispatcher = ClassRef("net.minecraft.client.renderer.BlockRendererDispatcher", "cll")
val renderBlock = MethodRef(BlockRendererDispatcher, "renderBlock", "func_175018_a", "a", ClassRef.boolean, IBlockState, BlockPos, IBlockAccess, WorldRenderer)
val TextureMap = ClassRef("net.minecraft.client.renderer.texture.TextureMap", "bpr")
val mapRegisteredSprites = FieldRef(TextureMap, "mapRegisteredSprites", "field_110574_e", "bpr", Map)
val World = ClassRef("net.minecraft.world.World", "aqu")
val WorldClient = ClassRef("net.minecraft.client.multiplayer.WorldClient", "cen")
val doVoidFogParticles = MethodRef(WorldClient, "doVoidFogParticles", "func_73029_E", "b", ClassRef.void, ClassRef.int, ClassRef.int, ClassRef.int)
val IMetadataSerializer = ClassRef("net.minecraft.client.resources.data.IMetadataSerializer", "brw")
val SimpleReloadableResourceManager = ClassRef("net.minecraft.client.resources.SimpleReloadableResourceManager", "brg")
val metadataSerializer = FieldRef(SimpleReloadableResourceManager, "rmMetadataSerializer", "field_110547_c", "f", IMetadataSerializer)
// val IMetadataSerializer = ClassRef("net.minecraft.client.resources.data.IMetadataSerializer", "brw")
// val SimpleReloadableResourceManager = ClassRef("net.minecraft.client.resources.SimpleReloadableResourceManager", "brg")
// val metadataSerializer = FieldRef(SimpleReloadableResourceManager, "rmMetadataSerializer", "field_110547_c", "f", IMetadataSerializer)
val IIcon = ClassRef("net.minecraft.util.IIcon", "rf")
val TextureAtlasSprite = ClassRef("net.minecraft.client.renderer.texture.TextureAtlasSprite", "bqd")
val IRegistry = ClassRef("net.minecraft.util.IRegistry", "ez")
val ModelLoader = ClassRef("net.minecraftforge.client.model.ModelLoader")
val stateModels = FieldRef(ModelLoader, "stateModels", Map)
val setupModelRegistry = MethodRef(ModelLoader, "setupModelRegistry", "func_177570_a", "a", IRegistry)
val IModel = ClassRef("net.minecraftforge.client.model.IModel", "")
val ModelBlock = ClassRef("net.minecraft.client.renderer.block.model.ModelBlock", "cmc")
val ModelResourceLocation = ClassRef("net.minecraft.client.renderer.block.model.ModelResourceLocation", "cmc")
val VanillaModelWrapper = ClassRef("net.minecraftforge.client.model.ModelLoader\$VanillaModelWrapper")
val model_VMW = FieldRef(VanillaModelWrapper, "model", ModelBlock)
val location_VMW = FieldRef(VanillaModelWrapper, "location", ModelBlock)
val WeightedPartWrapper = ClassRef("net.minecraftforge.client.model.ModelLoader\$WeightedPartWrapper")
val model_WPW = FieldRef(WeightedPartWrapper, "model", IModel)
val WeightedRandomModel = ClassRef("net.minecraftforge.client.model.ModelLoader\$WeightedRandomModel")
val models_WRM = FieldRef(WeightedRandomModel, "models", List)
// Better Foliage
val BetterFoliageHooks = ClassRef("mods.betterfoliage.client.Hooks")
val getAmbientOcclusionLightValueOverride = MethodRef(BetterFoliageHooks, "getAmbientOcclusionLightValueOverride", ClassRef.float, ClassRef.float, Block)
val getUseNeighborBrightnessOverride = MethodRef(BetterFoliageHooks, "getUseNeighborBrightnessOverride", ClassRef.boolean, ClassRef.boolean, Block)
val shouldRenderBlockSideOverride = MethodRef(BetterFoliageHooks, "shouldRenderBlockSideOverride", ClassRef.boolean, ClassRef.boolean, IBlockAccess, ClassRef.int, ClassRef.int, ClassRef.int, ClassRef.int)
val getRenderTypeOverride = MethodRef(BetterFoliageHooks, "getRenderTypeOverride", ClassRef.int, IBlockAccess, ClassRef.int, ClassRef.int, ClassRef.int, Block, ClassRef.int)
val onRandomDisplayTick = MethodRef(BetterFoliageHooks, "onRandomDisplayTick", ClassRef.void, Block, World, ClassRef.int, ClassRef.int, ClassRef.int)
// Shaders mod
val Shaders = ClassRef("shadersmodcore.client.Shaders")
val pushEntity = MethodRef(Shaders, "pushEntity", ClassRef.void, RenderBlocks, Block, ClassRef.int, ClassRef.int, ClassRef.int)
val pushEntity_I = MethodRef(Shaders, "pushEntity", ClassRef.void, ClassRef.int)
val popEntity = MethodRef(Shaders, "popEntity", ClassRef.void)
val ShadersModIntegration = ClassRef("mods.betterfoliage.client.integration.ShadersModIntegration")
val getBlockIdOverride = MethodRef(ShadersModIntegration, "getBlockIdOverride", ClassRef.int, ClassRef.int, Block)
val shouldRenderBlockSideOverride = MethodRef(BetterFoliageHooks, "shouldRenderBlockSideOverride", ClassRef.boolean, ClassRef.boolean, IBlockAccess, BlockPos, EnumFacing)
val onRandomDisplayTick = MethodRef(BetterFoliageHooks, "onRandomDisplayTick", ClassRef.void, World, IBlockState, BlockPos)
val onAfterLoadModelDefinitions = MethodRef(BetterFoliageHooks, "onAfterLoadModelDefinitions", ClassRef.void, ModelLoader)
val renderWorldBlock = MethodRef(BetterFoliageHooks, "renderWorldBlock", ClassRef.boolean, BlockRendererDispatcher, IBlockState, BlockPos, IBlockAccess, WorldRenderer, EnumWorldBlockLayer)
val canRenderBlockInLayer = MethodRef(BetterFoliageHooks, "canRenderBlockInLayer", ClassRef.boolean, Block, EnumWorldBlockLayer)
// Optifine
val ConnectedTextures = ClassRef("ConnectedTextures")
val getConnectedTexture = MethodRef(ConnectedTextures, "getConnectedTexture", IIcon, IBlockAccess, Block, ClassRef.int, ClassRef.int, ClassRef.int, ClassRef.int, IIcon)
val CTBlockProperties = FieldRef(ConnectedTextures, "blockProperties", null)
val CTTileProperties = FieldRef(ConnectedTextures, "tileProperties", null)
val OptifineClassTransformer = ClassRef("optifine.OptiFineClassTransformer")
val ConnectedProperties = ClassRef("ConnectedProperties")
val CPTileIcons = FieldRef(ConnectedProperties, "tileIcons", null)
// ShadersMod
val SVertexBuilder = ClassRef("shadersmod.client.SVertexBuilder")
val sVertexBuilder = FieldRef(WorldRenderer, "sVertexBuilder", SVertexBuilder)
val pushEntity_state = MethodRef(SVertexBuilder, "pushEntity", ClassRef.void, IBlockState, BlockPos, IBlockAccess, WorldRenderer)
val pushEntity_num = MethodRef(SVertexBuilder, "pushEntity", ClassRef.void, ClassRef.long)
val popEntity = MethodRef(SVertexBuilder, "popEntity", ClassRef.void)
// Colored Lights Core
val CLCLoadingPlugin = ClassRef("coloredlightscore.src.asm.ColoredLightsCoreLoadingPlugin")
val ShadersModIntegration = ClassRef("mods.betterfoliage.client.integration.ShadersModIntegration")
val getBlockIdOverride = MethodRef(ShadersModIntegration, "getBlockIdOverride", ClassRef.long, ClassRef.long, IBlockState)
}

View File

@@ -24,6 +24,15 @@ inline fun <T> MutableList<T>.exchange(idx1: Int, idx2: Int) {
/** Cross product of this [Iterable] with the parameter. */
fun <A, B> Iterable<A>.cross(other: Iterable<B>) = flatMap { a -> other.map { b -> a to b } }
inline fun <C, R, T> Iterable<T>.mapAs(transform: (C) -> R) = map { transform(it as C) }
inline fun <T1, T2> forEachNested(list1: Iterable<T1>, list2: Iterable<T2>, func: (T1, T2)-> Unit) =
list1.forEach { e1 ->
list2.forEach { e2 ->
func(e1, e2)
}
}
/**
* Property-level delegate backed by a [ThreadLocal].
*

View File

@@ -1,10 +1,10 @@
package mods.octarinecore.client
import cpw.mods.fml.client.registry.ClientRegistry
import cpw.mods.fml.common.FMLCommonHandler
import cpw.mods.fml.common.eventhandler.SubscribeEvent
import cpw.mods.fml.common.gameevent.InputEvent
import net.minecraft.client.settings.KeyBinding
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) {

View File

@@ -1,10 +1,10 @@
package mods.octarinecore.client.gui
import cpw.mods.fml.client.config.*
import net.minecraft.client.gui.GuiScreen
import net.minecraft.client.resources.I18n
import net.minecraft.util.EnumChatFormatting.GOLD
import net.minecraft.util.EnumChatFormatting.YELLOW
import net.minecraftforge.fml.client.config.*
/**
* Base class for a config GUI element.
@@ -12,9 +12,9 @@ import net.minecraft.util.EnumChatFormatting.YELLOW
* The config representation is an integer list of the selected objects' IDs.
*/
abstract class IdListConfigEntry<T>(
owningScreen: GuiConfig,
owningEntryList: GuiConfigEntries,
configElement: IConfigElement<*>
owningScreen: GuiConfig,
owningEntryList: GuiConfigEntries,
configElement: IConfigElement
) : GuiConfigEntries.CategoryEntry(owningScreen, owningEntryList, configElement) {
/** Create the child GUI elements. */
@@ -25,10 +25,14 @@ abstract class IdListConfigEntry<T>(
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))
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 {
@@ -45,14 +49,14 @@ abstract class IdListConfigEntry<T>(
/** Child config GUI element of a single toggleable object. */
inner class ItemWrapperElement(val item: T, value: Boolean, val default: Boolean) :
DummyConfigElement<Boolean>(item.itemName, default, ConfigGuiType.BOOLEAN, item.itemName) {
init { set(value) }
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}")
override fun set(value: Boolean) { this.value = value }
fun setDefault(value: Boolean) { this.defaultValue = value }
val booleanValue: Boolean get() = value as Boolean
}
}

View File

@@ -1,21 +1,21 @@
package mods.octarinecore.client.gui
import cpw.mods.fml.client.config.GuiConfig
import cpw.mods.fml.client.config.GuiConfigEntries
import cpw.mods.fml.client.config.IConfigElement
import net.minecraft.client.resources.I18n
import net.minecraft.util.EnumChatFormatting.*
import net.minecraft.util.EnumChatFormatting.AQUA
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<*>
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))
toolTip.addAll(mc.fontRendererObj.listFormattedStringToWidth("$AQUA${I18n.format("fml.configgui.tooltip.default", shortDefaults)}", 300))
}
override fun updateValueButtonText() {

View File

@@ -1,23 +1,21 @@
@file:JvmName("RendererHolder")
package mods.octarinecore.client.render
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler
import cpw.mods.fml.client.registry.RenderingRegistry
import mods.octarinecore.ThreadLocalDelegate
import mods.octarinecore.client.resource.ResourceHandler
import mods.octarinecore.common.Double3
import mods.octarinecore.common.Int3
import mods.octarinecore.common.forgeDirOffsets
import mods.octarinecore.common.plus
import net.minecraft.block.Block
import net.minecraft.block.state.IBlockState
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.RenderBlocks
import net.minecraft.util.IIcon
import net.minecraft.client.renderer.BlockRendererDispatcher
import net.minecraft.client.renderer.WorldRenderer
import net.minecraft.util.BlockPos
import net.minecraft.util.EnumWorldBlockLayer
import net.minecraft.util.MathHelper
import net.minecraft.world.IBlockAccess
import net.minecraftforge.common.util.ForgeDirection
/**
* [ThreadLocal] instance of [ExtendedRenderBlocks] used instead of the vanilla [RenderBlocks] to get the
* AO values and textures used in rendering without duplicating vanilla code.
*/
val renderBlocks by ThreadLocalDelegate { ExtendedRenderBlocks() }
/**
* [ThreadLocal] instance of [BlockContext] representing the block being rendered.
@@ -29,121 +27,77 @@ val blockContext by ThreadLocalDelegate { BlockContext() }
*/
val modelRenderer by ThreadLocalDelegate { ModelRenderer() }
abstract class AbstractBlockRenderingHandler(modId: String) : ResourceHandler(modId), ISimpleBlockRenderingHandler {
abstract class AbstractBlockRenderingHandler(modId: String) : ResourceHandler(modId) {
// ============================
// Self-registration
// ============================
val id = RenderingRegistry.getNextAvailableRenderId()
init {
RenderingRegistry.registerBlockHandler(this);
}
val moveToCutout: Boolean get() = true
// ============================
// Custom rendering
// ============================
abstract fun isEligible(ctx: BlockContext): Boolean
abstract fun render(ctx: BlockContext, parent: RenderBlocks): Boolean
// ============================
// Interface implementation
// ============================
override fun renderWorldBlock(world: IBlockAccess?, x: Int, y: Int, z: Int, block: Block?, modelId: Int, parentRenderer: RenderBlocks?): Boolean {
renderBlocks.blockAccess = world
return render(blockContext, parentRenderer!!)
}
override fun renderInventoryBlock(block: Block?, metadata: Int, modelId: Int, renderer: RenderBlocks?) {}
override fun shouldRender3DInInventory(modelId: Int) = true
override fun getRenderId(): Int = id
abstract fun render(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: WorldRenderer, layer: EnumWorldBlockLayer): Boolean
// ============================
// Vanilla rendering wrapper
// ============================
/**
* Render the block in the current [BlockContext], and capture shading and texture data.
*
* @param[parentRenderer] parent renderer passed in by rendering pipeline, used only for block breaking overlay
* @param[targetPass] which render pass to save shading and texture data from
* @param[block] lambda to use to render the block if it does not have a custom renderer
* @param[face] lambda to determine which faces of the block to render
*/
fun renderWorldBlockBase(
parentRenderer: RenderBlocks = renderBlocks,
targetPass: Int = 1,
block: () -> Unit = { blockContext.let { ctx -> renderBlocks.renderStandardBlock(ctx.block, ctx.x, ctx.y, ctx.z) } },
face: (ShadingCapture, ForgeDirection, Int, IIcon?) -> Boolean
): Boolean {
val ctx = blockContext
val renderBlocks = renderBlocks
// use original renderer for block breaking overlay
if (parentRenderer.hasOverrideBlockTexture()) {
parentRenderer.setRenderBoundsFromBlock(ctx.block);
parentRenderer.renderStandardBlock(ctx.block, ctx.x, ctx.y, ctx.z);
return true;
fun renderWorldBlockBase(ctx: BlockContext, dispatcher: BlockRendererDispatcher, renderer: WorldRenderer, layer: EnumWorldBlockLayer?): Boolean {
ctx.blockState(Int3.zero).let {
if (layer == null || it.block.canRenderInLayer(layer))
return dispatcher.renderBlock(it, ctx.pos, ctx.world, renderer)
}
// render block
renderBlocks.capture.reset(targetPass)
renderBlocks.capture.renderCallback = face
renderBlocks.setRenderBoundsFromBlock(ctx.block);
val handler = renderingHandlers[ctx.block.renderType];
if (handler != null && ctx.block.renderType != 0) {
handler.renderWorldBlock(ctx.world, ctx.x, ctx.y, ctx.z, ctx.block, ctx.block.renderType, renderBlocks);
} else {
block()
}
return false;
return false
}
}
data class BlockData(val state: IBlockState, val color: Int, val brightness: Int)
/**
* Represents the block being rendered. Has properties and methods to query the neighborhood of the block in
* block-relative coordinates.
*/
class BlockContext() {
var world: IBlockAccess? = null
var x: Int = 0
var y: Int = 0
var z: Int = 0
var pos = BlockPos.ORIGIN
fun set(world: IBlockAccess, x: Int, y: Int, z: Int) { this.world = world; this.x = x; this.y = y; this.z = z; }
fun set(world: IBlockAccess, pos: BlockPos) { this.world = world; this.pos = pos; }
/** Get the [Block] at the given offset. */
val block: Block get() = world!!.getBlock(x, y, z)
fun block(offset: Int3) = world!!.getBlock(x + offset.x, y + offset.y, z + offset.z)
/** Get the metadata at the given offset. */
val meta: Int get() = world!!.getBlockMetadata(x, y, z)
fun meta(offset: Int3) = world!!.getBlockMetadata(x + offset.x, y + offset.y, z + offset.z)
/** Get the block color multiplier at the given offset. */
val blockColor: Int get() = block.colorMultiplier(world, x, y, z)
fun blockColor(offset: Int3) = block(offset).colorMultiplier(world, x + offset.x, y + offset.y, z + offset.z)
/** Get the block brightness at the given offset. */
val blockBrightness: Int get() = block.getMixedBrightnessForBlock(world, x, y, z)
fun blockBrightness(offset: Int3) = block(offset).getMixedBrightnessForBlock(world, x + offset.x, y + offset.y, z + offset.z)
val block: Block get() = block(Int3.zero)
fun block(offset: Int3) = blockState(offset).block
fun blockState(offset: Int3) = (pos + offset).let { world!!.getBlockState(it) }
fun blockData(offset: Int3, pass: Int) = (pos + offset).let { pos ->
world!!.getBlockState(pos).let { state ->
BlockData(
state,
state.block.colorMultiplier(world!!, pos, pass),
state.block.getMixedBrightnessForBlock(world!!, pos)
)
}
}
/** Get the biome ID at the block position. */
val biomeId: Int get() = world!!.getBiomeGenForCoords(x, z).biomeID
/** Get the texture on a given face of the block being rendered. */
fun icon(face: ForgeDirection) = block(Int3.zero).getIcon(face.ordinal, meta(Int3.zero))
/** Get the texture on a given face of the block at the given offset. */
fun icon(offset: Int3, face: ForgeDirection) = block(offset).getIcon(face.ordinal, meta(offset))
val biomeId: Int get() = world!!.getBiomeGenForCoords(pos).biomeID
/** Get the centerpoint of the block being rendered. */
val blockCenter: Double3 get() = Double3(x + 0.5, y + 0.5, z + 0.5)
val blockCenter: Double3 get() = Double3(pos.x + 0.5, pos.y + 0.5, pos.z + 0.5)
val chunkBase: Double3 get() {
val cX = if (pos.x >= 0) pos.x / 16 else (pos.x + 1) / 16 - 1
val cY = pos.y / 16
val cZ = if (pos.z >= 0) pos.z / 16 else (pos.z + 1) / 16 - 1
return Double3(cX * 16.0, cY * 16.0, cZ * 16.0)
}
/** Is the block surrounded by other blocks that satisfy the predicate on all sides? */
fun isSurroundedBy(predicate: (Block)->Boolean) = forgeDirOffsets.all { predicate(block(it)) }
fun isSurroundedBy(predicate: (IBlockState)->Boolean) = forgeDirOffsets.all { predicate(blockState(it)) }
/** Get a semi-random value based on the block coordinate and the given seed. */
fun random(seed: Int): Int {
var value = (x * x + y * y + z * z + x * y + y * z + z * x + (seed * seed)) and 63
value = (3 * x * value + 5 * y * value + 7 * z * value + (11 * seed)) and 63
var value = (pos.x * pos.x + pos.y * pos.y + pos.z * pos.z + pos.x * pos.y + pos.y * pos.z + pos.z * pos.x + (seed * seed)) and 63
value = (3 * pos.x * value + 5 * pos.y * value + 7 * pos.z * value + (11 * seed)) and 63
return value
}
@@ -153,8 +107,8 @@ class BlockContext() {
/** Get the distance of the block from the camera (player). */
val cameraDistance: Int get() {
val camera = Minecraft.getMinecraft().renderViewEntity ?: return 0
return Math.abs(x - MathHelper.floor_double(camera.posX)) +
Math.abs(y - MathHelper.floor_double(camera.posY)) +
Math.abs(z - MathHelper.floor_double(camera.posZ))
return Math.abs(pos.x - MathHelper.floor_double(camera.posX)) +
Math.abs(pos.y - MathHelper.floor_double(camera.posY)) +
Math.abs(pos.z - MathHelper.floor_double(camera.posZ))
}
}

View File

@@ -1,10 +1,12 @@
package mods.octarinecore.client.render
import mods.octarinecore.PI2
import mods.octarinecore.common.Double3
import net.minecraft.client.Minecraft
import net.minecraft.client.particle.EntityFX
import net.minecraft.client.renderer.Tessellator
import net.minecraft.util.IIcon
import net.minecraft.client.renderer.WorldRenderer
import net.minecraft.client.renderer.texture.TextureAtlasSprite
import net.minecraft.entity.Entity
import net.minecraft.world.World
abstract class AbstractEntityFX(world: World, x: Double, y: Double, z: Double) : EntityFX(world, x, y, z) {
@@ -30,7 +32,7 @@ abstract class AbstractEntityFX(world: World, x: Double, y: Double, z: Double) :
}
/** Render the particle. */
abstract fun render(tessellator: Tessellator, partialTickTime: Float)
abstract fun render(worldRenderer: WorldRenderer, partialTickTime: Float)
/** Update particle on world tick. */
abstract fun update()
@@ -41,12 +43,11 @@ abstract class AbstractEntityFX(world: World, x: Double, y: Double, z: Double) :
/** Add the particle to the effect renderer if it is valid. */
fun addIfValid() { if (isValid) Minecraft.getMinecraft().effectRenderer.addEffect(this) }
override fun renderParticle(tessellator: Tessellator, partialTickTime: Float, rotX: Float, rotZ: Float, rotYZ: Float, rotXY: Float, rotXZ: Float) {
override fun renderParticle(worldRenderer: WorldRenderer, entity: Entity, partialTickTime: Float, rotX: Float, rotZ: Float, rotYZ: Float, rotXY: Float, rotXZ: Float) {
billboardRot.first.setTo(rotX + rotXY, rotZ, rotYZ + rotXZ)
billboardRot.second.setTo(rotX - rotXY, -rotZ, rotYZ - rotXZ)
render(tessellator, partialTickTime)
render(worldRenderer, partialTickTime)
}
/**
* Render a particle quad.
*
@@ -60,13 +61,13 @@ abstract class AbstractEntityFX(world: World, x: Double, y: Double, z: Double) :
* @param[isMirrored] mirror particle texture along V-axis
* @param[alpha] aplha blending
*/
fun renderParticleQuad(tessellator: Tessellator,
fun renderParticleQuad(worldRenderer: WorldRenderer,
partialTickTime: Float,
currentPos: Double3 = this.currentPos,
prevPos: Double3 = this.prevPos,
size: Double = particleScale.toDouble(),
rotation: Int = 0,
icon: IIcon = particleIcon,
icon: TextureAtlasSprite = particleIcon,
isMirrored: Boolean = false,
alpha: Float = this.particleAlpha) {
@@ -81,11 +82,11 @@ abstract class AbstractEntityFX(world: World, x: Double, y: Double, z: Double) :
val v2 = if (rotation == 0) billboardRot.second * size else
Double3.weight(billboardRot.first, -sin[rotation and 63] * size, billboardRot.second, cos[rotation and 63] * size)
tessellator.setColorRGBA_F(this.particleRed, this.particleGreen, this.particleBlue, alpha)
tessellator.addVertexWithUV(center.x - v1.x, center.y - v1.y, center.z - v1.z, maxU, maxV)
tessellator.addVertexWithUV(center.x - v2.x, center.y - v2.y, center.z - v2.z, maxU, minV)
tessellator.addVertexWithUV(center.x + v1.x, center.y + v1.y, center.z + v1.z, minU, minV)
tessellator.addVertexWithUV(center.x + v2.x, center.y + v2.y, center.z + v2.z, minU, maxV)
worldRenderer.setColorRGBA_F(this.particleRed, this.particleGreen, this.particleBlue, alpha)
worldRenderer.addVertexWithUV(center.x - v1.x, center.y - v1.y, center.z - v1.z, maxU, maxV)
worldRenderer.addVertexWithUV(center.x - v2.x, center.y - v2.y, center.z - v2.z, maxU, minV)
worldRenderer.addVertexWithUV(center.x + v1.x, center.y + v1.y, center.z + v1.z, minU, minV)
worldRenderer.addVertexWithUV(center.x + v2.x, center.y + v2.y, center.z + v2.z, minU, maxV)
}
override fun getFXLayer() = 1

View File

@@ -1,9 +1,11 @@
package mods.octarinecore.client.render
import mods.octarinecore.common.*
import mods.octarinecore.minmax
import mods.octarinecore.replace
import net.minecraftforge.common.util.ForgeDirection
import java.lang.Math.*
import net.minecraft.util.EnumFacing
import java.lang.Math.max
import java.lang.Math.min
/**
* Vertex UV coordinates
@@ -57,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
fun move(trans: Double3) = transformV { it.copy(xyz = it.xyz + trans) }
fun move(trans: Pair<Double, ForgeDirection>) = move(Double3(trans.second) * trans.first)
fun move(trans: Pair<Double, EnumFacing>) = move(Double3(trans.second) * trans.first)
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 scaleUV (scale: Double) = transformV { it.copy(uv = UV(it.uv.u * scale, it.uv.v * scale)) }
@@ -115,7 +117,7 @@ class Model() {
)
}
fun faceQuad(face: ForgeDirection): Quad {
fun faceQuad(face: EnumFacing): Quad {
val base = face.vec * 0.5
val top = faceCorners[face.ordinal].topLeft.first.vec * 0.5
val left = faceCorners[face.ordinal].topLeft.second.vec * 0.5

View File

@@ -1,10 +1,12 @@
package mods.octarinecore.client.render
import mods.octarinecore.client.resource.resourceManager
import mods.octarinecore.common.*
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.Tessellator
import net.minecraft.util.IIcon
import net.minecraftforge.common.util.ForgeDirection
import net.minecraftforge.common.util.ForgeDirection.*
import net.minecraft.client.renderer.WorldRenderer
import net.minecraft.client.renderer.texture.TextureAtlasSprite
import net.minecraft.util.EnumFacing
import net.minecraft.util.EnumFacing.*
class ModelRenderer() : ShadingContext() {
@@ -25,11 +27,12 @@ class ModelRenderer() : ShadingContext() {
* @param[postProcess] lambda to perform arbitrary modifications on the [RenderVertex] just before it goes to the [Tessellator]
*/
inline fun render(
worldRenderer: WorldRenderer,
model: Model,
rot: Rotation,
trans: Double3 = blockContext.blockCenter,
forceFlat: Boolean = false,
icon: (ShadingContext, Int, Quad) -> IIcon,
icon: (ShadingContext, Int, Quad) -> TextureAtlasSprite?,
rotateUV: (Quad) -> Int,
postProcess: RenderVertex.(ShadingContext, Int, Quad, Int, Vertex) -> Unit
) {
@@ -38,17 +41,19 @@ class ModelRenderer() : ShadingContext() {
model.quads.forEachIndexed { quadIdx, quad ->
val drawIcon = icon(this, quadIdx, quad)
val uvRot = rotateUV(quad)
quad.verts.forEachIndexed { vertIdx, vert ->
temp.init(vert)
temp.rotate(rotation).translate(trans).rotateUV(uvRot).setIcon(drawIcon)
val shader = if (aoEnabled && !forceFlat) vert.aoShader else vert.flatShader
shader.shade(this, temp)
temp.postProcess(this, quadIdx, quad, vertIdx, vert)
Tessellator.instance.apply {
setBrightness(temp.brightness)
setColorOpaque_F(temp.red, temp.green, temp.blue)
addVertexWithUV(temp.x, temp.y, temp.z, temp.u, temp.v)
if (drawIcon != null) {
val uvRot = rotateUV(quad)
quad.verts.forEachIndexed { vertIdx, vert ->
temp.init(vert)
temp.rotate(rotation).translate(trans).rotateUV(uvRot).setIcon(drawIcon)
val shader = if (aoEnabled && !forceFlat) vert.aoShader else vert.flatShader
shader.shade(this, temp)
temp.postProcess(this, quadIdx, quad, vertIdx, vert)
worldRenderer.setTextureUV(temp.u, temp.v)
worldRenderer.setBrightness(temp.brightness)
worldRenderer.setColorOpaque_F(temp.red, temp.green, temp.blue)
worldRenderer.addVertex(temp.x, temp.y, temp.z)
}
}
}
@@ -61,13 +66,16 @@ class ModelRenderer() : ShadingContext() {
open class ShadingContext {
var rotation = Rotation.identity
var aoEnabled = Minecraft.isAmbientOcclusionEnabled()
val aoFaces = Array(6) { AoFaceData(forgeDirs[it]) }
fun aoShading(face: ForgeDirection, corner1: ForgeDirection, corner2: ForgeDirection) =
renderBlocks.capture.aoShading(face.rotate(rotation), corner1.rotate(rotation), corner2.rotate(rotation))
fun updateShading(offset: Int3, predicate: (EnumFacing) -> Boolean = { true }) {
forgeDirs.forEach { if (predicate(it)) aoFaces[it.ordinal].update(offset) }
}
fun blockColor(offset: Int3) = blockContext.blockColor(offset.rotate(rotation))
fun blockBrightness(offset: Int3) = blockContext.blockBrightness(offset.rotate(rotation))
fun icon(face: ForgeDirection) = blockContext.icon(face.rotate(rotation))
fun aoShading(face: EnumFacing, corner1: EnumFacing, corner2: EnumFacing) =
aoFaces[face.rotate(rotation).ordinal][corner1.rotate(rotation), corner2.rotate(rotation)]
fun blockData(offset: Int3) = blockContext.blockData(offset.rotate(rotation), 0)
}
/**
@@ -112,7 +120,7 @@ class RenderVertex() {
else -> { return this }
}
}
inline fun setIcon(icon: IIcon): RenderVertex {
inline fun setIcon(icon: TextureAtlasSprite): RenderVertex {
u = (icon.maxU - icon.minU) * (u + 0.5) + icon.minU
v = (icon.maxV - icon.minV) * (v + 0.5) + icon.minV
return this
@@ -134,5 +142,8 @@ class RenderVertex() {
}
}
val allFaces: (EnumFacing) -> Boolean = { true }
val topOnly: (EnumFacing) -> Boolean = { it == UP }
/** Perform no post-processing */
val noPost: RenderVertex.(ShadingContext, Int, Quad, Int, Vertex) -> Unit = { ctx, qi, q, vi, v -> }

View File

@@ -1,54 +1,33 @@
package mods.octarinecore.client.render
import mods.octarinecore.minmax
import mods.octarinecore.common.Int3
import mods.octarinecore.common.plus
import net.minecraft.util.BlockPos
import net.minecraft.util.EnumFacing
import net.minecraft.world.IBlockAccess
import net.minecraftforge.common.util.ForgeDirection
/**
* 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
* @param[xModded] x coordinate of the _modified_ location
* @param[yModded] y coordinate of the _modified_ location
* @param[zModded] z coordinate of the _modified_ location
* @param[xTarget] x coordinate of the _target_ location
* @param[yTarget] y coordinate of the _target_ location
* @param[zTarget] z coordinate of the _target_ location
*/
class OffsetBlockAccess(val original: IBlockAccess,
@JvmField val xModded: Int, @JvmField val yModded: Int, @JvmField val zModded: Int,
@JvmField val xTarget: Int, @JvmField val yTarget: Int, @JvmField val zTarget: Int) : IBlockAccess {
@Suppress("NOTHING_TO_INLINE")
class OffsetBlockAccess(val original: IBlockAccess, val modded: BlockPos, val target: BlockPos) : IBlockAccess {
inline fun <reified T> withOffset(x: Int, y: Int, z: Int, func: (Int,Int,Int)->T): T {
if (x == xModded && y == yModded && z == zModded) {
return func(xTarget, yTarget, zTarget)
} else {
return func(x, y, z)
}
}
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 getBlock(x: Int, y: Int, z: Int) = withOffset(x, y, z)
{ xAct, yAct, zAct -> original.getBlock(xAct, yAct, zAct) }
override fun getBlockMetadata(x: Int, y: Int, z: Int) = withOffset(x, y, z)
{ xAct, yAct, zAct -> original.getBlockMetadata(xAct, yAct, zAct) }
override fun getTileEntity(x: Int, y: Int, z: Int) = withOffset(x, y, z)
{ xAct, yAct, zAct -> original.getTileEntity(xAct, yAct, zAct) }
override fun isSideSolid(x: Int, y: Int, z: Int, side: ForgeDirection?, _default: Boolean) = withOffset(x, y, z)
{ xAct, yAct, zAct -> original.isSideSolid(xAct, yAct, zAct, side, _default) }
override fun isAirBlock(x: Int, y: Int, z: Int) = withOffset(x, y, z)
{ xAct, yAct, zAct -> original.isAirBlock(xAct, yAct, zAct) }
override fun getLightBrightnessForSkyBlocks(x: Int, y: Int, z: Int, side: Int) = withOffset(x, y, z)
{ xAct, yAct, zAct -> original.getLightBrightnessForSkyBlocks(xAct, yAct, zAct, side) }
override fun isBlockProvidingPowerTo(x: Int, y: Int, z: Int, side: Int) = withOffset(x, y, z)
{ xAct, yAct, zAct -> original.isBlockProvidingPowerTo(xAct, yAct, zAct, side) }
override fun getBiomeGenForCoords(x: Int, z: Int) = withOffset(x, 0, z)
{ xAct, yAct, zAct -> original.getBiomeGenForCoords(xAct, zAct) }
override fun getHeight() = original.height
override fun extendedLevelsInChunkCache() = original.extendedLevelsInChunkCache()
override fun getBiomeGenForCoords(pos: BlockPos?) = original.getBiomeGenForCoords(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.
@@ -59,10 +38,8 @@ class OffsetBlockAccess(val original: IBlockAccess,
*/
inline fun <reified T> BlockContext.withOffset(modded: Int3, target: Int3, func: () -> T): T {
val original = world!!
world = OffsetBlockAccess(original, x + modded.x, y + modded.y, z + modded.z, x + target.x, y + target.y, z + target.z)
renderBlocks.blockAccess = world
world = OffsetBlockAccess(original, pos + modded, pos + target)
val result = func()
world = original
renderBlocks.blockAccess = original
return result
}

View File

@@ -1,147 +0,0 @@
package mods.octarinecore.client.render
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler
import cpw.mods.fml.client.registry.RenderingRegistry
import mods.octarinecore.metaprog.reflectField
import mods.octarinecore.metaprog.reflectStaticField
import net.minecraft.block.Block
import net.minecraft.client.renderer.RenderBlocks
import net.minecraft.util.IIcon
import net.minecraftforge.common.util.ForgeDirection
import net.minecraftforge.common.util.ForgeDirection.*
/** Reference to the handler list in Forge [RenderingRegistry]. */
val renderingHandlers: Map<Int, ISimpleBlockRenderingHandler> = RenderingRegistry::class.java
.reflectStaticField<RenderingRegistry>("INSTANCE")!!
.reflectField<Map<Int, ISimpleBlockRenderingHandler>>("blockRenderers")!!
/**
* Used instead of the vanilla [RenderBlocks] to get to the AO values and textures used in rendering
* without duplicating vanilla code.
*/
class ExtendedRenderBlocks : RenderBlocks() {
/** Captures the AO values and textures used in a specific rendering pass when rendering a block. */
val capture = ShadingCapture()
override fun renderFaceXPos(block: Block?, x: Double, y: Double, z: Double, icon: IIcon?) = renderFace(EAST, block, x, y, z, icon)
override fun renderFaceXNeg(block: Block?, x: Double, y: Double, z: Double, icon: IIcon?) = renderFace(WEST, block, x, y, z, icon)
override fun renderFaceYPos(block: Block?, x: Double, y: Double, z: Double, icon: IIcon?) = renderFace(UP, block, x, y, z, icon)
override fun renderFaceYNeg(block: Block?, x: Double, y: Double, z: Double, icon: IIcon?) = renderFace(DOWN, block, x, y, z, icon)
override fun renderFaceZPos(block: Block?, x: Double, y: Double, z: Double, icon: IIcon?) = renderFace(SOUTH, block, x, y, z, icon)
override fun renderFaceZNeg(block: Block?, x: Double, y: Double, z: Double, icon: IIcon?) = renderFace(NORTH, block, x, y, z, icon)
/**
* Render a block face, saving relevant data if appropriate.
*/
@Suppress("NON_EXHAUSTIVE_WHEN")
fun renderFace(face: ForgeDirection, block: Block?, x: Double, y: Double, z: Double, icon: IIcon?) {
if (capture.isCorrectPass(face)) {
saveAllShading(face); capture.icons[face.ordinal] = icon
}
if (capture.renderCallback(capture, face, capture.passes[face.ordinal], icon)) when (face) {
EAST -> super.renderFaceXPos(block, x, y, z, icon)
WEST -> super.renderFaceXNeg(block, x, y, z, icon)
UP -> super.renderFaceYPos(block, x, y, z, icon)
DOWN -> super.renderFaceYNeg(block, x, y, z, icon)
SOUTH -> super.renderFaceZPos(block, x, y, z, icon)
NORTH -> super.renderFaceZNeg(block, x, y, z, icon)
}
}
fun saveTopLeft(face: ForgeDirection, corner: Pair<ForgeDirection, ForgeDirection>) =
capture.aoShading(face, corner.first, corner.second)
.set(brightnessTopLeft, colorRedTopLeft, colorGreenTopLeft, colorBlueTopLeft)
fun saveTopRight(face: ForgeDirection, corner: Pair<ForgeDirection, ForgeDirection>) =
capture.aoShading(face, corner.first, corner.second)
.set(brightnessTopRight, colorRedTopRight, colorGreenTopRight, colorBlueTopRight)
fun saveBottomLeft(face: ForgeDirection, corner: Pair<ForgeDirection, ForgeDirection>) =
capture.aoShading(face, corner.first, corner.second)
.set(brightnessBottomLeft, colorRedBottomLeft, colorGreenBottomLeft, colorBlueBottomLeft)
fun saveBottomRight(face: ForgeDirection, corner: Pair<ForgeDirection, ForgeDirection>) =
capture.aoShading(face, corner.first, corner.second)
.set(brightnessBottomRight, colorRedBottomRight, colorGreenBottomRight, colorBlueBottomRight)
fun saveAllShading(face: ForgeDirection) {
saveTopLeft(face, faceCorners[face.ordinal].topLeft)
saveTopRight(face, faceCorners[face.ordinal].topRight)
saveBottomLeft(face, faceCorners[face.ordinal].bottomLeft)
saveBottomRight(face, faceCorners[face.ordinal].bottomRight)
}
}
/**
* Captures the AO values and textures used in a specific rendering pass when rendering a block.
*/
class ShadingCapture {
/** Sparse array of stored AO data. */
val aoShadings = arrayOfNulls<AoData>(6 * 6 * 6)
/** List of stored AO data (only valid instances). */
var shadingsList = listOf<AoData>()
/** List of stored texture data. */
val icons = arrayOfNulls<IIcon>(6)
/** Number of passes to go on a given face. */
val passes = Array(6) { 0 }
/** lambda to determine which faces to render. */
var renderCallback = alwaysRender
init {
(0..5).forEach { i1 ->
(0..5).forEach { i2 ->
(i2..5).forEach { i3 ->
aoShadings[cornerId(i1, i2, i3)] = AoData()
}
}
}
shadingsList = aoShadings.filterNotNull()
}
/**
* Get the AO data of a specific corner.
*
* The two corner directions are interchangeable. All 3 parameters must lie on different axes.
*
* @param[face] block face
* @param[corner1] first direction of corner on face
* @param[corner2] second direction of corner on face
*/
fun aoShading(face: ForgeDirection, corner1: ForgeDirection, corner2: ForgeDirection) =
aoShadings[cornerId(face, corner1, corner2)]!!
/** Returns true if the AO and texture data should be saved. Mutates state. */
fun isCorrectPass(face: ForgeDirection) = (passes[face.ordinal]-- > 0)
/**
* Reset all data and pass counters.
*
* @param[targetPass] which render pass to save
*/
fun reset(targetPass: Int) {
shadingsList.forEach { it.reset() }
(0..5).forEach { idx -> icons[idx] = null; passes[idx] = targetPass }
}
/** One-dimensional index of a specific corner. */
protected fun cornerId(face: Int, corner1: Int, corner2: Int) = when (corner2 > corner1) {
true -> 36 * face + 6 * corner1 + corner2
false -> 36 * face + 6 * corner2 + corner1
}
/** One-dimensional index of a specific corner. */
protected fun cornerId(face: ForgeDirection, corner1: ForgeDirection, corner2: ForgeDirection) =
cornerId(face.ordinal, corner1.ordinal, corner2.ordinal)
}
/** Lambda to render all faces of a block */
val alwaysRender: (ShadingCapture, ForgeDirection, Int, IIcon?) -> Boolean = { ctx, face, pass, icon -> true }
/** Lambda to render no faces of a block */
val neverRender: (ShadingCapture, ForgeDirection, Int, IIcon?) -> Boolean = { ctx, face, pass, icon -> false }

View File

@@ -1,24 +1,26 @@
package mods.octarinecore.client.render
import net.minecraftforge.common.util.ForgeDirection
import mods.octarinecore.common.*
import net.minecraft.util.EnumFacing
const val defaultCornerDimming = 0.5f
const val defaultEdgeDimming = 0.8f
// ================================
// Resolvers for automatic shading
// Shader instantiation lambdas
// ================================
fun cornerAo(fallbackAxis: Axis): (ForgeDirection, ForgeDirection, ForgeDirection)->Shader = { face, dir1, dir2 ->
fun cornerAo(fallbackAxis: EnumFacing.Axis): (EnumFacing, EnumFacing, EnumFacing)->Shader = { face, dir1, dir2 ->
val fallbackDir = listOf(face, dir1, dir2).find { it.axis == fallbackAxis }!!
CornerSingleFallback(face, dir1, dir2, fallbackDir)
}
val cornerFlat = { face: ForgeDirection, dir1: ForgeDirection, dir2: ForgeDirection -> FaceFlat(face) }
fun cornerAoTri(func: (AoData, AoData)-> AoData) = { face: ForgeDirection, dir1: ForgeDirection, dir2: ForgeDirection ->
val cornerFlat = { face: EnumFacing, dir1: EnumFacing, dir2: EnumFacing -> FaceFlat(face) }
fun cornerAoTri(func: (AoData, AoData)-> AoData) = { face: EnumFacing, dir1: EnumFacing, dir2: EnumFacing ->
CornerTri(face, dir1, dir2, func)
}
val cornerAoMaxGreen = cornerAoTri { s1, s2 -> if (s1.green > s2.green) s1 else s2 }
fun cornerInterpolate(edgeAxis: Axis, weight: Float, dimming: Float): (ForgeDirection, ForgeDirection, ForgeDirection)->Shader = { dir1, dir2, dir3 ->
fun cornerInterpolate(edgeAxis: EnumFacing.Axis, weight: Float, dimming: Float): (EnumFacing, EnumFacing, EnumFacing)->Shader = { dir1, dir2, dir3 ->
val edgeDir = listOf(dir1, dir2, dir3).find { it.axis == edgeAxis }!!
val faceDirs = listOf(dir1, dir2, dir3).filter { it.axis != edgeAxis }
CornerInterpolateDimming(faceDirs[0], faceDirs[1], edgeDir, weight, dimming)
@@ -32,14 +34,15 @@ object NoShader : Shader {
override fun rotate(rot: Rotation) = this
}
class CornerSingleFallback(val face: ForgeDirection, val dir1: ForgeDirection, val dir2: ForgeDirection, val fallbackDir: ForgeDirection, val fallbackDimming: Float = defaultCornerDimming) : Shader {
class CornerSingleFallback(val face: EnumFacing, val dir1: EnumFacing, val dir2: EnumFacing, val fallbackDir: EnumFacing, val fallbackDimming: Float = defaultCornerDimming) : Shader {
val offset = Int3(fallbackDir)
override fun shade(context: ShadingContext, vertex: RenderVertex) {
val shading = context.aoShading(face, dir1, dir2)
if (shading.valid)
vertex.shade(shading)
else
vertex.shade(context.blockBrightness(offset) brMul fallbackDimming, context.blockColor(offset) colorMul fallbackDimming)
else context.blockData(offset).let {
vertex.shade(it.brightness brMul fallbackDimming, it.color colorMul fallbackDimming)
}
}
override fun rotate(rot: Rotation) = CornerSingleFallback(face.rotate(rot), dir1.rotate(rot), dir2.rotate(rot), fallbackDir.rotate(rot), fallbackDimming)
}
@@ -53,7 +56,7 @@ inline fun accumulate(v1: AoData?, v2: AoData?, func: ((AoData, AoData)-> AoData
return null
}
class CornerTri(val face: ForgeDirection, val dir1: ForgeDirection, val dir2: ForgeDirection,
class CornerTri(val face: EnumFacing, val dir1: EnumFacing, val dir2: EnumFacing,
val func: ((AoData, AoData)-> AoData)) : Shader {
override fun shade(context: ShadingContext, vertex: RenderVertex) {
var acc = accumulate(
@@ -69,17 +72,18 @@ class CornerTri(val face: ForgeDirection, val dir1: ForgeDirection, val dir2: Fo
override fun rotate(rot: Rotation) = CornerTri(face.rotate(rot), dir1.rotate(rot), dir2.rotate(rot), func)
}
class EdgeInterpolateFallback(val face: ForgeDirection, val edgeDir: ForgeDirection, val pos: Double, val fallbackDimming: Float = defaultEdgeDimming): Shader {
class EdgeInterpolateFallback(val face: EnumFacing, val edgeDir: EnumFacing, val pos: Double, val fallbackDimming: Float = defaultEdgeDimming): Shader {
val offset = Int3(edgeDir)
val edgeAxis = axes.find { it != face.axis && it != edgeDir.axis }!!
val weightN = (0.5 - pos).toFloat()
val weightP = (0.5 + pos).toFloat()
override fun shade(context: ShadingContext, vertex: RenderVertex) {
val shadingP = context.aoShading(face, edgeDir, (edgeAxis to Dir.P).face)
val shadingN = context.aoShading(face, edgeDir, (edgeAxis to Dir.N).face)
if (!shadingP.valid && !shadingN.valid)
return vertex.shade(context.blockBrightness(offset) brMul fallbackDimming, context.blockColor(offset) colorMul fallbackDimming)
val shadingP = context.aoShading(face, edgeDir, (edgeAxis to EnumFacing.AxisDirection.POSITIVE).face)
val shadingN = context.aoShading(face, edgeDir, (edgeAxis to EnumFacing.AxisDirection.NEGATIVE).face)
if (!shadingP.valid && !shadingN.valid) context.blockData(offset).let {
return vertex.shade(it.brightness brMul fallbackDimming, it.color colorMul fallbackDimming)
}
if (!shadingP.valid) return vertex.shade(shadingN)
if (!shadingN.valid) return vertex.shade(shadingP)
vertex.shade(shadingP, shadingN, weightP, weightN)
@@ -87,7 +91,7 @@ class EdgeInterpolateFallback(val face: ForgeDirection, val edgeDir: ForgeDirect
override fun rotate(rot: Rotation) = EdgeInterpolateFallback(face.rotate(rot), edgeDir.rotate(rot), pos)
}
class CornerInterpolateDimming(val face1: ForgeDirection, val face2: ForgeDirection, val edgeDir: ForgeDirection,
class CornerInterpolateDimming(val face1: EnumFacing, val face2: EnumFacing, val edgeDir: EnumFacing,
val weight: Float, val dimming: Float, val fallbackDimming: Float = defaultCornerDimming) : Shader {
val offset = Int3(edgeDir)
override fun shade(context: ShadingContext, vertex: RenderVertex) {
@@ -95,8 +99,9 @@ class CornerInterpolateDimming(val face1: ForgeDirection, val face2: ForgeDirect
var shading2 = context.aoShading(face2, edgeDir, face1)
var weight1 = weight
var weight2 = 1.0f - weight
if (!shading1.valid && !shading2.valid)
return vertex.shade(context.blockBrightness(offset) brMul fallbackDimming, context.blockColor(offset) colorMul fallbackDimming)
if (!shading1.valid && !shading2.valid) context.blockData(offset).let {
return vertex.shade(it.brightness brMul fallbackDimming, it.color colorMul fallbackDimming)
}
if (!shading1.valid) { shading1 = shading2; weight1 *= dimming }
if (!shading2.valid) { shading2 = shading1; weight2 *= dimming }
vertex.shade(shading1, shading2, weight1, weight2)
@@ -106,7 +111,7 @@ class CornerInterpolateDimming(val face1: ForgeDirection, val face2: ForgeDirect
CornerInterpolateDimming(face1.rotate(rot), face2.rotate(rot), edgeDir.rotate(rot), weight, dimming, fallbackDimming)
}
class FaceCenter(val face: ForgeDirection): Shader {
class FaceCenter(val face: EnumFacing): Shader {
override fun shade(context: ShadingContext, vertex: RenderVertex) {
vertex.red = 0.0f; vertex.green = 0.0f; vertex.blue = 0.0f;
val b = IntArray(4)
@@ -123,25 +128,27 @@ class FaceCenter(val face: ForgeDirection): Shader {
override fun rotate(rot: Rotation) = FaceCenter(face.rotate(rot))
}
class FaceFlat(val face: ForgeDirection): Shader {
class FaceFlat(val face: EnumFacing): Shader {
override fun shade(context: ShadingContext, vertex: RenderVertex) {
val color = context.blockColor(Int3.zero)
vertex.shade(context.blockBrightness(face.offset), color)
val color = context.blockData(Int3.zero).color
vertex.shade(context.blockData(face.offset).brightness, color)
}
override fun rotate(rot: Rotation): Shader = FaceFlat(face.rotate(rot))
}
class FlatOffset(val offset: Int3): Shader {
override fun shade(context: ShadingContext, vertex: RenderVertex) {
vertex.brightness = context.blockBrightness(offset)
vertex.setColor(context.blockColor(offset))
context.blockData(offset).let {
vertex.brightness = it.brightness
vertex.setColor(it.color)
}
}
override fun rotate(rot: Rotation): Shader = this
}
class FlatOffsetNoColor(val offset: Int3): Shader {
override fun shade(context: ShadingContext, vertex: RenderVertex) {
vertex.brightness = context.blockBrightness(offset)
vertex.brightness = context.blockData(offset).brightness
vertex.red = 1.0f; vertex.green = 1.0f; vertex.blue = 1.0f
}
override fun rotate(rot: Rotation): Shader = this

View File

@@ -1,7 +1,11 @@
package mods.octarinecore.client.render
import net.minecraftforge.common.util.ForgeDirection
import java.lang.Math.*
import mods.octarinecore.common.*
import net.minecraft.client.BFBlockModelRenderer
import net.minecraft.util.EnumFacing
import net.minecraft.util.EnumFacing.*
import java.lang.Math.min
import java.util.*
/** Holds shading values for block corners as calculated by vanilla Minecraft rendering. */
class AoData() {
@@ -22,11 +26,58 @@ class AoData() {
this.blue = blue
}
fun set(brightness: Int, colorMultiplier: Float) {
this.valid = true
this.brightness = brightness
this.red = colorMultiplier
this.green = colorMultiplier
this.blue = colorMultiplier
}
companion object {
val black = AoData();
}
}
class AoFaceData(val face: EnumFacing) {
val ao = BFBlockModelRenderer.getVanillaAoObject()
val top = faceCorners[face.ordinal].topLeft.first
val left = faceCorners[face.ordinal].topLeft.second
val topLeft = AoData()
val topRight = AoData()
val bottomLeft = AoData()
val bottomRight = AoData()
val ordered = when(face) {
DOWN -> listOf(topLeft, bottomLeft, bottomRight, topRight)
UP -> listOf(bottomRight, topRight, topLeft, bottomLeft)
NORTH -> listOf(bottomLeft, bottomRight, topRight, topLeft)
SOUTH -> listOf(topLeft, bottomLeft, bottomRight, topRight)
WEST -> listOf(bottomLeft, bottomRight, topRight, topLeft)
EAST -> listOf(topRight, topLeft, bottomLeft, bottomRight)
}
fun update(offset: Int3, useBounds: Boolean = false) {
val ctx = blockContext
val blockState = ctx.blockState(offset)
val quadBounds: FloatArray = FloatArray(12)
val flags = BitSet(3).apply { set(0) }
ao.updateVertexBrightness(ctx.world, blockState.block, ctx.pos + offset, face, quadBounds, flags)
ordered.forEachIndexed { idx, aoData -> aoData.set(ao.vertexBrightness[idx], ao.vertexColorMultiplier[idx]) }
}
operator fun get(dir1: EnumFacing, dir2: EnumFacing): AoData {
val isTop = top == dir1 || top == dir2
val isLeft = left == dir1 || left == dir2
return if (isTop) {
if (isLeft) topLeft else topRight
} else {
if (isLeft) bottomLeft else bottomRight
}
}
}
/**
* Instances of this interface are associated with [Model] vertices, and used to apply brightness and color
* values to a [RenderVertex].
@@ -81,9 +132,9 @@ interface Shader {
* @param[corner] shader instantiation lambda for corner vertices
* @param[edge] shader instantiation lambda for edge midpoint vertices
*/
fun faceOrientedAuto(overrideFace: ForgeDirection? = null,
corner: ((ForgeDirection, ForgeDirection, ForgeDirection)->Shader)? = null,
edge: ((ForgeDirection, ForgeDirection)->Shader)? = null) =
fun faceOrientedAuto(overrideFace: EnumFacing? = null,
corner: ((EnumFacing, EnumFacing, EnumFacing)->Shader)? = null,
edge: ((EnumFacing, EnumFacing)->Shader)? = null) =
fun(quad: Quad, vertex: Vertex): Shader {
val quadFace = overrideFace ?: quad.normal.nearestCardinal
val nearestCorner = nearestPosition(vertex.xyz, faceCorners[quadFace.ordinal].asList) {
@@ -108,8 +159,8 @@ fun faceOrientedAuto(overrideFace: ForgeDirection? = null,
* @param[overrideEdge] assume the given edge instead of going by the _quad_ normal
* @param[corner] shader instantiation lambda
*/
fun edgeOrientedAuto(overrideEdge: Pair<ForgeDirection, ForgeDirection>? = null,
corner: (ForgeDirection, ForgeDirection, ForgeDirection)->Shader) =
fun edgeOrientedAuto(overrideEdge: Pair<EnumFacing, EnumFacing>? = null,
corner: (EnumFacing, EnumFacing, EnumFacing)->Shader) =
fun(quad: Quad, vertex: Vertex): Shader {
val edgeDir = overrideEdge ?: nearestAngle(quad.normal, boxEdges) { it.first.vec + it.second.vec }.first
val nearestFace = nearestPosition(vertex.xyz, edgeDir.toList()) { it.vec }.first
@@ -119,11 +170,11 @@ fun edgeOrientedAuto(overrideEdge: Pair<ForgeDirection, ForgeDirection>? = null,
return corner(nearestFace, nearestCorner.first, nearestCorner.second)
}
fun faceOrientedInterpolate(overrideFace: ForgeDirection? = null) =
fun faceOrientedInterpolate(overrideFace: EnumFacing? = null) =
fun(quad: Quad, vertex: Vertex): Shader {
val resolver = faceOrientedAuto(overrideFace, edge = { face, edgeDir ->
val axis = axes.find { it != face.axis && it != edgeDir.axis }!!
val vec = Double3((axis to Dir.P).face)
val vec = Double3((axis to EnumFacing.AxisDirection.POSITIVE).face)
val pos = vertex.xyz.dot(vec)
EdgeInterpolateFallback(face, edgeDir, pos)
})

View File

@@ -0,0 +1,124 @@
package mods.octarinecore.client.resource
import mods.betterfoliage.client.config.BlockMatcher
import mods.betterfoliage.loader.Refs
import mods.octarinecore.stripStart
import net.minecraft.block.Block
import net.minecraft.block.state.IBlockState
import net.minecraft.client.renderer.block.model.ModelBlock
import net.minecraft.client.renderer.block.statemap.DefaultStateMapper
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.client.resources.model.ModelResourceLocation
import net.minecraft.util.ResourceLocation
import net.minecraftforge.client.event.TextureStitchEvent
import net.minecraftforge.client.model.IModel
import net.minecraftforge.client.model.ModelLoader
import net.minecraftforge.common.MinecraftForge
import net.minecraftforge.fml.common.eventhandler.Event
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
class LoadModelDataEvent(val loader: ModelLoader) : Event()
abstract class ModelDataInspector {
abstract fun onAfterModelLoad()
abstract fun processModelDefinition(state: IBlockState, location: ModelResourceLocation, model: IModel)
abstract fun onStitch(atlas: TextureMap)
init { MinecraftForge.EVENT_BUS.register(this) }
@Suppress("UNCHECKED_CAST")
@SubscribeEvent
fun handleLoadModelData(event: LoadModelDataEvent) {
val stateMappings = Block.blockRegistry.flatMap { block ->
((event.loader.blockModelShapes.blockStateMapper.blockStateMap[block]as? IStateMapper ?: DefaultStateMapper())
.putStateModelLocations(block as Block) as Map<IBlockState, ModelResourceLocation>).entries
}
val stateModels = Refs.stateModels.get(event.loader) as Map<ModelResourceLocation, IModel>
onAfterModelLoad()
stateMappings.forEach { mapping ->
val model = stateModels[mapping.value]
if (model != null) {
val result = processModelDefinition(mapping.key, mapping.value, model)
}
}
}
@SubscribeEvent
fun handleTextureReload(event: TextureStitchEvent.Pre) { onStitch(event.map) }
}
abstract class BlockTextureInspector<T> : ModelDataInspector() {
val state2Names = hashMapOf<IBlockState, Iterable<String>>()
val modelMappings = linkedListOf<Pair<(IBlockState, IModel)->Boolean, Iterable<String>>>()
val infoMap = hashMapOf<IBlockState, T>()
fun match(textureNames: Iterable<String>, predicate: (IBlockState, IModel)->Boolean) =
modelMappings.add(predicate to textureNames)
fun matchClassAndModel(blockClass: BlockMatcher, modelLocation: String, textureNames: Iterable<String>) =
match(textureNames) { state, model -> blockClass.matchesClass(state.block) && model.derivesFromModel(modelLocation) }
operator fun get(state: IBlockState) = infoMap[state]
override fun onAfterModelLoad() {
infoMap.clear()
}
override fun processModelDefinition(state: IBlockState, modelDefLoc: ModelResourceLocation, model: IModel) {
modelMappings.forEach { mapping ->
if (mapping.first(state, model)) {
model.modelBlockAndLoc?.first?.let { modelBlock ->
val textures = mapping.second.map { modelBlock.resolveTextureName(it) }
if (textures.all { it != null && it != "missingno" }) {
state2Names.put(state, textures)
}
}
}
}
}
override fun onStitch(atlas: TextureMap) {
val state2Texture = hashMapOf<IBlockState, List<TextureAtlasSprite>>()
val texture2Info = hashMapOf<List<TextureAtlasSprite>, T>()
state2Names.forEach { state, textureNames ->
val textures = textureNames.map { atlas.getTextureExtry(ResourceLocation(it).toString()) }
if (textures.all { it != null }) {
state2Texture.put(state, textures)
if (textures !in texture2Info) texture2Info.put(textures, processTextures(textures, atlas))
}
}
state2Texture.forEach { state, texture -> infoMap.put(state, texture2Info[texture]!!) }
state2Names.clear()
}
abstract fun processTextures(textures: List<TextureAtlasSprite>, atlas: TextureMap): T
}
@Suppress("UNCHECKED_CAST")
val IModel.modelBlockAndLoc: Pair<ModelBlock, ResourceLocation>? get() {
if (Refs.VanillaModelWrapper.isInstance(this))
return Pair(Refs.model_VMW.get(this) as ModelBlock, Refs.location_VMW.get(this) as ResourceLocation)
else if (Refs.WeightedPartWrapper.isInstance(this)) Refs.model_WPW.get(this)?.let {
return (it as IModel).modelBlockAndLoc
}
else if (Refs.WeightedRandomModel.isInstance(this)) Refs.models_WRM.get(this)?.let {
(it as List<IModel>).forEach {
it.modelBlockAndLoc.let { if (it != null) return it }
}
}
return null
}
fun Pair<ModelBlock, ResourceLocation>.derivesFrom(targetLocation: String): Boolean {
if (second.stripStart("models/") == ResourceLocation(targetLocation)) return true
if (first.parent != null && first.parentLocation != null)
return Pair(first.parent, first.parentLocation).derivesFrom(targetLocation)
return false
}
fun IModel.derivesFromModel(modelLocation: String) = modelBlockAndLoc?.derivesFrom(modelLocation) ?: false

View File

@@ -1,13 +1,12 @@
package mods.octarinecore.client.resource
import cpw.mods.fml.client.FMLClientHandler
import mods.betterfoliage.loader.Refs
import mods.octarinecore.metaprog.reflectField
import net.minecraft.client.resources.IResourcePack
import net.minecraft.client.resources.data.IMetadataSerializer
import net.minecraft.client.resources.data.PackMetadataSection
import net.minecraft.util.ChatComponentText
import net.minecraft.util.ResourceLocation
import net.minecraftforge.fml.client.FMLClientHandler
import java.io.InputStream
import java.util.*
@@ -21,7 +20,6 @@ import java.util.*
class GeneratorPack(val name: String, vararg val generators: GeneratorBase) : IResourcePack {
init {
// add to the default resource packs
FMLClientHandler.instance().reflectField<MutableList<IResourcePack>>("resourcePackList")!!.add(this)
}
@@ -107,16 +105,8 @@ class ParameterList(val params: Map<String, String>, val value: String?) {
}
}
/**
* [GeneratorBase] returning parametrized generated resources.
*
* @param[domain] Resource domain of generator.
*/
abstract class ParameterBasedGenerator(domain: String) : GeneratorBase(domain) {
/** @see [IResourcePack.resourceExists] */
abstract fun resourceExists(params: ParameterList): Boolean
/** @see [IResourcePack.getInputStream] */
abstract fun getInputStream(params: ParameterList): InputStream?
override fun resourceExists(location: ResourceLocation?) =

View File

@@ -1,13 +1,11 @@
package mods.octarinecore.client.resource
import cpw.mods.fml.client.event.ConfigChangedEvent
import cpw.mods.fml.common.FMLCommonHandler
import cpw.mods.fml.common.eventhandler.SubscribeEvent
import mods.octarinecore.client.render.Double3
import mods.octarinecore.client.render.Int3
import mods.octarinecore.client.render.Model
import net.minecraft.client.renderer.texture.IIconRegister
import net.minecraft.util.IIcon
import mods.octarinecore.common.Double3
import mods.octarinecore.common.Int3
import net.minecraft.client.renderer.texture.TextureAtlasSprite
import net.minecraft.client.renderer.texture.TextureMap
import net.minecraft.util.BlockPos
import net.minecraft.util.MathHelper
import net.minecraft.util.ResourceLocation
import net.minecraft.world.World
@@ -15,12 +13,15 @@ import net.minecraft.world.gen.NoiseGeneratorSimplex
import net.minecraftforge.client.event.TextureStitchEvent
import net.minecraftforge.common.MinecraftForge
import net.minecraftforge.event.world.WorldEvent
import net.minecraftforge.fml.client.event.ConfigChangedEvent
import net.minecraftforge.fml.common.FMLCommonHandler
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import java.util.*
// ============================
// Resource types
// ============================
interface IStitchListener { fun onStitch(atlas: IIconRegister) }
interface IStitchListener { fun onStitch(atlas: TextureMap) }
interface IConfigChangeListener { fun onConfigChange() }
interface IWorldLoadListener { fun onWorldLoad(world: World) }
@@ -61,10 +62,8 @@ open class ResourceHandler(val modId: String) {
// ============================
@SubscribeEvent
fun onStitch(event: TextureStitchEvent.Pre) {
if (event.map.textureType == 0) {
resources.forEach { (it as? IStitchListener)?.onStitch(event.map) }
afterStitch()
}
resources.forEach { (it as? IStitchListener)?.onStitch(event.map) }
afterStitch()
}
@SubscribeEvent
@@ -81,8 +80,8 @@ open class ResourceHandler(val modId: String) {
// Resource container classes
// ============================
class IconHolder(val domain: String, val name: String) : IStitchListener {
var icon: IIcon? = null
override fun onStitch(atlas: IIconRegister) { icon = atlas.registerIcon("$domain:$name") }
var icon: TextureAtlasSprite? = null
override fun onStitch(atlas: TextureMap) { icon = atlas.registerSprite(ResourceLocation(domain, name)) }
}
class ModelHolder(val init: Model.()->Unit): IConfigChangeListener {
@@ -91,14 +90,14 @@ class ModelHolder(val init: Model.()->Unit): IConfigChangeListener {
}
class IconSet(val domain: String, val namePattern: String) : IStitchListener {
val icons = arrayOfNulls<IIcon>(16)
val icons = arrayOfNulls<TextureAtlasSprite>(16)
var num = 0
override fun onStitch(atlas: IIconRegister) {
override fun onStitch(atlas: TextureMap) {
num = 0;
(0..15).forEach { idx ->
val locReal = ResourceLocation(domain, "textures/blocks/${namePattern.format(idx)}.png")
if (resourceManager[locReal] != null) icons[num++] = atlas.registerIcon("$domain:${namePattern.format(idx)}")
val locReal = ResourceLocation(domain, "textures/${namePattern.format(idx)}.png")
if (resourceManager[locReal] != null) icons[num++] = atlas.registerSprite(ResourceLocation(domain, namePattern.format(idx)))
}
}
@@ -123,4 +122,5 @@ class SimplexNoise() : IWorldLoadListener {
}
operator fun get(x: Int, z: Int) = MathHelper.floor_double((noise.func_151605_a(x.toDouble(), z.toDouble()) + 1.0) * 32.0)
operator fun get(pos: Int3) = get(pos.x, pos.z)
operator fun get(pos: BlockPos) = get(pos.x, pos.z)
}

View File

@@ -39,9 +39,7 @@ abstract class TextureGenerator(domain: String) : ParameterBasedGenerator(domain
}
)
/**
* Get the type and location of the texture resource encoded by the given [ParameterList].
*/
/** Get the type and location of the texture resource encoded by the given [ParameterList]. */
fun targetResource(params: ParameterList): Pair<ResourceType, ResourceLocation>? {
val baseTexture =
if (listOf("dom", "path").all { it in params }) ResourceLocation(params["dom"]!!, params["path"]!!)

View File

@@ -18,7 +18,8 @@ import java.lang.Math.*
import javax.imageio.ImageIO
/** Concise getter for the Minecraft resource manager. */
val resourceManager: SimpleReloadableResourceManager get() = Minecraft.getMinecraft().resourceManager as SimpleReloadableResourceManager
val resourceManager: SimpleReloadableResourceManager get() =
Minecraft.getMinecraft().resourceManager as SimpleReloadableResourceManager
/** Append a string to the [ResourceLocation]'s path. */
operator fun ResourceLocation.plus(str: String) = ResourceLocation(resourceDomain, resourcePath + str)
@@ -86,5 +87,5 @@ val TextureAtlasSprite.averageColor: Int? get() {
* Get the actual location of a texture from the name of its [TextureAtlasSprite].
*/
fun textureLocation(iconName: String) = ResourceLocation(iconName).let {
ResourceLocation(it.resourceDomain, "textures/blocks/${it.resourcePath}")
ResourceLocation(it.resourceDomain, "textures/${it.resourcePath}")
}

View File

@@ -1,41 +1,52 @@
package mods.octarinecore.client.render
package mods.octarinecore.common
import mods.octarinecore.client.render.Axis.*
import mods.octarinecore.client.render.Dir.N
import mods.octarinecore.client.render.Dir.P
import mods.octarinecore.cross
import net.minecraftforge.common.util.ForgeDirection
import net.minecraftforge.common.util.ForgeDirection.*
import net.minecraft.util.BlockPos
import net.minecraft.util.EnumFacing
import net.minecraft.util.EnumFacing.*
import net.minecraft.util.EnumFacing.Axis.*
import net.minecraft.util.EnumFacing.AxisDirection.*
// ================================
// Axes and directions
// ================================
enum class Axis { X, Y, Z }
enum class Dir { P, N }
val axes = listOf(X, Y, Z)
val axisDirs = listOf(P, N)
val forgeDirs = ForgeDirection.VALID_DIRECTIONS
val axisDirs = listOf(POSITIVE, NEGATIVE)
val EnumFacing.dir: AxisDirection get() = axisDirection
val AxisDirection.sign: String get() = when(this) { POSITIVE -> "+"; NEGATIVE -> "-" }
val forgeDirs = EnumFacing.values()
val forgeDirOffsets = forgeDirs.map { Int3(it) }
val ForgeDirection.axis: Axis get() = when(this) {EAST, WEST -> X; UP, DOWN -> Y; else -> Z }
val ForgeDirection.dir: Dir get() = when(this) {UP, SOUTH, EAST -> P; else -> N }
val Pair<Axis, Dir>.face: ForgeDirection get() = when(this) {
X to P -> EAST; X to N -> WEST; Y to P -> UP; Y to N -> DOWN; Z to P -> SOUTH; Z to N -> NORTH; else -> UNKNOWN
val Pair<Axis, AxisDirection>.face: EnumFacing get() = when(this) {
X to POSITIVE -> EAST; X to NEGATIVE -> WEST;
Y to POSITIVE -> UP; Y to NEGATIVE -> DOWN;
Z to POSITIVE -> SOUTH; else -> NORTH;
}
val ForgeDirection.perpendiculars: List<ForgeDirection> get() =
val EnumFacing.perpendiculars: List<EnumFacing> get() =
axes.filter { it != this.axis }.cross(axisDirs).map { it.face }
val ForgeDirection.offset: Int3 get() = forgeDirOffsets[ordinal]
val EnumFacing.offset: Int3 get() = forgeDirOffsets[ordinal]
/** Old ForgeDirection rotation matrix yanked from 1.7.10 */
val ROTATION_MATRIX: Array<IntArray> get() = arrayOf(
intArrayOf(0, 1, 4, 5, 3, 2, 6),
intArrayOf(0, 1, 5, 4, 2, 3, 6),
intArrayOf(5, 4, 2, 3, 0, 1, 6),
intArrayOf(4, 5, 2, 3, 1, 0, 6),
intArrayOf(2, 3, 1, 0, 4, 5, 6),
intArrayOf(3, 2, 0, 1, 4, 5, 6)
)
// ================================
// Vectors
// ================================
operator fun ForgeDirection.times(scale: Double) =
Double3(offsetX.toDouble() * scale, offsetY.toDouble() * scale, offsetZ.toDouble() * scale)
val ForgeDirection.vec: Double3 get() = Double3(offsetX.toDouble(), offsetY.toDouble(), offsetZ.toDouble())
operator fun EnumFacing.times(scale: Double) =
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())
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. */
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(dir: ForgeDirection) : this(dir.offsetX.toDouble(), dir.offsetY.toDouble(), dir.offsetZ.toDouble())
constructor(dir: EnumFacing) : this(dir.directionVec.x.toDouble(), dir.directionVec.y.toDouble(), dir.directionVec.z.toDouble())
companion object {
val zero: Double3 get() = Double3(0.0, 0.0, 0.0)
fun weight(v1: Double3, weight1: Double, v2: Double3, weight2: Double) =
@@ -79,16 +90,16 @@ 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)
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 nearestCardinal: ForgeDirection get() = nearestAngle(this, forgeDirs.asIterable()) { it.vec }.first
val nearestCardinal: EnumFacing get() = nearestAngle(this, forgeDirs.asIterable()) { it.vec }.first
}
/** 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) {
constructor(dir: ForgeDirection) : this(dir.offsetX, dir.offsetY, dir.offsetZ)
constructor(offset: Pair<Int, ForgeDirection>) : this(
offset.first * offset.second.offsetX,
offset.first * offset.second.offsetY,
offset.first * offset.second.offsetZ
constructor(dir: EnumFacing) : this(dir.directionVec.x, dir.directionVec.y, dir.directionVec.z)
constructor(offset: Pair<Int, EnumFacing>) : this(
offset.first * offset.second.directionVec.x,
offset.first * offset.second.directionVec.y,
offset.first * offset.second.directionVec.z
)
companion object {
val zero = Int3(0, 0, 0)
@@ -96,10 +107,10 @@ data class Int3(var x: Int, var y: Int, var z: Int) {
// immutable operations
operator fun plus(other: Int3) = Int3(x + other.x, y + other.y, z + other.z)
operator fun plus(other: Pair<Int, ForgeDirection>) = Int3(
x + other.first * other.second.offsetX,
y + other.first * other.second.offsetY,
z + other.first * other.second.offsetZ
operator fun plus(other: Pair<Int, EnumFacing>) = Int3(
x + other.first * other.second.directionVec.x,
y + other.first * other.second.directionVec.y,
z + other.first * other.second.directionVec.z
)
operator fun unaryMinus() = Int3(-x, -y, -z)
operator fun minus(other: Int3) = Int3(x - other.x, y - other.y, z - other.z)
@@ -132,17 +143,17 @@ data class Int3(var x: Int, var y: Int, var z: Int) {
// ================================
// Rotation
// ================================
val ForgeDirection.rotations: Array<ForgeDirection> get() =
Array(6) { idx -> ForgeDirection.values()[ForgeDirection.ROTATION_MATRIX[ordinal][idx]] }
fun ForgeDirection.rotate(rot: Rotation) = rot.forward[ordinal]
fun rot(axis: ForgeDirection) = Rotation.rot90[axis.ordinal]
val EnumFacing.rotations: Array<EnumFacing> get() =
Array(6) { idx -> EnumFacing.values()[ROTATION_MATRIX[ordinal][idx]] }
fun EnumFacing.rotate(rot: Rotation) = rot.forward[ordinal]
fun rot(axis: EnumFacing) = Rotation.rot90[axis.ordinal]
/**
* Class representing an arbitrary rotation (or combination of rotations) around cardinal axes by 90 degrees.
* In effect, a permutation of [ForgeDirection]s.
*/
@Suppress("NOTHING_TO_INLINE")
class Rotation(val forward: Array<ForgeDirection>, val reverse: Array<ForgeDirection>) {
class Rotation(val forward: Array<EnumFacing>, val reverse: Array<EnumFacing>) {
operator fun plus(other: Rotation) = Rotation(
Array(6) { idx -> forward[other.forward[idx].ordinal] },
Array(6) { idx -> other.reverse[reverse[idx].ordinal] }
@@ -150,9 +161,9 @@ class Rotation(val forward: Array<ForgeDirection>, val reverse: Array<ForgeDirec
operator fun unaryMinus() = Rotation(reverse, forward)
operator fun times(num: Int) = when(num % 4) { 1 -> this; 2 -> this + this; 3 -> -this; else -> identity }
inline fun rotatedComponent(dir: ForgeDirection, x: Int, y: Int, z: Int) =
inline fun rotatedComponent(dir: EnumFacing, x: Int, y: Int, z: Int) =
when(reverse[dir.ordinal]) { EAST -> x; WEST -> -x; UP -> y; DOWN -> -y; SOUTH -> z; NORTH -> -z; else -> 0 }
inline fun rotatedComponent(dir: ForgeDirection, x: Double, y: Double, z: Double) =
inline fun rotatedComponent(dir: EnumFacing, 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 }
companion object {
@@ -177,7 +188,7 @@ val boxEdges = forgeDirs.flatMap { face1 -> forgeDirs.filter { it.axis > face1.a
* @param[objPos] lambda to calculate the position of an object
* @return [Pair] of (object, distance)
*/
fun <T> nearestPosition(vertex: Double3, objs: Iterable<T>, objPos: (T)->Double3): Pair<T, Double> =
fun <T> nearestPosition(vertex: Double3, objs: Iterable<T>, objPos: (T)-> Double3): Pair<T, Double> =
objs.map { it to (objPos(it) - vertex).length }.minBy { it.second }!!
/**
@@ -188,14 +199,14 @@ fun <T> nearestPosition(vertex: Double3, objs: Iterable<T>, objPos: (T)->Double3
* @param[objAngle] lambda to calculate the orientation of an object
* @return [Pair] of (object, normalized dot product)
*/
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 }!!
data class FaceCorners(val topLeft: Pair<ForgeDirection, ForgeDirection>,
val topRight: Pair<ForgeDirection, ForgeDirection>,
val bottomLeft: Pair<ForgeDirection, ForgeDirection>,
val bottomRight: Pair<ForgeDirection, ForgeDirection>) {
constructor(top: ForgeDirection, left: ForgeDirection) :
data class FaceCorners(val topLeft: Pair<EnumFacing, EnumFacing>,
val topRight: Pair<EnumFacing, EnumFacing>,
val bottomLeft: Pair<EnumFacing, EnumFacing>,
val bottomRight: Pair<EnumFacing, EnumFacing>) {
constructor(top: EnumFacing, left: EnumFacing) :
this(top to left, top to left.opposite, top.opposite to left, top.opposite to left.opposite)
val asArray = arrayOf(topLeft, topRight, bottomLeft, bottomRight)
@@ -208,7 +219,5 @@ val faceCorners = forgeDirs.map { when(it) {
NORTH -> FaceCorners(WEST, UP)
SOUTH -> FaceCorners(UP, WEST)
WEST -> FaceCorners(SOUTH, UP)
EAST ->FaceCorners(SOUTH, DOWN)
else -> FaceCorners(UNKNOWN, UNKNOWN)
EAST -> FaceCorners(SOUTH, DOWN)
}}

View File

@@ -1,17 +1,17 @@
package mods.octarinecore.config
package mods.octarinecore.common.config
import com.google.common.collect.LinkedListMultimap
import cpw.mods.fml.client.config.GuiConfigEntries
import cpw.mods.fml.client.config.IConfigElement
import cpw.mods.fml.client.event.ConfigChangedEvent
import cpw.mods.fml.common.FMLCommonHandler
import cpw.mods.fml.common.eventhandler.SubscribeEvent
import mods.octarinecore.metaprog.reflectField
import mods.octarinecore.metaprog.reflectFieldsOfType
import mods.octarinecore.metaprog.reflectNestedObjects
import net.minecraftforge.common.config.ConfigElement
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
// ============================
@@ -37,7 +37,7 @@ abstract class DelegatingConfig(val modId: String, val langPrefix: String) {
/** The [Configuration] backing this config object. */
var config: Configuration? = null
val rootGuiElements = linkedListOf<IConfigElement<*>>()
val rootGuiElements = linkedListOf<IConfigElement>()
/** Attach this config object to the given [Configuration] and update all properties. */
fun attach(config: Configuration) {
@@ -50,7 +50,7 @@ abstract class DelegatingConfig(val modId: String, val langPrefix: String) {
property.attach(config, langPrefix, category, name)
property.guiProperties.forEach { guiProperty ->
property.guiClass?.let { guiProperty.setConfigEntryClass(it) }
if (category == "global") rootGuiElements.add(ConfigElement.getTypedElement(guiProperty))
if (category == "global") rootGuiElements.add(ConfigElement(guiProperty))
else subProperties.put(category, guiProperty.name)
}
}
@@ -58,7 +58,7 @@ abstract class DelegatingConfig(val modId: String, val langPrefix: String) {
val configCategory = config.getCategory(category)
configCategory.setLanguageKey("$langPrefix.$category")
configCategory.setPropertyOrder(subProperties[category])
rootGuiElements.add(ConfigElement<String>(configCategory))
rootGuiElements.add(ConfigElement(configCategory))
}
save()
}
@@ -118,7 +118,7 @@ abstract class ConfigPropertyBase {
var lang: String? = null
/** GUI class to use. */
var guiClass: Class<out GuiConfigEntries.IConfigEntry<*>>? = null
var guiClass: Class<out GuiConfigEntries.IConfigEntry>? = null
/** @return true if the property has changed. */
abstract val hasChanged: Boolean

View File

@@ -64,6 +64,7 @@ open class ClassRef(val mcpName: String, val obfName: String) : Resolvable<Class
companion object {
val int = ClassRefPrimitive("I", Int::class.java)
val long = ClassRefPrimitive("J", Long::class.java)
val float = ClassRefPrimitive("F", Float::class.java)
val boolean = ClassRefPrimitive("Z", Boolean::class.java)
val void = ClassRefPrimitive("V", null)
@@ -73,6 +74,8 @@ open class ClassRef(val mcpName: String, val obfName: String) : Resolvable<Class
open fun asmDescriptor(namespace: Namespace) = "L${name(namespace).replace(".", "/")};"
override fun resolve() = listOf(mcpName, obfName).map { getJavaClass(it) }.filterNotNull().firstOrNull()
fun isInstance(obj: Any) = element?.isInstance(obj) ?: false
}
/**

View File

@@ -1,8 +1,9 @@
package mods.octarinecore.metaprog
import cpw.mods.fml.relauncher.IFMLLoadingPlugin
import mods.octarinecore.metaprog.Namespace.MCP
import mods.octarinecore.metaprog.Namespace.OBF
import net.minecraft.launchwrapper.IClassTransformer
import mods.octarinecore.metaprog.Namespace.*
import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin
import org.apache.logging.log4j.LogManager
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassWriter
@@ -32,14 +33,14 @@ open class Transformer : IClassTransformer {
var environment: Namespace = MCP
/** The list of transformers and targets. */
var transformers: MutableList<Pair<MethodRef, MethodTransformContext.()->Unit>> = arrayListOf()
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) = transformers.add(method to trans)
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
@@ -48,7 +49,7 @@ open class Transformer : IClassTransformer {
val classNode = ClassNode().apply { val reader = ClassReader(classData); reader.accept(this, 0) }
var workDone = false
val transformations: List<Pair<MethodTransformContext.()->Unit, MethodNode?>> = transformers.map { transformer ->
val transformations: List<Pair<MethodTransformContext.()->Unit, MethodNode?>> = methodTransformers.map { transformer ->
if (transformedName != transformer.first.parentClass.mcpName) return@map transformer.second to null
log.debug("Found class: $name -> $transformedName")
log.debug(" searching: ${transformer.first.name(OBF)} ${transformer.first.asmDescriptor(OBF)} -> ${transformer.first.name(MCP)} ${transformer.first.asmDescriptor(MCP)}")
@@ -72,7 +73,7 @@ open class Transformer : IClassTransformer {
}
}
return if (!workDone) classData else ClassWriter(0).apply { classNode.accept(this) }.toByteArray()
return if (!workDone) classData else ClassWriter(3).apply { classNode.accept(this) }.toByteArray()
}
}
@@ -84,6 +85,11 @@ open class Transformer : IClassTransformer {
* @param[environment] the type of environment we are in
*/
class MethodTransformContext(val method: MethodNode, val environment: Namespace) {
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.
*
@@ -117,9 +123,14 @@ class MethodTransformContext(val method: MethodNode, val environment: Namespace)
* @param[init] builder-style lambda to assemble instruction list
*/
fun AbstractInsnNode.insertBefore(init: InstructionList.()->Unit) = InstructionList(environment).apply{
this.init(); list.forEach { method.instructions.insertBefore(this@insertBefore, it) }
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
@@ -151,6 +162,16 @@ class MethodTransformContext(val method: MethodNode, val environment: Namespace)
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(environment).replace(".", "/")
} ?: false
}
}
/**
@@ -160,6 +181,8 @@ class MethodTransformContext(val method: MethodNode, val environment: Namespace)
*/
class InstructionList(val environment: Namespace) {
fun insn(opcode: Int) = list.add(InsnNode(opcode))
/** The instruction list being assembled. */
val list: MutableList<AbstractInsnNode> = arrayListOf()

View File

@@ -0,0 +1,28 @@
public net.minecraft.client.renderer.BlockModelRenderer func_178261_a(Lnet/minecraft/block/Block;[ILnet/minecraft/util/EnumFacing;[FLjava/util/BitSet;)V
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.BlockModelRenderer$VertexTranslations field_178191_g
public net.minecraft.client.renderer.BlockModelRenderer$VertexTranslations field_178200_h
public net.minecraft.client.renderer.BlockModelRenderer$VertexTranslations field_178201_i
public net.minecraft.client.renderer.BlockModelRenderer$VertexTranslations field_178198_j
public net.minecraft.client.renderer.WorldRenderer field_179008_i # rawBufferIndex
public net.minecraft.client.renderer.WorldRenderer field_179009_s # bufferSize
public net.minecraft.client.renderer.WorldRenderer field_179011_q # vertexFormat
public net.minecraft.client.renderer.WorldRenderer func_178983_e(I)V # growBuffer()
public net.minecraft.client.resources.model.ModelBakery field_177612_i # variants
public net.minecraft.client.resources.model.ModelBakery field_177611_h # models
public net.minecraft.client.resources.model.ModelBakery field_177609_j # textureMap
public net.minecraft.client.resources.model.ModelBakery field_177610_k # blockModelShapes
public net.minecraftforge.client.model.ModelLoader stateModels
public net.minecraft.client.resources.model.WeightedBakedModel field_177565_b # models
public net.minecraft.client.renderer.block.statemap.BlockStateMapper field_178450_a # blockStateMap
public net.minecraft.client.resources.SimpleReloadableResourceManager field_110548_a # domainResourceManagers

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 377 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 414 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 491 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 610 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 859 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 519 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 590 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 434 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 505 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 464 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 369 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 379 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 345 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 739 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 711 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 571 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 501 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 507 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 490 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 483 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 453 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 756 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB