[WIP] major rewrite, grass and leaves working already
This commit is contained in:
@@ -1,23 +1,31 @@
|
||||
package mods.betterfoliage.render.old
|
||||
|
||||
import mods.betterfoliage.render.lighting.ModelLighter
|
||||
import mods.betterfoliage.render.lighting.NoLighting
|
||||
import mods.betterfoliage.render.lighting.ShaderFactory
|
||||
import mods.betterfoliage.render.lighting.cornerAo
|
||||
import mods.betterfoliage.render.lighting.cornerFlat
|
||||
import mods.betterfoliage.render.lighting.faceOrientedAuto
|
||||
import mods.betterfoliage.util.Double3
|
||||
import mods.betterfoliage.util.Rotation
|
||||
import mods.betterfoliage.util.allDirections
|
||||
import mods.betterfoliage.util.boxFaces
|
||||
import mods.betterfoliage.util.get
|
||||
import mods.betterfoliage.util.minmax
|
||||
import mods.betterfoliage.util.nearestAngle
|
||||
import mods.betterfoliage.util.replace
|
||||
import mods.betterfoliage.util.rotate
|
||||
import mods.betterfoliage.util.times
|
||||
import mods.betterfoliage.util.toImmutableList
|
||||
import mods.betterfoliage.util.vec
|
||||
import net.minecraft.client.renderer.model.BakedQuad
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat
|
||||
import net.minecraft.client.renderer.vertex.VertexFormatElement
|
||||
import net.minecraft.client.renderer.vertex.VertexFormatElement.Type
|
||||
import net.minecraft.client.renderer.vertex.VertexFormatElement.Usage
|
||||
import net.minecraft.util.Direction
|
||||
import net.minecraftforge.client.model.pipeline.BakedQuadBuilder
|
||||
import java.lang.Math.max
|
||||
import java.lang.Math.min
|
||||
import java.util.Random
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
|
||||
/**
|
||||
* Vertex UV coordinates
|
||||
@@ -34,7 +42,7 @@ data class UV(val u: Double, val v: Double) {
|
||||
|
||||
val rotate: UV get() = UV(v, -u)
|
||||
|
||||
fun rotate(n: Int) = when(n % 4) {
|
||||
fun rotate(n: Int) = when (n % 4) {
|
||||
0 -> copy()
|
||||
1 -> UV(v, -u)
|
||||
2 -> UV(-u, -v)
|
||||
@@ -55,105 +63,153 @@ data class UV(val u: Double, val v: Double) {
|
||||
* @param[aoShader] [ModelLighter] instance to use with AO rendering
|
||||
* @param[flatShader] [ModelLighter] instance to use with non-AO rendering
|
||||
*/
|
||||
data class Vertex(val xyz: Double3 = Double3(0.0, 0.0, 0.0),
|
||||
val uv: UV = UV(0.0, 0.0),
|
||||
val aoShader: ModelLighter = NoLighting,
|
||||
val flatShader: ModelLighter = NoLighting
|
||||
data class Vertex(
|
||||
val xyz: Double3 = Double3(0.0, 0.0, 0.0),
|
||||
val uv: UV = UV(0.0, 0.0),
|
||||
val color: Color = Color.white,
|
||||
val normal: Double3? = null
|
||||
)
|
||||
|
||||
data class Color(val alpha: Int, val red: Int, val green: Int, val blue: Int) {
|
||||
constructor(combined: Int) : this(
|
||||
combined shr 24 and 255,
|
||||
combined shr 16 and 255,
|
||||
combined shr 8 and 255,
|
||||
combined and 255
|
||||
)
|
||||
|
||||
val asInt get() = (alpha shl 24) or (red shl 16) or (green shl 8) or blue
|
||||
operator fun times(f: Float) = Color(
|
||||
alpha,
|
||||
(f * red.toFloat()).toInt().coerceIn(0 until 256),
|
||||
(f * green.toFloat()).toInt().coerceIn(0 until 256),
|
||||
(f * blue.toFloat()).toInt().coerceIn(0 until 256)
|
||||
)
|
||||
|
||||
companion object {
|
||||
val white get() = Color(255, 255, 255, 255)
|
||||
}
|
||||
}
|
||||
|
||||
data class HSB(var hue: Float, var saturation: Float, var brightness: Float) {
|
||||
companion object {
|
||||
fun fromColor(color: Int): HSB {
|
||||
val hsbVals = java.awt.Color.RGBtoHSB((color shr 16) and 255, (color shr 8) and 255, color and 255, null)
|
||||
return HSB(hsbVals[0], hsbVals[1], hsbVals[2])
|
||||
}
|
||||
}
|
||||
val asColor: Int get() = java.awt.Color.HSBtoRGB(hue, saturation, brightness)
|
||||
}
|
||||
|
||||
/**
|
||||
* Model quad
|
||||
* Intermediate representation of model quad
|
||||
* Immutable, double-precision
|
||||
* Zero-centered (both XYZ and UV) coordinates for simpler rotation/mirroring
|
||||
*/
|
||||
data class Quad(val v1: Vertex, val v2: Vertex, val v3: Vertex, val v4: Vertex) {
|
||||
data class Quad(
|
||||
val v1: Vertex, val v2: Vertex, val v3: Vertex, val v4: Vertex,
|
||||
val sprite: TextureAtlasSprite? = null,
|
||||
val colorIndex: Int = -1,
|
||||
val face: Direction? = null
|
||||
) {
|
||||
val verts = arrayOf(v1, v2, v3, v4)
|
||||
inline fun transformV(trans: (Vertex)-> Vertex): Quad = transformVI { vertex, idx -> trans(vertex) }
|
||||
inline fun transformVI(trans: (Vertex, Int)-> Vertex): Quad =
|
||||
Quad(trans(v1, 0), trans(v2, 1), trans(v3, 2), trans(v4, 3))
|
||||
|
||||
inline fun transformV(trans: (Vertex) -> Vertex): Quad = transformVI { vertex, idx -> trans(vertex) }
|
||||
inline fun transformVI(trans: (Vertex, Int) -> Vertex): Quad = copy(
|
||||
v1 = trans(v1, 0), v2 = trans(v2, 1), v3 = trans(v3, 2), v4 = trans(v4, 3)
|
||||
)
|
||||
|
||||
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, Direction>) = 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)) }
|
||||
fun rotate(rot: Rotation) = transformV {
|
||||
it.copy(xyz = it.xyz.rotate(rot), aoShader = it.aoShader.rotate(rot), flatShader = it.flatShader.rotate(rot))
|
||||
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 rotate(rot: Rotation) =
|
||||
transformV { it.copy(xyz = it.xyz.rotate(rot), normal = it.normal?.rotate(rot)) }.copy(face = face?.rotate(rot))
|
||||
|
||||
fun rotateZ(angle: Double) = transformV {
|
||||
it.copy(
|
||||
xyz = Double3(
|
||||
it.xyz.x * cos(angle) + it.xyz.z * sin(angle),
|
||||
it.xyz.y,
|
||||
it.xyz.z * cos(angle) - it.xyz.x * sin(angle)
|
||||
),
|
||||
normal = it.normal?.let { normal ->
|
||||
Double3(
|
||||
normal.x * cos(angle) + normal.z * sin(angle),
|
||||
normal.y,
|
||||
normal.z * cos(angle) - normal.x * sin(angle)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun scaleUV(scale: Double) = transformV { it.copy(uv = UV(it.uv.u * scale, it.uv.v * scale)) }
|
||||
fun rotateUV(n: Int) = transformV { it.copy(uv = it.uv.rotate(n)) }
|
||||
fun clampUV(minU: Double = -0.5, maxU: Double = 0.5, minV: Double = -0.5, maxV: Double = 0.5) =
|
||||
transformV { it.copy(uv = it.uv.clamp(minU, maxU, minV, maxV)) }
|
||||
fun mirrorUV(mirrorU: Boolean, mirrorV: Boolean) = transformV { it.copy(uv = it.uv.mirror(mirrorU, mirrorV)) }
|
||||
fun setAoShader(factory: ShaderFactory, predicate: (Vertex, Int)->Boolean = { v, vi -> true }) =
|
||||
transformVI { vertex, idx ->
|
||||
if (!predicate(vertex, idx)) vertex else vertex.copy(aoShader = factory(this@Quad, vertex))
|
||||
}
|
||||
fun setFlatShader(factory: ShaderFactory, predicate: (Vertex, Int)->Boolean = { v, vi -> true }) =
|
||||
transformVI { vertex, idx ->
|
||||
if (!predicate(vertex, idx)) vertex else vertex.copy(flatShader = factory(this@Quad, vertex))
|
||||
}
|
||||
fun setFlatShader(shader: ModelLighter) = transformVI { vertex, idx -> vertex.copy(flatShader = shader) }
|
||||
val flipped: Quad get() = Quad(v4, v3, v2, v1)
|
||||
fun scrambleUV(random: Random, canFlipU: Boolean, canFlipV: Boolean, canRotate: Boolean) = this
|
||||
.mirrorUV(canFlipU && random.nextBoolean(), canFlipV && random.nextBoolean())
|
||||
.let { if (canRotate) it.rotateUV(random.nextInt(4)) else it }
|
||||
|
||||
fun cycleVertices(n: Int) = when(n % 4) {
|
||||
fun sprite(sprite: TextureAtlasSprite) = copy(sprite = sprite)
|
||||
fun color(color: Color) = transformV { it.copy(color = color) }
|
||||
fun color(color: Int) = transformV { it.copy(color = Color(color)) }
|
||||
fun colorIndex(colorIndex: Int) = copy(colorIndex = colorIndex)
|
||||
fun colorAndIndex(color: Color?) = color(color ?: Color.white).colorIndex(if (color == null) 0 else -1)
|
||||
|
||||
fun face() = face ?: nearestAngle(normal, Direction.values().toList()) { it.vec }.first
|
||||
|
||||
val flipped: Quad get() = Quad(v4, v3, v2, v1, sprite, colorIndex)
|
||||
fun cycleVertices(n: Int) = when (n % 4) {
|
||||
1 -> Quad(v2, v3, v4, v1)
|
||||
2 -> Quad(v3, v4, v1, v2)
|
||||
3 -> Quad(v4, v1, v2, v3)
|
||||
else -> this.copy()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Model. The basic unit of rendering blocks with OctarineCore.
|
||||
*
|
||||
* The model should be positioned so that (0,0,0) is the block center.
|
||||
* The block extends to (-0.5, 0.5) in all directions (inclusive).
|
||||
*/
|
||||
class Model() {
|
||||
constructor(other: List<Quad>) : this() { quads.addAll(other) }
|
||||
val quads = mutableListOf<Quad>()
|
||||
companion object {
|
||||
fun mix(first: Quad, second: Quad, vertexFactory: (Vertex, Vertex) -> Vertex) = Quad(
|
||||
v1 = vertexFactory(first.v1, second.v1),
|
||||
v2 = vertexFactory(first.v2, second.v2),
|
||||
v3 = vertexFactory(first.v3, second.v3),
|
||||
v4 = vertexFactory(first.v4, second.v4)
|
||||
)
|
||||
|
||||
fun Quad.add() = quads.add(this)
|
||||
fun Iterable<Quad>.addAll() = forEach { quads.add(it) }
|
||||
|
||||
fun transformQ(trans: (Quad)-> Quad) = quads.replace(trans)
|
||||
fun transformV(trans: (Vertex)-> Vertex) = quads.replace{ it.transformV(trans) }
|
||||
|
||||
fun verticalRectangle(x1: Double, z1: Double, x2: Double, z2: Double, yBottom: Double, yTop: Double) = Quad(
|
||||
fun verticalRectangle(x1: Double, z1: Double, x2: Double, z2: Double, yBottom: Double, yTop: Double) = Quad(
|
||||
Vertex(Double3(x1, yBottom, z1), UV.bottomLeft),
|
||||
Vertex(Double3(x2, yBottom, z2), UV.bottomRight),
|
||||
Vertex(Double3(x2, yTop, z2), UV.topRight),
|
||||
Vertex(Double3(x1, yTop, z1), UV.topLeft)
|
||||
)
|
||||
|
||||
fun horizontalRectangle(x1: Double, z1: Double, x2: Double, z2: Double, y: Double): Quad {
|
||||
val xMin = min(x1, x2); val xMax = max(x1, x2)
|
||||
val zMin = min(z1, z2); val zMax = max(z1, z2)
|
||||
return Quad(
|
||||
Vertex(Double3(xMin, y, zMin), UV.topLeft),
|
||||
Vertex(Double3(xMin, y, zMax), UV.bottomLeft),
|
||||
Vertex(Double3(xMax, y, zMax), UV.bottomRight),
|
||||
Vertex(Double3(xMax, y, zMin), UV.topRight)
|
||||
)
|
||||
}
|
||||
|
||||
fun faceQuad(face: Direction): Quad {
|
||||
val base = face.vec * 0.5
|
||||
val top = boxFaces[face].top * 0.5
|
||||
val left = boxFaces[face].left * 0.5
|
||||
return Quad(
|
||||
fun horizontalRectangle(x1: Double, z1: Double, x2: Double, z2: Double, y: Double): Quad {
|
||||
val xMin = min(x1, x2);
|
||||
val xMax = max(x1, x2)
|
||||
val zMin = min(z1, z2);
|
||||
val zMax = max(z1, z2)
|
||||
return Quad(
|
||||
Vertex(Double3(xMin, y, zMin), UV.topLeft),
|
||||
Vertex(Double3(xMin, y, zMax), UV.bottomLeft),
|
||||
Vertex(Double3(xMax, y, zMax), UV.bottomRight),
|
||||
Vertex(Double3(xMax, y, zMin), UV.topRight)
|
||||
)
|
||||
}
|
||||
|
||||
fun faceQuad(face: Direction): Quad {
|
||||
val base = face.vec * 0.5
|
||||
val top = boxFaces[face].top * 0.5
|
||||
val left = boxFaces[face].left * 0.5
|
||||
return Quad(
|
||||
Vertex(base + top + left, UV.topLeft),
|
||||
Vertex(base - top + left, UV.bottomLeft),
|
||||
Vertex(base - top - left, UV.bottomRight),
|
||||
Vertex(base + top - left, UV.topRight)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val fullCube = Model().apply {
|
||||
allDirections.forEach {
|
||||
faceQuad(it)
|
||||
.setAoShader(faceOrientedAuto(corner = cornerAo(it.axis), edge = null))
|
||||
.setFlatShader(faceOrientedAuto(corner = cornerFlat, edge = null))
|
||||
.add()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user