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       //LVecBase4f 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         egg_texture->set_uv_name(FROM_FSTRING(uvset->GetSemantic()));
00159       }
00160       // Apply sampler stuff
00161       if (sampler != NULL) {
00162         egg_texture->set_texture_type(convert_texture_type(sampler->GetSamplerType()));
00163         egg_texture->set_wrap_u(convert_wrap_mode(sampler->GetWrapS()));
00164         if (sampler->GetSamplerType() != FCDEffectParameterSampler::SAMPLER1D) {
00165           egg_texture->set_wrap_v(convert_wrap_mode(sampler->GetWrapT()));
00166         }
00167         if (sampler->GetSamplerType() == FCDEffectParameterSampler::SAMPLER3D) {
00168           egg_texture->set_wrap_w(convert_wrap_mode(sampler->GetWrapP()));
00169         }
00170         egg_texture->set_minfilter(convert_filter_type(sampler->GetMinFilter()));
00171         egg_texture->set_magfilter(convert_filter_type(sampler->GetMagFilter()));
00172         if (envtype != EggTexture::ET_unspecified) {
00173           egg_texture->set_env_type(envtype);
00174         }
00175         if (format != EggTexture::F_unspecified) {
00176           egg_texture->set_format(format);
00177         }
00178       }
00179       _materials[semantic]->_egg_textures.push_back(egg_texture);
00180     }
00181   }
00182 }
00183 
00184 ////////////////////////////////////////////////////////////////////
00185 //     Function: DaeMaterials::process_extra
00186 //       Access: Private
00187 //  Description: Processes the extra data in the given <extra> tag.
00188 //               If the given element is NULL, it just silently
00189 //               returns.
00190 ////////////////////////////////////////////////////////////////////
00191 void DaeMaterials::
00192 process_extra(const string semantic, const FCDExtra* extra) {
00193   if (extra == NULL) return;
00194   const FCDEType* etype = extra->GetDefaultType();
00195   if (etype == NULL) return;
00196   for (size_t et = 0; et < etype->GetTechniqueCount(); ++et) {
00197     const FCDENode* enode = ((const FCDENode*)(etype->GetTechnique(et)))->FindChildNode("double_sided");
00198     if (enode != NULL) {
00199       if (trim(enode->GetContent()) == "1") {
00200         _materials[semantic]->_double_sided = true;
00201       } else if (trim(enode->GetContent()) == "0") {
00202         _materials[semantic]->_double_sided = false;
00203       } else {
00204         daeegg_cat.warning() << "Expected <double_sided> tag to be either 1 or 0, found '" << enode->GetContent() << "' instead" << endl;
00205       }
00206     }
00207   }
00208 }
00209 
00210 ////////////////////////////////////////////////////////////////////
00211 //     Function: DaeMaterials::apply_to
00212 //       Access: Public
00213 //  Description: Applies the stuff to the given EggPrimitive.
00214 ////////////////////////////////////////////////////////////////////
00215 void DaeMaterials::
00216 apply_to(const string semantic, const PT(EggPrimitive) to) {
00217   if (_materials.count(semantic) > 0) {
00218     to->set_material(_materials[semantic]->_egg_material);
00219     for (pvector<PT_EggTexture>::iterator it = _materials[semantic]->_egg_textures.begin(); it != _materials[semantic]->_egg_textures.end(); ++it) {
00220       daeegg_cat.spam() << "Applying texture " << (*it)->get_name() << " from material with semantic " << semantic << endl;
00221       to->add_texture(*it);
00222     }
00223     to->set_bface_flag(_materials[semantic]->_double_sided);
00224   }
00225 }
00226 
00227 ////////////////////////////////////////////////////////////////////
00228 //     Function: DaeMaterials::apply_to
00229 //       Access: Public
00230 //  Description: Applies the colorblend stuff to the given EggGroup.
00231 ////////////////////////////////////////////////////////////////////
00232 void DaeMaterials::
00233 apply_to(const string semantic, const PT(EggGroup) to) {
00234   if (_materials.count(semantic) > 0) {
00235     PT(DaeBlendSettings) blend = _materials[semantic]->_blend;
00236     if (blend && blend->_enabled) {
00237       to->set_blend_mode(EggGroup::BM_add);
00238       to->set_blend_color(blend->_color);
00239       to->set_blend_operand_a(blend->_operand_a);
00240       to->set_blend_operand_b(blend->_operand_b);
00241     }
00242   }
00243 }
00244 
00245 ////////////////////////////////////////////////////////////////////
00246 //     Function: DaeMaterials::get_uvset_name
00247 //       Access: Public
00248 //  Description: Returns the semantic of the uvset with the
00249 //               specified input set, or an empty string if the
00250 //               given material has no input set.
00251 ////////////////////////////////////////////////////////////////////
00252 const string DaeMaterials::
00253 get_uvset_name(const string semantic, FUDaeGeometryInput::Semantic input_semantic, int32 input_set) {
00254   if (_materials.count(semantic) > 0) {
00255     if (input_set == -1 && _materials[semantic]->_uvsets.size() == 1) {
00256       return _materials[semantic]->_uvsets[0]->_semantic;
00257     } else {
00258       for (int i = 0; i < _materials[semantic]->_uvsets.size(); ++i) {
00259         if (_materials[semantic]->_uvsets[i]->_input_set == input_set &&
00260             _materials[semantic]->_uvsets[i]->_input_semantic == input_semantic) {
00261           return _materials[semantic]->_uvsets[i]->_semantic;
00262         }
00263       }
00264       // If we can't find it, let's look again, but don't care for the
00265       // input_semantic this time. The reason for this is that some tools
00266       // export textangents and texbinormals bound to a uvset with input
00267       // semantic TEXCOORD.
00268       for (int i = 0; i < _materials[semantic]->_uvsets.size(); ++i) {
00269         if (_materials[semantic]->_uvsets[i]->_input_set == input_set) {
00270           daeegg_cat.debug() << "Using uv set with non-matching input semantic " << _materials[semantic]->_uvsets[i]->_semantic << "\n";
00271           return _materials[semantic]->_uvsets[i]->_semantic;
00272         }
00273       }
00274       daeegg_cat.debug() << "No uv set binding found for input set " << input_set << "\n";
00275     }
00276   }
00277   return "";
00278 }
00279 
00280 ////////////////////////////////////////////////////////////////////
00281 //     Function: DaeMaterials::convert_texture_type
00282 //       Access: Public, Static
00283 //  Description: Converts an FCollada sampler type to the EggTexture
00284 //               texture type equivalent.
00285 ////////////////////////////////////////////////////////////////////
00286 EggTexture::TextureType DaeMaterials::
00287 convert_texture_type(const FCDEffectParameterSampler::SamplerType orig_type) {
00288   switch (orig_type) {
00289     case FCDEffectParameterSampler::SAMPLER1D:
00290       return EggTexture::TT_1d_texture;
00291     case FCDEffectParameterSampler::SAMPLER2D:
00292       return EggTexture::TT_2d_texture;
00293     case FCDEffectParameterSampler::SAMPLER3D:
00294       return EggTexture::TT_3d_texture;
00295     case FCDEffectParameterSampler::SAMPLERCUBE:
00296       return EggTexture::TT_cube_map;
00297     default:
00298       daeegg_cat.warning() << "Invalid sampler type found" << endl;
00299   }
00300   return EggTexture::TT_unspecified;
00301 }
00302 
00303 ////////////////////////////////////////////////////////////////////
00304 //     Function: DaeMaterials::convert_wrap_mode
00305 //       Access: Public, Static
00306 //  Description: Converts an FCollada wrap mode to the
00307 //               EggTexture wrap mode equivalent.
00308 ////////////////////////////////////////////////////////////////////
00309 EggTexture::WrapMode DaeMaterials::
00310 convert_wrap_mode(const FUDaeTextureWrapMode::WrapMode orig_mode) {
00311   switch (orig_mode) {
00312     case FUDaeTextureWrapMode::NONE:
00313       //FIXME: this shouldnt be unspecified
00314       return EggTexture::WM_unspecified;
00315     case FUDaeTextureWrapMode::WRAP:
00316       return EggTexture::WM_repeat;
00317     case FUDaeTextureWrapMode::MIRROR:
00318       return EggTexture::WM_mirror;
00319     case FUDaeTextureWrapMode::CLAMP:
00320       return EggTexture::WM_clamp;
00321     case FUDaeTextureWrapMode::BORDER:
00322       return EggTexture::WM_border_color;
00323     case FUDaeTextureWrapMode::UNKNOWN:
00324       return EggTexture::WM_unspecified;
00325     default:
00326       daeegg_cat.warning() << "Invalid wrap mode found: " << FUDaeTextureWrapMode::ToString(orig_mode) << endl;
00327   }
00328   return EggTexture::WM_unspecified;
00329 }
00330 
00331 ////////////////////////////////////////////////////////////////////
00332 //     Function: DaeMaterials::convert_filter_type
00333 //       Access: Public, Static
00334 //  Description: Converts an FCollada filter function to the
00335 //               EggTexture wrap type equivalent.
00336 ////////////////////////////////////////////////////////////////////
00337 EggTexture::FilterType DaeMaterials::
00338 convert_filter_type(const FUDaeTextureFilterFunction::FilterFunction orig_type) {
00339   switch (orig_type) {
00340     case FUDaeTextureFilterFunction::NONE:
00341       //FIXME: this shouldnt be unspecified
00342       return EggTexture::FT_unspecified;
00343     case FUDaeTextureFilterFunction::NEAREST:
00344       return EggTexture::FT_nearest;
00345     case FUDaeTextureFilterFunction::LINEAR:
00346       return EggTexture::FT_linear;
00347     case FUDaeTextureFilterFunction::NEAREST_MIPMAP_NEAREST:
00348       return EggTexture::FT_nearest_mipmap_nearest;
00349     case FUDaeTextureFilterFunction::LINEAR_MIPMAP_NEAREST:
00350       return EggTexture::FT_linear_mipmap_nearest;
00351     case FUDaeTextureFilterFunction::NEAREST_MIPMAP_LINEAR:
00352       return EggTexture::FT_nearest_mipmap_linear;
00353     case FUDaeTextureFilterFunction::LINEAR_MIPMAP_LINEAR:
00354       return EggTexture::FT_linear_mipmap_linear;
00355     case FUDaeTextureFilterFunction::UNKNOWN:
00356       return EggTexture::FT_unspecified;
00357     default:
00358       daeegg_cat.warning() << "Unknown filter type found: " << FUDaeTextureFilterFunction::ToString(orig_type) << endl;
00359   }
00360   return EggTexture::FT_unspecified;
00361 }
00362 
00363 ////////////////////////////////////////////////////////////////////
00364 //     Function: DaeMaterials::convert_blend
00365 //       Access: Private, Static
00366 //  Description: Converts collada blend attribs to Panda's equivalents.
00367 ////////////////////////////////////////////////////////////////////
00368 PT(DaeMaterials::DaeBlendSettings) DaeMaterials::
00369 convert_blend(FCDEffectStandard::TransparencyMode mode, Colorf transparent, double transparency) {
00370   // Create the DaeBlendSettings and fill it with some defaults.
00371   PT(DaeBlendSettings) blend = new DaeBlendSettings();
00372   blend->_enabled = true;
00373   blend->_color = Colorf::zero();
00374   blend->_operand_a = EggGroup::BO_unspecified;
00375   blend->_operand_b = EggGroup::BO_unspecified;
00376   
00377   // First fill in the color value.
00378   if (mode == FCDEffectStandard::A_ONE) {// || mode == FCDEffectStandard::A_ZERO) {
00379     double value = transparent[3] * transparency;
00380     blend->_color = Colorf(value, value, value, value);
00381   } else if (mode == FCDEffectStandard::RGB_ZERO) {//|| mode == FCDEffectStandard::RGB_ONE) {
00382     blend->_color = transparent * transparency;
00383     blend->_color[3] = luminance(blend->_color);
00384   } else {
00385     daeegg_cat.error() << "Unknown opaque type found!" << endl;
00386     blend->_enabled = false;
00387     return blend;
00388   }
00389   
00390   // Now figure out the operands.
00391   if (mode == FCDEffectStandard::RGB_ZERO) {// || mode == FCDEffectStandard::A_ZERO) {
00392     blend->_operand_a = EggGroup::BO_one_minus_constant_color;
00393     blend->_operand_b = EggGroup::BO_constant_color;
00394   } else if (mode == FCDEffectStandard::A_ONE) {// || mode == FCDEffectStandard::RGB_ONE) {
00395     blend->_operand_a = EggGroup::BO_constant_color;
00396     blend->_operand_b = EggGroup::BO_one_minus_constant_color;
00397   } else {
00398     daeegg_cat.error() << "Unknown opaque type found!" << endl;
00399     blend->_enabled = false;
00400     return blend;
00401   }
00402   
00403   // See if we can optimize out the color.
00404   if (blend->_operand_a == EggGroup::BO_constant_color) {
00405     if ((blend->_color[0] == 0) && (blend->_color[1] == 0) && (blend->_color[2] == 0) && (blend->_color[3] == 0)) {
00406       blend->_operand_a = EggGroup::BO_zero;
00407     }
00408     if ((blend->_color[0] == 1) && (blend->_color[1] == 1) && (blend->_color[2] == 1) && (blend->_color[3] == 1)) {
00409       blend->_operand_a = EggGroup::BO_one;
00410     }
00411   }
00412   if (blend->_operand_b == 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_b = 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_b = EggGroup::BO_one;
00418     }
00419   }
00420   if (blend->_operand_a == EggGroup::BO_one_minus_constant_color) {
00421     if ((blend->_color[0] == 0) && (blend->_color[1] == 0) && (blend->_color[2] == 0) && (blend->_color[3] == 0)) {
00422       blend->_operand_a = EggGroup::BO_one;
00423     }
00424     if ((blend->_color[0] == 1) && (blend->_color[1] == 1) && (blend->_color[2] == 1) && (blend->_color[3] == 1)) {
00425       blend->_operand_a = EggGroup::BO_zero;
00426     }
00427   }
00428   if (blend->_operand_b == 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_b = 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_b = EggGroup::BO_zero;
00434     }
00435   }
00436   
00437   // See if we can entirely disable the blend.
00438   if (blend->_operand_a == EggGroup::BO_one && blend->_operand_b == EggGroup::BO_zero) {
00439     blend->_enabled = false;
00440   }
00441   return blend;
00442 }
 All Classes Functions Variables Enumerations