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