Skip to content

Instantly share code, notes, and snippets.

@howtonotwin
Created August 20, 2016 17:22
A representation of Minecraft's block models in code.
sealed abstract class Axis {
import Axis._
def json: String = this match {
case X => "\"x\""
case Y => "\"y\""
case Z => "\"z\""
}
}
object Axis {
case object X extends Axis
case object Y extends Axis
case object Z extends Axis
}
sealed abstract class Sign
object Sign {
case object Positive extends Sign
case object Negative extends Sign
}
sealed abstract class Direction(val axis: Axis, val sign: Sign) {
import Direction._
def json: String = this match {
case Down => "\"down\""
case Up => "\"up\""
case North => "\"north\""
case South => "\"south\""
case West => "\"west\""
case East => "\"east\""
}
}
object Direction {
import Axis._
import Sign.{ Positive => +, Negative => - }
case object Down extends Direction(Y, -)
case object Up extends Direction(Y, +)
case object North extends Direction(Z, -)
case object South extends Direction(Z, +)
case object West extends Direction(X, -)
case object East extends Direction(X, +)
}
case class Coord(x: Int, y: Int, z: Int) {
def this(tuple: (Int, Int, Int)) = this(tuple._1, tuple._2, tuple._3)
def json: String = f"[ $x%3d, $y%3d, $z%3d ]"
def +(that: Coord): Coord = Coord(this.x + that.x, this.y + that.y, this.z + that.z)
def unary_- = this * -1
def -(that: Coord): Coord = this + (-that)
def *(scalar: Int): Coord = Coord(x * scalar, y * scalar, z * scalar)
}
object Coord {
import language.implicitConversions
implicit def apply(tuple: (Int, Int, Int)): Coord = new Coord(tuple)
}
case class UV(begin: Coord, end: Coord) {
require(begin.z == 0)
require(end.z == 0)
require(0 to 16 contains begin.x)
require(0 to 16 contains begin.y)
require(0 to 16 contains end.x)
require(0 to 16 contains end.y)
def this(tuple: ((Int, Int), (Int, Int))) = this(Coord(tuple._1._1, 0, tuple._1._2), Coord(tuple._2._1, 0, tuple._2._2))
def json: String = f"[ ${begin.x}%2d, ${begin.y}%2d, ${end.x}%2d, ${end.y}%2d ]"
}
object UV {
import language.implicitConversions
implicit def apply(tuple: ((Int, Int), (Int, Int))): UV = new UV(tuple)
}
case class FaceData(texture: Option[String] = None, uv: Option[UV] = None, cullface: Option[Direction] = None, rotation: Option[Int] = None, tintIndex: Option[Int] = None) {
require(rotation forall { _ % 90 == 0 })
def json: String = {
var elems = List.empty[String]
elems ++= texture map { t => s""""texture": "#$t"""" }
elems ++= uv map { uv => f""""uv": ${uv.json}""" }
elems ++= cullface map { c => f""""cullface": ${c.json}%7s""" }
elems ++= rotation map { i => f""""rotation": $i%3d""" }
elems ++= tintIndex map { t => f""""tintindex": $t""" }
s"{ ${elems mkString ", "} }"
}
}
case class Rotation(origin: Option[Coord] = None, axis: Axis, angle: Option[Float] = None, rescale: Option[Boolean] = None) {
require(angle forall { a => a <= 45 && a >= - 45 })
require(angle forall { _ % 22.5f == 0 })
def json: String = {
var elems = s""""axis": ${axis.json}""" :: Nil
elems ++= origin map { o => s""", "origin": ${o.json}""" }
elems ++= angle map { f => f""", "angle": $f%3.1f""" }
elems ++= rescale map { r => f""", "rescale": $r%5b""" }
s"{ ${elems mkString ", "} }"
}
}
case class Element(start: Coord, end: Coord, faces: Option[Map[Direction, FaceData]] = None, name: Option[String] = None, rotation: Option[Rotation] = None, shade: Option[Boolean] = None) {
def json: String = {
var elems = List.empty[String]
elems ++= name map { n => s""""name": "$n"""" }
elems :+= s""""from": ${start.json}"""
elems :+= s""""to": ${end.json}"""
elems ++= shade map { s => s""""shade": "$s"""" }
elems ++= rotation map { r => s""""rotation": ${r.json}""" }
elems ++= faces map { faces =>
val content = faces map { case (face, data) => (s"${face.json}:", data) } map { case (id, data) => f"""$id%-8s ${data.json}""" } mkString ",\n\t\t"
s""""faces": {\n\t\t$content\n\t}"""
}
s"{\n\t${elems mkString ",\n\t"}\n}"
}
}
case class Model(parent: Option[String] = None, elements: Option[List[Element]] = None, textures: Option[Map[String, String]] = None, ambientOcclusion: Option[Boolean] = None) {
def json: String = {
var elems = List.empty[String]
elems ++= parent map { p => s""""parent": "$p"""" }
elems ++= textures map { textures =>
val content = textures map { case (variable, value) => s""""$variable": "$value"""" } mkString ",\n\t\t"
s""""textures": {\n\t\t$content\n\t}"""
}
elems ++= ambientOcclusion map { a => s""""ambientocclusion": $a""" }
elems ++= elements map { elements =>
val content = elements map { _.json.split('\n') map { "\t\t" + _ } } map { _ mkString "\n" } mkString ",\n"
s""""elements": [\n$content\n\t]"""
}
s"{\n\t${elems mkString ",\n\t"}\n}"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment