summaryrefslogtreecommitdiff
path: root/cad/src/graphics/drawing/gl_lighting.py
blob: 05a24c2c3ca68b29ac987afe3766b67b0bcf8ac6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
# Copyright 2004-2009 Nanorex, Inc.  See LICENSE file for details.
"""
gl_lighting.py - Lights, materials, and special effects (fog).

@version: $Id$
@copyright: 2004-2009 Nanorex, Inc.  See LICENSE file for details.

History:

Originated by Josh in drawer.py .

Various developers extended it since then.

Brad G. added ColorSorter features and probably apply_material.

080519 russ: pulled the globals into a drawing_globals module and broke
drawer.py into 10 smaller chunks: glprefs.py setup_draw.py shape_vertices.py
ColorSorter.py CS_workers.py c_renderer.py CS_draw_primitives.py drawers.py
gl_lighting.py gl_buffers.py
"""

from OpenGL.GL import GL_AMBIENT
from OpenGL.GL import GL_AMBIENT_AND_DIFFUSE
from OpenGL.GL import glColor4fv
from OpenGL.GL import GL_CONSTANT_ATTENUATION
from OpenGL.GL import GL_DIFFUSE
from OpenGL.GL import glDisable
from OpenGL.GL import glEnable
from OpenGL.GL import GL_FOG
from OpenGL.GL import GL_FOG_COLOR
from OpenGL.GL import GL_FOG_END
from OpenGL.GL import GL_FOG_MODE
from OpenGL.GL import GL_FOG_START
from OpenGL.GL import GL_FRONT_AND_BACK
from OpenGL.GL import GL_LIGHT0
from OpenGL.GL import GL_LIGHT1
from OpenGL.GL import GL_LIGHT2
from OpenGL.GL import glLightf
from OpenGL.GL import glLightfv
from OpenGL.GL import GL_LIGHTING
from OpenGL.GL import GL_LINEAR
from OpenGL.GL import glLoadIdentity
from OpenGL.GL import glMaterialf
from OpenGL.GL import glMaterialfv
from OpenGL.GL import glMatrixMode
from OpenGL.GL import GL_MODELVIEW
from OpenGL.GL import GL_POSITION
from OpenGL.GL import GL_SHININESS
from OpenGL.GL import GL_SPECULAR

try:
    from OpenGL.GL import glFog
    from OpenGL.GL import glFogv # piotr 080515
except:
    # The installed version of OpenGL requires argument-typed glFog calls.
    from OpenGL.GL import glFogf as glFog
    from OpenGL.GL import glFogfv as glFogv
    pass

from utilities.constants import white

from utilities.debug import print_compact_traceback

import graphics.drawing.drawing_globals as drawing_globals # only for glprefs in apply_material

# ==

# Helper functions for use by GL widgets wanting to set up lighting.

#bruce 051212 made these from the code in GLPane which now calls them, so they
#can also be used in ThumbView

# Default lights tuples (format is as used by setup_standard_lights; perhaps
# also assumed by other code).
#grantham 20051121 comment - Light should probably be a class.  Right now,
# changing the behavior of lights requires changing a bunch of
# ambiguous tuples and tuple packing/unpacking.
#bruce 051212 moved this here from GLPane; maybe it belongs in prefs_constants
# instead?
# Note: I'm not sure whether this is the only place where this data is coded.
_default_lights = ((white, 0.1, 0.5, 0.5, -50, 70, 30, True),
                   (white, 0.1, 0.5, 0.5, -20, 20, 20, True),
                   (white, 0.1, 0.1, 0.1, 0, 0, 100, False))
        # for each of 3 lights, this stores ((r,g,b),a,d,s,x,y,z,e)
        # revised format to include s,x,y,z.  Mark 051202.
        # revised format to include c (r,g,b). Mark 051204.
        # Be sure to keep the lightColor prefs keys and _lights colors
        # synchronized.
        # Mark 051204. [a comment from when this was located in GLPane]

def glprefs_data_used_by_setup_standard_lights( glprefs): #bruce 051212
    """
    Return a summary of the glprefs data used by setup_standard_lights
    (when that was passed the same glprefs argument).
    """
    # This must be kept in sync with what's used by setup_standard_lights() .
    return (glprefs.override_light_specular,)

def setup_standard_lights( lights, glprefs):
    """
    Set up lighting in the current GL context using the supplied "lights" tuple
    (in the format used by GLPane's prefs) and the required glprefs object
    (of class GLPrefs).

    @note: the glprefs data used can be summarized by the related function
        glprefs_data_used_by_setup_standard_lights (which see).

    @warning: has side effects on GL_MODELVIEW matrix.

    @note: If GL_NORMALIZE needs to be enabled, callers should do that
        themselves, since this depends on what they will draw and might
        slow down drawing.
    """

    assert glprefs is not None

    # note: whatever glprefs data is used below must also be present
    # in the return value of glprefs_data_used_by_setup_standard_lights().
    # [bruce 051212]

    #e not sure whether projection matrix also needs to be reset here
    # [bruce 051212]
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()

    try:
        # new code
        (((r0,g0,b0),a0,d0,s0,x0,y0,z0,e0), \
         ( (r1,g1,b1),a1,d1,s1,x1,y1,z1,e1), \
         ( (r2,g2,b2),a2,d2,s2,x2,y2,z2,e2)) = lights

        # Great place for a print statement for debugging lights.  Keep this.
        # Mark 051204. [revised by bruce 051212]
        #print "-------------------------------------------------------------"
        #print "setup_standard_lights: lights[0]=", lights[0]
        #print "setup_standard_lights: lights[1]=", lights[1]
        #print "setup_standard_lights: lights[2]=", lights[2]

        glLightfv(GL_LIGHT0, GL_POSITION, (x0, y0, z0, 0))
        glLightfv(GL_LIGHT0, GL_AMBIENT, (r0*a0, g0*a0, b0*a0, 1.0))
        glLightfv(GL_LIGHT0, GL_DIFFUSE, (r0*d0, g0*d0, b0*d0, 1.0))
        if glprefs.override_light_specular is not None:
            glLightfv(GL_LIGHT0, GL_SPECULAR, glprefs.override_light_specular)
        else:
            # grantham 20051121 - this should be a component on its own
            # not replicating the diffuse color.
            # Added specular (s0) as its own component.  mark 051202.
            glLightfv(GL_LIGHT0, GL_SPECULAR, (r0*s0, g0*s0, b0*s0, 1.0))
        glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 1.0)

        glLightfv(GL_LIGHT1, GL_POSITION, (x1, y1, z1, 0))
        glLightfv(GL_LIGHT1, GL_AMBIENT, (r1*a1, g1*a1, b1*a1, 1.0))
        glLightfv(GL_LIGHT1, GL_DIFFUSE, (r1*d1, g1*d1, b1*d1, 1.0))
        if glprefs.override_light_specular is not None:
            glLightfv(GL_LIGHT1, GL_SPECULAR, glprefs.override_light_specular)
        else:
            glLightfv(GL_LIGHT1, GL_SPECULAR, (r1*s1, g1*s1, b1*s1, 1.0))
        glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, 1.0)

        glLightfv(GL_LIGHT2, GL_POSITION, (x2, y2, z2, 0))
        glLightfv(GL_LIGHT2, GL_AMBIENT, (r2*a2, g2*a2, b2*a2, 1.0))
        glLightfv(GL_LIGHT2, GL_DIFFUSE, (r2*d2, g2*d2, b2*d2, 1.0))
        if glprefs.override_light_specular is not None:
            glLightfv(GL_LIGHT2, GL_SPECULAR, glprefs.override_light_specular)
        else:
            glLightfv(GL_LIGHT2, GL_SPECULAR, (r2*s2, g2*s2, b2*s2, 1.0))
        glLightf(GL_LIGHT2, GL_CONSTANT_ATTENUATION, 1.0)

        glEnable(GL_LIGHTING)

        if e0:
            glEnable(GL_LIGHT0)
        else:
            glDisable(GL_LIGHT0)

        if e1:
            glEnable(GL_LIGHT1)
        else:
            glDisable(GL_LIGHT1)

        if e2:
            glEnable(GL_LIGHT2)
        else:
            glDisable(GL_LIGHT2)
    except:
        print_compact_traceback(
            "bug (worked around): setup_standard_lights reverting to old code.")
        # old code, used only to set up some sort of workable lighting in case
        # of bugs (this is not necessarily using the same values as
        # _default_lights; doesn't matter since never used unless there are
        # bugs)
        glLightfv(GL_LIGHT0, GL_POSITION, (-50, 70, 30, 0))
        glLightfv(GL_LIGHT0, GL_AMBIENT, (0.3, 0.3, 0.3, 1.0))
        glLightfv(GL_LIGHT0, GL_DIFFUSE, (0.8, 0.8, 0.8, 1.0))
        glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 1.0)

        glLightfv(GL_LIGHT1, GL_POSITION, (-20, 20, 20, 0))
        glLightfv(GL_LIGHT1, GL_AMBIENT, (0.4, 0.4, 0.4, 1.0))
        glLightfv(GL_LIGHT1, GL_DIFFUSE, (0.4, 0.4, 0.4, 1.0))
        glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, 1.0)

        glLightfv(GL_LIGHT2, GL_POSITION, (0, 0, 100, 0))
        glLightfv(GL_LIGHT2, GL_AMBIENT, (1.0, 1.0, 1.0, 1.0))
        glLightfv(GL_LIGHT2, GL_DIFFUSE, (1.0, 1.0, 1.0, 1.0))
        glLightf(GL_LIGHT2, GL_CONSTANT_ATTENUATION, 1.0)

        glEnable(GL_LIGHTING)

        glEnable(GL_LIGHT0)
        glEnable(GL_LIGHT1)
        glDisable(GL_LIGHT2)
    return # from setup_standard_lights

# ==

def setup_fog(fog_start, fog_end, fog_color):

    glFog(GL_FOG_MODE, GL_LINEAR)
    glFog(GL_FOG_START, fog_start)
    glFog(GL_FOG_END, fog_end)
    if len(fog_color) == 3:
        fog_color = (fog_color[0], fog_color[1], fog_color[2], 1.0)
    glFogv(GL_FOG_COLOR, fog_color) # piotr 080515

def enable_fog():
    glEnable(GL_FOG)

def disable_fog():
    glDisable(GL_FOG)

# ==

# grantham 20051121, renamed 20051201; revised by bruce 051126, 051203 (added
# specular_brightness), 051215, 090304 (added optional glprefs arg)

### REVIEW: should apply_material be a method of GLPrefs and perhaps GLPane?
# Or at least, should its glprefs arg be required (removing the last use of
# drawing_globals in this module)? [And in the meantime, moved to that module?]
#
# Probably yes, but making all uses know glprefs requires passing glpane or glprefs
# args or attrs into quite a few functions or methods or instances that don't
# have them yet, including at least CSDL, which ought to have a glprefs or glpane
# attr for use in .draw (as well as a GL resource context attr for other reasons),
# and the bond drawing code. So I don't quite have time to do it now. But it would
# be a good refactoring to do, for itself and to help refactor other things, and
# not too hard to do when someone has time. [bruce 090304 comment]

def apply_material(color, glprefs = None):
    # todo: move into glprefs.py (see comment above for why)
    """
    In the current GL context,
    set material parameters based on the given color (length 3 or 4) and
    the material-related prefs values in glprefs (which defaults to
    drawing_globals.glprefs).
    """
    if glprefs is None:
        glprefs = drawing_globals.glprefs
        pass

    #bruce 051215: make sure color is a tuple, and has length exactly 4, for all
    # uses inside this function, assuming callers pass sequences of length 3 or
    # 4. Needed because glMaterial requires four-component vector and PyOpenGL
    # doesn't check. [If this is useful elsewhere, we can split it into a
    # separate function.]
    color = tuple(color)
    if len(color) == 3:
        color = color + (1.0,) # usual case
    elif len(color) != 4:
        # should never happen; if it does, this assert will always fail
        assert len(color) in [3,4], \
               "color tuples must have length 3 or 4, unlike %r" % (color,)

    glColor4fv(color)          # For drawing lines with lighting disabled.

    if not glprefs.enable_specular_highlights:
        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color)
        # glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, (0,0,0,1))
        return

    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color)

    whiteness = glprefs.specular_whiteness
    brightness = glprefs.specular_brightness
    if whiteness == 1.0:
        specular = (1.0, 1.0, 1.0, 1.0) # optimization
    else:
        if whiteness == 0.0:
            specular = color # optimization
        else:
            # assume color[3] (alpha) is not passed or is always 1.0
            c1 = 1.0 - whiteness
            specular = ( c1 * color[0] + whiteness,
                         c1 * color[1] + whiteness,
                         c1 * color[2] + whiteness, 1.0 )
    if brightness != 1.0:
        specular = ( specular[0] * brightness,
                     specular[1] * brightness,
                     specular[2] * brightness, 1.0 )
            #e Could optimize by merging this with above 3 cases (or, of course,
            #  by doing it in C, which we'll do eventually.)
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular)

    glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS,
                glprefs.specular_shininess)
    return

# end