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