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