Panda3D
cLwoSurface.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file cLwoSurface.cxx
10  * @author drose
11  * @date 2001-04-25
12  */
13 
14 #include "cLwoSurface.h"
15 #include "cLwoSurfaceBlock.h"
16 #include "cLwoClip.h"
17 #include "lwoToEggConverter.h"
18 
19 #include "lwoSurfaceColor.h"
20 #include "lwoSurfaceParameter.h"
22 #include "lwoSurfaceSidedness.h"
23 #include "lwoSurfaceBlock.h"
24 #include "eggPrimitive.h"
25 #include "string_utils.h"
26 #include "mathNumbers.h"
27 #include "dcast.h"
28 
29 
30 /**
31  *
32  */
33 CLwoSurface::
34 CLwoSurface(LwoToEggConverter *converter, const LwoSurface *surface) :
35  _converter(converter),
36  _surface(surface)
37 {
38  _flags = 0;
39  _rgb.set(1.0, 1.0, 1.0);
40  _checked_material = false;
41  _checked_texture = false;
42  _map_uvs = nullptr;
43  _block = nullptr;
44 
45  // Walk through the chunk list, looking for some basic properties.
46  int num_chunks = _surface->get_num_chunks();
47  for (int i = 0; i < num_chunks; i++) {
48  const IffChunk *chunk = _surface->get_chunk(i);
49 
50  if (chunk->is_of_type(LwoSurfaceColor::get_class_type())) {
51  const LwoSurfaceColor *color = DCAST(LwoSurfaceColor, chunk);
52  _flags |= F_rgb;
53  _rgb = color->_color;
54 
55  } else if (chunk->is_of_type(LwoSurfaceParameter::get_class_type())) {
56  const LwoSurfaceParameter *param = DCAST(LwoSurfaceParameter, chunk);
57  IffId type = param->get_id();
58 
59  if (type == IffId("DIFF")) {
60  _flags |= F_diffuse;
61  _diffuse = param->_value;
62 
63  } else if (type == IffId("LUMI")) {
64  _flags |= F_luminosity;
65  _luminosity = param->_value;
66 
67  } else if (type == IffId("SPEC")) {
68  _flags |= F_specular;
69  _specular = param->_value;
70 
71  } else if (type == IffId("REFL")) {
72  _flags |= F_reflection;
73  _reflection = param->_value;
74 
75  } else if (type == IffId("TRAN")) {
76  _flags |= F_transparency;
77  _transparency = param->_value;
78 
79  } else if (type == IffId("GLOS")) {
80  _flags |= F_gloss;
81  _gloss = param->_value;
82 
83  } else if (type == IffId("TRNL")) {
84  _flags |= F_translucency;
85  _translucency = param->_value;
86  }
87 
88  } else if (chunk->is_of_type(LwoSurfaceSmoothingAngle::get_class_type())) {
89  const LwoSurfaceSmoothingAngle *sa = DCAST(LwoSurfaceSmoothingAngle, chunk);
90  _flags |= F_smooth_angle;
91  _smooth_angle = sa->_angle;
92 
93  } else if (chunk->is_of_type(LwoSurfaceSidedness::get_class_type())) {
94  const LwoSurfaceSidedness *sn = DCAST(LwoSurfaceSidedness, chunk);
95  _flags |= F_backface;
96  _backface = (sn->_sidedness == LwoSurfaceSidedness::S_front_and_back);
97 
98  } else if (chunk->is_of_type(LwoSurfaceBlock::get_class_type())) {
99  const LwoSurfaceBlock *lwo_block = DCAST(LwoSurfaceBlock, chunk);
100  // One of possibly several blocks in the texture that define additional
101  // fancy rendering properties.
102 
103  CLwoSurfaceBlock *block = new CLwoSurfaceBlock(_converter, lwo_block);
104 
105  // We only consider enabled "IMAP" type blocks that affect "COLR".
106  if (block->_block_type == IffId("IMAP") &&
107  block->_channel_id == IffId("COLR") &&
108  block->_enabled) {
109  // Now save the block with the lowest ordinal.
110  if (_block == nullptr) {
111  _block = block;
112 
113  } else if (block->_ordinal < _block->_ordinal) {
114  delete _block;
115  _block = block;
116 
117  } else {
118  delete block;
119  }
120 
121  } else {
122  delete block;
123  }
124  }
125  }
126 
127  // Now get the four-component color, based on combining the RGB and the
128  // transparency.
129  _color.set(1.0, 1.0, 1.0, 1.0);
130 
131  if ((_flags & F_rgb) != 0) {
132  _color[0] = _rgb[0];
133  _color[1] = _rgb[1];
134  _color[2] = _rgb[2];
135  }
136 
137  if ((_flags & F_transparency) != 0) {
138  _color[3] = 1.0 - _transparency;
139  }
140 
141  _diffuse_color = _color;
142 }
143 
144 /**
145  *
146  */
147 CLwoSurface::
148 ~CLwoSurface() {
149  if (_block != nullptr) {
150  delete _block;
151  }
152 }
153 
154 /**
155  * Applies the color, texture, etc. described by the surface to the indicated
156  * egg primitive.
157  *
158  * If the surface defines a smoothing angle, smooth_angle may be updated to
159  * reflect it if the angle is greater than that specified.
160  */
162 apply_properties(EggPrimitive *egg_prim, vector_PT_EggVertex &egg_vertices,
163  PN_stdfloat &smooth_angle) {
164  if (!_surface->_source.empty()) {
165  // This surface is derived from another surface; apply that one first.
166  CLwoSurface *parent = _converter->get_surface(_surface->_source);
167  if (parent != nullptr && parent != this) {
168  parent->apply_properties(egg_prim, egg_vertices, smooth_angle);
169  }
170  }
171 
172  bool has_texture = check_texture();
173  bool has_material = check_material();
174 
175  egg_prim->set_color(_diffuse_color);
176 
177  if (has_material) {
178  egg_prim->set_material(_egg_material);
179  }
180 
181  if (has_texture) {
182  egg_prim->set_texture(_egg_texture);
183 
184  // Assign UV's to the vertices.
185  generate_uvs(egg_vertices);
186  }
187 
188  if ((_flags & F_backface) != 0) {
189  egg_prim->set_bface_flag(_backface);
190  }
191 
192  if ((_flags & F_smooth_angle) != 0) {
193  smooth_angle = std::max(smooth_angle, _smooth_angle);
194  }
195 }
196 
197 /**
198  * Checks whether the surface demands a texture or not. Returns true if so,
199  * false otherwise.
200  *
201  * If the surface demands a texture, this also sets up _egg_texture and
202  * _compute_uvs as appropriate for the texture.
203  */
205 check_texture() {
206  if (_checked_texture) {
207  return (_egg_texture != nullptr);
208  }
209  _checked_texture = true;
210  _egg_texture = nullptr;
211  _map_uvs = nullptr;
212 
213  if (_block == nullptr) {
214  // No texture. Not even a shader block.
215  return false;
216  }
217 
218  int clip_index = _block->_clip_index;
219  if (clip_index < 0) {
220  // No image file associated with the texture.
221  return false;
222  }
223 
224  CLwoClip *clip = _converter->get_clip(clip_index);
225  if (clip == nullptr) {
226  nout << "No clip image with index " << clip_index << "\n";
227  return false;
228  }
229 
230  if (!clip->is_still_image()) {
231  // Can't do anything with an animated image right now.
232  return false;
233  }
234 
235  Filename pathname = _converter->convert_model_path(clip->_filename);
236 
237  _egg_texture = new EggTexture("clip" + format_string(clip_index), pathname);
238 
239  // Do we need to generate UV's?
240  switch (_block->_projection_mode) {
241  case LwoSurfaceBlockProjection::M_planar:
242  _map_uvs = &CLwoSurface::map_planar;
243  break;
244 
245  case LwoSurfaceBlockProjection::M_cylindrical:
246  _map_uvs = &CLwoSurface::map_cylindrical;
247  break;
248 
249  case LwoSurfaceBlockProjection::M_spherical:
250  _map_uvs = &CLwoSurface::map_spherical;
251  break;
252 
253  case LwoSurfaceBlockProjection::M_cubic:
254  _map_uvs = &CLwoSurface::map_cubic;
255  break;
256 
257  case LwoSurfaceBlockProjection::M_front:
258  // Cannot generate "front" UV's, since this depends on a camera. Is it
259  // supposed to be updated in real time, like a projected texture?
260  break;
261 
262  case LwoSurfaceBlockProjection::M_uv:
263  // "uv" projection means to use the existing UV's already defined for the
264  // vertex. This case was already handled in the code that created the
265  // EggVertex pointers.
266  break;
267  };
268 
269  // Texture overrides the primitive's natural color.
270  _color[0] = 1.0;
271  _color[1] = 1.0;
272  _color[2] = 1.0;
273 
274  return true;
275 }
276 
277 /**
278  * Checks whether the surface demands a material or not. Returns true if so,
279  * false otherwise.
280  */
282 check_material() {
283  if (_checked_material) {
284  return (_egg_material != nullptr);
285  }
286  _checked_material = true;
287  _egg_material = nullptr;
288 
289  if (!_converter->_make_materials) {
290  // If we aren't making materials, then don't make a material.
291  return false;
292  }
293 
294  _egg_material = new EggMaterial(get_name());
295 
296  if ((_flags & F_diffuse) != 0) {
297  _diffuse_color.set(_color[0] * _diffuse,
298  _color[1] * _diffuse,
299  _color[2] * _diffuse,
300  _color[3]);
301  // We want to avoid setting the diffuse color on the material. We're
302  // already setting the color explicitly on the object, so there's no need
303  // to also set a diffuse color on the material, and doing so prevents nice
304  // features like set_color() and set_color_scale() from working in Panda.
305 
306  // _egg_material->set_diff(_diffuse_color);
307  }
308 
309  if ((_flags & F_luminosity) != 0) {
310  LColor luminosity(_color[0] * _luminosity,
311  _color[1] * _luminosity,
312  _color[2] * _luminosity,
313  1.0);
314  _egg_material->set_emit(luminosity);
315  }
316 
317  if ((_flags & F_specular) != 0) {
318  LColor specular(_color[0] * _specular,
319  _color[1] * _specular,
320  _color[2] * _specular,
321  1.0);
322  _egg_material->set_spec(specular);
323  }
324 
325  if ((_flags & F_gloss) != 0) {
326  _egg_material->set_shininess(_gloss * 128.0);
327  }
328 
329  return true;
330 }
331 
332 
333 /**
334  * Computes all the UV's for the polygon's vertices, according to the
335  * _projection_mode defined in the block.
336  */
337 void CLwoSurface::
338 generate_uvs(vector_PT_EggVertex &egg_vertices) {
339  if (_map_uvs == nullptr) {
340  return;
341  }
342 
343  // To do this properly near seams and singularities (for instance, the back
344  // seam and the poles of the spherical map), we will need to know the
345  // polygon's centroid.
346  LPoint3d centroid(0.0, 0.0, 0.0);
347 
348  vector_PT_EggVertex::const_iterator vi;
349  for (vi = egg_vertices.begin(); vi != egg_vertices.end(); ++vi) {
350  EggVertex *egg_vertex = (*vi);
351  centroid += egg_vertex->get_pos3();
352  }
353 
354  centroid /= (double)egg_vertices.size();
355  centroid = centroid * _block->_inv_transform;
356 
357  // Now go back through and actually compute the UV's.
358  for (vi = egg_vertices.begin(); vi != egg_vertices.end(); ++vi) {
359  EggVertex *egg_vertex = (*vi);
360  LPoint3d pos = egg_vertex->get_pos3() * _block->_inv_transform;
361  LPoint2d uv = (this->*_map_uvs)(pos, centroid);
362  egg_vertex->set_uv(uv);
363  }
364 }
365 
366 /**
367  * Computes a UV based on the given point in space, using a planar projection.
368  */
369 LPoint2d CLwoSurface::
370 map_planar(const LPoint3d &pos, const LPoint3d &) const {
371  // A planar projection is about as easy as can be. We ignore the Y axis,
372  // and project the point into the XZ plane. Done.
373  double u = (pos[0] + 0.5);
374  double v = (pos[2] + 0.5);
375 
376  return LPoint2d(u, v);
377 }
378 
379 /**
380  * Computes a UV based on the given point in space, using a spherical
381  * projection.
382  */
383 LPoint2d CLwoSurface::
384 map_spherical(const LPoint3d &pos, const LPoint3d &centroid) const {
385  // To compute the x position on the frame, we only need to consider the
386  // angle of the vector about the Y axis. Project the vector into the XZ
387  // plane to do this.
388 
389  LVector2d xz_orig(pos[0], pos[2]);
390  LVector2d xz = xz_orig;
391  double u_offset = 0.0;
392 
393  if (xz == LVector2d::zero()) {
394  // If we have a point on either pole, we've got problems. This point maps
395  // to the entire bottom edge of the image, so which U value should we
396  // choose? It does make a difference, especially if we have a number of
397  // polygons around the south pole that all share the common vertex.
398 
399  // We choose the U value based on the polygon's centroid.
400  xz.set(centroid[0], centroid[2]);
401 
402  } else if (xz[1] >= 0.0 && ((xz[0] < 0.0) != (centroid[0] < 0.))) {
403  // Now, if our polygon crosses the seam along the back of the sphere--that
404  // is, the point is on the back of the sphere (xz[1] >= 0.0) and not on
405  // the same side of the XZ plane as the centroid, we've got problems too.
406  // We need to add an offset to the computed U value, either 1 or -1, to
407  // keep all the vertices of the polygon on the same side of the seam.
408 
409  u_offset = (xz[0] < 0.0) ? 1.0 : -1.0;
410  }
411 
412  // The U value is based on the longitude: the angle about the Y axis.
413  double u =
414  (atan2(xz[0], -xz[1]) / (2.0 * MathNumbers::pi) + 0.5 + u_offset) * _block->_w_repeat;
415 
416  // Now rotate the vector into the YZ plane, and the V value is based on the
417  // latitude: the angle about the X axis.
418  LVector2d yz(pos[1], xz_orig.length());
419  double v =
420  (atan2(yz[0], yz[1]) / MathNumbers::pi + 0.5) * _block->_h_repeat;
421 
422  return LPoint2d(u, v);
423 }
424 
425 /**
426  * Computes a UV based on the given point in space, using a cylindrical
427  * projection.
428  */
429 LPoint2d CLwoSurface::
430 map_cylindrical(const LPoint3d &pos, const LPoint3d &centroid) const {
431  // This is almost identical to the spherical projection, except for the
432  // computation of V.
433 
434  LVector2d xz(pos[0], pos[2]);
435  double u_offset = 0.0;
436 
437  if (xz == LVector2d::zero()) {
438  // Although a cylindrical mapping does not really have a singularity at
439  // the pole, it's still possible to put a point there, and we'd like to do
440  // the right thing with the polygon that shares that point. So the
441  // singularity logic remains.
442  xz.set(centroid[0], centroid[2]);
443 
444  } else if (xz[1] >= 0.0 && ((xz[0] < 0.0) != (centroid[0] < 0.))) {
445  // And cylinders do still have a seam at the back.
446  u_offset = (xz[0] < 0.0) ? 1.0 : -1.0;
447  }
448 
449  double u =
450  (atan2(xz[0], -xz[1]) / (2.0 * MathNumbers::pi) + 0.5 + u_offset) * _block->_w_repeat;
451 
452  // For a cylindrical mapping, the V value comes almost directly from Y.
453  // Easy.
454  double v = (pos[1] + 0.5);
455 
456  return LPoint2d(u, v);
457 }
458 
459 /**
460  * Computes a UV based on the given point in space, using a cubic projection.
461  */
462 LPoint2d CLwoSurface::
463 map_cubic(const LPoint3d &pos, const LPoint3d &centroid) const {
464  // A cubic projection is a planar projection, but we eliminate the dominant
465  // axis (based on the polygon's centroid) instead of arbitrarily eliminating
466  // Y.
467 
468  double x = fabs(centroid[0]);
469  double y = fabs(centroid[1]);
470  double z = fabs(centroid[2]);
471 
472  double u, v;
473 
474  if (x > y) {
475  if (x > z) {
476  // X is dominant.
477  u = (pos[2] + 0.5);
478  v = (pos[1] + 0.5);
479  } else {
480  // Z is dominant.
481  u = (pos[0] + 0.5);
482  v = (pos[1] + 0.5);
483  }
484  } else {
485  if (y > z) {
486  // Y is dominant.
487  u = (pos[0] + 0.5);
488  v = (pos[2] + 0.5);
489  } else {
490  // Z is dominant.
491  u = (pos[0] + 0.5);
492  v = (pos[1] + 0.5);
493  }
494  }
495 
496  return LPoint2d(u, v);
497 }
lwoSurfaceParameter.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
LwoSurface
Describes the shading attributes of a surface.
Definition: lwoSurface.h:25
eggPrimitive.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
string_utils.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
LwoSurfaceParameter
Records some parameter value of a surface material, as an entry within a LwoSurface chunk.
Definition: lwoSurfaceParameter.h:26
dcast.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
CLwoSurface::check_material
bool check_material()
Checks whether the surface demands a material or not.
Definition: cLwoSurface.cxx:282
LwoSurfaceBlock
A texture layer or shader, part of a LwoSurface chunk.
Definition: lwoSurfaceBlock.h:25
CLwoSurface::get_name
const std::string & get_name() const
Returns the name of the surface.
Definition: cLwoSurface.I:19
EggMaterial
Definition: eggMaterial.h:26
LwoSurfaceSidedness
Records whether polygons are frontfacing only or backfacing also.
Definition: lwoSurfaceSidedness.h:25
EggPrimitive::set_bface_flag
set_bface_flag
Sets the backfacing flag of the polygon.
Definition: eggPrimitive.h:116
EggPrimitive
A base class for any of a number of kinds of geometry primitives: polygons, point lights,...
Definition: eggPrimitive.h:49
IffChunk::get_id
IffId get_id() const
Returns the ID associated with this chunk.
Definition: iffChunk.I:25
LwoSurfaceSmoothingAngle
Indicates the maximum angle (in radians) between adjacent polygons that should be smooth-shaded.
Definition: lwoSurfaceSmoothingAngle.h:25
cLwoSurfaceBlock.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
cLwoClip.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
CLwoSurfaceBlock
This class is a wrapper around LwoSurfaceBlock and stores additional information useful during the co...
Definition: cLwoSurfaceBlock.h:34
CLwoSurface::check_texture
bool check_texture()
Checks whether the surface demands a texture or not.
Definition: cLwoSurface.cxx:205
IffId
A four-byte chunk ID appearing in an "IFF" file.
Definition: iffId.h:26
EggVertex::set_uv
void set_uv(const LTexCoordd &texCoord)
Replaces the unnamed UV coordinate pair on the vertex with the indicated value.
Definition: eggVertex.I:193
EggVertex
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal.
Definition: eggVertex.h:39
EggPrimitive::set_material
set_material
Applies the indicated material to the primitive.
Definition: eggPrimitive.h:115
CLwoClip::is_still_image
bool is_still_image() const
Returns true if this clip represents a still image, as opposed to an animated image.
Definition: cLwoClip.I:29
LwoToEggConverter::get_surface
CLwoSurface * get_surface(const std::string &name) const
Returns a pointer to the surface definition with the given name, or NULL if there is no such surface.
Definition: lwoToEggConverter.cxx:185
CLwoSurface
This class is a wrapper around LwoSurface and stores additional information useful during the convers...
Definition: cLwoSurface.h:39
EggPrimitive::set_texture
void set_texture(EggTexture *texture)
Replaces the current list of textures with the indicated texture.
Definition: eggPrimitive.I:116
lwoSurfaceSmoothingAngle.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
LwoToEggConverter::get_clip
CLwoClip * get_clip(int number) const
Returns a pointer to the clip with the given index number, or NULL if there is no such clip.
Definition: lwoToEggConverter.cxx:173
lwoToEggConverter.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
CLwoSurface::apply_properties
void apply_properties(EggPrimitive *egg_prim, vector_PT_EggVertex &egg_vertices, PN_stdfloat &smooth_angle)
Applies the color, texture, etc.
Definition: cLwoSurface.cxx:162
LwoToEggConverter
This class supervises the construction of an EggData structure from the data represented by the LwoHe...
Definition: lwoToEggConverter.h:38
EggTexture
Defines a texture map that may be applied to geometry.
Definition: eggTexture.h:30
EggVertex::get_pos3
LVertexd get_pos3() const
Valid if get_num_dimensions() returns 3 or 4.
Definition: eggVertex.I:131
cLwoSurface.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
SomethingToEggConverter::convert_model_path
Filename convert_model_path(const Filename &orig_filename)
Converts the indicated model filename to a relative or absolute or whatever filename,...
Definition: somethingToEggConverter.I:389
lwoSurfaceSidedness.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
CLwoClip
This class is a wrapper around LwoClip and stores additional information useful during the conversion...
Definition: cLwoClip.h:29
IffChunk
The basic kind of record in an EA "IFF" file, which the LightWave object file is based on.
Definition: iffChunk.h:30
LwoSurfaceColor
Records the base color of a surface, as an entry within a LwoSurface chunk.
Definition: lwoSurfaceColor.h:26
Filename
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
TypedObject::is_of_type
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
lwoSurfaceBlock.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
lwoSurfaceColor.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
mathNumbers.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.