Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- os.loadAPI("matrix")
- os.loadAPI("graphics")
- os.loadAPI("triapi")
- local w,h = term.getSize()
- --[[
- 3D Render
- Display objects (and eventually textures) mapped in 3D coordinate space
- Makes use of the graphics API for projection and matrix API for transformations
- By Nitrogen Fingers
- Feel free to play with this code as much as you want!
- ]]--
- camera = {
- position = vector.new(0,0,0);
- rX = 0; rY = 0; rZ = 0;
- }
- cube2 = {
- vertices = {
- [1] = vector.new(6,6,0),
- [2] = vector.new(6,0,0),
- [3] = vector.new(0,0,0),
- [4] = vector.new(0,6,0),
- [5] = vector.new(6,6,6),
- [6] = vector.new(6,0,6),
- [7] = vector.new(0,0,6),
- [8] = vector.new(0,6,6);
- };
- colors = {
- [1] = colors.red;
- [2] = colors.blue;
- [3] = colors.green;
- [4] = colors.yellow;
- [5] = colors.orange;
- [6] = colors.cyan;
- [7] = colors.purple;
- [8] = colors.lime;
- };
- lines = {
- {1,2},{2,3},{3,4},{1,4},{5,6},{6,7},
- {7,8},{8,5},{1,5},{2,6},{3,7},{4,8};
- };
- faces = {
- {1,2,3,4, colors.red, colors.orange, ":"},
- {5,6,7,8, colors.green, colors.lime, "|"},
- {5,6,2,1, colors.blue, colors.cyan, "-"},
- {3,4,8,7, colors.yellow, colors.red, "+"},
- {1,4,8,5, colors.cyan, colors.blue, "@"},
- {2,3,7,6, colors.lime, colors.green, "#"};
- };
- scale = 1;
- translate = vector.new(0,0,5);
- rotateX = 0; rotateY = 0; rotateZ = 0;
- }
- cube = {
- vertices = {
- [1] = vector.new(3,3,-3),
- [2] = vector.new(3,-3,-3),
- [3] = vector.new(-3,-3,-3),
- [4] = vector.new(-3,3,-3),
- [5] = vector.new(3,3,3),
- [6] = vector.new(3,-3,3),
- [7] = vector.new(-3,-3,3),
- [8] = vector.new(-3,3,3);
- };
- colors = {
- [1] = colors.red;
- [2] = colors.blue;
- [3] = colors.green;
- [4] = colors.yellow;
- [5] = colors.orange;
- [6] = colors.cyan;
- [7] = colors.purple;
- [8] = colors.lime;
- };
- lines = {
- {1,2},{2,3},{3,4},{1,4},{5,6},{6,7},
- {7,8},{8,5},{1,5},{2,6},{3,7},{4,8};
- };
- faces = {
- {1,2,3,4, colors.red, colors.orange, ":"},
- {5,6,7,8, colors.green, colors.lime, "|"},
- {5,6,2,1, colors.blue, colors.cyan, "-"},
- {3,4,8,7, colors.yellow, colors.red, "+"},
- {1,4,8,5, colors.cyan, colors.blue, "@"},
- {2,3,7,6, colors.lime, colors.green, "#"};
- };
- scale = 1;
- translate = vector.new(0,0,5);
- rotateX = 0; rotateY = 0; rotateZ = 0;
- }
- cube2.link = {{cube, 1, 1, colors.white, colors.black, "-"}}
- --The 'illusion of depth' function, really just for debugging and demo work.
- function drawAtPoint(vert, tab)
- if tab ~= nil then
- tab[vert.nov] = vert
- end
- term.setCursorPos(vert.x, vert.y)
- if vert.dist < 5 then
- term.setBackgroundColour(vert.color)
- term.write(" ")
- term.setBackgroundColour(colours.black)
- elseif vert.dist < 10 then
- term.setTextColour(vert.color)
- term.write("0")
- elseif vert.dist < 15 then
- term.setTextColour(vert.color)
- term.write("*")
- else
- term.setTextColour(vert.color)
- term.write(".")
- end
- end
- local function insertAt(tab, index, value)
- local ntab = {}
- if index > 1 then
- for i=1,index-1 do
- ntab[#ntab + 1] = tab[i]
- end
- ntab[index] = value
- if index <= #tab then
- for i=index,#tab do
- ntab[#ntab + 1] = tab[i]
- end
- end
- else
- ntab[#ntab + 1] = value
- for i=1,#tab do
- ntab[#ntab + 1] = tab[i]
- end
- end
- return ntab
- end
- --Draws our 3D models. Right now it only draws the vertices, not faces or lines.
- function renderModel(model)
- world = {}
- --Note: Matrix operations, especially rotations are expensive. Rather than re-creating
- --matrices over and over as done here, it is much safer to initialize them at the start
- --of a program, and only update when necessary. If something is beyond the view frustrum,
- --don't render it!!!
- --Creating the world space
- global = matrix.identity()
- global = global * matrix.createScale(model.scale)
- global = global * matrix.createRotationX(model.rotateX)
- global = global * matrix.createRotationY(model.rotateY)
- global = global * matrix.createRotationZ(model.rotateZ)
- global = global * matrix.createTranslation(model.translate)
- distances = {}
- for i,vertex in ipairs(model.vertices) do
- table.insert(world,global:apply(vertex, i))
- table.insert(distances, math.sqrt(math.pow(camera.position.x - world[i].x, 2) +
- math.pow(camera.position.y - world[i].y, 2) +
- math.pow(camera.position.z - world[i].z, 2)))
- end
- --Converting from world space to camera space
- global = matrix.identity()
- global = global * matrix.createTranslation(-camera.position)
- global = global * matrix.createRotationX(-camera.rX)
- for i,vertex in ipairs(world) do
- --Applying camera space
- world[i] = global:apply(vertex, vertex.nov)
- end
- --Project camera space into screen space
- for i=1,#world do
- local col = world[i].color
- local nov = world[i].nov
- world[i] = graphics.project(world[i])
- --Here until we sort out depth buffering
- world[i].color = model.colors[i]
- world[i].dist = distances[i]
- world[i].nov = nov
- end
- buffer = {}
- --Depth-sorting our points for cleaner drawing. Simple selection sort- not wasting time on this.
- --This is NOT sufficient for long-term. Will clean up later.
- while #world > 0 do
- local maxDepth = 1
- for i=2,#world do
- if world[i].dist > world[maxDepth].dist then maxDepth = i end
- end
- table.insert(buffer, world[maxDepth])
- table.remove(world, maxDepth)
- end
- --Draw the verts on screen if you can!
- local verts = {}
- for i,vert in ipairs(buffer) do
- if vert.x > 0 and vert.x < w and vert.y > 0 and vert.y < h then
- --drawAtPoint(vert, verts)
- verts[vert.nov] = vert
- end
- end
- model.verts = verts
- local sqrs = {}
- for i=1,#model.faces do
- local canDraw = true
- --list of vert ids and a color as the last index
- local sqtab = model.faces[i]
- --converted table of true vert coords compared to vert ids
- local ntab = {}
- local avgDist = 0
- local avgi = 0
- for i=1,(#sqtab - 3) do
- --ID for vert
- local id = sqtab[i]
- if verts[id] ~= nil then
- ntab[i] = {verts[id].x, verts[id].y}
- avgDist = avgDist + verts[id].dist
- avgi = avgi + 1
- else
- canDraw = false
- --break
- end
- end
- if canDraw then
- local square = triapi.fillSquare(ntab[1], ntab[2], ntab[3], ntab[4])
- local avgDist = avgDist/avgi
- square.dist = avgDist
- square.col = sqtab[5]
- square.tcol = sqtab[6]
- square.text = sqtab[7]
- local centre = {(ntab[1][1]+ntab[2][1]+ntab[3][1]+ntab[4][1])/4, (ntab[1][2]+ntab[2][2]+ntab[3][2]+ntab[4][2])/4}
- square.centre = centre
- sqrs[#sqrs + 1] = square
- end
- --triapi.drawShape(square, sqtab[5], colors.black, " ")
- end
- local lines = {}
- if model.link ~= nil then
- for i=1,#model.link do
- local link = model.link[i][1]
- local mFace = model.link[i][2]
- local lFace = model.link[i][3]
- local col = model.link[i][4]
- local tcol = model.link[i][5]
- local text = model.link[i][6]
- local face = link.faces[lFace]
- centre = {0,0}
- local n = 0
- for i=1,(#face - 3) do
- centre[1] = centre[1] + link.verts[face[i]].x
- centre[2] = centre[2] + link.verts[face[i]].y
- n = n + 1
- end
- centre[1] = centre[1]/n
- centre[2] = centre[2]/n
- modelCentre = sqrs[mFace].centre
- local line = triapi.drawLine(modelCentre[1], modelCentre[2], centre[1], centre[2])
- --[[triapi.drawShape(line,
- col,
- tcol,
- text)]]
- lines[#lines + 1] = {line, col, tcol, text}
- end
- end
- local sqrs_dists = {}
- while #sqrs > 0 do
- local maxDepth = 1
- for i=2,#sqrs do
- if sqrs[i].dist > sqrs[maxDepth].dist then maxDepth = i end
- end
- table.insert(sqrs_dists, sqrs[maxDepth])
- table.remove(sqrs, maxDepth)
- end
- if #sqrs_dists > 3 then
- for i=(#sqrs_dists - 3),#sqrs_dists do
- local sqr = sqrs_dists[i]
- triapi.drawShape(sqr, sqr.col, sqr.tcol, sqr.text)
- end
- else
- for i=1,#sqrs_dists do
- local sqr = sqrs_dists[i]
- triapi.drawShape(sqr, sqr.col, sqr.tcol, sqr.text)
- end
- end
- if model.link ~= nil then
- for i=1,#lines do
- triapi.drawShape(lines[i][1],
- lines[i][2],
- lines[i][3],
- lines[i][4])
- end
- end
- --That's it! Your first render!
- end
- --Again, a little demo function to show off the operations
- function printHUD()
- term.setCursorPos(1,h)
- term.setTextColour(colours.white)
- term.write("Arrows move, WSAD QE rotate, RF scale. Enter quits.")
- end
- local curCube = cube
- --Lets you play with the 3D model a bit.
- function run()
- quit = false
- repeat
- term.setBackgroundColor(colors.black)
- term.clear()
- renderModel(cube)
- renderModel(cube2)
- printHUD()
- local _,key = os.pullEvent("key")
- --Arrow keys translate
- if key == keys.up then
- curCube.translate.z = curCube.translate.z + 1
- elseif key == keys.down then
- curCube.translate.z = curCube.translate.z - 1
- elseif key == keys.left then
- curCube.translate.x = curCube.translate.x - 1
- elseif key == keys.right then
- curCube.translate.x = curCube.translate.x + 1
- --WSAD QE keys rotate
- elseif key == keys.w then
- curCube.rotateX = curCube.rotateX - (math.pi/16)
- elseif key == keys.s then
- curCube.rotateX = curCube.rotateX + (math.pi/16)
- elseif key == keys.q then
- curCube.rotateZ = curCube.rotateZ - (math.pi/16)
- elseif key == keys.e then
- curCube.rotateZ = curCube.rotateZ + (math.pi/16)
- elseif key == keys.a then
- curCube.rotateY = curCube.rotateY + (math.pi/16)
- elseif key == keys.d then
- curCube.rotateY = curCube.rotateY - (math.pi/16)
- --RF scales
- elseif key == keys.r then
- curCube.scale = curCube.scale * 1.1
- elseif key == keys.f then
- curCube.scale = curCube.scale / 1.1
- --Enter quits
- elseif key == keys.z then
- if curCube == cube then curCube = cube2 else curCube = cube end
- elseif key == keys.enter then
- quit = true
- end
- until quit == true
- end
- run()
- term.setBackgroundColor(colors.black)
- shell.run("clear")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement