~jtaylor/ubuntu/oneiric/soya/fix-780305

« back to all changes in this revision

Viewing changes to model/face.pyx

  • Committer: Bazaar Package Importer
  • Author(s): Marc Dequènes (Duck)
  • Date: 2005-01-30 09:55:06 UTC
  • mfrom: (1.2.1 upstream) (2.1.1 hoary)
  • Revision ID: james.westby@ubuntu.com-20050130095506-f21p6v6cgaobhn5j
Tags: 0.9.2-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Soya 3D
 
2
# Copyright (C) 2003-2004 Jean-Baptiste LAMY -- jiba@tuxfamily.org
 
3
#
 
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.
 
8
#
 
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.
 
13
#
 
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
 
17
 
 
18
cdef class _Vertex(_Point):
 
19
  #cdef float   _tex_x, _tex_y
 
20
  #cdef         _diffuse, _emissive
 
21
  #cdef _Face   _face
 
22
  #cdef _Vector _normal
 
23
  
 
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
 
26
    cdef Chunk* chunk
 
27
    chunk = get_chunk()
 
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
 
34
  
 
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)
 
38
    
 
39
    # don't work ???
 
40
    cdef Chunk* chunk
 
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)
 
47
    drop_chunk(chunk)
 
48
    
 
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]
 
56
    
 
57
  property tex_x:
 
58
    def __get__(self):
 
59
      return self._tex_x
 
60
    def __set__(self, float x):
 
61
      self._tex_x = x
 
62
  
 
63
  property tex_y:
 
64
    def __get__(self):
 
65
      return self._tex_y
 
66
    def __set__(self, float x):
 
67
      self._tex_y = x
 
68
  
 
69
  property color: # Compatibility, use rather diffuse
 
70
    def __get__(self):
 
71
      return self._diffuse
 
72
    def __set__(self, x):
 
73
      self._diffuse = x
 
74
      
 
75
  property diffuse:
 
76
    def __get__(self):
 
77
      return self._diffuse
 
78
    def __set__(self, x):
 
79
      self._diffuse = x
 
80
      
 
81
  property emissive:
 
82
    def __get__(self):
 
83
      return self._emissive
 
84
    def __set__(self, x):
 
85
      self._emissive = x
 
86
      
 
87
  property face:
 
88
    def __get__(self):
 
89
      return self._face
 
90
    
 
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)
 
93
 
 
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)
 
97
    self._tex_x    = tex_x
 
98
    self._tex_y    = tex_y
 
99
    self._diffuse  = diffuse
 
100
    self._emissive = emissive
 
101
    
 
102
  cdef void _render(self, CoordSyst coord_syst):
 
103
    cdef float coords[3]
 
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)
 
110
    else:
 
111
      self._into(coord_syst, coords)
 
112
      glVertex3fv(coords)
 
113
      
 
114
  cdef float _angle_at(self):
 
115
    cdef float u1[3], u2[3]
 
116
    cdef int index
 
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)
 
121
  
 
122
  
 
123
cdef class _Face(CoordSyst):
 
124
  #cdef object     _vertices
 
125
  #cdef _Material  _material
 
126
  #cdef _Vector    _normal
 
127
  
 
128
  property lit:
 
129
    def __get__(self):
 
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
 
134
      
 
135
  property smooth_lit:
 
136
    def __get__(self):
 
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
 
141
      
 
142
  property static_lit:
 
143
    def __get__(self):
 
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
 
148
      
 
149
  property double_sided:
 
150
    def __get__(self):
 
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
 
155
      
 
156
  property material:
 
157
    def __get__(self):
 
158
      return self._material
 
159
    def __set__(self, _Material x not None):
 
160
      self._material = x
 
161
      
 
162
  property vertices:
 
163
    def __get__(self):
 
164
      return self._vertices
 
165
    
 
166
  property normal:
 
167
    def __get__(self):
 
168
      self._compute_normal()
 
169
      return self._normal
 
170
    
 
171
  def __init__(self, _World parent = None, vertices = None, _Material material = None):
 
172
    """Face(parent = None, vertices = None, material = None) -> Face
 
173
 
 
174
Creates a new Face in World PARENT, with the given list of VERTICES,
 
175
and the given Material."""
 
176
    cdef _Vertex vertex
 
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
 
182
    
 
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
 
185
    cdef Chunk* chunk
 
186
    chunk = get_chunk()
 
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
 
190
  
 
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)
 
194
    cdef _Vertex vertex
 
195
    for vertex in self._vertices: vertex._face = self
 
196
    
 
197
    cdef Chunk* chunk
 
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)
 
201
    drop_chunk(chunk)
 
202
    self._validity = COORDSYS_INVALID
 
203
    
 
204
  def insert(self, int index, _Vertex vertex not None):
 
205
    """Face.insert(index, vertex)
 
206
 
 
207
Inserts the given VERTEX to the Face at position INDEX."""
 
208
    vertex._face = self
 
209
    self._vertices.insert(index, vertex)
 
210
    
 
211
  def append(self, _Vertex vertex not None):
 
212
    """Face.append(vertex)
 
213
 
 
214
Appends the given VERTEX to the Face."""
 
215
    vertex._face = self
 
216
    self._vertices.append(vertex)
 
217
    
 
218
  def add(self, _Vertex vertex not None):
 
219
    """Face.add(vertex)
 
220
 
 
221
Appends the given VERTEX to the Face."""
 
222
    self.append(vertex)
 
223
    
 
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
 
237
    
 
238
  def is_coplanar(self, float threshold = 0.005):
 
239
    """is_coplanar(threshold = 0.005) -> boolean
 
240
 
 
241
Returns true if the Face's vertices are all coplanar."""
 
242
 
 
243
    if len(self._vertices) < 4: return 1
 
244
    
 
245
    cdef _Vertex vertex
 
246
    cdef _Vector n1, n2
 
247
    self._compute_normal()
 
248
    n1 = self._normal
 
249
    n2 = Vector(self._parent)
 
250
    
 
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)
 
254
    
 
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)
 
259
      
 
260
      if n1.distance_to(n2) > threshold: return 0
 
261
    return 1
 
262
  
 
263
  def is_colored(self):
 
264
    """is_colored() -> boolean
 
265
 
 
266
Returns true if the Face is colored, i.e. at least one of its Vertices is colored."""
 
267
    cdef _Vertex vertex
 
268
    for vertex in self._vertices:
 
269
      if not vertex._diffuse is None: return 1
 
270
    return 0
 
271
  
 
272
  def is_alpha(self):
 
273
    """is_alpha() -> boolean
 
274
 
 
275
Returns true if the Face is alpha-blended."""
 
276
    return (self._material and self._material.is_alpha()) or self.has_alpha_vertex()
 
277
  
 
278
  def has_alpha_vertex(self):
 
279
    """has_alpha_vertex() -> boolean
 
280
 
 
281
Returns true if the Face has at least one alpha blended Vertex."""
 
282
    cdef _Vertex vertex
 
283
    for vertex in self._vertices:
 
284
      if (not vertex._diffuse is None) and vertex._diffuse[3] < 1.0: return 1
 
285
    return 0
 
286
  
 
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)
 
290
    
 
291
  cdef void _render(self, CoordSyst coord_syst):
 
292
    cdef _Vertex vertex
 
293
    cdef int    i
 
294
    i = len(self._vertices)
 
295
    if i == 0: return
 
296
    
 
297
    self._material._activate()
 
298
    
 
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)
 
303
      
 
304
    self._compute_normal()
 
305
    if not self._normal is None: glNormal3fv(self._normal._matrix)
 
306
    
 
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)
 
312
    
 
313
    for vertex in self._vertices: vertex._render(self._parent)
 
314
    glEnd()
 
315
    
 
316
    if self._option & FACE2_DOUBLE_SIDED:
 
317
      glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE)
 
318
      glEnable(GL_CULL_FACE)
 
319
      
 
320
    if not(self._option & FACE2_LIT): glEnable(GL_LIGHTING)
 
321
    
 
322
  def __iter__(self):
 
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
 
329
  def __len__(self):
 
330
    return len(self._vertices)
 
331
  
 
332
  def __repr__(self):
 
333
    cdef int i
 
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"
 
339
    else:        r = "<Polygon"
 
340
    if not self._material is _DEFAULT_MATERIAL: r = r + ", material %s" % self._material.filename
 
341
    return r + ">"
 
342
  
 
343
  cdef void _get_box(self, float* box, float* matrix):
 
344
    cdef _Vertex vertex
 
345
    cdef float   coord[3]
 
346
    
 
347
    for vertex in self._vertices:
 
348
      point_by_matrix_copy(coord, vertex._matrix, matrix)
 
349
      
 
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]
 
356
      
 
357
  cdef void _raypick(self, RaypickData data, CoordSyst parent):
 
358
    cdef float* p, r, root_r
 
359
    cdef float  normal[3]
 
360
    cdef int    nb_vertices, i, option
 
361
    nb_vertices = len(self._vertices)
 
362
    if nb_vertices < 3: return
 
363
    option = data.option
 
364
    if self.double_sided == 1: option = option & ~RAYPICK_CULL_FACE
 
365
    
 
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)
 
369
    
 
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.")
 
375
    
 
376
    if i != 0:
 
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:
 
380
          data.result           = r
 
381
          data.root_result      = root_r
 
382
          data.result_coordsyst = self
 
383
          memcpy(data.normal, normal, 3 * sizeof(float))
 
384
        elif i == RAYPICK_INDIRECT:
 
385
          data.result           = r
 
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))
 
392
          
 
393
    free(p)
 
394
    
 
395
  cdef int _raypick_b(self, RaypickData data, CoordSyst parent):
 
396
    cdef float* p, r
 
397
    cdef float  normal[3]
 
398
    cdef int    nb_vertices, i, option
 
399
    
 
400
    nb_vertices = len(self._vertices)
 
401
    if nb_vertices < 3: return 0
 
402
    option = data.option
 
403
    if (option & RAYPICK_CULL_FACE) and (self.double_sided == 1): option = option & ~RAYPICK_CULL_FACE
 
404
    
 
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)
 
408
    
 
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.")
 
414
    
 
415
    free(p)
 
416
    return i
 
417
  
 
418
  cdef void _collect_raypickables(self, Chunk* items, float* rsphere, float* sphere):
 
419
    if self._option & NON_SOLID: return
 
420
    
 
421
    # XXX not really implemented -- no selection
 
422
    chunk_add_ptr(items, <void*> self)
 
423
    
 
424
    
 
425
 
 
426
 
 
427