Panda3D

eggRenderState.cxx

00001 // Filename: eggRenderState.cxx
00002 // Created by:  drose (12Mar05)
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 "eggRenderState.h"
00016 #include "eggRenderMode.h"
00017 #include "eggLine.h"
00018 #include "eggPoint.h"
00019 #include "textureAttrib.h"
00020 #include "renderAttrib.h"
00021 #include "eggTexture.h"
00022 #include "texGenAttrib.h"
00023 #include "internalName.h"
00024 #include "eggCurve.h"
00025 #include "eggSurface.h"
00026 #include "cullBinAttrib.h"
00027 #include "cullFaceAttrib.h"
00028 #include "shadeModelAttrib.h"
00029 #include "transparencyAttrib.h"
00030 #include "depthWriteAttrib.h"
00031 #include "depthTestAttrib.h"
00032 #include "depthOffsetAttrib.h"
00033 #include "texMatrixAttrib.h"
00034 #include "renderModeAttrib.h"
00035 #include "material.h"
00036 #include "materialAttrib.h"
00037 #include "materialPool.h"
00038 #include "config_gobj.h"
00039 #include "config_egg2pg.h"
00040 
00041 
00042 ////////////////////////////////////////////////////////////////////
00043 //     Function: EggRenderState::fill_state
00044 //       Access: Public
00045 //  Description: Sets up the state as appropriate for the indicated
00046 //               primitive.
00047 ////////////////////////////////////////////////////////////////////
00048 void EggRenderState::
00049 fill_state(EggPrimitive *egg_prim) {
00050   // The various EggRenderMode properties can be defined directly at
00051   // the primitive, at a group above the primitive, or an a texture
00052   // applied to the primitive.  The EggNode::determine_*() functions
00053   // can find the right pointer to the level at which this is actually
00054   // defined for a given primitive.
00055   EggRenderMode::AlphaMode am = EggRenderMode::AM_unspecified;
00056   EggRenderMode::DepthWriteMode dwm = EggRenderMode::DWM_unspecified;
00057   EggRenderMode::DepthTestMode dtm = EggRenderMode::DTM_unspecified;
00058   EggRenderMode::VisibilityMode vm = EggRenderMode::VM_unspecified;
00059   bool implicit_alpha = false;
00060   bool binary_alpha_only = true;  // true if all alpha sources are binary alpha.
00061   bool has_draw_order = false;
00062   int draw_order = 0;
00063   bool has_depth_offset = false;
00064   int depth_offset = 0;
00065   bool has_bin = false;
00066   string bin;
00067 
00068   EggRenderMode *render_mode;
00069   render_mode = egg_prim->determine_alpha_mode();
00070   if (render_mode != (EggRenderMode *)NULL) {
00071     am = render_mode->get_alpha_mode();
00072   }
00073   render_mode = egg_prim->determine_depth_write_mode();
00074   if (render_mode != (EggRenderMode *)NULL) {
00075     dwm = render_mode->get_depth_write_mode();
00076   }
00077   render_mode = egg_prim->determine_depth_test_mode();
00078   if (render_mode != (EggRenderMode *)NULL) {
00079     dtm = render_mode->get_depth_test_mode();
00080   }
00081   render_mode = egg_prim->determine_visibility_mode();
00082   if (render_mode != (EggRenderMode *)NULL) {
00083     vm = render_mode->get_visibility_mode();
00084   }
00085   render_mode = egg_prim->determine_draw_order();
00086   if (render_mode != (EggRenderMode *)NULL) {
00087     has_draw_order = true;
00088     draw_order = render_mode->get_draw_order();
00089   }
00090   render_mode = egg_prim->determine_depth_offset();
00091   if (render_mode != (EggRenderMode *)NULL) {
00092     has_depth_offset = true;
00093     depth_offset = render_mode->get_depth_offset();
00094   }
00095   render_mode = egg_prim->determine_bin();
00096   if (render_mode != (EggRenderMode *)NULL) {
00097     has_bin = true;
00098     bin = render_mode->get_bin();
00099   }
00100 
00101   //  add_attrib(TextureAttrib::make_off());
00102   int num_textures = egg_prim->get_num_textures();
00103   CPT(RenderAttrib) texture_attrib = NULL;
00104   CPT(RenderAttrib) tex_gen_attrib = NULL;
00105   CPT(RenderAttrib) tex_mat_attrib = NULL;
00106   TexMats tex_mats;
00107 
00108   for (int i = 0; i < num_textures; i++) {
00109     PT_EggTexture egg_tex = egg_prim->get_texture(i);
00110 
00111     const TextureDef &def = _loader._textures[egg_tex];
00112     if (def._texture != (const RenderAttrib *)NULL) {
00113       if (texture_attrib == (RenderAttrib *)NULL) {
00114         texture_attrib = def._texture;
00115       } else {
00116         texture_attrib = texture_attrib->compose(def._texture);
00117       }
00118 
00119       if (egg_tex->affects_polygon_alpha()) {
00120         const TextureAttrib *tex_attrib = DCAST(TextureAttrib, def._texture);
00121         Texture *tex = tex_attrib->get_texture();
00122         nassertv(tex != (Texture *)NULL);
00123 
00124         Texture::Format format = tex->get_format();
00125         if (Texture::has_alpha(format) && !Texture::has_binary_alpha(format)) {
00126           // This texture specifies a gradient alpha format.
00127           binary_alpha_only = false;
00128         }
00129 
00130         if (am == EggRenderMode::AM_unspecified) {
00131           // If neither the primitive nor the texture specified an
00132           // alpha mode, assume it should be alpha'ed if the texture
00133           // has an alpha channel (unless the texture environment type
00134           // is one that doesn't apply its alpha to the result).
00135           int num_components = tex->get_num_components();
00136           if (egg_tex->has_alpha_channel(num_components)) {
00137             implicit_alpha = true;
00138           }
00139         }
00140       }
00141 
00142       // Check for a texgen attrib.
00143       bool has_tex_gen = false;
00144       if (egg_tex->get_tex_gen() != EggTexture::TG_unspecified) {
00145         has_tex_gen = true;
00146         if (tex_gen_attrib == (const RenderAttrib *)NULL) {
00147           tex_gen_attrib = TexGenAttrib::make();
00148         }
00149         tex_gen_attrib = DCAST(TexGenAttrib, tex_gen_attrib)->
00150           add_stage(def._stage, get_tex_gen(egg_tex));
00151       }
00152 
00153       // Record the texture's associated texture matrix, so we can see
00154       // if we can safely bake it into the UV's.  (We need to get the
00155       // complete list of textures that share this same set of UV's
00156       // per each unique texture matrix.  Whew!)
00157       CPT(InternalName) uv_name;
00158       if (egg_tex->has_uv_name() && egg_tex->get_uv_name() != string("default")) {
00159         uv_name = InternalName::get_texcoord_name(egg_tex->get_uv_name());
00160       } else {
00161         uv_name = InternalName::get_texcoord();
00162       }
00163 
00164       if (has_tex_gen) {
00165         // If the texture has a texgen mode, we will always apply its
00166         // texture transform, never bake it in.  In fact, we don't
00167         // even care about its UV's in this case, since we won't be
00168         // using them.
00169         tex_mat_attrib = apply_tex_mat(tex_mat_attrib, def._stage, egg_tex);
00170 
00171       } else {
00172         // Otherwise, we need to record that there is at least one
00173         // texture on this particular UV name and with this particular
00174         // texture matrix.  If there are no other textures, or if all
00175         // of the other textures use the same texture matrix, then
00176         // tex_mats[uv_name].size() will remain 1 (which tells us we
00177         // can bake in the texture matrix to the UV's).  On the other
00178         // hand, if there is another texture on the same uv name but
00179         // with a different transform, it will increase
00180         // tex_mats[uv_name].size() to at least 2, indicating we can't
00181         // bake in the texture matrix.
00182         tex_mats[uv_name][egg_tex->get_transform3d()].push_back(&def);
00183       }
00184     }
00185   }
00186 
00187   // These parametric primitive types can't have their UV's baked in,
00188   // so if we have one of these we always need to apply the texture
00189   // matrix as a separate attribute, regardless of how many textures
00190   // share the particular UV set.
00191   bool needs_tex_mat = (egg_prim->is_of_type(EggCurve::get_class_type()) ||
00192                         egg_prim->is_of_type(EggSurface::get_class_type()));
00193 
00194   // Now that we've visited all of the textures in the above loop, we
00195   // can go back and see how many of them share the same UV name and
00196   // texture matrix.
00197   TexMats::const_iterator tmi;
00198   for (tmi = tex_mats.begin(); tmi != tex_mats.end(); ++tmi) {
00199     const InternalName *uv_name = (*tmi).first;
00200     const TexMatTransforms &tmt = (*tmi).second;
00201 
00202     if (tmt.size() == 1 && !needs_tex_mat) {
00203       // Only one unique transform sharing this set of UV's.  We can
00204       // bake in the transform!
00205       const TexMatTextures &tmtex = (*tmt.begin()).second;
00206 
00207       // The first EggTexture on the list is sufficient, since we know
00208       // they all have the same transform.
00209       nassertv(!tmtex.empty());
00210       TexMatTextures::const_iterator tmtexi = tmtex.begin();
00211       const EggTexture *egg_tex = (*tmtexi)->_egg_tex;
00212       if (egg_tex->has_transform()) {
00213         // If there's no transform, it's an identity matrix; don't
00214         // bother recording it.  Of course, it would do no harm to
00215         // record it if we felt like it.
00216         _bake_in_uvs[uv_name] = egg_tex;
00217       }
00218 
00219     } else {
00220       // Multiple transforms on this UV set, or a geometry type that
00221       // doesn't support baking in UV's.  We have to apply the
00222       // texture matrix to each stage.
00223       TexMatTransforms::const_iterator tmti;
00224       for (tmti = tmt.begin(); tmti != tmt.end(); ++tmti) {
00225         const TexMatTextures &tmtex = (*tmti).second;
00226         TexMatTextures::const_iterator tmtexi;
00227         for (tmtexi = tmtex.begin(); tmtexi != tmtex.end(); ++tmtexi) {
00228           const EggTexture *egg_tex = (*tmtexi)->_egg_tex;
00229           TextureStage *stage = (*tmtexi)->_stage;
00230           
00231           tex_mat_attrib = apply_tex_mat(tex_mat_attrib, stage, egg_tex);
00232         }
00233       }
00234     }
00235   }
00236 
00237   if (texture_attrib != (RenderAttrib *)NULL) {
00238     add_attrib(texture_attrib);
00239   }
00240 
00241   if (tex_gen_attrib != (RenderAttrib *)NULL) {
00242     add_attrib(tex_gen_attrib);
00243   }
00244 
00245   if (tex_mat_attrib != (RenderAttrib *)NULL) {
00246     add_attrib(tex_mat_attrib);
00247   }
00248 
00249   if (egg_prim->has_material()) {
00250     PT(EggMaterial) material = egg_prim->get_material();
00251     CPT(RenderAttrib) mt =
00252       get_material_attrib(material, egg_prim->get_bface_flag());
00253     add_attrib(mt);
00254 
00255     if (material->has_diff() && material->get_diff()[3] != 1.0) {
00256       implicit_alpha = true;
00257       binary_alpha_only = false;
00258     }
00259   }
00260 
00261 
00262   // Also check the color of the primitive to see if we should assume
00263   // alpha based on the alpha values specified in the egg file.
00264   if (am == EggRenderMode::AM_unspecified) {
00265     if (egg_prim->has_color()) {
00266       if (egg_prim->get_color()[3] != 1.0) {
00267         implicit_alpha = true;
00268         binary_alpha_only = false;
00269       }
00270     }
00271     EggPrimitive::const_iterator vi;
00272     for (vi = egg_prim->begin();
00273          !implicit_alpha && vi != egg_prim->end();
00274          ++vi) {
00275       if ((*vi)->has_color()) {
00276         if ((*vi)->get_color()[3] != 1.0) {
00277           implicit_alpha = true;
00278           binary_alpha_only = false;
00279         }
00280       }
00281     }
00282 
00283     if (implicit_alpha) {
00284       am = EggRenderMode::AM_on;
00285     }
00286   }
00287 
00288   switch (am) {
00289   case EggRenderMode::AM_on:
00290     // Alpha type "on" means to get the default transparency type.
00291     if (binary_alpha_only) {
00292       am = EggRenderMode::AM_binary;
00293     } else if (egg_alpha_mode != EggRenderMode::AM_unspecified) {
00294       am = egg_alpha_mode;
00295     }
00296     break;
00297 
00298   case EggRenderMode::AM_blend:
00299   case EggRenderMode::AM_ms:
00300   case EggRenderMode::AM_ms_mask:
00301   case EggRenderMode::AM_dual:
00302     if (egg_implicit_alpha_binary) {
00303       // Any of these modes gets implicitly downgraded to AM_binary, if
00304       // all of the alpha sources only contribute a binary value to
00305       // alpha.
00306       if (binary_alpha_only) {
00307         am = EggRenderMode::AM_binary;
00308       }
00309     }
00310     break;
00311 
00312   default:
00313     break;
00314   }
00315 
00316   switch (am) {
00317   case EggRenderMode::AM_on:
00318   case EggRenderMode::AM_blend:
00319     add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
00320     break;
00321 
00322   case EggRenderMode::AM_blend_no_occlude:
00323     add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
00324     add_attrib(DepthWriteAttrib::make(DepthWriteAttrib::M_off));
00325     break;
00326 
00327   case EggRenderMode::AM_ms:
00328     add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_multisample));
00329     break;
00330 
00331   case EggRenderMode::AM_ms_mask:
00332     add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_multisample_mask));
00333     break;
00334 
00335   case EggRenderMode::AM_binary:
00336     add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_binary));
00337     break;
00338 
00339   case EggRenderMode::AM_dual:
00340     add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_dual));
00341     break;
00342 
00343   default:
00344     break;
00345   }
00346 
00347   switch (dwm) {
00348   case EggRenderMode::DWM_on:
00349     add_attrib(DepthWriteAttrib::make(DepthWriteAttrib::M_on));
00350     break;
00351 
00352   case EggRenderMode::DWM_off:
00353     add_attrib(DepthWriteAttrib::make(DepthWriteAttrib::M_off));
00354     break;
00355 
00356   default:
00357     break;
00358   }
00359 
00360   switch (dtm) {
00361   case EggRenderMode::DTM_on:
00362     add_attrib(DepthTestAttrib::make(DepthTestAttrib::M_less));
00363     break;
00364 
00365   case EggRenderMode::DTM_off:
00366     add_attrib(DepthTestAttrib::make(DepthTestAttrib::M_none));
00367     break;
00368 
00369   default:
00370     break;
00371   }
00372 
00373   switch (vm) {
00374   case EggRenderMode::VM_hidden:
00375     _hidden = true;
00376     break;
00377 
00378   case EggRenderMode::VM_normal:
00379   default:
00380     break;
00381   }
00382 
00383   _flat_shaded = 
00384     (egg_flat_shading &&
00385      egg_prim->get_connected_shading() == EggPrimitive::S_per_face);
00386 
00387   if (_flat_shaded) {
00388     add_attrib(ShadeModelAttrib::make(ShadeModelAttrib::M_flat));
00389   }
00390 
00391   if (egg_prim->is_of_type(EggLine::get_class_type())) {
00392     _primitive_type = Geom::PT_lines;
00393     EggLine *egg_line = DCAST(EggLine, egg_prim);
00394     if (egg_line->get_thick() != 1.0) {
00395       add_attrib(RenderModeAttrib::make(RenderModeAttrib::M_unchanged, 
00396                                         egg_line->get_thick()));
00397     }
00398   } else if (egg_prim->is_of_type(EggPoint::get_class_type())) {
00399     _primitive_type = Geom::PT_points;
00400     EggPoint *egg_point = DCAST(EggPoint, egg_prim);
00401     if (egg_point->get_thick() != 1.0 || egg_point->get_perspective()) {
00402       add_attrib(RenderModeAttrib::make(RenderModeAttrib::M_unchanged, 
00403                                         egg_point->get_thick(),
00404                                         egg_point->get_perspective()));
00405     }
00406   } else {
00407     _primitive_type = Geom::PT_polygons;
00408   }
00409 
00410   if (has_bin) {
00411     add_attrib(CullBinAttrib::make(bin, draw_order));
00412 
00413   } else if (has_draw_order) {
00414     add_attrib(CullBinAttrib::make("fixed", draw_order));
00415   }
00416   if (has_depth_offset) {
00417     add_attrib(DepthOffsetAttrib::make(depth_offset));
00418   }
00419  
00420 
00421   if (egg_prim->get_bface_flag()) {
00422     // The primitive is marked with backface culling disabled--we want
00423     // to see both sides.
00424     add_attrib(CullFaceAttrib::make(CullFaceAttrib::M_cull_none));
00425   }
00426 }
00427 
00428 ////////////////////////////////////////////////////////////////////
00429 //     Function: EggRenderState::int compare_to
00430 //       Access: Public
00431 //  Description: Provides a unique ordering for different
00432 //               EggRenderState objects, so that primitives of similar
00433 //               state can be grouped together by the EggBinner.
00434 ////////////////////////////////////////////////////////////////////
00435 int EggRenderState::
00436 compare_to(const EggRenderState &other) const {
00437   if (_state != other._state) {
00438     int c = _state->compare_to(*other._state);
00439     if (c != 0) {
00440       return c;
00441     }
00442   }
00443   if (_hidden != other._hidden) {
00444     return (int)_hidden - (int)other._hidden;
00445   }
00446   if (_flat_shaded != other._flat_shaded) {
00447     return (int)_flat_shaded - (int)other._flat_shaded;
00448   }
00449   if (_primitive_type != other._primitive_type) {
00450     return (int)_primitive_type - (int)other._primitive_type;
00451   }
00452 
00453   if (_bake_in_uvs.size() != other._bake_in_uvs.size()) {
00454     return (int)_bake_in_uvs.size() - (int)other._bake_in_uvs.size();
00455   }
00456 
00457   BakeInUVs::const_iterator ai, bi;
00458   ai = _bake_in_uvs.begin();
00459   bi = other._bake_in_uvs.begin();
00460   while (ai != _bake_in_uvs.end()) {
00461     nassertr(bi != other._bake_in_uvs.end(), false);
00462     if ((*ai).first != (*bi).first) {
00463       return (*ai).first < (*bi).first ? -1 : 1;
00464     }
00465     if ((*ai).second != (*bi).second) {
00466       return (*ai).second < (*bi).second ? -1 : 1;
00467     }
00468     ++ai;
00469     ++bi;
00470   }
00471   nassertr(bi == other._bake_in_uvs.end(), false);
00472 
00473   return 0;
00474 }
00475 
00476 ////////////////////////////////////////////////////////////////////
00477 //     Function: EggRenderState::get_material_attrib
00478 //       Access: Private
00479 //  Description: Returns a RenderAttrib suitable for enabling the
00480 //               material indicated by the given EggMaterial, and with
00481 //               the indicated backface flag.
00482 ////////////////////////////////////////////////////////////////////
00483 CPT(RenderAttrib) EggRenderState::
00484 get_material_attrib(const EggMaterial *egg_mat, bool bface) {
00485   Materials &materials = 
00486     bface ? _loader._materials_bface : _loader._materials;
00487 
00488   // First, check whether we've seen this material before.
00489   Materials::const_iterator mi;
00490   mi = materials.find(egg_mat);
00491   if (mi != materials.end()) {
00492     return (*mi).second;
00493   }
00494 
00495   // Ok, this is the first time we've seen this particular
00496   // EggMaterial.  Create a new Material that matches it.
00497   PT(Material) mat = new Material(egg_mat->get_name());
00498   if (egg_mat->has_diff()) {
00499     mat->set_diffuse(egg_mat->get_diff());
00500     // By default, ambient is the same as diffuse, if diffuse is
00501     // specified but ambient is not.
00502     mat->set_ambient(egg_mat->get_diff());
00503   }
00504   if (egg_mat->has_amb()) {
00505     mat->set_ambient(egg_mat->get_amb());
00506   }
00507   if (egg_mat->has_emit()) {
00508     mat->set_emission(egg_mat->get_emit());
00509   }
00510   if (egg_mat->has_spec()) {
00511     mat->set_specular(egg_mat->get_spec());
00512   }
00513   if (egg_mat->has_shininess()) {
00514     mat->set_shininess(egg_mat->get_shininess());
00515   }
00516   if (egg_mat->has_local()) {
00517     mat->set_local(egg_mat->get_local());
00518   }
00519 
00520   mat->set_twoside(bface);
00521 
00522   // Now get a global Material pointer, shared with other models.
00523   Material *shared_mat = MaterialPool::get_material(mat);
00524 
00525   // And create a MaterialAttrib for this Material.
00526   CPT(RenderAttrib) mt = MaterialAttrib::make(shared_mat);
00527   materials.insert(Materials::value_type(egg_mat, mt));
00528 
00529   return mt;
00530 }
00531 
00532 ////////////////////////////////////////////////////////////////////
00533 //     Function: EggRenderState::get_tex_gen
00534 //       Access: Private, Static
00535 //  Description: Extracts the tex_gen from the given egg texture,
00536 //               and returns its corresponding TexGenAttrib mode.
00537 ////////////////////////////////////////////////////////////////////
00538 TexGenAttrib::Mode EggRenderState::
00539 get_tex_gen(const EggTexture *egg_tex) {
00540   switch (egg_tex->get_tex_gen()) {
00541   case EggTexture::TG_unspecified:
00542     return TexGenAttrib::M_off;
00543 
00544   case EggTexture::TG_eye_sphere_map:
00545     return TexGenAttrib::M_eye_sphere_map;
00546 
00547   case EggTexture::TG_world_cube_map:
00548     return TexGenAttrib::M_world_cube_map;
00549 
00550   case EggTexture::TG_eye_cube_map:
00551     return TexGenAttrib::M_eye_cube_map;
00552 
00553   case EggTexture::TG_world_normal:
00554     return TexGenAttrib::M_world_normal;
00555 
00556   case EggTexture::TG_eye_normal:
00557     return TexGenAttrib::M_eye_normal;
00558 
00559   case EggTexture::TG_world_position:
00560     return TexGenAttrib::M_world_position;
00561 
00562   case EggTexture::TG_eye_position:
00563     return TexGenAttrib::M_eye_position;
00564 
00565   case EggTexture::TG_point_sprite:
00566     return TexGenAttrib::M_point_sprite;
00567   };
00568 
00569   return TexGenAttrib::M_off;
00570 }
00571 
00572 ////////////////////////////////////////////////////////////////////
00573 //     Function: EggRenderState::apply_tex_mat
00574 //       Access: Private
00575 //  Description: Applies the texture matrix from the indicated egg
00576 //               texture to the given TexMatrixAttrib, and returns the
00577 //               new attrib.
00578 ////////////////////////////////////////////////////////////////////
00579 CPT(RenderAttrib) EggRenderState::
00580 apply_tex_mat(CPT(RenderAttrib) tex_mat_attrib, 
00581               TextureStage *stage, const EggTexture *egg_tex) {
00582   if (egg_tex->has_transform()) {
00583     CPT(TransformState) transform = _loader.make_transform(egg_tex);
00584   
00585     if (tex_mat_attrib == (const RenderAttrib *)NULL) {
00586       tex_mat_attrib = TexMatrixAttrib::make();
00587     }
00588     tex_mat_attrib = DCAST(TexMatrixAttrib, tex_mat_attrib)->
00589       add_stage(stage, transform);
00590   }
00591     
00592   return tex_mat_attrib;
00593 }
 All Classes Functions Variables Enumerations