Advertisement
ezak

3D Renderer for Codea

Mar 2nd, 2012
727
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 16.98 KB | None | 0 0
  1.  
  2. --# Heightmap
  3. --            Heightmap building class              --
  4. ------------------------------------------------------
  5.  
  6. Model = class()
  7.    
  8. function Model:init(quality, n, wrapping, str)
  9.     mapSize = 512
  10.     step = math.pow(2,quality+2)
  11.     self.vertices = {}
  12.     self.faces = {}
  13.     self.detailTexCoords = {}
  14.     self.colorTexCoords = {}
  15.     self.tris = {}
  16.     self.tex = {}
  17.     self.cols = {}
  18.     local size = (mapSize/step)*(mapSize/step)*6
  19.     for i=1, size do
  20.         self.tris[i] = vec2(0,0)
  21.         if (textured == 1 or wireframe == 1) then
  22.             self.tex[i] = vec2(0,0)
  23.         end
  24.         if colored == 1 then
  25.             self.cols[i] = color(255, 255, 255, 255)
  26.         end
  27.     end
  28. -- create vertices, the height value of the heightmap is a simple noise
  29.     local i, j
  30.     i=1
  31.     for z=0, mapSize, step do
  32.         for x=0, mapSize, step do
  33.             j = (x + (z) * (mapSize+step)/step)/step+1
  34.             self.vertices[j] = Vertex(x*wrapping,(currentZ)-noise(x/n, z/n)*str, z*wrapping)
  35.             self.detailTexCoords[j] = vec2( x/step, z/step)
  36.             self.colorTexCoords[j] = vec2( x/mapSize, z/mapSize)
  37.            
  38.            
  39. -- filling the face array with the indices of the vertices
  40. -- 4 points, two triangles
  41.             if (z~=mapSize and x~=mapSize) then
  42.                 a = (x + (z) * (mapSize+step)/step)/step + 1
  43.                 b = (x + (z + step) * (mapSize+step)/step)/step + 1
  44.                 c = (x + step + (z + step) * (mapSize+step)/step)/step + 1
  45.                 d = (x + step + (z) * (mapSize+step)/step)/step + 1
  46.  
  47.                 self.faces[i] = Face(a, b, c)
  48.                 self.faces[i+1] = Face(c, d, a)
  49.                 i = i + 2
  50.             end
  51.         end
  52.     end
  53.    
  54.    
  55.     return self
  56. end
  57. --# Main
  58. --                   Main program                   --
  59. ------------------------------------------------------
  60.        
  61. function setup()
  62.     saveProjectInfo("Description", "3D renderer")
  63.     saveProjectInfo("Author", "Xavier de Boysson")
  64.     setupParameters()
  65.    
  66.    
  67.     textureQ = defaultTextureQuality*64
  68.     baseW = wrapping
  69.     baseS = str
  70.     baseQ = quality
  71.     baseSp = sphere  
  72.     baseP = nPower  
  73.     baseX = X
  74.     baseZ = Z
  75.     baseT = textureQ
  76.     baseTex = textured
  77.     baseC = colored
  78.     baseR = Red
  79.     baseG = Green
  80.     baseB = Blue
  81.     baseDTQ = defaultTextureQuality
  82.    
  83.     cnt = 0
  84.     txt = ""
  85.     lx = 0.1
  86.     lz = 0.1
  87.     baseX = lx
  88.     baseZ = lz
  89.    
  90.     nbTouches = 0
  91.     touches = {}
  92.     delta = 0
  93.     dx = -256*wrapping
  94.     dy = -256*wrapping
  95.    
  96.     currentZ = 512
  97.    
  98.     --stroke(255)
  99.     --strokeWidth(5)
  100.     --noSmooth()
  101.    
  102.    
  103.     p = mesh()
  104.     loadMdl()
  105.    
  106.     detailTexture = genWireframe(32)
  107.     colorTexture = genTexture(textureQ)
  108.    
  109.     textMode(CORNER)
  110.    
  111. end
  112.  
  113.  
  114. function setupParameters()
  115.     iparameter("moveLight", 0, 1, 0)
  116. -- wrapping is how much the heightmap wraps to the pseudo-sphere
  117.     iparameter("sphere", 0, 1, 1)
  118.     parameter("wrapping", 1, 10, 4)
  119.  
  120.    
  121. -- quality is the heightmap level of detail (step size)
  122.     iparameter("quality", 1, 6, 2)
  123. -- quality of the texture, warning :P
  124.     iparameter("defaultTextureQuality", 1, 4, 4)
  125.    
  126. -- str is the scaling of height values
  127.     iparameter("str", 0, 2048, 768)
  128.    
  129. -- npower is the perlin noise scaling
  130.     iparameter("nPower", 10, 200, 100)
  131.    
  132.     iparameter("wireframe", 0, 1, 0)
  133.     iparameter("textured", 0, 1, 1)
  134.     iparameter("colored", 0, 1, 0)
  135.    
  136.    
  137.    
  138.     parameter("Red", 0, 1, 1)  
  139.     parameter("Green", 0, 1, 0)
  140.     parameter("Blue", 0, 1, 0)
  141. end
  142.  
  143.  
  144. -- Ugly mess to handle parametres
  145. function readParameters()
  146.    
  147.    
  148.     if (baseW ~= wrapping) then
  149.         dx = -256*wrapping
  150.         dy = -256*wrapping
  151.         loadMdl()
  152.        
  153.         baseW = wrapping
  154.     end
  155.    
  156.     if (baseSp ~= sphere or baseQ ~= quality or baseS ~= str) then
  157.         loadMdl()
  158.        
  159.         baseS = str
  160.         baseQ = quality
  161.         baseSp = sphere
  162.     end
  163.    
  164.     if (baseP ~= nPower ) then
  165.         loadMdl()
  166.         if textured == 1 then
  167.             colorTexture = genTexture(textureQ)
  168.         end
  169.         baseP = nPower
  170.     end
  171.    
  172.     if (baseTex ~= textured or baseT ~= textureQ or baseX ~= lx or baseZ ~= lz) then
  173.         colorTexture = genTexture(textureQ)
  174.         baseX = lx
  175.         baseZ = lz
  176.        
  177.         baseT = textureQ
  178.         baseTex = textured
  179.     end
  180.    
  181.     if baseDTQ ~= defaultTextureQuality then
  182.         baseDTQ = defaultTextureQuality
  183.         textureQ = defaultTextureQuality*64
  184.         colorTexture = genTexture(textureQ)
  185.         baseT = textureQ
  186.     end
  187.    
  188.    
  189.    
  190.     if (baseC ~= colored or baseR ~= Red or baseG ~= Green or baseB ~= Blue) then
  191.         for i=1, #mdl.vertices do
  192.             v = mdl.vertices[i]
  193.             v.done = false
  194.         end
  195.         baseC = colored
  196.     end
  197.    
  198.    
  199.    
  200. end
  201.  
  202.  
  203. -- Load the heightmap
  204. function loadMdl()
  205.    
  206.     mdl = Model:init(quality, nPower, wrapping, str)
  207.         for i=1, #mdl.vertices do
  208.             local v = mdl.vertices[i]
  209.            
  210.             v.x = v.x + dx
  211.             v.z = v.z + dy
  212.            
  213.         -- the initial pseudo-sphere wrapping formula
  214.             if sphere == 1 then
  215.                 v.y = v.y + (v.x * v.x + v.z * v.z)*0.001
  216.             end
  217.         end
  218.     cnt = 0
  219.    
  220. end
  221.  
  222.  
  223.  
  224. --# Render
  225. --                   Render Loop                    --
  226. ------------------------------------------------------
  227.  
  228. function draw()
  229.    
  230.     readParameters()
  231.    
  232.     background(0)
  233.    
  234.     local p = p
  235.    
  236.     local offsetX = WIDTH*.5
  237.     local offsetY = HEIGHT*.5
  238.    
  239. -- Apply z ordering every 20 frame
  240.     local zOrder = cnt%20
  241.    
  242.     local v = mdl.vertices
  243.     local f = mdl.faces
  244.     local tris = mdl.tris
  245.     local cols = mdl.cols
  246.     local tex = mdl.tex
  247.    
  248.     local t
  249.     local texture
  250.    
  251. -- Select texture based on display mode
  252.     if textured == 1 then
  253.         t = mdl.colorTexCoords
  254.         texture = colorTexture
  255.     end
  256.     if wireframe == 1 then
  257.         t = mdl.detailTexCoords
  258.         texture = detailTexture
  259.     end
  260.    
  261.     local a, b, c, s
  262.     local vfx, vfy, vfz, col
  263.  
  264.     -- refresh fps every 20 frames so its actually readable
  265.     if zOrder == 0 then
  266.         txt = ((1024/step)*(1024/step)).." polygons at "..(math.floor(1/DeltaTime*10)/10).." FPS"
  267.     end
  268.  
  269.     text(txt, WIDTH - 250, HEIGHT - 50)
  270.     local n = 1
  271.    
  272. -- loop through every face in the model, each face contains the index of 3 vertices
  273.     for i=1,  #f do
  274.         local fx = f[i].x
  275.         local fy = f[i].y
  276.         local fz = f[i].z
  277.     -- extract the vertices that define the face
  278.         vfx = v[fx]
  279.         vfy = v[fy]
  280.         vfz = v[fz]
  281.    
  282.         a = vfx.pt
  283.         b = vfy.pt
  284.         c = vfz.pt
  285.        
  286.     -- to speed things up, a vertex is only projected once
  287.     -- the raw 3D to 2D screen space formula is x2D = x3D/z3D, y2D = y3D/z3D
  288.     -- base color of each vertex is based on its height
  289.        
  290.     -- point A
  291.         if not vfx.done then
  292.             s = 600/(600 + vfx.y)
  293.             a.x = vfx.x * s + offsetX
  294.             a.y = vfx.z * s + offsetY
  295.             if colored == 1 then
  296.                 col = 255 - vfx.y*.075
  297.                 vfx.col = color(col*Red, col*Green, col*Blue, 255)
  298.             end
  299.             vfx.done = true
  300.         end
  301.            
  302.     -- point B
  303.         if not vfy.done then
  304.             s = 600/(600 + vfy.y)
  305.             b.x = vfy.x * s + offsetX
  306.             b.y = vfy.z * s + offsetY
  307.             if colored == 1 then
  308.                 col = 255 - vfy.y*.075
  309.                 vfy.col = color(col*Red, col*Green, col*Blue, 255)
  310.             end
  311.             vfy.done = true
  312.         end
  313.  
  314.     -- point C
  315.         if not vfz.done then
  316.             s = 600/(600 + vfz.y)
  317.             c.x = vfz.x * s + offsetX
  318.             c.y = vfz.z * s + offsetY
  319.             if colored == 1 then
  320.                 col = 255 - vfz.y*.075
  321.                 vfz.col = color(col*Red, col*Green, col*Blue, 255)
  322.             end
  323.             vfz.done = true
  324.         end        
  325.        
  326.     -- getting distance from center of polygon to camera(0,0,0) to use painter's algorithm
  327.     -- this doesn't need to be that precise, so can easily be simplified
  328.             if zOrder == 0 then
  329.                 midx = (vfx.x + vfy.x + vfz.x)
  330.                 midy = (vfx.y + vfy.y + vfz.y)
  331.                 midz = (vfx.z + vfy.z + vfz.z)
  332.                    
  333.                 f[i].dist = (midx*midx+ midy*midy+ midz*midz)
  334.             else
  335.                 local nA = n
  336.                 local nB = n+1
  337.                 local nC = n+2
  338.             -- add vertices
  339.                 tris[nA] = a
  340.                 tris[nB] = b
  341.                 tris[nC] = c
  342.                
  343.             -- add wireframe texture UVs
  344.                 if textured == 1 or wireframe == 1 then
  345.                     tex[nA] = t[fx]
  346.                     tex[nB] = t[fy]
  347.                     tex[nC] = t[fz]
  348.                 end
  349.             -- add colors
  350.                 if colored == 1 then
  351.                     cols[nA] = vfx.col
  352.                     cols[nB] = vfy.col
  353.                     cols[nC] = vfz.col
  354.                 end
  355.                 n = n + 3
  356.             end
  357.     end
  358.    
  359. -- the painter's algorithm means drawings things closer to the camera last
  360. -- we sort the polygons based on distance to camera
  361.  
  362.  
  363.     if (zOrder == 0) then
  364.         table.sort(f, function (a,b) return a.dist>b.dist end)
  365.        
  366.         for i=1, #f do
  367.             local fx = f[i].x
  368.             local fy = f[i].y
  369.             local fz = f[i].z
  370.            
  371.             vfx = v[fx]
  372.             vfy = v[fy]
  373.             vfz = v[fz]
  374.            
  375.             local nA = n
  376.             local nB = n+1
  377.             local nC = n+2
  378.            
  379.         -- add vertices
  380.             tris[nA] = vfx.pt
  381.             tris[nB] = vfy.pt
  382.             tris[nC] = vfz.pt
  383.                
  384.         -- add wireframe texture UVs
  385.             if textured == 1 or wireframe == 1 then
  386.                 tex[nA] = t[fx]
  387.                 tex[nB] = t[fy]
  388.                 tex[nC] = t[fz]
  389.             end
  390.         -- add colors
  391.             if colored == 1 then
  392.                 cols[nA] = vfx.col
  393.                 cols[nB] = vfy.col
  394.                 cols[nC] = vfz.col
  395.             end
  396.             n = n + 3
  397.    
  398.         end
  399.     end
  400.    
  401.     p.vertices = tris
  402.     p.texture = nil
  403.     p.texCoords = nil
  404.     p.colors = nil
  405.    
  406.        
  407.     if textured == 1 or wireframe == 1 then
  408.         p.texture = texture
  409.         p.texCoords = tex
  410.         if colored == 0 then
  411.             p:setColors(255, 255, 255, 255)
  412.         end
  413.     end
  414.    
  415.     if colored == 1 then
  416.         p.colors = cols
  417.     end
  418.  
  419.     p:draw()
  420.    
  421.     cnt = cnt + 1
  422. end
  423. --# Texturing
  424. --                 Texture Generation               --
  425. ------------------------------------------------------
  426.  
  427. -- Main texture    
  428. function genTexture(w)
  429.        
  430.         local r = 512/w
  431.         local t = image(w, w)
  432.         local sx, sy, L, h, R, G, B
  433.        
  434.     -- define texture colors      
  435.         local t1x, t1y, t1z, t1w
  436.         local t2x, t2y, t2z, t2w
  437.         local t3x, t3y, t3z, t3w
  438.         local tW
  439.  
  440.         t1x = 0
  441.         t1y = 0
  442.         t1z = 1
  443.         t1w = 1
  444.        
  445.         t2x = 0
  446.         t2y = 1
  447.         t2z = 0
  448.         t2w = 1
  449.        
  450.         t3x = 1
  451.         t3y = 1
  452.         t3z = 1
  453.         t3w = 1
  454.        
  455.         local nx, ny, nz
  456.         local col = 0
  457.        
  458.         local min = math.min
  459.         local max = math.max
  460.         local abs = math.abs
  461.         local sqrt = math.sqrt
  462.        
  463.         local rs = r/nPower
  464.         local xrs, zrs
  465.        
  466.         for z=1, w do
  467.             for x=1, w do
  468.                 xrs = x*rs
  469.                 zrs = z*rs
  470.                 h = (128+127*noise(x*rs, z*rs))/255
  471.             -- Get the weight of each texture color type at current height
  472.        
  473.                 t1w = min(1, max(0, 1 - abs(h - 0.2)*4))
  474.                 t2w = min(1, max(0, 1 - abs(h - 0.5)*4))
  475.                 t3w = min(1, max(0, 1 - abs(h - 0.8)*4))
  476.                 tW = t1w + t2w + t3w
  477.                
  478.                 t1w = t1w/tW
  479.                 t2w = t2w/tW
  480.                 t3w = t3w/tW
  481.                
  482.             --  Blend the texture colors together
  483.                 R = t1x*t1w + t2x*t2w + t3x*t3w
  484.                 G = t1y*t1w + t2y*t2w + t3y*t3w
  485.                 B = t1z*t1w + t2z*t2w + t3z*t3w
  486.              
  487.             --  Calculate the normal for current pixel
  488.                 sx = noise(xrs+rs, zrs) - noise(xrs-rs, zrs)
  489.                 sy = noise(xrs, zrs+rs) - noise(xrs, zrs-rs)
  490.                
  491.                 nx = -sx*w
  492.                 nz = sy*w
  493.                
  494.                 L = 1/sqrt(nx*nx + 4 + nz*nz)
  495.                 nx = nx*L
  496.                 ny = 2*L
  497.                 nz = nz*L
  498.                                
  499.             --  Calculate lightning
  500.                 col = 255*min(1,max(0.1, nx*lx + ny + nz*lz)*2)
  501.            
  502.             --  Blend lightning with texture color
  503.                 t:set(x, z,col*R, col*G, col*B, 255)
  504.            
  505.             end
  506.         end
  507.        
  508.         return t
  509. end
  510.  
  511.  
  512. -- Wireframe texture
  513. function genWireframe(w)
  514.     local t = image(w, w)
  515.        
  516.     for y=1, w do
  517.         for x=1, 3 do
  518.             t:set(x, y, 255, 255, 255, 255)
  519.         end
  520.     end
  521.        
  522.     for x=1, w do
  523.         for y=1, 3 do    
  524.             t:set(x, y, 255, 255, 255, 255)
  525.         end
  526.     end
  527.        
  528.     for x=2, w-1 do
  529.         t:set(x+1, x, 255, 255, 255, 255)
  530.         t:set(x, x, 255, 255, 255, 255)
  531.         t:set(x-1, x, 255, 255, 255, 255)
  532.     end
  533.    
  534.     t:set(w, w, 255, 255, 255, 255)
  535.     t:set(w-1, w, 255, 255, 255, 255)
  536.    
  537.     return t
  538. end
  539. --# Touches
  540. --                   Touch Handling                 --
  541. ------------------------------------------------------
  542.    
  543. function touched(touch)
  544.     local state = touch.state
  545.     local touches = touches
  546.    
  547. -- Get number of touches
  548.     if state == BEGAN then
  549.         nbTouches = nbTouches + 1        
  550.     end
  551.    
  552.     if state == ENDED then
  553.         nbTouches = nbTouches - 1
  554.         touches[touch.id] = nil
  555.     else
  556.         touches[touch.id] = touch
  557.     end
  558.    
  559.     local delta = delta
  560.    
  561. --  need to save delta before looping through vertices, else laggy on high quality model
  562.     local deltaX = touch.deltaX
  563.     local deltaY = touch.deltaY
  564.     dx = dx + deltaX
  565.     dy = dy + deltaY
  566.    
  567. -- Handle pinch to zoom
  568.     if nbTouches == 2 then
  569.         local newPt = {}
  570.         local lastPt = {}
  571.         local ins = table.insert  
  572.         for k, p in pairs(touches) do
  573.             local px = p.x
  574.             local py = p.y
  575.             ins(newPt, vec2(px, py))
  576.             if state == BEGAN then
  577.                 ins(lastPt, vec2(px, py))
  578.             else
  579.                 ins(lastPt, vec2(px - p.deltaX, py - p.deltaY))
  580.             end
  581.         end
  582.  
  583.         delta = lastPt[1]:dist(lastPt[2]) - newPt[1]:dist(newPt[2])
  584.         currentZ = currentZ + delta
  585.         if currentZ<1 then
  586.             currentZ = 1
  587.             delta = 0
  588.         end
  589.        
  590.     end
  591.    
  592. -- change light vector and reduce texture quality for smoother animation
  593.     if moveLight == 1 then
  594.         if state == BEGAN then
  595.             textureQ = 64
  596.         end
  597.         lx = ((WIDTH*.5 - touch.x)/WIDTH)*.5
  598.         lz = (-(HEIGHT*.5 - touch.y)/HEIGHT)*.5
  599.         if state == ENDED then
  600.             textureQ = defaultTextureQuality*64
  601.             moveLight = 0
  602.         end
  603.     else
  604.    
  605.     -- this would normally be processed in a vertex shader, but this is software
  606.     -- i didn't find any other way
  607.         local vs = mdl.vertices
  608.        
  609.         for i=1, #vs do
  610.             local v = vs[i]
  611.             local vx = v.x
  612.             local vz = v.z
  613.            
  614.         -- restore the shape of the model (turn it from pseudo-sphere to simple heightmap)
  615.             if sphere == 1 then
  616.                 v.y = v.y - (vx * vx + vz * vz)*0.001
  617.             end
  618.            
  619.         -- due to the way the trick works, translating the vertices rotates the "planet"
  620.             v.x = vx + deltaX
  621.             v.z = vz + deltaY
  622.             v.y = v.y + delta
  623.            
  624.         -- once the translation work is done, turn it back into a pseudo-sphere
  625.             if sphere == 1 then
  626.                 vx = v.x
  627.                 vz = v.z
  628.                 v.y = v.y + (vx * vx + vz * vz)*0.001
  629.             end
  630.    
  631.             v.done = false
  632.         end  
  633.     end
  634. end
  635. --# Types
  636. --                    Custom Types                  --
  637. ------------------------------------------------------
  638.  
  639. Vertex = class()
  640.  
  641. function Vertex:init(x, y, z)
  642.     self.x = x
  643.     self.y = y
  644.     self.z = z
  645.     self.pt = vec2(0,0)
  646.     self.col = color(Red, Green, Blue, 255)
  647.     self.done = false
  648. end
  649.  
  650.  
  651. ------------------------------------------------------
  652. Face = class()
  653.  
  654. function Face:init(x, y, z)
  655.     self.x = x
  656.     self.y = y
  657.     self.z = z
  658.     self.dist = 0
  659. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement