Panda3D
|
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 }