File: GLSLRenderer.py

package info (click to toggle)
pycollada 0.8-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 9,116 kB
  • sloc: xml: 11,085; python: 6,859; makefile: 87
file content (266 lines) | stat: -rw-r--r-- 12,163 bytes parent folder | download | duplicates (2)
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
#!/usr/bin/env python

from __future__ import print_function
from __future__ import absolute_import

import collada
import numpy

import pyglet
from pyglet.gl import *

import ctypes

from . import glutils
from .glutils import VecF
from . import shader
from .shader import Shader
from . import shaders


class GLSLRenderer:

    def __init__(self, dae):
        self.dae = dae
        # To calculate model boundary along Z axis
        self.z_max = -100000.0
        self.z_min = 100000.0
        self.textures = {}
        self.shaders = {}
        self.batch_list = []

        # Initialize OpenGL
        glClearColor(0.0, 0.0, 0.0, 0.5)  # Black Background
        glEnable(GL_DEPTH_TEST)  # Enables Depth Testing
        glEnable(GL_CULL_FACE)
        glEnable(GL_MULTISAMPLE)

        glEnable(GL_BLEND)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

        glEnable(GL_LIGHTING)

        glEnable(GL_LIGHT0)
        glLightfv(GL_LIGHT0, GL_AMBIENT, VecF(0.9, 0.9, 0.9, 1.0))
        glLightfv(GL_LIGHT0, GL_DIFFUSE, VecF(1.0, 1.0, 1.0, 1.0))
        glLightfv(GL_LIGHT0, GL_SPECULAR, VecF(0.3, 0.3, 0.3, 1.0))

        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, VecF(0.1, 0.1, 0.1, 1.0))
        glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, VecF(0.1, 0.1, 0.1, 1.0))
        glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 50)

        print('Running with OpenGL version:', glutils.getOpenGLVersion())
        print('Initializing shaders...')
        #(vert, frag) = shaders.ADSPhong
        (vert, frag) = shaders.simplePhong
        prog = Shader(vert, frag)
        print('  phong')
        self.shaders['phong'] = prog
        (vert, frag) = shaders.pointLightDiff
        prog = Shader(vert, frag)
        self.shaders['lambert'] = prog
        print('  lambert')
        self.shaders['blinn'] = prog
        print('  blinn')
        (vert, frag) = shaders.flatShader
        prog = Shader(vert, frag)
        self.shaders['constant'] = prog
        print('  constant')
        (vert, frag) = shaders.texturePhong
        prog = Shader(vert, frag)
        self.shaders['texture'] = prog
        print('  texture')
        print('  done.')

        print('Creating GL buffer objects for geometry...')
        if self.dae.scene is not None:
            for geom in self.dae.scene.objects('geometry'):
                for prim in geom.primitives():
                    mat = prim.material
                    diff_color = VecF(0.3, 0.3, 0.3, 1.0)
                    spec_color = None
                    shininess = None
                    amb_color = None
                    tex_id = None
                    shader_prog = self.shaders[mat.effect.shadingtype]
                    for prop in mat.effect.supported:
                        value = getattr(mat.effect, prop)
                        # it can be a float, a color (tuple) or a Map
                        # ( a texture )
                        if isinstance(value, collada.material.Map):
                            colladaimage = value.sampler.surface.image

                            # Accessing this attribute forces the
                            # loading of the image using PIL if
                            # available. Unless it is already loaded.
                            img = colladaimage.pilimage
                            if img:  # can read and PIL available
                                shader_prog = self.shaders['texture']
                                # See if we already have texture for this image
                                if colladaimage.id in self.textures:
                                    tex_id = self.textures[colladaimage.id]
                                else:
                                    # If not - create new texture
                                    try:
                                        # get image meta-data
                                        # (dimensions) and data
                                        (ix, iy, tex_data) = (img.size[0], img.size[1], img.tobytes("raw", "RGBA", 0, -1))
                                    except (SystemError, ValueError):
                                        # has no alpha channel,
                                        # synthesize one
                                        (ix, iy, tex_data) = (img.size[0], img.size[1], img.tobytes("raw", "RGBX", 0, -1))
                                    # generate a texture ID
                                    tid = GLuint()
                                    glGenTextures(1, ctypes.byref(tid))
                                    tex_id = tid.value
                                    # make it current
                                    glBindTexture(GL_TEXTURE_2D, tex_id)
                                    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
                                    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
                                    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
                                    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
                                    # copy the texture into the
                                    # current texture ID
                                    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ix, iy, 0, GL_RGBA, GL_UNSIGNED_BYTE, tex_data)

                                    self.textures[colladaimage.id] = tex_id
                            else:
                                print('  %s = Texture %s: (not available)' % (
                                    prop, colladaimage.id))
                        else:
                            if prop == 'diffuse' and value is not None:
                                diff_color = value
                            elif prop == 'specular' and value is not None:
                                spec_color = value
                            elif prop == 'ambient' and value is not None:
                                amb_color = value
                            elif prop == 'shininess' and value is not None:
                                shininess = value

                    # use primitive-specific ways to get triangles
                    prim_type = type(prim).__name__
                    if prim_type == 'BoundTriangleSet':
                        triangles = prim
                    elif prim_type == 'BoundPolylist':
                        triangles = prim.triangleset()
                    else:
                        print('Unsupported mesh used:', prim_type)
                        triangles = None

                    if triangles is not None:
                        triangles.generateNormals()
                        # We will need flat lists for VBO (batch) initialization
                        vertices = triangles.vertex.flatten().tolist()
                        batch_len = len(vertices) // 3
                        indices = triangles.vertex_index.flatten().tolist()
                        normals = triangles.normal.flatten().tolist()

                        batch = pyglet.graphics.Batch()

                        # Track maximum and minimum Z coordinates
                        # (every third element) in the flattened
                        # vertex list
                        ma = max(vertices[2::3])
                        if ma > self.z_max:
                            self.z_max = ma

                        mi = min(vertices[2::3])
                        if mi < self.z_min:
                            self.z_min = mi

                        if tex_id is not None:

                            # This is probably the most inefficient
                            # way to get correct texture coordinate
                            # list (uv). I am sure that I just do not
                            # understand enough how texture
                            # coordinates and corresponding indexes
                            # are related to the vertices and vertex
                            # indices here, but this is what I found
                            # to work. Feel free to improve the way
                            # texture coordinates (uv) are collected
                            # for batch.add_indexed() invocation.
                            uv = [[0.0, 0.0]] * batch_len
                            for t in triangles:
                                nidx = 0
                                texcoords = t.texcoords[0]
                                for vidx in t.indices:
                                    uv[vidx] = texcoords[nidx].tolist()
                                    nidx += 1
                            # Flatten the uv list
                            uv = [item for sublist in uv for item in sublist]

                            # Create textured batch
                            batch.add_indexed(batch_len,
                                              GL_TRIANGLES,
                                              None,
                                              indices,
                                              ('v3f/static', vertices),
                                              ('n3f/static', normals),
                                              ('t2f/static', uv))
                        else:
                            # Create colored batch
                            batch.add_indexed(batch_len,
                                              GL_TRIANGLES,
                                              None,
                                              indices,
                                              ('v3f/static', vertices),
                                              ('n3f/static', normals))

                        # Append the batch with supplementary
                        # information to the batch list
                        self.batch_list.append(
                            (batch, shader_prog, tex_id, diff_color,
                             spec_color, amb_color, shininess))
        print('done. Ready to render.')

    def render(self, rotate_x, rotate_y, rotate_z):
        """Render batches created during class initialization"""

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        glLoadIdentity()
        # Place the light far behind our object
        z_offset = self.z_min - (self.z_max - self.z_min) * 3
        light_pos = VecF(100.0, 100.0, 10.0 * -z_offset)
        glLightfv(GL_LIGHT0, GL_POSITION, light_pos)

        # Move the object deeper to the screen and rotate
        glTranslatef(0, 0, z_offset)
        glRotatef(rotate_x, 1.0, 0.0, 0.0)
        glRotatef(rotate_y, 0.0, 1.0, 0.0)
        glRotatef(rotate_z, 0.0, 0.0, 1.0)

        prev_shader_prog = None
        # Draw batches (VBOs)
        for (batch, shader_prog, tex_id, diff_color, spec_color, amb_color, shininess) in self.batch_list:
            # Optimization to not make unnecessary bind/unbind for the
            # shader. Most of the time there will be same shaders for
            # geometries.
            if shader_prog != prev_shader_prog:
                if prev_shader_prog is not None:
                    prev_shader_prog.unbind()
                prev_shader_prog = shader_prog
                shader_prog.bind()

            if diff_color is not None:
                shader_prog.uniformf('diffuse', *diff_color)
            if spec_color is not None:
                shader_prog.uniformf('specular', *spec_color)
            if amb_color is not None:
                shader_prog.uniformf('ambient', *amb_color)
            if shininess is not None:
                shader_prog.uniformf('shininess', shininess)

            if tex_id is not None:
                # We assume that the shader here is 'texture'
                glActiveTexture(GL_TEXTURE0)
                glEnable(GL_TEXTURE_2D)
                glBindTexture(GL_TEXTURE_2D, tex_id)
                shader_prog.uniformi('my_color_texture[0]', 0)

            batch.draw()
        if prev_shader_prog is not None:
            prev_shader_prog.unbind()

    def cleanup(self):
        print('Renderer cleaning up')