Check out the Pastebin Gadgets Shop. We have thousands of fun, geeky & affordable gadgets on sale :-)Want more features on Pastebin? Sign Up, it's FREE!
tweet

By: a guest on Jan 9th, 2013  |  syntax: Python  |  size: 13.16 KB  |  views: 130  |  expires: Never
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
1. #! /usr/bin/python
2.
3. # a rewrite of my pygame port of
5. # which is by Bisqwit
6. #
8. #
9. # theinternetftw
10.
11. import pygame, math, random, sys
12. from multiprocessing import Pool
13.
14. def vMul(a,b):  return [ a[0]*b[0], a[1]*b[1], a[2]*b[2] ]
15. def vMulD(a,b): return [ a[0]*b,    a[1]*b,    a[2]*b    ]
16. def vAdd(a,b):  return [ a[0]+b[0], a[1]+b[1], a[2]+b[2] ]
17. def vAddD(a,b): return [ a[0]+b,    a[1]+b,    a[2]+b    ]
18. def vSub(a,b):  return [ a[0]-b[0], a[1]-b[1], a[2]-b[2] ]
19. def vSubD(a,b): return [ a[0]-b,    a[1]-b,    a[2]-b    ]
20. def vNeg(a):    return [-a[0],     -a[1],     -a[2]      ]
21. def vPow(a,b):  return [ a[0]**b,   a[1]**b,   a[2]**b   ]
22.
23. def vDot(a,b):  return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]
24. def vSqr(a):    return vDot(a,a)
25. def vLen(a):    return math.sqrt(vSqr(a))
26. def vNorm(a):   return vMulD(a, 1.0/vLen(a))
27.
28. def vMirrorAround(a, axis):
29.     n = vNorm(axis)
30.     v = vDot(a,n)
31.     return vSub(vMulD(n,v+v), a)
32.
33. # for color vectors (rgb)
34. def vLuma(a): return a[0]*0.299 + a[1]*0.587 + a[2]*0.114
35. def vClamp(a):
36.     for i in range(3):
37.         if   a[i] < 0.0: a[i] = 0.0
38.         elif a[i] > 1.0: a[i] = 1.0
39.     return a
40.
41. def vClampWithDesaturation(a):
42.     # if the color represented by this triplet
43.     # is too bright or too dim, decrease the saturation
44.     # as much as required, while keeping the luma unmodified
45.     l = vLuma(a)
46.     if   l > 1.0: return [1.0, 1.0, 1.0]
47.     elif l < 0.0: return [0.0, 0.0, 0.0]
48.     # if any component is over the bounds,
49.     # calculate how much the saturation must
50.     # be reduced to achieve an in-bounds value.
51.     # Since the luma was verified to be in 0..1,
52.     # a maximum reduction of saturation to 0% will
53.     # always produce an in-bounds value, but usually
54.     # such a drastic reduction is not necessary.
55.     # Because we're only doing relative modifications,
56.     # we don't need the original saturation level of the
57.     # pixel.
58.     sat = 1.0
59.     for c in a:
60.         if   c > 1.0: sat = min(sat, (l-1.0) / (l-c))
61.         elif c < 0.0: sat = min(sat,  l      / (l-c))
62.     if sat != 1.0:
64.         a = vClamp(a)
65.     return a
66.
67. def vGetRotMatrix(ang):
68.     cx,cy,cz = math.cos(ang[0]),math.cos(ang[1]),math.cos(ang[2])
69.     sx,sy,sz = math.sin(ang[0]),math.sin(ang[1]),math.sin(ang[2])
70.     sxsz,cxsz = sx*sz,cx*sz
71.     cxcz,sxcz = cx*cz,sx*cz
72.     return [ [cy*cz,          cy*sz,          -sy  ],
73.              [sxcz*sy - cxsz, sxsz*sy + cxcz, sx*cy],
74.              [cxcz*sy + sxsz, cxsz*sy - sxcz, cx*cy] ]
75.
76. def mTransform(m, vec):
77.     return [ vDot(m[0], vec), vDot(m[1], vec), vDot(m[2], vec) ]
78.
79. class Plane:
80.     def __init__(self, norm, off):
81.         self.normal = norm
82.         self.offset = off
83. # declare six planes, each looks
84. # towards the origin and is 30 units away
85. planes = [ Plane( [0.0,0.0,-1.0], -30),
86.            Plane( [0.0,1.0,0.0],  -30),
87.            Plane( [0.0,-1.0,0.0], -30),
88.            Plane( [1.0,0.0,0.0],  -30),
89.            Plane( [0.0,0.0,1.0],  -30),
90.            Plane( [-1.0,0.0,0.0], -30) ]
91.
92. class Sphere:
93.     def __init__(self, c, r):
94.         self.center = c
96. # declare a few spheres
97. spheres = [ Sphere( [0.0,0.0,0.0],    7.0),
98.             Sphere( [19.4, -19.4, 0], 2.1),
99.             Sphere( [-19.4, 19.4, 0], 2.1),
100.             Sphere( [13.1, 5.1, 0.0], 1.1),
101.             Sphere( [-5.1, -13.1, 0], 1.1),
102.             Sphere( [-30.0,-30.0,15.0], 11.0),
103.             Sphere( [15.0, -30.0,30.0], 6.0),
104.             Sphere( [30.0, 15.0, -30.0],6.0) ]
105.
106. class LightSource:
107.     def __init__(self, w, c):
108.         self.where = w
109.         self.color = c
110. #declare lightsources, each w/ a loc and a rgb color
111. lights = [ LightSource( [-28.0,-14.0,  4.0],  [ .4,.51, .9] ),
112.            LightSource( [-29.0,-29.0,-29.0],  [.95, .1, .1] ),
113.            LightSource( [ 14.0, 29.0,-14.0],  [ .8, .8, .8] ),
114.            LightSource( [ 29.0, 29.0, 29.0],  [1.0,1.0,1.0] ),
115.            LightSource( [ 28.0,  0.0, 29.0],  [ .5, .6, .1] ) ]
116.
117. numPlanes = len(planes)
118. numSpheres = len(spheres)
119. numLights = len(lights)
120. MAXTRACE = 6
121.
122. def rayFindObstacle(eye, dir, hitDist):
123.     #try to intersect ray w/ each object, see which gives the closest hit
124.     hitType = -1
125.     hitIndex = -1
126.     hitLoc = [0.0,0.0,0.0]
127.     hitNormal = [0.0,0.0,0.0]
128.     for i in range(numSpheres):
129.         v = vSub(eye, spheres[i].center)
131.         dv = vDot(dir,v)
132.         d2 = vSqr(dir)
133.         sq = dv*dv - d2*(vSqr(v) - r*r)
134.         #does the ray coincide w/ the sphere?
135.         if (sq < 1e-6):
136.             continue
137.         #if so, where?
138.         sqt = math.sqrt(sq)
139.         dist = min(-dv-sqt, -dv+sqt) / d2
140.         if dist < 1e-6 or dist >= hitDist:
141.             continue
142.         hitType = 1
143.         hitIndex = i
144.         hitDist = dist
146.         hitNormal = vMulD(vSub(hitLoc,spheres[i].center), 1/r)
147.     for i in range(numPlanes):
148.         dv = -vDot(planes[i].normal,dir)
149.         if dv > -1e-6:
150.             continue
151.         d2 = vDot(planes[i].normal,eye)
152.         dist = (d2 + planes[i].offset) / dv
153.         if dist < 1e-6 or dist >= hitDist:
154.             continue
155.         hitType = 0
156.         hitIndex = i
157.         hitDist = dist
159.         hitNormal = vNeg(planes[i].normal)
160.     return hitType, hitIndex, hitDist, hitLoc, hitNormal
161.
162. random.seed(1) #not doing this lends an interesting speckled pattern to the lighting,
163.                #as each proc that's started up (max of 4 atm) has different
164.                #values for the arealight vectors (i think the work is done as threads
165.                #on those procs, so the vectors are *not* changed w/ each call to
166.                #apply_sync)
167. numArealightVectors = 20
168. arealightVectors = []
169. for i in range(numArealightVectors):
170.     temp = []
171.     for i in range(3):
172.         temp.append(2.0*(random.random() - 0.5))
173.     arealightVectors.append( [temp[0],temp[1],temp[2]] )
174.
175. def rayTrace(eye, dir, k):
176.     hitDist = 1e6
177.     hitType, hitIndex, hitDist, hitLoc, hitNormal = rayFindObstacle(eye,dir,hitDist)
178.
179.     if hitType != -1:
180.
181.         # Found an obstacle. Next, find out how it is illuminated.
182.         # Shoot a ray to each lightsource, and determine if there
183.         # is an obstacle behind it. This is called "diffuse light".
184.         # To smooth out the infinitely sharp shadows caused by
185.         # infinitely small point-lightsources, assume the lightsource
186.         # is actually a cloud of small lightsources around its center.
187.         diffuseLight = [0.0,0.0,0.0]
188.         specularLight = [0.0,0.0,0.0]
189.         pigment = [1.0, .98, .94] #default pigment
190.         for i in range(numLights):
191.             for j in range(numArealightVectors):
193.                 lightDist = vLen(v)
194.                 v = vNorm(v)
195.                 diffuseEffect = vDot(hitNormal,v) / (numArealightVectors*1.0)
196.                 attenuation = (1 + ((lightDist/34.0)**2.0))
197.                 diffuseEffect /= attenuation
198.                 if diffuseEffect > 1e-3:
199.                     shadowDist = lightDist - 1e-4
201.                     if t == -1: #no obstacle occluding the light
203.
204.         if k > 1:
205.             # add specular light/reflection, unless recursion depth is maxed
206.             v = vNeg(dir)
207.             v = vMirrorAround(v, hitNormal)
209.
210.         if hitType == 0: #plane
211.             diffuseLight = vMulD(diffuseLight,0.9)
212.             specularLight = vMulD(specularLight,0.5)
213.             # color the different walls differently
214.             idx = hitIndex % 3
215.             if   idx == 0: pigment = [0.9,0.7,0.6]
216.             elif idx == 1: pigment = [0.6,0.7,0.7]
217.             elif idx == 2: pigment = [0.5,0.8,0.3]
218.
219.         elif hitType == 1: #sphere
220.             diffuseLight = vMulD(diffuseLight,1.0)
221.             specularLight = vMulD(specularLight, 0.34)
222.
224.
225.     #didn't hit anything, return black
226.     return [0.0,0.0,0.0]
227.
228. def getPix(x, y, w, h, zoom, camlookmatrix, campos, MAXTRACE):
229.
230.     camray = [ x/float(w) - 0.5,
231.                y/float(h) - 0.5,
232.                zoom ]
233.     camray[0] *= 4.0/3 # Aspect Ratio Correction
234.     camray = vNorm(camray)
235.     camray = mTransform(camlookmatrix, camray)
236.     campix = rayTrace(campos, camray, MAXTRACE)
237.
238.     campix = vMulD(campix, 0.5) #Adjust brightness
239.
240.     return campix
241.
242. def main():
243.
244.     pygame.display.init()
245.     screenw, screenh = 640, 480
246.     w, h = int(sys.argv[1]),int(sys.argv[2])
247.     screen = pygame.display.set_mode((screenw, screenh), pygame.DOUBLEBUF)
248.     frame = pygame.Surface((w,h))
249.
250.     camangle =      [  0.0,  0.0,  0.0]
251.     camangledelta = [-.005,-.011,-.017]
252.     camlook =       [  0.0,  0.0,  0.0]
253.     camlookdelta =  [-.001, .005, .004]
254.
255.     zoom = 46.0
256.     zoomdelta = 0.99
257.
258.     contrast = 32
259.     contrast_offset = -0.17
260.
261.     frames = []
262.
263.     #numFrames = 9300
264.     numFrames = int(sys.argv[3])
265.
266.     MAXTRACE = int(sys.argv[4])
267.
268.     pool = Pool(processes=4)
269.
270.     for frameNo in range(numFrames):
271.         # Put camera between central sphere and walls
272.         campos = [0.0,0.0,16.0]
273.         camrotatematrix = vGetRotMatrix(camangle)
274.         campos = mTransform(camrotatematrix, campos)
275.         camlookmatrix = vGetRotMatrix(camlook)
276.
277.         # Determine contrast ratio for this frame's pixels
278.         thisframe_min = 100
279.         thisframe_max = -100
280.
281.         pixels = pygame.PixelArray(frame)
282.
283.         for y in range(h):
284.
285.             results = []
286.
287.             for x in range(w):
288.                 result = pool.apply_async(getPix, [x, y, w, h, zoom, camlookmatrix, campos, MAXTRACE])
289.                 results.append(result)
290.
291.             for x in range(w):
292.
293.                 campix = results[x].get()
294.
295.                 # update frame luma info for automatic contrast adjuster
296.                 lum = vLuma(campix)
297.                 if lum < thisframe_min: thisframe_min = lum
298.                 if lum > thisframe_max: thisframe_max = lum
299.
300.                 # exagerate the colors to bring contrast better forth
302.
303.                 # Clamp, and compensate for display gamma (for dithering)
304.                 # ...But don't actually dither. Because that shit is bananas.
305.                 # Maybe later, I can look up the EGA palette, etc.
306.                 campix = vClampWithDesaturation(campix)
307.
308.                 # Draw pixel (use pygame)
309.                 r = int(campix[0] * 255)
310.                 g = int(campix[1] * 255)
311.                 b = int(campix[2] * 255)
312.
313.                 pixels[x][y] = pygame.Color(r,g,b)
314.
315.                 #del pixels?
316.                 pygame.transform.scale(frame, (screenw,screenh), screen)
317.                 pygame.display.flip()
318.
319.                 for e in pygame.event.get():
320.                     if e.type == pygame.QUIT:
321.                         pygame.quit()
322.                         sys.exit()
323.
324.         frames.append(frame.copy())
325.
326.         print 'frame ',frameNo
327.         sys.stdout.flush()
328.
329.         # Tweak coordinates/camera params for next frame
330.         much = 1.0
331.
332.         # In the beginning, do some camera action (play with zoom)
333.         if zoom <= 1.1:
334.             zoom = 1.1
335.         else:
336.             if zoom > 40:
337.                 if zoomdelta > 0.95:
338.                     zoomdelta -= 0.001
339.             elif zoom < 3:
340.                 if zoomdelta < 0.99:
341.                     zoomdelta += 0.001
342.             zoom *= zoomdelta
343.             much = 1.1 / ((zoom/1.1)**3.0)
344.
345.         # Update rotation angle
348.
349.         # dynamically adjust the contrast based on the contents of the
350.         # last frame
351.
352.         middle = (thisframe_min + thisframe_max) * 0.5
353.         span = (thisframe_max - thisframe_min)
354.         thisframe_min = middle - span*0.60 # Avoid dark tones
355.         thisframe_max = middle + span*0.37 # Emphasize bright tones
356.
357.         new_contrast_offset = -thisframe_min
358.         new_contrast = 1 / (thisframe_max - thisframe_min)
359.
360.         # Avoid abrupt changes, though
361.         l = 0.85
362.         if frameNo == 0: l = 0.7
363.         contrast_offset = (contrast_offset*l + new_contrast_offset*(1.0-l))
364.         contrast        = (contrast       *l + new_contrast       *(1.0-l))
365.
366.         sys.exit()
367.     while True:
368.         for f in frames:
369.             pygame.transform.scale(f, (screenw,screenh), screen)
370.             pygame.display.flip()
371.             pygame.time.wait(33)
372.             for e in pygame.event.get():
373.                 if e.type == pygame.QUIT:
374.                     pygame.quit()
375.                     sys.exit()
376.
377. if __name__ == '__main__':
378.     main()
clone this paste RAW Paste Data
Top