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