Compare commits
No commits in common. "master" and "v0.4" have entirely different histories.
17
.gitignore
vendored
|
@ -41,12 +41,10 @@ punyverse/*.c
|
|||
*.html
|
||||
*.exp
|
||||
*.lib
|
||||
/punyverse/assets/textures/*_large.*
|
||||
/punyverse/assets/textures/*_medium.*
|
||||
/punyverse/assets/textures/*_small.*
|
||||
/punyverse/assets/textures/*/*_large.*
|
||||
/punyverse/assets/textures/*/*_medium.*
|
||||
/punyverse/assets/textures/*/*_small.*
|
||||
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
|
||||
|
@ -59,10 +57,3 @@ library.zip
|
|||
/env
|
||||
/env2
|
||||
/env3
|
||||
|
||||
# Our special launcher
|
||||
!/punyverse/launcher.c
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
._.DS_Store
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
branches:
|
||||
only:
|
||||
- master
|
||||
- /^v\d+\./
|
||||
matrix:
|
||||
include:
|
||||
- sudo: required
|
||||
|
|
|
@ -3,7 +3,5 @@ include LICENSE
|
|||
include README.md
|
||||
include punyverse/world.json
|
||||
graft punyverse/assets
|
||||
include punyverse/shaders/*.glsl
|
||||
include punyverse/*.c
|
||||
include punyverse/*.h
|
||||
exclude punyverse/*.pyx
|
||||
|
|
33
README.md
|
@ -1,40 +1,17 @@
|
|||
# punyverse [](https://travis-ci.org/quantum5/punyverse) [](https://ci.appveyor.com/project/quantum5/punyverse) [](https://pypi.org/project/punyverse/) [](https://pypi.org/project/punyverse/) [](https://pypi.org/project/punyverse/)
|
||||
# punyverse [](https://travis-ci.org/quantum5/punyverse) [](https://ci.appveyor.com/project/quantum5/punyverse) [](https://pypi.org/project/punyverse/) [](https://pypi.org/project/punyverse/) [](https://pypi.org/project/punyverse/)
|
||||
|
||||
Python simulator of a puny universe. (How many words can I stick into one?)
|
||||
|
||||

|
||||

|
||||
|
||||
## Installation
|
||||
|
||||
To install, run `pip install punyverse`.
|
||||
|
||||
If you are on Windows, run `punyverse_make_launcher`. This should create special launchers that runs `punyverse` on
|
||||
your dedicated graphics card, should it exist.
|
||||
|
||||
Your graphics card might not support some of the larger textures used by `punyverse`, and so startup might fail.
|
||||
To solve this problem, run `punyverse_small_images`. It will do nothing if your graphics card supports all the
|
||||
textures, so when in doubt, run `punyverse_small_images` after installation.
|
||||
|
||||
Then, run `punyverse` to launch the simulator, or `punyversew` to launch without the console.
|
||||
|
||||
### Summary
|
||||
### A Note on Textures
|
||||
|
||||
```bash
|
||||
pip install punyverse
|
||||
punyverse_make_launcher
|
||||
punyverse_small_images
|
||||
# Installation finished. Run:
|
||||
punyverse
|
||||
```
|
||||
If your graphics card doesn't support the massive texture sizes this module comes with, you can shrink them.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If `punyverse` does not work, try upgrading your graphics card drivers.
|
||||
|
||||
If your graphics card does not appear to support OpenGL 3.3, then you cannot run the latest version of `punyverse`.
|
||||
You can try `pip install -U punyverse==0.5` to install the last version of `punyverse` to support legacy devices.
|
||||
You can download the wheels manually from [the PyPI page](https://pypi.org/project/punyverse/0.5/).
|
||||
|
||||
If the problem is unrelated to your graphics card, and it persists, try running punyverse under debug mode. To do this,
|
||||
run `punyverse` as `punyverse --debug`. Then paste the entirety of the output into a new GitHub issue
|
||||
[here](https://github.com/quantum5/punyverse/issues/new).
|
||||
To do this, run `punyverse_small_images`.
|
||||
|
|
|
@ -1,4 +1,55 @@
|
|||
from punyverse.main import main
|
||||
#!/usr/bin/python
|
||||
INITIAL_WIN_HEIGHT = 540
|
||||
INITIAL_WIN_WIDTH = 700
|
||||
WIN_TITLE = 'Punyverse'
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(prog='punyverse', description='''
|
||||
Python simulator of a puny universe.
|
||||
''')
|
||||
parser.add_argument('-d', '--high-depth', help='Use a larger depth buffer',
|
||||
const=32, default=24, dest='depth', nargs='?', type=int)
|
||||
parser.add_argument('-m', '--multisample', help='Use multisampled image, '
|
||||
'optional samples', const=2, default=0, nargs='?',
|
||||
type=int)
|
||||
parser.add_argument('-v', '--no-vsync', help='Disables vsync',
|
||||
action='store_false', dest='vsync')
|
||||
parser.add_argument('-n', '--normal', help='Enables the use of normal maps',
|
||||
action='store_true')
|
||||
args = parser.parse_args()
|
||||
|
||||
import pyglet
|
||||
pyglet.options['shadow_window'] = False
|
||||
|
||||
template = pyglet.gl.Config(depth_size=args.depth, double_buffer=True,
|
||||
sample_buffers=args.multisample > 1,
|
||||
samples=args.multisample)
|
||||
|
||||
platform = pyglet.window.get_platform()
|
||||
display = platform.get_default_display()
|
||||
screen = display.get_default_screen()
|
||||
try:
|
||||
config = screen.get_best_config(template)
|
||||
except pyglet.window.NoSuchConfigException:
|
||||
raise SystemExit('Graphics configuration not supported.')
|
||||
else:
|
||||
if hasattr(config, '_attribute_names'):
|
||||
print('OpenGL configuration:')
|
||||
for key in config._attribute_names:
|
||||
print(' %-17s %s' % (key + ':', getattr(config, key)))
|
||||
|
||||
world_options = {
|
||||
'normal': args.normal,
|
||||
}
|
||||
|
||||
from punyverse import game
|
||||
game.Applet(width=INITIAL_WIN_WIDTH, height=INITIAL_WIN_HEIGHT,
|
||||
caption=WIN_TITLE, resizable=True, vsync=args.vsync,
|
||||
config=config, world_options=world_options)
|
||||
pyglet.app.run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
main()
|
|
@ -1,4 +1,4 @@
|
|||
from punyverse.main import main
|
||||
from punyverse.__main__ import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
cdef extern from "glwrapper.h":
|
||||
IF UNAME_SYSNAME == "Windows":
|
||||
cdef extern from "windows.h":
|
||||
pass
|
||||
|
||||
cdef extern from "GL/gl.h":
|
||||
ctypedef unsigned int GLenum
|
||||
ctypedef unsigned char GLboolean
|
||||
ctypedef unsigned int GLbitfield
|
||||
|
@ -383,6 +387,74 @@ cdef extern from "glwrapper.h":
|
|||
void glDrawElements(GLenum mode, GLsizei count, GLenum type, GLvoid *indices) nogil
|
||||
void glInterleavedArrays(GLenum format, GLsizei stride, GLvoid *pointer) nogil
|
||||
|
||||
cdef extern from "stddef.h":
|
||||
ctypedef unsigned int wchar_t
|
||||
|
||||
cdef extern from "GL/glu.h":
|
||||
ctypedef struct GLUnurbs:
|
||||
pass
|
||||
ctypedef struct GLUquadric:
|
||||
pass
|
||||
ctypedef struct GLUtesselator:
|
||||
pass
|
||||
|
||||
ctypedef GLUnurbs GLUnurbsObj
|
||||
ctypedef GLUquadric GLUquadricObj
|
||||
ctypedef GLUtesselator GLUtesselatorObj
|
||||
ctypedef GLUtesselator GLUtriangulatorObj
|
||||
|
||||
void gluBeginCurve(GLUnurbs* nurb) nogil
|
||||
void gluBeginPolygon(GLUtesselator* tess) nogil
|
||||
void gluBeginSurface(GLUnurbs* nurb) nogil
|
||||
void gluBeginTrim(GLUnurbs* nurb) nogil
|
||||
GLint gluBuild1DMipmaps(GLenum target, GLint internalFormat, GLsizei width, GLenum format, GLenum type, void *data) nogil
|
||||
GLint gluBuild2DMipmaps(GLenum target, GLint internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, void *data) nogil
|
||||
void gluCylinder(GLUquadric* quad, GLdouble base, GLdouble top, GLdouble height, GLint slices, GLint stacks) nogil
|
||||
void gluDeleteNurbsRenderer(GLUnurbs* nurb) nogil
|
||||
void gluDeleteQuadric(GLUquadric* quad) nogil
|
||||
void gluDeleteTess(GLUtesselator* tess) nogil
|
||||
void gluDisk(GLUquadric* quad, GLdouble inner, GLdouble outer, GLint slices, GLint loops) nogil
|
||||
void gluEndCurve(GLUnurbs* nurb) nogil
|
||||
void gluEndPolygon(GLUtesselator* tess) nogil
|
||||
void gluEndSurface(GLUnurbs* nurb) nogil
|
||||
void gluEndTrim(GLUnurbs* nurb) nogil
|
||||
GLubyte * gluErrorString(GLenum error)
|
||||
wchar_t * gluErrorUnicodeStringEXT(GLenum error)
|
||||
void gluGetNurbsProperty(GLUnurbs* nurb, GLenum property, GLfloat* data) nogil
|
||||
GLubyte * gluGetString(GLenum name)
|
||||
void gluGetTessProperty(GLUtesselator* tess, GLenum which, GLdouble* data) nogil
|
||||
void gluLoadSamplingMatrices(GLUnurbs* nurb, GLfloat *model, GLfloat *perspective, GLint *view) nogil
|
||||
void gluLookAt(GLdouble eyeX, GLdouble eyeY, GLdouble eyeZ, GLdouble centerX, GLdouble centerY, GLdouble centerZ, GLdouble upX, GLdouble upY, GLdouble upZ) nogil
|
||||
GLUnurbs* gluNewNurbsRenderer()
|
||||
GLUquadric* gluNewQuadric()
|
||||
GLUtesselator* gluNewTess()
|
||||
void gluNextContour(GLUtesselator* tess, GLenum type) nogil
|
||||
void gluNurbsCurve(GLUnurbs* nurb, GLint knotCount, GLfloat *knots, GLint stride, GLfloat *control, GLint order, GLenum type) nogil
|
||||
void gluNurbsProperty(GLUnurbs* nurb, GLenum property, GLfloat value) nogil
|
||||
void gluNurbsSurface(GLUnurbs* nurb, GLint sKnotCount, GLfloat* sKnots, GLint tKnotCount, GLfloat* tKnots, GLint sStride, GLint tStride, GLfloat* control, GLint sOrder, GLint tOrder, GLenum type) nogil
|
||||
void gluOrtho2D(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top) nogil
|
||||
void gluPartialDisk(GLUquadric* quad, GLdouble inner, GLdouble outer, GLint slices, GLint loops, GLdouble start, GLdouble sweep) nogil
|
||||
void gluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar) nogil
|
||||
void gluPickMatrix(GLdouble x, GLdouble y, GLdouble delX, GLdouble delY, GLint *viewport) nogil
|
||||
GLint gluProject(GLdouble objX, GLdouble objY, GLdouble objZ, GLdouble *model, GLdouble *proj, GLint *view, GLdouble* winX, GLdouble* winY, GLdouble* winZ)
|
||||
void gluPwlCurve(GLUnurbs* nurb, GLint count, GLfloat* data, GLint stride, GLenum type) nogil
|
||||
void gluQuadricDrawStyle(GLUquadric* quad, GLenum draw) nogil
|
||||
void gluQuadricNormals(GLUquadric* quad, GLenum normal) nogil
|
||||
void gluQuadricOrientation(GLUquadric* quad, GLenum orientation) nogil
|
||||
void gluQuadricTexture(GLUquadric* quad, GLboolean texture) nogil
|
||||
GLint gluScaleImage(GLenum format, GLsizei wIn, GLsizei hIn, GLenum typeIn, void *dataIn, GLsizei wOut, GLsizei hOut, GLenum typeOut, GLvoid* dataOut) nogil
|
||||
void gluSphere(GLUquadric* quad, GLdouble radius, GLint slices, GLint stacks) nogil
|
||||
void gluTessBeginContour(GLUtesselator* tess) nogil
|
||||
void gluTessBeginPolygon(GLUtesselator* tess, GLvoid* data) nogil
|
||||
void gluTessEndContour(GLUtesselator* tess) nogil
|
||||
void gluTessEndPolygon(GLUtesselator* tess) nogil
|
||||
void gluTessNormal(GLUtesselator* tess, GLdouble valueX, GLdouble valueY, GLdouble valueZ) nogil
|
||||
void gluTessProperty(GLUtesselator* tess, GLenum which, GLdouble data) nogil
|
||||
void gluTessVertex(GLUtesselator* tess, GLdouble *location, GLvoid* data) nogil
|
||||
GLint gluUnProject(GLdouble winX, GLdouble winY, GLdouble winZ, GLdouble *model, GLdouble *proj, GLint *view, GLdouble* objX, GLdouble* objY, GLdouble* objZ)
|
||||
GLint gluUnProject4(GLdouble winX, GLdouble winY, GLdouble winZ, GLdouble clipW, GLdouble *model, GLdouble *proj, GLint *view, GLdouble nearVal, GLdouble farVal, GLdouble* objX, GLdouble* objY, GLdouble* objZ, GLdouble* objW)
|
||||
|
||||
|
||||
cdef enum:
|
||||
GL_FALSE = 0x0
|
||||
GL_TRUE = 0x1
|
||||
|
@ -985,3 +1057,153 @@ cdef enum:
|
|||
GL_CLIENT_VERTEX_ARRAY_BIT = 0x00000002
|
||||
GL_ALL_CLIENT_ATTRIB_BITS = 0xFFFFFFFF
|
||||
GL_CLIENT_ALL_ATTRIB_BITS = 0xFFFFFFFF
|
||||
|
||||
# Boolean
|
||||
GLU_FALSE = 0
|
||||
GLU_TRUE = 1
|
||||
|
||||
# StringName
|
||||
GLU_VERSION = 100800
|
||||
GLU_EXTENSIONS = 100801
|
||||
|
||||
# ErrorCode
|
||||
GLU_INVALID_ENUM = 100900
|
||||
GLU_INVALID_VALUE = 100901
|
||||
GLU_OUT_OF_MEMORY = 100902
|
||||
GLU_INVALID_OPERATION = 100904
|
||||
|
||||
# NurbsDisplay
|
||||
# GLU_FILL
|
||||
GLU_OUTLINE_POLYGON = 100240
|
||||
GLU_OUTLINE_PATCH = 100241
|
||||
|
||||
# NurbsError
|
||||
GLU_NURBS_ERROR1 = 100251
|
||||
GLU_NURBS_ERROR2 = 100252
|
||||
GLU_NURBS_ERROR3 = 100253
|
||||
GLU_NURBS_ERROR4 = 100254
|
||||
GLU_NURBS_ERROR5 = 100255
|
||||
GLU_NURBS_ERROR6 = 100256
|
||||
GLU_NURBS_ERROR7 = 100257
|
||||
GLU_NURBS_ERROR8 = 100258
|
||||
GLU_NURBS_ERROR9 = 100259
|
||||
GLU_NURBS_ERROR10 = 100260
|
||||
GLU_NURBS_ERROR11 = 100261
|
||||
GLU_NURBS_ERROR12 = 100262
|
||||
GLU_NURBS_ERROR13 = 100263
|
||||
GLU_NURBS_ERROR14 = 100264
|
||||
GLU_NURBS_ERROR15 = 100265
|
||||
GLU_NURBS_ERROR16 = 100266
|
||||
GLU_NURBS_ERROR17 = 100267
|
||||
GLU_NURBS_ERROR18 = 100268
|
||||
GLU_NURBS_ERROR19 = 100269
|
||||
GLU_NURBS_ERROR20 = 100270
|
||||
GLU_NURBS_ERROR21 = 100271
|
||||
GLU_NURBS_ERROR22 = 100272
|
||||
GLU_NURBS_ERROR23 = 100273
|
||||
GLU_NURBS_ERROR24 = 100274
|
||||
GLU_NURBS_ERROR25 = 100275
|
||||
GLU_NURBS_ERROR26 = 100276
|
||||
GLU_NURBS_ERROR27 = 100277
|
||||
GLU_NURBS_ERROR28 = 100278
|
||||
GLU_NURBS_ERROR29 = 100279
|
||||
GLU_NURBS_ERROR30 = 100280
|
||||
GLU_NURBS_ERROR31 = 100281
|
||||
GLU_NURBS_ERROR32 = 100282
|
||||
GLU_NURBS_ERROR33 = 100283
|
||||
GLU_NURBS_ERROR34 = 100284
|
||||
GLU_NURBS_ERROR35 = 100285
|
||||
GLU_NURBS_ERROR36 = 100286
|
||||
GLU_NURBS_ERROR37 = 100287
|
||||
|
||||
# NurbsProperty
|
||||
GLU_AUTO_LOAD_MATRIX = 100200
|
||||
GLU_CULLING = 100201
|
||||
GLU_SAMPLING_TOLERANCE = 100203
|
||||
GLU_DISPLAY_MODE = 100204
|
||||
GLU_PARAMETRIC_TOLERANCE = 100202
|
||||
GLU_SAMPLING_METHOD = 100205
|
||||
GLU_U_STEP = 100206
|
||||
GLU_V_STEP = 100207
|
||||
|
||||
# NurbsSampling
|
||||
GLU_PATH_LENGTH = 100215
|
||||
GLU_PARAMETRIC_ERROR = 100216
|
||||
GLU_DOMAIN_DISTANCE = 100217
|
||||
|
||||
# NurbsTrim
|
||||
GLU_MAP1_TRIM_2 = 100210
|
||||
GLU_MAP1_TRIM_3 = 100211
|
||||
|
||||
# QuadricDrawStyle
|
||||
GLU_POINT = 100010
|
||||
GLU_LINE = 100011
|
||||
GLU_FILL = 100012
|
||||
GLU_SILHOUETTE = 100013
|
||||
|
||||
# QuadricCallback
|
||||
GLU_ERROR = 100103
|
||||
|
||||
# QuadricNormal
|
||||
GLU_SMOOTH = 100000
|
||||
GLU_FLAT = 100001
|
||||
GLU_NONE = 100002
|
||||
|
||||
# QuadricOrientation
|
||||
GLU_OUTSIDE = 100020
|
||||
GLU_INSIDE = 100021
|
||||
|
||||
# TessCallback
|
||||
GLU_TESS_BEGIN = 100100
|
||||
GLU_BEGIN = 100100
|
||||
GLU_TESS_VERTEX = 100101
|
||||
GLU_VERTEX = 100101
|
||||
GLU_TESS_END = 100102
|
||||
GLU_END = 100102
|
||||
GLU_TESS_ERROR = 100103
|
||||
GLU_TESS_EDGE_FLAG = 100104
|
||||
GLU_EDGE_FLAG = 100104
|
||||
GLU_TESS_COMBINE = 100105
|
||||
GLU_TESS_BEGIN_DATA = 100106
|
||||
GLU_TESS_VERTEX_DATA = 100107
|
||||
GLU_TESS_END_DATA = 100108
|
||||
GLU_TESS_ERROR_DATA = 100109
|
||||
GLU_TESS_EDGE_FLAG_DATA = 100110
|
||||
GLU_TESS_COMBINE_DATA = 100111
|
||||
|
||||
# TessContour
|
||||
GLU_CW = 100120
|
||||
GLU_CCW = 100121
|
||||
GLU_INTERIOR = 100122
|
||||
GLU_EXTERIOR = 100123
|
||||
GLU_UNKNOWN = 100124
|
||||
|
||||
# TessProperty
|
||||
GLU_TESS_WINDING_RULE = 100140
|
||||
GLU_TESS_BOUNDARY_ONLY = 100141
|
||||
GLU_TESS_TOLERANCE = 100142
|
||||
|
||||
# TessError
|
||||
GLU_TESS_ERROR1 = 100151
|
||||
GLU_TESS_ERROR2 = 100152
|
||||
GLU_TESS_ERROR3 = 100153
|
||||
GLU_TESS_ERROR4 = 100154
|
||||
GLU_TESS_ERROR5 = 100155
|
||||
GLU_TESS_ERROR6 = 100156
|
||||
GLU_TESS_ERROR7 = 100157
|
||||
GLU_TESS_ERROR8 = 100158
|
||||
GLU_TESS_MISSING_BEGIN_POLYGON = 100151
|
||||
GLU_TESS_MISSING_BEGIN_CONTOUR = 100152
|
||||
GLU_TESS_MISSING_END_POLYGON = 100153
|
||||
GLU_TESS_MISSING_END_CONTOUR = 100154
|
||||
GLU_TESS_COORD_TOO_LARGE = 100155
|
||||
GLU_TESS_NEED_COMBINE_CALLBACK = 100156
|
||||
|
||||
# TessWinding
|
||||
GLU_TESS_WINDING_ODD = 100130
|
||||
GLU_TESS_WINDING_NONZERO = 100131
|
||||
GLU_TESS_WINDING_POSITIVE = 100132
|
||||
GLU_TESS_WINDING_NEGATIVE = 100133
|
||||
GLU_TESS_WINDING_ABS_GEQ_TWO = 100134
|
||||
|
||||
cdef float GLU_TESS_MAX_COORD = 1.0e150
|
|
@ -1,4 +1,7 @@
|
|||
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
|
||||
|
@ -9,6 +12,60 @@ cdef extern from "Python.h":
|
|||
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()
|
||||
|
||||
|
||||
cpdef bytes bgr_to_rgb(bytes buffer, int width, int height, bint alpha=0):
|
||||
cdef int length = len(buffer)
|
||||
cdef int depth = length / (width * height)
|
||||
|
|
359
punyverse/_model.pyx
Normal file
|
@ -0,0 +1,359 @@
|
|||
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 unicode name, texture
|
||||
cdef public tuple Ka, Kd, Ks
|
||||
cdef public double shininess
|
||||
|
||||
def __init__(self, unicode name, unicode 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 unicode 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, unicode name=None):
|
||||
if name is None:
|
||||
self.name = unicode(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):
|
||||
name = words[1].decode('utf-8')
|
||||
material = Material(name)
|
||||
self.materials[name] = 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].decode('utf-8')
|
||||
|
||||
@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(b'/')
|
||||
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].decode('utf-8')))
|
||||
|
||||
cdef void use_material(self, list words):
|
||||
mat = words[1].decode('utf-8')
|
||||
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].decode('utf-8')
|
||||
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, lambda x: open(x, 'rb'))(file)
|
||||
with reader:
|
||||
for buf in reader:
|
||||
if not buf or buf.startswith((b'\r', b'\n', b'#')):
|
||||
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:
|
||||
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' if os.name == 'nt' else 'utf8')
|
||||
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
|
|
@ -1,26 +1,13 @@
|
|||
# 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
|
||||
|
||||
sun.jpg
|
||||
mercury.jpg
|
||||
venus.jpg
|
||||
earth.jpg
|
||||
earth_normal.jpg
|
||||
earth_emission.jpg
|
||||
earth_specular.jpg
|
||||
cloudmap.jpg
|
||||
moon.jpg
|
||||
mars.jpg
|
||||
jupiter.jpg
|
||||
saturn.jpg
|
||||
uranus.jpg
|
||||
neptune.jpg
|
||||
sky_px.jpg
|
||||
sky_py.jpg
|
||||
sky_pz.jpg
|
||||
sky_nx.jpg
|
||||
sky_ny.jpg
|
||||
sky_nz.jpg
|
||||
moons/io.jpg
|
||||
moons/europa.jpg
|
||||
moons/ganymede.jpg
|
||||
|
|
BIN
punyverse/assets/textures/atmosphere_earth.png
Normal file
After Width: | Height: | Size: 114 B |
Before Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 87 KiB |
Before Width: | Height: | Size: 98 KiB |
Before Width: | Height: | Size: 88 KiB |
Before Width: | Height: | Size: 100 KiB |
Before Width: | Height: | Size: 108 KiB |
Before Width: | Height: | Size: 328 KiB |
Before Width: | Height: | Size: 522 KiB After Width: | Height: | Size: 81 KiB |
Before Width: | Height: | Size: 90 KiB |
Before Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 98 B |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 9.6 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 281 KiB After Width: | Height: | Size: 133 KiB |
BIN
punyverse/assets/textures/sky.jpg
Normal file
After Width: | Height: | Size: 7.2 MiB |
Before Width: | Height: | Size: 1.4 MiB |
Before Width: | Height: | Size: 1.1 MiB |
Before Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 1.4 MiB |
Before Width: | Height: | Size: 1.1 MiB |
Before Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 84 KiB 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 |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 981 KiB After Width: | Height: | Size: 92 KiB |
|
@ -1,9 +1,4 @@
|
|||
from __future__ import division
|
||||
|
||||
from math import sin, cos, radians, hypot, tan
|
||||
|
||||
from punyverse.glgeom import Matrix4f
|
||||
from punyverse.utils import cached_property
|
||||
from math import sin, cos, radians
|
||||
|
||||
|
||||
class Camera(object):
|
||||
|
@ -15,23 +10,12 @@ class Camera(object):
|
|||
self.yaw = yaw
|
||||
self.roll = roll
|
||||
|
||||
self.fov = radians(45)
|
||||
self.aspect = 1
|
||||
self.znear = 1
|
||||
self.zfar = 3000000
|
||||
|
||||
self.speed = 0
|
||||
self.roll_left = False
|
||||
self.roll_right = False
|
||||
|
||||
def move(self, speed):
|
||||
dx, dy, dz = self.direction()
|
||||
self.x += dx * speed
|
||||
self.y += dy * speed
|
||||
self.z += dz * speed
|
||||
|
||||
self.view_matrix = None
|
||||
|
||||
def mouse_move(self, dx, dy):
|
||||
if self.pitch > 90 or self.pitch < -90:
|
||||
dx = -dx
|
||||
|
@ -48,8 +32,6 @@ class Camera(object):
|
|||
elif self.pitch > 180:
|
||||
self.pitch -= 360
|
||||
|
||||
self.view_matrix = None
|
||||
|
||||
def direction(self):
|
||||
m = cos(radians(self.pitch))
|
||||
|
||||
|
@ -57,33 +39,3 @@ class Camera(object):
|
|||
dx = cos(radians(self.yaw - 90)) * m
|
||||
dz = sin(radians(self.yaw - 90)) * m
|
||||
return dx, dy, dz
|
||||
|
||||
def update(self, dt, move):
|
||||
if self.roll_left:
|
||||
self.roll -= 4 * dt * 10
|
||||
self.view_matrix = None
|
||||
if self.roll_right:
|
||||
self.roll += 4 * dt * 10
|
||||
self.view_matrix = None
|
||||
if move:
|
||||
self.move(self.speed * 10 * dt)
|
||||
|
||||
def reset_roll(self):
|
||||
self.roll = 0
|
||||
self.view_matrix = None
|
||||
|
||||
def distance(self, x, y, z):
|
||||
return hypot(hypot(x - self.x, y - self.y), z - self.z)
|
||||
|
||||
@cached_property
|
||||
def view_matrix(self):
|
||||
return Matrix4f.from_angles((self.x, self.y, self.z), (self.pitch, self.yaw, self.roll), view=True)
|
||||
|
||||
def projection_matrix(self):
|
||||
scale_y = 1 / tan(self.fov / 2)
|
||||
scale_x = scale_y / self.aspect
|
||||
frustrum = self.znear - self.zfar
|
||||
return Matrix4f([scale_x, 0, 0, 0,
|
||||
0, scale_y, 0, 0,
|
||||
0, 0, (self.znear + self.zfar) / frustrum, -1,
|
||||
0, 0, (2 * self.znear * self.zfar) / frustrum, 0])
|
||||
|
|
|
@ -1,597 +1,147 @@
|
|||
import random
|
||||
from math import sqrt, pi
|
||||
from math import sqrt
|
||||
|
||||
from pyglet.gl import *
|
||||
# noinspection PyUnresolvedReferences
|
||||
from six.moves import range
|
||||
from pyglet.gl import *
|
||||
|
||||
from punyverse.glgeom import *
|
||||
from punyverse.model import load_model, WavefrontVBO
|
||||
from punyverse.orbit import KeplerOrbit
|
||||
from punyverse.texture import get_best_texture, load_alpha_mask, get_cube_map, load_texture_1d
|
||||
from punyverse.utils import cached_property
|
||||
|
||||
G = 6.67384e-11 # Gravitation Constant
|
||||
|
||||
|
||||
class Entity(object):
|
||||
background = False
|
||||
|
||||
def __init__(self, world, name, location, rotation=(0, 0, 0), direction=(0, 0, 0)):
|
||||
self.world = world
|
||||
self.name = name
|
||||
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
|
||||
|
||||
@cached_property
|
||||
def model_matrix(self):
|
||||
return Matrix4f.from_angles(self.location, self.rotation)
|
||||
|
||||
@cached_property
|
||||
def mv_matrix(self):
|
||||
return self.world.view_matrix() * self.model_matrix
|
||||
|
||||
@cached_property
|
||||
def mvp_matrix(self):
|
||||
return self.world.vp_matrix * self.model_matrix
|
||||
self.background = background
|
||||
|
||||
def update(self):
|
||||
self.model_matrix = None
|
||||
self.mv_matrix = None
|
||||
self.mvp_matrix = None
|
||||
x, y, z = self.location
|
||||
dx, dy, dz = self.direction
|
||||
self.location = x + dx, y + dy, z + dz
|
||||
|
||||
|
||||
def collides(self, x, y, z):
|
||||
return False
|
||||
|
||||
def draw(self, options):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class Asteroid(Entity):
|
||||
def __init__(self, world, model, location, direction):
|
||||
super(Asteroid, self).__init__(world, 'Asteroid', location, direction=direction)
|
||||
self.model = model
|
||||
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'
|
||||
# Increment all axis to 'spin'
|
||||
self.rotation = rx + 1, ry + 1, rz + 1
|
||||
|
||||
def draw(self, options):
|
||||
shader = self.world.activate_shader('model')
|
||||
shader.uniform_mat4('u_mvpMatrix', self.mvp_matrix)
|
||||
shader.uniform_mat4('u_mvMatrix', self.mv_matrix)
|
||||
shader.uniform_mat4('u_modelMatrix', self.model_matrix)
|
||||
self.model.draw(shader)
|
||||
|
||||
|
||||
class AsteroidManager(object):
|
||||
def __init__(self, world):
|
||||
self.world = world
|
||||
self.asteroids = []
|
||||
|
||||
def __bool__(self):
|
||||
return bool(self.asteroids)
|
||||
__nonzero__ = __bool__
|
||||
|
||||
def load(self, file):
|
||||
shader = self.world.activate_shader('model')
|
||||
self.asteroids.append(WavefrontVBO(load_model(file), shader, 5, 5, 5))
|
||||
|
||||
def new(self, location, direction):
|
||||
return Asteroid(self.world, random.choice(self.asteroids), location, direction)
|
||||
|
||||
|
||||
class Belt(Entity):
|
||||
def __init__(self, name, world, info):
|
||||
x = world.evaluate(info.get('x', 0))
|
||||
y = world.evaluate(info.get('y', 0))
|
||||
z = world.evaluate(info.get('z', 0))
|
||||
radius = world.evaluate(info.get('radius', 0))
|
||||
cross = world.evaluate(info.get('cross', 0))
|
||||
count = int(world.evaluate(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)
|
||||
models = info['model']
|
||||
self.rotation_angle = 360.0 / rotation if rotation else 0
|
||||
|
||||
shader = world.activate_shader('belt')
|
||||
if not isinstance(models, list):
|
||||
models = [models]
|
||||
|
||||
self.belt = BeltVBO(radius, cross, len(models), count)
|
||||
self.objects = [
|
||||
WavefrontVBO(load_model(model), shader, info.get('sx', scale),
|
||||
info.get('sy', scale), info.get('sz', scale))
|
||||
for model in models
|
||||
]
|
||||
|
||||
def callback():
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo)
|
||||
shader.vertex_attribute('a_translate', self.belt.location_size, self.belt.type, GL_FALSE,
|
||||
self.belt.stride, self.belt.location_offset, divisor=1)
|
||||
shader.vertex_attribute('a_scale', self.belt.scale_size, self.belt.type, GL_FALSE,
|
||||
self.belt.stride, self.belt.scale_offset, divisor=1)
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
||||
|
||||
for model, vbo, count in zip(self.objects, self.belt.vbo, self.belt.sizes):
|
||||
model.additional_attributes(callback)
|
||||
|
||||
super(Belt, self).__init__(world, name, (x, y, z), (inclination, longitude, argument))
|
||||
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
|
||||
|
||||
def draw(self, options):
|
||||
shader = self.world.activate_shader('belt')
|
||||
shader.uniform_mat4('u_mvpMatrix', self.mvp_matrix)
|
||||
shader.uniform_mat4('u_mvMatrix', self.mv_matrix)
|
||||
shader.uniform_mat4('u_modelMatrix', self.model_matrix)
|
||||
|
||||
for object, vbo, count in zip(self.objects, self.belt.vbo, self.belt.sizes):
|
||||
object.draw(shader, instances=count)
|
||||
|
||||
|
||||
class Sky(Entity):
|
||||
background = True
|
||||
|
||||
def __init__(self, world, info, callback=None):
|
||||
pitch = world.evaluate(info.get('pitch', 0))
|
||||
yaw = world.evaluate(info.get('yaw', 0))
|
||||
roll = world.evaluate(info.get('roll', 0))
|
||||
|
||||
super(Sky, self).__init__(world, 'Sky', (0, 0, 0), [pitch, yaw, roll])
|
||||
|
||||
self.texture = get_best_texture(info['texture'], loader=get_cube_map, callback=callback)
|
||||
self.constellation = get_cube_map(info['constellation'])
|
||||
self.cube = Cube()
|
||||
self.vao = VAO()
|
||||
|
||||
shader = self.world.activate_shader('sky')
|
||||
with self.vao:
|
||||
glBindBuffer(GL_ARRAY_BUFFER, self.cube.vbo)
|
||||
shader.vertex_attribute('a_direction', self.cube.direction_size, self.cube.type, GL_FALSE,
|
||||
self.cube.stride, self.cube.direction_offset)
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
||||
|
||||
def draw(self, options):
|
||||
cam = self.world.cam
|
||||
shader = self.world.activate_shader('sky')
|
||||
shader.uniform_mat4('u_mvpMatrix', self.world.projection_matrix() *
|
||||
Matrix4f.from_angles(rotation=(cam.pitch, cam.yaw, cam.roll)) *
|
||||
Matrix4f.from_angles(rotation=self.rotation))
|
||||
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, self.texture)
|
||||
shader.uniform_texture('u_skysphere', 0)
|
||||
|
||||
glActiveTexture(GL_TEXTURE1)
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, self.constellation)
|
||||
shader.uniform_texture('u_constellation', 1)
|
||||
|
||||
shader.uniform_bool('u_lines', options.constellations)
|
||||
|
||||
with self.vao:
|
||||
glDrawArrays(GL_TRIANGLES, 0, self.cube.vertex_count)
|
||||
|
||||
glActiveTexture(GL_TEXTURE0)
|
||||
|
||||
|
||||
class Body(Entity):
|
||||
def __init__(self, name, world, info, parent=None):
|
||||
self.parent = parent
|
||||
self.satellites = []
|
||||
|
||||
x = world.evaluate(info.get('x', 0))
|
||||
y = world.evaluate(info.get('y', 0))
|
||||
z = world.evaluate(info.get('z', 0))
|
||||
pitch = world.evaluate(info.get('pitch', 0))
|
||||
yaw = world.evaluate(info.get('yaw', 0))
|
||||
roll = world.evaluate(info.get('roll', 0))
|
||||
rotation = world.evaluate(info.get('rotation', 86400))
|
||||
|
||||
self.mass = info.get('mass')
|
||||
|
||||
orbit_distance = float(world.evaluate(info.get('orbit_distance', world.au)))
|
||||
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.radius = kwargs.pop('radius', 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__(world, name, (x, y, z), (pitch, yaw, roll))
|
||||
self.initial_roll = roll
|
||||
|
||||
self.orbit = None
|
||||
self.orbit_speed = None
|
||||
|
||||
if parent:
|
||||
# Semi-major axis when actually displayed in virtual space
|
||||
distance = world.evaluate(info.get('distance', 100))
|
||||
# Semi-major axis used to calculate orbital speed
|
||||
sma = world.evaluate(info.get('sma', distance))
|
||||
|
||||
if hasattr(parent, 'mass') and parent.mass is not None:
|
||||
period = 2 * pi * sqrt((sma * 1000) ** 3 / (G * parent.mass))
|
||||
self.orbit_speed = 360.0 / period
|
||||
if not rotation: # Rotation = 0 assumes tidal lock
|
||||
rotation = period
|
||||
else:
|
||||
self.orbit_speed = info.get('orbit_speed', 1)
|
||||
|
||||
self.orbit = KeplerOrbit(distance / world.length, info.get('eccentricity', 0), info.get('inclination', 0),
|
||||
info.get('longitude', 0), info.get('argument', 0))
|
||||
|
||||
self.rotation_angle = 360.0 / rotation if rotation else 0
|
||||
|
||||
# Orbit calculation
|
||||
self.orbit_vbo = None
|
||||
self.orbit_vao = None
|
||||
self.orbit_cache = None
|
||||
|
||||
@cached_property
|
||||
def orbit_matrix(self):
|
||||
return self.world.view_matrix() * Matrix4f.from_angles(self.location)
|
||||
super(Body, self).__init__(*args, **kwargs)
|
||||
self.initial_roll = self.rotation[2]
|
||||
|
||||
def update(self):
|
||||
super(Body, self).update()
|
||||
|
||||
if self.rotation_angle:
|
||||
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
|
||||
|
||||
def collides(self, x, y, z):
|
||||
if self.radius is None:
|
||||
return False
|
||||
ox, oy, oz = self.location
|
||||
dx, dy, dz = x - ox, y - oy, z - oz
|
||||
distance = sqrt(dx*dx + dy*dy + dz*dz)
|
||||
if distance > self.radius:
|
||||
return False
|
||||
return (ox + dx * self.radius / distance,
|
||||
oy + dy * self.radius / distance,
|
||||
oz + dz * self.radius / distance)
|
||||
|
||||
|
||||
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 range(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
|
||||
|
||||
if self.orbit:
|
||||
self.parent.update()
|
||||
px, py, pz = self.parent.location
|
||||
x, z, y = self.orbit.orbit(self.world.tick * self.orbit_speed % 360)
|
||||
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)
|
||||
self.orbit_matrix = None
|
||||
|
||||
for satellite in self.satellites:
|
||||
satellite.update()
|
||||
|
||||
def get_orbit(self, shader):
|
||||
if not self.orbit:
|
||||
return
|
||||
|
||||
# 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_vbo, self.orbit_vao
|
||||
|
||||
if self.orbit_vbo is not None:
|
||||
self.orbit_vbo.close()
|
||||
|
||||
if self.orbit_vao is not None:
|
||||
self.orbit_vao.close()
|
||||
|
||||
self.orbit_vbo = OrbitVBO(self.orbit)
|
||||
self.orbit_vao = VAO()
|
||||
|
||||
with self.orbit_vao:
|
||||
glBindBuffer(GL_ARRAY_BUFFER, self.orbit_vbo.vbo)
|
||||
shader.vertex_attribute('a_position', self.orbit_vbo.position_size, self.orbit_vbo.type, GL_FALSE,
|
||||
self.orbit_vbo.stride, self.orbit_vbo.position_offset)
|
||||
|
||||
self.orbit_cache = cache
|
||||
return self.orbit_vbo, self.orbit_vao
|
||||
|
||||
def _draw_orbits(self, distance):
|
||||
shader = self.world.activate_shader('line')
|
||||
solid = distance < self.parent.orbit_opaque
|
||||
alpha = 1 if solid else (1 - (distance - self.parent.orbit_opaque) / self.parent.orbit_blend)
|
||||
shader.uniform_vec4('u_color', 1, 1, 1, alpha)
|
||||
shader.uniform_mat4('u_mvpMatrix', self.world.projection_matrix() * self.parent.orbit_matrix)
|
||||
|
||||
if not solid:
|
||||
glEnable(GL_BLEND)
|
||||
|
||||
vbo, vao = self.get_orbit(shader)
|
||||
with vao:
|
||||
glDrawArrays(GL_LINE_LOOP, 0, vbo.vertex_count)
|
||||
|
||||
if not solid:
|
||||
glDisable(GL_BLEND)
|
||||
|
||||
def draw(self, options):
|
||||
self._draw(options)
|
||||
|
||||
if options.orbit and self.orbit:
|
||||
dist = self.world.cam.distance(*self.parent.location)
|
||||
if dist < self.parent.orbit_show:
|
||||
self._draw_orbits(dist)
|
||||
|
||||
for satellite in self.satellites:
|
||||
satellite.draw(options)
|
||||
|
||||
def _draw(self, options):
|
||||
raise NotImplementedError()
|
||||
|
||||
def collides(self, x, y, z):
|
||||
return self._collides(x, y, z) or any(satellite.collides(x, y, z) for satellite in self.satellites)
|
||||
|
||||
def _collides(self, x, y, z):
|
||||
return False
|
||||
|
||||
|
||||
class SphericalBody(Body):
|
||||
_sphere_cache = {}
|
||||
|
||||
@classmethod
|
||||
def _get_sphere(cls, division, tangent=True):
|
||||
if (division, tangent) in cls._sphere_cache:
|
||||
return cls._sphere_cache[division, tangent]
|
||||
cls._sphere_cache[division, tangent] = sphere = \
|
||||
(TangentSphere if tangent else SimpleSphere)(division, division)
|
||||
return sphere
|
||||
|
||||
def __init__(self, name, world, info, parent=None):
|
||||
super(SphericalBody, self).__init__(name, world, info, parent)
|
||||
|
||||
self.radius = world.evaluate(info.get('radius', world.length)) / world.length
|
||||
division = info.get('division', max(min(int(self.radius / 8), 60), 10))
|
||||
|
||||
self.light_source = info.get('light_source', False)
|
||||
self.shininess = info.get('shininess', 0)
|
||||
self.type = info.get('type', 'planet')
|
||||
|
||||
self.texture = get_best_texture(info['texture'])
|
||||
self.normal_texture = None
|
||||
self.specular_texture = None
|
||||
self.emission_texture = None
|
||||
|
||||
self.sphere = self._get_sphere(division, tangent=self.type == 'planet')
|
||||
self.vao = VAO()
|
||||
|
||||
if self.type == 'planet':
|
||||
shader = self.world.activate_shader('planet')
|
||||
with self.vao:
|
||||
glBindBuffer(GL_ARRAY_BUFFER, self.sphere.vbo)
|
||||
shader.vertex_attribute('a_normal', self.sphere.direction_size, self.sphere.type, GL_FALSE,
|
||||
self.sphere.stride, self.sphere.direction_offset)
|
||||
shader.vertex_attribute('a_tangent', self.sphere.tangent_size, self.sphere.type, GL_FALSE,
|
||||
self.sphere.stride, self.sphere.tangent_offset)
|
||||
shader.vertex_attribute('a_uv', self.sphere.uv_size, self.sphere.type, GL_FALSE,
|
||||
self.sphere.stride, self.sphere.uv_offset)
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
||||
elif self.type == 'star':
|
||||
shader = self.world.activate_shader('star')
|
||||
with self.vao:
|
||||
glBindBuffer(GL_ARRAY_BUFFER, self.sphere.vbo)
|
||||
shader.vertex_attribute('a_normal', self.sphere.direction_size, self.sphere.type, GL_FALSE,
|
||||
self.sphere.stride, self.sphere.direction_offset)
|
||||
shader.vertex_attribute('a_uv', self.sphere.uv_size, self.sphere.type, GL_FALSE,
|
||||
self.sphere.stride, self.sphere.uv_offset)
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
||||
else:
|
||||
raise ValueError('Invalid type: %s' % self.type)
|
||||
|
||||
self.atmosphere = None
|
||||
self.clouds = None
|
||||
self.ring = 0
|
||||
|
||||
if 'normal_map' in info:
|
||||
self.normal_texture = get_best_texture(info['normal_map'])
|
||||
|
||||
if 'specular_map' in info:
|
||||
self.specular_texture = get_best_texture(info['specular_map'])
|
||||
|
||||
if 'emission_map' in info:
|
||||
self.emission_texture = get_best_texture(info['emission_map'])
|
||||
|
||||
if 'atmosphere' in info:
|
||||
atmosphere_data = info['atmosphere']
|
||||
atm_size = world.evaluate(atmosphere_data.get('glow_size', None))
|
||||
atm_texture = atmosphere_data.get('glow_texture', None)
|
||||
atm_color = atmosphere_data.get('glow_color', None)
|
||||
cloud_texture = atmosphere_data.get('cloud_texture', None)
|
||||
if cloud_texture is not None:
|
||||
self.cloud_transparency = get_best_texture(cloud_texture, loader=load_alpha_mask)
|
||||
self.cloud_radius = self.radius + 2
|
||||
self.clouds = self._get_sphere(division, tangent=False)
|
||||
self.cloud_vao = VAO()
|
||||
shader = self.world.activate_shader('clouds')
|
||||
with self.cloud_vao:
|
||||
glBindBuffer(GL_ARRAY_BUFFER, self.clouds.vbo)
|
||||
shader.vertex_attribute('a_normal', self.clouds.direction_size, self.clouds.type, GL_FALSE,
|
||||
self.clouds.stride, self.clouds.direction_offset)
|
||||
shader.vertex_attribute('a_uv', self.clouds.uv_size, self.clouds.type, GL_FALSE,
|
||||
self.clouds.stride, self.clouds.uv_offset)
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
||||
|
||||
if atm_texture is not None and atm_color is not None:
|
||||
self.atm_texture = load_texture_1d(atm_texture, clamp=True)
|
||||
self.atm_color = atm_color
|
||||
self.atmosphere = Disk(self.radius, self.radius + atm_size, 30)
|
||||
self.atmosphere_vao = VAO()
|
||||
shader = self.world.activate_shader('atmosphere')
|
||||
with self.atmosphere_vao:
|
||||
glBindBuffer(GL_ARRAY_BUFFER, self.atmosphere.vbo)
|
||||
shader.vertex_attribute('a_position', self.atmosphere.position_size, self.atmosphere.type, GL_FALSE,
|
||||
self.atmosphere.stride, self.atmosphere.position_offset)
|
||||
shader.vertex_attribute('a_u', self.atmosphere.u_size, self.atmosphere.type, GL_FALSE,
|
||||
self.atmosphere.stride, self.atmosphere.u_offset)
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
||||
|
||||
if 'ring' in info:
|
||||
distance = world.evaluate(info['ring'].get('distance', self.radius * 1.2))
|
||||
size = world.evaluate(info['ring'].get('size', self.radius / 2))
|
||||
|
||||
pitch, yaw, roll = self.rotation
|
||||
pitch = world.evaluate(info['ring'].get('pitch', pitch))
|
||||
yaw = world.evaluate(info['ring'].get('yaw', yaw))
|
||||
roll = world.evaluate(info['ring'].get('roll', roll))
|
||||
self.ring_rotation = pitch, yaw, roll
|
||||
|
||||
self.ring_texture = load_texture_1d(info['ring'].get('texture'), clamp=True)
|
||||
self.ring = Disk(distance, distance + size, 30)
|
||||
|
||||
self.ring_vao = VAO()
|
||||
shader = self.world.activate_shader('ring')
|
||||
with self.ring_vao:
|
||||
glBindBuffer(GL_ARRAY_BUFFER, self.ring.vbo)
|
||||
shader.vertex_attribute('a_position', self.ring.position_size, self.ring.type, GL_FALSE,
|
||||
self.ring.stride, self.ring.position_offset)
|
||||
shader.vertex_attribute('a_u', self.ring.u_size, self.ring.type, GL_FALSE,
|
||||
self.ring.stride, self.ring.u_offset)
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
||||
|
||||
def _draw_planet(self):
|
||||
shader = self.world.activate_shader('planet')
|
||||
shader.uniform_float('u_radius', self.radius)
|
||||
shader.uniform_mat4('u_modelMatrix', self.model_matrix)
|
||||
shader.uniform_mat4('u_mvMatrix', self.mv_matrix)
|
||||
shader.uniform_mat4('u_mvpMatrix', self.mvp_matrix)
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, self.texture)
|
||||
shader.uniform_texture('u_planet.diffuseMap', 0)
|
||||
|
||||
shader.uniform_bool('u_planet.hasNormal', self.normal_texture)
|
||||
if self.normal_texture:
|
||||
glActiveTexture(GL_TEXTURE1)
|
||||
glBindTexture(GL_TEXTURE_2D, self.normal_texture)
|
||||
shader.uniform_texture('u_planet.normalMap', 1)
|
||||
|
||||
shader.uniform_bool('u_planet.hasSpecular', self.specular_texture)
|
||||
if self.specular_texture:
|
||||
glActiveTexture(GL_TEXTURE2)
|
||||
glBindTexture(GL_TEXTURE_2D, self.specular_texture)
|
||||
shader.uniform_texture('u_planet.specularMap', 2)
|
||||
shader.uniform_vec3('u_planet.specular', 1, 1, 1)
|
||||
shader.uniform_float('u_planet.shininess', 10)
|
||||
else:
|
||||
shader.uniform_vec3('u_planet.specular', 0, 0, 0)
|
||||
shader.uniform_float('u_planet.shininess', 0)
|
||||
|
||||
shader.uniform_bool('u_planet.hasEmission', self.emission_texture)
|
||||
if self.emission_texture:
|
||||
glActiveTexture(GL_TEXTURE3)
|
||||
glBindTexture(GL_TEXTURE_2D, self.emission_texture)
|
||||
shader.uniform_texture('u_planet.emissionMap', 3)
|
||||
shader.uniform_vec3('u_planet.ambient', 0, 0, 0)
|
||||
shader.uniform_vec3('u_planet.emission', 1, 1, 1)
|
||||
else:
|
||||
shader.uniform_vec3('u_planet.ambient', 1, 1, 1)
|
||||
shader.uniform_vec3('u_planet.emission', 0, 0, 0)
|
||||
|
||||
shader.uniform_vec3('u_planet.diffuse', 1, 1, 1)
|
||||
|
||||
with self.vao:
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, self.sphere.vertex_count)
|
||||
|
||||
glActiveTexture(GL_TEXTURE0)
|
||||
|
||||
def _draw_star(self):
|
||||
shader = self.world.activate_shader('star')
|
||||
shader.uniform_float('u_radius', self.radius)
|
||||
shader.uniform_mat4('u_mvpMatrix', self.mvp_matrix)
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, self.texture)
|
||||
shader.uniform_texture('u_emission', 0)
|
||||
|
||||
with self.vao:
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, self.sphere.vertex_count)
|
||||
|
||||
def _draw_sphere(self):
|
||||
if self.type == 'planet':
|
||||
self._draw_planet()
|
||||
elif self.type == 'star':
|
||||
self._draw_star()
|
||||
|
||||
def _draw_atmosphere(self):
|
||||
glEnable(GL_BLEND)
|
||||
glDisable(GL_CULL_FACE)
|
||||
shader = self.world.activate_shader('atmosphere')
|
||||
|
||||
mv = self.mv_matrix.matrix
|
||||
matrix = Matrix4f([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, mv[12], mv[13], mv[14], 1])
|
||||
shader.uniform_mat4('u_mvpMatrix', self.world.projection_matrix() * matrix)
|
||||
|
||||
glBindTexture(GL_TEXTURE_1D, self.atm_texture)
|
||||
shader.uniform_texture('u_transparency', 0)
|
||||
shader.uniform_vec3('u_color', *self.atm_color)
|
||||
|
||||
with self.atmosphere_vao:
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, self.atmosphere.vertex_count)
|
||||
|
||||
glDisable(GL_BLEND)
|
||||
glEnable(GL_CULL_FACE)
|
||||
|
||||
def _draw_clouds(self):
|
||||
glEnable(GL_BLEND)
|
||||
shader = self.world.activate_shader('clouds')
|
||||
shader.uniform_float('u_radius', self.cloud_radius)
|
||||
shader.uniform_mat4('u_modelMatrix', self.model_matrix)
|
||||
shader.uniform_mat4('u_mvpMatrix', self.mvp_matrix)
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, self.cloud_transparency)
|
||||
shader.uniform_texture('u_transparency', 0)
|
||||
shader.uniform_vec3('u_diffuse', 1, 1, 1)
|
||||
shader.uniform_vec3('u_ambient', 0.1, 0.1, 0.1)
|
||||
|
||||
with self.cloud_vao:
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, self.clouds.vertex_count)
|
||||
|
||||
glDisable(GL_BLEND)
|
||||
|
||||
def _draw_rings(self):
|
||||
glEnable(GL_BLEND)
|
||||
glDisable(GL_CULL_FACE)
|
||||
shader = self.world.activate_shader('ring')
|
||||
shader.uniform_mat4('u_modelMatrix', self.model_matrix)
|
||||
shader.uniform_mat4('u_mvpMatrix', self.mvp_matrix)
|
||||
shader.uniform_vec3('u_planet', *self.location)
|
||||
shader.uniform_vec3('u_sun', 0, 0, 0)
|
||||
shader.uniform_float('u_planetRadius', self.radius)
|
||||
shader.uniform_float('u_ambient', 0.1)
|
||||
|
||||
glBindTexture(GL_TEXTURE_1D, self.ring_texture)
|
||||
shader.uniform_texture('u_texture', 0)
|
||||
|
||||
with self.ring_vao:
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, self.ring.vertex_count)
|
||||
|
||||
glDisable(GL_BLEND)
|
||||
glEnable(GL_CULL_FACE)
|
||||
|
||||
def _draw(self, options):
|
||||
self._draw_sphere()
|
||||
|
||||
if options.atmosphere and self.atmosphere:
|
||||
self._draw_atmosphere()
|
||||
|
||||
if options.cloud and self.clouds:
|
||||
self._draw_clouds()
|
||||
|
||||
if self.ring:
|
||||
self._draw_rings()
|
||||
|
||||
def _collides(self, x, y, z):
|
||||
ox, oy, oz = self.location
|
||||
dx, dy, dz = x - ox, y - oy, z - oz
|
||||
distance = sqrt(dx * dx + dy * dy + dz * dz)
|
||||
return distance <= self.radius
|
||||
|
||||
|
||||
class ModelBody(Body):
|
||||
def __init__(self, name, world, info, parent=None):
|
||||
super(ModelBody, self).__init__(name, world, info, parent)
|
||||
|
||||
scale = info.get('scale', 1)
|
||||
shader = world.activate_shader('model')
|
||||
self.vbo = WavefrontVBO(load_model(info['model']), shader, info.get('sx', scale),
|
||||
info.get('sy', scale), info.get('sz', scale))
|
||||
|
||||
def _draw(self, options):
|
||||
shader = self.world.activate_shader('model')
|
||||
shader.uniform_mat4('u_mvpMatrix', self.mvp_matrix)
|
||||
shader.uniform_mat4('u_mvMatrix', self.mv_matrix)
|
||||
shader.uniform_mat4('u_modelMatrix', self.model_matrix)
|
||||
self.vbo.draw(shader)
|
||||
|
|
499
punyverse/game.py
Normal file
|
@ -0,0 +1,499 @@
|
|||
#!/usr/bin/python
|
||||
from operator import attrgetter
|
||||
from math import hypot, sqrt, atan2, degrees
|
||||
from time import clock
|
||||
import time
|
||||
import random
|
||||
import os
|
||||
|
||||
import six
|
||||
|
||||
from punyverse.camera import Camera
|
||||
from punyverse.world import 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
|
||||
|
||||
try:
|
||||
from itertools import zip_longest
|
||||
except ImportError:
|
||||
from itertools import izip_longest as zip_longest
|
||||
|
||||
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):
|
||||
self.world_options = kwargs.pop('world_options', {})
|
||||
|
||||
super(Applet, self).__init__(*args, **kwargs)
|
||||
texture.init()
|
||||
|
||||
if hasattr(self.config, '_attribute_names'):
|
||||
info = [' %-22s %s' % (key + ':', getattr(self.config, key))
|
||||
for key in self.config._attribute_names]
|
||||
info = ['%-30s %-30s' % group for group in
|
||||
zip_longest(info[::2], info[1::2], fillvalue='')]
|
||||
info = 'OpenGL configuration:\n' + '\n'.join(info)
|
||||
else:
|
||||
info = 'Unknown OpenGL configuration'
|
||||
|
||||
self.loaded = False
|
||||
self.__load_started = False
|
||||
self._loading_phase = pyglet.text.Label(
|
||||
font_name='Consolas', font_size=20, x=10, y=self.height - 80,
|
||||
color=(255, 255, 255, 255), width=self.width - 20, align='center',
|
||||
multiline=True, text='Punyverse is starting...'
|
||||
)
|
||||
self._loading_label = pyglet.text.Label(
|
||||
font_name='Consolas', font_size=16, x=10, y=self.height - 150,
|
||||
color=(255, 255, 255, 255), width=self.width - 20, align='center',
|
||||
multiline=True
|
||||
)
|
||||
self._info_label = pyglet.text.Label(
|
||||
font_name='Consolas', font_size=13, x=10, y=self.height - 250,
|
||||
color=(255, 255, 255, 255), width=self.width - 20,
|
||||
multiline=True, text=info
|
||||
)
|
||||
pyglet.clock.schedule_once(self.load, 0)
|
||||
|
||||
def load(self, *args, **kwargs):
|
||||
if self.loaded or self.__load_started:
|
||||
return
|
||||
|
||||
self.__load_started = True
|
||||
|
||||
def callback(phase, message, progress):
|
||||
self.draw_loading(phase, message, progress)
|
||||
self.flip()
|
||||
self.dispatch_events()
|
||||
|
||||
start = clock()
|
||||
self.fps = 0
|
||||
self.world = World('world.json', callback, self.world_options)
|
||||
phase = 'Initializing game...'
|
||||
print(phase)
|
||||
callback(phase, '', 0)
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
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))
|
||||
|
||||
phase = 'Loading asteroids...'
|
||||
print(phase)
|
||||
|
||||
def load_asteroids(files):
|
||||
for id, file in enumerate(files):
|
||||
callback(phase, 'Loading %s...' % file, float(id) / len(files))
|
||||
yield model_list(load_model(file), 5, 5, 5, (0, 0, 0))
|
||||
|
||||
self.asteroid_ids = list(load_asteroids([r'asteroids/01.obj', r'asteroids/02.obj', r'asteroids/03.obj']))
|
||||
|
||||
c = self.cam
|
||||
c.x, c.y, c.z = self.world.start
|
||||
c.pitch, c.yaw, c.roll = self.world.direction
|
||||
|
||||
phase = 'Updating entities...'
|
||||
print(phase)
|
||||
callback(phase, '', 0)
|
||||
for entity in self.world.tracker:
|
||||
entity.update()
|
||||
|
||||
print('Loaded in %s seconds.' % (clock() - start))
|
||||
self.loaded = True
|
||||
pyglet.clock.schedule(self.update)
|
||||
self.on_resize(self.width, self.height) # On resize handler does nothing unless it's loaded
|
||||
|
||||
def screenshot(self):
|
||||
image = pyglet.image.get_buffer_manager().get_color_buffer()
|
||||
if hasattr(self, '_hwnd') and not self.modifiers & key.MOD_CTRL:
|
||||
from ctypes import windll, cdll
|
||||
from PIL import Image
|
||||
import tempfile
|
||||
CF_BITMAP = 2
|
||||
|
||||
image = Image.frombytes(image.format, (image.width, image.height), image.get_image_data().data)
|
||||
image = image.convert('RGB').transpose(Image.FLIP_TOP_BOTTOM)
|
||||
fd, filename = tempfile.mkstemp('.bmp')
|
||||
try:
|
||||
with os.fdopen(fd, 'wb') as file:
|
||||
image.save(file, 'BMP')
|
||||
if isinstance(filename, six.binary_type):
|
||||
filename = filename.decode('mbcs' if os.name == 'nt' else 'utf8')
|
||||
image = windll.user32.LoadImageW(None, filename, 0, 0, 0, 0x10)
|
||||
windll.user32.OpenClipboard(self._hwnd)
|
||||
windll.user32.EmptyClipboard()
|
||||
windll.user32.SetClipboardData(CF_BITMAP, image)
|
||||
windll.user32.CloseClipboard()
|
||||
finally:
|
||||
os.remove(filename)
|
||||
else:
|
||||
image.save(os.path.expanduser('~/punyverse.png'))
|
||||
|
||||
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):
|
||||
self.modifiers = modifiers
|
||||
if not self.loaded:
|
||||
return
|
||||
|
||||
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 not self.loaded:
|
||||
return
|
||||
|
||||
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):
|
||||
self.modifiers = modifiers
|
||||
if symbol == key.Q:
|
||||
self.screenshot()
|
||||
if not self.loaded:
|
||||
return
|
||||
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 not self.loaded:
|
||||
return
|
||||
|
||||
if symbol in self.keys:
|
||||
self.keys.remove(symbol)
|
||||
|
||||
def on_resize(self, width, height):
|
||||
if not self.loaded:
|
||||
return super(Applet, self).on_resize(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):
|
||||
if not self.loaded:
|
||||
return
|
||||
|
||||
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()
|
||||
collision = entity.collides(c.x, c.y, c.z)
|
||||
if collision:
|
||||
self.speed *= -1
|
||||
c.move(self.speed * 12 * dt)
|
||||
else:
|
||||
self.__time_accumulate += delta
|
||||
|
||||
def draw_loading(self, phase=None, message=None, progress=None):
|
||||
glClear(GL_COLOR_BUFFER_BIT)
|
||||
glLoadIdentity()
|
||||
if phase is not None:
|
||||
self._loading_phase.text = phase
|
||||
if message is not None:
|
||||
self._loading_label.text = message
|
||||
self._loading_phase.draw()
|
||||
self._loading_label.draw()
|
||||
if progress is not None:
|
||||
progress_bar(10, self.height - 170, self.width - 20, 50, progress)
|
||||
self._info_label.draw()
|
||||
|
||||
def on_draw(self, glMatrixBuffer=GLfloat * 16):
|
||||
if not self.loaded:
|
||||
return self.draw_loading()
|
||||
|
||||
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
|
||||
|
||||
with glMatrix(), glRestore(GL_CURRENT_BIT):
|
||||
if entity.background:
|
||||
glTranslatef(c.x, c.y, c.z)
|
||||
else:
|
||||
glTranslatef(x, y, z)
|
||||
glRotatef(pitch, 1, 0, 0)
|
||||
glRotatef(yaw, 0, 1, 0)
|
||||
glRotatef(roll, 0, 0, 1)
|
||||
glCallList(entity.id)
|
||||
if self.debug:
|
||||
with glMatrix(), glRestore(GL_ENABLE_BIT | GL_POLYGON_BIT | GL_LINE_BIT):
|
||||
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)
|
||||
|
||||
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):
|
||||
with glMatrix(), glRestore(GL_ENABLE_BIT):
|
||||
x0, y0, z0 = entity.location
|
||||
glTranslatef(x0, y0, z0)
|
||||
matrix = glMatrixBuffer()
|
||||
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)
|
||||
glEnable(GL_BLEND)
|
||||
if has_atmosphere:
|
||||
glCallList(entity.atmosphere)
|
||||
if has_corona:
|
||||
x, y, z = c.direction()
|
||||
glTranslatef(-x, -y, -z)
|
||||
glCallList(entity.corona)
|
||||
|
||||
if self.cloud and hasattr(entity, 'cloudmap') and entity.cloudmap:
|
||||
with glMatrix(), glRestore(GL_ENABLE_BIT):
|
||||
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)
|
||||
|
||||
if self.orbit and hasattr(entity, 'get_orbit') and hasattr(entity, 'parent'):
|
||||
parent = entity.parent
|
||||
distance = get_distance(parent)
|
||||
if distance < parent.orbit_show:
|
||||
with glMatrix(), glRestore(GL_ENABLE_BIT | GL_LINE_BIT | GL_CURRENT_BIT):
|
||||
glTranslatef(*entity.parent.location)
|
||||
glDisable(GL_LIGHTING)
|
||||
solid = distance < parent.orbit_opaque
|
||||
glColor4f(1, 1, 1, 1 if solid else
|
||||
(1 - (distance - parent.orbit_opaque) / parent.orbit_blend))
|
||||
if not solid:
|
||||
glEnable(GL_BLEND)
|
||||
glLineWidth(1)
|
||||
glCallList(entity.get_orbit())
|
||||
|
||||
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()
|
||||
with glRestore(GL_CURRENT_BIT | GL_LINE_BIT):
|
||||
glLineWidth(2)
|
||||
cx, cy = width / 2, height / 2
|
||||
glColor4f(0, 1, 0, 1)
|
||||
circle(10, 20, (cx, cy))
|
||||
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
|
|
@ -1,349 +1,344 @@
|
|||
from __future__ import division
|
||||
|
||||
from array import array
|
||||
from ctypes import c_int, c_float, byref, cast, POINTER, c_uint, c_short, c_ushort
|
||||
from math import *
|
||||
from random import random, gauss, choice
|
||||
|
||||
from pyglet.gl import *
|
||||
# noinspection PyUnresolvedReferences
|
||||
from six.moves import range
|
||||
from pyglet.gl import *
|
||||
from pyglet.gl.gl_info import have_extension
|
||||
|
||||
TWOPI = pi * 2
|
||||
|
||||
__all__ = ['FontEngine', 'Matrix4f', 'Disk', 'OrbitVBO', 'SimpleSphere',
|
||||
'TangentSphere', 'Cube', 'Circle', 'BeltVBO', 'VAO']
|
||||
__all__ = ['compile', 'ortho', 'frustrum', 'crosshair', 'circle', 'disk', 'sphere', 'colourball', 'torus', 'belt',
|
||||
'flare', 'normal_sphere', 'glSection', 'glMatrix', 'glRestore', 'progress_bar']
|
||||
|
||||
|
||||
def array_to_ctypes(arr):
|
||||
return cast(arr.buffer_info()[0], POINTER({
|
||||
'f': c_float,
|
||||
'i': c_int,
|
||||
'I': c_uint,
|
||||
'h': c_short,
|
||||
'H': c_ushort,
|
||||
}[arr.typecode]))
|
||||
class glSection(object):
|
||||
def __init__(self, type):
|
||||
self.type = type
|
||||
|
||||
def __enter__(self):
|
||||
glBegin(self.type)
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
glEnd()
|
||||
|
||||
|
||||
def array_to_gl_buffer(buffer):
|
||||
vbo = c_uint()
|
||||
glGenBuffers(1, byref(vbo))
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo.value)
|
||||
glBufferData(GL_ARRAY_BUFFER, buffer.itemsize * len(buffer), array_to_ctypes(buffer), GL_STATIC_DRAW)
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
||||
return vbo.value
|
||||
class glRestore(object):
|
||||
def __init__(self, flags):
|
||||
self.flags = flags
|
||||
|
||||
def __enter__(self):
|
||||
glPushAttrib(self.flags)
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
glPopAttrib()
|
||||
|
||||
|
||||
def list_to_gl_buffer(buffer, array_type='f'):
|
||||
return array_to_gl_buffer(array(array_type, buffer))
|
||||
class glMatrix(object):
|
||||
def __enter__(self):
|
||||
glPushMatrix()
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
glPopMatrix()
|
||||
|
||||
|
||||
class Matrix4f(object):
|
||||
def __init__(self, matrix):
|
||||
self.matrix = array('f', matrix)
|
||||
assert len(self.matrix) == 16
|
||||
|
||||
@classmethod
|
||||
def from_angles(cls, location=(0, 0, 0), rotation=(0, 0, 0), view=False):
|
||||
m = [0] * 16
|
||||
x, y, z = location
|
||||
pitch, yaw, roll = rotation
|
||||
sp, sy, sr = sin(radians(pitch)), sin(radians(yaw)), sin(radians(roll))
|
||||
cp, cy, cr = cos(radians(pitch)), cos(radians(yaw)), cos(radians(roll))
|
||||
|
||||
m[0x0] = cy * cr
|
||||
m[0x1] = sp * sy * cr + cp * sr
|
||||
m[0x2] = sp * sr - cp * sy * cr
|
||||
m[0x3] = 0
|
||||
m[0x4] = -cy * sr
|
||||
m[0x5] = cp * cr - sp * sy * sr
|
||||
m[0x6] = cp * sy * sr + sp * cr
|
||||
m[0x7] = 0
|
||||
m[0x8] = sy
|
||||
m[0x9] = -sp * cy
|
||||
m[0xA] = cp * cy
|
||||
m[0xB] = 0
|
||||
if view:
|
||||
m[0xC] = m[0x0] * -x + m[0x4] * -y + m[0x8] * -z
|
||||
m[0xD] = m[0x1] * -x + m[0x5] * -y + m[0x9] * -z
|
||||
m[0xE] = m[0x2] * -x + m[0x6] * -y + m[0xA] * -z
|
||||
else:
|
||||
m[0xC] = x
|
||||
m[0xD] = y
|
||||
m[0xE] = z
|
||||
m[0xF] = 1
|
||||
return cls(m)
|
||||
|
||||
@property
|
||||
def _as_parameter_(self):
|
||||
return array_to_ctypes(self.matrix)
|
||||
|
||||
@property
|
||||
def bytes(self):
|
||||
return self.matrix.itemsize * 16
|
||||
|
||||
def __mul__(self, other):
|
||||
if not isinstance(other, Matrix4f):
|
||||
return NotImplemented
|
||||
|
||||
rows = ((0, 4, 8, 12), (1, 5, 9, 13), (2, 6, 10, 14), (3, 7, 11, 15))
|
||||
cols = ((0, 1, 2, 3), (4, 5, 6, 7), (8, 9, 10, 11), (12, 13, 14, 15))
|
||||
a, b = self.matrix, other.matrix
|
||||
return type(self)(sum(a[i] * b[j] for i, j in zip(r, c)) for c in cols for r in rows)
|
||||
def compile(pointer, *args, **kwargs):
|
||||
display = glGenLists(1)
|
||||
glNewList(display, GL_COMPILE)
|
||||
pointer(*args, **kwargs)
|
||||
glEndList()
|
||||
return display
|
||||
|
||||
|
||||
class Circle(object):
|
||||
type = GL_FLOAT
|
||||
stride = 2 * 4
|
||||
position_offset = 0
|
||||
position_size = 2
|
||||
|
||||
def __init__(self, r, segs, shader):
|
||||
self.vertex_count = segs
|
||||
buffer = segs * 2 * [0]
|
||||
delta = 2 * pi / segs
|
||||
for i in range(segs):
|
||||
theta = delta * i
|
||||
buffer[2*i:2*i+2] = [cos(theta) * r, sin(theta) * r]
|
||||
self.vbo = list_to_gl_buffer(buffer)
|
||||
|
||||
self.vao = VAO()
|
||||
with self.vao:
|
||||
glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
|
||||
shader.vertex_attribute('a_position', self.position_size, self.type, GL_FALSE,
|
||||
self.stride, self.position_offset)
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
||||
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()
|
||||
|
||||
|
||||
class Disk(object):
|
||||
type = GL_FLOAT
|
||||
stride = 3 * 4
|
||||
position_offset = 0
|
||||
position_size = 2
|
||||
u_offset = position_size * 4
|
||||
u_size = 1
|
||||
def frustrum():
|
||||
glMatrixMode(GL_PROJECTION)
|
||||
glPopMatrix()
|
||||
glMatrixMode(GL_MODELVIEW)
|
||||
glPopMatrix()
|
||||
glEnable(GL_LIGHTING)
|
||||
glEnable(GL_DEPTH_TEST)
|
||||
|
||||
def __init__(self, rinner, router, segs):
|
||||
|
||||
def crosshair(size, coords):
|
||||
cx, cy = coords
|
||||
with glSection(GL_LINES):
|
||||
glVertex2f(cx - size, cy)
|
||||
glVertex2f(cx + size, cy)
|
||||
glVertex2f(cx, cy - size)
|
||||
glVertex2f(cx, cy + size)
|
||||
|
||||
|
||||
def circle(r, seg, coords):
|
||||
cx, cy = coords
|
||||
with glSection(GL_LINE_LOOP):
|
||||
for i in range(seg):
|
||||
theta = TWOPI * i / seg
|
||||
glVertex2f(cx + cos(theta) * r, cy + sin(theta) * r)
|
||||
|
||||
|
||||
def disk(rinner, router, segs, tex):
|
||||
with glRestore(GL_ENABLE_BIT):
|
||||
glEnable(GL_TEXTURE_2D)
|
||||
glEnable(GL_BLEND)
|
||||
glDisable(GL_LIGHTING)
|
||||
glDisable(GL_CULL_FACE)
|
||||
glBindTexture(GL_TEXTURE_2D, tex)
|
||||
res = segs * 5
|
||||
delta = 2 * pi / res
|
||||
self.vertex_count = (res + 1) * 2
|
||||
# Need padding to make the last vertex render correctly... why?
|
||||
buffer = self.vertex_count * 3 * [0]
|
||||
for i in range(res):
|
||||
theta = delta * i
|
||||
x, y = cos(theta), sin(theta)
|
||||
buffer[6*i:6*i+6] = [rinner * x, rinner * y, 0, router * x, router * y, 1]
|
||||
buffer[6*res:6*res+6] = buffer[:6]
|
||||
self.vbo = list_to_gl_buffer(buffer)
|
||||
|
||||
with glSection(GL_TRIANGLE_STRIP):
|
||||
factor = TWOPI / res
|
||||
theta = 0
|
||||
for n in range(res + 1):
|
||||
theta += factor
|
||||
x = cos(theta)
|
||||
y = sin(theta)
|
||||
glTexCoord2f(0, 0)
|
||||
glVertex2f(rinner * x, rinner * y)
|
||||
glTexCoord2f(1, 0)
|
||||
glVertex2f(router * x, router * y)
|
||||
|
||||
|
||||
class SimpleSphere(object):
|
||||
type = GL_FLOAT
|
||||
stride = 5 * 4
|
||||
direction_offset = 0
|
||||
direction_size = 3
|
||||
uv_offset = direction_size * 4
|
||||
uv_size = 2
|
||||
|
||||
def __init__(self, lats, longs):
|
||||
tau = pi * 2
|
||||
phi_div = tau / longs
|
||||
theta_div = pi / lats
|
||||
|
||||
self.vertex_count = (lats + 1) * (longs + 1) * 2
|
||||
buffer = self.vertex_count * 5 * [0]
|
||||
index = 0
|
||||
reverse = False
|
||||
for i in range(longs + 1):
|
||||
phi1, phi2 = i * phi_div, (i + 1) * phi_div
|
||||
if reverse:
|
||||
phi1, phi2 = phi2, phi1
|
||||
for j in range(lats + 1):
|
||||
theta = j * theta_div
|
||||
if reverse:
|
||||
theta = pi - theta
|
||||
sine = sin(theta)
|
||||
dz = cos(theta)
|
||||
t = 1 - theta / pi
|
||||
buffer[index:index + 10] = [sine * cos(phi2), sine * sin(phi2), dz, phi2 / tau, t,
|
||||
sine * cos(phi1), sine * sin(phi1), dz, phi1 / tau, t]
|
||||
index += 10
|
||||
reverse ^= True
|
||||
|
||||
self.vbo = list_to_gl_buffer(buffer)
|
||||
def flare(rinner, router, res, prob, tex):
|
||||
with glRestore(GL_ENABLE_BIT):
|
||||
glEnable(GL_TEXTURE_2D)
|
||||
glDisable(GL_CULL_FACE)
|
||||
glDisable(GL_LIGHTING)
|
||||
glBindTexture(GL_TEXTURE_2D, tex)
|
||||
last_x = 1
|
||||
last_y = 0
|
||||
last_theta = 0
|
||||
factor = TWOPI / res
|
||||
rdelta = (router - rinner)
|
||||
with glSection(GL_QUADS):
|
||||
for i in range(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
|
||||
|
||||
|
||||
class TangentSphere(object):
|
||||
type = GL_FLOAT
|
||||
stride = 7 * 4
|
||||
direction_offset = 0
|
||||
direction_size = 3
|
||||
tangent_offset = direction_size * 4
|
||||
tangent_size = 2
|
||||
uv_offset = tangent_offset + tangent_size * 4
|
||||
uv_size = 2
|
||||
def sphere(r, lats, longs, tex, lighting=True, inside=False, fv4=GLfloat * 4):
|
||||
with glRestore(GL_ENABLE_BIT | GL_TEXTURE_BIT):
|
||||
sphere = gluNewQuadric()
|
||||
gluQuadricDrawStyle(sphere, GLU_FILL)
|
||||
gluQuadricTexture(sphere, True)
|
||||
if lighting:
|
||||
gluQuadricNormals(sphere, GLU_SMOOTH)
|
||||
|
||||
def __init__(self, lats, longs):
|
||||
tau = pi * 2
|
||||
phi_div = tau / longs
|
||||
theta_div = pi / lats
|
||||
glEnable(GL_CULL_FACE)
|
||||
glCullFace(GL_FRONT if inside else GL_BACK)
|
||||
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)
|
||||
|
||||
self.vertex_count = (lats + 1) * (longs + 1) * 2
|
||||
buffer = self.vertex_count * 8 * [0]
|
||||
index = 0
|
||||
reverse = False
|
||||
for i in range(longs + 1):
|
||||
phi1, phi2 = i * phi_div, (i + 1) * phi_div
|
||||
if reverse:
|
||||
phi1, phi2 = phi2, phi1
|
||||
for j in range(lats + 1):
|
||||
theta = j * theta_div
|
||||
if reverse:
|
||||
theta = pi - theta
|
||||
sine = sin(theta)
|
||||
dz = cos(theta)
|
||||
t = 1 - theta / pi
|
||||
sphi2, cphi2 = sin(phi2), cos(phi2)
|
||||
sphi1, cphi1 = sin(phi1), cos(phi1)
|
||||
buffer[index:index + 14] = [
|
||||
sine * cphi2, sine * sphi2, dz, sine * -sphi2, sine * cphi2, phi2 / tau, t,
|
||||
sine * cphi1, sine * sphi1, dz, sine * -sphi1, sine * cphi1, phi1 / tau, t,
|
||||
]
|
||||
index += 14
|
||||
reverse ^= True
|
||||
self.vbo = list_to_gl_buffer(buffer)
|
||||
gluSphere(sphere, r, lats, longs)
|
||||
|
||||
gluDeleteQuadric(sphere)
|
||||
|
||||
|
||||
class Cube(object):
|
||||
type = GL_SHORT
|
||||
stride = 3 * 2
|
||||
direction_offset = 0
|
||||
direction_size = 3
|
||||
vertex_count = 36
|
||||
def colourball(r, lats, longs, colour, fv4=GLfloat * 4):
|
||||
"""
|
||||
Sphere function from the OpenGL red book.
|
||||
"""
|
||||
with glRestore(GL_ENABLE_BIT):
|
||||
sphere = gluNewQuadric()
|
||||
|
||||
def __init__(self):
|
||||
self.vbo = list_to_gl_buffer([
|
||||
-1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1,
|
||||
-1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1,
|
||||
-1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1
|
||||
], 'h')
|
||||
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)
|
||||
glEnable(GL_CULL_FACE)
|
||||
glCullFace(GL_BACK)
|
||||
|
||||
gluSphere(sphere, r, lats, longs)
|
||||
|
||||
glEnable(GL_BLEND)
|
||||
gluDeleteQuadric(sphere)
|
||||
|
||||
|
||||
class OrbitVBO(object):
|
||||
type = GL_FLOAT
|
||||
stride = 3 * 4
|
||||
position_offset = 0
|
||||
position_size = 3
|
||||
vertex_count = 360
|
||||
def normal_sphere(r, divide, tex, normal, lighting=True, inside=False, fv4=GLfloat * 4):
|
||||
if (not have_extension('GL_ARB_multitexture') or not
|
||||
have_extension('GL_ARB_texture_env_combine') or not
|
||||
have_extension('GL_EXT_texture_env_dot3')):
|
||||
print('No hardware normal mapping support. No bumping for you.')
|
||||
return sphere(r, divide, divide, tex, lighting, inside)
|
||||
|
||||
def __init__(self, orbit):
|
||||
buffer = 360 * 3 * [0]
|
||||
for theta in range(360):
|
||||
x, z, y = orbit.orbit(theta)
|
||||
buffer[3*theta:3*theta+3] = [x, y, z]
|
||||
from .texture import load_texture
|
||||
normal = load_texture(normal)
|
||||
|
||||
self.vbo = list_to_gl_buffer(buffer)
|
||||
with glRestore(GL_ENABLE_BIT | GL_TEXTURE_BIT):
|
||||
glEnable(GL_CULL_FACE)
|
||||
glCullFace(GL_FRONT if inside else GL_BACK)
|
||||
|
||||
def close(self):
|
||||
if self.vbo is not None:
|
||||
vbo = c_uint(self.vbo)
|
||||
glDeleteBuffers(1, byref(vbo))
|
||||
self.vbo = None
|
||||
glActiveTextureARB(GL_TEXTURE0_ARB)
|
||||
glBindTexture(GL_TEXTURE_2D, normal)
|
||||
glEnable(GL_TEXTURE_2D)
|
||||
|
||||
def __del__(self):
|
||||
self.close()
|
||||
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB)
|
||||
glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_DOT3_RGBA_ARB)
|
||||
|
||||
glActiveTextureARB(GL_TEXTURE1_ARB)
|
||||
glBindTexture(GL_TEXTURE_2D, tex)
|
||||
glEnable(GL_TEXTURE_2D)
|
||||
|
||||
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB)
|
||||
glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE)
|
||||
|
||||
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
|
||||
with glSection(GL_TRIANGLE_STRIP):
|
||||
for j in range(divide + 1):
|
||||
phi1 = j * twopi_divide
|
||||
phi2 = (j + 1) * twopi_divide
|
||||
|
||||
for i in range(divide + 1):
|
||||
theta = i * pi_divide
|
||||
|
||||
s = phi2 / TWOPI
|
||||
t = theta / pi
|
||||
dx, dy, dz = sin(theta) * cos(phi2), sin(theta) * sin(phi2), cos(theta)
|
||||
glNormal3f(dx, dy, dz)
|
||||
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, s, 1 - t)
|
||||
glMultiTexCoord2fARB(GL_TEXTURE1_ARB, s, 1 - t)
|
||||
glVertex3f(r * dx, r * dy, r * dz)
|
||||
|
||||
s = phi1 / TWOPI # x
|
||||
dx, dy = sin(theta) * cos(phi1), sin(theta) * sin(phi1)
|
||||
glNormal3f(dx, dy, dz)
|
||||
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, s, 1 - t)
|
||||
glMultiTexCoord2fARB(GL_TEXTURE1_ARB, s, 1 - t)
|
||||
glVertex3f(r * dx, r * dy, r * dz)
|
||||
|
||||
|
||||
class FontEngine(object):
|
||||
type = GL_SHORT
|
||||
stride = 4 * 2
|
||||
position_offset = 0
|
||||
position_size = 2
|
||||
tex_offset = position_size * 2
|
||||
tex_size = 2
|
||||
def belt(radius, cross, object, count):
|
||||
for i in range(count):
|
||||
theta = TWOPI * random()
|
||||
r = gauss(radius, cross)
|
||||
x, y, z = cos(theta) * r, gauss(0, cross), sin(theta) * r
|
||||
|
||||
def __init__(self, shader, max_length=256):
|
||||
self.storage = array('h', max_length * 24 * [0])
|
||||
vbo = GLuint()
|
||||
glGenBuffers(1, byref(vbo))
|
||||
self.vbo = vbo.value
|
||||
self.vertex_count = None
|
||||
|
||||
self.vao = VAO()
|
||||
with self.vao:
|
||||
glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
|
||||
shader.vertex_attribute('a_rc', self.position_size, self.type, GL_FALSE,
|
||||
self.stride, self.position_offset)
|
||||
shader.vertex_attribute('a_tex', self.tex_size, self.type, GL_FALSE,
|
||||
self.stride, self.tex_offset)
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
||||
|
||||
def draw(self, string):
|
||||
index = 0
|
||||
row = 0
|
||||
col = 0
|
||||
for c in string:
|
||||
if c == '\n':
|
||||
row += 1
|
||||
col = 0
|
||||
continue
|
||||
o = ord(c)
|
||||
if 32 <= o < 128:
|
||||
self.storage[24*index:24*index+24] = array('h', [
|
||||
row, col, o - 32, 1,
|
||||
row + 1, col, o - 32, 0,
|
||||
row + 1, col + 1, o - 31, 0,
|
||||
row, col, o - 32, 1,
|
||||
row + 1, col + 1, o - 31, 0,
|
||||
row, col + 1, o - 31, 1,
|
||||
])
|
||||
index += 1
|
||||
col += 1
|
||||
|
||||
self.vertex_count = index * 6
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
|
||||
glBufferData(GL_ARRAY_BUFFER, self.storage.itemsize * len(self.storage),
|
||||
array_to_ctypes(self.storage), GL_STREAM_DRAW)
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
||||
|
||||
|
||||
class BeltVBO(object):
|
||||
type = GL_FLOAT
|
||||
stride = 4 * 4
|
||||
location_offset = 0
|
||||
location_size = 3
|
||||
scale_offset = location_size * 4
|
||||
scale_size = 1
|
||||
|
||||
def __init__(self, radius, cross, objects, count):
|
||||
arrays = [array('f') for i in range(objects)]
|
||||
|
||||
for i in range(count):
|
||||
theta = TWOPI * random()
|
||||
r = gauss(radius, cross)
|
||||
x, y, z = cos(theta) * r, gauss(0, cross), sin(theta) * r
|
||||
with glMatrix():
|
||||
glTranslatef(x, y, z)
|
||||
scale = gauss(1, 0.5)
|
||||
if scale < 0:
|
||||
scale = 1
|
||||
choice(arrays).extend((x, y, z, scale))
|
||||
|
||||
self.vbo = []
|
||||
self.sizes = []
|
||||
for a in arrays:
|
||||
self.vbo.append(array_to_gl_buffer(a))
|
||||
self.sizes.append(len(a) // 4)
|
||||
glScalef(scale, scale, scale)
|
||||
glCallList(choice(object))
|
||||
|
||||
|
||||
class VAO(object):
|
||||
def __init__(self):
|
||||
buffer = GLuint()
|
||||
glGenVertexArrays(1, byref(buffer))
|
||||
self.vao = buffer
|
||||
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.
|
||||
"""
|
||||
with glRestore(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)
|
||||
|
||||
def __enter__(self):
|
||||
glBindVertexArray(self.vao)
|
||||
major_s = TWOPI / n_major
|
||||
minor_s = TWOPI / n_minor
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
glBindVertexArray(0)
|
||||
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 range(n_major):
|
||||
a0 = i * major_s
|
||||
a1 = a0 + major_s
|
||||
x0 = cos(a0)
|
||||
y0 = sin(a0)
|
||||
x1 = cos(a1)
|
||||
y1 = sin(a1)
|
||||
|
||||
with glSection(GL_TRIANGLE_STRIP):
|
||||
for j in range(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)
|
||||
|
||||
|
||||
def progress_bar(x, y, width, height, filled):
|
||||
with glRestore(GL_ENABLE_BIT):
|
||||
glDisable(GL_TEXTURE_2D)
|
||||
glDisable(GL_BLEND)
|
||||
x1 = x
|
||||
x2 = x + width
|
||||
y1 = y
|
||||
y2 = y - height
|
||||
y3 = 0.65 * y1 + 0.35 * y2
|
||||
y4 = 0.25 * y1 + 0.75 * y2
|
||||
|
||||
glColor3f(0.6, 0.6, 0.6)
|
||||
with glSection(GL_LINE_LOOP):
|
||||
glVertex2f(x1, y1)
|
||||
glVertex2f(x1, y2)
|
||||
glVertex2f(x2, y2)
|
||||
glVertex2f(x2, y1)
|
||||
|
||||
x1 += 1
|
||||
y1 -= 1
|
||||
x2 = x + width * filled - 1
|
||||
|
||||
with glSection(GL_TRIANGLE_STRIP):
|
||||
glColor3f(0.81, 1, 0.82)
|
||||
glVertex2f(x1, y1)
|
||||
glVertex2f(x2, y1)
|
||||
glColor3f(0, 0.83, 0.16)
|
||||
glVertex2f(x1, y3)
|
||||
glVertex2f(x2, y3)
|
||||
glVertex2f(x1, y4)
|
||||
glVertex2f(x2, y4)
|
||||
glColor3f(0.37, 0.92, 0.43)
|
||||
glVertex2f(x1, y2)
|
||||
glVertex2f(x2, y2)
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
#ifdef _MSC_VER
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
# include <OpenGL/gl.h>
|
||||
#else
|
||||
# include <GL/gl.h>
|
||||
#endif
|
|
@ -1,28 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <Windows.h>
|
||||
#include <Python.h>
|
||||
|
||||
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
|
||||
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 0x00000001;
|
||||
|
||||
#ifdef GUI
|
||||
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
|
||||
#else
|
||||
int main()
|
||||
#endif
|
||||
{
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
int argc;
|
||||
LPWSTR *argv = CommandLineToArgvW(GetCommandLineW(), &argc);
|
||||
#else
|
||||
int argc = __argc;
|
||||
char **argv = __argv;
|
||||
#endif
|
||||
|
||||
Py_SetProgramName(argv[0]);
|
||||
Py_Initialize();
|
||||
PySys_SetArgvEx(argc, argv, 0);
|
||||
PyRun_SimpleString("from punyverse.main import main; main()");
|
||||
Py_Finalize();
|
||||
return 0;
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
import os
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
if os.name != 'nt':
|
||||
print('Not on Windows. Nothing to do.')
|
||||
return
|
||||
|
||||
source_dir = os.path.dirname(__file__)
|
||||
dest_dir = os.path.join(sys.prefix, 'Scripts')
|
||||
|
||||
launcher_exe = os.path.join(source_dir, 'launcher.exe')
|
||||
launcherw_exe = os.path.join(source_dir, 'launcherw.exe')
|
||||
punyverse_exe = os.path.join(dest_dir, 'punyverse.exe')
|
||||
punyversew_exe = os.path.join(dest_dir, 'punyversew.exe')
|
||||
assert os.path.isfile(launcher_exe)
|
||||
assert os.path.isfile(launcherw_exe)
|
||||
assert os.path.isfile(punyverse_exe)
|
||||
assert os.path.isfile(punyversew_exe)
|
||||
|
||||
def copy(src, dst):
|
||||
print('Copying %s to %s...' % (src, dst))
|
||||
shutil.copy(src, dst)
|
||||
copy(launcher_exe, punyverse_exe)
|
||||
copy(launcherw_exe, punyversew_exe)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,190 +0,0 @@
|
|||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
import pyglet
|
||||
from pyglet.gl import *
|
||||
from six.moves import zip_longest
|
||||
|
||||
from punyverse.world import World
|
||||
|
||||
|
||||
class glContext(object):
|
||||
def __init__(self, context):
|
||||
self.new_context = context
|
||||
|
||||
def __enter__(self):
|
||||
self.old_context = get_current_context()
|
||||
self.new_context.set_current()
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.old_context.set_current()
|
||||
|
||||
|
||||
class glSection(object):
|
||||
def __init__(self, type):
|
||||
self.type = type
|
||||
|
||||
def __enter__(self):
|
||||
glBegin(self.type)
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
glEnd()
|
||||
|
||||
|
||||
def progress_bar(x, y, width, height, filled):
|
||||
glPushAttrib(GL_ENABLE_BIT)
|
||||
glDisable(GL_TEXTURE_2D)
|
||||
glDisable(GL_BLEND)
|
||||
x1 = x
|
||||
x2 = x + width
|
||||
y1 = y
|
||||
y2 = y - height
|
||||
y3 = 0.65 * y1 + 0.35 * y2
|
||||
y4 = 0.25 * y1 + 0.75 * y2
|
||||
|
||||
glColor3f(0.6, 0.6, 0.6)
|
||||
with glSection(GL_LINE_LOOP):
|
||||
glVertex2f(x1, y1)
|
||||
glVertex2f(x1, y2)
|
||||
glVertex2f(x2, y2)
|
||||
glVertex2f(x2, y1)
|
||||
|
||||
x1 += 1
|
||||
y1 -= 1
|
||||
x2 = x + width * filled - 1
|
||||
|
||||
with glSection(GL_TRIANGLE_STRIP):
|
||||
glColor3f(0.81, 1, 0.82)
|
||||
glVertex2f(x1, y1)
|
||||
glVertex2f(x2, y1)
|
||||
glColor3f(0, 0.83, 0.16)
|
||||
glVertex2f(x1, y3)
|
||||
glVertex2f(x2, y3)
|
||||
glVertex2f(x1, y4)
|
||||
glVertex2f(x2, y4)
|
||||
glColor3f(0.37, 0.92, 0.43)
|
||||
glVertex2f(x1, y2)
|
||||
glVertex2f(x2, y2)
|
||||
glPopAttrib()
|
||||
|
||||
|
||||
def get_context_info(context):
|
||||
info = [' %-22s %s' % (key + ':', value)
|
||||
for key, value in context.config.get_gl_attributes()]
|
||||
info = ['%-30s %-30s' % group for group in
|
||||
zip_longest(info[::2], info[1::2], fillvalue='')]
|
||||
|
||||
with glContext(context):
|
||||
gl_info.remove_active_context()
|
||||
gl_info.set_active_context()
|
||||
return '\n'.join([
|
||||
'Graphics Vendor: ' + gl_info.get_vendor(),
|
||||
'Graphics Version: ' + gl_info.get_version(),
|
||||
'Graphics Renderer: ' + gl_info.get_renderer(),
|
||||
]) + '\n\n' + 'OpenGL configuration:\n' + '\n'.join(info)
|
||||
|
||||
|
||||
class LoaderWindow(pyglet.window.Window):
|
||||
MONOSPACE = ('Consolas', 'Droid Sans Mono', 'Courier', 'Courier New', 'Dejavu Sans Mono')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(LoaderWindow, self).__init__(*args, **kwargs)
|
||||
|
||||
# work around pyglet bug: decoding font names as utf-8 instead of mbcs when using EnumFontsA.
|
||||
stderr = sys.stderr
|
||||
sys.stderr = open(os.devnull, 'w')
|
||||
pyglet.font.have_font(self.MONOSPACE[0])
|
||||
sys.stderr = stderr
|
||||
|
||||
self.loading_phase = pyglet.text.Label(
|
||||
font_name=self.MONOSPACE, font_size=20, x=10, y=self.height - 50,
|
||||
color=(255, 255, 255, 255), width=self.width - 20, align='center',
|
||||
multiline=True, text='Punyverse is starting...'
|
||||
)
|
||||
self.loading_label = pyglet.text.Label(
|
||||
font_name=self.MONOSPACE, font_size=16, x=10, y=self.height - 120,
|
||||
color=(255, 255, 255, 255), width=self.width - 20, align='center',
|
||||
multiline=True
|
||||
)
|
||||
self.info_label = pyglet.text.Label(
|
||||
font_name=self.MONOSPACE, font_size=13, x=10, y=self.height - 220,
|
||||
color=(255, 255, 255, 255), width=self.width - 20,
|
||||
multiline=True
|
||||
)
|
||||
self.progress = 0
|
||||
|
||||
self._main_context = None
|
||||
|
||||
def set_main_context(self, context):
|
||||
self._main_context = context
|
||||
self.info_label.text = get_context_info(context)
|
||||
print(self.info_label.text)
|
||||
|
||||
def _load_callback(self, phase, message, progress):
|
||||
print(message)
|
||||
with glContext(self.context):
|
||||
self.loading_phase.text = phase
|
||||
self.loading_label.text = message
|
||||
self.progress = progress
|
||||
|
||||
self.on_draw()
|
||||
self.flip()
|
||||
self.dispatch_events()
|
||||
|
||||
def load(self, **kwargs):
|
||||
start = time.clock()
|
||||
with glContext(self._main_context):
|
||||
world = World('world.json', self._load_callback, **kwargs)
|
||||
print('Loaded in %s seconds.' % (time.clock() - start))
|
||||
return world
|
||||
|
||||
def on_draw(self):
|
||||
glClear(GL_COLOR_BUFFER_BIT)
|
||||
glLoadIdentity()
|
||||
self.loading_phase.draw()
|
||||
self.loading_label.draw()
|
||||
progress_bar(10, self.height - 140, self.width - 20, 50, self.progress)
|
||||
self.info_label.draw()
|
||||
|
||||
def main_is_initializing(self):
|
||||
self._load_callback('Loading main window...', '', 0)
|
||||
|
||||
|
||||
class LoaderConsole(object):
|
||||
def __init__(self):
|
||||
from ctypes import windll
|
||||
self._own_console = False
|
||||
if windll.kernel32.AllocConsole():
|
||||
self._own_console = True
|
||||
self._output = open('CONOUT$', 'w')
|
||||
else:
|
||||
self._output = sys.stdout
|
||||
self._main_context = None
|
||||
|
||||
def _load_callback(self, phase, message, progress):
|
||||
print(message, file=self._output)
|
||||
|
||||
def load(self, **kwargs):
|
||||
start = time.clock()
|
||||
with glContext(self._main_context):
|
||||
world = World('world.json', self._load_callback, **kwargs)
|
||||
print('Loaded in %s seconds.' % (time.clock() - start), file=self._output)
|
||||
return world
|
||||
|
||||
def set_main_context(self, context):
|
||||
self._main_context = context
|
||||
print(get_context_info(context), file=self._output)
|
||||
print('=' * 79, file=self._output)
|
||||
print("You cannot see the normal loading screen because you are using Intel integrated graphics.",
|
||||
file=self._output)
|
||||
print('Please attempt to set python to execute with your dedicated graphics, if available.', file=self._output)
|
||||
print('=' * 79, file=self._output)
|
||||
|
||||
def close(self):
|
||||
if self._own_console:
|
||||
self._output.close()
|
||||
from ctypes import windll
|
||||
windll.kernel32.FreeConsole()
|
|
@ -1,87 +0,0 @@
|
|||
import argparse
|
||||
import sys
|
||||
|
||||
import pyglet
|
||||
|
||||
INITIAL_WIN_HEIGHT = 540
|
||||
INITIAL_WIN_WIDTH = 700
|
||||
DEBUG = False
|
||||
|
||||
|
||||
def main():
|
||||
macos = sys.platform == 'darwin'
|
||||
|
||||
parser = argparse.ArgumentParser(prog='punyverse', description='''
|
||||
Python simulator of a puny universe.
|
||||
''')
|
||||
parser.set_defaults(sky=not macos)
|
||||
parser.add_argument('-D', '--debug', help='Enable pyglet OpenGL debugging', action='store_true')
|
||||
parser.add_argument('-d', '--high-depth', help='Use a larger depth buffer',
|
||||
const=32, default=24, dest='depth', nargs='?', type=int)
|
||||
parser.add_argument('-m', '--multisample', help='Use multisampled image, optional samples',
|
||||
const=2, default=0, nargs='?', type=int)
|
||||
parser.add_argument('-v', '--no-vsync', help='Disables vsync',
|
||||
action='store_false', dest='vsync')
|
||||
parser.add_argument('-n', '--normal', help='Enables the use of normal maps',
|
||||
action='store_true')
|
||||
parser.add_argument('-s', '--sky', help='Enables the sky', dest='sky',
|
||||
action='store_true')
|
||||
parser.add_argument('-S', '--no-sky', help='Disables the sky', dest='sky',
|
||||
action='store_false')
|
||||
args = parser.parse_args()
|
||||
|
||||
versioning = dict(major_version=3, minor_version=3)
|
||||
pyglet.options['debug_gl'] = args.debug
|
||||
if macos:
|
||||
pyglet.options['shadow_window'] = False
|
||||
versioning = dict(major_version=4, minor_version=1, forward_compatible=True)
|
||||
|
||||
template = pyglet.gl.Config(depth_size=args.depth, double_buffer=True,
|
||||
sample_buffers=args.multisample > 1,
|
||||
samples=args.multisample, **versioning)
|
||||
|
||||
platform = pyglet.window.get_platform()
|
||||
display = platform.get_default_display()
|
||||
screen = display.get_default_screen()
|
||||
try:
|
||||
config = screen.get_best_config(template)
|
||||
except pyglet.window.NoSuchConfigException:
|
||||
raise SystemExit('Graphics configuration not supported.')
|
||||
|
||||
create_args = dict(width=INITIAL_WIN_WIDTH, height=INITIAL_WIN_HEIGHT,
|
||||
caption='Punyverse', resizable=True, vsync=args.vsync, visible=False)
|
||||
|
||||
from pyglet.gl import gl_info
|
||||
|
||||
from punyverse.loader import LoaderWindow, LoaderConsole
|
||||
from punyverse.ui import Punyverse
|
||||
|
||||
if pyglet.compat_platform in ('win32', 'cygwin') and gl_info.get_vendor() == 'Intel':
|
||||
# pyglet has some code that tries to create without ARB on Intel.
|
||||
# Of course, all that achieves is the message that you can't create OpenGL 3 contexts.
|
||||
# So we force create an ARB context.
|
||||
from pyglet.gl.win32 import Win32ARBContext
|
||||
context = Win32ARBContext(config, None)
|
||||
|
||||
# We use the console loader since using the GUI loader makes all sorts of wonderful things happen on Intel:
|
||||
# Access violations, mouse events going nowhere, you name it.
|
||||
loader = LoaderConsole()
|
||||
punyverse = Punyverse(context=context, **create_args)
|
||||
else:
|
||||
context = config.create_context(None)
|
||||
loader = LoaderWindow(width=INITIAL_WIN_WIDTH, height=INITIAL_WIN_HEIGHT,
|
||||
caption='Punyverse is loading...')
|
||||
punyverse = Punyverse(context=context, **create_args)
|
||||
loader.context.set_current()
|
||||
|
||||
loader.set_main_context(punyverse.context)
|
||||
world = loader.load(sky=args.sky)
|
||||
punyverse.context.set_current()
|
||||
punyverse.initialize(world)
|
||||
loader.close()
|
||||
punyverse.set_visible(True)
|
||||
pyglet.app.run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,15 +1,13 @@
|
|||
import bz2
|
||||
import gzip
|
||||
from uuid import uuid4
|
||||
import os
|
||||
import gzip
|
||||
import bz2
|
||||
import zipfile
|
||||
from collections import defaultdict
|
||||
|
||||
import six
|
||||
from six.moves import range
|
||||
from pyglet.gl import *
|
||||
# noinspection PyUnresolvedReferences
|
||||
from six.moves import range, zip
|
||||
|
||||
from punyverse.glgeom import list_to_gl_buffer, VAO
|
||||
from punyverse.texture import load_texture
|
||||
|
||||
|
||||
|
@ -24,19 +22,22 @@ openers = {
|
|||
}
|
||||
|
||||
|
||||
class Face(object):
|
||||
__slots__ = ('verts', 'norms', 'texs', 'size')
|
||||
FACE_TRIANGLES = 0
|
||||
FACE_QUADS = 1
|
||||
|
||||
def __init__(self, verts, norms, texs):
|
||||
|
||||
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.size = len(verts)
|
||||
self.vertices = vertices
|
||||
self.normals = normals
|
||||
self.textures = textures
|
||||
|
||||
|
||||
class Material(object):
|
||||
__slots__ = ('name', 'texture', 'Ka', 'Kd', 'Ks', 'shininess')
|
||||
|
||||
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
|
||||
|
@ -47,11 +48,28 @@ class Material(object):
|
|||
|
||||
|
||||
class Group(object):
|
||||
__slots__ = ('material', 'faces')
|
||||
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 __init__(self, material=None, faces=None):
|
||||
self.material = material
|
||||
self.faces = faces or []
|
||||
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):
|
||||
|
@ -95,18 +113,31 @@ class WavefrontObject(object):
|
|||
|
||||
def texture(self, words):
|
||||
l = len(words)
|
||||
u, v = 0, 0
|
||||
x, y, z = 0, 0, 0
|
||||
if l >= 2:
|
||||
u = float(words[1])
|
||||
x = float(words[1])
|
||||
if l >= 3:
|
||||
# OBJ origin is at upper left, OpenGL origin is at lower left
|
||||
v = 1 - float(words[2])
|
||||
self.textures.append((u, v))
|
||||
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 = []
|
||||
|
@ -115,17 +146,21 @@ class WavefrontObject(object):
|
|||
raw_faces = words[i].split(b'/')
|
||||
l = len(raw_faces)
|
||||
|
||||
vindices.append(int(raw_faces[0]) - 1)
|
||||
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]:
|
||||
tindices.append(int(raw_faces[1]) - 1)
|
||||
else:
|
||||
tindices.append(None)
|
||||
|
||||
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]:
|
||||
nindices.append(int(raw_faces[2]) - 1)
|
||||
else:
|
||||
nindices.append(None)
|
||||
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()
|
||||
|
@ -133,7 +168,14 @@ class WavefrontObject(object):
|
|||
else:
|
||||
group = self.current_group
|
||||
|
||||
group.faces.append(Face(vindices, nindices, tindices))
|
||||
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].decode('utf-8')))
|
||||
|
@ -148,7 +190,11 @@ class WavefrontObject(object):
|
|||
print("Warning: no group")
|
||||
|
||||
def group(self, words):
|
||||
group = Group()
|
||||
name = words[1].decode('utf-8')
|
||||
group = Group(name)
|
||||
|
||||
if self.groups:
|
||||
self.current_group.pack()
|
||||
self.groups.append(group)
|
||||
self.current_group = group
|
||||
|
||||
|
@ -184,8 +230,11 @@ class WavefrontObject(object):
|
|||
dispatcher.get(type, default)(words)
|
||||
return True
|
||||
|
||||
|
||||
model_base = os.path.join(os.path.dirname(__file__), 'assets', 'models')
|
||||
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):
|
||||
|
@ -196,151 +245,83 @@ def load_model(path):
|
|||
return WavefrontObject(path)
|
||||
|
||||
|
||||
class ModelVBO(object):
|
||||
__slots__ = ('has_normal', 'has_texture', 'data_buf', 'index_buf', 'offset_type', 'vertex_count', 'vao')
|
||||
def model_list(model, sx=1, sy=1, sz=1, rotation=(0, 0, 0)):
|
||||
for m, text in six.iteritems(model.materials):
|
||||
if text.texture:
|
||||
load_texture(os.path.join(model.root, text.texture))
|
||||
|
||||
def __init__(self):
|
||||
self.vao = VAO()
|
||||
display = glGenLists(1)
|
||||
|
||||
def build_vao(self, shader):
|
||||
stride = (3 + self.has_normal * 3 + self.has_texture * 2) * 4
|
||||
glNewList(display, GL_COMPILE)
|
||||
glPushMatrix()
|
||||
glPushAttrib(GL_CURRENT_BIT)
|
||||
|
||||
with self.vao:
|
||||
glBindBuffer(GL_ARRAY_BUFFER, self.data_buf)
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.index_buf)
|
||||
pitch, yaw, roll = rotation
|
||||
glPushAttrib(GL_TRANSFORM_BIT)
|
||||
glRotatef(pitch, 1, 0, 0)
|
||||
glRotatef(yaw, 0, 1, 0)
|
||||
glRotatef(roll, 0, 0, 1)
|
||||
glPopAttrib()
|
||||
|
||||
shader.vertex_attribute('a_position', 3, GL_FLOAT, GL_FALSE, stride, 0)
|
||||
if self.has_normal:
|
||||
shader.vertex_attribute('a_normal', 3, GL_FLOAT, GL_FALSE, stride, 3 * 4)
|
||||
if self.has_texture:
|
||||
shader.vertex_attribute('a_uv', 2, GL_FLOAT, GL_FALSE, stride, (6 if self.has_normal else 3) * 4)
|
||||
vertices = model.vertices
|
||||
textures = model.textures
|
||||
normals = model.normals
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
||||
for g in model.groups:
|
||||
material = g.material
|
||||
|
||||
def draw(self, shader, instances=None):
|
||||
with self.vao:
|
||||
if not self.has_normal:
|
||||
shader.vertex_attribute_vec3('a_normal', 0, 0, 0)
|
||||
tex_id = load_texture(os.path.join(model.root, material.texture)) if (material and material.texture) else 0
|
||||
|
||||
if not self.has_texture:
|
||||
shader.vertex_attribute_vec2('a_uv', 0, 0)
|
||||
if instances:
|
||||
glDrawElementsInstanced(GL_TRIANGLES, self.vertex_count, self.offset_type, 0, instances)
|
||||
else:
|
||||
glDrawElements(GL_TRIANGLES, self.vertex_count, self.offset_type, 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
|
||||
|
||||
class WavefrontVBO(object):
|
||||
def __init__(self, model, shader, sx=1, sy=1, sz=1):
|
||||
self._tex_cache = {}
|
||||
self.vbos = []
|
||||
self.scale = (sx, sy, sz)
|
||||
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)
|
||||
|
||||
for m, material in six.iteritems(model.materials):
|
||||
if material.texture and material.texture not in self._tex_cache:
|
||||
self._tex_cache[material.texture] = load_texture(os.path.join(model.root, material.texture))
|
||||
|
||||
vertices = model.vertices
|
||||
textures = model.textures
|
||||
normals = model.normals
|
||||
|
||||
for group in self.merge_groups(model):
|
||||
processed = self.process_group(group, vertices, normals, textures)
|
||||
self.vbos.append((group.material, processed))
|
||||
processed.build_vao(shader)
|
||||
|
||||
def additional_attributes(self, callback):
|
||||
for _, group in self.vbos:
|
||||
with group.vao:
|
||||
callback()
|
||||
|
||||
def draw(self, shader, instances=None):
|
||||
for mat, vbo in self.vbos:
|
||||
tex_id = self._tex_cache[mat.texture] if mat and mat.texture else 0
|
||||
type = -1
|
||||
|
||||
def point(f, vertices, normals, textures, n):
|
||||
if f.norms:
|
||||
glNormal3f(*normals[f.norms[n]])
|
||||
if tex_id:
|
||||
glBindTexture(GL_TEXTURE_2D, tex_id)
|
||||
shader.uniform_bool('u_material.hasDiffuse', True)
|
||||
shader.uniform_texture('u_material.diffuseMap', 0)
|
||||
else:
|
||||
shader.uniform_bool('u_material.hasDiffuse', False)
|
||||
glTexCoord2f(*textures[f.texs[n]][:2])
|
||||
|
||||
if mat and mat.Ka:
|
||||
shader.uniform_vec3('u_material.ambient', *mat.Ka)
|
||||
else:
|
||||
shader.uniform_vec3('u_material.ambient', 0.2, 0.2, 0.2)
|
||||
x, y, z = vertices[f.verts[n]]
|
||||
glVertex3f(x * sx, y * sy, z * sz)
|
||||
|
||||
if mat and mat.Kd:
|
||||
shader.uniform_vec3('u_material.diffuse', *mat.Kd)
|
||||
else:
|
||||
shader.uniform_vec3('u_material.diffuse', 0.8, 0.8, 0.8)
|
||||
glBegin(GL_TRIANGLES)
|
||||
for f in g.faces:
|
||||
point(f, vertices, normals, textures, 0)
|
||||
point(f, vertices, normals, textures, 1)
|
||||
point(f, vertices, normals, textures, 2)
|
||||
|
||||
if mat and mat.Ks:
|
||||
shader.uniform_vec3('u_material.specular', *mat.Ks)
|
||||
else:
|
||||
shader.uniform_vec3('u_material.specular', 0, 0, 0)
|
||||
if f.type == FACE_QUADS:
|
||||
point(f, vertices, normals, textures, 2)
|
||||
point(f, vertices, normals, textures, 3)
|
||||
point(f, vertices, normals, textures, 0)
|
||||
glEnd()
|
||||
|
||||
if mat:
|
||||
shader.uniform_float('u_material.shininess', mat.shininess)
|
||||
else:
|
||||
shader.uniform_float('u_material.shininess', 0)
|
||||
if tex_id:
|
||||
glBindTexture(GL_TEXTURE_2D, 0)
|
||||
glDisable(GL_TEXTURE_2D)
|
||||
|
||||
vbo.draw(shader, instances=instances)
|
||||
glPopAttrib()
|
||||
glPopMatrix()
|
||||
|
||||
def merge_groups(self, model):
|
||||
by_mat = defaultdict(list)
|
||||
for g in model.groups:
|
||||
if g.faces:
|
||||
by_mat[g.material].append(g)
|
||||
|
||||
groups = []
|
||||
for mat, gs in six.iteritems(by_mat):
|
||||
faces = []
|
||||
for g in gs:
|
||||
faces += g.faces
|
||||
groups.append(Group(mat, faces))
|
||||
return groups
|
||||
|
||||
def process_group(self, group, vertices, normals, textures):
|
||||
sx, sy, sz = self.scale
|
||||
max_texture = len(textures)
|
||||
has_texture = bool(textures) and any(any(n is not None for n in f.texs) for f in group.faces)
|
||||
has_normal = bool(normals) and any(any(n is not None for n in f.norms) for f in group.faces)
|
||||
buffer = []
|
||||
indices = []
|
||||
offsets = {}
|
||||
|
||||
for f in group.faces:
|
||||
verts = []
|
||||
for v, n, t in zip(f.verts, f.norms, f.texs):
|
||||
# Blender defines texture coordinates on faces even without textures.
|
||||
if t is not None and t >= max_texture:
|
||||
t = None
|
||||
if (v, n, t) in offsets:
|
||||
verts.append(offsets[v, n, t])
|
||||
else:
|
||||
index = len(offsets)
|
||||
verts.append(index)
|
||||
x, y, z = vertices[v]
|
||||
item = [sx * x, sy * y, sz * z]
|
||||
if has_normal:
|
||||
item += [0, 0, 0] if n is None else list(normals[n])
|
||||
if has_texture:
|
||||
item += [0, 0] if t is None else list(textures[t])
|
||||
offsets[v, n, t] = index
|
||||
buffer += item
|
||||
|
||||
for a, b in zip(verts[1:], verts[2:]):
|
||||
indices += [verts[0], a, b]
|
||||
|
||||
result = ModelVBO()
|
||||
result.has_normal = has_normal
|
||||
result.has_texture = has_texture
|
||||
result.offset_type = GL_UNSIGNED_SHORT if len(offsets) < 65536 else GL_UNSIGNED_INT
|
||||
result.data_buf = list_to_gl_buffer(buffer, 'f')
|
||||
result.index_buf = list_to_gl_buffer(indices, {
|
||||
GL_UNSIGNED_SHORT: 'H',
|
||||
GL_UNSIGNED_INT: 'I',
|
||||
}[result.offset_type])
|
||||
result.vertex_count = len(indices)
|
||||
return result
|
||||
glEndList()
|
||||
return display
|
||||
|
|
|
@ -66,11 +66,10 @@ class KeplerOrbit(object):
|
|||
self.__cos_argument = cos(self._argument)
|
||||
|
||||
def eccentric_anomaly(self, mean_anomaly):
|
||||
e1 = 0
|
||||
e2 = mean_anomaly
|
||||
e1 = mean_anomaly
|
||||
e2 = mean_anomaly + self.eccentricity * sin(e1)
|
||||
while abs(e1 - e2) > 0.000001:
|
||||
e1, e2 = e2, e2 - ((e2 - mean_anomaly - self.eccentricity * sin(e2)) /
|
||||
(1 - self.eccentricity * cos(e2)))
|
||||
e1, e2 = e2, mean_anomaly + self.eccentricity * sin(e2)
|
||||
return e2
|
||||
|
||||
def true_anomaly(self, mean_anomaly):
|
||||
|
|
|
@ -1,127 +0,0 @@
|
|||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
import sys
|
||||
from ctypes import pointer, byref, create_string_buffer, POINTER, cast
|
||||
|
||||
from pyglet.gl import *
|
||||
# noinspection PyUnresolvedReferences
|
||||
from six.moves import range
|
||||
|
||||
SHADERS_DIR = os.path.join(os.path.dirname(__file__), 'shaders')
|
||||
|
||||
|
||||
class CompileError(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
class glShader(object):
|
||||
def __init__(self, type):
|
||||
self.type = type
|
||||
|
||||
def __enter__(self):
|
||||
self.shader = glCreateShader(self.type)
|
||||
return self.shader
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
glDeleteShader(self.shader)
|
||||
|
||||
|
||||
class Program(object):
|
||||
@classmethod
|
||||
def load_file(cls, file):
|
||||
with open(os.path.join(SHADERS_DIR, file), 'rb') as f:
|
||||
return f.read()
|
||||
|
||||
@classmethod
|
||||
def compile_shader(cls, shader, source):
|
||||
buffer = create_string_buffer(source)
|
||||
glShaderSource(shader, 1, cast(pointer(pointer(buffer)), POINTER(POINTER(GLchar))), None)
|
||||
glCompileShader(shader)
|
||||
|
||||
succeeded = GLint()
|
||||
log_length = GLint()
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, byref(succeeded))
|
||||
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, byref(log_length))
|
||||
buffer = create_string_buffer(log_length.value + 1)
|
||||
glGetShaderInfoLog(shader, log_length.value, None, buffer)
|
||||
|
||||
if not succeeded:
|
||||
raise CompileError(buffer.value.decode('utf-8'))
|
||||
elif log_length.value:
|
||||
print('Warning:', file=sys.stderr)
|
||||
print(buffer.value.decode('utf-8'), file=sys.stderr)
|
||||
|
||||
def __init__(self, vertex_file, fragment_file):
|
||||
with glShader(GL_VERTEX_SHADER) as vertex_shader, glShader(GL_FRAGMENT_SHADER) as fragment_shader:
|
||||
self.compile_shader(vertex_shader, self.load_file(vertex_file))
|
||||
self.compile_shader(fragment_shader, self.load_file(fragment_file))
|
||||
|
||||
program = glCreateProgram()
|
||||
glAttachShader(program, vertex_shader)
|
||||
glAttachShader(program, fragment_shader)
|
||||
glLinkProgram(program)
|
||||
|
||||
succeeded = GLint()
|
||||
log_length = GLint()
|
||||
glGetProgramiv(program, GL_LINK_STATUS, byref(succeeded))
|
||||
if not succeeded:
|
||||
glGetProgramiv(program, GL_INFO_LOG_LENGTH, byref(log_length))
|
||||
buffer = create_string_buffer(log_length.value + 1)
|
||||
glGetProgramInfoLog(program, log_length.value, None, buffer)
|
||||
glDeleteProgram(program)
|
||||
raise CompileError(buffer.value)
|
||||
|
||||
glDetachShader(program, vertex_shader)
|
||||
glDetachShader(program, fragment_shader)
|
||||
|
||||
self.program = program
|
||||
self.attributes = self._variable_locations(GL_ACTIVE_ATTRIBUTES, glGetActiveAttrib, glGetAttribLocation)
|
||||
self.uniforms = self._variable_locations(GL_ACTIVE_UNIFORMS, glGetActiveUniform, glGetUniformLocation)
|
||||
|
||||
def vertex_attribute(self, name, size, type, normalized, stride, offset, divisor=None):
|
||||
location = self.attributes[name]
|
||||
glVertexAttribPointer(location, size, type, normalized, stride, offset)
|
||||
glEnableVertexAttribArray(location)
|
||||
if divisor:
|
||||
glVertexAttribDivisor(location, divisor)
|
||||
|
||||
def vertex_attribute_vec2(self, name, a, b):
|
||||
glVertexAttrib2f(self.attributes[name], a, b)
|
||||
|
||||
def vertex_attribute_vec3(self, name, a, b, c):
|
||||
glVertexAttrib3f(self.attributes[name], a, b, c)
|
||||
|
||||
def uniform_mat4(self, name, matrix):
|
||||
glUniformMatrix4fv(self.uniforms[name], 1, GL_FALSE, matrix)
|
||||
|
||||
def uniform_texture(self, name, index):
|
||||
glUniform1i(self.uniforms[name], index)
|
||||
|
||||
def uniform_float(self, name, value):
|
||||
glUniform1f(self.uniforms[name], value)
|
||||
|
||||
def uniform_bool(self, name, value):
|
||||
glUniform1i(self.uniforms[name], bool(value))
|
||||
|
||||
def uniform_vec2(self, name, a, b):
|
||||
glUniform2f(self.uniforms[name], a, b)
|
||||
|
||||
def uniform_vec3(self, name, a, b, c):
|
||||
glUniform3f(self.uniforms[name], a, b, c)
|
||||
|
||||
def uniform_vec4(self, name, a, b, c, d):
|
||||
glUniform4f(self.uniforms[name], a, b, c, d)
|
||||
|
||||
def _variable_locations(self, count_type, get_func, loc_func):
|
||||
variables = {}
|
||||
count = GLint()
|
||||
glGetProgramiv(self.program, count_type, byref(count))
|
||||
buffer = create_string_buffer(256)
|
||||
size = GLint()
|
||||
type = GLenum()
|
||||
|
||||
for index in range(count.value):
|
||||
get_func(self.program, index, 256, None, byref(size), byref(type), buffer)
|
||||
variables[buffer.value.decode('ascii')] = loc_func(self.program, buffer)
|
||||
return variables
|
|
@ -1,12 +0,0 @@
|
|||
#version 330 core
|
||||
|
||||
in float v_u;
|
||||
|
||||
out vec4 o_fragColor;
|
||||
|
||||
uniform vec3 u_color;
|
||||
uniform sampler1D u_transparency;
|
||||
|
||||
void main() {
|
||||
o_fragColor = vec4(u_color, texture(u_transparency, v_u).r);
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
#version 330 core
|
||||
|
||||
in vec2 a_position;
|
||||
in float a_u;
|
||||
|
||||
out float v_u;
|
||||
|
||||
uniform mat4 u_mvpMatrix;
|
||||
|
||||
void main() {
|
||||
gl_Position = u_mvpMatrix * vec4(a_position, 0, 1);
|
||||
v_u = a_u;
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
#version 330 core
|
||||
|
||||
in vec3 a_position;
|
||||
in vec3 a_normal;
|
||||
in vec2 a_uv;
|
||||
in vec3 a_translate;
|
||||
in float a_scale;
|
||||
|
||||
out vec2 v_uv;
|
||||
out vec3 v_normal;
|
||||
out vec3 v_position;
|
||||
out vec3 v_camDirection;
|
||||
|
||||
uniform mat4 u_mvpMatrix;
|
||||
uniform mat4 u_mvMatrix;
|
||||
uniform mat4 u_modelMatrix;
|
||||
|
||||
void main() {
|
||||
mat4 matrix = mat4(mat3(a_scale));
|
||||
matrix[3].xyz = a_translate;
|
||||
mat4 modelMatrix = u_modelMatrix * matrix;
|
||||
|
||||
gl_Position = u_mvpMatrix * matrix * vec4(a_position, 1);
|
||||
v_normal = normalize(vec3(modelMatrix * vec4(a_normal, 0)));
|
||||
v_uv = a_uv;
|
||||
v_position = (modelMatrix * vec4(a_position, 1)).xyz;
|
||||
v_camDirection = (u_mvMatrix * matrix * vec4(a_position, 1)).xyz;
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
#version 330 core
|
||||
|
||||
in vec2 v_uv;
|
||||
in vec3 v_normal;
|
||||
in vec3 v_position;
|
||||
|
||||
out vec4 o_fragColor;
|
||||
|
||||
uniform vec3 u_ambient;
|
||||
uniform vec3 u_diffuse;
|
||||
uniform vec3 u_sun;
|
||||
uniform sampler2D u_transparency;
|
||||
|
||||
void main() {
|
||||
vec3 incident = normalize(u_sun - v_position);
|
||||
vec3 diffuse = u_diffuse * clamp(dot(v_normal, incident) + 0.2, 0.0, 1.0);
|
||||
|
||||
o_fragColor = vec4(u_ambient + diffuse, texture(u_transparency, v_uv).r);
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
#version 330 core
|
||||
|
||||
in vec3 a_normal;
|
||||
in vec2 a_uv;
|
||||
|
||||
out vec2 v_uv;
|
||||
out vec3 v_normal;
|
||||
out vec3 v_position;
|
||||
|
||||
uniform float u_radius;
|
||||
uniform mat4 u_mvpMatrix;
|
||||
uniform mat4 u_modelMatrix;
|
||||
|
||||
void main() {
|
||||
vec3 position = u_radius * a_normal;
|
||||
v_uv = a_uv;
|
||||
v_normal = (u_modelMatrix * vec4(a_normal, 0)).xyz;
|
||||
v_position = (u_modelMatrix * vec4(position, 1)).xyz;
|
||||
gl_Position = u_mvpMatrix * vec4(position, 1);
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
#version 330 core
|
||||
|
||||
out vec4 o_fragColor;
|
||||
uniform vec4 u_color;
|
||||
|
||||
void main() {
|
||||
o_fragColor = u_color;
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
#version 330 core
|
||||
|
||||
in vec3 a_position;
|
||||
uniform mat4 u_mvpMatrix;
|
||||
|
||||
void main() {
|
||||
gl_Position = u_mvpMatrix * vec4(a_position, 1);
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
#version 330 core
|
||||
|
||||
in vec2 v_uv;
|
||||
in vec3 v_normal;
|
||||
in vec3 v_position;
|
||||
in vec3 v_camDirection;
|
||||
|
||||
out vec4 o_fragColor;
|
||||
|
||||
struct Material {
|
||||
bool hasDiffuse;
|
||||
sampler2D diffuseMap;
|
||||
vec3 ambient;
|
||||
vec3 diffuse;
|
||||
vec3 specular;
|
||||
float shininess;
|
||||
};
|
||||
|
||||
struct Sun {
|
||||
vec3 ambient;
|
||||
vec3 diffuse;
|
||||
vec3 specular;
|
||||
vec3 position;
|
||||
float intensity;
|
||||
};
|
||||
|
||||
uniform Sun u_sun;
|
||||
uniform Material u_material;
|
||||
|
||||
void main() {
|
||||
vec3 incident = normalize(u_sun.position - v_position);
|
||||
vec3 reflected = normalize(reflect(-incident, v_normal));
|
||||
|
||||
float diffuseIntensity = max(dot(v_normal, incident), 0.0);
|
||||
float shininess = pow(max(dot(normalize(v_camDirection), reflected), 0), u_material.shininess);
|
||||
|
||||
vec3 diffuse = u_material.hasDiffuse ? texture(u_material.diffuseMap, v_uv).rgb : vec3(1);
|
||||
vec3 ambient = u_material.ambient * u_sun.ambient * diffuse;
|
||||
vec3 specular = u_material.specular * u_sun.specular * max(shininess, 0) * diffuseIntensity;
|
||||
diffuse *= u_material.diffuse * u_sun.diffuse * diffuseIntensity;
|
||||
|
||||
o_fragColor = vec4((ambient + diffuse + specular) * u_sun.intensity, 1);
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
#version 330 core
|
||||
|
||||
in vec3 a_position;
|
||||
in vec3 a_normal;
|
||||
in vec2 a_uv;
|
||||
|
||||
out vec2 v_uv;
|
||||
out vec3 v_normal;
|
||||
out vec3 v_position;
|
||||
out vec3 v_camDirection;
|
||||
|
||||
uniform mat4 u_mvpMatrix;
|
||||
uniform mat4 u_mvMatrix;
|
||||
uniform mat4 u_modelMatrix;
|
||||
|
||||
void main() {
|
||||
gl_Position = u_mvpMatrix * vec4(a_position, 1);
|
||||
v_normal = normalize(vec3(u_modelMatrix * vec4(a_normal, 0)));
|
||||
v_uv = a_uv;
|
||||
v_position = (u_modelMatrix * vec4(a_position, 1)).xyz;
|
||||
v_camDirection = (u_mvMatrix * vec4(a_position, 1)).xyz;
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
#version 330 core
|
||||
|
||||
in vec2 v_uv;
|
||||
in vec3 v_normal;
|
||||
in vec3 v_position;
|
||||
in vec3 v_camDirection;
|
||||
in mat3 v_TBN;
|
||||
|
||||
out vec4 o_fragColor;
|
||||
|
||||
struct Surface {
|
||||
sampler2D diffuseMap;
|
||||
bool hasNormal;
|
||||
sampler2D normalMap;
|
||||
bool hasSpecular;
|
||||
sampler2D specularMap;
|
||||
bool hasEmission;
|
||||
sampler2D emissionMap;
|
||||
vec3 ambient;
|
||||
vec3 diffuse;
|
||||
vec3 specular;
|
||||
vec3 emission;
|
||||
float shininess;
|
||||
};
|
||||
|
||||
struct Sun {
|
||||
vec3 ambient;
|
||||
vec3 diffuse;
|
||||
vec3 specular;
|
||||
vec3 position;
|
||||
float intensity;
|
||||
};
|
||||
|
||||
uniform Sun u_sun;
|
||||
uniform Surface u_planet;
|
||||
|
||||
void main() {
|
||||
vec3 normal = u_planet.hasNormal ? normalize(v_TBN * texture(u_planet.normalMap, v_uv).rgb * 2 - 1) : v_normal;
|
||||
vec3 diffuse = texture(u_planet.diffuseMap, v_uv).rgb;
|
||||
vec3 specular = u_planet.hasSpecular ? texture(u_planet.specularMap, v_uv).rgb : vec3(1);
|
||||
vec3 emission = u_planet.hasEmission ? texture(u_planet.emissionMap, v_uv).rgb : vec3(1);
|
||||
|
||||
vec3 incident = normalize(u_sun.position - v_position);
|
||||
vec3 reflected = normalize(reflect(-incident, normal));
|
||||
|
||||
float diffuseIntensity = max(dot(normal, incident), 0.0);
|
||||
float shininess = pow(max(dot(normalize(v_camDirection), reflected), 0), u_planet.shininess);
|
||||
|
||||
vec3 ambient = u_planet.ambient * u_sun.ambient * diffuse;
|
||||
diffuse *= u_planet.diffuse * u_sun.diffuse * diffuseIntensity;
|
||||
emission *= u_planet.emission * (1 - min(diffuseIntensity * 2, 1));
|
||||
specular *= u_planet.specular * u_sun.specular * max(shininess, 0) * diffuseIntensity;
|
||||
|
||||
o_fragColor = vec4((ambient + diffuse + emission + specular) * u_sun.intensity, 1);
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
#version 330 core
|
||||
|
||||
in vec3 a_normal;
|
||||
in vec2 a_tangent;
|
||||
in vec2 a_uv;
|
||||
|
||||
out vec2 v_uv;
|
||||
out vec3 v_normal;
|
||||
out vec3 v_position;
|
||||
out vec3 v_camDirection;
|
||||
out mat3 v_TBN;
|
||||
|
||||
uniform float u_radius;
|
||||
uniform mat4 u_mvpMatrix;
|
||||
uniform mat4 u_mvMatrix;
|
||||
uniform mat4 u_modelMatrix;
|
||||
|
||||
void main() {
|
||||
vec3 position = u_radius * a_normal;
|
||||
|
||||
gl_Position = u_mvpMatrix * vec4(position, 1);
|
||||
|
||||
v_normal = normalize(vec3(u_modelMatrix * vec4(a_normal, 0)));
|
||||
v_uv = a_uv;
|
||||
v_position = (u_modelMatrix * vec4(position, 1)).xyz;
|
||||
v_camDirection = (u_mvMatrix * vec4(position, 1)).xyz;
|
||||
|
||||
vec3 tangent = normalize((u_modelMatrix * vec4(a_tangent, a_normal.z, 0)).xyz);
|
||||
v_TBN = mat3(tangent, cross(tangent, v_normal), v_normal);
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
#version 330 core
|
||||
|
||||
in vec3 v_position;
|
||||
in float v_u;
|
||||
|
||||
out vec4 o_fragColor;
|
||||
|
||||
uniform vec3 u_sun;
|
||||
uniform vec3 u_planet;
|
||||
uniform float u_planetRadius;
|
||||
uniform float u_ambient;
|
||||
uniform sampler1D u_texture;
|
||||
|
||||
void main() {
|
||||
vec3 incident = v_position - u_sun;
|
||||
vec3 plane_normal = u_planet - u_sun;
|
||||
vec3 plane_intersect = dot(plane_normal, plane_normal) / dot(incident, plane_normal) * incident;
|
||||
o_fragColor = texture(u_texture, v_u);
|
||||
if (length(plane_intersect) < length(incident) &&
|
||||
distance(plane_intersect, plane_normal) <= u_planetRadius)
|
||||
o_fragColor.rgb *= u_ambient;
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
#version 330 core
|
||||
|
||||
in vec2 a_position;
|
||||
in float a_u;
|
||||
|
||||
out vec3 v_position;
|
||||
out float v_u;
|
||||
|
||||
uniform mat4 u_mvpMatrix;
|
||||
uniform mat4 u_modelMatrix;
|
||||
|
||||
void main() {
|
||||
gl_Position = u_mvpMatrix * vec4(a_position, 0, 1);
|
||||
v_position = (u_modelMatrix * vec4(a_position, 0, 1)).xyz;
|
||||
v_u = a_u;
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
#version 330 core
|
||||
|
||||
in vec3 v_direction;
|
||||
out vec4 o_fragColor;
|
||||
uniform bool u_lines;
|
||||
uniform samplerCube u_skysphere;
|
||||
uniform samplerCube u_constellation;
|
||||
|
||||
void main() {
|
||||
o_fragColor = texture(u_skysphere, v_direction);
|
||||
if (u_lines)
|
||||
o_fragColor += texture(u_constellation, v_direction);
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
#version 330 core
|
||||
|
||||
in vec3 a_direction;
|
||||
out vec3 v_direction;
|
||||
uniform mat4 u_mvpMatrix;
|
||||
|
||||
void main() {
|
||||
gl_Position = (u_mvpMatrix * vec4(a_direction, 1)).xyww;
|
||||
v_direction = a_direction;
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
#version 330 core
|
||||
|
||||
in vec2 v_uv;
|
||||
out vec4 o_fragColor;
|
||||
uniform sampler2D u_emission;
|
||||
|
||||
void main() {
|
||||
o_fragColor = vec4(texture(u_emission, v_uv).rgb, 1);
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
#version 330 core
|
||||
|
||||
in vec3 a_normal;
|
||||
in vec2 a_uv;
|
||||
|
||||
out vec2 v_uv;
|
||||
|
||||
uniform float u_radius;
|
||||
uniform mat4 u_mvpMatrix;
|
||||
|
||||
void main() {
|
||||
gl_Position = u_mvpMatrix * vec4(u_radius * a_normal, 1);
|
||||
v_uv = a_uv;
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
#version 330 core
|
||||
|
||||
in vec2 v_uv;
|
||||
|
||||
out vec4 o_fragColor;
|
||||
|
||||
uniform sampler2D u_alpha;
|
||||
uniform vec3 u_color;
|
||||
|
||||
void main() {
|
||||
o_fragColor = vec4(u_color, texture(u_alpha, v_uv).r);
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
#version 330 core
|
||||
|
||||
in vec2 a_rc;
|
||||
in vec2 a_tex;
|
||||
|
||||
out vec2 v_uv;
|
||||
|
||||
uniform mat4 u_projMatrix;
|
||||
uniform vec2 u_start;
|
||||
|
||||
void main() {
|
||||
gl_Position = u_projMatrix * vec4(a_rc.y * 8 + u_start.x, a_rc.x * 16 + u_start.y, 0, 1);
|
||||
v_uv = vec2(a_tex.x * 8 / 1024, a_tex.y);
|
||||
}
|
|
@ -1,57 +1,83 @@
|
|||
from __future__ import print_function, division
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
from PIL import Image
|
||||
from pyglet.gl import GLint, glGetIntegerv, GL_MAX_TEXTURE_SIZE
|
||||
from ctypes import byref
|
||||
|
||||
from punyverse.texture import max_texture_size
|
||||
|
||||
max_texture = max_texture_size()
|
||||
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 / max(width, height)
|
||||
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
|
||||
|
||||
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:
|
||||
from PIL import Image
|
||||
|
||||
def shrink(file):
|
||||
image = Image.open(file)
|
||||
width, height = image.size
|
||||
def get_image(image):
|
||||
return Image.open(image)
|
||||
|
||||
if fits(width, height):
|
||||
print('no need')
|
||||
return
|
||||
|
||||
for attempt, new_size in [(4096, 'large'), (2048, 'medium')]:
|
||||
width, height = resize(width, height, attempt)
|
||||
if fits(width, height):
|
||||
size = new_size
|
||||
break
|
||||
else:
|
||||
width, height = resize(width, height, 1024) # 1024 is minimum
|
||||
size = 'small'
|
||||
|
||||
print('size %s, %dx%d...' % (size, width, height), end=' ')
|
||||
name = make_name(file, size)
|
||||
if not os.path.exists(name):
|
||||
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))
|
||||
image.resize((width, height), Image.ANTIALIAS).save(name)
|
||||
print('saved to:', os.path.basename(name))
|
||||
else:
|
||||
print('already there')
|
||||
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), end=' ')
|
||||
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',
|
||||
|
@ -73,7 +99,6 @@ textures = [
|
|||
'moons/mimas.jpg',
|
||||
]
|
||||
|
||||
|
||||
def main():
|
||||
punyverse = os.path.dirname(__file__)
|
||||
try:
|
||||
|
@ -90,6 +115,5 @@ def main():
|
|||
else:
|
||||
print('exists not')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
@ -1,21 +1,31 @@
|
|||
from __future__ import print_function
|
||||
|
||||
from pyglet import image
|
||||
from pyglet.gl import *
|
||||
from ctypes import c_int, byref, c_uint
|
||||
import os.path
|
||||
import struct
|
||||
from ctypes import c_int, byref
|
||||
import itertools
|
||||
from io import BytesIO
|
||||
|
||||
import six
|
||||
from pyglet import image
|
||||
from pyglet.gl import *
|
||||
from six.moves import range
|
||||
from six.moves import zip
|
||||
|
||||
try:
|
||||
from ._glgeom import bgr_to_rgb, flip_vertical
|
||||
from punyverse._glgeom import bgr_to_rgb, flip_vertical
|
||||
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
|
||||
|
||||
from six.moves import range
|
||||
|
||||
def bgr_to_rgb(source, width, height, alpha=False):
|
||||
length = len(source)
|
||||
|
@ -27,65 +37,88 @@ except ImportError:
|
|||
for x in range(width):
|
||||
offset = y * row + x * depth
|
||||
for i in range(depth2):
|
||||
result[offset + i] = source[offset + depth2 - i - 1]
|
||||
result[offset+i] = source[offset+depth2-i-1]
|
||||
if alpha:
|
||||
result[offset + depth2] = source[offset + depth2]
|
||||
result[offset+depth2] = source[offset+depth2]
|
||||
return six.binary_type(result)
|
||||
|
||||
|
||||
def flip_vertical(source, width, height):
|
||||
length = len(source)
|
||||
row = length // height
|
||||
result = bytearray(length)
|
||||
for y1 in range(height):
|
||||
y2 = height - y1 - 1
|
||||
result[y1 * row:y1 * row + row] = source[y2 * row:y2 * row + row]
|
||||
result[y1*row:y1*row+row] = source[y2*row:y2*row+row]
|
||||
return six.binary_type(result)
|
||||
else:
|
||||
magick = False
|
||||
|
||||
__all__ = ['load_texture', 'load_alpha_mask', 'load_image', 'get_best_texture', 'max_texture_size',
|
||||
'get_cube_map', 'load_texture_1d']
|
||||
__all__ = ['load_texture', 'load_clouds', 'load_image', 'pil_load']
|
||||
|
||||
id = 0
|
||||
cache = {}
|
||||
|
||||
max_texture = None
|
||||
power_of_two = None
|
||||
badcard = False
|
||||
bgra = False
|
||||
|
||||
def is_power2(num):
|
||||
return num != 0 and ((num & (num - 1)) == 0)
|
||||
|
||||
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')
|
||||
|
||||
#bgra = gl_info.have_extension('GL_EXT_bgra')
|
||||
|
||||
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 = six.binary_type(data)
|
||||
data = str(data)
|
||||
size = len(data)
|
||||
height = -1
|
||||
width = -1
|
||||
content_type = ''
|
||||
|
||||
# handle GIFs
|
||||
if size >= 10 and data[:6] in (b'GIF87a', b'GIF89a'):
|
||||
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])
|
||||
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(b'\211PNG\r\n\032\n') and data[12:16] == b'IHDR':
|
||||
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])
|
||||
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(b'\211PNG\r\n\032\n'):
|
||||
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])
|
||||
w, h = struct.unpack(">LL", data[8:16])
|
||||
width = int(w)
|
||||
height = int(h)
|
||||
|
||||
# handle JPEGs
|
||||
elif size >= 2 and data.startswith(b'\377\330'):
|
||||
elif (size >= 2) and data.startswith('\377\330'):
|
||||
content_type = 'image/jpeg'
|
||||
jpeg = BytesIO(data)
|
||||
jpeg.read(2)
|
||||
|
@ -98,11 +131,13 @@ def image_info(data):
|
|||
b = jpeg.read(1)
|
||||
if 0xC0 <= ord(b) <= 0xC3:
|
||||
jpeg.read(3)
|
||||
height, width = struct.unpack('>HH', jpeg.read(4))
|
||||
h, w = struct.unpack(">HH", jpeg.read(4))
|
||||
break
|
||||
else:
|
||||
jpeg.read(int(struct.unpack('>H', jpeg.read(2))[0]) - 2)
|
||||
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:
|
||||
|
@ -111,26 +146,15 @@ def image_info(data):
|
|||
return content_type, width, height
|
||||
|
||||
|
||||
def glGetInteger(index):
|
||||
buf = c_int()
|
||||
glGetIntegerv(index, byref(buf))
|
||||
return buf.value
|
||||
|
||||
|
||||
def max_texture_size():
|
||||
size = glGetInteger(GL_MAX_TEXTURE_SIZE)
|
||||
if gl_info.get_vendor() == 'Intel':
|
||||
# Intel can't seem to handle more than 4096
|
||||
size = min(size, 4096)
|
||||
return size
|
||||
|
||||
|
||||
def check_size(width, height):
|
||||
max_texture = max_texture_size()
|
||||
|
||||
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):
|
||||
|
@ -139,181 +163,115 @@ def load_image(file, path):
|
|||
try:
|
||||
file = open(path, 'rb')
|
||||
except IOError:
|
||||
print('does not exist')
|
||||
raise ValueError('Texture does not exist')
|
||||
|
||||
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)
|
||||
|
||||
try:
|
||||
raw = image.load(path, file=file)
|
||||
except Exception:
|
||||
print('cannot be loaded')
|
||||
raise ValueError('cannot be loaded')
|
||||
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()
|
||||
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 gl_info.have_extension('GL_EXT_bgra'):
|
||||
mode = GL_BGRA if 'A' in raw.format else GL_BGR
|
||||
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 = 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)
|
||||
|
||||
texture = raw.get_data('RGBA', width * 4)
|
||||
return path, width, height, len(raw.format), mode, flip_vertical(texture, width, height)
|
||||
|
||||
|
||||
def get_file_path(file):
|
||||
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)
|
||||
return path, file
|
||||
|
||||
|
||||
def create_texture():
|
||||
buffer = GLuint()
|
||||
glGenTextures(1, byref(buffer))
|
||||
id = buffer.value
|
||||
return id
|
||||
|
||||
|
||||
def delete_texture(id):
|
||||
buffer = GLuint(id)
|
||||
glDeleteTextures(1, byref(buffer))
|
||||
|
||||
|
||||
def get_internal_mode(mode):
|
||||
return {
|
||||
GL_RGB: GL_RGB8,
|
||||
GL_BGR: GL_RGB8,
|
||||
GL_RGBA: GL_RGBA8,
|
||||
GL_BGRA: GL_RGBA8,
|
||||
}[mode]
|
||||
|
||||
|
||||
def load_texture(file, clamp=False):
|
||||
path, file = get_file_path(file)
|
||||
if path in cache:
|
||||
return cache[path]
|
||||
|
||||
path, width, height, depth, mode, texture = load_image(file, path)
|
||||
|
||||
id = create_texture()
|
||||
glBindTexture(GL_TEXTURE_2D, id)
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, get_internal_mode(mode), width, height, 0, mode, GL_UNSIGNED_BYTE, texture)
|
||||
glGenerateMipmap(GL_TEXTURE_2D)
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)
|
||||
|
||||
if clamp:
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
|
||||
|
||||
if gl_info.have_extension('GL_EXT_texture_filter_anisotropic'):
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, glGetInteger(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT))
|
||||
|
||||
cache[path] = id
|
||||
return id
|
||||
|
||||
|
||||
def load_texture_1d(file, clamp=False):
|
||||
path, file = get_file_path(file)
|
||||
path, width, height, depth, mode, texture = load_image(file, path)
|
||||
|
||||
id = create_texture()
|
||||
glBindTexture(GL_TEXTURE_1D, id)
|
||||
|
||||
glTexImage1D(GL_TEXTURE_1D, 0, get_internal_mode(mode), width, 0, mode, GL_UNSIGNED_BYTE, texture)
|
||||
glGenerateMipmap(GL_TEXTURE_1D)
|
||||
|
||||
if clamp:
|
||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
|
||||
|
||||
if gl_info.have_extension('GL_EXT_texture_filter_anisotropic'):
|
||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAX_ANISOTROPY_EXT, glGetInteger(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT))
|
||||
|
||||
return id
|
||||
|
||||
|
||||
def load_alpha_mask(file, clamp=False):
|
||||
path, file = get_file_path(file)
|
||||
path, width, height, depth, mode, texture = load_image(file, path)
|
||||
|
||||
if depth != 1:
|
||||
texture = texture[::depth]
|
||||
|
||||
buffer = GLuint()
|
||||
buffer = c_uint()
|
||||
glGenTextures(1, byref(buffer))
|
||||
id = buffer.value
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, id)
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, texture)
|
||||
glGenerateMipmap(GL_TEXTURE_2D)
|
||||
|
||||
if badcard:
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, mode, width, height, 0, mode, GL_UNSIGNED_BYTE, texture)
|
||||
else:
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)
|
||||
gluBuild2DMipmaps(GL_TEXTURE_2D, depth, width, height, mode, GL_UNSIGNED_BYTE, texture)
|
||||
|
||||
cache[path] = id
|
||||
|
||||
return id
|
||||
|
||||
|
||||
def pil_load(file):
|
||||
from PIL 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_uint()
|
||||
glGenTextures(1, byref(buffer))
|
||||
id = buffer.value
|
||||
|
||||
pixels = bytearray(len(texture) * 4)
|
||||
white = b'\xff'[0]
|
||||
pixels[:] = itertools.chain.from_iterable(zip(itertools.repeat(white), itertools.repeat(white),
|
||||
itertools.repeat(white),
|
||||
itertools.islice(texture, 0, None, depth)))
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, id)
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, six.binary_type(pixels))
|
||||
|
||||
if clamp:
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
|
||||
|
||||
if gl_info.have_extension('GL_EXT_texture_filter_anisotropic'):
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, glGetInteger(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT))
|
||||
cache[path] = id
|
||||
|
||||
return id
|
||||
|
||||
|
||||
def get_cube_map(files, callback=None):
|
||||
assert len(files) == 6
|
||||
callback = callback or (lambda index, file: None)
|
||||
|
||||
id = create_texture()
|
||||
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, id)
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_LEVEL, 0)
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, 0)
|
||||
for index, (file, part) in enumerate(zip(files, [
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_X,
|
||||
GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
|
||||
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
|
||||
GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
|
||||
])):
|
||||
try:
|
||||
path, file = get_file_path(file)
|
||||
callback(index, file)
|
||||
path, width, height, depth, mode, texture = load_image(file, path)
|
||||
except Exception:
|
||||
delete_texture(id)
|
||||
raise
|
||||
glTexImage2D(part, 0, get_internal_mode(mode), width, height, 0, mode, GL_UNSIGNED_BYTE, texture)
|
||||
|
||||
return id
|
||||
|
||||
|
||||
def get_best_texture(info, loader=load_texture, optional=False, **kwargs):
|
||||
if isinstance(info, list):
|
||||
for item in info:
|
||||
try:
|
||||
return loader(item, **kwargs)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
return loader(info, **kwargs)
|
||||
if not optional:
|
||||
raise ValueError('No texture found')
|
||||
|
|
279
punyverse/ui.py
|
@ -1,279 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
from __future__ import division
|
||||
import os
|
||||
import time
|
||||
from math import hypot
|
||||
from operator import attrgetter
|
||||
|
||||
import pyglet
|
||||
import six
|
||||
from pyglet.gl import *
|
||||
from pyglet.window import key, mouse
|
||||
|
||||
from punyverse.glgeom import *
|
||||
|
||||
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 Punyverse(pyglet.window.Window):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Punyverse, self).__init__(*args, **kwargs)
|
||||
|
||||
self.fps = 0
|
||||
self.info = True
|
||||
self.debug = False
|
||||
self.orbit = True
|
||||
self.running = True
|
||||
self.moving = True
|
||||
self.info_precise = False
|
||||
self.atmosphere = True
|
||||
self.cloud = True
|
||||
self.constellations = False
|
||||
|
||||
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.key_handler = {}
|
||||
self.mouse_press_handler = {}
|
||||
|
||||
self.exclusive = False
|
||||
self.modifiers = 0
|
||||
|
||||
self.world = None
|
||||
|
||||
def initialize(self, world):
|
||||
self.world = world
|
||||
|
||||
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.world.tick_length) + 1
|
||||
if index < len(self.ticks):
|
||||
self.world.tick_length = self.ticks[index]
|
||||
|
||||
def decrement_tick():
|
||||
index = self.ticks.index(self.world.tick_length) - 1
|
||||
if index >= 0:
|
||||
self.world.tick_length = self.ticks[index]
|
||||
|
||||
self.key_handler = {
|
||||
key.ESCAPE: pyglet.app.exit,
|
||||
key.NUM_ADD: speed_incrementer(self.world.cam, 1),
|
||||
key.NUM_SUBTRACT: speed_incrementer(self.world.cam, -1),
|
||||
key.NUM_MULTIPLY: speed_incrementer(self.world.cam, 10),
|
||||
key.NUM_DIVIDE: speed_incrementer(self.world.cam, -10),
|
||||
key.PAGEUP: speed_incrementer(self.world.cam, 100),
|
||||
key.PAGEDOWN: speed_incrementer(self.world.cam, -100),
|
||||
key.HOME: speed_incrementer(self.world.cam, 1000),
|
||||
key.END: speed_incrementer(self.world.cam, -1000),
|
||||
key.R: self.world.cam.reset_roll,
|
||||
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.L: attribute_toggler(self, 'constellations'),
|
||||
key.ENTER: attribute_toggler(self, 'running'),
|
||||
key.INSERT: increment_tick,
|
||||
key.DELETE: decrement_tick,
|
||||
key.SPACE: self.world.spawn_asteroid,
|
||||
key.E: lambda: self.set_exclusive_mouse(False),
|
||||
key.F: lambda: self.set_fullscreen(not self.fullscreen),
|
||||
}
|
||||
|
||||
self.mouse_press_handler = {
|
||||
mouse.LEFT: self.world.spawn_asteroid,
|
||||
mouse.RIGHT: attribute_toggler(self, 'moving'),
|
||||
}
|
||||
|
||||
glClearColor(0, 0, 0, 1)
|
||||
glClearDepth(1.0)
|
||||
|
||||
glDepthFunc(GL_LEQUAL)
|
||||
glEnable(GL_DEPTH_TEST)
|
||||
glShadeModel(GL_SMOOTH)
|
||||
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
||||
|
||||
self.info_engine = FontEngine(self.world.activate_shader('text'))
|
||||
self.circle = Circle(10, 20, self.world.activate_shader('line'))
|
||||
|
||||
pyglet.clock.schedule(self.update)
|
||||
self.on_resize(self.width, self.height) # On resize handler does nothing unless it's loaded
|
||||
|
||||
def screenshot(self):
|
||||
image = pyglet.image.get_buffer_manager().get_color_buffer()
|
||||
if hasattr(self, '_hwnd') and not self.modifiers & key.MOD_CTRL:
|
||||
from ctypes import windll
|
||||
from PIL import Image
|
||||
import tempfile
|
||||
CF_BITMAP = 2
|
||||
|
||||
image = Image.frombytes(image.format, (image.width, image.height), image.get_image_data().data)
|
||||
image = image.convert('RGB').transpose(Image.FLIP_TOP_BOTTOM)
|
||||
fd, filename = tempfile.mkstemp('.bmp')
|
||||
try:
|
||||
with os.fdopen(fd, 'wb') as file:
|
||||
image.save(file, 'BMP')
|
||||
if isinstance(filename, six.binary_type):
|
||||
filename = filename.decode('mbcs' if os.name == 'nt' else 'utf8')
|
||||
image = windll.user32.LoadImageW(None, filename, 0, 0, 0, 0x10)
|
||||
windll.user32.OpenClipboard(self._hwnd)
|
||||
windll.user32.EmptyClipboard()
|
||||
windll.user32.SetClipboardData(CF_BITMAP, image)
|
||||
windll.user32.CloseClipboard()
|
||||
finally:
|
||||
os.remove(filename)
|
||||
else:
|
||||
image.save(os.path.expanduser('~/punyverse.png'))
|
||||
|
||||
def set_exclusive_mouse(self, exclusive):
|
||||
super(Punyverse, self).set_exclusive_mouse(exclusive)
|
||||
self.exclusive = exclusive
|
||||
|
||||
def on_mouse_press(self, x, y, button, modifiers):
|
||||
self.modifiers = 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.world.cam.mouse_move(dx * MOUSE_SENSITIVITY, dy * MOUSE_SENSITIVITY)
|
||||
|
||||
def on_key_press(self, symbol, modifiers):
|
||||
self.modifiers = modifiers
|
||||
if symbol == key.Q:
|
||||
self.screenshot()
|
||||
|
||||
if self.exclusive: # Only handle keyboard input if mouse is grabbed
|
||||
if symbol in self.key_handler:
|
||||
self.key_handler[symbol]()
|
||||
elif symbol == key.A:
|
||||
self.world.cam.roll_left = True
|
||||
elif symbol == key.S:
|
||||
self.world.cam.roll_right = True
|
||||
|
||||
def on_key_release(self, symbol, modifiers):
|
||||
if symbol == key.A:
|
||||
self.world.cam.roll_left = False
|
||||
elif symbol == key.S:
|
||||
self.world.cam.roll_right = False
|
||||
|
||||
def on_resize(self, width, height):
|
||||
if not width or not height:
|
||||
# Sometimes this happen for no reason?
|
||||
return
|
||||
if hasattr(self, 'get_viewport_size'):
|
||||
width, height = self.get_viewport_size()
|
||||
glViewport(0, 0, width, height)
|
||||
self.world.resize(width, height)
|
||||
|
||||
def on_mouse_scroll(self, x, y, scroll_x, scroll_y):
|
||||
self.world.cam.speed += scroll_y * 50 + scroll_x * 500
|
||||
|
||||
def get_time_per_second(self):
|
||||
time = self.world.tick_length
|
||||
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)
|
||||
return result
|
||||
|
||||
def update(self, dt):
|
||||
self.world.update(dt, move=self.exclusive and self.moving, tick=self.running)
|
||||
|
||||
def on_draw(self):
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
|
||||
c = self.world.cam
|
||||
x, y, z = c.x, c.y, c.z
|
||||
|
||||
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:
|
||||
entity.draw(self)
|
||||
|
||||
if self.info:
|
||||
width, height = self.get_size()
|
||||
projection = Matrix4f([
|
||||
2 / width, 0, 0, 0,
|
||||
0, -2 / height, 0, 0,
|
||||
0, 0, -1, 0,
|
||||
-1, 1, 0, 1,
|
||||
])
|
||||
|
||||
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.world.cam.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.world.cam.speed, self.get_time_per_second()))
|
||||
|
||||
glEnable(GL_BLEND)
|
||||
shader = self.world.activate_shader('text')
|
||||
shader.uniform_mat4('u_projMatrix', projection)
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, self.world.font_tex)
|
||||
shader.uniform_texture('u_alpha', 0)
|
||||
shader.uniform_vec3('u_color', 1, 1, 1)
|
||||
shader.uniform_vec2('u_start', 10, 10)
|
||||
|
||||
self.info_engine.draw(info)
|
||||
with self.info_engine.vao:
|
||||
glDrawArrays(GL_TRIANGLES, 0, self.info_engine.vertex_count)
|
||||
|
||||
glDisable(GL_BLEND)
|
||||
|
||||
glLineWidth(2)
|
||||
mvp = projection * Matrix4f.from_angles((width / 2, height /2, 0))
|
||||
shader = self.world.activate_shader('line')
|
||||
shader.uniform_vec4('u_color', 0, 1, 0, 1)
|
||||
shader.uniform_mat4('u_mvpMatrix', mvp)
|
||||
with self.circle.vao:
|
||||
glDrawArrays(GL_LINE_LOOP, 0, self.circle.vertex_count)
|
||||
glLineWidth(1)
|
|
@ -1,18 +0,0 @@
|
|||
class cached_property(object):
|
||||
def __init__(self, func, name=None):
|
||||
self.func = func
|
||||
self.name = name or func.__name__
|
||||
self.__doc__ = getattr(func, '__doc__')
|
||||
|
||||
def __get__(self, instance, owner=None):
|
||||
if instance is None:
|
||||
return self
|
||||
result = instance.__dict__[self.name] = self.func(instance)
|
||||
return result
|
||||
|
||||
def __set__(self, instance, value):
|
||||
if value is None:
|
||||
if self.name in instance.__dict__:
|
||||
del instance.__dict__[self.name]
|
||||
else:
|
||||
instance.__dict__[self.name] = value
|
|
@ -7,7 +7,7 @@
|
|||
"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",
|
||||
"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,
|
||||
|
@ -15,22 +15,23 @@
|
|||
"length": 63.7,
|
||||
"bodies": {
|
||||
"sun": {
|
||||
"texture": ["sun.jpg"],
|
||||
"texture": ["sun.jpg", [0.99, 0.97, 0.66, 1]],
|
||||
"radius": 80000,
|
||||
"pitch": -90,
|
||||
"yaw": 7.25,
|
||||
"mass": 1.9891e+30,
|
||||
"rotation": 2164320,
|
||||
"light_source": true,
|
||||
"type": "star",
|
||||
"atmosphere": {
|
||||
"glow_color": [0.92, 0.92, 0.82],
|
||||
"glow_texture": "glow.png",
|
||||
"glow_size": 300
|
||||
"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"],
|
||||
"texture": ["mercury.jpg", "mercury_small.jpg", [0.44, 0.43, 0.43, 1]],
|
||||
"radius": 2439.7,
|
||||
"z": "0.466697 * AU",
|
||||
"pitch": -90,
|
||||
|
@ -39,7 +40,7 @@
|
|||
"rotation": 5067014
|
||||
},
|
||||
"venus": {
|
||||
"texture": ["venus.jpg"],
|
||||
"texture": ["venus.jpg", [0.655, 0.38, 0.1, 1]],
|
||||
"radius": 6051.8,
|
||||
"z": "0.723327 * AU",
|
||||
"pitch": -90,
|
||||
|
@ -48,7 +49,7 @@
|
|||
"rotation": -20996798
|
||||
},
|
||||
"earth": {
|
||||
"texture": ["earth.jpg", "earth_large.jpg", "earth_medium.jpg", "earth_small.jpg"],
|
||||
"texture": ["earth.jpg", "earth_medium.jpg", "earth_small.jpg", [0.11, 0.32, 0.43, 1]],
|
||||
"radius": 6378.1,
|
||||
"z": "AU",
|
||||
"pitch": -90,
|
||||
|
@ -57,19 +58,16 @@
|
|||
"mass": 5.97219e+24,
|
||||
"rotation": 86400,
|
||||
"division": 90,
|
||||
"normal_map": ["earth_normal.jpg", "earth_normal_small.jpg"],
|
||||
"specular_map": ["earth_specular.jpg", "earth_specular_small.jpg"],
|
||||
"emission_map": ["earth_emission.jpg", "earth_emission_medium.jpg", "earth_emission_small.jpg"],
|
||||
"normal": "earth_normal.jpg",
|
||||
"atmosphere": {
|
||||
"cloud_texture": ["cloudmap.jpg", "cloudmap_small.jpg"],
|
||||
"glow_color": [0.11, 0.32, 0.43],
|
||||
"glow_texture": "glow.png",
|
||||
"glow_size": 30
|
||||
"diffuse_texture": "atmosphere_earth.png",
|
||||
"diffuse_size": 30
|
||||
},
|
||||
"orbit_distance": "AU",
|
||||
"satellites": {
|
||||
"moon": {
|
||||
"texture": ["moon.jpg", "moon_medium.jpg", "moon_small.jpg"],
|
||||
"texture": ["moon.jpg", "moon_medium.jpg", "moon_small.jpg", [0.53, 0.53, 0.53, 1]],
|
||||
"radius": 1738.14,
|
||||
"distance": 38439,
|
||||
"sma": 384399,
|
||||
|
@ -96,7 +94,7 @@
|
|||
}
|
||||
},
|
||||
"mars": {
|
||||
"texture": ["mars.jpg", "mars_large.jpg", "mars_medium.jpg", "mars_small.jpg"],
|
||||
"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,
|
||||
|
@ -115,7 +113,7 @@
|
|||
}
|
||||
},
|
||||
"jupiter": {
|
||||
"texture": ["jupiter.jpg", "jupiter_medium.jpg", "jupiter_small.jpg"],
|
||||
"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",
|
||||
|
@ -126,7 +124,7 @@
|
|||
"orbit_distance": "3 * AU",
|
||||
"satellites": {
|
||||
"io": {
|
||||
"texture": ["moons/io.jpg", "moons/io_small.jpg"],
|
||||
"texture": ["moons/io.jpg", "moons/io_small.jpg", [0.62, 0.56, 0.35, 1]],
|
||||
"radius": "1821.3 * 5",
|
||||
"distance": 126510,
|
||||
"sma": 421700,
|
||||
|
@ -136,7 +134,7 @@
|
|||
"eccentricity": 0.0041
|
||||
},
|
||||
"europa": {
|
||||
"texture": ["moons/europa.jpg", "moons/europa_small.jpg"],
|
||||
"texture": ["moons/europa.jpg", "moons/europa_small.jpg", [0.77, 0.74, 0.65, 1]],
|
||||
"radius": "1560.8 * 5",
|
||||
"distance": 201270,
|
||||
"sma": 670900,
|
||||
|
@ -146,7 +144,7 @@
|
|||
"eccentricity": 0.009
|
||||
},
|
||||
"ganymede": {
|
||||
"texture": ["moons/ganymede.jpg", "moons/ganymede_small.jpg"],
|
||||
"texture": ["moons/ganymede.jpg", "moons/ganymede_small.jpg", [0.52, 0.47, 0.46, 1]],
|
||||
"radius": "2634.1 * 5",
|
||||
"distance": 321120,
|
||||
"sma": 1070400,
|
||||
|
@ -156,7 +154,7 @@
|
|||
"eccentricity": 0.0013
|
||||
},
|
||||
"callisto": {
|
||||
"texture": ["moons/callisto.jpg", "moons/callisto_small.jpg"],
|
||||
"texture": ["moons/callisto.jpg", "moons/callisto_small.jpg", [0.49, 0.43, 0.34, 1]],
|
||||
"radius": "2410.3 * 5",
|
||||
"distance": 564810,
|
||||
"sma": 1882700,
|
||||
|
@ -168,7 +166,7 @@
|
|||
}
|
||||
},
|
||||
"saturn": {
|
||||
"texture": ["saturn.jpg", "saturn_medium.jpg", "saturn_small.jpg"],
|
||||
"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",
|
||||
|
@ -183,7 +181,7 @@
|
|||
"orbit_distance": "4 * AU",
|
||||
"satellites": {
|
||||
"titan": {
|
||||
"texture": ["moons/titan.jpg", "moons/titan_small.jpg"],
|
||||
"texture": ["moons/titan.jpg", "moons/titan_small.jpg", [0.52, 0.39, 0.23, 1]],
|
||||
"radius": "2576 * 10",
|
||||
"distance": "1221870 / 3 + 200000",
|
||||
"sma": 1221870,
|
||||
|
@ -193,7 +191,7 @@
|
|||
"eccentricity": 0.0288
|
||||
},
|
||||
"rhea": {
|
||||
"texture": ["moons/rhea.jpg", "moons/rhea_small.jpg"],
|
||||
"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,
|
||||
|
@ -203,7 +201,7 @@
|
|||
"eccentricity": 0.0012583
|
||||
},
|
||||
"iapetus": {
|
||||
"texture": ["moons/iapetus.jpg", "moons/iapetus_small.jpg"],
|
||||
"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,
|
||||
|
@ -213,7 +211,7 @@
|
|||
"eccentricity": 0.0286125
|
||||
},
|
||||
"dione": {
|
||||
"texture": ["moons/dione.jpg", "moons/dione_small.jpg"],
|
||||
"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,
|
||||
|
@ -223,7 +221,7 @@
|
|||
"eccentricity": 0.0022
|
||||
},
|
||||
"tethys": {
|
||||
"texture": ["moons/tethys.jpg", "moons/tethys_small.jpg"],
|
||||
"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,
|
||||
|
@ -233,7 +231,7 @@
|
|||
"eccentricity": 0.0001
|
||||
},
|
||||
"enceladus": {
|
||||
"texture": ["moons/enceladus.jpg", "moons/enceladus_small.jpg"],
|
||||
"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,
|
||||
|
@ -243,7 +241,7 @@
|
|||
"eccentricity": 0.0047
|
||||
},
|
||||
"mimas": {
|
||||
"texture": ["moons/mimas.jpg", "moons/mimas_small.jpg"],
|
||||
"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,
|
||||
|
@ -263,7 +261,7 @@
|
|||
}
|
||||
},
|
||||
"uranus": {
|
||||
"texture": ["uranus.jpg"],
|
||||
"texture": ["uranus.jpg", [0, 0.53, 0.84, 1]],
|
||||
"radius": 25559,
|
||||
"mass": 8.6810e+25,
|
||||
"z": "19.23 * AU",
|
||||
|
@ -280,7 +278,7 @@
|
|||
}
|
||||
},
|
||||
"neptune": {
|
||||
"texture": ["neptune.jpg"],
|
||||
"texture": ["neptune.jpg", [0.31, 0.49, 0.59, 1]],
|
||||
"radius": 24764,
|
||||
"mass": 1.0243e+26,
|
||||
"z": "30.5 * AU",
|
||||
|
@ -288,6 +286,19 @@
|
|||
"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": {
|
||||
|
@ -296,25 +307,10 @@
|
|||
"radius": "2.362 * AU",
|
||||
"cross": 1000,
|
||||
"scale": 30,
|
||||
"count": 4096,
|
||||
"count": 1024,
|
||||
"rotation": 114536500
|
||||
}
|
||||
},
|
||||
"sky": {
|
||||
"texture": [
|
||||
["sky_px.jpg", "sky_nx.jpg", "sky_py.jpg", "sky_ny.jpg", "sky_pz.jpg", "sky_nz.jpg"]
|
||||
],
|
||||
"constellation": [
|
||||
"constellation_px.png", "constellation_nx.png", "constellation_py.png",
|
||||
"constellation_ny.png", "constellation_pz.png", "constellation_nz.png"
|
||||
],
|
||||
"rotation": 0,
|
||||
"division": 30,
|
||||
"pitch": -119.3,
|
||||
"yaw": -97
|
||||
},
|
||||
"asteroids": ["asteroids/01.obj", "asteroids/02.obj", "asteroids/03.obj"],
|
||||
"font": "font.png",
|
||||
"start": {
|
||||
"z": "AU - 400",
|
||||
"yaw": 180
|
||||
|
|
|
@ -1,15 +1,64 @@
|
|||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
import json
|
||||
import os
|
||||
from collections import OrderedDict
|
||||
import os
|
||||
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
try:
|
||||
import simplejson as json
|
||||
except ImportError:
|
||||
raise SystemExit('No JSON module found')
|
||||
|
||||
import six
|
||||
|
||||
from punyverse import texture
|
||||
from punyverse.camera import Camera
|
||||
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.shader import Program
|
||||
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, callback=lambda message, completion: None):
|
||||
|
@ -17,85 +66,35 @@ def load_world(file, callback=lambda message, completion: None):
|
|||
|
||||
|
||||
class World(object):
|
||||
PROGRAMS = {
|
||||
'sky': ('sky.vertex.glsl', 'sky.fragment.glsl'),
|
||||
'planet': ('planet.vertex.glsl', 'planet.fragment.glsl'),
|
||||
'clouds': ('clouds.vertex.glsl', 'clouds.fragment.glsl'),
|
||||
'star': ('star.vertex.glsl', 'star.fragment.glsl'),
|
||||
'ring': ('ring.vertex.glsl', 'ring.fragment.glsl'),
|
||||
'atmosphere': ('atmosphere.vertex.glsl', 'atmosphere.fragment.glsl'),
|
||||
'text': ('text.vertex.glsl', 'text.fragment.glsl'),
|
||||
'line': ('line.vertex.glsl', 'line.fragment.glsl'),
|
||||
'model': ('model.vertex.glsl', 'model.fragment.glsl'),
|
||||
'belt': ('belt.vertex.glsl', 'model.fragment.glsl'),
|
||||
}
|
||||
|
||||
def __init__(self, file, callback, sky=True):
|
||||
def __init__(self, file, callback, options=None):
|
||||
self.tracker = []
|
||||
self.start = (0, 0, 0)
|
||||
self.direction = (0, 0, 0)
|
||||
self.x = None
|
||||
self.y = None
|
||||
self.z = None
|
||||
self.tick_length = 0
|
||||
self.tick_length = 1
|
||||
self.tick = 0
|
||||
self.asteroids = AsteroidManager(self)
|
||||
self.cam = Camera()
|
||||
|
||||
self._sky = sky
|
||||
|
||||
self._program = None
|
||||
self.callback = callback
|
||||
self.programs = self._load_programs()
|
||||
self.options = options or {}
|
||||
self._phase = 'Parsing configuration...'
|
||||
self._parse(file)
|
||||
del self.callback # So it can't be used after loading finishes
|
||||
|
||||
del self.callback # So it can't be used after loading finishes
|
||||
|
||||
self._time_accumulate = 0
|
||||
self._projection_matrix = self.cam.projection_matrix()
|
||||
|
||||
for entity in self.tracker:
|
||||
entity.update()
|
||||
|
||||
for name in ('planet', 'model', 'belt'):
|
||||
shader = self.activate_shader(name)
|
||||
shader.uniform_vec3('u_sun.ambient', 0.1, 0.1, 0.1)
|
||||
shader.uniform_vec3('u_sun.diffuse', 1, 1, 1)
|
||||
shader.uniform_vec3('u_sun.specular', 0.5, 0.5, 0.5)
|
||||
shader.uniform_vec3('u_sun.position', 0, 0, 0)
|
||||
shader.uniform_float('u_sun.intensity', 1)
|
||||
|
||||
shader = self.activate_shader('clouds')
|
||||
shader.uniform_vec3('u_sun', 0, 0, 0)
|
||||
self.activate_shader(None)
|
||||
|
||||
def _load_programs(self):
|
||||
programs = {}
|
||||
count = len(self.PROGRAMS)
|
||||
for i, (name, (vertex, fragment)) in enumerate(six.iteritems(self.PROGRAMS)):
|
||||
self.callback('Loading shaders (%d of %d)...' % (i, count),
|
||||
'Loading shader "%s" (%s, %s).' % (name, vertex, fragment), i / count)
|
||||
programs[name] = Program(vertex, fragment)
|
||||
return programs
|
||||
|
||||
def evaluate(self, value):
|
||||
def _eval(self, value):
|
||||
return eval(str(value), {'__builtins__': None}, self._context)
|
||||
|
||||
@property
|
||||
def length(self):
|
||||
return self._length
|
||||
|
||||
@property
|
||||
def au(self):
|
||||
return self._au
|
||||
|
||||
def _parse(self, file):
|
||||
self.callback('Parsing configuration...', 'Loading configuration file...', 0)
|
||||
self.callback(self._phase, 'Loading configuration file...', 0)
|
||||
with open(os.path.join(os.path.dirname(__file__), file)) as f:
|
||||
root = json.load(f, object_pairs_hook=OrderedDict)
|
||||
self._au = root.get('au', 2000)
|
||||
self._length = root.get('length', 4320)
|
||||
self._context = {'AU': self._au, 'TEXTURE': texture.max_texture_size(), 'KM': 1.0 / self._length}
|
||||
self._context = {'AU': self._au, 'TEXTURE': texture.max_texture, 'KM': 1.0 / self._length}
|
||||
|
||||
self.tick_length = root.get('tick', 4320) # How many second is a tick?
|
||||
tick = root.get('tick', 4320) # How many second is a tick?
|
||||
self.tick_length = tick
|
||||
|
||||
# Need to know how many objects are being loaded
|
||||
self._objects = 0
|
||||
|
@ -106,113 +105,173 @@ class World(object):
|
|||
self._objects += 1
|
||||
count_objects(body.get('satellites', {}))
|
||||
count_objects(root['bodies'])
|
||||
print(self._objects, 'objects to be loaded...')
|
||||
|
||||
if 'start' in root:
|
||||
info = root['start']
|
||||
self.cam.x = self.evaluate(info.get('x', 0))
|
||||
self.cam.y = self.evaluate(info.get('y', 0))
|
||||
self.cam.z = self.evaluate(info.get('z', 0))
|
||||
self.cam.pitch = self.evaluate(info.get('pitch', 0))
|
||||
self.cam.yaw = self.evaluate(info.get('yaw', 0))
|
||||
self.cam.roll = self.evaluate(info.get('roll', 0))
|
||||
x = self._eval(info.get('x', 0))
|
||||
y = self._eval(info.get('y', 0))
|
||||
z = self._eval(info.get('z', 0))
|
||||
pitch = self._eval(info.get('pitch', 0))
|
||||
yaw = self._eval(info.get('yaw', 0))
|
||||
roll = self._eval(info.get('roll', 0))
|
||||
self.start = (x, y, z)
|
||||
self.direction = (pitch, yaw, roll)
|
||||
|
||||
for planet, info in six.iteritems(root['bodies']):
|
||||
message = 'Loading %s.' % planet
|
||||
print(message)
|
||||
self.callback('Loading objects (%d of %d)...' % (self._current_object, self._objects),
|
||||
'Loading %s.' % planet, self._current_object / self._objects)
|
||||
message, float(self._current_object) / self._objects)
|
||||
self._body(planet, info)
|
||||
self._current_object += 1
|
||||
|
||||
if 'belts' in root:
|
||||
belt_count = len(root['belts'])
|
||||
for i, (name, info) in enumerate(six.iteritems(root['belts']), 1):
|
||||
self.callback('Loading belts (%d of %d)...' % (i, belt_count),
|
||||
'Loading %s.' % name, i / belt_count)
|
||||
self.tracker.append(Belt(name, self, info))
|
||||
self._phase = 'Loading belts...'
|
||||
self._current_object = 0
|
||||
for name, info in six.iteritems(root['belts']):
|
||||
message = 'Loading %s.' % name
|
||||
print(message)
|
||||
self.callback(self._phase, message, float(self._current_object) / len(root['belts']))
|
||||
self._belt(name, info)
|
||||
|
||||
if 'sky' in root and self._sky:
|
||||
def callback(index, file):
|
||||
self.callback('Loading sky...', 'Loading %s.' % file, index / 6)
|
||||
self.tracker.append(Sky(self, root['sky'], callback))
|
||||
def _belt(self, name, info):
|
||||
x = self._eval(info.get('x', 0))
|
||||
y = self._eval(info.get('y', 0))
|
||||
z = self._eval(info.get('z', 0))
|
||||
radius = self._eval(info.get('radius', 0))
|
||||
cross = self._eval(info.get('cross', 0))
|
||||
count = int(self._eval(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
|
||||
|
||||
if 'asteroids' in root:
|
||||
asteroids = root['asteroids']
|
||||
for i, file in enumerate(asteroids):
|
||||
self.callback('Loading asteroids...', 'Loading %s...' % file, i / len(asteroids))
|
||||
self.asteroids.load(file)
|
||||
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)))
|
||||
|
||||
self.font_tex = load_alpha_mask(root['font'], clamp=True)
|
||||
self.tracker.append(Belt(compile(belt, radius, cross, objects, count),
|
||||
(x, y, z), (inclination, longitude, argument),
|
||||
rotation_angle=theta, world=self))
|
||||
|
||||
def _body(self, name, info, parent=None):
|
||||
if 'texture' in info:
|
||||
body = SphericalBody(name, self, info, parent)
|
||||
elif 'model' in info:
|
||||
body = ModelBody(name, self, info, parent)
|
||||
else:
|
||||
raise ValueError('Nothing to load for %s.' % name)
|
||||
lighting = info.get('lighting', True)
|
||||
x = self._eval(info.get('x', 0))
|
||||
y = self._eval(info.get('y', 0))
|
||||
z = self._eval(info.get('z', 0))
|
||||
pitch = self._eval(info.get('pitch', 0))
|
||||
yaw = self._eval(info.get('yaw', 0))
|
||||
roll = self._eval(info.get('roll', 0))
|
||||
rotation = self._eval(info.get('rotation', 86400))
|
||||
radius = self._eval(info.get('radius', self._length)) / self._length
|
||||
background = info.get('background', False)
|
||||
orbit_distance = self._eval(info.get('orbit_distance', self._au))
|
||||
division = info.get('division', max(min(int(radius / 8), 60), 10))
|
||||
|
||||
if parent:
|
||||
parent.satellites.append(body)
|
||||
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 self.options.get('normal', False) and 'normal' in info:
|
||||
object_id = compile(normal_sphere, radius, division, texture,
|
||||
info['normal'], lighting=lighting, inside=background)
|
||||
else:
|
||||
object_id = compile(sphere, radius, division, division, texture,
|
||||
lighting=lighting, inside=background)
|
||||
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:
|
||||
self.tracker.append(body)
|
||||
print('Nothing to load for %s.' % name)
|
||||
return
|
||||
|
||||
params = {'world': self, 'orbit_distance': orbit_distance, 'radius': None if background else radius}
|
||||
if parent is None:
|
||||
type = Body
|
||||
else:
|
||||
x, y, z = parent.location
|
||||
distance = self._eval(info.get('distance', 100)) # Semi-major axis when actually displayed in virtual space
|
||||
sma = self._eval(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 / self._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 = self._eval(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.0 / rotation 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)
|
||||
self.tracker.append(object)
|
||||
|
||||
if 'ring' in info:
|
||||
ring_data = info['ring']
|
||||
texture = ring_data.get('texture', None)
|
||||
distance = self._eval(ring_data.get('distance', radius * 1.2))
|
||||
size = self._eval(ring_data.get('size', radius / 2))
|
||||
pitch = self._eval(ring_data.get('pitch', pitch))
|
||||
yaw = self._eval(ring_data.get('yaw', yaw))
|
||||
roll = self._eval(ring_data.get('roll', roll))
|
||||
|
||||
cheap, _, texture = get_best_texture(texture)
|
||||
if not cheap:
|
||||
self.tracker.append(
|
||||
type(compile(disk, distance, distance + size, 30, texture), (x, y, z),
|
||||
(pitch, yaw, roll), **params))
|
||||
|
||||
for satellite, info in six.iteritems(info.get('satellites', {})):
|
||||
message = 'Loading %s, satellite of %s.' % (satellite, name)
|
||||
print(message)
|
||||
self.callback('Loading objects (%d of %d)...' % (self._current_object, self._objects),
|
||||
'Loading %s, satellite of %s.' % (satellite, name), self._current_object / self._objects)
|
||||
self._body(satellite, info, body)
|
||||
message, float(self._current_object) / self._objects)
|
||||
self._body(satellite, info, object)
|
||||
self._current_object += 1
|
||||
|
||||
def spawn_asteroid(self):
|
||||
if self.asteroids:
|
||||
c = self.cam
|
||||
dx, dy, dz = c.direction()
|
||||
speed = abs(self.cam.speed) * 1.1 + 5
|
||||
self.tracker.append(self.asteroids.new((c.x, c.y - 3, c.z + 5), (dx * speed, dy * speed, dz * speed)))
|
||||
|
||||
def update(self, dt, move, tick):
|
||||
c = self.cam
|
||||
c.update(dt, move)
|
||||
self.vp_matrix = None
|
||||
|
||||
if tick:
|
||||
delta = self.tick_length * dt
|
||||
update = int(delta + self._time_accumulate + 0.5)
|
||||
if update:
|
||||
self._time_accumulate = 0
|
||||
self.tick += update
|
||||
|
||||
for entity in self.tracker:
|
||||
entity.update()
|
||||
collision = entity.collides(c.x, c.y, c.z)
|
||||
if collision:
|
||||
c.speed *= -1
|
||||
c.move(c.speed * 12 * dt)
|
||||
else:
|
||||
self._time_accumulate += delta
|
||||
|
||||
def view_matrix(self):
|
||||
return self.cam.view_matrix
|
||||
|
||||
def projection_matrix(self):
|
||||
return self._projection_matrix
|
||||
|
||||
@cached_property
|
||||
def vp_matrix(self):
|
||||
return self._projection_matrix * self.cam.view_matrix
|
||||
|
||||
def resize(self, width, height):
|
||||
self.cam.aspect = width / max(height, 1)
|
||||
self._projection_matrix = self.cam.projection_matrix()
|
||||
self.vp_matrix = None
|
||||
|
||||
def activate_shader(self, name):
|
||||
program = None
|
||||
if self._program != name:
|
||||
if name is None:
|
||||
glUseProgram(0)
|
||||
else:
|
||||
program = self.programs[name]
|
||||
glUseProgram(program.program)
|
||||
self._program = name
|
||||
elif self._program is not None:
|
||||
program = self.programs[self._program]
|
||||
return program
|
||||
|
|
109
setup.py
|
@ -1,11 +1,8 @@
|
|||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
import sys
|
||||
import os
|
||||
|
||||
from setuptools import setup, Extension
|
||||
from setuptools.command.build_ext import build_ext
|
||||
from setuptools.extension import Library
|
||||
|
||||
has_pyx = os.path.exists(os.path.join(os.path.dirname(__file__), 'punyverse', '_glgeom.pyx'))
|
||||
|
||||
|
@ -26,98 +23,19 @@ else:
|
|||
|
||||
if os.name == 'nt':
|
||||
gl_libs = ['opengl32']
|
||||
elif sys.platform == 'darwin':
|
||||
gl_libs = []
|
||||
else:
|
||||
gl_libs = ['GL']
|
||||
|
||||
if sys.platform == 'darwin':
|
||||
extra_compile_args = extra_link_args = ['-framework', 'OpenGL']
|
||||
else:
|
||||
extra_compile_args = extra_link_args = []
|
||||
|
||||
with open(os.path.join(os.path.dirname(__file__), 'README.md')) as f:
|
||||
long_description = f.read()
|
||||
|
||||
|
||||
if os.name == 'nt':
|
||||
class SimpleExecutable(Library, object):
|
||||
executable_names = set()
|
||||
|
||||
def __init__(self, name, *args, **kwargs):
|
||||
super(SimpleExecutable, self).__init__(name, *args, **kwargs)
|
||||
self.executable_names.add(name)
|
||||
if '.' in name:
|
||||
self.executable_names.add(name.split('.')[-1])
|
||||
|
||||
|
||||
def link_shared_object(
|
||||
self, objects, output_libname, output_dir=None, libraries=None,
|
||||
library_dirs=None, runtime_library_dirs=None, export_symbols=None,
|
||||
debug=0, extra_preargs=None, extra_postargs=None, build_temp=None,
|
||||
target_lang=None):
|
||||
self.link(
|
||||
self.EXECUTABLE, objects, output_libname,
|
||||
output_dir, libraries, library_dirs, runtime_library_dirs,
|
||||
export_symbols, debug, extra_preargs, extra_postargs,
|
||||
build_temp, target_lang
|
||||
)
|
||||
|
||||
def make_manifest_get_embed_info(old_func):
|
||||
def manifest_get_embed_info(self, target_desc, ld_args):
|
||||
temp_manifest, mfid = old_func(target_desc, ld_args)
|
||||
if not os.path.exists(temp_manifest):
|
||||
return None
|
||||
return temp_manifest, mfid
|
||||
return manifest_get_embed_info.__get__(old_func.__self__)
|
||||
|
||||
|
||||
class build_ext_exe(build_ext, object):
|
||||
def get_ext_filename(self, fullname):
|
||||
ext = self.ext_map[fullname]
|
||||
if isinstance(ext, SimpleExecutable):
|
||||
return fullname.replace('.', os.sep) + '.exe'
|
||||
return super(build_ext_exe, self).get_ext_filename(fullname)
|
||||
|
||||
def get_export_symbols(self, ext):
|
||||
if isinstance(ext, SimpleExecutable):
|
||||
return ext.export_symbols
|
||||
return super(build_ext_exe, self).get_export_symbols(ext)
|
||||
|
||||
def build_extension(self, ext):
|
||||
if isinstance(ext, SimpleExecutable):
|
||||
old = self.shlib_compiler.link_shared_object
|
||||
self.shlib_compiler.link_shared_object = link_shared_object.__get__(self.shlib_compiler)
|
||||
patched = False
|
||||
if hasattr(self.shlib_compiler, 'manifest_get_embed_info'):
|
||||
self.shlib_compiler.manifest_get_embed_info = \
|
||||
make_manifest_get_embed_info(self.shlib_compiler.manifest_get_embed_info)
|
||||
patched = True
|
||||
super(build_ext_exe, self).build_extension(ext)
|
||||
self.shlib_compiler.link_shared_object = old
|
||||
if patched:
|
||||
del self.shlib_compiler.manifest_get_embed_info
|
||||
else:
|
||||
super(build_ext_exe, self).build_extension(ext)
|
||||
|
||||
extra_libs = [
|
||||
SimpleExecutable('punyverse.launcher', sources=['punyverse/launcher.c'], libraries=['shell32']),
|
||||
SimpleExecutable('punyverse.launcherw', sources=['punyverse/launcher.c'],
|
||||
libraries=['shell32'], define_macros=[('GUI', 1)]),
|
||||
]
|
||||
build_ext = build_ext_exe
|
||||
else:
|
||||
extra_libs = []
|
||||
|
||||
|
||||
setup(
|
||||
name='punyverse',
|
||||
version='1.2',
|
||||
version='0.4',
|
||||
packages=['punyverse'],
|
||||
package_data={
|
||||
'punyverse': [
|
||||
'world.json',
|
||||
'shaders/*.glsl',
|
||||
'assets/textures.txt',
|
||||
'assets/textures/*.jpg',
|
||||
'assets/textures/*.png',
|
||||
|
@ -131,23 +49,21 @@ setup(
|
|||
],
|
||||
},
|
||||
ext_modules=cythonize([
|
||||
Extension('punyverse._glgeom', sources=[pyx_path('punyverse/_glgeom.pyx')], libraries=gl_libs,
|
||||
extra_compile_args=extra_compile_args, extra_link_args=extra_link_args),
|
||||
]) + extra_libs,
|
||||
cmdclass={'build_ext': build_ext},
|
||||
Extension('punyverse._glgeom', sources=[pyx_path('punyverse/_glgeom.pyx')], libraries=gl_libs),
|
||||
Extension('punyverse._model', sources=[pyx_path('punyverse/_model.pyx')], libraries=gl_libs),
|
||||
]),
|
||||
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'punyverse = punyverse.main:main',
|
||||
'punyverse_make_launcher = punyverse.launcher:main',
|
||||
'punyverse = punyverse.__main__:main',
|
||||
'punyverse_small_images = punyverse.small_images:main',
|
||||
],
|
||||
'gui_scripts': [
|
||||
'punyversew = punyverse.main:main'
|
||||
'punyversew = punyverse.__main__:main'
|
||||
]
|
||||
|
||||
},
|
||||
install_requires=['pyglet<1.4', 'Pillow', 'six'],
|
||||
install_requires=['pyglet', 'Pillow', 'six'],
|
||||
|
||||
author='quantum',
|
||||
author_email='quantum2048@gmail.com',
|
||||
|
@ -159,19 +75,10 @@ setup(
|
|||
classifiers=[
|
||||
'Development Status :: 4 - Beta',
|
||||
'Environment :: Win32 (MS Windows)',
|
||||
'Environment :: X11 Applications',
|
||||
'Intended Audience :: End Users/Desktop',
|
||||
'License :: OSI Approved :: GNU General Public License v2 (GPLv2)',
|
||||
'Operating System :: Microsoft :: Windows',
|
||||
'Operating System :: POSIX :: Linux',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 2',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Topic :: Games/Entertainment :: Simulation',
|
||||
'Topic :: Multimedia :: Graphics :: 3D Rendering',
|
||||
'Topic :: Scientific/Engineering :: Visualization',
|
||||
|
|