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)
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
@ -38,10 +40,14 @@ def main():
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)
config=config, world_options=world_options)
pyglet.app.run()

View file

@ -41,6 +41,8 @@ def entity_distance(x0, y0, z0):
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()
@ -79,7 +81,7 @@ class Applet(pyglet.window.Window):
start = clock()
self.fps = 0
self.world = World('world.json', callback)
self.world = World('world.json', callback, self.world_options)
phase = 'Initializing game...'
print phase
callback(phase, '', 0)

View file

@ -1,6 +1,7 @@
from math import *
from pyglet.gl import *
from random import random, gauss, choice
from pyglet.gl.gl_info import have_extension
TWOPI = pi * 2
@ -181,23 +182,31 @@ def colourball(r, lats, longs, colour, fv4=GLfloat * 4):
gluDeleteQuadric(sphere)
try:
from _glgeom import normal_sphere
except ImportError:
import warnings
warnings.warn('Large sphere drawing in Python is slow')
def normal_sphere(r, divide, tex, normal, lighting=True, fv4=GLfloat * 4):
from texture import pil_load
print 'Loading normal map: %s...' % normal,
normal_map = pil_load(normal)
normal = normal_map.load()
print
width, height = normal_map.size
gray_scale = len(normal[0, 0]) == 1
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)
from texture import load_texture
normal = load_texture(normal)
with glRestore(GL_ENABLE_BIT | GL_TEXTURE_BIT):
glActiveTextureARB(GL_TEXTURE0_ARB)
glBindTexture(GL_TEXTURE_2D, normal)
glEnable(GL_TEXTURE_2D)
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))
@ -218,33 +227,18 @@ except ImportError:
theta = i * pi_divide
s = phi2 / TWOPI
u = min(int(s * width), width - 1)
t = theta / pi
v = min(int(t * height), height - 1)
if gray_scale:
x = y = z = normal[u, v]
else:
x, y, z = normal[u, v]
dx, dy, dz = sin(theta) * cos(phi2), sin(theta) * sin(phi2), cos(theta)
nx, ny, nz = x / 127.5 - 1, y / 127.5 - 1, z / 127.5 - 1 # Make into [-1, 1]
nx, nz = cos(theta) * nx + sin(theta) * nz, -sin(theta) * nx + cos(theta) * nz
nx, ny = cos(phi2) * nx - sin(phi2) * ny, sin(phi2) * nx + cos(phi2) * ny
glNormal3f(nx, ny, nz)
glTexCoord2f(s, 1 - t) # GL is bottom up
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
u = min(int(s * width), width - 1)
if gray_scale:
x = y = z = normal[u, v]
else:
x, y, z = normal[u, v]
dx, dy = sin(theta) * cos(phi1), sin(theta) * sin(phi1)
nx, ny, nz = x / 127.5 - 1, y / 127.5 - 1, z / 127.5 - 1
nx, nz = cos(theta) * nx + sin(theta) * nz, -sin(theta) * nx + cos(theta) * nz
nx, ny = cos(phi1) * nx - sin(phi1) * ny, sin(phi1) * nx + cos(phi1) * ny
glNormal3f(nx, ny, nz)
glTexCoord2f(s, 1 - t)
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)

View file

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

View file

@ -62,7 +62,7 @@ def load_world(file, callback=lambda message, completion: None):
class World(object):
def __init__(self, file, callback):
def __init__(self, file, callback, options=None):
self.tracker = []
self.start = (0, 0, 0)
self.direction = (0, 0, 0)
@ -73,6 +73,7 @@ class World(object):
self.tick = 0
self.callback = callback
self.options = options or {}
self._phase = 'Parsing configuration...'
self._parse(file)
del self.callback # So it can't be used after loading finishes
@ -177,7 +178,7 @@ class World(object):
if cheap:
object_id = compile(colourball, radius, division, division, texture)
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)
else:
object_id = compile(sphere, radius, division, division, texture, lighting=lighting)