Panda3D

daeMaterials.cxx

00001 // Filename: daeMaterials.cxx
00002 // Created by:  pro-rsoft (03Oct08)
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 "daeMaterials.h"
00016 #include "config_daeegg.h"
00017 #include "fcollada_utils.h"
00018 
00019 #include "FCDocument/FCDocument.h"
00020 #include "FCDocument/FCDMaterial.h"
00021 #include "FCDocument/FCDEffect.h"
00022 #include "FCDocument/FCDTexture.h"
00023 #include "FCDocument/FCDEffectParameterSampler.h"
00024 #include "FCDocument/FCDImage.h"
00025 
00026 #include "filename.h"
00027 #include "string_utils.h"
00028 
00029 TypeHandle DaeMaterials::_type_handle;
00030 
00031 // luminance function, based on the ISO/CIE color standards
00032 // see ITU-R Recommendation BT.709-4
00033 #define luminance(c) ((c[0] * 0.212671 + c[1] * 0.715160 + c[2] * 0.072169))
00034 
00035 ////////////////////////////////////////////////////////////////////
00036 //     Function: DaeMaterials::Constructor
00037 //       Access: Public
00038 //  Description: 
00039 ////////////////////////////////////////////////////////////////////
00040 DaeMaterials::
00041 DaeMaterials(const FCDGeometryInstance* geometry_instance) {
00042   for (size_t mi = 0; mi < geometry_instance->GetMaterialInstanceCount(); ++mi) {
00043     add_material_instance(geometry_instance->GetMaterialInstance(mi));
00044   }
00045 }
00046 
00047 ////////////////////////////////////////////////////////////////////
00048 //     Function: DaeMaterials::add_material_instance
00049 //       Access: Public
00050 //  Description: Adds a material instance. Normally automatically
00051 //               done by constructor.
00052 ////////////////////////////////////////////////////////////////////
00053 void DaeMaterials::add_material_instance(const FCDMaterialInstance* instance) {
00054   nassertv(instance != NULL);
00055   const string semantic (FROM_FSTRING(instance->GetSemantic()));
00056   if (_materials.count(semantic) > 0) {
00057     daeegg_cat.warning() << "Ignoring duplicate material with semantic " << semantic << endl;
00058     return;
00059   }
00060   _materials[semantic] = new DaeMaterial();
00061   
00062   // Load in the uvsets
00063   for (size_t vib = 0; vib < instance->GetVertexInputBindingCount(); ++vib) {
00064     const FCDMaterialInstanceBindVertexInput* mivib = instance->GetVertexInputBinding(vib);
00065     assert(mivib != NULL);
00066     PT(DaeVertexInputBinding) bvi = new DaeVertexInputBinding();
00067     bvi->_input_set = mivib->inputSet;
00068 #if FCOLLADA_VERSION >= 0x00030005
00069     bvi->_input_semantic = mivib->GetInputSemantic();
00070     bvi->_semantic = *mivib->semantic;
00071 #else
00072     bvi->_input_semantic = mivib->inputSemantic;
00073     bvi->_semantic = FROM_FSTRING(mivib->semantic);
00074 #endif
00075     _materials[semantic]->_uvsets.push_back(bvi);
00076   }
00077   
00078   // Handle the material stuff
00079   daeegg_cat.spam() << "Trying to process material with semantic " << semantic << endl;
00080   PT_EggMaterial egg_material = new EggMaterial(semantic);
00081   pvector<PT_EggTexture> egg_textures;
00082   const FCDEffect* effect = instance->GetMaterial()->GetEffect();
00083   if (effect == NULL) {
00084     daeegg_cat.debug() << "Ignoring material (semantic: " << semantic << ") without assigned effect" << endl;
00085   } else {
00086     // Grab the common profile effect
00087     const FCDEffectStandard* effect_common = (FCDEffectStandard *)effect->FindProfile(FUDaeProfileType::COMMON);
00088     if (effect_common == NULL) {
00089       daeegg_cat.info() << "Ignoring effect referenced by material with semantic " << semantic
00090                          << " because it has no common profile" << endl;
00091     } else {
00092       daeegg_cat.spam() << "Processing effect, material semantic is " << semantic << endl;
00093       // Set the material parameters
00094       egg_material->set_amb(TO_COLOR(effect_common->GetAmbientColor()));
00095       ////We already process transparency using blend modes
00096       //LVecBase4 diffuse = TO_COLOR(effect_common->GetDiffuseColor());
00097       //diffuse.set_w(diffuse.get_w() * (1.0f - effect_common->GetOpacity()));
00098       //egg_material->set_diff(diffuse);
00099       egg_material->set_diff(TO_COLOR(effect_common->GetDiffuseColor()));
00100       egg_material->set_emit(TO_COLOR(effect_common->GetEmissionColor()) * effect_common->GetEmissionFactor());
00101       egg_material->set_shininess(effect_common->GetShininess());
00102       egg_material->set_spec(TO_COLOR(effect_common->GetSpecularColor()));
00103       // Now try to load in the textures
00104       process_texture_bucket(semantic, effect_common, FUDaeTextureChannel::DIFFUSE, EggTexture::ET_modulate);
00105       process_texture_bucket(semantic, effect_common, FUDaeTextureChannel::BUMP, EggTexture::ET_normal);
00106       process_texture_bucket(semantic, effect_common, FUDaeTextureChannel::SPECULAR, EggTexture::ET_modulate_gloss);
00107       process_texture_bucket(semantic, effect_common, FUDaeTextureChannel::SPECULAR_LEVEL, EggTexture::ET_gloss);
00108       process_texture_bucket(semantic, effect_common, FUDaeTextureChannel::TRANSPARENT, EggTexture::ET_unspecified, EggTexture::F_alpha);
00109       process_texture_bucket(semantic, effect_common, FUDaeTextureChannel::EMISSION, EggTexture::ET_add);
00110 #if FCOLLADA_VERSION < 0x00030005
00111       process_texture_bucket(semantic, effect_common, FUDaeTextureChannel::OPACITY, EggTexture::ET_unspecified, EggTexture::F_alpha);
00112 #endif
00113       // Now, calculate the color blend stuff.
00114       _materials[semantic]->_blend = convert_blend(effect_common->GetTransparencyMode(),
00115                                           TO_COLOR(effect_common->GetTranslucencyColor()),
00116                                                    effect_common->GetTranslucencyFactor());
00117     }
00118     // Find an <extra> tag to support some extra stuff from extensions
00119     process_extra(semantic, effect->GetExtra());
00120   }
00121   daeegg_cat.spam() << "Found " << egg_textures.size() << " textures in material" << endl;
00122   _materials[semantic]->_egg_material = egg_material;
00123 }
00124 
00125 ////////////////////////////////////////////////////////////////////
00126 //     Function: DaeMaterials::process_texture_bucket
00127 //       Access: Private
00128 //  Description: Processes the given texture bucket and gives
00129 //               the textures in it the given envtype and format.
00130 ////////////////////////////////////////////////////////////////////
00131 void DaeMaterials::
00132 process_texture_bucket(const string semantic, const FCDEffectStandard* effect_common, FUDaeTextureChannel::Channel bucket, EggTexture::EnvType envtype, EggTexture::Format format) {
00133   for (size_t tx = 0; tx < effect_common->GetTextureCount(bucket); ++tx) {
00134     const FCDImage* image = effect_common->GetTexture(bucket, tx)->GetImage();
00135     if (image == NULL) {
00136       daeegg_cat.warning() << "Texture references a nonexisting image!" << endl;
00137     } else {
00138       const FCDEffectParameterSampler* sampler = effect_common->GetTexture(bucket, tx)->GetSampler();
00139       // FCollada only supplies absolute paths. We need to grab the document
00140       // location ourselves and make the image path absolute.
00141       Filename texpath;
00142       if (image->GetDocument()) {
00143         Filename docpath = Filename::from_os_specific(FROM_FSTRING(image->GetDocument()->GetFileUrl()));
00144         docpath.make_canonical();
00145         texpath = Filename::from_os_specific(FROM_FSTRING(image->GetFilename()));
00146         texpath.make_canonical();
00147         texpath.make_relative_to(docpath.get_dirname(), true);
00148         daeegg_cat.debug() << "Found texture with path " << texpath << endl;
00149       } else {
00150         // Never mind.
00151         texpath = Filename::from_os_specific(FROM_FSTRING(image->GetFilename()));
00152       }
00153       PT_EggTexture egg_texture = new EggTexture(FROM_FSTRING(image->GetDaeId()), texpath.to_os_generic());
00154       // Find a set of UV coordinates
00155       const FCDEffectParameterInt* uvset = effect_common->GetTexture(bucket, tx)->GetSet();
00156       if (uvset != NULL) {
00157         daeegg_cat.debug() << "Texture has uv name '" << FROM_FSTRING(uvset->GetSemantic()) << "'\n";
00158         string uvset_semantic (FROM_FSTRING(uvset->GetSemantic()));
00159 
00160         // Only set the UV name if this UV set actually exists.
00161         for (int i = 0; i < _materials[semantic]->_uvsets.size(); ++i) {
00162           if (_materials[semantic]->_uvsets[i]->_semantic == uvset_semantic) {
00163             egg_texture->set_uv_name(uvset_semantic);
00164             break;
00165           }
00166         }
00167       }
00168       // Apply sampler stuff
00169       if (sampler != NULL) {
00170         egg_texture->set_texture_type(convert_texture_type(sampler->GetSamplerType()));
00171         egg_texture->set_wrap_u(convert_wrap_mode(sampler->GetWrapS()));
00172         if (sampler->GetSamplerType() != FCDEffectParameterSampler::SAMPLER1D) {
00173           egg_texture->set_wrap_v(convert_wrap_mode(sampler->GetWrapT()));
00174         }
00175         if (sampler->GetSamplerType() == FCDEffectParameterSampler::SAMPLER3D) {
00176           egg_texture->set_wrap_w(convert_wrap_mode(sampler->GetWrapP()));
00177         }
00178         egg_texture->set_minfilter(convert_filter_type(sampler->GetMinFilter()));
00179         egg_texture->set_magfilter(convert_filter_type(sampler->GetMagFilter()));
00180         if (envtype != EggTexture::ET_unspecified) {
00181           egg_texture->set_env_type(envtype);
00182         }
00183         if (format != EggTexture::F_unspecified) {
00184           egg_texture->set_format(format);
00185         }
00186       }
00187       _materials[semantic]->_egg_textures.push_back(egg_texture);
00188     }
00189   }
00190 }
00191 
00192 ////////////////////////////////////////////////////////////////////
00193 //     Function: DaeMaterials::process_extra
00194 //       Access: Private
00195 //  Description: Processes the extra data in the given <extra> tag.
00196 //               If the given element is NULL, it just silently
00197 //               returns.
00198 ////////////////////////////////////////////////////////////////////
00199 void DaeMaterials::
00200 process_extra(const string semantic, const FCDExtra* extra) {
00201   if (extra == NULL) return;
00202   const FCDEType* etype = extra->GetDefaultType();
00203   if (etype == NULL) return;
00204   for (size_t et = 0; et < etype->GetTechniqueCount(); ++et) {
00205     const FCDENode* enode = ((const FCDENode*)(etype->GetTechnique(et)))->FindChildNode("double_sided");
00206     if (enode != NULL) {
00207       if (trim(enode->GetContent()) == "1") {
00208         _materials[semantic]->_double_sided = true;
00209       } else if (trim(enode->GetContent()) == "0") {
00210         _materials[semantic]->_double_sided = false;
00211       } else {
00212         daeegg_cat.warning() << "Expected <double_sided> tag to be either 1 or 0, found '" << enode->GetContent() << "' instead" << endl;
00213       }
00214     }
00215   }
00216 }
00217 
00218 ////////////////////////////////////////////////////////////////////
00219 //     Function: DaeMaterials::apply_to
00220 //       Access: Public
00221 //  Description: Applies the stuff to the given EggPrimitive.
00222 ////////////////////////////////////////////////////////////////////
00223 void DaeMaterials::
00224 apply_to(const string semantic, const PT(EggPrimitive) to) {
00225   if (_materials.count(semantic) > 0) {
00226     to->set_material(_materials[semantic]->_egg_material);
00227     for (pvector<PT_EggTexture>::iterator it = _materials[semantic]->_egg_textures.begin(); it != _materials[semantic]->_egg_textures.end(); ++it) {
00228       daeegg_cat.spam() << "Applying texture " << (*it)->get_name() << " from material with semantic " << semantic << endl;
00229       to->add_texture(*it);
00230     }
00231     to->set_bface_flag(_materials[semantic]->_double_sided);
00232   }
00233 }
00234 
00235 ////////////////////////////////////////////////////////////////////
00236 //     Function: DaeMaterials::apply_to
00237 //       Access: Public
00238 //  Description: Applies the colorblend stuff to the given EggGroup.
00239 ////////////////////////////////////////////////////////////////////
00240 void DaeMaterials::
00241 apply_to(const string semantic, const PT(EggGroup) to) {
00242   if (_materials.count(semantic) > 0) {
00243     PT(DaeBlendSettings) blend = _materials[semantic]->_blend;
00244     if (blend && blend->_enabled) {
00245       to->set_blend_mode(EggGroup::BM_add);
00246       to->set_blend_color(blend->_color);
00247       to->set_blend_operand_a(blend->_operand_a);
00248       to->set_blend_operand_b(blend->_operand_b);
00249     }
00250   }
00251 }
00252 
00253 ////////////////////////////////////////////////////////////////////
00254 //     Function: DaeMaterials::get_uvset_name
00255 //       Access: Public
00256 //  Description: Returns the semantic of the uvset with the
00257 //               specified input set, or an empty string if the
00258 //               given material has no input set.
00259 ////////////////////////////////////////////////////////////////////
00260 const string DaeMaterials::
00261 get_uvset_name(const string semantic, FUDaeGeometryInput::Semantic input_semantic, int32 input_set) {
00262   if (_materials.count(semantic) > 0) {
00263     if (input_set == -1 && _materials[semantic]->_uvsets.size() == 1) {
00264       return _materials[semantic]->_uvsets[0]->_semantic;
00265     } else {
00266       for (int i = 0; i < _materials[semantic]->_uvsets.size(); ++i) {
00267         if (_materials[semantic]->_uvsets[i]->_input_set == input_set &&
00268             _materials[semantic]->_uvsets[i]->_input_semantic == input_semantic) {
00269           return _materials[semantic]->_uvsets[i]->_semantic;
00270         }
00271       }
00272       // If we can't find it, let's look again, but don't care for the
00273       // input_semantic this time. The reason for this is that some tools
00274       // export textangents and texbinormals bound to a uvset with input
00275       // semantic TEXCOORD.
00276       for (int i = 0; i < _materials[semantic]->_uvsets.size(); ++i) {
00277         if (_materials[semantic]->_uvsets[i]->_input_set == input_set) {
00278           daeegg_cat.debug() << "Using uv set with non-matching input semantic " << _materials[semantic]->_uvsets[i]->_semantic << "\n";
00279           return _materials[semantic]->_uvsets[i]->_semantic;
00280         }
00281       }
00282       daeegg_cat.debug() << "No uv set binding found for input set " << input_set << "\n";
00283     }
00284   }
00285   return "";
00286 }
00287 
00288 ////////////////////////////////////////////////////////////////////
00289 //     Function: DaeMaterials::convert_texture_type
00290 //       Access: Public, Static
00291 //  Description: Converts an FCollada sampler type to the EggTexture
00292 //               texture type equivalent.
00293 ////////////////////////////////////////////////////////////////////
00294 EggTexture::TextureType DaeMaterials::
00295 convert_texture_type(const FCDEffectParameterSampler::SamplerType orig_type) {
00296   switch (orig_type) {
00297     case FCDEffectParameterSampler::SAMPLER1D:
00298       return EggTexture::TT_1d_texture;
00299     case FCDEffectParameterSampler::SAMPLER2D:
00300       return EggTexture::TT_2d_texture;
00301     case FCDEffectParameterSampler::SAMPLER3D:
00302       return EggTexture::TT_3d_texture;
00303     case FCDEffectParameterSampler::SAMPLERCUBE:
00304       return EggTexture::TT_cube_map;
00305     default:
00306       daeegg_cat.warning() << "Invalid sampler type found" << endl;
00307   }
00308   return EggTexture::TT_unspecified;
00309 }
00310 
00311 ////////////////////////////////////////////////////////////////////
00312 //     Function: DaeMaterials::convert_wrap_mode
00313 //       Access: Public, Static
00314 //  Description: Converts an FCollada wrap mode to the
00315 //               EggTexture wrap mode equivalent.
00316 ////////////////////////////////////////////////////////////////////
00317 EggTexture::WrapMode DaeMaterials::
00318 convert_wrap_mode(const FUDaeTextureWrapMode::WrapMode orig_mode) {
00319   switch (orig_mode) {
00320     case FUDaeTextureWrapMode::NONE:
00321       //FIXME: this shouldnt be unspecified
00322       return EggTexture::WM_unspecified;
00323     case FUDaeTextureWrapMode::WRAP:
00324       return EggTexture::WM_repeat;
00325     case FUDaeTextureWrapMode::MIRROR:
00326       return EggTexture::WM_mirror;
00327     case FUDaeTextureWrapMode::CLAMP:
00328       return EggTexture::WM_clamp;
00329     case FUDaeTextureWrapMode::BORDER:
00330       return EggTexture::WM_border_color;
00331     case FUDaeTextureWrapMode::UNKNOWN:
00332       return EggTexture::WM_unspecified;
00333     default:
00334       daeegg_cat.warning() << "Invalid wrap mode found: " << FUDaeTextureWrapMode::ToString(orig_mode) << endl;
00335   }
00336   return EggTexture::WM_unspecified;
00337 }
00338 
00339 ////////////////////////////////////////////////////////////////////
00340 //     Function: DaeMaterials::convert_filter_type
00341 //       Access: Public, Static
00342 //  Description: Converts an FCollada filter function to the
00343 //               EggTexture wrap type equivalent.
00344 ////////////////////////////////////////////////////////////////////
00345 EggTexture::FilterType DaeMaterials::
00346 convert_filter_type(const FUDaeTextureFilterFunction::FilterFunction orig_type) {
00347   switch (orig_type) {
00348     case FUDaeTextureFilterFunction::NONE:
00349       //FIXME: this shouldnt be unspecified
00350       return EggTexture::FT_unspecified;
00351     case FUDaeTextureFilterFunction::NEAREST:
00352       return EggTexture::FT_nearest;
00353     case FUDaeTextureFilterFunction::LINEAR:
00354       return EggTexture::FT_linear;
00355     case FUDaeTextureFilterFunction::NEAREST_MIPMAP_NEAREST:
00356       return EggTexture::FT_nearest_mipmap_nearest;
00357     case FUDaeTextureFilterFunction::LINEAR_MIPMAP_NEAREST:
00358       return EggTexture::FT_linear_mipmap_nearest;
00359     case FUDaeTextureFilterFunction::NEAREST_MIPMAP_LINEAR:
00360       return EggTexture::FT_nearest_mipmap_linear;
00361     case FUDaeTextureFilterFunction::LINEAR_MIPMAP_LINEAR:
00362       return EggTexture::FT_linear_mipmap_linear;
00363     case FUDaeTextureFilterFunction::UNKNOWN:
00364       return EggTexture::FT_unspecified;
00365     default:
00366       daeegg_cat.warning() << "Unknown filter type found: " << FUDaeTextureFilterFunction::ToString(orig_type) << endl;
00367   }
00368   return EggTexture::FT_unspecified;
00369 }
00370 
00371 ////////////////////////////////////////////////////////////////////
00372 //     Function: DaeMaterials::convert_blend
00373 //       Access: Private, Static
00374 //  Description: Converts collada blend attribs to Panda's equivalents.
00375 ////////////////////////////////////////////////////////////////////
00376 PT(DaeMaterials::DaeBlendSettings) DaeMaterials::
00377 convert_blend(FCDEffectStandard::TransparencyMode mode, const LColor &transparent, double transparency) {
00378   // Create the DaeBlendSettings and fill it with some defaults.
00379   PT(DaeBlendSettings) blend = new DaeBlendSettings();
00380   blend->_enabled = true;
00381   blend->_color = LColor::zero();
00382   blend->_operand_a = EggGroup::BO_unspecified;
00383   blend->_operand_b = EggGroup::BO_unspecified;
00384   
00385   // First fill in the color value.
00386   if (mode == FCDEffectStandard::A_ONE) {// || mode == FCDEffectStandard::A_ZERO) {
00387     double value = transparent[3] * transparency;
00388     blend->_color = LColor(value, value, value, value);
00389   } else if (mode == FCDEffectStandard::RGB_ZERO) {//|| mode == FCDEffectStandard::RGB_ONE) {
00390     blend->_color = transparent * transparency;
00391     blend->_color[3] = luminance(blend->_color);
00392   } else {
00393     daeegg_cat.error() << "Unknown opaque type found!" << endl;
00394     blend->_enabled = false;
00395     return blend;
00396   }
00397   
00398   // Now figure out the operands.
00399   if (mode == FCDEffectStandard::RGB_ZERO) {// || mode == FCDEffectStandard::A_ZERO) {
00400     blend->_operand_a = EggGroup::BO_one_minus_constant_color;
00401     blend->_operand_b = EggGroup::BO_constant_color;
00402   } else if (mode == FCDEffectStandard::A_ONE) {// || mode == FCDEffectStandard::RGB_ONE) {
00403     blend->_operand_a = EggGroup::BO_constant_color;
00404     blend->_operand_b = EggGroup::BO_one_minus_constant_color;
00405   } else {
00406     daeegg_cat.error() << "Unknown opaque type found!" << endl;
00407     blend->_enabled = false;
00408     return blend;
00409   }
00410   
00411   // See if we can optimize out the color.
00412   if (blend->_operand_a == EggGroup::BO_constant_color) {
00413     if ((blend->_color[0] == 0) && (blend->_color[1] == 0) && (blend->_color[2] == 0) && (blend->_color[3] == 0)) {
00414       blend->_operand_a = EggGroup::BO_zero;
00415     }
00416     if ((blend->_color[0] == 1) && (blend->_color[1] == 1) && (blend->_color[2] == 1) && (blend->_color[3] == 1)) {
00417       blend->_operand_a = EggGroup::BO_one;
00418     }
00419   }
00420   if (blend->_operand_b == EggGroup::BO_constant_color) {
00421     if ((blend->_color[0] == 0) && (blend->_color[1] == 0) && (blend->_color[2] == 0) && (blend->_color[3] == 0)) {
00422       blend->_operand_b = EggGroup::BO_zero;
00423     }
00424     if ((blend->_color[0] == 1) && (blend->_color[1] == 1) && (blend->_color[2] == 1) && (blend->_color[3] == 1)) {
00425       blend->_operand_b = EggGroup::BO_one;
00426     }
00427   }
00428   if (blend->_operand_a == EggGroup::BO_one_minus_constant_color) {
00429     if ((blend->_color[0] == 0) && (blend->_color[1] == 0) && (blend->_color[2] == 0) && (blend->_color[3] == 0)) {
00430       blend->_operand_a = EggGroup::BO_one;
00431     }
00432     if ((blend->_color[0] == 1) && (blend->_color[1] == 1) && (blend->_color[2] == 1) && (blend->_color[3] == 1)) {
00433       blend->_operand_a = EggGroup::BO_zero;
00434     }
00435   }
00436   if (blend->_operand_b == EggGroup::BO_one_minus_constant_color) {
00437     if ((blend->_color[0] == 0) && (blend->_color[1] == 0) && (blend->_color[2] == 0) && (blend->_color[3] == 0)) {
00438       blend->_operand_b = EggGroup::BO_one;
00439     }
00440     if ((blend->_color[0] == 1) && (blend->_color[1] == 1) && (blend->_color[2] == 1) && (blend->_color[3] == 1)) {
00441       blend->_operand_b = EggGroup::BO_zero;
00442     }
00443   }
00444   
00445   // See if we can entirely disable the blend.
00446   if (blend->_operand_a == EggGroup::BO_one && blend->_operand_b == EggGroup::BO_zero) {
00447     blend->_enabled = false;
00448   }
00449   return blend;
00450 }
 All Classes Functions Variables Enumerations