mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-28 23:40:49 -04:00
Merge branch 'master' of github.com:MightyPirates/OpenComputers into MC1.7
Conflicts: src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRenderer.scala
This commit is contained in:
commit
2f691776ae
@ -169,8 +169,8 @@ oc:tooltip.ControlUnit=Klingt wichtig, ist es auch. Man baut daraus immerhin CPU
|
|||||||
oc:tooltip.CPU=Kernstück eines jeden Computers. Die Taktrate hat einen leichten Schatten, aber was kann man von einer Taschensonnenuhr schon erwarten?[nl] Unterstützte Komponenten: §f%s§7.
|
oc:tooltip.CPU=Kernstück eines jeden Computers. Die Taktrate hat einen leichten Schatten, aber was kann man von einer Taschensonnenuhr schon erwarten?[nl] Unterstützte Komponenten: §f%s§7.
|
||||||
oc:tooltip.CuttingWire=Wird gebraucht, um Tonblöcke in Leiterplattenform zu bekommen. Vermutlich das ineffizienteste Werkzeug in der Geschichte der Menschheit, da es nach einer Verwendung kaputt geht.
|
oc:tooltip.CuttingWire=Wird gebraucht, um Tonblöcke in Leiterplattenform zu bekommen. Vermutlich das ineffizienteste Werkzeug in der Geschichte der Menschheit, da es nach einer Verwendung kaputt geht.
|
||||||
oc:tooltip.Disassembler=Zerlegt Gegenstände in ihre Einzelteile. §lWarnung§7: zurückgewonnene Gegenstände haben eine %s%%-ige Chance beim Extrahieren kaputt zu gehen!
|
oc:tooltip.Disassembler=Zerlegt Gegenstände in ihre Einzelteile. §lWarnung§7: zurückgewonnene Gegenstände haben eine %s%%-ige Chance beim Extrahieren kaputt zu gehen!
|
||||||
oc:tooltip.Disk=Sehr einfaches Speichermedium, das verwendet werden kann, um Disketten und Festplatten bauen.
|
oc:tooltip.Disk=Sehr einfaches Speichermedium, das verwendet werden kann, um Disketten und Festplatten zu fertigen.
|
||||||
oc:tooltip.DiskDrive.CC=ComputerCraft-Disketten werden §aunterstützt§7.
|
oc:tooltip.DiskDrive.CC=ComputerCraft-Disketten werden §aauch unterstützt§7.
|
||||||
oc:tooltip.DiskDrive=Erlaubt es, Disketten zu lesen und zu beschreiben. Kann in Robotern installiert werden, um später Disketten einlegen zu können.
|
oc:tooltip.DiskDrive=Erlaubt es, Disketten zu lesen und zu beschreiben. Kann in Robotern installiert werden, um später Disketten einlegen zu können.
|
||||||
oc:tooltip.Geolyzer=Erlaubt es die Härte der Blöcke in der Umgebung abzufragen. Hilfreich um Hologramme der Gegend zu erzeugen, oder um Erze zu entdecken.
|
oc:tooltip.Geolyzer=Erlaubt es die Härte der Blöcke in der Umgebung abzufragen. Hilfreich um Hologramme der Gegend zu erzeugen, oder um Erze zu entdecken.
|
||||||
oc:tooltip.GraphicsCard=Erlaubt es, den angezeigten Inhalt von Bildschirmen zu ändern.[nl] Höchstauflösung: §f%sx%s§7.[nl] Maximale Farbtiefe: §f%s§7.[nl] Operationen/Tick: §f%s§7.
|
oc:tooltip.GraphicsCard=Erlaubt es, den angezeigten Inhalt von Bildschirmen zu ändern.[nl] Höchstauflösung: §f%sx%s§7.[nl] Maximale Farbtiefe: §f%s§7.[nl] Operationen/Tick: §f%s§7.
|
||||||
@ -203,7 +203,7 @@ oc:tooltip.Robot=Im Gegensatz zu normalen Computern können sich Roboter in der
|
|||||||
# The underscore makes sure this isn't hidden with the rest of the tooltip.
|
# The underscore makes sure this isn't hidden with the rest of the tooltip.
|
||||||
oc:tooltip.Robot_Level=§fStufe§7: §a%s§7.
|
oc:tooltip.Robot_Level=§fStufe§7: §a%s§7.
|
||||||
oc:tooltip.Robot_StoredEnergy=§fGespeicherte Energie§7: §a%s§7.
|
oc:tooltip.Robot_StoredEnergy=§fGespeicherte Energie§7: §a%s§7.
|
||||||
oc:tooltip.RobotAssembler=Erlaubt das Bauen von Robotern aus diversen anderen Computerteilen.
|
oc:tooltip.RobotAssembler=Erlaubt die Fertigung von Robotern aus diversen anderen Computerteilen.
|
||||||
oc:tooltip.Router=Erlaubt es, mehrere Netzwerke miteinander zu verbinden. Leitet ausschließlich Netzwerknachrichten weiter, Komponenten "hinter" dem Switch sind nicht sichtbar. Nützlich, um Netzwerke zu trennen, jedoch nach wie vor Kommunikation zwischen den Netzwerken zu erlauben, z.b. mittels Netzwerkkarten.
|
oc:tooltip.Router=Erlaubt es, mehrere Netzwerke miteinander zu verbinden. Leitet ausschließlich Netzwerknachrichten weiter, Komponenten "hinter" dem Switch sind nicht sichtbar. Nützlich, um Netzwerke zu trennen, jedoch nach wie vor Kommunikation zwischen den Netzwerken zu erlauben, z.b. mittels Netzwerkkarten.
|
||||||
oc:tooltip.Screen=Zeigt Text an, gesteuert von Grafikkarten in Computern.[nl] Höchstauflösung: §f%sx%s§7.[nl] Maximale Farbtiefe: §f%s§7.
|
oc:tooltip.Screen=Zeigt Text an, gesteuert von Grafikkarten in Computern.[nl] Höchstauflösung: §f%sx%s§7.[nl] Maximale Farbtiefe: §f%s§7.
|
||||||
oc:tooltip.Server=Ein Server kann wie ein gewöhnliches Computergehäuse mit Komponenten verbessert werden. Um den Server zu starten, muss er in einem Servergehäuse installiert werden.[nl] Anzahl unterstützter Fernbedienungen: §f%s§7.
|
oc:tooltip.Server=Ein Server kann wie ein gewöhnliches Computergehäuse mit Komponenten verbessert werden. Um den Server zu starten, muss er in einem Servergehäuse installiert werden.[nl] Anzahl unterstützter Fernbedienungen: §f%s§7.
|
||||||
@ -213,15 +213,15 @@ oc:tooltip.Terminal=Erlaubt es, einen Server aus der Ferne zu steuern, so lange
|
|||||||
oc:tooltip.TooLong=Halte [§f%s§7] gedrückt für mehr Infos.
|
oc:tooltip.TooLong=Halte [§f%s§7] gedrückt für mehr Infos.
|
||||||
oc:tooltip.Transistor=Elementarer Baustein der meisten Computerkomponenten. Nicht zu verwechseln mit Steinelementaren.
|
oc:tooltip.Transistor=Elementarer Baustein der meisten Computerkomponenten. Nicht zu verwechseln mit Steinelementaren.
|
||||||
oc:tooltip.UpgradeAngel=Erlaubt es Robotern, Blöcke in die Luft zu setzen, selbst wenn kein Referenzblock daneben existiert.
|
oc:tooltip.UpgradeAngel=Erlaubt es Robotern, Blöcke in die Luft zu setzen, selbst wenn kein Referenzblock daneben existiert.
|
||||||
oc:tooltip.UpgradeBattery=Erhöht die Energiespeicherkapazität eines Roboters, was ihm erlaubt länger zu arbeiten ohne erneut aufgeladen zu werden. [nl] Kapazität: §f%s§7.
|
oc:tooltip.UpgradeBattery=Erhöht die Energiespeicherkapazität eines Roboters, was ihm erlaubt, länger zu arbeiten, ohne erneut aufgeladen werden zu müssen. [nl] Kapazität: §f%s§7.
|
||||||
oc:tooltip.UpgradeChunkloader=Wenn sich im Wald ein Roboter bewegt, und niemand da ist ihn zu sehen, bewegt er sich dann wirklich? Dieses Upgrade stellt sicher, dass dem so ist. Es hält den Chunk in dem sich der Roboter befindet geladen, verbraucht während es aktiv ist jedoch Energie.
|
oc:tooltip.UpgradeChunkloader=Wenn sich im Wald ein Roboter bewegt, und niemand da ist, ihn zu sehen, bewegt er sich dann wirklich? Dieses Upgrade stellt sicher, dass dem so ist. Es hält den Chunk, in dem sich der Roboter befindet, geladen, verbraucht, während es aktiv ist, jedoch Energie.
|
||||||
oc:tooltip.UpgradeContainerCard=Dieses Behälter-Upgrade erlaubt es eine Karte in einem bereits zusammengebauten Roboter zu installieren und wieder zu entfernen. [nl] Maximale Stufe: §f%s§7.
|
oc:tooltip.UpgradeContainerCard=Dieses Behälter-Upgrade erlaubt es, eine Karte in einem bereits zusammengebauten Roboter zu installieren und wieder zu entfernen. [nl] Maximale Stufe: §f%s§7.
|
||||||
oc:tooltip.UpgradeContainerUpgrade=Dieses Behälter-Upgrade erlaubt es ein Upgrade in einem bereits zusammengebauten Roboter zu installieren und wieder zu entfernen. [nl] Maximale Stufe: §f%s§7.
|
oc:tooltip.UpgradeContainerUpgrade=Dieses Behälter-Upgrade erlaubt es, ein Upgrade in einem bereits zusammengebauten Roboter zu installieren und wieder zu entfernen. [nl] Maximale Stufe: §f%s§7.
|
||||||
oc:tooltip.UpgradeCrafting=Ermöglicht Robotern, in dem oberen linken Bereich ihres Inventars Dinge zu fertigen. Gegenstände müssen so angeordnet sein, wie sie es in einer Werkbank wären.
|
oc:tooltip.UpgradeCrafting=Ermöglicht Robotern, in dem oberen linken Bereich ihres Inventars Dinge zu fertigen. Gegenstände müssen so angeordnet sein, wie sie es in einer Werkbank wären.
|
||||||
oc:tooltip.UpgradeExperience=Dieses Upgrade erlaubt es Robotern durch verschiedene Aktionen Erfahrung zu sammeln. Je mehr Erfahrung ein Roboter hat, desto mehr Energie kann er speichern, desto schneller kann er Blöcke abbauen und desto effizienter kann er mit Werkzeugen umgehen.
|
oc:tooltip.UpgradeExperience=Dieses Upgrade erlaubt es Robotern durch verschiedene Aktionen Erfahrung zu sammeln. Je mehr Erfahrung ein Roboter hat, desto mehr Energie kann er speichern, desto schneller kann er Blöcke abbauen und desto effizienter kann er mit Werkzeugen umgehen.
|
||||||
oc:tooltip.UpgradeGenerator=Kann verwendet werden, um unterwegs Energie aus Brennstoffen zu erzeugen. Verbraucht Gegenstände über einen ihrem Brennwert gemäßen Zeitraum.[nl] §fEffizienz§7: §a%s%%§7
|
oc:tooltip.UpgradeGenerator=Kann verwendet werden, um unterwegs Energie aus Brennstoffen zu erzeugen. Verbraucht Gegenstände über einen ihrem Brennwert gemäßen Zeitraum.[nl] §fEffizienz§7: §a%s%%§7
|
||||||
oc:tooltip.UpgradeInventory=Dieses Upgrade gibt Robotern ein internes Inventar. Ohne ein solches Upgrade können Roboter keine Gegenstände verwahren.
|
oc:tooltip.UpgradeInventory=Dieses Upgrade gibt Robotern ein internes Inventar. Ohne ein solches Upgrade können Roboter keine Gegenstände verwahren.
|
||||||
oc:tooltip.UpgradeInventoryController=Dieses Upgrade erlaubt es dem Roboter präziser mit externen Inventaren zu interagieren, und erlaubt es ihm das angelegte Werkzeug mit einem Gegenstand in seinem Inventar auszutauschen.
|
oc:tooltip.UpgradeInventoryController=Dieses Upgrade erlaubt es dem Roboter, präziser mit externen Inventaren zu interagieren, und erlaubt es ihm, das angelegte Werkzeug mit einem Gegenstand in seinem Inventar auszutauschen.
|
||||||
oc:tooltip.UpgradeNavigation=Erlaubt es Robotern, ihre Position und Ausrichtung zu bestimmen. Die Position ist relativ zur Mitte der Karte, die in diesem Upgrade verbaut wurde.
|
oc:tooltip.UpgradeNavigation=Erlaubt es Robotern, ihre Position und Ausrichtung zu bestimmen. Die Position ist relativ zur Mitte der Karte, die in diesem Upgrade verbaut wurde.
|
||||||
oc:tooltip.UpgradeSign=Erlaubt das Lesen und Schreiben von Text auf Schildern.
|
oc:tooltip.UpgradeSign=Erlaubt das Lesen und Schreiben von Text auf Schildern.
|
||||||
oc:tooltip.UpgradeSolarGenerator=Kann verwendet werden, um unterwegs Energie aus Sonnenlicht zu generieren. Benötigt eine ungehinderte Sicht zum Himmel über dem Roboter. Generiert Energie mit %s%% der Geschwindigkeit eines Stirlingmotors.
|
oc:tooltip.UpgradeSolarGenerator=Kann verwendet werden, um unterwegs Energie aus Sonnenlicht zu generieren. Benötigt eine ungehinderte Sicht zum Himmel über dem Roboter. Generiert Energie mit %s%% der Geschwindigkeit eines Stirlingmotors.
|
||||||
|
@ -52,6 +52,12 @@ opencomputers {
|
|||||||
# down with the actual scale of the hologram.
|
# down with the actual scale of the hologram.
|
||||||
hologramRenderDistance: 64
|
hologramRenderDistance: 64
|
||||||
|
|
||||||
|
# The distance at which to start fading out the hologram (as with
|
||||||
|
# hologramRenderDistance). This is purely cosmetic, to avoid image
|
||||||
|
# disappearing instantly when moving too far away from a projector.
|
||||||
|
# It does not affect performance. Holograms are transparent anyway.
|
||||||
|
hologramFadeStartDistance: 48
|
||||||
|
|
||||||
# This controls how often holograms 'flicker'. This is the chance that it
|
# This controls how often holograms 'flicker'. This is the chance that it
|
||||||
# flickers for *each frame*, meaning if you're running at high FPS you
|
# flickers for *each frame*, meaning if you're running at high FPS you
|
||||||
# may want to lower this a bit, to avoid it flickering too much.
|
# may want to lower this a bit, to avoid it flickering too much.
|
||||||
|
@ -22,6 +22,7 @@ class Settings(config: Config) {
|
|||||||
val robotLabels = config.getBoolean("client.robotLabels")
|
val robotLabels = config.getBoolean("client.robotLabels")
|
||||||
val soundVolume = config.getDouble("client.soundVolume").toFloat max 0 min 2
|
val soundVolume = config.getDouble("client.soundVolume").toFloat max 0 min 2
|
||||||
val fontCharScale = config.getDouble("client.fontCharScale") max 0.5 min 2
|
val fontCharScale = config.getDouble("client.fontCharScale") max 0.5 min 2
|
||||||
|
val hologramFadeStartDistance = config.getDouble("client.hologramFadeStartDistance") max 0
|
||||||
val hologramRenderDistance = config.getDouble("client.hologramRenderDistance") max 0
|
val hologramRenderDistance = config.getDouble("client.hologramRenderDistance") max 0
|
||||||
val hologramFlickerFrequency = config.getDouble("client.hologramFlickerFrequency") max 0
|
val hologramFlickerFrequency = config.getDouble("client.hologramFlickerFrequency") max 0
|
||||||
|
|
||||||
|
@ -160,7 +160,6 @@ object PacketHandler extends CommonPacketHandler {
|
|||||||
p.readTileEntity[Hologram]() match {
|
p.readTileEntity[Hologram]() match {
|
||||||
case Some(t) =>
|
case Some(t) =>
|
||||||
t.scale = p.readDouble()
|
t.scale = p.readDouble()
|
||||||
t.dirty = true
|
|
||||||
case _ => // Invalid packet.
|
case _ => // Invalid packet.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,21 +8,33 @@ import li.cil.oc.client.Textures
|
|||||||
import li.cil.oc.common.tileentity.Hologram
|
import li.cil.oc.common.tileentity.Hologram
|
||||||
import li.cil.oc.util.RenderState
|
import li.cil.oc.util.RenderState
|
||||||
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer
|
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer
|
||||||
import net.minecraft.client.renderer.{GLAllocation, Tessellator}
|
|
||||||
import net.minecraft.tileentity.TileEntity
|
import net.minecraft.tileentity.TileEntity
|
||||||
import org.lwjgl.opengl.GL11
|
import org.lwjgl.opengl.{GL15, GL11}
|
||||||
import scala.util.Random
|
import scala.util.Random
|
||||||
|
import org.lwjgl.BufferUtils
|
||||||
import li.cil.oc.Settings
|
import li.cil.oc.Settings
|
||||||
|
import java.nio.IntBuffer
|
||||||
|
|
||||||
object HologramRenderer extends TileEntitySpecialRenderer with Callable[Int] with RemovalListener[TileEntity, Int] {
|
object HologramRenderer extends TileEntitySpecialRenderer with Callable[(Int, IntBuffer)] with RemovalListener[TileEntity, (Int, IntBuffer)] {
|
||||||
val random = new Random()
|
private val random = new Random()
|
||||||
|
|
||||||
/** We cache the display lists for the projectors we render for performance. */
|
/** We cache the VBOs for the projectors we render for performance. */
|
||||||
val cache = com.google.common.cache.CacheBuilder.newBuilder().
|
private val cache = com.google.common.cache.CacheBuilder.newBuilder().
|
||||||
expireAfterAccess(2, TimeUnit.SECONDS).
|
expireAfterAccess(10, TimeUnit.SECONDS).
|
||||||
removalListener(this).
|
removalListener(this).
|
||||||
asInstanceOf[CacheBuilder[Hologram, Int]].
|
asInstanceOf[CacheBuilder[Hologram, (Int, IntBuffer)]].
|
||||||
build[Hologram, Int]()
|
build[Hologram, (Int, IntBuffer)]()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common for all holograms. Holds the vertex positions, texture
|
||||||
|
* coordinates and normals information. Layout is: u v nx ny nz x y z
|
||||||
|
*
|
||||||
|
* WARNING: this optimization only works if all the holograms have the
|
||||||
|
* same dimensions (in voxels). If we ever need holograms of different
|
||||||
|
* sizes we could probably just fake that by making the outer layers
|
||||||
|
* immutable (i.e. always empty).
|
||||||
|
*/
|
||||||
|
private var commonBuffer = 0
|
||||||
|
|
||||||
/** Used to pass the current screen along to call(). */
|
/** Used to pass the current screen along to call(). */
|
||||||
private var hologram: Hologram = null
|
private var hologram: Hologram = null
|
||||||
@ -33,10 +45,16 @@ object HologramRenderer extends TileEntitySpecialRenderer with Callable[Int] wit
|
|||||||
hologram = te.asInstanceOf[Hologram]
|
hologram = te.asInstanceOf[Hologram]
|
||||||
if (!hologram.hasPower) return
|
if (!hologram.hasPower) return
|
||||||
|
|
||||||
|
GL11.glPushClientAttrib(GL11.GL_ALL_CLIENT_ATTRIB_BITS)
|
||||||
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS)
|
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS)
|
||||||
RenderState.makeItBlend()
|
RenderState.makeItBlend()
|
||||||
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE)
|
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE)
|
||||||
|
|
||||||
|
val playerDistSq = x * x + y * y + z * z
|
||||||
|
val maxDistSq = hologram.getMaxRenderDistanceSquared
|
||||||
|
val fadeDistSq = hologram.getFadeStartDistanceSquared
|
||||||
|
RenderState.setBlendAlpha(0.75f * (if (playerDistSq > fadeDistSq) math.max(0, 1 - ((playerDistSq - fadeDistSq) / (maxDistSq - fadeDistSq)).toFloat) else 1))
|
||||||
|
|
||||||
GL11.glPushMatrix()
|
GL11.glPushMatrix()
|
||||||
GL11.glTranslated(x + 0.5, y + 0.5, z + 0.5)
|
GL11.glTranslated(x + 0.5, y + 0.5, z + 0.5)
|
||||||
GL11.glScaled(1.001, 1.001, 1.001) // Avoid z-fighting with other blocks.
|
GL11.glScaled(1.001, 1.001, 1.001) // Avoid z-fighting with other blocks.
|
||||||
@ -48,50 +66,73 @@ object HologramRenderer extends TileEntitySpecialRenderer with Callable[Int] wit
|
|||||||
GL11.glTranslated(random.nextGaussian() * 0.01, random.nextGaussian() * 0.01, random.nextGaussian() * 0.01)
|
GL11.glTranslated(random.nextGaussian() * 0.01, random.nextGaussian() * 0.01, random.nextGaussian() * 0.01)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// After the below scaling, hologram is drawn inside a [0..48]x[0..32]x[0..48] box
|
||||||
|
GL11.glScaled(hologram.scale / 16f, hologram.scale / 16f, hologram.scale / 16f)
|
||||||
|
|
||||||
|
bindTexture(Textures.blockHologram)
|
||||||
|
|
||||||
|
// Normalize normals (yes, glScale scales them too).
|
||||||
|
GL11.glEnable(GL11.GL_NORMALIZE)
|
||||||
|
|
||||||
|
val sx = (x + 0.5) * hologram.scale
|
||||||
|
val sy = -(y + 0.5) * hologram.scale
|
||||||
|
val sz = (z + 0.5) * hologram.scale
|
||||||
|
if (sx >= -1.5 && sx <= 1.5 && sz >= -1.5 && sz <= 1.5 && sy >= 0 && sy <= 2) {
|
||||||
|
// Camera is inside the hologram.
|
||||||
|
GL11.glDisable(GL11.GL_CULL_FACE)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Camera is outside the hologram.
|
||||||
|
GL11.glEnable(GL11.GL_CULL_FACE)
|
||||||
|
GL11.glCullFace(GL11.GL_BACK)
|
||||||
|
}
|
||||||
|
|
||||||
// We do two passes here to avoid weird transparency effects: in the first
|
// We do two passes here to avoid weird transparency effects: in the first
|
||||||
// pass we find the front-most fragment, in the second we actually draw it.
|
// pass we find the front-most fragment, in the second we actually draw it.
|
||||||
// TODO proper transparency shader? depth peeling e.g.
|
// When we don't do this the hologram will look different from different
|
||||||
|
// angles (because some faces will shine through sometimes and sometimes
|
||||||
|
// they won't), so a more... consistent look is desirable.
|
||||||
|
val (glBuffer, dataBuffer) = cache.get(hologram, this)
|
||||||
GL11.glColorMask(false, false, false, false)
|
GL11.glColorMask(false, false, false, false)
|
||||||
GL11.glDepthMask(true)
|
GL11.glDepthMask(true)
|
||||||
val list = cache.get(hologram, this)
|
draw(glBuffer, dataBuffer)
|
||||||
compileOrDraw(list)
|
|
||||||
GL11.glColorMask(true, true, true, true)
|
GL11.glColorMask(true, true, true, true)
|
||||||
GL11.glDepthFunc(GL11.GL_EQUAL)
|
GL11.glDepthFunc(GL11.GL_EQUAL)
|
||||||
compileOrDraw(list)
|
draw(glBuffer, dataBuffer)
|
||||||
|
|
||||||
GL11.glPopMatrix()
|
GL11.glPopMatrix()
|
||||||
GL11.glPopAttrib()
|
GL11.glPopAttrib()
|
||||||
|
GL11.glPopClientAttrib()
|
||||||
|
|
||||||
RenderState.checkError(getClass.getName + ".renderTileEntityAt: leaving")
|
RenderState.checkError(getClass.getName + ".renderTileEntityAt: leaving")
|
||||||
}
|
}
|
||||||
|
|
||||||
def compileOrDraw(list: Int) = if (hologram.dirty) {
|
def draw(glBuffer: Int, dataBuffer: IntBuffer) {
|
||||||
val doCompile = !RenderState.compilingDisplayList
|
initialize()
|
||||||
if (doCompile) {
|
validate(glBuffer, dataBuffer)
|
||||||
hologram.dirty = false
|
publish(glBuffer)
|
||||||
GL11.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
def value(hx: Int, hy: Int, hz: Int) = if (hx >= 0 && hy >= 0 && hz >= 0 && hx < hologram.width && hy < hologram.height && hz < hologram.width) hologram.getColor(hx, hy, hz) else 0
|
private def initialize() {
|
||||||
|
// First run only, create structure information.
|
||||||
|
if (commonBuffer == 0) {
|
||||||
|
commonBuffer = GL15.glGenBuffers()
|
||||||
|
|
||||||
def isSolid(hx: Int, hy: Int, hz: Int) = value(hx, hy, hz) != 0
|
val data = BufferUtils.createFloatBuffer(hologram.width * hologram.width * hologram.height * 24 * (2 + 3 + 3))
|
||||||
|
def addVertex(x: Int, y: Int, z: Int, u: Int, v: Int, nx: Int, ny: Int, nz: Int) {
|
||||||
bindTexture(Textures.blockHologram)
|
data.put(u)
|
||||||
val t = Tessellator.instance
|
data.put(v)
|
||||||
t.startDrawingQuads()
|
data.put(nx)
|
||||||
|
data.put(ny)
|
||||||
// TODO merge quads for better rendering performance
|
data.put(nz)
|
||||||
val s = 1f / 16f * hologram.scale
|
data.put(x)
|
||||||
for (hx <- 0 until hologram.width) {
|
data.put(y)
|
||||||
val wx = hx * s
|
data.put(z)
|
||||||
for (hz <- 0 until hologram.width) {
|
}
|
||||||
val wz = hz * s
|
|
||||||
for (hy <- 0 until hologram.height) {
|
|
||||||
val wy = hy * s
|
|
||||||
|
|
||||||
if (isSolid(hx, hy, hz)) {
|
|
||||||
t.setColorRGBA_I(hologram.colors(value(hx, hy, hz) - 1), 192)
|
|
||||||
|
|
||||||
|
for (x <- 0 until hologram.width) {
|
||||||
|
for (z <- 0 until hologram.width) {
|
||||||
|
for (y <- 0 until hologram.height) {
|
||||||
/*
|
/*
|
||||||
0---1
|
0---1
|
||||||
| N |
|
| N |
|
||||||
@ -103,82 +144,168 @@ object HologramRenderer extends TileEntitySpecialRenderer with Callable[Int] wit
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// South
|
// South
|
||||||
if (!isSolid(hx, hy, hz + 1)) {
|
addVertex(x + 1, y + 1, z + 1, 0, 0, 0, 0, 1) // 5
|
||||||
t.setNormal(0, 0, 1)
|
addVertex(x + 0, y + 1, z + 1, 1, 0, 0, 0, 1) // 4
|
||||||
t.addVertexWithUV(wx + s, wy + s, wz + s, 0, 0) // 5
|
addVertex(x + 0, y + 0, z + 1, 1, 1, 0, 0, 1) // 7
|
||||||
t.addVertexWithUV(wx + 0, wy + s, wz + s, 1, 0) // 4
|
addVertex(x + 1, y + 0, z + 1, 0, 1, 0, 0, 1) // 6
|
||||||
t.addVertexWithUV(wx + 0, wy + 0, wz + s, 1, 1) // 7
|
|
||||||
t.addVertexWithUV(wx + s, wy + 0, wz + s, 0, 1) // 6
|
|
||||||
}
|
|
||||||
// North
|
// North
|
||||||
if (!isSolid(hx, hy, hz - 1)) {
|
addVertex(x + 1, y + 0, z + 0, 0, 0, 0, 0, -1) // 3
|
||||||
t.setNormal(0, 0, -1)
|
addVertex(x + 0, y + 0, z + 0, 1, 0, 0, 0, -1) // 2
|
||||||
t.addVertexWithUV(wx + s, wy + 0, wz + 0, 0, 0) // 3
|
addVertex(x + 0, y + 1, z + 0, 1, 1, 0, 0, -1) // 1
|
||||||
t.addVertexWithUV(wx + 0, wy + 0, wz + 0, 1, 0) // 2
|
addVertex(x + 1, y + 1, z + 0, 0, 1, 0, 0, -1) // 0
|
||||||
t.addVertexWithUV(wx + 0, wy + s, wz + 0, 1, 1) // 1
|
|
||||||
t.addVertexWithUV(wx + s, wy + s, wz + 0, 0, 1) // 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// East
|
// East
|
||||||
if (!isSolid(hx + 1, hy, hz)) {
|
addVertex(x + 1, y + 1, z + 1, 1, 0, 1, 0, 0) // 5
|
||||||
t.setNormal(1, 0, 0)
|
addVertex(x + 1, y + 0, z + 1, 1, 1, 1, 0, 0) // 6
|
||||||
t.addVertexWithUV(wx + s, wy + s, wz + s, 1, 0) // 5
|
addVertex(x + 1, y + 0, z + 0, 0, 1, 1, 0, 0) // 3
|
||||||
t.addVertexWithUV(wx + s, wy + 0, wz + s, 1, 1) // 6
|
addVertex(x + 1, y + 1, z + 0, 0, 0, 1, 0, 0) // 0
|
||||||
t.addVertexWithUV(wx + s, wy + 0, wz + 0, 0, 1) // 3
|
|
||||||
t.addVertexWithUV(wx + s, wy + s, wz + 0, 0, 0) // 0
|
|
||||||
}
|
|
||||||
// West
|
// West
|
||||||
if (!isSolid(hx - 1, hy, hz)) {
|
addVertex(x + 0, y + 0, z + 1, 1, 0, -1, 0, 0) // 7
|
||||||
t.setNormal(-1, 0, 0)
|
addVertex(x + 0, y + 1, z + 1, 1, 1, -1, 0, 0) // 4
|
||||||
t.addVertexWithUV(wx + 0, wy + 0, wz + s, 1, 0) // 7
|
addVertex(x + 0, y + 1, z + 0, 0, 1, -1, 0, 0) // 1
|
||||||
t.addVertexWithUV(wx + 0, wy + s, wz + s, 1, 1) // 4
|
addVertex(x + 0, y + 0, z + 0, 0, 0, -1, 0, 0) // 2
|
||||||
t.addVertexWithUV(wx + 0, wy + s, wz + 0, 0, 1) // 1
|
|
||||||
t.addVertexWithUV(wx + 0, wy + 0, wz + 0, 0, 0) // 2
|
|
||||||
}
|
|
||||||
|
|
||||||
// Up
|
// Up
|
||||||
if (!isSolid(hx, hy + 1, hz)) {
|
addVertex(x + 1, y + 1, z + 0, 0, 0, 0, 1, 0) // 0
|
||||||
t.setNormal(0, 1, 0)
|
addVertex(x + 0, y + 1, z + 0, 1, 0, 0, 1, 0) // 1
|
||||||
t.addVertexWithUV(wx + s, wy + s, wz + 0, 0, 0) // 0
|
addVertex(x + 0, y + 1, z + 1, 1, 1, 0, 1, 0) // 4
|
||||||
t.addVertexWithUV(wx + 0, wy + s, wz + 0, 1, 0) // 1
|
addVertex(x + 1, y + 1, z + 1, 0, 1, 0, 1, 0) // 5
|
||||||
t.addVertexWithUV(wx + 0, wy + s, wz + s, 1, 1) // 4
|
|
||||||
t.addVertexWithUV(wx + s, wy + s, wz + s, 0, 1) // 5
|
|
||||||
}
|
|
||||||
// Down
|
// Down
|
||||||
if (!isSolid(hx, hy - 1, hz)) {
|
addVertex(x + 1, y + 0, z + 1, 0, 0, 0, -1, 0) // 6
|
||||||
t.setNormal(0, -1, 0)
|
addVertex(x + 0, y + 0, z + 1, 1, 0, 0, -1, 0) // 7
|
||||||
t.addVertexWithUV(wx + s, wy + 0, wz + s, 0, 0) // 6
|
addVertex(x + 0, y + 0, z + 0, 1, 1, 0, -1, 0) // 2
|
||||||
t.addVertexWithUV(wx + 0, wy + 0, wz + s, 1, 0) // 7
|
addVertex(x + 1, y + 0, z + 0, 0, 1, 0, -1, 0) // 3
|
||||||
t.addVertexWithUV(wx + 0, wy + 0, wz + 0, 1, 1) // 2
|
}
|
||||||
t.addVertexWithUV(wx + s, wy + 0, wz + 0, 0, 1) // 3
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Important! OpenGL will start reading from the current buffer position.
|
||||||
|
data.rewind()
|
||||||
|
|
||||||
|
// This buffer never ever changes, so static is the way to go.
|
||||||
|
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, commonBuffer)
|
||||||
|
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STATIC_DRAW)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def validate(glBuffer: Int, dataBuffer: IntBuffer) {
|
||||||
|
// Refresh indexes when the hologram's data changed.
|
||||||
|
if (hologram.dirty) {
|
||||||
|
def value(hx: Int, hy: Int, hz: Int) = if (hx >= 0 && hy >= 0 && hz >= 0 && hx < hologram.width && hy < hologram.height && hz < hologram.width) hologram.getColor(hx, hy, hz) else 0
|
||||||
|
|
||||||
|
def isSolid(hx: Int, hy: Int, hz: Int) = value(hx, hy, hz) != 0
|
||||||
|
|
||||||
|
def addFace(index: Int, color: Int) {
|
||||||
|
dataBuffer.put(index)
|
||||||
|
dataBuffer.put(index + 1)
|
||||||
|
dataBuffer.put(index + 2)
|
||||||
|
dataBuffer.put(index + 3)
|
||||||
|
|
||||||
|
dataBuffer.put(index, color)
|
||||||
|
dataBuffer.put(index + 1, color)
|
||||||
|
dataBuffer.put(index + 2, color)
|
||||||
|
dataBuffer.put(index + 3, color)
|
||||||
|
|
||||||
|
hologram.visibleQuads += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy color information, identify which quads to render and prepare data for glDrawElements
|
||||||
|
hologram.visibleQuads = 0
|
||||||
|
var index = 0
|
||||||
|
dataBuffer.position(hologram.width * hologram.width * hologram.height * 6 * 4)
|
||||||
|
for (hx <- 0 until hologram.width) {
|
||||||
|
for (hz <- 0 until hologram.width) {
|
||||||
|
for (hy <- 0 until hologram.height) {
|
||||||
|
// Do we need to draw at least one face?
|
||||||
|
if (isSolid(hx, hy, hz)) {
|
||||||
|
// Yes, get the color of the voxel.
|
||||||
|
val color = hologram.colors(value(hx, hy, hz) - 1)
|
||||||
|
|
||||||
|
// South
|
||||||
|
if (!isSolid(hx, hy, hz + 1)) {
|
||||||
|
addFace(index, color)
|
||||||
|
}
|
||||||
|
index += 4
|
||||||
|
// North
|
||||||
|
if (!isSolid(hx, hy, hz - 1)) {
|
||||||
|
addFace(index, color)
|
||||||
|
}
|
||||||
|
index += 4
|
||||||
|
|
||||||
|
// East
|
||||||
|
if (!isSolid(hx + 1, hy, hz)) {
|
||||||
|
addFace(index, color)
|
||||||
|
}
|
||||||
|
index += 4
|
||||||
|
// West
|
||||||
|
if (!isSolid(hx - 1, hy, hz)) {
|
||||||
|
addFace(index, color)
|
||||||
|
}
|
||||||
|
index += 4
|
||||||
|
|
||||||
|
// Up
|
||||||
|
if (!isSolid(hx, hy + 1, hz)) {
|
||||||
|
addFace(index, color)
|
||||||
|
}
|
||||||
|
index += 4
|
||||||
|
// Down
|
||||||
|
if (!isSolid(hx, hy - 1, hz)) {
|
||||||
|
addFace(index, color)
|
||||||
|
}
|
||||||
|
index += 4
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// No, skip all associated indices.
|
||||||
|
index += 6 * 4
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Important! OpenGL will start reading from the current buffer position.
|
||||||
|
dataBuffer.rewind()
|
||||||
|
|
||||||
|
// This buffer can be updated quite frequently, so dynamic seems sensible.
|
||||||
|
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, glBuffer)
|
||||||
|
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, dataBuffer, GL15.GL_DYNAMIC_DRAW)
|
||||||
|
|
||||||
|
hologram.dirty = false
|
||||||
}
|
}
|
||||||
|
|
||||||
t.draw()
|
|
||||||
|
|
||||||
if (doCompile) {
|
|
||||||
GL11.glEndList()
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
else GL11.glCallList(list)
|
|
||||||
|
private def publish(glBuffer: Int) {
|
||||||
|
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, commonBuffer)
|
||||||
|
GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY)
|
||||||
|
GL11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY)
|
||||||
|
GL11.glEnableClientState(GL11.GL_NORMAL_ARRAY)
|
||||||
|
GL11.glInterleavedArrays(GL11.GL_T2F_N3F_V3F, 0, 0)
|
||||||
|
|
||||||
|
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, glBuffer)
|
||||||
|
GL11.glEnableClientState(GL11.GL_COLOR_ARRAY)
|
||||||
|
GL11.glColorPointer(3, GL11.GL_UNSIGNED_BYTE, 4, 0)
|
||||||
|
|
||||||
|
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, glBuffer)
|
||||||
|
GL11.glDrawElements(GL11.GL_QUADS, hologram.visibleQuads * 4, GL11.GL_UNSIGNED_INT, hologram.width * hologram.width * hologram.height * 6 * 4 * 4)
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
// Cache
|
// Cache
|
||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
def call = {
|
def call = {
|
||||||
val list = GLAllocation.generateDisplayLists(1)
|
val glBuffer = GL15.glGenBuffers()
|
||||||
hologram.dirty = true // Force compilation.
|
val dataBuffer = BufferUtils.createIntBuffer(hologram.width * hologram.width * hologram.height * 6 * 4 * 2)
|
||||||
list
|
|
||||||
|
// Force re-indexing.
|
||||||
|
hologram.dirty = true
|
||||||
|
|
||||||
|
(glBuffer, dataBuffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
def onRemoval(e: RemovalNotification[TileEntity, Int]) {
|
def onRemoval(e: RemovalNotification[TileEntity, (Int, IntBuffer)]) {
|
||||||
GLAllocation.deleteDisplayLists(e.getValue)
|
val (glBuffer, dataBuffer) = e.getValue
|
||||||
|
GL15.glDeleteBuffers(glBuffer)
|
||||||
|
dataBuffer.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
|
@ -34,6 +34,10 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w
|
|||||||
// Whether we need to send an update packet/recompile our display list.
|
// Whether we need to send an update packet/recompile our display list.
|
||||||
var dirty = false
|
var dirty = false
|
||||||
|
|
||||||
|
// Store it here for convenience, this is the number of visible voxel faces
|
||||||
|
// as determined in the last VBO index update. See HologramRenderer.
|
||||||
|
var visibleQuads = 0
|
||||||
|
|
||||||
// Interval of dirty columns.
|
// Interval of dirty columns.
|
||||||
var dirtyFromX = Int.MaxValue
|
var dirtyFromX = Int.MaxValue
|
||||||
var dirtyUntilX = -1
|
var dirtyUntilX = -1
|
||||||
@ -45,7 +49,9 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w
|
|||||||
|
|
||||||
var hasPower = true
|
var hasPower = true
|
||||||
|
|
||||||
val colorsByTier = Array(Array(0x00FF00), Array(0xFF0000, 0x00FF00, 0x0000FF))
|
val colorsByTier = Array(Array(0x00FF00), Array(0x0000FF, 0x00FF00, 0xFF0000)) // 0xBBGGRR for rendering convenience
|
||||||
|
|
||||||
|
// This is a def and not a val for loading (where the tier comes from the nbt and is always 0 here).
|
||||||
def colors = colorsByTier(tier)
|
def colors = colorsByTier(tier)
|
||||||
|
|
||||||
def getColor(x: Int, y: Int, z: Int) = {
|
def getColor(x: Int, y: Int, z: Int) = {
|
||||||
@ -203,7 +209,8 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w
|
|||||||
def getPaletteColor(computer: Context, args: Arguments): Array[AnyRef] = {
|
def getPaletteColor(computer: Context, args: Arguments): Array[AnyRef] = {
|
||||||
val index = args.checkInteger(0)
|
val index = args.checkInteger(0)
|
||||||
if (index < 0 || index >= colors.length) throw new ArrayIndexOutOfBoundsException()
|
if (index < 0 || index >= colors.length) throw new ArrayIndexOutOfBoundsException()
|
||||||
result(colors(index))
|
// Colors are stored as 0xAABBGGRR for rendering convenience, so convert them.
|
||||||
|
result(convertColor(colors(index)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Callback(doc = """function(index:number, value:number):number -- Set the color defined for the specified value.""")
|
@Callback(doc = """function(index:number, value:number):number -- Set the color defined for the specified value.""")
|
||||||
@ -212,7 +219,9 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w
|
|||||||
if (index < 0 || index >= colors.length) throw new ArrayIndexOutOfBoundsException()
|
if (index < 0 || index >= colors.length) throw new ArrayIndexOutOfBoundsException()
|
||||||
val value = args.checkInteger(1)
|
val value = args.checkInteger(1)
|
||||||
val oldValue = colors(index)
|
val oldValue = colors(index)
|
||||||
colors(index) = value & 0xFFFFFF
|
// Change byte order here to allow passing stored color to OpenGL "as-is"
|
||||||
|
// (as whole Int, i.e. 0xAABBGGRR, alpha is unused but present for alignment)
|
||||||
|
colors(index) = convertColor(value)
|
||||||
ServerPacketSender.sendHologramColor(this, index, value)
|
ServerPacketSender.sendHologramColor(this, index, value)
|
||||||
result(oldValue)
|
result(oldValue)
|
||||||
}
|
}
|
||||||
@ -237,6 +246,10 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w
|
|||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def convertColor(color: Int) = {
|
||||||
|
((color & 0x0000FF) << 16) | (color & 0x00FF00) | ((color & 0xFF0000) >>> 16)
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
override def canUpdate = isServer
|
override def canUpdate = isServer
|
||||||
@ -275,6 +288,7 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w
|
|||||||
override def shouldRenderInPass(pass: Int) = pass == 1
|
override def shouldRenderInPass(pass: Int) = pass == 1
|
||||||
|
|
||||||
override def getMaxRenderDistanceSquared = scale / Settings.hologramMaxScaleByTier.max * Settings.get.hologramRenderDistance * Settings.get.hologramRenderDistance
|
override def getMaxRenderDistanceSquared = scale / Settings.hologramMaxScaleByTier.max * Settings.get.hologramRenderDistance * Settings.get.hologramRenderDistance
|
||||||
|
def getFadeStartDistanceSquared = scale / Settings.hologramMaxScaleByTier.max * Settings.get.hologramFadeStartDistance * Settings.get.hologramFadeStartDistance
|
||||||
|
|
||||||
override def getRenderBoundingBox = AxisAlignedBB.getAABBPool.getAABB(xCoord + 0.5 - 1.5 * scale, yCoord, zCoord - scale, xCoord + 0.5 + 1.5 * scale, yCoord + 0.25 + 2 * scale, zCoord + 0.5 + 1.5 * scale)
|
override def getRenderBoundingBox = AxisAlignedBB.getAABBPool.getAABB(xCoord + 0.5 - 1.5 * scale, yCoord, zCoord - scale, xCoord + 0.5 + 1.5 * scale, yCoord + 0.25 + 2 * scale, zCoord + 0.5 + 1.5 * scale)
|
||||||
|
|
||||||
@ -284,7 +298,7 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w
|
|||||||
tier = nbt.getByte(Settings.namespace + "tier") max 0 min 1
|
tier = nbt.getByte(Settings.namespace + "tier") max 0 min 1
|
||||||
super.readFromNBT(nbt)
|
super.readFromNBT(nbt)
|
||||||
nbt.getIntArray(Settings.namespace + "volume").copyToArray(volume)
|
nbt.getIntArray(Settings.namespace + "volume").copyToArray(volume)
|
||||||
nbt.getIntArray(Settings.namespace + "colors").copyToArray(colors)
|
nbt.getIntArray(Settings.namespace + "colors").map(convertColor).copyToArray(colors)
|
||||||
scale = nbt.getDouble(Settings.namespace + "scale")
|
scale = nbt.getDouble(Settings.namespace + "scale")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,7 +306,7 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w
|
|||||||
nbt.setByte(Settings.namespace + "tier", tier.toByte)
|
nbt.setByte(Settings.namespace + "tier", tier.toByte)
|
||||||
super.writeToNBT(nbt)
|
super.writeToNBT(nbt)
|
||||||
nbt.setIntArray(Settings.namespace + "volume", volume)
|
nbt.setIntArray(Settings.namespace + "volume", volume)
|
||||||
nbt.setIntArray(Settings.namespace + "colors", colors)
|
nbt.setIntArray(Settings.namespace + "colors", colors.map(convertColor))
|
||||||
nbt.setDouble(Settings.namespace + "scale", scale)
|
nbt.setDouble(Settings.namespace + "scale", scale)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,7 +317,6 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w
|
|||||||
nbt.getIntArray("colors").copyToArray(colors)
|
nbt.getIntArray("colors").copyToArray(colors)
|
||||||
scale = nbt.getDouble("scale")
|
scale = nbt.getDouble("scale")
|
||||||
hasPower = nbt.getBoolean("hasPower")
|
hasPower = nbt.getBoolean("hasPower")
|
||||||
dirty = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override def writeToNBTForClient(nbt: NBTTagCompound) {
|
override def writeToNBTForClient(nbt: NBTTagCompound) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user