Compare commits
72 commits
master
...
launcher0.
Author | SHA1 | Date | |
---|---|---|---|
|
77647d688f | ||
|
5659439a8f | ||
|
3fdef85957 | ||
|
35d77c2bc0 | ||
|
e9403fe746 | ||
|
878db3658a | ||
|
5435b594d4 | ||
|
2d9edde95c | ||
|
8ae6186325 | ||
|
16a07c01b0 | ||
|
571ac1c443 | ||
|
7774330b8b | ||
|
0b0c6f8fba | ||
|
3eba00caa6 | ||
|
0f6f0e4bc4 | ||
|
d6c93bb739 | ||
|
5b67e045b1 | ||
|
6f450f37e7 | ||
|
b78582b27d | ||
|
1667b1c672 | ||
|
7943066fbb | ||
|
3fb601a2e7 | ||
|
06e6a803a2 | ||
|
ad942261f9 | ||
|
d5b69b8fc1 | ||
|
4a624e4a4f | ||
|
818f4bd0bb | ||
|
8b3a6d7130 | ||
|
6173cbe094 | ||
|
2a7e089edb | ||
|
cab7974f2a | ||
|
58bc2b9c67 | ||
|
2ac84c0dbc | ||
|
5b28d5c5a1 | ||
|
c7cee9a471 | ||
|
b2ec3ef2b3 | ||
|
e210fa2fa8 | ||
|
38dc57c22f | ||
|
8cc74e8f22 | ||
|
7befc8c14b | ||
|
67c6becca3 | ||
|
4f4ec651c1 | ||
|
4707aee221 | ||
|
83cd160ed4 | ||
|
fe0363627a | ||
|
9e8f2f38c0 | ||
|
c043a6fe88 | ||
|
5d5f7cceb4 | ||
|
0375b5570f | ||
|
e46b09ba06 | ||
|
e45f8f6f85 | ||
|
de08850f24 | ||
|
a7ffda822e | ||
|
118bcd02a7 | ||
|
c21c0b9535 | ||
|
d831e996e7 | ||
|
70e12f180d | ||
|
a9aabc7465 | ||
|
376628d9a6 | ||
|
54a134c73e | ||
|
46b46df60f | ||
|
1bb8893635 | ||
|
45d7142feb | ||
|
c224ba4074 | ||
|
a034975928 | ||
|
8236efcffc | ||
|
f21499062d | ||
|
2cc82a2724 | ||
|
d26c7dd536 | ||
|
b2e3a1d486 | ||
|
3c6eed8f39 | ||
|
355f156295 |
18
.gitignore
vendored
|
@ -34,3 +34,21 @@ nosetests.xml
|
|||
.mr.developer.cfg
|
||||
.project
|
||||
.pydevproject
|
||||
|
||||
.idea
|
||||
punyverse/*.obj
|
||||
punyverse/*.c
|
||||
*.html
|
||||
*.exp
|
||||
*.lib
|
||||
punyverse/assets/textures/*_medium.*
|
||||
punyverse/assets/textures/*_small.*
|
||||
punyverse/assets/textures/*/*_medium.*
|
||||
punyverse/assets/textures/*/*_small.*
|
||||
temp
|
||||
|
||||
# There is a py2exe package that can execute punyverse directly
|
||||
library.zip
|
||||
*.exe
|
||||
*.log
|
||||
*.dll
|
||||
|
|
33
README.md
|
@ -2,3 +2,36 @@ punyverse
|
|||
=========
|
||||
|
||||
Python simulator of a puny universe. (How many words can i stick into one?)
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
To install, simply clone this repository, or download a copy [here]
|
||||
(https://github.com/xiaomao5/punyverse/archive/master.zip).
|
||||
|
||||
After that, download the [launcher](https://github.com/xiaomao5/punyverse/releases/download/launcher0.3/launcher.exe),
|
||||
put it into the repository directory and let it unpack in your repository (or copy).
|
||||
|
||||
You may start playing any time by running `punyverse.exe`, or `punyverse_debug.exe` if you desire a console.
|
||||
|
||||
### A Note on Textures
|
||||
|
||||
If your graphics card doesn't support the massive texture sizes this module comes with, you can shrink them.
|
||||
|
||||
You can run `small_images.exe` (or `small_images.py`, if you have python) to generate smaller versions of
|
||||
shipped textures, which requires either `PIL` or `pgmagick` to process the images.
|
||||
|
||||
### Advanced Install
|
||||
|
||||
If you wish to use your own python installation, to run `punyverse`, you can clone the code.
|
||||
Here are the things you need:
|
||||
|
||||
* Python 2.7, I have no Python 2.6 install to test this.
|
||||
* a C compiler to compile `_model.c` and `_glgeom.c`
|
||||
* requires OpenGL headers and libraries.
|
||||
* not really necessary, but it runs way faster with these.
|
||||
* install `pyglet`
|
||||
|
||||
After getting the dependencies done, you can now run the `punyverse` module using `python -mpunyverse`.
|
||||
|
||||
See above if you run into texture issues.
|
||||
|
|
36
bootloader.py
Normal file
|
@ -0,0 +1,36 @@
|
|||
import pyglet
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import uuid
|
||||
import imp
|
||||
import argparse
|
||||
|
||||
def load_dll(dir, module):
|
||||
name = 'punyverse.' + module
|
||||
path = os.path.join(dir, 'punyverse', module + '.pyd')
|
||||
if not os.path.exists(path):
|
||||
path = os.path.join(dir, 'punyverse.%s.pyd' % module)
|
||||
if not os.path.exists(path):
|
||||
raise ImportError('No module named %s' % module)
|
||||
return imp.load_dynamic(name, path)
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
dir = os.path.dirname(sys.executable)
|
||||
if sys.frozen == 'windows_exe':
|
||||
sys.stderr = open(os.path.join(dir, 'punyverse.log'), 'a')
|
||||
except AttributeError:
|
||||
sys.exit('This is only meant to be ran frozen.')
|
||||
|
||||
sys.path.insert(0, dir)
|
||||
|
||||
# Model indirectly depends on _glgeom to handle textures
|
||||
load_dll(dir, '_glgeom')
|
||||
|
||||
# Model path needs special handling
|
||||
_model = load_dll(dir, '_model')
|
||||
_model.model_base = os.path.join(dir, 'punyverse', 'assets', 'models')
|
||||
|
||||
with open('punyverse\__main__.py', 'r') as code:
|
||||
exec(code)
|
29
launcher.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
from distutils.core import setup
|
||||
import py2exe
|
||||
from glob import glob
|
||||
import sys
|
||||
import os
|
||||
import shutil
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
sys.argv.append('py2exe')
|
||||
|
||||
parent = os.path.dirname(__file__)
|
||||
join = os.path.join
|
||||
|
||||
setup(
|
||||
console=[{'dest_base': 'punyverse_debug', 'script': 'bootloader.py'}, 'small_images.py'],
|
||||
windows=[{'dest_base': 'punyverse', 'script': 'bootloader.py'}],
|
||||
options={'py2exe': {
|
||||
'unbuffered': True, 'optimize': 2,
|
||||
'excludes': [
|
||||
'_ssl', 'unittest', 'doctest', 'PIL', 'email', 'distutils',
|
||||
'pyglet.window.carbon', 'pyglet.window.xlib',
|
||||
'pyglet.media.drivers.alsa',
|
||||
'win32wnet', 'netbios', 'pgmagick'
|
||||
],
|
||||
'includes': ['punyverse._model', 'punyverse._glgeom'],
|
||||
'dll_excludes': ['MPR.dll', 'w9xpopen.exe'],
|
||||
}
|
||||
}
|
||||
)
|
1
punyverse/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
|
29
punyverse/__main__.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/python
|
||||
INITIAL_WIN_HEIGHT = 540
|
||||
INITIAL_WIN_WIDTH = 700
|
||||
WIN_TITLE = 'Punyverse'
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
import argparse
|
||||
except ImportError:
|
||||
args = False
|
||||
else:
|
||||
parser = argparse.ArgumentParser(prog='punyverse', description='Python simulator of a puny universe.')
|
||||
parser.add_argument('-t', '--ticks', help='Ticks per second for game, more means more responsive, but '
|
||||
' may run slower, default is 20.', default=20, type=int)
|
||||
args = parser.parse_args()
|
||||
|
||||
import pyglet
|
||||
from punyverse import game
|
||||
pyglet.options['shadow_window'] = False
|
||||
if args:
|
||||
game.TICKS_PER_SECOND = args.ticks
|
||||
|
||||
game.Applet(width=INITIAL_WIN_WIDTH, height=INITIAL_WIN_HEIGHT, caption=WIN_TITLE, resizable=True, vsync=0)
|
||||
pyglet.app.run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
4
punyverse/__main__.pyw
Normal file
|
@ -0,0 +1,4 @@
|
|||
from punyverse.__main__ import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
1209
punyverse/_cyopengl.pxi
Normal file
161
punyverse/_glgeom.pyx
Normal file
|
@ -0,0 +1,161 @@
|
|||
from libc.math cimport sin, cos, sqrt
|
||||
from libc.stdlib cimport malloc, free
|
||||
from libc.string cimport memcpy
|
||||
cimport cython
|
||||
|
||||
include "_cyopengl.pxi"
|
||||
cdef float PI = 3.1415926535897932324626
|
||||
cdef float TWOPI = PI * 2
|
||||
|
||||
cdef extern from "Python.h":
|
||||
object PyBytes_FromStringAndSize(const char *s, Py_ssize_t len)
|
||||
const char* PyBytes_AsString(bytes o)
|
||||
|
||||
|
||||
@cython.cdivision(True)
|
||||
cpdef torus(float major_radius, float minor_radius, int n_major, int n_minor, tuple material, int shininess=125):
|
||||
"""
|
||||
Torus function from the OpenGL red book.
|
||||
"""
|
||||
glPushAttrib(GL_CURRENT_BIT)
|
||||
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, [material[0], material[1], material[2], material[3]])
|
||||
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, [1, 1, 1, 1])
|
||||
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininess)
|
||||
|
||||
assert n_major > 0 and n_minor > 0
|
||||
assert minor_radius > 0 and major_radius > 0
|
||||
|
||||
cdef float major_s, minor_s
|
||||
cdef float a0, a1, x0, y0, x1, y1, b, c, r, z, m, x, y, z2
|
||||
cdef int i, j
|
||||
|
||||
with nogil:
|
||||
major_s = TWOPI / n_major
|
||||
minor_s = TWOPI / n_minor
|
||||
|
||||
for i in xrange(n_major):
|
||||
a0 = i * major_s
|
||||
a1 = a0 + major_s
|
||||
x0 = cos(a0)
|
||||
y0 = sin(a0)
|
||||
x1 = cos(a1)
|
||||
y1 = sin(a1)
|
||||
|
||||
glBegin(GL_TRIANGLE_STRIP)
|
||||
|
||||
for j in xrange(n_minor + 1):
|
||||
b = j * minor_s
|
||||
c = cos(b)
|
||||
r = minor_radius * c + major_radius
|
||||
z = minor_radius * sin(b)
|
||||
|
||||
x = x0 * c
|
||||
y = y0 * c
|
||||
z2 = z / minor_radius
|
||||
m = 1.0 / sqrt(x * x + y * y + z2 * z2)
|
||||
glNormal3f(x * m, y * z, z2 * m)
|
||||
glVertex3f(x0 * r, y0 * r, z)
|
||||
|
||||
x = x1 * c
|
||||
y = y1 * c
|
||||
m = 1.0 / sqrt(x * x + y * y + z2 * z2)
|
||||
glNormal3f(x * m, y * z, z2 * m)
|
||||
glVertex3f(x1 * r, y1 * r, z)
|
||||
|
||||
glEnd()
|
||||
glPopAttrib()
|
||||
|
||||
|
||||
@cython.cdivision(True)
|
||||
cpdef normal_sphere(double r, int divide, GLuint tex, normal, bint lighting=True):
|
||||
from texture import pil_load
|
||||
print 'Loading normal map: %s...' % normal,
|
||||
normal_map = pil_load(normal)
|
||||
normal = normal_map.load()
|
||||
print 'Loaded'
|
||||
|
||||
cdef int width, height
|
||||
width, height = normal_map.size
|
||||
cdef bint gray_scale = len(normal[0, 0]) == 1
|
||||
|
||||
glEnable(GL_TEXTURE_2D)
|
||||
if lighting:
|
||||
glDisable(GL_BLEND)
|
||||
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, [1, 1, 1, 0])
|
||||
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, [1, 1, 1, 0])
|
||||
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 125)
|
||||
else:
|
||||
glDisable(GL_LIGHTING)
|
||||
glBindTexture(GL_TEXTURE_2D, tex)
|
||||
|
||||
cdef double twopi_divide, pi_divide
|
||||
cdef int i, j
|
||||
cdef double phi1, phi2
|
||||
cdef double theta, s, t
|
||||
cdef int u, v
|
||||
cdef double x, y, z
|
||||
cdef double dx, dy, xz
|
||||
cdef double nx, ny, nz
|
||||
twopi_divide = TWOPI / divide
|
||||
pi_divide = PI / divide
|
||||
glBegin(GL_TRIANGLE_STRIP)
|
||||
for j in xrange(divide + 1):
|
||||
phi1 = j * twopi_divide
|
||||
phi2 = (j + 1) * twopi_divide
|
||||
|
||||
for i in xrange(divide + 1):
|
||||
theta = i * pi_divide
|
||||
|
||||
s = phi2 / TWOPI
|
||||
u = min(<int>(s * width), width - 1)
|
||||
t = theta / PI
|
||||
v = min(<int>(t * height), height - 1)
|
||||
if gray_scale:
|
||||
x = y = z = normal[u, v]
|
||||
else:
|
||||
x, y, z = normal[u, v]
|
||||
dx, dy, dz = sin(theta) * cos(phi2), sin(theta) * sin(phi2), cos(theta)
|
||||
nx, ny, nz = x / 127.5 - 1, y / 127.5 - 1, z / 127.5 - 1 # Make into [-1, 1]
|
||||
nx, nz = cos(theta) * nx + sin(theta) * nz, -sin(theta) * nx + cos(theta) * nz
|
||||
nx, ny = cos(phi2) * nx - sin(phi2) * ny, sin(phi2) * nx + cos(phi2) * ny
|
||||
glNormal3f(nx, ny, nz)
|
||||
glTexCoord2f(s, 1 - t) # GL is bottom up
|
||||
glVertex3f(r * dx, r * dy, r * dz)
|
||||
|
||||
s = phi1 / TWOPI # x
|
||||
u = min(<int>(s * width), width - 1)
|
||||
if gray_scale:
|
||||
x = y = z = normal[u, v]
|
||||
else:
|
||||
x, y, z = normal[u, v]
|
||||
dx, dy = sin(theta) * cos(phi1), sin(theta) * sin(phi1)
|
||||
nx, ny, nz = x / 127.5 - 1, y / 127.5 - 1, z / 127.5 - 1
|
||||
nx, nz = cos(theta) * nx + sin(theta) * nz, -sin(theta) * nx + cos(theta) * nz
|
||||
nx, ny = cos(phi1) * nx - sin(phi1) * ny, sin(phi1) * nx + cos(phi1) * ny
|
||||
glNormal3f(nx, ny, nz)
|
||||
glTexCoord2f(s, 1 - t)
|
||||
glVertex3f(r * dx, r * dy, r * dz)
|
||||
glEnd()
|
||||
|
||||
glDisable(GL_TEXTURE_2D)
|
||||
glEnable(GL_LIGHTING)
|
||||
glEnable(GL_BLEND)
|
||||
|
||||
|
||||
cpdef bytes bgr_to_rgb(bytes buffer, int width, int height, bint alpha=0, bint bottom_up=1):
|
||||
cdef int length = len(buffer)
|
||||
cdef int depth = length / (width * height)
|
||||
cdef int depth2 = depth - alpha
|
||||
cdef object final = PyBytes_FromStringAndSize(NULL, length)
|
||||
cdef char *result = PyBytes_AsString(final)
|
||||
cdef const char *source = PyBytes_AsString(buffer)
|
||||
cdef int x, y, ioffset, ooffset, i, row = width * depth
|
||||
for y in xrange(height):
|
||||
for x in xrange(width):
|
||||
ioffset = y * width * depth + x * depth
|
||||
ooffset = (height - y - 1 if bottom_up else y) * row + x * depth
|
||||
for i in xrange(depth2):
|
||||
result[ooffset+i] = source[ioffset+depth2-i-1]
|
||||
if alpha:
|
||||
result[ooffset+depth2] = source[ioffset+depth2]
|
||||
return final
|
362
punyverse/_model.pyx
Normal file
|
@ -0,0 +1,362 @@
|
|||
from libc.string cimport strcmp, strlen
|
||||
from libc.stdlib cimport malloc, free, atof
|
||||
from libc.stdio cimport fopen, fclose, fgets, FILE
|
||||
cimport cython
|
||||
|
||||
from punyverse.texture import load_texture
|
||||
include "_cyopengl.pxi"
|
||||
from uuid import uuid4
|
||||
import os
|
||||
import gzip
|
||||
import bz2
|
||||
import zipfile
|
||||
|
||||
def zip_open(file):
|
||||
zip = zipfile.ZipFile(file)
|
||||
return zip.open(zip.namelist()[0])
|
||||
|
||||
openers = {
|
||||
'gz': gzip.open,
|
||||
'bz2': bz2.BZ2File,
|
||||
'zip': zip_open,
|
||||
}
|
||||
|
||||
|
||||
cdef enum:
|
||||
FACE_TRIANGLES
|
||||
FACE_QUADS
|
||||
|
||||
cdef class Face(object):
|
||||
cdef public int type
|
||||
cdef public list verts, norms, texs, vertices, normals, textures
|
||||
|
||||
def __init__(self, int type, list verts, list norms, list texs,
|
||||
list vertices, list normals, list textures):
|
||||
self.type = type
|
||||
self.verts = verts
|
||||
self.norms = norms
|
||||
self.texs = texs
|
||||
self.vertices = vertices
|
||||
self.normals = normals
|
||||
self.textures = textures
|
||||
|
||||
cdef class Material(object):
|
||||
cdef public str name, texture
|
||||
cdef public tuple Ka, Kd, Ks
|
||||
cdef public double shininess
|
||||
|
||||
def __init__(self, str name, str texture=None, tuple Ka=(0, 0, 0),
|
||||
tuple Kd=(0, 0, 0), tuple Ks=(0, 0, 0), double shininess=0.0):
|
||||
self.name = name
|
||||
self.texture = texture
|
||||
self.Ka = Ka
|
||||
self.Kd = Kd
|
||||
self.Ks = Ks
|
||||
self.shininess = shininess
|
||||
|
||||
cdef class Group(object):
|
||||
cdef public str name
|
||||
cdef public tuple min
|
||||
cdef public Material material
|
||||
cdef public list faces, indices, vertices, normals, textures
|
||||
cdef public int idx_count
|
||||
|
||||
def __init__(self, str name=None):
|
||||
if name is None:
|
||||
self.name = str(uuid4())
|
||||
else:
|
||||
self.name = name
|
||||
self.min = ()
|
||||
self.material = None
|
||||
self.faces = []
|
||||
self.indices = []
|
||||
self.vertices = []
|
||||
self.normals = []
|
||||
self.textures = []
|
||||
self.idx_count = 0
|
||||
|
||||
def pack(self):
|
||||
min_x, min_y, min_z = 0, 0, 0
|
||||
for face in self.faces:
|
||||
for x, y, z in face.vertices:
|
||||
min_x = max(min_x, abs(x))
|
||||
min_y = max(min_y, abs(y))
|
||||
min_z = max(min_x, abs(z))
|
||||
self.min = (min_x, min_y, min_z)
|
||||
|
||||
cdef class WavefrontObject(object):
|
||||
cdef unicode root
|
||||
cdef public list vertices, normals, textures, groups
|
||||
cdef public dict materials
|
||||
cdef unicode path
|
||||
cdef Material current_material
|
||||
cdef Group current_group
|
||||
def __init__(self, unicode path):
|
||||
self.path = path
|
||||
self.root = os.path.abspath(os.path.dirname(path))
|
||||
self.vertices = []
|
||||
self.normals = []
|
||||
self.textures = []
|
||||
self.groups = []
|
||||
self.materials = {}
|
||||
|
||||
self.perform_io(self.path)
|
||||
|
||||
cdef void new_material(self, list words):
|
||||
material = Material(words[1])
|
||||
self.materials[words[1]] = material
|
||||
self.current_material = material
|
||||
|
||||
cdef void Ka(self, list words):
|
||||
self.current_material.Ka = (float(words[1]), float(words[2]), float(words[3]))
|
||||
|
||||
cdef void Kd(self, list words):
|
||||
self.current_material.Kd = (float(words[1]), float(words[2]), float(words[3]))
|
||||
|
||||
cdef void Ks(self, list words):
|
||||
self.current_material.Ks = (float(words[1]), float(words[2]), float(words[3]))
|
||||
|
||||
cdef void material_shininess(self, list words):
|
||||
self.current_material.shininess = min(float(words[1]), 125)
|
||||
|
||||
cdef void material_texture(self, list words):
|
||||
self.current_material.texture = words[-1]
|
||||
|
||||
@cython.nonecheck(False)
|
||||
cdef void vertex(self, list words):
|
||||
self.vertices.append((float(words[1]), float(words[2]), float(words[3])))
|
||||
|
||||
@cython.nonecheck(False)
|
||||
cdef void normal(self, list words):
|
||||
self.normals.append((float(words[1]), float(words[2]), float(words[3])))
|
||||
|
||||
cdef void texture(self, list words):
|
||||
cdef int l = len(words)
|
||||
cdef object x = 0, y = 0, z = 0
|
||||
if l >= 2:
|
||||
x = float(words[1])
|
||||
if l >= 3:
|
||||
# OBJ origin is at upper left, OpenGL origin is at lower left
|
||||
y = 1 - float(words[2])
|
||||
if l >= 4:
|
||||
z = float(words[3])
|
||||
self.textures.append((x, y, z))
|
||||
|
||||
cdef void face(self, list words):
|
||||
cdef int l = len(words)
|
||||
cdef int type = -1
|
||||
cdef int vertex_count = l - 1
|
||||
cdef list face_vertices = [], face_normals = [], face_textures = []
|
||||
|
||||
if vertex_count == 3:
|
||||
type = FACE_TRIANGLES
|
||||
else:
|
||||
type = FACE_QUADS
|
||||
|
||||
cdef int current_value = -1, texture_len = len(self.textures)
|
||||
cdef list raw_faces, vindices = [], nindices = [], tindices = []
|
||||
|
||||
for i in xrange(1, vertex_count + 1):
|
||||
raw_faces = words[i].split('/')
|
||||
l = len(raw_faces)
|
||||
|
||||
current_value = int(raw_faces[0])
|
||||
vindices.append(current_value - 1)
|
||||
face_vertices.append(self.vertices[current_value - 1])
|
||||
|
||||
if l == 1:
|
||||
continue
|
||||
if l >= 2 and raw_faces[1]:
|
||||
current_value = int(raw_faces[1])
|
||||
if current_value <= texture_len:
|
||||
tindices.append(current_value - 1)
|
||||
face_textures.append(self.textures[current_value - 1])
|
||||
if l >= 3 and raw_faces[2]:
|
||||
current_value = int(raw_faces[2])
|
||||
nindices.append(current_value - 1)
|
||||
face_normals.append(self.normals[current_value - 1])
|
||||
|
||||
cdef Group group
|
||||
if self.current_group is None:
|
||||
self.current_group = group = Group()
|
||||
self.groups.append(group)
|
||||
else:
|
||||
group = self.current_group
|
||||
|
||||
group.vertices += face_vertices
|
||||
group.normals += face_normals
|
||||
group.textures += face_textures
|
||||
idx_count = group.idx_count
|
||||
group.indices += (idx_count + 1, idx_count + 2, idx_count + 3)
|
||||
group.idx_count += 3
|
||||
|
||||
group.faces.append(Face(type, vindices, nindices, tindices, face_vertices, face_normals, face_textures))
|
||||
|
||||
cdef bint material(self, list words) except False:
|
||||
return self.perform_io(os.path.join(self.root, words[1]))
|
||||
|
||||
cdef void use_material(self, list words):
|
||||
mat = words[1]
|
||||
try:
|
||||
self.current_group.material = self.materials[mat]
|
||||
except KeyError:
|
||||
print "Warning: material %s undefined, only %s defined." % (mat, self.materials)
|
||||
except AttributeError:
|
||||
print "Warning: no group"
|
||||
|
||||
cdef void group(self, list words):
|
||||
name = words[1]
|
||||
group = Group(name)
|
||||
|
||||
if self.groups:
|
||||
self.current_group.pack()
|
||||
self.groups.append(group)
|
||||
self.current_group = group
|
||||
|
||||
cdef inline bint perform_io(self, unicode file) except False:
|
||||
cdef const char *type
|
||||
cdef list words
|
||||
cdef int hash, length
|
||||
|
||||
ext = os.path.splitext(file)[1].lstrip('.')
|
||||
reader = openers.get(ext, open)(file)
|
||||
with reader:
|
||||
for buf in reader:
|
||||
if not buf or buf.startswith(('\r', '\n', '#')):
|
||||
continue # Empty or comment
|
||||
words = buf.split()
|
||||
type = words[0]
|
||||
|
||||
length = strlen(type)
|
||||
if not length:
|
||||
continue
|
||||
elif length < 3:
|
||||
hash = type[0] << 8 | type[1]
|
||||
if hash == 0x7600: # v\0
|
||||
self.vertex(words)
|
||||
elif hash == 0x766e: # vn
|
||||
self.normal(words)
|
||||
elif hash == 0x7674: # vt
|
||||
self.texture(words)
|
||||
elif hash == 0x6600: # f
|
||||
self.face(words)
|
||||
elif hash == 0x6700: # g
|
||||
self.group(words)
|
||||
elif hash == 0x6f00: # o
|
||||
self.group(words)
|
||||
elif hash == 0x4b61: # Ka
|
||||
self.Ka(words)
|
||||
elif hash == 0x4b64: # Kd
|
||||
self.Kd(words)
|
||||
elif hash == 0x4b73: # Ks
|
||||
self.Ks(words)
|
||||
elif hash == 0x4e73: # Ns
|
||||
self.material_shininess(words)
|
||||
elif strcmp(type, b'mtllib') == 0:
|
||||
self.material(words)
|
||||
elif strcmp(type, b'usemtl') == 0:
|
||||
self.use_material(words)
|
||||
elif strcmp(type, b'newmtl') == 0:
|
||||
self.new_material(words)
|
||||
elif strcmp(type, b'map_Kd') == 0:
|
||||
self.material_texture(words)
|
||||
return True
|
||||
|
||||
model_base = None
|
||||
|
||||
def load_model(path):
|
||||
global model_base
|
||||
if model_base is None:
|
||||
import sys
|
||||
if hasattr(sys, 'frozen'):
|
||||
model_base = os.path.dirname(sys.executable)
|
||||
else:
|
||||
model_base = os.path.join(os.path.dirname(__file__), 'assets', 'models')
|
||||
if not os.path.isabs(path):
|
||||
path = os.path.join(model_base, path)
|
||||
if not isinstance(path, unicode):
|
||||
path = path.decode('mbcs')
|
||||
return WavefrontObject(path)
|
||||
|
||||
@cython.nonecheck(False)
|
||||
cdef inline void point(Face f, WavefrontObject m, int tex_id, float sx, float sy, float sz, int n):
|
||||
cdef float x, y, z
|
||||
cdef tuple normal, texture
|
||||
if f.norms:
|
||||
normal = m.normals[f.norms[n]]
|
||||
glNormal3f(normal[0], normal[1], normal[2])
|
||||
if tex_id:
|
||||
texture = m.textures[f.texs[n]]
|
||||
glTexCoord2f(texture[0], texture[1])
|
||||
|
||||
x, y, z = m.vertices[f.verts[n]]
|
||||
glVertex3f(x * sx, y * sy, z * sz)
|
||||
|
||||
cpdef int model_list(WavefrontObject model, float sx=1, float sy=1, float sz=1, object rotation=(0, 0, 0)):
|
||||
for m, text in model.materials.iteritems():
|
||||
if text.texture:
|
||||
load_texture(os.path.join(model.root, text.texture))
|
||||
|
||||
cdef int display = glGenLists(1)
|
||||
|
||||
glNewList(display, GL_COMPILE)
|
||||
glPushMatrix()
|
||||
glPushAttrib(GL_CURRENT_BIT)
|
||||
|
||||
cdef float pitch, yaw, roll
|
||||
cdef float kx, ky, kz
|
||||
|
||||
pitch, yaw, roll = rotation
|
||||
glPushAttrib(GL_TRANSFORM_BIT)
|
||||
glRotatef(pitch, 1, 0, 0)
|
||||
glRotatef(yaw, 0, 1, 0)
|
||||
glRotatef(roll, 0, 0, 1)
|
||||
glPopAttrib()
|
||||
|
||||
cdef Face f
|
||||
cdef Group g
|
||||
cdef int tex_id
|
||||
|
||||
for g in model.groups:
|
||||
tex_id = load_texture(os.path.join(model.root, g.material.texture)) if (g.material and g.material.texture) else 0
|
||||
|
||||
if tex_id:
|
||||
glEnable(GL_TEXTURE_2D)
|
||||
glBindTexture(GL_TEXTURE_2D, tex_id)
|
||||
else:
|
||||
glBindTexture(GL_TEXTURE_2D, 0)
|
||||
glDisable(GL_TEXTURE_2D)
|
||||
|
||||
if g.material is not None:
|
||||
if g.material.Ka:
|
||||
kx, ky, kz = g.material.Ka
|
||||
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, [kx, ky, kz, 1])
|
||||
if g.material.Kd:
|
||||
kx, ky, kz = g.material.Kd
|
||||
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, [kx, ky, kz, 1])
|
||||
if g.material.Ks:
|
||||
kx, ky, kz = g.material.Ks
|
||||
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, [kx, ky, kz, 1])
|
||||
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, g.material.shininess)
|
||||
|
||||
glBegin(GL_TRIANGLES)
|
||||
for f in g.faces:
|
||||
point(f, model, tex_id, sx, sy, sz, 0)
|
||||
point(f, model, tex_id, sx, sy, sz, 1)
|
||||
point(f, model, tex_id, sx, sy, sz, 2)
|
||||
|
||||
if f.type == FACE_QUADS:
|
||||
point(f, model, tex_id, sx, sy, sz, 2)
|
||||
point(f, model, tex_id, sx, sy, sz, 3)
|
||||
point(f, model, tex_id, sx, sy, sz, 0)
|
||||
glEnd()
|
||||
|
||||
if tex_id:
|
||||
glBindTexture(GL_TEXTURE_2D, 0)
|
||||
glDisable(GL_TEXTURE_2D)
|
||||
|
||||
glPopAttrib()
|
||||
glPopMatrix()
|
||||
|
||||
glEndList()
|
||||
return display
|
4619
punyverse/assets/models/asteroids/01.obj
Normal file
18443
punyverse/assets/models/asteroids/02.obj
Normal file
18443
punyverse/assets/models/asteroids/03.obj
Normal file
11
punyverse/assets/models/asteroids/asteroid.mtl
Normal file
|
@ -0,0 +1,11 @@
|
|||
# Blender MTL File: 'None'
|
||||
# Material Count: 1
|
||||
|
||||
newmtl Material
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.520996 0.473531 0.337164
|
||||
Ks 0.079916 0.069096 0.039636
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
11
punyverse/assets/models/asteroids/mainbelt.mtl
Normal file
|
@ -0,0 +1,11 @@
|
|||
# Blender MTL File: 'None'
|
||||
# Material Count: 1
|
||||
|
||||
newmtl Material
|
||||
Ns 92.156863
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.389517 0.389517 0.389517
|
||||
Ks 0.303197 0.303197 0.303197
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
188
punyverse/assets/models/asteroids/mainbelt.obj
Normal file
|
@ -0,0 +1,188 @@
|
|||
# Blender v2.68 (sub 0) OBJ File: 'mainbeltastroid.blend'
|
||||
# www.blender.org
|
||||
mtllib mainbelt.mtl
|
||||
g Cube
|
||||
v 0.290193 -0.983926 0.474791
|
||||
v 0.437387 -1.185402 -0.058906
|
||||
v -0.278179 0.795311 -0.169923
|
||||
v -0.176207 0.961698 -0.576060
|
||||
v -0.749408 0.677473 -0.403865
|
||||
v -0.843249 1.101376 0.368964
|
||||
v 0.921024 0.543359 -0.373421
|
||||
v 1.228522 0.313481 -0.796992
|
||||
v 1.413710 0.491171 0.016800
|
||||
v 0.824964 -0.186862 -0.430968
|
||||
v 0.169830 0.339481 0.955115
|
||||
v 0.788238 0.224175 0.864338
|
||||
v 0.579672 -0.319492 0.985489
|
||||
v -0.850858 0.163566 0.318706
|
||||
v 0.631733 -0.588108 -0.649624
|
||||
v -0.094402 -0.322324 -1.249913
|
||||
v -0.249124 -1.065447 -0.286061
|
||||
v -0.020992 -0.954752 -0.765611
|
||||
v -0.632247 -1.107953 -0.512581
|
||||
v 0.124837 1.315392 0.134372
|
||||
v 0.577110 0.882123 0.035559
|
||||
v 0.730517 0.855449 0.648931
|
||||
v -0.248585 0.742805 0.757662
|
||||
v 0.897809 -0.004470 0.247323
|
||||
v 0.947047 -0.715323 0.120538
|
||||
v 0.998978 -0.476379 0.574922
|
||||
v -0.353813 -0.204947 1.105379
|
||||
v -0.981584 -0.532307 0.798081
|
||||
v -0.690767 0.347252 0.944776
|
||||
v -1.260799 -0.267750 -0.576518
|
||||
v -0.903255 -0.554117 -0.072930
|
||||
v -0.873585 -0.350903 -0.897106
|
||||
v -1.083754 0.162032 -0.623495
|
||||
v -0.679472 0.071680 -0.834524
|
||||
v -0.150468 0.605678 -1.224164
|
||||
v -0.292701 -0.962443 0.791399
|
||||
v 0.496173 1.124109 -0.437672
|
||||
v 0.278706 -0.267373 1.389388
|
||||
vn 0.529098 -0.735317 0.423514
|
||||
vn 0.695321 -0.606928 -0.384925
|
||||
vn 0.383061 -0.662029 0.644190
|
||||
vn 0.511064 -0.783451 0.353579
|
||||
vn -0.138461 0.908731 -0.393746
|
||||
vn 0.593966 0.354629 -0.722110
|
||||
vn -0.359523 0.891682 0.275039
|
||||
vn 0.500172 0.865296 0.033023
|
||||
vn 0.619478 -0.019645 -0.784768
|
||||
vn 0.619649 0.775456 -0.121254
|
||||
vn 0.983189 0.132696 0.125425
|
||||
vn 0.171122 0.151303 0.973563
|
||||
vn 0.197398 0.332706 0.922139
|
||||
vn -0.945107 0.286186 0.157704
|
||||
vn -0.676128 0.735533 -0.042912
|
||||
vn 0.472294 -0.437683 -0.765096
|
||||
vn 0.304249 0.044783 -0.951539
|
||||
vn 0.283559 0.913995 -0.290184
|
||||
vn 0.830567 -0.515214 0.211455
|
||||
vn -0.311526 -0.600264 -0.736637
|
||||
vn 0.483338 -0.690051 -0.538714
|
||||
vn -0.198815 -0.976318 0.085294
|
||||
vn -0.529631 -0.847817 -0.026400
|
||||
vn -0.165780 0.630247 0.758489
|
||||
vn 0.699114 0.677982 0.227114
|
||||
vn 0.259894 0.628514 0.733093
|
||||
vn 0.677448 -0.097416 0.729091
|
||||
vn 0.987177 0.091683 -0.130671
|
||||
vn 0.891772 -0.211464 -0.400032
|
||||
vn -0.233827 -0.388331 0.891361
|
||||
vn -0.437396 -0.005360 0.899253
|
||||
vn 0.072347 -0.376917 0.923417
|
||||
vn -0.408216 0.016368 0.912739
|
||||
vn -0.926227 -0.356903 -0.121344
|
||||
vn -0.988224 0.122316 -0.091933
|
||||
vn -0.501469 0.749738 -0.431766
|
||||
vn -0.483647 -0.150069 -0.862302
|
||||
vn -0.612900 0.611035 -0.500988
|
||||
vn -0.774342 0.425106 -0.468699
|
||||
vn 0.338886 -0.661704 0.668808
|
||||
vn 0.178682 -0.903205 0.390248
|
||||
vn -0.801728 0.596145 0.042935
|
||||
vn -0.110776 0.869208 -0.481877
|
||||
vn -0.805509 0.589632 0.059074
|
||||
vn 0.018409 0.877560 -0.479114
|
||||
vn 0.759682 -0.149805 0.632805
|
||||
vn 0.713637 -0.691918 0.109416
|
||||
vn 0.786427 0.594041 0.169258
|
||||
vn 0.282085 0.931005 -0.231641
|
||||
vn 0.583533 -0.247678 0.773398
|
||||
vn 0.732741 0.478045 0.484317
|
||||
vn -0.004524 0.581410 0.813598
|
||||
vn -0.006905 0.433901 0.900934
|
||||
vn -0.991584 0.014936 -0.128596
|
||||
vn -0.563344 -0.039656 0.825270
|
||||
vn -0.858666 0.466868 0.211488
|
||||
vn 0.423569 -0.519252 -0.742271
|
||||
vn 0.187865 -0.972846 -0.135191
|
||||
vn -0.222629 -0.496977 -0.838719
|
||||
vn -0.081738 -0.961894 -0.260919
|
||||
vn 0.052827 -0.993867 0.097149
|
||||
vn -0.766365 -0.585261 0.264864
|
||||
vn 0.669742 0.730079 -0.135756
|
||||
vn 0.981787 0.097127 -0.163283
|
||||
vn 0.963043 0.258645 0.075174
|
||||
vn -0.116467 -0.992337 0.041265
|
||||
vn -0.385386 -0.892604 -0.233959
|
||||
vn -0.546778 0.362906 -0.754541
|
||||
vn -0.849251 -0.203792 0.487075
|
||||
vn -0.445795 0.086256 -0.890970
|
||||
vn -0.893227 0.392020 0.220152
|
||||
vn -0.412489 0.000355 -0.910963
|
||||
usemtl Material
|
||||
s off
|
||||
f 1//1 2//1 25//1
|
||||
f 2//2 15//2 25//2
|
||||
f 1//3 26//3 13//3
|
||||
f 1//4 25//4 26//4
|
||||
f 20//5 37//5 4//5
|
||||
f 37//6 7//6 35//6
|
||||
f 4//7 5//7 3//7
|
||||
f 3//8 6//8 23//8
|
||||
f 7//9 9//9 10//9
|
||||
f 7//10 21//10 22//10
|
||||
f 22//11 12//11 24//11
|
||||
f 11//12 13//12 12//12
|
||||
f 11//13 12//13 22//13
|
||||
f 14//14 28//14 29//14
|
||||
f 14//15 29//15 23//15
|
||||
f 16//16 8//16 15//16
|
||||
f 16//17 35//17 8//17
|
||||
f 35//18 7//18 8//18
|
||||
f 15//19 8//19 10//19
|
||||
f 16//20 18//20 32//20
|
||||
f 18//21 15//21 2//21
|
||||
f 17//22 2//22 36//22
|
||||
f 36//23 28//23 31//23
|
||||
f 20//24 11//24 22//24
|
||||
f 21//25 37//25 20//25
|
||||
f 20//26 23//26 11//26
|
||||
f 26//27 12//27 13//27
|
||||
f 24//28 25//28 10//28
|
||||
f 25//29 15//29 10//29
|
||||
f 27//30 28//30 36//30
|
||||
f 27//31 29//31 28//31
|
||||
f 36//32 13//32 27//32
|
||||
f 27//33 38//33 29//33
|
||||
f 32//34 19//34 31//34
|
||||
f 31//35 28//35 14//35
|
||||
f 35//36 5//36 4//36
|
||||
f 34//37 33//37 35//37
|
||||
f 35//38 33//38 5//38
|
||||
f 34//39 35//39 32//39
|
||||
f 36//40 1//40 13//40
|
||||
f 2//41 1//41 36//41
|
||||
f 3//42 20//42 4//42
|
||||
f 4//43 37//43 35//43
|
||||
f 20//44 3//44 23//44
|
||||
f 3//45 5//45 6//45
|
||||
f 8//46 7//46 10//46
|
||||
f 9//47 24//47 10//47
|
||||
f 21//48 7//48 37//48
|
||||
f 9//49 7//49 22//49
|
||||
f 9//50 22//50 24//50
|
||||
f 11//51 38//51 13//51
|
||||
f 38//52 11//52 29//52
|
||||
f 11//53 23//53 29//53
|
||||
f 5//54 14//54 6//54
|
||||
f 6//55 14//55 23//55
|
||||
f 14//56 5//56 33//56
|
||||
f 15//57 18//57 16//57
|
||||
f 17//58 19//58 18//58
|
||||
f 18//59 19//59 32//59
|
||||
f 17//60 18//60 2//60
|
||||
f 19//61 17//61 36//61
|
||||
f 19//62 36//62 31//62
|
||||
f 21//63 20//63 22//63
|
||||
f 25//64 24//64 26//64
|
||||
f 26//65 24//65 12//65
|
||||
f 38//66 27//66 13//66
|
||||
f 31//67 30//67 32//67
|
||||
f 30//68 34//68 32//68
|
||||
f 30//69 31//69 14//69
|
||||
f 30//70 33//70 34//70
|
||||
f 30//71 14//71 33//71
|
||||
f 35//72 16//72 32//72
|
103
punyverse/assets/models/asteroids/mainbelt2.obj
Normal file
|
@ -0,0 +1,103 @@
|
|||
# Blender v2.66 (sub 1) OBJ File: ''
|
||||
# www.blender.org
|
||||
mtllib mainbelt.mtl
|
||||
g Cube.002
|
||||
v -0.289079 -0.829861 0.215310
|
||||
v 0.057641 -0.817976 -0.343786
|
||||
v 0.543391 -0.663372 0.025647
|
||||
v -0.097897 -0.384127 -0.766643
|
||||
v 0.545964 -0.487153 -0.565775
|
||||
v 0.504985 -0.681732 0.425810
|
||||
v 0.124050 -0.113115 0.938530
|
||||
v -0.706349 -0.467985 0.350889
|
||||
v -0.654122 -0.523922 -0.388949
|
||||
v 0.176965 0.828195 -0.359842
|
||||
v -0.383487 0.292923 -0.693979
|
||||
v -0.738214 0.373419 -0.411660
|
||||
v -0.714335 0.466863 0.124949
|
||||
v -0.398389 0.720367 0.469422
|
||||
v 0.451232 0.721178 0.355306
|
||||
v 0.882521 0.137464 -0.019655
|
||||
v 0.520709 0.162782 -0.672326
|
||||
v 0.692252 0.151771 0.606789
|
||||
v 0.088118 0.461316 0.717918
|
||||
v -0.701579 0.121702 0.577301
|
||||
v 0.132479 0.283880 -0.909159
|
||||
vn 0.220116 -0.968562 0.115914
|
||||
vn 0.121188 -0.670211 -0.732209
|
||||
vn 0.190171 -0.981386 -0.026776
|
||||
vn -0.062026 -0.690971 0.720217
|
||||
vn -0.653070 -0.757215 0.011149
|
||||
vn -0.323725 0.721233 -0.612393
|
||||
vn -0.462979 0.861322 -0.209224
|
||||
vn 0.018021 0.989816 0.141208
|
||||
vn 0.872936 -0.045704 -0.485690
|
||||
vn 0.912617 -0.392863 -0.113086
|
||||
vn 0.731293 0.657305 -0.182098
|
||||
vn 0.733716 0.559838 -0.385022
|
||||
vn 0.919930 -0.268686 0.285545
|
||||
vn 0.580622 -0.295204 0.758771
|
||||
vn 0.400075 0.506643 0.763710
|
||||
vn -0.295046 0.312154 0.903055
|
||||
vn -0.794843 0.471399 0.382109
|
||||
vn -0.998990 0.017376 0.041429
|
||||
vn -0.999351 -0.005584 0.035599
|
||||
vn -0.993512 -0.094698 -0.062975
|
||||
vn -0.266191 0.695397 -0.667507
|
||||
vn 0.273402 -0.145262 -0.950868
|
||||
vn -0.385081 -0.063621 -0.920687
|
||||
vn 0.461921 -0.850540 -0.251416
|
||||
vn -0.256648 -0.720214 -0.644534
|
||||
vn -0.357494 -0.902317 -0.240879
|
||||
vn 0.550017 0.570541 -0.609888
|
||||
vn -0.434722 0.890284 -0.135687
|
||||
vn 0.084553 0.767893 0.634973
|
||||
vn 0.848402 0.468199 0.246991
|
||||
vn 0.919967 -0.385585 0.070604
|
||||
vn 0.342132 0.355460 0.869824
|
||||
vn -0.299721 0.325644 0.896730
|
||||
vn -0.275650 -0.599397 0.751492
|
||||
vn -0.454304 -0.316110 0.832876
|
||||
vn -0.547764 -0.072427 -0.833492
|
||||
vn 0.416633 -0.324827 -0.849061
|
||||
vn -0.637398 -0.188684 -0.747076
|
||||
usemtl Material
|
||||
s off
|
||||
f 1//1 2//1 3//1
|
||||
f 4//2 5//2 2//2
|
||||
f 6//3 1//3 3//3
|
||||
f 7//4 1//4 6//4
|
||||
f 1//5 8//5 9//5
|
||||
f 10//6 11//6 12//6
|
||||
f 10//7 13//7 14//7
|
||||
f 10//8 14//8 15//8
|
||||
f 16//9 5//9 17//9
|
||||
f 3//10 5//10 16//10
|
||||
f 16//11 10//11 15//11
|
||||
f 16//12 17//12 10//12
|
||||
f 16//13 18//13 6//13
|
||||
f 7//14 6//14 18//14
|
||||
f 19//15 18//15 15//15
|
||||
f 19//16 14//16 20//16
|
||||
f 20//17 14//17 13//17
|
||||
f 20//18 13//18 12//18
|
||||
f 20//19 12//19 8//19
|
||||
f 12//20 9//20 8//20
|
||||
f 11//21 10//21 21//21
|
||||
f 17//22 5//22 4//22
|
||||
f 21//23 4//23 11//23
|
||||
f 3//24 2//24 5//24
|
||||
f 2//25 9//25 4//25
|
||||
f 2//26 1//26 9//26
|
||||
f 21//27 10//27 17//27
|
||||
f 13//28 10//28 12//28
|
||||
f 15//29 14//29 19//29
|
||||
f 18//30 16//30 15//30
|
||||
f 3//31 16//31 6//31
|
||||
f 7//32 18//32 19//32
|
||||
f 20//33 7//33 19//33
|
||||
f 1//34 7//34 8//34
|
||||
f 7//35 20//35 8//35
|
||||
f 9//36 12//36 4//36
|
||||
f 4//37 21//37 17//37
|
||||
f 11//38 4//38 12//38
|
125
punyverse/assets/models/satellites/cassini.mtl
Normal file
|
@ -0,0 +1,125 @@
|
|||
# Blender MTL File: 'Cassini_tiny.blend'
|
||||
# Material Count: 13
|
||||
|
||||
newmtl None
|
||||
Ns 0.000000
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.800000 0.800000 0.800000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl aluminum
|
||||
Ns 92.156863
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.317021 0.317021 0.317021
|
||||
Ks 1.000000 1.000000 1.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl black_krinkle
|
||||
Ns 92.156863
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.117961 0.117961 0.117961
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd cassini/foil.jpg
|
||||
|
||||
newmtl black_krinkle_NONE
|
||||
Ns 92.156863
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.117961 0.117961 0.117961
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd cassini/foil.jpg
|
||||
|
||||
newmtl black_krinkle_cassini_01_.psd.001
|
||||
Ns 92.156863
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.117961 0.117961 0.117961
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl dish_AO
|
||||
Ns 92.156863
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.512000 0.512000 0.512000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl dish_AO_dish_AO.png
|
||||
Ns 92.156863
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.512000 0.512000 0.512000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl foil_gold
|
||||
Ns 92.156863
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 1.0 1.0 1.0
|
||||
Ks 0.5 0.5 0.5
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd cassini/tex.jpg
|
||||
|
||||
newmtl foil_gold_2
|
||||
Ns 92.156863
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 1.0 1.0 1.0
|
||||
Ks 0.5 0.5 0.5
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd cassini/tex.jpg
|
||||
|
||||
newmtl plastic_dark
|
||||
Ns 92.156863
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.132862 0.132862 0.132862
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl plastic_white
|
||||
Ns 92.156863
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.593267 0.593267 0.593267
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl tex_01
|
||||
Ns 92.156863
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.512000 0.512000 0.512000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd cassini/tex.jpg
|
||||
|
||||
newmtl tex_01_NONE
|
||||
Ns 92.156863
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.512000 0.512000 0.512000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd cassini/tex.jpg
|
12235
punyverse/assets/models/satellites/cassini.obj
Normal file
BIN
punyverse/assets/models/satellites/cassini/foil.jpg
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
punyverse/assets/models/satellites/cassini/tex.jpg
Normal file
After Width: | Height: | Size: 67 KiB |
BIN
punyverse/assets/models/satellites/hbltel_1.jpg
Normal file
After Width: | Height: | Size: 46 KiB |
BIN
punyverse/assets/models/satellites/hbltel_2.jpg
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
punyverse/assets/models/satellites/hbltel_3.jpg
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
punyverse/assets/models/satellites/hbltel_4.jpg
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
punyverse/assets/models/satellites/hbltel_w.jpg
Normal file
After Width: | Height: | Size: 25 KiB |
52
punyverse/assets/models/satellites/hst.mtl
Normal file
|
@ -0,0 +1,52 @@
|
|||
# Blender MTL File: 'None'
|
||||
# Material Count: 5
|
||||
|
||||
newmtl hbltel_1
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.467451 0.467451 0.467451
|
||||
Ks 0.449020 0.449020 0.449020
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd hbltel_1.jpg
|
||||
|
||||
newmtl hbltel_2
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.467451 0.467451 0.467451
|
||||
Ks 0.449020 0.449020 0.449020
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd hbltel_2.jpg
|
||||
|
||||
newmtl hbltel_3
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.467451 0.467451 0.467451
|
||||
Ks 0.449020 0.449020 0.449020
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd hbltel_3.jpg
|
||||
|
||||
newmtl hbltel_4
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.467451 0.467451 0.467451
|
||||
Ks 0.449020 0.449020 0.449020
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd hbltel_4.jpg
|
||||
|
||||
newmtl hbltel_wfc_1
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.467451 0.467451 0.467451
|
||||
Ks 0.449020 0.449020 0.449020
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd hbltel_w.jpg
|
12321
punyverse/assets/models/satellites/hst.obj
Normal file
110
punyverse/assets/models/satellites/iss.mtl
Normal file
|
@ -0,0 +1,110 @@
|
|||
# Blender MTL File: 'None'
|
||||
# Material Count: 12
|
||||
|
||||
newmtl Brown
|
||||
Ns 25
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.238431 0.189074 0.102853
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl ISSDarkMetal
|
||||
Ns 25
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.070138 0.070138 0.070138
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl ISSGray2
|
||||
Ns 25
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.241098 0.241098 0.241098
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl ISSMetal1
|
||||
Ns 25
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.381373 0.381373 0.381373
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl ISSSolarPanels
|
||||
Ns 25
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.043620 0.023622 0.301177
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl ISSWhiteMetal
|
||||
Ns 25
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.561093 0.561093 0.561093
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl _Brown
|
||||
Ns 25
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.238431 0.189074 0.102853
|
||||
Ks 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl _ISSDarkMetal
|
||||
Ns 25
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.070138 0.070138 0.070138
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl _ISSGray2
|
||||
Ns 25
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.241098 0.241098 0.241098
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl _ISSMetal1
|
||||
Ns 25
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.381373 0.381373 0.381373
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl _ISSSolarPanels
|
||||
Ns 25
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.043620 0.023622 0.301177
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl _ISSWhiteMetal
|
||||
Ns 25
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.561093 0.561093 0.561093
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
21459
punyverse/assets/models/satellites/iss.obj
Normal file
BIN
punyverse/assets/models/satellites/phobos.jpg
Normal file
After Width: | Height: | Size: 100 KiB |
20
punyverse/assets/models/satellites/phobos.mtl
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Ns = Phong specular component. Ranges from 0 to 1000. (I've seen various statements about this range (see below))
|
||||
# Kd = Diffuse color weighted by the diffuse coefficient.
|
||||
# Ka = Ambient color weighted by the ambient coefficient.
|
||||
# Ks = Specular color weighted by the specular coefficient.
|
||||
# d = Dissolve factor (pseudo-transparency). Values are from 0-1. 0 is completely transparent, 1 is opaque.
|
||||
# Ni = Refraction index. Values range from 1 upwards. A value of 1 will cause no refraction. A higher value implies refraction.
|
||||
# illum = (0, 1, or 2) 0 to disable lighting, 1 for ambient & diffuse only (specular color set to black), 2 for full lighting (see below)
|
||||
# sharpness = ? (see below)
|
||||
# c
|
||||
# map_Bump = Bump texture map.
|
||||
# map_d = Opacity texture map.
|
||||
# refl = reflection type and filename (?)
|
||||
|
||||
newmtl white
|
||||
Ka 1 1 1
|
||||
Kd 1 1 1
|
||||
Ks 0 0 0
|
||||
map_Kd phobos.jpg
|
||||
illum 2
|
||||
Ns 8
|
5131
punyverse/assets/models/satellites/phobos.obj
Normal file
21
punyverse/assets/textures.txt
Normal file
|
@ -0,0 +1,21 @@
|
|||
# This file contains all the textures that is bigger than the minimum OpenGL texture size
|
||||
# and hence may need to be resized depending on the machine
|
||||
|
||||
mercury.jpg
|
||||
earth.jpg
|
||||
cloudmap.jpg
|
||||
moon.jpg
|
||||
mars.jpg
|
||||
jupiter.jpg
|
||||
saturn.jpg
|
||||
moons/io.jpg
|
||||
moons/europa.jpg
|
||||
moons/ganymede.jpg
|
||||
moons/callisto.jpg
|
||||
moons/titan.jpg
|
||||
moons/rhea.jpg
|
||||
moons/iapetus.jpg
|
||||
moons/dione.jpg
|
||||
moons/tethys.jpg
|
||||
moons/enceladus.jpg
|
||||
moons/mimas.jpg
|
BIN
punyverse/assets/textures/atmosphere_earth.png
Normal file
After Width: | Height: | Size: 114 B |
BIN
punyverse/assets/textures/cloudmap.jpg
Normal file
After Width: | Height: | Size: 351 KiB |
BIN
punyverse/assets/textures/earth.jpg
Normal file
After Width: | Height: | Size: 1.7 MiB |
BIN
punyverse/assets/textures/earth_normal.jpg
Normal file
After Width: | Height: | Size: 81 KiB |
BIN
punyverse/assets/textures/jupiter.jpg
Normal file
After Width: | Height: | Size: 482 KiB |
BIN
punyverse/assets/textures/mars.jpg
Normal file
After Width: | Height: | Size: 2 MiB |
BIN
punyverse/assets/textures/mercury.jpg
Normal file
After Width: | Height: | Size: 358 KiB |
BIN
punyverse/assets/textures/moon.jpg
Normal file
After Width: | Height: | Size: 1.3 MiB |
BIN
punyverse/assets/textures/moons/callisto.jpg
Normal file
After Width: | Height: | Size: 364 KiB |
BIN
punyverse/assets/textures/moons/dione.jpg
Normal file
After Width: | Height: | Size: 236 KiB |
BIN
punyverse/assets/textures/moons/enceladus.jpg
Normal file
After Width: | Height: | Size: 206 KiB |
BIN
punyverse/assets/textures/moons/europa.jpg
Normal file
After Width: | Height: | Size: 244 KiB |
BIN
punyverse/assets/textures/moons/ganymede.jpg
Normal file
After Width: | Height: | Size: 284 KiB |
BIN
punyverse/assets/textures/moons/iapetus.jpg
Normal file
After Width: | Height: | Size: 175 KiB |
BIN
punyverse/assets/textures/moons/io.jpg
Normal file
After Width: | Height: | Size: 268 KiB |
BIN
punyverse/assets/textures/moons/mimas.jpg
Normal file
After Width: | Height: | Size: 278 KiB |
BIN
punyverse/assets/textures/moons/rhea.jpg
Normal file
After Width: | Height: | Size: 303 KiB |
BIN
punyverse/assets/textures/moons/tethys.jpg
Normal file
After Width: | Height: | Size: 281 KiB |
BIN
punyverse/assets/textures/moons/titan.jpg
Normal file
After Width: | Height: | Size: 210 KiB |
BIN
punyverse/assets/textures/neptune.jpg
Normal file
After Width: | Height: | Size: 9.6 KiB |
BIN
punyverse/assets/textures/ring_saturn.png
Normal file
After Width: | Height: | Size: 3 KiB |
BIN
punyverse/assets/textures/ring_uranus.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
punyverse/assets/textures/saturn.jpg
Normal file
After Width: | Height: | Size: 133 KiB |
BIN
punyverse/assets/textures/sky.jpg
Normal file
After Width: | Height: | Size: 7.2 MiB |
BIN
punyverse/assets/textures/sun.jpg
Normal file
After Width: | Height: | Size: 46 KiB |
BIN
punyverse/assets/textures/sun_corona.png
Normal file
After Width: | Height: | Size: 640 B |
BIN
punyverse/assets/textures/sun_diffuse.png
Normal file
After Width: | Height: | Size: 111 B |
BIN
punyverse/assets/textures/uranus.jpg
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
punyverse/assets/textures/venus.jpg
Normal file
After Width: | Height: | Size: 92 KiB |
41
punyverse/camera.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
from math import sin, cos, radians
|
||||
|
||||
|
||||
class Camera(object):
|
||||
def __init__(self, x=0, y=0, z=0, pitch=0, yaw=0, roll=0):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.z = z
|
||||
self.pitch = pitch
|
||||
self.yaw = yaw
|
||||
self.roll = roll
|
||||
|
||||
def move(self, speed):
|
||||
dx, dy, dz = self.direction()
|
||||
self.x += dx * speed
|
||||
self.y += dy * speed
|
||||
self.z += dz * speed
|
||||
|
||||
def mouse_move(self, dx, dy):
|
||||
if self.pitch > 90 or self.pitch < -90:
|
||||
dx = -dx
|
||||
if self.yaw + dx >= 360:
|
||||
self.yaw = self.yaw + dx - 360
|
||||
elif self.yaw + dx < 0:
|
||||
self.yaw = 360 - self.yaw + dx
|
||||
else:
|
||||
self.yaw += dx
|
||||
|
||||
self.pitch -= dy
|
||||
if self.pitch < -180:
|
||||
self.pitch += 360
|
||||
elif self.pitch > 180:
|
||||
self.pitch -= 360
|
||||
|
||||
def direction(self):
|
||||
m = cos(radians(self.pitch))
|
||||
|
||||
dy = -sin(radians(self.pitch))
|
||||
dx = cos(radians(self.yaw - 90)) * m
|
||||
dz = sin(radians(self.yaw - 90)) * m
|
||||
return dx, dy, dz
|
128
punyverse/entity.py
Normal file
|
@ -0,0 +1,128 @@
|
|||
from punyverse.orbit import KeplerOrbit
|
||||
|
||||
from pyglet.gl import *
|
||||
|
||||
|
||||
class Entity(object):
|
||||
def __init__(self, id, location, rotation=(0, 0, 0), direction=(0, 0, 0), background=False):
|
||||
self.id = id
|
||||
self.location = location
|
||||
self.rotation = rotation
|
||||
self.direction = direction
|
||||
self.background = background
|
||||
|
||||
def update(self):
|
||||
x, y, z = self.location
|
||||
dx, dy, dz = self.direction
|
||||
self.location = x + dx, y + dy, z + dz
|
||||
|
||||
|
||||
class Asteroid(Entity):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Asteroid, self).__init__(*args, **kwargs)
|
||||
|
||||
def update(self):
|
||||
super(Asteroid, self).update()
|
||||
rx, ry, rz = self.rotation
|
||||
# Increment all axis to 'spin'
|
||||
self.rotation = rx + 1, ry + 1, rz + 1
|
||||
|
||||
|
||||
class Belt(Entity):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.rotation_angle = kwargs.pop('rotation_angle', 5)
|
||||
self.world = kwargs.pop('world')
|
||||
super(Belt, self).__init__(*args, **kwargs)
|
||||
|
||||
def update(self):
|
||||
super(Belt, self).update()
|
||||
pitch, yaw, roll = self.rotation
|
||||
self.rotation = pitch, self.world.tick * self.rotation_angle % 360, roll
|
||||
|
||||
|
||||
class Body(Entity):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.rotation_angle = kwargs.pop('rotation_angle', 5)
|
||||
self.atmosphere = kwargs.pop('atmosphere', 0)
|
||||
self.cloudmap = kwargs.pop('cloudmap', 0)
|
||||
self.corona = kwargs.pop('corona', 0)
|
||||
self.last_tick = 0
|
||||
self.mass = kwargs.pop('mass', None)
|
||||
self.world = kwargs.pop('world')
|
||||
orbit_distance = kwargs.pop('orbit_distance', 40000) + .0
|
||||
self.orbit_show = orbit_distance * 1.25
|
||||
self.orbit_blend = orbit_distance / 4
|
||||
self.orbit_opaque = orbit_distance
|
||||
super(Body, self).__init__(*args, **kwargs)
|
||||
self.initial_roll = self.rotation[2]
|
||||
|
||||
def update(self):
|
||||
super(Body, self).update()
|
||||
|
||||
if self.last_tick != self.world.tick:
|
||||
self.last_tick = self.world.tick
|
||||
pitch, yaw, roll = self.rotation
|
||||
roll = (self.initial_roll + self.world.tick * self.rotation_angle) % 360
|
||||
self.rotation = pitch, yaw, roll
|
||||
|
||||
|
||||
class Satellite(Body):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.parent = kwargs.pop('parent')
|
||||
self.orbit_speed = kwargs.pop('orbit_speed', 1)
|
||||
|
||||
# Semi-major axis and eccentricity defines orbit
|
||||
distance = kwargs.pop('distance', 100)
|
||||
eccentricity = kwargs.pop('eccentricity', 0)
|
||||
|
||||
# Inclination, longitude of ascending node, and argument of periapsis defines orbital plane
|
||||
inclination = kwargs.pop('inclination', 0)
|
||||
longitude = kwargs.pop('longitude', 0)
|
||||
argument = kwargs.pop('argument', 0)
|
||||
|
||||
# Orbit calculation
|
||||
self.orbit_id = None
|
||||
self.orbit_cache = None
|
||||
|
||||
self.theta = 0
|
||||
# OpenGL's z-axis is reversed
|
||||
self.orbit = KeplerOrbit(distance, eccentricity, inclination, longitude, argument)
|
||||
super(Satellite, self).__init__(*args, **kwargs)
|
||||
|
||||
def get_orbit(self):
|
||||
# Cache key is the three orbital plane parameters and eccentricity
|
||||
cache = (self.orbit.eccentricity, self.orbit.longitude, self.orbit.inclination, self.orbit.argument)
|
||||
if self.orbit_cache == cache:
|
||||
return self.orbit_id
|
||||
|
||||
if self.orbit_id is not None:
|
||||
glDeleteLists(self.orbit_id, 1)
|
||||
|
||||
id = glGenLists(1)
|
||||
glNewList(id, GL_COMPILE)
|
||||
glBegin(GL_LINE_LOOP)
|
||||
for theta in xrange(360):
|
||||
x, z, y = self.orbit.orbit(theta)
|
||||
glVertex3f(x, y, z)
|
||||
glEnd()
|
||||
glEndList()
|
||||
|
||||
self.orbit_id = id
|
||||
self.orbit_cache = cache
|
||||
return id
|
||||
|
||||
def update(self):
|
||||
super(Body, self).update() # Notice how the parent class is skipped
|
||||
|
||||
if self.last_tick != self.world.tick:
|
||||
self.last_tick = self.world.tick
|
||||
pitch, yaw, roll = self.rotation
|
||||
roll = (self.initial_roll + self.world.tick * self.rotation_angle) % 360
|
||||
self.rotation = pitch, yaw, roll
|
||||
|
||||
self.parent.update()
|
||||
px, py, pz = self.parent.location
|
||||
self.theta = self.world.tick * self.orbit_speed % 360
|
||||
x, z, y = self.orbit.orbit(self.theta)
|
||||
self.location = (x + px, y + py, z + pz)
|
||||
|
396
punyverse/game.py
Normal file
|
@ -0,0 +1,396 @@
|
|||
#!/usr/bin/python
|
||||
from operator import attrgetter
|
||||
from math import hypot, sqrt, atan2, degrees
|
||||
from time import clock
|
||||
import time
|
||||
import random
|
||||
|
||||
from punyverse.camera import Camera
|
||||
from punyverse.world import load_world
|
||||
from punyverse.glgeom import *
|
||||
from punyverse.entity import Asteroid
|
||||
from punyverse import texture
|
||||
|
||||
try:
|
||||
from punyverse._model import model_list, load_model
|
||||
except ImportError:
|
||||
from punyverse.model import model_list, load_model
|
||||
|
||||
from pyglet.gl import *
|
||||
from pyglet.window import key, mouse
|
||||
|
||||
import pyglet
|
||||
|
||||
|
||||
INITIAL_SPEED = 0 # The initial speed of the player
|
||||
MOUSE_SENSITIVITY = 0.3 # Mouse sensitivity, 0..1, none...hyperspeed
|
||||
|
||||
MAX_DELTA = 5
|
||||
SEED = int(time.time())
|
||||
|
||||
|
||||
def entity_distance(x0, y0, z0):
|
||||
def distance(entity):
|
||||
x1, y1, z1 = entity.location
|
||||
return hypot(hypot(x1 - x0, y1 - y0), z1 - z0)
|
||||
|
||||
return distance
|
||||
|
||||
|
||||
class Applet(pyglet.window.Window):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Applet, self).__init__(*args, **kwargs)
|
||||
texture.init()
|
||||
|
||||
start = clock()
|
||||
self.fps = 0
|
||||
self.world = load_world('world.json')
|
||||
print 'Initializing game...'
|
||||
self.speed = INITIAL_SPEED
|
||||
self.keys = set()
|
||||
self.info = True
|
||||
self.debug = False
|
||||
self.orbit = True
|
||||
self.running = True
|
||||
self.moving = True
|
||||
self.info_precise = False
|
||||
self.atmosphere = True
|
||||
self.cloud = not texture.badcard
|
||||
|
||||
self.tick = self.world.tick_length
|
||||
self.ticks = [1, 2, 5, 10, 20, 40, 60, # Second range
|
||||
120, 300, 600, 1200, 1800, 2700, 3600, # Minute range
|
||||
7200, 14400, 21600, 43200, 86400, # Hour range
|
||||
172800, 432000, 604800, # 2, 5, 7 days
|
||||
1209600, 2592000, # 2 week, 1 month
|
||||
5270400, 7884000, 15768000, 31536000, # 2, 3, 6, 12 months
|
||||
63072000, 157680000, 315360000, # 2, 5, 10 years
|
||||
630720000, 1576800000, 3153600000, # 20, 50, 100 years
|
||||
]
|
||||
self.__time_per_second_cache = None
|
||||
self.__time_per_second_value = None
|
||||
self.__time_accumulate = 0
|
||||
pyglet.clock.schedule(self.update)
|
||||
|
||||
def speed_incrementer(object, increment):
|
||||
def incrementer():
|
||||
object.speed += increment
|
||||
|
||||
return incrementer
|
||||
|
||||
def attribute_toggler(object, attribute):
|
||||
getter = attrgetter(attribute)
|
||||
|
||||
def toggler():
|
||||
setattr(object, attribute, not getter(object))
|
||||
|
||||
return toggler
|
||||
|
||||
def increment_tick():
|
||||
index = self.ticks.index(self.tick) + 1
|
||||
if index < len(self.ticks):
|
||||
self.tick = self.ticks[index]
|
||||
|
||||
def decrement_tick():
|
||||
index = self.ticks.index(self.tick) - 1
|
||||
if index >= 0:
|
||||
self.tick = self.ticks[index]
|
||||
|
||||
self.key_handler = {
|
||||
key.ESCAPE: pyglet.app.exit,
|
||||
key.NUM_ADD: speed_incrementer(self, 1),
|
||||
key.NUM_SUBTRACT: speed_incrementer(self, -1),
|
||||
key.NUM_MULTIPLY: speed_incrementer(self, 10),
|
||||
key.NUM_DIVIDE: speed_incrementer(self, -10),
|
||||
key.PAGEUP: speed_incrementer(self, 100),
|
||||
key.PAGEDOWN: speed_incrementer(self, -100),
|
||||
key.HOME: speed_incrementer(self, 1000),
|
||||
key.END: speed_incrementer(self, -1000),
|
||||
key.R: lambda: setattr(self.cam, 'roll', 0),
|
||||
key.I: attribute_toggler(self, 'info'),
|
||||
key.D: attribute_toggler(self, 'debug'),
|
||||
key.O: attribute_toggler(self, 'orbit'),
|
||||
key.P: attribute_toggler(self, 'info_precise'),
|
||||
key.C: attribute_toggler(self, 'cloud'),
|
||||
key.X: attribute_toggler(self, 'atmosphere'),
|
||||
key.ENTER: attribute_toggler(self, 'running'),
|
||||
key.INSERT: increment_tick,
|
||||
key.DELETE: decrement_tick,
|
||||
key.SPACE: self.launch_meteor,
|
||||
key.E: lambda: self.set_exclusive_mouse(False),
|
||||
key.F: lambda: self.set_fullscreen(not self.fullscreen),
|
||||
}
|
||||
|
||||
self.mouse_press_handler = {
|
||||
mouse.LEFT: self.launch_meteor,
|
||||
mouse.RIGHT: attribute_toggler(self, 'moving'),
|
||||
}
|
||||
|
||||
self.label = pyglet.text.Label('', font_name='Consolas', font_size=12, x=10, y=self.height - 20,
|
||||
color=(255,) * 4, multiline=True, width=600)
|
||||
self.cam = Camera()
|
||||
|
||||
self.exclusive = False
|
||||
|
||||
glClearColor(0, 0, 0, 1)
|
||||
glClearDepth(1.0)
|
||||
|
||||
if not texture.badcard:
|
||||
glEnable(GL_BLEND)
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
||||
glEnable(GL_LINE_SMOOTH)
|
||||
glEnable(GL_POLYGON_SMOOTH)
|
||||
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST)
|
||||
glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST)
|
||||
|
||||
glAlphaFunc(GL_GEQUAL, 0.2)
|
||||
glDepthFunc(GL_LEQUAL)
|
||||
glEnable(GL_DEPTH_TEST)
|
||||
glShadeModel(GL_SMOOTH)
|
||||
|
||||
glMatrixMode(GL_MODELVIEW)
|
||||
|
||||
glEnable(GL_LIGHTING)
|
||||
glEnable(GL_LIGHT0)
|
||||
glEnable(GL_LIGHT1)
|
||||
|
||||
glEnable(GL_POLYGON_OFFSET_FILL)
|
||||
|
||||
fv4 = GLfloat * 4
|
||||
|
||||
glLightfv(GL_LIGHT0, GL_POSITION, fv4(.5, .5, 1, 0))
|
||||
glLightfv(GL_LIGHT0, GL_SPECULAR, fv4(.5, .5, 1, 1))
|
||||
glLightfv(GL_LIGHT0, GL_DIFFUSE, fv4(1, 1, 1, 1))
|
||||
glLightfv(GL_LIGHT1, GL_POSITION, fv4(1, 0, .5, 0))
|
||||
glLightfv(GL_LIGHT1, GL_DIFFUSE, fv4(.5, .5, .5, 1))
|
||||
glLightfv(GL_LIGHT1, GL_SPECULAR, fv4(1, 1, 1, 1))
|
||||
|
||||
print 'Loading asteroids...'
|
||||
self.asteroid_ids = [model_list(load_model(r'asteroids/01.obj'), 5, 5, 5, (0, 0, 0)),
|
||||
model_list(load_model(r'asteroids/02.obj'), 5, 5, 5, (0, 0, 0)),
|
||||
model_list(load_model(r'asteroids/03.obj'), 5, 5, 5, (0, 0, 0)),
|
||||
]
|
||||
|
||||
c = self.cam
|
||||
c.x, c.y, c.z = self.world.start
|
||||
c.pitch, c.yaw, c.roll = self.world.direction
|
||||
|
||||
print 'Updating entities...'
|
||||
for entity in self.world.tracker:
|
||||
entity.update()
|
||||
|
||||
print 'Loaded in %s seconds.' % (clock() - start)
|
||||
|
||||
def set_exclusive_mouse(self, exclusive):
|
||||
super(Applet, self).set_exclusive_mouse(exclusive)
|
||||
self.exclusive = exclusive
|
||||
|
||||
def launch_meteor(self):
|
||||
c = self.cam
|
||||
dx, dy, dz = c.direction()
|
||||
speed = abs(self.speed) * 1.1 + 5
|
||||
dx *= speed
|
||||
dy *= speed
|
||||
dz *= speed
|
||||
self.world.tracker.append(Asteroid(random.choice(self.asteroid_ids), (c.x, c.y - 3, c.z + 5),
|
||||
direction=(dx, dy, dz)))
|
||||
|
||||
def on_mouse_press(self, x, y, button, modifiers):
|
||||
if not self.exclusive:
|
||||
self.set_exclusive_mouse(True)
|
||||
else:
|
||||
if button in self.mouse_press_handler:
|
||||
self.mouse_press_handler[button]()
|
||||
|
||||
def on_mouse_motion(self, x, y, dx, dy):
|
||||
if self.exclusive: # Only handle camera movement if mouse is grabbed
|
||||
self.cam.mouse_move(dx * MOUSE_SENSITIVITY, dy * MOUSE_SENSITIVITY)
|
||||
|
||||
def on_key_press(self, symbol, modifiers):
|
||||
if self.exclusive: # Only handle keyboard input if mouse is grabbed
|
||||
if symbol in self.key_handler:
|
||||
self.key_handler[symbol]()
|
||||
else:
|
||||
self.keys.add(symbol)
|
||||
|
||||
def on_key_release(self, symbol, modifiers):
|
||||
if symbol in self.keys:
|
||||
self.keys.remove(symbol)
|
||||
|
||||
def on_resize(self, width, height):
|
||||
height = max(height, 1) # Prevent / by 0
|
||||
self.label.y = height - 20
|
||||
glViewport(0, 0, width, height)
|
||||
glMatrixMode(GL_PROJECTION)
|
||||
glLoadIdentity()
|
||||
# A field of view of 45
|
||||
gluPerspective(45.0, width / float(height), 1, 50000000.0)
|
||||
glMatrixMode(GL_MODELVIEW)
|
||||
|
||||
def on_mouse_scroll(self, x, y, scroll_x, scroll_y):
|
||||
self.speed += scroll_y * 50 + scroll_x * 500
|
||||
|
||||
def get_time_per_second(self):
|
||||
if self.__time_per_second_cache == self.tick:
|
||||
return self.__time_per_second_value
|
||||
time = self.tick + .0
|
||||
unit = 'seconds'
|
||||
for size, name in ((60, 'minutes'), (60, 'hours'), (24, 'days'), (365, 'years')):
|
||||
if time < size:
|
||||
break
|
||||
time /= size
|
||||
unit = name
|
||||
result = '%s %s' % (round(time, 1), unit)
|
||||
self.__time_per_second_cache = self.tick
|
||||
self.__time_per_second_value = result
|
||||
return result
|
||||
|
||||
def update(self, dt):
|
||||
c = self.cam
|
||||
|
||||
if self.exclusive:
|
||||
if key.A in self.keys:
|
||||
c.roll += 4 * dt * 10
|
||||
if key.S in self.keys:
|
||||
c.roll -= 4 * dt * 10
|
||||
if self.moving:
|
||||
c.move(self.speed * 10 * dt)
|
||||
|
||||
if self.running:
|
||||
delta = self.tick * dt
|
||||
update = int(delta + self.__time_accumulate + 0.5)
|
||||
if update:
|
||||
self.__time_accumulate = 0
|
||||
self.world.tick += update
|
||||
for entity in self.world.tracker:
|
||||
entity.update()
|
||||
else:
|
||||
self.__time_accumulate += delta
|
||||
|
||||
def on_draw(self, glMatrix=GLfloat * 16):
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
|
||||
glLoadIdentity()
|
||||
c = self.cam
|
||||
|
||||
x, y, z = c.x, c.y, c.z
|
||||
glRotatef(c.pitch, 1, 0, 0)
|
||||
glRotatef(c.yaw, 0, 1, 0)
|
||||
glRotatef(c.roll, 0, 0, 1)
|
||||
glTranslatef(-x, -y, -z)
|
||||
|
||||
glEnable(GL_LIGHTING)
|
||||
world = self.world
|
||||
get_distance = entity_distance(x, y, z)
|
||||
if x != world.x or y != world.y or z != world.z:
|
||||
world.tracker.sort(key=get_distance, reverse=True)
|
||||
world.tracker.sort(key=attrgetter('background'), reverse=True)
|
||||
world.x, world.y, world.z = x, y, z
|
||||
|
||||
for entity in world.tracker:
|
||||
x, y, z = entity.location
|
||||
pitch, yaw, roll = entity.rotation
|
||||
|
||||
glPushMatrix()
|
||||
glTranslatef(x, y, z)
|
||||
glRotatef(pitch, 1, 0, 0)
|
||||
glRotatef(yaw, 0, 1, 0)
|
||||
glRotatef(roll, 0, 0, 1)
|
||||
glPushAttrib(GL_CURRENT_BIT)
|
||||
glCallList(entity.id)
|
||||
if self.debug:
|
||||
glPushMatrix()
|
||||
glLineWidth(0.25)
|
||||
glPolygonOffset(1, 1)
|
||||
glDisable(GL_LIGHTING)
|
||||
glDisable(GL_TEXTURE_2D)
|
||||
glColor3f(0, 1, 0)
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
|
||||
glCallList(entity.id)
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
|
||||
glEnable(GL_LIGHTING)
|
||||
glEnable(GL_TEXTURE_2D)
|
||||
glPopMatrix()
|
||||
glPopAttrib()
|
||||
glPopMatrix()
|
||||
|
||||
has_corona = hasattr(entity, 'corona') and entity.corona
|
||||
has_atmosphere = hasattr(entity, 'atmosphere') and entity.atmosphere
|
||||
if self.atmosphere and (has_corona or has_atmosphere):
|
||||
glPushMatrix()
|
||||
x0, y0, z0 = entity.location
|
||||
|
||||
glTranslatef(x0, y0, z0)
|
||||
matrix = glMatrix()
|
||||
glGetFloatv(GL_MODELVIEW_MATRIX, matrix)
|
||||
matrix[0: 3] = [1, 0, 0]
|
||||
matrix[4: 7] = [0, 1, 0]
|
||||
matrix[8:11] = [0, 0, 1]
|
||||
glLoadMatrixf(matrix)
|
||||
if has_atmosphere:
|
||||
glCallList(entity.atmosphere)
|
||||
if has_corona:
|
||||
x, y, z = c.direction()
|
||||
glTranslatef(-x, -y, -z)
|
||||
glCallList(entity.corona)
|
||||
glPopMatrix()
|
||||
|
||||
if self.cloud and hasattr(entity, 'cloudmap') and entity.cloudmap:
|
||||
glPushMatrix()
|
||||
glEnable(GL_BLEND)
|
||||
glEnable(GL_ALPHA_TEST)
|
||||
glTranslatef(*entity.location)
|
||||
pitch, yaw, roll = entity.rotation
|
||||
glRotatef(pitch, 1, 0, 0)
|
||||
glRotatef(yaw, 0, 1, 0)
|
||||
glRotatef(roll, 0, 0, 1)
|
||||
glCallList(entity.cloudmap)
|
||||
glDisable(GL_ALPHA_TEST)
|
||||
glDisable(GL_BLEND)
|
||||
glPopMatrix()
|
||||
|
||||
if self.orbit and hasattr(entity, 'get_orbit') and hasattr(entity, 'parent'):
|
||||
parent = entity.parent
|
||||
distance = get_distance(parent)
|
||||
if distance < parent.orbit_show:
|
||||
glPushMatrix()
|
||||
glTranslatef(*entity.parent.location)
|
||||
glDisable(GL_LIGHTING)
|
||||
glPushAttrib(GL_LINE_BIT | GL_CURRENT_BIT)
|
||||
glColor4f(1, 1, 1, 1 if distance < parent.orbit_opaque else
|
||||
(1 - (distance - parent.orbit_opaque) / parent.orbit_blend))
|
||||
glLineWidth(1)
|
||||
glCallList(entity.get_orbit())
|
||||
glPopAttrib()
|
||||
glEnable(GL_LIGHTING)
|
||||
glPopMatrix()
|
||||
|
||||
glColor4f(1, 1, 1, 1)
|
||||
glDisable(GL_TEXTURE_2D)
|
||||
|
||||
width, height = self.get_size()
|
||||
|
||||
if self.info:
|
||||
ortho(width, height)
|
||||
|
||||
if self.info_precise:
|
||||
info = ('%d FPS @ (x=%.2f, y=%.2f, z=%.2f) @ %s, %s/s\n'
|
||||
'Direction(pitch=%.2f, yaw=%.2f, roll=%.2f)\nTick: %d' %
|
||||
(pyglet.clock.get_fps(), c.x, c.y, c.z, self.speed, self.get_time_per_second(),
|
||||
c.pitch, c.yaw, c.roll, self.world.tick))
|
||||
else:
|
||||
info = ('%d FPS @ (x=%.2f, y=%.2f, z=%.2f) @ %s, %s/s\n' %
|
||||
(pyglet.clock.get_fps(), c.x, c.y, c.z, self.speed, self.get_time_per_second()))
|
||||
self.label.text = info
|
||||
self.label.draw()
|
||||
|
||||
glPushAttrib(GL_CURRENT_BIT | GL_LINE_BIT)
|
||||
|
||||
glLineWidth(2)
|
||||
|
||||
cx, cy = width / 2, height / 2
|
||||
|
||||
glColor4f(0, 1, 0, 1)
|
||||
circle(10, 20, (cx, cy))
|
||||
glPopAttrib()
|
||||
|
||||
frustrum()
|
29
punyverse/gencheap.bat
Normal file
|
@ -0,0 +1,29 @@
|
|||
@echo off
|
||||
cd %~dp0assets\textures
|
||||
call :convert mercury.jpg mercury_small.jpg 1024x512
|
||||
call :convert earth.jpg earth_medium.jpg 2048x1024
|
||||
call :convert earth.jpg earth_small.jpg 1024x512
|
||||
call :convert moon.jpg moon_medium.jpg 2048x1024
|
||||
call :convert moon.jpg moon_small.jpg 1024x512
|
||||
call :convert mars.jpg mars_medium.jpg 2048x1024
|
||||
call :convert mars.jpg mars_small.jpg 1024x512
|
||||
call :convert jupiter.jpg jupiter_medium.jpg 2048x1024
|
||||
call :convert jupiter.jpg jupiter_small.jpg 1024x512
|
||||
call :convert saturn.jpg saturn_medium.jpg 2048x1024
|
||||
call :convert saturn.jpg saturn_small.jpg 1024x512
|
||||
call :convert moons\io.jpg moons\io_small.jpg 1024x512
|
||||
call :convert moons\europa.jpg moons\europa_small.jpg 1024x512
|
||||
call :convert moons\ganymede.jpg moons\ganymede_small.jpg 1024x512
|
||||
call :convert moons\callisto.jpg moons\callisto_small.jpg 1024x512
|
||||
call :convert moons\titan.jpg moons\titan_small.jpg 1024x512
|
||||
call :convert moons\rhea.jpg moons\rhea_small.jpg 1024x512
|
||||
call :convert moons\iapetus.jpg moons\iapetus_small.jpg 1024x512
|
||||
call :convert moons\dione.jpg moons\dione_small.jpg 1024x512
|
||||
call :convert moons\tethys.jpg moons\tethys_small.jpg 1024x512
|
||||
call :convert moons\enceladus.jpg moons\enceladus_small.jpg 1024x512
|
||||
call :convert moons\mimas.jpg moons\mimas_small.jpg 1024x512
|
||||
goto :eof
|
||||
|
||||
:convert
|
||||
echo Converting %1 to %2, size %3...
|
||||
if not exist %2 gm convert %1 -resize %3 %2
|
292
punyverse/glgeom.py
Normal file
|
@ -0,0 +1,292 @@
|
|||
from math import *
|
||||
from pyglet.gl import *
|
||||
from random import random, gauss, choice
|
||||
|
||||
TWOPI = pi * 2
|
||||
|
||||
__all__ = ['compile', 'ortho', 'frustrum', 'crosshair', 'circle', 'disk', 'sphere', 'colourball', 'torus', 'belt',
|
||||
'flare', 'normal_sphere']
|
||||
|
||||
|
||||
def compile(pointer, *args, **kwargs):
|
||||
display = glGenLists(1)
|
||||
glNewList(display, GL_COMPILE)
|
||||
pointer(*args, **kwargs)
|
||||
glEndList()
|
||||
return display
|
||||
|
||||
|
||||
def ortho(width, height):
|
||||
glDisable(GL_LIGHTING)
|
||||
glDisable(GL_DEPTH_TEST)
|
||||
glMatrixMode(GL_PROJECTION)
|
||||
glPushMatrix()
|
||||
glLoadIdentity()
|
||||
glOrtho(0, width, 0, height, -1, 1)
|
||||
glMatrixMode(GL_MODELVIEW)
|
||||
glPushMatrix()
|
||||
glLoadIdentity()
|
||||
|
||||
|
||||
def frustrum():
|
||||
glMatrixMode(GL_PROJECTION)
|
||||
glPopMatrix()
|
||||
glMatrixMode(GL_MODELVIEW)
|
||||
glPopMatrix()
|
||||
glEnable(GL_LIGHTING)
|
||||
glEnable(GL_DEPTH_TEST)
|
||||
|
||||
|
||||
def crosshair(size, (cx, cy)):
|
||||
glBegin(GL_LINES)
|
||||
glVertex2f(cx - size, cy)
|
||||
glVertex2f(cx + size, cy)
|
||||
glVertex2f(cx, cy - size)
|
||||
glVertex2f(cx, cy + size)
|
||||
glEnd()
|
||||
|
||||
|
||||
def circle(r, seg, (cx, cy)):
|
||||
glBegin(GL_LINE_LOOP)
|
||||
for i in xrange(seg):
|
||||
theta = TWOPI * i / seg
|
||||
glVertex2f(cx + cos(theta) * r, cy + sin(theta) * r)
|
||||
glEnd()
|
||||
|
||||
|
||||
def disk(rinner, router, segs, tex):
|
||||
glEnable(GL_TEXTURE_2D)
|
||||
glDisable(GL_LIGHTING)
|
||||
glBindTexture(GL_TEXTURE_2D, tex)
|
||||
res = segs * 5
|
||||
|
||||
glBegin(GL_TRIANGLE_STRIP)
|
||||
texture = 0
|
||||
factor = TWOPI / res
|
||||
theta = 0
|
||||
for n in xrange(res + 1):
|
||||
theta += factor
|
||||
x = cos(theta)
|
||||
y = sin(theta)
|
||||
glTexCoord2f(0, texture)
|
||||
glVertex2f(rinner * x, rinner * y)
|
||||
glTexCoord2f(1, texture)
|
||||
glVertex2f(router * x, router * y)
|
||||
texture ^= 1
|
||||
glEnd()
|
||||
glEnable(GL_LIGHTING)
|
||||
glDisable(GL_TEXTURE_2D)
|
||||
|
||||
|
||||
def flare(rinner, router, res, prob, tex):
|
||||
glEnable(GL_TEXTURE_2D)
|
||||
glDisable(GL_LIGHTING)
|
||||
glBindTexture(GL_TEXTURE_2D, tex)
|
||||
last_x = 1
|
||||
last_y = 0
|
||||
last_theta = 0
|
||||
factor = TWOPI / res
|
||||
rdelta = (router - rinner)
|
||||
glBegin(GL_QUADS)
|
||||
for i in xrange(res + 1):
|
||||
theta = last_theta + factor
|
||||
x = cos(theta)
|
||||
y = sin(theta)
|
||||
if random() > prob:
|
||||
distance = rinner + rdelta * random()
|
||||
avg_theta = (last_theta + theta) / 2
|
||||
x0, y0 = rinner * last_x, rinner * last_y
|
||||
x1, y1 = rinner * x, rinner * y
|
||||
x2, y2 = distance * cos(avg_theta), distance * sin(avg_theta)
|
||||
glTexCoord2f(0, 0)
|
||||
glVertex2f(x0, y0)
|
||||
glTexCoord2f(0, 1)
|
||||
glVertex2f(x1, y1)
|
||||
glTexCoord2f(1, 0)
|
||||
glVertex2f(x2, y2)
|
||||
glTexCoord2f(1, 1)
|
||||
glVertex2f(x2, y2)
|
||||
last_theta = theta
|
||||
last_x = x
|
||||
last_y = y
|
||||
glEnd()
|
||||
glEnable(GL_LIGHTING)
|
||||
glDisable(GL_TEXTURE_2D)
|
||||
|
||||
|
||||
def sphere(r, lats, longs, tex, lighting=True, fv4=GLfloat * 4):
|
||||
"""
|
||||
Sphere function from the OpenGL red book.
|
||||
"""
|
||||
sphere = gluNewQuadric()
|
||||
gluQuadricDrawStyle(sphere, GLU_FILL)
|
||||
gluQuadricTexture(sphere, True)
|
||||
if lighting:
|
||||
gluQuadricNormals(sphere, GLU_SMOOTH)
|
||||
|
||||
glEnable(GL_TEXTURE_2D)
|
||||
if lighting:
|
||||
glDisable(GL_BLEND)
|
||||
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, fv4(1, 1, 1, 0))
|
||||
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, fv4(1, 1, 1, 0))
|
||||
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 125)
|
||||
else:
|
||||
glDisable(GL_LIGHTING)
|
||||
glBindTexture(GL_TEXTURE_2D, tex)
|
||||
|
||||
gluSphere(sphere, r, lats, longs)
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0)
|
||||
glDisable(GL_TEXTURE_2D)
|
||||
glEnable(GL_LIGHTING)
|
||||
glEnable(GL_BLEND)
|
||||
gluDeleteQuadric(sphere)
|
||||
|
||||
|
||||
def colourball(r, lats, longs, colour, fv4=GLfloat * 4):
|
||||
"""
|
||||
Sphere function from the OpenGL red book.
|
||||
"""
|
||||
sphere = gluNewQuadric()
|
||||
|
||||
glDisable(GL_BLEND)
|
||||
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, fv4(*colour))
|
||||
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, fv4(1, 1, 1, 1))
|
||||
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 125)
|
||||
|
||||
gluSphere(sphere, r, lats, longs)
|
||||
|
||||
glEnable(GL_BLEND)
|
||||
gluDeleteQuadric(sphere)
|
||||
|
||||
|
||||
try:
|
||||
from _glgeom import normal_sphere
|
||||
except ImportError:
|
||||
import warnings
|
||||
warnings.warn('Large sphere drawing in Python is slow')
|
||||
|
||||
def normal_sphere(r, divide, tex, normal, lighting=True, fv4=GLfloat * 4):
|
||||
from texture import pil_load
|
||||
print 'Loading normal map: %s...' % normal,
|
||||
normal_map = pil_load(normal)
|
||||
normal = normal_map.load()
|
||||
print
|
||||
width, height = normal_map.size
|
||||
gray_scale = len(normal[0, 0]) == 1
|
||||
|
||||
glEnable(GL_TEXTURE_2D)
|
||||
if lighting:
|
||||
glDisable(GL_BLEND)
|
||||
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, fv4(1, 1, 1, 0))
|
||||
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, fv4(1, 1, 1, 0))
|
||||
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 125)
|
||||
else:
|
||||
glDisable(GL_LIGHTING)
|
||||
glBindTexture(GL_TEXTURE_2D, tex)
|
||||
|
||||
twopi_divide = TWOPI / divide
|
||||
pi_divide = pi / divide
|
||||
glBegin(GL_TRIANGLE_STRIP)
|
||||
for j in xrange(divide + 1):
|
||||
phi1 = j * twopi_divide
|
||||
phi2 = (j + 1) * twopi_divide
|
||||
|
||||
for i in xrange(divide + 1):
|
||||
theta = i * pi_divide
|
||||
|
||||
s = phi2 / TWOPI
|
||||
u = min(int(s * width), width - 1)
|
||||
t = theta / pi
|
||||
v = min(int(t * height), height - 1)
|
||||
if gray_scale:
|
||||
x = y = z = normal[u, v]
|
||||
else:
|
||||
x, y, z = normal[u, v]
|
||||
dx, dy, dz = sin(theta) * cos(phi2), sin(theta) * sin(phi2), cos(theta)
|
||||
nx, ny, nz = x / 127.5 - 1, y / 127.5 - 1, z / 127.5 - 1 # Make into [-1, 1]
|
||||
nx, nz = cos(theta) * nx + sin(theta) * nz, -sin(theta) * nx + cos(theta) * nz
|
||||
nx, ny = cos(phi2) * nx - sin(phi2) * ny, sin(phi2) * nx + cos(phi2) * ny
|
||||
glNormal3f(nx, ny, nz)
|
||||
glTexCoord2f(s, 1 - t) # GL is bottom up
|
||||
glVertex3f(r * dx, r * dy, r * dz)
|
||||
|
||||
s = phi1 / TWOPI # x
|
||||
u = min(int(s * width), width - 1)
|
||||
if gray_scale:
|
||||
x = y = z = normal[u, v]
|
||||
else:
|
||||
x, y, z = normal[u, v]
|
||||
dx, dy = sin(theta) * cos(phi1), sin(theta) * sin(phi1)
|
||||
nx, ny, nz = x / 127.5 - 1, y / 127.5 - 1, z / 127.5 - 1
|
||||
nx, nz = cos(theta) * nx + sin(theta) * nz, -sin(theta) * nx + cos(theta) * nz
|
||||
nx, ny = cos(phi1) * nx - sin(phi1) * ny, sin(phi1) * nx + cos(phi1) * ny
|
||||
glNormal3f(nx, ny, nz)
|
||||
glTexCoord2f(s, 1 - t)
|
||||
glVertex3f(r * dx, r * dy, r * dz)
|
||||
glEnd()
|
||||
|
||||
glDisable(GL_TEXTURE_2D)
|
||||
glEnable(GL_LIGHTING)
|
||||
glEnable(GL_BLEND)
|
||||
|
||||
|
||||
def belt(radius, cross, object, count):
|
||||
for i in xrange(count):
|
||||
theta = TWOPI * random()
|
||||
r = gauss(radius, cross)
|
||||
x, y, z = cos(theta) * r, gauss(0, cross), sin(theta) * r
|
||||
|
||||
glPushMatrix()
|
||||
glTranslatef(x, y, z)
|
||||
scale = gauss(1, 0.5)
|
||||
if scale < 0:
|
||||
scale = 1
|
||||
glScalef(scale, scale, scale)
|
||||
glCallList(choice(object))
|
||||
glPopMatrix()
|
||||
|
||||
|
||||
try:
|
||||
from _glgeom import torus
|
||||
except ImportError:
|
||||
def torus(major_radius, minor_radius, n_major, n_minor, material, shininess=125, fv4=GLfloat * 4):
|
||||
"""
|
||||
Torus function from the OpenGL red book.
|
||||
"""
|
||||
glPushAttrib(GL_CURRENT_BIT)
|
||||
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, fv4(*material))
|
||||
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, fv4(1, 1, 1, 1))
|
||||
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininess)
|
||||
|
||||
major_s = TWOPI / n_major
|
||||
minor_s = TWOPI / n_minor
|
||||
|
||||
def n(x, y, z):
|
||||
m = 1.0 / sqrt(x * x + y * y + z * z)
|
||||
return x * m, y * m, z * m
|
||||
|
||||
for i in xrange(n_major):
|
||||
a0 = i * major_s
|
||||
a1 = a0 + major_s
|
||||
x0 = cos(a0)
|
||||
y0 = sin(a0)
|
||||
x1 = cos(a1)
|
||||
y1 = sin(a1)
|
||||
|
||||
glBegin(GL_TRIANGLE_STRIP)
|
||||
|
||||
for j in xrange(n_minor + 1):
|
||||
b = j * minor_s
|
||||
c = cos(b)
|
||||
r = minor_radius * c + major_radius
|
||||
z = minor_radius * sin(b)
|
||||
|
||||
glNormal3f(*n(x0 * c, y0 * c, z / minor_radius))
|
||||
glVertex3f(x0 * r, y0 * r, z)
|
||||
|
||||
glNormal3f(*n(x1 * c, y1 * c, z / minor_radius))
|
||||
glVertex3f(x1 * r, y1 * r, z)
|
||||
|
||||
glEnd()
|
||||
glPopAttrib()
|
328
punyverse/model.py
Normal file
|
@ -0,0 +1,328 @@
|
|||
from punyverse.texture import load_texture
|
||||
|
||||
from pyglet.gl import *
|
||||
from uuid import uuid4
|
||||
import os
|
||||
import gzip
|
||||
import bz2
|
||||
import zipfile
|
||||
|
||||
|
||||
def zip_open(file):
|
||||
zip = zipfile.ZipFile(file)
|
||||
return zip.open(zip.namelist()[0])
|
||||
|
||||
openers = {
|
||||
'gz': gzip.open,
|
||||
'bz2': bz2.BZ2File,
|
||||
'zip': zip_open,
|
||||
}
|
||||
|
||||
|
||||
FACE_TRIANGLES = 0
|
||||
FACE_QUADS = 1
|
||||
|
||||
|
||||
class Face(object):
|
||||
def __init__(self, type, verts, norms, texs, vertices, normals, textures):
|
||||
self.type = type
|
||||
self.verts = verts
|
||||
self.norms = norms
|
||||
self.texs = texs
|
||||
self.vertices = vertices
|
||||
self.normals = normals
|
||||
self.textures = textures
|
||||
|
||||
|
||||
class Material(object):
|
||||
def __init__(self, name, texture=None, Ka=(0, 0, 0), Kd=(0, 0, 0), Ks=(0, 0, 0), shininess=0.0):
|
||||
self.name = name
|
||||
self.texture = texture
|
||||
self.Ka = Ka
|
||||
self.Kd = Kd
|
||||
self.Ks = Ks
|
||||
self.shininess = shininess
|
||||
|
||||
|
||||
class Group(object):
|
||||
def __init__(self, name=None):
|
||||
if name is None:
|
||||
self.name = str(uuid4())
|
||||
else:
|
||||
self.name = name
|
||||
self.min = ()
|
||||
self.material = None
|
||||
self.faces = []
|
||||
self.indices = []
|
||||
self.vertices = []
|
||||
self.normals = []
|
||||
self.textures = []
|
||||
self.idx_count = 0
|
||||
|
||||
def pack(self):
|
||||
min_x, min_y, min_z = 0, 0, 0
|
||||
for face in self.faces:
|
||||
for x, y, z in face.vertices:
|
||||
min_x = max(min_x, abs(x))
|
||||
min_y = max(min_y, abs(y))
|
||||
min_z = max(min_x, abs(z))
|
||||
self.min = (min_x, min_y, min_z)
|
||||
|
||||
|
||||
class WavefrontObject(object):
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
self.root = os.path.abspath(os.path.dirname(path))
|
||||
self.vertices = []
|
||||
self.normals = []
|
||||
self.textures = []
|
||||
self.groups = []
|
||||
self.materials = {}
|
||||
|
||||
self.perform_io(self.path)
|
||||
|
||||
def new_material(self, words):
|
||||
material = Material(words[1])
|
||||
self.materials[words[1]] = material
|
||||
self.current_material = material
|
||||
|
||||
def Ka(self, words):
|
||||
self.current_material.Ka = (float(words[1]), float(words[2]), float(words[3]))
|
||||
|
||||
def Kd(self, words):
|
||||
self.current_material.Kd = (float(words[1]), float(words[2]), float(words[3]))
|
||||
|
||||
def Ks(self, words):
|
||||
self.current_material.Ks = (float(words[1]), float(words[2]), float(words[3]))
|
||||
|
||||
def material_shininess(self, words):
|
||||
self.current_material.shininess = min(float(words[1]), 125)
|
||||
|
||||
def material_texture(self, words):
|
||||
self.current_material.texture = words[-1]
|
||||
|
||||
def vertex(self, words):
|
||||
self.vertices.append((float(words[1]), float(words[2]), float(words[3])))
|
||||
|
||||
def normal(self, words):
|
||||
self.normals.append((float(words[1]), float(words[2]), float(words[3])))
|
||||
|
||||
def texture(self, words):
|
||||
l = len(words)
|
||||
x, y, z = 0, 0, 0
|
||||
if l >= 2:
|
||||
x = float(words[1])
|
||||
if l >= 3:
|
||||
# OBJ origin is at upper left, OpenGL origin is at lower left
|
||||
y = 1 - float(words[2])
|
||||
if l >= 4:
|
||||
z = float(words[3])
|
||||
self.textures.append((x, y, z))
|
||||
|
||||
def face(self, words):
|
||||
l = len(words)
|
||||
type = -1
|
||||
vertex_count = l - 1
|
||||
face_vertices = []
|
||||
face_normals = []
|
||||
face_textures = []
|
||||
|
||||
if vertex_count == 3:
|
||||
type = FACE_TRIANGLES
|
||||
else:
|
||||
type = FACE_QUADS
|
||||
|
||||
current_value = -1
|
||||
texture_len = len(self.textures)
|
||||
vindices = []
|
||||
nindices = []
|
||||
tindices = []
|
||||
|
||||
for i in xrange(1, vertex_count + 1):
|
||||
raw_faces = words[i].split('/')
|
||||
l = len(raw_faces)
|
||||
|
||||
current_value = int(raw_faces[0])
|
||||
vindices.append(current_value - 1)
|
||||
face_vertices.append(self.vertices[current_value - 1])
|
||||
|
||||
if l == 1:
|
||||
continue
|
||||
if l >= 2 and raw_faces[1]:
|
||||
current_value = int(raw_faces[1])
|
||||
if current_value <= texture_len:
|
||||
tindices.append(current_value - 1)
|
||||
face_textures.append(self.textures[current_value - 1])
|
||||
if l >= 3 and raw_faces[2]:
|
||||
current_value = int(raw_faces[2])
|
||||
nindices.append(current_value - 1)
|
||||
face_normals.append(self.normals[current_value - 1])
|
||||
|
||||
if self.current_group is None:
|
||||
self.current_group = group = Group()
|
||||
self.groups.append(group)
|
||||
else:
|
||||
group = self.current_group
|
||||
|
||||
group.vertices += face_vertices
|
||||
group.normals += face_normals
|
||||
group.textures += face_textures
|
||||
idx_count = group.idx_count
|
||||
group.indices += (idx_count + 1, idx_count + 2, idx_count + 3)
|
||||
group.idx_count += 3
|
||||
|
||||
group.faces.append(Face(type, vindices, nindices, tindices, face_vertices, face_normals, face_textures))
|
||||
|
||||
def material(self, words):
|
||||
self.perform_io(os.path.join(self.root, words[1]))
|
||||
|
||||
def use_material(self, words):
|
||||
mat = words[1]
|
||||
try:
|
||||
self.current_group.material = self.materials[mat]
|
||||
except KeyError:
|
||||
print "Warning: material %s undefined, only %s defined." % (mat, self.materials)
|
||||
except AttributeError:
|
||||
print "Warning: no group"
|
||||
|
||||
def group(self, words):
|
||||
name = words[1]
|
||||
group = Group(name)
|
||||
|
||||
if self.groups:
|
||||
self.current_group.pack()
|
||||
self.groups.append(group)
|
||||
self.current_group = group
|
||||
|
||||
def perform_io(self, file):
|
||||
ext = os.path.splitext(file)[1].lstrip('.')
|
||||
reader = openers.get(ext, open)(file)
|
||||
|
||||
dispatcher = {
|
||||
'v': self.vertex,
|
||||
'vn': self.normal,
|
||||
'vt': self.texture,
|
||||
'f': self.face,
|
||||
'mtllib': self.material,
|
||||
'usemtl': self.use_material,
|
||||
'g': self.group,
|
||||
'o': self.group,
|
||||
'newmtl': self.new_material,
|
||||
'Ka': self.Ka,
|
||||
'Kd': self.Kd,
|
||||
'Ks': self.Ks,
|
||||
'Ns': self.material_shininess,
|
||||
'map_Kd': self.material_texture,
|
||||
}
|
||||
default = lambda words: None
|
||||
|
||||
with reader:
|
||||
for buf in reader:
|
||||
if not buf or buf.startswith(('\r', '\n', '#')):
|
||||
continue # Empty or comment
|
||||
words = buf.split()
|
||||
type = words[0]
|
||||
|
||||
dispatcher.get(type, default)(words)
|
||||
return True
|
||||
|
||||
import sys
|
||||
if hasattr(sys, 'frozen'):
|
||||
model_base = os.path.dirname(sys.executable)
|
||||
else:
|
||||
model_base = os.path.join(os.path.dirname(__file__), 'assets', 'models')
|
||||
|
||||
|
||||
def load_model(path):
|
||||
if not os.path.isabs(path):
|
||||
path = os.path.join(model_base, path)
|
||||
if not isinstance(path, unicode):
|
||||
path = path.decode('mbcs')
|
||||
return WavefrontObject(path)
|
||||
|
||||
|
||||
def model_list(model, sx=1, sy=1, sz=1, rotation=(0, 0, 0)):
|
||||
for m, text in model.materials.iteritems():
|
||||
if text.texture:
|
||||
load_texture(os.path.join(model.root, text.texture))
|
||||
|
||||
display = glGenLists(1)
|
||||
|
||||
glNewList(display, GL_COMPILE)
|
||||
glPushMatrix()
|
||||
glPushAttrib(GL_CURRENT_BIT)
|
||||
|
||||
pitch, yaw, roll = rotation
|
||||
glPushAttrib(GL_TRANSFORM_BIT)
|
||||
glRotatef(pitch, 1, 0, 0)
|
||||
glRotatef(yaw, 0, 1, 0)
|
||||
glRotatef(roll, 0, 0, 1)
|
||||
glPopAttrib()
|
||||
|
||||
vertices = model.vertices
|
||||
textures = model.textures
|
||||
normals = model.normals
|
||||
|
||||
for g in model.groups:
|
||||
material = g.material
|
||||
|
||||
tex_id = load_texture(os.path.join(model.root, material.texture)) if (material and material.texture) else 0
|
||||
|
||||
if tex_id:
|
||||
glEnable(GL_TEXTURE_2D)
|
||||
glBindTexture(GL_TEXTURE_2D, tex_id)
|
||||
else:
|
||||
glBindTexture(GL_TEXTURE_2D, 0)
|
||||
glDisable(GL_TEXTURE_2D)
|
||||
|
||||
if material:
|
||||
fv4 = GLfloat * 4
|
||||
|
||||
if material.Ka:
|
||||
kx, ky, kz = material.Ka
|
||||
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, fv4(kx, ky, kz, 1))
|
||||
if material.Kd:
|
||||
kx, ky, kz = material.Kd
|
||||
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, fv4(kx, ky, kz, 1))
|
||||
if material.Ks:
|
||||
kx, ky, kz = material.Ks
|
||||
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, fv4(kx, ky, kz, 1))
|
||||
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, material.shininess)
|
||||
|
||||
type = -1
|
||||
|
||||
def point(f, vertices, normals, textures, n):
|
||||
if f.norms:
|
||||
glNormal3f(*normals[f.norms[n]])
|
||||
if tex_id:
|
||||
glTexCoord2f(*textures[f.texs[n]][:2])
|
||||
|
||||
x, y, z = vertices[f.verts[n]]
|
||||
glVertex3f(x * sx, y * sy, z * sz)
|
||||
|
||||
for f in g.faces:
|
||||
if type != f.type:
|
||||
if type != -1:
|
||||
glEnd()
|
||||
glBegin(GL_TRIANGLES)
|
||||
type = f.type
|
||||
|
||||
point(f, vertices, normals, textures, 0)
|
||||
point(f, vertices, normals, textures, 1)
|
||||
point(f, vertices, normals, textures, 2)
|
||||
|
||||
if type == FACE_QUADS:
|
||||
point(f, vertices, normals, textures, 2)
|
||||
point(f, vertices, normals, textures, 3)
|
||||
point(f, vertices, normals, textures, 0)
|
||||
glEnd()
|
||||
|
||||
if tex_id:
|
||||
glBindTexture(GL_TEXTURE_2D, 0)
|
||||
glDisable(GL_TEXTURE_2D)
|
||||
|
||||
glPopAttrib()
|
||||
glPopMatrix()
|
||||
|
||||
glEndList()
|
||||
return display
|
95
punyverse/orbit.py
Normal file
|
@ -0,0 +1,95 @@
|
|||
from math import sin, cos, tan, atan, sqrt, radians, degrees
|
||||
|
||||
|
||||
class KeplerOrbit(object):
|
||||
def __init__(self, sma, eccentricity, inclination=0, longitude=0, argument=0):
|
||||
self.sma = sma
|
||||
self.eccentricity = eccentricity
|
||||
self.inclination = inclination
|
||||
self.longitude = longitude
|
||||
self.argument = argument
|
||||
self.__true_anomaly_factor = sqrt((1 + eccentricity)/(1 - eccentricity))
|
||||
self.__distance_factor = sma * (1 - eccentricity ** 2)
|
||||
|
||||
@property
|
||||
def inclination(self):
|
||||
return degrees(self._inclination)
|
||||
|
||||
@inclination.setter
|
||||
def inclination(self, value):
|
||||
self.inclination_radian = radians(value)
|
||||
|
||||
@property
|
||||
def inclination_radian(self):
|
||||
return self._inclination
|
||||
|
||||
@inclination_radian.setter
|
||||
def inclination_radian(self, value):
|
||||
self._inclination = value
|
||||
self.__sin_inclination = sin(self._inclination)
|
||||
self.__cos_inclination = cos(self._inclination)
|
||||
|
||||
@property
|
||||
def longitude(self):
|
||||
return degrees(self._longitude)
|
||||
|
||||
@longitude.setter
|
||||
def longitude(self, value):
|
||||
self.longitude_radian = radians(value)
|
||||
|
||||
@property
|
||||
def longitude_radian(self):
|
||||
return self._longitude
|
||||
|
||||
@longitude_radian.setter
|
||||
def longitude_radian(self, value):
|
||||
self._longitude = value
|
||||
self.__sin_longitude = sin(self._longitude)
|
||||
self.__cos_longitude = cos(self._longitude)
|
||||
|
||||
@property
|
||||
def argument(self):
|
||||
return degrees(self._argument)
|
||||
|
||||
@argument.setter
|
||||
def argument(self, value):
|
||||
self.argument_radian = radians(value)
|
||||
|
||||
@property
|
||||
def argument_radian(self):
|
||||
return self._argument
|
||||
|
||||
@argument_radian.setter
|
||||
def argument_radian(self, value):
|
||||
self._argument = value
|
||||
self.__sin_argument = sin(self._argument)
|
||||
self.__cos_argument = cos(self._argument)
|
||||
|
||||
def eccentric_anomaly(self, mean_anomaly):
|
||||
e1 = mean_anomaly
|
||||
e2 = mean_anomaly + self.eccentricity * sin(e1)
|
||||
while abs(e1 - e2) > 0.000001:
|
||||
e1, e2 = e2, mean_anomaly + self.eccentricity * sin(e2)
|
||||
return e2
|
||||
|
||||
def true_anomaly(self, mean_anomaly):
|
||||
eccentric_anomaly = self.eccentric_anomaly(mean_anomaly)
|
||||
return 2 * atan(self.__true_anomaly_factor * tan(eccentric_anomaly / 2))
|
||||
|
||||
def orbit(self, mean_anomaly):
|
||||
mean_anomaly = radians(mean_anomaly)
|
||||
phi = self.true_anomaly(mean_anomaly)
|
||||
r = self.__distance_factor / (1 + self.eccentricity * cos(phi))
|
||||
x = r * cos(phi)
|
||||
y = r * sin(phi)
|
||||
z = 0
|
||||
|
||||
# phi = longitude, theta = inclination, psi = argument
|
||||
x, y = (x * self.__cos_longitude + y * self.__sin_longitude,
|
||||
-x * self.__sin_longitude + y * self.__cos_longitude)
|
||||
x, z = (x * self.__cos_inclination + z * self.__sin_inclination,
|
||||
-x * self.__sin_inclination + z * self.__cos_inclination)
|
||||
x, y = (x * self.__cos_argument + y * self.__sin_argument,
|
||||
-x * self.__sin_argument + y * self.__cos_argument)
|
||||
|
||||
return x, y, z
|
266
punyverse/texture.py
Normal file
|
@ -0,0 +1,266 @@
|
|||
from pyglet import image
|
||||
from pyglet.gl import *
|
||||
from ctypes import c_int, byref, c_ulong
|
||||
import os.path
|
||||
import struct
|
||||
import itertools
|
||||
|
||||
try:
|
||||
from _glgeom import bgr_to_rgb
|
||||
except ImportError:
|
||||
import warnings
|
||||
warnings.warn('Compile _glgeom.c, or double the start up time.')
|
||||
|
||||
# Use magick when _glgeom is not compiled (is actually slower)
|
||||
try:
|
||||
from pgmagick import Blob, Image
|
||||
except ImportError:
|
||||
magick = False
|
||||
else:
|
||||
magick = True
|
||||
|
||||
def bgr_to_rgb(source, width, height, alpha=False, bottom_up=True):
|
||||
length = len(source)
|
||||
depth = length / (width * height)
|
||||
depth2 = depth - alpha
|
||||
result = bytearray(length)
|
||||
row = width * depth
|
||||
for y in xrange(height):
|
||||
for x in xrange(width):
|
||||
ioffset = y * width * depth + x * depth
|
||||
ooffset = (height - y - 1 if bottom_up else y) * row + x * depth
|
||||
for i in xrange(depth2):
|
||||
result[ooffset+i] = source[ioffset+depth2-i-1]
|
||||
if alpha:
|
||||
result[ooffset+depth2] = source[ioffset+depth2]
|
||||
return str(result)
|
||||
else:
|
||||
magick = False
|
||||
|
||||
try:
|
||||
from cStringIO import StringIO
|
||||
except ImportError:
|
||||
from StringIO import StringIO
|
||||
|
||||
__all__ = ['load_texture', 'load_clouds', 'load_image', 'pil_load']
|
||||
|
||||
id = 0
|
||||
cache = {}
|
||||
|
||||
max_texture = None
|
||||
power_of_two = None
|
||||
badcard = False
|
||||
bgra = False
|
||||
|
||||
|
||||
def init():
|
||||
global max_texture, power_of_two, badcard, bgra, magick
|
||||
|
||||
if max_texture is None:
|
||||
buf = c_int()
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, byref(buf))
|
||||
max_texture = buf.value
|
||||
badcard = gl_info.get_renderer() in ('GDI Generic',)
|
||||
if badcard:
|
||||
import warnings
|
||||
warnings.warn('Please update your graphics drivers if possible')
|
||||
#extensions = gl_info.get_extensions()
|
||||
#bgra = 'GL_EXT_bgra' in extensions
|
||||
#if bgra and magick:
|
||||
# magick = False # Disable magick because BGRA needs it not
|
||||
|
||||
if power_of_two is None:
|
||||
power_of_two = gl_info.have_version(2) or gl_info.have_extension('GL_ARB_texture_non_power_of_two')
|
||||
|
||||
is_power2 = lambda num: num != 0 and ((num & (num - 1)) == 0)
|
||||
|
||||
|
||||
def image_info(data):
|
||||
data = str(data)
|
||||
size = len(data)
|
||||
height = -1
|
||||
width = -1
|
||||
content_type = ''
|
||||
|
||||
# handle GIFs
|
||||
if (size >= 10) and data[:6] in ('GIF87a', 'GIF89a'):
|
||||
# Check to see if content_type is correct
|
||||
content_type = 'image/gif'
|
||||
w, h = struct.unpack("<HH", data[6:10])
|
||||
width = int(w)
|
||||
height = int(h)
|
||||
|
||||
# See PNG 2. Edition spec (http://www.w3.org/TR/PNG/)
|
||||
# Bytes 0-7 are below, 4-byte chunk length, then 'IHDR'
|
||||
# and finally the 4-byte width, height
|
||||
elif ((size >= 24) and data.startswith('\211PNG\r\n\032\n')
|
||||
and (data[12:16] == 'IHDR')):
|
||||
content_type = 'image/png'
|
||||
w, h = struct.unpack(">LL", data[16:24])
|
||||
width = int(w)
|
||||
height = int(h)
|
||||
|
||||
# Maybe this is for an older PNG version.
|
||||
elif (size >= 16) and data.startswith('\211PNG\r\n\032\n'):
|
||||
# Check to see if we have the right content type
|
||||
content_type = 'image/png'
|
||||
w, h = struct.unpack(">LL", data[8:16])
|
||||
width = int(w)
|
||||
height = int(h)
|
||||
|
||||
# handle JPEGs
|
||||
elif (size >= 2) and data.startswith('\377\330'):
|
||||
content_type = 'image/jpeg'
|
||||
jpeg = StringIO(data)
|
||||
jpeg.read(2)
|
||||
b = jpeg.read(1)
|
||||
try:
|
||||
while b and ord(b) != 0xDA:
|
||||
while ord(b) != 0xFF:
|
||||
b = jpeg.read(1)
|
||||
while ord(b) == 0xFF:
|
||||
b = jpeg.read(1)
|
||||
if 0xC0 <= ord(b) <= 0xC3:
|
||||
jpeg.read(3)
|
||||
h, w = struct.unpack(">HH", jpeg.read(4))
|
||||
break
|
||||
else:
|
||||
jpeg.read(int(struct.unpack(">H", jpeg.read(2))[0])-2)
|
||||
b = jpeg.read(1)
|
||||
width = int(w)
|
||||
height = int(h)
|
||||
except struct.error:
|
||||
pass
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
return content_type, width, height
|
||||
|
||||
|
||||
def check_size(width, height):
|
||||
init()
|
||||
if width > max_texture or height > max_texture:
|
||||
print 'too large'
|
||||
raise ValueError('Texture too large')
|
||||
elif not power_of_two:
|
||||
if not is_power2(width) or not is_power2(height):
|
||||
print 'not power of two'
|
||||
raise ValueError('Texture not power of two')
|
||||
|
||||
|
||||
def load_image(file, path):
|
||||
print "Loading image %s..." % file,
|
||||
|
||||
try:
|
||||
file = open(path, 'rb')
|
||||
except IOError:
|
||||
print 'exists not'
|
||||
raise ValueError('Texture exists not')
|
||||
type, width, height = image_info(file.read(65536))
|
||||
file.seek(0, 0)
|
||||
if type:
|
||||
check_size(width, height)
|
||||
|
||||
if magick:
|
||||
file.close()
|
||||
file = Image(path.encode('mbcs' if os.name == 'nt' else 'utf8'))
|
||||
geo = file.size()
|
||||
check_size(geo.width(), geo.height())
|
||||
print
|
||||
blob = Blob()
|
||||
file.flip()
|
||||
file.write(blob, 'RGBA')
|
||||
texture = blob.data
|
||||
mode = GL_RGBA
|
||||
else:
|
||||
try:
|
||||
raw = image.load(path, file=file)
|
||||
except IOError:
|
||||
print 'exists not'
|
||||
raise ValueError('Texture exists not')
|
||||
|
||||
width, height = raw.width, raw.height
|
||||
check_size(width, height)
|
||||
print
|
||||
|
||||
mode = GL_RGBA if 'A' in raw.format else GL_RGB
|
||||
# Flip from BGR to RGB
|
||||
if raw.format in ('BGR', 'BGRA'):
|
||||
if bgra:
|
||||
mode = {GL_RGBA: GL_BGRA, GL_RGB: GL_BGR}[mode]
|
||||
texture = raw.data
|
||||
else:
|
||||
texture = bgr_to_rgb(raw.data, width, height, 'A' in raw.format)
|
||||
elif raw.format in ('RGB', 'RGBA'):
|
||||
texture = raw.data
|
||||
else:
|
||||
texture = raw.get_data('RGBA', width * 4)
|
||||
return path, width, height, len(raw.format), mode, texture
|
||||
|
||||
|
||||
def load_texture(file):
|
||||
if os.path.isabs(file):
|
||||
path = file
|
||||
file = os.path.basename(path)
|
||||
else:
|
||||
path = os.path.join(os.path.dirname(__file__), 'assets', 'textures', file)
|
||||
|
||||
if path in cache:
|
||||
return cache[path]
|
||||
|
||||
path, width, height, depth, mode, texture = load_image(file, path)
|
||||
|
||||
buffer = c_ulong()
|
||||
glGenTextures(1, byref(buffer))
|
||||
id = buffer.value
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, id)
|
||||
|
||||
filter = GL_NEAREST if badcard else GL_LINEAR
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter)
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter)
|
||||
#gluBuild2DMipmaps(GL_TEXTURE_2D, depth, width, height, mode, GL_UNSIGNED_BYTE, texture)
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, mode, width, height, 0, mode, GL_UNSIGNED_BYTE, texture)
|
||||
|
||||
cache[path] = id
|
||||
|
||||
return id
|
||||
|
||||
|
||||
def pil_load(file):
|
||||
import Image
|
||||
return Image.open(os.path.join(os.path.dirname(__file__), 'assets', 'textures', file))
|
||||
|
||||
|
||||
def load_clouds(file):
|
||||
if os.path.isabs(file):
|
||||
path = file
|
||||
file = os.path.basename(path)
|
||||
else:
|
||||
path = os.path.join(os.path.dirname(__file__), 'assets', 'textures', file)
|
||||
|
||||
if path in cache:
|
||||
return cache[path]
|
||||
|
||||
path, width, height, depth, mode, texture = load_image(file, path)
|
||||
|
||||
buffer = c_ulong()
|
||||
glGenTextures(1, byref(buffer))
|
||||
id = buffer.value
|
||||
|
||||
pixels = bytearray(len(texture) * 4)
|
||||
white = chr(255)
|
||||
pixels[:] = itertools.chain.from_iterable(itertools.izip(itertools.repeat(white), itertools.repeat(white),
|
||||
itertools.repeat(white),
|
||||
itertools.islice(texture, 0, None, depth)))
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, id)
|
||||
|
||||
filter = GL_LINEAR
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter)
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter)
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, str(pixels))
|
||||
|
||||
cache[path] = id
|
||||
|
||||
return id
|
318
punyverse/world.json
Normal file
|
@ -0,0 +1,318 @@
|
|||
{
|
||||
"comments": {
|
||||
"au": "greatly shrunk so planets can actually be seen",
|
||||
"tick": "real world second per game tick",
|
||||
"length": "kilometre per world units for satellite distance and planetary radius",
|
||||
"radius": "note that radius is equatorial, flattening will be implemented later",
|
||||
"distance": "virtual distance to look better, in km",
|
||||
"sma": "semi-major axis used with mass of parent to calculate orbit, in km",
|
||||
"mass": "mass in kg",
|
||||
"texture": "a group of texture to use, tried in that order. a list means a colour",
|
||||
"model": "used to load a wavefront object instead of a textured sphere"
|
||||
},
|
||||
"au": 10000,
|
||||
"tick": 3600,
|
||||
"length": 63.7,
|
||||
"bodies": {
|
||||
"sun": {
|
||||
"texture": ["sun.jpg", [0.99, 0.97, 0.66, 1]],
|
||||
"radius": 80000,
|
||||
"pitch": -90,
|
||||
"yaw": 7.25,
|
||||
"mass": 1.9891e+30,
|
||||
"rotation": 2164320,
|
||||
"atmosphere": {
|
||||
"corona_texture": "sun_corona.png",
|
||||
"corona_size": 1500,
|
||||
"corona_division": 100,
|
||||
"corona_prob": 0.5,
|
||||
"diffuse_texture": "sun_diffuse.png",
|
||||
"diffuse_size": 300
|
||||
}
|
||||
},
|
||||
"mercury": {
|
||||
"texture": ["mercury.jpg", "mercury_small.jpg", [0.44, 0.43, 0.43, 1]],
|
||||
"radius": 2439.7,
|
||||
"z": "0.466697 * AU",
|
||||
"pitch": -90,
|
||||
"yaw": 0.35,
|
||||
"division": 30,
|
||||
"rotation": 5067014
|
||||
},
|
||||
"venus": {
|
||||
"texture": ["venus.jpg", [0.655, 0.38, 0.1, 1]],
|
||||
"radius": 6051.8,
|
||||
"z": "0.723327 * AU",
|
||||
"pitch": -90,
|
||||
"yaw": 177.36,
|
||||
"division": 30,
|
||||
"rotation": -20996798
|
||||
},
|
||||
"earth": {
|
||||
"texture": ["earth.jpg", "earth_medium.jpg", "earth_small.jpg", [0.11, 0.32, 0.43, 1]],
|
||||
"radius": 6378.1,
|
||||
"z": "AU",
|
||||
"pitch": -90,
|
||||
"yaw": 23.4,
|
||||
"roll": -90,
|
||||
"mass": 5.97219e+24,
|
||||
"rotation": 86400,
|
||||
"division": 250,
|
||||
"normal": "earth_normal.jpg",
|
||||
"atmosphere": {
|
||||
"cloud_texture": ["cloudmap.jpg", "cloudmap_small.jpg"],
|
||||
"diffuse_texture": "atmosphere_earth.png",
|
||||
"diffuse_size": 30
|
||||
},
|
||||
"orbit_distance": "AU",
|
||||
"satellites": {
|
||||
"moon": {
|
||||
"texture": ["moon.jpg", "moon_medium.jpg", "moon_small.jpg", [0.53, 0.53, 0.53, 1]],
|
||||
"radius": 1738.14,
|
||||
"distance": 38439,
|
||||
"sma": 384399,
|
||||
"division": 30,
|
||||
"eccentricity": 0.0549,
|
||||
"inclination": 5.145,
|
||||
"rotation": 0,
|
||||
"pitch": -90,
|
||||
"yaw": 6.68,
|
||||
"roll": -90
|
||||
},
|
||||
"iss": {
|
||||
"model": "satellites/iss.obj",
|
||||
"inclination": 51.65,
|
||||
"distance": 6800,
|
||||
"scale": 5
|
||||
},
|
||||
"hst": {
|
||||
"model": "satellites/hst.obj",
|
||||
"inclination": 28.5,
|
||||
"distance": 6937,
|
||||
"scale": 5
|
||||
}
|
||||
}
|
||||
},
|
||||
"mars": {
|
||||
"texture": ["mars.jpg", "mars_small.jpg", "mars_medium.jpg", [0.85, 0.47, 0.2, 1]],
|
||||
"radius": 3396.2,
|
||||
"z": "1.524 * AU",
|
||||
"pitch": -90,
|
||||
"yaw": 25.19,
|
||||
"mass": 6.4185e+23,
|
||||
"rotation": 88643,
|
||||
"orbit_distance": "AU",
|
||||
"division": 30,
|
||||
"satellites": {
|
||||
"phobos": {
|
||||
"distance": 9377,
|
||||
"inclination": 26.04,
|
||||
"eccentricity": 0.0151,
|
||||
"model": "satellites/phobos.obj"
|
||||
}
|
||||
}
|
||||
},
|
||||
"jupiter": {
|
||||
"texture": ["jupiter.jpg", "jupiter_medium.jpg", "jupiter_small.jpg", [0.65, 0.36, 0.19, 1]],
|
||||
"radius": 71492,
|
||||
"mass": 1.8986e+27,
|
||||
"z": "5.2 * AU",
|
||||
"pitch": -90,
|
||||
"yaw": 3.13,
|
||||
"comment": "satellites here are 3/10 the virtual distance than physical, and five times the size",
|
||||
"rotation": 35730,
|
||||
"orbit_distance": "3 * AU",
|
||||
"satellites": {
|
||||
"io": {
|
||||
"texture": ["moons/io.jpg", "moons/io_small.jpg", [0.62, 0.56, 0.35, 1]],
|
||||
"radius": "1821.3 * 5",
|
||||
"distance": 126510,
|
||||
"sma": 421700,
|
||||
"rotation": 0,
|
||||
"pitch": -90,
|
||||
"inclination": 2.21,
|
||||
"eccentricity": 0.0041
|
||||
},
|
||||
"europa": {
|
||||
"texture": ["moons/europa.jpg", "moons/europa_small.jpg", [0.77, 0.74, 0.65, 1]],
|
||||
"radius": "1560.8 * 5",
|
||||
"distance": 201270,
|
||||
"sma": 670900,
|
||||
"pitch": -90,
|
||||
"rotation": 0,
|
||||
"inclination": 2.71,
|
||||
"eccentricity": 0.009
|
||||
},
|
||||
"ganymede": {
|
||||
"texture": ["moons/ganymede.jpg", "moons/ganymede_small.jpg", [0.52, 0.47, 0.46, 1]],
|
||||
"radius": "2634.1 * 5",
|
||||
"distance": 321120,
|
||||
"sma": 1070400,
|
||||
"pitch": -90,
|
||||
"rotation": 0,
|
||||
"inclination": 2.51,
|
||||
"eccentricity": 0.0013
|
||||
},
|
||||
"callisto": {
|
||||
"texture": ["moons/callisto.jpg", "moons/callisto_small.jpg", [0.49, 0.43, 0.34, 1]],
|
||||
"radius": "2410.3 * 5",
|
||||
"distance": 564810,
|
||||
"sma": 1882700,
|
||||
"pitch": -90,
|
||||
"rotation": 0,
|
||||
"inclination": 0.192,
|
||||
"eccentricity": 0.0074
|
||||
}
|
||||
}
|
||||
},
|
||||
"saturn": {
|
||||
"texture": ["saturn.jpg", "saturn_medium.jpg", "saturn_small.jpg", [0.9, 0.8, 0.64, 1]],
|
||||
"radius": 60268,
|
||||
"mass": 5.6846e+26,
|
||||
"z": "9.58 * AU",
|
||||
"pitch": -90,
|
||||
"yaw": 26.73,
|
||||
"rotation": 38052,
|
||||
"ring": {
|
||||
"texture": "ring_saturn.png",
|
||||
"distance": 1169,
|
||||
"size": 2247
|
||||
},
|
||||
"orbit_distance": "4 * AU",
|
||||
"satellites": {
|
||||
"titan": {
|
||||
"texture": ["moons/titan.jpg", "moons/titan_small.jpg", [0.52, 0.39, 0.23, 1]],
|
||||
"radius": "2576 * 10",
|
||||
"distance": "1221870 / 3 + 200000",
|
||||
"sma": 1221870,
|
||||
"pitch": -90,
|
||||
"rotation": 0,
|
||||
"inclination": 27.07854,
|
||||
"eccentricity": 0.0288
|
||||
},
|
||||
"rhea": {
|
||||
"texture": ["moons/rhea.jpg", "moons/rhea_small.jpg", [0.62, 0.60, 0.59, 1]],
|
||||
"radius": "763.8 * 10",
|
||||
"distance": "527108 / 3 + 200000",
|
||||
"sma": 527108,
|
||||
"pitch": -90,
|
||||
"rotation": 0,
|
||||
"inclination": 27.075,
|
||||
"eccentricity": 0.0012583
|
||||
},
|
||||
"iapetus": {
|
||||
"texture": ["moons/iapetus.jpg", "moons/iapetus_small.jpg", [0.62, 0.60, 0.59, 1]],
|
||||
"radius": "734.5 * 10",
|
||||
"distance": "3560820 / 3 + 200000",
|
||||
"sma": 3560820,
|
||||
"pitch": -90,
|
||||
"rotation": 0,
|
||||
"inclination": 17.28,
|
||||
"eccentricity": 0.0286125
|
||||
},
|
||||
"dione": {
|
||||
"texture": ["moons/dione.jpg", "moons/dione_small.jpg", [0.46, 0.46, 0.46, 1]],
|
||||
"radius": "561.4 * 10",
|
||||
"distance": "377396 / 3 + 200000",
|
||||
"sma": 377396,
|
||||
"pitch": -90,
|
||||
"rotation": 0,
|
||||
"inclination": 26.711,
|
||||
"eccentricity": 0.0022
|
||||
},
|
||||
"tethys": {
|
||||
"texture": ["moons/tethys.jpg", "moons/tethys_small.jpg", [0.68, 0.68, 0.66, 1]],
|
||||
"radius": "531.1 * 10",
|
||||
"distance": "294619 / 3 + 200000",
|
||||
"sma": 294619,
|
||||
"pitch": -90,
|
||||
"rotation": 0,
|
||||
"inclination": 25.61,
|
||||
"eccentricity": 0.0001
|
||||
},
|
||||
"enceladus": {
|
||||
"texture": ["moons/enceladus.jpg", "moons/enceladus_small.jpg", [0.74, 0.74, 0.74, 1]],
|
||||
"radius": "252.1 * 10",
|
||||
"distance": "237948 / 3 + 200000",
|
||||
"sma": 237948,
|
||||
"pitch": -90,
|
||||
"rotation": 0,
|
||||
"inclination": 26.711,
|
||||
"eccentricity": 0.0047
|
||||
},
|
||||
"mimas": {
|
||||
"texture": ["moons/mimas.jpg", "moons/mimas_small.jpg", [0.47, 0.47, 0.47, 1]],
|
||||
"radius": "198.2 * 10",
|
||||
"distance": "181902 / 3 + 200000",
|
||||
"sma": 181902,
|
||||
"pitch": -90,
|
||||
"rotation": 0,
|
||||
"inclination": 28.304,
|
||||
"eccentricity": 0.0196
|
||||
},
|
||||
"cassini": {
|
||||
"model": "satellites/cassini.obj",
|
||||
"inclination": 51.65,
|
||||
"eccentricity": 0.5,
|
||||
"distance": "1221870 / 3 + 200000",
|
||||
"sma": 1221870,
|
||||
"scale": 100
|
||||
}
|
||||
}
|
||||
},
|
||||
"uranus": {
|
||||
"texture": ["uranus.jpg", [0, 0.53, 0.84, 1]],
|
||||
"radius": 25559,
|
||||
"mass": 8.6810e+25,
|
||||
"z": "19.23 * AU",
|
||||
"pitch": 7.77,
|
||||
"rotation": -62064,
|
||||
"orbit_distance": "6 * AU",
|
||||
"ring": {
|
||||
"texture": "ring_uranus.png",
|
||||
"pitch": 0,
|
||||
"yaw": 0,
|
||||
"roll": 90,
|
||||
"distance": 421,
|
||||
"size": 781
|
||||
}
|
||||
},
|
||||
"neptune": {
|
||||
"texture": ["neptune.jpg", [0.31, 0.49, 0.59, 1]],
|
||||
"radius": 24764,
|
||||
"mass": 1.0243e+26,
|
||||
"z": "30.5 * AU",
|
||||
"orbit_distance": "6 * AU",
|
||||
"rotation": 57996,
|
||||
"pitch": -90,
|
||||
"yaw": 28.32
|
||||
},
|
||||
"sky": {
|
||||
"texture": "sky.jpg",
|
||||
"rotation": 0,
|
||||
"optional": true,
|
||||
"lighting": false,
|
||||
"radius": 305000000,
|
||||
"division": 30,
|
||||
"pitch": 90,
|
||||
"yaw": 30,
|
||||
"roll": 180,
|
||||
"delta": 0,
|
||||
"background": true
|
||||
}
|
||||
},
|
||||
"belts": {
|
||||
"main": {
|
||||
"model": ["asteroids/mainbelt.obj", "asteroids/mainbelt2.obj"],
|
||||
"radius": "2.362 * AU",
|
||||
"cross": 1000,
|
||||
"scale": 30,
|
||||
"count": 2048,
|
||||
"rotation": 114536500
|
||||
}
|
||||
},
|
||||
"start": {
|
||||
"z": "AU - 400",
|
||||
"yaw": 180
|
||||
}
|
||||
}
|
234
punyverse/world.py
Normal file
|
@ -0,0 +1,234 @@
|
|||
from collections import OrderedDict
|
||||
import os.path
|
||||
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
try:
|
||||
import simplejson as json
|
||||
except ImportError:
|
||||
raise SystemExit('No JSON module found')
|
||||
|
||||
try:
|
||||
from punyverse._model import model_list, load_model
|
||||
except ImportError:
|
||||
from punyverse.model import model_list, load_model
|
||||
|
||||
from punyverse.glgeom import *
|
||||
from punyverse.entity import *
|
||||
from punyverse.texture import *
|
||||
from punyverse import texture
|
||||
|
||||
from math import pi, sqrt
|
||||
|
||||
G = 6.67384e-11 # Gravitation Constant
|
||||
|
||||
|
||||
def get_best_texture(info, optional=False, loader=load_texture):
|
||||
cheap = False
|
||||
skip = False
|
||||
texture = None
|
||||
if isinstance(info, list):
|
||||
for item in info:
|
||||
if isinstance(item, list):
|
||||
if len(item) == 4:
|
||||
cheap = True
|
||||
texture = item
|
||||
break
|
||||
continue
|
||||
try:
|
||||
texture = loader(item)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
break
|
||||
else:
|
||||
cheap = True
|
||||
texture = [1, 1, 1, 1]
|
||||
else:
|
||||
try:
|
||||
texture = loader(info)
|
||||
except ValueError:
|
||||
if optional:
|
||||
skip = True
|
||||
else:
|
||||
cheap = True
|
||||
texture = [1, 1, 1, 1]
|
||||
return cheap, skip, texture
|
||||
|
||||
|
||||
def load_world(file):
|
||||
with open(os.path.join(os.path.dirname(__file__), file)) as f:
|
||||
root = json.load(f, object_pairs_hook=OrderedDict)
|
||||
|
||||
world = World()
|
||||
au = root.get('au', 2000)
|
||||
e = lambda x: eval(str(x), {'__builtins__': None}, {'AU': au, 'TEXTURE': texture.max_texture})
|
||||
tick = root.get('tick', 4320) # How many second is a tick?
|
||||
length = root.get('length', 4320) # Satellite distance is in km, divide by this gets in world units
|
||||
world.tick_length = tick
|
||||
|
||||
if 'start' in root:
|
||||
info = root['start']
|
||||
x = e(info.get('x', 0))
|
||||
y = e(info.get('y', 0))
|
||||
z = e(info.get('z', 0))
|
||||
pitch = e(info.get('pitch', 0))
|
||||
yaw = e(info.get('yaw', 0))
|
||||
roll = e(info.get('roll', 0))
|
||||
world.start = (x, y, z)
|
||||
world.direction = (pitch, yaw, roll)
|
||||
|
||||
def body(name, info, parent=None):
|
||||
lighting = info.get('lighting', True)
|
||||
x = e(info.get('x', 0))
|
||||
y = e(info.get('y', 0))
|
||||
z = e(info.get('z', 0))
|
||||
pitch = e(info.get('pitch', 0))
|
||||
yaw = e(info.get('yaw', 0))
|
||||
roll = e(info.get('roll', 0))
|
||||
rotation = e(info.get('rotation', 86400))
|
||||
radius = e(info.get('radius', length)) / length
|
||||
background = info.get('background', False)
|
||||
orbit_distance = e(info.get('orbit_distance', au))
|
||||
division = info.get('division', max(min(int(radius / 8), 60), 10))
|
||||
|
||||
if 'texture' in info:
|
||||
cheap, skip, texture = get_best_texture(info['texture'], optional=info.get('optional', False))
|
||||
if skip:
|
||||
return
|
||||
if cheap:
|
||||
object_id = compile(colourball, radius, division, division, texture)
|
||||
else:
|
||||
if 'normal' in info:
|
||||
object_id = compile(normal_sphere, radius, division, texture, info['normal'], lighting=lighting)
|
||||
else:
|
||||
object_id = compile(sphere, radius, division, division, texture, lighting=lighting)
|
||||
elif 'model' in info:
|
||||
scale = info.get('scale', 1)
|
||||
object_id = model_list(load_model(info['model']), info.get('sx', scale), info.get('sy', scale),
|
||||
info.get('sz', scale), (0, 0, 0))
|
||||
else:
|
||||
print 'Nothing to load for %s.' % name
|
||||
return
|
||||
|
||||
params = {'world': world, 'orbit_distance': orbit_distance}
|
||||
if parent is None:
|
||||
type = Body
|
||||
else:
|
||||
x, y, z = parent.location
|
||||
distance = e(info.get('distance', 100)) # Semi-major axis when actually displayed in virtual space
|
||||
sma = e(info.get('sma', distance)) # Semi-major axis used to calculate orbital speed
|
||||
if hasattr(parent, 'mass') and parent.mass is not None:
|
||||
period = 2 * pi * sqrt((sma * 1000) ** 3 / (G * parent.mass))
|
||||
speed = 360 / (period + .0)
|
||||
if not rotation: # Rotation = 0 assumes tidal lock
|
||||
rotation = period
|
||||
else:
|
||||
speed = info.get('orbit_speed', 1)
|
||||
type = Satellite
|
||||
params.update(parent=parent, orbit_speed=speed,
|
||||
distance=distance / length, eccentricity=info.get('eccentricity', 0),
|
||||
inclination=info.get('inclination', 0), longitude=info.get('longitude', 0),
|
||||
argument=info.get('argument', 0))
|
||||
|
||||
if 'mass' in info:
|
||||
params['mass'] = info['mass']
|
||||
|
||||
atmosphere_id = 0
|
||||
cloudmap_id = 0
|
||||
corona_id = 0
|
||||
if 'atmosphere' in info:
|
||||
atmosphere_data = info['atmosphere']
|
||||
atm_size = e(atmosphere_data.get('diffuse_size', None))
|
||||
atm_texture = atmosphere_data.get('diffuse_texture', None)
|
||||
cloud_texture = atmosphere_data.get('cloud_texture', None)
|
||||
corona_texture = atmosphere_data.get('corona_texture', None)
|
||||
if cloud_texture is not None:
|
||||
cheap, _, cloud_texture = get_best_texture(cloud_texture, loader=load_clouds)
|
||||
if not cheap:
|
||||
cloudmap_id = compile(sphere, radius + 2, division, division, cloud_texture,
|
||||
lighting=False)
|
||||
if corona_texture is not None:
|
||||
cheap, _, corona = get_best_texture(corona_texture)
|
||||
if not cheap:
|
||||
corona_size = atmosphere_data.get('corona_size', radius / 2)
|
||||
corona_division = atmosphere_data.get('corona_division', 100)
|
||||
corona_ratio = atmosphere_data.get('corona_ratio', 0.5)
|
||||
corona_id = compile(flare, radius, radius + corona_size, corona_division,
|
||||
corona_ratio, corona)
|
||||
|
||||
if atm_texture is not None:
|
||||
cheap, _, atm_texture = get_best_texture(atm_texture)
|
||||
if not cheap:
|
||||
atmosphere_id = compile(disk, radius, radius + atm_size, 30, atm_texture)
|
||||
|
||||
theta = 360 / (rotation + .0) if rotation else 0
|
||||
object = type(object_id, (x, y, z), (pitch, yaw, roll), rotation_angle=theta,
|
||||
atmosphere=atmosphere_id, cloudmap=cloudmap_id, background=background,
|
||||
corona=corona_id, **params)
|
||||
world.tracker.append(object)
|
||||
|
||||
if 'ring' in info:
|
||||
ring_data = info['ring']
|
||||
texture = ring_data.get('texture', None)
|
||||
distance = e(ring_data.get('distance', radius * 1.2))
|
||||
size = e(ring_data.get('size', radius / 2))
|
||||
pitch = e(ring_data.get('pitch', pitch))
|
||||
yaw = e(ring_data.get('yaw', yaw))
|
||||
roll = e(ring_data.get('roll', roll))
|
||||
|
||||
cheap, _, texture = get_best_texture(texture)
|
||||
if not cheap:
|
||||
world.tracker.append(
|
||||
type(compile(disk, distance, distance + size, 30, texture), (x, y, z),
|
||||
(pitch, yaw, roll), **params))
|
||||
|
||||
for satellite, info in info.get('satellites', {}).iteritems():
|
||||
print 'Loading %s, satellite of %s.' % (satellite, name)
|
||||
body(satellite, info, object)
|
||||
|
||||
for planet, info in root['bodies'].iteritems():
|
||||
print 'Loading %s.' % planet
|
||||
body(planet, info)
|
||||
|
||||
for name, info in root['belts'].iteritems():
|
||||
print 'Loading %s.' % name
|
||||
x = e(info.get('x', 0))
|
||||
y = e(info.get('y', 0))
|
||||
z = e(info.get('z', 0))
|
||||
radius = e(info.get('radius', 0))
|
||||
cross = e(info.get('cross', 0))
|
||||
count = int(e(info.get('count', 0)))
|
||||
scale = info.get('scale', 1)
|
||||
longitude = info.get('longitude', 0)
|
||||
inclination = info.get('inclination', 0)
|
||||
argument = info.get('argument', 0)
|
||||
rotation = info.get('period', 31536000)
|
||||
theta = 360 / (rotation + .0) if rotation else 0
|
||||
|
||||
models = info['model']
|
||||
if not isinstance(models, list):
|
||||
models = [models]
|
||||
objects = []
|
||||
for model in models:
|
||||
objects.append(model_list(load_model(model), info.get('sx', scale), info.get('sy', scale),
|
||||
info.get('sz', scale), (0, 0, 0)))
|
||||
|
||||
world.tracker.append(Belt(compile(belt, radius, cross, objects, count),
|
||||
(x, y, z), (inclination, longitude, argument),
|
||||
rotation_angle=theta, world=world))
|
||||
|
||||
return world
|
||||
|
||||
|
||||
class World(object):
|
||||
def __init__(self):
|
||||
self.tracker = []
|
||||
self.start = (0, 0, 0)
|
||||
self.direction = (0, 0, 0)
|
||||
self.x = None
|
||||
self.y = None
|
||||
self.z = None
|
||||
self.tick_length = 1
|
||||
self.tick = 0
|
128
small_images.py
Normal file
|
@ -0,0 +1,128 @@
|
|||
import sys
|
||||
import os
|
||||
|
||||
from pyglet.gl import GLint, glGetIntegerv, GL_MAX_TEXTURE_SIZE
|
||||
from ctypes import byref
|
||||
|
||||
buf = GLint()
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, byref(buf))
|
||||
max_texture = buf.value
|
||||
|
||||
del byref, GLint, glGetIntegerv, GL_MAX_TEXTURE_SIZE, buf
|
||||
|
||||
def resize(width, height, target):
|
||||
factor = (target + .0) / max(width, height)
|
||||
return int(width * factor), int(height * factor)
|
||||
|
||||
def fits(width, height):
|
||||
return width < max_texture and height < max_texture
|
||||
|
||||
def make_name(image, suffix):
|
||||
name, ext = os.path.splitext(image)
|
||||
return '%s_%s%s' % (name, suffix, ext)
|
||||
|
||||
try:
|
||||
import pgmagick
|
||||
|
||||
def get_image(image):
|
||||
return pgmagick.Image(image)
|
||||
|
||||
def get_size(image):
|
||||
size = image.size()
|
||||
return size.width(), size.height()
|
||||
|
||||
def scale(image, width, height):
|
||||
image.filterType(pgmagick.FilterTypes.LanczosFilter)
|
||||
image.scale(pgmagick.Geometry(width, height))
|
||||
return image
|
||||
|
||||
def save(image, file):
|
||||
image.write(file)
|
||||
except ImportError:
|
||||
import Image
|
||||
|
||||
def get_image(image):
|
||||
return Image.open(image)
|
||||
|
||||
def get_size(image):
|
||||
return image.size
|
||||
|
||||
def scale(image, width, height):
|
||||
original_width, original_height = image.size
|
||||
if width * 3 < original_width and height * 3 < original_height:
|
||||
image = image.resize((width * 2, height * 2))
|
||||
return image.resize((width, height), Image.ANTIALIAS)
|
||||
|
||||
def save(image, file):
|
||||
image.save(file)
|
||||
|
||||
def shrink(file):
|
||||
image = get_image(file)
|
||||
width, height = get_size(image)
|
||||
if fits(width, height):
|
||||
print 'no need'
|
||||
return
|
||||
width, height = resize(width, height, 2048)
|
||||
if fits(width, height):
|
||||
size = 'medium'
|
||||
else:
|
||||
width, height = resize(width, height, 1024) # 1024 is minimum
|
||||
size = 'small'
|
||||
print 'size %s, %dx%d...' % (size, width, height),
|
||||
name = make_name(file, size)
|
||||
if not os.path.exists(name):
|
||||
image = scale(image, width, height)
|
||||
print 'saved to:', os.path.basename(name)
|
||||
save(image, name)
|
||||
else:
|
||||
print 'alrady there'
|
||||
|
||||
textures = [
|
||||
'mercury.jpg',
|
||||
'earth.jpg',
|
||||
'moon.jpg',
|
||||
'mars.jpg',
|
||||
'jupiter.jpg',
|
||||
'saturn.jpg',
|
||||
'moons/io.jpg',
|
||||
'moons/europa.jpg',
|
||||
'moons/ganymede.jpg',
|
||||
'moons/callisto.jpg',
|
||||
'moons/titan.jpg',
|
||||
'moons/rhea.jpg',
|
||||
'moons/iapetus.jpg',
|
||||
'moons/dione.jpg',
|
||||
'moons/tethys.jpg',
|
||||
'moons/enceladus.jpg',
|
||||
'moons/mimas.jpg',
|
||||
]
|
||||
|
||||
def frozen():
|
||||
import imp
|
||||
return (hasattr(sys, 'frozen') or # new py2exe
|
||||
hasattr(sys, 'importers') # old py2exe
|
||||
or imp.is_frozen('__main__')) # tools/freeze
|
||||
|
||||
def get_main_dir():
|
||||
if frozen():
|
||||
return os.path.dirname(sys.executable)
|
||||
return os.path.dirname(__file__)
|
||||
|
||||
def main():
|
||||
punyverse = os.path.join(get_main_dir(), 'punyverse')
|
||||
try:
|
||||
with open(os.path.join(punyverse, 'assets', 'textures.txt')) as f:
|
||||
files = [i.strip() for i in f if not i.startswith('#') and i.strip()]
|
||||
except IOError:
|
||||
files = textures
|
||||
texture = os.path.join(punyverse, 'assets', 'textures')
|
||||
for file in files:
|
||||
print 'Resizing %s:' % file,
|
||||
file = os.path.join(texture, file.replace('/', os.sep))
|
||||
if os.path.exists(file):
|
||||
shrink(file)
|
||||
else:
|
||||
print 'exists not'
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|