Allow printing 3D prints that can be walked through (non-colliding), as well as shapes with no texture (texture="") appearing transparent, closes #1238.

Also improved face culling on 3D prints when rendering, improving render performance in certain scenarios.
This commit is contained in:
Florian Nücke 2015-06-25 19:50:12 +02:00
parent 64d3f93177
commit 1d933fd9f5
8 changed files with 58 additions and 5 deletions

View File

@ -972,6 +972,11 @@ opencomputers {
# don't care about that and prefer them to be not totally shadowless,
# enable this.
printsHaveOpacity: false
# By what (linear) factor the cost of a print increases if one or both of
# its states are non-collidable (i.e. entities can move through them).
# This only influences the chamelium cost.
noclipMultiplier: 2
}
hologram {

View File

@ -144,6 +144,7 @@ hdd3 {
["oc:materialCircuitBoardPrinted", "oc:materialDisk", craftingPiston]
["oc:circuitChip3", "oc:materialDisk", diamond]]
}
dataCard {
input: [[nuggetIron, "oc:materialALU", "oc:circuitChip2"]
["", "oc:materialCard", ""]]

View File

@ -328,6 +328,7 @@ class Settings(val config: Config) {
val printMaterialValue = config.getInt("printer.materialValue") max 0
val printInkValue = config.getInt("printer.inkValue") max 0
val printsHaveOpacity = config.getBoolean("printer.printsHaveOpacity")
val noclipMultiplier = config.getDouble("printer.noclipMultiplier") max 0
// ----------------------------------------------------------------------- //
// integration

View File

@ -1,5 +1,6 @@
package li.cil.oc.client.renderer.block
import com.google.common.base.Strings
import li.cil.oc.Constants
import li.cil.oc.api.Items
import li.cil.oc.common.block
@ -17,12 +18,13 @@ object Print {
def render(data: PrintData, state: Boolean, facing: ForgeDirection, x: Int, y: Int, z: Int, block: Block, renderer: RenderBlocks): Unit = {
val shapes = if (state) data.stateOn else data.stateOff
printBlock.isSingleShape = shapes.size == 1
if (shapes.isEmpty) {
printBlock.textureOverride = Option(resolveTexture("missingno"))
renderer.setRenderBounds(0, 0, 0, 1, 1, 1)
renderer.renderStandardBlock(block, x, y, z)
}
else for (shape <- shapes) {
else for (shape <- shapes if !Strings.isNullOrEmpty(shape.texture)) {
val bounds = shape.bounds.rotateTowards(facing)
printBlock.colorMultiplierOverride = shape.tint
printBlock.textureOverride = Option(resolveTexture(shape.texture))
@ -33,6 +35,7 @@ object Print {
}
printBlock.colorMultiplierOverride = None
printBlock.textureOverride = None
printBlock.isSingleShape = false
}
def resolveTexture(name: String): IIcon = {

View File

@ -1,5 +1,6 @@
package li.cil.oc.client.renderer.item
import com.google.common.base.Strings
import li.cil.oc.Constants
import li.cil.oc.Settings
import li.cil.oc.api
@ -150,6 +151,11 @@ object ItemRenderer extends IItemRenderer {
val bounds = shape.bounds
val texture = Print.resolveTexture(shape.texture)
if (Strings.isNullOrEmpty(shape.texture)) {
RenderState.makeItBlend()
GL11.glColor4f(1, 1, 1, 0.25f)
}
shape.tint.foreach(color => {
val r = (color >> 16).toByte
val g = (color >> 8).toByte

View File

@ -3,6 +3,7 @@ package li.cil.oc.common.block
import java.util
import java.util.Random
import com.google.common.base.Strings
import cpw.mods.fml.relauncher.Side
import cpw.mods.fml.relauncher.SideOnly
import li.cil.oc.Localization
@ -40,6 +41,8 @@ class Print(protected implicit val tileTag: ClassTag[tileentity.Print]) extends
var colorMultiplierOverride: Option[Int] = None
// Also used in model rendering, can't use renderer's override logic because that'll disable tinting.
var textureOverride: Option[IIcon] = None
// Again, used in model rendering, used to know whether we can potentially skip rendering sides.
var isSingleShape = false
@SideOnly(Side.CLIENT)
override def getIcon(world: IBlockAccess, x: Int, y: Int, z: Int, globalSide: ForgeDirection, localSide: ForgeDirection): IIcon =
@ -80,7 +83,19 @@ class Print(protected implicit val tileTag: ClassTag[tileentity.Print]) extends
case _ => super.getLightOpacity(world, x, y, z)
}
override def shouldSideBeRendered(world: IBlockAccess, x: Int, y: Int, z: Int, side: ForgeDirection) = true
override def shouldSideBeRendered(world: IBlockAccess, x: Int, y: Int, z: Int, side: ForgeDirection) = !isSingleShape || (world.getTileEntity(x, y, z) match {
case print: tileentity.Print if isSideSolid(world, x, y, z, side.getOpposite) =>
side match {
case ForgeDirection.DOWN => minY > 0
case ForgeDirection.UP => maxY < 1
case ForgeDirection.NORTH => minZ > 0
case ForgeDirection.SOUTH => maxZ < 1
case ForgeDirection.WEST => minX > 0
case ForgeDirection.EAST => maxX < 1
case _ => true
}
case _ => super.shouldSideBeRendered(world, x, y, z, side)
})
override def isBlockSolid(world: IBlockAccess, x: Int, y: Int, z: Int, side: ForgeDirection) = isSideSolid(world, x, y, z, side)
@ -88,7 +103,7 @@ class Print(protected implicit val tileTag: ClassTag[tileentity.Print]) extends
world.getTileEntity(x, y, z) match {
case print: tileentity.Print =>
val shapes = if (print.state) print.data.stateOn else print.data.stateOff
for (shape <- shapes) {
for (shape <- shapes if !Strings.isNullOrEmpty(shape.texture)) {
val bounds = shape.bounds.rotateTowards(print.facing)
val fullX = bounds.minX == 0 && bounds.maxX == 1
val fullY = bounds.minY == 0 && bounds.maxY == 1
@ -118,6 +133,8 @@ class Print(protected implicit val tileTag: ClassTag[tileentity.Print]) extends
override def addCollisionBoxesToList(world: World, x: Int, y: Int, z: Int, mask: AxisAlignedBB, list: util.List[_], entity: Entity): Unit = {
world.getTileEntity(x, y, z) match {
case print: tileentity.Print =>
if (if (print.state) print.data.noclipOn else print.data.noclipOff) return
def add[T](list: util.List[T], value: Any) = list.add(value.asInstanceOf[T])
val shapes = if (print.state) print.data.stateOn else print.data.stateOff
for (shape <- shapes) {

View File

@ -30,6 +30,8 @@ class PrintData extends ItemData(Constants.BlockName.Print) {
val stateOn = mutable.Set.empty[PrintData.Shape]
var isBeaconBase = false
var lightLevel = 0
var noclipOff = false
var noclipOn = false
def hasActiveState = stateOn.nonEmpty
@ -68,6 +70,8 @@ class PrintData extends ItemData(Constants.BlockName.Print) {
stateOn ++= nbt.getTagList("stateOn", NBT.TAG_COMPOUND).map(PrintData.nbtToShape)
isBeaconBase = nbt.getBoolean("isBeaconBase")
lightLevel = (nbt.getByte("lightLevel") & 0xFF) max 0 min 15
noclipOff = nbt.getBoolean("noclipOff")
noclipOn = nbt.getBoolean("noclipOn")
opacityDirty = true
}
@ -82,6 +86,8 @@ class PrintData extends ItemData(Constants.BlockName.Print) {
nbt.setNewTagList("stateOn", stateOn.map(PrintData.shapeToNBT))
nbt.setBoolean("isBeaconBase", isBeaconBase)
nbt.setByte("lightLevel", lightLevel.toByte)
nbt.setBoolean("noclipOff", noclipOff)
nbt.setBoolean("noclipOn", noclipOn)
}
}
@ -119,6 +125,7 @@ object PrintData {
def computeCosts(data: PrintData) = {
val totalVolume = data.stateOn.foldLeft(0)((acc, shape) => acc + shape.bounds.volume) + data.stateOff.foldLeft(0)((acc, shape) => acc + shape.bounds.volume)
val totalSurface = data.stateOn.foldLeft(0)((acc, shape) => acc + shape.bounds.surface) + data.stateOff.foldLeft(0)((acc, shape) => acc + shape.bounds.surface)
val multiplier = if (data.noclipOff || data.noclipOn) Settings.get.noclipMultiplier else 1
if (totalVolume > 0) {
val baseMaterialRequired = (totalVolume / 2) max 1
@ -127,7 +134,7 @@ object PrintData {
else baseMaterialRequired
val inkRequired = (totalSurface / 6) max 1
Option((materialRequired, inkRequired))
Option((materialRequired * multiplier, inkRequired))
}
else None
}

View File

@ -56,7 +56,7 @@ class Printer extends traits.Environment with traits.Inventory with traits.Rotat
// ----------------------------------------------------------------------- //
def canPrint = data.stateOff.size > 0 && data.stateOff.size <= Settings.get.maxPrintComplexity && data.stateOn.size <= Settings.get.maxPrintComplexity
def canPrint = data.stateOff.nonEmpty && data.stateOff.size <= Settings.get.maxPrintComplexity && data.stateOn.size <= Settings.get.maxPrintComplexity
def isPrinting = output.isDefined
@ -136,6 +136,19 @@ class Printer extends traits.Environment with traits.Inventory with traits.Rotat
result(data.isButtonMode)
}
@Callback(doc = """function(collideOff:boolean, collideOn:boolean) -- Get whether the printed block should be collidable or not.""")
def setCollidable(context: Context, args: Arguments): Array[Object] = {
val (collideOff, collideOn) = (args.checkBoolean(0), args.checkBoolean(1))
data.noclipOff = !collideOff
data.noclipOn = !collideOn
null
}
@Callback(doc = """function():boolean, boolean -- Get whether the printed block should be collidable or not.""")
def isCollidable(context: Context, args: Arguments): Array[Object] = {
result(!data.noclipOff, !data.noclipOn)
}
@Callback(doc = """function(minX:number, minY:number, minZ:number, maxX:number, maxY:number, maxZ:number, texture:string[, state:boolean=false][,tint:number]) -- Adds a shape to the printers configuration, optionally specifying whether it is for the off or on state.""")
def addShape(context: Context, args: Arguments): Array[Object] = {
if (data.stateOff.size > Settings.get.maxPrintComplexity || data.stateOn.size > Settings.get.maxPrintComplexity) {