Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- +--------------------------------------------------------+
- -- | |
- -- | print3d |
- -- | |
- -- +--------------------------------------------------------+
- local version = "Version 1.1.0"
- -- By Jeffrey Alexander, aka Bomb Bloke.
- -- Command Computer script for the purpose of mimicking an OpenComputers 3D per.
- -- http://www.computercraft.info/forums2/index.php?/topic/29653-opencomputers-p3d
- -- https://ocdoc.cil.li/block:3d_per
- ---------------------------------------------
- ------------ Functions ------------
- ---------------------------------------------
- -- Returns true/false depending on whether "val" falls into the specified numeric range.
- local function inRange(val, low, high)
- if type(val) ~= "number" then return false end
- return val >= low and val <= high
- end
- -- Where "inData" is an unserialised .3dm file. Returns a string representing the NBT data of the object.
- local function _makeNBT(inData, noisy)
- local json, jCount = {}, 1
- local function w(...) if noisy then return write(...) end end
- local function p(...) if noisy then return print(...) end end
- local function reg(val)
- json[jCount] = tostring(val)
- jCount = jCount + 1
- end
- local function regShape(shape, last)
- reg("{texture:\"" .. shape.texture .. "\",")
- if shape.tint then reg("tint:" .. shape.tint .. ",") end
- reg("bounds:[B;" .. shape[1] .. "B," .. shape[2] .. "B," .. (16 - shape[6]) .. "B,")
- reg(shape[4] .. "B," .. shape[5] .. "B," .. (16 - shape[3]) .. "B]}")
- if not last then reg(",") end
- end
- reg("{label:\"")
- w("Configuring...\nLabel: ")
- if type(inData.label) == "string" then
- reg(inData.label .. "\",")
- p("'".. inData.label .. "'")
- else
- reg("3D Print\",")
- p("'not set'")
- end
- w("Tooltip: ")
- if type(inData.tooltip) == "string" then
- reg("tooltip:\"" .. inData.tooltip .. "\",")
- p("'".. inData.tooltip .. "'")
- else
- p("'not set'")
- end
- reg("lightLevel:")
- w("Light level: ")
- if inRange(inData.lightLevel, 0, 15) then
- reg(math.floor(inData.lightLevel) .. ",")
- p(math.floor(inData.lightLevel))
- else
- reg("0b,")
- p(0)
- end
- reg("redstoneLevel:")
- w("Redstone level: ")
- if inRange(inData.emitRedstone, 0, 15) then
- local str = math.floor(inData.emitRedstone)
- reg(str .. ",")
- p(str)
- elseif inData.emitRedstone == true then
- reg(15 .. ",")
- p(15)
- else
- reg(0 .. ",")
- p(0)
- end
- reg("isButtonMode:")
- w("Button mode: ")
- if inData.buttonMode == true then
- reg("1b,")
- p("true")
- else
- reg("0b,")
- p("false")
- end
- w("Collidable: ")
- if type(inData.collidable) == "table" then
- if inData.collidable[1] == true then
- reg("noclipOff:0b,")
- w("true")
- else
- reg("noclipOff:1b,")
- w("false")
- end
- w("/")
- if inData.collidable[2] == true then
- reg("noclipOn:0b,")
- p("true")
- else
- reg("noclipOn:1b,")
- p("false")
- end
- else
- reg("noclipOff:0b,noclipOn:0b,")
- p("true/true")
- end
- local inactive, active = {}, {}
- if type(inData.shapes) == "table" then
- for i = 1, table.maxn(inData.shapes) do
- local shape, nShape = inData.shapes[i], {}
- if shape then
- for j = 1, 6 do
- local val = tonumber(shape[j])
- if not inRange(val, 0, 16) then error("Failed parsing data: invalid coord within shape #" .. i, 0) end
- nShape[j] = math.floor(val)
- end
- --Apparently this doesn't matter and I can do it anyway: for j = 1, 3 do if nShape[j] == nShape[j + 3] then error("Failed parsing data: shape #" .. i .. " is flat", 0) end end
- if type(shape.texture) ~= "string" then error("Failed parsing data: invalid texture within shape #" .. i, 0) end
- nShape.texture = shape.texture
- if inRange(shape.tint, 0, 0xFFFFFF) then nShape.tint = math.floor(shape.tint) end
- if shape.state == true then
- active[#active + 1] = nShape
- else
- inactive[#inactive + 1] = nShape
- end
- end
- end
- else
- error("Failed parsing data: no shapes", 0)
- end
- if inactive == 0 then error("Failed parsing data: no inactive shapes", 0) end
- reg("stateOff:[")
- for i = 1, #inactive do regShape(inactive[i], i == #inactive) end
- reg("],stateOn:[")
- for i = 1, #active do regShape(active[i], i == #active) end
- reg("]}")
- p("Shapes: " .. #inactive .. " inactive, " .. #active .. " active\nJob successfully committed!")
- return table.concat(json)
- end
- -- Returns a numerically indexed table of NBT data strings for all objects in a model made by initModel().
- local function parseObject(obj)
- local result = {}
- for z = 1, obj.len[3] do for y = 1, obj.len[2] do for x = 1, obj.len[1] do
- result[#result + 1] = _makeNBT(obj[z][y][x])
- end end end
- return result
- end
- -- Helper function for setPixel(). Combines nearby pixels into larger polys.
- local function insertPixel(obj, sPos, shape, xDir, yDir)
- obj = obj[sPos[3]][sPos[2]][sPos[1]]
- local shapes, merge = obj.shapes, obj.merge
- local xO, yO
- if xDir[1] + yDir[1] == 0 then
- xO, yO = 6, 5
- elseif xDir[2] + yDir[2] == 0 then
- xO, yO = 4, 6
- elseif xDir[3] + yDir[3] == 0 then
- xO, yO = 4, 5
- end
- local x, y = shape[xO], shape[yO]
- if merge[y][x] then error("Can't draw to same pixel twice, sorry.") end
- merge[y][x] = {#shapes + 1, 1, 1, x, y, shape.texture, shape.tint}
- shapes[#shapes + 1] = shape
- repeat
- local found = false
- for y = 1, 16 do for x = 1, 16 do
- local m, mX, mY = merge[y][x]
- if x < 16 then mX = merge[y][x + 1] end
- if y < 16 then mY = merge[y + 1][x] end
- if m then
- if mX and m ~= mX and m[3] == mX[3] and m[5] == mX[5] and m[6] == mX[6] and m[7] == mX[7] then
- m[2], found, shapes[m[1]][xO], shapes[mX[1]] = m[2] + mX[2], true, shapes[m[1]][xO] + mX[2]
- for yy = mX[5], mX[5] + mX[3] - 1 do for xx = mX[4], mX[4] + mX[2] - 1 do merge[yy][xx] = m end end
- end
- if mY and m ~= mY and m[2] == mY[2] and m[4] == mY[4] and m[6] == mY[6] and m[7] == mY[7] then
- m[3], found, shapes[m[1]][yO], shapes[mY[1]] = m[3] + mY[3], true, shapes[m[1]][yO] + mY[3]
- for yy = mY[5], mY[5] + mY[3] - 1 do for xx = mY[4], mY[4] + mY[2] - 1 do merge[yy][xx] = m end end
- end
- end
- end end
- until not found
- end
- -- Sets a colour within a 2D plane returned by obj.get2DPlane().
- local function setPixel(obj, mirror, start, xDir, yDir, x, y, texture, tint)
- local pos, shape = {}, {["texture"] = texture, ["tint"] = tint}
- for i = 1, 3 do
- pos[i] = {}
- pos[i][1] = start[i] + (x - 1) * xDir[i] + (y - 1) * yDir[i]
- pos[i][2] = start[i] + x * xDir[i] + y * yDir[i]
- table.sort(pos[i])
- shape[i] = pos[i][1] % 16
- shape[i + 3] = pos[i][2] % 16
- if shape[i + 3] == 0 and xDir[i] + yDir[i] ~= 0 then shape[i + 3] = 16 end
- end
- local sPos = {math.floor(pos[1][1] / 16) + 1, math.floor(pos[2][1] / 16) + 1, math.floor(pos[3][1] / 16) + 1}
- for i = 1, 3 do if sPos[i] > obj.len[i] then
- sPos[i] = sPos[i] - 1
- shape[i], shape[i + 3] = 16, 16
- end end
- insertPixel(obj, sPos, shape, xDir, yDir)
- if mirror then
- local extraShape = {["texture"] = texture, ["tint"] = tint}
- for j = 1, 6 do extraShape[j] = shape[j] end
- extraShape[mirror], extraShape[mirror + 3], sPos[mirror] = 16, 16, sPos[mirror] - 1
- insertPixel(obj, sPos, extraShape, xDir, yDir)
- end
- end
- -- Helper function for makeMergeTables().
- local function makeMergeTable()
- local result = {}
- for i = 1, 16 do result[i] = {} end
- return result
- end
- -- Helper function for initModel(). These tables are used to combine single pixels into larger polys.
- local function makeMergeTables(obj, sPos, xDir, yDir)
- if xDir[1] + yDir[1] == 0 then
- for z = 1, obj.len[3] do
- for y = 1, obj.len[2] do
- obj[z][y][sPos[1]].merge = makeMergeTable()
- end
- end
- elseif xDir[2] + yDir[2] == 0 then
- for z = 1, obj.len[3] do
- for x = 1, obj.len[1] do
- obj[z][sPos[2]][x].merge = makeMergeTable()
- end
- end
- elseif xDir[3] + yDir[3] == 0 then
- for y = 1, obj.len[2] do
- for x = 1, obj.len[1] do
- obj[sPos[3]][y][x].merge = makeMergeTable()
- end
- end
- end
- end
- -- Generates a new 3D object, which can span multiple blocks.
- local function _initModel(label, xLen, yLen, zLen)
- local obj = {["label"] = label, ["len"] = {math.ceil(xLen / 16), math.ceil(yLen / 16), math.ceil(zLen / 16)}}
- for z = 1, obj.len[3] do
- local zLayer = {}
- for y = 1, obj.len[2] do
- local yLayer = {}
- for x = 1, obj.len[1] do
- yLayer[x] = {["label"] = label, ["tooltip"] = x .."/" .. y .. "/" .. z, shapes = {}}
- end
- zLayer[y] = yLayer
- end
- obj[z] = zLayer
- end
- local invertZ = obj.len[3] * 16
- -- Produces a function for drawing pixels across a 2D plane within the 3D model.
- function obj.get2DPlane(start, xDir, yDir)
- -- Re-invert z axis:
- start[3], xDir[3], yDir[3] = invertZ - start[3], -xDir[3], -yDir[3]
- local sPos = {}
- for i = 1, 3 do
- sPos[i] = (xDir[i] + yDir[i] < 0) and math.ceil(start[i] / 16) or (math.floor(start[i] / 16) + 1)
- if sPos[i] > obj.len[i] then sPos[i] = obj.len[i] end
- end
- for z = 1, obj.len[3] do for y = 1, obj.len[2] do for x = 1, obj.len[1] do obj[z][y][x].merge = nil end end end
- makeMergeTables(obj, sPos, xDir, yDir)
- local mirror = false
- for i = 1, 3 do if start[i] % 16 == 0 and xDir[i] + yDir[i] == 0 and sPos[i] > 1 and start[i] < obj.len[i] * 16 then
- mirror, sPos[i] = i, sPos[i] - 1
- makeMergeTables(obj, sPos, xDir, yDir)
- break
- end end
- return function(x, y, texture, tint)
- setPixel(obj, mirror, start, xDir, yDir, x, y, texture, tint)
- end
- end
- -- Returns a numerically indexed table of NBT data strings for all objects in the model.
- function obj.parse()
- return parseObject(obj)
- end
- return obj
- end
- ---------------------------------------------
- ------------ Load as API ------------
- ---------------------------------------------
- if not shell then
- makeNBT = _makeNBT
- initModel = _initModel
- ---------------------------------------------
- ------------ Script Code ------------
- ---------------------------------------------
- else
- if not commands then error("This script requires a Command Computer to execute.", 0) end
- local filename, num = ...
- if not filename then
- print("Usage: print3d FILE [count]")
- error()
- end
- num = tonumber(num)
- if not inRange(num, 1, 64) then num = 1 end
- if filename:lower():sub(-4) ~= ".3dm" and not fs.exists(filename) then filename = filename .. ".3dm" end
- if not fs.exists(filename) then error("Failed opening file: file not found", 0) end
- local inFile = fs.open(filename, "r")
- if not inFile then error("Failed opening file: possibly locked?", 0) end
- local inData = textutils.unserialise(inFile.readAll())
- inFile.close()
- if not inData then error("Failed unserialising file: invalid table data", 0) end
- local cmd = "give @p opencomputers:print " .. num .. " 0 " .. _makeNBT(inData, true)
- local ok, result = commands.exec(cmd)
- if not ok then error("Print failed: ".. textutils.serialise(result), 0) end
- if filename:lower():sub(-4) == ".3dm" then filename = filename:sub(1, -5) end
- local outFile = fs.open(filename .. ".txt", "w")
- outFile.write(cmd)
- outFile.close()
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement