ocelot-desktop/src/main/scala/ocelot/desktop/windows/HologramProjectorWindow.scala
2025-09-04 01:46:43 +03:00

291 lines
9.2 KiB
Scala

package ocelot.desktop.windows
import ocelot.desktop.OcelotDesktop
import ocelot.desktop.color.{Color, IntColor}
import ocelot.desktop.util.NumberUtils.ExtendedFloat
import ocelot.desktop.geometry._
import ocelot.desktop.graphics.IconSource
import ocelot.desktop.graphics.mesh.{Mesh3D, MeshBuilder3D}
import ocelot.desktop.graphics.scene.{Scene3D, SceneMesh3D}
import ocelot.desktop.node.nodes.HologramProjectorNode
import ocelot.desktop.ui.widget.window.PanelWindow
import ocelot.desktop.ui.widget.{IconButton, Viewport3DWidget, Widget}
import totoro.ocelot.brain.Settings
import totoro.ocelot.brain.entity.HologramProjector
import totoro.ocelot.brain.util.Tier
object HologramProjectorWindow {
private lazy val ProjectorMesh: Array[Mesh3D] = Array(Tier.One, Tier.Two).map(tier => {
val builder = new MeshBuilder3D
builder.scale = 1.0f / 16.0f
builder.spriteUVScale = 1.0f / 16.0f
val spriteTop = s"blocks/HologramProjector${tier.num}Top"
val spriteBottom = "blocks/Generic"
val spriteSide = "blocks/HologramProjectorSide"
builder.cuboid(
Vector3D(4, 0, 4),
Vector3D(12, 3, 12),
left = None,
right = None,
front = None,
back = None,
top = Some((spriteTop, Rect2D(4, 4, 8, 8))),
bottom = Some((spriteBottom, Rect2D(4, 4, 8, 8))),
)
builder.cuboid(
Vector3D(0, 0, 0),
Vector3D(2, 7, 16),
top = Some((spriteTop, Rect2D(0, 0, 2, 16))),
bottom = Some((spriteBottom, Rect2D(0, 0, 2, 16))),
front = Some((spriteSide, Rect2D(2, 9, -2, 7))),
back = Some((spriteSide, Rect2D(0, 9, 2, 7))),
left = Some((spriteSide, Rect2D(0, 9, 16, 7))),
right = Some((spriteSide, Rect2D(0, 9, 16, 7))),
)
builder.cuboid(
Vector3D(14, 0, 0),
Vector3D(16, 7, 16),
top = Some((spriteTop, Rect2D(14, 0, 2, 16))),
bottom = Some((spriteBottom, Rect2D(14, 0, 2, 16))),
front = Some((spriteSide, Rect2D(0, 9, 2, 7))),
back = Some((spriteSide, Rect2D(2, 9, -2, 7))),
left = Some((spriteSide, Rect2D(0, 9, 16, 7))),
right = Some((spriteSide, Rect2D(0, 9, 16, 7))),
)
builder.cuboid(
Vector3D(2, 0, 0),
Vector3D(14, 7, 2),
left = None,
right = None,
top = Some((spriteTop, Rect2D(2, 0, 12, 2))),
bottom = Some((spriteBottom, Rect2D(2, 2, 12, -2))),
front = Some((spriteSide, Rect2D(2, 9, 12, 7))),
back = Some((spriteSide, Rect2D(2, 9, 12, 7))),
)
builder.cuboid(
Vector3D(2, 0, 14),
Vector3D(14, 7, 16),
left = None,
right = None,
top = Some((spriteTop, Rect2D(2, 14, 12, 2))),
bottom = Some((spriteBottom, Rect2D(2, 16, 12, -2))),
front = Some((spriteSide, Rect2D(2, 9, 12, 7))),
back = Some((spriteSide, Rect2D(2, 9, 12, 7))),
)
builder.cuboid(
Vector3D(2, 0, 2),
Vector3D(4, 5, 14),
front = None,
back = None,
left = None,
top = Some((spriteTop, Rect2D(2, 2, 2, 12))),
bottom = Some((spriteBottom, Rect2D(2, 2, 2, 12))),
right = Some((spriteSide, Rect2D(2, 16, 12, -5))),
)
builder.cuboid(
Vector3D(12, 0, 2),
Vector3D(14, 5, 14),
front = None,
back = None,
right = None,
top = Some((spriteTop, Rect2D(12, 2, 2, 12))),
bottom = Some((spriteBottom, Rect2D(12, 2, 2, 12))),
left = Some((spriteSide, Rect2D(2, 16, 12, -5))),
)
builder.cuboid(
Vector3D(4, 0, 2),
Vector3D(12, 5, 4),
left = None,
right = None,
front = None,
top = Some((spriteTop, Rect2D(4, 2, 8, 2))),
bottom = Some((spriteBottom, Rect2D(4, 2, 8, 2))),
back = Some((spriteSide, Rect2D(4, 16, 8, -5))),
)
builder.cuboid(
Vector3D(4, 0, 12),
Vector3D(12, 5, 14),
left = None,
right = None,
back = None,
top = Some((spriteTop, Rect2D(4, 12, 8, 2))),
bottom = Some((spriteBottom, Rect2D(4, 12, 8, 2))),
front = Some((spriteSide, Rect2D(4, 16, 8, -5))),
)
builder.build()
})
}
class HologramProjectorWindow(val hologramProjectorNode: HologramProjectorNode) extends PanelWindow {
private def hologram: HologramProjector = hologramProjectorNode.hologramProjector
private var mesh: Option[Mesh3D] = None
private val viewport = new Viewport3DWidget {
private var gridVisible = true
override protected def setupToolbar(toolbar: Widget): Unit = {
super.setupToolbar(toolbar)
toolbar.children :+= new IconButton(
IconSource.Icons.GridOff,
IconSource.Icons.Grid,
pressedColor = Color.White.withAlpha(0.5f),
releasedColor = Color.White.withAlpha(0.2f),
mode = IconButton.Mode.Switch,
darkenActiveColorFactor = 0.5f,
model = IconButton.ReadOnlyModel(gridVisible),
) {
override def onPressed(): Unit = gridVisible = true
override def onReleased(): Unit = gridVisible = false
}
}
override protected def createScene(): Scene3D = {
val scene = new Scene3D
scene.camera = camera
scene.lightSource.basis = Basis3D.rotate(Vector3D.AxisZ, 30f.toRadians) *
Basis3D.rotate(Vector3D.AxisX, 30f.toRadians)
if (gridVisible) {
val grid = new SceneMesh3D(Mesh3D.Grid)
grid.lightingFactor = 0
grid.textureFactor = 0
scene.meshes += grid
}
val projectorMesh = new SceneMesh3D(HologramProjectorWindow.ProjectorMesh(hologram.tier.id))
scene.meshes += projectorMesh
mesh.foreach(mesh => {
val workspace = hologram.workspace
val subTick =
(System.nanoTime() - workspace.getLastTickNanoTime).toFloat / 1e9f / OcelotDesktop.tpsCounter.dt.max(1e-9f)
val time = workspace.getIngameTime + subTick.clamped(0, 1)
var transform = Transform3D.translate(0.5f, 0.5f, 0.5f) *
Transform3D.rotate(
Vector3D(hologram.rotationX, hologram.rotationY, hologram.rotationZ),
hologram.rotationAngle.toRadians,
) *
Transform3D.rotate(
Vector3D(hologram.rotationSpeedX, hologram.rotationSpeedY, hologram.rotationSpeedZ),
(hologram.rotationSpeed * (time % (360 * 20 - 1)) / 20f).toRadians,
) *
Transform3D.scale(1.001f) *
Transform3D.translate(
(hologram.translationX * hologram.width / 16f - 1.5f) * hologram.scale,
hologram.translationY * hologram.height / 16f * hologram.scale,
(hologram.translationZ * hologram.width / 16f - 1.5f) * hologram.scale,
)
val hologramFlickerFrequency = Settings.get.hologramFlickerFrequency
val random = workspace.rand
if (hologramFlickerFrequency > 0 && workspace.rand.nextDouble() < hologramFlickerFrequency) {
transform *= Transform3D.scale(Vector3D(
1 + random.nextGaussian() * 0.01,
1 + random.nextGaussian() * 0.001,
1 + random.nextGaussian() * 0.01,
))
transform *= Transform3D.translate(Vector3D(
random.nextGaussian() * 0.01,
random.nextGaussian() * 0.01,
random.nextGaussian() * 0.01,
))
}
transform *= Transform3D.scale(Vector3D(hologram.scale / 16f, hologram.scale / 16f, hologram.scale / 16f))
val hologramMesh = new SceneMesh3D(mesh)
hologramMesh.isOpaque = false
hologramMesh.transform = transform
hologramMesh.color = Color.White.withAlpha(0.75f)
scene.meshes += hologramMesh
})
scene
}
}
setInner(viewport)
override protected def title: String = "Hologram Projector " + hologramProjectorNode.label.getOrElse("")
override protected def titleMaxLength: Int = 69
override def update(): Unit = {
super.update()
if (hologram.isDirty) {
updateMesh()
hologram.isDirty = false
}
}
private def updateMesh(): Unit = {
val builder = new MeshBuilder3D
def getValue(x: Int, y: Int, z: Int): Int = {
if (x >= 0 && y >= 0 && z >= 0 && x < hologram.width && z < hologram.width && y < hologram.height)
hologram.getColor(x, y, z)
else
0
}
def isEmpty(x: Int, y: Int, z: Int): Boolean = getValue(x, y, z) == 0
def getColor(x: Int, y: Int, z: Int): Color = IntColor(hologram.colors(getValue(x, y, z) - 1))
var hologramIsEmpty = true
for (x <- 0 until hologram.width) {
for (z <- 0 until hologram.width) {
for (y <- 0 until hologram.height) {
if (!isEmpty(x, y, z)) {
hologramIsEmpty = false
val color = getColor(x, y, z)
val face = Some(("blocks/HologramEffect", Rect2D.Unit))
builder.cuboid(
Vector3D(x, y, z),
Vector3D(x + 1, y + 1, z + 1),
color = color,
left = face.filter(_ => isEmpty(x - 1, y, z)),
right = face.filter(_ => isEmpty(x + 1, y, z)),
top = face.filter(_ => isEmpty(x, y + 1, z)),
bottom = face.filter(_ => isEmpty(x, y - 1, z)),
front = face.filter(_ => isEmpty(x, y, z - 1)),
back = face.filter(_ => isEmpty(x, y, z + 1)),
)
}
}
}
}
if (!hologramIsEmpty) {
mesh = Some(builder.build())
} else {
mesh = None
}
}
override def dispose(): Unit = {
viewport.dispose()
}
}