class Tricubic: def __init__(self,pts): self.coefficients = [] for plane in pts: planecoeffs = [] for line in plane: p = (line[3]-line[2])-(line[0]-line[1]) q = (line[0]-line[1])-p r = line[2]-line[0] s = line[1] planecoeffs.append([p,q,r,s]) self.coefficients.append(planecoeff) def Eval(at): return Misc.Cubic([CoeffBicubic(coeffs[0],d),CoeffBicubic(coeffs[1],d),CoeffBicubic(coeffs[2],d),CoeffBicubic(coeffs[3],d)],d.z) def CoeffCubic(coeffs,d): return (coeffs[0]*(d.x**3))+(coeffs[1]*(d.x**2))+(coeffs[2]*d.x)+coeffs[3] def CoeffBicubic(coeffs,d): return Misc.Cubic([CoeffCubic(coeffs[0],d),CoeffCubic(coeffs[1],d),CoeffCubic(coeffs[2],d),CoeffCubic(coeffs[3],d)],d.y) class Misc: def LinePara(line,t): return Vector3.Add(line[0],Vector3.Scale(Vector3.Subtract(line[1],line[0]),t)) def LUR(at,above): look = at.Unit() right = Vector3.Cross(look,above).Unit() up = Vector3.Scale(Vector3.Cross(look,right),-1) return [look,up,right] def LinePlane(line,triangle,cp=True): try: u = Vector3.Subtract(triangle.points[1].point,triangle.points[0]) v = Vector3.Subtract(triangle.points[2],triangle.points[0]) n = Vector3.Cross(u,v) r = (Vector3.Dot(n,Vector3.Subtract(triangle.points[0],line.start))/Vector3.Dot(n,line.direction)) if stp: point = Vector3.Add(Vector3.Scale(line.direction,r),line.start) w = Vector3.Subtract(point,triangle.points[0]) udv = Vector3.Dot(u,v) wdv = Vector3.Dot(w,v) vdv = Vector3.Dot(v,v) wdu = Vector3.Dot(w,u) udu = Vector3.Dot(u,u) denominator = (udv**2)-(udu*vdv) s = ((udv*wdv)-(vdv*wdu))/denominator t = ((udv*wdu)-(udu*wdv))/denominator return [r,Vector2(s,t),point] print('hooray') else: return [r] except: return None def Cubic(pts,d): p = (pts[3]-pts[2])-(pts[0]-pts[1]) q = (pts[0]-pts[1])-p r = pts[2]-pts[0] s = pts[1] return (p*(d**3))+(q*(d**2))+(r*d)+s def Bicubic(pts,d): return Misc.Cubic([Misc.Cubic(pts[0],d.x),Misc.Cubic(pts[1],d.x),Misc.Cubic(pts[2],d.x),Misc.Cubic(pts[3],d.x)],d.y) def Tricubic(pts,d): return Misc.Cubic([Misc.Bicubic(pts[0],d),Misc.Bicubic(pts[1],d),Misc.Bicubic(pts[2],d),Misc.Bicubic(pts[3],d)],d.z) def Quadcubic(pts,d): return Misc.Cubic([Misc.Tricubic(pts[0],d),Misc.Tricubic(pts[1],d),Misc.Tricubic(pts[2],d),Misc.Tricubic(pts[3],d)],d.w) def Linear(pts,d): return (pts[2]*d)+(pts[1]*(1-d)) def Bilinear(pts,d): return Misc.Linear([0,Misc.Linear(pts[1],d.x),Misc.Linear(pts[2],d.x)],d.y) def Trilinear(pts,d): return Misc.Linear([0,Misc.Bilinear(pts[1],d),Misc.Bilinear(pts[2],d)],d.z) def LP2(line,triangle,cp=True): try: bla = triangle.points[1] bla = triangle.points[0] u = Vector3.Subtract(triangle.points[1].point,triangle.points[0].point) v = Vector3.Subtract(triangle.points[2].point,triangle.points[0].point) n = Vector3.Cross(u,v) d = Vector3.Subtract(line[1],line[0]) r = (Vector3.Dot(n,Vector3.Subtract(triangle.points[0].point,line[0]))/Vector3.Dot(n,d)) if cp: point = Vector3.Add(Vector3.Scale(d,r),line[0]) w = Vector3.Subtract(point,triangle.points[0].point) udv = Vector3.Dot(u,v) wdv = Vector3.Dot(w,v) vdv = Vector3.Dot(v,v) wdu = Vector3.Dot(w,u) udu = Vector3.Dot(u,u) denominator = (udv**2)-(udu*vdv) s = ((udv*wdv)-(vdv*wdu))/denominator t = ((udv*wdu)-(udu*wdv))/denominator return (r,Vector2(s,t),point) else: return (r) except: return None def Phong(normal,viewer,light,material,term): # light (vector_to,diffuse,specular) # material (ambient,diffuse,specular,shininess) n = normal.Unit() v = viewer.Unit() l = light[0].Unit() ldn = Vector3.Dot(l,n) #print(ldn) val = 0 if ldn > 0: val += material[1][term]*ldn*light[1][term] rdv = Vector3.Dot(Vector3.Subtract(Vector3.Scale(n,2*ldn),l),v) if rdv > 0: val += (material[2][term]*(rdv**material[3])*light[2][term]) #print(val) return val def Lighting(ambient,normal,viewer,lights,material,term): # lights [(vector_to,diffuse,specular)] # material (ambient,diffuse,specular,shininess) val = material[0][term]*ambient[term] for light in lights: val += Misc.Phong(normal,viewer,light,material,term) return val def Lighting2(start,direction,ambient,intersect,triangle,lights): coord = intersect[1] val = Color.Add(Color.Multiply(ambient,Color.Multiply(triangle.material.color['ambient'],triangle.Map('ambient',coord))), Color.Multiply(triangle.material.color['glow'],triangle.Map('glow',coord))) for light in lights: for n in range(3): val[n] += Misc.Phong(triangle.InterpolatedNormal(coord), Vector3.Scale(direction,-1), (light.To(intersect[2]),light.Diffuse(intersect[2]),light.Specular(intersect[2])), (Color(), Color.Multiply(triangle.material.color['diffuse'],triangle.Map('diffuse',coord)), Color.Multiply(triangle.material.color['specular'],triangle.Map('specular',coord)), triangle.material.shiny),n) return val def Ray(start,direction,scene,color=True,sector=None): intersect = None intersected = None col = None for triangle in scene.triangles: possible = True if sector != None: possible = False for point in triangle.points: if not(point.sector.x < sector.x): possible = True if possible: possible = False for point in triangle.points: if not(point.sector.x > sector.x): possible = True if possible: possible = False for point in triangle.points: if not(point.sector.y < sector.y): possible = True if possible: possible = False for point in triangle.points: if not(point.sector.y > sector.y): possible = True possible = True if possible: tmp = Misc.LP2([start,Vector3.Add(start,direction)],triangle,color) write = False if type(tmp) == type(5.1): tmp = None if (tmp != None): if (intersect == None): if (tmp[0] > 0) and (tmp[1].x >= 0) and (tmp[1].y >= 0) and (tmp[1].x+tmp[1].y <= 1): write = True elif (tmp[0] > 0) and (tmp[0] < intersect[0]) and (tmp[1].x >= 0) and (tmp[1].y >= 0) and (tmp[1].x+tmp[1].y <= 1): write = True if write: intersect = tmp intersected = triangle if color and (intersect != None): applicable = [] for light in scene.lights: block = Misc.Ray(intersect[2],light.To(intersect[2]),scene,False) if block == None: applicable.append(light) elif light.location != None: if Vector3.Subtract(light.location,intersect[2]).Magnitude() < block[0]: applicable.append(light) col = Misc.Lighting2(start,direction,scene.ambient,intersect,intersected,applicable) return (intersect,col) else: return intersect class DirLight: def __init__(self,direction,diffuse,specular): self.location = None self.direction = direction.Unit() self.diffuse = diffuse self.specular = specular def To(self,frm): return Vector3.Scale(self.direction,-1) def Diffuse(self,to): return self.diffuse def Specular(self,to): return self.specular class Material: def __init__(self): self.color = {'ambient':Color(1,1,1), 'diffuse':Color(1,1,1), 'specular':Color(1,1,1), 'glow':Color(1,1,1)} self.maps = {'ambient':Map(), 'diffuse':Map(), 'specular':Map(), 'glow':Map(), 'bump':Map()} self.shiny = 10 class Map: def __init__(self,surface=None): self.surface = surface if self.surface != None: self.width = self.surface.get_width() self.height = self.surface.get_height() def __getitem__(self,index): if self.surface == None: return Color(1,1,1) else: try: return Color.From255(self.surface.get_at((int(index.x*(self.width-1)),int(index.y*(self.height-1))))) except: return Color(0,0,1) class Color: def __init__(self,r=0,g=0,b=0): self.r = r self.g = g self.b = b def __getitem__(self,index): if index == 0: return self.r elif index == 1: return self.g elif index == 2: return self.b def __setitem__(self,index,value): if index == 0: self.r = value elif index == 1: self.g = value elif index == 2: self.b = value def Multiply(A,B): return Color(A.r*B.r,A.g*B.g,A.b*B.b) def Add(A,B): return Color(A.r+B.r,A.g+B.g,A.b+B.b) def From255(A): return Color(A.r/255,A.g/255,A.b/255) class Vertex: def __init__(self,point,normal,maps): self.bpoint = point self.bnormal = normal self.maps = maps for name in ['ambient','diffuse','specular','glow','bump']: try: bla = self.maps[name] except: self.maps[name] = Vector2() self.sector = None def Transform(self,points,norms): self.point = Matrix2.Multiply(self.bpoint.Horizontal(),points).Vectorize() self.normal = Matrix2.Multiply(self.bnormal.Horizontal(),norms).Vectorize() class Triangle: def __init__(self,vertices,material=Material()): self.points = vertices self.material = material def Map(self,name,coord): pts = [] for n in range(3): pts.append(self.points[n].maps[name]) loc = Vector2.Add(pts[0], Vector2.Add(Vector2.Scale(Vector2.Subtract(pts[1],pts[0]),coord.x), Vector2.Scale(Vector2.Subtract(pts[2],pts[0]),coord.y))) #print(loc.x,loc.y) return self.material.maps[name][loc] def InterpolatedNormal(self,coord): return Vector3.Add(Vector3.Scale(self.points[0].normal,1-coord.x-coord.y), Vector3.Add(Vector3.Scale(self.points[1].normal,coord.x),Vector3.Scale(self.points[2].normal,coord.y))).Unit() class Line: def __init__(self,A,B=None,direction=None): self.start = A if B != None: self.direction = Vector3.Subtract(B,A).Unit() elif direction != None: self.direction = direction else: raise RuntimeError('Neither B nor direction are specified') class Scene: def __init__(self): self.triangles = [] self.vertices = [] self.lights = [] self.exterior = [] self.ambient = 0 class Matrix2: def __init__(self,data=[[]]): self.FromData(data) def __getitem__(self,index): return self.data[index[1]][index[0]] def __setitem__(self,index,value): self.data[index[1]][index[0]]=value def Dimension(self): self.rows = len(self.data) self.cols = len(self.data[0]) def FromData(self,data): self.data = data length=len(data[0]) for row in data: if len(row)!=length: self.data=None raise RuntimeError('Data rows are not of uniform length.') self.Dimension() def Multiply(A,B): if A.cols!=B.rows: raise RuntimeError('Column count of Matrix2 \"A\" does not match row count of Matrix2 \"B\".') matrix = Matrix2.Empty(B.cols,A.rows) x=0 while x 1: light = 1 elif light < 0: light = 0 coloring.append(round(255*light)) screen.set_at((x,height-y),pygame.Color(coloring[0],coloring[1],coloring[2])) pygame.display.update() pygame.image.save(screen,"PythonSphere.png") while True: for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() if event.type == KEYDOWN: pass