Advertisement
nonogamer9

Utah Teapot on OpenComputers

Sep 23rd, 2024
75
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 7.07 KB | Software | 0 0
  1. local component, computer, event, math = require("component"), require("computer"), require("event"), require("math")
  2. local gpu, internet = component.gpu, require("internet")
  3. local sin, cos, floor, min, max, huge, sqrt, rad, abs = math.sin, math.cos, math.floor, math.min, math.max, math.huge, math.sqrt, math.rad, math.abs
  4. local byte, format = string.byte, string.format
  5.  
  6. local W, H = gpu.getResolution()
  7. local buffer, vertices, faces = {}, {}, {}
  8. local sinT, cosT = {}, {}
  9.  
  10. for i = 0, 360 do local r = rad(i) sinT[i], cosT[i] = sin(r), cos(r) end
  11.  
  12. local function downloadOBJ()
  13.     local c = ""
  14.     for chunk in internet.request("https://raw.githubusercontent.com/kevinroast/phoria.js/master/teapot.obj") do c = c .. chunk end
  15.     return c
  16. end
  17.  
  18. local function parseOBJ(c)
  19.     for l in c:gmatch("[^\r\n]+") do
  20.         local p = l:sub(1, 2)
  21.         if p == "v " then
  22.             local x, y, z = l:match("v%s+([%d.-]+)%s+([%d.-]+)%s+([%d.-]+)")
  23.             vertices[#vertices + 1] = {tonumber(x), tonumber(y), tonumber(z)}
  24.         elseif p == "f " then
  25.             local v1, v2, v3 = l:match("f%s+(%d+)/?%S*%s+(%d+)/?%S*%s+(%d+)/?%S*")
  26.             if v1 then faces[#faces + 1] = {tonumber(v1), tonumber(v2), tonumber(v3)} end
  27.         end
  28.     end
  29. end
  30.  
  31. local function calcBoundingBox()
  32.     local minX, minY, minZ, maxX, maxY, maxZ = huge, huge, huge, -huge, -huge, -huge
  33.     for _, v in ipairs(vertices) do
  34.         minX, minY, minZ = min(minX, v[1]), min(minY, v[2]), min(minZ, v[3])
  35.         maxX, maxY, maxZ = max(maxX, v[1]), max(maxY, v[2]), max(maxZ, v[3])
  36.     end
  37.     return minX, minY, minZ, maxX, maxY, maxZ
  38. end
  39.  
  40. local function rotateY(x, y, z, a)
  41.     local c, s = cosT[a], sinT[a]
  42.     return x * c + z * s, y, -x * s + z * c
  43. end
  44.  
  45. local function project(x, y, z, s, oX, oY)
  46.     return floor(x * s + oX), floor(-y * s + oY)
  47. end
  48.  
  49. local function drawLine(x1, y1, x2, y2, char)
  50.     local dx, dy = abs(x2 - x1), abs(y2 - y1)
  51.     local sx, sy = x1 < x2 and 1 or -1, y1 < y2 and 1 or -1
  52.     local err, e2 = dx - dy, 0
  53.  
  54.     while true do
  55.         buffer[y1 * W + x1] = char
  56.         if x1 == x2 and y1 == y2 then break end
  57.         e2 = 2 * err
  58.         if e2 > -dy then err, x1 = err - dy, x1 + sx end
  59.         if e2 < dx then err, y1 = err + dx, y1 + sy end
  60.     end
  61. end
  62.  
  63. local function clearBuffer() for i = 1, W * H do buffer[i] = ' ' end end
  64.  
  65. local function renderBuffer()
  66.     local i, s = 1, {}
  67.     for y = 1, H do
  68.         for x = 1, W do s[x] = buffer[i] or ' ' i = i + 1 end
  69.         gpu.set(1, y, table.concat(s))
  70.     end
  71. end
  72.  
  73. local function normalize(x, y, z)
  74.     local l = sqrt(x*x + y*y + z*z)
  75.     return x/l, y/l, z/l
  76. end
  77.  
  78. local function dot(x1, y1, z1, x2, y2, z2) return x1*x2 + y1*y2 + z1*z2 end
  79.  
  80. local function detectGPUTier()
  81.     local maxRes, mem = gpu.maxResolution(), gpu.totalMemory()
  82.     if maxRes >= 160 and mem >= 786432 then return 3
  83.     elseif maxRes >= 80 and mem >= 196608 then return 2
  84.     else return 1 end
  85. end
  86.  
  87. local function main()
  88.     gpu.setBackground(0x000000)
  89.     gpu.setForeground(0xFFFFFF)
  90.     gpu.fill(1, 1, W, H, " ")
  91.     print("Downloading teapot.obj...")
  92.     local objContent = downloadOBJ()
  93.     print("Parsing OBJ file...")
  94.     parseOBJ(objContent)
  95.    
  96.     local gpuTier = detectGPUTier()
  97.     local faceSkip, rotationSpeed = 4 - gpuTier, gpuTier
  98.    
  99.     local minX, minY, minZ, maxX, maxY, maxZ = calcBoundingBox()
  100.     local cX, cY, cZ = (minX + maxX) / 2, (minY + maxY) / 2, (minZ + maxZ) / 2
  101.    
  102.     for i, v in ipairs(vertices) do v[1], v[2], v[3] = v[1] - cX, v[2] - cY, v[3] - cZ end
  103.    
  104.     local modelSize = max(maxX - minX, maxY - minY, maxZ - minZ)
  105.     local scale = min(W, H) * 0.8 / modelSize
  106.     local oX, oY = W / 2, H / 2
  107.    
  108.     local angle, lastRender = 0, 0
  109.     local frameCount = 0
  110.     local lastFPSUpdate = computer.uptime()
  111.     local fps = 0
  112.    
  113.     print("Rendering... Press 'x' to exit, '+' to zoom in, '-' to zoom out")
  114.     os.sleep(1)
  115.  
  116.     local shadeChars = {[0]='·', ':', '.', '+', '%', '&', '#', '@'}
  117.  
  118.     local totalMemory = computer.totalMemory()
  119.  
  120.     while true do
  121.         local currentTime = computer.uptime()
  122.        
  123.         frameCount = frameCount + 1
  124.         if currentTime - lastFPSUpdate >= 1 then
  125.             fps = frameCount / (currentTime - lastFPSUpdate)
  126.             frameCount = 0
  127.             lastFPSUpdate = currentTime
  128.         end
  129.  
  130.         if currentTime - lastRender >= 0.05 then
  131.             clearBuffer()
  132.            
  133.             local la = (angle * 2) % 360
  134.             local lx, ly, lz = sin(rad(la)) * 2, 1, cos(rad(la)) * 2
  135.             lx, ly, lz = normalize(lx, ly, lz)
  136.            
  137.             for i, face in ipairs(faces) do
  138.                 if i % faceSkip == 0 then
  139.                     local v1, v2, v3 = vertices[face[1]], vertices[face[2]], vertices[face[3]]
  140.                    
  141.                     local ux, uy, uz = v2[1] - v1[1], v2[2] - v1[2], v2[3] - v1[3]
  142.                     local vx, vy, vz = v3[1] - v1[1], v3[2] - v1[2], v3[3] - v1[3]
  143.                     local nx, ny, nz = uy*vz - uz*vy, uz*vx - ux*vz, ux*vy - uy*vx
  144.                     nx, ny, nz = normalize(nx, ny, nz)
  145.                    
  146.                     local shade = dot(nx, ny, nz, lx, ly, lz)
  147.                    
  148.                     local rx, ry, rz = 2 * shade * nx - lx, 2 * shade * ny - ly, 2 * shade * nz - lz
  149.                    
  150.                     local spec = max(0, dot(rx, ry, rz, 0, 0, 1)) ^ 8
  151.                    
  152.                     local char = shadeChars[floor(max(shade, spec) * 7)]
  153.                    
  154.                     for j = 1, 3 do
  155.                         local v1, v2 = vertices[face[j]], vertices[face[j % 3 + 1]]
  156.                        
  157.                         local x1, y1, z1 = rotateY(v1[1], v1[2], v1[3], angle)
  158.                         local x2, y2, z2 = rotateY(v2[1], v2[2], v2[3], angle)
  159.                        
  160.                         local px1, py1 = project(x1, y1, z1, scale, oX, oY)
  161.                         local px2, py2 = project(x2, y2, z2, scale, oX, oY)
  162.                        
  163.                         drawLine(px1, py1, px2, py2, char)
  164.                     end
  165.                 end
  166.             end
  167.            
  168.             renderBuffer()
  169.            
  170.             local memUsage = (1 - computer.freeMemory() / totalMemory) * 100
  171.            
  172.             local statsText = format("FPS: %.1f | MEM: %.1f%% | Zoom: %.2f", fps, memUsage, scale)
  173.            
  174.             gpu.setForeground(0xFFFF00)
  175.             gpu.set(1, 1, statsText)
  176.             gpu.setForeground(0xFFFFFF)
  177.            
  178.             angle = (angle + rotationSpeed) % 360
  179.             lastRender = currentTime
  180.         end
  181.        
  182.         local eventType, _, char = event.pull(0.01)
  183.         if eventType == "key_down" then
  184.             if char == byte('x') or char == byte('X') then
  185.                 break
  186.             elseif char == byte('+') then
  187.                 scale = scale * 1.1
  188.             elseif char == byte('-') then
  189.                 scale = scale * 0.9
  190.             end
  191.         end
  192.     end
  193.    
  194.     gpu.fill(1, 1, W, H, " ")
  195.     print("Exiting...")
  196. end
  197.  
  198. main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement