[WIP] initial Fabric port
major package refactoring
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
package mods.betterfoliage.render.column
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.*
|
||||
import mods.betterfoliage.resource.model.*
|
||||
import mods.betterfoliage.util.Double3
|
||||
import mods.betterfoliage.util.Rotation
|
||||
import net.minecraft.block.BlockRenderLayer.SOLID
|
||||
import net.minecraft.client.texture.Sprite
|
||||
import net.minecraft.util.math.Direction.*
|
||||
|
||||
/**
|
||||
* Collection of dynamically generated meshes used to render rounded columns.
|
||||
*/
|
||||
class ColumnMeshSet(
|
||||
radiusSmall: Double,
|
||||
radiusLarge: Double,
|
||||
zProtection: Double,
|
||||
val axis: Axis,
|
||||
val spriteLeft: Sprite,
|
||||
val spriteRight: Sprite,
|
||||
val spriteTop: Sprite,
|
||||
val spriteBottom: Sprite
|
||||
) {
|
||||
protected fun sideRounded(radius: Double, yBottom: Double, yTop: Double): List<Quad> {
|
||||
val halfRadius = radius * 0.5
|
||||
return listOf(
|
||||
// left side of the diagonal
|
||||
verticalRectangle(0.0, 0.5, 0.5 - radius, 0.5, yBottom, yTop).clampUV(minU = 0.0, maxU = 0.5 - radius),
|
||||
verticalRectangle(0.5 - radius, 0.5, 0.5 - halfRadius, 0.5 - halfRadius, yBottom, yTop).clampUV(minU = 0.5 - radius),
|
||||
// right side of the diagonal
|
||||
verticalRectangle(0.5 - halfRadius, 0.5 - halfRadius, 0.5, 0.5 - radius, yBottom, yTop).clampUV(maxU = radius - 0.5),
|
||||
verticalRectangle(0.5, 0.5 - radius, 0.5, 0.0, yBottom, yTop).clampUV(minU = radius - 0.5, maxU = 0.0)
|
||||
)
|
||||
}
|
||||
|
||||
protected fun sideRoundedTransition(radiusBottom: Double, radiusTop: Double, yBottom: Double, yTop: Double): List<Quad> {
|
||||
val ySplit = 0.5 * (yBottom + yTop)
|
||||
val modelTop = sideRounded(radiusTop, yBottom, yTop)
|
||||
val modelBottom = sideRounded(radiusBottom, yBottom, yTop)
|
||||
return (modelBottom zip modelTop).map { (quadBottom, quadTop) ->
|
||||
Quad.mix(quadBottom, quadTop) { vBottom, vTop -> if (vBottom.xyz.y < ySplit) vBottom.copy() else vTop.copy() }
|
||||
}
|
||||
}
|
||||
|
||||
protected fun sideSquare(yBottom: Double, yTop: Double) = listOf(
|
||||
verticalRectangle(0.0, 0.5, 0.5, 0.5, yBottom, yTop).clampUV(minU = 0.0),
|
||||
verticalRectangle(0.5, 0.5, 0.5, 0.0, yBottom, yTop).clampUV(maxU = 0.0)
|
||||
)
|
||||
|
||||
protected fun lidRounded(radius: Double, y: Double, isBottom: Boolean) = Array(4) { quadrant ->
|
||||
val rotation = baseRotation(axis) + quadrantRotations[quadrant]
|
||||
val v1 = Vertex(Double3(0.0, y, 0.0), UV(0.0, 0.0))
|
||||
val v2 = Vertex(Double3(0.0, y, 0.5), UV(0.0, 0.5))
|
||||
val v3 = Vertex(Double3(0.5 - radius, y, 0.5), UV(0.5 - radius, 0.5))
|
||||
val v4 = Vertex(Double3(0.5 - radius * 0.5, y, 0.5 - radius * 0.5), UV(0.5, 0.5))
|
||||
val v5 = Vertex(Double3(0.5, y, 0.5 - radius), UV(0.5, 0.5 - radius))
|
||||
val v6 = Vertex(Double3(0.5, y, 0.0), UV(0.5, 0.0))
|
||||
listOf(Quad(v1, v2, v3, v4), Quad(v1, v4, v5, v6))
|
||||
.map { it.cycleVertices(if (isBottom xor BetterFoliage.config.nVidia) 0 else 1) }
|
||||
.map { it.rotate(rotation).rotateUV(quadrant) }
|
||||
.map { it.sprite(if (isBottom) spriteBottom else spriteTop).colorAndIndex(Color.white.asInt) }
|
||||
.map { if (isBottom) it.flipped else it }
|
||||
}
|
||||
|
||||
protected fun lidSquare(y: Double, isBottom: Boolean) = Array(4) { quadrant ->
|
||||
val rotation = baseRotation(axis) + quadrantRotations[quadrant]
|
||||
listOf(
|
||||
horizontalRectangle(x1 = 0.0, x2 = 0.5, z1 = 0.0, z2 = 0.5, y = y).clampUV(minU = 0.0, minV = 0.0)
|
||||
.rotate(rotation).rotateUV(quadrant)
|
||||
.sprite(if (isBottom) spriteBottom else spriteTop).colorAndIndex(Color.white.asInt)
|
||||
.let { if (isBottom) it.flipped else it }
|
||||
)
|
||||
}
|
||||
|
||||
protected val zProtectionScale = zProtection.let { Double3(it, 1.0, it) }
|
||||
|
||||
protected fun List<Quad>.extendTop(size: Double) = map { q -> q.clampUV(minV = 0.5 - size).transformV { v ->
|
||||
if (v.xyz.y > 0.501) v.copy(xyz = v.xyz * zProtectionScale) else v }
|
||||
}
|
||||
protected fun List<Quad>.extendBottom(size: Double) = map { q -> q.clampUV(maxV = -0.5 + size).transformV { v ->
|
||||
if (v.xyz.y < -0.501) v.copy(xyz = v.xyz * zProtectionScale) else v }
|
||||
}
|
||||
protected fun List<Quad>.buildSides(quadsPerSprite: Int) = Array(4) { quadrant ->
|
||||
val rotation = baseRotation(axis) + quadrantRotations[quadrant]
|
||||
this.map { it.rotate(rotation).colorAndIndex(Color.white.asInt) }
|
||||
.mapIndexed { idx, q -> if (idx % (2 * quadsPerSprite) >= quadsPerSprite) q.sprite(spriteRight) else q.sprite(spriteLeft) }
|
||||
.build(SOLID, flatLighting = false)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun baseRotation(axis: Axis) = when(axis) {
|
||||
Axis.X -> Rotation.fromUp[EAST.ordinal]
|
||||
Axis.Y -> Rotation.fromUp[UP.ordinal]
|
||||
Axis.Z -> Rotation.fromUp[SOUTH.ordinal]
|
||||
}
|
||||
val quadrantRotations = Array(4) { Rotation.rot90[UP.ordinal] * it }
|
||||
}
|
||||
|
||||
//
|
||||
// Mesh definitions
|
||||
// 4-element arrays hold prebuild meshes for each of the rotations around the axis
|
||||
//
|
||||
val sideSquare = sideSquare(-0.5, 0.5).buildSides(quadsPerSprite = 1)
|
||||
val sideRoundSmall = sideRounded(radiusSmall, -0.5, 0.5).buildSides(quadsPerSprite = 2)
|
||||
val sideRoundLarge = sideRounded(radiusLarge, -0.5, 0.5).buildSides(quadsPerSprite = 2)
|
||||
|
||||
val sideExtendTopSquare = sideSquare(0.5, 0.5 + radiusLarge).extendTop(radiusLarge).buildSides(quadsPerSprite = 1)
|
||||
val sideExtendTopRoundSmall = sideRounded(radiusSmall, 0.5, 0.5 + radiusLarge).extendTop(radiusLarge).buildSides(quadsPerSprite = 2)
|
||||
val sideExtendTopRoundLarge = sideRounded(radiusLarge, 0.5, 0.5 + radiusLarge).extendTop(radiusLarge).buildSides(quadsPerSprite = 2)
|
||||
|
||||
val sideExtendBottomSquare = sideSquare(-0.5 - radiusLarge, -0.5).extendBottom(radiusLarge).buildSides(quadsPerSprite = 1)
|
||||
val sideExtendBottomRoundSmall = sideRounded(radiusSmall, -0.5 - radiusLarge, -0.5).extendBottom(radiusLarge).buildSides(quadsPerSprite = 2)
|
||||
val sideExtendBottomRoundLarge = sideRounded(radiusLarge, -0.5 - radiusLarge, -0.5).extendBottom(radiusLarge).buildSides(quadsPerSprite = 2)
|
||||
|
||||
val lidTopSquare = lidSquare(0.5, false).build(SOLID, flatLighting = false)
|
||||
val lidTopRoundSmall = lidRounded(radiusSmall, 0.5, false).build(SOLID, flatLighting = false)
|
||||
val lidTopRoundLarge = lidRounded(radiusLarge, 0.5, false).build(SOLID, flatLighting = false)
|
||||
|
||||
val lidBottomSquare = lidSquare(-0.5, true).build(SOLID, flatLighting = false)
|
||||
val lidBottomRoundSmall = lidRounded(radiusSmall, -0.5, true).build(SOLID, flatLighting = false)
|
||||
val lidBottomRoundLarge = lidRounded(radiusLarge, -0.5, true).build(SOLID, flatLighting = false)
|
||||
|
||||
val transitionTop = sideRoundedTransition(radiusLarge, radiusSmall, -0.5, 0.5).buildSides(quadsPerSprite = 2)
|
||||
val transitionBottom = sideRoundedTransition(radiusSmall, radiusLarge, -0.5, 0.5).buildSides(quadsPerSprite = 2)
|
||||
|
||||
//
|
||||
// Helper fuctions for lids (block ends)
|
||||
//
|
||||
fun flatTop(quadrantTypes: Array<QuadrantType>, quadrant: Int) = when(quadrantTypes[quadrant]) {
|
||||
SMALL_RADIUS -> lidTopRoundSmall[quadrant]
|
||||
LARGE_RADIUS -> lidTopRoundLarge[quadrant]
|
||||
SQUARE -> lidTopSquare[quadrant]
|
||||
INVISIBLE -> lidTopSquare[quadrant]
|
||||
}
|
||||
|
||||
fun flatBottom(quadrantTypes: Array<QuadrantType>, quadrant: Int) = when(quadrantTypes[quadrant]) {
|
||||
SMALL_RADIUS -> lidBottomRoundSmall[quadrant]
|
||||
LARGE_RADIUS -> lidBottomRoundLarge[quadrant]
|
||||
SQUARE -> lidBottomSquare[quadrant]
|
||||
INVISIBLE -> lidBottomSquare[quadrant]
|
||||
}
|
||||
|
||||
fun extendTop(quadrantTypes: Array<QuadrantType>, quadrant: Int) = when(quadrantTypes[quadrant]) {
|
||||
SMALL_RADIUS -> sideExtendTopRoundSmall[quadrant]
|
||||
LARGE_RADIUS -> sideExtendTopRoundLarge[quadrant]
|
||||
SQUARE -> sideExtendTopSquare[quadrant]
|
||||
INVISIBLE -> sideExtendTopSquare[quadrant]
|
||||
}
|
||||
|
||||
fun extendBottom(quadrantTypes: Array<QuadrantType>, quadrant: Int) = when(quadrantTypes[quadrant]) {
|
||||
SMALL_RADIUS -> sideExtendBottomRoundSmall[quadrant]
|
||||
LARGE_RADIUS -> sideExtendBottomRoundLarge[quadrant]
|
||||
SQUARE -> sideExtendBottomSquare[quadrant]
|
||||
INVISIBLE -> sideExtendBottomSquare[quadrant]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package mods.betterfoliage.render.column
|
||||
|
||||
import mods.betterfoliage.chunk.CachedBlockCtx
|
||||
import mods.betterfoliage.chunk.ChunkOverlayManager
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.NormalRender
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.BlockType.*
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.*
|
||||
import mods.betterfoliage.resource.model.WrappedBakedModel
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.render.model.BakedModel
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction.Axis
|
||||
import net.minecraft.world.ExtendedBlockView
|
||||
import java.util.*
|
||||
import java.util.function.Supplier
|
||||
|
||||
abstract class ColumnModelBase(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
|
||||
abstract val enabled: Boolean
|
||||
abstract val overlayLayer: ColumnRenderLayer
|
||||
abstract val connectPerpendicular: Boolean
|
||||
abstract fun getMeshSet(axis: Axis, quadrant: Int): ColumnMeshSet
|
||||
|
||||
override fun emitBlockQuads(blockView: ExtendedBlockView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
|
||||
val ctx = CachedBlockCtx(blockView, pos)
|
||||
val roundLog = ChunkOverlayManager.get(overlayLayer, ctx)
|
||||
|
||||
when(roundLog) {
|
||||
ColumnLayerData.SkipRender -> return
|
||||
NormalRender -> return super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
||||
ColumnLayerData.ResolveError, null -> {
|
||||
return super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
||||
}
|
||||
}
|
||||
|
||||
// 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 super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
|
||||
}
|
||||
|
||||
val axis = roundLog.column.axis ?: Axis.Y
|
||||
val baseRotation = ColumnMeshSet.baseRotation(axis)
|
||||
ColumnMeshSet.quadrantRotations.forEachIndexed { idx, quadrantRotation ->
|
||||
// set rotation for the current quadrant
|
||||
val rotation = baseRotation + quadrantRotation
|
||||
val meshSet = getMeshSet(axis, idx)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// select meshes for current quadrant based on connectivity rules
|
||||
val sideMesh = when (roundLog.quadrants[idx]) {
|
||||
SMALL_RADIUS -> meshSet.sideRoundSmall[idx]
|
||||
LARGE_RADIUS -> if (roundLog.upType == PARALLEL && roundLog.quadrantsTop[idx] == SMALL_RADIUS) meshSet.transitionTop[idx]
|
||||
else if (roundLog.downType == PARALLEL && roundLog.quadrantsBottom[idx] == SMALL_RADIUS) meshSet.transitionBottom[idx]
|
||||
else meshSet.sideRoundLarge[idx]
|
||||
SQUARE -> meshSet.sideSquare[idx]
|
||||
else -> null
|
||||
}
|
||||
|
||||
val upMesh = when(roundLog.upType) {
|
||||
NONSOLID -> meshSet.flatTop(roundLog.quadrants, idx)
|
||||
PERPENDICULAR -> {
|
||||
if (!connectPerpendicular) {
|
||||
meshSet.flatTop(roundLog.quadrants, idx)
|
||||
} else {
|
||||
meshSet.extendTop(roundLog.quadrants, idx)
|
||||
}
|
||||
}
|
||||
PARALLEL -> {
|
||||
if (roundLog.quadrants[idx] discontinuousWith roundLog.quadrantsTop[idx] &&
|
||||
roundLog.quadrants[idx].let { it == SQUARE || it == INVISIBLE } )
|
||||
meshSet.flatTop(roundLog.quadrants, idx)
|
||||
else null
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
||||
val downMesh = when(roundLog.downType) {
|
||||
NONSOLID -> meshSet.flatBottom(roundLog.quadrants, idx)
|
||||
PERPENDICULAR -> {
|
||||
if (!connectPerpendicular) {
|
||||
meshSet.flatBottom(roundLog.quadrants, idx)
|
||||
} else {
|
||||
meshSet.extendBottom(roundLog.quadrants, idx)
|
||||
}
|
||||
}
|
||||
PARALLEL -> {
|
||||
if (roundLog.quadrants[idx] discontinuousWith roundLog.quadrantsBottom[idx] &&
|
||||
roundLog.quadrants[idx].let { it == SQUARE || it == INVISIBLE } )
|
||||
meshSet.flatBottom(roundLog.quadrants, idx)
|
||||
else null
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
||||
// render
|
||||
sideMesh?.let { context.meshConsumer().accept(it) }
|
||||
upMesh?.let { context.meshConsumer().accept(it) }
|
||||
downMesh?.let { context.meshConsumer().accept(it) }
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
package mods.betterfoliage.render.column
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.chunk.ChunkOverlayLayer
|
||||
import mods.betterfoliage.chunk.ChunkOverlayManager
|
||||
import mods.betterfoliage.chunk.dimType
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.BlockType.*
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.*
|
||||
import mods.betterfoliage.chunk.BlockCtx
|
||||
import mods.betterfoliage.util.*
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction.Axis
|
||||
import net.minecraft.util.math.Direction.AxisDirection
|
||||
import net.minecraft.world.ExtendedBlockView
|
||||
|
||||
/** Index of SOUTH-EAST quadrant. */
|
||||
const val SE = 0
|
||||
/** Index of NORTH-EAST quadrant. */
|
||||
const val NE = 1
|
||||
/** Index of NORTH-WEST quadrant. */
|
||||
const val NW = 2
|
||||
/** Index of SOUTH-WEST quadrant. */
|
||||
const val SW = 3
|
||||
|
||||
interface ColumnBlockKey {
|
||||
val axis: Axis?
|
||||
}
|
||||
|
||||
/**
|
||||
* Sealed class hierarchy for all possible render outcomes
|
||||
*/
|
||||
sealed class ColumnLayerData {
|
||||
/**
|
||||
* Data structure to cache texture and world neighborhood data relevant to column rendering
|
||||
*/
|
||||
@Suppress("ArrayInDataClass") // not used in comparisons anywhere
|
||||
data class SpecialRender(
|
||||
val column: ColumnBlockKey,
|
||||
val upType: BlockType,
|
||||
val downType: BlockType,
|
||||
val quadrants: Array<QuadrantType>,
|
||||
val quadrantsTop: Array<QuadrantType>,
|
||||
val quadrantsBottom: Array<QuadrantType>
|
||||
) : ColumnLayerData() {
|
||||
enum class BlockType { SOLID, NONSOLID, PARALLEL, PERPENDICULAR }
|
||||
enum class QuadrantType {
|
||||
SMALL_RADIUS, LARGE_RADIUS, SQUARE, INVISIBLE;
|
||||
infix fun continuousWith(other: QuadrantType) =
|
||||
this == other || ((this == SQUARE || this == INVISIBLE) && (other == SQUARE || other == INVISIBLE))
|
||||
infix fun discontinuousWith(other: QuadrantType) = !continuousWith(other)
|
||||
}
|
||||
}
|
||||
|
||||
/** Column block should not be rendered at all */
|
||||
object SkipRender : ColumnLayerData()
|
||||
|
||||
/** Column block must be rendered normally */
|
||||
object NormalRender : ColumnLayerData()
|
||||
|
||||
/** Error while resolving render data, column block must be rendered normally */
|
||||
object ResolveError : ColumnLayerData()
|
||||
}
|
||||
|
||||
abstract class ColumnRenderLayer : ChunkOverlayLayer<ColumnLayerData> {
|
||||
|
||||
abstract val connectSolids: Boolean
|
||||
abstract val lenientConnect: Boolean
|
||||
abstract val defaultToY: Boolean
|
||||
|
||||
abstract fun getColumnKey(state: BlockState): ColumnBlockKey?
|
||||
|
||||
val allNeighborOffsets = (-1..1).flatMap { offsetX -> (-1..1).flatMap { offsetY -> (-1..1).map { offsetZ -> Int3(offsetX, offsetY, offsetZ) }}}
|
||||
|
||||
override fun onBlockUpdate(world: ExtendedBlockView, pos: BlockPos) {
|
||||
allNeighborOffsets.forEach { offset -> ChunkOverlayManager.clear(world.dimType, this, pos + offset) }
|
||||
}
|
||||
|
||||
override fun calculate(ctx: BlockCtx): ColumnLayerData {
|
||||
if (allDirections.all { ctx.offset(it).isNormalCube }) return ColumnLayerData.SkipRender
|
||||
// val columnTextures = registry[ctx] ?: return ColumnLayerData.ResolveError
|
||||
val columnTextures = getColumnKey(ctx.state) ?: return ColumnLayerData.ResolveError
|
||||
|
||||
// if log axis is not defined and "Default to vertical" config option is not set, render normally
|
||||
val logAxis = columnTextures.axis ?: if (defaultToY) Axis.Y else return ColumnLayerData.NormalRender
|
||||
|
||||
// check log neighborhood
|
||||
val baseRotation = Rotation.fromUp[(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))
|
||||
|
||||
val quadrants = Array(4) { SMALL_RADIUS }.checkNeighbors(ctx, baseRotation, logAxis, 0)
|
||||
val quadrantsTop = Array(4) { SMALL_RADIUS }
|
||||
if (upType == PARALLEL) quadrantsTop.checkNeighbors(ctx, baseRotation, logAxis, 1)
|
||||
val quadrantsBottom = Array(4) { SMALL_RADIUS }
|
||||
if (downType == PARALLEL) quadrantsBottom.checkNeighbors(ctx, baseRotation, logAxis, -1)
|
||||
return ColumnLayerData.SpecialRender(columnTextures, upType, downType, quadrants, quadrantsTop, quadrantsBottom)
|
||||
}
|
||||
|
||||
/** Sets the type of the given quadrant only if the new value is "stronger" (larger ordinal). */
|
||||
inline fun Array<QuadrantType>.upgrade(idx: Int, value: QuadrantType) {
|
||||
if (this[idx].ordinal < value.ordinal) this[idx] = value
|
||||
}
|
||||
|
||||
/** Fill the array of [QuadrantType]s based on the blocks to the sides of this one. */
|
||||
fun Array<QuadrantType>.checkNeighbors(ctx: BlockCtx, rotation: Rotation, logAxis: Axis, yOff: Int): Array<QuadrantType> {
|
||||
val blkS = ctx.blockType(rotation, logAxis, Int3(0, yOff, 1))
|
||||
val blkE = ctx.blockType(rotation, logAxis, Int3(1, yOff, 0))
|
||||
val blkN = ctx.blockType(rotation, logAxis, Int3(0, yOff, -1))
|
||||
val blkW = ctx.blockType(rotation, logAxis, Int3(-1, yOff, 0))
|
||||
|
||||
// a solid block on one side will make the 2 neighboring quadrants SQUARE
|
||||
// if there are solid blocks to both sides of a quadrant, it is INVISIBLE
|
||||
if (connectSolids) {
|
||||
if (blkS == SOLID) {
|
||||
upgrade(SW, SQUARE); upgrade(SE, SQUARE)
|
||||
}
|
||||
if (blkE == SOLID) {
|
||||
upgrade(SE, SQUARE); upgrade(NE, SQUARE)
|
||||
}
|
||||
if (blkN == SOLID) {
|
||||
upgrade(NE, SQUARE); upgrade(NW, SQUARE)
|
||||
}
|
||||
if (blkW == SOLID) {
|
||||
upgrade(NW, SQUARE); upgrade(SW, SQUARE)
|
||||
}
|
||||
if (blkS == SOLID && blkE == SOLID) upgrade(SE, INVISIBLE)
|
||||
if (blkN == SOLID && blkE == SOLID) upgrade(NE, INVISIBLE)
|
||||
if (blkN == SOLID && blkW == SOLID) upgrade(NW, INVISIBLE)
|
||||
if (blkS == SOLID && blkW == SOLID) upgrade(SW, INVISIBLE)
|
||||
}
|
||||
val blkSE = ctx.blockType(rotation, logAxis, Int3(1, yOff, 1))
|
||||
val blkNE = ctx.blockType(rotation, logAxis, Int3(1, yOff, -1))
|
||||
val blkNW = ctx.blockType(rotation, logAxis, Int3(-1, yOff, -1))
|
||||
val blkSW = ctx.blockType(rotation, logAxis, Int3(-1, yOff, 1))
|
||||
|
||||
if (lenientConnect) {
|
||||
// if the block forms the tip of an L-shape, connect to its neighbor with SQUARE quadrants
|
||||
if (blkE == PARALLEL && (blkSE == PARALLEL || blkNE == PARALLEL)) {
|
||||
upgrade(SE, SQUARE); upgrade(NE, SQUARE)
|
||||
}
|
||||
if (blkN == PARALLEL && (blkNE == PARALLEL || blkNW == PARALLEL)) {
|
||||
upgrade(NE, SQUARE); upgrade(NW, SQUARE)
|
||||
}
|
||||
if (blkW == PARALLEL && (blkNW == PARALLEL || blkSW == PARALLEL)) {
|
||||
upgrade(NW, SQUARE); upgrade(SW, SQUARE)
|
||||
}
|
||||
if (blkS == PARALLEL && (blkSE == PARALLEL || blkSW == PARALLEL)) {
|
||||
upgrade(SW, SQUARE); upgrade(SE, SQUARE)
|
||||
}
|
||||
}
|
||||
|
||||
// if the block forms the middle of an L-shape, or is part of a 2x2 configuration,
|
||||
// connect to its neighbors with SQUARE quadrants, INVISIBLE on the inner corner, and LARGE_RADIUS on the outer corner
|
||||
if (blkN == PARALLEL && blkW == PARALLEL && (lenientConnect || blkNW == PARALLEL)) {
|
||||
upgrade(SE, LARGE_RADIUS); upgrade(NE, SQUARE); upgrade(SW, SQUARE); upgrade(NW, INVISIBLE)
|
||||
}
|
||||
if (blkS == PARALLEL && blkW == PARALLEL && (lenientConnect || blkSW == PARALLEL)) {
|
||||
upgrade(NE, LARGE_RADIUS); upgrade(SE, SQUARE); upgrade(NW, SQUARE); upgrade(SW, INVISIBLE)
|
||||
}
|
||||
if (blkS == PARALLEL && blkE == PARALLEL && (lenientConnect || blkSE == PARALLEL)) {
|
||||
upgrade(NW, LARGE_RADIUS); upgrade(NE, SQUARE); upgrade(SW, SQUARE); upgrade(SE, INVISIBLE)
|
||||
}
|
||||
if (blkN == PARALLEL && blkE == PARALLEL && (lenientConnect || blkNE == PARALLEL)) {
|
||||
upgrade(SW, LARGE_RADIUS); upgrade(SE, SQUARE); upgrade(NW, SQUARE); upgrade(NE, INVISIBLE)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of the block at the given offset in a rotated reference frame.
|
||||
*/
|
||||
fun BlockCtx.blockType(rotation: Rotation, axis: Axis, offset: Int3): ColumnLayerData.SpecialRender.BlockType {
|
||||
val offsetRot = offset.rotate(rotation)
|
||||
val key = getColumnKey(state(offsetRot))
|
||||
return if (key == null) {
|
||||
if (offset(offsetRot).isNormalCube) SOLID else NONSOLID
|
||||
} else {
|
||||
(key.axis ?: if (BetterFoliage.config.roundLogs.defaultY) Axis.Y else null)?.let {
|
||||
if (it == axis) PARALLEL else PERPENDICULAR
|
||||
} ?: SOLID
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user