collada: export UV Textures as materials. Note: the reimport of the exported collada...
[blender.git] / source / blender / collada / EffectExporter.cpp
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Contributor(s): Chingiz Dyussenov, Arystanbek Dyussenov, Jan Diederich, Tod Liverseed,
19  *                 Nathan Letwory
20  *
21  * ***** END GPL LICENSE BLOCK *****
22  */
23
24 /** \file blender/collada/EffectExporter.cpp
25  *  \ingroup collada
26  */
27
28
29 #include <map>
30
31 #include "COLLADASWEffectProfile.h"
32 #include "COLLADAFWColorOrTexture.h"
33
34 #include "EffectExporter.h"
35 #include "DocumentExporter.h"
36 #include "MaterialExporter.h"
37
38 #include "collada_internal.h"
39 #include "collada_utils.h"
40
41 extern "C" {
42         #include "DNA_mesh_types.h"
43         #include "DNA_texture_types.h"
44         #include "DNA_world_types.h"
45
46         #include "BKE_customdata.h"
47         #include "BKE_mesh.h"
48         #include "BKE_material.h"
49 }
50
51 EffectsExporter::EffectsExporter(COLLADASW::StreamWriter *sw, const ExportSettings *export_settings) : COLLADASW::LibraryEffects(sw), export_settings(export_settings) {
52 }
53
54
55 bool EffectsExporter::hasEffects(Scene *sce)
56 {
57         Base *base = (Base *)sce->base.first;
58         
59         while (base) {
60                 Object *ob = base->object;
61                 int a;
62                 for (a = 0; a < ob->totcol; a++) {
63                         Material *ma = give_current_material(ob, a + 1);
64
65                         // no material, but check all of the slots
66                         if (!ma) continue;
67
68                         return true;
69                 }
70                 base = base->next;
71         }
72         return false;
73 }
74
75 void EffectsExporter::exportEffects(Scene *sce)
76 {
77         this->scene = sce;
78         
79         if (this->export_settings->export_texture_type == BC_TEXTURE_TYPE_MAT) {
80                 if (hasEffects(sce)) {
81                                 MaterialFunctor mf;
82                                 openLibrary();
83                                 mf.forEachMaterialInExportSet<EffectsExporter>(sce, *this, this->export_settings->export_set);
84                                 closeLibrary();
85                 }
86         }
87         else if (this->export_settings->export_texture_type == BC_TEXTURE_TYPE_UV) {
88                 std::set<Object *> uv_textured_obs = bc_getUVTexturedObjects(sce, !this->export_settings->active_uv_only);
89                 std::set<Image *> uv_images = bc_getUVImages(sce, !this->export_settings->active_uv_only);
90                 if (uv_images.size() > 0) {
91                         openLibrary();
92                         std::set<Image *>::iterator uv_images_iter;
93                         for (uv_images_iter = uv_images.begin();
94                                 uv_images_iter != uv_images.end();
95                                 uv_images_iter++) {
96
97                                 Image *ima = *uv_images_iter;
98                                 std::string key(id_name(ima));
99                                 key = translate_id(key);
100                                 COLLADASW::Sampler sampler(COLLADASW::Sampler::SAMPLER_TYPE_2D,
101                                         key + COLLADASW::Sampler::SAMPLER_SID_SUFFIX,
102                                         key + COLLADASW::Sampler::SURFACE_SID_SUFFIX);
103                                 sampler.setImageId(key);
104
105                                 openEffect(key + "-effect");
106                                 COLLADASW::EffectProfile ep(mSW);
107                                 ep.setProfileType(COLLADASW::EffectProfile::COMMON);
108                                 ep.setShaderType(COLLADASW::EffectProfile::PHONG);
109                                 ep.setDiffuse(createTexture(ima, key, &sampler), false, "diffuse");
110                                 COLLADASW::ColorOrTexture cot = getcol(0, 0, 0, 1.0f);
111                                 ep.setSpecular(cot, false, "specular");
112                                 ep.openProfile();
113                                 ep.addProfileElements();
114                                 ep.addExtraTechniques(mSW);
115                                 ep.closeProfile();
116                                 closeEffect();
117                         }
118                         closeLibrary();
119                 }
120         }
121 }
122
123 void EffectsExporter::writeBlinn(COLLADASW::EffectProfile &ep, Material *ma)
124 {
125         COLLADASW::ColorOrTexture cot;
126         ep.setShaderType(COLLADASW::EffectProfile::BLINN);
127         // shininess
128         ep.setShininess(ma->har, false, "shininess");
129         // specular
130         cot = getcol(ma->specr, ma->specg, ma->specb, 1.0f);
131         ep.setSpecular(cot, false, "specular");
132 }
133
134 void EffectsExporter::writeLambert(COLLADASW::EffectProfile &ep, Material *ma)
135 {
136         COLLADASW::ColorOrTexture cot;
137         ep.setShaderType(COLLADASW::EffectProfile::LAMBERT);
138 }
139
140 void EffectsExporter::writePhong(COLLADASW::EffectProfile &ep, Material *ma)
141 {
142         COLLADASW::ColorOrTexture cot;
143         ep.setShaderType(COLLADASW::EffectProfile::PHONG);
144         // shininess
145         ep.setShininess(ma->har, false, "shininess");
146         // specular
147         cot = getcol(ma->specr, ma->specg, ma->specb, 1.0f);
148         ep.setSpecular(cot, false, "specular");
149 }
150
151 void EffectsExporter::writeTextures(COLLADASW::EffectProfile &ep,
152                                                                         std::string &key,
153                                                                         COLLADASW::Sampler *sampler, 
154                                                                         MTex *t, Image *ima,
155                                                                         std::string &uvname ) {
156                 
157         // Image not set for texture
158         if (!ima) return;
159
160         // color
161         if (t->mapto & MAP_COL) {
162                 ep.setDiffuse(createTexture(ima, uvname, sampler), false, "diffuse");
163         }
164         // ambient
165         if (t->mapto & MAP_AMB) {
166                 ep.setAmbient(createTexture(ima, uvname, sampler), false, "ambient");
167         }
168         // specular
169         if (t->mapto & (MAP_SPEC | MAP_COLSPEC)) {
170                 ep.setSpecular(createTexture(ima, uvname, sampler), false, "specular");
171         }
172         // emission
173         if (t->mapto & MAP_EMIT) {
174                 ep.setEmission(createTexture(ima, uvname, sampler), false, "emission");
175         }
176         // reflective
177         if (t->mapto & MAP_REF) {
178                 ep.setReflective(createTexture(ima, uvname, sampler));
179         }
180         // alpha
181         if (t->mapto & MAP_ALPHA) {
182                 ep.setTransparent(createTexture(ima, uvname, sampler));
183         }
184         // extension:
185         // Normal map --> Must be stored with <extra> tag as different technique, 
186         // since COLLADA doesn't support normal maps, even in current COLLADA 1.5.
187         if (t->mapto & MAP_NORM) {
188                 COLLADASW::Texture texture(key);
189                 texture.setTexcoord(uvname);
190                 texture.setSampler(*sampler);
191                 // technique FCOLLADA, with the <bump> tag, is most likely the best understood,
192                 // most widespread de-facto standard.
193                 texture.setProfileName("FCOLLADA");
194                 texture.setChildElementName("bump");
195                 ep.addExtraTechniqueColorOrTexture(COLLADASW::ColorOrTexture(texture));
196         }
197 }
198
199 void EffectsExporter::exportUVMats(Object *ob)
200 {
201         std::vector<int> tex_indices;
202         int active_uv_layer = -1;
203         std::set<Image *> uv_textures;
204         if (ob->type == OB_MESH && ob->totcol && this->export_settings->export_texture_type == BC_TEXTURE_TYPE_UV) {
205                 bool active_uv_only = this->export_settings->active_uv_only;
206                 Mesh *me = (Mesh *)ob->data;
207                 active_uv_layer = CustomData_get_active_layer_index(&me->pdata, CD_MTEXPOLY);
208         }
209 }
210
211 void EffectsExporter::operator()(Material *ma, Object *ob)
212 {
213         // create a list of indices to textures of type TEX_IMAGE
214         std::vector<int> tex_indices;
215         if (this->export_settings->export_texture_type == BC_TEXTURE_TYPE_MAT)
216                 createTextureIndices(ma, tex_indices);
217
218         openEffect(translate_id(id_name(ma)) + "-effect");
219         
220         COLLADASW::EffectProfile ep(mSW);
221         ep.setProfileType(COLLADASW::EffectProfile::COMMON);
222         ep.openProfile();
223         // set shader type - one of three blinn, phong or lambert
224         if (ma->spec > 0.0f) {
225                 if (ma->spec_shader == MA_SPEC_BLINN) {
226                         writeBlinn(ep, ma);
227                 }
228                 else {
229                         // \todo figure out handling of all spec+diff shader combos blender has, for now write phong
230                         // for now set phong in case spec shader is not blinn
231                         writePhong(ep, ma);
232                 }
233         }
234         else {
235                 if (ma->diff_shader == MA_DIFF_LAMBERT) {
236                         writeLambert(ep, ma);
237                 }
238                 else {
239                         // \todo figure out handling of all spec+diff shader combos blender has, for now write phong
240                         writePhong(ep, ma);
241                 }
242         }
243         
244         // index of refraction
245         if (ma->mode & MA_RAYTRANSP) {
246                 ep.setIndexOfRefraction(ma->ang, false, "index_of_refraction");
247         }
248         else {
249                 ep.setIndexOfRefraction(1.0f, false, "index_of_refraction");
250         }
251
252         COLLADASW::ColorOrTexture cot;
253
254         // transparency
255         if (ma->mode & MA_TRANSP) {
256                 // Tod: because we are in A_ONE mode transparency is calculated like this:
257                 cot = getcol(1.0f, 1.0f, 1.0f, ma->alpha);
258                 ep.setTransparent(cot);
259                 ep.setOpaque(COLLADASW::EffectProfile::A_ONE);
260         }
261
262         // emission
263         cot = getcol(ma->emit, ma->emit, ma->emit, 1.0f);
264         ep.setEmission(cot, false, "emission");
265
266         // diffuse multiplied by diffuse intensity
267         cot = getcol(ma->r * ma->ref, ma->g * ma->ref, ma->b * ma->ref, 1.0f);
268         ep.setDiffuse(cot, false, "diffuse");
269
270         // ambient
271         /* ma->ambX is calculated only on render, so lets do it here manually and not rely on ma->ambX. */
272         if (this->scene->world)
273                 cot = getcol(this->scene->world->ambr * ma->amb, this->scene->world->ambg * ma->amb, this->scene->world->ambb * ma->amb, 1.0f);
274         else
275                 cot = getcol(ma->amb, ma->amb, ma->amb, 1.0f);
276
277         ep.setAmbient(cot, false, "ambient");
278
279         // reflective, reflectivity
280         if (ma->mode & MA_RAYMIRROR) {
281                 cot = getcol(ma->mirr, ma->mirg, ma->mirb, 1.0f);
282                 ep.setReflective(cot);
283                 ep.setReflectivity(ma->ray_mirror);
284         }
285         // else {
286         //  cot = getcol(ma->specr, ma->specg, ma->specb, 1.0f);
287         //  ep.setReflective(cot);
288         //  ep.setReflectivity(ma->spec);
289         // }
290
291         // specular
292         if (ep.getShaderType() != COLLADASW::EffectProfile::LAMBERT) {
293                 cot = getcol(ma->specr * ma->spec, ma->specg * ma->spec, ma->specb * ma->spec, 1.0f);
294                 ep.setSpecular(cot, false, "specular");
295         }
296
297         // XXX make this more readable if possible
298
299         // create <sampler> and <surface> for each image
300         COLLADASW::Sampler samplers[MAX_MTEX];
301         //COLLADASW::Surface surfaces[MAX_MTEX];
302         //void *samp_surf[MAX_MTEX][2];
303         void *samp_surf[MAX_MTEX];
304         
305         // image to index to samp_surf map
306         // samp_surf[index] stores 2 pointers, sampler and surface
307         std::map<std::string, int> im_samp_map;
308
309         unsigned int a, b;
310         for (a = 0, b = 0; a < tex_indices.size(); a++) {
311                 MTex *t = ma->mtex[tex_indices[a]];
312                 Image *ima = t->tex->ima;
313                 
314                 // Image not set for texture
315                 if (!ima) continue;
316                 
317                 std::string key(id_name(ima));
318                 key = translate_id(key);
319
320                 // create only one <sampler>/<surface> pair for each unique image
321                 if (im_samp_map.find(key) == im_samp_map.end()) {
322                         // //<newparam> <surface> <init_from>
323                         // COLLADASW::Surface surface(COLLADASW::Surface::SURFACE_TYPE_2D,
324                         //                         key + COLLADASW::Surface::SURFACE_SID_SUFFIX);
325                         // COLLADASW::SurfaceInitOption sio(COLLADASW::SurfaceInitOption::INIT_FROM);
326                         // sio.setImageReference(key);
327                         // surface.setInitOption(sio);
328
329                         // COLLADASW::NewParamSurface surface(mSW);
330                         // surface->setParamType(COLLADASW::CSW_SURFACE_TYPE_2D);
331                         
332                         //<newparam> <sampler> <source>
333                         COLLADASW::Sampler sampler(COLLADASW::Sampler::SAMPLER_TYPE_2D,
334                                                    key + COLLADASW::Sampler::SAMPLER_SID_SUFFIX,
335                                                    key + COLLADASW::Sampler::SURFACE_SID_SUFFIX);
336                         sampler.setImageId(key);
337                         // copy values to arrays since they will live longer
338                         samplers[a] = sampler;
339                         //surfaces[a] = surface;
340                         
341                         // store pointers so they can be used later when we create <texture>s
342                         samp_surf[b] = &samplers[a];
343                         //samp_surf[b][1] = &surfaces[a];
344                         
345                         im_samp_map[key] = b;
346                         b++;
347                 }
348         }
349
350         int active_uv_layer = -1;
351         std::set<Image *> uv_textures;
352         if (ob->type == OB_MESH && ob->totcol && this->export_settings->export_texture_type == BC_TEXTURE_TYPE_UV) {
353                 bool active_uv_only = this->export_settings->active_uv_only;
354                 Mesh *me     = (Mesh *) ob->data;
355                 active_uv_layer = CustomData_get_active_layer_index(&me->pdata, CD_MTEXPOLY);
356
357                 BKE_mesh_tessface_ensure(me);
358                 for (int i = 0; i < me->pdata.totlayer; i++) {
359                         if (!active_uv_only || active_uv_layer == i)
360                         {
361                                 if (me->pdata.layers[i].type == CD_MTEXPOLY) {
362                                         MTexPoly *txface = (MTexPoly *)me->pdata.layers[i].data;
363                                         MPoly *mpoly = me->mpoly;
364                                         for (int j = 0; j < me->totpoly; j++, mpoly++, txface++) {
365
366                                                 Material *mat = give_current_material(ob, mpoly->mat_nr + 1);
367                                                 if (mat != ma) 
368                                                         continue;
369
370                                                 Image *ima = txface->tpage;
371                                                 if (ima == NULL)
372                                                         continue;
373
374
375                                                 bool not_in_list = uv_textures.find(ima)==uv_textures.end();
376                                                 if (not_in_list) {
377                                                         std::string name = id_name(ima);
378                                                         std::string key(name);
379                                                         key = translate_id(key);
380
381                                                         // create only one <sampler>/<surface> pair for each unique image
382                                                         if (im_samp_map.find(key) == im_samp_map.end()) {
383                                                                 //<newparam> <sampler> <source>
384                                                                 COLLADASW::Sampler sampler(COLLADASW::Sampler::SAMPLER_TYPE_2D,
385                                                                                                                    key + COLLADASW::Sampler::SAMPLER_SID_SUFFIX,
386                                                                                                                    key + COLLADASW::Sampler::SURFACE_SID_SUFFIX);
387                                                                 sampler.setImageId(key);
388                                                                 samplers[a] = sampler;
389                                                                 samp_surf[b] = &samplers[a];
390                                                                 im_samp_map[key] = b;
391                                                                 b++;
392                                                                 a++;
393                                                                 uv_textures.insert(ima);
394                                                         }
395                                                 }
396                                         }
397                                 }
398                         }
399                 }
400         }
401
402         // used as fallback when MTex->uvname is "" (this is pretty common)
403         // it is indeed the correct value to use in that case
404         std::string active_uv(bc_get_active_uvlayer_name(ob));
405
406         // write textures
407         // XXX very slow
408         for (a = 0; a < tex_indices.size(); a++) {
409                 MTex *t = ma->mtex[tex_indices[a]];
410                 Image *ima = t->tex->ima;
411
412                 if (!ima) {
413                         continue;
414                 }
415
416                 std::string key(id_name(ima));
417                 key = translate_id(key);
418                 int i = im_samp_map[key];
419                 std::string uvname = strlen(t->uvname) ? t->uvname : active_uv;
420                 COLLADASW::Sampler *sampler = (COLLADASW::Sampler *)samp_surf[i];
421                 writeTextures(ep, key, sampler, t, ima, uvname);
422         }
423
424         if (active_uv_layer > -1) {
425                 // Export only UV textures assigned to active UV Layer (sounds reasonable, but is that correct?)
426                 std::set<Image *>::iterator uv_t_iter;
427
428                 for (uv_t_iter = uv_textures.begin(); uv_t_iter != uv_textures.end(); uv_t_iter++) {
429                         Image *ima = *uv_t_iter;
430                         std::string key(id_name(ima));
431                         key = translate_id(key);
432                         int i = im_samp_map[key];
433                         COLLADASW::Sampler *sampler = (COLLADASW::Sampler *)samp_surf[i];
434                         ep.setDiffuse(createTexture(ima, active_uv, sampler), false, "diffuse");
435                         ep.setShaderType(COLLADASW::EffectProfile::PHONG);
436                 }
437         }
438
439         // performs the actual writing
440         ep.addProfileElements();
441         bool twoSided = false;
442         if (ob->type == OB_MESH && ob->data) {
443                 Mesh *me = (Mesh *)ob->data;
444                 if (me->flag & ME_TWOSIDED)
445                         twoSided = true;
446         }
447         if (twoSided)
448                 ep.addExtraTechniqueParameter("GOOGLEEARTH", "double_sided", 1);
449         ep.addExtraTechniques(mSW);
450
451         ep.closeProfile();
452         if (twoSided)
453                 mSW->appendTextBlock("<extra><technique profile=\"MAX3D\"><double_sided>1</double_sided></technique></extra>");
454         closeEffect();
455 }
456
457 COLLADASW::ColorOrTexture EffectsExporter::createTexture(Image *ima,
458                                                          std::string& uv_layer_name,
459                                                          COLLADASW::Sampler *sampler
460                                                          /*COLLADASW::Surface *surface*/)
461 {
462         
463         COLLADASW::Texture texture(translate_id(id_name(ima)));
464         texture.setTexcoord(uv_layer_name);
465         //texture.setSurface(*surface);
466         texture.setSampler(*sampler);
467         
468         COLLADASW::ColorOrTexture cot(texture);
469         return cot;
470 }
471
472 COLLADASW::ColorOrTexture EffectsExporter::getcol(float r, float g, float b, float a)
473 {
474         COLLADASW::Color color(r, g, b, a);
475         COLLADASW::ColorOrTexture cot(color);
476         return cot;
477 }
478
479 //returns the array of mtex indices which have image 
480 //need this for exporting textures
481 void EffectsExporter::createTextureIndices(Material *ma, std::vector<int> &indices)
482 {
483         indices.clear();
484
485         for (int a = 0; a < MAX_MTEX; a++) {
486                 if (ma->mtex[a] &&
487                     ma->mtex[a]->tex &&
488                     ma->mtex[a]->tex->type == TEX_IMAGE &&
489                     ma->mtex[a]->texco == TEXCO_UV)
490                 {
491                         indices.push_back(a);
492                 }
493         }
494 }