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

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