Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- Custom bit32 implementation for vanilla ComputerCraft
- local bit32 = {}
- function bit32.band(a, b)
- local result = 0
- local bitval = 1
- while a > 0 and b > 0 do
- if a % 2 == 1 and b % 2 == 1 then
- result = result + bitval
- end
- bitval = bitval * 2
- a = math.floor(a / 2)
- b = math.floor(b / 2)
- end
- return result
- end
- function bit32.bor(a, b)
- local result = 0
- local bitval = 1
- while a > 0 or b > 0 do
- if a % 2 == 1 or b % 2 == 1 then
- result = result + bitval
- end
- bitval = bitval * 2
- a = math.floor(a / 2)
- b = math.floor(b / 2)
- end
- return result
- end
- function bit32.lshift(a, disp)
- return a * (2 ^ disp)
- end
- function bit32.rshift(a, disp)
- return math.floor(a / (2 ^ disp))
- end
- local function downloadFile(url, path)
- print("Downloading " .. url .. "...")
- local response = http.get(url)
- if response then
- local file = fs.open(path, "wb")
- file.write(response.readAll())
- file.close()
- response.close()
- print("Download complete!")
- return true
- end
- print("Download failed!")
- return false
- end
- local sceneUrl = "https://github.com/pulkomandy/beniccc/raw/master/scene1.bin"
- local scenePath = "scene1.bin"
- if not fs.exists(scenePath) and not downloadFile(sceneUrl, scenePath) then
- error("Failed to download scene1.bin")
- end
- local w, h = term.getSize()
- local DEMO_WIDTH, DEMO_HEIGHT = 256, 200
- local SCALE_X, SCALE_Y = w / DEMO_WIDTH, h / DEMO_HEIGHT
- local frontBuffer, backBuffer = {}, {}
- for y = 1, h do
- frontBuffer[y], backBuffer[y] = {}, {}
- for x = 1, w do
- frontBuffer[y][x] = {bg = colors.black, char = " "}
- backBuffer[y][x] = {bg = colors.black, char = " "}
- end
- end
- local function decode(raw)
- raw = bit32.band(raw, 0xF)
- local bits = bit32.band(bit32.rshift(raw, 3), 1)
- bits = bit32.bor(bits, bit32.band(bit32.lshift(raw, 1), 0xE))
- return bits * 17
- end
- local function rgbToTerminal(r, g, b)
- local colors = {
- [colors.white] = {240, 240, 240},
- [colors.orange] = {242, 178, 51},
- [colors.magenta] = {229, 127, 216},
- [colors.lightBlue] = {153, 178, 242},
- [colors.yellow] = {222, 222, 108},
- [colors.lime] = {127, 204, 25},
- [colors.pink] = {242, 178, 204},
- [colors.gray] = {76, 76, 76},
- [colors.lightGray] = {153, 153, 153},
- [colors.cyan] = {76, 153, 178},
- [colors.purple] = {178, 102, 229},
- [colors.blue] = {51, 102, 204},
- [colors.brown] = {127, 102, 76},
- [colors.green] = {87, 166, 78},
- [colors.red] = {204, 76, 76},
- [colors.black] = {17, 17, 17}
- }
- local minDist, bestColor = math.huge, colors.black
- for color, rgb in pairs(colors) do
- local dr, dg, db = r - rgb[1], g - rgb[2], b - rgb[3]
- local dist = dr * dr + dg * dg + db * db
- if dist < minDist then
- minDist, bestColor = dist, color
- end
- end
- return bestColor
- end
- local file = fs.open(scenePath, "rb")
- local sceneData = file.readAll()
- file.close()
- local position = 1
- local function readByte()
- if position > #sceneData then
- return nil
- end
- local byte = string.byte(sceneData:sub(position, position))
- position = position + 1
- return byte
- end
- local function readWord()
- local high, low = readByte(), readByte()
- if not high or not low then return nil end
- return bit32.bor(bit32.lshift(high, 8), low)
- end
- local function drawPolygon(buffer, poly, color)
- if not poly or #poly < 3 then
- return -- Not enough points to draw a polygon
- end
- local minY, maxY = math.huge, -math.huge
- for _, p in ipairs(poly) do
- if p and p.y then
- minY = math.min(minY, p.y)
- maxY = math.max(maxY, p.y)
- end
- end
- if minY == math.huge or maxY == -math.huge then
- return -- No valid Y coordinates found
- end
- for y = math.max(1, math.floor(minY * SCALE_Y)), math.min(h, math.ceil(maxY * SCALE_Y)) do
- local nodeX = {}
- local j = #poly
- for i = 1, #poly do
- local pi, pj = poly[i], poly[j]
- if pi and pj and pi.x and pi.y and pj.x and pj.y then
- if (pi.y * SCALE_Y < y and pj.y * SCALE_Y >= y) or (pj.y * SCALE_Y < y and pi.y * SCALE_Y >= y) then
- if pj.y ~= pi.y then
- local x = pi.x + (y / SCALE_Y - pi.y) / (pj.y - pi.y) * (pj.x - pi.x)
- table.insert(nodeX, math.floor(x * SCALE_X))
- end
- end
- end
- j = i
- end
- table.sort(nodeX)
- for i = 1, #nodeX, 2 do
- local startX, endX = nodeX[i], (nodeX[i+1] or w)
- for x = math.max(1, startX), math.min(w, endX) do
- if buffer[y] and buffer[y][x] then
- buffer[y][x] = {bg = color, char = " "}
- end
- end
- end
- end
- end
- local palette = {}
- for i = 1, 16 do palette[i] = colors.black end
- local frameCount, startTime = 0, os.clock()
- local targetFrameTime = 1 / 30
- local shouldExit = false
- while position <= #sceneData and not shouldExit do
- local frameStartTime = os.clock()
- local flags = readByte()
- if not flags then
- print("End of data reached")
- break
- end
- if bit32.band(flags, 2) ~= 0 then
- local colorMask = readWord()
- if not colorMask then
- print("End of data reached while reading color mask")
- break
- end
- for i = 0, 15 do
- if bit32.band(colorMask, bit32.lshift(1, 15 - i)) ~= 0 then
- local color = readWord()
- if not color then
- print("End of data reached while reading color")
- shouldExit = true
- break
- end
- local r = decode(color)
- local g = decode(bit32.rshift(color, 12))
- local b = decode(bit32.rshift(color, 8))
- palette[i + 1] = rgbToTerminal(r,g,b)
- end
- end
- end
- if bit32.band(flags, 1) ~= 0 then
- for y = 1, h do
- for x = 1, w do
- backBuffer[y][x] = {bg = palette[1], char = " "}
- end
- end
- end
- if bit32.band(flags, 4) ~= 0 then
- local vertices = readByte()
- if not vertices then
- print("End of data reached")
- break
- end
- local points = {}
- for i = 1, vertices do
- local x, y = readByte(), readByte()
- if not x or not y then
- print("End of data reached while reading vertices")
- shouldExit = true
- break
- end
- points[i] = {x = x, y = y}
- end
- if shouldExit then break end
- while true do
- local bits = readByte()
- if not bits then
- print("End of data reached while reading bits")
- shouldExit = true
- break
- end
- if bits == 0xFF then break -- End of frame
- elseif bits == 0xFE then
- position = bit32.band(position - 1 + 0x10000, 0xFFFF0000) + 1
- break
- elseif bits == 0xFD then
- shouldExit = true
- break
- else
- local color = palette[bit32.rshift(bits, 4) + 1]
- if not color then
- print("Invalid color index")
- shouldExit = true
- break
- end
- local numPoints = bit32.band(bits, 0xF)
- local poly = {}
- for i = 1, numPoints do
- local index = readByte()
- if not index then
- print("End of data reached while reading polygon points")
- shouldExit = true
- break
- end
- index = index + 1
- if points[index] then
- table.insert(poly, points[index])
- end
- end
- if shouldExit then break end
- if #poly >= 3 then
- drawPolygon(backBuffer, poly, color)
- end
- end
- end
- else
- while true do
- local bits = readByte()
- if not bits then
- print("End of data reached while reading bits")
- shouldExit = true
- break
- end
- if bits == 0xFF then break -- End of frame
- elseif bits == 0xFE then
- position = bit32.band(position - 1 + 0x10000, 0xFFFF0000) + 1
- break
- elseif bits == 0xFD then
- shouldExit = true
- break
- else
- local color = palette[bit32.rshift(bits, 4) + 1]
- if not color then
- print("Invalid color index")
- shouldExit = true
- break
- end
- local numPoints = bit32.band(bits, 0xF)
- local poly = {}
- for i = 1, numPoints do
- local x, y = readByte(), readByte()
- if not x or not y then
- print("End of data reached while reading polygon points")
- shouldExit = true
- break
- end
- table.insert(poly, {x = x, y = y})
- end
- if shouldExit then break end
- if #poly >= 3 then
- drawPolygon(backBuffer, poly, color)
- end
- end
- end
- end
- frontBuffer, backBuffer = backBuffer, frontBuffer
- for y = 1, h do
- for x = 1, w do
- term.setCursorPos(x, y)
- term.setBackgroundColor(frontBuffer[y][x].bg)
- term.write(frontBuffer[y][x].char)
- end
- end
- frameCount = frameCount + 1
- local currentTime = os.clock()
- local fps = frameCount / (currentTime - startTime)
- term.setCursorPos(1, 1)
- term.setBackgroundColor(colors.black)
- term.setTextColor(colors.white)
- term.write(string.format("Frame: %d FPS: %.2f", frameCount, fps))
- local frameEndTime = os.clock()
- local frameTime = frameEndTime - frameStartTime
- if frameTime < targetFrameTime then sleep(targetFrameTime - frameTime) end
- end
- local endTime = os.clock()
- local totalTime = endTime - startTime
- local avgFPS = frameCount / totalTime
- print(string.format("Demo completed in %.2f seconds", totalTime))
- print(string.format("Total frames: %d", frameCount))
- print(string.format("Average FPS: %.2f", avgFPS))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement