2
# Copyright (C) 2004 Jean-Baptiste LAMY -- jiba@tuxfamily.org
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU General Public License for more details.
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
cdef class _CellShadingShape(_SimpleShape):
20
#cdef _Material _shader
21
#cdef float _outline_color[4]
22
#cdef float _outline_width, _outline_attenuation
24
cdef __getcstate__(self):
27
chunk_add_float_endian_safe (chunk, self._outline_width)
28
chunk_add_float_endian_safe (chunk, self._outline_attenuation)
29
chunk_add_floats_endian_safe(chunk, self._outline_color, 4)
30
return _SimpleShape.__getcstate__(self), drop_chunk_to_string(chunk), self._shader
32
cdef void __setcstate__(self, cstate):
33
_SimpleShape.__setcstate_data__(self, cstate[0])
36
chunk = string_to_chunk(cstate[1])
37
chunk_get_float_endian_safe (chunk, &self._outline_width)
38
chunk_get_float_endian_safe (chunk, &self._outline_attenuation)
39
chunk_get_floats_endian_safe(chunk, self._outline_color, 4)
41
self._shader = cstate[2]
43
# Build the display list data, but don't create the corresponding OpenGL display list
44
self._build_display_list()
46
cdef void _build_cellshading(self, _Material shader, outline_color, float outline_width, float outline_attenuation):
49
self._outline_width = outline_width
50
self._outline_attenuation = outline_attenuation
51
for i from 0 <= i < 4: self._outline_color[i] = outline_color[i]
53
cdef void _batch(self, CoordSyst coordsyst):
54
if quality == QUALITY_LOW:
55
_SimpleShape._batch(self, coordsyst)
61
frustum = renderer._frustum(coordsyst)
62
if (self._option & SHAPE_HAS_SPHERE) and (sphere_in_frustum(frustum, self._sphere) == 0): return
64
if self._display_lists.nb_opaque_list != 0: renderer._batch(renderer.opaque, self, coordsyst, -1)
65
if self._display_lists.nb_alpha_list != 0: renderer._batch(renderer.alpha , self, coordsyst, -1)
68
# for i from 0 <= i < self._nb_faces: self._batch_face(self._faces + i)
69
# pack_batch_end(self, coordsyst)
72
if self._outline_width > 0.0: renderer._batch(renderer.secondpass, self, coordsyst, 0)
75
# cdef void _render(self, CoordSyst coordsyst):
76
# cdef int i, start, end
77
# cdef Frustum* frustum
81
# if renderer.state == RENDERER_STATE_SECONDPASS:
82
# frustum = renderer._frustum(coordsyst)
83
# self._render_outline(frustum)
85
# shape_option_activate(self._option)
88
# chunk_register(chunk, self._nb_vnormals * sizeof(float))
89
# shades = <float*> chunk.content
90
# self._prepare_cellshading(coordsyst, shades)
92
# self._pack_render_cellshading(shades)
94
# shape_option_inactivate(self._option)
97
# cdef void _pack_render_cellshading(self, float* shades):
99
# cdef ShapeFace* face
100
# cdef _Material material
102
# # Activate shader texture
103
# glActiveTexture(GL_TEXTURE1)
104
# if self._shader._id == 0: self._shader._init_texture()
105
# glEnable (GL_TEXTURE_2D)
106
# glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE)
107
# glBindTexture (GL_TEXTURE_2D, self._shader._id)
108
# glActiveTexture(GL_TEXTURE0)
110
# pack = <Pack*> chunk_get_ptr(renderer.data)
112
# material = <_Material> (pack.material_id)
113
# material._activate()
115
# face_option_activate(pack.option)
117
# face = <ShapeFace*> chunk_get_ptr(renderer.data)
118
# if pack.option & FACE_TRIANGLE:
119
# glBegin(GL_TRIANGLES)
121
# self._render_triangle_cellshading(face, shades)
122
# face = <ShapeFace*> chunk_get_ptr(renderer.data)
124
# elif pack.option & FACE_QUAD:
127
# self._render_quad_cellshading(face, shades)
128
# face = <ShapeFace*> chunk_get_ptr(renderer.data)
132
# face_option_inactivate(pack.option)
133
# pack = <Pack*> chunk_get_ptr(renderer.data)
135
# # Unactivate shader texture
136
# glActiveTexture(GL_TEXTURE1)
137
# glDisable(GL_TEXTURE_2D)
138
# glActiveTexture(GL_TEXTURE0)
141
cdef void _render(self, CoordSyst coordsyst):
142
if quality == QUALITY_LOW:
143
_SimpleShape._render(self, coordsyst)
146
cdef int i, start, end
148
cdef Frustum* frustum
151
cdef _Material material
152
cdef DisplayList* display_list
154
if renderer.state == RENDERER_STATE_SECONDPASS:
155
frustum = renderer._frustum(coordsyst)
156
self._render_outline(frustum)
158
shape_option_activate(self._option)
161
chunk_register(chunk, self._nb_vnormals * sizeof(float))
162
shades = <float*> chunk.content
163
self._prepare_cellshading(coordsyst, shades)
165
if renderer.state == RENDERER_STATE_OPAQUE:
167
end = self._display_lists.nb_opaque_list
169
start = self._display_lists.nb_opaque_list
170
end = start + self._display_lists.nb_alpha_list
172
# Activate shader texture
173
glActiveTexture(GL_TEXTURE1)
174
if self._shader._id == 0: self._shader._init_texture()
175
glEnable (GL_TEXTURE_2D)
176
glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL)
177
glBindTexture (GL_TEXTURE_2D, self._shader._id)
178
glActiveTexture(GL_TEXTURE0)
180
for i from start <= i < end:
181
display_list = self._display_lists.display_lists + i
183
material = <_Material> (display_list.material_id)
186
face_option_activate(display_list.option)
188
face_id = display_list.faces_id
189
if display_list.option & FACE_TRIANGLE:
190
glBegin(GL_TRIANGLES)
191
while face_id[0] != -1:
192
self._render_triangle_cellshading(self._faces + face_id[0], shades)
193
face_id = face_id + 1
195
elif display_list.option & FACE_QUAD:
197
while face_id[0] != -1:
198
self._render_quad_cellshading(self._faces + face_id[0], shades)
199
face_id = face_id + 1
203
face_option_inactivate(display_list.option)
205
# Unactivate shader texture
206
glActiveTexture(GL_TEXTURE1)
207
glDisable (GL_TEXTURE_2D)
208
glActiveTexture(GL_TEXTURE0)
212
shape_option_inactivate(self._option)
215
cdef void _render_outline(self, Frustum* frustum):
216
cdef int i, j, k, ns, nb
219
cdef ShapeFace* face, neighbor_face
221
# Compute outline width, which depends on distance to camera
222
d = sphere_distance_point(self._sphere, frustum.position) * self._outline_attenuation
223
if d < 1.0: d = self._outline_width
225
d = self._outline_width / d
228
_DEFAULT_MATERIAL._activate()
230
glColor4fv (self._outline_color)
231
glDisable (GL_LIGHTING)
232
glDepthFunc(GL_LEQUAL)
234
# mark faces as either front or back
235
for i from 0 <= i < self._nb_faces:
236
face = self._faces + i
237
plane = self._values + face.normal
238
if plane[0] * frustum.position[0] + plane[1] * frustum.position[1] + plane[2] * frustum.position[2] + plane[3] > 0.0:
239
face.option = (face.option & ~FACE_BACK ) | FACE_FRONT
241
face.option = (face.option & ~FACE_FRONT) | FACE_BACK
244
# # find and draw edges
246
# for i from 0 <= i < self._nb_faces:
247
# face = self._faces + i
248
# if face.option & FACE_FRONT:
249
# # test if neighbors are back
250
# for j from 0 <= j < 4:
251
# k = self._neighbors[4 * i + j]
253
# neighbor_face = self._faces[k]
254
# if neighbor_face.option & FACE_BACK:
255
# # draw edge between vertices k and k + 1
256
# #print "an edge", (face.option & FACE_QUAD),
257
# #print " ", i, j, k
258
# #print " ", face.v[j]
259
# #print " ", self._vertex_coords[face.v[j]]
260
# glVertex3fv(self._coords + self._vertex_coords[face.v[j]])
263
# if ((face.option & FACE_QUAD) and (j < 3)) or (j < 2):
264
# glVertex3fv(self._coords + self._vertex_coords[face.v[j + 1]])
266
# glVertex3fv(self._coords + self._vertex_coords[face.v[ 0]])
272
# chunk = get_chunk()
273
# chunk_register(chunk, self._nb_coords * sizeof(int))
274
# cdef int* vertex2next
275
# vertex2next = <int*> (chunk.content)
276
# for i from 0 <= i < self._nb_coords: vertex2next[i] = -1
279
# #print "draw edges", self._nb_coords
280
# for i from 0 <= i < self._nb_faces:
281
# face = self._faces + i
282
# if face.option & FACE_FRONT:
283
# # test if neighbors are back
284
# for j from 0 <= j < 4:
285
# k = self._neighbors[4 * i + j]
287
# neighbor_face = self._faces[k]
288
# if neighbor_face.option & FACE_BACK:
289
# # draw edge between vertices k and k + 1
290
# if ((face.option & FACE_QUAD) and (j < 3)) or (j < 2):
291
# vertex2next[self._vertex_coords[face.v[j]] / 3] = self._vertex_coords[face.v[j + 1]] / 3
293
# vertex2next[self._vertex_coords[face.v[j]] / 3] = self._vertex_coords[face.v[0]] / 3
296
# for i from 0 <= i < self._nb_coords:
297
# if vertex2next[i] >= 0:
299
# glBegin(GL_LINE_STRIP)
300
# while vertex2next[j] >= 0:
301
# glVertex3fv(self._coords + j * 3)
302
# k = vertex2next[j] # Next vertex
303
# vertex2next[j] = -2
306
# glVertex3fv(self._coords + j * 3)
315
chunk_register(chunk, self._nb_coords * sizeof(int))
316
cdef int* vertex_used
317
vertex_used = <int*> (chunk.content)
318
for i from 0 <= i < self._nb_coords: vertex_used[i] = -1
321
# find and draw edges
323
for i from 0 <= i < self._nb_faces:
324
face = self._faces + i
325
if face.option & FACE_ALPHA: continue
327
if face.option & FACE_QUAD: nb = 4
330
if face.option & FACE_SMOOTH_LIT:
331
if face.option & FACE_DOUBLE_SIDED:
332
for j from 0 <= j < nb:
333
k = self._neighbors[4 * i + j]
334
if k == -1: # No neighbor, but double-sided face => the face is its own neighbor
335
vertex_used[self._vertex_coords[face.v[j]] / 3] = 1
337
# draw edge between vertices j and j + 1
338
glVertex3fv(self._coords + self._vertex_coords[face.v[j]])
339
if j < nb - 1: glVertex3fv(self._coords + self._vertex_coords[face.v[j + 1]])
340
else: glVertex3fv(self._coords + self._vertex_coords[face.v[ 0]])
343
ns = self._neighbors_side[4 * i + j]
344
neighbor_face = self._faces[k]
346
(ns == -1) and (((face.option & FACE_FRONT) and (neighbor_face.option & FACE_BACK )) or ((face.option & FACE_BACK) and (neighbor_face.option & FACE_FRONT)))
348
(ns == 1) and (((face.option & FACE_FRONT) and (neighbor_face.option & FACE_FRONT)) or ((face.option & FACE_BACK) and (neighbor_face.option & FACE_BACK)))
350
vertex_used[self._vertex_coords[face.v[j]] / 3] = 1
352
# draw edge between vertices j and j + 1
353
glVertex3fv(self._coords + self._vertex_coords[face.v[j]])
354
if j < nb - 1: glVertex3fv(self._coords + self._vertex_coords[face.v[j + 1]])
355
else: glVertex3fv(self._coords + self._vertex_coords[face.v[ 0]])
358
if face.option & FACE_FRONT:
359
# test if neighbors are back
360
for j from 0 <= j < nb:
361
k = self._neighbors[4 * i + j]
362
if (k == -1) or (self._faces[k].option & FACE_BACK):
363
vertex_used[self._vertex_coords[face.v[j]] / 3] = 1
365
# draw edge between vertices j and j + 1
366
glVertex3fv(self._coords + self._vertex_coords[face.v[j]])
367
if j < nb - 1: glVertex3fv(self._coords + self._vertex_coords[face.v[j + 1]])
368
else: glVertex3fv(self._coords + self._vertex_coords[face.v[ 0]])
370
else: # Not smoothlit
371
if (face.option & FACE_FRONT) or (face.option & FACE_DOUBLE_SIDED):
372
for j from 0 <= j < nb:
373
# draw edge between vertices j and j + 1
374
glVertex3fv(self._coords + self._vertex_coords[face.v[j]])
375
if j < nb - 1: glVertex3fv(self._coords + self._vertex_coords[face.v[j + 1]])
376
else: glVertex3fv(self._coords + self._vertex_coords[face.v[ 0]])
383
for i from 0 <= i < self._nb_coords:
384
if vertex_used[i] == 1: glVertex3fv(self._coords + i * 3)
390
glLineWidth(1.0) # Reset to default
391
glPointSize(1.0) # Reset to default
392
glEnable (GL_LIGHTING)
397
cdef float _vertex_compute_cellshading(self, float* coord, float* normal, lights, float shade):
404
if light._w == 0.0: # directional light
405
tmp = -vector_dot_product(normal, light._data)
406
else: # positional light
407
ptr[0] = light._data[0] - coord[0]
408
ptr[1] = light._data[1] - coord[1]
409
ptr[2] = light._data[2] - coord[2]
410
vector_normalize(ptr)
411
tmp = vector_dot_product(normal, ptr)
417
cdef void _prepare_cellshading_shades(self, float* shades, lights):
419
cdef float* ptr1, *ptr2
425
ptr1 = self._vnormals
426
if light._w == 0.0: # directional light
427
for j from 0 <= j < self._nb_vnormals:
428
tmp = -vector_dot_product(ptr1, light._data)
429
shades[j] = shades[j] + tmp
432
else: # positional light
434
for j from 0 <= j < self._nb_vnormals:
435
v[0] = light._data[0] - ptr2[0]
436
v[1] = light._data[1] - ptr2[1]
437
v[2] = light._data[2] - ptr2[2]
439
tmp = vector_dot_product(ptr1, v)
440
shades[j] = shades[j] + tmp
444
cdef void _prepare_cellshading(self, CoordSyst coordsyst, float* shades):
447
for light in renderer.top_lights: light._cast_into(coordsyst)
448
for light in renderer.current_context.lights: light._cast_into(coordsyst)
450
if self._nb_vnormals > 0: # Else the shades are computed at the vertex rendering time, since the shades are not shared (all (coord,normal) couples are different)
451
for n from 0 <= n < self._nb_vnormals: shades[n] = 0.5
452
self._prepare_cellshading_shades(shades, renderer.top_lights)
453
self._prepare_cellshading_shades(shades, renderer.current_context.lights)
455
# clip shade texcoord values
456
for n from 0 <= n < self._nb_vnormals:
457
# do not clip with interval [0, 1] because smooth magnification of texture
459
if shades[n] > 0.95: shades[n] = 0.95
460
elif shades[n] < 0.05: shades[n] = 0.05
463
cdef void _render_triangle_cellshading(self, ShapeFace* face, float* shades):
464
if face.option & FACE_SMOOTH_LIT:
465
self._render_vertex_cellshading_smoothlit(face.v[0], face.option, shades)
466
self._render_vertex_cellshading_smoothlit(face.v[1], face.option, shades)
467
self._render_vertex_cellshading_smoothlit(face.v[2], face.option, shades)
469
glNormal3fv(self._values + face.normal)
470
self._render_vertex_cellshading(face.v[0], face.option, self._values + face.normal)
471
self._render_vertex_cellshading(face.v[1], face.option, self._values + face.normal)
472
self._render_vertex_cellshading(face.v[2], face.option, self._values + face.normal)
474
cdef void _render_quad_cellshading(self, ShapeFace* face, float* shades):
475
if face.option & FACE_SMOOTH_LIT:
476
self._render_vertex_cellshading_smoothlit(face.v[0], face.option, shades)
477
self._render_vertex_cellshading_smoothlit(face.v[1], face.option, shades)
478
self._render_vertex_cellshading_smoothlit(face.v[2], face.option, shades)
479
self._render_vertex_cellshading_smoothlit(face.v[3], face.option, shades)
481
glNormal3fv(self._values + face.normal)
482
self._render_vertex_cellshading(face.v[0], face.option, self._values + face.normal)
483
self._render_vertex_cellshading(face.v[1], face.option, self._values + face.normal)
484
self._render_vertex_cellshading(face.v[2], face.option, self._values + face.normal)
485
self._render_vertex_cellshading(face.v[3], face.option, self._values + face.normal)
488
# XXX face_option arg is useless
489
cdef void _render_vertex_cellshading_smoothlit (self, int index, int face_option, float* shades):
493
n = self._vertex_coords[index]
494
coord = self._coords + n
496
if face_option & FACE_NON_LIT:
497
shade = 0.5 # Medium shade
499
shade = shades[n / 3]
501
if self._option & SHAPE_DIFFUSES : glColor4fv (self._colors + self._vertex_diffuses [index])
502
if self._option & SHAPE_EMISSIVES: glMaterialfv (GL_FRONT_AND_BACK, GL_EMISSION, self._colors + self._vertex_emissives[index]) # XXX use glColorMaterial when emissive color but no diffuse ?
503
if self._option & SHAPE_TEXCOORDS:
504
glMultiTexCoord2fv(GL_TEXTURE0, self._values + self._vertex_texcoords[index])
505
glMultiTexCoord2f (GL_TEXTURE1, shade, shade)
506
else: glTexCoord2f(shade, shade)
508
glNormal3fv(self._vnormals + n)
511
# XXX face_option arg is useless
512
cdef void _render_vertex_cellshading(self, int index, int face_option, float* fnormal):
515
coord = self._coords + self._vertex_coords[index]
517
if face_option & FACE_NON_LIT:
518
shade = 0.5 # Medium value
520
shade = self._vertex_compute_cellshading(coord, fnormal, renderer.top_lights, 0.5)
521
shade = self._vertex_compute_cellshading(coord, fnormal, renderer.current_context.lights, shade)
522
if shade < 0.05: shade = 0.05
523
elif shade > 0.95: shade = 0.95
525
if self._option & SHAPE_DIFFUSES : glColor4fv (self._colors + self._vertex_diffuses [index])
526
if self._option & SHAPE_EMISSIVES: glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, self._colors + self._vertex_emissives[index]) # XXX use glColorMaterial when emissive color but no diffuse ?
527
if self._option & SHAPE_TEXCOORDS:
528
glMultiTexCoord2fv(GL_TEXTURE0, self._values + self._vertex_texcoords[index])
529
glMultiTexCoord2f (GL_TEXTURE1, shade, shade)
530
else: glTexCoord2f(shade, shade)