# HG changeset patch # User paulb # Date 1189901427 0 # Node ID 2160dc40d9d9efc9d86568349d3b5a4a0cc57e53 # Parent 77f3347f8be91f6b68e1725bb0b8fdaa2417ec63 [project @ 2007-09-16 00:10:27 by paulb] Made separate main programs for the scene. diff -r 77f3347f8be9 -r 2160dc40d9d9 examples/PyGmy/README.txt --- a/examples/PyGmy/README.txt Sun Sep 16 00:02:49 2007 +0000 +++ b/examples/PyGmy/README.txt Sun Sep 16 00:10:27 2007 +0000 @@ -5,6 +5,6 @@ This directory contains a version modified for use with pprocess. See the copyright and licensing information in the main README.txt file for details. -By changing the import statement in scene.py and running that program (making -sure that pprocess is either installed or found by the PYTHONPATH), different +By running scene.py, scene_pmap.py or scene_queue.py (making sure that +pprocess is either installed or found by the PYTHONPATH), different implementations can be tried - they should all produce the same image. diff -r 77f3347f8be9 -r 2160dc40d9d9 examples/PyGmy/ppygmy_points.py --- a/examples/PyGmy/ppygmy_points.py Sun Sep 16 00:02:49 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,395 +0,0 @@ -#!/usr/bin/env python - -""" -An adaptation of pygmy.py ("a rubbish raytracer") employing pprocess -functionality in order to take advantage of multiprocessing environments. - --------- - -Copyright (C) 2005 Dave Griffiths -Copyright (C) 2006, 2007 Paul Boddie - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -""" - -import Image, ImageDraw, random, copy -from math import * -import pprocess -import sys - -def sq(a): - return a*a - -class vec: - def __init__(self, x, y, z): - self.x=float(x) - self.y=float(y) - self.z=float(z) - - def __add__(self,other): - return vec(self.x+other.x,self.y+other.y,self.z+other.z) - - def __sub__(self,other): - return vec(self.x-other.x,self.y-other.y,self.z-other.z) - - def __mul__(self,amount): - return vec(self.x*amount,self.y*amount,self.z*amount) - - def __div__(self,amount): - return vec(self.x/amount,self.y/amount,self.z/amount) - - def __neg__(self): - return vec(-self.x,-self.y,-self.z) - - def dot(self,other): - return (self.x*other.x)+(self.y*other.y)+(self.z*other.z) - - def cross(self,other): - return vec(self.y*other.z - self.z*other.y, - self.z*other.x - self.x*other.z, - self.x*other.y - self.y*other.x) - - def dist(self,other): - return sqrt((other.x-self.x)*(other.x-self.x)+ - (other.y-self.y)*(other.y-self.y)+ - (other.z-self.z)*(other.z-self.z)) - - def sq(self): - return sq(self.x)+sq(self.y)+sq(self.z) - - def mag(self): - return self.dist(vec(0,0,0)) - - def norm(self): - mag=self.mag() - if mag!=0: - self.x=self.x/mag - self.y=self.y/mag - self.z=self.z/mag - - def reflect(self,normal): - vdn=self.dot(normal)*2 - return self-normal*vdn - -class line: - def __init__(self, start, end): - self.start=start - self.end=end - - def vec(self): - return self.end-self.start - - def closestpoint(self, point): - l=self.end-self.start - l2=point-self.start - t=l.dot(l2) - if t<=0: return self.start - if t>l.mag(): return self.end - return self.start+l*t - -class renderobject: - def __init__(self, shader): - self.shader=shader - - def intersect(self,l): - return "none",vec(0,0,0),vec(0,0,0) # type, position, normal - -class plane(renderobject): - def __init__(self,plane,dist,shader): - renderobject.__init__(self,shader) - self.plane=plane - self.dist=dist - - def intersect(self,l): - vd=self.plane.dot(l.vec()) - if vd==0: return "none",vec(0,0,0),vec(0,0,0) - v0 = -(self.plane.dot(l.start)+self.dist) - t = v0/vd - if t<0 or t>1: return "none",vec(0,0,0),vec(0,0,0) - return "one",l.start+(l.vec()*t),self.plane - - -class sphere(renderobject): - def __init__(self, pos, radius, shader): - renderobject.__init__(self,shader) - self.pos=pos - self.radius=radius - - def disttoline(self,l): - return self.pos.dist(l.closestpoint(self.pos)) - - def intersect(self,l): - lvec=l.vec() - a = sq(lvec.x)+sq(lvec.y)+sq(lvec.z) - - b = 2*(lvec.x*(l.start.x-self.pos.x)+ \ - lvec.y*(l.start.y-self.pos.y)+ \ - lvec.z*(l.start.z-self.pos.z)) - - c = self.pos.sq()+l.start.sq() - \ - 2*(self.pos.x*l.start.x+self.pos.y*l.start.y+self.pos.z*l.start.z)-sq(self.radius) - - i = b*b-4*a*c - - intersectiontype="none" - pos=vec(0,0,0) - norm=vec(0,0,0) - t=0 - - if i>0 : - if i==0: - intersectiontype="one" - t = -b/(2*a); - else: - intersectiontype="two" - t = (-b - sqrt( b*b - 4*a*c )) / (2*a) - # just bother with one for the moment - # t2= (-b + sqrt( b*b - 4*a*c )) / (2*a) - - if t>0 and t<1: - pos = l.start+lvec*t - norm=pos-self.pos - norm.norm() - else: - intersectiontype="none" - - return intersectiontype,pos,norm - - def intersects(self,l): - return self.disttoline(l)0: - lray=copy.copy(shaderinfo["ray"]) - ray=lray.vec() - normal=copy.copy(shaderinfo["normal"]) - ray=ray.reflect(normal) - reflected=line(shaderinfo["position"],shaderinfo["position"]+ray) - obj=shaderinfo["thisobj"] - objects=shaderinfo["objects"] - newshaderinfo = copy.copy(shaderinfo) - newshaderinfo["ray"]=reflected - newshaderinfo["depth"]=depth-1 - # todo - depth test - for ob in objects: - if ob is not obj: - intersects,position,normal = ob.intersect(reflected) - if intersects is not "none": - newshaderinfo["thisobj"]=ob - newshaderinfo["position"]=position - newshaderinfo["normal"]=normal - col=col+ob.shader.shade(newshaderinfo) - return col - - def isoccluded(self,ray,shaderinfo): - dist=ray.mag() - test=line(shaderinfo["position"],shaderinfo["position"]+ray) - obj=shaderinfo["thisobj"] - objects=shaderinfo["objects"] - # todo - depth test - for ob in objects: - if ob is not obj: - intersects,position,normal = ob.intersect(test) - if intersects is not "none": - return 1 - return 0 - - def doocclusion(self,samples,shaderinfo): - # not really very scientific, or good in any way... - oc=0.0 - for i in range(0,samples): - ray=vec(random.randrange(-100,100),random.randrange(-100,100),random.randrange(-100,100)) - ray.norm() - ray=ray*2.5 - if self.isoccluded(ray,shaderinfo): - oc=oc+1 - oc=oc/float(samples) - return 1-oc - - def getcolour(self,ray,shaderinfo): - depth=shaderinfo["depth"] - col=vec(0,0,0) - if depth>0: - test=line(shaderinfo["position"],shaderinfo["position"]+ray) - obj=shaderinfo["thisobj"] - objects=shaderinfo["objects"] - newshaderinfo = copy.copy(shaderinfo) - newshaderinfo["ray"]=test - newshaderinfo["depth"]=depth-1 - # todo - depth test - for ob in objects: - if ob is not obj: - intersects,position,normal = ob.intersect(test) - if intersects is not "none": - newshaderinfo["thisobj"]=ob - newshaderinfo["position"]=position - newshaderinfo["normal"]=normal - col=col+ob.shader.shade(newshaderinfo) - return col - - def docolourbleed(self,samples,shaderinfo): - # not really very scientific, or good in any way... - col=vec(0,0,0) - for i in range(0,samples): - ray=vec(random.randrange(-100,100),random.randrange(-100,100),random.randrange(-100,100)) - ray.norm() - ray=ray*5 - col=col+self.getcolour(ray,shaderinfo) - col=col/float(samples) - return col - - def shade(self,shaderinfo): - col=vec(0,0,0) - for lite in shaderinfo["lights"]: - col=col+lite.light(shaderinfo) - return col - -class world: - def __init__(self,width,height): - self.lights=[] - self.objects=[] - self.cameratype="persp" - self.width=width - self.height=height - self.backplane=2000.0 - self.imageplane=5.0 - self.aspect=self.width/float(self.height) - - def render_point(self, channel, sx, sy): - - """ - Render the given point, using the 'channel' provided to communicate - result data back to the coordinating process, and using 'sx' and 'sy' as - the point position. A tuple containing 'sx', 'sy' and a result is - returned by this function via the given 'channel'. - """ - - x=2*(0.5-sx/float(self.width))*self.aspect - y=2*(0.5-sy/float(self.height)) - if self.cameratype=="ortho": - ray = line(vec(x,y,0),vec(x,y,self.backplane)) - else: - ray = line(vec(0,0,0),vec(x,y,self.imageplane)) - ray.end=ray.end*self.backplane - - col=vec(0,0,0) - depth=self.backplane - shaderinfo={"ray":ray,"lights":self.lights,"objects":self.objects,"depth":2} - - for obj in self.objects: - intersects,position,normal = obj.intersect(ray) - if intersects is not "none": - if position.z0: - depth=position.z - shaderinfo["thisobj"]=obj - shaderinfo["position"]=position - shaderinfo["normal"]=normal - col=obj.shader.shade(shaderinfo) - - channel.send((sx, sy, col)) - - def render(self, filename, limit): - - """ - Render the image with many processes, saving it to 'filename', using the - given process 'limit' to constrain the number of processes used. - """ - - image = Image.new("RGB", (self.width,self.height)) - exchange = PyGmyExchange(limit=limit) - exchange.draw = ImageDraw.Draw(image) - exchange.total = self.width*self.height - exchange.count = 0 - - for y in range(0, self.height): - for x in range(0,self.width): - channel = pprocess.start(self.render_point, x, y) - exchange.add_wait(channel) - - exchange.finish() - image.save(filename) - -class PyGmyExchange(pprocess.Exchange): - - "A convenience class for parallelisation." - - def store_data(self, channel): - - "Store the data arriving on the given 'channel'." - - sx, sy, col = channel.receive() - self.draw.point((sx,sy),fill=(col.x*255,col.y*255,col.z*255)) - self.count = self.count + 1 - - percent = int((self.count/float(self.total))*100) - sys.stdout.write(("\010" * 13) + "%3d%% %3d %3d" % (percent, sx, sy)) - sys.stdout.flush() - -# vim: tabstop=4 expandtab shiftwidth=4 diff -r 77f3347f8be9 -r 2160dc40d9d9 examples/PyGmy/scene.py --- a/examples/PyGmy/scene.py Sun Sep 16 00:02:49 2007 +0000 +++ b/examples/PyGmy/scene.py Sun Sep 16 00:10:27 2007 +0000 @@ -7,10 +7,7 @@ """ import math -# Choose one of the ppygmy imports: from ppygmy import * -#from ppygmy_pmap import * -#from ppygmy_queue import * import sys class everythingshader(shader): diff -r 77f3347f8be9 -r 2160dc40d9d9 examples/PyGmy/scene_pmap.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/PyGmy/scene_pmap.py Sun Sep 16 00:10:27 2007 +0000 @@ -0,0 +1,66 @@ +#!/usr/bin/env python + +""" +An example scene from... + +http://www.pawfal.org/index.php?page=PyGmy +""" + +import math +from ppygmy_pmap import * +import sys + +class everythingshader(shader): + def __init__(self): + pass + + def shade(self,shaderinfo): + col = shader.shade(self,shaderinfo) + ref = self.getreflected(shaderinfo) + col = col*0.5+ref*0.5 + return col*self.doocclusion(10,shaderinfo) + +class spotshader(shader): + def __init__(self): + pass + + def shade(self,shaderinfo): + col = shader.shade(self,shaderinfo) + position=shaderinfo["position"] + jitter=(math.sin(position.x)+math.cos(position.z)) + if jitter>0.5: col=col/2 + ref = self.getreflected(shaderinfo) + return ref*0.5+col*0.5*self.doocclusion(10,shaderinfo) + +if __name__ == "__main__": + w = world(300,200) + numballs=10.0 + offset = vec(0,-5,55) + rad=12.0 + radperball=(2*3.141)/numballs + + for i in range(0,int(numballs)): + x=sin(0.3+radperball*float(i))*rad + y=cos(0.3+radperball*float(i))*rad + w.objects.append(sphere(vec(x,0,y)+offset,2,everythingshader())) + + w.objects.append(sphere(vec(3,3,0)+offset,5,everythingshader())) + w.objects.append(plane(vec(0,1,0),7,spotshader())) + w.lights.append(parallellight(vec(1,1,-1),vec(0.3,0.9,0.1))) + w.lights.append(pointlight(vec(5,100,-5),vec(0.5,0.5,1))) + + if len(sys.argv) > 1: + if "--help" in sys.argv: + print "Specify a limit to the number of processes." + print "For example:" + print "python", sys.argv[0], "4" + sys.exit(1) + else: + limit = int(sys.argv[1]) + else: + limit = 1 + + print "Number of processes:", limit + w.render("test.tif", limit) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 77f3347f8be9 -r 2160dc40d9d9 examples/PyGmy/scene_queue.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/PyGmy/scene_queue.py Sun Sep 16 00:10:27 2007 +0000 @@ -0,0 +1,66 @@ +#!/usr/bin/env python + +""" +An example scene from... + +http://www.pawfal.org/index.php?page=PyGmy +""" + +import math +from ppygmy_queue import * +import sys + +class everythingshader(shader): + def __init__(self): + pass + + def shade(self,shaderinfo): + col = shader.shade(self,shaderinfo) + ref = self.getreflected(shaderinfo) + col = col*0.5+ref*0.5 + return col*self.doocclusion(10,shaderinfo) + +class spotshader(shader): + def __init__(self): + pass + + def shade(self,shaderinfo): + col = shader.shade(self,shaderinfo) + position=shaderinfo["position"] + jitter=(math.sin(position.x)+math.cos(position.z)) + if jitter>0.5: col=col/2 + ref = self.getreflected(shaderinfo) + return ref*0.5+col*0.5*self.doocclusion(10,shaderinfo) + +if __name__ == "__main__": + w = world(300,200) + numballs=10.0 + offset = vec(0,-5,55) + rad=12.0 + radperball=(2*3.141)/numballs + + for i in range(0,int(numballs)): + x=sin(0.3+radperball*float(i))*rad + y=cos(0.3+radperball*float(i))*rad + w.objects.append(sphere(vec(x,0,y)+offset,2,everythingshader())) + + w.objects.append(sphere(vec(3,3,0)+offset,5,everythingshader())) + w.objects.append(plane(vec(0,1,0),7,spotshader())) + w.lights.append(parallellight(vec(1,1,-1),vec(0.3,0.9,0.1))) + w.lights.append(pointlight(vec(5,100,-5),vec(0.5,0.5,1))) + + if len(sys.argv) > 1: + if "--help" in sys.argv: + print "Specify a limit to the number of processes." + print "For example:" + print "python", sys.argv[0], "4" + sys.exit(1) + else: + limit = int(sys.argv[1]) + else: + limit = 1 + + print "Number of processes:", limit + w.render("test.tif", limit) + +# vim: tabstop=4 expandtab shiftwidth=4