Add main files
This commit is contained in:
parent
6edf69adf1
commit
10716020af
21
LICENSE
21
LICENSE
@ -1,21 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2014 Michele Guerini Rocco
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
13
README.md
13
README.md
@ -1,4 +1,11 @@
|
|||||||
terrain
|
# Terrain
|
||||||
=======
|
|
||||||
|
|
||||||
Simple terrain generator
|
## Simple terrain generator
|
||||||
|
![Screenshot](screenshot.png)
|
||||||
|
## Info
|
||||||
|
A simple application which uses the diamond square algorithm to generate a terrain and renders it in OpenGL.
|
||||||
|
|
||||||
|
### License
|
||||||
|
Dual licensed under the MIT and GPL licenses:
|
||||||
|
http://www.opensource.org/licenses/mit-license.php
|
||||||
|
http://www.gnu.org/licenses/gpl.html
|
||||||
|
BIN
screenshot.png
Normal file
BIN
screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 46 KiB |
148
terrain.py
Normal file
148
terrain.py
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
import random
|
||||||
|
import numpy
|
||||||
|
import pyglet
|
||||||
|
from pyglet.gl import *
|
||||||
|
from trackball_camera import TrackballCamera
|
||||||
|
|
||||||
|
|
||||||
|
class Terrain():
|
||||||
|
def __init__(self, details):
|
||||||
|
self.size = 2 ** details + 1
|
||||||
|
self.map = numpy.zeros((self.size, self.size))
|
||||||
|
|
||||||
|
def _square(self, x, y, size, offset):
|
||||||
|
average = sum([
|
||||||
|
self.map[x - size][y - size],
|
||||||
|
self.map[x + size][y - size],
|
||||||
|
self.map[x + size][y + size],
|
||||||
|
self.map[x - size][y + size]]) / 4
|
||||||
|
self.map[x][y] = average + offset
|
||||||
|
|
||||||
|
def _diamond(self, x, y, size, offset):
|
||||||
|
average = sum([
|
||||||
|
self.map[x][y - size],
|
||||||
|
self.map[x + size][y],
|
||||||
|
self.map[x][y + size],
|
||||||
|
self.map[x - size][y]]) / 4
|
||||||
|
self.map[x][y] = average + offset
|
||||||
|
|
||||||
|
def generate(self, high):
|
||||||
|
i, half = self.size - 1, (self.size - 1) / 2
|
||||||
|
scale = high * self.size
|
||||||
|
|
||||||
|
while i > 1:
|
||||||
|
for y in numpy.arange(half, self.size - 1, i):
|
||||||
|
for x in numpy.arange(half, self.size - 1, i):
|
||||||
|
self._square(x, y, half, scale / 2 * random.random())
|
||||||
|
for y in numpy.arange(0, self.size - 1, half):
|
||||||
|
for x in numpy.arange((y + half) % i, self.size - 1, i):
|
||||||
|
self._diamond(x, y, half, scale / 2 * random.random())
|
||||||
|
i /= 2
|
||||||
|
half = i / 2
|
||||||
|
scale = high * half * 2
|
||||||
|
|
||||||
|
|
||||||
|
class Window(pyglet.window.Window):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.terrain = None
|
||||||
|
self.camera = kwargs['camera']
|
||||||
|
del kwargs['camera']
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
# Initialize OpenGL
|
||||||
|
glEnable(GL_DEPTH_TEST)
|
||||||
|
glDisable(GL_CULL_FACE)
|
||||||
|
|
||||||
|
def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers):
|
||||||
|
"""Move camera/zoom on mouse drag"""
|
||||||
|
if buttons & pyglet.window.mouse.LEFT:
|
||||||
|
self.camera.mouse_roll(
|
||||||
|
self._norm(x, self.width),
|
||||||
|
self._norm(y, self.height))
|
||||||
|
elif buttons & pyglet.window.mouse.RIGHT:
|
||||||
|
self.camera.mouse_zoom(
|
||||||
|
self._norm(x * 2, self.width),
|
||||||
|
self._norm(y * 2, self.height))
|
||||||
|
|
||||||
|
def on_mouse_press(self, x, y, button, modifiers):
|
||||||
|
"""Move camera/zoom on mouse drag"""
|
||||||
|
if button == pyglet.window.mouse.LEFT:
|
||||||
|
self.camera.mouse_roll(
|
||||||
|
self._norm(x, self.width),
|
||||||
|
self._norm(y, self.height),
|
||||||
|
False)
|
||||||
|
elif button == pyglet.window.mouse.RIGHT:
|
||||||
|
self.camera.mouse_zoom(
|
||||||
|
self._norm(x * 2, self.width),
|
||||||
|
self._norm(y * 2, self.height),
|
||||||
|
False)
|
||||||
|
|
||||||
|
def on_resize(self, width, height):
|
||||||
|
"""Adjust drawing after window is resized"""
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
glViewport(0, 0, self.width, self.height)
|
||||||
|
self.on_show()
|
||||||
|
|
||||||
|
def on_show(self):
|
||||||
|
"""Set OpenGl config"""
|
||||||
|
glMatrixMode(GL_PROJECTION)
|
||||||
|
glLoadIdentity()
|
||||||
|
gluPerspective(40, self.width / self.height, 1, 400)
|
||||||
|
self.camera.update_modelview()
|
||||||
|
|
||||||
|
def on_draw(self):
|
||||||
|
"""Draw the current frame"""
|
||||||
|
if self.terrain is None:
|
||||||
|
return
|
||||||
|
self.clear()
|
||||||
|
map, size = self.terrain.map, self.terrain.size
|
||||||
|
|
||||||
|
glPushMatrix()
|
||||||
|
glTranslatef(-size / 2, 0, -size / 2)
|
||||||
|
|
||||||
|
# Draw terrain
|
||||||
|
for x in numpy.arange(size - 1):
|
||||||
|
for y in numpy.arange(size - 1):
|
||||||
|
glBegin(GL_TRIANGLE_STRIP)
|
||||||
|
glColor3f(map[x][y] / 20, map[x][y] / 20, map[x][y] / 20)
|
||||||
|
glVertex3f(x, map[x][y], y)
|
||||||
|
glVertex3f(x + 1, map[x + 1][y], y)
|
||||||
|
glVertex3f(x, map[x][y + 1], y + 1)
|
||||||
|
glVertex3f(x + 1, map[x + 1][y + 1], y + 1)
|
||||||
|
glEnd()
|
||||||
|
glPopMatrix()
|
||||||
|
|
||||||
|
# Draw axis
|
||||||
|
glBegin(GL_LINES)
|
||||||
|
glColor3f(1, 0, 0)
|
||||||
|
glVertex3f(-size, 0, 0)
|
||||||
|
glVertex3f(size, 0, 0)
|
||||||
|
glColor3f(0, 1, 0)
|
||||||
|
glVertex3f(0, -size, 0)
|
||||||
|
glVertex3f(0, size, 0)
|
||||||
|
glColor3f(0, 0, 1)
|
||||||
|
glVertex3f(0, 0, -size)
|
||||||
|
glVertex3f(0, 0, size)
|
||||||
|
glEnd()
|
||||||
|
|
||||||
|
def _norm(self, x, max_x):
|
||||||
|
"""given x within [0,max_x], scale to a range [-1,1]"""
|
||||||
|
return (2 * x - float(max_x)) / float(max_x)
|
||||||
|
|
||||||
|
def draw(self, terrain):
|
||||||
|
"""Render the height map"""
|
||||||
|
self.terrain = terrain
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
terrain = Terrain(5)
|
||||||
|
camera = TrackballCamera(150)
|
||||||
|
window = Window(caption="Terrain", resizable=True,
|
||||||
|
width=800, height=600, camera=camera)
|
||||||
|
terrain.generate(0.7)
|
||||||
|
window.draw(terrain)
|
||||||
|
pyglet.app.run()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
338
trackball_camera.py
Normal file
338
trackball_camera.py
Normal file
@ -0,0 +1,338 @@
|
|||||||
|
"""trackball_camera.py - An OpenGL Trackball Camera Class for Pyglet
|
||||||
|
|
||||||
|
by Roger Allen, July 2008
|
||||||
|
roger@rogerandwendy.com
|
||||||
|
|
||||||
|
A class for simple-minded 3d example apps.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
Initialize with a radius from the center/focus point:
|
||||||
|
|
||||||
|
tbcam = TrackballCamera(5.0)
|
||||||
|
|
||||||
|
After adjusting your projection matrix, set the modelview matrix.
|
||||||
|
|
||||||
|
tbcam.update_modelview()
|
||||||
|
|
||||||
|
On each primary mouse click, scale the x & y to [-1,1] and call:
|
||||||
|
|
||||||
|
tbcam.mouse_roll(x,y,False)
|
||||||
|
|
||||||
|
On each primary mouse drag, scale the x & y to [-1,1] and call:
|
||||||
|
|
||||||
|
tbcam.mouse_roll(x,y)
|
||||||
|
|
||||||
|
Mouse movements adjust the modelview projection matrix directly.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
__version__ = "1.0"
|
||||||
|
|
||||||
|
# Code derived from the GLUT trackball.c, but now quite different and
|
||||||
|
# customized for pyglet.
|
||||||
|
#
|
||||||
|
# I simply wanted an easy-to-use trackball camera for quick-n-dirty
|
||||||
|
# opengl programs that I'd like to write. Finding none, I grabbed
|
||||||
|
# the trackball.c code & started hacking.
|
||||||
|
#
|
||||||
|
# Originally implemented by Gavin Bell, lots of ideas from Thant Tessman
|
||||||
|
# and the August '88 issue of Siggraph's "Computer Graphics," pp. 121-129.
|
||||||
|
# and David M. Ciemiewicz, Mark Grossman, Henry Moreton, and Paul Haeberli
|
||||||
|
#
|
||||||
|
# Note: See the following for more information on quaternions:
|
||||||
|
#
|
||||||
|
# - Shoemake, K., Animating rotation with quaternion curves, Computer
|
||||||
|
# Graphics 19, No 3 (Proc. SIGGRAPH'85), 245-254, 1985.
|
||||||
|
# - Pletinckx, D., Quaternion calculus as a basic tool in computer
|
||||||
|
# graphics, The Visual Computer 5, 2-13, 1989.
|
||||||
|
#
|
||||||
|
# Gavin Bell's code had this copyright notice:
|
||||||
|
# (c) Copyright 1993, 1994, Silicon Graphics, Inc.
|
||||||
|
# ALL RIGHTS RESERVED
|
||||||
|
# Permission to use, copy, modify, and distribute this software for
|
||||||
|
# any purpose and without fee is hereby granted, provided that the above
|
||||||
|
# copyright notice appear in all copies and that both the copyright notice
|
||||||
|
# and this permission notice appear in supporting documentation, and that
|
||||||
|
# the name of Silicon Graphics, Inc. not be used in advertising
|
||||||
|
# or publicity pertaining to distribution of the software without specific,
|
||||||
|
# written prior permission.
|
||||||
|
#
|
||||||
|
# THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS"
|
||||||
|
# AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,
|
||||||
|
# INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
|
||||||
|
# GRAPHICS, INC. BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT,
|
||||||
|
# SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY
|
||||||
|
# KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION,
|
||||||
|
# LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF
|
||||||
|
# THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC. HAS BEEN
|
||||||
|
# ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON
|
||||||
|
# ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
|
||||||
|
# POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
#
|
||||||
|
# US Government Users Restricted Rights
|
||||||
|
# Use, duplication, or disclosure by the Government is subject to
|
||||||
|
# restrictions set forth in FAR 52.227.19(c)(2) or subparagraph
|
||||||
|
# (c)(1)(ii) of the Rights in Technical Data and Computer Software
|
||||||
|
# clause at DFARS 252.227-7013 and/or in similar or successor
|
||||||
|
# clauses in the FAR or the DOD or NASA FAR Supplement.
|
||||||
|
# Unpublished-- rights reserved under the copyright laws of the
|
||||||
|
# United States. Contractor/manufacturer is Silicon Graphics,
|
||||||
|
# Inc., 2011 N. Shoreline Blvd., Mountain View, CA 94039-7311.
|
||||||
|
|
||||||
|
import math
|
||||||
|
import copy
|
||||||
|
from pyglet.gl import *
|
||||||
|
|
||||||
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
# a little vector library that is misused in odd ways below.
|
||||||
|
def v3add(src1, src2):
|
||||||
|
return [ src1[0] + src2[0],
|
||||||
|
src1[1] + src2[1],
|
||||||
|
src1[2] + src2[2] ]
|
||||||
|
|
||||||
|
def v3sub(src1, src2):
|
||||||
|
return [ src1[0] - src2[0],
|
||||||
|
src1[1] - src2[1],
|
||||||
|
src1[2] - src2[2] ]
|
||||||
|
|
||||||
|
def v3scale(v, scale):
|
||||||
|
return [ v[0] * scale,
|
||||||
|
v[1] * scale,
|
||||||
|
v[2] * scale ]
|
||||||
|
|
||||||
|
def v3dot(v1, v2):
|
||||||
|
return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]
|
||||||
|
|
||||||
|
def v3cross(v1, v2):
|
||||||
|
return [ (v1[1] * v2[2]) - (v1[2] * v2[1]),
|
||||||
|
(v1[2] * v2[0]) - (v1[0] * v2[2]),
|
||||||
|
(v1[0] * v2[1]) - (v1[1] * v2[0]) ]
|
||||||
|
|
||||||
|
def v3length(v):
|
||||||
|
return math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2])
|
||||||
|
|
||||||
|
def v3normalize(v):
|
||||||
|
try:
|
||||||
|
tmp = v3scale(v,1.0/v3length(v))
|
||||||
|
return tmp
|
||||||
|
except ZeroDivisionError:
|
||||||
|
return v
|
||||||
|
|
||||||
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
# Some quaternion routines
|
||||||
|
def q_add(q1, q2):
|
||||||
|
"""Given two quaternions, add them together to get a third quaternion.
|
||||||
|
Adding quaternions to get a compound rotation is analagous to adding
|
||||||
|
translations to get a compound translation. When incrementally
|
||||||
|
adding rotations, the first argument here should be the new rotation.
|
||||||
|
"""
|
||||||
|
t1 = v3scale(q1,q2[3])
|
||||||
|
t2 = v3scale(q2,q1[3])
|
||||||
|
t3 = v3cross(q2,q1)
|
||||||
|
tf = v3add(t1,t2)
|
||||||
|
tf = v3add(t3,tf)
|
||||||
|
tf.append( q1[3] * q2[3] - v3dot(q1,q2) )
|
||||||
|
return tf
|
||||||
|
|
||||||
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
def q_from_axis_angle(a, phi):
|
||||||
|
# a is a 3-vector, q is a 4-vector
|
||||||
|
"""Computes a quaternion based on an axis (defined by the given vector)
|
||||||
|
and an angle about which to rotate. The angle is expressed in radians.
|
||||||
|
"""
|
||||||
|
q = v3normalize(a)
|
||||||
|
q = v3scale(q, math.sin(phi/2.0))
|
||||||
|
q.append(math.cos(phi/2.0))
|
||||||
|
return q
|
||||||
|
|
||||||
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
def q_normalize(q):
|
||||||
|
"""Return a normalized quaternion"""
|
||||||
|
mag = (q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3])
|
||||||
|
if mag != 0:
|
||||||
|
for i in range(4):
|
||||||
|
q[i] /= mag;
|
||||||
|
return q
|
||||||
|
|
||||||
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
def q_matrix(q):
|
||||||
|
"""return the rotation matrix based on q"""
|
||||||
|
m = [0.0]*16
|
||||||
|
m[0*4+0] = 1.0 - 2.0 * (q[1] * q[1] + q[2] * q[2])
|
||||||
|
m[0*4+1] = 2.0 * (q[0] * q[1] - q[2] * q[3])
|
||||||
|
m[0*4+2] = 2.0 * (q[2] * q[0] + q[1] * q[3])
|
||||||
|
m[0*4+3] = 0.0
|
||||||
|
|
||||||
|
m[1*4+0] = 2.0 * (q[0] * q[1] + q[2] * q[3])
|
||||||
|
m[1*4+1] = 1.0 - 2.0 * (q[2] * q[2] + q[0] * q[0])
|
||||||
|
m[1*4+2] = 2.0 * (q[1] * q[2] - q[0] * q[3])
|
||||||
|
m[1*4+3] = 0.0
|
||||||
|
|
||||||
|
m[2*4+0] = 2.0 * (q[2] * q[0] - q[1] * q[3])
|
||||||
|
m[2*4+1] = 2.0 * (q[1] * q[2] + q[0] * q[3])
|
||||||
|
m[2*4+2] = 1.0 - 2.0 * (q[1] * q[1] + q[0] * q[0])
|
||||||
|
m[2*4+3] = 0.0
|
||||||
|
|
||||||
|
m[3*4+0] = 0.0
|
||||||
|
m[3*4+1] = 0.0
|
||||||
|
m[3*4+2] = 0.0
|
||||||
|
m[3*4+3] = 1.0
|
||||||
|
return m
|
||||||
|
|
||||||
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
def project_z(r, x, y):
|
||||||
|
"""Project an x,y pair onto a sphere of radius r OR a hyperbolic sheet
|
||||||
|
if we are away from the center of the sphere.
|
||||||
|
"""
|
||||||
|
d = math.sqrt(x*x + y*y)
|
||||||
|
if (d < r * 0.70710678118654752440): # Inside sphere
|
||||||
|
z = math.sqrt(r*r - d*d)
|
||||||
|
else: # On hyperbola
|
||||||
|
t = r / 1.41421356237309504880
|
||||||
|
z = t*t / d
|
||||||
|
return z
|
||||||
|
|
||||||
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
#
|
||||||
|
# Trackball Camera Class
|
||||||
|
#
|
||||||
|
class TrackballCamera:
|
||||||
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
def __init__(self, radius=1.0):
|
||||||
|
""" initialize the camera, giving a radius from the focal point for
|
||||||
|
the camera eye. Update focal point & up via the update_modelview call.
|
||||||
|
"""
|
||||||
|
# the quaternion storing the rotation
|
||||||
|
self.rot_quat = [0,0,0,1]
|
||||||
|
# the last mouse update
|
||||||
|
self.last_x = None
|
||||||
|
self.last_y = None
|
||||||
|
# camera vars
|
||||||
|
self.cam_eye = [0.,0.,radius]
|
||||||
|
self.cam_focus = [0.,0.,0.]
|
||||||
|
self.cam_up = [0.,1.,0.]
|
||||||
|
# in add_quat routine, renormalize "sometimes"
|
||||||
|
self.RENORMCOUNT = 97
|
||||||
|
self.count = 0
|
||||||
|
# Trackballsize should really be based on the distance from the center of
|
||||||
|
# rotation to the point on the object underneath the mouse. That
|
||||||
|
# point would then track the mouse as closely as possible. This is a
|
||||||
|
# simple example, though, so that is left as an Exercise for the
|
||||||
|
# Programmer.
|
||||||
|
self.TRACKBALLSIZE = 0.8
|
||||||
|
|
||||||
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
def mouse_roll(self, norm_mouse_x, norm_mouse_y, dragging=True):
|
||||||
|
"""When you click or drag the primary mouse button, scale the mouse
|
||||||
|
x & y to the range [-1.0,1.0] and call this routine to roll the trackball
|
||||||
|
and update the modelview matrix.
|
||||||
|
|
||||||
|
The initial click should set dragging to False.
|
||||||
|
"""
|
||||||
|
if dragging:
|
||||||
|
norm_mouse_quat = self._rotate(norm_mouse_x, norm_mouse_y)
|
||||||
|
self.rot_quat = q_add(norm_mouse_quat,self.rot_quat)
|
||||||
|
self.count += 1
|
||||||
|
if (self.count > self.RENORMCOUNT):
|
||||||
|
self.rot_quat = q_normalize(self.rot_quat)
|
||||||
|
self.count = 0
|
||||||
|
self.update_modelview()
|
||||||
|
self.last_x = norm_mouse_x
|
||||||
|
self.last_y = norm_mouse_y
|
||||||
|
|
||||||
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
def mouse_zoom(self, norm_mouse_x, norm_mouse_y, dragging=True):
|
||||||
|
"""When you click or drag a secondary mouse button, scale the mouse
|
||||||
|
x & y to the range [-1.0,1.0] and call this routine to change the
|
||||||
|
trackball's camera radius and update the modelview matrix.
|
||||||
|
|
||||||
|
The initial click should set dragging to False.
|
||||||
|
"""
|
||||||
|
if self.last_x:
|
||||||
|
dx = norm_mouse_x - self.last_x
|
||||||
|
dy = norm_mouse_y - self.last_y
|
||||||
|
norm_mouse_r_delta = 20.0*math.sqrt(dx*dx+dy*dy)
|
||||||
|
if dy > 0.0:
|
||||||
|
norm_mouse_r_delta = -norm_mouse_r_delta
|
||||||
|
if dragging:
|
||||||
|
self.cam_eye[2] = self.cam_eye[2] + norm_mouse_r_delta
|
||||||
|
if self.cam_eye[2] < 1.0:
|
||||||
|
self.cam_eye[2] == 1.0
|
||||||
|
self.update_modelview()
|
||||||
|
self.last_x = norm_mouse_x
|
||||||
|
self.last_y = norm_mouse_y
|
||||||
|
|
||||||
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
def update_modelview(self,cam_radius=None,cam_focus=None,cam_up=None):
|
||||||
|
"""Given a radius for the trackball camera, a focus-point 3-vector,
|
||||||
|
another 3-vector the points 'up' combined with the current
|
||||||
|
orientation of the trackball, update the GL_MODELVIEW matrix.
|
||||||
|
"""
|
||||||
|
if cam_radius:
|
||||||
|
self.cam_eye[2] = cam_radius
|
||||||
|
if cam_focus:
|
||||||
|
self.cam_focus = cam_focus
|
||||||
|
if cam_up:
|
||||||
|
self.cam_up = cam_up
|
||||||
|
glMatrixMode(GL_MODELVIEW)
|
||||||
|
glLoadIdentity()
|
||||||
|
gluLookAt(
|
||||||
|
self.cam_eye[0],self.cam_eye[1],self.cam_eye[2],
|
||||||
|
self.cam_focus[0],self.cam_focus[1],self.cam_focus[2],
|
||||||
|
self.cam_up[0],self.cam_up[1],self.cam_up[2]
|
||||||
|
)
|
||||||
|
# rotate this view by the current orientation
|
||||||
|
m = self._matrix()
|
||||||
|
mm = (GLfloat * len(m))(*m) # FIXME there is prob a better way...
|
||||||
|
glMultMatrixf(mm)
|
||||||
|
|
||||||
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
def _matrix(self):
|
||||||
|
"""return the rotation matrix for the trackball"""
|
||||||
|
return q_matrix(self.rot_quat)
|
||||||
|
|
||||||
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
def _rotate(self, norm_mouse_x, norm_mouse_y):
|
||||||
|
"""Pass the x and y coordinates of the last and current positions of
|
||||||
|
the mouse, scaled so they are in the range [-1.0,1.0].
|
||||||
|
|
||||||
|
Simulate a track-ball. Project the points onto the virtual
|
||||||
|
trackball, then figure out the axis of rotation, which is the cross
|
||||||
|
product of LAST NEW and O LAST (O is the center of the ball, 0,0,0)
|
||||||
|
Note: This is a deformed trackball-- is a trackball in the center,
|
||||||
|
but is deformed into a hyperbolic sheet of rotation away from the
|
||||||
|
center. This particular function was chosen after trying out
|
||||||
|
several variations.
|
||||||
|
"""
|
||||||
|
# handle special case
|
||||||
|
if (self.last_x == norm_mouse_x and self.last_y == norm_mouse_y):
|
||||||
|
# Zero rotation
|
||||||
|
return [ 0.0, 0.0, 0.0, 1.0]
|
||||||
|
|
||||||
|
#
|
||||||
|
# First, figure out z-coordinates for projection of P1 and P2 to
|
||||||
|
# deformed sphere
|
||||||
|
#
|
||||||
|
last = [self.last_x, self.last_y, project_z(self.TRACKBALLSIZE,self.last_x,self.last_y)]
|
||||||
|
new = [norm_mouse_x, norm_mouse_y, project_z(self.TRACKBALLSIZE,norm_mouse_x,norm_mouse_y)]
|
||||||
|
|
||||||
|
#
|
||||||
|
# Now, we want the cross product of LAST and NEW
|
||||||
|
# aka the axis of rotation
|
||||||
|
#
|
||||||
|
a = v3cross(new,last)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Figure out how much to rotate around that axis (phi)
|
||||||
|
#
|
||||||
|
d = v3sub(last,new)
|
||||||
|
t = v3length(d) / (2.0*self.TRACKBALLSIZE)
|
||||||
|
# Avoid problems with out-of-control values...
|
||||||
|
if (t > 1.0): t = 1.0
|
||||||
|
if (t < -1.0): t = -1.0
|
||||||
|
phi = 2.0 * math.asin(t)
|
||||||
|
|
||||||
|
return q_from_axis_angle(a,phi)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user