Files
BetterFoliage/src/main/kotlin/mods/betterfoliage/client/render/column/AbstractRenderer.kt
octarine-noise 4efa831296 [WIP] more async texture loading
keep stitch() call in original method body, in case others want to mod it too
2020-01-06 18:15:17 +01:00

211 lines
9.6 KiB
Kotlin

package mods.betterfoliage.client.render.column
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.client.Client
import mods.betterfoliage.client.chunk.ChunkOverlayManager
import mods.betterfoliage.client.integration.ShadersModIntegration.renderAs
import mods.betterfoliage.client.render.*
import mods.betterfoliage.client.render.column.ColumnLayerData.SpecialRender.BlockType.*
import mods.betterfoliage.client.render.column.ColumnLayerData.SpecialRender.QuadrantType
import mods.betterfoliage.client.render.column.ColumnLayerData.SpecialRender.QuadrantType.*
import mods.octarinecore.client.render.CombinedContext
import mods.octarinecore.client.render.Model
import mods.octarinecore.client.render.RenderDecorator
import mods.octarinecore.client.render.noPost
import mods.octarinecore.common.Rotation
import mods.octarinecore.common.face
import mods.octarinecore.common.rot
import net.minecraft.block.BlockRenderType.MODEL
import net.minecraft.util.Direction.*
import net.minecraftforge.eventbus.api.IEventBus
@Suppress("NOTHING_TO_INLINE")
abstract class AbstractRenderColumn(modId: String, modBus: IEventBus) : RenderDecorator(modId, modBus) {
/** The rotations necessary to bring the models in position for the 4 quadrants */
val quadrantRotations = Array(4) { Rotation.rot90[UP.ordinal] * it }
// ============================
// Configuration
// ============================
abstract val overlayLayer: ColumnRenderLayer
abstract val connectPerpendicular: Boolean
abstract val radiusSmall: Double
abstract val radiusLarge: Double
// ============================
// Models
// ============================
val sideSquare = model { columnSideSquare(-0.5, 0.5) }
val sideRoundSmall = model { columnSide(radiusSmall, -0.5, 0.5) }
val sideRoundLarge = model { columnSide(radiusLarge, -0.5, 0.5) }
val extendTopSquare = model { columnSideSquare(0.5, 0.5 + radiusLarge, topExtension(radiusLarge)) }
val extendTopRoundSmall = model { columnSide(radiusSmall, 0.5, 0.5 + radiusLarge, topExtension(radiusLarge)) }
val extendTopRoundLarge = model { columnSide(radiusLarge, 0.5, 0.5 + radiusLarge, topExtension(radiusLarge)) }
inline fun extendTop(type: QuadrantType) = when(type) {
SMALL_RADIUS -> extendTopRoundSmall.model
LARGE_RADIUS -> extendTopRoundLarge.model
SQUARE -> extendTopSquare.model
INVISIBLE -> extendTopSquare.model
else -> null
}
val extendBottomSquare = model { columnSideSquare(-0.5 - radiusLarge, -0.5, bottomExtension(radiusLarge)) }
val extendBottomRoundSmall = model { columnSide(radiusSmall, -0.5 - radiusLarge, -0.5, bottomExtension(radiusLarge)) }
val extendBottomRoundLarge = model { columnSide(radiusLarge, -0.5 - radiusLarge, -0.5, bottomExtension(radiusLarge)) }
inline fun extendBottom(type: QuadrantType) = when (type) {
SMALL_RADIUS -> extendBottomRoundSmall.model
LARGE_RADIUS -> extendBottomRoundLarge.model
SQUARE -> extendBottomSquare.model
INVISIBLE -> extendBottomSquare.model
else -> null
}
val topSquare = model { columnLidSquare() }
val topRoundSmall = model { columnLid(radiusSmall) }
val topRoundLarge = model { columnLid(radiusLarge) }
inline fun flatTop(type: QuadrantType) = when(type) {
SMALL_RADIUS -> topRoundSmall.model
LARGE_RADIUS -> topRoundLarge.model
SQUARE -> topSquare.model
INVISIBLE -> topSquare.model
else -> null
}
val bottomSquare = model { columnLidSquare() { it.rotate(rot(EAST) * 2 + rot(UP)).mirrorUV(true, true) } }
val bottomRoundSmall = model { columnLid(radiusSmall) { it.rotate(rot(EAST) * 2 + rot(UP)).mirrorUV(true, true) } }
val bottomRoundLarge = model { columnLid(radiusLarge) { it.rotate(rot(EAST) * 2 + rot(UP)).mirrorUV(true, true) } }
inline fun flatBottom(type: QuadrantType) = when(type) {
SMALL_RADIUS -> bottomRoundSmall.model
LARGE_RADIUS -> bottomRoundLarge.model
SQUARE -> bottomSquare.model
INVISIBLE -> bottomSquare.model
else -> null
}
val transitionTop = model { mix(sideRoundLarge.model, sideRoundSmall.model) { it > 1 } }
val transitionBottom = model { mix(sideRoundSmall.model, sideRoundLarge.model) { it > 1 } }
inline fun continuous(q1: QuadrantType, q2: QuadrantType) =
q1 == q2 || ((q1 == SQUARE || q1 == INVISIBLE) && (q2 == SQUARE || q2 == INVISIBLE))
@Suppress("NON_EXHAUSTIVE_WHEN")
override fun render(ctx: CombinedContext) {
val roundLog = ChunkOverlayManager.get(overlayLayer, ctx)
when(roundLog) {
ColumnLayerData.SkipRender -> return
ColumnLayerData.NormalRender -> return ctx.render()
ColumnLayerData.ResolveError, null -> {
BetterFoliage.logRenderError(ctx.state, ctx.pos)
return ctx.render()
}
}
// if log axis is not defined and "Default to vertical" config option is not set, render normally
if ((roundLog as ColumnLayerData.SpecialRender).column.axis == null && !overlayLayer.defaultToY) {
return ctx.render()
}
val baseRotation = rotationFromUp[((roundLog.column.axis ?: Axis.Y) to AxisDirection.POSITIVE).face.ordinal]
renderAs(ctx, MODEL) {
quadrantRotations.forEachIndexed { idx, quadrantRotation ->
// set rotation for the current quadrant
val rotation = baseRotation + quadrantRotation
// disallow sharp discontinuities in the chamfer radius, or tapering-in where inappropriate
if (roundLog.quadrants[idx] == LARGE_RADIUS &&
roundLog.upType == PARALLEL && roundLog.quadrantsTop[idx] != LARGE_RADIUS &&
roundLog.downType == PARALLEL && roundLog.quadrantsBottom[idx] != LARGE_RADIUS) {
roundLog.quadrants[idx] = SMALL_RADIUS
}
// render side of current quadrant
val sideModel = when (roundLog.quadrants[idx]) {
SMALL_RADIUS -> sideRoundSmall.model
LARGE_RADIUS -> if (roundLog.upType == PARALLEL && roundLog.quadrantsTop[idx] == SMALL_RADIUS) transitionTop.model
else if (roundLog.downType == PARALLEL && roundLog.quadrantsBottom[idx] == SMALL_RADIUS) transitionBottom.model
else sideRoundLarge.model
SQUARE -> sideSquare.model
else -> null
}
if (sideModel != null) ctx.render(
sideModel,
rotation,
icon = roundLog.column.side,
postProcess = noPost
)
// render top and bottom end of current quadrant
var upModel: Model? = null
var downModel: Model? = null
var upIcon = roundLog.column.top
var downIcon = roundLog.column.bottom
var isLidUp = true
var isLidDown = true
when (roundLog.upType) {
NONSOLID -> upModel = flatTop(roundLog.quadrants[idx])
PERPENDICULAR -> {
if (!connectPerpendicular) {
upModel = flatTop(roundLog.quadrants[idx])
} else {
upIcon = roundLog.column.side
upModel = extendTop(roundLog.quadrants[idx])
isLidUp = false
}
}
PARALLEL -> {
if (!continuous(roundLog.quadrants[idx], roundLog.quadrantsTop[idx])) {
if (roundLog.quadrants[idx] == SQUARE || roundLog.quadrants[idx] == INVISIBLE) {
upModel = topSquare.model
}
}
}
}
when (roundLog.downType) {
NONSOLID -> downModel = flatBottom(roundLog.quadrants[idx])
PERPENDICULAR -> {
if (!connectPerpendicular) {
downModel = flatBottom(roundLog.quadrants[idx])
} else {
downIcon = roundLog.column.side
downModel = extendBottom(roundLog.quadrants[idx])
isLidDown = false
}
}
PARALLEL -> {
if (!continuous(roundLog.quadrants[idx], roundLog.quadrantsBottom[idx]) &&
(roundLog.quadrants[idx] == SQUARE || roundLog.quadrants[idx] == INVISIBLE)) {
downModel = bottomSquare.model
}
}
}
if (upModel != null) ctx.render(
upModel,
rotation,
icon = upIcon,
postProcess = { _, _, _, _, _ ->
if (isLidUp) {
rotateUV(idx + if (roundLog.column.axis == Axis.X) 1 else 0)
}
}
)
if (downModel != null) ctx.render(
downModel,
rotation,
icon = downIcon,
postProcess = { _, _, _, _, _ ->
if (isLidDown) {
rotateUV((if (roundLog.column.axis == Axis.X) 0 else 3) - idx)
}
}
)
}
}
}
}