Created
August 20, 2016 17:22
A representation of Minecraft's block models in code.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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