Panda3D
Loading...
Searching...
No Matches
daeMaterials.cxx
Go to the documentation of this file.
1/**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file daeMaterials.cxx
10 * @author rdb
11 * @date 2008-10-03
12 */
13
14#include "daeMaterials.h"
15#include "config_daeegg.h"
16#include "fcollada_utils.h"
17
18#include <FCDocument/FCDocument.h>
19#include <FCDocument/FCDMaterial.h>
20#include <FCDocument/FCDEffect.h>
21#include <FCDocument/FCDTexture.h>
22#include <FCDocument/FCDEffectParameterSampler.h>
23#include <FCDocument/FCDImage.h>
24
25#include "filename.h"
26#include "string_utils.h"
27
28using std::endl;
29using std::string;
30
31TypeHandle DaeMaterials::_type_handle;
32
33// luminance function, based on the ISOCIE color standards see ITU-R
34// Recommendation BT.709-4
35#define luminance(c) ((c[0] * 0.212671 + c[1] * 0.715160 + c[2] * 0.072169))
36
37/**
38 *
39 */
40DaeMaterials::
41DaeMaterials(const FCDGeometryInstance* geometry_instance) {
42 for (size_t mi = 0; mi < geometry_instance->GetMaterialInstanceCount(); ++mi) {
43 add_material_instance(geometry_instance->GetMaterialInstance(mi));
44 }
45}
46
47/**
48 * Adds a material instance. Normally automatically done by constructor.
49 */
50void DaeMaterials::add_material_instance(const FCDMaterialInstance* instance) {
51 nassertv(instance != nullptr);
52 const string semantic (FROM_FSTRING(instance->GetSemantic()));
53 if (_materials.count(semantic) > 0) {
54 daeegg_cat.warning() << "Ignoring duplicate material with semantic " << semantic << endl;
55 return;
56 }
57 _materials[semantic] = new DaeMaterial();
58
59 // Load in the uvsets
60 for (size_t vib = 0; vib < instance->GetVertexInputBindingCount(); ++vib) {
61 const FCDMaterialInstanceBindVertexInput* mivib = instance->GetVertexInputBinding(vib);
62 assert(mivib != nullptr);
63 PT(DaeVertexInputBinding) bvi = new DaeVertexInputBinding();
64 bvi->_input_set = mivib->inputSet;
65#if FCOLLADA_VERSION >= 0x00030005
66 bvi->_input_semantic = mivib->GetInputSemantic();
67 bvi->_semantic = *mivib->semantic;
68#else
69 bvi->_input_semantic = mivib->inputSemantic;
70 bvi->_semantic = FROM_FSTRING(mivib->semantic);
71#endif
72 _materials[semantic]->_uvsets.push_back(bvi);
73 }
74
75 // Handle the material stuff
76 daeegg_cat.spam() << "Trying to process material with semantic " << semantic << endl;
77 PT_EggMaterial egg_material = new EggMaterial(semantic);
78 pvector<PT_EggTexture> egg_textures;
79 const FCDEffect* effect = instance->GetMaterial()->GetEffect();
80 if (effect == nullptr) {
81 daeegg_cat.debug() << "Ignoring material (semantic: " << semantic << ") without assigned effect" << endl;
82 } else {
83 // Grab the common profile effect
84 const FCDEffectStandard* effect_common = (FCDEffectStandard *)effect->FindProfile(FUDaeProfileType::COMMON);
85 if (effect_common == nullptr) {
86 daeegg_cat.info() << "Ignoring effect referenced by material with semantic " << semantic
87 << " because it has no common profile" << endl;
88 } else {
89 daeegg_cat.spam() << "Processing effect, material semantic is " << semantic << endl;
90 // Set the material parameters
91 egg_material->set_amb(TO_COLOR(effect_common->GetAmbientColor()));
92 // We already process transparency using blend modes LVecBase4 diffuse =
93 // TO_COLOR(effect_common->GetDiffuseColor());
94 // diffuse.set_w(diffuse.get_w() * (1.0f -
95 // effect_common->GetOpacity())); egg_material->set_diff(diffuse);
96 egg_material->set_diff(TO_COLOR(effect_common->GetDiffuseColor()));
97 egg_material->set_emit(TO_COLOR(effect_common->GetEmissionColor()) * effect_common->GetEmissionFactor());
98 egg_material->set_shininess(effect_common->GetShininess());
99 egg_material->set_spec(TO_COLOR(effect_common->GetSpecularColor()));
100 // Now try to load in the textures
101 process_texture_bucket(semantic, effect_common, FUDaeTextureChannel::DIFFUSE, EggTexture::ET_modulate);
102 process_texture_bucket(semantic, effect_common, FUDaeTextureChannel::BUMP, EggTexture::ET_normal);
103 process_texture_bucket(semantic, effect_common, FUDaeTextureChannel::SPECULAR, EggTexture::ET_modulate_gloss);
104 process_texture_bucket(semantic, effect_common, FUDaeTextureChannel::SPECULAR_LEVEL, EggTexture::ET_gloss);
105 process_texture_bucket(semantic, effect_common, FUDaeTextureChannel::TRANSPARENT, EggTexture::ET_unspecified, EggTexture::F_alpha);
106 process_texture_bucket(semantic, effect_common, FUDaeTextureChannel::EMISSION, EggTexture::ET_add);
107#if FCOLLADA_VERSION < 0x00030005
108 process_texture_bucket(semantic, effect_common, FUDaeTextureChannel::OPACITY, EggTexture::ET_unspecified, EggTexture::F_alpha);
109#endif
110 // Now, calculate the color blend stuff.
111 _materials[semantic]->_blend = convert_blend(effect_common->GetTransparencyMode(),
112 TO_COLOR(effect_common->GetTranslucencyColor()),
113 effect_common->GetTranslucencyFactor());
114 }
115 // Find an <extra> tag to support some extra stuff from extensions
116 process_extra(semantic, effect->GetExtra());
117 }
118 daeegg_cat.spam() << "Found " << egg_textures.size() << " textures in material" << endl;
119 _materials[semantic]->_egg_material = egg_material;
120}
121
122/**
123 * Processes the given texture bucket and gives the textures in it the given
124 * envtype and format.
125 */
126void DaeMaterials::
127process_texture_bucket(const string semantic, const FCDEffectStandard* effect_common, FUDaeTextureChannel::Channel bucket, EggTexture::EnvType envtype, EggTexture::Format format) {
128 for (size_t tx = 0; tx < effect_common->GetTextureCount(bucket); ++tx) {
129 const FCDImage* image = effect_common->GetTexture(bucket, tx)->GetImage();
130 if (image == nullptr) {
131 daeegg_cat.warning() << "Texture references a nonexisting image!" << endl;
132 } else {
133 const FCDEffectParameterSampler* sampler = effect_common->GetTexture(bucket, tx)->GetSampler();
134 // FCollada only supplies absolute paths. We need to grab the document
135 // location ourselves and make the image path absolute.
136 Filename texpath;
137 if (image->GetDocument()) {
138 Filename docpath = Filename::from_os_specific(FROM_FSTRING(image->GetDocument()->GetFileUrl()));
139 docpath.make_canonical();
140 texpath = Filename::from_os_specific(FROM_FSTRING(image->GetFilename()));
141 texpath.make_canonical();
142 texpath.make_relative_to(docpath.get_dirname(), true);
143 daeegg_cat.debug() << "Found texture with path " << texpath << endl;
144 } else {
145 // Never mind.
146 texpath = Filename::from_os_specific(FROM_FSTRING(image->GetFilename()));
147 }
148 PT_EggTexture egg_texture = new EggTexture(FROM_FSTRING(image->GetDaeId()), texpath.to_os_generic());
149 // Find a set of UV coordinates
150 const FCDEffectParameterInt* uvset = effect_common->GetTexture(bucket, tx)->GetSet();
151 if (uvset != nullptr) {
152 daeegg_cat.debug() << "Texture has uv name '" << FROM_FSTRING(uvset->GetSemantic()) << "'\n";
153 string uvset_semantic (FROM_FSTRING(uvset->GetSemantic()));
154
155 // Only set the UV name if this UV set actually exists.
156 for (size_t i = 0; i < _materials[semantic]->_uvsets.size(); ++i) {
157 if (_materials[semantic]->_uvsets[i]->_semantic == uvset_semantic) {
158 egg_texture->set_uv_name(uvset_semantic);
159 break;
160 }
161 }
162 }
163 // Apply sampler stuff
164 if (sampler != nullptr) {
165 egg_texture->set_texture_type(convert_texture_type(sampler->GetSamplerType()));
166 egg_texture->set_wrap_u(convert_wrap_mode(sampler->GetWrapS()));
167 if (sampler->GetSamplerType() != FCDEffectParameterSampler::SAMPLER1D) {
168 egg_texture->set_wrap_v(convert_wrap_mode(sampler->GetWrapT()));
169 }
170 if (sampler->GetSamplerType() == FCDEffectParameterSampler::SAMPLER3D) {
171 egg_texture->set_wrap_w(convert_wrap_mode(sampler->GetWrapP()));
172 }
173 egg_texture->set_minfilter(convert_filter_type(sampler->GetMinFilter()));
174 egg_texture->set_magfilter(convert_filter_type(sampler->GetMagFilter()));
175 if (envtype != EggTexture::ET_unspecified) {
176 egg_texture->set_env_type(envtype);
177 }
178 if (format != EggTexture::F_unspecified) {
179 egg_texture->set_format(format);
180 }
181 }
182 _materials[semantic]->_egg_textures.push_back(egg_texture);
183 }
184 }
185}
186
187/**
188 * Processes the extra data in the given <extra> tag. If the given element is
189 * NULL, it just silently returns.
190 */
191void DaeMaterials::
192process_extra(const string semantic, const FCDExtra* extra) {
193 if (extra == nullptr) return;
194 const FCDEType* etype = extra->GetDefaultType();
195 if (etype == nullptr) return;
196 for (size_t et = 0; et < etype->GetTechniqueCount(); ++et) {
197 const FCDENode* enode = ((const FCDENode*)(etype->GetTechnique(et)))->FindChildNode("double_sided");
198 if (enode != nullptr) {
199 string content = trim(enode->GetContent());
200 if (content == "1" || content == "true") {
201 _materials[semantic]->_double_sided = true;
202 } else if (content == "0" || content == "false") {
203 _materials[semantic]->_double_sided = false;
204 } else {
205 daeegg_cat.warning() << "Expected <double_sided> tag to be either 1 or 0, found '" << content << "' instead" << endl;
206 }
207 }
208 }
209}
210
211/**
212 * Applies the stuff to the given EggPrimitive.
213 */
215apply_to_primitive(const string semantic, const PT(EggPrimitive) to) {
216 if (_materials.count(semantic) > 0) {
217 to->set_material(_materials[semantic]->_egg_material);
218 for (pvector<PT_EggTexture>::iterator it = _materials[semantic]->_egg_textures.begin(); it != _materials[semantic]->_egg_textures.end(); ++it) {
219 daeegg_cat.spam() << "Applying texture " << (*it)->get_name() << " from material with semantic " << semantic << endl;
220 to->add_texture(*it);
221 }
222 to->set_bface_flag(_materials[semantic]->_double_sided);
223 }
224}
225
226/**
227 * Applies the colorblend stuff to the given EggGroup.
228 */
230apply_to_group(const string semantic, const PT(EggGroup) to, bool invert_transparency) {
231 if (_materials.count(semantic) > 0) {
232 PT(DaeBlendSettings) blend = _materials[semantic]->_blend;
233 if (blend && blend->_enabled) {
234 to->set_blend_mode(EggGroup::BM_add);
235 to->set_blend_color(blend->_color);
236 if (invert_transparency) {
237 to->set_blend_operand_a(blend->_operand_b);
238 to->set_blend_operand_b(blend->_operand_a);
239 } else {
240 to->set_blend_operand_a(blend->_operand_a);
241 to->set_blend_operand_b(blend->_operand_b);
242 }
243 } else if (blend && invert_transparency) {
244 to->set_blend_mode(EggGroup::BM_add);
245 to->set_blend_color(blend->_color);
246 to->set_blend_operand_a(blend->_operand_b);
247 to->set_blend_operand_b(blend->_operand_a);
248 }
249 }
250}
251
252/**
253 * Returns the semantic of the uvset with the specified input set, or an empty
254 * string if the given material has no input set.
255 */
256const string DaeMaterials::
257get_uvset_name(const string semantic, FUDaeGeometryInput::Semantic input_semantic, int32 input_set) {
258 if (_materials.count(semantic) > 0) {
259 if (input_set == -1 && _materials[semantic]->_uvsets.size() == 1) {
260 return _materials[semantic]->_uvsets[0]->_semantic;
261 } else {
262 for (size_t i = 0; i < _materials[semantic]->_uvsets.size(); ++i) {
263 if (_materials[semantic]->_uvsets[i]->_input_set == input_set &&
264 _materials[semantic]->_uvsets[i]->_input_semantic == input_semantic) {
265 return _materials[semantic]->_uvsets[i]->_semantic;
266 }
267 }
268 // If we can't find it, let's look again, but don't care for the
269 // input_semantic this time. The reason for this is that some tools
270 // export textangents and texbinormals bound to a uvset with input
271 // semantic TEXCOORD.
272 for (size_t i = 0; i < _materials[semantic]->_uvsets.size(); ++i) {
273 if (_materials[semantic]->_uvsets[i]->_input_set == input_set) {
274 daeegg_cat.debug() << "Using uv set with non-matching input semantic " << _materials[semantic]->_uvsets[i]->_semantic << "\n";
275 return _materials[semantic]->_uvsets[i]->_semantic;
276 }
277 }
278 daeegg_cat.debug() << "No uv set binding found for input set " << input_set << "\n";
279 }
280 }
281 return "";
282}
283
284/**
285 * Converts an FCollada sampler type to the EggTexture texture type
286 * equivalent.
287 */
288EggTexture::TextureType DaeMaterials::
289convert_texture_type(const FCDEffectParameterSampler::SamplerType orig_type) {
290 switch (orig_type) {
291 case FCDEffectParameterSampler::SAMPLER1D:
292 return EggTexture::TT_1d_texture;
293 case FCDEffectParameterSampler::SAMPLER2D:
294 return EggTexture::TT_2d_texture;
295 case FCDEffectParameterSampler::SAMPLER3D:
296 return EggTexture::TT_3d_texture;
297 case FCDEffectParameterSampler::SAMPLERCUBE:
298 return EggTexture::TT_cube_map;
299 default:
300 daeegg_cat.warning() << "Invalid sampler type found" << endl;
301 }
302 return EggTexture::TT_unspecified;
303}
304
305/**
306 * Converts an FCollada wrap mode to the EggTexture wrap mode equivalent.
307 */
308EggTexture::WrapMode DaeMaterials::
309convert_wrap_mode(const FUDaeTextureWrapMode::WrapMode orig_mode) {
310 switch (orig_mode) {
311 case FUDaeTextureWrapMode::NONE:
312 // FIXME: this shouldnt be unspecified
313 return EggTexture::WM_unspecified;
314 case FUDaeTextureWrapMode::WRAP:
315 return EggTexture::WM_repeat;
316 case FUDaeTextureWrapMode::MIRROR:
317 return EggTexture::WM_mirror;
318 case FUDaeTextureWrapMode::CLAMP:
319 return EggTexture::WM_clamp;
320 case FUDaeTextureWrapMode::BORDER:
321 return EggTexture::WM_border_color;
322 case FUDaeTextureWrapMode::UNKNOWN:
323 return EggTexture::WM_unspecified;
324 default:
325 daeegg_cat.warning() << "Invalid wrap mode found: " << FUDaeTextureWrapMode::ToString(orig_mode) << endl;
326 }
327 return EggTexture::WM_unspecified;
328}
329
330/**
331 * Converts an FCollada filter function to the EggTexture wrap type
332 * equivalent.
333 */
334EggTexture::FilterType DaeMaterials::
335convert_filter_type(const FUDaeTextureFilterFunction::FilterFunction orig_type) {
336 switch (orig_type) {
337 case FUDaeTextureFilterFunction::NONE:
338 // FIXME: this shouldnt be unspecified
339 return EggTexture::FT_unspecified;
340 case FUDaeTextureFilterFunction::NEAREST:
341 return EggTexture::FT_nearest;
342 case FUDaeTextureFilterFunction::LINEAR:
343 return EggTexture::FT_linear;
344 case FUDaeTextureFilterFunction::NEAREST_MIPMAP_NEAREST:
345 return EggTexture::FT_nearest_mipmap_nearest;
346 case FUDaeTextureFilterFunction::LINEAR_MIPMAP_NEAREST:
347 return EggTexture::FT_linear_mipmap_nearest;
348 case FUDaeTextureFilterFunction::NEAREST_MIPMAP_LINEAR:
349 return EggTexture::FT_nearest_mipmap_linear;
350 case FUDaeTextureFilterFunction::LINEAR_MIPMAP_LINEAR:
351 return EggTexture::FT_linear_mipmap_linear;
352 case FUDaeTextureFilterFunction::UNKNOWN:
353 return EggTexture::FT_unspecified;
354 default:
355 daeegg_cat.warning() << "Unknown filter type found: " << FUDaeTextureFilterFunction::ToString(orig_type) << endl;
356 }
357 return EggTexture::FT_unspecified;
358}
359
360/**
361 * Converts collada blend attribs to Panda's equivalents.
362 */
363PT(DaeMaterials::DaeBlendSettings) DaeMaterials::
364convert_blend(FCDEffectStandard::TransparencyMode mode, const LColor &transparent, double transparency) {
365 // Create the DaeBlendSettings and fill it with some defaults.
366 PT(DaeBlendSettings) blend = new DaeBlendSettings();
367 blend->_enabled = true;
368 blend->_color = LColor::zero();
369 blend->_operand_a = EggGroup::BO_unspecified;
370 blend->_operand_b = EggGroup::BO_unspecified;
371
372 // First fill in the color value.
373 if (mode == FCDEffectStandard::A_ONE) {// || mode == FCDEffectStandard::A_ZERO) {
374 double value = transparent[3] * transparency;
375 blend->_color = LColor(value, value, value, value);
376 } else if (mode == FCDEffectStandard::RGB_ZERO) {//|| mode == FCDEffectStandard::RGB_ONE) {
377 blend->_color = transparent * transparency;
378 blend->_color[3] = luminance(blend->_color);
379 } else {
380 daeegg_cat.error() << "Unknown opaque type found!" << endl;
381 blend->_enabled = false;
382 return blend;
383 }
384
385 // Now figure out the operands.
386 if (mode == FCDEffectStandard::RGB_ZERO) {// || mode == FCDEffectStandard::A_ZERO) {
387 blend->_operand_a = EggGroup::BO_one_minus_constant_color;
388 blend->_operand_b = EggGroup::BO_constant_color;
389 } else if (mode == FCDEffectStandard::A_ONE) {// || mode == FCDEffectStandard::RGB_ONE) {
390 blend->_operand_a = EggGroup::BO_constant_color;
391 blend->_operand_b = EggGroup::BO_one_minus_constant_color;
392 } else {
393 daeegg_cat.error() << "Unknown opaque type found!" << endl;
394 blend->_enabled = false;
395 return blend;
396 }
397
398 // See if we can optimize out the color.
399 if (blend->_operand_a == EggGroup::BO_constant_color) {
400 if (blend->_color == LColor::zero()) {
401 blend->_operand_a = EggGroup::BO_zero;
402 } else if (blend->_color == LColor(1, 1, 1, 1)) {
403 blend->_operand_a = EggGroup::BO_one;
404 }
405 }
406 if (blend->_operand_b == EggGroup::BO_constant_color) {
407 if (blend->_color == LColor::zero()) {
408 blend->_operand_b = EggGroup::BO_zero;
409 } else if (blend->_color == LColor(1, 1, 1, 1)) {
410 blend->_operand_b = EggGroup::BO_one;
411 }
412 }
413 if (blend->_operand_a == EggGroup::BO_one_minus_constant_color) {
414 if (blend->_color == LColor::zero()) {
415 blend->_operand_a = EggGroup::BO_one;
416 } else if (blend->_color == LColor(1, 1, 1, 1)) {
417 blend->_operand_a = EggGroup::BO_zero;
418 }
419 }
420 if (blend->_operand_b == EggGroup::BO_one_minus_constant_color) {
421 if (blend->_color == LColor::zero()) {
422 blend->_operand_b = EggGroup::BO_one;
423 } else if (blend->_color == LColor(1, 1, 1, 1)) {
424 blend->_operand_b = EggGroup::BO_zero;
425 }
426 }
427
428 // See if we can entirely disable the blend.
429 if (blend->_operand_a == EggGroup::BO_one && blend->_operand_b == EggGroup::BO_zero) {
430 blend->_enabled = false;
431 }
432 return blend;
433}
static EggTexture::TextureType convert_texture_type(const FCDEffectParameterSampler::SamplerType orig_type)
Converts an FCollada sampler type to the EggTexture texture type equivalent.
void add_material_instance(const FCDMaterialInstance *instance)
Adds a material instance.
static EggTexture::WrapMode convert_wrap_mode(const FUDaeTextureWrapMode::WrapMode orig_mode)
Converts an FCollada wrap mode to the EggTexture wrap mode equivalent.
const std::string get_uvset_name(const std::string semantic, FUDaeGeometryInput::Semantic input_semantic, int32 input_set)
Returns the semantic of the uvset with the specified input set, or an empty string if the given mater...
void apply_to_group(const std::string semantic, const PT(EggGroup) to, bool invert_transparency=false)
Applies the colorblend stuff to the given EggGroup.
void apply_to_primitive(const std::string semantic, const PT(EggPrimitive) to)
Applies the stuff to the given EggPrimitive.
static EggTexture::FilterType convert_filter_type(const FUDaeTextureFilterFunction::FilterFunction orig_type)
Converts an FCollada filter function to the EggTexture wrap type equivalent.
The main glue of the egg hierarchy, this corresponds to the <Group>, <Instance>, and <Joint> type nod...
Definition eggGroup.h:34
A base class for any of a number of kinds of geometry primitives: polygons, point lights,...
set_bface_flag
Sets the backfacing flag of the polygon.
set_material
Applies the indicated material to the primitive.
void add_texture(EggTexture *texture)
Applies the indicated texture to the primitive.
Defines a texture map that may be applied to geometry.
Definition eggTexture.h:30
The name of a file, such as a texture file or an Egg file.
Definition filename.h:44
bool make_canonical()
Converts this filename to a canonical name by replacing the directory part with the fully-qualified d...
static Filename from_os_specific(const std::string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes,...
Definition filename.cxx:328
std::string to_os_generic() const
This is similar to to_os_specific(), but it is designed to generate a filename that can be understood...
bool make_relative_to(Filename directory, bool allow_backups=true)
Adjusts this filename, which must be a fully-specified pathname beginning with a slash,...
std::string get_dirname() const
Returns the directory part of the filename.
Definition filename.I:358
TypeHandle is the identifier used to differentiate C++ class types.
Definition typeHandle.h:81
This is our own Panda specialization on the default STL vector.
Definition pvector.h:42
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
string trim(const string &str)
Returns a new string representing the contents of the given string with both leading and trailing whi...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.