Skip to content

Commit

Permalink
Merge pull request aosabook#62 from EkkiD/chapter
Browse files Browse the repository at this point in the history
Modeller Chapter
  • Loading branch information
MichaelDiBernardo committed Nov 11, 2014
2 parents 0980f15 + 30c00ad commit da541fb
Show file tree
Hide file tree
Showing 13 changed files with 994 additions and 92 deletions.
Binary file added modeller/AABBError.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 0 additions & 5 deletions modeller/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

Modeller
=========

Expand All @@ -13,10 +12,6 @@ Prerequisites
* A virtualenv with:
* pip install -I pyopengl
* pip install -I numpy
* pip install -I PIL
* pip install -I PyDispatcher
* pip install -I https://pypi.python.org/packages/source/O/OpenGLContext/OpenGLContext-2.2.0a3.tar.gz#md5=b5bdedbdae5215e7acff3b087c8220d3
* pip install -I https://pypi.python.org/packages/source/P/PyVRML97/PyVRML97-2.3.0a3.tar.gz#md5=56cd4dd382cfb5a4ca5fdb88ae8f1733


Running
Expand Down
Binary file added modeller/StartScene.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
886 changes: 886 additions & 0 deletions modeller/chapter.md

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions modeller/interaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ def register(self):
glutMouseFunc(self.handle_mouse_button)
glutMotionFunc(self.handle_mouse_move)
glutKeyboardFunc(self.handle_keystroke)

glutSpecialFunc(self.handle_keystroke)
glutPassiveMotionFunc(None)

def register_callback(self, name, func):
""" registers a callback for a certain event """
Expand Down Expand Up @@ -61,9 +59,9 @@ def handle_mouse_button(self, button, mode, x, y):
elif button == GLUT_LEFT_BUTTON: # pick
self.trigger('pick', x, y)
elif button == 3: # scroll up
self.translate(0, 0, -1.0)
elif button == 4: # scroll up
self.translate(0, 0, 1.0)
elif button == 4: # scroll down
self.translate(0, 0, -1.0)
else: # mouse button release
self.pressed = None
glutPostRedisplay()
Expand All @@ -73,11 +71,11 @@ def handle_mouse_move(self, x, screen_y):
xSize, ySize = glutGet(GLUT_WINDOW_WIDTH), glutGet(GLUT_WINDOW_HEIGHT)
y = ySize - screen_y # invert the y coordinate because OpenGL is inverted
if self.pressed is not None:
dx = self.mouse_loc[0] - x
dy = self.mouse_loc[1] - y
dx = x - self.mouse_loc[0]
dy = y - self.mouse_loc[1]
if self.pressed == GLUT_RIGHT_BUTTON and self.trackball is not None:
# ignore the updated camera loc because we want to always rotate around the origin
self.trackball.drag_to(self.mouse_loc[0], self.mouse_loc[1], -dx, -dy)
self.trackball.drag_to(self.mouse_loc[0], self.mouse_loc[1], dx, dy)
elif self.pressed == GLUT_LEFT_BUTTON:
self.trigger('move', x, y)
elif self.pressed == GLUT_MIDDLE_BUTTON:
Expand All @@ -95,6 +93,8 @@ def handle_keystroke(self, key, x, screen_y):
self.trigger('place', 'sphere', x, y)
elif key == 'c':
self.trigger('place', 'cube', x, y)
elif key == 'f':
self.trigger('place', 'figure', x, y)
elif key == GLUT_KEY_UP:
self.trigger('scale', up=True)
elif key == GLUT_KEY_DOWN:
Expand Down
Binary file added modeller/newtranspipe.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
69 changes: 47 additions & 22 deletions modeller/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,33 @@
class Node(object):
""" Base class for scene elements """
def __init__(self):
self.location = [0, 0, 0]
self.color_index = random.randint(color.MIN_COLOR, color.MAX_COLOR)
self.aabb = AABB([0.0, 0.0, 0.0], [0.5, 0.5, 0.5])
self.translation = numpy.identity(4)
self.scalemat = numpy.identity(4)
self.translation_matrix = numpy.identity(4)
self.scaling_matrix = numpy.identity(4)
self.selected = False
self.scale_mult = 1.0

def render(self):
""" renders the item to the screen """
raise NotImplementedError("The Abstract Node Class doesn't define 'render'")
glPushMatrix()
glMultMatrixf(numpy.transpose(self.translation_matrix))
glMultMatrixf(self.scaling_matrix)
cur_color = color.COLORS[self.color_index]
glColor3f(cur_color[0], cur_color[1], cur_color[2])
if self.selected: # emit light if the node is selected
glMaterialfv(GL_FRONT, GL_EMISSION, [0.3, 0.3, 0.3])

self.render_self()
if self.selected:
glMaterialfv(GL_FRONT, GL_EMISSION, [0.0, 0.0, 0.0])

glPopMatrix()

def render_self(self):
raise NotImplementedError("The Abstract Node Class doesn't define 'render_self'")

def translate(self, x, y, z):
self.translation = numpy.dot(self.translation, translation([x, y, z]))
self.translation_matrix = numpy.dot(self.translation_matrix, translation([x, y, z]))

def rotate_color(self, forwards):
self.color_index += 1 if forwards else -1
Expand All @@ -35,17 +48,16 @@ def rotate_color(self, forwards):
self.color_index = color.MAX_COLOR

def scale(self, up):
s = self.scale_mult * 1.1 if up else 0.9
self.scalemat = numpy.dot(self.scalemat, scaling([s, s, s]))
self.aabb.scale(s)
s = 1.1 if up else 0.9
self.scaling_matrix = numpy.dot(self.scaling_matrix, scaling([s, s, s]))

def pick(self, start, direction, mat):
""" Return whether or not the ray hits the object
Consume: start, direction the ray to check
mat the modelview matrix to transform the ray by """

# transform the modelview matrix by the current translation
newmat = numpy.dot(mat, self.translation)
newmat = numpy.dot(numpy.dot(mat, self.translation_matrix), numpy.linalg.inv(self.scaling_matrix))
results = self.aabb.ray_hit(start, direction, newmat)
return results

Expand All @@ -61,19 +73,8 @@ def __init__(self):
super(Primitive, self).__init__()
self.call_list = None

def render(self):
glPushMatrix()
glMultMatrixf(numpy.transpose(self.translation))
glMultMatrixf(self.scalemat)
cur_color = color.COLORS[self.color_index]
glColor3f(cur_color[0], cur_color[1], cur_color[2])
if self.selected: # emit light if the node is selected
glMaterialfv(GL_FRONT, GL_EMISSION, [0.3, 0.3, 0.3])
def render_self(self):
glCallList(self.call_list)
if self.selected:
glMaterialfv(GL_FRONT, GL_EMISSION, [0.0, 0.0, 0.0])

glPopMatrix()



Expand All @@ -89,3 +90,27 @@ class Cube(Primitive):
def __init__(self):
super(Cube, self).__init__()
self.call_list = G_OBJ_CUBE


class HierarchicalNode(Node):
def __init__(self):
super(HierarchicalNode, self).__init__()
self.child_nodes = []

def render_self(self):
for child in self.child_nodes:
child.render()


class SnowFigure(HierarchicalNode):
def __init__(self):
super(SnowFigure, self).__init__()
self.child_nodes = [Sphere(), Sphere(), Sphere()]
self.child_nodes[0].translate(0, -0.6, 0)
self.child_nodes[1].translate(0, 0.1, 0)
self.child_nodes[1].scaling_matrix = numpy.dot(self.scaling_matrix, scaling([0.8, 0.8, 0.8]))
self.child_nodes[2].translate(0, 0.75, 0)
self.child_nodes[2].scaling_matrix = numpy.dot(self.scaling_matrix, scaling([0.7, 0.7, 0.7]))
for child_node in self.child_nodes:
child_node.color_index = color.MIN_COLOR
self.aabb = AABB([0.0, 0.0, 0.0], [0.5, 1.1, 0.5])
Binary file added modeller/nodes.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added modeller/scale.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 13 additions & 14 deletions modeller/scene.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,29 @@
import sys
import numpy
from node import Sphere, Cube
from node import Sphere, Cube, SnowFigure


class Scene(object):
""" Base class for scene nodes.
Scene nodes currently only include primitives """

# the default depth from the camera to place an object at
PLACE_DEPTH = 15.0

def __init__(self):
""" Initialize the Scene """
# The scene keeps a list of nodes that are displayed
self.node_list = list()
# Keep track of the currently selected node.
# Actions may depend on whether or not something is selected
self.selected_node = None

def add_node(self, node):
""" Add a new node to the scene """
self.node_list.append(node)

def render(self):
""" Render the scene. This function simply calls the render function for each node. """
for node in self.node_list:
node.render()

def add_node(self, node):
""" Add a new node to the scene """
self.node_list.append(node)

def pick(self, start, direction, mat):
""" Execute selection.
Consume: start, direction describing a Ray
Expand All @@ -36,7 +33,8 @@ def pick(self, start, direction, mat):
self.selected_node = None

# Keep track of the closest hit.
mindist, closest_node = sys.maxint, None
mindist = sys.maxint
closest_node = None
for node in self.node_list:
hit, distance = node.pick(start, direction, mat)
if hit and distance < mindist:
Expand All @@ -49,10 +47,10 @@ def pick(self, start, direction, mat):
closest_node.selected_loc = start + direction * mindist
self.selected_node = closest_node

def move(self, start, direction, inv_modelview):
def move_selected(self, start, direction, inv_modelview):
""" Move the selected node, if there is one.
Consume: start, direction describes the Ray to move to
mat is the modelview matrix for the scene """
inv_modelview is the inverse modelview matrix for the scene """
if self.selected_node is None: return

# Find the current depth and location of the selected node
Expand All @@ -76,10 +74,11 @@ def place(self, shape, start, direction, inv_modelview):
""" Place a new node.
Consume: shape the shape to add
start, direction describes the Ray to move to
mat is the modelview matrix for the scene """
inv_modelview is the inverse modelview matrix for the scene """
new_node = None
if shape == 'sphere': new_node = Sphere()
elif shape == 'cube': new_node = Cube()
elif shape == 'figure': new_node = SnowFigure()

self.add_node(new_node)

Expand All @@ -92,12 +91,12 @@ def place(self, shape, start, direction, inv_modelview):

new_node.translate(translation[0], translation[1], translation[2])

def rotate_color(self, forwards):
def rotate_selected_color(self, forwards):
""" Rotate the color of the currently selected node """
if self.selected_node is None: return
self.selected_node.rotate_color(forwards)

def scale(self, up):
def scale_selected(self, up):
""" Scale the current selection """
if self.selected_node is None: return
self.selected_node.scale(up)
1 change: 0 additions & 1 deletion modeller/transformation.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import numpy


def translation(displacement):
t = numpy.identity(4)
t[0, 3] = displacement[0]
Expand Down
Binary file added modeller/translate.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit da541fb

Please sign in to comment.