satellite motion and rendering complete
This commit is contained in:
parent
6403e48f22
commit
294fd10388
|
@ -23,6 +23,9 @@
|
|||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="OrbitSim.py" />
|
||||
<Compile Include="renderer.py">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Interpreter Include="env\BoSLOOenv\">
|
||||
|
|
79
OrbitSim.py
79
OrbitSim.py
|
@ -1,4 +1,6 @@
|
|||
import os, json, numpy, pygame
|
||||
import os, json, numpy, pygame, time
|
||||
from renderer import *
|
||||
from copy import deepcopy
|
||||
|
||||
groundControlPath = "GroundControlFiles"
|
||||
configPath = os.path.join("ConfigFiles", "OrbitSim")
|
||||
|
@ -13,10 +15,8 @@ def config():
|
|||
if not os.path.exists(configFilename):
|
||||
#generate default
|
||||
config_dic = {
|
||||
"g": 6674,
|
||||
"gExp": -14, #G = g * 10^gExp
|
||||
"earthMass": 5972,
|
||||
"earthMassExp": 21, #Me = earthMass * 10^earthMassExp; in kg
|
||||
"G": 6.674e-11,
|
||||
"earthMass": 5.972e24, #in kg
|
||||
"earthRadius": 6378000, #meters
|
||||
"timeScale": 1 #higher number go faster wheeeeee
|
||||
}
|
||||
|
@ -27,61 +27,56 @@ def config():
|
|||
with open(configFilename) as file:
|
||||
return json.load(file)
|
||||
|
||||
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 distanceFrom(self, otherPoint:"Point"):
|
||||
return numpy.linalg.norm(self.vector - otherPoint.vector)
|
||||
|
||||
Point.zero = Point(0, 0, 0)
|
||||
|
||||
class Camera:
|
||||
"""Object which will be used to paint pixels on screen."""
|
||||
def __init__(self, location:Point, target:Point = Point.zero, FOV = 75):
|
||||
self.location = location
|
||||
self.target = target
|
||||
self.FOV = FOV
|
||||
|
||||
def isInside(self, planet:"Planet"):
|
||||
"""returns True if camera is inside the planet."""
|
||||
return numpy.linalg.norm(self.location.magnitude) < planet.radius
|
||||
|
||||
class OrbitingBody:
|
||||
"""a zero-mass point object parented to a planet"""
|
||||
def __init__(self, location:Point, velocity:Point, name, displaySize, parentPlanet):
|
||||
self.location = location
|
||||
self.velocity = velocity
|
||||
self.name = name
|
||||
self.displaySize = displaySize #the size of the object on camera, for visibility reasons
|
||||
self.displaySize = displaySize #the size of the object on camera in pixels, for visibility reasons
|
||||
self.parentPlanet = parentPlanet
|
||||
|
||||
class Planet:
|
||||
"""A massive body at 0,0,0 and a given radius."""
|
||||
def __init__(self, name, mass, radius, rotationPeriod):
|
||||
def __init__(self, name, mass, radius, rotationPeriod, location:Point = deepcopy(Point.zero)):
|
||||
"""Rotation period given in seconds."""
|
||||
self.location = location
|
||||
self.name = name
|
||||
self.mass = mass
|
||||
self.radius = radius
|
||||
self.rotationPercentage = 0
|
||||
self.rotationPercentage = 0.00
|
||||
self.rotationPeriod = rotationPeriod
|
||||
|
||||
Planet.Earth = Planet("Earth", (config()["earthMass"] * 10**config()["earthMassExp"]), config()["earthRadius"], 86400)
|
||||
def rotate(self, timeDelta:"Seconds"):
|
||||
self.rotationPercentage += timeDelta/self.rotationPeriod
|
||||
if self.rotationPercentage >= 100.0:
|
||||
self.rotationPercentage -= 100.0
|
||||
|
||||
Planet.Earth = Planet("Earth", config()["earthMass"], config()["earthRadius"], 86400)
|
||||
|
||||
def physicsUpdate(objects, deltaTime):
|
||||
"""updates the positions of all orbiting objects in [objects] with timestep deltaTime"""
|
||||
for obj in objects:
|
||||
if type(obj).__name__ == "OrbitingBody":
|
||||
accel = Point.scalarMult(Point.subtract(obj.location, obj.parentPlanet.location).normalize(),-(config()["G"] * obj.parentPlanet.mass)/(Point.subtract(obj.location, obj.parentPlanet.location).magnitude() ** 2))
|
||||
obj.velocity = Point.add(obj.velocity, Point.scalarMult(accel, deltaTime))
|
||||
obj.location = Point.add(obj.location, Point.scalarMult(obj.velocity, deltaTime))
|
||||
|
||||
if __name__=="__main__":
|
||||
pygame.init()
|
||||
pygame.display.set_caption("Spinny")
|
||||
|
||||
window = pygame.display.set_mode((400, 400))
|
||||
window = pygame.display.set_mode((600, 600))
|
||||
resolutionDownscaling = 2
|
||||
pygame.display.flip()
|
||||
|
||||
frameTime = 1/30 #framerate
|
||||
|
||||
running = True
|
||||
display = False
|
||||
thisEarth = deepcopy(Planet.Earth)
|
||||
sat = OrbitingBody(Point(config()["earthRadius"] * 1.1, 0, 0), Point(0,1000,-6500), "BoSLOO", 3, thisEarth)
|
||||
renderObjects = [thisEarth, sat]
|
||||
|
||||
while running:
|
||||
for event in pygame.event.get():
|
||||
|
@ -90,7 +85,21 @@ if __name__=="__main__":
|
|||
elif event.type == pygame.MOUSEBUTTONDOWN:
|
||||
if not display:
|
||||
display = True
|
||||
camera = Camera(Point(0, 0, 6378000*4))
|
||||
camera = Camera(window, Point(0, 0, 3 * config()["earthRadius"]), thisEarth, renderObjects)
|
||||
pygame.draw.circle(window, (255,255,255), pygame.mouse.get_pos(), 100)
|
||||
camera.renderFrame()
|
||||
pygame.display.flip()
|
||||
else:
|
||||
display = False
|
||||
window.fill((0,0,0))
|
||||
pygame.display.flip()
|
||||
if display:
|
||||
deltaTime = frameTime * config()["timeScale"]
|
||||
physicsUpdate(renderObjects, deltaTime)
|
||||
camera.renderFrame()
|
||||
pygame.display.flip()
|
||||
time.sleep(frameTime)
|
||||
|
||||
pygame.quit()
|
||||
|
||||
print("Bye!")
|
106
renderer.py
Normal file
106
renderer.py
Normal file
|
@ -0,0 +1,106 @@
|
|||
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))
|
Loading…
Reference in a new issue