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