[WIP] Cactus, netherrack, round logs work
+ lots more cleanup + Optifine x-ray fix
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
package mods.betterfoliage.render.column
|
||||
|
||||
import mods.betterfoliage.config.Config
|
||||
import mods.betterfoliage.model.Quad
|
||||
import mods.betterfoliage.model.UV
|
||||
import mods.betterfoliage.model.Vertex
|
||||
import mods.betterfoliage.model.bake
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.INVISIBLE
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.LARGE_RADIUS
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.SMALL_RADIUS
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.SQUARE
|
||||
import mods.betterfoliage.util.Double3
|
||||
import mods.betterfoliage.util.Rotation
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||
import net.minecraft.util.Direction.Axis
|
||||
import net.minecraft.util.Direction.EAST
|
||||
import net.minecraft.util.Direction.SOUTH
|
||||
import net.minecraft.util.Direction.UP
|
||||
|
||||
/**
|
||||
* Collection of dynamically generated meshes used to render rounded columns.
|
||||
*/
|
||||
class ColumnMeshSet(
|
||||
radiusSmall: Double,
|
||||
radiusLarge: Double,
|
||||
zProtection: Double,
|
||||
val axis: Axis,
|
||||
val spriteLeft: TextureAtlasSprite,
|
||||
val spriteRight: TextureAtlasSprite,
|
||||
val spriteTop: TextureAtlasSprite,
|
||||
val spriteBottom: TextureAtlasSprite
|
||||
) {
|
||||
protected fun sideRounded(radius: Double, yBottom: Double, yTop: Double): List<Quad> {
|
||||
val halfRadius = radius * 0.5
|
||||
return listOf(
|
||||
// left side of the diagonal
|
||||
Quad.verticalRectangle(0.0, 0.5, 0.5 - radius, 0.5, yBottom, yTop).clampUV(minU = 0.0, maxU = 0.5 - radius),
|
||||
Quad.verticalRectangle(0.5 - radius, 0.5, 0.5 - halfRadius, 0.5 - halfRadius, yBottom, yTop).clampUV(minU = 0.5 - radius),
|
||||
// right side of the diagonal
|
||||
Quad.verticalRectangle(0.5 - halfRadius, 0.5 - halfRadius, 0.5, 0.5 - radius, yBottom, yTop).clampUV(maxU = radius - 0.5),
|
||||
Quad.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(
|
||||
Quad.verticalRectangle(0.0, 0.5, 0.5, 0.5, yBottom, yTop).clampUV(minU = 0.0),
|
||||
Quad.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 Config.nVidia) 0 else 1) }
|
||||
.map { it.rotate(rotation).rotateUV(quadrant) }
|
||||
.map { it.sprite(if (isBottom) spriteBottom else spriteTop) }
|
||||
.map { if (isBottom) it.flipped else it }
|
||||
}
|
||||
|
||||
protected fun lidSquare(y: Double, isBottom: Boolean) = Array(4) { quadrant ->
|
||||
val rotation = baseRotation(axis) + quadrantRotations[quadrant]
|
||||
listOf(
|
||||
Quad.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)
|
||||
.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) }
|
||||
.mapIndexed { idx, q -> if (idx % (2 * quadsPerSprite) >= quadsPerSprite) q.sprite(spriteRight) else q.sprite(spriteLeft) }
|
||||
.bake(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).bake(false)
|
||||
val lidTopRoundSmall = lidRounded(radiusSmall, 0.5, false).bake(false)
|
||||
val lidTopRoundLarge = lidRounded(radiusLarge, 0.5, false).bake(false)
|
||||
|
||||
val lidBottomSquare = lidSquare(-0.5, true).bake(false)
|
||||
val lidBottomRoundSmall = lidRounded(radiusSmall, -0.5, true).bake(false)
|
||||
val lidBottomRoundLarge = lidRounded(radiusLarge, -0.5, true).bake(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]
|
||||
}
|
||||
}
|
||||
112
src/main/kotlin/mods/betterfoliage/render/column/ColumnModel.kt
Normal file
112
src/main/kotlin/mods/betterfoliage/render/column/ColumnModel.kt
Normal file
@@ -0,0 +1,112 @@
|
||||
package mods.betterfoliage.render.column
|
||||
|
||||
import mods.betterfoliage.chunk.ChunkOverlayManager
|
||||
import mods.betterfoliage.model.HalfBakedSpecialWrapper
|
||||
import mods.betterfoliage.model.SpecialRenderModel
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.NormalRender
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.BlockType.NONSOLID
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.BlockType.PARALLEL
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.BlockType.PERPENDICULAR
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.INVISIBLE
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.LARGE_RADIUS
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.SMALL_RADIUS
|
||||
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.SQUARE
|
||||
import mods.betterfoliage.render.lighting.ColumnLighting
|
||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
||||
import net.minecraft.util.Direction.Axis
|
||||
|
||||
abstract class ColumnModelBase(
|
||||
wrapped: SpecialRenderModel
|
||||
) : HalfBakedSpecialWrapper(wrapped) {
|
||||
|
||||
abstract val enabled: Boolean
|
||||
abstract val overlayLayer: ColumnRenderLayer
|
||||
abstract val connectPerpendicular: Boolean
|
||||
abstract fun getMeshSet(axis: Axis, quadrant: Int): ColumnMeshSet
|
||||
|
||||
override fun render(ctx: RenderCtxBase, noDecorations: Boolean) {
|
||||
val roundLog = ChunkOverlayManager.get(overlayLayer, ctx)
|
||||
|
||||
when(roundLog) {
|
||||
ColumnLayerData.SkipRender -> return
|
||||
NormalRender -> return super.render(ctx, noDecorations)
|
||||
ColumnLayerData.ResolveError, null -> {
|
||||
return super.render(ctx, noDecorations)
|
||||
}
|
||||
}
|
||||
|
||||
// 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.render(ctx, noDecorations)
|
||||
}
|
||||
|
||||
ctx.vertexLighter = ColumnLighting
|
||||
|
||||
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 { ctx.renderQuads(it) }
|
||||
upMesh?.let { ctx.renderQuads(it) }
|
||||
downMesh?.let { ctx.renderQuads(it) }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -90,7 +90,6 @@ abstract class ColumnRenderLayer : ChunkOverlayLayer<ColumnLayerData> {
|
||||
override fun calculate(ctx: BlockCtx): ColumnLayerData {
|
||||
// TODO detect round logs
|
||||
if (allDirections.all { dir -> ctx.offset(dir).let { 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
|
||||
|
||||
Reference in New Issue
Block a user