Skip to content

Commit

Permalink
#39 Added generated sphere segments at higher mesh quality than planets.
Browse files Browse the repository at this point in the history
So far these sphere segments are always visible, untextured, and take a long time to generate (there's an ugly sleep(0.1) command in there). Also, these sphere segments should point towards the habitat, currently they aren't rotated at all.
  • Loading branch information
pmelanson committed Jan 8, 2019
1 parent a0e5d2e commit 632dc98
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 4 deletions.
2 changes: 1 addition & 1 deletion data/saves/lithobraking.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"entities": [
{
"name": "Habitat",
"x": -60806343434.98506,
"x": -60806427334.98506,
"y": -133925930692.8104,
"vx": -27309.49221664917,
"vy": 12806.8541293456,
Expand Down
99 changes: 96 additions & 3 deletions orbitx/flight_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,7 @@ def callback(*_):
width=1000,
height=600,
center=vpython.vector(0, 0, 0),
autoscale=True,
caption="\n"
autoscale=True
)

self._commands = []
Expand All @@ -68,6 +67,7 @@ def callback(*_):
self._scene.bind('click', self._handle_click)

self._spheres = {}
self._sphere_parts = {}
self._labels = {}

self._texture_path = texture_path
Expand All @@ -84,6 +84,10 @@ def callback(*_):

for planet in physical_state_to_draw.entities:
self._spheres[planet.name] = self._draw_sphere(planet)
self._sphere_parts[planet.name] = self._draw_sphere_segment(planet)
if planet.name == 'Habitat':
# Habitat isn't a sphere, and isn't big enough to need any LOD
del self._sphere_parts[planet.name]
self._labels[planet.name] = self._draw_labels(planet)
if planet.name == DEFAULT_CENTRE:
self.recentre_camera(DEFAULT_CENTRE)
Expand Down Expand Up @@ -275,7 +279,6 @@ def _set_caption(self):
self._scene.append_to_caption(" Help text")
self._scene.append_to_caption(INPUT_CHEATSHEET)


# TODO: create bind functions for target, ref, and NAV MODE
def _set_menus(self):
"""This creates dropped down menu which is used when set_caption."""
Expand Down Expand Up @@ -349,6 +352,7 @@ def draw(self, physical_state_to_draw):
self._scene.pause("Simulation is paused. \n Press 'p' to continue")
for planet in physical_state_to_draw.entities:
self._update_sphere(planet)
self._update_sphere_segment(planet)
if self._show_label:
self._update_label(planet)

Expand Down Expand Up @@ -413,6 +417,31 @@ def _draw_sphere(self, planet):

return obj

def _draw_sphere_segment(self, planet):
if planet.name == 'Habitat':
return None
tri_coords = _build_sphere_segment_vertices(
planet.r)

# Tris will be all the triangles in the sphere segment, but start with
# a triangle at 0, 0, 0. This will be the centre of the compound object
# so we can rotate the compound object around this centre.
tris = [self._vpython.triangle(vs=[
self._vpython.vertex(pos=self._vpython.vector(1, 0, 0)),
self._vpython.vertex(pos=self._vpython.vector(0.5, 0.5, 0)),
self._vpython.vertex(pos=self._vpython.vector(0, -0.5, 0))
])]

for tri in tri_coords:
vec_tri = [self._vpython.vertex(pos=self._vpython.vector(*coord))
for coord in tri]
tris.append(self._vpython.triangle(vs=vec_tri))

import time
time.sleep(0.1)
return self._vpython.compound(
tris, pos=self._posn(planet))

def _update_sphere(self, planet):
sphere = self._spheres[planet.name]
sphere.pos = self._posn(planet)
Expand All @@ -422,6 +451,12 @@ def _update_sphere(self, planet):
sphere.arrow = self._unit_velocity(planet)
self._habitat = planet

def _update_sphere_segment(self, planet):
if planet.name == 'Habitat':
return
sphere_segment = self._sphere_parts[planet.name]
sphere_segment.pos = self._posn(planet)

def _update_label(self, planet):
label = self._labels[planet.name]
label.text = label.text_function(planet)
Expand Down Expand Up @@ -540,3 +575,61 @@ def rate(self, framerate):
</tr>-->
</table>
"""


def _build_sphere_segment_vertices(
radius, refine_steps=4, ang_size=np.deg2rad(30)):
"""Returns a segment of a sphere, which has a specified radius.
The return is a list of xyz-tuples, each representing a vertex."""
# This code inspired by:
# http://blog.andreaskahler.com/2009/06/creating-icosphere-mesh-in-code.html
# Thanks, Andreas Kahler.
# TODO: limit the number of vertices generated to 65536 (the max in a
# vpython compound object).

# We set the 'middle' of the surface we're constructing to be (0, 0, r).
# Think of this as a point on the surface of the sphere centred on (0,0,0)
# with radius r.
# Then, we construct four equilateral triangles that will all meet at
# this point. Each triangle is ang_size degrees away from the 'middle'.

# The values of 100 are placeholders, and get replaced by cos(ang_size) * r
tris = np.array([
((0, 1, 100), (1, 0, 100), (0, 0, 100)),
((1, 0, 100), (0, -1, 100), (0, 0, 100)),
((0, -1, 100), (-1, 0, 100), (0, 0, 100)),
((-1, 0, 100), (0, 1, 100), (0, 0, 100))
])

# Set the z of each xyz-tuple to be radius, and everything else to be
# the coordinates on the radius-sphere times -1, 0, or 1.
tris = np.where(
[True, True, False],
radius * np.sin(ang_size) * tris,
radius * np.cos(ang_size))

def middle_point(left, right):
# Find the midpoint between the xyz-tuples left and right, but also
# on the surface of a sphere (so not just a simple average).
midpoint = (left + right) / 2
midpoint_radial_dist = np.linalg.norm(midpoint)
return radius * midpoint / midpoint_radial_dist

for _ in range(0, refine_steps):
new_tris = np.ndarray(shape=(0, 3, 3))
for tri in tris:
# A tri is a 3-tuple of xyz-tuples.
a = middle_point(tri[0], tri[1])
b = middle_point(tri[1], tri[2])
c = middle_point(tri[2], tri[0])

# Turn one triangle into a triforce projected onto a sphere.
new_tris = np.append(new_tris, [
(tri[0], a, c),
(tri[1], b, a),
(tri[2], c, b),
(a, b, c) # The centre triangle of the triforce
], axis=0)
tris = new_tris

return tris

0 comments on commit 632dc98

Please sign in to comment.