Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

pandatool/src/lwoegg/cLwoSurface.cxx

Go to the documentation of this file.
00001 // Filename: cLwoSurface.cxx
00002 // Created by:  drose (25Apr01)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #include "cLwoSurface.h"
00020 #include "cLwoSurfaceBlock.h"
00021 #include "cLwoClip.h"
00022 #include "lwoToEggConverter.h"
00023 
00024 #include "lwoSurfaceColor.h"
00025 #include "lwoSurfaceParameter.h"
00026 #include "lwoSurfaceSmoothingAngle.h"
00027 #include "lwoSurfaceSidedness.h"
00028 #include "lwoSurfaceBlock.h"
00029 #include "eggPrimitive.h"
00030 #include "string_utils.h"
00031 #include "mathNumbers.h"
00032 #include "dcast.h"
00033 
00034 
00035 ////////////////////////////////////////////////////////////////////
00036 //     Function: CLwoSurface::Constructor
00037 //       Access: Public
00038 //  Description:
00039 ////////////////////////////////////////////////////////////////////
00040 CLwoSurface::
00041 CLwoSurface(LwoToEggConverter *converter, const LwoSurface *surface) :
00042   _converter(converter),
00043   _surface(surface)
00044 {
00045   _flags = 0;
00046   _rgb.set(1.0, 1.0, 1.0);
00047   _checked_material = false;
00048   _checked_texture = false;
00049   _map_uvs = NULL;
00050   _block = (CLwoSurfaceBlock *)NULL;
00051 
00052   // Walk through the chunk list, looking for some basic properties.
00053   int num_chunks = _surface->get_num_chunks();
00054   for (int i = 0; i < num_chunks; i++) {
00055     const IffChunk *chunk = _surface->get_chunk(i);
00056 
00057     if (chunk->is_of_type(LwoSurfaceColor::get_class_type())) {
00058       const LwoSurfaceColor *color = DCAST(LwoSurfaceColor, chunk);
00059       _flags |= F_rgb;
00060       _rgb = color->_color;
00061 
00062     } else if (chunk->is_of_type(LwoSurfaceParameter::get_class_type())) {
00063       const LwoSurfaceParameter *param = DCAST(LwoSurfaceParameter, chunk);
00064       IffId type = param->get_id();
00065 
00066       if (type == IffId("DIFF")) {
00067         _flags |= F_diffuse;
00068         _diffuse = param->_value;
00069 
00070       } else if (type == IffId("LUMI")) {
00071         _flags |= F_luminosity;
00072         _luminosity = param->_value;
00073 
00074       } else if (type == IffId("SPEC")) {
00075         _flags |= F_specular;
00076         _specular = param->_value;
00077 
00078       } else if (type == IffId("REFL")) {
00079         _flags |= F_reflection;
00080         _reflection = param->_value;
00081 
00082       } else if (type == IffId("TRAN")) {
00083         _flags |= F_transparency;
00084         _transparency = param->_value;
00085 
00086       } else if (type == IffId("GLOS")) {
00087         _flags |= F_gloss;
00088         _gloss = param->_value;
00089 
00090       } else if (type == IffId("TRNL")) {
00091         _flags |= F_translucency;
00092         _translucency = param->_value;
00093       }
00094 
00095     } else if (chunk->is_of_type(LwoSurfaceSmoothingAngle::get_class_type())) {
00096       const LwoSurfaceSmoothingAngle *sa = DCAST(LwoSurfaceSmoothingAngle, chunk);
00097       _flags |= F_smooth_angle;
00098       _smooth_angle = sa->_angle;
00099 
00100     } else if (chunk->is_of_type(LwoSurfaceSidedness::get_class_type())) {
00101       const LwoSurfaceSidedness *sn = DCAST(LwoSurfaceSidedness, chunk);
00102       _flags |= F_backface;
00103       _backface = (sn->_sidedness == LwoSurfaceSidedness::S_front_and_back);
00104 
00105     } else if (chunk->is_of_type(LwoSurfaceBlock::get_class_type())) {
00106       const LwoSurfaceBlock *lwo_block = DCAST(LwoSurfaceBlock, chunk);
00107       // One of possibly several blocks in the texture that define
00108       // additional fancy rendering properties.
00109 
00110       CLwoSurfaceBlock *block = new CLwoSurfaceBlock(_converter, lwo_block);
00111 
00112       // We only consider enabled "IMAP" type blocks that affect "COLR".
00113       if (block->_block_type == IffId("IMAP") &&
00114           block->_channel_id == IffId("COLR") &&
00115           block->_enabled) {
00116         // Now save the block with the lowest ordinal.
00117         if (_block == (CLwoSurfaceBlock *)NULL) {
00118           _block = block;
00119 
00120         } else if (block->_ordinal < _block->_ordinal) {
00121           delete _block;
00122           _block = block;
00123 
00124         } else {
00125           delete block;
00126         }
00127 
00128       } else {
00129         delete block;
00130       }
00131     }
00132   }
00133 
00134   // Now get the four-component color, based on combining the RGB and
00135   // the transparency.
00136   _color.set(1.0, 1.0, 1.0, 1.0);
00137 
00138   if ((_flags & F_rgb) != 0) {
00139     _color[0] = _rgb[0];
00140     _color[1] = _rgb[1];
00141     _color[2] = _rgb[2];
00142   }
00143 
00144   if ((_flags & F_transparency) != 0) {
00145     _color[3] = 1.0 - _transparency;
00146   }
00147 
00148   _diffuse_color = _color;
00149 }
00150 
00151 ////////////////////////////////////////////////////////////////////
00152 //     Function: CLwoSurface::Destructor
00153 //       Access: Public
00154 //  Description:
00155 ////////////////////////////////////////////////////////////////////
00156 CLwoSurface::
00157 ~CLwoSurface() {
00158   if (_block != (CLwoSurfaceBlock *)NULL) {
00159     delete _block;
00160   }
00161 }
00162 
00163 ////////////////////////////////////////////////////////////////////
00164 //     Function: CLwoSurface::apply_properties
00165 //       Access: Public
00166 //  Description: Applies the color, texture, etc. described by the
00167 //               surface to the indicated egg primitive.
00168 //
00169 //               If the surface defines a smoothing angle,
00170 //               smooth_angle may be updated to reflect it if the
00171 //               angle is greater than that specified.
00172 ////////////////////////////////////////////////////////////////////
00173 void CLwoSurface::
00174 apply_properties(EggPrimitive *egg_prim, vector_PT_EggVertex &egg_vertices,
00175                  float &smooth_angle) {
00176   if (!_surface->_source.empty()) {
00177     // This surface is derived from another surface; apply that one
00178     // first.
00179     CLwoSurface *parent = _converter->get_surface(_surface->_source);
00180     if (parent != (CLwoSurface *)NULL && parent != this) {
00181       parent->apply_properties(egg_prim, egg_vertices, smooth_angle);
00182     }
00183   }
00184 
00185   bool has_texture = check_texture();
00186   bool has_material = check_material();
00187 
00188   egg_prim->set_color(_diffuse_color);
00189 
00190   if (has_material) {
00191     egg_prim->set_material(_egg_material);
00192   }
00193 
00194   if (has_texture) {
00195     egg_prim->set_texture(_egg_texture);
00196 
00197     // Assign UV's to the vertices.
00198     generate_uvs(egg_vertices);
00199   }
00200 
00201   if ((_flags & F_backface) != 0) {
00202     egg_prim->set_bface_flag(_backface);
00203   }
00204 
00205   if ((_flags & F_smooth_angle) != 0) {
00206     smooth_angle = max(smooth_angle, _smooth_angle);
00207   }
00208 }
00209 
00210 ////////////////////////////////////////////////////////////////////
00211 //     Function: CLwoSurface::check_texture
00212 //       Access: Public
00213 //  Description: Checks whether the surface demands a texture or not.
00214 //               Returns true if so, false otherwise.
00215 //
00216 //               If the surface demands a texture, this also sets up
00217 //               _egg_texture and _compute_uvs as appropriate for the
00218 //               texture.
00219 ////////////////////////////////////////////////////////////////////
00220 bool CLwoSurface::
00221 check_texture() {
00222   if (_checked_texture) {
00223     return (_egg_texture != (EggTexture *)NULL);
00224   }
00225   _checked_texture = true;
00226   _egg_texture = (EggTexture *)NULL;
00227   _map_uvs = NULL;
00228 
00229   if (_block == (CLwoSurfaceBlock *)NULL) {
00230     // No texture.  Not even a shader block.
00231     return false;
00232   }
00233 
00234   int clip_index = _block->_clip_index;
00235   if (clip_index < 0) {
00236     // No image file associated with the texture.
00237     return false;
00238   }
00239 
00240   CLwoClip *clip = _converter->get_clip(clip_index);
00241   if (clip == (CLwoClip *)NULL) {
00242     nout << "No clip image with index " << clip_index << "\n";
00243     return false;
00244   }
00245 
00246   if (!clip->is_still_image()) {
00247     // Can't do anything with an animated image right now.
00248     return false;
00249   }
00250 
00251   Filename pathname = _converter->convert_texture_path(clip->_filename);
00252 
00253   _egg_texture = new EggTexture("clip" + format_string(clip_index), pathname);
00254 
00255   // Do we need to generate UV's?
00256   switch (_block->_projection_mode) {
00257   case LwoSurfaceBlockProjection::M_planar:
00258     _map_uvs = &CLwoSurface::map_planar;
00259     break;
00260 
00261   case LwoSurfaceBlockProjection::M_cylindrical:
00262     _map_uvs = &CLwoSurface::map_cylindrical;
00263     break;
00264 
00265   case LwoSurfaceBlockProjection::M_spherical:
00266     _map_uvs = &CLwoSurface::map_spherical;
00267     break;
00268 
00269   case LwoSurfaceBlockProjection::M_cubic:
00270     _map_uvs = &CLwoSurface::map_cubic;
00271     break;
00272 
00273   case LwoSurfaceBlockProjection::M_front:
00274     // Cannot generate "front" UV's, since this depends on a camera.
00275     // Is it supposed to be updated in real time, like a projected
00276     // texture?
00277     break;
00278 
00279   case LwoSurfaceBlockProjection::M_uv:
00280     // "uv" projection means to use the existing UV's already defined
00281     // for the vertex.  This case was already handled in the code that
00282     // created the EggVertex pointers.
00283     break;
00284   };
00285 
00286   // Texture overrides the primitive's natural color.
00287   _color[0] = 1.0;
00288   _color[1] = 1.0;
00289   _color[2] = 1.0;
00290 
00291   return true;
00292 }
00293 
00294 ////////////////////////////////////////////////////////////////////
00295 //     Function: CLwoSurface::check_material
00296 //       Access: Public
00297 //  Description: Checks whether the surface demands a material or not.
00298 //               Returns true if so, false otherwise.
00299 ////////////////////////////////////////////////////////////////////
00300 bool CLwoSurface::
00301 check_material() {
00302   if (_checked_material) {
00303     return (_egg_material != (EggMaterial *)NULL);
00304   }
00305   _checked_material = true;
00306   _egg_material = (EggMaterial *)NULL;
00307 
00308   if (!_converter->_make_materials) {
00309     // If we aren't making materials, then don't make a material.
00310     return false;
00311   }
00312 
00313   _egg_material = new EggMaterial(get_name());
00314 
00315   if ((_flags & F_diffuse) != 0) {
00316     _diffuse_color.set(_color[0] * _diffuse,
00317                        _color[1] * _diffuse,
00318                        _color[2] * _diffuse,
00319                        _color[3]);
00320     // We want to avoid setting the diffuse color on the material.
00321     // We're already setting the color explicitly on the object, so
00322     // there's no need to also set a diffuse color on the material,
00323     // and doing so prevents nice features like set_color() and
00324     // set_color_scale() from working in Panda.
00325 
00326     //_egg_material->set_diff(_diffuse_color);
00327   }
00328 
00329   if ((_flags & F_luminosity) != 0) {
00330     Colorf luminosity(_color[0] * _luminosity,
00331                       _color[1] * _luminosity,
00332                       _color[2] * _luminosity,
00333                       1.0);
00334     _egg_material->set_emit(luminosity);
00335   }
00336 
00337   if ((_flags & F_specular) != 0) {
00338     Colorf specular(_color[0] * _specular,
00339                     _color[1] * _specular,
00340                     _color[2] * _specular,
00341                     1.0);
00342     _egg_material->set_spec(specular);
00343   }
00344 
00345   if ((_flags & F_gloss) != 0) {
00346     _egg_material->set_shininess(_gloss * 128.0);
00347   }
00348 
00349   return true;
00350 }
00351 
00352 
00353 ////////////////////////////////////////////////////////////////////
00354 //     Function: CLwoSurface::generate_uvs
00355 //       Access: Private
00356 //  Description: Computes all the UV's for the polygon's vertices,
00357 //               according to the _projection_mode defined in the
00358 //               block.
00359 ////////////////////////////////////////////////////////////////////
00360 void CLwoSurface::
00361 generate_uvs(vector_PT_EggVertex &egg_vertices) {
00362   if (_map_uvs == NULL) {
00363     return;
00364   }
00365 
00366   // To do this properly near seams and singularities (for instance,
00367   // the back seam and the poles of the spherical map), we will need
00368   // to know the polygon's centroid.
00369   LPoint3d centroid(0.0, 0.0, 0.0);
00370 
00371   vector_PT_EggVertex::const_iterator vi;
00372   for (vi = egg_vertices.begin(); vi != egg_vertices.end(); ++vi) {
00373     EggVertex *egg_vertex = (*vi);
00374     centroid += egg_vertex->get_pos3();
00375   }
00376 
00377   centroid /= (double)egg_vertices.size();
00378   centroid = centroid * _block->_inv_transform;
00379 
00380   // Now go back through and actually compute the UV's.
00381   for (vi = egg_vertices.begin(); vi != egg_vertices.end(); ++vi) {
00382     EggVertex *egg_vertex = (*vi);
00383     LPoint3d pos = egg_vertex->get_pos3() * _block->_inv_transform;
00384     LPoint2d uv = (this->*_map_uvs)(pos, centroid);
00385     egg_vertex->set_uv(uv);
00386   }
00387 }
00388 
00389 ////////////////////////////////////////////////////////////////////
00390 //     Function: CLwoSurface::map_planar
00391 //       Access: Private
00392 //  Description: Computes a UV based on the given point in space,
00393 //               using a planar projection.
00394 ////////////////////////////////////////////////////////////////////
00395 LPoint2d CLwoSurface::
00396 map_planar(const LPoint3d &pos, const LPoint3d &) const {
00397   // A planar projection is about as easy as can be.  We ignore the Y
00398   // axis, and project the point into the XZ plane.  Done.
00399   double u = (pos[0] + 0.5);
00400   double v = (pos[2] + 0.5);
00401 
00402   return LPoint2d(u, v);
00403 }
00404 
00405 ////////////////////////////////////////////////////////////////////
00406 //     Function: CLwoSurface::map_spherical
00407 //       Access: Private
00408 //  Description: Computes a UV based on the given point in space,
00409 //               using a spherical projection.
00410 ////////////////////////////////////////////////////////////////////
00411 LPoint2d CLwoSurface::
00412 map_spherical(const LPoint3d &pos, const LPoint3d &centroid) const {
00413   // To compute the x position on the frame, we only need to consider
00414   // the angle of the vector about the Y axis.  Project the vector
00415   // into the XZ plane to do this.
00416 
00417   LVector2d xz_orig(pos[0], pos[2]);
00418   LVector2d xz = xz_orig;
00419   double u_offset = 0.0;
00420 
00421   if (xz == LVector2d::zero()) {
00422     // If we have a point on either pole, we've got problems.  This
00423     // point maps to the entire bottom edge of the image, so which U
00424     // value should we choose?  It does make a difference, especially
00425     // if we have a number of polygons around the south pole that all
00426     // share the common vertex.
00427 
00428     // We choose the U value based on the polygon's centroid.
00429     xz.set(centroid[0], centroid[2]);
00430 
00431   } else if (xz[1] >= 0.0 && ((xz[0] < 0.0) != (centroid[0] < 0.))) {
00432     // Now, if our polygon crosses the seam along the back of the
00433     // sphere--that is, the point is on the back of the sphere (xz[1]
00434     // >= 0.0) and not on the same side of the XZ plane as the
00435     // centroid, we've got problems too.  We need to add an offset to
00436     // the computed U value, either 1 or -1, to keep all the vertices
00437     // of the polygon on the same side of the seam.
00438 
00439     u_offset = (xz[0] < 0.0) ? 1.0 : -1.0;
00440   }
00441 
00442   // The U value is based on the longitude: the angle about the Y
00443   // axis.
00444   double u =
00445     (atan2(xz[0], -xz[1]) / (2.0 * MathNumbers::pi) + 0.5 + u_offset) * _block->_w_repeat;
00446 
00447   // Now rotate the vector into the YZ plane, and the V value is based
00448   // on the latitude: the angle about the X axis.
00449   LVector2d yz(pos[1], xz_orig.length());
00450   double v =
00451     (atan2(yz[0], yz[1]) / MathNumbers::pi + 0.5) * _block->_h_repeat;
00452 
00453   return LPoint2d(u, v);
00454 }
00455 
00456 ////////////////////////////////////////////////////////////////////
00457 //     Function: CLwoSurface::map_cylindrical
00458 //       Access: Private
00459 //  Description: Computes a UV based on the given point in space,
00460 //               using a cylindrical projection.
00461 ////////////////////////////////////////////////////////////////////
00462 LPoint2d CLwoSurface::
00463 map_cylindrical(const LPoint3d &pos, const LPoint3d &centroid) const {
00464   // This is almost identical to the spherical projection, except for
00465   // the computation of V.
00466 
00467   LVector2d xz(pos[0], pos[2]);
00468   double u_offset = 0.0;
00469 
00470   if (xz == LVector2d::zero()) {
00471     // Although a cylindrical mapping does not really have a
00472     // singularity at the pole, it's still possible to put a point
00473     // there, and we'd like to do the right thing with the polygon
00474     // that shares that point.  So the singularity logic remains.
00475     xz.set(centroid[0], centroid[2]);
00476 
00477   } else if (xz[1] >= 0.0 && ((xz[0] < 0.0) != (centroid[0] < 0.))) {
00478     // And cylinders do still have a seam at the back.
00479     u_offset = (xz[0] < 0.0) ? 1.0 : -1.0;
00480   }
00481 
00482   double u =
00483     (atan2(xz[0], -xz[1]) / (2.0 * MathNumbers::pi) + 0.5 + u_offset) * _block->_w_repeat;
00484 
00485   // For a cylindrical mapping, the V value comes almost directly from
00486   // Y.  Easy.
00487   double v = (pos[1] + 0.5);
00488 
00489   return LPoint2d(u, v);
00490 }
00491 
00492 ////////////////////////////////////////////////////////////////////
00493 //     Function: CLwoSurface::map_cubic
00494 //       Access: Private
00495 //  Description: Computes a UV based on the given point in space,
00496 //               using a cubic projection.
00497 ////////////////////////////////////////////////////////////////////
00498 LPoint2d CLwoSurface::
00499 map_cubic(const LPoint3d &pos, const LPoint3d &centroid) const {
00500   // A cubic projection is a planar projection, but we eliminate the
00501   // dominant axis (based on the polygon's centroid) instead of
00502   // arbitrarily eliminating Y.
00503 
00504   double x = fabs(centroid[0]);
00505   double y = fabs(centroid[1]);
00506   double z = fabs(centroid[2]);
00507 
00508   double u, v;
00509 
00510   if (x > y) {
00511     if (x > z) {
00512       // X is dominant.
00513       u = (pos[2] + 0.5);
00514       v = (pos[1] + 0.5);
00515     } else {
00516       // Z is dominant.
00517       u = (pos[0] + 0.5);
00518       v = (pos[1] + 0.5);
00519     }
00520   } else {
00521     if (y > z) {
00522       // Y is dominant.
00523       u = (pos[0] + 0.5);
00524       v = (pos[2] + 0.5);
00525     } else {
00526       // Z is dominant.
00527       u = (pos[0] + 0.5);
00528       v = (pos[1] + 0.5);
00529     }
00530   }
00531 
00532   return LPoint2d(u, v);
00533 }

Generated on Fri May 2 03:21:10 2003 for Panda-Tool by doxygen1.3