Conflicts:
	src/main/resources/reference.conf
	src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRenderer.scala
This commit is contained in:
Florian Nücke 2014-06-10 15:43:10 +02:00
commit 234b33f474
5 changed files with 216 additions and 93 deletions

View File

@ -67,6 +67,12 @@ opencomputers {
# down with the actual scale of the hologram.
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
# 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.

View File

@ -31,6 +31,7 @@ class Settings(config: Config) {
val robotLabels = config.getBoolean("client.robotLabels")
val soundVolume = config.getDouble("client.soundVolume").toFloat max 0 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 hologramFlickerFrequency = config.getDouble("client.hologramFlickerFrequency") max 0

View File

@ -153,7 +153,6 @@ class PacketHandler extends CommonPacketHandler {
p.readTileEntity[Hologram]() match {
case Some(t) =>
t.scale = p.readDouble()
t.dirty = true
case _ => // Invalid packet.
}

View File

@ -8,18 +8,18 @@ import li.cil.oc.client.Textures
import li.cil.oc.common.tileentity.Hologram
import li.cil.oc.util.RenderState
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer
import net.minecraft.client.renderer.{GLAllocation, Tessellator}
import net.minecraft.tileentity.TileEntity
import org.lwjgl.opengl.GL11
import org.lwjgl.opengl.{GL15, GL11}
import scala.util.Random
import org.lwjgl.BufferUtils
import li.cil.oc.Settings
object HologramRenderer extends TileEntitySpecialRenderer with Callable[Int] with RemovalListener[TileEntity, Int] with ITickHandler {
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().
expireAfterAccess(2, TimeUnit.SECONDS).
expireAfterAccess(10, TimeUnit.SECONDS).
removalListener(this).
asInstanceOf[CacheBuilder[Hologram, Int]].
build[Hologram, Int]()
@ -37,6 +37,11 @@ object HologramRenderer extends TileEntitySpecialRenderer with Callable[Int] wit
RenderState.makeItBlend()
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.glTranslated(x + 0.5, y + 0.5, z + 0.5)
GL11.glScaled(1.001, 1.001, 1.001) // Avoid z-fighting with other blocks.
@ -48,16 +53,21 @@ object HologramRenderer extends TileEntitySpecialRenderer with Callable[Int] wit
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)
// 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.
// TODO proper transparency shader? depth peeling e.g.
// evg-zhabotinsky: I'd rather not do it. Anyway it won't work for multiple holograms.
// Also I commented out the first pass to see what it will look like and I prefer it the way it is now.
GL11.glColorMask(false, false, false, false)
GL11.glDepthMask(true)
val list = cache.get(hologram, this)
compileOrDraw(list)
val privateBuf = cache.get(hologram, this)
compileOrDraw(privateBuf)
GL11.glColorMask(true, true, true, true)
GL11.glDepthFunc(GL11.GL_EQUAL)
compileOrDraw(list)
compileOrDraw(privateBuf)
GL11.glPopMatrix()
GL11.glPopAttrib()
@ -65,33 +75,30 @@ object HologramRenderer extends TileEntitySpecialRenderer with Callable[Int] wit
RenderState.checkError(getClass.getName + ".renderTileEntityAt: leaving")
}
def compileOrDraw(list: Int) = if (hologram.dirty) {
val doCompile = !RenderState.compilingDisplayList
if (doCompile) {
hologram.dirty = false
GL11.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE)
val compileOrDraw = {
// WARNING works only if all the holograms have the same dimensions (in voxels)
var commonBuffer = 0 // Common for all holograms (a-la static variable)
(privateBuf: Int) => {
// Save current state (don't forget to restore)
GL11.glPushClientAttrib(GL11.GL_ALL_CLIENT_ATTRIB_BITS)
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS)
if (commonBuffer == 0) { // First run only
commonBuffer = GL15.glGenBuffers()
var tmpBuf = BufferUtils.createFloatBuffer(hologram.width * hologram.width * hologram.height * 24 * (2 + 3 + 3))
def newVert = (x: Int, y: Int, z: Int, u: Int, v: Int, nx: Int, ny: Int, nz: Int) => {
tmpBuf.put(u)
tmpBuf.put(v)
tmpBuf.put(nx)
tmpBuf.put(ny)
tmpBuf.put(nz)
tmpBuf.put(x)
tmpBuf.put(y)
tmpBuf.put(z)
}
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
bindTexture(Textures.blockHologram)
val t = Tessellator.instance
t.startDrawingQuads()
// TODO merge quads for better rendering performance
val s = 1f / 16f * hologram.scale
for (hx <- 0 until hologram.width) {
val wx = hx * s
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
| N |
@ -103,82 +110,185 @@ object HologramRenderer extends TileEntitySpecialRenderer with Callable[Int] wit
*/
// South
if (!isSolid(hx, hy, hz + 1)) {
t.setNormal(0, 0, 1)
t.addVertexWithUV(wx + s, wy + s, wz + s, 0, 0) // 5
t.addVertexWithUV(wx + 0, wy + s, wz + s, 1, 0) // 4
t.addVertexWithUV(wx + 0, wy + 0, wz + s, 1, 1) // 7
t.addVertexWithUV(wx + s, wy + 0, wz + s, 0, 1) // 6
}
// North
if (!isSolid(hx, hy, hz - 1)) {
t.setNormal(0, 0, -1)
t.addVertexWithUV(wx + s, wy + 0, wz + 0, 0, 0) // 3
t.addVertexWithUV(wx + 0, wy + 0, wz + 0, 1, 0) // 2
t.addVertexWithUV(wx + 0, wy + s, wz + 0, 1, 1) // 1
t.addVertexWithUV(wx + s, wy + s, wz + 0, 0, 1) // 0
}
newVert(x + 1, y + 1, z + 1, 0, 0, 0, 0, 1) // 5
newVert(x + 0, y + 1, z + 1, 1, 0, 0, 0, 1) // 4
newVert(x + 0, y + 0, z + 1, 1, 1, 0, 0, 1) // 7
newVert(x + 1, y + 0, z + 1, 0, 1, 0, 0, 1) // 6
// North
newVert(x + 1, y + 0, z + 0, 0, 0, 0, 0, -1) // 3
newVert(x + 0, y + 0, z + 0, 1, 0, 0, 0, -1) // 2
newVert(x + 0, y + 1, z + 0, 1, 1, 0, 0, -1) // 1
newVert(x + 1, y + 1, z + 0, 0, 1, 0, 0, -1) // 0
// East
if (!isSolid(hx + 1, hy, hz)) {
t.setNormal(1, 0, 0)
t.addVertexWithUV(wx + s, wy + s, wz + s, 1, 0) // 5
t.addVertexWithUV(wx + s, wy + 0, wz + s, 1, 1) // 6
t.addVertexWithUV(wx + s, wy + 0, wz + 0, 0, 1) // 3
t.addVertexWithUV(wx + s, wy + s, wz + 0, 0, 0) // 0
}
// West
if (!isSolid(hx - 1, hy, hz)) {
t.setNormal(-1, 0, 0)
t.addVertexWithUV(wx + 0, wy + 0, wz + s, 1, 0) // 7
t.addVertexWithUV(wx + 0, wy + s, wz + s, 1, 1) // 4
t.addVertexWithUV(wx + 0, wy + s, wz + 0, 0, 1) // 1
t.addVertexWithUV(wx + 0, wy + 0, wz + 0, 0, 0) // 2
}
// East
newVert(x + 1, y + 1, z + 1, 1, 0, 1, 0, 0) // 5
newVert(x + 1, y + 0, z + 1, 1, 1, 1, 0, 0) // 6
newVert(x + 1, y + 0, z + 0, 0, 1, 1, 0, 0) // 3
newVert(x + 1, y + 1, z + 0, 0, 0, 1, 0, 0) // 0
// West
newVert(x + 0, y + 0, z + 1, 1, 0, -1, 0, 0) // 7
newVert(x + 0, y + 1, z + 1, 1, 1, -1, 0, 0) // 4
newVert(x + 0, y + 1, z + 0, 0, 1, -1, 0, 0) // 1
newVert(x + 0, y + 0, z + 0, 0, 0, -1, 0, 0) // 2
// Up
if (!isSolid(hx, hy + 1, hz)) {
t.setNormal(0, 1, 0)
t.addVertexWithUV(wx + s, wy + s, wz + 0, 0, 0) // 0
t.addVertexWithUV(wx + 0, wy + s, wz + 0, 1, 0) // 1
t.addVertexWithUV(wx + 0, wy + s, wz + s, 1, 1) // 4
t.addVertexWithUV(wx + s, wy + s, wz + s, 0, 1) // 5
}
// Down
if (!isSolid(hx, hy - 1, hz)) {
t.setNormal(0, -1, 0)
t.addVertexWithUV(wx + s, wy + 0, wz + s, 0, 0) // 6
t.addVertexWithUV(wx + 0, wy + 0, wz + s, 1, 0) // 7
t.addVertexWithUV(wx + 0, wy + 0, wz + 0, 1, 1) // 2
t.addVertexWithUV(wx + s, wy + 0, wz + 0, 0, 1) // 3
// Up
newVert(x + 1, y + 1, z + 0, 0, 0, 0, 1, 0) // 0
newVert(x + 0, y + 1, z + 0, 1, 0, 0, 1, 0) // 1
newVert(x + 0, y + 1, z + 1, 1, 1, 0, 1, 0) // 4
newVert(x + 1, y + 1, z + 1, 0, 1, 0, 1, 0) // 5
// Down
newVert(x + 1, y + 0, z + 1, 0, 0, 0, -1, 0) // 6
newVert(x + 0, y + 0, z + 1, 1, 0, 0, -1, 0) // 7
newVert(x + 0, y + 0, z + 0, 1, 1, 0, -1, 0) // 2
newVert(x + 1, y + 0, z + 0, 0, 1, 0, -1, 0) // 3
}
}
}
tmpBuf.rewind() // Important!
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, commonBuffer)
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, tmpBuf, GL15.GL_STATIC_DRAW)
}
if (hologram.dirty) { // Refresh hologram
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
var tmpBuf = BufferUtils.createIntBuffer(hologram.width * hologram.width * hologram.height * 24 * 2)
// Copy color information, identify which quads to render and prepare data for glDrawElements
hologram.visibleQuads = 0
var c = 0
tmpBuf.position(hologram.width * hologram.width * hologram.height * 24)
for (hx <- 0 until hologram.width) {
for (hz <- 0 until hologram.width) {
for (hy <- 0 until hologram.height) {
if (isSolid(hx, hy, hz)) {
val v: Int = hologram.colors(value(hx, hy, hz) - 1)
// South
if (!isSolid(hx, hy, hz + 1)) {
tmpBuf.put(c)
tmpBuf.put(c + 1)
tmpBuf.put(c + 2)
tmpBuf.put(c + 3)
tmpBuf.put(c, v)
tmpBuf.put(c + 1, v)
tmpBuf.put(c + 2, v)
tmpBuf.put(c + 3, v)
hologram.visibleQuads += 1
}
c += 4
// North
if (!isSolid(hx, hy, hz - 1)) {
tmpBuf.put(c)
tmpBuf.put(c + 1)
tmpBuf.put(c + 2)
tmpBuf.put(c + 3)
tmpBuf.put(c, v)
tmpBuf.put(c + 1, v)
tmpBuf.put(c + 2, v)
tmpBuf.put(c + 3, v)
hologram.visibleQuads += 1
}
c += 4
// East
if (!isSolid(hx + 1, hy, hz)) {
tmpBuf.put(c)
tmpBuf.put(c + 1)
tmpBuf.put(c + 2)
tmpBuf.put(c + 3)
tmpBuf.put(c, v)
tmpBuf.put(c + 1, v)
tmpBuf.put(c + 2, v)
tmpBuf.put(c + 3, v)
hologram.visibleQuads += 1
}
c += 4
// West
if (!isSolid(hx - 1, hy, hz)) {
tmpBuf.put(c)
tmpBuf.put(c + 1)
tmpBuf.put(c + 2)
tmpBuf.put(c + 3)
tmpBuf.put(c, v)
tmpBuf.put(c + 1, v)
tmpBuf.put(c + 2, v)
tmpBuf.put(c + 3, v)
hologram.visibleQuads += 1
}
c += 4
// Up
if (!isSolid(hx, hy + 1, hz)) {
tmpBuf.put(c)
tmpBuf.put(c + 1)
tmpBuf.put(c + 2)
tmpBuf.put(c + 3)
tmpBuf.put(c, v)
tmpBuf.put(c + 1, v)
tmpBuf.put(c + 2, v)
tmpBuf.put(c + 3, v)
hologram.visibleQuads += 1
}
c += 4
// Down
if (!isSolid(hx, hy - 1, hz)) {
tmpBuf.put(c)
tmpBuf.put(c + 1)
tmpBuf.put(c + 2)
tmpBuf.put(c + 3)
tmpBuf.put(c, v)
tmpBuf.put(c + 1, v)
tmpBuf.put(c + 2, v)
tmpBuf.put(c + 3, v)
hologram.visibleQuads += 1
}
c += 4
} else c += 24
}
}
}
tmpBuf.rewind() // Important!
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, privateBuf)
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, tmpBuf, GL15.GL_STATIC_DRAW)
hologram.dirty = false
}
t.draw()
GL11.glEnable(GL11.GL_NORMALIZE) // Normalize normals!!! (Yes, glScale scales them too!)
GL11.glEnable(GL11.GL_CULL_FACE)
GL11.glCullFace(GL11.GL_BACK) // Because fragment processing started to slow things down
bindTexture(Textures.blockHologram)
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, privateBuf)
GL11.glEnableClientState(GL11.GL_COLOR_ARRAY)
GL11.glColorPointer(3, GL11.GL_UNSIGNED_BYTE, 4, 0)
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, privateBuf)
if (doCompile) {
GL11.glEndList()
}
GL11.glDrawElements(GL11.GL_QUADS, hologram.visibleQuads * 4, GL11.GL_UNSIGNED_INT, hologram.width * hologram.width * hologram.height * 24 * 4)
true
// Restore original state
GL11.glPopAttrib()
GL11.glPopClientAttrib()
}
}
else GL11.glCallList(list)
// ----------------------------------------------------------------------- //
// Cache
// ----------------------------------------------------------------------- //
def call = {
val list = GLAllocation.generateDisplayLists(1)
val privateBuf = GL15.glGenBuffers()
hologram.dirty = true // Force compilation.
list
privateBuf
}
def onRemoval(e: RemovalNotification[TileEntity, Int]) {
GLAllocation.deleteDisplayLists(e.getValue)
GL15.glDeleteBuffers(e.getValue)
}
// ----------------------------------------------------------------------- //

View File

@ -34,6 +34,9 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w
// Whether we need to send an update packet/recompile our display list.
var dirty = false
// Store it here for convenience
var visibleQuads = 0
// Interval of dirty columns.
var dirtyFromX = Int.MaxValue
var dirtyUntilX = -1
@ -45,7 +48,7 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w
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
def colors = colorsByTier(tier)
def getColor(x: Int, y: Int, z: Int) = {
@ -203,7 +206,9 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w
def getPaletteColor(computer: Context, args: Arguments): Array[AnyRef] = {
val index = args.checkInteger(0)
if (index < 0 || index >= colors.length) throw new ArrayIndexOutOfBoundsException()
result(colors(index))
var col = colors(index)
// Colors are stored as 0xAABBGGRR for rendering convenience, so convert them back here
result(((col & 0xFF) << 16) | (col & 0xFF00) | ((col >>> 16) & 0xFF))
}
@Callback(doc = """function(index:number, value:number):number -- Set the color defined for the specified value.""")
@ -212,7 +217,9 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w
if (index < 0 || index >= colors.length) throw new ArrayIndexOutOfBoundsException()
val value = args.checkInteger(1)
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) = ((value & 0xFF) << 16) | (value & 0xFF00) | ((value >>> 16) & 0xFF)
ServerPacketSender.sendHologramColor(this, index, value)
result(oldValue)
}
@ -275,6 +282,7 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w
override def shouldRenderInPass(pass: Int) = pass == 1
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)
@ -303,7 +311,6 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w
nbt.getIntArray("colors").copyToArray(colors)
scale = nbt.getDouble("scale")
hasPower = nbt.getBoolean("hasPower")
dirty = true
}
override def writeToNBTForClient(nbt: NBTTagCompound) {