Added hardware normal mapping, usable with -n/--normal option.

This commit is contained in:
Quantum 2014-02-02 18:22:45 -05:00
parent 83d8baeb8e
commit 2348e1d275
5 changed files with 67 additions and 63 deletions

View file

@ -16,6 +16,8 @@ def main():
type=int) type=int)
parser.add_argument('-v', '--no-vsync', help='Disables vsync', parser.add_argument('-v', '--no-vsync', help='Disables vsync',
action='store_false', dest='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() args = parser.parse_args()
import pyglet import pyglet
@ -38,10 +40,14 @@ def main():
for key in config._attribute_names: for key in config._attribute_names:
print ' %-17s %s' % (key + ':', getattr(config, key)) print ' %-17s %s' % (key + ':', getattr(config, key))
world_options = {
'normal': args.normal,
}
from punyverse import game from punyverse import game
game.Applet(width=INITIAL_WIN_WIDTH, height=INITIAL_WIN_HEIGHT, game.Applet(width=INITIAL_WIN_WIDTH, height=INITIAL_WIN_HEIGHT,
caption=WIN_TITLE, resizable=True, vsync=args.vsync, caption=WIN_TITLE, resizable=True, vsync=args.vsync,
config=config) config=config, world_options=world_options)
pyglet.app.run() pyglet.app.run()

View file

@ -41,6 +41,8 @@ def entity_distance(x0, y0, z0):
class Applet(pyglet.window.Window): class Applet(pyglet.window.Window):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.world_options = kwargs.pop('world_options', {})
super(Applet, self).__init__(*args, **kwargs) super(Applet, self).__init__(*args, **kwargs)
texture.init() texture.init()
@ -79,7 +81,7 @@ class Applet(pyglet.window.Window):
start = clock() start = clock()
self.fps = 0 self.fps = 0
self.world = World('world.json', callback) self.world = World('world.json', callback, self.world_options)
phase = 'Initializing game...' phase = 'Initializing game...'
print phase print phase
callback(phase, '', 0) callback(phase, '', 0)

View file

@ -1,6 +1,7 @@
from math import * from math import *
from pyglet.gl import * from pyglet.gl import *
from random import random, gauss, choice from random import random, gauss, choice
from pyglet.gl.gl_info import have_extension
TWOPI = pi * 2 TWOPI = pi * 2
@ -181,71 +182,64 @@ def colourball(r, lats, longs, colour, fv4=GLfloat * 4):
gluDeleteQuadric(sphere) gluDeleteQuadric(sphere)
try: def normal_sphere(r, divide, tex, normal, lighting=True, fv4=GLfloat * 4):
from _glgeom import normal_sphere if (not have_extension('GL_ARB_multitexture') or not
except ImportError: have_extension('GL_ARB_texture_env_combine') or not
import warnings have_extension('GL_EXT_texture_env_dot3')):
warnings.warn('Large sphere drawing in Python is slow') print 'No hardware normal mapping support. No bumping for you.'
return sphere(r, divide, divide, tex, lighting)
def normal_sphere(r, divide, tex, normal, lighting=True, fv4=GLfloat * 4): from texture import load_texture
from texture import pil_load normal = load_texture(normal)
print 'Loading normal map: %s...' % normal,
normal_map = pil_load(normal)
normal = normal_map.load()
print
width, height = normal_map.size
gray_scale = len(normal[0, 0]) == 1
with glRestore(GL_ENABLE_BIT | GL_TEXTURE_BIT): with glRestore(GL_ENABLE_BIT | GL_TEXTURE_BIT):
glEnable(GL_TEXTURE_2D) glActiveTextureARB(GL_TEXTURE0_ARB)
if lighting: glBindTexture(GL_TEXTURE_2D, normal)
glDisable(GL_BLEND) glEnable(GL_TEXTURE_2D)
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 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB)
pi_divide = pi / divide glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_DOT3_RGBA_ARB)
with glSection(GL_TRIANGLE_STRIP):
for j in xrange(divide + 1):
phi1 = j * twopi_divide
phi2 = (j + 1) * twopi_divide
for i in xrange(divide + 1): glActiveTextureARB(GL_TEXTURE1_ARB)
theta = i * pi_divide glBindTexture(GL_TEXTURE_2D, tex)
glEnable(GL_TEXTURE_2D)
s = phi2 / TWOPI glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB)
u = min(int(s * width), width - 1) glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE)
t = theta / pi
v = min(int(t * height), height - 1)
if gray_scale:
x = y = z = normal[u, v]
else:
x, y, z = normal[u, v]
dx, dy, dz = sin(theta) * cos(phi2), sin(theta) * sin(phi2), cos(theta)
nx, ny, nz = x / 127.5 - 1, y / 127.5 - 1, z / 127.5 - 1 # Make into [-1, 1]
nx, nz = cos(theta) * nx + sin(theta) * nz, -sin(theta) * nx + cos(theta) * nz
nx, ny = cos(phi2) * nx - sin(phi2) * ny, sin(phi2) * nx + cos(phi2) * ny
glNormal3f(nx, ny, nz)
glTexCoord2f(s, 1 - t) # GL is bottom up
glVertex3f(r * dx, r * dy, r * dz)
s = phi1 / TWOPI # x if lighting:
u = min(int(s * width), width - 1) glDisable(GL_BLEND)
if gray_scale: glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, fv4(1, 1, 1, 0))
x = y = z = normal[u, v] glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, fv4(1, 1, 1, 0))
else: glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 125)
x, y, z = normal[u, v] else:
dx, dy = sin(theta) * cos(phi1), sin(theta) * sin(phi1) glDisable(GL_LIGHTING)
nx, ny, nz = x / 127.5 - 1, y / 127.5 - 1, z / 127.5 - 1 glBindTexture(GL_TEXTURE_2D, tex)
nx, nz = cos(theta) * nx + sin(theta) * nz, -sin(theta) * nx + cos(theta) * nz
nx, ny = cos(phi1) * nx - sin(phi1) * ny, sin(phi1) * nx + cos(phi1) * ny twopi_divide = TWOPI / divide
glNormal3f(nx, ny, nz) pi_divide = pi / divide
glTexCoord2f(s, 1 - t) with glSection(GL_TRIANGLE_STRIP):
glVertex3f(r * dx, r * dy, r * dz) for j in xrange(divide + 1):
phi1 = j * twopi_divide
phi2 = (j + 1) * twopi_divide
for i in xrange(divide + 1):
theta = i * pi_divide
s = phi2 / TWOPI
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)
def belt(radius, cross, object, count): def belt(radius, cross, object, count):

View file

@ -58,6 +58,7 @@
"mass": 5.97219e+24, "mass": 5.97219e+24,
"rotation": 86400, "rotation": 86400,
"division": 90, "division": 90,
"normal": "earth_normal.jpg",
"atmosphere": { "atmosphere": {
"cloud_texture": ["cloudmap.jpg", "cloudmap_small.jpg"], "cloud_texture": ["cloudmap.jpg", "cloudmap_small.jpg"],
"diffuse_texture": "atmosphere_earth.png", "diffuse_texture": "atmosphere_earth.png",

View file

@ -62,7 +62,7 @@ def load_world(file, callback=lambda message, completion: None):
class World(object): class World(object):
def __init__(self, file, callback): def __init__(self, file, callback, options=None):
self.tracker = [] self.tracker = []
self.start = (0, 0, 0) self.start = (0, 0, 0)
self.direction = (0, 0, 0) self.direction = (0, 0, 0)
@ -73,6 +73,7 @@ class World(object):
self.tick = 0 self.tick = 0
self.callback = callback self.callback = callback
self.options = options or {}
self._phase = 'Parsing configuration...' self._phase = 'Parsing configuration...'
self._parse(file) 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
@ -177,7 +178,7 @@ class World(object):
if cheap: if cheap:
object_id = compile(colourball, radius, division, division, texture) object_id = compile(colourball, radius, division, division, texture)
else: else:
if 'normal' in info: if self.options.get('normal', False) and 'normal' in info:
object_id = compile(normal_sphere, radius, division, texture, info['normal'], lighting=lighting) object_id = compile(normal_sphere, radius, division, texture, info['normal'], lighting=lighting)
else: else:
object_id = compile(sphere, radius, division, division, texture, lighting=lighting) object_id = compile(sphere, radius, division, division, texture, lighting=lighting)