style cleanup: trailing tabs & expand some non prefix tabs into spaces.
[blender.git] / source / blender / editors / render / render_preview.c
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  * The Original Code is Copyright (C) Blender Foundation.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): none yet.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/render/render_preview.c
29  *  \ingroup edrend
30  */
31
32
33 /* global includes */
34
35 #include <stdlib.h>
36 #include <math.h>
37 #include <string.h>
38
39 #ifndef WIN32
40 #include <unistd.h>
41 #else
42 #include <io.h>
43 #endif   
44 #include "MEM_guardedalloc.h"
45
46 #include "BLO_readfile.h" 
47
48 #include "BLI_math.h"
49 #include "BLI_blenlib.h"
50 #include "BLI_threads.h"
51 #include "BLI_utildefines.h"
52
53 #include "DNA_world_types.h"
54 #include "DNA_camera_types.h"
55 #include "DNA_material_types.h"
56 #include "DNA_node_types.h"
57 #include "DNA_object_types.h"
58 #include "DNA_lamp_types.h"
59 #include "DNA_space_types.h"
60 #include "DNA_view3d_types.h"
61 #include "DNA_scene_types.h"
62 #include "DNA_brush_types.h"
63 #include "DNA_screen_types.h"
64
65 #include "BKE_brush.h"
66 #include "BKE_context.h"
67 #include "BKE_colortools.h"
68 #include "BKE_depsgraph.h"
69 #include "BKE_global.h"
70 #include "BKE_idprop.h"
71 #include "BKE_image.h"
72 #include "BKE_icons.h"
73 #include "BKE_lamp.h"
74 #include "BKE_library.h"
75 #include "BKE_main.h"
76 #include "BKE_material.h"
77 #include "BKE_node.h"
78 #include "BKE_object.h"
79 #include "BKE_scene.h"
80 #include "BKE_texture.h"
81 #include "BKE_world.h"
82
83 #include "IMB_imbuf.h"
84 #include "IMB_imbuf_types.h"
85 #include "IMB_colormanagement.h"
86
87 #include "BIF_gl.h"
88 #include "BIF_glutil.h"
89
90 #include "PIL_time.h"
91
92 #include "RE_pipeline.h"
93
94
95 #include "WM_api.h"
96 #include "WM_types.h"
97
98 #include "ED_render.h"
99 #include "ED_view3d.h"
100
101 #include "UI_interface.h"
102
103 #include "render_intern.h"
104
105 ImBuf *get_brush_icon(Brush *brush)
106 {
107         static const int flags = IB_rect | IB_multilayer | IB_metadata;
108
109         char path[FILE_MAX];
110         char *folder;
111
112         if (!(brush->icon_imbuf)) {
113                 if (brush->flag & BRUSH_CUSTOM_ICON) {
114
115                         if (brush->icon_filepath[0]) {
116                                 // first use the path directly to try and load the file
117
118                                 BLI_strncpy(path, brush->icon_filepath, sizeof(brush->icon_filepath));
119                                 BLI_path_abs(path, G.main->name);
120
121                                 /* use default colorspaces for brushes */
122                                 brush->icon_imbuf = IMB_loadiffname(path, flags, NULL);
123
124                                 // otherwise lets try to find it in other directories
125                                 if (!(brush->icon_imbuf)) {
126                                         folder = BLI_get_folder(BLENDER_DATAFILES, "brushicons");
127
128                                         BLI_make_file_string(G.main->name, path, folder, brush->icon_filepath);
129
130                                         if (path[0]) {
131                                                 /* use fefault color spaces */
132                                                 brush->icon_imbuf = IMB_loadiffname(path, flags, NULL);
133                                         }
134                                 }
135
136                                 if (brush->icon_imbuf)
137                                         BKE_icon_changed(BKE_icon_getid(&brush->id));
138                         }
139                 }
140         }
141
142         if (!(brush->icon_imbuf))
143                 brush->id.icon_id = 0;
144
145         return brush->icon_imbuf;
146 }
147
148 typedef struct ShaderPreview {
149         /* from wmJob */
150         void *owner;
151         short *stop, *do_update;
152         
153         Scene *scene;
154         ID *id;
155         ID *parent;
156         MTex *slot;
157         
158         /* datablocks with nodes need full copy during preview render, glsl uses it too */
159         Material *matcopy;
160         Tex *texcopy;
161         Lamp *lampcopy;
162         World *worldcopy;
163         
164         float col[4];       /* active object color */
165         
166         int sizex, sizey;
167         unsigned int *pr_rect;
168         int pr_method;
169         
170 } ShaderPreview;
171
172 typedef struct IconPreviewSize {
173         struct IconPreviewSize *next, *prev;
174         int sizex, sizey;
175         unsigned int *rect;
176 } IconPreviewSize;
177
178 typedef struct IconPreview {
179         Scene *scene;
180         void *owner;
181         ID *id;
182         ListBase sizes;
183 } IconPreview;
184
185 /* *************************** Preview for buttons *********************** */
186
187 static Main *pr_main = NULL;
188
189 void ED_preview_init_dbase(void)
190 {
191 #ifndef WITH_HEADLESS
192         BlendFileData *bfd;
193         extern int datatoc_preview_blend_size;
194         extern char datatoc_preview_blend[];
195         const int fileflags = G.fileflags;
196         
197         G.fileflags |= G_FILE_NO_UI;
198         bfd = BLO_read_from_memory(datatoc_preview_blend, datatoc_preview_blend_size, NULL);
199         if (bfd) {
200                 pr_main = bfd->main;
201                 
202                 MEM_freeN(bfd);
203         }
204         G.fileflags = fileflags;
205 #endif
206 }
207
208 void ED_preview_free_dbase(void)
209 {
210         if (pr_main)
211                 free_main(pr_main);
212 }
213
214 static int preview_mat_has_sss(Material *mat, bNodeTree *ntree)
215 {
216         if (mat) {
217                 if (mat->sss_flag & MA_DIFF_SSS)
218                         return 1;
219                 if (mat->nodetree)
220                         if (preview_mat_has_sss(NULL, mat->nodetree))
221                                 return 1;
222         }
223         else if (ntree) {
224                 bNode *node;
225                 for (node = ntree->nodes.first; node; node = node->next) {
226                         if (node->type == NODE_GROUP && node->id) {
227                                 if (preview_mat_has_sss(NULL, (bNodeTree *)node->id))
228                                         return 1;
229                         }
230                         else if (node->id && ELEM(node->type, SH_NODE_MATERIAL, SH_NODE_MATERIAL_EXT)) {
231                                 mat = (Material *)node->id;
232                                 if (mat->sss_flag & MA_DIFF_SSS)
233                                         return 1;
234                         }
235                 }
236         }
237         return 0;
238 }
239
240 /* call this with a pointer to initialize preview scene */
241 /* call this with NULL to restore assigned ID pointers in preview scene */
242 static Scene *preview_prepare_scene(Scene *scene, ID *id, int id_type, ShaderPreview *sp)
243 {
244         Scene *sce;
245         Base *base;
246         
247         if (pr_main == NULL) return NULL;
248         
249         sce = pr_main->scene.first;
250         if (sce) {
251                 
252                 /* this flag tells render to not execute depsgraph or ipos etc */
253                 sce->r.scemode |= R_PREVIEWBUTS;
254                 /* set world always back, is used now */
255                 sce->world = pr_main->world.first;
256                 /* now: exposure copy */
257                 if (scene->world) {
258                         sce->world->exp = scene->world->exp;
259                         sce->world->range = scene->world->range;
260                 }
261                 
262                 sce->r.color_mgt_flag = scene->r.color_mgt_flag;
263                 BKE_color_managed_display_settings_copy(&sce->display_settings, &scene->display_settings);
264
265                 BKE_color_managed_view_settings_free(&sce->view_settings);
266                 BKE_color_managed_view_settings_copy(&sce->view_settings, &scene->view_settings);
267                 
268                 /* prevent overhead for small renders and icons (32) */
269                 if (id && sp->sizex < 40)
270                         sce->r.xparts = sce->r.yparts = 1;
271                 else
272                         sce->r.xparts = sce->r.yparts = 4;
273                 
274                 /* exception: don't apply render part of display transform for texture previews or icons */
275                 if ((id && sp->pr_method == PR_ICON_RENDER) || id_type == ID_TE) {
276                         BKE_scene_disable_color_management(sce);
277                 }
278                 
279                 if ((id && sp->pr_method == PR_ICON_RENDER) && id_type != ID_WO)
280                         sce->r.alphamode = R_ALPHAPREMUL;
281                 else
282                         sce->r.alphamode = R_ADDSKY;
283
284                 sce->r.cfra = scene->r.cfra;
285                 BLI_strncpy(sce->r.engine, scene->r.engine, sizeof(sce->r.engine));
286                 
287                 if (id_type == ID_MA) {
288                         Material *mat = NULL, *origmat = (Material *)id;
289                         
290                         if (origmat) {
291                                 /* work on a copy */
292                                 mat = localize_material(origmat);
293                                 sp->matcopy = mat;
294                                 BLI_addtail(&pr_main->mat, mat);
295                                 
296                                 init_render_material(mat, 0, NULL);     /* call that retrieves mode_l */
297                                 end_render_material(mat);
298                                 
299                                 /* un-useful option */
300                                 if (sp->pr_method == PR_ICON_RENDER)
301                                         mat->shade_flag &= ~MA_OBCOLOR;
302
303                                 /* turn on raytracing if needed */
304                                 if (mat->mode_l & MA_RAYMIRROR)
305                                         sce->r.mode |= R_RAYTRACE;
306                                 if (mat->material_type == MA_TYPE_VOLUME)
307                                         sce->r.mode |= R_RAYTRACE;
308                                 if ((mat->mode_l & MA_RAYTRANSP) && (mat->mode_l & MA_TRANSP))
309                                         sce->r.mode |= R_RAYTRACE;
310                                 if (preview_mat_has_sss(mat, NULL))
311                                         sce->r.mode |= R_SSS;
312                                 
313                                 /* turn off fake shadows if needed */
314                                 /* this only works in a specific case where the preview.blend contains
315                                  * an object starting with 'c' which has a material linked to it (not the obdata)
316                                  * and that material has a fake shadow texture in the active texture slot */
317                                 for (base = sce->base.first; base; base = base->next) {
318                                         if (base->object->id.name[2] == 'c') {
319                                                 Material *shadmat = give_current_material(base->object, base->object->actcol);
320                                                 if (shadmat) {
321                                                         if (mat->mode & MA_SHADBUF) shadmat->septex = 0;
322                                                         else shadmat->septex |= 1;
323                                                 }
324                                         }
325                                 }
326                                 
327                                 /* turn off bounce lights for volume, 
328                                  * doesn't make much visual difference and slows it down too */
329                                 if (mat->material_type == MA_TYPE_VOLUME) {
330                                         for (base = sce->base.first; base; base = base->next) {
331                                                 if (base->object->type == OB_LAMP) {
332                                                         /* if doesn't match 'Lamp.002' --> main key light */
333                                                         if (strcmp(base->object->id.name + 2, "Lamp.002") != 0) {
334                                                                 base->object->restrictflag |= OB_RESTRICT_RENDER;
335                                                         }
336                                                 }
337                                         }
338                                 }
339
340                                 
341                                 if (sp->pr_method == PR_ICON_RENDER) {
342                                         if (mat->material_type == MA_TYPE_HALO) {
343                                                 sce->lay = 1 << MA_FLAT;
344                                         }
345                                         else {
346                                                 sce->lay = 1 << MA_SPHERE_A;
347                                         }
348                                 }
349                                 else {
350                                         sce->lay = 1 << mat->pr_type;
351                                         if (mat->nodetree && sp->pr_method == PR_NODE_RENDER) {
352                                                 /* two previews, they get copied by wmJob */
353                                                 ntreeInitPreview(mat->nodetree, sp->sizex, sp->sizey);
354                                                 ntreeInitPreview(origmat->nodetree, sp->sizex, sp->sizey);
355                                         }
356                                 }
357                         }
358                         else {
359                                 sce->r.mode &= ~(R_OSA | R_RAYTRACE | R_SSS);
360                                 
361                         }
362                         
363                         for (base = sce->base.first; base; base = base->next) {
364                                 if (base->object->id.name[2] == 'p') {
365                                         /* copy over object color, in case material uses it */
366                                         copy_v4_v4(base->object->col, sp->col);
367                                         
368                                         if (OB_TYPE_SUPPORT_MATERIAL(base->object->type)) {
369                                                 /* don't use assign_material, it changed mat->id.us, which shows in the UI */
370                                                 Material ***matar = give_matarar(base->object);
371                                                 int actcol = MAX2(base->object->actcol - 1, 0);
372
373                                                 if (matar && actcol < base->object->totcol)
374                                                         (*matar)[actcol] = mat;
375                                         }
376                                         else if (base->object->type == OB_LAMP) {
377                                                 base->object->restrictflag &= ~OB_RESTRICT_RENDER;
378                                         }
379                                 }
380                         }
381                 }
382                 else if (id_type == ID_TE) {
383                         Tex *tex = NULL, *origtex = (Tex *)id;
384                         
385                         if (origtex) {
386                                 tex = localize_texture(origtex);
387                                 sp->texcopy = tex;
388                                 BLI_addtail(&pr_main->tex, tex);
389                         }
390                         sce->lay = 1 << MA_TEXTURE;
391                         
392                         for (base = sce->base.first; base; base = base->next) {
393                                 if (base->object->id.name[2] == 't') {
394                                         Material *mat = give_current_material(base->object, base->object->actcol);
395                                         if (mat && mat->mtex[0]) {
396                                                 mat->mtex[0]->tex = tex;
397                                                 
398                                                 if (tex && sp->slot)
399                                                         mat->mtex[0]->which_output = sp->slot->which_output;
400                                                 
401                                                 /* show alpha in this case */
402                                                 if (tex == NULL || (tex->flag & TEX_PRV_ALPHA)) {
403                                                         mat->mtex[0]->mapto |= MAP_ALPHA;
404                                                         mat->alpha = 0.0f;
405                                                 }
406                                                 else {
407                                                         mat->mtex[0]->mapto &= ~MAP_ALPHA;
408                                                         mat->alpha = 1.0f;
409                                                 }
410                                         }
411                                 }
412                         }
413
414                         if (tex && tex->nodetree && sp->pr_method == PR_NODE_RENDER) {
415                                 /* two previews, they get copied by wmJob */
416                                 ntreeInitPreview(origtex->nodetree, sp->sizex, sp->sizey);
417                                 ntreeInitPreview(tex->nodetree, sp->sizex, sp->sizey);
418                         }
419                 }
420                 else if (id_type == ID_LA) {
421                         Lamp *la = NULL, *origla = (Lamp *)id;
422
423                         /* work on a copy */
424                         if (origla) {
425                                 la = localize_lamp(origla);
426                                 sp->lampcopy = la;
427                                 BLI_addtail(&pr_main->lamp, la);
428                         }
429                         
430                         if (la && la->type == LA_SUN && (la->sun_effect_type & LA_SUN_EFFECT_SKY)) {
431                                 sce->lay = 1 << MA_ATMOS;
432                                 sce->world = scene->world;
433                                 sce->camera = (Object *)BLI_findstring(&pr_main->object, "CameraAtmo", offsetof(ID, name) + 2);
434                         }
435                         else {
436                                 sce->lay = 1 << MA_LAMP;
437                                 sce->world = NULL;
438                                 sce->camera = (Object *)BLI_findstring(&pr_main->object, "Camera", offsetof(ID, name) + 2);
439                         }
440                         sce->r.mode &= ~R_SHADOW;
441                         
442                         for (base = sce->base.first; base; base = base->next) {
443                                 if (base->object->id.name[2] == 'p') {
444                                         if (base->object->type == OB_LAMP)
445                                                 base->object->data = la;
446                                 }
447                         }
448
449                         if (la && la->nodetree && sp->pr_method == PR_NODE_RENDER) {
450                                 /* two previews, they get copied by wmJob */
451                                 ntreeInitPreview(origla->nodetree, sp->sizex, sp->sizey);
452                                 ntreeInitPreview(la->nodetree, sp->sizex, sp->sizey);
453                         }
454                 }
455                 else if (id_type == ID_WO) {
456                         World *wrld = NULL, *origwrld = (World *)id;
457
458                         if (origwrld) {
459                                 wrld = localize_world(origwrld);
460                                 sp->worldcopy = wrld;
461                                 BLI_addtail(&pr_main->world, wrld);
462                         }
463
464                         sce->lay = 1 << MA_SKY;
465                         sce->world = wrld;
466
467                         if (wrld && wrld->nodetree && sp->pr_method == PR_NODE_RENDER) {
468                                 /* two previews, they get copied by wmJob */
469                                 ntreeInitPreview(wrld->nodetree, sp->sizex, sp->sizey);
470                                 ntreeInitPreview(origwrld->nodetree, sp->sizex, sp->sizey);
471                         }
472                 }
473                 
474                 return sce;
475         }
476         
477         return NULL;
478 }
479
480 /* new UI convention: draw is in pixel space already. */
481 /* uses ROUNDBOX button in block to get the rect */
482 static int ed_preview_draw_rect(ScrArea *sa, Scene *sce, ID *id, int split, int first, rcti *rect, rcti *newrect)
483 {
484         Render *re;
485         RenderResult rres;
486         char name[32];
487         int do_gamma_correct = FALSE, do_predivide = FALSE;
488         int offx = 0;
489         int newx = BLI_rcti_size_x(rect);
490         int newy = BLI_rcti_size_y(rect);
491
492         if (id && GS(id->name) != ID_TE) {
493                 /* exception: don't color manage texture previews - show the raw values */
494                 if (sce) {
495                         do_gamma_correct = TRUE;
496                         do_predivide = sce->r.color_mgt_flag & R_COLOR_MANAGEMENT_PREDIVIDE;
497                 }
498         }
499
500         if (!split || first) sprintf(name, "Preview %p", (void *)sa);
501         else sprintf(name, "SecondPreview %p", (void *)sa);
502
503         if (split) {
504                 if (first) {
505                         offx = 0;
506                         newx = newx / 2;
507                 }
508                 else {
509                         offx = newx / 2;
510                         newx = newx - newx / 2;
511                 }
512         }
513
514         re = RE_GetRender(name);
515         RE_AcquireResultImage(re, &rres);
516
517         if (rres.rectf) {
518                 
519                 if (ABS(rres.rectx - newx) < 2 && ABS(rres.recty - newy) < 2) {
520
521                         newrect->xmax = MAX2(newrect->xmax, rect->xmin + rres.rectx + offx);
522                         newrect->ymax = MAX2(newrect->ymax, rect->ymin + rres.recty);
523
524                         if (rres.rectx && rres.recty) {
525                                 /* temporary conversion to byte for drawing */
526                                 float fx = rect->xmin + offx;
527                                 float fy = rect->ymin;
528                                 int dither = 0;
529                                 unsigned char *rect_byte;
530
531                                 rect_byte = MEM_mallocN(rres.rectx * rres.recty * sizeof(int), "ed_preview_draw_rect");
532
533                                 if (do_gamma_correct) {
534                                         IMB_display_buffer_transform_apply(rect_byte, rres.rectf, rres.rectx, rres.recty, 4,
535                                                                            &sce->view_settings, &sce->display_settings, do_predivide);
536
537                                 }
538                                 else {
539                                         /* OCIO_TODO: currently seems an exception for textures (came fro mlegacish time),
540                                          *            but is it indeed expected behavior, or textures should be
541                                          *            color managed as well?
542                                          */
543                                         IMB_buffer_byte_from_float(rect_byte, rres.rectf,
544                                                                    4, dither, IB_PROFILE_SRGB, IB_PROFILE_SRGB, do_predivide,
545                                                                    rres.rectx, rres.recty, rres.rectx, rres.rectx);
546                                 }
547
548                                 glaDrawPixelsSafe(fx, fy, rres.rectx, rres.recty, rres.rectx, GL_RGBA, GL_UNSIGNED_BYTE, rect_byte);
549
550                                 MEM_freeN(rect_byte);
551                         }
552
553                         RE_ReleaseResultImage(re);
554                         return 1;
555                 }
556         }
557
558         RE_ReleaseResultImage(re);
559         return 0;
560 }
561
562 void ED_preview_draw(const bContext *C, void *idp, void *parentp, void *slotp, rcti *rect)
563 {
564         if (idp) {
565                 ScrArea *sa = CTX_wm_area(C);
566                 Scene *sce = CTX_data_scene(C);
567                 ID *id = (ID *)idp;
568                 ID *parent = (ID *)parentp;
569                 MTex *slot = (MTex *)slotp;
570                 SpaceButs *sbuts = sa->spacedata.first;
571                 rcti newrect;
572                 int ok;
573                 int newx = BLI_rcti_size_x(rect);
574                 int newy = BLI_rcti_size_y(rect);
575
576                 newrect.xmin = rect->xmin;
577                 newrect.xmax = rect->xmin;
578                 newrect.ymin = rect->ymin;
579                 newrect.ymax = rect->ymin;
580
581                 if (parent) {
582                         ok = ed_preview_draw_rect(sa, sce, id, 1, 1, rect, &newrect);
583                         ok &= ed_preview_draw_rect(sa, sce, parent, 1, 0, rect, &newrect);
584                 }
585                 else
586                         ok = ed_preview_draw_rect(sa, sce, id, 0, 0, rect, &newrect);
587
588                 if (ok)
589                         *rect = newrect;
590
591                 /* check for spacetype... */
592                 if (sbuts->spacetype == SPACE_BUTS && sbuts->preview) {
593                         sbuts->preview = 0;
594                         ok = 0;
595                 }
596         
597                 if (ok == 0) {
598                         ED_preview_shader_job(C, sa, id, parent, slot, newx, newy, PR_BUTS_RENDER);
599                 }
600         }
601 }
602
603 /* **************************** new shader preview system ****************** */
604
605 /* inside thread, called by renderer, sets job update value */
606 static void shader_preview_draw(void *spv, RenderResult *UNUSED(rr), volatile struct rcti *UNUSED(rect))
607 {
608         ShaderPreview *sp = spv;
609         
610         *(sp->do_update) = TRUE;
611 }
612
613 /* called by renderer, checks job value */
614 static int shader_preview_break(void *spv)
615 {
616         ShaderPreview *sp = spv;
617
618         return *(sp->stop);
619 }
620
621 /* outside thread, called before redraw notifiers, it moves finished preview over */
622 static void shader_preview_updatejob(void *spv)
623 {
624         ShaderPreview *sp = spv;
625         
626         if (sp->id) {
627                 if (sp->pr_method == PR_NODE_RENDER) {
628                         if (GS(sp->id->name) == ID_MA) {
629                                 Material *mat = (Material *)sp->id;
630                                 
631                                 if (sp->matcopy && mat->nodetree && sp->matcopy->nodetree)
632                                         ntreeLocalSync(sp->matcopy->nodetree, mat->nodetree);
633                         }
634                         else if (GS(sp->id->name) == ID_TE) {
635                                 Tex *tex = (Tex *)sp->id;
636                                 
637                                 if (sp->texcopy && tex->nodetree && sp->texcopy->nodetree)
638                                         ntreeLocalSync(sp->texcopy->nodetree, tex->nodetree);
639                         }
640                         else if (GS(sp->id->name) == ID_WO) {
641                                 World *wrld = (World *)sp->id;
642                                 
643                                 if (sp->worldcopy && wrld->nodetree && sp->worldcopy->nodetree)
644                                         ntreeLocalSync(sp->worldcopy->nodetree, wrld->nodetree);
645                         }
646                         else if (GS(sp->id->name) == ID_LA) {
647                                 Lamp *la = (Lamp *)sp->id;
648                                 
649                                 if (sp->lampcopy && la->nodetree && sp->lampcopy->nodetree)
650                                         ntreeLocalSync(sp->lampcopy->nodetree, la->nodetree);
651                         }
652                 }
653         }
654 }
655
656 static void shader_preview_render(ShaderPreview *sp, ID *id, int split, int first)
657 {
658         Render *re;
659         Scene *sce;
660         float oldlens;
661         short idtype = GS(id->name);
662         char name[32];
663         int sizex;
664         
665         /* get the stuff from the builtin preview dbase */
666         sce = preview_prepare_scene(sp->scene, id, idtype, sp); // XXX sizex
667         if (sce == NULL) return;
668         
669         if (!split || first) sprintf(name, "Preview %p", sp->owner);
670         else sprintf(name, "SecondPreview %p", sp->owner);
671         re = RE_GetRender(name);
672         
673         /* full refreshed render from first tile */
674         if (re == NULL)
675                 re = RE_NewRender(name);
676                 
677         /* sce->r gets copied in RE_InitState! */
678         sce->r.scemode &= ~(R_MATNODE_PREVIEW | R_TEXNODE_PREVIEW);
679         sce->r.scemode &= ~R_NO_IMAGE_LOAD;
680
681         if (sp->pr_method == PR_ICON_RENDER) {
682                 sce->r.scemode |= R_NO_IMAGE_LOAD;
683                 sce->r.mode |= R_OSA;
684         }
685         else if (sp->pr_method == PR_NODE_RENDER) {
686                 if (idtype == ID_MA) sce->r.scemode |= R_MATNODE_PREVIEW;
687                 else if (idtype == ID_TE) sce->r.scemode |= R_TEXNODE_PREVIEW;
688                 sce->r.mode &= ~R_OSA;
689         }
690         else {  /* PR_BUTS_RENDER */
691                 sce->r.mode |= R_OSA;
692         }
693
694         /* in case of split preview, use border render */
695         if (split) {
696                 if (first) sizex = sp->sizex / 2;
697                 else sizex = sp->sizex - sp->sizex / 2;
698         }
699         else sizex = sp->sizex;
700
701         /* allocates or re-uses render result */
702         sce->r.xsch = sizex;
703         sce->r.ysch = sp->sizey;
704         sce->r.size = 100;
705
706         /* callbacs are cleared on GetRender() */
707         if (ELEM(sp->pr_method, PR_BUTS_RENDER, PR_NODE_RENDER)) {
708                 RE_display_draw_cb(re, sp, shader_preview_draw);
709         }
710         /* set this for all previews, default is react to G.is_break still */
711         RE_test_break_cb(re, sp, shader_preview_break);
712         
713         /* lens adjust */
714         oldlens = ((Camera *)sce->camera->data)->lens;
715         if (sizex > sp->sizey)
716                 ((Camera *)sce->camera->data)->lens *= (float)sp->sizey / (float)sizex;
717
718         /* entire cycle for render engine */
719         RE_PreviewRender(re, pr_main, sce);
720
721         ((Camera *)sce->camera->data)->lens = oldlens;
722
723         /* handle results */
724         if (sp->pr_method == PR_ICON_RENDER) {
725                 // char *rct= (char *)(sp->pr_rect + 32*16 + 16);
726                 
727                 if (sp->pr_rect)
728                         RE_ResultGet32(re, sp->pr_rect);
729         }
730         else {
731                 /* validate owner */
732                 //if (ri->rect == NULL)
733                 //      ri->rect= MEM_mallocN(sizeof(int)*ri->pr_rectx*ri->pr_recty, "BIF_previewrender");
734                 //RE_ResultGet32(re, ri->rect);
735         }
736
737         /* unassign the pointers, reset vars */
738         preview_prepare_scene(sp->scene, NULL, GS(id->name), sp);
739         
740         /* XXX bad exception, end-exec is not being called in render, because it uses local main */
741 //      if (idtype == ID_TE) {
742 //              Tex *tex= (Tex *)id;
743 //              if (tex->use_nodes && tex->nodetree)
744 //                      ntreeEndExecTree(tex->nodetree);
745 //      }
746
747 }
748
749 /* runs inside thread for material and icons */
750 static void shader_preview_startjob(void *customdata, short *stop, short *do_update)
751 {
752         ShaderPreview *sp = customdata;
753
754         sp->stop = stop;
755         sp->do_update = do_update;
756
757         if (sp->parent) {
758                 shader_preview_render(sp, sp->id, 1, 1);
759                 shader_preview_render(sp, sp->parent, 1, 0);
760         }
761         else
762                 shader_preview_render(sp, sp->id, 0, 0);
763
764         *do_update = TRUE;
765 }
766
767 static void shader_preview_free(void *customdata)
768 {
769         ShaderPreview *sp = customdata;
770         
771         if (sp->matcopy) {
772                 struct IDProperty *properties;
773                 
774                 /* node previews */
775                 shader_preview_updatejob(sp);
776                 
777                 /* get rid of copied material */
778                 BLI_remlink(&pr_main->mat, sp->matcopy);
779                 
780                 BKE_material_free_ex(sp->matcopy, FALSE);
781
782                 properties = IDP_GetProperties((ID *)sp->matcopy, FALSE);
783                 if (properties) {
784                         IDP_FreeProperty(properties);
785                         MEM_freeN(properties);
786                 }
787                 MEM_freeN(sp->matcopy);
788         }
789         if (sp->texcopy) {
790                 struct IDProperty *properties;
791                 /* node previews */
792                 shader_preview_updatejob(sp);
793                 
794                 /* get rid of copied texture */
795                 BLI_remlink(&pr_main->tex, sp->texcopy);
796                 BKE_texture_free(sp->texcopy);
797                 
798                 properties = IDP_GetProperties((ID *)sp->texcopy, FALSE);
799                 if (properties) {
800                         IDP_FreeProperty(properties);
801                         MEM_freeN(properties);
802                 }
803                 MEM_freeN(sp->texcopy);
804         }
805         if (sp->worldcopy) {
806                 struct IDProperty *properties;
807                 /* node previews */
808                 shader_preview_updatejob(sp);
809                 
810                 /* get rid of copied world */
811                 BLI_remlink(&pr_main->world, sp->worldcopy);
812                 BKE_world_free_ex(sp->worldcopy, TRUE); /* [#32865] - we need to unlink the texture copies, unlike for materials */
813                 
814                 properties = IDP_GetProperties((ID *)sp->worldcopy, FALSE);
815                 if (properties) {
816                         IDP_FreeProperty(properties);
817                         MEM_freeN(properties);
818                 }
819                 MEM_freeN(sp->worldcopy);
820         }
821         if (sp->lampcopy) {
822                 struct IDProperty *properties;
823                 /* node previews */
824                 shader_preview_updatejob(sp);
825                 
826                 /* get rid of copied lamp */
827                 BLI_remlink(&pr_main->lamp, sp->lampcopy);
828                 BKE_lamp_free(sp->lampcopy);
829                 
830                 properties = IDP_GetProperties((ID *)sp->lampcopy, FALSE);
831                 if (properties) {
832                         IDP_FreeProperty(properties);
833                         MEM_freeN(properties);
834                 }
835                 MEM_freeN(sp->lampcopy);
836         }
837         
838         MEM_freeN(sp);
839 }
840
841 /* ************************* icon preview ********************** */
842
843 static void icon_copy_rect(ImBuf *ibuf, unsigned int w, unsigned int h, unsigned int *rect)
844 {
845         struct ImBuf *ima;
846         unsigned int *drect, *srect;
847         float scaledx, scaledy;
848         short ex, ey, dx, dy;
849
850         /* paranoia test */
851         if (ibuf == NULL || (ibuf->rect == NULL && ibuf->rect_float == NULL))
852                 return;
853         
854         /* waste of cpu cyles... but the imbuf API has no other way to scale fast (ton) */
855         ima = IMB_dupImBuf(ibuf);
856         
857         if (!ima) 
858                 return;
859         
860         if (ima->x > ima->y) {
861                 scaledx = (float)w;
862                 scaledy =  ( (float)ima->y / (float)ima->x) * (float)w;
863         }
864         else {
865                 scaledx =  ( (float)ima->x / (float)ima->y) * (float)h;
866                 scaledy = (float)h;
867         }
868         
869         ex = (short)scaledx;
870         ey = (short)scaledy;
871         
872         dx = (w - ex) / 2;
873         dy = (h - ey) / 2;
874         
875         IMB_scalefastImBuf(ima, ex, ey);
876         
877         /* if needed, convert to 32 bits */
878         if (ima->rect == NULL)
879                 IMB_rect_from_float(ima);
880
881         srect = ima->rect;
882         drect = rect;
883
884         drect += dy * w + dx;
885         for (; ey > 0; ey--) {
886                 memcpy(drect, srect, ex * sizeof(int));
887                 drect += w;
888                 srect += ima->x;
889         }
890
891         IMB_freeImBuf(ima);
892 }
893
894 static void set_alpha(char *cp, int sizex, int sizey, char alpha) 
895 {
896         int a, size = sizex * sizey;
897
898         for (a = 0; a < size; a++, cp += 4)
899                 cp[3] = alpha;
900 }
901
902 static void icon_preview_startjob(void *customdata, short *stop, short *do_update)
903 {
904         ShaderPreview *sp = customdata;
905         ID *id = sp->id;
906         short idtype = GS(id->name);
907
908         if (idtype == ID_IM) {
909                 Image *ima = (Image *)id;
910                 ImBuf *ibuf = NULL;
911                 ImageUser iuser = {NULL};
912
913                 /* ima->ok is zero when Image cannot load */
914                 if (ima == NULL || ima->ok == 0)
915                         return;
916
917                 /* setup dummy image user */
918                 iuser.ok = iuser.framenr = 1;
919                 iuser.scene = sp->scene;
920                 
921                 /* elubie: this needs to be changed: here image is always loaded if not
922                  * already there. Very expensive for large images. Need to find a way to 
923                  * only get existing ibuf */
924                 ibuf = BKE_image_get_ibuf(ima, &iuser);
925                 if (ibuf == NULL || ibuf->rect == NULL)
926                         return;
927                 
928                 icon_copy_rect(ibuf, sp->sizex, sp->sizey, sp->pr_rect);
929
930                 *do_update = TRUE;
931         }
932         else if (idtype == ID_BR) {
933                 Brush *br = (Brush *)id;
934
935                 br->icon_imbuf = get_brush_icon(br);
936
937                 memset(sp->pr_rect, 0x888888, sp->sizex * sp->sizey * sizeof(unsigned int));
938
939                 if (!(br->icon_imbuf) || !(br->icon_imbuf->rect))
940                         return;
941
942                 icon_copy_rect(br->icon_imbuf, sp->sizex, sp->sizey, sp->pr_rect);
943
944                 *do_update = TRUE;
945         }
946         else {
947                 /* re-use shader job */
948                 shader_preview_startjob(customdata, stop, do_update);
949
950                 /* world is rendered with alpha=0, so it wasn't displayed 
951                  * this could be render option for sky to, for later */
952                 if (idtype == ID_WO) {
953                         set_alpha((char *)sp->pr_rect, sp->sizex, sp->sizey, 255);
954                 }
955                 else if (idtype == ID_MA) {
956                         Material *ma = (Material *)id;
957
958                         if (ma->material_type == MA_TYPE_HALO)
959                                 set_alpha((char *)sp->pr_rect, sp->sizex, sp->sizey, 255);
960                 }
961         }
962 }
963
964 /* use same function for icon & shader, so the job manager
965  * does not run two of them at the same time. */
966
967 static void common_preview_startjob(void *customdata, short *stop, short *do_update, float *UNUSED(progress))
968 {
969         ShaderPreview *sp = customdata;
970
971         if (sp->pr_method == PR_ICON_RENDER)
972                 icon_preview_startjob(customdata, stop, do_update);
973         else
974                 shader_preview_startjob(customdata, stop, do_update);
975 }
976
977 /* exported functions */
978
979 static void icon_preview_add_size(IconPreview *ip, unsigned int *rect, int sizex, int sizey)
980 {
981         IconPreviewSize *cur_size = ip->sizes.first, *new_size;
982
983         while (cur_size) {
984                 if (cur_size->sizex == sizex && cur_size->sizey == sizey) {
985                         /* requested size is already in list, no need to add it again */
986                         return;
987                 }
988
989                 cur_size = cur_size->next;
990         }
991
992         new_size = MEM_callocN(sizeof(IconPreviewSize), "IconPreviewSize");
993         new_size->sizex = sizex;
994         new_size->sizey = sizey;
995         new_size->rect = rect;
996
997         BLI_addtail(&ip->sizes, new_size);
998 }
999
1000 static void icon_preview_startjob_all_sizes(void *customdata, short *stop, short *do_update, float *progress)
1001 {
1002         IconPreview *ip = (IconPreview *)customdata;
1003         IconPreviewSize *cur_size = ip->sizes.first;
1004
1005         while (cur_size) {
1006                 ShaderPreview *sp = MEM_callocN(sizeof(ShaderPreview), "Icon ShaderPreview");
1007
1008                 /* construct shader preview from image size and previewcustomdata */
1009                 sp->scene = ip->scene;
1010                 sp->owner = ip->owner;
1011                 sp->sizex = cur_size->sizex;
1012                 sp->sizey = cur_size->sizey;
1013                 sp->pr_method = PR_ICON_RENDER;
1014                 sp->pr_rect = cur_size->rect;
1015                 sp->id = ip->id;
1016
1017                 common_preview_startjob(sp, stop, do_update, progress);
1018                 shader_preview_free(sp);
1019
1020                 cur_size = cur_size->next;
1021         }
1022 }
1023
1024 static void icon_preview_endjob(void *customdata)
1025 {
1026         IconPreview *ip = customdata;
1027
1028         if (ip->id && GS(ip->id->name) == ID_BR)
1029                 WM_main_add_notifier(NC_BRUSH | NA_EDITED, ip->id);
1030 }
1031
1032 static void icon_preview_free(void *customdata)
1033 {
1034         IconPreview *ip = (IconPreview *)customdata;
1035
1036         BLI_freelistN(&ip->sizes);
1037         MEM_freeN(ip);
1038 }
1039
1040 void ED_preview_icon_job(const bContext *C, void *owner, ID *id, unsigned int *rect, int sizex, int sizey)
1041 {
1042         wmJob *wm_job;
1043         IconPreview *ip, *old_ip;
1044         
1045         /* suspended start means it starts after 1 timer step, see WM_jobs_timer below */
1046         wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), owner, "Icon Preview",
1047                              WM_JOB_EXCL_RENDER | WM_JOB_SUSPEND, WM_JOB_TYPE_RENDER_PREVIEW);
1048
1049         ip = MEM_callocN(sizeof(IconPreview), "icon preview");
1050
1051         /* render all resolutions from suspended job too */
1052         old_ip = WM_jobs_customdata_get(wm_job);
1053         if (old_ip)
1054                 BLI_movelisttolist(&ip->sizes, &old_ip->sizes);
1055
1056         /* customdata for preview thread */
1057         ip->scene = CTX_data_scene(C);
1058         ip->owner = id;
1059         ip->id = id;
1060
1061         icon_preview_add_size(ip, rect, sizex, sizey);
1062
1063         /* setup job */
1064         WM_jobs_customdata_set(wm_job, ip, icon_preview_free);
1065         WM_jobs_timer(wm_job, 0.25, NC_MATERIAL, NC_MATERIAL);
1066         WM_jobs_callbacks(wm_job, icon_preview_startjob_all_sizes, NULL, NULL, icon_preview_endjob);
1067
1068         WM_jobs_start(CTX_wm_manager(C), wm_job);
1069 }
1070
1071 void ED_preview_shader_job(const bContext *C, void *owner, ID *id, ID *parent, MTex *slot, int sizex, int sizey, int method)
1072 {
1073         Object *ob = CTX_data_active_object(C);
1074         wmJob *wm_job;
1075         ShaderPreview *sp;
1076
1077         wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), owner, "Shader Preview",
1078                             WM_JOB_EXCL_RENDER, WM_JOB_TYPE_RENDER_PREVIEW);
1079         sp = MEM_callocN(sizeof(ShaderPreview), "shader preview");
1080
1081         /* customdata for preview thread */
1082         sp->scene = CTX_data_scene(C);
1083         sp->owner = owner;
1084         sp->sizex = sizex;
1085         sp->sizey = sizey;
1086         sp->pr_method = method;
1087         sp->id = id;
1088         sp->parent = parent;
1089         sp->slot = slot;
1090         if (ob && ob->totcol) copy_v4_v4(sp->col, ob->col);
1091         else sp->col[0] = sp->col[1] = sp->col[2] = sp->col[3] = 1.0f;
1092         
1093         /* setup job */
1094         WM_jobs_customdata_set(wm_job, sp, shader_preview_free);
1095         WM_jobs_timer(wm_job, 0.1, NC_MATERIAL, NC_MATERIAL);
1096         WM_jobs_callbacks(wm_job, common_preview_startjob, NULL, shader_preview_updatejob, NULL);
1097         
1098         WM_jobs_start(CTX_wm_manager(C), wm_job);
1099 }
1100
1101 void ED_preview_kill_jobs(const struct bContext *C)
1102 {
1103         wmWindowManager *wm = CTX_wm_manager(C);
1104         if (wm)
1105                 WM_jobs_kill(wm, NULL, common_preview_startjob);
1106 }
1107