106 lines
4.1 KiB
Python
106 lines
4.1 KiB
Python
import numpy, pygame
|
|
|
|
class Point:
|
|
"""Numpy 3-vec"""
|
|
def __init__(self, x, y, z):
|
|
self.vector = numpy.array([x, y, z])
|
|
|
|
def magnitude(self):
|
|
return numpy.linalg.norm(self.vector)
|
|
|
|
def normalize(self):
|
|
self.vector = self.vector/self.magnitude()
|
|
return self
|
|
|
|
def distanceFrom(self, otherPoint:"Point"):
|
|
return numpy.linalg.norm(self.vector - otherPoint.vector)
|
|
|
|
def add(p1, p2):
|
|
sum = numpy.add(p1.vector, p2.vector)
|
|
return Point(sum[0], sum[1], sum[2])
|
|
|
|
def subtract(p1, p2):
|
|
diff = numpy.subtract(p1.vector, p2.vector)
|
|
return Point(diff[0], diff[1], diff[2])
|
|
|
|
def dot(p1, p2):
|
|
return numpy.dot(p1.vector, p2.vector)
|
|
|
|
def scalarMult(p1, scalar):
|
|
mult = p1.vector * scalar
|
|
return Point(mult[0], mult[1], mult[2])
|
|
|
|
|
|
Point.zero = Point(0, 0, 0)
|
|
|
|
class Ray:
|
|
def __init__(self, origin:Point, direction:Point):
|
|
self.origin = origin
|
|
self.direction = direction
|
|
|
|
class Line:
|
|
def __init__(self, p1:Point, p2:Point):
|
|
self.p1 = p1
|
|
self.p2 = p2
|
|
|
|
def intersectWithPlane(self, plane):
|
|
lineVec = Point.subtract(self.p2, self.p1)
|
|
dot = Point.dot(plane.normal, lineVec)
|
|
|
|
if abs(dot) > 1e-6:
|
|
w = Point.subtract(self.p1, plane.point)
|
|
fac = -Point.dot(plane.normal, w) / dot
|
|
u = Point.scalarMult(lineVec, fac)
|
|
return Point.add(self.p1, u)
|
|
else:
|
|
return None
|
|
|
|
class Plane:
|
|
def __init__(self, point:Point, normal:Point):
|
|
self.point = point
|
|
self.normal = normal
|
|
|
|
class Camera:
|
|
"""Object which will be used to paint pixels on screen."""
|
|
def __init__(self, surface:pygame.Surface, location:Point, target:"Planet", objects, hFOV = 55, vFOV = 55):
|
|
self.surface = surface
|
|
self.objects = objects
|
|
self.location = location
|
|
self.target = target
|
|
self.hFOV = hFOV
|
|
self.vFOV = vFOV
|
|
|
|
def isInside(self, planet:"Planet"):
|
|
"""returns True if camera is inside the planet."""
|
|
return numpy.linalg.norm(self.location.magnitude) < planet.radius
|
|
|
|
def renderFrame(self):
|
|
"""generates a frame and draws it to the surface. Does not update screen; use pygame.display.flip()"""
|
|
winWidth, winHeight = self.surface.get_size()
|
|
winDistance = winWidth * numpy.cos(numpy.radians(self.hFOV)/2) / 2 #distance for a virtual screen to exist in-space to give the correct FOV
|
|
vecToCenter = Point.subtract(self.target.location, self.location)
|
|
vecToCenter.normalize()
|
|
screenPlane = Plane(Point.add(self.location, Point.scalarMult(vecToCenter, winDistance)), vecToCenter)
|
|
screenSurface = pygame.Surface((winWidth, winHeight))
|
|
#pygame uses 0,0 as the top left corner
|
|
for obj in self.objects:
|
|
if type(obj).__name__ == "OrbitingBody":
|
|
lineToCamera = Line(obj.location, self.location)
|
|
intersectPoint = lineToCamera.intersectWithPlane(screenPlane)
|
|
if intersectPoint is not None:
|
|
intersectPoint = Point.add(intersectPoint, Point(int(winWidth/2), int(winHeight/2), 0))
|
|
pygame.draw.circle(screenSurface, (255,255,150), (int(intersectPoint.vector[0]), int(intersectPoint.vector[1])), obj.displaySize)
|
|
elif type(obj).__name__ == "Planet":
|
|
lineToCamera = Line(obj.location, self.location)
|
|
intersectPoint = lineToCamera.intersectWithPlane(screenPlane)
|
|
if intersectPoint is not None:
|
|
intersectPoint = Point.add(intersectPoint, Point(int(winWidth/2), int(winHeight/2), 0))
|
|
pygame.draw.circle(screenSurface, (255,255,150), (int(intersectPoint.vector[0]), int(intersectPoint.vector[1])), 15)
|
|
|
|
screenSurface = pygame.transform.flip(screenSurface, False, True)
|
|
self.surface.blit(screenSurface, (0,0))
|
|
|
|
|
|
#for row in range(int(-winHeight/2), int(winHeight/2)):
|
|
# for column in range(int(-winWidth/2), int(winWidth/2)):
|
|
# line = Line(self.location, Point(self.location.x + column)) |