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 CPT(RenderAttrib) mt = 00251 get_material_attrib(egg_prim->get_material(), 00252 egg_prim->get_bface_flag()); 00253 add_attrib(mt); 00254 } 00255 00256 00257 // Also check the color of the primitive to see if we should assume 00258 // alpha based on the alpha values specified in the egg file. 00259 if (am == EggRenderMode::AM_unspecified) { 00260 if (egg_prim->has_color()) { 00261 if (egg_prim->get_color()[3] != 1.0) { 00262 implicit_alpha = true; 00263 binary_alpha_only = false; 00264 } 00265 } 00266 EggPrimitive::const_iterator vi; 00267 for (vi = egg_prim->begin(); 00268 !implicit_alpha && vi != egg_prim->end(); 00269 ++vi) { 00270 if ((*vi)->has_color()) { 00271 if ((*vi)->get_color()[3] != 1.0) { 00272 implicit_alpha = true; 00273 binary_alpha_only = false; 00274 } 00275 } 00276 } 00277 00278 if (implicit_alpha) { 00279 am = EggRenderMode::AM_on; 00280 } 00281 } 00282 00283 switch (am) { 00284 case EggRenderMode::AM_on: 00285 // Alpha type "on" means to get the default transparency type. 00286 if (binary_alpha_only) { 00287 am = EggRenderMode::AM_binary; 00288 } else if (egg_alpha_mode != EggRenderMode::AM_unspecified) { 00289 am = egg_alpha_mode; 00290 } 00291 break; 00292 00293 case EggRenderMode::AM_blend: 00294 case EggRenderMode::AM_ms: 00295 case EggRenderMode::AM_ms_mask: 00296 case EggRenderMode::AM_dual: 00297 // Any of these modes gets implicitly downgraded to AM_binary, if 00298 // all of the alpha sources only contribute a binary value to 00299 // alpha. 00300 if (binary_alpha_only) { 00301 am = EggRenderMode::AM_binary; 00302 } 00303 break; 00304 00305 default: 00306 break; 00307 } 00308 00309 switch (am) { 00310 case EggRenderMode::AM_on: 00311 case EggRenderMode::AM_blend: 00312 add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha)); 00313 break; 00314 00315 case EggRenderMode::AM_blend_no_occlude: 00316 add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha)); 00317 add_attrib(DepthWriteAttrib::make(DepthWriteAttrib::M_off)); 00318 break; 00319 00320 case EggRenderMode::AM_ms: 00321 add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_multisample)); 00322 break; 00323 00324 case EggRenderMode::AM_ms_mask: 00325 add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_multisample_mask)); 00326 break; 00327 00328 case EggRenderMode::AM_binary: 00329 add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_binary)); 00330 break; 00331 00332 case EggRenderMode::AM_dual: 00333 add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_dual)); 00334 break; 00335 00336 default: 00337 break; 00338 } 00339 00340 switch (dwm) { 00341 case EggRenderMode::DWM_on: 00342 add_attrib(DepthWriteAttrib::make(DepthWriteAttrib::M_on)); 00343 break; 00344 00345 case EggRenderMode::DWM_off: 00346 add_attrib(DepthWriteAttrib::make(DepthWriteAttrib::M_off)); 00347 break; 00348 00349 default: 00350 break; 00351 } 00352 00353 switch (dtm) { 00354 case EggRenderMode::DTM_on: 00355 add_attrib(DepthTestAttrib::make(DepthTestAttrib::M_less)); 00356 break; 00357 00358 case EggRenderMode::DTM_off: 00359 add_attrib(DepthTestAttrib::make(DepthTestAttrib::M_none)); 00360 break; 00361 00362 default: 00363 break; 00364 } 00365 00366 switch (vm) { 00367 case EggRenderMode::VM_hidden: 00368 _hidden = true; 00369 break; 00370 00371 case EggRenderMode::VM_normal: 00372 default: 00373 break; 00374 } 00375 00376 _flat_shaded = 00377 (egg_flat_shading && 00378 egg_prim->get_connected_shading() == EggPrimitive::S_per_face); 00379 00380 if (_flat_shaded) { 00381 add_attrib(ShadeModelAttrib::make(ShadeModelAttrib::M_flat)); 00382 } 00383 00384 if (egg_prim->is_of_type(EggLine::get_class_type())) { 00385 _primitive_type = Geom::PT_lines; 00386 EggLine *egg_line = DCAST(EggLine, egg_prim); 00387 if (egg_line->get_thick() != 1.0) { 00388 add_attrib(RenderModeAttrib::make(RenderModeAttrib::M_unchanged, 00389 egg_line->get_thick())); 00390 } 00391 } else if (egg_prim->is_of_type(EggPoint::get_class_type())) { 00392 _primitive_type = Geom::PT_points; 00393 EggPoint *egg_point = DCAST(EggPoint, egg_prim); 00394 if (egg_point->get_thick() != 1.0 || egg_point->get_perspective()) { 00395 add_attrib(RenderModeAttrib::make(RenderModeAttrib::M_unchanged, 00396 egg_point->get_thick(), 00397 egg_point->get_perspective())); 00398 } 00399 } else { 00400 _primitive_type = Geom::PT_polygons; 00401 } 00402 00403 if (has_bin) { 00404 add_attrib(CullBinAttrib::make(bin, draw_order)); 00405 00406 } else if (has_draw_order) { 00407 add_attrib(CullBinAttrib::make("fixed", draw_order)); 00408 } 00409 if (has_depth_offset) { 00410 add_attrib(DepthOffsetAttrib::make(depth_offset)); 00411 } 00412 00413 00414 if (egg_prim->get_bface_flag()) { 00415 // The primitive is marked with backface culling disabled--we want 00416 // to see both sides. 00417 add_attrib(CullFaceAttrib::make(CullFaceAttrib::M_cull_none)); 00418 } 00419 } 00420 00421 //////////////////////////////////////////////////////////////////// 00422 // Function: EggRenderState::int compare_to 00423 // Access: Public 00424 // Description: Provides a unique ordering for different 00425 // EggRenderState objects, so that primitives of similar 00426 // state can be grouped together by the EggBinner. 00427 //////////////////////////////////////////////////////////////////// 00428 int EggRenderState:: 00429 compare_to(const EggRenderState &other) const { 00430 if (_state != other._state) { 00431 if ((*_state) < (*other._state)) { 00432 return -1; 00433 } 00434 if ((*other._state) < (*_state)) { 00435 return 1; 00436 } 00437 } 00438 if (_hidden != other._hidden) { 00439 return (int)_hidden - (int)other._hidden; 00440 } 00441 if (_flat_shaded != other._flat_shaded) { 00442 return (int)_flat_shaded - (int)other._flat_shaded; 00443 } 00444 if (_primitive_type != other._primitive_type) { 00445 return (int)_primitive_type - (int)other._primitive_type; 00446 } 00447 00448 if (_bake_in_uvs.size() != other._bake_in_uvs.size()) { 00449 return (int)_bake_in_uvs.size() - (int)other._bake_in_uvs.size(); 00450 } 00451 00452 BakeInUVs::const_iterator ai, bi; 00453 ai = _bake_in_uvs.begin(); 00454 bi = other._bake_in_uvs.begin(); 00455 while (ai != _bake_in_uvs.end()) { 00456 nassertr(bi != other._bake_in_uvs.end(), false); 00457 if ((*ai).first != (*bi).first) { 00458 return (*ai).first < (*bi).first ? -1 : 1; 00459 } 00460 if ((*ai).second != (*bi).second) { 00461 return (*ai).second < (*bi).second ? -1 : 1; 00462 } 00463 ++ai; 00464 ++bi; 00465 } 00466 nassertr(bi == other._bake_in_uvs.end(), false); 00467 00468 return 0; 00469 } 00470 00471 //////////////////////////////////////////////////////////////////// 00472 // Function: EggRenderState::get_material_attrib 00473 // Access: Private 00474 // Description: Returns a RenderAttrib suitable for enabling the 00475 // material indicated by the given EggMaterial, and with 00476 // the indicated backface flag. 00477 //////////////////////////////////////////////////////////////////// 00478 CPT(RenderAttrib) EggRenderState:: 00479 get_material_attrib(const EggMaterial *egg_mat, bool bface) { 00480 Materials &materials = 00481 bface ? _loader._materials_bface : _loader._materials; 00482 00483 // First, check whether we've seen this material before. 00484 Materials::const_iterator mi; 00485 mi = materials.find(egg_mat); 00486 if (mi != materials.end()) { 00487 return (*mi).second; 00488 } 00489 00490 // Ok, this is the first time we've seen this particular 00491 // EggMaterial. Create a new Material that matches it. 00492 PT(Material) mat = new Material(egg_mat->get_name()); 00493 if (egg_mat->has_diff()) { 00494 mat->set_diffuse(egg_mat->get_diff()); 00495 // By default, ambient is the same as diffuse, if diffuse is 00496 // specified but ambient is not. 00497 mat->set_ambient(egg_mat->get_diff()); 00498 } 00499 if (egg_mat->has_amb()) { 00500 mat->set_ambient(egg_mat->get_amb()); 00501 } 00502 if (egg_mat->has_emit()) { 00503 mat->set_emission(egg_mat->get_emit()); 00504 } 00505 if (egg_mat->has_spec()) { 00506 mat->set_specular(egg_mat->get_spec()); 00507 } 00508 if (egg_mat->has_shininess()) { 00509 mat->set_shininess(egg_mat->get_shininess()); 00510 } 00511 if (egg_mat->has_local()) { 00512 mat->set_local(egg_mat->get_local()); 00513 } 00514 00515 mat->set_twoside(bface); 00516 00517 // Now get a global Material pointer, shared with other models. 00518 Material *shared_mat = MaterialPool::get_material(mat); 00519 00520 // And create a MaterialAttrib for this Material. 00521 CPT(RenderAttrib) mt = MaterialAttrib::make(shared_mat); 00522 materials.insert(Materials::value_type(egg_mat, mt)); 00523 00524 return mt; 00525 } 00526 00527 //////////////////////////////////////////////////////////////////// 00528 // Function: EggRenderState::get_tex_gen 00529 // Access: Private, Static 00530 // Description: Extracts the tex_gen from the given egg texture, 00531 // and returns its corresponding TexGenAttrib mode. 00532 //////////////////////////////////////////////////////////////////// 00533 TexGenAttrib::Mode EggRenderState:: 00534 get_tex_gen(const EggTexture *egg_tex) { 00535 switch (egg_tex->get_tex_gen()) { 00536 case EggTexture::TG_unspecified: 00537 return TexGenAttrib::M_off; 00538 00539 case EggTexture::TG_eye_sphere_map: 00540 return TexGenAttrib::M_eye_sphere_map; 00541 00542 case EggTexture::TG_world_cube_map: 00543 return TexGenAttrib::M_world_cube_map; 00544 00545 case EggTexture::TG_eye_cube_map: 00546 return TexGenAttrib::M_eye_cube_map; 00547 00548 case EggTexture::TG_world_normal: 00549 return TexGenAttrib::M_world_normal; 00550 00551 case EggTexture::TG_eye_normal: 00552 return TexGenAttrib::M_eye_normal; 00553 00554 case EggTexture::TG_world_position: 00555 return TexGenAttrib::M_world_position; 00556 00557 case EggTexture::TG_eye_position: 00558 return TexGenAttrib::M_eye_position; 00559 00560 case EggTexture::TG_point_sprite: 00561 return TexGenAttrib::M_point_sprite; 00562 }; 00563 00564 return TexGenAttrib::M_off; 00565 } 00566 00567 //////////////////////////////////////////////////////////////////// 00568 // Function: EggRenderState::apply_tex_mat 00569 // Access: Private 00570 // Description: Applies the texture matrix from the indicated egg 00571 // texture to the given TexMatrixAttrib, and returns the 00572 // new attrib. 00573 //////////////////////////////////////////////////////////////////// 00574 CPT(RenderAttrib) EggRenderState:: 00575 apply_tex_mat(CPT(RenderAttrib) tex_mat_attrib, 00576 TextureStage *stage, const EggTexture *egg_tex) { 00577 if (egg_tex->has_transform()) { 00578 CPT(TransformState) transform = _loader.make_transform(egg_tex); 00579 00580 if (tex_mat_attrib == (const RenderAttrib *)NULL) { 00581 tex_mat_attrib = TexMatrixAttrib::make(); 00582 } 00583 tex_mat_attrib = DCAST(TexMatrixAttrib, tex_mat_attrib)-> 00584 add_stage(stage, transform); 00585 } 00586 00587 return tex_mat_attrib; 00588 }