00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
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
00032
00033 #define luminance(c) ((c[0] * 0.212671 + c[1] * 0.715160 + c[2] * 0.072169))
00034
00035
00036
00037
00038
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
00049
00050
00051
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
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
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
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
00094 egg_material->set_amb(TO_COLOR(effect_common->GetAmbientColor()));
00095
00096
00097
00098
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
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
00114 _materials[semantic]->_blend = convert_blend(effect_common->GetTransparencyMode(),
00115 TO_COLOR(effect_common->GetTranslucencyColor()),
00116 effect_common->GetTranslucencyFactor());
00117 }
00118
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
00127
00128
00129
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
00140
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
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
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
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
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
00194
00195
00196
00197
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
00220
00221
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
00237
00238
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
00255
00256
00257
00258
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
00273
00274
00275
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
00290
00291
00292
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
00313
00314
00315
00316
00317 EggTexture::WrapMode DaeMaterials::
00318 convert_wrap_mode(const FUDaeTextureWrapMode::WrapMode orig_mode) {
00319 switch (orig_mode) {
00320 case FUDaeTextureWrapMode::NONE:
00321
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
00341
00342
00343
00344
00345 EggTexture::FilterType DaeMaterials::
00346 convert_filter_type(const FUDaeTextureFilterFunction::FilterFunction orig_type) {
00347 switch (orig_type) {
00348 case FUDaeTextureFilterFunction::NONE:
00349
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
00373
00374
00375
00376 PT(DaeMaterials::DaeBlendSettings) DaeMaterials::
00377 convert_blend(FCDEffectStandard::TransparencyMode mode, const LColor &transparent, double transparency) {
00378
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
00386 if (mode == FCDEffectStandard::A_ONE) {
00387 double value = transparent[3] * transparency;
00388 blend->_color = LColor(value, value, value, value);
00389 } else if (mode == FCDEffectStandard::RGB_ZERO) {
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
00399 if (mode == FCDEffectStandard::RGB_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) {
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
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
00446 if (blend->_operand_a == EggGroup::BO_one && blend->_operand_b == EggGroup::BO_zero) {
00447 blend->_enabled = false;
00448 }
00449 return blend;
00450 }