• API
• FAQ
• Tools
• Archive
SHARE
TWEET # 3D Renderer for Codea ezak  Mar 2nd, 2012 643 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
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()
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
146.
147.
148.     if (baseW ~= wrapping) then
149.         dx = -256*wrapping
150.         dy = -256*wrapping
152.
153.         baseW = wrapping
154.     end
155.
156.     if (baseSp ~= sphere or baseQ ~= quality or baseS ~= str) then
158.
159.         baseS = str
160.         baseQ = quality
161.         baseSp = sphere
162.     end
163.
164.     if (baseP ~= nPower ) then
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.
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.
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
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
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.
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
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:dist(lastPt) - newPt:dist(newPt)
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
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy.
Top