2
# Copyright (C) 2003-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
18
cdef class _Vertex(_Point):
19
#cdef float _tex_x, _tex_y
20
#cdef _diffuse, _emissive
24
cdef __getcstate__(self):
25
#return struct.pack("<fffff", self._matrix[0], self._matrix[1], self._matrix[2], self._tex_x, self._tex_y), self._parent, self._diffuse, self._emissive
28
chunk_add_floats_endian_safe(chunk, self._matrix, 3)
29
#chunk_add_float_endian_safe (chunk, self._tex_x)
30
#chunk_add_float_endian_safe (chunk, self._tex_y)
31
chunk_add_floats_endian_safe (chunk, &self._tex_x, 1)
32
chunk_add_floats_endian_safe (chunk, &self._tex_y, 1)
33
return drop_chunk_to_string(chunk), self._parent, self._diffuse, self._emissive
35
cdef void __setcstate__(self, cstate):
36
cstate2, self._parent, self._diffuse, self._emissive = cstate
37
#self._matrix[0], self._matrix[1], self._matrix[2], self._tex_x, self._tex_y = struct.unpack("<fffff", cstate2)
41
chunk = string_to_chunk(cstate2)
42
chunk_get_floats_endian_safe(chunk, self._matrix, 3)
43
#self._tex_x = chunk_get_float_endian_safe(chunk)
44
#self._tex_y = chunk_get_float_endian_safe(chunk)
45
chunk_get_float_endian_safe(chunk, &self._tex_x)
46
chunk_get_float_endian_safe(chunk, &self._tex_y)
49
# cdef float* cstate_data
50
# cstate_data = <float*> PyString_AS_STRING(cstate2)
51
# self._matrix[0] = cstate_data[0]
52
# self._matrix[1] = cstate_data[1]
53
# self._matrix[2] = cstate_data[2]
54
# self._tex_x = cstate_data[3]
55
# self._tex_y = cstate_data[4]
60
def __set__(self, float x):
66
def __set__(self, float x):
69
property color: # Compatibility, use rather diffuse
91
def __init__(self, CoordSyst parent = None, float x = 0.0, float y = 0.0, float z = 0.0, float tex_x = 0.0, float tex_y = 0.0, diffuse = None, emissive = None):
92
"""Vertex(parent = None, x = 0.0, y = 0.0, z = 0.0, tex_x = 0.0, tex_y = 0.0, diffuse = None, emissive = None)
94
Creates a new Vertex in coordinate systems PARENT, at position X, Y, Z, with texture
95
coordinates TEX_X and TEX_Y, and the given DIFFUSE and EMISSIVE colors."""
96
Point.__init__(self, parent, x, y, z)
99
self._diffuse = diffuse
100
self._emissive = emissive
102
cdef void _render(self, CoordSyst coord_syst):
104
glTexCoord2f(self._tex_x, self._tex_y)
105
if not self._diffuse is None:
106
glMaterialfv(GL_FRONT, GL_DIFFUSE, <float*> self._diffuse)
107
if not self._emissive is None:
108
glMaterialfv(GL_FRONT, GL_EMISSION, <float*> self._emissive)
109
if coord_syst is None: glVertex3fv(self._matrix)
111
self._into(coord_syst, coords)
114
cdef float _angle_at(self):
115
cdef float u1[3], u2[3]
117
index = self._face._vertices.index(self)
118
vector_from_points(u1, (<_Vertex> (self._face._vertices[(index + 1) % len(self._face._vertices)]))._matrix, self._matrix)
119
vector_from_points(u2, (<_Vertex> (self._face._vertices[ index - 1 ]))._matrix, self._matrix)
120
return vector_angle(u1, u2)
123
cdef class _Face(CoordSyst):
124
#cdef object _vertices
125
#cdef _Material _material
126
#cdef _Vector _normal
130
return self._option & FACE2_LIT
131
def __set__(self, int x):
132
if x: self._option = self._option | FACE2_LIT
133
else: self._option = self._option & ~FACE2_LIT
137
return self._option & FACE2_SMOOTH_LIT
138
def __set__(self, int x):
139
if x: self._option = self._option | FACE2_SMOOTH_LIT
140
else: self._option = self._option & ~FACE2_SMOOTH_LIT
144
return self._option & FACE2_STATIC_LIT
145
def __set__(self, int x):
146
if x: self._option = self._option | FACE2_STATIC_LIT
147
else: self._option = self._option & ~FACE2_STATIC_LIT
149
property double_sided:
151
return self._option & FACE2_DOUBLE_SIDED
152
def __set__(self, int x):
153
if x: self._option = self._option | FACE2_DOUBLE_SIDED
154
else: self._option = self._option & ~FACE2_DOUBLE_SIDED
158
return self._material
159
def __set__(self, _Material x not None):
164
return self._vertices
168
self._compute_normal()
171
def __init__(self, _World parent = None, vertices = None, _Material material = None):
172
"""Face(parent = None, vertices = None, material = None) -> Face
174
Creates a new Face in World PARENT, with the given list of VERTICES,
175
and the given Material."""
177
CoordSyst.__init__(self, parent)
178
self._vertices = vertices or []
179
self._material = material or _DEFAULT_MATERIAL
180
self._option = FACE2_LIT | FACE2_STATIC_LIT
181
for vertex in self.vertices: vertex._face = self
183
cdef __getcstate__(self):
184
#return struct.pack("<ifffffffffffffffffffiiiii", self._option, self._matrix[0], self._matrix[1], self._matrix[2], self._matrix[3], self._matrix[4], self._matrix[5], self._matrix[6], self._matrix[7], self._matrix[8], self._matrix[9], self._matrix[10], self._matrix[11], self._matrix[12], self._matrix[13], self._matrix[14], self._matrix[15], self._matrix[16], self._matrix[17], self._matrix[18], self.double_sided, self.lit, self.static_lit, self.smooth_lit, self.solid), self._vertices, self._material
187
chunk_add_int_endian_safe (chunk, self._option)
188
chunk_add_floats_endian_safe(chunk, self._matrix, 19)
189
return drop_chunk_to_string(chunk), self._vertices, self._material
191
cdef void __setcstate__(self, cstate):
192
cstate2, self._vertices, self._material = cstate
193
#self._option, self._matrix[0], self._matrix[1], self._matrix[2], self._matrix[3], self._matrix[4], self._matrix[5], self._matrix[6], self._matrix[7], self._matrix[8], self._matrix[9], self._matrix[10], self._matrix[11], self._matrix[12], self._matrix[13], self._matrix[14], self._matrix[15], self._matrix[16], self._matrix[17], self._matrix[18], self.double_sided, self.lit, self.static_lit, self.smooth_lit, self.solid = struct.unpack("<ifffffffffffffffffffiiiii", cstate2)
195
for vertex in self._vertices: vertex._face = self
198
chunk = string_to_chunk(cstate2)
199
chunk_get_int_endian_safe(chunk, &self._option)
200
chunk_get_floats_endian_safe(chunk, self._matrix, 19)
202
self._validity = COORDSYS_INVALID
204
def insert(self, int index, _Vertex vertex not None):
205
"""Face.insert(index, vertex)
207
Inserts the given VERTEX to the Face at position INDEX."""
209
self._vertices.insert(index, vertex)
211
def append(self, _Vertex vertex not None):
212
"""Face.append(vertex)
214
Appends the given VERTEX to the Face."""
216
self._vertices.append(vertex)
218
def add(self, _Vertex vertex not None):
221
Appends the given VERTEX to the Face."""
224
cdef void _compute_normal(self):
225
# Computes the normal vector of the Face
226
cdef float a[3], b[3], c[3]
227
a[0] = a[1] = a[2] = b[0] = b[1] = b[2] = c[0] = c[1] = c[2] = 0.0
228
if len(self._vertices) > 2:
229
if self._normal is None: self._normal = Vector(self._parent)
230
else: self._normal._parent = self._parent
231
(<_Vertex> self._vertices[0])._into(self._parent, a)
232
(<_Vertex> self._vertices[1])._into(self._parent, b)
233
(<_Vertex> self._vertices[2])._into(self._parent, c)
234
face_normal(self._normal._matrix, a, b, c)
235
vector_normalize(self._normal._matrix)
236
else: self._normal = None
238
def is_coplanar(self, float threshold = 0.005):
239
"""is_coplanar(threshold = 0.005) -> boolean
241
Returns true if the Face's vertices are all coplanar."""
243
if len(self._vertices) < 4: return 1
247
self._compute_normal()
249
n2 = Vector(self._parent)
251
cdef float a[3], b[3], c[3]
252
(<_Vertex> self._vertices[0])._into(self._parent, a)
253
(<_Vertex> self._vertices[1])._into(self._parent, b)
255
for vertex in self._vertices[2:]:
256
vertex._into(self._parent, c)
257
face_normal(n2._matrix, a, b, c)
258
vector_normalize(n2._matrix)
260
if n1.distance_to(n2) > threshold: return 0
263
def is_colored(self):
264
"""is_colored() -> boolean
266
Returns true if the Face is colored, i.e. at least one of its Vertices is colored."""
268
for vertex in self._vertices:
269
if not vertex._diffuse is None: return 1
273
"""is_alpha() -> boolean
275
Returns true if the Face is alpha-blended."""
276
return (self._material and self._material.is_alpha()) or self.has_alpha_vertex()
278
def has_alpha_vertex(self):
279
"""has_alpha_vertex() -> boolean
281
Returns true if the Face has at least one alpha blended Vertex."""
283
for vertex in self._vertices:
284
if (not vertex._diffuse is None) and vertex._diffuse[3] < 1.0: return 1
287
cdef void _batch(self, CoordSyst coord_syst):
288
if self.is_alpha(): renderer._batch(renderer.alpha , self, coord_syst, -1)
289
else: renderer._batch(renderer.opaque, self, coord_syst, -1)
291
cdef void _render(self, CoordSyst coord_syst):
294
i = len(self._vertices)
297
self._material._activate()
299
if not(self._option & FACE2_LIT): glDisable(GL_LIGHTING)
300
if self._option & FACE2_DOUBLE_SIDED:
301
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE)
302
glDisable(GL_CULL_FACE)
304
self._compute_normal()
305
if not self._normal is None: glNormal3fv(self._normal._matrix)
307
if i == 1: glBegin(GL_POINTS)
308
elif i == 2: glBegin(GL_LINES)
309
elif i == 3: glBegin(GL_TRIANGLES)
310
elif i == 4: glBegin(GL_QUADS)
311
else: glBegin(GL_POLYGON)
313
for vertex in self._vertices: vertex._render(self._parent)
316
if self._option & FACE2_DOUBLE_SIDED:
317
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE)
318
glEnable(GL_CULL_FACE)
320
if not(self._option & FACE2_LIT): glEnable(GL_LIGHTING)
323
return iter(self._vertices)
324
def __getitem__(self, index):
325
return self._vertices[index]
326
# Pyrex does not like this one
327
#def __contains__(self, vertex):
328
# return vertex in self._vertices
330
return len(self._vertices)
334
i = len(self._vertices)
335
if i == 1: r = "<Plot"
336
elif i == 2: r = "<Line"
337
elif i == 3: r = "<Triangle"
338
elif i == 4: r = "<Quad"
340
if not self._material is _DEFAULT_MATERIAL: r = r + ", material %s" % self._material.filename
343
cdef void _get_box(self, float* box, float* matrix):
347
for vertex in self._vertices:
348
point_by_matrix_copy(coord, vertex._matrix, matrix)
350
if coord[0] < box[0]: box[0] = coord[0]
351
if coord[1] < box[1]: box[1] = coord[1]
352
if coord[2] < box[2]: box[2] = coord[2]
353
if coord[0] > box[3]: box[3] = coord[0]
354
if coord[1] > box[4]: box[4] = coord[1]
355
if coord[2] > box[5]: box[5] = coord[2]
357
cdef void _raypick(self, RaypickData data, CoordSyst parent):
358
cdef float* p, r, root_r
360
cdef int nb_vertices, i, option
361
nb_vertices = len(self._vertices)
362
if nb_vertices < 3: return
364
if self.double_sided == 1: option = option & ~RAYPICK_CULL_FACE
366
# get vertices coordinates
367
p = <float*> malloc(nb_vertices * 3 * sizeof(float))
368
for i from 0 <= i < nb_vertices: (<_Vertex> (self._vertices[i]))._into(self, p + 3 * i)
370
face_normal(normal, p, p + 3, p + 6)
371
vector_normalize(normal)
372
if nb_vertices == 3: i = triangle_raypick(self._raypick_data(data), p, p + 3, p + 6, normal, option, &r)
373
elif nb_vertices == 4: i = quad_raypick (self._raypick_data(data), p, p + 3, p + 6, p + 9, normal, option, &r)
374
else: raise ValueError("Raypicking on a face with more than 4 vertices is not supported yet.")
377
root_r = self._distance_out(r)
378
if (data.result_coordsyst is None) or (fabs(root_r) < fabs(data.root_result)):
379
if i == RAYPICK_DIRECT:
381
data.root_result = root_r
382
data.result_coordsyst = self
383
memcpy(data.normal, normal, 3 * sizeof(float))
384
elif i == RAYPICK_INDIRECT:
386
data.result_coordsyst = self
387
if self.double_sided == 1:
388
data.normal[0] = -normal[0]
389
data.normal[1] = -normal[1]
390
data.normal[2] = -normal[2]
391
else: memcpy (data.normal, normal, 3 * sizeof(float))
395
cdef int _raypick_b(self, RaypickData data, CoordSyst parent):
398
cdef int nb_vertices, i, option
400
nb_vertices = len(self._vertices)
401
if nb_vertices < 3: return 0
403
if (option & RAYPICK_CULL_FACE) and (self.double_sided == 1): option = option & ~RAYPICK_CULL_FACE
405
# get vertices coordinates
406
p = <float*> malloc(nb_vertices * 3 * sizeof(float))
407
for i from 0 <= i < nb_vertices: (<_Vertex> (self._vertices[i]))._into(self._parent, p + 3 * i)
409
face_normal(normal, p, p + 3, p + 6)
410
vector_normalize(normal)
411
if nb_vertices == 3: i = triangle_raypick(self._raypick_data(data), p, p + 3, p + 6, normal, option, &r)
412
elif nb_vertices == 4: i = quad_raypick (self._raypick_data(data), p, p + 3, p + 6, p + 9, normal, option, &r)
413
else: raise ValueError("Raypicking on a face with more than 4 vertices is not supported yet.")
418
cdef void _collect_raypickables(self, Chunk* items, float* rsphere, float* sphere):
419
if self._option & NON_SOLID: return
421
# XXX not really implemented -- no selection
422
chunk_add_ptr(items, <void*> self)