class Render: def __init__(self,width,height): self.globalLight = 0.45 self.accelerate = True self.width = width self.height = height # world self.world = World() self.world.setCube() #self.world.setTest() # Viewport Transformation nx = self.width # number of x in screen ny = self.height # number of y in screen self.Tvp = ViewportTransformation(nx,ny) # Camera Transformation self.viewDist = 200 self.Tcam = CamaraTransformation(self.viewDist) # Perspective Projection self.Tper = PerspectiveProjection(l=-100,r=100,b=-100,t=100,n=-100,f=-300) # background self.backgound = Color(255,255,255) self.lineColor = Color(0,0,0) def toggleAccelerate(self): self.accelerate = not self.accelerate def changeViewDist(self,deltaViewDist): newViewDist = self.viewDist + deltaViewDist if newViewDist < 142 or newViewDist > 1000: return self.viewDist = newViewDist self.Tcam = CamaraTransformation(self.viewDist) def naiveDrawLine(self,window,p,q): # see https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm x0,y0 = p x1,y1 = q if x0 > x1: p,q = q,p x0,y0 = p x1,y1 = q # x0 <= x1 x0,y0,x1,y1 = int(x0),int(y0),int(x1),int(y1) dx = abs(x1-x0) dy = abs(y1-y0) if x0 < x1: sx = 1 else: sx = -1 if y0 < y1: sy = 1 else: sy = -1 err = dx-dy while True: p = [x0,y0] pygame.draw.line(window,self.lineColor,p,p) if x0 == x1 and y0 == y1: break e2 = 2 * err if e2 > -dy: err = err - dy x0 = x0 + sx if x0 == x1 and y0 == y1: p = [x0,y0] pygame.draw.line(window,self.lineColor,p,p) break if e2 < dx: err = err + dx y0 = y0 + sy def drawLine(self,window,p,q): if self.accelerate: pygame.draw.aaline(window,self.lineColor,p,q, 2) else: self.naiveDrawLine(window,p,q) def naiveDrawPolygon(self,window,color,pointList): if len(pointList) > 3: self.naiveDrawPolygon(window,color,pointList[:3]) self.naiveDrawPolygon(window,color,[pointList[0]]+pointList[2:]) return x1,y1 = pointList[0][0], pointList[0][1] x2,y2 = pointList[1][0], pointList[1][1] x3,y3 = pointList[2][0], pointList[2][1] x2,y2 = x2-x1, y2-y1 x3,y3 = x3-x1, y3-y1 if x2*y3 - x3*y2 > 0: pointList[2],pointList[1] = pointList[1], pointList[2] #print(pointList) x1,y1 = pointList[0][0], pointList[0][1] x2,y2 = pointList[1][0], pointList[1][1] x3,y3 = pointList[2][0], pointList[2][1] minx = int(min(x1,x2,x3)) maxx = int(max(x1,x2,x3)) miny = int(min(y1,y2,y3)) maxy = int(max(y1,y2,y3)) for x in range(minx,maxx+1): for y in range(miny,maxy+1): if (x1 - x2) * (y - y1) - (y1 - y2) * (x - x1) >= 0 and \ (x2 - x3) * (y - y2) - (y2 - y3) * (x - x2) >= 0 and \ (x3 - x1) * (y - y3) - (y3 - y1) * (x - x3) >= 0: p = [x,y] pygame.draw.line(window,color,p,p) def drawPolygon(self,window,color,pointList): if self.accelerate: pygame.draw.polygon( window, color, pointList ) else: self.naiveDrawPolygon( window, color, pointList ) def draw(self,window): window.fill( self.backgound ) shapes = self.world.geometryObjects shapes.sort(key=lambda t:-t.getDist(matrix([0,0,self.viewDist]).transpose())) M = self.Tvp.M*self.Tper.M*self.Tcam.M for shape in shapes: pointList = [] for a in shape.vertices: ext_a = vstack([a.coordinate,[1]]) p = M * ext_a px, py = p[0]/p[3], p[1]/p[3] pointList.append( [int(px),self.height-int(py)] ) # calculate color n1 = shape.vertices[1].coordinate - shape.vertices[0].coordinate n2 = shape.vertices[2].coordinate - shape.vertices[0].coordinate n = cross(n1.transpose(),n2.transpose()).transpose() n = n / linalg.norm(n) p = matrix([0,0,self.viewDist]).transpose() p = p / linalg.norm(p) colorScale = 1.0-abs(linalg.norm( cross(n.transpose(),p.transpose()) )) colorScale = self.globalLight+(1-self.globalLight)*colorScale color = Color( int(shape.color.r*colorScale), int(shape.color.g*colorScale), int(shape.color.b*colorScale) ) self.drawPolygon( window, color, pointList ) lines = shape.getLines(); for line in lines: a, b = line[0], line[1] ext_a = vstack([a.coordinate,[1]]) ext_b = vstack([b.coordinate,[1]]) p = M * ext_a q = M * ext_b px, py = p[0]/p[3], p[1]/p[3] qx, qy = q[0]/q[3], q[1]/q[3] self.drawLine(window,[px,self.height-py],[qx,self.height-qy])